geo-ai-search-optimization 1.2.5 → 1.2.7

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
@@ -24,6 +24,42 @@ npx geo-ai-search-optimization
24
24
  - installs the bundled GEO skills into your Codex skills directory
25
25
  - ships a local resource folder with `SKILL.md`, references, and a scanner script
26
26
  - provides a CLI for installing, locating, and scanning projects for GEO signals
27
+ - now exposes the bundled skill package directly, so agents can discover which GEO sub-skill to use next
28
+
29
+ ## Skills 命令
30
+
31
+ 如果你想先看这个 npm 包里到底打包了哪些 skills,现在可以直接用:
32
+
33
+ ```bash
34
+ geo-ai-search-optimization skills
35
+ geo-ai-search-optimization skills --json
36
+ ```
37
+
38
+ 它会列出:
39
+
40
+ - 核心 GEO skill
41
+ - usage / onboarding skill
42
+ - agent 执行闭环相关 skills
43
+ - 分享 / 导出 / 最终交付相关 skills
44
+
45
+ ## Auto Flow 命令
46
+
47
+ 如果你希望 agent 不用自己判断现在该用哪个 skill、该跑哪个命令,可以直接用 `auto-flow`:
48
+
49
+ ```bash
50
+ geo-ai-search-optimization auto-flow "我想把这份 GEO 结果交给下一个 agent 继续修"
51
+ geo-ai-search-optimization auto-flow https://example.com
52
+ geo-ai-search-optimization auto-flow ./your-site
53
+ geo-ai-search-optimization auto-flow ./reports/apply-plan.json --json
54
+ ```
55
+
56
+ 它会自动输出:
57
+
58
+ - 当前输入属于哪一类
59
+ - 现在更像诊断、执行、复盘还是交付阶段
60
+ - 最适合的下一个 skill
61
+ - 建议命令顺序
62
+ - 可直接复制给 agent 的 prompt
27
63
 
28
64
  ## Quick Start
29
65
 
@@ -418,6 +454,8 @@ geo-ai-search-optimization onboard --url https://example.com --json --out ./repo
418
454
  geo-ai-search-optimization
419
455
  geo-ai-search-optimization install
420
456
  geo-ai-search-optimization install --target ./tmp/custom-skills --json
457
+ geo-ai-search-optimization auto-flow "audit this site and tell me the next skill"
458
+ geo-ai-search-optimization skills
421
459
  geo-ai-search-optimization where
422
460
  geo-ai-search-optimization doctor
423
461
  geo-ai-search-optimization quick-start
@@ -452,6 +490,20 @@ geo-ai-search-optimization version
452
490
  geo-ai-search-optimization help
