geo-ai-search-optimization 1.2.11 → 1.2.12

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
@@ -145,6 +145,26 @@ geo-ai-search-optimization agent-batch-executor ./reports/apply-plan.json --task
145
145
  - 批次收尾命令
146
146
  - 可直接复制给 agent 的 batch prompt
147
147
 
148
+ ## Agent Progress Tracker 命令
149
+
150
+ 如果你希望 agent 不是只拿到执行队列,而是能够明确回答“现在做到第几包、当前卡在哪、下一包是什么”,可以直接用 `agent-progress-tracker`:
151
+
152
+ ```bash
153
+ geo-ai-search-optimization agent-progress-tracker ./your-site
154
+ geo-ai-search-optimization agent-progress-tracker ./reports/apply-plan.json --completed fix-01,fix-02 --current fix-03
155
+ geo-ai-search-optimization agent-progress-tracker ./reports/agent-batch-executor.json --blocked "缺少仓库权限,缺少模板文件" --format json --out ./reports/agent-progress-tracker.json
156
+ ```
157
+
158
+ `agent-progress-tracker` 会输出:
159
+
160
+ - 当前状态是未开始、进行中、阻塞还是已完成
161
+ - 已完成到哪几包
162
+ - 当前正在推进哪一包
163
+ - 下一包是什么
164
+ - 当前阻塞项
165
+ - 建议下一步命令
166
+ - 可直接复制给 agent 的 progress prompt
167
+
148
168
  ## Quick Start
149
169
 
150
170
  如果你要从 0 到 1 启动一个 GEO 项目,建议照这个顺序做。
@@ -543,6 +563,7 @@ geo-ai-search-optimization agent-session ./your-site
543
563
  geo-ai-search-optimization agent-runbook ./your-site
544
564
  geo-ai-search-optimization agent-executor ./your-site
545
565
  geo-ai-search-optimization agent-batch-executor ./your-site
566
+ geo-ai-search-optimization agent-progress-tracker ./your-site
546
567
  geo-ai-search-optimization skills
547
568
  geo-ai-search-optimization where
548
569
  geo-ai-search-optimization doctor
@@ -613,6 +634,13 @@ geo-ai-search-optimization help
613
634
  - 输出 do-now checklist、stop checklist、success checklist、验证命令和回报模板
614
635
  - 新增 `geo-ai-search-optimization-agent-executor` skill
615
636
 
637
+ ## New in 1.2.12
638
+
639
+ - 新增 `agent-progress-tracker` 命令
640
+ - 可以从 `apply-plan`、`agent-executor`、`agent-batch-executor` 等工件推导当前执行进度
641
+ - 输出已完成任务、当前包、下一包、阻塞项和建议下一步命令
642
+ - 新增 `geo-ai-search-optimization-agent-progress-tracker` skill
643
+
616
644
  ## New in 1.2.11
617
645
 
618
646
  - 新增 `agent-batch-executor` 命令
@@ -811,6 +839,7 @@ The installed package now includes a bundled GEO skill pack, including:
811
839
  - `geo-ai-search-optimization-agent-runbook`
812
840
  - `geo-ai-search-optimization-agent-executor`
813
841
  - `geo-ai-search-optimization-agent-batch-executor`
842
+ - `geo-ai-search-optimization-agent-progress-tracker`
814
843
  - `geo-ai-search-optimization-usage`
815
844
  - `geo-ai-search-optimization-agent-handoff`
