geo-ai-search-optimization 1.2.9 → 1.2.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -103,6 +103,48 @@ geo-ai-search-optimization agent-runbook ./reports/apply-plan.json --json
103
103
  - 回报清单
104
104
  - 可直接复制给 agent 的 runbook prompt
105
105
 
106
+ ## Agent Executor 命令
107
+
108
+ 如果你希望 agent 不只是拿到 runbook,而是直接得到“这一轮先执行哪 1 个任务”的入口,可以直接用 `agent-executor`:
109
+
110
+ ```bash
111
+ geo-ai-search-optimization agent-executor ./your-site
112
+ geo-ai-search-optimization agent-executor ./reports/apply-plan.json
113
+ geo-ai-search-optimization agent-executor ./reports/apply-plan.json --task fix-02 --json
114
+ ```
115
+
116
+ 它会输出:
117
+
118
+ - 当前先做哪一包
119
+ - 为什么先做这一包
120
+ - do-now checklist
121
+ - stop checklist
122
+ - success checklist
123
+ - 验证命令
124
+ - 给用户的回报模板
125
+ - 可直接复制给 agent 的 executor prompt
126
+
127
+ ## Agent Batch Executor 命令
128
+
129
+ 如果你希望 agent 不只做 1 包,而是把前 2 到 3 包排成一个连续执行序列,同时仍然保持“一次只推进一包”,可以直接用 `agent-batch-executor`:
130
+
131
+ ```bash
132
+ geo-ai-search-optimization agent-batch-executor ./your-site
133
+ geo-ai-search-optimization agent-batch-executor ./reports/apply-plan.json
134
+ geo-ai-search-optimization agent-batch-executor ./reports/apply-plan.json --task fix-02 --count 3 --format json --out ./reports/agent-batch-executor.json
135
+ ```
136
+
137
+ `agent-batch-executor` 会输出:
138
+
139
+ - 当前批次先推进哪几包
140
+ - 为什么按这个顺序推进
141
+ - 每一包的 do-now checklist
142
+ - 每一包的 stop checklist
143
+ - 每一包的 success checklist
144
+ - 批次总验证命令
145
+ - 批次收尾命令
146
+ - 可直接复制给 agent 的 batch prompt
147
+
106
148
  ## Quick Start
107
149
 
108
150
  如果你要从 0 到 1 启动一个 GEO 项目,建议照这个顺序做。
@@ -499,6 +541,8 @@ geo-ai-search-optimization install --target ./tmp/custom-skills --json
499
541
  geo-ai-search-optimization auto-flow "audit this site and tell me the next skill"
500
542
  geo-ai-search-optimization agent-session ./your-site
501
543
  geo-ai-search-optimization agent-runbook ./your-site
544
+ geo-ai-search-optimization agent-executor ./your-site
545
+ geo-ai-search-optimization agent-batch-executor ./your-site
502
546
  geo-ai-search-optimization skills
503
547
  geo-ai-search-optimization where
504
548
  geo-ai-search-optimization doctor
@@ -562,6 +606,20 @@ geo-ai-search-optimization help
562
606
  - 输出开始前检查、工作规则、停止条件、验证清单、回报清单
563
607
  - 新增 `geo-ai-search-optimization-agent-runbook` skill
564
608
 
609
+ ## New in 1.2.10
610
+
611
+ - 新增 `agent-executor` 命令
612
+ - 把 runbook 再收敛成“这一轮先做哪 1 包”的单任务入口
613
+ - 输出 do-now checklist、stop checklist、success checklist、验证命令和回报模板
614
+ - 新增 `geo-ai-search-optimization-agent-executor` skill
615
+
616
+ ## New in 1.2.11
617
+
618
+ - 新增 `agent-batch-executor` 命令
619
+ - 让 agent 可以连续推进前几包任务,但仍然保持一次只执行一包
620
+ - 输出批次顺序、每包检查清单、批次总验证命令和收尾命令
621
+ - 新增 `geo-ai-search-optimization-agent-batch-executor` skill
622
+
565
623
  ## New in 1.2.5
566
624
 
567
625
  - 新增 `publish-pack`
@@ -751,6 +809,8 @@ The installed package now includes a bundled GEO skill pack, including:
751
809
  - `geo-ai-search-optimization-auto-flow`
752
810
  - `geo-ai-search-optimization-agent-session`
753
811
  - `geo-ai-search-optimization-agent-runbook`
812
+ - `geo-ai-search-optimization-agent-executor`
813
+ - `geo-ai-search-optimization-agent-batch-executor`
754
814
  - `geo-ai-search-optimization-usage`
755
815
  - `geo-ai-search-optimization-agent-handoff`
756
816
  - `geo-ai-search-optimization-repair-loop`
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "geo-ai-search-optimization",
3
- "version": "1.2.9",
3
+ "version": "1.2.11",
4
4
  "description": "Install and run a Generative Engine Optimization (GEO)-first, SEO-supported Codex skill for website optimization.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -52,6 +52,26 @@ Best for:
52
52
  - telling the next agent what to check before acting
53
53
  - defining stop conditions, validation checklist, and reporting checklist
54
54
 
55
+ ### `geo-ai-search-optimization-agent-executor`
56
+
57
+ Use this when the next agent should stop planning and start one concrete execution packet.
58
+
59
+ Best for:
60
+
61
+ - selecting the first packet to execute now
62
+ - reducing a runbook into one actionable task
63
+ - giving the next agent a single-task prompt, validation commands, and reply template
64
+
65
+ ### `geo-ai-search-optimization-agent-batch-executor`
66
+
67
+ Use this when the next agent should continuously advance the first few execution packets, but still only work on one packet at a time.
68
+
69
+ Best for:
70
+
71
+ - turning an `apply-plan` into a short execution queue
72
+ - making the next agent finish packet 1 before packet 2 starts
73
+ - giving the next agent a batch-level prompt, per-packet checklists, and final closeout commands
74
+
55
75
  ## Usage guide
56
76
 
57
77
  ### `geo-ai-search-optimization-usage`
@@ -0,0 +1,24 @@
1
+ ---
2
+ name: geo-ai-search-optimization-agent-batch-executor
3
+ description: Turn a GEO input into a short multi-packet execution queue for the next agent. Use when an agent should continuously advance the first 2 to 3 GEO packets in order, while still executing only one packet at a time, with per-packet checklists, validation commands, and closeout steps.
4
+ ---
5
+
6
+ # GEO Agent Batch Executor
7
+
8
+ Use this skill when the next agent should not stop at one task, but also should not expand into an uncontrolled multi-task run.
9
+
10
+ `GEO = Generative Engine Optimization`
11
+
12
+ ## What it does
13
+
14
+ - select the first few execution packets from an apply-plan or equivalent artifact
15
+ - keep the execution order explicit
16
+ - enforce one-packet-at-a-time progress
17
+ - provide per-packet do-now, stop, and success checklists
18
+ - give the next agent one batch prompt plus final closeout commands
19
+
20
+ ## Best use
21
+
22
+ - when `agent-executor` is too narrow because the next agent should keep going after packet 1
23
+ - when a PM wants “finish the first 2 to 3 packets in order”
24
+ - when the next agent needs a short queue, not a full open-ended plan
@@ -0,0 +1,4 @@
1
+ interface:
2
+ display_name: "GEO Agent Batch Executor"
3
+ short_description: "Queue the first GEO packets for one-by-one execution"
4
+ default_prompt: "Use $geo-ai-search-optimization-agent-batch-executor to choose the first 2 to 3 GEO packets to advance in order, while still executing only one packet at a time."
@@ -0,0 +1,24 @@
1
+ ---
2
+ name: geo-ai-search-optimization-agent-executor
3
+ description: Turn a GEO input into a single-task execution entrypoint for the next agent. Use when an agent should stop planning and start one concrete GEO packet now, with a selected task, do-now checklist, validation commands, success criteria, and a reply template.
4
+ ---
5
+
6
+ # GEO Agent Executor
7
+
8
+ Use this skill when the next agent should execute one packet now, not keep expanding the plan.
9
+
10
+ `GEO = Generative Engine Optimization`
11
+
12
+ ## What it does
13
+
14
+ - select the first or requested execution packet
15
+ - compress the runbook into one task entrypoint
16
+ - define do-now, stop, and success checklists
17
+ - provide validation commands and a reply template
18
+ - make the next agent focus on one packet at a time
19
+
20
+ ## Best use
21
+
22
+ - when `agent-runbook` is still too broad
23
+ - when a PM or another agent asks “this round, what should we do first?”
24
+ - when execution should start from one concrete packet
@@ -0,0 +1,4 @@
1
+ interface:
2
+ display_name: "GEO Agent Executor"
3
+ short_description: "Start one GEO execution packet right now"
4
+ default_prompt: "Use $geo-ai-search-optimization-agent-executor to choose the first GEO packet to execute now, with a do-now checklist, validation commands, and a reply template."
@@ -13,24 +13,26 @@ Treat this tool as a PM-friendly GEO workflow for websites.
13
13
 
