@agimon-ai/doompi-workflow 0.0.1-alpha.2 → 0.0.1-alpha.21

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.
Files changed (180) hide show
  1. package/README.md +47 -21
  2. package/dist/adapters/pi/dispatcher.cjs.map +1 -0
  3. package/dist/{dispatcher.d.cts → adapters/pi/dispatcher.d.cts} +1 -1
  4. package/dist/adapters/pi/dispatcher.d.cts.map +1 -0
  5. package/dist/{dispatcher.d.mts → adapters/pi/dispatcher.d.mts} +1 -1
  6. package/dist/adapters/pi/dispatcher.d.mts.map +1 -0
  7. package/dist/adapters/pi/dispatcher.mjs.map +1 -0
  8. package/dist/adapters/pi/extension.cjs +2 -0
  9. package/dist/adapters/pi/extension.cjs.map +1 -0
  10. package/dist/{extension.d.cts → adapters/pi/extension.d.cts} +1 -1
  11. package/dist/adapters/pi/extension.d.cts.map +1 -0
  12. package/dist/{extension.d.mts → adapters/pi/extension.d.mts} +1 -1
  13. package/dist/adapters/pi/extension.d.mts.map +1 -0
  14. package/dist/adapters/pi/extension.mjs +2 -0
  15. package/dist/adapters/pi/extension.mjs.map +1 -0
  16. package/dist/adapters/pi/leader.cjs +2 -0
  17. package/dist/adapters/pi/leader.cjs.map +1 -0
  18. package/dist/adapters/pi/leader.mjs +2 -0
  19. package/dist/adapters/pi/leader.mjs.map +1 -0
  20. package/dist/adapters/pi/workflow/piExtension.cjs +11 -0
  21. package/dist/adapters/pi/workflow/piExtension.cjs.map +1 -0
  22. package/dist/adapters/pi/workflow/piExtension.mjs +11 -0
  23. package/dist/adapters/pi/workflow/piExtension.mjs.map +1 -0
  24. package/dist/{workflow → adapters/pi/workflow}/piToolBridge.cjs +1 -1
  25. package/dist/adapters/pi/workflow/piToolBridge.cjs.map +1 -0
  26. package/dist/{workflow → adapters/pi/workflow}/piToolBridge.mjs +1 -1
  27. package/dist/adapters/pi/workflow/piToolBridge.mjs.map +1 -0
  28. package/dist/adapters/pi/workflow/piTools.cjs +5 -0
  29. package/dist/adapters/pi/workflow/piTools.cjs.map +1 -0
  30. package/dist/adapters/pi/workflow/piTools.mjs +5 -0
  31. package/dist/adapters/pi/workflow/piTools.mjs.map +1 -0
  32. package/dist/adapters/pi/workflow/workflowLauncher.cjs +2 -0
  33. package/dist/adapters/pi/workflow/workflowLauncher.cjs.map +1 -0
  34. package/dist/adapters/pi/workflow/workflowLauncher.mjs +2 -0
  35. package/dist/adapters/pi/workflow/workflowLauncher.mjs.map +1 -0
  36. package/dist/extensions/dispatcher.cjs +1 -1
  37. package/dist/extensions/dispatcher.cjs.map +1 -1
  38. package/dist/extensions/dispatcher.d.cts +1 -1
  39. package/dist/extensions/dispatcher.d.mts +1 -1
  40. package/dist/extensions/dispatcher.mjs +1 -1
  41. package/dist/extensions/dispatcher.mjs.map +1 -1
  42. package/dist/extensions/pi.cjs +1 -1
  43. package/dist/extensions/pi.cjs.map +1 -1
  44. package/dist/extensions/pi.d.cts +1 -1
  45. package/dist/extensions/pi.d.mts +1 -1
  46. package/dist/extensions/pi.mjs +1 -1
  47. package/dist/extensions/pi.mjs.map +1 -1
  48. package/dist/index.cjs +1 -1
  49. package/dist/index.d.cts +2 -2
  50. package/dist/index.d.mts +2 -2
  51. package/dist/index.mjs +1 -1
  52. package/dist/schemas/workflowPi.cjs +2 -0
  53. package/dist/schemas/workflowPi.cjs.map +1 -0
  54. package/dist/schemas/workflowPi.mjs +2 -0
  55. package/dist/schemas/workflowPi.mjs.map +1 -0
  56. package/dist/services/workflow/backgroundWork.cjs.map +1 -0
  57. package/dist/services/workflow/backgroundWork.mjs.map +1 -0
  58. package/dist/services/workflow/workflowNarration.cjs +2 -0
  59. package/dist/services/workflow/workflowNarration.cjs.map +1 -0
  60. package/dist/services/workflow/workflowNarration.mjs +2 -0
  61. package/dist/services/workflow/workflowNarration.mjs.map +1 -0
  62. package/dist/tui/workflow/doomOverlay.cjs.map +1 -0
  63. package/dist/tui/workflow/doomOverlay.mjs.map +1 -0
  64. package/dist/tui/workflow/overlayText.cjs.map +1 -0
  65. package/dist/tui/workflow/overlayText.mjs.map +1 -0
  66. package/dist/tui/workflow/rendering.cjs.map +1 -0
  67. package/dist/tui/workflow/rendering.mjs.map +1 -0
  68. package/dist/tui/workflow/workflowInspector.cjs +2 -0
  69. package/dist/{workflow/workflowInspector.mjs.map → tui/workflow/workflowInspector.cjs.map} +1 -1
  70. package/dist/tui/workflow/workflowInspector.mjs +2 -0
  71. package/dist/tui/workflow/workflowInspector.mjs.map +1 -0
  72. package/dist/tui/workflow/workflowOverlay.cjs.map +1 -0
  73. package/dist/tui/workflow/workflowOverlay.mjs.map +1 -0
  74. package/dist/tui/workflow/workflowPicker.cjs.map +1 -0
  75. package/dist/tui/workflow/workflowPicker.mjs.map +1 -0
  76. package/dist/tui/workflow/workflowProgressOverlay.cjs +2 -0
  77. package/dist/tui/workflow/workflowProgressOverlay.cjs.map +1 -0
  78. package/dist/tui/workflow/workflowProgressOverlay.mjs +2 -0
  79. package/dist/tui/workflow/workflowProgressOverlay.mjs.map +1 -0
  80. package/dist/tui/workflow/workflowPrompt.cjs.map +1 -0
  81. package/dist/tui/workflow/workflowPrompt.mjs.map +1 -0
  82. package/dist/tui/workflow/workflowRunPanel.cjs.map +1 -0
  83. package/dist/tui/workflow/workflowRunPanel.mjs.map +1 -0
  84. package/dist/tui/workflow/workflowStatusRow.cjs +2 -0
  85. package/dist/tui/workflow/workflowStatusRow.cjs.map +1 -0
  86. package/dist/tui/workflow/workflowStatusRow.mjs +2 -0
  87. package/dist/tui/workflow/workflowStatusRow.mjs.map +1 -0
  88. package/dist/tui/workflow/workflowStepMessage.cjs +2 -0
  89. package/dist/tui/workflow/workflowStepMessage.cjs.map +1 -0
  90. package/dist/tui/workflow/workflowStepMessage.mjs +2 -0
  91. package/dist/tui/workflow/workflowStepMessage.mjs.map +1 -0
  92. package/dist/tui/workflow/workflowToolRender.cjs +7 -0
  93. package/dist/tui/workflow/workflowToolRender.cjs.map +1 -0
  94. package/dist/tui/workflow/workflowToolRender.mjs +7 -0
  95. package/dist/tui/workflow/workflowToolRender.mjs.map +1 -0
  96. package/dist/{types.cjs → types/index.cjs} +1 -1
  97. package/dist/types/index.cjs.map +1 -0
  98. package/dist/{types.mjs → types/index.mjs} +1 -1
  99. package/dist/types/index.mjs.map +1 -0
  100. package/llms.txt +8 -0
  101. package/package.json +20 -11
  102. package/dist/dispatcher.cjs.map +0 -1
  103. package/dist/dispatcher.d.cts.map +0 -1
  104. package/dist/dispatcher.d.mts.map +0 -1
  105. package/dist/dispatcher.mjs.map +0 -1
  106. package/dist/extension.cjs +0 -2
  107. package/dist/extension.cjs.map +0 -1
  108. package/dist/extension.d.cts.map +0 -1
  109. package/dist/extension.d.mts.map +0 -1
  110. package/dist/extension.mjs +0 -2
  111. package/dist/extension.mjs.map +0 -1
  112. package/dist/leader.cjs +0 -2
  113. package/dist/leader.cjs.map +0 -1
  114. package/dist/leader.mjs +0 -2
  115. package/dist/leader.mjs.map +0 -1
  116. package/dist/types.cjs.map +0 -1
  117. package/dist/types.mjs.map +0 -1
  118. package/dist/workflow/backgroundWork.cjs.map +0 -1
  119. package/dist/workflow/backgroundWork.mjs.map +0 -1
  120. package/dist/workflow/doomOverlay.cjs.map +0 -1
  121. package/dist/workflow/doomOverlay.mjs.map +0 -1
  122. package/dist/workflow/overlayText.cjs.map +0 -1
  123. package/dist/workflow/overlayText.mjs.map +0 -1
  124. package/dist/workflow/piExtension.cjs +0 -11
  125. package/dist/workflow/piExtension.cjs.map +0 -1
  126. package/dist/workflow/piExtension.mjs +0 -11
  127. package/dist/workflow/piExtension.mjs.map +0 -1
  128. package/dist/workflow/piToolBridge.cjs.map +0 -1
  129. package/dist/workflow/piToolBridge.mjs.map +0 -1
  130. package/dist/workflow/piTools.cjs +0 -4
  131. package/dist/workflow/piTools.cjs.map +0 -1
  132. package/dist/workflow/piTools.mjs +0 -4
  133. package/dist/workflow/piTools.mjs.map +0 -1
  134. package/dist/workflow/rendering.cjs.map +0 -1
  135. package/dist/workflow/rendering.mjs.map +0 -1
  136. package/dist/workflow/workflowFooterStatus.cjs +0 -2
  137. package/dist/workflow/workflowFooterStatus.cjs.map +0 -1
  138. package/dist/workflow/workflowFooterStatus.mjs +0 -2
  139. package/dist/workflow/workflowFooterStatus.mjs.map +0 -1
  140. package/dist/workflow/workflowInspector.cjs +0 -2
  141. package/dist/workflow/workflowInspector.cjs.map +0 -1
  142. package/dist/workflow/workflowInspector.mjs +0 -2
  143. package/dist/workflow/workflowLauncher.cjs +0 -2
  144. package/dist/workflow/workflowLauncher.cjs.map +0 -1
  145. package/dist/workflow/workflowLauncher.mjs +0 -2
  146. package/dist/workflow/workflowLauncher.mjs.map +0 -1
  147. package/dist/workflow/workflowOverlay.cjs.map +0 -1
  148. package/dist/workflow/workflowOverlay.mjs.map +0 -1
  149. package/dist/workflow/workflowPicker.cjs.map +0 -1
  150. package/dist/workflow/workflowPicker.mjs.map +0 -1
  151. package/dist/workflow/workflowPrompt.cjs.map +0 -1
  152. package/dist/workflow/workflowPrompt.mjs.map +0 -1
  153. package/dist/workflow/workflowRunPanel.cjs.map +0 -1
  154. package/dist/workflow/workflowRunPanel.mjs.map +0 -1
  155. package/dist/workflow/workflowStatusRow.cjs +0 -2
  156. package/dist/workflow/workflowStatusRow.cjs.map +0 -1
  157. package/dist/workflow/workflowStatusRow.mjs +0 -2
  158. package/dist/workflow/workflowStatusRow.mjs.map +0 -1
  159. package/dist/workflow/workflowStepMessage.cjs +0 -2
  160. package/dist/workflow/workflowStepMessage.cjs.map +0 -1
  161. package/dist/workflow/workflowStepMessage.mjs +0 -2
  162. package/dist/workflow/workflowStepMessage.mjs.map +0 -1
  163. /package/dist/{dispatcher.cjs → adapters/pi/dispatcher.cjs} +0 -0
  164. /package/dist/{dispatcher.mjs → adapters/pi/dispatcher.mjs} +0 -0
  165. /package/dist/{workflow → services/workflow}/backgroundWork.cjs +0 -0
  166. /package/dist/{workflow → services/workflow}/backgroundWork.mjs +0 -0
  167. /package/dist/{workflow → tui/workflow}/doomOverlay.cjs +0 -0
  168. /package/dist/{workflow → tui/workflow}/doomOverlay.mjs +0 -0
  169. /package/dist/{workflow → tui/workflow}/overlayText.cjs +0 -0
  170. /package/dist/{workflow → tui/workflow}/overlayText.mjs +0 -0
  171. /package/dist/{workflow → tui/workflow}/rendering.cjs +0 -0
  172. /package/dist/{workflow → tui/workflow}/rendering.mjs +0 -0
  173. /package/dist/{workflow → tui/workflow}/workflowOverlay.cjs +0 -0
  174. /package/dist/{workflow → tui/workflow}/workflowOverlay.mjs +0 -0
  175. /package/dist/{workflow → tui/workflow}/workflowPicker.cjs +0 -0
  176. /package/dist/{workflow → tui/workflow}/workflowPicker.mjs +0 -0
  177. /package/dist/{workflow → tui/workflow}/workflowPrompt.cjs +0 -0
  178. /package/dist/{workflow → tui/workflow}/workflowPrompt.mjs +0 -0
  179. /package/dist/{workflow → tui/workflow}/workflowRunPanel.cjs +0 -0
  180. /package/dist/{workflow → tui/workflow}/workflowRunPanel.mjs +0 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"piTools.mjs","names":[],"sources":["../../../../src/adapters/pi/workflow/piTools.ts"],"sourcesContent":["import { resolveRootSessionId } from '@agimon-ai/doompi-extension-contracts/child-process';\nimport {\n createEmbeddedWorkflowFeature,\n type EmbeddedWorkflowFeature,\n type WorkflowRunRecord,\n} from '@agimon-ai/workflow-mcp';\nimport type {\n AgentToolResult,\n AgentToolUpdateCallback,\n ExtensionAPI,\n ExtensionContext,\n} from '@earendil-works/pi-coding-agent';\nimport type { CallToolResult } from '@modelcontextprotocol/sdk/types.js';\nimport { z } from 'zod';\nimport { type WorkflowRunInput, workflowRunInputSchema } from '../../../schemas/workflowPi.ts';\nimport { renderWorkflowToolCall, renderWorkflowToolResult } from '../../../tui/workflow/workflowToolRender.ts';\nimport {\n launchedRunSummary,\n launchHandoffSummary,\n PI_SESSION_ENV,\n resolveMaxConcurrent,\n toAgentToolResult,\n toolResultText,\n withOptions,\n} from './piToolBridge';\n\nconst AGIFLOW_JOB_ID_ENV = 'AGIFLOW_JOB_ID';\nconst AGIFLOW_JOB_KIND_ENV = 'AGIFLOW_JOB_KIND';\n\nconst PI_LAUNCH_FIELDS = {\n workflowPath: true,\n workspace: true,\n runner: true,\n name: true,\n env: true,\n prompt: true,\n job: true,\n inputs: true,\n} as const;\n\nexport const WORKFLOW_PI_TOOL_NAMES = ['list_workflows', 'launch_workflow', 'workflow_run'] as const;\nconst [LIST_WORKFLOWS_TOOL_NAME, LAUNCH_WORKFLOW_TOOL_NAME, WORKFLOW_RUN_TOOL_NAME] = WORKFLOW_PI_TOOL_NAMES;\n\ntype WorkflowRunSelector = Pick<WorkflowRunInput, 'runKey' | 'workspace'>;\n\nexport type WorkflowLaunchInput = z.infer<ReturnType<EmbeddedWorkflowFeature['runTool']['getInputSchema']>>;\n\n/**\n * Where a launch was answered from, which decides what the caller is told.\n *\n * Tagged rather than inferred from an absent field: the failure carries an\n * `unknown`, and `unknown` includes `undefined`, so only a tag can tell a\n * rejection apart from a result.\n */\ntype LaunchOutcome = { kind: 'value'; result: CallToolResult } | { kind: 'error'; error: unknown };\ntype LaunchAck =\n | { kind: 'settled'; outcome: LaunchOutcome }\n | { kind: 'record'; record: WorkflowRunRecord }\n | { kind: 'handoff' };\n\n/** How often the registry is asked whether the launch has produced a run. */\nconst LAUNCH_ACK_POLL_MS = 500;\n/**\n * How long to wait for that before answering without a run key.\n *\n * Long enough to cover a launcher that is merely slow, short enough that a\n * caller is never left holding a turn open for a launcher that will not answer.\n */\nconst LAUNCH_ACK_TIMEOUT_MS = 15_000;\n\nexport interface LaunchedRunQuery {\n sessionId: string | undefined;\n /** Epoch milliseconds the launch began, so an earlier run cannot match. */\n since: number;\n workflowPath: string;\n}\n\nexport interface WorkflowLaunchExecutorDependencies {\n activeRunCount?: () => Promise<number>;\n onLaunch?: (ctx: ExtensionContext) => Promise<void> | void;\n observeSession?: (sessionId: string | undefined) => void;\n rejectRunner?: (workflowPath: string, runner: string) => string | undefined;\n runTool: EmbeddedWorkflowFeature['runTool'];\n trackPendingRun: <T>(run: Promise<T>) => Promise<T>;\n /**\n * The run this launch registered, or undefined while none has appeared.\n *\n * Its presence is what lets a launch answer on the registry instead of on the\n * launcher process exiting. Left out, the launch waits for the process, which\n * is the behaviour every caller had before.\n */\n findLaunchedRun?: (query: LaunchedRunQuery) => Promise<WorkflowRunRecord | undefined>;\n /** Report a launch that fails after this call already answered \"started\". */\n onLateFailure?: (error: unknown, ctx: ExtensionContext) => void;\n launchAckPollMs?: number;\n launchAckTimeoutMs?: number;\n}\n\nexport interface WorkflowLaunchExecutor {\n execute(\n input: WorkflowLaunchInput,\n ctx: ExtensionContext,\n onUpdate?: AgentToolUpdateCallback<{ tool: string }>,\n ): Promise<CallToolResult>;\n}\n\nexport interface WorkflowPiToolDependencies {\n launchExecutor?: WorkflowLaunchExecutor;\n onLaunch?: (ctx: ExtensionContext) => Promise<void> | void;\n activeRunCount?: () => Promise<number>;\n followRun?: (input: WorkflowRunSelector, ctx: ExtensionContext) => Promise<string>;\n tailRun?: (input: WorkflowRunSelector, ctx: ExtensionContext) => Promise<string>;\n openRun?: (input: WorkflowRunSelector, ctx: ExtensionContext) => Promise<string>;\n rejectRunner?: (workflowPath: string, runner: string) => string | undefined;\n requireSessionRun?: (input: WorkflowRunSelector, sessionId: string | undefined) => Promise<WorkflowRunRecord>;\n /**\n * Resolve a failed recovery target globally.\n *\n * Recovery is the one ownership-transferring action: a replacement Pi\n * session must be able to adopt a terminal run whose launching session is\n * gone. Every non-recovery action remains session-scoped.\n */\n requireRecoverableRun?: (input: WorkflowRunSelector) => Promise<WorkflowRunRecord>;\n readRecoveryEvidence?: (input: WorkflowRunSelector, ctx: ExtensionContext) => Promise<string>;\n /**\n * Hand a recovery to a terminal launcher, returning what to tell the caller.\n *\n * Undefined means this recovery cannot be delegated, and the in-process\n * replay below is used instead.\n */\n delegateRecovery?: (\n input: { runKey: string; workspace?: string; runner?: string },\n ctx: ExtensionContext,\n ) => Promise<string | undefined>;\n observeSession?: (sessionId: string | undefined) => void;\n trackPendingRun?: <T>(run: Promise<T>) => Promise<T>;\n feature?: EmbeddedWorkflowFeature;\n recoverTool?: {\n execute(input: {\n dryRun?: boolean;\n job?: string;\n runKey: string;\n runner?: string;\n workspace?: string;\n }): Promise<CallToolResult>;\n };\n controlTool?: ReturnType<EmbeddedWorkflowFeature['createControlTool']>;\n runTool?: EmbeddedWorkflowFeature['runTool'];\n listWorkflowsTool?: EmbeddedWorkflowFeature['listWorkflowsTool'];\n}\n\nfunction reportProgress(\n onUpdate: AgentToolUpdateCallback<{ tool: string }> | undefined,\n tool: string,\n message: string,\n): void {\n onUpdate?.({ content: [{ type: 'text', text: message }], details: { tool } });\n}\n\nfunction appendGuidance(\n result: AgentToolResult<{ tool: string }>,\n guidance: string,\n): AgentToolResult<{ tool: string }> {\n return { ...result, content: [...result.content, { type: 'text', text: guidance }] };\n}\n\nfunction statusAction(runKey: string): string {\n return `workflow_run {\"action\":\"status\",\"runKey\":${JSON.stringify(runKey)}}`;\n}\n\nfunction actionGuidance(action: WorkflowRunInput['action'], runKey: string): string[] {\n const status = statusAction(runKey);\n if (action === 'stop') return [`Next: call ${status} to confirm the run reached a terminal stage.`];\n if (action === 'recover') {\n return [\n `Report the recovery failure and do not retry it blindly: call ${status} first.`,\n 'Ask the user before launching fresh work when the recorded recovery is no longer valid.',\n ];\n }\n return [`Next: call ${status} to verify the recorded execution state.`];\n}\n\nconst launchFailureOptions = [\n 'Report the failure to the user with the error above. A launch failure is usually a bad workflow path, a missing launcher, or a workflow whose own pre-conditions refused.',\n 'list_workflows: confirm the workflow path exists and its description matches the work, if the path may be wrong.',\n 'workflow_run with action status: check whether an earlier attempt is already running before launching again.',\n];\n\n/**\n * An Error, whatever the launch rejected with.\n *\n * A rejection reaches here as `unknown` because it crossed a promise boundary,\n * and rethrowing that as-is hands Pi's tool layer something it cannot read a\n * message or a stack off. The original is kept as the cause rather than\n * flattened into a string.\n */\nfunction asError(value: unknown): Error {\n return value instanceof Error ? value : new Error(String(value), { cause: value });\n}\n\n/** Resolves to undefined, so a race against it reads as \"nothing settled yet\". */\nfunction sleep(ms: number): Promise<undefined> {\n return new Promise((settle) => {\n const timer = setTimeout(() => settle(undefined), ms);\n timer.unref?.();\n });\n}\n\n/**\n * Wait for whichever answer arrives first: the launch settling, the run\n * appearing in the registry, or the budget running out.\n *\n * A launch that delegates to a terminal launcher settles when that launcher's\n * whole process chain closes, which on a loaded machine has been measured\n * minutes after the run itself was registered and working. The registry is the\n * earlier and more truthful signal: a run recorded there is a run that started.\n */\nasync function awaitLaunchAck(\n settled: Promise<LaunchOutcome>,\n dependencies: WorkflowLaunchExecutorDependencies,\n query: LaunchedRunQuery,\n): Promise<LaunchAck> {\n const findLaunchedRun = dependencies.findLaunchedRun;\n if (!findLaunchedRun) return { kind: 'settled', outcome: await settled };\n\n const pollMs = dependencies.launchAckPollMs ?? LAUNCH_ACK_POLL_MS;\n const deadline = Date.now() + (dependencies.launchAckTimeoutMs ?? LAUNCH_ACK_TIMEOUT_MS);\n for (;;) {\n const outcome = await Promise.race([settled, sleep(pollMs)]);\n if (outcome) return { kind: 'settled', outcome };\n // A registry read that throws must not fail the launch: the run may be\n // perfectly healthy, and the settled promise is still the fallback.\n const record = await findLaunchedRun(query).catch(() => undefined);\n if (record) return { kind: 'record', record };\n if (Date.now() >= deadline) return { kind: 'handoff' };\n }\n}\n\n/**\n * Carry a failure that lands after the caller was told the run started.\n *\n * Answering early trades the launcher's exit code for timeliness. Dropping that\n * code entirely is not part of the trade: a launch that fails a minute later\n * would otherwise leave a user waiting on a run that never existed.\n */\nfunction reportLateFailure(\n settled: Promise<LaunchOutcome>,\n dependencies: WorkflowLaunchExecutorDependencies,\n ctx: ExtensionContext,\n): void {\n void settled.then((outcome) => {\n if (outcome.kind === 'error') {\n dependencies.onLateFailure?.(asError(outcome.error), ctx);\n return;\n }\n if (outcome.result.isError) {\n dependencies.onLateFailure?.(new Error(toolResultText(outcome.result) || 'Workflow launch failed.'), ctx);\n }\n });\n}\n\nexport function createWorkflowLaunchExecutor(dependencies: WorkflowLaunchExecutorDependencies): WorkflowLaunchExecutor {\n return {\n async execute(input, ctx, onUpdate) {\n const sessionId = resolveRootSessionId(ctx.sessionManager.getSessionId());\n dependencies.observeSession?.(sessionId);\n const agiflowJobKind = input.env?.[AGIFLOW_JOB_KIND_ENV]?.trim();\n const agiflowJobId = input.env?.[AGIFLOW_JOB_ID_ENV]?.trim();\n if (Boolean(agiflowJobKind) !== Boolean(agiflowJobId)) {\n throw new Error('Agiflow workflow launches require AGIFLOW_JOB_KIND and AGIFLOW_JOB_ID together.');\n }\n if (agiflowJobKind && !input.prompt?.trim()) {\n throw new Error(\n 'Agiflow workflow launches require a non-empty prompt so user_prompt runs do not wait for terminal input.',\n );\n }\n reportProgress(onUpdate, LAUNCH_WORKFLOW_TOOL_NAME, 'Checking workflow capacity...');\n const maxConcurrent = resolveMaxConcurrent();\n const active = (await dependencies.activeRunCount?.()) ?? 0;\n if (active >= maxConcurrent) {\n throw new Error(\n withOptions(`This session is at capacity: ${active}/${maxConcurrent} workflows running.`, [\n 'Wait for a running workflow to finish, then launch again. The extension reports each one as it ends.',\n 'workflow_run with action status: show what is running, so the user can judge whether something is stuck.',\n 'workflow_run with action stop: free a slot by stopping a run the user no longer wants.',\n ]),\n );\n }\n\n if (input.runner) {\n const rejection = dependencies.rejectRunner?.(input.workflowPath, input.runner);\n if (rejection) throw new Error(rejection);\n }\n reportProgress(onUpdate, LAUNCH_WORKFLOW_TOOL_NAME, `Launching workflow ${input.workflowPath}...`);\n const workflowEnv = { ...input.env };\n if (process.env.AGIFLOW_DISPATCH_CONTEXT_FILE) {\n for (const key of [\n 'AGIFLOW_ORGANIZATION_ID',\n 'AGIFLOW_PROJECT_ID',\n 'AGIFLOW_DEVICE_ID',\n 'BACKEND_AGIFLOW_API_ENDPOINT',\n 'AGIFLOW_DISPATCH_SECRET_FILE',\n ] as const) {\n const hostValue = process.env[key];\n if (hostValue) workflowEnv[key] = hostValue;\n }\n }\n const since = Date.now();\n const launch = dependencies.trackPendingRun(\n dependencies.runTool.execute({\n ...input,\n env: { ...workflowEnv, [PI_SESSION_ENV]: sessionId },\n }),\n );\n // Folded into a value before anything races it. A promise this function\n // may stop awaiting must never be able to reject unobserved, which in\n // Node ends the whole process, Pi's TUI included.\n const settled: Promise<LaunchOutcome> = launch.then(\n (value): LaunchOutcome => ({ kind: 'value', result: value }),\n (error: unknown): LaunchOutcome => ({ kind: 'error', error }),\n );\n\n const ack = await awaitLaunchAck(settled, dependencies, {\n sessionId,\n since,\n workflowPath: input.workflowPath,\n });\n\n if (ack.kind === 'settled') {\n const outcome = ack.outcome;\n if (outcome.kind === 'error') throw asError(outcome.error);\n const result = outcome.result;\n if (result.isError)\n throw new Error(withOptions(toolResultText(result) || 'Workflow launch failed.', launchFailureOptions));\n await dependencies.onLaunch?.(ctx);\n return result;\n }\n\n reportLateFailure(settled, dependencies, ctx);\n await dependencies.onLaunch?.(ctx);\n return {\n content: [\n {\n type: 'text',\n text: ack.kind === 'record' ? launchedRunSummary(ack.record) : launchHandoffSummary(input.workflowPath),\n },\n ],\n };\n },\n };\n}\n\nexport function registerWorkflowPiTools(pi: ExtensionAPI, dependencies: WorkflowPiToolDependencies = {}): void {\n const feature = dependencies.feature ?? createEmbeddedWorkflowFeature();\n const runTool = dependencies.runTool ?? feature.runTool;\n const listWorkflowsTool = dependencies.listWorkflowsTool ?? feature.listWorkflowsTool;\n const recoverTool = dependencies.recoverTool ?? feature.createRecoverTool();\n const controlTool = dependencies.controlTool ?? feature.createControlTool();\n const trackPendingRun = dependencies.trackPendingRun ?? (<T>(run: Promise<T>): Promise<T> => run);\n const launchExecutor =\n dependencies.launchExecutor ??\n createWorkflowLaunchExecutor({\n activeRunCount: dependencies.activeRunCount,\n onLaunch: dependencies.onLaunch,\n observeSession: dependencies.observeSession,\n rejectRunner: dependencies.rejectRunner,\n runTool,\n trackPendingRun,\n });\n\n pi.registerTool({\n name: LIST_WORKFLOWS_TOOL_NAME,\n label: 'List Workflows',\n description: 'List the workflow definitions available in this repository.',\n promptSnippet: 'List available workflow definitions',\n parameters: z.toJSONSchema(listWorkflowsTool.getInputSchema()),\n renderShell: 'self',\n renderCall: (args, theme) =>\n renderWorkflowToolCall(LIST_WORKFLOWS_TOOL_NAME, args as Record<string, unknown>, theme),\n renderResult: (result, options, theme, context) =>\n renderWorkflowToolResult(\n LIST_WORKFLOWS_TOOL_NAME,\n context.args as Record<string, unknown>,\n result,\n { ...options, isError: context.isError },\n theme,\n ),\n async execute(_toolCallId, params) {\n return toAgentToolResult(\n LIST_WORKFLOWS_TOOL_NAME,\n await listWorkflowsTool.execute(\n params as Parameters<EmbeddedWorkflowFeature['listWorkflowsTool']['execute']>[0],\n ),\n );\n },\n });\n\n pi.registerTool({\n name: LAUNCH_WORKFLOW_TOOL_NAME,\n label: 'Launch Workflow',\n description:\n 'Start a workflow run. Returns once the run has started, not once it finishes; use workflow_run with action status or wait for the completion notice.',\n promptSnippet: 'Start a workflow run and return its identifier',\n promptGuidelines: [\n 'Before launching, call list_workflows and pick the workflow whose own description matches the work. A workflow description is the source of truth; never infer one from its filename. If nothing matches, say so and stop rather than launching an approximate fit.',\n 'launch_workflow returns when the run has STARTED, not when it has finished. Never report success from its result. Poll workflow_run with action status, or wait for the completion notice the extension delivers on its own.',\n 'When dispatching Agiflow work, pass AGIFLOW_JOB_KIND and AGIFLOW_JOB_ID together through env plus a non-empty prompt. The id alone is not a key, and omitting prompt leaves user_prompt workflows waiting for terminal input.',\n 'A workflow claims its own job in its pre step. Never claim a job yourself. JOB_ALREADY_CLAIMED or HTTP 409 means another worker won the race, so report contention and do not unlock, release, or retry automatically.',\n 'If a result says WORKFLOW_NOT_OWNED or a release failed, inspect current ownership and ask the user before any release or unlock. Never force another worker’s lock.',\n 'If a running workflow is not found, call workflow_run with action status to re-check current state before retrying.',\n 'The per-session concurrency ceiling is a real limit. If launch_workflow refuses, wait for a run to finish; do not launch through the shell to get around it.',\n ],\n parameters: z.toJSONSchema(runTool.getInputSchema().pick(PI_LAUNCH_FIELDS)),\n renderShell: 'self',\n renderCall: (args, theme) =>\n renderWorkflowToolCall(LAUNCH_WORKFLOW_TOOL_NAME, args as Record<string, unknown>, theme),\n renderResult: (result, options, theme, context) =>\n renderWorkflowToolResult(\n LAUNCH_WORKFLOW_TOOL_NAME,\n context.args as Record<string, unknown>,\n result,\n { ...options, isError: context.isError },\n theme,\n ),\n async execute(_toolCallId, params, _signal, onUpdate, ctx) {\n const input = params as WorkflowLaunchInput;\n const result = await launchExecutor.execute(input, ctx, onUpdate);\n const adapted = toAgentToolResult(LAUNCH_WORKFLOW_TOOL_NAME, result, launchFailureOptions);\n return appendGuidance(\n adapted,\n 'Next: call workflow_run with action status and verify the recorded stage before reporting the workflow outcome.',\n );\n },\n });\n\n pi.registerTool({\n name: WORKFLOW_RUN_TOOL_NAME,\n label: 'Workflow Run',\n description:\n 'Inspect or control one workflow run. Use action status, tail, recovery-evidence, follow, open, pause, resume, stop, or recover. Every action requires an exact run key.',\n promptSnippet: 'Inspect or control a workflow run',\n promptGuidelines: [\n 'Use action status as the authority for stage, outcome, execution state, and pause cursor. A run that started is not a run that succeeded.',\n 'Use action pause or resume for cooperative lifecycle control. Pause is not stop: the run keeps its stage running, capacity, services, and heartbeat while paused.',\n 'Use action stop only when the user requests stopping a run. A user-requested stop is not a failure and needs no failure diagnosis.',\n 'Do not use follow or tail merely to wait for a healthy run; use status or the completion notice. Tail returns chat-safe status without raw PTY frames. Use follow only when the user asks to pin live progress, and open only when a human wants the recorded launcher foregrounded.',\n 'Use action recovery-evidence for terminal-only, durable status and artifacts before recovery. It is the only inspection path that may read a failed run from an earlier session and it never reads a live launcher.',\n 'Use action recover only after loading the `workflow-recovery` skill and gathering its required evidence. Recovery may adopt a terminal failed run from an earlier Pi session; it never grants cross-session control of running work. Never edit issue.md or repair.json, and verify real process and registry progress before recovery.',\n 'Never unlock or force-release an Agiflow claim from this tool.',\n ],\n parameters: z.toJSONSchema(workflowRunInputSchema),\n renderShell: 'self',\n renderCall: (args, theme) => renderWorkflowToolCall(WORKFLOW_RUN_TOOL_NAME, args as Record<string, unknown>, theme),\n renderResult: (result, options, theme, context) =>\n renderWorkflowToolResult(\n WORKFLOW_RUN_TOOL_NAME,\n context.args as Record<string, unknown>,\n result,\n { ...options, isError: context.isError },\n theme,\n ),\n async execute(_toolCallId, rawParams, _signal, onUpdate, ctx) {\n const input = workflowRunInputSchema.parse(rawParams);\n const sessionId = resolveRootSessionId(ctx.sessionManager.getSessionId());\n dependencies.observeSession?.(sessionId);\n const selector: WorkflowRunSelector = { runKey: input.runKey, workspace: input.workspace };\n const requireOwnedRun = async (): Promise<WorkflowRunRecord> => {\n if (!dependencies.requireSessionRun) {\n throw new Error('Workflow run ownership is not available in this session.');\n }\n return dependencies.requireSessionRun(selector, sessionId);\n };\n\n if (input.action === 'status') {\n const record = await requireOwnedRun();\n return {\n content: [{ type: 'text', text: JSON.stringify(record, null, 2) }],\n details: { tool: WORKFLOW_RUN_TOOL_NAME },\n };\n }\n if (input.action === 'tail') {\n if (!dependencies.tailRun) throw new Error('Reading workflow output is not available in this session.');\n const record = await requireOwnedRun();\n reportProgress(onUpdate, WORKFLOW_RUN_TOOL_NAME, `Reading output for workflow ${input.runKey}...`);\n await dependencies.tailRun(selector, ctx);\n return {\n content: [\n {\n type: 'text',\n text: [\n `Workflow: ${record.displayName} (${record.runKey})`,\n `Stage: ${record.effectiveStage ?? record.stage}`,\n 'Raw launcher output is hidden from chat. Use action follow or open to view it.',\n ].join('\\n'),\n },\n ],\n details: { tool: WORKFLOW_RUN_TOOL_NAME },\n };\n }\n if (input.action === 'recovery-evidence') {\n if (!dependencies.readRecoveryEvidence) {\n throw new Error('Reading workflow recovery evidence is not available in this session.');\n }\n reportProgress(onUpdate, WORKFLOW_RUN_TOOL_NAME, `Reading recovery evidence for workflow ${input.runKey}...`);\n return {\n content: [{ type: 'text', text: await dependencies.readRecoveryEvidence(selector, ctx) }],\n details: { tool: WORKFLOW_RUN_TOOL_NAME },\n };\n }\n if (input.action === 'follow') {\n if (!dependencies.followRun) throw new Error('Following workflow output is not available in this session.');\n await requireOwnedRun();\n reportProgress(onUpdate, WORKFLOW_RUN_TOOL_NAME, `Following workflow ${input.runKey}...`);\n return {\n content: [{ type: 'text', text: await dependencies.followRun(selector, ctx) }],\n details: { tool: WORKFLOW_RUN_TOOL_NAME },\n };\n }\n if (input.action === 'open') {\n if (!dependencies.openRun) throw new Error('Opening a workflow launcher is not available in this session.');\n await requireOwnedRun();\n reportProgress(onUpdate, WORKFLOW_RUN_TOOL_NAME, `Opening workflow ${input.runKey}...`);\n return {\n content: [{ type: 'text', text: await dependencies.openRun(selector, ctx) }],\n details: { tool: WORKFLOW_RUN_TOOL_NAME },\n };\n }\n\n if (input.action === 'recover') {\n if (!dependencies.requireRecoverableRun) {\n throw new Error('Workflow recovery ownership transfer is not available in this session.');\n }\n const recoverable = await dependencies.requireRecoverableRun(selector);\n const recoverySelector = { runKey: recoverable.runKey, workspace: recoverable.workspace };\n reportProgress(onUpdate, WORKFLOW_RUN_TOOL_NAME, `Recovering workflow ${input.runKey}...`);\n // A recovery replays the workflow, and the replay skips the launch\n // step, so every job runs in THIS process. For a workflow whose steps\n // are interactive that means an agent TUI spawned onto this process's\n // own terminal. Hand it to a launcher instead, where the workflow says\n // how. A dry run prints rather than executes, so it stays here.\n if (!input.dryRun) {\n const delegated = await dependencies.delegateRecovery?.({ ...recoverySelector, runner: input.runner }, ctx);\n if (delegated) {\n const guidance = actionGuidance(input.action, input.runKey);\n await dependencies.onLaunch?.(ctx);\n return {\n content: [{ type: 'text', text: [delegated, guidance[0]].join('\\n\\n') }],\n details: { tool: WORKFLOW_RUN_TOOL_NAME },\n };\n }\n }\n const recoverInput = {\n dryRun: input.dryRun,\n job: input.job,\n runKey: recoverySelector.runKey,\n runner: input.runner,\n workspace: recoverySelector.workspace,\n };\n const result = await trackPendingRun(recoverTool.execute(recoverInput));\n const guidance = actionGuidance(input.action, input.runKey);\n const adapted = toAgentToolResult(WORKFLOW_RUN_TOOL_NAME, result, guidance);\n await dependencies.onLaunch?.(ctx);\n return appendGuidance(adapted, guidance[0]!);\n }\n\n const ownedRun = await requireOwnedRun();\n if (!ownedRun.runId) throw new Error(`Workflow ${input.runKey} has no controllable run generation.`);\n reportProgress(onUpdate, WORKFLOW_RUN_TOOL_NAME, `Requesting ${input.action} for workflow ${input.runKey}...`);\n const result = await controlTool.execute({\n action: input.action,\n expectedRunId: ownedRun.runId,\n reason: input.reason,\n runKey: input.runKey,\n workspace: input.workspace,\n });\n const guidance = actionGuidance(input.action, input.runKey);\n const adapted = toAgentToolResult(WORKFLOW_RUN_TOOL_NAME, result, guidance);\n return appendGuidance(adapted, guidance[0]!);\n },\n });\n}\n"],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ const e=require("./piToolBridge.cjs"),t=require("../../../schemas/workflowPi.cjs");let n=require("node:path");function r(e){return e&&typeof e==`object`&&!Array.isArray(e)?{...e}:{}}function i(e){return r(e.on)}function a(e){let n=i(e).workflow_dispatch;if(n==null)return{};let r=t.workflowDispatchInputsSchema.safeParse(n);if(!r.success)throw Error(`Invalid workflow_dispatch input definitions: ${r.error.message}`);return r.data.inputs??{}}function o(t){return Error(e.toolResultText(t)||`Workflow launch failed.`)}function s(r){if(r.isError)throw o(r);let i;try{i=JSON.parse(e.toolResultText(r))}catch(e){throw Error(`Workflow catalog returned invalid JSON.`,{cause:e})}let a=t.workflowCatalogPageSchema.safeParse(i);if(!a.success)throw Error(`Workflow catalog response was invalid: ${a.error.message}`);return{directory:a.data.directory,hasNextPage:a.data.hasNextPage,page:a.data.page,pageSize:a.data.pageSize,workflows:a.data.workflows.map(e=>({description:e.description,name:e.name,path:(0,n.resolve)(a.data.directory,e.path),relativePath:e.path,tags:e.tags}))}}async function c(e,t){let n=[],r=1,i=!0;for(;i;){let a={directory:t,page:r,pageSize:100},o=s(await e.execute(a));n.push(...o.workflows),i=o.hasNextPage,r+=1}return n}async function l(e){for(;;){let t=await e.editor(`Workflow prompt`);if(t===void 0)return;let n=t.trim();if(n)return n;e.notify(`A workflow prompt is required.`,`warning`)}}async function u(e,t,n){let r=n.description?`${t}: ${n.description}`:`Workflow input: ${t}`;if(n.options&&n.options.length>0)return e.select(r,n.options);if(n.type===`boolean`)return e.select(r,[`true`,`false`]);for(;;){let i=await e.input(r,n.default);if(i===void 0)return;let a=i.trim()||n.default;if(a||!n.required)return a||void 0;e.notify(`A value is required for workflow input ${t}.`,`warning`)}}async function d(e,t){let n=i(e),r=Object.hasOwn(n,`user_prompt`),o=r?await l(t):void 0;if(r&&o===void 0)return;let s=a(e),c={};for(let[e,n]of Object.entries(s)){let r=await u(t,e,n);if(r===void 0&&n.required)return;r!==void 0&&(c[e]=r)}return{...o===void 0?{}:{prompt:o},...Object.keys(c).length===0?{}:{inputs:c}}}async function f(e,t){let n=await c(e.listWorkflowsTool,e.cwd);if(n.length===0){e.ui.notify(`No workflow definitions were found in this repository.`,`warning`);return}let r=await e.pickWorkflow(n);if(r===void 0)return;let i=e.parseWorkflow(r.path),a=e.compatibleRunners(i);if(a&&a.length===0)throw Error(`No compatible runner is available for this workflow.`);let o;if(a&&(o=await e.ui.select(`Select a workflow runner`,a),o===void 0))return;let s=await d(i,e.ui);if(s===void 0)return;let l={workflowPath:r.path,...o===void 0?{}:{runner:o},...typeof i.workspace==`string`?{workspace:i.workspace}:{},...s};return e.launch(l,t)}exports.collectWorkflowInputs=d,exports.loadWorkflowCatalog=c,exports.openWorkflowLaunchWizard=f,exports.parseWorkflowCatalogPage=s;
