@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,1037 @@
1
+ "use strict";
2
+ // SPDX-FileCopyrightText: 2025-2026 Tyrone Ross, Jr <46267523+tyroneross@users.noreply.github.com>
3
+ // SPDX-License-Identifier: Apache-2.0
4
+ /**
5
+ * Debugging Memory System - Storage Layer
6
+ *
7
+ * Handles reading/writing incidents and patterns to filesystem.
8
+ * File-based storage for simplicity and portability.
9
+ * Now supports configurable paths (local or shared mode).
10
+ */
11
+ var __importDefault = (this && this.__importDefault) || function (mod) {
12
+ return (mod && mod.__esModule) ? mod : { "default": mod };
13
+ };
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.storeIncident = storeIncident;
16
+ exports.loadIncident = loadIncident;
17
+ exports.loadAllIncidents = loadAllIncidents;
18
+ exports.storePattern = storePattern;
19
+ exports.loadPattern = loadPattern;
20
+ exports.loadAllPatterns = loadAllPatterns;
21
+ exports.validateIncident = validateIncident;
22
+ exports.generateIncidentId = generateIncidentId;
23
+ exports.generatePatternId = generatePatternId;
24
+ exports.getMemoryStats = getMemoryStats;
25
+ exports.generateIncidentSummary = generateIncidentSummary;
26
+ exports.toCompactIncident = toCompactIncident;
27
+ exports.toCompactPattern = toCompactPattern;
28
+ exports.toCompactIncidents = toCompactIncidents;
29
+ exports.toCompactPatterns = toCompactPatterns;
30
+ exports.estimateCompactIncidentTokens = estimateCompactIncidentTokens;
31
+ exports.estimateCompactPatternTokens = estimateCompactPatternTokens;
32
+ exports.enforceTokenBudget = enforceTokenBudget;
33
+ exports.loadCompactIncidents = loadCompactIncidents;
34
+ exports.loadCompactPatterns = loadCompactPatterns;
35
+ exports.appendToIncidentLog = appendToIncidentLog;
36
+ exports.searchIncidentLog = searchIncidentLog;
37
+ exports.rebuildIndex = rebuildIndex;
38
+ exports.loadIndex = loadIndex;
39
+ exports.buildMemorySummary = buildMemorySummary;
40
+ exports.archiveOldIncidents = archiveOldIncidents;
41
+ exports.compressContext = compressContext;
42
+ exports.updateKeywordIndex = updateKeywordIndex;
43
+ exports.loadKeywordIndex = loadKeywordIndex;
44
+ exports.findCandidatesByKeyword = findCandidatesByKeyword;
45
+ exports.rebuildKeywordIndex = rebuildKeywordIndex;
46
+ exports.updateIndexIncremental = updateIndexIncremental;
47
+ exports.recordOutcome = recordOutcome;
48
+ exports.loadOutcomes = loadOutcomes;
49
+ exports.getOutcomeStats = getOutcomeStats;
50
+ const promises_1 = __importDefault(require("fs/promises"));
51
+ const path_1 = __importDefault(require("path"));
52
+ const config_1 = require("./config");
53
+ const interactive_verifier_1 = require("./interactive-verifier");
54
+ const logger_1 = require("./logger");
55
+ /**
56
+ * Store an incident in memory
57
+ */
58
+ async function storeIncident(incident, options = {}) {
59
+ return (0, logger_1.traced)('storage:storeIncident', { id: incident.incident_id }, async () => {
60
+ let finalIncident = incident;
61
+ // Use interactive mode if requested
62
+ if (options.interactive) {
63
+ finalIncident = await (0, interactive_verifier_1.buildIncidentInteractive)(incident);
64
+ }
65
+ // Calculate quality score if not already set
66
+ if (!finalIncident.completeness?.quality_score) {
67
+ const qualityScore = (0, interactive_verifier_1.calculateQualityScore)(finalIncident);
68
+ if (!finalIncident.completeness) {
69
+ finalIncident.completeness = {
70
+ symptom: !!finalIncident.symptom && finalIncident.symptom.length >= 20,
71
+ root_cause: !!finalIncident.root_cause?.description && finalIncident.root_cause.description.length >= 50,
72
+ fix: !!finalIncident.fix?.approach && (finalIncident.fix.changes?.length || 0) > 0,
73
+ verification: finalIncident.verification?.status === 'verified',
74
+ quality_score: qualityScore
75
+ };
76
+ }
77
+ else {
78
+ finalIncident.completeness.quality_score = qualityScore;
79
+ }
80
+ }
81
+ // Validate if requested
82
+ if (options.validate_schema) {
83
+ const validation = validateIncident(finalIncident);
84
+ if (!validation.valid) {
85
+ throw new Error(`Invalid incident: ${validation.errors.join(', ')}`);
86
+ }
87
+ }
88
+ // Get paths from config
89
+ const paths = (0, config_1.getMemoryPaths)(options.config);
90
+ const INCIDENTS_DIR = paths.incidents;
91
+ // Ensure directory exists
92
+ await promises_1.default.mkdir(INCIDENTS_DIR, { recursive: true });
93
+ // Generate filename
94
+ const filename = `${finalIncident.incident_id}.json`;
95
+ const filepath = path_1.default.join(INCIDENTS_DIR, filename);
96
+ // Write to file
97
+ await promises_1.default.writeFile(filepath, JSON.stringify(finalIncident, null, 2), 'utf-8');
98
+ const qualityEmoji = (finalIncident.completeness.quality_score >= 0.75) ? '🌟' :
99
+ (finalIncident.completeness.quality_score >= 0.5) ? '✅' : '⚠️';
100
+ console.log(`${qualityEmoji} Incident stored: ${finalIncident.incident_id} (quality: ${(finalIncident.completeness.quality_score * 100).toFixed(0)}%)`);
101
+ // Append to JSONL log for fast full-text search
102
+ await appendToIncidentLog(finalIncident, options.config);
103
+ // Incremental index update (avoids loading all incidents)
104
+ await updateIndexIncremental(finalIncident, options.config);
105
+ // Update keyword index
106
+ await updateKeywordIndex(finalIncident, options.config);
107
+ return {
108
+ incident_id: finalIncident.incident_id,
109
+ file_path: filepath
110
+ };
111
+ });
112
+ }
113
+ /**
114
+ * Validate incident ID format to prevent path traversal attacks
115
+ * Supports both formats: INC_YYYYMMDD_HHMMSS_xxxx and INC_CATEGORY_YYYYMMDD_HHMMSS_xxxx
116
+ */
117
+ function isValidIncidentId(incident_id) {
118
+ return /^INC_[\w\-]+$/.test(incident_id);
119
+ }
120
+ /**
121
+ * Load an incident by ID
122
+ */
123
+ async function loadIncident(incident_id, config) {
124
+ return (0, logger_1.traced)('storage:loadIncident', { id: incident_id }, async () => {
125
+ // Validate incident ID to prevent path traversal
126
+ if (!isValidIncidentId(incident_id)) {
127
+ throw new Error(`Invalid incident ID format: ${incident_id}`);
128
+ }
129
+ const paths = (0, config_1.getMemoryPaths)(config);
130
+ const filename = `${incident_id}.json`;
131
+ const filepath = path_1.default.join(paths.incidents, filename);
132
+ try {
133
+ const content = await promises_1.default.readFile(filepath, 'utf-8');
134
+ return JSON.parse(content);
135
+ }
136
+ catch (error) {
137
+ if (error.code === 'ENOENT') {
138
+ return null; // File not found
139
+ }
140
+ throw error;
141
+ }
142
+ });
143
+ }
144
+ /**
145
+ * Load all incidents with batched I/O (max 50 concurrent reads)
146
+ */
147
+ async function loadAllIncidents(config) {
148
+ const paths = (0, config_1.getMemoryPaths)(config);
149
+ const INCIDENTS_DIR = paths.incidents;
150
+ try {
151
+ const files = await promises_1.default.readdir(INCIDENTS_DIR);
152
+ const jsonFiles = files.filter(f => f.endsWith('.json'));
153
+ const incidents = [];
154
+ const BATCH_SIZE = 50;
155
+ for (let i = 0; i < jsonFiles.length; i += BATCH_SIZE) {
156
+ const batch = jsonFiles.slice(i, i + BATCH_SIZE);
157
+ const results = await Promise.all(batch.map(async (file) => {
158
+ try {
159
+ const filepath = path_1.default.join(INCIDENTS_DIR, file);
160
+ const content = await promises_1.default.readFile(filepath, 'utf-8');
161
+ return JSON.parse(content);
162
+ }
163
+ catch {
164
+ return null;
165
+ }
166
+ }));
167
+ incidents.push(...results.filter((r) => r !== null));
168
+ }
169
+ return incidents;
170
+ }
171
+ catch (error) {
172
+ if (error.code === 'ENOENT') {
173
+ return []; // Directory doesn't exist yet
174
+ }
175
+ throw error;
176
+ }
177
+ }
178
+ /**
179
+ * Store a pattern
180
+ */
181
+ async function storePattern(pattern, config) {
182
+ const paths = (0, config_1.getMemoryPaths)(config);
183
+ await promises_1.default.mkdir(paths.patterns, { recursive: true });
184
+ const filename = `${pattern.pattern_id}.json`;
185
+ const filepath = path_1.default.join(paths.patterns, filename);
186
+ await promises_1.default.writeFile(filepath, JSON.stringify(pattern, null, 2), 'utf-8');
187
+ console.log(`✅ Pattern stored: ${pattern.pattern_id}`);
188
+ return filepath;
189
+ }
190
+ /**
191
+ * Validate pattern ID format to prevent path traversal attacks
192
+ */
193
+ function isValidPatternId(pattern_id) {
194
+ // Only allow alphanumeric and underscores (PTN_CATEGORY_NAME format)
195
+ return /^PTN_[\w]+$/.test(pattern_id);
196
+ }
197
+ /**
198
+ * Load a pattern by ID
199
+ */
200
+ async function loadPattern(pattern_id, config) {
201
+ // Validate pattern ID to prevent path traversal
202
+ if (!isValidPatternId(pattern_id)) {
203
+ throw new Error(`Invalid pattern ID format: ${pattern_id}`);
204
+ }
205
+ const paths = (0, config_1.getMemoryPaths)(config);
206
+ const filename = `${pattern_id}.json`;
207
+ const filepath = path_1.default.join(paths.patterns, filename);
208
+ try {
209
+ const content = await promises_1.default.readFile(filepath, 'utf-8');
210
+ return JSON.parse(content);
211
+ }
212
+ catch (error) {
213
+ if (error.code === 'ENOENT') {
214
+ return null;
215
+ }
216
+ throw error;
217
+ }
218
+ }
219
+ /**
220
+ * Load all patterns with batched I/O (max 50 concurrent reads)
221
+ */
222
+ async function loadAllPatterns(config) {
223
+ const paths = (0, config_1.getMemoryPaths)(config);
224
+ const PATTERNS_DIR = paths.patterns;
225
+ try {
226
+ const files = await promises_1.default.readdir(PATTERNS_DIR);
227
+ const jsonFiles = files.filter(f => f.endsWith('.json'));
228
+ const patterns = [];
229
+ const BATCH_SIZE = 50;
230
+ for (let i = 0; i < jsonFiles.length; i += BATCH_SIZE) {
231
+ const batch = jsonFiles.slice(i, i + BATCH_SIZE);
232
+ const results = await Promise.all(batch.map(async (file) => {
233
+ try {
234
+ const filepath = path_1.default.join(PATTERNS_DIR, file);
235
+ const content = await promises_1.default.readFile(filepath, 'utf-8');
236
+ return JSON.parse(content);
237
+ }
238
+ catch {
239
+ return null;
240
+ }
241
+ }));
242
+ patterns.push(...results.filter((r) => r !== null));
243
+ }
244
+ return patterns;
245
+ }
246
+ catch (error) {
247
+ if (error.code === 'ENOENT') {
248
+ return [];
249
+ }
250
+ throw error;
251
+ }
252
+ }
253
+ /**
254
+ * Validate incident structure
255
+ */
256
+ function validateIncident(incident) {
257
+ const errors = [];
258
+ const warnings = [];
259
+ // Required fields
260
+ if (!incident.incident_id)
261
+ errors.push('Missing incident_id');
262
+ if (!incident.timestamp)
263
+ errors.push('Missing timestamp');
264
+ if (!incident.symptom)
265
+ errors.push('Missing symptom');
266
+ if (!incident.root_cause)
267
+ errors.push('Missing root_cause');
268
+ if (!incident.fix)
269
+ errors.push('Missing fix');
270
+ // Check completeness
271
+ if (incident.root_cause && !incident.root_cause.confidence) {
272
+ warnings.push('Root cause missing confidence score');
273
+ }
274
+ if (incident.verification && incident.verification.status === 'unverified') {
275
+ warnings.push('Incident not verified');
276
+ }
277
+ if (!incident.quality_gates || !incident.quality_gates.guardian_validated) {
278
+ warnings.push('Not validated by guardian (expected in current system)');
279
+ }
280
+ return {
281
+ valid: errors.length === 0,
282
+ errors,
283
+ warnings
284
+ };
285
+ }
286
+ /**
287
+ * Generate incident ID with optional category prefix for self-documenting filenames
288
+ *
289
+ * Without category: INC_20250215_143022_a1b2
290
+ * With category: INC_API_20250215_143022_a1b2
291
+ */
292
+ function generateIncidentId(category) {
293
+ const now = new Date();
294
+ const dateStr = now.toISOString().slice(0, 10).replace(/-/g, '');
295
+ const timeStr = now.toISOString().slice(11, 19).replace(/:/g, '');
296
+ const random = Math.random().toString(36).substring(2, 6);
297
+ if (category) {
298
+ const cleanCat = category.toUpperCase().replace(/[^A-Z0-9]/g, '').slice(0, 12);
299
+ return `INC_${cleanCat}_${dateStr}_${timeStr}_${random}`;
300
+ }
301
+ return `INC_${dateStr}_${timeStr}_${random}`;
302
+ }
303
+ /**
304
+ * Generate pattern ID
305
+ */
306
+ function generatePatternId(category, name) {
307
+ const cleanCategory = category.toUpperCase().replace(/[^A-Z]/g, '_');
308
+ const cleanName = name.toUpperCase().replace(/[^A-Z]/g, '_');
309
+ return `PTN_${cleanCategory}_${cleanName}`;
310
+ }
311
+ /**
312
+ * Get memory statistics
313
+ */
314
+ async function getMemoryStats(config) {
315
+ const incidents = await loadAllIncidents(config);
316
+ const patterns = await loadAllPatterns(config);
317
+ const timestamps = incidents.map(i => i.timestamp).filter(Boolean);
318
+ const oldest = timestamps.length > 0 ? Math.min(...timestamps) : 0;
319
+ const newest = timestamps.length > 0 ? Math.max(...timestamps) : 0;
320
+ // Estimate disk usage (rough)
321
+ const diskUsage = incidents.length * 1 + // ~1KB per incident
322
+ patterns.length * 2; // ~2KB per pattern
323
+ return {
324
+ total_incidents: incidents.length,
325
+ total_patterns: patterns.length,
326
+ oldest_incident: oldest,
327
+ newest_incident: newest,
328
+ disk_usage_kb: diskUsage
329
+ };
330
+ }
331
+ /**
332
+ * Truncate text to max length with ellipsis
333
+ */
334
+ function truncate(text, maxLen) {
335
+ if (!text)
336
+ return '';
337
+ if (text.length <= maxLen)
338
+ return text;
339
+ return text.substring(0, maxLen - 3) + '...';
340
+ }
341
+ /**
342
+ * Generate minimal incident summary (~100 tokens)
343
+ */
344
+ function generateIncidentSummary(incident) {
345
+ return {
346
+ incident_id: incident.incident_id,
347
+ symptom_preview: truncate(incident.symptom, 80),
348
+ root_cause_preview: truncate(incident.root_cause?.description, 100),
349
+ fix_preview: truncate(incident.fix?.approach, 80),
350
+ category: incident.root_cause?.category || 'unknown',
351
+ confidence: incident.root_cause?.confidence || 0,
352
+ quality: incident.completeness?.quality_score || 0,
353
+ };
354
+ }
355
+ /**
356
+ * Convert full incident to compact format (~200 tokens)
357
+ *
358
+ * Uses short keys to minimize token usage in LLM context.
359
+ * Preserves essential information for debugging assistance.
360
+ */
361
+ function toCompactIncident(incident) {
362
+ return {
363
+ id: incident.incident_id,
364
+ ts: incident.timestamp,
365
+ sym: truncate(incident.symptom, 80),
366
+ rc: {
367
+ d: truncate(incident.root_cause?.description, 100),
368
+ cat: incident.root_cause?.category || 'unknown',
369
+ conf: incident.root_cause?.confidence || 0,
370
+ },
371
+ fix: {
372
+ a: truncate(incident.fix?.approach, 80),
373
+ n: incident.fix?.changes?.length || 0,
374
+ },
375
+ v: incident.verification?.status === 'verified'
376
+ ? 'V'
377
+ : incident.verification?.status === 'partial'
378
+ ? 'P'
379
+ : 'U',
380
+ t: (incident.tags || []).slice(0, 5),
381
+ q: incident.completeness?.quality_score || 0,
382
+ sim: incident.similarity_score,
383
+ };
384
+ }
385
+ /**
386
+ * Convert full pattern to compact format (~120 tokens)
387
+ */
388
+ function toCompactPattern(pattern) {
389
+ return {
390
+ id: pattern.pattern_id,
391
+ n: pattern.usage_history?.total_uses || 1,
392
+ desc: truncate(pattern.description, 100),
393
+ sig: pattern.detection_signature.slice(0, 5),
394
+ fix: truncate(pattern.solution_template, 150),
395
+ sr: pattern.success_rate,
396
+ cat: pattern.tags[0] || 'general',
397
+ t: pattern.tags.slice(0, 5),
398
+ last: pattern.last_used || Date.now(),
399
+ };
400
+ }
401
+ /**
402
+ * Batch convert incidents to compact format
403
+ */
404
+ function toCompactIncidents(incidents) {
405
+ return incidents.map(toCompactIncident);
406
+ }
407
+ /**
408
+ * Batch convert patterns to compact format
409
+ */
410
+ function toCompactPatterns(patterns) {
411
+ return patterns.map(toCompactPattern);
412
+ }
413
+ /**
414
+ * Estimate tokens for a compact incident
415
+ */
416
+ function estimateCompactIncidentTokens(incident) {
417
+ // Rough estimate: JSON structure + field names + values
418
+ const json = JSON.stringify(incident);
419
+ return Math.ceil(json.length / 4);
420
+ }
421
+ /**
422
+ * Estimate tokens for a compact pattern
423
+ */
424
+ function estimateCompactPatternTokens(pattern) {
425
+ const json = JSON.stringify(pattern);
426
+ return Math.ceil(json.length / 4);
427
+ }
428
+ /**
429
+ * Enforce token budget on results
430
+ *
431
+ * Returns limited incidents and patterns that fit within budget.
432
+ * Patterns get 30% budget, incidents get 60%, metadata gets 10%.
433
+ */
434
+ function enforceTokenBudget(incidents, patterns, budget = 2500) {
435
+ const patternBudget = Math.floor(budget * 0.3);
436
+ const incidentBudget = Math.floor(budget * 0.6);
437
+ // Estimate tokens per item (using averages from DEFAULT_TOKEN_BUDGET)
438
+ const tokensPerPattern = 120;
439
+ const tokensPerIncident = 200;
440
+ const maxPatterns = Math.floor(patternBudget / tokensPerPattern);
441
+ const maxIncidents = Math.floor(incidentBudget / tokensPerIncident);
442
+ const limitedPatterns = patterns.slice(0, maxPatterns);
443
+ const limitedIncidents = incidents.slice(0, maxIncidents);
444
+ const tokensUsed = limitedPatterns.length * tokensPerPattern +
445
+ limitedIncidents.length * tokensPerIncident +
446
+ Math.floor(budget * 0.1); // metadata overhead
447
+ return {
448
+ limitedPatterns,
449
+ limitedIncidents,
450
+ tokensUsed,
451
+ truncated: {
452
+ incidents: incidents.length - limitedIncidents.length,
453
+ patterns: patterns.length - limitedPatterns.length,
454
+ },
455
+ };
456
+ }
457
+ /**
458
+ * Load incidents and return compact versions
459
+ */
460
+ async function loadCompactIncidents(config) {
461
+ const incidents = await loadAllIncidents(config);
462
+ return toCompactIncidents(incidents);
463
+ }
464
+ /**
465
+ * Load patterns and return compact versions
466
+ */
467
+ async function loadCompactPatterns(config) {
468
+ const patterns = await loadAllPatterns(config);
469
+ return toCompactPatterns(patterns);
470
+ }
471
+ // ============================================================================
472
+ // JSONL APPEND LOG - Fast full-text search without loading individual files
473
+ // ============================================================================
474
+ /**
475
+ * Append incident entry to JSONL log for fast search
476
+ */
477
+ async function appendToIncidentLog(incident, config) {
478
+ const paths = (0, config_1.getMemoryPaths)(config);
479
+ const logPath = path_1.default.join(paths.root, 'incidents.jsonl');
480
+ const entry = {
481
+ incident_id: incident.incident_id,
482
+ timestamp: incident.timestamp,
483
+ symptom: incident.symptom,
484
+ category: incident.root_cause?.category || 'unknown',
485
+ tags: incident.tags || [],
486
+ quality_score: incident.completeness?.quality_score || 0,
487
+ verification_status: incident.verification?.status || 'unverified',
488
+ files_changed: incident.files_changed || [],
489
+ };
490
+ try {
491
+ await promises_1.default.appendFile(logPath, JSON.stringify(entry) + '\n', 'utf-8');
492
+ }
493
+ catch {
494
+ // Log file doesn't exist yet, create it
495
+ await promises_1.default.mkdir(paths.root, { recursive: true });
496
+ await promises_1.default.writeFile(logPath, JSON.stringify(entry) + '\n', 'utf-8');
497
+ }
498
+ }
499
+ /**
500
+ * Search JSONL log for fast text matching (avoids loading all incident files)
501
+ */
502
+ async function searchIncidentLog(query, config) {
503
+ const paths = (0, config_1.getMemoryPaths)(config);
504
+ const logPath = path_1.default.join(paths.root, 'incidents.jsonl');
505
+ try {
506
+ const content = await promises_1.default.readFile(logPath, 'utf-8');
507
+ const queryLower = query.toLowerCase();
508
+ return content
509
+ .split('\n')
510
+ .filter(line => line.trim())
511
+ .map(line => {
512
+ try {
513
+ return JSON.parse(line);
514
+ }
515
+ catch {
516
+ return null;
517
+ }
518
+ })
519
+ .filter((entry) => {
520
+ if (!entry)
521
+ return false;
522
+ return (entry.incident_id.toLowerCase().includes(queryLower) ||
523
+ entry.symptom.toLowerCase().includes(queryLower) ||
524
+ entry.category.toLowerCase().includes(queryLower) ||
525
+ entry.tags.some(t => t.toLowerCase().includes(queryLower)));
526
+ });
527
+ }
528
+ catch {
529
+ return [];
530
+ }
531
+ }
532
+ // ============================================================================
533
+ // MEMORY INDEX - O(1) lookups by category, tag, file
534
+ // ============================================================================
535
+ /**
536
+ * Build or rebuild the memory index from all incidents
537
+ */
538
+ async function rebuildIndex(config) {
539
+ return (0, logger_1.traced)('storage:rebuildIndex', undefined, async () => {
540
+ const incidents = await loadAllIncidents(config);
541
+ const paths = (0, config_1.getMemoryPaths)(config);
542
+ const index = {
543
+ version: 1,
544
+ last_updated: Date.now(),
545
+ stats: {
546
+ total_incidents: incidents.length,
547
+ total_patterns: 0,
548
+ categories: {},
549
+ tags: {},
550
+ quality_distribution: { excellent: 0, good: 0, fair: 0 },
551
+ oldest_timestamp: Infinity,
552
+ newest_timestamp: 0,
553
+ },
554
+ by_category: {},
555
+ by_tag: {},
556
+ by_file: {},
557
+ by_quality: { excellent: [], good: [], fair: [] },
558
+ recent: [],
559
+ };
560
+ // Count patterns
561
+ try {
562
+ const patternFiles = await promises_1.default.readdir(paths.patterns);
563
+ index.stats.total_patterns = patternFiles.filter(f => f.endsWith('.json')).length;
564
+ }
565
+ catch { /* patterns dir may not exist */ }
566
+ // Build indexes
567
+ for (const incident of incidents) {
568
+ const id = incident.incident_id;
569
+ const cat = incident.root_cause?.category || 'unknown';
570
+ const qs = incident.completeness?.quality_score || 0;
571
+ // Category index
572
+ if (!index.by_category[cat])
573
+ index.by_category[cat] = [];
574
+ index.by_category[cat].push(id);
575
+ index.stats.categories[cat] = (index.stats.categories[cat] || 0) + 1;
576
+ // Tag index
577
+ for (const tag of (incident.tags || [])) {
578
+ if (!index.by_tag[tag])
579
+ index.by_tag[tag] = [];
580
+ index.by_tag[tag].push(id);
581
+ index.stats.tags[tag] = (index.stats.tags[tag] || 0) + 1;
582
+ }
583
+ // File index
584
+ for (const file of (incident.files_changed || [])) {
585
+ if (!index.by_file[file])
586
+ index.by_file[file] = [];
587
+ index.by_file[file].push(id);
588
+ }
589
+ // Quality distribution
590
+ if (qs >= 0.75) {
591
+ index.by_quality.excellent.push(id);
592
+ index.stats.quality_distribution.excellent++;
593
+ }
594
+ else if (qs >= 0.5) {
595
+ index.by_quality.good.push(id);
596
+ index.stats.quality_distribution.good++;
597
+ }
598
+ else {
599
+ index.by_quality.fair.push(id);
600
+ index.stats.quality_distribution.fair++;
601
+ }
602
+ // Timestamps
603
+ if (incident.timestamp < index.stats.oldest_timestamp) {
604
+ index.stats.oldest_timestamp = incident.timestamp;
605
+ }
606
+ if (incident.timestamp > index.stats.newest_timestamp) {
607
+ index.stats.newest_timestamp = incident.timestamp;
608
+ }
609
+ }
610
+ // Fix infinity if no incidents
611
+ if (index.stats.oldest_timestamp === Infinity)
612
+ index.stats.oldest_timestamp = 0;
613
+ // Recent (newest first, max 20)
614
+ index.recent = incidents
615
+ .sort((a, b) => b.timestamp - a.timestamp)
616
+ .slice(0, 20)
617
+ .map(i => i.incident_id);
618
+ // Write index
619
+ const indexPath = path_1.default.join(paths.root, 'index.json');
620
+ await promises_1.default.mkdir(paths.root, { recursive: true });
621
+ await promises_1.default.writeFile(indexPath, JSON.stringify(index, null, 2), 'utf-8');
622
+ return index;
623
+ });
624
+ }
625
+ /**
626
+ * Load memory index (fast, no incident file reads needed)
627
+ */
628
+ async function loadIndex(config) {
629
+ const paths = (0, config_1.getMemoryPaths)(config);
630
+ const indexPath = path_1.default.join(paths.root, 'index.json');
631
+ try {
632
+ const content = await promises_1.default.readFile(indexPath, 'utf-8');
633
+ return JSON.parse(content);
634
+ }
635
+ catch {
636
+ return null;
637
+ }
638
+ }
639
+ // ============================================================================
640
+ // MEMORY SUMMARY - Compressed context for LLM cold starts
641
+ // ============================================================================
642
+ /**
643
+ * Build a compact MEMORY_SUMMARY.md for LLM context injection
644
+ * Stays under 150 lines to fit in context windows
645
+ */
646
+ async function buildMemorySummary(config) {
647
+ const index = await loadIndex(config) || await rebuildIndex(config);
648
+ const patterns = await loadAllPatterns(config);
649
+ const lines = [];
650
+ lines.push('# Debugging Memory Summary');
651
+ lines.push(`> ${index.stats.total_incidents} incidents | ${index.stats.total_patterns} patterns`);
652
+ lines.push(`> Quality: ${index.stats.quality_distribution.excellent} excellent, ${index.stats.quality_distribution.good} good, ${index.stats.quality_distribution.fair} fair`);
653
+ lines.push('');
654
+ // Top categories
655
+ const sortedCats = Object.entries(index.stats.categories)
656
+ .sort(([, a], [, b]) => b - a)
657
+ .slice(0, 10);
658
+ if (sortedCats.length > 0) {
659
+ lines.push('## Categories');
660
+ for (const [cat, count] of sortedCats) {
661
+ lines.push(`- **${cat}**: ${count} incidents`);
662
+ }
663
+ lines.push('');
664
+ }
665
+ // Top tags
666
+ const sortedTags = Object.entries(index.stats.tags)
667
+ .sort(([, a], [, b]) => b - a)
668
+ .slice(0, 15);
669
+ if (sortedTags.length > 0) {
670
+ lines.push('## Common Tags');
671
+ lines.push(sortedTags.map(([tag, count]) => `\`${tag}\`(${count})`).join(', '));
672
+ lines.push('');
673
+ }
674
+ // Patterns
675
+ if (patterns.length > 0) {
676
+ lines.push('## Known Patterns');
677
+ for (const p of patterns.slice(0, 10)) {
678
+ lines.push(`- **${p.name}** (${(p.success_rate * 100).toFixed(0)}% success) - ${p.description.slice(0, 80)}`);
679
+ }
680
+ lines.push('');
681
+ }
682
+ // Recent incidents (from index, no file reads)
683
+ if (index.recent.length > 0) {
684
+ lines.push('## Recent Incident IDs');
685
+ lines.push(index.recent.slice(0, 10).map(id => `\`${id}\``).join(', '));
686
+ lines.push('');
687
+ }
688
+ // Hot files
689
+ const sortedFiles = Object.entries(index.by_file)
690
+ .sort(([, a], [, b]) => b.length - a.length)
691
+ .slice(0, 10);
692
+ if (sortedFiles.length > 0) {
693
+ lines.push('## Frequently Affected Files');
694
+ for (const [file, ids] of sortedFiles) {
695
+ lines.push(`- \`${file}\` (${ids.length} incidents)`);
696
+ }
697
+ lines.push('');
698
+ }
699
+ lines.push(`*Updated: ${new Date().toISOString().slice(0, 19)}*`);
700
+ const content = lines.join('\n');
701
+ // Write to disk
702
+ const paths = (0, config_1.getMemoryPaths)(config);
703
+ const summaryPath = path_1.default.join(paths.root, 'MEMORY_SUMMARY.md');
704
+ await promises_1.default.mkdir(paths.root, { recursive: true });
705
+ await promises_1.default.writeFile(summaryPath, content, 'utf-8');
706
+ return content;
707
+ }
708
+ /**
709
+ * Archive incidents older than maxAge days, keeping at most maxActive incidents
710
+ */
711
+ async function archiveOldIncidents(options = {}, config) {
712
+ const { maxActive = 200, maxAgeDays = 180, dryRun = false } = options;
713
+ const paths = (0, config_1.getMemoryPaths)(config);
714
+ const archivePath = path_1.default.join(paths.root, 'archive');
715
+ const incidents = await loadAllIncidents(config);
716
+ const sorted = incidents.sort((a, b) => b.timestamp - a.timestamp);
717
+ const cutoff = Date.now() - maxAgeDays * 24 * 60 * 60 * 1000;
718
+ const toArchive = [];
719
+ for (let i = 0; i < sorted.length; i++) {
720
+ if (i >= maxActive || sorted[i].timestamp < cutoff) {
721
+ toArchive.push(sorted[i]);
722
+ }
723
+ }
724
+ if (toArchive.length === 0) {
725
+ return { archived: [], kept: incidents.length };
726
+ }
727
+ console.log(`📦 ${dryRun ? '[DRY RUN] Would archive' : 'Archiving'} ${toArchive.length} incidents`);
728
+ if (!dryRun) {
729
+ // Create archive directory with timestamp
730
+ const archiveDir = path_1.default.join(archivePath, `archive_${Date.now()}`);
731
+ await promises_1.default.mkdir(archiveDir, { recursive: true });
732
+ // Move incidents to archive
733
+ for (const incident of toArchive) {
734
+ const srcPath = path_1.default.join(paths.incidents, `${incident.incident_id}.json`);
735
+ const destPath = path_1.default.join(archiveDir, `${incident.incident_id}.json`);
736
+ try {
737
+ await promises_1.default.rename(srcPath, destPath);
738
+ }
739
+ catch {
740
+ // File may already be archived or missing
741
+ }
742
+ }
743
+ // Write manifest
744
+ const manifest = {
745
+ archived_at: Date.now(),
746
+ incident_count: toArchive.length,
747
+ oldest_timestamp: Math.min(...toArchive.map(i => i.timestamp)),
748
+ newest_timestamp: Math.max(...toArchive.map(i => i.timestamp)),
749
+ reason: `Exceeded ${maxActive} active or older than ${maxAgeDays} days`,
750
+ };
751
+ await promises_1.default.writeFile(path_1.default.join(archiveDir, 'manifest.json'), JSON.stringify(manifest, null, 2), 'utf-8');
752
+ // Rebuild index and summary
753
+ await rebuildIndex(config);
754
+ await buildMemorySummary(config);
755
+ }
756
+ return {
757
+ archived: toArchive.map(i => i.incident_id),
758
+ kept: incidents.length - toArchive.length,
759
+ };
760
+ }
761
+ // ============================================================================
762
+ // CONTEXT COMPRESSION - Compress memory for LLM context injection
763
+ // ============================================================================
764
+ /**
765
+ * Generate compressed context string optimized for LLM consumption
766
+ * Fits within token budget while maximizing information density
767
+ */
768
+ function compressContext(incidents, patterns, budget = 2500) {
769
+ const { limitedIncidents, limitedPatterns, tokensUsed, truncated } = enforceTokenBudget(incidents, patterns, budget);
770
+ const sections = [];
771
+ // Header
772
+ sections.push(`[Memory: ${limitedIncidents.length}i/${limitedPatterns.length}p, ~${tokensUsed}tok]`);
773
+ // Patterns first (highest value)
774
+ if (limitedPatterns.length > 0) {
775
+ sections.push('PATTERNS:');
776
+ for (const p of limitedPatterns) {
777
+ sections.push(` ${p.id}|${p.cat}|${(p.sr * 100).toFixed(0)}%|${p.desc}`);
778
+ sections.push(` fix: ${p.fix}`);
779
+ }
780
+ }
781
+ // Incidents
782
+ if (limitedIncidents.length > 0) {
783
+ sections.push('INCIDENTS:');
784
+ for (const inc of limitedIncidents) {
785
+ const vLabel = inc.v === 'V' ? 'verified' : inc.v === 'P' ? 'partial' : 'unverified';
786
+ sections.push(` ${inc.id}|${inc.rc.cat}|${(inc.rc.conf * 100).toFixed(0)}%|${vLabel}`);
787
+ sections.push(` sym: ${inc.sym}`);
788
+ sections.push(` fix: ${inc.fix.a}`);
789
+ }
790
+ }
791
+ // Truncation notice
792
+ if (truncated.incidents > 0 || truncated.patterns > 0) {
793
+ sections.push(`[+${truncated.incidents}i/${truncated.patterns}p omitted]`);
794
+ }
795
+ return sections.join('\n');
796
+ }
797
+ // ============================================================================
798
+ // KEYWORD INDEX - Inverted index for scalable retrieval
799
+ // ============================================================================
800
+ /**
801
+ * Extract keywords from an incident for indexing
802
+ */
803
+ function extractIndexKeywords(incident) {
804
+ const stopWords = new Set([
805
+ 'the', 'a', 'an', 'and', 'or', 'but', 'in', 'on', 'at', 'to', 'for',
806
+ 'of', 'with', 'is', 'are', 'was', 'were', 'be', 'been', 'being',
807
+ 'have', 'has', 'had', 'do', 'does', 'did', 'will', 'would', 'should',
808
+ 'can', 'could', 'may', 'might', 'must', 'this', 'that', 'these', 'those',
809
+ 'not', 'from', 'it', 'its', 'when', 'where', 'what', 'which', 'how',
810
+ ]);
811
+ const text = [
812
+ incident.symptom || '',
813
+ incident.root_cause?.description || '',
814
+ incident.root_cause?.category || '',
815
+ incident.fix?.approach || '',
816
+ ...(incident.tags || []),
817
+ ].join(' ');
818
+ return [...new Set(text
819
+ .toLowerCase()
820
+ .replace(/[^\w\s]/g, ' ')
821
+ .split(/\s+/)
822
+ .filter(w => w.length > 2 && !stopWords.has(w)))];
823
+ }
824
+ /**
825
+ * Update keyword index with a new incident (incremental, no full rebuild)
826
+ */
827
+ async function updateKeywordIndex(incident, config) {
828
+ const index = await loadKeywordIndex(config) || {
829
+ version: 1,
830
+ last_updated: Date.now(),
831
+ keywords: {},
832
+ total_incidents: 0,
833
+ total_keywords: 0,
834
+ };
835
+ const keywords = extractIndexKeywords(incident);
836
+ const id = incident.incident_id;
837
+ for (const kw of keywords) {
838
+ if (!index.keywords[kw])
839
+ index.keywords[kw] = [];
840
+ if (!index.keywords[kw].includes(id)) {
841
+ index.keywords[kw].push(id);
842
+ }
843
+ }
844
+ index.last_updated = Date.now();
845
+ index.total_incidents++;
846
+ index.total_keywords = Object.keys(index.keywords).length;
847
+ const paths = (0, config_1.getMemoryPaths)(config);
848
+ const indexPath = path_1.default.join(paths.root, 'keyword-index.json');
849
+ await promises_1.default.mkdir(paths.root, { recursive: true });
850
+ await promises_1.default.writeFile(indexPath, JSON.stringify(index), 'utf-8');
851
+ }
852
+ /**
853
+ * Load keyword index from disk
854
+ */
855
+ async function loadKeywordIndex(config) {
856
+ const paths = (0, config_1.getMemoryPaths)(config);
857
+ const indexPath = path_1.default.join(paths.root, 'keyword-index.json');
858
+ try {
859
+ const content = await promises_1.default.readFile(indexPath, 'utf-8');
860
+ return JSON.parse(content);
861
+ }
862
+ catch {
863
+ return null;
864
+ }
865
+ }
866
+ /**
867
+ * Find candidate incident IDs by keyword intersection
868
+ */
869
+ function findCandidatesByKeyword(queryWords, index, maxCandidates = 20) {
870
+ const candidateScores = new Map();
871
+ for (const word of queryWords) {
872
+ const ids = index.keywords[word] || [];
873
+ for (const id of ids) {
874
+ candidateScores.set(id, (candidateScores.get(id) || 0) + 1);
875
+ }
876
+ }
877
+ // Sort by number of keyword hits (descending)
878
+ return [...candidateScores.entries()]
879
+ .sort((a, b) => b[1] - a[1])
880
+ .slice(0, maxCandidates)
881
+ .map(([id]) => id);
882
+ }
883
+ /**
884
+ * Rebuild keyword index from scratch
885
+ */
886
+ async function rebuildKeywordIndex(config) {
887
+ const incidents = await loadAllIncidents(config);
888
+ const index = {
889
+ version: 1,
890
+ last_updated: Date.now(),
891
+ keywords: {},
892
+ total_incidents: incidents.length,
893
+ total_keywords: 0,
894
+ };
895
+ for (const incident of incidents) {
896
+ const keywords = extractIndexKeywords(incident);
897
+ const id = incident.incident_id;
898
+ for (const kw of keywords) {
899
+ if (!index.keywords[kw])
900
+ index.keywords[kw] = [];
901
+ if (!index.keywords[kw].includes(id)) {
902
+ index.keywords[kw].push(id);
903
+ }
904
+ }
905
+ }
906
+ index.total_keywords = Object.keys(index.keywords).length;
907
+ const paths = (0, config_1.getMemoryPaths)(config);
908
+ const indexPath = path_1.default.join(paths.root, 'keyword-index.json');
909
+ await promises_1.default.mkdir(paths.root, { recursive: true });
910
+ await promises_1.default.writeFile(indexPath, JSON.stringify(index), 'utf-8');
911
+ return index;
912
+ }
913
+ // ============================================================================
914
+ // INCREMENTAL INDEX UPDATE - Avoid full rebuilds on every store
915
+ // ============================================================================
916
+ /**
917
+ * Update index incrementally with a new incident (avoids full loadAllIncidents)
918
+ */
919
+ async function updateIndexIncremental(incident, config) {
920
+ const paths = (0, config_1.getMemoryPaths)(config);
921
+ const indexPath = path_1.default.join(paths.root, 'index.json');
922
+ let index;
923
+ try {
924
+ const content = await promises_1.default.readFile(indexPath, 'utf-8');
925
+ index = JSON.parse(content);
926
+ }
927
+ catch {
928
+ // No index yet — do a full rebuild
929
+ await rebuildIndex(config);
930
+ return;
931
+ }
932
+ const id = incident.incident_id;
933
+ const cat = incident.root_cause?.category || 'unknown';
934
+ const qs = incident.completeness?.quality_score || 0;
935
+ // Stats
936
+ index.stats.total_incidents++;
937
+ index.stats.categories[cat] = (index.stats.categories[cat] || 0) + 1;
938
+ for (const tag of (incident.tags || [])) {
939
+ index.stats.tags[tag] = (index.stats.tags[tag] || 0) + 1;
940
+ }
941
+ if (incident.timestamp < index.stats.oldest_timestamp || index.stats.oldest_timestamp === 0) {
942
+ index.stats.oldest_timestamp = incident.timestamp;
943
+ }
944
+ if (incident.timestamp > index.stats.newest_timestamp) {
945
+ index.stats.newest_timestamp = incident.timestamp;
946
+ }
947
+ // Category index
948
+ if (!index.by_category[cat])
949
+ index.by_category[cat] = [];
950
+ index.by_category[cat].push(id);
951
+ // Tag index
952
+ for (const tag of (incident.tags || [])) {
953
+ if (!index.by_tag[tag])
954
+ index.by_tag[tag] = [];
955
+ index.by_tag[tag].push(id);
956
+ }
957
+ // File index
958
+ for (const file of (incident.files_changed || [])) {
959
+ if (!index.by_file[file])
960
+ index.by_file[file] = [];
961
+ index.by_file[file].push(id);
962
+ }
963
+ // Quality
964
+ if (qs >= 0.75) {
965
+ index.by_quality.excellent.push(id);
966
+ index.stats.quality_distribution.excellent++;
967
+ }
968
+ else if (qs >= 0.5) {
969
+ index.by_quality.good.push(id);
970
+ index.stats.quality_distribution.good++;
971
+ }
972
+ else {
973
+ index.by_quality.fair.push(id);
974
+ index.stats.quality_distribution.fair++;
975
+ }
976
+ // Recent
977
+ index.recent.unshift(id);
978
+ if (index.recent.length > 20)
979
+ index.recent = index.recent.slice(0, 20);
980
+ index.last_updated = Date.now();
981
+ await promises_1.default.writeFile(indexPath, JSON.stringify(index, null, 2), 'utf-8');
982
+ }
983
+ // ============================================================================
984
+ // OUTCOME TRACKING - Did the suggested fix actually work?
985
+ // ============================================================================
986
+ /**
987
+ * Record whether a verdict suggestion actually worked
988
+ */
989
+ async function recordOutcome(outcome, config) {
990
+ const paths = (0, config_1.getMemoryPaths)(config);
991
+ const outcomesPath = path_1.default.join(paths.root, 'outcomes.jsonl');
992
+ try {
993
+ await promises_1.default.appendFile(outcomesPath, JSON.stringify(outcome) + '\n', 'utf-8');
994
+ }
995
+ catch {
996
+ await promises_1.default.mkdir(paths.root, { recursive: true });
997
+ await promises_1.default.writeFile(outcomesPath, JSON.stringify(outcome) + '\n', 'utf-8');
998
+ }
999
+ }
1000
+ /**
1001
+ * Load all recorded outcomes
1002
+ */
1003
+ async function loadOutcomes(config) {
1004
+ const paths = (0, config_1.getMemoryPaths)(config);
1005
+ const outcomesPath = path_1.default.join(paths.root, 'outcomes.jsonl');
1006
+ try {
1007
+ const content = await promises_1.default.readFile(outcomesPath, 'utf-8');
1008
+ return content
1009
+ .split('\n')
1010
+ .filter(line => line.trim())
1011
+ .map(line => {
1012
+ try {
1013
+ return JSON.parse(line);
1014
+ }
1015
+ catch {
1016
+ return null;
1017
+ }
1018
+ })
1019
+ .filter((o) => o !== null);
1020
+ }
1021
+ catch {
1022
+ return [];
1023
+ }
1024
+ }
1025
+ /**
1026
+ * Get outcome statistics for a specific incident
1027
+ */
1028
+ async function getOutcomeStats(incident_id, config) {
1029
+ const outcomes = await loadOutcomes(config);
1030
+ const relevant = outcomes.filter(o => o.incident_id === incident_id);
1031
+ return {
1032
+ worked: relevant.filter(o => o.outcome === 'worked').length,
1033
+ failed: relevant.filter(o => o.outcome === 'failed').length,
1034
+ modified: relevant.filter(o => o.outcome === 'modified').length,
1035
+ };
1036
+ }
1037
+ //# sourceMappingURL=storage.js.map