imhcode 1.0.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/.github/ISSUE_TEMPLATE/bug_report.md +41 -0
- package/.github/ISSUE_TEMPLATE/feature_request.md +39 -0
- package/.github/PULL_REQUEST_TEMPLATE.md +29 -0
- package/.github/copilot-instructions.md +43 -0
- package/.github/workflows/framework-validator.yml +53 -0
- package/.gitignore.template +15 -0
- package/AGENTS.md +69 -0
- package/CLAUDE.md +27 -0
- package/README.md +63 -0
- package/SETUP.md +81 -0
- package/USER_MANUAL.md +70 -0
- package/agents/android-executor/SYSTEM.md +32 -0
- package/agents/android-executor/agent.yml +53 -0
- package/agents/debugger/SYSTEM.md +32 -0
- package/agents/debugger/agent.yml +56 -0
- package/agents/designer/SYSTEM.md +32 -0
- package/agents/designer/agent.yml +59 -0
- package/agents/devops-executor/SYSTEM.md +32 -0
- package/agents/devops-executor/agent.yml +56 -0
- package/agents/flutter-executor/SYSTEM.md +32 -0
- package/agents/flutter-executor/agent.yml +54 -0
- package/agents/ios-executor/SYSTEM.md +32 -0
- package/agents/ios-executor/agent.yml +53 -0
- package/agents/java-executor/SYSTEM.md +32 -0
- package/agents/java-executor/agent.yml +57 -0
- package/agents/laravel-executor/SYSTEM.md +32 -0
- package/agents/laravel-executor/agent.yml +59 -0
- package/agents/nextjs-executor/SYSTEM.md +32 -0
- package/agents/nextjs-executor/agent.yml +60 -0
- package/agents/planner/SYSTEM.md +32 -0
- package/agents/planner/agent.yml +56 -0
- package/agents/python-executor/SYSTEM.md +32 -0
- package/agents/python-executor/agent.yml +58 -0
- package/agents/react-executor/SYSTEM.md +32 -0
- package/agents/react-executor/agent.yml +58 -0
- package/agents/react-native-executor/SYSTEM.md +32 -0
- package/agents/react-native-executor/agent.yml +53 -0
- package/agents/security-auditor/SYSTEM.md +32 -0
- package/agents/security-auditor/agent.yml +56 -0
- package/agents/seo-optimizer/SYSTEM.md +32 -0
- package/agents/seo-optimizer/agent.yml +54 -0
- package/agents/systems-executor/SYSTEM.md +32 -0
- package/agents/systems-executor/agent.yml +54 -0
- package/agents/tester/SYSTEM.md +32 -0
- package/agents/tester/agent.yml +58 -0
- package/agents/vue-executor/SYSTEM.md +32 -0
- package/agents/vue-executor/agent.yml +58 -0
- package/agents/web3-executor/SYSTEM.md +32 -0
- package/agents/web3-executor/agent.yml +55 -0
- package/bin/imhcode.js +1634 -0
- package/dist/orchestrator/builder.d.ts +23 -0
- package/dist/orchestrator/builder.d.ts.map +1 -0
- package/dist/orchestrator/builder.js +231 -0
- package/dist/orchestrator/builder.js.map +1 -0
- package/dist/orchestrator/executor.d.ts +43 -0
- package/dist/orchestrator/executor.d.ts.map +1 -0
- package/dist/orchestrator/executor.js +356 -0
- package/dist/orchestrator/executor.js.map +1 -0
- package/dist/orchestrator/index.d.ts +35 -0
- package/dist/orchestrator/index.d.ts.map +1 -0
- package/dist/orchestrator/index.js +490 -0
- package/dist/orchestrator/index.js.map +1 -0
- package/dist/orchestrator/loader.d.ts +33 -0
- package/dist/orchestrator/loader.d.ts.map +1 -0
- package/dist/orchestrator/loader.js +203 -0
- package/dist/orchestrator/loader.js.map +1 -0
- package/dist/orchestrator/registry.d.ts +63 -0
- package/dist/orchestrator/registry.d.ts.map +1 -0
- package/dist/orchestrator/registry.js +181 -0
- package/dist/orchestrator/registry.js.map +1 -0
- package/dist/orchestrator/schema.d.ts +132 -0
- package/dist/orchestrator/schema.d.ts.map +1 -0
- package/dist/orchestrator/schema.js +101 -0
- package/dist/orchestrator/schema.js.map +1 -0
- package/dist/orchestrator/session.d.ts +25 -0
- package/dist/orchestrator/session.d.ts.map +1 -0
- package/dist/orchestrator/session.js +80 -0
- package/dist/orchestrator/session.js.map +1 -0
- package/dist/orchestrator/types.d.ts +91 -0
- package/dist/orchestrator/types.d.ts.map +1 -0
- package/dist/orchestrator/types.js +7 -0
- package/dist/orchestrator/types.js.map +1 -0
- package/package.json +95 -0
- package/skills/AnimatePresence/SKILL.md +3 -0
- package/skills/accessibility/SKILL.md +146 -0
- package/skills/agent-architecture-audit/SKILL.md +256 -0
- package/skills/agent-eval/SKILL.md +145 -0
- package/skills/agent-introspection-debugging/SKILL.md +153 -0
- package/skills/agentic-engineering/SKILL.md +63 -0
- package/skills/ai-regression-testing/SKILL.md +385 -0
- package/skills/android-clean-architecture/SKILL.md +339 -0
- package/skills/animated-component-libraries/SKILL.md +8 -0
- package/skills/animejs/SKILL.md +8 -0
- package/skills/api-connector-builder/SKILL.md +120 -0
- package/skills/api-design/SKILL.md +523 -0
- package/skills/apple/SKILL.md +158 -0
- package/skills/apple/metadata.json +16 -0
- package/skills/architecture-decision-records/SKILL.md +179 -0
- package/skills/article-writing/SKILL.md +79 -0
- package/skills/automation-audit-ops/SKILL.md +142 -0
- package/skills/awwwards/SKILL.md +162 -0
- package/skills/awwwards/metadata.json +16 -0
- package/skills/babylonjs-engine/SKILL.md +8 -0
- package/skills/backend-patterns/SKILL.md +561 -0
- package/skills/baqir-docs/SKILL.md +3 -0
- package/skills/barba-js/SKILL.md +8 -0
- package/skills/benchmark/SKILL.md +93 -0
- package/skills/best-practice/SKILL.md +7 -0
- package/skills/best-practice/assets/claude-memory/claude-memory-monorepo.jpg +0 -0
- package/skills/best-practice/assets/claude-power-ups/dial-the-model-1.png +0 -0
- package/skills/best-practice/assets/claude-power-ups/dial-the-model-2.png +0 -0
- package/skills/best-practice/assets/claude-power-ups/dial-the-model-3.png +0 -0
- package/skills/best-practice/assets/claude-power-ups/powerup-menu.png +0 -0
- package/skills/best-practice/claude-cli-startup-flags.md +231 -0
- package/skills/best-practice/claude-commands.md +132 -0
- package/skills/best-practice/claude-mcp.md +132 -0
- package/skills/best-practice/claude-memory.md +121 -0
- package/skills/best-practice/claude-power-ups.md +66 -0
- package/skills/best-practice/claude-settings.md +1170 -0
- package/skills/best-practice/claude-skills.md +58 -0
- package/skills/best-practice/claude-subagents.md +56 -0
- package/skills/blender-motion-state-inspection/SKILL.md +164 -0
- package/skills/bootstrap-patterns/SKILL.md +316 -0
- package/skills/brand-guidelines/LICENSE.txt +202 -0
- package/skills/brand-guidelines/SKILL.md +73 -0
- package/skills/brand-voice/SKILL.md +97 -0
- package/skills/brand-voice/references/voice-profile-schema.md +55 -0
- package/skills/browser-qa/SKILL.md +87 -0
- package/skills/canary-watch/SKILL.md +107 -0
- package/skills/canvas-design/LICENSE.txt +202 -0
- package/skills/canvas-design/SKILL.md +8 -0
- package/skills/canvas-design/canvas-fonts/ArsenalSC-OFL.txt +93 -0
- package/skills/canvas-design/canvas-fonts/ArsenalSC-Regular.ttf +0 -0
- package/skills/canvas-design/canvas-fonts/BigShoulders-Bold.ttf +0 -0
- package/skills/canvas-design/canvas-fonts/BigShoulders-OFL.txt +93 -0
- package/skills/canvas-design/canvas-fonts/BigShoulders-Regular.ttf +0 -0
- package/skills/canvas-design/canvas-fonts/Boldonse-OFL.txt +93 -0
- package/skills/canvas-design/canvas-fonts/Boldonse-Regular.ttf +0 -0
- package/skills/canvas-design/canvas-fonts/BricolageGrotesque-Bold.ttf +0 -0
- package/skills/canvas-design/canvas-fonts/BricolageGrotesque-OFL.txt +93 -0
- package/skills/canvas-design/canvas-fonts/BricolageGrotesque-Regular.ttf +0 -0
- package/skills/canvas-design/canvas-fonts/CrimsonPro-Bold.ttf +0 -0
- package/skills/canvas-design/canvas-fonts/CrimsonPro-Italic.ttf +0 -0
- package/skills/canvas-design/canvas-fonts/CrimsonPro-OFL.txt +93 -0
- package/skills/canvas-design/canvas-fonts/CrimsonPro-Regular.ttf +0 -0
- package/skills/canvas-design/canvas-fonts/DMMono-OFL.txt +93 -0
- package/skills/canvas-design/canvas-fonts/DMMono-Regular.ttf +0 -0
- package/skills/canvas-design/canvas-fonts/EricaOne-OFL.txt +94 -0
- package/skills/canvas-design/canvas-fonts/EricaOne-Regular.ttf +0 -0
- package/skills/canvas-design/canvas-fonts/GeistMono-Bold.ttf +0 -0
- package/skills/canvas-design/canvas-fonts/GeistMono-OFL.txt +93 -0
- package/skills/canvas-design/canvas-fonts/GeistMono-Regular.ttf +0 -0
- package/skills/canvas-design/canvas-fonts/Gloock-OFL.txt +93 -0
- package/skills/canvas-design/canvas-fonts/Gloock-Regular.ttf +0 -0
- package/skills/canvas-design/canvas-fonts/IBMPlexMono-Bold.ttf +0 -0
- package/skills/canvas-design/canvas-fonts/IBMPlexMono-OFL.txt +93 -0
- package/skills/canvas-design/canvas-fonts/IBMPlexMono-Regular.ttf +0 -0
- package/skills/canvas-design/canvas-fonts/IBMPlexSerif-Bold.ttf +0 -0
- package/skills/canvas-design/canvas-fonts/IBMPlexSerif-BoldItalic.ttf +0 -0
- package/skills/canvas-design/canvas-fonts/IBMPlexSerif-Italic.ttf +0 -0
- package/skills/canvas-design/canvas-fonts/IBMPlexSerif-Regular.ttf +0 -0
- package/skills/canvas-design/canvas-fonts/InstrumentSans-Bold.ttf +0 -0
- package/skills/canvas-design/canvas-fonts/InstrumentSans-BoldItalic.ttf +0 -0
- package/skills/canvas-design/canvas-fonts/InstrumentSans-Italic.ttf +0 -0
- package/skills/canvas-design/canvas-fonts/InstrumentSans-OFL.txt +93 -0
- package/skills/canvas-design/canvas-fonts/InstrumentSans-Regular.ttf +0 -0
- package/skills/canvas-design/canvas-fonts/InstrumentSerif-Italic.ttf +0 -0
- package/skills/canvas-design/canvas-fonts/InstrumentSerif-Regular.ttf +0 -0
- package/skills/canvas-design/canvas-fonts/Italiana-OFL.txt +93 -0
- package/skills/canvas-design/canvas-fonts/Italiana-Regular.ttf +0 -0
- package/skills/canvas-design/canvas-fonts/JetBrainsMono-Bold.ttf +0 -0
- package/skills/canvas-design/canvas-fonts/JetBrainsMono-OFL.txt +93 -0
- package/skills/canvas-design/canvas-fonts/JetBrainsMono-Regular.ttf +0 -0
- package/skills/canvas-design/canvas-fonts/Jura-Light.ttf +0 -0
- package/skills/canvas-design/canvas-fonts/Jura-Medium.ttf +0 -0
- package/skills/canvas-design/canvas-fonts/Jura-OFL.txt +93 -0
- package/skills/canvas-design/canvas-fonts/LibreBaskerville-OFL.txt +93 -0
- package/skills/canvas-design/canvas-fonts/LibreBaskerville-Regular.ttf +0 -0
- package/skills/canvas-design/canvas-fonts/Lora-Bold.ttf +0 -0
- package/skills/canvas-design/canvas-fonts/Lora-BoldItalic.ttf +0 -0
- package/skills/canvas-design/canvas-fonts/Lora-Italic.ttf +0 -0
- package/skills/canvas-design/canvas-fonts/Lora-OFL.txt +93 -0
- package/skills/canvas-design/canvas-fonts/Lora-Regular.ttf +0 -0
- package/skills/canvas-design/canvas-fonts/NationalPark-Bold.ttf +0 -0
- package/skills/canvas-design/canvas-fonts/NationalPark-OFL.txt +93 -0
- package/skills/canvas-design/canvas-fonts/NationalPark-Regular.ttf +0 -0
- package/skills/canvas-design/canvas-fonts/NothingYouCouldDo-OFL.txt +93 -0
- package/skills/canvas-design/canvas-fonts/NothingYouCouldDo-Regular.ttf +0 -0
- package/skills/canvas-design/canvas-fonts/Outfit-Bold.ttf +0 -0
- package/skills/canvas-design/canvas-fonts/Outfit-OFL.txt +93 -0
- package/skills/canvas-design/canvas-fonts/Outfit-Regular.ttf +0 -0
- package/skills/canvas-design/canvas-fonts/PixelifySans-Medium.ttf +0 -0
- package/skills/canvas-design/canvas-fonts/PixelifySans-OFL.txt +93 -0
- package/skills/canvas-design/canvas-fonts/PoiretOne-OFL.txt +93 -0
- package/skills/canvas-design/canvas-fonts/PoiretOne-Regular.ttf +0 -0
- package/skills/canvas-design/canvas-fonts/RedHatMono-Bold.ttf +0 -0
- package/skills/canvas-design/canvas-fonts/RedHatMono-OFL.txt +93 -0
- package/skills/canvas-design/canvas-fonts/RedHatMono-Regular.ttf +0 -0
- package/skills/canvas-design/canvas-fonts/Silkscreen-OFL.txt +93 -0
- package/skills/canvas-design/canvas-fonts/Silkscreen-Regular.ttf +0 -0
- package/skills/canvas-design/canvas-fonts/SmoochSans-Medium.ttf +0 -0
- package/skills/canvas-design/canvas-fonts/SmoochSans-OFL.txt +93 -0
- package/skills/canvas-design/canvas-fonts/Tektur-Medium.ttf +0 -0
- package/skills/canvas-design/canvas-fonts/Tektur-OFL.txt +93 -0
- package/skills/canvas-design/canvas-fonts/Tektur-Regular.ttf +0 -0
- package/skills/canvas-design/canvas-fonts/WorkSans-Bold.ttf +0 -0
- package/skills/canvas-design/canvas-fonts/WorkSans-BoldItalic.ttf +0 -0
- package/skills/canvas-design/canvas-fonts/WorkSans-Italic.ttf +0 -0
- package/skills/canvas-design/canvas-fonts/WorkSans-OFL.txt +93 -0
- package/skills/canvas-design/canvas-fonts/WorkSans-Regular.ttf +0 -0
- package/skills/canvas-design/canvas-fonts/YoungSerif-OFL.txt +93 -0
- package/skills/canvas-design/canvas-fonts/YoungSerif-Regular.ttf +0 -0
- package/skills/caveman/SKILL.md +74 -0
- package/skills/changelog-generator/SKILL.md +104 -0
- package/skills/cisco-ios-patterns/SKILL.md +163 -0
- package/skills/ckm:design-system/SKILL.md +3 -0
- package/skills/ckm:slides/SKILL.md +3 -0
- package/skills/claude-red/SKILL.md +35 -0
- package/skills/click-path-audit/SKILL.md +244 -0
- package/skills/clickhouse-io/SKILL.md +439 -0
- package/skills/codebase-onboarding/SKILL.md +233 -0
- package/skills/coding-standards/SKILL.md +549 -0
- package/skills/compose-multiplatform-patterns/SKILL.md +299 -0
- package/skills/computational-architecture/SKILL.md +31 -0
- package/skills/content-engine/SKILL.md +131 -0
- package/skills/content-research-writer/SKILL.md +538 -0
- package/skills/continuous-agent-loop/SKILL.md +45 -0
- package/skills/cost-aware-llm-pipeline/SKILL.md +183 -0
- package/skills/cpp-coding-standards/SKILL.md +723 -0
- package/skills/crosspost/SKILL.md +111 -0
- package/skills/css-container-queries/SKILL.md +132 -0
- package/skills/customer-billing-ops/SKILL.md +140 -0
- package/skills/customs-trade-compliance/SKILL.md +263 -0
- package/skills/dart-flutter-patterns/SKILL.md +563 -0
- package/skills/dashboard-builder/SKILL.md +108 -0
- package/skills/data-scraper-agent/SKILL.md +764 -0
- package/skills/database-migrations/SKILL.md +429 -0
- package/skills/deep-research/SKILL.md +159 -0
- package/skills/defi-amm-security/SKILL.md +166 -0
- package/skills/deployment-patterns/SKILL.md +427 -0
- package/skills/design-auditor/README.md +504 -0
- package/skills/design-auditor/README_KR.md +327 -0
- package/skills/design-auditor/SKILL.md +8 -0
- package/skills/design-auditor/references/animation.md +300 -0
- package/skills/design-auditor/references/color.md +241 -0
- package/skills/design-auditor/references/corner-radius.md +331 -0
- package/skills/design-auditor/references/elevation.md +249 -0
- package/skills/design-auditor/references/ethics.md +877 -0
- package/skills/design-auditor/references/figma-mcp.md +377 -0
- package/skills/design-auditor/references/heuristics.md +730 -0
- package/skills/design-auditor/references/i18n.md +254 -0
- package/skills/design-auditor/references/iconography.md +201 -0
- package/skills/design-auditor/references/microcopy.md +227 -0
- package/skills/design-auditor/references/navigation.md +311 -0
- package/skills/design-auditor/references/spacing.md +313 -0
- package/skills/design-auditor/references/states.md +254 -0
- package/skills/design-auditor/references/tokens.md +286 -0
- package/skills/design-auditor/references/typography.md +273 -0
- package/skills/design-system/SKILL.md +82 -0
- package/skills/design-tokens-system/SKILL.md +156 -0
- package/skills/development-workflows/SKILL.md +7 -0
- package/skills/development-workflows/cross-model-workflow/assets/cross-model-workflow.png +0 -0
- package/skills/development-workflows/cross-model-workflow/cross-model-workflow.md +55 -0
- package/skills/development-workflows/rpi/.claude/agents/code-reviewer.md +21 -0
- package/skills/development-workflows/rpi/.claude/agents/constitutional-validator.md +288 -0
- package/skills/development-workflows/rpi/.claude/agents/documentation-analyst-writer.md +64 -0
- package/skills/development-workflows/rpi/.claude/agents/product-manager.md +15 -0
- package/skills/development-workflows/rpi/.claude/agents/requirement-parser.md +238 -0
- package/skills/development-workflows/rpi/.claude/agents/senior-software-engineer.md +15 -0
- package/skills/development-workflows/rpi/.claude/agents/technical-cto-advisor.md +201 -0
- package/skills/development-workflows/rpi/.claude/agents/ux-designer.md +14 -0
- package/skills/development-workflows/rpi/.claude/commands/rpi/implement.md +634 -0
- package/skills/development-workflows/rpi/.claude/commands/rpi/plan.md +416 -0
- package/skills/development-workflows/rpi/.claude/commands/rpi/research.md +381 -0
- package/skills/development-workflows/rpi/rpi-workflow.md +101 -0
- package/skills/development-workflows/rpi/rpi-workflow.svg +178 -0
- package/skills/django-celery/SKILL.md +457 -0
- package/skills/django-patterns/SKILL.md +734 -0
- package/skills/django-security/SKILL.md +593 -0
- package/skills/django-tdd/SKILL.md +729 -0
- package/skills/dmux-workflows/SKILL.md +7 -0
- package/skills/docker-patterns/SKILL.md +364 -0
- package/skills/documentation-lookup/SKILL.md +90 -0
- package/skills/domain-name-brainstormer/SKILL.md +212 -0
- package/skills/dribbble/SKILL.md +160 -0
- package/skills/dribbble/metadata.json +16 -0
- package/skills/e2e-testing/SKILL.md +326 -0
- package/skills/empirical-research/SKILL.md +22 -0
- package/skills/error-handling/SKILL.md +376 -0
- package/skills/eval-harness/SKILL.md +270 -0
- package/skills/evm-token-decimals/SKILL.md +130 -0
- package/skills/fastapi-patterns/SKILL.md +327 -0
- package/skills/finance-billing-ops/SKILL.md +127 -0
- package/skills/flox-environments/SKILL.md +496 -0
- package/skills/flutter-dart-code-review/SKILL.md +435 -0
- package/skills/foundation-models-on-device/SKILL.md +243 -0
- package/skills/framer/SKILL.md +159 -0
- package/skills/framer/metadata.json +16 -0
- package/skills/frontend-design/LICENSE.txt +21 -0
- package/skills/frontend-design/README.md +71 -0
- package/skills/frontend-design/SKILL.md +115 -0
- package/skills/frontend-design-direction/SKILL.md +92 -0
- package/skills/frontend-patterns/SKILL.md +7 -0
- package/skills/frontend-slides/SKILL.md +184 -0
- package/skills/frontend-slides/STYLE_PRESETS.md +330 -0
- package/skills/frontend-slides/animation-patterns.md +122 -0
- package/skills/frontend-slides/html-template.md +419 -0
- package/skills/frontend-slides/scripts/export-pdf.sh +418 -0
- package/skills/frontend-slides/scripts/extract-pptx.py +96 -0
- package/skills/frontend-slides/viewport-base.css +153 -0
- package/skills/gateguard/SKILL.md +125 -0
- package/skills/git-workflow/SKILL.md +715 -0
- package/skills/github-ops/SKILL.md +144 -0
- package/skills/golang-patterns/SKILL.md +674 -0
- package/skills/golang-testing/SKILL.md +720 -0
- package/skills/graphify/__init__.py +28 -0
- package/skills/graphify/__main__.py +2899 -0
- package/skills/graphify/analyze.py +608 -0
- package/skills/graphify/benchmark.py +152 -0
- package/skills/graphify/build.py +366 -0
- package/skills/graphify/cache.py +244 -0
- package/skills/graphify/callflow_html.py +2014 -0
- package/skills/graphify/cluster.py +267 -0
- package/skills/graphify/dedup.py +414 -0
- package/skills/graphify/detect.py +1018 -0
- package/skills/graphify/export.py +1264 -0
- package/skills/graphify/extract.py +6646 -0
- package/skills/graphify/global_graph.py +155 -0
- package/skills/graphify/google_workspace.py +223 -0
- package/skills/graphify/hooks.py +304 -0
- package/skills/graphify/ingest.py +331 -0
- package/skills/graphify/llm.py +1111 -0
- package/skills/graphify/manifest.py +4 -0
- package/skills/graphify/prs.py +746 -0
- package/skills/graphify/report.py +203 -0
- package/skills/graphify/security.py +242 -0
- package/skills/graphify/serve.py +899 -0
- package/skills/graphify/skill-aider.md +1229 -0
- package/skills/graphify/skill-claw.md +1229 -0
- package/skills/graphify/skill-codex.md +1290 -0
- package/skills/graphify/skill-copilot.md +1317 -0
- package/skills/graphify/skill-droid.md +1285 -0
- package/skills/graphify/skill-kiro.md +1228 -0
- package/skills/graphify/skill-opencode.md +1340 -0
- package/skills/graphify/skill-pi.md +1228 -0
- package/skills/graphify/skill-trae.md +1254 -0
- package/skills/graphify/skill-vscode.md +258 -0
- package/skills/graphify/skill-windows.md +1434 -0
- package/skills/graphify/skill.md +1086 -0
- package/skills/graphify/transcribe.py +184 -0
- package/skills/graphify/tree_html.py +580 -0
- package/skills/graphify/validate.py +72 -0
- package/skills/graphify/watch.py +728 -0
- package/skills/graphify/wiki.py +259 -0
- package/skills/growth-marketing/SKILL.md +26 -0
- package/skills/gsap-scrolltrigger/SKILL.md +8 -0
- package/skills/healthcare-cdss-patterns/SKILL.md +245 -0
- package/skills/healthcare-emr-patterns/SKILL.md +159 -0
- package/skills/healthcare-phi-compliance/SKILL.md +145 -0
- package/skills/hexagonal-architecture/SKILL.md +276 -0
- package/skills/hipaa-compliance/SKILL.md +78 -0
- package/skills/homelab-network-setup/SKILL.md +129 -0
- package/skills/imhcode/SKILL.md +183 -0
- package/skills/imhcode-brainstorm/SKILL.md +37 -0
- package/skills/imhcode-codebase-knowledge/SKILL.md +37 -0
- package/skills/imhcode-codebase-knowledge/assets/templates/ARCHITECTURE.md +49 -0
- package/skills/imhcode-codebase-knowledge/assets/templates/CONCERNS.md +56 -0
- package/skills/imhcode-codebase-knowledge/assets/templates/CONVENTIONS.md +52 -0
- package/skills/imhcode-codebase-knowledge/assets/templates/INTEGRATIONS.md +48 -0
- package/skills/imhcode-codebase-knowledge/assets/templates/STACK.md +56 -0
- package/skills/imhcode-codebase-knowledge/assets/templates/STRUCTURE.md +44 -0
- package/skills/imhcode-codebase-knowledge/assets/templates/TESTING.md +57 -0
- package/skills/imhcode-codebase-knowledge/references/inquiry-checkpoints.md +70 -0
- package/skills/imhcode-codebase-knowledge/references/stack-detection.md +131 -0
- package/skills/imhcode-codebase-knowledge/scripts/scan.py +712 -0
- package/skills/imhcode-dispatch/SKILL.md +31 -0
- package/skills/imhcode-saas-architect/SKILL.md +55 -0
- package/skills/imhcode-security/SKILL.md +42 -0
- package/skills/imhcode-sprint-planner/SKILL.md +42 -0
- package/skills/inventory-demand-planning/SKILL.md +247 -0
- package/skills/investor-materials/SKILL.md +96 -0
- package/skills/investor-outreach/SKILL.md +91 -0
- package/skills/ios-icon-gen/SKILL.md +157 -0
- package/skills/ios-icon-gen/scripts/generate_icons.swift +258 -0
- package/skills/ios-icon-gen/scripts/iconify_gen.sh +235 -0
- package/skills/java-coding-standards/SKILL.md +383 -0
- package/skills/jpa-patterns/SKILL.md +151 -0
- package/skills/kotlin-ktor-patterns/SKILL.md +689 -0
- package/skills/kotlin-patterns/SKILL.md +711 -0
- package/skills/laravel-patterns/SKILL.md +415 -0
- package/skills/laravel-plugin-discovery/SKILL.md +229 -0
- package/skills/laravel-security/SKILL.md +285 -0
- package/skills/laravel-tdd/SKILL.md +283 -0
- package/skills/laravel-verification/SKILL.md +179 -0
- package/skills/lightweight-3d-effects/SKILL.md +8 -0
- package/skills/linear/SKILL.md +156 -0
- package/skills/linear/metadata.json +16 -0
- package/skills/liquid-glass-design/SKILL.md +279 -0
- package/skills/llm-trading-agent-security/SKILL.md +146 -0
- package/skills/locomotive-scroll/SKILL.md +8 -0
- package/skills/logistics-exception-management/SKILL.md +222 -0
- package/skills/lottie-animations/SKILL.md +8 -0
- package/skills/make-interfaces-feel-better/SKILL.md +151 -0
- package/skills/mcp-server-patterns/SKILL.md +69 -0
- package/skills/meeting-insights-analyzer/SKILL.md +327 -0
- package/skills/micro-interactions/SKILL.md +178 -0
- package/skills/mle-workflow/SKILL.md +346 -0
- package/skills/mobbin/SKILL.md +157 -0
- package/skills/mobbin/metadata.json +16 -0
- package/skills/mobile-app-design/README.md +302 -0
- package/skills/mobile-app-design/SKILL.md +330 -0
- package/skills/mobile-app-design/examples/design-system-config.ts +685 -0
- package/skills/mobile-app-design/examples/form-validation-example.tsx +501 -0
- package/skills/mobile-app-design/examples/optimized-list-example.tsx +384 -0
- package/skills/mobile-app-design/examples/profile-screen-example.tsx +450 -0
- package/skills/mobile-app-design/references/accessibility-checklist.md +525 -0
- package/skills/mobile-app-design/references/android-guidelines.md +582 -0
- package/skills/mobile-app-design/references/common-mistakes.md +834 -0
- package/skills/mobile-app-design/references/ios-guidelines.md +421 -0
- package/skills/mobile-app-design/references/performance-patterns.md +705 -0
- package/skills/mobile-app-design/references/platform-differences.md +353 -0
- package/skills/mobile-app-design/references/ui-libraries.md +729 -0
- package/skills/mobile-app-design/scripts/accessibility-audit.sh +281 -0
- package/skills/mobile-app-design/scripts/check-contrast.py +188 -0
- package/skills/mobile-app-design/scripts/validate-touch-targets.sh +228 -0
- package/skills/mobile-app-ui-design/INDEX.md +131 -0
- package/skills/mobile-app-ui-design/README.md +135 -0
- package/skills/mobile-app-ui-design/SKILL.md +7 -0
- package/skills/mobile-app-ui-design/references/industry-conventions.md +151 -0
- package/skills/modern-web-design/SKILL.md +8 -0
- package/skills/motion-advanced/SKILL.md +596 -0
- package/skills/motion-design/SKILL.md +21 -0
- package/skills/motion-foundations/SKILL.md +299 -0
- package/skills/motion-framer/SKILL.md +8 -0
- package/skills/motion-patterns/SKILL.md +435 -0
- package/skills/motion-ui/SKILL.md +575 -0
- package/skills/mysql-patterns/SKILL.md +412 -0
- package/skills/nestjs-patterns/SKILL.md +230 -0
- package/skills/network-config-validation/SKILL.md +210 -0
- package/skills/nextjs-turbopack/SKILL.md +44 -0
- package/skills/nodejs-keccak256/SKILL.md +102 -0
- package/skills/nothing-design/SKILL.md +177 -0
- package/skills/nothing-design/references/components.md +153 -0
- package/skills/nothing-design/references/platform-mapping.md +64 -0
- package/skills/nothing-design/references/tokens.md +142 -0
- package/skills/nuxt4-patterns/SKILL.md +100 -0
- package/skills/openclaw-persona-forge/SKILL.md +288 -0
- package/skills/openclaw-persona-forge/gacha.py +224 -0
- package/skills/openclaw-persona-forge/gacha.sh +5 -0
- package/skills/openclaw-persona-forge/references/avatar-style.md +124 -0
- package/skills/openclaw-persona-forge/references/boundary-rules.md +53 -0
- package/skills/openclaw-persona-forge/references/error-handling.md +53 -0
- package/skills/openclaw-persona-forge/references/identity-tension.md +48 -0
- package/skills/openclaw-persona-forge/references/naming-system.md +39 -0
- package/skills/openclaw-persona-forge/references/output-template.md +166 -0
- package/skills/orchestration-workflow/SKILL.md +199 -0
- package/skills/orchestration-workflow/orchestration-workflow.gif +0 -0
- package/skills/orchestration-workflow/orchestration-workflow.md +199 -0
- package/skills/orchestration-workflow/orchestration-workflow.svg +78 -0
- package/skills/orchestration-workflow/output.md +13 -0
- package/skills/orchestration-workflow/weather.svg +6 -0
- package/skills/pixijs-2d/SKILL.md +8 -0
- package/skills/plan-orchestrate/SKILL.md +262 -0
- package/skills/plankton-code-quality/SKILL.md +236 -0
- package/skills/postgres-patterns/SKILL.md +147 -0
- package/skills/ppt-mckinsey/SKILL.md +27 -0
- package/skills/prisma-patterns/SKILL.md +371 -0
- package/skills/product-capability/SKILL.md +141 -0
- package/skills/product-lens/SKILL.md +92 -0
- package/skills/production-audit/SKILL.md +206 -0
- package/skills/project-flow-ops/SKILL.md +111 -0
- package/skills/prompt-optimizer/SKILL.md +398 -0
- package/skills/python-patterns/SKILL.md +750 -0
- package/skills/python-testing/SKILL.md +816 -0
- package/skills/pytorch-patterns/SKILL.md +396 -0
- package/skills/quarkus-patterns/SKILL.md +722 -0
- package/skills/radix-ui-primitives/SKILL.md +86 -0
- package/skills/react-native-best-practices/SKILL.md +83 -0
- package/skills/react-native-brownfield-migration/SKILL.md +115 -0
- package/skills/react-spring-physics/SKILL.md +8 -0
- package/skills/react-three-fiber/SKILL.md +8 -0
- package/skills/recsys-pipeline-architect/SKILL.md +114 -0
- package/skills/redis-patterns/SKILL.md +403 -0
- package/skills/remotion-video-creation/SKILL.md +43 -0
- package/skills/remotion-video-creation/rules/3d.md +86 -0
- package/skills/remotion-video-creation/rules/animations.md +29 -0
- package/skills/remotion-video-creation/rules/assets/charts-bar-chart.tsx +173 -0
- package/skills/remotion-video-creation/rules/assets/text-animations-typewriter.tsx +100 -0
- package/skills/remotion-video-creation/rules/assets/text-animations-word-highlight.tsx +108 -0
- package/skills/remotion-video-creation/rules/assets.md +78 -0
- package/skills/remotion-video-creation/rules/audio.md +172 -0
- package/skills/remotion-video-creation/rules/calculate-metadata.md +104 -0
- package/skills/remotion-video-creation/rules/can-decode.md +75 -0
- package/skills/remotion-video-creation/rules/charts.md +58 -0
- package/skills/remotion-video-creation/rules/compositions.md +146 -0
- package/skills/remotion-video-creation/rules/display-captions.md +126 -0
- package/skills/remotion-video-creation/rules/extract-frames.md +229 -0
- package/skills/remotion-video-creation/rules/fonts.md +152 -0
- package/skills/remotion-video-creation/rules/get-audio-duration.md +58 -0
- package/skills/remotion-video-creation/rules/get-video-dimensions.md +68 -0
- package/skills/remotion-video-creation/rules/get-video-duration.md +58 -0
- package/skills/remotion-video-creation/rules/gifs.md +138 -0
- package/skills/remotion-video-creation/rules/images.md +130 -0
- package/skills/remotion-video-creation/rules/import-srt-captions.md +67 -0
- package/skills/remotion-video-creation/rules/lottie.md +67 -0
- package/skills/remotion-video-creation/rules/measuring-dom-nodes.md +34 -0
- package/skills/remotion-video-creation/rules/measuring-text.md +143 -0
- package/skills/remotion-video-creation/rules/sequencing.md +106 -0
- package/skills/remotion-video-creation/rules/tailwind.md +11 -0
- package/skills/remotion-video-creation/rules/text-animations.md +20 -0
- package/skills/remotion-video-creation/rules/timing.md +179 -0
- package/skills/remotion-video-creation/rules/transcribe-captions.md +19 -0
- package/skills/remotion-video-creation/rules/transitions.md +122 -0
- package/skills/remotion-video-creation/rules/trimming.md +52 -0
- package/skills/remotion-video-creation/rules/videos.md +171 -0
- package/skills/repo-scan/SKILL.md +78 -0
- package/skills/responsive-email-templates/SKILL.md +110 -0
- package/skills/rive-interactive/SKILL.md +8 -0
- package/skills/rust-patterns/SKILL.md +499 -0
- package/skills/rust-testing/SKILL.md +500 -0
- package/skills/scroll-reveal-libraries/SKILL.md +8 -0
- package/skills/security-bounty-hunter/SKILL.md +99 -0
- package/skills/security-review/SKILL.md +503 -0
- package/skills/security-review/cloud-infrastructure-security.md +361 -0
- package/skills/security-scan/SKILL.md +165 -0
- package/skills/seo/SKILL.md +154 -0
- package/skills/seo-growth/SKILL.md +34 -0
- package/skills/shadcn-ui-patterns/SKILL.md +123 -0
- package/skills/spline-interactive/SKILL.md +8 -0
- package/skills/springboot-patterns/SKILL.md +314 -0
- package/skills/springboot-security/SKILL.md +272 -0
- package/skills/springboot-tdd/SKILL.md +158 -0
- package/skills/strategic-compact/SKILL.md +131 -0
- package/skills/stripe/SKILL.md +168 -0
- package/skills/stripe/metadata.json +16 -0
- package/skills/swift-actor-persistence/SKILL.md +143 -0
- package/skills/swift-concurrency-6-2/SKILL.md +216 -0
- package/skills/swiftui-patterns/SKILL.md +259 -0
- package/skills/systematic-debugging/CREATION-LOG.md +119 -0
- package/skills/systematic-debugging/SKILL.md +296 -0
- package/skills/systematic-debugging/condition-based-waiting-example.ts +158 -0
- package/skills/systematic-debugging/condition-based-waiting.md +115 -0
- package/skills/systematic-debugging/defense-in-depth.md +122 -0
- package/skills/systematic-debugging/find-polluter.sh +63 -0
- package/skills/systematic-debugging/root-cause-tracing.md +169 -0
- package/skills/systematic-debugging/test-academic.md +14 -0
- package/skills/systematic-debugging/test-pressure-1.md +58 -0
- package/skills/systematic-debugging/test-pressure-2.md +68 -0
- package/skills/systematic-debugging/test-pressure-3.md +69 -0
- package/skills/tailwindcss-v4/SKILL.md +107 -0
- package/skills/tdd-workflow/SKILL.md +463 -0
- package/skills/team-builder/SKILL.md +168 -0
- package/skills/test-driven-development/SKILL.md +371 -0
- package/skills/test-driven-development/testing-anti-patterns.md +299 -0
- package/skills/theme-factory/LICENSE.txt +202 -0
- package/skills/theme-factory/SKILL.md +59 -0
- package/skills/theme-factory/theme-showcase.pdf +0 -0
- package/skills/theme-factory/themes/arctic-frost.md +19 -0
- package/skills/theme-factory/themes/botanical-garden.md +19 -0
- package/skills/theme-factory/themes/desert-rose.md +19 -0
- package/skills/theme-factory/themes/forest-canopy.md +19 -0
- package/skills/theme-factory/themes/golden-hour.md +19 -0
- package/skills/theme-factory/themes/midnight-galaxy.md +19 -0
- package/skills/theme-factory/themes/modern-minimalist.md +19 -0
- package/skills/theme-factory/themes/ocean-depths.md +19 -0
- package/skills/theme-factory/themes/sunset-boulevard.md +19 -0
- package/skills/theme-factory/themes/tech-innovation.md +19 -0
- package/skills/threejs-webgl/SKILL.md +8 -0
- package/skills/trailofbits-auditing/SKILL.md +30 -0
- package/skills/twitter-algorithm-optimizer/SKILL.md +327 -0
- package/skills/typeui-cli/SKILL.md +90 -0
- package/skills/typeui-main/DESIGN.md +114 -0
- package/skills/typeui-main/LICENSE.md +9 -0
- package/skills/typeui-main/README.md +84 -0
- package/skills/typeui-main/REGISTRY.md +60 -0
- package/skills/typeui-main/SKILL.md +114 -0
- package/skills/typeui-main/package-lock.json +2123 -0
- package/skills/typeui-main/package.json +56 -0
- package/skills/typeui-main/skills/typeui-cli/SKILL.md +90 -0
- package/skills/typeui-main/src/cli.ts +282 -0
- package/skills/typeui-main/src/config.ts +28 -0
- package/skills/typeui-main/src/domain/designSystemSchema.ts +77 -0
- package/skills/typeui-main/src/generation/existingDesignSystem.ts +228 -0
- package/skills/typeui-main/src/generation/randomDesignSystem.ts +348 -0
- package/skills/typeui-main/src/generation/runDesignGeneration.ts +31 -0
- package/skills/typeui-main/src/generation/runGeneration.ts +36 -0
- package/skills/typeui-main/src/generation/runPull.ts +62 -0
- package/skills/typeui-main/src/io/updateSkillFile.ts +118 -0
- package/skills/typeui-main/src/prompts/designSystem.ts +755 -0
- package/skills/typeui-main/src/prompts/registry.ts +72 -0
- package/skills/typeui-main/src/registry/registryClient.ts +246 -0
- package/skills/typeui-main/src/renderers/claudeRenderer.ts +10 -0
- package/skills/typeui-main/src/renderers/codexRenderer.ts +10 -0
- package/skills/typeui-main/src/renderers/cursorRenderer.ts +10 -0
- package/skills/typeui-main/src/renderers/index.ts +14 -0
- package/skills/typeui-main/src/renderers/openCodeRenderer.ts +10 -0
- package/skills/typeui-main/src/renderers/shared.ts +252 -0
- package/skills/typeui-main/src/skillMetadata.ts +19 -0
- package/skills/typeui-main/src/types.ts +204 -0
- package/skills/typeui-main/src/ui/banner.ts +20 -0
- package/skills/typeui-main/test/existingDesignSystem.test.ts +67 -0
- package/skills/typeui-main/test/orchestrator.test.ts +113 -0
- package/skills/typeui-main/test/randomDesignSystem.test.ts +20 -0
- package/skills/typeui-main/test/registryClient.test.ts +408 -0
- package/skills/typeui-main/test/renderers.test.ts +41 -0
- package/skills/typeui-main/test/runDesignGeneration.test.ts +52 -0
- package/skills/typeui-main/test/runPull.test.ts +92 -0
- package/skills/typeui-main/test/skillMetadata.test.ts +11 -0
- package/skills/typeui-main/test/updateSkillFile.test.ts +160 -0
- package/skills/typeui-main/tsconfig.json +16 -0
- package/skills/ui-demo/SKILL.md +465 -0
- package/skills/ui-to-vue/SKILL.md +134 -0
- package/skills/ui-ux-pro-max/.claude/skills/banner-design/SKILL.md +192 -0
- package/skills/ui-ux-pro-max/.claude/skills/banner-design/references/banner-sizes-and-styles.md +118 -0
- package/skills/ui-ux-pro-max/.claude/skills/brand/SKILL.md +97 -0
- package/skills/ui-ux-pro-max/.claude/skills/brand/references/approval-checklist.md +169 -0
- package/skills/ui-ux-pro-max/.claude/skills/brand/references/asset-organization.md +157 -0
- package/skills/ui-ux-pro-max/.claude/skills/brand/references/brand-guideline-template.md +140 -0
- package/skills/ui-ux-pro-max/.claude/skills/brand/references/color-palette-management.md +186 -0
- package/skills/ui-ux-pro-max/.claude/skills/brand/references/consistency-checklist.md +94 -0
- package/skills/ui-ux-pro-max/.claude/skills/brand/references/logo-usage-rules.md +185 -0
- package/skills/ui-ux-pro-max/.claude/skills/brand/references/messaging-framework.md +85 -0
- package/skills/ui-ux-pro-max/.claude/skills/brand/references/typography-specifications.md +214 -0
- package/skills/ui-ux-pro-max/.claude/skills/brand/references/update.md +118 -0
- package/skills/ui-ux-pro-max/.claude/skills/brand/references/visual-identity.md +96 -0
- package/skills/ui-ux-pro-max/.claude/skills/brand/references/voice-framework.md +88 -0
- package/skills/ui-ux-pro-max/.claude/skills/brand/scripts/extract-colors.cjs +341 -0
- package/skills/ui-ux-pro-max/.claude/skills/brand/scripts/inject-brand-context.cjs +349 -0
- package/skills/ui-ux-pro-max/.claude/skills/brand/scripts/sync-brand-to-tokens.cjs +266 -0
- package/skills/ui-ux-pro-max/.claude/skills/brand/scripts/validate-asset.cjs +387 -0
- package/skills/ui-ux-pro-max/.claude/skills/brand/templates/brand-guidelines-starter.md +275 -0
- package/skills/ui-ux-pro-max/.claude/skills/design/SKILL.md +302 -0
- package/skills/ui-ux-pro-max/.claude/skills/design/data/cip/deliverables.csv +51 -0
- package/skills/ui-ux-pro-max/.claude/skills/design/data/cip/industries.csv +21 -0
- package/skills/ui-ux-pro-max/.claude/skills/design/data/cip/mockup-contexts.csv +21 -0
- package/skills/ui-ux-pro-max/.claude/skills/design/data/cip/styles.csv +21 -0
- package/skills/ui-ux-pro-max/.claude/skills/design/data/icon/styles.csv +16 -0
- package/skills/ui-ux-pro-max/.claude/skills/design/data/logo/colors.csv +56 -0
- package/skills/ui-ux-pro-max/.claude/skills/design/data/logo/industries.csv +56 -0
- package/skills/ui-ux-pro-max/.claude/skills/design/data/logo/styles.csv +56 -0
- package/skills/ui-ux-pro-max/.claude/skills/design/references/banner-sizes-and-styles.md +118 -0
- package/skills/ui-ux-pro-max/.claude/skills/design/references/cip-deliverable-guide.md +95 -0
- package/skills/ui-ux-pro-max/.claude/skills/design/references/cip-design.md +121 -0
- package/skills/ui-ux-pro-max/.claude/skills/design/references/cip-prompt-engineering.md +84 -0
- package/skills/ui-ux-pro-max/.claude/skills/design/references/cip-style-guide.md +68 -0
- package/skills/ui-ux-pro-max/.claude/skills/design/references/design-routing.md +207 -0
- package/skills/ui-ux-pro-max/.claude/skills/design/references/icon-design.md +122 -0
- package/skills/ui-ux-pro-max/.claude/skills/design/references/logo-color-psychology.md +101 -0
- package/skills/ui-ux-pro-max/.claude/skills/design/references/logo-design.md +92 -0
- package/skills/ui-ux-pro-max/.claude/skills/design/references/logo-prompt-engineering.md +158 -0
- package/skills/ui-ux-pro-max/.claude/skills/design/references/logo-style-guide.md +109 -0
- package/skills/ui-ux-pro-max/.claude/skills/design/references/slides-copywriting-formulas.md +84 -0
- package/skills/ui-ux-pro-max/.claude/skills/design/references/slides-create.md +4 -0
- package/skills/ui-ux-pro-max/.claude/skills/design/references/slides-html-template.md +295 -0
- package/skills/ui-ux-pro-max/.claude/skills/design/references/slides-layout-patterns.md +137 -0
- package/skills/ui-ux-pro-max/.claude/skills/design/references/slides-strategies.md +94 -0
- package/skills/ui-ux-pro-max/.claude/skills/design/references/slides.md +42 -0
- package/skills/ui-ux-pro-max/.claude/skills/design/references/social-photos-design.md +329 -0
- package/skills/ui-ux-pro-max/.claude/skills/design/scripts/cip/core.py +215 -0
- package/skills/ui-ux-pro-max/.claude/skills/design/scripts/cip/generate.py +484 -0
- package/skills/ui-ux-pro-max/.claude/skills/design/scripts/cip/render-html.py +424 -0
- package/skills/ui-ux-pro-max/.claude/skills/design/scripts/cip/search.py +127 -0
- package/skills/ui-ux-pro-max/.claude/skills/design/scripts/icon/generate.py +487 -0
- package/skills/ui-ux-pro-max/.claude/skills/design/scripts/logo/core.py +175 -0
- package/skills/ui-ux-pro-max/.claude/skills/design/scripts/logo/generate.py +362 -0
- package/skills/ui-ux-pro-max/.claude/skills/design/scripts/logo/search.py +114 -0
- package/skills/ui-ux-pro-max/.claude/skills/design-system/SKILL.md +244 -0
- package/skills/ui-ux-pro-max/.claude/skills/design-system/data/slide-backgrounds.csv +11 -0
- package/skills/ui-ux-pro-max/.claude/skills/design-system/data/slide-charts.csv +26 -0
- package/skills/ui-ux-pro-max/.claude/skills/design-system/data/slide-color-logic.csv +14 -0
- package/skills/ui-ux-pro-max/.claude/skills/design-system/data/slide-copy.csv +26 -0
- package/skills/ui-ux-pro-max/.claude/skills/design-system/data/slide-layout-logic.csv +16 -0
- package/skills/ui-ux-pro-max/.claude/skills/design-system/data/slide-layouts.csv +26 -0
- package/skills/ui-ux-pro-max/.claude/skills/design-system/data/slide-strategies.csv +16 -0
- package/skills/ui-ux-pro-max/.claude/skills/design-system/data/slide-typography.csv +15 -0
- package/skills/ui-ux-pro-max/.claude/skills/design-system/references/component-specs.md +236 -0
- package/skills/ui-ux-pro-max/.claude/skills/design-system/references/component-tokens.md +214 -0
- package/skills/ui-ux-pro-max/.claude/skills/design-system/references/primitive-tokens.md +203 -0
- package/skills/ui-ux-pro-max/.claude/skills/design-system/references/semantic-tokens.md +215 -0
- package/skills/ui-ux-pro-max/.claude/skills/design-system/references/states-and-variants.md +241 -0
- package/skills/ui-ux-pro-max/.claude/skills/design-system/references/tailwind-integration.md +251 -0
- package/skills/ui-ux-pro-max/.claude/skills/design-system/references/token-architecture.md +224 -0
- package/skills/ui-ux-pro-max/.claude/skills/design-system/scripts/embed-tokens.cjs +99 -0
- package/skills/ui-ux-pro-max/.claude/skills/design-system/scripts/fetch-background.py +317 -0
- package/skills/ui-ux-pro-max/.claude/skills/design-system/scripts/generate-slide.py +753 -0
- package/skills/ui-ux-pro-max/.claude/skills/design-system/scripts/generate-tokens.cjs +205 -0
- package/skills/ui-ux-pro-max/.claude/skills/design-system/scripts/html-token-validator.py +327 -0
- package/skills/ui-ux-pro-max/.claude/skills/design-system/scripts/search-slides.py +218 -0
- package/skills/ui-ux-pro-max/.claude/skills/design-system/scripts/slide-token-validator.py +35 -0
- package/skills/ui-ux-pro-max/.claude/skills/design-system/scripts/slide_search_core.py +453 -0
- package/skills/ui-ux-pro-max/.claude/skills/design-system/scripts/validate-tokens.cjs +251 -0
- package/skills/ui-ux-pro-max/.claude/skills/design-system/templates/design-tokens-starter.json +143 -0
- package/skills/ui-ux-pro-max/.claude/skills/slides/SKILL.md +42 -0
- package/skills/ui-ux-pro-max/.claude/skills/slides/references/copywriting-formulas.md +84 -0
- package/skills/ui-ux-pro-max/.claude/skills/slides/references/create.md +4 -0
- package/skills/ui-ux-pro-max/.claude/skills/slides/references/html-template.md +295 -0
- package/skills/ui-ux-pro-max/.claude/skills/slides/references/layout-patterns.md +137 -0
- package/skills/ui-ux-pro-max/.claude/skills/slides/references/slide-strategies.md +94 -0
- package/skills/ui-ux-pro-max/.claude/skills/ui-styling/LICENSE.txt +202 -0
- package/skills/ui-ux-pro-max/.claude/skills/ui-styling/SKILL.md +324 -0
- package/skills/ui-ux-pro-max/.claude/skills/ui-styling/canvas-fonts/ArsenalSC-OFL.txt +93 -0
- package/skills/ui-ux-pro-max/.claude/skills/ui-styling/canvas-fonts/ArsenalSC-Regular.ttf +0 -0
- package/skills/ui-ux-pro-max/.claude/skills/ui-styling/canvas-fonts/BigShoulders-Bold.ttf +0 -0
- package/skills/ui-ux-pro-max/.claude/skills/ui-styling/canvas-fonts/BigShoulders-OFL.txt +93 -0
- package/skills/ui-ux-pro-max/.claude/skills/ui-styling/canvas-fonts/BigShoulders-Regular.ttf +0 -0
- package/skills/ui-ux-pro-max/.claude/skills/ui-styling/canvas-fonts/Boldonse-OFL.txt +93 -0
- package/skills/ui-ux-pro-max/.claude/skills/ui-styling/canvas-fonts/Boldonse-Regular.ttf +0 -0
- package/skills/ui-ux-pro-max/.claude/skills/ui-styling/canvas-fonts/BricolageGrotesque-Bold.ttf +0 -0
- package/skills/ui-ux-pro-max/.claude/skills/ui-styling/canvas-fonts/BricolageGrotesque-OFL.txt +93 -0
- package/skills/ui-ux-pro-max/.claude/skills/ui-styling/canvas-fonts/BricolageGrotesque-Regular.ttf +0 -0
- package/skills/ui-ux-pro-max/.claude/skills/ui-styling/canvas-fonts/CrimsonPro-Bold.ttf +0 -0
- package/skills/ui-ux-pro-max/.claude/skills/ui-styling/canvas-fonts/CrimsonPro-Italic.ttf +0 -0
- package/skills/ui-ux-pro-max/.claude/skills/ui-styling/canvas-fonts/CrimsonPro-OFL.txt +93 -0
- package/skills/ui-ux-pro-max/.claude/skills/ui-styling/canvas-fonts/CrimsonPro-Regular.ttf +0 -0
- package/skills/ui-ux-pro-max/.claude/skills/ui-styling/canvas-fonts/DMMono-OFL.txt +93 -0
- package/skills/ui-ux-pro-max/.claude/skills/ui-styling/canvas-fonts/DMMono-Regular.ttf +0 -0
- package/skills/ui-ux-pro-max/.claude/skills/ui-styling/canvas-fonts/EricaOne-OFL.txt +94 -0
- package/skills/ui-ux-pro-max/.claude/skills/ui-styling/canvas-fonts/EricaOne-Regular.ttf +0 -0
- package/skills/ui-ux-pro-max/.claude/skills/ui-styling/canvas-fonts/GeistMono-Bold.ttf +0 -0
- package/skills/ui-ux-pro-max/.claude/skills/ui-styling/canvas-fonts/GeistMono-OFL.txt +93 -0
- package/skills/ui-ux-pro-max/.claude/skills/ui-styling/canvas-fonts/GeistMono-Regular.ttf +0 -0
- package/skills/ui-ux-pro-max/.claude/skills/ui-styling/canvas-fonts/Gloock-OFL.txt +93 -0
- package/skills/ui-ux-pro-max/.claude/skills/ui-styling/canvas-fonts/Gloock-Regular.ttf +0 -0
- package/skills/ui-ux-pro-max/.claude/skills/ui-styling/canvas-fonts/IBMPlexMono-Bold.ttf +0 -0
- package/skills/ui-ux-pro-max/.claude/skills/ui-styling/canvas-fonts/IBMPlexMono-OFL.txt +93 -0
- package/skills/ui-ux-pro-max/.claude/skills/ui-styling/canvas-fonts/IBMPlexMono-Regular.ttf +0 -0
- package/skills/ui-ux-pro-max/.claude/skills/ui-styling/canvas-fonts/IBMPlexSerif-Bold.ttf +0 -0
- package/skills/ui-ux-pro-max/.claude/skills/ui-styling/canvas-fonts/IBMPlexSerif-BoldItalic.ttf +0 -0
- package/skills/ui-ux-pro-max/.claude/skills/ui-styling/canvas-fonts/IBMPlexSerif-Italic.ttf +0 -0
- package/skills/ui-ux-pro-max/.claude/skills/ui-styling/canvas-fonts/IBMPlexSerif-Regular.ttf +0 -0
- package/skills/ui-ux-pro-max/.claude/skills/ui-styling/canvas-fonts/InstrumentSans-Bold.ttf +0 -0
- package/skills/ui-ux-pro-max/.claude/skills/ui-styling/canvas-fonts/InstrumentSans-BoldItalic.ttf +0 -0
- package/skills/ui-ux-pro-max/.claude/skills/ui-styling/canvas-fonts/InstrumentSans-Italic.ttf +0 -0
- package/skills/ui-ux-pro-max/.claude/skills/ui-styling/canvas-fonts/InstrumentSans-OFL.txt +93 -0
- package/skills/ui-ux-pro-max/.claude/skills/ui-styling/canvas-fonts/InstrumentSans-Regular.ttf +0 -0
- package/skills/ui-ux-pro-max/.claude/skills/ui-styling/canvas-fonts/InstrumentSerif-Italic.ttf +0 -0
- package/skills/ui-ux-pro-max/.claude/skills/ui-styling/canvas-fonts/InstrumentSerif-Regular.ttf +0 -0
- package/skills/ui-ux-pro-max/.claude/skills/ui-styling/canvas-fonts/Italiana-OFL.txt +93 -0
- package/skills/ui-ux-pro-max/.claude/skills/ui-styling/canvas-fonts/Italiana-Regular.ttf +0 -0
- package/skills/ui-ux-pro-max/.claude/skills/ui-styling/canvas-fonts/JetBrainsMono-Bold.ttf +0 -0
- package/skills/ui-ux-pro-max/.claude/skills/ui-styling/canvas-fonts/JetBrainsMono-OFL.txt +93 -0
- package/skills/ui-ux-pro-max/.claude/skills/ui-styling/canvas-fonts/JetBrainsMono-Regular.ttf +0 -0
- package/skills/ui-ux-pro-max/.claude/skills/ui-styling/canvas-fonts/Jura-Light.ttf +0 -0
- package/skills/ui-ux-pro-max/.claude/skills/ui-styling/canvas-fonts/Jura-Medium.ttf +0 -0
- package/skills/ui-ux-pro-max/.claude/skills/ui-styling/canvas-fonts/Jura-OFL.txt +93 -0
- package/skills/ui-ux-pro-max/.claude/skills/ui-styling/canvas-fonts/LibreBaskerville-OFL.txt +93 -0
- package/skills/ui-ux-pro-max/.claude/skills/ui-styling/canvas-fonts/LibreBaskerville-Regular.ttf +0 -0
- package/skills/ui-ux-pro-max/.claude/skills/ui-styling/canvas-fonts/Lora-Bold.ttf +0 -0
- package/skills/ui-ux-pro-max/.claude/skills/ui-styling/canvas-fonts/Lora-BoldItalic.ttf +0 -0
- package/skills/ui-ux-pro-max/.claude/skills/ui-styling/canvas-fonts/Lora-Italic.ttf +0 -0
- package/skills/ui-ux-pro-max/.claude/skills/ui-styling/canvas-fonts/Lora-OFL.txt +93 -0
- package/skills/ui-ux-pro-max/.claude/skills/ui-styling/canvas-fonts/Lora-Regular.ttf +0 -0
- package/skills/ui-ux-pro-max/.claude/skills/ui-styling/canvas-fonts/NationalPark-Bold.ttf +0 -0
- package/skills/ui-ux-pro-max/.claude/skills/ui-styling/canvas-fonts/NationalPark-OFL.txt +93 -0
- package/skills/ui-ux-pro-max/.claude/skills/ui-styling/canvas-fonts/NationalPark-Regular.ttf +0 -0
- package/skills/ui-ux-pro-max/.claude/skills/ui-styling/canvas-fonts/NothingYouCouldDo-OFL.txt +93 -0
- package/skills/ui-ux-pro-max/.claude/skills/ui-styling/canvas-fonts/NothingYouCouldDo-Regular.ttf +0 -0
- package/skills/ui-ux-pro-max/.claude/skills/ui-styling/canvas-fonts/Outfit-Bold.ttf +0 -0
- package/skills/ui-ux-pro-max/.claude/skills/ui-styling/canvas-fonts/Outfit-OFL.txt +93 -0
- package/skills/ui-ux-pro-max/.claude/skills/ui-styling/canvas-fonts/Outfit-Regular.ttf +0 -0
- package/skills/ui-ux-pro-max/.claude/skills/ui-styling/canvas-fonts/PixelifySans-Medium.ttf +0 -0
- package/skills/ui-ux-pro-max/.claude/skills/ui-styling/canvas-fonts/PixelifySans-OFL.txt +93 -0
- package/skills/ui-ux-pro-max/.claude/skills/ui-styling/canvas-fonts/PoiretOne-OFL.txt +93 -0
- package/skills/ui-ux-pro-max/.claude/skills/ui-styling/canvas-fonts/PoiretOne-Regular.ttf +0 -0
- package/skills/ui-ux-pro-max/.claude/skills/ui-styling/canvas-fonts/RedHatMono-Bold.ttf +0 -0
- package/skills/ui-ux-pro-max/.claude/skills/ui-styling/canvas-fonts/RedHatMono-OFL.txt +93 -0
- package/skills/ui-ux-pro-max/.claude/skills/ui-styling/canvas-fonts/RedHatMono-Regular.ttf +0 -0
- package/skills/ui-ux-pro-max/.claude/skills/ui-styling/canvas-fonts/Silkscreen-OFL.txt +93 -0
- package/skills/ui-ux-pro-max/.claude/skills/ui-styling/canvas-fonts/Silkscreen-Regular.ttf +0 -0
- package/skills/ui-ux-pro-max/.claude/skills/ui-styling/canvas-fonts/SmoochSans-Medium.ttf +0 -0
- package/skills/ui-ux-pro-max/.claude/skills/ui-styling/canvas-fonts/SmoochSans-OFL.txt +93 -0
- package/skills/ui-ux-pro-max/.claude/skills/ui-styling/canvas-fonts/Tektur-Medium.ttf +0 -0
- package/skills/ui-ux-pro-max/.claude/skills/ui-styling/canvas-fonts/Tektur-OFL.txt +93 -0
- package/skills/ui-ux-pro-max/.claude/skills/ui-styling/canvas-fonts/Tektur-Regular.ttf +0 -0
- package/skills/ui-ux-pro-max/.claude/skills/ui-styling/canvas-fonts/WorkSans-Bold.ttf +0 -0
- package/skills/ui-ux-pro-max/.claude/skills/ui-styling/canvas-fonts/WorkSans-BoldItalic.ttf +0 -0
- package/skills/ui-ux-pro-max/.claude/skills/ui-styling/canvas-fonts/WorkSans-Italic.ttf +0 -0
- package/skills/ui-ux-pro-max/.claude/skills/ui-styling/canvas-fonts/WorkSans-OFL.txt +93 -0
- package/skills/ui-ux-pro-max/.claude/skills/ui-styling/canvas-fonts/WorkSans-Regular.ttf +0 -0
- package/skills/ui-ux-pro-max/.claude/skills/ui-styling/canvas-fonts/YoungSerif-OFL.txt +93 -0
- package/skills/ui-ux-pro-max/.claude/skills/ui-styling/canvas-fonts/YoungSerif-Regular.ttf +0 -0
- package/skills/ui-ux-pro-max/.claude/skills/ui-styling/references/canvas-design-system.md +320 -0
- package/skills/ui-ux-pro-max/.claude/skills/ui-styling/references/shadcn-accessibility.md +471 -0
- package/skills/ui-ux-pro-max/.claude/skills/ui-styling/references/shadcn-components.md +424 -0
- package/skills/ui-ux-pro-max/.claude/skills/ui-styling/references/shadcn-theming.md +373 -0
- package/skills/ui-ux-pro-max/.claude/skills/ui-styling/references/tailwind-customization.md +483 -0
- package/skills/ui-ux-pro-max/.claude/skills/ui-styling/references/tailwind-responsive.md +382 -0
- package/skills/ui-ux-pro-max/.claude/skills/ui-styling/references/tailwind-utilities.md +455 -0
- package/skills/ui-ux-pro-max/.claude/skills/ui-styling/scripts/.coverage +0 -0
- package/skills/ui-ux-pro-max/.claude/skills/ui-styling/scripts/requirements.txt +17 -0
- package/skills/ui-ux-pro-max/.claude/skills/ui-styling/scripts/shadcn_add.py +292 -0
- package/skills/ui-ux-pro-max/.claude/skills/ui-styling/scripts/tailwind_config_gen.py +456 -0
- package/skills/ui-ux-pro-max/.claude/skills/ui-styling/scripts/tests/coverage-ui.json +1 -0
- package/skills/ui-ux-pro-max/.claude/skills/ui-styling/scripts/tests/requirements.txt +3 -0
- package/skills/ui-ux-pro-max/.claude/skills/ui-styling/scripts/tests/test_shadcn_add.py +266 -0
- package/skills/ui-ux-pro-max/.claude/skills/ui-styling/scripts/tests/test_tailwind_config_gen.py +336 -0
- package/skills/ui-ux-pro-max/.claude/skills/ui-ux-pro-max/SKILL.md +659 -0
- package/skills/ui-ux-pro-max/.claude-plugin/marketplace.json +35 -0
- package/skills/ui-ux-pro-max/.claude-plugin/plugin.json +11 -0
- package/skills/ui-ux-pro-max/.github/workflows/claude-code-review.yml +44 -0
- package/skills/ui-ux-pro-max/.github/workflows/claude.yml +50 -0
- package/skills/ui-ux-pro-max/.github/workflows/python-package-conda.yml +34 -0
- package/skills/ui-ux-pro-max/CLAUDE.md +98 -0
- package/skills/ui-ux-pro-max/LICENSE +21 -0
- package/skills/ui-ux-pro-max/README.md +513 -0
- package/skills/ui-ux-pro-max/SKILL.md +292 -0
- package/skills/ui-ux-pro-max/cli/README.md +63 -0
- package/skills/ui-ux-pro-max/cli/assets/data/_sync_all.py +414 -0
- package/skills/ui-ux-pro-max/cli/assets/data/app-interface.csv +31 -0
- package/skills/ui-ux-pro-max/cli/assets/data/charts.csv +26 -0
- package/skills/ui-ux-pro-max/cli/assets/data/colors.csv +162 -0
- package/skills/ui-ux-pro-max/cli/assets/data/design.csv +1776 -0
- package/skills/ui-ux-pro-max/cli/assets/data/draft.csv +1779 -0
- package/skills/ui-ux-pro-max/cli/assets/data/google-fonts.csv +1924 -0
- package/skills/ui-ux-pro-max/cli/assets/data/icons.csv +105 -0
- package/skills/ui-ux-pro-max/cli/assets/data/landing.csv +31 -0
- package/skills/ui-ux-pro-max/cli/assets/data/products.csv +162 -0
- package/skills/ui-ux-pro-max/cli/assets/data/react-performance.csv +45 -0
- package/skills/ui-ux-pro-max/cli/assets/data/stacks/angular.csv +51 -0
- package/skills/ui-ux-pro-max/cli/assets/data/stacks/astro.csv +54 -0
- package/skills/ui-ux-pro-max/cli/assets/data/stacks/flutter.csv +53 -0
- package/skills/ui-ux-pro-max/cli/assets/data/stacks/html-tailwind.csv +56 -0
- package/skills/ui-ux-pro-max/cli/assets/data/stacks/jetpack-compose.csv +53 -0
- package/skills/ui-ux-pro-max/cli/assets/data/stacks/laravel.csv +51 -0
- package/skills/ui-ux-pro-max/cli/assets/data/stacks/nextjs.csv +53 -0
- package/skills/ui-ux-pro-max/cli/assets/data/stacks/nuxt-ui.csv +51 -0
- package/skills/ui-ux-pro-max/cli/assets/data/stacks/nuxtjs.csv +59 -0
- package/skills/ui-ux-pro-max/cli/assets/data/stacks/react-native.csv +52 -0
- package/skills/ui-ux-pro-max/cli/assets/data/stacks/react.csv +54 -0
- package/skills/ui-ux-pro-max/cli/assets/data/stacks/shadcn.csv +61 -0
- package/skills/ui-ux-pro-max/cli/assets/data/stacks/svelte.csv +54 -0
- package/skills/ui-ux-pro-max/cli/assets/data/stacks/swiftui.csv +51 -0
- package/skills/ui-ux-pro-max/cli/assets/data/stacks/threejs.csv +54 -0
- package/skills/ui-ux-pro-max/cli/assets/data/stacks/vue.csv +50 -0
- package/skills/ui-ux-pro-max/cli/assets/data/styles.csv +85 -0
- package/skills/ui-ux-pro-max/cli/assets/data/typography.csv +74 -0
- package/skills/ui-ux-pro-max/cli/assets/data/ui-reasoning.csv +162 -0
- package/skills/ui-ux-pro-max/cli/assets/data/ux-guidelines.csv +100 -0
- package/skills/ui-ux-pro-max/cli/assets/scripts/core.py +247 -0
- package/skills/ui-ux-pro-max/cli/assets/scripts/design_system.py +1148 -0
- package/skills/ui-ux-pro-max/cli/assets/scripts/search.py +114 -0
- package/skills/ui-ux-pro-max/cli/assets/templates/base/quick-reference.md +297 -0
- package/skills/ui-ux-pro-max/cli/assets/templates/base/skill-content.md +353 -0
- package/skills/ui-ux-pro-max/cli/assets/templates/platforms/agent.json +21 -0
- package/skills/ui-ux-pro-max/cli/assets/templates/platforms/augment.json +18 -0
- package/skills/ui-ux-pro-max/cli/assets/templates/platforms/claude.json +21 -0
- package/skills/ui-ux-pro-max/cli/assets/templates/platforms/codebuddy.json +21 -0
- package/skills/ui-ux-pro-max/cli/assets/templates/platforms/codex.json +21 -0
- package/skills/ui-ux-pro-max/cli/assets/templates/platforms/continue.json +21 -0
- package/skills/ui-ux-pro-max/cli/assets/templates/platforms/copilot.json +21 -0
- package/skills/ui-ux-pro-max/cli/assets/templates/platforms/cursor.json +21 -0
- package/skills/ui-ux-pro-max/cli/assets/templates/platforms/droid.json +21 -0
- package/skills/ui-ux-pro-max/cli/assets/templates/platforms/gemini.json +21 -0
- package/skills/ui-ux-pro-max/cli/assets/templates/platforms/kilocode.json +21 -0
- package/skills/ui-ux-pro-max/cli/assets/templates/platforms/kiro.json +21 -0
- package/skills/ui-ux-pro-max/cli/assets/templates/platforms/opencode.json +21 -0
- package/skills/ui-ux-pro-max/cli/assets/templates/platforms/qoder.json +21 -0
- package/skills/ui-ux-pro-max/cli/assets/templates/platforms/roocode.json +21 -0
- package/skills/ui-ux-pro-max/cli/assets/templates/platforms/trae.json +21 -0
- package/skills/ui-ux-pro-max/cli/assets/templates/platforms/warp.json +18 -0
- package/skills/ui-ux-pro-max/cli/assets/templates/platforms/windsurf.json +21 -0
- package/skills/ui-ux-pro-max/cli/bun.lock +77 -0
- package/skills/ui-ux-pro-max/cli/package-lock.json +370 -0
- package/skills/ui-ux-pro-max/cli/package.json +48 -0
- package/skills/ui-ux-pro-max/cli/src/commands/init.ts +216 -0
- package/skills/ui-ux-pro-max/cli/src/commands/uninstall.ts +135 -0
- package/skills/ui-ux-pro-max/cli/src/commands/update.ts +36 -0
- package/skills/ui-ux-pro-max/cli/src/commands/versions.ts +42 -0
- package/skills/ui-ux-pro-max/cli/src/index.ts +83 -0
- package/skills/ui-ux-pro-max/cli/src/types/index.ts +68 -0
- package/skills/ui-ux-pro-max/cli/src/utils/detect.ts +120 -0
- package/skills/ui-ux-pro-max/cli/src/utils/extract.ts +149 -0
- package/skills/ui-ux-pro-max/cli/src/utils/github.ts +104 -0
- package/skills/ui-ux-pro-max/cli/src/utils/logger.ts +11 -0
- package/skills/ui-ux-pro-max/cli/src/utils/template.ts +243 -0
- package/skills/ui-ux-pro-max/cli/tsconfig.json +17 -0
- package/skills/ui-ux-pro-max/data/charts.csv +26 -0
- package/skills/ui-ux-pro-max/data/colors.csv +97 -0
- package/skills/ui-ux-pro-max/data/icons.csv +101 -0
- package/skills/ui-ux-pro-max/data/landing.csv +31 -0
- package/skills/ui-ux-pro-max/data/products.csv +97 -0
- package/skills/ui-ux-pro-max/data/react-performance.csv +45 -0
- package/skills/ui-ux-pro-max/data/stacks/astro.csv +54 -0
- package/skills/ui-ux-pro-max/data/stacks/flutter.csv +53 -0
- package/skills/ui-ux-pro-max/data/stacks/html-tailwind.csv +56 -0
- package/skills/ui-ux-pro-max/data/stacks/jetpack-compose.csv +53 -0
- package/skills/ui-ux-pro-max/data/stacks/nextjs.csv +53 -0
- package/skills/ui-ux-pro-max/data/stacks/nuxt-ui.csv +51 -0
- package/skills/ui-ux-pro-max/data/stacks/nuxtjs.csv +59 -0
- package/skills/ui-ux-pro-max/data/stacks/react-native.csv +52 -0
- package/skills/ui-ux-pro-max/data/stacks/react.csv +54 -0
- package/skills/ui-ux-pro-max/data/stacks/shadcn.csv +61 -0
- package/skills/ui-ux-pro-max/data/stacks/svelte.csv +54 -0
- package/skills/ui-ux-pro-max/data/stacks/swiftui.csv +51 -0
- package/skills/ui-ux-pro-max/data/stacks/vue.csv +50 -0
- package/skills/ui-ux-pro-max/data/styles.csv +68 -0
- package/skills/ui-ux-pro-max/data/typography.csv +58 -0
- package/skills/ui-ux-pro-max/data/ui-reasoning.csv +101 -0
- package/skills/ui-ux-pro-max/data/ux-guidelines.csv +100 -0
- package/skills/ui-ux-pro-max/data/web-interface.csv +31 -0
- package/skills/ui-ux-pro-max/docs//344/270/211/344/270/252 data-scripts-templates /347/232/204/345/214/272/345/210/253.md" +30 -0
- package/skills/ui-ux-pro-max/preview/xiaomaomi-app.html +411 -0
- package/skills/ui-ux-pro-max/screenshots/website.png +0 -0
- package/skills/ui-ux-pro-max/scripts/core.py +253 -0
- package/skills/ui-ux-pro-max/scripts/design_system.py +1067 -0
- package/skills/ui-ux-pro-max/scripts/search.py +114 -0
- package/skills/ui-ux-pro-max/skill.json +41 -0
- package/skills/ui-ux-pro-max/src/ui-ux-pro-max/data/_sync_all.py +414 -0
- package/skills/ui-ux-pro-max/src/ui-ux-pro-max/data/app-interface.csv +31 -0
- package/skills/ui-ux-pro-max/src/ui-ux-pro-max/data/charts.csv +26 -0
- package/skills/ui-ux-pro-max/src/ui-ux-pro-max/data/colors.csv +162 -0
- package/skills/ui-ux-pro-max/src/ui-ux-pro-max/data/design.csv +1776 -0
- package/skills/ui-ux-pro-max/src/ui-ux-pro-max/data/draft.csv +1779 -0
- package/skills/ui-ux-pro-max/src/ui-ux-pro-max/data/google-fonts.csv +1924 -0
- package/skills/ui-ux-pro-max/src/ui-ux-pro-max/data/icons.csv +106 -0
- package/skills/ui-ux-pro-max/src/ui-ux-pro-max/data/landing.csv +35 -0
- package/skills/ui-ux-pro-max/src/ui-ux-pro-max/data/products.csv +162 -0
- package/skills/ui-ux-pro-max/src/ui-ux-pro-max/data/react-performance.csv +45 -0
- package/skills/ui-ux-pro-max/src/ui-ux-pro-max/data/stacks/angular.csv +51 -0
- package/skills/ui-ux-pro-max/src/ui-ux-pro-max/data/stacks/astro.csv +54 -0
- package/skills/ui-ux-pro-max/src/ui-ux-pro-max/data/stacks/flutter.csv +53 -0
- package/skills/ui-ux-pro-max/src/ui-ux-pro-max/data/stacks/html-tailwind.csv +56 -0
- package/skills/ui-ux-pro-max/src/ui-ux-pro-max/data/stacks/jetpack-compose.csv +53 -0
- package/skills/ui-ux-pro-max/src/ui-ux-pro-max/data/stacks/laravel.csv +51 -0
- package/skills/ui-ux-pro-max/src/ui-ux-pro-max/data/stacks/nextjs.csv +53 -0
- package/skills/ui-ux-pro-max/src/ui-ux-pro-max/data/stacks/nuxt-ui.csv +51 -0
- package/skills/ui-ux-pro-max/src/ui-ux-pro-max/data/stacks/nuxtjs.csv +59 -0
- package/skills/ui-ux-pro-max/src/ui-ux-pro-max/data/stacks/react-native.csv +52 -0
- package/skills/ui-ux-pro-max/src/ui-ux-pro-max/data/stacks/react.csv +54 -0
- package/skills/ui-ux-pro-max/src/ui-ux-pro-max/data/stacks/shadcn.csv +61 -0
- package/skills/ui-ux-pro-max/src/ui-ux-pro-max/data/stacks/svelte.csv +54 -0
- package/skills/ui-ux-pro-max/src/ui-ux-pro-max/data/stacks/swiftui.csv +51 -0
- package/skills/ui-ux-pro-max/src/ui-ux-pro-max/data/stacks/threejs.csv +54 -0
- package/skills/ui-ux-pro-max/src/ui-ux-pro-max/data/stacks/vue.csv +50 -0
- package/skills/ui-ux-pro-max/src/ui-ux-pro-max/data/styles.csv +85 -0
- package/skills/ui-ux-pro-max/src/ui-ux-pro-max/data/typography.csv +74 -0
- package/skills/ui-ux-pro-max/src/ui-ux-pro-max/data/ui-reasoning.csv +162 -0
- package/skills/ui-ux-pro-max/src/ui-ux-pro-max/data/ux-guidelines.csv +100 -0
- package/skills/ui-ux-pro-max/src/ui-ux-pro-max/scripts/core.py +262 -0
- package/skills/ui-ux-pro-max/src/ui-ux-pro-max/scripts/design_system.py +1148 -0
- package/skills/ui-ux-pro-max/src/ui-ux-pro-max/scripts/search.py +114 -0
- package/skills/ui-ux-pro-max/src/ui-ux-pro-max/templates/base/quick-reference.md +297 -0
- package/skills/ui-ux-pro-max/src/ui-ux-pro-max/templates/base/skill-content.md +358 -0
- package/skills/ui-ux-pro-max/src/ui-ux-pro-max/templates/platforms/agent.json +21 -0
- package/skills/ui-ux-pro-max/src/ui-ux-pro-max/templates/platforms/augment.json +18 -0
- package/skills/ui-ux-pro-max/src/ui-ux-pro-max/templates/platforms/claude.json +21 -0
- package/skills/ui-ux-pro-max/src/ui-ux-pro-max/templates/platforms/codebuddy.json +21 -0
- package/skills/ui-ux-pro-max/src/ui-ux-pro-max/templates/platforms/codex.json +21 -0
- package/skills/ui-ux-pro-max/src/ui-ux-pro-max/templates/platforms/continue.json +21 -0
- package/skills/ui-ux-pro-max/src/ui-ux-pro-max/templates/platforms/copilot.json +21 -0
- package/skills/ui-ux-pro-max/src/ui-ux-pro-max/templates/platforms/cursor.json +21 -0
- package/skills/ui-ux-pro-max/src/ui-ux-pro-max/templates/platforms/droid.json +21 -0
- package/skills/ui-ux-pro-max/src/ui-ux-pro-max/templates/platforms/gemini.json +21 -0
- package/skills/ui-ux-pro-max/src/ui-ux-pro-max/templates/platforms/kilocode.json +21 -0
- package/skills/ui-ux-pro-max/src/ui-ux-pro-max/templates/platforms/kiro.json +21 -0
- package/skills/ui-ux-pro-max/src/ui-ux-pro-max/templates/platforms/opencode.json +21 -0
- package/skills/ui-ux-pro-max/src/ui-ux-pro-max/templates/platforms/qoder.json +21 -0
- package/skills/ui-ux-pro-max/src/ui-ux-pro-max/templates/platforms/roocode.json +21 -0
- package/skills/ui-ux-pro-max/src/ui-ux-pro-max/templates/platforms/trae.json +21 -0
- package/skills/ui-ux-pro-max/src/ui-ux-pro-max/templates/platforms/warp.json +18 -0
- package/skills/ui-ux-pro-max/src/ui-ux-pro-max/templates/platforms/windsurf.json +21 -0
- package/skills/uncloud/SKILL.md +343 -0
- package/skills/upgrading-react-native/SKILL.md +58 -0
- package/skills/using-git-worktrees/SKILL.md +215 -0
- package/skills/vercel/SKILL.md +161 -0
- package/skills/vercel/metadata.json +16 -0
- package/skills/verification-loop/SKILL.md +126 -0
- package/skills/view-transitions-api/SKILL.md +108 -0
- package/skills/vite-patterns/SKILL.md +449 -0
- package/skills/vue3-composition-patterns/SKILL.md +375 -0
- package/skills/waza-habits/SKILL.md +22 -0
- package/skills/webapp-testing/LICENSE.txt +202 -0
- package/skills/webapp-testing/SKILL.md +96 -0
- package/skills/webapp-testing/examples/console_logging.py +35 -0
- package/skills/webapp-testing/examples/element_discovery.py +40 -0
- package/skills/webapp-testing/examples/static_html_automation.py +33 -0
- package/skills/webapp-testing/scripts/with_server.py +106 -0
- package/src/orchestrator/builder.ts +234 -0
- package/src/orchestrator/executor.ts +386 -0
- package/src/orchestrator/index.ts +535 -0
- package/src/orchestrator/loader.ts +237 -0
- package/src/orchestrator/registry.ts +211 -0
- package/src/orchestrator/schema.ts +141 -0
- package/src/orchestrator/session.ts +128 -0
- package/src/orchestrator/types.ts +109 -0
- package/tsconfig.json +21 -0
|
@@ -0,0 +1,1111 @@
|
|
|
1
|
+
# Direct LLM backend for semantic extraction — supports Claude, Kimi K2.6,
|
|
2
|
+
# Gemini, and OpenAI.
|
|
3
|
+
# Used by `graphify extract . --backend gemini` and the benchmark scripts.
|
|
4
|
+
# The default graphify pipeline uses Claude Code subagents via skill.md;
|
|
5
|
+
# this module provides a direct API path for non-Claude-Code environments.
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
import json
|
|
9
|
+
import os
|
|
10
|
+
import sys
|
|
11
|
+
import time
|
|
12
|
+
from collections.abc import Callable
|
|
13
|
+
from concurrent.futures import ThreadPoolExecutor, as_completed
|
|
14
|
+
from pathlib import Path
|
|
15
|
+
|
|
16
|
+
# `_read_files` truncates each file at this many characters before joining into
|
|
17
|
+
# the user message. Token estimates use the same cap so packing matches reality.
|
|
18
|
+
_FILE_CHAR_CAP = 20_000
|
|
19
|
+
# `_read_files` also wraps each file in a `=== {rel} ===\n...\n\n` separator;
|
|
20
|
+
# this is roughly the per-file overhead in characters that the prompt adds.
|
|
21
|
+
_PER_FILE_OVERHEAD_CHARS = 80
|
|
22
|
+
# Coarse fallback used only when `tiktoken` is not installed. 1 token ≈ 4 chars
|
|
23
|
+
# is the standard heuristic for English/code on BPE tokenizers.
|
|
24
|
+
_CHARS_PER_TOKEN = 4
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def _get_tokenizer():
|
|
28
|
+
"""Return a tiktoken encoder for accurate token counts, or None if tiktoken
|
|
29
|
+
is not installed. We use `cl100k_base` (GPT-4 / GPT-3.5-turbo) as a proxy:
|
|
30
|
+
Kimi-K2 ships a tiktoken-based tokenizer with very similar BPE behaviour,
|
|
31
|
+
and Claude's tokenizer has a comparable token-to-char ratio for prose/code.
|
|
32
|
+
Estimates only need to be within ~5%, not exact.
|
|
33
|
+
"""
|
|
34
|
+
try:
|
|
35
|
+
import tiktoken
|
|
36
|
+
except ImportError:
|
|
37
|
+
return None
|
|
38
|
+
try:
|
|
39
|
+
return tiktoken.get_encoding("cl100k_base")
|
|
40
|
+
except Exception: # network failure on first-use download, etc.
|
|
41
|
+
return None
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
# Cached at import time. None if tiktoken is unavailable; consumers must handle.
|
|
45
|
+
_TOKENIZER = _get_tokenizer()
|
|
46
|
+
|
|
47
|
+
BACKENDS: dict[str, dict] = {
|
|
48
|
+
"claude": {
|
|
49
|
+
"base_url": "https://api.anthropic.com",
|
|
50
|
+
"default_model": "claude-sonnet-4-6",
|
|
51
|
+
"env_key": "ANTHROPIC_API_KEY",
|
|
52
|
+
"pricing": {"input": 3.0, "output": 15.0}, # USD per 1M tokens
|
|
53
|
+
"temperature": 0,
|
|
54
|
+
"max_tokens": 16384,
|
|
55
|
+
},
|
|
56
|
+
"kimi": {
|
|
57
|
+
"base_url": "https://api.moonshot.ai/v1",
|
|
58
|
+
"default_model": "kimi-k2.6",
|
|
59
|
+
"env_key": "MOONSHOT_API_KEY",
|
|
60
|
+
"pricing": {"input": 0.74, "output": 4.66}, # USD per 1M tokens
|
|
61
|
+
"temperature": None, # kimi-k2.6 enforces its own fixed temperature; sending any value raises 400
|
|
62
|
+
"max_tokens": 16384,
|
|
63
|
+
},
|
|
64
|
+
"ollama": {
|
|
65
|
+
"base_url": os.environ.get("OLLAMA_BASE_URL", "http://localhost:11434/v1"),
|
|
66
|
+
"default_model": os.environ.get("OLLAMA_MODEL", "qwen2.5-coder:7b"),
|
|
67
|
+
"env_key": "OLLAMA_API_KEY",
|
|
68
|
+
"pricing": {"input": 0.0, "output": 0.0},
|
|
69
|
+
"temperature": 0,
|
|
70
|
+
"max_tokens": 16384,
|
|
71
|
+
},
|
|
72
|
+
"gemini": {
|
|
73
|
+
"base_url": "https://generativelanguage.googleapis.com/v1beta/openai/",
|
|
74
|
+
"default_model": "gemini-3-flash-preview",
|
|
75
|
+
"env_keys": ["GEMINI_API_KEY", "GOOGLE_API_KEY"],
|
|
76
|
+
"model_env_key": "GRAPHIFY_GEMINI_MODEL",
|
|
77
|
+
"pricing": {"input": 0.50, "output": 3.00}, # USD per 1M tokens
|
|
78
|
+
"temperature": 0,
|
|
79
|
+
"reasoning_effort": "low",
|
|
80
|
+
"max_completion_tokens": 16384,
|
|
81
|
+
},
|
|
82
|
+
"openai": {
|
|
83
|
+
"base_url": "https://api.openai.com/v1",
|
|
84
|
+
"default_model": "gpt-4.1-mini",
|
|
85
|
+
"env_key": "OPENAI_API_KEY",
|
|
86
|
+
"model_env_key": "GRAPHIFY_OPENAI_MODEL",
|
|
87
|
+
"pricing": {"input": 0.40, "output": 1.60}, # USD per 1M tokens
|
|
88
|
+
"temperature": 0,
|
|
89
|
+
},
|
|
90
|
+
"deepseek": {
|
|
91
|
+
"base_url": "https://api.deepseek.com",
|
|
92
|
+
"default_model": "deepseek-v4-flash",
|
|
93
|
+
"env_key": "DEEPSEEK_API_KEY",
|
|
94
|
+
"model_env_key": "GRAPHIFY_DEEPSEEK_MODEL",
|
|
95
|
+
"pricing": {"input": 0.14, "output": 0.28}, # USD per 1M tokens (v4-flash)
|
|
96
|
+
# deepseek-reasoner / thinking-mode models silently ignore temperature;
|
|
97
|
+
# deepseek-chat / v4-flash (non-thinking) accept 0-2. Safe to send 0.
|
|
98
|
+
"temperature": 0,
|
|
99
|
+
"max_tokens": 16384,
|
|
100
|
+
},
|
|
101
|
+
"bedrock": {
|
|
102
|
+
"default_model": "anthropic.claude-3-5-sonnet-20241022-v2:0",
|
|
103
|
+
"model_env_key": "GRAPHIFY_BEDROCK_MODEL",
|
|
104
|
+
"pricing": {"input": 3.0, "output": 15.0}, # USD per 1M tokens
|
|
105
|
+
"temperature": 0,
|
|
106
|
+
"max_tokens": 16384,
|
|
107
|
+
},
|
|
108
|
+
"claude-cli": {
|
|
109
|
+
# Routes through the locally-installed `claude` CLI (Claude Code) using
|
|
110
|
+
# `-p --output-format json`. Authenticates via the user's existing
|
|
111
|
+
# Pro/Max subscription instead of a separate ANTHROPIC_API_KEY — costs
|
|
112
|
+
# are billed to the plan, not pay-as-you-go API credit.
|
|
113
|
+
"default_model": "claude-code-plan",
|
|
114
|
+
"pricing": {"input": 0.0, "output": 0.0},
|
|
115
|
+
"temperature": 0,
|
|
116
|
+
"max_tokens": 16384,
|
|
117
|
+
},
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
def _resolve_max_tokens(default: int) -> int:
|
|
122
|
+
"""Honour GRAPHIFY_MAX_OUTPUT_TOKENS env var override, else use backend default."""
|
|
123
|
+
raw = os.environ.get("GRAPHIFY_MAX_OUTPUT_TOKENS", "").strip()
|
|
124
|
+
if raw:
|
|
125
|
+
try:
|
|
126
|
+
v = int(raw)
|
|
127
|
+
if v > 0:
|
|
128
|
+
return v
|
|
129
|
+
except ValueError:
|
|
130
|
+
pass
|
|
131
|
+
return default
|
|
132
|
+
|
|
133
|
+
_EXTRACTION_SYSTEM = """\
|
|
134
|
+
You are a graphify semantic extraction agent. Extract a knowledge graph fragment from the files provided.
|
|
135
|
+
Output ONLY valid JSON — no explanation, no markdown fences, no preamble.
|
|
136
|
+
|
|
137
|
+
Rules:
|
|
138
|
+
- EXTRACTED: relationship explicit in source (import, call, citation, reference)
|
|
139
|
+
- INFERRED: reasonable inference (shared data structure, implied dependency)
|
|
140
|
+
- AMBIGUOUS: uncertain — flag for review, do not omit
|
|
141
|
+
|
|
142
|
+
Node ID format: lowercase, only [a-z0-9_], no dots or slashes.
|
|
143
|
+
Format: {stem}_{entity} where stem = filename without extension, entity = symbol name (both normalised).
|
|
144
|
+
|
|
145
|
+
Output exactly this schema:
|
|
146
|
+
{"nodes":[{"id":"stem_entity","label":"Human Readable Name","file_type":"code|document|paper|image|rationale|concept","source_file":"relative/path","source_location":null,"source_url":null,"captured_at":null,"author":null,"contributor":null}],"edges":[{"source":"node_id","target":"node_id","relation":"calls|implements|references|cites|conceptually_related_to|shares_data_with|semantically_similar_to","confidence":"EXTRACTED|INFERRED|AMBIGUOUS","confidence_score":1.0,"source_file":"relative/path","source_location":null,"weight":1.0}],"hyperedges":[],"input_tokens":0,"output_tokens":0}
|
|
147
|
+
"""
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
def _read_files(paths: list[Path], root: Path) -> str:
|
|
151
|
+
"""Return file contents formatted for the extraction prompt."""
|
|
152
|
+
parts: list[str] = []
|
|
153
|
+
for p in paths:
|
|
154
|
+
try:
|
|
155
|
+
rel = p.relative_to(root)
|
|
156
|
+
except ValueError:
|
|
157
|
+
rel = p
|
|
158
|
+
try:
|
|
159
|
+
content = p.read_text(encoding="utf-8", errors="replace")
|
|
160
|
+
except OSError:
|
|
161
|
+
continue
|
|
162
|
+
parts.append(f"=== {rel} ===\n{content[:20000]}")
|
|
163
|
+
return "\n\n".join(parts)
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
_LLM_JSON_MAX_BYTES = 10 * 1024 * 1024 # 10 MB hard cap before json.loads (F-016)
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
def _parse_llm_json(raw: str) -> dict:
|
|
170
|
+
"""Strip optional markdown fences and parse JSON. Returns empty fragment on failure.
|
|
171
|
+
|
|
172
|
+
Caps the input at `_LLM_JSON_MAX_BYTES` so a hostile or runaway model
|
|
173
|
+
response cannot exhaust memory inside `json.loads` (F-016).
|
|
174
|
+
"""
|
|
175
|
+
if len(raw) > _LLM_JSON_MAX_BYTES:
|
|
176
|
+
print(
|
|
177
|
+
f"[graphify] LLM response exceeds {_LLM_JSON_MAX_BYTES} bytes "
|
|
178
|
+
f"({len(raw)} bytes); refusing to parse and dropping chunk.",
|
|
179
|
+
file=sys.stderr,
|
|
180
|
+
)
|
|
181
|
+
return {"nodes": [], "edges": [], "hyperedges": []}
|
|
182
|
+
if raw.startswith("```"):
|
|
183
|
+
raw = raw.split("```", 2)[1]
|
|
184
|
+
if raw.startswith("json"):
|
|
185
|
+
raw = raw[4:]
|
|
186
|
+
raw = raw.rsplit("```", 1)[0]
|
|
187
|
+
try:
|
|
188
|
+
return json.loads(raw.strip())
|
|
189
|
+
except json.JSONDecodeError as exc:
|
|
190
|
+
print(f"[graphify] LLM returned invalid JSON, skipping chunk: {exc}", file=sys.stderr)
|
|
191
|
+
return {"nodes": [], "edges": [], "hyperedges": []}
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
def _response_is_hollow(raw_content: str | None, parsed: dict) -> bool:
|
|
195
|
+
"""Detect a successful HTTP response that yielded no usable extraction.
|
|
196
|
+
|
|
197
|
+
A local model under load (most often Ollama) can return HTTP 200 with an
|
|
198
|
+
empty / null `message.content`, with whitespace, or with a half-generated
|
|
199
|
+
JSON prefix that fails to parse. All of these collapse to a "successful"
|
|
200
|
+
call producing zero nodes and zero edges. Without this check the chunk
|
|
201
|
+
is silently dropped from the corpus because no exception is raised and
|
|
202
|
+
`finish_reason` is `"stop"` rather than `"length"`. By flagging the
|
|
203
|
+
result as hollow, callers can re-route it through the same bisection
|
|
204
|
+
path used for context-window overflow and `finish_reason="length"`.
|
|
205
|
+
"""
|
|
206
|
+
if raw_content is None or not raw_content.strip():
|
|
207
|
+
return True
|
|
208
|
+
nodes = parsed.get("nodes")
|
|
209
|
+
edges = parsed.get("edges")
|
|
210
|
+
hyperedges = parsed.get("hyperedges")
|
|
211
|
+
return not nodes and not edges and not hyperedges
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
def _backend_env_keys(backend: str) -> list[str]:
|
|
215
|
+
"""Return accepted API-key environment variables for a backend."""
|
|
216
|
+
cfg = BACKENDS[backend]
|
|
217
|
+
keys = cfg.get("env_keys")
|
|
218
|
+
if keys:
|
|
219
|
+
return list(keys)
|
|
220
|
+
env_key = cfg.get("env_key")
|
|
221
|
+
if env_key:
|
|
222
|
+
return [env_key]
|
|
223
|
+
return []
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
def _get_backend_api_key(backend: str) -> str:
|
|
227
|
+
"""Return the first configured API key for backend, or an empty string."""
|
|
228
|
+
for env_key in _backend_env_keys(backend):
|
|
229
|
+
value = os.environ.get(env_key)
|
|
230
|
+
if value:
|
|
231
|
+
return value
|
|
232
|
+
return ""
|
|
233
|
+
|
|
234
|
+
|
|
235
|
+
def _format_backend_env_keys(backend: str) -> str:
|
|
236
|
+
"""Return user-facing accepted API-key variable names."""
|
|
237
|
+
keys = _backend_env_keys(backend)
|
|
238
|
+
return " or ".join(keys) if keys else "AWS_PROFILE or AWS_REGION"
|
|
239
|
+
|
|
240
|
+
|
|
241
|
+
def _default_model_for_backend(backend: str) -> str:
|
|
242
|
+
"""Return configured model override or backend default model."""
|
|
243
|
+
cfg = BACKENDS[backend]
|
|
244
|
+
model_env_key = cfg.get("model_env_key")
|
|
245
|
+
if model_env_key:
|
|
246
|
+
model = os.environ.get(model_env_key)
|
|
247
|
+
if model:
|
|
248
|
+
return model
|
|
249
|
+
return cfg["default_model"]
|
|
250
|
+
|
|
251
|
+
|
|
252
|
+
def _call_openai_compat(
|
|
253
|
+
base_url: str,
|
|
254
|
+
api_key: str,
|
|
255
|
+
model: str,
|
|
256
|
+
user_message: str,
|
|
257
|
+
temperature: float | None = 0,
|
|
258
|
+
reasoning_effort: str | None = None,
|
|
259
|
+
max_completion_tokens: int = 8192,
|
|
260
|
+
*,
|
|
261
|
+
backend: str = "",
|
|
262
|
+
) -> dict:
|
|
263
|
+
"""Call any OpenAI-compatible API (Kimi, OpenAI, etc.) and return parsed JSON."""
|
|
264
|
+
try:
|
|
265
|
+
from openai import OpenAI
|
|
266
|
+
except ImportError as exc:
|
|
267
|
+
pkg_hint = "graphifyy[kimi]" if backend == "kimi" else "openai"
|
|
268
|
+
raise ImportError(
|
|
269
|
+
"Gemini/Kimi/Ollama/OpenAI-compatible extraction requires the openai package. "
|
|
270
|
+
f"Run: pip install {pkg_hint}"
|
|
271
|
+
) from exc
|
|
272
|
+
|
|
273
|
+
# Local backends (ollama, llama.cpp, vLLM) routinely take >60s for a
|
|
274
|
+
# single chunk on a large model — far longer than the openai SDK's
|
|
275
|
+
# default. Honour GRAPHIFY_API_TIMEOUT (seconds) for explicit override;
|
|
276
|
+
# default to 600s, which is long enough for a 31B model on a 16k chunk
|
|
277
|
+
# but still bounds runaway connections (issue #792 addendum).
|
|
278
|
+
timeout_raw = os.environ.get("GRAPHIFY_API_TIMEOUT", "").strip()
|
|
279
|
+
timeout_s: float = 600.0
|
|
280
|
+
if timeout_raw:
|
|
281
|
+
try:
|
|
282
|
+
v = float(timeout_raw)
|
|
283
|
+
if v > 0:
|
|
284
|
+
timeout_s = v
|
|
285
|
+
except ValueError:
|
|
286
|
+
pass
|
|
287
|
+
client = OpenAI(api_key=api_key, base_url=base_url, timeout=timeout_s)
|
|
288
|
+
kwargs: dict = {
|
|
289
|
+
"model": model,
|
|
290
|
+
"messages": [
|
|
291
|
+
{"role": "system", "content": _EXTRACTION_SYSTEM},
|
|
292
|
+
{"role": "user", "content": user_message},
|
|
293
|
+
],
|
|
294
|
+
"max_completion_tokens": max_completion_tokens,
|
|
295
|
+
}
|
|
296
|
+
if temperature is not None:
|
|
297
|
+
kwargs["temperature"] = temperature
|
|
298
|
+
if reasoning_effort is not None:
|
|
299
|
+
kwargs["reasoning_effort"] = reasoning_effort
|
|
300
|
+
# Kimi-k2.6 is a reasoning model — disable thinking so content isn't empty
|
|
301
|
+
if "moonshot" in base_url:
|
|
302
|
+
kwargs["extra_body"] = {"thinking": {"type": "disabled"}}
|
|
303
|
+
# Ollama defaults num_ctx to 2048 and silently truncates prompts larger
|
|
304
|
+
# than that — the symptom is hollow 200 OK responses after the first few
|
|
305
|
+
# chunks (#798). We derive num_ctx from the actual prompt size so we don't
|
|
306
|
+
# over-allocate KV-cache VRAM. Over-allocation (e.g. 128k slots for an 8k
|
|
307
|
+
# prompt on a 31B model) exhausts VRAM by chunk 4 and produces the same
|
|
308
|
+
# hollow-200 symptom — just from a different direction (#798 follow-up).
|
|
309
|
+
# Formula: actual input tokens + output cap + system prompt headroom.
|
|
310
|
+
# Capped at 131072 (enough for the default 60k token_budget); env var wins.
|
|
311
|
+
if backend == "ollama":
|
|
312
|
+
num_ctx_raw = os.environ.get("GRAPHIFY_OLLAMA_NUM_CTX", "").strip()
|
|
313
|
+
# Auto-derive num_ctx from actual chunk size regardless — used as the
|
|
314
|
+
# fallback and for the mismatch check below.
|
|
315
|
+
estimated_input = len(user_message) // _CHARS_PER_TOKEN + 400
|
|
316
|
+
auto_num_ctx = min(estimated_input + max_completion_tokens + 2000, 131072)
|
|
317
|
+
auto_num_ctx = max(auto_num_ctx, 8192)
|
|
318
|
+
if num_ctx_raw:
|
|
319
|
+
try:
|
|
320
|
+
num_ctx = int(num_ctx_raw)
|
|
321
|
+
except ValueError:
|
|
322
|
+
# Bad env var: fall through to auto-derivation (not 131072 —
|
|
323
|
+
# hardcoding the cap is what causes OOM on constrained VRAM).
|
|
324
|
+
print(
|
|
325
|
+
f"[graphify] GRAPHIFY_OLLAMA_NUM_CTX={num_ctx_raw!r} is not a valid integer; "
|
|
326
|
+
f"using auto-derived value ({auto_num_ctx}).",
|
|
327
|
+
file=sys.stderr,
|
|
328
|
+
)
|
|
329
|
+
num_ctx = auto_num_ctx
|
|
330
|
+
else:
|
|
331
|
+
# Warn when the pinned value is smaller than the estimated input —
|
|
332
|
+
# Ollama silently truncates the prompt and returns empty responses.
|
|
333
|
+
if num_ctx < estimated_input:
|
|
334
|
+
print(
|
|
335
|
+
f"[graphify] warning: GRAPHIFY_OLLAMA_NUM_CTX={num_ctx} is smaller than "
|
|
336
|
+
f"the estimated chunk input (~{estimated_input} tokens). Ollama will "
|
|
337
|
+
f"silently truncate the prompt and return empty responses. "
|
|
338
|
+
f"Try --token-budget {max(1024, num_ctx // 3)} or increase NUM_CTX.",
|
|
339
|
+
file=sys.stderr,
|
|
340
|
+
)
|
|
341
|
+
else:
|
|
342
|
+
# Estimate input tokens: user_message chars / 4 (standard BPE
|
|
343
|
+
# heuristic) + 400 for the system prompt, then add output headroom.
|
|
344
|
+
num_ctx = auto_num_ctx
|
|
345
|
+
keep_alive = os.environ.get("GRAPHIFY_OLLAMA_KEEP_ALIVE", "30m")
|
|
346
|
+
kwargs["extra_body"] = {"options": {"num_ctx": num_ctx}, "keep_alive": keep_alive}
|
|
347
|
+
resp = client.chat.completions.create(**kwargs)
|
|
348
|
+
if not resp.choices or resp.choices[0].message is None:
|
|
349
|
+
raise ValueError("LLM returned empty or filtered response")
|
|
350
|
+
raw_content = resp.choices[0].message.content
|
|
351
|
+
result = _parse_llm_json(raw_content or "{}")
|
|
352
|
+
result["input_tokens"] = resp.usage.prompt_tokens if resp.usage else 0
|
|
353
|
+
result["output_tokens"] = resp.usage.completion_tokens if resp.usage else 0
|
|
354
|
+
result["model"] = model
|
|
355
|
+
# `finish_reason == "length"` means the model hit max_completion_tokens
|
|
356
|
+
# mid-generation. The JSON we got back is truncated; callers should
|
|
357
|
+
# treat this as a signal to retry with smaller input.
|
|
358
|
+
result["finish_reason"] = resp.choices[0].finish_reason
|
|
359
|
+
# An overwhelmed local model (typically Ollama) can return HTTP 200 with
|
|
360
|
+
# empty / null content or unparseable half-generated JSON. The call looks
|
|
361
|
+
# successful, `finish_reason` is `"stop"`, and the chunk would be silently
|
|
362
|
+
# dropped from the corpus. Re-label as `"length"` so the adaptive retry
|
|
363
|
+
# layer bisects the chunk — same recovery as a true truncation.
|
|
364
|
+
if _response_is_hollow(raw_content, result) and result["finish_reason"] != "length":
|
|
365
|
+
print(
|
|
366
|
+
f"[graphify] {backend or 'backend'} returned a hollow response "
|
|
367
|
+
f"(content={'empty' if not (raw_content or '').strip() else 'no nodes/edges'}, "
|
|
368
|
+
f"output_tokens={result['output_tokens']}); "
|
|
369
|
+
"treating as truncation so adaptive retry can bisect the chunk.",
|
|
370
|
+
file=sys.stderr,
|
|
371
|
+
)
|
|
372
|
+
result["finish_reason"] = "length"
|
|
373
|
+
output_tokens = result["output_tokens"]
|
|
374
|
+
if output_tokens < 50 and backend == "ollama":
|
|
375
|
+
print(
|
|
376
|
+
"[graphify] warning: ollama returned very few tokens — likely causes: "
|
|
377
|
+
"(1) VRAM pressure: check `nvidia-smi` and reduce chunk size with "
|
|
378
|
+
"--token-budget (e.g. --token-budget 4096) or set "
|
|
379
|
+
"GRAPHIFY_OLLAMA_NUM_CTX to a smaller value; "
|
|
380
|
+
"(2) model too small for JSON instruction following — "
|
|
381
|
+
"try a larger model with --model (e.g. --model qwen2.5-coder:14b).",
|
|
382
|
+
file=sys.stderr,
|
|
383
|
+
)
|
|
384
|
+
return result
|
|
385
|
+
|
|
386
|
+
|
|
387
|
+
def _call_claude(api_key: str, model: str, user_message: str, max_tokens: int = 8192) -> dict:
|
|
388
|
+
"""Call Anthropic Claude directly (not via OpenAI compat layer)."""
|
|
389
|
+
try:
|
|
390
|
+
import anthropic
|
|
391
|
+
except ImportError as exc:
|
|
392
|
+
raise ImportError(
|
|
393
|
+
"Claude direct extraction requires the anthropic package. "
|
|
394
|
+
"Run: pip install anthropic"
|
|
395
|
+
) from exc
|
|
396
|
+
|
|
397
|
+
client = anthropic.Anthropic(api_key=api_key)
|
|
398
|
+
resp = client.messages.create(
|
|
399
|
+
model=model,
|
|
400
|
+
max_tokens=max_tokens,
|
|
401
|
+
system=_EXTRACTION_SYSTEM,
|
|
402
|
+
messages=[{"role": "user", "content": user_message}],
|
|
403
|
+
)
|
|
404
|
+
raw_content = resp.content[0].text if resp.content else None
|
|
405
|
+
result = _parse_llm_json(raw_content or "{}")
|
|
406
|
+
result["input_tokens"] = resp.usage.input_tokens if resp.usage else 0
|
|
407
|
+
result["output_tokens"] = resp.usage.output_tokens if resp.usage else 0
|
|
408
|
+
result["model"] = model
|
|
409
|
+
# Normalise Anthropic's `stop_reason` to the OpenAI-compat `finish_reason`
|
|
410
|
+
# vocabulary so the adaptive-retry layer doesn't have to know which
|
|
411
|
+
# backend produced the result.
|
|
412
|
+
result["finish_reason"] = "length" if resp.stop_reason == "max_tokens" else "stop"
|
|
413
|
+
if _response_is_hollow(raw_content, result) and result["finish_reason"] != "length":
|
|
414
|
+
print(
|
|
415
|
+
"[graphify] claude returned a hollow response; treating as "
|
|
416
|
+
"truncation so adaptive retry can bisect the chunk.",
|
|
417
|
+
file=sys.stderr,
|
|
418
|
+
)
|
|
419
|
+
result["finish_reason"] = "length"
|
|
420
|
+
return result
|
|
421
|
+
|
|
422
|
+
|
|
423
|
+
def _call_claude_cli(user_message: str, max_tokens: int = 8192) -> dict:
|
|
424
|
+
"""Call Claude via the locally-installed Claude Code CLI (`claude -p`).
|
|
425
|
+
|
|
426
|
+
Routes through the user's Claude Code subscription auth instead of a separate
|
|
427
|
+
ANTHROPIC_API_KEY. Useful for Pro/Max subscribers who don't want to provision
|
|
428
|
+
a pay-as-you-go API key just to run graphify's semantic pass.
|
|
429
|
+
"""
|
|
430
|
+
import shutil
|
|
431
|
+
import subprocess
|
|
432
|
+
|
|
433
|
+
if shutil.which("claude") is None:
|
|
434
|
+
raise RuntimeError(
|
|
435
|
+
"Claude Code CLI not found on $PATH. Install from "
|
|
436
|
+
"https://claude.ai/code and run `claude` once to authenticate."
|
|
437
|
+
)
|
|
438
|
+
|
|
439
|
+
proc = subprocess.run(
|
|
440
|
+
[
|
|
441
|
+
"claude", "-p",
|
|
442
|
+
"--output-format", "json",
|
|
443
|
+
"--no-session-persistence",
|
|
444
|
+
"--append-system-prompt", _EXTRACTION_SYSTEM,
|
|
445
|
+
],
|
|
446
|
+
input=user_message,
|
|
447
|
+
capture_output=True,
|
|
448
|
+
text=True,
|
|
449
|
+
encoding="utf-8", # Force UTF-8 — prevents UnicodeEncodeError on Windows cp1252
|
|
450
|
+
timeout=600,
|
|
451
|
+
check=False,
|
|
452
|
+
)
|
|
453
|
+
if proc.returncode != 0:
|
|
454
|
+
raise RuntimeError(
|
|
455
|
+
f"claude -p exited {proc.returncode}: {proc.stderr.strip()[:500]}"
|
|
456
|
+
)
|
|
457
|
+
|
|
458
|
+
try:
|
|
459
|
+
envelope = json.loads(proc.stdout)
|
|
460
|
+
except json.JSONDecodeError as exc:
|
|
461
|
+
raise RuntimeError(
|
|
462
|
+
f"claude -p produced unparseable JSON envelope: {exc}; "
|
|
463
|
+
f"first 500 chars of stdout: {proc.stdout[:500]!r}"
|
|
464
|
+
) from exc
|
|
465
|
+
|
|
466
|
+
raw_content = envelope.get("result", "")
|
|
467
|
+
result = _parse_llm_json(raw_content or "{}")
|
|
468
|
+
usage = envelope.get("usage") or {}
|
|
469
|
+
result["input_tokens"] = (
|
|
470
|
+
int(usage.get("input_tokens", 0) or 0)
|
|
471
|
+
+ int(usage.get("cache_read_input_tokens", 0) or 0)
|
|
472
|
+
+ int(usage.get("cache_creation_input_tokens", 0) or 0)
|
|
473
|
+
)
|
|
474
|
+
result["output_tokens"] = int(usage.get("output_tokens", 0) or 0)
|
|
475
|
+
model_usage = envelope.get("modelUsage") or {}
|
|
476
|
+
result["model"] = next(iter(model_usage), "claude-code-plan")
|
|
477
|
+
stop_reason = envelope.get("stop_reason", "")
|
|
478
|
+
result["finish_reason"] = "length" if stop_reason == "max_tokens" else "stop"
|
|
479
|
+
if _response_is_hollow(raw_content, result) and result["finish_reason"] != "length":
|
|
480
|
+
print(
|
|
481
|
+
"[graphify] claude-cli returned a hollow response; treating as "
|
|
482
|
+
"truncation so adaptive retry can bisect the chunk.",
|
|
483
|
+
file=sys.stderr,
|
|
484
|
+
)
|
|
485
|
+
result["finish_reason"] = "length"
|
|
486
|
+
return result
|
|
487
|
+
|
|
488
|
+
|
|
489
|
+
def _call_bedrock(model: str, user_message: str, max_tokens: int = 8192) -> dict:
|
|
490
|
+
"""Call AWS Bedrock via boto3 Converse API using the standard AWS credential chain."""
|
|
491
|
+
try:
|
|
492
|
+
import boto3
|
|
493
|
+
import botocore.exceptions
|
|
494
|
+
except ImportError as exc:
|
|
495
|
+
raise ImportError(
|
|
496
|
+
"AWS Bedrock extraction requires boto3. Run: pip install graphifyy[bedrock]"
|
|
497
|
+
) from exc
|
|
498
|
+
|
|
499
|
+
region = os.environ.get("AWS_REGION") or os.environ.get("AWS_DEFAULT_REGION") or "us-east-1"
|
|
500
|
+
profile = os.environ.get("AWS_PROFILE")
|
|
501
|
+
session = boto3.Session(profile_name=profile, region_name=region)
|
|
502
|
+
client = session.client("bedrock-runtime")
|
|
503
|
+
|
|
504
|
+
try:
|
|
505
|
+
resp = client.converse(
|
|
506
|
+
modelId=model,
|
|
507
|
+
system=[{"text": _EXTRACTION_SYSTEM}],
|
|
508
|
+
messages=[{"role": "user", "content": [{"text": user_message}]}],
|
|
509
|
+
inferenceConfig={"maxTokens": max_tokens, "temperature": 0},
|
|
510
|
+
)
|
|
511
|
+
except botocore.exceptions.ClientError as exc:
|
|
512
|
+
code = exc.response["Error"]["Code"]
|
|
513
|
+
msg = exc.response["Error"]["Message"]
|
|
514
|
+
raise RuntimeError(f"Bedrock API error ({code}): {msg}") from exc
|
|
515
|
+
|
|
516
|
+
text = resp.get("output", {}).get("message", {}).get("content", [{}])[0].get("text", "{}")
|
|
517
|
+
result = _parse_llm_json(text)
|
|
518
|
+
usage = resp.get("usage", {})
|
|
519
|
+
result["input_tokens"] = usage.get("inputTokens", 0)
|
|
520
|
+
result["output_tokens"] = usage.get("outputTokens", 0)
|
|
521
|
+
result["model"] = model
|
|
522
|
+
result["finish_reason"] = "length" if resp.get("stopReason") == "max_tokens" else "stop"
|
|
523
|
+
if _response_is_hollow(text, result) and result["finish_reason"] != "length":
|
|
524
|
+
print(
|
|
525
|
+
"[graphify] bedrock returned a hollow response; treating as "
|
|
526
|
+
"truncation so adaptive retry can bisect the chunk.",
|
|
527
|
+
file=sys.stderr,
|
|
528
|
+
)
|
|
529
|
+
result["finish_reason"] = "length"
|
|
530
|
+
return result
|
|
531
|
+
|
|
532
|
+
|
|
533
|
+
def extract_files_direct(
|
|
534
|
+
files: list[Path],
|
|
535
|
+
backend: str = "kimi",
|
|
536
|
+
api_key: str | None = None,
|
|
537
|
+
model: str | None = None,
|
|
538
|
+
root: Path = Path("."),
|
|
539
|
+
) -> dict:
|
|
540
|
+
"""Extract semantic nodes/edges from a list of files using the given backend.
|
|
541
|
+
|
|
542
|
+
Returns dict with nodes, edges, hyperedges, input_tokens, output_tokens.
|
|
543
|
+
Raises ValueError for unknown backends. Raises ImportError if SDK missing.
|
|
544
|
+
"""
|
|
545
|
+
if backend not in BACKENDS:
|
|
546
|
+
raise ValueError(f"Unknown backend {backend!r}. Available: {sorted(BACKENDS)}")
|
|
547
|
+
|
|
548
|
+
cfg = BACKENDS[backend]
|
|
549
|
+
key = api_key or _get_backend_api_key(backend)
|
|
550
|
+
if not key and backend == "ollama":
|
|
551
|
+
# Ollama ignores auth but the OpenAI client library requires a non-empty
|
|
552
|
+
# string. Use a placeholder and surface a visible warning so this never
|
|
553
|
+
# silently routes traffic without the user realising — see F-029.
|
|
554
|
+
ollama_url = os.environ.get("OLLAMA_BASE_URL", cfg.get("base_url", ""))
|
|
555
|
+
_validate_ollama_base_url(ollama_url)
|
|
556
|
+
print(
|
|
557
|
+
"[graphify] WARNING: ollama backend selected with no OLLAMA_API_KEY set; "
|
|
558
|
+
f"sending corpus to {ollama_url}. Set OLLAMA_API_KEY (any non-empty value) "
|
|
559
|
+
"to suppress this warning.",
|
|
560
|
+
file=sys.stderr,
|
|
561
|
+
)
|
|
562
|
+
key = "ollama"
|
|
563
|
+
if not key and backend not in ("bedrock", "claude-cli"):
|
|
564
|
+
raise ValueError(
|
|
565
|
+
f"No API key for backend '{backend}'. "
|
|
566
|
+
f"Set {_format_backend_env_keys(backend)} or pass api_key=."
|
|
567
|
+
)
|
|
568
|
+
mdl = model or _default_model_for_backend(backend)
|
|
569
|
+
user_msg = _read_files(files, root)
|
|
570
|
+
max_out = _resolve_max_tokens(cfg.get("max_tokens", 8192))
|
|
571
|
+
|
|
572
|
+
if backend == "claude":
|
|
573
|
+
return _call_claude(key, mdl, user_msg, max_tokens=max_out)
|
|
574
|
+
if backend == "claude-cli":
|
|
575
|
+
return _call_claude_cli(user_msg, max_tokens=max_out)
|
|
576
|
+
if backend == "bedrock":
|
|
577
|
+
return _call_bedrock(mdl, user_msg, max_tokens=max_out)
|
|
578
|
+
return _call_openai_compat(
|
|
579
|
+
cfg["base_url"],
|
|
580
|
+
key,
|
|
581
|
+
mdl,
|
|
582
|
+
user_msg,
|
|
583
|
+
temperature=cfg.get("temperature", 0),
|
|
584
|
+
reasoning_effort=cfg.get("reasoning_effort"),
|
|
585
|
+
max_completion_tokens=cfg.get("max_completion_tokens", max_out),
|
|
586
|
+
backend=backend,
|
|
587
|
+
)
|
|
588
|
+
|
|
589
|
+
|
|
590
|
+
def _estimate_file_tokens(path: Path) -> int:
|
|
591
|
+
"""Estimate the prompt-token cost of a single file under `_read_files` rules.
|
|
592
|
+
|
|
593
|
+
Uses tiktoken (`cl100k_base`) when available for accurate counts. Falls back
|
|
594
|
+
to the chars/4 heuristic if tiktoken is not installed. Both paths cap at
|
|
595
|
+
`_FILE_CHAR_CAP` to match `_read_files`'s truncation, plus a constant for
|
|
596
|
+
the `=== rel ===` separator. Returns 0 for unreadable paths so they don't
|
|
597
|
+
blow up packing.
|
|
598
|
+
"""
|
|
599
|
+
if _TOKENIZER is None:
|
|
600
|
+
try:
|
|
601
|
+
size = path.stat().st_size
|
|
602
|
+
except OSError:
|
|
603
|
+
return 0
|
|
604
|
+
chars = min(size, _FILE_CHAR_CAP) + _PER_FILE_OVERHEAD_CHARS
|
|
605
|
+
return chars // _CHARS_PER_TOKEN
|
|
606
|
+
|
|
607
|
+
try:
|
|
608
|
+
content = path.read_text(encoding="utf-8", errors="replace")[:_FILE_CHAR_CAP]
|
|
609
|
+
except OSError:
|
|
610
|
+
return 0
|
|
611
|
+
return len(_TOKENIZER.encode(content)) + (_PER_FILE_OVERHEAD_CHARS // _CHARS_PER_TOKEN)
|
|
612
|
+
|
|
613
|
+
|
|
614
|
+
def _pack_chunks_by_tokens(
|
|
615
|
+
files: list[Path],
|
|
616
|
+
token_budget: int,
|
|
617
|
+
) -> list[list[Path]]:
|
|
618
|
+
"""Greedily pack files into chunks that fit a token budget.
|
|
619
|
+
|
|
620
|
+
Files are first grouped by parent directory so related artifacts share a
|
|
621
|
+
chunk (cross-file edges are more likely to be extracted within a chunk
|
|
622
|
+
than across chunks). Within each directory, files are added one at a
|
|
623
|
+
time; a chunk is closed when adding the next file would exceed the
|
|
624
|
+
budget. A single file larger than the budget gets its own chunk and the
|
|
625
|
+
caller is expected to handle the API error if it actually overflows the
|
|
626
|
+
model's context window — packing can't shrink one big file.
|
|
627
|
+
"""
|
|
628
|
+
if token_budget <= 0:
|
|
629
|
+
raise ValueError(f"token_budget must be positive, got {token_budget}")
|
|
630
|
+
|
|
631
|
+
by_dir: dict[Path, list[Path]] = {}
|
|
632
|
+
for f in files:
|
|
633
|
+
by_dir.setdefault(f.parent, []).append(f)
|
|
634
|
+
|
|
635
|
+
chunks: list[list[Path]] = []
|
|
636
|
+
current: list[Path] = []
|
|
637
|
+
current_tokens = 0
|
|
638
|
+
|
|
639
|
+
for directory in sorted(by_dir):
|
|
640
|
+
for path in by_dir[directory]:
|
|
641
|
+
cost = _estimate_file_tokens(path)
|
|
642
|
+
if current and current_tokens + cost > token_budget:
|
|
643
|
+
chunks.append(current)
|
|
644
|
+
current = []
|
|
645
|
+
current_tokens = 0
|
|
646
|
+
current.append(path)
|
|
647
|
+
current_tokens += cost
|
|
648
|
+
|
|
649
|
+
if current:
|
|
650
|
+
chunks.append(current)
|
|
651
|
+
return chunks
|
|
652
|
+
|
|
653
|
+
|
|
654
|
+
_CONTEXT_EXCEEDED_MARKERS = (
|
|
655
|
+
"context size",
|
|
656
|
+
"context length",
|
|
657
|
+
"context_length",
|
|
658
|
+
"context window",
|
|
659
|
+
"n_keep",
|
|
660
|
+
"exceeds the available",
|
|
661
|
+
"n_ctx",
|
|
662
|
+
"maximum context",
|
|
663
|
+
"too many tokens",
|
|
664
|
+
"prompt is too long",
|
|
665
|
+
"context_length_exceeded",
|
|
666
|
+
)
|
|
667
|
+
|
|
668
|
+
|
|
669
|
+
def _looks_like_context_exceeded(exc: BaseException) -> bool:
|
|
670
|
+
"""Heuristically classify an exception as a context-window overflow.
|
|
671
|
+
|
|
672
|
+
Different backends raise different exception types and messages for the
|
|
673
|
+
same underlying problem ("the prompt + max_completion_tokens did not fit
|
|
674
|
+
in the model's context window"). We match on substrings of the stringified
|
|
675
|
+
exception so the retry layer can recover without depending on a specific
|
|
676
|
+
SDK class. False positives are cheap (we'll re-extract on halves and
|
|
677
|
+
likely recover); false negatives are expensive (chunk fails entirely).
|
|
678
|
+
"""
|
|
679
|
+
msg = str(exc).lower()
|
|
680
|
+
return any(marker in msg for marker in _CONTEXT_EXCEEDED_MARKERS)
|
|
681
|
+
|
|
682
|
+
|
|
683
|
+
def _extract_with_adaptive_retry(
|
|
684
|
+
chunk: list[Path],
|
|
685
|
+
backend: str,
|
|
686
|
+
api_key: str | None,
|
|
687
|
+
model: str | None,
|
|
688
|
+
root: Path,
|
|
689
|
+
max_depth: int,
|
|
690
|
+
_depth: int = 0,
|
|
691
|
+
) -> dict:
|
|
692
|
+
"""Extract a chunk; if the response is truncated (`finish_reason="length"`)
|
|
693
|
+
or the API rejects the prompt as too large for the model's context window,
|
|
694
|
+
split the chunk in half and recurse.
|
|
695
|
+
|
|
696
|
+
Three signals drive the retry, all funnelled through the same code:
|
|
697
|
+
|
|
698
|
+
- `finish_reason == "length"` — the model accepted the input but ran out of
|
|
699
|
+
`max_completion_tokens` mid-output. The truncated JSON is unparseable, so
|
|
700
|
+
we discard it and re-extract on smaller inputs that produce shorter
|
|
701
|
+
outputs.
|
|
702
|
+
|
|
703
|
+
- context-window-exceeded API errors — the model rejected the input
|
|
704
|
+
outright (HTTP 400 from LM Studio, llama.cpp, vLLM, OpenAI, etc.).
|
|
705
|
+
Without a retry the whole chunk would fail with no output. Splitting in
|
|
706
|
+
half is the same recovery as for the `length` case and works for the
|
|
707
|
+
same reason.
|
|
708
|
+
|
|
709
|
+
- hollow successful responses — the model returned HTTP 200 with empty,
|
|
710
|
+
null, or unparseable content (typical of a local Ollama under load).
|
|
711
|
+
`_call_openai_compat` re-labels these as `finish_reason="length"` so they
|
|
712
|
+
take the same recovery path; without that the chunk would be silently
|
|
713
|
+
dropped from the corpus.
|
|
714
|
+
|
|
715
|
+
Recursion is capped at `max_depth` to bound worst-case cost. A chunk of N
|
|
716
|
+
files can split into up to 2**max_depth pieces — at depth=3 that's 8x. If
|
|
717
|
+
still failing at the cap, we surface the (likely empty) result with a
|
|
718
|
+
warning rather than infinite-loop.
|
|
719
|
+
|
|
720
|
+
A single-file chunk that overflows is unrecoverable here — we can't make
|
|
721
|
+
one file smaller than itself, so we return what we got and warn.
|
|
722
|
+
"""
|
|
723
|
+
try:
|
|
724
|
+
result = extract_files_direct(
|
|
725
|
+
chunk, backend=backend, api_key=api_key, model=model, root=root
|
|
726
|
+
)
|
|
727
|
+
except Exception as exc: # noqa: BLE001 — re-raise unless it's a known context overflow
|
|
728
|
+
if not _looks_like_context_exceeded(exc):
|
|
729
|
+
raise
|
|
730
|
+
if len(chunk) <= 1:
|
|
731
|
+
print(
|
|
732
|
+
f"[graphify] single-file chunk {chunk[0]} exceeds model context "
|
|
733
|
+
f"and cannot be split further: {exc}",
|
|
734
|
+
file=sys.stderr,
|
|
735
|
+
)
|
|
736
|
+
return {"nodes": [], "edges": [], "hyperedges": [], "input_tokens": 0, "output_tokens": 0, "model": model, "finish_reason": "stop"}
|
|
737
|
+
if _depth >= max_depth:
|
|
738
|
+
print(
|
|
739
|
+
f"[graphify] chunk of {len(chunk)} still overflows context at "
|
|
740
|
+
f"recursion depth {_depth} (max {max_depth}) — dropping",
|
|
741
|
+
file=sys.stderr,
|
|
742
|
+
)
|
|
743
|
+
return {"nodes": [], "edges": [], "hyperedges": [], "input_tokens": 0, "output_tokens": 0, "model": model, "finish_reason": "stop"}
|
|
744
|
+
print(
|
|
745
|
+
f"[graphify] chunk of {len(chunk)} exceeded context at depth "
|
|
746
|
+
f"{_depth} ({type(exc).__name__}); splitting in half and retrying",
|
|
747
|
+
file=sys.stderr,
|
|
748
|
+
)
|
|
749
|
+
mid = len(chunk) // 2
|
|
750
|
+
left = _extract_with_adaptive_retry(
|
|
751
|
+
chunk[:mid], backend, api_key, model, root, max_depth, _depth + 1
|
|
752
|
+
)
|
|
753
|
+
right = _extract_with_adaptive_retry(
|
|
754
|
+
chunk[mid:], backend, api_key, model, root, max_depth, _depth + 1
|
|
755
|
+
)
|
|
756
|
+
return {
|
|
757
|
+
"nodes": left.get("nodes", []) + right.get("nodes", []),
|
|
758
|
+
"edges": left.get("edges", []) + right.get("edges", []),
|
|
759
|
+
"hyperedges": left.get("hyperedges", []) + right.get("hyperedges", []),
|
|
760
|
+
"input_tokens": left.get("input_tokens", 0) + right.get("input_tokens", 0),
|
|
761
|
+
"output_tokens": left.get("output_tokens", 0) + right.get("output_tokens", 0),
|
|
762
|
+
"model": model,
|
|
763
|
+
"finish_reason": "stop",
|
|
764
|
+
}
|
|
765
|
+
|
|
766
|
+
if result.get("finish_reason") != "length":
|
|
767
|
+
return result
|
|
768
|
+
|
|
769
|
+
if len(chunk) <= 1:
|
|
770
|
+
print(
|
|
771
|
+
f"[graphify] single-file chunk {chunk[0]} truncated at "
|
|
772
|
+
f"max_completion_tokens — partial result kept",
|
|
773
|
+
file=sys.stderr,
|
|
774
|
+
)
|
|
775
|
+
return result
|
|
776
|
+
|
|
777
|
+
if _depth >= max_depth:
|
|
778
|
+
print(
|
|
779
|
+
f"[graphify] chunk of {len(chunk)} still truncated at recursion "
|
|
780
|
+
f"depth {_depth} (max {max_depth}) — partial result kept",
|
|
781
|
+
file=sys.stderr,
|
|
782
|
+
)
|
|
783
|
+
return result
|
|
784
|
+
|
|
785
|
+
print(
|
|
786
|
+
f"[graphify] chunk of {len(chunk)} truncated at depth {_depth}, "
|
|
787
|
+
f"splitting into halves of {len(chunk) // 2} and "
|
|
788
|
+
f"{len(chunk) - len(chunk) // 2}",
|
|
789
|
+
file=sys.stderr,
|
|
790
|
+
)
|
|
791
|
+
mid = len(chunk) // 2
|
|
792
|
+
left = _extract_with_adaptive_retry(
|
|
793
|
+
chunk[:mid], backend, api_key, model, root, max_depth, _depth + 1
|
|
794
|
+
)
|
|
795
|
+
right = _extract_with_adaptive_retry(
|
|
796
|
+
chunk[mid:], backend, api_key, model, root, max_depth, _depth + 1
|
|
797
|
+
)
|
|
798
|
+
|
|
799
|
+
return {
|
|
800
|
+
"nodes": left.get("nodes", []) + right.get("nodes", []),
|
|
801
|
+
"edges": left.get("edges", []) + right.get("edges", []),
|
|
802
|
+
"hyperedges": left.get("hyperedges", []) + right.get("hyperedges", []),
|
|
803
|
+
"input_tokens": left.get("input_tokens", 0) + right.get("input_tokens", 0),
|
|
804
|
+
"output_tokens": left.get("output_tokens", 0) + right.get("output_tokens", 0),
|
|
805
|
+
"model": result.get("model"),
|
|
806
|
+
# Both halves either succeeded or have already surfaced their own
|
|
807
|
+
# truncation warning; the merged result is no longer truncated as a
|
|
808
|
+
# logical unit.
|
|
809
|
+
"finish_reason": "stop",
|
|
810
|
+
}
|
|
811
|
+
|
|
812
|
+
|
|
813
|
+
def extract_corpus_parallel(
|
|
814
|
+
files: list[Path],
|
|
815
|
+
backend: str = "kimi",
|
|
816
|
+
api_key: str | None = None,
|
|
817
|
+
model: str | None = None,
|
|
818
|
+
root: Path = Path("."),
|
|
819
|
+
chunk_size: int = 20,
|
|
820
|
+
on_chunk_done: Callable | None = None,
|
|
821
|
+
token_budget: int | None = 60_000,
|
|
822
|
+
max_concurrency: int = 4,
|
|
823
|
+
max_retry_depth: int = 3,
|
|
824
|
+
) -> dict:
|
|
825
|
+
"""Extract a corpus in chunks, merging results.
|
|
826
|
+
|
|
827
|
+
Chunking strategy:
|
|
828
|
+
- If `token_budget` is set (default 60_000), files are packed to fit
|
|
829
|
+
the budget and grouped by parent directory. This avoids the worst
|
|
830
|
+
case where 20 randomly-grouped files exceed a model's context
|
|
831
|
+
window in a single request.
|
|
832
|
+
- If `token_budget=None`, falls back to the legacy fixed-count
|
|
833
|
+
`chunk_size` packing for backwards compatibility.
|
|
834
|
+
|
|
835
|
+
Concurrency:
|
|
836
|
+
- Chunks run in parallel via a thread pool capped at `max_concurrency`
|
|
837
|
+
(default 4 — conservative to stay under provider rate limits).
|
|
838
|
+
- Set `max_concurrency=1` to force sequential execution.
|
|
839
|
+
|
|
840
|
+
Adaptive retry on truncation:
|
|
841
|
+
- When the LLM returns `finish_reason="length"` (output truncated at
|
|
842
|
+
`max_completion_tokens`), the chunk is split in half and each half
|
|
843
|
+
re-extracted recursively, up to `max_retry_depth` levels deep
|
|
844
|
+
(default 3 → max 8x expansion of one chunk).
|
|
845
|
+
- This is signal-driven: chunks too dense to fit in one response
|
|
846
|
+
self-heal by splitting until they do, while well-sized chunks pay
|
|
847
|
+
no extra cost. Set `max_retry_depth=0` to disable retries.
|
|
848
|
+
|
|
849
|
+
`on_chunk_done(idx, total, chunk_result)` fires once per chunk as it
|
|
850
|
+
completes (in completion order, not submission order). `idx` is the
|
|
851
|
+
chunk's submission index so callers can correlate progress. The
|
|
852
|
+
callback fires once per top-level chunk; recursive splits are merged
|
|
853
|
+
transparently before the callback is invoked.
|
|
854
|
+
|
|
855
|
+
Returns merged dict with nodes, edges, hyperedges, input_tokens,
|
|
856
|
+
output_tokens. Failed chunks are logged to stderr and skipped — one bad
|
|
857
|
+
chunk does not abort the run.
|
|
858
|
+
"""
|
|
859
|
+
if token_budget is not None:
|
|
860
|
+
chunks = _pack_chunks_by_tokens(files, token_budget=token_budget)
|
|
861
|
+
else:
|
|
862
|
+
chunks = [files[i:i + chunk_size] for i in range(0, len(files), chunk_size)]
|
|
863
|
+
|
|
864
|
+
merged: dict = {
|
|
865
|
+
"nodes": [], "edges": [], "hyperedges": [],
|
|
866
|
+
"input_tokens": 0, "output_tokens": 0,
|
|
867
|
+
"failed_chunks": 0, # count of chunks that raised — loud failure on chunk errors
|
|
868
|
+
}
|
|
869
|
+
total = len(chunks)
|
|
870
|
+
|
|
871
|
+
def _run_one(idx: int, chunk: list[Path]) -> tuple[int, dict | None, Exception | None]:
|
|
872
|
+
t0 = time.time()
|
|
873
|
+
try:
|
|
874
|
+
result = _extract_with_adaptive_retry(
|
|
875
|
+
chunk,
|
|
876
|
+
backend=backend,
|
|
877
|
+
api_key=api_key,
|
|
878
|
+
model=model,
|
|
879
|
+
root=root,
|
|
880
|
+
max_depth=max_retry_depth,
|
|
881
|
+
)
|
|
882
|
+
result["elapsed_seconds"] = round(time.time() - t0, 2)
|
|
883
|
+
return idx, result, None
|
|
884
|
+
except Exception as exc: # noqa: BLE001 — caller-facing surface, log + continue
|
|
885
|
+
return idx, None, exc
|
|
886
|
+
|
|
887
|
+
# Ollama serves one request at a time per loaded model on a single GPU.
|
|
888
|
+
# Four concurrent 60k-token requests cause VRAM pressure and hollow
|
|
889
|
+
# responses after 3-4 chunks (#798). Force serial unless the user opts in.
|
|
890
|
+
if backend == "ollama" and os.environ.get("GRAPHIFY_OLLAMA_PARALLEL", "").strip() != "1":
|
|
891
|
+
max_concurrency = 1
|
|
892
|
+
# claude-cli shells out to a Claude Code session; parallel subprocesses conflict
|
|
893
|
+
# over session state. Force serial unless the user explicitly opts in.
|
|
894
|
+
if backend == "claude-cli" and os.environ.get("GRAPHIFY_CLAUDE_CLI_PARALLEL", "").strip() != "1":
|
|
895
|
+
max_concurrency = 1
|
|
896
|
+
workers = max(1, min(max_concurrency, total))
|
|
897
|
+
if workers == 1:
|
|
898
|
+
# Avoid thread pool overhead for single-worker runs (and keep
|
|
899
|
+
# callback ordering identical to the pre-refactor sequential path).
|
|
900
|
+
for idx, chunk in enumerate(chunks):
|
|
901
|
+
_, result, exc = _run_one(idx, chunk)
|
|
902
|
+
if exc is not None:
|
|
903
|
+
print(f"[graphify] chunk {idx + 1}/{total} failed: {exc}", file=sys.stderr)
|
|
904
|
+
merged["failed_chunks"] += 1
|
|
905
|
+
continue
|
|
906
|
+
assert result is not None
|
|
907
|
+
_merge_into(merged, result)
|
|
908
|
+
if callable(on_chunk_done):
|
|
909
|
+
on_chunk_done(idx, total, result)
|
|
910
|
+
else:
|
|
911
|
+
with ThreadPoolExecutor(max_workers=workers) as pool:
|
|
912
|
+
futures = [pool.submit(_run_one, idx, chunk) for idx, chunk in enumerate(chunks)]
|
|
913
|
+
for future in as_completed(futures):
|
|
914
|
+
idx, result, exc = future.result()
|
|
915
|
+
if exc is not None:
|
|
916
|
+
print(
|
|
917
|
+
f"[graphify] chunk {idx + 1}/{total} failed: {exc}",
|
|
918
|
+
file=sys.stderr,
|
|
919
|
+
)
|
|
920
|
+
merged["failed_chunks"] += 1
|
|
921
|
+
continue
|
|
922
|
+
assert result is not None
|
|
923
|
+
_merge_into(merged, result)
|
|
924
|
+
if callable(on_chunk_done):
|
|
925
|
+
on_chunk_done(idx, total, result)
|
|
926
|
+
|
|
927
|
+
# Loud failure summary — surface chunk failures at end so they're never
|
|
928
|
+
# buried mid-log. Exit 0 preserved for caller compatibility; the
|
|
929
|
+
# summary block makes the problem visible.
|
|
930
|
+
if merged["failed_chunks"] > 0:
|
|
931
|
+
print(
|
|
932
|
+
f"[graphify] WARNING: {merged['failed_chunks']}/{total} semantic chunk(s) failed"
|
|
933
|
+
" — see errors above. Partial results returned.",
|
|
934
|
+
file=sys.stderr,
|
|
935
|
+
)
|
|
936
|
+
return merged
|
|
937
|
+
|
|
938
|
+
|
|
939
|
+
def _merge_into(merged: dict, result: dict) -> None:
|
|
940
|
+
"""Append a chunk result into the running merged accumulator."""
|
|
941
|
+
merged["nodes"].extend(result.get("nodes", []))
|
|
942
|
+
merged["edges"].extend(result.get("edges", []))
|
|
943
|
+
merged["hyperedges"].extend(result.get("hyperedges", []))
|
|
944
|
+
merged["input_tokens"] += result.get("input_tokens", 0)
|
|
945
|
+
merged["output_tokens"] += result.get("output_tokens", 0)
|
|
946
|
+
|
|
947
|
+
|
|
948
|
+
def _call_llm(prompt: str, *, backend: str, max_tokens: int = 200) -> str:
|
|
949
|
+
"""Send a plain-text prompt to `backend` and return the model's text reply.
|
|
950
|
+
|
|
951
|
+
Used by lightweight callers (e.g. `graphify.dedup` LLM tiebreaker) that
|
|
952
|
+
don't need the full extraction prompt or JSON-shaped output. Mirrors the
|
|
953
|
+
backend dispatch logic of `extract_files_direct` but skips the
|
|
954
|
+
`_EXTRACTION_SYSTEM` prompt and JSON parsing.
|
|
955
|
+
|
|
956
|
+
Previously `graphify.dedup` imported a `_call_llm` symbol that did not
|
|
957
|
+
exist in this module, so the LLM tiebreaker silently no-op'd on
|
|
958
|
+
`ImportError` (F-038). Adding the function here re-enables it.
|
|
959
|
+
"""
|
|
960
|
+
if backend not in BACKENDS:
|
|
961
|
+
raise ValueError(f"Unknown backend {backend!r}")
|
|
962
|
+
cfg = BACKENDS[backend]
|
|
963
|
+
key = _get_backend_api_key(backend)
|
|
964
|
+
if not key and backend == "ollama":
|
|
965
|
+
ollama_url = os.environ.get("OLLAMA_BASE_URL", cfg.get("base_url", ""))
|
|
966
|
+
_validate_ollama_base_url(ollama_url)
|
|
967
|
+
key = "ollama"
|
|
968
|
+
if not key and backend not in ("bedrock", "claude-cli"):
|
|
969
|
+
raise ValueError(
|
|
970
|
+
f"No API key for backend '{backend}'. Set {_format_backend_env_keys(backend)}."
|
|
971
|
+
)
|
|
972
|
+
mdl = _default_model_for_backend(backend)
|
|
973
|
+
|
|
974
|
+
if backend == "claude":
|
|
975
|
+
try:
|
|
976
|
+
import anthropic
|
|
977
|
+
except ImportError as exc:
|
|
978
|
+
raise ImportError("anthropic package required for claude backend") from exc
|
|
979
|
+
client = anthropic.Anthropic(api_key=key)
|
|
980
|
+
resp = client.messages.create(
|
|
981
|
+
model=mdl,
|
|
982
|
+
max_tokens=max_tokens,
|
|
983
|
+
messages=[{"role": "user", "content": prompt}],
|
|
984
|
+
)
|
|
985
|
+
return resp.content[0].text if resp.content else ""
|
|
986
|
+
|
|
987
|
+
if backend == "claude-cli":
|
|
988
|
+
import shutil, subprocess
|
|
989
|
+
if shutil.which("claude") is None:
|
|
990
|
+
raise RuntimeError("Claude Code CLI not found on $PATH")
|
|
991
|
+
proc = subprocess.run(
|
|
992
|
+
["claude", "-p", "--output-format", "json", "--no-session-persistence"],
|
|
993
|
+
input=prompt,
|
|
994
|
+
capture_output=True,
|
|
995
|
+
text=True,
|
|
996
|
+
encoding="utf-8", # Force UTF-8 — prevents UnicodeEncodeError on Windows cp1252
|
|
997
|
+
timeout=600,
|
|
998
|
+
check=False,
|
|
999
|
+
)
|
|
1000
|
+
if proc.returncode != 0:
|
|
1001
|
+
raise RuntimeError(f"claude -p exited {proc.returncode}: {proc.stderr.strip()[:500]}")
|
|
1002
|
+
try:
|
|
1003
|
+
envelope = json.loads(proc.stdout)
|
|
1004
|
+
except json.JSONDecodeError as exc:
|
|
1005
|
+
raise RuntimeError(f"claude -p produced unparseable JSON envelope: {exc}") from exc
|
|
1006
|
+
return envelope.get("result", "")
|
|
1007
|
+
|
|
1008
|
+
if backend == "bedrock":
|
|
1009
|
+
try:
|
|
1010
|
+
import boto3
|
|
1011
|
+
except ImportError as exc:
|
|
1012
|
+
raise ImportError("boto3 required for bedrock backend") from exc
|
|
1013
|
+
region = os.environ.get("AWS_REGION") or os.environ.get("AWS_DEFAULT_REGION") or "us-east-1"
|
|
1014
|
+
profile = os.environ.get("AWS_PROFILE")
|
|
1015
|
+
session = boto3.Session(profile_name=profile, region_name=region)
|
|
1016
|
+
client = session.client("bedrock-runtime")
|
|
1017
|
+
resp = client.converse(
|
|
1018
|
+
modelId=mdl,
|
|
1019
|
+
messages=[{"role": "user", "content": [{"text": prompt}]}],
|
|
1020
|
+
inferenceConfig={"maxTokens": max_tokens, "temperature": 0},
|
|
1021
|
+
)
|
|
1022
|
+
return resp.get("output", {}).get("message", {}).get("content", [{}])[0].get("text", "")
|
|
1023
|
+
|
|
1024
|
+
# OpenAI-compatible (kimi, openai, gemini, ollama)
|
|
1025
|
+
try:
|
|
1026
|
+
from openai import OpenAI
|
|
1027
|
+
except ImportError as exc:
|
|
1028
|
+
raise ImportError("openai package required for this backend") from exc
|
|
1029
|
+
client = OpenAI(api_key=key, base_url=cfg["base_url"])
|
|
1030
|
+
kwargs: dict = {
|
|
1031
|
+
"model": mdl,
|
|
1032
|
+
"messages": [{"role": "user", "content": prompt}],
|
|
1033
|
+
"max_completion_tokens": max_tokens,
|
|
1034
|
+
}
|
|
1035
|
+
temperature = cfg.get("temperature", 0)
|
|
1036
|
+
if temperature is not None:
|
|
1037
|
+
kwargs["temperature"] = temperature
|
|
1038
|
+
if cfg.get("reasoning_effort"):
|
|
1039
|
+
kwargs["reasoning_effort"] = cfg["reasoning_effort"]
|
|
1040
|
+
if "moonshot" in cfg["base_url"]:
|
|
1041
|
+
kwargs["extra_body"] = {"thinking": {"type": "disabled"}}
|
|
1042
|
+
resp = client.chat.completions.create(**kwargs)
|
|
1043
|
+
if not resp.choices or resp.choices[0].message is None:
|
|
1044
|
+
raise ValueError("LLM returned empty or filtered response")
|
|
1045
|
+
return resp.choices[0].message.content or ""
|
|
1046
|
+
|
|
1047
|
+
|
|
1048
|
+
def estimate_cost(backend: str, input_tokens: int, output_tokens: int) -> float:
|
|
1049
|
+
"""Estimate USD cost for a given token count using published pricing."""
|
|
1050
|
+
if backend not in BACKENDS:
|
|
1051
|
+
return 0.0
|
|
1052
|
+
p = BACKENDS[backend]["pricing"]
|
|
1053
|
+
return (input_tokens * p["input"] + output_tokens * p["output"]) / 1_000_000
|
|
1054
|
+
|
|
1055
|
+
|
|
1056
|
+
def _validate_ollama_base_url(url: str) -> None:
|
|
1057
|
+
"""Warn (do not raise) if OLLAMA_BASE_URL looks unsafe.
|
|
1058
|
+
|
|
1059
|
+
Sending an entire corpus to a non-loopback http:// endpoint silently leaks
|
|
1060
|
+
proprietary code; we surface a visible stderr warning instead of failing
|
|
1061
|
+
closed (some users genuinely run Ollama on a LAN host they trust).
|
|
1062
|
+
"""
|
|
1063
|
+
try:
|
|
1064
|
+
from urllib.parse import urlparse
|
|
1065
|
+
parsed = urlparse(url)
|
|
1066
|
+
except Exception:
|
|
1067
|
+
print(
|
|
1068
|
+
f"[graphify] WARNING: OLLAMA_BASE_URL={url!r} is not a parseable URL.",
|
|
1069
|
+
file=sys.stderr,
|
|
1070
|
+
)
|
|
1071
|
+
return
|
|
1072
|
+
if parsed.scheme not in ("http", "https"):
|
|
1073
|
+
print(
|
|
1074
|
+
f"[graphify] WARNING: OLLAMA_BASE_URL has unexpected scheme {parsed.scheme!r}; "
|
|
1075
|
+
"expected http or https.",
|
|
1076
|
+
file=sys.stderr,
|
|
1077
|
+
)
|
|
1078
|
+
return
|
|
1079
|
+
host = (parsed.hostname or "").lower()
|
|
1080
|
+
is_loopback = host in ("localhost", "127.0.0.1", "::1") or host.startswith("127.")
|
|
1081
|
+
if not is_loopback:
|
|
1082
|
+
scheme_note = " (UNENCRYPTED)" if parsed.scheme == "http" else ""
|
|
1083
|
+
print(
|
|
1084
|
+
f"[graphify] WARNING: OLLAMA_BASE_URL points to non-loopback host {host!r}{scheme_note}. "
|
|
1085
|
+
"Your full corpus will be sent to that endpoint. "
|
|
1086
|
+
"Set OLLAMA_BASE_URL=http://localhost:11434/v1 to keep extraction local.",
|
|
1087
|
+
file=sys.stderr,
|
|
1088
|
+
)
|
|
1089
|
+
|
|
1090
|
+
|
|
1091
|
+
def detect_backend() -> str | None:
|
|
1092
|
+
"""Return the name of whichever backend has an API key set, or None.
|
|
1093
|
+
|
|
1094
|
+
Priority: gemini → kimi → claude → openai → bedrock → ollama (last, opt-in).
|
|
1095
|
+
|
|
1096
|
+
Ollama is intentionally checked LAST so a paid API key (Anthropic/OpenAI/etc.)
|
|
1097
|
+
is never silently shadowed by an incidental OLLAMA_BASE_URL in the environment
|
|
1098
|
+
— see security finding F-002/F-029. Setting OLLAMA_BASE_URL alongside a paid
|
|
1099
|
+
key now keeps you on the paid backend; remove the paid key (or pass
|
|
1100
|
+
--backend ollama explicitly) to route to the local model.
|
|
1101
|
+
"""
|
|
1102
|
+
for backend in ("gemini", "kimi", "claude", "openai", "deepseek"):
|
|
1103
|
+
if _get_backend_api_key(backend):
|
|
1104
|
+
return backend
|
|
1105
|
+
if os.environ.get("AWS_PROFILE") or os.environ.get("AWS_REGION") or os.environ.get("AWS_DEFAULT_REGION"):
|
|
1106
|
+
return "bedrock"
|
|
1107
|
+
ollama_url = os.environ.get("OLLAMA_BASE_URL")
|
|
1108
|
+
if ollama_url:
|
|
1109
|
+
_validate_ollama_base_url(ollama_url)
|
|
1110
|
+
return "ollama"
|
|
1111
|
+
return None
|