agency-orchestrator 0.3.2 → 0.4.2

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 (73) hide show
  1. package/README.en.md +539 -0
  2. package/README.md +391 -256
  3. package/dist/cli/compose.d.ts +4 -2
  4. package/dist/cli/compose.js +35 -39
  5. package/dist/cli/demo.d.ts +8 -0
  6. package/dist/cli/demo.js +301 -0
  7. package/dist/cli.js +47 -10
  8. package/dist/connectors/claude-code.d.ts +11 -0
  9. package/dist/connectors/claude-code.js +23 -0
  10. package/dist/connectors/cli-base.d.ts +16 -0
  11. package/dist/connectors/cli-base.js +53 -0
  12. package/dist/connectors/codex-cli.d.ts +11 -0
  13. package/dist/connectors/codex-cli.js +19 -0
  14. package/dist/connectors/copilot-cli.d.ts +11 -0
  15. package/dist/connectors/copilot-cli.js +19 -0
  16. package/dist/connectors/factory.d.ts +5 -0
  17. package/dist/connectors/factory.js +42 -0
  18. package/dist/connectors/gemini-cli.d.ts +11 -0
  19. package/dist/connectors/gemini-cli.js +19 -0
  20. package/dist/connectors/ollama.js +1 -1
  21. package/dist/connectors/openclaw-cli.d.ts +15 -0
  22. package/dist/connectors/openclaw-cli.js +37 -0
  23. package/dist/core/parser.js +7 -2
  24. package/dist/index.d.ts +3 -0
  25. package/dist/index.js +8 -27
  26. package/dist/mcp/server.d.ts +1 -0
  27. package/dist/mcp/server.js +288 -0
  28. package/dist/types.d.ts +2 -2
  29. package/examples/sample-code-for-review.md +43 -0
  30. package/examples/sample-prd.md +31 -0
  31. package/examples/sample-premise.txt +1 -0
  32. package/integrations/aider/CONVENTIONS.md +50 -0
  33. package/integrations/aider/README.md +112 -0
  34. package/integrations/antigravity/AGENTS.md +1 -1
  35. package/integrations/antigravity/README.md +2 -2
  36. package/integrations/claude-code/README.md +27 -11
  37. package/integrations/codex/README.md +2 -2
  38. package/integrations/codex/instructions.md +1 -1
  39. package/integrations/copilot/README.md +111 -0
  40. package/integrations/copilot/copilot-instructions.md +50 -0
  41. package/integrations/cursor/README.md +31 -3
  42. package/integrations/cursor/workflow-runner.mdc +1 -1
  43. package/integrations/deerflow/README.md +2 -2
  44. package/integrations/deerflow/SKILL.md +1 -1
  45. package/integrations/gemini-cli/GEMINI.md +1 -1
  46. package/integrations/gemini-cli/README.md +2 -2
  47. package/integrations/kiro/README.md +1 -1
  48. package/integrations/kiro/ao-workflow-runner.md +1 -1
  49. package/integrations/openclaw/README.md +2 -2
  50. package/integrations/opencode/README.md +72 -0
  51. package/integrations/opencode/instructions.md +50 -0
  52. package/integrations/qwen/README.md +104 -0
  53. package/integrations/qwen/ao-workflow-runner.md +50 -0
  54. package/integrations/trae/README.md +1 -1
  55. package/integrations/trae/ao-workflow-runner.md +1 -1
  56. package/integrations/windsurf/.windsurfrules +50 -0
  57. package/integrations/windsurf/README.md +110 -0
  58. package/package.json +26 -4
  59. package/workflows/ai-opinion-article.yaml +122 -0
  60. package/workflows/content-pipeline.yaml +1 -1
  61. package/workflows/department-collab/ceo-org-delegation.yaml +189 -0
  62. package/workflows/dev/pr-review.yaml +20 -10
  63. package/workflows/dev/tech-design-review.yaml +115 -0
  64. package/workflows/hr/interview-questions.yaml +108 -0
  65. package/workflows/legal/contract-review.yaml +106 -0
  66. package/workflows/marketing/competitor-analysis.yaml +99 -0
  67. package/workflows/marketing/seo-content-matrix.yaml +98 -0
  68. package/workflows/marketing/xiaohongshu-content.yaml +103 -0
  69. package/workflows/ops/weekly-report.yaml +90 -0
  70. package/workflows/product-review.yaml +56 -21
  71. package/workflows/story-creation.yaml +48 -33
  72. package/workflows/strategy/business-plan.yaml +122 -0
  73. package/README.zh-CN.md +0 -471
