avorelo 0.1.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 (258) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +56 -0
  3. package/bin/avorelo +9 -0
  4. package/package.json +135 -0
  5. package/scripts/README.md +40 -0
  6. package/scripts/cco-dashboard.js +252 -0
  7. package/scripts/cco-status.js +430 -0
  8. package/scripts/lib/activation/account-state.js +37 -0
  9. package/scripts/lib/activation/activation-runner.js +546 -0
  10. package/scripts/lib/activation/activation-self-healing.js +480 -0
  11. package/scripts/lib/activation/activation-state.js +83 -0
  12. package/scripts/lib/activation/activation-summary.js +191 -0
  13. package/scripts/lib/activation/adapters/claude-code.js +77 -0
  14. package/scripts/lib/activation/adapters/codex-cli.js +52 -0
  15. package/scripts/lib/activation/adapters/cursor.js +37 -0
  16. package/scripts/lib/activation/adapters/github-agent.js +39 -0
  17. package/scripts/lib/activation/adapters/terminal.js +42 -0
  18. package/scripts/lib/activation/adapters/vscode.js +39 -0
  19. package/scripts/lib/activation/adapters/windsurf.js +37 -0
  20. package/scripts/lib/activation/ai-surface-detector.js +151 -0
  21. package/scripts/lib/activation/connect-account.js +145 -0
  22. package/scripts/lib/activation/detect-environment.js +75 -0
  23. package/scripts/lib/activation/detect-hosts.js +62 -0
  24. package/scripts/lib/activation/format-activation-output.js +109 -0
  25. package/scripts/lib/activation/next-action.js +43 -0
  26. package/scripts/lib/activation/repair-engine.js +219 -0
  27. package/scripts/lib/activation-distribution-readiness.js +507 -0
  28. package/scripts/lib/adapter-conformance.js +176 -0
  29. package/scripts/lib/adapter-readiness.js +417 -0
  30. package/scripts/lib/adapter-safety-boundaries.js +335 -0
  31. package/scripts/lib/adapter-technical-readiness-gate.js +205 -0
  32. package/scripts/lib/agent-access-governance.js +455 -0
  33. package/scripts/lib/agent-enforcement.js +765 -0
  34. package/scripts/lib/agent-policy-profile.js +210 -0
  35. package/scripts/lib/agent-security/action-evaluator.js +507 -0
  36. package/scripts/lib/agent-security/adapter-registry.js +98 -0
  37. package/scripts/lib/agent-security/auto-policy.js +139 -0
  38. package/scripts/lib/agent-security/bounded-scan.js +93 -0
  39. package/scripts/lib/agent-security/enforcement-adapter.js +174 -0
  40. package/scripts/lib/agent-security/enforcement-engine.js +1129 -0
  41. package/scripts/lib/agent-security/file-write-adapter.js +183 -0
  42. package/scripts/lib/agent-security/file-write-rules.js +178 -0
  43. package/scripts/lib/agent-security/index.js +3342 -0
  44. package/scripts/lib/agent-security/instruction-risk.js +181 -0
  45. package/scripts/lib/agent-security/mcp-action-adapter.js +185 -0
  46. package/scripts/lib/agent-security/mcp-action-rules.js +184 -0
  47. package/scripts/lib/agent-security/package-action-adapter.js +175 -0
  48. package/scripts/lib/agent-security/package-action-rules.js +233 -0
  49. package/scripts/lib/agent-security/performance.js +148 -0
  50. package/scripts/lib/agent-security/permission-minimizer.js +403 -0
  51. package/scripts/lib/agent-security/scan-cache.js +74 -0
  52. package/scripts/lib/agent-security/source-trust.js +146 -0
  53. package/scripts/lib/ai-install-prompt.js +288 -0
  54. package/scripts/lib/ai-workspace-hygiene.js +1499 -0
  55. package/scripts/lib/alpha-activation.js +520 -0
  56. package/scripts/lib/alpha-feedback.js +263 -0
  57. package/scripts/lib/alpha-readiness-gate.js +332 -0
  58. package/scripts/lib/anti-gaming.js +169 -0
  59. package/scripts/lib/artifact-health.js +431 -0
  60. package/scripts/lib/attribution.js +180 -0
  61. package/scripts/lib/audit.js +289 -0
  62. package/scripts/lib/avorelo-skill-registry.js +810 -0
  63. package/scripts/lib/batch-jobs.js +71 -0
  64. package/scripts/lib/brain-pack.js +578 -0
  65. package/scripts/lib/brand-boundary.js +424 -0
  66. package/scripts/lib/brand.js +74 -0
  67. package/scripts/lib/browser-capability.js +1048 -0
  68. package/scripts/lib/browser-proof-preflight.js +321 -0
  69. package/scripts/lib/cache-readiness.js +187 -0
  70. package/scripts/lib/canonical-reentry.js +162 -0
  71. package/scripts/lib/capability-packs.js +314 -0
  72. package/scripts/lib/capability-recommender.js +512 -0
  73. package/scripts/lib/capability-registry.js +1059 -0
  74. package/scripts/lib/carry-forward-surfacing.js +194 -0
  75. package/scripts/lib/ccusage-adapter.js +188 -0
  76. package/scripts/lib/company-loop.js +1149 -0
  77. package/scripts/lib/config.js +637 -0
  78. package/scripts/lib/context-acquisition-plan.js +287 -0
  79. package/scripts/lib/context-budget-guard.js +170 -0
  80. package/scripts/lib/context-budget-scanner.js +257 -0
  81. package/scripts/lib/context-optimizer.js +715 -0
  82. package/scripts/lib/context-reduction-plan.js +178 -0
  83. package/scripts/lib/context-safety.js +88 -0
  84. package/scripts/lib/context-savings-engine.js +158 -0
  85. package/scripts/lib/cost-evidence.js +254 -0
  86. package/scripts/lib/cross-host-install-plan.js +308 -0
  87. package/scripts/lib/cross-host-install-readiness.js +237 -0
  88. package/scripts/lib/cross-host-value-flow.js +268 -0
  89. package/scripts/lib/dashboard.js +900 -0
  90. package/scripts/lib/design-partner-feedback.js +346 -0
  91. package/scripts/lib/entitlements.js +100 -0
  92. package/scripts/lib/execution-packet.js +559 -0
  93. package/scripts/lib/experimentation-events.js +547 -0
  94. package/scripts/lib/external-capability-compliance.js +107 -0
  95. package/scripts/lib/external-user-simulation.js +166 -0
  96. package/scripts/lib/failure-recovery-readiness.js +81 -0
  97. package/scripts/lib/failure-recovery.js +419 -0
  98. package/scripts/lib/feedback-intelligence.js +537 -0
  99. package/scripts/lib/feedback-signals.js +205 -0
  100. package/scripts/lib/file-integrity.js +68 -0
  101. package/scripts/lib/fsx.js +127 -0
  102. package/scripts/lib/full-readiness-gate.js +451 -0
  103. package/scripts/lib/guidance-builder.js +174 -0
  104. package/scripts/lib/hook-apply.js +1019 -0
  105. package/scripts/lib/hook-baseline.js +310 -0
  106. package/scripts/lib/hook-config-preview.js +275 -0
  107. package/scripts/lib/hook-contracts.js +290 -0
  108. package/scripts/lib/hook-safety-boundary-readiness.js +80 -0
  109. package/scripts/lib/host-capability-matrix.js +351 -0
  110. package/scripts/lib/host-support-context.js +254 -0
  111. package/scripts/lib/http-hook-action.js +538 -0
  112. package/scripts/lib/install-ai-readiness.js +84 -0
  113. package/scripts/lib/install-intake-risk.js +1037 -0
  114. package/scripts/lib/install-journey-intelligence.js +329 -0
  115. package/scripts/lib/intervention-guidance.js +57 -0
  116. package/scripts/lib/known-limitations.js +115 -0
  117. package/scripts/lib/l8-path-truth.js +146 -0
  118. package/scripts/lib/launch-hardening-gate.js +436 -0
  119. package/scripts/lib/launch-readiness.js +628 -0
  120. package/scripts/lib/learning-memory.js +686 -0
  121. package/scripts/lib/lifecycle-hooks.js +802 -0
  122. package/scripts/lib/local-package-smoke.js +423 -0
  123. package/scripts/lib/local-pricing.js +299 -0
  124. package/scripts/lib/mcp-enforcement.js +311 -0
  125. package/scripts/lib/mcp-least-privilege-policy.js +303 -0
  126. package/scripts/lib/mcp-tool-inventory.js +388 -0
  127. package/scripts/lib/mcp-tool-risk.js +0 -0
  128. package/scripts/lib/memory.js +335 -0
  129. package/scripts/lib/metrics.js +699 -0
  130. package/scripts/lib/micro-proof.js +133 -0
  131. package/scripts/lib/next-run-context.js +436 -0
  132. package/scripts/lib/operating-value.js +1648 -0
  133. package/scripts/lib/optimization-v3.js +122 -0
  134. package/scripts/lib/orchestration/adapters/_shared.js +49 -0
  135. package/scripts/lib/orchestration/adapters/aider.js +18 -0
  136. package/scripts/lib/orchestration/adapters/claude-code.js +35 -0
  137. package/scripts/lib/orchestration/adapters/codex.js +35 -0
  138. package/scripts/lib/orchestration/adapters/gemini-cli.js +18 -0
  139. package/scripts/lib/orchestration/adapters/git.js +25 -0
  140. package/scripts/lib/orchestration/adapters/index.js +31 -0
  141. package/scripts/lib/orchestration/adapters/lm-studio.js +18 -0
  142. package/scripts/lib/orchestration/adapters/ollama.js +18 -0
  143. package/scripts/lib/orchestration/adapters/opencode.js +18 -0
  144. package/scripts/lib/orchestration/adapters/openrouter.js +18 -0
  145. package/scripts/lib/orchestration/adapters/test-runner.js +25 -0
  146. package/scripts/lib/orchestration/cli.js +438 -0
  147. package/scripts/lib/orchestration/execution-manager.js +279 -0
  148. package/scripts/lib/orchestration/handoff.js +314 -0
  149. package/scripts/lib/orchestration/index.js +456 -0
  150. package/scripts/lib/orchestration/inventory.js +47 -0
  151. package/scripts/lib/orchestration/model-discovery.js +498 -0
  152. package/scripts/lib/orchestration/model-profiler.js +170 -0
  153. package/scripts/lib/orchestration/model-profiles.js +252 -0
  154. package/scripts/lib/orchestration/model-refresh-policy.js +72 -0
  155. package/scripts/lib/orchestration/proof-writer.js +349 -0
  156. package/scripts/lib/orchestration/provider-discovery/aider.js +49 -0
  157. package/scripts/lib/orchestration/provider-discovery/claude-code.js +56 -0
  158. package/scripts/lib/orchestration/provider-discovery/codex.js +49 -0
  159. package/scripts/lib/orchestration/provider-discovery/common.js +186 -0
  160. package/scripts/lib/orchestration/provider-discovery/gemini.js +106 -0
  161. package/scripts/lib/orchestration/provider-discovery/lm-studio.js +118 -0
  162. package/scripts/lib/orchestration/provider-discovery/models-dev.js +12 -0
  163. package/scripts/lib/orchestration/provider-discovery/ollama.js +100 -0
  164. package/scripts/lib/orchestration/provider-discovery/opencode.js +47 -0
  165. package/scripts/lib/orchestration/provider-discovery/openrouter.js +44 -0
  166. package/scripts/lib/orchestration/risk-classifier.js +130 -0
  167. package/scripts/lib/orchestration/routing-policy.js +486 -0
  168. package/scripts/lib/orchestration/settings.js +112 -0
  169. package/scripts/lib/orchestration/state.js +165 -0
  170. package/scripts/lib/orchestration/verification-manager.js +138 -0
  171. package/scripts/lib/output-profiles.js +146 -0
  172. package/scripts/lib/package-content-audit.js +368 -0
  173. package/scripts/lib/package-runtime.js +278 -0
  174. package/scripts/lib/plan-surface.js +53 -0
  175. package/scripts/lib/plans.js +2318 -0
  176. package/scripts/lib/policy-provider.js +27 -0
  177. package/scripts/lib/prelaunch-activation-readiness.js +409 -0
  178. package/scripts/lib/prelaunch-evidence-store.js +816 -0
  179. package/scripts/lib/prelaunch-intelligence.js +869 -0
  180. package/scripts/lib/pricing-experiment.js +118 -0
  181. package/scripts/lib/pro-moment-events.js +77 -0
  182. package/scripts/lib/pro-moment-state.js +227 -0
  183. package/scripts/lib/pro-moments.js +1216 -0
  184. package/scripts/lib/product-learning-events.js +629 -0
  185. package/scripts/lib/project-profile.js +555 -0
  186. package/scripts/lib/prompt-compiler.js +280 -0
  187. package/scripts/lib/prompt-lint.js +32 -0
  188. package/scripts/lib/prompt-suggestions.js +52 -0
  189. package/scripts/lib/proof-canonical.js +398 -0
  190. package/scripts/lib/proof-drilldown.js +383 -0
  191. package/scripts/lib/proof-events.js +342 -0
  192. package/scripts/lib/proof-history.js +243 -0
  193. package/scripts/lib/proof-metrics.js +296 -0
  194. package/scripts/lib/proof-outcome-evidence.js +134 -0
  195. package/scripts/lib/proof-receipt.js +335 -0
  196. package/scripts/lib/proof-record.js +461 -0
  197. package/scripts/lib/public-activation-distribution-gate.js +258 -0
  198. package/scripts/lib/public-cli.js +3891 -0
  199. package/scripts/lib/public-distribution-truth.js +211 -0
  200. package/scripts/lib/public-install-claim-checker.js +294 -0
  201. package/scripts/lib/publish-provenance-readiness.js +283 -0
  202. package/scripts/lib/readiness-delta.js +218 -0
  203. package/scripts/lib/readiness-evidence-closure.js +196 -0
  204. package/scripts/lib/reentry-memory-capture.js +241 -0
  205. package/scripts/lib/reentry-memory-retrieval.js +302 -0
  206. package/scripts/lib/reentry-memory-status.js +146 -0
  207. package/scripts/lib/reentry-memory-store.js +178 -0
  208. package/scripts/lib/reentry-state.js +66 -0
  209. package/scripts/lib/release-candidate-bundle.js +166 -0
  210. package/scripts/lib/remediation.js +81 -0
  211. package/scripts/lib/repo-map.js +391 -0
  212. package/scripts/lib/run-improvements-lifecycle.js +330 -0
  213. package/scripts/lib/run-improvements.js +789 -0
  214. package/scripts/lib/runtime-decision-policy.js +387 -0
  215. package/scripts/lib/safe-path-engine.js +705 -0
  216. package/scripts/lib/safe-run-controller.js +887 -0
  217. package/scripts/lib/score.js +262 -0
  218. package/scripts/lib/seamless-enforcement.js +329 -0
  219. package/scripts/lib/seamless-outcome.js +689 -0
  220. package/scripts/lib/seamless-reality-gate.js +5043 -0
  221. package/scripts/lib/security-risk-classifier.js +511 -0
  222. package/scripts/lib/security-scan.js +384 -0
  223. package/scripts/lib/session-context-optimizer.js +1211 -0
  224. package/scripts/lib/session-timing.js +315 -0
  225. package/scripts/lib/skill-hygiene.js +805 -0
  226. package/scripts/lib/skill-packs.js +161 -0
  227. package/scripts/lib/skills-operating-layer.js +580 -0
  228. package/scripts/lib/smart-work-routing.js +768 -0
  229. package/scripts/lib/source-catalog.js +700 -0
  230. package/scripts/lib/status-value-summary.js +32 -0
  231. package/scripts/lib/support-bundle.js +578 -0
  232. package/scripts/lib/task-continuation.js +440 -0
  233. package/scripts/lib/test-helpers.js +15 -0
  234. package/scripts/lib/tier.js +38 -0
  235. package/scripts/lib/token-context-quality-gate.js +370 -0
  236. package/scripts/lib/token-cost-capture.js +187 -0
  237. package/scripts/lib/token-cost-intelligence.js +358 -0
  238. package/scripts/lib/token-efficiency-evidence.js +213 -0
  239. package/scripts/lib/token-evidence.js +699 -0
  240. package/scripts/lib/tokenish.js +17 -0
  241. package/scripts/lib/tool-output-sandbox.js +304 -0
  242. package/scripts/lib/trust-audit.js +136 -0
  243. package/scripts/lib/unified-events.js +396 -0
  244. package/scripts/lib/upgrade-interruption-recovery.js +407 -0
  245. package/scripts/lib/usage-ledger.js +201 -0
  246. package/scripts/lib/value-ledger.js +130 -0
  247. package/scripts/lib/value-proof-calibration.js +531 -0
  248. package/scripts/lib/visual-qa.js +231 -0
  249. package/scripts/lib/voice-alpha.js +29 -0
  250. package/scripts/lib/work-aware-orchestration.js +976 -0
  251. package/scripts/lib/work-control-receipts.js +577 -0
  252. package/scripts/lib/work-ledger.js +1123 -0
  253. package/scripts/lib/work-panel-preview.js +352 -0
  254. package/scripts/lib/workflow-discipline.js +280 -0
  255. package/scripts/lib/workflow-signals.js +419 -0
  256. package/scripts/lib/workspace-map.js +281 -0
  257. package/scripts/lib/workspace-registry.js +1367 -0
  258. package/scripts/lib/workspace-resolver.js +480 -0
