geo-ai-search-optimization 1.1.8 → 1.2.0

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
@@ -147,6 +147,48 @@ geo-ai-search-optimization agent-handoff https://example.com
147
147
  - 让 agent 先基于当前工件给出结构化建议
148
148
  - 让 PM 把 GEO 问题变成交接包,而不是临时口头说明
149
149
 
150
+ ## Apply Plan 命令
151
+
152
+ 如果你希望 agent 不只是“接手”,而是直接进入执行闭环,现在可以用 `apply-plan`:
153
+
154
+ ```bash
155
+ geo-ai-search-optimization apply-plan ./your-site
156
+ geo-ai-search-optimization apply-plan ./your-site --task fix-01 --format json --out ./reports/apply-plan.json
157
+ geo-ai-search-optimization apply-plan https://example.com
158
+ ```
159
+
160
+ `apply-plan` 会输出:
161
+
162
+ - 本轮应该先执行哪些任务
163
+ - 每个任务先检查什么
164
+ - 建议怎么改
165
+ - 该跑哪些验证命令
166
+ - 完成后怎么向用户回报
167
+
168
+ 这适合:
169
+
170
+ - 让 agent 直接开始修复
171
+ - 让 agent 在每个任务完成后有一致的回报格式
172
+ - 把 GEO workflow 从“分析”推进到“执行 + 验证 + 回报”
173
+
174
+ ## Completion Report 命令
175
+
176
+ 如果你希望 agent 在完成一轮修复或建议后,自动产出复盘、剩余风险和下一轮任务,现在可以用 `completion-report`:
177
+
178
+ ```bash
179
+ geo-ai-search-optimization completion-report ./your-site
180
+ geo-ai-search-optimization completion-report ./reports/apply-plan.json --format json --out ./reports/completion-report.json
181
+ geo-ai-search-optimization completion-report https://example.com
182
+ ```
183
+
184
+ `completion-report` 会输出:
185
+
186
+ - 本轮已完成什么
187
+ - 当前还剩哪些风险
188
+ - 下一轮建议先做哪些任务
189
+ - 建议复测命令
190
+ - 可直接复用的完成回报模板
191
+
150
192
  ## Fix Plan 命令
151
193
 
152
194
  如果你已经跑过 `audit`、`report` 或 `onboard-url`,下一步就可以直接把结果转成 PM 待办清单:
@@ -298,6 +340,8 @@ geo-ai-search-optimization init-schema organization ./site --site-url "https://e
298
340
  geo-ai-search-optimization init-schema faq-page ./site --json
299
341
  geo-ai-search-optimization audit ./my-site
300
342
  geo-ai-search-optimization agent-handoff ./my-site
343
+ geo-ai-search-optimization apply-plan ./my-site
344
+ geo-ai-search-optimization completion-report ./my-site
301
345
  geo-ai-search-optimization exec-summary ./my-site
302
346
  geo-ai-search-optimization fix-plan ./my-site
303
347
  geo-ai-search-optimization owner-board ./my-site
@@ -313,6 +357,20 @@ geo-ai-search-optimization version
313
357
  geo-ai-search-optimization help