2
+ //# sourceMappingURL=workflowLauncher.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"workflowLauncher.cjs","names":["workflowDispatchInputsSchema","toolResultText","workflowCatalogPageSchema","resolve"],"sources":["../../../../src/adapters/pi/workflow/workflowLauncher.ts"],"sourcesContent":["import { resolve } from 'node:path';\nimport { type EmbeddedWorkflowFeature, type Workflow } from '@agimon-ai/workflow-mcp';\nimport type { ExtensionContext } from '@earendil-works/pi-coding-agent';\nimport type { CallToolResult } from '@modelcontextprotocol/sdk/types.js';\nimport {\n type DispatchInput,\n workflowCatalogPageSchema,\n workflowDispatchInputsSchema,\n} from '../../../schemas/workflowPi.ts';\nimport { toolResultText } from './piToolBridge';\nimport { type WorkflowLaunchExecutor, type WorkflowLaunchInput } from './piTools';\n\nconst CATALOG_PAGE_SIZE = 100;\nconst USER_PROMPT_TRIGGER = 'user_prompt';\nconst WORKFLOW_DISPATCH_TRIGGER = 'workflow_dispatch';\nconst BOOLEAN_INPUT_TYPE = 'boolean';\n\ntype WorkflowListTool = EmbeddedWorkflowFeature['listWorkflowsTool'];\ntype WorkflowListInput = Parameters<WorkflowListTool['execute']>[0];\n\nexport interface WorkflowCatalogEntry {\n description: string;\n name: string;\n path: string;\n relativePath: string;\n tags: string[];\n}\n\nexport interface WorkflowCatalogPage {\n directory: string;\n hasNextPage: boolean;\n page: number;\n pageSize: number;\n workflows: WorkflowCatalogEntry[];\n}\n\nexport interface WorkflowLauncherUi {\n editor(title: string, prefill?: string): Promise<string | undefined>;\n input(title: string, placeholder?: string): Promise<string | undefined>;\n notify(message: string, type?: 'info' | 'warning' | 'error'): void;\n select(title: string, options: string[]): Promise<string | undefined>;\n}\n\nexport interface WorkflowLaunchWizardOptions {\n compatibleRunners(workflow: Workflow): string[] | undefined;\n cwd: string;\n launch: WorkflowLaunchExecutor['execute'];\n listWorkflowsTool: WorkflowListTool;\n parseWorkflow(workflowPath: string): Workflow;\n /**\n * Workflow selection, injected rather than taken from `ui.select`: a catalog\n * of this size needs a scrollable, filterable surface, and keeping it out of\n * here leaves the wizard assertable without a terminal.\n */\n pickWorkflow(entries: readonly WorkflowCatalogEntry[]): Promise<WorkflowCatalogEntry | undefined>;\n ui: WorkflowLauncherUi;\n}\n\nfunction asRecord(value: unknown): Record<string, unknown> {\n return value && typeof value === 'object' && !Array.isArray(value) ? { ...value } : {};\n}\n\nfunction workflowTriggerBlock(workflow: Workflow): Record<string, unknown> {\n return asRecord(workflow.on);\n}\n\nfunction dispatchInputs(workflow: Workflow): Record<string, DispatchInput> {\n const dispatch = workflowTriggerBlock(workflow)[WORKFLOW_DISPATCH_TRIGGER];\n if (dispatch === null || dispatch === undefined) return {};\n const parsed = workflowDispatchInputsSchema.safeParse(dispatch);\n if (!parsed.success) throw new Error(`Invalid workflow_dispatch input definitions: ${parsed.error.message}`);\n return parsed.data.inputs ?? {};\n}\n\nfunction launchResultError(result: CallToolResult): Error {\n return new Error(toolResultText(result) || 'Workflow launch failed.');\n}\n\nexport function parseWorkflowCatalogPage(result: CallToolResult): WorkflowCatalogPage {\n if (result.isError) throw launchResultError(result);\n let value: unknown;\n try {\n value = JSON.parse(toolResultText(result));\n } catch (cause) {\n throw new Error('Workflow catalog returned invalid JSON.', { cause });\n }\n const parsed = workflowCatalogPageSchema.safeParse(value);\n if (!parsed.success) throw new Error(`Workflow catalog response was invalid: ${parsed.error.message}`);\n return {\n directory: parsed.data.directory,\n hasNextPage: parsed.data.hasNextPage,\n page: parsed.data.page,\n pageSize: parsed.data.pageSize,\n workflows: parsed.data.workflows.map((entry) => ({\n description: entry.description,\n name: entry.name,\n path: resolve(parsed.data.directory, entry.path),\n relativePath: entry.path,\n tags: entry.tags,\n })),\n };\n}\n\nexport async function loadWorkflowCatalog(\n listWorkflowsTool: WorkflowListTool,\n directory: string,\n): Promise<WorkflowCatalogEntry[]> {\n const entries: WorkflowCatalogEntry[] = [];\n let page = 1;\n let hasNextPage = true;\n while (hasNextPage) {\n const input: WorkflowListInput = { directory, page, pageSize: CATALOG_PAGE_SIZE };\n const result = await listWorkflowsTool.execute(input);\n const catalogPage = parseWorkflowCatalogPage(result);\n entries.push(...catalogPage.workflows);\n hasNextPage = catalogPage.hasNextPage;\n page += 1;\n }\n return entries;\n}\n\nasync function requestPrompt(ui: WorkflowLauncherUi): Promise<string | undefined> {\n while (true) {\n const value = await ui.editor('Workflow prompt');\n if (value === undefined) return undefined;\n const prompt = value.trim();\n if (prompt) return prompt;\n ui.notify('A workflow prompt is required.', 'warning');\n }\n}\n\nasync function requestDispatchInput(\n ui: WorkflowLauncherUi,\n name: string,\n input: DispatchInput,\n): Promise<string | undefined> {\n const title = input.description ? `${name}: ${input.description}` : `Workflow input: ${name}`;\n if (input.options && input.options.length > 0) return ui.select(title, input.options);\n if (input.type === BOOLEAN_INPUT_TYPE) return ui.select(title, ['true', 'false']);\n\n while (true) {\n const value = await ui.input(title, input.default);\n if (value === undefined) return undefined;\n const normalized = value.trim() || input.default;\n if (normalized || !input.required) return normalized || undefined;\n ui.notify(`A value is required for workflow input ${name}.`, 'warning');\n }\n}\n\nexport async function collectWorkflowInputs(\n workflow: Workflow,\n ui: WorkflowLauncherUi,\n): Promise<{ inputs?: Record<string, string>; prompt?: string } | undefined> {\n const triggers = workflowTriggerBlock(workflow);\n const requiresPrompt = Object.hasOwn(triggers, USER_PROMPT_TRIGGER);\n const prompt = requiresPrompt ? await requestPrompt(ui) : undefined;\n if (requiresPrompt && prompt === undefined) return undefined;\n\n const definitions = dispatchInputs(workflow);\n const inputs: Record<string, string> = {};\n for (const [name, definition] of Object.entries(definitions)) {\n const value = await requestDispatchInput(ui, name, definition);\n if (value === undefined && definition.required) return undefined;\n if (value !== undefined) inputs[name] = value;\n }\n return {\n ...(prompt === undefined ? {} : { prompt }),\n ...(Object.keys(inputs).length === 0 ? {} : { inputs }),\n };\n}\n\nexport async function openWorkflowLaunchWizard(\n options: WorkflowLaunchWizardOptions,\n ctx: ExtensionContext,\n): Promise<CallToolResult | undefined> {\n const entries = await loadWorkflowCatalog(options.listWorkflowsTool, options.cwd);\n if (entries.length === 0) {\n options.ui.notify('No workflow definitions were found in this repository.', 'warning');\n return undefined;\n }\n\n const selected = await options.pickWorkflow(entries);\n if (selected === undefined) return undefined;\n\n const workflow = options.parseWorkflow(selected.path);\n const runners = options.compatibleRunners(workflow);\n if (runners && runners.length === 0) throw new Error('No compatible runner is available for this workflow.');\n let runner: string | undefined;\n if (runners) {\n runner = await options.ui.select('Select a workflow runner', runners);\n if (runner === undefined) return undefined;\n }\n\n const triggerInputs = await collectWorkflowInputs(workflow, options.ui);\n if (triggerInputs === undefined) return undefined;\n const launchInput: WorkflowLaunchInput = {\n workflowPath: selected.path,\n ...(runner === undefined ? {} : { runner }),\n ...(typeof workflow.workspace === 'string' ? { workspace: workflow.workspace } : {}),\n ...triggerInputs,\n };\n return options.launch(launchInput, ctx);\n}\n"],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import{toolResultText as e}from"./piToolBridge.mjs";import{workflowCatalogPageSchema as t,workflowDispatchInputsSchema as n}from"../../../schemas/workflowPi.mjs";import{resolve as r}from"node:path";function i(e){return e&&typeof e==`object`&&!Array.isArray(e)?{...e}:{}}function a(e){return i(e.on)}function o(e){let t=a(e).workflow_dispatch;if(t==null)return{};let r=n.safeParse(t);if(!r.success)throw Error(`Invalid workflow_dispatch input definitions: ${r.error.message}`);return r.data.inputs??{}}function s(t){return Error(e(t)||`Workflow launch failed.`)}function c(n){if(n.isError)throw s(n);let i;try{i=JSON.parse(e(n))}catch(e){throw Error(`Workflow catalog returned invalid JSON.`,{cause:e})}let a=t.safeParse(i);if(!a.success)throw Error(`Workflow catalog response was invalid: ${a.error.message}`);return{directory:a.data.directory,hasNextPage:a.data.hasNextPage,page:a.data.page,pageSize:a.data.pageSize,workflows:a.data.workflows.map(e=>({description:e.description,name:e.name,path:r(a.data.directory,e.path),relativePath:e.path,tags:e.tags}))}}async function l(e,t){let n=[],r=1,i=!0;for(;i;){let a={directory:t,page:r,pageSize:100},o=c(await e.execute(a));n.push(...o.workflows),i=o.hasNextPage,r+=1}return n}async function u(e){for(;;){let t=await e.editor(`Workflow prompt`);if(t===void 0)return;let n=t.trim();if(n)return n;e.notify(`A workflow prompt is required.`,`warning`)}}async function d(e,t,n){let r=n.description?`${t}: ${n.description}`:`Workflow input: ${t}`;if(n.options&&n.options.length>0)return e.select(r,n.options);if(n.type===`boolean`)return e.select(r,[`true`,`false`]);for(;;){let i=await e.input(r,n.default);if(i===void 0)return;let a=i.trim()||n.default;if(a||!n.required)return a||void 0;e.notify(`A value is required for workflow input ${t}.`,`warning`)}}async function f(e,t){let n=a(e),r=Object.hasOwn(n,`user_prompt`),i=r?await u(t):void 0;if(r&&i===void 0)return;let s=o(e),c={};for(let[e,n]of Object.entries(s)){let r=await d(t,e,n);if(r===void 0&&n.required)return;r!==void 0&&(c[e]=r)}return{...i===void 0?{}:{prompt:i},...Object.keys(c).length===0?{}:{inputs:c}}}async function p(e,t){let n=await l(e.listWorkflowsTool,e.cwd);if(n.length===0){e.ui.notify(`No workflow definitions were found in this repository.`,`warning`);return}let r=await e.pickWorkflow(n);if(r===void 0)return;let i=e.parseWorkflow(r.path),a=e.compatibleRunners(i);if(a&&a.length===0)throw Error(`No compatible runner is available for this workflow.`);let o;if(a&&(o=await e.ui.select(`Select a workflow runner`,a),o===void 0))return;let s=await f(i,e.ui);if(s===void 0)return;let c={workflowPath:r.path,...o===void 0?{}:{runner:o},...typeof i.workspace==`string`?{workspace:i.workspace}:{},...s};return e.launch(c,t)}export{f as collectWorkflowInputs,l as loadWorkflowCatalog,p as openWorkflowLaunchWizard,c as parseWorkflowCatalogPage};
2
+ //# sourceMappingURL=workflowLauncher.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"workflowLauncher.mjs","names":[],"sources":["../../../../src/adapters/pi/workflow/workflowLauncher.ts"],"sourcesContent":["import { resolve } from 'node:path';\nimport { type EmbeddedWorkflowFeature, type Workflow } from '@agimon-ai/workflow-mcp';\nimport type { ExtensionContext } from '@earendil-works/pi-coding-agent';\nimport type { CallToolResult } from '@modelcontextprotocol/sdk/types.js';\nimport {\n type DispatchInput,\n workflowCatalogPageSchema,\n workflowDispatchInputsSchema,\n} from '../../../schemas/workflowPi.ts';\nimport { toolResultText } from './piToolBridge';\nimport { type WorkflowLaunchExecutor, type WorkflowLaunchInput } from './piTools';\n\nconst CATALOG_PAGE_SIZE = 100;\nconst USER_PROMPT_TRIGGER = 'user_prompt';\nconst WORKFLOW_DISPATCH_TRIGGER = 'workflow_dispatch';\nconst BOOLEAN_INPUT_TYPE = 'boolean';\n\ntype WorkflowListTool = EmbeddedWorkflowFeature['listWorkflowsTool'];\ntype WorkflowListInput = Parameters<WorkflowListTool['execute']>[0];\n\nexport interface WorkflowCatalogEntry {\n description: string;\n name: string;\n path: string;\n relativePath: string;\n tags: string[];\n}\n\nexport interface WorkflowCatalogPage {\n directory: string;\n hasNextPage: boolean;\n page: number;\n pageSize: number;\n workflows: WorkflowCatalogEntry[];\n}\n\nexport interface WorkflowLauncherUi {\n editor(title: string, prefill?: string): Promise<string | undefined>;\n input(title: string, placeholder?: string): Promise<string | undefined>;\n notify(message: string, type?: 'info' | 'warning' | 'error'): void;\n select(title: string, options: string[]): Promise<string | undefined>;\n}\n\nexport interface WorkflowLaunchWizardOptions {\n compatibleRunners(workflow: Workflow): string[] | undefined;\n cwd: string;\n launch: WorkflowLaunchExecutor['execute'];\n listWorkflowsTool: WorkflowListTool;\n parseWorkflow(workflowPath: string): Workflow;\n /**\n * Workflow selection, injected rather than taken from `ui.select`: a catalog\n * of this size needs a scrollable, filterable surface, and keeping it out of\n * here leaves the wizard assertable without a terminal.\n */\n pickWorkflow(entries: readonly WorkflowCatalogEntry[]): Promise<WorkflowCatalogEntry | undefined>;\n ui: WorkflowLauncherUi;\n}\n\nfunction asRecord(value: unknown): Record<string, unknown> {\n return value && typeof value === 'object' && !Array.isArray(value) ? { ...value } : {};\n}\n\nfunction workflowTriggerBlock(workflow: Workflow): Record<string, unknown> {\n return asRecord(workflow.on);\n}\n\nfunction dispatchInputs(workflow: Workflow): Record<string, DispatchInput> {\n const dispatch = workflowTriggerBlock(workflow)[WORKFLOW_DISPATCH_TRIGGER];\n if (dispatch === null || dispatch === undefined) return {};\n const parsed = workflowDispatchInputsSchema.safeParse(dispatch);\n if (!parsed.success) throw new Error(`Invalid workflow_dispatch input definitions: ${parsed.error.message}`);\n return parsed.data.inputs ?? {};\n}\n\nfunction launchResultError(result: CallToolResult): Error {\n return new Error(toolResultText(result) || 'Workflow launch failed.');\n}\n\nexport function parseWorkflowCatalogPage(result: CallToolResult): WorkflowCatalogPage {\n if (result.isError) throw launchResultError(result);\n let value: unknown;\n try {\n value = JSON.parse(toolResultText(result));\n } catch (cause) {\n throw new Error('Workflow catalog returned invalid JSON.', { cause });\n }\n const parsed = workflowCatalogPageSchema.safeParse(value);\n if (!parsed.success) throw new Error(`Workflow catalog response was invalid: ${parsed.error.message}`);\n return {\n directory: parsed.data.directory,\n hasNextPage: parsed.data.hasNextPage,\n page: parsed.data.page,\n pageSize: parsed.data.pageSize,\n workflows: parsed.data.workflows.map((entry) => ({\n description: entry.description,\n name: entry.name,\n path: resolve(parsed.data.directory, entry.path),\n relativePath: entry.path,\n tags: entry.tags,\n })),\n };\n}\n\nexport async function loadWorkflowCatalog(\n listWorkflowsTool: WorkflowListTool,\n directory: string,\n): Promise<WorkflowCatalogEntry[]> {\n const entries: WorkflowCatalogEntry[] = [];\n let page = 1;\n let hasNextPage = true;\n while (hasNextPage) {\n const input: WorkflowListInput = { directory, page, pageSize: CATALOG_PAGE_SIZE };\n const result = await listWorkflowsTool.execute(input);\n const catalogPage = parseWorkflowCatalogPage(result);\n entries.push(...catalogPage.workflows);\n hasNextPage = catalogPage.hasNextPage;\n page += 1;\n }\n return entries;\n}\n\nasync function requestPrompt(ui: WorkflowLauncherUi): Promise<string | undefined> {\n while (true) {\n const value = await ui.editor('Workflow prompt');\n if (value === undefined) return undefined;\n const prompt = value.trim();\n if (prompt) return prompt;\n ui.notify('A workflow prompt is required.', 'warning');\n }\n}\n\nasync function requestDispatchInput(\n ui: WorkflowLauncherUi,\n name: string,\n input: DispatchInput,\n): Promise<string | undefined> {\n const title = input.description ? `${name}: ${input.description}` : `Workflow input: ${name}`;\n if (input.options && input.options.length > 0) return ui.select(title, input.options);\n if (input.type === BOOLEAN_INPUT_TYPE) return ui.select(title, ['true', 'false']);\n\n while (true) {\n const value = await ui.input(title, input.default);\n if (value === undefined) return undefined;\n const normalized = value.trim() || input.default;\n if (normalized || !input.required) return normalized || undefined;\n ui.notify(`A value is required for workflow input ${name}.`, 'warning');\n }\n}\n\nexport async function collectWorkflowInputs(\n workflow: Workflow,\n ui: WorkflowLauncherUi,\n): Promise<{ inputs?: Record<string, string>; prompt?: string } | undefined> {\n const triggers = workflowTriggerBlock(workflow);\n const requiresPrompt = Object.hasOwn(triggers, USER_PROMPT_TRIGGER);\n const prompt = requiresPrompt ? await requestPrompt(ui) : undefined;\n if (requiresPrompt && prompt === undefined) return undefined;\n\n const definitions = dispatchInputs(workflow);\n const inputs: Record<string, string> = {};\n for (const [name, definition] of Object.entries(definitions)) {\n const value = await requestDispatchInput(ui, name, definition);\n if (value === undefined && definition.required) return undefined;\n if (value !== undefined) inputs[name] = value;\n }\n return {\n ...(prompt === undefined ? {} : { prompt }),\n ...(Object.keys(inputs).length === 0 ? {} : { inputs }),\n };\n}\n\nexport async function openWorkflowLaunchWizard(\n options: WorkflowLaunchWizardOptions,\n ctx: ExtensionContext,\n): Promise<CallToolResult | undefined> {\n const entries = await loadWorkflowCatalog(options.listWorkflowsTool, options.cwd);\n if (entries.length === 0) {\n options.ui.notify('No workflow definitions were found in this repository.', 'warning');\n return undefined;\n }\n\n const selected = await options.pickWorkflow(entries);\n if (selected === undefined) return undefined;\n\n const workflow = options.parseWorkflow(selected.path);\n const runners = options.compatibleRunners(workflow);\n if (runners && runners.length === 0) throw new Error('No compatible runner is available for this workflow.');\n let runner: string | undefined;\n if (runners) {\n runner = await options.ui.select('Select a workflow runner', runners);\n if (runner === undefined) return undefined;\n }\n\n const triggerInputs = await collectWorkflowInputs(workflow, options.ui);\n if (triggerInputs === undefined) return undefined;\n const launchInput: WorkflowLaunchInput = {\n workflowPath: selected.path,\n ...(runner === undefined ? {} : { runner }),\n ...(typeof workflow.workspace === 'string' ? { workspace: workflow.workspace } : {}),\n ...triggerInputs,\n };\n return options.launch(launchInput, ctx);\n}\n"],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- var e=require("../dispatcher.cjs").registerWorkflowDispatcherExtension;module.exports=e;
1
+ var e=require("../adapters/pi/dispatcher.cjs").registerWorkflowDispatcherExtension;module.exports=e;
2
2
  //# sourceMappingURL=dispatcher.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"dispatcher.cjs","names":["registerWorkflowDispatcherExtension"],"sources":["../../src/extensions/dispatcher.ts"],"sourcesContent":["import { registerWorkflowDispatcherExtension } from '../dispatcher.ts';\n\nexport default registerWorkflowDispatcherExtension;\n"],"mappings":"AAEA,IAAA,6BAAeA,CAAAA,CAAAA"}
