mindweave 0.0.1 → 2.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +190 -0
- package/README.md +171 -14
- package/dist/alternator/chassis/cache.js +47 -0
- package/dist/alternator/chassis/cache.js.map +1 -0
- package/dist/alternator/chassis/domRefs.js +53 -0
- package/dist/alternator/chassis/domRefs.js.map +1 -0
- package/dist/alternator/chassis/extractWorker.js +65 -0
- package/dist/alternator/chassis/extractWorker.js.map +1 -0
- package/dist/alternator/chassis/graph.js +221 -0
- package/dist/alternator/chassis/graph.js.map +1 -0
- package/dist/alternator/chassis/index.js +463 -0
- package/dist/alternator/chassis/index.js.map +1 -0
- package/dist/alternator/chassis/isolation.js +336 -0
- package/dist/alternator/chassis/isolation.js.map +1 -0
- package/dist/alternator/chassis/langCases.js +69 -0
- package/dist/alternator/chassis/langCases.js.map +1 -0
- package/dist/alternator/chassis/lsp.js +469 -0
- package/dist/alternator/chassis/lsp.js.map +1 -0
- package/dist/alternator/chassis/markup.js +262 -0
- package/dist/alternator/chassis/markup.js.map +1 -0
- package/dist/alternator/chassis/provision.js +198 -0
- package/dist/alternator/chassis/provision.js.map +1 -0
- package/dist/alternator/chassis/rank.js +72 -0
- package/dist/alternator/chassis/rank.js.map +1 -0
- package/dist/alternator/chassis/servers.js +215 -0
- package/dist/alternator/chassis/servers.js.map +1 -0
- package/dist/alternator/chassis/treesitter.js +608 -0
- package/dist/alternator/chassis/treesitter.js.map +1 -0
- package/dist/alternator/chassis/types.js +24 -0
- package/dist/alternator/chassis/types.js.map +1 -0
- package/dist/alternator/lane.js +67 -0
- package/dist/alternator/lane.js.map +1 -0
- package/dist/cli/App.js +2586 -0
- package/dist/cli/App.js.map +1 -0
- package/dist/cli/altScreen.js +67 -0
- package/dist/cli/altScreen.js.map +1 -0
- package/dist/cli/approvalChannel.js +72 -0
- package/dist/cli/approvalChannel.js.map +1 -0
- package/dist/cli/attachments.js +188 -0
- package/dist/cli/attachments.js.map +1 -0
- package/dist/cli/bootstrap.js +279 -0
- package/dist/cli/bootstrap.js.map +1 -0
- package/dist/cli/chatAnchor.js +94 -0
- package/dist/cli/chatAnchor.js.map +1 -0
- package/dist/cli/commandArgs.js +79 -0
- package/dist/cli/commandArgs.js.map +1 -0
- package/dist/cli/commandRoute.js +78 -0
- package/dist/cli/commandRoute.js.map +1 -0
- package/dist/cli/compaction.js +69 -0
- package/dist/cli/compaction.js.map +1 -0
- package/dist/cli/components/ApprovalBox.js +100 -0
- package/dist/cli/components/ApprovalBox.js.map +1 -0
- package/dist/cli/components/BlockView.js +128 -0
- package/dist/cli/components/BlockView.js.map +1 -0
- package/dist/cli/components/FirstRunFrame.js +41 -0
- package/dist/cli/components/FirstRunFrame.js.map +1 -0
- package/dist/cli/components/KeyManager.js +197 -0
- package/dist/cli/components/KeyManager.js.map +1 -0
- package/dist/cli/components/KeySetup.js +125 -0
- package/dist/cli/components/KeySetup.js.map +1 -0
- package/dist/cli/components/MenuFrame.js +24 -0
- package/dist/cli/components/MenuFrame.js.map +1 -0
- package/dist/cli/components/Picker.js +82 -0
- package/dist/cli/components/Picker.js.map +1 -0
- package/dist/cli/components/PromptInput.js +509 -0
- package/dist/cli/components/PromptInput.js.map +1 -0
- package/dist/cli/components/SubagentView.js +86 -0
- package/dist/cli/components/SubagentView.js.map +1 -0
- package/dist/cli/components/ToolGroup.js +60 -0
- package/dist/cli/components/ToolGroup.js.map +1 -0
- package/dist/cli/components/ToolLine.js +123 -0
- package/dist/cli/components/ToolLine.js.map +1 -0
- package/dist/cli/components/TrustGate.js +37 -0
- package/dist/cli/components/TrustGate.js.map +1 -0
- package/dist/cli/costView.js +40 -0
- package/dist/cli/costView.js.map +1 -0
- package/dist/cli/frameRate.js +30 -0
- package/dist/cli/frameRate.js.map +1 -0
- package/dist/cli/framebuffer/paint.js +165 -0
- package/dist/cli/framebuffer/paint.js.map +1 -0
- package/dist/cli/framebuffer/parse.js +266 -0
- package/dist/cli/framebuffer/parse.js.map +1 -0
- package/dist/cli/framebuffer/screen.js +196 -0
- package/dist/cli/framebuffer/screen.js.map +1 -0
- package/dist/cli/framebuffer/writer.js +139 -0
- package/dist/cli/framebuffer/writer.js.map +1 -0
- package/dist/cli/groupReveal.js +51 -0
- package/dist/cli/groupReveal.js.map +1 -0
- package/dist/cli/help.js +55 -0
- package/dist/cli/help.js.map +1 -0
- package/dist/cli/inputView.js +105 -0
- package/dist/cli/inputView.js.map +1 -0
- package/dist/cli/keyManager.js +65 -0
- package/dist/cli/keyManager.js.map +1 -0
- package/dist/cli/keySetup.js +35 -0
- package/dist/cli/keySetup.js.map +1 -0
- package/dist/cli/keyStore.js +73 -0
- package/dist/cli/keyStore.js.map +1 -0
- package/dist/cli/markdown.js +460 -0
- package/dist/cli/markdown.js.map +1 -0
- package/dist/cli/messageQueue.js +96 -0
- package/dist/cli/messageQueue.js.map +1 -0
- package/dist/cli/modes.js +90 -0
- package/dist/cli/modes.js.map +1 -0
- package/dist/cli/mouse.js +78 -0
- package/dist/cli/mouse.js.map +1 -0
- package/dist/cli/pasteAssembler.js +44 -0
- package/dist/cli/pasteAssembler.js.map +1 -0
- package/dist/cli/pathComplete.js +39 -0
- package/dist/cli/pathComplete.js.map +1 -0
- package/dist/cli/perfLog.js +90 -0
- package/dist/cli/perfLog.js.map +1 -0
- package/dist/cli/reveal.js +32 -0
- package/dist/cli/reveal.js.map +1 -0
- package/dist/cli/revealPace.js +59 -0
- package/dist/cli/revealPace.js.map +1 -0
- package/dist/cli/sessionCarry.js +19 -0
- package/dist/cli/sessionCarry.js.map +1 -0
- package/dist/cli/smoothScroll.js +74 -0
- package/dist/cli/smoothScroll.js.map +1 -0
- package/dist/cli/startupArgs.js +96 -0
- package/dist/cli/startupArgs.js.map +1 -0
- package/dist/cli/terminalRestore.js +34 -0
- package/dist/cli/terminalRestore.js.map +1 -0
- package/dist/cli/toolDisplay.js +219 -0
- package/dist/cli/toolDisplay.js.map +1 -0
- package/dist/cli/toolItems.js +65 -0
- package/dist/cli/toolItems.js.map +1 -0
- package/dist/cli/transcript.js +351 -0
- package/dist/cli/transcript.js.map +1 -0
- package/dist/cli/trust.js +79 -0
- package/dist/cli/trust.js.map +1 -0
- package/dist/cli/updateCheck.js +107 -0
- package/dist/cli/updateCheck.js.map +1 -0
- package/dist/cli/version.js +39 -0
- package/dist/cli/version.js.map +1 -0
- package/dist/cli/viewport.js +179 -0
- package/dist/cli/viewport.js.map +1 -0
- package/dist/cli/virtualWindow.js +103 -0
- package/dist/cli/virtualWindow.js.map +1 -0
- package/dist/cli/workingVerb.js +37 -0
- package/dist/cli/workingVerb.js.map +1 -0
- package/dist/cli/wrap.js +145 -0
- package/dist/cli/wrap.js.map +1 -0
- package/dist/drivers/anthropic/cachePoints.js +77 -0
- package/dist/drivers/anthropic/cachePoints.js.map +1 -0
- package/dist/drivers/anthropic/client.js +432 -0
- package/dist/drivers/anthropic/client.js.map +1 -0
- package/dist/drivers/anthropic/index.js +10 -0
- package/dist/drivers/anthropic/index.js.map +1 -0
- package/dist/drivers/anthropic/manifest.js +221 -0
- package/dist/drivers/anthropic/manifest.js.map +1 -0
- package/dist/drivers/cerebras/client.js +69 -0
- package/dist/drivers/cerebras/client.js.map +1 -0
- package/dist/drivers/cerebras/index.js +5 -0
- package/dist/drivers/cerebras/index.js.map +1 -0
- package/dist/drivers/cerebras/manifest.js +140 -0
- package/dist/drivers/cerebras/manifest.js.map +1 -0
- package/dist/drivers/clientId.js +41 -0
- package/dist/drivers/clientId.js.map +1 -0
- package/dist/drivers/contextOverflow.js +77 -0
- package/dist/drivers/contextOverflow.js.map +1 -0
- package/dist/drivers/deepseek/client.js +168 -0
- package/dist/drivers/deepseek/client.js.map +1 -0
- package/dist/drivers/deepseek/index.js +16 -0
- package/dist/drivers/deepseek/index.js.map +1 -0
- package/dist/drivers/deepseek/inlineTools.js +46 -0
- package/dist/drivers/deepseek/inlineTools.js.map +1 -0
- package/dist/drivers/deepseek/manifest.js +166 -0
- package/dist/drivers/deepseek/manifest.js.map +1 -0
- package/dist/drivers/gemini/client.js +38 -0
- package/dist/drivers/gemini/client.js.map +1 -0
- package/dist/drivers/gemini/index.js +5 -0
- package/dist/drivers/gemini/index.js.map +1 -0
- package/dist/drivers/gemini/manifest.js +150 -0
- package/dist/drivers/gemini/manifest.js.map +1 -0
- package/dist/drivers/glm/client.js +90 -0
- package/dist/drivers/glm/client.js.map +1 -0
- package/dist/drivers/glm/index.js +9 -0
- package/dist/drivers/glm/index.js.map +1 -0
- package/dist/drivers/glm/manifest.js +183 -0
- package/dist/drivers/glm/manifest.js.map +1 -0
- package/dist/drivers/groq/client.js +80 -0
- package/dist/drivers/groq/client.js.map +1 -0
- package/dist/drivers/groq/index.js +5 -0
- package/dist/drivers/groq/index.js.map +1 -0
- package/dist/drivers/groq/manifest.js +160 -0
- package/dist/drivers/groq/manifest.js.map +1 -0
- package/dist/drivers/kimi/client.js +82 -0
- package/dist/drivers/kimi/client.js.map +1 -0
- package/dist/drivers/kimi/index.js +9 -0
- package/dist/drivers/kimi/index.js.map +1 -0
- package/dist/drivers/kimi/manifest.js +168 -0
- package/dist/drivers/kimi/manifest.js.map +1 -0
- package/dist/drivers/meta/client.js +30 -0
- package/dist/drivers/meta/client.js.map +1 -0
- package/dist/drivers/meta/index.js +5 -0
- package/dist/drivers/meta/index.js.map +1 -0
- package/dist/drivers/meta/manifest.js +101 -0
- package/dist/drivers/meta/manifest.js.map +1 -0
- package/dist/drivers/minimax/client.js +36 -0
- package/dist/drivers/minimax/client.js.map +1 -0
- package/dist/drivers/minimax/index.js +5 -0
- package/dist/drivers/minimax/index.js.map +1 -0
- package/dist/drivers/minimax/manifest.js +120 -0
- package/dist/drivers/minimax/manifest.js.map +1 -0
- package/dist/drivers/mistral/client.js +54 -0
- package/dist/drivers/mistral/client.js.map +1 -0
- package/dist/drivers/mistral/index.js +5 -0
- package/dist/drivers/mistral/index.js.map +1 -0
- package/dist/drivers/mistral/manifest.js +118 -0
- package/dist/drivers/mistral/manifest.js.map +1 -0
- package/dist/drivers/openai/client.js +290 -0
- package/dist/drivers/openai/client.js.map +1 -0
- package/dist/drivers/openai/index.js +9 -0
- package/dist/drivers/openai/index.js.map +1 -0
- package/dist/drivers/openai/manifest.js +133 -0
- package/dist/drivers/openai/manifest.js.map +1 -0
- package/dist/drivers/openaiCompat/wire.js +493 -0
- package/dist/drivers/openaiCompat/wire.js.map +1 -0
- package/dist/drivers/partialTurn.js +23 -0
- package/dist/drivers/partialTurn.js.map +1 -0
- package/dist/drivers/providerError.js +181 -0
- package/dist/drivers/providerError.js.map +1 -0
- package/dist/drivers/qwen/client.js +93 -0
- package/dist/drivers/qwen/client.js.map +1 -0
- package/dist/drivers/qwen/index.js +9 -0
- package/dist/drivers/qwen/index.js.map +1 -0
- package/dist/drivers/qwen/manifest.js +146 -0
- package/dist/drivers/qwen/manifest.js.map +1 -0
- package/dist/drivers/registry.js +177 -0
- package/dist/drivers/registry.js.map +1 -0
- package/dist/drivers/retryPolicy.js +129 -0
- package/dist/drivers/retryPolicy.js.map +1 -0
- package/dist/drivers/searchBlocks.js +61 -0
- package/dist/drivers/searchBlocks.js.map +1 -0
- package/dist/drivers/types.js +2 -0
- package/dist/drivers/types.js.map +1 -0
- package/dist/drivers/xai/client.js +63 -0
- package/dist/drivers/xai/client.js.map +1 -0
- package/dist/drivers/xai/index.js +5 -0
- package/dist/drivers/xai/index.js.map +1 -0
- package/dist/drivers/xai/manifest.js +137 -0
- package/dist/drivers/xai/manifest.js.map +1 -0
- package/dist/dynamo/_probe.tmp.js +20 -0
- package/dist/dynamo/_probe.tmp.js.map +1 -0
- package/dist/dynamo/cacheBreak.js +88 -0
- package/dist/dynamo/cacheBreak.js.map +1 -0
- package/dist/dynamo/contextWindow.js +243 -0
- package/dist/dynamo/contextWindow.js.map +1 -0
- package/dist/dynamo/engine.js +2011 -0
- package/dist/dynamo/engine.js.map +1 -0
- package/dist/dynamo/guard.js +132 -0
- package/dist/dynamo/guard.js.map +1 -0
- package/dist/dynamo/liveMeter.js +58 -0
- package/dist/dynamo/liveMeter.js.map +1 -0
- package/dist/dynamo/model.js +172 -0
- package/dist/dynamo/model.js.map +1 -0
- package/dist/dynamo/planArtifact.js +125 -0
- package/dist/dynamo/planArtifact.js.map +1 -0
- package/dist/dynamo/pricing.js +140 -0
- package/dist/dynamo/pricing.js.map +1 -0
- package/dist/dynamo/prompt.js +264 -0
- package/dist/dynamo/prompt.js.map +1 -0
- package/dist/dynamo/spend.js +45 -0
- package/dist/dynamo/spend.js.map +1 -0
- package/dist/dynamo/verify.js +483 -0
- package/dist/dynamo/verify.js.map +1 -0
- package/dist/governor/forbidden.js +181 -0
- package/dist/governor/forbidden.js.map +1 -0
- package/dist/governor/freshness.js +83 -0
- package/dist/governor/freshness.js.map +1 -0
- package/dist/governor/frontmatter.js +38 -0
- package/dist/governor/frontmatter.js.map +1 -0
- package/dist/governor/glob.js +75 -0
- package/dist/governor/glob.js.map +1 -0
- package/dist/governor/index.js +87 -0
- package/dist/governor/index.js.map +1 -0
- package/dist/governor/rules.js +80 -0
- package/dist/governor/rules.js.map +1 -0
- package/dist/governor/scope.js +91 -0
- package/dist/governor/scope.js.map +1 -0
- package/dist/governor/skills.js +176 -0
- package/dist/governor/skills.js.map +1 -0
- package/dist/governor/types.js +16 -0
- package/dist/governor/types.js.map +1 -0
- package/dist/governor/write.js +182 -0
- package/dist/governor/write.js.map +1 -0
- package/dist/index.js +89 -0
- package/dist/index.js.map +1 -0
- package/dist/mcp/catalog.js +232 -0
- package/dist/mcp/catalog.js.map +1 -0
- package/dist/mcp/config.js +116 -0
- package/dist/mcp/config.js.map +1 -0
- package/dist/mcp/configWrite.js +254 -0
- package/dist/mcp/configWrite.js.map +1 -0
- package/dist/mcp/connection.js +504 -0
- package/dist/mcp/connection.js.map +1 -0
- package/dist/mcp/deferred.js +104 -0
- package/dist/mcp/deferred.js.map +1 -0
- package/dist/mcp/discover.js +128 -0
- package/dist/mcp/discover.js.map +1 -0
- package/dist/mcp/manager.js +679 -0
- package/dist/mcp/manager.js.map +1 -0
- package/dist/mcp/paramHeaders.js +163 -0
- package/dist/mcp/paramHeaders.js.map +1 -0
- package/dist/mcp/prompts.js +136 -0
- package/dist/mcp/prompts.js.map +1 -0
- package/dist/mcp/protocol.js +171 -0
- package/dist/mcp/protocol.js.map +1 -0
- package/dist/mcp/resources.js +119 -0
- package/dist/mcp/resources.js.map +1 -0
- package/dist/mcp/resultStore.js +180 -0
- package/dist/mcp/resultStore.js.map +1 -0
- package/dist/mcp/subscriptions.js +74 -0
- package/dist/mcp/subscriptions.js.map +1 -0
- package/dist/mcp/transport/headerValue.js +40 -0
- package/dist/mcp/transport/headerValue.js.map +1 -0
- package/dist/mcp/transport/http.js +321 -0
- package/dist/mcp/transport/http.js.map +1 -0
- package/dist/mcp/transport/stdio.js +234 -0
- package/dist/mcp/transport/stdio.js.map +1 -0
- package/dist/mcp/transport/types.js +27 -0
- package/dist/mcp/transport/types.js.map +1 -0
- package/dist/mcp/trust.js +98 -0
- package/dist/mcp/trust.js.map +1 -0
- package/dist/mcp/trustStore.js +55 -0
- package/dist/mcp/trustStore.js.map +1 -0
- package/dist/memory/autoMemory.js +340 -0
- package/dist/memory/autoMemory.js.map +1 -0
- package/dist/memory/compaction.js +513 -0
- package/dist/memory/compaction.js.map +1 -0
- package/dist/memory/images.js +173 -0
- package/dist/memory/images.js.map +1 -0
- package/dist/memory/presence.js +59 -0
- package/dist/memory/presence.js.map +1 -0
- package/dist/memory/projectNotes.js +247 -0
- package/dist/memory/projectNotes.js.map +1 -0
- package/dist/memory/restore.js +72 -0
- package/dist/memory/restore.js.map +1 -0
- package/dist/memory/session.js +472 -0
- package/dist/memory/session.js.map +1 -0
- package/dist/memory/sessionMemory.js +142 -0
- package/dist/memory/sessionMemory.js.map +1 -0
- package/dist/memory/sessionMemoryCompact.js +136 -0
- package/dist/memory/sessionMemoryCompact.js.map +1 -0
- package/dist/memory/store.js +217 -0
- package/dist/memory/store.js.map +1 -0
- package/dist/memory/types.js +14 -0
- package/dist/memory/types.js.map +1 -0
- package/dist/memory/workingSet.js +383 -0
- package/dist/memory/workingSet.js.map +1 -0
- package/dist/project/context.js +425 -0
- package/dist/project/context.js.map +1 -0
- package/dist/tools/approval.js +189 -0
- package/dist/tools/approval.js.map +1 -0
- package/dist/tools/askUser.js +103 -0
- package/dist/tools/askUser.js.map +1 -0
- package/dist/tools/atomicWrite.js +151 -0
- package/dist/tools/atomicWrite.js.map +1 -0
- package/dist/tools/backgroundShells.js +468 -0
- package/dist/tools/backgroundShells.js.map +1 -0
- package/dist/tools/captureSweep.js +77 -0
- package/dist/tools/captureSweep.js.map +1 -0
- package/dist/tools/chassisMux.js +92 -0
- package/dist/tools/chassisMux.js.map +1 -0
- package/dist/tools/checkpoints.js +328 -0
- package/dist/tools/checkpoints.js.map +1 -0
- package/dist/tools/codeIntel.js +267 -0
- package/dist/tools/codeIntel.js.map +1 -0
- package/dist/tools/deferredNative.js +121 -0
- package/dist/tools/deferredNative.js.map +1 -0
- package/dist/tools/detail.js +135 -0
- package/dist/tools/detail.js.map +1 -0
- package/dist/tools/diagnostics.js +174 -0
- package/dist/tools/diagnostics.js.map +1 -0
- package/dist/tools/edit.js +167 -0
- package/dist/tools/edit.js.map +1 -0
- package/dist/tools/editCore.js +283 -0
- package/dist/tools/editCore.js.map +1 -0
- package/dist/tools/editFile.js +183 -0
- package/dist/tools/editFile.js.map +1 -0
- package/dist/tools/editRipple.js +126 -0
- package/dist/tools/editRipple.js.map +1 -0
- package/dist/tools/editTarget.js +131 -0
- package/dist/tools/editTarget.js.map +1 -0
- package/dist/tools/editWindow.js +49 -0
- package/dist/tools/editWindow.js.map +1 -0
- package/dist/tools/eol.js +56 -0
- package/dist/tools/eol.js.map +1 -0
- package/dist/tools/exitPlan.js +175 -0
- package/dist/tools/exitPlan.js.map +1 -0
- package/dist/tools/focus.js +42 -0
- package/dist/tools/focus.js.map +1 -0
- package/dist/tools/gitignore.js +148 -0
- package/dist/tools/gitignore.js.map +1 -0
- package/dist/tools/glob.js +134 -0
- package/dist/tools/glob.js.map +1 -0
- package/dist/tools/governorTools.js +258 -0
- package/dist/tools/governorTools.js.map +1 -0
- package/dist/tools/grep.js +450 -0
- package/dist/tools/grep.js.map +1 -0
- package/dist/tools/guard.js +220 -0
- package/dist/tools/guard.js.map +1 -0
- package/dist/tools/killTree.js +215 -0
- package/dist/tools/killTree.js.map +1 -0
- package/dist/tools/listDir.js +143 -0
- package/dist/tools/listDir.js.map +1 -0
- package/dist/tools/mcpAdd.js +176 -0
- package/dist/tools/mcpAdd.js.map +1 -0
- package/dist/tools/mcpResources.js +116 -0
- package/dist/tools/mcpResources.js.map +1 -0
- package/dist/tools/mcpSearch.js +123 -0
- package/dist/tools/mcpSearch.js.map +1 -0
- package/dist/tools/multiEdit.js +168 -0
- package/dist/tools/multiEdit.js.map +1 -0
- package/dist/tools/paths.js +225 -0
- package/dist/tools/paths.js.map +1 -0
- package/dist/tools/posixShell.js +123 -0
- package/dist/tools/posixShell.js.map +1 -0
- package/dist/tools/readFile.js +371 -0
- package/dist/tools/readFile.js.map +1 -0
- package/dist/tools/readSymbol.js +145 -0
- package/dist/tools/readSymbol.js.map +1 -0
- package/dist/tools/registry.js +120 -0
- package/dist/tools/registry.js.map +1 -0
- package/dist/tools/replaceSymbol.js +141 -0
- package/dist/tools/replaceSymbol.js.map +1 -0
- package/dist/tools/results.js +15 -0
- package/dist/tools/results.js.map +1 -0
- package/dist/tools/ripgrep.js +145 -0
- package/dist/tools/ripgrep.js.map +1 -0
- package/dist/tools/runCommand.js +633 -0
- package/dist/tools/runCommand.js.map +1 -0
- package/dist/tools/saveMemory.js +102 -0
- package/dist/tools/saveMemory.js.map +1 -0
- package/dist/tools/screenshot.js +200 -0
- package/dist/tools/screenshot.js.map +1 -0
- package/dist/tools/screenshotWin.js +227 -0
- package/dist/tools/screenshotWin.js.map +1 -0
- package/dist/tools/search.js +121 -0
- package/dist/tools/search.js.map +1 -0
- package/dist/tools/sessionTools.js +187 -0
- package/dist/tools/sessionTools.js.map +1 -0
- package/dist/tools/shellCheckpoint.js +123 -0
- package/dist/tools/shellCheckpoint.js.map +1 -0
- package/dist/tools/shellLint.js +102 -0
- package/dist/tools/shellLint.js.map +1 -0
- package/dist/tools/shellTools.js +203 -0
- package/dist/tools/shellTools.js.map +1 -0
- package/dist/tools/spanCore.js +78 -0
- package/dist/tools/spanCore.js.map +1 -0
- package/dist/tools/subagent.js +224 -0
- package/dist/tools/subagent.js.map +1 -0
- package/dist/tools/subagentReport.js +114 -0
- package/dist/tools/subagentReport.js.map +1 -0
- package/dist/tools/tempSweep.js +118 -0
- package/dist/tools/tempSweep.js.map +1 -0
- package/dist/tools/todo.js +117 -0
- package/dist/tools/todo.js.map +1 -0
- package/dist/tools/types.js +2 -0
- package/dist/tools/types.js.map +1 -0
- package/dist/tools/untrusted.js +46 -0
- package/dist/tools/untrusted.js.map +1 -0
- package/dist/tools/useSkill.js +62 -0
- package/dist/tools/useSkill.js.map +1 -0
- package/dist/tools/walk.js +152 -0
- package/dist/tools/walk.js.map +1 -0
- package/dist/tools/web.js +61 -0
- package/dist/tools/web.js.map +1 -0
- package/dist/tools/webFetch.js +444 -0
- package/dist/tools/webFetch.js.map +1 -0
- package/dist/tools/webSearch.js +130 -0
- package/dist/tools/webSearch.js.map +1 -0
- package/dist/tools/workspace.js +212 -0
- package/dist/tools/workspace.js.map +1 -0
- package/dist/tools/workspaceDiscover.js +216 -0
- package/dist/tools/workspaceDiscover.js.map +1 -0
- package/dist/tools/writeFile.js +162 -0
- package/dist/tools/writeFile.js.map +1 -0
- package/package.json +74 -28
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* forbidden.ts — the user's per-project deny-list, enforced mechanically.
|
|
3
|
+
*
|
|
4
|
+
* This is the strong half of "forbidden": besides being told in the prompt, the
|
|
5
|
+
* mutating tools call these pure checks before acting, so a forbidden file
|
|
6
|
+
* literally cannot be edited/overwritten and a command that names a forbidden
|
|
7
|
+
* path is refused. It's the user-defined sibling of guard.ts's built-in
|
|
8
|
+
* protected-paths list — same shape (reason string or null), same fail-open rule
|
|
9
|
+
* (anything not matched is allowed).
|
|
10
|
+
*
|
|
11
|
+
* Format of `forbidden.md` (one pattern per line; `#` comments and blanks
|
|
12
|
+
* ignored): gitignore-style globs relative to the project root —
|
|
13
|
+
* src/legacy/** # a folder and everything under it
|
|
14
|
+
* config/prod.json # a single file
|
|
15
|
+
* *.pem # a glob
|
|
16
|
+
* A leading `/` or `./` is tolerated and treated as project-relative.
|
|
17
|
+
*/
|
|
18
|
+
import { isAbsolute, relative, resolve } from "node:path";
|
|
19
|
+
import { globToRegExp, literalPrefix } from "./glob.js";
|
|
20
|
+
/** Parse the raw text of a forbidden.md into a clean pattern list. */
|
|
21
|
+
export function parseForbidden(text) {
|
|
22
|
+
const patterns = [];
|
|
23
|
+
for (const line of text.split(/\r?\n/)) {
|
|
24
|
+
const trimmed = line.trim();
|
|
25
|
+
if (!trimmed || trimmed.startsWith("#"))
|
|
26
|
+
continue;
|
|
27
|
+
// Normalize a leading `/` or `./` to a plain project-relative pattern.
|
|
28
|
+
const cleaned = trimmed.replace(/^\.?\//, "").replace(/\/+$/, "");
|
|
29
|
+
if (cleaned)
|
|
30
|
+
patterns.push(cleaned);
|
|
31
|
+
}
|
|
32
|
+
return patterns;
|
|
33
|
+
}
|
|
34
|
+
/** Parse the raw text of a forbidden-commands.md into a clean pattern list. Unlike
|
|
35
|
+
* path patterns these are command fragments, so there is NO path normalization —
|
|
36
|
+
* a line is kept verbatim (trimmed), minus `#` comments and blanks. */
|
|
37
|
+
export function parseForbiddenCommands(text) {
|
|
38
|
+
const patterns = [];
|
|
39
|
+
for (const line of text.split(/\r?\n/)) {
|
|
40
|
+
const trimmed = line.trim();
|
|
41
|
+
if (!trimmed || trimmed.startsWith("#"))
|
|
42
|
+
continue;
|
|
43
|
+
patterns.push(trimmed);
|
|
44
|
+
}
|
|
45
|
+
return patterns;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* If `command` matches a forbidden command pattern, return the pattern (for the
|
|
49
|
+
* message); otherwise null. Match is case-insensitive on whitespace-collapsed text,
|
|
50
|
+
* so `tauri dev` blocks `npm run tauri dev` alike. Deterministic and strict by
|
|
51
|
+
* design — the model cannot run a forbidden command; only the user can lift it.
|
|
52
|
+
*/
|
|
53
|
+
/**
|
|
54
|
+
* Parse `forbidden-mcp-tools.md` into tool names. Verbatim like commands, not
|
|
55
|
+
* path-normalized: an MCP tool name is an identifier, not a path.
|
|
56
|
+
*/
|
|
57
|
+
export function parseForbiddenMcpTools(text) {
|
|
58
|
+
return parseForbiddenCommands(text);
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Compile one command pattern into a word-boundary matcher (pure).
|
|
62
|
+
*
|
|
63
|
+
* A bare substring test is what this used to do, and it over-blocks badly on short
|
|
64
|
+
* patterns: forbidding `rm` also refused `npm run warm` and `npm run format`, which
|
|
65
|
+
* reads as the agent being broken rather than as a rule firing.
|
|
66
|
+
*
|
|
67
|
+
* Two details make the boundary version actually work:
|
|
68
|
+
*
|
|
69
|
+
* - The pattern is user text and can hold regex metacharacters (`c++`, `a|b`), so it
|
|
70
|
+
* is escaped before it becomes a pattern.
|
|
71
|
+
* - `\b` is defined between a word character and a non-word one, so a pattern that
|
|
72
|
+
* starts or ends with something else (`-rf`, `./deploy`, `--force`) would never
|
|
73
|
+
* match with `\b` glued on. The boundary is therefore applied only at an edge that
|
|
74
|
+
* is itself a word character; other edges match anywhere, which is correct because
|
|
75
|
+
* the neighbouring character is already punctuation.
|
|
76
|
+
*/
|
|
77
|
+
export function commandPatternRegExp(pattern) {
|
|
78
|
+
const pat = pattern.toLowerCase().replace(/\s+/g, " ").trim();
|
|
79
|
+
if (!pat)
|
|
80
|
+
return null;
|
|
81
|
+
const escaped = pat.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
82
|
+
const left = /^\w/.test(pat) ? "\\b" : "";
|
|
83
|
+
const right = /\w$/.test(pat) ? "\\b" : "";
|
|
84
|
+
return new RegExp(`${left}${escaped}${right}`);
|
|
85
|
+
}
|
|
86
|
+
export function forbiddenCommandPatternReason(cfg, command) {
|
|
87
|
+
const commands = cfg?.commands;
|
|
88
|
+
if (!commands || commands.length === 0)
|
|
89
|
+
return null;
|
|
90
|
+
// Whitespace is collapsed on BOTH sides, so `tauri dev` still blocks
|
|
91
|
+
// `npm run tauri dev` — the run of spaces in the command becomes one.
|
|
92
|
+
const norm = command.toLowerCase().replace(/\s+/g, " ");
|
|
93
|
+
for (const raw of commands) {
|
|
94
|
+
const re = commandPatternRegExp(raw);
|
|
95
|
+
if (re && re.test(norm))
|
|
96
|
+
return raw;
|
|
97
|
+
}
|
|
98
|
+
return null;
|
|
99
|
+
}
|
|
100
|
+
function toPosix(p) {
|
|
101
|
+
return p.split("\\").join("/");
|
|
102
|
+
}
|
|
103
|
+
// Cache compilation per `patterns` array (tiny lists, but checks run per
|
|
104
|
+
// edit/write/command). Keyed on the array identity, which is stable for a session.
|
|
105
|
+
const cache = new WeakMap();
|
|
106
|
+
function compile(patterns) {
|
|
107
|
+
let compiled = cache.get(patterns);
|
|
108
|
+
if (!compiled) {
|
|
109
|
+
compiled = patterns.map((raw) => ({ raw, re: globToRegExp(raw), prefix: literalPrefix(raw) }));
|
|
110
|
+
cache.set(patterns, compiled);
|
|
111
|
+
}
|
|
112
|
+
return compiled;
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* If `absPath` is forbidden, return the pattern that matched (for the message);
|
|
116
|
+
* otherwise null. Paths outside the project root are never matched by relative
|
|
117
|
+
* patterns. `cfg` may be undefined (no forbidden list) → always allow.
|
|
118
|
+
*/
|
|
119
|
+
export function forbiddenPathReason(cfg, absPath, roots = []) {
|
|
120
|
+
if (!cfg || cfg.patterns.length === 0)
|
|
121
|
+
return null;
|
|
122
|
+
const abs = isAbsolute(absPath) ? absPath : resolve(cfg.root, absPath);
|
|
123
|
+
// EVERY root in the workspace, not just the one the project started in.
|
|
124
|
+
//
|
|
125
|
+
// A pattern is written relative to a project, and a session can hold several once
|
|
126
|
+
// `/include` has added a folder. Judging only against the primary meant a rule that
|
|
127
|
+
// blocked `src/legacy` in the project you opened silently did nothing in the folder
|
|
128
|
+
// you added, which is the shape of failure a deny-list can least afford: the rule is
|
|
129
|
+
// still listed, still shown by `/forbidden`, and simply not in force.
|
|
130
|
+
//
|
|
131
|
+
// Erring towards denying is deliberate and the same call made for case folding. A
|
|
132
|
+
// pattern that matches something in an included folder the user did not have in mind
|
|
133
|
+
// costs one refusal they can adjust; the other direction costs the protection itself.
|
|
134
|
+
for (const base of [cfg.root, ...roots]) {
|
|
135
|
+
const hit = matchUnderRoot(cfg, abs, base);
|
|
136
|
+
if (hit)
|
|
137
|
+
return hit;
|
|
138
|
+
}
|
|
139
|
+
return null;
|
|
140
|
+
}
|
|
141
|
+
/** Test one absolute path against the patterns, read relative to `base`. */
|
|
142
|
+
function matchUnderRoot(cfg, abs, base) {
|
|
143
|
+
const rel = toPosix(relative(base, abs));
|
|
144
|
+
if (rel === "" || rel.startsWith(".."))
|
|
145
|
+
return null; // the root itself / outside it
|
|
146
|
+
for (const { raw, re, prefix } of compile(cfg.patterns)) {
|
|
147
|
+
if (re.test(rel))
|
|
148
|
+
return raw;
|
|
149
|
+
// A bare folder/file prefix matches the path itself and anything under it,
|
|
150
|
+
// so `src/legacy` forbids `src/legacy` and `src/legacy/x.ts` alike.
|
|
151
|
+
// Case-folded, matching globToRegExp: on Windows and macOS `src/Legacy` IS
|
|
152
|
+
// `src/legacy`, and a sensitive compare let the other spelling through.
|
|
153
|
+
const lower = prefix.toLowerCase();
|
|
154
|
+
const relLower = rel.toLowerCase();
|
|
155
|
+
if (prefix && (relLower === lower || relLower.startsWith(lower + "/")))
|
|
156
|
+
return raw;
|
|
157
|
+
}
|
|
158
|
+
return null;
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* If `command` references a forbidden path, return the pattern that matched;
|
|
162
|
+
* otherwise null. Best a static check can do for a shell: if a forbidden
|
|
163
|
+
* pattern's literal portion appears anywhere in the command string, refuse it
|
|
164
|
+
* (the chosen strict policy — better a false positive the user can adjust than a
|
|
165
|
+
* silent shell bypass of a forbidden file).
|
|
166
|
+
*/
|
|
167
|
+
export function forbiddenCommandReason(cfg, command) {
|
|
168
|
+
if (!cfg || cfg.patterns.length === 0)
|
|
169
|
+
return null;
|
|
170
|
+
for (const { raw, prefix } of compile(cfg.patterns)) {
|
|
171
|
+
// Need a meaningful literal to scan for; a pure-glob pattern like `*.pem`
|
|
172
|
+
// has prefix "" and can't be located in free-form command text.
|
|
173
|
+
// Case-folded like the path check. `cat SRC/LEGACY/keys` names the same file on
|
|
174
|
+
// Windows as `src/legacy/keys`, and a sensitive scan is the shell bypass this
|
|
175
|
+
// function exists to prevent, spelled slightly differently.
|
|
176
|
+
if (prefix.length >= 2 && command.toLowerCase().includes(prefix.toLowerCase()))
|
|
177
|
+
return raw;
|
|
178
|
+
}
|
|
179
|
+
return null;
|
|
180
|
+
}
|
|
181
|
+
//# sourceMappingURL=forbidden.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"forbidden.js","sourceRoot":"","sources":["../../src/governor/forbidden.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* freshness.ts — noticing that the user edited their governance by hand.
|
|
3
|
+
*
|
|
4
|
+
* Rules, skills and the forbidden lists are files. The model writes them through tools,
|
|
5
|
+
* but a person editing `rules/use-pnpm.md` in their editor is at least as likely, and
|
|
6
|
+
* that edit used to do nothing at all until the session was restarted — silently, with
|
|
7
|
+
* the old rule still being enforced and nothing on screen to say so.
|
|
8
|
+
*
|
|
9
|
+
* Freshness is kept two different ways, and both are here because they answer
|
|
10
|
+
* different failure modes:
|
|
11
|
+
*
|
|
12
|
+
* 1. STAT FRESHNESS. A watched config is re-read when
|
|
13
|
+
* mtime moves, guarded so their own writes do not cause a re-read and so a
|
|
14
|
+
* concurrent write-through is never regressed to a stale snapshot. `stamp()` below
|
|
15
|
+
* is the same signal (mtime + size), sampled once per turn rather than by a
|
|
16
|
+
* persistent watcher: governance is only ever consulted while building a request,
|
|
17
|
+
* so checking it when it is used costs one stat pass and needs no watcher
|
|
18
|
+
* lifecycle, no polling thread, and no cleanup on exit.
|
|
19
|
+
*
|
|
20
|
+
* 2. LIFECYCLE INVALIDATION. A memoized snapshot has to be dropped at the moments that
|
|
21
|
+
* rebuild everything around it: starting a fresh conversation, and after a compaction.
|
|
22
|
+
* A compaction rebuilds the prompt from scratch, so it is the natural point to also
|
|
23
|
+
* rebuild what the prompt is made of. The engine forces a reload there.
|
|
24
|
+
*
|
|
25
|
+
* The stamp deliberately covers presence as well as content: a rule DELETED from disk
|
|
26
|
+
* has to stop applying, and a stamp built only from surviving files would miss it.
|
|
27
|
+
*/
|
|
28
|
+
import { promises as fs } from "node:fs";
|
|
29
|
+
import { join } from "node:path";
|
|
30
|
+
import { projectDir } from "../memory/store.js";
|
|
31
|
+
/** The files and directories a project's governance is read from. */
|
|
32
|
+
const FORBIDDEN_FILES = ["forbidden.md", "forbidden-commands.md", "forbidden-mcp-tools.md"];
|
|
33
|
+
/** One directory's entries as `name:mtime:size`, sorted so the stamp is order-stable. */
|
|
34
|
+
async function dirStamp(dir, depth) {
|
|
35
|
+
let names;
|
|
36
|
+
try {
|
|
37
|
+
names = (await fs.readdir(dir)).sort();
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
40
|
+
return `${dir}:-`; // absent is a state too — creating the first rule must register
|
|
41
|
+
}
|
|
42
|
+
const parts = [];
|
|
43
|
+
for (const name of names) {
|
|
44
|
+
const full = join(dir, name);
|
|
45
|
+
try {
|
|
46
|
+
const st = await fs.stat(full);
|
|
47
|
+
// Skills are a directory each (SKILL.md plus bundled files), so one level of
|
|
48
|
+
// recursion is what makes editing a skill body count as a change.
|
|
49
|
+
if (st.isDirectory() && depth > 0)
|
|
50
|
+
parts.push(`${name}/(${await dirStamp(full, depth - 1)})`);
|
|
51
|
+
else
|
|
52
|
+
parts.push(`${name}:${st.mtimeMs}:${st.size}`);
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
parts.push(`${name}:?`);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
return parts.join(",");
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* A cheap fingerprint of everything `loadGovernance` reads.
|
|
62
|
+
*
|
|
63
|
+
* Compared as an opaque string: any difference means reload, and the cost of a false
|
|
64
|
+
* positive is one directory read, so it errs towards reloading.
|
|
65
|
+
*/
|
|
66
|
+
export async function governanceStamp(cwd) {
|
|
67
|
+
const stateDir = projectDir(cwd);
|
|
68
|
+
const [rules, skills, ...files] = await Promise.all([
|
|
69
|
+
dirStamp(join(stateDir, "rules"), 0),
|
|
70
|
+
dirStamp(join(stateDir, "skills"), 1),
|
|
71
|
+
...FORBIDDEN_FILES.map(async (name) => {
|
|
72
|
+
try {
|
|
73
|
+
const st = await fs.stat(join(stateDir, name));
|
|
74
|
+
return `${name}:${st.mtimeMs}:${st.size}`;
|
|
75
|
+
}
|
|
76
|
+
catch {
|
|
77
|
+
return `${name}:-`;
|
|
78
|
+
}
|
|
79
|
+
}),
|
|
80
|
+
]);
|
|
81
|
+
return `r[${rules}] s[${skills}] f[${files.join("|")}]`;
|
|
82
|
+
}
|
|
83
|
+
//# sourceMappingURL=freshness.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"freshness.js","sourceRoot":"","sources":["../../src/governor/freshness.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,OAAO,EAAE,QAAQ,IAAI,EAAE,EAAE,MAAM,SAAS,CAAC;AACzC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAEhD,qEAAqE;AACrE,MAAM,eAAe,GAAG,CAAC,cAAc,EAAE,uBAAuB,EAAE,wBAAwB,CAAC,CAAC;AAE5F,yFAAyF;AACzF,KAAK,UAAU,QAAQ,CAAC,GAAW,EAAE,KAAa;IAChD,IAAI,KAAe,CAAC;IACpB,IAAI,CAAC;QACH,KAAK,GAAG,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;IACzC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,GAAG,GAAG,IAAI,CAAC,CAAC,gEAAgE;IACrF,CAAC;IACD,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;QAC7B,IAAI,CAAC;YACH,MAAM,EAAE,GAAG,MAAM,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAC/B,6EAA6E;YAC7E,kEAAkE;YAClE,IAAI,EAAE,CAAC,WAAW,EAAE,IAAI,KAAK,GAAG,CAAC;gBAAE,KAAK,CAAC,IAAI,CAAC,GAAG,IAAI,KAAK,MAAM,QAAQ,CAAC,IAAI,EAAE,KAAK,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC;;gBACzF,KAAK,CAAC,IAAI,CAAC,GAAG,IAAI,IAAI,EAAE,CAAC,OAAO,IAAI,EAAE,CAAC,IAAI,EAAE,CAAC,CAAC;QACtD,CAAC;QAAC,MAAM,CAAC;YACP,KAAK,CAAC,IAAI,CAAC,GAAG,IAAI,IAAI,CAAC,CAAC;QAC1B,CAAC;IACH,CAAC;IACD,OAAO,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AACzB,CAAC;AAED;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,GAAW;IAC/C,MAAM,QAAQ,GAAG,UAAU,CAAC,GAAG,CAAC,CAAC;IACjC,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,KAAK,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;QAClD,QAAQ,CAAC,IAAI,CAAC,QAAQ,EAAE,OAAO,CAAC,EAAE,CAAC,CAAC;QACpC,QAAQ,CAAC,IAAI,CAAC,QAAQ,EAAE,QAAQ,CAAC,EAAE,CAAC,CAAC;QACrC,GAAG,eAAe,CAAC,GAAG,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE;YACpC,IAAI,CAAC;gBACH,MAAM,EAAE,GAAG,MAAM,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC,CAAC;gBAC/C,OAAO,GAAG,IAAI,IAAI,EAAE,CAAC,OAAO,IAAI,EAAE,CAAC,IAAI,EAAE,CAAC;YAC5C,CAAC;YAAC,MAAM,CAAC;gBACP,OAAO,GAAG,IAAI,IAAI,CAAC;YACrB,CAAC;QACH,CAAC,CAAC;KACH,CAAC,CAAC;IACH,OAAO,KAAK,KAAK,OAAO,MAAM,OAAO,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC;AAC1D,CAAC"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* frontmatter.ts — a tiny YAML-frontmatter reader for governor files.
|
|
3
|
+
*
|
|
4
|
+
* Rules and skills are plain markdown with a small `--- … ---` header, the same
|
|
5
|
+
* shape widely used for agent rules and skills. We
|
|
6
|
+
* only need flat `key: value` pairs, so this is a deliberately minimal parser —
|
|
7
|
+
* no YAML dependency, no nesting, no lists. Anything fancier than `key: value`
|
|
8
|
+
* is ignored rather than erroring, so a hand-edited file never breaks loading.
|
|
9
|
+
*/
|
|
10
|
+
/** Split a markdown file into its frontmatter header and body. */
|
|
11
|
+
export function parseFrontmatter(raw) {
|
|
12
|
+
// Strip a UTF-8 BOM so a `---` on line 1 is still recognized.
|
|
13
|
+
const text = raw.replace(/^/, "");
|
|
14
|
+
const match = /^---\r?\n([\s\S]*?)\r?\n---\r?\n?/.exec(text);
|
|
15
|
+
if (!match)
|
|
16
|
+
return { data: {}, body: text.trim() };
|
|
17
|
+
const data = {};
|
|
18
|
+
for (const line of match[1].split(/\r?\n/)) {
|
|
19
|
+
const trimmed = line.trim();
|
|
20
|
+
if (!trimmed || trimmed.startsWith("#"))
|
|
21
|
+
continue;
|
|
22
|
+
const colon = trimmed.indexOf(":");
|
|
23
|
+
if (colon === -1)
|
|
24
|
+
continue;
|
|
25
|
+
const key = trimmed.slice(0, colon).trim();
|
|
26
|
+
let value = trimmed.slice(colon + 1).trim();
|
|
27
|
+
// Unwrap a single layer of surrounding quotes.
|
|
28
|
+
if (value.length >= 2 &&
|
|
29
|
+
((value.startsWith('"') && value.endsWith('"')) ||
|
|
30
|
+
(value.startsWith("'") && value.endsWith("'")))) {
|
|
31
|
+
value = value.slice(1, -1);
|
|
32
|
+
}
|
|
33
|
+
if (key)
|
|
34
|
+
data[key] = value;
|
|
35
|
+
}
|
|
36
|
+
return { data, body: text.slice(match[0].length).trim() };
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=frontmatter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"frontmatter.js","sourceRoot":"","sources":["../../src/governor/frontmatter.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AASH,kEAAkE;AAClE,MAAM,UAAU,gBAAgB,CAAC,GAAW;IAC1C,8DAA8D;IAC9D,MAAM,IAAI,GAAG,GAAG,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;IACnC,MAAM,KAAK,GAAG,mCAAmC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC7D,IAAI,CAAC,KAAK;QAAE,OAAO,EAAE,IAAI,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,EAAE,CAAC;IAEnD,MAAM,IAAI,GAA2B,EAAE,CAAC;IACxC,KAAK,MAAM,IAAI,IAAI,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,EAAE,CAAC;QAC3C,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;QAC5B,IAAI,CAAC,OAAO,IAAI,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC;YAAE,SAAS;QAClD,MAAM,KAAK,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QACnC,IAAI,KAAK,KAAK,CAAC,CAAC;YAAE,SAAS;QAC3B,MAAM,GAAG,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,IAAI,EAAE,CAAC;QAC3C,IAAI,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;QAC5C,+CAA+C;QAC/C,IACE,KAAK,CAAC,MAAM,IAAI,CAAC;YACjB,CAAC,CAAC,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;gBAC7C,CAAC,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EACjD,CAAC;YACD,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;QAC7B,CAAC;QACD,IAAI,GAAG;YAAE,IAAI,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;IAC7B,CAAC;IAED,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC;AAC5D,CAAC"}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* glob.ts — the one gitignore-ish path matcher the governor shares.
|
|
3
|
+
*
|
|
4
|
+
* Both the forbidden deny-list and glob-scoped rules ask the same question: does
|
|
5
|
+
* this project-relative path match a pattern? `**` spans path segments, `*`/`?`
|
|
6
|
+
* stay within one, and a bare folder/file prefix (no wildcards) matches the path
|
|
7
|
+
* itself and everything under it (so `src/api` covers `src/api/x.ts`).
|
|
8
|
+
*/
|
|
9
|
+
/** The non-wildcard prefix of a pattern (used for dir matching + command scan). */
|
|
10
|
+
export function literalPrefix(pattern) {
|
|
11
|
+
const wildcard = pattern.search(/[*?]/);
|
|
12
|
+
const prefix = wildcard === -1 ? pattern : pattern.slice(0, wildcard);
|
|
13
|
+
return prefix.replace(/\/+$/, "");
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Translate a glob to an anchored RegExp (`**` spans segments, `*`/`?` don't).
|
|
17
|
+
*
|
|
18
|
+
* CASE-INSENSITIVE, and that is a correctness fix rather than a convenience. Windows
|
|
19
|
+
* and macOS filesystems are case-insensitive, so `.env` and `.ENV` are one file — but
|
|
20
|
+
* this matcher backs the forbidden deny-list, and a case-sensitive compare let the
|
|
21
|
+
* second spelling straight past a rule written with the first. Nothing adversarial was
|
|
22
|
+
* needed: a model that writes `.ENV` because it saw that spelling somewhere silently
|
|
23
|
+
* escaped the deny-list and wrote the very file it was forbidden.
|
|
24
|
+
*
|
|
25
|
+
* Always, rather than switched on the platform. A deny-list should err towards denying,
|
|
26
|
+
* and a guard that protects you on one machine and not another is worse than one that
|
|
27
|
+
* is merely slightly broad: it makes the same rule mean two things. The cost on a
|
|
28
|
+
* case-sensitive filesystem is that `src/Foo` and `src/foo`, genuinely different files
|
|
29
|
+
* there, are covered by one pattern. For a deny-list that is the safe direction, and
|
|
30
|
+
* for glob-scoped rules, which are advisory, it is harmless.
|
|
31
|
+
*/
|
|
32
|
+
export function globToRegExp(glob) {
|
|
33
|
+
let re = "";
|
|
34
|
+
for (let i = 0; i < glob.length; i++) {
|
|
35
|
+
const c = glob[i];
|
|
36
|
+
if (c === "*") {
|
|
37
|
+
if (glob[i + 1] === "*") {
|
|
38
|
+
re += ".*";
|
|
39
|
+
i++;
|
|
40
|
+
if (glob[i + 1] === "/")
|
|
41
|
+
i++; // collapse `**/` so it can match zero segments
|
|
42
|
+
}
|
|
43
|
+
else {
|
|
44
|
+
re += "[^/]*";
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
else if (c === "?") {
|
|
48
|
+
re += "[^/]";
|
|
49
|
+
}
|
|
50
|
+
else if ("\\^$.|+()[]{}".includes(c)) {
|
|
51
|
+
re += "\\" + c;
|
|
52
|
+
}
|
|
53
|
+
else {
|
|
54
|
+
re += c;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
return new RegExp("^" + re + "$", "i");
|
|
58
|
+
}
|
|
59
|
+
/** Does one project-relative POSIX path match one glob pattern? */
|
|
60
|
+
export function globMatch(relPosixPath, glob) {
|
|
61
|
+
if (globToRegExp(glob).test(relPosixPath))
|
|
62
|
+
return true;
|
|
63
|
+
const prefix = literalPrefix(glob);
|
|
64
|
+
// Folded to one case for the same reason the regex is: a bare-prefix pattern is the
|
|
65
|
+
// commonest kind of forbidden entry (`src/legacy`, `secrets`), so a case-sensitive
|
|
66
|
+
// compare here would leave the widest half of the deny-list bypassable.
|
|
67
|
+
const path = relPosixPath.toLowerCase();
|
|
68
|
+
const lower = prefix.toLowerCase();
|
|
69
|
+
return prefix !== "" && (path === lower || path.startsWith(lower + "/"));
|
|
70
|
+
}
|
|
71
|
+
/** Does any of `relPaths` match any of `globs`? */
|
|
72
|
+
export function anyPathMatches(relPaths, globs) {
|
|
73
|
+
return globs.some((g) => relPaths.some((p) => globMatch(p, g)));
|
|
74
|
+
}
|
|
75
|
+
//# sourceMappingURL=glob.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"glob.js","sourceRoot":"","sources":["../../src/governor/glob.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,mFAAmF;AACnF,MAAM,UAAU,aAAa,CAAC,OAAe;IAC3C,MAAM,QAAQ,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IACxC,MAAM,MAAM,GAAG,QAAQ,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC;IACtE,OAAO,MAAM,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;AACpC,CAAC;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,UAAU,YAAY,CAAC,IAAY;IACvC,IAAI,EAAE,GAAG,EAAE,CAAC;IACZ,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACrC,MAAM,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,IAAI,CAAC,KAAK,GAAG,EAAE,CAAC;YACd,IAAI,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,GAAG,EAAE,CAAC;gBACxB,EAAE,IAAI,IAAI,CAAC;gBACX,CAAC,EAAE,CAAC;gBACJ,IAAI,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,GAAG;oBAAE,CAAC,EAAE,CAAC,CAAC,+CAA+C;YAC/E,CAAC;iBAAM,CAAC;gBACN,EAAE,IAAI,OAAO,CAAC;YAChB,CAAC;QACH,CAAC;aAAM,IAAI,CAAC,KAAK,GAAG,EAAE,CAAC;YACrB,EAAE,IAAI,MAAM,CAAC;QACf,CAAC;aAAM,IAAI,eAAe,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC;YACvC,EAAE,IAAI,IAAI,GAAG,CAAC,CAAC;QACjB,CAAC;aAAM,CAAC;YACN,EAAE,IAAI,CAAC,CAAC;QACV,CAAC;IACH,CAAC;IACD,OAAO,IAAI,MAAM,CAAC,GAAG,GAAG,EAAE,GAAG,GAAG,EAAE,GAAG,CAAC,CAAC;AACzC,CAAC;AAED,mEAAmE;AACnE,MAAM,UAAU,SAAS,CAAC,YAAoB,EAAE,IAAY;IAC1D,IAAI,YAAY,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,YAAY,CAAC;QAAE,OAAO,IAAI,CAAC;IACvD,MAAM,MAAM,GAAG,aAAa,CAAC,IAAI,CAAC,CAAC;IACnC,oFAAoF;IACpF,mFAAmF;IACnF,wEAAwE;IACxE,MAAM,IAAI,GAAG,YAAY,CAAC,WAAW,EAAE,CAAC;IACxC,MAAM,KAAK,GAAG,MAAM,CAAC,WAAW,EAAE,CAAC;IACnC,OAAO,MAAM,KAAK,EAAE,IAAI,CAAC,IAAI,KAAK,KAAK,IAAI,IAAI,CAAC,UAAU,CAAC,KAAK,GAAG,GAAG,CAAC,CAAC,CAAC;AAC3E,CAAC;AAED,mDAAmD;AACnD,MAAM,UAAU,cAAc,CAAC,QAAkB,EAAE,KAAe;IAChE,OAAO,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,SAAS,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;AAClE,CAAC"}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* governor — the per-project control layer (rules · skills · forbidden).
|
|
3
|
+
*
|
|
4
|
+
* Like an engine's governor regulates how it may run, this regulates how Mindweave
|
|
5
|
+
* works inside a project: the standing rules it must follow, the skills it can
|
|
6
|
+
* run, and the paths/actions it must never touch. Everything is scoped to one
|
|
7
|
+
* project and stored under that project's state dir (`~/.mindweave/projects/<proj>/`),
|
|
8
|
+
* the same place sessions live — so a rule set in project A never leaks into B.
|
|
9
|
+
* (Global rules/skills are a planned second layer; the per-project design leaves
|
|
10
|
+
* a clean seam for merging one on top.)
|
|
11
|
+
*
|
|
12
|
+
* `loadGovernance` reads it all once at session start (cheap: a couple of small
|
|
13
|
+
* directory reads). The forbidden config rides on the tool context for
|
|
14
|
+
* mechanical enforcement; the rules and skill catalog are rendered into the
|
|
15
|
+
* system prompt by the engine.
|
|
16
|
+
*/
|
|
17
|
+
import { projectDir } from "../memory/store.js";
|
|
18
|
+
import { promises as fs } from "node:fs";
|
|
19
|
+
import { join } from "node:path";
|
|
20
|
+
import { loadRules } from "./rules.js";
|
|
21
|
+
import { loadSkillCatalog } from "./skills.js";
|
|
22
|
+
import { parseForbidden, parseForbiddenCommands, parseForbiddenMcpTools } from "./forbidden.js";
|
|
23
|
+
/** Read a project state file (forbidden.md / forbidden-commands.md), "" if absent. */
|
|
24
|
+
async function readStateFile(stateDir, name) {
|
|
25
|
+
try {
|
|
26
|
+
return await fs.readFile(join(stateDir, name), "utf8");
|
|
27
|
+
}
|
|
28
|
+
catch {
|
|
29
|
+
return "";
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
/** Load all governance for the project rooted at `cwd`. Always succeeds (empties). */
|
|
33
|
+
export async function loadGovernance(cwd) {
|
|
34
|
+
const stateDir = projectDir(cwd);
|
|
35
|
+
const [rules, skills, forbiddenText, forbiddenCmdText, forbiddenMcpText] = await Promise.all([
|
|
36
|
+
loadRules(stateDir),
|
|
37
|
+
loadSkillCatalog(stateDir),
|
|
38
|
+
readStateFile(stateDir, "forbidden.md"),
|
|
39
|
+
readStateFile(stateDir, "forbidden-commands.md"),
|
|
40
|
+
readStateFile(stateDir, "forbidden-mcp-tools.md"),
|
|
41
|
+
]);
|
|
42
|
+
return {
|
|
43
|
+
rules,
|
|
44
|
+
skills,
|
|
45
|
+
forbidden: {
|
|
46
|
+
patterns: parseForbidden(forbiddenText),
|
|
47
|
+
commands: parseForbiddenCommands(forbiddenCmdText),
|
|
48
|
+
mcpTools: parseForbiddenMcpTools(forbiddenMcpText),
|
|
49
|
+
root: cwd,
|
|
50
|
+
},
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
export { renderRules } from "./rules.js";
|
|
54
|
+
export { renderSkillCatalog } from "./skills.js";
|
|
55
|
+
/**
|
|
56
|
+
* Re-read governance from disk, keeping everything that exists only in this session.
|
|
57
|
+
*
|
|
58
|
+
* Three things do NOT come from disk and must survive a reload:
|
|
59
|
+
* - `lifted` — patterns the user allowed for this session only (approval.ts). The
|
|
60
|
+
* on-disk rule is intentionally left in place, so a plain reload would restore it
|
|
61
|
+
* and re-block a path the user had just permitted.
|
|
62
|
+
* - `notices` — one-shot lines the UI has not drained yet. Dropping them loses the
|
|
63
|
+
* message rather than delaying it.
|
|
64
|
+
* - the fired rule-scope, which is not on `Governance` at all: it lives on the tool
|
|
65
|
+
* context and is re-judged against the new rule list by the caller.
|
|
66
|
+
*
|
|
67
|
+
* Returns a NEW object rather than mutating: the forbidden matcher caches compiled
|
|
68
|
+
* patterns in a WeakMap keyed on array identity, so fresh arrays are what make it
|
|
69
|
+
* recompile instead of enforcing the old list.
|
|
70
|
+
*/
|
|
71
|
+
export async function reloadGovernance(cwd, previous) {
|
|
72
|
+
const fresh = await loadGovernance(cwd);
|
|
73
|
+
const lifted = previous.lifted ?? [];
|
|
74
|
+
return {
|
|
75
|
+
...fresh,
|
|
76
|
+
forbidden: {
|
|
77
|
+
...fresh.forbidden,
|
|
78
|
+
patterns: fresh.forbidden.patterns.filter((p) => !lifted.includes(p)),
|
|
79
|
+
commands: (fresh.forbidden.commands ?? []).filter((c) => !lifted.includes(c)),
|
|
80
|
+
},
|
|
81
|
+
...(lifted.length > 0 ? { lifted } : {}),
|
|
82
|
+
...(previous.notices && previous.notices.length > 0 ? { notices: previous.notices } : {}),
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
export { governanceStamp } from "./freshness.js";
|
|
86
|
+
export { createRuleScope, noteScopePath, rescope } from "./scope.js";
|
|
87
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/governor/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* rules.ts — the user's standing rules for a project.
|
|
3
|
+
*
|
|
4
|
+
* A rule is a directive the user has asked Mindweave to always follow here ("use
|
|
5
|
+
* pnpm, never npm"; "every component gets a test"). Each lives as its own
|
|
6
|
+
* markdown file under `<project-state>/rules/`, and ALL of them are injected into
|
|
7
|
+
* the system prompt every turn — this is the always-on layer of project directives.
|
|
8
|
+
* Per-file (not one big file) so a single rule
|
|
9
|
+
* can be added or removed cleanly, the same way the memory system works.
|
|
10
|
+
*
|
|
11
|
+
* "Make it a rule" is the model writing a new file here; the loader just reads
|
|
12
|
+
* whatever files exist, so hand-authored rules work identically.
|
|
13
|
+
*/
|
|
14
|
+
import { promises as fs } from "node:fs";
|
|
15
|
+
import { basename, join } from "node:path";
|
|
16
|
+
import { parseFrontmatter } from "./frontmatter.js";
|
|
17
|
+
/** Parse a frontmatter `globs` field (comma/space/newline separated) into a clean list. */
|
|
18
|
+
export function parseGlobs(raw) {
|
|
19
|
+
if (!raw)
|
|
20
|
+
return [];
|
|
21
|
+
return raw
|
|
22
|
+
.split(/[,\s]+/)
|
|
23
|
+
.map((g) => g.trim().replace(/^\.?\//, "").replace(/\/+$/, ""))
|
|
24
|
+
.filter((g) => g.length > 0);
|
|
25
|
+
}
|
|
26
|
+
/** Read every `rules/*.md` for the project, newest-named last (stable order). */
|
|
27
|
+
export async function loadRules(stateDir) {
|
|
28
|
+
const dir = join(stateDir, "rules");
|
|
29
|
+
let names;
|
|
30
|
+
try {
|
|
31
|
+
names = (await fs.readdir(dir)).filter((n) => n.toLowerCase().endsWith(".md")).sort();
|
|
32
|
+
}
|
|
33
|
+
catch {
|
|
34
|
+
return []; // no rules dir yet
|
|
35
|
+
}
|
|
36
|
+
const rules = [];
|
|
37
|
+
for (const name of names) {
|
|
38
|
+
try {
|
|
39
|
+
const raw = await fs.readFile(join(dir, name), "utf8");
|
|
40
|
+
const { data, body } = parseFrontmatter(raw);
|
|
41
|
+
if (!body)
|
|
42
|
+
continue; // an empty rule says nothing — skip it
|
|
43
|
+
const globs = parseGlobs(data.globs);
|
|
44
|
+
rules.push({
|
|
45
|
+
name: data.name || basename(name, ".md"),
|
|
46
|
+
description: data.description || "",
|
|
47
|
+
body,
|
|
48
|
+
...(globs.length > 0 ? { globs } : {}),
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
catch {
|
|
52
|
+
// Unreadable file — skip it rather than fail all rule loading.
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
return rules;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Pick the rules active for this turn: every always-on rule (no globs) plus any
|
|
59
|
+
* glob-scoped rule that has already FIRED — see scope.ts, which decides that once, when
|
|
60
|
+
* a matching path is touched, instead of re-deriving it from the whole working set on
|
|
61
|
+
* every model call.
|
|
62
|
+
*
|
|
63
|
+
* `fired` holds rule NAMES. Filtering by the live rule list rather than by that set is
|
|
64
|
+
* what makes a rule deleted from disk stop rendering even though its name is remembered.
|
|
65
|
+
*/
|
|
66
|
+
export function activeRules(rules, fired = new Set()) {
|
|
67
|
+
return rules.filter((r) => !r.globs || r.globs.length === 0 || fired.has(r.name));
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Render the active rules into the block injected in the system prompt. "" when
|
|
71
|
+
* none apply (the engine then omits the block). Content only — the engine wraps
|
|
72
|
+
* it in its `<rules>` framing, matching how the task list is done.
|
|
73
|
+
*/
|
|
74
|
+
export function renderRules(rules, fired = new Set()) {
|
|
75
|
+
const active = activeRules(rules, fired);
|
|
76
|
+
if (active.length === 0)
|
|
77
|
+
return "";
|
|
78
|
+
return active.map((r) => `- ${r.body}`).join("\n");
|
|
79
|
+
}
|
|
80
|
+
//# sourceMappingURL=rules.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rules.js","sourceRoot":"","sources":["../../src/governor/rules.ts"],"names":[],"mappings":"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"}
|