453
491
  ```
454
492
 
493
+ ## New in 1.2.6
494
+
495
+ - 新增 `skills` 命令,直接列出整套 GEO 技能包
496
+ - 安装后会额外写入 `.skill-bundle.json`,方便 agent 自动发现技能
497
+ - 为缺失的 skill 补齐 `agents/openai.yaml`
498
+ - 主 skill 新增 `skill-bundle-map` 参考,usage skill 也同步纳入 `publish-pack` 和 `skills`
499
+
500
+ ## New in 1.2.7
501
+
502
+ - 新增 `auto-flow` 命令,自动选择下一步该用哪个 GEO skill
503
+ - 支持任务描述、网站网址、本地项目目录和 JSON 工件作为输入
504
+ - 输出推荐 skill、命令顺序与可直接复制给 agent 的 prompt
505
+ - 新增 `geo-ai-search-optimization-auto-flow` skill,作为 agent 的自动路由入口
506
+
455
507
  ## New in 1.2.5
456
508
 
457
509
  - 新增 `publish-pack`
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "geo-ai-search-optimization",
3
- "version": "1.2.5",
3
+ "version": "1.2.7",
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": {
@@ -163,3 +163,4 @@ When the user asks for the latest vendor behavior or policy:
163
163
  - `references/quick-start.md`: from-0-to-1 startup sequence for using the CLI and the skill together
164
164
  - `references/geo-playbook.md`: content patterns and GEO-first page design heuristics
165
165
  - `references/implementation-checklist.md`: technical, content, and measurement checklist
166
+ - `references/skill-bundle-map.md`: bundled GEO sub-skills and when to use each one
@@ -0,0 +1,94 @@
1
+ # GEO Skill Bundle Map
2
+
3
+ ## Table of Contents
4
+
5
+ - Core skill
6
+ - Auto entry
7
+ - Usage guide
8
+ - Agent execution loop
9
+ - Delivery chain
10
+
11
+ ## Core skill
12
+
13
+ ### `geo-ai-search-optimization`
14
+
15
+ Use this first when the task is still about diagnosing or improving GEO readiness itself.
16
+
17
+ Best for:
18
+
19
+ - auditing a website or codebase
20
+ - improving answerability, structure, and citation readiness
21
+ - deciding what the highest-impact GEO fixes are
22
+
23
+ ## Auto entry
24
+
25
+ ### `geo-ai-search-optimization-auto-flow`
26
+
27
+ Use this when the next agent needs a routing entrypoint and should not have to decide manually which skill to use.
28
+
29
+ Best for:
30
+
31
+ - selecting the next skill from a URL, directory, or artifact
32
+ - turning one-line task briefs into a concrete command sequence
33
+ - deciding whether the work should move into diagnosis, execution, closeout, or delivery
34
+
35
+ ## Usage guide
36
+
37
+ ### `geo-ai-search-optimization-usage`
38
+
39
+ Use this when the next agent or user does not know which command or skill to use next.
40
+
41
+ Best for:
42
+
43
+ - onboarding new users
44
+ - explaining command order
45
+ - translating a GEO request into concrete CLI steps
46
+
47
+ ## Agent execution loop
48
+
49
+ Use these when the work is moving from analysis into action.
50
+
51
+ ### `geo-ai-search-optimization-agent-handoff`
52
+
53
+ Turn GEO findings into a package that another agent can continue from.
54
+
55
+ ### `geo-ai-search-optimization-repair-loop`
56
+
57
+ Continue from `apply-plan` style execution tasks and close the loop on actual fixes.
58
+
59
+ ### `geo-ai-search-optimization-completion-report`
60
+
61
+ Summarize what finished, what still blocks progress, and what should happen next.
62
+
63
+ ### `geo-ai-search-optimization-handoff-bundle`
64
+
65
+ Use when one artifact needs to contain handoff, execution, and closeout together.
66
+
67
+ ## Delivery chain
68
+
69
+ Use these when the output needs to be shared, exported, or handed off to humans and agents.
70
+
71
+ ### `geo-ai-search-optimization-share-pack`
72
+
73
+ Prepare role-specific views for PM, engineering, management, and the next agent.
74
+
75
+ ### `geo-ai-search-optimization-export-pack`
76
+
77
+ Generate a disk folder with ready-to-send markdown or JSON deliverables.
78
+
79
+ ### `geo-ai-search-optimization-html-pack`
80
+
81
+ Generate browsable HTML pages for quick sharing.
82
+
83
+ ### `geo-ai-search-optimization-publish-pack`
84
+
85
+ Generate the final delivery folder that combines:
86
+
87
+ - `site/` HTML pages
88
+ - `documents/` markdown files
89
+ - `data/` JSON artifacts
90
+ - `START-HERE.md`
91
+ - `AGENT-START.md`
92
+ - `manifest.json`
93
+
94
+ This is the best choice when the output should be directly handed to a PM, a stakeholder group, or another agent without further manual packaging.
@@ -0,0 +1,23 @@
1
+ ---
2
+ name: geo-ai-search-optimization-auto-flow
3
+ description: Automatically choose the next GEO skill and command chain from a task description, website URL, local project directory, or existing GEO artifact. Use when an agent needs a routing entrypoint to decide which GEO skill to invoke next and what commands should follow.
4
+ ---
5
+
6
+ # GEO Auto Flow
7
+
8
+ Use this skill when the next agent should not have to guess which GEO skill to use.
9
+
10
+ `GEO = Generative Engine Optimization`
11
+
12
+ ## What it does
13
+
14
+ - identify whether the input is a task brief, URL, project directory, or GEO artifact
15
+ - route to the most suitable next GEO skill
16
+ - recommend the next commands
17
+ - generate a copyable agent prompt
18
+
19
+ ## Best use
20
+
21
+ - start a new agent session from one line of user input
22
+ - continue from an existing GEO artifact without re-reading the whole system
23
+ - decide whether the next move is diagnosis, execution, closeout, or delivery
@@ -0,0 +1,4 @@
1
+ interface:
2
+ display_name: "GEO Auto Flow"
3
+ short_description: "Auto-select the next GEO skill and command chain"
4
+ default_prompt: "Use $geo-ai-search-optimization-auto-flow to choose the next GEO skill, explain why, and recommend the next commands for this input."
@@ -0,0 +1,4 @@
1
+ interface:
2
+ display_name: "GEO Export Pack"
3
+ short_description: "Export GEO deliverables into a shareable folder"
4
+ default_prompt: "Use $geo-ai-search-optimization-export-pack to generate a folder of GEO deliverables for PM, engineering, management, and agent handoff."
@@ -0,0 +1,4 @@
1
+ interface:
2
+ display_name: "GEO Handoff Bundle"
3
+ short_description: "Continue from one bundled GEO execution artifact"
4
+ default_prompt: "Use $geo-ai-search-optimization-handoff-bundle to continue from this combined GEO handoff, execution, and closeout artifact."
@@ -0,0 +1,4 @@
1
+ interface:
2
+ display_name: "GEO HTML Pack"
3
+ short_description: "Generate browsable HTML pages from GEO outputs"
4
+ default_prompt: "Use $geo-ai-search-optimization-html-pack to convert these GEO deliverables into browsable HTML pages for sharing."
@@ -0,0 +1,4 @@
1
+ interface:
2
+ display_name: "GEO Share Pack"
3
+ short_description: "Prepare audience-specific GEO views for sharing"
4
+ default_prompt: "Use $geo-ai-search-optimization-share-pack to turn this GEO result into role-specific views for PM, engineering, management, and the next agent."
@@ -13,26 +13,29 @@ 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 twelve layers:
17
-
18
- 1. `onboard-url` / `onboard`: first look
19
- 2. `scan`: raw signal check
20
- 3. `audit` / `report`: diagnosis
21
- 4. `fix-plan` / `owner-board`: execution planning
22
- 5. `agent-handoff`: agent takeover package
23
- 6. `apply-plan`: execution loop
24
- 7. `completion-report`: closeout
25
- 8. `handoff-bundle`: all-in-one package
26
- 9. `share-pack`: audience-ready delivery
27
- 10. `export-pack`: folder export
28
- 11. `html-pack`: browsable output
29
- 12. `pm-brief` / `roadmap`: stakeholder alignment
16
+ The package is best explained as fourteen layers:
17
+
18
+ 1. `auto-flow`: auto-select the next skill and command chain
19
+ 2. `skills`: inspect the bundled skill package
20
+ 3. `onboard-url` / `onboard`: first look
21
+ 4. `scan`: raw signal check
22
+ 5. `audit` / `report`: diagnosis
23
+ 6. `fix-plan` / `owner-board`: execution planning
24
+ 7. `agent-handoff`: agent takeover package
25
+ 8. `apply-plan`: execution loop
26
+ 9. `completion-report`: closeout
27
+ 10. `handoff-bundle`: all-in-one package
28
+ 11. `share-pack`: audience-ready delivery
29
+ 12. `export-pack`: folder export
30
+ 13. `html-pack` / `publish-pack`: browsable and final delivery output
31
+ 14. `pm-brief` / `roadmap`: stakeholder alignment
30
32
 
31
33
  ## Recommended command order
32
34
 
33
35
  If the user only has a website URL:
34
36
 
35
37
  ```bash
38
+ npx geo-ai-search-optimization auto-flow https://example.com
36
39
  npx geo-ai-search-optimization onboard-url https://example.com
37
40
  npx geo-ai-search-optimization pm-brief https://example.com
38
41
  npx geo-ai-search-optimization roadmap https://example.com
@@ -41,6 +44,7 @@ npx geo-ai-search-optimization roadmap https://example.com
41
44
  If the user has the website codebase:
42
45
 
43
46
  ```bash
47
+ npx geo-ai-search-optimization auto-flow ./your-site
44
48
  npx geo-ai-search-optimization scan ./your-site
45
49
  npx geo-ai-search-optimization audit ./your-site
46
50
  npx geo-ai-search-optimization fix-plan ./your-site
@@ -51,14 +55,17 @@ npx geo-ai-search-optimization handoff-bundle ./your-site
51
55
  npx geo-ai-search-optimization share-pack ./your-site
52
56
  npx geo-ai-search-optimization export-pack ./your-site
53
57
  npx geo-ai-search-optimization html-pack ./your-site
58
+ npx geo-ai-search-optimization publish-pack ./your-site
54
59
  npx geo-ai-search-optimization owner-board ./your-site
55
60
  npx geo-ai-search-optimization roadmap ./your-site
56
61
  ```
57
62
 
58
63
  ## When to recommend each command
59
64
 
65
+ - `auto-flow`: auto-select the next skill and command order from a task brief, URL, project path, or GEO artifact
60
66
  - `onboard-url`: first-time website check from a live URL
61
67
  - `onboard`: interactive first-time onboarding
68
+ - `skills`: list the bundled skills and decide which skill or command chain fits the task
62
69
  - `scan`: check raw technical signals across files
63
70
  - `audit`: get GEO score, issue areas, blockers, and PM action list
64
71
  - `report`: export audit/onboarding/scan into readable files
@@ -70,6 +77,7 @@ npx geo-ai-search-optimization roadmap ./your-site
70
77
  - `share-pack`: prepare role-specific shareable outputs for PM, engineering, management, and agents
71
78
  - `export-pack`: generate a folder with multiple ready-to-send files