1
+ {"version":3,"file":"dispatcher.cjs","names":["registerWorkflowDispatcherExtension"],"sources":["../../src/extensions/dispatcher.ts"],"sourcesContent":["import { registerWorkflowDispatcherExtension } from '../adapters/pi/dispatcher.ts';\n\nexport default registerWorkflowDispatcherExtension;\n"],"mappings":"AAEA,IAAA,yCAAeA,CAAAA,CAAAA"}
@@ -1,2 +1,2 @@
1
- import { registerWorkflowDispatcherExtension } from "../dispatcher.cjs";
1
+ import { registerWorkflowDispatcherExtension } from "../adapters/pi/dispatcher.cjs";
2
2
  export = registerWorkflowDispatcherExtension;
@@ -1,2 +1,2 @@
1
- import { registerWorkflowDispatcherExtension } from "../dispatcher.mjs";
1
+ import { registerWorkflowDispatcherExtension } from "../adapters/pi/dispatcher.mjs";
2
2
  export { registerWorkflowDispatcherExtension as default };
@@ -1,2 +1,2 @@
1
- import{registerWorkflowDispatcherExtension as e}from"../dispatcher.mjs";var t=e;export{t as default};
1
+ import{registerWorkflowDispatcherExtension as e}from"../adapters/pi/dispatcher.mjs";var t=e;export{t as default};
2
2
  //# sourceMappingURL=dispatcher.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"dispatcher.mjs","names":[],"sources":["../../src/extensions/dispatcher.ts"],"sourcesContent":["import { registerWorkflowDispatcherExtension } from '../dispatcher.ts';\n\nexport default registerWorkflowDispatcherExtension;\n"],"mappings":"wEAEA,IAAA,EAAe"}
