mirror of
https://github.com/NanmiCoder/claude-code-haha.git
synced 2026-10-10 03:43:11 +08:00
3ce7c67822
A workflow is a JS script the model writes in the moment and hands to the Workflow tool, which runs it in a locked-down `node:vm` and orchestrates subagents through `agent()`/`parallel()`/`pipeline()`/`phase()`. Saving one as a `/name` command is the secondary path; the inline script is the point. Runtime: cross-realm value marshalling so a script cannot reach the host `Function`, determinism guards on `Date.now()`/`Math.random()` (they would make a resume replay diverge), a FIFO concurrency gate, and a journal that lets an interrupted run resume from its longest unchanged prefix. Desktop: the run shows up as a `workflow` section in the existing activity panel — phases as headings, their agents beneath. A workflow agent is an ordinary subagent run by the same runner, so its row opens the existing subagent page rather than a parallel viewer of its own; that needed a `by-agent` lookup, because these agents have no parent `Agent` tool call to key off. Finished runs are rebuilt from the per-agent sidecars when a session is reopened, since the live progress stream does not outlive the process.
68 lines
2.6 KiB
TypeScript
68 lines
2.6 KiB
TypeScript
import type { ContentBlockParam } from '@anthropic-ai/sdk/resources/index.mjs'
|
|
import type { Command } from '../../types/command.js'
|
|
import { loadWorkflows } from '../../utils/workflows/discovery.js'
|
|
import { areWorkflowsEnabled } from '../../utils/workflows/enabled.js'
|
|
import type { WorkflowDefinition } from '../../utils/workflows/types.js'
|
|
import { WORKFLOW_TOOL_NAME } from './constants.js'
|
|
|
|
/**
|
|
* Turn every discovered workflow into a `/<name>` command.
|
|
*
|
|
* The command is a prompt, not a direct tool call: whatever the user typed
|
|
* after the name has to become the `args` value, and only the model can decide
|
|
* whether "issues 1024, 1025" is a list of numbers or a sentence. The prompt
|
|
* pins everything else so the model's only job is that conversion.
|
|
*/
|
|
export async function getWorkflowCommands(cwd?: string): Promise<Command[]> {
|
|
if (!areWorkflowsEnabled()) return []
|
|
const workflows = await loadWorkflows(cwd)
|
|
return workflows.map(workflow => createWorkflowCommand(workflow))
|
|
}
|
|
|
|
export function createWorkflowCommand(workflow: WorkflowDefinition): Command {
|
|
return {
|
|
type: 'prompt',
|
|
name: workflow.name,
|
|
description: workflow.description,
|
|
progressMessage: `running the ${workflow.name} workflow`,
|
|
contentLength: workflow.description.length + 200,
|
|
argNames: ['args'],
|
|
source: workflowCommandSource(workflow),
|
|
isEnabled: () => areWorkflowsEnabled(),
|
|
userFacingName: () => workflow.name,
|
|
async getPromptForCommand(args: string): Promise<ContentBlockParam[]> {
|
|
const argsLine =
|
|
args.trim() === ''
|
|
? 'Pass no `args`.'
|
|
: `Convert the following invocation text into the \`args\` value and pass it: ${JSON.stringify(args.trim())}. ` +
|
|
'If it is a list of items, pass a real JSON array, not a string.'
|
|
return [
|
|
{
|
|
type: 'text',
|
|
text:
|
|
`Run the saved workflow "${workflow.name}" by calling ${WORKFLOW_TOOL_NAME} exactly once with ` +
|
|
`{ name: "${workflow.name}" }. ${argsLine}\n` +
|
|
'Do not write a new script and do not do the work yourself — the saved workflow already ' +
|
|
'contains the orchestration. After the tool returns, end your turn; the result arrives as a ' +
|
|
'task notification.',
|
|
},
|
|
]
|
|
},
|
|
} as Command
|
|
}
|
|
|
|
function workflowCommandSource(
|
|
workflow: WorkflowDefinition,
|
|
): 'builtin' | 'userSettings' | 'projectSettings' | 'plugin' {
|
|
switch (workflow.source) {
|
|
case 'built-in':
|
|
return 'builtin'
|
|
case 'plugin':
|
|
return 'plugin'
|
|
case 'projectSettings':
|
|
return 'projectSettings'
|
|
default:
|
|
return 'userSettings'
|
|
}
|
|
}
|