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,170 @@
1
+ #!/usr/bin/env python3
2
+ """Fetch federal contracts/awards from USAspending.gov API v2.
3
+
4
+ No auth required. POST to /api/v2/search/spending_by_award/ with filters.
5
+ """
6
+ from __future__ import annotations
7
+
8
+ import argparse
9
+ import csv
10
+ import json
11
+ import sys
12
+ import time
13
+ import urllib.request
14
+ from pathlib import Path
15
+
16
+ ENDPOINT = "https://api.usaspending.gov/api/v2/search/spending_by_award/"
17
+ COLUMNS = [
18
+ "award_id",
19
+ "recipient_name",
20
+ "recipient_uei",
21
+ "recipient_duns",
22
+ "recipient_parent_name",
23
+ "recipient_state",
24
+ "awarding_agency",
25
+ "awarding_sub_agency",
26
+ "award_type",
27
+ "award_amount",
28
+ "award_date",
29
+ "period_of_performance_start",
30
+ "period_of_performance_end",
31
+ "naics_code",
32
+ "psc_code",
33
+ "competition_extent",
34
+ "description",
35
+ ]
36
+
37
+ # USAspending result column "code" → human label mapping for output.
38
+ _FIELDS = [
39
+ "Award ID",
40
+ "Recipient Name",
41
+ "Recipient UEI",
42
+ "Recipient DUNS Number",
43
+ "Recipient Parent Name",
44
+ "Recipient State Code",
45
+ "Awarding Agency",
46
+ "Awarding Sub Agency",
47
+ "Award Type",
48
+ "Award Amount",
49
+ "Start Date",
50
+ "End Date",
51
+ "NAICS Code",
52
+ "PSC Code",
53
+ "Type of Set Aside",
54
+ "Description",
55
+ ]
56
+
57
+
58
+ def _post(body: dict) -> dict:
59
+ req = urllib.request.Request(
60
+ ENDPOINT,
61
+ data=json.dumps(body).encode("utf-8"),
62
+ headers={"Content-Type": "application/json", "User-Agent": "hermes-agent osint-investigation"},
63
+ method="POST",
64
+ )
65
+ with urllib.request.urlopen(req, timeout=60) as resp:
66
+ return json.loads(resp.read().decode("utf-8"))
67
+
68
+
69
+ def fetch(
70
+ recipient: str | None,
71
+ agency: str | None,
72
+ fy: int,
73
+ sole_source_only: bool,
74
+ out_path: str,
75
+ page_size: int = 100,
76
+ max_pages: int = 20,
77
+ ) -> int:
78
+ filters: dict = {
79
+ "time_period": [{"start_date": f"{fy - 1}-10-01", "end_date": f"{fy}-09-30"}],
80
+ # Contracts only by default; adjust award_type_codes for grants/loans.
81
+ "award_type_codes": ["A", "B", "C", "D"],
82
+ }
83
+ if recipient:
84
+ filters["recipient_search_text"] = [recipient]
85
+ if agency:
86
+ filters["agencies"] = [{"type": "awarding", "tier": "toptier", "name": agency}]
87
+
88
+ rows: list[dict[str, str]] = []
89
+ page = 1
90
+ while page <= max_pages:
91
+ body = {
92
+ "filters": filters,
93
+ "fields": _FIELDS,
94
+ "page": page,
95
+ "limit": page_size,
96
+ "sort": "Award Amount",
97
+ "order": "desc",
98
+ }
99
+ try:
100
+ payload = _post(body)
101
+ except Exception as e: # noqa: BLE001
102
+ print(f"USAspending error on page {page}: {e}", file=sys.stderr)
103
+ break
104
+ results = payload.get("results", [])
105
+ if not results:
106
+ break
107
+ for r in results:
108
+ set_aside = r.get("Type of Set Aside", "") or ""
109
+ if sole_source_only and "sole" not in set_aside.lower():
110
+ continue
111
+ rows.append(
112
+ {
113
+ "award_id": r.get("Award ID", "") or "",
114
+ "recipient_name": r.get("Recipient Name", "") or "",
115
+ "recipient_uei": r.get("Recipient UEI", "") or "",
116
+ "recipient_duns": r.get("Recipient DUNS Number", "") or "",
117
+ "recipient_parent_name": r.get("Recipient Parent Name", "") or "",
118
+ "recipient_state": r.get("Recipient State Code", "") or "",
119
+ "awarding_agency": r.get("Awarding Agency", "") or "",
120
+ "awarding_sub_agency": r.get("Awarding Sub Agency", "") or "",
121
+ "award_type": r.get("Award Type", "") or "",
122
+ "award_amount": str(r.get("Award Amount", "") or ""),
123
+ "award_date": r.get("Start Date", "") or "",
124
+ "period_of_performance_start": r.get("Start Date", "") or "",
125
+ "period_of_performance_end": r.get("End Date", "") or "",
126
+ "naics_code": str(r.get("NAICS Code", "") or ""),
127
+ "psc_code": str(r.get("PSC Code", "") or ""),
128
+ "competition_extent": set_aside,
129
+ "description": r.get("Description", "") or "",
130
+ }
131
+ )
132
+ meta = payload.get("page_metadata", {})
133
+ if not meta.get("hasNext"):
134
+ break
135
+ page += 1
136
+ time.sleep(0.5)
137
+
138
+ Path(out_path).parent.mkdir(parents=True, exist_ok=True)
139
+ with open(out_path, "w", newline="", encoding="utf-8") as fh:
140
+ w = csv.DictWriter(fh, fieldnames=COLUMNS)
141
+ w.writeheader()
142
+ w.writerows(rows)
143
+ return len(rows)
144
+
145
+
146
+ def main() -> int:
147
+ p = argparse.ArgumentParser(description=__doc__)
148
+ p.add_argument("--recipient", help="Recipient name search")
149
+ p.add_argument("--agency", help="Awarding agency (top-tier)")
150
+ p.add_argument("--fy", type=int, default=2024, help="Federal fiscal year")
151
+ p.add_argument("--sole-source-only", action="store_true")
152
+ p.add_argument("--max-pages", type=int, default=20)
153
+ p.add_argument("--out", required=True)
154
+ a = p.parse_args()
155
+ if not (a.recipient or a.agency):
156
+ p.error("must supply at least one of --recipient / --agency")
157
+ n = fetch(
158
+ recipient=a.recipient,
159
+ agency=a.agency,
160
+ fy=a.fy,
161
+ sole_source_only=a.sole_source_only,
162
+ out_path=a.out,
163
+ max_pages=a.max_pages,
164
+ )
165
+ print(f"Wrote {n} USAspending rows to {a.out}")
166
+ return 0
167
+
168
+
169
+ if __name__ == "__main__":
170
+ raise SystemExit(main())
@@ -0,0 +1,142 @@
1
+ #!/usr/bin/env python3
2
+ """Search the Internet Archive Wayback Machine via the CDX server.
3
+
4
+ The CDX API indexes ~900B+ archived web pages. Anonymous read access,
5
+ no auth required. Useful for finding deleted / changed pages by URL,
6
+ domain, or substring match.
7
+ """
8
+ from __future__ import annotations
9
+
10
+ import argparse
11
+ import csv
12
+ import sys
13
+ import urllib.parse
14
+ from pathlib import Path
15
+
16
+ sys.path.insert(0, str(Path(__file__).parent))
17
+ from _http import get_json # noqa: E402
18
+
19
+ BASE = "https://web.archive.org/cdx/search/cdx"
20
+
21
+ COLUMNS = [
22
+ "url",
23
+ "timestamp",
24
+ "wayback_url",
25
+ "mimetype",
26
+ "status",
27
+ "digest",
28
+ "length",
29
+ ]
30
+
31
+
32
+ def fetch(
33
+ url_or_host: str,
34
+ match_type: str,
35
+ from_date: str | None,
36
+ to_date: str | None,
37
+ status: str | None,
38
+ mime: str | None,
39
+ collapse: str | None,
40
+ limit: int,
41
+ out_path: str,
42
+ ) -> int:
43
+ params: dict[str, str] = {
44
+ "url": url_or_host,
45
+ "matchType": match_type,
46
+ "output": "json",
47
+ "limit": str(limit),
48
+ }
49
+ if from_date:
50
+ params["from"] = from_date.replace("-", "")
51
+ if to_date:
52
+ params["to"] = to_date.replace("-", "")
53
+ if status:
54
+ params["filter"] = f"statuscode:{status}"
55
+ if mime:
56
+ params.setdefault("filter", "")
57
+ # Multiple filters: CDX accepts repeated filter params via urlencode list
58
+ params["filter"] = f"mimetype:{mime}"
59
+ if collapse:
60
+ params["collapse"] = collapse
61
+
62
+ url = f"{BASE}?{urllib.parse.urlencode(params)}"
63
+ try:
64
+ payload = get_json(url)
65
+ except Exception as e: # noqa: BLE001
66
+ print(f"Wayback CDX error: {e}", file=sys.stderr)
67
+ payload = []
68
+
69
+ rows: list[dict[str, str]] = []
70
+ if isinstance(payload, list) and len(payload) > 1:
71
+ header = payload[0]
72
+ idx = {h: i for i, h in enumerate(header)}
73
+ for entry in payload[1:]:
74
+ ts = entry[idx["timestamp"]] if "timestamp" in idx else ""
75
+ orig = entry[idx["original"]] if "original" in idx else ""
76
+ rows.append(
77
+ {
78
+ "url": orig,
79
+ "timestamp": ts,
80
+ "wayback_url": f"https://web.archive.org/web/{ts}/{orig}" if ts and orig else "",
81
+ "mimetype": entry[idx["mimetype"]] if "mimetype" in idx else "",
82
+ "status": entry[idx["statuscode"]] if "statuscode" in idx else "",
83
+ "digest": entry[idx["digest"]] if "digest" in idx else "",
84
+ "length": entry[idx["length"]] if "length" in idx else "",
85
+ }
86
+ )
87
+
88
+ Path(out_path).parent.mkdir(parents=True, exist_ok=True)
89
+ with open(out_path, "w", newline="", encoding="utf-8") as fh:
90
+ w = csv.DictWriter(fh, fieldnames=COLUMNS)
91
+ w.writeheader()
92
+ w.writerows(rows)
93
+ if not rows:
94
+ print(
95
+ f"Wayback Machine: 0 captures for {url_or_host!r} matchType={match_type}.",
96
+ file=sys.stderr,
97
+ )
98
+ return len(rows)
99
+
100
+
101
+ def main() -> int:
102
+ p = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter)
103
+ p.add_argument("--url", required=True, help="URL or host to look up in the archive")
104
+ p.add_argument(
105
+ "--match",
106
+ default="exact",
107
+ choices=["exact", "prefix", "host", "domain"],
108
+ help=(
109
+ "exact: this URL only. "
110
+ "prefix: this URL's path-prefix. "
111
+ "host: any URL on this host. "
112
+ "domain: any URL on this domain or subdomains."
113
+ ),
114
+ )
115
+ p.add_argument("--from-date", help="Earliest capture YYYY-MM-DD")
116
+ p.add_argument("--to-date", help="Latest capture YYYY-MM-DD")
117
+ p.add_argument("--status", help="HTTP status filter (e.g. 200)")
118
+ p.add_argument("--mime", help="MIME type filter (e.g. text/html)")
119
+ p.add_argument(
120
+ "--collapse",
121
+ help="Collapse adjacent identical entries (e.g. 'digest' for unique-content captures)",
122
+ )
123
+ p.add_argument("--limit", type=int, default=200)
124
+ p.add_argument("--out", required=True)
125
+ a = p.parse_args()
126
+ n = fetch(
127
+ url_or_host=a.url,
128
+ match_type=a.match,
129
+ from_date=a.from_date,
130
+ to_date=a.to_date,
131
+ status=a.status,
132
+ mime=a.mime,
133
+ collapse=a.collapse,
134
+ limit=a.limit,
135
+ out_path=a.out,
136
+ )
137
+ print(f"Wrote {n} Wayback capture rows to {a.out}")
138
+ return 0
139
+
140
+
141
+ if __name__ == "__main__":
142
+ raise SystemExit(main())
@@ -0,0 +1,266 @@
1
+ #!/usr/bin/env python3
2
+ """Search Wikipedia + Wikidata for an entity (person, company, place, concept).
3
+
4
+ Two free APIs:
5
+ - Wikipedia OpenSearch + REST summary endpoint for narrative bio
6
+ - Wikidata SPARQL endpoint for structured facts (birth, employer, awards, etc.)
7
+
8
+ Both are anonymous-access. Useful for resolving who-is-this-entity questions
9
+ and surfacing cross-references that other sources can join against.
10
+ """
11
+ from __future__ import annotations
12
+
13
+ import argparse
14
+ import csv
15
+ import re
16
+ import sys
17
+ import urllib.parse
18
+ from pathlib import Path
19
+
20
+ sys.path.insert(0, str(Path(__file__).parent))
21
+ from _http import get_json # noqa: E402
22
+
23
+ WP_OPENSEARCH = "https://en.wikipedia.org/w/api.php"
24
+ WP_SUMMARY = "https://en.wikipedia.org/api/rest_v1/page/summary/"
25
+ WD_ACTION = "https://www.wikidata.org/w/api.php"
26
+
27
+ COLUMNS = [
28
+ "source",
29
+ "label",
30
+ "description",
31
+ "qid",
32
+ "wikipedia_title",
33
+ "wikipedia_url",
34
+ "wikidata_url",
35
+ "instance_of",
36
+ "country",
37
+ "occupation",
38
+ "employer",
39
+ "date_of_birth",
40
+ "place_of_birth",
41
+ "summary",
42
+ ]
43
+
44
+
45
+ def _wp_search(query: str, limit: int) -> list[dict]:
46
+ params = {
47
+ "action": "opensearch",
48
+ "search": query,
49
+ "limit": str(min(limit, 20)),
50
+ "format": "json",
51
+ }
52
+ url = f"{WP_OPENSEARCH}?{urllib.parse.urlencode(params)}"
53
+ data = get_json(url)
54
+ if not isinstance(data, list) or len(data) < 4:
55
+ return []
56
+ titles, descs, urls = data[1], data[2], data[3]
57
+ out = []
58
+ for i, title in enumerate(titles):
59
+ out.append(
60
+ {
61
+ "title": title,
62
+ "description": descs[i] if i < len(descs) else "",
63
+ "url": urls[i] if i < len(urls) else "",
64
+ }
65
+ )
66
+ return out
67
+
68
+
69
+ def _wp_summary(title: str) -> dict:
70
+ """Pull the REST summary for a title — short bio, image, type."""
71
+ url = f"{WP_SUMMARY}{urllib.parse.quote(title.replace(' ', '_'))}"
72
+ try:
73
+ return get_json(url) # type: ignore[return-value]
74
+ except Exception as e: # noqa: BLE001
75
+ print(f"Wikipedia summary lookup for {title!r} failed: {e}", file=sys.stderr)
76
+ return {}
77
+
78
+
79
+ def _wd_lookup_by_qid(qid: str) -> dict:
80
+ """Pull common facts for a QID via Wikidata's Action API (no SPARQL).
81
+
82
+ The Action API is far more lenient on rate-limits than the SPARQL Query
83
+ Service. We get claims as QIDs and then resolve labels in one batch call.
84
+ """
85
+ # Properties of interest. The Action API returns claims as QIDs or
86
+ # typed literals, so the slot mapping is local-only.
87
+ interesting = {
88
+ "P31": "instance_of",
89
+ "P17": "country", # for orgs / places
90
+ "P27": "country", # for individuals (country of citizenship)
91
+ "P106": "occupation",
92
+ "P108": "employer",
93
+ "P569": "date_of_birth",
94
+ "P19": "place_of_birth",
95
+ }
96
+ params = {
97
+ "action": "wbgetentities",
98
+ "ids": qid,
99
+ "props": "claims",
100
+ "format": "json",
101
+ }
102
+ url = f"{WD_ACTION}?{urllib.parse.urlencode(params)}"
103
+ try:
104
+ data = get_json(url)
105
+ except Exception as e: # noqa: BLE001
106
+ print(f"Wikidata wbgetentities for {qid} failed: {e}", file=sys.stderr)
107
+ return {}
108
+ if not isinstance(data, dict):
109
+ return {}
110
+ claims = (data.get("entities", {}).get(qid, {}) or {}).get("claims", {}) or {}
111
+
112
+ # Collect raw values (QIDs or literals) and remember which slot each
113
+ # came from. Date literals come back as ISO strings; QIDs need a label
114
+ # resolution pass.
115
+ qid_to_slots: dict[str, list[str]] = {}
116
+ facts: dict[str, list[str]] = {}
117
+ for prop_id, slot in interesting.items():
118
+ for claim in claims.get(prop_id, []) or []:
119
+ v = (claim.get("mainsnak", {}) or {}).get("datavalue", {}) or {}
120
+ vtype = v.get("type")
121
+ value = v.get("value")
122
+ if vtype == "wikibase-entityid" and isinstance(value, dict):
123
+ vqid = value.get("id", "")
124
+ if vqid:
125
+ qid_to_slots.setdefault(vqid, [])
126
+ if slot not in qid_to_slots[vqid]:
127
+ qid_to_slots[vqid].append(slot)
128
+ elif vtype == "time" and isinstance(value, dict):
129
+ raw = value.get("time", "") or ""
130
+ # +1955-10-28T00:00:00Z → 1955-10-28
131
+ m = re.search(r"[+-]?(\d{4})-(\d{2})-(\d{2})", raw)
132
+ if m:
133
+ facts.setdefault(slot, []).append(
134
+ f"{m.group(1)}-{m.group(2)}-{m.group(3)}"
135
+ )
136
+ elif vtype == "string":
137
+ facts.setdefault(slot, []).append(str(value))
138
+
139
+ # Resolve labels for all referenced QIDs in one batch (up to 50 at a time).
140
+ qids = list(qid_to_slots)
141
+ for i in range(0, len(qids), 50):
142
+ batch = qids[i : i + 50]
143
+ params = {
144
+ "action": "wbgetentities",
145
+ "ids": "|".join(batch),
146
+ "props": "labels",
147
+ "languages": "en",
148
+ "format": "json",
149
+ }
150
+ url = f"{WD_ACTION}?{urllib.parse.urlencode(params)}"
151
+ try:
152
+ data = get_json(url)
153
+ except Exception as e: # noqa: BLE001
154
+ print(f"Wikidata label batch failed: {e}", file=sys.stderr)
155
+ continue
156
+ if not isinstance(data, dict):
157
+ continue
158
+ ents = data.get("entities", {}) or {}
159
+ for vqid, ent in ents.items():
160
+ label = (ent.get("labels", {}).get("en", {}) or {}).get("value", "") or vqid
161
+ for slot in qid_to_slots.get(vqid, []):
162
+ facts.setdefault(slot, []).append(label)
163
+
164
+ # Deduplicate per slot, preserving order.
165
+ deduped: dict[str, list[str]] = {}
166
+ for slot, vals in facts.items():
167
+ seen = set()
168
+ out = []
169
+ for v in vals:
170
+ if v in seen:
171
+ continue
172
+ seen.add(v)
173
+ out.append(v)
174
+ deduped[slot] = out
175
+ return deduped
176
+
177
+
178
+ def _wd_qid_for_title(title: str) -> str:
179
+ """Get the Wikidata QID associated with a Wikipedia article title."""
180
+ params = {
181
+ "action": "query",
182
+ "format": "json",
183
+ "prop": "pageprops",
184
+ "ppprop": "wikibase_item",
185
+ "titles": title,
186
+ "redirects": 1,
187
+ }
188
+ url = f"{WP_OPENSEARCH}?{urllib.parse.urlencode(params)}"
189
+ try:
190
+ data = get_json(url)
191
+ except Exception: # noqa: BLE001
192
+ return ""
193
+ if not isinstance(data, dict):
194
+ return ""
195
+ pages = data.get("query", {}).get("pages", {}) or {}
196
+ for page in pages.values():
197
+ qid = (page.get("pageprops") or {}).get("wikibase_item", "")
198
+ if qid:
199
+ return qid
200
+ return ""
201
+
202
+
203
+ def fetch(query: str, limit: int, no_wikidata: bool, out_path: str) -> int:
204
+ hits = _wp_search(query, limit)
205
+ rows: list[dict[str, str]] = []
206
+ for hit in hits[:limit]:
207
+ title = hit.get("title", "")
208
+ if not title:
209
+ continue
210
+ summary = _wp_summary(title)
211
+ qid = _wd_qid_for_title(title) if not no_wikidata else ""
212
+ facts: dict = {}
213
+ if qid:
214
+ facts = _wd_lookup_by_qid(qid)
215
+ rows.append(
216
+ {
217
+ "source": "wikipedia+wikidata" if qid else "wikipedia",
218
+ "label": title,
219
+ "description": (summary.get("description") or hit.get("description") or "").strip(),
220
+ "qid": qid,
221
+ "wikipedia_title": title,
222
+ "wikipedia_url": hit.get("url", ""),
223
+ "wikidata_url": f"https://www.wikidata.org/wiki/{qid}" if qid else "",
224
+ "instance_of": "; ".join(facts.get("instance_of", [])),
225
+ "country": "; ".join(facts.get("country", [])),
226
+ "occupation": "; ".join(facts.get("occupation", [])),
227
+ "employer": "; ".join(facts.get("employer", [])),
228
+ "date_of_birth": "; ".join(facts.get("date_of_birth", []))[:10] if facts.get("date_of_birth") else "",
229
+ "place_of_birth": "; ".join(facts.get("place_of_birth", [])),
230
+ "summary": (summary.get("extract") or "").replace("\n", " ")[:1000],
231
+ }
232
+ )
233
+
234
+ Path(out_path).parent.mkdir(parents=True, exist_ok=True)
235
+ with open(out_path, "w", newline="", encoding="utf-8") as fh:
236
+ w = csv.DictWriter(fh, fieldnames=COLUMNS)
237
+ w.writeheader()
238
+ w.writerows(rows)
239
+ if not rows:
240
+ print(
241
+ f"Wikipedia: 0 articles for query={query!r}. "
242
+ "Private individuals not notable enough for a Wikipedia article "
243
+ "won't appear here (the bar is real).",
244
+ file=sys.stderr,
245
+ )
246
+ return len(rows)
247
+
248
+
249
+ def main() -> int:
250
+ p = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter)
251
+ p.add_argument("--query", required=True, help="Entity name (person, company, place, concept)")
252
+ p.add_argument("--limit", type=int, default=5)
253
+ p.add_argument(
254
+ "--no-wikidata",
255
+ action="store_true",
256
+ help="Skip the Wikidata SPARQL enrichment (faster, less detail)",
257
+ )
258
+ p.add_argument("--out", required=True)
259
+ a = p.parse_args()
260
+ n = fetch(query=a.query, limit=a.limit, no_wikidata=a.no_wikidata, out_path=a.out)
261
+ print(f"Wrote {n} Wikipedia/Wikidata rows to {a.out}")
262
+ return 0
263
+
264
+
265
+ if __name__ == "__main__":
266
+ raise SystemExit(main())