14
14
  `GEO = Generative Engine Optimization`
15
15
 
16
- The package is best explained as sixteen layers:
16
+ The package is best explained as eighteen layers:
17
17
 
18
18
  1. `auto-flow`: auto-select the next skill and command chain
19
19
  2. `agent-session`: build a runnable session for the next agent
20
20
  3. `agent-runbook`: execution manual and checklist for the next agent
21
- 4. `skills`: inspect the bundled skill package
22
- 5. `onboard-url` / `onboard`: first look
23
- 6. `scan`: raw signal check
24
- 7. `audit` / `report`: diagnosis
25
- 8. `fix-plan` / `owner-board`: execution planning
26
- 9. `agent-handoff`: agent takeover package
27
- 10. `apply-plan`: execution loop
28
- 11. `completion-report`: closeout
29
- 12. `handoff-bundle`: all-in-one package
30
- 13. `share-pack`: audience-ready delivery
31
- 14. `export-pack`: folder export
32
- 15. `html-pack` / `publish-pack`: browsable and final delivery output
33
- 16. `pm-brief` / `roadmap`: stakeholder alignment
21
+ 4. `agent-executor`: choose one packet to execute right now
22
+ 5. `agent-batch-executor`: queue the first few packets, but still advance one packet at a time
23
+ 6. `skills`: inspect the bundled skill package
24
+ 7. `onboard-url` / `onboard`: first look
25
+ 8. `scan`: raw signal check
26
+ 9. `audit` / `report`: diagnosis
27
+ 10. `fix-plan` / `owner-board`: execution planning
28
+ 11. `agent-handoff`: agent takeover package
29
+ 12. `apply-plan`: execution loop
30
+ 13. `completion-report`: closeout
31
+ 14. `handoff-bundle`: all-in-one package
32
+ 15. `share-pack`: audience-ready delivery
33
+ 16. `export-pack`: folder export
34
+ 17. `html-pack` / `publish-pack`: browsable and final delivery output
35
+ 18. `pm-brief` / `roadmap`: stakeholder alignment
34
36
 
35
37
  ## Recommended command order
36
38
 
@@ -40,6 +42,8 @@ If the user only has a website URL:
40
42
  npx geo-ai-search-optimization auto-flow https://example.com
41
43
  npx geo-ai-search-optimization agent-session https://example.com
42
44
  npx geo-ai-search-optimization agent-runbook https://example.com
45
+ npx geo-ai-search-optimization agent-executor https://example.com
46
+ npx geo-ai-search-optimization agent-batch-executor https://example.com
43
47
  npx geo-ai-search-optimization onboard-url https://example.com
44
48
  npx geo-ai-search-optimization pm-brief https://example.com
45
49
  npx geo-ai-search-optimization roadmap https://example.com
@@ -51,6 +55,8 @@ If the user has the website codebase:
51
55
  npx geo-ai-search-optimization auto-flow ./your-site
52
56
  npx geo-ai-search-optimization agent-session ./your-site
53
57
  npx geo-ai-search-optimization agent-runbook ./your-site
58
+ npx geo-ai-search-optimization agent-executor ./your-site
59
+ npx geo-ai-search-optimization agent-batch-executor ./your-site
54
60
  npx geo-ai-search-optimization scan ./your-site
55
61
  npx geo-ai-search-optimization audit ./your-site
56
62
  npx geo-ai-search-optimization fix-plan ./your-site
@@ -71,6 +77,8 @@ npx geo-ai-search-optimization roadmap ./your-site
71
77
  - `auto-flow`: auto-select the next skill and command order from a task brief, URL, project path, or GEO artifact
72
78
  - `agent-session`: build a step-by-step session packet for the next agent from the same kinds of inputs
73
79
  - `agent-runbook`: build a checklist-driven runbook with preflight, validation, and reporting rules
80
+ - `agent-executor`: select one packet to execute now and package it into a single-task entrypoint
81
+ - `agent-batch-executor`: line up the first few packets in execution order while preserving one-packet-at-a-time discipline
74
82
  - `onboard-url`: first-time website check from a live URL
75
83
  - `onboard`: interactive first-time onboarding
76
84
  - `skills`: list the bundled skills and decide which skill or command chain fits the task
@@ -98,6 +106,8 @@ When explaining the tool to a user:
98
106
  - if the user or the next agent is unsure where to start, move them to `auto-flow` first
99
107
  - if the user wants something the next agent can follow step by step, move them to `agent-session`
100
108
  - if the user wants the next agent to follow a checklist and execution manual, move them to `agent-runbook`
109
+ - if the user wants the next agent to start one concrete task now, move them to `agent-executor`
110
+ - if the user wants the next agent to continuously advance the first 2 to 3 packets in order, move them to `agent-batch-executor`
101
111
  - explain the result in PM language, not implementation jargon
102
112
  - if the user sounds new, start with `onboard-url` or `quick-start`
103
113
  - if the user wants another agent to take over, move them to `agent-handoff`
