patchwork-os 0.2.0-alpha.9 → 0.2.0-beta.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/README.bridge.md +6 -0
- package/README.md +315 -35
- package/deploy/bootstrap-new-vps.sh +12 -12
- package/deploy/bootstrap-vps.sh +187 -0
- package/deploy/deploy-dashboard.sh +174 -0
- package/deploy/deploy-landing.sh +136 -0
- package/dist/activationMetrics.d.ts +67 -0
- package/dist/activationMetrics.js +255 -0
- package/dist/activationMetrics.js.map +1 -0
- package/dist/activityLog.d.ts +49 -0
- package/dist/activityLog.js +78 -0
- package/dist/activityLog.js.map +1 -1
- package/dist/approvalHttp.d.ts +49 -2
- package/dist/approvalHttp.js +217 -21
- package/dist/approvalHttp.js.map +1 -1
- package/dist/approvalInsights.d.ts +49 -0
- package/dist/approvalInsights.js +97 -0
- package/dist/approvalInsights.js.map +1 -0
- package/dist/approvalQueue.d.ts +27 -1
- package/dist/approvalQueue.js +123 -3
- package/dist/approvalQueue.js.map +1 -1
- package/dist/approvalSignals.d.ts +124 -0
- package/dist/approvalSignals.js +512 -0
- package/dist/approvalSignals.js.map +1 -0
- package/dist/automation.d.ts +57 -0
- package/dist/automation.js +156 -59
- package/dist/automation.js.map +1 -1
- package/dist/automationSuggestions.d.ts +79 -0
- package/dist/automationSuggestions.js +150 -0
- package/dist/automationSuggestions.js.map +1 -0
- package/dist/bridge.d.ts +3 -0
- package/dist/bridge.js +174 -143
- package/dist/bridge.js.map +1 -1
- package/dist/bridgeToken.js +57 -19
- package/dist/bridgeToken.js.map +1 -1
- package/dist/ccPermissions.d.ts +15 -0
- package/dist/ccPermissions.js +21 -4
- package/dist/ccPermissions.js.map +1 -1
- package/dist/claudeDriver.js +74 -16
- package/dist/claudeDriver.js.map +1 -1
- package/dist/claudeOrchestrator.d.ts +1 -1
- package/dist/claudeOrchestrator.js +14 -8
- package/dist/claudeOrchestrator.js.map +1 -1
- package/dist/commands/dashboard.js +1 -1
- package/dist/commands/dashboard.js.map +1 -1
- package/dist/commands/launchd.d.ts +2 -0
- package/dist/commands/launchd.js +94 -0
- package/dist/commands/launchd.js.map +1 -0
- package/dist/commands/patchworkInit.d.ts +8 -0
- package/dist/commands/patchworkInit.js +77 -11
- package/dist/commands/patchworkInit.js.map +1 -1
- package/dist/commands/recipe.d.ts +289 -0
- package/dist/commands/recipe.js +1359 -0
- package/dist/commands/recipe.js.map +1 -0
- package/dist/commands/recipeInstall.d.ts +150 -0
- package/dist/commands/recipeInstall.js +647 -0
- package/dist/commands/recipeInstall.js.map +1 -0
- package/dist/commands/tracesExport.d.ts +83 -0
- package/dist/commands/tracesExport.js +269 -0
- package/dist/commands/tracesExport.js.map +1 -0
- package/dist/commands/tracesImport.d.ts +56 -0
- package/dist/commands/tracesImport.js +161 -0
- package/dist/commands/tracesImport.js.map +1 -0
- package/dist/config.d.ts +22 -1
- package/dist/config.js +108 -9
- package/dist/config.js.map +1 -1
- package/dist/connectorRoutes.d.ts +43 -0
- package/dist/connectorRoutes.js +1609 -0
- package/dist/connectorRoutes.js.map +1 -0
- package/dist/connectors/asana.d.ts +198 -0
- package/dist/connectors/asana.js +679 -0
- package/dist/connectors/asana.js.map +1 -0
- package/dist/connectors/baseConnector.d.ts +153 -0
- package/dist/connectors/baseConnector.js +336 -0
- package/dist/connectors/baseConnector.js.map +1 -0
- package/dist/connectors/confluence.d.ts +111 -0
- package/dist/connectors/confluence.js +406 -0
- package/dist/connectors/confluence.js.map +1 -0
- package/dist/connectors/datadog.d.ts +116 -0
- package/dist/connectors/datadog.js +385 -0
- package/dist/connectors/datadog.js.map +1 -0
- package/dist/connectors/discord.d.ts +150 -0
- package/dist/connectors/discord.js +543 -0
- package/dist/connectors/discord.js.map +1 -0
- package/dist/connectors/fixtureLibrary.d.ts +21 -0
- package/dist/connectors/fixtureLibrary.js +70 -0
- package/dist/connectors/fixtureLibrary.js.map +1 -0
- package/dist/connectors/fixtureRecorder.d.ts +1 -0
- package/dist/connectors/fixtureRecorder.js +35 -0
- package/dist/connectors/fixtureRecorder.js.map +1 -0
- package/dist/connectors/github.js +17 -18
- package/dist/connectors/github.js.map +1 -1
- package/dist/connectors/gitlab.d.ts +180 -0
- package/dist/connectors/gitlab.js +582 -0
- package/dist/connectors/gitlab.js.map +1 -0
- package/dist/connectors/gmail.d.ts +4 -1
- package/dist/connectors/gmail.js +149 -27
- package/dist/connectors/gmail.js.map +1 -1
- package/dist/connectors/googleCalendar.d.ts +4 -1
- package/dist/connectors/googleCalendar.js +88 -25
- package/dist/connectors/googleCalendar.js.map +1 -1
- package/dist/connectors/googleDrive.d.ts +34 -0
- package/dist/connectors/googleDrive.js +321 -0
- package/dist/connectors/googleDrive.js.map +1 -0
- package/dist/connectors/htmlEscape.d.ts +5 -0
- package/dist/connectors/htmlEscape.js +13 -0
- package/dist/connectors/htmlEscape.js.map +1 -0
- package/dist/connectors/hubspot.d.ts +112 -0
- package/dist/connectors/hubspot.js +408 -0
- package/dist/connectors/hubspot.js.map +1 -0
- package/dist/connectors/intercom.d.ts +102 -0
- package/dist/connectors/intercom.js +402 -0
- package/dist/connectors/intercom.js.map +1 -0
- package/dist/connectors/jira.d.ts +98 -0
- package/dist/connectors/jira.js +379 -0
- package/dist/connectors/jira.js.map +1 -0
- package/dist/connectors/linear.js +30 -19
- package/dist/connectors/linear.js.map +1 -1
- package/dist/connectors/mcpOAuth.d.ts +3 -0
- package/dist/connectors/mcpOAuth.js +64 -10
- package/dist/connectors/mcpOAuth.js.map +1 -1
- package/dist/connectors/mockConnector.d.ts +28 -0
- package/dist/connectors/mockConnector.js +81 -0
- package/dist/connectors/mockConnector.js.map +1 -0
- package/dist/connectors/notion.d.ts +143 -0
- package/dist/connectors/notion.js +424 -0
- package/dist/connectors/notion.js.map +1 -0
- package/dist/connectors/oauthStateStore.d.ts +31 -0
- package/dist/connectors/oauthStateStore.js +52 -0
- package/dist/connectors/oauthStateStore.js.map +1 -0
- package/dist/connectors/pagerduty.d.ts +160 -0
- package/dist/connectors/pagerduty.js +464 -0
- package/dist/connectors/pagerduty.js.map +1 -0
- package/dist/connectors/sentry.js +5 -13
- package/dist/connectors/sentry.js.map +1 -1
- package/dist/connectors/slack.d.ts +16 -1
- package/dist/connectors/slack.js +155 -32
- package/dist/connectors/slack.js.map +1 -1
- package/dist/connectors/stripe.d.ts +116 -0
- package/dist/connectors/stripe.js +379 -0
- package/dist/connectors/stripe.js.map +1 -0
- package/dist/connectors/tokenStorage.d.ts +35 -0
- package/dist/connectors/tokenStorage.js +484 -0
- package/dist/connectors/tokenStorage.js.map +1 -0
- package/dist/connectors/zendesk.d.ts +104 -0
- package/dist/connectors/zendesk.js +442 -0
- package/dist/connectors/zendesk.js.map +1 -0
- package/dist/cors.d.ts +10 -0
- package/dist/cors.js +29 -0
- package/dist/cors.js.map +1 -0
- package/dist/decisionReplay.d.ts +72 -0
- package/dist/decisionReplay.js +92 -0
- package/dist/decisionReplay.js.map +1 -0
- package/dist/decisionTraceLog.d.ts +6 -0
- package/dist/decisionTraceLog.js +54 -2
- package/dist/decisionTraceLog.js.map +1 -1
- package/dist/drivers/gemini/index.d.ts +5 -1
- package/dist/drivers/gemini/index.js +39 -5
- package/dist/drivers/gemini/index.js.map +1 -1
- package/dist/drivers/index.d.ts +5 -0
- package/dist/drivers/index.js +1 -1
- package/dist/drivers/index.js.map +1 -1
- package/dist/featureFlags.d.ts +79 -0
- package/dist/featureFlags.js +208 -0
- package/dist/featureFlags.js.map +1 -0
- package/dist/fp/automationInterpreter.js +26 -21
- package/dist/fp/automationInterpreter.js.map +1 -1
- package/dist/fp/automationProgram.d.ts +1 -1
- package/dist/fp/automationProgram.js.map +1 -1
- package/dist/fp/automationState.js +4 -1
- package/dist/fp/automationState.js.map +1 -1
- package/dist/fp/policyParser.js +21 -1
- package/dist/fp/policyParser.js.map +1 -1
- package/dist/inboxRoutes.d.ts +22 -0
- package/dist/inboxRoutes.js +114 -0
- package/dist/inboxRoutes.js.map +1 -0
- package/dist/index.js +1400 -201
- package/dist/index.js.map +1 -1
- package/dist/installGuard.d.ts +25 -0
- package/dist/installGuard.js +48 -0
- package/dist/installGuard.js.map +1 -0
- package/dist/mcpRoutes.d.ts +37 -0
- package/dist/mcpRoutes.js +76 -0
- package/dist/mcpRoutes.js.map +1 -0
- package/dist/oauth.d.ts +7 -1
- package/dist/oauth.js +201 -39
- package/dist/oauth.js.map +1 -1
- package/dist/oauthRoutes.d.ts +32 -0
- package/dist/oauthRoutes.js +124 -0
- package/dist/oauthRoutes.js.map +1 -0
- package/dist/orchestrator/orchestratorBridge.js +2 -2
- package/dist/orchestrator/orchestratorBridge.js.map +1 -1
- package/dist/patchworkConfig.d.ts +16 -0
- package/dist/patchworkConfig.js +1 -1
- package/dist/patchworkConfig.js.map +1 -1
- package/dist/pluginLoader.d.ts +28 -0
- package/dist/pluginLoader.js +77 -11
- package/dist/pluginLoader.js.map +1 -1
- package/dist/pluginWatcher.js +8 -3
- package/dist/pluginWatcher.js.map +1 -1
- package/dist/preToolUseHook.d.ts +12 -0
- package/dist/preToolUseHook.js +23 -0
- package/dist/preToolUseHook.js.map +1 -1
- package/dist/recipeOrchestration.d.ts +121 -0
- package/dist/recipeOrchestration.js +955 -0
- package/dist/recipeOrchestration.js.map +1 -0
- package/dist/recipeRoutes.d.ts +180 -0
- package/dist/recipeRoutes.js +1345 -0
- package/dist/recipeRoutes.js.map +1 -0
- package/dist/recipes/RecipeOrchestrator.d.ts +40 -0
- package/dist/recipes/RecipeOrchestrator.js +51 -0
- package/dist/recipes/RecipeOrchestrator.js.map +1 -0
- package/dist/recipes/agentExecutor.d.ts +29 -0
- package/dist/recipes/agentExecutor.js +49 -0
- package/dist/recipes/agentExecutor.js.map +1 -0
- package/dist/recipes/chainedRunner.d.ts +191 -0
- package/dist/recipes/chainedRunner.js +759 -0
- package/dist/recipes/chainedRunner.js.map +1 -0
- package/dist/recipes/compiler.js +3 -3
- package/dist/recipes/compiler.js.map +1 -1
- package/dist/recipes/dependencyGraph.d.ts +39 -0
- package/dist/recipes/dependencyGraph.js +199 -0
- package/dist/recipes/dependencyGraph.js.map +1 -0
- package/dist/recipes/disabledMarkers.d.ts +48 -0
- package/dist/recipes/disabledMarkers.js +52 -0
- package/dist/recipes/disabledMarkers.js.map +1 -0
- package/dist/recipes/installer.js +3 -3
- package/dist/recipes/installer.js.map +1 -1
- package/dist/recipes/legacyRecipeCompat.d.ts +10 -0
- package/dist/recipes/legacyRecipeCompat.js +131 -0
- package/dist/recipes/legacyRecipeCompat.js.map +1 -0
- package/dist/recipes/manifest.d.ts +47 -0
- package/dist/recipes/manifest.js +156 -0
- package/dist/recipes/manifest.js.map +1 -0
- package/dist/recipes/migrationWarnings.d.ts +12 -0
- package/dist/recipes/migrationWarnings.js +44 -0
- package/dist/recipes/migrationWarnings.js.map +1 -0
- package/dist/recipes/migrations/index.d.ts +24 -0
- package/dist/recipes/migrations/index.js +55 -0
- package/dist/recipes/migrations/index.js.map +1 -0
- package/dist/recipes/migrations/types.d.ts +28 -0
- package/dist/recipes/migrations/types.js +2 -0
- package/dist/recipes/migrations/types.js.map +1 -0
- package/dist/recipes/migrations/v1.d.ts +11 -0
- package/dist/recipes/migrations/v1.js +18 -0
- package/dist/recipes/migrations/v1.js.map +1 -0
- package/dist/recipes/names.d.ts +40 -0
- package/dist/recipes/names.js +66 -0
- package/dist/recipes/names.js.map +1 -0
- package/dist/recipes/nestedRecipeStep.d.ts +58 -0
- package/dist/recipes/nestedRecipeStep.js +95 -0
- package/dist/recipes/nestedRecipeStep.js.map +1 -0
- package/dist/recipes/outputRegistry.d.ts +28 -0
- package/dist/recipes/outputRegistry.js +52 -0
- package/dist/recipes/outputRegistry.js.map +1 -0
- package/dist/recipes/parser.js +4 -1
- package/dist/recipes/parser.js.map +1 -1
- package/dist/recipes/replayRun.d.ts +62 -0
- package/dist/recipes/replayRun.js +97 -0
- package/dist/recipes/replayRun.js.map +1 -0
- package/dist/recipes/resolveRecipePath.d.ts +69 -0
- package/dist/recipes/resolveRecipePath.js +202 -0
- package/dist/recipes/resolveRecipePath.js.map +1 -0
- package/dist/recipes/scheduler.d.ts +23 -7
- package/dist/recipes/scheduler.js +225 -45
- package/dist/recipes/scheduler.js.map +1 -1
- package/dist/recipes/schema.d.ts +17 -2
- package/dist/recipes/schemaGenerator.d.ts +28 -0
- package/dist/recipes/schemaGenerator.js +565 -0
- package/dist/recipes/schemaGenerator.js.map +1 -0
- package/dist/recipes/stepObservation.d.ts +44 -0
- package/dist/recipes/stepObservation.js +232 -0
- package/dist/recipes/stepObservation.js.map +1 -0
- package/dist/recipes/templateEngine.d.ts +62 -0
- package/dist/recipes/templateEngine.js +201 -0
- package/dist/recipes/templateEngine.js.map +1 -0
- package/dist/recipes/toolRegistry.d.ts +186 -0
- package/dist/recipes/toolRegistry.js +309 -0
- package/dist/recipes/toolRegistry.js.map +1 -0
- package/dist/recipes/tools/asana.d.ts +16 -0
- package/dist/recipes/tools/asana.js +524 -0
- package/dist/recipes/tools/asana.js.map +1 -0
- package/dist/recipes/tools/calendar.d.ts +6 -0
- package/dist/recipes/tools/calendar.js +61 -0
- package/dist/recipes/tools/calendar.js.map +1 -0
- package/dist/recipes/tools/confluence.d.ts +6 -0
- package/dist/recipes/tools/confluence.js +254 -0
- package/dist/recipes/tools/confluence.js.map +1 -0
- package/dist/recipes/tools/datadog.d.ts +6 -0
- package/dist/recipes/tools/datadog.js +239 -0
- package/dist/recipes/tools/datadog.js.map +1 -0
- package/dist/recipes/tools/diagnostics.d.ts +6 -0
- package/dist/recipes/tools/diagnostics.js +36 -0
- package/dist/recipes/tools/diagnostics.js.map +1 -0
- package/dist/recipes/tools/discord.d.ts +18 -0
- package/dist/recipes/tools/discord.js +254 -0
- package/dist/recipes/tools/discord.js.map +1 -0
- package/dist/recipes/tools/file.d.ts +12 -0
- package/dist/recipes/tools/file.js +174 -0
- package/dist/recipes/tools/file.js.map +1 -0
- package/dist/recipes/tools/git.d.ts +6 -0
- package/dist/recipes/tools/git.js +63 -0
- package/dist/recipes/tools/git.js.map +1 -0
- package/dist/recipes/tools/github.d.ts +6 -0
- package/dist/recipes/tools/github.js +116 -0
- package/dist/recipes/tools/github.js.map +1 -0
- package/dist/recipes/tools/gitlab.d.ts +11 -0
- package/dist/recipes/tools/gitlab.js +285 -0
- package/dist/recipes/tools/gitlab.js.map +1 -0
- package/dist/recipes/tools/gmail.d.ts +6 -0
- package/dist/recipes/tools/gmail.js +434 -0
- package/dist/recipes/tools/gmail.js.map +1 -0
- package/dist/recipes/tools/googleDrive.d.ts +1 -0
- package/dist/recipes/tools/googleDrive.js +55 -0
- package/dist/recipes/tools/googleDrive.js.map +1 -0
- package/dist/recipes/tools/hubspot.d.ts +6 -0
- package/dist/recipes/tools/hubspot.js +232 -0
- package/dist/recipes/tools/hubspot.js.map +1 -0
- package/dist/recipes/tools/index.d.ts +30 -0
- package/dist/recipes/tools/index.js +33 -0
- package/dist/recipes/tools/index.js.map +1 -0
- package/dist/recipes/tools/intercom.d.ts +6 -0
- package/dist/recipes/tools/intercom.js +226 -0
- package/dist/recipes/tools/intercom.js.map +1 -0
- package/dist/recipes/tools/jira.d.ts +14 -0
- package/dist/recipes/tools/jira.js +369 -0
- package/dist/recipes/tools/jira.js.map +1 -0
- package/dist/recipes/tools/linear.d.ts +7 -0
- package/dist/recipes/tools/linear.js +307 -0
- package/dist/recipes/tools/linear.js.map +1 -0
- package/dist/recipes/tools/meetingNotes.d.ts +21 -0
- package/dist/recipes/tools/meetingNotes.js +701 -0
- package/dist/recipes/tools/meetingNotes.js.map +1 -0
- package/dist/recipes/tools/notion.d.ts +6 -0
- package/dist/recipes/tools/notion.js +278 -0
- package/dist/recipes/tools/notion.js.map +1 -0
- package/dist/recipes/tools/pagerduty.d.ts +15 -0
- package/dist/recipes/tools/pagerduty.js +451 -0
- package/dist/recipes/tools/pagerduty.js.map +1 -0
- package/dist/recipes/tools/sentry.d.ts +12 -0
- package/dist/recipes/tools/sentry.js +73 -0
- package/dist/recipes/tools/sentry.js.map +1 -0
- package/dist/recipes/tools/slack.d.ts +6 -0
- package/dist/recipes/tools/slack.js +82 -0
- package/dist/recipes/tools/slack.js.map +1 -0
- package/dist/recipes/tools/stripe.d.ts +6 -0
- package/dist/recipes/tools/stripe.js +265 -0
- package/dist/recipes/tools/stripe.js.map +1 -0
- package/dist/recipes/tools/zendesk.d.ts +6 -0
- package/dist/recipes/tools/zendesk.js +245 -0
- package/dist/recipes/tools/zendesk.js.map +1 -0
- package/dist/recipes/validation.d.ts +13 -0
- package/dist/recipes/validation.js +617 -0
- package/dist/recipes/validation.js.map +1 -0
- package/dist/recipes/yamlRunner.d.ts +116 -1
- package/dist/recipes/yamlRunner.js +1000 -401
- package/dist/recipes/yamlRunner.js.map +1 -1
- package/dist/recipesHttp.d.ts +137 -6
- package/dist/recipesHttp.js +941 -29
- package/dist/recipesHttp.js.map +1 -1
- package/dist/riskTier.js +7 -1
- package/dist/riskTier.js.map +1 -1
- package/dist/runLog.d.ts +100 -1
- package/dist/runLog.js +258 -5
- package/dist/runLog.js.map +1 -1
- package/dist/schemas/dry-run-plan.v1.json +139 -0
- package/dist/schemas/recipe.v1.json +684 -0
- package/dist/server.d.ts +121 -8
- package/dist/server.js +538 -735
- package/dist/server.js.map +1 -1
- package/dist/ssrfGuard.d.ts +54 -0
- package/dist/ssrfGuard.js +122 -0
- package/dist/ssrfGuard.js.map +1 -0
- package/dist/streamableHttp.d.ts +39 -1
- package/dist/streamableHttp.js +128 -17
- package/dist/streamableHttp.js.map +1 -1
- package/dist/tokenUsageTracker.d.ts +33 -0
- package/dist/tokenUsageTracker.js +146 -0
- package/dist/tokenUsageTracker.js.map +1 -0
- package/dist/tools/activityLog.d.ts +2 -0
- package/dist/tools/addLinearComment.d.ts +1 -0
- package/dist/tools/addLinearComment.js +4 -2
- package/dist/tools/addLinearComment.js.map +1 -1
- package/dist/tools/batchLsp.d.ts +3 -0
- package/dist/tools/bridgeDoctor.d.ts +1 -0
- package/dist/tools/bridgeDoctor.js +2 -2
- package/dist/tools/bridgeDoctor.js.map +1 -1
- package/dist/tools/bridgeStatus.d.ts +1 -0
- package/dist/tools/cancelClaudeTask.d.ts +2 -0
- package/dist/tools/cancelClaudeTask.js +1 -0
- package/dist/tools/cancelClaudeTask.js.map +1 -1
- package/dist/tools/checkDocumentDirty.d.ts +1 -0
- package/dist/tools/clipboard.d.ts +2 -0
- package/dist/tools/closeTabs.d.ts +2 -0
- package/dist/tools/codeLens.d.ts +1 -0
- package/dist/tools/contextBundle.d.ts +1 -0
- package/dist/tools/createIssueFromAIComment.d.ts +1 -0
- package/dist/tools/createLinearIssue.d.ts +1 -0
- package/dist/tools/ctxGetTaskContext.d.ts +1 -0
- package/dist/tools/ctxQueryTraces.d.ts +1 -0
- 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/documentLinks.d.ts +1 -0
- package/dist/tools/editText.d.ts +1 -0
- package/dist/tools/enrichCommit.d.ts +1 -0
- package/dist/tools/enrichStackTrace.d.ts +1 -0
- package/dist/tools/explainDiagnostic.d.ts +1 -0
- package/dist/tools/explainSymbol.d.ts +1 -0
- package/dist/tools/fetchCalendarEvents.d.ts +1 -0
- package/dist/tools/fetchGithubIssue.d.ts +1 -0
- package/dist/tools/fetchGithubPR.d.ts +1 -0
- package/dist/tools/fetchLinearIssue.d.ts +1 -0
- package/dist/tools/fetchSentryIssue.d.ts +1 -0
- package/dist/tools/fetchSlackProfile.d.ts +1 -0
- package/dist/tools/fetchSlackProfile.js +4 -1
- package/dist/tools/fetchSlackProfile.js.map +1 -1
- package/dist/tools/fileOperations.d.ts +3 -0
- package/dist/tools/fileWatcher.d.ts +2 -0
- package/dist/tools/findFiles.d.ts +1 -0
- package/dist/tools/findRelatedTests.d.ts +1 -0
- package/dist/tools/fixAllLintErrors.d.ts +1 -0
- package/dist/tools/foldingRanges.d.ts +1 -0
- package/dist/tools/formatDocument.d.ts +1 -0
- package/dist/tools/generateTests.d.ts +1 -0
- package/dist/tools/getAIComments.d.ts +1 -0
- package/dist/tools/getAnalyticsReport.d.ts +1 -0
- package/dist/tools/getArchitectureContext.d.ts +1 -0
- package/dist/tools/getBufferContent.d.ts +1 -0
- package/dist/tools/getChangeImpact.d.ts +1 -0
- package/dist/tools/getClaudeTaskStatus.d.ts +2 -0
- package/dist/tools/getClaudeTaskStatus.js +1 -0
- package/dist/tools/getClaudeTaskStatus.js.map +1 -1
- package/dist/tools/getCodeCoverage.d.ts +1 -0
- package/dist/tools/getCommitsForIssue.d.ts +1 -0
- package/dist/tools/getConnectorStatus.d.ts +1 -0
- package/dist/tools/getCurrentSelection.d.ts +2 -0
- package/dist/tools/getDebugState.d.ts +1 -0
- package/dist/tools/getDependencyTree.d.ts +1 -0
- package/dist/tools/getDiagnostics.d.ts +1 -0
- package/dist/tools/getDiffFromHandoff.d.ts +1 -0
- package/dist/tools/getDocumentSymbols.d.ts +25 -0
- package/dist/tools/getDocumentSymbols.js +74 -8
- package/dist/tools/getDocumentSymbols.js.map +1 -1
- package/dist/tools/getFileTree.d.ts +1 -0
- package/dist/tools/getGitDiff.d.ts +1 -0
- package/dist/tools/getGitHotspots.d.ts +1 -0
- package/dist/tools/getGitLog.d.ts +1 -0
- package/dist/tools/getGitStatus.d.ts +1 -0
- package/dist/tools/getImportTree.d.ts +1 -0
- package/dist/tools/getImportedSignatures.d.ts +1 -0
- package/dist/tools/getOpenEditors.d.ts +1 -0
- package/dist/tools/getPRTemplate.d.ts +1 -0
- package/dist/tools/getProjectContext.d.ts +1 -0
- package/dist/tools/getProjectInfo.d.ts +1 -0
- package/dist/tools/getSecurityAdvisories.d.ts +1 -0
- package/dist/tools/getSecurityAdvisories.js +10 -1
- package/dist/tools/getSecurityAdvisories.js.map +1 -1
- package/dist/tools/getSessionUsage.d.ts +4 -0
- package/dist/tools/getSessionUsage.js +3 -0
- package/dist/tools/getSessionUsage.js.map +1 -1
- package/dist/tools/getSymbolHistory.d.ts +1 -0
- package/dist/tools/getToolCapabilities.d.ts +1 -0
- package/dist/tools/getTypeSignature.d.ts +1 -0
- package/dist/tools/getWorkspaceFolders.d.ts +1 -0
- package/dist/tools/getWorkspaceSettings.d.ts +1 -0
- package/dist/tools/gitHistory.d.ts +2 -0
- package/dist/tools/gitWrite.d.ts +11 -0
- package/dist/tools/github/actions.d.ts +2 -0
- package/dist/tools/github/actions.js +4 -2
- package/dist/tools/github/actions.js.map +1 -1
- package/dist/tools/github/composite.d.ts +342 -0
- package/dist/tools/github/composite.js +343 -0
- package/dist/tools/github/composite.js.map +1 -0
- package/dist/tools/github/index.d.ts +1 -0
- package/dist/tools/github/index.js +1 -0
- package/dist/tools/github/index.js.map +1 -1
- package/dist/tools/github/issues.d.ts +4 -0
- package/dist/tools/github/issues.js +8 -4
- package/dist/tools/github/issues.js.map +1 -1
- package/dist/tools/github/pr.d.ts +7 -0
- package/dist/tools/github/pr.js +50 -12
- package/dist/tools/github/pr.js.map +1 -1
- package/dist/tools/handoffNote.d.ts +4 -0
- package/dist/tools/handoffNote.js +2 -0
- package/dist/tools/handoffNote.js.map +1 -1
- package/dist/tools/hoverAtCursor.d.ts +1 -0
- package/dist/tools/httpClient.d.ts +2 -0
- package/dist/tools/index.d.ts +8 -0
- package/dist/tools/index.js +47 -8
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/inlayHints.d.ts +1 -0
- package/dist/tools/launchQuickTask.d.ts +2 -0
- package/dist/tools/launchQuickTask.js +1 -0
- package/dist/tools/launchQuickTask.js.map +1 -1
- package/dist/tools/listClaudeTasks.d.ts +2 -0
- package/dist/tools/listClaudeTasks.js +1 -0
- package/dist/tools/listClaudeTasks.js.map +1 -1
- package/dist/tools/listTerminals.d.ts +1 -0
- package/dist/tools/lsp.d.ts +14 -0
- package/dist/tools/navigateToSymbolByName.d.ts +1 -0
- package/dist/tools/openDiff.d.ts +1 -0
- package/dist/tools/openFile.d.ts +1 -0
- package/dist/tools/openInBrowser.d.ts +1 -0
- package/dist/tools/organizeImports.d.ts +1 -0
- package/dist/tools/performanceReport.d.ts +1 -0
- package/dist/tools/planPersistence.d.ts +5 -0
- package/dist/tools/previewEdit.d.ts +1 -0
- package/dist/tools/refactorAnalyze.d.ts +1 -0
- package/dist/tools/refactorPreview.d.ts +2 -0
- package/dist/tools/refactorPreview.js +1 -0
- package/dist/tools/refactorPreview.js.map +1 -1
- package/dist/tools/replaceBlock.d.ts +1 -0
- package/dist/tools/resumeClaudeTask.d.ts +2 -0
- package/dist/tools/resumeClaudeTask.js +1 -0
- package/dist/tools/resumeClaudeTask.js.map +1 -1
- package/dist/tools/runClaudeTask.d.ts +2 -0
- package/dist/tools/runClaudeTask.js +1 -0
- package/dist/tools/runClaudeTask.js.map +1 -1
- package/dist/tools/runCommand.d.ts +1 -0
- package/dist/tools/runTests.d.ts +1 -0
- package/dist/tools/saveDocument.d.ts +1 -0
- package/dist/tools/screenshotAndAnnotate.d.ts +1 -0
- package/dist/tools/searchAndReplace.d.ts +1 -0
- package/dist/tools/searchTools.d.ts +1 -0
- package/dist/tools/searchTools.js +1 -1
- 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/setActiveWorkspaceFolder.d.ts +1 -0
- package/dist/tools/signatureHelp.d.ts +1 -0
- package/dist/tools/slackListChannels.d.ts +1 -0
- package/dist/tools/slackListChannels.js.map +1 -1
- package/dist/tools/slackPostMessage.d.ts +1 -0
- package/dist/tools/slackPostMessage.js +11 -6
- package/dist/tools/slackPostMessage.js.map +1 -1
- package/dist/tools/terminal.d.ts +6 -0
- package/dist/tools/testTraceToSource.d.ts +1 -0
- package/dist/tools/testTraceToSource.js +2 -2
- package/dist/tools/testTraceToSource.js.map +1 -1
- package/dist/tools/transaction.d.ts +23 -0
- package/dist/tools/transaction.js +29 -0
- package/dist/tools/transaction.js.map +1 -1
- package/dist/tools/typeHierarchy.d.ts +1 -0
- package/dist/tools/updateLinearIssue.d.ts +1 -0
- package/dist/tools/updateLinearIssue.js +20 -6
- package/dist/tools/updateLinearIssue.js.map +1 -1
- package/dist/tools/utils.d.ts +2 -0
- 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/traceEncryption.d.ts +46 -0
- package/dist/traceEncryption.js +124 -0
- package/dist/traceEncryption.js.map +1 -0
- package/dist/transport.d.ts +46 -1
- package/dist/transport.js +173 -19
- package/dist/transport.js.map +1 -1
- package/package.json +30 -8
- package/scripts/mcp-stdio-shim.cjs +19 -3
- package/scripts/start-all.sh +30 -1
- package/templates/automation-policies/recipe-authoring.json +25 -0
- package/templates/automation-policy.example.json +6 -0
- package/templates/co.patchwork-os.bridge.plist +34 -0
- package/templates/policies/README.md +72 -0
- package/templates/policies/conservative.json +14 -0
- package/templates/policies/developer.json +14 -0
- package/templates/policies/headless-ci.json +24 -0
- package/templates/policies/personal-assistant.json +15 -0
- package/templates/policies/regulated-industry.json +18 -0
- package/templates/recipes/lint-on-save.yaml +1 -2
- package/templates/recipes/morning-brief-slack.yaml +57 -0
- package/templates/recipes/morning-brief.yaml +2 -2
- package/templates/recipes/project-health-check.yaml +50 -0
- package/templates/recipes/webhook/README.md +70 -0
- package/templates/recipes/webhook/capture-thought.yaml +26 -0
- package/templates/recipes/webhook/customer-escalation.yaml +49 -0
- package/templates/recipes/webhook/incident-intake.yaml +46 -0
- package/templates/recipes/webhook/meeting-prep.yaml +48 -0
- package/templates/recipes/webhook/morning-brief.yaml +57 -0
|
@@ -0,0 +1,701 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* meetingNotes.parse — deterministic parser for Google Meet / Gemini note docs.
|
|
3
|
+
*
|
|
4
|
+
* Accepts the plain-text content of a meeting notes document (or a JSON array
|
|
5
|
+
* of resolved items from the drive.fetchDoc step) and returns structured JSON.
|
|
6
|
+
* No LLM call required — pure regex extraction.
|
|
7
|
+
*/
|
|
8
|
+
import { registerTool } from "../toolRegistry.js";
|
|
9
|
+
// ── Section-heading patterns ──────────────────────────────────────────────────
|
|
10
|
+
// Section heading patterns. Accept either Markdown-style (`# Attendees`) or
|
|
11
|
+
// bare-line plain-text style (`Attendees` on its own line, optionally with a
|
|
12
|
+
// trailing colon) — Google Drive's plain-text export drops `#` prefixes.
|
|
13
|
+
const SECTION_PATTERNS = {
|
|
14
|
+
attendees: /^\s*(?:#+\s*)?(attendees?|participants?|invited|who\s+attended)\s*:?\s*$/im,
|
|
15
|
+
actionItems: /^\s*(?:#+\s*)?(action\s+items?|next\s+steps?|suggested\s+next\s+steps?|todos?|follow[\s-]?ups?)\s*:?\s*$/im,
|
|
16
|
+
decisions: /^\s*(?:#+\s*)?(decisions?|key\s+decisions?|resolved)\s*:?\s*$/im,
|
|
17
|
+
openQuestions: /^\s*(?:#+\s*)?(open\s+questions?|parking\s+lot|unresolved|questions?)\s*:?\s*$/im,
|
|
18
|
+
summary: /^\s*(?:#+\s*)?(summary|details|recap|meeting\s+notes?|discussion\s+notes?)\s*:?\s*$/im,
|
|
19
|
+
};
|
|
20
|
+
// ── Helpers ───────────────────────────────────────────────────────────────────
|
|
21
|
+
function extractTitle(text) {
|
|
22
|
+
// First h1 heading
|
|
23
|
+
const h1 = text.match(/^#\s+(.+)$/m);
|
|
24
|
+
if (h1)
|
|
25
|
+
return h1[1].trim();
|
|
26
|
+
// First non-empty line that looks substantive. Skip Gemini boilerplate
|
|
27
|
+
// (e.g. "📝 Notes", "Notes by Gemini") and pick the next real line.
|
|
28
|
+
const lines = text
|
|
29
|
+
.split("\n")
|
|
30
|
+
.map((l) => l.replace(/^[#*\->\s]+/, "").trim())
|
|
31
|
+
.filter((l) => l.length > 3);
|
|
32
|
+
const BOILERPLATE = /^(?:[\p{Emoji_Presentation}\p{Extended_Pictographic}]\s*)?(notes|notes by gemini|meeting notes)\s*$/iu;
|
|
33
|
+
const first = lines.find((l) => !BOILERPLATE.test(l));
|
|
34
|
+
return first ?? lines[0] ?? "";
|
|
35
|
+
}
|
|
36
|
+
function extractDate(text) {
|
|
37
|
+
// ISO date
|
|
38
|
+
const iso = text.match(/\b(\d{4}-\d{2}-\d{2})\b/);
|
|
39
|
+
if (iso)
|
|
40
|
+
return iso[1];
|
|
41
|
+
// YYYY/MM/DD (e.g. "2026/04/27" — Gemini-generated doc titles)
|
|
42
|
+
const slash = text.match(/\b(\d{4})\/(\d{1,2})\/(\d{1,2})\b/);
|
|
43
|
+
if (slash) {
|
|
44
|
+
const y = slash[1];
|
|
45
|
+
const m = slash[2].padStart(2, "0");
|
|
46
|
+
const d = slash[3].padStart(2, "0");
|
|
47
|
+
return `${y}-${m}-${d}`;
|
|
48
|
+
}
|
|
49
|
+
// Month names: full or 3-letter abbreviation. Handles "Apr 27, 2026" and
|
|
50
|
+
// "27 April 2026" alike.
|
|
51
|
+
const MONTHS = "January|February|March|April|May|June|July|August|September|October|November|December|Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Sept|Oct|Nov|Dec";
|
|
52
|
+
const human = text.match(new RegExp(`\\b(${MONTHS})\\s+(\\d{1,2}),?\\s+(\\d{4})\\b`, "i"));
|
|
53
|
+
if (human) {
|
|
54
|
+
const month = human[1];
|
|
55
|
+
const day = human[2].padStart(2, "0");
|
|
56
|
+
const year = human[3];
|
|
57
|
+
const mon = new Date(`${month} 1, 2000`).getMonth() + 1;
|
|
58
|
+
return `${year}-${String(mon).padStart(2, "0")}-${day}`;
|
|
59
|
+
}
|
|
60
|
+
const human2 = text.match(new RegExp(`\\b(\\d{1,2})\\s+(${MONTHS})\\s+(\\d{4})\\b`, "i"));
|
|
61
|
+
if (human2) {
|
|
62
|
+
const day = human2[1].padStart(2, "0");
|
|
63
|
+
const month = human2[2];
|
|
64
|
+
const year = human2[3];
|
|
65
|
+
const mon = new Date(`${month} 1, 2000`).getMonth() + 1;
|
|
66
|
+
return `${year}-${String(mon).padStart(2, "0")}-${day}`;
|
|
67
|
+
}
|
|
68
|
+
return "";
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Pre-process the doc text so headings that share a line with content are
|
|
72
|
+
* split onto their own line. Gemini Notes exports often render attendee chips
|
|
73
|
+
* inline like `Invited [a@b](mailto:a@b) [c@d](mailto:c@d)` — without this
|
|
74
|
+
* step, splitSections never recognises the heading.
|
|
75
|
+
*/
|
|
76
|
+
function normalizeInlineHeadings(text) {
|
|
77
|
+
const HEADER_NAMES = "attendees?|participants?|invited|attachments?|action\\s+items?|next\\s+steps?|suggested\\s+next\\s+steps?|todos?|follow[\\s-]?ups?|decisions?|key\\s+decisions?|resolved|open\\s+questions?|parking\\s+lot|unresolved|questions?|summary|details|recap|meeting\\s+notes?|discussion\\s+notes?";
|
|
78
|
+
const inlineHeader = new RegExp(`^(\\s*(?:#+\\s*)?(?:${HEADER_NAMES}))[\\s:]+(.+)$`, "i");
|
|
79
|
+
return text
|
|
80
|
+
.split("\n")
|
|
81
|
+
.map((line) => {
|
|
82
|
+
const m = inlineHeader.exec(line);
|
|
83
|
+
if (!m)
|
|
84
|
+
return line;
|
|
85
|
+
// Only split if the trailing portion is non-trivial — avoids mangling
|
|
86
|
+
// genuine prose lines that happen to start with a section word.
|
|
87
|
+
const rest = m[2].trim();
|
|
88
|
+
if (rest.length === 0)
|
|
89
|
+
return line;
|
|
90
|
+
return `${m[1].trim()}\n${rest}`;
|
|
91
|
+
})
|
|
92
|
+
.join("\n");
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Split doc into named sections. Returns a map of section-name → lines[].
|
|
96
|
+
* Everything before the first recognised heading goes into "preamble".
|
|
97
|
+
*/
|
|
98
|
+
function splitSections(text) {
|
|
99
|
+
const lines = normalizeInlineHeadings(text).split("\n");
|
|
100
|
+
const sections = new Map();
|
|
101
|
+
sections.set("preamble", []);
|
|
102
|
+
let current = "preamble";
|
|
103
|
+
for (const line of lines) {
|
|
104
|
+
// Drop the standalone `Attachments` block — it's metadata, never content
|
|
105
|
+
// we care about. Both bare and Markdown-link forms are matched.
|
|
106
|
+
if (/^\s*(?:#+\s*)?attachments?\s*:?\s*$/i.test(line)) {
|
|
107
|
+
current = "_drop";
|
|
108
|
+
sections.set("_drop", []);
|
|
109
|
+
continue;
|
|
110
|
+
}
|
|
111
|
+
let matched = false;
|
|
112
|
+
for (const [name, rx] of Object.entries(SECTION_PATTERNS)) {
|
|
113
|
+
if (rx.test(line)) {
|
|
114
|
+
current = name;
|
|
115
|
+
// If the same logical section recurs (e.g. `Summary` followed later
|
|
116
|
+
// by `Details` — both map to "summary"), append rather than reset so
|
|
117
|
+
// earlier content wins for `buildSummary`'s first-3-sentences cutoff.
|
|
118
|
+
if (!sections.has(name))
|
|
119
|
+
sections.set(name, []);
|
|
120
|
+
matched = true;
|
|
121
|
+
break;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
if (!matched) {
|
|
125
|
+
sections.get(current).push(line);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
sections.delete("_drop");
|
|
129
|
+
return sections;
|
|
130
|
+
}
|
|
131
|
+
function stripMarkdownInline(s) {
|
|
132
|
+
// Strip ** bold, * / _ italic, and ` inline code markers without touching
|
|
133
|
+
// their content. Keep this lightweight — full Markdown rendering is overkill.
|
|
134
|
+
return s
|
|
135
|
+
.replace(/\*\*([^*]+)\*\*/g, "$1")
|
|
136
|
+
.replace(/(?<![*])\*([^*\n]+)\*(?![*])/g, "$1")
|
|
137
|
+
.replace(/`([^`\n]+)`/g, "$1");
|
|
138
|
+
}
|
|
139
|
+
function bulletLines(lines) {
|
|
140
|
+
return (lines
|
|
141
|
+
// Strip inline Markdown emphasis FIRST. Otherwise the bullet-prefix
|
|
142
|
+
// stripper below would eat leading `**` and leave an orphan trailing `**`.
|
|
143
|
+
.map(stripMarkdownInline)
|
|
144
|
+
// Strip Markdown checkbox markers (`- [ ]`, `* [x]`) before generic
|
|
145
|
+
// bullet-prefix stripping so the checkbox brackets don't survive.
|
|
146
|
+
.map((l) => l.replace(/^[\s\-*•>]+\[[ xX]\]\s*/, ""))
|
|
147
|
+
// Drive's text/plain export renders checkbox bullets as Unicode glyphs
|
|
148
|
+
// (e.g. ☐ ☑ ▢ □ ◯ ✓ ✔). Strip these too so the assignee bracket regex
|
|
149
|
+
// in parseActionItems can match the leading `[Person]` token.
|
|
150
|
+
.map((l) => l.replace(/^\s*[☐☑▢□◯✓✔✗✘]\s*/, ""))
|
|
151
|
+
.map((l) => l.replace(/^[\s\-*•>]+/, "").trim())
|
|
152
|
+
.filter((l) => l.length > 0));
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Extract display names / emails from a line of inline chips. Handles three
|
|
156
|
+
* formats Drive emits across export modes:
|
|
157
|
+
* 1. Markdown link form: `[Stephanie M](mailto:sm@wamae.com)`
|
|
158
|
+
* 2. Tab / 2+ space separated tokens (markdown export of bare chips)
|
|
159
|
+
* 3. Single-space separated tokens with emails as boundaries
|
|
160
|
+
* (`text/plain` export form): `kwkarago@gmail.com Stephanie M sm@x.com`
|
|
161
|
+
* where multi-word names are several capitalised tokens in a row.
|
|
162
|
+
*/
|
|
163
|
+
function extractChips(line) {
|
|
164
|
+
const out = [];
|
|
165
|
+
// 1. Markdown links — pull link text out first, strip from the line.
|
|
166
|
+
const linkRx = /\[([^\]]+)\]\(mailto:[^)]+\)/g;
|
|
167
|
+
let m;
|
|
168
|
+
while ((m = linkRx.exec(line)) !== null) {
|
|
169
|
+
out.push(m[1].trim());
|
|
170
|
+
}
|
|
171
|
+
let working = line.replace(linkRx, " ");
|
|
172
|
+
// 2. Tab / 2+ space splits — Drive's markdown export between bare chips.
|
|
173
|
+
const wideSplit = working.split(/\t+|\s{2,}/).filter((s) => s.trim());
|
|
174
|
+
if (wideSplit.length > 1) {
|
|
175
|
+
for (const piece of wideSplit) {
|
|
176
|
+
const t = piece.trim();
|
|
177
|
+
if (t.length > 1 && t.length < 100)
|
|
178
|
+
out.push(t);
|
|
179
|
+
}
|
|
180
|
+
return out;
|
|
181
|
+
}
|
|
182
|
+
// 3. Single-space export form. Tokenise, then walk: emails are atomic;
|
|
183
|
+
// consecutive capitalised tokens are merged into one display name.
|
|
184
|
+
const tokens = working.split(/\s+/).filter(Boolean);
|
|
185
|
+
let nameBuf = [];
|
|
186
|
+
const flushName = () => {
|
|
187
|
+
if (nameBuf.length === 0)
|
|
188
|
+
return;
|
|
189
|
+
const name = nameBuf.join(" ").trim();
|
|
190
|
+
if (name.length > 1 && name.length < 100)
|
|
191
|
+
out.push(name);
|
|
192
|
+
nameBuf = [];
|
|
193
|
+
};
|
|
194
|
+
const isEmail = (t) => /@/.test(t);
|
|
195
|
+
// A "name token" starts with a capital letter / is short and alphabetic
|
|
196
|
+
// (handles initials like "M"). Avoid eating section keywords accidentally.
|
|
197
|
+
const isNameToken = (t) => /^[A-Z][A-Za-z'.\-]*$/.test(t);
|
|
198
|
+
for (const tok of tokens) {
|
|
199
|
+
if (isEmail(tok)) {
|
|
200
|
+
flushName();
|
|
201
|
+
out.push(tok);
|
|
202
|
+
}
|
|
203
|
+
else if (isNameToken(tok)) {
|
|
204
|
+
nameBuf.push(tok);
|
|
205
|
+
}
|
|
206
|
+
else {
|
|
207
|
+
flushName();
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
flushName();
|
|
211
|
+
return out;
|
|
212
|
+
}
|
|
213
|
+
/**
|
|
214
|
+
* Parse action item lines. Supports several Gemini / Meet formats:
|
|
215
|
+
* `[Person] Title: description` (escaped or raw brackets)
|
|
216
|
+
* `[Person 1, Person 2] Title: description`
|
|
217
|
+
* `Person: task`
|
|
218
|
+
* plain task lines (assignee=null)
|
|
219
|
+
*/
|
|
220
|
+
function parseActionItems(lines) {
|
|
221
|
+
const UNASSIGNED = /^(unassigned|tbd|n\/a|none|-|the group)$/i;
|
|
222
|
+
return bulletLines(lines).map((line) => {
|
|
223
|
+
// Strip Markdown-escaped brackets that Drive emits (e.g. `\[Stephanie M\]`).
|
|
224
|
+
const cleaned = line.replace(/\\\[/g, "[").replace(/\\\]/g, "]");
|
|
225
|
+
// Form 1: leading `[Person ...] task...`
|
|
226
|
+
const bracket = /^\[([^\]]+)\]\s*(.+)$/.exec(cleaned);
|
|
227
|
+
if (bracket) {
|
|
228
|
+
const assignee = bracket[1].trim();
|
|
229
|
+
const task = bracket[2].trim();
|
|
230
|
+
return {
|
|
231
|
+
assignee: UNASSIGNED.test(assignee) ? null : assignee,
|
|
232
|
+
task,
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
// Form 2: `Person: task` (timestamp-safe — left side must look like a name)
|
|
236
|
+
const colonIdx = cleaned.indexOf(":");
|
|
237
|
+
if (colonIdx > 0 && colonIdx < 40) {
|
|
238
|
+
const left = cleaned.slice(0, colonIdx).trim();
|
|
239
|
+
const right = cleaned.slice(colonIdx + 1).trim();
|
|
240
|
+
if (/^[A-Za-z\s.'-]{2,30}$/.test(left) && right.length > 0) {
|
|
241
|
+
return { assignee: UNASSIGNED.test(left) ? null : left, task: right };
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
return { assignee: null, task: cleaned };
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
function buildSummary(sections) {
|
|
248
|
+
const body = bulletLines(sections.get("summary") ?? []);
|
|
249
|
+
if (body.length === 0)
|
|
250
|
+
return "";
|
|
251
|
+
// Return up to 3 sentences joined as prose
|
|
252
|
+
const sentences = body.join(" ").match(/[^.!?]+[.!?]*/g) ?? [];
|
|
253
|
+
return sentences.slice(0, 3).join(" ").trim();
|
|
254
|
+
}
|
|
255
|
+
// ── Core parser ───────────────────────────────────────────────────────────────
|
|
256
|
+
export function parseMeetingNotes(content) {
|
|
257
|
+
// Drive's plain-text export uses CRLF line endings. Trailing \r on each
|
|
258
|
+
// line breaks every line-anchored section regex (`Invited\r` ≠ `Invited`),
|
|
259
|
+
// so normalise to LF before any pattern matching happens.
|
|
260
|
+
content = content.replace(/\r\n?/g, "\n");
|
|
261
|
+
const sections = splitSections(content);
|
|
262
|
+
const title = extractTitle(content);
|
|
263
|
+
const date = extractDate((sections.get("preamble") ?? []).slice(0, 10).join("\n") + content);
|
|
264
|
+
// Drive's exports render attendee "chips" inline on one line, either as
|
|
265
|
+
// Markdown links (`[Name](mailto:email)`) or as raw tokens separated by
|
|
266
|
+
// tabs / runs of 2+ spaces. extractChips handles both.
|
|
267
|
+
const attendees = bulletLines(sections.get("attendees") ?? [])
|
|
268
|
+
.flatMap(extractChips)
|
|
269
|
+
.filter((a) => a.length > 1 && a.length < 100 && !/^attachments?\s*:?$/i.test(a));
|
|
270
|
+
const actionItems = parseActionItems(sections.get("actionItems") ?? []);
|
|
271
|
+
const decisions = bulletLines(sections.get("decisions") ?? []);
|
|
272
|
+
const openQuestions = bulletLines(sections.get("openQuestions") ?? []);
|
|
273
|
+
const summaryText = buildSummary(sections);
|
|
274
|
+
return {
|
|
275
|
+
meetingTitle: title,
|
|
276
|
+
meetingDate: date,
|
|
277
|
+
attendees,
|
|
278
|
+
actionItems,
|
|
279
|
+
decisions,
|
|
280
|
+
openQuestions,
|
|
281
|
+
summaryText,
|
|
282
|
+
};
|
|
283
|
+
}
|
|
284
|
+
/**
|
|
285
|
+
* Parse an array of resolved items (from the drive.fetchDoc step) or a raw
|
|
286
|
+
* content string. Input may be:
|
|
287
|
+
* - JSON array: [{ emailId, subject, source, content }]
|
|
288
|
+
* - Plain string: raw meeting notes text
|
|
289
|
+
*/
|
|
290
|
+
function parseInput(raw) {
|
|
291
|
+
const trimmed = raw.trim();
|
|
292
|
+
if (trimmed.startsWith("[")) {
|
|
293
|
+
let items;
|
|
294
|
+
try {
|
|
295
|
+
items = JSON.parse(trimmed);
|
|
296
|
+
}
|
|
297
|
+
catch {
|
|
298
|
+
return [parseMeetingNotes(trimmed)];
|
|
299
|
+
}
|
|
300
|
+
return items.map((item) => parseMeetingNotes(item.content ?? ""));
|
|
301
|
+
}
|
|
302
|
+
if (trimmed.startsWith("{")) {
|
|
303
|
+
let obj;
|
|
304
|
+
try {
|
|
305
|
+
obj = JSON.parse(trimmed);
|
|
306
|
+
return [parseMeetingNotes(obj.content ?? trimmed)];
|
|
307
|
+
}
|
|
308
|
+
catch {
|
|
309
|
+
// fall through
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
return [parseMeetingNotes(trimmed)];
|
|
313
|
+
}
|
|
314
|
+
const TEAM_LABELS = {
|
|
315
|
+
Sales: ["meeting-action-item", "sales"],
|
|
316
|
+
Marketing: ["meeting-action-item", "marketing"],
|
|
317
|
+
Engineering: ["meeting-action-item", "engineering"],
|
|
318
|
+
};
|
|
319
|
+
function isUrgentTask(team, task) {
|
|
320
|
+
const t = task.toLowerCase();
|
|
321
|
+
if (team === "Sales")
|
|
322
|
+
return /follow.?up|proposal|demo|close date/.test(t);
|
|
323
|
+
if (team === "Marketing")
|
|
324
|
+
return /deadline|launch|campaign/.test(t);
|
|
325
|
+
// Engineering
|
|
326
|
+
return /bug|incident|fix|deploy|urgent/.test(t);
|
|
327
|
+
}
|
|
328
|
+
function buildTitle(team, meetingTitle, task) {
|
|
329
|
+
if (team !== "Sales")
|
|
330
|
+
return task;
|
|
331
|
+
// Extract company/prospect name from meeting title heuristically:
|
|
332
|
+
// e.g. "Acme Q2 Review" → "[Acme] task"
|
|
333
|
+
const company = meetingTitle
|
|
334
|
+
.split(/\s+/)
|
|
335
|
+
.slice(0, 2)
|
|
336
|
+
.join(" ")
|
|
337
|
+
.replace(/[^A-Za-z0-9 ]/g, "")
|
|
338
|
+
.trim();
|
|
339
|
+
return company ? `[${company}] ${task}` : task;
|
|
340
|
+
}
|
|
341
|
+
function buildDescription(meeting, task) {
|
|
342
|
+
const attendees = meeting.attendees.join(", ") || "—";
|
|
343
|
+
return [
|
|
344
|
+
`**Meeting:** ${meeting.meetingTitle} (${meeting.meetingDate})`,
|
|
345
|
+
`**Attendees:** ${attendees}`,
|
|
346
|
+
`**Action item:** ${task}`,
|
|
347
|
+
"",
|
|
348
|
+
"---",
|
|
349
|
+
`**Meeting recap:** ${meeting.summaryText}`,
|
|
350
|
+
].join("\n");
|
|
351
|
+
}
|
|
352
|
+
// ── Tool registration ─────────────────────────────────────────────────────────
|
|
353
|
+
registerTool({
|
|
354
|
+
id: "meetingNotes.createLinearIssues",
|
|
355
|
+
namespace: "meetingNotes",
|
|
356
|
+
description: "For each parsed meeting, create a Linear issue per action item routed to the correct team (Sales / Marketing / Engineering) with team-specific labels, priority, and title rules. Optionally assigns the issue if the action item assignee matches a known Linear user. Returns { created: [{ issueId, identifier, url, task, assignee }] }.",
|
|
357
|
+
paramsSchema: {
|
|
358
|
+
type: "object",
|
|
359
|
+
required: ["meetings", "team"],
|
|
360
|
+
properties: {
|
|
361
|
+
meetings: {
|
|
362
|
+
description: "Array of ParsedMeeting objects (output of meetingNotes.parse), or a JSON string of that array.",
|
|
363
|
+
},
|
|
364
|
+
team: {
|
|
365
|
+
type: "string",
|
|
366
|
+
description: "Target Linear team for all issues. Accepts the canonical Sales/Marketing/Engineering routing keys (used for label + priority logic) or any team name; falls back to the workspace's first team when no exact match is found in Linear.",
|
|
367
|
+
},
|
|
368
|
+
into: { type: "string" },
|
|
369
|
+
},
|
|
370
|
+
},
|
|
371
|
+
outputSchema: {
|
|
372
|
+
type: "object",
|
|
373
|
+
properties: {
|
|
374
|
+
created: {
|
|
375
|
+
type: "array",
|
|
376
|
+
items: {
|
|
377
|
+
type: "object",
|
|
378
|
+
properties: {
|
|
379
|
+
issueId: { type: "string" },
|
|
380
|
+
identifier: { type: "string" },
|
|
381
|
+
url: { type: "string" },
|
|
382
|
+
task: { type: "string" },
|
|
383
|
+
assignee: { type: ["string", "null"] },
|
|
384
|
+
},
|
|
385
|
+
},
|
|
386
|
+
},
|
|
387
|
+
error: { type: "string" },
|
|
388
|
+
},
|
|
389
|
+
},
|
|
390
|
+
riskDefault: "medium",
|
|
391
|
+
isWrite: true,
|
|
392
|
+
isConnector: true,
|
|
393
|
+
execute: async ({ params }) => {
|
|
394
|
+
const { loadTokens, createIssue, updateIssue, listTeams, listLabels } = await import("../../connectors/linear.js");
|
|
395
|
+
if (!loadTokens()) {
|
|
396
|
+
return JSON.stringify({ created: [], error: "Linear not connected" });
|
|
397
|
+
}
|
|
398
|
+
const team = String(params.team);
|
|
399
|
+
const validTeams = ["Sales", "Marketing", "Engineering"];
|
|
400
|
+
if (!validTeams.includes(team)) {
|
|
401
|
+
return JSON.stringify({ created: [], error: `Unknown team: ${team}` });
|
|
402
|
+
}
|
|
403
|
+
// Resolve the requested team name to one Linear actually has. The MCP
|
|
404
|
+
// save_issue tool requires an existing team name or ID; if the workspace
|
|
405
|
+
// doesn't name a team literally "Sales"/"Marketing"/"Engineering", every
|
|
406
|
+
// create would otherwise fail. Strategy: case-insensitive exact match,
|
|
407
|
+
// then prefix match, then fall back to the first available team and
|
|
408
|
+
// surface that fallback in the output so /runs makes it visible.
|
|
409
|
+
let resolvedTeam = team;
|
|
410
|
+
let teamFallbackNote = null;
|
|
411
|
+
try {
|
|
412
|
+
const teams = await listTeams();
|
|
413
|
+
if (teams.length === 0) {
|
|
414
|
+
return JSON.stringify({
|
|
415
|
+
created: [],
|
|
416
|
+
error: "Linear workspace has no teams",
|
|
417
|
+
});
|
|
418
|
+
}
|
|
419
|
+
const wanted = team.toLowerCase();
|
|
420
|
+
const exact = teams.find((t) => t.name.toLowerCase() === wanted);
|
|
421
|
+
const prefix = exact ?? teams.find((t) => t.name.toLowerCase().startsWith(wanted));
|
|
422
|
+
const picked = exact ?? prefix ?? teams[0];
|
|
423
|
+
resolvedTeam = picked.name;
|
|
424
|
+
if (!exact) {
|
|
425
|
+
teamFallbackNote = `Linear team \"${team}\" not found; used \"${picked.name}\" instead. Available: ${teams.map((t) => t.name).join(", ")}`;
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
catch (err) {
|
|
429
|
+
// listTeams failure is fatal — without team resolution every save_issue
|
|
430
|
+
// call would fail anyway. Surface the underlying error.
|
|
431
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
432
|
+
return JSON.stringify({
|
|
433
|
+
created: [],
|
|
434
|
+
error: `Failed to list Linear teams: ${msg}`,
|
|
435
|
+
});
|
|
436
|
+
}
|
|
437
|
+
// Linear MCP's save_issue rejects label names that don't already exist in
|
|
438
|
+
// the workspace with a generic "Argument Validation Error". Filter the
|
|
439
|
+
// recipe's hardcoded label list down to only those that exist so creates
|
|
440
|
+
// succeed even on workspaces that haven't pre-created our routing labels.
|
|
441
|
+
let allowedLabels;
|
|
442
|
+
try {
|
|
443
|
+
const existing = await listLabels();
|
|
444
|
+
const existingNames = new Set(existing.map((l) => l.name.toLowerCase()));
|
|
445
|
+
const wanted = TEAM_LABELS[team] ?? [];
|
|
446
|
+
const filtered = wanted.filter((n) => existingNames.has(n.toLowerCase()));
|
|
447
|
+
allowedLabels = filtered.length > 0 ? filtered : undefined;
|
|
448
|
+
}
|
|
449
|
+
catch {
|
|
450
|
+
// listLabels failure is non-fatal — fall back to no labels rather than
|
|
451
|
+
// breaking issue creation entirely.
|
|
452
|
+
allowedLabels = undefined;
|
|
453
|
+
}
|
|
454
|
+
let meetings;
|
|
455
|
+
try {
|
|
456
|
+
const raw = params.meetings;
|
|
457
|
+
meetings = typeof raw === "string" ? JSON.parse(raw) : raw;
|
|
458
|
+
if (!Array.isArray(meetings))
|
|
459
|
+
meetings = [meetings];
|
|
460
|
+
}
|
|
461
|
+
catch {
|
|
462
|
+
return JSON.stringify({ created: [], error: "Invalid meetings input" });
|
|
463
|
+
}
|
|
464
|
+
const created = [];
|
|
465
|
+
// Surface per-item failures so the recipe can show them downstream.
|
|
466
|
+
// Previously these were swallowed with `continue`, which made silent
|
|
467
|
+
// failures impossible to diagnose.
|
|
468
|
+
const errors = [];
|
|
469
|
+
for (const meeting of meetings) {
|
|
470
|
+
for (const item of meeting.actionItems ?? []) {
|
|
471
|
+
const title = buildTitle(team, meeting.meetingTitle, item.task);
|
|
472
|
+
const priority = isUrgentTask(team, item.task) ? 1 : 2;
|
|
473
|
+
const description = buildDescription(meeting, item.task);
|
|
474
|
+
const issueArgs = {
|
|
475
|
+
team: resolvedTeam,
|
|
476
|
+
title,
|
|
477
|
+
description,
|
|
478
|
+
priority,
|
|
479
|
+
...(allowedLabels ? { labels: allowedLabels } : {}),
|
|
480
|
+
};
|
|
481
|
+
let result;
|
|
482
|
+
try {
|
|
483
|
+
result = await createIssue(issueArgs);
|
|
484
|
+
}
|
|
485
|
+
catch (err) {
|
|
486
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
487
|
+
errors.push({ task: item.task, error: msg });
|
|
488
|
+
continue;
|
|
489
|
+
}
|
|
490
|
+
// Assign if we have a non-null assignee
|
|
491
|
+
if (item.assignee) {
|
|
492
|
+
try {
|
|
493
|
+
await updateIssue({
|
|
494
|
+
id: result.identifier,
|
|
495
|
+
assignee: item.assignee,
|
|
496
|
+
});
|
|
497
|
+
}
|
|
498
|
+
catch {
|
|
499
|
+
// Assignment failure is non-fatal — issue is already created.
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
created.push({
|
|
503
|
+
issueId: result.id,
|
|
504
|
+
identifier: result.identifier,
|
|
505
|
+
url: result.url,
|
|
506
|
+
task: item.task,
|
|
507
|
+
assignee: item.assignee,
|
|
508
|
+
});
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
// Top-level error string surfaces the first failure so the calling
|
|
512
|
+
// step's outputTail makes the problem visible without inspecting
|
|
513
|
+
// step output JSON.
|
|
514
|
+
const out = { created, team: resolvedTeam };
|
|
515
|
+
if (teamFallbackNote)
|
|
516
|
+
out.warning = teamFallbackNote;
|
|
517
|
+
if (errors.length > 0) {
|
|
518
|
+
out.errors = errors;
|
|
519
|
+
if (created.length === 0) {
|
|
520
|
+
out.error = `All ${errors.length} issue creates failed. First: ${errors[0].error}`;
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
return JSON.stringify(out);
|
|
524
|
+
},
|
|
525
|
+
});
|
|
526
|
+
registerTool({
|
|
527
|
+
id: "meetingNotes.parse",
|
|
528
|
+
namespace: "meetingNotes",
|
|
529
|
+
description: "Parse Google Meet / Gemini meeting notes plain-text into structured JSON (title, date, attendees, action items, decisions, open questions, summary). Accepts raw text or the JSON array produced by the resolve_content step.",
|
|
530
|
+
paramsSchema: {
|
|
531
|
+
type: "object",
|
|
532
|
+
properties: {
|
|
533
|
+
content: {
|
|
534
|
+
type: "string",
|
|
535
|
+
description: "Meeting notes plain-text, or JSON array [{emailId,subject,source,content}] from drive.fetchDoc.",
|
|
536
|
+
},
|
|
537
|
+
into: { type: "string" },
|
|
538
|
+
},
|
|
539
|
+
required: ["content"],
|
|
540
|
+
},
|
|
541
|
+
outputSchema: {
|
|
542
|
+
type: "array",
|
|
543
|
+
items: {
|
|
544
|
+
type: "object",
|
|
545
|
+
properties: {
|
|
546
|
+
meetingTitle: { type: "string" },
|
|
547
|
+
meetingDate: { type: "string" },
|
|
548
|
+
attendees: { type: "array", items: { type: "string" } },
|
|
549
|
+
actionItems: {
|
|
550
|
+
type: "array",
|
|
551
|
+
items: {
|
|
552
|
+
type: "object",
|
|
553
|
+
properties: {
|
|
554
|
+
assignee: { type: ["string", "null"] },
|
|
555
|
+
task: { type: "string" },
|
|
556
|
+
},
|
|
557
|
+
},
|
|
558
|
+
},
|
|
559
|
+
decisions: { type: "array", items: { type: "string" } },
|
|
560
|
+
openQuestions: { type: "array", items: { type: "string" } },
|
|
561
|
+
summaryText: { type: "string" },
|
|
562
|
+
},
|
|
563
|
+
},
|
|
564
|
+
},
|
|
565
|
+
riskDefault: "low",
|
|
566
|
+
isWrite: false,
|
|
567
|
+
isConnector: false,
|
|
568
|
+
execute: async ({ params }) => {
|
|
569
|
+
const content = String(params.content ?? "");
|
|
570
|
+
const meetings = parseInput(content);
|
|
571
|
+
return JSON.stringify(meetings);
|
|
572
|
+
},
|
|
573
|
+
});
|
|
574
|
+
// ── meetingNotes.flatten ──────────────────────────────────────────────────────
|
|
575
|
+
registerTool({
|
|
576
|
+
id: "meetingNotes.flatten",
|
|
577
|
+
namespace: "meetingNotes",
|
|
578
|
+
description: "Extract the first meeting from a ParsedMeeting array and return a flat object ready for Slack template interpolation: { title, date, attendees, summary, decisions, openQuestions, slackChannel }.",
|
|
579
|
+
paramsSchema: {
|
|
580
|
+
type: "object",
|
|
581
|
+
required: ["meetings", "team"],
|
|
582
|
+
properties: {
|
|
583
|
+
meetings: {
|
|
584
|
+
description: "Output of meetingNotes.parse — array of ParsedMeeting or JSON string.",
|
|
585
|
+
},
|
|
586
|
+
team: {
|
|
587
|
+
type: "string",
|
|
588
|
+
description: "Routing key matching one of Sales/Marketing/Engineering; selects the Slack channel and is forwarded to the Slack template.",
|
|
589
|
+
},
|
|
590
|
+
slackChannelSales: { type: "string", default: "" },
|
|
591
|
+
slackChannelMarketing: { type: "string", default: "" },
|
|
592
|
+
slackChannelEngineering: { type: "string", default: "" },
|
|
593
|
+
issues: {
|
|
594
|
+
description: "Optional output of meetingNotes.createLinearIssues, used to render the action-items bullet list for Slack.",
|
|
595
|
+
},
|
|
596
|
+
into: { type: "string" },
|
|
597
|
+
},
|
|
598
|
+
},
|
|
599
|
+
outputSchema: {
|
|
600
|
+
type: "object",
|
|
601
|
+
properties: {
|
|
602
|
+
title: { type: "string" },
|
|
603
|
+
date: { type: "string" },
|
|
604
|
+
attendees: { type: "string" },
|
|
605
|
+
summary: { type: "string" },
|
|
606
|
+
decisions: { type: "string" },
|
|
607
|
+
openQuestions: { type: "string" },
|
|
608
|
+
actionItems: { type: "string" },
|
|
609
|
+
slackChannel: { type: "string" },
|
|
610
|
+
},
|
|
611
|
+
},
|
|
612
|
+
riskDefault: "low",
|
|
613
|
+
isWrite: false,
|
|
614
|
+
isConnector: false,
|
|
615
|
+
execute: async ({ params }) => {
|
|
616
|
+
let meetings;
|
|
617
|
+
try {
|
|
618
|
+
const raw = params.meetings;
|
|
619
|
+
meetings =
|
|
620
|
+
typeof raw === "string"
|
|
621
|
+
? JSON.parse(raw)
|
|
622
|
+
: raw;
|
|
623
|
+
if (!Array.isArray(meetings))
|
|
624
|
+
meetings = [meetings];
|
|
625
|
+
}
|
|
626
|
+
catch {
|
|
627
|
+
return JSON.stringify({ error: "Invalid meetings input" });
|
|
628
|
+
}
|
|
629
|
+
const m = meetings[0];
|
|
630
|
+
if (!m)
|
|
631
|
+
return JSON.stringify({ error: "No meetings found" });
|
|
632
|
+
const team = String(params.team ?? "Engineering");
|
|
633
|
+
const channelMap = {
|
|
634
|
+
Sales: String(params.slackChannelSales ?? ""),
|
|
635
|
+
Marketing: String(params.slackChannelMarketing ?? ""),
|
|
636
|
+
Engineering: String(params.slackChannelEngineering ?? ""),
|
|
637
|
+
};
|
|
638
|
+
const toBullets = (arr) => arr.length ? arr.map((s) => `• ${s}`).join("\n") : "(none)";
|
|
639
|
+
// Build the Slack-formatted action-items bullet list from the upstream
|
|
640
|
+
// meetingNotes.createLinearIssues output. The recipe template renderer
|
|
641
|
+
// (src/recipes/parser.ts renderTemplate) only supports plain dotted
|
|
642
|
+
// lookups, so per-item formatting must happen here, not in the YAML.
|
|
643
|
+
const issuesRaw = params.issues;
|
|
644
|
+
let createdIssues = [];
|
|
645
|
+
let issuesError;
|
|
646
|
+
try {
|
|
647
|
+
const parsed = typeof issuesRaw === "string" && issuesRaw.length > 0
|
|
648
|
+
? JSON.parse(issuesRaw)
|
|
649
|
+
: issuesRaw;
|
|
650
|
+
if (parsed && typeof parsed === "object") {
|
|
651
|
+
const obj = parsed;
|
|
652
|
+
if (Array.isArray(obj.created))
|
|
653
|
+
createdIssues = obj.created;
|
|
654
|
+
if (typeof obj.error === "string")
|
|
655
|
+
issuesError = obj.error;
|
|
656
|
+
}
|
|
657
|
+
}
|
|
658
|
+
catch {
|
|
659
|
+
// Tolerate missing/invalid issues input — flatten still produces all
|
|
660
|
+
// other Slack fields so the post is useful even when Linear failed.
|
|
661
|
+
}
|
|
662
|
+
const formatIssueBullet = (i) => {
|
|
663
|
+
const task = (i.task ?? "").trim() || "(untitled task)";
|
|
664
|
+
const assignee = i.assignee && i.assignee.length > 0 ? i.assignee : "unassigned";
|
|
665
|
+
if (i.url && i.identifier) {
|
|
666
|
+
return `• <${i.url}|${i.identifier}> ${task} — ${assignee}`;
|
|
667
|
+
}
|
|
668
|
+
if (i.identifier)
|
|
669
|
+
return `• ${i.identifier} ${task} — ${assignee}`;
|
|
670
|
+
return `• ${task} — ${assignee}`;
|
|
671
|
+
};
|
|
672
|
+
let actionItems;
|
|
673
|
+
if (createdIssues.length > 0) {
|
|
674
|
+
actionItems = createdIssues.map(formatIssueBullet).join("\n");
|
|
675
|
+
}
|
|
676
|
+
else if (issuesError) {
|
|
677
|
+
actionItems = `(Linear create failed: ${issuesError})`;
|
|
678
|
+
}
|
|
679
|
+
else if ((m.actionItems?.length ?? 0) > 0) {
|
|
680
|
+
// Fallback: render parsed action items even if Linear creation was
|
|
681
|
+
// skipped or returned nothing.
|
|
682
|
+
actionItems = m.actionItems
|
|
683
|
+
.map((a) => `• ${a.task} — ${a.assignee ?? "unassigned"}`)
|
|
684
|
+
.join("\n");
|
|
685
|
+
}
|
|
686
|
+
else {
|
|
687
|
+
actionItems = "(none)";
|
|
688
|
+
}
|
|
689
|
+
return JSON.stringify({
|
|
690
|
+
title: m.meetingTitle,
|
|
691
|
+
date: m.meetingDate,
|
|
692
|
+
attendees: m.attendees.join(", "),
|
|
693
|
+
summary: m.summaryText,
|
|
694
|
+
decisions: toBullets(m.decisions),
|
|
695
|
+
openQuestions: toBullets(m.openQuestions),
|
|
696
|
+
actionItems,
|
|
697
|
+
slackChannel: channelMap[team] ?? "",
|
|
698
|
+
});
|
|
699
|
+
},
|
|
700
|
+
});
|
|
701
|
+
//# sourceMappingURL=meetingNotes.js.map
|