geo-ai-search-optimization 1.2.14 → 1.2.16

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
@@ -207,6 +207,45 @@ geo-ai-search-optimization agent-checkpoint ./reports/agent-progress-tracker.jso
207
207
  - 交接说明
208
208
  - 可直接复制给 agent 的 checkpoint prompt
209
209
 
210
+ ## Agent Decision Log 命令
211
+
212
+ 如果你希望把每一轮 checkpoint 累积成“为什么之前这样决定”的历史,而不是只保留单轮状态,可以直接用 `agent-decision-log`:
213
+
214
+ ```bash
215
+ geo-ai-search-optimization agent-decision-log ./your-site
216
+ geo-ai-search-optimization agent-decision-log ./reports/agent-checkpoint.json --note "本轮先解除模板缺失问题"
217
+ geo-ai-search-optimization agent-decision-log ./your-site --append-from ./reports/agent-decision-log.json --blocked "缺少模板文件" --format json --out ./reports/agent-decision-log.json
218
+ ```
219
+
220
+ `agent-decision-log` 会输出:
221
+
222
+ - 当前累计决策次数
223
+ - 最新检查点类型与决策
224
+ - 当前重点与开放阻塞
225
+ - 决策时间线
226
+ - 最新建议下一步命令
227
+ - 可直接复制给 agent 的 decision log prompt
228
+
229
+ ## Agent Retrospective 命令
230
+
231
+ 如果你希望不只是看多轮决策历史,而是直接总结“为什么这几轮顺利 / 为什么总卡住”,可以直接用 `agent-retrospective`:
232
+
233
+ ```bash
234
+ geo-ai-search-optimization agent-retrospective ./your-site
235
+ geo-ai-search-optimization agent-retrospective ./reports/agent-decision-log.json
236
+ geo-ai-search-optimization agent-retrospective ./reports/agent-decision-log.json --format json --out ./reports/agent-retrospective.json
237
+ ```
238
+
239
+ `agent-retrospective` 会输出:
240
+
241
+ - 当前复盘状态
242
+ - 重复阻塞与重复任务包
243
+ - 关键学习
244
+ - 下一轮建议
245
+ - 决策分布
246
+ - 多轮时间线
247
+ - 可直接复制给 agent 的 retrospective prompt
248
+
210
249
  ## Quick Start
211
250
 
212
251
  如果你要从 0 到 1 启动一个 GEO 项目,建议照这个顺序做。
@@ -608,6 +647,8 @@ geo-ai-search-optimization agent-batch-executor ./your-site
608
647
  geo-ai-search-optimization agent-progress-tracker ./your-site
609
648
  geo-ai-search-optimization agent-status-board ./your-site
610
649
  geo-ai-search-optimization agent-checkpoint ./your-site
650
+ geo-ai-search-optimization agent-decision-log ./your-site
651
+ geo-ai-search-optimization agent-retrospective ./your-site
611
652
  geo-ai-search-optimization skills
612
653
  geo-ai-search-optimization where
613
654
  geo-ai-search-optimization doctor
@@ -678,6 +719,20 @@ geo-ai-search-optimization help
678
719
  - 输出 do-now checklist、stop checklist、success checklist、验证命令和回报模板
679
720
  - 新增 `geo-ai-search-optimization-agent-executor` skill
680
721
 
722
+ ## New in 1.2.16
723
+
724
+ - 新增 `agent-retrospective` 命令
725
+ - 把多轮 decision log 压成复盘视图,识别重复阻塞、重复任务包和反复出现的决策模式
726
+ - 支持从 `agent-decision-log`、`agent-checkpoint`、目录、网址等输入继续生成 retrospective
727
+ - 新增 `geo-ai-search-optimization-agent-retrospective` skill
728
+
729
+ ## New in 1.2.15
730
+
731
+ - 新增 `agent-decision-log` 命令
732
+ - 把多轮 checkpoint 沉淀成可继承的决策历史,而不是只有单轮阶段判断
733
+ - 支持从 `agent-checkpoint`、`agent-status-board`、`agent-progress-tracker`、目录、网址等输入继续生成或追加决策记录
734
+ - 新增 `geo-ai-search-optimization-agent-decision-log` skill
735
+
681
736
  ## New in 1.2.14
