@tyroneross/build-loop 0.30.3

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 (389) hide show
  1. package/.agents/plugins/marketplace.json +10 -0
  2. package/.claude-plugin/marketplace.json +38 -0
  3. package/.claude-plugin/plugin.json +30 -0
  4. package/.codex-plugin/plugin.json +41 -0
  5. package/.cursor/rules/build-loop-surface.mdc +22 -0
  6. package/AGENTS.md +498 -0
  7. package/LICENSE +202 -0
  8. package/README.md +403 -0
  9. package/agents/alignment-checker.md +124 -0
  10. package/agents/api-assessor.md +136 -0
  11. package/agents/architecture-scout.md +185 -0
  12. package/agents/assessment-orchestrator.md +163 -0
  13. package/agents/build-orchestrator.md +198 -0
  14. package/agents/database-assessor.md +133 -0
  15. package/agents/design-contract-specialist.md +239 -0
  16. package/agents/fact-checker.md +69 -0
  17. package/agents/fix-critique.md +161 -0
  18. package/agents/frontend-assessor.md +136 -0
  19. package/agents/implementer.md +185 -0
  20. package/agents/independent-auditor.md +143 -0
  21. package/agents/mock-scanner.md +78 -0
  22. package/agents/optimize-runner.md +112 -0
  23. package/agents/overfitting-reviewer.md +96 -0
  24. package/agents/performance-assessor.md +159 -0
  25. package/agents/plan-critic.md +85 -0
  26. package/agents/promotion-reviewer.md +121 -0
  27. package/agents/recurring-pattern-detector.md +248 -0
  28. package/agents/retrospective-synthesizer.md +109 -0
  29. package/agents/root-cause-investigator.md +285 -0
  30. package/agents/scope-auditor.md +121 -0
  31. package/agents/security-reviewer.md +151 -0
  32. package/agents/self-improvement-architect.md +125 -0
  33. package/agents/synthesis-critic.md +99 -0
  34. package/agents/transcript-pattern-miner.md +165 -0
  35. package/agents/ui-validator.md +193 -0
  36. package/codex-skills/build-loop/SKILL.md +20 -0
  37. package/codex-skills/debug-loop/SKILL.md +18 -0
  38. package/codex-skills/handoff/SKILL.md +22 -0
  39. package/codex-skills/knowledge/SKILL.md +21 -0
  40. package/codex-skills/optimize/SKILL.md +18 -0
  41. package/codex-skills/research/SKILL.md +18 -0
  42. package/commands/agent-rally-point.md +301 -0
  43. package/commands/assess.md +41 -0
  44. package/commands/debug.md +20 -0
  45. package/commands/debugger-detail.md +34 -0
  46. package/commands/debugger-scan.md +15 -0
  47. package/commands/debugger-status.md +20 -0
  48. package/commands/debugger.md +32 -0
  49. package/commands/handoff.md +113 -0
  50. package/commands/knowledge-review.md +16 -0
  51. package/commands/optimize.md +31 -0
  52. package/commands/plan-verify.md +39 -0
  53. package/commands/promote-experiment.md +129 -0
  54. package/commands/research.md +22 -0
  55. package/commands/run.md +12 -0
  56. package/commands/self-review.md +51 -0
  57. package/commands/start-prd.md +57 -0
  58. package/commands/test.md +34 -0
  59. package/dist/src/assessment-orchestrator.d.ts +78 -0
  60. package/dist/src/assessment-orchestrator.d.ts.map +1 -0
  61. package/dist/src/assessment-orchestrator.js +304 -0
  62. package/dist/src/assessment-orchestrator.js.map +1 -0
  63. package/dist/src/audit-miner.d.ts +21 -0
  64. package/dist/src/audit-miner.d.ts.map +1 -0
  65. package/dist/src/audit-miner.js +421 -0
  66. package/dist/src/audit-miner.js.map +1 -0
  67. package/dist/src/batch-operations.d.ts +30 -0
  68. package/dist/src/batch-operations.d.ts.map +1 -0
  69. package/dist/src/batch-operations.js +339 -0
  70. package/dist/src/batch-operations.js.map +1 -0
  71. package/dist/src/config.d.ts +83 -0
  72. package/dist/src/config.d.ts.map +1 -0
  73. package/dist/src/config.js +176 -0
  74. package/dist/src/config.js.map +1 -0
  75. package/dist/src/context-engine.d.ts +45 -0
  76. package/dist/src/context-engine.d.ts.map +1 -0
  77. package/dist/src/context-engine.js +135 -0
  78. package/dist/src/context-engine.js.map +1 -0
  79. package/dist/src/debug-wrapper.d.ts +58 -0
  80. package/dist/src/debug-wrapper.d.ts.map +1 -0
  81. package/dist/src/debug-wrapper.js +285 -0
  82. package/dist/src/debug-wrapper.js.map +1 -0
  83. package/dist/src/index.d.ts +31 -0
  84. package/dist/src/index.d.ts.map +1 -0
  85. package/dist/src/index.js +149 -0
  86. package/dist/src/index.js.map +1 -0
  87. package/dist/src/interactive-verifier.d.ts +26 -0
  88. package/dist/src/interactive-verifier.d.ts.map +1 -0
  89. package/dist/src/interactive-verifier.js +435 -0
  90. package/dist/src/interactive-verifier.js.map +1 -0
  91. package/dist/src/lessons.d.ts +59 -0
  92. package/dist/src/lessons.d.ts.map +1 -0
  93. package/dist/src/lessons.js +172 -0
  94. package/dist/src/lessons.js.map +1 -0
  95. package/dist/src/log-reader.d.ts +39 -0
  96. package/dist/src/log-reader.d.ts.map +1 -0
  97. package/dist/src/log-reader.js +291 -0
  98. package/dist/src/log-reader.js.map +1 -0
  99. package/dist/src/logger.d.ts +59 -0
  100. package/dist/src/logger.d.ts.map +1 -0
  101. package/dist/src/logger.js +328 -0
  102. package/dist/src/logger.js.map +1 -0
  103. package/dist/src/parallel-retrieval.d.ts +52 -0
  104. package/dist/src/parallel-retrieval.d.ts.map +1 -0
  105. package/dist/src/parallel-retrieval.js +309 -0
  106. package/dist/src/parallel-retrieval.js.map +1 -0
  107. package/dist/src/pattern-extractor.d.ts +30 -0
  108. package/dist/src/pattern-extractor.d.ts.map +1 -0
  109. package/dist/src/pattern-extractor.js +386 -0
  110. package/dist/src/pattern-extractor.js.map +1 -0
  111. package/dist/src/result-aggregator.d.ts +44 -0
  112. package/dist/src/result-aggregator.d.ts.map +1 -0
  113. package/dist/src/result-aggregator.js +315 -0
  114. package/dist/src/result-aggregator.js.map +1 -0
  115. package/dist/src/retrieval.d.ts +139 -0
  116. package/dist/src/retrieval.d.ts.map +1 -0
  117. package/dist/src/retrieval.js +724 -0
  118. package/dist/src/retrieval.js.map +1 -0
  119. package/dist/src/setup/auto-setup.d.ts +15 -0
  120. package/dist/src/setup/auto-setup.d.ts.map +1 -0
  121. package/dist/src/setup/auto-setup.js +157 -0
  122. package/dist/src/setup/auto-setup.js.map +1 -0
  123. package/dist/src/setup/configure-hooks.d.ts +8 -0
  124. package/dist/src/setup/configure-hooks.d.ts.map +1 -0
  125. package/dist/src/setup/configure-hooks.js +16 -0
  126. package/dist/src/setup/configure-hooks.js.map +1 -0
  127. package/dist/src/setup/create-slash-commands.d.ts +8 -0
  128. package/dist/src/setup/create-slash-commands.d.ts.map +1 -0
  129. package/dist/src/setup/create-slash-commands.js +101 -0
  130. package/dist/src/setup/create-slash-commands.js.map +1 -0
  131. package/dist/src/setup/index.d.ts +8 -0
  132. package/dist/src/setup/index.d.ts.map +1 -0
  133. package/dist/src/setup/index.js +17 -0
  134. package/dist/src/setup/index.js.map +1 -0
  135. package/dist/src/setup/inject-claude-md.d.ts +10 -0
  136. package/dist/src/setup/inject-claude-md.d.ts.map +1 -0
  137. package/dist/src/setup/inject-claude-md.js +150 -0
  138. package/dist/src/setup/inject-claude-md.js.map +1 -0
  139. package/dist/src/setup/uninstall.d.ts +22 -0
  140. package/dist/src/setup/uninstall.d.ts.map +1 -0
  141. package/dist/src/setup/uninstall.js +235 -0
  142. package/dist/src/setup/uninstall.js.map +1 -0
  143. package/dist/src/storage.d.ts +192 -0
  144. package/dist/src/storage.d.ts.map +1 -0
  145. package/dist/src/storage.js +1037 -0
  146. package/dist/src/storage.js.map +1 -0
  147. package/dist/src/string-similarity.d.ts +8 -0
  148. package/dist/src/string-similarity.d.ts.map +1 -0
  149. package/dist/src/string-similarity.js +57 -0
  150. package/dist/src/string-similarity.js.map +1 -0
  151. package/dist/src/traces/adapters/browser.d.ts +69 -0
  152. package/dist/src/traces/adapters/browser.d.ts.map +1 -0
  153. package/dist/src/traces/adapters/browser.js +280 -0
  154. package/dist/src/traces/adapters/browser.js.map +1 -0
  155. package/dist/src/traces/adapters/index.d.ts +23 -0
  156. package/dist/src/traces/adapters/index.d.ts.map +1 -0
  157. package/dist/src/traces/adapters/index.js +67 -0
  158. package/dist/src/traces/adapters/index.js.map +1 -0
  159. package/dist/src/traces/adapters/langchain.d.ts +42 -0
  160. package/dist/src/traces/adapters/langchain.d.ts.map +1 -0
  161. package/dist/src/traces/adapters/langchain.js +151 -0
  162. package/dist/src/traces/adapters/langchain.js.map +1 -0
  163. package/dist/src/traces/adapters/opentelemetry.d.ts +46 -0
  164. package/dist/src/traces/adapters/opentelemetry.d.ts.map +1 -0
  165. package/dist/src/traces/adapters/opentelemetry.js +164 -0
  166. package/dist/src/traces/adapters/opentelemetry.js.map +1 -0
  167. package/dist/src/traces/adapters/sentry.d.ts +58 -0
  168. package/dist/src/traces/adapters/sentry.d.ts.map +1 -0
  169. package/dist/src/traces/adapters/sentry.js +212 -0
  170. package/dist/src/traces/adapters/sentry.js.map +1 -0
  171. package/dist/src/traces/index.d.ts +10 -0
  172. package/dist/src/traces/index.d.ts.map +1 -0
  173. package/dist/src/traces/index.js +45 -0
  174. package/dist/src/traces/index.js.map +1 -0
  175. package/dist/src/traces/storage.d.ts +78 -0
  176. package/dist/src/traces/storage.d.ts.map +1 -0
  177. package/dist/src/traces/storage.js +399 -0
  178. package/dist/src/traces/storage.js.map +1 -0
  179. package/dist/src/traces/summarizer.d.ts +83 -0
  180. package/dist/src/traces/summarizer.d.ts.map +1 -0
  181. package/dist/src/traces/summarizer.js +247 -0
  182. package/dist/src/traces/summarizer.js.map +1 -0
  183. package/dist/src/traces/types.d.ts +365 -0
  184. package/dist/src/traces/types.d.ts.map +1 -0
  185. package/dist/src/traces/types.js +14 -0
  186. package/dist/src/traces/types.js.map +1 -0
  187. package/dist/src/types.d.ts +495 -0
  188. package/dist/src/types.d.ts.map +1 -0
  189. package/dist/src/types.js +27 -0
  190. package/dist/src/types.js.map +1 -0
  191. package/docs/agent-surface-policy.md +45 -0
  192. package/docs/memory-setup.md +226 -0
  193. package/hooks/_arch_scan_bg.py +88 -0
  194. package/hooks/_plugin_drift_check_bg.py +85 -0
  195. package/hooks/_session_start_lib.sh +42 -0
  196. package/hooks/git/pre-push +141 -0
  197. package/hooks/hooks.json +126 -0
  198. package/hooks/post-commit +30 -0
  199. package/hooks/pre-commit +29 -0
  200. package/hooks/pre-edit-apppulse.sh +6 -0
  201. package/hooks/pre-edit-architecture.sh +106 -0
  202. package/hooks/pre-edit-rally-point.sh +92 -0
  203. package/hooks/session-start-apppulse.sh +6 -0
  204. package/hooks/session-start-architecture.sh +10 -0
  205. package/hooks/session-start-git-hooks.sh +12 -0
  206. package/hooks/session-start-plugin-drift.sh +27 -0
  207. package/hooks/session-start-rally-point.sh +44 -0
  208. package/hooks/session-start-retrieval.sh +24 -0
  209. package/hooks/session-start-worktree-gc.sh +105 -0
  210. package/hooks/test_apppulse_hooks.sh +6 -0
  211. package/hooks/test_rally_point_hooks.sh +388 -0
  212. package/hooks/test_sec008_hooks.sh +66 -0
  213. package/package.json +111 -0
  214. package/scripts/README.md +301 -0
  215. package/scripts/_db_url.py +61 -0
  216. package/scripts/_paths.py +445 -0
  217. package/scripts/blm.py +162 -0
  218. package/scripts/blm_api.py +263 -0
  219. package/scripts/check_cache_sync.py +490 -0
  220. package/scripts/embed_backend.py +475 -0
  221. package/scripts/install_memory.py +556 -0
  222. package/scripts/lessons_index/__init__.py +77 -0
  223. package/scripts/lessons_index/__main__.py +135 -0
  224. package/scripts/lessons_index/ingest.py +288 -0
  225. package/scripts/lessons_index/query.py +285 -0
  226. package/scripts/lessons_index/schema.py +114 -0
  227. package/scripts/memory_context/__init__.py +610 -0
  228. package/scripts/memory_graph/__init__.py +393 -0
  229. package/scripts/project_resolver.py +172 -0
  230. package/scripts/prune_codex_plugin_cache.py +23 -0
  231. package/scripts/prune_plugin_cache.py +359 -0
  232. package/scripts/semantic_index/__init__.py +482 -0
  233. package/scripts/semantic_index/_bench_hybrid.py +175 -0
  234. package/scripts/semantic_index/backfill.py +263 -0
  235. package/scripts/semantic_index/hybrid.py +174 -0
  236. package/scripts/semantic_index/test_backfill.py +292 -0
  237. package/scripts/semantic_index/test_hybrid.py +541 -0
  238. package/scripts/sync_navgator_lessons.py +666 -0
  239. package/scripts/sync_plugin_cache.py +388 -0
  240. package/skills/agent-rally-point/SKILL.md +165 -0
  241. package/skills/agent-rally-watcher/SKILL.md +83 -0
  242. package/skills/api-registry-bridge/SKILL.md +59 -0
  243. package/skills/architecture/dead/SKILL.md +56 -0
  244. package/skills/architecture/impact/SKILL.md +88 -0
  245. package/skills/architecture/review/SKILL.md +227 -0
  246. package/skills/architecture/rules/SKILL.md +72 -0
  247. package/skills/architecture/scan/SKILL.md +79 -0
  248. package/skills/architecture/trace/SKILL.md +65 -0
  249. package/skills/attribution-standard/SKILL.md +122 -0
  250. package/skills/authentication/SKILL.md +81 -0
  251. package/skills/authentication/references/better-auth-magic-link.md +135 -0
  252. package/skills/authentication/references/better-auth-setup.md +224 -0
  253. package/skills/authentication/references/google-calendar-sync.md +278 -0
  254. package/skills/authentication/references/google-cloud-console.md +159 -0
  255. package/skills/authentication/references/google-geocoding-directions.md +126 -0
  256. package/skills/authentication/references/google-lessons-example-web-app.md +177 -0
  257. package/skills/authentication/references/google-maps.md +162 -0
  258. package/skills/authentication/references/google-oauth-setup.md +343 -0
  259. package/skills/authentication/references/google-places.md +173 -0
  260. package/skills/authentication/references/lessons-example-web-app-better-auth.md +126 -0
  261. package/skills/authentication/references/resend-email.md +227 -0
  262. package/skills/authentication/references/resend-otp-magic-link.md +153 -0
  263. package/skills/authentication/references/supabase-auth.md +172 -0
  264. package/skills/auto-decision-capture/SKILL.md +419 -0
  265. package/skills/build-loop/SKILL.md +516 -0
  266. package/skills/build-loop/detect-plugins.mjs +82 -0
  267. package/skills/build-loop/eval-guide.md +65 -0
  268. package/skills/build-loop/fallbacks.md +549 -0
  269. package/skills/build-loop/phases/fact-check.md +42 -0
  270. package/skills/build-loop/phases/ui-validation.md +267 -0
  271. package/skills/build-loop/references/apple-native-planning.md +439 -0
  272. package/skills/build-loop/references/capability-routing.md +181 -0
  273. package/skills/build-loop/references/codex-subagents.md +98 -0
  274. package/skills/build-loop/references/coordination.md +161 -0
  275. package/skills/build-loop/references/correction-aware-capture.md +177 -0
  276. package/skills/build-loop/references/independent-auditor.md +72 -0
  277. package/skills/build-loop/references/intent-capability-pack.md +170 -0
  278. package/skills/build-loop/references/intent-exploration-prompts.md +96 -0
  279. package/skills/build-loop/references/leadership.md +72 -0
  280. package/skills/build-loop/references/memory.md +284 -0
  281. package/skills/build-loop/references/modular-systems-pack.md +96 -0
  282. package/skills/build-loop/references/output-style.md +129 -0
  283. package/skills/build-loop/references/pay-it-forward-arch.md +98 -0
  284. package/skills/build-loop/references/phase-1-assess.md +218 -0
  285. package/skills/build-loop/references/phase-2-plan.md +77 -0
  286. package/skills/build-loop/references/phase-3-execute.md +47 -0
  287. package/skills/build-loop/references/phase-4-review.md +310 -0
  288. package/skills/build-loop/references/phase-5-iterate.md +71 -0
  289. package/skills/build-loop/references/phase-6-learn.md +56 -0
  290. package/skills/build-loop/references/recent-design-structures.md +274 -0
  291. package/skills/build-loop/references/refactor-history/ASSESSMENT.md +85 -0
  292. package/skills/build-loop/references/refactor-history/STANDALONE_TEST_RUN.md +149 -0
  293. package/skills/build-loop/references/refactor-history/scenarios/01-simple-bugfix.md +32 -0
  294. package/skills/build-loop/references/refactor-history/scenarios/02-ui-build-with-iteration.md +48 -0
  295. package/skills/build-loop/references/refactor-history/scenarios/03-multi-failure-escalation.md +60 -0
  296. package/skills/build-loop/references/refactor-history/scenarios/04-ui-build-ibr-absent.md +51 -0
  297. package/skills/build-loop/references/refactor-history/scenarios/05-refactor-navgator-absent.md +71 -0
  298. package/skills/build-loop/references/refactor-history/scenarios/06-recurring-bug-debugger-absent.md +52 -0
  299. package/skills/build-loop/references/refactor-history/trace-comparison.md +202 -0
  300. package/skills/build-loop/references/self-recursive-dev.md +77 -0
  301. package/skills/build-loop/references/self-review.md +232 -0
  302. package/skills/build-loop/references/ui-io-contract.md +116 -0
  303. package/skills/build-loop/scanners/audit-design-rules.mjs +476 -0
  304. package/skills/build-loop/scanners/require-visual-evidence.mjs +239 -0
  305. package/skills/build-loop/templates/backlog-item.md +35 -0
  306. package/skills/build-loop/templates/codex-worker-prompt.md +89 -0
  307. package/skills/build-loop/templates/ui-subagent-prompt.md +179 -0
  308. package/skills/build-loop/templates/ux-fix-plan.md +40 -0
  309. package/skills/building-with-deepagents/SKILL.md +195 -0
  310. package/skills/building-with-deepagents/references/anti-patterns.md +141 -0
  311. package/skills/building-with-deepagents/references/api-quick-reference.md +184 -0
  312. package/skills/capabilities/SKILL.md +91 -0
  313. package/skills/debug-loop/SKILL.md +278 -0
  314. package/skills/debug-loop/references/convergence-rules.md +145 -0
  315. package/skills/debug-loop/references/swiftui-macos-clunky-ui-debugger.html +823 -0
  316. package/skills/debugging/assess/SKILL.md +111 -0
  317. package/skills/debugging/debug-loop/SKILL.md +231 -0
  318. package/skills/debugging/memory/SKILL.md +147 -0
  319. package/skills/debugging/store/SKILL.md +153 -0
  320. package/skills/debugging-memory/SKILL.md +440 -0
  321. package/skills/debugging-memory/examples/incident-example.json +67 -0
  322. package/skills/debugging-memory/references/incident-documentation.md +280 -0
  323. package/skills/debugging-memory/references/ios-notification-alarm-playbook.md +300 -0
  324. package/skills/debugging-memory/references/pattern-extraction.md +163 -0
  325. package/skills/debugging-memory/references/subagent-integration.md +312 -0
  326. package/skills/defenseclaw-bridge/SKILL.md +156 -0
  327. package/skills/defenseclaw-bridge/references/dc-config-mapping.md +162 -0
  328. package/skills/defenseclaw-bridge/references/output-format.md +108 -0
  329. package/skills/handoff/SKILL.md +94 -0
  330. package/skills/ibr-bridge/SKILL.md +68 -0
  331. package/skills/knowledge/SKILL.md +127 -0
  332. package/skills/knowledge/references/recall-integration.md +83 -0
  333. package/skills/knowledge/templates/madr-minimal.md +56 -0
  334. package/skills/knowledge-review/SKILL.md +112 -0
  335. package/skills/logging-tracer/SKILL.md +202 -0
  336. package/skills/logging-tracer/references/log-analysis.md +128 -0
  337. package/skills/logging-tracer/references/stack-templates.md +274 -0
  338. package/skills/logging-tracer-bridge/SKILL.md +70 -0
  339. package/skills/mcp-builder/SKILL.md +278 -0
  340. package/skills/mcp-builder/references/mcp-security.md +106 -0
  341. package/skills/model-tiering/SKILL.md +216 -0
  342. package/skills/native-ax-driver/SKILL.md +220 -0
  343. package/skills/native-ax-driver/scripts/layout_fill.py +222 -0
  344. package/skills/native-ax-driver/scripts/native_driver.py +495 -0
  345. package/skills/native-ax-driver/swift/bl-ax-driver/Package.swift +20 -0
  346. package/skills/native-ax-driver/swift/bl-ax-driver/Sources/main.swift +542 -0
  347. package/skills/optimize/SKILL.md +202 -0
  348. package/skills/optimize/profiles.md +61 -0
  349. package/skills/plan-verify/SKILL.md +115 -0
  350. package/skills/plan-verify/test-fixtures/example-app-v20-findings.json +27 -0
  351. package/skills/plan-verify/test-fixtures/example-app-v20.md +42 -0
  352. package/skills/plan-verify/test-fixtures/example-app-v22-findings.json +15 -0
  353. package/skills/plan-verify/test-fixtures/example-app-v22.md +1608 -0
  354. package/skills/plan-verify/test-fixtures/unrelated-good-plan-findings.json +15 -0
  355. package/skills/plan-verify/test-fixtures/unrelated-good-plan.md +55 -0
  356. package/skills/plugin-builder/SKILL.md +329 -0
  357. package/skills/plugin-builder/references/authoritative-sources.md +104 -0
  358. package/skills/plugin-builder/references/build-loop-phase-guidance.md +94 -0
  359. package/skills/plugin-builder/references/components-guide.md +221 -0
  360. package/skills/plugin-builder/references/distribution.md +317 -0
  361. package/skills/plugin-builder/references/dual-host-claude-codex.md +220 -0
  362. package/skills/plugin-builder/references/hooks-reference.md +210 -0
  363. package/skills/plugin-builder/references/manifest-schema.md +111 -0
  364. package/skills/plugin-builder/references/plugin-hygiene-lessons.md +307 -0
  365. package/skills/plugin-tests/SKILL.md +94 -0
  366. package/skills/prd-bridge/SKILL.md +135 -0
  367. package/skills/research/SKILL.md +80 -0
  368. package/skills/security-methodology/SKILL.md +105 -0
  369. package/skills/security-methodology/references/cross-source-matrix.md +77 -0
  370. package/skills/security-methodology/references/mitre-atlas-starter.md +83 -0
  371. package/skills/security-methodology/references/nist-600-1-mapping.md +119 -0
  372. package/skills/security-methodology/references/owasp-agentic-top-10.md +157 -0
  373. package/skills/security-methodology/references/owasp-llm-top-10.md +144 -0
  374. package/skills/security-methodology/references/owasp-web-top-10.md +65 -0
  375. package/skills/self-improve/SKILL.md +298 -0
  376. package/skills/spec-writing/SKILL.md +548 -0
  377. package/skills/spec-writing/scripts/check_checklist.py +478 -0
  378. package/skills/sync-skills/SKILL.md +132 -0
  379. package/skills/telemetry/SKILL.md +66 -0
  380. package/skills/ui-design/SKILL.md +75 -0
  381. package/skills/ui-design/references/design-patterns-multi.md +165 -0
  382. package/skills/ui-design/references/design-preferences-from-owned-apps.md +150 -0
  383. package/skills/ui-design/references/evidence-capture-policy.md +55 -0
  384. package/skills/ui-design/references/ui-guidance-sources.md +330 -0
  385. package/skills/ui-design/references/universal-design-principles.alt.md +182 -0
  386. package/skills/ui-design/references/universal-design-principles.md +419 -0
  387. package/templates/memory/MEMORY.md.template +55 -0
  388. package/templates/memory/constitution.md.template +64 -0
  389. package/templates/memory/manifest.json +47 -0
