mindforge-cc 11.8.2 → 11.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (361) hide show
  1. package/.agent/mindforge/wf-orchestrate-review.md +35 -0
  2. package/.agent/mindforge/wf-verification-loop.md +45 -0
  3. package/.agent/mindforge/wf-workflow-optimizer.md +51 -0
  4. package/.agent/skills/article-illustrator/SKILL.md +7 -7
  5. package/.agent/skills/arxiv/SKILL.md +6 -6
  6. package/.agent/skills/code-wiki/SKILL.md +11 -11
  7. package/.agent/skills/concept-diagrams/SKILL.md +6 -6
  8. package/.agent/skills/concept-diagrams/examples/apartment-floor-plan-conversion.md +244 -0
  9. package/.agent/skills/concept-diagrams/examples/automated-password-reset-flow.md +276 -0
  10. package/.agent/skills/concept-diagrams/examples/autonomous-llm-research-agent-flow.md +240 -0
  11. package/.agent/skills/concept-diagrams/examples/banana-journey-tree-to-smoothie.md +161 -0
  12. package/.agent/skills/concept-diagrams/examples/commercial-aircraft-structure.md +209 -0
  13. package/.agent/skills/concept-diagrams/examples/cpu-ooo-microarchitecture.md +236 -0
  14. package/.agent/skills/concept-diagrams/examples/electricity-grid-flow.md +182 -0
  15. package/.agent/skills/concept-diagrams/examples/feature-film-production-pipeline.md +172 -0
  16. package/.agent/skills/concept-diagrams/examples/hospital-emergency-department-flow.md +165 -0
  17. package/.agent/skills/concept-diagrams/examples/ml-benchmark-grouped-bar-chart.md +114 -0
  18. package/.agent/skills/concept-diagrams/examples/place-order-uml-sequence.md +325 -0
  19. package/.agent/skills/concept-diagrams/examples/smart-city-infrastructure.md +173 -0
  20. package/.agent/skills/concept-diagrams/examples/smartphone-layer-anatomy.md +154 -0
  21. package/.agent/skills/concept-diagrams/examples/sn2-reaction-mechanism.md +247 -0
  22. package/.agent/skills/concept-diagrams/examples/wind-turbine-structure.md +338 -0
  23. package/.agent/skills/concept-diagrams/templates/template.html +174 -0
  24. package/.agent/skills/domain-intel/SKILL.md +7 -7
  25. package/.agent/skills/duckduckgo-search/SKILL.md +17 -17
  26. package/.agent/skills/github-auth/scripts/gh-env.sh +66 -0
  27. package/.agent/skills/github-code-review/SKILL.md +9 -9
  28. package/.agent/skills/github-pr-workflow/SKILL.md +3 -3
  29. package/.agent/skills/godmode/SKILL.md +8 -8
  30. package/.agent/skills/jupyter-live-kernel/SKILL.md +3 -3
  31. package/.agent/skills/kanban-orchestrator/SKILL.md +10 -10
  32. package/.agent/skills/kanban-worker/SKILL.md +6 -6
  33. package/.agent/skills/llm-wiki/SKILL.md +7 -7
  34. package/.agent/skills/mindforge-autonomous/SKILL.md +1 -1
  35. package/.agent/skills/mindforge-complete-milestone/SKILL.md +1 -1
  36. package/.agent/skills/mindforge-discuss-phase/SKILL.md +1 -1
  37. package/.agent/skills/mindforge-do/SKILL.md +1 -1
  38. package/.agent/skills/mindforge-execute-phase/SKILL.md +1 -1
  39. package/.agent/skills/mindforge-list-workspaces/SKILL.md +1 -1
  40. package/.agent/skills/mindforge-manager/SKILL.md +1 -1
  41. package/.agent/skills/mindforge-neural-orchestrator/SKILL.md +1 -1
  42. package/.agent/skills/mindforge-new-milestone/SKILL.md +4 -4
  43. package/.agent/skills/mindforge-new-project/SKILL.md +4 -4
  44. package/.agent/skills/mindforge-new-workspace/SKILL.md +1 -1
  45. package/.agent/skills/mindforge-note/SKILL.md +1 -1
  46. package/.agent/skills/mindforge-plan-phase/SKILL.md +1 -1
  47. package/.agent/skills/mindforge-profile-user/SKILL.md +1 -1
  48. package/.agent/skills/mindforge-remove-workspace/SKILL.md +1 -1
  49. package/.agent/skills/mindforge-research-phase/SKILL.md +1 -1
  50. package/.agent/skills/mindforge-ui-phase/SKILL.md +1 -1
  51. package/.agent/skills/mindforge-ui-review/SKILL.md +1 -1
  52. package/.agent/skills/mindforge-verify-work/SKILL.md +1 -1
  53. package/.agent/skills/mindforge-workspace-isolated/SKILL.md +1 -1
  54. package/.agent/skills/node-inspect-debugger/SKILL.md +1 -1
  55. package/.agent/skills/obsidian/SKILL.md +15 -15
  56. package/.agent/skills/osint-investigation/SKILL.md +3 -2
  57. package/.agent/skills/osint-investigation/references/sources/courtlistener.md +98 -0
  58. package/.agent/skills/osint-investigation/references/sources/gdelt.md +104 -0
  59. package/.agent/skills/osint-investigation/references/sources/icij-offshore.md +104 -0
  60. package/.agent/skills/osint-investigation/references/sources/nyc-acris.md +90 -0
  61. package/.agent/skills/osint-investigation/references/sources/ofac-sdn.md +92 -0
  62. package/.agent/skills/osint-investigation/references/sources/opencorporates.md +103 -0
  63. package/.agent/skills/osint-investigation/references/sources/sec-edgar.md +83 -0
  64. package/.agent/skills/osint-investigation/references/sources/senate-ld.md +89 -0
  65. package/.agent/skills/osint-investigation/references/sources/usaspending.md +97 -0
  66. package/.agent/skills/osint-investigation/references/sources/wayback.md +93 -0
  67. package/.agent/skills/osint-investigation/references/sources/wikipedia.md +107 -0
  68. package/.agent/skills/osint-investigation/scripts/_http.py +82 -0
  69. package/.agent/skills/osint-investigation/scripts/_normalize.py +67 -0
  70. package/.agent/skills/osint-investigation/scripts/build_findings.py +221 -0
  71. package/.agent/skills/osint-investigation/scripts/entity_resolution.py +228 -0
  72. package/.agent/skills/osint-investigation/scripts/fetch_courtlistener.py +149 -0
  73. package/.agent/skills/osint-investigation/scripts/fetch_gdelt.py +161 -0
  74. package/.agent/skills/osint-investigation/scripts/fetch_icij_offshore.py +234 -0
  75. package/.agent/skills/osint-investigation/scripts/fetch_nyc_acris.py +203 -0
  76. package/.agent/skills/osint-investigation/scripts/fetch_ofac_sdn.py +175 -0
  77. package/.agent/skills/osint-investigation/scripts/fetch_opencorporates.py +191 -0
  78. package/.agent/skills/osint-investigation/scripts/fetch_sec_edgar.py +184 -0
  79. package/.agent/skills/osint-investigation/scripts/fetch_senate_ld.py +146 -0
  80. package/.agent/skills/osint-investigation/scripts/fetch_usaspending.py +170 -0
  81. package/.agent/skills/osint-investigation/scripts/fetch_wayback.py +142 -0
  82. package/.agent/skills/osint-investigation/scripts/fetch_wikipedia.py +266 -0
  83. package/.agent/skills/osint-investigation/scripts/timing_analysis.py +252 -0
  84. package/.agent/skills/oss-forensics/SKILL.md +10 -9
  85. package/.agent/skills/oss-forensics/scripts/evidence-store.py +313 -0
  86. package/.agent/skills/parallel-cli/SKILL.md +2 -2
  87. package/.agent/skills/pinggy-tunnel/SKILL.md +3 -3
  88. package/.agent/skills/pixel-art/SKILL.md +2 -2
  89. package/.agent/skills/plan/SKILL.md +10 -10
  90. package/.agent/skills/requesting-code-review/SKILL.md +4 -6
  91. package/.agent/skills/research-paper-writing/SKILL.md +32 -32
  92. package/.agent/skills/rest-graphql-debug/SKILL.md +31 -32
  93. package/.agent/skills/s6-container-supervision/SKILL.md +1 -1
  94. package/.agent/skills/scrapling/SKILL.md +1 -1
  95. package/.agent/skills/sherlock/SKILL.md +1 -1
  96. package/.agent/skills/simplify-code/SKILL.md +5 -7
  97. package/.agent/skills/skill-authoring/SKILL.md +4 -4
  98. package/.agent/skills/spike/SKILL.md +11 -13
  99. package/.agent/skills/subagent-driven-development/SKILL.md +7 -11
  100. package/.agent/skills/systematic-debugging/SKILL.md +13 -14
  101. package/.agent/skills/test-driven-development/SKILL.md +6 -7
  102. package/.agent/skills/video-orchestrator/SKILL.md +1 -1
  103. package/.agent/skills/web-pentest/SKILL.md +7 -7
  104. package/.mindforge/config.json +3 -2
  105. package/.mindforge/dynamic-workflows/REGISTRY.md +4 -1
  106. package/.mindforge/dynamic-workflows/index.json +910 -840
  107. package/.mindforge/dynamic-workflows/scripts/accessibility-audit.js +88 -90
  108. package/.mindforge/dynamic-workflows/scripts/ai-model-eval.js +59 -61
  109. package/.mindforge/dynamic-workflows/scripts/api-contract-test.js +87 -85
  110. package/.mindforge/dynamic-workflows/scripts/api-migration.js +120 -122
  111. package/.mindforge/dynamic-workflows/scripts/architecture-modernization.js +84 -86
  112. package/.mindforge/dynamic-workflows/scripts/code-audit.js +75 -77
  113. package/.mindforge/dynamic-workflows/scripts/code-explainer.js +99 -101
  114. package/.mindforge/dynamic-workflows/scripts/competitive-analysis.js +62 -64
  115. package/.mindforge/dynamic-workflows/scripts/competitive-teardown.js +106 -108
  116. package/.mindforge/dynamic-workflows/scripts/cost-analysis.js +78 -80
  117. package/.mindforge/dynamic-workflows/scripts/data-pipeline-validate.js +47 -49
  118. package/.mindforge/dynamic-workflows/scripts/database-migration.js +85 -87
  119. package/.mindforge/dynamic-workflows/scripts/debug-detective.js +97 -99
  120. package/.mindforge/dynamic-workflows/scripts/dependency-health.js +81 -83
  121. package/.mindforge/dynamic-workflows/scripts/design-system-audit.js +84 -86
  122. package/.mindforge/dynamic-workflows/scripts/documentation-gen.js +65 -67
  123. package/.mindforge/dynamic-workflows/scripts/feature-planner.js +78 -80
  124. package/.mindforge/dynamic-workflows/scripts/incident-response.js +81 -83
  125. package/.mindforge/dynamic-workflows/scripts/multi-repo-sync.js +49 -51
  126. package/.mindforge/dynamic-workflows/scripts/mutation-testing.js +113 -115
  127. package/.mindforge/dynamic-workflows/scripts/onboard-codebase.js +79 -81
  128. package/.mindforge/dynamic-workflows/scripts/orchestrate-review.js +115 -0
  129. package/.mindforge/dynamic-workflows/scripts/perf-optimize.js +97 -99
  130. package/.mindforge/dynamic-workflows/scripts/pr-review.js +61 -63
  131. package/.mindforge/dynamic-workflows/scripts/refactor-plan.js +88 -90
  132. package/.mindforge/dynamic-workflows/scripts/release-prep.js +79 -81
  133. package/.mindforge/dynamic-workflows/scripts/security-hardening.js +117 -119
  134. package/.mindforge/dynamic-workflows/scripts/security-threat-model.js +119 -121
  135. package/.mindforge/dynamic-workflows/scripts/tdd-sprint.js +80 -82
  136. package/.mindforge/dynamic-workflows/scripts/tech-evaluation.js +50 -52
  137. package/.mindforge/dynamic-workflows/scripts/test-coverage-gap.js +68 -70
  138. package/.mindforge/dynamic-workflows/scripts/ux-heuristic-audit.js +89 -91
  139. package/.mindforge/dynamic-workflows/scripts/verification-loop.js +138 -0
  140. package/.mindforge/dynamic-workflows/scripts/workflow-optimizer.js +136 -0
  141. package/.mindforge/dynamic-workflows/scripts/writer-reviewer.js +63 -65
  142. package/.mindforge/memory/sync-manifest.json +1 -1
  143. package/.mindforge/personas/advisor-researcher.md +2 -2
  144. package/.mindforge/personas/api-load-tester.md +1 -1
  145. package/.mindforge/personas/authentication-architect.md +1 -1
  146. package/.mindforge/personas/backup-recovery-specialist.md +1 -1
  147. package/.mindforge/personas/build-optimizer.md +1 -1
  148. package/.mindforge/personas/caching-strategist.md +1 -1
  149. package/.mindforge/personas/chaos-engineer.md +1 -1
  150. package/.mindforge/personas/compliance-auditor.md +1 -1
  151. package/.mindforge/personas/config-management-expert.md +1 -1
  152. package/.mindforge/personas/coverage-specialist.md +1 -1
  153. package/.mindforge/personas/debug-specialist.md +2 -2
  154. package/.mindforge/personas/debugger.md +2 -2
  155. package/.mindforge/personas/dependency-auditor.md +1 -1
  156. package/.mindforge/personas/developer.md +1 -1
  157. package/.mindforge/personas/devops-engineer.md +1 -1
  158. package/.mindforge/personas/executor.md +1 -1
  159. package/.mindforge/personas/go-specialist.md +1 -1
  160. package/.mindforge/personas/incident-commander.md +1 -1
  161. package/.mindforge/personas/java-specialist.md +1 -1
  162. package/.mindforge/personas/kubernetes-debugger.md +1 -1
  163. package/.mindforge/personas/logging-architect.md +1 -1
  164. package/.mindforge/personas/mf-planner.md +1 -1
  165. package/.mindforge/personas/mf-researcher.md +1 -1
  166. package/.mindforge/personas/mf-reviewer.md +1 -1
  167. package/.mindforge/personas/nyquist-auditor.md +1 -1
  168. package/.mindforge/personas/observability-engineer.md +1 -1
  169. package/.mindforge/personas/performance-optimizer.md +1 -1
  170. package/.mindforge/personas/phase-researcher.md +5 -5
  171. package/.mindforge/personas/planner.md +1 -1
  172. package/.mindforge/personas/project-researcher.md +2 -2
  173. package/.mindforge/personas/python-specialist.md +1 -1
  174. package/.mindforge/personas/qa-engineer.md +1 -1
  175. package/.mindforge/personas/release-manager.md +1 -1
  176. package/.mindforge/personas/research-agent.md +4 -4
  177. package/.mindforge/personas/roadmapper-extend.md +1 -1
  178. package/.mindforge/personas/rust-specialist.md +1 -1
  179. package/.mindforge/personas/security-reviewer.md +1 -1
  180. package/.mindforge/personas/tech-writer.md +1 -1
  181. package/.mindforge/personas/typescript-wizard.md +1 -1
  182. package/.mindforge/personas/ui-researcher.md +1 -1
  183. package/.mindforge/skills/accessibility/SKILL.md +3 -3
  184. package/.mindforge/skills/agent-architecture-audit/SKILL.md +11 -0
  185. package/.mindforge/skills/agent-loops/SKILL.md +1 -1
  186. package/.mindforge/skills/arxiv/SKILL.md +6 -6
  187. package/.mindforge/skills/arxiv/scripts/search_arxiv.py +114 -0
  188. package/.mindforge/skills/code-tour/SKILL.md +2 -2
  189. package/.mindforge/skills/code-wiki/SKILL.md +11 -11
  190. package/.mindforge/skills/code-wiki/templates/README.md +31 -0
  191. package/.mindforge/skills/code-wiki/templates/architecture.md +30 -0
  192. package/.mindforge/skills/code-wiki/templates/getting-started.md +47 -0
  193. package/.mindforge/skills/code-wiki/templates/module.md +38 -0
  194. package/.mindforge/skills/concept-diagrams/SKILL.md +6 -6
  195. package/.mindforge/skills/concept-diagrams/examples/apartment-floor-plan-conversion.md +244 -0
  196. package/.mindforge/skills/concept-diagrams/examples/automated-password-reset-flow.md +276 -0
  197. package/.mindforge/skills/concept-diagrams/examples/autonomous-llm-research-agent-flow.md +240 -0
  198. package/.mindforge/skills/concept-diagrams/examples/banana-journey-tree-to-smoothie.md +161 -0
  199. package/.mindforge/skills/concept-diagrams/examples/commercial-aircraft-structure.md +209 -0
  200. package/.mindforge/skills/concept-diagrams/examples/cpu-ooo-microarchitecture.md +236 -0
  201. package/.mindforge/skills/concept-diagrams/examples/electricity-grid-flow.md +182 -0
  202. package/.mindforge/skills/concept-diagrams/examples/feature-film-production-pipeline.md +172 -0
  203. package/.mindforge/skills/concept-diagrams/examples/hospital-emergency-department-flow.md +165 -0
  204. package/.mindforge/skills/concept-diagrams/examples/ml-benchmark-grouped-bar-chart.md +114 -0
  205. package/.mindforge/skills/concept-diagrams/examples/place-order-uml-sequence.md +325 -0
  206. package/.mindforge/skills/concept-diagrams/examples/smart-city-infrastructure.md +173 -0
  207. package/.mindforge/skills/concept-diagrams/examples/smartphone-layer-anatomy.md +154 -0
  208. package/.mindforge/skills/concept-diagrams/examples/sn2-reaction-mechanism.md +247 -0
  209. package/.mindforge/skills/concept-diagrams/examples/wind-turbine-structure.md +338 -0
  210. package/.mindforge/skills/concept-diagrams/references/dashboard-patterns.md +43 -0
  211. package/.mindforge/skills/concept-diagrams/references/infrastructure-patterns.md +144 -0
  212. package/.mindforge/skills/concept-diagrams/references/physical-shape-cookbook.md +42 -0
  213. package/.mindforge/skills/concept-diagrams/templates/template.html +174 -0
  214. package/.mindforge/skills/creative-ideation/references/full-prompt-library.md +110 -0
  215. package/.mindforge/skills/documentation/SKILL.md +1 -1
  216. package/.mindforge/skills/domain-intel/SKILL.md +7 -7
  217. package/.mindforge/skills/domain-intel/scripts/domain_intel.py +397 -0
  218. package/.mindforge/skills/duckduckgo-search/SKILL.md +17 -17
  219. package/.mindforge/skills/duckduckgo-search/scripts/duckduckgo.sh +28 -0
  220. package/.mindforge/skills/environment-management/SKILL.md +1 -1
  221. package/.mindforge/skills/github-code-review/SKILL.md +9 -9
  222. package/.mindforge/skills/github-code-review/references/review-output-template.md +74 -0
  223. package/.mindforge/skills/github-issues/templates/bug-report.md +35 -0
  224. package/.mindforge/skills/github-issues/templates/feature-request.md +31 -0
  225. package/.mindforge/skills/github-pr-workflow/SKILL.md +3 -3
  226. package/.mindforge/skills/github-pr-workflow/references/ci-troubleshooting.md +183 -0
  227. package/.mindforge/skills/github-pr-workflow/references/conventional-commits.md +71 -0
  228. package/.mindforge/skills/github-pr-workflow/templates/pr-body-bugfix.md +35 -0
  229. package/.mindforge/skills/github-pr-workflow/templates/pr-body-feature.md +33 -0
  230. package/.mindforge/skills/incident-response/SKILL.md +1 -1
  231. package/.mindforge/skills/jupyter-live-kernel/SKILL.md +3 -3
  232. package/.mindforge/skills/kanban-orchestrator/SKILL.md +10 -10
  233. package/.mindforge/skills/kanban-worker/SKILL.md +6 -6
  234. package/.mindforge/skills/meme-generation/SKILL.md +1 -1
  235. package/.mindforge/skills/meme-generation/scripts/generate_meme.py +470 -0
  236. package/.mindforge/skills/meme-generation/scripts/templates.json +97 -0
  237. package/.mindforge/skills/obsidian/SKILL.md +15 -15
  238. package/.mindforge/skills/orch-pipeline/SKILL.md +2 -2
  239. package/.mindforge/skills/osint-investigation/SKILL.md +3 -2
  240. package/.mindforge/skills/osint-investigation/references/sources/courtlistener.md +98 -0
  241. package/.mindforge/skills/osint-investigation/references/sources/gdelt.md +104 -0
  242. package/.mindforge/skills/osint-investigation/references/sources/icij-offshore.md +104 -0
  243. package/.mindforge/skills/osint-investigation/references/sources/nyc-acris.md +90 -0
  244. package/.mindforge/skills/osint-investigation/references/sources/ofac-sdn.md +92 -0
  245. package/.mindforge/skills/osint-investigation/references/sources/opencorporates.md +103 -0
  246. package/.mindforge/skills/osint-investigation/references/sources/sec-edgar.md +83 -0
  247. package/.mindforge/skills/osint-investigation/references/sources/senate-ld.md +89 -0
  248. package/.mindforge/skills/osint-investigation/references/sources/usaspending.md +97 -0
  249. package/.mindforge/skills/osint-investigation/references/sources/wayback.md +93 -0
  250. package/.mindforge/skills/osint-investigation/references/sources/wikipedia.md +107 -0
  251. package/.mindforge/skills/osint-investigation/scripts/_http.py +82 -0
  252. package/.mindforge/skills/osint-investigation/scripts/_normalize.py +67 -0
  253. package/.mindforge/skills/osint-investigation/scripts/build_findings.py +221 -0
  254. package/.mindforge/skills/osint-investigation/scripts/entity_resolution.py +228 -0
  255. package/.mindforge/skills/osint-investigation/scripts/fetch_courtlistener.py +149 -0
  256. package/.mindforge/skills/osint-investigation/scripts/fetch_gdelt.py +161 -0
  257. package/.mindforge/skills/osint-investigation/scripts/fetch_icij_offshore.py +234 -0
  258. package/.mindforge/skills/osint-investigation/scripts/fetch_nyc_acris.py +203 -0
  259. package/.mindforge/skills/osint-investigation/scripts/fetch_ofac_sdn.py +175 -0
  260. package/.mindforge/skills/osint-investigation/scripts/fetch_opencorporates.py +191 -0
  261. package/.mindforge/skills/osint-investigation/scripts/fetch_sec_edgar.py +184 -0
  262. package/.mindforge/skills/osint-investigation/scripts/fetch_senate_ld.py +146 -0
  263. package/.mindforge/skills/osint-investigation/scripts/fetch_usaspending.py +170 -0
  264. package/.mindforge/skills/osint-investigation/scripts/fetch_wayback.py +142 -0
  265. package/.mindforge/skills/osint-investigation/scripts/fetch_wikipedia.py +266 -0
  266. package/.mindforge/skills/osint-investigation/scripts/timing_analysis.py +252 -0
  267. package/.mindforge/skills/osint-investigation/templates/source-template.md +59 -0
  268. package/.mindforge/skills/oss-forensics/SKILL.md +6 -6
  269. package/.mindforge/skills/oss-forensics/references/evidence-types.md +89 -0
  270. package/.mindforge/skills/oss-forensics/references/github-archive-guide.md +184 -0
  271. package/.mindforge/skills/oss-forensics/references/investigation-templates.md +131 -0
  272. package/.mindforge/skills/oss-forensics/references/recovery-techniques.md +164 -0
  273. package/.mindforge/skills/oss-forensics/scripts/evidence-store.py +313 -0
  274. package/.mindforge/skills/oss-forensics/templates/forensic-report.md +151 -0
  275. package/.mindforge/skills/oss-forensics/templates/malicious-package-report.md +43 -0
  276. package/.mindforge/skills/pixel-art/SKILL.md +2 -2
  277. package/.mindforge/skills/pixel-art/references/palettes.md +49 -0
  278. package/.mindforge/skills/plan/SKILL.md +10 -10
  279. package/.mindforge/skills/requesting-code-review/SKILL.md +4 -6
  280. package/.mindforge/skills/research-paper-writing/SKILL.md +32 -32
  281. package/.mindforge/skills/research-paper-writing/references/autoreason-methodology.md +394 -0
  282. package/.mindforge/skills/research-paper-writing/references/checklists.md +434 -0
  283. package/.mindforge/skills/research-paper-writing/references/citation-workflow.md +563 -0
  284. package/.mindforge/skills/research-paper-writing/references/experiment-patterns.md +728 -0
  285. package/.mindforge/skills/research-paper-writing/references/human-evaluation.md +476 -0
  286. package/.mindforge/skills/research-paper-writing/references/paper-types.md +481 -0
  287. package/.mindforge/skills/research-paper-writing/references/reviewer-guidelines.md +433 -0
  288. package/.mindforge/skills/research-paper-writing/references/sources.md +191 -0
  289. package/.mindforge/skills/research-paper-writing/references/writing-guide.md +474 -0
  290. package/.mindforge/skills/research-paper-writing/templates/README.md +251 -0
  291. package/.mindforge/skills/scrapling/SKILL.md +1 -1
  292. package/.mindforge/skills/security-review/SKILL.md +1 -1
  293. package/.mindforge/skills/sherlock/SKILL.md +1 -1
  294. package/.mindforge/skills/simplify-code/SKILL.md +5 -7
  295. package/.mindforge/skills/spike/SKILL.md +11 -13
  296. package/.mindforge/skills/subagent-driven-development/SKILL.md +7 -11
  297. package/.mindforge/skills/subagent-driven-development/references/context-budget-discipline.md +53 -0
  298. package/.mindforge/skills/subagent-driven-development/references/gates-taxonomy.md +93 -0
  299. package/.mindforge/skills/systematic-debugging/SKILL.md +13 -14
  300. package/.mindforge/skills/test-driven-development/SKILL.md +7 -8
  301. package/.mindforge/skills/testing-standards/SKILL.md +1 -1
  302. package/.mindforge/skills/web-pentest/SKILL.md +2 -2
  303. package/.mindforge/skills/web-pentest/references/bypass-techniques.md +133 -0
  304. package/.mindforge/skills/web-pentest/references/exploitation-techniques.md +204 -0
  305. package/.mindforge/skills/web-pentest/references/scope-enforcement.md +110 -0
  306. package/.mindforge/skills/web-pentest/references/vuln-taxonomy.md +81 -0
  307. package/.mindforge/skills/web-pentest/templates/authorization.md +69 -0
  308. package/.mindforge/skills/web-pentest/templates/pentest-report.md +178 -0
  309. package/.mindforge/skills/writing-skills/SKILL.md +4 -3
  310. package/CHANGELOG.md +35 -0
  311. package/MINDFORGE.md +3 -3
  312. package/README.md +9 -7
  313. package/RELEASENOTES.md +65 -2
  314. package/SECURITY.md +5 -3
  315. package/bin/autonomous/auto-runner.js +5 -2
  316. package/bin/engine/skill-loader.js +4 -0
  317. package/bin/governance/rbac.js +4 -0
  318. package/bin/memory/eis-client.js +1 -0
  319. package/bin/mindforge-cli.js +5 -0
  320. package/bin/models/bedrock-provider.js +192 -0
  321. package/bin/models/model-client.js +19 -0
  322. package/bin/spawn-agent.js +9 -2
  323. package/docs/commands-reference.md +68 -6
  324. package/docs/faq.md +38 -1
  325. package/docs/getting-started.md +27 -12
  326. package/docs/sdk-reference.md +36 -3
  327. package/docs/troubleshooting.md +59 -1
  328. package/docs/user-guide.md +7 -4
  329. package/package.json +3 -3
  330. package/.claude/commands/mindforge/skill-tdd.md +0 -53
  331. package/.claude/commands/mindforge/skills-index.md +0 -118
  332. package/.claude/commands/mindforge/systematic-debug.md +0 -60
  333. package/.claude/commands/mindforge/wf-accessibility-audit.md +0 -31
  334. package/.claude/commands/mindforge/wf-ai-model-eval.md +0 -31
  335. package/.claude/commands/mindforge/wf-api-migration.md +0 -31
  336. package/.claude/commands/mindforge/wf-architecture-modernization.md +0 -32
  337. package/.claude/commands/mindforge/wf-catalog.md +0 -59
  338. package/.claude/commands/mindforge/wf-code-audit.md +0 -31
  339. package/.claude/commands/mindforge/wf-code-explainer.md +0 -31
  340. package/.claude/commands/mindforge/wf-competitive-analysis.md +0 -31
  341. package/.claude/commands/mindforge/wf-competitive-teardown.md +0 -31
  342. package/.claude/commands/mindforge/wf-cost-analysis.md +0 -31
  343. package/.claude/commands/mindforge/wf-data-pipeline-validate.md +0 -31
  344. package/.claude/commands/mindforge/wf-database-migration.md +0 -31
  345. package/.claude/commands/mindforge/wf-debug-detective.md +0 -32
  346. package/.claude/commands/mindforge/wf-design-system-audit.md +0 -31
  347. package/.claude/commands/mindforge/wf-documentation-gen.md +0 -31
  348. package/.claude/commands/mindforge/wf-feature-planner.md +0 -31
  349. package/.claude/commands/mindforge/wf-incident-response.md +0 -31
  350. package/.claude/commands/mindforge/wf-multi-repo-sync.md +0 -31
  351. package/.claude/commands/mindforge/wf-mutation-testing.md +0 -31
  352. package/.claude/commands/mindforge/wf-onboard-codebase.md +0 -31
  353. package/.claude/commands/mindforge/wf-perf-optimize.md +0 -31
  354. package/.claude/commands/mindforge/wf-pr-review.md +0 -31
  355. package/.claude/commands/mindforge/wf-refactor-plan.md +0 -31
  356. package/.claude/commands/mindforge/wf-release-prep.md +0 -31
  357. package/.claude/commands/mindforge/wf-security-threat-model.md +0 -31
  358. package/.claude/commands/mindforge/wf-tdd-sprint.md +0 -31
  359. package/.claude/commands/mindforge/wf-tech-evaluation.md +0 -31
  360. package/.claude/commands/mindforge/wf-ux-heuristic-audit.md +0 -31
  361. package/.claude/commands/mindforge/wf-writer-reviewer.md +0 -30