314
358
  ```
315
359
 
360
+ ## New in 1.2.0
361
+
362
+ - 新增 `completion-report`
363
+ - 让 agent 在完成一轮 GEO 修复或建议后自动产出 closeout report
364
+ - 会输出本轮完成项、剩余风险、下一轮任务、复测命令和完成回报模板
365
+ - 新增 `geo-ai-search-optimization-completion-report` skill,帮助 agent 稳定产出复盘
366
+
367
+ ## New in 1.1.9
368
+
369
+ - 新增 `apply-plan`
370
+ - 让 agent-handoff 继续进入执行闭环,而不是停在交接阶段
371
+ - 会输出任务执行包、验证指令、Done 定义、完成回报模板
372
+ - 新增 `geo-ai-search-optimization-repair-loop` skill,帮助 agent 继续做修复、验证和回报
373
+
316
374
  ## New in 1.1.8
317
375
 
318
376
  - 新增 `agent-handoff`
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "geo-ai-search-optimization",
3
- "version": "1.1.8",
3
+ "version": "1.2.0",
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": {
@@ -43,6 +43,8 @@ geo-ai-search-optimization report ./your-site --mode audit --format html --out .
43
43
  geo-ai-search-optimization exec-summary ./your-site --out ./reports/exec-summary.md
44
44
  geo-ai-search-optimization fix-plan ./your-site --format json --out ./reports/fix-plan.json
45
45
  geo-ai-search-optimization agent-handoff ./your-site --out ./reports/agent-handoff.md
46
+ geo-ai-search-optimization apply-plan ./your-site --out ./reports/apply-plan.md
47
+ geo-ai-search-optimization completion-report ./your-site --out ./reports/completion-report.md
46
48
  geo-ai-search-optimization owner-board ./your-site --out ./reports/owner-board.md
47
49
  geo-ai-search-optimization meeting-pack ./your-site --out ./reports/meeting-pack.md
48
50
  geo-ai-search-optimization pm-brief ./your-site --out ./reports/pm-brief.md
@@ -57,7 +59,9 @@ geo-ai-search-optimization roadmap ./your-site --out ./reports/roadmap.md
57
59
  2. 再补 llms.txt / schema / author / freshness
58
60
  3. 再做 FAQ / comparison / methodology / evidence 页面
59
61
  4. 需要交接给 agent 时,生成 agent-handoff 工件
60
- 5. 重跑 scan / audit / report 验证是否改善
62
+ 5. 需要 agent 直接开始做时,生成 apply-plan
63
+ 6. 完成一轮后生成 completion-report
64
+ 7. 重跑 scan / audit / report 验证是否改善
61
65
 
62
66
  ## 推荐节奏
63
67
 
@@ -0,0 +1,43 @@
1
+ ---
2
+ name: geo-ai-search-optimization-completion-report
3
+ description: Create GEO completion reports after an execution round. Use when an agent has already produced fixes or recommendations and needs to summarize what was completed, what risks remain, what should happen next, and how to report the outcome back to PM or stakeholders.
4
+ ---
5
+
6
+ # GEO Completion Report
7
+
8
+ Use this skill when GEO work is no longer at the planning stage and the user needs a closeout report for the round.
9
+
10
+ `GEO = Generative Engine Optimization`
11
+
12
+ ## What this skill is for
13
+
14
+ Create a clear post-execution report that answers:
15
+
16
+ 1. what got done this round
17
+ 2. what still remains risky
18
+ 3. what should happen in the next round
19
+ 4. what validation should be rerun
20
+ 5. how the agent should report completion back
21
+
22
+ ## Preferred input order
23
+
24
+ Prefer these artifacts in order:
25
+
26
+ 1. `completion-report`
27
+ 2. `apply-plan`
28
+ 3. `agent-handoff`
29
+ 4. `fix-plan` or `report`
30
+
31
+ If only a repo path or URL is available, derive the report from the latest GEO execution context.
32
+
33
+ ## Response order
34
+
35
+ Always return:
36
+
37
+ 1. round summary
38
+ 2. completed this round
39
+ 3. residual risks
40
+ 4. next-round tasks
41
+ 5. validation commands
42
+
43
+ Read [references/completion-patterns.md](references/completion-patterns.md) when you need concrete closeout patterns.
@@ -0,0 +1,4 @@
1
+ interface:
2
+ display_name: "GEO Completion Report"
3
+ short_description: "Summarize what was completed, what remains, and what comes next"
4
+ default_prompt: "Use $geo-ai-search-optimization-completion-report to produce a closeout report for this GEO execution round."
@@ -0,0 +1,25 @@
1
+ # GEO Completion Patterns
2
+
3
+ ## Good completion report shape
4
+
5
+ 1. round summary
6
+ 2. completed this round
7
+ 3. residual risks
8
+ 4. next-round tasks
9
+ 5. validation rerun commands
10
+
11
+ ## If the round was direct implementation
12
+
13
+ Emphasize:
14
+
15
+ - what changed
16
+ - what was verified
17
+ - what still needs monitoring
18
+
19
+ ## If the round was recommendation only
20
+
21
+ Emphasize:
22
+
23
+ - what was clarified
24
+ - what context is still missing
25
+ - what must happen before implementation can start
@@ -0,0 +1,40 @@
1
+ ---
2
+ name: geo-ai-search-optimization-repair-loop
3
+ description: Execute GEO repair work from apply-plan and agent-handoff artifacts. Use when an agent should move past recommendations and actually perform a fix, validate the result, and report completion in a PM-readable format.
4
+ ---
5
+
6
+ # GEO Repair Loop
7
+
8
+ Use this skill when a GEO artifact is no longer just for reading or planning, and the user expects the agent to continue through execution, validation, and completion reporting.
9
+
10
+ `GEO = Generative Engine Optimization`
11
+
12
+ ## Preferred flow
13
+
14
+ When possible, use this order:
15
+
16
+ 1. read `agent-handoff` to understand the task and mode
17
+ 2. read `apply-plan` to get the execution packet
18
+ 3. perform the fix or produce the next-best structured recommendation
19
+ 4. rerun the suggested GEO CLI validation commands
20
+ 5. generate `completion-report`
21
+ 6. report back using the completion template
22
+
23
+ ## Execution rules
24
+
25
+ - if the artifact says `implementation-ready`, prefer direct code changes over advice-only text
26
+ - if the artifact says `advice-only`, do not pretend code was changed
27
+ - if the artifact says `artifact-guided`, explain what additional repo or page context is needed before direct implementation
28
+ - keep outputs PM-readable while still naming the specific files, templates, or systems you inspected
29
+
30
+ ## Completion reporting
31
+
32
+ Always include:
33
+
34
+ 1. what you checked
35
+ 2. what you changed or recommended
36
+ 3. what validation you ran
37
+ 4. whether the task can be marked done
38
+ 5. what remains next
39
+
40
+ Read [references/repair-loop-patterns.md](references/repair-loop-patterns.md) when you need concrete execution patterns.
@@ -0,0 +1,4 @@
1
+ interface:
2
+ display_name: "GEO Repair Loop"
3
+ short_description: "Execute GEO fixes from apply-plan and validate completion"
4
+ default_prompt: "Use $geo-ai-search-optimization-repair-loop to continue from this apply-plan or handoff artifact and complete the next GEO repair task."
@@ -0,0 +1,33 @@
1
+ # GEO Repair Loop Patterns
2
+
3
+ ## If you have a local project directory
4
+
5
+ Do:
6
+
7
+ - inspect reusable templates, metadata helpers, schema generators, and content models first
8
+ - fix the shared layer before patching individual pages where possible
9
+ - rerun the validation commands from `apply-plan`
10
+ - report completion using the provided completion template
11
+
12
+ ## If you only have a public URL
13
+
14
+ Do:
15
+
16
+ - give structured recommendations
17
+ - identify the smallest set of pages or templates the user should share next
18
+ - keep the output close to implementation, but do not claim code changes were made
19
+
20
+ ## If you have only exported artifacts
21
+
22
+ Do:
23
+
24
+ - use them to determine order, dependencies, and done criteria
25
+ - state clearly when repo access is required for the next step
26
+
27
+ ## Good completion report shape
28
+
29
+ 1. checked
30
+ 2. changed or recommended
31
+ 3. validation run
32
+ 4. done or not done
33
+ 5. next step
@@ -13,14 +13,16 @@ 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 six layers:
16
+ The package is best explained as eight layers:
17
17
 
18
18
  1. `onboard-url` / `onboard`: first look
19
19
  2. `scan`: raw signal check
20
20
  3. `audit` / `report`: diagnosis
21
21
  4. `fix-plan` / `owner-board`: execution planning
22
22
  5. `agent-handoff`: agent takeover package
23
- 6. `pm-brief` / `roadmap`: stakeholder alignment
23
+ 6. `apply-plan`: execution loop
24
+ 7. `completion-report`: closeout
25
+ 8. `pm-brief` / `roadmap`: stakeholder alignment
24
26
 
25
27
  ## Recommended command order
26
28
 
@@ -39,6 +41,8 @@ npx geo-ai-search-optimization scan ./your-site
39
41
  npx geo-ai-search-optimization audit ./your-site
40
42
  npx geo-ai-search-optimization fix-plan ./your-site
41
43
  npx geo-ai-search-optimization agent-handoff ./your-site
44
+ npx geo-ai-search-optimization apply-plan ./your-site
45
+ npx geo-ai-search-optimization completion-report ./your-site
42
46
  npx geo-ai-search-optimization owner-board ./your-site
43
47
  npx geo-ai-search-optimization roadmap ./your-site
44
48
  ```