@@ -0,0 +1,327 @@
1
+ import { readFile } from "node:fs/promises";
2
+ import { createApplyPlan } from "./apply-plan.js";
3
+ import { createAgentExecutor } from "./agent-executor.js";
4
+ import { writeScanOutput } from "./scan.js";
5
+
6
+ const VALID_FORMATS = new Set(["markdown", "json"]);
7
+
8
+ function normalizeFormat(format) {
9
+ const resolved = (format || "markdown").toLowerCase();
10
+ if (!VALID_FORMATS.has(resolved)) {
11
+ throw new Error(`不支持的 agent-batch-executor 格式:${format}。可选值:${Array.from(VALID_FORMATS).join(", ")}`);
12
+ }
13
+ return resolved;
14
+ }
15
+
16
+ function normalizeCount(count) {
17
+ if (count == null) {
18
+ return 3;
19
+ }
20
+
21
+ const parsed = Number.parseInt(count, 10);
22
+ if (!Number.isInteger(parsed) || parsed <= 0) {
23
+ throw new Error("--count 必须是正整数");
24
+ }
25
+
26
+ return Math.min(parsed, 5);
27
+ }
28
+
29
+ async function loadApplyPlanArtifact(input) {
30
+ const raw = await readFile(input, "utf8");
31
+ const parsed = JSON.parse(raw);
32
+
33
+ if (parsed?.kind === "geo-apply-plan") {
34
+ return parsed;
35
+ }
36
+ if (parsed?.kind === "geo-agent-runbook" && parsed.applyPlan?.kind === "geo-apply-plan") {
37
+ return parsed.applyPlan;
38
+ }
39
+ if (parsed?.kind === "geo-agent-executor" && parsed.applyPlan?.kind === "geo-apply-plan") {
40
+ return parsed.applyPlan;
41
+ }
42
+
43
+ return null;
44
+ }
45
+
46
+ async function resolveApplyPlan(input) {
47
+ const artifact = await loadApplyPlanArtifact(input).catch(() => null);
48
+ if (artifact) {
49
+ return artifact;
50
+ }
51
+
52
+ return createApplyPlan(input, {
53
+ format: "json"
54
+ });
55
+ }
56
+
57
+ function selectPackets(applyPlan, count, startTaskId) {
58
+ const packets = applyPlan?.packets || [];
59
+ if (packets.length === 0) {
60
+ return [];
61
+ }
62
+
63
+ let startIndex = 0;
64
+ if (startTaskId) {
65
+ startIndex = packets.findIndex((packet) => packet.id === startTaskId);
66
+ if (startIndex === -1) {
67
+ throw new Error(`找不到执行包:${startTaskId}`);
68
+ }
69
+ }
70
+
71
+ return packets.slice(startIndex, startIndex + count);
72
+ }
73
+
74
+ async function buildPacketExecutors(input, packets, options) {
75
+ const results = [];
76
+
77
+ for (const packet of packets) {
78
+ const executor = await createAgentExecutor(input, {
79
+ format: "json",
80
+ intent: options.intent,
81
+ taskId: packet.id
82
+ });
83
+
84
+ results.push({
85
+ position: results.length + 1,
86
+ id: packet.id,
87
+ title: packet.title,
88
+ priority: packet.priority,
89
+ owner: packet.owner,
90
+ executorMode: executor.executorMode,
91
+ doNowChecklist: executor.doNowChecklist,
92
+ stopChecklist: executor.stopChecklist,
93
+ successChecklist: executor.successChecklist,
94
+ validationCommands: executor.validationCommands,
95
+ userReplyTemplate: executor.userReplyTemplate,
96
+ executorPrompt: executor.executorPrompt,
97
+ startCommand: `geo-ai-search-optimization agent-executor ${executor.source} --task ${packet.id}`,
98
+ nextCommands: executor.nextCommands
99
+ });
100
+ }
101
+
102
+ return results;
103
+ }
104
+
105
+ function inferBatchMode(packetExecutors) {
106
+ if (packetExecutors.length === 0) {
107
+ return "needs-context";
108
+ }
109
+
110
+ const modes = new Set(packetExecutors.map((packet) => packet.executorMode));
111
+ if (modes.size === 1) {
112
+ return packetExecutors[0].executorMode;
113
+ }
114
+ return "mixed";
115
+ }
116
+
117
+ function buildSelectionReason(packets, applyPlan, count, startTaskId) {
118
+ if (packets.length === 0) {
119
+ return "当前还没有可批量执行的任务包,说明需要先补上下文或先生成 apply-plan。";
120
+ }
121
+
122
+ if (startTaskId) {
123
+ return `已从指定任务 ${startTaskId} 开始,连续选出后续 ${packets.length} 包,按顺序推进。`;
124
+ }
125
+
126
+ if (packets.length === 1) {
127
+ return "当前只拿到 1 个任务包,因此直接把它作为本轮批处理入口。";
128
+ }
129
+
130
+ const available = applyPlan?.packets?.length || packets.length;
131
+ if (available < count) {
132
+ return `计划最多取前 ${count} 包,但当前只有 ${available} 包可用,因此按现有顺序推进这 ${packets.length} 包。`;
133
+ }
134
+
135
+ return `已按优先级选出前 ${packets.length} 包,按顺序推进,但一次只执行一包。`;
136
+ }
137
+
138
+ function buildBatchRules(batchMode) {
139
+ const items = [
140
+ "始终一次只推进一包,不要同时展开多个修复包。",
141
+ "每做完一包,都先检查完成标准和验证命令,再决定是否进入下一包。",
142
+ "如果上一包没有通过验收,不要直接跳到下一包。"
143
+ ];
144
+
145
+ if (batchMode === "direct-fix") {
146
+ items.push("每包都优先改复用层,而不是只 patch 单页。");
147
+ }
148
+ if (batchMode === "recommendation-only") {
149
+ items.push("当前更适合给建议,不要伪造已经完成的代码修改。");
150
+ }
151
+
152
+ return items;
153
+ }
154
+
155
+ function buildQueueSummary(packetExecutors) {
156
+ return packetExecutors.map((packet, index) => ({
157
+ position: index + 1,
158
+ id: packet.id,
159
+ title: packet.title,
160
+ priority: packet.priority,
161
+ owner: packet.owner,
162
+ startCommand: packet.startCommand,
163
+ stopIf: packet.stopChecklist[packet.stopChecklist.length - 1] || "当前包未达成完成标准时不要继续。",
164
+ afterSuccess:
165
+ index < packetExecutors.length - 1
166
+ ? `进入下一包:${packetExecutors[index + 1].id}`
167
+ : "进入 completion-report 与 handoff-bundle 收尾。"
168
+ }));
169
+ }
170
+
171
+ function buildValidationRollup(packetExecutors) {
172
+ const commands = new Set();
173
+ for (const packet of packetExecutors) {
174
+ for (const command of packet.validationCommands) {
175
+ commands.add(command);
176
+ }
177
+ }
178
+ return Array.from(commands);
179
+ }
180
+
181
+ function buildFinalCommands(source) {
182
+ return [
183
+ `geo-ai-search-optimization completion-report ${source}`,
184
+ `geo-ai-search-optimization handoff-bundle ${source}`,
185
+ `geo-ai-search-optimization publish-pack ${source}`
186
+ ];
187
+ }
188
+
189
+ function buildBatchPrompt(batch) {
190
+ const lines = [
191
+ "你现在进入 GEO 多任务批次执行模式。",
192
+ `当前输入:${batch.source}`,
193
+ `批次模式:${batch.batchMode}`,
194
+ `批次目标:${batch.goal}`,
195
+ `批次选择原因:${batch.selectionReason}`,
196
+ "请遵守:一次只推进一包,上一包通过验证后再进入下一包。"
197
+ ];
198
+
199
+ if (batch.packetExecutors.length > 0) {
200
+ lines.push("当前批次顺序:");
201
+ for (const packet of batch.packetExecutors) {
202
+ lines.push(`${packet.position}. ${packet.id}|${packet.title}`);
203
+ }
204
+ } else {
205
+ lines.push("当前还没有可执行包。请先告诉用户缺什么上下文。");
206
+ }
207
+
208
+ lines.push("每一包都要输出:先检查什么、怎么做、如何验证、完成后如何回报。");
209
+ return lines.join("\n");
210
+ }
211
+
212
+ export async function createAgentBatchExecutor(input, options = {}) {
213
+ const format = normalizeFormat(options.format);
214
+ const count = normalizeCount(options.count);
215
+ const applyPlan = await resolveApplyPlan(input);
216
+ const selectedPackets = selectPackets(applyPlan, count, options.taskId);
217
+ const packetExecutors = await buildPacketExecutors(input, selectedPackets, options);
218
+ const batchMode = inferBatchMode(packetExecutors);
219
+ const source = packetExecutors[0]?.startCommand ? applyPlan.source || input : applyPlan?.source || input;
220
+
221
+ return {
222
+ kind: "geo-agent-batch-executor",
223
+ input,
224
+ source,
225
+ sourceType: applyPlan?.sourceType || "unknown",
226
+ artifactKind: applyPlan?.kind || "unknown",
227
+ format,
228
+ batchMode,
229
+ packetCount: packetExecutors.length,
230
+ goal:
231
+ packetExecutors.length > 0
232
+ ? `按顺序推进这 ${packetExecutors.length} 包,但每次只执行一包。`
233
+ : "当前先补上下文,再进入多任务批次执行。",
234
+ selectionReason: buildSelectionReason(selectedPackets, applyPlan, count, options.taskId),
235
+ batchRules: buildBatchRules(batchMode),
236
+ queueSummary: buildQueueSummary(packetExecutors),
237
+ packetExecutors,
238
+ validationRollup: buildValidationRollup(packetExecutors),
239
+ finalCommands: buildFinalCommands(source),
240
+ batchPrompt: buildBatchPrompt({
241
+ source,
242
+ batchMode,
243
+ goal:
244
+ packetExecutors.length > 0
245
+ ? `按顺序推进这 ${packetExecutors.length} 包,但每次只执行一包。`
246
+ : "当前先补上下文,再进入多任务批次执行。",
247
+ selectionReason: buildSelectionReason(selectedPackets, applyPlan, count, options.taskId),
248
+ packetExecutors
249
+ }),
250
+ applyPlan
251
+ };
252
+ }
253
+
254
+ export function renderAgentBatchExecutorMarkdown(batch) {
255
+ const lines = [
256
+ "# GEO Agent Batch Executor",
257
+ "",
258
+ `- 输入:\`${batch.source}\``,
259
+ `- 来源类型:\`${batch.sourceType}\``,
260
+ `- 工件类型:\`${batch.artifactKind}\``,
261
+ `- 批次模式:\`${batch.batchMode}\``,
262
+ `- 批次数量:\`${batch.packetCount}\``,
263
+ `- 目标:${batch.goal}`,
264
+ `- 选择原因:${batch.selectionReason}`,
265
+ "",
266
+ "## 批次规则",
267
+ ""
268
+ ];
269
+
270
+ for (const item of batch.batchRules) {
271
+ lines.push(`- [ ] ${item}`);
272
+ }
273
+
274
+ lines.push("", "## 执行顺序", "");
275
+ for (const item of batch.queueSummary) {
276
+ lines.push(`### ${item.position}. ${item.id}|${item.title}`, "");
277
+ lines.push(`- 优先级:${item.priority}`);
278
+ lines.push(`- Owner:${item.owner}`);
279
+ lines.push(`- 开始命令:\`${item.startCommand}\``);
280
+ lines.push(`- 继续条件:${item.afterSuccess}`);
281
+ lines.push(`- 停下条件:${item.stopIf}`);
282
+ lines.push("");
283
+ }
284
+
285
+ for (const packet of batch.packetExecutors) {
286
+ lines.push(`## ${packet.position}. ${packet.id}|${packet.title}`, "");
287
+ lines.push("- Do now:");
288
+ for (const item of packet.doNowChecklist) {
289
+ lines.push(` - [ ] ${item}`);
290
+ }
291
+ lines.push("- 停下条件:");
292
+ for (const item of packet.stopChecklist) {
293
+ lines.push(` - [ ] ${item}`);
294
+ }
295
+ lines.push("- 完成标准:");
296
+ for (const item of packet.successChecklist) {
297
+ lines.push(` - [ ] ${item}`);
298
+ }
299
+ lines.push("- 验证命令:");
300
+ for (const command of packet.validationCommands) {
301
+ lines.push(` - \`${command}\``);
302
+ }
303
+ lines.push("- 回报模板:");
304
+ lines.push("```text");
305
+ lines.push(packet.userReplyTemplate);
306
+ lines.push("```");
307
+ lines.push("");
308
+ }
309
+
310
+ lines.push("## 批次总验证命令", "");
311
+ for (const command of batch.validationRollup) {
312
+ lines.push(`- \`${command}\``);
313
+ }
314
+
315
+ lines.push("", "## 最终收尾命令", "");
316
+ for (const command of batch.finalCommands) {
317
+ lines.push(`- \`${command}\``);
318
+ }
319
+
320
+ lines.push("", "## 可直接复制给 Agent 的 Batch Prompt", "", "```text", batch.batchPrompt, "```");
321
+
322
+ return `${lines.join("\n")}\n`;
323
+ }
324
+
325
+ export async function writeAgentBatchExecutorOutput(outputPath, content) {
326
+ return writeScanOutput(outputPath, content);
327
+ }
@@ -0,0 +1,319 @@
1
+ import { readFile } from "node:fs/promises";
2
+ import { createApplyPlan } from "./apply-plan.js";
3
+ import { createAgentRunbook } from "./agent-runbook.js";
4
+ import { writeScanOutput } from "./scan.js";
5
+
6
+ const VALID_FORMATS = new Set(["markdown", "json"]);
7
+
8
+ function normalizeFormat(format) {
9
+ const resolved = (format || "markdown").toLowerCase();
10
+ if (!VALID_FORMATS.has(resolved)) {
11
+ throw new Error(`不支持的 agent-executor 格式:${format}。可选值:${Array.from(VALID_FORMATS).join(", ")}`);
12
+ }
13
+ return resolved;
14
+ }
15
+
16
+ async function loadJsonArtifact(input) {
17
+ const raw = await readFile(input, "utf8");
18
+ return JSON.parse(raw);
19
+ }
20
+
21
+ async function resolveRunbookAndPlan(input, options = {}) {
22
+ const runbook = await createAgentRunbook(input, {
23
+ format: "json",
24
+ intent: options.intent,
25
+ taskId: options.taskId
26
+ });
27
+
28
+ if (runbook.applyPlan?.kind === "geo-apply-plan") {
29
+ return {
30
+ runbook,
31
+ applyPlan: runbook.applyPlan
32
+ };
33
+ }
34
+
35
+ if (runbook.sourceType === "json") {
36
+ const parsed = await loadJsonArtifact(input).catch(() => null);
37
+ if (parsed?.kind === "geo-apply-plan") {
38
+ return {
39
+ runbook,
40
+ applyPlan: parsed
41
+ };
42
+ }
43
+ if (parsed?.kind === "geo-agent-runbook" && parsed.applyPlan?.kind === "geo-apply-plan") {
44
+ return {
45
+ runbook,
46
+ applyPlan: parsed.applyPlan
47
+ };
48
+ }
49
+ }
50
+
51
+ try {
52
+ const applyPlan = await createApplyPlan(input, {
53
+ format: "json",
54
+ taskId: options.taskId
55
+ });
56
+ return {
57
+ runbook,
58
+ applyPlan
59
+ };
60
+ } catch {
61
+ return {
62
+ runbook,
63
+ applyPlan: null
64
+ };
65
+ }
66
+ }
67
+
68
+ function inferExecutorMode(runbook, applyPlan) {
69
+ if (!applyPlan?.packets?.length) {
70
+ if (runbook.contextNeeded?.length) {
71
+ return "needs-context";
72
+ }
73
+ return "planning-only";
74
+ }
75
+ return applyPlan.executionType || "guided-planning";
76
+ }
77
+
78
+ function selectPacket(applyPlan, taskId) {
79
+ if (!applyPlan?.packets?.length) {
80
+ return null;
81
+ }
82
+
83
+ if (taskId) {
84
+ const selected = applyPlan.packets.find((packet) => packet.id === taskId);
85
+ if (!selected) {
86
+ throw new Error(`找不到执行包:${taskId}`);
87
+ }
88
+ return selected;
89
+ }
90
+
91
+ return applyPlan.packets[0];
92
+ }
93
+
94
+ function buildSelectionReason(packet, applyPlan, taskId) {
95
+ if (!packet) {
96
+ return "当前还没有可执行任务包,说明需要先补上下文或先生成 apply-plan。";
97
+ }
98
+ if (taskId) {
99
+ return `已按指定任务 ID 选中:${taskId}。`;
100
+ }
101
+ if (applyPlan?.selectedTaskCount > 1) {
102
+ return "默认选择第一包,代表当前优先级最高、最适合立刻开始的一项任务。";
103
+ }
104
+ return "当前只有一个任务包,因此直接将它作为本轮执行入口。";
105
+ }
106
+
107
+ function buildDoNowChecklist(packet, executorMode) {
108
+ if (!packet) {
109
+ return [];
110
+ }
111
+
112
+ const items = [
113
+ `先锁定任务范围:${packet.title}`,
114
+ ...packet.inspectTargets.slice(0, 3).map((item) => `先检查:${item}`),
115
+ ...packet.editPlan.slice(0, 3).map((item) => `执行动作:${item}`)
116
+ ];
117
+
118
+ if (executorMode === "direct-fix") {
119
+ items.push("开始修改前先确认会影响整类页面的复用层,而不是只 patch 单页。");
120
+ }
121
+
122
+ return items;
123
+ }
124
+
125
+ function buildStopChecklist(runbook, packet, executorMode) {
126
+ const items = [...(runbook.stopConditions || [])];
127
+
128
+ if (!packet) {
129
+ items.push("还没有拿到执行包时,不要假装已经进入真正修复阶段。");
130
+ return items;
131
+ }
132
+
133
+ if (executorMode === "recommendation-only") {
134
+ items.push("如果当前只有网址或建议上下文,不要输出伪造的代码修改结果。");
135
+ }
136
+
137
+ items.push("如果执行中发现这不是最高优先级任务,应先说明原因,再决定是否切换。");
138
+ return items;
139
+ }
140
+
141
+ function buildSuccessChecklist(packet) {
142
+ if (!packet) {
143
+ return [];
144
+ }
145
+
146
+ return [
147
+ ...packet.doneWhen,
148
+ "你已经能用 PM 可读的方式说明:改了什么、为什么改、验证结果是什么。"
149
+ ];
150
+ }
151
+
152
+ function buildNextCommands(runbook, packet, input) {
153
+ if (!packet) {
154
+ return runbook.nextCommands || [`geo-ai-search-optimization agent-runbook ${input}`];
155
+ }
156
+
157
+ const source = runbook.source;
158
+ return [
159
+ `geo-ai-search-optimization completion-report ${source}`,
160
+ `geo-ai-search-optimization handoff-bundle ${source}`
161
+ ];
162
+ }
163
+
164
+ function buildUserReplyTemplate(packet, executorMode) {
165
+ if (!packet) {
166
+ return [
167
+ "结论:我还不能直接执行,因为当前缺少足够上下文。",
168
+ "我目前确认到的情况:",
169
+ "- ",
170
+ "我还需要你补充:",
171
+ "- ",
172
+ "拿到这些之后,我会先执行:",
173
+ "- "
174
+ ].join("\n");
175
+ }
176
+
177
+ const closingLine =
178
+ executorMode === "direct-fix"
179
+ ? "我已经完成这一包可直接落地的部分,并附上复测结果。"
180
+ : executorMode === "recommendation-only"
181
+ ? "我已经完成这一包的建议版执行,但还需要更多上下文才能继续实际修改。"
182
+ : "我已经把这一包的执行顺序和阻塞项整理好,下一步可以继续推进。";
183
+
184
+ return [
185
+ `任务:${packet.title}`,
186
+ `结论:${closingLine}`,
187
+ "这次我先做了什么:",
188
+ "- ",
189
+ "我检查了哪些文件 / 页面 / 模板:",
190
+ "- ",
191
+ "我实际修改或建议了什么:",
192
+ "- ",
193
+ "验证结果:",
194
+ "- ",
195
+ "剩余风险或下一步:",
196
+ "- "
197
+ ].join("\n");
198
+ }
199
+
200
+ function buildExecutorPrompt(runbook, packet, executorMode, selectionReason) {
201
+ const lines = [
202
+ "你现在进入 GEO 单任务执行模式。",
203
+ `当前输入:${runbook.source}`,
204
+ `当前阶段:${runbook.stage}`,
205
+ `执行模式:${executorMode}`,
206
+ `任务选择原因:${selectionReason}`
207
+ ];
208
+
209
+ if (packet) {
210
+ lines.push(`本轮只做这一包:${packet.title}`);
211
+ lines.push("请你按这个顺序输出:");
212
+ lines.push("1. 先检查什么");
213
+ lines.push("2. 你准备如何执行这一包");
214
+ lines.push("3. 你会如何验证");
215
+ lines.push("4. 完成后如何向用户回报");
216
+ } else {
217
+ lines.push("当前还不能执行具体任务。请先明确缺什么上下文,再告诉用户下一步要补什么。");
218
+ }
219
+
220
+ lines.push("一次只推进一包,不要在同一轮同时展开多个任务。");
221
+ return lines.join("\n");
222
+ }
223
+
224
+ export async function createAgentExecutor(input, options = {}) {
225
+ const format = normalizeFormat(options.format);
226
+ const { runbook, applyPlan } = await resolveRunbookAndPlan(input, options);
227
+ const executorMode = inferExecutorMode(runbook, applyPlan);
228
+ const packet = selectPacket(applyPlan, options.taskId);
229
+ const selectionReason = buildSelectionReason(packet, applyPlan, options.taskId);
230
+
231
+ return {
232
+ kind: "geo-agent-executor",
233
+ input,
234
+ source: runbook.source,
235
+ sourceType: runbook.sourceType,
236
+ artifactKind: runbook.artifactKind,
237
+ format,
238
+ intent: runbook.intent,
239
+ stage: runbook.stage,
240
+ executorMode,
241
+ goal: packet ? `先完成这一包:${packet.title}` : runbook.goal,
242
+ selectionReason,
243
+ selectedSkill: {
244
+ name: "geo-ai-search-optimization-agent-executor",
245
+ displayName: "GEO Agent Executor",
246
+ shortDescription: "Start one GEO execution packet right now"
247
+ },
248
+ selectedPacket: packet,
249
+ doNowChecklist: buildDoNowChecklist(packet, executorMode),
250
+ stopChecklist: buildStopChecklist(runbook, packet, executorMode),
251
+ successChecklist: buildSuccessChecklist(packet),
252
+ validationCommands: packet?.validationCommands || runbook.validationChecklist || [],
253
+ userReplyTemplate: buildUserReplyTemplate(packet, executorMode),
254
+ nextCommands: buildNextCommands(runbook, packet, input),
255
+ executorPrompt: buildExecutorPrompt(runbook, packet, executorMode, selectionReason),
256
+ agentRunbook: runbook,
257
+ applyPlan
258
+ };
259
+ }
260
+
261
+ export function renderAgentExecutorMarkdown(executor) {
262
+ const lines = [
263
+ "# GEO Agent Executor",
264
+ "",
265
+ `- 输入:\`${executor.source}\``,
266
+ `- 输入类型:\`${executor.sourceType}\``,
267
+ `- 工件类型:\`${executor.artifactKind}\``,
268
+ `- 当前阶段:${executor.stage}`,
269
+ `- 执行模式:\`${executor.executorMode}\``,
270
+ `- 本轮目标:${executor.goal}`,
271
+ `- 任务选择原因:${executor.selectionReason}`,
272
+ "",
273
+ "## 现在先做什么",
274
+ ""
275
+ ];
276
+
277
+ for (const item of executor.doNowChecklist) {
278
+ lines.push(`- [ ] ${item}`);
279
+ }
280
+
281
+ if (executor.selectedPacket) {
282
+ lines.push("", "## 当前任务包", "");
283
+ lines.push(`- ID:\`${executor.selectedPacket.id}\``);
284
+ lines.push(`- 标题:${executor.selectedPacket.title}`);
285
+ lines.push(`- 优先级:${executor.selectedPacket.priority}`);
286
+ lines.push(`- Owner:${executor.selectedPacket.owner}`);
287
+ lines.push(`- 执行类型:\`${executor.selectedPacket.executionType}\``);
288
+ lines.push("");
289
+ lines.push("### 验证命令", "");
290
+ for (const command of executor.validationCommands) {
291
+ lines.push(`- \`${command}\``);
292
+ }
293
+ }
294
+
295
+ lines.push("", "## 需要停下来的情况", "");
296
+ for (const item of executor.stopChecklist) {
297
+ lines.push(`- [ ] ${item}`);
298
+ }
299
+
300
+ lines.push("", "## 完成标准", "");
301
+ for (const item of executor.successChecklist) {
302
+ lines.push(`- [ ] ${item}`);
303
+ }
304
+
305
+ lines.push("", "## 完成后怎么回报用户", "", "```text", executor.userReplyTemplate, "```");
306
+
307
+ lines.push("", "## 下一步推荐命令", "");
308
+ for (const command of executor.nextCommands) {
309
+ lines.push(`- \`${command}\``);
310
+ }
311
+
312
+ lines.push("", "## 可直接复制给 Agent 的 Executor Prompt", "", "```text", executor.executorPrompt, "```");
313
+
314
+ return `${lines.join("\n")}\n`;
315
+ }
316
+
317
+ export async function writeAgentExecutorOutput(outputPath, content) {
318
+ return writeScanOutput(outputPath, content);
319
+ }
@@ -49,6 +49,12 @@ function inferSkillForCommand(commandName, flow) {
49
49
  if (commandName === "agent-runbook") {
50
50
  return "geo-ai-search-optimization-agent-runbook";
51
51
  }
52
+ if (commandName === "agent-executor") {
53
+ return "geo-ai-search-optimization-agent-executor";
54
+ }
55
+ if (commandName === "agent-batch-executor") {
56
+ return "geo-ai-search-optimization-agent-batch-executor";
57
+ }
52
58
  if (commandName === "skills" || commandName === "quick-start") {
53
59
  return "geo-ai-search-optimization-usage";
54
60
  }
@@ -111,6 +117,10 @@ function inferStepPurpose(commandName, flow) {
111
117
  return "把当前结果交接成 agent 可继续执行的工件。";
112
118
  case "agent-runbook":
113
119
  return "把当前链路整理成 agent 可照着执行的手册和检查表。";
120
+ case "agent-executor":
121
+ return "把这一轮先做哪一个任务包压成单任务执行入口。";
122
+ case "agent-batch-executor":
123
+ return "把前几包任务排成连续执行顺序,但保持一次只推进一包。";
114
124
  case "apply-plan":
115
125
  return "把交接结果推进到具体执行包。";
116
126
  case "completion-report":
@@ -153,6 +163,10 @@ function inferExpectedArtifact(commandName) {
153
163
  return "agent 交接工件";
154
164
  case "agent-runbook":
155
165
  return "agent 执行手册";
166
+ case "agent-executor":
167
+ return "agent 单任务执行包";
168
+ case "agent-batch-executor":
169
+ return "agent 多任务批次执行包";
156
170
  case "apply-plan":
157
171
  return "执行包";
158
172
  case "completion-report":
@@ -188,6 +202,12 @@ function buildStepInstructions(parsedCommand, flow) {
188
202
  if (parsedCommand.commandName === "agent-runbook") {
189
203
  lines.push("先用 runbook 稳定顺序、检查项和回报方式,再开始真正执行。");
190
204
  }
205
+ if (parsedCommand.commandName === "agent-executor") {
206
+ lines.push("这一轮只推进一包任务,不要把多个修复包混在同一次执行里。");
207
+ }
208
+ if (parsedCommand.commandName === "agent-batch-executor") {
209
+ lines.push("批次模式下也要一次只做一包,上一包通过验证后再进入下一包。");
210
+ }
191
211
  if (parsedCommand.commandName === "agent-handoff" && flow.intent === "execute") {
192
212
  lines.push("如果还是 advice-only,说明还缺仓库或本地项目上下文。");
193
213
  }
package/src/auto-flow.js CHANGED
@@ -44,6 +44,12 @@ function inferTaskTextMode(text) {
44
44
  if (/(share-pack|export-pack|html-pack|publish-pack|分享|导出|交付|外发|报告包)/i.test(normalized)) {
45
45
  return "share";
46
46
  }
47
+ if (/(batch-executor|batch executor|多包|前3包|批次执行|连续推进前几包)/i.test(normalized)) {
48
+ return "execute";
49
+ }
50
+ if (/(executor|先做哪一个|先做哪一包|single task|执行第一包|先执行一个任务)/i.test(normalized)) {
51
+ return "execute";
52
+ }
47
53
  if (/(runbook|checklist|playbook|执行手册|检查清单|操作手册)/i.test(normalized)) {
48
54
  return "execute";
49
55
  }
@@ -59,6 +65,10 @@ function inferTaskTextMode(text) {
59
65
  return "diagnose";
60
66
  }
61
67
 
68
+ function taskTextWantsBatch(text) {
69
+ return /(batch-executor|batch executor|多包|前3包|前 3 包|批次执行|连续推进前几包|前几包)/i.test(String(text));
70
+ }
71
+
62
72
  async function detectInput(input) {
63
73
  if (!input) {
64
74
  throw new Error("auto-flow 需要一个输入值,可以是任务描述、项目路径、网站网址或已导出的工件。");
@@ -150,9 +160,15 @@ function resolveEffectiveIntent(intent, detected) {
150
160
  return "share";
151
161
  }
152
162
  if (
153
- ["geo-agent-handoff", "geo-agent-runbook", "geo-apply-plan", "geo-handoff-bundle", "geo-fix-plan"].includes(
154
- detected.artifactKind
155
- )
163
+ [
164
+ "geo-agent-handoff",
165
+ "geo-agent-runbook",
166
+ "geo-agent-executor",
167
+ "geo-agent-batch-executor",
168
+ "geo-apply-plan",
169
+ "geo-handoff-bundle",
170
+ "geo-fix-plan"
171
+ ].includes(detected.artifactKind)
156
172
  ) {
157
173
  return "execute";
158
174
  }
@@ -203,7 +219,8 @@ function buildCommandChain(detected, intent) {
203
219
  `geo-ai-search-optimization onboard-url ${source}`,
204
220
  `geo-ai-search-optimization fix-plan ${source}`,
205
221
  `geo-ai-search-optimization agent-handoff ${source}`,
206
- `geo-ai-search-optimization agent-runbook ${source}`
222
+ `geo-ai-search-optimization agent-runbook ${source}`,
223
+ `geo-ai-search-optimization agent-executor ${source}`
207
224
  ];
208
225
  }
209
226
  return [
@@ -220,6 +237,7 @@ function buildCommandChain(detected, intent) {
220
237
  `geo-ai-search-optimization fix-plan ${source}`,
221
238
  `geo-ai-search-optimization agent-handoff ${source}`,
222
239
  `geo-ai-search-optimization agent-runbook ${source}`,
240
+ `geo-ai-search-optimization agent-executor ${source}`,
223
241
  `geo-ai-search-optimization apply-plan ${source}`
224
242
  ];
225
243
  }
@@ -238,6 +256,7 @@ function buildCommandChain(detected, intent) {
238
256
  `geo-ai-search-optimization fix-plan ${source}`,
239
257
  `geo-ai-search-optimization agent-handoff ${source}`,
240
258
  `geo-ai-search-optimization agent-runbook ${source}`,
259
+ `geo-ai-search-optimization agent-executor ${source}`,
241
260
  `geo-ai-search-optimization apply-plan ${source}`
242
261
  ]
243
262
  : [`geo-ai-search-optimization fix-plan ${source}`, `geo-ai-search-optimization owner-board ${source}`];
@@ -250,26 +269,41 @@ function buildCommandChain(detected, intent) {
250
269
  ];
251
270
  case "geo-fix-plan":
252
271
  return [
253
- `geo-ai-search-optimization agent-handoff ${source}`,
254
272
  `geo-ai-search-optimization agent-runbook ${source}`,
255
- `geo-ai-search-optimization apply-plan ${source}`
273
+ `geo-ai-search-optimization agent-executor ${source}`,
274
+ `geo-ai-search-optimization agent-batch-executor ${source}`,
275
+ `geo-ai-search-optimization completion-report ${source}`
256
276
  ];
257
277
  case "geo-agent-handoff":
258
278
  return [
259
279
  `geo-ai-search-optimization agent-runbook ${source}`,
260
- `geo-ai-search-optimization apply-plan ${source}`,
280
+ `geo-ai-search-optimization agent-executor ${source}`,
281
+ `geo-ai-search-optimization agent-batch-executor ${source}`,
261
282
  `geo-ai-search-optimization completion-report ${source}`
262
283
  ];
263
284
  case "geo-agent-runbook":
264
285
  return [
265
- `geo-ai-search-optimization apply-plan ${source}`,
286
+ `geo-ai-search-optimization agent-executor ${source}`,
287
+ `geo-ai-search-optimization agent-batch-executor ${source}`,
266
288
  `geo-ai-search-optimization completion-report ${source}`,
267
289
  `geo-ai-search-optimization handoff-bundle ${source}`
268
290
  ];
291
+ case "geo-agent-executor":
292
+ return [
293
+ `geo-ai-search-optimization completion-report ${source}`,
294
+ `geo-ai-search-optimization handoff-bundle ${source}`,
295
+ `geo-ai-search-optimization publish-pack ${source}`
296
+ ];
297
+ case "geo-agent-batch-executor":
298
+ return [
299
+ `geo-ai-search-optimization completion-report ${source}`,
300
+ `geo-ai-search-optimization handoff-bundle ${source}`,
301
+ `geo-ai-search-optimization publish-pack ${source}`
302
+ ];
269
303
  case "geo-apply-plan":
270
304
  return [
271
- `geo-ai-search-optimization agent-runbook ${source}`,
272
- `geo-ai-search-optimization apply-plan ${source}`,
305
+ `geo-ai-search-optimization agent-executor ${source}`,
306
+ `geo-ai-search-optimization agent-batch-executor ${source}`,
273
307
  `geo-ai-search-optimization completion-report ${source}`,
274
308
  `geo-ai-search-optimization handoff-bundle ${source}`
275
309
  ];
@@ -317,6 +351,9 @@ function pickSkillName(detected, intent) {
317
351
 
318
352
  switch (detected.artifactKind) {
319
353
  case "task-brief":
354
+ if (detected.inferredTextIntent === "execute" && taskTextWantsBatch(detected.source)) {
355
+ return "geo-ai-search-optimization-agent-batch-executor";
356
+ }
320
357
  return detected.inferredTextIntent === "share"
321
358
  ? "geo-ai-search-optimization-publish-pack"
322
359
  : detected.inferredTextIntent === "execute"
@@ -330,11 +367,13 @@ function pickSkillName(detected, intent) {
330
367
  case "geo-report:onboarding":
331
368
  return intent === "execute" ? "geo-ai-search-optimization-agent-handoff" : "geo-ai-search-optimization";
332
369
  case "geo-fix-plan":
333
- return "geo-ai-search-optimization-agent-handoff";
334
370
  case "geo-agent-handoff":
335
371
  case "geo-agent-runbook":
372
+ case "geo-agent-executor":
336
373
  case "geo-apply-plan":
337
- return "geo-ai-search-optimization-agent-runbook";
374
+ return "geo-ai-search-optimization-agent-executor";
375
+ case "geo-agent-batch-executor":
376
+ return "geo-ai-search-optimization-agent-batch-executor";
338
377
  case "geo-completion-report":
339
378
  return "geo-ai-search-optimization-completion-report";
340
379
  case "geo-handoff-bundle":
@@ -366,8 +405,19 @@ function buildSecondarySkillNames(primarySkill, intent, detected) {
366
405
  }
367
406
  if (
368
407
  intent === "execute" ||
369
- ["geo-fix-plan", "geo-agent-handoff", "geo-agent-runbook", "geo-apply-plan"].includes(detected.artifactKind)
408
+ [
409
+ "geo-fix-plan",
410
+ "geo-agent-handoff",
411
+ "geo-agent-runbook",
412
+ "geo-agent-executor",
413
+ "geo-agent-batch-executor",
414
+ "geo-apply-plan"
415
+ ].includes(
416
+ detected.artifactKind
417
+ )
370
418
  ) {
419
+ names.add("geo-ai-search-optimization-agent-batch-executor");
420
+ names.add("geo-ai-search-optimization-agent-executor");
371
421
  names.add("geo-ai-search-optimization-agent-runbook");
372
422
  names.add("geo-ai-search-optimization-agent-handoff");
373
423
  names.add("geo-ai-search-optimization-repair-loop");
@@ -391,16 +441,28 @@ function buildStage(intent, detected) {
391
441
  return "执行复盘";
392
442
  }
393
443
  if (intent === "execute") {
394
- return ["geo-fix-plan", "geo-agent-handoff", "geo-agent-runbook", "geo-apply-plan", "geo-handoff-bundle"].includes(
395
- detected.artifactKind
396
- )
444
+ return [
445
+ "geo-fix-plan",
446
+ "geo-agent-handoff",
447
+ "geo-agent-runbook",
448
+ "geo-agent-executor",
449
+ "geo-agent-batch-executor",
450
+ "geo-apply-plan",
451
+ "geo-handoff-bundle"
452
+ ].includes(detected.artifactKind)
397
453
  ? "Agent 执行"
398
454
  : "执行准备";
399
455
  }
400
456
  if (
401
- ["geo-fix-plan", "geo-agent-handoff", "geo-agent-runbook", "geo-apply-plan", "geo-handoff-bundle"].includes(
402
- detected.artifactKind
403
- )
457
+ [
458
+ "geo-fix-plan",
459
+ "geo-agent-handoff",
460
+ "geo-agent-runbook",
461
+ "geo-agent-executor",
462
+ "geo-agent-batch-executor",
463
+ "geo-apply-plan",
464
+ "geo-handoff-bundle"
465
+ ].includes(detected.artifactKind)
404
466
  ) {
405
467
  return "Agent 执行";
406
468
  }
package/src/cli.js CHANGED
@@ -2,6 +2,12 @@ import { fileURLToPath } from "node:url";
2
2
  import { readFile } from "node:fs/promises";
3
3
  import path from "node:path";
4
4
  import { createApplyPlan, renderApplyPlanMarkdown, writeApplyPlanOutput } from "./apply-plan.js";
5
+ import {
6
+ createAgentBatchExecutor,
7
+ renderAgentBatchExecutorMarkdown,
8
+ writeAgentBatchExecutorOutput
9
+ } from "./agent-batch-executor.js";
10
+ import { createAgentExecutor, renderAgentExecutorMarkdown, writeAgentExecutorOutput } from "./agent-executor.js";
5
11
  import { createAgentHandoff, renderAgentHandoffMarkdown, writeAgentHandoffOutput } from "./agent-handoff.js";
6
12
  import { createAgentRunbook, renderAgentRunbookMarkdown, writeAgentRunbookOutput } from "./agent-runbook.js";
7
13
  import { createAgentSession, renderAgentSessionMarkdown, writeAgentSessionOutput } from "./agent-session.js";
@@ -63,6 +69,8 @@ function printHelp() {
63
69
  " geo-ai-search-optimization auto-flow <input> [--intent <auto|diagnose|guide|execute|share|closeout>] [--json] [--out <file>]",
64
70
  " geo-ai-search-optimization agent-session <input> [--intent <auto|diagnose|guide|execute|share|closeout>] [--json] [--out <file>]",
65
71
  " geo-ai-search-optimization agent-runbook <input> [--intent <auto|diagnose|guide|execute|share|closeout>] [--task <id>] [--format <markdown|json>] [--out <file>]",
72
+ " geo-ai-search-optimization agent-executor <input> [--intent <auto|diagnose|guide|execute|share|closeout>] [--task <id>] [--format <markdown|json>] [--out <file>]",
73
+ " geo-ai-search-optimization agent-batch-executor <input> [--intent <auto|diagnose|guide|execute|share|closeout>] [--task <id>] [--count <count>] [--format <markdown|json>] [--out <file>]",
66
74
  " geo-ai-search-optimization skills [--json]",
67
75
  " geo-ai-search-optimization where",
68
76
  " geo-ai-search-optimization doctor [--json]",
@@ -206,6 +214,61 @@ async function handleAgentRunbook(args) {
206
214
  process.stdout.write(renderedOutput);
207
215
  }
208
216
 
217
+ async function handleAgentExecutor(args) {
218
+ const input = args.find((value) => !value.startsWith("-"));
219
+ if (!input) {
220
+ throw new Error("agent-executor 需要一个输入值,可以是任务描述、项目路径、网站网址或已导出的工件");
221
+ }
222
+
223
+ const format = getFlagValue(args, "--format");
224
+ const executor = await createAgentExecutor(input, {
225
+ intent: getFlagValue(args, "--intent"),
226
+ format,
227
+ taskId: getFlagValue(args, "--task")
228
+ });
229
+ const outputJson = format === "json";
230
+ const renderedOutput = outputJson
231
+ ? `${JSON.stringify(executor, null, 2)}\n`
232
+ : renderAgentExecutorMarkdown(executor);
233
+
234
+ const outputPath = getFlagValue(args, "--out");
235
+ if (outputPath) {
236
+ const resolvedOutputPath = await writeAgentExecutorOutput(outputPath, renderedOutput);
237
+ process.stdout.write(`已保存 agent executor:${resolvedOutputPath}\n`);
238
+ return;
239
+ }
240
+
241
+ process.stdout.write(renderedOutput);
242
+ }
243
+
244
+ async function handleAgentBatchExecutor(args) {
245
+ const input = args.find((value) => !value.startsWith("-"));
246
+ if (!input) {
247
+ throw new Error("agent-batch-executor 需要一个输入值,可以是任务描述、项目路径、网站网址或已导出的工件");
248
+ }
249
+
250
+ const format = getFlagValue(args, "--format") || (hasFlag(args, "--json") ? "json" : undefined);
251
+ const batch = await createAgentBatchExecutor(input, {
252
+ intent: getFlagValue(args, "--intent"),
253
+ format,
254
+ taskId: getFlagValue(args, "--task"),
255
+ count: getFlagValue(args, "--count")
256
+ });
257
+ const outputJson = batch.format === "json";
258
+ const renderedOutput = outputJson
259
+ ? `${JSON.stringify(batch, null, 2)}\n`
260
+ : renderAgentBatchExecutorMarkdown(batch);
261
+
262
+ const outputPath = getFlagValue(args, "--out");
263
+ if (outputPath) {
264
+ const resolvedOutputPath = await writeAgentBatchExecutorOutput(outputPath, renderedOutput);
265
+ process.stdout.write(`已保存 agent batch executor:${resolvedOutputPath}\n`);
266
+ return;
267
+ }
268
+
269
+ process.stdout.write(renderedOutput);
270
+ }
271
+
209
272
  function handleWhere() {
210
273
  process.stdout.write(
211
274
  [
@@ -761,6 +824,16 @@ export async function runCli(args = []) {
761
824
  return;
762
825
  }
763
826
 
827
+ if (command === "agent-executor") {
828
+ await handleAgentExecutor(rest);
829
+ return;
830
+ }
831
+
832
+ if (command === "agent-batch-executor") {
833
+ await handleAgentBatchExecutor(rest);
834
+ return;
835
+ }
836
+
764
837
  if (command === "skills") {
765
838
  await handleSkills(rest);
766
839
  return;
package/src/index.js CHANGED
@@ -7,7 +7,9 @@ export {
7
7
  } from "./interactive-onboarding.js";
8
8
  export { createAutoFlow, renderAutoFlowMarkdown, writeAutoFlowOutput } from "./auto-flow.js";
9
9
  export { createApplyPlan, renderApplyPlanMarkdown, writeApplyPlanOutput } from "./apply-plan.js";
10
+ export { createAgentBatchExecutor, renderAgentBatchExecutorMarkdown, writeAgentBatchExecutorOutput } from "./agent-batch-executor.js";
10
11
  export { createAgentHandoff, renderAgentHandoffMarkdown, writeAgentHandoffOutput } from "./agent-handoff.js";
12
+ export { createAgentExecutor, renderAgentExecutorMarkdown, writeAgentExecutorOutput } from "./agent-executor.js";
11
13
  export { createAgentRunbook, renderAgentRunbookMarkdown, writeAgentRunbookOutput } from "./agent-runbook.js";
12
14
  export { createAgentSession, renderAgentSessionMarkdown, writeAgentSessionOutput } from "./agent-session.js";
13
15
  export { createCompletionReport, renderCompletionReportMarkdown, writeCompletionReportOutput } from "./completion-report.js";
package/src/skills.js CHANGED
@@ -7,6 +7,8 @@ const SKILL_ORDER = [
7
7
  "geo-ai-search-optimization-auto-flow",
8
8
  "geo-ai-search-optimization-agent-session",
9
9
  "geo-ai-search-optimization-agent-runbook",
10
+ "geo-ai-search-optimization-agent-executor",
11
+ "geo-ai-search-optimization-agent-batch-executor",
10
12
  "geo-ai-search-optimization-usage",
11
13
  "geo-ai-search-optimization-agent-handoff",
12
14
  "geo-ai-search-optimization-repair-loop",
@@ -23,6 +25,8 @@ const SKILL_CATEGORY = {
23
25
  "geo-ai-search-optimization-auto-flow": "routing",
24
26
  "geo-ai-search-optimization-agent-session": "routing",
25
27
  "geo-ai-search-optimization-agent-runbook": "execution",
28
+ "geo-ai-search-optimization-agent-executor": "execution",
29
+ "geo-ai-search-optimization-agent-batch-executor": "execution",
26
30
  "geo-ai-search-optimization-usage": "guidance",
27
31
  "geo-ai-search-optimization-agent-handoff": "execution",
28
32
  "geo-ai-search-optimization-repair-loop": "execution",
@@ -158,6 +162,8 @@ export function renderBundledSkillsMarkdown(bundle) {
158
162
  "- 如果 agent 需要自动选 skill,先跑 auto-flow。",
159
163
  "- 如果要给 agent 明确步骤,继续进入 agent-session。",
160
164
  "- 如果要给 agent 一份执行手册和检查表,再进入 agent-runbook。",
165
+ "- 如果要直接告诉 agent 这轮先做哪 1 包,再进入 agent-executor。",
166
+ "- 如果要连续推进前 2 到 3 包,但仍然一次只做一包,再进入 agent-batch-executor。",
161
167
  "- 再看 usage skill,知道什么时候该跑哪个命令。",
162
168
  "- 如果要交给 agent 执行,再进入 handoff / apply / completion 这一条执行链。",
163
169
  "- 如果要产出给团队分发,再进入 share / export / html / publish 这一条交付链。",