geo-ai-search-optimization 1.2.11 → 1.2.13
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 +59 -0
- package/package.json +1 -1
- package/resources/geo-ai-search-optimization/references/skill-bundle-map.md +20 -0
- package/resources/geo-ai-search-optimization-agent-progress-tracker/SKILL.md +23 -0
- package/resources/geo-ai-search-optimization-agent-progress-tracker/agents/openai.yaml +4 -0
- package/resources/geo-ai-search-optimization-agent-status-board/SKILL.md +23 -0
- package/resources/geo-ai-search-optimization-agent-status-board/agents/openai.yaml +4 -0
- package/resources/geo-ai-search-optimization-usage/SKILL.md +24 -14
- package/src/agent-progress-tracker.js +496 -0
- package/src/agent-session.js +20 -0
- package/src/agent-status-board.js +246 -0
- package/src/auto-flow.js +68 -1
- package/src/cli.js +74 -0
- package/src/index.js +6 -0
- package/src/skills.js +6 -0
|
@@ -0,0 +1,496 @@
|
|
|
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-agent-status-board" && parsed.tracker?.applyPlan?.kind === "geo-apply-plan") {
|
|
124
|
+
return {
|
|
125
|
+
source: parsed.source || parsed.tracker.source || parsed.tracker.applyPlan.source || input,
|
|
126
|
+
sourceType: parsed.sourceType || parsed.tracker.sourceType || parsed.tracker.applyPlan.sourceType || "json",
|
|
127
|
+
artifactKind: parsed.kind,
|
|
128
|
+
applyPlan: parsed.tracker.applyPlan,
|
|
129
|
+
trackerState: {
|
|
130
|
+
currentTaskId: parsed.tracker.currentTaskId || parsed.tracker.activePacket?.id || null,
|
|
131
|
+
completedPacketIds: parsed.tracker.completedPacketIds || [],
|
|
132
|
+
blockedReasons: parsed.tracker.blockedReasons || []
|
|
133
|
+
}
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
if (parsed?.kind === "geo-completion-report") {
|
|
138
|
+
return {
|
|
139
|
+
source: parsed.source || input,
|
|
140
|
+
sourceType: parsed.sourceType || "json",
|
|
141
|
+
artifactKind: parsed.kind,
|
|
142
|
+
applyPlan: buildPseudoApplyPlanFromCompletionReport(parsed),
|
|
143
|
+
trackerState: {}
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
return null;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
async function resolveExecutionContext(input) {
|
|
151
|
+
if (!input) {
|
|
152
|
+
throw new Error("agent-progress-tracker 需要一个输入值,可以是项目路径、网站网址或已导出的 JSON 工件。");
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
if (isUrlInput(input)) {
|
|
156
|
+
const applyPlan = await createApplyPlan(input, { format: "json" });
|
|
157
|
+
return {
|
|
158
|
+
source: applyPlan.source,
|
|
159
|
+
sourceType: applyPlan.sourceType,
|
|
160
|
+
artifactKind: applyPlan.kind,
|
|
161
|
+
applyPlan,
|
|
162
|
+
trackerState: {}
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
const resolvedPath = path.resolve(input);
|
|
167
|
+
if (!(await pathExists(resolvedPath))) {
|
|
168
|
+
const applyPlan = await createApplyPlan(input, { format: "json" });
|
|
169
|
+
return {
|
|
170
|
+
source: applyPlan.source,
|
|
171
|
+
sourceType: applyPlan.sourceType,
|
|
172
|
+
artifactKind: applyPlan.kind,
|
|
173
|
+
applyPlan,
|
|
174
|
+
trackerState: {}
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
const stat = await fs.stat(resolvedPath);
|
|
179
|
+
if (stat.isDirectory()) {
|
|
180
|
+
const applyPlan = await createApplyPlan(resolvedPath, { format: "json" });
|
|
181
|
+
return {
|
|
182
|
+
source: applyPlan.source,
|
|
183
|
+
sourceType: applyPlan.sourceType,
|
|
184
|
+
artifactKind: applyPlan.kind,
|
|
185
|
+
applyPlan,
|
|
186
|
+
trackerState: {}
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
if (path.extname(resolvedPath).toLowerCase() === ".json") {
|
|
191
|
+
const raw = await fs.readFile(resolvedPath, "utf8");
|
|
192
|
+
const parsed = JSON.parse(raw);
|
|
193
|
+
const resolved = buildExecutionContextFromParsedArtifact(parsed, resolvedPath);
|
|
194
|
+
if (resolved) {
|
|
195
|
+
return resolved;
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
const applyPlan = await createApplyPlan(resolvedPath, { format: "json" });
|
|
200
|
+
return {
|
|
201
|
+
source: applyPlan.source,
|
|
202
|
+
sourceType: applyPlan.sourceType,
|
|
203
|
+
artifactKind: applyPlan.kind,
|
|
204
|
+
applyPlan,
|
|
205
|
+
trackerState: {}
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
function pickCompletedIds(packets, options, trackerState) {
|
|
210
|
+
const packetIds = new Set((packets || []).map((packet) => packet.id));
|
|
211
|
+
const requestedIds =
|
|
212
|
+
options.completedPacketIds != null ? parseCommaList(options.completedPacketIds) : trackerState.completedPacketIds || [];
|
|
213
|
+
|
|
214
|
+
return requestedIds.filter((id) => packetIds.has(id));
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
function pickCurrentTaskId(packets, completedIds, options, trackerState) {
|
|
218
|
+
const packetIds = new Set((packets || []).map((packet) => packet.id));
|
|
219
|
+
const completedSet = new Set(completedIds);
|
|
220
|
+
const requested = options.currentTaskId || trackerState.currentTaskId;
|
|
221
|
+
if (requested && packetIds.has(requested) && !completedSet.has(requested)) {
|
|
222
|
+
return requested;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
const remaining = (packets || []).filter((packet) => !completedSet.has(packet.id));
|
|
226
|
+
return remaining[0]?.id || null;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
function pickBlockedReasons(options, trackerState) {
|
|
230
|
+
if (options.blockedReasons != null) {
|
|
231
|
+
return parseCommaList(options.blockedReasons);
|
|
232
|
+
}
|
|
233
|
+
return trackerState.blockedReasons || [];
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
function inferTrackerStatus(totalPackets, completedCount, blockedReasons) {
|
|
237
|
+
if (blockedReasons.length > 0) {
|
|
238
|
+
return "blocked";
|
|
239
|
+
}
|
|
240
|
+
if (totalPackets === 0) {
|
|
241
|
+
return "needs-context";
|
|
242
|
+
}
|
|
243
|
+
if (completedCount === 0) {
|
|
244
|
+
return "not-started";
|
|
245
|
+
}
|
|
246
|
+
if (completedCount >= totalPackets) {
|
|
247
|
+
return "completed";
|
|
248
|
+
}
|
|
249
|
+
return "in-progress";
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
function buildStatusSummary(status, activePacket, nextPacket, blockedReasons) {
|
|
253
|
+
switch (status) {
|
|
254
|
+
case "blocked":
|
|
255
|
+
return `当前已识别到阻塞,先处理这些问题再继续:${blockedReasons.join(";")}`;
|
|
256
|
+
case "needs-context":
|
|
257
|
+
return "当前还没有足够的执行包,说明需要先补上下文或重新生成 apply-plan。";
|
|
258
|
+
case "completed":
|
|
259
|
+
return "当前批次已全部完成,可以进入 completion-report 与交付收尾。";
|
|
260
|
+
case "in-progress":
|
|
261
|
+
return activePacket
|
|
262
|
+
? `当前正推进 ${activePacket.id},完成后继续 ${nextPacket?.id || "进入收尾阶段"}。`
|
|
263
|
+
: "当前已经开始执行,但还没有明确当前包,请先校准执行顺序。";
|
|
264
|
+
default:
|
|
265
|
+
return activePacket
|
|
266
|
+
? `当前尚未开始,建议先从 ${activePacket.id} 开始。`
|
|
267
|
+
: "当前尚未开始,先确认执行包和优先级。";
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
function buildSuggestedNextCommand(source, status, activePacket, remainingPackets, blockedReasons) {
|
|
272
|
+
if (status === "blocked") {
|
|
273
|
+
if (activePacket) {
|
|
274
|
+
return `geo-ai-search-optimization agent-runbook ${source} --task ${activePacket.id}`;
|
|
275
|
+
}
|
|
276
|
+
return `geo-ai-search-optimization agent-session ${source}`;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
if (status === "completed") {
|
|
280
|
+
return `geo-ai-search-optimization completion-report ${source}`;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
if (activePacket) {
|
|
284
|
+
return `geo-ai-search-optimization agent-executor ${source} --task ${activePacket.id}`;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
if (remainingPackets.length > 1) {
|
|
288
|
+
return `geo-ai-search-optimization agent-batch-executor ${source}`;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
if (blockedReasons.length > 0) {
|
|
292
|
+
return `geo-ai-search-optimization agent-session ${source}`;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
return `geo-ai-search-optimization apply-plan ${source}`;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
function buildFollowupCommands(source, status, activePacket, nextPacket, remainingPackets) {
|
|
299
|
+
if (status === "completed") {
|
|
300
|
+
return [
|
|
301
|
+
`geo-ai-search-optimization handoff-bundle ${source}`,
|
|
302
|
+
`geo-ai-search-optimization publish-pack ${source}`
|
|
303
|
+
];
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
const commands = [];
|
|
307
|
+
|
|
308
|
+
if (nextPacket) {
|
|
309
|
+
commands.push(`geo-ai-search-optimization agent-batch-executor ${source} --task ${nextPacket.id}`);
|
|
310
|
+
} else if (remainingPackets.length > 1) {
|
|
311
|
+
commands.push(`geo-ai-search-optimization agent-batch-executor ${source}`);
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
if (activePacket) {
|
|
315
|
+
commands.push(`geo-ai-search-optimization agent-progress-tracker ${source} --current ${activePacket.id}`);
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
commands.push(`geo-ai-search-optimization completion-report ${source}`);
|
|
319
|
+
commands.push(`geo-ai-search-optimization handoff-bundle ${source}`);
|
|
320
|
+
return commands;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
function buildTrackerPrompt(tracker) {
|
|
324
|
+
const lines = [
|
|
325
|
+
"你现在进入 GEO 执行进度追踪模式。",
|
|
326
|
+
`当前输入:${tracker.source}`,
|
|
327
|
+
`当前状态:${tracker.status}`,
|
|
328
|
+
`当前总结:${tracker.statusSummary}`,
|
|
329
|
+
`总任务包:${tracker.totalPackets}`,
|
|
330
|
+
`已完成:${tracker.completedCount}`,
|
|
331
|
+
`剩余:${tracker.remainingCount}`
|
|
332
|
+
];
|
|
333
|
+
|
|
334
|
+
if (tracker.activePacket) {
|
|
335
|
+
lines.push(`当前执行包:${tracker.activePacket.id}|${tracker.activePacket.title}`);
|
|
336
|
+
}
|
|
337
|
+
if (tracker.nextPacket) {
|
|
338
|
+
lines.push(`下一包:${tracker.nextPacket.id}|${tracker.nextPacket.title}`);
|
|
339
|
+
}
|
|
340
|
+
if (tracker.blockedReasons.length > 0) {
|
|
341
|
+
lines.push(`阻塞原因:${tracker.blockedReasons.join(";")}`);
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
lines.push("请输出:");
|
|
345
|
+
lines.push("1. 当前已完成到哪");
|
|
346
|
+
lines.push("2. 当前卡点是什么");
|
|
347
|
+
lines.push("3. 现在该继续哪一包");
|
|
348
|
+
lines.push("4. 完成这一包后下一步是什么");
|
|
349
|
+
return lines.join("\n");
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
function summarizePacket(packet) {
|
|
353
|
+
if (!packet) {
|
|
354
|
+
return null;
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
return {
|
|
358
|
+
id: packet.id,
|
|
359
|
+
title: packet.title,
|
|
360
|
+
owner: packet.owner,
|
|
361
|
+
priority: packet.priority || "P1",
|
|
362
|
+
doneWhen: packet.doneWhen || [],
|
|
363
|
+
validationCommands: packet.validationCommands || []
|
|
364
|
+
};
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
export async function createAgentProgressTracker(input, options = {}) {
|
|
368
|
+
const format = normalizeFormat(options.format);
|
|
369
|
+
const context = await resolveExecutionContext(input);
|
|
370
|
+
const packets = context.applyPlan?.packets || [];
|
|
371
|
+
const completedPacketIds = pickCompletedIds(packets, options, context.trackerState);
|
|
372
|
+
const completedSet = new Set(completedPacketIds);
|
|
373
|
+
const currentTaskId = pickCurrentTaskId(packets, completedPacketIds, options, context.trackerState);
|
|
374
|
+
const blockedReasons = pickBlockedReasons(options, context.trackerState);
|
|
375
|
+
const completedPackets = packets.filter((packet) => completedSet.has(packet.id));
|
|
376
|
+
const remainingPackets = packets.filter((packet) => !completedSet.has(packet.id));
|
|
377
|
+
const activePacket = summarizePacket(remainingPackets.find((packet) => packet.id === currentTaskId) || remainingPackets[0]);
|
|
378
|
+
const nextPacket = summarizePacket(
|
|
379
|
+
activePacket ? remainingPackets.find((packet) => packet.id !== activePacket.id) : remainingPackets[0]
|
|
380
|
+
);
|
|
381
|
+
const totalPackets = packets.length;
|
|
382
|
+
const completedCount = completedPackets.length;
|
|
383
|
+
const remainingCount = Math.max(totalPackets - completedCount, 0);
|
|
384
|
+
const progressPercent = totalPackets > 0 ? Math.round((completedCount / totalPackets) * 100) : 0;
|
|
385
|
+
const status = inferTrackerStatus(totalPackets, completedCount, blockedReasons);
|
|
386
|
+
const statusSummary = buildStatusSummary(status, activePacket, nextPacket, blockedReasons);
|
|
387
|
+
const suggestedNextCommand = buildSuggestedNextCommand(
|
|
388
|
+
context.source,
|
|
389
|
+
status,
|
|
390
|
+
activePacket,
|
|
391
|
+
remainingPackets,
|
|
392
|
+
blockedReasons
|
|
393
|
+
);
|
|
394
|
+
const followupCommands = buildFollowupCommands(context.source, status, activePacket, nextPacket, remainingPackets);
|
|
395
|
+
|
|
396
|
+
const tracker = {
|
|
397
|
+
kind: "geo-agent-progress-tracker",
|
|
398
|
+
input,
|
|
399
|
+
source: context.source,
|
|
400
|
+
sourceType: context.sourceType,
|
|
401
|
+
artifactKind: context.artifactKind,
|
|
402
|
+
format,
|
|
403
|
+
executionType: context.applyPlan?.executionType || "guided-planning",
|
|
404
|
+
status,
|
|
405
|
+
statusSummary,
|
|
406
|
+
totalPackets,
|
|
407
|
+
completedCount,
|
|
408
|
+
remainingCount,
|
|
409
|
+
progressPercent,
|
|
410
|
+
currentTaskId: activePacket?.id || null,
|
|
411
|
+
completedPacketIds,
|
|
412
|
+
blockedReasons,
|
|
413
|
+
completedPackets: completedPackets.map(summarizePacket),
|
|
414
|
+
activePacket,
|
|
415
|
+
nextPacket,
|
|
416
|
+
remainingPackets: remainingPackets.map(summarizePacket),
|
|
417
|
+
suggestedNextCommand,
|
|
418
|
+
followupCommands,
|
|
419
|
+
trackerPrompt: "",
|
|
420
|
+
applyPlan: context.applyPlan
|
|
421
|
+
};
|
|
422
|
+
|
|
423
|
+
tracker.trackerPrompt = buildTrackerPrompt(tracker);
|
|
424
|
+
return tracker;
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
export function renderAgentProgressTrackerMarkdown(tracker) {
|
|
428
|
+
const lines = [
|
|
429
|
+
"# GEO Agent Progress Tracker",
|
|
430
|
+
"",
|
|
431
|
+
`- 输入:\`${tracker.source}\``,
|
|
432
|
+
`- 来源类型:\`${tracker.sourceType}\``,
|
|
433
|
+
`- 工件类型:\`${tracker.artifactKind}\``,
|
|
434
|
+
`- 当前状态:\`${tracker.status}\``,
|
|
435
|
+
`- 执行类型:\`${tracker.executionType}\``,
|
|
436
|
+
`- 进度:\`${tracker.progressPercent}%\``,
|
|
437
|
+
`- 已完成:\`${tracker.completedCount}/${tracker.totalPackets}\``,
|
|
438
|
+
`- 当前总结:${tracker.statusSummary}`,
|
|
439
|
+
""
|
|
440
|
+
];
|
|
441
|
+
|
|
442
|
+
lines.push("## 已完成任务包", "");
|
|
443
|
+
if (tracker.completedPackets.length === 0) {
|
|
444
|
+
lines.push("- 当前还没有标记为已完成的任务包。", "");
|
|
445
|
+
} else {
|
|
446
|
+
for (const packet of tracker.completedPackets) {
|
|
447
|
+
lines.push(`- ${packet.id}|${packet.title}`);
|
|
448
|
+
}
|
|
449
|
+
lines.push("");
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
lines.push("## 当前任务包", "");
|
|
453
|
+
if (!tracker.activePacket) {
|
|
454
|
+
lines.push("- 当前还没有可推进的任务包。", "");
|
|
455
|
+
} else {
|
|
456
|
+
lines.push(`- ID:\`${tracker.activePacket.id}\``);
|
|
457
|
+
lines.push(`- 标题:${tracker.activePacket.title}`);
|
|
458
|
+
lines.push(`- Owner:${tracker.activePacket.owner}`);
|
|
459
|
+
lines.push(`- 优先级:${tracker.activePacket.priority}`);
|
|
460
|
+
lines.push("");
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
lines.push("## 下一包", "");
|
|
464
|
+
if (!tracker.nextPacket) {
|
|
465
|
+
lines.push("- 当前没有下一包,完成后可进入收尾。", "");
|
|
466
|
+
} else {
|
|
467
|
+
lines.push(`- ID:\`${tracker.nextPacket.id}\``);
|
|
468
|
+
lines.push(`- 标题:${tracker.nextPacket.title}`);
|
|
469
|
+
lines.push(`- Owner:${tracker.nextPacket.owner}`);
|
|
470
|
+
lines.push("");
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
lines.push("## 阻塞项", "");
|
|
474
|
+
if (tracker.blockedReasons.length === 0) {
|
|
475
|
+
lines.push("- 当前没有手动标记的阻塞项。", "");
|
|
476
|
+
} else {
|
|
477
|
+
for (const reason of tracker.blockedReasons) {
|
|
478
|
+
lines.push(`- ${reason}`);
|
|
479
|
+
}
|
|
480
|
+
lines.push("");
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
lines.push("## 建议下一步命令", "");
|
|
484
|
+
lines.push(`- \`${tracker.suggestedNextCommand}\``);
|
|
485
|
+
lines.push("", "## 后续命令", "");
|
|
486
|
+
for (const command of tracker.followupCommands) {
|
|
487
|
+
lines.push(`- \`${command}\``);
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
lines.push("", "## 可直接复制给 Agent 的 Progress Prompt", "", "```text", tracker.trackerPrompt, "```");
|
|
491
|
+
return `${lines.join("\n")}\n`;
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
export async function writeAgentProgressTrackerOutput(outputPath, content) {
|
|
495
|
+
return writeScanOutput(outputPath, content);
|
|
496
|
+
}
|
package/src/agent-session.js
CHANGED
|
@@ -55,6 +55,12 @@ 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
|
+
}
|
|
61
|
+
if (commandName === "agent-status-board") {
|
|
62
|
+
return "geo-ai-search-optimization-agent-status-board";
|
|
63
|
+
}
|
|
58
64
|
if (commandName === "skills" || commandName === "quick-start") {
|
|
59
65
|
return "geo-ai-search-optimization-usage";
|
|
60
66
|
}
|
|
@@ -121,6 +127,10 @@ function inferStepPurpose(commandName, flow) {
|
|
|
121
127
|
return "把这一轮先做哪一个任务包压成单任务执行入口。";
|
|
122
128
|
case "agent-batch-executor":
|
|
123
129
|
return "把前几包任务排成连续执行顺序,但保持一次只推进一包。";
|
|
130
|
+
case "agent-progress-tracker":
|
|
131
|
+
return "追踪当前做到第几包、卡点在哪里,以及下一包该推进什么。";
|
|
132
|
+
case "agent-status-board":
|
|
133
|
+
return "把当前执行状态整理成 PM 和 agent 都能直接消费的分栏看板。";
|
|
124
134
|
case "apply-plan":
|
|
125
135
|
return "把交接结果推进到具体执行包。";
|
|
126
136
|
case "completion-report":
|
|
@@ -167,6 +177,10 @@ function inferExpectedArtifact(commandName) {
|
|
|
167
177
|
return "agent 单任务执行包";
|
|
168
178
|
case "agent-batch-executor":
|
|
169
179
|
return "agent 多任务批次执行包";
|
|
180
|
+
case "agent-progress-tracker":
|
|
181
|
+
return "agent 执行进度追踪工件";
|
|
182
|
+
case "agent-status-board":
|
|
183
|
+
return "agent 执行状态看板";
|
|
170
184
|
case "apply-plan":
|
|
171
185
|
return "执行包";
|
|
172
186
|
case "completion-report":
|
|
@@ -208,6 +222,12 @@ function buildStepInstructions(parsedCommand, flow) {
|
|
|
208
222
|
if (parsedCommand.commandName === "agent-batch-executor") {
|
|
209
223
|
lines.push("批次模式下也要一次只做一包,上一包通过验证后再进入下一包。");
|
|
210
224
|
}
|
|
225
|
+
if (parsedCommand.commandName === "agent-progress-tracker") {
|
|
226
|
+
lines.push("这一步用于校准当前做到哪、当前卡点和下一包,不要跳过状态确认直接进入收尾。");
|
|
227
|
+
}
|
|
228
|
+
if (parsedCommand.commandName === "agent-status-board") {
|
|
229
|
+
lines.push("这一步用于把当前执行状态转成看板视图,方便 PM 和 agent 对齐当前阶段。");
|
|
230
|
+
}
|
|
211
231
|
if (parsedCommand.commandName === "agent-handoff" && flow.intent === "execute") {
|
|
212
232
|
lines.push("如果还是 advice-only,说明还缺仓库或本地项目上下文。");
|
|
213
233
|
}
|