@@ -52,6 +56,8 @@ npx geo-ai-search-optimization roadmap ./your-site
52
56
  - `report`: export audit/onboarding/scan into readable files
53
57
  - `fix-plan`: turn issues into backlog-ready tasks
54
58
  - `agent-handoff`: turn GEO findings into an artifact an agent can directly continue from
59
+ - `apply-plan`: turn handoff into execution packets with validation commands and completion template
60
+ - `completion-report`: summarize what the agent finished, residual risks, and next-round work
55
61
  - `owner-board`: group tasks by PM / engineering / SEO / content
56
62
  - `pm-brief`: prepare a PM summary for meetings and prioritization
57
63
  - `roadmap`: turn tasks into a 2-week / 4-week execution sequence
@@ -64,6 +70,8 @@ When explaining the tool to a user:
64
70
  - explain the result in PM language, not implementation jargon
65
71
  - if the user sounds new, start with `onboard-url` or `quick-start`
66
72
  - if the user wants another agent to take over, move them to `agent-handoff`
73
+ - if the user wants the next agent to start executing, move them to `apply-plan`
74
+ - if the user wants a closeout summary after one round, move them to `completion-report`
67
75
  - if the user already has a report, move them to `fix-plan`, `owner-board`, or `roadmap`
68
76
 
69
77
  Read [references/usage-patterns.md](references/usage-patterns.md) when you need response patterns and command selection examples.
@@ -59,6 +59,34 @@ If they only have a URL:
59
59
  npx geo-ai-search-optimization agent-handoff https://example.com