@@ -0,0 +1,810 @@
1
+ "use strict";
2
+
3
+ const fs = require("fs");
4
+ const path = require("path");
5
+ const { scanTextForSecurity } = require("./security-scan");
6
+ const {
7
+ SOURCE_CATALOG,
8
+ getSource,
9
+ canReuseSource,
10
+ canVendorSource,
11
+ canLearnPatterns,
12
+ hasConfirmedReusableLicense,
13
+ } = require("./source-catalog");
14
+ const { CAPABILITIES, getCapability } = require("./plans");
15
+
16
+ const GENERATED_SKILL_REGISTRY_REL_PATH = ".avorelo/skills/registry.json";
17
+ const VENDOR_SKILLPACKS_DIR = "vendor/skillpacks";
18
+ const CANONICAL_AVORELO_SKILLS_DIR = "skills/avorelo";
19
+ const MAX_ROUTE_SUMMARY_CHARS = 240;
20
+ const MAX_ROUTE_SUPPORTING_SKILLS = 2;
21
+ const BIDI_CHARS = ["\u202A", "\u202B", "\u202D", "\u202E", "\u202C", "\u2066", "\u2067", "\u2068", "\u2069"];
22
+
23
+ const SECTION_ALIASES = Object.freeze({
24
+ overview: ["Overview"],
25
+ whenToUse: ["When to use this", "When to Use", "When to use"],
26
+ whenNotToUse: ["When NOT to use", "When not to use"],
27
+ workflow: [
28
+ "How It Works",
29
+ "How it Works",
30
+ "How it works",
31
+ "Workflow",
32
+ "The Process",
33
+ "The Planning Process",
34
+ "The TDD Cycle",
35
+ "The Five-Axis Review",
36
+ "Skill Discovery",
37
+ "Lifecycle Sequence",
38
+ ],
39
+ lifecycleStage: ["Lifecycle Mapping", "Lifecycle Sequence", "Lifecycle Stage"],
40
+ requiredChecks: ["Required checks", "Verification", "Verify the Verification"],
41
+ requiredOutput: ["Required output", "Output", "Usage"],
42
+ antiPatterns: ["Anti-patterns", "Common Rationalizations", "Failure Modes to Avoid"],
43
+ stopConditions: ["Stop conditions", "Red Flags"],
44
+ definitionOfDone: ["Definition of done"],
45
+ examples: ["Examples", "Quick Reference", "Usage", "Plan Document Template"],
46
+ references: ["References", "Source hierarchy"],
47
+ });
48
+
49
+ const CAPABILITY_BINDING_RULES = Object.freeze([
50
+ {
51
+ match: /using-agent-skills|using-avorelo-skills/i,
52
+ capabilityIds: ["source_backed_skill_router_basic", "dynamic_skill_pack_selection"],
53
+ },
54
+ {
55
+ match: /planning-and-task-breakdown|spec-driven-development|reference-first-implementation|source-driven-development/i,
56
+ capabilityIds: ["skill_pattern_extraction_basic", "source_backed_skill_router_basic", "prompt_compiler_advanced"],
57
+ },
58
+ {
59
+ match: /code-review-and-quality|pr-quality-gate/i,
60
+ capabilityIds: ["skill_pattern_extraction_basic", "source_backed_skill_router_basic", "evidence_latest"],
61
+ },
62
+ {
63
+ match: /test-driven-development/i,
64
+ capabilityIds: ["skill_pattern_extraction_basic", "source_backed_skill_router_basic", "evidence_latest"],
65
+ },
66
+ {
67
+ match: /prompt-context-optimization/i,
68
+ capabilityIds: ["skill_pattern_extraction_basic", "source_backed_skill_router_basic", "context_cost_advanced"],
69
+ },
70
+ {
71
+ match: /workspace-map/i,
72
+ capabilityIds: ["workspace_map_basic", "source_backed_skill_router_basic"],
73
+ },
74
+ {
75
+ match: /product-dogfood/i,
76
+ capabilityIds: ["skill_pattern_extraction_basic", "evidence_latest"],
77
+ },
78
+ ]);
79
+
80
+ const TASK_SIGNAL_RULES = Object.freeze([
81
+ { match: /\b(plan|planning|breakdown|spec|acceptance criteria|task list)\b/i, boosts: ["planning-and-task-breakdown", "spec-driven-development", "reference-first-implementation"] },
82
+ { match: /\b(review|pr review|code review|quality gate|merge)\b/i, boosts: ["code-review-and-quality", "pr-quality-gate"] },
83
+ { match: /\b(test|testing|tdd|verify|verification|regression)\b/i, boosts: ["test-driven-development", "pr-quality-gate"] },
84
+ { match: /\b(source|reference|upstream|docs|documentation|verified)\b/i, boosts: ["source-driven-development", "reference-first-implementation"] },
85
+ { match: /\b(skill|skills|route|routing|select)\b/i, boosts: ["using-agent-skills", "using-avorelo-skills"] },
86
+ { match: /\b(context|token|prompt|scope|compact)\b/i, boosts: ["prompt-context-optimization"] },
87
+ { match: /\b(workspace|repo navigation|map|reentry)\b/i, boosts: ["workspace-map"] },
88
+ { match: /\b(dogfood|product learning|telemetry)\b/i, boosts: ["product-dogfood"] },
89
+ ]);
90
+
91
+ function normalize(value) {
92
+ return String(value || "").replace(/\\/g, "/");
93
+ }
94
+
95
+ function compactText(value, maxChars = MAX_ROUTE_SUMMARY_CHARS) {
96
+ const text = String(value || "").replace(/\s+/g, " ").trim();
97
+ if (text.length <= maxChars) return text;
98
+ return `${text.slice(0, maxChars - 3).trimEnd()}...`;
99
+ }
100
+
101
+ function tokenize(text) {
102
+ return String(text || "")
103
+ .toLowerCase()
104
+ .replace(/[^a-z0-9/_-]+/g, " ")
105
+ .split(/\s+/)
106
+ .map((term) => term.trim())
107
+ .filter((term) => term.length >= 3);
108
+ }
109
+
110
+ function estimateTokens(textLength) {
111
+ return Math.max(1, Math.ceil(Number(textLength || 0) / 4));
112
+ }
113
+
114
+ function buildContextWeight(charCount) {
115
+ const estimatedTokens = estimateTokens(charCount);
116
+ if (estimatedTokens >= 3500) return { label: "high", estimatedTokens, lazyLoadRecommended: true };
117
+ if (estimatedTokens >= 1200) return { label: "medium", estimatedTokens, lazyLoadRecommended: true };
118
+ return { label: "low", estimatedTokens, lazyLoadRecommended: false };
119
+ }
120
+
121
+ function listSkillFiles(cwd) {
122
+ const results = [];
123
+ const vendorRoot = path.join(cwd, VENDOR_SKILLPACKS_DIR);
124
+ const canonicalRoot = path.join(cwd, CANONICAL_AVORELO_SKILLS_DIR);
125
+
126
+ if (fs.existsSync(canonicalRoot)) {
127
+ for (const entry of fs.readdirSync(canonicalRoot, { withFileTypes: true })) {
128
+ if (!entry.isDirectory()) continue;
129
+ const skillFile = path.join(canonicalRoot, entry.name, "SKILL.md");
130
+ if (fs.existsSync(skillFile)) {
131
+ results.push(skillFile);
132
+ }
133
+ }
134
+ }
135
+
136
+ if (fs.existsSync(vendorRoot)) {
137
+ for (const pack of fs.readdirSync(vendorRoot, { withFileTypes: true })) {
138
+ if (!pack.isDirectory()) continue;
139
+ const skillsRoot = path.join(vendorRoot, pack.name, "skills");
140
+ if (!fs.existsSync(skillsRoot)) continue;
141
+ for (const skill of fs.readdirSync(skillsRoot, { withFileTypes: true })) {
142
+ if (!skill.isDirectory()) continue;
143
+ const skillFile = path.join(skillsRoot, skill.name, "SKILL.md");
144
+ if (fs.existsSync(skillFile)) {
145
+ results.push(skillFile);
146
+ }
147
+ }
148
+ }
149
+ }
150
+
151
+ return results.sort((left, right) => left.localeCompare(right));
152
+ }
153
+
154
+ function parseFrontmatter(raw) {
155
+ const match = raw.match(/^---\r?\n([\s\S]*?)\r?\n---\r?\n?/);
156
+ if (!match) {
157
+ return { frontmatter: {}, body: raw, errors: ["Missing YAML frontmatter"] };
158
+ }
159
+
160
+ const frontmatter = {};
161
+ const errors = [];
162
+ const lines = match[1].split(/\r?\n/);
163
+ let currentListKey = null;
164
+
165
+ lines.forEach((line) => {
166
+ const listMatch = line.match(/^\s*-\s+(.*)$/);
167
+ if (listMatch && currentListKey) {
168
+ if (!Array.isArray(frontmatter[currentListKey])) frontmatter[currentListKey] = [];
169
+ frontmatter[currentListKey].push(listMatch[1].trim());
170
+ return;
171
+ }
172
+
173
+ const separator = line.indexOf(":");
174
+ if (separator === -1) return;
175
+ const key = line.slice(0, separator).trim();
176
+ const value = line.slice(separator + 1).trim();
177
+ if (!key) return;
178
+ if (!value) {
179
+ currentListKey = key;
180
+ frontmatter[key] = [];
181
+ return;
182
+ }
183
+ currentListKey = null;
184
+ frontmatter[key] = value;
185
+ });
186
+
187
+ if (!frontmatter.name) errors.push("Frontmatter must include name");
188
+ if (!frontmatter.description) errors.push("Frontmatter must include description");
189
+ return {
190
+ frontmatter,
191
+ body: raw.slice(match[0].length),
192
+ errors,
193
+ };
194
+ }
195
+
196
+ function extractSection(body, heading) {
197
+ const escaped = heading.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
198
+ const regex = new RegExp(`(?:^|\\r?\\n)##\\s+${escaped}\\r?\\n([\\s\\S]*?)(?=\\r?\\n##\\s+|$)`, "i");
199
+ const match = body.match(regex);
200
+ return match ? match[1].trim() : "";
201
+ }
202
+
203
+ function findSection(body, aliases) {
204
+ for (const heading of aliases) {
205
+ const value = extractSection(body, heading);
206
+ if (value) return value;
207
+ }
208
+ return "";
209
+ }
210
+
211
+ function detectReferences(body) {
212
+ const inline = (body.match(/`([^`]+)`/g) || [])
213
+ .map((token) => token.slice(1, -1))
214
+ .filter((token) => token.includes("/") || token.endsWith(".js") || token.endsWith(".md") || token.startsWith("http"));
215
+ const urls = body.match(/https?:\/\/[^\s)]+/g) || [];
216
+ return Array.from(new Set([...inline, ...urls]));
217
+ }
218
+
219
+ function detectCrossSkillReferences(body) {
220
+ return Array.from(new Set((body.match(/skills\/[A-Za-z0-9/_-]+/g) || [])));
221
+ }
222
+
223
+ function detectScripts(body) {
224
+ return Array.from(new Set([
225
+ ...(body.match(/scripts\/[A-Za-z0-9/_-]+\.[A-Za-z0-9]+/g) || []),
226
+ ...(body.match(/[A-Za-z0-9/_-]+\.sh\b/g) || []),
227
+ ]));
228
+ }
229
+
230
+ function detectAssets(body) {
231
+ return Array.from(new Set((body.match(/assets\/[A-Za-z0-9/_-]+\.[A-Za-z0-9]+/g) || [])));
232
+ }
233
+
234
+ function detectSupportingFiles(body) {
235
+ const matches = new Set();
236
+ const patterns = [
237
+ /`((?:\.{1,2}\/)?(?:references|scripts|assets)\/[^`\n]+\.(?:md|json|js|ts|tsx|sh|txt|yaml|yml))`/g,
238
+ /`((?:\.{1,2}\/)?[A-Za-z0-9._/-]+\.(?:md|json|js|ts|tsx|sh|txt|yaml|yml))`/g,
239
+ /\b((?:\.{1,2}\/)?(?:references|scripts|assets)\/[A-Za-z0-9._/-]+\.(?:md|json|js|ts|tsx|sh|txt|yaml|yml))\b/g,
240
+ ];
241
+
242
+ patterns.forEach((pattern) => {
243
+ let match = null;
244
+ while ((match = pattern.exec(body)) !== null) {
245
+ matches.add(match[1]);
246
+ }
247
+ });
248
+
249
+ return Array.from(matches);
250
+ }
251
+
252
+ function skillIdForFile(cwd, absPath) {
253
+ const relPath = normalize(path.relative(cwd, absPath));
254
+ if (relPath.startsWith("skills/avorelo/")) {
255
+ return relPath.split("/").slice(2, 3)[0];
256
+ }
257
+ if (relPath.startsWith("vendor/skillpacks/")) {
258
+ const parts = relPath.split("/");
259
+ return `${parts[2]}:${parts[4]}`;
260
+ }
261
+ return relPath;
262
+ }
263
+
264
+ function packIdForFile(cwd, absPath) {
265
+ const relPath = normalize(path.relative(cwd, absPath));
266
+ if (relPath.startsWith("vendor/skillpacks/")) {
267
+ return relPath.split("/")[2];
268
+ }
269
+ return "avorelo";
270
+ }
271
+
272
+ function sourceMetadataForPack(cwd, packId) {
273
+ if (packId === "avorelo") {
274
+ return {
275
+ id: "avorelo",
276
+ name: "Avorelo canonical skills",
277
+ sourceUrl: null,
278
+ reviewStatus: "trusted",
279
+ trustLevel: "high",
280
+ riskLevel: "low",
281
+ license: "proprietary",
282
+ canReuse: true,
283
+ canVendor: true,
284
+ canLearnPatterns: true,
285
+ attributionText: null,
286
+ runtimePolicy: "local_first_owned",
287
+ copyPolicy: "internal_only",
288
+ sourceSnapshot: { type: "canonical" },
289
+ };
290
+ }
291
+
292
+ const source = getSource(packId);
293
+ const sourceJsonPath = path.join(cwd, VENDOR_SKILLPACKS_DIR, packId, "SOURCE.json");
294
+ let sourceJson = null;
295
+ if (fs.existsSync(sourceJsonPath)) {
296
+ try {
297
+ sourceJson = JSON.parse(fs.readFileSync(sourceJsonPath, "utf8"));
298
+ } catch {}
299
+ }
300
+
301
+ return {
302
+ id: packId,
303
+ name: source?.name || sourceJson?.name || packId,
304
+ sourceUrl: source?.sourceUrl || sourceJson?.sourceUrl || null,
305
+ reviewStatus: source?.sourceReviewStatus || "unreviewed",
306
+ trustLevel: source?.sourceTrustLevel || "unknown",
307
+ riskLevel: source?.riskLevel || "high",
308
+ license: source?.license || sourceJson?.license || "unknown",
309
+ canReuse: source ? canReuseSource(source) : false,
310
+ canVendor: source ? canVendorSource(source.id) : false,
311
+ canLearnPatterns: source ? canLearnPatterns(source) : false,
312
+ attributionText: source?.attributionText || null,
313
+ runtimePolicy: sourceJson?.runtimePolicy || "unknown",
314
+ copyPolicy: sourceJson?.copyPolicy || "unknown",
315
+ sourceSnapshot: source?.sourceSnapshot || sourceJson?.sourceSnapshot || null,
316
+ };
317
+ }
318
+
319
+ function resolvePathRelativeToSkill(absSkillPath, candidate) {
320
+ const candidateText = String(candidate || "").trim();
321
+ if (!candidateText || /^https?:\/\//i.test(candidateText)) return null;
322
+ if (/^[A-Za-z]:\//i.test(candidateText)) return candidateText;
323
+ if (/^(?:references|assets|skills)\//.test(candidateText) && normalize(absSkillPath).includes("/vendor/skillpacks/")) {
324
+ const packRoot = path.resolve(path.dirname(absSkillPath), "..", "..");
325
+ return path.resolve(packRoot, candidateText);
326
+ }
327
+ return path.resolve(path.dirname(absSkillPath), candidateText);
328
+ }
329
+
330
+ function detectRedFlags(raw, sections) {
331
+ const findings = [];
332
+ if (BIDI_CHARS.some((char) => raw.includes(char))) {
333
+ findings.push({ code: "hidden_unicode", severity: "high", message: "Hidden Unicode control characters detected." });
334
+ }
335
+
336
+ const combined = [sections.antiPatterns, sections.stopConditions, raw].filter(Boolean).join("\n");
337
+ [
338
+ { code: "policy_bypass_phrase", severity: "high", pattern: /\b(ignore|bypass|disable|remove)\s+(?:the\s+)?(?:policy|guardrails|audit|proof|verification|review)\b/i, message: "Bypass-policy red flag detected." },
339
+ { code: "unsafe_auto_execution_phrase", severity: "high", pattern: /\b(auto-?run|execute automatically|run immediately)\b/i, message: "Auto-execution language detected." },
340
+ ].forEach((rule) => {
341
+ if (rule.pattern.test(combined)) findings.push({ code: rule.code, severity: rule.severity, message: rule.message });
342
+ });
343
+ return findings;
344
+ }
345
+
346
+ function buildWarnings({ absPath, relPath, raw, parsed, packMetadata, references, scripts, assets, supportingFiles, sections }) {
347
+ const warnings = [];
348
+
349
+ parsed.errors.forEach((error) => warnings.push({ code: "frontmatter_error", severity: "medium", message: error }));
350
+
351
+ if (!hasConfirmedReusableLicense({
352
+ license: packMetadata.license,
353
+ productDecision: packMetadata.reviewStatus === "blocked" ? "blocked" : "defer",
354
+ allowedUseModes: [],
355
+ }) && packMetadata.id !== "avorelo") {
356
+ warnings.push({ code: "unknown_or_unconfirmed_license", severity: "high", message: "Source license is not confirmed for reuse." });
357
+ }
358
+ if (packMetadata.reviewStatus === "unreviewed") {
359
+ warnings.push({ code: "unreviewed_source", severity: "high", message: "Source remains unreviewed and should stay deferred." });
360
+ }
361
+ if (packMetadata.reviewStatus === "blocked") {
362
+ warnings.push({ code: "blocked_source", severity: "high", message: "Source is blocked and must not be routed." });
363
+ }
364
+ if (packMetadata.id !== "avorelo" && !packMetadata.attributionText) {
365
+ warnings.push({ code: "missing_attribution", severity: "medium", message: "Vendored or external source is missing attribution guidance." });
366
+ }
367
+ if (scripts.length > 0 && packMetadata.id !== "avorelo") {
368
+ warnings.push({ code: "external_script_metadata_only", severity: "medium", message: "Imported script references must remain data-only and never auto-run." });
369
+ }
370
+
371
+ supportingFiles.forEach((candidate) => {
372
+ if (!/^(?:\.{1,2}\/|references\/|scripts\/|assets\/)/.test(candidate)) return;
373
+ const resolved = resolvePathRelativeToSkill(absPath, candidate);
374
+ if (!resolved) return;
375
+ if (!fs.existsSync(resolved)) {
376
+ warnings.push({
377
+ code: "missing_supporting_file",
378
+ severity: "medium",
379
+ message: `Referenced supporting file is missing: ${candidate}`,
380
+ });
381
+ }
382
+ });
383
+
384
+ const weight = buildContextWeight(raw.length);
385
+ if (weight.label === "high") {
386
+ warnings.push({
387
+ code: "context_heavy_skill",
388
+ severity: "medium",
389
+ message: `Skill is context-heavy (${weight.estimatedTokens} estimated tokens) and should be lazy-loaded.`,
390
+ });
391
+ }
392
+
393
+ if (references.some((reference) => /AGENTS\.md|CLAUDE\.md|GEMINI\.md|copilot/i.test(reference)) && raw.length > 3000) {
394
+ warnings.push({
395
+ code: "adapter_leakage_risk",
396
+ severity: "medium",
397
+ message: "Skill references adapter surfaces and is too large for thin adapter copying.",
398
+ });
399
+ }
400
+
401
+ detectRedFlags(raw, sections).forEach((finding) => warnings.push(finding));
402
+ return warnings;
403
+ }
404
+
405
+ function summarizeWarnings(warnings) {
406
+ return warnings.map((warning) => ({
407
+ code: warning.code,
408
+ severity: warning.severity,
409
+ message: warning.message,
410
+ }));
411
+ }
412
+
413
+ function suggestCapabilityBindings(skill) {
414
+ const suggestions = new Set();
415
+ const haystack = [skill.id, skill.name, skill.description, skill.whenToUse, skill.workflow].join(" ");
416
+ CAPABILITY_BINDING_RULES.forEach((rule) => {
417
+ if (rule.match.test(haystack)) {
418
+ rule.capabilityIds.forEach((id) => suggestions.add(id));
419
+ }
420
+ });
421
+
422
+ if (skill.packId !== "avorelo") {
423
+ suggestions.add("reference_source_intake_basic");
424
+ suggestions.add("skill_md_parser_basic");
425
+ suggestions.add("skill_pattern_extraction_basic");
426
+ suggestions.add("skill_trust_scan_basic");
427
+ suggestions.add("capability_binding_basic");
428
+ suggestions.add("source_backed_skill_router_basic");
429
+ }
430
+
431
+ if (skill.packId === "avorelo") {
432
+ suggestions.add("skill_md_parser_basic");
433
+ suggestions.add("skill_pattern_extraction_basic");
434
+ suggestions.add("capability_binding_basic");
435
+ suggestions.add("source_backed_skill_router_basic");
436
+ }
437
+
438
+ if (skill.adapterSuitability.length > 0) {
439
+ suggestions.add("thin_adapter_guidance_sync_basic");
440
+ }
441
+
442
+ return Array.from(suggestions).filter((id) => getCapability(id)).sort();
443
+ }
444
+
445
+ function routeEligibility(skill) {
446
+ if (skill.packId === "avorelo") {
447
+ return { routeEligible: true, deferredReason: null };
448
+ }
449
+ if (skill.sourceReviewStatus === "blocked") {
450
+ return { routeEligible: false, deferredReason: "Source is blocked." };
451
+ }
452
+ if (!skill.sourceLicenseConfirmed) {
453
+ return { routeEligible: false, deferredReason: "Source license or provenance is not confirmed for reuse." };
454
+ }
455
+ if (!skill.sourceReusable) {
456
+ return { routeEligible: false, deferredReason: "Source is reference-only or deferred." };
457
+ }
458
+ if (skill.warnings.some((warning) => warning.severity === "high" && warning.code !== "frontmatter_error")) {
459
+ return { routeEligible: false, deferredReason: "Skill has unresolved high-severity trust or safety warnings." };
460
+ }
461
+ return { routeEligible: true, deferredReason: null };
462
+ }
463
+
464
+ function routeSummaryForSkill(skill, score, chosen = false) {
465
+ return {
466
+ id: skill.id,
467
+ name: skill.name,
468
+ description: compactText(skill.description),
469
+ packId: skill.packId,
470
+ sourceId: skill.sourceId,
471
+ sourceReviewStatus: skill.sourceReviewStatus,
472
+ trustLevel: skill.sourceTrustLevel,
473
+ score,
474
+ chosen,
475
+ summary: compactText(skill.whenToUse || skill.description),
476
+ verification: compactText(skill.requiredChecks || skill.definitionOfDone || ""),
477
+ evidenceRequirements: compactText(skill.evidenceRequirements || skill.requiredOutput || ""),
478
+ contextWeight: skill.contextWeight,
479
+ capabilityIds: skill.capabilityBindingSuggestions,
480
+ sourcePath: skill.sourcePath,
481
+ warnings: summarizeWarnings(skill.warnings).slice(0, 5),
482
+ };
483
+ }
484
+
485
+ function parseSkillFile(cwd, absPath) {
486
+ const raw = fs.readFileSync(absPath, "utf8").replace(/^\uFEFF/, "");
487
+ const parsed = parseFrontmatter(raw);
488
+ const body = parsed.body || "";
489
+ const relPath = normalize(path.relative(cwd, absPath));
490
+ const packId = packIdForFile(cwd, absPath);
491
+ const packMetadata = sourceMetadataForPack(cwd, packId);
492
+
493
+ const sections = {
494
+ overview: findSection(body, SECTION_ALIASES.overview),
495
+ whenToUse: findSection(body, SECTION_ALIASES.whenToUse),
496
+ whenNotToUse: findSection(body, SECTION_ALIASES.whenNotToUse),
497
+ workflow: findSection(body, SECTION_ALIASES.workflow),
498
+ lifecycleStage: findSection(body, SECTION_ALIASES.lifecycleStage),
499
+ requiredChecks: findSection(body, SECTION_ALIASES.requiredChecks),
500
+ requiredOutput: findSection(body, SECTION_ALIASES.requiredOutput),
501
+ antiPatterns: findSection(body, SECTION_ALIASES.antiPatterns),
502
+ stopConditions: findSection(body, SECTION_ALIASES.stopConditions),
503
+ definitionOfDone: findSection(body, SECTION_ALIASES.definitionOfDone),
504
+ examples: findSection(body, SECTION_ALIASES.examples),
505
+ referencesSection: findSection(body, SECTION_ALIASES.references),
506
+ };
507
+ const references = detectReferences(body);
508
+ const scripts = detectScripts(body);
509
+ const assets = detectAssets(body);
510
+ const supportingFiles = Array.from(new Set([...detectSupportingFiles(body), ...references, ...scripts, ...assets]));
511
+ const securityReport = scanTextForSecurity({
512
+ text: raw,
513
+ pathHint: relPath,
514
+ allowlist: { reasonCodes: [], pathPatterns: [] },
515
+ });
516
+ const warnings = buildWarnings({
517
+ absPath,
518
+ relPath,
519
+ raw,
520
+ parsed,
521
+ packMetadata,
522
+ references,
523
+ scripts,
524
+ assets,
525
+ supportingFiles,
526
+ sections,
527
+ });
528
+ const contextWeight = buildContextWeight(raw.length);
529
+ const skill = {
530
+ id: skillIdForFile(cwd, absPath),
531
+ name: parsed.frontmatter.name || null,
532
+ description: parsed.frontmatter.description || null,
533
+ overview: sections.overview,
534
+ whenToUse: sections.whenToUse,
535
+ whenNotToUse: sections.whenNotToUse,
536
+ workflow: sections.workflow,
537
+ lifecycleStage: sections.lifecycleStage,
538
+ requiredChecks: sections.requiredChecks,
539
+ requiredOutput: sections.requiredOutput,
540
+ antiPatterns: sections.antiPatterns,
541
+ stopConditions: sections.stopConditions,
542
+ definitionOfDone: sections.definitionOfDone,
543
+ examples: sections.examples,
544
+ referencesSection: sections.referencesSection,
545
+ verification: sections.requiredChecks || sections.definitionOfDone,
546
+ evidenceRequirements: sections.requiredOutput,
547
+ redFlags: summarizeWarnings(warnings.filter((warning) => warning.severity === "high" || warning.code.includes("policy") || warning.code.includes("auto_execution"))),
548
+ references,
549
+ scripts,
550
+ assets,
551
+ supportingFiles,
552
+ crossSkillReferences: detectCrossSkillReferences(body),
553
+ sourcePath: relPath,
554
+ packId,
555
+ sourceId: packMetadata.id,
556
+ sourceName: packMetadata.name,
557
+ sourceUrl: packMetadata.sourceUrl,
558
+ sourceReviewStatus: packMetadata.reviewStatus,
559
+ sourceTrustLevel: packMetadata.trustLevel,
560
+ sourceRiskLevel: packMetadata.riskLevel,
561
+ sourceLicense: packMetadata.license,
562
+ sourceLicenseConfirmed: hasConfirmedReusableLicense({
563
+ license: packMetadata.license,
564
+ }) || packId === "avorelo",
565
+ sourceReusable: packId === "avorelo" ? true : packMetadata.canReuse,
566
+ sourceVendorAllowed: packId === "avorelo" ? false : packMetadata.canVendor,
567
+ sourceLearnPatternAllowed: packId === "avorelo" ? true : packMetadata.canLearnPatterns,
568
+ sourceAttributionText: packMetadata.attributionText,
569
+ runtimePolicy: packMetadata.runtimePolicy,
570
+ copyPolicy: packMetadata.copyPolicy,
571
+ sourceSnapshot: packMetadata.sourceSnapshot,
572
+ estimatedContextChars: raw.length,
573
+ contextWeight,
574
+ adapterSuitability: ["agents", "claude", "cursor", "gemini", "copilot"].filter((surface) => raw.length < 14000 || surface === "agents"),
575
+ errors: parsed.errors,
576
+ warnings,
577
+ securityScan: {
578
+ riskScore: securityReport.riskScore,
579
+ topReasons: securityReport.topReasons,
580
+ },
581
+ };
582
+ const eligibility = routeEligibility(skill);
583
+ skill.routeEligible = eligibility.routeEligible;
584
+ skill.deferredReason = eligibility.deferredReason;
585
+ skill.capabilityBindingSuggestions = suggestCapabilityBindings(skill);
586
+ return skill;
587
+ }
588
+
589
+ function buildSkillRegistry(cwd) {
590
+ const skillFiles = listSkillFiles(cwd);
591
+ const skills = skillFiles.map((absPath) => parseSkillFile(cwd, absPath));
592
+ const warnings = [];
593
+ const seenIds = new Set();
594
+
595
+ skills.forEach((skill) => {
596
+ if (seenIds.has(skill.id)) {
597
+ warnings.push(`Duplicate skill id: ${skill.id}`);
598
+ }
599
+ seenIds.add(skill.id);
600
+ skill.errors.forEach((error) => warnings.push(`[${skill.id}] ${error}`));
601
+ skill.warnings.forEach((warning) => warnings.push(`[${skill.id}] ${warning.code}: ${warning.message}`));
602
+ skill.capabilityBindingSuggestions.forEach((capabilityId) => {
603
+ if (!getCapability(capabilityId)) {
604
+ warnings.push(`[${skill.id}] Unknown capability binding suggestion: ${capabilityId}`);
605
+ }
606
+ });
607
+ });
608
+
609
+ const reviewedSourceIds = Array.from(new Set(skills
610
+ .filter((skill) => skill.sourceReviewStatus === "reviewed" || skill.sourceReviewStatus === "trusted")
611
+ .map((skill) => skill.sourceId)))
612
+ .filter((id) => id !== "avorelo");
613
+ const deferredSourceIds = Array.from(new Set(skills
614
+ .filter((skill) => !skill.routeEligible && skill.packId !== "avorelo")
615
+ .map((skill) => skill.sourceId)));
616
+ const blockedSourceIds = Array.from(new Set(skills
617
+ .filter((skill) => skill.sourceReviewStatus === "blocked")
618
+ .map((skill) => skill.sourceId)));
619
+
620
+ return {
621
+ schemaVersion: 2,
622
+ generatedAt: new Date().toISOString(),
623
+ skills,
624
+ warnings,
625
+ summary: {
626
+ totalSkills: skills.length,
627
+ canonicalSkills: skills.filter((skill) => skill.packId === "avorelo").length,
628
+ reviewedExternalSkills: skills.filter((skill) => skill.packId !== "avorelo" && skill.routeEligible).length,
629
+ deferredExternalSkills: skills.filter((skill) => skill.packId !== "avorelo" && !skill.routeEligible).length,
630
+ skillsWithWarnings: skills.filter((skill) => skill.warnings.length > 0).length,
631
+ capabilityBoundSkills: skills.filter((skill) => skill.capabilityBindingSuggestions.length > 0).length,
632
+ reviewedSourceIds,
633
+ deferredSourceIds,
634
+ blockedSourceIds,
635
+ },
636
+ };
637
+ }
638
+
639
+ function writeSkillRegistry(cwd, registry) {
640
+ const absPath = path.join(cwd, GENERATED_SKILL_REGISTRY_REL_PATH);
641
+ fs.mkdirSync(path.dirname(absPath), { recursive: true });
642
+ fs.writeFileSync(absPath, JSON.stringify(registry, null, 2), "utf8");
643
+ return GENERATED_SKILL_REGISTRY_REL_PATH;
644
+ }
645
+
646
+ function scoreSkill(skill, taskText) {
647
+ const terms = tokenize(taskText);
648
+ const sectionWeights = [
649
+ { text: skill.name, weight: 8 },
650
+ { text: skill.description, weight: 6 },
651
+ { text: skill.whenToUse, weight: 5 },
652
+ { text: skill.workflow, weight: 3 },
653
+ { text: skill.requiredChecks, weight: 3 },
654
+ { text: skill.antiPatterns, weight: 2 },
655
+ { text: skill.examples, weight: 1 },
656
+ ];
657
+
658
+ let score = 0;
659
+ let matchedTerms = 0;
660
+ const haystacks = sectionWeights.map((entry) => ({
661
+ text: String(entry.text || "").toLowerCase(),
662
+ weight: entry.weight,
663
+ }));
664
+ terms.forEach((term) => {
665
+ haystacks.forEach((entry) => {
666
+ if (entry.text.includes(term)) {
667
+ score += entry.weight;
668
+ matchedTerms += 1;
669
+ }
670
+ });
671
+ });
672
+
673
+ TASK_SIGNAL_RULES.forEach((rule) => {
674
+ if (rule.match.test(taskText) && rule.boosts.some((boost) => skill.id.includes(boost) || skill.name === boost)) {
675
+ score += 12;
676
+ }
677
+ });
678
+
679
+ if (skill.packId === "avorelo") score += 6;
680
+ else if (skill.routeEligible) score += 2;
681
+
682
+ if (skill.contextWeight.label === "high") score -= 3;
683
+ if (skill.contextWeight.label === "medium") score -= 1;
684
+
685
+ if (matchedTerms === 0 && score === 0) {
686
+ const taskLower = String(taskText || "").toLowerCase();
687
+ if (taskLower.includes("source-backed") && /using-agent-skills|reference-first-implementation|source-driven-development/i.test(skill.id)) {
688
+ score += 5;
689
+ }
690
+ }
691
+
692
+ return score;
693
+ }
694
+
695
+ function uniqueDeferredSources(skipped) {
696
+ const seen = new Map();
697
+ skipped
698
+ .filter((entry) => entry.reason === "deferred_source")
699
+ .forEach((entry) => {
700
+ const key = entry.sourceId || entry.packId;
701
+ if (!seen.has(key)) {
702
+ seen.set(key, {
703
+ sourceId: entry.sourceId,
704
+ sourceName: entry.sourceName,
705
+ reason: entry.deferredReason,
706
+ });
707
+ }
708
+ });
709
+ return Array.from(seen.values());
710
+ }
711
+
712
+ function routeSkillForTask(registry, task) {
713
+ const taskText = String(task || "").trim();
714
+ const candidates = [];
715
+ const skipped = [];
716
+
717
+ registry.skills.forEach((skill) => {
718
+ const score = scoreSkill(skill, taskText);
719
+ if (!skill.routeEligible) {
720
+ skipped.push({
721
+ id: skill.id,
722
+ name: skill.name,
723
+ packId: skill.packId,
724
+ sourceId: skill.sourceId,
725
+ sourceName: skill.sourceName,
726
+ reason: "deferred_source",
727
+ deferredReason: skill.deferredReason,
728
+ });
729
+ return;
730
+ }
731
+
732
+ if (score <= 0) {
733
+ skipped.push({
734
+ id: skill.id,
735
+ name: skill.name,
736
+ packId: skill.packId,
737
+ sourceId: skill.sourceId,
738
+ sourceName: skill.sourceName,
739
+ reason: "not_relevant",
740
+ deferredReason: null,
741
+ });
742
+ return;
743
+ }
744
+
745
+ candidates.push({ skill, score });
746
+ });
747
+
748
+ candidates.sort((left, right) => {
749
+ if (right.score !== left.score) return right.score - left.score;
750
+ if (left.skill.packId === "avorelo" && right.skill.packId !== "avorelo") return -1;
751
+ if (right.skill.packId === "avorelo" && left.skill.packId !== "avorelo") return 1;
752
+ return left.skill.id.localeCompare(right.skill.id);
753
+ });
754
+
755
+ const primary = candidates[0] || null;
756
+ const supporting = candidates.slice(1, 1 + MAX_ROUTE_SUPPORTING_SKILLS)
757
+ .filter((entry) => entry.score >= Math.max(2, (primary?.score || 0) - 6));
758
+
759
+ const selected = [primary, ...supporting].filter(Boolean);
760
+ const selectedCapabilities = Array.from(new Set(selected.flatMap((entry) => entry.skill.capabilityBindingSuggestions)))
761
+ .filter((capabilityId) => getCapability(capabilityId))
762
+ .sort();
763
+ const evidenceRequirements = selected
764
+ .map((entry) => compactText(entry.skill.evidenceRequirements || entry.skill.requiredOutput || entry.skill.definitionOfDone || ""))
765
+ .filter(Boolean);
766
+ const verificationRequirements = selected
767
+ .map((entry) => compactText(entry.skill.requiredChecks || entry.skill.definitionOfDone || ""))
768
+ .filter(Boolean);
769
+ const totalEstimatedTokens = selected.reduce((sum, entry) => sum + Number(entry.skill.contextWeight.estimatedTokens || 0), 0);
770
+ const skillContextWarnings = selected
771
+ .flatMap((entry) => (entry.skill.warnings || []).map((warning) => ({
772
+ code: warning.code,
773
+ severity: warning.severity,
774
+ skillId: entry.skill.id,
775
+ message: compactText(warning.message, 120),
776
+ })));
777
+
778
+ return {
779
+ task: taskText,
780
+ primarySkill: primary ? routeSummaryForSkill(primary.skill, primary.score, true) : null,
781
+ supportingSkills: supporting.map((entry) => routeSummaryForSkill(entry.skill, entry.score, false)),
782
+ selectedCapabilities,
783
+ skippedSkills: skipped.slice(0, 10),
784
+ deferredSources: uniqueDeferredSources(skipped),
785
+ evidenceRequirements,
786
+ verificationRequirements,
787
+ skillContextWarnings,
788
+ contextWeight: {
789
+ label: totalEstimatedTokens >= 3500 ? "high" : totalEstimatedTokens >= 1200 ? "medium" : "low",
790
+ estimatedTokens: totalEstimatedTokens,
791
+ lazyLoad: totalEstimatedTokens >= 1200,
792
+ },
793
+ nextAction: primary
794
+ ? `Load ${primary.skill.id} with compact supporting context, then verify against its required checks and evidence expectations.`
795
+ : "No reviewed skill matched strongly. Use canonical Avorelo skills or review deferred sources before routing external content.",
796
+ routeState: primary ? "selected" : "no_match",
797
+ };
798
+ }
799
+
800
+ module.exports = {
801
+ GENERATED_SKILL_REGISTRY_REL_PATH,
802
+ VENDOR_SKILLPACKS_DIR,
803
+ CANONICAL_AVORELO_SKILLS_DIR,
804
+ listSkillFiles,
805
+ parseFrontmatter,
806
+ parseSkillFile,
807
+ buildSkillRegistry,
808
+ writeSkillRegistry,
809
+ routeSkillForTask,
810
+ };