@event4u/agent-config 12.0.0 → 13.0.0

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 (148) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/CHANGELOG.md +273 -0
  3. package/README.md +1 -1
  4. package/agents/templates/.ai-council.yml.example +46 -3
  5. package/dist/agent-src/commands/cost/report.md +23 -0
  6. package/dist/agent-src/contexts/execution/subagent-routing.md +148 -33
  7. package/dist/agent-src/contexts/model-recommendations.md +6 -4
  8. package/dist/agent-src/contexts/subagent-configuration.md +69 -7
  9. package/dist/agent-src/rules/autonomous-execution.md +1 -1
  10. package/dist/agent-src/rules/design-fidelity.md +1 -1
  11. package/dist/agent-src/rules/design-review-after-ui-write.md +10 -5
  12. package/dist/agent-src/rules/evaluator-independence.md +27 -5
  13. package/dist/agent-src/rules/git-history-discipline.md +5 -1
  14. package/dist/agent-src/rules/low-impact-corpus-privacy-floor.md +1 -0
  15. package/dist/agent-src/rules/missing-skill-recovery.md +89 -0
  16. package/dist/agent-src/rules/ui-audit-gate.md +9 -4
  17. package/dist/agent-src/scripts/archive_completed_roadmaps.ts +33 -0
  18. package/dist/agent-src/scripts/roadmap_gates.ts +30 -2
  19. package/dist/agent-src/scripts/update_roadmap_progress.ts +47 -1
  20. package/dist/agent-src/skills/authz-review/SKILL.md +4 -0
  21. package/dist/agent-src/skills/design-system-capture/SKILL.md +4 -0
  22. package/dist/agent-src/skills/design-system-capture/references/snapshot-preference-order.md +77 -0
  23. package/dist/agent-src/skills/merge-conflicts/SKILL.md +4 -0
  24. package/dist/agent-src/skills/prompt-optimizer/SKILL.md +17 -1
  25. package/dist/agent-src/skills/roadmap-writing/SKILL.md +11 -11
  26. package/dist/agent-src/skills/systematic-debugging/SKILL.md +4 -0
  27. package/dist/agent-src/skills/threat-modeling/SKILL.md +3 -0
  28. package/dist/agent-src/templates/agents/agent-project-settings.example.yml +1 -1
  29. package/dist/agent-src/templates/roadmaps.md +41 -3
  30. package/dist/cli/registry.js +1 -0
  31. package/dist/cli/registry.js.map +1 -1
  32. package/dist/cli-delegate/{chunk-NN4KW4HZ.js → chunk-3B72LSZL.js} +123 -2338
  33. package/dist/cli-delegate/{chunk-55WZN5VF.js → chunk-EVTY7ISR.js} +2 -2
  34. package/dist/cli-delegate/chunk-N7FEST6D.js +3895 -0
  35. package/dist/cli-delegate/{chunk-L565YFXH.js → chunk-XURJ4AQC.js} +566 -298
  36. package/dist/cli-delegate/cmd_conformance.js +4 -4
  37. package/dist/cli-delegate/cmd_doctor.js +2 -2
  38. package/dist/cli-delegate/cmd_export.js +2 -2
  39. package/dist/cli-delegate/cmd_handoff.js +1 -1
  40. package/dist/cli-delegate/cmd_prune.js +2 -2
  41. package/dist/cli-delegate/cmd_settings_migrate.js +2 -2
  42. package/dist/cli-delegate/cmd_settings_set.js +1 -1
  43. package/dist/cli-delegate/cmd_sync.js +2 -2
  44. package/dist/cli-delegate/cmd_uninstall.js +2 -2
  45. package/dist/cli-delegate/cmd_validate.js +2 -2
  46. package/dist/discovery/deprecation-report.md +1 -1
  47. package/dist/discovery/discovery-manifest.json +44 -23
  48. package/dist/discovery/discovery-manifest.json.sha256 +1 -1
  49. package/dist/discovery/discovery-manifest.summary.md +3 -3
  50. package/dist/discovery/dormancy-report.md +1 -1
  51. package/dist/discovery/orphan-report.md +1 -1
  52. package/dist/discovery/packs.json +6 -5
  53. package/dist/discovery/trust-report.md +2 -2
  54. package/dist/discovery/workspaces.json +5 -4
  55. package/dist/hooks/dispatch.js +10625 -8541
  56. package/dist/install/install.mjs +754 -484
  57. package/dist/mcp/registry-manifest.json +3 -3
  58. package/dist/mcp/server.json +2 -2
  59. package/dist/mcp/server.mjs +1302 -167
  60. package/dist/router.json +1 -1
  61. package/dist/scripts/_lib/model_tier.js +9 -0
  62. package/dist/scripts/_lib/model_tier.js.map +1 -1
  63. package/dist/scripts/condense.js +50 -0
  64. package/dist/scripts/condense.js.map +1 -1
  65. package/dist/server/schemas/settings.js +2 -2
  66. package/dist/server/schemas/settings.js.map +1 -1
  67. package/docs/MIGRATION.md +20 -1
  68. package/docs/guidelines/design-fidelity-mechanics.md +21 -0
  69. package/docs/guidelines/design-handover-extraction.md +87 -0
  70. package/docs/guidelines/prompt-templates.md +24 -1
  71. package/package.json +1 -1
  72. package/src/agent-src/scripts/archive_completed_roadmaps.ts +33 -0
  73. package/src/agent-src/scripts/roadmap_gates.ts +30 -2
  74. package/src/agent-src/scripts/update_roadmap_progress.ts +47 -1
  75. package/src/config/evaluator-budgets.json +9 -8
  76. package/src/config/gate-coverage.yml +63 -11
  77. package/src/config/gate-violation-baselines.json +12 -0
  78. package/src/config/hook-token-budget.json +15 -2
  79. package/src/config/preamble-payload-budget.json +28 -0
  80. package/src/scripts/_dispatch.bash +10 -0
  81. package/src/scripts/_lib/bench_ab_complexity.ts +439 -0
  82. package/src/scripts/_lib/bench_ab_safety_tier.ts +142 -0
  83. package/src/scripts/_lib/bench_ab_scoring_v2.ts +28 -3
  84. package/src/scripts/_lib/bench_ab_search_adherence.ts +423 -0
  85. package/src/scripts/_lib/bench_ab_size_claim.ts +206 -0
  86. package/src/scripts/_lib/bench_ab_workspace.ts +111 -0
  87. package/src/scripts/_lib/git_common_dir.ts +173 -0
  88. package/src/scripts/_lib/model_tier.ts +9 -0
  89. package/src/scripts/_lib/release_highlights.ts +108 -7
  90. package/src/scripts/_lib/skill_catalogue.ts +1115 -0
  91. package/src/scripts/_lib/tier_budget_routing.ts +31 -331
  92. package/src/scripts/ai_council/clients.ts +197 -18
  93. package/src/scripts/ai_council/config.ts +16 -1
  94. package/src/scripts/ai_council/low_impact.ts +14 -1
  95. package/src/scripts/ai_council/orchestrator.ts +14 -1
  96. package/src/scripts/ai_council/transport_resolver.ts +32 -2
  97. package/src/scripts/bench_ab_v2_complexity.ts +252 -0
  98. package/src/scripts/bench_ab_v2_run.ts +3 -42
  99. package/src/scripts/bench_ab_v2_safety.ts +296 -0
  100. package/src/scripts/bench_ab_v2_search.ts +343 -0
  101. package/src/scripts/bench_ab_v2_stats.ts +191 -0
  102. package/src/scripts/build_archive_index.ts +383 -0
  103. package/src/scripts/capture_skill_catalogue.ts +279 -317
  104. package/src/scripts/check_branch_freshness.ts +7 -1
  105. package/src/scripts/check_release_published.ts +6 -2
  106. package/src/scripts/check_release_trunk_sync.ts +18 -2
  107. package/src/scripts/check_source_size_budget.ts +428 -0
  108. package/src/scripts/check_suppression_hygiene.ts +5 -1
  109. package/src/scripts/condense.ts +50 -1
  110. package/src/scripts/cost/backfill_rates.mjs +298 -0
  111. package/src/scripts/cost/budget.mjs +12 -32
  112. package/src/scripts/cost_summary.ts +25 -0
  113. package/src/scripts/dispatch_r2_reviewer.ts +15 -5
  114. package/src/scripts/evidence_report.ts +15 -4
  115. package/src/scripts/external_sources_denylist.json +1 -0
  116. package/src/scripts/hook_manifest.yaml +75 -15
  117. package/src/scripts/hooks/concern_registry.ts +4 -0
  118. package/src/scripts/hooks/orchestration_record_hook.ts +21 -0
  119. package/src/scripts/hooks/skill_route_hook.ts +226 -0
  120. package/src/scripts/hooks/tool_result_bytes_hook.ts +219 -0
  121. package/src/scripts/hooks/ui_route_nudge_hook.ts +6 -3
  122. package/src/scripts/hot_context_hook.ts +13 -1
  123. package/src/scripts/install.ts +129 -0
  124. package/src/scripts/janitor.ts +77 -2
  125. package/src/scripts/lint_design_antipattern_parity.ts +40 -6
  126. package/src/scripts/lint_harvest_provenance.ts +35 -1
  127. package/src/scripts/lint_plan_risk_register.ts +22 -9
  128. package/src/scripts/lint_roadmap_blockers.ts +109 -6
  129. package/src/scripts/lint_rule_skill_pack_reach.ts +43 -0
  130. package/src/scripts/lint_scheduled_deprecations.ts +738 -0
  131. package/src/scripts/lint_skill_router_head.ts +21 -0
  132. package/src/scripts/mcp_server/consumer_tool_catalog.json +26 -3
  133. package/src/scripts/mcp_server/tool_catalog_source.ts +6 -24
  134. package/src/scripts/mcp_server/tools.ts +87 -0
  135. package/src/scripts/migration_status.ts +17 -1
  136. package/src/scripts/probe_review_binding_drift.ts +731 -0
  137. package/src/scripts/release.ts +100 -6
  138. package/src/scripts/routing_doctor.ts +50 -13
  139. package/src/scripts/schemas/command.schema.json +3 -3
  140. package/src/scripts/schemas/skill.schema.json +20 -2
  141. package/src/scripts/schemas/subagent.schema.json +2 -2
  142. package/src/scripts/session_eol_report.ts +81 -0
  143. package/src/scripts/trigger_coverage.ts +224 -3
  144. package/src/scripts/workspace_doctor.ts +464 -0
  145. package/src/scripts/worktree_cleanup_check.ts +2 -1
  146. package/src/server/schemas/settings.ts +2 -2
  147. package/dist/cli-delegate/chunk-AYKQGIBG.js +0 -1594
  148. package/src/config/budget-routing.json +0 -7