60
60
  ```
61
61
 
62
+ ## If the user says "我想让 agent 直接开始执行"
63
+
64
+ Recommend:
65
+
66
+ ```bash
67
+ npx geo-ai-search-optimization apply-plan ./your-site
68
+ ```
69
+
70
+ If they only have a URL:
71
+
72
+ ```bash
73
+ npx geo-ai-search-optimization apply-plan https://example.com
74
+ ```
75
+
76
+ ## If the user says "我想看这一轮做完后的复盘"
77
+
78
+ Recommend:
79
+
80
+ ```bash
81
+ npx geo-ai-search-optimization completion-report ./your-site
82
+ ```
83
+
84
+ If they only have a URL:
85
+
86
+ ```bash
87
+ npx geo-ai-search-optimization completion-report https://example.com
88
+ ```
89
+
62
90
  ## If the user says "我要开会同步"
63
91
 
64
92
  Recommend:
@@ -0,0 +1,303 @@
1
+ import { createAgentHandoff, writeAgentHandoffOutput } from "./agent-handoff.js";
2
+
3
+ const VALID_FORMATS = new Set(["markdown", "json"]);
4
+
5
+ function normalizeFormat(format) {
6
+ const resolved = (format || "markdown").toLowerCase();
7
+ if (!VALID_FORMATS.has(resolved)) {
8
+ throw new Error(`不支持的 apply-plan 格式:${format}。可选值:${Array.from(VALID_FORMATS).join(", ")}`);
9
+ }
10
+ return resolved;
11
+ }
12
+
13
+ function slugifyTaskTitle(value) {
14
+ return value
15
+ .toLowerCase()
16
+ .replace(/[^a-z0-9\u4e00-\u9fff]+/gi, "-")
17
+ .replace(/^-+|-+$/g, "");
18
+ }
19
+
20
+ function inferExecutionType(mode) {
21
+ if (mode === "implementation-ready") {
22
+ return "direct-fix";
23
+ }
24
+ if (mode === "advice-only") {
25
+ return "recommendation-only";
26
+ }
27
+ return "guided-planning";
28
+ }
29
+
30
+ function buildInspectTargets(task, mode) {
31
+ const targets = [];
32
+
33
+ if (mode === "implementation-ready") {
34
+ targets.push("站点全局 layout、SEO metadata helper、head 输出逻辑。");
35
+ targets.push("会影响该问题区域的可复用页面模板,而不是只看单页。");
36
+ } else {
37
+ targets.push("首页与最关键的 1 到 3 个落地页。");
38
+ targets.push("能体现该问题的模板类型、导航层级或内容模块。");
39
+ }
40
+
41
+ if (task.title.includes("robots.txt")) {
42
+ targets.push("robots.txt 生成逻辑、静态文件目录或部署输出目录。");
43
+ }
44
+ if (task.title.includes("sitemap")) {
45
+ targets.push("sitemap.xml 生成逻辑、路由清单或内容集合。");
46
+ }
47
+ if (task.title.includes("canonical")) {
48
+ targets.push("canonical 计算逻辑、metadata 映射层、URL 归一策略。");
49
+ }
50
+ if (task.title.includes("llms.txt")) {
51
+ targets.push("llms.txt 文件、站点导航概览、核心页面入口。");
52
+ }
53
+ if (task.title.includes("JSON-LD") || task.title.includes("schema")) {
54
+ targets.push("JSON-LD 输出逻辑、结构化数据模板、页面可见内容是否匹配。");
55
+ }
56
+ if (task.title.includes("meta description") || task.title.includes("标题")) {
57
+ targets.push("title / meta description 模板、字段来源、默认值与覆盖逻辑。");
58
+ }
59
+ if (task.title.includes("breadcrumb")) {
60
+ targets.push("站点层级、breadcrumb 组件或 breadcrumb schema 输出点。");
61
+ }
62
+
63
+ return Array.from(new Set(targets));
64
+ }
65
+
66
+ function buildEditPlan(task, mode) {
67
+ const steps = [];
68
+
69
+ if (mode === "implementation-ready") {
70
+ steps.push("先定位复用层,再判断是否能一次修复整类页面。");
71
+ steps.push("优先改模板、helper、内容模型或生成逻辑,避免只 patch 单页。");
72
+ } else if (mode === "advice-only") {
73
+ steps.push("先输出页面层、模板层、流程层三层建议。");
74
+ steps.push("把需要工程、SEO、内容团队补充的前置条件写清楚。");
75
+ } else {
76
+ steps.push("先把任务拆成可执行顺序,再明确哪一步需要代码仓库。");
77
+ steps.push("把实施前必须补齐的上下文列出来。");
78
+ }
79
+
80
+ steps.push(`围绕任务「${task.title}」设计最小可落地改动,不要把范围扩散到整站重构。`);
81
+ steps.push("完成后一定要回到验收标准,确认问题真的被修掉,而不是只做表面调整。");
82
+
83
+ return steps;
84
+ }
85
+
86
+ function buildValidationCommands(source, mode) {
87
+ if (mode === "implementation-ready") {
88
+ return [
89
+ `geo-ai-search-optimization audit ${source}`,
90
+ `geo-ai-search-optimization fix-plan ${source}`,
91
+ `geo-ai-search-optimization agent-handoff ${source}`
92
+ ];
93
+ }
94
+
95
+ if (mode === "advice-only") {
96
+ return [
97
+ `geo-ai-search-optimization onboard-url ${source}`,
98
+ `geo-ai-search-optimization pm-brief ${source}`,
99
+ `geo-ai-search-optimization report ${source} --mode onboarding --format markdown`
100
+ ];
101
+ }
102
+
103
+ return [
104
+ `geo-ai-search-optimization agent-handoff ${source}`,
105
+ `geo-ai-search-optimization meeting-pack ${source}`
106
+ ];
107
+ }
108
+
109
+ function buildDeliverables(task, executionType) {
110
+ const deliverables = [
111
+ "当前问题摘要",
112
+ "你检查过的页面、模板或文件",
113
+ "实际修改或建议动作",
114
+ "验收结果",
115
+ "下一步建议"
116
+ ];
117
+
118
+ if (executionType === "direct-fix") {
119
+ deliverables.push("修改后的验证结果和剩余风险");
120
+ } else if (executionType === "recommendation-only") {
121
+ deliverables.push("用户还需要提供的仓库、页面或权限");
122
+ } else {
123
+ deliverables.push("进入直接修复前的补充上下文清单");
124
+ }
125
+
126
+ return deliverables;
127
+ }
128
+
129
+ function buildCompletionTemplate(task, executionType) {
130
+ const closingLine =
131
+ executionType === "direct-fix"
132
+ ? "我已经完成可直接落地的部分,并附上复测结果。"
133
+ : executionType === "recommendation-only"
134
+ ? "我已经给出建议路径,下一步需要用户提供更多上下文后再继续落地。"
135
+ : "我已经把后续执行顺序整理好,待补齐仓库或页面上下文后可进入直接实施。";
136
+
137
+ return [
138
+ `任务:${task.title}`,
139
+ `结论:${closingLine}`,
140
+ "我先做了什么:",
141
+ "- ",
142
+ "我检查了哪些页面 / 文件 / 模板:",
143
+ "- ",
144
+ "我实际修改或建议了什么:",
145
+ "- ",
146
+ "验收结果:",
147
+ "- ",
148
+ "剩余风险或下一步:",
149
+ "- "
150
+ ].join("\n");
151
+ }
152
+
153
+ function buildExecutionPrompt(task, handoff, executionType, source) {
154
+ return [
155
+ "你现在进入 GEO 修复执行阶段。",
156
+ `任务:${task.title}`,
157
+ `执行类型:${executionType}`,
158
+ `输入来源:${source}`,
159
+ `本轮目标:${handoff.primaryObjective}`,
160
+ "请你按以下顺序输出:",
161
+ "1. 先检查什么",
162
+ "2. 你准备如何执行",
163
+ "3. 你会如何验证",
164
+ "4. 完成后如何向用户回报",
165
+ "",
166
+ "必须遵守:",
167
+ executionType === "direct-fix"
168
+ ? "如果已经有仓库访问权限,优先直接修复。"
169
+ : "如果没有仓库访问权限,不要假装已经完成代码修改。",
170
+ "输出要保持 PM 可读,同时明确技术执行动作。"
171
+ ].join("\n");
172
+ }
173
+
174
+ function buildGlobalWorkflow(mode) {
175
+ if (mode === "implementation-ready") {
176
+ return [
177
+ "先找复用模板和 metadata / schema 输出点。",
178
+ "按优先级修复 1 到 3 个最高价值任务。",
179
+ "每完成一轮修改就复跑 GEO CLI 验证。"
180
+ ];
181
+ }
182
+ if (mode === "advice-only") {
183
+ return [
184
+ "先用当前网址确认最高优先级问题。",
185
+ "给出结构化建议,并说明需要哪些工程上下文。",
186
+ "拿到仓库后再切换到直接修复。"
187
+ ];
188
+ }
189
+ return [
190
+ "先用工件整理顺序和依赖关系。",
191
+ "确认哪些任务可先给建议,哪些任务必须等仓库。",
192
+ "当用户补充仓库后,转成 implementation-ready 再执行。"
193
+ ];
194
+ }
195
+
196
+ export async function createApplyPlan(input, options = {}) {
197
+ const format = normalizeFormat(options.format);
198
+ const handoff = await createAgentHandoff(input, { format: "json" });
199
+ const executionType = inferExecutionType(handoff.executionMode);
200
+ const requestedTaskId = options.taskId || null;
201
+ const selectedTasks = requestedTaskId
202
+ ? handoff.tasks.filter((task) => task.id === requestedTaskId)
203
+ : handoff.tasks.slice(0, 3);
204
+
205
+ if (selectedTasks.length === 0) {
206
+ throw new Error(`找不到任务:${requestedTaskId}`);
207
+ }
208
+
209
+ const executionPackets = selectedTasks.map((task) => ({
210
+ id: task.id,
211
+ title: task.title,
212
+ packetSlug: `${task.id}-${slugifyTaskTitle(task.title)}`,
213
+ priority: task.priority,
214
+ owner: task.owner,
215
+ executionType,
216
+ inspectTargets: buildInspectTargets(task, handoff.executionMode),
217
+ editPlan: buildEditPlan(task, handoff.executionMode),
218
+ validationCommands: buildValidationCommands(handoff.source, handoff.executionMode),
219
+ deliverables: buildDeliverables(task, executionType),
220
+ doneWhen: task.acceptanceCriteria,
221
+ completionTemplate: buildCompletionTemplate(task, executionType),
222
+ executionPrompt: buildExecutionPrompt(task, handoff, executionType, handoff.source)
223
+ }));
224
+
225
+ return {
226
+ kind: "geo-apply-plan",
227
+ source: handoff.source,
228
+ sourceType: handoff.sourceType,
229
+ format,
230
+ executionMode: handoff.executionMode,
231
+ executionType,
232
+ summary: handoff.summary,
233
+ primaryObjective: handoff.primaryObjective,
234
+ workflow: buildGlobalWorkflow(handoff.executionMode),
235
+ selectedTaskCount: executionPackets.length,
236
+ packets: executionPackets
237
+ };
238
+ }
239
+
240
+ export function renderApplyPlanMarkdown(plan) {
241
+ const lines = [
242
+ "# GEO Apply Plan",
243
+ "",
244
+ `- 输入来源:\`${plan.source}\``,
245
+ `- 来源类型:\`${plan.sourceType}\``,
246
+ `- 执行模式:\`${plan.executionMode}\``,
247
+ `- 执行类型:\`${plan.executionType}\``,
248
+ `- 当前总结:${plan.summary}`,
249
+ `- 本轮目标:${plan.primaryObjective}`,
250
+ "",
251
+ "## 全局执行顺序",
252
+ ""
253
+ ];
254
+
255
+ for (const step of plan.workflow) {
256
+ lines.push(`- ${step}`);
257
+ }
258
+
259
+ lines.push("", "## 执行包", "");
260
+
261
+ for (const packet of plan.packets) {
262
+ lines.push(`### ${packet.id}|${packet.title}`);
263
+ lines.push("");
264
+ lines.push(`- 负责人:${packet.owner}`);
265
+ lines.push(`- 执行类型:${packet.executionType}`);
266
+ lines.push(`- 包标识:\`${packet.packetSlug}\``);
267
+ lines.push("- 先检查:");
268
+ for (const target of packet.inspectTargets) {
269
+ lines.push(` - ${target}`);
270
+ }
271
+ lines.push("- 执行步骤:");
272
+ for (const step of packet.editPlan) {
273
+ lines.push(` - ${step}`);
274
+ }
275
+ lines.push("- 验证指令:");
276
+ for (const command of packet.validationCommands) {
277
+ lines.push(` - \`${command}\``);
278
+ }
279
+ lines.push("- 交付物:");
280
+ for (const deliverable of packet.deliverables) {
281
+ lines.push(` - ${deliverable}`);
282
+ }
283
+ lines.push("- Done 定义:");
284
+ for (const criterion of packet.doneWhen) {
285
+ lines.push(` - ${criterion}`);
286
+ }
287
+ lines.push("- 执行提示词:");
288
+ lines.push("```text");
289
+ lines.push(packet.executionPrompt);
290
+ lines.push("```");
291
+ lines.push("- 完成回报模板:");
292
+ lines.push("```text");
293
+ lines.push(packet.completionTemplate);
294
+ lines.push("```");
295
+ lines.push("");
296
+ }
297
+
298
+ return `${lines.join("\n")}\n`;
299
+ }
300
+
301
+ export async function writeApplyPlanOutput(outputPath, content) {
302
+ return writeAgentHandoffOutput(outputPath, content);
303
+ }
package/src/cli.js CHANGED
@@ -1,7 +1,13 @@
1
1
  import { fileURLToPath } from "node:url";
