patchwork-os 0.2.0-beta.1 → 0.2.0-beta.10.canary.95
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/README.bridge.md +14 -14
- package/README.md +201 -34
- package/deploy/README.md +1 -1
- package/deploy/bootstrap-vps.sh +14 -9
- package/deploy/deploy-dashboard.sh +25 -1
- package/deploy/macos/README.md +153 -0
- package/deploy/macos/com.patchwork.bridge.plist.template +54 -0
- package/deploy/macos/com.patchwork.tunnel.plist.template +76 -0
- package/deploy/macos/install-mac-bridge.sh +244 -0
- package/deploy/macos/uninstall-mac-bridge.sh +22 -0
- package/dist/activityLog.d.ts +6 -0
- package/dist/activityLog.js +63 -26
- package/dist/activityLog.js.map +1 -1
- package/dist/adapters/claude.js +22 -16
- package/dist/adapters/claude.js.map +1 -1
- package/dist/adapters/gemini.js +15 -11
- package/dist/adapters/gemini.js.map +1 -1
- package/dist/adapters/openai.js +9 -9
- package/dist/adapters/openai.js.map +1 -1
- package/dist/ajv2020.d.ts +25 -0
- package/dist/ajv2020.js +33 -0
- package/dist/ajv2020.js.map +1 -0
- package/dist/analyticsAggregator.js +1 -1
- package/dist/analyticsAggregator.js.map +1 -1
- package/dist/analyticsConfig.d.ts +29 -0
- package/dist/analyticsConfig.js +102 -0
- package/dist/analyticsConfig.js.map +1 -0
- package/dist/analyticsPrefs.d.ts +38 -2
- package/dist/analyticsPrefs.js +148 -23
- package/dist/analyticsPrefs.js.map +1 -1
- package/dist/analyticsSend.d.ts +17 -1
- package/dist/analyticsSend.js +67 -5
- package/dist/analyticsSend.js.map +1 -1
- package/dist/approvalHttp.d.ts +14 -0
- package/dist/approvalHttp.js +212 -9
- package/dist/approvalHttp.js.map +1 -1
- package/dist/approvalInsights.js +13 -5
- package/dist/approvalInsights.js.map +1 -1
- package/dist/approvalQueue.d.ts +97 -3
- package/dist/approvalQueue.js +193 -19
- package/dist/approvalQueue.js.map +1 -1
- package/dist/approvalSignals.js +19 -11
- package/dist/approvalSignals.js.map +1 -1
- package/dist/automation.d.ts +35 -10
- package/dist/automation.js +133 -37
- package/dist/automation.js.map +1 -1
- package/dist/automationSuggestions.js +10 -8
- package/dist/automationSuggestions.js.map +1 -1
- package/dist/bridge.d.ts +2 -0
- package/dist/bridge.js +202 -25
- package/dist/bridge.js.map +1 -1
- package/dist/bridgeLockDiscovery.d.ts +27 -1
- package/dist/bridgeLockDiscovery.js +38 -11
- package/dist/bridgeLockDiscovery.js.map +1 -1
- package/dist/bridgeToken.js +3 -2
- package/dist/bridgeToken.js.map +1 -1
- package/dist/claudeDriver.js +23 -8
- package/dist/claudeDriver.js.map +1 -1
- package/dist/claudeOrchestrator.d.ts +1 -1
- package/dist/claudeOrchestrator.js +87 -44
- package/dist/claudeOrchestrator.js.map +1 -1
- package/dist/commands/analytics.d.ts +8 -0
- package/dist/commands/analytics.js +134 -0
- package/dist/commands/analytics.js.map +1 -0
- package/dist/commands/auditEnv.d.ts +36 -0
- package/dist/commands/auditEnv.js +202 -0
- package/dist/commands/auditEnv.js.map +1 -0
- package/dist/commands/dashboard.js +8 -1
- package/dist/commands/dashboard.js.map +1 -1
- package/dist/commands/doctor.d.ts +35 -0
- package/dist/commands/doctor.js +51 -0
- package/dist/commands/doctor.js.map +1 -0
- package/dist/commands/install.js +3 -0
- package/dist/commands/install.js.map +1 -1
- package/dist/commands/launchd.js +15 -16
- package/dist/commands/launchd.js.map +1 -1
- package/dist/commands/patchworkInit.d.ts +5 -0
- package/dist/commands/patchworkInit.js +89 -7
- package/dist/commands/patchworkInit.js.map +1 -1
- package/dist/commands/recipe.d.ts +110 -0
- package/dist/commands/recipe.js +512 -9
- package/dist/commands/recipe.js.map +1 -1
- package/dist/commands/recipeInstall.js +11 -4
- package/dist/commands/recipeInstall.js.map +1 -1
- package/dist/commands/shadowScan.d.ts +34 -0
- package/dist/commands/shadowScan.js +142 -0
- package/dist/commands/shadowScan.js.map +1 -0
- package/dist/commands/task.js +2 -2
- package/dist/commands/task.js.map +1 -1
- package/dist/commands/tools.d.ts +20 -1
- package/dist/commands/tools.js +112 -3
- package/dist/commands/tools.js.map +1 -1
- package/dist/commands/tracesImport.js +21 -4
- package/dist/commands/tracesImport.js.map +1 -1
- package/dist/commitIssueLinkLog.d.ts +16 -0
- package/dist/commitIssueLinkLog.js +110 -24
- package/dist/commitIssueLinkLog.js.map +1 -1
- package/dist/companions/registry.js +15 -7
- package/dist/companions/registry.js.map +1 -1
- package/dist/config.d.ts +47 -3
- package/dist/config.js +111 -21
- package/dist/config.js.map +1 -1
- package/dist/connectorRoutes.js +866 -57
- package/dist/connectorRoutes.js.map +1 -1
- package/dist/connectors/airtable.d.ts +230 -0
- package/dist/connectors/airtable.js +700 -0
- package/dist/connectors/airtable.js.map +1 -0
- package/dist/connectors/asana.js +13 -9
- package/dist/connectors/asana.js.map +1 -1
- package/dist/connectors/baseConnector.js +25 -3
- package/dist/connectors/baseConnector.js.map +1 -1
- package/dist/connectors/caldiy.d.ts +137 -0
- package/dist/connectors/caldiy.js +424 -0
- package/dist/connectors/caldiy.js.map +1 -0
- package/dist/connectors/circleci.d.ts +162 -0
- package/dist/connectors/circleci.js +576 -0
- package/dist/connectors/circleci.js.map +1 -0
- package/dist/connectors/cloudflare.d.ts +132 -0
- package/dist/connectors/cloudflare.js +622 -0
- package/dist/connectors/cloudflare.js.map +1 -0
- package/dist/connectors/confluence.js +35 -0
- package/dist/connectors/confluence.js.map +1 -1
- package/dist/connectors/connectorRedirectUri.d.ts +30 -0
- package/dist/connectors/connectorRedirectUri.js +38 -0
- package/dist/connectors/connectorRedirectUri.js.map +1 -0
- package/dist/connectors/connectorRegistry.d.ts +63 -0
- package/dist/connectors/connectorRegistry.js +354 -0
- package/dist/connectors/connectorRegistry.js.map +1 -0
- package/dist/connectors/datadog.js +33 -4
- package/dist/connectors/datadog.js.map +1 -1
- package/dist/connectors/discord.js +14 -10
- package/dist/connectors/discord.js.map +1 -1
- package/dist/connectors/elasticsearch.d.ts +141 -0
- package/dist/connectors/elasticsearch.js +601 -0
- package/dist/connectors/elasticsearch.js.map +1 -0
- package/dist/connectors/figma.d.ts +130 -0
- package/dist/connectors/figma.js +387 -0
- package/dist/connectors/figma.js.map +1 -0
- package/dist/connectors/github.d.ts +17 -0
- package/dist/connectors/github.js +53 -2
- package/dist/connectors/github.js.map +1 -1
- package/dist/connectors/gitlab.js +12 -6
- package/dist/connectors/gitlab.js.map +1 -1
- package/dist/connectors/gmail.js +72 -21
- package/dist/connectors/gmail.js.map +1 -1
- package/dist/connectors/googleCalendar.js +8 -8
- package/dist/connectors/googleCalendar.js.map +1 -1
- package/dist/connectors/googleDocs.d.ts +103 -0
- package/dist/connectors/googleDocs.js +409 -0
- package/dist/connectors/googleDocs.js.map +1 -0
- package/dist/connectors/googleDrive.d.ts +12 -0
- package/dist/connectors/googleDrive.js +35 -8
- package/dist/connectors/googleDrive.js.map +1 -1
- package/dist/connectors/grafana.d.ts +133 -0
- package/dist/connectors/grafana.js +478 -0
- package/dist/connectors/grafana.js.map +1 -0
- package/dist/connectors/jira.d.ts +25 -0
- package/dist/connectors/jira.js +227 -1
- package/dist/connectors/jira.js.map +1 -1
- package/dist/connectors/linear.js +1 -1
- package/dist/connectors/linear.js.map +1 -1
- package/dist/connectors/mcpOAuth.js +78 -16
- package/dist/connectors/mcpOAuth.js.map +1 -1
- package/dist/connectors/monday.d.ts +217 -0
- package/dist/connectors/monday.js +655 -0
- package/dist/connectors/monday.js.map +1 -0
- package/dist/connectors/mongodb.d.ts +139 -0
- package/dist/connectors/mongodb.js +455 -0
- package/dist/connectors/mongodb.js.map +1 -0
- package/dist/connectors/oauthStateStore.d.ts +20 -0
- package/dist/connectors/oauthStateStore.js +94 -4
- package/dist/connectors/oauthStateStore.js.map +1 -1
- package/dist/connectors/obsidian.d.ts +83 -0
- package/dist/connectors/obsidian.js +441 -0
- package/dist/connectors/obsidian.js.map +1 -0
- package/dist/connectors/paystack.d.ts +159 -0
- package/dist/connectors/paystack.js +607 -0
- package/dist/connectors/paystack.js.map +1 -0
- package/dist/connectors/pipedrive.d.ts +189 -0
- package/dist/connectors/pipedrive.js +559 -0
- package/dist/connectors/pipedrive.js.map +1 -0
- package/dist/connectors/postgres.d.ts +127 -0
- package/dist/connectors/postgres.js +512 -0
- package/dist/connectors/postgres.js.map +1 -0
- package/dist/connectors/posthog.d.ts +119 -0
- package/dist/connectors/posthog.js +479 -0
- package/dist/connectors/posthog.js.map +1 -0
- package/dist/connectors/redis.d.ts +140 -0
- package/dist/connectors/redis.js +571 -0
- package/dist/connectors/redis.js.map +1 -0
- package/dist/connectors/resend.d.ts +131 -0
- package/dist/connectors/resend.js +529 -0
- package/dist/connectors/resend.js.map +1 -0
- package/dist/connectors/salesforce.d.ts +136 -0
- package/dist/connectors/salesforce.js +603 -0
- package/dist/connectors/salesforce.js.map +1 -0
- package/dist/connectors/secrets.d.ts +51 -0
- package/dist/connectors/secrets.js +102 -0
- package/dist/connectors/secrets.js.map +1 -0
- package/dist/connectors/sendgrid.d.ts +102 -0
- package/dist/connectors/sendgrid.js +423 -0
- package/dist/connectors/sendgrid.js.map +1 -0
- package/dist/connectors/shopify.d.ts +145 -0
- package/dist/connectors/shopify.js +502 -0
- package/dist/connectors/shopify.js.map +1 -0
- package/dist/connectors/slack.d.ts +1 -1
- package/dist/connectors/slack.js +61 -9
- package/dist/connectors/slack.js.map +1 -1
- package/dist/connectors/snowflake.d.ts +119 -0
- package/dist/connectors/snowflake.js +615 -0
- package/dist/connectors/snowflake.js.map +1 -0
- package/dist/connectors/supabase.d.ts +92 -0
- package/dist/connectors/supabase.js +630 -0
- package/dist/connectors/supabase.js.map +1 -0
- package/dist/connectors/todoist.d.ts +117 -0
- package/dist/connectors/todoist.js +485 -0
- package/dist/connectors/todoist.js.map +1 -0
- package/dist/connectors/tokenStorage.js +56 -14
- package/dist/connectors/tokenStorage.js.map +1 -1
- package/dist/connectors/twilio.d.ts +118 -0
- package/dist/connectors/twilio.js +475 -0
- package/dist/connectors/twilio.js.map +1 -0
- package/dist/connectors/vercel.d.ts +110 -0
- package/dist/connectors/vercel.js +479 -0
- package/dist/connectors/vercel.js.map +1 -0
- package/dist/connectors/webflow.d.ts +118 -0
- package/dist/connectors/webflow.js +393 -0
- package/dist/connectors/webflow.js.map +1 -0
- package/dist/connectors/woocommerce.d.ts +220 -0
- package/dist/connectors/woocommerce.js +464 -0
- package/dist/connectors/woocommerce.js.map +1 -0
- package/dist/crypto.js +7 -2
- package/dist/crypto.js.map +1 -1
- package/dist/decisionReplay.js +15 -6
- package/dist/decisionReplay.js.map +1 -1
- package/dist/decisionTraceLog.d.ts +28 -0
- package/dist/decisionTraceLog.js +156 -29
- package/dist/decisionTraceLog.js.map +1 -1
- package/dist/drivers/claude/api.js +5 -4
- package/dist/drivers/claude/api.js.map +1 -1
- package/dist/drivers/claude/envSanitizer.d.ts +0 -6
- package/dist/drivers/claude/envSanitizer.js +17 -2
- package/dist/drivers/claude/envSanitizer.js.map +1 -1
- package/dist/drivers/claude/streamParser.js +5 -4
- package/dist/drivers/claude/streamParser.js.map +1 -1
- package/dist/drivers/claude/subprocess.js +22 -3
- package/dist/drivers/claude/subprocess.js.map +1 -1
- package/dist/drivers/gemini/index.d.ts +22 -0
- package/dist/drivers/gemini/index.js +256 -129
- package/dist/drivers/gemini/index.js.map +1 -1
- package/dist/drivers/local/index.d.ts +17 -0
- package/dist/drivers/local/index.js +99 -0
- package/dist/drivers/local/index.js.map +1 -1
- package/dist/drivers/openai/index.js +30 -2
- package/dist/drivers/openai/index.js.map +1 -1
- package/dist/extensionClient.d.ts +37 -4
- package/dist/extensionClient.js +58 -16
- package/dist/extensionClient.js.map +1 -1
- package/dist/featureFlags.d.ts +91 -0
- package/dist/featureFlags.js +174 -3
- package/dist/featureFlags.js.map +1 -1
- package/dist/fileLock.js +21 -12
- package/dist/fileLock.js.map +1 -1
- package/dist/fileLockSync.d.ts +67 -0
- package/dist/fileLockSync.js +126 -0
- package/dist/fileLockSync.js.map +1 -0
- package/dist/fp/activityAnalytics.js +26 -12
- package/dist/fp/activityAnalytics.js.map +1 -1
- package/dist/fp/automationInterpreter.d.ts +15 -1
- package/dist/fp/automationInterpreter.js +217 -82
- package/dist/fp/automationInterpreter.js.map +1 -1
- package/dist/fp/automationProgram.d.ts +30 -0
- package/dist/fp/automationProgram.js.map +1 -1
- package/dist/fp/automationState.d.ts +24 -5
- package/dist/fp/automationState.js +56 -9
- package/dist/fp/automationState.js.map +1 -1
- package/dist/fp/automationUtils.js +1 -1
- package/dist/fp/automationUtils.js.map +1 -1
- package/dist/fp/commandDescription.js +7 -1
- package/dist/fp/commandDescription.js.map +1 -1
- package/dist/fp/extensionSnapshot.js +8 -2
- package/dist/fp/extensionSnapshot.js.map +1 -1
- package/dist/fp/interpreterContext.d.ts +66 -1
- package/dist/fp/interpreterContext.js +91 -1
- package/dist/fp/interpreterContext.js.map +1 -1
- package/dist/fp/policyParser.js +29 -1
- package/dist/fp/policyParser.js.map +1 -1
- package/dist/fsWatchWithFallback.d.ts +36 -0
- package/dist/fsWatchWithFallback.js +128 -0
- package/dist/fsWatchWithFallback.js.map +1 -0
- package/dist/haltPushDispatch.d.ts +33 -0
- package/dist/haltPushDispatch.js +103 -0
- package/dist/haltPushDispatch.js.map +1 -0
- package/dist/httpBodyValidation.d.ts +41 -0
- package/dist/httpBodyValidation.js +45 -0
- package/dist/httpBodyValidation.js.map +1 -0
- package/dist/inboxRoutes.d.ts +22 -0
- package/dist/inboxRoutes.js +61 -1
- package/dist/inboxRoutes.js.map +1 -1
- package/dist/index.js +1053 -76
- package/dist/index.js.map +1 -1
- package/dist/installGuard.js +6 -2
- package/dist/installGuard.js.map +1 -1
- package/dist/lockfile.js +31 -4
- package/dist/lockfile.js.map +1 -1
- package/dist/oauth.d.ts +9 -0
- package/dist/oauth.js +33 -0
- package/dist/oauth.js.map +1 -1
- package/dist/oauthRoutes.d.ts +1 -1
- package/dist/oauthRoutes.js +5 -3
- package/dist/oauthRoutes.js.map +1 -1
- package/dist/orchestrator/childBridgeRegistry.js +29 -11
- package/dist/orchestrator/childBridgeRegistry.js.map +1 -1
- package/dist/patchworkConfig.d.ts +44 -1
- package/dist/patchworkConfig.js +28 -3
- package/dist/patchworkConfig.js.map +1 -1
- package/dist/pluginLoader.js +10 -1
- package/dist/pluginLoader.js.map +1 -1
- package/dist/pluginWatcher.js +12 -14
- package/dist/pluginWatcher.js.map +1 -1
- package/dist/preToolUseHook.js +3 -2
- package/dist/preToolUseHook.js.map +1 -1
- package/dist/processTree.d.ts +34 -0
- package/dist/processTree.js +105 -0
- package/dist/processTree.js.map +1 -0
- package/dist/prompts.js +3 -3
- package/dist/prompts.js.map +1 -1
- package/dist/recipeOrchestration.d.ts +9 -0
- package/dist/recipeOrchestration.js +341 -24
- package/dist/recipeOrchestration.js.map +1 -1
- package/dist/recipeRoutes.d.ts +58 -2
- package/dist/recipeRoutes.js +744 -115
- package/dist/recipeRoutes.js.map +1 -1
- package/dist/recipes/RecipeOrchestrator.d.ts +2 -0
- package/dist/recipes/RecipeOrchestrator.js +6 -1
- package/dist/recipes/RecipeOrchestrator.js.map +1 -1
- package/dist/recipes/agentExecutor.d.ts +25 -5
- package/dist/recipes/agentExecutor.js.map +1 -1
- package/dist/recipes/chainedRunner.d.ts +2 -0
- package/dist/recipes/chainedRunner.js +142 -5
- package/dist/recipes/chainedRunner.js.map +1 -1
- package/dist/recipes/connectorPreflight.d.ts +66 -0
- package/dist/recipes/connectorPreflight.js +169 -0
- package/dist/recipes/connectorPreflight.js.map +1 -0
- package/dist/recipes/dependencyGraph.js +13 -5
- package/dist/recipes/dependencyGraph.js.map +1 -1
- package/dist/recipes/githubInstallSource.d.ts +128 -0
- package/dist/recipes/githubInstallSource.js +206 -0
- package/dist/recipes/githubInstallSource.js.map +1 -0
- package/dist/recipes/haltCategory.d.ts +119 -0
- package/dist/recipes/haltCategory.js +188 -0
- package/dist/recipes/haltCategory.js.map +1 -0
- package/dist/recipes/idempotencyKey.d.ts +134 -0
- package/dist/recipes/idempotencyKey.js +322 -0
- package/dist/recipes/idempotencyKey.js.map +1 -0
- package/dist/recipes/installer.js +48 -2
- package/dist/recipes/installer.js.map +1 -1
- package/dist/recipes/judgeSummary.d.ts +50 -0
- package/dist/recipes/judgeSummary.js +47 -0
- package/dist/recipes/judgeSummary.js.map +1 -0
- package/dist/recipes/judgeVerdict.d.ts +48 -0
- package/dist/recipes/judgeVerdict.js +174 -0
- package/dist/recipes/judgeVerdict.js.map +1 -0
- package/dist/recipes/migrations/index.d.ts +9 -0
- package/dist/recipes/migrations/index.js +133 -0
- package/dist/recipes/migrations/index.js.map +1 -1
- package/dist/recipes/names.d.ts +20 -0
- package/dist/recipes/names.js +25 -0
- package/dist/recipes/names.js.map +1 -1
- package/dist/recipes/parser.js +88 -5
- package/dist/recipes/parser.js.map +1 -1
- package/dist/recipes/replayRun.js +1 -1
- package/dist/recipes/replayRun.js.map +1 -1
- package/dist/recipes/runBudget.d.ts +70 -0
- package/dist/recipes/runBudget.js +109 -0
- package/dist/recipes/runBudget.js.map +1 -0
- package/dist/recipes/scheduler.d.ts +30 -0
- package/dist/recipes/scheduler.js +69 -19
- package/dist/recipes/scheduler.js.map +1 -1
- package/dist/recipes/schema.d.ts +36 -0
- package/dist/recipes/schemaGenerator.js +103 -5
- package/dist/recipes/schemaGenerator.js.map +1 -1
- package/dist/recipes/stepObservation.js +9 -0
- package/dist/recipes/stepObservation.js.map +1 -1
- package/dist/recipes/toolRegistry.js +20 -3
- package/dist/recipes/toolRegistry.js.map +1 -1
- package/dist/recipes/tools/fanOut.d.ts +20 -0
- package/dist/recipes/tools/fanOut.js +199 -0
- package/dist/recipes/tools/fanOut.js.map +1 -0
- package/dist/recipes/tools/file.js +5 -2
- package/dist/recipes/tools/file.js.map +1 -1
- package/dist/recipes/tools/github.d.ts +1 -1
- package/dist/recipes/tools/github.js +75 -1
- package/dist/recipes/tools/github.js.map +1 -1
- package/dist/recipes/tools/gmail.js +27 -5
- package/dist/recipes/tools/gmail.js.map +1 -1
- package/dist/recipes/tools/googleDrive.js +64 -0
- package/dist/recipes/tools/googleDrive.js.map +1 -1
- package/dist/recipes/tools/http.d.ts +10 -0
- package/dist/recipes/tools/http.js +176 -0
- package/dist/recipes/tools/http.js.map +1 -0
- package/dist/recipes/tools/index.d.ts +2 -0
- package/dist/recipes/tools/index.js +2 -0
- package/dist/recipes/tools/index.js.map +1 -1
- package/dist/recipes/tools/slack.js +1 -1
- package/dist/recipes/validation.d.ts +17 -0
- package/dist/recipes/validation.js +29 -11
- package/dist/recipes/validation.js.map +1 -1
- package/dist/recipes/workspaceRoot.d.ts +37 -0
- package/dist/recipes/workspaceRoot.js +73 -0
- package/dist/recipes/workspaceRoot.js.map +1 -0
- package/dist/recipes/yamlPositions.d.ts +56 -0
- package/dist/recipes/yamlPositions.js +183 -0
- package/dist/recipes/yamlPositions.js.map +1 -0
- package/dist/recipes/yamlRunner.d.ts +182 -8
- package/dist/recipes/yamlRunner.js +877 -217
- package/dist/recipes/yamlRunner.js.map +1 -1
- package/dist/recipesHttp.d.ts +19 -3
- package/dist/recipesHttp.js +67 -11
- package/dist/recipesHttp.js.map +1 -1
- package/dist/resources.js +21 -13
- package/dist/resources.js.map +1 -1
- package/dist/runLog.d.ts +58 -5
- package/dist/runLog.js +98 -21
- package/dist/runLog.js.map +1 -1
- package/dist/sanitizeParsedJson.d.ts +39 -0
- package/dist/sanitizeParsedJson.js +55 -0
- package/dist/sanitizeParsedJson.js.map +1 -0
- package/dist/schemas/recipe.v1.json +885 -196
- package/dist/server.d.ts +160 -3
- package/dist/server.js +1099 -122
- package/dist/server.js.map +1 -1
- package/dist/sessionCheckpoint.d.ts +8 -0
- package/dist/sessionCheckpoint.js +33 -3
- package/dist/sessionCheckpoint.js.map +1 -1
- package/dist/ssrfGuard.d.ts +16 -0
- package/dist/ssrfGuard.js +87 -4
- package/dist/ssrfGuard.js.map +1 -1
- package/dist/streamableHttp.d.ts +9 -4
- package/dist/streamableHttp.js +76 -20
- package/dist/streamableHttp.js.map +1 -1
- package/dist/telemetry.js +19 -12
- package/dist/telemetry.js.map +1 -1
- package/dist/testing/shadowRun.d.ts +52 -0
- package/dist/testing/shadowRun.js +71 -0
- package/dist/testing/shadowRun.js.map +1 -0
- package/dist/tools/batchLsp.d.ts +3 -0
- package/dist/tools/bridgeDoctor.d.ts +11 -0
- package/dist/tools/bridgeDoctor.js +48 -2
- package/dist/tools/bridgeDoctor.js.map +1 -1
- package/dist/tools/bridgeStatus.d.ts +0 -12
- package/dist/tools/bridgeStatus.js +2 -28
- package/dist/tools/bridgeStatus.js.map +1 -1
- package/dist/tools/cancelClaudeTask.d.ts +1 -0
- package/dist/tools/clipboard.d.ts +2 -0
- package/dist/tools/closeTabs.d.ts +1 -0
- package/dist/tools/codeLens.d.ts +1 -0
- package/dist/tools/createIssueFromAIComment.d.ts +1 -0
- package/dist/tools/ctxGetTaskContext.d.ts +9 -0
- package/dist/tools/ctxGetTaskContext.js +50 -2
- package/dist/tools/ctxGetTaskContext.js.map +1 -1
- package/dist/tools/ctxQueryTraces.js +32 -24
- package/dist/tools/ctxQueryTraces.js.map +1 -1
- package/dist/tools/ctxSaveTrace.d.ts +1 -0
- package/dist/tools/debug.d.ts +4 -0
- package/dist/tools/decorations.d.ts +2 -0
- package/dist/tools/detectUnusedCode.js +9 -7
- package/dist/tools/detectUnusedCode.js.map +1 -1
- package/dist/tools/documentLinks.d.ts +1 -0
- package/dist/tools/editText.d.ts +1 -0
- package/dist/tools/editText.js +2 -1
- package/dist/tools/editText.js.map +1 -1
- package/dist/tools/enrichCommit.d.ts +1 -0
- package/dist/tools/enrichStackTrace.js +11 -5
- package/dist/tools/enrichStackTrace.js.map +1 -1
- package/dist/tools/explainDiagnostic.d.ts +1 -0
- package/dist/tools/explainSymbol.d.ts +1 -0
- package/dist/tools/fileOperations.d.ts +3 -0
- package/dist/tools/fileOperations.js +2 -1
- package/dist/tools/fileOperations.js.map +1 -1
- package/dist/tools/fileWatcher.d.ts +2 -0
- package/dist/tools/fileWatcher.js +8 -2
- package/dist/tools/fileWatcher.js.map +1 -1
- package/dist/tools/findFiles.d.ts +1 -0
- package/dist/tools/fixAllLintErrors.d.ts +1 -0
- package/dist/tools/fixAllLintErrors.js +10 -5
- package/dist/tools/fixAllLintErrors.js.map +1 -1
- package/dist/tools/foldingRanges.d.ts +1 -0
- package/dist/tools/formatDocument.d.ts +1 -0
- package/dist/tools/formatDocument.js +10 -5
- package/dist/tools/formatDocument.js.map +1 -1
- package/dist/tools/generateTests.d.ts +1 -0
- package/dist/tools/getAIComments.d.ts +1 -0
- package/dist/tools/getAnalyticsReport.js +5 -1
- package/dist/tools/getAnalyticsReport.js.map +1 -1
- package/dist/tools/getBufferContent.d.ts +1 -0
- package/dist/tools/getChangeImpact.d.ts +1 -0
- package/dist/tools/getChangeImpact.js +23 -14
- package/dist/tools/getChangeImpact.js.map +1 -1
- package/dist/tools/getClaudeTaskStatus.d.ts +1 -0
- package/dist/tools/getCodeCoverage.d.ts +1 -0
- package/dist/tools/getCodeCoverage.js +32 -14
- package/dist/tools/getCodeCoverage.js.map +1 -1
- package/dist/tools/getCommitsForIssue.d.ts +1 -0
- package/dist/tools/getDebugState.d.ts +1 -0
- package/dist/tools/getDiagnostics.js +32 -29
- package/dist/tools/getDiagnostics.js.map +1 -1
- package/dist/tools/getDiffFromHandoff.js +8 -2
- package/dist/tools/getDiffFromHandoff.js.map +1 -1
- package/dist/tools/getDocumentSymbols.d.ts +1 -0
- package/dist/tools/getGitHotspots.d.ts +1 -0
- package/dist/tools/getImportedSignatures.d.ts +1 -0
- package/dist/tools/getImportedSignatures.js +18 -14
- package/dist/tools/getImportedSignatures.js.map +1 -1
- package/dist/tools/getPRTemplate.d.ts +1 -0
- package/dist/tools/getProjectContext.js +23 -10
- package/dist/tools/getProjectContext.js.map +1 -1
- package/dist/tools/getSymbolHistory.d.ts +1 -0
- package/dist/tools/getToolCapabilities.d.ts +10 -5
- package/dist/tools/getToolCapabilities.js +79 -202
- package/dist/tools/getToolCapabilities.js.map +1 -1
- package/dist/tools/getTypeSignature.d.ts +1 -0
- package/dist/tools/getWorkspaceSettings.d.ts +1 -0
- package/dist/tools/gitWrite.d.ts +11 -0
- package/dist/tools/github/actions.d.ts +2 -0
- package/dist/tools/github/composite.d.ts +3 -0
- package/dist/tools/github/issues.d.ts +4 -0
- package/dist/tools/github/pr.d.ts +7 -0
- package/dist/tools/handoffNote.d.ts +1 -0
- package/dist/tools/handoffNote.js +2 -1
- package/dist/tools/handoffNote.js.map +1 -1
- package/dist/tools/headless/lspClient.js +3 -0
- package/dist/tools/headless/lspClient.js.map +1 -1
- package/dist/tools/hoverAtCursor.d.ts +1 -0
- package/dist/tools/httpClient.d.ts +2 -0
- package/dist/tools/httpClient.js +38 -71
- package/dist/tools/httpClient.js.map +1 -1
- package/dist/tools/inlayHints.d.ts +1 -0
- package/dist/tools/launchQuickTask.d.ts +1 -0
- package/dist/tools/listClaudeTasks.d.ts +1 -0
- package/dist/tools/listClaudeTasks.js +10 -8
- package/dist/tools/listClaudeTasks.js.map +1 -1
- package/dist/tools/listTerminals.d.ts +1 -0
- package/dist/tools/lsp.d.ts +15 -0
- package/dist/tools/lsp.js +17 -0
- package/dist/tools/lsp.js.map +1 -1
- package/dist/tools/navigateToSymbolByName.d.ts +1 -0
- package/dist/tools/openDiff.d.ts +1 -0
- package/dist/tools/openDiff.js +4 -1
- package/dist/tools/openDiff.js.map +1 -1
- package/dist/tools/openFile.d.ts +1 -0
- package/dist/tools/openFile.js +4 -1
- package/dist/tools/openFile.js.map +1 -1
- package/dist/tools/openInBrowser.js +6 -1
- package/dist/tools/openInBrowser.js.map +1 -1
- package/dist/tools/organizeImports.d.ts +1 -0
- package/dist/tools/organizeImports.js +5 -3
- package/dist/tools/organizeImports.js.map +1 -1
- package/dist/tools/performanceReport.js +5 -3
- package/dist/tools/performanceReport.js.map +1 -1
- package/dist/tools/planPersistence.d.ts +3 -0
- package/dist/tools/planPersistence.js +4 -1
- package/dist/tools/planPersistence.js.map +1 -1
- package/dist/tools/previewEdit.d.ts +1 -0
- package/dist/tools/previewEdit.js +15 -4
- package/dist/tools/previewEdit.js.map +1 -1
- package/dist/tools/recentTracesDigest.js +54 -11
- package/dist/tools/recentTracesDigest.js.map +1 -1
- package/dist/tools/refactorAnalyze.d.ts +1 -0
- package/dist/tools/refactorExtractFunction.js +4 -1
- package/dist/tools/refactorExtractFunction.js.map +1 -1
- package/dist/tools/refactorPreview.d.ts +1 -0
- package/dist/tools/refactorPreview.js +10 -2
- package/dist/tools/refactorPreview.js.map +1 -1
- package/dist/tools/replaceBlock.d.ts +1 -0
- package/dist/tools/replaceBlock.js +2 -1
- package/dist/tools/replaceBlock.js.map +1 -1
- package/dist/tools/resumeClaudeTask.d.ts +1 -0
- package/dist/tools/runClaudeTask.d.ts +1 -0
- package/dist/tools/runTests.js +15 -4
- package/dist/tools/runTests.js.map +1 -1
- package/dist/tools/screenshot.d.ts +1 -0
- package/dist/tools/screenshotAndAnnotate.js +6 -2
- package/dist/tools/screenshotAndAnnotate.js.map +1 -1
- package/dist/tools/searchAndReplace.d.ts +1 -0
- package/dist/tools/searchAndReplace.js +17 -7
- package/dist/tools/searchAndReplace.js.map +1 -1
- package/dist/tools/searchTools.js +20 -19
- package/dist/tools/searchTools.js.map +1 -1
- package/dist/tools/searchWorkspace.d.ts +1 -0
- package/dist/tools/selectionRanges.d.ts +1 -0
- package/dist/tools/semanticTokens.d.ts +1 -0
- package/dist/tools/signatureHelp.d.ts +1 -0
- package/dist/tools/spawnWorkspace.js +15 -7
- package/dist/tools/spawnWorkspace.js.map +1 -1
- package/dist/tools/terminal.d.ts +6 -0
- package/dist/tools/testRunners/pytest.js +6 -2
- package/dist/tools/testRunners/pytest.js.map +1 -1
- package/dist/tools/testRunners/vitestJest.js +3 -1
- package/dist/tools/testRunners/vitestJest.js.map +1 -1
- package/dist/tools/testTraceToSource.d.ts +1 -0
- package/dist/tools/transaction.d.ts +4 -0
- package/dist/tools/transaction.js +4 -1
- package/dist/tools/transaction.js.map +1 -1
- package/dist/tools/typeHierarchy.d.ts +1 -0
- package/dist/tools/utils.d.ts +18 -0
- package/dist/tools/utils.js +103 -18
- package/dist/tools/utils.js.map +1 -1
- package/dist/tools/vscodeCommands.d.ts +2 -0
- package/dist/tools/vscodeTasks.d.ts +2 -0
- package/dist/tools/workspaceSettings.d.ts +1 -0
- package/dist/transport.d.ts +3 -1
- package/dist/transport.js +103 -52
- package/dist/transport.js.map +1 -1
- package/dist/winShim.d.ts +34 -0
- package/dist/winShim.js +94 -0
- package/dist/winShim.js.map +1 -0
- package/dist/wireHaltPushDispatch.d.ts +38 -0
- package/dist/wireHaltPushDispatch.js +71 -0
- package/dist/wireHaltPushDispatch.js.map +1 -0
- package/dist/writeFileAtomic.d.ts +23 -0
- package/dist/writeFileAtomic.js +121 -0
- package/dist/writeFileAtomic.js.map +1 -0
- package/package.json +23 -7
- package/scripts/postinstall.mjs +42 -2
- package/scripts/smoke/run-all.mjs +213 -0
- package/scripts/start-all.mjs +572 -0
- package/scripts/start-all.ps1 +209 -0
- package/scripts/start-all.sh +73 -17
- package/scripts/start-orchestrator.ps1 +158 -0
- package/scripts/start-remote.mjs +122 -0
- package/templates/automation-policies/recipe-authoring.json +1 -1
- package/templates/automation-policies/security-first.json +1 -1
- package/templates/automation-policies/strict-lint.json +1 -1
- package/templates/automation-policies/test-driven.json +1 -1
- package/templates/automation-policy.example.json +1 -1
- package/templates/co.patchwork-os.bridge.plist +2 -2
- package/templates/recipes/approval-queue-ui-test.yaml +1 -1
- package/templates/recipes/ctx-loop-test.yaml +1 -1
- package/templates/recipes/fix-errors-on-save.yaml +71 -0
- package/templates/recipes/morning-brief.yaml +5 -2
- package/templates/recipes/project-health-check.yaml +4 -1
- package/templates/recipes/sentry-to-linear.yaml +72 -38
- package/templates/recipes/webhook/apple-watch-health-log.yaml +145 -0
- package/templates/recipes/webhook/customer-escalation.yaml +8 -9
- package/templates/recipes/webhook/meeting-prep.yaml +11 -5
- package/dist/commands/marketplace.d.ts +0 -16
- package/dist/commands/marketplace.js +0 -32
- package/dist/commands/marketplace.js.map +0 -1
- package/dist/recipes/legacyRecipeCompat.d.ts +0 -10
- package/dist/recipes/legacyRecipeCompat.js +0 -131
- package/dist/recipes/legacyRecipeCompat.js.map +0 -1
package/dist/commands/recipe.js
CHANGED
|
@@ -11,10 +11,11 @@ import { parse as parseYaml, stringify as stringifyYaml } from "yaml";
|
|
|
11
11
|
import "../recipes/tools/index.js";
|
|
12
12
|
import { loadFixtureLibrary } from "../connectors/fixtureLibrary.js";
|
|
13
13
|
import { MockConnector } from "../connectors/mockConnector.js";
|
|
14
|
-
import {
|
|
14
|
+
import { HALT_CATEGORY_HINTS, HALT_CATEGORY_LABELS, } from "../recipes/haltCategory.js";
|
|
15
|
+
import { defaultDeprecationWarn, normalizeRecipeForRuntime, } from "../recipes/migrations/index.js";
|
|
15
16
|
import { tryResolveRecipePath } from "../recipes/resolveRecipePath.js";
|
|
16
17
|
import { generateSchemaSet, writeSchemas } from "../recipes/schemaGenerator.js";
|
|
17
|
-
import { getTool, isConnectorNamespace, seedToolOutputPreviewContext, } from "../recipes/toolRegistry.js";
|
|
18
|
+
import { getTool, isConnectorNamespace, listConnectorNamespaces, listTools, seedToolOutputPreviewContext, } from "../recipes/toolRegistry.js";
|
|
18
19
|
import { validateRecipeDefinition, } from "../recipes/validation.js";
|
|
19
20
|
import { buildChainedDeps, dispatchRecipe, loadYamlRecipe, render, runYamlRecipe, } from "../recipes/yamlRunner.js";
|
|
20
21
|
import { findYamlRecipePath } from "../recipesHttp.js";
|
|
@@ -113,6 +114,355 @@ export function runNew(options) {
|
|
|
113
114
|
export function listTemplates() {
|
|
114
115
|
return Object.keys(TEMPLATES);
|
|
115
116
|
}
|
|
117
|
+
// ============================================================================
|
|
118
|
+
// patchwork recipe new --interactive
|
|
119
|
+
//
|
|
120
|
+
// Connector-aware prompt tree. Writes a valid recipe YAML based on
|
|
121
|
+
// user choices. Use a `node:readline/promises` adapter in real CLI;
|
|
122
|
+
// tests inject a stub `InteractivePromptDeps`.
|
|
123
|
+
// ============================================================================
|
|
124
|
+
const RECIPE_NAME_RE = /^[a-z0-9][a-z0-9_-]{1,63}$/;
|
|
125
|
+
const CRON_SHAPE_RE = /^(@every\s+\d+\s*(ms|s|m|h)|\S+\s+\S+\s+\S+\s+\S+\s+\S+)$/i;
|
|
126
|
+
async function askWithValidation(deps, question, validate) {
|
|
127
|
+
for (let attempt = 0; attempt < 5; attempt++) {
|
|
128
|
+
const answer = await deps.ask(question);
|
|
129
|
+
const error = validate(answer);
|
|
130
|
+
if (!error)
|
|
131
|
+
return answer;
|
|
132
|
+
// Surface error and re-ask via the question prompt itself.
|
|
133
|
+
question = `${error} ${question}`;
|
|
134
|
+
}
|
|
135
|
+
throw new Error("Too many invalid answers");
|
|
136
|
+
}
|
|
137
|
+
function yamlScalar(value) {
|
|
138
|
+
// Quote unless the string is a simple identifier-ish token. Conservative
|
|
139
|
+
// — when in doubt, JSON-encode (always parses as a YAML string).
|
|
140
|
+
if (/^[A-Za-z0-9_.\-/:]+$/.test(value))
|
|
141
|
+
return value;
|
|
142
|
+
return JSON.stringify(value);
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Prompt the user for the tool's required JSON Schema fields. Optional
|
|
146
|
+
* properties are skipped — the user can hand-edit the YAML afterwards
|
|
147
|
+
* if they want to set defaults. `into` is excluded since the generator
|
|
148
|
+
* supplies its own name.
|
|
149
|
+
*
|
|
150
|
+
* Returns an ordered list of `[key, value]` pairs so the YAML output is
|
|
151
|
+
* stable across runs (insertion order of object keys is sufficient in
|
|
152
|
+
* V8 for string keys, but an array makes the contract explicit).
|
|
153
|
+
*/
|
|
154
|
+
async function collectRequiredParams(deps, tool) {
|
|
155
|
+
const schema = tool.paramsSchema;
|
|
156
|
+
if (!schema || typeof schema !== "object")
|
|
157
|
+
return [];
|
|
158
|
+
const required = Array.isArray(schema.required)
|
|
159
|
+
? schema.required.filter((k) => typeof k === "string" && k !== "into")
|
|
160
|
+
: [];
|
|
161
|
+
if (required.length === 0)
|
|
162
|
+
return [];
|
|
163
|
+
const out = [];
|
|
164
|
+
const properties = schema.properties ?? {};
|
|
165
|
+
for (const key of required) {
|
|
166
|
+
const propMeta = properties[key];
|
|
167
|
+
const hint = propMeta?.description ? ` — ${propMeta.description}` : "";
|
|
168
|
+
const value = await askWithValidation(deps, `${tool.id} → ${key}${hint}`, (a) => (a.trim().length > 0 ? null : "required, cannot be empty."));
|
|
169
|
+
out.push([key, value.trim()]);
|
|
170
|
+
}
|
|
171
|
+
return out;
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* AI-suggest path. Discovers the running bridge via lock file, POSTs
|
|
175
|
+
* the user's natural-language goal to `/recipes/generate`, writes the
|
|
176
|
+
* bridge's raw YAML response to disk (no form normalization — per
|
|
177
|
+
* memory project_recipe_audit_2026_05_06_part2), and validates
|
|
178
|
+
* post-hoc as warnings only.
|
|
179
|
+
*
|
|
180
|
+
* Failure modes:
|
|
181
|
+
* - No bridge running → clear actionable error
|
|
182
|
+
* - Bridge returns 503 → "AI generation unavailable — start the
|
|
183
|
+
* bridge with --driver subprocess"
|
|
184
|
+
* - Bridge returns 4xx → surface server error message
|
|
185
|
+
* - Bridge returns 200 but `result.yaml` is empty → write nothing,
|
|
186
|
+
* throw with the raw refusal text
|
|
187
|
+
*/
|
|
188
|
+
async function runNewAiSuggest(options) {
|
|
189
|
+
const { deps } = options;
|
|
190
|
+
const name = await askWithValidation(deps, "Recipe name", (a) => RECIPE_NAME_RE.test(a)
|
|
191
|
+
? null
|
|
192
|
+
: 'must match /^[a-z0-9][a-z0-9_-]{1,63}$/ (e.g. "morning-brief").');
|
|
193
|
+
const goal = await askWithValidation(deps, "Describe what the recipe should do (one-paragraph natural language)", (a) => (a.trim().length > 0 ? null : "goal cannot be empty."));
|
|
194
|
+
// Resolve bridge discovery + fetch — production defaults or test injection.
|
|
195
|
+
const findBridge = options.aiSuggest?.findBridge ??
|
|
196
|
+
(() => {
|
|
197
|
+
// Lazy import so non-AI paths don't pay the cost.
|
|
198
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
199
|
+
const mod = require("../bridgeLockDiscovery.js");
|
|
200
|
+
return mod.findBridgeLock();
|
|
201
|
+
});
|
|
202
|
+
const doFetch = options.aiSuggest?.fetch ?? globalThis.fetch;
|
|
203
|
+
const lock = findBridge();
|
|
204
|
+
if (!lock) {
|
|
205
|
+
throw new Error("AI generation requires a running bridge. Start one with `patchwork start` " +
|
|
206
|
+
"(or `claude-ide-bridge --driver subprocess` for the bridge-only mode), " +
|
|
207
|
+
"then retry.");
|
|
208
|
+
}
|
|
209
|
+
let response;
|
|
210
|
+
try {
|
|
211
|
+
response = await doFetch(`http://127.0.0.1:${lock.port}/recipes/generate`, {
|
|
212
|
+
method: "POST",
|
|
213
|
+
headers: {
|
|
214
|
+
"Content-Type": "application/json",
|
|
215
|
+
Authorization: `Bearer ${lock.authToken}`,
|
|
216
|
+
},
|
|
217
|
+
body: JSON.stringify({ prompt: goal.trim() }),
|
|
218
|
+
});
|
|
219
|
+
}
|
|
220
|
+
catch (err) {
|
|
221
|
+
throw new Error(`Could not reach the bridge at port ${lock.port}: ${err instanceof Error ? err.message : String(err)}`);
|
|
222
|
+
}
|
|
223
|
+
let payload;
|
|
224
|
+
try {
|
|
225
|
+
payload = (await response.json());
|
|
226
|
+
}
|
|
227
|
+
catch {
|
|
228
|
+
throw new Error(`Bridge returned non-JSON response (status ${response.status})`);
|
|
229
|
+
}
|
|
230
|
+
if (response.status === 503 || payload.unavailable) {
|
|
231
|
+
throw new Error("AI generation unavailable — start the bridge with `--driver subprocess`.");
|
|
232
|
+
}
|
|
233
|
+
if (!payload.ok) {
|
|
234
|
+
throw new Error(payload.error ?? `Bridge returned ${response.status}`);
|
|
235
|
+
}
|
|
236
|
+
const yamlBody = (payload.yaml ?? "").trim();
|
|
237
|
+
if (!yamlBody) {
|
|
238
|
+
throw new Error("Bridge returned empty YAML — try a more specific goal.");
|
|
239
|
+
}
|
|
240
|
+
// Ensure the SchemaStore pragma is present at line 1 so the file
|
|
241
|
+
// gets autocomplete on first open. If the bridge already prepended
|
|
242
|
+
// one, leave it.
|
|
243
|
+
const hasPragma = /^#\s*yaml-language-server:/.test(yamlBody);
|
|
244
|
+
const content = hasPragma
|
|
245
|
+
? `${yamlBody}\n`
|
|
246
|
+
: `${RECIPE_SCHEMA_HEADER}\n${yamlBody}\n`;
|
|
247
|
+
if (deps.preview)
|
|
248
|
+
deps.preview(content);
|
|
249
|
+
const shouldWrite = await deps.confirm("Write to disk?");
|
|
250
|
+
if (!shouldWrite) {
|
|
251
|
+
throw new Error("Cancelled by user");
|
|
252
|
+
}
|
|
253
|
+
const outputDir = options.outputDir ?? RECIPES_DIR;
|
|
254
|
+
if (!existsSync(outputDir)) {
|
|
255
|
+
mkdirSync(outputDir, { recursive: true });
|
|
256
|
+
}
|
|
257
|
+
const outputPath = join(outputDir, `${name}.yaml`);
|
|
258
|
+
if (existsSync(outputPath)) {
|
|
259
|
+
throw new Error(`Recipe already exists: ${outputPath}`);
|
|
260
|
+
}
|
|
261
|
+
writeFileSync(outputPath, content);
|
|
262
|
+
// Lint post-hoc. Surface as warnings; never block — the user asked
|
|
263
|
+
// for a draft, the bridge produced it, the file is on disk.
|
|
264
|
+
let warnings = [];
|
|
265
|
+
try {
|
|
266
|
+
const parsed = parseYaml(content);
|
|
267
|
+
const lintResult = validateRecipeDefinition(parsed);
|
|
268
|
+
warnings = lintResult.issues ?? [];
|
|
269
|
+
}
|
|
270
|
+
catch (err) {
|
|
271
|
+
warnings = [
|
|
272
|
+
{
|
|
273
|
+
level: "warning",
|
|
274
|
+
message: `AI-generated YAML did not parse cleanly: ${err instanceof Error ? err.message : String(err)}`,
|
|
275
|
+
},
|
|
276
|
+
];
|
|
277
|
+
}
|
|
278
|
+
return { path: outputPath, content, warnings };
|
|
279
|
+
}
|
|
280
|
+
export async function runNewInteractive(options) {
|
|
281
|
+
const { deps } = options;
|
|
282
|
+
// Mode pick — Guided (full prompt tree), Template (existing runNew
|
|
283
|
+
// templates: minimal | daily | inbox), or AI-suggest (asks the running
|
|
284
|
+
// bridge's /recipes/generate endpoint and writes the raw response).
|
|
285
|
+
//
|
|
286
|
+
// AI-suggest skips the form normalizer per memory
|
|
287
|
+
// project_recipe_audit_2026_05_06_part2 — applyAiYaml on the dashboard
|
|
288
|
+
// was lossy. Here we writeFileSync the bridge's `result.yaml` verbatim
|
|
289
|
+
// (only the SchemaStore pragma is prepended if absent) and validate
|
|
290
|
+
// post-hoc as warnings, never blocking the write.
|
|
291
|
+
const templates = listTemplates();
|
|
292
|
+
const modeChoices = [
|
|
293
|
+
"Guided — full prompt tree (recommended)",
|
|
294
|
+
`Template — pick from ${templates.length} starters (${templates.join(", ")})`,
|
|
295
|
+
"AI suggest — describe what you want; the bridge drafts a YAML",
|
|
296
|
+
];
|
|
297
|
+
const modeIdx = await deps.pickFromList("How do you want to start?", modeChoices);
|
|
298
|
+
// AI-suggest mode: route through the running bridge's /recipes/generate.
|
|
299
|
+
if (modeIdx === 3) {
|
|
300
|
+
return runNewAiSuggest(options);
|
|
301
|
+
}
|
|
302
|
+
// Template mode: re-use the existing runNew() flow with the picked template.
|
|
303
|
+
if (modeIdx === 2) {
|
|
304
|
+
const name = await askWithValidation(deps, "Recipe name", (a) => RECIPE_NAME_RE.test(a)
|
|
305
|
+
? null
|
|
306
|
+
: 'must match /^[a-z0-9][a-z0-9_-]{1,63}$/ (e.g. "morning-brief").');
|
|
307
|
+
const description = await askWithValidation(deps, "One-line description", (a) => (a.trim().length > 0 ? null : "cannot be empty."));
|
|
308
|
+
const templateIdx = await deps.pickFromList("Pick a template", templates);
|
|
309
|
+
const template = templates[templateIdx - 1];
|
|
310
|
+
if (!template)
|
|
311
|
+
throw new Error("Invalid template selection");
|
|
312
|
+
const result = runNew({
|
|
313
|
+
name,
|
|
314
|
+
description,
|
|
315
|
+
template,
|
|
316
|
+
...(options.outputDir ? { outputDir: options.outputDir } : {}),
|
|
317
|
+
});
|
|
318
|
+
if (deps.preview)
|
|
319
|
+
deps.preview(result.content);
|
|
320
|
+
const shouldWrite = await deps.confirm("Keep this recipe?");
|
|
321
|
+
if (!shouldWrite) {
|
|
322
|
+
// runNew already wrote the file — clean up to honor "cancel."
|
|
323
|
+
try {
|
|
324
|
+
if (existsSync(result.path)) {
|
|
325
|
+
const { unlinkSync } = await import("node:fs");
|
|
326
|
+
unlinkSync(result.path);
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
catch {
|
|
330
|
+
// best effort
|
|
331
|
+
}
|
|
332
|
+
throw new Error("Cancelled by user");
|
|
333
|
+
}
|
|
334
|
+
return { path: result.path, content: result.content, warnings: [] };
|
|
335
|
+
}
|
|
336
|
+
// Guided mode (default — modeIdx === 1).
|
|
337
|
+
const name = await askWithValidation(deps, "Recipe name", (a) => RECIPE_NAME_RE.test(a)
|
|
338
|
+
? null
|
|
339
|
+
: 'must match /^[a-z0-9][a-z0-9_-]{1,63}$/ (e.g. "morning-brief").');
|
|
340
|
+
const description = await askWithValidation(deps, "One-line description", (a) => (a.trim().length > 0 ? null : "cannot be empty."));
|
|
341
|
+
const triggerTypes = ["manual", "cron"];
|
|
342
|
+
const triggerIdx = await deps.pickFromList("Trigger type", triggerTypes.slice());
|
|
343
|
+
const triggerType = triggerTypes[triggerIdx - 1];
|
|
344
|
+
if (!triggerType)
|
|
345
|
+
throw new Error("Invalid trigger selection");
|
|
346
|
+
const triggerLines = [`trigger:`, ` type: ${triggerType}`];
|
|
347
|
+
if (triggerType === "cron") {
|
|
348
|
+
const cron = await askWithValidation(deps, "Cron expression (e.g. '0 8 * * *' for 8am daily)", (a) => CRON_SHAPE_RE.test(a.trim())
|
|
349
|
+
? null
|
|
350
|
+
: 'expected 5-field cron or "@every Ns|Nm|Nh".');
|
|
351
|
+
triggerLines.push(` at: ${yamlScalar(cron.trim())}`);
|
|
352
|
+
}
|
|
353
|
+
// Step loop. The user adds tool / agent steps until they pick "done".
|
|
354
|
+
// Each tool pick reads its paramsSchema and prompts for required fields,
|
|
355
|
+
// so the generated recipe is closer to runnable than the MVP slice.
|
|
356
|
+
const namespaces = listConnectorNamespaces();
|
|
357
|
+
const stepLines = ["steps:"];
|
|
358
|
+
const intoNames = [];
|
|
359
|
+
let lastAgentInto = null;
|
|
360
|
+
let toolStepCount = 0;
|
|
361
|
+
let agentStepCount = 0;
|
|
362
|
+
const stepKindChoices = [
|
|
363
|
+
"Add a tool step (calls a registered tool — gmail.fetch_unread, github.list_issues, …)",
|
|
364
|
+
"Add an agent step (LLM prompt — drafts, classifies, summarizes)",
|
|
365
|
+
"Done — preview and write",
|
|
366
|
+
];
|
|
367
|
+
for (let stepIdx = 0; stepIdx < 20; stepIdx++) {
|
|
368
|
+
const kind = await deps.pickFromList(stepIdx === 0
|
|
369
|
+
? "Build steps — what's next?"
|
|
370
|
+
: "Add another step? (or pick Done)", stepKindChoices);
|
|
371
|
+
if (kind === 3)
|
|
372
|
+
break;
|
|
373
|
+
if (kind === 1) {
|
|
374
|
+
// Tool step
|
|
375
|
+
const nsIdx = await deps.pickFromList("Pick a connector", namespaces.slice());
|
|
376
|
+
const ns = namespaces[nsIdx - 1];
|
|
377
|
+
if (!ns)
|
|
378
|
+
throw new Error("Invalid connector selection");
|
|
379
|
+
const tools = listTools(ns);
|
|
380
|
+
const labels = tools.map((t) => `${t.id} — ${t.description}`);
|
|
381
|
+
const toolIdx = await deps.pickFromList(`Pick a ${ns} tool`, labels);
|
|
382
|
+
const tool = tools[toolIdx - 1];
|
|
383
|
+
if (!tool)
|
|
384
|
+
throw new Error(`Invalid tool selection for ${ns}`);
|
|
385
|
+
toolStepCount += 1;
|
|
386
|
+
const intoName = toolStepCount === 1 ? `${ns}_result` : `${ns}_result_${toolStepCount}`;
|
|
387
|
+
intoNames.push(intoName);
|
|
388
|
+
stepLines.push(` - tool: ${tool.id}`);
|
|
389
|
+
// Prompt for required params from the tool's JSON schema.
|
|
390
|
+
const params = await collectRequiredParams(deps, tool);
|
|
391
|
+
for (const [key, value] of params) {
|
|
392
|
+
stepLines.push(` ${key}: ${yamlScalar(value)}`);
|
|
393
|
+
}
|
|
394
|
+
stepLines.push(` into: ${intoName}`);
|
|
395
|
+
}
|
|
396
|
+
else if (kind === 2) {
|
|
397
|
+
// Agent step
|
|
398
|
+
const prompt = await askWithValidation(deps, "Agent prompt", (a) => a.trim().length > 0 ? null : "cannot be empty.");
|
|
399
|
+
agentStepCount += 1;
|
|
400
|
+
const intoName = agentStepCount === 1
|
|
401
|
+
? "agent_output"
|
|
402
|
+
: `agent_output_${agentStepCount}`;
|
|
403
|
+
lastAgentInto = intoName;
|
|
404
|
+
stepLines.push(` - agent: true`);
|
|
405
|
+
stepLines.push(` prompt: |`);
|
|
406
|
+
for (const line of prompt.split("\n")) {
|
|
407
|
+
stepLines.push(` ${line}`);
|
|
408
|
+
}
|
|
409
|
+
stepLines.push(` into: ${intoName}`);
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
// Always tail with file.write to inbox so the user sees output somewhere.
|
|
413
|
+
// Reference the most recent agent output when one exists, otherwise fall
|
|
414
|
+
// back to the most recent tool result (or a TODO comment if neither).
|
|
415
|
+
const tailRef = lastAgentInto ?? intoNames[intoNames.length - 1] ?? null;
|
|
416
|
+
stepLines.push(` - tool: file.write`);
|
|
417
|
+
stepLines.push(` path: "~/.patchwork/inbox/${name}-{{date}}.md"`);
|
|
418
|
+
stepLines.push(` content: |`);
|
|
419
|
+
stepLines.push(` # ${name} — {{date}}`);
|
|
420
|
+
stepLines.push(``);
|
|
421
|
+
if (tailRef) {
|
|
422
|
+
stepLines.push(` {{${tailRef}}}`);
|
|
423
|
+
}
|
|
424
|
+
else {
|
|
425
|
+
stepLines.push(` (no steps configured — fill in)`);
|
|
426
|
+
}
|
|
427
|
+
const content = `${RECIPE_SCHEMA_HEADER}\n` +
|
|
428
|
+
`version: 1.0.0\n` +
|
|
429
|
+
`name: ${name}\n` +
|
|
430
|
+
`description: ${yamlScalar(description)}\n` +
|
|
431
|
+
`${triggerLines.join("\n")}\n` +
|
|
432
|
+
`${stepLines.join("\n")}\n`;
|
|
433
|
+
// Lint pre-write. validateRecipeDefinition expects parsed YAML.
|
|
434
|
+
let warnings = [];
|
|
435
|
+
try {
|
|
436
|
+
const parsed = parseYaml(content);
|
|
437
|
+
const lintResult = validateRecipeDefinition(parsed);
|
|
438
|
+
warnings = lintResult.issues ?? [];
|
|
439
|
+
}
|
|
440
|
+
catch {
|
|
441
|
+
// Parse failure here is a bug in the generator — surface but don't block.
|
|
442
|
+
warnings = [
|
|
443
|
+
{
|
|
444
|
+
level: "error",
|
|
445
|
+
message: "Generated YAML failed to parse — please report this.",
|
|
446
|
+
},
|
|
447
|
+
];
|
|
448
|
+
}
|
|
449
|
+
if (deps.preview)
|
|
450
|
+
deps.preview(content);
|
|
451
|
+
const shouldWrite = await deps.confirm("Write to disk?");
|
|
452
|
+
if (!shouldWrite) {
|
|
453
|
+
throw new Error("Cancelled by user");
|
|
454
|
+
}
|
|
455
|
+
const outputDir = options.outputDir ?? RECIPES_DIR;
|
|
456
|
+
if (!existsSync(outputDir)) {
|
|
457
|
+
mkdirSync(outputDir, { recursive: true });
|
|
458
|
+
}
|
|
459
|
+
const outputPath = join(outputDir, `${name}.yaml`);
|
|
460
|
+
if (existsSync(outputPath)) {
|
|
461
|
+
throw new Error(`Recipe already exists: ${outputPath}`);
|
|
462
|
+
}
|
|
463
|
+
writeFileSync(outputPath, content);
|
|
464
|
+
return { path: outputPath, content, warnings };
|
|
465
|
+
}
|
|
116
466
|
export async function runSchema(outputDir) {
|
|
117
467
|
const resolvedOutputDir = resolve(outputDir);
|
|
118
468
|
const schemas = generateSchemaSet();
|
|
@@ -183,8 +533,12 @@ export function runLint(recipePath) {
|
|
|
183
533
|
const chainIssues = lintChainRefs(parsed, recipePath);
|
|
184
534
|
if (chainIssues.length > 0) {
|
|
185
535
|
result.issues.push(...chainIssues);
|
|
186
|
-
|
|
187
|
-
|
|
536
|
+
for (const i of chainIssues) {
|
|
537
|
+
if (i.level === "error")
|
|
538
|
+
result.errors++;
|
|
539
|
+
else if (i.level === "warning")
|
|
540
|
+
result.warnings++;
|
|
541
|
+
}
|
|
188
542
|
if (result.errors > 0) {
|
|
189
543
|
result.valid = false;
|
|
190
544
|
}
|
|
@@ -420,6 +774,8 @@ export async function runRecipe(recipeRef, options = {}) {
|
|
|
420
774
|
const runnerDeps = {
|
|
421
775
|
...options.deps,
|
|
422
776
|
workdir: options.workdir ?? options.deps?.workdir ?? process.cwd(),
|
|
777
|
+
...(options.manualRunId && { manualRunId: options.manualRunId }),
|
|
778
|
+
...(options.ledgerDir && { ledgerDir: options.ledgerDir }),
|
|
423
779
|
};
|
|
424
780
|
if (options.dryRun) {
|
|
425
781
|
throw new Error("runRecipeDryPlan must be used for dry-run execution");
|
|
@@ -832,7 +1188,16 @@ export async function runPreflight(recipeRef, options = {}) {
|
|
|
832
1188
|
}
|
|
833
1189
|
const plan = await runRecipeDryPlan(recipeRef, options);
|
|
834
1190
|
const requireWriteAck = options.requireWriteAck ?? true;
|
|
835
|
-
|
|
1191
|
+
// Merge the recipe's declared allowWrites (YAML) with any caller-supplied
|
|
1192
|
+
// entries (e.g. --allow-write CLI flag). Either source is sufficient.
|
|
1193
|
+
const recipeForWrites = loadYamlRecipe(recipePath);
|
|
1194
|
+
const recipeAllowWrites = Array.isArray(recipeForWrites.allowWrites)
|
|
1195
|
+
? recipeForWrites.allowWrites.filter((entry) => typeof entry === "string")
|
|
1196
|
+
: [];
|
|
1197
|
+
const allowlist = new Set([
|
|
1198
|
+
...recipeAllowWrites,
|
|
1199
|
+
...(options.allowWrites ?? []),
|
|
1200
|
+
]);
|
|
836
1201
|
for (const step of plan.steps) {
|
|
837
1202
|
if (step.type === "tool" && step.tool && step.resolved === false) {
|
|
838
1203
|
issues.push({
|
|
@@ -898,6 +1263,134 @@ export function runPreflightWatch(options) {
|
|
|
898
1263
|
};
|
|
899
1264
|
return runWatch(watchOptions);
|
|
900
1265
|
}
|
|
1266
|
+
/**
|
|
1267
|
+
* `recipe doctor` — one-screen "why is this recipe unhealthy + how do I
|
|
1268
|
+
* fix it" diagnosis. Composes the static `preflight` check (lint + policy
|
|
1269
|
+
* + plan) with the recipe-scoped runtime halt summary, mapping every
|
|
1270
|
+
* finding to an actionable hint. Fail-soft: a missing bridge degrades to
|
|
1271
|
+
* static-only rather than erroring.
|
|
1272
|
+
*/
|
|
1273
|
+
export async function runRecipeDoctor(recipeRef, options = {}) {
|
|
1274
|
+
const { fetchHalts, ...preflightOptions } = options;
|
|
1275
|
+
const recipePath = resolveRecipePath(recipeRef);
|
|
1276
|
+
// Lint first — it never throws and returns structured issues even for
|
|
1277
|
+
// recipes that can't load. Then attempt the fuller preflight (policy +
|
|
1278
|
+
// plan); if the recipe is too broken to load/plan, `runPreflight`
|
|
1279
|
+
// throws — fall back to a lint-only static result so doctor still
|
|
1280
|
+
// produces a clean diagnosis instead of a stack trace.
|
|
1281
|
+
const lint = runLint(recipePath);
|
|
1282
|
+
let staticResult;
|
|
1283
|
+
try {
|
|
1284
|
+
const pf = await runPreflight(recipePath, preflightOptions);
|
|
1285
|
+
staticResult = { ok: pf.ok, recipe: pf.recipe, issues: pf.issues };
|
|
1286
|
+
}
|
|
1287
|
+
catch (err) {
|
|
1288
|
+
const issues = lint.issues.map((i) => ({
|
|
1289
|
+
level: i.level,
|
|
1290
|
+
code: i.level === "error" ? "lint-error" : "lint-warning",
|
|
1291
|
+
message: i.message,
|
|
1292
|
+
}));
|
|
1293
|
+
// Guarantee at least one error so the verdict is unhealthy — the
|
|
1294
|
+
// load failure itself is the signal even if lint surfaced only
|
|
1295
|
+
// warnings (or nothing it could attribute to a line).
|
|
1296
|
+
if (!issues.some((i) => i.level === "error")) {
|
|
1297
|
+
issues.push({
|
|
1298
|
+
level: "error",
|
|
1299
|
+
code: "lint-error",
|
|
1300
|
+
message: err instanceof Error ? err.message : String(err),
|
|
1301
|
+
});
|
|
1302
|
+
}
|
|
1303
|
+
staticResult = {
|
|
1304
|
+
ok: false,
|
|
1305
|
+
recipe: basename(recipePath).replace(/\.(ya?ml|json)$/i, ""),
|
|
1306
|
+
issues,
|
|
1307
|
+
planSkipped: true,
|
|
1308
|
+
};
|
|
1309
|
+
}
|
|
1310
|
+
const recipeName = staticResult.recipe;
|
|
1311
|
+
let runtime = null;
|
|
1312
|
+
let runtimeNote;
|
|
1313
|
+
if (fetchHalts) {
|
|
1314
|
+
try {
|
|
1315
|
+
runtime = await fetchHalts(recipeName);
|
|
1316
|
+
if (runtime === null) {
|
|
1317
|
+
runtimeNote = "no running bridge — runtime halt check skipped";
|
|
1318
|
+
}
|
|
1319
|
+
}
|
|
1320
|
+
catch (err) {
|
|
1321
|
+
runtime = null;
|
|
1322
|
+
runtimeNote = `runtime halt check failed: ${err instanceof Error ? err.message : String(err)}`;
|
|
1323
|
+
}
|
|
1324
|
+
}
|
|
1325
|
+
else {
|
|
1326
|
+
runtimeNote = "runtime halt check not requested";
|
|
1327
|
+
}
|
|
1328
|
+
const ok = staticResult.ok && (runtime?.total ?? 0) === 0;
|
|
1329
|
+
return {
|
|
1330
|
+
recipe: recipeName,
|
|
1331
|
+
recipePath,
|
|
1332
|
+
static: staticResult,
|
|
1333
|
+
runtime,
|
|
1334
|
+
runtimeNote,
|
|
1335
|
+
ok,
|
|
1336
|
+
};
|
|
1337
|
+
}
|
|
1338
|
+
/**
|
|
1339
|
+
* Render a `RecipeDoctorResult` as a one-screen human report. Returns the
|
|
1340
|
+
* text so the CLI handler can write it (and tests can assert on it)
|
|
1341
|
+
* without coupling to process.stdout.
|
|
1342
|
+
*/
|
|
1343
|
+
export function formatRecipeDoctorReport(result) {
|
|
1344
|
+
const lines = [];
|
|
1345
|
+
const verdict = result.ok ? "✓ healthy" : "✗ needs attention";
|
|
1346
|
+
lines.push(`Recipe doctor — ${result.recipe} (${verdict})`);
|
|
1347
|
+
lines.push(` ${result.recipePath}`);
|
|
1348
|
+
lines.push("");
|
|
1349
|
+
// --- Static: lint + policy + plan ---
|
|
1350
|
+
const s = result.static;
|
|
1351
|
+
const staticErrors = s.issues.filter((i) => i.level === "error");
|
|
1352
|
+
const staticWarnings = s.issues.filter((i) => i.level === "warning");
|
|
1353
|
+
if (s.issues.length === 0) {
|
|
1354
|
+
lines.push("Static checks: ✓ lint + policy clean");
|
|
1355
|
+
}
|
|
1356
|
+
else {
|
|
1357
|
+
const planNote = s.planSkipped
|
|
1358
|
+
? " (could not plan — lint-only; fix the errors below first)"
|
|
1359
|
+
: "";
|
|
1360
|
+
lines.push(`Static checks: ${staticErrors.length} error(s), ${staticWarnings.length} warning(s)${planNote}`);
|
|
1361
|
+
for (const issue of s.issues) {
|
|
1362
|
+
const icon = issue.level === "error" ? "✗" : "⚠";
|
|
1363
|
+
const where = issue.stepId ? ` [step "${issue.stepId}"]` : "";
|
|
1364
|
+
lines.push(` ${icon} (${issue.code})${where} ${issue.message}`);
|
|
1365
|
+
}
|
|
1366
|
+
}
|
|
1367
|
+
lines.push("");
|
|
1368
|
+
// --- Runtime: recent halts ---
|
|
1369
|
+
if (result.runtime === null) {
|
|
1370
|
+
lines.push(`Runtime halts: — (${result.runtimeNote ?? "unavailable"})`);
|
|
1371
|
+
}
|
|
1372
|
+
else if (result.runtime.total === 0) {
|
|
1373
|
+
lines.push("Runtime halts: ✓ none in the recent window");
|
|
1374
|
+
}
|
|
1375
|
+
else {
|
|
1376
|
+
lines.push(`Runtime halts: ${result.runtime.total} in the recent window`);
|
|
1377
|
+
const entries = Object.entries(result.runtime.byCategory).sort(([, a], [, b]) => (b ?? 0) - (a ?? 0));
|
|
1378
|
+
for (const [cat, count] of entries) {
|
|
1379
|
+
const category = cat;
|
|
1380
|
+
const label = HALT_CATEGORY_LABELS[category] ?? cat;
|
|
1381
|
+
const hint = HALT_CATEGORY_HINTS[category] ?? "open run trace";
|
|
1382
|
+
lines.push(` ✗ ${label}: ${count} → ${hint}`);
|
|
1383
|
+
}
|
|
1384
|
+
if (result.runtime.recent.length > 0) {
|
|
1385
|
+
lines.push(" most recent:");
|
|
1386
|
+
for (const r of result.runtime.recent.slice(0, 5)) {
|
|
1387
|
+
const reason = r.reason.length > 100 ? `${r.reason.slice(0, 99)}…` : r.reason;
|
|
1388
|
+
lines.push(` · [run #${r.runSeq}] ${reason}`);
|
|
1389
|
+
}
|
|
1390
|
+
}
|
|
1391
|
+
}
|
|
1392
|
+
return `${lines.join("\n")}\n`;
|
|
1393
|
+
}
|
|
901
1394
|
/**
|
|
902
1395
|
* Exported for the cli-warns-when-out-of-jail.test regression suite —
|
|
903
1396
|
* the warn path is what we assert on, not the path-resolution behaviour.
|
|
@@ -1088,8 +1581,13 @@ export async function runRecord(recipePath, options = {}) {
|
|
|
1088
1581
|
});
|
|
1089
1582
|
}
|
|
1090
1583
|
}
|
|
1091
|
-
|
|
1092
|
-
const
|
|
1584
|
+
let errors = 0, warnings = 0;
|
|
1585
|
+
for (const issue of issues) {
|
|
1586
|
+
if (issue.level === "error")
|
|
1587
|
+
errors++;
|
|
1588
|
+
else if (issue.level === "warning")
|
|
1589
|
+
warnings++;
|
|
1590
|
+
}
|
|
1093
1591
|
return {
|
|
1094
1592
|
valid: errors === 0,
|
|
1095
1593
|
issues,
|
|
@@ -1200,8 +1698,13 @@ export async function runTest(recipePath, options = {}) {
|
|
|
1200
1698
|
});
|
|
1201
1699
|
}
|
|
1202
1700
|
}
|
|
1203
|
-
|
|
1204
|
-
const
|
|
1701
|
+
let errors = 0, warnings = 0;
|
|
1702
|
+
for (const issue of issues) {
|
|
1703
|
+
if (issue.level === "error")
|
|
1704
|
+
errors++;
|
|
1705
|
+
else if (issue.level === "warning")
|
|
1706
|
+
warnings++;
|
|
1707
|
+
}
|
|
1205
1708
|
return {
|
|
1206
1709
|
valid: errors === 0,
|
|
1207
1710
|
issues,
|