mixdog 0.7.1
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-plugin/marketplace.json +31 -0
- package/.claude-plugin/plugin.json +20 -0
- package/.gitattributes +34 -0
- package/.mcp.json +14 -0
- package/ARCHITECTURE.md +77 -0
- package/CHANGELOG.md +7 -0
- package/CONTRIBUTING.md +45 -0
- package/DATA-FLOW.md +79 -0
- package/LICENSE +21 -0
- package/README.md +389 -0
- package/SECURITY.md +138 -0
- package/UNINSTALL.md +112 -0
- package/agents/maintenance.md +5 -0
- package/agents/memory-classification.md +30 -0
- package/agents/scheduler-task.md +18 -0
- package/agents/webhook-handler.md +27 -0
- package/agents/worker.md +24 -0
- package/bin/bridge +133 -0
- package/bin/statusline-launcher.mjs +78 -0
- package/bin/statusline-lib.mjs +550 -0
- package/bin/statusline.mjs +607 -0
- package/bun.lock +802 -0
- package/commands/config.md +16 -0
- package/commands/doctor.md +13 -0
- package/commands/setup.md +17 -0
- package/defaults/cycle3-review-prompt.md +90 -0
- package/defaults/hidden-roles.json +65 -0
- package/defaults/memory-chunk-prompt.md +63 -0
- package/defaults/memory-promote-prompt.md +135 -0
- package/defaults/mixdog-config.template.json +27 -0
- package/defaults/user-workflow.json +8 -0
- package/defaults/user-workflow.md +12 -0
- package/hooks/hooks.json +73 -0
- package/hooks/lib/active-instance.cjs +77 -0
- package/hooks/lib/permission-evaluator.cjs +411 -0
- package/hooks/lib/permission-route.cjs +63 -0
- package/hooks/lib/permission-rules.cjs +170 -0
- package/hooks/lib/settings-loader.cjs +116 -0
- package/hooks/post-tool-use.cjs +84 -0
- package/hooks/pre-mcp-sandbox.cjs +158 -0
- package/hooks/pre-tool-subagent.cjs +253 -0
- package/hooks/session-start.cjs +1372 -0
- package/hooks/turn-timer.cjs +82 -0
- package/lib/claude-md-writer.cjs +386 -0
- package/lib/config-cjs.cjs +61 -0
- package/lib/hook-pipe-path.cjs +10 -0
- package/lib/keychain-cjs.cjs +263 -0
- package/lib/plugin-paths.cjs +61 -0
- package/lib/rules-builder.cjs +241 -0
- package/lib/text-utils.cjs +61 -0
- package/native/README.md +117 -0
- package/native/prebuilt/linux-aarch64/mixdog-shim +0 -0
- package/native/prebuilt/linux-x86_64/mixdog-shim +0 -0
- package/native/prebuilt/macos-aarch64/mixdog-shim +0 -0
- package/native/prebuilt/macos-x86_64/mixdog-shim +0 -0
- package/native/prebuilt/windows-x86_64/mixdog-shim.exe +0 -0
- package/package.json +107 -0
- package/prompts/code-review.txt +16 -0
- package/prompts/security-audit.txt +17 -0
- package/rules/bridge/00-common.md +39 -0
- package/rules/bridge/20-skip-protocol.md +18 -0
- package/rules/bridge/30-explorer.md +33 -0
- package/rules/bridge/40-cycle1-agent.md +52 -0
- package/rules/bridge/41-cycle2-agent.md +62 -0
- package/rules/bridge/42-cycle3-agent.md +44 -0
- package/rules/lead/00-tool-lead.md +61 -0
- package/rules/lead/01-general.md +23 -0
- package/rules/lead/02-channels.md +49 -0
- package/rules/lead/03-team.md +27 -0
- package/rules/lead/04-workflow.md +20 -0
- package/rules/shared/00-language.md +14 -0
- package/rules/shared/01-tool.md +138 -0
- package/scripts/bootstrap.mjs +184 -0
- package/scripts/bridge-unify-smoke.mjs +308 -0
- package/scripts/build-runtime-linux.sh +348 -0
- package/scripts/build-runtime-macos.sh +217 -0
- package/scripts/build-runtime-windows.ps1 +242 -0
- package/scripts/builtin-utils-smoke.mjs +392 -0
- package/scripts/check-json.mjs +45 -0
- package/scripts/check-syntax-changed.mjs +102 -0
- package/scripts/check-syntax.mjs +58 -0
- package/scripts/code-graph-batch.test.mjs +33 -0
- package/scripts/config-preserve-smoke.mjs +180 -0
- package/scripts/doctor.mjs +484 -0
- package/scripts/edit-normalize-fuzz.mjs +130 -0
- package/scripts/edit-normalize-smoke.mjs +401 -0
- package/scripts/edit-operation-smoke.mjs +369 -0
- package/scripts/edit2-smoke.mjs +63 -0
- package/scripts/fuzzy-e2e.mjs +28 -0
- package/scripts/fuzzy-smoke.mjs +26 -0
- package/scripts/generate-runtime-manifest.mjs +166 -0
- package/scripts/guard-smoke.mjs +66 -0
- package/scripts/hidden-role-schema-smoke.mjs +162 -0
- package/scripts/hook-routing-smoke.mjs +29 -0
- package/scripts/inject-input.ps1 +204 -0
- package/scripts/io-complex-smoke.mjs +667 -0
- package/scripts/io-explore-bench.mjs +424 -0
- package/scripts/io-guardrails-smoke.mjs +205 -0
- package/scripts/io-mini-bench-baseline.json +11 -0
- package/scripts/io-mini-bench.mjs +216 -0
- package/scripts/io-route-harness.mjs +933 -0
- package/scripts/io-telemetry-report.mjs +691 -0
- package/scripts/mutation-bench.mjs +564 -0
- package/scripts/mutation-io-smoke.mjs +1081 -0
- package/scripts/native-patch-bridge-smoke.mjs +288 -0
- package/scripts/native-patch-smoke.mjs +304 -0
- package/scripts/patch-interior-context-smoke.mjs +49 -0
- package/scripts/patch-newline-utf8-smoke.mjs +157 -0
- package/scripts/perf-hook-smoke.mjs +71 -0
- package/scripts/permission-eval-smoke.mjs +426 -0
- package/scripts/prep-patch.mjs +53 -0
- package/scripts/prep-shim.mjs +96 -0
- package/scripts/provider-cache-smoke.mjs +687 -0
- package/scripts/report-runtime-health.mjs +132 -0
- package/scripts/run-mcp.mjs +1547 -0
- package/scripts/salvage-v4a-shatter.test.mjs +58 -0
- package/scripts/scoped-cache-io-smoke.mjs +103 -0
- package/scripts/shell-policy-round3-smoke.mjs +46 -0
- package/scripts/smoke-runtime-negative.ps1 +100 -0
- package/scripts/smoke-runtime-negative.sh +95 -0
- package/scripts/stall-policy-smoke.mjs +50 -0
- package/scripts/start-memory-worker.mjs +23 -0
- package/scripts/statusline-launcher-smoke.mjs +82 -0
- package/scripts/stress-atomic-write.mjs +1028 -0
- package/scripts/test-config-rmw-restore.mjs +122 -0
- package/scripts/test-fault-inject.mjs +164 -0
- package/scripts/test-large-file.mjs +174 -0
- package/scripts/tool-edge-smoke.mjs +209 -0
- package/scripts/uninstall.mjs +201 -0
- package/scripts/webhook-selfheal-smoke.mjs +29 -0
- package/scripts/write-overwrite-guard-smoke.mjs +56 -0
- package/server-main.mjs +3055 -0
- package/server.mjs +468 -0
- package/setup/config-merge.mjs +254 -0
- package/setup/install.mjs +120 -0
- package/setup/launch-core.mjs +507 -0
- package/setup/launch.mjs +101 -0
- package/setup/setup-server.mjs +3206 -0
- package/setup/setup.html +3693 -0
- package/skills/retro-skill-proposer/SKILL.md +92 -0
- package/skills/schedule-add/SKILL.md +77 -0
- package/skills/setup/SKILL.md +346 -0
- package/skills/webhook-add/SKILL.md +81 -0
- package/src/agent/bridge-stall-watchdog.mjs +337 -0
- package/src/agent/index.mjs +2138 -0
- package/src/agent/orchestrator/activity-bus.mjs +38 -0
- package/src/agent/orchestrator/ai-wrapped-dispatch.mjs +1010 -0
- package/src/agent/orchestrator/bridge-retry.mjs +220 -0
- package/src/agent/orchestrator/bridge-trace.mjs +583 -0
- package/src/agent/orchestrator/cache-mtime.mjs +58 -0
- package/src/agent/orchestrator/config.mjs +358 -0
- package/src/agent/orchestrator/context/collect.mjs +651 -0
- package/src/agent/orchestrator/dispatch-persist.mjs +549 -0
- package/src/agent/orchestrator/drain-registry.mjs +50 -0
- package/src/agent/orchestrator/explore-validator.mjs +8 -0
- package/src/agent/orchestrator/internal-roles.mjs +118 -0
- package/src/agent/orchestrator/internal-tools.mjs +88 -0
- package/src/agent/orchestrator/jobs.mjs +116 -0
- package/src/agent/orchestrator/mcp/client.mjs +364 -0
- package/src/agent/orchestrator/providers/anthropic-betas.mjs +21 -0
- package/src/agent/orchestrator/providers/anthropic-oauth.mjs +1745 -0
- package/src/agent/orchestrator/providers/anthropic.mjs +437 -0
- package/src/agent/orchestrator/providers/gemini.mjs +1175 -0
- package/src/agent/orchestrator/providers/grok-oauth.mjs +782 -0
- package/src/agent/orchestrator/providers/model-catalog.mjs +241 -0
- package/src/agent/orchestrator/providers/openai-compat.mjs +1467 -0
- package/src/agent/orchestrator/providers/openai-oauth-ws.mjs +1890 -0
- package/src/agent/orchestrator/providers/openai-oauth.mjs +1307 -0
- package/src/agent/orchestrator/providers/openai-ws.mjs +104 -0
- package/src/agent/orchestrator/providers/registry.mjs +192 -0
- package/src/agent/orchestrator/providers/retry-classifier.mjs +325 -0
- package/src/agent/orchestrator/session/abort-lookup.mjs +13 -0
- package/src/agent/orchestrator/session/cache/post-edit-marks.mjs +42 -0
- package/src/agent/orchestrator/session/cache/prefetch-cache.mjs +142 -0
- package/src/agent/orchestrator/session/cache/read-cache.mjs +319 -0
- package/src/agent/orchestrator/session/cache/scoped-cache-outcome.mjs +11 -0
- package/src/agent/orchestrator/session/cache/scoped-cache.mjs +361 -0
- package/src/agent/orchestrator/session/cache/util.mjs +49 -0
- package/src/agent/orchestrator/session/loop.mjs +1478 -0
- package/src/agent/orchestrator/session/manager.mjs +1975 -0
- package/src/agent/orchestrator/session/read-dedup.mjs +6 -0
- package/src/agent/orchestrator/session/result-classification.mjs +65 -0
- package/src/agent/orchestrator/session/save-session-worker.mjs +18 -0
- package/src/agent/orchestrator/session/store.mjs +624 -0
- package/src/agent/orchestrator/session/stream-watchdog.mjs +130 -0
- package/src/agent/orchestrator/session/tool-result-offload.mjs +166 -0
- package/src/agent/orchestrator/session/trim.mjs +491 -0
- package/src/agent/orchestrator/smart-bridge/CACHE-SHARD.md +115 -0
- package/src/agent/orchestrator/smart-bridge/bridge-llm.mjs +327 -0
- package/src/agent/orchestrator/smart-bridge/cache-obs.mjs +150 -0
- package/src/agent/orchestrator/smart-bridge/cache-strategy.mjs +228 -0
- package/src/agent/orchestrator/smart-bridge/index.mjs +215 -0
- package/src/agent/orchestrator/smart-bridge/profiles.mjs +37 -0
- package/src/agent/orchestrator/smart-bridge/registry.mjs +348 -0
- package/src/agent/orchestrator/smart-bridge/session-builder.mjs +116 -0
- package/src/agent/orchestrator/stall-policy.mjs +195 -0
- package/src/agent/orchestrator/tool-loop-guard.mjs +75 -0
- package/src/agent/orchestrator/tools/bash-policy-scan.mjs +77 -0
- package/src/agent/orchestrator/tools/bash-session.mjs +721 -0
- package/src/agent/orchestrator/tools/builtin/advisory-lock.mjs +171 -0
- package/src/agent/orchestrator/tools/builtin/arg-guard.mjs +455 -0
- package/src/agent/orchestrator/tools/builtin/atomic-write.mjs +236 -0
- package/src/agent/orchestrator/tools/builtin/bash-tool.mjs +480 -0
- package/src/agent/orchestrator/tools/builtin/binary-file.mjs +76 -0
- package/src/agent/orchestrator/tools/builtin/builtin-tools.mjs +256 -0
- package/src/agent/orchestrator/tools/builtin/cache-layers.mjs +386 -0
- package/src/agent/orchestrator/tools/builtin/cwd-utils.mjs +37 -0
- package/src/agent/orchestrator/tools/builtin/device-paths.mjs +154 -0
- package/src/agent/orchestrator/tools/builtin/diagnostics-tool.mjs +292 -0
- package/src/agent/orchestrator/tools/builtin/diff-utils.mjs +109 -0
- package/src/agent/orchestrator/tools/builtin/edit-base-guard.mjs +58 -0
- package/src/agent/orchestrator/tools/builtin/edit-byte-plan.mjs +240 -0
- package/src/agent/orchestrator/tools/builtin/edit-byte-utils.mjs +113 -0
- package/src/agent/orchestrator/tools/builtin/edit-commit.mjs +74 -0
- package/src/agent/orchestrator/tools/builtin/edit-context-utils.mjs +242 -0
- package/src/agent/orchestrator/tools/builtin/edit-diagnostics.mjs +211 -0
- package/src/agent/orchestrator/tools/builtin/edit-engine.mjs +1364 -0
- package/src/agent/orchestrator/tools/builtin/edit-failure-context.mjs +126 -0
- package/src/agent/orchestrator/tools/builtin/edit-hint.mjs +141 -0
- package/src/agent/orchestrator/tools/builtin/edit-match-utils.mjs +194 -0
- package/src/agent/orchestrator/tools/builtin/edit-partial-write.mjs +60 -0
- package/src/agent/orchestrator/tools/builtin/edit-stale-refresh.mjs +168 -0
- package/src/agent/orchestrator/tools/builtin/edit-tool.mjs +173 -0
- package/src/agent/orchestrator/tools/builtin/edit-utf8-guard.mjs +48 -0
- package/src/agent/orchestrator/tools/builtin/fs-reachability.mjs +48 -0
- package/src/agent/orchestrator/tools/builtin/fuzzy-match.mjs +99 -0
- package/src/agent/orchestrator/tools/builtin/glob-walk.mjs +170 -0
- package/src/agent/orchestrator/tools/builtin/grep-formatting.mjs +113 -0
- package/src/agent/orchestrator/tools/builtin/hash-utils.mjs +6 -0
- package/src/agent/orchestrator/tools/builtin/list-formatting.mjs +7 -0
- package/src/agent/orchestrator/tools/builtin/list-tool.mjs +593 -0
- package/src/agent/orchestrator/tools/builtin/native-edit-runner.mjs +89 -0
- package/src/agent/orchestrator/tools/builtin/notebook-edit-tool.mjs +300 -0
- package/src/agent/orchestrator/tools/builtin/open-config-tool.mjs +26 -0
- package/src/agent/orchestrator/tools/builtin/path-diagnostics.mjs +152 -0
- package/src/agent/orchestrator/tools/builtin/path-locks.mjs +35 -0
- package/src/agent/orchestrator/tools/builtin/path-utils.mjs +201 -0
- package/src/agent/orchestrator/tools/builtin/read-args.mjs +103 -0
- package/src/agent/orchestrator/tools/builtin/read-batch.mjs +172 -0
- package/src/agent/orchestrator/tools/builtin/read-constants.mjs +40 -0
- package/src/agent/orchestrator/tools/builtin/read-formatting.mjs +118 -0
- package/src/agent/orchestrator/tools/builtin/read-image-resize.mjs +189 -0
- package/src/agent/orchestrator/tools/builtin/read-image.mjs +88 -0
- package/src/agent/orchestrator/tools/builtin/read-lines.mjs +12 -0
- package/src/agent/orchestrator/tools/builtin/read-mode-tool.mjs +455 -0
- package/src/agent/orchestrator/tools/builtin/read-open.mjs +190 -0
- package/src/agent/orchestrator/tools/builtin/read-range-index.mjs +271 -0
- package/src/agent/orchestrator/tools/builtin/read-ranges.mjs +26 -0
- package/src/agent/orchestrator/tools/builtin/read-single-tool.mjs +728 -0
- package/src/agent/orchestrator/tools/builtin/read-snapshot-runtime.mjs +173 -0
- package/src/agent/orchestrator/tools/builtin/read-special-files.mjs +268 -0
- package/src/agent/orchestrator/tools/builtin/read-streaming.mjs +602 -0
- package/src/agent/orchestrator/tools/builtin/read-tool.mjs +530 -0
- package/src/agent/orchestrator/tools/builtin/read-windows.mjs +107 -0
- package/src/agent/orchestrator/tools/builtin/rename-tool.mjs +196 -0
- package/src/agent/orchestrator/tools/builtin/rg-runner.mjs +422 -0
- package/src/agent/orchestrator/tools/builtin/search-builders.mjs +158 -0
- package/src/agent/orchestrator/tools/builtin/search-tool.mjs +869 -0
- package/src/agent/orchestrator/tools/builtin/shell-analysis.mjs +653 -0
- package/src/agent/orchestrator/tools/builtin/shell-jobs.mjs +936 -0
- package/src/agent/orchestrator/tools/builtin/shell-output.mjs +36 -0
- package/src/agent/orchestrator/tools/builtin/shell-runtime.mjs +214 -0
- package/src/agent/orchestrator/tools/builtin/snapshot-helpers.mjs +143 -0
- package/src/agent/orchestrator/tools/builtin/snapshot-store.mjs +206 -0
- package/src/agent/orchestrator/tools/builtin/snapshot-validation.mjs +98 -0
- package/src/agent/orchestrator/tools/builtin/text-stats.mjs +69 -0
- package/src/agent/orchestrator/tools/builtin/windows-roots.mjs +23 -0
- package/src/agent/orchestrator/tools/builtin/write-tool.mjs +401 -0
- package/src/agent/orchestrator/tools/builtin.mjs +500 -0
- package/src/agent/orchestrator/tools/code-graph-prewarm-worker.mjs +39 -0
- package/src/agent/orchestrator/tools/code-graph-tool-defs.mjs +24 -0
- package/src/agent/orchestrator/tools/code-graph.mjs +4095 -0
- package/src/agent/orchestrator/tools/cwd-tool.mjs +298 -0
- package/src/agent/orchestrator/tools/destructive-warning.mjs +323 -0
- package/src/agent/orchestrator/tools/edit-normalize.mjs +603 -0
- package/src/agent/orchestrator/tools/env-scrub.mjs +100 -0
- package/src/agent/orchestrator/tools/graph-binary-fetcher.mjs +144 -0
- package/src/agent/orchestrator/tools/graph-manifest.json +26 -0
- package/src/agent/orchestrator/tools/host-input.mjs +204 -0
- package/src/agent/orchestrator/tools/mutation-content-cache.mjs +67 -0
- package/src/agent/orchestrator/tools/mutation-planner.mjs +75 -0
- package/src/agent/orchestrator/tools/next-call-utils.mjs +48 -0
- package/src/agent/orchestrator/tools/patch-binary-fetcher.mjs +133 -0
- package/src/agent/orchestrator/tools/patch-manifest.json +26 -0
- package/src/agent/orchestrator/tools/patch-tool-defs.mjs +20 -0
- package/src/agent/orchestrator/tools/patch.mjs +2754 -0
- package/src/agent/orchestrator/tools/progress-message.mjs +118 -0
- package/src/agent/orchestrator/tools/result-compression.mjs +279 -0
- package/src/agent/orchestrator/tools/shell-command.mjs +865 -0
- package/src/agent/orchestrator/tools/shell-exec-policy.mjs +89 -0
- package/src/agent/orchestrator/tools/shell-policy-danger-target.mjs +27 -0
- package/src/agent/orchestrator/tools/shell-policy-imports.mjs +7 -0
- package/src/agent/orchestrator/tools/shell-policy.mjs +345 -0
- package/src/agent/orchestrator/tools/shell-snapshot.mjs +313 -0
- package/src/agent/orchestrator/workflow-store.mjs +93 -0
- package/src/agent/tool-defs.mjs +103 -0
- package/src/channels/backends/discord.mjs +784 -0
- package/src/channels/data/voice-runtime-manifest.json +138 -0
- package/src/channels/index.mjs +3229 -0
- package/src/channels/lib/cli-worker-host.mjs +12 -0
- package/src/channels/lib/config-lock.mjs +13 -0
- package/src/channels/lib/config.mjs +292 -0
- package/src/channels/lib/drop-trace.mjs +71 -0
- package/src/channels/lib/event-pipeline.mjs +81 -0
- package/src/channels/lib/event-queue.mjs +345 -0
- package/src/channels/lib/executor.mjs +168 -0
- package/src/channels/lib/format.mjs +188 -0
- package/src/channels/lib/holidays.mjs +138 -0
- package/src/channels/lib/hook-pipe-server.mjs +802 -0
- package/src/channels/lib/interaction-workflows.mjs +184 -0
- package/src/channels/lib/memory-client.mjs +149 -0
- package/src/channels/lib/output-forwarder.mjs +765 -0
- package/src/channels/lib/runtime-paths.mjs +479 -0
- package/src/channels/lib/scheduler.mjs +723 -0
- package/src/channels/lib/session-control.mjs +36 -0
- package/src/channels/lib/session-discovery.mjs +103 -0
- package/src/channels/lib/settings.mjs +11 -0
- package/src/channels/lib/state-file.mjs +68 -0
- package/src/channels/lib/status-snapshot.mjs +219 -0
- package/src/channels/lib/tool-format.mjs +140 -0
- package/src/channels/lib/transcript-discovery.mjs +195 -0
- package/src/channels/lib/voice-runtime-fetcher.mjs +734 -0
- package/src/channels/lib/webhook.mjs +1179 -0
- package/src/channels/lib/whisper-server.mjs +477 -0
- package/src/channels/tool-defs.mjs +170 -0
- package/src/daemon/host.mjs +118 -0
- package/src/daemon/mcp-transport.mjs +47 -0
- package/src/daemon/session.mjs +100 -0
- package/src/daemon/thin-client.mjs +71 -0
- package/src/daemon/transport.mjs +163 -0
- package/src/memory/data/runtime-manifest.json +40 -0
- package/src/memory/index.mjs +3305 -0
- package/src/memory/lib/agent-ipc.mjs +93 -0
- package/src/memory/lib/bridge-trace-queries.mjs +120 -0
- package/src/memory/lib/core-memory-store.mjs +330 -0
- package/src/memory/lib/embedding-provider.mjs +269 -0
- package/src/memory/lib/embedding-worker.mjs +323 -0
- package/src/memory/lib/llm-worker-host.mjs +17 -0
- package/src/memory/lib/memory-cycle.mjs +11 -0
- package/src/memory/lib/memory-cycle1.mjs +641 -0
- package/src/memory/lib/memory-cycle2.mjs +1284 -0
- package/src/memory/lib/memory-cycle3.mjs +540 -0
- package/src/memory/lib/memory-embed.mjs +299 -0
- package/src/memory/lib/memory-extraction.mjs +5 -0
- package/src/memory/lib/memory-maintenance-store.mjs +32 -0
- package/src/memory/lib/memory-ops-policy.mjs +190 -0
- package/src/memory/lib/memory-recall-id-patch.mjs +15 -0
- package/src/memory/lib/memory-recall-read-query.mjs +7 -0
- package/src/memory/lib/memory-recall-scope-filter.mjs +63 -0
- package/src/memory/lib/memory-recall-store.mjs +621 -0
- package/src/memory/lib/memory-retrievers.mjs +112 -0
- package/src/memory/lib/memory-score.mjs +71 -0
- package/src/memory/lib/memory-text-utils.mjs +58 -0
- package/src/memory/lib/memory.mjs +412 -0
- package/src/memory/lib/model-profile.mjs +85 -0
- package/src/memory/lib/pg/adapter.mjs +308 -0
- package/src/memory/lib/pg/process.mjs +360 -0
- package/src/memory/lib/pg/supervisor.mjs +396 -0
- package/src/memory/lib/project-id-resolver.mjs +86 -0
- package/src/memory/lib/runtime-fetcher.mjs +442 -0
- package/src/memory/lib/trace-store.mjs +728 -0
- package/src/memory/tool-defs.mjs +79 -0
- package/src/search/index.mjs +1173 -0
- package/src/search/lib/backends/anthropic-oauth.mjs +98 -0
- package/src/search/lib/backends/exa.mjs +50 -0
- package/src/search/lib/backends/firecrawl.mjs +61 -0
- package/src/search/lib/backends/gemini-api.mjs +83 -0
- package/src/search/lib/backends/grok-oauth.mjs +86 -0
- package/src/search/lib/backends/index.mjs +150 -0
- package/src/search/lib/backends/openai-api.mjs +144 -0
- package/src/search/lib/backends/openai-oauth.mjs +98 -0
- package/src/search/lib/backends/openai-web-search.mjs +76 -0
- package/src/search/lib/backends/tavily.mjs +55 -0
- package/src/search/lib/backends/xai-api.mjs +113 -0
- package/src/search/lib/cache.mjs +131 -0
- package/src/search/lib/config.mjs +192 -0
- package/src/search/lib/formatter.mjs +115 -0
- package/src/search/lib/provider-usage.mjs +67 -0
- package/src/search/lib/providers.mjs +47 -0
- package/src/search/lib/search-intent.mjs +109 -0
- package/src/search/lib/setup-handler.mjs +261 -0
- package/src/search/lib/state.mjs +201 -0
- package/src/search/lib/web-tools.mjs +1207 -0
- package/src/search/tool-defs.mjs +83 -0
- package/src/setup/defender-exclusion.mjs +183 -0
- package/src/shared/abort-controller.mjs +15 -0
- package/src/shared/atomic-file.mjs +420 -0
- package/src/shared/config.mjs +350 -0
- package/src/shared/daemon-recycle.mjs +108 -0
- package/src/shared/disable-claude-builtins.mjs +88 -0
- package/src/shared/err-text.mjs +12 -0
- package/src/shared/llm/cost.mjs +66 -0
- package/src/shared/llm/http-agent.mjs +123 -0
- package/src/shared/llm/index.mjs +41 -0
- package/src/shared/llm/pid-cleanup.mjs +27 -0
- package/src/shared/llm/usage-log.mjs +47 -0
- package/src/shared/plugin-paths.mjs +58 -0
- package/src/shared/schedules-store.mjs +70 -0
- package/src/shared/seed.mjs +119 -0
- package/src/shared/user-cwd.mjs +213 -0
- package/src/shared/user-data-guard.mjs +238 -0
- package/src/status/aggregator.mjs +584 -0
- package/src/status/server.mjs +413 -0
- package/tools.json +1653 -0
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { spawnSync } = require('child_process');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
const fs = require('fs');
|
|
6
|
+
const { resolvePluginData } = require('./plugin-paths.cjs');
|
|
7
|
+
|
|
8
|
+
const SERVICE = 'mixdog';
|
|
9
|
+
const POWERSHELL_TIMEOUT_MS = Number(process.env.MIXDOG_KEYCHAIN_TIMEOUT_MS || 15000);
|
|
10
|
+
|
|
11
|
+
// ---------------------------------------------------------------------------
|
|
12
|
+
// Helpers
|
|
13
|
+
// ---------------------------------------------------------------------------
|
|
14
|
+
|
|
15
|
+
function platform() {
|
|
16
|
+
return process.platform;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function run(cmd, args, opts) {
|
|
20
|
+
// windowsHide + stdio:['ignore','pipe','pipe'] keeps powershell.exe
|
|
21
|
+
// consoleless during DPAPI ops. Without these flags every keychain
|
|
22
|
+
// read/write flashes a conhost window: setup-html's loaders call
|
|
23
|
+
// /agent/config, /memory/auth, /search/config which each invoke
|
|
24
|
+
// hasOpenAIOAuthCredentials / hasAnthropicOAuthCredentials /
|
|
25
|
+
// getSearchApiKey, and each of those triggers one powershell.exe
|
|
26
|
+
// spawn — users saw 8-15 console flashes during config-UI page load.
|
|
27
|
+
// Default opts go BEFORE the spread so callers can still override.
|
|
28
|
+
const result = spawnSync(cmd, args, {
|
|
29
|
+
encoding: 'utf8',
|
|
30
|
+
shell: false,
|
|
31
|
+
windowsHide: true,
|
|
32
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
33
|
+
...opts,
|
|
34
|
+
});
|
|
35
|
+
return result;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// PS 5.1(powershell.exe) first: pwsh(PS 7) does not auto-load ProtectedData assembly,
|
|
39
|
+
// so DPAPI calls silently fail there. Windows always ships powershell.exe.
|
|
40
|
+
function powershell(script) {
|
|
41
|
+
// Bound DPAPI PowerShell calls with a timeout: a hung powershell.exe
|
|
42
|
+
// (AV scan stall, profile loader, transient cert chain lookup) would
|
|
43
|
+
// otherwise block hook/server callers synchronously. The default is long
|
|
44
|
+
// enough for cold PowerShell startup on Windows without silently dropping
|
|
45
|
+
// setup UI credential saves.
|
|
46
|
+
// Resolve powershell.exe to its deterministic System32 location rather than
|
|
47
|
+
// a PATH lookup, so a shadow `powershell.exe` earlier in PATH cannot hijack
|
|
48
|
+
// secret encryption/decryption. (pwsh/PS7 is intentionally not used: it does
|
|
49
|
+
// not auto-load the ProtectedData assembly, so DPAPI silently fails there.)
|
|
50
|
+
const _sysRoot = process.env.SystemRoot || process.env.windir;
|
|
51
|
+
if (!_sysRoot) {
|
|
52
|
+
throw new Error('[keychain] cannot resolve SystemRoot to locate powershell.exe for DPAPI');
|
|
53
|
+
}
|
|
54
|
+
const _psHosts = [path.join(_sysRoot, 'System32', 'WindowsPowerShell', 'v1.0', 'powershell.exe')];
|
|
55
|
+
let timedOut = null;
|
|
56
|
+
for (const exe of _psHosts) {
|
|
57
|
+
const r = run(exe, ['-NonInteractive', '-NoProfile', '-Command', script], { timeout: POWERSHELL_TIMEOUT_MS });
|
|
58
|
+
if (r.error && r.error.code === 'ENOENT') continue;
|
|
59
|
+
if (r.error && r.error.code === 'ETIMEDOUT') {
|
|
60
|
+
timedOut = { exe, timeoutMs: POWERSHELL_TIMEOUT_MS };
|
|
61
|
+
continue;
|
|
62
|
+
}
|
|
63
|
+
return r;
|
|
64
|
+
}
|
|
65
|
+
if (timedOut) {
|
|
66
|
+
throw new Error(`[keychain] PowerShell DPAPI command timed out after ${timedOut.timeoutMs}ms (last host: ${timedOut.exe})`);
|
|
67
|
+
}
|
|
68
|
+
const err = new Error('[keychain] PowerShell not found (tried powershell, pwsh)');
|
|
69
|
+
throw err;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function secretsDir() {
|
|
73
|
+
return path.join(resolvePluginData(), 'secrets');
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function dpApiFile(account) {
|
|
77
|
+
return path.join(secretsDir(), account + '.dpapi');
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// ---------------------------------------------------------------------------
|
|
81
|
+
// darwin — security(1)
|
|
82
|
+
// ---------------------------------------------------------------------------
|
|
83
|
+
|
|
84
|
+
function darwinGet(account) {
|
|
85
|
+
const r = run('security', ['find-generic-password', '-a', account, '-s', SERVICE, '-w']);
|
|
86
|
+
if (r.status !== 0) return null;
|
|
87
|
+
return r.stdout.trimEnd();
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function darwinSet(account, value) {
|
|
91
|
+
const r = run('security', ['add-generic-password', '-a', account, '-s', SERVICE, '-w', value, '-U']);
|
|
92
|
+
if (r.status !== 0) throw new Error(`[keychain] security set failed: ${r.stderr || r.stdout}`);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function darwinDelete(account) {
|
|
96
|
+
const r = run('security', ['delete-generic-password', '-a', account, '-s', SERVICE]);
|
|
97
|
+
if (r.status !== 0) throw new Error(`[keychain] security delete failed: ${r.stderr || r.stdout}`);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// ---------------------------------------------------------------------------
|
|
101
|
+
// linux/WSL — keytar (libsecret binding, optionalDependency)
|
|
102
|
+
// ---------------------------------------------------------------------------
|
|
103
|
+
// keytar must be installed: npm install keytar (requires libsecret-dev on
|
|
104
|
+
// Debian/Ubuntu, or libsecret on other distros). If not installed, every
|
|
105
|
+
// call throws immediately — silent credential loss is not permitted.
|
|
106
|
+
|
|
107
|
+
let _keytarMod = null;
|
|
108
|
+
function loadKeytar() {
|
|
109
|
+
if (_keytarMod !== null) return _keytarMod;
|
|
110
|
+
try { _keytarMod = require('keytar'); }
|
|
111
|
+
catch {
|
|
112
|
+
throw new Error(
|
|
113
|
+
'[keychain] keytar is not installed — run: npm install keytar\n' +
|
|
114
|
+
' Requires libsecret-dev (Debian/Ubuntu) or libsecret (other distros).\n' +
|
|
115
|
+
' Cannot access credentials on this platform without it.'
|
|
116
|
+
);
|
|
117
|
+
}
|
|
118
|
+
return _keytarMod;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// keytar is Promise-based; bridge to sync via spawnSync of a child Node process.
|
|
122
|
+
// Avoids Atomics.wait on main thread (which hangs when SAB is not forwarded to worker).
|
|
123
|
+
function keytarSync(method, ...args) {
|
|
124
|
+
loadKeytar(); // throws if not installed — before spawning child
|
|
125
|
+
const { spawnSync } = require('child_process');
|
|
126
|
+
// Pass service/account/value via env to avoid shell injection entirely.
|
|
127
|
+
const env = {
|
|
128
|
+
...process.env,
|
|
129
|
+
_KEYTAR_METHOD: method,
|
|
130
|
+
_KEYTAR_ARGS: JSON.stringify(args),
|
|
131
|
+
};
|
|
132
|
+
const script = [
|
|
133
|
+
'const kt = require("keytar");',
|
|
134
|
+
'const method = process.env._KEYTAR_METHOD;',
|
|
135
|
+
'const args = JSON.parse(process.env._KEYTAR_ARGS);',
|
|
136
|
+
'kt[method](...args)',
|
|
137
|
+
' .then(v => { process.stdout.write(JSON.stringify({ ok: true, value: v })); })',
|
|
138
|
+
' .catch(e => { process.stdout.write(JSON.stringify({ ok: false, error: e.message })); });',
|
|
139
|
+
].join(' ');
|
|
140
|
+
const r = spawnSync(process.execPath, ['-e', script], {
|
|
141
|
+
env,
|
|
142
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
143
|
+
timeout: 5000,
|
|
144
|
+
encoding: 'utf8',
|
|
145
|
+
windowsHide: true,
|
|
146
|
+
});
|
|
147
|
+
if (r.error) throw new Error(`[keychain] keytar.${method} spawnSync failed: ${r.error.message}`);
|
|
148
|
+
if (r.status !== 0) {
|
|
149
|
+
const detail = (r.stderr || '').trim() || `exit ${r.status}`;
|
|
150
|
+
throw new Error(`[keychain] keytar.${method} child exited with error: ${detail}`);
|
|
151
|
+
}
|
|
152
|
+
let parsed;
|
|
153
|
+
try { parsed = JSON.parse(r.stdout); } catch {
|
|
154
|
+
throw new Error(`[keychain] keytar.${method} child returned unparseable output: ${String(r.stdout).slice(0, 200)}`);
|
|
155
|
+
}
|
|
156
|
+
if (!parsed.ok) throw new Error(`[keychain] keytar.${method} failed: ${parsed.error}`);
|
|
157
|
+
return parsed.value ?? null;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
function linuxGet(account) {
|
|
161
|
+
return keytarSync('getPassword', SERVICE, account);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
function linuxSet(account, value) {
|
|
165
|
+
keytarSync('setPassword', SERVICE, account, value);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
function linuxDelete(account) {
|
|
169
|
+
keytarSync('deletePassword', SERVICE, account);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// ---------------------------------------------------------------------------
|
|
173
|
+
// win32 — DPAPI via PowerShell
|
|
174
|
+
// ---------------------------------------------------------------------------
|
|
175
|
+
|
|
176
|
+
// Add-Type loads System.Security on PS 5.1 hosts where the assembly is not
|
|
177
|
+
// auto-loaded — ProtectedData/DataProtectionScope resolve to TypeNotFound
|
|
178
|
+
// otherwise, and the host returns exit 0 with empty stdout, which then falls
|
|
179
|
+
// through win32Set's empty-output guard. PS 7+ already has the type loaded;
|
|
180
|
+
// SilentlyContinue covers the benign "already loaded" case on repeat calls.
|
|
181
|
+
const PS_PROTECT = [
|
|
182
|
+
'Add-Type -AssemblyName System.Security -ErrorAction SilentlyContinue;',
|
|
183
|
+
'[Console]::OutputEncoding = [System.Text.Encoding]::UTF8;',
|
|
184
|
+
'$value = $args[0];',
|
|
185
|
+
'$bytes = [System.Text.Encoding]::UTF8.GetBytes($value);',
|
|
186
|
+
'$scope = [System.Security.Cryptography.DataProtectionScope]::CurrentUser;',
|
|
187
|
+
'$enc = [System.Security.Cryptography.ProtectedData]::Protect($bytes, $null, $scope);',
|
|
188
|
+
'[Convert]::ToBase64String($enc)',
|
|
189
|
+
].join(' ');
|
|
190
|
+
|
|
191
|
+
const PS_UNPROTECT = [
|
|
192
|
+
'Add-Type -AssemblyName System.Security -ErrorAction SilentlyContinue;',
|
|
193
|
+
'[Console]::OutputEncoding = [System.Text.Encoding]::UTF8;',
|
|
194
|
+
'$b64 = $args[0];',
|
|
195
|
+
'$enc = [Convert]::FromBase64String($b64);',
|
|
196
|
+
'$scope = [System.Security.Cryptography.DataProtectionScope]::CurrentUser;',
|
|
197
|
+
'$dec = [System.Security.Cryptography.ProtectedData]::Unprotect($enc, $null, $scope);',
|
|
198
|
+
'[System.Text.Encoding]::UTF8.GetString($dec)',
|
|
199
|
+
].join(' ');
|
|
200
|
+
|
|
201
|
+
function win32Get(account) {
|
|
202
|
+
const file = dpApiFile(account);
|
|
203
|
+
if (!fs.existsSync(file)) return null;
|
|
204
|
+
const b64 = fs.readFileSync(file, 'utf8').trim();
|
|
205
|
+
if (!b64) throw new Error(`[keychain] DPAPI ciphertext file is empty: ${file}`);
|
|
206
|
+
const r = powershell(`& { ${PS_UNPROTECT} } '${b64}'`);
|
|
207
|
+
if (r.status !== 0) throw new Error(`[keychain] DPAPI decrypt failed (exit ${r.status}): ${r.stderr || r.stdout}`);
|
|
208
|
+
const out = (r.stdout || '').trim();
|
|
209
|
+
if (!out) throw new Error(`[keychain] DPAPI decrypt returned empty output (stderr: ${(r.stderr || '').trim()})`);
|
|
210
|
+
return out;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
function win32Set(account, value) {
|
|
214
|
+
const dir = secretsDir();
|
|
215
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
216
|
+
// Pass value as a PS argument via -Command inline to avoid shell injection;
|
|
217
|
+
// value is embedded as a PS single-quoted string literal with ' escaped.
|
|
218
|
+
const escaped = value.replace(/'/g, "''");
|
|
219
|
+
const r = powershell(`& { ${PS_PROTECT} } '${escaped}'`);
|
|
220
|
+
if (r.status !== 0) throw new Error(`[keychain] DPAPI encrypt failed (exit ${r.status}): ${r.stderr || r.stdout}`);
|
|
221
|
+
const out = (r.stdout || '').trim();
|
|
222
|
+
if (!out) throw new Error(`[keychain] DPAPI encrypt returned empty output (stderr: ${(r.stderr || '').trim()})`);
|
|
223
|
+
fs.writeFileSync(dpApiFile(account), out, 'utf8');
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
function win32Delete(account) {
|
|
227
|
+
const file = dpApiFile(account);
|
|
228
|
+
if (!fs.existsSync(file)) throw new Error(`[keychain] secret not found: ${account}`);
|
|
229
|
+
fs.unlinkSync(file);
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
// ---------------------------------------------------------------------------
|
|
233
|
+
// Public API
|
|
234
|
+
// ---------------------------------------------------------------------------
|
|
235
|
+
|
|
236
|
+
function getSecret(account) {
|
|
237
|
+
switch (platform()) {
|
|
238
|
+
case 'darwin': return darwinGet(account);
|
|
239
|
+
case 'linux': return linuxGet(account);
|
|
240
|
+
case 'win32': return win32Get(account);
|
|
241
|
+
default: throw new Error(`[keychain] unsupported platform: ${process.platform}`);
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
function setSecret(account, value) {
|
|
246
|
+
switch (platform()) {
|
|
247
|
+
case 'darwin': return darwinSet(account, value);
|
|
248
|
+
case 'linux': return linuxSet(account, value);
|
|
249
|
+
case 'win32': return win32Set(account, value);
|
|
250
|
+
default: throw new Error(`[keychain] unsupported platform: ${process.platform}`);
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
function deleteSecret(account) {
|
|
255
|
+
switch (platform()) {
|
|
256
|
+
case 'darwin': return darwinDelete(account);
|
|
257
|
+
case 'linux': return linuxDelete(account);
|
|
258
|
+
case 'win32': return win32Delete(account);
|
|
259
|
+
default: throw new Error(`[keychain] unsupported platform: ${process.platform}`);
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
module.exports = { getSecret, setSecret, deleteSecret, SERVICE };
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Canonical resolver for CLAUDE_PLUGIN_DATA (plugin data dir).
|
|
5
|
+
*
|
|
6
|
+
* Resolution order:
|
|
7
|
+
* 1. process.env.CLAUDE_PLUGIN_DATA — set by Claude Code when spawning
|
|
8
|
+
* the MCP server or a hook.
|
|
9
|
+
* 2. Derive from CLAUDE_PLUGIN_ROOT — works both for cache layout
|
|
10
|
+
* (.../cache/{marketplace}/{plugin}/{version}/)
|
|
11
|
+
* and marketplace layout
|
|
12
|
+
* (.../marketplaces/{marketplace}/external_plugins/{plugin}/).
|
|
13
|
+
*
|
|
14
|
+
* Throws if neither env var is present — the plugin always runs under
|
|
15
|
+
* Claude Code, which sets one of them. Callers must not silently fall
|
|
16
|
+
* back to a hardcoded path.
|
|
17
|
+
*
|
|
18
|
+
* DEFAULT_PLUGIN / DEFAULT_MARKETPLACE are exported so a handful of
|
|
19
|
+
* callers (MCP client spawning sibling plugins, session-manager building
|
|
20
|
+
* PLUGIN_ROOT for rule injection) can reference the canonical names
|
|
21
|
+
* without re-hardcoding the strings. Update both in lockstep with
|
|
22
|
+
* `.claude-plugin/marketplace.json` if the marketplace is ever renamed.
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
const path = require('path');
|
|
26
|
+
const os = require('os');
|
|
27
|
+
const fs = require('fs');
|
|
28
|
+
|
|
29
|
+
const DEFAULT_PLUGIN = 'mixdog';
|
|
30
|
+
const DEFAULT_MARKETPLACE = 'trib-plugin';
|
|
31
|
+
|
|
32
|
+
function readPluginManifestName(root) {
|
|
33
|
+
try {
|
|
34
|
+
const manifest = JSON.parse(fs.readFileSync(path.join(root, '.claude-plugin', 'plugin.json'), 'utf8'));
|
|
35
|
+
if (manifest && typeof manifest.name === 'string' && manifest.name.trim()) return manifest.name.trim();
|
|
36
|
+
} catch { /* fall through to default */ }
|
|
37
|
+
return DEFAULT_PLUGIN;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function resolvePluginData() {
|
|
41
|
+
if (process.env.CLAUDE_PLUGIN_DATA) return process.env.CLAUDE_PLUGIN_DATA;
|
|
42
|
+
const root = process.env.CLAUDE_PLUGIN_ROOT;
|
|
43
|
+
if (root) {
|
|
44
|
+
const dirName = path.basename(root);
|
|
45
|
+
// Cache layout schema: <plugin>/mixdog/<semver>/ — regex matches the semver dir prefix in the path.
|
|
46
|
+
if (/^\d+\.\d+\.\d+/.test(dirName)) {
|
|
47
|
+
const pluginName = path.basename(path.join(root, '..'));
|
|
48
|
+
const marketplace = path.basename(path.join(root, '..', '..'));
|
|
49
|
+
return path.join(os.homedir(), '.claude', 'plugins', 'data', `${pluginName}-${marketplace}`);
|
|
50
|
+
}
|
|
51
|
+
// Marketplace layout: .../marketplaces/{marketplace}/
|
|
52
|
+
// The root dir itself is the marketplace. Plugin name lives in the
|
|
53
|
+
// manifest; fall back to DEFAULT_PLUGIN when it's unreadable.
|
|
54
|
+
const marketplace = dirName;
|
|
55
|
+
const pluginName = readPluginManifestName(root);
|
|
56
|
+
return path.join(os.homedir(), '.claude', 'plugins', 'data', `${pluginName}-${marketplace}`);
|
|
57
|
+
}
|
|
58
|
+
throw new Error('[plugin-paths] CLAUDE_PLUGIN_DATA and CLAUDE_PLUGIN_ROOT are both unset — cannot resolve plugin data dir outside of Claude Code.');
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
module.exports = { resolvePluginData, DEFAULT_PLUGIN, DEFAULT_MARKETPLACE };
|
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* mixdog rules builder.
|
|
5
|
+
*
|
|
6
|
+
* Three surfaces:
|
|
7
|
+
* - buildInjectionContent — Lead (Claude Code main session)
|
|
8
|
+
* - buildBridgeInjectionContent — bridge session BP1 (true cross-role common)
|
|
9
|
+
* - buildBridgeRoleSpecificContent — bridge session BP3 (role-specific instructions)
|
|
10
|
+
*
|
|
11
|
+
* 4-BP cache layout (composeSystemPrompt):
|
|
12
|
+
* BP1 = bridge BP1 content (this file's buildBridgeInjectionContent) — every role identical
|
|
13
|
+
* BP2 = scoped role catalog (collect.mjs loadScopedRoleCatalog) — role family / self
|
|
14
|
+
* BP3 = project context + role marker + permission + role-specific instructions
|
|
15
|
+
* BP4 = task brief + memory recap (5m volatile)
|
|
16
|
+
*
|
|
17
|
+
* Source files (rules/):
|
|
18
|
+
* - shared/01-tool.md — universal tool policy (Lead + bridge BP1, identical full set)
|
|
19
|
+
* - lead/00-tool-lead.md — Lead-specific control-tower / delegation / ToolSearch guidance
|
|
20
|
+
* - lead/01-04 — Lead workflow / channels / team / general
|
|
21
|
+
* - bridge/00-common.md — bridge common behavior + universal worker contract (BP1)
|
|
22
|
+
* - bridge/10..50-*.md — per-hidden-role bodies (consumed by loadScopedRoleCatalog)
|
|
23
|
+
*
|
|
24
|
+
* Core memory snapshot and session recap are injected separately by
|
|
25
|
+
* hooks/session-start.cjs from the memory worker (pgdata) (Lead only).
|
|
26
|
+
*/
|
|
27
|
+
|
|
28
|
+
const fs = require('fs');
|
|
29
|
+
const path = require('path');
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Read a single section from mixdog-config.json (unified config).
|
|
33
|
+
*
|
|
34
|
+
* @param {string} dataDir — DATA_DIR passed into build* functions
|
|
35
|
+
* @param {string} section — top-level key ('memory' | 'search' | …)
|
|
36
|
+
* @returns {object}
|
|
37
|
+
*/
|
|
38
|
+
function readConfigSection(dataDir, section) {
|
|
39
|
+
try {
|
|
40
|
+
const unified = JSON.parse(fs.readFileSync(path.join(dataDir, 'mixdog-config.json'), 'utf8'));
|
|
41
|
+
if (unified && typeof unified === 'object') return unified[section] || {};
|
|
42
|
+
} catch {}
|
|
43
|
+
return {};
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function readOptional(filePath) {
|
|
47
|
+
try { return fs.readFileSync(filePath, 'utf8').trim(); } catch { return ''; }
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Recursively collect all `.md` files under `dir`. Returns absolute paths
|
|
52
|
+
* in stack-DFS order (callers sort before use). Missing/unreadable `dir`
|
|
53
|
+
* yields an empty array — matches the previous inline `try {} catch {}`
|
|
54
|
+
* behavior at every call site.
|
|
55
|
+
*/
|
|
56
|
+
function collectMarkdownFilesRecursive(dir) {
|
|
57
|
+
const collected = [];
|
|
58
|
+
try {
|
|
59
|
+
const stack = [dir];
|
|
60
|
+
while (stack.length) {
|
|
61
|
+
const current = stack.pop();
|
|
62
|
+
const entries = fs.readdirSync(current, { withFileTypes: true });
|
|
63
|
+
for (const entry of entries) {
|
|
64
|
+
const full = path.join(current, entry.name);
|
|
65
|
+
if (entry.isDirectory()) stack.push(full);
|
|
66
|
+
else if (entry.isFile() && entry.name.endsWith('.md')) collected.push(full);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
} catch {}
|
|
70
|
+
return collected;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Address-form rule. Reads only `user.title` — `user.name` is intentionally
|
|
74
|
+
// not consumed; the user is addressed solely by the configured form.
|
|
75
|
+
// Returns '' when title is empty so nothing is injected.
|
|
76
|
+
function composeUserAddressBullet(memoryConfig) {
|
|
77
|
+
const userTitle = (memoryConfig.user && memoryConfig.user.title || '').trim();
|
|
78
|
+
if (!userTitle) return '';
|
|
79
|
+
return `- User address form: ${userTitle} (use this address form exactly as written; do not combine it with any other name or honorific)`;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Build the Lead injection content.
|
|
84
|
+
*/
|
|
85
|
+
function buildInjectionContent({ PLUGIN_ROOT, DATA_DIR }) {
|
|
86
|
+
const RULES_DIR = path.join(PLUGIN_ROOT, 'rules');
|
|
87
|
+
const SHARED_DIR = path.join(RULES_DIR, 'shared');
|
|
88
|
+
const LEAD_DIR = path.join(RULES_DIR, 'lead');
|
|
89
|
+
const HISTORY_DIR = path.join(DATA_DIR, 'history');
|
|
90
|
+
|
|
91
|
+
const memoryConfig = readConfigSection(DATA_DIR, 'memory');
|
|
92
|
+
const parts = [];
|
|
93
|
+
|
|
94
|
+
// Language policy injects first — global default, applied to every Lead reply
|
|
95
|
+
// and to plugin-internal communication regardless of locale.
|
|
96
|
+
const language = readOptional(path.join(SHARED_DIR, '00-language.md'));
|
|
97
|
+
if (language) parts.push(language);
|
|
98
|
+
|
|
99
|
+
const general = readOptional(path.join(LEAD_DIR, '01-general.md'));
|
|
100
|
+
if (general) {
|
|
101
|
+
const addressBullet = composeUserAddressBullet(memoryConfig);
|
|
102
|
+
parts.push(addressBullet ? `${general}\n${addressBullet}` : general);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const tool = readOptional(path.join(SHARED_DIR, '01-tool.md'));
|
|
106
|
+
if (tool) parts.push(tool);
|
|
107
|
+
|
|
108
|
+
const toolLead = readOptional(path.join(LEAD_DIR, '00-tool-lead.md'));
|
|
109
|
+
if (toolLead) parts.push(toolLead);
|
|
110
|
+
|
|
111
|
+
const channels = readOptional(path.join(LEAD_DIR, '02-channels.md'));
|
|
112
|
+
if (channels) parts.push(channels);
|
|
113
|
+
|
|
114
|
+
const team = readOptional(path.join(LEAD_DIR, '03-team.md'));
|
|
115
|
+
if (team) parts.push(team);
|
|
116
|
+
|
|
117
|
+
const workflow = readOptional(path.join(LEAD_DIR, '04-workflow.md'));
|
|
118
|
+
if (workflow) parts.push(workflow);
|
|
119
|
+
|
|
120
|
+
const userWorkflowJsonPath = path.join(DATA_DIR, 'user-workflow.json');
|
|
121
|
+
let userWorkflow = { roles: [] };
|
|
122
|
+
try {
|
|
123
|
+
if (fs.existsSync(userWorkflowJsonPath)) {
|
|
124
|
+
userWorkflow = JSON.parse(fs.readFileSync(userWorkflowJsonPath, 'utf8'));
|
|
125
|
+
}
|
|
126
|
+
} catch {}
|
|
127
|
+
if (Array.isArray(userWorkflow.roles) && userWorkflow.roles.length > 0) {
|
|
128
|
+
const roleLines = ['# Roles', ''];
|
|
129
|
+
for (const role of userWorkflow.roles) {
|
|
130
|
+
roleLines.push(`- ${role.name}: ${role.preset}`);
|
|
131
|
+
}
|
|
132
|
+
parts.push(roleLines.join('\n'));
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const userWorkflowMdPath = path.join(DATA_DIR, 'user-workflow.md');
|
|
136
|
+
const userWorkflowMd = readOptional(userWorkflowMdPath);
|
|
137
|
+
if (userWorkflowMd) {
|
|
138
|
+
const startsWithHeader = /^#\s+User Workflow/i.test(userWorkflowMd);
|
|
139
|
+
parts.push(startsWithHeader ? userWorkflowMd : `# User Workflow\n\n${userWorkflowMd}`);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
const userProfile = readOptional(path.join(HISTORY_DIR, 'user.md'));
|
|
143
|
+
if (userProfile) parts.push(`# User Profile\n\n${userProfile}`);
|
|
144
|
+
|
|
145
|
+
const botPersona = readOptional(path.join(HISTORY_DIR, 'bot.md'));
|
|
146
|
+
if (botPersona) parts.push(`# Bot Persona\n\n${botPersona}`);
|
|
147
|
+
|
|
148
|
+
return parts.join('\n\n');
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* BP1 — true cross-role common. Identical for every bridge role; the
|
|
153
|
+
* role-specific stuff (per-event webhook instructions, per-task schedule
|
|
154
|
+
* instructions, hidden role tool detail) lives in BP3 instead.
|
|
155
|
+
*
|
|
156
|
+
* @param {object} opts
|
|
157
|
+
* @param {string} opts.PLUGIN_ROOT
|
|
158
|
+
* @param {string} opts.DATA_DIR
|
|
159
|
+
* @returns {string}
|
|
160
|
+
*/
|
|
161
|
+
function buildBridgeInjectionContent({ PLUGIN_ROOT, DATA_DIR }) {
|
|
162
|
+
const RULES_DIR = path.join(PLUGIN_ROOT, 'rules');
|
|
163
|
+
const SHARED_DIR = path.join(RULES_DIR, 'shared');
|
|
164
|
+
const BRIDGE_DIR = path.join(RULES_DIR, 'bridge');
|
|
165
|
+
const parts = [];
|
|
166
|
+
|
|
167
|
+
// 0. Language policy — global default, language-neutral plugin behavior.
|
|
168
|
+
const language = readOptional(path.join(SHARED_DIR, '00-language.md'));
|
|
169
|
+
if (language) parts.push(language);
|
|
170
|
+
|
|
171
|
+
// 1. Universal tool policy — same full set Lead receives.
|
|
172
|
+
const tool = readOptional(path.join(SHARED_DIR, '01-tool.md'));
|
|
173
|
+
if (tool) parts.push(tool);
|
|
174
|
+
|
|
175
|
+
// 2. Bridge common behavior.
|
|
176
|
+
const common = readOptional(path.join(BRIDGE_DIR, '00-common.md'));
|
|
177
|
+
if (common) parts.push(common);
|
|
178
|
+
|
|
179
|
+
// 3. User-defined work-role overrides (DATA_DIR/roles/*.md). Pool-wide.
|
|
180
|
+
const rolesDir = path.join(DATA_DIR, 'roles');
|
|
181
|
+
const collected = collectMarkdownFilesRecursive(rolesDir);
|
|
182
|
+
if (collected.length > 0) {
|
|
183
|
+
collected.sort();
|
|
184
|
+
const blocks = collected.map(f => readOptional(f)).filter(Boolean);
|
|
185
|
+
if (blocks.length > 0) {
|
|
186
|
+
parts.push(['# Agent roles', '', blocks.join('\n\n')].join('\n'));
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// userTitle / address form is intentionally NOT injected here — bridge
|
|
191
|
+
// workers produce tool I/O, not user-facing replies, so the persona signal
|
|
192
|
+
// only biases response language/tone without serving a purpose. Lead BP1
|
|
193
|
+
// (buildInjectionContent above) still carries it.
|
|
194
|
+
|
|
195
|
+
return parts.join('\n\n');
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* BP3 role-specific instructions. Only the calling role's own task / tool
|
|
200
|
+
* detail body emits — webhook-handler gets webhooks/<all-events>/, scheduler
|
|
201
|
+
* gets schedules/<all-tasks>/, hidden retrieval roles get their own tool
|
|
202
|
+
* detail. Other roles return ''.
|
|
203
|
+
*
|
|
204
|
+
* NOTE: webhook-event narrowing (one event per call) requires the inbound
|
|
205
|
+
* payload's event id at compose time; not implemented yet, so all 4 webhook
|
|
206
|
+
* instructions still bake into webhook-handler BP3 for now.
|
|
207
|
+
*
|
|
208
|
+
* @param {object} opts
|
|
209
|
+
* @param {string} opts.DATA_DIR
|
|
210
|
+
* @param {string|null} opts.currentRole
|
|
211
|
+
* @returns {string}
|
|
212
|
+
*/
|
|
213
|
+
function buildBridgeRoleSpecificContent({ DATA_DIR, currentRole }) {
|
|
214
|
+
if (!currentRole) return '';
|
|
215
|
+
const parts = [];
|
|
216
|
+
|
|
217
|
+
// webhook-handler / scheduler-task — pull their respective instruction trees.
|
|
218
|
+
const subdirForRole =
|
|
219
|
+
currentRole === 'webhook-handler' ? 'webhooks'
|
|
220
|
+
: currentRole === 'scheduler-task' ? 'schedules'
|
|
221
|
+
: null;
|
|
222
|
+
if (subdirForRole) {
|
|
223
|
+
const dir = path.join(DATA_DIR, subdirForRole);
|
|
224
|
+
const collected = collectMarkdownFilesRecursive(dir);
|
|
225
|
+
if (collected.length > 0) {
|
|
226
|
+
collected.sort();
|
|
227
|
+
const blocks = collected.map(f => readOptional(f)).filter(Boolean);
|
|
228
|
+
if (blocks.length > 0) {
|
|
229
|
+
parts.push([`# Agent ${subdirForRole}`, '', blocks.join('\n\n')].join('\n'));
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
return parts.join('\n\n');
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
module.exports = {
|
|
238
|
+
buildInjectionContent,
|
|
239
|
+
buildBridgeInjectionContent,
|
|
240
|
+
buildBridgeRoleSpecificContent,
|
|
241
|
+
};
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Shared text-cleaning utilities. Used by both:
|
|
5
|
+
* - hooks/session-start.cjs (CJS hook)
|
|
6
|
+
* - src/memory/lib/memory-extraction.mjs (ESM, via createRequire re-export)
|
|
7
|
+
*
|
|
8
|
+
* Single source of truth for the regex set that strips:
|
|
9
|
+
* - markdown fences, headers, list markers, bold
|
|
10
|
+
* - tool/system tags emitted by Claude Code (system-reminder, tool-use-id,
|
|
11
|
+
* local-command-*, etc.)
|
|
12
|
+
* - mixdog tags (channel, schedule-context, teammate-message)
|
|
13
|
+
* - URLs, emoji, "Ran X", "Process exited", and other transcript noise
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
function cleanMemoryText(text) {
|
|
17
|
+
return String(text ?? '')
|
|
18
|
+
.replace(/```[\s\S]*?```/g, '')
|
|
19
|
+
.replace(/<memory-context>[\s\S]*?<\/memory-context>/gi, '')
|
|
20
|
+
.replace(/<local-command-caveat>[\s\S]*?<\/local-command-caveat>/gi, '')
|
|
21
|
+
.replace(/<local-command-stdout>[\s\S]*?<\/local-command-stdout>/gi, '')
|
|
22
|
+
.replace(/<command-name>[\s\S]*?<\/command-name>/gi, '')
|
|
23
|
+
.replace(/<command-message>[\s\S]*?<\/command-message>/gi, '')
|
|
24
|
+
.replace(/<command-args>[\s\S]*?<\/command-args>/gi, '')
|
|
25
|
+
.replace(/<task-notification>[\s\S]*?<\/task-notification>/gi, '')
|
|
26
|
+
.replace(/<tool-use-id>[\s\S]*?<\/tool-use-id>/gi, '')
|
|
27
|
+
.replace(/<output-file>[\s\S]*?<\/output-file>/gi, '')
|
|
28
|
+
.replace(/<system-reminder>[\s\S]*?<\/system-reminder>/gi, '')
|
|
29
|
+
.replace(/<schedule-context>[\s\S]*?<\/schedule-context>/gi, '')
|
|
30
|
+
.replace(/<teammate-message[\s\S]*?<\/teammate-message>/gi, '')
|
|
31
|
+
.replace(/<channel[^>]*>\n?([\s\S]*?)\n?<\/channel>/g, '$1')
|
|
32
|
+
.replace(/^[ \t]*\|.*\|[ \t]*$/gm, '')
|
|
33
|
+
.replace(/`([^`]+)`/g, '$1')
|
|
34
|
+
.replace(/\*\*/g, '')
|
|
35
|
+
.replace(/^#{1,4}\s+/gm, '')
|
|
36
|
+
.replace(/^>\s?/gm, '')
|
|
37
|
+
.replace(/^[-*]\s+/gm, '')
|
|
38
|
+
.replace(/https?:\/\/\S+/g, '')
|
|
39
|
+
.replace(/^This session is being continued from a previous conversation[\s\S]*?(?=\n\n|$)/gim, '')
|
|
40
|
+
.replace(/^\[[^\]\n]{1,140}\]\s*$/gm, '')
|
|
41
|
+
.replace(/^\s*●\s.*$/gm, '')
|
|
42
|
+
.replace(/^\s*Ran .*$/gm, '')
|
|
43
|
+
// stable best-effort sanitization, do not extend without justification
|
|
44
|
+
.replace(/^\s*Command: .*$/gm, '')
|
|
45
|
+
.replace(/^\s*Process exited .*$/gm, '')
|
|
46
|
+
.replace(/^\s*Full transcript available at: .*$/gm, '')
|
|
47
|
+
.replace(/^\s*Read the output file to retrieve the result: .*$/gm, '')
|
|
48
|
+
.replace(/^\s*Original token count: .*$/gm, '')
|
|
49
|
+
.replace(/^\s*Wall time: .*$/gm, '')
|
|
50
|
+
.replace(/^\s*Chunk ID: .*$/gm, '')
|
|
51
|
+
.replace(/^\s*tool_uses: .*$/gm, '')
|
|
52
|
+
.replace(/^\s*menu item .*$/gm, '')
|
|
53
|
+
.replace(/<\/?[a-z][-a-z]*(?:\s[^>]*)?\/?>/gi, '')
|
|
54
|
+
.replace(/[\u{1F300}-\u{1FAD6}\u{2600}-\u{27BF}]/gu, '')
|
|
55
|
+
.replace(/[ \t]+/g, ' ')
|
|
56
|
+
.replace(/\n{2,}/g, '\n')
|
|
57
|
+
.replace(/^\s+|\s+$/gm, '')
|
|
58
|
+
.trim();
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
module.exports = { cleanMemoryText };
|