@@ -0,0 +1,53 @@
1
+ /**
2
+ * CLI Connector 通用基类
3
+ * 通过本地 AI CLI 工具调用,使用用户的订阅额度,无需 API key
4
+ *
5
+ * 支持: Claude Code / Gemini CLI / Copilot CLI / Codex CLI / OpenClaw CLI
6
+ */
7
+ import { execFile } from 'node:child_process';
8
+ import { promisify } from 'node:util';
9
+ const execFileAsync = promisify(execFile);
10
+ export class CLIBaseConnector {
11
+ cfg;
12
+ constructor(cfg) {
13
+ this.cfg = cfg;
14
+ }
15
+ async chat(systemPrompt, userMessage, config) {
16
+ const fullPrompt = systemPrompt
17
+ ? `<system>\n${systemPrompt}\n</system>\n\n${userMessage}`
18
+ : userMessage;
19
+ const args = this.cfg.buildArgs(fullPrompt, config);
20
+ const timeout = config.timeout || 180000;
21
+ try {
22
+ const { stdout, stderr } = await execFileAsync(this.cfg.command, args, {
23
+ timeout,
24
+ maxBuffer: 10 * 1024 * 1024,
25
+ env: { ...process.env },
26
+ });
27
+ const content = this.cfg.parseOutput
28
+ ? this.cfg.parseOutput(stdout)
29
+ : stdout.trim();
30
+ if (!content && stderr) {
31
+ throw new Error(`${this.cfg.displayName} 返回空内容,stderr: ${stderr.slice(0, 500)}`);
32
+ }
33
+ return {
34
+ content,
35
+ usage: {
36
+ input_tokens: Math.ceil((systemPrompt.length + userMessage.length) / 4),
37
+ output_tokens: Math.ceil(content.length / 4),
38
+ },
39
+ };
40
+ }
41
+ catch (err) {
42
+ if (err.killed || err.signal === 'SIGTERM') {
43
+ throw new Error(`${this.cfg.displayName} 超时 (${timeout / 1000}s),可在 YAML 中设置 timeout 增加等待时间`);
44
+ }
45
+ // 区分"未安装"和"执行失败"
46
+ if (err.code === 'ENOENT') {
47
+ throw new Error(`找不到 ${this.cfg.command} 命令,请先安装 ${this.cfg.displayName}\n` +
48
+ `参考: https://github.com/jnMetaCode/agency-orchestrator#llm-配置`);
49
+ }
50
+ throw new Error(`${this.cfg.displayName} 调用失败: ${err.message}`);
51
+ }
52
+ }
53
+ }
@@ -0,0 +1,11 @@
1
+ /**
2
+ * OpenAI Codex CLI Connector
3
+ * 通过本地 `codex` CLI 调用,直接使用 ChatGPT Plus/Pro 订阅额度
4
+ *
5
+ * 安装: npm install -g @openai/codex
6
+ * 认证: codex 首次运行会引导 OpenAI 登录,之后自动认证
7
+ */
8
+ import { CLIBaseConnector } from './cli-base.js';
9
+ export declare class CodexCLIConnector extends CLIBaseConnector {
10
+ constructor();
11
+ }
@@ -0,0 +1,19 @@
1
+ /**
2
+ * OpenAI Codex CLI Connector
3
+ * 通过本地 `codex` CLI 调用,直接使用 ChatGPT Plus/Pro 订阅额度
4
+ *
5
+ * 安装: npm install -g @openai/codex
6
+ * 认证: codex 首次运行会引导 OpenAI 登录,之后自动认证
7
+ */
8
+ import { CLIBaseConnector } from './cli-base.js';
9
+ export class CodexCLIConnector extends CLIBaseConnector {
10
+ constructor() {
11
+ super({
12
+ command: 'codex',
13
+ displayName: 'OpenAI Codex CLI',
14
+ buildArgs: (prompt, _config) => {
15
+ return ['-q', prompt];
16
+ },
17
+ });
18
+ }
19
+ }
@@ -0,0 +1,11 @@
1
+ /**
2
+ * GitHub Copilot CLI Connector
3
+ * 通过本地 `copilot` CLI 调用,直接使用 GitHub Copilot 订阅额度
4
+ *
5
+ * 安装: npm install -g @github/copilot
6
+ * 认证: copilot 首次运行会引导 GitHub 登录,之后自动认证
7
+ */
8
+ import { CLIBaseConnector } from './cli-base.js';
9
+ export declare class CopilotCLIConnector extends CLIBaseConnector {
10
+ constructor();
11
+ }
@@ -0,0 +1,19 @@
1
+ /**
2
+ * GitHub Copilot CLI Connector
3
+ * 通过本地 `copilot` CLI 调用,直接使用 GitHub Copilot 订阅额度
4
+ *
5
+ * 安装: npm install -g @github/copilot
6
+ * 认证: copilot 首次运行会引导 GitHub 登录,之后自动认证
7
+ */
8
+ import { CLIBaseConnector } from './cli-base.js';
9
+ export class CopilotCLIConnector extends CLIBaseConnector {
10
+ constructor() {
11
+ super({
12
+ command: 'copilot',
13
+ displayName: 'GitHub Copilot CLI',
14
+ buildArgs: (prompt, _config) => {
15
+ return ['-p', prompt];
16
+ },
17
+ });
18
+ }
19
+ }
@@ -0,0 +1,5 @@
1
+ /**
2
+ * LLM Connector 工厂 — 根据 LLMConfig 创建对应的 connector
3
+ */
4
+ import type { LLMConfig, LLMConnector } from '../types.js';
5
+ export declare function createConnector(config: LLMConfig): LLMConnector;
@@ -0,0 +1,42 @@
1
+ import { ClaudeConnector } from './claude.js';
2
+ import { ClaudeCodeConnector } from './claude-code.js';
3
+ import { GeminiCLIConnector } from './gemini-cli.js';
4
+ import { CopilotCLIConnector } from './copilot-cli.js';
5
+ import { CodexCLIConnector } from './codex-cli.js';
6
+ import { OpenClawCLIConnector } from './openclaw-cli.js';
7
+ import { OllamaConnector } from './ollama.js';
8
+ import { OpenAICompatibleConnector } from './openai-compatible.js';
9
+ export function createConnector(config) {
10
+ switch (config.provider) {
11
+ // ── 免 API key(用订阅 / 免费额度)──
12
+ case 'claude-code':
13
+ return new ClaudeCodeConnector();
14
+ case 'gemini-cli':
15
+ return new GeminiCLIConnector();
16
+ case 'copilot-cli':
17
+ return new CopilotCLIConnector();
18
+ case 'codex-cli':
19
+ return new CodexCLIConnector();
20
+ case 'openclaw-cli':
21
+ return new OpenClawCLIConnector();
22
+ case 'ollama':
23
+ return new OllamaConnector(config.base_url);
24
+ // ── 需要 API key ──
25
+ case 'claude':
26
+ return new ClaudeConnector(config.api_key);
27
+ case 'deepseek':
28
+ return new OpenAICompatibleConnector({
29
+ apiKey: config.api_key || process.env.DEEPSEEK_API_KEY,
30
+ baseUrl: config.base_url || 'https://api.deepseek.com/v1',
31
+ });
32
+ case 'openai':
33
+ return new OpenAICompatibleConnector({
34
+ apiKey: config.api_key || process.env.OPENAI_API_KEY,
35
+ baseUrl: config.base_url || 'https://api.openai.com/v1',
36
+ });
37
+ default:
38
+ throw new Error(`暂不支持 provider: ${config.provider}\n` +
39
+ '免 API key: claude-code / gemini-cli / copilot-cli / codex-cli / openclaw-cli / ollama\n' +
40
+ '需 API key: claude / deepseek / openai');
41
+ }
42
+ }
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Gemini CLI Connector
3
+ * 通过本地 `gemini` CLI 调用,Google 账号免费使用(1000 次/天,Gemini 2.5 Pro)
4
+ *
5
+ * 安装: npm install -g @google/gemini-cli
6
+ * 认证: gemini 首次运行会引导 Google 账号登录,之后自动认证
7
+ */
8
+ import { CLIBaseConnector } from './cli-base.js';
9
+ export declare class GeminiCLIConnector extends CLIBaseConnector {
10
+ constructor();
11
+ }
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Gemini CLI Connector
3
+ * 通过本地 `gemini` CLI 调用,Google 账号免费使用(1000 次/天,Gemini 2.5 Pro)
4
+ *
5
+ * 安装: npm install -g @google/gemini-cli
6
+ * 认证: gemini 首次运行会引导 Google 账号登录,之后自动认证
7
+ */
8
+ import { CLIBaseConnector } from './cli-base.js';
9
+ export class GeminiCLIConnector extends CLIBaseConnector {
10
+ constructor() {
11
+ super({
12
+ command: 'gemini',
13
+ displayName: 'Gemini CLI',
14
+ buildArgs: (prompt, _config) => {
15
+ return ['-p', prompt];
16
+ },
17
+ });
18
+ }
19
+ }
@@ -9,7 +9,7 @@ export class OllamaConnector {
9
9
  method: 'POST',
10
10
  headers: { 'Content-Type': 'application/json' },
11
11
  body: JSON.stringify({
12
- model: config.model,
12
+ model: config.model || 'llama3.1',
13
13
  messages: [
14
14
  { role: 'system', content: systemPrompt },
15
15
  { role: 'user', content: userMessage },
@@ -0,0 +1,15 @@
1
+ /**
2
+ * OpenClaw CLI Connector
3
+ * 通过本地 `openclaw` CLI 调用,支持 OAuth 和 API key 多种认证方式
4
+ *
5
+ * 安装: npm install -g openclaw@latest
6
+ * 认证: openclaw onboard --install-daemon(引导配置)
7
+ *
8
+ * 注意: openclaw agent 必须指定 --agent <id>,否则会报
9
+ * "Pass --to <E.164>, --session-id, or --agent to choose a session"
10
+ * 默认使用 "main" agent,可通过 YAML model 字段或 OPENCLAW_AGENT 环境变量覆盖
11
+ */
12
+ import { CLIBaseConnector } from './cli-base.js';
13
+ export declare class OpenClawCLIConnector extends CLIBaseConnector {
14
+ constructor();
15
+ }
@@ -0,0 +1,37 @@
1
+ /**
2
+ * OpenClaw CLI Connector
3
+ * 通过本地 `openclaw` CLI 调用,支持 OAuth 和 API key 多种认证方式
4
+ *
5
+ * 安装: npm install -g openclaw@latest
6
+ * 认证: openclaw onboard --install-daemon(引导配置)
7
+ *
8
+ * 注意: openclaw agent 必须指定 --agent <id>,否则会报
9
+ * "Pass --to <E.164>, --session-id, or --agent to choose a session"
10
+ * 默认使用 "main" agent,可通过 YAML model 字段或 OPENCLAW_AGENT 环境变量覆盖
11
+ */
12
+ import { CLIBaseConnector } from './cli-base.js';
13
+ export class OpenClawCLIConnector extends CLIBaseConnector {
14
+ constructor() {
15
+ super({
16
+ command: 'openclaw',
17
+ displayName: 'OpenClaw CLI',
18
+ buildArgs: (prompt, config) => {
19
+ // model 字段复用为 agent id,例如 model: "my-agent"
20
+ const agentId = config.model || process.env.OPENCLAW_AGENT || 'main';
21
+ return ['agent', '--agent', agentId, '--message', prompt];
22
+ },
23
+ parseOutput: (stdout) => {
24
+ // OpenClaw 的 stdout 可能混入插件日志(如 ShellWard 的 ANSI 彩色输出)
25
+ // 过滤掉 [plugins] 前缀行和 ANSI 控制码行
26
+ return stdout
27
+ .split('\n')
28
+ .filter(line => {
29
+ const clean = line.replace(/\x1b\[[0-9;]*m/g, '').trim();
30
+ return clean && !clean.startsWith('[plugins]');
31
+ })
32
+ .join('\n')
33
+ .trim();
34
+ },
35
+ });
36
+ }
37
+ }
@@ -20,8 +20,13 @@ export function parseWorkflow(filePath) {
20
20
  throw new Error('工作流缺少 llm 配置');
21
21
  }
22
22
  const llm = doc.llm;
23
- if (!llm.provider || !llm.model) {
24
- throw new Error('llm 配置缺少 provider 或 model');
23
+ if (!llm.provider) {
24
+ throw new Error('llm 配置缺少 provider');
25
+ }
26
+ // CLI providers (claude-code, gemini-cli, copilot-cli, codex-cli, openclaw-cli) 和 ollama 不需要 model
27
+ const cliProviders = ['claude-code', 'gemini-cli', 'copilot-cli', 'codex-cli', 'openclaw-cli', 'ollama'];
28
+ if (!llm.model && !cliProviders.includes(llm.provider)) {
29
+ throw new Error('llm 配置缺少 model(CLI provider 可省略)');
25
30
  }
26
31
  // 校验每个 step
27
32
  const stepIds = new Set();
package/dist/index.d.ts CHANGED
@@ -13,6 +13,7 @@ export { loadAgent, listAgents } from './agents/loader.js';
13
13
  export { ClaudeConnector } from './connectors/claude.js';
14
14
  export { OllamaConnector } from './connectors/ollama.js';
15
15
  export { OpenAICompatibleConnector } from './connectors/openai-compatible.js';
16
+ export { createConnector } from './connectors/factory.js';
16
17
  export { saveResults, loadPreviousContext, findLatestOutput } from './output/reporter.js';
17
18
  export { composeWorkflow, buildRoleCatalog, extractYamlFromResponse } from './cli/compose.js';
18
19
  export type { WorkflowDefinition, StepDefinition, LLMConfig, LLMConnector, LLMResult, AgentDefinition, WorkflowResult, StepResult, DAGNode, } from './types.js';
@@ -28,4 +29,6 @@ export declare function run(workflowPath: string, inputs: Record<string, string>
28
29
  resumeDir?: string;
29
30
  /** 从指定步骤开始重新执行(跳过之前的步骤) */
30
31
  fromStep?: string;
32
+ /** 覆盖 LLM 配置(例如来自 ao demo) */
33
+ llmOverride?: Partial<import('./types.js').LLMConfig>;
31
34
  }): Promise<import('./types.js').WorkflowResult>;
package/dist/index.js CHANGED
@@ -13,14 +13,13 @@ export { loadAgent, listAgents } from './agents/loader.js';
13
13
  export { ClaudeConnector } from './connectors/claude.js';
14
14
  export { OllamaConnector } from './connectors/ollama.js';
15
15
  export { OpenAICompatibleConnector } from './connectors/openai-compatible.js';
16
+ export { createConnector } from './connectors/factory.js';
16
17
  export { saveResults, loadPreviousContext, findLatestOutput } from './output/reporter.js';
17
18
  export { composeWorkflow, buildRoleCatalog, extractYamlFromResponse } from './cli/compose.js';
18
19
  import { parseWorkflow, validateWorkflow } from './core/parser.js';
19
20
  import { buildDAG } from './core/dag.js';
20
21
  import { executeDAG } from './core/executor.js';
21
- import { ClaudeConnector } from './connectors/claude.js';
22
- import { OllamaConnector } from './connectors/ollama.js';
23
- import { OpenAICompatibleConnector } from './connectors/openai-compatible.js';
22
+ import { createConnector } from './connectors/factory.js';
24
23
  import { saveResults, printStepResult, printStepRunning, clearRunningLine, printSummary, loadPreviousContext, getCompletedStepIds } from './output/reporter.js';
25
24
  import { existsSync } from 'node:fs';
26
25
  import { resolve, dirname } from 'node:path';
@@ -38,30 +37,12 @@ export async function run(workflowPath, inputs, options) {
38
37
  }
39
38
  // 构建 DAG
40
39
  const dag = buildDAG(workflow);
41
- // 创建 connector
42
- let connector;
43
- switch (workflow.llm.provider) {
44
- case 'claude':
45
- connector = new ClaudeConnector(workflow.llm.api_key);
46
- break;
47
- case 'ollama':
48
- connector = new OllamaConnector(workflow.llm.base_url);
49
- break;
50
- case 'deepseek':
51
- connector = new OpenAICompatibleConnector({
52
- apiKey: workflow.llm.api_key || process.env.DEEPSEEK_API_KEY,
53
- baseUrl: workflow.llm.base_url || 'https://api.deepseek.com/v1',
54
- });
55
- break;
56
- case 'openai':
57
- connector = new OpenAICompatibleConnector({
58
- apiKey: workflow.llm.api_key || process.env.OPENAI_API_KEY,
59
- baseUrl: workflow.llm.base_url || 'https://api.openai.com/v1',
60
- });
61
- break;
62
- default:
63
- throw new Error(`暂不支持 provider: ${workflow.llm.provider}(支持 claude / deepseek / openai / ollama)`);
40
+ // Apply LLM override (e.g., from ao demo)
41
+ if (options?.llmOverride) {
42
+ Object.assign(workflow.llm, options.llmOverride);
64
43
  }
44
+ // 创建 connector
45
+ const connector = createConnector(workflow.llm);
65
46
  // 构建输入
66
47
  const inputMap = new Map(Object.entries(inputs));
67
48
  // 检查必填输入 + 注入默认值
@@ -163,7 +144,7 @@ export async function run(workflowPath, inputs, options) {
163
144
  });
164
145
  result.name = workflow.name;
165
146
  // 保存结果
166
- const outputDir = options?.outputDir || '.ao-output';
147
+ const outputDir = options?.outputDir || 'ao-output';
167
148
  const outputPath = saveResults(result, outputDir);
168
149
  if (!quiet) {
169
150
  printSummary(result, outputPath);
@@ -0,0 +1 @@
1
+ export declare function startServer(verbose?: boolean): Promise<void>;
@@ -0,0 +1,288 @@
1
+ /**
2
+ * MCP Server — 通过 MCP 协议暴露工作流操作
3
+ *
4
+ * 6 个工具,每个都是对现有函数的薄封装。
5
+ * 传输层: StdioServerTransport (stdin/stdout JSON-RPC)
6
+ *
7
+ * 重要: 所有日志必须输出到 stderr,stdout 是 MCP 协议通道。
8
+ */
9
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
10
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
11
+ import { z } from 'zod';
12
+ import { resolve, relative, dirname } from 'node:path';
13
+ import { existsSync, readFileSync, readdirSync, statSync } from 'node:fs';
14
+ import { createRequire } from 'node:module';
15
+ import * as yaml from 'js-yaml';
16
+ import { run } from '../index.js';
17
+ import { parseWorkflow, validateWorkflow } from '../core/parser.js';
18
+ import { buildDAG, formatDAG } from '../core/dag.js';
19
+ import { listAgents } from '../agents/loader.js';
20
+ import { composeWorkflow } from '../cli/compose.js';
21
+ /** 自动查找 agents 目录 */
22
+ function findAgentsDir(hint) {
23
+ if (hint && existsSync(resolve(hint)))
24
+ return resolve(hint);
25
+ const candidates = [
26
+ resolve('agency-agents-zh'),
27
+ resolve('../agency-agents-zh'),
28
+ resolve('agents'),
29
+ resolve('node_modules/agency-agents-zh'),
30
+ resolve(dirname(new URL(import.meta.url).pathname), '../../node_modules/agency-agents-zh'),
31
+ ];
32
+ for (const dir of candidates) {
33
+ if (existsSync(dir))
34
+ return dir;
35
+ }
36
+ return resolve(hint || './agency-agents-zh');
37
+ }
38
+ /** 递归查找 YAML 文件 */
39
+ function findYamlFiles(dir, result) {
40
+ for (const entry of readdirSync(dir)) {
41
+ const full = resolve(dir, entry);
42
+ if (statSync(full).isDirectory()) {
43
+ findYamlFiles(full, result);
44
+ }
45
+ else if (entry.endsWith('.yaml') || entry.endsWith('.yml')) {
46
+ result.push(full);
47
+ }
48
+ }
49
+ }
50
+ /** 列出 workflows 目录下的 YAML 文件 */
51
+ function discoverWorkflows() {
52
+ const workflowsDir = resolve('workflows');
53
+ if (!existsSync(workflowsDir))
54
+ return [];
55
+ const files = [];
56
+ findYamlFiles(workflowsDir, files);
57
+ return files.map(f => {
58
+ try {
59
+ const content = readFileSync(f, 'utf-8');
60
+ const doc = yaml.load(content);
61
+ return {
62
+ file: relative(process.cwd(), f),
63
+ name: doc?.name || '(unnamed)',
64
+ description: doc?.description || '',
65
+ };
66
+ }
67
+ catch {
68
+ return { file: relative(process.cwd(), f), name: '(parse error)', description: '' };
69
+ }
70
+ });
71
+ }
72
+ /**
73
+ * 静默执行函数 — 临时屏蔽 stdout 输出(引用计数,并发安全)
74
+ * composeWorkflow 等函数内部有 console.log,会污染 MCP 协议通道
75
+ */
76
+ let suppressCount = 0;
77
+ let origStdoutWrite = null;
78
+ async function silentCall(fn) {
79
+ if (suppressCount === 0) {
80
+ origStdoutWrite = process.stdout.write.bind(process.stdout);
81
+ process.stdout.write = () => true;
82
+ }
83
+ suppressCount++;
84
+ try {
85
+ return await fn();
86
+ }
87
+ finally {
88
+ suppressCount--;
89
+ if (suppressCount === 0 && origStdoutWrite) {
90
+ process.stdout.write = origStdoutWrite;
91
+ origStdoutWrite = null;
92
+ }
93
+ }
94
+ }
95
+ export async function startServer(verbose = false) {
96
+ const require = createRequire(import.meta.url);
97
+ const pkgPath = require.resolve('../../package.json');
98
+ const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'));
99
+ const server = new McpServer({
100
+ name: 'agency-orchestrator',
101
+ version: pkg.version || '0.0.0',
102
+ });
103
+ // ─── Tool 1: run_workflow ───
104
+ server.tool('run_workflow', 'Execute a YAML workflow with the DAG engine', {
105
+ path: z.string().describe('Path to workflow YAML file'),
106
+ inputs: z.record(z.string(), z.string()).optional().describe('Key-value input variables'),
107
+ provider: z.enum(['claude-code', 'gemini-cli', 'copilot-cli', 'codex-cli', 'openclaw-cli', 'deepseek', 'claude', 'openai', 'ollama']).optional().describe('Override LLM provider'),
108
+ model: z.string().optional().describe('Override model name'),
109
+ }, async ({ path: workflowPath, inputs, provider, model }) => {
110
+ try {
111
+ const absPath = resolve(workflowPath);
112
+ if (!existsSync(absPath)) {
113
+ return { content: [{ type: 'text', text: `文件不存在: ${workflowPath}` }], isError: true };
114
+ }
115
+ const llmOverride = {};
116
+ if (provider)
117
+ llmOverride.provider = provider;
118
+ if (model)
119
+ llmOverride.model = model;
120
+ const result = await silentCall(() => run(absPath, (inputs || {}), {
121
+ quiet: true,
122
+ llmOverride: Object.keys(llmOverride).length > 0 ? llmOverride : undefined,
123
+ }));
124
+ const lastStep = result.steps[result.steps.length - 1];
125
+ const output = lastStep?.output || '(no output)';
126
+ const tokenSummary = `Tokens: ${result.totalTokens.input} in / ${result.totalTokens.output} out`;
127
+ return {
128
+ content: [{ type: 'text', text: `${output}\n\n---\n${tokenSummary}` }],
129
+ };
130
+ }
131
+ catch (err) {
132
+ return {
133
+ content: [{ type: 'text', text: `执行失败: ${err instanceof Error ? err.message : String(err)}` }],
134
+ isError: true,
135
+ };
136
+ }
137
+ });
138
+ // ─── Tool 2: validate_workflow ───
139
+ server.tool('validate_workflow', 'Validate a workflow YAML without executing', {
140
+ path: z.string().describe('Path to workflow YAML file'),
141
+ }, async ({ path: workflowPath }) => {
142
+ try {
143
+ const absPath = resolve(workflowPath);
144
+ if (!existsSync(absPath)) {
145
+ return { content: [{ type: 'text', text: `文件不存在: ${workflowPath}` }], isError: true };
146
+ }
147
+ const workflow = parseWorkflow(absPath);
148
+ const errors = validateWorkflow(workflow);
149
+ if (errors.length === 0) {
150
+ return {
151
+ content: [{
152
+ type: 'text',
153
+ text: `✅ ${workflow.name} — 校验通过\n步骤数: ${workflow.steps.length}\n输入数: ${(workflow.inputs || []).length}`,
154
+ }],
155
+ };
156
+ }
157
+ else {
158
+ return {
159
+ content: [{
160
+ type: 'text',
161
+ text: `❌ ${workflow.name} — 校验失败:\n${errors.map(e => ` - ${e}`).join('\n')}`,
162
+ }],
163
+ isError: true,
164
+ };
165
+ }
166
+ }
167
+ catch (err) {
168
+ return {
169
+ content: [{ type: 'text', text: `校验错误: ${err instanceof Error ? err.message : String(err)}` }],
170
+ isError: true,
171
+ };
172
+ }
173
+ });
174
+ // ─── Tool 3: list_workflows ───
175
+ server.tool('list_workflows', 'List available workflow templates from the workflows/ directory', {}, async () => {
176
+ try {
177
+ const workflows = discoverWorkflows();
178
+ if (workflows.length === 0) {
179
+ return { content: [{ type: 'text', text: '未找到工作流文件(workflows/ 目录不存在或为空)' }] };
180
+ }
181
+ const lines = workflows.map(w => `- ${w.file}: ${w.name}${w.description ? ` — ${w.description}` : ''}`);
182
+ return {
183
+ content: [{ type: 'text', text: `共 ${workflows.length} 个工作流:\n\n${lines.join('\n')}` }],
184
+ };
185
+ }
186
+ catch (err) {
187
+ return {
188
+ content: [{ type: 'text', text: `列出工作流失败: ${err instanceof Error ? err.message : String(err)}` }],
189
+ isError: true,
190
+ };
191
+ }
192
+ });
193
+ // ─── Tool 4: plan_workflow ───
194
+ server.tool('plan_workflow', 'Show the DAG execution plan for a workflow', {
195
+ path: z.string().describe('Path to workflow YAML file'),
196
+ }, async ({ path: workflowPath }) => {
197
+ try {
198
+ const absPath = resolve(workflowPath);
199
+ if (!existsSync(absPath)) {
200
+ return { content: [{ type: 'text', text: `文件不存在: ${workflowPath}` }], isError: true };
201
+ }
202
+ const workflow = parseWorkflow(absPath);
203
+ const errors = validateWorkflow(workflow);
204
+ if (errors.length > 0) {
205
+ return {
206
+ content: [{ type: 'text', text: `校验失败:\n${errors.map(e => ` - ${e}`).join('\n')}` }],
207
+ isError: true,
208
+ };
209
+ }
210
+ const dag = buildDAG(workflow);
211
+ const dagText = formatDAG(dag);
212
+ return {
213
+ content: [{ type: 'text', text: `${workflow.name}\n\n${dagText}` }],
214
+ };
215
+ }
216
+ catch (err) {
217
+ return {
218
+ content: [{ type: 'text', text: `计划失败: ${err instanceof Error ? err.message : String(err)}` }],
219
+ isError: true,
220
+ };
221
+ }
222
+ });
223
+ // ─── Tool 5: compose_workflow ───
224
+ server.tool('compose_workflow', 'Generate a workflow YAML from a natural language description using AI', {
225
+ description: z.string().describe('One-sentence workflow description'),
226
+ provider: z.enum(['deepseek', 'claude', 'openai', 'ollama']).optional().describe('LLM provider (default: deepseek)'),
227
+ model: z.string().optional().describe('Model name'),
228
+ }, async ({ description, provider, model }) => {
229
+ try {
230
+ const agentsDir = findAgentsDir();
231
+ const llmProvider = provider || 'deepseek';
232
+ const defaultModels = {
233
+ deepseek: 'deepseek-chat',
234
+ claude: 'claude-sonnet-4-20250514',
235
+ openai: 'gpt-4o',
236
+ ollama: 'llama3',
237
+ };
238
+ const llmModel = model || defaultModels[llmProvider] || 'gpt-4o';
239
+ const result = await silentCall(() => composeWorkflow({
240
+ description,
241
+ agentsDir,
242
+ llmConfig: { provider: llmProvider, model: llmModel },
243
+ }));
244
+ let text = `✅ 工作流已生成: ${result.relativePath}\n\n${result.yaml}`;
245
+ if (result.warnings.length > 0) {
246
+ text += `\n\n⚠️ 校验警告:\n${result.warnings.map(w => ` - ${w}`).join('\n')}`;
247
+ }
248
+ return { content: [{ type: 'text', text }] };
249
+ }
250
+ catch (err) {
251
+ return {
252
+ content: [{ type: 'text', text: `生成失败: ${err instanceof Error ? err.message : String(err)}` }],
253
+ isError: true,
254
+ };
255
+ }
256
+ });
257
+ // ─── Tool 6: list_roles ───
258
+ server.tool('list_roles', 'List available AI roles from the agents directory', {
259
+ agents_dir: z.string().optional().describe('Path to agents directory (auto-resolved if omitted)'),
260
+ }, async ({ agents_dir }) => {
261
+ try {
262
+ const agentsDir = findAgentsDir(agents_dir);
263
+ const agents = listAgents(agentsDir);
264
+ const lines = agents.map(a => {
265
+ const emoji = a.emoji || ' ';
266
+ return `${emoji} ${a.name} — ${a.description || '(无描述)'}`;
267
+ });
268
+ return {
269
+ content: [{ type: 'text', text: `共 ${agents.length} 个角色:\n\n${lines.join('\n')}` }],
270
+ };
271
+ }
272
+ catch (err) {
273
+ return {
274
+ content: [{ type: 'text', text: `列出角色失败: ${err instanceof Error ? err.message : String(err)}` }],
275
+ isError: true,
276
+ };
277
+ }
278
+ });
279
+ // ─── Start server ───
280
+ if (verbose) {
281
+ console.error('[ao-mcp] Starting MCP server...');
282
+ }
283
+ const transport = new StdioServerTransport();
284
+ await server.connect(transport);
285
+ if (verbose) {
286
+ console.error('[ao-mcp] Server connected via stdio');
287
+ }
288
+ }
package/dist/types.d.ts CHANGED
@@ -9,10 +9,10 @@ export interface WorkflowDefinition {
9
9
  steps: StepDefinition[];
10
10
  }
11
11
  export interface LLMConfig {
12
- provider: 'claude' | 'openai' | 'ollama' | 'deepseek';
12
+ provider: 'claude' | 'openai' | 'ollama' | 'deepseek' | 'claude-code' | 'gemini-cli' | 'copilot-cli' | 'codex-cli' | 'openclaw-cli';
13
13
  base_url?: string;
14
14
  api_key?: string;
15
- model: string;
15
+ model?: string;
16
16
  max_tokens?: number;
17
17
  timeout?: number;
18
18
  retry?: number;