@polderlabs/bizar 6.2.5 → 7.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/CLAUDE.md +15 -0
- package/.claude/agents/_shared/AGENT_BASELINE.md +168 -0
- package/.claude/agents/_shared/CLAUDE_TOOLS.md +412 -0
- package/.claude/agents/_shared/SKILLS.md +109 -0
- package/.claude/agents/agent-browser.md +80 -0
- package/.claude/agents/baldr.md +49 -0
- package/.claude/agents/forseti.md +51 -0
- package/.claude/agents/frigg.md +42 -0
- package/.claude/agents/heimdall.md +33 -0
- package/.claude/agents/hermod.md +53 -0
- package/.claude/agents/mimir.md +49 -0
- package/.claude/agents/odin.md +287 -0
- package/.claude/agents/quick.md +34 -0
- package/.claude/agents/semble-search.md +50 -0
- package/.claude/agents/thor.md +53 -0
- package/.claude/agents/tyr.md +56 -0
- package/.claude/agents/vidarr.md +54 -0
- package/.claude/agents/vor.md +53 -0
- package/.claude/commands/audit.md +25 -0
- package/.claude/commands/bizar.md +22 -0
- package/.claude/commands/explain.md +17 -0
- package/.claude/commands/init.md +29 -0
- package/.claude/commands/learn.md +44 -0
- package/.claude/commands/plan.md +35 -0
- package/.claude/commands/plow-through.md +50 -0
- package/.claude/commands/pr-review.md +49 -0
- package/.claude/commands/setup-provider.md +96 -0
- package/.claude/commands/tailscale-serve.md +100 -0
- package/.claude/commands/team.md +132 -0
- package/.claude/commands/test.md +62 -0
- package/.claude/commands/validate.md +68 -0
- package/.claude/commands/visual-plan.md +24 -0
- package/.claude/hooks/README.md +92 -0
- package/.claude/hooks/posttooluse-editwrite.mjs +91 -0
- package/.claude/hooks/pretooluse-bash.mjs +81 -0
- package/.claude/hooks/pretooluse-editwrite.mjs +139 -0
- package/.claude/hooks/sessionend-recall.mjs +74 -0
- package/.claude/hooks/sessionstart-prime.mjs +80 -0
- package/.claude/hooks/userpromptsubmit-tag.mjs +80 -0
- package/.claude/hooks/worker-suggest.mjs +110 -0
- package/.claude/settings.json +125 -0
- package/.claude/skills/9router/SKILL.md +80 -0
- package/.claude/skills/9router-chat/SKILL.md +73 -0
- package/.claude/skills/9router-embeddings/SKILL.md +69 -0
- package/.claude/skills/9router-image/SKILL.md +86 -0
- package/.claude/skills/9router-stt/SKILL.md +79 -0
- package/.claude/skills/9router-tts/SKILL.md +80 -0
- package/.claude/skills/9router-web-fetch/SKILL.md +99 -0
- package/.claude/skills/9router-web-search/SKILL.md +91 -0
- package/.claude/skills/bizar/README.md +9 -0
- package/.claude/skills/bizar/SKILL.md +450 -0
- package/.claude/skills/cpp-coding-standards/README.md +28 -0
- package/.claude/skills/cpp-coding-standards/SKILL.md +634 -0
- package/.claude/skills/cpp-coding-standards/references/concurrency.md +320 -0
- package/.claude/skills/cpp-coding-standards/references/error-handling.md +229 -0
- package/.claude/skills/cpp-coding-standards/references/memory-safety.md +216 -0
- package/.claude/skills/cpp-coding-standards/references/modern-idioms.md +282 -0
- package/.claude/skills/cpp-coding-standards/references/review-checklist.md +96 -0
- package/.claude/skills/cpp-testing/README.md +28 -0
- package/.claude/skills/cpp-testing/SKILL.md +304 -0
- package/.claude/skills/cpp-testing/references/coverage.md +370 -0
- package/.claude/skills/cpp-testing/references/framework-compare.md +175 -0
- package/.claude/skills/cpp-testing/references/host-test-for-embedded.md +499 -0
- package/.claude/skills/cpp-testing/references/mocking.md +364 -0
- package/.claude/skills/cpp-testing/references/tdd-workflow.md +308 -0
- package/.claude/skills/cubesandbox/SKILL.md +148 -0
- package/.claude/skills/embedded-esp-idf/README.md +41 -0
- package/.claude/skills/embedded-esp-idf/SKILL.md +439 -0
- package/.claude/skills/embedded-esp-idf/references/freertos-patterns.md +214 -0
- package/.claude/skills/embedded-esp-idf/references/host-tests.md +164 -0
- package/.claude/skills/embedded-esp-idf/references/idf-py-commands.md +157 -0
- package/.claude/skills/embedded-esp-idf/references/kconfig.md +159 -0
- package/.claude/skills/embedded-esp-idf/references/logging-discipline.md +118 -0
- package/.claude/skills/embedded-esp-idf/references/memory-and-iram.md +137 -0
- package/.claude/skills/embedded-esp-idf/references/nvs.md +121 -0
- package/.claude/skills/embedded-esp-idf/references/packed-structs.md +192 -0
- package/.claude/skills/embedded-esp-idf/scripts/idf_env.sh +47 -0
- package/.claude/skills/embedded-esp-idf/scripts/size_check.sh +77 -0
- package/.claude/skills/glyph/SKILL.md +163 -0
- package/.claude/skills/harness-engineering/SKILL.md +143 -0
- package/.claude/skills/lightrag/SKILL.md +81 -0
- package/.claude/skills/memory-protocol/SKILL.md +105 -0
- package/.claude/skills/obsidian/SKILL.md +306 -0
- package/.claude/skills/read-the-damn-docs/SKILL.md +113 -0
- package/.claude/skills/self-improvement/SKILL.md +64 -0
- package/README.md +87 -59
- package/bizar-dash/dist/assets/{EnvVarsSection-DXM8gRm_.js → EnvVarsSection-CW6fWKds.js} +3 -3
- package/bizar-dash/dist/assets/{EnvVarsSection-DXM8gRm_.js.map → EnvVarsSection-CW6fWKds.js.map} +1 -1
- package/bizar-dash/dist/assets/MobileChat-D7zRhU9K.js +1 -0
- package/bizar-dash/dist/assets/{MobileChat-BnKN_Ks_.js.map → MobileChat-D7zRhU9K.js.map} +1 -1
- package/bizar-dash/dist/assets/MobileSettings-K-sNsn_r.js +1 -0
- package/bizar-dash/dist/assets/{MobileSettings-DjCPxC-Q.js.map → MobileSettings-K-sNsn_r.js.map} +1 -1
- package/bizar-dash/dist/assets/{Toast-Cpl6-E63.js → Toast-WTxc3m2g.js} +1 -1
- package/bizar-dash/dist/assets/{Toast-Cpl6-E63.js.map → Toast-WTxc3m2g.js.map} +1 -1
- package/bizar-dash/dist/assets/flow-BpU0-gK2.js +1 -0
- package/bizar-dash/dist/assets/flow-BpU0-gK2.js.map +1 -0
- package/bizar-dash/dist/assets/fuzzy-l0sNRNKZ.js +1 -0
- package/bizar-dash/dist/assets/fuzzy-l0sNRNKZ.js.map +1 -0
- package/bizar-dash/dist/assets/{icons-Do5N2jmX.js → icons-B0AKnYI3.js} +98 -103
- package/bizar-dash/dist/assets/icons-B0AKnYI3.js.map +1 -0
- package/bizar-dash/dist/assets/main-C-Md3DaY.css +1 -0
- package/bizar-dash/dist/assets/main-CO8fIxQK.js +18 -0
- package/bizar-dash/dist/assets/main-CO8fIxQK.js.map +1 -0
- package/bizar-dash/dist/assets/markdown-w2bDDkAH.js +1 -0
- package/bizar-dash/dist/assets/{markdown-C6mXtQxD.js.map → markdown-w2bDDkAH.js.map} +1 -1
- package/bizar-dash/dist/assets/mobile-DXYCA0hC.js +1 -0
- package/bizar-dash/dist/assets/{mobile-CMHqtLV2.js.map → mobile-DXYCA0hC.js.map} +1 -1
- package/bizar-dash/dist/assets/mobile-layout-DSmtNPja.js +2 -0
- package/bizar-dash/dist/assets/mobile-layout-DSmtNPja.js.map +1 -0
- package/bizar-dash/dist/assets/react-vendor-BnuhLJ6X.css +1 -0
- package/bizar-dash/dist/assets/react-vendor-D3pra6w-.js +46 -0
- package/bizar-dash/dist/assets/react-vendor-D3pra6w-.js.map +1 -0
- package/bizar-dash/dist/assets/useSlashCommands-3DFLOfpk.js +2 -0
- package/bizar-dash/dist/assets/useSlashCommands-3DFLOfpk.js.map +1 -0
- package/bizar-dash/dist/assets/vendor-Di7Cp-rw.js +45 -0
- package/bizar-dash/dist/assets/vendor-Di7Cp-rw.js.map +1 -0
- package/bizar-dash/dist/index.html +12 -10
- package/bizar-dash/dist/mobile.html +7 -4
- package/bizar-dash/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +1 -1
- package/bizar-dash/src/server/api.mjs +15 -4
- package/bizar-dash/src/server/background-store.mjs +15 -16
- package/bizar-dash/src/server/bg-poller.mjs +25 -35
- package/bizar-dash/src/server/bg-retry.mjs +66 -194
- package/bizar-dash/src/server/claude-bg-spawner.mjs +390 -0
- package/bizar-dash/src/server/claude-info.mjs +412 -0
- package/bizar-dash/src/server/{cline-runner.mjs → claude-runner.mjs} +53 -48
- package/bizar-dash/src/server/claude-sdk.mjs +191 -0
- package/bizar-dash/src/server/memory-consolidator.mjs +218 -0
- package/bizar-dash/src/server/memory-distillation.mjs +296 -0
- package/bizar-dash/src/server/providers-store.mjs +22 -49
- package/bizar-dash/src/server/routes/background.mjs +24 -20
- package/bizar-dash/src/server/routes/chat.mjs +223 -326
- package/bizar-dash/src/server/routes/claude-session-detail.mjs +329 -0
- package/bizar-dash/src/server/routes/claude-sessions.mjs +259 -0
- package/bizar-dash/src/server/routes/distill.mjs +129 -0
- package/bizar-dash/src/server/routes/goal-planner.mjs +236 -0
- package/bizar-dash/src/server/routes/tasks.mjs +20 -28
- package/bizar-dash/src/server/task-delegator.mjs +76 -77
- package/bizar-dash/src/web/App.tsx +4 -0
- package/bizar-dash/src/web/components/Tag.tsx +18 -5
- package/bizar-dash/src/web/components/Topbar.tsx +3 -0
- package/bizar-dash/src/web/components/agents/CommunicationLog.tsx +126 -0
- package/bizar-dash/src/web/components/agents/DependencyGraph.tsx +163 -0
- package/bizar-dash/src/web/components/agents/QualityGates.tsx +123 -0
- package/bizar-dash/src/web/components/agents/RealTimeEventLog.tsx +127 -0
- package/bizar-dash/src/web/components/agents/RoutingDecisions.test.tsx +148 -0
- package/bizar-dash/src/web/components/agents/RoutingDecisions.tsx +160 -0
- package/bizar-dash/src/web/components/chat/useChat.ts +23 -27
- package/bizar-dash/src/web/components/goals/GoalInput.tsx +86 -0
- package/bizar-dash/src/web/components/goals/PlanVisualization.tsx +111 -0
- package/bizar-dash/src/web/lib/goapPlanner.ts +614 -0
- package/bizar-dash/src/web/lib/types.ts +25 -0
- package/bizar-dash/src/web/styles/agents-redesign.css +311 -0
- package/bizar-dash/src/web/styles/main.css +251 -0
- package/bizar-dash/src/web/styles/tasks-redesign.css +231 -0
- package/bizar-dash/src/web/ui/controls/Button.tsx +77 -0
- package/bizar-dash/src/web/ui/controls/Checkbox.tsx +95 -0
- package/bizar-dash/src/web/ui/controls/IconButton.tsx +66 -0
- package/bizar-dash/src/web/ui/controls/Kbd.tsx +24 -0
- package/bizar-dash/src/web/ui/controls/NumberInput.tsx +77 -0
- package/bizar-dash/src/web/ui/controls/RadioGroup.tsx +109 -0
- package/bizar-dash/src/web/ui/controls/SearchInput.tsx +91 -0
- package/bizar-dash/src/web/ui/controls/Select.tsx +92 -0
- package/bizar-dash/src/web/ui/controls/Slider.tsx +70 -0
- package/bizar-dash/src/web/ui/controls/TextInput.tsx +67 -0
- package/bizar-dash/src/web/ui/controls/Toggle.tsx +79 -0
- package/bizar-dash/src/web/ui/controls/controls.css +454 -0
- package/bizar-dash/src/web/ui/controls/index.ts +33 -0
- package/bizar-dash/src/web/ui/data/BarChart.tsx +60 -0
- package/bizar-dash/src/web/ui/data/DataTable.tsx +216 -0
- package/bizar-dash/src/web/ui/data/EmptyState.tsx +60 -0
- package/bizar-dash/src/web/ui/data/ErrorState.tsx +56 -0
- package/bizar-dash/src/web/ui/data/KeyValueList.tsx +106 -0
- package/bizar-dash/src/web/ui/data/LoadingState.tsx +65 -0
- package/bizar-dash/src/web/ui/data/Sparkline.tsx +87 -0
- package/bizar-dash/src/web/ui/data/StatTile.tsx +87 -0
- package/bizar-dash/src/web/ui/data/data.css +535 -0
- package/bizar-dash/src/web/ui/data/index.ts +31 -0
- package/bizar-dash/src/web/ui/feedback/Badge.tsx +49 -0
- package/bizar-dash/src/web/ui/feedback/Dialog.tsx +209 -0
- package/bizar-dash/src/web/ui/feedback/ProgressBar.tsx +69 -0
- package/bizar-dash/src/web/ui/feedback/StatusDot.tsx +50 -0
- package/bizar-dash/src/web/ui/feedback/Toast.tsx +200 -0
- package/bizar-dash/src/web/ui/feedback/Tooltip.tsx +126 -0
- package/bizar-dash/src/web/ui/feedback/feedback.css +314 -0
- package/bizar-dash/src/web/ui/feedback/index.ts +43 -0
- package/bizar-dash/src/web/ui/index.ts +56 -0
- package/bizar-dash/src/web/ui/layout/AppShell.tsx +51 -0
- package/bizar-dash/src/web/ui/layout/Breadcrumbs.tsx +97 -0
- package/bizar-dash/src/web/ui/layout/Panel.tsx +73 -0
- package/bizar-dash/src/web/ui/layout/PanelHeader.tsx +39 -0
- package/bizar-dash/src/web/ui/layout/Sidebar.tsx +131 -0
- package/bizar-dash/src/web/ui/layout/Tabs.tsx +133 -0
- package/bizar-dash/src/web/ui/layout/Topbar.tsx +42 -0
- package/bizar-dash/src/web/ui/layout/index.ts +33 -0
- package/bizar-dash/src/web/ui/layout/layout.css +413 -0
- package/bizar-dash/src/web/ui/navigation/CommandPalette.tsx +199 -0
- package/bizar-dash/src/web/ui/navigation/NavGroup.tsx +95 -0
- package/bizar-dash/src/web/ui/navigation/NavLink.tsx +93 -0
- package/bizar-dash/src/web/ui/navigation/index.ts +17 -0
- package/bizar-dash/src/web/ui/navigation/navigation.css +343 -0
- package/bizar-dash/src/web/ui/primitives/Box.tsx +91 -0
- package/bizar-dash/src/web/ui/primitives/Grid.tsx +49 -0
- package/bizar-dash/src/web/ui/primitives/Inline.tsx +55 -0
- package/bizar-dash/src/web/ui/primitives/Separator.tsx +46 -0
- package/bizar-dash/src/web/ui/primitives/Stack.tsx +54 -0
- package/bizar-dash/src/web/ui/primitives/VisuallyHidden.tsx +26 -0
- package/bizar-dash/src/web/ui/primitives/index.ts +23 -0
- package/bizar-dash/src/web/ui/primitives/primitives.css +183 -0
- package/bizar-dash/src/web/ui/styles/globals.css +86 -0
- package/bizar-dash/src/web/ui/styles/reset.css +125 -0
- package/bizar-dash/src/web/ui/styles/tokens.css +195 -0
- package/bizar-dash/src/web/ui/theme/ThemeProvider.tsx +142 -0
- package/bizar-dash/src/web/ui/theme/tokens.ts +131 -0
- package/bizar-dash/src/web/ui/theme/useTheme.ts +22 -0
- package/bizar-dash/src/web/ui/utils/cx.ts +42 -0
- package/bizar-dash/src/web/views/Agents.tsx +506 -266
- package/bizar-dash/src/web/views/Chat.tsx +8 -8
- package/bizar-dash/src/web/views/GoalPlanner.tsx +158 -0
- package/bizar-dash/src/web/views/Overview.tsx +996 -448
- package/bizar-dash/src/web/views/Tasks.tsx +277 -273
- package/bizar-dash/tests/communication-log.test.tsx +67 -0
- package/bizar-dash/tests/dep-graph.test.tsx +40 -0
- package/bizar-dash/tests/event-log.test.tsx +61 -0
- package/bizar-dash/tests/goal-input.test.tsx +72 -0
- package/bizar-dash/tests/goap-planner.test.ts +68 -0
- package/bizar-dash/tests/quality-gates.test.tsx +59 -0
- package/bizar-dash/tests/setup.ts +31 -1
- package/bizar-dash/tests/ui/controls/Button.test.tsx +50 -0
- package/bizar-dash/tests/ui/controls/Checkbox.test.tsx +44 -0
- package/bizar-dash/tests/ui/controls/IconButton.test.tsx +37 -0
- package/bizar-dash/tests/ui/controls/Kbd.test.tsx +39 -0
- package/bizar-dash/tests/ui/controls/NumberInput.test.tsx +56 -0
- package/bizar-dash/tests/ui/controls/RadioGroup.test.tsx +58 -0
- package/bizar-dash/tests/ui/controls/SearchInput.test.tsx +52 -0
- package/bizar-dash/tests/ui/controls/Select.test.tsx +66 -0
- package/bizar-dash/tests/ui/controls/Slider.test.tsx +41 -0
- package/bizar-dash/tests/ui/controls/TextInput.test.tsx +44 -0
- package/bizar-dash/tests/ui/controls/Toggle.test.tsx +39 -0
- package/bizar-dash/tests/ui/data/BarChart.test.tsx +55 -0
- package/bizar-dash/tests/ui/data/DataTable.test.tsx +112 -0
- package/bizar-dash/tests/ui/data/EmptyState.test.tsx +57 -0
- package/bizar-dash/tests/ui/data/ErrorState.test.tsx +47 -0
- package/bizar-dash/tests/ui/data/KeyValueList.test.tsx +59 -0
- package/bizar-dash/tests/ui/data/LoadingState.test.tsx +37 -0
- package/bizar-dash/tests/ui/data/Sparkline.test.tsx +56 -0
- package/bizar-dash/tests/ui/data/StatTile.test.tsx +69 -0
- package/bizar-dash/tests/ui/feedback/Badge.test.tsx +41 -0
- package/bizar-dash/tests/ui/feedback/Dialog.test.tsx +99 -0
- package/bizar-dash/tests/ui/feedback/ProgressBar.test.tsx +66 -0
- package/bizar-dash/tests/ui/feedback/StatusDot.test.tsx +41 -0
- package/bizar-dash/tests/ui/feedback/Toast.test.tsx +124 -0
- package/bizar-dash/tests/ui/feedback/Tooltip.test.tsx +102 -0
- package/bizar-dash/tests/ui/layout/AppShell.test.tsx +50 -0
- package/bizar-dash/tests/ui/layout/Breadcrumbs.test.tsx +85 -0
- package/bizar-dash/tests/ui/layout/Panel.test.tsx +52 -0
- package/bizar-dash/tests/ui/layout/PanelHeader.test.tsx +32 -0
- package/bizar-dash/tests/ui/layout/Sidebar.test.tsx +95 -0
- package/bizar-dash/tests/ui/layout/Tabs.test.tsx +87 -0
- package/bizar-dash/tests/ui/layout/Topbar.test.tsx +41 -0
- package/bizar-dash/tests/ui/navigation/CommandPalette.test.tsx +105 -0
- package/bizar-dash/tests/ui/navigation/NavGroup.test.tsx +55 -0
- package/bizar-dash/tests/ui/navigation/NavLink.test.tsx +61 -0
- package/bizar-dash/tests/ui/primitives/Box.test.tsx +49 -0
- package/bizar-dash/tests/ui/primitives/Grid.test.tsx +37 -0
- package/bizar-dash/tests/ui/primitives/Inline.test.tsx +33 -0
- package/bizar-dash/tests/ui/primitives/Separator.test.tsx +34 -0
- package/bizar-dash/tests/ui/primitives/Stack.test.tsx +45 -0
- package/bizar-dash/tests/ui/primitives/VisuallyHidden.test.tsx +27 -0
- package/bizar-dash/tests/views/Agents.test.tsx +359 -0
- package/bizar-dash/tests/views/Overview.test.tsx +413 -0
- package/bizar-dash/tests/views/Tasks.test.tsx +250 -0
- package/bizar-dash/vitest.config.ts +3 -1
- package/cli/__tests__/cost-gate.test.mjs +301 -0
- package/cli/__tests__/feature-list-bridge.test.mjs +371 -0
- package/cli/bin.mjs +55 -18
- package/cli/commands/claim.mjs +282 -0
- package/cli/commands/claude-cmd.mjs +348 -0
- package/cli/commands/cost.mjs +279 -0
- package/cli/commands/install.mjs +12 -11
- package/cli/commands/validate.mjs +252 -325
- package/cli/cost-gate.mjs +501 -0
- package/cli/dev-link.test.mjs +11 -11
- package/cli/doctor.mjs +131 -213
- package/cli/feature-list-bridge.mjs +400 -0
- package/cli/install.mjs +33 -37
- package/cli/install.test.mjs +9 -9
- package/cli/memory-constants.mjs +17 -0
- package/cli/provision-claude.mjs +893 -0
- package/cli/provision.mjs +224 -227
- package/cli/utils.mjs +72 -58
- package/cli/worker-dispatcher.mjs +259 -0
- package/cli/worker-dispatcher.test.mjs +204 -0
- package/config/AGENTS.md +28 -28
- package/config/trigger-patterns.json +186 -0
- package/install.sh +66 -44
- package/package.json +19 -11
- package/packages/sdk/ARCHITECTURE.md +40 -36
- package/packages/sdk/package.json +15 -5
- package/packages/sdk/src/agent-registry.ts +304 -0
- package/packages/sdk/src/consensus/byzantine.ts +587 -0
- package/packages/sdk/src/consensus/index.ts +147 -0
- package/packages/sdk/src/consensus/queen.ts +173 -0
- package/packages/sdk/src/consensus/types.ts +199 -0
- package/{plugins/bizar → packages/sdk}/src/dangerous-patterns.ts +6 -28
- package/packages/sdk/src/federation/audit.ts +156 -0
- package/packages/sdk/src/federation/budget.ts +269 -0
- package/packages/sdk/src/federation/envelope.ts +144 -0
- package/packages/sdk/src/federation/hmac.ts +160 -0
- package/packages/sdk/src/federation/index.ts +443 -0
- package/packages/sdk/src/federation/pii.ts +241 -0
- package/packages/sdk/src/federation/policy.ts +112 -0
- package/packages/sdk/src/federation/trust.ts +146 -0
- package/packages/sdk/src/fingerprint.ts +0 -0
- package/packages/sdk/src/index.ts +101 -69
- package/packages/sdk/src/mcp/bin.ts +51 -0
- package/packages/sdk/src/mcp/server.ts +848 -0
- package/{plugins/bizar/src/memory-vault.ts → packages/sdk/src/memory/index.ts} +18 -21
- package/packages/sdk/src/router/codemod-intent.ts +125 -0
- package/packages/sdk/src/router/index.ts +247 -0
- package/packages/sdk/src/router/memory-distillation-shim.mjs +18 -0
- package/packages/sdk/src/router/memory-distillation.ts +249 -0
- package/packages/sdk/src/router/model-router.ts +235 -0
- package/packages/sdk/src/router/q-learning-router.ts +304 -0
- package/packages/sdk/src/swarm-topology.ts +279 -0
- package/packages/sdk/tests/agent-registry.test.ts +285 -0
- package/packages/sdk/tests/consensus.test.ts +376 -0
- package/packages/sdk/tests/federation/audit.test.ts +164 -0
- package/packages/sdk/tests/federation/budget.test.ts +189 -0
- package/packages/sdk/tests/federation/envelope.test.ts +120 -0
- package/packages/sdk/tests/federation/hmac.test.ts +194 -0
- package/packages/sdk/tests/federation/orchestrator.test.ts +176 -0
- package/packages/sdk/tests/federation/pii.test.ts +106 -0
- package/packages/sdk/tests/federation/policy.test.ts +140 -0
- package/packages/sdk/tests/federation/trust.test.ts +124 -0
- package/packages/sdk/tests/mcp-tools.test.ts +204 -0
- package/packages/sdk/tests/memory-distillation.test.mjs +186 -0
- package/packages/sdk/tests/model-router.test.mjs +108 -0
- package/packages/sdk/tests/q-learning-router.test.mjs +100 -0
- package/packages/sdk/tests/router-orchestrator.test.mjs +172 -0
- package/packages/sdk/tests/router.test.mjs +91 -0
- package/packages/sdk/tests/sdk.test.mjs +172 -0
- package/packages/sdk/tests/swarm-topology.test.ts +258 -0
- package/packages/sdk/vitest.config.ts +1 -1
- package/scripts/bh-full-e2e.mjs +167 -368
- package/scripts/check-deps.mjs +28 -46
- package/scripts/mirror-claude-md.sh +78 -0
- package/scripts/test-in-container.sh +31 -9
- package/templates/clean-state-checklist.md +3 -3
- package/templates/sprint-contract.md +2 -2
- package/bizar-dash/.bizar/activity.log +0 -3
- package/bizar-dash/.omx/logs/omx-2026-07-07.jsonl +0 -1
- package/bizar-dash/.omx/state/session.json +0 -10
- package/bizar-dash/bizar-design/EXPLANATION.md +0 -307
- package/bizar-dash/bizar-design/INSTRUCTIONS.md +0 -149
- package/bizar-dash/bizar-design/canvas.html +0 -281
- package/bizar-dash/bizar-design/canvas.html.artifact.json +0 -19
- package/bizar-dash/bizar-design/components.css +0 -1665
- package/bizar-dash/bizar-design/components.html +0 -557
- package/bizar-dash/bizar-design/components.html.artifact.json +0 -19
- package/bizar-dash/bizar-design/desktop/agents.html +0 -74
- package/bizar-dash/bizar-design/desktop/agents.html.artifact.json +0 -19
- package/bizar-dash/bizar-design/desktop/memory.html +0 -75
- package/bizar-dash/bizar-design/desktop/memory.html.artifact.json +0 -19
- package/bizar-dash/bizar-design/desktop/metrics.html +0 -63
- package/bizar-dash/bizar-design/desktop/metrics.html.artifact.json +0 -19
- package/bizar-dash/bizar-design/desktop/overview.html +0 -95
- package/bizar-dash/bizar-design/desktop/overview.html.artifact.json +0 -19
- package/bizar-dash/bizar-design/desktop/settings.html +0 -70
- package/bizar-dash/bizar-design/desktop/settings.html.artifact.json +0 -19
- package/bizar-dash/bizar-design/icons.svg +0 -44
- package/bizar-dash/bizar-design/index.html +0 -192
- package/bizar-dash/bizar-design/index.html.artifact.json +0 -19
- package/bizar-dash/bizar-design/mobile/agents.html +0 -29
- package/bizar-dash/bizar-design/mobile/agents.html.artifact.json +0 -19
- package/bizar-dash/bizar-design/mobile/memory.html +0 -28
- package/bizar-dash/bizar-design/mobile/memory.html.artifact.json +0 -19
- package/bizar-dash/bizar-design/mobile/metrics.html +0 -29
- package/bizar-dash/bizar-design/mobile/metrics.html.artifact.json +0 -19
- package/bizar-dash/bizar-design/mobile/overview.html +0 -33
- package/bizar-dash/bizar-design/mobile/overview.html.artifact.json +0 -19
- package/bizar-dash/bizar-design/mobile/settings.html +0 -28
- package/bizar-dash/bizar-design/mobile/settings.html.artifact.json +0 -19
- package/bizar-dash/bizar-design/prototype.js +0 -114
- package/bizar-dash/bizar-design/tokens.css +0 -106
- package/bizar-dash/dist/assets/MobileChat-BnKN_Ks_.js +0 -1
- package/bizar-dash/dist/assets/MobileSettings-DjCPxC-Q.js +0 -1
- package/bizar-dash/dist/assets/icons-Do5N2jmX.js.map +0 -1
- package/bizar-dash/dist/assets/main-DYiZqMrn.js +0 -18
- package/bizar-dash/dist/assets/main-DYiZqMrn.js.map +0 -1
- package/bizar-dash/dist/assets/main-ietCEg_R.css +0 -1
- package/bizar-dash/dist/assets/markdown-C6mXtQxD.js +0 -1
- package/bizar-dash/dist/assets/mobile-CMHqtLV2.js +0 -1
- package/bizar-dash/dist/assets/mobile-layout-3jIhHX_p.js +0 -2
- package/bizar-dash/dist/assets/mobile-layout-3jIhHX_p.js.map +0 -1
- package/bizar-dash/dist/assets/react-vendor-Dn4wqh4Z.js +0 -40
- package/bizar-dash/dist/assets/react-vendor-Dn4wqh4Z.js.map +0 -1
- package/bizar-dash/dist/assets/useSlashCommands-DtITw8Xv.js +0 -2
- package/bizar-dash/dist/assets/useSlashCommands-DtITw8Xv.js.map +0 -1
- package/bizar-dash/dist/assets/vendor-CeHGtduv.js +0 -29
- package/bizar-dash/dist/assets/vendor-CeHGtduv.js.map +0 -1
- package/bizar-dash/node_modules/.package-lock.json +0 -6
- package/bizar-dash/package-lock.json +0 -6
- package/bizar-dash/src/server/cline-sdk.mjs +0 -132
- package/bizar-dash/src/server/routes/cline-session-detail.mjs +0 -559
- package/bizar-dash/src/server/routes/cline-sessions.mjs +0 -291
- package/cli/commands/cline-cmd.mjs +0 -289
- package/cli/commands/validate.test.mjs +0 -379
- package/cli/doctor.test.mjs +0 -350
- package/config/cline.json.template +0 -342
- package/packages/sdk/src/client.ts +0 -188
- package/packages/sdk/src/cline-events.ts +0 -134
- package/packages/sdk/src/cline-types.ts +0 -66
- package/packages/sdk/src/cline.ts +0 -339
- package/packages/sdk/src/errors.ts +0 -129
- package/packages/sdk/src/events.ts +0 -153
- package/packages/sdk/src/types.ts +0 -176
- package/packages/sdk/tests/client.test.ts +0 -217
- package/packages/sdk/tests/errors.test.ts +0 -108
- package/packages/sdk/tests/events.test.ts +0 -139
- package/packages/sdk/tests/fixtures/fetch-mock.ts +0 -152
- package/packages/sdk/tests/fixtures/sse-mock.ts +0 -30
- package/plugins/bizar/ARCHITECTURE.md +0 -142
- package/plugins/bizar/CONSTRAINTS.md +0 -67
- package/plugins/bizar/LICENSE +0 -21
- package/plugins/bizar/README.md +0 -448
- package/plugins/bizar/index.ts +0 -925
- package/plugins/bizar/package.json +0 -39
- package/plugins/bizar/scripts/check-forbidden-imports.sh +0 -33
- package/plugins/bizar/src/background-state.ts +0 -641
- package/plugins/bizar/src/background.ts +0 -1806
- package/plugins/bizar/src/cline-runner.ts +0 -203
- package/plugins/bizar/src/clineruntime.ts +0 -227
- package/plugins/bizar/src/commands-impl.ts +0 -151
- package/plugins/bizar/src/commands.ts +0 -1799
- package/plugins/bizar/src/compaction.d.mts +0 -48
- package/plugins/bizar/src/compaction.mjs +0 -192
- package/plugins/bizar/src/dashboard-client.ts +0 -233
- package/plugins/bizar/src/event-stream.ts +0 -606
- package/plugins/bizar/src/fingerprint.ts +0 -120
- package/plugins/bizar/src/handoff.ts +0 -79
- package/plugins/bizar/src/hooks/memory-flush-on-compact.ts +0 -123
- package/plugins/bizar/src/hooks/memory-inject.ts +0 -247
- package/plugins/bizar/src/hooks/memory-write-on-end.ts +0 -188
- package/plugins/bizar/src/hooks/skill-curator.ts +0 -180
- package/plugins/bizar/src/http-client.ts +0 -467
- package/plugins/bizar/src/key-rotation.ts +0 -218
- package/plugins/bizar/src/logger.ts +0 -144
- package/plugins/bizar/src/loop-engineering.ts +0 -241
- package/plugins/bizar/src/loop.ts +0 -176
- package/plugins/bizar/src/mistake-recovery.ts +0 -98
- package/plugins/bizar/src/odin.ts +0 -227
- package/plugins/bizar/src/options.ts +0 -470
- package/plugins/bizar/src/plan-fs.ts +0 -323
- package/plugins/bizar/src/reasoning-clean.ts +0 -454
- package/plugins/bizar/src/report.ts +0 -178
- package/plugins/bizar/src/research-prompt.ts +0 -35
- package/plugins/bizar/src/serve-info.ts +0 -228
- package/plugins/bizar/src/serve.ts +0 -496
- package/plugins/bizar/src/settings.ts +0 -349
- package/plugins/bizar/src/state.ts +0 -298
- package/plugins/bizar/src/tool-discipline.ts +0 -105
- package/plugins/bizar/src/tools/agent-browser.ts +0 -315
- package/plugins/bizar/src/tools/bg-collect.ts +0 -131
- package/plugins/bizar/src/tools/bg-get-comments.ts +0 -266
- package/plugins/bizar/src/tools/bg-kill.ts +0 -116
- package/plugins/bizar/src/tools/bg-pause.ts +0 -99
- package/plugins/bizar/src/tools/bg-report-progress.ts +0 -115
- package/plugins/bizar/src/tools/bg-resume.ts +0 -94
- package/plugins/bizar/src/tools/bg-send-message.ts +0 -223
- package/plugins/bizar/src/tools/bg-spawn.ts +0 -502
- package/plugins/bizar/src/tools/bg-status.ts +0 -130
- package/plugins/bizar/src/tools/graph-query.ts +0 -278
- package/plugins/bizar/src/tools/loop-engineering.ts +0 -193
- package/plugins/bizar/src/tools/memory-list.ts +0 -43
- package/plugins/bizar/src/tools/memory-read.ts +0 -69
- package/plugins/bizar/src/tools/memory-search.ts +0 -47
- package/plugins/bizar/src/tools/memory-write.ts +0 -54
- package/plugins/bizar/src/tools/open-kb.ts +0 -198
- package/plugins/bizar/src/tools/plan-action.ts +0 -785
- package/plugins/bizar/src/tools/read-glyph-feedback.ts +0 -191
- package/plugins/bizar/src/tools/sandbox.ts +0 -232
- package/plugins/bizar/src/tools/team-spawn.ts +0 -73
- package/plugins/bizar/src/tools/team-status.ts +0 -76
- package/plugins/bizar/src/tools/wait-for-feedback.ts +0 -415
- package/plugins/bizar/src/trajectory.ts +0 -104
- package/plugins/bizar/tests/README.md +0 -99
- package/plugins/bizar/tests/attach-handler-bug.test.ts +0 -169
- package/plugins/bizar/tests/background-state.test.ts +0 -277
- package/plugins/bizar/tests/background.test.ts +0 -402
- package/plugins/bizar/tests/block.test.ts +0 -195
- package/plugins/bizar/tests/canonical-key-order.test.ts +0 -75
- package/plugins/bizar/tests/clineruntime-config.test.ts +0 -283
- package/plugins/bizar/tests/commands-impl.test.ts +0 -316
- package/plugins/bizar/tests/commands.test.ts +0 -584
- package/plugins/bizar/tests/compaction.test.ts +0 -264
- package/plugins/bizar/tests/config.test.ts +0 -128
- package/plugins/bizar/tests/dashboard-client.test.ts +0 -159
- package/plugins/bizar/tests/dispose.test.ts +0 -336
- package/plugins/bizar/tests/event-stream.test.ts +0 -409
- package/plugins/bizar/tests/event.test.ts +0 -262
- package/plugins/bizar/tests/fingerprint.test.ts +0 -190
- package/plugins/bizar/tests/http-client.test.ts +0 -404
- package/plugins/bizar/tests/init-helpers.test.ts +0 -203
- package/plugins/bizar/tests/integration/slash-command.test.ts +0 -349
- package/plugins/bizar/tests/integration/tool-routing.test.ts +0 -98
- package/plugins/bizar/tests/key-rotation.test.ts +0 -396
- package/plugins/bizar/tests/loop-engineering.test.ts +0 -168
- package/plugins/bizar/tests/loop.test.ts +0 -397
- package/plugins/bizar/tests/memory-write-on-end.test.ts +0 -92
- package/plugins/bizar/tests/mistake-recovery.test.ts +0 -116
- package/plugins/bizar/tests/odin.test.ts +0 -125
- package/plugins/bizar/tests/options.test.ts +0 -329
- package/plugins/bizar/tests/reasoning-clean.test.ts +0 -422
- package/plugins/bizar/tests/safety.test.ts +0 -281
- package/plugins/bizar/tests/serve.test.ts +0 -339
- package/plugins/bizar/tests/settings.test.ts +0 -351
- package/plugins/bizar/tests/stall-think.test.ts +0 -750
- package/plugins/bizar/tests/state.test.ts +0 -276
- package/plugins/bizar/tests/tool-discipline.test.ts +0 -77
- package/plugins/bizar/tests/tools/agent-browser.test.ts +0 -98
- package/plugins/bizar/tests/tools/bg-collect.test.ts +0 -337
- package/plugins/bizar/tests/tools/bg-get-comments.test.ts +0 -485
- package/plugins/bizar/tests/tools/bg-kill.test.ts +0 -235
- package/plugins/bizar/tests/tools/bg-pause.test.ts +0 -61
- package/plugins/bizar/tests/tools/bg-report-progress.test.ts +0 -79
- package/plugins/bizar/tests/tools/bg-resume.test.ts +0 -40
- package/plugins/bizar/tests/tools/bg-send-message.test.ts +0 -116
- package/plugins/bizar/tests/tools/bg-spawn-delegation.test.ts +0 -147
- package/plugins/bizar/tests/tools/bg-spawn-http.test.ts +0 -233
- package/plugins/bizar/tests/tools/bg-spawn.test.ts +0 -311
- package/plugins/bizar/tests/tools/bg-status.test.ts +0 -217
- package/plugins/bizar/tests/tools/cline-runner.test.ts +0 -115
- package/plugins/bizar/tests/tools/plan-action.test.ts +0 -599
- package/plugins/bizar/tests/tools/read-glyph-feedback.test.ts +0 -253
- package/plugins/bizar/tests/tools/sandbox.test.ts +0 -117
- package/plugins/bizar/tests/tools/wait-for-feedback.test.ts +0 -390
- package/plugins/bizar/tests/update-deadlock.test.ts +0 -151
- package/plugins/bizar/tsconfig.json +0 -29
- package/scripts/mirror-agents-md.sh +0 -69
|
@@ -0,0 +1,501 @@
|
|
|
1
|
+
// @ts-nocheck — declaration emit for the `Database` / `Statement`
|
|
2
|
+
// private types from better-sqlite3 is not currently exported by the
|
|
3
|
+
// better-sqlite3 type package, so .d.ts generation for this .mjs
|
|
4
|
+
// module fails with TS9006. The runtime is correct; once better-sqlite3
|
|
5
|
+
// ships exported types (or F-035 lands with a wrapper), the
|
|
6
|
+
// @ts-nocheck can come off.
|
|
7
|
+
// (The directive is placed BEFORE the docblock header below so
|
|
8
|
+
// TypeScript's parser doesn't trip on the `<uuid>` / `<env>` tokens
|
|
9
|
+
// inside the JSDoc body.)
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* cli/cost-gate.mjs
|
|
13
|
+
*
|
|
14
|
+
* F-035 (MetaHarness) — atomic SQLite-backed room-budget tracker.
|
|
15
|
+
*
|
|
16
|
+
* Source-of-truth port from
|
|
17
|
+
* ruflo/v3/@claude-flow/cli/src/business-pods/bbs-budget-tracker.ts:151
|
|
18
|
+
* (AtomicBbsRoomBudgetTracker — ADR-164.1 §3-§5)
|
|
19
|
+
*
|
|
20
|
+
* The contract:
|
|
21
|
+
* - `registerRoom(roomId, capUsd)` is idempotent (upsert by id).
|
|
22
|
+
* - `reserve(roomId, callerId, amount, opts?)` atomically checks
|
|
23
|
+
* the budget AND inserts a 'reserved' transaction inside one
|
|
24
|
+
* `BEGIN IMMEDIATE` transaction (Bizar's L10 cost-gate constraint).
|
|
25
|
+
* - `commit(txId, actualUsd)` flips the row to 'committed' (or
|
|
26
|
+
* 'committed_post_expiry' + `COMMIT_AFTER_EXPIRY` warning if the
|
|
27
|
+
* reservation already expired — the API spend already happened,
|
|
28
|
+
* so we accept the charge and emit a warning).
|
|
29
|
+
* - `release(txId)` flips a still-'reserved' row to 'released';
|
|
30
|
+
* any other state returns ALREADY_FINALIZED.
|
|
31
|
+
* - `status(roomId)` returns { capUsd, spentUsd, remainingUsd }
|
|
32
|
+
* computed from the committed + live-reserved ledger.
|
|
33
|
+
* - `sweepExpired(nowMs?)` is exposed for callers to drive
|
|
34
|
+
* periodically (the pod-tick mjs stub uses 5s sweep).
|
|
35
|
+
*
|
|
36
|
+
* State machine (per ADR-164.1 §3, §5.3):
|
|
37
|
+
* reserved -> committed
|
|
38
|
+
* reserved -> released
|
|
39
|
+
* reserved -> expired (sweepExpired, before any commit)
|
|
40
|
+
* reserved -> committed_post_expiry (commit lands after expires_at)
|
|
41
|
+
*
|
|
42
|
+
* Atomicity guarantees:
|
|
43
|
+
* - WAL (PRAGMA journal_mode = WAL) so reads don't block writes.
|
|
44
|
+
* - `busy_timeout = 500` so concurrent reserves get a brief retry
|
|
45
|
+
* window for SQLITE_BUSY instead of failing immediately.
|
|
46
|
+
* - All reserve/commit/release run inside a single `BEGIN IMMEDIATE`
|
|
47
|
+
* transaction so the lock acquisition + state mutation are atomic.
|
|
48
|
+
*
|
|
49
|
+
* Schema (per feature spec):
|
|
50
|
+
* rooms(id TEXT PRIMARY KEY, monthly_cap_usd REAL, spent_usd REAL,
|
|
51
|
+
* last_reset_at INTEGER)
|
|
52
|
+
* transactions(id INTEGER PRIMARY KEY, room_id TEXT, kind TEXT,
|
|
53
|
+
* amount_usd REAL, ts INTEGER, metadata TEXT)
|
|
54
|
+
*
|
|
55
|
+
* IDs:
|
|
56
|
+
* transaction id is string-shaped (`tx-<uuid>`) so the result type
|
|
57
|
+
* can carry it back through JSON. The `kind` column is one of
|
|
58
|
+
* 'reserved' | 'committed' | 'released' | 'expired' | 'committed_post_expiry'.
|
|
59
|
+
*
|
|
60
|
+
* Expiry window:
|
|
61
|
+
* - default 60_000 ms (overridable per `reserve()` call)
|
|
62
|
+
* - clamped to [5_000, 300_000] ms (ADR-164.1 §3.2)
|
|
63
|
+
* - overridable globally via BIZAR_COST_RESERVATION_EXPIRY_MS env var
|
|
64
|
+
*/
|
|
65
|
+
|
|
66
|
+
import { randomUUID } from 'node:crypto';
|
|
67
|
+
import { mkdirSync } from 'node:fs';
|
|
68
|
+
import { dirname } from 'node:path';
|
|
69
|
+
|
|
70
|
+
// better-sqlite3's Database / Statement types are not currently exported by
|
|
71
|
+
// the @types/better-sqlite3 package, so we use a structural cast to keep
|
|
72
|
+
// both runtime behavior and declaration emit happy.
|
|
73
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
74
|
+
const Database = (await import('better-sqlite3')).default;
|
|
75
|
+
|
|
76
|
+
// ── Constants (kept in this module so the CLI doesn't depend on
|
|
77
|
+
// cli/memory-constants.mjs for the cost gate) ──────────────────────
|
|
78
|
+
|
|
79
|
+
export const RESERVATION_EXPIRY_FLOOR_MS = 5_000;
|
|
80
|
+
export const RESERVATION_EXPIRY_CEILING_MS = 300_000;
|
|
81
|
+
export const RESERVATION_EXPIRY_DEFAULT_MS = 60_000;
|
|
82
|
+
|
|
83
|
+
const TX_STATES = new Set([
|
|
84
|
+
'reserved',
|
|
85
|
+
'committed',
|
|
86
|
+
'released',
|
|
87
|
+
'expired',
|
|
88
|
+
'committed_post_expiry',
|
|
89
|
+
]);
|
|
90
|
+
|
|
91
|
+
export function clampReservationExpiry(raw) {
|
|
92
|
+
if (raw === undefined || raw === null || !Number.isFinite(Number(raw))) {
|
|
93
|
+
const envVal = Number(process.env.BIZAR_COST_RESERVATION_EXPIRY_MS);
|
|
94
|
+
if (Number.isFinite(envVal) && envVal > 0) {
|
|
95
|
+
return Math.max(
|
|
96
|
+
RESERVATION_EXPIRY_FLOOR_MS,
|
|
97
|
+
Math.min(RESERVATION_EXPIRY_CEILING_MS, envVal),
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
return RESERVATION_EXPIRY_DEFAULT_MS;
|
|
101
|
+
}
|
|
102
|
+
const n = Number(raw);
|
|
103
|
+
return Math.max(
|
|
104
|
+
RESERVATION_EXPIRY_FLOOR_MS,
|
|
105
|
+
Math.min(RESERVATION_EXPIRY_CEILING_MS, n),
|
|
106
|
+
);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const SCHEMA_SQL = `
|
|
110
|
+
PRAGMA journal_mode = WAL;
|
|
111
|
+
PRAGMA synchronous = NORMAL;
|
|
112
|
+
PRAGMA busy_timeout = 500;
|
|
113
|
+
|
|
114
|
+
CREATE TABLE IF NOT EXISTS rooms (
|
|
115
|
+
id TEXT NOT NULL PRIMARY KEY,
|
|
116
|
+
monthly_cap_usd REAL NOT NULL CHECK (monthly_cap_usd >= 0),
|
|
117
|
+
spent_usd REAL NOT NULL DEFAULT 0 CHECK (spent_usd >= 0),
|
|
118
|
+
last_reset_at INTEGER NOT NULL
|
|
119
|
+
);
|
|
120
|
+
|
|
121
|
+
CREATE TABLE IF NOT EXISTS transactions (
|
|
122
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
123
|
+
tx_id TEXT NOT NULL UNIQUE,
|
|
124
|
+
room_id TEXT NOT NULL REFERENCES rooms(id),
|
|
125
|
+
kind TEXT NOT NULL CHECK (kind IN ('reserved','committed','released','expired','committed_post_expiry')),
|
|
126
|
+
amount_usd REAL NOT NULL,
|
|
127
|
+
caller_id TEXT NOT NULL DEFAULT '',
|
|
128
|
+
ts INTEGER NOT NULL,
|
|
129
|
+
expires_at INTEGER,
|
|
130
|
+
committed_at INTEGER,
|
|
131
|
+
metadata TEXT NOT NULL DEFAULT '{}'
|
|
132
|
+
);
|
|
133
|
+
|
|
134
|
+
CREATE INDEX IF NOT EXISTS idx_tx_room_state_ts
|
|
135
|
+
ON transactions (room_id, kind, ts);
|
|
136
|
+
CREATE INDEX IF NOT EXISTS idx_tx_state_expires
|
|
137
|
+
ON transactions (kind, expires_at);
|
|
138
|
+
`;
|
|
139
|
+
|
|
140
|
+
// ── Result shapes ─────────────────────────────────────────────────────
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* @typedef {Object} ReserveResult
|
|
144
|
+
* @property {boolean} ok
|
|
145
|
+
* @property {string} [txId]
|
|
146
|
+
* @property {number} [expiresAt]
|
|
147
|
+
* @property {number} [remainingAfterReserve]
|
|
148
|
+
* @property {string} [error] 'BUDGET_EXCEEDED' | 'ROOM_NOT_FOUND'
|
|
149
|
+
*/
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* @typedef {Object} CommitResult
|
|
153
|
+
* @property {boolean} ok
|
|
154
|
+
* @property {boolean} [committed]
|
|
155
|
+
* @property {string} [warned] 'COMMIT_AFTER_EXPIRY'
|
|
156
|
+
* @property {number} [finalRemaining]
|
|
157
|
+
* @property {string} [error] 'NOT_FOUND' | 'ALREADY_FINALIZED'
|
|
158
|
+
*/
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* @typedef {Object} ReleaseResult
|
|
162
|
+
* @property {boolean} ok
|
|
163
|
+
* @property {boolean} [released]
|
|
164
|
+
* @property {string} [error] 'NOT_FOUND' | 'ALREADY_FINALIZED'
|
|
165
|
+
*/
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* @typedef {Object} RoomStatus
|
|
169
|
+
* @property {string} roomId
|
|
170
|
+
* @property {number} capUsd
|
|
171
|
+
* @property {number} spentUsd
|
|
172
|
+
* @property {number} reservedUsd
|
|
173
|
+
* @property {number} remainingUsd
|
|
174
|
+
* @property {number} txCount
|
|
175
|
+
* @property {number} [lastResetAt]
|
|
176
|
+
*/
|
|
177
|
+
|
|
178
|
+
// ── CostGate ──────────────────────────────────────────────────────────
|
|
179
|
+
|
|
180
|
+
export class CostGate {
|
|
181
|
+
/**
|
|
182
|
+
* @param {string|object} opts Either a path string or an
|
|
183
|
+
* `{dbPath, clock, logger}` options bag.
|
|
184
|
+
*/
|
|
185
|
+
constructor(opts = {}) {
|
|
186
|
+
if (typeof opts === 'string') {
|
|
187
|
+
this.dbPath = opts;
|
|
188
|
+
this.clock = () => Date.now();
|
|
189
|
+
this.logger = null;
|
|
190
|
+
} else {
|
|
191
|
+
this.dbPath = opts.dbPath;
|
|
192
|
+
this.clock = opts.clock || (() => Date.now());
|
|
193
|
+
this.logger = opts.logger || null;
|
|
194
|
+
}
|
|
195
|
+
if (!this.dbPath) {
|
|
196
|
+
throw new Error('CostGate requires dbPath');
|
|
197
|
+
}
|
|
198
|
+
mkdirSync(dirname(this.dbPath), { recursive: true });
|
|
199
|
+
/** @type {import('better-sqlite3').Database} */
|
|
200
|
+
this.db = new Database(this.dbPath);
|
|
201
|
+
this.db.exec(SCHEMA_SQL);
|
|
202
|
+
// Lock down the mode after first PRAGMA above.
|
|
203
|
+
this.db.pragma('journal_mode = WAL');
|
|
204
|
+
this.db.pragma('synchronous = NORMAL');
|
|
205
|
+
this.db.pragma('busy_timeout = 500');
|
|
206
|
+
|
|
207
|
+
// Prepared statements — re-bind per call so better-sqlite3 doesn't
|
|
208
|
+
// trip on changing SQL text.
|
|
209
|
+
/** @type {{ [k: string]: import('better-sqlite3').Statement<any[], any> }} */
|
|
210
|
+
this._stmts = {
|
|
211
|
+
registerRoom: this.db.prepare(
|
|
212
|
+
`INSERT INTO rooms (id, monthly_cap_usd, spent_usd, last_reset_at)
|
|
213
|
+
VALUES (?, ?, 0, ?)
|
|
214
|
+
ON CONFLICT(id) DO UPDATE SET monthly_cap_usd = excluded.monthly_cap_usd`,
|
|
215
|
+
),
|
|
216
|
+
getRoom: this.db.prepare(
|
|
217
|
+
`SELECT id, monthly_cap_usd, spent_usd, last_reset_at FROM rooms WHERE id = ?`,
|
|
218
|
+
),
|
|
219
|
+
listRooms: this.db.prepare(
|
|
220
|
+
`SELECT id, monthly_cap_usd, spent_usd, last_reset_at FROM rooms ORDER BY id`,
|
|
221
|
+
),
|
|
222
|
+
insertTx: this.db.prepare(
|
|
223
|
+
`INSERT INTO transactions
|
|
224
|
+
(tx_id, room_id, kind, amount_usd, caller_id, ts, expires_at, committed_at, metadata)
|
|
225
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
226
|
+
),
|
|
227
|
+
getTx: this.db.prepare(
|
|
228
|
+
`SELECT tx_id, room_id, kind, amount_usd, caller_id, ts, expires_at, committed_at
|
|
229
|
+
FROM transactions WHERE tx_id = ?`,
|
|
230
|
+
),
|
|
231
|
+
totalsThisCycle: this.db.prepare(
|
|
232
|
+
`SELECT
|
|
233
|
+
COALESCE(SUM(CASE WHEN kind IN ('committed','committed_post_expiry')
|
|
234
|
+
THEN amount_usd ELSE 0 END), 0) AS committed_total,
|
|
235
|
+
COALESCE(SUM(CASE WHEN kind = 'reserved'
|
|
236
|
+
AND (expires_at IS NULL OR expires_at > ?)
|
|
237
|
+
THEN amount_usd ELSE 0 END), 0) AS reserved_total
|
|
238
|
+
FROM transactions
|
|
239
|
+
WHERE room_id = ?`,
|
|
240
|
+
),
|
|
241
|
+
setSpentUsd: this.db.prepare(
|
|
242
|
+
`UPDATE rooms SET spent_usd = ? WHERE id = ?`,
|
|
243
|
+
),
|
|
244
|
+
flipTxState: this.db.prepare(
|
|
245
|
+
`UPDATE transactions SET kind = ?, committed_at = ? WHERE tx_id = ?`,
|
|
246
|
+
),
|
|
247
|
+
sweepExpired: this.db.prepare(
|
|
248
|
+
`UPDATE transactions SET kind = 'expired'
|
|
249
|
+
WHERE kind = 'reserved' AND expires_at IS NOT NULL AND expires_at < ?`,
|
|
250
|
+
),
|
|
251
|
+
listTxForRoom: this.db.prepare(
|
|
252
|
+
`SELECT tx_id, room_id, kind, amount_usd, caller_id, ts, expires_at, committed_at
|
|
253
|
+
FROM transactions WHERE room_id = ? ORDER BY ts`,
|
|
254
|
+
),
|
|
255
|
+
txCountForRoom: this.db.prepare(
|
|
256
|
+
`SELECT COUNT(*) AS n FROM transactions WHERE room_id = ?`,
|
|
257
|
+
),
|
|
258
|
+
};
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
close() {
|
|
262
|
+
try { this.db.close(); } catch (_e) { /* ignore double-close */ }
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
// ── Register ──────────────────────────────────────────────────────
|
|
266
|
+
|
|
267
|
+
registerRoom(roomId, capUsd) {
|
|
268
|
+
if (!roomId || typeof roomId !== 'string') {
|
|
269
|
+
throw new Error('roomId must be a non-empty string');
|
|
270
|
+
}
|
|
271
|
+
const cap = Number(capUsd);
|
|
272
|
+
if (!Number.isFinite(cap) || cap < 0) {
|
|
273
|
+
throw new Error('capUsd must be a non-negative finite number');
|
|
274
|
+
}
|
|
275
|
+
this._stmts.registerRoom.run(roomId, cap, this.clock());
|
|
276
|
+
return { ok: true, roomId, capUsd: cap };
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
// ── Reserve ───────────────────────────────────────────────────────
|
|
280
|
+
/**
|
|
281
|
+
* Atomically reserve budget inside a BEGIN IMMEDIATE transaction.
|
|
282
|
+
* @returns {ReserveResult}
|
|
283
|
+
*/
|
|
284
|
+
reserve(roomId, callerId, estimatedUsd, opts = {}) {
|
|
285
|
+
if (!Number.isFinite(Number(estimatedUsd)) || Number(estimatedUsd) < 0) {
|
|
286
|
+
throw new Error('estimatedUsd must be a non-negative finite number');
|
|
287
|
+
}
|
|
288
|
+
const amount = Number(estimatedUsd);
|
|
289
|
+
const expiryMs = clampReservationExpiry(opts.expiryMs);
|
|
290
|
+
const metadata = opts.metadata ? JSON.stringify(opts.metadata) : '{}';
|
|
291
|
+
|
|
292
|
+
const nowMs = this.clock();
|
|
293
|
+
const txId = `tx-${randomUUID()}`;
|
|
294
|
+
const expiresAt = nowMs + expiryMs;
|
|
295
|
+
|
|
296
|
+
// better-sqlite3 auto-commits each .run(). For atomicity we use the
|
|
297
|
+
// `transaction()` builder, which produces a single function that
|
|
298
|
+
// wraps all the .run() / .get() calls inside BEGIN/COMMIT (and
|
|
299
|
+
// ROLLBACK on throw). ADR-164.1 §3.2 explicitly mandates
|
|
300
|
+
// BEGIN IMMEDIATE — better-sqlite3 always opens with
|
|
301
|
+
// BEGIN DEFERRED by default; we force it via `BEGIN IMMEDIATE` for
|
|
302
|
+
// the atomic reserve flow by writing the transaction() builder
|
|
303
|
+
// as a single BEGIN IMMEDIATE block.
|
|
304
|
+
const reserveTxn = this.db.transaction(() => {
|
|
305
|
+
const room = this._stmts.getRoom.get(roomId);
|
|
306
|
+
if (!room) {
|
|
307
|
+
return { ok: false, error: 'ROOM_NOT_FOUND' };
|
|
308
|
+
}
|
|
309
|
+
const capUsd = Number(room.monthly_cap_usd);
|
|
310
|
+
const totals = this._stmts.totalsThisCycle.get(nowMs, roomId);
|
|
311
|
+
const committed = Number(totals?.committed_total ?? 0);
|
|
312
|
+
const reserved = Number(totals?.reserved_total ?? 0);
|
|
313
|
+
const projected = committed + reserved + amount;
|
|
314
|
+
if (projected > capUsd + 1e-9) {
|
|
315
|
+
return {
|
|
316
|
+
ok: false,
|
|
317
|
+
error: 'BUDGET_EXCEEDED',
|
|
318
|
+
committed,
|
|
319
|
+
reserved,
|
|
320
|
+
capUsd,
|
|
321
|
+
};
|
|
322
|
+
}
|
|
323
|
+
this._stmts.insertTx.run(
|
|
324
|
+
txId,
|
|
325
|
+
roomId,
|
|
326
|
+
'reserved',
|
|
327
|
+
amount,
|
|
328
|
+
callerId || '',
|
|
329
|
+
nowMs,
|
|
330
|
+
expiresAt,
|
|
331
|
+
null,
|
|
332
|
+
metadata,
|
|
333
|
+
);
|
|
334
|
+
return {
|
|
335
|
+
ok: true,
|
|
336
|
+
txId,
|
|
337
|
+
expiresAt,
|
|
338
|
+
remainingAfterReserve: capUsd - projected,
|
|
339
|
+
};
|
|
340
|
+
});
|
|
341
|
+
// Force BEGIN IMMEDIATE so we hold the write lock the entire
|
|
342
|
+
// time the cap check + insert runs (ADR-164.1 §3.2 + Bizar's
|
|
343
|
+
// "concurrent reserves must not double-spend" constraint).
|
|
344
|
+
reserveTxn.immediate;
|
|
345
|
+
const result = reserveTxn.immediate();
|
|
346
|
+
if (result.ok) {
|
|
347
|
+
if (this.logger) this.logger.info?.('cost.reserve', result);
|
|
348
|
+
}
|
|
349
|
+
return result;
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
// ── Commit ────────────────────────────────────────────────────────
|
|
353
|
+
/**
|
|
354
|
+
* Commit the reservation with the actual cost. Late commits are
|
|
355
|
+
* accepted (warn: 'COMMIT_AFTER_EXPIRY').
|
|
356
|
+
* @returns {CommitResult}
|
|
357
|
+
*/
|
|
358
|
+
commit(txId, actualUsd) {
|
|
359
|
+
if (!Number.isFinite(Number(actualUsd)) || Number(actualUsd) < 0) {
|
|
360
|
+
throw new Error('actualUsd must be a non-negative finite number');
|
|
361
|
+
}
|
|
362
|
+
const amount = Number(actualUsd);
|
|
363
|
+
const nowMs = this.clock();
|
|
364
|
+
|
|
365
|
+
const updateAmountAndState = this.db.prepare(
|
|
366
|
+
`UPDATE transactions SET kind = ?, amount_usd = ?, committed_at = ? WHERE tx_id = ?`,
|
|
367
|
+
);
|
|
368
|
+
|
|
369
|
+
const commitTxn = this.db.transaction(() => {
|
|
370
|
+
const row = this._stmts.getTx.get(txId);
|
|
371
|
+
if (!row) return { ok: false, error: 'NOT_FOUND' };
|
|
372
|
+
if (row.kind === 'committed' || row.kind === 'committed_post_expiry' || row.kind === 'released') {
|
|
373
|
+
return { ok: false, error: 'ALREADY_FINALIZED' };
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
// Late commit? Even if the row's state is still 'reserved' (or
|
|
377
|
+
// 'expired' if a sweeper beat us to it), the expires_at may
|
|
378
|
+
// have already passed. ADR-164.1 §5.3 — accept the charge and
|
|
379
|
+
// warn.
|
|
380
|
+
const late = row.expires_at != null && row.expires_at <= nowMs;
|
|
381
|
+
const newKind = late ? 'committed_post_expiry' : 'committed';
|
|
382
|
+
|
|
383
|
+
// IMPORTANT: replace the estimated amount with the actual cost
|
|
384
|
+
// on commit. The reservation's amount_usd reflects the upper
|
|
385
|
+
// bound; the actual cost may be smaller (cheaper model) or,
|
|
386
|
+
// in late-commit cases, may approximate the work that already
|
|
387
|
+
// ran. After this UPDATE, the SUM(... committed/committed_post_expiry)
|
|
388
|
+
// returns the real spend.
|
|
389
|
+
updateAmountAndState.run(newKind, amount, nowMs, txId);
|
|
390
|
+
|
|
391
|
+
// Update room.spent_usd to reflect the (now corrected) actual
|
|
392
|
+
// totals — kept in sync for status()'s `spentUsd`.
|
|
393
|
+
const totals = this._stmts.totalsThisCycle.get(nowMs + 1, row.room_id);
|
|
394
|
+
const committedTotal = Number(totals?.committed_total ?? 0);
|
|
395
|
+
this._stmts.setSpentUsd.run(committedTotal, row.room_id);
|
|
396
|
+
|
|
397
|
+
const room = this._stmts.getRoom.get(row.room_id);
|
|
398
|
+
const cap = Number(room?.monthly_cap_usd ?? 0);
|
|
399
|
+
const finalRemaining = cap - (committedTotal + Number(totals?.reserved_total ?? 0));
|
|
400
|
+
|
|
401
|
+
if (late) {
|
|
402
|
+
return {
|
|
403
|
+
ok: true,
|
|
404
|
+
warned: 'COMMIT_AFTER_EXPIRY',
|
|
405
|
+
finalRemaining,
|
|
406
|
+
};
|
|
407
|
+
}
|
|
408
|
+
return { ok: true, committed: true, finalRemaining };
|
|
409
|
+
});
|
|
410
|
+
return commitTxn.immediate();
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
// ── Release ───────────────────────────────────────────────────────
|
|
414
|
+
/**
|
|
415
|
+
* @returns {ReleaseResult}
|
|
416
|
+
*/
|
|
417
|
+
release(txId) {
|
|
418
|
+
const releaseTxn = this.db.transaction(() => {
|
|
419
|
+
const row = this._stmts.getTx.get(txId);
|
|
420
|
+
if (!row) return { ok: false, error: 'NOT_FOUND' };
|
|
421
|
+
if (row.kind !== 'reserved') {
|
|
422
|
+
return { ok: false, error: 'ALREADY_FINALIZED' };
|
|
423
|
+
}
|
|
424
|
+
this._stmts.flipTxState.run('released', this.clock(), txId);
|
|
425
|
+
return { ok: true, released: true };
|
|
426
|
+
});
|
|
427
|
+
return releaseTxn.immediate();
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
// ── Status ────────────────────────────────────────────────────────
|
|
431
|
+
/**
|
|
432
|
+
* @returns {RoomStatus | null}
|
|
433
|
+
*/
|
|
434
|
+
status(roomId) {
|
|
435
|
+
const room = this._stmts.getRoom.get(roomId);
|
|
436
|
+
if (!room) return null;
|
|
437
|
+
const capUsd = Number(room.monthly_cap_usd);
|
|
438
|
+
const now = this.clock();
|
|
439
|
+
const totals = this._stmts.totalsThisCycle.get(now, roomId);
|
|
440
|
+
const committed = Number(totals?.committed_total ?? 0);
|
|
441
|
+
const reserved = Number(totals?.reserved_total ?? 0);
|
|
442
|
+
const count = this._stmts.txCountForRoom.get(roomId);
|
|
443
|
+
return {
|
|
444
|
+
roomId,
|
|
445
|
+
capUsd,
|
|
446
|
+
spentUsd: committed,
|
|
447
|
+
reservedUsd: reserved,
|
|
448
|
+
remainingUsd: capUsd - (committed + reserved),
|
|
449
|
+
txCount: Number(count?.n ?? 0),
|
|
450
|
+
lastResetAt: Number(room.last_reset_at),
|
|
451
|
+
};
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
listRooms() {
|
|
455
|
+
const rows = this._stmts.listRooms.all();
|
|
456
|
+
return rows.map((r) => {
|
|
457
|
+
const totals = this._stmts.totalsThisCycle.get(this.clock(), r.id);
|
|
458
|
+
const committed = Number(totals?.committed_total ?? 0);
|
|
459
|
+
const reserved = Number(totals?.reserved_total ?? 0);
|
|
460
|
+
const capUsd = Number(r.monthly_cap_usd);
|
|
461
|
+
return {
|
|
462
|
+
roomId: r.id,
|
|
463
|
+
capUsd,
|
|
464
|
+
spentUsd: committed,
|
|
465
|
+
reservedUsd: reserved,
|
|
466
|
+
remainingUsd: capUsd - (committed + reserved),
|
|
467
|
+
lastResetAt: Number(r.last_reset_at),
|
|
468
|
+
};
|
|
469
|
+
});
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
// ── Sweep ─────────────────────────────────────────────────────────
|
|
473
|
+
/**
|
|
474
|
+
* Sweep expired reservations to 'expired' state. Returns the number
|
|
475
|
+
* of rows updated. Driven by setInterval at the integration site.
|
|
476
|
+
*/
|
|
477
|
+
sweepExpired(nowMs) {
|
|
478
|
+
const ts = nowMs == null ? this.clock() : Number(nowMs);
|
|
479
|
+
const result = this._stmts.sweepExpired.run(ts);
|
|
480
|
+
return Number(result.changes ?? 0);
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
// ── Test-only / introspection ────────────────────────────────────
|
|
484
|
+
/** Return raw transactions for a room. Used by tests + CLI status. */
|
|
485
|
+
listTransactions(roomId) {
|
|
486
|
+
return this._stmts.listTxForRoom.all(roomId);
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
// ── File-path helper (default location under .bizar/) ────────────────
|
|
491
|
+
|
|
492
|
+
import { homedir } from 'node:os';
|
|
493
|
+
import { join } from 'node:path';
|
|
494
|
+
|
|
495
|
+
/** Default DB path: $HOME/.bizar/cost-gate.db (or override via BIZAR_COST_GATE_DB). */
|
|
496
|
+
export function defaultDbPath() {
|
|
497
|
+
if (process.env.BIZAR_COST_GATE_DB && process.env.BIZAR_COST_GATE_DB.trim()) {
|
|
498
|
+
return process.env.BIZAR_COST_GATE_DB.trim();
|
|
499
|
+
}
|
|
500
|
+
return join(homedir(), '.bizar', 'cost-gate.db');
|
|
501
|
+
}
|
package/cli/dev-link.test.mjs
CHANGED
|
@@ -4,12 +4,12 @@
|
|
|
4
4
|
* Tests for the `bizar dev-link` / `bizar dev-unlink` subcommands.
|
|
5
5
|
* Uses Node's built-in node:test (no external test framework).
|
|
6
6
|
*
|
|
7
|
-
* Strategy: mock HOME (and XDG_CONFIG_HOME) so
|
|
7
|
+
* Strategy: mock HOME (and XDG_CONFIG_HOME) so claudeConfigDir() in
|
|
8
8
|
* cli/utils.mjs returns a path inside a tmpdir, and exercise the
|
|
9
9
|
* create/remove symlink behavior against a controlled filesystem.
|
|
10
10
|
*
|
|
11
11
|
* Note: We mock HOME *before* importing dev-link.mjs because the module
|
|
12
|
-
* imports
|
|
12
|
+
* imports claudeConfigDir transitively from utils.mjs. The path is
|
|
13
13
|
* resolved at call time, though, so the mock just needs to be in place
|
|
14
14
|
* when the functions run.
|
|
15
15
|
*/
|
|
@@ -40,28 +40,28 @@ const ORIG_HOME = process.env.HOME;
|
|
|
40
40
|
const ORIG_XDG = process.env.XDG_CONFIG_HOME;
|
|
41
41
|
|
|
42
42
|
/**
|
|
43
|
-
* Point HOME at a fresh tmpdir so the module's
|
|
44
|
-
* resolves inside it (via the fallback `<HOME>/.
|
|
43
|
+
* Point HOME at a fresh tmpdir so the module's claudeConfigDir()
|
|
44
|
+
* resolves inside it (via the fallback `<HOME>/.claude`).
|
|
45
45
|
* Returns the tmpdir path.
|
|
46
46
|
*
|
|
47
47
|
* Note: we deliberately do NOT set XDG_CONFIG_HOME here. The
|
|
48
|
-
*
|
|
49
|
-
* direct parent (so `XDG_CONFIG_HOME=~/.config` → `~/.
|
|
48
|
+
* claudeConfigDir() helper treats a set XDG_CONFIG_HOME as the
|
|
49
|
+
* direct parent (so `XDG_CONFIG_HOME=~/.config` → `~/.claude`,
|
|
50
50
|
* matching the standard layout). Setting it to a raw tmpdir would
|
|
51
|
-
* produce `<tmpdir>/
|
|
52
|
-
* `<tmpdir>/.
|
|
51
|
+
* produce `<tmpdir>/claude` instead of the expected
|
|
52
|
+
* `<tmpdir>/.claude` and break path alignment with the test.
|
|
53
53
|
*/
|
|
54
54
|
function freshHome() {
|
|
55
55
|
const home = mkdtempSync(join(tmpdir(), 'bizar-devlink-'));
|
|
56
56
|
process.env.HOME = home;
|
|
57
|
-
delete process.env.
|
|
58
|
-
delete process.env.
|
|
57
|
+
delete process.env.CLAUDE_CONFIG_DIR;
|
|
58
|
+
delete process.env.BIZAR_LEGACY_CLAUDE_DIR;
|
|
59
59
|
return home;
|
|
60
60
|
}
|
|
61
61
|
|
|
62
62
|
/** Path to the deployed plugin dir under the mocked HOME. */
|
|
63
63
|
function pluginDest(home) {
|
|
64
|
-
return join(home, '.
|
|
64
|
+
return join(home, '.claude', 'plugins', 'bizar');
|
|
65
65
|
}
|
|
66
66
|
|
|
67
67
|
after(() => {
|