patchwork-os 0.2.0-alpha.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.bridge.md +352 -0
- package/README.md +72 -0
- package/deploy/README.md +172 -0
- package/deploy/bootstrap-new-vps.sh +364 -0
- package/deploy/claude-ide-bridge.service.template +67 -0
- package/deploy/claude-ide-bridge@.service +31 -0
- package/deploy/ecosystem.config.js.example +36 -0
- package/deploy/install-vps-service.sh +240 -0
- package/deploy/nginx-claude-bridge.conf.template +129 -0
- package/dist/activityLog.d.ts +112 -0
- package/dist/activityLog.js +399 -0
- package/dist/activityLog.js.map +1 -0
- package/dist/activityTypes.d.ts +28 -0
- package/dist/activityTypes.js +9 -0
- package/dist/activityTypes.js.map +1 -0
- package/dist/adapters/base.d.ts +78 -0
- package/dist/adapters/base.js +14 -0
- package/dist/adapters/base.js.map +1 -0
- package/dist/adapters/claude.d.ts +18 -0
- package/dist/adapters/claude.js +276 -0
- package/dist/adapters/claude.js.map +1 -0
- package/dist/adapters/gemini.d.ts +17 -0
- package/dist/adapters/gemini.js +218 -0
- package/dist/adapters/gemini.js.map +1 -0
- package/dist/adapters/grok.d.ts +7 -0
- package/dist/adapters/grok.js +21 -0
- package/dist/adapters/grok.js.map +1 -0
- package/dist/adapters/index.d.ts +5 -0
- package/dist/adapters/index.js +37 -0
- package/dist/adapters/index.js.map +1 -0
- package/dist/adapters/local.d.ts +7 -0
- package/dist/adapters/local.js +22 -0
- package/dist/adapters/local.js.map +1 -0
- package/dist/adapters/openai.d.ts +22 -0
- package/dist/adapters/openai.js +284 -0
- package/dist/adapters/openai.js.map +1 -0
- package/dist/adapters/sse.d.ts +13 -0
- package/dist/adapters/sse.js +58 -0
- package/dist/adapters/sse.js.map +1 -0
- package/dist/analyticsAggregator.d.ts +28 -0
- package/dist/analyticsAggregator.js +133 -0
- package/dist/analyticsAggregator.js.map +1 -0
- package/dist/analyticsPrefs.d.ts +9 -0
- package/dist/analyticsPrefs.js +50 -0
- package/dist/analyticsPrefs.js.map +1 -0
- package/dist/analyticsSend.d.ts +12 -0
- package/dist/analyticsSend.js +34 -0
- package/dist/analyticsSend.js.map +1 -0
- package/dist/approvalHttp.d.ts +46 -0
- package/dist/approvalHttp.js +370 -0
- package/dist/approvalHttp.js.map +1 -0
- package/dist/approvalQueue.d.ts +49 -0
- package/dist/approvalQueue.js +84 -0
- package/dist/approvalQueue.js.map +1 -0
- package/dist/automation.d.ts +675 -0
- package/dist/automation.js +1038 -0
- package/dist/automation.js.map +1 -0
- package/dist/bridge.d.ts +85 -0
- package/dist/bridge.js +1535 -0
- package/dist/bridge.js.map +1 -0
- package/dist/bridgeLockDiscovery.d.ts +11 -0
- package/dist/bridgeLockDiscovery.js +49 -0
- package/dist/bridgeLockDiscovery.js.map +1 -0
- package/dist/bridgeToken.d.ts +22 -0
- package/dist/bridgeToken.js +114 -0
- package/dist/bridgeToken.js.map +1 -0
- package/dist/bridgeToolsRules.d.ts +20 -0
- package/dist/bridgeToolsRules.js +79 -0
- package/dist/bridgeToolsRules.js.map +1 -0
- package/dist/ccPermissions.d.ts +59 -0
- package/dist/ccPermissions.js +163 -0
- package/dist/ccPermissions.js.map +1 -0
- package/dist/claudeDriver.d.ts +129 -0
- package/dist/claudeDriver.js +459 -0
- package/dist/claudeDriver.js.map +1 -0
- package/dist/claudeMdPatch.d.ts +29 -0
- package/dist/claudeMdPatch.js +164 -0
- package/dist/claudeMdPatch.js.map +1 -0
- package/dist/claudeOrchestrator.d.ts +171 -0
- package/dist/claudeOrchestrator.js +591 -0
- package/dist/claudeOrchestrator.js.map +1 -0
- package/dist/commands/install.d.ts +1 -0
- package/dist/commands/install.js +158 -0
- package/dist/commands/install.js.map +1 -0
- package/dist/commands/marketplace.d.ts +11 -0
- package/dist/commands/marketplace.js +120 -0
- package/dist/commands/marketplace.js.map +1 -0
- package/dist/commands/patchworkInit.d.ts +14 -0
- package/dist/commands/patchworkInit.js +155 -0
- package/dist/commands/patchworkInit.js.map +1 -0
- package/dist/commands/task.d.ts +14 -0
- package/dist/commands/task.js +289 -0
- package/dist/commands/task.js.map +1 -0
- package/dist/commands/tokenEfficiency.d.ts +9 -0
- package/dist/commands/tokenEfficiency.js +211 -0
- package/dist/commands/tokenEfficiency.js.map +1 -0
- package/dist/commands/tools.d.ts +28 -0
- package/dist/commands/tools.js +326 -0
- package/dist/commands/tools.js.map +1 -0
- package/dist/commitIssueLinkLog.d.ts +77 -0
- package/dist/commitIssueLinkLog.js +142 -0
- package/dist/commitIssueLinkLog.js.map +1 -0
- package/dist/companions/registry.d.ts +12 -0
- package/dist/companions/registry.js +71 -0
- package/dist/companions/registry.js.map +1 -0
- package/dist/config.d.ts +105 -0
- package/dist/config.js +720 -0
- package/dist/config.js.map +1 -0
- package/dist/crypto.d.ts +16 -0
- package/dist/crypto.js +34 -0
- package/dist/crypto.js.map +1 -0
- package/dist/dashboard.d.ts +12 -0
- package/dist/dashboard.js +149 -0
- package/dist/dashboard.js.map +1 -0
- package/dist/decisionTraceLog.d.ts +77 -0
- package/dist/decisionTraceLog.js +147 -0
- package/dist/decisionTraceLog.js.map +1 -0
- package/dist/errors.d.ts +32 -0
- package/dist/errors.js +34 -0
- package/dist/errors.js.map +1 -0
- package/dist/extensionClient.d.ts +279 -0
- package/dist/extensionClient.js +1253 -0
- package/dist/extensionClient.js.map +1 -0
- package/dist/fileLock.d.ts +36 -0
- package/dist/fileLock.js +121 -0
- package/dist/fileLock.js.map +1 -0
- package/dist/fp/activityAnalytics.d.ts +39 -0
- package/dist/fp/activityAnalytics.js +111 -0
- package/dist/fp/activityAnalytics.js.map +1 -0
- package/dist/fp/async.d.ts +48 -0
- package/dist/fp/async.js +60 -0
- package/dist/fp/async.js.map +1 -0
- package/dist/fp/automationInterpreter.d.ts +37 -0
- package/dist/fp/automationInterpreter.js +523 -0
- package/dist/fp/automationInterpreter.js.map +1 -0
- package/dist/fp/automationProgram.d.ts +89 -0
- package/dist/fp/automationProgram.js +29 -0
- package/dist/fp/automationProgram.js.map +1 -0
- package/dist/fp/automationState.d.ts +135 -0
- package/dist/fp/automationState.js +206 -0
- package/dist/fp/automationState.js.map +1 -0
- package/dist/fp/automationUtils.d.ts +31 -0
- package/dist/fp/automationUtils.js +61 -0
- package/dist/fp/automationUtils.js.map +1 -0
- package/dist/fp/brandedTypes.d.ts +32 -0
- package/dist/fp/brandedTypes.js +41 -0
- package/dist/fp/brandedTypes.js.map +1 -0
- package/dist/fp/commandDescription.d.ts +18 -0
- package/dist/fp/commandDescription.js +125 -0
- package/dist/fp/commandDescription.js.map +1 -0
- package/dist/fp/extensionSnapshot.d.ts +10 -0
- package/dist/fp/extensionSnapshot.js +14 -0
- package/dist/fp/extensionSnapshot.js.map +1 -0
- package/dist/fp/index.d.ts +8 -0
- package/dist/fp/index.js +9 -0
- package/dist/fp/index.js.map +1 -0
- package/dist/fp/interpreterContext.d.ts +69 -0
- package/dist/fp/interpreterContext.js +56 -0
- package/dist/fp/interpreterContext.js.map +1 -0
- package/dist/fp/policyParser.d.ts +16 -0
- package/dist/fp/policyParser.js +334 -0
- package/dist/fp/policyParser.js.map +1 -0
- package/dist/fp/result.d.ts +38 -0
- package/dist/fp/result.js +57 -0
- package/dist/fp/result.js.map +1 -0
- package/dist/fp/tokenBucket.d.ts +27 -0
- package/dist/fp/tokenBucket.js +36 -0
- package/dist/fp/tokenBucket.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +1465 -0
- package/dist/index.js.map +1 -0
- package/dist/instructionsUtils.d.ts +17 -0
- package/dist/instructionsUtils.js +38 -0
- package/dist/instructionsUtils.js.map +1 -0
- package/dist/lockfile.d.ts +16 -0
- package/dist/lockfile.js +172 -0
- package/dist/lockfile.js.map +1 -0
- package/dist/logger.d.ts +16 -0
- package/dist/logger.js +68 -0
- package/dist/logger.js.map +1 -0
- package/dist/oauth.d.ts +105 -0
- package/dist/oauth.js +880 -0
- package/dist/oauth.js.map +1 -0
- package/dist/orchestrator/childBridgeClient.d.ts +33 -0
- package/dist/orchestrator/childBridgeClient.js +321 -0
- package/dist/orchestrator/childBridgeClient.js.map +1 -0
- package/dist/orchestrator/childBridgeRegistry.d.ts +67 -0
- package/dist/orchestrator/childBridgeRegistry.js +297 -0
- package/dist/orchestrator/childBridgeRegistry.js.map +1 -0
- package/dist/orchestrator/index.d.ts +3 -0
- package/dist/orchestrator/index.js +3 -0
- package/dist/orchestrator/index.js.map +1 -0
- package/dist/orchestrator/orchestratorBridge.d.ts +32 -0
- package/dist/orchestrator/orchestratorBridge.js +412 -0
- package/dist/orchestrator/orchestratorBridge.js.map +1 -0
- package/dist/orchestrator/orchestratorConfig.d.ts +11 -0
- package/dist/orchestrator/orchestratorConfig.js +85 -0
- package/dist/orchestrator/orchestratorConfig.js.map +1 -0
- package/dist/orchestrator/orchestratorTools.d.ts +16 -0
- package/dist/orchestrator/orchestratorTools.js +272 -0
- package/dist/orchestrator/orchestratorTools.js.map +1 -0
- package/dist/patchworkCli.d.ts +15 -0
- package/dist/patchworkCli.js +41 -0
- package/dist/patchworkCli.js.map +1 -0
- package/dist/patchworkConfig.d.ts +28 -0
- package/dist/patchworkConfig.js +30 -0
- package/dist/patchworkConfig.js.map +1 -0
- package/dist/plugin.d.ts +106 -0
- package/dist/plugin.js +31 -0
- package/dist/plugin.js.map +1 -0
- package/dist/pluginLoader.d.ts +44 -0
- package/dist/pluginLoader.js +357 -0
- package/dist/pluginLoader.js.map +1 -0
- package/dist/pluginWatcher.d.ts +24 -0
- package/dist/pluginWatcher.js +139 -0
- package/dist/pluginWatcher.js.map +1 -0
- package/dist/preToolUseHook.d.ts +10 -0
- package/dist/preToolUseHook.js +57 -0
- package/dist/preToolUseHook.js.map +1 -0
- package/dist/probe.d.ts +35 -0
- package/dist/probe.js +143 -0
- package/dist/probe.js.map +1 -0
- package/dist/prompts.d.ts +27 -0
- package/dist/prompts.js +1680 -0
- package/dist/prompts.js.map +1 -0
- package/dist/quickTaskPresets.d.ts +64 -0
- package/dist/quickTaskPresets.js +156 -0
- package/dist/quickTaskPresets.js.map +1 -0
- package/dist/recipes/compiler.d.ts +44 -0
- package/dist/recipes/compiler.js +140 -0
- package/dist/recipes/compiler.js.map +1 -0
- package/dist/recipes/installer.d.ts +25 -0
- package/dist/recipes/installer.js +62 -0
- package/dist/recipes/installer.js.map +1 -0
- package/dist/recipes/parser.d.ts +18 -0
- package/dist/recipes/parser.js +160 -0
- package/dist/recipes/parser.js.map +1 -0
- package/dist/recipes/scheduler.d.ts +45 -0
- package/dist/recipes/scheduler.js +110 -0
- package/dist/recipes/scheduler.js.map +1 -0
- package/dist/recipes/schema.d.ts +71 -0
- package/dist/recipes/schema.js +11 -0
- package/dist/recipes/schema.js.map +1 -0
- package/dist/recipesHttp.d.ts +63 -0
- package/dist/recipesHttp.js +183 -0
- package/dist/recipesHttp.js.map +1 -0
- package/dist/resources.d.ts +33 -0
- package/dist/resources.js +266 -0
- package/dist/resources.js.map +1 -0
- package/dist/riskTier.d.ts +40 -0
- package/dist/riskTier.js +142 -0
- package/dist/riskTier.js.map +1 -0
- package/dist/runLog.d.ts +90 -0
- package/dist/runLog.js +143 -0
- package/dist/runLog.js.map +1 -0
- package/dist/server.d.ts +160 -0
- package/dist/server.js +1244 -0
- package/dist/server.js.map +1 -0
- package/dist/sessionCheckpoint.d.ts +37 -0
- package/dist/sessionCheckpoint.js +123 -0
- package/dist/sessionCheckpoint.js.map +1 -0
- package/dist/streamableHttp.d.ts +86 -0
- package/dist/streamableHttp.js +702 -0
- package/dist/streamableHttp.js.map +1 -0
- package/dist/telemetry.d.ts +18 -0
- package/dist/telemetry.js +95 -0
- package/dist/telemetry.js.map +1 -0
- package/dist/tools/activityLog.d.ts +140 -0
- package/dist/tools/activityLog.js +204 -0
- package/dist/tools/activityLog.js.map +1 -0
- package/dist/tools/auditDependencies.d.ts +67 -0
- package/dist/tools/auditDependencies.js +298 -0
- package/dist/tools/auditDependencies.js.map +1 -0
- package/dist/tools/batchLsp.d.ts +262 -0
- package/dist/tools/batchLsp.js +328 -0
- package/dist/tools/batchLsp.js.map +1 -0
- package/dist/tools/blame-utils.d.ts +30 -0
- package/dist/tools/blame-utils.js +60 -0
- package/dist/tools/blame-utils.js.map +1 -0
- package/dist/tools/bridgeDoctor.d.ts +78 -0
- package/dist/tools/bridgeDoctor.js +542 -0
- package/dist/tools/bridgeDoctor.js.map +1 -0
- package/dist/tools/bridgeStatus.d.ts +122 -0
- package/dist/tools/bridgeStatus.js +250 -0
- package/dist/tools/bridgeStatus.js.map +1 -0
- package/dist/tools/cancelClaudeTask.d.ts +48 -0
- package/dist/tools/cancelClaudeTask.js +56 -0
- package/dist/tools/cancelClaudeTask.js.map +1 -0
- package/dist/tools/checkDocumentDirty.d.ts +56 -0
- package/dist/tools/checkDocumentDirty.js +74 -0
- package/dist/tools/checkDocumentDirty.js.map +1 -0
- package/dist/tools/clipboard.d.ts +80 -0
- package/dist/tools/clipboard.js +211 -0
- package/dist/tools/clipboard.js.map +1 -0
- package/dist/tools/closeTabs.d.ts +84 -0
- package/dist/tools/closeTabs.js +97 -0
- package/dist/tools/closeTabs.js.map +1 -0
- package/dist/tools/codeLens.d.ts +50 -0
- package/dist/tools/codeLens.js +47 -0
- package/dist/tools/codeLens.js.map +1 -0
- package/dist/tools/contextBundle.d.ts +75 -0
- package/dist/tools/contextBundle.js +218 -0
- package/dist/tools/contextBundle.js.map +1 -0
- package/dist/tools/createIssueFromAIComment.d.ts +75 -0
- package/dist/tools/createIssueFromAIComment.js +119 -0
- package/dist/tools/createIssueFromAIComment.js.map +1 -0
- package/dist/tools/ctxGetTaskContext.d.ts +103 -0
- package/dist/tools/ctxGetTaskContext.js +274 -0
- package/dist/tools/ctxGetTaskContext.js.map +1 -0
- package/dist/tools/ctxQueryTraces.d.ts +142 -0
- package/dist/tools/ctxQueryTraces.js +194 -0
- package/dist/tools/ctxQueryTraces.js.map +1 -0
- package/dist/tools/ctxSaveTrace.d.ts +87 -0
- package/dist/tools/ctxSaveTrace.js +94 -0
- package/dist/tools/ctxSaveTrace.js.map +1 -0
- package/dist/tools/debug.d.ts +206 -0
- package/dist/tools/debug.js +234 -0
- package/dist/tools/debug.js.map +1 -0
- package/dist/tools/decorations.d.ts +130 -0
- package/dist/tools/decorations.js +160 -0
- package/dist/tools/decorations.js.map +1 -0
- package/dist/tools/detectUnusedCode.d.ts +78 -0
- package/dist/tools/detectUnusedCode.js +173 -0
- package/dist/tools/detectUnusedCode.js.map +1 -0
- package/dist/tools/documentLinks.d.ts +62 -0
- package/dist/tools/documentLinks.js +55 -0
- package/dist/tools/documentLinks.js.map +1 -0
- package/dist/tools/editText.d.ts +108 -0
- package/dist/tools/editText.js +318 -0
- package/dist/tools/editText.js.map +1 -0
- package/dist/tools/enrichCommit.d.ts +89 -0
- package/dist/tools/enrichCommit.js +201 -0
- package/dist/tools/enrichCommit.js.map +1 -0
- package/dist/tools/enrichStackTrace.d.ts +121 -0
- package/dist/tools/enrichStackTrace.js +194 -0
- package/dist/tools/enrichStackTrace.js.map +1 -0
- package/dist/tools/explainDiagnostic.d.ts +137 -0
- package/dist/tools/explainDiagnostic.js +230 -0
- package/dist/tools/explainDiagnostic.js.map +1 -0
- package/dist/tools/explainSymbol.d.ts +119 -0
- package/dist/tools/explainSymbol.js +177 -0
- package/dist/tools/explainSymbol.js.map +1 -0
- package/dist/tools/fileOperations.d.ts +186 -0
- package/dist/tools/fileOperations.js +330 -0
- package/dist/tools/fileOperations.js.map +1 -0
- package/dist/tools/fileWatcher.d.ts +107 -0
- package/dist/tools/fileWatcher.js +121 -0
- package/dist/tools/fileWatcher.js.map +1 -0
- package/dist/tools/findFiles.d.ts +65 -0
- package/dist/tools/findFiles.js +142 -0
- package/dist/tools/findFiles.js.map +1 -0
- package/dist/tools/findRelatedTests.d.ts +83 -0
- package/dist/tools/findRelatedTests.js +196 -0
- package/dist/tools/findRelatedTests.js.map +1 -0
- package/dist/tools/fixAllLintErrors.d.ts +66 -0
- package/dist/tools/fixAllLintErrors.js +128 -0
- package/dist/tools/fixAllLintErrors.js.map +1 -0
- package/dist/tools/foldingRanges.d.ts +50 -0
- package/dist/tools/foldingRanges.js +51 -0
- package/dist/tools/foldingRanges.js.map +1 -0
- package/dist/tools/formatAndSave.d.ts +57 -0
- package/dist/tools/formatAndSave.js +87 -0
- package/dist/tools/formatAndSave.js.map +1 -0
- package/dist/tools/formatDocument.d.ts +61 -0
- package/dist/tools/formatDocument.js +144 -0
- package/dist/tools/formatDocument.js.map +1 -0
- package/dist/tools/generateAPIDocumentation.d.ts +62 -0
- package/dist/tools/generateAPIDocumentation.js +249 -0
- package/dist/tools/generateAPIDocumentation.js.map +1 -0
- package/dist/tools/generateTests.d.ts +75 -0
- package/dist/tools/generateTests.js +226 -0
- package/dist/tools/generateTests.js.map +1 -0
- package/dist/tools/getAIComments.d.ts +79 -0
- package/dist/tools/getAIComments.js +93 -0
- package/dist/tools/getAIComments.js.map +1 -0
- package/dist/tools/getAnalyticsReport.d.ts +102 -0
- package/dist/tools/getAnalyticsReport.js +137 -0
- package/dist/tools/getAnalyticsReport.js.map +1 -0
- package/dist/tools/getArchitectureContext.d.ts +85 -0
- package/dist/tools/getArchitectureContext.js +135 -0
- package/dist/tools/getArchitectureContext.js.map +1 -0
- package/dist/tools/getBufferContent.d.ts +80 -0
- package/dist/tools/getBufferContent.js +207 -0
- package/dist/tools/getBufferContent.js.map +1 -0
- package/dist/tools/getChangeImpact.d.ts +76 -0
- package/dist/tools/getChangeImpact.js +184 -0
- package/dist/tools/getChangeImpact.js.map +1 -0
- package/dist/tools/getClaudeTaskStatus.d.ts +87 -0
- package/dist/tools/getClaudeTaskStatus.js +89 -0
- package/dist/tools/getClaudeTaskStatus.js.map +1 -0
- package/dist/tools/getCodeCoverage.d.ts +86 -0
- package/dist/tools/getCodeCoverage.js +237 -0
- package/dist/tools/getCodeCoverage.js.map +1 -0
- package/dist/tools/getCommitsForIssue.d.ts +98 -0
- package/dist/tools/getCommitsForIssue.js +106 -0
- package/dist/tools/getCommitsForIssue.js.map +1 -0
- package/dist/tools/getCurrentSelection.d.ts +123 -0
- package/dist/tools/getCurrentSelection.js +113 -0
- package/dist/tools/getCurrentSelection.js.map +1 -0
- package/dist/tools/getDebugState.d.ts +140 -0
- package/dist/tools/getDebugState.js +109 -0
- package/dist/tools/getDebugState.js.map +1 -0
- package/dist/tools/getDependencyTree.d.ts +59 -0
- package/dist/tools/getDependencyTree.js +207 -0
- package/dist/tools/getDependencyTree.js.map +1 -0
- package/dist/tools/getDiagnostics.d.ts +108 -0
- package/dist/tools/getDiagnostics.js +371 -0
- package/dist/tools/getDiagnostics.js.map +1 -0
- package/dist/tools/getDiffFromHandoff.d.ts +89 -0
- package/dist/tools/getDiffFromHandoff.js +163 -0
- package/dist/tools/getDiffFromHandoff.js.map +1 -0
- package/dist/tools/getDocumentSymbols.d.ts +74 -0
- package/dist/tools/getDocumentSymbols.js +177 -0
- package/dist/tools/getDocumentSymbols.js.map +1 -0
- package/dist/tools/getFileTree.d.ts +66 -0
- package/dist/tools/getFileTree.js +131 -0
- package/dist/tools/getFileTree.js.map +1 -0
- package/dist/tools/getGitDiff.d.ts +50 -0
- package/dist/tools/getGitDiff.js +73 -0
- package/dist/tools/getGitDiff.js.map +1 -0
- package/dist/tools/getGitHotspots.d.ts +88 -0
- package/dist/tools/getGitHotspots.js +145 -0
- package/dist/tools/getGitHotspots.js.map +1 -0
- package/dist/tools/getGitLog.d.ts +62 -0
- package/dist/tools/getGitLog.js +87 -0
- package/dist/tools/getGitLog.js.map +1 -0
- package/dist/tools/getGitStatus.d.ts +72 -0
- package/dist/tools/getGitStatus.js +126 -0
- package/dist/tools/getGitStatus.js.map +1 -0
- package/dist/tools/getImportTree.d.ts +73 -0
- package/dist/tools/getImportTree.js +223 -0
- package/dist/tools/getImportTree.js.map +1 -0
- package/dist/tools/getImportedSignatures.d.ts +62 -0
- package/dist/tools/getImportedSignatures.js +255 -0
- package/dist/tools/getImportedSignatures.js.map +1 -0
- package/dist/tools/getOpenEditors.d.ts +62 -0
- package/dist/tools/getOpenEditors.js +126 -0
- package/dist/tools/getOpenEditors.js.map +1 -0
- package/dist/tools/getPRTemplate.d.ts +68 -0
- package/dist/tools/getPRTemplate.js +187 -0
- package/dist/tools/getPRTemplate.js.map +1 -0
- package/dist/tools/getProjectContext.d.ts +114 -0
- package/dist/tools/getProjectContext.js +344 -0
- package/dist/tools/getProjectContext.js.map +1 -0
- package/dist/tools/getProjectInfo.d.ts +51 -0
- package/dist/tools/getProjectInfo.js +325 -0
- package/dist/tools/getProjectInfo.js.map +1 -0
- package/dist/tools/getSecurityAdvisories.d.ts +105 -0
- package/dist/tools/getSecurityAdvisories.js +472 -0
- package/dist/tools/getSecurityAdvisories.js.map +1 -0
- package/dist/tools/getSessionUsage.d.ts +58 -0
- package/dist/tools/getSessionUsage.js +57 -0
- package/dist/tools/getSessionUsage.js.map +1 -0
- package/dist/tools/getSymbolHistory.d.ts +157 -0
- package/dist/tools/getSymbolHistory.js +256 -0
- package/dist/tools/getSymbolHistory.js.map +1 -0
- package/dist/tools/getToolCapabilities.d.ts +69 -0
- package/dist/tools/getToolCapabilities.js +298 -0
- package/dist/tools/getToolCapabilities.js.map +1 -0
- package/dist/tools/getTypeSignature.d.ts +70 -0
- package/dist/tools/getTypeSignature.js +132 -0
- package/dist/tools/getTypeSignature.js.map +1 -0
- package/dist/tools/getWorkspaceFolders.d.ts +58 -0
- package/dist/tools/getWorkspaceFolders.js +69 -0
- package/dist/tools/getWorkspaceFolders.js.map +1 -0
- package/dist/tools/getWorkspaceSettings.d.ts +44 -0
- package/dist/tools/getWorkspaceSettings.js +70 -0
- package/dist/tools/getWorkspaceSettings.js.map +1 -0
- package/dist/tools/git-utils.d.ts +16 -0
- package/dist/tools/git-utils.js +46 -0
- package/dist/tools/git-utils.js.map +1 -0
- package/dist/tools/gitHistory.d.ts +110 -0
- package/dist/tools/gitHistory.js +167 -0
- package/dist/tools/gitHistory.js.map +1 -0
- package/dist/tools/gitWrite.d.ts +612 -0
- package/dist/tools/gitWrite.js +983 -0
- package/dist/tools/gitWrite.js.map +1 -0
- package/dist/tools/github/actions.d.ts +152 -0
- package/dist/tools/github/actions.js +195 -0
- package/dist/tools/github/actions.js.map +1 -0
- package/dist/tools/github/index.d.ts +3 -0
- package/dist/tools/github/index.js +4 -0
- package/dist/tools/github/index.js.map +1 -0
- package/dist/tools/github/issues.d.ts +281 -0
- package/dist/tools/github/issues.js +340 -0
- package/dist/tools/github/issues.js.map +1 -0
- package/dist/tools/github/pr.d.ts +433 -0
- package/dist/tools/github/pr.js +588 -0
- package/dist/tools/github/pr.js.map +1 -0
- package/dist/tools/github/shared.d.ts +4 -0
- package/dist/tools/github/shared.js +12 -0
- package/dist/tools/github/shared.js.map +1 -0
- package/dist/tools/handoffNote.d.ts +106 -0
- package/dist/tools/handoffNote.js +232 -0
- package/dist/tools/handoffNote.js.map +1 -0
- package/dist/tools/headless/lspClient.d.ts +26 -0
- package/dist/tools/headless/lspClient.js +221 -0
- package/dist/tools/headless/lspClient.js.map +1 -0
- package/dist/tools/headless/lspFallback.d.ts +28 -0
- package/dist/tools/headless/lspFallback.js +122 -0
- package/dist/tools/headless/lspFallback.js.map +1 -0
- package/dist/tools/hoverAtCursor.d.ts +54 -0
- package/dist/tools/hoverAtCursor.js +68 -0
- package/dist/tools/hoverAtCursor.js.map +1 -0
- package/dist/tools/httpClient.d.ts +141 -0
- package/dist/tools/httpClient.js +486 -0
- package/dist/tools/httpClient.js.map +1 -0
- package/dist/tools/index.d.ts +49 -0
- package/dist/tools/index.js +672 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/inlayHints.d.ts +81 -0
- package/dist/tools/inlayHints.js +76 -0
- package/dist/tools/inlayHints.js.map +1 -0
- package/dist/tools/issueRefs.d.ts +14 -0
- package/dist/tools/issueRefs.js +27 -0
- package/dist/tools/issueRefs.js.map +1 -0
- package/dist/tools/jumpToFirstError.d.ts +63 -0
- package/dist/tools/jumpToFirstError.js +124 -0
- package/dist/tools/jumpToFirstError.js.map +1 -0
- package/dist/tools/launchQuickTask.d.ts +76 -0
- package/dist/tools/launchQuickTask.js +170 -0
- package/dist/tools/launchQuickTask.js.map +1 -0
- package/dist/tools/linters/biome.d.ts +2 -0
- package/dist/tools/linters/biome.js +44 -0
- package/dist/tools/linters/biome.js.map +1 -0
- package/dist/tools/linters/cargo.d.ts +2 -0
- package/dist/tools/linters/cargo.js +45 -0
- package/dist/tools/linters/cargo.js.map +1 -0
- package/dist/tools/linters/eslint.d.ts +2 -0
- package/dist/tools/linters/eslint.js +59 -0
- package/dist/tools/linters/eslint.js.map +1 -0
- package/dist/tools/linters/govet.d.ts +2 -0
- package/dist/tools/linters/govet.js +37 -0
- package/dist/tools/linters/govet.js.map +1 -0
- package/dist/tools/linters/pyright.d.ts +2 -0
- package/dist/tools/linters/pyright.js +34 -0
- package/dist/tools/linters/pyright.js.map +1 -0
- package/dist/tools/linters/ruff.d.ts +2 -0
- package/dist/tools/linters/ruff.js +30 -0
- package/dist/tools/linters/ruff.js.map +1 -0
- package/dist/tools/linters/types.d.ts +16 -0
- package/dist/tools/linters/types.js +2 -0
- package/dist/tools/linters/types.js.map +1 -0
- package/dist/tools/linters/typescript.d.ts +2 -0
- package/dist/tools/linters/typescript.js +38 -0
- package/dist/tools/linters/typescript.js.map +1 -0
- package/dist/tools/listClaudeTasks.d.ts +84 -0
- package/dist/tools/listClaudeTasks.js +88 -0
- package/dist/tools/listClaudeTasks.js.map +1 -0
- package/dist/tools/listTerminals.d.ts +55 -0
- package/dist/tools/listTerminals.js +78 -0
- package/dist/tools/listTerminals.js.map +1 -0
- package/dist/tools/lsp.d.ts +1086 -0
- package/dist/tools/lsp.js +1339 -0
- package/dist/tools/lsp.js.map +1 -0
- package/dist/tools/navigateToSymbolByName.d.ts +56 -0
- package/dist/tools/navigateToSymbolByName.js +170 -0
- package/dist/tools/navigateToSymbolByName.js.map +1 -0
- package/dist/tools/openDiff.d.ts +66 -0
- package/dist/tools/openDiff.js +126 -0
- package/dist/tools/openDiff.js.map +1 -0
- package/dist/tools/openFile.d.ts +69 -0
- package/dist/tools/openFile.js +129 -0
- package/dist/tools/openFile.js.map +1 -0
- package/dist/tools/openInBrowser.d.ts +55 -0
- package/dist/tools/openInBrowser.js +129 -0
- package/dist/tools/openInBrowser.js.map +1 -0
- package/dist/tools/organizeImports.d.ts +56 -0
- package/dist/tools/organizeImports.js +115 -0
- package/dist/tools/organizeImports.js.map +1 -0
- package/dist/tools/performanceReport.d.ts +133 -0
- package/dist/tools/performanceReport.js +218 -0
- package/dist/tools/performanceReport.js.map +1 -0
- package/dist/tools/planPersistence.d.ts +306 -0
- package/dist/tools/planPersistence.js +485 -0
- package/dist/tools/planPersistence.js.map +1 -0
- package/dist/tools/previewEdit.d.ts +107 -0
- package/dist/tools/previewEdit.js +270 -0
- package/dist/tools/previewEdit.js.map +1 -0
- package/dist/tools/recentTracesDigest.d.ts +35 -0
- package/dist/tools/recentTracesDigest.js +98 -0
- package/dist/tools/recentTracesDigest.js.map +1 -0
- package/dist/tools/refactorAnalyze.d.ts +78 -0
- package/dist/tools/refactorAnalyze.js +141 -0
- package/dist/tools/refactorAnalyze.js.map +1 -0
- package/dist/tools/refactorExtractFunction.d.ts +52 -0
- package/dist/tools/refactorExtractFunction.js +121 -0
- package/dist/tools/refactorExtractFunction.js.map +1 -0
- package/dist/tools/refactorPreview.d.ts +75 -0
- package/dist/tools/refactorPreview.js +93 -0
- package/dist/tools/refactorPreview.js.map +1 -0
- package/dist/tools/replaceBlock.d.ts +62 -0
- package/dist/tools/replaceBlock.js +125 -0
- package/dist/tools/replaceBlock.js.map +1 -0
- package/dist/tools/resumeClaudeTask.d.ts +75 -0
- package/dist/tools/resumeClaudeTask.js +149 -0
- package/dist/tools/resumeClaudeTask.js.map +1 -0
- package/dist/tools/runClaudeTask.d.ts +97 -0
- package/dist/tools/runClaudeTask.js +224 -0
- package/dist/tools/runClaudeTask.js.map +1 -0
- package/dist/tools/runCommand.d.ts +82 -0
- package/dist/tools/runCommand.js +101 -0
- package/dist/tools/runCommand.js.map +1 -0
- package/dist/tools/runTests.d.ts +146 -0
- package/dist/tools/runTests.js +315 -0
- package/dist/tools/runTests.js.map +1 -0
- package/dist/tools/saveDocument.d.ts +50 -0
- package/dist/tools/saveDocument.js +73 -0
- package/dist/tools/saveDocument.js.map +1 -0
- package/dist/tools/screenshot.d.ts +23 -0
- package/dist/tools/screenshot.js +43 -0
- package/dist/tools/screenshot.js.map +1 -0
- package/dist/tools/screenshotAndAnnotate.d.ts +103 -0
- package/dist/tools/screenshotAndAnnotate.js +192 -0
- package/dist/tools/screenshotAndAnnotate.js.map +1 -0
- package/dist/tools/searchAndReplace.d.ts +108 -0
- package/dist/tools/searchAndReplace.js +281 -0
- package/dist/tools/searchAndReplace.js.map +1 -0
- package/dist/tools/searchTools.d.ts +61 -0
- package/dist/tools/searchTools.js +85 -0
- package/dist/tools/searchTools.js.map +1 -0
- package/dist/tools/searchWorkspace.d.ts +99 -0
- package/dist/tools/searchWorkspace.js +189 -0
- package/dist/tools/searchWorkspace.js.map +1 -0
- package/dist/tools/selectionRanges.d.ts +58 -0
- package/dist/tools/selectionRanges.js +61 -0
- package/dist/tools/selectionRanges.js.map +1 -0
- package/dist/tools/semanticTokens.d.ts +87 -0
- package/dist/tools/semanticTokens.js +86 -0
- package/dist/tools/semanticTokens.js.map +1 -0
- package/dist/tools/setActiveWorkspaceFolder.d.ts +41 -0
- package/dist/tools/setActiveWorkspaceFolder.js +38 -0
- package/dist/tools/setActiveWorkspaceFolder.js.map +1 -0
- package/dist/tools/signatureHelp.d.ts +86 -0
- package/dist/tools/signatureHelp.js +79 -0
- package/dist/tools/signatureHelp.js.map +1 -0
- package/dist/tools/spawnWorkspace.d.ts +103 -0
- package/dist/tools/spawnWorkspace.js +268 -0
- package/dist/tools/spawnWorkspace.js.map +1 -0
- package/dist/tools/stackTraceParser.d.ts +43 -0
- package/dist/tools/stackTraceParser.js +139 -0
- package/dist/tools/stackTraceParser.js.map +1 -0
- package/dist/tools/terminal.d.ts +352 -0
- package/dist/tools/terminal.js +670 -0
- package/dist/tools/terminal.js.map +1 -0
- package/dist/tools/testRunners/cargoTest.d.ts +2 -0
- package/dist/tools/testRunners/cargoTest.js +129 -0
- package/dist/tools/testRunners/cargoTest.js.map +1 -0
- package/dist/tools/testRunners/goTest.d.ts +2 -0
- package/dist/tools/testRunners/goTest.js +108 -0
- package/dist/tools/testRunners/goTest.js.map +1 -0
- package/dist/tools/testRunners/pytest.d.ts +2 -0
- package/dist/tools/testRunners/pytest.js +135 -0
- package/dist/tools/testRunners/pytest.js.map +1 -0
- package/dist/tools/testRunners/types.d.ts +18 -0
- package/dist/tools/testRunners/types.js +2 -0
- package/dist/tools/testRunners/types.js.map +1 -0
- package/dist/tools/testRunners/vitestJest.d.ts +3 -0
- package/dist/tools/testRunners/vitestJest.js +215 -0
- package/dist/tools/testRunners/vitestJest.js.map +1 -0
- package/dist/tools/testTraceToSource.d.ts +80 -0
- package/dist/tools/testTraceToSource.js +206 -0
- package/dist/tools/testTraceToSource.js.map +1 -0
- package/dist/tools/transaction.d.ts +243 -0
- package/dist/tools/transaction.js +309 -0
- package/dist/tools/transaction.js.map +1 -0
- package/dist/tools/typeHierarchy.d.ts +77 -0
- package/dist/tools/typeHierarchy.js +86 -0
- package/dist/tools/typeHierarchy.js.map +1 -0
- package/dist/tools/utils.d.ts +124 -0
- package/dist/tools/utils.js +566 -0
- package/dist/tools/utils.js.map +1 -0
- package/dist/tools/vscodeCommands.d.ts +90 -0
- package/dist/tools/vscodeCommands.js +112 -0
- package/dist/tools/vscodeCommands.js.map +1 -0
- package/dist/tools/vscodeTasks.d.ts +102 -0
- package/dist/tools/vscodeTasks.js +110 -0
- package/dist/tools/vscodeTasks.js.map +1 -0
- package/dist/tools/watchDiagnostics.d.ts +64 -0
- package/dist/tools/watchDiagnostics.js +270 -0
- package/dist/tools/watchDiagnostics.js.map +1 -0
- package/dist/tools/workspaceSettings.d.ts +57 -0
- package/dist/tools/workspaceSettings.js +80 -0
- package/dist/tools/workspaceSettings.js.map +1 -0
- package/dist/transport.d.ts +207 -0
- package/dist/transport.js +1272 -0
- package/dist/transport.js.map +1 -0
- package/dist/version.d.ts +13 -0
- package/dist/version.js +31 -0
- package/dist/version.js.map +1 -0
- package/dist/wsUtils.d.ts +8 -0
- package/dist/wsUtils.js +54 -0
- package/dist/wsUtils.js.map +1 -0
- package/package.json +118 -0
- package/scripts/gen-claude-desktop-config.sh +124 -0
- package/scripts/gen-mcp-config.sh +390 -0
- package/scripts/install-extension.sh +106 -0
- package/scripts/mcp-stdio-shim.cjs +482 -0
- package/scripts/postinstall.mjs +68 -0
- package/scripts/start-all.sh +502 -0
- package/scripts/start-orchestrator.sh +186 -0
- package/scripts/start-remote.sh +126 -0
- package/scripts/start-vps.sh +116 -0
- package/templates/CLAUDE.bridge.md +125 -0
- package/templates/automation-policies/security-first.json +46 -0
- package/templates/automation-policies/strict-lint.json +41 -0
- package/templates/automation-policies/test-driven.json +54 -0
- package/templates/automation-policy.example.json +105 -0
- package/templates/bridge-tools.md +111 -0
- package/templates/dispatch-context.md +33 -0
- package/templates/managed-agent/code-review-agent.md +50 -0
- package/templates/managed-agent/managed-agent-mcp.json +102 -0
- package/templates/recipes/ambient-journal.yaml +11 -0
- package/templates/recipes/daily-status.yaml +21 -0
- package/templates/recipes/lint-on-save.yaml +13 -0
- package/templates/recipes/stale-branches.yaml +18 -0
- package/templates/recipes/watch-failing-tests.yaml +15 -0
- package/templates/scheduled-tasks/dependency-audit/SKILL.md +77 -0
- package/templates/scheduled-tasks/health-check/SKILL.md +73 -0
- package/templates/scheduled-tasks/nightly-review/SKILL.md +69 -0
|
@@ -0,0 +1,1038 @@
|
|
|
1
|
+
import crypto from "node:crypto";
|
|
2
|
+
import fs from "node:fs";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { executeAutomationPolicy } from "./fp/automationInterpreter.js";
|
|
5
|
+
import { EMPTY_AUTOMATION_STATE, setLatestDiagnostics, setTestRunnerStatus, tasksInLastHour, } from "./fp/automationState.js";
|
|
6
|
+
import { VsCodeBackend } from "./fp/interpreterContext.js";
|
|
7
|
+
import { parsePolicy } from "./fp/policyParser.js";
|
|
8
|
+
/** Maximum length (chars) of an automation policy prompt template (matches runClaudeTask cap) */
|
|
9
|
+
const MAX_POLICY_PROMPT_CHARS = 32_768;
|
|
10
|
+
/** Default system prompt for automation subprocesses when none is set in policy. */
|
|
11
|
+
const DEFAULT_AUTOMATION_SYSTEM_PROMPT = "You are a concise automation assistant. " +
|
|
12
|
+
"Respond in \u22645 lines. No preamble. No markdown headers. " +
|
|
13
|
+
"Call the tools listed in the task prompt, then report results only.";
|
|
14
|
+
/**
|
|
15
|
+
* Deterministic signature over a diagnostic list — used for content-aware
|
|
16
|
+
* dedupe in onDiagnosticsError. Sorting by a stable key makes the signature
|
|
17
|
+
* order-independent so LSP re-emissions with the same diagnostics in a
|
|
18
|
+
* different order still collide.
|
|
19
|
+
*/
|
|
20
|
+
export function diagnosticSignature(diagnostics) {
|
|
21
|
+
const keyOf = (d) => `${d.severity}|${d.code ?? ""}|${(d.source ?? "").toLowerCase()}|${d.message.slice(0, 200)}`;
|
|
22
|
+
const sigText = [...diagnostics].map(keyOf).sort().join("\n");
|
|
23
|
+
return crypto.createHash("sha256").update(sigText).digest("hex").slice(0, 12);
|
|
24
|
+
}
|
|
25
|
+
const MIN_COOLDOWN_MS = 5_000;
|
|
26
|
+
/** Maximum length of a promptName value in a policy. */
|
|
27
|
+
const MAX_PROMPT_NAME_CHARS = 64;
|
|
28
|
+
// ── Policy loading ────────────────────────────────────────────────────────────
|
|
29
|
+
/**
|
|
30
|
+
* Validates that a hook config has exactly one of `prompt` (non-empty inline string)
|
|
31
|
+
* or `promptName` (reference to a built-in MCP prompt), plus optional `promptArgs`.
|
|
32
|
+
* Throws a descriptive error on violation.
|
|
33
|
+
*/
|
|
34
|
+
function validatePromptSource(hookName, cfg) {
|
|
35
|
+
const hasPrompt = typeof cfg.prompt === "string" && cfg.prompt.trim() !== "";
|
|
36
|
+
const hasPromptName = typeof cfg.promptName === "string" && cfg.promptName.trim() !== "";
|
|
37
|
+
if (hasPrompt && hasPromptName) {
|
|
38
|
+
throw new Error(`"${hookName}" must specify either "prompt" or "promptName", not both`);
|
|
39
|
+
}
|
|
40
|
+
if (!hasPrompt && !hasPromptName) {
|
|
41
|
+
throw new Error(`"${hookName}" must have a non-empty "prompt" or "promptName"`);
|
|
42
|
+
}
|
|
43
|
+
if (hasPrompt && (cfg.prompt?.length ?? 0) > MAX_POLICY_PROMPT_CHARS) {
|
|
44
|
+
throw new Error(`"${hookName}.prompt" must be ≤ ${MAX_POLICY_PROMPT_CHARS} characters`);
|
|
45
|
+
}
|
|
46
|
+
if (hasPromptName) {
|
|
47
|
+
if ((cfg.promptName?.length ?? 0) > MAX_PROMPT_NAME_CHARS) {
|
|
48
|
+
throw new Error(`"${hookName}.promptName" must be ≤ ${MAX_PROMPT_NAME_CHARS} characters`);
|
|
49
|
+
}
|
|
50
|
+
if (cfg.promptArgs !== undefined) {
|
|
51
|
+
if (typeof cfg.promptArgs !== "object" ||
|
|
52
|
+
cfg.promptArgs === null ||
|
|
53
|
+
Array.isArray(cfg.promptArgs)) {
|
|
54
|
+
throw new Error(`"${hookName}.promptArgs" must be a plain object`);
|
|
55
|
+
}
|
|
56
|
+
for (const [k, v] of Object.entries(cfg.promptArgs)) {
|
|
57
|
+
if (typeof v !== "string") {
|
|
58
|
+
throw new Error(`"${hookName}.promptArgs.${k}" must be a string`);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
if (cfg.condition !== undefined) {
|
|
64
|
+
if (typeof cfg.condition !== "string" || cfg.condition.length > 1024) {
|
|
65
|
+
throw new Error(`"${hookName}.condition" must be a string ≤ 1024 characters`);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
if (cfg.model !== undefined) {
|
|
69
|
+
if (typeof cfg.model !== "string" || cfg.model.trim() === "") {
|
|
70
|
+
throw new Error(`"${hookName}.model" must be a non-empty string`);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
if (cfg.effort !== undefined) {
|
|
74
|
+
if (!["low", "medium", "high", "max"].includes(cfg.effort)) {
|
|
75
|
+
throw new Error(`"${hookName}.effort" must be one of "low", "medium", "high", "max"`);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Expand a discriminated unified hook (e.g. onCompaction.phase="pre"|"post")
|
|
81
|
+
* into the corresponding internal legacy slot, then clear the unified key.
|
|
82
|
+
* Throws if the discriminator value is invalid or if both the unified hook
|
|
83
|
+
* and the legacy slot are set simultaneously.
|
|
84
|
+
* legacyA/legacyB may be null when there is no legacy field to guard against.
|
|
85
|
+
*/
|
|
86
|
+
function expandDiscriminatedHook(policy, unifiedKey, discriminatorKey, valueA, slotA, legacyA, valueB, slotB, legacyB, filePath) {
|
|
87
|
+
const p = policy;
|
|
88
|
+
const hook = p[unifiedKey];
|
|
89
|
+
if (hook === undefined)
|
|
90
|
+
return;
|
|
91
|
+
if (typeof hook !== "object" || hook === null) {
|
|
92
|
+
throw new Error(`"${unifiedKey}" must be an object`);
|
|
93
|
+
}
|
|
94
|
+
const discriminator = hook[discriminatorKey];
|
|
95
|
+
if (discriminator !== valueA && discriminator !== valueB) {
|
|
96
|
+
throw new Error(`"${unifiedKey}.${discriminatorKey}" must be "${valueA}" or "${valueB}" (got ${JSON.stringify(discriminator)})`);
|
|
97
|
+
}
|
|
98
|
+
// Only guard the slot that matches the actual discriminator value.
|
|
99
|
+
if (discriminator === valueA &&
|
|
100
|
+
legacyA !== null &&
|
|
101
|
+
p[legacyA] !== undefined) {
|
|
102
|
+
throw new Error(`Cannot set both "${unifiedKey}" (${discriminatorKey}: "${valueA}") and "${legacyA}" — use ${unifiedKey} only.`);
|
|
103
|
+
}
|
|
104
|
+
if (discriminator === valueB &&
|
|
105
|
+
legacyB !== null &&
|
|
106
|
+
p[legacyB] !== undefined) {
|
|
107
|
+
throw new Error(`Cannot set both "${unifiedKey}" (${discriminatorKey}: "${valueB}") and "${legacyB}" — use ${unifiedKey} only.`);
|
|
108
|
+
}
|
|
109
|
+
const { [discriminatorKey]: _disc, ...rest } = hook;
|
|
110
|
+
const targetKey = discriminator === valueA ? slotA : slotB;
|
|
111
|
+
p[targetKey] = rest;
|
|
112
|
+
p[unifiedKey] = undefined;
|
|
113
|
+
// Deprecation warnings for pre-existing legacy fields (set before expansion).
|
|
114
|
+
// These are emitted after the expansion so callers can check the hadLegacy*
|
|
115
|
+
// flags they captured before calling this helper.
|
|
116
|
+
void filePath; // filePath passed through to callers for their warn messages
|
|
117
|
+
}
|
|
118
|
+
/** Load and validate a JSON automation policy file. Throws on any failure. */
|
|
119
|
+
export function loadPolicy(filePath) {
|
|
120
|
+
let raw;
|
|
121
|
+
try {
|
|
122
|
+
raw = fs.readFileSync(filePath, "utf-8");
|
|
123
|
+
}
|
|
124
|
+
catch (err) {
|
|
125
|
+
throw new Error(`Failed to read automation policy file "${filePath}": ${err instanceof Error ? err.message : String(err)}`);
|
|
126
|
+
}
|
|
127
|
+
let parsed;
|
|
128
|
+
try {
|
|
129
|
+
parsed = JSON.parse(raw);
|
|
130
|
+
}
|
|
131
|
+
catch (err) {
|
|
132
|
+
throw new Error(`Failed to parse automation policy file "${filePath}": ${err instanceof Error ? err.message : String(err)}`);
|
|
133
|
+
}
|
|
134
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
|
|
135
|
+
throw new Error(`Automation policy must be a JSON object in "${filePath}"`);
|
|
136
|
+
}
|
|
137
|
+
const policy = parsed;
|
|
138
|
+
// ── onCompaction normalization (v2.43.0+) ───────────────────────────────
|
|
139
|
+
// Expand unified `onCompaction` into internal onPreCompact/onPostCompact.
|
|
140
|
+
// hadLegacy* captured BEFORE expansion so warn only for user-set values.
|
|
141
|
+
const hadLegacyPreCompact = policy.onPreCompact !== undefined;
|
|
142
|
+
const hadLegacyPostCompact = policy.onPostCompact !== undefined;
|
|
143
|
+
expandDiscriminatedHook(policy, "onCompaction", "phase", "pre", "onPreCompact", "onPreCompact", "post", "onPostCompact", "onPostCompact", filePath);
|
|
144
|
+
if (hadLegacyPreCompact) {
|
|
145
|
+
console.warn(`[automation-policy] "onPreCompact" in "${filePath}" is deprecated — migrate to "onCompaction" with phase: "pre". Legacy name removed no earlier than v2.46 + 30 days after v2.43.0 release.`);
|
|
146
|
+
}
|
|
147
|
+
if (hadLegacyPostCompact) {
|
|
148
|
+
console.warn(`[automation-policy] "onPostCompact" in "${filePath}" is deprecated — migrate to "onCompaction" with phase: "post". Legacy name removed no earlier than v2.46 + 30 days after v2.43.0 release.`);
|
|
149
|
+
}
|
|
150
|
+
// ── onDiagnosticsStateChange normalization (v2.43.0+) ───────────────────
|
|
151
|
+
// Expand into internal onDiagnosticsError/onDiagnosticsCleared based on state.
|
|
152
|
+
const hadLegacyDiagError = policy.onDiagnosticsError !== undefined;
|
|
153
|
+
const hadLegacyDiagCleared = policy.onDiagnosticsCleared !== undefined;
|
|
154
|
+
expandDiscriminatedHook(policy, "onDiagnosticsStateChange", "state", "error", "onDiagnosticsError", "onDiagnosticsError", "cleared", "onDiagnosticsCleared", "onDiagnosticsCleared", filePath);
|
|
155
|
+
if (hadLegacyDiagError) {
|
|
156
|
+
console.warn(`[automation-policy] "onDiagnosticsError" in "${filePath}" is deprecated — migrate to "onDiagnosticsStateChange" with state: "error". Legacy name removed no earlier than v2.46 + 30 days after v2.43.0 release.`);
|
|
157
|
+
}
|
|
158
|
+
if (hadLegacyDiagCleared) {
|
|
159
|
+
console.warn(`[automation-policy] "onDiagnosticsCleared" in "${filePath}" is deprecated — migrate to "onDiagnosticsStateChange" with state: "cleared". Legacy name removed no earlier than v2.46 + 30 days after v2.43.0 release.`);
|
|
160
|
+
}
|
|
161
|
+
// ── onDebugSession normalization (v2.43.0+) ─────────────────────────────
|
|
162
|
+
// Expand into internal onDebugSessionStart/onDebugSessionEnd based on phase.
|
|
163
|
+
const hadLegacyDebugStart = policy.onDebugSessionStart !== undefined;
|
|
164
|
+
const hadLegacyDebugEnd = policy.onDebugSessionEnd !== undefined;
|
|
165
|
+
expandDiscriminatedHook(policy, "onDebugSession", "phase", "start", "onDebugSessionStart", "onDebugSessionStart", "end", "onDebugSessionEnd", "onDebugSessionEnd", filePath);
|
|
166
|
+
if (hadLegacyDebugStart) {
|
|
167
|
+
console.warn(`[automation-policy] "onDebugSessionStart" in "${filePath}" is deprecated — migrate to "onDebugSession" with phase: "start". Legacy name removed no earlier than v2.46 + 30 days after v2.43.0 release.`);
|
|
168
|
+
}
|
|
169
|
+
if (hadLegacyDebugEnd) {
|
|
170
|
+
console.warn(`[automation-policy] "onDebugSessionEnd" in "${filePath}" is deprecated — migrate to "onDebugSession" with phase: "end". Legacy name removed no earlier than v2.46 + 30 days after v2.43.0 release.`);
|
|
171
|
+
}
|
|
172
|
+
// ── onTestRun(filter) normalization (v2.43.0+) ──────────────────────────
|
|
173
|
+
// The new canonical form is `onTestRun.filter: "any"|"failure"|"pass-after-fail"`.
|
|
174
|
+
// - "any" / "failure" rewrite to onFailureOnly:false/true on the same hook.
|
|
175
|
+
// - "pass-after-fail" is routed into the onTestPassAfterFailure slot (a
|
|
176
|
+
// separate internal hook with its own dispatch path).
|
|
177
|
+
// Legacy `onFailureOnly` field + legacy `onTestPassAfterFailure` hook still
|
|
178
|
+
// work but warn at load time.
|
|
179
|
+
const hadLegacyOnFailureOnly = policy.onTestRun !== undefined &&
|
|
180
|
+
policy.onTestRun.onFailureOnly !==
|
|
181
|
+
undefined;
|
|
182
|
+
const hadLegacyTestPassAfterFailure = policy.onTestPassAfterFailure !== undefined;
|
|
183
|
+
if (policy.onTestRun !== undefined &&
|
|
184
|
+
typeof policy.onTestRun === "object" &&
|
|
185
|
+
policy.onTestRun !== null) {
|
|
186
|
+
const tr = policy.onTestRun;
|
|
187
|
+
if (tr.filter !== undefined) {
|
|
188
|
+
if (tr.filter !== "any" &&
|
|
189
|
+
tr.filter !== "failure" &&
|
|
190
|
+
tr.filter !== "pass-after-fail") {
|
|
191
|
+
throw new Error(`"onTestRun.filter" must be one of "any", "failure", "pass-after-fail" (got ${JSON.stringify(tr.filter)})`);
|
|
192
|
+
}
|
|
193
|
+
if (tr.onFailureOnly !== undefined) {
|
|
194
|
+
throw new Error(`Cannot set both "onTestRun.filter" and "onTestRun.onFailureOnly" — use "filter" only.`);
|
|
195
|
+
}
|
|
196
|
+
if (tr.filter === "pass-after-fail") {
|
|
197
|
+
if (hadLegacyTestPassAfterFailure) {
|
|
198
|
+
throw new Error(`Cannot set both "onTestRun" (filter: "pass-after-fail") and legacy "onTestPassAfterFailure" — use onTestRun.filter only.`);
|
|
199
|
+
}
|
|
200
|
+
const { filter: _f, onFailureOnly: _ofo, ...rest } = tr;
|
|
201
|
+
policy.onTestPassAfterFailure =
|
|
202
|
+
rest;
|
|
203
|
+
policy.onTestRun = undefined;
|
|
204
|
+
}
|
|
205
|
+
else {
|
|
206
|
+
tr.onFailureOnly = tr.filter === "failure";
|
|
207
|
+
tr.filter = undefined;
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
if (hadLegacyOnFailureOnly) {
|
|
212
|
+
console.warn(`[automation-policy] "onTestRun.onFailureOnly" in "${filePath}" is deprecated — migrate to "onTestRun.filter" ("failure" or "any"). Legacy field removed no earlier than v2.46 + 30 days after v2.43.0 release.`);
|
|
213
|
+
}
|
|
214
|
+
if (hadLegacyTestPassAfterFailure) {
|
|
215
|
+
console.warn(`[automation-policy] "onTestPassAfterFailure" in "${filePath}" is deprecated — migrate to "onTestRun" with filter: "pass-after-fail". Legacy hook removed no earlier than v2.46 + 30 days after v2.43.0 release.`);
|
|
216
|
+
}
|
|
217
|
+
// Helper: throw with actual value in message for easier debugging
|
|
218
|
+
function expectType(value, type, field) {
|
|
219
|
+
if (typeof value !== type) {
|
|
220
|
+
throw new Error(`"${field}" must be ${type} (got ${typeof value}: ${JSON.stringify(value)})`);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
// Validate top-level fields
|
|
224
|
+
if (policy.defaultModel !== undefined &&
|
|
225
|
+
typeof policy.defaultModel !== "string") {
|
|
226
|
+
throw new Error(`"defaultModel" must be a string`);
|
|
227
|
+
}
|
|
228
|
+
if (policy.maxTasksPerHour !== undefined) {
|
|
229
|
+
if (typeof policy.maxTasksPerHour !== "number" ||
|
|
230
|
+
!Number.isInteger(policy.maxTasksPerHour) ||
|
|
231
|
+
policy.maxTasksPerHour < 0) {
|
|
232
|
+
throw new Error(`"maxTasksPerHour" must be a non-negative integer`);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
if (policy.automationSystemPrompt !== undefined) {
|
|
236
|
+
if (typeof policy.automationSystemPrompt !== "string") {
|
|
237
|
+
throw new Error(`"automationSystemPrompt" must be a string`);
|
|
238
|
+
}
|
|
239
|
+
if (policy.automationSystemPrompt.length > 4096) {
|
|
240
|
+
throw new Error(`"automationSystemPrompt" must be ≤ 4096 characters`);
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
if (policy.defaultEffort !== undefined) {
|
|
244
|
+
if (!["low", "medium", "high", "max"].includes(policy.defaultEffort)) {
|
|
245
|
+
throw new Error(`"defaultEffort" must be one of "low", "medium", "high", "max"`);
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
const HOOK_SUBJECT_KEY = {
|
|
249
|
+
onFileSave: "file",
|
|
250
|
+
onFileChanged: "file",
|
|
251
|
+
onGitCommit: "message",
|
|
252
|
+
onGitPush: "branch",
|
|
253
|
+
onGitPull: "branch",
|
|
254
|
+
onBranchCheckout: "branch",
|
|
255
|
+
onPullRequest: "title",
|
|
256
|
+
onTestPassAfterFailure: "runner",
|
|
257
|
+
onTaskCreated: "prompt",
|
|
258
|
+
onTaskSuccess: "output",
|
|
259
|
+
onPermissionDenied: "tool",
|
|
260
|
+
onDiagnosticsCleared: "file",
|
|
261
|
+
onCwdChanged: "cwd",
|
|
262
|
+
onPreCompact: "session",
|
|
263
|
+
onPostCompact: "session",
|
|
264
|
+
onTestRun: "runner",
|
|
265
|
+
onDebugSessionStart: "sessionName",
|
|
266
|
+
onDebugSessionEnd: "sessionName",
|
|
267
|
+
};
|
|
268
|
+
void HOOK_SUBJECT_KEY; // referenced by callers; kept for documentation
|
|
269
|
+
// Standard hooks: required cooldownMs, no extra fields
|
|
270
|
+
const STANDARD_HOOK_KEYS = [
|
|
271
|
+
"onTestPassAfterFailure",
|
|
272
|
+
"onGitCommit",
|
|
273
|
+
"onGitPush",
|
|
274
|
+
"onGitPull",
|
|
275
|
+
"onBranchCheckout",
|
|
276
|
+
"onPullRequest",
|
|
277
|
+
"onTaskCreated",
|
|
278
|
+
"onPermissionDenied",
|
|
279
|
+
"onDiagnosticsCleared",
|
|
280
|
+
"onTaskSuccess",
|
|
281
|
+
"onDebugSessionStart",
|
|
282
|
+
"onDebugSessionEnd",
|
|
283
|
+
"onCwdChanged",
|
|
284
|
+
"onPreCompact",
|
|
285
|
+
"onPostCompact",
|
|
286
|
+
];
|
|
287
|
+
for (const key of STANDARD_HOOK_KEYS) {
|
|
288
|
+
const cfg = policy[key];
|
|
289
|
+
if (cfg === undefined)
|
|
290
|
+
continue;
|
|
291
|
+
if (typeof cfg !== "object" || cfg === null) {
|
|
292
|
+
throw new Error(`"${key}" must be an object`);
|
|
293
|
+
}
|
|
294
|
+
const rec = cfg;
|
|
295
|
+
if (typeof rec.enabled !== "boolean") {
|
|
296
|
+
throw new Error(`"${key}.enabled" must be a boolean`);
|
|
297
|
+
}
|
|
298
|
+
validatePromptSource(key, rec);
|
|
299
|
+
expectType(rec.cooldownMs, "number", `${key}.cooldownMs`);
|
|
300
|
+
if (!Number.isFinite(rec.cooldownMs)) {
|
|
301
|
+
throw new Error(`"${key}.cooldownMs" must be a finite number`);
|
|
302
|
+
}
|
|
303
|
+
rec.cooldownMs = Math.max(rec.cooldownMs, MIN_COOLDOWN_MS);
|
|
304
|
+
}
|
|
305
|
+
// ── Per-hook extras (after generic fold) ─────────────────────────────────
|
|
306
|
+
// Validate onDiagnosticsError (extra: minSeverity required, diagnosticTypes,
|
|
307
|
+
// dedupeByContent, dedupeContentCooldownMs)
|
|
308
|
+
if (policy.onDiagnosticsError !== undefined) {
|
|
309
|
+
const d = policy.onDiagnosticsError;
|
|
310
|
+
if (typeof d !== "object" || d === null) {
|
|
311
|
+
throw new Error(`"onDiagnosticsError" must be an object`);
|
|
312
|
+
}
|
|
313
|
+
if (typeof d.enabled !== "boolean") {
|
|
314
|
+
throw new Error(`"onDiagnosticsError.enabled" must be a boolean`);
|
|
315
|
+
}
|
|
316
|
+
if (d.minSeverity !== "error" && d.minSeverity !== "warning") {
|
|
317
|
+
throw new Error(`"onDiagnosticsError.minSeverity" must be "error" or "warning"`);
|
|
318
|
+
}
|
|
319
|
+
validatePromptSource("onDiagnosticsError", d);
|
|
320
|
+
expectType(d.cooldownMs, "number", "onDiagnosticsError.cooldownMs");
|
|
321
|
+
if (!Number.isFinite(d.cooldownMs)) {
|
|
322
|
+
throw new Error(`"onDiagnosticsError.cooldownMs" must be a finite number`);
|
|
323
|
+
}
|
|
324
|
+
d.cooldownMs = Math.max(d.cooldownMs, MIN_COOLDOWN_MS);
|
|
325
|
+
if (d.diagnosticTypes !== undefined) {
|
|
326
|
+
if (!Array.isArray(d.diagnosticTypes) ||
|
|
327
|
+
d.diagnosticTypes.length === 0 ||
|
|
328
|
+
!d.diagnosticTypes.every((t) => typeof t === "string")) {
|
|
329
|
+
throw new Error(`"onDiagnosticsError.diagnosticTypes" must be a non-empty array of strings`);
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
if (d.dedupeByContent !== undefined &&
|
|
333
|
+
typeof d.dedupeByContent !== "boolean") {
|
|
334
|
+
throw new Error(`"onDiagnosticsError.dedupeByContent" must be a boolean`);
|
|
335
|
+
}
|
|
336
|
+
if (d.dedupeContentCooldownMs !== undefined) {
|
|
337
|
+
if (typeof d.dedupeContentCooldownMs !== "number" ||
|
|
338
|
+
!Number.isFinite(d.dedupeContentCooldownMs)) {
|
|
339
|
+
throw new Error(`"onDiagnosticsError.dedupeContentCooldownMs" must be a number`);
|
|
340
|
+
}
|
|
341
|
+
d.dedupeContentCooldownMs = Math.max(d.dedupeContentCooldownMs, MIN_COOLDOWN_MS);
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
// Validate onFileSave (extra: patterns required)
|
|
345
|
+
if (policy.onFileSave !== undefined) {
|
|
346
|
+
const s = policy.onFileSave;
|
|
347
|
+
if (typeof s !== "object" || s === null) {
|
|
348
|
+
throw new Error(`"onFileSave" must be an object`);
|
|
349
|
+
}
|
|
350
|
+
if (typeof s.enabled !== "boolean") {
|
|
351
|
+
throw new Error(`"onFileSave.enabled" must be a boolean`);
|
|
352
|
+
}
|
|
353
|
+
if (!Array.isArray(s.patterns) ||
|
|
354
|
+
s.patterns.length > 100 ||
|
|
355
|
+
s.patterns.some((p) => typeof p !== "string" || p.length > 1024)) {
|
|
356
|
+
throw new Error("onFileSave.patterns must be an array of ≤100 strings, each ≤1024 chars");
|
|
357
|
+
}
|
|
358
|
+
validatePromptSource("onFileSave", s);
|
|
359
|
+
expectType(s.cooldownMs, "number", "onFileSave.cooldownMs");
|
|
360
|
+
if (!Number.isFinite(s.cooldownMs)) {
|
|
361
|
+
throw new Error(`"onFileSave.cooldownMs" must be a finite number`);
|
|
362
|
+
}
|
|
363
|
+
s.cooldownMs = Math.max(s.cooldownMs, MIN_COOLDOWN_MS);
|
|
364
|
+
}
|
|
365
|
+
// Validate onFileChanged (extra: patterns required)
|
|
366
|
+
if (policy.onFileChanged !== undefined) {
|
|
367
|
+
const fc = policy.onFileChanged;
|
|
368
|
+
if (typeof fc !== "object" || fc === null) {
|
|
369
|
+
throw new Error(`"onFileChanged" must be an object`);
|
|
370
|
+
}
|
|
371
|
+
if (typeof fc.enabled !== "boolean") {
|
|
372
|
+
throw new Error(`"onFileChanged.enabled" must be a boolean`);
|
|
373
|
+
}
|
|
374
|
+
if (!Array.isArray(fc.patterns) ||
|
|
375
|
+
fc.patterns.length > 100 ||
|
|
376
|
+
fc.patterns.some((p) => typeof p !== "string" || p.length > 1024)) {
|
|
377
|
+
throw new Error("onFileChanged.patterns must be an array of ≤100 strings, each ≤1024 chars");
|
|
378
|
+
}
|
|
379
|
+
validatePromptSource("onFileChanged", fc);
|
|
380
|
+
expectType(fc.cooldownMs, "number", "onFileChanged.cooldownMs");
|
|
381
|
+
if (!Number.isFinite(fc.cooldownMs)) {
|
|
382
|
+
throw new Error(`"onFileChanged.cooldownMs" must be a finite number`);
|
|
383
|
+
}
|
|
384
|
+
fc.cooldownMs = Math.max(fc.cooldownMs, MIN_COOLDOWN_MS);
|
|
385
|
+
}
|
|
386
|
+
// Validate onInstructionsLoaded (special: cooldownMs optional, min 5000)
|
|
387
|
+
if (policy.onInstructionsLoaded !== undefined) {
|
|
388
|
+
const il = policy.onInstructionsLoaded;
|
|
389
|
+
if (typeof il !== "object" || il === null) {
|
|
390
|
+
throw new Error(`"onInstructionsLoaded" must be an object`);
|
|
391
|
+
}
|
|
392
|
+
if (typeof il.enabled !== "boolean") {
|
|
393
|
+
throw new Error(`"onInstructionsLoaded.enabled" must be a boolean`);
|
|
394
|
+
}
|
|
395
|
+
if (il.cooldownMs !== undefined) {
|
|
396
|
+
if (typeof il.cooldownMs !== "number" || il.cooldownMs < 5000) {
|
|
397
|
+
throw new Error(`"onInstructionsLoaded.cooldownMs" must be a number >= 5000`);
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
validatePromptSource("onInstructionsLoaded", il);
|
|
401
|
+
}
|
|
402
|
+
// Validate onTestRun (extra: onFailureOnly required, minDuration optional)
|
|
403
|
+
if (policy.onTestRun !== undefined) {
|
|
404
|
+
const tr = policy.onTestRun;
|
|
405
|
+
if (typeof tr !== "object" || tr === null) {
|
|
406
|
+
throw new Error(`"onTestRun" must be an object`);
|
|
407
|
+
}
|
|
408
|
+
if (typeof tr.enabled !== "boolean") {
|
|
409
|
+
throw new Error(`"onTestRun.enabled" must be a boolean`);
|
|
410
|
+
}
|
|
411
|
+
// After C3 expansion, onFailureOnly is always populated (either by the
|
|
412
|
+
// filter rewrite or by user-provided legacy value). Accept missing as
|
|
413
|
+
// "failure" default for safety.
|
|
414
|
+
if (tr.onFailureOnly === undefined) {
|
|
415
|
+
tr.onFailureOnly = true;
|
|
416
|
+
}
|
|
417
|
+
else if (typeof tr.onFailureOnly !== "boolean") {
|
|
418
|
+
throw new Error(`"onTestRun.onFailureOnly" must be a boolean`);
|
|
419
|
+
}
|
|
420
|
+
validatePromptSource("onTestRun", tr);
|
|
421
|
+
expectType(tr.cooldownMs, "number", "onTestRun.cooldownMs");
|
|
422
|
+
if (!Number.isFinite(tr.cooldownMs)) {
|
|
423
|
+
throw new Error(`"onTestRun.cooldownMs" must be a finite number`);
|
|
424
|
+
}
|
|
425
|
+
tr.cooldownMs = Math.max(tr.cooldownMs, MIN_COOLDOWN_MS);
|
|
426
|
+
if (tr.minDuration !== undefined) {
|
|
427
|
+
if (typeof tr.minDuration !== "number" ||
|
|
428
|
+
!Number.isFinite(tr.minDuration) ||
|
|
429
|
+
tr.minDuration < 0) {
|
|
430
|
+
throw new Error(`"onTestRun.minDuration" must be a non-negative number`);
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
return policy;
|
|
435
|
+
}
|
|
436
|
+
/**
|
|
437
|
+
* CC hook events that require a settings.json entry calling a bridge notify tool.
|
|
438
|
+
* Bridge-tool-triggered hooks (onFileSave, onGitCommit, etc.) need no CC wiring.
|
|
439
|
+
*/
|
|
440
|
+
const CC_HOOK_TOOL_MAP = {
|
|
441
|
+
PreCompact: "notifyPreCompact",
|
|
442
|
+
PostCompact: "notifyPostCompact",
|
|
443
|
+
InstructionsLoaded: "notifyInstructionsLoaded",
|
|
444
|
+
TaskCreated: "notifyTaskCreated",
|
|
445
|
+
PermissionDenied: "notifyPermissionDenied",
|
|
446
|
+
CwdChanged: "notifyCwdChanged",
|
|
447
|
+
};
|
|
448
|
+
/** Policy hook names that correspond to CC hook events (need settings.json wiring). */
|
|
449
|
+
const _POLICY_TO_CC_EVENT = {
|
|
450
|
+
onPreCompact: "PreCompact",
|
|
451
|
+
onPostCompact: "PostCompact",
|
|
452
|
+
onInstructionsLoaded: "InstructionsLoaded",
|
|
453
|
+
onTaskCreated: "TaskCreated",
|
|
454
|
+
onPermissionDenied: "PermissionDenied",
|
|
455
|
+
onCwdChanged: "CwdChanged",
|
|
456
|
+
};
|
|
457
|
+
/**
|
|
458
|
+
* Check which CC hook events have at least one settings.json entry whose
|
|
459
|
+
* command references the matching bridge notify tool. Returns a map of
|
|
460
|
+
* CC event name → wired (boolean).
|
|
461
|
+
*/
|
|
462
|
+
export function checkCcHookWiring() {
|
|
463
|
+
const result = {};
|
|
464
|
+
for (const ccEvent of Object.keys(CC_HOOK_TOOL_MAP)) {
|
|
465
|
+
result[ccEvent] = false;
|
|
466
|
+
}
|
|
467
|
+
try {
|
|
468
|
+
const configDir = process.env.CLAUDE_CONFIG_DIR ??
|
|
469
|
+
path.join(process.env.HOME ?? process.env.USERPROFILE ?? "~", ".claude");
|
|
470
|
+
const settingsPath = path.join(configDir, "settings.json");
|
|
471
|
+
const raw = fs.readFileSync(settingsPath, "utf-8");
|
|
472
|
+
const settings = JSON.parse(raw);
|
|
473
|
+
const hooks = settings.hooks ?? {};
|
|
474
|
+
const commandMatches = (cmd, ccEvent, toolName) => typeof cmd === "string" &&
|
|
475
|
+
(cmd.includes(toolName) || cmd.includes(`notify ${ccEvent}`));
|
|
476
|
+
for (const [ccEvent, toolName] of Object.entries(CC_HOOK_TOOL_MAP)) {
|
|
477
|
+
const entries = hooks[ccEvent] ?? [];
|
|
478
|
+
result[ccEvent] = entries.some((e) => {
|
|
479
|
+
// New format: { matcher, hooks: [{ type, command }] }
|
|
480
|
+
if (e && Array.isArray(e.hooks)) {
|
|
481
|
+
return e.hooks?.some((h) => commandMatches(h.command, ccEvent, toolName));
|
|
482
|
+
}
|
|
483
|
+
// Legacy flat format: { type, command }
|
|
484
|
+
return commandMatches(e.command, ccEvent, toolName);
|
|
485
|
+
});
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
catch {
|
|
489
|
+
// Settings file missing or unparseable — treat all as unwired
|
|
490
|
+
}
|
|
491
|
+
return result;
|
|
492
|
+
}
|
|
493
|
+
// ── AutomationHooks ───────────────────────────────────────────────────────────
|
|
494
|
+
export class AutomationHooks {
|
|
495
|
+
policy;
|
|
496
|
+
log;
|
|
497
|
+
/** Compiled AST for the functional interpreter. Null if parse failed. */
|
|
498
|
+
_programAST = null;
|
|
499
|
+
/** Backend instance for the functional interpreter. */
|
|
500
|
+
_interpreterBackend = null;
|
|
501
|
+
/**
|
|
502
|
+
* Pure-value state holding cooldown timestamps, diagnostic error counts, and
|
|
503
|
+
* test outcomes. Mutations go through the pure-function helpers from
|
|
504
|
+
* `src/fp/automationState.ts`; the class re-assigns `_automationState` on
|
|
505
|
+
* each "write" to maintain immutability semantics at the value level.
|
|
506
|
+
*/
|
|
507
|
+
_automationState = EMPTY_AUTOMATION_STATE;
|
|
508
|
+
/** Accumulated skip counts keyed by hook name → reason. */
|
|
509
|
+
_skipCountsByHookAndReason = new Map();
|
|
510
|
+
/** Tracks previous error count per normalized file path for zero-transition detection. */
|
|
511
|
+
prevDiagnosticErrors = new Map();
|
|
512
|
+
/**
|
|
513
|
+
* Per-runner last outcome — used to detect fail→pass transitions for onTestPassAfterFailure.
|
|
514
|
+
* Key: runner name. Value: "pass" | "fail".
|
|
515
|
+
*/
|
|
516
|
+
lastTestOutcomeByRunner = new Map();
|
|
517
|
+
_lastFiredAt = null;
|
|
518
|
+
/** Last interpreter run promise — allows tests to await completion. */
|
|
519
|
+
_lastRunPromise = Promise.resolve();
|
|
520
|
+
constructor(policy, orchestrator, log, _extensionClient, _workspace) {
|
|
521
|
+
this.policy = policy;
|
|
522
|
+
this.log = log;
|
|
523
|
+
// Phase 4: always initialise interpreter (primary path)
|
|
524
|
+
{
|
|
525
|
+
const parseResult = parsePolicy(policy);
|
|
526
|
+
if (parseResult.ok) {
|
|
527
|
+
this._programAST = parseResult.value;
|
|
528
|
+
this._interpreterBackend = new VsCodeBackend(orchestrator, {
|
|
529
|
+
info: this.log.bind(this),
|
|
530
|
+
});
|
|
531
|
+
}
|
|
532
|
+
else {
|
|
533
|
+
this.log(`[automation] interpreter parse failed: ${parseResult.message}`);
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
async _runInterpreter(eventType, eventData) {
|
|
538
|
+
if (!this._programAST || !this._interpreterBackend)
|
|
539
|
+
return;
|
|
540
|
+
const ctx = {
|
|
541
|
+
state: this._automationState,
|
|
542
|
+
now: Date.now(),
|
|
543
|
+
eventType,
|
|
544
|
+
eventData,
|
|
545
|
+
backend: this._interpreterBackend,
|
|
546
|
+
log: this.log.bind(this),
|
|
547
|
+
};
|
|
548
|
+
const result = await executeAutomationPolicy(this._programAST, ctx);
|
|
549
|
+
if (result.ok) {
|
|
550
|
+
this._automationState = result.value.updatedState;
|
|
551
|
+
if (result.value.taskIds.length > 0) {
|
|
552
|
+
this.log(`[interpreter] ${eventType}: enqueued ${result.value.taskIds.length} task(s)`);
|
|
553
|
+
}
|
|
554
|
+
for (const s of result.value.skipped) {
|
|
555
|
+
this.log(`[interpreter] ${eventType}: skipped ${s.hook} — ${s.reason}`);
|
|
556
|
+
if (!this._skipCountsByHookAndReason.has(s.hook)) {
|
|
557
|
+
this._skipCountsByHookAndReason.set(s.hook, new Map());
|
|
558
|
+
}
|
|
559
|
+
const byReason = this._skipCountsByHookAndReason.get(s.hook);
|
|
560
|
+
if (byReason) {
|
|
561
|
+
byReason.set(s.reason, (byReason.get(s.reason) ?? 0) + 1);
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
for (const e of result.value.errors) {
|
|
565
|
+
this.log(`[interpreter] ${eventType}: error in ${e.hook} — ${e.message}`);
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
else {
|
|
569
|
+
this.log(`[interpreter] ${eventType}: interpreter error — ${result.message}`);
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
/**
|
|
573
|
+
* Returns a Promise that resolves once all in-flight interpreter runs finish.
|
|
574
|
+
* Useful in tests to await async side-effects before asserting on task counts.
|
|
575
|
+
*/
|
|
576
|
+
async flush() {
|
|
577
|
+
await this._lastRunPromise;
|
|
578
|
+
}
|
|
579
|
+
/** Tear down the instance: nulls interpreter references. */
|
|
580
|
+
destroy() {
|
|
581
|
+
this._programAST = null;
|
|
582
|
+
this._interpreterBackend = null;
|
|
583
|
+
}
|
|
584
|
+
handleDiagnosticsChanged(file, diagnostics) {
|
|
585
|
+
const normalizedFile = path.resolve(file);
|
|
586
|
+
// Track error count for zero-transition detection (onDiagnosticsCleared)
|
|
587
|
+
const severityRankForClear = {
|
|
588
|
+
error: 2,
|
|
589
|
+
warning: 1,
|
|
590
|
+
info: 0,
|
|
591
|
+
information: 0,
|
|
592
|
+
hint: 0,
|
|
593
|
+
};
|
|
594
|
+
const currentErrorCount = diagnostics.filter((d) => (severityRankForClear[d.severity] ?? 0) >= 1).length;
|
|
595
|
+
const prevErrorCount = this.prevDiagnosticErrors.get(normalizedFile) ?? 0;
|
|
596
|
+
this.prevDiagnosticErrors.set(normalizedFile, currentErrorCount);
|
|
597
|
+
// FIFO cap to bound memory
|
|
598
|
+
if (this.prevDiagnosticErrors.size > 5_000) {
|
|
599
|
+
const oldest = this.prevDiagnosticErrors.keys().next().value;
|
|
600
|
+
if (oldest !== undefined)
|
|
601
|
+
this.prevDiagnosticErrors.delete(oldest);
|
|
602
|
+
}
|
|
603
|
+
// Feed interpreter state using severity numbers where lower = more severe
|
|
604
|
+
// (error=0, warning=1, info/hint=2+) matching automationState.ts / evaluateWhen convention.
|
|
605
|
+
const severityToNum = {
|
|
606
|
+
error: 0,
|
|
607
|
+
warning: 1,
|
|
608
|
+
info: 2,
|
|
609
|
+
information: 2,
|
|
610
|
+
hint: 3,
|
|
611
|
+
};
|
|
612
|
+
const maxSeverityNum = diagnostics.reduce((min, d) => {
|
|
613
|
+
const rank = severityToNum[d.severity] ?? 3;
|
|
614
|
+
return rank < min ? rank : min;
|
|
615
|
+
}, 4); // 4 = no diagnostics / below hint
|
|
616
|
+
this._automationState = setLatestDiagnostics(this._automationState, normalizedFile, maxSeverityNum, diagnostics.length);
|
|
617
|
+
// Build diagnostics text for {{diagnostics}} placeholder (capped at 20)
|
|
618
|
+
const diagsForPrompt = diagnostics.slice(0, 20);
|
|
619
|
+
const overflow = diagnostics.length - diagsForPrompt.length;
|
|
620
|
+
const diagnosticsText = diagsForPrompt
|
|
621
|
+
.map((d) => `[${d.severity}] ${d.message}${d.source ? ` (${d.source})` : ""}`)
|
|
622
|
+
.join("\n") + (overflow > 0 ? `\n… and ${overflow} more` : "");
|
|
623
|
+
// Collect source/code strings for diagnosticTypes filtering
|
|
624
|
+
const diagnosticSources = diagnostics
|
|
625
|
+
.flatMap((d) => [
|
|
626
|
+
d.source?.toLowerCase() ?? "",
|
|
627
|
+
String(d.code ?? "").toLowerCase(),
|
|
628
|
+
])
|
|
629
|
+
.filter(Boolean)
|
|
630
|
+
.join(",");
|
|
631
|
+
const diagnosticSig = diagnosticSignature(diagnostics);
|
|
632
|
+
// Fire onDiagnosticsCleared if transitioning from non-zero → zero; chain
|
|
633
|
+
// the interpreter runs so flush() awaits both and state is updated correctly.
|
|
634
|
+
if (prevErrorCount > 0 && currentErrorCount === 0) {
|
|
635
|
+
this._lastRunPromise = this._runInterpreter("onDiagnosticsCleared", {
|
|
636
|
+
file: normalizedFile,
|
|
637
|
+
}).then(() => this._runInterpreter("onDiagnosticsError", {
|
|
638
|
+
file: normalizedFile,
|
|
639
|
+
diagnostics: diagnosticsText,
|
|
640
|
+
diagnosticSources,
|
|
641
|
+
diagnosticSig,
|
|
642
|
+
count: String(diagnostics.length),
|
|
643
|
+
}));
|
|
644
|
+
}
|
|
645
|
+
else {
|
|
646
|
+
this._lastRunPromise = this._runInterpreter("onDiagnosticsError", {
|
|
647
|
+
file: normalizedFile,
|
|
648
|
+
diagnostics: diagnosticsText,
|
|
649
|
+
diagnosticSources,
|
|
650
|
+
diagnosticSig,
|
|
651
|
+
count: String(diagnostics.length),
|
|
652
|
+
});
|
|
653
|
+
}
|
|
654
|
+
}
|
|
655
|
+
/**
|
|
656
|
+
* Called when Claude Code fires a CwdChanged hook (Claude Code 2.1.83+).
|
|
657
|
+
* Fires when CC's working directory changes — useful for re-snapshotting workspace context.
|
|
658
|
+
*/
|
|
659
|
+
handleCwdChanged(newCwd) {
|
|
660
|
+
this._lastRunPromise = this._runInterpreter("onCwdChanged", {
|
|
661
|
+
cwd: newCwd,
|
|
662
|
+
});
|
|
663
|
+
}
|
|
664
|
+
/**
|
|
665
|
+
* Called when Claude Code fires a PreCompact hook.
|
|
666
|
+
* Fires the onPreCompact automation hook before context trimming — use to snapshot state or
|
|
667
|
+
* write a handoff note before Claude loses context.
|
|
668
|
+
*/
|
|
669
|
+
handlePreCompact() {
|
|
670
|
+
this._lastRunPromise = this._runInterpreter("onPreCompact", {});
|
|
671
|
+
}
|
|
672
|
+
/**
|
|
673
|
+
* Called when Claude Code fires a PostCompact hook (Claude Code 2.1.76+).
|
|
674
|
+
* Re-enqueues the configured prompt so Claude can re-snapshot IDE state after losing context.
|
|
675
|
+
*/
|
|
676
|
+
handlePostCompact() {
|
|
677
|
+
this._lastRunPromise = this._runInterpreter("onPostCompact", {});
|
|
678
|
+
}
|
|
679
|
+
/**
|
|
680
|
+
* Called when Claude Code fires an InstructionsLoaded hook (Claude Code 2.1.76+).
|
|
681
|
+
* Fires once per session; injects bridge status / tool capability summary at start.
|
|
682
|
+
*/
|
|
683
|
+
handleInstructionsLoaded() {
|
|
684
|
+
this._lastRunPromise = this._runInterpreter("onInstructionsLoaded", {});
|
|
685
|
+
}
|
|
686
|
+
handleFileSaved(_id, type, file) {
|
|
687
|
+
if (type !== "save")
|
|
688
|
+
return;
|
|
689
|
+
const normalizedFile = path.resolve(file);
|
|
690
|
+
this._lastRunPromise = this._runInterpreter("onFileSave", {
|
|
691
|
+
file: normalizedFile,
|
|
692
|
+
});
|
|
693
|
+
}
|
|
694
|
+
/**
|
|
695
|
+
* Called when the VS Code extension reports a file-changed event (type === "change").
|
|
696
|
+
* Distinct from handleFileSaved — reacts to any editor buffer change, not just explicit saves.
|
|
697
|
+
* Useful for triggering tasks on unsaved edits (e.g. lint-as-you-type workflows).
|
|
698
|
+
*/
|
|
699
|
+
handleFileChanged(_id, type, file) {
|
|
700
|
+
if (type !== "change")
|
|
701
|
+
return;
|
|
702
|
+
const normalizedFile = path.resolve(file);
|
|
703
|
+
this._lastRunPromise = this._runInterpreter("onFileChanged", {
|
|
704
|
+
file: normalizedFile,
|
|
705
|
+
});
|
|
706
|
+
}
|
|
707
|
+
/**
|
|
708
|
+
* Called after every runTests tool invocation completes.
|
|
709
|
+
* Triggers an automation task when tests fail (or on every run if onFailureOnly is false).
|
|
710
|
+
*/
|
|
711
|
+
handleTestRun(result) {
|
|
712
|
+
const failureCount = result.summary.failed + result.summary.errored;
|
|
713
|
+
const current = failureCount === 0 ? "pass" : "fail";
|
|
714
|
+
// Update per-runner outcome state unconditionally so onTestPassAfterFailure
|
|
715
|
+
// can detect fail→pass transitions even when onTestRun is disabled/absent.
|
|
716
|
+
const passAfterFailRunners = [];
|
|
717
|
+
for (const runner of result.runners) {
|
|
718
|
+
const prev = this.lastTestOutcomeByRunner.get(runner);
|
|
719
|
+
this.lastTestOutcomeByRunner.set(runner, current);
|
|
720
|
+
// Feed interpreter state
|
|
721
|
+
this._automationState = setTestRunnerStatus(this._automationState, runner, current);
|
|
722
|
+
if (prev === "fail" && current === "pass") {
|
|
723
|
+
passAfterFailRunners.push(runner);
|
|
724
|
+
}
|
|
725
|
+
}
|
|
726
|
+
const testRunEventData = {
|
|
727
|
+
runner: result.runners.join(", ") || "",
|
|
728
|
+
failed: String(failureCount),
|
|
729
|
+
passed: String(result.summary.passed),
|
|
730
|
+
total: String(result.summary.total),
|
|
731
|
+
failures: JSON.stringify(result.failures.slice(0, 100)),
|
|
732
|
+
durationMs: String(result.summary.durationMs ?? ""),
|
|
733
|
+
};
|
|
734
|
+
// Chain interpreter runs so flush() awaits all of them and state is updated
|
|
735
|
+
// sequentially. If any runner had a fail→pass transition, run that first so
|
|
736
|
+
// its cooldown state is visible to subsequent runs within the same flush.
|
|
737
|
+
if (passAfterFailRunners.length > 0) {
|
|
738
|
+
let chain = Promise.resolve();
|
|
739
|
+
for (const runner of passAfterFailRunners) {
|
|
740
|
+
chain = chain.then(() => this._runInterpreter("onTestPassAfterFailure", { runner }));
|
|
741
|
+
}
|
|
742
|
+
this._lastRunPromise = chain.then(() => this._runInterpreter("onTestRun", testRunEventData));
|
|
743
|
+
}
|
|
744
|
+
else {
|
|
745
|
+
this._lastRunPromise = this._runInterpreter("onTestRun", testRunEventData);
|
|
746
|
+
}
|
|
747
|
+
}
|
|
748
|
+
/**
|
|
749
|
+
* Called after a successful gitCommit tool call.
|
|
750
|
+
* Fires the onGitCommit automation hook if configured.
|
|
751
|
+
*/
|
|
752
|
+
async handleGitCommit(result) {
|
|
753
|
+
this._lastRunPromise = this._runInterpreter("onGitCommit", {
|
|
754
|
+
hash: result.hash,
|
|
755
|
+
branch: result.branch,
|
|
756
|
+
message: result.message,
|
|
757
|
+
count: String(result.count),
|
|
758
|
+
files: result.files.join(", "),
|
|
759
|
+
});
|
|
760
|
+
}
|
|
761
|
+
/**
|
|
762
|
+
* Called after a successful gitPush tool call.
|
|
763
|
+
* Fires the onGitPush automation hook if configured.
|
|
764
|
+
*/
|
|
765
|
+
handleGitPush(result) {
|
|
766
|
+
this._lastRunPromise = this._runInterpreter("onGitPush", {
|
|
767
|
+
branch: result.branch,
|
|
768
|
+
remote: result.remote,
|
|
769
|
+
hash: result.hash,
|
|
770
|
+
});
|
|
771
|
+
}
|
|
772
|
+
/**
|
|
773
|
+
* Fires the onGitPull automation hook if configured.
|
|
774
|
+
*/
|
|
775
|
+
handleGitPull(result) {
|
|
776
|
+
this._lastRunPromise = this._runInterpreter("onGitPull", {
|
|
777
|
+
branch: result.branch,
|
|
778
|
+
remote: result.remote,
|
|
779
|
+
});
|
|
780
|
+
}
|
|
781
|
+
/**
|
|
782
|
+
* Called after a successful gitCheckout tool call.
|
|
783
|
+
* Fires the onBranchCheckout automation hook if configured.
|
|
784
|
+
*/
|
|
785
|
+
handleBranchCheckout(result) {
|
|
786
|
+
this._lastRunPromise = this._runInterpreter("onBranchCheckout", {
|
|
787
|
+
branch: result.branch,
|
|
788
|
+
previousBranch: result.previousBranch ?? "(detached HEAD)",
|
|
789
|
+
created: String(result.created),
|
|
790
|
+
});
|
|
791
|
+
}
|
|
792
|
+
/**
|
|
793
|
+
* Fires the onPullRequest automation hook if configured.
|
|
794
|
+
*/
|
|
795
|
+
handlePullRequest(result) {
|
|
796
|
+
this._lastRunPromise = this._runInterpreter("onPullRequest", {
|
|
797
|
+
title: result.title,
|
|
798
|
+
url: result.url,
|
|
799
|
+
branch: result.branch,
|
|
800
|
+
number: result.number != null ? String(result.number) : "",
|
|
801
|
+
});
|
|
802
|
+
}
|
|
803
|
+
handleTaskCreated(result) {
|
|
804
|
+
this._lastRunPromise = this._runInterpreter("onTaskCreated", {
|
|
805
|
+
taskId: result.taskId,
|
|
806
|
+
prompt: result.prompt,
|
|
807
|
+
});
|
|
808
|
+
}
|
|
809
|
+
handlePermissionDenied(result) {
|
|
810
|
+
this._lastRunPromise = this._runInterpreter("onPermissionDenied", {
|
|
811
|
+
tool: result.tool,
|
|
812
|
+
reason: result.reason,
|
|
813
|
+
});
|
|
814
|
+
}
|
|
815
|
+
/**
|
|
816
|
+
* Fires the onDiagnosticsCleared hook when a file transitions from non-zero to zero errors.
|
|
817
|
+
* Called internally by handleDiagnosticsChanged.
|
|
818
|
+
*/
|
|
819
|
+
handleDiagnosticsCleared(normalizedFile) {
|
|
820
|
+
this._lastRunPromise = this._runInterpreter("onDiagnosticsCleared", {
|
|
821
|
+
file: normalizedFile,
|
|
822
|
+
diagnosticSig: "",
|
|
823
|
+
});
|
|
824
|
+
}
|
|
825
|
+
/**
|
|
826
|
+
* Fires the onTaskSuccess hook when a Claude orchestrator task completes with status "done".
|
|
827
|
+
* Call from bridge.ts when a task transitions to done.
|
|
828
|
+
*/
|
|
829
|
+
handleTaskSuccess(result) {
|
|
830
|
+
this._lastRunPromise = this._runInterpreter("onTaskSuccess", {
|
|
831
|
+
taskId: result.taskId,
|
|
832
|
+
output: result.output,
|
|
833
|
+
});
|
|
834
|
+
}
|
|
835
|
+
/**
|
|
836
|
+
* Called when a VS Code debug session ends (hasActiveSession transitions true→false).
|
|
837
|
+
* Fires the onDebugSessionEnd automation hook if configured.
|
|
838
|
+
*/
|
|
839
|
+
async handleDebugSessionEnd(result) {
|
|
840
|
+
this._lastRunPromise = this._runInterpreter("onDebugSessionEnd", {
|
|
841
|
+
sessionName: result.sessionName,
|
|
842
|
+
sessionType: result.sessionType,
|
|
843
|
+
});
|
|
844
|
+
}
|
|
845
|
+
/**
|
|
846
|
+
* Called when a VS Code debug session starts (hasActiveSession transitions false→true).
|
|
847
|
+
* Fires the onDebugSessionStart automation hook if configured.
|
|
848
|
+
*/
|
|
849
|
+
async handleDebugSessionStart(result) {
|
|
850
|
+
this._lastRunPromise = this._runInterpreter("onDebugSessionStart", {
|
|
851
|
+
sessionName: result.sessionName,
|
|
852
|
+
sessionType: result.sessionType,
|
|
853
|
+
breakpointCount: String(result.breakpointCount),
|
|
854
|
+
activeFile: result.activeFile,
|
|
855
|
+
});
|
|
856
|
+
}
|
|
857
|
+
/** Summary of automation policy for getBridgeStatus. */
|
|
858
|
+
getStatus() {
|
|
859
|
+
const p = this.policy;
|
|
860
|
+
const wiring = checkCcHookWiring();
|
|
861
|
+
const unwiredEnabledHooks = Object.entries(_POLICY_TO_CC_EVENT)
|
|
862
|
+
.filter(([policyKey, ccEvent]) => {
|
|
863
|
+
const hookCfg = p[policyKey];
|
|
864
|
+
return hookCfg?.enabled === true && wiring[ccEvent] === false;
|
|
865
|
+
})
|
|
866
|
+
.map(([policyKey]) => policyKey);
|
|
867
|
+
return {
|
|
868
|
+
onPreCompact: p.onPreCompact
|
|
869
|
+
? {
|
|
870
|
+
enabled: p.onPreCompact.enabled,
|
|
871
|
+
cooldownMs: p.onPreCompact.cooldownMs,
|
|
872
|
+
}
|
|
873
|
+
: null,
|
|
874
|
+
onPostCompact: p.onPostCompact
|
|
875
|
+
? {
|
|
876
|
+
enabled: p.onPostCompact.enabled,
|
|
877
|
+
cooldownMs: p.onPostCompact.cooldownMs,
|
|
878
|
+
}
|
|
879
|
+
: null,
|
|
880
|
+
onDiagnosticsError: p.onDiagnosticsError
|
|
881
|
+
? { enabled: p.onDiagnosticsError.enabled }
|
|
882
|
+
: null,
|
|
883
|
+
onFileSave: p.onFileSave
|
|
884
|
+
? {
|
|
885
|
+
enabled: p.onFileSave.enabled,
|
|
886
|
+
patternCount: p.onFileSave.patterns.length,
|
|
887
|
+
}
|
|
888
|
+
: null,
|
|
889
|
+
onFileChanged: p.onFileChanged
|
|
890
|
+
? {
|
|
891
|
+
enabled: p.onFileChanged.enabled,
|
|
892
|
+
patternCount: p.onFileChanged.patterns.length,
|
|
893
|
+
}
|
|
894
|
+
: null,
|
|
895
|
+
onCwdChanged: p.onCwdChanged
|
|
896
|
+
? {
|
|
897
|
+
enabled: p.onCwdChanged.enabled,
|
|
898
|
+
cooldownMs: p.onCwdChanged.cooldownMs,
|
|
899
|
+
}
|
|
900
|
+
: null,
|
|
901
|
+
onTestRun: p.onTestRun
|
|
902
|
+
? {
|
|
903
|
+
enabled: p.onTestRun.enabled,
|
|
904
|
+
onFailureOnly: p.onTestRun.onFailureOnly ?? true,
|
|
905
|
+
cooldownMs: p.onTestRun.cooldownMs,
|
|
906
|
+
}
|
|
907
|
+
: null,
|
|
908
|
+
onTestPassAfterFailure: p.onTestPassAfterFailure
|
|
909
|
+
? {
|
|
910
|
+
enabled: p.onTestPassAfterFailure.enabled,
|
|
911
|
+
cooldownMs: p.onTestPassAfterFailure.cooldownMs,
|
|
912
|
+
}
|
|
913
|
+
: null,
|
|
914
|
+
onGitCommit: p.onGitCommit
|
|
915
|
+
? {
|
|
916
|
+
enabled: p.onGitCommit.enabled,
|
|
917
|
+
cooldownMs: p.onGitCommit.cooldownMs,
|
|
918
|
+
}
|
|
919
|
+
: null,
|
|
920
|
+
onGitPush: p.onGitPush
|
|
921
|
+
? {
|
|
922
|
+
enabled: p.onGitPush.enabled,
|
|
923
|
+
cooldownMs: p.onGitPush.cooldownMs,
|
|
924
|
+
}
|
|
925
|
+
: null,
|
|
926
|
+
onBranchCheckout: p.onBranchCheckout
|
|
927
|
+
? {
|
|
928
|
+
enabled: p.onBranchCheckout.enabled,
|
|
929
|
+
cooldownMs: p.onBranchCheckout.cooldownMs,
|
|
930
|
+
}
|
|
931
|
+
: null,
|
|
932
|
+
onPullRequest: p.onPullRequest
|
|
933
|
+
? {
|
|
934
|
+
enabled: p.onPullRequest.enabled,
|
|
935
|
+
cooldownMs: p.onPullRequest.cooldownMs,
|
|
936
|
+
}
|
|
937
|
+
: null,
|
|
938
|
+
onTaskCreated: p.onTaskCreated
|
|
939
|
+
? {
|
|
940
|
+
enabled: p.onTaskCreated.enabled,
|
|
941
|
+
cooldownMs: p.onTaskCreated.cooldownMs,
|
|
942
|
+
}
|
|
943
|
+
: null,
|
|
944
|
+
onInstructionsLoaded: p.onInstructionsLoaded
|
|
945
|
+
? {
|
|
946
|
+
enabled: p.onInstructionsLoaded.enabled,
|
|
947
|
+
cooldownMs: p.onInstructionsLoaded.cooldownMs ?? 60_000,
|
|
948
|
+
}
|
|
949
|
+
: null,
|
|
950
|
+
onPermissionDenied: p.onPermissionDenied
|
|
951
|
+
? {
|
|
952
|
+
enabled: p.onPermissionDenied.enabled,
|
|
953
|
+
cooldownMs: p.onPermissionDenied.cooldownMs,
|
|
954
|
+
}
|
|
955
|
+
: null,
|
|
956
|
+
onDiagnosticsCleared: p.onDiagnosticsCleared
|
|
957
|
+
? {
|
|
958
|
+
enabled: p.onDiagnosticsCleared.enabled,
|
|
959
|
+
cooldownMs: p.onDiagnosticsCleared.cooldownMs,
|
|
960
|
+
}
|
|
961
|
+
: null,
|
|
962
|
+
onTaskSuccess: p.onTaskSuccess
|
|
963
|
+
? {
|
|
964
|
+
enabled: p.onTaskSuccess.enabled,
|
|
965
|
+
cooldownMs: p.onTaskSuccess.cooldownMs,
|
|
966
|
+
}
|
|
967
|
+
: null,
|
|
968
|
+
onGitPull: p.onGitPull
|
|
969
|
+
? {
|
|
970
|
+
enabled: p.onGitPull.enabled,
|
|
971
|
+
cooldownMs: p.onGitPull.cooldownMs,
|
|
972
|
+
}
|
|
973
|
+
: null,
|
|
974
|
+
onDebugSessionEnd: p.onDebugSessionEnd
|
|
975
|
+
? {
|
|
976
|
+
enabled: p.onDebugSessionEnd.enabled,
|
|
977
|
+
cooldownMs: p.onDebugSessionEnd.cooldownMs,
|
|
978
|
+
}
|
|
979
|
+
: null,
|
|
980
|
+
onDebugSessionStart: p.onDebugSessionStart
|
|
981
|
+
? {
|
|
982
|
+
enabled: p.onDebugSessionStart.enabled,
|
|
983
|
+
cooldownMs: p.onDebugSessionStart.cooldownMs,
|
|
984
|
+
}
|
|
985
|
+
: null,
|
|
986
|
+
unwiredEnabledHooks,
|
|
987
|
+
defaultModel: p.defaultModel ?? "claude-haiku-4-5-20251001",
|
|
988
|
+
maxTasksPerHour: p.maxTasksPerHour ?? 20,
|
|
989
|
+
tasksThisHour: tasksInLastHour(this._automationState, Date.now()),
|
|
990
|
+
defaultEffort: p.defaultEffort ?? "low",
|
|
991
|
+
automationSystemPrompt: (p.automationSystemPrompt ?? DEFAULT_AUTOMATION_SYSTEM_PROMPT).slice(0, 80),
|
|
992
|
+
};
|
|
993
|
+
}
|
|
994
|
+
isPreCompactEnabled() {
|
|
995
|
+
return this.policy.onPreCompact?.enabled === true;
|
|
996
|
+
}
|
|
997
|
+
getStats() {
|
|
998
|
+
const hookKeys = [
|
|
999
|
+
"onFileSave",
|
|
1000
|
+
"onFileChanged",
|
|
1001
|
+
"onDiagnosticsError",
|
|
1002
|
+
"onDiagnosticsCleared",
|
|
1003
|
+
"onPreCompact",
|
|
1004
|
+
"onPostCompact",
|
|
1005
|
+
"onInstructionsLoaded",
|
|
1006
|
+
"onBranchCheckout",
|
|
1007
|
+
"onGitCommit",
|
|
1008
|
+
"onGitPull",
|
|
1009
|
+
"onGitPush",
|
|
1010
|
+
"onPullRequest",
|
|
1011
|
+
"onTestRun",
|
|
1012
|
+
"onTestPassAfterFailure",
|
|
1013
|
+
"onPermissionDenied",
|
|
1014
|
+
"onCwdChanged",
|
|
1015
|
+
"onTaskCreated",
|
|
1016
|
+
"onTaskSuccess",
|
|
1017
|
+
"onDebugSessionStart",
|
|
1018
|
+
"onDebugSessionEnd",
|
|
1019
|
+
];
|
|
1020
|
+
const hooksEnabled = hookKeys.filter((k) => {
|
|
1021
|
+
const hook = this.policy[k];
|
|
1022
|
+
return hook !== undefined && hook.enabled !== false;
|
|
1023
|
+
}).length;
|
|
1024
|
+
// Convert skipCountsByHookAndReason Map to plain object
|
|
1025
|
+
const skipCountsByHookAndReason = {};
|
|
1026
|
+
for (const [hook, reasons] of this._skipCountsByHookAndReason) {
|
|
1027
|
+
skipCountsByHookAndReason[hook] = Object.fromEntries(reasons);
|
|
1028
|
+
}
|
|
1029
|
+
return {
|
|
1030
|
+
hooksEnabled,
|
|
1031
|
+
lastFiredAt: this._lastFiredAt,
|
|
1032
|
+
skipCountsByHookAndReason: Object.keys(skipCountsByHookAndReason).length > 0
|
|
1033
|
+
? skipCountsByHookAndReason
|
|
1034
|
+
: undefined,
|
|
1035
|
+
};
|
|
1036
|
+
}
|
|
1037
|
+
}
|
|
1038
|
+
//# sourceMappingURL=automation.js.map
|