1
+ {"version":3,"file":"dispatcher.mjs","names":[],"sources":["../../src/extensions/dispatcher.ts"],"sourcesContent":["import { registerWorkflowDispatcherExtension } from '../adapters/pi/dispatcher.ts';\n\nexport default registerWorkflowDispatcherExtension;\n"],"mappings":"oFAEA,IAAA,EAAe"}
@@ -1,2 +1,2 @@
1
- var e=require("../extension.cjs").registerWorkflowExtension;module.exports=e;
1
+ var e=require("../adapters/pi/extension.cjs").registerWorkflowExtension;module.exports=e;
2
2
  //# sourceMappingURL=pi.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"pi.cjs","names":["registerWorkflowExtension"],"sources":["../../src/extensions/pi.ts"],"sourcesContent":["import { registerWorkflowExtension } from '../extension.ts';\n\nexport default registerWorkflowExtension;\n"],"mappings":"AAEA,IAAA,4BAAeA,CAAAA,CAAAA"}
1
+ {"version":3,"file":"pi.cjs","names":["registerWorkflowExtension"],"sources":["../../src/extensions/pi.ts"],"sourcesContent":["import { registerWorkflowExtension } from '../adapters/pi/extension.ts';\n\nexport default registerWorkflowExtension;\n"],"mappings":"AAEA,IAAA,wCAAeA,CAAAA,CAAAA"}
@@ -1,2 +1,2 @@
1
- import { registerWorkflowExtension } from "../extension.cjs";
1
+ import { registerWorkflowExtension } from "../adapters/pi/extension.cjs";
2
2
  export = registerWorkflowExtension;
@@ -1,2 +1,2 @@
1
- import { registerWorkflowExtension } from "../extension.mjs";
1
+ import { registerWorkflowExtension } from "../adapters/pi/extension.mjs";
2
2
  export { registerWorkflowExtension as default };
@@ -1,2 +1,2 @@
1
- import{registerWorkflowExtension as e}from"../extension.mjs";var t=e;export{t as default};
1
+ import{registerWorkflowExtension as e}from"../adapters/pi/extension.mjs";var t=e;export{t as default};
2
2
  //# sourceMappingURL=pi.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"pi.mjs","names":[],"sources":["../../src/extensions/pi.ts"],"sourcesContent":["import { registerWorkflowExtension } from '../extension.ts';\n\nexport default registerWorkflowExtension;\n"],"mappings":"6DAEA,IAAA,EAAe"}
1
+ {"version":3,"file":"pi.mjs","names":[],"sources":["../../src/extensions/pi.ts"],"sourcesContent":["import { registerWorkflowExtension } from '../adapters/pi/extension.ts';\n\nexport default registerWorkflowExtension;\n"],"mappings":"yEAEA,IAAA,EAAe"}
package/dist/index.cjs CHANGED
@@ -1 +1 @@
1
- Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./extension.cjs"),t=require("./dispatcher.cjs");exports.registerWorkflowDispatcherExtension=t.registerWorkflowDispatcherExtension,exports.registerWorkflowExtension=e.registerWorkflowExtension;
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./adapters/pi/dispatcher.cjs"),t=require("./adapters/pi/extension.cjs");exports.registerWorkflowDispatcherExtension=e.registerWorkflowDispatcherExtension,exports.registerWorkflowExtension=t.registerWorkflowExtension;
package/dist/index.d.cts CHANGED
@@ -1,3 +1,3 @@
1
- import { registerWorkflowDispatcherExtension } from "./dispatcher.cjs";
2
- import { registerWorkflowExtension } from "./extension.cjs";
1
+ import { registerWorkflowDispatcherExtension } from "./adapters/pi/dispatcher.cjs";
2
+ import { registerWorkflowExtension } from "./adapters/pi/extension.cjs";
3
3
  export { registerWorkflowDispatcherExtension, registerWorkflowExtension };