72
79
  - `html-pack`: generate static HTML pages for sharing and browsing
80
+ - `publish-pack`: generate the final delivery folder for humans and agents together
73
81
  - `owner-board`: group tasks by PM / engineering / SEO / content
74
82
  - `pm-brief`: prepare a PM summary for meetings and prioritization
75
83
  - `roadmap`: turn tasks into a 2-week / 4-week execution sequence
@@ -79,6 +87,7 @@ npx geo-ai-search-optimization roadmap ./your-site
79
87
  When explaining the tool to a user:
80
88
 
81
89
  - prefer telling them which command to run next, not listing every command
90
+ - if the user or the next agent is unsure where to start, move them to `auto-flow` first
82
91
  - explain the result in PM language, not implementation jargon
83
92
  - if the user sounds new, start with `onboard-url` or `quick-start`
84
93
  - if the user wants another agent to take over, move them to `agent-handoff`
@@ -88,6 +97,7 @@ When explaining the tool to a user:
88
97
  - if the user wants something ready to forward to multiple audiences, move them to `share-pack`
89
98
  - if the user wants actual files on disk for each audience, move them to `export-pack`
90
99
  - if the user wants browsable HTML pages, move them to `html-pack`
100
+ - if the user wants one final package with HTML, markdown, JSON, and an agent entrypoint, move them to `publish-pack`
91
101
  - if the user already has a report, move them to `fix-plan`, `owner-board`, or `roadmap`
92
102
 
93
103
  Read [references/usage-patterns.md](references/usage-patterns.md) when you need response patterns and command selection examples.