816
845
  - `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.11",
3
+ "version": "1.2.12",
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": {
@@ -72,6 +72,16 @@ Best for:
72
72
  - making the next agent finish packet 1 before packet 2 starts
73
73
  - giving the next agent a batch-level prompt, per-packet checklists, and final closeout commands
74
74
 
75
+ ### `geo-ai-search-optimization-agent-progress-tracker`
76
+
77
+ Use this when the next agent should explain current execution status, not just produce the next queue.
78
+
79
+ Best for:
80
+
81
+ - showing which packet is already done
82
+ - clarifying the current active packet and the next packet
83
+ - surfacing blockers before the team moves into closeout or the next batch
84
+
75
85
  ## Usage guide
76
86
 
77
87
  ### `geo-ai-search-optimization-usage`
@@ -0,0 +1,23 @@
1
+ ---
2
+ name: geo-ai-search-optimization-agent-progress-tracker
3
+ description: Track GEO execution progress from apply-plan, agent-executor, agent-batch-executor, completion-report, or related artifacts. Use when an agent should explain which GEO packet is already done, which one is active, what is blocked, and what command should run next.
4
+ ---
5
+
6
+ # GEO Agent Progress Tracker
7
+
8
+ Use this skill when the next agent should stop guessing current status and make the execution state explicit.
9
+
10
+ `GEO = Generative Engine Optimization`
11
+
12
+ ## What it does
13
+
14
+ - reads GEO execution artifacts and infers the current packet state
15
+ - shows completed packets, the active packet, the next packet, and blockers
16
+ - recommends the next command for continuing or closing out the work
17
+ - gives the next agent a progress-tracking prompt instead of another open-ended plan
18
+
19
+ ## Best use
20
+
21
+ - when a PM asks “现在做到哪了?”
22
+ - when the next agent should explain status before touching the next packet
23
+ - when batch execution is underway and the team needs a stable progress checkpoint
@@ -0,0 +1,4 @@
1
+ interface:
2
+ display_name: "GEO Agent Progress Tracker"
3
+ short_description: "Track which GEO packet is done, active, or blocked"
4
+ default_prompt: "Use $geo-ai-search-optimization-agent-progress-tracker to explain GEO execution progress, blockers, the current packet, and the next command."
@@ -13,26 +13,27 @@ 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 eighteen layers:
16
+ The package is best explained as nineteen 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
21
  4. `agent-executor`: choose one packet to execute right now
22
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
23
+ 6. `agent-progress-tracker`: track which packet is done, which one is active, and what comes next
24
+ 7. `skills`: inspect the bundled skill package
25
+ 8. `onboard-url` / `onboard`: first look
26
+ 9. `scan`: raw signal check
27
+ 10. `audit` / `report`: diagnosis
28
+ 11. `fix-plan` / `owner-board`: execution planning
29
+ 12. `agent-handoff`: agent takeover package
30
+ 13. `apply-plan`: execution loop
31
+ 14. `completion-report`: closeout
32
+ 15. `handoff-bundle`: all-in-one package
33
+ 16. `share-pack`: audience-ready delivery
34
+ 17. `export-pack`: folder export
35
+ 18. `html-pack` / `publish-pack`: browsable and final delivery output
36
+ 19. `pm-brief` / `roadmap`: stakeholder alignment
36
37
 
37
38
  ## Recommended command order
38
39
 
@@ -44,6 +45,7 @@ npx geo-ai-search-optimization agent-session https://example.com
44
45
  npx geo-ai-search-optimization agent-runbook https://example.com
45
46
  npx geo-ai-search-optimization agent-executor https://example.com
46
47
  npx geo-ai-search-optimization agent-batch-executor https://example.com
48
+ npx geo-ai-search-optimization agent-progress-tracker https://example.com
47
49
  npx geo-ai-search-optimization onboard-url https://example.com
48
50
  npx geo-ai-search-optimization pm-brief https://example.com
49
51
  npx geo-ai-search-optimization roadmap https://example.com
@@ -57,6 +59,7 @@ npx geo-ai-search-optimization agent-session ./your-site
57
59
  npx geo-ai-search-optimization agent-runbook ./your-site
58
60
  npx geo-ai-search-optimization agent-executor ./your-site
59
61
  npx geo-ai-search-optimization agent-batch-executor ./your-site
62
+ npx geo-ai-search-optimization agent-progress-tracker ./your-site
60
63
  npx geo-ai-search-optimization scan ./your-site
61
64
  npx geo-ai-search-optimization audit ./your-site
62
65
  npx geo-ai-search-optimization fix-plan ./your-site
@@ -79,6 +82,7 @@ npx geo-ai-search-optimization roadmap ./your-site
79
82
  - `agent-runbook`: build a checklist-driven runbook with preflight, validation, and reporting rules
80
83
  - `agent-executor`: select one packet to execute now and package it into a single-task entrypoint
81
84
  - `agent-batch-executor`: line up the first few packets in execution order while preserving one-packet-at-a-time discipline
85
+ - `agent-progress-tracker`: show execution progress, current packet, blockers, and the next packet to advance
82
86
  - `onboard-url`: first-time website check from a live URL
83
87
  - `onboard`: interactive first-time onboarding
84
88
  - `skills`: list the bundled skills and decide which skill or command chain fits the task
@@ -108,6 +112,7 @@ When explaining the tool to a user:
108
112
  - if the user wants the next agent to follow a checklist and execution manual, move them to `agent-runbook`
109
113
  - if the user wants the next agent to start one concrete task now, move them to `agent-executor`
110
114
  - if the user wants the next agent to continuously advance the first 2 to 3 packets in order, move them to `agent-batch-executor`
115
+ - if the user wants the next agent to explain current progress, blockers, and the next packet, move them to `agent-progress-tracker`
111
116
  - explain the result in PM language, not implementation jargon
112
117
  - if the user sounds new, start with `onboard-url` or `quick-start`
113
118
  - if the user wants another agent to take over, move them to `agent-handoff`
@@ -0,0 +1,482 @@
1
+ import fs from "node:fs/promises";
2
+ import path from "node:path";
3
+ import { createApplyPlan } from "./apply-plan.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-progress-tracker 格式:${format}。可选值:${Array.from(VALID_FORMATS).join(", ")}`);
12
+ }
13
+ return resolved;
14
+ }
15
+
16
+ function parseCommaList(value) {
17
+ if (!value) {
18
+ return [];
19
+ }
20
+
21
+ return String(value)
22
+ .split(",")
23
+ .map((item) => item.trim())
24
+ .filter(Boolean);
25
+ }
26
+
27
+ async function pathExists(targetPath) {
28
+ try {
29
+ await fs.access(targetPath);
30
+ return true;
31
+ } catch {
32
+ return false;
33
+ }
34
+ }
35
+
36
+ function isUrlInput(input) {
37
+ return /^https?:\/\//i.test(input);
38
+ }
39
+
40
+ function buildPseudoApplyPlanFromCompletionReport(report) {
41
+ return {
42
+ kind: "geo-apply-plan",
43
+ source: report.source,
44
+ sourceType: report.sourceType || "json",
45
+ executionType: report.executionType || "guided-planning",
46
+ executionMode: report.executionMode || "mixed",
47
+ selectedTaskCount: Array.isArray(report.nextRoundTasks) ? report.nextRoundTasks.length : 0,
48
+ packets: (report.nextRoundTasks || []).map((task) => {
49
+ const template = (report.completionTemplates || []).find((item) => item.id === task.id);
50
+ return {
51
+ id: task.id,
52
+ title: task.title,
53
+ owner: task.owner,
54
+ priority: "P1",
55
+ executionType: report.executionType || "guided-planning",
56
+ doneWhen: task.doneWhen || [],
57
+ validationCommands: report.validationChecklist || [],
58
+ completionTemplate: template?.template || ""
59
+ };
60
+ })
61
+ };
62
+ }
63
+
64
+ function buildExecutionContextFromParsedArtifact(parsed, input) {
65
+ if (parsed?.kind === "geo-apply-plan") {
66
+ return {
67
+ source: parsed.source || input,
68
+ sourceType: parsed.sourceType || "json",
69
+ artifactKind: parsed.kind,
70
+ applyPlan: parsed,
71
+ trackerState: {}
72
+ };
73
+ }
74
+
75
+ if (parsed?.kind === "geo-agent-runbook" && parsed.applyPlan?.kind === "geo-apply-plan") {
76
+ return {
77
+ source: parsed.source || parsed.applyPlan.source || input,
78
+ sourceType: parsed.sourceType || parsed.applyPlan.sourceType || "json",
79
+ artifactKind: parsed.kind,
80
+ applyPlan: parsed.applyPlan,
81
+ trackerState: {}
82
+ };
83
+ }
84
+
85
+ if (parsed?.kind === "geo-agent-executor" && parsed.applyPlan?.kind === "geo-apply-plan") {
86
+ return {
87
+ source: parsed.source || parsed.applyPlan.source || input,
88
+ sourceType: parsed.sourceType || parsed.applyPlan.sourceType || "json",
89
+ artifactKind: parsed.kind,
90
+ applyPlan: parsed.applyPlan,
91
+ trackerState: {
92
+ currentTaskId: parsed.selectedPacket?.id || null
93
+ }
94
+ };
95
+ }
96
+
97
+ if (parsed?.kind === "geo-agent-batch-executor" && parsed.applyPlan?.kind === "geo-apply-plan") {
98
+ return {
99
+ source: parsed.source || parsed.applyPlan.source || input,
100
+ sourceType: parsed.sourceType || parsed.applyPlan.sourceType || "json",
101
+ artifactKind: parsed.kind,
102
+ applyPlan: parsed.applyPlan,
103
+ trackerState: {
104
+ currentTaskId: parsed.packetExecutors?.[0]?.id || null
105
+ }
106
+ };
107
+ }
108
+
109
+ if (parsed?.kind === "geo-agent-progress-tracker" && parsed.applyPlan?.kind === "geo-apply-plan") {
110
+ return {
111
+ source: parsed.source || parsed.applyPlan.source || input,
112
+ sourceType: parsed.sourceType || parsed.applyPlan.sourceType || "json",
113
+ artifactKind: parsed.kind,
114
+ applyPlan: parsed.applyPlan,
115
+ trackerState: {
116
+ currentTaskId: parsed.currentTaskId || parsed.activePacket?.id || null,
117
+ completedPacketIds: parsed.completedPacketIds || [],
118
+ blockedReasons: parsed.blockedReasons || []
119
+ }
120
+ };
121
+ }
122
+
123
+ if (parsed?.kind === "geo-completion-report") {
124
+ return {
125
+ source: parsed.source || input,
126
+ sourceType: parsed.sourceType || "json",
127
+ artifactKind: parsed.kind,
128
+ applyPlan: buildPseudoApplyPlanFromCompletionReport(parsed),
129
+ trackerState: {}
130
+ };
131
+ }
132
+
133
+ return null;
134
+ }
135
+
136
+ async function resolveExecutionContext(input) {
137
+ if (!input) {
138
+ throw new Error("agent-progress-tracker 需要一个输入值,可以是项目路径、网站网址或已导出的 JSON 工件。");
139
+ }
140
+
141
+ if (isUrlInput(input)) {
142
+ const applyPlan = await createApplyPlan(input, { format: "json" });
143
+ return {
144
+ source: applyPlan.source,
145
+ sourceType: applyPlan.sourceType,
146
+ artifactKind: applyPlan.kind,
147
+ applyPlan,
148
+ trackerState: {}
149
+ };
150
+ }
151
+
152
+ const resolvedPath = path.resolve(input);
153
+ if (!(await pathExists(resolvedPath))) {
154
+ const applyPlan = await createApplyPlan(input, { format: "json" });
155
+ return {
156
+ source: applyPlan.source,
157
+ sourceType: applyPlan.sourceType,
158
+ artifactKind: applyPlan.kind,
159
+ applyPlan,
160
+ trackerState: {}
161
+ };
162
+ }
163
+
164
+ const stat = await fs.stat(resolvedPath);
165
+ if (stat.isDirectory()) {
166
+ const applyPlan = await createApplyPlan(resolvedPath, { format: "json" });
167
+ return {
168
+ source: applyPlan.source,
169
+ sourceType: applyPlan.sourceType,
170
+ artifactKind: applyPlan.kind,
171
+ applyPlan,
172
+ trackerState: {}
173
+ };
174
+ }
175
+
176
+ if (path.extname(resolvedPath).toLowerCase() === ".json") {
177
+ const raw = await fs.readFile(resolvedPath, "utf8");
178
+ const parsed = JSON.parse(raw);
179
+ const resolved = buildExecutionContextFromParsedArtifact(parsed, resolvedPath);
180
+ if (resolved) {
181
+ return resolved;
182
+ }
183
+ }
184
+
185
+ const applyPlan = await createApplyPlan(resolvedPath, { format: "json" });
186
+ return {
187
+ source: applyPlan.source,
188
+ sourceType: applyPlan.sourceType,
189
+ artifactKind: applyPlan.kind,
190
+ applyPlan,
191
+ trackerState: {}
192
+ };
193
+ }
194
+
195
+ function pickCompletedIds(packets, options, trackerState) {
196
+ const packetIds = new Set((packets || []).map((packet) => packet.id));
197
+ const requestedIds =
198
+ options.completedPacketIds != null ? parseCommaList(options.completedPacketIds) : trackerState.completedPacketIds || [];
199
+
200
+ return requestedIds.filter((id) => packetIds.has(id));
201
+ }
202
+
203
+ function pickCurrentTaskId(packets, completedIds, options, trackerState) {
204
+ const packetIds = new Set((packets || []).map((packet) => packet.id));
205
+ const completedSet = new Set(completedIds);
206
+ const requested = options.currentTaskId || trackerState.currentTaskId;
207
+ if (requested && packetIds.has(requested) && !completedSet.has(requested)) {
208
+ return requested;
209
+ }
210
+
211
+ const remaining = (packets || []).filter((packet) => !completedSet.has(packet.id));
212
+ return remaining[0]?.id || null;
213
+ }
214
+
215
+ function pickBlockedReasons(options, trackerState) {
216
+ if (options.blockedReasons != null) {
217
+ return parseCommaList(options.blockedReasons);
218
+ }
219
+ return trackerState.blockedReasons || [];
220
+ }
221
+
222
+ function inferTrackerStatus(totalPackets, completedCount, blockedReasons) {
223
+ if (blockedReasons.length > 0) {
224
+ return "blocked";
225
+ }
226
+ if (totalPackets === 0) {
227
+ return "needs-context";
228
+ }
229
+ if (completedCount === 0) {
230
+ return "not-started";
231
+ }
232
+ if (completedCount >= totalPackets) {
233
+ return "completed";
234
+ }
235
+ return "in-progress";
236
+ }
237
+
238
+ function buildStatusSummary(status, activePacket, nextPacket, blockedReasons) {
239
+ switch (status) {
240
+ case "blocked":
241
+ return `当前已识别到阻塞,先处理这些问题再继续:${blockedReasons.join(";")}`;
242
+ case "needs-context":
243
+ return "当前还没有足够的执行包,说明需要先补上下文或重新生成 apply-plan。";
244
+ case "completed":
245
+ return "当前批次已全部完成,可以进入 completion-report 与交付收尾。";
246
+ case "in-progress":
247
+ return activePacket
248
+ ? `当前正推进 ${activePacket.id},完成后继续 ${nextPacket?.id || "进入收尾阶段"}。`
249
+ : "当前已经开始执行,但还没有明确当前包,请先校准执行顺序。";
250
+ default:
251
+ return activePacket
252
+ ? `当前尚未开始,建议先从 ${activePacket.id} 开始。`
253
+ : "当前尚未开始,先确认执行包和优先级。";
254
+ }
255
+ }
256
+
257
+ function buildSuggestedNextCommand(source, status, activePacket, remainingPackets, blockedReasons) {
258
+ if (status === "blocked") {
259
+ if (activePacket) {
260
+ return `geo-ai-search-optimization agent-runbook ${source} --task ${activePacket.id}`;
261
+ }
262
+ return `geo-ai-search-optimization agent-session ${source}`;
263
+ }
264
+
265
+ if (status === "completed") {
266
+ return `geo-ai-search-optimization completion-report ${source}`;
267
+ }
268
+
269
+ if (activePacket) {
270
+ return `geo-ai-search-optimization agent-executor ${source} --task ${activePacket.id}`;
271
+ }
272
+
273
+ if (remainingPackets.length > 1) {
274
+ return `geo-ai-search-optimization agent-batch-executor ${source}`;
275
+ }
276
+
277
+ if (blockedReasons.length > 0) {
278
+ return `geo-ai-search-optimization agent-session ${source}`;
279
+ }
280
+
281
+ return `geo-ai-search-optimization apply-plan ${source}`;
282
+ }
283
+
284
+ function buildFollowupCommands(source, status, activePacket, nextPacket, remainingPackets) {
285
+ if (status === "completed") {
286
+ return [
287
+ `geo-ai-search-optimization handoff-bundle ${source}`,
288
+ `geo-ai-search-optimization publish-pack ${source}`
289
+ ];
290
+ }
291
+
292
+ const commands = [];
293
+
294
+ if (nextPacket) {
295
+ commands.push(`geo-ai-search-optimization agent-batch-executor ${source} --task ${nextPacket.id}`);
296
+ } else if (remainingPackets.length > 1) {
297
+ commands.push(`geo-ai-search-optimization agent-batch-executor ${source}`);
298
+ }
299
+
300
+ if (activePacket) {
301
+ commands.push(`geo-ai-search-optimization agent-progress-tracker ${source} --current ${activePacket.id}`);
302
+ }
303
+
304
+ commands.push(`geo-ai-search-optimization completion-report ${source}`);
305
+ commands.push(`geo-ai-search-optimization handoff-bundle ${source}`);
306
+ return commands;
307
+ }
308
+
309
+ function buildTrackerPrompt(tracker) {
310
+ const lines = [
311
+ "你现在进入 GEO 执行进度追踪模式。",
312
+ `当前输入:${tracker.source}`,
313
+ `当前状态:${tracker.status}`,
314
+ `当前总结:${tracker.statusSummary}`,
315
+ `总任务包:${tracker.totalPackets}`,
316
+ `已完成:${tracker.completedCount}`,
317
+ `剩余:${tracker.remainingCount}`
318
+ ];
319
+
320
+ if (tracker.activePacket) {
321
+ lines.push(`当前执行包:${tracker.activePacket.id}|${tracker.activePacket.title}`);
322
+ }
323
+ if (tracker.nextPacket) {
324
+ lines.push(`下一包:${tracker.nextPacket.id}|${tracker.nextPacket.title}`);
325
+ }
326
+ if (tracker.blockedReasons.length > 0) {
327
+ lines.push(`阻塞原因:${tracker.blockedReasons.join(";")}`);
328
+ }
329
+
330
+ lines.push("请输出:");
331
+ lines.push("1. 当前已完成到哪");
332
+ lines.push("2. 当前卡点是什么");
333
+ lines.push("3. 现在该继续哪一包");
334
+ lines.push("4. 完成这一包后下一步是什么");
335
+ return lines.join("\n");
336
+ }
337
+
338
+ function summarizePacket(packet) {
339
+ if (!packet) {
340
+ return null;
341
+ }
342
+
343
+ return {
344
+ id: packet.id,
345
+ title: packet.title,
346
+ owner: packet.owner,
347
+ priority: packet.priority || "P1",
348
+ doneWhen: packet.doneWhen || [],
349
+ validationCommands: packet.validationCommands || []
350
+ };
351
+ }
352
+
353
+ export async function createAgentProgressTracker(input, options = {}) {
354
+ const format = normalizeFormat(options.format);
355
+ const context = await resolveExecutionContext(input);
356
+ const packets = context.applyPlan?.packets || [];
357
+ const completedPacketIds = pickCompletedIds(packets, options, context.trackerState);
358
+ const completedSet = new Set(completedPacketIds);
359
+ const currentTaskId = pickCurrentTaskId(packets, completedPacketIds, options, context.trackerState);
360
+ const blockedReasons = pickBlockedReasons(options, context.trackerState);
361
+ const completedPackets = packets.filter((packet) => completedSet.has(packet.id));
362
+ const remainingPackets = packets.filter((packet) => !completedSet.has(packet.id));
363
+ const activePacket = summarizePacket(remainingPackets.find((packet) => packet.id === currentTaskId) || remainingPackets[0]);
364
+ const nextPacket = summarizePacket(
365
+ activePacket ? remainingPackets.find((packet) => packet.id !== activePacket.id) : remainingPackets[0]
366
+ );
367
+ const totalPackets = packets.length;
368
+ const completedCount = completedPackets.length;
369
+ const remainingCount = Math.max(totalPackets - completedCount, 0);
370
+ const progressPercent = totalPackets > 0 ? Math.round((completedCount / totalPackets) * 100) : 0;
371
+ const status = inferTrackerStatus(totalPackets, completedCount, blockedReasons);
372
+ const statusSummary = buildStatusSummary(status, activePacket, nextPacket, blockedReasons);
373
+ const suggestedNextCommand = buildSuggestedNextCommand(
374
+ context.source,
375
+ status,
376
+ activePacket,
377
+ remainingPackets,
378
+ blockedReasons
379
+ );
380
+ const followupCommands = buildFollowupCommands(context.source, status, activePacket, nextPacket, remainingPackets);
381
+
382
+ const tracker = {
383
+ kind: "geo-agent-progress-tracker",
384
+ input,
385
+ source: context.source,
386
+ sourceType: context.sourceType,
387
+ artifactKind: context.artifactKind,
388
+ format,
389
+ executionType: context.applyPlan?.executionType || "guided-planning",
390
+ status,
391
+ statusSummary,
392
+ totalPackets,
393
+ completedCount,
394
+ remainingCount,
395
+ progressPercent,
396
+ currentTaskId: activePacket?.id || null,
397
+ completedPacketIds,
398
+ blockedReasons,
399
+ completedPackets: completedPackets.map(summarizePacket),
400
+ activePacket,
401
+ nextPacket,
402
+ remainingPackets: remainingPackets.map(summarizePacket),
403
+ suggestedNextCommand,
404
+ followupCommands,
405
+ trackerPrompt: "",
406
+ applyPlan: context.applyPlan
407
+ };
408
+
409
+ tracker.trackerPrompt = buildTrackerPrompt(tracker);
410
+ return tracker;
411
+ }
412
+
413
+ export function renderAgentProgressTrackerMarkdown(tracker) {
414
+ const lines = [
415
+ "# GEO Agent Progress Tracker",
416
+ "",
417
+ `- 输入:\`${tracker.source}\``,
418
+ `- 来源类型:\`${tracker.sourceType}\``,
419
+ `- 工件类型:\`${tracker.artifactKind}\``,
420
+ `- 当前状态:\`${tracker.status}\``,
421
+ `- 执行类型:\`${tracker.executionType}\``,
422
+ `- 进度:\`${tracker.progressPercent}%\``,
423
+ `- 已完成:\`${tracker.completedCount}/${tracker.totalPackets}\``,
424
+ `- 当前总结:${tracker.statusSummary}`,
425
+ ""
426
+ ];
427
+
428
+ lines.push("## 已完成任务包", "");
429
+ if (tracker.completedPackets.length === 0) {
430
+ lines.push("- 当前还没有标记为已完成的任务包。", "");
431
+ } else {
432
+ for (const packet of tracker.completedPackets) {
433
+ lines.push(`- ${packet.id}|${packet.title}`);
434
+ }
435
+ lines.push("");
436
+ }
437
+
438
+ lines.push("## 当前任务包", "");
439
+ if (!tracker.activePacket) {
440
+ lines.push("- 当前还没有可推进的任务包。", "");
441
+ } else {
442
+ lines.push(`- ID:\`${tracker.activePacket.id}\``);
443
+ lines.push(`- 标题:${tracker.activePacket.title}`);
444
+ lines.push(`- Owner:${tracker.activePacket.owner}`);
445
+ lines.push(`- 优先级:${tracker.activePacket.priority}`);
446
+ lines.push("");
447
+ }
448
+
449
+ lines.push("## 下一包", "");
450
+ if (!tracker.nextPacket) {
451
+ lines.push("- 当前没有下一包,完成后可进入收尾。", "");
452
+ } else {
453
+ lines.push(`- ID:\`${tracker.nextPacket.id}\``);
454
+ lines.push(`- 标题:${tracker.nextPacket.title}`);
455
+ lines.push(`- Owner:${tracker.nextPacket.owner}`);
456
+ lines.push("");
457
+ }
458
+
459
+ lines.push("## 阻塞项", "");
460
+ if (tracker.blockedReasons.length === 0) {
461
+ lines.push("- 当前没有手动标记的阻塞项。", "");
462
+ } else {
463
+ for (const reason of tracker.blockedReasons) {
464
+ lines.push(`- ${reason}`);
465
+ }
466
+ lines.push("");
467
+ }
468
+
469
+ lines.push("## 建议下一步命令", "");
470
+ lines.push(`- \`${tracker.suggestedNextCommand}\``);
471
+ lines.push("", "## 后续命令", "");
472
+ for (const command of tracker.followupCommands) {
473
+ lines.push(`- \`${command}\``);
474
+ }
475
+
476
+ lines.push("", "## 可直接复制给 Agent 的 Progress Prompt", "", "```text", tracker.trackerPrompt, "```");
477
+ return `${lines.join("\n")}\n`;
478
+ }
479
+
480
+ export async function writeAgentProgressTrackerOutput(outputPath, content) {
481
+ return writeScanOutput(outputPath, content);
482
+ }
@@ -55,6 +55,9 @@ function inferSkillForCommand(commandName, flow) {
55
55
  if (commandName === "agent-batch-executor") {
56
56
  return "geo-ai-search-optimization-agent-batch-executor";
57
57
  }
58
+ if (commandName === "agent-progress-tracker") {
59
+ return "geo-ai-search-optimization-agent-progress-tracker";
60
+ }
58
61
  if (commandName === "skills" || commandName === "quick-start") {
59
62
  return "geo-ai-search-optimization-usage";
60
63
  }
@@ -121,6 +124,8 @@ function inferStepPurpose(commandName, flow) {
121
124
  return "把这一轮先做哪一个任务包压成单任务执行入口。";
122
125
  case "agent-batch-executor":
123
126
  return "把前几包任务排成连续执行顺序,但保持一次只推进一包。";
127
+ case "agent-progress-tracker":
128
+ return "追踪当前做到第几包、卡点在哪里,以及下一包该推进什么。";
124
129
  case "apply-plan":
125
130
  return "把交接结果推进到具体执行包。";
126
131
  case "completion-report":
@@ -167,6 +172,8 @@ function inferExpectedArtifact(commandName) {
167
172
  return "agent 单任务执行包";
168
173
  case "agent-batch-executor":
169
174
  return "agent 多任务批次执行包";
175
+ case "agent-progress-tracker":
176
+ return "agent 执行进度追踪工件";
170
177
  case "apply-plan":
171
178
  return "执行包";
172
179
  case "completion-report":
@@ -208,6 +215,9 @@ function buildStepInstructions(parsedCommand, flow) {
208
215
  if (parsedCommand.commandName === "agent-batch-executor") {
209
216
  lines.push("批次模式下也要一次只做一包,上一包通过验证后再进入下一包。");
210
217
  }
218
+ if (parsedCommand.commandName === "agent-progress-tracker") {
219
+ lines.push("这一步用于校准当前做到哪、当前卡点和下一包,不要跳过状态确认直接进入收尾。");
220
+ }
211
221
  if (parsedCommand.commandName === "agent-handoff" && flow.intent === "execute") {
212
222
  lines.push("如果还是 advice-only,说明还缺仓库或本地项目上下文。");
213
223
  }
package/src/auto-flow.js CHANGED
@@ -47,6 +47,9 @@ function inferTaskTextMode(text) {
47
47
  if (/(batch-executor|batch executor|多包|前3包|批次执行|连续推进前几包)/i.test(normalized)) {
48
48
  return "execute";
49
49
  }
50
+ if (/(progress-tracker|progress tracker|进度追踪|做到第几包|卡在哪|下一包是什么)/i.test(normalized)) {
51
+ return "execute";
52
+ }
50
53
  if (/(executor|先做哪一个|先做哪一包|single task|执行第一包|先执行一个任务)/i.test(normalized)) {
51
54
  return "execute";
52
55
  }
@@ -165,6 +168,7 @@ function resolveEffectiveIntent(intent, detected) {
165
168
  "geo-agent-runbook",
166
169
  "geo-agent-executor",
167
170
  "geo-agent-batch-executor",
171
+ "geo-agent-progress-tracker",
168
172
  "geo-apply-plan",
169
173
  "geo-handoff-bundle",
170
174
  "geo-fix-plan"
@@ -257,7 +261,9 @@ function buildCommandChain(detected, intent) {
257
261
  `geo-ai-search-optimization agent-handoff ${source}`,
258
262
  `geo-ai-search-optimization agent-runbook ${source}`,
259
263
  `geo-ai-search-optimization agent-executor ${source}`,
260
- `geo-ai-search-optimization apply-plan ${source}`
264
+ `geo-ai-search-optimization apply-plan ${source}`,
265
+ `geo-ai-search-optimization agent-batch-executor ${source}`,
266
+ `geo-ai-search-optimization agent-progress-tracker ${source}`
261
267
  ]
262
268
  : [`geo-ai-search-optimization fix-plan ${source}`, `geo-ai-search-optimization owner-board ${source}`];
263
269
  case "geo-url-onboarding":
@@ -272,6 +278,7 @@ function buildCommandChain(detected, intent) {
272
278
  `geo-ai-search-optimization agent-runbook ${source}`,
273
279
  `geo-ai-search-optimization agent-executor ${source}`,
274
280
  `geo-ai-search-optimization agent-batch-executor ${source}`,
281
+ `geo-ai-search-optimization agent-progress-tracker ${source}`,
275
282
  `geo-ai-search-optimization completion-report ${source}`
276
283
  ];
277
284
  case "geo-agent-handoff":
@@ -279,31 +286,54 @@ function buildCommandChain(detected, intent) {
279
286
  `geo-ai-search-optimization agent-runbook ${source}`,
280
287
  `geo-ai-search-optimization agent-executor ${source}`,
281
288
  `geo-ai-search-optimization agent-batch-executor ${source}`,
289
+ `geo-ai-search-optimization agent-progress-tracker ${source}`,
282
290
  `geo-ai-search-optimization completion-report ${source}`
283
291
  ];
284
292
  case "geo-agent-runbook":
285
293
  return [
286
294
  `geo-ai-search-optimization agent-executor ${source}`,
287
295
  `geo-ai-search-optimization agent-batch-executor ${source}`,
296
+ `geo-ai-search-optimization agent-progress-tracker ${source}`,
288
297
  `geo-ai-search-optimization completion-report ${source}`,
289
298
  `geo-ai-search-optimization handoff-bundle ${source}`
290
299
  ];
291
300
  case "geo-agent-executor":
292
301
  return [
302
+ `geo-ai-search-optimization agent-progress-tracker ${source}`,
293
303
  `geo-ai-search-optimization completion-report ${source}`,
294
304
  `geo-ai-search-optimization handoff-bundle ${source}`,
295
305
  `geo-ai-search-optimization publish-pack ${source}`
296
306
  ];
297
307
  case "geo-agent-batch-executor":
298
308
  return [
309
+ `geo-ai-search-optimization agent-progress-tracker ${source}`,
299
310
  `geo-ai-search-optimization completion-report ${source}`,
300
311
  `geo-ai-search-optimization handoff-bundle ${source}`,
301
312
  `geo-ai-search-optimization publish-pack ${source}`
302
313
  ];
314
+ case "geo-agent-progress-tracker":
315
+ return detected.parsed?.status === "completed"
316
+ ? [
317
+ `geo-ai-search-optimization completion-report ${source}`,
318
+ `geo-ai-search-optimization handoff-bundle ${source}`,
319
+ `geo-ai-search-optimization publish-pack ${source}`
320
+ ]
321
+ : detected.parsed?.currentTaskId
322
+ ? [
323
+ `geo-ai-search-optimization agent-executor ${source} --task ${detected.parsed.currentTaskId}`,
324
+ `geo-ai-search-optimization completion-report ${source}`,
325
+ `geo-ai-search-optimization handoff-bundle ${source}`
326
+ ]
327
+ : [
328
+ `geo-ai-search-optimization agent-batch-executor ${source}`,
329
+ `geo-ai-search-optimization completion-report ${source}`,
330
+ `geo-ai-search-optimization handoff-bundle ${source}`
331
+ ];
303
332
  case "geo-apply-plan":
304
333
  return [
305
334
  `geo-ai-search-optimization agent-executor ${source}`,
306
335
  `geo-ai-search-optimization agent-batch-executor ${source}`,
336
+ `geo-ai-search-optimization agent-progress-tracker ${source}`,
307
337
  `geo-ai-search-optimization completion-report ${source}`,
308
338
  `geo-ai-search-optimization handoff-bundle ${source}`
309
339
  ];
@@ -374,6 +404,8 @@ function pickSkillName(detected, intent) {
374
404
  return "geo-ai-search-optimization-agent-executor";
375
405
  case "geo-agent-batch-executor":
376
406
  return "geo-ai-search-optimization-agent-batch-executor";
407
+ case "geo-agent-progress-tracker":
408
+ return "geo-ai-search-optimization-agent-progress-tracker";
377
409
  case "geo-completion-report":
378
410
  return "geo-ai-search-optimization-completion-report";
379
411
  case "geo-handoff-bundle":
@@ -411,12 +443,14 @@ function buildSecondarySkillNames(primarySkill, intent, detected) {
411
443
  "geo-agent-runbook",
412
444
  "geo-agent-executor",
413
445
  "geo-agent-batch-executor",
446
+ "geo-agent-progress-tracker",
414
447
  "geo-apply-plan"
415
448
  ].includes(
416
449
  detected.artifactKind
417
450
  )
418
451
  ) {
419
452
  names.add("geo-ai-search-optimization-agent-batch-executor");
453
+ names.add("geo-ai-search-optimization-agent-progress-tracker");
420
454
  names.add("geo-ai-search-optimization-agent-executor");
421
455
  names.add("geo-ai-search-optimization-agent-runbook");
422
456
  names.add("geo-ai-search-optimization-agent-handoff");
@@ -447,6 +481,7 @@ function buildStage(intent, detected) {
447
481
  "geo-agent-runbook",
448
482
  "geo-agent-executor",
449
483
  "geo-agent-batch-executor",
484
+ "geo-agent-progress-tracker",
450
485
  "geo-apply-plan",
451
486
  "geo-handoff-bundle"
452
487
  ].includes(detected.artifactKind)
@@ -460,6 +495,7 @@ function buildStage(intent, detected) {
460
495
  "geo-agent-runbook",
461
496
  "geo-agent-executor",
462
497
  "geo-agent-batch-executor",
498
+ "geo-agent-progress-tracker",
463
499
  "geo-apply-plan",
464
500
  "geo-handoff-bundle"
465
501
  ].includes(detected.artifactKind)
@@ -546,6 +582,9 @@ function buildNextAction(detected, intent, commands) {
546
582
  return `先运行 \`${commands[0]}\` 生成适合外发或交接的结果。`;
547
583
  }
548
584
  if (intent === "execute") {
585
+ if (detected.artifactKind === "geo-agent-progress-tracker") {
586
+ return `先运行 \`${commands[0]}\`,继续当前执行包或校准下一包。`;
587
+ }
549
588
  return `先运行 \`${commands[0]}\`,把当前输入推进到 agent 可执行状态。`;
550
589
  }
551
590
  if (intent === "closeout") {
package/src/cli.js CHANGED
@@ -9,6 +9,11 @@ import {
9
9
  } from "./agent-batch-executor.js";
10
10
  import { createAgentExecutor, renderAgentExecutorMarkdown, writeAgentExecutorOutput } from "./agent-executor.js";
11
11
  import { createAgentHandoff, renderAgentHandoffMarkdown, writeAgentHandoffOutput } from "./agent-handoff.js";
12
+ import {
13
+ createAgentProgressTracker,
14
+ renderAgentProgressTrackerMarkdown,
15
+ writeAgentProgressTrackerOutput
16
+ } from "./agent-progress-tracker.js";
12
17
  import { createAgentRunbook, renderAgentRunbookMarkdown, writeAgentRunbookOutput } from "./agent-runbook.js";
13
18
  import { createAgentSession, renderAgentSessionMarkdown, writeAgentSessionOutput } from "./agent-session.js";
14
19
  import { createAutoFlow, renderAutoFlowMarkdown, writeAutoFlowOutput } from "./auto-flow.js";
@@ -71,6 +76,7 @@ function printHelp() {
71
76
  " geo-ai-search-optimization agent-runbook <input> [--intent <auto|diagnose|guide|execute|share|closeout>] [--task <id>] [--format <markdown|json>] [--out <file>]",
72
77
  " geo-ai-search-optimization agent-executor <input> [--intent <auto|diagnose|guide|execute|share|closeout>] [--task <id>] [--format <markdown|json>] [--out <file>]",
73
78
  " geo-ai-search-optimization agent-batch-executor <input> [--intent <auto|diagnose|guide|execute|share|closeout>] [--task <id>] [--count <count>] [--format <markdown|json>] [--out <file>]",
79
+ " geo-ai-search-optimization agent-progress-tracker <input> [--current <id>] [--completed <id,id>] [--blocked <reason,reason>] [--format <markdown|json>] [--out <file>]",
74
80
  " geo-ai-search-optimization skills [--json]",
75
81
  " geo-ai-search-optimization where",
76
82
  " geo-ai-search-optimization doctor [--json]",
@@ -269,6 +275,34 @@ async function handleAgentBatchExecutor(args) {
269
275
  process.stdout.write(renderedOutput);
270
276
  }
271
277
 
278
+ async function handleAgentProgressTracker(args) {
279
+ const input = args.find((value) => !value.startsWith("-"));
280
+ if (!input) {
281
+ throw new Error("agent-progress-tracker 需要一个输入值,可以是项目路径、网站网址或已导出的工件");
282
+ }
283
+
284
+ const format = getFlagValue(args, "--format") || (hasFlag(args, "--json") ? "json" : undefined);
285
+ const tracker = await createAgentProgressTracker(input, {
286
+ format,
287
+ currentTaskId: getFlagValue(args, "--current"),
288
+ completedPacketIds: getFlagValue(args, "--completed"),
289
+ blockedReasons: getFlagValue(args, "--blocked")
290
+ });
291
+ const outputJson = tracker.format === "json";
292
+ const renderedOutput = outputJson
293
+ ? `${JSON.stringify(tracker, null, 2)}\n`
294
+ : renderAgentProgressTrackerMarkdown(tracker);
295
+
296
+ const outputPath = getFlagValue(args, "--out");
297
+ if (outputPath) {
298
+ const resolvedOutputPath = await writeAgentProgressTrackerOutput(outputPath, renderedOutput);
299
+ process.stdout.write(`已保存 agent progress tracker:${resolvedOutputPath}\n`);
300
+ return;
301
+ }
302
+
303
+ process.stdout.write(renderedOutput);
304
+ }
305
+
272
306
  function handleWhere() {
273
307
  process.stdout.write(
274
308
  [
@@ -834,6 +868,11 @@ export async function runCli(args = []) {
834
868
  return;
835
869
  }
836
870
 
871
+ if (command === "agent-progress-tracker") {
872
+ await handleAgentProgressTracker(rest);
873
+ return;
874
+ }
875
+
837
876
  if (command === "skills") {
838
877
  await handleSkills(rest);
839
878
  return;
package/src/index.js CHANGED
@@ -10,6 +10,11 @@ export { createApplyPlan, renderApplyPlanMarkdown, writeApplyPlanOutput } from "
10
10
  export { createAgentBatchExecutor, renderAgentBatchExecutorMarkdown, writeAgentBatchExecutorOutput } from "./agent-batch-executor.js";
11
11
  export { createAgentHandoff, renderAgentHandoffMarkdown, writeAgentHandoffOutput } from "./agent-handoff.js";
12
12
  export { createAgentExecutor, renderAgentExecutorMarkdown, writeAgentExecutorOutput } from "./agent-executor.js";
13
+ export {
14
+ createAgentProgressTracker,
15
+ renderAgentProgressTrackerMarkdown,
16
+ writeAgentProgressTrackerOutput
17
+ } from "./agent-progress-tracker.js";
13
18
  export { createAgentRunbook, renderAgentRunbookMarkdown, writeAgentRunbookOutput } from "./agent-runbook.js";
14
19
  export { createAgentSession, renderAgentSessionMarkdown, writeAgentSessionOutput } from "./agent-session.js";
15
20
  export { createCompletionReport, renderCompletionReportMarkdown, writeCompletionReportOutput } from "./completion-report.js";
package/src/skills.js CHANGED
@@ -9,6 +9,7 @@ const SKILL_ORDER = [
9
9
  "geo-ai-search-optimization-agent-runbook",
10
10
  "geo-ai-search-optimization-agent-executor",
11
11
  "geo-ai-search-optimization-agent-batch-executor",
12
+ "geo-ai-search-optimization-agent-progress-tracker",
12
13
  "geo-ai-search-optimization-usage",
13
14
  "geo-ai-search-optimization-agent-handoff",
14
15
  "geo-ai-search-optimization-repair-loop",
@@ -27,6 +28,7 @@ const SKILL_CATEGORY = {
27
28
  "geo-ai-search-optimization-agent-runbook": "execution",
28
29
  "geo-ai-search-optimization-agent-executor": "execution",
29
30
  "geo-ai-search-optimization-agent-batch-executor": "execution",
31
+ "geo-ai-search-optimization-agent-progress-tracker": "execution",
30
32
  "geo-ai-search-optimization-usage": "guidance",
31
33
  "geo-ai-search-optimization-agent-handoff": "execution",
32
34
  "geo-ai-search-optimization-repair-loop": "execution",
@@ -164,6 +166,7 @@ export function renderBundledSkillsMarkdown(bundle) {
164
166
  "- 如果要给 agent 一份执行手册和检查表,再进入 agent-runbook。",
165
167
  "- 如果要直接告诉 agent 这轮先做哪 1 包,再进入 agent-executor。",
166
168
  "- 如果要连续推进前 2 到 3 包,但仍然一次只做一包,再进入 agent-batch-executor。",
169
+ "- 如果要追踪目前做到第几包、卡在哪、下一包是什么,再进入 agent-progress-tracker。",
167
170
  "- 再看 usage skill,知道什么时候该跑哪个命令。",
168
171
  "- 如果要交给 agent 执行,再进入 handoff / apply / completion 这一条执行链。",
169
172
  "- 如果要产出给团队分发,再进入 share / export / html / publish 这一条交付链。",