@@ -1841,17 +1841,17 @@ var require_visit = __commonJS({
1841
1841
  visit.BREAK = BREAK;
1842
1842
  visit.SKIP = SKIP;
1843
1843
  visit.REMOVE = REMOVE;
1844
- function visit_(key, node, visitor, path75) {
1845
- const ctrl = callVisitor(key, node, visitor, path75);
1844
+ function visit_(key, node, visitor, path77) {
1845
+ const ctrl = callVisitor(key, node, visitor, path77);
1846
1846
  if (identity.isNode(ctrl) || identity.isPair(ctrl)) {
1847
- replaceNode(key, path75, ctrl);
1848
- return visit_(key, ctrl, visitor, path75);
1847
+ replaceNode(key, path77, ctrl);
1848
+ return visit_(key, ctrl, visitor, path77);
1849
1849
  }
1850
1850
  if (typeof ctrl !== "symbol") {
1851
1851
  if (identity.isCollection(node)) {
1852
- path75 = Object.freeze(path75.concat(node));
1852
+ path77 = Object.freeze(path77.concat(node));
1853
1853
  for (let i = 0; i < node.items.length; ++i) {
1854
- const ci = visit_(i, node.items[i], visitor, path75);
1854
+ const ci = visit_(i, node.items[i], visitor, path77);
1855
1855
  if (typeof ci === "number")
1856
1856
  i = ci - 1;
1857
1857
  else if (ci === BREAK)
@@ -1862,13 +1862,13 @@ var require_visit = __commonJS({
1862
1862
  }
1863
1863
  }
1864
1864
  } else if (identity.isPair(node)) {
1865
- path75 = Object.freeze(path75.concat(node));
1866
- const ck = visit_("key", node.key, visitor, path75);
1865
+ path77 = Object.freeze(path77.concat(node));
1866
+ const ck = visit_("key", node.key, visitor, path77);
1867
1867
  if (ck === BREAK)
1868
1868
  return BREAK;
1869
1869
  else if (ck === REMOVE)
1870
1870
  node.key = null;
1871
- const cv = visit_("value", node.value, visitor, path75);
1871
+ const cv = visit_("value", node.value, visitor, path77);
1872
1872
  if (cv === BREAK)
1873
1873
  return BREAK;
1874
1874
  else if (cv === REMOVE)
@@ -1889,17 +1889,17 @@ var require_visit = __commonJS({
1889
1889
  visitAsync.BREAK = BREAK;
1890
1890
  visitAsync.SKIP = SKIP;
1891
1891
  visitAsync.REMOVE = REMOVE;
1892
- async function visitAsync_(key, node, visitor, path75) {
1893
- const ctrl = await callVisitor(key, node, visitor, path75);
1892
+ async function visitAsync_(key, node, visitor, path77) {
1893
+ const ctrl = await callVisitor(key, node, visitor, path77);
1894
1894
  if (identity.isNode(ctrl) || identity.isPair(ctrl)) {
1895
- replaceNode(key, path75, ctrl);
1896
- return visitAsync_(key, ctrl, visitor, path75);
1895
+ replaceNode(key, path77, ctrl);
1896
+ return visitAsync_(key, ctrl, visitor, path77);
1897
1897
  }
1898
1898
  if (typeof ctrl !== "symbol") {
1899
1899
  if (identity.isCollection(node)) {
1900
- path75 = Object.freeze(path75.concat(node));
1900
+ path77 = Object.freeze(path77.concat(node));
1901
1901
  for (let i = 0; i < node.items.length; ++i) {
1902
- const ci = await visitAsync_(i, node.items[i], visitor, path75);
1902
+ const ci = await visitAsync_(i, node.items[i], visitor, path77);
1903
1903
  if (typeof ci === "number")
1904
1904
  i = ci - 1;
1905
1905
  else if (ci === BREAK)
@@ -1910,13 +1910,13 @@ var require_visit = __commonJS({
1910
1910
  }
1911
1911
  }
1912
1912
  } else if (identity.isPair(node)) {
1913
- path75 = Object.freeze(path75.concat(node));
1914
- const ck = await visitAsync_("key", node.key, visitor, path75);
1913
+ path77 = Object.freeze(path77.concat(node));
1914
+ const ck = await visitAsync_("key", node.key, visitor, path77);
1915
1915
  if (ck === BREAK)
1916
1916
  return BREAK;
1917
1917
  else if (ck === REMOVE)
1918
1918
  node.key = null;
1919
- const cv = await visitAsync_("value", node.value, visitor, path75);
1919
+ const cv = await visitAsync_("value", node.value, visitor, path77);
1920
1920
  if (cv === BREAK)
1921
1921
  return BREAK;
1922
1922
  else if (cv === REMOVE)
@@ -1943,23 +1943,23 @@ var require_visit = __commonJS({
1943
1943
  }
1944
1944
  return visitor;
1945
1945
  }
1946
- function callVisitor(key, node, visitor, path75) {
1946
+ function callVisitor(key, node, visitor, path77) {
1947
1947
  if (typeof visitor === "function")
1948
- return visitor(key, node, path75);
1948
+ return visitor(key, node, path77);
1949
1949
  if (identity.isMap(node))
1950
- return visitor.Map?.(key, node, path75);
1950
+ return visitor.Map?.(key, node, path77);
1951
1951
  if (identity.isSeq(node))
1952
- return visitor.Seq?.(key, node, path75);
1952
+ return visitor.Seq?.(key, node, path77);
1953
1953
  if (identity.isPair(node))
1954
- return visitor.Pair?.(key, node, path75);
1954
+ return visitor.Pair?.(key, node, path77);
1955
1955
  if (identity.isScalar(node))
1956
- return visitor.Scalar?.(key, node, path75);
1956
+ return visitor.Scalar?.(key, node, path77);
1957
1957
  if (identity.isAlias(node))
1958
- return visitor.Alias?.(key, node, path75);
1958
+ return visitor.Alias?.(key, node, path77);
1959
1959
  return void 0;
1960
1960
  }
1961
- function replaceNode(key, path75, node) {
1962
- const parent = path75[path75.length - 1];
1961
+ function replaceNode(key, path77, node) {
1962
+ const parent = path77[path77.length - 1];
1963
1963
  if (identity.isCollection(parent)) {
1964
1964
  parent.items[key] = node;
1965
1965
  } else if (identity.isPair(parent)) {
@@ -2569,10 +2569,10 @@ var require_Collection = __commonJS({
2569
2569
  var createNode = require_createNode();
2570
2570
  var identity = require_identity();
2571
2571
  var Node = require_Node();
2572
- function collectionFromPath(schema, path75, value) {
2572
+ function collectionFromPath(schema, path77, value) {
2573
2573
  let v = value;
2574
- for (let i = path75.length - 1; i >= 0; --i) {
2575
- const k = path75[i];
2574
+ for (let i = path77.length - 1; i >= 0; --i) {
2575
+ const k = path77[i];
2576
2576
  if (typeof k === "number" && Number.isInteger(k) && k >= 0) {
2577
2577
  const a = [];
2578
2578
  a[k] = v;
@@ -2591,7 +2591,7 @@ var require_Collection = __commonJS({
2591
2591
  sourceObjects: /* @__PURE__ */ new Map()
2592
2592
  });
2593
2593
  }
2594
- var isEmptyPath = (path75) => path75 == null || typeof path75 === "object" && !!path75[Symbol.iterator]().next().done;
2594
+ var isEmptyPath = (path77) => path77 == null || typeof path77 === "object" && !!path77[Symbol.iterator]().next().done;
2595
2595
  var Collection = class extends Node.NodeBase {
2596
2596
  constructor(type, schema) {
2597
2597
  super(type);
@@ -2621,11 +2621,11 @@ var require_Collection = __commonJS({
2621
2621
  * be a Pair instance or a `{ key, value }` object, which may not have a key
2622
2622
  * that already exists in the map.
2623
2623
  */
2624
- addIn(path75, value) {
2625
- if (isEmptyPath(path75))
2624
+ addIn(path77, value) {
2625
+ if (isEmptyPath(path77))
2626
2626
  this.add(value);
2627
2627
  else {
2628
- const [key, ...rest] = path75;
2628
+ const [key, ...rest] = path77;
2629
2629
  const node = this.get(key, true);
2630
2630
  if (identity.isCollection(node))
2631
2631
  node.addIn(rest, value);
@@ -2639,8 +2639,8 @@ var require_Collection = __commonJS({
2639
2639
  * Removes a value from the collection.
2640
2640
  * @returns `true` if the item was found and removed.
2641
2641
  */
2642
- deleteIn(path75) {
2643
- const [key, ...rest] = path75;
2642
+ deleteIn(path77) {
2643
+ const [key, ...rest] = path77;
2644
2644
  if (rest.length === 0)
2645
2645
  return this.delete(key);
2646
2646
  const node = this.get(key, true);
@@ -2654,8 +2654,8 @@ var require_Collection = __commonJS({
2654
2654
  * scalar values from their surrounding node; to disable set `keepScalar` to
2655
2655
  * `true` (collections are always returned intact).
2656
2656
  */
2657
- getIn(path75, keepScalar) {
2658
- const [key, ...rest] = path75;
2657
+ getIn(path77, keepScalar) {
2658
+ const [key, ...rest] = path77;
2659
2659
  const node = this.get(key, true);
2660
2660
  if (rest.length === 0)
2661
2661
  return !keepScalar && identity.isScalar(node) ? node.value : node;
@@ -2673,8 +2673,8 @@ var require_Collection = __commonJS({
2673
2673
  /**
2674
2674
  * Checks if the collection includes a value with the key `key`.
2675
2675
  */
2676
- hasIn(path75) {
2677
- const [key, ...rest] = path75;
2676
+ hasIn(path77) {
2677
+ const [key, ...rest] = path77;
2678
2678
  if (rest.length === 0)
2679
2679
  return this.has(key);
2680
2680
  const node = this.get(key, true);
@@ -2684,8 +2684,8 @@ var require_Collection = __commonJS({
2684
2684
  * Sets a value in this collection. For `!!set`, `value` needs to be a
2685
2685
  * boolean to add/remove the item from the set.
2686
2686
  */
2687
- setIn(path75, value) {
2688
- const [key, ...rest] = path75;
2687
+ setIn(path77, value) {
2688
+ const [key, ...rest] = path77;
2689
2689
  if (rest.length === 0) {
2690
2690
  this.set(key, value);
2691
2691
  } else {
@@ -5200,9 +5200,9 @@ var require_Document = __commonJS({
5200
5200
  this.contents.add(value);
5201
5201
  }
5202
5202
  /** Adds a value to the document. */
5203
- addIn(path75, value) {
5203
+ addIn(path77, value) {
5204
5204
  if (assertCollection(this.contents))
5205
- this.contents.addIn(path75, value);
5205
+ this.contents.addIn(path77, value);
5206
5206
  }
5207
5207
  /**
5208
5208
  * Create a new `Alias` node, ensuring that the target `node` has the required anchor.
@@ -5277,14 +5277,14 @@ var require_Document = __commonJS({
5277
5277
  * Removes a value from the document.
5278
5278
  * @returns `true` if the item was found and removed.
5279
5279
  */
5280
- deleteIn(path75) {
5281
- if (Collection.isEmptyPath(path75)) {
5280
+ deleteIn(path77) {
5281
+ if (Collection.isEmptyPath(path77)) {
5282
5282
  if (this.contents == null)
5283
5283
  return false;
5284
5284
  this.contents = null;
5285
5285
  return true;
5286
5286
  }
5287
- return assertCollection(this.contents) ? this.contents.deleteIn(path75) : false;
5287
+ return assertCollection(this.contents) ? this.contents.deleteIn(path77) : false;
5288
5288
  }
5289
5289
  /**
5290
5290
  * Returns item at `key`, or `undefined` if not found. By default unwraps
@@ -5299,10 +5299,10 @@ var require_Document = __commonJS({
5299
5299
  * scalar values from their surrounding node; to disable set `keepScalar` to
5300
5300
  * `true` (collections are always returned intact).
5301
5301
  */
5302
- getIn(path75, keepScalar) {
5303
- if (Collection.isEmptyPath(path75))
5302
+ getIn(path77, keepScalar) {
5303
+ if (Collection.isEmptyPath(path77))
5304
5304
  return !keepScalar && identity.isScalar(this.contents) ? this.contents.value : this.contents;
5305
- return identity.isCollection(this.contents) ? this.contents.getIn(path75, keepScalar) : void 0;
5305
+ return identity.isCollection(this.contents) ? this.contents.getIn(path77, keepScalar) : void 0;
5306
5306
  }
5307
5307
  /**
5308
5308
  * Checks if the document includes a value with the key `key`.
@@ -5313,10 +5313,10 @@ var require_Document = __commonJS({
5313
5313
  /**
5314
5314
  * Checks if the document includes a value at `path`.
5315
5315
  */
5316
- hasIn(path75) {
5317
- if (Collection.isEmptyPath(path75))
5316
+ hasIn(path77) {
5317
+ if (Collection.isEmptyPath(path77))
5318
5318
  return this.contents !== void 0;
5319
- return identity.isCollection(this.contents) ? this.contents.hasIn(path75) : false;
5319
+ return identity.isCollection(this.contents) ? this.contents.hasIn(path77) : false;
5320
5320
  }
5321
5321
  /**
5322
5322
  * Sets a value in this document. For `!!set`, `value` needs to be a
@@ -5333,13 +5333,13 @@ var require_Document = __commonJS({
5333
5333
  * Sets a value in this document. For `!!set`, `value` needs to be a
5334
5334
  * boolean to add/remove the item from the set.
5335
5335
  */
5336
- setIn(path75, value) {
5337
- if (Collection.isEmptyPath(path75)) {
5336
+ setIn(path77, value) {
5337
+ if (Collection.isEmptyPath(path77)) {
5338
5338
  this.contents = value;
5339
5339
  } else if (this.contents == null) {
5340
- this.contents = Collection.collectionFromPath(this.schema, Array.from(path75), value);
5340
+ this.contents = Collection.collectionFromPath(this.schema, Array.from(path77), value);
5341
5341
  } else if (assertCollection(this.contents)) {
5342
- this.contents.setIn(path75, value);
5342
+ this.contents.setIn(path77, value);
5343
5343
  }
5344
5344
  }
5345
5345
  /**
@@ -7299,9 +7299,9 @@ var require_cst_visit = __commonJS({
7299
7299
  visit.BREAK = BREAK;
7300
7300
  visit.SKIP = SKIP;
7301
7301
  visit.REMOVE = REMOVE;
7302
- visit.itemAtPath = (cst, path75) => {
7302
+ visit.itemAtPath = (cst, path77) => {
7303
7303
  let item = cst;
7304
- for (const [field, index] of path75) {
7304
+ for (const [field, index] of path77) {
7305
7305
  const tok = item?.[field];
7306
7306
  if (tok && "items" in tok) {
7307
7307
  item = tok.items[index];
@@ -7310,23 +7310,23 @@ var require_cst_visit = __commonJS({
7310
7310
  }
7311
7311
  return item;
7312
7312
  };
7313
- visit.parentCollection = (cst, path75) => {
7314
- const parent = visit.itemAtPath(cst, path75.slice(0, -1));
7315
- const field = path75[path75.length - 1][0];
7313
+ visit.parentCollection = (cst, path77) => {
7314
+ const parent = visit.itemAtPath(cst, path77.slice(0, -1));
7315
+ const field = path77[path77.length - 1][0];
7316
7316
  const coll = parent?.[field];
7317
7317
  if (coll && "items" in coll)
7318
7318
  return coll;
7319
7319
  throw new Error("Parent collection not found");
7320
7320
  };
7321
- function _visit(path75, item, visitor) {
7322
- let ctrl = visitor(item, path75);
7321
+ function _visit(path77, item, visitor) {
7322
+ let ctrl = visitor(item, path77);
7323
7323
  if (typeof ctrl === "symbol")
7324
7324
  return ctrl;
7325
7325
  for (const field of ["key", "value"]) {
7326
7326
  const token = item[field];
7327
7327
  if (token && "items" in token) {
7328
7328
  for (let i = 0; i < token.items.length; ++i) {
7329
- const ci = _visit(Object.freeze(path75.concat([[field, i]])), token.items[i], visitor);
7329
+ const ci = _visit(Object.freeze(path77.concat([[field, i]])), token.items[i], visitor);
7330
7330
  if (typeof ci === "number")
7331
7331
  i = ci - 1;
7332
7332
  else if (ci === BREAK)
@@ -7337,10 +7337,10 @@ var require_cst_visit = __commonJS({
7337
7337
  }
7338
7338
  }
7339
7339
  if (typeof ctrl === "function" && field === "key")
7340
- ctrl = ctrl(item, path75);
7340
+ ctrl = ctrl(item, path77);
7341
7341
  }
7342
7342
  }
7343
- return typeof ctrl === "function" ? ctrl(item, path75) : ctrl;
7343
+ return typeof ctrl === "function" ? ctrl(item, path77) : ctrl;
7344
7344
  }
7345
7345
  exports.visit = visit;
7346
7346
  }
@@ -8642,14 +8642,14 @@ var require_parser = __commonJS({
8642
8642
  case "scalar":
8643
8643
  case "single-quoted-scalar":
8644
8644
  case "double-quoted-scalar": {
8645
- const fs78 = this.flowScalar(this.type);
8645
+ const fs80 = this.flowScalar(this.type);
8646
8646
  if (atNextItem || it.value) {
8647
- map.items.push({ start, key: fs78, sep: [] });
8647
+ map.items.push({ start, key: fs80, sep: [] });
8648
8648
  this.onKeyLine = true;
8649
8649
  } else if (it.sep) {
8650
- this.stack.push(fs78);
8650
+ this.stack.push(fs80);
8651
8651
  } else {
8652
- Object.assign(it, { key: fs78, sep: [] });
8652
+ Object.assign(it, { key: fs80, sep: [] });
8653
8653
  this.onKeyLine = true;
8654
8654
  }
8655
8655
  return;
@@ -8777,13 +8777,13 @@ var require_parser = __commonJS({
8777
8777
  case "scalar":
8778
8778
  case "single-quoted-scalar":
8779
8779
  case "double-quoted-scalar": {
8780
- const fs78 = this.flowScalar(this.type);
8780
+ const fs80 = this.flowScalar(this.type);
8781
8781
  if (!it || it.value)
8782
- fc.items.push({ start: [], key: fs78, sep: [] });
8782
+ fc.items.push({ start: [], key: fs80, sep: [] });
8783
8783
  else if (it.sep)
8784
- this.stack.push(fs78);
8784
+ this.stack.push(fs80);
8785
8785
  else
8786
- Object.assign(it, { key: fs78, sep: [] });
8786
+ Object.assign(it, { key: fs80, sep: [] });
8787
8787
  return;
8788
8788
  }
8789
8789
  case "flow-map-end":
@@ -18909,7 +18909,10 @@ function parse_blockers(raw_text) {
18909
18909
  owner: _blockerField(body, "Owner") ?? "user",
18910
18910
  blocks: _blockerField(body, "Blocks") ?? "(unspecified)",
18911
18911
  todo: _blockerTodo(body),
18912
- resolvedWhen: _blockerField(body, "Resolved when") ?? "(unspecified)"
18912
+ resolvedWhen: _blockerField(body, "Resolved when") ?? "(unspecified)",
18913
+ recommendation: _blockerField(body, "Recommendation") ?? "",
18914
+ ifNothing: _blockerField(body, "If you do nothing") ?? "",
18915
+ question: _blockerField(body, "Question") ?? ""
18913
18916
  });
18914
18917
  }
18915
18918
  }
@@ -18935,7 +18938,10 @@ function parse_blockers(raw_text) {
18935
18938
  owner: "user",
18936
18939
  blocks: "entire roadmap",
18937
18940
  todo: [parts.join(" ")],
18938
- resolvedWhen: "condition described above clears"
18941
+ resolvedWhen: "condition described above clears",
18942
+ recommendation: "",
18943
+ ifNothing: "",
18944
+ question: ""
18939
18945
  });
18940
18946
  }
18941
18947
  return blockers;
@@ -19207,6 +19213,12 @@ function render(roadmaps, bundles = null) {
19207
19213
  lines.push("**Blockers**\n");
19208
19214
  for (const b of openBlockers) {
19209
19215
  lines.push(`- **${b.id}** (owner: ${b.owner}) \u2014 blocks ${b.blocks}`);
19216
+ if (b.recommendation) {
19217
+ lines.push(` - **Recommendation:** ${b.recommendation}`);
19218
+ }
19219
+ if (b.ifNothing) {
19220
+ lines.push(` - **If you do nothing:** ${b.ifNothing}`);
19221
+ }
19210
19222
  lines.push(" - **What to do:**");
19211
19223
  for (const step of b.todo) {
19212
19224
  lines.push(` ${step}`);
@@ -19535,7 +19547,7 @@ var init_update_roadmap_progress = __esm({
19535
19547
  BLOCKER_HEADING_RE = /^###[ \t]+blocker:[ \t]*(.+?)[ \t]*$/gim;
19536
19548
  NEXT_H2_RE = /^##[ \t]+\S/m;
19537
19549
  LEGACY_BLOCKED_UNTIL_RE = /^>[ \t]*Blocked until:?[ \t]*(.+)$/im;
19538
- BLOCKER_FIELD_RE = /^-[ \t]*\*\*(Status|Owner|Blocks|Resolved when|What to do):\*\*/i;
19550
+ BLOCKER_FIELD_RE = /^-[ \t]*\*\*[^*]+:\*\*/;
19539
19551
  ArgparseExit = class extends Error {
19540
19552
  code;
19541
19553
  constructor(code) {
@@ -19781,6 +19793,21 @@ function _regen_dashboard(root, dry_run) {
19781
19793
  _run(["git", "add", "--", "agents/roadmaps-progress.md"], root);
19782
19794
  }
19783
19795
  }
19796
+ function _regen_archive_index(root, dry_run) {
19797
+ if (dry_run) {
19798
+ return;
19799
+ }
19800
+ const script = path22.join(root, "src", "scripts", "build_archive_index.ts");
19801
+ if (!_isFile8(script)) {
19802
+ return;
19803
+ }
19804
+ _runTwin(root, script);
19805
+ for (const rel of ["agents/roadmaps/archive/INDEX.md", "agents/roadmaps/archive/index.json"]) {
19806
+ if (_isFile8(path22.join(root, rel))) {
19807
+ _run(["git", "add", "--", rel], root);
19808
+ }
19809
+ }
19810
+ }
19784
19811
  function _runTwin(root, script) {
19785
19812
  const binName = process9.platform === "win32" ? "tsx.cmd" : "tsx";
19786
19813
  let dir = path22.dirname(_HERE6);
@@ -19853,6 +19880,7 @@ function main10(argv) {
19853
19880
  return 0;
19854
19881
  }
19855
19882
  _regen_dashboard(root, ns.dry_run);
19883
+ _regen_archive_index(root, ns.dry_run);
19856
19884
  const verb = ns.dry_run ? "Would archive" : "Archived";
19857
19885
  for (const rec of archived) {
19858
19886
  process9.stdout.write(
@@ -21700,8 +21728,8 @@ function lockfile_path(env) {
21700
21728
  }
21701
21729
  return write_target("installed.lock", { env: env ?? null });
21702
21730
  }
21703
- function read_lockfile(path75) {
21704
- const target = path75 ?? lockfile_path();
21731
+ function read_lockfile(path77) {
21732
+ const target = path77 ?? lockfile_path();
21705
21733
  let text;
21706
21734
  try {
21707
21735
  text = fs31.readFileSync(target, { encoding: "utf-8" });
@@ -21911,18 +21939,18 @@ function manifest_path(project_root, env) {
21911
21939
  }
21912
21940
  return fsPath.join(project_root, DEFAULT_MANIFEST_RELATIVE);
21913
21941
  }
21914
- function read_manifest(path75) {
21942
+ function read_manifest(path77) {
21915
21943
  let text;
21916
21944
  try {
21917
- text = require_read_text(path75);
21945
+ text = require_read_text(path77);
21918
21946
  } catch {
21919
21947
  return null;
21920
21948
  }
21921
21949
  const data = _parse_manual(text);
21922
21950
  return _normalise_v2_shape(data);
21923
21951
  }
21924
- function require_read_text(path75) {
21925
- return fs33.readFileSync(path75, { encoding: "utf-8" });
21952
+ function require_read_text(path77) {
21953
+ return fs33.readFileSync(path77, { encoding: "utf-8" });
21926
21954
  }
21927
21955
  function _normalise_v2_shape(data) {
21928
21956
  if (data["tools"] === void 0 || data["tools"] === null) {
@@ -22103,9 +22131,9 @@ function stable_sort(items, key) {
22103
22131
  return a.index - b.index;
22104
22132
  }).map((entry) => entry.item);
22105
22133
  }
22106
- function write_manifest(path75, version, tools, options = {}) {
22134
+ function write_manifest(path77, version, tools, options = {}) {
22107
22135
  const rendered = _render(version, tools, { deploy_roots: options.deploy_roots ?? null });
22108
- return write_atomic(path75, rendered);
22136
+ return write_atomic(path77, rendered);
22109
22137
  }
22110
22138
  function upsert_tool(existing, options) {
22111
22139
  const {
@@ -25792,6 +25820,7 @@ __export(clients_exports, {
25792
25820
  AnthropicCliClient: () => AnthropicCliClient,
25793
25821
  AnthropicClient: () => AnthropicClient,
25794
25822
  CLI_CALLS_FILENAME: () => CLI_CALLS_FILENAME,
25823
+ CODEX_MEASURED_UNSERVABLE: () => CODEX_MEASURED_UNSERVABLE,
25795
25824
  CliClient: () => CliClient,
25796
25825
  CliClientError: () => CliClientError,
25797
25826
  CouncilResponse: () => CouncilResponse,
@@ -25813,6 +25842,7 @@ __export(clients_exports, {
25813
25842
  KeyGateError: () => KeyGateError,
25814
25843
  MANUAL_END_MARKER: () => MANUAL_END_MARKER,
25815
25844
  ManualClient: () => ManualClient,
25845
+ OPENAI_CLI_VENDOR_DEFAULT: () => OPENAI_CLI_VENDOR_DEFAULT,
25816
25846
  OPENAI_KEY_FILENAME: () => OPENAI_KEY_FILENAME,
25817
25847
  OPENAI_KEY_PATH: () => OPENAI_KEY_PATH,
25818
25848
  OpenAICliClient: () => OpenAICliClient,
@@ -26435,7 +26465,7 @@ function _defaultStdin() {
26435
26465
  }
26436
26466
  };
26437
26467
  }
26438
- var _modelCeilingMemo, ANTHROPIC_KEY_FILENAME, OPENAI_KEY_FILENAME, ANTHROPIC_KEY_PATH, OPENAI_KEY_PATH, DEFAULT_ANTHROPIC_MODEL, DEFAULT_OPENAI_MODEL, DEFAULT_GEMINI_MODEL, DEFAULT_XAI_MODEL, DEFAULT_PERPLEXITY_MODEL, DEFAULT_OPENAI_CLI_MODEL, DEFAULT_ANTHROPIC_CLI_MODEL, DEFAULT_GEMINI_CLI_MODEL, XAI_BASE_URL, PERPLEXITY_BASE_URL, DEFAULT_MAX_TOKENS, UNLIMITED_TOKENS_FALLBACK, _REASONING_PREFIXES, KeyGateError, CliClientError, DEFAULT_PROMPT_CACHE_TTL, CouncilResponse, _monotonicSource, ExternalAIClient, AnthropicClient, OpenAIClient, GeminiClient, _OpenAICompatibleClient, XAIClient, PerplexityClient, CLI_CALLS_FILENAME, DEFAULT_CLI_TIMEOUT_SECONDS, SubprocessError, CliClient, AnthropicCliClient, OpenAICliClient, GeminiCliClient, XAICliClient, PerplexityCliClient, MANUAL_END_MARKER, ManualClient, ValueError3, JSONDecodeError;
26468
+ var _modelCeilingMemo, ANTHROPIC_KEY_FILENAME, OPENAI_KEY_FILENAME, ANTHROPIC_KEY_PATH, OPENAI_KEY_PATH, DEFAULT_ANTHROPIC_MODEL, DEFAULT_OPENAI_MODEL, DEFAULT_GEMINI_MODEL, DEFAULT_XAI_MODEL, DEFAULT_PERPLEXITY_MODEL, OPENAI_CLI_VENDOR_DEFAULT, CODEX_MEASURED_UNSERVABLE, DEFAULT_OPENAI_CLI_MODEL, DEFAULT_ANTHROPIC_CLI_MODEL, DEFAULT_GEMINI_CLI_MODEL, XAI_BASE_URL, PERPLEXITY_BASE_URL, DEFAULT_MAX_TOKENS, UNLIMITED_TOKENS_FALLBACK, _REASONING_PREFIXES, KeyGateError, CliClientError, DEFAULT_PROMPT_CACHE_TTL, CouncilResponse, _monotonicSource, ExternalAIClient, AnthropicClient, OpenAIClient, GeminiClient, _OpenAICompatibleClient, XAIClient, PerplexityClient, CLI_CALLS_FILENAME, DEFAULT_CLI_TIMEOUT_SECONDS, SubprocessError, CliClient, AnthropicCliClient, OpenAICliClient, GeminiCliClient, XAICliClient, PerplexityCliClient, MANUAL_END_MARKER, ManualClient, ValueError3, JSONDecodeError;
26439
26469
  var init_clients = __esm({
26440
26470
  "src/scripts/ai_council/clients.ts"() {
26441
26471
  "use strict";
@@ -26454,7 +26484,13 @@ var init_clients = __esm({
26454
26484
  DEFAULT_GEMINI_MODEL = "gemini-2.5-pro";
26455
26485
  DEFAULT_XAI_MODEL = "grok-4";
26456
26486
  DEFAULT_PERPLEXITY_MODEL = "sonar-pro";
26457
- DEFAULT_OPENAI_CLI_MODEL = "gpt-5";
26487
+ OPENAI_CLI_VENDOR_DEFAULT = "codex-default";
26488
+ CODEX_MEASURED_UNSERVABLE = /* @__PURE__ */ new Map([
26489
+ ["gpt-4o", "2026-08-15"],
26490
+ ["gpt-5", "2026-08-15"],
26491
+ ["gpt-5.1-codex", "2026-08-15"]
26492
+ ]);
26493
+ DEFAULT_OPENAI_CLI_MODEL = OPENAI_CLI_VENDOR_DEFAULT;
26458
26494
  DEFAULT_ANTHROPIC_CLI_MODEL = "claude-sonnet-4-5";
26459
26495
  DEFAULT_GEMINI_CLI_MODEL = "gemini-2.5-pro";
26460
26496
  XAI_BASE_URL = "https://api.x.ai/v1";
@@ -26677,7 +26713,7 @@ var init_clients = __esm({
26677
26713
  _client;
26678
26714
  constructor(opts = {}) {
26679
26715
  super();
26680
- this.model = opts.model ?? DEFAULT_OPENAI_MODEL;
26716
+ this.model = opts.model === void 0 || opts.model === OPENAI_CLI_VENDOR_DEFAULT ? DEFAULT_OPENAI_MODEL : opts.model;
26681
26717
  if (opts.client !== void 0 && opts.client !== null) {
26682
26718
  this._client = opts.client;
26683
26719
  return;
@@ -26997,6 +27033,19 @@ ${user_prompt}`;
26997
27033
  } catch {
26998
27034
  }
26999
27035
  }
27036
+ /**
27037
+ * Refuse a call this transport cannot serve, BEFORE spawning.
27038
+ *
27039
+ * `null` means "nothing known to be wrong" — deliberately not "verified
27040
+ * servable". No vendor CLI here publishes the set of models its
27041
+ * subscription tier accepts, so an allow-list would be invented and would
27042
+ * reject valid pins the day the vendor adds one. What CAN be stated is the
27043
+ * complement: models this estate has MEASURED being rejected. Subclasses
27044
+ * override; the base transport knows of none.
27045
+ */
27046
+ _preflight_transport() {
27047
+ return null;
27048
+ }
27000
27049
  /** Return text to send on stdin, or `null` to inherit caller's stdin. */
27001
27050
  _stdin_payload(system_prompt, user_prompt) {
27002
27051
  void system_prompt;
@@ -27123,6 +27172,17 @@ ${user_prompt}`;
27123
27172
  });
27124
27173
  }
27125
27174
  }
27175
+ const preflight = this._preflight_transport();
27176
+ if (preflight !== null) {
27177
+ return new CouncilResponse({
27178
+ provider: this.name,
27179
+ model: this.model,
27180
+ text: "",
27181
+ latency_ms: _elapsedMs(t0),
27182
+ error: preflight.code,
27183
+ metadata: { cli: true, transport: this.transport, detail: preflight.detail }
27184
+ });
27185
+ }
27126
27186
  const cmd = this._build_command(system_prompt, user_prompt, max_tokens);
27127
27187
  const stdinPayload = this._stdin_payload(system_prompt, user_prompt);
27128
27188
  let proc;
@@ -27263,23 +27323,23 @@ ${user_prompt}`;
27263
27323
  // documented "disable all tools" value.
27264
27324
  //
27265
27325
  // It also repairs a live failure, which is how the grant was found
27266
- // rather than reasoned about. The spawn inherits the caller's cwd
27267
- // (`_runSubprocess` sets none, deliberately — the worker-role marker
27268
- // below depends on the vendor CLI loading this repo's hook chain),
27269
- // so invoking the council from inside a repository made the child
27270
- // load that project's own instructions AND every tool definition.
27271
- // In this package that overflowed the model's context window
27272
- // outright: `is_error: true`, `"the request is ~214331 tokens
27326
+ // rather than reasoned about. AT THE TIME (2026-08-12) the spawn
27327
+ // inherited the caller's cwd, so invoking the council from inside a
27328
+ // repository made the child load that project's own instructions AND
27329
+ // every tool definition. In this package that overflowed the context
27330
+ // window outright: `is_error: true`, `"the request is ~214331 tokens
27273
27331
  // (limit 200000) … the rest is system prompt, tool definitions"`,
27274
- // exit 1, empty stderr — so the member reported as unavailable with
27275
- // no diagnosable cause. Measured 2026-08-12: identical call from
27276
- // `/tmp` exit 0, from the worktree exit 1, and adding this one flag
27277
- // returns the worktree call to exit 0.
27332
+ // exit 1, empty stderr — the member reported unavailable with no
27333
+ // diagnosable cause. Identical call from `/tmp` exit 0, from the
27334
+ // worktree exit 1, and this one flag returns the worktree to exit 0.
27278
27335
  //
27279
- // Dropping the tools rather than neutralising the cwd is the
27280
- // narrower fix of the two: it leaves the worker-role hook chain
27281
- // intact and removes only the capability the role never had a use
27282
- // for. The context saving is a consequence, not the justification.
27336
+ // TENSE CORRECTED 2026-08-16. This block read "`_runSubprocess` sets
27337
+ // none, deliberately" and "dropping the tools RATHER THAN
27338
+ // neutralising the cwd" — both false since `_runSubprocess` set
27339
+ // `cwd: os.tmpdir()` for a DIFFERENT contamination (a member
27340
+ // answering this repo's `end-review-nudge`). Two repairs, one
27341
+ // cause, neither aware of the other, so a reader here concluded
27342
+ // the cwd is inherited. Both are live; this flag bounds what a member may DO.
27283
27343
  "--tools",
27284
27344
  "",
27285
27345
  "--append-system-prompt",
@@ -27354,7 +27414,23 @@ ${user_prompt}`;
27354
27414
  void system_prompt;
27355
27415
  void user_prompt;
27356
27416
  void max_tokens;
27357
- return [this.binary, "exec", "--json", "--model", this.model, "-"];
27417
+ const modelArgs = this.model === OPENAI_CLI_VENDOR_DEFAULT ? [] : ["--model", this.model];
27418
+ return [this.binary, "exec", "--json", "--skip-git-repo-check", ...modelArgs, "-"];
27419
+ }
27420
+ /**
27421
+ * Refuse a pin this estate has measured the transport rejecting.
27422
+ *
27423
+ * The remedy is named because it is not guessable: removing the `model:`
27424
+ * line is the fix, and "remove a setting" is the last thing an operator
27425
+ * tries when a member is silent.
27426
+ */
27427
+ _preflight_transport() {
27428
+ const measuredOn = CODEX_MEASURED_UNSERVABLE.get(this.model);
27429
+ if (measuredOn === void 0) return null;
27430
+ return {
27431
+ code: "model_unsupported_on_transport",
27432
+ detail: `openai member pins model \`${this.model}\`, which the codex CLI (subscription transport) refused when measured on ${measuredOn}: "The '${this.model}' model is not supported when using Codex with a ChatGPT account." The CLI publishes no list of models it DOES accept, so there is no supported set to offer here \u2014 the one configuration measured to work is no pin at all. Fix: set \`model: codex-default\` on the openai member. Do NOT delete the \`model:\` line \u2014 an enabled member with no model fails config load, so that takes EVERY provider to NOT CONFIGURED. A pin is reachable only on the API transport.`
27433
+ };
27358
27434
  }
27359
27435
  /**
27360
27436
  * System and user prompt travel as ONE stdin payload, because `codex exec`
@@ -27376,6 +27452,7 @@ ${user_prompt}`;
27376
27452
  let text = "";
27377
27453
  let input_tokens = 0;
27378
27454
  let output_tokens = 0;
27455
+ let failure = null;
27379
27456
  const meta = {};
27380
27457
  for (let line of _splitlines6(stdout)) {
27381
27458
  line = line.trim();
@@ -27418,6 +27495,12 @@ ${user_prompt}`;
27418
27495
  text = chunks.join("\n").trim();
27419
27496
  }
27420
27497
  }
27498
+ if (_dictGet(itemObj, "type", void 0) === "agent_message") {
27499
+ const flat = _pyStr3(_dictGet(itemObj, "text", "")).trim();
27500
+ if (flat) {
27501
+ text = flat;
27502
+ }
27503
+ }
27421
27504
  if (_pyTruthy2(_dictGet(itemObj, "id", void 0))) {
27422
27505
  meta["item_id"] = _pyStr3(itemObj["id"]);
27423
27506
  }
@@ -27436,8 +27519,36 @@ ${user_prompt}`;
27436
27519
  if (_pyTruthy2(_dictGet(ev, "session_id", void 0))) {
27437
27520
  meta["session_id"] = _pyStr3(ev["session_id"]);
27438
27521
  }
27522
+ } else if (event_type === "turn.failed") {
27523
+ let err = _dictGet(ev, "error", null);
27524
+ if (!_pyTruthy2(err)) err = {};
27525
+ if (_isPlainObject7(err)) {
27526
+ const raw = _pyStr3(_dictGet(err, "message", ""));
27527
+ if (raw) failure = raw;
27528
+ }
27439
27529
  }
27440
27530
  }
27531
+ if (failure !== null && !text) {
27532
+ const unsupported = /not supported when using Codex/i.test(failure);
27533
+ return new CouncilResponse({
27534
+ provider: this.name,
27535
+ model: this.model,
27536
+ text: "",
27537
+ input_tokens,
27538
+ output_tokens,
27539
+ error: unsupported ? "model_unsupported_on_transport" : "turn_failed",
27540
+ metadata: {
27541
+ ...meta,
27542
+ // The vendor's own sentence, kept because it names the
27543
+ // model and the account type — the two facts an operator
27544
+ // needs and neither of which the error code carries.
27545
+ detail: failure.slice(0, 400),
27546
+ ...unsupported ? {
27547
+ hint: "the codex subscription transport serves no explicitly pinned model measured so far \u2014 set `model: codex-default` on the openai member; deleting the line fails config load"
27548
+ } : {}
27549
+ }
27550
+ });
27551
+ }
27441
27552
  return new CouncilResponse({
27442
27553
  provider: this.name,
27443
27554
  model: this.model,
@@ -28828,7 +28939,7 @@ function _build_member(name, cfg, default_mode = "auto", ignored = []) {
28828
28939
  }
28829
28940
  entries.push(entry);
28830
28941
  }
28831
- if (member_enabled && model && !entries.includes(model)) {
28942
+ if (member_enabled && model && model !== OPENAI_CLI_VENDOR_DEFAULT && !entries.includes(model)) {
28832
28943
  throw new CouncilConfigError(
28833
28944
  `members.${name}.model_ladder must include the active \`model\` (${_pyReprStr(model)}); got ${_pyRepr6(entries)}.`
28834
28945
  );
@@ -29066,6 +29177,7 @@ var init_config = __esm({
29066
29177
  "use strict";
29067
29178
  import_yaml5 = __toESM(require_dist(), 1);
29068
29179
  init_user_global_paths();
29180
+ init_clients();
29069
29181
  init_quorum();
29070
29182
  _VALID_PROVIDERS = /* @__PURE__ */ new Set([
29071
29183
  "anthropic",
@@ -29716,6 +29828,9 @@ function classifyCliFailure(raw) {
29716
29828
  if (s === "cli_unsupported" || s === "unsupported" || s === "parse_failed") {
29717
29829
  return "cli_unsupported";
29718
29830
  }
29831
+ if (s.startsWith("model_unservable") || s.includes("is not supported when using codex")) {
29832
+ return "model_unservable";
29833
+ }
29719
29834
  if (s === "timeout") return "timeout";
29720
29835
  if (s === "cli_quota_exhausted" || s === "quota_exhausted") return "quota_exhausted";
29721
29836
  if (/^exit_5\d\d$/.test(s) || s === "server_error" || s === "os") return "server_error";
@@ -30484,8 +30599,8 @@ function ruleFileArrives(sourcePath, scope) {
30484
30599
  if (!sourcePath.endsWith(".md")) {
30485
30600
  return true;
30486
30601
  }
30487
- const basename21 = path52.basename(sourcePath);
30488
- if (COMPAT_ALWAYS_EXCLUDED.includes(basename21)) {
30602
+ const basename22 = path52.basename(sourcePath);
30603
+ if (COMPAT_ALWAYS_EXCLUDED.includes(basename22)) {
30489
30604
  return false;
30490
30605
  }
30491
30606
  return rule_in_scope(sourcePath, scope.workspaces, scope.packs, scope.roles ?? null);
@@ -33345,9 +33460,9 @@ function detectToolPresence(root) {
33345
33460
  agentsMd: existsSync25(join49(root, "AGENTS.md"))
33346
33461
  };
33347
33462
  }
33348
- function isDir3(path75) {
33463
+ function isDir3(path77) {
33349
33464
  try {
33350
- return statSync32(path75).isDirectory();
33465
+ return statSync32(path77).isDirectory();
33351
33466
  } catch {
33352
33467
  return false;
33353
33468
  }
@@ -42706,8 +42821,8 @@ function aggregate_scores(findings, scores) {
42706
42821
  by_id.get(s.finding_id).push(s);
42707
42822
  }
42708
42823
  const out = /* @__PURE__ */ new Map();
42709
- for (const [fid, fs78] of by_id) {
42710
- if (fs78.length === 0) {
42824
+ for (const [fid, fs80] of by_id) {
42825
+ if (fs80.length === 0) {
42711
42826
  out.set(
42712
42827
  fid,
42713
42828
  new ConsensusMetadata({
@@ -42723,13 +42838,13 @@ function aggregate_scores(findings, scores) {
42723
42838
  );
42724
42839
  continue;
42725
42840
  }
42726
- const mean2 = fs78.reduce((acc, s) => acc + s.score, 0) / fs78.length;
42727
- const agree_rate = fs78.filter((s) => s.agree).length / fs78.length;
42841
+ const mean2 = fs80.reduce((acc, s) => acc + s.score, 0) / fs80.length;
42842
+ const agree_rate = fs80.filter((s) => s.agree).length / fs80.length;
42728
42843
  const strength = mean2 / 10 * agree_rate;
42729
- const dissent = fs78.filter((s) => !s.agree).length;
42730
- const concur = fs78.filter((s) => s.agree).length;
42731
- const scorers = fs78.map((s) => s.scorer);
42732
- const dissent_reasons = fs78.filter((s) => !s.agree).map((s) => [s.scorer, s.reason]);
42844
+ const dissent = fs80.filter((s) => !s.agree).length;
42845
+ const concur = fs80.filter((s) => s.agree).length;
42846
+ const scorers = fs80.map((s) => s.scorer);
42847
+ const dissent_reasons = fs80.filter((s) => !s.agree).map((s) => [s.scorer, s.reason]);
42733
42848
  const mean_rounded = pyRound(mean2, 2);
42734
42849
  out.set(
42735
42850
  fid,
@@ -44353,7 +44468,8 @@ ${body}`);
44353
44468
  function _render_quorum_line(q) {
44354
44469
  const verdict = q.status === "concluded" ? "concluded" : "INCONCLUSIVE \u2014 release gate holds";
44355
44470
  const solo = isSoloConcluded(q) ? " \xB7 **solo** \u2014 one voice concluded this pass" : "";
44356
- return `**Quorum:** ${q.present}/${q.total} present, needed ${q.threshold} \u2014 ${verdict}.${solo}`;
44471
+ const degraded = q.present < q.total ? ` \u26A0\uFE0F DEGRADED \u2014 ${String(q.total - q.present)} member(s) did not answer; this is not convergence.` : "";
44472
+ return `**Quorum:** ${q.present}/${q.total} present, needed ${q.threshold} \u2014 ${verdict}.${solo}${degraded}`;
44357
44473
  }
44358
44474
  function _render_absent_members(absent) {
44359
44475
  const lines = ["### Absent Members", ""];
@@ -45714,7 +45830,9 @@ function resolve_low_impact(question_text, plan, clients, price_table = null, no
45714
45830
  const pinned = model_downgrade.model_tier_override?.[member.name];
45715
45831
  const cheapest = member.model_ladder.length > 0 ? member.model_ladder[0] : null;
45716
45832
  const target = pinned && pinned.length > 0 ? pinned : cheapest;
45717
- if (target && target !== client.model) {
45833
+ const unpinned = client.model === OPENAI_CLI_VENDOR_DEFAULT;
45834
+ const overrideWins = pinned !== void 0 && pinned.length > 0;
45835
+ if (target && target !== client.model && (!unpinned || overrideWins)) {
45718
45836
  client.model = target;
45719
45837
  }
45720
45838
  }
@@ -46020,6 +46138,7 @@ var init_low_impact = __esm({
46020
46138
  "src/scripts/ai_council/low_impact.ts"() {
46021
46139
  "use strict";
46022
46140
  init_value_ladder();
46141
+ init_clients();
46023
46142
  init_orchestrator();
46024
46143
  _INPUT_RATIO = 0.6;
46025
46144
  FastPathPlan = class {
@@ -49089,7 +49208,7 @@ function run_enveloped(spec) {
49089
49208
  if (cmd === void 0) {
49090
49209
  throw new Error("envelope argv must not be empty");
49091
49210
  }
49092
- return new Promise((resolve43) => {
49211
+ return new Promise((resolve44) => {
49093
49212
  execFile(
49094
49213
  cmd,
49095
49214
  args,
@@ -49112,7 +49231,7 @@ function run_enveloped(spec) {
49112
49231
  }
49113
49232
  const [out, outTrunc] = _truncate(String(stdout ?? ""), spec.max_output_bytes);
49114
49233
  const [err, errTrunc] = _truncate(String(stderr ?? ""), spec.max_output_bytes);
49115
- resolve43({
49234
+ resolve44({
49116
49235
  exit_code: timedOut ? null : exitCode,
49117
49236
  timed_out: timedOut,
49118
49237
  stdout: out,
@@ -49132,9 +49251,977 @@ var init_safety_envelope = __esm({
49132
49251
  }
49133
49252
  });
49134
49253
 
49135
- // src/scripts/mcp_server/__main__.ts
49254
+ // src/scripts/_lib/skill_catalogue.ts
49255
+ var skill_catalogue_exports = {};
49256
+ __export(skill_catalogue_exports, {
49257
+ DEFAULT_CATALOGUE_ROOTS: () => DEFAULT_CATALOGUE_ROOTS,
49258
+ OBSERVATION_LOG: () => OBSERVATION_LOG,
49259
+ analyzeSelector: () => analyzeSelector,
49260
+ buildHostEventRecord: () => buildHostEventRecord,
49261
+ buildNoTruncationRecord: () => buildNoTruncationRecord,
49262
+ buildObservationRecord: () => buildObservationRecord,
49263
+ catalogueLimitWarning: () => catalogueLimitWarning,
49264
+ classifyHostProjection: () => classifyHostProjection,
49265
+ formatPerHostVerdicts: () => formatPerHostVerdicts,
49266
+ formatProjectionModes: () => formatProjectionModes,
49267
+ formatReport: () => formatReport,
49268
+ headlineRecordPerHost: () => headlineRecordPerHost,
49269
+ knownHostLimits: () => knownHostLimits,
49270
+ measureCatalogueVolume: () => measureCatalogueVolume,
49271
+ migrationEligibility: () => migrationEligibility,
49272
+ migrationPromptLines: () => migrationPromptLines,
49273
+ observationSourceOf: () => observationSourceOf,
49274
+ parseHostBudgetEvent: () => parseHostBudgetEvent,
49275
+ readObservationLog: () => readObservationLog,
49276
+ readProjectedCatalogue: () => readProjectedCatalogue,
49277
+ resolveSkillsRoot: () => resolveSkillsRoot,
49278
+ truncationModeOf: () => truncationModeOf
49279
+ });
49280
+ import * as fs77 from "node:fs";
49281
+ import * as path73 from "node:path";
49282
+ function resolveSkillsRoot(workspaceRoot) {
49283
+ for (const candidate of DEFAULT_CATALOGUE_ROOTS) {
49284
+ const abs = path73.join(workspaceRoot, candidate);
49285
+ try {
49286
+ if (fs77.existsSync(abs) && fs77.statSync(abs).isDirectory()) return abs;
49287
+ } catch {
49288
+ }
49289
+ }
49290
+ return null;
49291
+ }
49292
+ function frontmatterOf(content) {
49293
+ if (!content.startsWith("---")) return "";
49294
+ const end = content.indexOf("\n---", 3);
49295
+ if (end === -1) return "";
49296
+ return content.slice(3, end);
49297
+ }
49298
+ function topLevelKeys(frontmatter) {
49299
+ const keys = [];
49300
+ for (const line of frontmatter.split("\n")) {
49301
+ const match = /^([A-Za-z_][A-Za-z0-9_-]*):/.exec(line);
49302
+ if (match) keys.push(match[1]);
49303
+ }
49304
+ return keys;
49305
+ }
49306
+ function descriptionOf(frontmatter) {
49307
+ const lines = frontmatter.split("\n");
49308
+ for (let i = 0; i < lines.length; i += 1) {
49309
+ const match = /^description:\s*(.*)$/.exec(lines[i]);
49310
+ if (!match) continue;
49311
+ const rest = match[1].trim();
49312
+ if (/^[|>][-+]?\d*$/.test(rest)) {
49313
+ const body = [];
49314
+ for (let j = i + 1; j < lines.length; j += 1) {
49315
+ const line = lines[j];
49316
+ if (line.trim() === "") {
49317
+ body.push("");
49318
+ continue;
49319
+ }
49320
+ if (!/^\s/.test(line)) break;
49321
+ body.push(line.trim());
49322
+ }
49323
+ return body.join(" ").trim();
49324
+ }
49325
+ return rest.replace(/^["']|["']$/g, "");
49326
+ }
49327
+ return "";
49328
+ }
49329
+ function readProjectedCatalogue(root) {
49330
+ const names = fs77.readdirSync(root).filter((n) => fs77.existsSync(path73.join(root, n, "SKILL.md"))).sort();
49331
+ return names.map((name, index) => {
49332
+ const content = fs77.readFileSync(path73.join(root, name, "SKILL.md"), "utf-8");
49333
+ const frontmatter = frontmatterOf(content);
49334
+ const description = descriptionOf(frontmatter);
49335
+ return {
49336
+ name,
49337
+ position: index + 1,
49338
+ hasDescription: description.length > 0,
49339
+ descriptionLength: description.length,
49340
+ description,
49341
+ frontmatterKeys: topLevelKeys(frontmatter)
49342
+ };
49343
+ });
49344
+ }
49345
+ function rangesAreDisjoint(a, b) {
49346
+ if (a.length === 0 || b.length === 0) return false;
49347
+ const maxA = Math.max(...a);
49348
+ const minA = Math.min(...a);
49349
+ const maxB = Math.max(...b);
49350
+ const minB = Math.min(...b);
49351
+ return maxA < minB || maxB < minA;
49352
+ }
49353
+ function analyzeSelector(projected, observedBare, observedDescribed) {
49354
+ const known = new Map(projected.map((e) => [e.name, e]));
49355
+ const bare = observedBare.filter((n) => known.has(n));
49356
+ const describedInput = observedDescribed ?? projected.map((e) => e.name).filter((n) => !bare.includes(n));
49357
+ const described = describedInput.filter((n) => known.has(n));
49358
+ const unknownObserved = [
49359
+ .../* @__PURE__ */ new Set([
49360
+ ...observedBare.filter((n) => !known.has(n)),
49361
+ ...(observedDescribed ?? []).filter((n) => !known.has(n))
49362
+ ])
49363
+ ].sort();
49364
+ const base = {
49365
+ entriesTotal: projected.length,
49366
+ observedBare: [...bare].sort(),
49367
+ observedDescribed: [...described].sort(),
49368
+ unknownObserved
49369
+ };
49370
+ if (bare.length === 0 || described.length === 0) {
49371
+ return {
49372
+ ...base,
49373
+ candidates: [],
49374
+ verdict: "insufficient-observation"
49375
+ };
49376
+ }
49377
+ const bareEntries = bare.map((n) => known.get(n));
49378
+ const describedEntries = described.map((n) => known.get(n));
49379
+ const candidates = [];
49380
+ const bareWithDesc = bareEntries.filter((e) => e.hasDescription).length;
49381
+ const describedWithoutDesc = describedEntries.filter((e) => !e.hasDescription).length;
49382
+ candidates.push({
49383
+ id: "declares-description",
49384
+ describes: "the projected SKILL.md declares a `description:`",
49385
+ separates: bareWithDesc === 0 && describedWithoutDesc === 0,
49386
+ detail: `${bareWithDesc}/${bareEntries.length} bare entries declare one; ${describedWithoutDesc}/${describedEntries.length} described entries do not`
49387
+ });
49388
+ const barePositions = bareEntries.map((e) => e.position);
49389
+ const describedPositions = describedEntries.map((e) => e.position);
49390
+ const positional = Math.max(...describedPositions) < Math.min(...barePositions);
49391
+ candidates.push({
49392
+ id: "positional-head",
49393
+ describes: "every described entry sorts before every bare entry (a head-N budget)",
49394
+ separates: positional,
49395
+ detail: positional ? `described top out at #${Math.max(...describedPositions)}, bare start at #${Math.min(...barePositions)}` : `described reach #${Math.max(...describedPositions)} while bare start at #${Math.min(...barePositions)} \u2014 the ranges overlap, so no head-N cut explains this`
49396
+ });
49397
+ const lengthSeparates = rangesAreDisjoint(
49398
+ bareEntries.map((e) => e.descriptionLength),
49399
+ describedEntries.map((e) => e.descriptionLength)
49400
+ );
49401
+ candidates.push({
49402
+ id: "description-length",
49403
+ describes: "description length ranges do not overlap between the groups",
49404
+ separates: lengthSeparates,
49405
+ detail: lengthSeparates ? "a length threshold separates the two groups" : "length ranges overlap"
49406
+ });
49407
+ const allKeys = /* @__PURE__ */ new Set();
49408
+ for (const entry of projected) for (const key of entry.frontmatterKeys) allKeys.add(key);
49409
+ for (const key of [...allKeys].sort()) {
49410
+ const bareHas = bareEntries.filter((e) => e.frontmatterKeys.includes(key)).length;
49411
+ const describedHas = describedEntries.filter(
49412
+ (e) => e.frontmatterKeys.includes(key)
49413
+ ).length;
49414
+ const separates = bareHas === 0 && describedHas === describedEntries.length || describedHas === 0 && bareHas === bareEntries.length;
49415
+ if (!separates) continue;
49416
+ candidates.push({
49417
+ id: `frontmatter:${key}`,
49418
+ describes: `presence of the \`${key}:\` frontmatter key`,
49419
+ separates: true,
49420
+ detail: `bare ${bareHas}/${bareEntries.length}, described ${describedHas}/${describedEntries.length}`
49421
+ });
49422
+ }
49423
+ return {
49424
+ ...base,
49425
+ candidates,
49426
+ verdict: candidates.some((c) => c.separates) ? "selector-found" : "no-selector"
49427
+ };
49428
+ }
49429
+ function truncationModeOf(record) {
49430
+ return record.truncation_mode ?? "per-entry";
49431
+ }
49432
+ function observationSourceOf(record) {
49433
+ return record.observation_source ?? "self-report";
49434
+ }
49435
+ function buildObservationRecord(report2, host, observedAt) {
49436
+ return {
49437
+ schema: 1,
49438
+ observed_at: observedAt,
49439
+ host,
49440
+ entries_total: report2.entriesTotal,
49441
+ bare_count: report2.observedBare.length,
49442
+ described_count: report2.observedDescribed.length,
49443
+ bare_names: report2.observedBare,
49444
+ verdict: report2.verdict,
49445
+ separating_candidates: report2.candidates.filter((c) => c.separates).map((c) => c.id),
49446
+ truncation_mode: "per-entry",
49447
+ observation_source: "self-report"
49448
+ };
49449
+ }
49450
+ function parseHostBudgetEvent(stream) {
49451
+ for (const line of stream.split("\n")) {
49452
+ const trimmed = line.trim();
49453
+ if (!trimmed.startsWith("{")) continue;
49454
+ let parsed;
49455
+ try {
49456
+ parsed = JSON.parse(trimmed);
49457
+ } catch {
49458
+ continue;
49459
+ }
49460
+ const item = parsed.item;
49461
+ if (item === void 0 || item === null || item.type !== "error") continue;
49462
+ const message = typeof item.message === "string" ? item.message : "";
49463
+ if (!/skills context budget/i.test(message)) continue;
49464
+ const dropped = /(\d+)\s+additional skills were not included/i.exec(message);
49465
+ if (!dropped) {
49466
+ return null;
49467
+ }
49468
+ return {
49469
+ droppedCount: Number.parseInt(dropped[1], 10),
49470
+ descriptionsStripped: /descriptions were removed/i.test(message)
49471
+ };
49472
+ }
49473
+ return null;
49474
+ }
49475
+ function buildHostEventRecord(host, observedAt, entriesOffered, event, projectedSkillCount, projectionMode) {
49476
+ return {
49477
+ schema: 1,
49478
+ observed_at: observedAt,
49479
+ host,
49480
+ // OUR projection, not the host's denominator. Measured 2026-08-15 they
49481
+ // are not the same number and there is no way to read the host's:
49482
+ // `~/.codex` projects 297 skills + 200 commands = 497 by this tool's
49483
+ // count, while the host reported dropping 393 — and a controlled +60
49484
+ // commands moved that by ZERO. Whatever codex counts, it is not this.
49485
+ // So `entries_total - dropped_count` is NOT a delivered count, and
49486
+ // nothing here computes one: a subtraction across two different
49487
+ // denominators produces a confident number describing neither.
49488
+ entries_total: entriesOffered,
49489
+ bare_count: 0,
49490
+ described_count: 0,
49491
+ bare_names: [],
49492
+ // There is nothing to separate: the host did not choose per entry, it
49493
+ // stripped all of them. `no-selector` would read as "we looked and
49494
+ // found none", which is not what happened.
49495
+ verdict: "insufficient-observation",
49496
+ separating_candidates: [],
49497
+ truncation_mode: "budget-strip-and-drop",
49498
+ observation_source: "host-event",
49499
+ dropped_count: event.droppedCount,
49500
+ ...projectedSkillCount === void 0 ? {} : { projected_skill_count: projectedSkillCount },
49501
+ ...projectionMode === void 0 ? {} : { projection_mode: projectionMode }
49502
+ };
49503
+ }
49504
+ function buildNoTruncationRecord(host, observedAt, entriesOffered, projectedSkillCount, projectionMode) {
49505
+ return {
49506
+ schema: 1,
49507
+ observed_at: observedAt,
49508
+ host,
49509
+ entries_total: entriesOffered,
49510
+ // BOTH counts stay 0, and neither is a claim that nothing arrived
49511
+ // described. The host published no per-entry breakdown at all — it
49512
+ // published nothing, which is what `truncation_mode: 'none'` records.
49513
+ // Writing `described_count: entriesOffered` would look like a
49514
+ // measurement and be an inference; 0 here means "not counted", exactly
49515
+ // as it does on the strip-and-drop record beside it.
49516
+ bare_count: 0,
49517
+ described_count: 0,
49518
+ bare_names: [],
49519
+ // Nothing arrived bare, so no property separated bare from described —
49520
+ // which is what `no-selector` states. Unlike the strip-and-drop case,
49521
+ // here the label is literally true rather than merely available.
49522
+ verdict: "no-selector",
49523
+ separating_candidates: [],
49524
+ truncation_mode: "none",
49525
+ observation_source: "host-event",
49526
+ dropped_count: 0,
49527
+ ...projectedSkillCount === void 0 ? {} : { projected_skill_count: projectedSkillCount },
49528
+ ...projectionMode === void 0 ? {} : { projection_mode: projectionMode }
49529
+ };
49530
+ }
49531
+ function markdownFilesUnder(root) {
49532
+ if (!fs77.existsSync(root)) return [];
49533
+ const out = [];
49534
+ const walk = (dir) => {
49535
+ for (const entry of fs77.readdirSync(dir, { withFileTypes: true })) {
49536
+ const full = path73.join(dir, entry.name);
49537
+ if (fs77.statSync(full).isDirectory()) walk(full);
49538
+ else if (entry.name.endsWith(".md")) out.push(full);
49539
+ }
49540
+ };
49541
+ walk(root);
49542
+ return out;
49543
+ }
49544
+ function measureCatalogueVolume(host, root) {
49545
+ const skillsRoot = path73.join(root, "skills");
49546
+ const skills = fs77.existsSync(skillsRoot) ? readProjectedCatalogue(skillsRoot) : [];
49547
+ const commandEntries = markdownFilesUnder(path73.join(root, "commands")).length;
49548
+ return {
49549
+ host,
49550
+ root,
49551
+ skillEntries: skills.length,
49552
+ commandEntries,
49553
+ artefacts: skills.length + commandEntries,
49554
+ descriptionBytes: skills.reduce((sum, e) => sum + Buffer.byteLength(e.description), 0)
49555
+ };
49556
+ }
49557
+ function classifyHostProjection(volume, counts) {
49558
+ const ambiguous = counts.scoped === counts.legacyAll;
49559
+ let matches = "indeterminate";
49560
+ if (!ambiguous) {
49561
+ if (volume.skillEntries === counts.scoped) matches = "scoped";
49562
+ else if (volume.skillEntries === counts.legacyAll) matches = "legacy-all";
49563
+ }
49564
+ return {
49565
+ host: volume.host,
49566
+ root: volume.root,
49567
+ installedSkills: volume.skillEntries,
49568
+ matches
49569
+ };
49570
+ }
49571
+ function formatProjectionModes(counts, rows) {
49572
+ const lines = [
49573
+ "projected SKILL counts by projection mode (package side, one walk):",
49574
+ ` scoped: ${counts.scoped}`,
49575
+ ` legacy-all: ${counts.legacyAll}`,
49576
+ ` pruned under scoped: ${counts.prunedUnderScoped}`,
49577
+ ` active packs under scoped: ${counts.activePacks.length}`,
49578
+ ""
49579
+ ];
49580
+ if (rows.length === 0) {
49581
+ lines.push(
49582
+ "No host root measured. Pass --host-root <dir> (repeatable) to read what",
49583
+ "each host currently holds against the two numbers above."
49584
+ );
49585
+ return lines.join("\n");
49586
+ }
49587
+ lines.push("installed host roots:");
49588
+ for (const row of [...rows].sort((a, b) => a.host.localeCompare(b.host))) {
49589
+ lines.push(` ${row.host}: ${row.installedSkills} skills \u2014 consistent with ${row.matches}`);
49590
+ }
49591
+ lines.push(
49592
+ "",
49593
+ "This is the PROJECTION half only, and it is a measurement: no default was",
49594
+ "read, changed, or recommended here. `indeterminate` means the root carries",
49595
+ "neither count \u2014 another suite, a plugin, or a stale install all produce it.",
49596
+ "No host limit is extrapolated from another host, and no delivered or",
49597
+ "survivor count is computed anywhere."
49598
+ );
49599
+ return lines.join("\n");
49600
+ }
49601
+ function _supersedes(candidate, incumbent) {
49602
+ if (candidate.observed_at !== incumbent.observed_at) {
49603
+ return candidate.observed_at > incumbent.observed_at;
49604
+ }
49605
+ return (candidate.dropped_count ?? 0) > (incumbent.dropped_count ?? 0);
49606
+ }
49607
+ function headlineRecordPerHost(records) {
49608
+ const out = /* @__PURE__ */ new Map();
49609
+ for (const record of records) {
49610
+ const previous = out.get(record.host);
49611
+ if (previous === void 0 || _supersedes(record, previous)) {
49612
+ out.set(record.host, record);
49613
+ }
49614
+ }
49615
+ return out;
49616
+ }
49617
+ function knownHostLimits(records) {
49618
+ const out = /* @__PURE__ */ new Map();
49619
+ const chosen = /* @__PURE__ */ new Map();
49620
+ for (const record of records) {
49621
+ if (truncationModeOf(record) !== "budget-strip-and-drop") continue;
49622
+ if (typeof record.dropped_count !== "number") continue;
49623
+ if (record.dropped_count <= 0) continue;
49624
+ const previous = chosen.get(record.host);
49625
+ if (previous !== void 0 && !_supersedes(record, previous)) continue;
49626
+ chosen.set(record.host, record);
49627
+ out.set(record.host, {
49628
+ host: record.host,
49629
+ droppedEntries: record.dropped_count,
49630
+ projectedVolume: record.entries_total,
49631
+ projectedSkills: record.projected_skill_count ?? null,
49632
+ observedAt: record.observed_at
49633
+ });
49634
+ }
49635
+ return out;
49636
+ }
49637
+ function catalogueLimitWarning(volume, limit) {
49638
+ if (limit === void 0) return null;
49639
+ if (limit.projectedSkills === null) return null;
49640
+ if (volume.skillEntries < limit.projectedSkills) return null;
49641
+ return `${volume.host}: deploying ${volume.skillEntries} skills. This host reported dropping ${limit.droppedEntries} entries from the model-visible list when last measured (${limit.observedAt}, at ${limit.projectedSkills} skills) \u2014 that much never reaches the model. Explain: \`agent-config exec capture_skill_catalogue --limits\``;
49642
+ }
49643
+ function migrationEligibility(host, resolvedMode, currentSkillCount, limits) {
49644
+ if (resolvedMode === "scoped") return { eligible: false, reason: "already-scoped" };
49645
+ const limit = limits.get(host);
49646
+ if (limit === void 0) return { eligible: false, reason: "no-observation-for-host" };
49647
+ if (limit.projectedSkills === null) return { eligible: false, reason: "observation-not-comparable" };
49648
+ if (limit.droppedEntries <= 0) return { eligible: false, reason: "no-truncation-observed" };
49649
+ if (currentSkillCount < limit.projectedSkills) {
49650
+ return { eligible: false, reason: "below-observed-skill-volume" };
49651
+ }
49652
+ return {
49653
+ eligible: true,
49654
+ reason: "eligible",
49655
+ droppedEntries: limit.droppedEntries,
49656
+ observedAt: limit.observedAt
49657
+ };
49658
+ }
49659
+ function migrationPromptLines(host, eligibility, settingsPath) {
49660
+ return [
49661
+ `${host}: ${eligibility.droppedEntries} catalogue entries never reach the model.`,
49662
+ `Measured on this machine (${eligibility.observedAt}). This install is on \`legacy-all\`,`,
49663
+ "which ships everything and lets the host drop what does not fit \u2014 silently.",
49664
+ "",
49665
+ "Scoping the projection to your active packs is the alternative. Nothing here",
49666
+ "changes it for you: `projection.mode` is yours to set, by hand or in the GUI.",
49667
+ "",
49668
+ ` edit ${settingsPath}`,
49669
+ " projection:",
49670
+ " mode: scoped",
49671
+ "",
49672
+ " or agent-config config (Settings \u2192 projection)",
49673
+ "",
49674
+ "Keeping `legacy-all` is a legitimate choice \u2014 this is a notification, not a gate."
49675
+ ];
49676
+ }
49677
+ function readObservationLog(logPath) {
49678
+ if (!fs77.existsSync(logPath)) return [];
49679
+ const out = [];
49680
+ for (const line of fs77.readFileSync(logPath, "utf-8").split("\n")) {
49681
+ const trimmed = line.trim();
49682
+ if (trimmed.length === 0) continue;
49683
+ try {
49684
+ out.push(JSON.parse(trimmed));
49685
+ } catch {
49686
+ continue;
49687
+ }
49688
+ }
49689
+ return out;
49690
+ }
49691
+ function formatPerHostVerdicts(records) {
49692
+ if (records.length === 0) return "no observations recorded yet.";
49693
+ const byHost = /* @__PURE__ */ new Map();
49694
+ for (const record of records) {
49695
+ const bucket = byHost.get(record.host) ?? [];
49696
+ bucket.push(record);
49697
+ byHost.set(record.host, bucket);
49698
+ }
49699
+ const headline = headlineRecordPerHost(records);
49700
+ const lines = [];
49701
+ for (const host of [...byHost.keys()].sort()) {
49702
+ const bucket = byHost.get(host);
49703
+ const latest = headline.get(host);
49704
+ const mode = truncationModeOf(latest);
49705
+ lines.push(
49706
+ `${host}: ${bucket.length} observation(s) \xB7 mode ${mode} \xB7 source ${observationSourceOf(latest)} \xB7 latest ${latest.observed_at}`
49707
+ );
49708
+ lines.push(
49709
+ mode === "budget-strip-and-drop" ? ` offered ${latest.entries_total}, dropped ${latest.dropped_count ?? "unknown"} \u2014 no per-entry selector exists on this host; the quantity is the budget.` : ` verdict ${latest.verdict} \xB7 ${latest.bare_count} bare of ${latest.entries_total}`
49710
+ );
49711
+ }
49712
+ const modes = new Set([...headline.values()].map((r) => truncationModeOf(r)));
49713
+ lines.push("");
49714
+ lines.push(
49715
+ modes.size > 1 ? `hosts: ${byHost.size} \xB7 truncation modes DIFFER (${[...modes].sort().join(", ")}) \u2014 a pooled verdict across them would describe neither host.` : `hosts: ${byHost.size} \xB7 all observed hosts truncate the same way (${[...modes][0] ?? "none"}).`
49716
+ );
49717
+ return lines.join("\n");
49718
+ }
49719
+ function formatReport(report2) {
49720
+ const lines = [];
49721
+ lines.push(`catalogue root: ${report2.catalogueRoot}`);
49722
+ lines.push(`entries: ${report2.entriesTotal}`);
49723
+ lines.push(
49724
+ `observed: ${report2.observedBare.length} bare \xB7 ${report2.observedDescribed.length} described (self-reported)`
49725
+ );
49726
+ if (report2.unknownObserved.length > 0) {
49727
+ lines.push(
49728
+ `\u26A0\uFE0F ${report2.unknownObserved.length} observed name(s) absent from the projection: ${report2.unknownObserved.join(", ")}`
49729
+ );
49730
+ }
49731
+ if (report2.verdict === "insufficient-observation") {
49732
+ lines.push("");
49733
+ lines.push(
49734
+ "verdict: insufficient-observation \u2014 an observation needs at least one bare AND one described entry to separate anything."
49735
+ );
49736
+ return lines.join("\n");
49737
+ }
49738
+ lines.push("");
49739
+ lines.push("selector candidates:");
49740
+ for (const candidate of report2.candidates) {
49741
+ lines.push(` ${candidate.separates ? "\u2705" : "\u274C"} ${candidate.id} \u2014 ${candidate.describes}`);
49742
+ lines.push(` ${candidate.detail}`);
49743
+ }
49744
+ lines.push("");
49745
+ lines.push(
49746
+ report2.verdict === "selector-found" ? "verdict: selector-found \u2014 a delivery fix can act on the separating property above." : "verdict: no-selector \u2014 no measured property separates the groups; the selector is host-internal on this evidence. Publish the null rather than guessing a fix."
49747
+ );
49748
+ return lines.join("\n");
49749
+ }
49750
+ var DEFAULT_CATALOGUE_ROOTS, OBSERVATION_LOG;
49751
+ var init_skill_catalogue = __esm({
49752
+ "src/scripts/_lib/skill_catalogue.ts"() {
49753
+ "use strict";
49754
+ DEFAULT_CATALOGUE_ROOTS = [".claude/skills", "src/skills"];
49755
+ OBSERVATION_LOG = path73.join(
49756
+ "agents",
49757
+ "evidence",
49758
+ "metrics",
49759
+ "skill-catalogue.jsonl"
49760
+ );
49761
+ }
49762
+ });
49763
+
49764
+ // src/scripts/skill_tools/score_skill_relevance.ts
49765
+ var score_skill_relevance_exports = {};
49766
+ __export(score_skill_relevance_exports, {
49767
+ DEFAULT_SKILLS_DIR: () => DEFAULT_SKILLS_DIR,
49768
+ ROOT: () => ROOT3,
49769
+ _SAMPLE: () => _SAMPLE,
49770
+ _parse_frontmatter: () => _parse_frontmatter2,
49771
+ _tokenize: () => _tokenize,
49772
+ main: () => main35,
49773
+ parse_args: () => parse_args7,
49774
+ rank: () => rank
49775
+ });
49776
+ import * as fs78 from "node:fs";
49136
49777
  import * as path74 from "node:path";
49137
- import { fileURLToPath as fileURLToPath47 } from "node:url";
49778
+ import { fileURLToPath as fileURLToPath46, pathToFileURL as pathToFileURL35 } from "node:url";
49779
+ function pyLen(s) {
49780
+ let n = 0;
49781
+ for (const _ of s) {
49782
+ n++;
49783
+ }
49784
+ return n;
49785
+ }
49786
+ function _tokenize(text) {
49787
+ const out = /* @__PURE__ */ new Set();
49788
+ const matches = text.toLowerCase().match(TOKEN_RE3) ?? [];
49789
+ for (const t of matches) {
49790
+ if (!STOPWORDS.has(t) && pyLen(t) > 2) {
49791
+ out.add(t);
49792
+ }
49793
+ }
49794
+ return out;
49795
+ }
49796
+ function _parse_frontmatter2(filePath) {
49797
+ const text = fs78.readFileSync(filePath, "utf-8");
49798
+ if (!text.startsWith("---")) {
49799
+ return {};
49800
+ }
49801
+ const end = text.indexOf("\n---", 3);
49802
+ if (end === -1) {
49803
+ return {};
49804
+ }
49805
+ const block = text.slice(3, end);
49806
+ const out = {};
49807
+ let currentListKey = null;
49808
+ for (const raw of _splitlines13(block)) {
49809
+ const line = _rstrip3(raw);
49810
+ if (!line || line.startsWith("#")) {
49811
+ continue;
49812
+ }
49813
+ if (currentListKey !== null && line.startsWith(" - ")) {
49814
+ const existing = out[currentListKey];
49815
+ const list = Array.isArray(existing) ? existing : [];
49816
+ if (!Array.isArray(existing)) {
49817
+ out[currentListKey] = list;
49818
+ }
49819
+ list.push(line.slice(4).trim());
49820
+ continue;
49821
+ }
49822
+ currentListKey = null;
49823
+ const m = /^([a-zA-Z_][\w-]*)[ \t\r\f\v]*:[ \t\r\f\v]*(.*)$/u.exec(line);
49824
+ if (!m) {
49825
+ continue;
49826
+ }
49827
+ const key = m[1];
49828
+ let val = m[2].trim();
49829
+ if (val === "") {
49830
+ currentListKey = key;
49831
+ continue;
49832
+ }
49833
+ if (val.startsWith('"') && val.endsWith('"')) {
49834
+ val = val.slice(1, -1);
49835
+ }
49836
+ out[key] = val;
49837
+ }
49838
+ return out;
49839
+ }
49840
+ function _splitlines13(s) {
49841
+ if (s === "") {
49842
+ return [];
49843
+ }
49844
+ return s.split(/\r\n|\r|\n/u);
49845
+ }
49846
+ function _rstrip3(s) {
49847
+ return s.replace(/\s+$/u, "");
49848
+ }
49849
+ function _load_skills(skillsDir) {
49850
+ const skills = [];
49851
+ for (const skillMd of _globSkillMd(skillsDir)) {
49852
+ const fm = _parse_frontmatter2(skillMd);
49853
+ const rawName = fm["name"];
49854
+ const name = _truthyStr(rawName) ? String(rawName) : path74.basename(path74.dirname(skillMd));
49855
+ const rawDesc = fm["description"];
49856
+ const desc = _truthyStr(rawDesc) ? String(rawDesc) : "";
49857
+ let personas = fm["personas"] ?? [];
49858
+ if (typeof personas === "string") {
49859
+ personas = [personas];
49860
+ }
49861
+ const personaList = [...personas];
49862
+ skills.push({
49863
+ name,
49864
+ description: desc,
49865
+ personas: personaList,
49866
+ terms: _tokenize(name + " " + desc)
49867
+ });
49868
+ }
49869
+ return skills;
49870
+ }
49871
+ function _truthyStr(v) {
49872
+ if (typeof v === "string") {
49873
+ return v !== "";
49874
+ }
49875
+ return false;
49876
+ }
49877
+ function _globSkillMd(root) {
49878
+ let names;
49879
+ try {
49880
+ names = fs78.readdirSync(root);
49881
+ } catch {
49882
+ return [];
49883
+ }
49884
+ const dirs = [];
49885
+ for (const name of names) {
49886
+ const dir = path74.join(root, name);
49887
+ const skillMd = path74.join(dir, "SKILL.md");
49888
+ try {
49889
+ if (fs78.statSync(dir).isDirectory() && fs78.statSync(skillMd).isFile()) {
49890
+ dirs.push(name);
49891
+ }
49892
+ } catch {
49893
+ }
49894
+ }
49895
+ dirs.sort();
49896
+ return dirs.map((name) => path74.join(root, name, "SKILL.md"));
49897
+ }
49898
+ function _score3(taskTerms, skill) {
49899
+ if (taskTerms.size === 0) {
49900
+ return 0;
49901
+ }
49902
+ const skillTerms = skill.terms;
49903
+ let inter = 0;
49904
+ for (const t of taskTerms) {
49905
+ if (skillTerms.has(t)) {
49906
+ inter++;
49907
+ }
49908
+ }
49909
+ const overlap = inter / Math.max(taskTerms.size, 1);
49910
+ let personaHit = 0;
49911
+ const taskLower = [...taskTerms].join(" ");
49912
+ for (const persona of skill.personas) {
49913
+ const slug = String(persona).toLowerCase();
49914
+ const parts = slug.split("-");
49915
+ if (taskLower.includes(slug) || parts.some((part) => taskTerms.has(part))) {
49916
+ personaHit = 1;
49917
+ break;
49918
+ }
49919
+ }
49920
+ return pyRound(overlap * 70 + personaHit * 30, 0);
49921
+ }
49922
+ function rank(task, skillsDir) {
49923
+ const taskTerms = _tokenize(task);
49924
+ const skills = _load_skills(skillsDir);
49925
+ const rows = [];
49926
+ for (const s of skills) {
49927
+ const score = _score3(taskTerms, s);
49928
+ if (score > 0) {
49929
+ rows.push([s.name, score, [...s.personas]]);
49930
+ }
49931
+ }
49932
+ rows.sort((a, b) => {
49933
+ if (b[1] !== a[1]) {
49934
+ return b[1] - a[1];
49935
+ }
49936
+ if (a[0] < b[0]) {
49937
+ return -1;
49938
+ }
49939
+ if (a[0] > b[0]) {
49940
+ return 1;
49941
+ }
49942
+ return 0;
49943
+ });
49944
+ return rows;
49945
+ }
49946
+ function _ljust4(s, w) {
49947
+ const len = pyLen(s);
49948
+ return len >= w ? s : s + " ".repeat(w - len);
49949
+ }
49950
+ function _rjust3(n) {
49951
+ const s = String(n);
49952
+ return s.length >= 3 ? s : " ".repeat(3 - s.length) + s;
49953
+ }
49954
+ function _print_human(rowsIn, top) {
49955
+ let rows = rowsIn;
49956
+ if (top) {
49957
+ rows = rows.slice(0, top);
49958
+ }
49959
+ if (rows.length === 0) {
49960
+ return ["(no relevant skills found)"];
49961
+ }
49962
+ const width = Math.max(...rows.map((r) => pyLen(r[0])));
49963
+ const lines = [];
49964
+ for (const [name, score, personas] of rows) {
49965
+ const personaStr = personas.length > 0 ? personas.join(", ") : "\u2014";
49966
+ lines.push(` ${_rjust3(score)} ${_ljust4(name, width)} ${personaStr}`);
49967
+ }
49968
+ return lines;
49969
+ }
49970
+ function _pyJsonStr3(s) {
49971
+ let out = '"';
49972
+ for (const ch of s) {
49973
+ const code = ch.codePointAt(0);
49974
+ if (ch === '"') {
49975
+ out += '\\"';
49976
+ } else if (ch === "\\") {
49977
+ out += "\\\\";
49978
+ } else if (ch === "\n") {
49979
+ out += "\\n";
49980
+ } else if (ch === "\r") {
49981
+ out += "\\r";
49982
+ } else if (ch === " ") {
49983
+ out += "\\t";
49984
+ } else if (ch === "\b") {
49985
+ out += "\\b";
49986
+ } else if (ch === "\f") {
49987
+ out += "\\f";
49988
+ } else if (code < 32) {
49989
+ out += `\\u${code.toString(16).padStart(4, "0")}`;
49990
+ } else if (code < 127) {
49991
+ out += ch;
49992
+ } else if (code <= 65535) {
49993
+ out += `\\u${code.toString(16).padStart(4, "0")}`;
49994
+ } else {
49995
+ const c = code - 65536;
49996
+ const hi = 55296 + (c >> 10);
49997
+ const lo = 56320 + (c & 1023);
49998
+ out += `\\u${hi.toString(16).padStart(4, "0")}\\u${lo.toString(16).padStart(4, "0")}`;
49999
+ }
50000
+ }
50001
+ return out + '"';
50002
+ }
50003
+ function pyJsonDumpsIndent2(obj, level = 0) {
50004
+ if (obj === null) {
50005
+ return "null";
50006
+ }
50007
+ if (typeof obj === "number") {
50008
+ return String(obj);
50009
+ }
50010
+ if (typeof obj === "string") {
50011
+ return _pyJsonStr3(obj);
50012
+ }
50013
+ if (obj === true) {
50014
+ return "true";
50015
+ }
50016
+ if (obj === false) {
50017
+ return "false";
50018
+ }
50019
+ if (Array.isArray(obj)) {
50020
+ if (obj.length === 0) {
50021
+ return "[]";
50022
+ }
50023
+ const pad2 = " ".repeat(2 * (level + 1));
50024
+ const closePad2 = " ".repeat(2 * level);
50025
+ return `[
50026
+ ${obj.map((v) => pad2 + pyJsonDumpsIndent2(v, level + 1)).join(",\n")}
50027
+ ${closePad2}]`;
50028
+ }
50029
+ const keys = Object.keys(obj);
50030
+ if (keys.length === 0) {
50031
+ return "{}";
50032
+ }
50033
+ const pad = " ".repeat(2 * (level + 1));
50034
+ const closePad = " ".repeat(2 * level);
50035
+ const parts = keys.map(
50036
+ (k) => `${pad}${_pyJsonStr3(k)}: ${pyJsonDumpsIndent2(obj[k], level + 1)}`
50037
+ );
50038
+ return `{
50039
+ ${parts.join(",\n")}
50040
+ ${closePad}}`;
50041
+ }
50042
+ function _argError10(message) {
50043
+ process.stderr.write(`${PROG5}: error: ${message}
50044
+ `);
50045
+ process.exit(2);
50046
+ }
50047
+ function _parseInt2(raw) {
50048
+ if (!/^[+-]?\d+$/u.test(raw.trim())) {
50049
+ _argError10(`argument --top: invalid int value: '${raw}'`);
50050
+ }
50051
+ return parseInt(raw, 10);
50052
+ }
50053
+ function parse_args7(argv) {
50054
+ const args = {
50055
+ task: "",
50056
+ skills_dir: DEFAULT_SKILLS_DIR,
50057
+ top: 0,
50058
+ json: false,
50059
+ sample: false
50060
+ };
50061
+ for (let i = 0; i < argv.length; i++) {
50062
+ const a = argv[i];
50063
+ if (a === "--json") {
50064
+ args.json = true;
50065
+ } else if (a === "--sample") {
50066
+ args.sample = true;
50067
+ } else if (a === "--task") {
50068
+ const v = argv[++i];
50069
+ if (v === void 0) {
50070
+ _argError10("argument --task: expected one argument");
50071
+ }
50072
+ args.task = v;
50073
+ } else if (a.startsWith("--task=")) {
50074
+ args.task = a.slice("--task=".length);
50075
+ } else if (a === "--skills-dir") {
50076
+ const v = argv[++i];
50077
+ if (v === void 0) {
50078
+ _argError10("argument --skills-dir: expected one argument");
50079
+ }
50080
+ args.skills_dir = v;
50081
+ } else if (a.startsWith("--skills-dir=")) {
50082
+ args.skills_dir = a.slice("--skills-dir=".length);
50083
+ } else if (a === "--top") {
50084
+ const v = argv[++i];
50085
+ if (v === void 0) {
50086
+ _argError10("argument --top: expected one argument");
50087
+ }
50088
+ args.top = _parseInt2(v);
50089
+ } else if (a.startsWith("--top=")) {
50090
+ args.top = _parseInt2(a.slice("--top=".length));
50091
+ } else {
50092
+ _argError10(`unrecognized arguments: ${a}`);
50093
+ }
50094
+ }
50095
+ return args;
50096
+ }
50097
+ function main35(argv = null) {
50098
+ const args = parse_args7(argv ?? process.argv.slice(2));
50099
+ const task = args.sample ? _SAMPLE.task : args.task;
50100
+ if (!task) {
50101
+ _argError10("--task is required (or pass --sample)");
50102
+ }
50103
+ const rows = rank(task, args.skills_dir);
50104
+ if (args.json) {
50105
+ const sliced = args.top ? rows.slice(0, args.top) : rows;
50106
+ const payload = sliced.map(([n, s, p]) => ({ name: n, score: s, personas: p }));
50107
+ process.stdout.write(pyJsonDumpsIndent2({ task, ranked: payload }));
50108
+ process.stdout.write("\n");
50109
+ } else {
50110
+ const lines = _print_human(rows, args.top || null);
50111
+ process.stdout.write(lines.join("\n") + "\n");
50112
+ }
50113
+ return 0;
50114
+ }
50115
+ function _isCliEntry32() {
50116
+ if (process.argv[1] === void 0) {
50117
+ return false;
50118
+ }
50119
+ const argvUrl = pathToFileURL35(path74.resolve(process.argv[1])).href;
50120
+ if (import.meta.url === argvUrl) {
50121
+ return true;
50122
+ }
50123
+ try {
50124
+ const here = fs78.realpathSync(fileURLToPath46(import.meta.url));
50125
+ const argv = fs78.realpathSync(path74.resolve(process.argv[1]));
50126
+ return here === argv;
50127
+ } catch {
50128
+ return false;
50129
+ }
50130
+ }
50131
+ var _HERE23, ROOT3, DEFAULT_SKILLS_DIR, TOKEN_RE3, STOPWORDS, PROG5, _SAMPLE, _isMain5;
50132
+ var init_score_skill_relevance = __esm({
50133
+ "src/scripts/skill_tools/score_skill_relevance.ts"() {
50134
+ "use strict";
50135
+ init_value_ladder();
50136
+ _HERE23 = fileURLToPath46(import.meta.url);
50137
+ ROOT3 = path74.resolve(path74.dirname(_HERE23), "..", "..", "..");
50138
+ DEFAULT_SKILLS_DIR = path74.join(ROOT3, ".agent-src.uncondensed", "skills");
50139
+ TOKEN_RE3 = /[a-z][a-z0-9]+/g;
50140
+ STOPWORDS = /* @__PURE__ */ new Set([
50141
+ "the",
50142
+ "a",
50143
+ "an",
50144
+ "and",
50145
+ "or",
50146
+ "but",
50147
+ "of",
50148
+ "for",
50149
+ "with",
50150
+ "to",
50151
+ "in",
50152
+ "on",
50153
+ "at",
50154
+ "by",
50155
+ "from",
50156
+ "as",
50157
+ "is",
50158
+ "are",
50159
+ "was",
50160
+ "were",
50161
+ "be",
50162
+ "been",
50163
+ "this",
50164
+ "that",
50165
+ "these",
50166
+ "those",
50167
+ "it",
50168
+ "its",
50169
+ "use",
50170
+ "when",
50171
+ "even",
50172
+ "via",
50173
+ "via:",
50174
+ "into",
50175
+ "onto",
50176
+ "use:",
50177
+ "skill",
50178
+ "skills",
50179
+ "task",
50180
+ "tasks",
50181
+ "code",
50182
+ "file",
50183
+ "files",
50184
+ "doing",
50185
+ "make",
50186
+ "do",
50187
+ "go",
50188
+ "get",
50189
+ "set",
50190
+ "not",
50191
+ "no",
50192
+ "yes",
50193
+ "any",
50194
+ "some",
50195
+ "all",
50196
+ "one",
50197
+ "two",
50198
+ "new",
50199
+ "old",
50200
+ "user",
50201
+ "users",
50202
+ "our",
50203
+ "your",
50204
+ "their",
50205
+ "they",
50206
+ "we",
50207
+ "you",
50208
+ "i",
50209
+ "me"
50210
+ ]);
50211
+ PROG5 = "score_skill_relevance.py";
50212
+ _SAMPLE = {
50213
+ task: "build a livewire component for the user dashboard with reactive state"
50214
+ };
50215
+ _isMain5 = _isCliEntry32();
50216
+ if (_isMain5) {
50217
+ process.exitCode = main35();
50218
+ }
50219
+ }
50220
+ });
50221
+
50222
+ // src/scripts/mcp_server/__main__.ts
50223
+ import * as path76 from "node:path";
50224
+ import { fileURLToPath as fileURLToPath48 } from "node:url";
49138
50225
 
49139
50226
  // src/scripts/mcp_server/index.ts
49140
50227
  var __version__ = "0.1.0";
@@ -49181,9 +50268,9 @@ init_prompts();
49181
50268
  init_resources();
49182
50269
 
49183
50270
  // src/scripts/mcp_server/tools.ts
49184
- import * as fs77 from "node:fs";
49185
- import * as path73 from "node:path";
49186
- import { fileURLToPath as fileURLToPath46 } from "node:url";
50271
+ import * as fs79 from "node:fs";
50272
+ import * as path75 from "node:path";
50273
+ import { fileURLToPath as fileURLToPath47 } from "node:url";
49187
50274
 
49188
50275
  // src/scripts/chat_history.ts
49189
50276
  init_memory_lookup();
@@ -51416,9 +52503,9 @@ function _strip8(s) {
51416
52503
  }
51417
52504
  function _resolvePath2(p) {
51418
52505
  try {
51419
- return fs77.realpathSync(p);
52506
+ return fs79.realpathSync(p);
51420
52507
  } catch {
51421
- return path73.resolve(p);
52508
+ return path75.resolve(p);
51422
52509
  }
51423
52510
  }
51424
52511
  function _resolveConsumerRoot(override) {
@@ -51428,14 +52515,14 @@ function _resolveConsumerRoot(override) {
51428
52515
  return _resolvePath2(process.cwd());
51429
52516
  }
51430
52517
  function _relativeUnder(target, root) {
51431
- const rel = path73.relative(root, target);
52518
+ const rel = path75.relative(root, target);
51432
52519
  if (rel === "") {
51433
52520
  return "";
51434
52521
  }
51435
- if (rel.startsWith("..") || path73.isAbsolute(rel)) {
52522
+ if (rel.startsWith("..") || path75.isAbsolute(rel)) {
51436
52523
  return null;
51437
52524
  }
51438
- return rel.split(path73.sep).join("/");
52525
+ return rel.split(path75.sep).join("/");
51439
52526
  }
51440
52527
  function _reprStrList(values) {
51441
52528
  return `[${values.map((v) => `'${v}'`).join(", ")}]`;
@@ -51444,11 +52531,11 @@ function _validateInTreePath(raw, consumerRoot) {
51444
52531
  const root = _resolvePath2(consumerRoot);
51445
52532
  let target;
51446
52533
  if (raw === null || raw === void 0 || raw === "") {
51447
- target = path73.join(root, "agents", "runtime", ".agent-chat-history");
51448
- } else if (path73.isAbsolute(raw)) {
52534
+ target = path75.join(root, "agents", "runtime", ".agent-chat-history");
52535
+ } else if (path75.isAbsolute(raw)) {
51449
52536
  target = _resolvePath2(raw);
51450
52537
  } else {
51451
- target = _resolvePath2(path73.join(root, raw));
52538
+ target = _resolvePath2(path75.join(root, raw));
51452
52539
  }
51453
52540
  const rel = _relativeUnder(target, root);
51454
52541
  if (rel === null) {
@@ -51486,11 +52573,11 @@ async function _lintSkillsHandler(args, consumerRoot) {
51486
52573
  if (typeof raw !== "string") {
51487
52574
  throw new Error("'paths' entries must be strings");
51488
52575
  }
51489
- const resolved = path73.isAbsolute(raw) ? _resolvePath2(raw) : _resolvePath2(path73.join(root, raw));
52576
+ const resolved = path75.isAbsolute(raw) ? _resolvePath2(raw) : _resolvePath2(path75.join(root, raw));
51490
52577
  if (_relativeUnder(resolved, root) === null) {
51491
52578
  throw new Error(`path escapes consumer_root: ${resolved}`);
51492
52579
  }
51493
- if (fs77.existsSync(resolved)) {
52580
+ if (fs79.existsSync(resolved)) {
51494
52581
  paths.push(resolved);
51495
52582
  }
51496
52583
  }
@@ -51527,7 +52614,7 @@ async function _chatHistoryAppendHandler(args, consumerRoot) {
51527
52614
  if (typeof minSchema !== "number" || !Number.isInteger(minSchema) || typeof minSchema === "boolean") {
51528
52615
  throw new Error("'min_schema_version' must be an integer");
51529
52616
  }
51530
- const existingHeader = fs77.existsSync(target) ? _chReadHeader(target) : null;
52617
+ const existingHeader = fs79.existsSync(target) ? _chReadHeader(target) : null;
51531
52618
  const observed = existingHeader !== null && typeof existingHeader === "object" ? _pyInt7(existingHeader.v ?? 0) : SCHEMA_VERSION3;
51532
52619
  if (observed < minSchema) {
51533
52620
  throw new Error(`chat-history schema ${observed} below required ${minSchema}`);
@@ -51542,8 +52629,8 @@ async function _chatHistoryAppendHandler(args, consumerRoot) {
51542
52629
  session: session === void 0 ? null : session
51543
52630
  };
51544
52631
  }
51545
- if (!fs77.existsSync(target) || _chReadHeader(target) === null) {
51546
- fs77.mkdirSync(path73.dirname(target), { recursive: true });
52632
+ if (!fs79.existsSync(target) || _chReadHeader(target) === null) {
52633
+ fs79.mkdirSync(path75.dirname(target), { recursive: true });
51547
52634
  _chInit(target);
51548
52635
  }
51549
52636
  _chAppend(entry, target, session ?? null);
@@ -51587,7 +52674,7 @@ async function _chatHistoryReadHandler(args, consumerRoot) {
51587
52674
  if (detail !== "full" && detail !== "index") {
51588
52675
  throw new Error("'detail' must be 'index' or 'full'");
51589
52676
  }
51590
- if (!fs77.existsSync(target)) {
52677
+ if (!fs79.existsSync(target)) {
51591
52678
  return { path: target, entries: [], count: 0 };
51592
52679
  }
51593
52680
  if (around !== void 0 && around !== null) {
@@ -51689,8 +52776,8 @@ async function _memoryStatusHandler(_args, consumerRoot) {
51689
52776
  }
51690
52777
  return result;
51691
52778
  }
51692
- var _PACKAGE_ROOT3 = path73.resolve(
51693
- path73.dirname(fileURLToPath46(import.meta.url)),
52779
+ var _PACKAGE_ROOT3 = path75.resolve(
52780
+ path75.dirname(fileURLToPath47(import.meta.url)),
51694
52781
  "..",
51695
52782
  "..",
51696
52783
  ".."
@@ -51734,10 +52821,10 @@ async function _memorySignalHandler(args, consumerRoot) {
51734
52821
  async function _roadmapProgressHandler(args, consumerRoot) {
51735
52822
  const dryRun = _pyTruthy6(args.dry_run ?? false);
51736
52823
  const root = _resolvePath2(consumerRoot);
51737
- const roadmapRoot = path73.join(root, "agents", "roadmaps");
51738
- const target = path73.join(root, "agents", "roadmaps-progress.md");
52824
+ const roadmapRoot = path75.join(root, "agents", "roadmaps");
52825
+ const target = path75.join(root, "agents", "roadmaps-progress.md");
51739
52826
  const urp = await Promise.resolve().then(() => (init_update_roadmap_progress(), update_roadmap_progress_exports));
51740
- if (!fs77.existsSync(roadmapRoot)) {
52827
+ if (!fs79.existsSync(roadmapRoot)) {
51741
52828
  return {
51742
52829
  written: false,
51743
52830
  path: target,
@@ -51752,7 +52839,7 @@ async function _roadmapProgressHandler(args, consumerRoot) {
51752
52839
  const stepsDone = roadmaps.reduce((s, r) => s + r.done, 0);
51753
52840
  const stepsTotal = roadmaps.reduce((s, r) => s + r.total_active, 0);
51754
52841
  if (!dryRun) {
51755
- fs77.writeFileSync(target, content, "utf-8");
52842
+ fs79.writeFileSync(target, content, "utf-8");
51756
52843
  }
51757
52844
  return {
51758
52845
  written: !dryRun,
@@ -51773,11 +52860,11 @@ async function _roadmapArchiveHandler(_args, consumerRoot) {
51773
52860
  let dashboardRegenerated = false;
51774
52861
  if (archived.length > 0) {
51775
52862
  const urp = await Promise.resolve().then(() => (init_update_roadmap_progress(), update_roadmap_progress_exports));
51776
- const roadmapRoot = path73.join(root, "agents", "roadmaps");
51777
- if (fs77.existsSync(roadmapRoot)) {
52863
+ const roadmapRoot = path75.join(root, "agents", "roadmaps");
52864
+ if (fs79.existsSync(roadmapRoot)) {
51778
52865
  const roadmaps = urp.collect(roadmapRoot);
51779
- fs77.writeFileSync(
51780
- path73.join(root, "agents", "roadmaps-progress.md"),
52866
+ fs79.writeFileSync(
52867
+ path75.join(root, "agents", "roadmaps-progress.md"),
51781
52868
  urp.render(roadmaps, urp.collect_bundles(root)),
51782
52869
  "utf-8"
51783
52870
  );
@@ -51793,9 +52880,9 @@ async function _roadmapArchiveHandler(_args, consumerRoot) {
51793
52880
  async function _capabilitiesIndexHandler(args, consumerRoot) {
51794
52881
  const check = _pyTruthy6(args.check ?? false);
51795
52882
  const cap = await Promise.resolve().then(() => (init_generate_capabilities_index(), generate_capabilities_index_exports));
51796
- const out = path73.join(_PACKAGE_ROOT3, "CAPABILITIES.yaml");
52883
+ const out = path75.join(_PACKAGE_ROOT3, "CAPABILITIES.yaml");
51797
52884
  const content = cap.build();
51798
- const onDisk = fs77.existsSync(out) ? fs77.readFileSync(out, "utf-8") : "";
52885
+ const onDisk = fs79.existsSync(out) ? fs79.readFileSync(out, "utf-8") : "";
51799
52886
  const drift = onDisk !== content;
51800
52887
  if (check) {
51801
52888
  return { check: true, written: false, drift, path: out };
@@ -51806,7 +52893,7 @@ async function _capabilitiesIndexHandler(args, consumerRoot) {
51806
52893
  `CAPABILITIES.yaml path escapes consumer_root: ${out} not under ${root}`
51807
52894
  );
51808
52895
  }
51809
- fs77.writeFileSync(out, content, "utf-8");
52896
+ fs79.writeFileSync(out, content, "utf-8");
51810
52897
  return { check: false, written: true, drift, path: out };
51811
52898
  }
51812
52899
  async function _runDoctorLeg(consumerRoot) {
@@ -51906,11 +52993,11 @@ async function _councilEstimateHandler(args, consumerRoot) {
51906
52993
  throw new Error("'depth' must be 'shallow' or 'deep' when provided");
51907
52994
  }
51908
52995
  const root = _resolvePath2(consumerRoot);
51909
- const target = path73.isAbsolute(inputRaw) ? _resolvePath2(inputRaw) : _resolvePath2(path73.join(root, inputRaw));
52996
+ const target = path75.isAbsolute(inputRaw) ? _resolvePath2(inputRaw) : _resolvePath2(path75.join(root, inputRaw));
51910
52997
  if (_relativeUnder(target, root) === null) {
51911
52998
  throw new Error(`path escapes consumer_root: ${target}`);
51912
52999
  }
51913
- if (!fs77.existsSync(target)) {
53000
+ if (!fs79.existsSync(target)) {
51914
53001
  throw new Error(`input_path not found: ${target}`);
51915
53002
  }
51916
53003
  const cli = await Promise.resolve().then(() => (init_council_cli(), council_cli_exports));
@@ -51972,18 +53059,18 @@ async function _runTestsHandler(args, consumerRoot) {
51972
53059
  if (typeof relPathRaw !== "string" || !_strip8(relPathRaw)) {
51973
53060
  throw new Error("'path' must be a non-empty string when provided");
51974
53061
  }
51975
- const resolved = path73.isAbsolute(relPathRaw) ? _resolvePath2(relPathRaw) : _resolvePath2(path73.join(root, relPathRaw));
53062
+ const resolved = path75.isAbsolute(relPathRaw) ? _resolvePath2(relPathRaw) : _resolvePath2(path75.join(root, relPathRaw));
51976
53063
  const rel = _relativeUnder(resolved, root);
51977
53064
  if (rel === null) {
51978
53065
  throw new Error(`path escapes consumer_root: ${resolved}`);
51979
53066
  }
51980
- if (!fs77.existsSync(resolved)) {
53067
+ if (!fs79.existsSync(resolved)) {
51981
53068
  throw new Error(`path not found: ${resolved}`);
51982
53069
  }
51983
53070
  relArg = rel;
51984
53071
  }
51985
- const vitestEntry = path73.join(root, "node_modules", "vitest", "vitest.mjs");
51986
- if (!fs77.existsSync(vitestEntry)) {
53072
+ const vitestEntry = path75.join(root, "node_modules", "vitest", "vitest.mjs");
53073
+ if (!fs79.existsSync(vitestEntry)) {
51987
53074
  throw new Error(
51988
53075
  "run_tests pilot supports vitest projects only \u2014 node_modules/vitest/vitest.mjs not found under consumer_root"
51989
53076
  );
@@ -52088,7 +53175,55 @@ async function _readResourceBodyHandler(args, consumerRoot) {
52088
53175
  body: resource.body
52089
53176
  };
52090
53177
  }
53178
+ async function _suggestSkillForTaskHandler(args, consumerRoot) {
53179
+ const task = typeof args["task"] === "string" ? args["task"] : "";
53180
+ if (task.trim() === "") {
53181
+ return { status: "error", error: "task must be a non-empty string", suggestions: [] };
53182
+ }
53183
+ const limitRaw = args["limit"];
53184
+ const limit = typeof limitRaw === "number" && Number.isFinite(limitRaw) ? Math.floor(limitRaw) : 5;
53185
+ const catalogue = await Promise.resolve().then(() => (init_skill_catalogue(), skill_catalogue_exports));
53186
+ const skillsDir = catalogue.resolveSkillsRoot(consumerRoot);
53187
+ if (skillsDir === null) {
53188
+ return {
53189
+ status: "no_catalogue",
53190
+ searched: [...catalogue.DEFAULT_CATALOGUE_ROOTS],
53191
+ consumer_root: consumerRoot,
53192
+ suggestions: []
53193
+ };
53194
+ }
53195
+ const { rank: rank2 } = await Promise.resolve().then(() => (init_score_skill_relevance(), score_skill_relevance_exports));
53196
+ const rows = rank2(task, skillsDir).slice(0, Math.max(1, limit));
53197
+ return {
53198
+ status: "ok",
53199
+ skills_root: skillsDir,
53200
+ suggestions: rows.map(([name, score, personas]) => ({ skill: name, score, personas }))
53201
+ };
53202
+ }
52091
53203
  var ALLOWLIST = {
53204
+ suggest_skill_for_task: {
53205
+ name: "suggest_skill_for_task",
53206
+ side_effect: "ro",
53207
+ description: "Match a free-form task description to the most relevant skills, ranked by a deterministic keyword scorer over SKILL.md frontmatter. Use when a skill you need is not in the catalogue the host delivered \u2014 a measured host dropped 402 entries from its model-visible list \u2014 so asking by name is impossible while asking by task is not. Read-only: no shell, no writes, and no skill bodies are returned, only names, scores and declared personas.",
53208
+ input_schema: {
53209
+ type: "object",
53210
+ required: ["task"],
53211
+ properties: {
53212
+ task: {
53213
+ type: "string",
53214
+ description: "Free-form description of the task to match skills against."
53215
+ },
53216
+ limit: {
53217
+ type: "integer",
53218
+ minimum: 1,
53219
+ default: 5,
53220
+ description: "Maximum number of skills to return. Defaults to 5."
53221
+ }
53222
+ },
53223
+ additionalProperties: false
53224
+ },
53225
+ handler: _suggestSkillForTaskHandler
53226
+ },
52092
53227
  lint_skills: {
52093
53228
  name: "lint_skills",
52094
53229
  side_effect: "ro",
@@ -52589,8 +53724,8 @@ function _strCmp3(a, b) {
52589
53724
  return a < b ? -1 : a > b ? 1 : 0;
52590
53725
  }
52591
53726
  function _pathlibCompare3(a, b) {
52592
- const pa = a.split(path73.sep);
52593
- const pb = b.split(path73.sep);
53727
+ const pa = a.split(path75.sep);
53728
+ const pb = b.split(path75.sep);
52594
53729
  const n = Math.min(pa.length, pb.length);
52595
53730
  for (let i = 0; i < n; i++) {
52596
53731
  const c = _strCmp3(pa[i], pb[i]);
@@ -52809,18 +53944,18 @@ async function _serveOverSdk(server) {
52809
53944
  const transport = new StdioServerTransport();
52810
53945
  await sdkServer.connect(transport);
52811
53946
  }
52812
- function main35() {
53947
+ function main36() {
52813
53948
  void run_stdio();
52814
53949
  }
52815
53950
 
52816
53951
  // src/scripts/mcp_server/__main__.ts
52817
- var _selfPath = fileURLToPath47(import.meta.url);
52818
- var _invokedDirectly2 = globalThis.__AC_MCP_BUNDLE_DIRECT === true || process.argv[1] !== void 0 && path74.resolve(process.argv[1]) === path74.resolve(_selfPath);
53952
+ var _selfPath = fileURLToPath48(import.meta.url);
53953
+ var _invokedDirectly2 = globalThis.__AC_MCP_BUNDLE_DIRECT === true || process.argv[1] !== void 0 && path76.resolve(process.argv[1]) === path76.resolve(_selfPath);
52819
53954
  if (_invokedDirectly2) {
52820
- main35();
53955
+ main36();
52821
53956
  }
52822
53957
  export {
52823
- main35 as main
53958
+ main36 as main
52824
53959
  };
52825
53960
  /*! Bundled license information:
52826
53961