@@ -0,0 +1,221 @@
1
+ #!/usr/bin/env python3
2
+ """Build a structured findings.json with evidence chains (stdlib-only).
3
+
4
+ Aggregates cross_links.csv (entity_resolution output) and an optional
5
+ timing.json (timing_analysis output) into a single evidence-chain document.
6
+
7
+ Output structure:
8
+ {
9
+ "metadata": {...},
10
+ "findings": [
11
+ {
12
+ "id": "F0001",
13
+ "title": "...",
14
+ "severity": "HIGH|MEDIUM|LOW",
15
+ "confidence": "high|medium|low",
16
+ "summary": "...",
17
+ "evidence": [
18
+ {"source": "cross_links.csv", "row": 12, "fields": {...}},
19
+ ...
20
+ ],
21
+ "sources": ["cross_links.csv", "timing.json"]
22
+ }
23
+ ]
24
+ }
25
+
26
+ Every finding traces to specific source rows. No naked claims.
27
+ """
28
+ from __future__ import annotations
29
+
30
+ import argparse
31
+ import csv
32
+ import json
33
+ from collections import defaultdict
34
+ from pathlib import Path
35
+
36
+ CONFIDENCE_ORDER = {"high": 0, "medium": 1, "low": 2}
37
+ SEVERITY_ORDER = {"HIGH": 0, "MEDIUM": 1, "LOW": 2}
38
+
39
+
40
+ def _read_cross_links(path: str) -> list[dict[str, str]]:
41
+ with open(path, newline="", encoding="utf-8") as fh:
42
+ return list(csv.DictReader(fh))
43
+
44
+
45
+ def build_findings(
46
+ cross_links_path: str,
47
+ timing_path: str | None = None,
48
+ out_path: str = "findings.json",
49
+ bundled_threshold: int = 3,
50
+ ) -> dict:
51
+ findings: list[dict] = []
52
+ next_id = 1
53
+
54
+ # 1. Match-based findings, grouped by (left_normalized, right_normalized).
55
+ matches = _read_cross_links(cross_links_path)
56
+ grouped: dict[tuple[str, str], list[dict[str, str]]] = defaultdict(list)
57
+ for i, row in enumerate(matches):
58
+ row["__row__"] = str(i)
59
+ grouped[(row.get("left_normalized", ""), row.get("right_normalized", ""))].append(row)
60
+
61
+ for (left_norm, right_norm), rows in grouped.items():
62
+ if not left_norm or not right_norm:
63
+ continue
64
+ # Use the highest-confidence match for the finding's overall confidence.
65
+ best = min(rows, key=lambda r: CONFIDENCE_ORDER.get(r.get("confidence", "low"), 2))
66
+ finding_id = f"F{next_id:04d}"
67
+ next_id += 1
68
+ evidence = [
69
+ {
70
+ "source": "cross_links.csv",
71
+ "row": int(r["__row__"]),
72
+ "fields": {
73
+ "match_type": r.get("match_type", ""),
74
+ "confidence": r.get("confidence", ""),
75
+ "left_name": r.get("left_name", ""),
76
+ "right_name": r.get("right_name", ""),
77
+ "overlap_ratio": r.get("overlap_ratio", ""),
78
+ "shared_tokens": r.get("shared_tokens", ""),
79
+ },
80
+ }
81
+ for r in rows
82
+ ]
83
+ findings.append(
84
+ {
85
+ "id": finding_id,
86
+ "title": f"Entity match: {best.get('left_name', '')} ↔ {best.get('right_name', '')}",
87
+ "severity": "MEDIUM" if best.get("confidence") == "high" else "LOW",
88
+ "confidence": best.get("confidence", "low"),
89
+ "summary": (
90
+ f"{len(rows)} cross-link record(s) tie "
91
+ f"'{best.get('left_name', '')}' to "
92
+ f"'{best.get('right_name', '')}' "
93
+ f"(best tier: {best.get('match_type', '')})."
94
+ ),
95
+ "evidence": evidence,
96
+ "sources": ["cross_links.csv"],
97
+ }
98
+ )
99
+
100
+ # 2. Bundled-donations findings (if cross_links carries donor↔candidate pattern).
101
+ # Heuristic: many distinct left names sharing the same right name.
102
+ by_right: dict[str, set[str]] = defaultdict(set)
103
+ by_right_rows: dict[str, list[dict[str, str]]] = defaultdict(list)
104
+ for r in matches:
105
+ right = r.get("right_normalized", "")
106
+ left_raw = r.get("left_name", "").strip()
107
+ if right and left_raw:
108
+ by_right[right].add(left_raw)
109
+ by_right_rows[right].append(r)
110
+ for right_norm, lefts in by_right.items():
111
+ if len(lefts) < bundled_threshold:
112
+ continue
113
+ rows = by_right_rows[right_norm]
114
+ right_raw = rows[0].get("right_name", "")
115
+ findings.append(
116
+ {
117
+ "id": f"F{next_id:04d}",
118
+ "title": f"Bundled cross-links: {len(lefts)} distinct left entities ↔ '{right_raw}'",
119
+ "severity": "HIGH",
120
+ "confidence": "medium",
121
+ "summary": (
122
+ f"{len(lefts)} distinct left-side entities link to "
123
+ f"'{right_raw}'. Pattern suggests coordinated relationship "
124
+ f"(e.g. bundled donations, multi-vendor employer)."
125
+ ),
126
+ "evidence": [
127
+ {
128
+ "source": "cross_links.csv",
129
+ "row": int(r.get("__row__", "0")),
130
+ "fields": {
131
+ "left_name": r.get("left_name", ""),
132
+ "match_type": r.get("match_type", ""),
133
+ },
134
+ }
135
+ for r in rows
136
+ ],
137
+ "sources": ["cross_links.csv"],
138
+ }
139
+ )
140
+ next_id += 1
141
+
142
+ # 3. Timing-based findings.
143
+ if timing_path and Path(timing_path).exists():
144
+ timing = json.loads(Path(timing_path).read_text())
145
+ for r in timing.get("results", []):
146
+ if not r.get("significant"):
147
+ continue
148
+ findings.append(
149
+ {
150
+ "id": f"F{next_id:04d}",
151
+ "title": (
152
+ f"Donation timing significantly clusters near awards: "
153
+ f"{r['donor']} ↔ {r['recipient']}"
154
+ ),
155
+ "severity": "HIGH" if r["p_value"] < 0.01 else "MEDIUM",
156
+ "confidence": "medium",
157
+ "summary": (
158
+ f"Mean nearest-award distance {r['observed_mean_days']} days "
159
+ f"(null {r['null_mean_days']} days). p={r['p_value']}, "
160
+ f"effect size {r['effect_size_sd']} SD. "
161
+ f"{r['n_donations']} donations, {r['n_award_dates']} awards."
162
+ ),
163
+ "evidence": [
164
+ {
165
+ "source": "timing.json",
166
+ "row": None,
167
+ "fields": r,
168
+ }
169
+ ],
170
+ "sources": ["timing.json"],
171
+ }
172
+ )
173
+ next_id += 1
174
+
175
+ # Sort: severity → confidence → id.
176
+ findings.sort(
177
+ key=lambda f: (
178
+ SEVERITY_ORDER.get(f["severity"], 3),
179
+ CONFIDENCE_ORDER.get(f["confidence"], 3),
180
+ f["id"],
181
+ )
182
+ )
183
+
184
+ payload = {
185
+ "metadata": {
186
+ "n_findings": len(findings),
187
+ "cross_links_path": cross_links_path,
188
+ "timing_path": timing_path,
189
+ "bundled_threshold": bundled_threshold,
190
+ },
191
+ "findings": findings,
192
+ }
193
+ Path(out_path).write_text(json.dumps(payload, indent=2))
194
+ return payload
195
+
196
+
197
+ def main() -> int:
198
+ p = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter)
199
+ p.add_argument("--cross-links", required=True)
200
+ p.add_argument("--timing", help="Optional timing.json from timing_analysis.py")
201
+ p.add_argument("--out", default="findings.json")
202
+ p.add_argument(
203
+ "--bundled-threshold",
204
+ type=int,
205
+ default=3,
206
+ help="Minimum distinct left entities to flag as bundled (default 3)",
207
+ )
208
+ a = p.parse_args()
209
+
210
+ payload = build_findings(
211
+ cross_links_path=a.cross_links,
212
+ timing_path=a.timing,
213
+ out_path=a.out,
214
+ bundled_threshold=a.bundled_threshold,
215
+ )
216
+ print(f"Wrote {payload['metadata']['n_findings']} findings to {a.out}")
217
+ return 0
218
+
219
+
220
+ if __name__ == "__main__":
221
+ raise SystemExit(main())
@@ -0,0 +1,228 @@
1
+ #!/usr/bin/env python3
2
+ """Cross-source entity resolution (stdlib-only).
3
+
4
+ Given two CSV files with name columns, find candidate matches using three
5
+ tiers of normalization:
6
+
7
+ 1. exact — normalized strings equal
8
+ 2. fuzzy — sorted-token (word-bag) match
9
+ 3. token_overlap — >=60% Jaccard overlap on >=4-char tokens, >=2 shared
10
+
11
+ Adapted from ShinMegamiBoson/OpenPlanter (MIT) but generalized: no Boston-
12
+ specific record types, no contribution-code filters, no fixed schemas.
13
+
14
+ Output CSV columns:
15
+ match_type, confidence, left_name, right_name,
16
+ left_normalized, right_normalized, left_row, right_row,
17
+ overlap_ratio, shared_tokens
18
+ """
19
+ from __future__ import annotations
20
+
21
+ import argparse
22
+ import csv
23
+ import sys
24
+ from pathlib import Path
25
+
26
+ # Allow running directly or as a module.
27
+ sys.path.insert(0, str(Path(__file__).parent))
28
+ from _normalize import ( # noqa: E402
29
+ normalize_name,
30
+ normalize_aggressive,
31
+ token_overlap_ratio,
32
+ )
33
+
34
+ CONFIDENCE = {
35
+ "exact": "high",
36
+ "fuzzy": "medium",
37
+ "token_overlap": "low",
38
+ }
39
+
40
+
41
+ def _read_csv(path: str, name_col: str) -> list[dict[str, str]]:
42
+ rows = []
43
+ with open(path, newline="", encoding="utf-8") as fh:
44
+ reader = csv.DictReader(fh)
45
+ if name_col not in (reader.fieldnames or []):
46
+ raise SystemExit(
47
+ f"Column {name_col!r} not in {path}. "
48
+ f"Available: {reader.fieldnames}"
49
+ )
50
+ for i, row in enumerate(reader):
51
+ row["__row__"] = str(i)
52
+ rows.append(row)
53
+ return rows
54
+
55
+
56
+ def _build_index(rows: list[dict[str, str]], name_col: str):
57
+ """Index by exact-normalized and aggressive (sorted-token) form."""
58
+ exact: dict[str, list[dict[str, str]]] = {}
59
+ aggressive: dict[str, list[dict[str, str]]] = {}
60
+ for row in rows:
61
+ raw = row.get(name_col, "")
62
+ n = normalize_name(raw)
63
+ if n:
64
+ exact.setdefault(n, []).append(row)
65
+ a = normalize_aggressive(raw)
66
+ if a:
67
+ aggressive.setdefault(a, []).append(row)
68
+ return exact, aggressive
69
+
70
+
71
+ def _emit(
72
+ out_rows: list[dict[str, str]],
73
+ seen: set[tuple],
74
+ match_type: str,
75
+ left_row: dict[str, str],
76
+ right_row: dict[str, str],
77
+ left_col: str,
78
+ right_col: str,
79
+ ratio: float = 0.0,
80
+ shared: int = 0,
81
+ ):
82
+ left_raw = left_row.get(left_col, "")
83
+ right_raw = right_row.get(right_col, "")
84
+ key = (
85
+ left_row["__row__"],
86
+ right_row["__row__"],
87
+ match_type,
88
+ )
89
+ if key in seen:
90
+ return
91
+ seen.add(key)
92
+ out_rows.append(
93
+ {
94
+ "match_type": match_type,
95
+ "confidence": CONFIDENCE[match_type],
96
+ "left_name": left_raw,
97
+ "right_name": right_raw,
98
+ "left_normalized": normalize_name(left_raw),
99
+ "right_normalized": normalize_name(right_raw),
100
+ "left_row": left_row["__row__"],
101
+ "right_row": right_row["__row__"],
102
+ "overlap_ratio": f"{ratio:.3f}" if ratio else "",
103
+ "shared_tokens": str(shared) if shared else "",
104
+ }
105
+ )
106
+
107
+
108
+ def resolve(
109
+ left_path: str,
110
+ left_col: str,
111
+ right_path: str,
112
+ right_col: str,
113
+ out_path: str,
114
+ overlap_threshold: float = 0.60,
115
+ min_shared: int = 2,
116
+ skip_overlap: bool = False,
117
+ ) -> int:
118
+ left_rows = _read_csv(left_path, left_col)
119
+ right_rows = _read_csv(right_path, right_col)
120
+
121
+ right_exact, right_aggressive = _build_index(right_rows, right_col)
122
+
123
+ out_rows: list[dict[str, str]] = []
124
+ seen: set[tuple] = set()
125
+
126
+ # Pass 1+2: exact / fuzzy via index lookup.
127
+ for lrow in left_rows:
128
+ raw = lrow.get(left_col, "")
129
+ n = normalize_name(raw)
130
+ if not n:
131
+ continue
132
+ for rrow in right_exact.get(n, []):
133
+ _emit(out_rows, seen, "exact", lrow, rrow, left_col, right_col)
134
+ a = normalize_aggressive(raw)
135
+ if a:
136
+ for rrow in right_aggressive.get(a, []):
137
+ _emit(out_rows, seen, "fuzzy", lrow, rrow, left_col, right_col)
138
+
139
+ if not skip_overlap:
140
+ # Pass 3: token overlap (O(N*M) — expensive; allow opt-out).
141
+ for lrow in left_rows:
142
+ l_raw = lrow.get(left_col, "")
143
+ if not normalize_name(l_raw):
144
+ continue
145
+ for rrow in right_rows:
146
+ ratio, shared = token_overlap_ratio(
147
+ l_raw, rrow.get(right_col, "")
148
+ )
149
+ if ratio >= overlap_threshold and shared >= min_shared:
150
+ _emit(
151
+ out_rows,
152
+ seen,
153
+ "token_overlap",
154
+ lrow,
155
+ rrow,
156
+ left_col,
157
+ right_col,
158
+ ratio=ratio,
159
+ shared=shared,
160
+ )
161
+
162
+ fieldnames = [
163
+ "match_type",
164
+ "confidence",
165
+ "left_name",
166
+ "right_name",
167
+ "left_normalized",
168
+ "right_normalized",
169
+ "left_row",
170
+ "right_row",
171
+ "overlap_ratio",
172
+ "shared_tokens",
173
+ ]
174
+ with open(out_path, "w", newline="", encoding="utf-8") as fh:
175
+ writer = csv.DictWriter(fh, fieldnames=fieldnames)
176
+ writer.writeheader()
177
+ writer.writerows(out_rows)
178
+ return len(out_rows)
179
+
180
+
181
+ def main() -> int:
182
+ p = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter)
183
+ p.add_argument("--left", required=True, help="Left CSV path")
184
+ p.add_argument(
185
+ "--left-name-col", required=True, help="Name column in left CSV"
186
+ )
187
+ p.add_argument("--right", required=True, help="Right CSV path")
188
+ p.add_argument(
189
+ "--right-name-col",
190
+ required=True,
191
+ help="Name column in right CSV",
192
+ )
193
+ p.add_argument("--out", required=True, help="Output CSV path")
194
+ p.add_argument(
195
+ "--overlap-threshold",
196
+ type=float,
197
+ default=0.60,
198
+ help="Jaccard overlap threshold for token_overlap tier (default 0.60)",
199
+ )
200
+ p.add_argument(
201
+ "--min-shared",
202
+ type=int,
203
+ default=2,
204
+ help="Minimum shared tokens for token_overlap tier (default 2)",
205
+ )
206
+ p.add_argument(
207
+ "--skip-overlap",
208
+ action="store_true",
209
+ help="Skip the O(N*M) token_overlap pass (much faster on large CSVs)",
210
+ )
211
+ args = p.parse_args()
212
+
213
+ count = resolve(
214
+ left_path=args.left,
215
+ left_col=args.left_name_col,
216
+ right_path=args.right,
217
+ right_col=args.right_name_col,
218
+ out_path=args.out,
219
+ overlap_threshold=args.overlap_threshold,
220
+ min_shared=args.min_shared,
221
+ skip_overlap=args.skip_overlap,
222
+ )
223
+ print(f"Wrote {count} match rows to {args.out}")
224
+ return 0
225
+
226
+
227
+ if __name__ == "__main__":
228
+ raise SystemExit(main())
@@ -0,0 +1,149 @@
1
+ #!/usr/bin/env python3
2
+ """Search court records via CourtListener (Free Law Project).
3
+
4
+ Covers ~10M federal and state court opinions, plus PACER docket data
5
+ where available. Public REST API v4 supports anonymous read access for
6
+ search; some endpoints require a token (free at courtlistener.com).
7
+
8
+ Set COURTLISTENER_TOKEN to authenticate (raises rate limits).
9
+ """
10
+ from __future__ import annotations
11
+
12
+ import argparse
13
+ import csv
14
+ import os
15
+ import sys
16
+ import urllib.parse
17
+ from pathlib import Path
18
+
19
+ sys.path.insert(0, str(Path(__file__).parent))
20
+ from _http import get_json # noqa: E402
21
+
22
+ BASE = "https://www.courtlistener.com/api/rest/v4/search/"
23
+
24
+ COLUMNS = [
25
+ "case_name",
26
+ "court",
27
+ "court_id",
28
+ "date_filed",
29
+ "docket_number",
30
+ "judge",
31
+ "citation",
32
+ "result_type",
33
+ "snippet",
34
+ "absolute_url",
35
+ ]
36
+
37
+ SEARCH_TYPES = {
38
+ "opinions": "o", # Court opinions
39
+ "dockets": "r", # PACER dockets (may require auth depending on coverage)
40
+ "oral": "oa", # Oral arguments
41
+ "people": "p", # Judges / people
42
+ "recap": "r", # Same as dockets in v4
43
+ }
44
+
45
+
46
+ def fetch(
47
+ query: str,
48
+ search_type: str,
49
+ court: str | None,
50
+ date_from: str | None,
51
+ date_to: str | None,
52
+ token: str | None,
53
+ limit: int,
54
+ out_path: str,
55
+ ) -> int:
56
+ type_code = SEARCH_TYPES.get(search_type, search_type)
57
+ params = {
58
+ "q": query,
59
+ "type": type_code,
60
+ }
61
+ if court:
62
+ params["court"] = court
63
+ if date_from:
64
+ params["filed_after"] = date_from
65
+ if date_to:
66
+ params["filed_before"] = date_to
67
+ headers = {"Authorization": f"Token {token}"} if token else None
68
+
69
+ rows: list[dict[str, str]] = []
70
+ next_url: str | None = f"{BASE}?{urllib.parse.urlencode(params)}"
71
+ while next_url and len(rows) < limit:
72
+ try:
73
+ payload = get_json(next_url, headers=headers)
74
+ except Exception as e: # noqa: BLE001
75
+ print(f"CourtListener error: {e}", file=sys.stderr)
76
+ break
77
+ if not isinstance(payload, dict):
78
+ break
79
+ results = payload.get("results", [])
80
+ for r in results:
81
+ if len(rows) >= limit:
82
+ break
83
+ rows.append(
84
+ {
85
+ "case_name": r.get("caseName", "") or r.get("case_name", "") or "",
86
+ "court": r.get("court", "") or "",
87
+ "court_id": r.get("court_id", "") or "",
88
+ "date_filed": (r.get("dateFiled", "") or r.get("date_filed", "") or "")[:10],
89
+ "docket_number": r.get("docketNumber", "") or r.get("docket_number", "") or "",
90
+ "judge": r.get("judge", "") or "",
91
+ "citation": "; ".join(r.get("citation", []) or []) if isinstance(r.get("citation"), list) else (r.get("citation") or ""),
92
+ "result_type": search_type,
93
+ "snippet": (r.get("snippet", "") or "").replace("\n", " ")[:500],
94
+ "absolute_url": (
95
+ f"https://www.courtlistener.com{r.get('absolute_url', '')}"
96
+ if r.get("absolute_url", "").startswith("/")
97
+ else r.get("absolute_url", "")
98
+ ),
99
+ }
100
+ )
101
+ next_url = payload.get("next")
102
+
103
+ Path(out_path).parent.mkdir(parents=True, exist_ok=True)
104
+ with open(out_path, "w", newline="", encoding="utf-8") as fh:
105
+ w = csv.DictWriter(fh, fieldnames=COLUMNS)
106
+ w.writeheader()
107
+ w.writerows(rows)
108
+ if not rows:
109
+ print(
110
+ f"CourtListener: 0 results for type={search_type!r} q={query!r}. "
111
+ "Most private individuals don't appear in published court records "
112
+ "unless they were party to a federal or state appellate case.",
113
+ file=sys.stderr,
114
+ )
115
+ return len(rows)
116
+
117
+
118
+ def main() -> int:
119
+ p = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter)
120
+ p.add_argument("--query", required=True, help="Search query (party name, case name, keyword)")
121
+ p.add_argument(
122
+ "--type",
123
+ default="opinions",
124
+ choices=list(SEARCH_TYPES.keys()),
125
+ help="Search type (default: opinions)",
126
+ )
127
+ p.add_argument("--court", help="Court ID filter (e.g. 'nysd' = SDNY, 'scotus' = Supreme Court)")
128
+ p.add_argument("--date-from", help="Filed-after date YYYY-MM-DD")
129
+ p.add_argument("--date-to", help="Filed-before date YYYY-MM-DD")
130
+ p.add_argument("--token", default=os.environ.get("COURTLISTENER_TOKEN"))
131
+ p.add_argument("--limit", type=int, default=100)
132
+ p.add_argument("--out", required=True)
133
+ a = p.parse_args()
134
+ n = fetch(
135
+ query=a.query,
136
+ search_type=a.type,
137
+ court=a.court,
138
+ date_from=a.date_from,
139
+ date_to=a.date_to,
140
+ token=a.token,
141
+ limit=a.limit,
142
+ out_path=a.out,
143
+ )
144
+ print(f"Wrote {n} CourtListener rows to {a.out}")
145
+ return 0
146
+
147
+
148
+ if __name__ == "__main__":
149
+ raise SystemExit(main())