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.
- package/.agent/mindforge/wf-orchestrate-review.md +35 -0
- package/.agent/mindforge/wf-verification-loop.md +45 -0
- package/.agent/mindforge/wf-workflow-optimizer.md +51 -0
- package/.agent/skills/article-illustrator/SKILL.md +7 -7
- package/.agent/skills/arxiv/SKILL.md +6 -6
- package/.agent/skills/code-wiki/SKILL.md +11 -11
- package/.agent/skills/concept-diagrams/SKILL.md +6 -6
- package/.agent/skills/concept-diagrams/examples/apartment-floor-plan-conversion.md +244 -0
- package/.agent/skills/concept-diagrams/examples/automated-password-reset-flow.md +276 -0
- package/.agent/skills/concept-diagrams/examples/autonomous-llm-research-agent-flow.md +240 -0
- package/.agent/skills/concept-diagrams/examples/banana-journey-tree-to-smoothie.md +161 -0
- package/.agent/skills/concept-diagrams/examples/commercial-aircraft-structure.md +209 -0
- package/.agent/skills/concept-diagrams/examples/cpu-ooo-microarchitecture.md +236 -0
- package/.agent/skills/concept-diagrams/examples/electricity-grid-flow.md +182 -0
- package/.agent/skills/concept-diagrams/examples/feature-film-production-pipeline.md +172 -0
- package/.agent/skills/concept-diagrams/examples/hospital-emergency-department-flow.md +165 -0
- package/.agent/skills/concept-diagrams/examples/ml-benchmark-grouped-bar-chart.md +114 -0
- package/.agent/skills/concept-diagrams/examples/place-order-uml-sequence.md +325 -0
- package/.agent/skills/concept-diagrams/examples/smart-city-infrastructure.md +173 -0
- package/.agent/skills/concept-diagrams/examples/smartphone-layer-anatomy.md +154 -0
- package/.agent/skills/concept-diagrams/examples/sn2-reaction-mechanism.md +247 -0
- package/.agent/skills/concept-diagrams/examples/wind-turbine-structure.md +338 -0
- package/.agent/skills/concept-diagrams/templates/template.html +174 -0
- package/.agent/skills/domain-intel/SKILL.md +7 -7
- package/.agent/skills/duckduckgo-search/SKILL.md +17 -17
- package/.agent/skills/github-auth/scripts/gh-env.sh +66 -0
- package/.agent/skills/github-code-review/SKILL.md +9 -9
- package/.agent/skills/github-pr-workflow/SKILL.md +3 -3
- package/.agent/skills/godmode/SKILL.md +8 -8
- package/.agent/skills/jupyter-live-kernel/SKILL.md +3 -3
- package/.agent/skills/kanban-orchestrator/SKILL.md +10 -10
- package/.agent/skills/kanban-worker/SKILL.md +6 -6
- package/.agent/skills/llm-wiki/SKILL.md +7 -7
- package/.agent/skills/mindforge-autonomous/SKILL.md +1 -1
- package/.agent/skills/mindforge-complete-milestone/SKILL.md +1 -1
- package/.agent/skills/mindforge-discuss-phase/SKILL.md +1 -1
- package/.agent/skills/mindforge-do/SKILL.md +1 -1
- package/.agent/skills/mindforge-execute-phase/SKILL.md +1 -1
- package/.agent/skills/mindforge-list-workspaces/SKILL.md +1 -1
- package/.agent/skills/mindforge-manager/SKILL.md +1 -1
- package/.agent/skills/mindforge-neural-orchestrator/SKILL.md +1 -1
- package/.agent/skills/mindforge-new-milestone/SKILL.md +4 -4
- package/.agent/skills/mindforge-new-project/SKILL.md +4 -4
- package/.agent/skills/mindforge-new-workspace/SKILL.md +1 -1
- package/.agent/skills/mindforge-note/SKILL.md +1 -1
- package/.agent/skills/mindforge-plan-phase/SKILL.md +1 -1
- package/.agent/skills/mindforge-profile-user/SKILL.md +1 -1
- package/.agent/skills/mindforge-remove-workspace/SKILL.md +1 -1
- package/.agent/skills/mindforge-research-phase/SKILL.md +1 -1
- package/.agent/skills/mindforge-ui-phase/SKILL.md +1 -1
- package/.agent/skills/mindforge-ui-review/SKILL.md +1 -1
- package/.agent/skills/mindforge-verify-work/SKILL.md +1 -1
- package/.agent/skills/mindforge-workspace-isolated/SKILL.md +1 -1
- package/.agent/skills/node-inspect-debugger/SKILL.md +1 -1
- package/.agent/skills/obsidian/SKILL.md +15 -15
- package/.agent/skills/osint-investigation/SKILL.md +3 -2
- package/.agent/skills/osint-investigation/references/sources/courtlistener.md +98 -0
- package/.agent/skills/osint-investigation/references/sources/gdelt.md +104 -0
- package/.agent/skills/osint-investigation/references/sources/icij-offshore.md +104 -0
- package/.agent/skills/osint-investigation/references/sources/nyc-acris.md +90 -0
- package/.agent/skills/osint-investigation/references/sources/ofac-sdn.md +92 -0
- package/.agent/skills/osint-investigation/references/sources/opencorporates.md +103 -0
- package/.agent/skills/osint-investigation/references/sources/sec-edgar.md +83 -0
- package/.agent/skills/osint-investigation/references/sources/senate-ld.md +89 -0
- package/.agent/skills/osint-investigation/references/sources/usaspending.md +97 -0
- package/.agent/skills/osint-investigation/references/sources/wayback.md +93 -0
- package/.agent/skills/osint-investigation/references/sources/wikipedia.md +107 -0
- package/.agent/skills/osint-investigation/scripts/_http.py +82 -0
- package/.agent/skills/osint-investigation/scripts/_normalize.py +67 -0
- package/.agent/skills/osint-investigation/scripts/build_findings.py +221 -0
- package/.agent/skills/osint-investigation/scripts/entity_resolution.py +228 -0
- package/.agent/skills/osint-investigation/scripts/fetch_courtlistener.py +149 -0
- package/.agent/skills/osint-investigation/scripts/fetch_gdelt.py +161 -0
- package/.agent/skills/osint-investigation/scripts/fetch_icij_offshore.py +234 -0
- package/.agent/skills/osint-investigation/scripts/fetch_nyc_acris.py +203 -0
- package/.agent/skills/osint-investigation/scripts/fetch_ofac_sdn.py +175 -0
- package/.agent/skills/osint-investigation/scripts/fetch_opencorporates.py +191 -0
- package/.agent/skills/osint-investigation/scripts/fetch_sec_edgar.py +184 -0
- package/.agent/skills/osint-investigation/scripts/fetch_senate_ld.py +146 -0
- package/.agent/skills/osint-investigation/scripts/fetch_usaspending.py +170 -0
- package/.agent/skills/osint-investigation/scripts/fetch_wayback.py +142 -0
- package/.agent/skills/osint-investigation/scripts/fetch_wikipedia.py +266 -0
- package/.agent/skills/osint-investigation/scripts/timing_analysis.py +252 -0
- package/.agent/skills/oss-forensics/SKILL.md +10 -9
- package/.agent/skills/oss-forensics/scripts/evidence-store.py +313 -0
- package/.agent/skills/parallel-cli/SKILL.md +2 -2
- package/.agent/skills/pinggy-tunnel/SKILL.md +3 -3
- package/.agent/skills/pixel-art/SKILL.md +2 -2
- package/.agent/skills/plan/SKILL.md +10 -10
- package/.agent/skills/requesting-code-review/SKILL.md +4 -6
- package/.agent/skills/research-paper-writing/SKILL.md +32 -32
- package/.agent/skills/rest-graphql-debug/SKILL.md +31 -32
- package/.agent/skills/s6-container-supervision/SKILL.md +1 -1
- package/.agent/skills/scrapling/SKILL.md +1 -1
- package/.agent/skills/sherlock/SKILL.md +1 -1
- package/.agent/skills/simplify-code/SKILL.md +5 -7
- package/.agent/skills/skill-authoring/SKILL.md +4 -4
- package/.agent/skills/spike/SKILL.md +11 -13
- package/.agent/skills/subagent-driven-development/SKILL.md +7 -11
- package/.agent/skills/systematic-debugging/SKILL.md +13 -14
- package/.agent/skills/test-driven-development/SKILL.md +6 -7
- package/.agent/skills/video-orchestrator/SKILL.md +1 -1
- package/.agent/skills/web-pentest/SKILL.md +7 -7
- package/.mindforge/config.json +3 -2
- package/.mindforge/dynamic-workflows/REGISTRY.md +4 -1
- package/.mindforge/dynamic-workflows/index.json +910 -840
- package/.mindforge/dynamic-workflows/scripts/accessibility-audit.js +88 -90
- package/.mindforge/dynamic-workflows/scripts/ai-model-eval.js +59 -61
- package/.mindforge/dynamic-workflows/scripts/api-contract-test.js +87 -85
- package/.mindforge/dynamic-workflows/scripts/api-migration.js +120 -122
- package/.mindforge/dynamic-workflows/scripts/architecture-modernization.js +84 -86
- package/.mindforge/dynamic-workflows/scripts/code-audit.js +75 -77
- package/.mindforge/dynamic-workflows/scripts/code-explainer.js +99 -101
- package/.mindforge/dynamic-workflows/scripts/competitive-analysis.js +62 -64
- package/.mindforge/dynamic-workflows/scripts/competitive-teardown.js +106 -108
- package/.mindforge/dynamic-workflows/scripts/cost-analysis.js +78 -80
- package/.mindforge/dynamic-workflows/scripts/data-pipeline-validate.js +47 -49
- package/.mindforge/dynamic-workflows/scripts/database-migration.js +85 -87
- package/.mindforge/dynamic-workflows/scripts/debug-detective.js +97 -99
- package/.mindforge/dynamic-workflows/scripts/dependency-health.js +81 -83
- package/.mindforge/dynamic-workflows/scripts/design-system-audit.js +84 -86
- package/.mindforge/dynamic-workflows/scripts/documentation-gen.js +65 -67
- package/.mindforge/dynamic-workflows/scripts/feature-planner.js +78 -80
- package/.mindforge/dynamic-workflows/scripts/incident-response.js +81 -83
- package/.mindforge/dynamic-workflows/scripts/multi-repo-sync.js +49 -51
- package/.mindforge/dynamic-workflows/scripts/mutation-testing.js +113 -115
- package/.mindforge/dynamic-workflows/scripts/onboard-codebase.js +79 -81
- package/.mindforge/dynamic-workflows/scripts/orchestrate-review.js +115 -0
- package/.mindforge/dynamic-workflows/scripts/perf-optimize.js +97 -99
- package/.mindforge/dynamic-workflows/scripts/pr-review.js +61 -63
- package/.mindforge/dynamic-workflows/scripts/refactor-plan.js +88 -90
- package/.mindforge/dynamic-workflows/scripts/release-prep.js +79 -81
- package/.mindforge/dynamic-workflows/scripts/security-hardening.js +117 -119
- package/.mindforge/dynamic-workflows/scripts/security-threat-model.js +119 -121
- package/.mindforge/dynamic-workflows/scripts/tdd-sprint.js +80 -82
- package/.mindforge/dynamic-workflows/scripts/tech-evaluation.js +50 -52
- package/.mindforge/dynamic-workflows/scripts/test-coverage-gap.js +68 -70
- package/.mindforge/dynamic-workflows/scripts/ux-heuristic-audit.js +89 -91
- package/.mindforge/dynamic-workflows/scripts/verification-loop.js +138 -0
- package/.mindforge/dynamic-workflows/scripts/workflow-optimizer.js +136 -0
- package/.mindforge/dynamic-workflows/scripts/writer-reviewer.js +63 -65
- package/.mindforge/memory/sync-manifest.json +1 -1
- package/.mindforge/personas/advisor-researcher.md +2 -2
- package/.mindforge/personas/api-load-tester.md +1 -1
- package/.mindforge/personas/authentication-architect.md +1 -1
- package/.mindforge/personas/backup-recovery-specialist.md +1 -1
- package/.mindforge/personas/build-optimizer.md +1 -1
- package/.mindforge/personas/caching-strategist.md +1 -1
- package/.mindforge/personas/chaos-engineer.md +1 -1
- package/.mindforge/personas/compliance-auditor.md +1 -1
- package/.mindforge/personas/config-management-expert.md +1 -1
- package/.mindforge/personas/coverage-specialist.md +1 -1
- package/.mindforge/personas/debug-specialist.md +2 -2
- package/.mindforge/personas/debugger.md +2 -2
- package/.mindforge/personas/dependency-auditor.md +1 -1
- package/.mindforge/personas/developer.md +1 -1
- package/.mindforge/personas/devops-engineer.md +1 -1
- package/.mindforge/personas/executor.md +1 -1
- package/.mindforge/personas/go-specialist.md +1 -1
- package/.mindforge/personas/incident-commander.md +1 -1
- package/.mindforge/personas/java-specialist.md +1 -1
- package/.mindforge/personas/kubernetes-debugger.md +1 -1
- package/.mindforge/personas/logging-architect.md +1 -1
- package/.mindforge/personas/mf-planner.md +1 -1
- package/.mindforge/personas/mf-researcher.md +1 -1
- package/.mindforge/personas/mf-reviewer.md +1 -1
- package/.mindforge/personas/nyquist-auditor.md +1 -1
- package/.mindforge/personas/observability-engineer.md +1 -1
- package/.mindforge/personas/performance-optimizer.md +1 -1
- package/.mindforge/personas/phase-researcher.md +5 -5
- package/.mindforge/personas/planner.md +1 -1
- package/.mindforge/personas/project-researcher.md +2 -2
- package/.mindforge/personas/python-specialist.md +1 -1
- package/.mindforge/personas/qa-engineer.md +1 -1
- package/.mindforge/personas/release-manager.md +1 -1
- package/.mindforge/personas/research-agent.md +4 -4
- package/.mindforge/personas/roadmapper-extend.md +1 -1
- package/.mindforge/personas/rust-specialist.md +1 -1
- package/.mindforge/personas/security-reviewer.md +1 -1
- package/.mindforge/personas/tech-writer.md +1 -1
- package/.mindforge/personas/typescript-wizard.md +1 -1
- package/.mindforge/personas/ui-researcher.md +1 -1
- package/.mindforge/skills/accessibility/SKILL.md +3 -3
- package/.mindforge/skills/agent-architecture-audit/SKILL.md +11 -0
- package/.mindforge/skills/agent-loops/SKILL.md +1 -1
- package/.mindforge/skills/arxiv/SKILL.md +6 -6
- package/.mindforge/skills/arxiv/scripts/search_arxiv.py +114 -0
- package/.mindforge/skills/code-tour/SKILL.md +2 -2
- package/.mindforge/skills/code-wiki/SKILL.md +11 -11
- package/.mindforge/skills/code-wiki/templates/README.md +31 -0
- package/.mindforge/skills/code-wiki/templates/architecture.md +30 -0
- package/.mindforge/skills/code-wiki/templates/getting-started.md +47 -0
- package/.mindforge/skills/code-wiki/templates/module.md +38 -0
- package/.mindforge/skills/concept-diagrams/SKILL.md +6 -6
- package/.mindforge/skills/concept-diagrams/examples/apartment-floor-plan-conversion.md +244 -0
- package/.mindforge/skills/concept-diagrams/examples/automated-password-reset-flow.md +276 -0
- package/.mindforge/skills/concept-diagrams/examples/autonomous-llm-research-agent-flow.md +240 -0
- package/.mindforge/skills/concept-diagrams/examples/banana-journey-tree-to-smoothie.md +161 -0
- package/.mindforge/skills/concept-diagrams/examples/commercial-aircraft-structure.md +209 -0
- package/.mindforge/skills/concept-diagrams/examples/cpu-ooo-microarchitecture.md +236 -0
- package/.mindforge/skills/concept-diagrams/examples/electricity-grid-flow.md +182 -0
- package/.mindforge/skills/concept-diagrams/examples/feature-film-production-pipeline.md +172 -0
- package/.mindforge/skills/concept-diagrams/examples/hospital-emergency-department-flow.md +165 -0
- package/.mindforge/skills/concept-diagrams/examples/ml-benchmark-grouped-bar-chart.md +114 -0
- package/.mindforge/skills/concept-diagrams/examples/place-order-uml-sequence.md +325 -0
- package/.mindforge/skills/concept-diagrams/examples/smart-city-infrastructure.md +173 -0
- package/.mindforge/skills/concept-diagrams/examples/smartphone-layer-anatomy.md +154 -0
- package/.mindforge/skills/concept-diagrams/examples/sn2-reaction-mechanism.md +247 -0
- package/.mindforge/skills/concept-diagrams/examples/wind-turbine-structure.md +338 -0
- package/.mindforge/skills/concept-diagrams/references/dashboard-patterns.md +43 -0
- package/.mindforge/skills/concept-diagrams/references/infrastructure-patterns.md +144 -0
- package/.mindforge/skills/concept-diagrams/references/physical-shape-cookbook.md +42 -0
- package/.mindforge/skills/concept-diagrams/templates/template.html +174 -0
- package/.mindforge/skills/creative-ideation/references/full-prompt-library.md +110 -0
- package/.mindforge/skills/documentation/SKILL.md +1 -1
- package/.mindforge/skills/domain-intel/SKILL.md +7 -7
- package/.mindforge/skills/domain-intel/scripts/domain_intel.py +397 -0
- package/.mindforge/skills/duckduckgo-search/SKILL.md +17 -17
- package/.mindforge/skills/duckduckgo-search/scripts/duckduckgo.sh +28 -0
- package/.mindforge/skills/environment-management/SKILL.md +1 -1
- package/.mindforge/skills/github-code-review/SKILL.md +9 -9
- package/.mindforge/skills/github-code-review/references/review-output-template.md +74 -0
- package/.mindforge/skills/github-issues/templates/bug-report.md +35 -0
- package/.mindforge/skills/github-issues/templates/feature-request.md +31 -0
- package/.mindforge/skills/github-pr-workflow/SKILL.md +3 -3
- package/.mindforge/skills/github-pr-workflow/references/ci-troubleshooting.md +183 -0
- package/.mindforge/skills/github-pr-workflow/references/conventional-commits.md +71 -0
- package/.mindforge/skills/github-pr-workflow/templates/pr-body-bugfix.md +35 -0
- package/.mindforge/skills/github-pr-workflow/templates/pr-body-feature.md +33 -0
- package/.mindforge/skills/incident-response/SKILL.md +1 -1
- package/.mindforge/skills/jupyter-live-kernel/SKILL.md +3 -3
- package/.mindforge/skills/kanban-orchestrator/SKILL.md +10 -10
- package/.mindforge/skills/kanban-worker/SKILL.md +6 -6
- package/.mindforge/skills/meme-generation/SKILL.md +1 -1
- package/.mindforge/skills/meme-generation/scripts/generate_meme.py +470 -0
- package/.mindforge/skills/meme-generation/scripts/templates.json +97 -0
- package/.mindforge/skills/obsidian/SKILL.md +15 -15
- package/.mindforge/skills/orch-pipeline/SKILL.md +2 -2
- package/.mindforge/skills/osint-investigation/SKILL.md +3 -2
- package/.mindforge/skills/osint-investigation/references/sources/courtlistener.md +98 -0
- package/.mindforge/skills/osint-investigation/references/sources/gdelt.md +104 -0
- package/.mindforge/skills/osint-investigation/references/sources/icij-offshore.md +104 -0
- package/.mindforge/skills/osint-investigation/references/sources/nyc-acris.md +90 -0
- package/.mindforge/skills/osint-investigation/references/sources/ofac-sdn.md +92 -0
- package/.mindforge/skills/osint-investigation/references/sources/opencorporates.md +103 -0
- package/.mindforge/skills/osint-investigation/references/sources/sec-edgar.md +83 -0
- package/.mindforge/skills/osint-investigation/references/sources/senate-ld.md +89 -0
- package/.mindforge/skills/osint-investigation/references/sources/usaspending.md +97 -0
- package/.mindforge/skills/osint-investigation/references/sources/wayback.md +93 -0
- package/.mindforge/skills/osint-investigation/references/sources/wikipedia.md +107 -0
- package/.mindforge/skills/osint-investigation/scripts/_http.py +82 -0
- package/.mindforge/skills/osint-investigation/scripts/_normalize.py +67 -0
- package/.mindforge/skills/osint-investigation/scripts/build_findings.py +221 -0
- package/.mindforge/skills/osint-investigation/scripts/entity_resolution.py +228 -0
- package/.mindforge/skills/osint-investigation/scripts/fetch_courtlistener.py +149 -0
- package/.mindforge/skills/osint-investigation/scripts/fetch_gdelt.py +161 -0
- package/.mindforge/skills/osint-investigation/scripts/fetch_icij_offshore.py +234 -0
- package/.mindforge/skills/osint-investigation/scripts/fetch_nyc_acris.py +203 -0
- package/.mindforge/skills/osint-investigation/scripts/fetch_ofac_sdn.py +175 -0
- package/.mindforge/skills/osint-investigation/scripts/fetch_opencorporates.py +191 -0
- package/.mindforge/skills/osint-investigation/scripts/fetch_sec_edgar.py +184 -0
- package/.mindforge/skills/osint-investigation/scripts/fetch_senate_ld.py +146 -0
- package/.mindforge/skills/osint-investigation/scripts/fetch_usaspending.py +170 -0
- package/.mindforge/skills/osint-investigation/scripts/fetch_wayback.py +142 -0
- package/.mindforge/skills/osint-investigation/scripts/fetch_wikipedia.py +266 -0
- package/.mindforge/skills/osint-investigation/scripts/timing_analysis.py +252 -0
- package/.mindforge/skills/osint-investigation/templates/source-template.md +59 -0
- package/.mindforge/skills/oss-forensics/SKILL.md +6 -6
- package/.mindforge/skills/oss-forensics/references/evidence-types.md +89 -0
- package/.mindforge/skills/oss-forensics/references/github-archive-guide.md +184 -0
- package/.mindforge/skills/oss-forensics/references/investigation-templates.md +131 -0
- package/.mindforge/skills/oss-forensics/references/recovery-techniques.md +164 -0
- package/.mindforge/skills/oss-forensics/scripts/evidence-store.py +313 -0
- package/.mindforge/skills/oss-forensics/templates/forensic-report.md +151 -0
- package/.mindforge/skills/oss-forensics/templates/malicious-package-report.md +43 -0
- package/.mindforge/skills/pixel-art/SKILL.md +2 -2
- package/.mindforge/skills/pixel-art/references/palettes.md +49 -0
- package/.mindforge/skills/plan/SKILL.md +10 -10
- package/.mindforge/skills/requesting-code-review/SKILL.md +4 -6
- package/.mindforge/skills/research-paper-writing/SKILL.md +32 -32
- package/.mindforge/skills/research-paper-writing/references/autoreason-methodology.md +394 -0
- package/.mindforge/skills/research-paper-writing/references/checklists.md +434 -0
- package/.mindforge/skills/research-paper-writing/references/citation-workflow.md +563 -0
- package/.mindforge/skills/research-paper-writing/references/experiment-patterns.md +728 -0
- package/.mindforge/skills/research-paper-writing/references/human-evaluation.md +476 -0
- package/.mindforge/skills/research-paper-writing/references/paper-types.md +481 -0
- package/.mindforge/skills/research-paper-writing/references/reviewer-guidelines.md +433 -0
- package/.mindforge/skills/research-paper-writing/references/sources.md +191 -0
- package/.mindforge/skills/research-paper-writing/references/writing-guide.md +474 -0
- package/.mindforge/skills/research-paper-writing/templates/README.md +251 -0
- package/.mindforge/skills/scrapling/SKILL.md +1 -1
- package/.mindforge/skills/security-review/SKILL.md +1 -1
- package/.mindforge/skills/sherlock/SKILL.md +1 -1
- package/.mindforge/skills/simplify-code/SKILL.md +5 -7
- package/.mindforge/skills/spike/SKILL.md +11 -13
- package/.mindforge/skills/subagent-driven-development/SKILL.md +7 -11
- package/.mindforge/skills/subagent-driven-development/references/context-budget-discipline.md +53 -0
- package/.mindforge/skills/subagent-driven-development/references/gates-taxonomy.md +93 -0
- package/.mindforge/skills/systematic-debugging/SKILL.md +13 -14
- package/.mindforge/skills/test-driven-development/SKILL.md +7 -8
- package/.mindforge/skills/testing-standards/SKILL.md +1 -1
- package/.mindforge/skills/web-pentest/SKILL.md +2 -2
- package/.mindforge/skills/web-pentest/references/bypass-techniques.md +133 -0
- package/.mindforge/skills/web-pentest/references/exploitation-techniques.md +204 -0
- package/.mindforge/skills/web-pentest/references/scope-enforcement.md +110 -0
- package/.mindforge/skills/web-pentest/references/vuln-taxonomy.md +81 -0
- package/.mindforge/skills/web-pentest/templates/authorization.md +69 -0
- package/.mindforge/skills/web-pentest/templates/pentest-report.md +178 -0
- package/.mindforge/skills/writing-skills/SKILL.md +4 -3
- package/CHANGELOG.md +35 -0
- package/MINDFORGE.md +3 -3
- package/README.md +9 -7
- package/RELEASENOTES.md +65 -2
- package/SECURITY.md +5 -3
- package/bin/autonomous/auto-runner.js +5 -2
- package/bin/engine/skill-loader.js +4 -0
- package/bin/governance/rbac.js +4 -0
- package/bin/memory/eis-client.js +1 -0
- package/bin/mindforge-cli.js +5 -0
- package/bin/models/bedrock-provider.js +192 -0
- package/bin/models/model-client.js +19 -0
- package/bin/spawn-agent.js +9 -2
- package/docs/commands-reference.md +68 -6
- package/docs/faq.md +38 -1
- package/docs/getting-started.md +27 -12
- package/docs/sdk-reference.md +36 -3
- package/docs/troubleshooting.md +59 -1
- package/docs/user-guide.md +7 -4
- package/package.json +3 -3
- package/.claude/commands/mindforge/skill-tdd.md +0 -53
- package/.claude/commands/mindforge/skills-index.md +0 -118
- package/.claude/commands/mindforge/systematic-debug.md +0 -60
- package/.claude/commands/mindforge/wf-accessibility-audit.md +0 -31
- package/.claude/commands/mindforge/wf-ai-model-eval.md +0 -31
- package/.claude/commands/mindforge/wf-api-migration.md +0 -31
- package/.claude/commands/mindforge/wf-architecture-modernization.md +0 -32
- package/.claude/commands/mindforge/wf-catalog.md +0 -59
- package/.claude/commands/mindforge/wf-code-audit.md +0 -31
- package/.claude/commands/mindforge/wf-code-explainer.md +0 -31
- package/.claude/commands/mindforge/wf-competitive-analysis.md +0 -31
- package/.claude/commands/mindforge/wf-competitive-teardown.md +0 -31
- package/.claude/commands/mindforge/wf-cost-analysis.md +0 -31
- package/.claude/commands/mindforge/wf-data-pipeline-validate.md +0 -31
- package/.claude/commands/mindforge/wf-database-migration.md +0 -31
- package/.claude/commands/mindforge/wf-debug-detective.md +0 -32
- package/.claude/commands/mindforge/wf-design-system-audit.md +0 -31
- package/.claude/commands/mindforge/wf-documentation-gen.md +0 -31
- package/.claude/commands/mindforge/wf-feature-planner.md +0 -31
- package/.claude/commands/mindforge/wf-incident-response.md +0 -31
- package/.claude/commands/mindforge/wf-multi-repo-sync.md +0 -31
- package/.claude/commands/mindforge/wf-mutation-testing.md +0 -31
- package/.claude/commands/mindforge/wf-onboard-codebase.md +0 -31
- package/.claude/commands/mindforge/wf-perf-optimize.md +0 -31
- package/.claude/commands/mindforge/wf-pr-review.md +0 -31
- package/.claude/commands/mindforge/wf-refactor-plan.md +0 -31
- package/.claude/commands/mindforge/wf-release-prep.md +0 -31
- package/.claude/commands/mindforge/wf-security-threat-model.md +0 -31
- package/.claude/commands/mindforge/wf-tdd-sprint.md +0 -31
- package/.claude/commands/mindforge/wf-tech-evaluation.md +0 -31
- package/.claude/commands/mindforge/wf-ux-heuristic-audit.md +0 -31
- package/.claude/commands/mindforge/wf-writer-reviewer.md +0 -30
|
@@ -0,0 +1,470 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Generate a meme image by overlaying text on a template.
|
|
3
|
+
|
|
4
|
+
Usage:
|
|
5
|
+
python generate_meme.py <template_id_or_name> <output_path> <text1> [text2] [text3] [text4]
|
|
6
|
+
|
|
7
|
+
Example:
|
|
8
|
+
python generate_meme.py drake /tmp/meme.png "Writing tests" "Shipping to prod and hoping"
|
|
9
|
+
python generate_meme.py "Disaster Girl" /tmp/meme.png "Top text" "Bottom text"
|
|
10
|
+
python generate_meme.py --list # show curated templates
|
|
11
|
+
python generate_meme.py --search "distracted" # search all imgflip templates
|
|
12
|
+
|
|
13
|
+
Templates with custom text positioning are in templates.json (10 curated).
|
|
14
|
+
Any of the ~100 popular imgflip templates can also be used by name or ID —
|
|
15
|
+
unknown templates get smart default text positioning based on their box_count.
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
import json
|
|
19
|
+
import os
|
|
20
|
+
import sys
|
|
21
|
+
from io import BytesIO
|
|
22
|
+
from pathlib import Path
|
|
23
|
+
|
|
24
|
+
try:
|
|
25
|
+
import requests as _requests
|
|
26
|
+
except ImportError:
|
|
27
|
+
_requests = None
|
|
28
|
+
|
|
29
|
+
from PIL import Image, ImageDraw, ImageFont
|
|
30
|
+
|
|
31
|
+
SCRIPT_DIR = Path(__file__).parent
|
|
32
|
+
TEMPLATES_FILE = SCRIPT_DIR / "templates.json"
|
|
33
|
+
CACHE_DIR = SCRIPT_DIR / ".cache"
|
|
34
|
+
IMGFLIP_API = "https://api.imgflip.com/get_memes"
|
|
35
|
+
IMGFLIP_CACHE_FILE = CACHE_DIR / "imgflip_memes.json"
|
|
36
|
+
IMGFLIP_CACHE_MAX_AGE = 86400 # 24 hours
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def _fetch_url(url: str, timeout: int = 15) -> bytes:
|
|
40
|
+
"""Fetch URL content, using requests if available, else urllib."""
|
|
41
|
+
if _requests is not None:
|
|
42
|
+
resp = _requests.get(url, timeout=timeout)
|
|
43
|
+
resp.raise_for_status()
|
|
44
|
+
return resp.content
|
|
45
|
+
import urllib.request
|
|
46
|
+
return urllib.request.urlopen(url, timeout=timeout).read()
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def load_curated_templates() -> dict:
|
|
50
|
+
"""Load templates with hand-tuned text field positions."""
|
|
51
|
+
with open(TEMPLATES_FILE) as f:
|
|
52
|
+
return json.load(f)
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def _default_fields(box_count: int) -> list:
|
|
56
|
+
"""Generate sensible default text field positions for unknown templates."""
|
|
57
|
+
if box_count <= 0:
|
|
58
|
+
box_count = 2
|
|
59
|
+
if box_count == 1:
|
|
60
|
+
return [{"name": "text", "x_pct": 0.5, "y_pct": 0.5, "w_pct": 0.90, "align": "center"}]
|
|
61
|
+
if box_count == 2:
|
|
62
|
+
return [
|
|
63
|
+
{"name": "top", "x_pct": 0.5, "y_pct": 0.08, "w_pct": 0.95, "align": "center"},
|
|
64
|
+
{"name": "bottom", "x_pct": 0.5, "y_pct": 0.92, "w_pct": 0.95, "align": "center"},
|
|
65
|
+
]
|
|
66
|
+
# 3+: evenly space vertically
|
|
67
|
+
fields = []
|
|
68
|
+
for i in range(box_count):
|
|
69
|
+
y = 0.08 + (0.84 * i / (box_count - 1)) if box_count > 1 else 0.5
|
|
70
|
+
fields.append({
|
|
71
|
+
"name": f"text{i+1}",
|
|
72
|
+
"x_pct": 0.5,
|
|
73
|
+
"y_pct": round(y, 2),
|
|
74
|
+
"w_pct": 0.90,
|
|
75
|
+
"align": "center",
|
|
76
|
+
})
|
|
77
|
+
return fields
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def fetch_imgflip_templates() -> list:
|
|
81
|
+
"""Fetch popular meme templates from imgflip API. Cached for 24h."""
|
|
82
|
+
import time
|
|
83
|
+
|
|
84
|
+
CACHE_DIR.mkdir(exist_ok=True)
|
|
85
|
+
# Check cache
|
|
86
|
+
if IMGFLIP_CACHE_FILE.exists():
|
|
87
|
+
age = time.time() - IMGFLIP_CACHE_FILE.stat().st_mtime
|
|
88
|
+
if age < IMGFLIP_CACHE_MAX_AGE:
|
|
89
|
+
with open(IMGFLIP_CACHE_FILE) as f:
|
|
90
|
+
return json.load(f)
|
|
91
|
+
|
|
92
|
+
try:
|
|
93
|
+
data = json.loads(_fetch_url(IMGFLIP_API))
|
|
94
|
+
memes = data.get("data", {}).get("memes", [])
|
|
95
|
+
with open(IMGFLIP_CACHE_FILE, "w") as f:
|
|
96
|
+
json.dump(memes, f)
|
|
97
|
+
return memes
|
|
98
|
+
except Exception as e:
|
|
99
|
+
# If fetch fails and we have stale cache, use it
|
|
100
|
+
if IMGFLIP_CACHE_FILE.exists():
|
|
101
|
+
with open(IMGFLIP_CACHE_FILE) as f:
|
|
102
|
+
return json.load(f)
|
|
103
|
+
print(f"Warning: could not fetch imgflip templates: {e}", file=sys.stderr)
|
|
104
|
+
return []
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def _slugify(name: str) -> str:
|
|
108
|
+
"""Convert a template name to a slug for matching."""
|
|
109
|
+
return name.lower().replace(" ", "-").replace("'", "").replace("\"", "")
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def resolve_template(identifier: str) -> dict:
|
|
113
|
+
"""Resolve a template by curated ID, imgflip name, or imgflip ID.
|
|
114
|
+
|
|
115
|
+
Returns dict with: name, url, fields, source.
|
|
116
|
+
"""
|
|
117
|
+
curated = load_curated_templates()
|
|
118
|
+
|
|
119
|
+
# 1. Exact curated ID match
|
|
120
|
+
if identifier in curated:
|
|
121
|
+
tmpl = curated[identifier]
|
|
122
|
+
return {**tmpl, "source": "curated"}
|
|
123
|
+
|
|
124
|
+
# 2. Slugified curated match
|
|
125
|
+
slug = _slugify(identifier)
|
|
126
|
+
for tid, tmpl in curated.items():
|
|
127
|
+
if _slugify(tmpl["name"]) == slug or tid == slug:
|
|
128
|
+
return {**tmpl, "source": "curated"}
|
|
129
|
+
|
|
130
|
+
# 3. Search imgflip templates
|
|
131
|
+
imgflip_memes = fetch_imgflip_templates()
|
|
132
|
+
slug_lower = slug.lower()
|
|
133
|
+
id_lower = identifier.strip()
|
|
134
|
+
|
|
135
|
+
for meme in imgflip_memes:
|
|
136
|
+
meme_slug = _slugify(meme["name"])
|
|
137
|
+
# Check curated first for this imgflip template (custom positioning)
|
|
138
|
+
for tid, ctmpl in curated.items():
|
|
139
|
+
if _slugify(ctmpl["name"]) == meme_slug:
|
|
140
|
+
if meme_slug == slug_lower or meme["id"] == id_lower:
|
|
141
|
+
return {**ctmpl, "source": "curated"}
|
|
142
|
+
|
|
143
|
+
if meme_slug == slug_lower or meme["id"] == id_lower or slug_lower in meme_slug:
|
|
144
|
+
return {
|
|
145
|
+
"name": meme["name"],
|
|
146
|
+
"url": meme["url"],
|
|
147
|
+
"fields": _default_fields(meme.get("box_count", 2)),
|
|
148
|
+
"source": "imgflip",
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
return None
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
def get_template_image(url: str) -> Image.Image:
|
|
155
|
+
"""Download a template image, caching it locally."""
|
|
156
|
+
CACHE_DIR.mkdir(exist_ok=True)
|
|
157
|
+
# Use URL hash as cache key
|
|
158
|
+
cache_name = url.split("/")[-1]
|
|
159
|
+
cache_path = CACHE_DIR / cache_name
|
|
160
|
+
|
|
161
|
+
# Always cache as PNG to avoid JPEG/RGBA conflicts
|
|
162
|
+
cache_path = cache_path.with_suffix(".png")
|
|
163
|
+
|
|
164
|
+
if cache_path.exists():
|
|
165
|
+
return Image.open(cache_path).convert("RGBA")
|
|
166
|
+
|
|
167
|
+
data = _fetch_url(url)
|
|
168
|
+
img = Image.open(BytesIO(data)).convert("RGBA")
|
|
169
|
+
img.save(cache_path, "PNG")
|
|
170
|
+
return img
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
def find_font(size: int) -> ImageFont.FreeTypeFont:
|
|
174
|
+
"""Find a bold font for meme text. Tries Impact, then falls back."""
|
|
175
|
+
candidates = [
|
|
176
|
+
"/usr/share/fonts/truetype/msttcorefonts/Impact.ttf",
|
|
177
|
+
"/usr/share/fonts/truetype/liberation/LiberationSans-Bold.ttf",
|
|
178
|
+
"/usr/share/fonts/liberation-sans/LiberationSans-Bold.ttf",
|
|
179
|
+
"/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf",
|
|
180
|
+
"/usr/share/fonts/dejavu-sans/DejaVuSans-Bold.ttf",
|
|
181
|
+
"/System/Library/Fonts/Helvetica.ttc",
|
|
182
|
+
"/System/Library/Fonts/SFCompact.ttf",
|
|
183
|
+
]
|
|
184
|
+
for path in candidates:
|
|
185
|
+
if os.path.exists(path):
|
|
186
|
+
try:
|
|
187
|
+
return ImageFont.truetype(path, size)
|
|
188
|
+
except (OSError, IOError):
|
|
189
|
+
continue
|
|
190
|
+
# Last resort: Pillow default
|
|
191
|
+
try:
|
|
192
|
+
return ImageFont.truetype("DejaVuSans-Bold", size)
|
|
193
|
+
except (OSError, IOError):
|
|
194
|
+
return ImageFont.load_default()
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
def _wrap_text(text: str, font: ImageFont.FreeTypeFont, max_width: int) -> str:
|
|
198
|
+
"""Word-wrap text to fit within max_width pixels. Never breaks mid-word."""
|
|
199
|
+
words = text.split()
|
|
200
|
+
if not words:
|
|
201
|
+
return text
|
|
202
|
+
lines = []
|
|
203
|
+
current_line = words[0]
|
|
204
|
+
for word in words[1:]:
|
|
205
|
+
test_line = current_line + " " + word
|
|
206
|
+
if font.getlength(test_line) <= max_width:
|
|
207
|
+
current_line = test_line
|
|
208
|
+
else:
|
|
209
|
+
lines.append(current_line)
|
|
210
|
+
current_line = word
|
|
211
|
+
lines.append(current_line)
|
|
212
|
+
return "\n".join(lines)
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
def draw_outlined_text(
|
|
216
|
+
draw: ImageDraw.ImageDraw,
|
|
217
|
+
text: str,
|
|
218
|
+
x: int,
|
|
219
|
+
y: int,
|
|
220
|
+
font_size: int,
|
|
221
|
+
max_width: int,
|
|
222
|
+
align: str = "center",
|
|
223
|
+
):
|
|
224
|
+
"""Draw white text with black outline, auto-scaled to fit max_width."""
|
|
225
|
+
# Auto-scale: reduce font size until text fits reasonably
|
|
226
|
+
size = font_size
|
|
227
|
+
while size > 12:
|
|
228
|
+
font = find_font(size)
|
|
229
|
+
wrapped = _wrap_text(text, font, max_width)
|
|
230
|
+
bbox = draw.multiline_textbbox((0, 0), wrapped, font=font, align=align)
|
|
231
|
+
text_w = bbox[2] - bbox[0]
|
|
232
|
+
line_count = wrapped.count("\n") + 1
|
|
233
|
+
# Accept if width fits and not too many lines
|
|
234
|
+
if text_w <= max_width * 1.05 and line_count <= 4:
|
|
235
|
+
break
|
|
236
|
+
size -= 2
|
|
237
|
+
else:
|
|
238
|
+
font = find_font(size)
|
|
239
|
+
wrapped = _wrap_text(text, font, max_width)
|
|
240
|
+
|
|
241
|
+
# Measure total text block
|
|
242
|
+
bbox = draw.multiline_textbbox((0, 0), wrapped, font=font, align=align)
|
|
243
|
+
text_w = bbox[2] - bbox[0]
|
|
244
|
+
text_h = bbox[3] - bbox[1]
|
|
245
|
+
|
|
246
|
+
# Center horizontally at x, vertically at y
|
|
247
|
+
tx = x - text_w // 2
|
|
248
|
+
ty = y - text_h // 2
|
|
249
|
+
|
|
250
|
+
# Draw outline (black border)
|
|
251
|
+
outline_range = max(2, font.size // 18)
|
|
252
|
+
for dx in range(-outline_range, outline_range + 1):
|
|
253
|
+
for dy in range(-outline_range, outline_range + 1):
|
|
254
|
+
if dx == 0 and dy == 0:
|
|
255
|
+
continue
|
|
256
|
+
draw.multiline_text(
|
|
257
|
+
(tx + dx, ty + dy), wrapped, font=font, fill="black", align=align
|
|
258
|
+
)
|
|
259
|
+
# Draw main text (white)
|
|
260
|
+
draw.multiline_text((tx, ty), wrapped, font=font, fill="white", align=align)
|
|
261
|
+
|
|
262
|
+
|
|
263
|
+
def _overlay_on_image(img: Image.Image, texts: list, fields: list) -> Image.Image:
|
|
264
|
+
"""Overlay meme text directly on an image using field positions."""
|
|
265
|
+
draw = ImageDraw.Draw(img)
|
|
266
|
+
w, h = img.size
|
|
267
|
+
base_font_size = max(16, min(w, h) // 12)
|
|
268
|
+
|
|
269
|
+
for i, field in enumerate(fields):
|
|
270
|
+
if i >= len(texts):
|
|
271
|
+
break
|
|
272
|
+
text = texts[i].strip()
|
|
273
|
+
if not text:
|
|
274
|
+
continue
|
|
275
|
+
fx = int(field["x_pct"] * w)
|
|
276
|
+
fy = int(field["y_pct"] * h)
|
|
277
|
+
fw = int(field["w_pct"] * w)
|
|
278
|
+
draw_outlined_text(draw, text, fx, fy, base_font_size, fw, field.get("align", "center"))
|
|
279
|
+
return img
|
|
280
|
+
|
|
281
|
+
|
|
282
|
+
def _add_bars(img: Image.Image, texts: list) -> Image.Image:
|
|
283
|
+
"""Add black bars with white text above/below the image.
|
|
284
|
+
|
|
285
|
+
Distributes texts across bars: first text on top bar, last text on
|
|
286
|
+
bottom bar, any middle texts overlaid on the image center.
|
|
287
|
+
"""
|
|
288
|
+
w, h = img.size
|
|
289
|
+
bar_font_size = max(20, w // 16)
|
|
290
|
+
font = find_font(bar_font_size)
|
|
291
|
+
padding = bar_font_size // 2
|
|
292
|
+
|
|
293
|
+
top_text = texts[0].strip() if texts else ""
|
|
294
|
+
bottom_text = texts[-1].strip() if len(texts) > 1 else ""
|
|
295
|
+
middle_texts = [t.strip() for t in texts[1:-1]] if len(texts) > 2 else []
|
|
296
|
+
|
|
297
|
+
def _measure_bar(text: str) -> int:
|
|
298
|
+
if not text:
|
|
299
|
+
return 0
|
|
300
|
+
wrapped = _wrap_text(text, font, int(w * 0.92))
|
|
301
|
+
bbox = ImageDraw.Draw(Image.new("RGB", (1, 1))).multiline_textbbox(
|
|
302
|
+
(0, 0), wrapped, font=font, align="center"
|
|
303
|
+
)
|
|
304
|
+
return (bbox[3] - bbox[1]) + padding * 2
|
|
305
|
+
|
|
306
|
+
top_h = _measure_bar(top_text)
|
|
307
|
+
bottom_h = _measure_bar(bottom_text)
|
|
308
|
+
new_h = h + top_h + bottom_h
|
|
309
|
+
|
|
310
|
+
canvas = Image.new("RGB", (w, new_h), (0, 0, 0))
|
|
311
|
+
canvas.paste(img.convert("RGB"), (0, top_h))
|
|
312
|
+
draw = ImageDraw.Draw(canvas)
|
|
313
|
+
|
|
314
|
+
if top_text:
|
|
315
|
+
wrapped = _wrap_text(top_text, font, int(w * 0.92))
|
|
316
|
+
bbox = draw.multiline_textbbox((0, 0), wrapped, font=font, align="center")
|
|
317
|
+
tw = bbox[2] - bbox[0]
|
|
318
|
+
th = bbox[3] - bbox[1]
|
|
319
|
+
tx = (w - tw) // 2
|
|
320
|
+
ty = (top_h - th) // 2
|
|
321
|
+
draw.multiline_text((tx, ty), wrapped, font=font, fill="white", align="center")
|
|
322
|
+
|
|
323
|
+
if bottom_text:
|
|
324
|
+
wrapped = _wrap_text(bottom_text, font, int(w * 0.92))
|
|
325
|
+
bbox = draw.multiline_textbbox((0, 0), wrapped, font=font, align="center")
|
|
326
|
+
tw = bbox[2] - bbox[0]
|
|
327
|
+
th = bbox[3] - bbox[1]
|
|
328
|
+
tx = (w - tw) // 2
|
|
329
|
+
ty = top_h + h + (bottom_h - th) // 2
|
|
330
|
+
draw.multiline_text((tx, ty), wrapped, font=font, fill="white", align="center")
|
|
331
|
+
|
|
332
|
+
# Overlay any middle texts centered on the image
|
|
333
|
+
if middle_texts:
|
|
334
|
+
mid_fields = _default_fields(len(middle_texts))
|
|
335
|
+
# Shift y positions to account for top bar offset
|
|
336
|
+
for field in mid_fields:
|
|
337
|
+
field["y_pct"] = (top_h + field["y_pct"] * h) / new_h
|
|
338
|
+
field["w_pct"] = 0.90
|
|
339
|
+
_overlay_on_image(canvas, middle_texts, mid_fields)
|
|
340
|
+
|
|
341
|
+
return canvas
|
|
342
|
+
|
|
343
|
+
|
|
344
|
+
def generate_meme(template_id: str, texts: list[str], output_path: str) -> str:
|
|
345
|
+
"""Generate a meme from a template and save it. Returns the path."""
|
|
346
|
+
tmpl = resolve_template(template_id)
|
|
347
|
+
|
|
348
|
+
if tmpl is None:
|
|
349
|
+
print(f"Unknown template: {template_id}", file=sys.stderr)
|
|
350
|
+
print("Use --list to see curated templates or --search to find imgflip templates.", file=sys.stderr)
|
|
351
|
+
sys.exit(1)
|
|
352
|
+
|
|
353
|
+
fields = tmpl["fields"]
|
|
354
|
+
print(f"Using template: {tmpl['name']} ({tmpl['source']}, {len(fields)} fields)", file=sys.stderr)
|
|
355
|
+
|
|
356
|
+
img = get_template_image(tmpl["url"])
|
|
357
|
+
img = _overlay_on_image(img, texts, fields)
|
|
358
|
+
|
|
359
|
+
output = Path(output_path)
|
|
360
|
+
if output.suffix.lower() in {".jpg", ".jpeg"}:
|
|
361
|
+
img = img.convert("RGB")
|
|
362
|
+
img.save(str(output), quality=95)
|
|
363
|
+
return str(output)
|
|
364
|
+
|
|
365
|
+
|
|
366
|
+
def generate_from_image(
|
|
367
|
+
image_path: str, texts: list[str], output_path: str, use_bars: bool = False
|
|
368
|
+
) -> str:
|
|
369
|
+
"""Generate a meme from a custom image (e.g. AI-generated). Returns the path."""
|
|
370
|
+
img = Image.open(image_path).convert("RGBA")
|
|
371
|
+
print(f"Custom image: {img.size[0]}x{img.size[1]}, {len(texts)} text(s), mode={'bars' if use_bars else 'overlay'}", file=sys.stderr)
|
|
372
|
+
|
|
373
|
+
if use_bars:
|
|
374
|
+
result = _add_bars(img, texts)
|
|
375
|
+
else:
|
|
376
|
+
fields = _default_fields(len(texts))
|
|
377
|
+
result = _overlay_on_image(img, texts, fields)
|
|
378
|
+
|
|
379
|
+
output = Path(output_path)
|
|
380
|
+
if output.suffix.lower() in {".jpg", ".jpeg"}:
|
|
381
|
+
result = result.convert("RGB")
|
|
382
|
+
result.save(str(output), quality=95)
|
|
383
|
+
return str(output)
|
|
384
|
+
|
|
385
|
+
|
|
386
|
+
def list_templates():
|
|
387
|
+
"""Print curated templates with custom positioning."""
|
|
388
|
+
templates = load_curated_templates()
|
|
389
|
+
print(f"{'ID':<25} {'Name':<30} {'Fields':<8} Best for")
|
|
390
|
+
print("-" * 90)
|
|
391
|
+
for tid, tmpl in sorted(templates.items()):
|
|
392
|
+
fields = len(tmpl["fields"])
|
|
393
|
+
print(f"{tid:<25} {tmpl['name']:<30} {fields:<8} {tmpl['best_for']}")
|
|
394
|
+
print(f"\n{len(templates)} curated templates with custom text positioning.")
|
|
395
|
+
print("Use --search to find any of the ~100 popular imgflip templates.")
|
|
396
|
+
|
|
397
|
+
|
|
398
|
+
def search_templates(query: str):
|
|
399
|
+
"""Search imgflip templates by name."""
|
|
400
|
+
imgflip_memes = fetch_imgflip_templates()
|
|
401
|
+
curated = load_curated_templates()
|
|
402
|
+
curated_slugs = {_slugify(t["name"]) for t in curated.values()}
|
|
403
|
+
query_lower = query.lower()
|
|
404
|
+
|
|
405
|
+
matches = []
|
|
406
|
+
for meme in imgflip_memes:
|
|
407
|
+
if query_lower in meme["name"].lower():
|
|
408
|
+
slug = _slugify(meme["name"])
|
|
409
|
+
has_custom = "curated" if slug in curated_slugs else "default"
|
|
410
|
+
matches.append((meme["name"], meme["id"], meme.get("box_count", 2), has_custom))
|
|
411
|
+
|
|
412
|
+
if not matches:
|
|
413
|
+
print(f"No templates found matching '{query}'")
|
|
414
|
+
return
|
|
415
|
+
|
|
416
|
+
print(f"{'Name':<40} {'ID':<12} {'Fields':<8} Positioning")
|
|
417
|
+
print("-" * 75)
|
|
418
|
+
for name, mid, boxes, positioning in matches:
|
|
419
|
+
print(f"{name:<40} {mid:<12} {boxes:<8} {positioning}")
|
|
420
|
+
print(f"\n{len(matches)} template(s) found. Use the name or ID as the first argument.")
|
|
421
|
+
|
|
422
|
+
|
|
423
|
+
if __name__ == "__main__":
|
|
424
|
+
if len(sys.argv) < 2:
|
|
425
|
+
print("Usage: generate_meme.py <template_id_or_name> <output_path> <text1> [text2] ...")
|
|
426
|
+
print(" generate_meme.py --image <path> [--bars] <output_path> <text1> [text2] ...")
|
|
427
|
+
print(" generate_meme.py --list # curated templates")
|
|
428
|
+
print(" generate_meme.py --search <query> # search all imgflip templates")
|
|
429
|
+
sys.exit(1)
|
|
430
|
+
|
|
431
|
+
if sys.argv[1] == "--list":
|
|
432
|
+
list_templates()
|
|
433
|
+
sys.exit(0)
|
|
434
|
+
|
|
435
|
+
if sys.argv[1] == "--search":
|
|
436
|
+
if len(sys.argv) < 3:
|
|
437
|
+
print("Usage: generate_meme.py --search <query>")
|
|
438
|
+
sys.exit(1)
|
|
439
|
+
search_templates(sys.argv[2])
|
|
440
|
+
sys.exit(0)
|
|
441
|
+
|
|
442
|
+
if sys.argv[1] == "--image":
|
|
443
|
+
# Custom image mode: --image <path> [--bars] <output> <text1> ...
|
|
444
|
+
args = sys.argv[2:]
|
|
445
|
+
if len(args) < 3:
|
|
446
|
+
print("Usage: generate_meme.py --image <image_path> [--bars] <output_path> <text1> ...")
|
|
447
|
+
sys.exit(1)
|
|
448
|
+
image_path = args.pop(0)
|
|
449
|
+
use_bars = False
|
|
450
|
+
if args and args[0] == "--bars":
|
|
451
|
+
use_bars = True
|
|
452
|
+
args.pop(0)
|
|
453
|
+
if len(args) < 2:
|
|
454
|
+
print("Need at least: output_path and one text argument")
|
|
455
|
+
sys.exit(1)
|
|
456
|
+
output_path = args.pop(0)
|
|
457
|
+
result = generate_from_image(image_path, args, output_path, use_bars=use_bars)
|
|
458
|
+
print(f"Meme saved to: {result}")
|
|
459
|
+
sys.exit(0)
|
|
460
|
+
|
|
461
|
+
if len(sys.argv) < 4:
|
|
462
|
+
print("Need at least: template_id_or_name, output_path, and one text argument")
|
|
463
|
+
sys.exit(1)
|
|
464
|
+
|
|
465
|
+
template_id = sys.argv[1]
|
|
466
|
+
output_path = sys.argv[2]
|
|
467
|
+
texts = sys.argv[3:]
|
|
468
|
+
|
|
469
|
+
result = generate_meme(template_id, texts, output_path)
|
|
470
|
+
print(f"Meme saved to: {result}")
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
{
|
|
2
|
+
"this-is-fine": {
|
|
3
|
+
"name": "This is Fine",
|
|
4
|
+
"url": "https://i.imgflip.com/wxica.jpg",
|
|
5
|
+
"best_for": "chaos, denial, pretending things are okay",
|
|
6
|
+
"fields": [
|
|
7
|
+
{"name": "top", "x_pct": 0.5, "y_pct": 0.08, "w_pct": 0.95, "align": "center"},
|
|
8
|
+
{"name": "bottom", "x_pct": 0.5, "y_pct": 0.92, "w_pct": 0.95, "align": "center"}
|
|
9
|
+
]
|
|
10
|
+
},
|
|
11
|
+
"drake": {
|
|
12
|
+
"name": "Drake Hotline Bling",
|
|
13
|
+
"url": "https://i.imgflip.com/30b1gx.jpg",
|
|
14
|
+
"best_for": "rejecting one thing, preferring another",
|
|
15
|
+
"fields": [
|
|
16
|
+
{"name": "reject", "x_pct": 0.73, "y_pct": 0.25, "w_pct": 0.45, "align": "center"},
|
|
17
|
+
{"name": "approve", "x_pct": 0.73, "y_pct": 0.75, "w_pct": 0.45, "align": "center"}
|
|
18
|
+
]
|
|
19
|
+
},
|
|
20
|
+
"distracted-boyfriend": {
|
|
21
|
+
"name": "Distracted Boyfriend",
|
|
22
|
+
"url": "https://i.imgflip.com/1ur9b0.jpg",
|
|
23
|
+
"best_for": "distraction, shifting priorities, temptation",
|
|
24
|
+
"fields": [
|
|
25
|
+
{"name": "distraction", "x_pct": 0.18, "y_pct": 0.90, "w_pct": 0.30, "align": "center"},
|
|
26
|
+
{"name": "current", "x_pct": 0.55, "y_pct": 0.90, "w_pct": 0.30, "align": "center"},
|
|
27
|
+
{"name": "person", "x_pct": 0.82, "y_pct": 0.90, "w_pct": 0.30, "align": "center"}
|
|
28
|
+
]
|
|
29
|
+
},
|
|
30
|
+
"two-buttons": {
|
|
31
|
+
"name": "Two Buttons",
|
|
32
|
+
"url": "https://i.imgflip.com/1g8my4.jpg",
|
|
33
|
+
"best_for": "impossible choice, dilemma between two options",
|
|
34
|
+
"fields": [
|
|
35
|
+
{"name": "left_button", "x_pct": 0.30, "y_pct": 0.20, "w_pct": 0.28, "align": "center"},
|
|
36
|
+
{"name": "right_button", "x_pct": 0.62, "y_pct": 0.12, "w_pct": 0.28, "align": "center"},
|
|
37
|
+
{"name": "person", "x_pct": 0.5, "y_pct": 0.85, "w_pct": 0.90, "align": "center"}
|
|
38
|
+
]
|
|
39
|
+
},
|
|
40
|
+
"expanding-brain": {
|
|
41
|
+
"name": "Expanding Brain",
|
|
42
|
+
"url": "https://i.imgflip.com/1jwhww.jpg",
|
|
43
|
+
"best_for": "escalating irony, increasingly absurd ideas",
|
|
44
|
+
"fields": [
|
|
45
|
+
{"name": "level1", "x_pct": 0.25, "y_pct": 0.12, "w_pct": 0.45, "align": "center"},
|
|
46
|
+
{"name": "level2", "x_pct": 0.25, "y_pct": 0.38, "w_pct": 0.45, "align": "center"},
|
|
47
|
+
{"name": "level3", "x_pct": 0.25, "y_pct": 0.63, "w_pct": 0.45, "align": "center"},
|
|
48
|
+
{"name": "level4", "x_pct": 0.25, "y_pct": 0.88, "w_pct": 0.45, "align": "center"}
|
|
49
|
+
]
|
|
50
|
+
},
|
|
51
|
+
"change-my-mind": {
|
|
52
|
+
"name": "Change My Mind",
|
|
53
|
+
"url": "https://i.imgflip.com/24y43o.jpg",
|
|
54
|
+
"best_for": "strong or ironic opinion, controversial take",
|
|
55
|
+
"fields": [
|
|
56
|
+
{"name": "statement", "x_pct": 0.58, "y_pct": 0.78, "w_pct": 0.35, "align": "center"}
|
|
57
|
+
]
|
|
58
|
+
},
|
|
59
|
+
"woman-yelling-at-cat": {
|
|
60
|
+
"name": "Woman Yelling at Cat",
|
|
61
|
+
"url": "https://i.imgflip.com/345v97.jpg",
|
|
62
|
+
"best_for": "argument, blame, misunderstanding",
|
|
63
|
+
"fields": [
|
|
64
|
+
{"name": "woman", "x_pct": 0.27, "y_pct": 0.10, "w_pct": 0.50, "align": "center"},
|
|
65
|
+
{"name": "cat", "x_pct": 0.76, "y_pct": 0.10, "w_pct": 0.44, "align": "center"}
|
|
66
|
+
]
|
|
67
|
+
},
|
|
68
|
+
"one-does-not-simply": {
|
|
69
|
+
"name": "One Does Not Simply",
|
|
70
|
+
"url": "https://i.imgflip.com/1bij.jpg",
|
|
71
|
+
"best_for": "something that sounds easy but is actually hard",
|
|
72
|
+
"fields": [
|
|
73
|
+
{"name": "top", "x_pct": 0.5, "y_pct": 0.08, "w_pct": 0.95, "align": "center"},
|
|
74
|
+
{"name": "bottom", "x_pct": 0.5, "y_pct": 0.92, "w_pct": 0.95, "align": "center"}
|
|
75
|
+
]
|
|
76
|
+
},
|
|
77
|
+
"grus-plan": {
|
|
78
|
+
"name": "Gru's Plan",
|
|
79
|
+
"url": "https://i.imgflip.com/26jxvs.jpg",
|
|
80
|
+
"best_for": "a plan that backfires, unexpected consequence",
|
|
81
|
+
"fields": [
|
|
82
|
+
{"name": "step1", "x_pct": 0.5, "y_pct": 0.05, "w_pct": 0.45, "align": "center"},
|
|
83
|
+
{"name": "step2", "x_pct": 0.5, "y_pct": 0.30, "w_pct": 0.45, "align": "center"},
|
|
84
|
+
{"name": "step3", "x_pct": 0.5, "y_pct": 0.55, "w_pct": 0.45, "align": "center"},
|
|
85
|
+
{"name": "realization", "x_pct": 0.5, "y_pct": 0.80, "w_pct": 0.45, "align": "center"}
|
|
86
|
+
]
|
|
87
|
+
},
|
|
88
|
+
"batman-slapping-robin": {
|
|
89
|
+
"name": "Batman Slapping Robin",
|
|
90
|
+
"url": "https://i.imgflip.com/9ehk.jpg",
|
|
91
|
+
"best_for": "shutting down a bad idea, correcting someone",
|
|
92
|
+
"fields": [
|
|
93
|
+
{"name": "robin", "x_pct": 0.28, "y_pct": 0.08, "w_pct": 0.50, "align": "center"},
|
|
94
|
+
{"name": "batman", "x_pct": 0.72, "y_pct": 0.08, "w_pct": 0.50, "align": "center"}
|
|
95
|
+
]
|
|
96
|
+
}
|
|
97
|
+
}
|
|
@@ -15,49 +15,49 @@ Use this skill for filesystem-first Obsidian vault work: reading notes, listing
|
|
|
15
15
|
|
|
16
16
|
Use a known or resolved vault path before calling file tools.
|
|
17
17
|
|
|
18
|
-
The documented vault-path convention is the `OBSIDIAN_VAULT_PATH` environment variable, for example from `${
|
|
18
|
+
The documented vault-path convention is the `OBSIDIAN_VAULT_PATH` environment variable, for example from `${MINDFORGE_HOME:-~/.mindforge}/.env`. If it is unset, use `~/Documents/Obsidian Vault`.
|
|
19
19
|
|
|
20
|
-
File tools do not expand shell variables. Do not pass paths containing `$OBSIDIAN_VAULT_PATH` to `
|
|
20
|
+
File tools do not expand shell variables. Do not pass paths containing `$OBSIDIAN_VAULT_PATH` to `Read`, `Write`, `Edit`, or `Grep`; resolve the vault path first and pass a concrete absolute path. Vault paths may contain spaces, which is another reason to prefer file tools over shell commands.
|
|
21
21
|
|
|
22
|
-
If the vault path is unknown, `
|
|
22
|
+
If the vault path is unknown, `Bash` is acceptable for resolving `OBSIDIAN_VAULT_PATH` or checking whether the fallback path exists. Once the path is known, switch back to file tools.
|
|
23
23
|
|
|
24
24
|
## Read a note
|
|
25
25
|
|
|
26
|
-
Use `
|
|
26
|
+
Use `Read` with the resolved absolute path to the note. Prefer this over `cat` because it provides line numbers and pagination.
|
|
27
27
|
|
|
28
28
|
## List notes
|
|
29
29
|
|
|
30
|
-
Use `
|
|
30
|
+
Use `Grep` with `target: "files"` and the resolved vault path. Prefer this over `find` or `ls`.
|
|
31
31
|
|
|
32
32
|
- To list all markdown notes, use `pattern: "*.md"` under the vault path.
|
|
33
33
|
- To list a subfolder, search under that subfolder's absolute path.
|
|
34
34
|
|
|
35
35
|
## Search
|
|
36
36
|
|
|
37
|
-
Use `
|
|
37
|
+
Use `Grep` for both filename and content searches. Prefer this over `grep`, `find`, or `ls`.
|
|
38
38
|
|
|
39
|
-
- For filenames, use `
|
|
40
|
-
- For note contents, use `
|
|
39
|
+
- For filenames, use `Grep` with `target: "files"` and a filename `pattern`.
|
|
40
|
+
- For note contents, use `Grep` with `target: "content"`, the content regex as `pattern`, and `file_glob: "*.md"` when you want to restrict matches to markdown notes.
|
|
41
41
|
|
|
42
42
|
## Create a note
|
|
43
43
|
|
|
44
|
-
Use `
|
|
44
|
+
Use `Write` with the resolved absolute path and the full markdown content. Prefer this over shell heredocs or `echo` because it avoids shell quoting issues and returns structured results.
|
|
45
45
|
|
|
46
46
|
## Append to a note
|
|
47
47
|
|
|
48
48
|
Prefer a native file-tool workflow when it is not awkward:
|
|
49
49
|
|
|
50
|
-
- Read the target note with `
|
|
51
|
-
- Use `
|
|
52
|
-
- Use `
|
|
50
|
+
- Read the target note with `Read`.
|
|
51
|
+
- Use `Edit` for an anchored append when there is stable context, such as adding a section after an existing heading or appending before a known trailing block.
|
|
52
|
+
- Use `Write` when rewriting the whole note is clearer than constructing a fragile patch.
|
|
53
53
|
|
|
54
|
-
For an anchored append with `
|
|
54
|
+
For an anchored append with `Edit`, replace the anchor with the anchor plus the new content.
|
|
55
55
|
|
|
56
|
-
For a simple append with no stable context, `
|
|
56
|
+
For a simple append with no stable context, `Bash` is acceptable if it is the clearest safe option.
|
|
57
57
|
|
|
58
58
|
## Targeted edits
|
|
59
59
|
|
|
60
|
-
Use `
|
|
60
|
+
Use `Edit` for focused note changes when the current content gives you stable context. Prefer this over shell text rewriting.
|
|
61
61
|
|
|
62
62
|
## Wikilinks
|
|
63
63
|
|
|
@@ -108,7 +108,7 @@ Each phase delegates — it does not do the work inline.
|
|
|
108
108
|
- **0. Intake** — restate the request in one line. For `orch-build-mvp`, read the
|
|
109
109
|
spec/design doc and extract scope, locked decisions, and a feature list.
|
|
110
110
|
- **1. Research & Reuse** — Search-Before-Building (Builder Ethos): `gh search
|
|
111
|
-
repos` / `gh search code`, then
|
|
111
|
+
repos` / `gh search code`, then WebFetch / vendor docs, then package registries,
|
|
112
112
|
then Exa / web search. Prefer adopting a proven implementation over net-new
|
|
113
113
|
code. Use `/mindforge:research` for a focused research subagent when the task
|
|
114
114
|
involves unfamiliar libraries.
|
|
@@ -177,7 +177,7 @@ Everything between the gates flows without stopping.
|
|
|
177
177
|
| Phase | Primary (MindForge) | Fallback / escalation |
|
|
178
178
|
|-------|---------------------|-----------------------|
|
|
179
179
|
| Intake / understand | `/mindforge:map-codebase` / `/mindforge:code-tour` | trace existing paths before a tweak, fix, or refactor |
|
|
180
|
-
| Research | `/mindforge:research` |
|
|
180
|
+
| Research | `/mindforge:research` | WebFetch / vendor docs / package registries / Exa |
|
|
181
181
|
| Plan | `writing-plans` skill via `/mindforge:plan-write` | `/mindforge:system-design`, `/mindforge:rfc` for structural calls |
|
|
182
182
|
| Implement | `mindforge-tdd_extended` protocol (Red-Green-Refactor) | `/mindforge:debug` on build/test breaks |
|
|
183
183
|
| Implement (large/swarm) | `WaveExecutor` / `mindforge-swarm-execution` protocol | `SwarmController` cluster selection (`.mindforge/engine/swarm-controller.md`) |
|
|
@@ -20,6 +20,7 @@ chains.
|
|
|
20
20
|
sources work with no API key (OpenCorporates has an optional free token
|
|
21
21
|
that raises rate limits).
|
|
22
22
|
|
|
23
|
+
Adapted from the MIT-licensed ShinMegamiBoson/OpenPlanter project; expanded
|
|
23
24
|
to cover identity / property / litigation / archives / news sources that
|
|
24
25
|
the original didn't address.
|
|
25
26
|
|
|
@@ -43,7 +44,7 @@ Use when the user asks for:
|
|
|
43
44
|
|
|
44
45
|
Do NOT use this skill for:
|
|
45
46
|
|
|
46
|
-
- general web research → `
|
|
47
|
+
- general web research → `WebSearch` / `WebFetch`
|
|
47
48
|
- domain/infrastructure OSINT → `domain-intel` skill
|
|
48
49
|
- academic literature → `arxiv` skill
|
|
49
50
|
- social-media profile discovery → `sherlock` skill (optional)
|
|
@@ -54,7 +55,7 @@ Do NOT use this skill for:
|
|
|
54
55
|
|
|
55
56
|
## Workflow
|
|
56
57
|
|
|
57
|
-
The agent runs scripts via the `
|
|
58
|
+
The agent runs scripts via the `Bash` tool. `SKILL_DIR` is the directory
|
|
58
59
|
holding this SKILL.md.
|
|
59
60
|
|
|
60
61
|
### 1. Identify which sources apply
|