package/dist/index.d.mts CHANGED
@@ -1,3 +1,3 @@
1
- import { registerWorkflowDispatcherExtension } from "./dispatcher.mjs";
2
- import { registerWorkflowExtension } from "./extension.mjs";
1
+ import { registerWorkflowDispatcherExtension } from "./adapters/pi/dispatcher.mjs";
2
+ import { registerWorkflowExtension } from "./adapters/pi/extension.mjs";
3
3
  export { registerWorkflowDispatcherExtension, registerWorkflowExtension };
package/dist/index.mjs CHANGED
@@ -1 +1 @@
1
- import{registerWorkflowExtension as e}from"./extension.mjs";import{registerWorkflowDispatcherExtension as t}from"./dispatcher.mjs";export{t as registerWorkflowDispatcherExtension,e as registerWorkflowExtension};
1
+ import{registerWorkflowDispatcherExtension as e}from"./adapters/pi/dispatcher.mjs";import{registerWorkflowExtension as t}from"./adapters/pi/extension.mjs";export{e as registerWorkflowDispatcherExtension,t as registerWorkflowExtension};
@@ -0,0 +1,2 @@
1
+ let e=require("zod");const t={runKey:e.z.string().min(1).describe(`Run key of the workflow run.`),workspace:e.z.string().optional().describe(`Workspace the run belongs to.`)},n=e.z.discriminatedUnion(`action`,[e.z.object({action:e.z.literal(`status`),...t}).strict(),e.z.object({action:e.z.literal(`tail`),...t}).strict(),e.z.object({action:e.z.literal(`recovery-evidence`),...t}).strict(),e.z.object({action:e.z.literal(`follow`),...t}).strict(),e.z.object({action:e.z.literal(`open`),...t}).strict(),e.z.object({action:e.z.literal(`pause`),...t,reason:e.z.string().optional()}).strict(),e.z.object({action:e.z.literal(`resume`),...t,reason:e.z.string().optional()}).strict(),e.z.object({action:e.z.literal(`stop`),...t,reason:e.z.string().optional()}).strict(),e.z.object({action:e.z.literal(`recover`),...t,dryRun:e.z.boolean().optional(),job:e.z.string().optional(),runner:e.z.string().optional()}).strict()]),r=e.z.object({description:e.z.string().optional().describe(`Description shown for the workflow input.`),required:e.z.boolean().optional().describe(`Whether the workflow input must be provided.`),default:e.z.string().optional().describe(`Default value for the workflow input.`),type:e.z.string().optional().describe(`Declared workflow input type.`),options:e.z.array(e.z.string()).optional().describe(`Allowed values for the workflow input.`)}).passthrough(),i=e.z.object({inputs:e.z.record(e.z.string(),r).optional().describe(`Workflow dispatch input definitions.`)}).passthrough(),a=e.z.object({directory:e.z.string().min(1).describe(`Directory searched for workflow definitions.`),hasNextPage:e.z.boolean().describe(`Whether another catalog page exists.`),page:e.z.number().int().positive().describe(`One-based catalog page number.`),pageSize:e.z.number().int().positive().describe(`Catalog page size.`),workflows:e.z.array(e.z.object({path:e.z.string().min(1).describe(`Workflow path relative to the catalog directory.`),name:e.z.string().describe(`Workflow display name.`),description:e.z.string().describe(`Workflow description.`),tags:e.z.array(e.z.string()).describe(`Workflow tags.`)}).passthrough()).describe(`Workflow definitions on this page.`)}).passthrough();exports.dispatchInputSchema=r,exports.workflowCatalogPageSchema=a,exports.workflowDispatchInputsSchema=i,exports.workflowRunInputSchema=n;
2
+ //# sourceMappingURL=workflowPi.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"workflowPi.cjs","names":["z"],"sources":["../../src/schemas/workflowPi.ts"],"sourcesContent":["import { z } from 'zod';\n\nconst RUN_SELECTOR_FIELDS = {\n runKey: z.string().min(1).describe('Run key of the workflow run.'),\n workspace: z.string().optional().describe('Workspace the run belongs to.'),\n} as const;\n\nexport const workflowRunInputSchema = z.discriminatedUnion('action', [\n z.object({ action: z.literal('status'), ...RUN_SELECTOR_FIELDS }).strict(),\n z.object({ action: z.literal('tail'), ...RUN_SELECTOR_FIELDS }).strict(),\n z.object({ action: z.literal('recovery-evidence'), ...RUN_SELECTOR_FIELDS }).strict(),\n z.object({ action: z.literal('follow'), ...RUN_SELECTOR_FIELDS }).strict(),\n z.object({ action: z.literal('open'), ...RUN_SELECTOR_FIELDS }).strict(),\n z.object({ action: z.literal('pause'), ...RUN_SELECTOR_FIELDS, reason: z.string().optional() }).strict(),\n z.object({ action: z.literal('resume'), ...RUN_SELECTOR_FIELDS, reason: z.string().optional() }).strict(),\n z.object({ action: z.literal('stop'), ...RUN_SELECTOR_FIELDS, reason: z.string().optional() }).strict(),\n z\n .object({\n action: z.literal('recover'),\n ...RUN_SELECTOR_FIELDS,\n dryRun: z.boolean().optional(),\n job: z.string().optional(),\n runner: z.string().optional(),\n })\n .strict(),\n]);\n\nexport type WorkflowRunInput = z.infer<typeof workflowRunInputSchema>;\n\nexport const dispatchInputSchema = z\n .object({\n description: z.string().optional().describe('Description shown for the workflow input.'),\n required: z.boolean().optional().describe('Whether the workflow input must be provided.'),\n default: z.string().optional().describe('Default value for the workflow input.'),\n type: z.string().optional().describe('Declared workflow input type.'),\n options: z.array(z.string()).optional().describe('Allowed values for the workflow input.'),\n })\n .passthrough();\n\nexport type DispatchInput = z.infer<typeof dispatchInputSchema>;\n\nexport const workflowDispatchInputsSchema = z\n .object({\n inputs: z.record(z.string(), dispatchInputSchema).optional().describe('Workflow dispatch input definitions.'),\n })\n .passthrough();\n\nexport const workflowCatalogPageSchema = z\n .object({\n directory: z.string().min(1).describe('Directory searched for workflow definitions.'),\n hasNextPage: z.boolean().describe('Whether another catalog page exists.'),\n page: z.number().int().positive().describe('One-based catalog page number.'),\n pageSize: z.number().int().positive().describe('Catalog page size.'),\n workflows: z\n .array(\n z\n .object({\n path: z.string().min(1).describe('Workflow path relative to the catalog directory.'),\n name: z.string().describe('Workflow display name.'),\n description: z.string().describe('Workflow description.'),\n tags: z.array(z.string()).describe('Workflow tags.'),\n })\n .passthrough(),\n )\n .describe('Workflow definitions on this page.'),\n })\n .passthrough();\n"],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import{z as e}from"zod";const t={runKey:e.string().min(1).describe(`Run key of the workflow run.`),workspace:e.string().optional().describe(`Workspace the run belongs to.`)},n=e.discriminatedUnion(`action`,[e.object({action:e.literal(`status`),...t}).strict(),e.object({action:e.literal(`tail`),...t}).strict(),e.object({action:e.literal(`recovery-evidence`),...t}).strict(),e.object({action:e.literal(`follow`),...t}).strict(),e.object({action:e.literal(`open`),...t}).strict(),e.object({action:e.literal(`pause`),...t,reason:e.string().optional()}).strict(),e.object({action:e.literal(`resume`),...t,reason:e.string().optional()}).strict(),e.object({action:e.literal(`stop`),...t,reason:e.string().optional()}).strict(),e.object({action:e.literal(`recover`),...t,dryRun:e.boolean().optional(),job:e.string().optional(),runner:e.string().optional()}).strict()]),r=e.object({description:e.string().optional().describe(`Description shown for the workflow input.`),required:e.boolean().optional().describe(`Whether the workflow input must be provided.`),default:e.string().optional().describe(`Default value for the workflow input.`),type:e.string().optional().describe(`Declared workflow input type.`),options:e.array(e.string()).optional().describe(`Allowed values for the workflow input.`)}).passthrough(),i=e.object({inputs:e.record(e.string(),r).optional().describe(`Workflow dispatch input definitions.`)}).passthrough(),a=e.object({directory:e.string().min(1).describe(`Directory searched for workflow definitions.`),hasNextPage:e.boolean().describe(`Whether another catalog page exists.`),page:e.number().int().positive().describe(`One-based catalog page number.`),pageSize:e.number().int().positive().describe(`Catalog page size.`),workflows:e.array(e.object({path:e.string().min(1).describe(`Workflow path relative to the catalog directory.`),name:e.string().describe(`Workflow display name.`),description:e.string().describe(`Workflow description.`),tags:e.array(e.string()).describe(`Workflow tags.`)}).passthrough()).describe(`Workflow definitions on this page.`)}).passthrough();export{r as dispatchInputSchema,a as workflowCatalogPageSchema,i as workflowDispatchInputsSchema,n as workflowRunInputSchema};
2
+ //# sourceMappingURL=workflowPi.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"workflowPi.mjs","names":[],"sources":["../../src/schemas/workflowPi.ts"],"sourcesContent":["import { z } from 'zod';\n\nconst RUN_SELECTOR_FIELDS = {\n runKey: z.string().min(1).describe('Run key of the workflow run.'),\n workspace: z.string().optional().describe('Workspace the run belongs to.'),\n} as const;\n\nexport const workflowRunInputSchema = z.discriminatedUnion('action', [\n z.object({ action: z.literal('status'), ...RUN_SELECTOR_FIELDS }).strict(),\n z.object({ action: z.literal('tail'), ...RUN_SELECTOR_FIELDS }).strict(),\n z.object({ action: z.literal('recovery-evidence'), ...RUN_SELECTOR_FIELDS }).strict(),\n z.object({ action: z.literal('follow'), ...RUN_SELECTOR_FIELDS }).strict(),\n z.object({ action: z.literal('open'), ...RUN_SELECTOR_FIELDS }).strict(),\n z.object({ action: z.literal('pause'), ...RUN_SELECTOR_FIELDS, reason: z.string().optional() }).strict(),\n z.object({ action: z.literal('resume'), ...RUN_SELECTOR_FIELDS, reason: z.string().optional() }).strict(),\n z.object({ action: z.literal('stop'), ...RUN_SELECTOR_FIELDS, reason: z.string().optional() }).strict(),\n z\n .object({\n action: z.literal('recover'),\n ...RUN_SELECTOR_FIELDS,\n dryRun: z.boolean().optional(),\n job: z.string().optional(),\n runner: z.string().optional(),\n })\n .strict(),\n]);\n\nexport type WorkflowRunInput = z.infer<typeof workflowRunInputSchema>;\n\nexport const dispatchInputSchema = z\n .object({\n description: z.string().optional().describe('Description shown for the workflow input.'),\n required: z.boolean().optional().describe('Whether the workflow input must be provided.'),\n default: z.string().optional().describe('Default value for the workflow input.'),\n type: z.string().optional().describe('Declared workflow input type.'),\n options: z.array(z.string()).optional().describe('Allowed values for the workflow input.'),\n })\n .passthrough();\n\nexport type DispatchInput = z.infer<typeof dispatchInputSchema>;\n\nexport const workflowDispatchInputsSchema = z\n .object({\n inputs: z.record(z.string(), dispatchInputSchema).optional().describe('Workflow dispatch input definitions.'),\n })\n .passthrough();\n\nexport const workflowCatalogPageSchema = z\n .object({\n directory: z.string().min(1).describe('Directory searched for workflow definitions.'),\n hasNextPage: z.boolean().describe('Whether another catalog page exists.'),\n page: z.number().int().positive().describe('One-based catalog page number.'),\n pageSize: z.number().int().positive().describe('Catalog page size.'),\n workflows: z\n .array(\n z\n .object({\n path: z.string().min(1).describe('Workflow path relative to the catalog directory.'),\n name: z.string().describe('Workflow display name.'),\n description: z.string().describe('Workflow description.'),\n tags: z.array(z.string()).describe('Workflow tags.'),\n })\n .passthrough(),\n )\n .describe('Workflow definitions on this page.'),\n })\n .passthrough();\n"],"mappings":"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"}
@@ -0,0 +1 @@
1
+ {"version":3,"file":"backgroundWork.cjs","names":["registerBackgroundWorkProvider"],"sources":["../../../src/services/workflow/backgroundWork.ts"],"sourcesContent":["/**\n * Background-work registration for workflow runs.\n *\n * DESIGN PATTERNS:\n * - Pi event-bus integration through the shared typed protocol\n * - Snapshot pattern: the extension pushes the current run set, the provider\n * only reads it\n *\n * CODING STANDARDS:\n * - Named exports only\n * - Explicit return types on exported functions\n *\n * AVOID:\n * - Process-global symbols or dynamic imports for extension communication\n * - Reporting runs owned by another Pi session\n */\n\nimport {\n type BackgroundWorkProviderHandle,\n registerBackgroundWorkProvider,\n} from '@agimon-ai/doompi-extension-contracts/background-work';\nimport type { ProtocolRuntime } from '@agimon-ai/doompi-extension-contracts/protocol';\n\nexport interface RunProviderItem {\n id: string;\n sessionId: string;\n}\n\nexport interface RunProviderHandle {\n /** Replace the reported run set. */\n update(items: RunProviderItem[]): void;\n /** Remove the registration. Safe to call when registration never happened. */\n dispose(): void;\n}\n\nconst PROVIDER_NAME = 'workflow-mcp';\n\n/**\n * Register a background-work provider for this session's workflow runs.\n *\n * The runtime pins all announcements to the active Pi session and validates\n * each snapshot before it reaches another extension.\n */\nexport function registerRunProvider(runtime: ProtocolRuntime): RunProviderHandle {\n let items: RunProviderItem[] = [];\n const provider: BackgroundWorkProviderHandle = registerBackgroundWorkProvider(runtime, {\n provider: PROVIDER_NAME,\n listActiveWork: () => items,\n });\n const handle: RunProviderHandle = {\n update(next) {\n items = next;\n provider.update();\n },\n dispose() {\n items = [];\n provider.dispose();\n },\n };\n return handle;\n}\n"],"mappings":"uEA2CA,SAAgB,EAAoB,EAA6C,CAC/E,IAAI,EAA2B,CAAC,EAC1B,GAAA,EAAyCA,EAAAA,+BAAAA,CAA+B,EAAS,CACrF,SAAU,eACV,mBAAsB,CACxB,CAAC,EAWD,MAAO,CATL,OAAO,EAAM,CACX,EAAQ,EACR,EAAS,OAAO,CAClB,EACA,SAAU,CACR,EAAQ,CAAC,EACT,EAAS,QAAQ,CACnB,CAEU,CACd"}
@@ -0,0 +1 @@
1
+ {"version":3,"file":"backgroundWork.mjs","names":[],"sources":["../../../src/services/workflow/backgroundWork.ts"],"sourcesContent":["/**\n * Background-work registration for workflow runs.\n *\n * DESIGN PATTERNS:\n * - Pi event-bus integration through the shared typed protocol\n * - Snapshot pattern: the extension pushes the current run set, the provider\n * only reads it\n *\n * CODING STANDARDS:\n * - Named exports only\n * - Explicit return types on exported functions\n *\n * AVOID:\n * - Process-global symbols or dynamic imports for extension communication\n * - Reporting runs owned by another Pi session\n */\n\nimport {\n type BackgroundWorkProviderHandle,\n registerBackgroundWorkProvider,\n} from '@agimon-ai/doompi-extension-contracts/background-work';\nimport type { ProtocolRuntime } from '@agimon-ai/doompi-extension-contracts/protocol';\n\nexport interface RunProviderItem {\n id: string;\n sessionId: string;\n}\n\nexport interface RunProviderHandle {\n /** Replace the reported run set. */\n update(items: RunProviderItem[]): void;\n /** Remove the registration. Safe to call when registration never happened. */\n dispose(): void;\n}\n\nconst PROVIDER_NAME = 'workflow-mcp';\n\n/**\n * Register a background-work provider for this session's workflow runs.\n *\n * The runtime pins all announcements to the active Pi session and validates\n * each snapshot before it reaches another extension.\n */\nexport function registerRunProvider(runtime: ProtocolRuntime): RunProviderHandle {\n let items: RunProviderItem[] = [];\n const provider: BackgroundWorkProviderHandle = registerBackgroundWorkProvider(runtime, {\n provider: PROVIDER_NAME,\n listActiveWork: () => items,\n });\n const handle: RunProviderHandle = {\n update(next) {\n items = next;\n provider.update();\n },\n dispose() {\n items = [];\n provider.dispose();\n },\n };\n return handle;\n}\n"],"mappings":"uGA2CA,SAAgB,EAAoB,EAA6C,CAC/E,IAAI,EAA2B,CAAC,EAC1B,EAAyC,EAA+B,EAAS,CACrF,SAAU,eACV,mBAAsB,CACxB,CAAC,EAWD,MAAO,CATL,OAAO,EAAM,CACX,EAAQ,EACR,EAAS,OAAO,CAClB,EACA,SAAU,CACR,EAAQ,CAAC,EACT,EAAS,QAAQ,CACnB,CAEU,CACd"}
@@ -0,0 +1,2 @@
1
+ function e(e,t,n,r){let i=n===void 0&&r;i&&e.narrate(`Workflow launched: ${t.displayName}.`),!(n!==`running`&&!i)&&(t.stage===`completed`&&e.narrate(`Workflow succeeded: ${t.displayName}.`),t.stage===`error`&&e.narrate(`Workflow failed: ${t.displayName}.`))}exports.narrateWorkflowTransition=e;
2
+ //# sourceMappingURL=workflowNarration.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"workflowNarration.cjs","names":[],"sources":["../../../src/services/workflow/workflowNarration.ts"],"sourcesContent":["import type { NarrationRequester } from '@agimon-ai/doompi-extension-contracts/narration';\nimport type { WorkflowRunRecord, WorkflowStage } from '@agimon-ai/workflow-mcp';\n\nconst RUNNING_STAGE: WorkflowStage = 'running';\nconst COMPLETED_STAGE: WorkflowStage = 'completed';\nconst ERROR_STAGE: WorkflowStage = 'error';\n\ntype NarratedWorkflow = Pick<WorkflowRunRecord, 'displayName' | 'stage'>;\n\n/**\n * Translate an observed workflow lifecycle transition into generic speech.\n * Voice owns delivery and ignores requests while its minor mode is inactive.\n */\nexport function narrateWorkflowTransition(\n requester: NarrationRequester,\n record: NarratedWorkflow,\n previousStage: WorkflowStage | undefined,\n startedDuringSession: boolean,\n): void {\n const newlyLaunched = previousStage === undefined && startedDuringSession;\n if (newlyLaunched) requester.narrate(`Workflow launched: ${record.displayName}.`);\n\n if (previousStage !== RUNNING_STAGE && !newlyLaunched) return;\n if (record.stage === COMPLETED_STAGE) requester.narrate(`Workflow succeeded: ${record.displayName}.`);\n if (record.stage === ERROR_STAGE) requester.narrate(`Workflow failed: ${record.displayName}.`);\n}\n"],"mappings":"AAaA,SAAgB,EACd,EACA,EACA,EACA,EACM,CACN,IAAM,EAAgB,IAAkB,IAAA,IAAa,EACjD,GAAe,EAAU,QAAQ,sBAAsB,EAAO,YAAY,EAAE,EAE5E,MAAkB,WAAiB,CAAC,KACpC,EAAO,QAAU,aAAiB,EAAU,QAAQ,uBAAuB,EAAO,YAAY,EAAE,EAChG,EAAO,QAAU,SAAa,EAAU,QAAQ,oBAAoB,EAAO,YAAY,EAAE,EAC/F"}
@@ -0,0 +1,2 @@
1
+ function e(e,t,n,r){let i=n===void 0&&r;i&&e.narrate(`Workflow launched: ${t.displayName}.`),!(n!==`running`&&!i)&&(t.stage===`completed`&&e.narrate(`Workflow succeeded: ${t.displayName}.`),t.stage===`error`&&e.narrate(`Workflow failed: ${t.displayName}.`))}export{e as narrateWorkflowTransition};
2
+ //# sourceMappingURL=workflowNarration.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"workflowNarration.mjs","names":[],"sources":["../../../src/services/workflow/workflowNarration.ts"],"sourcesContent":["import type { NarrationRequester } from '@agimon-ai/doompi-extension-contracts/narration';\nimport type { WorkflowRunRecord, WorkflowStage } from '@agimon-ai/workflow-mcp';\n\nconst RUNNING_STAGE: WorkflowStage = 'running';\nconst COMPLETED_STAGE: WorkflowStage = 'completed';\nconst ERROR_STAGE: WorkflowStage = 'error';\n\ntype NarratedWorkflow = Pick<WorkflowRunRecord, 'displayName' | 'stage'>;\n\n/**\n * Translate an observed workflow lifecycle transition into generic speech.\n * Voice owns delivery and ignores requests while its minor mode is inactive.\n */\nexport function narrateWorkflowTransition(\n requester: NarrationRequester,\n record: NarratedWorkflow,\n previousStage: WorkflowStage | undefined,\n startedDuringSession: boolean,\n): void {\n const newlyLaunched = previousStage === undefined && startedDuringSession;\n if (newlyLaunched) requester.narrate(`Workflow launched: ${record.displayName}.`);\n\n if (previousStage !== RUNNING_STAGE && !newlyLaunched) return;\n if (record.stage === COMPLETED_STAGE) requester.narrate(`Workflow succeeded: ${record.displayName}.`);\n if (record.stage === ERROR_STAGE) requester.narrate(`Workflow failed: ${record.displayName}.`);\n}\n"],"mappings":"AAaA,SAAgB,EACd,EACA,EACA,EACA,EACM,CACN,IAAM,EAAgB,IAAkB,IAAA,IAAa,EACjD,GAAe,EAAU,QAAQ,sBAAsB,EAAO,YAAY,EAAE,EAE5E,MAAkB,WAAiB,CAAC,KACpC,EAAO,QAAU,aAAiB,EAAU,QAAQ,uBAAuB,EAAO,YAAY,EAAE,EAChG,EAAO,QAAU,SAAa,EAAU,QAAQ,oBAAoB,EAAO,YAAY,EAAE,EAC/F"}
@@ -0,0 +1 @@
1
+ {"version":3,"file":"doomOverlay.cjs","names":["visibleWidth","truncateToWidth","padLine","frameLine","fitLine"],"sources":["../../../src/tui/workflow/doomOverlay.ts"],"sourcesContent":["import type { Theme } from '@earendil-works/pi-coding-agent';\nimport { type Component, truncateToWidth, visibleWidth } from '@earendil-works/pi-tui';\nimport { fitLine, frameLine, padLine } from './rendering.ts';\n\nconst DEFAULT_TERMINAL_ROWS = 24;\nconst FULL_CHROME_ROWS = 6;\n/** Two frame columns and two gutter columns, leaving content worth framing. */\nconst MIN_FRAMED_WIDTH = 8;\nconst GUTTER = ' ';\nconst GUTTER_COLUMNS = 2;\n/** Blank columns kept between a row's left cluster and its right-aligned status. */\nconst ROW_GAP = 2;\n/** A secondary segment thinner than this is dropped rather than shredded into a stub. */\nconst MIN_SEGMENT_WIDTH = 8;\nconst ELLIPSIS = '…';\nconst BORDER_COLOR = 'borderMuted';\nconst TEXT_COLOR = 'text';\n/** Filled key caps read as pressable, matching the doom mockups' footers. */\nconst HINT_CAP_BACKGROUND = 'selectedBg';\nconst HINT_SEPARATOR = ' ';\n/** A cap's two pad columns plus the space before its label. */\nconst HINT_CHROME_COLUMNS = 6;\nconst TITLE_COLOR = 'accent';\nconst SECONDARY_COLOR = 'dim';\nconst HORIZONTAL_BORDER = '─';\nconst VERTICAL_BORDER = '│';\nconst TOP_CORNERS = ['╭', '╮'] as const;\nconst DIVIDER_JOINTS = ['├', '┤'] as const;\nconst BOTTOM_CORNERS = ['╰', '╯'] as const;\n\nexport interface DoomOverlayTui {\n terminal?: {\n rows: number;\n columns?: number;\n };\n requestRender(force?: boolean): void;\n}\n\nexport interface DoomOverlayChrome {\n title: string;\n breadcrumb?: string;\n headerRight?: string;\n footer: string;\n footerRight?: string;\n /**\n * Key/label pairs rendered as filled caps, e.g. `[ enter ] edit subject`.\n * Takes precedence over `footer`, which stays for surfaces that want a plain\n * sentence rather than a key legend.\n */\n footerHints?: readonly (readonly [string, string])[];\n /**\n * Signature colour for the surface. When set, the frame takes it and the\n * title renders as a filled badge rather than plain accent text, matching the\n * doom mockups. Left unset, the frame stays muted and the title plain.\n */\n accent?: Parameters<Theme['fg']>[0];\n}\n\n/**\n * One signature colour for every doom overlay: the frame and the title badge.\n * Per-surface colours were tried and read as arbitrary, and they also borrowed\n * tokens that mean something else (`warning`, `syntaxNumber`), so a theme edit\n * elsewhere would have repainted a frame. Change it here to change them all.\n */\nexport const DOOM_OVERLAY_ACCENT = 'mdHeading' satisfies DoomOverlayChrome['accent'];\n\nexport const DOOM_FULLSCREEN_UI_OPTIONS = {\n overlay: true,\n overlayOptions: {\n anchor: 'top-left',\n width: '100%',\n maxHeight: '100%',\n margin: 0,\n },\n} as const;\n\n/**\n * Fits a secondary segment into `budget`, or drops it entirely when too few\n * columns remain for it to stay readable. Truncating everything to fit is what\n * makes a tight header read as one run-on string.\n */\nfunction fitSegment(text: string | undefined, budget: number): string {\n if (!text || budget <= 0) return '';\n return budget < Math.min(visibleWidth(text), MIN_SEGMENT_WIDTH) ? '' : truncateToWidth(text, budget, ELLIPSIS);\n}\n\nexport abstract class DoomOverlay implements Component {\n protected constructor(\n protected readonly tui: DoomOverlayTui,\n protected readonly theme: Theme,\n ) {}\n\n invalidate(): void {\n this.tui.requestRender();\n }\n\n render(width: number): string[] {\n const safeWidth = Math.max(0, Math.floor(width));\n const height = Math.max(0, Math.floor(this.tui.terminal?.rows ?? DEFAULT_TERMINAL_ROWS));\n if (safeWidth === 0 || height === 0) return [];\n\n const chrome = this.getChrome();\n if (height < FULL_CHROME_ROWS || safeWidth < MIN_FRAMED_WIDTH) {\n return this.renderCompact(chrome, safeWidth, height);\n }\n\n const innerWidth = safeWidth - GUTTER_COLUMNS;\n const contentWidth = innerWidth - GUTTER_COLUMNS;\n const bodyHeight = height - FULL_CHROME_ROWS;\n const body = this.renderBody(contentWidth, bodyHeight).slice(0, bodyHeight);\n while (body.length < bodyHeight) body.push('');\n\n const frame = chrome.accent ?? BORDER_COLOR;\n return [\n this.borderRow(TOP_CORNERS, innerWidth, frame),\n this.contentRow(this.headerRow(chrome, contentWidth), contentWidth, safeWidth, frame),\n this.borderRow(DIVIDER_JOINTS, innerWidth, frame, BORDER_COLOR),\n ...body.map((line) => this.contentRow(line, contentWidth, safeWidth, frame)),\n this.borderRow(DIVIDER_JOINTS, innerWidth, frame, BORDER_COLOR),\n this.contentRow(this.footerRow(chrome, contentWidth), contentWidth, safeWidth, frame),\n this.borderRow(BOTTOM_CORNERS, innerWidth, frame),\n ].map((line) => padLine(line, safeWidth));\n }\n\n protected abstract getChrome(): DoomOverlayChrome;\n\n protected abstract renderBody(width: number, height: number): string[];\n\n /**\n * Edge glyphs and the rule between them are coloured separately: the outer\n * rectangle carries the signature colour while an internal separator stays\n * muted, meeting the frame at accent-coloured junctions.\n */\n private borderRow(\n [left, right]: readonly [string, string],\n innerWidth: number,\n edge: DoomOverlayChrome['accent'] = BORDER_COLOR,\n fill: DoomOverlayChrome['accent'] = edge,\n ): string {\n const edgeColour = edge ?? BORDER_COLOR;\n return [\n this.theme.fg(edgeColour, left),\n this.theme.fg(fill ?? edgeColour, HORIZONTAL_BORDER.repeat(innerWidth)),\n this.theme.fg(edgeColour, right),\n ].join('');\n }\n\n private contentRow(\n content: string,\n contentWidth: number,\n width: number,\n colour: DoomOverlayChrome['accent'] = BORDER_COLOR,\n ): string {\n const edge = this.theme.fg(colour ?? BORDER_COLOR, VERTICAL_BORDER);\n return frameLine(`${GUTTER}${padLine(content, contentWidth)}${GUTTER}`, width, edge, edge);\n }\n\n private headerRow(chrome: DoomOverlayChrome, width: number): string {\n // A signature colour turns the title into a filled badge, as the mockups\n // show; without one it stays plain accent text.\n const accent = chrome.accent;\n const titleBudget = accent ? width - GUTTER_COLUMNS : width;\n const title = truncateToWidth(chrome.title, Math.max(0, titleBudget), ELLIPSIS);\n let left = accent\n ? this.theme.inverse(this.theme.bold(this.theme.fg(accent, ` ${title} `)))\n : this.theme.bold(this.theme.fg(TITLE_COLOR, title));\n let leftWidth = visibleWidth(title) + (accent ? GUTTER_COLUMNS : 0);\n\n // The breadcrumb claims room before the right status: it says where you are,\n // which outranks ambient context when the terminal is narrow.\n const breadcrumb = fitSegment(chrome.breadcrumb, width - leftWidth - ROW_GAP);\n if (breadcrumb) {\n left += `${' '.repeat(ROW_GAP)}${this.theme.fg(SECONDARY_COLOR, breadcrumb)}`;\n leftWidth += ROW_GAP + visibleWidth(breadcrumb);\n }\n return this.withRightStatus(left, leftWidth, chrome.headerRight, width);\n }\n\n private footerRow(chrome: DoomOverlayChrome, width: number): string {\n const footer = chrome.footerHints?.length\n ? this.hintLegend(chrome.footerHints, width)\n : this.theme.fg(SECONDARY_COLOR, truncateToWidth(chrome.footer, width, ELLIPSIS));\n return this.withRightStatus(footer, visibleWidth(footer), chrome.footerRight, width);\n }\n\n /**\n * Key legend with each key in a filled cap. Every segment sets its own colour\n * so the caps survive: a cap's reset would otherwise strip the surrounding\n * dim from the labels that follow it.\n */\n private hintLegend(hints: readonly (readonly [string, string])[], width: number): string {\n let legend = '';\n let used = 0;\n for (const [key, label] of hints) {\n const segmentWidth = visibleWidth(key) + visibleWidth(label) + HINT_CHROME_COLUMNS;\n if (used + segmentWidth > width) break;\n const cap = this.theme.bg(HINT_CAP_BACKGROUND, this.theme.fg(TEXT_COLOR, ` ${key} `));\n legend += `${legend ? HINT_SEPARATOR : ''}${cap}${this.theme.fg(SECONDARY_COLOR, ` ${label}`)}`;\n used += segmentWidth;\n }\n return legend;\n }\n\n /**\n * Right-aligns a status cluster, dropping it whole when the row cannot spare\n * the columns. A clipped status reads as a stub (`doom-log…`) that carries\n * less than the blank space it costs.\n */\n private withRightStatus(left: string, leftWidth: number, right: string | undefined, width: number): string {\n const statusWidth = right ? visibleWidth(right) : 0;\n if (!right || statusWidth === 0 || leftWidth + ROW_GAP + statusWidth > width) return left;\n return `${left}${' '.repeat(width - leftWidth - statusWidth)}${this.theme.fg(SECONDARY_COLOR, right)}`;\n }\n\n private renderCompact(chrome: DoomOverlayChrome, width: number, height: number): string[] {\n const lines = Array.from({ length: height }, () => '');\n lines[0] = this.theme.bold(this.theme.fg(TITLE_COLOR, chrome.title));\n if (height > 1) lines[height - 1] = this.theme.fg(SECONDARY_COLOR, chrome.footer);\n return lines.map((line) => padLine(fitLine(line, width), width));\n }\n}\n"],"mappings":"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"}
@@ -0,0 +1 @@
1
+ {"version":3,"file":"doomOverlay.mjs","names":[],"sources":["../../../src/tui/workflow/doomOverlay.ts"],"sourcesContent":["import type { Theme } from '@earendil-works/pi-coding-agent';\nimport { type Component, truncateToWidth, visibleWidth } from '@earendil-works/pi-tui';\nimport { fitLine, frameLine, padLine } from './rendering.ts';\n\nconst DEFAULT_TERMINAL_ROWS = 24;\nconst FULL_CHROME_ROWS = 6;\n/** Two frame columns and two gutter columns, leaving content worth framing. */\nconst MIN_FRAMED_WIDTH = 8;\nconst GUTTER = ' ';\nconst GUTTER_COLUMNS = 2;\n/** Blank columns kept between a row's left cluster and its right-aligned status. */\nconst ROW_GAP = 2;\n/** A secondary segment thinner than this is dropped rather than shredded into a stub. */\nconst MIN_SEGMENT_WIDTH = 8;\nconst ELLIPSIS = '…';\nconst BORDER_COLOR = 'borderMuted';\nconst TEXT_COLOR = 'text';\n/** Filled key caps read as pressable, matching the doom mockups' footers. */\nconst HINT_CAP_BACKGROUND = 'selectedBg';\nconst HINT_SEPARATOR = ' ';\n/** A cap's two pad columns plus the space before its label. */\nconst HINT_CHROME_COLUMNS = 6;\nconst TITLE_COLOR = 'accent';\nconst SECONDARY_COLOR = 'dim';\nconst HORIZONTAL_BORDER = '─';\nconst VERTICAL_BORDER = '│';\nconst TOP_CORNERS = ['╭', '╮'] as const;\nconst DIVIDER_JOINTS = ['├', '┤'] as const;\nconst BOTTOM_CORNERS = ['╰', '╯'] as const;\n\nexport interface DoomOverlayTui {\n terminal?: {\n rows: number;\n columns?: number;\n };\n requestRender(force?: boolean): void;\n}\n\nexport interface DoomOverlayChrome {\n title: string;\n breadcrumb?: string;\n headerRight?: string;\n footer: string;\n footerRight?: string;\n /**\n * Key/label pairs rendered as filled caps, e.g. `[ enter ] edit subject`.\n * Takes precedence over `footer`, which stays for surfaces that want a plain\n * sentence rather than a key legend.\n */\n footerHints?: readonly (readonly [string, string])[];\n /**\n * Signature colour for the surface. When set, the frame takes it and the\n * title renders as a filled badge rather than plain accent text, matching the\n * doom mockups. Left unset, the frame stays muted and the title plain.\n */\n accent?: Parameters<Theme['fg']>[0];\n}\n\n/**\n * One signature colour for every doom overlay: the frame and the title badge.\n * Per-surface colours were tried and read as arbitrary, and they also borrowed\n * tokens that mean something else (`warning`, `syntaxNumber`), so a theme edit\n * elsewhere would have repainted a frame. Change it here to change them all.\n */\nexport const DOOM_OVERLAY_ACCENT = 'mdHeading' satisfies DoomOverlayChrome['accent'];\n\nexport const DOOM_FULLSCREEN_UI_OPTIONS = {\n overlay: true,\n overlayOptions: {\n anchor: 'top-left',\n width: '100%',\n maxHeight: '100%',\n margin: 0,\n },\n} as const;\n\n/**\n * Fits a secondary segment into `budget`, or drops it entirely when too few\n * columns remain for it to stay readable. Truncating everything to fit is what\n * makes a tight header read as one run-on string.\n */\nfunction fitSegment(text: string | undefined, budget: number): string {\n if (!text || budget <= 0) return '';\n return budget < Math.min(visibleWidth(text), MIN_SEGMENT_WIDTH) ? '' : truncateToWidth(text, budget, ELLIPSIS);\n}\n\nexport abstract class DoomOverlay implements Component {\n protected constructor(\n protected readonly tui: DoomOverlayTui,\n protected readonly theme: Theme,\n ) {}\n\n invalidate(): void {\n this.tui.requestRender();\n }\n\n render(width: number): string[] {\n const safeWidth = Math.max(0, Math.floor(width));\n const height = Math.max(0, Math.floor(this.tui.terminal?.rows ?? DEFAULT_TERMINAL_ROWS));\n if (safeWidth === 0 || height === 0) return [];\n\n const chrome = this.getChrome();\n if (height < FULL_CHROME_ROWS || safeWidth < MIN_FRAMED_WIDTH) {\n return this.renderCompact(chrome, safeWidth, height);\n }\n\n const innerWidth = safeWidth - GUTTER_COLUMNS;\n const contentWidth = innerWidth - GUTTER_COLUMNS;\n const bodyHeight = height - FULL_CHROME_ROWS;\n const body = this.renderBody(contentWidth, bodyHeight).slice(0, bodyHeight);\n while (body.length < bodyHeight) body.push('');\n\n const frame = chrome.accent ?? BORDER_COLOR;\n return [\n this.borderRow(TOP_CORNERS, innerWidth, frame),\n this.contentRow(this.headerRow(chrome, contentWidth), contentWidth, safeWidth, frame),\n this.borderRow(DIVIDER_JOINTS, innerWidth, frame, BORDER_COLOR),\n ...body.map((line) => this.contentRow(line, contentWidth, safeWidth, frame)),\n this.borderRow(DIVIDER_JOINTS, innerWidth, frame, BORDER_COLOR),\n this.contentRow(this.footerRow(chrome, contentWidth), contentWidth, safeWidth, frame),\n this.borderRow(BOTTOM_CORNERS, innerWidth, frame),\n ].map((line) => padLine(line, safeWidth));\n }\n\n protected abstract getChrome(): DoomOverlayChrome;\n\n protected abstract renderBody(width: number, height: number): string[];\n\n /**\n * Edge glyphs and the rule between them are coloured separately: the outer\n * rectangle carries the signature colour while an internal separator stays\n * muted, meeting the frame at accent-coloured junctions.\n */\n private borderRow(\n [left, right]: readonly [string, string],\n innerWidth: number,\n edge: DoomOverlayChrome['accent'] = BORDER_COLOR,\n fill: DoomOverlayChrome['accent'] = edge,\n ): string {\n const edgeColour = edge ?? BORDER_COLOR;\n return [\n this.theme.fg(edgeColour, left),\n this.theme.fg(fill ?? edgeColour, HORIZONTAL_BORDER.repeat(innerWidth)),\n this.theme.fg(edgeColour, right),\n ].join('');\n }\n\n private contentRow(\n content: string,\n contentWidth: number,\n width: number,\n colour: DoomOverlayChrome['accent'] = BORDER_COLOR,\n ): string {\n const edge = this.theme.fg(colour ?? BORDER_COLOR, VERTICAL_BORDER);\n return frameLine(`${GUTTER}${padLine(content, contentWidth)}${GUTTER}`, width, edge, edge);\n }\n\n private headerRow(chrome: DoomOverlayChrome, width: number): string {\n // A signature colour turns the title into a filled badge, as the mockups\n // show; without one it stays plain accent text.\n const accent = chrome.accent;\n const titleBudget = accent ? width - GUTTER_COLUMNS : width;\n const title = truncateToWidth(chrome.title, Math.max(0, titleBudget), ELLIPSIS);\n let left = accent\n ? this.theme.inverse(this.theme.bold(this.theme.fg(accent, ` ${title} `)))\n : this.theme.bold(this.theme.fg(TITLE_COLOR, title));\n let leftWidth = visibleWidth(title) + (accent ? GUTTER_COLUMNS : 0);\n\n // The breadcrumb claims room before the right status: it says where you are,\n // which outranks ambient context when the terminal is narrow.\n const breadcrumb = fitSegment(chrome.breadcrumb, width - leftWidth - ROW_GAP);\n if (breadcrumb) {\n left += `${' '.repeat(ROW_GAP)}${this.theme.fg(SECONDARY_COLOR, breadcrumb)}`;\n leftWidth += ROW_GAP + visibleWidth(breadcrumb);\n }\n return this.withRightStatus(left, leftWidth, chrome.headerRight, width);\n }\n\n private footerRow(chrome: DoomOverlayChrome, width: number): string {\n const footer = chrome.footerHints?.length\n ? this.hintLegend(chrome.footerHints, width)\n : this.theme.fg(SECONDARY_COLOR, truncateToWidth(chrome.footer, width, ELLIPSIS));\n return this.withRightStatus(footer, visibleWidth(footer), chrome.footerRight, width);\n }\n\n /**\n * Key legend with each key in a filled cap. Every segment sets its own colour\n * so the caps survive: a cap's reset would otherwise strip the surrounding\n * dim from the labels that follow it.\n */\n private hintLegend(hints: readonly (readonly [string, string])[], width: number): string {\n let legend = '';\n let used = 0;\n for (const [key, label] of hints) {\n const segmentWidth = visibleWidth(key) + visibleWidth(label) + HINT_CHROME_COLUMNS;\n if (used + segmentWidth > width) break;\n const cap = this.theme.bg(HINT_CAP_BACKGROUND, this.theme.fg(TEXT_COLOR, ` ${key} `));\n legend += `${legend ? HINT_SEPARATOR : ''}${cap}${this.theme.fg(SECONDARY_COLOR, ` ${label}`)}`;\n used += segmentWidth;\n }\n return legend;\n }\n\n /**\n * Right-aligns a status cluster, dropping it whole when the row cannot spare\n * the columns. A clipped status reads as a stub (`doom-log…`) that carries\n * less than the blank space it costs.\n */\n private withRightStatus(left: string, leftWidth: number, right: string | undefined, width: number): string {\n const statusWidth = right ? visibleWidth(right) : 0;\n if (!right || statusWidth === 0 || leftWidth + ROW_GAP + statusWidth > width) return left;\n return `${left}${' '.repeat(width - leftWidth - statusWidth)}${this.theme.fg(SECONDARY_COLOR, right)}`;\n }\n\n private renderCompact(chrome: DoomOverlayChrome, width: number, height: number): string[] {\n const lines = Array.from({ length: height }, () => '');\n lines[0] = this.theme.bold(this.theme.fg(TITLE_COLOR, chrome.title));\n if (height > 1) lines[height - 1] = this.theme.fg(SECONDARY_COLOR, chrome.footer);\n return lines.map((line) => padLine(fitLine(line, width), width));\n }\n}\n"],"mappings":"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"}
@@ -0,0 +1 @@
1
+ {"version":3,"file":"overlayText.cjs","names":["truncateToWidth","visibleWidth"],"sources":["../../../src/tui/workflow/overlayText.ts"],"sourcesContent":["/**\n * Text primitives shared by every doom overlay in this extension.\n *\n * Kept in one place because each of them encodes a decision that has to hold on\n * every surface: how a cell is clipped, which end of a path survives, and what\n * counts as a keystroke that carries text.\n */\n\nimport { truncateToWidth, visibleWidth } from '@earendil-works/pi-tui';\n\nexport const ELLIPSIS = '…';\nexport const CURSOR_BLOCK = '█';\nexport const SELECTION_MARKER = '›';\n/** pi-tui brackets a truncation ellipsis with this; see `fit`. */\nconst HARD_RESET = '\\x1b[0m';\nconst FIRST_PRINTABLE_CODE_POINT = 0x20;\nconst DELETE_CODE_POINT = 0x7f;\n\n/**\n * Truncates and pads to an exact column count.\n *\n * `truncateToWidth` wraps its ellipsis in a hard `\\x1b[0m`, which would strip a\n * row's background for everything after a clipped cell. Nothing here sets a\n * colour that a full reset is the correct end for -- the theme closes its own\n * with `\\x1b[39m` and `\\x1b[49m` -- so the injected resets are dropped.\n */\nexport function fit(text: string, width: number): string {\n const clipped = truncateToWidth(text, Math.max(0, width), ELLIPSIS).replaceAll(HARD_RESET, '');\n return clipped + ' '.repeat(Math.max(0, width - visibleWidth(clipped)));\n}\n\n/** Right-aligns a trailing cluster against a left one, both clipped to fit. */\nexport function rightAligned(left: string, right: string, width: number): string {\n const rightWidth = visibleWidth(right);\n const leftWidth = Math.max(0, width - rightWidth - 1);\n return fit(left, leftWidth) + ' '.repeat(Math.max(1, width - leftWidth - rightWidth)) + fit(right, rightWidth);\n}\n\n/**\n * Trims from the front rather than the tail.\n *\n * For a path the file name identifies the thing, while the\n * `automations/marketing-workflows/` prefix every entry shares does not.\n */\nexport function pathTail(text: string, width: number): string {\n if (width <= 0) return '';\n if (visibleWidth(text) <= width) return text;\n return `${ELLIPSIS}${text.slice(text.length - Math.max(0, width - 1))}`;\n}\n\n/**\n * Keystrokes that carry no text: escape sequences, control keys, delete.\n *\n * Indexed rather than iterated so surrogate pairs stay intact; both halves sit\n * far above the control range, so no emoji is ever mistaken for one.\n */\nexport function isControlInput(data: string): boolean {\n for (let index = 0; index < data.length; index++) {\n const code = data.charCodeAt(index);\n if (code < FIRST_PRINTABLE_CODE_POINT || code === DELETE_CODE_POINT) return true;\n }\n return false;\n}\n\n/**\n * Whitespace-separated terms, each of which must appear somewhere in the row's\n * searchable text. Splitting on whitespace is what lets `short video` match a\n * row whose words are divided between its title and its file name.\n */\nexport function matchesQuery(haystack: string, query: string): boolean {\n const terms = query.toLowerCase().split(/\\s+/).filter(Boolean);\n if (terms.length === 0) return true;\n const lowered = haystack.toLowerCase();\n return terms.every((term) => lowered.includes(term));\n}\n"],"mappings":"wCA0BA,SAAgB,EAAI,EAAc,EAAuB,CACvD,IAAM,GAAA,EAAUA,EAAAA,gBAAAA,CAAgB,EAAM,KAAK,IAAI,EAAG,CAAK,EAAA,GAAW,CAAC,CAAC,WAAW,UAAY,EAAE,EAC7F,OAAO,EAAU,IAAI,OAAO,KAAK,IAAI,EAAG,GAAA,EAAQC,EAAAA,aAAAA,CAAa,CAAO,CAAC,CAAC,CACxE,CAGA,SAAgB,EAAa,EAAc,EAAe,EAAuB,CAC/E,IAAM,GAAA,EAAaA,EAAAA,aAAAA,CAAa,CAAK,EAC/B,EAAY,KAAK,IAAI,EAAG,EAAQ,EAAa,CAAC,EACpD,OAAO,EAAI,EAAM,CAAS,EAAI,IAAI,OAAO,KAAK,IAAI,EAAG,EAAQ,EAAY,CAAU,CAAC,EAAI,EAAI,EAAO,CAAU,CAC/G,CAQA,SAAgB,EAAS,EAAc,EAAuB,CAG5D,OAFI,GAAS,EAAU,IACvB,EAAIA,EAAAA,aAAAA,CAAa,CAAI,GAAK,EAAc,EACjC,IAAc,EAAK,MAAM,EAAK,OAAS,KAAK,IAAI,EAAG,EAAQ,CAAC,CAAC,GACtE,CAQA,SAAgB,EAAe,EAAuB,CACpD,IAAK,IAAI,EAAQ,EAAG,EAAQ,EAAK,OAAQ,IAAS,CAChD,IAAM,EAAO,EAAK,WAAW,CAAK,EAClC,GAAI,EAAO,IAA8B,IAAS,IAAmB,MAAO,EAC9E,CACA,MAAO,EACT,CAOA,SAAgB,EAAa,EAAkB,EAAwB,CACrE,IAAM,EAAQ,EAAM,YAAY,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC,OAAO,OAAO,EAC7D,GAAI,EAAM,SAAW,EAAG,MAAO,GAC/B,IAAM,EAAU,EAAS,YAAY,EACrC,OAAO,EAAM,MAAO,GAAS,EAAQ,SAAS,CAAI,CAAC,CACrD"}
@@ -0,0 +1 @@
1
+ {"version":3,"file":"overlayText.mjs","names":[],"sources":["../../../src/tui/workflow/overlayText.ts"],"sourcesContent":["/**\n * Text primitives shared by every doom overlay in this extension.\n *\n * Kept in one place because each of them encodes a decision that has to hold on\n * every surface: how a cell is clipped, which end of a path survives, and what\n * counts as a keystroke that carries text.\n */\n\nimport { truncateToWidth, visibleWidth } from '@earendil-works/pi-tui';\n\nexport const ELLIPSIS = '…';\nexport const CURSOR_BLOCK = '█';\nexport const SELECTION_MARKER = '›';\n/** pi-tui brackets a truncation ellipsis with this; see `fit`. */\nconst HARD_RESET = '\\x1b[0m';\nconst FIRST_PRINTABLE_CODE_POINT = 0x20;\nconst DELETE_CODE_POINT = 0x7f;\n\n/**\n * Truncates and pads to an exact column count.\n *\n * `truncateToWidth` wraps its ellipsis in a hard `\\x1b[0m`, which would strip a\n * row's background for everything after a clipped cell. Nothing here sets a\n * colour that a full reset is the correct end for -- the theme closes its own\n * with `\\x1b[39m` and `\\x1b[49m` -- so the injected resets are dropped.\n */\nexport function fit(text: string, width: number): string {\n const clipped = truncateToWidth(text, Math.max(0, width), ELLIPSIS).replaceAll(HARD_RESET, '');\n return clipped + ' '.repeat(Math.max(0, width - visibleWidth(clipped)));\n}\n\n/** Right-aligns a trailing cluster against a left one, both clipped to fit. */\nexport function rightAligned(left: string, right: string, width: number): string {\n const rightWidth = visibleWidth(right);\n const leftWidth = Math.max(0, width - rightWidth - 1);\n return fit(left, leftWidth) + ' '.repeat(Math.max(1, width - leftWidth - rightWidth)) + fit(right, rightWidth);\n}\n\n/**\n * Trims from the front rather than the tail.\n *\n * For a path the file name identifies the thing, while the\n * `automations/marketing-workflows/` prefix every entry shares does not.\n */\nexport function pathTail(text: string, width: number): string {\n if (width <= 0) return '';\n if (visibleWidth(text) <= width) return text;\n return `${ELLIPSIS}${text.slice(text.length - Math.max(0, width - 1))}`;\n}\n\n/**\n * Keystrokes that carry no text: escape sequences, control keys, delete.\n *\n * Indexed rather than iterated so surrogate pairs stay intact; both halves sit\n * far above the control range, so no emoji is ever mistaken for one.\n */\nexport function isControlInput(data: string): boolean {\n for (let index = 0; index < data.length; index++) {\n const code = data.charCodeAt(index);\n if (code < FIRST_PRINTABLE_CODE_POINT || code === DELETE_CODE_POINT) return true;\n }\n return false;\n}\n\n/**\n * Whitespace-separated terms, each of which must appear somewhere in the row's\n * searchable text. Splitting on whitespace is what lets `short video` match a\n * row whose words are divided between its title and its file name.\n */\nexport function matchesQuery(haystack: string, query: string): boolean {\n const terms = query.toLowerCase().split(/\\s+/).filter(Boolean);\n if (terms.length === 0) return true;\n const lowered = haystack.toLowerCase();\n return terms.every((term) => lowered.includes(term));\n}\n"],"mappings":"2EA0BA,SAAgB,EAAI,EAAc,EAAuB,CACvD,IAAM,EAAU,EAAgB,EAAM,KAAK,IAAI,EAAG,CAAK,EAAA,GAAW,CAAC,CAAC,WAAW,UAAY,EAAE,EAC7F,OAAO,EAAU,IAAI,OAAO,KAAK,IAAI,EAAG,EAAQ,EAAa,CAAO,CAAC,CAAC,CACxE,CAGA,SAAgB,EAAa,EAAc,EAAe,EAAuB,CAC/E,IAAM,EAAa,EAAa,CAAK,EAC/B,EAAY,KAAK,IAAI,EAAG,EAAQ,EAAa,CAAC,EACpD,OAAO,EAAI,EAAM,CAAS,EAAI,IAAI,OAAO,KAAK,IAAI,EAAG,EAAQ,EAAY,CAAU,CAAC,EAAI,EAAI,EAAO,CAAU,CAC/G,CAQA,SAAgB,EAAS,EAAc,EAAuB,CAG5D,OAFI,GAAS,EAAU,GACnB,EAAa,CAAI,GAAK,EAAc,EACjC,IAAc,EAAK,MAAM,EAAK,OAAS,KAAK,IAAI,EAAG,EAAQ,CAAC,CAAC,GACtE,CAQA,SAAgB,EAAe,EAAuB,CACpD,IAAK,IAAI,EAAQ,EAAG,EAAQ,EAAK,OAAQ,IAAS,CAChD,IAAM,EAAO,EAAK,WAAW,CAAK,EAClC,GAAI,EAAO,IAA8B,IAAS,IAAmB,MAAO,EAC9E,CACA,MAAO,EACT,CAOA,SAAgB,EAAa,EAAkB,EAAwB,CACrE,IAAM,EAAQ,EAAM,YAAY,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC,OAAO,OAAO,EAC7D,GAAI,EAAM,SAAW,EAAG,MAAO,GAC/B,IAAM,EAAU,EAAS,YAAY,EACrC,OAAO,EAAM,MAAO,GAAS,EAAQ,SAAS,CAAI,CAAC,CACrD"}
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rendering.cjs","names":["truncateToWidth","visibleWidth"],"sources":["../../../src/tui/workflow/rendering.ts"],"sourcesContent":["import { truncateToWidth, visibleWidth } from '@earendil-works/pi-tui';\n\nconst THOUSAND = 1000;\nconst MILLION = 1_000_000;\n/** Below this many tokens the \"k\" form keeps a decimal, above it rounds. */\nconst K_DECIMAL_LIMIT = 10_000;\n\n/** Compact token count, e.g. `812`, `4.2k`, `186k`, `1.3M`. */\nexport function formatTokens(value: number): string {\n if (value < THOUSAND) return String(value);\n if (value < MILLION) return `${(value / THOUSAND).toFixed(value < K_DECIMAL_LIMIT ? 1 : 0)}k`;\n return `${(value / MILLION).toFixed(1)}M`;\n}\n\nexport function fitLine(text: string, width: number): string {\n return truncateToWidth(text, Math.max(0, width), '');\n}\n\nexport function padLine(text: string, width: number): string {\n const safeWidth = Math.max(0, width);\n const fitted = fitLine(text, safeWidth);\n return `${fitted}${' '.repeat(Math.max(0, safeWidth - visibleWidth(fitted)))}`;\n}\n\nexport function alignLine(left: string, right: string, width: number): string {\n const safeWidth = Math.max(0, width);\n const rightWidth = visibleWidth(right);\n if (!right || rightWidth >= safeWidth) return fitLine(right || left, safeWidth);\n\n const maxLeftWidth = Math.max(0, safeWidth - rightWidth - 1);\n const fittedLeft = fitLine(left, maxLeftWidth);\n const gap = ' '.repeat(Math.max(1, safeWidth - visibleWidth(fittedLeft) - rightWidth));\n return fitLine(`${fittedLeft}${gap}${right}`, safeWidth);\n}\n\nexport function frameLine(content: string, width: number, left = '│', right = '│'): string {\n const safeWidth = Math.max(0, width);\n const frameWidth = visibleWidth(left) + visibleWidth(right);\n if (safeWidth <= frameWidth) return fitLine(`${left}${right}`, safeWidth);\n return `${left}${padLine(content, safeWidth - frameWidth)}${right}`;\n}\n\nexport function packSegments(segments: string[], width: number, separator = ' '): string[] {\n const safeWidth = Math.max(0, width);\n if (safeWidth === 0) return [];\n\n const lines: string[] = [];\n let current = '';\n for (const segment of segments) {\n const candidate = current ? `${current}${separator}${segment}` : segment;\n if (visibleWidth(candidate) <= safeWidth) {\n current = candidate;\n continue;\n }\n if (current) lines.push(fitLine(current, safeWidth));\n current = fitLine(segment, safeWidth);\n }\n if (current) lines.push(current);\n return lines;\n}\n"],"mappings":"wCAcA,SAAgB,EAAQ,EAAc,EAAuB,CAC3D,OAAA,EAAOA,EAAAA,gBAAAA,CAAgB,EAAM,KAAK,IAAI,EAAG,CAAK,EAAG,EAAE,CACrD,CAEA,SAAgB,EAAQ,EAAc,EAAuB,CAC3D,IAAM,EAAY,KAAK,IAAI,EAAG,CAAK,EAC7B,EAAS,EAAQ,EAAM,CAAS,EACtC,MAAO,GAAG,IAAS,IAAI,OAAO,KAAK,IAAI,EAAG,GAAA,EAAYC,EAAAA,aAAAA,CAAa,CAAM,CAAC,CAAC,GAC7E,CAaA,SAAgB,EAAU,EAAiB,EAAe,EAAO,IAAK,EAAQ,IAAa,CACzF,IAAM,EAAY,KAAK,IAAI,EAAG,CAAK,EAC7B,GAAA,EAAaA,EAAAA,aAAAA,CAAa,CAAI,GAAA,EAAIA,EAAAA,aAAAA,CAAa,CAAK,EAE1D,OADI,GAAa,EAAmB,EAAQ,GAAG,IAAO,IAAS,CAAS,EACjE,GAAG,IAAO,EAAQ,EAAS,EAAY,CAAU,IAAI,GAC9D"}
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rendering.mjs","names":[],"sources":["../../../src/tui/workflow/rendering.ts"],"sourcesContent":["import { truncateToWidth, visibleWidth } from '@earendil-works/pi-tui';\n\nconst THOUSAND = 1000;\nconst MILLION = 1_000_000;\n/** Below this many tokens the \"k\" form keeps a decimal, above it rounds. */\nconst K_DECIMAL_LIMIT = 10_000;\n\n/** Compact token count, e.g. `812`, `4.2k`, `186k`, `1.3M`. */\nexport function formatTokens(value: number): string {\n if (value < THOUSAND) return String(value);\n if (value < MILLION) return `${(value / THOUSAND).toFixed(value < K_DECIMAL_LIMIT ? 1 : 0)}k`;\n return `${(value / MILLION).toFixed(1)}M`;\n}\n\nexport function fitLine(text: string, width: number): string {\n return truncateToWidth(text, Math.max(0, width), '');\n}\n\nexport function padLine(text: string, width: number): string {\n const safeWidth = Math.max(0, width);\n const fitted = fitLine(text, safeWidth);\n return `${fitted}${' '.repeat(Math.max(0, safeWidth - visibleWidth(fitted)))}`;\n}\n\nexport function alignLine(left: string, right: string, width: number): string {\n const safeWidth = Math.max(0, width);\n const rightWidth = visibleWidth(right);\n if (!right || rightWidth >= safeWidth) return fitLine(right || left, safeWidth);\n\n const maxLeftWidth = Math.max(0, safeWidth - rightWidth - 1);\n const fittedLeft = fitLine(left, maxLeftWidth);\n const gap = ' '.repeat(Math.max(1, safeWidth - visibleWidth(fittedLeft) - rightWidth));\n return fitLine(`${fittedLeft}${gap}${right}`, safeWidth);\n}\n\nexport function frameLine(content: string, width: number, left = '│', right = '│'): string {\n const safeWidth = Math.max(0, width);\n const frameWidth = visibleWidth(left) + visibleWidth(right);\n if (safeWidth <= frameWidth) return fitLine(`${left}${right}`, safeWidth);\n return `${left}${padLine(content, safeWidth - frameWidth)}${right}`;\n}\n\nexport function packSegments(segments: string[], width: number, separator = ' '): string[] {\n const safeWidth = Math.max(0, width);\n if (safeWidth === 0) return [];\n\n const lines: string[] = [];\n let current = '';\n for (const segment of segments) {\n const candidate = current ? `${current}${separator}${segment}` : segment;\n if (visibleWidth(candidate) <= safeWidth) {\n current = candidate;\n continue;\n }\n if (current) lines.push(fitLine(current, safeWidth));\n current = fitLine(segment, safeWidth);\n }\n if (current) lines.push(current);\n return lines;\n}\n"],"mappings":"2EAcA,SAAgB,EAAQ,EAAc,EAAuB,CAC3D,OAAO,EAAgB,EAAM,KAAK,IAAI,EAAG,CAAK,EAAG,EAAE,CACrD,CAEA,SAAgB,EAAQ,EAAc,EAAuB,CAC3D,IAAM,EAAY,KAAK,IAAI,EAAG,CAAK,EAC7B,EAAS,EAAQ,EAAM,CAAS,EACtC,MAAO,GAAG,IAAS,IAAI,OAAO,KAAK,IAAI,EAAG,EAAY,EAAa,CAAM,CAAC,CAAC,GAC7E,CAaA,SAAgB,EAAU,EAAiB,EAAe,EAAO,IAAK,EAAQ,IAAa,CACzF,IAAM,EAAY,KAAK,IAAI,EAAG,CAAK,EAC7B,EAAa,EAAa,CAAI,EAAI,EAAa,CAAK,EAE1D,OADI,GAAa,EAAmB,EAAQ,GAAG,IAAO,IAAS,CAAS,EACjE,GAAG,IAAO,EAAQ,EAAS,EAAY,CAAU,IAAI,GAC9D"}
@@ -0,0 +1,2 @@
1
+ const e=require("./doomOverlay.cjs"),t=require("./workflowStatusRow.cjs");let n=require("@agimon-ai/workflow-mcp"),r=require("@earendil-works/pi-tui");e.DOOM_FULLSCREEN_UI_OPTIONS.overlayOptions;const i=`running`,a=`paused`,o=`↑↓`,s=`${o} select · PgUp/PgDn page · enter open · esc close`,c=[[o,`select`],[`PgUp/PgDn`,`page`],[`enter`,`open`],[`esc`,`close`]];function l(e,t){let n=(0,r.truncateToWidth)(e,Math.max(0,t),`…`).replaceAll(`\x1B[0m`,``);return n+` `.repeat(Math.max(0,t-(0,r.visibleWidth)(n)))}function u(e,t,n){let i=(0,r.visibleWidth)(t),a=Math.max(0,n-i-1);return l(e,a)+` `.repeat(Math.max(1,n-a-i))+l(t,i)}function d(e){return e===`completed`?`✓`:e===`failed`?`✗`:`●`}function f(e){return e===`completed`?`success`:e===`failed`?`error`:`accent`}function p(e,t,n){let a=[];for(let o of e.jobs){let e=o.total?` · ${(o.index??0)+1}/${o.total}`:``;if(a.push((0,r.truncateToWidth)(n.fg(f(o.status),`${d(o.status)} ${o.name}`)+n.fg(`dim`,e),t,`…`)),o.status===i)for(let e of o.steps){let o=e.status===`failed`?`error`:e.status===i?`muted`:`dim`;a.push((0,r.truncateToWidth)(n.fg(o,` ${d(e.status)} ${e.name}`),t,`…`))}}return a}function m(e,o,s,c){if(!e)return[c.fg(`dim`,`Select a workflow to inspect its jobs and output.`)];let l=(0,n.currentWorkflowPosition)(e.jobs),d=t.humanizeDuration(Math.max(0,Date.now()-Date.parse(e.startedAt))),f=e.executionState??i,m=f===a?`warning`:`dim`,h=[u(`${c.fg(`accent`,`●`)} ${c.bold(e.displayName)}`,c.fg(m,f),o),(0,r.truncateToWidth)(c.fg(`dim`,`${e.runKey} · ${e.workspace} · ${d}`),o,`…`),l?(0,r.truncateToWidth)(`${c.fg(`accent`,l.job)}${l.step?c.fg(`muted`,` · ${l.step}`):``}`,o,`…`):c.fg(`dim`,`starting`),``,c.fg(`dim`,`PROGRESS`)],g=e.output.length>0?3:0,_=Math.max(0,s-h.length-g);if(h.push(...p(e,o,c).slice(0,_)),e.output.length>0&&s-h.length>=2){h.push(``,c.fg(`dim`,`OUTPUT · newest last`));let t=Math.max(0,s-h.length);h.push(...e.output.slice(-t).map(e=>(0,r.truncateToWidth)(e,o,`…`)))}return h.slice(0,s)}var h=class extends e.DoomOverlay{source;done;bodyHeight=8;completed=!1;error;items=[];refreshActive=!1;selected=0;selectedKey;timer;constructor(e,t,n,r,i={}){super(e,t),this.source=n,this.done=r,this.selectedKey=i.initialKey,this.refresh(),this.timer=setInterval(()=>void this.refresh(),i.refreshMs??750),this.timer.unref?.()}async refresh(){if(!(this.refreshActive||this.completed)){this.refreshActive=!0;try{let e=this.items[this.selected]?.key??this.selectedKey,t=await this.source.list();if(this.completed)return;let n=e?t.findIndex(t=>t.key===e):-1;this.items=t,this.selected=n>=0?n:Math.min(this.selected,Math.max(0,t.length-1)),this.selectedKey=t[this.selected]?.key;let r=t[this.selected];r&&(r.output=await this.source.output(r)),this.error=void 0,this.completed||this.tui.requestRender()}catch(e){this.error=e instanceof Error?e.message:String(e),this.completed||this.tui.requestRender()}finally{this.refreshActive=!1}}}move(e){this.items.length!==0&&(this.selected=Math.max(0,Math.min(this.items.length-1,this.selected+e)),this.selectedKey=this.items[this.selected]?.key,this.refreshSelectedOutput(),this.tui.requestRender())}async refreshSelectedOutput(){let e=this.items[this.selected];if(!(!e||this.completed)){try{e.output=await this.source.output(e),this.error=void 0}catch(e){this.error=e instanceof Error?e.message:String(e)}!this.completed&&e.key===this.selectedKey&&this.tui.requestRender()}}finish(e){this.completed||(this.completed=!0,clearInterval(this.timer),this.done(e))}close(){this.finish(void 0)}handleInput(e){if((0,r.matchesKey)(e,`escape`)||(0,r.matchesKey)(e,`ctrl+c`)){this.finish(void 0);return}if((0,r.matchesKey)(e,`up`)||e===`k`){this.move(-1);return}if((0,r.matchesKey)(e,`down`)||e===`j`){this.move(1);return}if((0,r.matchesKey)(e,`pageUp`)){this.move(-this.rosterCapacity());return}if((0,r.matchesKey)(e,`pageDown`)){this.move(this.rosterCapacity());return}if((0,r.matchesKey)(e,`enter`)){let e=this.items[this.selected];e&&this.finish({...e.runId?{runId:e.runId}:{},runKey:e.runKey,startedAt:e.startedAt,workspace:e.workspace})}}rosterCapacity(){return Math.max(1,Math.floor((this.bodyHeight-2)/2))}current(){if(this.items.length!==0)return this.items[Math.min(this.selected,this.items.length-1)]}getChrome(){let t=this.current(),n=t?`${t.displayName} · ${t.executionState??i}`:`no active workflows`;return{title:`WORKFLOW SPACE`,accent:e.DOOM_OVERLAY_ACCENT,breadcrumb:`SPC › w / workflows › l / manage`,headerRight:`${this.items.length} running · ${n}`,footer:s,footerHints:c,footerRight:this.items.length>0?`${Math.min(this.selected,this.items.length-1)+1}/${this.items.length}`:`empty`}}renderBody(e,t){this.bodyHeight=t;let n=e=>this.error?[this.theme.fg(`warning`,`Unable to refresh workflows: ${this.error}`)]:m(this.current(),e,t,this.theme);if(e<48)return n(e).slice(0,t);let i=Math.max(18,Math.floor((e-1)*.3333333333333333)),a=Math.max(1,e-i-1),o=Math.max(1,i-1),s=Math.max(1,a-1),c=this.rosterLines(o),u=n(s),d=this.theme.fg(`borderMuted`,`│`);return Array.from({length:t},(t,n)=>{let i=`${l(c[n]??``,o)} ${d} ${l(u[n]??``,s)}`;return(0,r.truncateToWidth)(i,e,`…`)})}rosterLines(e){let t=[this.theme.bold(`RUNNING · this session`),``];if(this.items.length===0)return t.push(this.theme.fg(`dim`,`No active workflows`)),t;let r=this.rosterCapacity(),i=Math.min(this.selected,this.items.length-1),o=Math.max(0,Math.min(i-r+1,Math.max(0,this.items.length-r)));for(let[s,c]of this.items.slice(o,o+r).entries()){let r=o+s===i,u=c.executionState===a,d=r?this.theme.fg(`accent`,`›`):` `,f=this.theme.fg(u?`warning`:`accent`,`●`),p=r?this.theme.bold(c.displayName):c.displayName,m=u?a:(0,n.currentWorkflowPosition)(c.jobs)?.job??`starting`,h=`${d} ${f} ${p}`,g=`${` `.repeat(4)}${this.theme.fg(u?`warning`:`dim`,m)}`,_=r?`selectedBg`:`userMessageBg`;for(let n of[h,g])t.push(this.theme.bg(_,l(n,e)))}return t}invalidate(){this.refresh()}dispose(){this.completed||(this.completed=!0,clearInterval(this.timer))}};exports.WorkflowInspectorComponent=h;
2
+ //# sourceMappingURL=workflowInspector.cjs.map