@@ -0,0 +1,563 @@
1
+ import fs from "node:fs/promises";
2
+ import path from "node:path";
3
+ import { writeScanOutput } from "./scan.js";
4
+ import { listBundledSkills } from "./skills.js";
5
+
6
+ const VALID_INTENTS = new Set(["auto", "diagnose", "guide", "execute", "share", "closeout"]);
7
+
8
+ function isUrlInput(input) {
9
+ return /^https?:\/\//i.test(input);
10
+ }
11
+
12
+ function normalizeIntent(intent) {
13
+ const resolved = (intent || "auto").toLowerCase();
14
+ if (!VALID_INTENTS.has(resolved)) {
15
+ throw new Error(`不支持的 auto-flow intent:${intent}。可选值:${Array.from(VALID_INTENTS).join(", ")}`);
16
+ }
17
+ return resolved;
18
+ }
19
+
20
+ async function pathExists(targetPath) {
21
+ try {
22
+ await fs.access(targetPath);
23
+ return true;
24
+ } catch {
25
+ return false;
26
+ }
27
+ }
28
+
29
+ function detectArtifactKindFromParsedJson(parsed) {
30
+ if (!parsed || typeof parsed !== "object") {
31
+ return "unknown-json";
32
+ }
33
+
34
+ if (parsed.kind === "geo-report") {
35
+ return `geo-report:${parsed.mode || "unknown"}`;
36
+ }
37
+
38
+ return parsed.kind || "unknown-json";
39
+ }
40
+
41
+ function inferTaskTextMode(text) {
42
+ const normalized = String(text).toLowerCase();
43
+
44
+ if (/(share-pack|export-pack|html-pack|publish-pack|分享|导出|交付|外发|报告包)/i.test(normalized)) {
45
+ return "share";
46
+ }
47
+ if (/(apply-plan|handoff|接手|交给.*agent|继续修|继续做|修复|执行|修改|落地|repair|implement|fix)/i.test(normalized)) {
48
+ return "execute";
49
+ }
50
+ if (/(completion-report|closeout|复盘|收尾|总结|剩余风险)/i.test(normalized)) {
51
+ return "closeout";
52
+ }
53
+ if (/(how to use|怎么用|如何用|命令|skill|技能|流程|next step|下一步)/i.test(normalized)) {
54
+ return "guide";
55
+ }
56
+ return "diagnose";
57
+ }
58
+
59
+ async function detectInput(input) {
60
+ if (!input) {
61
+ throw new Error("auto-flow 需要一个输入值,可以是任务描述、项目路径、网站网址或已导出的工件。");
62
+ }
63
+
64
+ if (isUrlInput(input)) {
65
+ return {
66
+ source: input,
67
+ sourceType: "url",
68
+ artifactKind: "website-url",
69
+ summary: "公开网站网址"
70
+ };
71
+ }
72
+
73
+ const resolvedPath = path.resolve(input);
74
+ if (!(await pathExists(resolvedPath))) {
75
+ return {
76
+ source: input,
77
+ sourceType: "task-text",
78
+ artifactKind: "task-brief",
79
+ summary: "自然语言任务描述",
80
+ inferredTextIntent: inferTaskTextMode(input)
81
+ };
82
+ }
83
+
84
+ const stat = await fs.stat(resolvedPath);
85
+ if (stat.isDirectory()) {
86
+ return {
87
+ source: resolvedPath,
88
+ sourceType: "directory",
89
+ artifactKind: "project-directory",
90
+ summary: "本地网站项目目录"
91
+ };
92
+ }
93
+
94
+ const extension = path.extname(resolvedPath).toLowerCase();
95
+ if (extension === ".json") {
96
+ const raw = await fs.readFile(resolvedPath, "utf8");
97
+ const parsed = JSON.parse(raw);
98
+ return {
99
+ source: resolvedPath,
100
+ sourceType: "json",
101
+ artifactKind: detectArtifactKindFromParsedJson(parsed),
102
+ summary: "GEO JSON 工件",
103
+ parsed
104
+ };
105
+ }
106
+
107
+ const fileName = path.basename(resolvedPath);
108
+ let artifactKind = "generic-file";
109
+ if (fileName === "START-HERE.md" || fileName === "AGENT-START.md" || fileName === "manifest.json") {
110
+ artifactKind = "geo-publish-pack-entry";
111
+ } else if (/share-pack/i.test(fileName)) {
112
+ artifactKind = "geo-share-pack-file";
113
+ } else if (/owner-board/i.test(fileName)) {
114
+ artifactKind = "geo-owner-board-file";
115
+ }
116
+
117
+ return {
118
+ source: resolvedPath,
119
+ sourceType: "file",
120
+ artifactKind,
121
+ summary: "本地文件工件"
122
+ };
123
+ }
124
+
125
+ function resolveEffectiveIntent(intent, detected) {
126
+ if (intent !== "auto") {
127
+ return intent;
128
+ }
129
+
130
+ if (detected.sourceType === "task-text") {
131
+ return detected.inferredTextIntent || "diagnose";
132
+ }
133
+
134
+ if (detected.artifactKind === "geo-completion-report") {
135
+ return "closeout";
136
+ }
137
+ if (
138
+ [
139
+ "geo-share-pack",
140
+ "geo-export-pack",
141
+ "geo-html-pack",
142
+ "geo-publish-pack",
143
+ "geo-publish-pack-entry",
144
+ "geo-share-pack-file"
145
+ ].includes(detected.artifactKind)
146
+ ) {
147
+ return "share";
148
+ }
149
+ if (
150
+ ["geo-agent-handoff", "geo-apply-plan", "geo-handoff-bundle", "geo-fix-plan"].includes(detected.artifactKind)
151
+ ) {
152
+ return "execute";
153
+ }
154
+ if (detected.artifactKind === "task-brief") {
155
+ return detected.inferredTextIntent || "diagnose";
156
+ }
157
+ return "diagnose";
158
+ }
159
+
160
+ function buildCommandChain(detected, intent) {
161
+ const source = detected.source;
162
+
163
+ if (detected.sourceType === "task-text") {
164
+ return [`geo-ai-search-optimization skills`, `geo-ai-search-optimization quick-start`];
165
+ }
166
+
167
+ if (intent === "guide") {
168
+ return [`geo-ai-search-optimization skills`, `geo-ai-search-optimization quick-start`, `geo-ai-search-optimization auto-flow ${source}`];
169
+ }
170
+
171
+ if (intent === "share") {
172
+ if (detected.artifactKind === "geo-completion-report") {
173
+ return [
174
+ `geo-ai-search-optimization share-pack ${source}`,
175
+ `geo-ai-search-optimization export-pack ${source}`,
176
+ `geo-ai-search-optimization publish-pack ${source}`
177
+ ];
178
+ }
179
+
180
+ return [
181
+ `geo-ai-search-optimization share-pack ${source}`,
182
+ `geo-ai-search-optimization html-pack ${source}`,
183
+ `geo-ai-search-optimization publish-pack ${source}`
184
+ ];
185
+ }
186
+
187
+ if (intent === "closeout") {
188
+ return [
189
+ `geo-ai-search-optimization completion-report ${source}`,
190
+ `geo-ai-search-optimization meeting-pack ${source}`,
191
+ `geo-ai-search-optimization publish-pack ${source}`
192
+ ];
193
+ }
194
+
195
+ if (detected.sourceType === "url") {
196
+ if (intent === "execute") {
197
+ return [
198
+ `geo-ai-search-optimization onboard-url ${source}`,
199
+ `geo-ai-search-optimization fix-plan ${source}`,
200
+ `geo-ai-search-optimization agent-handoff ${source}`
201
+ ];
202
+ }
203
+ return [
204
+ `geo-ai-search-optimization onboard-url ${source}`,
205
+ `geo-ai-search-optimization pm-brief ${source}`,
206
+ `geo-ai-search-optimization report ${source} --mode onboarding --format markdown`
207
+ ];
208
+ }
209
+
210
+ if (detected.sourceType === "directory") {
211
+ if (intent === "execute") {
212
+ return [
213
+ `geo-ai-search-optimization audit ${source}`,
214
+ `geo-ai-search-optimization fix-plan ${source}`,
215
+ `geo-ai-search-optimization agent-handoff ${source}`,
216
+ `geo-ai-search-optimization apply-plan ${source}`
217
+ ];
218
+ }
219
+ return [
220
+ `geo-ai-search-optimization scan ${source}`,
221
+ `geo-ai-search-optimization audit ${source}`,
222
+ `geo-ai-search-optimization fix-plan ${source}`
223
+ ];
224
+ }
225
+
226
+ switch (detected.artifactKind) {
227
+ case "geo-report:audit":
228
+ case "geo-audit":
229
+ return intent === "execute"
230
+ ? [
231
+ `geo-ai-search-optimization fix-plan ${source}`,
232
+ `geo-ai-search-optimization agent-handoff ${source}`,
233
+ `geo-ai-search-optimization apply-plan ${source}`
234
+ ]
235
+ : [`geo-ai-search-optimization fix-plan ${source}`, `geo-ai-search-optimization owner-board ${source}`];
236
+ case "geo-url-onboarding":
237
+ case "geo-report:onboarding":
238
+ return [
239
+ `geo-ai-search-optimization pm-brief ${source}`,
240
+ `geo-ai-search-optimization fix-plan ${source}`,
241
+ `geo-ai-search-optimization roadmap ${source}`
242
+ ];
243
+ case "geo-fix-plan":
244
+ return [
245
+ `geo-ai-search-optimization agent-handoff ${source}`,
246
+ `geo-ai-search-optimization apply-plan ${source}`
247
+ ];
248
+ case "geo-agent-handoff":
249
+ return [
250
+ `geo-ai-search-optimization apply-plan ${source}`,
251
+ `geo-ai-search-optimization completion-report ${source}`
252
+ ];
253
+ case "geo-apply-plan":
254
+ return [
255
+ `geo-ai-search-optimization apply-plan ${source}`,
256
+ `geo-ai-search-optimization completion-report ${source}`,
257
+ `geo-ai-search-optimization handoff-bundle ${source}`
258
+ ];
259
+ case "geo-completion-report":
260
+ return [
261
+ `geo-ai-search-optimization meeting-pack ${source}`,
262
+ `geo-ai-search-optimization share-pack ${source}`,
263
+ `geo-ai-search-optimization publish-pack ${source}`
264
+ ];
265
+ case "geo-handoff-bundle":
266
+ return [
267
+ `geo-ai-search-optimization apply-plan ${source}`,
268
+ `geo-ai-search-optimization completion-report ${source}`,
269
+ `geo-ai-search-optimization publish-pack ${source}`
270
+ ];
271
+ case "geo-share-pack":
272
+ case "geo-share-pack-file":
273
+ return [
274
+ `geo-ai-search-optimization export-pack ${source}`,
275
+ `geo-ai-search-optimization html-pack ${source}`,
276
+ `geo-ai-search-optimization publish-pack ${source}`
277
+ ];
278
+ case "geo-export-pack":
279
+ return [`geo-ai-search-optimization html-pack ${source}`, `geo-ai-search-optimization publish-pack ${source}`];
280
+ case "geo-html-pack":
281
+ return [`geo-ai-search-optimization publish-pack ${source}`];
282
+ case "geo-publish-pack":
283
+ case "geo-publish-pack-entry":
284
+ return [`geo-ai-search-optimization auto-flow ${source} --intent guide`, `geo-ai-search-optimization skills`];
285
+ default:
286
+ return [`geo-ai-search-optimization skills`, `geo-ai-search-optimization quick-start`];
287
+ }
288
+ }
289
+
290
+ function pickSkillName(detected, intent) {
291
+ if (intent === "guide") {
292
+ return "geo-ai-search-optimization-usage";
293
+ }
294
+ if (intent === "share") {
295
+ return "geo-ai-search-optimization-publish-pack";
296
+ }
297
+ if (intent === "closeout") {
298
+ return "geo-ai-search-optimization-completion-report";
299
+ }
300
+
301
+ switch (detected.artifactKind) {
302
+ case "task-brief":
303
+ return detected.inferredTextIntent === "share"
304
+ ? "geo-ai-search-optimization-publish-pack"
305
+ : detected.inferredTextIntent === "execute"
306
+ ? "geo-ai-search-optimization-agent-handoff"
307
+ : "geo-ai-search-optimization-usage";
308
+ case "website-url":
309
+ case "project-directory":
310
+ case "geo-audit":
311
+ case "geo-report:audit":
312
+ case "geo-url-onboarding":
313
+ case "geo-report:onboarding":
314
+ return intent === "execute" ? "geo-ai-search-optimization-agent-handoff" : "geo-ai-search-optimization";
315
+ case "geo-fix-plan":
316
+ case "geo-agent-handoff":
317
+ return "geo-ai-search-optimization-agent-handoff";
318
+ case "geo-apply-plan":
319
+ return "geo-ai-search-optimization-repair-loop";
320
+ case "geo-completion-report":
321
+ return "geo-ai-search-optimization-completion-report";
322
+ case "geo-handoff-bundle":
323
+ return "geo-ai-search-optimization-handoff-bundle";
324
+ case "geo-share-pack":
325
+ case "geo-share-pack-file":
326
+ return "geo-ai-search-optimization-share-pack";
327
+ case "geo-export-pack":
328
+ return "geo-ai-search-optimization-export-pack";
329
+ case "geo-html-pack":
330
+ return "geo-ai-search-optimization-html-pack";
331
+ case "geo-publish-pack":
332
+ case "geo-publish-pack-entry":
333
+ return "geo-ai-search-optimization-publish-pack";
334
+ default:
335
+ return "geo-ai-search-optimization-usage";
336
+ }
337
+ }
338
+
339
+ function buildSecondarySkillNames(primarySkill, intent, detected) {
340
+ const names = new Set(["geo-ai-search-optimization-usage"]);
341
+
342
+ if (primarySkill !== "geo-ai-search-optimization") {
343
+ names.add("geo-ai-search-optimization");
344
+ }
345
+ if (intent === "share") {
346
+ names.add("geo-ai-search-optimization-share-pack");
347
+ names.add("geo-ai-search-optimization-publish-pack");
348
+ }
349
+ if (intent === "execute" || ["geo-fix-plan", "geo-agent-handoff", "geo-apply-plan"].includes(detected.artifactKind)) {
350
+ names.add("geo-ai-search-optimization-agent-handoff");
351
+ names.add("geo-ai-search-optimization-repair-loop");
352
+ }
353
+ if (intent === "closeout") {
354
+ names.add("geo-ai-search-optimization-completion-report");
355
+ }
356
+
357
+ names.delete(primarySkill);
358
+ return Array.from(names);
359
+ }
360
+
361
+ function buildStage(intent, detected) {
362
+ if (intent === "guide") {
363
+ return "使用引导";
364
+ }
365
+ if (intent === "share") {
366
+ return "交付分享";
367
+ }
368
+ if (intent === "closeout") {
369
+ return "执行复盘";
370
+ }
371
+ if (intent === "execute") {
372
+ return ["geo-fix-plan", "geo-agent-handoff", "geo-apply-plan", "geo-handoff-bundle"].includes(detected.artifactKind)
373
+ ? "Agent 执行"
374
+ : "执行准备";
375
+ }
376
+ if (["geo-fix-plan", "geo-agent-handoff", "geo-apply-plan", "geo-handoff-bundle"].includes(detected.artifactKind)) {
377
+ return "Agent 执行";
378
+ }
379
+ return "诊断规划";
380
+ }
381
+
382
+ function buildWhy(detected, intent, primarySkillLabel) {
383
+ if (detected.sourceType === "task-text") {
384
+ return `当前输入是一段任务描述,还不是具体工件,所以先用 ${primarySkillLabel} 来判断阶段,并明确下一步还缺什么上下文。`;
385
+ }
386
+ if (detected.sourceType === "url") {
387
+ return `当前只有公开网址,没有仓库上下文,最适合先走网址级 GEO 流程,再决定是否进入 agent 执行。`;
388
+ }
389
+ if (detected.sourceType === "directory") {
390
+ return intent === "execute"
391
+ ? "当前已经有本地项目目录,可以直接进入 GEO 诊断到 agent 接手的执行链。"
392
+ : "当前已有本地项目目录,先跑诊断和优先级判断最稳妥。";
393
+ }
394
+ if (detected.sourceType === "json") {
395
+ return `当前输入已经是 GEO 工件(${detected.artifactKind}),所以可以跳过前置步骤,直接进入对应阶段。`;
396
+ }
397
+ return "当前输入更像中间结果或说明文本,先根据上下文自动选择最接近的 skill。";
398
+ }
399
+
400
+ function buildContextNeeded(detected, intent) {
401
+ if (detected.sourceType === "task-text") {
402
+ const items = ["至少补一个公开网址、本地项目路径,或已有 GEO JSON 工件。"];
403
+ if (intent === "execute") {
404
+ items.push("如果希望 agent 直接修复,最好提供代码仓库或本地项目目录。");
405
+ }
406
+ if (intent === "share") {
407
+ items.push("如果希望直接交付,最好提供已生成的 audit / handoff / completion 等工件。");
408
+ }
409
+ return items;
410
+ }
411
+
412
+ if (detected.sourceType === "url" && intent === "execute") {
413
+ return ["当前只有公开网址,能先做建议型执行。若要直接修复,后续仍需提供本地项目目录或仓库上下文。"];
414
+ }
415
+
416
+ return [];
417
+ }
418
+
419
+ function buildAgentPrompt(skill, detected, intent, commands) {
420
+ const lines = [
421
+ skill.defaultPrompt || `Use $${skill.name} to continue this GEO task.`,
422
+ `输入:${detected.source}`,
423
+ `意图:${intent}`,
424
+ "请先说明你为什么选择这个 skill,然后按建议命令顺序继续。"
425
+ ];
426
+
427
+ if (commands.length > 0) {
428
+ lines.push("建议命令顺序:");
429
+ for (const command of commands) {
430
+ lines.push(`- ${command}`);
431
+ }
432
+ }
433
+
434
+ return lines.join("\n");
435
+ }
436
+
437
+ function buildNextAction(detected, intent, commands) {
438
+ if (detected.sourceType === "task-text") {
439
+ if (intent === "execute") {
440
+ return "先补一个公开网址、本地项目目录或 GEO JSON 工件;有了具体输入后再重跑 auto-flow,才能真正进入 agent 执行链。";
441
+ }
442
+ if (intent === "share") {
443
+ return "先补一个 GEO 工件或项目输入,再生成可分享的交付包。";
444
+ }
445
+ if (intent === "closeout") {
446
+ return "先补 completion-report、apply-plan 或 handoff-bundle 这类执行工件,再进入复盘。";
447
+ }
448
+ return "先补一个公开网址、本地项目目录或 GEO 工件,让系统能自动定位到正确阶段。";
449
+ }
450
+
451
+ if (commands.length === 0) {
452
+ return "先运行 skills 查看整套技能包。";
453
+ }
454
+
455
+ if (intent === "share") {
456
+ return `先运行 \`${commands[0]}\` 生成适合外发或交接的结果。`;
457
+ }
458
+ if (intent === "execute") {
459
+ return `先运行 \`${commands[0]}\`,把当前输入推进到 agent 可执行状态。`;
460
+ }
461
+ if (intent === "closeout") {
462
+ return `先运行 \`${commands[0]}\`,整理本轮完成情况和剩余风险。`;
463
+ }
464
+ if (intent === "guide") {
465
+ return `先运行 \`${commands[0]}\`,确认技能包与命令顺序。`;
466
+ }
467
+ return `先运行 \`${commands[0]}\`,建立当前阶段的 GEO 基线。`;
468
+ }
469
+
470
+ export async function createAutoFlow(input, options = {}) {
471
+ const bundle = await listBundledSkills();
472
+ const detected = await detectInput(input);
473
+ const intent = resolveEffectiveIntent(normalizeIntent(options.intent), detected);
474
+ const primarySkillName = pickSkillName(detected, intent);
475
+ const primarySkill = bundle.skills.find((skill) => skill.name === primarySkillName);
476
+
477
+ if (!primarySkill) {
478
+ throw new Error(`找不到技能:${primarySkillName}`);
479
+ }
480
+
481
+ const secondarySkillNames = buildSecondarySkillNames(primarySkillName, intent, detected);
482
+ const secondarySkills = bundle.skills.filter((skill) => secondarySkillNames.includes(skill.name));
483
+ const commandChain = buildCommandChain(detected, intent);
484
+
485
+ return {
486
+ kind: "geo-auto-flow",
487
+ input,
488
+ source: detected.source,
489
+ sourceType: detected.sourceType,
490
+ artifactKind: detected.artifactKind,
491
+ summary: detected.summary,
492
+ intent,
493
+ stage: buildStage(intent, detected),
494
+ selectedSkill: {
495
+ name: primarySkill.name,
496
+ displayName: primarySkill.displayName,
497
+ shortDescription: primarySkill.shortDescription,
498
+ defaultPrompt: primarySkill.defaultPrompt,
499
+ skillPath: primarySkill.skillPath
500
+ },
501
+ secondarySkills: secondarySkills.map((skill) => ({
502
+ name: skill.name,
503
+ displayName: skill.displayName,
504
+ shortDescription: skill.shortDescription,
505
+ skillPath: skill.skillPath
506
+ })),
507
+ whyThisSkill: buildWhy(detected, intent, primarySkill.displayName),
508
+ contextNeeded: buildContextNeeded(detected, intent),
509
+ nextAction: buildNextAction(detected, intent, commandChain),
510
+ commandChain,
511
+ agentPrompt: buildAgentPrompt(primarySkill, detected, intent, commandChain)
512
+ };
513
+ }
514
+
515
+ export function renderAutoFlowMarkdown(flow) {
516
+ const lines = [
517
+ "# GEO Auto Flow",
518
+ "",
519
+ `- 输入:\`${flow.source}\``,
520
+ `- 输入类型:\`${flow.sourceType}\``,
521
+ `- 工件类型:\`${flow.artifactKind}\``,
522
+ `- 当前阶段:${flow.stage}`,
523
+ `- 当前意图:${flow.intent}`,
524
+ `- 自动选择的 skill:\`${flow.selectedSkill.name}\``,
525
+ `- 显示名:${flow.selectedSkill.displayName}`,
526
+ `- 说明:${flow.selectedSkill.shortDescription}`,
527
+ "",
528
+ "## 为什么是这个 skill",
529
+ "",
530
+ `- ${flow.whyThisSkill}`,
531
+ "",
532
+ "## 下一步",
533
+ "",
534
+ `- ${flow.nextAction}`,
535
+ "",
536
+ "## 建议命令顺序",
537
+ ""
538
+ ];
539
+
540
+ for (const command of flow.commandChain) {
541
+ lines.push(`- \`${command}\``);
542
+ }
543
+
544
+ if (flow.contextNeeded.length > 0) {
545
+ lines.push("", "## 还需要补什么", "");
546
+ for (const item of flow.contextNeeded) {
547
+ lines.push(`- ${item}`);
548
+ }
549
+ }
550
+
551
+ lines.push("", "## 可一起参考的 skill", "");
552
+ for (const skill of flow.secondarySkills) {
553
+ lines.push(`- ${skill.name}|${skill.displayName}|${skill.shortDescription}`);
554
+ }
555
+
556
+ lines.push("", "## 可直接复制给 Agent 的 Prompt", "", "```text", flow.agentPrompt, "```");
557
+
558
+ return `${lines.join("\n")}\n`;
559
+ }
560
+
561
+ export async function writeAutoFlowOutput(outputPath, content) {
562
+ return writeScanOutput(outputPath, content);
563
+ }
package/src/cli.js CHANGED
@@ -3,6 +3,7 @@ import { readFile } from "node:fs/promises";
3
3
  import path from "node:path";
