mcp-efficiency-engine 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 (360) hide show
  1. package/.github/CODEOWNERS +40 -0
  2. package/.github/GITHUB_MCP_INTEGRATION.md +366 -0
  3. package/.github/ISSUE_TEMPLATE/bug.md +51 -0
  4. package/.github/ISSUE_TEMPLATE/feature_request.md +45 -0
  5. package/.github/ISSUE_TEMPLATE/question.md +18 -0
  6. package/.github/agents/backend.agent.md +18 -0
  7. package/.github/agents/community-manager.agent.md +18 -0
  8. package/.github/agents/dba.agent.md +18 -0
  9. package/.github/agents/frontend-agent.agent.md +18 -0
  10. package/.github/agents/github-repository-manager.agent.md +338 -0
  11. package/.github/agents/iot.agent.md +20 -0
  12. package/.github/agents/legacy.agent.md +18 -0
  13. package/.github/agents/rag-azure.agent.md +18 -0
  14. package/.github/agents/rag-local.agent.md +18 -0
  15. package/.github/agents/snapshot.agent.md +18 -0
  16. package/.github/agents/ux-ui.agent.md +18 -0
  17. package/.github/agents/wiki-agent.agent.md +23 -0
  18. package/.github/copilot-instructions.md +44 -0
  19. package/.github/instructions/always-on-optimization.instructions.md +54 -0
  20. package/.github/prompts/auto-route.prompt.md +33 -0
  21. package/.github/prompts/azure-rag.query.prompt.md +19 -0
  22. package/.github/prompts/backend.fix-bug.prompt.md +13 -0
  23. package/.github/prompts/cavecrew.prompt.md +16 -0
  24. package/.github/prompts/caveman-commit.prompt.md +14 -0
  25. package/.github/prompts/caveman-compress.prompt.md +15 -0
  26. package/.github/prompts/caveman-debug.prompt.md +13 -0
  27. package/.github/prompts/caveman-help.prompt.md +11 -0
  28. package/.github/prompts/caveman-review.prompt.md +15 -0
  29. package/.github/prompts/caveman-stats.prompt.md +11 -0
  30. package/.github/prompts/caveman.prompt.md +11 -0
  31. package/.github/prompts/community.post.prompt.md +14 -0
  32. package/.github/prompts/dba.query-review.prompt.md +15 -0
  33. package/.github/prompts/dev.fix-bug.prompt.md +13 -0
  34. package/.github/prompts/frontend.code.prompt.md +13 -0
  35. package/.github/prompts/legacy.impact-analysis.prompt.md +14 -0
  36. package/.github/prompts/project.kickoff-analysis.prompt.md +51 -0
  37. package/.github/prompts/rag.knowledge-answer.prompt.md +13 -0
  38. package/.github/prompts/token-saver-review.prompt.md +14 -0
  39. package/.github/prompts/ux-ui.review.prompt.md +14 -0
  40. package/.github/pull_request_template.md +71 -0
  41. package/.github/skills/ahorro-tokens/SKILL.md +36 -0
  42. package/.github/skills/azure-rag-enterprise/SKILL.md +24 -0
  43. package/.github/skills/backend-coding/SKILL.md +24 -0
  44. package/.github/skills/cavecrew/SKILL.md +17 -0
  45. package/.github/skills/caveman/SKILL.md +25 -0
  46. package/.github/skills/caveman-commit/SKILL.md +15 -0
  47. package/.github/skills/caveman-compress/SKILL.md +14 -0
  48. package/.github/skills/caveman-help/SKILL.md +19 -0
  49. package/.github/skills/caveman-mode/SKILL.md +43 -0
  50. package/.github/skills/caveman-review/SKILL.md +16 -0
  51. package/.github/skills/caveman-stats/SKILL.md +13 -0
  52. package/.github/skills/community-content/SKILL.md +22 -0
  53. package/.github/skills/database-analysis/SKILL.md +23 -0
  54. package/.github/skills/dev-coding/SKILL.md +24 -0
  55. package/.github/skills/frontend-coding/SKILL.md +24 -0
  56. package/.github/skills/human-in-the-loop/SKILL.md +47 -0
  57. package/.github/skills/iot-architecture/SKILL.md +22 -0
  58. package/.github/skills/legacy-migration/SKILL.md +22 -0
  59. package/.github/skills/rag-knowledge/SKILL.md +21 -0
  60. package/.github/skills/token-saver/SKILL.md +36 -0
  61. package/.github/skills/ux-ui-governance/SKILL.md +24 -0
  62. package/.github/skills/wiki-tools.json +24 -0
  63. package/.github/workflows/auto-devlog.yml +56 -0
  64. package/.github/workflows/auto-manage-issues.yml +64 -0
  65. package/.github/workflows/auto-sync.yml +54 -0
  66. package/.github/workflows/autodocs-sync.yml +114 -0
  67. package/.github/workflows/ci.yml +68 -0
  68. package/.github/workflows/pages.yml +59 -0
  69. package/.github/workflows/routing-evals.yml +59 -0
  70. package/.github/workflows/techriders-coverage-effective.yml +91 -0
  71. package/.vscode/mcp.json +44 -0
  72. package/AGENTS.md +160 -0
  73. package/ARCHITECTURE.md +57 -0
  74. package/FINAL_USAGE_GUIDE.md +106 -0
  75. package/LICENSE +21 -0
  76. package/README.md +385 -0
  77. package/README_WIKI.md +130 -0
  78. package/autodocs/README.md +22 -0
  79. package/autodocs/schema/wiki-node.schema.json +56 -0
  80. package/autolearning/feedback-loop.md +90 -0
  81. package/bin/install-host.js +326 -0
  82. package/bin/mcpee.js +116 -0
  83. package/context/repomix/repomix.config.json +20 -0
  84. package/memory/cross-memory-reasoning.md +17 -0
  85. package/memory/mempry-registry.yml +16 -0
  86. package/observability/evals/chat-token-usage-report.json +64 -0
  87. package/observability/evals/chat-token-usage-report.md +33 -0
  88. package/observability/evals/iteration-value-report.json +76 -0
  89. package/observability/evals/iteration-value-report.md +39 -0
  90. package/observability/evals/learning-loop-report.json +268 -0
  91. package/observability/evals/learning-loop-report.md +37 -0
  92. package/observability/evals/repo-intake-eval-cases.md +23 -0
  93. package/observability/evals/routing-eval-cases.json +68 -0
  94. package/observability/evals/routing-eval-cases.md +25 -0
  95. package/observability/evals/routing-eval-report.json +72 -0
  96. package/observability/evaluation.md +34 -0
  97. package/observability/logs/copilot-usage-ingest-report.json +13 -0
  98. package/observability/logs/iteration-metrics.jsonl +75 -0
  99. package/observability/logs/learning-feedback.jsonl +152 -0
  100. package/observability/logs/routing-decisions.jsonl +333 -0
  101. package/observability/logs/session/agent-pipeline-preflight-20260701-004146.log +8 -0
  102. package/observability/logs/session/agent-pipeline-preflight-20260701-005554.log +8 -0
  103. package/observability/logs/session/agent-pipeline-preflight-20260701-010351.log +8 -0
  104. package/observability/logs/session/agent-pipeline-preflight-20260704-145829.log +8 -0
  105. package/observability/logs/session/agent-pipeline-preflight-20260704-150021.log +8 -0
  106. package/observability/logs/session/agent-pipeline-preflight-20260704-154328.log +8 -0
  107. package/observability/logs/session/bye-20260629-221513.json +39 -0
  108. package/observability/logs/session/bye-20260629-221545.json +59 -0
  109. package/observability/logs/session/bye-20260629-222007.json +52 -0
  110. package/observability/logs/session/bye-20260629-222133.json +59 -0
  111. package/observability/logs/session/bye-20260629-222412.json +45 -0
  112. package/observability/logs/session/bye-20260629-223224.json +52 -0
  113. package/observability/logs/session/bye-20260629-224050.json +59 -0
  114. package/observability/logs/session/bye-20260630-105732.json +24 -0
  115. package/observability/logs/session/bye-20260630-211536.json +87 -0
  116. package/observability/logs/session/bye-20260630-223423.json +87 -0
  117. package/observability/logs/session/bye-20260630-232534.json +87 -0
  118. package/observability/logs/session/bye-20260701-003638.json +126 -0
  119. package/observability/logs/session/bye-20260701-003849.json +126 -0
  120. package/observability/logs/session/bye-20260701-004315.json +138 -0
  121. package/observability/logs/session/bye-20260701-004559.json +76 -0
  122. package/observability/logs/session/bye-20260701-005913.json +138 -0
  123. package/observability/logs/session/bye-20260701-010753.json +138 -0
  124. package/observability/logs/session/bye-20260703-194106.json +24 -0
  125. package/observability/logs/session/bye-20260703-233443.json +76 -0
  126. package/observability/logs/session/bye-20260703-233653.json +76 -0
  127. package/observability/logs/session/bye-20260704-150317.json +152 -0
  128. package/observability/logs/session/bye-20260704-150929.json +75 -0
  129. package/observability/logs/session/bye-20260704-151050.json +75 -0
  130. package/observability/logs/session/bye-20260705-115458.json +165 -0
  131. package/observability/logs/session/codegraph-status-20260701-004228.log +39 -0
  132. package/observability/logs/session/codegraph-status-20260701-004310.log +39 -0
  133. package/observability/logs/session/codegraph-status-20260701-005637.log +39 -0
  134. package/observability/logs/session/codegraph-status-20260701-005908.log +39 -0
  135. package/observability/logs/session/codegraph-status-20260701-010444.log +39 -0
  136. package/observability/logs/session/codegraph-status-20260701-010747.log +39 -0
  137. package/observability/logs/session/codegraph-status-20260704-150137.log +26 -0
  138. package/observability/logs/session/codegraph-status-20260704-150313.log +28 -0
  139. package/observability/logs/session/codegraph-status-20260704-154445.log +26 -0
  140. package/observability/logs/session/codegraph-status-20260705-115454.log +27 -0
  141. package/observability/logs/session/codegraph-sync-20260701-004211.log +88 -0
  142. package/observability/logs/session/codegraph-sync-20260701-004241.log +82 -0
  143. package/observability/logs/session/codegraph-sync-20260701-005620.log +89 -0
  144. package/observability/logs/session/codegraph-sync-20260701-005840.log +88 -0
  145. package/observability/logs/session/codegraph-sync-20260701-010419.log +93 -0
  146. package/observability/logs/session/codegraph-sync-20260701-010609.log +90 -0
  147. package/observability/logs/session/codegraph-sync-20260704-145856.log +70 -0
  148. package/observability/logs/session/codegraph-sync-20260704-150047.log +53 -0
  149. package/observability/logs/session/codegraph-sync-20260704-150204.log +44 -0
  150. package/observability/logs/session/codegraph-sync-20260704-154353.log +26 -0
  151. package/observability/logs/session/codegraph-sync-20260705-115337.log +32 -0
  152. package/observability/logs/session/hi-20260629-221459.json +38 -0
  153. package/observability/logs/session/hi-20260629-221945.json +59 -0
  154. package/observability/logs/session/hi-20260629-222108.json +66 -0
  155. package/observability/logs/session/hi-20260629-222352.json +45 -0
  156. package/observability/logs/session/hi-20260629-225719.json +59 -0
  157. package/observability/logs/session/hi-20260629-233722.json +52 -0
  158. package/observability/logs/session/hi-20260629-233913.json +198 -0
  159. package/observability/logs/session/hi-20260630-110156.json +177 -0
  160. package/observability/logs/session/hi-20260630-191243.json +184 -0
  161. package/observability/logs/session/hi-20260630-230050.json +234 -0
  162. package/observability/logs/session/hi-20260630-230523.json +227 -0
  163. package/observability/logs/session/hi-20260630-230855.json +227 -0
  164. package/observability/logs/session/hi-20260630-231638.json +227 -0
  165. package/observability/logs/session/hi-20260630-231756.json +219 -0
  166. package/observability/logs/session/hi-20260701-002842.json +234 -0
  167. package/observability/logs/session/hi-20260701-003458.json +282 -0
  168. package/observability/logs/session/hi-20260701-004233.json +290 -0
  169. package/observability/logs/session/hi-20260701-004554.json +236 -0
  170. package/observability/logs/session/hi-20260701-005641.json +290 -0
  171. package/observability/logs/session/hi-20260701-010450.json +290 -0
  172. package/observability/logs/session/hi-20260703-233854.json +312 -0
  173. package/observability/logs/session/hi-20260703-234448.json +318 -0
  174. package/observability/logs/session/hi-20260704-145907.json +353 -0
  175. package/observability/logs/session/hi-20260704-150141.json +392 -0
  176. package/observability/logs/session/hi-20260704-154448.json +392 -0
  177. package/observability/logs/session/iteration-value-refresh-20260701-004309.log +3 -0
  178. package/observability/logs/session/iteration-value-refresh-20260701-005907.log +3 -0
  179. package/observability/logs/session/iteration-value-refresh-20260701-010747.log +3 -0
  180. package/observability/logs/session/iteration-value-refresh-20260704-150312.log +3 -0
  181. package/observability/logs/session/iteration-value-refresh-20260705-115453.log +3 -0
  182. package/observability/logs/session/learning-loop-refresh-20260701-004309.log +3 -0
  183. package/observability/logs/session/learning-loop-refresh-20260701-005906.log +3 -0
  184. package/observability/logs/session/learning-loop-refresh-20260701-010745.log +3 -0
  185. package/observability/logs/session/learning-loop-refresh-20260704-150309.log +3 -0
  186. package/observability/logs/session/learning-loop-refresh-20260705-115450.log +3 -0
  187. package/observability/logs/session/project-notes-refresh-20260701-004227.log +1 -0
  188. package/observability/logs/session/project-notes-refresh-20260701-004308.log +1 -0
  189. package/observability/logs/session/project-notes-refresh-20260701-005636.log +1 -0
  190. package/observability/logs/session/project-notes-refresh-20260701-005906.log +1 -0
  191. package/observability/logs/session/project-notes-refresh-20260701-010443.log +1 -0
  192. package/observability/logs/session/project-notes-refresh-20260701-010744.log +1 -0
  193. package/observability/logs/session/project-notes-refresh-20260704-150133.log +1 -0
  194. package/observability/logs/session/project-notes-refresh-20260704-150309.log +1 -0
  195. package/observability/logs/session/project-notes-refresh-20260704-154442.log +1 -0
  196. package/observability/logs/session/project-notes-refresh-20260705-115449.log +1 -0
  197. package/observability/logs/session/repo-intake-20260701-004218.log +6 -0
  198. package/observability/logs/session/repo-intake-20260701-005626.log +6 -0
  199. package/observability/logs/session/repo-intake-20260701-010433.log +6 -0
  200. package/observability/logs/session/repo-intake-20260704-150053.log +6 -0
  201. package/observability/logs/session/repo-intake-20260704-154404.log +6 -0
  202. package/observability/logs/session/repo-intake-refresh-20260701-004247.log +6 -0
  203. package/observability/logs/session/repo-intake-refresh-20260701-005847.log +6 -0
  204. package/observability/logs/session/repo-intake-refresh-20260701-010615.log +6 -0
  205. package/observability/logs/session/repo-intake-refresh-20260704-150209.log +6 -0
  206. package/observability/logs/session/repo-intake-refresh-20260705-115342.log +6 -0
  207. package/observability/logs/session/repo-registry-validation-20260701-004234.log +2 -0
  208. package/observability/logs/session/repo-registry-validation-20260701-004556.log +2 -0
  209. package/observability/logs/session/repo-registry-validation-20260701-005833.log +2 -0
  210. package/observability/logs/session/repo-registry-validation-20260701-010601.log +2 -0
  211. package/observability/logs/session/repo-registry-validation-20260704-150155.log +2 -0
  212. package/observability/logs/session/repo-registry-validation-20260705-115323.log +2 -0
  213. package/observability/logs/session/repomix-refresh-20260701-003823.log +4236 -0
  214. package/observability/logs/session/repomix-refresh-20260701-004251.log +4275 -0
  215. package/observability/logs/session/repomix-refresh-20260701-005850.log +4248 -0
  216. package/observability/logs/session/repomix-refresh-20260701-010619.log +6531 -0
  217. package/observability/logs/session/repomix-refresh-20260704-150239.log +652 -0
  218. package/observability/logs/session/repomix-refresh-20260705-115419.log +178 -0
  219. package/observability/logs/session/routing-evals-20260701-004221.log +2 -0
  220. package/observability/logs/session/routing-evals-20260701-004235.log +2 -0
  221. package/observability/logs/session/routing-evals-20260701-005629.log +2 -0
  222. package/observability/logs/session/routing-evals-20260701-005834.log +2 -0
  223. package/observability/logs/session/routing-evals-20260701-010437.log +2 -0
  224. package/observability/logs/session/routing-evals-20260701-010603.log +2 -0
  225. package/observability/logs/session/routing-evals-20260704-150125.log +2 -0
  226. package/observability/logs/session/routing-evals-20260704-150157.log +2 -0
  227. package/observability/logs/session/routing-evals-20260704-154434.log +2 -0
  228. package/observability/logs/session/routing-evals-20260705-115327.log +2 -0
  229. package/observability/logs/session/sibling-discovery-refresh-20260701-004250.log +3 -0
  230. package/observability/logs/session/sibling-discovery-refresh-20260701-005850.log +3 -0
  231. package/observability/logs/session/sibling-discovery-refresh-20260701-010618.log +3 -0
  232. package/observability/logs/session/sibling-discovery-refresh-20260704-150238.log +3 -0
  233. package/observability/logs/session/sibling-discovery-refresh-20260705-115418.log +3 -0
  234. package/observability/logs.schema.json +95 -0
  235. package/observability/logs.schema.md +26 -0
  236. package/observability/metrics.md +35 -0
  237. package/observability/routing-evaluator.md +37 -0
  238. package/observability/scoring-rubric.md +38 -0
  239. package/optimization/ALWAYS_ON_OPTIMIZATION.md +98 -0
  240. package/optimization/caveman-mode.md +62 -0
  241. package/optimization/examples/login-bug.md +25 -0
  242. package/optimization/examples/sla-query.md +25 -0
  243. package/optimization/optimization-routing.md +30 -0
  244. package/optimization/policies/response-style.policy.md +45 -0
  245. package/optimization/policies/token-budget.policy.md +28 -0
  246. package/optimization/profiles/always-on.profile.md +19 -0
  247. package/optimization/profiles/architecture.profile.md +20 -0
  248. package/optimization/profiles/debug.profile.md +20 -0
  249. package/optimization/profiles/enterprise-rag.profile.md +20 -0
  250. package/optimization/profiles/training.profile.md +20 -0
  251. package/optimization/token-saver.md +58 -0
  252. package/orchestrator/corporate-routing.md +51 -0
  253. package/orchestrator/decision-matrix.md +33 -0
  254. package/orchestrator/fallback.md +29 -0
  255. package/orchestrator/repo-routing.md +58 -0
  256. package/orchestrator/router.md +75 -0
  257. package/orchestrator/wiki/__init__.py +5 -0
  258. package/orchestrator/wiki/graph_consolidator.py +506 -0
  259. package/orchestrator/wiki/incremental_engine.py +57 -0
  260. package/orchestrator/wiki/plugin_manager.py +40 -0
  261. package/package.json +70 -0
  262. package/policies/context-policy.md +25 -0
  263. package/policies/cost-policy.md +25 -0
  264. package/policies/repo-intake-policy.md +36 -0
  265. package/policies/security-policy.md +25 -0
  266. package/repo-intake/README.md +81 -0
  267. package/repo-intake/templates/agent.template.md +4 -0
  268. package/repo-intake/templates/context-manifest.template.md +4 -0
  269. package/repo-intake/templates/skill.template.md +3 -0
  270. package/repo-registry/repos.schema.json +102 -0
  271. package/repo-registry/repos.template.json +26 -0
  272. package/requirements.txt +1 -0
  273. package/scripts/README.md +109 -0
  274. package/scripts/__init__.py +0 -0
  275. package/scripts/bootstrap-portable.cmd +4 -0
  276. package/scripts/bootstrap-portable.ps1 +90 -0
  277. package/scripts/context/azure-rag-mcp-adapter.py +1 -0
  278. package/scripts/context/build-graphify.ps1 +1 -0
  279. package/scripts/context/build-repomix.ps1 +1 -0
  280. package/scripts/discovery/discover-boost-repos.cmd +4 -0
  281. package/scripts/discovery/discover-boost-repos.py +186 -0
  282. package/scripts/discovery/refresh-project-notes.py +179 -0
  283. package/scripts/github/INSTALL.md +295 -0
  284. package/scripts/github/README.md +455 -0
  285. package/scripts/github/apply-boost-templates.ps1 +163 -0
  286. package/scripts/github/apply-single-repo.ps1 +85 -0
  287. package/scripts/github/create-devlog.ps1 +380 -0
  288. package/scripts/github/manage-issues.ps1 +332 -0
  289. package/scripts/github/sync-repo.ps1 +267 -0
  290. package/scripts/intake/agent-pipeline-preflight.py +151 -0
  291. package/scripts/intake/init-template-registry.cmd +4 -0
  292. package/scripts/intake/init-template-registry.ps1 +258 -0
  293. package/scripts/intake/repo-intake.py +354 -0
  294. package/scripts/intake/resolve-routing.py +620 -0
  295. package/scripts/intake/run-repo-intake.cmd +4 -0
  296. package/scripts/intake/run-repo-intake.ps1 +46 -0
  297. package/scripts/intake/run-routing-evals.py +147 -0
  298. package/scripts/intake/validate-database-routing.py +91 -0
  299. package/scripts/intake/validate-rag-routing.py +85 -0
  300. package/scripts/intake/validate-repo-registry.ps1 +20 -0
  301. package/scripts/intake/validate-repo-registry.py +286 -0
  302. package/scripts/intake/validate-security.py +170 -0
  303. package/scripts/learning/autolearning-gate.py +71 -0
  304. package/scripts/learning/chat-token-usage-report.py +326 -0
  305. package/scripts/learning/ingest-copilot-session-usage.py +421 -0
  306. package/scripts/learning/iteration-value-report.py +275 -0
  307. package/scripts/learning/learning-loop-report.py +272 -0
  308. package/scripts/learning/record-iteration-metrics.py +116 -0
  309. package/scripts/learning/record-learning-feedback.py +91 -0
  310. package/scripts/ops/bye.ps1 +620 -0
  311. package/scripts/ops/compare-demo-results.ps1 +826 -0
  312. package/scripts/ops/demo1.ps1 +11 -0
  313. package/scripts/ops/demo2.ps1 +11 -0
  314. package/scripts/ops/demo3.ps1 +11 -0
  315. package/scripts/ops/demo4.ps1 +11 -0
  316. package/scripts/ops/hi.ps1 +1045 -0
  317. package/scripts/ops/llm-token-probe.py +164 -0
  318. package/scripts/ops/run-all-demos.ps1 +39 -0
  319. package/scripts/ops/run-demo-block.ps1 +85 -0
  320. package/scripts/ops/run-demo-session.ps1 +532 -0
  321. package/scripts/setup/optimize-engines.ps1 +183 -0
  322. package/scripts/setup/setup-codegraph.ps1 +3 -0
  323. package/scripts/setup/setup-gitnexus.ps1 +3 -0
  324. package/scripts/setup/setup-graphify.ps1 +2 -0
  325. package/scripts/setup/setup-prerequisites.ps1 +205 -0
  326. package/scripts/setup/start-gitnexus.ps1 +71 -0
  327. package/scripts/setup/validate-context.ps1 +361 -0
  328. package/scripts/wiki/__init__.py +0 -0
  329. package/scripts/wiki/compiler_main.py +189 -0
  330. package/scripts/wiki/normalizer.py +206 -0
  331. package/scripts/wiki/providers/base_provider.py +25 -0
  332. package/scripts/wiki/providers/codegraph_provider.py +49 -0
  333. package/scripts/wiki/providers/graphify_provider.py +49 -0
  334. package/scripts/wiki/providers/repo_content_provider.py +408 -0
  335. package/scripts/wiki/query_autodocs.py +60 -0
  336. package/scripts/wiki/validator.py +183 -0
  337. package/scripts/wiki/wiki_compiler.py +5 -0
  338. package/specs/architecture.spec.md +32 -0
  339. package/specs/azure-rag.spec.md +28 -0
  340. package/specs/coding-standards.spec.md +24 -0
  341. package/specs/database.spec.md +29 -0
  342. package/specs/migration.spec.md +26 -0
  343. package/specs/observability.spec.md +24 -0
  344. package/specs/optimization.spec.md +31 -0
  345. package/specs/rag.spec.md +30 -0
  346. package/specs/repo-intake.spec.md +25 -0
  347. package/specs/routing.spec.md +24 -0
  348. package/specs/security.spec.md +30 -0
  349. package/specs/wiki/provider-contract.json +33 -0
  350. package/specs/wiki/unified-knowledge-graph.json +25 -0
  351. package/templates/BOOST_README_TEMPLATE.md +96 -0
  352. package/templates/CODEOWNERS_TEMPLATE.txt +29 -0
  353. package/templates/LICENSE_MIT_TEMPLATE.txt +21 -0
  354. package/templates/PERSONAL_README_TEMPLATE.md +90 -0
  355. package/templates/README_boost_RAG_Azure.md +46 -0
  356. package/templates/README_boost_azure_iot_edge.md +48 -0
  357. package/templates/README_boost_backend.md +47 -0
  358. package/templates/README_boost_dba.md +57 -0
  359. package/templates/ci.yml.template +73 -0
  360. package/tooling/tooling.manifest.json +72 -0