2
2
  import { readFile } from "node:fs/promises";
3
3
  import path from "node:path";
4
+ import { createApplyPlan, renderApplyPlanMarkdown, writeApplyPlanOutput } from "./apply-plan.js";
4
5
  import { createAgentHandoff, renderAgentHandoffMarkdown, writeAgentHandoffOutput } from "./agent-handoff.js";
6
+ import {
7
+ createCompletionReport,
8
+ renderCompletionReportMarkdown,
9
+ writeCompletionReportOutput
10
+ } from "./completion-report.js";
5
11
  import { installSkill } from "./install-skill.js";
6
12
  import { getBundledSkillPath, getInstalledSkillPath, getSkillName, getSkillsDir } from "./paths.js";
7
13
  import { renderScanMarkdown, scanProject, writeScanOutput } from "./scan.js";
@@ -54,6 +60,8 @@ function printHelp() {
54
60
  " geo-ai-search-optimization init-schema <type> [target-dir] [--site-url <url>] [--overwrite] [--json]",
55
61
  " geo-ai-search-optimization audit <project-path> [--json] [--out <file>] [--max-file-size <bytes>] [--max-examples <count>]",
56
62
  " geo-ai-search-optimization agent-handoff <input> [--format <markdown|json>] [--out <file>]",
63
+ " geo-ai-search-optimization apply-plan <input> [--task <id>] [--format <markdown|json>] [--out <file>]",
64
+ " geo-ai-search-optimization completion-report <input> [--format <markdown|json>] [--out <file>]",
57
65
  " geo-ai-search-optimization exec-summary <input> [--format <markdown|json>] [--out <file>]",
58
66
  " geo-ai-search-optimization fix-plan <input> [--format <markdown|json>] [--out <file>]",
59
67
  " geo-ai-search-optimization owner-board <input> [--format <markdown|json>] [--out <file>]",
@@ -360,6 +368,55 @@ async function handleAgentHandoff(args) {
360
368
  process.stdout.write(renderedOutput);
361
369
  }
362
370
 
371
+ async function handleApplyPlan(args) {
372
+ const input = args.find((value) => !value.startsWith("-"));
373
+ if (!input) {
374
+ throw new Error("apply-plan 需要一个输入值,可以是项目路径、网站网址或已导出的 JSON 报告");
375
+ }
376
+
377
+ const format = getFlagValue(args, "--format");
378
+ const plan = await createApplyPlan(input, {
379
+ format,
380
+ taskId: getFlagValue(args, "--task")
381
+ });
382
+ const outputJson = format === "json";
383
+ const renderedOutput = outputJson
384
+ ? `${JSON.stringify(plan, null, 2)}\n`
385
+ : renderApplyPlanMarkdown(plan);
386
+
387
+ const outputPath = getFlagValue(args, "--out");
388
+ if (outputPath) {
389
+ const resolvedOutputPath = await writeApplyPlanOutput(outputPath, renderedOutput);
390
+ process.stdout.write(`已保存 apply plan:${resolvedOutputPath}\n`);
391
+ return;
392
+ }
393
+
394
+ process.stdout.write(renderedOutput);
395
+ }
396
+
397
+ async function handleCompletionReport(args) {
398
+ const input = args.find((value) => !value.startsWith("-"));
399
+ if (!input) {
400
+ throw new Error("completion-report 需要一个输入值,可以是项目路径、网站网址或已导出的 JSON 工件");
401
+ }
402
+
403
+ const format = getFlagValue(args, "--format");
404
+ const report = await createCompletionReport(input, { format });
405
+ const outputJson = format === "json";
406
+ const renderedOutput = outputJson
407
+ ? `${JSON.stringify(report, null, 2)}\n`
408
+ : renderCompletionReportMarkdown(report);
409
+
410
+ const outputPath = getFlagValue(args, "--out");
411
+ if (outputPath) {
412
+ const resolvedOutputPath = await writeCompletionReportOutput(outputPath, renderedOutput);
413
+ process.stdout.write(`已保存 completion report:${resolvedOutputPath}\n`);
414
+ return;
415
+ }
416
+
417
+ process.stdout.write(renderedOutput);
418
+ }
419
+
363
420
  async function handleExecSummary(args) {
364
421
  const input = args.find((value) => !value.startsWith("-"));
365
422
  if (!input) {
@@ -538,6 +595,16 @@ export async function runCli(args = []) {
538
595
  return;
539
596
  }
540
597
 
598
+ if (command === "apply-plan") {
599
+ await handleApplyPlan(rest);
600
+ return;
601
+ }
602
+
603
+ if (command === "completion-report") {
604
+ await handleCompletionReport(rest);
605
+ return;
606
+ }
607
+
541
608
  if (command === "exec-summary") {
542
609
  await handleExecSummary(rest);
543
610
  return;
@@ -0,0 +1,220 @@
1
+ import fs from "node:fs/promises";
2
+ import path from "node:path";
3
+ import { createApplyPlan, writeApplyPlanOutput } from "./apply-plan.js";
4
+
5
+ const VALID_FORMATS = new Set(["markdown", "json"]);
6
+
7
+ function normalizeFormat(format) {
8
+ const resolved = (format || "markdown").toLowerCase();
9
+ if (!VALID_FORMATS.has(resolved)) {
10
+ throw new Error(`不支持的 completion-report 格式:${format}。可选值:${Array.from(VALID_FORMATS).join(", ")}`);
11
+ }
12
+ return resolved;
13
+ }
14
+
15
+ async function pathExists(targetPath) {
16
+ try {
17
+ await fs.access(targetPath);
18
+ return true;
19
+ } catch {
20
+ return false;
21
+ }
22
+ }
23
+
24
+ function isUrlInput(input) {
25
+ return /^https?:\/\//i.test(input);
26
+ }
27
+
28
+ function inferProgressStatus(plan) {
29
+ if (plan.executionType === "direct-fix") {
30
+ return "已进入执行与验证阶段";
31
+ }
32
+ if (plan.executionType === "recommendation-only") {
33
+ return "已完成建议输出,待补仓库上下文";
34
+ }
35
+ return "已完成计划整理,待进入直接实施";
36
+ }
37
+
38
+ function buildCompletedThisRound(plan) {
39
+ if (plan.executionType === "direct-fix") {
40
+ return [
41
+ "已整理本轮最优先执行的任务包。",
42
+ "已明确每个任务先检查哪些模板、页面或系统。",
43
+ "已为每个任务准备验证命令与完成回报模板。"
44
+ ];
45
+ }
46
+ if (plan.executionType === "recommendation-only") {
47
+ return [
48
+ "已确认当前网址级问题与首批建议任务。",
49
+ "已准备好建议型执行包,避免在没有仓库时误报已修复。",
50
+ "已整理下一步需要用户提供的仓库或页面上下文。"
51
+ ];
52
+ }
53
+ return [
54
+ "已把现有工件整理成执行顺序。",
55
+ "已明确哪些任务可先建议,哪些需要仓库后再落地。",
56
+ "已为下一阶段实施准备验证与回报结构。"
57
+ ];
58
+ }
59
+
60
+ function buildResidualRisks(plan) {
61
+ if (plan.executionType === "direct-fix") {
62
+ return [
63
+ "如果只修单页、不修模板,问题可能会在其他页面重复出现。",
64
+ "如果修完后没有重跑 GEO CLI,团队无法确认问题是否真正下降。",
65
+ "如果结构化数据与页面可见内容不一致,可能造成新的理解偏差。"
66
+ ];
67
+ }
68
+ if (plan.executionType === "recommendation-only") {
69
+ return [
70
+ "当前仍缺少代码仓库或 CMS 上下文,暂时不能确认修复会落在哪些模板。",
71
+ "如果只停留在建议层,没有工程落地,问题不会真正消失。",
72
+ "仅凭首页和公开页面可能无法覆盖深层模板问题。"
73
+ ];
74
+ }
75
+ return [
76
+ "当前还是工件驱动阶段,尚未确认具体模板或文件路径。",
77
+ "没有仓库权限前,无法验证修复动作是否会影响整类页面。",
78
+ "如果不补齐上下文,执行顺序再清楚也无法进入真正落地。"
79
+ ];
80
+ }
81
+
82
+ function buildNextRoundTasks(plan) {
83
+ return plan.packets.map((packet) => ({
84
+ id: packet.id,
85
+ title: packet.title,
86
+ owner: packet.owner,
87
+ nextAction:
88
+ plan.executionType === "direct-fix"
89
+ ? "按执行包完成修改并立刻复测。"
90
+ : plan.executionType === "recommendation-only"
91
+ ? "先补仓库或页面上下文,再把建议转成直接实施。"
92
+ : "先补上下文,再切到 implementation-ready 模式执行。",
93
+ doneWhen: packet.doneWhen
94
+ }));
95
+ }
96
+
97
+ function buildManagementSummary(plan) {
98
+ if (plan.executionType === "direct-fix") {
99
+ return "团队已经具备直接执行条件,重点是尽快完成首批高优先级修复并验证结果。";
100
+ }
101
+ if (plan.executionType === "recommendation-only") {
102
+ return "当前更适合先完成建议与对齐,拿到仓库或 CMS 访问后再进入正式修复。";
103
+ }
104
+ return "当前已经明确下一轮执行顺序,但仍需要额外上下文才能真正进入修复。";
105
+ }
106
+
107
+ async function resolveApplyPlan(input) {
108
+ if (!input) {
109
+ throw new Error("completion-report 需要一个输入值,可以是项目路径、网站网址或已导出的 JSON 工件。");
110
+ }
111
+
112
+ if (isUrlInput(input)) {
113
+ return createApplyPlan(input, { format: "json" });
114
+ }
115
+
116
+ const resolvedPath = path.resolve(input);
117
+ const exists = await pathExists(resolvedPath);
118
+ if (!exists) {
119
+ return createApplyPlan(input, { format: "json" });
120
+ }
121
+
122
+ const stat = await fs.stat(resolvedPath);
123
+ if (stat.isDirectory()) {
124
+ return createApplyPlan(resolvedPath, { format: "json" });
125
+ }
126
+
127
+ const raw = await fs.readFile(resolvedPath, "utf8");
128
+ const parsed = JSON.parse(raw);
129
+
130
+ if (parsed.kind === "geo-apply-plan") {
131
+ return parsed;
132
+ }
133
+
134
+ return createApplyPlan(resolvedPath, { format: "json" });
135
+ }
136
+
137
+ export async function createCompletionReport(input, options = {}) {
138
+ const format = normalizeFormat(options.format);
139
+ const plan = await resolveApplyPlan(input);
140
+
141
+ return {
142
+ kind: "geo-completion-report",
143
+ source: plan.source,
144
+ sourceType: plan.sourceType,
145
+ format,
146
+ executionMode: plan.executionMode,
147
+ executionType: plan.executionType,
148
+ progressStatus: inferProgressStatus(plan),
149
+ summary: plan.summary,
150
+ managementSummary: buildManagementSummary(plan),
151
+ completedThisRound: buildCompletedThisRound(plan),
152
+ residualRisks: buildResidualRisks(plan),
153
+ nextRoundTasks: buildNextRoundTasks(plan),
154
+ validationChecklist: Array.from(new Set(plan.packets.flatMap((packet) => packet.validationCommands))),
155
+ completionTemplates: plan.packets.map((packet) => ({
156
+ id: packet.id,
157
+ title: packet.title,
158
+ template: packet.completionTemplate
159
+ }))
160
+ };
161
+ }
162
+
163
+ export function renderCompletionReportMarkdown(report) {
164
+ const lines = [
165
+ "# GEO Completion Report",
166
+ "",
167
+ `- 输入来源:\`${report.source}\``,
168
+ `- 来源类型:\`${report.sourceType}\``,
169
+ `- 执行模式:\`${report.executionMode}\``,
170
+ `- 执行类型:\`${report.executionType}\``,
171
+ `- 当前进度:${report.progressStatus}`,
172
+ `- 当前总结:${report.summary}`,
173
+ `- 管理层摘要:${report.managementSummary}`,
174
+ "",
175
+ "## 本轮已完成",
176
+ ""
177
+ ];
178
+
179
+ for (const item of report.completedThisRound) {
180
+ lines.push(`- ${item}`);
181
+ }
182
+
183
+ lines.push("", "## 剩余风险", "");
184
+ for (const risk of report.residualRisks) {
185
+ lines.push(`- ${risk}`);
186
+ }
187
+
188
+ lines.push("", "## 下一轮任务", "");
189
+ for (const task of report.nextRoundTasks) {
190
+ lines.push(`### ${task.id}|${task.title}`);
191
+ lines.push("");
192
+ lines.push(`- 负责人:${task.owner}`);
193
+ lines.push(`- 下一步:${task.nextAction}`);
194
+ lines.push("- 完成标准:");
195
+ for (const criterion of task.doneWhen) {
196
+ lines.push(` - ${criterion}`);
197
+ }
198
+ lines.push("");
199
+ }
200
+
201
+ lines.push("## 建议复测指令", "");
202
+ for (const command of report.validationChecklist) {
203
+ lines.push(`- \`${command}\``);
204
+ }
205
+
206
+ lines.push("", "## 完成回报模板", "");
207
+ for (const template of report.completionTemplates) {
208
+ lines.push(`### ${template.id}|${template.title}`);
209
+ lines.push("```text");
210
+ lines.push(template.template);
211
+ lines.push("```");
212
+ lines.push("");
213
+ }
214
+
215
+ return `${lines.join("\n")}\n`;
216
+ }
217
+
218
+ export async function writeCompletionReportOutput(outputPath, content) {
219
+ return writeApplyPlanOutput(outputPath, content);
220
+ }
package/src/index.js CHANGED
@@ -5,7 +5,9 @@ export {
5
5
  runInteractiveOnboarding,
6
6
  writeInteractiveOnboardingOutput
7
7
  } from "./interactive-onboarding.js";
8
+ export { createApplyPlan, renderApplyPlanMarkdown, writeApplyPlanOutput } from "./apply-plan.js";
8
9
  export { createAgentHandoff, renderAgentHandoffMarkdown, writeAgentHandoffOutput } from "./agent-handoff.js";
10
+ export { createCompletionReport, renderCompletionReportMarkdown, writeCompletionReportOutput } from "./completion-report.js";
9
11
  export { createFixPlan, renderFixPlanMarkdown, writeFixPlanOutput } from "./fix-plan.js";
10
12
  export { installSkill } from "./install-skill.js";
11
13
  export { runCli } from "./cli.js";
@@ -58,7 +58,9 @@ function buildSteps() {
58
58
  goal: "让 PM、工程、内容、SEO 各自知道先做什么。",
59
59
  commands: [
60
60
  "geo-ai-search-optimization fix-plan ./your-site --out ./reports/fix-plan.md",
61
- "geo-ai-search-optimization agent-handoff ./your-site --out ./reports/agent-handoff.md"
61
+ "geo-ai-search-optimization agent-handoff ./your-site --out ./reports/agent-handoff.md",
62
+ "geo-ai-search-optimization apply-plan ./your-site --out ./reports/apply-plan.md",
63
+ "geo-ai-search-optimization completion-report ./your-site --out ./reports/completion-report.md"
62
64
  ]
63
65
  },
64
66
  {