4
4
  import { createApplyPlan, renderApplyPlanMarkdown, writeApplyPlanOutput } from "./apply-plan.js";
5
5
  import { createAgentHandoff, renderAgentHandoffMarkdown, writeAgentHandoffOutput } from "./agent-handoff.js";
6
+ import { createAutoFlow, renderAutoFlowMarkdown, writeAutoFlowOutput } from "./auto-flow.js";
6
7
  import {
7
8
  createCompletionReport,
8
9
  renderCompletionReportMarkdown,
@@ -33,6 +34,7 @@ import {
33
34
  import { createQuickStartPlan, renderQuickStartMarkdown, writeQuickStartOutput } from "./quick-start.js";
34
35
  import { createSchemaTemplate } from "./schema.js";
35
36
  import { createSharePack, renderSharePackMarkdown, writeSharePackOutput } from "./share-pack.js";
37
+ import { listBundledSkills, renderBundledSkillsMarkdown } from "./skills.js";
36
38
  import { analyzeWebsiteUrl, renderWebsiteOnboardingMarkdown, writeWebsiteOnboardingOutput } from "./url-onboarding.js";
37
39
 
38
40
  let cachedVersion;
@@ -56,6 +58,8 @@ function printHelp() {
56
58
  "Usage:",
57
59
  " geo-ai-search-optimization",
58
60
  " geo-ai-search-optimization install [--target <dir>] [--json]",
61
+ " geo-ai-search-optimization auto-flow <input> [--intent <auto|diagnose|guide|execute|share|closeout>] [--json] [--out <file>]",
62
+ " geo-ai-search-optimization skills [--json]",
59
63
  " geo-ai-search-optimization where",
60
64
  " geo-ai-search-optimization doctor [--json]",
61
65
  " geo-ai-search-optimization quick-start [--json] [--out <file>]",
@@ -125,6 +129,28 @@ async function handleInstall(args) {
125
129
  }
126
130
  }
127
131
 
132
+ async function handleAutoFlow(args) {
133
+ const input = args.find((value) => !value.startsWith("-"));
134
+ if (!input) {
135
+ throw new Error("auto-flow 需要一个输入值,可以是任务描述、项目路径、网站网址或已导出的工件");
136
+ }
137
+
138
+ const flow = await createAutoFlow(input, {
139
+ intent: getFlagValue(args, "--intent")
140
+ });
141
+ const outputJson = hasFlag(args, "--json");
142
+ const renderedOutput = outputJson ? `${JSON.stringify(flow, null, 2)}\n` : renderAutoFlowMarkdown(flow);
143
+
144
+ const outputPath = getFlagValue(args, "--out");
145
+ if (outputPath) {
146
+ const resolvedOutputPath = await writeAutoFlowOutput(outputPath, renderedOutput);
147
+ process.stdout.write(`已保存 auto-flow 结果:${resolvedOutputPath}\n`);
148
+ return;
149
+ }
150
+
151
+ process.stdout.write(renderedOutput);
152
+ }
153
+
128
154
  function handleWhere() {
129
155
  process.stdout.write(
130
156
  [
@@ -136,6 +162,16 @@ function handleWhere() {
136
162
  );
137
163
  }
138
164
 
165
+ async function handleSkills(args) {
166
+ const bundle = await listBundledSkills();
167
+ if (hasFlag(args, "--json")) {
168
+ process.stdout.write(`${JSON.stringify(bundle, null, 2)}\n`);
169
+ return;
170
+ }
171
+
172
+ process.stdout.write(renderBundledSkillsMarkdown(bundle));
173
+ }
174
+
139
175
  async function handleDoctor(args) {
140
176
  const report = await runDoctor();
141
177
  if (hasFlag(args, "--json")) {
@@ -655,6 +691,16 @@ export async function runCli(args = []) {
655
691
  return;
656
692
  }
657
693
 
694
+ if (command === "auto-flow") {
695
+ await handleAutoFlow(rest);
696
+ return;
697
+ }
698
+
699
+ if (command === "skills") {
700
+ await handleSkills(rest);
701
+ return;
702
+ }
703
+
658
704
  if (command === "where") {
659
705
  handleWhere();
660
706
  return;
package/src/index.js CHANGED
@@ -5,6 +5,7 @@ export {
5
5
  runInteractiveOnboarding,
6
6
  writeInteractiveOnboardingOutput
7
7
  } from "./interactive-onboarding.js";
8
+ export { createAutoFlow, renderAutoFlowMarkdown, writeAutoFlowOutput } from "./auto-flow.js";
8
9
  export { createApplyPlan, renderApplyPlanMarkdown, writeApplyPlanOutput } from "./apply-plan.js";
9
10
  export { createAgentHandoff, renderAgentHandoffMarkdown, writeAgentHandoffOutput } from "./agent-handoff.js";
10
11
  export { createCompletionReport, renderCompletionReportMarkdown, writeCompletionReportOutput } from "./completion-report.js";
@@ -27,4 +28,5 @@ export { createRoadmap, renderRoadmapMarkdown, writeRoadmapOutput } from "./road
27
28
  export { createSchemaTemplate } from "./schema.js";
28
29
  export { scanProject, renderScanMarkdown, writeScanOutput } from "./scan.js";
29
30
  export { createSharePack, renderSharePackMarkdown, writeSharePackOutput } from "./share-pack.js";
31
+ export { listBundledSkills, renderBundledSkillsMarkdown } from "./skills.js";
30
32
  export { analyzeWebsiteUrl, renderWebsiteOnboardingMarkdown, writeWebsiteOnboardingOutput } from "./url-onboarding.js";
@@ -1,6 +1,7 @@
1
1
  import fs from "node:fs/promises";
2
2
  import path from "node:path";
3
3
  import { getBundledSkillPath, getInstalledSkillPath, getSkillName } from "./paths.js";
4
+ import { listBundledSkills } from "./skills.js";
4
5
 
5
6
  async function pathExists(targetPath) {
6
7
  try {
@@ -11,10 +12,18 @@ async function pathExists(targetPath) {
11
12
  }
12
13
  }
13
14
 
15
+ function resolveInstalledSkillPath(skillName, targetDir) {
16
+ if (skillName === getSkillName()) {
17
+ return targetDir;
18
+ }
19
+ return path.join(path.dirname(targetDir), skillName);
20
+ }
21
+
14
22
  export async function installSkill(options = {}) {
15
23
  const sourceDir = getBundledSkillPath();
16
24
  const targetDir = options.targetDir || getInstalledSkillPath();
17
25
  const silent = Boolean(options.silent);
26
+ const bundle = await listBundledSkills();
18
27
 
19
28
  if (!(await pathExists(sourceDir))) {
20
29
  throw new Error(`Bundled skill folder not found: ${sourceDir}`);
@@ -48,6 +57,14 @@ export async function installSkill(options = {}) {
48
57
  const manifest = {
49
58
  skill: getSkillName(),
50
59
  installedSkills,
60
+ installedSkillDetails: bundle.skills
61
+ .filter((skill) => installedSkills.includes(skill.name))
62
+ .map((skill) => ({
63
+ name: skill.name,
64
+ displayName: skill.displayName,
65
+ shortDescription: skill.shortDescription,
66
+ installPath: resolveInstalledSkillPath(skill.name, targetDir)
67
+ })),
51
68
  installedAt: new Date().toISOString(),
52
69
  installedFrom: sourceDir,
53
70
  packageName: "geo-ai-search-optimization",
@@ -60,6 +77,30 @@ export async function installSkill(options = {}) {
60
77
  "utf8"
61
78
  );
62
79
 
80
+ const bundleManifestPath = path.join(targetDir, ".skill-bundle.json");
81
+ await fs.writeFile(
82
+ bundleManifestPath,
83
+ `${JSON.stringify(
84
+ {
85
+ kind: bundle.kind,
86
+ totalSkills: bundle.totalSkills,
87
+ groups: bundle.groups.map((group) => ({
88
+ category: group.category,
89
+ label: group.label,
90
+ skills: group.skills.map((skill) => ({
91
+ name: skill.name,
92
+ displayName: skill.displayName,
93
+ shortDescription: skill.shortDescription,
94
+ installPath: resolveInstalledSkillPath(skill.name, targetDir)
95
+ }))
96
+ }))
97
+ },
98
+ null,
99
+ 2
100
+ )}\n`,
101
+ "utf8"
102
+ );
103
+
63
104
  if (!silent) {
64
105
  process.stdout.write(`Installed skills (${installedSkills.join(", ")}) to ${path.dirname(targetDir)}\n`);
65
106
  }
@@ -69,6 +110,7 @@ export async function installSkill(options = {}) {
69
110
  sourceDir,
70
111
  skillName: getSkillName(),
71
112
  installedSkills,
72
- manifest
113
+ manifest,
114
+ bundleManifestPath
73
115
  };
74
116
  }
package/src/skills.js ADDED
@@ -0,0 +1,173 @@
1
+ import fs from "node:fs/promises";
2
+ import path from "node:path";
3
+ import { getPackageRoot } from "./paths.js";
4
+
5
+ const SKILL_ORDER = [
6
+ "geo-ai-search-optimization",
7
+ "geo-ai-search-optimization-auto-flow",
8
+ "geo-ai-search-optimization-usage",
9
+ "geo-ai-search-optimization-agent-handoff",
10
+ "geo-ai-search-optimization-repair-loop",
11
+ "geo-ai-search-optimization-completion-report",
12
+ "geo-ai-search-optimization-handoff-bundle",
13
+ "geo-ai-search-optimization-share-pack",
14
+ "geo-ai-search-optimization-export-pack",
15
+ "geo-ai-search-optimization-html-pack",
16
+ "geo-ai-search-optimization-publish-pack"
17
+ ];
18
+
19
+ const SKILL_CATEGORY = {
20
+ "geo-ai-search-optimization": "core",
21
+ "geo-ai-search-optimization-auto-flow": "routing",
22
+ "geo-ai-search-optimization-usage": "guidance",
23
+ "geo-ai-search-optimization-agent-handoff": "execution",
24
+ "geo-ai-search-optimization-repair-loop": "execution",
25
+ "geo-ai-search-optimization-completion-report": "execution",
26
+ "geo-ai-search-optimization-handoff-bundle": "execution",
27
+ "geo-ai-search-optimization-share-pack": "delivery",
28
+ "geo-ai-search-optimization-export-pack": "delivery",
29
+ "geo-ai-search-optimization-html-pack": "delivery",
30
+ "geo-ai-search-optimization-publish-pack": "delivery"
31
+ };
32
+
33
+ const CATEGORY_LABELS = {
34
+ core: "核心 GEO 能力",
35
+ routing: "自动入口",
36
+ guidance: "使用引导",
37
+ execution: "Agent 执行闭环",
38
+ delivery: "交付与分享"
39
+ };
40
+
41
+ function stripQuotes(value) {
42
+ return String(value).trim().replace(/^['"]|['"]$/g, "");
43
+ }
44
+
45
+ function parseYamlBlock(raw) {
46
+ const record = {};
47
+ for (const line of raw.split(/\r?\n/)) {
48
+ const trimmed = line.trim();
49
+ if (!trimmed || trimmed.startsWith("#")) {
50
+ continue;
51
+ }
52
+ const separatorIndex = trimmed.indexOf(":");
53
+ if (separatorIndex === -1) {
54
+ continue;
55
+ }
56
+ const key = trimmed.slice(0, separatorIndex).trim();
57
+ const value = stripQuotes(trimmed.slice(separatorIndex + 1));
58
+ record[key] = value;
59
+ }
60
+ return record;
61
+ }
62
+
63
+ async function readSkillMetadata(skillDir) {
64
+ const skillPath = path.join(skillDir, "SKILL.md");
65
+ const raw = await fs.readFile(skillPath, "utf8");
66
+ const frontmatterMatch = raw.match(/^---\n([\s\S]*?)\n---/);
67
+ const frontmatter = frontmatterMatch ? parseYamlBlock(frontmatterMatch[1]) : {};
68
+ const yamlPath = path.join(skillDir, "agents", "openai.yaml");
69
+
70
+ let interfaceMetadata = {};
71
+ try {
72
+ const yamlRaw = await fs.readFile(yamlPath, "utf8");
73
+ const interfaceMatch = yamlRaw.match(/interface:\n([\s\S]*)/);
74
+ if (interfaceMatch) {
75
+ interfaceMetadata = parseYamlBlock(interfaceMatch[1]);
76
+ }
77
+ } catch {
78
+ interfaceMetadata = {};
79
+ }
80
+
81
+ const directoryName = path.basename(skillDir);
82
+ const name = frontmatter.name || directoryName;
83
+
84
+ return {
85
+ name,
86
+ directoryName,
87
+ description: frontmatter.description || "",
88
+ displayName: interfaceMetadata.display_name || name,
89
+ shortDescription: interfaceMetadata.short_description || frontmatter.description || "",
90
+ defaultPrompt: interfaceMetadata.default_prompt || "",
91
+ category: SKILL_CATEGORY[name] || "delivery",
92
+ skillPath
93
+ };
94
+ }
95
+
96
+ function sortSkills(skills) {
97
+ return [...skills].sort((left, right) => {
98
+ const leftIndex = SKILL_ORDER.indexOf(left.name);
99
+ const rightIndex = SKILL_ORDER.indexOf(right.name);
100
+ if (leftIndex === -1 && rightIndex === -1) {
101
+ return left.name.localeCompare(right.name);
102
+ }
103
+ if (leftIndex === -1) {
104
+ return 1;
105
+ }
106
+ if (rightIndex === -1) {
107
+ return -1;
108
+ }
109
+ return leftIndex - rightIndex;
110
+ });
111
+ }
112
+
113
+ export async function listBundledSkills() {
114
+ const resourcesDir = path.join(getPackageRoot(), "resources");
115
+ const entries = await fs.readdir(resourcesDir, { withFileTypes: true });
116
+ const skillDirs = entries
117
+ .filter((entry) => entry.isDirectory())
118
+ .map((entry) => path.join(resourcesDir, entry.name));
119
+
120
+ const skills = [];
121
+ for (const skillDir of skillDirs) {
122
+ try {
123
+ await fs.access(path.join(skillDir, "SKILL.md"));
124
+ } catch {
125
+ continue;
126
+ }
127
+ skills.push(await readSkillMetadata(skillDir));
128
+ }
129
+
130
+ const ordered = sortSkills(skills);
131
+ const grouped = Object.entries(CATEGORY_LABELS).map(([category, label]) => ({
132
+ category,
133
+ label,
134
+ skills: ordered.filter((skill) => skill.category === category)
135
+ }));
136
+
137
+ return {
138
+ kind: "geo-skill-bundle",
139
+ totalSkills: ordered.length,
140
+ skills: ordered,
141
+ groups: grouped.filter((group) => group.skills.length > 0)
142
+ };
143
+ }
144
+
145
+ export function renderBundledSkillsMarkdown(bundle) {
146
+ const lines = [
147
+ "# GEO Skill Bundle",
148
+ "",
149
+ `- 技能数量:\`${bundle.totalSkills}\``,
150
+ "",
151
+ "## 推荐理解顺序",
152
+ "",
153
+ "- 先看核心 GEO skill。",
154
+ "- 如果 agent 需要自动选 skill,先跑 auto-flow。",
155
+ "- 再看 usage skill,知道什么时候该跑哪个命令。",
156
+ "- 如果要交给 agent 执行,再进入 handoff / apply / completion 这一条执行链。",
157
+ "- 如果要产出给团队分发,再进入 share / export / html / publish 这一条交付链。",
158
+ ""
159
+ ];
160
+
161
+ for (const group of bundle.groups) {
162
+ lines.push(`## ${group.label}`, "");
163
+ for (const skill of group.skills) {
164
+ lines.push(`- ${skill.name}`);
165
+ lines.push(` - 显示名:${skill.displayName}`);
166
+ lines.push(` - 说明:${skill.shortDescription}`);
167
+ lines.push(` - 路径:\`${skill.skillPath}\``);
168
+ }
169
+ lines.push("");
170
+ }
171
+
172
+ return `${lines.join("\n")}\n`;
173
+ }