@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,541 @@
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
+ """Tests for the hybrid keyword+embedding rerank path in semantic_index.
5
+
6
+ Covers the three P1 acceptance criteria:
7
+ 1. Synonym/paraphrase: a query that keyword ranking PUTS WRONG-RANK
8
+ (or misses) gets the right top hit via dense rerank.
9
+ 2. Graceful fallback: with no embed backend (force-fail), recall
10
+ returns keyword results and never raises.
11
+ 3. Migration idempotency: re-running ``init()`` against an existing
12
+ DB is a no-op; pre-existing rows survive.
13
+
14
+ The tests inject a deterministic ``embed_fn`` so we don't depend on MLX
15
+ or Ollama being installed. The synonym test uses a tiny 4-dim vector
16
+ space where curated coordinates make ``alpha → beta`` cosine-close while
17
+ keyword overlap is zero.
18
+ """
19
+ from __future__ import annotations
20
+
21
+ import sqlite3
22
+ import sys
23
+ from pathlib import Path
24
+
25
+ import pytest
26
+
27
+ HERE = Path(__file__).resolve().parent
28
+ SCRIPTS = HERE.parent
29
+ if str(SCRIPTS) not in sys.path:
30
+ sys.path.insert(0, str(SCRIPTS))
31
+
32
+ from semantic_index import ( # noqa: E402
33
+ init,
34
+ query_facts,
35
+ upsert_fact,
36
+ )
37
+ from semantic_index.hybrid import ( # noqa: E402
38
+ _cosine,
39
+ _parse_embedding,
40
+ _safe_embed_query,
41
+ has_any_embedding,
42
+ )
43
+
44
+
45
+ # ---------------------------------------------------------------- helpers
46
+
47
+
48
+ def _make_db(tmp_path: Path) -> Path:
49
+ return tmp_path / "hybrid.sqlite"
50
+
51
+
52
+ # Tiny 4-dim semantic space. Two clusters:
53
+ # - "vehicle" cluster: synonyms map to roughly the same direction.
54
+ # - "weather" cluster: orthogonal.
55
+ _VEC = {
56
+ # vehicle cluster
57
+ "auto": [1.0, 0.0, 0.0, 0.0],
58
+ "car": [0.95, 0.31, 0.0, 0.0],
59
+ "automobile": [0.92, 0.39, 0.0, 0.0],
60
+ "vehicle": [0.93, 0.37, 0.0, 0.0],
61
+ # weather cluster (orthogonal)
62
+ "rain": [0.0, 0.0, 1.0, 0.0],
63
+ "storm": [0.0, 0.0, 0.95, 0.31],
64
+ # generic/noise
65
+ "thing": [0.0, 0.0, 0.0, 1.0],
66
+ }
67
+
68
+
69
+ def _embed_fake(text: str) -> list[float]:
70
+ """Deterministic embedder: returns the curated vector for the first
71
+ known token in the text, else a neutral vector.
72
+ """
73
+ tokens = text.lower().split()
74
+ for t in tokens:
75
+ t = t.strip(".,!?;:")
76
+ if t in _VEC:
77
+ return list(_VEC[t])
78
+ return [0.0, 0.0, 0.0, 1.0]
79
+
80
+
81
+ # ---------------------------------------------------------------- 1. synonym
82
+
83
+
84
+ def test_dense_rerank_surfaces_synonym_keyword_misses(tmp_path: Path) -> None:
85
+ """The headline P1 acceptance: a query that keyword-only RANKS WRONG
86
+ gets the right top hit once dense rerank is on.
87
+
88
+ Setup: 3 facts.
89
+ F1 "auto cluster: a transport device" (semantically the right hit
90
+ for query "automobile")
91
+ F2 "thing about the auto cluster" (keyword decoy: matches the
92
+ query token "the" but not
93
+ semantically aligned)
94
+ F3 "rain weather report" (unrelated)
95
+
96
+ Query: "automobile". Keyword-only puts F2 ahead (decoy token "the" +
97
+ semantic-irrelevant noise) — dense rerank pushes F1 to the top
98
+ because ``automobile ≈ auto`` in cosine space.
99
+ """
100
+ db = _make_db(tmp_path)
101
+ upsert_fact(
102
+ subject="F1",
103
+ predicate="cluster",
104
+ object_text="auto cluster device",
105
+ project="p",
106
+ embedding=_VEC["auto"],
107
+ db_path=db,
108
+ )
109
+ upsert_fact(
110
+ subject="F2",
111
+ predicate="thing",
112
+ object_text="the thing notes",
113
+ project="p",
114
+ embedding=_VEC["thing"],
115
+ db_path=db,
116
+ )
117
+ upsert_fact(
118
+ subject="F3",
119
+ predicate="weather",
120
+ object_text="rain weather report",
121
+ project="p",
122
+ embedding=_VEC["rain"],
123
+ db_path=db,
124
+ )
125
+
126
+ keyword_results = query_facts(
127
+ query="automobile", project="p", db_path=db, mode="keyword",
128
+ )
129
+ hybrid_results = query_facts(
130
+ query="automobile",
131
+ project="p",
132
+ db_path=db,
133
+ mode="hybrid",
134
+ embed_fn=_embed_fake,
135
+ )
136
+
137
+ # Control: keyword-only completely MISSES (the query token "automobile"
138
+ # has zero substring hits across F1/F2/F3).
139
+ assert keyword_results == [], (
140
+ f"keyword-only must miss this synonym query; got {[r['subject'] for r in keyword_results]}"
141
+ )
142
+
143
+ # Treatment: hybrid SURFACES F1 (auto ≈ automobile in cosine space).
144
+ assert hybrid_results, "hybrid mode must surface at least one result"
145
+ assert hybrid_results[0]["subject"] == "F1", (
146
+ f"hybrid top result expected F1 (auto cluster device); got "
147
+ f"{[r['subject'] for r in hybrid_results]}"
148
+ )
149
+
150
+
151
+ # ---------------------------------------------------------------- 2. fallback
152
+
153
+
154
+ def test_fallback_to_keyword_when_embed_backend_unavailable(
155
+ tmp_path: Path,
156
+ ) -> None:
157
+ """When the embed backend raises, recall must NOT crash. It must
158
+ degrade to keyword ranking, return successfully, exit cleanly.
159
+ """
160
+ db = _make_db(tmp_path)
161
+ upsert_fact(
162
+ subject="alpha",
163
+ predicate="describes",
164
+ object_text="contains the word adapter explicitly",
165
+ project="p",
166
+ embedding=_VEC["auto"],
167
+ db_path=db,
168
+ )
169
+ upsert_fact(
170
+ subject="beta",
171
+ predicate="describes",
172
+ object_text="unrelated cluster",
173
+ project="p",
174
+ embedding=_VEC["rain"],
175
+ db_path=db,
176
+ )
177
+
178
+ def broken_embed(_text: str) -> list[float]:
179
+ raise RuntimeError("simulated MLX + Ollama outage")
180
+
181
+ out = query_facts(
182
+ query="adapter",
183
+ project="p",
184
+ db_path=db,
185
+ mode="hybrid",
186
+ embed_fn=broken_embed,
187
+ )
188
+
189
+ # Got at least one result back (alpha matches keyword "adapter") and
190
+ # no exception was raised — the headline fallback contract.
191
+ assert out, "fallback must return keyword results, not empty"
192
+ assert out[0]["subject"] == "alpha", out
193
+
194
+
195
+ def test_fallback_when_no_candidate_has_embedding(tmp_path: Path) -> None:
196
+ """When zero candidates carry an embedding, hybrid must transparently
197
+ degrade to pure keyword ranking — never call the embedder.
198
+
199
+ NOTE: post-f1, ``upsert_fact`` auto-embeds on the write path by
200
+ default. To assert the no-embeddings-anywhere state we must opt out
201
+ via ``auto_embed=False`` (this is the legacy behavior). The new
202
+ production behavior is covered by
203
+ ``test_production_path_synonym_hit_without_explicit_embedding``.
204
+ """
205
+ db = _make_db(tmp_path)
206
+ upsert_fact(
207
+ subject="only-keyword-fact",
208
+ predicate="p",
209
+ object_text="adapter boundary lesson",
210
+ project="p",
211
+ db_path=db,
212
+ auto_embed=False,
213
+ )
214
+ calls = {"n": 0}
215
+
216
+ def tracker_embed(text: str) -> list[float]:
217
+ calls["n"] += 1
218
+ return _VEC.get("auto", [0.0, 0.0, 0.0, 1.0])
219
+
220
+ out = query_facts(
221
+ query="adapter",
222
+ project="p",
223
+ db_path=db,
224
+ mode="hybrid",
225
+ embed_fn=tracker_embed,
226
+ )
227
+
228
+ assert out, "must return keyword candidates even with no embeddings"
229
+ assert out[0]["subject"] == "only-keyword-fact"
230
+ # The embedder was never invoked — no candidate had an embedding.
231
+ assert calls["n"] == 0, (
232
+ f"embed_fn should not be called when no candidates carry vectors; "
233
+ f"calls={calls['n']}"
234
+ )
235
+
236
+
237
+ def test_safe_embed_query_swallows_runtime_errors() -> None:
238
+ """Unit-level guard: ``_safe_embed_query`` must return None on any
239
+ raise. This is the seam that turns backend outages into fallback."""
240
+
241
+ def boom(_text: str) -> list[float]:
242
+ raise RuntimeError("backend down")
243
+
244
+ assert _safe_embed_query("anything", boom) is None
245
+ assert _safe_embed_query("", _embed_fake) is None
246
+ assert _safe_embed_query("auto", _embed_fake) == _VEC["auto"]
247
+
248
+
249
+ # ---------------------------------------------------------------- 3. migration
250
+
251
+
252
+ def test_init_is_idempotent(tmp_path: Path) -> None:
253
+ """Re-running ``init()`` against an existing DB is a no-op: pre-existing
254
+ rows survive, schema stays intact, no exception raised.
255
+ """
256
+ db = _make_db(tmp_path)
257
+ # First init creates the table + indexes.
258
+ init(db)
259
+ upsert_fact(
260
+ subject="kept",
261
+ predicate="p",
262
+ object_text="should survive re-init",
263
+ project="p",
264
+ embedding=_VEC["auto"],
265
+ db_path=db,
266
+ )
267
+ # Second init must be a no-op (idempotent) — no schema reset.
268
+ init(db)
269
+ # Third init for good measure (proves N-times idempotency).
270
+ init(db)
271
+
272
+ # Verify the row survived.
273
+ conn = sqlite3.connect(str(db))
274
+ try:
275
+ rows = conn.execute(
276
+ "SELECT subject, embedding_json FROM semantic_facts"
277
+ ).fetchall()
278
+ finally:
279
+ conn.close()
280
+ assert len(rows) == 1
281
+ assert rows[0][0] == "kept"
282
+ # embedding_json column exists AND the value persisted.
283
+ parsed = _parse_embedding(rows[0][1])
284
+ assert parsed is not None and parsed == _VEC["auto"]
285
+
286
+
287
+ def test_init_creates_embedding_column_on_fresh_db(tmp_path: Path) -> None:
288
+ """Fresh DB must have ``embedding_json`` column from the start —
289
+ the embedding store IS the column, no separate migration step."""
290
+ db = _make_db(tmp_path)
291
+ init(db)
292
+ conn = sqlite3.connect(str(db))
293
+ try:
294
+ cols = {r[1] for r in conn.execute("PRAGMA table_info(semantic_facts)").fetchall()}
295
+ finally:
296
+ conn.close()
297
+ assert "embedding_json" in cols, f"missing embedding_json column; got {cols}"
298
+
299
+
300
+ # ---------------------------------------------------------------- math
301
+
302
+
303
+ def test_cosine_orthogonal_is_zero() -> None:
304
+ assert _cosine([1.0, 0.0, 0.0], [0.0, 1.0, 0.0]) == 0.0
305
+
306
+
307
+ def test_cosine_identical_is_one() -> None:
308
+ assert _cosine([1.0, 2.0, 3.0], [1.0, 2.0, 3.0]) == pytest.approx(1.0)
309
+
310
+
311
+ def test_cosine_zero_vector_returns_zero() -> None:
312
+ assert _cosine([0.0, 0.0, 0.0], [1.0, 0.0, 0.0]) == 0.0
313
+
314
+
315
+ def test_parse_embedding_handles_corrupt_json() -> None:
316
+ assert _parse_embedding(None) is None
317
+ assert _parse_embedding("") is None
318
+ assert _parse_embedding("not json") is None
319
+ assert _parse_embedding('{"not": "a list"}') is None
320
+ assert _parse_embedding('[1, "bad", 3]') is None
321
+ assert _parse_embedding("[1.0, 2.0, 3.0]") == [1.0, 2.0, 3.0]
322
+
323
+
324
+ # ---------------------------------------------------------------- f3: has_any_embedding
325
+
326
+
327
+ def test_has_any_embedding_skips_column_less_rows_finds_later_hit(tmp_path: Path) -> None:
328
+ """Regression for the dormancy bug: a candidate row WITHOUT the column
329
+ must not short-circuit the probe. A later row WITH a valid embedding
330
+ must still cause the probe to return True.
331
+
332
+ Before the fix: ``has_any_embedding`` returned False on the first
333
+ column-less row, hybrid silently downgraded to keyword, and dense
334
+ rerank never fired against any heterogeneous candidate set.
335
+ """
336
+ # Build two minimal sqlite3.Row stand-ins via a real SQLite conn.
337
+ conn = sqlite3.connect(":memory:")
338
+ conn.row_factory = sqlite3.Row
339
+ # Row 1: no embedding_json column at all.
340
+ conn.execute("CREATE TABLE no_emb (subject TEXT)")
341
+ conn.execute("INSERT INTO no_emb (subject) VALUES ('without-emb')")
342
+ row_without = conn.execute("SELECT * FROM no_emb").fetchone()
343
+ # Row 2: has embedding_json column and a valid vector.
344
+ conn.execute("CREATE TABLE with_emb (subject TEXT, embedding_json TEXT)")
345
+ conn.execute(
346
+ "INSERT INTO with_emb (subject, embedding_json) VALUES ('with-emb', ?)",
347
+ ("[0.1, 0.2, 0.3]",),
348
+ )
349
+ row_with = conn.execute("SELECT * FROM with_emb").fetchone()
350
+ conn.close()
351
+
352
+ # Bug repro order: column-less row FIRST, embedded row SECOND.
353
+ candidates = [(1.0, row_without), (1.0, row_with)]
354
+ assert has_any_embedding(candidates) is True, (
355
+ "has_any_embedding must continue past column-less rows; "
356
+ "before the fix this returned False on the first row and hybrid "
357
+ "silently downgraded to keyword."
358
+ )
359
+
360
+ # Sanity: all column-less → False.
361
+ assert has_any_embedding([(1.0, row_without)]) is False
362
+
363
+ # Sanity: all-embedded → True.
364
+ assert has_any_embedding([(1.0, row_with)]) is True
365
+
366
+
367
+ # ---------------------------------------------------------------- f1/f2: production write+read path
368
+
369
+
370
+ def test_production_path_synonym_hit_without_explicit_embedding(tmp_path: Path) -> None:
371
+ """The headline f2 regression. ``upsert_fact`` is called WITHOUT
372
+ ``embedding=`` and WITHOUT a curated vector — only an ``embed_fn``
373
+ that the write path uses to populate ``embedding_json`` automatically.
374
+ Then we read via the SAME default path (``query_facts``, no explicit
375
+ ``embedding`` arg anywhere), inject the SAME embedder, and verify
376
+ that a synonym query (zero keyword overlap) returns the right hit.
377
+
378
+ This test would have FAILED before f1 because:
379
+ - upsert_fact stored embedding_json=NULL,
380
+ - has_any_embedding returned False (no candidate had a vector),
381
+ - hybrid silently degraded to keyword,
382
+ - keyword has zero overlap on the synonym query → empty result.
383
+
384
+ After f1: ``upsert_fact(auto_embed=True)`` (default) calls the
385
+ injected ``embed_fn`` and persists the vector, so the query hits.
386
+ """
387
+ db = _make_db(tmp_path)
388
+ # NO explicit embedding= on the write — production code never passes one.
389
+ upsert_fact(
390
+ subject="F1",
391
+ predicate="cluster",
392
+ object_text="auto cluster device",
393
+ project="p",
394
+ db_path=db,
395
+ embed_fn=_embed_fake,
396
+ )
397
+ upsert_fact(
398
+ subject="F2",
399
+ predicate="thing",
400
+ object_text="the thing notes",
401
+ project="p",
402
+ db_path=db,
403
+ embed_fn=_embed_fake,
404
+ )
405
+ upsert_fact(
406
+ subject="F3",
407
+ predicate="weather",
408
+ object_text="rain weather report",
409
+ project="p",
410
+ db_path=db,
411
+ embed_fn=_embed_fake,
412
+ )
413
+
414
+ # Verify embeddings were auto-populated on write.
415
+ conn = sqlite3.connect(str(db))
416
+ null_count = conn.execute(
417
+ "SELECT COUNT(*) FROM semantic_facts WHERE embedding_json IS NULL"
418
+ ).fetchone()[0]
419
+ conn.close()
420
+ assert null_count == 0, (
421
+ f"production write path must auto-populate embedding_json; "
422
+ f"got {null_count} NULL rows"
423
+ )
424
+
425
+ # Keyword-only sanity: synonym query has zero substring hits.
426
+ keyword_results = query_facts(
427
+ query="automobile", project="p", db_path=db, mode="keyword",
428
+ )
429
+ assert keyword_results == []
430
+
431
+ # Default hybrid path — same embed_fn on the read side, no explicit
432
+ # embedding anywhere. Without f1's fix, this would also be empty.
433
+ hybrid_results = query_facts(
434
+ query="automobile",
435
+ project="p",
436
+ db_path=db,
437
+ mode="hybrid",
438
+ embed_fn=_embed_fake,
439
+ )
440
+ assert hybrid_results, (
441
+ "production path synonym hit failed — hybrid is dormant. "
442
+ "Auto-embed-on-write + read-path embed_fn must both fire."
443
+ )
444
+ assert hybrid_results[0]["subject"] == "F1", (
445
+ f"expected F1 (auto cluster) as the top synonym hit; got "
446
+ f"{[r['subject'] for r in hybrid_results]}"
447
+ )
448
+
449
+
450
+ def test_upsert_fact_auto_embed_false_keeps_null(tmp_path: Path) -> None:
451
+ """Opt-out preserves legacy behavior — used by the backfill path
452
+ (which controls the embedder itself) and by tests that want to
453
+ assert the NULL-embedding-then-backfill flow."""
454
+ db = _make_db(tmp_path)
455
+ upsert_fact(
456
+ subject="no-vec",
457
+ predicate="p",
458
+ object_text="text",
459
+ project="p",
460
+ db_path=db,
461
+ embed_fn=_embed_fake, # provided but ignored
462
+ auto_embed=False,
463
+ )
464
+ conn = sqlite3.connect(str(db))
465
+ raw = conn.execute(
466
+ "SELECT embedding_json FROM semantic_facts WHERE subject='no-vec'"
467
+ ).fetchone()[0]
468
+ conn.close()
469
+ assert raw is None
470
+
471
+
472
+ def test_upsert_fact_write_path_swallows_embed_failure(tmp_path: Path) -> None:
473
+ """Write contract: backend down on auto-embed MUST NOT raise. Row
474
+ lands with NULL embedding_json; backfill can populate later."""
475
+ db = _make_db(tmp_path)
476
+
477
+ def broken(_t: str) -> list[float]:
478
+ raise RuntimeError("simulated backend down at write time")
479
+
480
+ # Must not raise — this is the production-availability contract.
481
+ upsert_fact(
482
+ subject="written-despite-outage",
483
+ predicate="p",
484
+ object_text="content",
485
+ project="p",
486
+ db_path=db,
487
+ embed_fn=broken,
488
+ )
489
+
490
+ conn = sqlite3.connect(str(db))
491
+ raw = conn.execute(
492
+ "SELECT embedding_json FROM semantic_facts WHERE subject='written-despite-outage'"
493
+ ).fetchone()[0]
494
+ conn.close()
495
+ assert raw is None # NULL — backfill can pick this up later.
496
+
497
+
498
+ def test_read_semantic_passes_embed_fn_through_to_query_facts(tmp_path: Path) -> None:
499
+ """The f1(b) wiring: ``memory_facade.semantic.read_semantic`` must
500
+ forward an ``embed_fn`` to ``query_facts`` so hybrid rerank actually
501
+ fires on the production read path.
502
+
503
+ Verified by writing rows that pass synonym hits via embedding (zero
504
+ keyword overlap), then calling read_semantic with the same embedder.
505
+ """
506
+ db = _make_db(tmp_path)
507
+ upsert_fact(
508
+ subject="F1",
509
+ predicate="cluster",
510
+ object_text="auto cluster device",
511
+ project="p",
512
+ db_path=db,
513
+ embed_fn=_embed_fake,
514
+ )
515
+
516
+ # We must point read_semantic's underlying query_facts at OUR temp DB.
517
+ # The facade resolves the path via semantic_index.default_db_path(); we
518
+ # monkey-patch _db_path for the duration of this call.
519
+ import semantic_index as si # noqa: PLC0415
520
+ from memory_facade import semantic as facade # noqa: PLC0415
521
+
522
+ original_path_fn = si._db_path
523
+ si._db_path = lambda _p=None: db # type: ignore[assignment]
524
+ try:
525
+ out, reasons = facade.read_semantic(
526
+ workdir=tmp_path,
527
+ query="automobile",
528
+ limit=5,
529
+ project="p",
530
+ skip_postgres=True,
531
+ embed_fn=_embed_fake,
532
+ )
533
+ finally:
534
+ si._db_path = original_path_fn
535
+
536
+ assert out, f"read_semantic must surface the synonym via embed_fn; reasons={reasons}"
537
+ assert out[0]["subject"] == "F1"
538
+
539
+
540
+ if __name__ == "__main__":
541
+ pytest.main([__file__, "-v"])