@polderlabs/bizar 6.2.5 → 6.3.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/settings.json +116 -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-B58aiJiE.js} +1 -1
- package/bizar-dash/dist/assets/{EnvVarsSection-DXM8gRm_.js.map → EnvVarsSection-B58aiJiE.js.map} +1 -1
- package/bizar-dash/dist/assets/{MobileChat-BnKN_Ks_.js → MobileChat-BJrqwVDd.js} +1 -1
- package/bizar-dash/dist/assets/{MobileChat-BnKN_Ks_.js.map → MobileChat-BJrqwVDd.js.map} +1 -1
- package/bizar-dash/dist/assets/{MobileSettings-DjCPxC-Q.js → MobileSettings-CEQNJNLJ.js} +1 -1
- package/bizar-dash/dist/assets/{MobileSettings-DjCPxC-Q.js.map → MobileSettings-CEQNJNLJ.js.map} +1 -1
- package/bizar-dash/dist/assets/{main-DYiZqMrn.js → main-IvfQAOfy.js} +1 -1
- package/bizar-dash/dist/assets/{main-DYiZqMrn.js.map → main-IvfQAOfy.js.map} +1 -1
- package/bizar-dash/dist/assets/{markdown-C6mXtQxD.js → markdown-tOLaD6nm.js} +1 -1
- package/bizar-dash/dist/assets/{markdown-C6mXtQxD.js.map → markdown-tOLaD6nm.js.map} +1 -1
- package/bizar-dash/dist/assets/{mobile-CMHqtLV2.js → mobile-DYCHcUpq.js} +1 -1
- package/bizar-dash/dist/assets/{mobile-CMHqtLV2.js.map → mobile-DYCHcUpq.js.map} +1 -1
- package/bizar-dash/dist/assets/{mobile-layout-3jIhHX_p.js → mobile-layout-CBHjpwsb.js} +2 -2
- package/bizar-dash/dist/assets/{mobile-layout-3jIhHX_p.js.map → mobile-layout-CBHjpwsb.js.map} +1 -1
- package/bizar-dash/dist/assets/{useSlashCommands-DtITw8Xv.js → useSlashCommands-Bd7_FA6U.js} +2 -2
- package/bizar-dash/dist/assets/{useSlashCommands-DtITw8Xv.js.map → useSlashCommands-Bd7_FA6U.js.map} +1 -1
- package/bizar-dash/dist/assets/{vendor-CeHGtduv.js → vendor-C843201K.js} +12 -12
- package/bizar-dash/dist/assets/vendor-C843201K.js.map +1 -0
- package/bizar-dash/dist/index.html +6 -6
- package/bizar-dash/dist/mobile.html +2 -2
- package/bizar-dash/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +1 -1
- package/bizar-dash/src/server/api.mjs +4 -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/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/tasks.mjs +20 -28
- package/bizar-dash/src/server/task-delegator.mjs +76 -77
- package/bizar-dash/src/web/components/chat/useChat.ts +23 -27
- package/bizar-dash/src/web/views/Chat.tsx +8 -8
- package/cli/bin.mjs +17 -18
- package/cli/commands/claude-cmd.mjs +348 -0
- package/cli/commands/install.mjs +12 -11
- package/cli/commands/validate.mjs +252 -325
- package/cli/dev-link.test.mjs +11 -11
- package/cli/doctor.mjs +131 -213
- package/cli/install.mjs +33 -37
- package/cli/install.test.mjs +9 -9
- package/cli/provision-claude.mjs +893 -0
- package/cli/provision.mjs +224 -227
- package/cli/utils.mjs +72 -58
- package/config/AGENTS.md +28 -28
- package/install.sh +66 -44
- package/package.json +19 -11
- package/packages/sdk/ARCHITECTURE.md +40 -36
- package/packages/sdk/package-lock.json +32 -0
- package/packages/sdk/package.json +15 -5
- package/{plugins/bizar → packages/sdk}/src/dangerous-patterns.ts +6 -28
- package/packages/sdk/src/fingerprint.ts +0 -0
- package/packages/sdk/src/index.ts +38 -73
- package/packages/sdk/src/mcp/bin.ts +51 -0
- package/packages/sdk/src/mcp/server.ts +498 -0
- package/{plugins/bizar/src/memory-vault.ts → packages/sdk/src/memory/index.ts} +18 -21
- package/packages/sdk/tests/sdk.test.mjs +148 -0
- package/packages/sdk/vitest.config.ts +1 -1
- package/scripts/bh-full-e2e.mjs +166 -363
- 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/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
|
@@ -1,351 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* settings.test.ts
|
|
3
|
-
*
|
|
4
|
-
* Tests for SettingsStore — read/write round-trip, atomic writes,
|
|
5
|
-
* corrupt-file fallback, validation, and concurrency.
|
|
6
|
-
*
|
|
7
|
-
* Groups:
|
|
8
|
-
* 1. defaults-on-missing-file
|
|
9
|
-
* 2. defaults-on-corrupt-file
|
|
10
|
-
* 3. round-trip update()
|
|
11
|
-
* 4. typed set() for each key
|
|
12
|
-
* 5. invalid-value rejection (clamped to safe value)
|
|
13
|
-
* 6. file path expansion (~ → home)
|
|
14
|
-
* 7. concurrent update() serialization
|
|
15
|
-
* 8. sequential update() round-trip
|
|
16
|
-
*/
|
|
17
|
-
|
|
18
|
-
import { describe, test, expect, beforeEach, afterEach } from "bun:test";
|
|
19
|
-
import {
|
|
20
|
-
rmSync,
|
|
21
|
-
mkdirSync,
|
|
22
|
-
writeFileSync,
|
|
23
|
-
existsSync,
|
|
24
|
-
readFileSync,
|
|
25
|
-
} from "node:fs";
|
|
26
|
-
import path from "node:path";
|
|
27
|
-
|
|
28
|
-
import {
|
|
29
|
-
SettingsStore,
|
|
30
|
-
DEFAULT_PLAN_SETTINGS,
|
|
31
|
-
type PlanSettings,
|
|
32
|
-
} from "../src/settings";
|
|
33
|
-
import os from "node:os";
|
|
34
|
-
|
|
35
|
-
// Minimal mock logger
|
|
36
|
-
class MockLogger {
|
|
37
|
-
messages: Array<{ level: string; message: string }> = [];
|
|
38
|
-
log(opts: { level: string; message: string }) {
|
|
39
|
-
this.messages.push(opts);
|
|
40
|
-
}
|
|
41
|
-
debug(m: string) { this.messages.push({ level: "debug", message: m }); }
|
|
42
|
-
info(m: string) { this.messages.push({ level: "info", message: m }); }
|
|
43
|
-
warn(m: string) { this.messages.push({ level: "warn", message: m }); }
|
|
44
|
-
error(m: string) { this.messages.push({ level: "error", message: m }); }
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
const TEST_DIR = path.join(os.tmpdir(), "bizar-settings-test");
|
|
48
|
-
|
|
49
|
-
beforeEach(() => {
|
|
50
|
-
try { rmSync(TEST_DIR, { recursive: true, force: true }); } catch { /* ok */ }
|
|
51
|
-
mkdirSync(TEST_DIR, { recursive: true });
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
afterEach(() => {
|
|
55
|
-
try { rmSync(TEST_DIR, { recursive: true, force: true }); } catch { /* ok */ }
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
function makeLogger(): MockLogger {
|
|
59
|
-
return new MockLogger();
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
// ===========================================================================
|
|
63
|
-
// Group 1 — defaults on missing file
|
|
64
|
-
// ===========================================================================
|
|
65
|
-
|
|
66
|
-
describe("SettingsStore — missing file", () => {
|
|
67
|
-
test("returns DEFAULT_PLAN_SETTINGS when file does not exist", async () => {
|
|
68
|
-
const logger = makeLogger();
|
|
69
|
-
const store = new SettingsStore(TEST_DIR, logger);
|
|
70
|
-
const s = await store.get();
|
|
71
|
-
expect(s).toEqual(DEFAULT_PLAN_SETTINGS);
|
|
72
|
-
});
|
|
73
|
-
|
|
74
|
-
test("does not log a warning when file is simply missing", async () => {
|
|
75
|
-
const logger = makeLogger();
|
|
76
|
-
const store = new SettingsStore(TEST_DIR, logger);
|
|
77
|
-
await store.get();
|
|
78
|
-
// ENOENT is silent — only SyntaxError / schema errors log.
|
|
79
|
-
const warns = logger.messages.filter((m) => m.level === "warn");
|
|
80
|
-
expect(warns).toHaveLength(0);
|
|
81
|
-
});
|
|
82
|
-
});
|
|
83
|
-
|
|
84
|
-
// ===========================================================================
|
|
85
|
-
// Group 2 — corrupt file fallback
|
|
86
|
-
// ===========================================================================
|
|
87
|
-
|
|
88
|
-
describe("SettingsStore — corrupt file", () => {
|
|
89
|
-
test("returns defaults + warning when JSON is invalid", async () => {
|
|
90
|
-
const logger = makeLogger();
|
|
91
|
-
const filePath = path.join(TEST_DIR, "plan-settings.json");
|
|
92
|
-
writeFileSync(filePath, "{ not valid json :: !!", "utf8");
|
|
93
|
-
const store = new SettingsStore(TEST_DIR, logger);
|
|
94
|
-
const s = await store.get();
|
|
95
|
-
expect(s).toEqual(DEFAULT_PLAN_SETTINGS);
|
|
96
|
-
expect(logger.messages.some((m) => m.level === "warn" && m.message.includes("corrupt"))).toBe(true);
|
|
97
|
-
});
|
|
98
|
-
|
|
99
|
-
test("returns defaults + warning when JSON is null", async () => {
|
|
100
|
-
const logger = makeLogger();
|
|
101
|
-
const filePath = path.join(TEST_DIR, "plan-settings.json");
|
|
102
|
-
writeFileSync(filePath, "null", "utf8");
|
|
103
|
-
const store = new SettingsStore(TEST_DIR, logger);
|
|
104
|
-
const s = await store.get();
|
|
105
|
-
expect(s).toEqual(DEFAULT_PLAN_SETTINGS);
|
|
106
|
-
});
|
|
107
|
-
|
|
108
|
-
test("returns defaults + warning when top-level is an array", async () => {
|
|
109
|
-
const logger = makeLogger();
|
|
110
|
-
const filePath = path.join(TEST_DIR, "plan-settings.json");
|
|
111
|
-
writeFileSync(filePath, "[]", "utf8");
|
|
112
|
-
const store = new SettingsStore(TEST_DIR, logger);
|
|
113
|
-
const s = await store.get();
|
|
114
|
-
expect(s).toEqual(DEFAULT_PLAN_SETTINGS);
|
|
115
|
-
});
|
|
116
|
-
|
|
117
|
-
test("clamps invalid values in valid JSON without losing the file", async () => {
|
|
118
|
-
const logger = makeLogger();
|
|
119
|
-
const filePath = path.join(TEST_DIR, "plan-settings.json");
|
|
120
|
-
writeFileSync(
|
|
121
|
-
filePath,
|
|
122
|
-
JSON.stringify({
|
|
123
|
-
visualPlanEnabled: "yes", // wrong type
|
|
124
|
-
defaultTemplate: "nonsense", // unknown
|
|
125
|
-
lastUsedSlug: 42, // wrong type
|
|
126
|
-
}),
|
|
127
|
-
"utf8",
|
|
128
|
-
);
|
|
129
|
-
const store = new SettingsStore(TEST_DIR, logger);
|
|
130
|
-
const s = await store.get();
|
|
131
|
-
// Invalid values clamped to defaults, but the file is preserved.
|
|
132
|
-
expect(s.visualPlanEnabled).toBe(false);
|
|
133
|
-
expect(s.defaultTemplate).toBe("blank");
|
|
134
|
-
expect(s.lastUsedSlug).toBeNull();
|
|
135
|
-
expect(existsSync(filePath)).toBe(true);
|
|
136
|
-
});
|
|
137
|
-
});
|
|
138
|
-
|
|
139
|
-
// ===========================================================================
|
|
140
|
-
// Group 3 — round-trip update()
|
|
141
|
-
// ===========================================================================
|
|
142
|
-
|
|
143
|
-
describe("SettingsStore — update() round-trip", () => {
|
|
144
|
-
test("update() persists a single field and returns the merged state", async () => {
|
|
145
|
-
const logger = makeLogger();
|
|
146
|
-
const store = new SettingsStore(TEST_DIR, logger);
|
|
147
|
-
const after = await store.update({ visualPlanEnabled: true });
|
|
148
|
-
expect(after.visualPlanEnabled).toBe(true);
|
|
149
|
-
expect(after.defaultTemplate).toBe("blank"); // unchanged
|
|
150
|
-
// File written
|
|
151
|
-
const raw = JSON.parse(readFileSync(path.join(TEST_DIR, "plan-settings.json"), "utf8"));
|
|
152
|
-
expect(raw.visualPlanEnabled).toBe(true);
|
|
153
|
-
});
|
|
154
|
-
|
|
155
|
-
test("update() merges multiple fields in a single call", async () => {
|
|
156
|
-
const logger = makeLogger();
|
|
157
|
-
const store = new SettingsStore(TEST_DIR, logger);
|
|
158
|
-
await store.update({
|
|
159
|
-
visualPlanEnabled: true,
|
|
160
|
-
defaultTemplate: "feature-design",
|
|
161
|
-
lastUsedSlug: "my-feature",
|
|
162
|
-
});
|
|
163
|
-
const loaded = await store.get();
|
|
164
|
-
expect(loaded).toEqual({
|
|
165
|
-
visualPlanEnabled: true,
|
|
166
|
-
defaultTemplate: "feature-design",
|
|
167
|
-
lastUsedSlug: "my-feature",
|
|
168
|
-
});
|
|
169
|
-
});
|
|
170
|
-
|
|
171
|
-
test("update() preserves existing fields not in the patch", async () => {
|
|
172
|
-
const logger = makeLogger();
|
|
173
|
-
const store = new SettingsStore(TEST_DIR, logger);
|
|
174
|
-
await store.update({ visualPlanEnabled: true, lastUsedSlug: "alpha" });
|
|
175
|
-
await store.update({ defaultTemplate: "decision-record" });
|
|
176
|
-
const loaded = await store.get();
|
|
177
|
-
expect(loaded.visualPlanEnabled).toBe(true); // preserved
|
|
178
|
-
expect(loaded.lastUsedSlug).toBe("alpha"); // preserved
|
|
179
|
-
expect(loaded.defaultTemplate).toBe("decision-record"); // updated
|
|
180
|
-
});
|
|
181
|
-
});
|
|
182
|
-
|
|
183
|
-
// ===========================================================================
|
|
184
|
-
// Group 4 — typed set() for each key
|
|
185
|
-
// ===========================================================================
|
|
186
|
-
|
|
187
|
-
describe("SettingsStore — set() with typed keys", () => {
|
|
188
|
-
test("set('visualPlanEnabled', true) persists correctly", async () => {
|
|
189
|
-
const logger = makeLogger();
|
|
190
|
-
const store = new SettingsStore(TEST_DIR, logger);
|
|
191
|
-
const after = await store.set("visualPlanEnabled", true);
|
|
192
|
-
expect(after.visualPlanEnabled).toBe(true);
|
|
193
|
-
});
|
|
194
|
-
|
|
195
|
-
test("set('defaultTemplate', 'feature-design') persists correctly", async () => {
|
|
196
|
-
const logger = makeLogger();
|
|
197
|
-
const store = new SettingsStore(TEST_DIR, logger);
|
|
198
|
-
const after = await store.set("defaultTemplate", "feature-design");
|
|
199
|
-
expect(after.defaultTemplate).toBe("feature-design");
|
|
200
|
-
});
|
|
201
|
-
|
|
202
|
-
test("set('lastUsedSlug', 'foo') persists correctly", async () => {
|
|
203
|
-
const logger = makeLogger();
|
|
204
|
-
const store = new SettingsStore(TEST_DIR, logger);
|
|
205
|
-
const after = await store.set("lastUsedSlug", "foo");
|
|
206
|
-
expect(after.lastUsedSlug).toBe("foo");
|
|
207
|
-
});
|
|
208
|
-
|
|
209
|
-
test("set('lastUsedSlug', null) clears the slug", async () => {
|
|
210
|
-
const logger = makeLogger();
|
|
211
|
-
const store = new SettingsStore(TEST_DIR, logger);
|
|
212
|
-
await store.set("lastUsedSlug", "foo");
|
|
213
|
-
const after = await store.set("lastUsedSlug", null);
|
|
214
|
-
expect(after.lastUsedSlug).toBeNull();
|
|
215
|
-
});
|
|
216
|
-
});
|
|
217
|
-
|
|
218
|
-
// ===========================================================================
|
|
219
|
-
// Group 5 — invalid-value rejection
|
|
220
|
-
// ===========================================================================
|
|
221
|
-
|
|
222
|
-
describe("SettingsStore — invalid-value rejection", () => {
|
|
223
|
-
test("set('defaultTemplate', 'nonsense') is rejected, default preserved", async () => {
|
|
224
|
-
const logger = makeLogger();
|
|
225
|
-
const store = new SettingsStore(TEST_DIR, logger);
|
|
226
|
-
// Force a type-coercion via cast
|
|
227
|
-
const after = await store.set("defaultTemplate", "nonsense" as never);
|
|
228
|
-
expect(after.defaultTemplate).toBe("blank"); // unchanged
|
|
229
|
-
expect(logger.messages.some((m) => m.level === "warn")).toBe(true);
|
|
230
|
-
});
|
|
231
|
-
|
|
232
|
-
test("update() with invalid template is silently ignored", async () => {
|
|
233
|
-
const logger = makeLogger();
|
|
234
|
-
const store = new SettingsStore(TEST_DIR, logger);
|
|
235
|
-
await store.set("defaultTemplate", "feature-design");
|
|
236
|
-
await store.update({ defaultTemplate: "nonsense" as never });
|
|
237
|
-
const after = await store.get();
|
|
238
|
-
expect(after.defaultTemplate).toBe("feature-design"); // unchanged
|
|
239
|
-
});
|
|
240
|
-
});
|
|
241
|
-
|
|
242
|
-
// ===========================================================================
|
|
243
|
-
// Group 6 — file path expansion
|
|
244
|
-
// ===========================================================================
|
|
245
|
-
|
|
246
|
-
describe("SettingsStore — file path", () => {
|
|
247
|
-
test("constructor expands ~ in settingsDir", () => {
|
|
248
|
-
const logger = makeLogger();
|
|
249
|
-
const store = new SettingsStore("~/.cache/test-bizar", logger);
|
|
250
|
-
const expected = path.join(os.homedir(), ".cache/test-bizar/plan-settings.json");
|
|
251
|
-
expect(store.getFilePath()).toBe(expected);
|
|
252
|
-
});
|
|
253
|
-
|
|
254
|
-
test("settings written with ~ path land in the expanded location", async () => {
|
|
255
|
-
const tmp = path.join(os.tmpdir(), "bizar-settings-expansion-test");
|
|
256
|
-
rmSync(tmp, { recursive: true, force: true });
|
|
257
|
-
try {
|
|
258
|
-
const logger = makeLogger();
|
|
259
|
-
const store = new SettingsStore(tmp, logger);
|
|
260
|
-
await store.update({ visualPlanEnabled: true });
|
|
261
|
-
expect(existsSync(path.join(tmp, "plan-settings.json"))).toBe(true);
|
|
262
|
-
} finally {
|
|
263
|
-
rmSync(tmp, { recursive: true, force: true });
|
|
264
|
-
}
|
|
265
|
-
});
|
|
266
|
-
});
|
|
267
|
-
|
|
268
|
-
// ===========================================================================
|
|
269
|
-
// Group 7 — concurrent update() serialization
|
|
270
|
-
// ===========================================================================
|
|
271
|
-
|
|
272
|
-
describe("SettingsStore — concurrent updates", () => {
|
|
273
|
-
test("concurrent update() calls do not corrupt the file", async () => {
|
|
274
|
-
const logger = makeLogger();
|
|
275
|
-
const store = new SettingsStore(TEST_DIR, logger);
|
|
276
|
-
|
|
277
|
-
// Fire many updates in parallel that each flip the visualPlanEnabled
|
|
278
|
-
// flag. The mutex guarantees no write is lost or interleaved.
|
|
279
|
-
const updates = Array.from({ length: 30 }, (_, i) =>
|
|
280
|
-
store.update({ visualPlanEnabled: i % 2 === 0 }),
|
|
281
|
-
);
|
|
282
|
-
const results = await Promise.all(updates);
|
|
283
|
-
|
|
284
|
-
// All returned states are valid (either true or false).
|
|
285
|
-
for (const r of results) {
|
|
286
|
-
expect(typeof r.visualPlanEnabled).toBe("boolean");
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
// The on-disk file is valid JSON (parseable).
|
|
290
|
-
const filePath = path.join(TEST_DIR, "plan-settings.json");
|
|
291
|
-
expect(existsSync(filePath)).toBe(true);
|
|
292
|
-
const raw = readFileSync(filePath, "utf8");
|
|
293
|
-
const parsed = JSON.parse(raw) as PlanSettings;
|
|
294
|
-
expect(typeof parsed.visualPlanEnabled).toBe("boolean");
|
|
295
|
-
});
|
|
296
|
-
|
|
297
|
-
test("concurrent set() calls do not corrupt the file", async () => {
|
|
298
|
-
const logger = makeLogger();
|
|
299
|
-
const store = new SettingsStore(TEST_DIR, logger);
|
|
300
|
-
const sets = Array.from({ length: 20 }, (_, i) =>
|
|
301
|
-
store.set("lastUsedSlug", `slug-${i}`),
|
|
302
|
-
);
|
|
303
|
-
const results = await Promise.all(sets);
|
|
304
|
-
for (const r of results) {
|
|
305
|
-
expect(typeof r.lastUsedSlug).toBe("string");
|
|
306
|
-
expect(r.lastUsedSlug).toMatch(/^slug-\d+$/);
|
|
307
|
-
}
|
|
308
|
-
// File is valid JSON
|
|
309
|
-
const filePath = path.join(TEST_DIR, "plan-settings.json");
|
|
310
|
-
const parsed = JSON.parse(readFileSync(filePath, "utf8"));
|
|
311
|
-
expect(parsed.lastUsedSlug).toMatch(/^slug-\d+$/);
|
|
312
|
-
});
|
|
313
|
-
});
|
|
314
|
-
|
|
315
|
-
// ===========================================================================
|
|
316
|
-
// Group 8 — atomic write (no .tmp file left behind)
|
|
317
|
-
// ===========================================================================
|
|
318
|
-
|
|
319
|
-
describe("SettingsStore — atomic write", () => {
|
|
320
|
-
test("update() uses atomic rename (no .tmp file on disk)", async () => {
|
|
321
|
-
const logger = makeLogger();
|
|
322
|
-
const store = new SettingsStore(TEST_DIR, logger);
|
|
323
|
-
await store.update({ visualPlanEnabled: true });
|
|
324
|
-
const filePath = path.join(TEST_DIR, "plan-settings.json");
|
|
325
|
-
expect(existsSync(filePath)).toBe(true);
|
|
326
|
-
expect(existsSync(`${filePath}.tmp`)).toBe(false);
|
|
327
|
-
});
|
|
328
|
-
});
|
|
329
|
-
|
|
330
|
-
// ===========================================================================
|
|
331
|
-
// Group 9 — round-trip persistence
|
|
332
|
-
// ===========================================================================
|
|
333
|
-
|
|
334
|
-
describe("SettingsStore — full round-trip", () => {
|
|
335
|
-
test("write, then create a fresh store, reads back the same value", async () => {
|
|
336
|
-
const logger1 = makeLogger();
|
|
337
|
-
const store1 = new SettingsStore(TEST_DIR, logger1);
|
|
338
|
-
const written = await store1.update({
|
|
339
|
-
visualPlanEnabled: true,
|
|
340
|
-
defaultTemplate: "bug-investigation",
|
|
341
|
-
lastUsedSlug: "round-trip",
|
|
342
|
-
});
|
|
343
|
-
|
|
344
|
-
// Fresh store from the same dir
|
|
345
|
-
const logger2 = makeLogger();
|
|
346
|
-
const store2 = new SettingsStore(TEST_DIR, logger2);
|
|
347
|
-
const read = await store2.get();
|
|
348
|
-
|
|
349
|
-
expect(read).toEqual(written);
|
|
350
|
-
});
|
|
351
|
-
});
|