682
737
 
683
738
  - 新增 `agent-checkpoint` 命令
@@ -900,6 +955,8 @@ The installed package now includes a bundled GEO skill pack, including:
900
955
  - `geo-ai-search-optimization-agent-progress-tracker`
901
956
  - `geo-ai-search-optimization-agent-status-board`
902
957
  - `geo-ai-search-optimization-agent-checkpoint`
958
+ - `geo-ai-search-optimization-agent-decision-log`
959
+ - `geo-ai-search-optimization-agent-retrospective`
903
960
  - `geo-ai-search-optimization-usage`
904
961
  - `geo-ai-search-optimization-agent-handoff`
905
962
  - `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.14",
3
+ "version": "1.2.16",
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": {
@@ -102,6 +102,26 @@ Best for:
102
102
  - producing a clean handoff note at the end of one execution round
103
103
  - giving PM and the next agent one checkpoint artifact to align on
104
104
 
105
+ ### `geo-ai-search-optimization-agent-decision-log`
106
+
107
+ Use this when the next agent should inherit decision history, not just one checkpoint.
108
+
109
+ Best for:
110
+
111
+ - preserving why previous rounds continued, paused, or moved to closeout
112
+ - giving PM and the next agent a reusable decision history across rounds
113
+ - appending a fresh checkpoint onto an existing GEO execution history
114
+
115
+ ### `geo-ai-search-optimization-agent-retrospective`
116
+
117
+ Use this when the next agent or PM should understand the pattern across several rounds, not just inherit the last decision.
118
+
119
+ Best for:
120
+
121
+ - explaining why the same blocker or packet kept recurring
122
+ - turning decision history into lessons learned and next-round advice
123
+ - producing a cross-round retrospective before meetings, closeout, or final delivery
124
+
105
125
  ## Usage guide
106
126
 
107
127
  ### `geo-ai-search-optimization-usage`
@@ -0,0 +1,23 @@
1
+ ---
2
+ name: geo-ai-search-optimization-agent-decision-log
3
+ description: Turn GEO checkpoints into a reusable decision history. Use when an agent or PM should preserve why each GEO round continued, paused for blockers, or moved to closeout, so the next agent can inherit the reasoning instead of re-evaluating the entire chain from scratch.
4
+ ---
5
+
6
+ # GEO Agent Decision Log
7
+
8
+ Use this skill when the team needs durable decision memory across multiple GEO execution rounds.
9
+
10
+ `GEO = Generative Engine Optimization`
11
+
12
+ ## What it does
13
+
14
+ - converts the current GEO round into a loggable decision entry
15
+ - preserves why the round continued, paused for blockers, or moved to closeout
16
+ - keeps the latest checkpoint, current packet, next packet, blockers, and handoff note together
17
+ - gives the next agent a stable history instead of isolated one-round artifacts
18
+
19
+ ## Best use
20
+
21
+ - when one checkpoint is not enough and the next agent should see decision history
22
+ - when PM asks why the team chose to continue, unblock, or close out in previous rounds
23
+ - when you want to append a new decision to an existing GEO execution history
@@ -0,0 +1,4 @@
1
+ interface:
2
+ display_name: "GEO Agent Decision Log"
3
+ short_description: "Preserve GEO round decisions as reusable history"
4
+ default_prompt: "Use $geo-ai-search-optimization-agent-decision-log to turn this GEO execution state into a reusable decision history with latest checkpoint, rationale, and next command."
@@ -0,0 +1,23 @@
1
+ ---
2
+ name: geo-ai-search-optimization-agent-retrospective
3
+ description: Turn GEO decision history into a multi-round retrospective. Use when an agent or PM should understand why prior GEO rounds kept advancing, stalled on blockers, or reached closeout, and needs recurring patterns, lessons learned, and next-round guidance instead of a single checkpoint.
4
+ ---
5
+
6
+ # GEO Agent Retrospective
7
+
8
+ Use this skill when one checkpoint or one decision log is not enough, and the team needs to understand the pattern across rounds.
9
+
10
+ `GEO = Generative Engine Optimization`
11
+
12
+ ## What it does
13
+
14
+ - reads GEO decision history across multiple rounds
15
+ - highlights recurring blockers, repeated packets, and repeated decisions
16
+ - explains what changed from round to round
17
+ - turns history into lessons learned and next-round advice
18
+
19
+ ## Best use
20
+
21
+ - when PM asks why the team keeps getting blocked in the same place
22
+ - when the next agent should inherit not only the latest decision, but the pattern behind it
23
+ - when the team wants a reusable retrospective before closeout, meetings, or final delivery
@@ -0,0 +1,4 @@
1
+ interface:
2
+ display_name: "GEO Agent Retrospective"
3
+ short_description: "Summarize cross-round GEO patterns and lessons"
4
+ default_prompt: "Use $geo-ai-search-optimization-agent-retrospective to turn this GEO decision history into a multi-round retrospective with recurring patterns, lessons learned, and next-round guidance."
@@ -13,7 +13,7 @@ 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 twenty-one layers:
16
+ The package is best explained as twenty-three 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
@@ -23,19 +23,21 @@ The package is best explained as twenty-one layers:
23
23
  6. `agent-progress-tracker`: track which packet is done, which one is active, and what comes next
24
24
  7. `agent-status-board`: turn the execution state into a board view for PM and agents
25
25
  8. `agent-checkpoint`: freeze the current round into a continue / unblock / closeout decision
26
- 9. `skills`: inspect the bundled skill package
27
- 10. `onboard-url` / `onboard`: first look
28
- 11. `scan`: raw signal check
29
- 12. `audit` / `report`: diagnosis
30
- 13. `fix-plan` / `owner-board`: execution planning
31
- 14. `agent-handoff`: agent takeover package
32
- 15. `apply-plan`: execution loop
33
- 16. `completion-report`: closeout
34
- 17. `handoff-bundle`: all-in-one package
35
- 18. `share-pack`: audience-ready delivery
36
- 19. `export-pack`: folder export
37
- 20. `html-pack` / `publish-pack`: browsable and final delivery output
38
- 21. `pm-brief` / `roadmap`: stakeholder alignment
26
+ 9. `agent-decision-log`: preserve why each round continued, paused, or closed out
27
+ 10. `agent-retrospective`: explain multi-round patterns, lessons, and next-round advice
28
+ 11. `skills`: inspect the bundled skill package
29
+ 12. `onboard-url` / `onboard`: first look
30
+ 13. `scan`: raw signal check
31
+ 14. `audit` / `report`: diagnosis
32
+ 15. `fix-plan` / `owner-board`: execution planning
33
+ 16. `agent-handoff`: agent takeover package
34
+ 17. `apply-plan`: execution loop
35
+ 18. `completion-report`: closeout
36
+ 19. `handoff-bundle`: all-in-one package
37
+ 20. `share-pack`: audience-ready delivery
38
+ 21. `export-pack`: folder export
39
+ 22. `html-pack` / `publish-pack`: browsable and final delivery output
40
+ 23. `pm-brief` / `roadmap`: stakeholder alignment
39
41
 
40
42
  ## Recommended command order
41
43
 
@@ -50,6 +52,8 @@ npx geo-ai-search-optimization agent-batch-executor https://example.com
50
52
  npx geo-ai-search-optimization agent-progress-tracker https://example.com
51
53
  npx geo-ai-search-optimization agent-status-board https://example.com
52
54
  npx geo-ai-search-optimization agent-checkpoint https://example.com
55
+ npx geo-ai-search-optimization agent-decision-log https://example.com
56
+ npx geo-ai-search-optimization agent-retrospective https://example.com
53
57
  npx geo-ai-search-optimization onboard-url https://example.com
54
58
  npx geo-ai-search-optimization pm-brief https://example.com
55
59
  npx geo-ai-search-optimization roadmap https://example.com
@@ -66,6 +70,8 @@ npx geo-ai-search-optimization agent-batch-executor ./your-site
66
70
  npx geo-ai-search-optimization agent-progress-tracker ./your-site
67
71
  npx geo-ai-search-optimization agent-status-board ./your-site
68
72
  npx geo-ai-search-optimization agent-checkpoint ./your-site
73
+ npx geo-ai-search-optimization agent-decision-log ./your-site
74
+ npx geo-ai-search-optimization agent-retrospective ./your-site
69
75
  npx geo-ai-search-optimization scan ./your-site
70
76
  npx geo-ai-search-optimization audit ./your-site
71
77
  npx geo-ai-search-optimization fix-plan ./your-site
@@ -91,6 +97,8 @@ npx geo-ai-search-optimization roadmap ./your-site
91
97
  - `agent-progress-tracker`: show execution progress, current packet, blockers, and the next packet to advance
92
98
  - `agent-status-board`: present the current execution state as a board with done, in-progress, blocked, next, and queued columns
93
99
  - `agent-checkpoint`: convert the current round into a checkpoint decision for continue, unblock, or closeout
100
+ - `agent-decision-log`: preserve multiple rounds of checkpoint history so the next agent can inherit the reasoning
101
+ - `agent-retrospective`: explain why the last few rounds advanced or stalled, and turn that into lessons and next-round guidance
94
102
  - `onboard-url`: first-time website check from a live URL
95
103
  - `onboard`: interactive first-time onboarding
96
104
  - `skills`: list the bundled skills and decide which skill or command chain fits the task
@@ -123,6 +131,8 @@ When explaining the tool to a user:
123
131
  - if the user wants the next agent to explain current progress, blockers, and the next packet, move them to `agent-progress-tracker`
124
132
  - if the user wants the next agent to present execution state as a board for PM and agent coordination, move them to `agent-status-board`
125
133
  - if the user wants a per-round decision artifact that says continue, unblock, or close out, move them to `agent-checkpoint`
134
+ - if the user wants cross-round decision memory and not just one checkpoint, move them to `agent-decision-log`
135
+ - if the user wants to understand why multiple rounds kept advancing or getting stuck, move them to `agent-retrospective`
126
136
  - explain the result in PM language, not implementation jargon
127
137
  - if the user sounds new, start with `onboard-url` or `quick-start`
128
138
  - if the user wants another agent to take over, move them to `agent-handoff`
@@ -45,6 +45,26 @@ async function resolveStatusBoard(input, options = {}) {
45
45
  : (parsed.statusBoard.tracker?.blockedReasons || []).join(",")
46
46
  });
47
47
  }
48
+ if (parsed?.kind === "geo-agent-decision-log" && parsed.latestCheckpoint?.statusBoard?.kind === "geo-agent-status-board") {
49
+ return createAgentStatusBoard(
50
+ parsed.latestCheckpoint.statusBoard.source || parsed.latestCheckpoint.source || input,
51
+ {
52
+ format: "json",
53
+ currentTaskId:
54
+ options.currentTaskId ||
55
+ parsed.latestCheckpoint.currentPacket?.id ||
56
+ parsed.latestCheckpoint.statusBoard.tracker?.currentTaskId,
57
+ completedPacketIds:
58
+ options.completedPacketIds != null
59
+ ? options.completedPacketIds
60
+ : (parsed.latestCheckpoint.completedPackets || []).map((packet) => packet.id).join(","),
61
+ blockedReasons:
62
+ options.blockedReasons != null
63
+ ? options.blockedReasons
64
+ : (parsed.latestCheckpoint.blockedItems || []).map((item) => item.title).join(",")
65
+ }
66
+ );
67
+ }
48
68
  } catch {
49
69
  // Fall through to status board generation
50
70
  }
@@ -0,0 +1,368 @@
1
+ import fs from "node:fs/promises";
2
+ import path from "node:path";
3
+ import { createAgentCheckpoint } from "./agent-checkpoint.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-decision-log 格式:${format}。可选值:${Array.from(VALID_FORMATS).join(", ")}`);
12
+ }
13
+ return resolved;
14
+ }
15
+
16
+ async function pathExists(targetPath) {
17
+ try {
18
+ await fs.access(targetPath);
19
+ return true;
20
+ } catch {
21
+ return false;
22
+ }
23
+ }
24
+
25
+ function cloneForFormat(record, format) {
26
+ if (!record || typeof record !== "object") {
27
+ return record;
28
+ }
29
+
30
+ return {
31
+ ...record,
32
+ format
33
+ };
34
+ }
35
+
36
+ function buildEntryId(index) {
37
+ return `decision-${String(index).padStart(2, "0")}`;
38
+ }
39
+
40
+ function buildDecisionNote(options) {
41
+ return options.note || options.decisionNote || null;
42
+ }
43
+
44
+ function buildRecommendedFocus(checkpoint) {
45
+ switch (checkpoint.decision) {
46
+ case "resolve-blockers":
47
+ return `先解除 ${checkpoint.blockedItems.length || 1} 个阻塞项,再继续当前包。`;
48
+ case "move-to-closeout":
49
+ return "进入复盘、交接和交付,不要再扩展新任务。";
50
+ case "continue-current-packet":
51
+ return `继续推进当前包 ${checkpoint.currentPacket?.id || ""},不要切换任务。`.trim();
52
+ case "start-first-packet":
53
+ return `启动第一包 ${checkpoint.currentPacket?.id || ""},建立第一轮执行节奏。`.trim();
54
+ default:
55
+ return "先补执行上下文,再进入下一轮。";
56
+ }
57
+ }
58
+
59
+ function buildLogSummary(entries, checkpoint) {
60
+ if (entries.length === 1) {
61
+ return `目前已有 1 次阶段决策,最新结论是 ${checkpoint.decision}。`;
62
+ }
63
+
64
+ return `目前已累计 ${entries.length} 次阶段决策,最新结论是 ${checkpoint.decision},当前重点是:${buildRecommendedFocus(checkpoint)}`;
65
+ }
66
+
67
+ function buildEntry(checkpoint, note, index) {
68
+ return {
69
+ id: buildEntryId(index),
70
+ createdAt: new Date().toISOString(),
71
+ checkpointType: checkpoint.checkpointType,
72
+ decision: checkpoint.decision,
73
+ boardStatus: checkpoint.boardStatus,
74
+ progressPercent: checkpoint.progressPercent,
75
+ decisionReason: checkpoint.decisionReason,
76
+ currentPacket: checkpoint.currentPacket
77
+ ? {
78
+ id: checkpoint.currentPacket.id,
79
+ title: checkpoint.currentPacket.title,
80
+ owner: checkpoint.currentPacket.owner,
81
+ priority: checkpoint.currentPacket.priority
82
+ }
83
+ : null,
84
+ nextPacket: checkpoint.nextPacket
85
+ ? {
86
+ id: checkpoint.nextPacket.id,
87
+ title: checkpoint.nextPacket.title,
88
+ owner: checkpoint.nextPacket.owner,
89
+ priority: checkpoint.nextPacket.priority
90
+ }
91
+ : null,
92
+ blockedItems: checkpoint.blockedItems.map((item) => ({
93
+ id: item.id,
94
+ title: item.title,
95
+ owner: item.owner,
96
+ priority: item.priority
97
+ })),
98
+ suggestedNextCommand: checkpoint.suggestedNextCommand,
99
+ alternateCommands: checkpoint.alternateCommands,
100
+ handoffNote: checkpoint.handoffNote,
101
+ note: note || null
102
+ };
103
+ }
104
+
105
+ function buildLogPrompt(log) {
106
+ const latest = log.latestCheckpoint;
107
+ const lines = [
108
+ "你现在进入 GEO 决策历史模式。",
109
+ `当前输入:${log.source}`,
110
+ `已累计决策次数:${log.totalEntries}`,
111
+ `最新检查点类型:${latest.checkpointType}`,
112
+ `最新决策:${latest.decision}`,
113
+ `最新决策原因:${latest.decisionReason}`,
114
+ `当前重点:${log.recommendedFocus}`
115
+ ];
116
+
117
+ if (latest.currentPacket) {
118
+ lines.push(`当前包:${latest.currentPacket.id}|${latest.currentPacket.title}`);
119
+ }
120
+ if (latest.nextPacket) {
121
+ lines.push(`下一包:${latest.nextPacket.id}|${latest.nextPacket.title}`);
122
+ }
123
+ if (latest.blockedItems.length > 0) {
124
+ lines.push(`当前阻塞:${latest.blockedItems.map((item) => item.title).join(";")}`);
125
+ }
126
+
127
+ lines.push("请先说明最近几轮是如何决策的,再给出这一轮应该继续、解阻还是收尾。");
128
+ lines.push("最后输出下一步命令、交接说明,以及是否需要追加一条新的决策记录。");
129
+ return lines.join("\n");
130
+ }
131
+
132
+ async function readJsonIfExists(targetPath) {
133
+ if (!(await pathExists(targetPath))) {
134
+ return null;
135
+ }
136
+
137
+ const raw = await fs.readFile(targetPath, "utf8");
138
+ return JSON.parse(raw);
139
+ }
140
+
141
+ async function resolveExistingLog(options = {}) {
142
+ const appendFrom = options.appendFrom || options.appendPath;
143
+ if (!appendFrom) {
144
+ return null;
145
+ }
146
+
147
+ const resolvedPath = path.resolve(appendFrom);
148
+ const parsed = await readJsonIfExists(resolvedPath);
149
+ if (!parsed || parsed.kind !== "geo-agent-decision-log") {
150
+ throw new Error(`append-from 需要指向一个 geo-agent-decision-log JSON 工件:${resolvedPath}`);
151
+ }
152
+
153
+ return {
154
+ path: resolvedPath,
155
+ log: parsed
156
+ };
157
+ }
158
+
159
+ function shouldReuseDecisionLog(parsed, options = {}) {
160
+ return (
161
+ parsed?.kind === "geo-agent-decision-log" &&
162
+ !options.appendFrom &&
163
+ !options.appendPath &&
164
+ !options.currentTaskId &&
165
+ !options.completedPacketIds &&
166
+ !options.blockedReasons &&
167
+ !buildDecisionNote(options)
168
+ );
169
+ }
170
+
171
+ function getCheckpointSourceFromLog(parsed, fallbackInput) {
172
+ return (
173
+ parsed?.latestCheckpoint?.source ||
174
+ parsed?.latestCheckpoint?.statusBoard?.source ||
175
+ parsed?.source ||
176
+ fallbackInput
177
+ );
178
+ }
179
+
180
+ async function resolveCheckpoint(input, options = {}) {
181
+ const resolvedInput = path.resolve(input);
182
+ if (await pathExists(resolvedInput)) {
183
+ try {
184
+ const raw = await fs.readFile(resolvedInput, "utf8");
185
+ const parsed = JSON.parse(raw);
186
+
187
+ if (shouldReuseDecisionLog(parsed, options)) {
188
+ return {
189
+ reuseLog: cloneForFormat(parsed, normalizeFormat(options.format)),
190
+ checkpoint: parsed.latestCheckpoint || null
191
+ };
192
+ }
193
+
194
+ if (parsed?.kind === "geo-agent-decision-log" && parsed.latestCheckpoint?.kind === "geo-agent-checkpoint") {
195
+ const checkpointSource = getCheckpointSourceFromLog(parsed, resolvedInput);
196
+ const checkpoint = await createAgentCheckpoint(checkpointSource, {
197
+ format: "json",
198
+ currentTaskId: options.currentTaskId || parsed.latestCheckpoint.currentPacket?.id,
199
+ completedPacketIds:
200
+ options.completedPacketIds != null
201
+ ? options.completedPacketIds
202
+ : (parsed.latestCheckpoint.completedPackets || []).map((packet) => packet.id).join(","),
203
+ blockedReasons:
204
+ options.blockedReasons != null
205
+ ? options.blockedReasons
206
+ : (parsed.latestCheckpoint.blockedItems || []).map((item) => item.title).join(",")
207
+ });
208
+
209
+ return {
210
+ reuseLog: null,
211
+ checkpoint
212
+ };
213
+ }
214
+ } catch {
215
+ // Fall through to checkpoint generation.
216
+ }
217
+ }
218
+
219
+ return {
220
+ reuseLog: null,
221
+ checkpoint: await createAgentCheckpoint(input, {
222
+ format: "json",
223
+ currentTaskId: options.currentTaskId,
224
+ completedPacketIds: options.completedPacketIds,
225
+ blockedReasons: options.blockedReasons
226
+ })
227
+ };
228
+ }
229
+
230
+ export async function createAgentDecisionLog(input, options = {}) {
231
+ const format = normalizeFormat(options.format);
232
+ const existing = await resolveExistingLog(options);
233
+ const { reuseLog, checkpoint } = await resolveCheckpoint(input, {
234
+ ...options,
235
+ format: "json"
236
+ });
237
+
238
+ if (reuseLog) {
239
+ return reuseLog;
240
+ }
241
+
242
+ if (!checkpoint) {
243
+ throw new Error("无法从当前输入生成 agent checkpoint,因此不能创建 decision log。");
244
+ }
245
+
246
+ const note = buildDecisionNote(options);
247
+ const previousEntries = existing?.log?.entries || [];
248
+ const entries = [
249
+ ...previousEntries,
250
+ buildEntry(checkpoint, note, previousEntries.length + 1)
251
+ ];
252
+
253
+ const log = {
254
+ kind: "geo-agent-decision-log",
255
+ input,
256
+ source: checkpoint.source,
257
+ sourceType: checkpoint.sourceType,
258
+ artifactKind: checkpoint.kind,
259
+ format,
260
+ updatedAt: new Date().toISOString(),
261
+ totalEntries: entries.length,
262
+ latestDecision: checkpoint.decision,
263
+ latestDecisionReason: checkpoint.decisionReason,
264
+ latestCheckpointType: checkpoint.checkpointType,
265
+ latestBoardStatus: checkpoint.boardStatus,
266
+ currentPacket: checkpoint.currentPacket,
267
+ nextPacket: checkpoint.nextPacket,
268
+ openBlockers: checkpoint.blockedItems,
269
+ recommendedFocus: buildRecommendedFocus(checkpoint),
270
+ logSummary: buildLogSummary(entries, checkpoint),
271
+ suggestedNextCommand: checkpoint.suggestedNextCommand,
272
+ alternateCommands: checkpoint.alternateCommands,
273
+ entries,
274
+ latestCheckpoint: checkpoint,
275
+ logPrompt: ""
276
+ };
277
+
278
+ log.logPrompt = buildLogPrompt(log);
279
+ return log;
280
+ }
281
+
282
+ export function renderAgentDecisionLogMarkdown(log) {
283
+ const lines = [
284
+ "# GEO Agent Decision Log",
285
+ "",
286
+ `- 输入:\`${log.source}\``,
287
+ `- 来源类型:\`${log.sourceType}\``,
288
+ `- 最新工件类型:\`${log.artifactKind}\``,
289
+ `- 决策次数:\`${log.totalEntries}\``,
290
+ `- 最新检查点类型:\`${log.latestCheckpointType}\``,
291
+ `- 最新状态:\`${log.latestBoardStatus}\``,
292
+ `- 最新决策:\`${log.latestDecision}\``,
293
+ `- 当前重点:${log.recommendedFocus}`,
294
+ `- 总结:${log.logSummary}`,
295
+ ""
296
+ ];
297
+
298
+ if (log.currentPacket) {
299
+ lines.push("## 当前包", "", `- ${log.currentPacket.id}|${log.currentPacket.title}`);
300
+ lines.push(`- Owner:${log.currentPacket.owner}`);
301
+ lines.push(`- 优先级:${log.currentPacket.priority}`);
302
+ lines.push("");
303
+ }
304
+
305
+ if (log.nextPacket) {
306
+ lines.push("## 下一包", "", `- ${log.nextPacket.id}|${log.nextPacket.title}`);
307
+ lines.push(`- Owner:${log.nextPacket.owner}`);
308
+ lines.push(`- 优先级:${log.nextPacket.priority}`);
309
+ lines.push("");
310
+ }
311
+
312
+ lines.push("## 当前阻塞", "");
313
+ if (log.openBlockers.length === 0) {
314
+ lines.push("- 当前没有阻塞。", "");
315
+ } else {
316
+ for (const blocker of log.openBlockers) {
317
+ lines.push(`- ${blocker.id}|${blocker.title}`);
318
+ lines.push(` - Owner:${blocker.owner}`);
319
+ lines.push(` - 优先级:${blocker.priority}`);
320
+ }
321
+ lines.push("");
322
+ }
323
+
324
+ lines.push("## 决策时间线", "");
325
+ for (const entry of [...log.entries].reverse()) {
326
+ lines.push(`### ${entry.id}`);
327
+ lines.push("");
328
+ lines.push(`- 时间:\`${entry.createdAt}\``);
329
+ lines.push(`- 检查点类型:\`${entry.checkpointType}\``);
330
+ lines.push(`- 决策:\`${entry.decision}\``);
331
+ lines.push(`- 状态:\`${entry.boardStatus}\``);
332
+ lines.push(`- 进度:\`${entry.progressPercent}%\``);
333
+ lines.push(`- 原因:${entry.decisionReason}`);
334
+ if (entry.currentPacket) {
335
+ lines.push(`- 当前包:${entry.currentPacket.id}|${entry.currentPacket.title}`);
336
+ }
337
+ if (entry.nextPacket) {
338
+ lines.push(`- 下一包:${entry.nextPacket.id}|${entry.nextPacket.title}`);
339
+ }
340
+ if (entry.blockedItems.length > 0) {
341
+ lines.push(`- 阻塞:${entry.blockedItems.map((item) => item.title).join(";")}`);
342
+ }
343
+ if (entry.note) {
344
+ lines.push(`- 备注:${entry.note}`);
345
+ }
346
+ lines.push(`- 建议命令:\`${entry.suggestedNextCommand}\``);
347
+ lines.push(`- 交接说明:${entry.handoffNote}`);
348
+ lines.push("");
349
+ }
350
+
351
+ lines.push("## 建议下一步命令", "", `- \`${log.suggestedNextCommand}\``);
352
+
353
+ if (log.alternateCommands.length > 0) {
354
+ lines.push("", "## 备选命令", "");
355
+ for (const command of log.alternateCommands) {
356
+ lines.push(`- \`${command}\``);
357
+ }
358
+ }
359
+
360
+ lines.push("", "## 最新检查点", "", "```text", log.latestCheckpoint.checkpointPrompt, "```");
361
+ lines.push("", "## 可直接复制给 Agent 的 Decision Log Prompt", "", "```text", log.logPrompt, "```");
362
+
363
+ return `${lines.join("\n")}\n`;
364
+ }
365
+
366
+ export async function writeAgentDecisionLogOutput(outputPath, content) {
367
+ return writeScanOutput(outputPath, content);
368
+ }