compound-engineering-pi 0.2.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +124 -0
- package/bin/compound-engineering-pi +12 -0
- package/bin/compound-plugin +12 -0
- package/compound-engineering-pi +12 -0
- package/compound-plugin +5 -0
- package/docs/pi.md +152 -0
- package/extensions/compound-engineering-compat.ts +452 -0
- package/package.json +84 -0
- package/pi-resources/compound-engineering/mcporter.json +7 -0
- package/plugins/coding-tutor/.claude-plugin/plugin.json +9 -0
- package/plugins/coding-tutor/README.md +37 -0
- package/plugins/coding-tutor/commands/quiz-me.md +1 -0
- package/plugins/coding-tutor/commands/sync-tutorials.md +25 -0
- package/plugins/coding-tutor/commands/teach-me.md +1 -0
- package/plugins/coding-tutor/skills/coding-tutor/SKILL.md +214 -0
- package/plugins/coding-tutor/skills/coding-tutor/scripts/create_tutorial.py +207 -0
- package/plugins/coding-tutor/skills/coding-tutor/scripts/index_tutorials.py +193 -0
- package/plugins/coding-tutor/skills/coding-tutor/scripts/quiz_priority.py +190 -0
- package/plugins/coding-tutor/skills/coding-tutor/scripts/setup_tutorials.py +118 -0
- package/plugins/compound-engineering/.claude-plugin/plugin.json +33 -0
- package/plugins/compound-engineering/CHANGELOG.md +457 -0
- package/plugins/compound-engineering/CLAUDE.md +89 -0
- package/plugins/compound-engineering/LICENSE +21 -0
- package/plugins/compound-engineering/README.md +232 -0
- package/plugins/compound-engineering/agents/design/design-implementation-reviewer.md +109 -0
- package/plugins/compound-engineering/agents/design/design-iterator.md +224 -0
- package/plugins/compound-engineering/agents/design/figma-design-sync.md +190 -0
- package/plugins/compound-engineering/agents/docs/ankane-readme-writer.md +65 -0
- package/plugins/compound-engineering/agents/research/best-practices-researcher.md +126 -0
- package/plugins/compound-engineering/agents/research/framework-docs-researcher.md +106 -0
- package/plugins/compound-engineering/agents/research/git-history-analyzer.md +59 -0
- package/plugins/compound-engineering/agents/research/learnings-researcher.md +264 -0
- package/plugins/compound-engineering/agents/research/repo-research-analyst.md +135 -0
- package/plugins/compound-engineering/agents/review/agent-native-reviewer.md +261 -0
- package/plugins/compound-engineering/agents/review/architecture-strategist.md +67 -0
- package/plugins/compound-engineering/agents/review/code-simplicity-reviewer.md +101 -0
- package/plugins/compound-engineering/agents/review/data-integrity-guardian.md +85 -0
- package/plugins/compound-engineering/agents/review/data-migration-expert.md +112 -0
- package/plugins/compound-engineering/agents/review/deployment-verification-agent.md +174 -0
- package/plugins/compound-engineering/agents/review/dhh-rails-reviewer.md +66 -0
- package/plugins/compound-engineering/agents/review/julik-frontend-races-reviewer.md +221 -0
- package/plugins/compound-engineering/agents/review/kieran-python-reviewer.md +133 -0
- package/plugins/compound-engineering/agents/review/kieran-rails-reviewer.md +115 -0
- package/plugins/compound-engineering/agents/review/kieran-typescript-reviewer.md +124 -0
- package/plugins/compound-engineering/agents/review/pattern-recognition-specialist.md +72 -0
- package/plugins/compound-engineering/agents/review/performance-oracle.md +137 -0
- package/plugins/compound-engineering/agents/review/schema-drift-detector.md +154 -0
- package/plugins/compound-engineering/agents/review/security-sentinel.md +114 -0
- package/plugins/compound-engineering/agents/workflow/bug-reproduction-validator.md +82 -0
- package/plugins/compound-engineering/agents/workflow/every-style-editor.md +64 -0
- package/plugins/compound-engineering/agents/workflow/lint.md +16 -0
- package/plugins/compound-engineering/agents/workflow/pr-comment-resolver.md +84 -0
- package/plugins/compound-engineering/agents/workflow/spec-flow-analyzer.md +134 -0
- package/plugins/compound-engineering/commands/agent-native-audit.md +278 -0
- package/plugins/compound-engineering/commands/changelog.md +138 -0
- package/plugins/compound-engineering/commands/create-agent-skill.md +9 -0
- package/plugins/compound-engineering/commands/deepen-plan.md +546 -0
- package/plugins/compound-engineering/commands/deploy-docs.md +113 -0
- package/plugins/compound-engineering/commands/feature-video.md +342 -0
- package/plugins/compound-engineering/commands/generate_command.md +163 -0
- package/plugins/compound-engineering/commands/heal-skill.md +143 -0
- package/plugins/compound-engineering/commands/lfg.md +20 -0
- package/plugins/compound-engineering/commands/release-docs.md +212 -0
- package/plugins/compound-engineering/commands/report-bug.md +151 -0
- package/plugins/compound-engineering/commands/reproduce-bug.md +100 -0
- package/plugins/compound-engineering/commands/resolve_parallel.md +35 -0
- package/plugins/compound-engineering/commands/resolve_todo_parallel.md +37 -0
- package/plugins/compound-engineering/commands/slfg.md +32 -0
- package/plugins/compound-engineering/commands/technical_review.md +8 -0
- package/plugins/compound-engineering/commands/test-browser.md +339 -0
- package/plugins/compound-engineering/commands/test-xcode.md +332 -0
- package/plugins/compound-engineering/commands/triage.md +311 -0
- package/plugins/compound-engineering/commands/workflows/brainstorm.md +124 -0
- package/plugins/compound-engineering/commands/workflows/compound.md +239 -0
- package/plugins/compound-engineering/commands/workflows/plan.md +551 -0
- package/plugins/compound-engineering/commands/workflows/review.md +526 -0
- package/plugins/compound-engineering/commands/workflows/work.md +433 -0
- package/plugins/compound-engineering/skills/agent-browser/SKILL.md +223 -0
- package/plugins/compound-engineering/skills/agent-native-architecture/SKILL.md +435 -0
- package/plugins/compound-engineering/skills/agent-native-architecture/references/action-parity-discipline.md +409 -0
- package/plugins/compound-engineering/skills/agent-native-architecture/references/agent-execution-patterns.md +467 -0
- package/plugins/compound-engineering/skills/agent-native-architecture/references/agent-native-testing.md +582 -0
- package/plugins/compound-engineering/skills/agent-native-architecture/references/architecture-patterns.md +478 -0
- package/plugins/compound-engineering/skills/agent-native-architecture/references/dynamic-context-injection.md +338 -0
- package/plugins/compound-engineering/skills/agent-native-architecture/references/files-universal-interface.md +301 -0
- package/plugins/compound-engineering/skills/agent-native-architecture/references/from-primitives-to-domain-tools.md +359 -0
- package/plugins/compound-engineering/skills/agent-native-architecture/references/mcp-tool-design.md +506 -0
- package/plugins/compound-engineering/skills/agent-native-architecture/references/mobile-patterns.md +871 -0
- package/plugins/compound-engineering/skills/agent-native-architecture/references/product-implications.md +443 -0
- package/plugins/compound-engineering/skills/agent-native-architecture/references/refactoring-to-prompt-native.md +317 -0
- package/plugins/compound-engineering/skills/agent-native-architecture/references/self-modification.md +269 -0
- package/plugins/compound-engineering/skills/agent-native-architecture/references/shared-workspace-architecture.md +680 -0
- package/plugins/compound-engineering/skills/agent-native-architecture/references/system-prompt-design.md +250 -0
- package/plugins/compound-engineering/skills/andrew-kane-gem-writer/SKILL.md +184 -0
- package/plugins/compound-engineering/skills/andrew-kane-gem-writer/references/database-adapters.md +231 -0
- package/plugins/compound-engineering/skills/andrew-kane-gem-writer/references/module-organization.md +121 -0
- package/plugins/compound-engineering/skills/andrew-kane-gem-writer/references/rails-integration.md +183 -0
- package/plugins/compound-engineering/skills/andrew-kane-gem-writer/references/resources.md +119 -0
- package/plugins/compound-engineering/skills/andrew-kane-gem-writer/references/testing-patterns.md +261 -0
- package/plugins/compound-engineering/skills/brainstorming/SKILL.md +190 -0
- package/plugins/compound-engineering/skills/compound-docs/SKILL.md +511 -0
- package/plugins/compound-engineering/skills/compound-docs/assets/critical-pattern-template.md +34 -0
- package/plugins/compound-engineering/skills/compound-docs/assets/resolution-template.md +93 -0
- package/plugins/compound-engineering/skills/compound-docs/references/yaml-schema.md +65 -0
- package/plugins/compound-engineering/skills/compound-docs/schema.yaml +176 -0
- package/plugins/compound-engineering/skills/create-agent-skills/SKILL.md +275 -0
- package/plugins/compound-engineering/skills/create-agent-skills/references/api-security.md +226 -0
- package/plugins/compound-engineering/skills/create-agent-skills/references/be-clear-and-direct.md +531 -0
- package/plugins/compound-engineering/skills/create-agent-skills/references/best-practices.md +404 -0
- package/plugins/compound-engineering/skills/create-agent-skills/references/common-patterns.md +595 -0
- package/plugins/compound-engineering/skills/create-agent-skills/references/core-principles.md +437 -0
- package/plugins/compound-engineering/skills/create-agent-skills/references/executable-code.md +175 -0
- package/plugins/compound-engineering/skills/create-agent-skills/references/iteration-and-testing.md +474 -0
- package/plugins/compound-engineering/skills/create-agent-skills/references/official-spec.md +134 -0
- package/plugins/compound-engineering/skills/create-agent-skills/references/recommended-structure.md +168 -0
- package/plugins/compound-engineering/skills/create-agent-skills/references/skill-structure.md +152 -0
- package/plugins/compound-engineering/skills/create-agent-skills/references/using-scripts.md +113 -0
- package/plugins/compound-engineering/skills/create-agent-skills/references/using-templates.md +112 -0
- package/plugins/compound-engineering/skills/create-agent-skills/references/workflows-and-validation.md +510 -0
- package/plugins/compound-engineering/skills/create-agent-skills/templates/router-skill.md +73 -0
- package/plugins/compound-engineering/skills/create-agent-skills/templates/simple-skill.md +33 -0
- package/plugins/compound-engineering/skills/create-agent-skills/workflows/add-reference.md +96 -0
- package/plugins/compound-engineering/skills/create-agent-skills/workflows/add-script.md +93 -0
- package/plugins/compound-engineering/skills/create-agent-skills/workflows/add-template.md +74 -0
- package/plugins/compound-engineering/skills/create-agent-skills/workflows/add-workflow.md +120 -0
- package/plugins/compound-engineering/skills/create-agent-skills/workflows/audit-skill.md +138 -0
- package/plugins/compound-engineering/skills/create-agent-skills/workflows/create-domain-expertise-skill.md +605 -0
- package/plugins/compound-engineering/skills/create-agent-skills/workflows/create-new-skill.md +191 -0
- package/plugins/compound-engineering/skills/create-agent-skills/workflows/get-guidance.md +121 -0
- package/plugins/compound-engineering/skills/create-agent-skills/workflows/upgrade-to-router.md +161 -0
- package/plugins/compound-engineering/skills/create-agent-skills/workflows/verify-skill.md +204 -0
- package/plugins/compound-engineering/skills/dhh-rails-style/SKILL.md +185 -0
- package/plugins/compound-engineering/skills/dhh-rails-style/references/architecture.md +653 -0
- package/plugins/compound-engineering/skills/dhh-rails-style/references/controllers.md +303 -0
- package/plugins/compound-engineering/skills/dhh-rails-style/references/frontend.md +510 -0
- package/plugins/compound-engineering/skills/dhh-rails-style/references/gems.md +266 -0
- package/plugins/compound-engineering/skills/dhh-rails-style/references/models.md +359 -0
- package/plugins/compound-engineering/skills/dhh-rails-style/references/testing.md +338 -0
- package/plugins/compound-engineering/skills/document-review/SKILL.md +87 -0
- package/plugins/compound-engineering/skills/dspy-ruby/SKILL.md +737 -0
- package/plugins/compound-engineering/skills/dspy-ruby/assets/config-template.rb +187 -0
- package/plugins/compound-engineering/skills/dspy-ruby/assets/module-template.rb +300 -0
- package/plugins/compound-engineering/skills/dspy-ruby/assets/signature-template.rb +221 -0
- package/plugins/compound-engineering/skills/dspy-ruby/references/core-concepts.md +674 -0
- package/plugins/compound-engineering/skills/dspy-ruby/references/observability.md +366 -0
- package/plugins/compound-engineering/skills/dspy-ruby/references/optimization.md +603 -0
- package/plugins/compound-engineering/skills/dspy-ruby/references/providers.md +418 -0
- package/plugins/compound-engineering/skills/dspy-ruby/references/toolsets.md +502 -0
- package/plugins/compound-engineering/skills/every-style-editor/SKILL.md +134 -0
- package/plugins/compound-engineering/skills/every-style-editor/references/EVERY_WRITE_STYLE.md +529 -0
- package/plugins/compound-engineering/skills/file-todos/SKILL.md +252 -0
- package/plugins/compound-engineering/skills/file-todos/assets/todo-template.md +155 -0
- package/plugins/compound-engineering/skills/frontend-design/SKILL.md +42 -0
- package/plugins/compound-engineering/skills/gemini-imagegen/SKILL.md +237 -0
- package/plugins/compound-engineering/skills/gemini-imagegen/requirements.txt +2 -0
- package/plugins/compound-engineering/skills/gemini-imagegen/scripts/compose_images.py +157 -0
- package/plugins/compound-engineering/skills/gemini-imagegen/scripts/edit_image.py +144 -0
- package/plugins/compound-engineering/skills/gemini-imagegen/scripts/gemini_images.py +263 -0
- package/plugins/compound-engineering/skills/gemini-imagegen/scripts/generate_image.py +133 -0
- package/plugins/compound-engineering/skills/gemini-imagegen/scripts/multi_turn_chat.py +216 -0
- package/plugins/compound-engineering/skills/git-worktree/SKILL.md +302 -0
- package/plugins/compound-engineering/skills/git-worktree/scripts/worktree-manager.sh +337 -0
- package/plugins/compound-engineering/skills/orchestrating-swarms/SKILL.md +1718 -0
- package/plugins/compound-engineering/skills/rclone/SKILL.md +150 -0
- package/plugins/compound-engineering/skills/rclone/scripts/check_setup.sh +60 -0
- package/plugins/compound-engineering/skills/resolve-pr-parallel/SKILL.md +89 -0
- package/plugins/compound-engineering/skills/resolve-pr-parallel/scripts/get-pr-comments +68 -0
- package/plugins/compound-engineering/skills/resolve-pr-parallel/scripts/resolve-pr-thread +23 -0
- package/plugins/compound-engineering/skills/skill-creator/SKILL.md +210 -0
- package/plugins/compound-engineering/skills/skill-creator/scripts/init_skill.py +303 -0
- package/plugins/compound-engineering/skills/skill-creator/scripts/package_skill.py +110 -0
- package/plugins/compound-engineering/skills/skill-creator/scripts/quick_validate.py +65 -0
- package/prompts/deepen-plan.md +549 -0
- package/prompts/feature-video.md +341 -0
- package/prompts/resolve_todo_parallel.md +36 -0
- package/prompts/test-browser.md +342 -0
- package/prompts/workflows-brainstorm.md +123 -0
- package/prompts/workflows-compound.md +238 -0
- package/prompts/workflows-plan.md +550 -0
- package/prompts/workflows-review.md +529 -0
- package/prompts/workflows-work.md +432 -0
- package/skills/agent-browser/SKILL.md +223 -0
- package/skills/agent-native-architecture/SKILL.md +435 -0
- package/skills/agent-native-architecture/references/action-parity-discipline.md +409 -0
- package/skills/agent-native-architecture/references/agent-execution-patterns.md +467 -0
- package/skills/agent-native-architecture/references/agent-native-testing.md +582 -0
- package/skills/agent-native-architecture/references/architecture-patterns.md +478 -0
- package/skills/agent-native-architecture/references/dynamic-context-injection.md +338 -0
- package/skills/agent-native-architecture/references/files-universal-interface.md +301 -0
- package/skills/agent-native-architecture/references/from-primitives-to-domain-tools.md +359 -0
- package/skills/agent-native-architecture/references/mcp-tool-design.md +506 -0
- package/skills/agent-native-architecture/references/mobile-patterns.md +871 -0
- package/skills/agent-native-architecture/references/product-implications.md +443 -0
- package/skills/agent-native-architecture/references/refactoring-to-prompt-native.md +317 -0
- package/skills/agent-native-architecture/references/self-modification.md +269 -0
- package/skills/agent-native-architecture/references/shared-workspace-architecture.md +680 -0
- package/skills/agent-native-architecture/references/system-prompt-design.md +250 -0
- package/skills/agent-native-reviewer/SKILL.md +260 -0
- package/skills/andrew-kane-gem-writer/SKILL.md +184 -0
- package/skills/andrew-kane-gem-writer/references/database-adapters.md +231 -0
- package/skills/andrew-kane-gem-writer/references/module-organization.md +121 -0
- package/skills/andrew-kane-gem-writer/references/rails-integration.md +183 -0
- package/skills/andrew-kane-gem-writer/references/resources.md +119 -0
- package/skills/andrew-kane-gem-writer/references/testing-patterns.md +261 -0
- package/skills/ankane-readme-writer/SKILL.md +63 -0
- package/skills/architecture-strategist/SKILL.md +66 -0
- package/skills/best-practices-researcher/SKILL.md +125 -0
- package/skills/brainstorming/SKILL.md +190 -0
- package/skills/bug-reproduction-validator/SKILL.md +81 -0
- package/skills/code-simplicity-reviewer/SKILL.md +100 -0
- package/skills/compound-docs/SKILL.md +511 -0
- package/skills/compound-docs/assets/critical-pattern-template.md +34 -0
- package/skills/compound-docs/assets/resolution-template.md +93 -0
- package/skills/compound-docs/references/yaml-schema.md +65 -0
- package/skills/compound-docs/schema.yaml +176 -0
- package/skills/create-agent-skills/SKILL.md +275 -0
- package/skills/create-agent-skills/references/api-security.md +226 -0
- package/skills/create-agent-skills/references/be-clear-and-direct.md +531 -0
- package/skills/create-agent-skills/references/best-practices.md +404 -0
- package/skills/create-agent-skills/references/common-patterns.md +595 -0
- package/skills/create-agent-skills/references/core-principles.md +437 -0
- package/skills/create-agent-skills/references/executable-code.md +175 -0
- package/skills/create-agent-skills/references/iteration-and-testing.md +474 -0
- package/skills/create-agent-skills/references/official-spec.md +134 -0
- package/skills/create-agent-skills/references/recommended-structure.md +168 -0
- package/skills/create-agent-skills/references/skill-structure.md +152 -0
- package/skills/create-agent-skills/references/using-scripts.md +113 -0
- package/skills/create-agent-skills/references/using-templates.md +112 -0
- package/skills/create-agent-skills/references/workflows-and-validation.md +510 -0
- package/skills/create-agent-skills/templates/router-skill.md +73 -0
- package/skills/create-agent-skills/templates/simple-skill.md +33 -0
- package/skills/create-agent-skills/workflows/add-reference.md +96 -0
- package/skills/create-agent-skills/workflows/add-script.md +93 -0
- package/skills/create-agent-skills/workflows/add-template.md +74 -0
- package/skills/create-agent-skills/workflows/add-workflow.md +120 -0
- package/skills/create-agent-skills/workflows/audit-skill.md +138 -0
- package/skills/create-agent-skills/workflows/create-domain-expertise-skill.md +605 -0
- package/skills/create-agent-skills/workflows/create-new-skill.md +191 -0
- package/skills/create-agent-skills/workflows/get-guidance.md +121 -0
- package/skills/create-agent-skills/workflows/upgrade-to-router.md +161 -0
- package/skills/create-agent-skills/workflows/verify-skill.md +204 -0
- package/skills/data-integrity-guardian/SKILL.md +84 -0
- package/skills/data-migration-expert/SKILL.md +111 -0
- package/skills/deployment-verification-agent/SKILL.md +173 -0
- package/skills/design-implementation-reviewer/SKILL.md +107 -0
- package/skills/design-iterator/SKILL.md +222 -0
- package/skills/dhh-rails-reviewer/SKILL.md +65 -0
- package/skills/dhh-rails-style/SKILL.md +185 -0
- package/skills/dhh-rails-style/references/architecture.md +653 -0
- package/skills/dhh-rails-style/references/controllers.md +303 -0
- package/skills/dhh-rails-style/references/frontend.md +510 -0
- package/skills/dhh-rails-style/references/gems.md +266 -0
- package/skills/dhh-rails-style/references/models.md +359 -0
- package/skills/dhh-rails-style/references/testing.md +338 -0
- package/skills/document-review/SKILL.md +87 -0
- package/skills/dspy-ruby/SKILL.md +737 -0
- package/skills/dspy-ruby/assets/config-template.rb +187 -0
- package/skills/dspy-ruby/assets/module-template.rb +300 -0
- package/skills/dspy-ruby/assets/signature-template.rb +221 -0
- package/skills/dspy-ruby/references/core-concepts.md +674 -0
- package/skills/dspy-ruby/references/observability.md +366 -0
- package/skills/dspy-ruby/references/optimization.md +603 -0
- package/skills/dspy-ruby/references/providers.md +418 -0
- package/skills/dspy-ruby/references/toolsets.md +502 -0
- package/skills/every-style-editor/SKILL.md +134 -0
- package/skills/every-style-editor/references/EVERY_WRITE_STYLE.md +529 -0
- package/skills/every-style-editor-2/SKILL.md +62 -0
- package/skills/figma-design-sync/SKILL.md +188 -0
- package/skills/file-todos/SKILL.md +252 -0
- package/skills/file-todos/assets/todo-template.md +155 -0
- package/skills/framework-docs-researcher/SKILL.md +105 -0
- package/skills/frontend-design/SKILL.md +42 -0
- package/skills/gemini-imagegen/SKILL.md +237 -0
- package/skills/gemini-imagegen/requirements.txt +2 -0
- package/skills/gemini-imagegen/scripts/compose_images.py +157 -0
- package/skills/gemini-imagegen/scripts/edit_image.py +144 -0
- package/skills/gemini-imagegen/scripts/gemini_images.py +263 -0
- package/skills/gemini-imagegen/scripts/generate_image.py +133 -0
- package/skills/gemini-imagegen/scripts/multi_turn_chat.py +216 -0
- package/skills/git-history-analyzer/SKILL.md +58 -0
- package/skills/git-worktree/SKILL.md +302 -0
- package/skills/git-worktree/scripts/worktree-manager.sh +337 -0
- package/skills/julik-frontend-races-reviewer/SKILL.md +220 -0
- package/skills/kieran-python-reviewer/SKILL.md +132 -0
- package/skills/kieran-rails-reviewer/SKILL.md +114 -0
- package/skills/kieran-typescript-reviewer/SKILL.md +123 -0
- package/skills/learnings-researcher/SKILL.md +263 -0
- package/skills/lint/SKILL.md +14 -0
- package/skills/orchestrating-swarms/SKILL.md +1718 -0
- package/skills/pattern-recognition-specialist/SKILL.md +71 -0
- package/skills/performance-oracle/SKILL.md +136 -0
- package/skills/pr-comment-resolver/SKILL.md +82 -0
- package/skills/rclone/SKILL.md +150 -0
- package/skills/rclone/scripts/check_setup.sh +60 -0
- package/skills/repo-research-analyst/SKILL.md +134 -0
- package/skills/resolve_pr_parallel/SKILL.md +89 -0
- package/skills/resolve_pr_parallel/scripts/get-pr-comments +68 -0
- package/skills/resolve_pr_parallel/scripts/resolve-pr-thread +23 -0
- package/skills/schema-drift-detector/SKILL.md +153 -0
- package/skills/security-sentinel/SKILL.md +113 -0
- package/skills/skill-creator/SKILL.md +210 -0
- package/skills/skill-creator/scripts/init_skill.py +303 -0
- package/skills/skill-creator/scripts/package_skill.py +110 -0
- package/skills/skill-creator/scripts/quick_validate.py +65 -0
- package/skills/spec-flow-analyzer/SKILL.md +133 -0
- package/src/commands/convert.ts +183 -0
- package/src/commands/install.ts +273 -0
- package/src/commands/list.ts +37 -0
- package/src/commands/sync.ts +89 -0
- package/src/converters/claude-to-codex.ts +182 -0
- package/src/converters/claude-to-opencode.ts +395 -0
- package/src/converters/claude-to-pi.ts +205 -0
- package/src/index.ts +22 -0
- package/src/parsers/claude-home.ts +65 -0
- package/src/parsers/claude.ts +252 -0
- package/src/sync/codex.ts +92 -0
- package/src/sync/opencode.ts +75 -0
- package/src/sync/pi.ts +88 -0
- package/src/targets/codex.ts +96 -0
- package/src/targets/index.ts +38 -0
- package/src/targets/opencode.ts +57 -0
- package/src/targets/pi.ts +131 -0
- package/src/templates/pi/compat-extension.ts +452 -0
- package/src/types/claude.ts +90 -0
- package/src/types/codex.ts +23 -0
- package/src/types/opencode.ts +54 -0
- package/src/types/pi.ts +40 -0
- package/src/utils/codex-agents.ts +64 -0
- package/src/utils/files.ts +77 -0
- package/src/utils/frontmatter.ts +65 -0
- package/src/utils/symlink.ts +43 -0
|
@@ -0,0 +1,737 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: dspy-ruby
|
|
3
|
+
description: Build type-safe LLM applications with DSPy.rb — Ruby's programmatic prompt framework with signatures, modules, agents, and optimization. Use when implementing predictable AI features, creating LLM signatures and modules, configuring language model providers, building agent systems with tools, optimizing prompts, or testing LLM-powered functionality in Ruby applications.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# DSPy.rb
|
|
7
|
+
|
|
8
|
+
> Build LLM apps like you build software. Type-safe, modular, testable.
|
|
9
|
+
|
|
10
|
+
DSPy.rb brings software engineering best practices to LLM development. Instead of tweaking prompts, define what you want with Ruby types and let DSPy handle the rest.
|
|
11
|
+
|
|
12
|
+
## Overview
|
|
13
|
+
|
|
14
|
+
DSPy.rb is a Ruby framework for building language model applications with programmatic prompts. It provides:
|
|
15
|
+
|
|
16
|
+
- **Type-safe signatures** — Define inputs/outputs with Sorbet types
|
|
17
|
+
- **Modular components** — Compose and reuse LLM logic
|
|
18
|
+
- **Automatic optimization** — Use data to improve prompts, not guesswork
|
|
19
|
+
- **Production-ready** — Built-in observability, testing, and error handling
|
|
20
|
+
|
|
21
|
+
## Core Concepts
|
|
22
|
+
|
|
23
|
+
### 1. Signatures
|
|
24
|
+
|
|
25
|
+
Define interfaces between your app and LLMs using Ruby types:
|
|
26
|
+
|
|
27
|
+
```ruby
|
|
28
|
+
class EmailClassifier < DSPy::Signature
|
|
29
|
+
description "Classify customer support emails by category and priority"
|
|
30
|
+
|
|
31
|
+
class Priority < T::Enum
|
|
32
|
+
enums do
|
|
33
|
+
Low = new('low')
|
|
34
|
+
Medium = new('medium')
|
|
35
|
+
High = new('high')
|
|
36
|
+
Urgent = new('urgent')
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
input do
|
|
41
|
+
const :email_content, String
|
|
42
|
+
const :sender, String
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
output do
|
|
46
|
+
const :category, String
|
|
47
|
+
const :priority, Priority # Type-safe enum with defined values
|
|
48
|
+
const :confidence, Float
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### 2. Modules
|
|
54
|
+
|
|
55
|
+
Build complex workflows from simple building blocks:
|
|
56
|
+
|
|
57
|
+
- **Predict** — Basic LLM calls with signatures
|
|
58
|
+
- **ChainOfThought** — Step-by-step reasoning
|
|
59
|
+
- **ReAct** — Tool-using agents
|
|
60
|
+
- **CodeAct** — Dynamic code generation agents (install the `dspy-code_act` gem)
|
|
61
|
+
|
|
62
|
+
### 3. Tools & Toolsets
|
|
63
|
+
|
|
64
|
+
Create type-safe tools for agents with comprehensive Sorbet support:
|
|
65
|
+
|
|
66
|
+
```ruby
|
|
67
|
+
# Enum-based tool with automatic type conversion
|
|
68
|
+
class CalculatorTool < DSPy::Tools::Base
|
|
69
|
+
tool_name 'calculator'
|
|
70
|
+
tool_description 'Performs arithmetic operations with type-safe enum inputs'
|
|
71
|
+
|
|
72
|
+
class Operation < T::Enum
|
|
73
|
+
enums do
|
|
74
|
+
Add = new('add')
|
|
75
|
+
Subtract = new('subtract')
|
|
76
|
+
Multiply = new('multiply')
|
|
77
|
+
Divide = new('divide')
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
sig { params(operation: Operation, num1: Float, num2: Float).returns(T.any(Float, String)) }
|
|
82
|
+
def call(operation:, num1:, num2:)
|
|
83
|
+
case operation
|
|
84
|
+
when Operation::Add then num1 + num2
|
|
85
|
+
when Operation::Subtract then num1 - num2
|
|
86
|
+
when Operation::Multiply then num1 * num2
|
|
87
|
+
when Operation::Divide
|
|
88
|
+
return "Error: Division by zero" if num2 == 0
|
|
89
|
+
num1 / num2
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
# Multi-tool toolset with rich types
|
|
95
|
+
class DataToolset < DSPy::Tools::Toolset
|
|
96
|
+
toolset_name "data_processing"
|
|
97
|
+
|
|
98
|
+
class Format < T::Enum
|
|
99
|
+
enums do
|
|
100
|
+
JSON = new('json')
|
|
101
|
+
CSV = new('csv')
|
|
102
|
+
XML = new('xml')
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
tool :convert, description: "Convert data between formats"
|
|
107
|
+
tool :validate, description: "Validate data structure"
|
|
108
|
+
|
|
109
|
+
sig { params(data: String, from: Format, to: Format).returns(String) }
|
|
110
|
+
def convert(data:, from:, to:)
|
|
111
|
+
"Converted from #{from.serialize} to #{to.serialize}"
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
sig { params(data: String, format: Format).returns(T::Hash[String, T.any(String, Integer, T::Boolean)]) }
|
|
115
|
+
def validate(data:, format:)
|
|
116
|
+
{ valid: true, format: format.serialize, row_count: 42, message: "Data validation passed" }
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
### 4. Type System & Discriminators
|
|
122
|
+
|
|
123
|
+
DSPy.rb uses sophisticated type discrimination for complex data structures:
|
|
124
|
+
|
|
125
|
+
- **Automatic `_type` field injection** — DSPy adds discriminator fields to structs for type safety
|
|
126
|
+
- **Union type support** — `T.any()` types automatically disambiguated by `_type`
|
|
127
|
+
- **Reserved field name** — Avoid defining your own `_type` fields in structs
|
|
128
|
+
- **Recursive filtering** — `_type` fields filtered during deserialization at all nesting levels
|
|
129
|
+
|
|
130
|
+
### 5. Optimization
|
|
131
|
+
|
|
132
|
+
Improve accuracy with real data:
|
|
133
|
+
|
|
134
|
+
- **MIPROv2** — Advanced multi-prompt optimization with bootstrap sampling and Bayesian optimization
|
|
135
|
+
- **GEPA** — Genetic-Pareto Reflective Prompt Evolution with feedback maps, experiment tracking, and telemetry
|
|
136
|
+
- **Evaluation** — Comprehensive framework with built-in and custom metrics, error handling, and batch processing
|
|
137
|
+
|
|
138
|
+
## Quick Start
|
|
139
|
+
|
|
140
|
+
```ruby
|
|
141
|
+
# Install
|
|
142
|
+
gem 'dspy'
|
|
143
|
+
|
|
144
|
+
# Configure
|
|
145
|
+
DSPy.configure do |c|
|
|
146
|
+
c.lm = DSPy::LM.new('openai/gpt-4o-mini', api_key: ENV['OPENAI_API_KEY'])
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
# Define a task
|
|
150
|
+
class SentimentAnalysis < DSPy::Signature
|
|
151
|
+
description "Analyze sentiment of text"
|
|
152
|
+
|
|
153
|
+
input do
|
|
154
|
+
const :text, String
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
output do
|
|
158
|
+
const :sentiment, String # positive, negative, neutral
|
|
159
|
+
const :score, Float # 0.0 to 1.0
|
|
160
|
+
end
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
# Use it
|
|
164
|
+
analyzer = DSPy::Predict.new(SentimentAnalysis)
|
|
165
|
+
result = analyzer.call(text: "This product is amazing!")
|
|
166
|
+
puts result.sentiment # => "positive"
|
|
167
|
+
puts result.score # => 0.92
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
## Provider Adapter Gems
|
|
171
|
+
|
|
172
|
+
Two strategies for connecting to LLM providers:
|
|
173
|
+
|
|
174
|
+
### Per-provider adapters (direct SDK access)
|
|
175
|
+
|
|
176
|
+
```ruby
|
|
177
|
+
# Gemfile
|
|
178
|
+
gem 'dspy'
|
|
179
|
+
gem 'dspy-openai' # OpenAI, OpenRouter, Ollama
|
|
180
|
+
gem 'dspy-anthropic' # Claude
|
|
181
|
+
gem 'dspy-gemini' # Gemini
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
Each adapter gem pulls in the official SDK (`openai`, `anthropic`, `gemini-ai`).
|
|
185
|
+
|
|
186
|
+
### Unified adapter via RubyLLM (recommended for multi-provider)
|
|
187
|
+
|
|
188
|
+
```ruby
|
|
189
|
+
# Gemfile
|
|
190
|
+
gem 'dspy'
|
|
191
|
+
gem 'dspy-ruby_llm' # Routes to any provider via ruby_llm
|
|
192
|
+
gem 'ruby_llm'
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
RubyLLM handles provider routing based on the model name. Use the `ruby_llm/` prefix:
|
|
196
|
+
|
|
197
|
+
```ruby
|
|
198
|
+
DSPy.configure do |c|
|
|
199
|
+
c.lm = DSPy::LM.new('ruby_llm/gemini-2.5-flash', structured_outputs: true)
|
|
200
|
+
# c.lm = DSPy::LM.new('ruby_llm/claude-sonnet-4-20250514', structured_outputs: true)
|
|
201
|
+
# c.lm = DSPy::LM.new('ruby_llm/gpt-4o-mini', structured_outputs: true)
|
|
202
|
+
end
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
## Events System
|
|
206
|
+
|
|
207
|
+
DSPy.rb ships with a structured event bus for observing runtime behavior.
|
|
208
|
+
|
|
209
|
+
### Module-Scoped Subscriptions (preferred for agents)
|
|
210
|
+
|
|
211
|
+
```ruby
|
|
212
|
+
class MyAgent < DSPy::Module
|
|
213
|
+
subscribe 'lm.tokens', :track_tokens, scope: :descendants
|
|
214
|
+
|
|
215
|
+
def track_tokens(_event, attrs)
|
|
216
|
+
@total_tokens += attrs.fetch(:total_tokens, 0)
|
|
217
|
+
end
|
|
218
|
+
end
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
### Global Subscriptions (for observability/integrations)
|
|
222
|
+
|
|
223
|
+
```ruby
|
|
224
|
+
subscription_id = DSPy.events.subscribe('score.create') do |event, attrs|
|
|
225
|
+
Langfuse.export_score(attrs)
|
|
226
|
+
end
|
|
227
|
+
|
|
228
|
+
# Wildcards supported
|
|
229
|
+
DSPy.events.subscribe('llm.*') { |name, attrs| puts "[#{name}] tokens=#{attrs[:total_tokens]}" }
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
Event names use dot-separated namespaces (`llm.generate`, `react.iteration_complete`). Every event includes module metadata (`module_path`, `module_leaf`, `module_scope.ancestry_token`) for filtering.
|
|
233
|
+
|
|
234
|
+
## Lifecycle Callbacks
|
|
235
|
+
|
|
236
|
+
Rails-style lifecycle hooks ship with every `DSPy::Module`:
|
|
237
|
+
|
|
238
|
+
- **`before`** — Runs ahead of `forward` for setup (metrics, context loading)
|
|
239
|
+
- **`around`** — Wraps `forward`, calls `yield`, and lets you pair setup/teardown logic
|
|
240
|
+
- **`after`** — Fires after `forward` returns for cleanup or persistence
|
|
241
|
+
|
|
242
|
+
```ruby
|
|
243
|
+
class InstrumentedModule < DSPy::Module
|
|
244
|
+
before :setup_metrics
|
|
245
|
+
around :manage_context
|
|
246
|
+
after :log_metrics
|
|
247
|
+
|
|
248
|
+
def forward(question:)
|
|
249
|
+
@predictor.call(question: question)
|
|
250
|
+
end
|
|
251
|
+
|
|
252
|
+
private
|
|
253
|
+
|
|
254
|
+
def setup_metrics
|
|
255
|
+
@start_time = Time.now
|
|
256
|
+
end
|
|
257
|
+
|
|
258
|
+
def manage_context
|
|
259
|
+
load_context
|
|
260
|
+
result = yield
|
|
261
|
+
save_context
|
|
262
|
+
result
|
|
263
|
+
end
|
|
264
|
+
|
|
265
|
+
def log_metrics
|
|
266
|
+
duration = Time.now - @start_time
|
|
267
|
+
Rails.logger.info "Prediction completed in #{duration}s"
|
|
268
|
+
end
|
|
269
|
+
end
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
Execution order: before → around (before yield) → forward → around (after yield) → after. Callbacks are inherited from parent classes and execute in registration order.
|
|
273
|
+
|
|
274
|
+
## Fiber-Local LM Context
|
|
275
|
+
|
|
276
|
+
Override the language model temporarily using fiber-local storage:
|
|
277
|
+
|
|
278
|
+
```ruby
|
|
279
|
+
fast_model = DSPy::LM.new("openai/gpt-4o-mini", api_key: ENV['OPENAI_API_KEY'])
|
|
280
|
+
|
|
281
|
+
DSPy.with_lm(fast_model) do
|
|
282
|
+
result = classifier.call(text: "test") # Uses fast_model inside this block
|
|
283
|
+
end
|
|
284
|
+
# Back to global LM outside the block
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
**LM resolution hierarchy**: Instance-level LM → Fiber-local LM (`DSPy.with_lm`) → Global LM (`DSPy.configure`).
|
|
288
|
+
|
|
289
|
+
Use `configure_predictor` for fine-grained control over agent internals:
|
|
290
|
+
|
|
291
|
+
```ruby
|
|
292
|
+
agent = DSPy::ReAct.new(MySignature, tools: tools)
|
|
293
|
+
agent.configure { |c| c.lm = default_model }
|
|
294
|
+
agent.configure_predictor('thought_generator') { |c| c.lm = powerful_model }
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
## Evaluation Framework
|
|
298
|
+
|
|
299
|
+
Systematically test LLM application performance with `DSPy::Evals`:
|
|
300
|
+
|
|
301
|
+
```ruby
|
|
302
|
+
metric = DSPy::Metrics.exact_match(field: :answer, case_sensitive: false)
|
|
303
|
+
evaluator = DSPy::Evals.new(predictor, metric: metric)
|
|
304
|
+
result = evaluator.evaluate(test_examples, display_table: true)
|
|
305
|
+
puts "Pass Rate: #{(result.pass_rate * 100).round(1)}%"
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
Built-in metrics: `exact_match`, `contains`, `numeric_difference`, `composite_and`. Custom metrics return `true`/`false` or a `DSPy::Prediction` with `score:` and `feedback:` fields.
|
|
309
|
+
|
|
310
|
+
Use `DSPy::Example` for typed test data and `export_scores: true` to push results to Langfuse.
|
|
311
|
+
|
|
312
|
+
## GEPA Optimization
|
|
313
|
+
|
|
314
|
+
GEPA (Genetic-Pareto Reflective Prompt Evolution) uses reflection-driven instruction rewrites:
|
|
315
|
+
|
|
316
|
+
```ruby
|
|
317
|
+
gem 'dspy-gepa'
|
|
318
|
+
|
|
319
|
+
teleprompter = DSPy::Teleprompt::GEPA.new(
|
|
320
|
+
metric: metric,
|
|
321
|
+
reflection_lm: DSPy::ReflectionLM.new('openai/gpt-4o-mini', api_key: ENV['OPENAI_API_KEY']),
|
|
322
|
+
feedback_map: feedback_map,
|
|
323
|
+
config: { max_metric_calls: 600, minibatch_size: 6 }
|
|
324
|
+
)
|
|
325
|
+
|
|
326
|
+
result = teleprompter.compile(program, trainset: train, valset: val)
|
|
327
|
+
optimized_program = result.optimized_program
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
The metric must return `DSPy::Prediction.new(score:, feedback:)` so the reflection model can reason about failures. Use `feedback_map` to target individual predictors in composite modules.
|
|
331
|
+
|
|
332
|
+
## Typed Context Pattern
|
|
333
|
+
|
|
334
|
+
Replace opaque string context blobs with `T::Struct` inputs. Each field gets its own `description:` annotation in the JSON schema the LLM sees:
|
|
335
|
+
|
|
336
|
+
```ruby
|
|
337
|
+
class NavigationContext < T::Struct
|
|
338
|
+
const :workflow_hint, T.nilable(String),
|
|
339
|
+
description: "Current workflow phase guidance for the agent"
|
|
340
|
+
const :action_log, T::Array[String], default: [],
|
|
341
|
+
description: "Compact one-line-per-action history of research steps taken"
|
|
342
|
+
const :iterations_remaining, Integer,
|
|
343
|
+
description: "Budget remaining. Each tool call costs 1 iteration."
|
|
344
|
+
end
|
|
345
|
+
|
|
346
|
+
class ToolSelectionSignature < DSPy::Signature
|
|
347
|
+
input do
|
|
348
|
+
const :query, String
|
|
349
|
+
const :context, NavigationContext # Structured, not an opaque string
|
|
350
|
+
end
|
|
351
|
+
|
|
352
|
+
output do
|
|
353
|
+
const :tool_name, String
|
|
354
|
+
const :tool_args, String, description: "JSON-encoded arguments"
|
|
355
|
+
end
|
|
356
|
+
end
|
|
357
|
+
```
|
|
358
|
+
|
|
359
|
+
Benefits: type safety at compile time, per-field descriptions in the LLM schema, easy to test as value objects, extensible by adding `const` declarations.
|
|
360
|
+
|
|
361
|
+
## Schema Formats (BAML / TOON)
|
|
362
|
+
|
|
363
|
+
Control how DSPy describes signature structure to the LLM:
|
|
364
|
+
|
|
365
|
+
- **JSON Schema** (default) — Standard format, works with `structured_outputs: true`
|
|
366
|
+
- **BAML** (`schema_format: :baml`) — 84% token reduction for Enhanced Prompting mode. Requires `sorbet-baml` gem.
|
|
367
|
+
- **TOON** (`schema_format: :toon, data_format: :toon`) — Table-oriented format for both schemas and data. Enhanced Prompting mode only.
|
|
368
|
+
|
|
369
|
+
BAML and TOON apply only when `structured_outputs: false`. With `structured_outputs: true`, the provider receives JSON Schema directly.
|
|
370
|
+
|
|
371
|
+
## Storage System
|
|
372
|
+
|
|
373
|
+
Persist and reload optimized programs with `DSPy::Storage::ProgramStorage`:
|
|
374
|
+
|
|
375
|
+
```ruby
|
|
376
|
+
storage = DSPy::Storage::ProgramStorage.new(storage_path: "./dspy_storage")
|
|
377
|
+
storage.save_program(result.optimized_program, result, metadata: { optimizer: 'MIPROv2' })
|
|
378
|
+
```
|
|
379
|
+
|
|
380
|
+
Supports checkpoint management, optimization history tracking, and import/export between environments.
|
|
381
|
+
|
|
382
|
+
## Rails Integration
|
|
383
|
+
|
|
384
|
+
### Directory Structure
|
|
385
|
+
|
|
386
|
+
Organize DSPy components using Rails conventions:
|
|
387
|
+
|
|
388
|
+
```
|
|
389
|
+
app/
|
|
390
|
+
entities/ # T::Struct types shared across signatures
|
|
391
|
+
signatures/ # DSPy::Signature definitions
|
|
392
|
+
tools/ # DSPy::Tools::Base implementations
|
|
393
|
+
concerns/ # Shared tool behaviors (error handling, etc.)
|
|
394
|
+
modules/ # DSPy::Module orchestrators
|
|
395
|
+
services/ # Plain Ruby services that compose DSPy modules
|
|
396
|
+
config/
|
|
397
|
+
initializers/
|
|
398
|
+
dspy.rb # DSPy + provider configuration
|
|
399
|
+
feature_flags.rb # Model selection per role
|
|
400
|
+
spec/
|
|
401
|
+
signatures/ # Schema validation tests
|
|
402
|
+
tools/ # Tool unit tests
|
|
403
|
+
modules/ # Integration tests with VCR
|
|
404
|
+
vcr_cassettes/ # Recorded HTTP interactions
|
|
405
|
+
```
|
|
406
|
+
|
|
407
|
+
### Initializer
|
|
408
|
+
|
|
409
|
+
```ruby
|
|
410
|
+
# config/initializers/dspy.rb
|
|
411
|
+
Rails.application.config.after_initialize do
|
|
412
|
+
next if Rails.env.test? && ENV["DSPY_ENABLE_IN_TEST"].blank?
|
|
413
|
+
|
|
414
|
+
RubyLLM.configure do |config|
|
|
415
|
+
config.gemini_api_key = ENV["GEMINI_API_KEY"] if ENV["GEMINI_API_KEY"].present?
|
|
416
|
+
config.anthropic_api_key = ENV["ANTHROPIC_API_KEY"] if ENV["ANTHROPIC_API_KEY"].present?
|
|
417
|
+
config.openai_api_key = ENV["OPENAI_API_KEY"] if ENV["OPENAI_API_KEY"].present?
|
|
418
|
+
end
|
|
419
|
+
|
|
420
|
+
model = ENV.fetch("DSPY_MODEL", "ruby_llm/gemini-2.5-flash")
|
|
421
|
+
DSPy.configure do |config|
|
|
422
|
+
config.lm = DSPy::LM.new(model, structured_outputs: true)
|
|
423
|
+
config.logger = Rails.logger
|
|
424
|
+
end
|
|
425
|
+
|
|
426
|
+
# Langfuse observability (optional)
|
|
427
|
+
if ENV["LANGFUSE_PUBLIC_KEY"].present? && ENV["LANGFUSE_SECRET_KEY"].present?
|
|
428
|
+
DSPy::Observability.configure!
|
|
429
|
+
end
|
|
430
|
+
end
|
|
431
|
+
```
|
|
432
|
+
|
|
433
|
+
### Feature-Flagged Model Selection
|
|
434
|
+
|
|
435
|
+
Use different models for different roles (fast/cheap for classification, powerful for synthesis):
|
|
436
|
+
|
|
437
|
+
```ruby
|
|
438
|
+
# config/initializers/feature_flags.rb
|
|
439
|
+
module FeatureFlags
|
|
440
|
+
SELECTOR_MODEL = ENV.fetch("DSPY_SELECTOR_MODEL", "ruby_llm/gemini-2.5-flash-lite")
|
|
441
|
+
SYNTHESIZER_MODEL = ENV.fetch("DSPY_SYNTHESIZER_MODEL", "ruby_llm/gemini-2.5-flash")
|
|
442
|
+
end
|
|
443
|
+
```
|
|
444
|
+
|
|
445
|
+
Then override per-tool or per-predictor:
|
|
446
|
+
|
|
447
|
+
```ruby
|
|
448
|
+
class ClassifyTool < DSPy::Tools::Base
|
|
449
|
+
def call(query:)
|
|
450
|
+
predictor = DSPy::Predict.new(ClassifyQuery)
|
|
451
|
+
predictor.configure { |c| c.lm = DSPy::LM.new(FeatureFlags::SELECTOR_MODEL, structured_outputs: true) }
|
|
452
|
+
predictor.call(query: query)
|
|
453
|
+
end
|
|
454
|
+
end
|
|
455
|
+
```
|
|
456
|
+
|
|
457
|
+
## Schema-Driven Signatures
|
|
458
|
+
|
|
459
|
+
**Prefer typed schemas over string descriptions.** Let the type system communicate structure to the LLM rather than prose in the signature description.
|
|
460
|
+
|
|
461
|
+
### Entities as Shared Types
|
|
462
|
+
|
|
463
|
+
Define reusable `T::Struct` and `T::Enum` types in `app/entities/` and reference them across signatures:
|
|
464
|
+
|
|
465
|
+
```ruby
|
|
466
|
+
# app/entities/search_strategy.rb
|
|
467
|
+
class SearchStrategy < T::Enum
|
|
468
|
+
enums do
|
|
469
|
+
SingleSearch = new("single_search")
|
|
470
|
+
DateDecomposition = new("date_decomposition")
|
|
471
|
+
end
|
|
472
|
+
end
|
|
473
|
+
|
|
474
|
+
# app/entities/scored_item.rb
|
|
475
|
+
class ScoredItem < T::Struct
|
|
476
|
+
const :id, String
|
|
477
|
+
const :score, Float, description: "Relevance score 0.0-1.0"
|
|
478
|
+
const :verdict, String, description: "relevant, maybe, or irrelevant"
|
|
479
|
+
const :reason, String, default: ""
|
|
480
|
+
end
|
|
481
|
+
```
|
|
482
|
+
|
|
483
|
+
### Schema vs Description: When to Use Each
|
|
484
|
+
|
|
485
|
+
**Use schemas (T::Struct/T::Enum)** for:
|
|
486
|
+
- Multi-field outputs with specific types
|
|
487
|
+
- Enums with defined values the LLM must pick from
|
|
488
|
+
- Nested structures, arrays of typed objects
|
|
489
|
+
- Outputs consumed by code (not displayed to users)
|
|
490
|
+
|
|
491
|
+
**Use string descriptions** for:
|
|
492
|
+
- Simple single-field outputs where the type is `String`
|
|
493
|
+
- Natural language generation (summaries, answers)
|
|
494
|
+
- Fields where constraint guidance helps (e.g., `description: "YYYY-MM-DD format"`)
|
|
495
|
+
|
|
496
|
+
**Rule of thumb**: If you'd write a `case` statement on the output, it should be a `T::Enum`. If you'd call `.each` on it, it should be `T::Array[SomeStruct]`.
|
|
497
|
+
|
|
498
|
+
## Tool Patterns
|
|
499
|
+
|
|
500
|
+
### Tools That Wrap Predictions
|
|
501
|
+
|
|
502
|
+
A common pattern: tools encapsulate a DSPy prediction, adding error handling, model selection, and serialization:
|
|
503
|
+
|
|
504
|
+
```ruby
|
|
505
|
+
class RerankTool < DSPy::Tools::Base
|
|
506
|
+
tool_name "rerank"
|
|
507
|
+
tool_description "Score and rank search results by relevance"
|
|
508
|
+
|
|
509
|
+
MAX_ITEMS = 200
|
|
510
|
+
MIN_ITEMS_FOR_LLM = 5
|
|
511
|
+
|
|
512
|
+
sig { params(query: String, items: T::Array[T::Hash[Symbol, T.untyped]]).returns(T::Hash[Symbol, T.untyped]) }
|
|
513
|
+
def call(query:, items: [])
|
|
514
|
+
return { scored_items: items, reranked: false } if items.size < MIN_ITEMS_FOR_LLM
|
|
515
|
+
|
|
516
|
+
capped_items = items.first(MAX_ITEMS)
|
|
517
|
+
predictor = DSPy::Predict.new(RerankSignature)
|
|
518
|
+
predictor.configure { |c| c.lm = DSPy::LM.new(FeatureFlags::SYNTHESIZER_MODEL, structured_outputs: true) }
|
|
519
|
+
|
|
520
|
+
result = predictor.call(query: query, items: capped_items)
|
|
521
|
+
{ scored_items: result.scored_items, reranked: true }
|
|
522
|
+
rescue => e
|
|
523
|
+
Rails.logger.warn "[RerankTool] LLM rerank failed: #{e.message}"
|
|
524
|
+
{ error: "Rerank failed: #{e.message}", scored_items: items, reranked: false }
|
|
525
|
+
end
|
|
526
|
+
end
|
|
527
|
+
```
|
|
528
|
+
|
|
529
|
+
**Key patterns:**
|
|
530
|
+
- Short-circuit LLM calls when unnecessary (small data, trivial cases)
|
|
531
|
+
- Cap input size to prevent token overflow
|
|
532
|
+
- Per-tool model selection via `configure`
|
|
533
|
+
- Graceful error handling with fallback data
|
|
534
|
+
|
|
535
|
+
### Error Handling Concern
|
|
536
|
+
|
|
537
|
+
```ruby
|
|
538
|
+
module ErrorHandling
|
|
539
|
+
extend ActiveSupport::Concern
|
|
540
|
+
|
|
541
|
+
private
|
|
542
|
+
|
|
543
|
+
def safe_predict(signature_class, **inputs)
|
|
544
|
+
predictor = DSPy::Predict.new(signature_class)
|
|
545
|
+
yield predictor if block_given?
|
|
546
|
+
predictor.call(**inputs)
|
|
547
|
+
rescue Faraday::Error, Net::HTTPError => e
|
|
548
|
+
Rails.logger.error "[#{self.class.name}] API error: #{e.message}"
|
|
549
|
+
nil
|
|
550
|
+
rescue JSON::ParserError => e
|
|
551
|
+
Rails.logger.error "[#{self.class.name}] Invalid LLM output: #{e.message}"
|
|
552
|
+
nil
|
|
553
|
+
end
|
|
554
|
+
end
|
|
555
|
+
```
|
|
556
|
+
|
|
557
|
+
## Observability
|
|
558
|
+
|
|
559
|
+
### Tracing with DSPy::Context
|
|
560
|
+
|
|
561
|
+
Wrap operations in spans for Langfuse/OpenTelemetry visibility:
|
|
562
|
+
|
|
563
|
+
```ruby
|
|
564
|
+
result = DSPy::Context.with_span(
|
|
565
|
+
operation: "tool_selector.select",
|
|
566
|
+
"dspy.module" => "ToolSelector",
|
|
567
|
+
"tool_selector.tools" => tool_names.join(",")
|
|
568
|
+
) do
|
|
569
|
+
@predictor.call(query: query, context: context, available_tools: schemas)
|
|
570
|
+
end
|
|
571
|
+
```
|
|
572
|
+
|
|
573
|
+
### Setup for Langfuse
|
|
574
|
+
|
|
575
|
+
```ruby
|
|
576
|
+
# Gemfile
|
|
577
|
+
gem 'dspy-o11y'
|
|
578
|
+
gem 'dspy-o11y-langfuse'
|
|
579
|
+
|
|
580
|
+
# .env
|
|
581
|
+
LANGFUSE_PUBLIC_KEY=pk-...
|
|
582
|
+
LANGFUSE_SECRET_KEY=sk-...
|
|
583
|
+
DSPY_TELEMETRY_BATCH_SIZE=5
|
|
584
|
+
```
|
|
585
|
+
|
|
586
|
+
Every `DSPy::Predict`, `DSPy::ReAct`, and tool call is automatically traced when observability is configured.
|
|
587
|
+
|
|
588
|
+
### Score Reporting
|
|
589
|
+
|
|
590
|
+
Report evaluation scores to Langfuse:
|
|
591
|
+
|
|
592
|
+
```ruby
|
|
593
|
+
DSPy.score(name: "relevance", value: 0.85, trace_id: current_trace_id)
|
|
594
|
+
```
|
|
595
|
+
|
|
596
|
+
## Testing
|
|
597
|
+
|
|
598
|
+
### VCR Setup for Rails
|
|
599
|
+
|
|
600
|
+
```ruby
|
|
601
|
+
VCR.configure do |config|
|
|
602
|
+
config.cassette_library_dir = "spec/vcr_cassettes"
|
|
603
|
+
config.hook_into :webmock
|
|
604
|
+
config.configure_rspec_metadata!
|
|
605
|
+
config.filter_sensitive_data('<GEMINI_API_KEY>') { ENV['GEMINI_API_KEY'] }
|
|
606
|
+
config.filter_sensitive_data('<OPENAI_API_KEY>') { ENV['OPENAI_API_KEY'] }
|
|
607
|
+
end
|
|
608
|
+
```
|
|
609
|
+
|
|
610
|
+
### Signature Schema Tests
|
|
611
|
+
|
|
612
|
+
Test that signatures produce valid schemas without calling any LLM:
|
|
613
|
+
|
|
614
|
+
```ruby
|
|
615
|
+
RSpec.describe ClassifyResearchQuery do
|
|
616
|
+
it "has required input fields" do
|
|
617
|
+
schema = described_class.input_json_schema
|
|
618
|
+
expect(schema[:required]).to include("query")
|
|
619
|
+
end
|
|
620
|
+
|
|
621
|
+
it "has typed output fields" do
|
|
622
|
+
schema = described_class.output_json_schema
|
|
623
|
+
expect(schema[:properties]).to have_key(:search_strategy)
|
|
624
|
+
end
|
|
625
|
+
end
|
|
626
|
+
```
|
|
627
|
+
|
|
628
|
+
### Tool Tests with Mocked Predictions
|
|
629
|
+
|
|
630
|
+
```ruby
|
|
631
|
+
RSpec.describe RerankTool do
|
|
632
|
+
let(:tool) { described_class.new }
|
|
633
|
+
|
|
634
|
+
it "skips LLM for small result sets" do
|
|
635
|
+
expect(DSPy::Predict).not_to receive(:new)
|
|
636
|
+
result = tool.call(query: "test", items: [{ id: "1" }])
|
|
637
|
+
expect(result[:reranked]).to be false
|
|
638
|
+
end
|
|
639
|
+
|
|
640
|
+
it "calls LLM for large result sets", :vcr do
|
|
641
|
+
items = 10.times.map { |i| { id: i.to_s, title: "Item #{i}" } }
|
|
642
|
+
result = tool.call(query: "relevant items", items: items)
|
|
643
|
+
expect(result[:reranked]).to be true
|
|
644
|
+
end
|
|
645
|
+
end
|
|
646
|
+
```
|
|
647
|
+
|
|
648
|
+
## Resources
|
|
649
|
+
|
|
650
|
+
- [core-concepts.md](./references/core-concepts.md) — Signatures, modules, predictors, type system deep-dive
|
|
651
|
+
- [toolsets.md](./references/toolsets.md) — Tools::Base, Tools::Toolset DSL, type safety, testing
|
|
652
|
+
- [providers.md](./references/providers.md) — Provider adapters, RubyLLM, fiber-local LM context, compatibility matrix
|
|
653
|
+
- [optimization.md](./references/optimization.md) — MIPROv2, GEPA, evaluation framework, storage system
|
|
654
|
+
- [observability.md](./references/observability.md) — Event system, dspy-o11y gems, Langfuse, score reporting
|
|
655
|
+
- [signature-template.rb](./assets/signature-template.rb) — Signature scaffold with T::Enum, Date/Time, defaults, union types
|
|
656
|
+
- [module-template.rb](./assets/module-template.rb) — Module scaffold with .call(), lifecycle callbacks, fiber-local LM
|
|
657
|
+
- [config-template.rb](./assets/config-template.rb) — Rails initializer with RubyLLM, observability, feature flags
|
|
658
|
+
|
|
659
|
+
## Key URLs
|
|
660
|
+
|
|
661
|
+
- Homepage: https://oss.vicente.services/dspy.rb/
|
|
662
|
+
- GitHub: https://github.com/vicentereig/dspy.rb
|
|
663
|
+
- Documentation: https://oss.vicente.services/dspy.rb/getting-started/
|
|
664
|
+
|
|
665
|
+
## Guidelines for Claude
|
|
666
|
+
|
|
667
|
+
When helping users with DSPy.rb:
|
|
668
|
+
|
|
669
|
+
1. **Schema over prose** — Define output structure with `T::Struct` and `T::Enum` types, not string descriptions
|
|
670
|
+
2. **Entities in `app/entities/`** — Extract shared types so signatures stay thin
|
|
671
|
+
3. **Per-tool model selection** — Use `predictor.configure { |c| c.lm = ... }` to pick the right model per task
|
|
672
|
+
4. **Short-circuit LLM calls** — Skip the LLM for trivial cases (small data, cached results)
|
|
673
|
+
5. **Cap input sizes** — Prevent token overflow by limiting array sizes before sending to LLM
|
|
674
|
+
6. **Test schemas without LLM** — Validate `input_json_schema` and `output_json_schema` in unit tests
|
|
675
|
+
7. **VCR for integration tests** — Record real HTTP interactions, never mock LLM responses by hand
|
|
676
|
+
8. **Trace with spans** — Wrap tool calls in `DSPy::Context.with_span` for observability
|
|
677
|
+
9. **Graceful degradation** — Always rescue LLM errors and return fallback data
|
|
678
|
+
|
|
679
|
+
### Signature Best Practices
|
|
680
|
+
|
|
681
|
+
**Keep description concise** — The signature `description` should state the goal, not the field details:
|
|
682
|
+
|
|
683
|
+
```ruby
|
|
684
|
+
# Good — concise goal
|
|
685
|
+
class ParseOutline < DSPy::Signature
|
|
686
|
+
description 'Extract block-level structure from HTML as a flat list of skeleton sections.'
|
|
687
|
+
|
|
688
|
+
input do
|
|
689
|
+
const :html, String, description: 'Raw HTML to parse'
|
|
690
|
+
end
|
|
691
|
+
|
|
692
|
+
output do
|
|
693
|
+
const :sections, T::Array[Section], description: 'Block elements: headings, paragraphs, code blocks, lists'
|
|
694
|
+
end
|
|
695
|
+
end
|
|
696
|
+
```
|
|
697
|
+
|
|
698
|
+
**Use defaults over nilable arrays** — For OpenAI structured outputs compatibility:
|
|
699
|
+
|
|
700
|
+
```ruby
|
|
701
|
+
# Good — works with OpenAI structured outputs
|
|
702
|
+
class ASTNode < T::Struct
|
|
703
|
+
const :children, T::Array[ASTNode], default: []
|
|
704
|
+
end
|
|
705
|
+
```
|
|
706
|
+
|
|
707
|
+
### Recursive Types with `$defs`
|
|
708
|
+
|
|
709
|
+
DSPy.rb supports recursive types in structured outputs using JSON Schema `$defs`:
|
|
710
|
+
|
|
711
|
+
```ruby
|
|
712
|
+
class TreeNode < T::Struct
|
|
713
|
+
const :value, String
|
|
714
|
+
const :children, T::Array[TreeNode], default: [] # Self-reference
|
|
715
|
+
end
|
|
716
|
+
```
|
|
717
|
+
|
|
718
|
+
The schema generator automatically creates `#/$defs/TreeNode` references for recursive types, compatible with OpenAI and Gemini structured outputs.
|
|
719
|
+
|
|
720
|
+
### Field Descriptions for T::Struct
|
|
721
|
+
|
|
722
|
+
DSPy.rb extends T::Struct to support field-level `description:` kwargs that flow to JSON Schema:
|
|
723
|
+
|
|
724
|
+
```ruby
|
|
725
|
+
class ASTNode < T::Struct
|
|
726
|
+
const :node_type, NodeType, description: 'The type of node (heading, paragraph, etc.)'
|
|
727
|
+
const :text, String, default: "", description: 'Text content of the node'
|
|
728
|
+
const :level, Integer, default: 0 # No description — field is self-explanatory
|
|
729
|
+
const :children, T::Array[ASTNode], default: []
|
|
730
|
+
end
|
|
731
|
+
```
|
|
732
|
+
|
|
733
|
+
**When to use field descriptions**: complex field semantics, enum-like strings, constrained values, nested structs with ambiguous names. **When to skip**: self-explanatory fields like `name`, `id`, `url`, or boolean flags.
|
|
734
|
+
|
|
735
|
+
## Version
|
|
736
|
+
|
|
737
|
+
Current: 0.34.3
|