@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,556 @@
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
+ """install_memory.py — bootstrap the build-loop memory directory.
5
+
6
+ Creates the canonical build-loop-memory root (default:
7
+ `~/dev/git-folder/build-loop-memory`) and seeds it with template
8
+ `constitution.md` + `MEMORY.md` if either is missing. Idempotent — never
9
+ overwrites existing files.
10
+
11
+ Optional: link the directory to a private git repo so user-specific memory
12
+ content is versioned. The build-loop public repo ships ONLY the templates;
13
+ the user's actual lessons / patterns / decisions belong in a separate private
14
+ repo (see `docs/memory-setup.md`).
15
+
16
+ Usage:
17
+ python3 scripts/install_memory.py # bootstrap + seed
18
+ python3 scripts/install_memory.py --guided # guided terminal install
19
+ python3 scripts/install_memory.py --check # report status, no writes
20
+ python3 scripts/install_memory.py --validate-seed # validate packaged public seed
21
+ python3 scripts/install_memory.py --link-repo <url> # bootstrap + clone private repo
22
+ python3 scripts/install_memory.py --dest <path> # override default location
23
+
24
+ Exit codes:
25
+ 0 — success (or check completed)
26
+ 1 — write failure
27
+ 2 — invalid arguments
28
+ """
29
+ from __future__ import annotations
30
+
31
+ import argparse
32
+ import json
33
+ import re
34
+ import shutil
35
+ import subprocess
36
+ import sys
37
+ from pathlib import Path
38
+ from typing import Optional
39
+
40
+ HERE = Path(__file__).resolve().parent
41
+ if str(HERE) not in sys.path:
42
+ sys.path.insert(0, str(HERE))
43
+
44
+ try:
45
+ from _paths import _safe_project_relpath, memory_store_root # type: ignore # noqa: E402
46
+ except Exception: # noqa: BLE001
47
+ memory_store_root = None # type: ignore[assignment]
48
+ _safe_project_relpath = None # type: ignore[assignment]
49
+
50
+ DEFAULT_DEST = memory_store_root() if memory_store_root is not None else Path.home() / "dev" / "git-folder" / "build-loop-memory"
51
+ TEMPLATE_DIR_RELATIVE = "templates/memory"
52
+ SEED_MANIFEST_FILENAME = "manifest.json"
53
+ TEMPLATES = [
54
+ ("constitution.md.template", "constitution.md"),
55
+ ("MEMORY.md.template", "MEMORY.md"),
56
+ ]
57
+ PROJECT_RAW_SUBDIRS = (
58
+ "documents",
59
+ "data",
60
+ "db",
61
+ "runtime",
62
+ "agent-artifacts",
63
+ "artifacts",
64
+ "files",
65
+ )
66
+ PROJECT_TOPIC_DIRS = frozenset({
67
+ "apps",
68
+ "assets",
69
+ "architecture",
70
+ "context",
71
+ "decisions",
72
+ "docs",
73
+ "features",
74
+ "formats",
75
+ "indexes",
76
+ "lessons",
77
+ "plugins",
78
+ "product",
79
+ "prompts",
80
+ "raw",
81
+ "research",
82
+ "semantic",
83
+ "skills",
84
+ "sources",
85
+ "testing",
86
+ "tradeoffs",
87
+ })
88
+ KEEP_FILENAME = ".gitkeep"
89
+
90
+
91
+ def script_dir() -> Path:
92
+ return HERE
93
+
94
+
95
+ def template_dir() -> Path:
96
+ # scripts/install_memory.py lives next to scripts/ at the repo root
97
+ return script_dir().parent / TEMPLATE_DIR_RELATIVE
98
+
99
+
100
+ def seed_manifest_path(tpl_dir: Optional[Path] = None) -> Path:
101
+ return (tpl_dir or template_dir()) / SEED_MANIFEST_FILENAME
102
+
103
+
104
+ def _load_seed_manifest(tpl_dir: Optional[Path] = None) -> tuple[Optional[dict], list[str]]:
105
+ path = seed_manifest_path(tpl_dir)
106
+ if not path.exists():
107
+ return None, [f"seed manifest missing: {path}"]
108
+ try:
109
+ data = json.loads(path.read_text(encoding="utf-8"))
110
+ except json.JSONDecodeError as exc:
111
+ return None, [f"seed manifest invalid JSON: {exc}"]
112
+ if not isinstance(data, dict):
113
+ return None, ["seed manifest root must be an object"]
114
+ issues: list[str] = []
115
+ if data.get("kind") != "build-loop-memory-public-seed":
116
+ issues.append("seed manifest kind must be build-loop-memory-public-seed")
117
+ if not data.get("schema_version"):
118
+ issues.append("seed manifest schema_version missing")
119
+ if not isinstance(data.get("sources"), list):
120
+ issues.append("seed manifest sources must be a list")
121
+ privacy = data.get("privacy")
122
+ if not isinstance(privacy, dict):
123
+ issues.append("seed manifest privacy block missing")
124
+ elif not isinstance(privacy.get("deny_patterns"), list):
125
+ issues.append("seed manifest privacy.deny_patterns must be a list")
126
+ return data, issues
127
+
128
+
129
+ def _manifest_source_rels(manifest: dict) -> tuple[set[str], list[str]]:
130
+ rels: set[str] = set()
131
+ issues: list[str] = []
132
+ for entry in manifest.get("sources", []):
133
+ if not isinstance(entry, dict):
134
+ issues.append("seed manifest source entry must be an object")
135
+ continue
136
+ source = entry.get("source")
137
+ if isinstance(source, str):
138
+ rel_path = Path(source)
139
+ if rel_path.is_absolute() or ".." in rel_path.parts:
140
+ issues.append(f"seed manifest source must stay inside template dir: {source}")
141
+ continue
142
+ rels.add(source)
143
+ else:
144
+ issues.append("seed manifest source entry missing source string")
145
+ return rels, issues
146
+
147
+
148
+ def validate_public_seed(tpl_dir: Optional[Path] = None) -> dict:
149
+ """Validate that the packaged memory seed is scaffold-only and allowlisted."""
150
+ root = tpl_dir or template_dir()
151
+ manifest, issues = _load_seed_manifest(root)
152
+ files: list[str] = []
153
+ seed_version = None
154
+ privacy_classification = None
155
+ if manifest is not None:
156
+ seed_version = manifest.get("seed_version")
157
+ privacy = manifest.get("privacy") if isinstance(manifest.get("privacy"), dict) else {}
158
+ privacy_classification = privacy.get("classification") if isinstance(privacy, dict) else None
159
+ allowed, source_issues = _manifest_source_rels(manifest)
160
+ issues.extend(source_issues)
161
+ if not allowed:
162
+ issues.append("seed manifest has no source allowlist")
163
+ for rel in sorted(allowed):
164
+ if not (root / rel).is_file():
165
+ issues.append(f"allowlisted seed file missing: {rel}")
166
+ deny_patterns = privacy.get("deny_patterns", []) if isinstance(privacy, dict) else []
167
+ compiled_patterns: list[tuple[str, re.Pattern[str]]] = []
168
+ for pattern in deny_patterns:
169
+ if not isinstance(pattern, str):
170
+ issues.append("seed manifest deny pattern must be a string")
171
+ continue
172
+ try:
173
+ compiled_patterns.append((pattern, re.compile(pattern)))
174
+ except re.error as exc:
175
+ issues.append(f"seed manifest deny pattern invalid: {pattern}: {exc}")
176
+
177
+ for path in sorted(root.rglob("*")):
178
+ if path.is_dir():
179
+ continue
180
+ rel = path.relative_to(root).as_posix()
181
+ if rel == SEED_MANIFEST_FILENAME:
182
+ continue
183
+ files.append(rel)
184
+ if rel not in allowed:
185
+ issues.append(f"seed file not allowlisted in manifest: {rel}")
186
+ continue
187
+ try:
188
+ body = path.read_text(encoding="utf-8")
189
+ except UnicodeDecodeError:
190
+ issues.append(f"seed file is not UTF-8 text: {rel}")
191
+ continue
192
+ for pattern, compiled in compiled_patterns:
193
+ if compiled.search(body):
194
+ issues.append(f"privacy deny pattern matched {rel}: {pattern}")
195
+ return {
196
+ "ok": not issues,
197
+ "kind": "build-loop-memory-public-seed-validation",
198
+ "manifest": str(seed_manifest_path(root)),
199
+ "seed_version": seed_version,
200
+ "privacy_classification": privacy_classification,
201
+ "files": files,
202
+ "issues": issues,
203
+ }
204
+
205
+
206
+ def print_seed_validation(validation: dict) -> None:
207
+ status = "ok" if validation.get("ok") else "failed"
208
+ print(f"public seed: {status}")
209
+ print(f" manifest: {validation.get('manifest')}")
210
+ print(f" seed_version: {validation.get('seed_version') or 'unknown'}")
211
+ print(f" privacy: {validation.get('privacy_classification') or 'unknown'}")
212
+ for rel in validation.get("files") or []:
213
+ print(f" - {rel}")
214
+ for issue in validation.get("issues") or []:
215
+ print(f" ! {issue}")
216
+
217
+
218
+ def report(dest: Path) -> dict:
219
+ """Inspect the destination and report what's present / missing."""
220
+ status = {
221
+ "dest": str(dest),
222
+ "exists": dest.exists(),
223
+ "is_git_repo": (dest / ".git").exists() if dest.exists() else False,
224
+ "files": {},
225
+ }
226
+ for _, target_name in TEMPLATES:
227
+ target = dest / target_name
228
+ status["files"][target_name] = {
229
+ "exists": target.exists(),
230
+ "size_bytes": target.stat().st_size if target.exists() else 0,
231
+ }
232
+ # Count user content (root level, excluding templates and the projects/ tree)
233
+ if dest.exists():
234
+ template_names = {t[1] for t in TEMPLATES}
235
+ user_files = [
236
+ p for p in dest.glob("*.md")
237
+ if p.name not in template_names and p.name != "README.md"
238
+ ]
239
+ status["user_md_files"] = len(user_files)
240
+ # Project segmentation (NEW PR 1)
241
+ indexes_dir = dest / "indexes"
242
+ status["indexes_dir_exists"] = indexes_dir.is_dir()
243
+ projects_dir = dest / "projects"
244
+ status["projects_dir_exists"] = projects_dir.is_dir()
245
+ if projects_dir.is_dir():
246
+ per_project: list[dict] = []
247
+ for sub in sorted(projects_dir.iterdir()):
248
+ if not sub.is_dir():
249
+ continue
250
+ # Count .md files at top level of the project subdir (sub-component
251
+ # dirs like workers/ contribute their own row).
252
+ md_count = sum(1 for _ in sub.glob("*.md"))
253
+ raw_status = _raw_status(sub)
254
+ per_project.append({
255
+ "slug": sub.name,
256
+ "md_files": md_count,
257
+ "raw_exists": raw_status["exists"],
258
+ "raw_missing": raw_status["missing"],
259
+ })
260
+ # Sub-components (e.g. workers/) — surface as <slug>/<sub>
261
+ for nested in sorted(sub.iterdir()):
262
+ if nested.is_dir() and not nested.name.startswith("_") and nested.name not in PROJECT_TOPIC_DIRS:
263
+ nested_count = sum(1 for _ in nested.glob("*.md"))
264
+ if nested_count:
265
+ per_project.append({
266
+ "slug": f"{sub.name}/{nested.name}",
267
+ "md_files": nested_count,
268
+ })
269
+ status["projects"] = per_project
270
+ status["public_seed"] = validate_public_seed()
271
+ return status
272
+
273
+
274
+ def _raw_status(project_dir: Path) -> dict:
275
+ """Return raw-lane presence for a project directory."""
276
+ raw = project_dir / "raw"
277
+ missing = [
278
+ name for name in PROJECT_RAW_SUBDIRS
279
+ if not (raw / name).is_dir()
280
+ ]
281
+ return {"exists": raw.is_dir() and not missing, "missing": missing}
282
+
283
+
284
+ def seed_template(src: Path, dest: Path, force: bool = False) -> tuple[bool, str]:
285
+ """Copy a template if the destination doesn't exist (or force=True). Returns (wrote, reason)."""
286
+ if dest.exists() and not force:
287
+ return False, "already exists"
288
+ if not src.exists():
289
+ return False, f"template missing: {src}"
290
+ shutil.copy2(src, dest)
291
+ return True, "seeded"
292
+
293
+
294
+ def _project_relpath(project: str) -> Path:
295
+ if _safe_project_relpath is None:
296
+ raise ValueError("_paths._safe_project_relpath unavailable")
297
+ return _safe_project_relpath(project)
298
+
299
+
300
+ def ensure_project_scaffold(dest: Path, project: str) -> Path:
301
+ """Create the per-project raw-source scaffold and return the project dir."""
302
+ project_dir = dest / "projects" / _project_relpath(project)
303
+ raw_dir = project_dir / "raw"
304
+ for subdir in PROJECT_RAW_SUBDIRS:
305
+ leaf = raw_dir / subdir
306
+ leaf.mkdir(parents=True, exist_ok=True)
307
+ keep = leaf / KEEP_FILENAME
308
+ if not keep.exists():
309
+ keep.write_text("", encoding="utf-8")
310
+ return project_dir
311
+
312
+
313
+ def link_private_repo(dest: Path, repo_url: str) -> tuple[bool, str]:
314
+ """Clone a private repo INTO the dest path. Requires dest to be empty or non-existent.
315
+
316
+ The repo is expected to contain user-specific memory content (feedback_*.md, pattern_*.md, etc.).
317
+ Templates are NOT seeded when linking — the repo provides the actual content.
318
+ """
319
+ if dest.exists() and any(dest.iterdir()):
320
+ return False, (
321
+ f"{dest} exists and is non-empty. Move existing content out, then re-run with --link-repo."
322
+ )
323
+ dest.parent.mkdir(parents=True, exist_ok=True)
324
+ try:
325
+ subprocess.run(
326
+ ["git", "clone", repo_url, str(dest)],
327
+ check=True,
328
+ capture_output=True,
329
+ )
330
+ except subprocess.CalledProcessError as e:
331
+ return False, f"git clone failed: {e.stderr.decode().strip() or e}"
332
+ return True, f"cloned {repo_url} → {dest}"
333
+
334
+
335
+ def main(argv: Optional[list[str]] = None) -> int:
336
+ p = argparse.ArgumentParser(description=__doc__.split("\n\n")[0])
337
+ p.add_argument("--dest", default=str(DEFAULT_DEST),
338
+ help=f"Memory directory location (default: {DEFAULT_DEST})")
339
+ p.add_argument("--check", action="store_true",
340
+ help="Report status without writing")
341
+ p.add_argument("--guided", action="store_true",
342
+ help="Terminal-first guided install: validate packaged seed, bootstrap, and print next steps.")
343
+ p.add_argument("--validate-seed", action="store_true",
344
+ help="Validate the packaged public memory seed without writing")
345
+ p.add_argument("--json", action="store_true",
346
+ help="Emit machine-readable JSON for --check or --validate-seed")
347
+ p.add_argument("--link-repo", default=None,
348
+ help="Clone a private git repo into the dest (for versioned user content). "
349
+ "Skips template seeding — the repo provides content.")
350
+ p.add_argument("--force", action="store_true",
351
+ help="Overwrite existing template files (dangerous; use only on fresh install)")
352
+ p.add_argument("--ensure-project", action="append", default=[],
353
+ help="Create projects/<slug>/raw/ lane folders with .gitkeep files. "
354
+ "May be passed more than once.")
355
+ args = p.parse_args(argv)
356
+
357
+ dest = Path(args.dest).expanduser().resolve()
358
+
359
+ if args.json and not (args.check or args.validate_seed):
360
+ print("install_memory: --json is only supported with --check or --validate-seed", file=sys.stderr)
361
+ return 2
362
+
363
+ if args.guided and args.link_repo:
364
+ print("install_memory: --guided and --link-repo are separate paths; run one at a time", file=sys.stderr)
365
+ return 2
366
+
367
+ if args.validate_seed:
368
+ validation = validate_public_seed()
369
+ if args.json:
370
+ print(json.dumps(validation, indent=2, sort_keys=True))
371
+ else:
372
+ print_seed_validation(validation)
373
+ return 0 if validation.get("ok") else 1
374
+
375
+ if args.check:
376
+ status = report(dest)
377
+ if args.json:
378
+ print(json.dumps(status, indent=2, sort_keys=True))
379
+ return 0
380
+ print(f"dest: {status['dest']}")
381
+ print(f" exists: {status['exists']}")
382
+ print(f" is_git_repo: {status['is_git_repo']}")
383
+ for name, info in status["files"].items():
384
+ present = "✓" if info["exists"] else "✗"
385
+ print(f" {present} {name} ({info['size_bytes']}B)")
386
+ if "user_md_files" in status:
387
+ print(f" global content: {status['user_md_files']} .md files beyond templates")
388
+ projects_present = "✓" if status.get("projects_dir_exists") else "✗"
389
+ indexes_present = "✓" if status.get("indexes_dir_exists") else "✗"
390
+ print(f" {indexes_present} indexes/ subtree")
391
+ print(f" {projects_present} projects/ subtree")
392
+ if status.get("projects"):
393
+ for entry in status["projects"]:
394
+ if "raw_exists" not in entry:
395
+ raw = "raw n/a"
396
+ else:
397
+ raw = "raw ok" if entry.get("raw_exists") else (
398
+ "raw missing: " + ",".join(entry.get("raw_missing") or [])
399
+ )
400
+ print(f" - {entry['slug']}: {entry['md_files']} file(s), {raw}")
401
+ elif status.get("projects_dir_exists"):
402
+ print(" (no project subdirs yet — populated by migration script in PR 1.5)")
403
+ print_seed_validation(status["public_seed"])
404
+ return 0
405
+
406
+ # Link-repo path: clone instead of seeding
407
+ if args.link_repo:
408
+ ok, reason = link_private_repo(dest, args.link_repo)
409
+ if not ok:
410
+ print(f"install_memory: {reason}", file=sys.stderr)
411
+ return 1
412
+ print(f"install_memory: {reason}")
413
+ # Verify templates landed via the repo (warn if not)
414
+ for _, target_name in TEMPLATES:
415
+ if not (dest / target_name).exists():
416
+ print(f" ⚠️ expected file not in cloned repo: {target_name}", file=sys.stderr)
417
+ return 0
418
+
419
+ seed_validation = validate_public_seed()
420
+ if not seed_validation.get("ok"):
421
+ print_seed_validation(seed_validation)
422
+ print("install_memory: packaged public seed failed validation; refusing to install templates", file=sys.stderr)
423
+ return 1
424
+
425
+ if args.guided and not args.json:
426
+ print("Build-loop memory guided install")
427
+ print(f" dest: {dest}")
428
+ print(" source: packaged public seed only; no personal memory content is copied")
429
+ print()
430
+ print_seed_validation(seed_validation)
431
+ print()
432
+
433
+ # Standard bootstrap: mkdir + seed templates
434
+ dest.mkdir(parents=True, exist_ok=True)
435
+ print(f"install_memory: dest = {dest}")
436
+ tpl_dir = template_dir()
437
+ if not tpl_dir.exists():
438
+ print(f"install_memory: template directory missing: {tpl_dir}", file=sys.stderr)
439
+ return 1
440
+
441
+ for template_name, target_name in TEMPLATES:
442
+ src = tpl_dir / template_name
443
+ target = dest / target_name
444
+ wrote, reason = seed_template(src, target, force=args.force)
445
+ status = "✓ seeded" if wrote else f"– skipped ({reason})"
446
+ print(f" {status}: {target_name}")
447
+
448
+ indexes_dir = dest / "indexes"
449
+ if not indexes_dir.exists():
450
+ indexes_dir.mkdir(parents=True, exist_ok=True)
451
+ print(" ✓ created: indexes/")
452
+ else:
453
+ print(" – skipped: indexes/ (already exists)")
454
+
455
+ # Bootstrap the project-segmentation subtree (PR 1).
456
+ projects_dir = dest / "projects"
457
+ projects_readme = projects_dir / "README.md"
458
+ if not projects_dir.exists():
459
+ projects_dir.mkdir(parents=True, exist_ok=True)
460
+ print(" ✓ created: projects/")
461
+ else:
462
+ print(" – skipped: projects/ (already exists)")
463
+ if not projects_readme.exists():
464
+ projects_readme.write_text(_PROJECTS_README_BODY, encoding="utf-8")
465
+ print(" ✓ seeded: projects/README.md")
466
+ else:
467
+ print(" – skipped: projects/README.md (already exists)")
468
+
469
+ for project in args.ensure_project:
470
+ try:
471
+ project_dir = ensure_project_scaffold(dest, project)
472
+ except ValueError as exc:
473
+ print(f"install_memory: invalid project tag {project!r}: {exc}", file=sys.stderr)
474
+ return 2
475
+ print(f" ✓ ensured: {project_dir.relative_to(dest)}/raw/")
476
+
477
+ print()
478
+ print("Done. Next steps:")
479
+ print(f" - Edit {dest / 'constitution.md'} with your invariants")
480
+ print(f" - Add lessons over time as feedback_*.md / pattern_*.md / reference_*.md")
481
+ print(" - Optionally version this directory in a private git repo:")
482
+ print(f" cd {dest} && git init && git remote add origin <your-private-repo-url>")
483
+ print(f" - Project-scoped lessons: {dest / 'projects' / '<slug>'} (filled by migration when needed)")
484
+ print(f" - Raw source files: {dest / 'projects' / '<slug>' / 'raw'}")
485
+ print(" - See docs/memory-setup.md for full setup guide")
486
+ return 0
487
+
488
+
489
+ _PROJECTS_README_BODY = """# projects/ — project-scoped lessons
490
+
491
+ Per-project memory entries live in subdirectories of this folder:
492
+
493
+ ```
494
+ projects/
495
+ ├── build-loop/
496
+ │ ├── MEMORY.md # project-specific index (optional)
497
+ │ ├── constitution.md # project-specific overrides (optional)
498
+ │ ├── raw/ # verbatim source material
499
+ │ │ ├── documents/ # PDFs, docs, notes, exported pages
500
+ │ │ ├── data/ # JSON, YAML, TOML, CSV, XML
501
+ │ │ ├── db/ # schemas, migrations, SQL
502
+ │ │ ├── agent-artifacts/ # .build-loop/.claude/.codex/.navgator context
503
+ │ │ ├── files/ # reusable source files copied for reference
504
+ │ │ └── artifacts/ # screenshots, traces, exports
505
+ │ └── feedback_*.md / pattern_*.md / reference_*.md
506
+ ├── example-project/
507
+ │ └── workers/ # sub-component memory (deliberate hierarchy)
508
+ └── _archive/
509
+ └── <retired-project>/ # historical memory, still queryable
510
+ ```
511
+
512
+ ## Slug derivation
513
+
514
+ The slug for the current working directory is derived by
515
+ `scripts/derive_slug_from_cwd` in build-loop:
516
+
517
+ 1. Resolve symlinks (`Path.resolve()`)
518
+ 2. Walk up looking for a `.git` directory
519
+ 3. Slug = `basename(repo_root)` normalized (lowercase, non-safe chars → `-`)
520
+ 4. If `cwd` is under `<repo_root>/workers/...`, append `/workers` to the slug
521
+ 5. No `.git` ancestor → `_unscoped`
522
+
523
+ This is filesystem-driven; no hand-maintained registry. The legacy
524
+ `projects.yaml` (in build-loop-memory repo) remains as a fallback for
525
+ explicit aliases only.
526
+
527
+ ## Precedence
528
+
529
+ When the orchestrator loads memory at Phase 1 Assess:
530
+
531
+ 1. Global tier — files at the root of the build-loop-memory store
532
+ 2. Project tier — files at `projects/<slug>/` under that store
533
+
534
+ Later tiers OVERRIDE earlier ones on filename collision (project wins
535
+ over global).
536
+
537
+ Raw source material belongs under `projects/<slug>/raw/` and stays verbatim.
538
+ Use `documents/` for human-readable source documents, `data/` for structured
539
+ data/config, `db/` for schemas and migrations, `agent-artifacts/` for local
540
+ agent context, `files/` for reusable source files, and `artifacts/` for
541
+ screenshots, traces, and exports.
542
+
543
+ Historical: a legacy per-repo tier (`<repo>/.build-loop/memory/`) was
544
+ read during the PR 1/2 transition. PR 3 (2026-05-13) removed that
545
+ read-shim; the consolidated tree is now the only readable location.
546
+
547
+ ## Privacy
548
+
549
+ Same as the root: this directory should be in a private git repo. The
550
+ build-loop public repo ships templates + the projects/ scaffolding only,
551
+ not the user-specific content.
552
+ """
553
+
554
+
555
+ if __name__ == "__main__":
556
+ sys.exit(main())
@@ -0,0 +1,77 @@
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
+ """lessons_index — portable SQLite/FTS5 lessons index for build-loop.
5
+
6
+ Zero external dependencies. Works out-of-box on a fresh install.
7
+ Postgres recall.py remains the optional power-tier; this is the PRIMARY.
8
+
9
+ Public API:
10
+ ingest(project, db_path) — walk memory lanes, upsert markdown → SQLite
11
+ query(goal_text, project, limit, db_path) — BM25 + optional cosine rerank
12
+ stats(db_path) — counts + schema_version + last_ingest_ts
13
+ open_db(db_path) — connection helper (creates schema if needed)
14
+ """
15
+ from __future__ import annotations
16
+
17
+ import sys
18
+ from pathlib import Path
19
+
20
+ _PKG_DIR = Path(__file__).resolve().parent
21
+ for _p in (str(_PKG_DIR), str(_PKG_DIR.parent)):
22
+ if _p not in sys.path:
23
+ sys.path.insert(0, _p)
24
+
25
+ import importlib.util as _iutil # noqa: E402
26
+
27
+ def _load_pkg_module(name: str):
28
+ spec = _iutil.spec_from_file_location(
29
+ f"lessons_index._{name}", _PKG_DIR / f"{name}.py"
30
+ )
31
+ mod = _iutil.module_from_spec(spec)
32
+ spec.loader.exec_module(mod)
33
+ return mod
34
+
35
+ _schema = _load_pkg_module("schema")
36
+ _ingest = _load_pkg_module("ingest")
37
+ _query = _load_pkg_module("query")
38
+
39
+ open_db = _schema.open_db
40
+ ingest = _ingest.ingest
41
+ query = _query.query
42
+
43
+
44
+ def stats(db_path=None) -> dict:
45
+ """Return a stats dict: {total_facts, schema_version, last_ingest_ts}.
46
+
47
+ Safe to call on a non-existent DB (returns zeros).
48
+ """
49
+ import sqlite3 # noqa: PLC0415
50
+ from pathlib import Path as _Path # noqa: PLC0415
51
+
52
+ if db_path is None:
53
+ try:
54
+ from _paths import memory_store_root # type: ignore # noqa: PLC0415
55
+ db_path = memory_store_root() / "indexes" / "lessons_index.db"
56
+ except ImportError:
57
+ return {"total_facts": 0, "schema_version": None, "last_ingest_ts": None}
58
+
59
+ db_path = _Path(db_path)
60
+ if not db_path.exists():
61
+ return {"total_facts": 0, "schema_version": None, "last_ingest_ts": None}
62
+
63
+ conn = open_db(db_path)
64
+ try:
65
+ total = conn.execute("SELECT COUNT(*) FROM facts").fetchone()[0]
66
+ meta_rows = conn.execute("SELECT key, value FROM meta").fetchall()
67
+ meta = {r["key"]: r["value"] for r in meta_rows}
68
+ return {
69
+ "total_facts": total,
70
+ "schema_version": meta.get("schema_version"),
71
+ "last_ingest_ts": meta.get("last_ingest_ts"),
72
+ }
73
+ finally:
74
+ conn.close()
75
+
76
+
77
+ __all__ = ["ingest", "query", "stats", "open_db"]