@@ -0,0 +1,620 @@
1
+ from __future__ import annotations
2
+
3
+ import argparse
4
+ import json
5
+ import uuid
6
+ from datetime import datetime, timezone
7
+ from pathlib import Path
8
+ from typing import Any
9
+
10
+ try:
11
+ import yaml # type: ignore
12
+ except Exception:
13
+ yaml = None
14
+
15
+
16
+ def utc_now() -> str:
17
+ return datetime.now(timezone.utc).isoformat()
18
+
19
+
20
+ def parse_simple_yml(path: Path) -> dict[str, Any]:
21
+ repos: list[dict[str, Any]] = []
22
+ cur: dict[str, Any] | None = None
23
+ for line in path.read_text(encoding="utf-8").splitlines():
24
+ st = line.strip()
25
+ if st.startswith("- name:"):
26
+ if cur:
27
+ repos.append(cur)
28
+ cur = {"name": st.split(":", 1)[1].strip()}
29
+ elif cur and ":" in st and not st.startswith("#"):
30
+ k, v = st.split(":", 1)
31
+ k = k.strip()
32
+ v = v.strip().strip('"')
33
+ if k in {"domain", "location", "type", "version"}:
34
+ cur[k] = v
35
+ if cur:
36
+ repos.append(cur)
37
+ return {"schema_version": "1.0", "repos": repos}
38
+
39
+
40
+ def load_registry(path: Path) -> dict[str, Any]:
41
+ content = path.read_text(encoding="utf-8")
42
+ if content.lstrip().startswith("{"):
43
+ return json.loads(content)
44
+ if yaml is not None:
45
+ data = yaml.safe_load(content) or {}
46
+ if isinstance(data, dict):
47
+ return data
48
+ return parse_simple_yml(path)
49
+
50
+
51
+ def load_capability_index(generated_root: Path) -> list[dict[str, Any]]:
52
+ items: list[dict[str, Any]] = []
53
+ for path in generated_root.glob("*/capabilities/capability.json"):
54
+ try:
55
+ items.append(json.loads(path.read_text(encoding="utf-8")))
56
+ except Exception:
57
+ continue
58
+ return items
59
+
60
+
61
+ def load_manifest_index(generated_root: Path) -> dict[str, dict[str, Any]]:
62
+ manifests: dict[str, dict[str, Any]] = {}
63
+ for path in generated_root.glob("*/context-manifests/manifest.json"):
64
+ try:
65
+ data = json.loads(path.read_text(encoding="utf-8"))
66
+ except Exception:
67
+ continue
68
+ name = str(data.get("repo", "")).strip()
69
+ if name:
70
+ manifests[name] = data
71
+ return manifests
72
+
73
+
74
+ def domain_defaults(domain: str) -> dict[str, str]:
75
+ defaults = {
76
+ "dba": {"agent": "dba", "engine": "Graphify", "capability": "database-analysis"},
77
+ "iot": {"agent": "iot", "engine": "GitNexus/CodeGraph + Graphify", "capability": "iot-architecture"},
78
+ "azure-rag": {"agent": "rag-azure", "engine": "Azure RAG Builder", "capability": "azure-rag-enterprise"},
79
+ "rag": {"agent": "rag-local", "engine": "Graphify", "capability": "rag-knowledge"},
80
+ "backend": {"agent": "backend", "engine": "CodeGraph", "capability": "backend-coding"},
81
+ "frontend": {"agent": "frontend-agent", "engine": "CodeGraph", "capability": "frontend-coding"},
82
+ "ux-ui": {"agent": "ux-ui", "engine": "Graphify", "capability": "ux-ui-governance"},
83
+ "community-content": {"agent": "community-manager", "engine": "Graphify", "capability": "community-content"},
84
+ "legacy": {"agent": "legacy", "engine": "GitNexus", "capability": "legacy-migration"},
85
+ }
86
+ return defaults.get(domain, defaults["backend"])
87
+
88
+
89
+ def normalize_frontend_vs_ux_ui(*, domain: str, intent: str, source_type: str) -> tuple[str, str | None]:
90
+ if domain not in {"frontend", "ux-ui"}:
91
+ return domain, None
92
+
93
+ intent_l = intent.lower()
94
+ source_l = source_type.lower()
95
+
96
+ ux_ui_keywords = {
97
+ "review",
98
+ "design",
99
+ "design-system",
100
+ "accesibilidad",
101
+ "accessibility",
102
+ "usabilidad",
103
+ "usability",
104
+ "consistency",
105
+ "consistencia",
106
+ "governance",
107
+ "heuristic",
108
+ "heuristica",
109
+ }
110
+ frontend_keywords = {
111
+ "bug",
112
+ "bug-fix",
113
+ "fix",
114
+ "feature",
115
+ "implement",
116
+ "implementation",
117
+ "refactor",
118
+ "ui",
119
+ "component",
120
+ "frontend",
121
+ }
122
+
123
+ if source_l == "technical-docs" or any(k in intent_l for k in ux_ui_keywords):
124
+ normalized = "ux-ui"
125
+ elif source_l == "code" and any(k in intent_l for k in frontend_keywords):
126
+ normalized = "frontend"
127
+ else:
128
+ normalized = domain
129
+
130
+ if normalized != domain:
131
+ return normalized, f"domain_normalized={domain}->{normalized}"
132
+ return normalized, None
133
+
134
+
135
+ def is_repo_approved(repo: dict[str, Any]) -> bool:
136
+ approval = repo.get("approval")
137
+ if not isinstance(approval, dict):
138
+ return False
139
+ return approval.get("status") == "approved"
140
+
141
+
142
+ def pick_route(
143
+ *,
144
+ registry: dict[str, Any],
145
+ capabilities: list[dict[str, Any]],
146
+ manifests: dict[str, dict[str, Any]],
147
+ domain: str,
148
+ capability: str | None,
149
+ ) -> tuple[dict[str, Any], list[str]]:
150
+ notes: list[str] = []
151
+ repos = registry.get("repos", []) if isinstance(registry.get("repos", []), list) else []
152
+ repo_by_name = {str(r.get("name", "")): r for r in repos if isinstance(r, dict)}
153
+
154
+ candidates: list[tuple[dict[str, Any], str]] = []
155
+ if capability:
156
+ for c in capabilities:
157
+ if str(c.get("capability", "")).strip() == capability:
158
+ candidates.append((c, "selected_by=capability"))
159
+ for c in capabilities:
160
+ if str(c.get("domain", "")).strip() == domain:
161
+ candidates.append((c, "selected_by=domain_capability_index"))
162
+
163
+ candidate: dict[str, Any] | None = None
164
+ for item, reason in candidates:
165
+ repo_name = str(item.get("repo", "")).strip()
166
+ registry_repo = repo_by_name.get(repo_name)
167
+ if not registry_repo:
168
+ notes.append(f"repo_not_found_in_registry={repo_name}")
169
+ continue
170
+ if not is_repo_approved(registry_repo):
171
+ notes.append(f"repo_not_approved={repo_name}")
172
+ continue
173
+
174
+ manifest = manifests.get(repo_name, {})
175
+ deps = manifest.get("dependencies", []) if isinstance(manifest, dict) else []
176
+ unresolved: list[str] = []
177
+ for dep in deps:
178
+ if not isinstance(dep, dict):
179
+ continue
180
+ dep_ref = str(dep.get("ref", "")).strip()
181
+ if not dep_ref:
182
+ continue
183
+ dep_repo = repo_by_name.get(dep_ref)
184
+ dep_manifest_exists = dep_ref in manifests
185
+ dep_ok = dep_repo is not None and is_repo_approved(dep_repo) and dep_manifest_exists
186
+ if not dep_ok:
187
+ unresolved.append(dep_ref)
188
+ if unresolved:
189
+ notes.append("unresolved_dependencies=" + ",".join(unresolved))
190
+ continue
191
+
192
+ candidate = item
193
+ notes.append(reason)
194
+ break
195
+
196
+ if candidate is not None:
197
+ route = {
198
+ "agent": str(candidate.get("agent", "backend")),
199
+ "engine": str(candidate.get("engine", "CodeGraph")),
200
+ "capability": str(candidate.get("capability", "backend-coding")),
201
+ "repo": str(candidate.get("repo", "")),
202
+ }
203
+ return route, notes
204
+
205
+ approved_repos_for_domain = [
206
+ r
207
+ for r in repos
208
+ if isinstance(r, dict)
209
+ and str(r.get("domain", "")).strip() == domain
210
+ and is_repo_approved(r)
211
+ ]
212
+
213
+ fallback = domain_defaults(domain)
214
+ if approved_repos_for_domain:
215
+ notes.append("selected_by=domain_default")
216
+ else:
217
+ # Some logical domains can be valid without a dedicated onboarded repo.
218
+ notes.append("selected_by=domain_default_expected")
219
+ return {
220
+ "agent": fallback["agent"],
221
+ "engine": fallback["engine"],
222
+ "capability": fallback["capability"],
223
+ "repo": "",
224
+ }, notes
225
+
226
+
227
+ def resolve_sources_and_grounding(*, route: dict[str, Any], source_type: str, repo_root: Path) -> tuple[list[str], bool]:
228
+ sources: list[str] = []
229
+
230
+ repo_name = str(route.get("repo", "")).strip()
231
+ if repo_name:
232
+ sources.append(f"repo:{repo_name}")
233
+
234
+ engine = str(route.get("engine", "")).lower()
235
+ if source_type == "technical-docs":
236
+ graphify_index = repo_root / "context/graphify-out/graph.json"
237
+ if graphify_index.exists():
238
+ sources.append("graphify:index")
239
+
240
+ if "gitnexus" in engine:
241
+ gitnexus_index = repo_root / ".gitnexus/lbug"
242
+ if gitnexus_index.exists():
243
+ sources.append("gitnexus:index")
244
+
245
+ return sources, bool(sources)
246
+
247
+
248
+ def profile_for_source(source_type: str) -> tuple[str, str, str]:
249
+ if source_type == "corporate-docs":
250
+ return "evidence-first", "evidence-first", "chunk"
251
+ if source_type == "code":
252
+ return "strict", "full", "symbol"
253
+ if source_type == "snapshot":
254
+ return "strict", "lite", "snapshot-scope"
255
+ return "balanced", "lite", "manifest"
256
+
257
+
258
+ def memory_selection_for_source(source_type: str) -> tuple[list[str], str]:
259
+ if source_type == "code":
260
+ return ["repo-memory", "routing-memory", "code-memory"], "code route uses structural retrieval"
261
+ if source_type == "corporate-docs":
262
+ return ["repo-memory", "routing-memory", "enterprise-memory"], "corporate evidence route"
263
+ if source_type == "technical-docs":
264
+ return ["repo-memory", "routing-memory", "knowledge-memory"], "technical docs route"
265
+ return ["repo-memory", "routing-memory"], "capability-aware routing"
266
+
267
+
268
+ def runtime_requirements_for_route(source_type: str, engine: str) -> list[str]:
269
+ base = ["token-saver-mcp", "codebase-memory-mcp"]
270
+ requirements: list[str] = list(base)
271
+
272
+ engine_l = engine.lower()
273
+ if "codegraph" in engine_l:
274
+ requirements.extend(["codegraph-command", "codegraph-index"])
275
+ if "gitnexus" in engine_l:
276
+ requirements.append("gitnexus-command")
277
+ if "graphify" in engine_l:
278
+ requirements.append("graphify-index")
279
+ if "azure rag" in engine_l:
280
+ requirements.append("azure-rag-connector")
281
+ if "repomix" in engine_l:
282
+ requirements.append("repomix-command")
283
+
284
+ # Keep source-specific connector requirement explicit for evidence routes.
285
+ if source_type == "corporate-docs" and "azure-rag-connector" not in requirements:
286
+ requirements.append("azure-rag-connector")
287
+
288
+ seen: set[str] = set()
289
+ deduped: list[str] = []
290
+ for req in requirements:
291
+ if req in seen:
292
+ continue
293
+ seen.add(req)
294
+ deduped.append(req)
295
+ return deduped
296
+
297
+
298
+ def hitl_policy_for_event(*, intent: str, domain: str, source_type: str, fallback: bool, notes: list[str]) -> dict[str, Any]:
299
+ text = f"{intent} {domain} {source_type} {' '.join(notes)}".lower()
300
+
301
+ high_impact_keywords = [
302
+ "deploy",
303
+ "production",
304
+ "migrate",
305
+ "migration",
306
+ "delete",
307
+ "drop",
308
+ "remove",
309
+ "destroy",
310
+ "governance",
311
+ "policy",
312
+ "rbac",
313
+ "security",
314
+ "role",
315
+ ]
316
+ destructive_keywords = ["delete", "drop", "destroy", "remove"]
317
+
318
+ triggered = [k for k in high_impact_keywords if k in text]
319
+ destructive = any(k in text for k in destructive_keywords)
320
+ unresolved_deps = any(n.startswith("unresolved_dependencies=") for n in notes)
321
+
322
+ required = bool(triggered) or fallback or unresolved_deps
323
+ if required:
324
+ reasons: list[str] = []
325
+ if triggered:
326
+ reasons.append("high_impact:" + ",".join(sorted(set(triggered))))
327
+ if fallback:
328
+ reasons.append("routing_fallback")
329
+ if unresolved_deps:
330
+ reasons.append("unresolved_dependencies")
331
+
332
+ action = "block_until_human_approval" if destructive else "request_human_confirmation"
333
+ return {
334
+ "mode": "always_on_auto",
335
+ "required": True,
336
+ "action": action,
337
+ "reason": ";".join(reasons),
338
+ }
339
+
340
+ return {
341
+ "mode": "always_on_auto",
342
+ "required": False,
343
+ "action": "none",
344
+ "reason": "low_risk_route",
345
+ }
346
+
347
+
348
+ def select_prompt_for_route(
349
+ *,
350
+ intent: str,
351
+ domain: str,
352
+ source_type: str,
353
+ agent: str,
354
+ prompt_root: Path,
355
+ ) -> tuple[str, bool]:
356
+ """Return prompt path relative to repo root and whether it exists."""
357
+ # Highest-priority route: corporate evidence questions.
358
+ if source_type == "corporate-docs" or domain == "azure-rag" or agent == "rag-azure":
359
+ candidate = ".github/prompts/azure-rag.query.prompt.md"
360
+ # Code-centric workflows.
361
+ elif domain == "frontend":
362
+ candidate = ".github/prompts/frontend.code.prompt.md"
363
+ elif domain == "backend" and ("bug" in intent or intent == "bug-fix"):
364
+ candidate = ".github/prompts/backend.fix-bug.prompt.md"
365
+ elif domain == "community-content" or agent == "community-manager":
366
+ candidate = ".github/prompts/community.post.prompt.md"
367
+ elif domain == "ux-ui" or agent == "ux-ui":
368
+ candidate = ".github/prompts/ux-ui.review.prompt.md"
369
+ elif domain == "legacy":
370
+ candidate = ".github/prompts/legacy.impact-analysis.prompt.md"
371
+ # Data/document technical flows.
372
+ elif domain == "dba":
373
+ candidate = ".github/prompts/dba.query-review.prompt.md"
374
+ elif agent == "rag-local" or source_type == "technical-docs":
375
+ candidate = ".github/prompts/rag.knowledge-answer.prompt.md"
376
+ elif domain == "snapshot" or source_type == "snapshot":
377
+ candidate = ".github/prompts/cavecrew.prompt.md"
378
+ else:
379
+ candidate = ".github/prompts/auto-route.prompt.md"
380
+
381
+ exists = (prompt_root / candidate).exists()
382
+ return candidate, exists
383
+
384
+
385
+ def select_skill_for_route(*, capability: str, skill_root: Path) -> tuple[str, bool]:
386
+ """Return skill file path relative to repo root and whether it exists."""
387
+ normalized = capability.strip()
388
+ candidate = f".github/skills/{normalized}/SKILL.md" if normalized else ".github/skills/token-saver/SKILL.md"
389
+ exists = (skill_root / candidate).exists()
390
+ if exists:
391
+ return candidate, True
392
+
393
+ # Safe fallback: always-on optimization skill.
394
+ fallback = ".github/skills/token-saver/SKILL.md"
395
+ return fallback, (skill_root / fallback).exists()
396
+
397
+
398
+ def append_jsonl(path: Path, event: dict[str, Any]) -> None:
399
+ path.parent.mkdir(parents=True, exist_ok=True)
400
+ with path.open("a", encoding="utf-8") as fh:
401
+ fh.write(json.dumps(event, ensure_ascii=False) + "\n")
402
+
403
+
404
+ def build_tool_mode(local_tools: int, remote_tools: int) -> str:
405
+ if local_tools > 0 and remote_tools == 0:
406
+ return "local-only"
407
+ if local_tools == 0 and remote_tools > 0:
408
+ return "remote-only"
409
+ if local_tools > 0 and remote_tools > 0:
410
+ return "hybrid"
411
+ return "model-only"
412
+
413
+
414
+ def build_iteration_metric(
415
+ *,
416
+ event_id: str,
417
+ model: str,
418
+ input_tokens: int,
419
+ output_tokens: int,
420
+ estimated_cost_usd: float,
421
+ local_tools: int,
422
+ remote_tools: int,
423
+ notes: str,
424
+ ) -> dict[str, Any]:
425
+ total_tokens = input_tokens + output_tokens
426
+ return {
427
+ "timestamp": utc_now(),
428
+ "event_id": event_id,
429
+ "model": model,
430
+ "cost": {
431
+ "input_tokens": input_tokens,
432
+ "output_tokens": output_tokens,
433
+ "total_tokens": total_tokens,
434
+ "estimated_cost_usd": round(float(estimated_cost_usd), 6),
435
+ },
436
+ "execution": {
437
+ "local_tools": local_tools,
438
+ "remote_tools": remote_tools,
439
+ "tool_mode": build_tool_mode(local_tools, remote_tools),
440
+ },
441
+ "notes": notes,
442
+ }
443
+
444
+
445
+ def main() -> int:
446
+ parser = argparse.ArgumentParser(description="Resolve routing with capability-aware local-first policy.")
447
+ parser.add_argument("--input", required=True, help="Original user input for routing event")
448
+ parser.add_argument("--intent", required=True, help="Detected intent")
449
+ parser.add_argument("--domain", required=True, help="Detected domain")
450
+ parser.add_argument("--source-type", required=True, help="code|technical-docs|corporate-docs|snapshot")
451
+ parser.add_argument("--capability", default="", help="Optional requested capability")
452
+ parser.add_argument("--registry", default="repo-registry/repos.yml")
453
+ parser.add_argument("--generated-root", default="repo-intake/generated")
454
+ parser.add_argument("--output", default="observability/logs/routing-decisions.jsonl")
455
+ parser.add_argument("--input-tokens", type=int, default=-1, help="Real input tokens for this call. Provide with --output-tokens.")
456
+ parser.add_argument("--output-tokens", type=int, default=-1, help="Real output tokens for this call. Provide with --input-tokens.")
457
+ parser.add_argument("--estimated-cost-usd", type=float, default=0.0, help="Estimated cost in USD for this call.")
458
+ parser.add_argument("--model", default="unknown", help="Model used by this call, e.g. GPT-5.3-Codex")
459
+ parser.add_argument("--local-tools", type=int, default=0, help="Count of local tools used by this call")
460
+ parser.add_argument("--remote-tools", type=int, default=0, help="Count of remote tools used by this call")
461
+ parser.add_argument("--metrics-output", default="observability/logs/iteration-metrics.jsonl", help="JSONL output for per-call token metrics")
462
+ args = parser.parse_args()
463
+
464
+ has_input_tokens = args.input_tokens >= 0
465
+ has_output_tokens = args.output_tokens >= 0
466
+ if has_input_tokens != has_output_tokens:
467
+ print("Both --input-tokens and --output-tokens must be provided together (or omitted together).")
468
+ return 1
469
+ if args.input_tokens < -1 or args.output_tokens < -1:
470
+ print("Token values must be >= 0 when provided.")
471
+ return 1
472
+ if args.estimated_cost_usd < 0:
473
+ print("--estimated-cost-usd must be >= 0")
474
+ return 1
475
+ if args.local_tools < 0 or args.remote_tools < 0:
476
+ print("--local-tools and --remote-tools must be >= 0")
477
+ return 1
478
+
479
+ repo_root = Path(__file__).resolve().parents[2]
480
+ registry = load_registry((repo_root / args.registry).resolve())
481
+ capabilities = load_capability_index((repo_root / args.generated_root).resolve())
482
+ manifests = load_manifest_index((repo_root / args.generated_root).resolve())
483
+
484
+ normalized_domain, normalized_note = normalize_frontend_vs_ux_ui(
485
+ domain=args.domain,
486
+ intent=args.intent,
487
+ source_type=args.source_type,
488
+ )
489
+
490
+ route, notes = pick_route(
491
+ registry=registry,
492
+ capabilities=capabilities,
493
+ manifests=manifests,
494
+ domain=normalized_domain,
495
+ capability=args.capability.strip() or None,
496
+ )
497
+ if normalized_note:
498
+ notes.append(normalized_note)
499
+
500
+ token_saver_profile, caveman_profile, context_strategy = profile_for_source(args.source_type)
501
+ fallback = any(n == "selected_by=domain_default" for n in notes)
502
+ sources, grounded = resolve_sources_and_grounding(route=route, source_type=args.source_type, repo_root=repo_root)
503
+
504
+ selected_memories, memory_reason = memory_selection_for_source(args.source_type)
505
+ selected_prompt, prompt_exists = select_prompt_for_route(
506
+ intent=args.intent,
507
+ domain=normalized_domain,
508
+ source_type=args.source_type,
509
+ agent=route["agent"],
510
+ prompt_root=repo_root,
511
+ )
512
+ if not prompt_exists:
513
+ notes.append(f"prompt_not_found={selected_prompt}")
514
+ selected_skill, skill_exists = select_skill_for_route(
515
+ capability=route["capability"],
516
+ skill_root=repo_root,
517
+ )
518
+ if not skill_exists:
519
+ notes.append(f"skill_not_found={selected_skill}")
520
+ event_id = str(uuid.uuid4())
521
+ hitl = hitl_policy_for_event(
522
+ intent=args.intent,
523
+ domain=normalized_domain,
524
+ source_type=args.source_type,
525
+ fallback=fallback,
526
+ notes=notes,
527
+ )
528
+ event = {
529
+ "event_id": event_id,
530
+ "timestamp": utc_now(),
531
+ "input": args.input,
532
+ "intent": args.intent,
533
+ "domain": normalized_domain,
534
+ "source_type": args.source_type,
535
+ "agent": route["agent"],
536
+ "engine": route["engine"],
537
+ "optimization_profile": f"{token_saver_profile}+{caveman_profile}",
538
+ "fallback": fallback,
539
+ "grounded": grounded,
540
+ "sources": sources,
541
+ "notes": ";".join(notes) if notes else "",
542
+ "prompt": {
543
+ "selected": selected_prompt,
544
+ "exists": prompt_exists,
545
+ "selection_mode": "auto",
546
+ },
547
+ "skill": {
548
+ "selected": selected_skill,
549
+ "exists": skill_exists,
550
+ "selection_mode": "auto",
551
+ },
552
+ "optimization": {
553
+ "token_saver": "always_on",
554
+ "token_saver_profile": token_saver_profile,
555
+ "caveman": "always_on",
556
+ "caveman_profile": caveman_profile,
557
+ "sources_preserved": True,
558
+ "context_reduction_strategy": context_strategy,
559
+ },
560
+ "memory": {
561
+ "selected": selected_memories,
562
+ "reason": memory_reason,
563
+ },
564
+ "requirements": runtime_requirements_for_route(args.source_type, route["engine"]),
565
+ "learning": {
566
+ "used_pattern": f"{normalized_domain}+{args.intent}",
567
+ # Real success must be written post-execution by the feedback updater.
568
+ "success": None,
569
+ "outcome_status": "pending",
570
+ "fallback": fallback,
571
+ "confidence": 0.9 if not fallback else 0.7,
572
+ },
573
+ "hitl": hitl,
574
+ }
575
+
576
+ if has_input_tokens and has_output_tokens:
577
+ total_tokens = args.input_tokens + args.output_tokens
578
+ event["usage"] = {
579
+ "input_tokens": args.input_tokens,
580
+ "output_tokens": args.output_tokens,
581
+ "total_tokens": total_tokens,
582
+ "estimated_cost_usd": round(float(args.estimated_cost_usd), 6),
583
+ }
584
+
585
+ metrics_recorded = False
586
+ metrics_output_path = ""
587
+ if has_input_tokens and has_output_tokens:
588
+ metric = build_iteration_metric(
589
+ event_id=event_id,
590
+ model=str(args.model),
591
+ input_tokens=args.input_tokens,
592
+ output_tokens=args.output_tokens,
593
+ estimated_cost_usd=float(args.estimated_cost_usd),
594
+ local_tools=args.local_tools,
595
+ remote_tools=args.remote_tools,
596
+ notes="auto-recorded by resolve-routing",
597
+ )
598
+ metrics_path = (repo_root / args.metrics_output).resolve()
599
+ append_jsonl(metrics_path, metric)
600
+ metrics_recorded = True
601
+ metrics_output_path = args.metrics_output.replace("\\", "/")
602
+
603
+ event["telemetry"] = {
604
+ "iteration_metrics_recorded": metrics_recorded,
605
+ "metrics_output": metrics_output_path,
606
+ }
607
+
608
+ output_path = (repo_root / args.output).resolve()
609
+ append_jsonl(output_path, event)
610
+
611
+ print(json.dumps(event, indent=2, ensure_ascii=False))
612
+ print(f"Event appended to: {output_path}")
613
+ if metrics_recorded:
614
+ print(f"Metrics appended to: {metrics_output_path}")
615
+ return 0
616
+
617
+
618
+ if __name__ == "__main__":
619
+ raise SystemExit(main())
620
+
@@ -0,0 +1,4 @@
1
+ @echo off
2
+ setlocal
3
+ pwsh -NoProfile -ExecutionPolicy Bypass -File "%~dp0run-repo-intake.ps1"
4
+ exit /b %ERRORLEVEL%
@@ -0,0 +1,46 @@
1
+ Set-StrictMode -Version Latest
2
+ $ErrorActionPreference = "Stop"
3
+
4
+ $repoRoot = (Resolve-Path (Join-Path $PSScriptRoot '..\..')).Path
5
+ Set-Location $repoRoot
6
+
7
+ $registryPath = Join-Path $repoRoot 'repo-registry\repos.yml'
8
+ if (-not (Test-Path $registryPath)) {
9
+ Write-Error "Missing repo registry: $registryPath. Run .\\scripts\\intake\\init-template-registry.cmd first."
10
+ exit 1
11
+ }
12
+
13
+ $registryRaw = Get-Content -Raw -Path $registryPath
14
+ $registry = $registryRaw | ConvertFrom-Json -Depth 20
15
+ $registryMode = 'enterprise'
16
+ if (($registry.PSObject.Properties.Name -contains 'registry_mode') -and [string]::IsNullOrWhiteSpace([string]$registry.registry_mode) -eq $false) {
17
+ $registryMode = [string]$registry.registry_mode
18
+ }
19
+
20
+ $repos = @()
21
+ if (($registry.PSObject.Properties.Name -contains 'repos') -and $null -ne $registry.repos) {
22
+ $repos = @($registry.repos)
23
+ }
24
+
25
+ if ($registryMode -eq 'template') {
26
+ Write-Host "Validating repo registry (template mode)..."
27
+ pwsh -NoProfile -ExecutionPolicy Bypass -File .\scripts\intake\validate-repo-registry.ps1
28
+ }
29
+ else {
30
+ Write-Host "Validating repo registry (strict mode)..."
31
+ pwsh -NoProfile -ExecutionPolicy Bypass -File .\scripts\intake\validate-repo-registry.ps1 -Strict
32
+ }
33
+ if ($LASTEXITCODE -ne 0) {
34
+ Write-Error "Repo registry validation failed. Aborting intake generation."
35
+ exit $LASTEXITCODE
36
+ }
37
+
38
+ Write-Host "Running repo intake generation..."
39
+ python .\scripts\intake\repo-intake.py
40
+
41
+ if ($registryMode -eq 'template' -and $repos.Count -eq 0) {
42
+ Write-Host "Template registry is empty. Intake reports were generated with 0 repositories."
43
+ Write-Host "Edit repo-registry\\repos.yml and rerun .\\scripts\\intake\\run-repo-intake.cmd when you add repos."
44
+ }
45
+
46
+ Write-Host "Repo intake completed."