@lavralabs/lavra 0.7.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/LICENSE +21 -0
- package/README.md +159 -0
- package/bin/install.js +302 -0
- package/bin/plan-export.js +300 -0
- package/bin/plan-view.js +695 -0
- package/install.sh +136 -0
- package/package.json +28 -0
- package/plugins/lavra/.claude-plugin/plugin.json +22 -0
- package/plugins/lavra/.mcp.json +8 -0
- package/plugins/lavra/README.md +125 -0
- package/plugins/lavra/agents/design/design-implementation-reviewer.md +123 -0
- package/plugins/lavra/agents/design/design-iterator.md +219 -0
- package/plugins/lavra/agents/design/figma-design-sync.md +212 -0
- package/plugins/lavra/agents/docs/ankane-readme-writer.md +90 -0
- package/plugins/lavra/agents/research/best-practices-researcher.md +131 -0
- package/plugins/lavra/agents/research/framework-docs-researcher.md +111 -0
- package/plugins/lavra/agents/research/git-history-analyzer.md +73 -0
- package/plugins/lavra/agents/research/learnings-researcher.md +255 -0
- package/plugins/lavra/agents/research/repo-research-analyst.md +157 -0
- package/plugins/lavra/agents/review/agent-native-reviewer.md +274 -0
- package/plugins/lavra/agents/review/architecture-strategist.md +82 -0
- package/plugins/lavra/agents/review/code-simplicity-reviewer.md +109 -0
- package/plugins/lavra/agents/review/data-integrity-guardian.md +89 -0
- package/plugins/lavra/agents/review/data-migration-expert.md +118 -0
- package/plugins/lavra/agents/review/deployment-verification-agent.md +178 -0
- package/plugins/lavra/agents/review/dhh-rails-reviewer.md +94 -0
- package/plugins/lavra/agents/review/goal-verifier.md +109 -0
- package/plugins/lavra/agents/review/julik-frontend-races-reviewer.md +239 -0
- package/plugins/lavra/agents/review/kieran-python-reviewer.md +148 -0
- package/plugins/lavra/agents/review/kieran-rails-reviewer.md +130 -0
- package/plugins/lavra/agents/review/kieran-typescript-reviewer.md +139 -0
- package/plugins/lavra/agents/review/migration-drift-detector.md +307 -0
- package/plugins/lavra/agents/review/pattern-recognition-specialist.md +87 -0
- package/plugins/lavra/agents/review/performance-oracle.md +154 -0
- package/plugins/lavra/agents/review/security-sentinel.md +125 -0
- package/plugins/lavra/agents/workflow/bug-reproduction-validator.md +119 -0
- package/plugins/lavra/agents/workflow/every-style-editor.md +97 -0
- package/plugins/lavra/agents/workflow/lint.md +30 -0
- package/plugins/lavra/agents/workflow/pr-comment-resolver.md +95 -0
- package/plugins/lavra/agents/workflow/spec-flow-analyzer.md +156 -0
- package/plugins/lavra/commands/changelog.md +149 -0
- package/plugins/lavra/commands/heal-skill.md +130 -0
- package/plugins/lavra/commands/lavra-brainstorm.md +388 -0
- package/plugins/lavra/commands/lavra-ceo-review.md +388 -0
- package/plugins/lavra/commands/lavra-checkpoint.md +162 -0
- package/plugins/lavra/commands/lavra-design.md +621 -0
- package/plugins/lavra/commands/lavra-eng-review.md +262 -0
- package/plugins/lavra/commands/lavra-import.md +194 -0
- package/plugins/lavra/commands/lavra-learn.md +176 -0
- package/plugins/lavra/commands/lavra-plan.md +515 -0
- package/plugins/lavra/commands/lavra-qa.md +357 -0
- package/plugins/lavra/commands/lavra-quick.md +178 -0
- package/plugins/lavra/commands/lavra-recall.md +279 -0
- package/plugins/lavra/commands/lavra-research.md +267 -0
- package/plugins/lavra/commands/lavra-retro.md +400 -0
- package/plugins/lavra/commands/lavra-review.md +401 -0
- package/plugins/lavra/commands/lavra-ship.md +330 -0
- package/plugins/lavra/commands/lavra-triage.md +159 -0
- package/plugins/lavra/commands/lavra-work-ralph.md +521 -0
- package/plugins/lavra/commands/lavra-work-teams.md +504 -0
- package/plugins/lavra/commands/lavra-work.md +1019 -0
- package/plugins/lavra/commands/optional/agent-native-audit.md +282 -0
- package/plugins/lavra/commands/optional/feature-video.md +262 -0
- package/plugins/lavra/commands/optional/generate-command.md +146 -0
- package/plugins/lavra/commands/optional/reproduce-bug.md +97 -0
- package/plugins/lavra/commands/optional/xcode-test.md +287 -0
- package/plugins/lavra/commands/report-bug.md +155 -0
- package/plugins/lavra/commands/test-browser.md +305 -0
- package/plugins/lavra/cortex/agents/design/design-implementation-reviewer.md +127 -0
- package/plugins/lavra/cortex/agents/design/design-iterator.md +222 -0
- package/plugins/lavra/cortex/agents/design/figma-design-sync.md +215 -0
- package/plugins/lavra/cortex/agents/docs/ankane-readme-writer.md +93 -0
- package/plugins/lavra/cortex/agents/research/best-practices-researcher.md +135 -0
- package/plugins/lavra/cortex/agents/research/framework-docs-researcher.md +115 -0
- package/plugins/lavra/cortex/agents/research/git-history-analyzer.md +77 -0
- package/plugins/lavra/cortex/agents/research/learnings-researcher.md +259 -0
- package/plugins/lavra/cortex/agents/research/repo-research-analyst.md +161 -0
- package/plugins/lavra/cortex/agents/review/agent-native-reviewer.md +278 -0
- package/plugins/lavra/cortex/agents/review/architecture-strategist.md +86 -0
- package/plugins/lavra/cortex/agents/review/code-simplicity-reviewer.md +113 -0
- package/plugins/lavra/cortex/agents/review/data-integrity-guardian.md +93 -0
- package/plugins/lavra/cortex/agents/review/data-migration-expert.md +122 -0
- package/plugins/lavra/cortex/agents/review/deployment-verification-agent.md +182 -0
- package/plugins/lavra/cortex/agents/review/dhh-rails-reviewer.md +98 -0
- package/plugins/lavra/cortex/agents/review/goal-verifier.md +113 -0
- package/plugins/lavra/cortex/agents/review/julik-frontend-races-reviewer.md +243 -0
- package/plugins/lavra/cortex/agents/review/kieran-python-reviewer.md +152 -0
- package/plugins/lavra/cortex/agents/review/kieran-rails-reviewer.md +134 -0
- package/plugins/lavra/cortex/agents/review/kieran-typescript-reviewer.md +143 -0
- package/plugins/lavra/cortex/agents/review/migration-drift-detector.md +311 -0
- package/plugins/lavra/cortex/agents/review/pattern-recognition-specialist.md +91 -0
- package/plugins/lavra/cortex/agents/review/performance-oracle.md +158 -0
- package/plugins/lavra/cortex/agents/review/security-sentinel.md +129 -0
- package/plugins/lavra/cortex/agents/workflow/bug-reproduction-validator.md +123 -0
- package/plugins/lavra/cortex/agents/workflow/every-style-editor.md +101 -0
- package/plugins/lavra/cortex/agents/workflow/lint.md +33 -0
- package/plugins/lavra/cortex/agents/workflow/pr-comment-resolver.md +98 -0
- package/plugins/lavra/cortex/agents/workflow/spec-flow-analyzer.md +160 -0
- package/plugins/lavra/cortex/commands/agent-native-audit.md +286 -0
- package/plugins/lavra/cortex/commands/changelog.md +153 -0
- package/plugins/lavra/cortex/commands/create-agent-skill.md +21 -0
- package/plugins/lavra/cortex/commands/deploy-docs.md +69 -0
- package/plugins/lavra/cortex/commands/feature-video.md +266 -0
- package/plugins/lavra/cortex/commands/generate-command.md +150 -0
- package/plugins/lavra/cortex/commands/heal-skill.md +134 -0
- package/plugins/lavra/cortex/commands/lavra-brainstorm.md +392 -0
- package/plugins/lavra/cortex/commands/lavra-ceo-review.md +392 -0
- package/plugins/lavra/cortex/commands/lavra-checkpoint.md +166 -0
- package/plugins/lavra/cortex/commands/lavra-compound.md +168 -0
- package/plugins/lavra/cortex/commands/lavra-deepen.md +389 -0
- package/plugins/lavra/cortex/commands/lavra-design.md +625 -0
- package/plugins/lavra/cortex/commands/lavra-eng-review.md +266 -0
- package/plugins/lavra/cortex/commands/lavra-import.md +198 -0
- package/plugins/lavra/cortex/commands/lavra-learn.md +180 -0
- package/plugins/lavra/cortex/commands/lavra-parallel.md +910 -0
- package/plugins/lavra/cortex/commands/lavra-plan.md +519 -0
- package/plugins/lavra/cortex/commands/lavra-qa.md +361 -0
- package/plugins/lavra/cortex/commands/lavra-quick.md +182 -0
- package/plugins/lavra/cortex/commands/lavra-recall.md +283 -0
- package/plugins/lavra/cortex/commands/lavra-research.md +271 -0
- package/plugins/lavra/cortex/commands/lavra-retro.md +404 -0
- package/plugins/lavra/cortex/commands/lavra-review.md +405 -0
- package/plugins/lavra/cortex/commands/lavra-ship.md +334 -0
- package/plugins/lavra/cortex/commands/lavra-triage.md +163 -0
- package/plugins/lavra/cortex/commands/lavra-work-ralph.md +525 -0
- package/plugins/lavra/cortex/commands/lavra-work-teams.md +508 -0
- package/plugins/lavra/cortex/commands/lavra-work.md +1023 -0
- package/plugins/lavra/cortex/commands/lfg.md +30 -0
- package/plugins/lavra/cortex/commands/release-docs.md +148 -0
- package/plugins/lavra/cortex/commands/report-bug.md +159 -0
- package/plugins/lavra/cortex/commands/reproduce-bug.md +101 -0
- package/plugins/lavra/cortex/commands/resolve-pr-parallel.md +58 -0
- package/plugins/lavra/cortex/commands/resolve-todo-parallel.md +56 -0
- package/plugins/lavra/cortex/commands/test-browser.md +309 -0
- package/plugins/lavra/cortex/commands/xcode-test.md +291 -0
- package/plugins/lavra/cortex/skills/agent-browser/SKILL.md +227 -0
- package/plugins/lavra/cortex/skills/agent-native-architecture/SKILL.md +439 -0
- package/plugins/lavra/cortex/skills/andrew-kane-gem-writer/SKILL.md +188 -0
- package/plugins/lavra/cortex/skills/brainstorming/SKILL.md +197 -0
- package/plugins/lavra/cortex/skills/create-agent-skills/SKILL.md +304 -0
- package/plugins/lavra/cortex/skills/dhh-rails-style/SKILL.md +189 -0
- package/plugins/lavra/cortex/skills/dspy-ruby/SKILL.md +741 -0
- package/plugins/lavra/cortex/skills/every-style-editor/SKILL.md +138 -0
- package/plugins/lavra/cortex/skills/file-todos/SKILL.md +256 -0
- package/plugins/lavra/cortex/skills/frontend-design/SKILL.md +46 -0
- package/plugins/lavra/cortex/skills/gemini-imagegen/SKILL.md +242 -0
- package/plugins/lavra/cortex/skills/git-worktree/SKILL.md +307 -0
- package/plugins/lavra/cortex/skills/lavra-knowledge/SKILL.md +464 -0
- package/plugins/lavra/cortex/skills/project-setup/SKILL.md +418 -0
- package/plugins/lavra/cortex/skills/rclone/SKILL.md +155 -0
- package/plugins/lavra/docs/quickstart.md +267 -0
- package/plugins/lavra/examples/example-plan.md +197 -0
- package/plugins/lavra/gemini/agents/design/design-implementation-reviewer.md +130 -0
- package/plugins/lavra/gemini/agents/design/design-iterator.md +225 -0
- package/plugins/lavra/gemini/agents/design/figma-design-sync.md +218 -0
- package/plugins/lavra/gemini/agents/docs/ankane-readme-writer.md +96 -0
- package/plugins/lavra/gemini/agents/research/best-practices-researcher.md +138 -0
- package/plugins/lavra/gemini/agents/research/framework-docs-researcher.md +118 -0
- package/plugins/lavra/gemini/agents/research/git-history-analyzer.md +80 -0
- package/plugins/lavra/gemini/agents/research/learnings-researcher.md +262 -0
- package/plugins/lavra/gemini/agents/research/repo-research-analyst.md +164 -0
- package/plugins/lavra/gemini/agents/review/agent-native-reviewer.md +281 -0
- package/plugins/lavra/gemini/agents/review/architecture-strategist.md +89 -0
- package/plugins/lavra/gemini/agents/review/code-simplicity-reviewer.md +116 -0
- package/plugins/lavra/gemini/agents/review/data-integrity-guardian.md +96 -0
- package/plugins/lavra/gemini/agents/review/data-migration-expert.md +125 -0
- package/plugins/lavra/gemini/agents/review/deployment-verification-agent.md +185 -0
- package/plugins/lavra/gemini/agents/review/dhh-rails-reviewer.md +101 -0
- package/plugins/lavra/gemini/agents/review/goal-verifier.md +116 -0
- package/plugins/lavra/gemini/agents/review/julik-frontend-races-reviewer.md +246 -0
- package/plugins/lavra/gemini/agents/review/kieran-python-reviewer.md +155 -0
- package/plugins/lavra/gemini/agents/review/kieran-rails-reviewer.md +137 -0
- package/plugins/lavra/gemini/agents/review/kieran-typescript-reviewer.md +146 -0
- package/plugins/lavra/gemini/agents/review/migration-drift-detector.md +314 -0
- package/plugins/lavra/gemini/agents/review/pattern-recognition-specialist.md +94 -0
- package/plugins/lavra/gemini/agents/review/performance-oracle.md +161 -0
- package/plugins/lavra/gemini/agents/review/security-sentinel.md +132 -0
- package/plugins/lavra/gemini/agents/workflow/bug-reproduction-validator.md +126 -0
- package/plugins/lavra/gemini/agents/workflow/every-style-editor.md +103 -0
- package/plugins/lavra/gemini/agents/workflow/lint.md +36 -0
- package/plugins/lavra/gemini/agents/workflow/pr-comment-resolver.md +101 -0
- package/plugins/lavra/gemini/agents/workflow/spec-flow-analyzer.md +163 -0
- package/plugins/lavra/gemini/commands/agent-native-audit.toml +284 -0
- package/plugins/lavra/gemini/commands/beads-brainstorm.toml +292 -0
- package/plugins/lavra/gemini/commands/beads-checkpoint.toml +145 -0
- package/plugins/lavra/gemini/commands/beads-compound.toml +167 -0
- package/plugins/lavra/gemini/commands/beads-deepen.toml +388 -0
- package/plugins/lavra/gemini/commands/beads-design.toml +295 -0
- package/plugins/lavra/gemini/commands/beads-import.toml +197 -0
- package/plugins/lavra/gemini/commands/beads-parallel.toml +909 -0
- package/plugins/lavra/gemini/commands/beads-plan-review.toml +201 -0
- package/plugins/lavra/gemini/commands/beads-plan.toml +391 -0
- package/plugins/lavra/gemini/commands/beads-quick.toml +134 -0
- package/plugins/lavra/gemini/commands/beads-recall.toml +281 -0
- package/plugins/lavra/gemini/commands/beads-review.toml +338 -0
- package/plugins/lavra/gemini/commands/beads-triage.toml +161 -0
- package/plugins/lavra/gemini/commands/beads-work.toml +347 -0
- package/plugins/lavra/gemini/commands/changelog.toml +151 -0
- package/plugins/lavra/gemini/commands/create-agent-skill.toml +18 -0
- package/plugins/lavra/gemini/commands/deploy-docs.toml +68 -0
- package/plugins/lavra/gemini/commands/feature-video.toml +264 -0
- package/plugins/lavra/gemini/commands/generate-command.toml +148 -0
- package/plugins/lavra/gemini/commands/heal-skill.toml +131 -0
- package/plugins/lavra/gemini/commands/lavra-brainstorm.toml +391 -0
- package/plugins/lavra/gemini/commands/lavra-ceo-review.toml +391 -0
- package/plugins/lavra/gemini/commands/lavra-checkpoint.toml +165 -0
- package/plugins/lavra/gemini/commands/lavra-design.toml +624 -0
- package/plugins/lavra/gemini/commands/lavra-eng-review.toml +265 -0
- package/plugins/lavra/gemini/commands/lavra-import.toml +197 -0
- package/plugins/lavra/gemini/commands/lavra-learn.toml +179 -0
- package/plugins/lavra/gemini/commands/lavra-plan-review.toml +201 -0
- package/plugins/lavra/gemini/commands/lavra-plan.toml +518 -0
- package/plugins/lavra/gemini/commands/lavra-qa.toml +360 -0
- package/plugins/lavra/gemini/commands/lavra-quick.toml +181 -0
- package/plugins/lavra/gemini/commands/lavra-recall.toml +281 -0
- package/plugins/lavra/gemini/commands/lavra-research.toml +270 -0
- package/plugins/lavra/gemini/commands/lavra-retro.toml +403 -0
- package/plugins/lavra/gemini/commands/lavra-review.toml +404 -0
- package/plugins/lavra/gemini/commands/lavra-ship.toml +333 -0
- package/plugins/lavra/gemini/commands/lavra-triage.toml +161 -0
- package/plugins/lavra/gemini/commands/lavra-work-ralph.toml +523 -0
- package/plugins/lavra/gemini/commands/lavra-work-teams.toml +507 -0
- package/plugins/lavra/gemini/commands/lavra-work.toml +1022 -0
- package/plugins/lavra/gemini/commands/lfg.toml +28 -0
- package/plugins/lavra/gemini/commands/release-docs.toml +146 -0
- package/plugins/lavra/gemini/commands/report-bug.toml +157 -0
- package/plugins/lavra/gemini/commands/reproduce-bug.toml +99 -0
- package/plugins/lavra/gemini/commands/resolve-pr-parallel.toml +56 -0
- package/plugins/lavra/gemini/commands/resolve-todo-parallel.toml +54 -0
- package/plugins/lavra/gemini/commands/test-browser.toml +307 -0
- package/plugins/lavra/gemini/commands/xcode-test.toml +289 -0
- package/plugins/lavra/gemini/docs/MCP_SETUP.md +41 -0
- package/plugins/lavra/gemini/skills/agent-browser/SKILL.md +227 -0
- package/plugins/lavra/gemini/skills/agent-native-architecture/SKILL.md +439 -0
- package/plugins/lavra/gemini/skills/andrew-kane-gem-writer/SKILL.md +188 -0
- package/plugins/lavra/gemini/skills/beads-knowledge/SKILL.md +464 -0
- package/plugins/lavra/gemini/skills/brainstorming/SKILL.md +197 -0
- package/plugins/lavra/gemini/skills/create-agent-skills/SKILL.md +304 -0
- package/plugins/lavra/gemini/skills/dhh-rails-style/SKILL.md +189 -0
- package/plugins/lavra/gemini/skills/dspy-ruby/SKILL.md +741 -0
- package/plugins/lavra/gemini/skills/every-style-editor/SKILL.md +138 -0
- package/plugins/lavra/gemini/skills/file-todos/SKILL.md +256 -0
- package/plugins/lavra/gemini/skills/frontend-design/SKILL.md +46 -0
- package/plugins/lavra/gemini/skills/gemini-imagegen/SKILL.md +242 -0
- package/plugins/lavra/gemini/skills/git-worktree/SKILL.md +307 -0
- package/plugins/lavra/gemini/skills/lavra-knowledge/SKILL.md +464 -0
- package/plugins/lavra/gemini/skills/project-setup/SKILL.md +418 -0
- package/plugins/lavra/gemini/skills/rclone/SKILL.md +155 -0
- package/plugins/lavra/gemini-extension.json +50 -0
- package/plugins/lavra/gemini-src/settings.json +37 -0
- package/plugins/lavra/hooks/auto-recall.sh +210 -0
- package/plugins/lavra/hooks/check-memory.sh +169 -0
- package/plugins/lavra/hooks/hooks.json +47 -0
- package/plugins/lavra/hooks/knowledge-db.sh +255 -0
- package/plugins/lavra/hooks/memory-capture.sh +132 -0
- package/plugins/lavra/hooks/provision-memory.sh +144 -0
- package/plugins/lavra/hooks/recall.sh +152 -0
- package/plugins/lavra/hooks/subagent-wrapup.sh +44 -0
- package/plugins/lavra/hooks/teammate-idle-check.sh +29 -0
- package/plugins/lavra/opencode/agents/design/design-implementation-reviewer.md +127 -0
- package/plugins/lavra/opencode/agents/design/design-iterator.md +222 -0
- package/plugins/lavra/opencode/agents/design/figma-design-sync.md +215 -0
- package/plugins/lavra/opencode/agents/docs/ankane-readme-writer.md +93 -0
- package/plugins/lavra/opencode/agents/research/best-practices-researcher.md +135 -0
- package/plugins/lavra/opencode/agents/research/framework-docs-researcher.md +115 -0
- package/plugins/lavra/opencode/agents/research/git-history-analyzer.md +77 -0
- package/plugins/lavra/opencode/agents/research/learnings-researcher.md +259 -0
- package/plugins/lavra/opencode/agents/research/repo-research-analyst.md +161 -0
- package/plugins/lavra/opencode/agents/review/agent-native-reviewer.md +278 -0
- package/plugins/lavra/opencode/agents/review/architecture-strategist.md +86 -0
- package/plugins/lavra/opencode/agents/review/code-simplicity-reviewer.md +113 -0
- package/plugins/lavra/opencode/agents/review/data-integrity-guardian.md +93 -0
- package/plugins/lavra/opencode/agents/review/data-migration-expert.md +122 -0
- package/plugins/lavra/opencode/agents/review/deployment-verification-agent.md +182 -0
- package/plugins/lavra/opencode/agents/review/dhh-rails-reviewer.md +98 -0
- package/plugins/lavra/opencode/agents/review/goal-verifier.md +113 -0
- package/plugins/lavra/opencode/agents/review/julik-frontend-races-reviewer.md +243 -0
- package/plugins/lavra/opencode/agents/review/kieran-python-reviewer.md +152 -0
- package/plugins/lavra/opencode/agents/review/kieran-rails-reviewer.md +134 -0
- package/plugins/lavra/opencode/agents/review/kieran-typescript-reviewer.md +143 -0
- package/plugins/lavra/opencode/agents/review/migration-drift-detector.md +311 -0
- package/plugins/lavra/opencode/agents/review/pattern-recognition-specialist.md +91 -0
- package/plugins/lavra/opencode/agents/review/performance-oracle.md +158 -0
- package/plugins/lavra/opencode/agents/review/security-sentinel.md +129 -0
- package/plugins/lavra/opencode/agents/workflow/bug-reproduction-validator.md +123 -0
- package/plugins/lavra/opencode/agents/workflow/every-style-editor.md +100 -0
- package/plugins/lavra/opencode/agents/workflow/lint.md +33 -0
- package/plugins/lavra/opencode/agents/workflow/pr-comment-resolver.md +98 -0
- package/plugins/lavra/opencode/agents/workflow/spec-flow-analyzer.md +160 -0
- package/plugins/lavra/opencode/commands/agent-native-audit.md +286 -0
- package/plugins/lavra/opencode/commands/changelog.md +153 -0
- package/plugins/lavra/opencode/commands/create-agent-skill.md +21 -0
- package/plugins/lavra/opencode/commands/deploy-docs.md +69 -0
- package/plugins/lavra/opencode/commands/feature-video.md +266 -0
- package/plugins/lavra/opencode/commands/generate-command.md +150 -0
- package/plugins/lavra/opencode/commands/heal-skill.md +134 -0
- package/plugins/lavra/opencode/commands/lavra-brainstorm.md +392 -0
- package/plugins/lavra/opencode/commands/lavra-ceo-review.md +392 -0
- package/plugins/lavra/opencode/commands/lavra-checkpoint.md +166 -0
- package/plugins/lavra/opencode/commands/lavra-compound.md +168 -0
- package/plugins/lavra/opencode/commands/lavra-deepen.md +389 -0
- package/plugins/lavra/opencode/commands/lavra-design.md +625 -0
- package/plugins/lavra/opencode/commands/lavra-eng-review.md +266 -0
- package/plugins/lavra/opencode/commands/lavra-import.md +198 -0
- package/plugins/lavra/opencode/commands/lavra-learn.md +180 -0
- package/plugins/lavra/opencode/commands/lavra-parallel.md +910 -0
- package/plugins/lavra/opencode/commands/lavra-plan.md +519 -0
- package/plugins/lavra/opencode/commands/lavra-qa.md +361 -0
- package/plugins/lavra/opencode/commands/lavra-quick.md +182 -0
- package/plugins/lavra/opencode/commands/lavra-recall.md +283 -0
- package/plugins/lavra/opencode/commands/lavra-research.md +271 -0
- package/plugins/lavra/opencode/commands/lavra-retro.md +404 -0
- package/plugins/lavra/opencode/commands/lavra-review.md +405 -0
- package/plugins/lavra/opencode/commands/lavra-ship.md +334 -0
- package/plugins/lavra/opencode/commands/lavra-triage.md +163 -0
- package/plugins/lavra/opencode/commands/lavra-work-ralph.md +525 -0
- package/plugins/lavra/opencode/commands/lavra-work-teams.md +508 -0
- package/plugins/lavra/opencode/commands/lavra-work.md +1023 -0
- package/plugins/lavra/opencode/commands/lfg.md +30 -0
- package/plugins/lavra/opencode/commands/release-docs.md +148 -0
- package/plugins/lavra/opencode/commands/report-bug.md +159 -0
- package/plugins/lavra/opencode/commands/reproduce-bug.md +101 -0
- package/plugins/lavra/opencode/commands/resolve-pr-parallel.md +58 -0
- package/plugins/lavra/opencode/commands/resolve-todo-parallel.md +56 -0
- package/plugins/lavra/opencode/commands/test-browser.md +309 -0
- package/plugins/lavra/opencode/commands/xcode-test.md +291 -0
- package/plugins/lavra/opencode/docs/MCP_SETUP.md +48 -0
- package/plugins/lavra/opencode/skills/agent-browser/SKILL.md +227 -0
- package/plugins/lavra/opencode/skills/agent-native-architecture/SKILL.md +439 -0
- package/plugins/lavra/opencode/skills/andrew-kane-gem-writer/SKILL.md +188 -0
- package/plugins/lavra/opencode/skills/brainstorming/SKILL.md +197 -0
- package/plugins/lavra/opencode/skills/create-agent-skills/SKILL.md +304 -0
- package/plugins/lavra/opencode/skills/dhh-rails-style/SKILL.md +189 -0
- package/plugins/lavra/opencode/skills/dspy-ruby/SKILL.md +741 -0
- package/plugins/lavra/opencode/skills/every-style-editor/SKILL.md +138 -0
- package/plugins/lavra/opencode/skills/file-todos/SKILL.md +256 -0
- package/plugins/lavra/opencode/skills/frontend-design/SKILL.md +46 -0
- package/plugins/lavra/opencode/skills/gemini-imagegen/SKILL.md +242 -0
- package/plugins/lavra/opencode/skills/git-worktree/SKILL.md +307 -0
- package/plugins/lavra/opencode/skills/lavra-knowledge/SKILL.md +464 -0
- package/plugins/lavra/opencode/skills/project-setup/SKILL.md +418 -0
- package/plugins/lavra/opencode/skills/rclone/SKILL.md +155 -0
- package/plugins/lavra/opencode-src/package.json +13 -0
- package/plugins/lavra/opencode-src/plugin.ts +176 -0
- package/plugins/lavra/scripts/import-plan.sh +141 -0
- package/plugins/lavra/skills/agent-browser/SKILL.md +223 -0
- package/plugins/lavra/skills/agent-native-architecture/SKILL.md +435 -0
- package/plugins/lavra/skills/agent-native-architecture/references/action-parity-discipline.md +353 -0
- package/plugins/lavra/skills/agent-native-architecture/references/agent-execution-patterns.md +362 -0
- package/plugins/lavra/skills/agent-native-architecture/references/agent-native-testing.md +508 -0
- package/plugins/lavra/skills/agent-native-architecture/references/architecture-patterns.md +478 -0
- package/plugins/lavra/skills/agent-native-architecture/references/dynamic-context-injection.md +281 -0
- package/plugins/lavra/skills/agent-native-architecture/references/files-universal-interface.md +301 -0
- package/plugins/lavra/skills/agent-native-architecture/references/from-primitives-to-domain-tools.md +227 -0
- package/plugins/lavra/skills/agent-native-architecture/references/mcp-tool-design.md +427 -0
- package/plugins/lavra/skills/agent-native-architecture/references/mobile-patterns.md +410 -0
- package/plugins/lavra/skills/agent-native-architecture/references/product-implications.md +341 -0
- package/plugins/lavra/skills/agent-native-architecture/references/refactoring-to-prompt-native.md +317 -0
- package/plugins/lavra/skills/agent-native-architecture/references/self-modification.md +269 -0
- package/plugins/lavra/skills/agent-native-architecture/references/shared-workspace-architecture.md +517 -0
- package/plugins/lavra/skills/agent-native-architecture/references/system-prompt-design.md +250 -0
- package/plugins/lavra/skills/brainstorming/SKILL.md +193 -0
- package/plugins/lavra/skills/create-agent-skills/SKILL.md +300 -0
- package/plugins/lavra/skills/create-agent-skills/references/api-security.md +60 -0
- package/plugins/lavra/skills/create-agent-skills/references/be-clear-and-direct.md +84 -0
- package/plugins/lavra/skills/create-agent-skills/references/best-practices.md +404 -0
- package/plugins/lavra/skills/create-agent-skills/references/common-patterns.md +121 -0
- package/plugins/lavra/skills/create-agent-skills/references/core-principles.md +103 -0
- package/plugins/lavra/skills/create-agent-skills/references/executable-code.md +92 -0
- package/plugins/lavra/skills/create-agent-skills/references/iteration-and-testing.md +164 -0
- package/plugins/lavra/skills/create-agent-skills/references/official-spec.md +185 -0
- package/plugins/lavra/skills/create-agent-skills/references/recommended-structure.md +168 -0
- package/plugins/lavra/skills/create-agent-skills/references/skill-structure.md +215 -0
- package/plugins/lavra/skills/create-agent-skills/references/using-scripts.md +113 -0
- package/plugins/lavra/skills/create-agent-skills/references/using-templates.md +112 -0
- package/plugins/lavra/skills/create-agent-skills/references/workflows-and-validation.md +122 -0
- package/plugins/lavra/skills/create-agent-skills/templates/router-skill.md +73 -0
- package/plugins/lavra/skills/create-agent-skills/templates/simple-skill.md +33 -0
- package/plugins/lavra/skills/create-agent-skills/workflows/add-reference.md +55 -0
- package/plugins/lavra/skills/create-agent-skills/workflows/add-script.md +59 -0
- package/plugins/lavra/skills/create-agent-skills/workflows/add-template.md +51 -0
- package/plugins/lavra/skills/create-agent-skills/workflows/add-workflow.md +54 -0
- package/plugins/lavra/skills/create-agent-skills/workflows/audit-skill.md +63 -0
- package/plugins/lavra/skills/create-agent-skills/workflows/create-domain-expertise-skill.md +68 -0
- package/plugins/lavra/skills/create-agent-skills/workflows/create-new-skill.md +92 -0
- package/plugins/lavra/skills/create-agent-skills/workflows/get-guidance.md +70 -0
- package/plugins/lavra/skills/create-agent-skills/workflows/upgrade-to-router.md +68 -0
- package/plugins/lavra/skills/create-agent-skills/workflows/verify-skill.md +63 -0
- package/plugins/lavra/skills/file-todos/SKILL.md +252 -0
- package/plugins/lavra/skills/file-todos/assets/todo-template.md +155 -0
- package/plugins/lavra/skills/git-worktree/SKILL.md +303 -0
- package/plugins/lavra/skills/git-worktree/scripts/worktree-manager.sh +345 -0
- package/plugins/lavra/skills/lavra-knowledge/SKILL.md +460 -0
- package/plugins/lavra/skills/lavra-knowledge/references/jsonl-schema.md +104 -0
- package/plugins/lavra/skills/optional/andrew-kane-gem-writer/SKILL.md +184 -0
- package/plugins/lavra/skills/optional/andrew-kane-gem-writer/references/database-adapters.md +231 -0
- package/plugins/lavra/skills/optional/andrew-kane-gem-writer/references/module-organization.md +121 -0
- package/plugins/lavra/skills/optional/andrew-kane-gem-writer/references/rails-integration.md +183 -0
- package/plugins/lavra/skills/optional/andrew-kane-gem-writer/references/resources.md +119 -0
- package/plugins/lavra/skills/optional/andrew-kane-gem-writer/references/testing-patterns.md +261 -0
- package/plugins/lavra/skills/optional/dhh-rails-style/SKILL.md +185 -0
- package/plugins/lavra/skills/optional/dhh-rails-style/references/architecture.md +653 -0
- package/plugins/lavra/skills/optional/dhh-rails-style/references/controllers.md +303 -0
- package/plugins/lavra/skills/optional/dhh-rails-style/references/frontend.md +510 -0
- package/plugins/lavra/skills/optional/dhh-rails-style/references/gems.md +266 -0
- package/plugins/lavra/skills/optional/dhh-rails-style/references/models.md +359 -0
- package/plugins/lavra/skills/optional/dhh-rails-style/references/testing.md +338 -0
- package/plugins/lavra/skills/optional/dspy-ruby/SKILL.md +737 -0
- package/plugins/lavra/skills/optional/dspy-ruby/assets/config-template.rb +187 -0
- package/plugins/lavra/skills/optional/dspy-ruby/assets/module-template.rb +300 -0
- package/plugins/lavra/skills/optional/dspy-ruby/assets/signature-template.rb +221 -0
- package/plugins/lavra/skills/optional/dspy-ruby/references/core-concepts.md +674 -0
- package/plugins/lavra/skills/optional/dspy-ruby/references/observability.md +366 -0
- package/plugins/lavra/skills/optional/dspy-ruby/references/optimization.md +603 -0
- package/plugins/lavra/skills/optional/dspy-ruby/references/providers.md +418 -0
- package/plugins/lavra/skills/optional/dspy-ruby/references/toolsets.md +502 -0
- package/plugins/lavra/skills/optional/every-style-editor/SKILL.md +134 -0
- package/plugins/lavra/skills/optional/every-style-editor/references/EVERY_WRITE_STYLE.md +529 -0
- package/plugins/lavra/skills/optional/frontend-design/SKILL.md +42 -0
- package/plugins/lavra/skills/optional/gemini-imagegen/SKILL.md +238 -0
- package/plugins/lavra/skills/optional/gemini-imagegen/requirements.txt +2 -0
- package/plugins/lavra/skills/optional/gemini-imagegen/scripts/compose_images.py +157 -0
- package/plugins/lavra/skills/optional/gemini-imagegen/scripts/edit_image.py +144 -0
- package/plugins/lavra/skills/optional/gemini-imagegen/scripts/gemini_images.py +263 -0
- package/plugins/lavra/skills/optional/gemini-imagegen/scripts/generate_image.py +133 -0
- package/plugins/lavra/skills/optional/gemini-imagegen/scripts/multi_turn_chat.py +216 -0
- package/plugins/lavra/skills/optional/rclone/SKILL.md +151 -0
- package/plugins/lavra/skills/optional/rclone/scripts/check_setup.sh +60 -0
- package/plugins/lavra/skills/project-setup/SKILL.md +414 -0
- package/plugins/lavra/tests/build-index.sh +116 -0
- package/plugins/lavra/tests/recall-bench.sh +224 -0
- package/plugins/lavra/tests/search-fts5.sh +65 -0
- package/plugins/lavra/tests/search-grep.sh +54 -0
- package/plugins/lavra/tests/test-queries.jsonl +25 -0
- package/scripts/apply-context-optimizations.py +345 -0
- package/scripts/convert-cortex.ts +257 -0
- package/scripts/convert-gemini.ts +369 -0
- package/scripts/convert-opencode.ts +313 -0
- package/scripts/package.json +27 -0
- package/scripts/pre-release-check.sh +176 -0
- package/scripts/select-opencode-models.sh +178 -0
- package/scripts/shared/model-config.json +17 -0
- package/scripts/shared/model-mapping.ts +129 -0
- package/scripts/shared/security.ts +97 -0
- package/scripts/shared/yaml-parser.ts +55 -0
- package/scripts/sqlite-to-jsonl.py +207 -0
- package/scripts/test-compatibility.ts +539 -0
- package/scripts/test-features.sh +342 -0
- package/scripts/test-installation.sh +514 -0
- package/scripts/test-security.ts +275 -0
- package/scripts/trim-agent-descriptions.py +177 -0
- package/uninstall.sh +133 -0
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Pre-release checks that mirror the CI verify-release job.
|
|
3
|
+
# Run this before tagging a release to catch failures locally.
|
|
4
|
+
#
|
|
5
|
+
# SYNC: This script must stay in sync with the `verify-release` job in
|
|
6
|
+
# .github/workflows/test-installation.yml. When modifying either file,
|
|
7
|
+
# update the other to match.
|
|
8
|
+
set -euo pipefail
|
|
9
|
+
|
|
10
|
+
REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
|
11
|
+
cd "$REPO_ROOT"
|
|
12
|
+
|
|
13
|
+
PASS=0
|
|
14
|
+
FAIL=0
|
|
15
|
+
|
|
16
|
+
check() {
|
|
17
|
+
local label="$1"
|
|
18
|
+
shift
|
|
19
|
+
if "$@" 2>/dev/null; then
|
|
20
|
+
echo " PASS $label"
|
|
21
|
+
((PASS++)) || true
|
|
22
|
+
else
|
|
23
|
+
echo " FAIL $label"
|
|
24
|
+
((FAIL++)) || true
|
|
25
|
+
fi
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
fail() {
|
|
29
|
+
local label="$1"
|
|
30
|
+
local msg="$2"
|
|
31
|
+
echo " FAIL $label: $msg"
|
|
32
|
+
((FAIL++)) || true
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
echo ""
|
|
36
|
+
echo "=== Version consistency ==="
|
|
37
|
+
|
|
38
|
+
PLUGIN_VERSION=$(jq -r '.version' plugins/lavra/.claude-plugin/plugin.json)
|
|
39
|
+
MARKETPLACE_VERSION=$(jq -r '.plugins[] | select(.name == "lavra") | .version' .claude-plugin/marketplace.json)
|
|
40
|
+
|
|
41
|
+
echo " plugin.json: $PLUGIN_VERSION"
|
|
42
|
+
echo " marketplace.json: $MARKETPLACE_VERSION"
|
|
43
|
+
|
|
44
|
+
if [[ "$PLUGIN_VERSION" == "$MARKETPLACE_VERSION" ]]; then
|
|
45
|
+
echo " PASS Versions match"
|
|
46
|
+
((PASS++)) || true
|
|
47
|
+
else
|
|
48
|
+
fail "Version mismatch" "plugin.json=$PLUGIN_VERSION marketplace.json=$MARKETPLACE_VERSION"
|
|
49
|
+
fi
|
|
50
|
+
|
|
51
|
+
echo ""
|
|
52
|
+
echo "=== Hook version constants ==="
|
|
53
|
+
|
|
54
|
+
HOOK_VERSION=$(grep 'LAVRA_VERSION=' plugins/lavra/hooks/auto-recall.sh | grep -oE '[0-9]+\.[0-9]+\.[0-9]+')
|
|
55
|
+
PROVISION_VERSION=$(grep 'echo "' plugins/lavra/hooks/provision-memory.sh | grep -oE '[0-9]+\.[0-9]+\.[0-9]+' | head -1)
|
|
56
|
+
|
|
57
|
+
echo " auto-recall.sh: $HOOK_VERSION"
|
|
58
|
+
echo " provision-memory.sh: $PROVISION_VERSION"
|
|
59
|
+
|
|
60
|
+
[[ "$HOOK_VERSION" == "$PLUGIN_VERSION" ]] && { echo " PASS auto-recall.sh version"; ((PASS++)) || true; } || fail "auto-recall.sh version" "has $HOOK_VERSION, expected $PLUGIN_VERSION"
|
|
61
|
+
[[ "$PROVISION_VERSION" == "$PLUGIN_VERSION" ]] && { echo " PASS provision-memory.sh version"; ((PASS++)) || true; } || fail "provision-memory.sh version" "has $PROVISION_VERSION, expected $PLUGIN_VERSION"
|
|
62
|
+
|
|
63
|
+
echo ""
|
|
64
|
+
echo "=== Conversion outputs ==="
|
|
65
|
+
echo " Generating OpenCode and Gemini outputs..."
|
|
66
|
+
(cd scripts && bun install --frozen-lockfile --silent && bun run convert-opencode.ts && bun run convert-gemini.ts && bun run convert-cortex.ts) || {
|
|
67
|
+
fail "Conversion scripts" "bun run failed"
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
echo ""
|
|
71
|
+
echo "=== Component counts ==="
|
|
72
|
+
|
|
73
|
+
COMMANDS=$(find plugins/lavra/commands -maxdepth 1 -name "*.md" | wc -l | tr -d ' ')
|
|
74
|
+
OPTIONAL_COMMANDS=$(find plugins/lavra/commands/optional -name "*.md" 2>/dev/null | wc -l | tr -d ' ')
|
|
75
|
+
AGENTS=$(find plugins/lavra/agents -name "*.md" | wc -l | tr -d ' ')
|
|
76
|
+
SKILLS=$(find plugins/lavra/skills -name "SKILL.md" | wc -l | tr -d ' ')
|
|
77
|
+
|
|
78
|
+
echo " Commands: $COMMANDS (need 23+) + $OPTIONAL_COMMANDS optional"
|
|
79
|
+
echo " Agents: $AGENTS (need 30+)"
|
|
80
|
+
echo " Skills: $SKILLS (need 15+)"
|
|
81
|
+
|
|
82
|
+
[[ "$COMMANDS" -ge 23 ]] && { echo " PASS Commands"; ((PASS++)) || true; } || fail "Commands" "$COMMANDS < 23"
|
|
83
|
+
[[ "$AGENTS" -ge 30 ]] && { echo " PASS Agents"; ((PASS++)) || true; } || fail "Agents" "$AGENTS < 30"
|
|
84
|
+
[[ "$SKILLS" -ge 15 ]] && { echo " PASS Skills"; ((PASS++)) || true; } || fail "Skills" "$SKILLS < 15"
|
|
85
|
+
|
|
86
|
+
echo ""
|
|
87
|
+
echo "=== Source files ==="
|
|
88
|
+
|
|
89
|
+
check "opencode-src/plugin.ts" test -f plugins/lavra/opencode-src/plugin.ts
|
|
90
|
+
check "opencode-src/package.json" test -f plugins/lavra/opencode-src/package.json
|
|
91
|
+
check "gemini-src/settings.json" test -f plugins/lavra/gemini-src/settings.json
|
|
92
|
+
|
|
93
|
+
echo ""
|
|
94
|
+
echo "=== Conversion output files ==="
|
|
95
|
+
|
|
96
|
+
check "opencode/ directory" test -d plugins/lavra/opencode
|
|
97
|
+
check "gemini/ directory" test -d plugins/lavra/gemini
|
|
98
|
+
check "cortex/ directory" test -d plugins/lavra/cortex
|
|
99
|
+
|
|
100
|
+
OPENCODE_COMMANDS=$(find plugins/lavra/opencode/commands -name "*.md" 2>/dev/null | wc -l | tr -d ' ')
|
|
101
|
+
GEMINI_TOML=$(find plugins/lavra/gemini/commands -name "*.toml" 2>/dev/null | wc -l | tr -d ' ')
|
|
102
|
+
|
|
103
|
+
echo " OpenCode .md commands: $OPENCODE_COMMANDS (need 23+)"
|
|
104
|
+
echo " Gemini .toml commands: $GEMINI_TOML (need 23+)"
|
|
105
|
+
|
|
106
|
+
[[ "$OPENCODE_COMMANDS" -ge 23 ]] && { echo " PASS OpenCode commands"; ((PASS++)) || true; } || fail "OpenCode commands" "$OPENCODE_COMMANDS < 23"
|
|
107
|
+
[[ "$GEMINI_TOML" -ge 23 ]] && { echo " PASS Gemini commands"; ((PASS++)) || true; } || fail "Gemini commands" "$GEMINI_TOML < 23"
|
|
108
|
+
|
|
109
|
+
CORTEX_COMMANDS=$(find plugins/lavra/cortex/commands -name "*.md" 2>/dev/null | wc -l | tr -d ' ')
|
|
110
|
+
echo " Cortex .md commands: $CORTEX_COMMANDS (need 20+)"
|
|
111
|
+
[[ "$CORTEX_COMMANDS" -ge 20 ]] && { echo " PASS Cortex commands"; ((PASS++)) || true; } || fail "Cortex commands" "$CORTEX_COMMANDS < 20"
|
|
112
|
+
|
|
113
|
+
echo ""
|
|
114
|
+
echo "=== Command map ==="
|
|
115
|
+
|
|
116
|
+
check "site/public/command-map.html exists" test -f site/public/command-map.html
|
|
117
|
+
|
|
118
|
+
# Verify command map references current node counts (spot check: NODES array has entries)
|
|
119
|
+
MAP_NODES=$(grep -c "id:'" site/public/command-map.html || echo 0)
|
|
120
|
+
echo " Command map nodes: $MAP_NODES (need 40+)"
|
|
121
|
+
[[ "$MAP_NODES" -ge 40 ]] && { echo " PASS Command map nodes"; ((PASS++)) || true; } || fail "Command map nodes" "$MAP_NODES < 40"
|
|
122
|
+
|
|
123
|
+
echo ""
|
|
124
|
+
echo "=== Catalog accuracy ==="
|
|
125
|
+
# SYNC: This section must stay in sync with the verify-docs step in .github/workflows/test-installation.yml.
|
|
126
|
+
# When modifying either, update the other.
|
|
127
|
+
|
|
128
|
+
# Extract command names from CATALOG.md table rows only (lines starting with |)
|
|
129
|
+
# to avoid matching prose mentions like "Included in `/lavra-design`"
|
|
130
|
+
CATALOG_COMMANDS=$(grep -E '^\|' site/src/content/docs/CATALOG.md | \
|
|
131
|
+
grep -oE '`/[a-z][a-z0-9-]+`' | tr -d '`' | sed 's|^/||' | sort -u)
|
|
132
|
+
|
|
133
|
+
# Actual command files (core + optional, recurse into subdirs)
|
|
134
|
+
ACTUAL_COMMANDS=$(find plugins/lavra/commands -name "*.md" | \
|
|
135
|
+
xargs -I{} basename {} .md 2>/dev/null | sort -u)
|
|
136
|
+
|
|
137
|
+
GHOST_COUNT=0
|
|
138
|
+
MISSING_COUNT=0
|
|
139
|
+
|
|
140
|
+
# Check for ghost commands (in catalog, no file exists)
|
|
141
|
+
while IFS= read -r cmd; do
|
|
142
|
+
if ! echo "$ACTUAL_COMMANDS" | grep -qx "$cmd"; then
|
|
143
|
+
fail "Catalog ghost" "/${cmd} listed in CATALOG.md but no ${cmd}.md file found"
|
|
144
|
+
((GHOST_COUNT++)) || true
|
|
145
|
+
fi
|
|
146
|
+
done <<< "$CATALOG_COMMANDS"
|
|
147
|
+
|
|
148
|
+
# Check for missing catalog entries (file exists, not in catalog)
|
|
149
|
+
while IFS= read -r cmd; do
|
|
150
|
+
if ! echo "$CATALOG_COMMANDS" | grep -qx "$cmd"; then
|
|
151
|
+
fail "Missing from catalog" "${cmd}.md exists but /${cmd} not in CATALOG.md"
|
|
152
|
+
((MISSING_COUNT++)) || true
|
|
153
|
+
fi
|
|
154
|
+
done <<< "$ACTUAL_COMMANDS"
|
|
155
|
+
|
|
156
|
+
if [[ "$GHOST_COUNT" -eq 0 && "$MISSING_COUNT" -eq 0 ]]; then
|
|
157
|
+
echo " PASS Catalog accuracy (no ghosts, no missing entries)"
|
|
158
|
+
((PASS++)) || true
|
|
159
|
+
fi
|
|
160
|
+
|
|
161
|
+
echo ""
|
|
162
|
+
echo "=== Compatibility tests ==="
|
|
163
|
+
(cd scripts && bun run test-compatibility.ts) && { echo " PASS Compatibility tests"; ((PASS++)) || true; } || fail "Compatibility tests" "see output above"
|
|
164
|
+
|
|
165
|
+
echo ""
|
|
166
|
+
echo "================================"
|
|
167
|
+
echo " $PASS passed, $FAIL failed"
|
|
168
|
+
echo "================================"
|
|
169
|
+
echo ""
|
|
170
|
+
|
|
171
|
+
if [[ "$FAIL" -gt 0 ]]; then
|
|
172
|
+
echo "Fix the failures above before releasing."
|
|
173
|
+
exit 1
|
|
174
|
+
fi
|
|
175
|
+
|
|
176
|
+
echo "All checks passed. Safe to release."
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
#
|
|
3
|
+
# Interactive model selection for OpenCode installation
|
|
4
|
+
# Queries available models via 'opencode models' and lets user select preferences
|
|
5
|
+
#
|
|
6
|
+
# Usage:
|
|
7
|
+
# ./select-opencode-models.sh [--yes]
|
|
8
|
+
#
|
|
9
|
+
# With --yes: Uses defaults without prompting
|
|
10
|
+
#
|
|
11
|
+
|
|
12
|
+
set -euo pipefail
|
|
13
|
+
|
|
14
|
+
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
15
|
+
CONFIG_FILE="$SCRIPT_DIR/shared/model-config.json"
|
|
16
|
+
|
|
17
|
+
# Parse --yes flag
|
|
18
|
+
AUTO_YES=false
|
|
19
|
+
for arg in "$@"; do
|
|
20
|
+
case "$arg" in
|
|
21
|
+
--yes|-y) AUTO_YES=true ;;
|
|
22
|
+
esac
|
|
23
|
+
done
|
|
24
|
+
|
|
25
|
+
# Check if opencode is available
|
|
26
|
+
if ! command -v opencode &>/dev/null; then
|
|
27
|
+
echo "[!] Error: 'opencode' command not found"
|
|
28
|
+
echo " OpenCode must be installed to query available models"
|
|
29
|
+
echo " Visit: https://github.com/smallcloudai/refact"
|
|
30
|
+
exit 1
|
|
31
|
+
fi
|
|
32
|
+
|
|
33
|
+
# Query available models
|
|
34
|
+
echo "🔍 Querying available models from OpenCode..."
|
|
35
|
+
MODELS_OUTPUT=$(opencode models 2>&1 || true)
|
|
36
|
+
|
|
37
|
+
# Extract Anthropic/Claude models
|
|
38
|
+
CLAUDE_MODELS=$(echo "$MODELS_OUTPUT" | grep -E "anthropic/claude-" | awk '{print $1}' | sort -u || true)
|
|
39
|
+
|
|
40
|
+
if [ -z "$CLAUDE_MODELS" ]; then
|
|
41
|
+
echo "[!] Warning: No Anthropic/Claude models found in OpenCode"
|
|
42
|
+
echo " Using defaults from model-config.json"
|
|
43
|
+
exit 0
|
|
44
|
+
fi
|
|
45
|
+
|
|
46
|
+
echo ""
|
|
47
|
+
echo "Found $(echo "$CLAUDE_MODELS" | wc -l | tr -d ' ') Claude models:"
|
|
48
|
+
echo "$CLAUDE_MODELS" | sed 's/^/ - /'
|
|
49
|
+
echo ""
|
|
50
|
+
|
|
51
|
+
# Load current config
|
|
52
|
+
if [ -f "$CONFIG_FILE" ]; then
|
|
53
|
+
CURRENT_HAIKU=$(jq -r '.opencode.haiku' "$CONFIG_FILE")
|
|
54
|
+
CURRENT_SONNET=$(jq -r '.opencode.sonnet' "$CONFIG_FILE")
|
|
55
|
+
CURRENT_OPUS=$(jq -r '.opencode.opus' "$CONFIG_FILE")
|
|
56
|
+
else
|
|
57
|
+
CURRENT_HAIKU="anthropic/claude-haiku-4-5-20251001"
|
|
58
|
+
CURRENT_SONNET="anthropic/claude-sonnet-4-5-20250929"
|
|
59
|
+
CURRENT_OPUS="anthropic/claude-opus-4-6"
|
|
60
|
+
fi
|
|
61
|
+
|
|
62
|
+
# If --yes flag, use current config
|
|
63
|
+
if [ "$AUTO_YES" = true ]; then
|
|
64
|
+
echo "Using defaults (--yes flag):"
|
|
65
|
+
echo " haiku: $CURRENT_HAIKU"
|
|
66
|
+
echo " sonnet: $CURRENT_SONNET"
|
|
67
|
+
echo " opus: $CURRENT_OPUS"
|
|
68
|
+
exit 0
|
|
69
|
+
fi
|
|
70
|
+
|
|
71
|
+
# Interactive selection function
|
|
72
|
+
select_model() {
|
|
73
|
+
local tier="$1"
|
|
74
|
+
local current="$2"
|
|
75
|
+
local pattern="$3"
|
|
76
|
+
|
|
77
|
+
echo "Select model for $tier tier (current: $current):"
|
|
78
|
+
echo ""
|
|
79
|
+
|
|
80
|
+
# Filter models by pattern
|
|
81
|
+
local filtered=$(echo "$CLAUDE_MODELS" | grep -i "$pattern" || echo "$CLAUDE_MODELS")
|
|
82
|
+
|
|
83
|
+
# Create numbered list
|
|
84
|
+
local i=1
|
|
85
|
+
local model_array=()
|
|
86
|
+
while IFS= read -r model; do
|
|
87
|
+
model_array+=("$model")
|
|
88
|
+
if [ "$model" = "$current" ]; then
|
|
89
|
+
echo " $i) $model (current)"
|
|
90
|
+
else
|
|
91
|
+
echo " $i) $model"
|
|
92
|
+
fi
|
|
93
|
+
((i++))
|
|
94
|
+
done <<< "$filtered"
|
|
95
|
+
|
|
96
|
+
echo " 0) Keep current ($current)"
|
|
97
|
+
echo ""
|
|
98
|
+
|
|
99
|
+
# Get user selection
|
|
100
|
+
while true; do
|
|
101
|
+
read -p "Selection (0-$((i-1))): " selection
|
|
102
|
+
|
|
103
|
+
if [[ "$selection" =~ ^[0-9]+$ ]]; then
|
|
104
|
+
if [ "$selection" -eq 0 ]; then
|
|
105
|
+
echo "$current"
|
|
106
|
+
return
|
|
107
|
+
elif [ "$selection" -ge 1 ] && [ "$selection" -lt "$i" ]; then
|
|
108
|
+
echo "${model_array[$((selection-1))]}"
|
|
109
|
+
return
|
|
110
|
+
fi
|
|
111
|
+
fi
|
|
112
|
+
|
|
113
|
+
echo "Invalid selection. Please enter a number between 0 and $((i-1))."
|
|
114
|
+
done
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
118
|
+
echo "Model Selection"
|
|
119
|
+
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
120
|
+
echo ""
|
|
121
|
+
|
|
122
|
+
# Select models for each tier
|
|
123
|
+
HAIKU_MODEL=$(select_model "haiku" "$CURRENT_HAIKU" "haiku")
|
|
124
|
+
echo ""
|
|
125
|
+
|
|
126
|
+
SONNET_MODEL=$(select_model "sonnet" "$CURRENT_SONNET" "sonnet")
|
|
127
|
+
echo ""
|
|
128
|
+
|
|
129
|
+
OPUS_MODEL=$(select_model "opus" "$CURRENT_OPUS" "opus")
|
|
130
|
+
echo ""
|
|
131
|
+
|
|
132
|
+
# Confirm selections
|
|
133
|
+
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
134
|
+
echo "Selected Models:"
|
|
135
|
+
echo " haiku: $HAIKU_MODEL"
|
|
136
|
+
echo " sonnet: $SONNET_MODEL"
|
|
137
|
+
echo " opus: $OPUS_MODEL"
|
|
138
|
+
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
139
|
+
echo ""
|
|
140
|
+
|
|
141
|
+
read -p "Save this configuration? (y/N): " confirm
|
|
142
|
+
if [[ ! "$confirm" =~ ^[Yy]$ ]]; then
|
|
143
|
+
echo "Configuration not saved."
|
|
144
|
+
exit 0
|
|
145
|
+
fi
|
|
146
|
+
|
|
147
|
+
# Create config directory if needed
|
|
148
|
+
mkdir -p "$(dirname "$CONFIG_FILE")"
|
|
149
|
+
|
|
150
|
+
# Load existing config or create new
|
|
151
|
+
if [ -f "$CONFIG_FILE" ]; then
|
|
152
|
+
CONFIG=$(cat "$CONFIG_FILE")
|
|
153
|
+
else
|
|
154
|
+
CONFIG='{}'
|
|
155
|
+
fi
|
|
156
|
+
|
|
157
|
+
# Update OpenCode models
|
|
158
|
+
CONFIG=$(echo "$CONFIG" | jq ".opencode.haiku = \"$HAIKU_MODEL\"")
|
|
159
|
+
CONFIG=$(echo "$CONFIG" | jq ".opencode.sonnet = \"$SONNET_MODEL\"")
|
|
160
|
+
CONFIG=$(echo "$CONFIG" | jq ".opencode.opus = \"$OPUS_MODEL\"")
|
|
161
|
+
|
|
162
|
+
# Ensure Gemini config exists (preserve existing if present)
|
|
163
|
+
if ! echo "$CONFIG" | jq -e '.gemini' >/dev/null 2>&1; then
|
|
164
|
+
CONFIG=$(echo "$CONFIG" | jq '.gemini = {
|
|
165
|
+
"haiku": "gemini-2.5-flash",
|
|
166
|
+
"sonnet": "gemini-2.5-pro",
|
|
167
|
+
"opus": "gemini-2.5-pro"
|
|
168
|
+
}')
|
|
169
|
+
fi
|
|
170
|
+
|
|
171
|
+
# Write config file
|
|
172
|
+
echo "$CONFIG" | jq . > "$CONFIG_FILE"
|
|
173
|
+
|
|
174
|
+
echo ""
|
|
175
|
+
echo "✅ Configuration saved to: $CONFIG_FILE"
|
|
176
|
+
echo ""
|
|
177
|
+
echo "Next steps:"
|
|
178
|
+
echo " Run the OpenCode installer to use these model selections"
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
{
|
|
2
|
+
"opencode": {
|
|
3
|
+
"haiku": "anthropic/claude-haiku-4-5-20251001",
|
|
4
|
+
"sonnet": "anthropic/claude-sonnet-4-5-20250929",
|
|
5
|
+
"opus": "anthropic/claude-opus-4-6"
|
|
6
|
+
},
|
|
7
|
+
"gemini": {
|
|
8
|
+
"haiku": "gemini-2.5-flash",
|
|
9
|
+
"sonnet": "gemini-2.5-pro",
|
|
10
|
+
"opus": "gemini-2.5-pro"
|
|
11
|
+
},
|
|
12
|
+
"cortex": {
|
|
13
|
+
"haiku": "haiku",
|
|
14
|
+
"sonnet": "sonnet",
|
|
15
|
+
"opus": "opus"
|
|
16
|
+
}
|
|
17
|
+
}
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Model tier mapping across platforms
|
|
3
|
+
* Preserves cost/quality tiers (haiku/sonnet/opus) while using platform-native models
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { readFileSync } from "node:fs";
|
|
7
|
+
import { join } from "node:path";
|
|
8
|
+
|
|
9
|
+
// Load model configuration from file
|
|
10
|
+
let modelConfig: any;
|
|
11
|
+
try {
|
|
12
|
+
const configPath = join(import.meta.dir, "model-config.json");
|
|
13
|
+
modelConfig = JSON.parse(readFileSync(configPath, "utf-8"));
|
|
14
|
+
} catch {
|
|
15
|
+
// Fallback to defaults if config file doesn't exist
|
|
16
|
+
modelConfig = {
|
|
17
|
+
opencode: {
|
|
18
|
+
haiku: "anthropic/claude-haiku-4-5-20251001",
|
|
19
|
+
sonnet: "anthropic/claude-sonnet-4-5-20250929",
|
|
20
|
+
opus: "anthropic/claude-opus-4-6",
|
|
21
|
+
},
|
|
22
|
+
gemini: {
|
|
23
|
+
haiku: "gemini-2.5-flash",
|
|
24
|
+
sonnet: "gemini-2.5-pro",
|
|
25
|
+
opus: "gemini-2.5-pro",
|
|
26
|
+
},
|
|
27
|
+
cortex: {
|
|
28
|
+
haiku: "haiku",
|
|
29
|
+
sonnet: "sonnet",
|
|
30
|
+
opus: "opus",
|
|
31
|
+
},
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export const MODEL_TIERS = {
|
|
36
|
+
claude: {
|
|
37
|
+
haiku: "haiku",
|
|
38
|
+
sonnet: "sonnet",
|
|
39
|
+
opus: "opus",
|
|
40
|
+
inherit: "inherit",
|
|
41
|
+
},
|
|
42
|
+
opencode: {
|
|
43
|
+
haiku: modelConfig.opencode.haiku,
|
|
44
|
+
sonnet: modelConfig.opencode.sonnet,
|
|
45
|
+
opus: modelConfig.opencode.opus,
|
|
46
|
+
inherit: "inherit",
|
|
47
|
+
},
|
|
48
|
+
gemini: {
|
|
49
|
+
haiku: modelConfig.gemini.haiku,
|
|
50
|
+
sonnet: modelConfig.gemini.sonnet,
|
|
51
|
+
opus: modelConfig.gemini.opus, // Gemini has no equivalent to Opus, use Pro
|
|
52
|
+
inherit: "inherit",
|
|
53
|
+
},
|
|
54
|
+
cortex: {
|
|
55
|
+
haiku: modelConfig.cortex?.haiku ?? "haiku",
|
|
56
|
+
sonnet: modelConfig.cortex?.sonnet ?? "sonnet",
|
|
57
|
+
opus: modelConfig.cortex?.opus ?? "opus",
|
|
58
|
+
inherit: "inherit",
|
|
59
|
+
},
|
|
60
|
+
} as const;
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Maps a Claude Code model tier to OpenCode model ID
|
|
64
|
+
*/
|
|
65
|
+
export function mapToOpenCode(claudeModel: string): string {
|
|
66
|
+
const model = claudeModel.toLowerCase();
|
|
67
|
+
|
|
68
|
+
// If already a full model ID, validate and return
|
|
69
|
+
if (model.includes("/") || model.includes("-")) {
|
|
70
|
+
return claudeModel;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Map tier to OpenCode model ID
|
|
74
|
+
const tier = model as keyof typeof MODEL_TIERS.opencode;
|
|
75
|
+
if (tier in MODEL_TIERS.opencode) {
|
|
76
|
+
return MODEL_TIERS.opencode[tier];
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// Default to inherit if unknown
|
|
80
|
+
return "inherit";
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Maps a Claude Code model tier to Gemini model ID
|
|
85
|
+
*/
|
|
86
|
+
export function mapToGemini(claudeModel: string): string {
|
|
87
|
+
const model = claudeModel.toLowerCase();
|
|
88
|
+
|
|
89
|
+
// If it's "inherit", keep it
|
|
90
|
+
if (model === "inherit") {
|
|
91
|
+
return "inherit";
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// If already a Gemini model, return as-is
|
|
95
|
+
if (model.startsWith("gemini-")) {
|
|
96
|
+
return claudeModel;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Map tier to Gemini model ID
|
|
100
|
+
const tier = model as keyof typeof MODEL_TIERS.gemini;
|
|
101
|
+
if (tier in MODEL_TIERS.gemini) {
|
|
102
|
+
return MODEL_TIERS.gemini[tier];
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// Default to gemini-2.5-pro if unknown
|
|
106
|
+
return "gemini-2.5-pro";
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Maps a Claude Code model tier to Cortex Code model tier
|
|
111
|
+
* Identity mapping: Cortex uses the same tier names as Claude
|
|
112
|
+
*/
|
|
113
|
+
export function mapToCortex(claudeModel: string): string {
|
|
114
|
+
const model = claudeModel.toLowerCase();
|
|
115
|
+
|
|
116
|
+
// If it's "inherit", keep it
|
|
117
|
+
if (model === "inherit") {
|
|
118
|
+
return "inherit";
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// Map tier to Cortex model tier (identity mapping)
|
|
122
|
+
const tier = model as keyof typeof MODEL_TIERS.cortex;
|
|
123
|
+
if (tier in MODEL_TIERS.cortex) {
|
|
124
|
+
return MODEL_TIERS.cortex[tier];
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// Default to sonnet if unknown
|
|
128
|
+
return "sonnet";
|
|
129
|
+
}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { resolve, relative } from "node:path";
|
|
2
|
+
import { stat, chmod } from "node:fs/promises";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Security configuration constants
|
|
6
|
+
*/
|
|
7
|
+
export const MAX_FILE_SIZE = 10 * 1024 * 1024; // 10MB
|
|
8
|
+
export const ALLOWED_FILE_PATTERN = /^[a-zA-Z0-9._-]+\.md$/;
|
|
9
|
+
export const ALLOWED_MODEL_NAMES = new Set([
|
|
10
|
+
"sonnet",
|
|
11
|
+
"opus",
|
|
12
|
+
"haiku",
|
|
13
|
+
"inherit",
|
|
14
|
+
"anthropic/claude-sonnet-4-20250514",
|
|
15
|
+
"anthropic/claude-sonnet-4-5-20250929",
|
|
16
|
+
"anthropic/claude-opus-4-6",
|
|
17
|
+
"anthropic/claude-haiku-4-5-20251001",
|
|
18
|
+
"gemini-2.5-pro",
|
|
19
|
+
"gemini-2.5-flash",
|
|
20
|
+
"gemini-2.0-flash",
|
|
21
|
+
]);
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Validates that a path does not escape the base directory
|
|
25
|
+
* Prevents path traversal attacks like ../../../etc/passwd
|
|
26
|
+
*/
|
|
27
|
+
export function validatePath(basePath: string, targetPath: string): string {
|
|
28
|
+
const resolved = resolve(basePath, targetPath);
|
|
29
|
+
const rel = relative(basePath, resolved);
|
|
30
|
+
|
|
31
|
+
if (rel.startsWith("..") || resolve(basePath, rel) !== resolved) {
|
|
32
|
+
throw new Error(`Path traversal detected: ${targetPath}`);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return resolved;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Sanitizes a filename to prevent malicious characters
|
|
40
|
+
* Allows only alphanumeric, dots, underscores, and hyphens
|
|
41
|
+
*/
|
|
42
|
+
export function sanitizeFilename(filename: string): string {
|
|
43
|
+
return filename.replace(/[^a-zA-Z0-9._-]/g, "_");
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Reads a file safely with size limit enforcement
|
|
48
|
+
*/
|
|
49
|
+
export async function readFileSafe(path: string): Promise<string> {
|
|
50
|
+
const stats = await stat(path);
|
|
51
|
+
|
|
52
|
+
if (stats.size > MAX_FILE_SIZE) {
|
|
53
|
+
throw new Error(
|
|
54
|
+
`File too large: ${path} (${stats.size} bytes, max ${MAX_FILE_SIZE})`
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const file = Bun.file(path);
|
|
59
|
+
return await file.text();
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Writes a file with explicit permissions
|
|
64
|
+
* Ensures files are not world-writable
|
|
65
|
+
*/
|
|
66
|
+
export async function writeFileSafe(
|
|
67
|
+
path: string,
|
|
68
|
+
content: string,
|
|
69
|
+
mode: number = 0o644
|
|
70
|
+
): Promise<void> {
|
|
71
|
+
await Bun.write(path, content);
|
|
72
|
+
await chmod(path, mode);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Validates a model name against allowlist
|
|
77
|
+
* Prevents injection via model field
|
|
78
|
+
*/
|
|
79
|
+
export function validateModelName(modelName: string): string {
|
|
80
|
+
// Strict alphanumeric + hyphen + slash + dot validation
|
|
81
|
+
if (!/^[a-z0-9/.-]+$/i.test(modelName)) {
|
|
82
|
+
throw new Error(`Invalid model name format: ${modelName}`);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
if (!ALLOWED_MODEL_NAMES.has(modelName)) {
|
|
86
|
+
throw new Error(`Unsupported model: ${modelName}`);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return modelName;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Validates a filename matches allowed pattern
|
|
94
|
+
*/
|
|
95
|
+
export function validateFilename(filename: string): boolean {
|
|
96
|
+
return ALLOWED_FILE_PATTERN.test(filename);
|
|
97
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { load as yamlLoad, dump as yamlDump } from "js-yaml";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Safely parses YAML frontmatter from markdown content
|
|
5
|
+
* Uses SAFE_SCHEMA to prevent code execution via malicious YAML
|
|
6
|
+
*/
|
|
7
|
+
export function parseFrontmatter(content: string): Record<string, any> {
|
|
8
|
+
const match = content.match(/^---\n([\s\S]+?)\n---/);
|
|
9
|
+
if (!match) {
|
|
10
|
+
return {};
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
try {
|
|
14
|
+
// Use CORE_SCHEMA (safer than DEFAULT_SCHEMA, blocks !!python/object etc)
|
|
15
|
+
const parsed = yamlLoad(match[1], {
|
|
16
|
+
schema: yamlLoad.CORE_SCHEMA,
|
|
17
|
+
json: true, // Only allow JSON-compatible subset
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
|
|
21
|
+
throw new Error("Invalid frontmatter structure - must be object");
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
return parsed as Record<string, any>;
|
|
25
|
+
} catch (err: any) {
|
|
26
|
+
throw new Error(`Frontmatter parse error: ${err.message}`);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Extracts body content (everything after frontmatter)
|
|
32
|
+
*/
|
|
33
|
+
export function extractBody(content: string): string {
|
|
34
|
+
const match = content.match(/^---\n[\s\S]+?\n---\n([\s\S]*)$/);
|
|
35
|
+
return match ? match[1] : content;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Safely stringifies frontmatter to YAML
|
|
40
|
+
*/
|
|
41
|
+
export function stringifyFrontmatter(obj: Record<string, any>): string {
|
|
42
|
+
return yamlDump(obj, {
|
|
43
|
+
schema: yamlDump.CORE_SCHEMA,
|
|
44
|
+
noRefs: true, // Prevent circular references
|
|
45
|
+
lineWidth: -1, // Don't wrap lines
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Reconstructs markdown file with frontmatter
|
|
51
|
+
*/
|
|
52
|
+
export function buildMarkdown(frontmatter: Record<string, any>, body: string): string {
|
|
53
|
+
const yaml = stringifyFrontmatter(frontmatter).trim();
|
|
54
|
+
return `---\n${yaml}\n---\n${body}`;
|
|
55
|
+
}
|