@@ -0,0 +1,393 @@
1
+ #!/usr/bin/env python3
2
+ # SPDX-FileCopyrightText: 2025-2026 Tyrone Ross, Jr <46267523+tyroneross@users.noreply.github.com>
3
+ # SPDX-License-Identifier: Apache-2.0
4
+ """Portable graph adapter for build-loop-memory.
5
+
6
+ The canonical graph is the rebuildable JSONL pair in
7
+ ``<memory-root>/indexes/graph-nodes.jsonl`` and ``graph-edges.jsonl``.
8
+ ``sqlite_edges`` is the default runtime backend: it mirrors that JSONL into a
9
+ small SQLite index and answers bounded related-node traversals. ``ladybug`` is
10
+ opt-in only; when unavailable or smoke-gate incompatible, callers fall back to
11
+ ``sqlite_edges`` with a reason in the response.
12
+ """
13
+ from __future__ import annotations
14
+
15
+ import json
16
+ import os
17
+ import sqlite3
18
+ import sys
19
+ from collections import deque
20
+ from pathlib import Path
21
+ from typing import Any, Iterable
22
+
23
+ HERE = Path(__file__).resolve().parent
24
+ SCRIPTS_DIR = HERE.parent
25
+ if str(SCRIPTS_DIR) not in sys.path:
26
+ sys.path.insert(0, str(SCRIPTS_DIR))
27
+
28
+ from _paths import memory_indexes_dir, memory_store_root # type: ignore # noqa: E402
29
+
30
+ SCHEMA_VERSION = "1.0.0"
31
+ DEFAULT_BACKEND = "sqlite_edges"
32
+ VALID_DIRECTIONS = {"out", "in", "both"}
33
+
34
+
35
+ def _read_jsonl(path: Path) -> list[dict[str, Any]]:
36
+ rows: list[dict[str, Any]] = []
37
+ if not path.is_file():
38
+ return rows
39
+ for raw in path.read_text(encoding="utf-8").splitlines():
40
+ raw = raw.strip()
41
+ if not raw:
42
+ continue
43
+ try:
44
+ data = json.loads(raw)
45
+ except json.JSONDecodeError:
46
+ continue
47
+ if isinstance(data, dict):
48
+ rows.append(data)
49
+ return rows
50
+
51
+
52
+ def _source_signature(nodes_path: Path, edges_path: Path) -> str:
53
+ parts: list[dict[str, Any]] = []
54
+ for path in (nodes_path, edges_path):
55
+ try:
56
+ stat = path.stat()
57
+ except OSError:
58
+ parts.append({"path": str(path), "exists": False})
59
+ continue
60
+ parts.append(
61
+ {
62
+ "path": str(path),
63
+ "exists": True,
64
+ "mtime_ns": stat.st_mtime_ns,
65
+ "size": stat.st_size,
66
+ }
67
+ )
68
+ return json.dumps(parts, sort_keys=True)
69
+
70
+
71
+ def _text(value: Any, default: str = "") -> str:
72
+ if value is None:
73
+ return default
74
+ return str(value)
75
+
76
+
77
+ def _node_from_row(row: dict[str, Any]) -> dict[str, Any] | None:
78
+ node_id = _text(row.get("id") or row.get("canonical_id") or row.get("path")).strip()
79
+ if not node_id:
80
+ return None
81
+ return {
82
+ "id": node_id,
83
+ "title": _text(row.get("title") or row.get("name") or node_id, node_id),
84
+ "path": _text(row.get("path") or row.get("canonical_path")),
85
+ "project": _text(row.get("project")),
86
+ "memory_type": _text(row.get("memory_type") or row.get("type")),
87
+ "status": _text(row.get("status")),
88
+ "data": row,
89
+ }
90
+
91
+
92
+ def _edge_from_row(row: dict[str, Any]) -> dict[str, Any] | None:
93
+ source = _text(row.get("source")).strip()
94
+ target = _text(row.get("target")).strip()
95
+ if not source or not target:
96
+ return None
97
+ edge_id = _text(row.get("id") or f"{source}->{target}:{row.get('relation', '')}")
98
+ return {
99
+ "id": edge_id,
100
+ "source": source,
101
+ "target": target,
102
+ "relation": _text(row.get("relation") or row.get("type") or "related"),
103
+ "data": row,
104
+ }
105
+
106
+
107
+ class SQLiteEdgesStore:
108
+ """SQLite-backed adapter over rebuildable graph JSONL files."""
109
+
110
+ backend = DEFAULT_BACKEND
111
+
112
+ def __init__(
113
+ self,
114
+ *,
115
+ root: Path,
116
+ db_path: Path | str | None = None,
117
+ reasons: Iterable[str] = (),
118
+ ) -> None:
119
+ self.root = root
120
+ self.index_dir = root / "indexes"
121
+ self.nodes_path = self.index_dir / "graph-nodes.jsonl"
122
+ self.edges_path = self.index_dir / "graph-edges.jsonl"
123
+ self.db_path = db_path or self.index_dir / "graph.sqlite"
124
+ self.reasons = list(reasons)
125
+ self._conn = sqlite3.connect(str(self.db_path))
126
+ self._conn.row_factory = sqlite3.Row
127
+ self._sync_if_needed()
128
+
129
+ def close(self) -> None:
130
+ self._conn.close()
131
+
132
+ def _ensure_schema(self) -> None:
133
+ self._conn.executescript(
134
+ """
135
+ CREATE TABLE IF NOT EXISTS meta (
136
+ key TEXT PRIMARY KEY,
137
+ value TEXT NOT NULL
138
+ );
139
+ CREATE TABLE IF NOT EXISTS nodes (
140
+ id TEXT PRIMARY KEY,
141
+ title TEXT NOT NULL DEFAULT '',
142
+ path TEXT NOT NULL DEFAULT '',
143
+ project TEXT NOT NULL DEFAULT '',
144
+ memory_type TEXT NOT NULL DEFAULT '',
145
+ status TEXT NOT NULL DEFAULT '',
146
+ data TEXT NOT NULL DEFAULT '{}'
147
+ );
148
+ CREATE TABLE IF NOT EXISTS edges (
149
+ id TEXT PRIMARY KEY,
150
+ source TEXT NOT NULL,
151
+ target TEXT NOT NULL,
152
+ relation TEXT NOT NULL DEFAULT 'related',
153
+ data TEXT NOT NULL DEFAULT '{}'
154
+ );
155
+ CREATE INDEX IF NOT EXISTS idx_edges_source ON edges(source);
156
+ CREATE INDEX IF NOT EXISTS idx_edges_target ON edges(target);
157
+ CREATE INDEX IF NOT EXISTS idx_edges_relation ON edges(relation);
158
+ """
159
+ )
160
+
161
+ def _sync_if_needed(self) -> None:
162
+ self.index_dir.mkdir(parents=True, exist_ok=True)
163
+ self._ensure_schema()
164
+ signature = _source_signature(self.nodes_path, self.edges_path)
165
+ current = self._conn.execute(
166
+ "SELECT value FROM meta WHERE key = 'source_signature'"
167
+ ).fetchone()
168
+ if current and current["value"] == signature:
169
+ return
170
+
171
+ node_rows = [_node_from_row(row) for row in _read_jsonl(self.nodes_path)]
172
+ edge_rows = [_edge_from_row(row) for row in _read_jsonl(self.edges_path)]
173
+ nodes = [row for row in node_rows if row is not None]
174
+ edges = [row for row in edge_rows if row is not None]
175
+
176
+ with self._conn:
177
+ self._conn.execute("DELETE FROM edges")
178
+ self._conn.execute("DELETE FROM nodes")
179
+ self._conn.executemany(
180
+ """
181
+ INSERT OR REPLACE INTO nodes
182
+ (id, title, path, project, memory_type, status, data)
183
+ VALUES (:id, :title, :path, :project, :memory_type, :status, :data)
184
+ """,
185
+ [
186
+ {
187
+ **row,
188
+ "data": json.dumps(row["data"], sort_keys=True, default=str),
189
+ }
190
+ for row in nodes
191
+ ],
192
+ )
193
+ self._conn.executemany(
194
+ """
195
+ INSERT OR REPLACE INTO edges (id, source, target, relation, data)
196
+ VALUES (:id, :source, :target, :relation, :data)
197
+ """,
198
+ [
199
+ {
200
+ **row,
201
+ "data": json.dumps(row["data"], sort_keys=True, default=str),
202
+ }
203
+ for row in edges
204
+ ],
205
+ )
206
+ for edge in edges:
207
+ for endpoint in (edge["source"], edge["target"]):
208
+ self._conn.execute(
209
+ """
210
+ INSERT OR IGNORE INTO nodes
211
+ (id, title, path, project, memory_type, status, data)
212
+ VALUES (?, ?, '', '', '', '', '{}')
213
+ """,
214
+ (endpoint, endpoint),
215
+ )
216
+ self._conn.execute(
217
+ "INSERT OR REPLACE INTO meta(key, value) VALUES('source_signature', ?)",
218
+ (signature,),
219
+ )
220
+ self._conn.execute(
221
+ "INSERT OR REPLACE INTO meta(key, value) VALUES('schema_version', ?)",
222
+ (SCHEMA_VERSION,),
223
+ )
224
+
225
+ def stats(self) -> dict[str, int]:
226
+ nodes = self._conn.execute("SELECT COUNT(*) AS n FROM nodes").fetchone()["n"]
227
+ edges = self._conn.execute("SELECT COUNT(*) AS n FROM edges").fetchone()["n"]
228
+ return {"nodes": int(nodes), "edges": int(edges)}
229
+
230
+ def _edges_for(
231
+ self,
232
+ node_id: str,
233
+ *,
234
+ direction: str,
235
+ relation: str | None,
236
+ ) -> list[sqlite3.Row]:
237
+ if direction not in VALID_DIRECTIONS:
238
+ raise ValueError(f"invalid direction {direction!r}; expected one of {sorted(VALID_DIRECTIONS)}")
239
+ rel_sql = " AND relation = ?" if relation else ""
240
+ rel_args: tuple[str, ...] = (relation,) if relation else ()
241
+ rows: list[sqlite3.Row] = []
242
+ if direction in {"out", "both"}:
243
+ rows.extend(
244
+ self._conn.execute(
245
+ f"SELECT * FROM edges WHERE source = ?{rel_sql}",
246
+ (node_id, *rel_args),
247
+ ).fetchall()
248
+ )
249
+ if direction in {"in", "both"}:
250
+ rows.extend(
251
+ self._conn.execute(
252
+ f"SELECT * FROM edges WHERE target = ?{rel_sql}",
253
+ (node_id, *rel_args),
254
+ ).fetchall()
255
+ )
256
+ return rows
257
+
258
+ def _node(self, node_id: str) -> dict[str, Any] | None:
259
+ row = self._conn.execute("SELECT * FROM nodes WHERE id = ?", (node_id,)).fetchone()
260
+ if row is None:
261
+ return None
262
+ try:
263
+ data = json.loads(row["data"])
264
+ except json.JSONDecodeError:
265
+ data = {}
266
+ return {
267
+ "id": row["id"],
268
+ "title": row["title"],
269
+ "path": row["path"],
270
+ "project": row["project"],
271
+ "memory_type": row["memory_type"],
272
+ "status": row["status"],
273
+ "data": data,
274
+ }
275
+
276
+ def related(
277
+ self,
278
+ seeds: str | Iterable[str],
279
+ *,
280
+ depth: int = 1,
281
+ limit: int = 10,
282
+ direction: str = "both",
283
+ relation: str | None = None,
284
+ project: str | None = None,
285
+ ) -> dict[str, Any]:
286
+ """Return nodes and edges reachable from ``seeds`` within ``depth``.
287
+
288
+ The response shape follows the graph-query convention
289
+ ``MATCH (seed)-[r*1..depth]-(related) RETURN related, r`` without
290
+ binding callers to a non-portable graph database.
291
+ """
292
+ self._sync_if_needed()
293
+ seed_list = [seeds] if isinstance(seeds, str) else [str(seed) for seed in seeds]
294
+ seed_list = [seed for seed in seed_list if seed]
295
+ max_depth = max(0, int(depth))
296
+ max_limit = max(0, int(limit))
297
+
298
+ queue: deque[tuple[str, int]] = deque((seed, 0) for seed in seed_list)
299
+ seen = set(seed_list)
300
+ node_hits: dict[str, dict[str, Any]] = {}
301
+ edge_hits: dict[str, dict[str, Any]] = {}
302
+
303
+ while queue:
304
+ node_id, hop = queue.popleft()
305
+ if hop >= max_depth:
306
+ continue
307
+ for edge in self._edges_for(node_id, direction=direction, relation=relation):
308
+ source = edge["source"]
309
+ target = edge["target"]
310
+ neighbor = target if source == node_id else source
311
+ next_hop = hop + 1
312
+ edge_hits.setdefault(
313
+ edge["id"],
314
+ {
315
+ "id": edge["id"],
316
+ "source": source,
317
+ "target": target,
318
+ "relation": edge["relation"],
319
+ "hop": next_hop,
320
+ },
321
+ )
322
+ if neighbor not in seen:
323
+ seen.add(neighbor)
324
+ queue.append((neighbor, next_hop))
325
+ node = self._node(neighbor)
326
+ if node is None:
327
+ continue
328
+ if project and node.get("project") and node["project"] != project:
329
+ continue
330
+ prior = node_hits.get(neighbor)
331
+ score = 1.0 / (next_hop + 1)
332
+ if prior is None or next_hop < prior["hop"]:
333
+ node_hits[neighbor] = {**node, "hop": next_hop, "score": score}
334
+ elif prior is not None:
335
+ prior["score"] += score
336
+
337
+ nodes = [
338
+ row
339
+ for row in node_hits.values()
340
+ if row["id"] not in seed_list
341
+ ]
342
+ nodes.sort(key=lambda row: (-float(row.get("score", 0.0)), int(row.get("hop", 0)), row.get("title", "")))
343
+ if max_limit:
344
+ nodes = nodes[:max_limit]
345
+ return {
346
+ "schema_version": SCHEMA_VERSION,
347
+ "backend": self.backend,
348
+ "query_shape": f"MATCH (seed)-[r*1..{max_depth}]-(related) RETURN related, r",
349
+ "seeds": seed_list,
350
+ "depth": max_depth,
351
+ "direction": direction,
352
+ "relation": relation,
353
+ "nodes": nodes,
354
+ "edges": list(edge_hits.values()),
355
+ "stats": self.stats(),
356
+ "reasons": list(self.reasons),
357
+ }
358
+
359
+
360
+ class GraphStore:
361
+ """Backend-selecting graph adapter.
362
+
363
+ ``sqlite_edges`` is the default. ``ladybug`` is accepted only as an opt-in
364
+ request and falls back automatically unless its import + smoke gate succeed.
365
+ """
366
+
367
+ @classmethod
368
+ def open(
369
+ cls,
370
+ *,
371
+ root: Path | str | None = None,
372
+ backend: str | None = None,
373
+ db_path: Path | str | None = None,
374
+ ) -> SQLiteEdgesStore:
375
+ memory_root = Path(root).resolve() if root else memory_store_root().resolve()
376
+ requested = backend or os.environ.get("BUILD_LOOP_MEMORY_GRAPH_BACKEND") or DEFAULT_BACKEND
377
+ reasons: list[str] = []
378
+ if requested in {"ladybug", "auto"}:
379
+ reasons.extend(_ladybug_smoke_reasons())
380
+ elif requested != DEFAULT_BACKEND:
381
+ reasons.append(f"unsupported_backend:{requested}; using {DEFAULT_BACKEND}")
382
+ return SQLiteEdgesStore(root=memory_root, db_path=db_path, reasons=reasons)
383
+
384
+
385
+ def _ladybug_smoke_reasons() -> list[str]:
386
+ try:
387
+ __import__("ladybug")
388
+ except Exception as exc: # noqa: BLE001
389
+ return [f"ladybug_unavailable:{type(exc).__name__}"]
390
+ return ["ladybug_smoke_unimplemented; using sqlite_edges"]
391
+
392
+
393
+ __all__ = ["GraphStore", "SQLiteEdgesStore"]
@@ -0,0 +1,172 @@
1
+ #!/usr/bin/env python3
2
+ # SPDX-FileCopyrightText: 2025-2026 Tyrone Ross, Jr <46267523+tyroneross@users.noreply.github.com>
3
+ # SPDX-License-Identifier: Apache-2.0
4
+ """Resolve a current working directory to a project tag.
5
+
6
+ Reads ``<memory_store_root>/config/projects.yaml`` when present, falling
7
+ back to ``<memory_store_root>/.config/projects.yaml`` for migration, and returns the
8
+ project tag whose ``path:`` is the longest prefix match against the
9
+ given ``cwd``. Falls back to the YAML's ``default:`` key, which itself
10
+ defaults to ``_unscoped`` if absent.
11
+
12
+ Pure stdlib parser — projects.yaml is a small file we control, so we
13
+ parse just the subset we emit (top-level ``default:`` scalar plus a
14
+ ``projects:`` list of ``- path: ...\\n project: ...`` blocks).
15
+
16
+ Public API:
17
+ resolve_project(cwd: Path) -> str
18
+ load_projects_yaml(path: Path | None = None) -> dict
19
+ """
20
+ from __future__ import annotations
21
+
22
+ import os
23
+ from pathlib import Path
24
+ from typing import Any
25
+
26
+ HERE = Path(__file__).resolve().parent
27
+ import sys
28
+ if str(HERE) not in sys.path:
29
+ sys.path.insert(0, str(HERE))
30
+ from _paths import derive_slug_from_cwd, memory_store_root # type: ignore # noqa: E402
31
+
32
+ DEFAULT_PROJECT_TAG = "_unscoped"
33
+
34
+
35
+ def _projects_yaml_path() -> Path:
36
+ canonical = memory_store_root() / "config" / "projects.yaml"
37
+ if canonical.exists():
38
+ return canonical
39
+ return memory_store_root() / ".config" / "projects.yaml"
40
+
41
+
42
+ def load_projects_yaml(path: Path | None = None) -> dict[str, Any]:
43
+ """Parse the small subset of YAML we emit.
44
+
45
+ Returns ``{"default": <tag>, "projects": [{"path": <abs>, "project": <tag>}, ...]}``.
46
+ Missing file → ``{"default": "_unscoped", "projects": []}``.
47
+ """
48
+ if path is None:
49
+ path = _projects_yaml_path()
50
+ if not path.exists():
51
+ return {"default": DEFAULT_PROJECT_TAG, "projects": []}
52
+ text = path.read_text(encoding="utf-8")
53
+ return _parse_projects_yaml(text)
54
+
55
+
56
+ def _parse_projects_yaml(text: str) -> dict[str, Any]:
57
+ """Parse the subset of YAML used in projects.yaml.
58
+
59
+ Recognized shapes:
60
+ default: <scalar>
61
+ projects:
62
+ - path: <scalar>
63
+ project: <scalar>
64
+ Lines starting with ``#`` and blank lines are ignored.
65
+ """
66
+ default_tag = DEFAULT_PROJECT_TAG
67
+ projects: list[dict[str, str]] = []
68
+ in_projects = False
69
+ cur: dict[str, str] | None = None
70
+ for raw_line in text.splitlines():
71
+ line = raw_line.rstrip()
72
+ # Strip inline comments (very simple: a `#` not inside quotes).
73
+ # projects.yaml never quotes values, so this is safe.
74
+ if "#" in line:
75
+ stripped = line.lstrip()
76
+ if stripped.startswith("#"):
77
+ continue
78
+ line = line.split("#", 1)[0].rstrip()
79
+ if not line:
80
+ continue
81
+ # Top-level key
82
+ if not line[0].isspace():
83
+ if line.startswith("default:"):
84
+ val = line.split(":", 1)[1].strip()
85
+ if val:
86
+ default_tag = val
87
+ in_projects = False
88
+ cur = None
89
+ elif line.startswith("projects:"):
90
+ in_projects = True
91
+ cur = None
92
+ else:
93
+ in_projects = False
94
+ cur = None
95
+ continue
96
+ # Indented line under projects:
97
+ if not in_projects:
98
+ continue
99
+ stripped = line.strip()
100
+ if stripped.startswith("- "):
101
+ # Start of a new project entry. The "- " line might itself
102
+ # carry the first key (e.g. "- path: ~/dev/foo").
103
+ if cur is not None:
104
+ projects.append(cur)
105
+ cur = {}
106
+ after = stripped[2:].strip()
107
+ if ":" in after:
108
+ k, _, v = after.partition(":")
109
+ cur[k.strip()] = v.strip()
110
+ elif ":" in stripped and cur is not None:
111
+ k, _, v = stripped.partition(":")
112
+ cur[k.strip()] = v.strip()
113
+ if cur is not None:
114
+ projects.append(cur)
115
+ # Filter out incomplete entries.
116
+ projects = [p for p in projects if "path" in p and "project" in p]
117
+ return {"default": default_tag or DEFAULT_PROJECT_TAG, "projects": projects}
118
+
119
+
120
+ def _normalize(p: str | Path) -> str:
121
+ """Expand ``~`` and resolve symlinks to an absolute path string."""
122
+ try:
123
+ return os.path.normpath(str(Path(os.path.expanduser(str(p))).resolve()))
124
+ except (OSError, RuntimeError):
125
+ return os.path.normpath(os.path.expanduser(str(p)))
126
+
127
+
128
+ def resolve_project(cwd: Path | str) -> str:
129
+ """Return the project tag for ``cwd``.
130
+
131
+ Resolution order (post memory-consolidation PR 1):
132
+ 1. ``derive_slug_from_cwd(cwd)`` — filesystem-driven slug from git
133
+ toplevel + ``_safe_project_tag`` normalization. The PRIMARY path.
134
+ Returns ``_unscoped`` when ``cwd`` is outside any git repo.
135
+ 2. ``projects.yaml`` lookup (exact then longest-prefix) — used ONLY
136
+ when (a) the filesystem rule returned ``_unscoped`` AND (b) the
137
+ operator has registered an explicit alias for this cwd. This
138
+ covers the rare case where the working directory isn't a git
139
+ repo but should still map to a known project tag.
140
+ 3. ``projects.yaml`` ``default:`` key, else ``_unscoped``.
141
+
142
+ Aligns by construction with Postgres ``semantic_facts.project`` —
143
+ ``derive_slug_from_cwd`` routes through ``_safe_project_tag``, which
144
+ is the same validator used by ``decisions_dir_for_project``.
145
+ """
146
+ # Step 1 — filesystem-driven derivation (the new primary path).
147
+ slug = derive_slug_from_cwd(cwd)
148
+ if slug != DEFAULT_PROJECT_TAG:
149
+ return slug
150
+
151
+ # Step 2 — fall back to projects.yaml only when filesystem returned
152
+ # _unscoped. This preserves backward-compat for explicit aliases.
153
+ data = load_projects_yaml()
154
+ cwd_norm = _normalize(cwd)
155
+ best_match: tuple[int, str] | None = None
156
+ for entry in data["projects"]:
157
+ path_norm = _normalize(entry["path"])
158
+ if path_norm == cwd_norm:
159
+ return entry["project"]
160
+ if cwd_norm.startswith(path_norm + os.sep):
161
+ length = len(path_norm)
162
+ if best_match is None or length > best_match[0]:
163
+ best_match = (length, entry["project"])
164
+ if best_match is not None:
165
+ return best_match[1]
166
+ return data["default"]
167
+
168
+
169
+ if __name__ == "__main__": # pragma: no cover - manual smoke tool
170
+ import sys
171
+ target = sys.argv[1] if len(sys.argv) > 1 else os.getcwd()
172
+ print(resolve_project(target))
@@ -0,0 +1,23 @@
1
+ #!/usr/bin/env python3
2
+ # SPDX-FileCopyrightText: 2025-2026 Tyrone Ross, Jr <46267523+tyroneross@users.noreply.github.com>
3
+ # SPDX-License-Identifier: Apache-2.0
4
+ """Compatibility wrapper for pruning Codex plugin cache versions."""
5
+ from __future__ import annotations
6
+
7
+ import sys
8
+ from pathlib import Path
9
+
10
+ sys.path.insert(0, str(Path(__file__).resolve().parent))
11
+ from prune_plugin_cache import main as prune_main # noqa: E402
12
+
13
+
14
+ def main() -> int:
15
+ args = sys.argv[1:]
16
+ if "--host" not in args:
17
+ args = ["--host", "codex", *args]
18
+ sys.argv = [str(Path(__file__).with_name("prune_plugin_cache.py")), *args]
19
+ return prune_main()
20
+
21
+
22
+ if __name__ == "__main__":
23
+ sys.exit(main())