geo-ai-search-optimization 1.2.4 → 1.2.6
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 +51 -0
- package/package.json +1 -1
- package/resources/geo-ai-search-optimization/SKILL.md +1 -0
- package/resources/geo-ai-search-optimization/references/skill-bundle-map.md +81 -0
- package/resources/geo-ai-search-optimization-export-pack/agents/openai.yaml +4 -0
- package/resources/geo-ai-search-optimization-handoff-bundle/agents/openai.yaml +4 -0
- package/resources/geo-ai-search-optimization-html-pack/agents/openai.yaml +4 -0
- package/resources/geo-ai-search-optimization-publish-pack/SKILL.md +26 -0
- package/resources/geo-ai-search-optimization-publish-pack/agents/openai.yaml +4 -0
- package/resources/geo-ai-search-optimization-share-pack/agents/openai.yaml +4 -0
- package/resources/geo-ai-search-optimization-usage/SKILL.md +19 -14
- package/src/cli.js +38 -0
- package/src/index.js +2 -0
- package/src/install-skill.js +43 -1
- package/src/publish-pack.js +420 -0
- package/src/skills.js +169 -0
package/README.md
CHANGED
|
@@ -24,6 +24,23 @@ 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
|
|
27
44
|
|
|
28
45
|
## Quick Start
|
|
29
46
|
|
|
@@ -261,6 +278,24 @@ geo-ai-search-optimization html-pack https://example.com --out-dir ./exports/exa
|
|
|
261
278
|
- `agent.html`
|
|
262
279
|
- `share.html`
|
|
263
280
|
|
|
281
|
+
## Publish Pack 命令
|
|
282
|
+
|
|
283
|
+
如果你希望直接生成一份“最终可交付包”,同时给人和 agent 使用,现在可以用 `publish-pack`:
|
|
284
|
+
|
|
285
|
+
```bash
|
|
286
|
+
geo-ai-search-optimization publish-pack ./your-site --out-dir ./exports/your-site-publish-pack
|
|
287
|
+
geo-ai-search-optimization publish-pack https://example.com --out-dir ./exports/example-publish-pack
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
`publish-pack` 会一次生成:
|
|
291
|
+
|
|
292
|
+
- `site/`: 可浏览的静态 HTML 页面
|
|
293
|
+
- `documents/`: 给 PM、工程、管理层直接转发的 Markdown 文档
|
|
294
|
+
- `data/`: 给 agent 或自动化流程使用的 JSON 工件
|
|
295
|
+
- `START-HERE.md`: 给人的起始说明
|
|
296
|
+
- `AGENT-START.md`: 给 agent 的执行入口
|
|
297
|
+
- `manifest.json`: 机器可读总索引
|
|
298
|
+
|
|
264
299
|
## Fix Plan 命令
|
|
265
300
|
|
|
266
301
|
如果你已经跑过 `audit`、`report` 或 `onboard-url`,下一步就可以直接把结果转成 PM 待办清单:
|
|
@@ -400,6 +435,7 @@ geo-ai-search-optimization onboard --url https://example.com --json --out ./repo
|
|
|
400
435
|
geo-ai-search-optimization
|
|
401
436
|
geo-ai-search-optimization install
|
|
402
437
|
geo-ai-search-optimization install --target ./tmp/custom-skills --json
|
|
438
|
+
geo-ai-search-optimization skills
|
|
403
439
|
geo-ai-search-optimization where
|
|
404
440
|
geo-ai-search-optimization doctor
|
|
405
441
|
geo-ai-search-optimization quick-start
|
|
@@ -418,6 +454,7 @@ geo-ai-search-optimization handoff-bundle ./my-site
|
|
|
418
454
|
geo-ai-search-optimization share-pack ./my-site
|
|
419
455
|
geo-ai-search-optimization export-pack ./my-site --out-dir ./exports/my-site-pack
|
|
420
456
|
geo-ai-search-optimization html-pack ./my-site --out-dir ./exports/my-site-html
|
|
457
|
+
geo-ai-search-optimization publish-pack ./my-site --out-dir ./exports/my-site-publish-pack
|
|
421
458
|
geo-ai-search-optimization exec-summary ./my-site
|
|
422
459
|
geo-ai-search-optimization fix-plan ./my-site
|
|
423
460
|
geo-ai-search-optimization owner-board ./my-site
|
|
@@ -433,6 +470,20 @@ geo-ai-search-optimization version
|
|
|
433
470
|
geo-ai-search-optimization help
|
|
434
471
|
```
|
|
435
472
|
|
|
473
|
+
## New in 1.2.6
|
|
474
|
+
|
|
475
|
+
- 新增 `skills` 命令,直接列出整套 GEO 技能包
|
|
476
|
+
- 安装后会额外写入 `.skill-bundle.json`,方便 agent 自动发现技能
|
|
477
|
+
- 为缺失的 skill 补齐 `agents/openai.yaml`
|
|
478
|
+
- 主 skill 新增 `skill-bundle-map` 参考,usage skill 也同步纳入 `publish-pack` 和 `skills`
|
|
479
|
+
|
|
480
|
+
## New in 1.2.5
|
|
481
|
+
|
|
482
|
+
- 新增 `publish-pack`
|
|
483
|
+
- 一次生成最终可交付目录,而不是手动拼 HTML、Markdown、JSON
|
|
484
|
+
- 增加 `START-HERE.md`、`AGENT-START.md`、`manifest.json`
|
|
485
|
+
- 新增 `geo-ai-search-optimization-publish-pack` skill,帮助 agent 直接接手最终交付包
|
|
486
|
+
|
|
436
487
|
## New in 1.2.4
|
|
437
488
|
|
|
438
489
|
- 新增 `html-pack`
|
package/package.json
CHANGED
|
@@ -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,81 @@
|
|
|
1
|
+
# GEO Skill Bundle Map
|
|
2
|
+
|
|
3
|
+
## Table of Contents
|
|
4
|
+
|
|
5
|
+
- Core skill
|
|
6
|
+
- Usage guide
|
|
7
|
+
- Agent execution loop
|
|
8
|
+
- Delivery chain
|
|
9
|
+
|
|
10
|
+
## Core skill
|
|
11
|
+
|
|
12
|
+
### `geo-ai-search-optimization`
|
|
13
|
+
|
|
14
|
+
Use this first when the task is still about diagnosing or improving GEO readiness itself.
|
|
15
|
+
|
|
16
|
+
Best for:
|
|
17
|
+
|
|
18
|
+
- auditing a website or codebase
|
|
19
|
+
- improving answerability, structure, and citation readiness
|
|
20
|
+
- deciding what the highest-impact GEO fixes are
|
|
21
|
+
|
|
22
|
+
## Usage guide
|
|
23
|
+
|
|
24
|
+
### `geo-ai-search-optimization-usage`
|
|
25
|
+
|
|
26
|
+
Use this when the next agent or user does not know which command or skill to use next.
|
|
27
|
+
|
|
28
|
+
Best for:
|
|
29
|
+
|
|
30
|
+
- onboarding new users
|
|
31
|
+
- explaining command order
|
|
32
|
+
- translating a GEO request into concrete CLI steps
|
|
33
|
+
|
|
34
|
+
## Agent execution loop
|
|
35
|
+
|
|
36
|
+
Use these when the work is moving from analysis into action.
|
|
37
|
+
|
|
38
|
+
### `geo-ai-search-optimization-agent-handoff`
|
|
39
|
+
|
|
40
|
+
Turn GEO findings into a package that another agent can continue from.
|
|
41
|
+
|
|
42
|
+
### `geo-ai-search-optimization-repair-loop`
|
|
43
|
+
|
|
44
|
+
Continue from `apply-plan` style execution tasks and close the loop on actual fixes.
|
|
45
|
+
|
|
46
|
+
### `geo-ai-search-optimization-completion-report`
|
|
47
|
+
|
|
48
|
+
Summarize what finished, what still blocks progress, and what should happen next.
|
|
49
|
+
|
|
50
|
+
### `geo-ai-search-optimization-handoff-bundle`
|
|
51
|
+
|
|
52
|
+
Use when one artifact needs to contain handoff, execution, and closeout together.
|
|
53
|
+
|
|
54
|
+
## Delivery chain
|
|
55
|
+
|
|
56
|
+
Use these when the output needs to be shared, exported, or handed off to humans and agents.
|
|
57
|
+
|
|
58
|
+
### `geo-ai-search-optimization-share-pack`
|
|
59
|
+
|
|
60
|
+
Prepare role-specific views for PM, engineering, management, and the next agent.
|
|
61
|
+
|
|
62
|
+
### `geo-ai-search-optimization-export-pack`
|
|
63
|
+
|
|
64
|
+
Generate a disk folder with ready-to-send markdown or JSON deliverables.
|
|
65
|
+
|
|
66
|
+
### `geo-ai-search-optimization-html-pack`
|
|
67
|
+
|
|
68
|
+
Generate browsable HTML pages for quick sharing.
|
|
69
|
+
|
|
70
|
+
### `geo-ai-search-optimization-publish-pack`
|
|
71
|
+
|
|
72
|
+
Generate the final delivery folder that combines:
|
|
73
|
+
|
|
74
|
+
- `site/` HTML pages
|
|
75
|
+
- `documents/` markdown files
|
|
76
|
+
- `data/` JSON artifacts
|
|
77
|
+
- `START-HERE.md`
|
|
78
|
+
- `AGENT-START.md`
|
|
79
|
+
- `manifest.json`
|
|
80
|
+
|
|
81
|
+
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,26 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: geo-ai-search-optimization-publish-pack
|
|
3
|
+
description: Generate a final GEO delivery package that combines HTML pages, markdown documents, JSON artifacts, and agent start files. Use when a PM or agent wants one folder that can be shared directly with humans and handed off directly to another agent.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# GEO Publish Pack
|
|
7
|
+
|
|
8
|
+
Use this skill when the user wants a single final package instead of separate GEO artifacts.
|
|
9
|
+
|
|
10
|
+
`GEO = Generative Engine Optimization`
|
|
11
|
+
|
|
12
|
+
## What it generates
|
|
13
|
+
|
|
14
|
+
- `site/` for browsable HTML pages
|
|
15
|
+
- `documents/` for markdown files that can be sent to PM, engineering, or management
|
|
16
|
+
- `data/` for machine-readable JSON artifacts
|
|
17
|
+
- `START-HERE.md` for people
|
|
18
|
+
- `AGENT-START.md` for the next agent
|
|
19
|
+
- `manifest.json` for automation and indexing
|
|
20
|
+
|
|
21
|
+
## Best use
|
|
22
|
+
|
|
23
|
+
- generate one delivery folder at the end of an analysis round
|
|
24
|
+
- share `site/index.html` for quick browsing
|
|
25
|
+
- point the next agent to `AGENT-START.md`
|
|
26
|
+
- use `manifest.json` when another tool needs to ingest the package
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
interface:
|
|
2
|
+
display_name: "GEO Publish Pack"
|
|
3
|
+
short_description: "Build a final delivery folder for humans and agents"
|
|
4
|
+
default_prompt: "Use $geo-ai-search-optimization-publish-pack to generate a final GEO delivery package with HTML, markdown, JSON, and an agent handoff entrypoint."
|
|
@@ -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,20 +13,21 @@ 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
|
|
17
|
-
|
|
18
|
-
1. `
|
|
19
|
-
2. `
|
|
20
|
-
3. `
|
|
21
|
-
4. `
|
|
22
|
-
5. `
|
|
23
|
-
6. `
|
|
24
|
-
7. `
|
|
25
|
-
8. `
|
|
26
|
-
9. `
|
|
27
|
-
10. `
|
|
28
|
-
11. `
|
|
29
|
-
12. `
|
|
16
|
+
The package is best explained as thirteen layers:
|
|
17
|
+
|
|
18
|
+
1. `skills`: inspect the bundled skill package
|
|
19
|
+
2. `onboard-url` / `onboard`: first look
|
|
20
|
+
3. `scan`: raw signal check
|
|
21
|
+
4. `audit` / `report`: diagnosis
|
|
22
|
+
5. `fix-plan` / `owner-board`: execution planning
|
|
23
|
+
6. `agent-handoff`: agent takeover package
|
|
24
|
+
7. `apply-plan`: execution loop
|
|
25
|
+
8. `completion-report`: closeout
|
|
26
|
+
9. `handoff-bundle`: all-in-one package
|
|
27
|
+
10. `share-pack`: audience-ready delivery
|
|
28
|
+
11. `export-pack`: folder export
|
|
29
|
+
12. `html-pack` / `publish-pack`: browsable and final delivery output
|
|
30
|
+
13. `pm-brief` / `roadmap`: stakeholder alignment
|
|
30
31
|
|
|
31
32
|
## Recommended command order
|
|
32
33
|
|
|
@@ -51,6 +52,7 @@ npx geo-ai-search-optimization handoff-bundle ./your-site
|
|
|
51
52
|
npx geo-ai-search-optimization share-pack ./your-site
|
|
52
53
|
npx geo-ai-search-optimization export-pack ./your-site
|
|
53
54
|
npx geo-ai-search-optimization html-pack ./your-site
|
|
55
|
+
npx geo-ai-search-optimization publish-pack ./your-site
|
|
54
56
|
npx geo-ai-search-optimization owner-board ./your-site
|
|
55
57
|
npx geo-ai-search-optimization roadmap ./your-site
|
|
56
58
|
```
|
|
@@ -59,6 +61,7 @@ npx geo-ai-search-optimization roadmap ./your-site
|
|
|
59
61
|
|
|
60
62
|
- `onboard-url`: first-time website check from a live URL
|
|
61
63
|
- `onboard`: interactive first-time onboarding
|
|
64
|
+
- `skills`: list the bundled skills and decide which skill or command chain fits the task
|
|
62
65
|
- `scan`: check raw technical signals across files
|
|
63
66
|
- `audit`: get GEO score, issue areas, blockers, and PM action list
|
|
64
67
|
- `report`: export audit/onboarding/scan into readable files
|
|
@@ -70,6 +73,7 @@ npx geo-ai-search-optimization roadmap ./your-site
|
|
|
70
73
|
- `share-pack`: prepare role-specific shareable outputs for PM, engineering, management, and agents
|
|
71
74
|
- `export-pack`: generate a folder with multiple ready-to-send files
|
|
72
75
|
- `html-pack`: generate static HTML pages for sharing and browsing
|
|
76
|
+
- `publish-pack`: generate the final delivery folder for humans and agents together
|
|
73
77
|
- `owner-board`: group tasks by PM / engineering / SEO / content
|
|
74
78
|
- `pm-brief`: prepare a PM summary for meetings and prioritization
|
|
75
79
|
- `roadmap`: turn tasks into a 2-week / 4-week execution sequence
|
|
@@ -88,6 +92,7 @@ When explaining the tool to a user:
|
|
|
88
92
|
- if the user wants something ready to forward to multiple audiences, move them to `share-pack`
|
|
89
93
|
- if the user wants actual files on disk for each audience, move them to `export-pack`
|
|
90
94
|
- if the user wants browsable HTML pages, move them to `html-pack`
|
|
95
|
+
- if the user wants one final package with HTML, markdown, JSON, and an agent entrypoint, move them to `publish-pack`
|
|
91
96
|
- if the user already has a report, move them to `fix-plan`, `owner-board`, or `roadmap`
|
|
92
97
|
|
|
93
98
|
Read [references/usage-patterns.md](references/usage-patterns.md) when you need response patterns and command selection examples.
|
package/src/cli.js
CHANGED
|
@@ -22,6 +22,7 @@ import { renderHtmlPackMarkdown, writeHtmlPack } from "./html-pack.js";
|
|
|
22
22
|
import { createOwnerBoard, renderOwnerBoardMarkdown, writeOwnerBoardOutput } from "./owner-board.js";
|
|
23
23
|
import { createMeetingPack, renderMeetingPackMarkdown, writeMeetingPackOutput } from "./meeting-pack.js";
|
|
24
24
|
import { createPmBrief, renderPmBriefMarkdown, writePmBriefOutput } from "./pm-brief.js";
|
|
25
|
+
import { renderPublishPackMarkdown, writePublishPack } from "./publish-pack.js";
|
|
25
26
|
import { generateReport, writeReportOutput } from "./report.js";
|
|
26
27
|
import { createRoadmap, renderRoadmapMarkdown, writeRoadmapOutput } from "./roadmap.js";
|
|
27
28
|
import {
|
|
@@ -32,6 +33,7 @@ import {
|
|
|
32
33
|
import { createQuickStartPlan, renderQuickStartMarkdown, writeQuickStartOutput } from "./quick-start.js";
|
|
33
34
|
import { createSchemaTemplate } from "./schema.js";
|
|
34
35
|
import { createSharePack, renderSharePackMarkdown, writeSharePackOutput } from "./share-pack.js";
|
|
36
|
+
import { listBundledSkills, renderBundledSkillsMarkdown } from "./skills.js";
|
|
35
37
|
import { analyzeWebsiteUrl, renderWebsiteOnboardingMarkdown, writeWebsiteOnboardingOutput } from "./url-onboarding.js";
|
|
36
38
|
|
|
37
39
|
let cachedVersion;
|
|
@@ -55,6 +57,7 @@ function printHelp() {
|
|
|
55
57
|
"Usage:",
|
|
56
58
|
" geo-ai-search-optimization",
|
|
57
59
|
" geo-ai-search-optimization install [--target <dir>] [--json]",
|
|
60
|
+
" geo-ai-search-optimization skills [--json]",
|
|
58
61
|
" geo-ai-search-optimization where",
|
|
59
62
|
" geo-ai-search-optimization doctor [--json]",
|
|
60
63
|
" geo-ai-search-optimization quick-start [--json] [--out <file>]",
|
|
@@ -70,6 +73,7 @@ function printHelp() {
|
|
|
70
73
|
" geo-ai-search-optimization share-pack <input> [--task <id>] [--format <markdown|json>] [--out <file>]",
|
|
71
74
|
" geo-ai-search-optimization export-pack <input> [--task <id>] [--format <markdown|json>] [--out-dir <dir>]",
|
|
72
75
|
" geo-ai-search-optimization html-pack <input> [--task <id>] [--out-dir <dir>]",
|
|
76
|
+
" geo-ai-search-optimization publish-pack <input> [--task <id>] [--out-dir <dir>]",
|
|
73
77
|
" geo-ai-search-optimization exec-summary <input> [--format <markdown|json>] [--out <file>]",
|
|
74
78
|
" geo-ai-search-optimization fix-plan <input> [--format <markdown|json>] [--out <file>]",
|
|
75
79
|
" geo-ai-search-optimization owner-board <input> [--format <markdown|json>] [--out <file>]",
|
|
@@ -134,6 +138,16 @@ function handleWhere() {
|
|
|
134
138
|
);
|
|
135
139
|
}
|
|
136
140
|
|
|
141
|
+
async function handleSkills(args) {
|
|
142
|
+
const bundle = await listBundledSkills();
|
|
143
|
+
if (hasFlag(args, "--json")) {
|
|
144
|
+
process.stdout.write(`${JSON.stringify(bundle, null, 2)}\n`);
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
process.stdout.write(renderBundledSkillsMarkdown(bundle));
|
|
149
|
+
}
|
|
150
|
+
|
|
137
151
|
async function handleDoctor(args) {
|
|
138
152
|
const report = await runDoctor();
|
|
139
153
|
if (hasFlag(args, "--json")) {
|
|
@@ -506,6 +520,20 @@ async function handleHtmlPack(args) {
|
|
|
506
520
|
process.stdout.write(renderHtmlPackMarkdown(pack));
|
|
507
521
|
}
|
|
508
522
|
|
|
523
|
+
async function handlePublishPack(args) {
|
|
524
|
+
const input = args.find((value) => !value.startsWith("-"));
|
|
525
|
+
if (!input) {
|
|
526
|
+
throw new Error("publish-pack 需要一个输入值,可以是项目路径、网站网址或已导出的 JSON 工件");
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
const pack = await writePublishPack(input, {
|
|
530
|
+
taskId: getFlagValue(args, "--task"),
|
|
531
|
+
outputDir: getFlagValue(args, "--out-dir")
|
|
532
|
+
});
|
|
533
|
+
|
|
534
|
+
process.stdout.write(renderPublishPackMarkdown(pack));
|
|
535
|
+
}
|
|
536
|
+
|
|
509
537
|
async function handleExecSummary(args) {
|
|
510
538
|
const input = args.find((value) => !value.startsWith("-"));
|
|
511
539
|
if (!input) {
|
|
@@ -639,6 +667,11 @@ export async function runCli(args = []) {
|
|
|
639
667
|
return;
|
|
640
668
|
}
|
|
641
669
|
|
|
670
|
+
if (command === "skills") {
|
|
671
|
+
await handleSkills(rest);
|
|
672
|
+
return;
|
|
673
|
+
}
|
|
674
|
+
|
|
642
675
|
if (command === "where") {
|
|
643
676
|
handleWhere();
|
|
644
677
|
return;
|
|
@@ -714,6 +747,11 @@ export async function runCli(args = []) {
|
|
|
714
747
|
return;
|
|
715
748
|
}
|
|
716
749
|
|
|
750
|
+
if (command === "publish-pack") {
|
|
751
|
+
await handlePublishPack(rest);
|
|
752
|
+
return;
|
|
753
|
+
}
|
|
754
|
+
|
|
717
755
|
if (command === "exec-summary") {
|
|
718
756
|
await handleExecSummary(rest);
|
|
719
757
|
return;
|
package/src/index.js
CHANGED
|
@@ -20,10 +20,12 @@ export { createExportPack, renderExportPackMarkdown, writeExportPack } from "./e
|
|
|
20
20
|
export { createMeetingPack, renderMeetingPackMarkdown, writeMeetingPackOutput } from "./meeting-pack.js";
|
|
21
21
|
export { createOwnerBoard, renderOwnerBoardMarkdown, writeOwnerBoardOutput } from "./owner-board.js";
|
|
22
22
|
export { createPmBrief, renderPmBriefMarkdown, writePmBriefOutput } from "./pm-brief.js";
|
|
23
|
+
export { renderPublishPackMarkdown, writePublishPack } from "./publish-pack.js";
|
|
23
24
|
export { createQuickStartPlan, renderQuickStartMarkdown, writeQuickStartOutput } from "./quick-start.js";
|
|
24
25
|
export { generateReport, writeReportOutput } from "./report.js";
|
|
25
26
|
export { createRoadmap, renderRoadmapMarkdown, writeRoadmapOutput } from "./roadmap.js";
|
|
26
27
|
export { createSchemaTemplate } from "./schema.js";
|
|
27
28
|
export { scanProject, renderScanMarkdown, writeScanOutput } from "./scan.js";
|
|
28
29
|
export { createSharePack, renderSharePackMarkdown, writeSharePackOutput } from "./share-pack.js";
|
|
30
|
+
export { listBundledSkills, renderBundledSkillsMarkdown } from "./skills.js";
|
|
29
31
|
export { analyzeWebsiteUrl, renderWebsiteOnboardingMarkdown, writeWebsiteOnboardingOutput } from "./url-onboarding.js";
|
package/src/install-skill.js
CHANGED
|
@@ -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
|
}
|
|
@@ -0,0 +1,420 @@
|
|
|
1
|
+
import fs from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { createExecSummary, renderExecSummaryMarkdown } from "./exec-summary.js";
|
|
4
|
+
import { createHandoffBundle, renderHandoffBundleMarkdown } from "./handoff-bundle.js";
|
|
5
|
+
import { createOwnerBoard, renderOwnerBoardMarkdown } from "./owner-board.js";
|
|
6
|
+
import { createPmBrief, renderPmBriefMarkdown } from "./pm-brief.js";
|
|
7
|
+
import { createSharePack, renderSharePackMarkdown } from "./share-pack.js";
|
|
8
|
+
|
|
9
|
+
function sanitizeSegment(value) {
|
|
10
|
+
return String(value)
|
|
11
|
+
.replace(/[^a-z0-9-_]+/gi, "-")
|
|
12
|
+
.replace(/^-+|-+$/g, "")
|
|
13
|
+
.toLowerCase();
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function buildBaseName(sourceType, source) {
|
|
17
|
+
if (sourceType === "url") {
|
|
18
|
+
try {
|
|
19
|
+
const hostname = new URL(source).hostname || "website";
|
|
20
|
+
return sanitizeSegment(hostname) || "website";
|
|
21
|
+
} catch {
|
|
22
|
+
return "website";
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
return sanitizeSegment(path.basename(source)) || "site";
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function escapeHtml(value) {
|
|
30
|
+
return String(value)
|
|
31
|
+
.replaceAll("&", "&")
|
|
32
|
+
.replaceAll("<", "<")
|
|
33
|
+
.replaceAll(">", ">")
|
|
34
|
+
.replaceAll('"', """)
|
|
35
|
+
.replaceAll("'", "'");
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function wrapHtml(title, body) {
|
|
39
|
+
return [
|
|
40
|
+
"<!doctype html>",
|
|
41
|
+
'<html lang="zh-Hant">',
|
|
42
|
+
"<head>",
|
|
43
|
+
'<meta charset="utf-8">',
|
|
44
|
+
'<meta name="viewport" content="width=device-width, initial-scale=1">',
|
|
45
|
+
`<title>${escapeHtml(title)}</title>`,
|
|
46
|
+
"<style>",
|
|
47
|
+
"body{font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif;margin:0;background:#f5f7fb;color:#16324f;}",
|
|
48
|
+
".shell{max-width:1100px;margin:0 auto;padding:32px 20px 64px;}",
|
|
49
|
+
".hero,.card{background:#fff;border:1px solid #d9e2ec;border-radius:16px;box-shadow:0 8px 24px rgba(15,23,42,.06);}",
|
|
50
|
+
".hero{padding:28px;margin-bottom:24px;}",
|
|
51
|
+
".grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:16px;}",
|
|
52
|
+
".card{padding:20px;}",
|
|
53
|
+
"h1,h2,h3{margin-top:0;color:#102a43;}",
|
|
54
|
+
"ul{padding-left:20px;line-height:1.7;}",
|
|
55
|
+
"a{color:#0b7285;text-decoration:none;}",
|
|
56
|
+
"code,pre{background:#f4f7fb;border-radius:10px;}",
|
|
57
|
+
"pre{padding:16px;white-space:pre-wrap;overflow:auto;}",
|
|
58
|
+
".meta{display:flex;gap:12px;flex-wrap:wrap;color:#486581;}",
|
|
59
|
+
"</style>",
|
|
60
|
+
"</head>",
|
|
61
|
+
"<body>",
|
|
62
|
+
body,
|
|
63
|
+
"</body>",
|
|
64
|
+
"</html>"
|
|
65
|
+
].join("");
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function buildDocumentHtml(title, content) {
|
|
69
|
+
return wrapHtml(
|
|
70
|
+
title,
|
|
71
|
+
`
|
|
72
|
+
<main class="shell">
|
|
73
|
+
<section class="hero">
|
|
74
|
+
<h1>${escapeHtml(title)}</h1>
|
|
75
|
+
</section>
|
|
76
|
+
<section class="card">
|
|
77
|
+
<pre>${escapeHtml(content)}</pre>
|
|
78
|
+
</section>
|
|
79
|
+
</main>
|
|
80
|
+
`
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function buildIndexHtml(pack) {
|
|
85
|
+
const cards = pack.htmlFiles
|
|
86
|
+
.map(
|
|
87
|
+
(file) => `
|
|
88
|
+
<article class="card">
|
|
89
|
+
<h3>${escapeHtml(file.label)}</h3>
|
|
90
|
+
<p>${escapeHtml(file.description)}</p>
|
|
91
|
+
<a href="./${escapeHtml(file.filename)}">打开 ${escapeHtml(file.filename)}</a>
|
|
92
|
+
</article>
|
|
93
|
+
`
|
|
94
|
+
)
|
|
95
|
+
.join("");
|
|
96
|
+
|
|
97
|
+
return wrapHtml(
|
|
98
|
+
"GEO Publish Pack",
|
|
99
|
+
`
|
|
100
|
+
<main class="shell">
|
|
101
|
+
<section class="hero">
|
|
102
|
+
<h1>GEO Publish Pack</h1>
|
|
103
|
+
<div class="meta">
|
|
104
|
+
<span>来源:${escapeHtml(pack.source)}</span>
|
|
105
|
+
<span>类型:${escapeHtml(pack.sourceType)}</span>
|
|
106
|
+
<span>目录:${escapeHtml(pack.outputDir)}</span>
|
|
107
|
+
</div>
|
|
108
|
+
<p>这是一个适合直接交付、分发与交接的 GEO 最终发布包,覆盖 PM、工程、管理层与下一位 agent。</p>
|
|
109
|
+
</section>
|
|
110
|
+
<section class="grid">
|
|
111
|
+
${cards}
|
|
112
|
+
</section>
|
|
113
|
+
</main>
|
|
114
|
+
`
|
|
115
|
+
);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function buildStartHereContent(pack) {
|
|
119
|
+
return [
|
|
120
|
+
"# GEO Publish Pack",
|
|
121
|
+
"",
|
|
122
|
+
`- 输入来源:\`${pack.source}\``,
|
|
123
|
+
`- 来源类型:\`${pack.sourceType}\``,
|
|
124
|
+
`- 生成时间:${pack.generatedAt}`,
|
|
125
|
+
"",
|
|
126
|
+
"## 这份包怎么用",
|
|
127
|
+
"",
|
|
128
|
+
"1. 人先打开 `site/index.html` 看总览。",
|
|
129
|
+
"2. PM 看 `documents/pm-brief.md` 或 `site/pm.html`。",
|
|
130
|
+
"3. 工程 / 执行团队看 `documents/owner-board.md` 或 `site/engineering.html`。",
|
|
131
|
+
"4. 管理层看 `documents/exec-summary.md` 或 `site/exec.html`。",
|
|
132
|
+
"5. 下一位 agent 从 `AGENT-START.md` 和 `data/agent-handoff-bundle.json` 开始。",
|
|
133
|
+
"",
|
|
134
|
+
"## 目录结构",
|
|
135
|
+
"",
|
|
136
|
+
"- `site/`: 可浏览的静态 HTML 页面。",
|
|
137
|
+
"- `documents/`: 可直接分享的 Markdown 文档。",
|
|
138
|
+
"- `data/`: 给 agent 或自动化流程使用的 JSON 工件。",
|
|
139
|
+
"- `START-HERE.md`: 给人看的起始说明。",
|
|
140
|
+
"- `AGENT-START.md`: 给 agent 的执行入口。",
|
|
141
|
+
"- `manifest.json`: 机器可读的总索引。",
|
|
142
|
+
"",
|
|
143
|
+
"## 快速入口",
|
|
144
|
+
"",
|
|
145
|
+
`- 总览页:\`${pack.quickLinks.indexHtml}\``,
|
|
146
|
+
`- PM:\`${pack.quickLinks.pmDocument}\``,
|
|
147
|
+
`- 工程:\`${pack.quickLinks.engineeringDocument}\``,
|
|
148
|
+
`- 管理层:\`${pack.quickLinks.execDocument}\``,
|
|
149
|
+
`- Agent:\`${pack.quickLinks.agentData}\``,
|
|
150
|
+
"",
|
|
151
|
+
"## 推荐分发顺序",
|
|
152
|
+
""
|
|
153
|
+
].concat(pack.sharePack.audienceOrder.map((item) => `- ${item}`)).join("\n") + "\n";
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
function buildAgentStartContent(pack) {
|
|
157
|
+
const firstPacket = pack.handoffBundle.applyPlan.packets[0];
|
|
158
|
+
const firstValidation = firstPacket?.validationCommands ?? [];
|
|
159
|
+
const nextRoundTasks = pack.handoffBundle.completionReport.nextRoundTasks.slice(0, 3);
|
|
160
|
+
|
|
161
|
+
const lines = [
|
|
162
|
+
"# AGENT START",
|
|
163
|
+
"",
|
|
164
|
+
"你现在接手这份 GEO 发布包。",
|
|
165
|
+
"",
|
|
166
|
+
`- 输入来源:\`${pack.source}\``,
|
|
167
|
+
`- 来源类型:\`${pack.sourceType}\``,
|
|
168
|
+
`- 执行模式:\`${pack.handoffBundle.summary.executionMode}\``,
|
|
169
|
+
`- 执行类型:\`${pack.handoffBundle.summary.executionType}\``,
|
|
170
|
+
"",
|
|
171
|
+
"## 先读什么",
|
|
172
|
+
"",
|
|
173
|
+
"1. `data/agent-handoff-bundle.json`",
|
|
174
|
+
"2. `documents/share-pack.md`",
|
|
175
|
+
"3. `documents/owner-board.md`",
|
|
176
|
+
"4. `documents/pm-brief.md`",
|
|
177
|
+
"",
|
|
178
|
+
"## 你现在该做什么",
|
|
179
|
+
""
|
|
180
|
+
];
|
|
181
|
+
|
|
182
|
+
if (firstPacket) {
|
|
183
|
+
lines.push(`- 先从任务包 \`${firstPacket.id}\` 开始:${firstPacket.title}`);
|
|
184
|
+
lines.push(`- 负责人建议:${firstPacket.owner}`);
|
|
185
|
+
} else {
|
|
186
|
+
lines.push("- 当前没有具体任务包,请先根据 share-pack 和 owner-board 重新确认优先级。");
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
if (pack.handoffBundle.summary.executionMode === "implementation-ready") {
|
|
190
|
+
lines.push("- 当前已有本地项目上下文,可以直接检查模板、metadata、schema 或内容文件并实施修复。");
|
|
191
|
+
} else if (pack.handoffBundle.summary.executionMode === "advice-only") {
|
|
192
|
+
lines.push("- 当前更适合先给结构化建议,不要假设你已经拥有代码仓库写入权限。");
|
|
193
|
+
lines.push("- 如果用户能提供仓库或 CMS 上下文,请在拿到后重新生成 publish-pack,再进入直接执行。");
|
|
194
|
+
} else {
|
|
195
|
+
lines.push("- 当前输入主要来自已有工件,先根据工件整理修复顺序,再等待更多上下文。");
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
lines.push("", "## 建议先跑的验证命令", "");
|
|
199
|
+
|
|
200
|
+
for (const command of firstValidation) {
|
|
201
|
+
lines.push(`- \`${command}\``);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
if (firstValidation.length === 0) {
|
|
205
|
+
lines.push("- 当前没有额外验证命令,先检查 handoff bundle 中的任务描述。");
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
lines.push("", "## 下一轮任务", "");
|
|
209
|
+
|
|
210
|
+
for (const task of nextRoundTasks) {
|
|
211
|
+
lines.push(`- ${task.id}|${task.title}|${task.nextAction}`);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
if (nextRoundTasks.length === 0) {
|
|
215
|
+
lines.push("- 暂无下一轮任务,先根据当前工件确认缺口。");
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
lines.push(
|
|
219
|
+
"",
|
|
220
|
+
"## 完成后怎么回报",
|
|
221
|
+
"",
|
|
222
|
+
"- 完成后更新 completion-report 风格的总结。",
|
|
223
|
+
"- 说明你检查了哪些文件、修改了什么、如何验证、还剩什么风险。",
|
|
224
|
+
"- 如果无法直接修复,要明确说明缺少哪些上下文或权限。"
|
|
225
|
+
);
|
|
226
|
+
|
|
227
|
+
return `${lines.join("\n")}\n`;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
function buildManifest(pack) {
|
|
231
|
+
return {
|
|
232
|
+
kind: "geo-publish-pack",
|
|
233
|
+
generatedAt: pack.generatedAt,
|
|
234
|
+
source: pack.source,
|
|
235
|
+
sourceType: pack.sourceType,
|
|
236
|
+
outputDir: pack.outputDir,
|
|
237
|
+
directories: {
|
|
238
|
+
site: pack.siteDir,
|
|
239
|
+
documents: pack.documentsDir,
|
|
240
|
+
data: pack.dataDir
|
|
241
|
+
},
|
|
242
|
+
quickLinks: pack.quickLinks,
|
|
243
|
+
shareAudienceOrder: pack.sharePack.audienceOrder,
|
|
244
|
+
executionMode: pack.handoffBundle.summary.executionMode,
|
|
245
|
+
executionType: pack.handoffBundle.summary.executionType
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
export async function writePublishPack(input, options = {}) {
|
|
250
|
+
const [sharePack, pmBrief, ownerBoard, execSummary, handoffBundle] = await Promise.all([
|
|
251
|
+
createSharePack(input, { format: "json", taskId: options.taskId }),
|
|
252
|
+
createPmBrief(input, { format: "json" }),
|
|
253
|
+
createOwnerBoard(input, { format: "json" }),
|
|
254
|
+
createExecSummary(input, { format: "json" }),
|
|
255
|
+
createHandoffBundle(input, { format: "json", taskId: options.taskId })
|
|
256
|
+
]);
|
|
257
|
+
|
|
258
|
+
const baseName = buildBaseName(sharePack.sourceType, sharePack.source);
|
|
259
|
+
const outputDir = path.resolve(options.outputDir || `./exports/${baseName}-geo-publish-pack`);
|
|
260
|
+
const documentsDir = path.join(outputDir, "documents");
|
|
261
|
+
const dataDir = path.join(outputDir, "data");
|
|
262
|
+
const siteDir = path.join(outputDir, "site");
|
|
263
|
+
|
|
264
|
+
await fs.mkdir(documentsDir, { recursive: true });
|
|
265
|
+
await fs.mkdir(dataDir, { recursive: true });
|
|
266
|
+
await fs.mkdir(siteDir, { recursive: true });
|
|
267
|
+
|
|
268
|
+
const documentFiles = [
|
|
269
|
+
{ key: "share_pack", filename: "share-pack.md", content: renderSharePackMarkdown(sharePack) },
|
|
270
|
+
{ key: "pm", filename: "pm-brief.md", content: renderPmBriefMarkdown(pmBrief) },
|
|
271
|
+
{ key: "engineering", filename: "owner-board.md", content: renderOwnerBoardMarkdown(ownerBoard) },
|
|
272
|
+
{ key: "exec", filename: "exec-summary.md", content: renderExecSummaryMarkdown(execSummary) },
|
|
273
|
+
{ key: "agent", filename: "agent-handoff-bundle.md", content: renderHandoffBundleMarkdown(handoffBundle) }
|
|
274
|
+
];
|
|
275
|
+
|
|
276
|
+
const dataFiles = [
|
|
277
|
+
{ key: "share_pack", filename: "share-pack.json", content: `${JSON.stringify(sharePack, null, 2)}\n` },
|
|
278
|
+
{ key: "pm", filename: "pm-brief.json", content: `${JSON.stringify(pmBrief, null, 2)}\n` },
|
|
279
|
+
{ key: "engineering", filename: "owner-board.json", content: `${JSON.stringify(ownerBoard, null, 2)}\n` },
|
|
280
|
+
{ key: "exec", filename: "exec-summary.json", content: `${JSON.stringify(execSummary, null, 2)}\n` },
|
|
281
|
+
{ key: "agent", filename: "agent-handoff-bundle.json", content: `${JSON.stringify(handoffBundle, null, 2)}\n` }
|
|
282
|
+
];
|
|
283
|
+
|
|
284
|
+
const htmlFiles = [
|
|
285
|
+
{
|
|
286
|
+
key: "index",
|
|
287
|
+
label: "总览页",
|
|
288
|
+
description: "适合直接打开给团队看的入口。",
|
|
289
|
+
filename: "index.html",
|
|
290
|
+
content: ""
|
|
291
|
+
},
|
|
292
|
+
{
|
|
293
|
+
key: "pm",
|
|
294
|
+
label: "PM Brief",
|
|
295
|
+
description: "给 PM 的优先级和推进摘要。",
|
|
296
|
+
filename: "pm.html",
|
|
297
|
+
content: documentFiles.find((file) => file.key === "pm")?.content || ""
|
|
298
|
+
},
|
|
299
|
+
{
|
|
300
|
+
key: "engineering",
|
|
301
|
+
label: "Owner Board",
|
|
302
|
+
description: "给工程与执行团队的任务视图。",
|
|
303
|
+
filename: "engineering.html",
|
|
304
|
+
content: documentFiles.find((file) => file.key === "engineering")?.content || ""
|
|
305
|
+
},
|
|
306
|
+
{
|
|
307
|
+
key: "exec",
|
|
308
|
+
label: "Exec Summary",
|
|
309
|
+
description: "给管理层的高层摘要。",
|
|
310
|
+
filename: "exec.html",
|
|
311
|
+
content: documentFiles.find((file) => file.key === "exec")?.content || ""
|
|
312
|
+
},
|
|
313
|
+
{
|
|
314
|
+
key: "agent",
|
|
315
|
+
label: "Agent Bundle",
|
|
316
|
+
description: "给下一位 agent 的交接入口。",
|
|
317
|
+
filename: "agent.html",
|
|
318
|
+
content: documentFiles.find((file) => file.key === "agent")?.content || ""
|
|
319
|
+
},
|
|
320
|
+
{
|
|
321
|
+
key: "share",
|
|
322
|
+
label: "Share Pack",
|
|
323
|
+
description: "给协调者的总览文档。",
|
|
324
|
+
filename: "share.html",
|
|
325
|
+
content: documentFiles.find((file) => file.key === "share_pack")?.content || ""
|
|
326
|
+
}
|
|
327
|
+
];
|
|
328
|
+
|
|
329
|
+
const generatedAt = new Date().toISOString();
|
|
330
|
+
const quickLinks = {
|
|
331
|
+
startHere: "START-HERE.md",
|
|
332
|
+
agentStart: "AGENT-START.md",
|
|
333
|
+
manifest: "manifest.json",
|
|
334
|
+
indexHtml: "site/index.html",
|
|
335
|
+
pmDocument: "documents/pm-brief.md",
|
|
336
|
+
engineeringDocument: "documents/owner-board.md",
|
|
337
|
+
execDocument: "documents/exec-summary.md",
|
|
338
|
+
agentData: "data/agent-handoff-bundle.json"
|
|
339
|
+
};
|
|
340
|
+
|
|
341
|
+
const pack = {
|
|
342
|
+
kind: "geo-publish-pack",
|
|
343
|
+
generatedAt,
|
|
344
|
+
source: sharePack.source,
|
|
345
|
+
sourceType: sharePack.sourceType,
|
|
346
|
+
outputDir,
|
|
347
|
+
documentsDir,
|
|
348
|
+
dataDir,
|
|
349
|
+
siteDir,
|
|
350
|
+
quickLinks,
|
|
351
|
+
sharePack,
|
|
352
|
+
handoffBundle,
|
|
353
|
+
htmlFiles
|
|
354
|
+
};
|
|
355
|
+
|
|
356
|
+
await Promise.all(
|
|
357
|
+
documentFiles.map((file) => fs.writeFile(path.join(documentsDir, file.filename), file.content, "utf8"))
|
|
358
|
+
);
|
|
359
|
+
await Promise.all(dataFiles.map((file) => fs.writeFile(path.join(dataDir, file.filename), file.content, "utf8")));
|
|
360
|
+
|
|
361
|
+
const indexHtml = buildIndexHtml(pack);
|
|
362
|
+
await fs.writeFile(path.join(siteDir, "index.html"), indexHtml, "utf8");
|
|
363
|
+
|
|
364
|
+
for (const file of htmlFiles.filter((file) => file.key !== "index")) {
|
|
365
|
+
await fs.writeFile(path.join(siteDir, file.filename), buildDocumentHtml(file.label, file.content), "utf8");
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
await fs.writeFile(path.join(outputDir, "START-HERE.md"), buildStartHereContent(pack), "utf8");
|
|
369
|
+
await fs.writeFile(path.join(outputDir, "AGENT-START.md"), buildAgentStartContent(pack), "utf8");
|
|
370
|
+
|
|
371
|
+
const manifest = buildManifest(pack);
|
|
372
|
+
await fs.writeFile(path.join(outputDir, "manifest.json"), `${JSON.stringify(manifest, null, 2)}\n`, "utf8");
|
|
373
|
+
|
|
374
|
+
return {
|
|
375
|
+
...manifest,
|
|
376
|
+
files: {
|
|
377
|
+
root: [
|
|
378
|
+
path.join(outputDir, "START-HERE.md"),
|
|
379
|
+
path.join(outputDir, "AGENT-START.md"),
|
|
380
|
+
path.join(outputDir, "manifest.json")
|
|
381
|
+
],
|
|
382
|
+
documents: documentFiles.map((file) => path.join(documentsDir, file.filename)),
|
|
383
|
+
data: dataFiles.map((file) => path.join(dataDir, file.filename)),
|
|
384
|
+
site: htmlFiles.map((file) => path.join(siteDir, file.filename))
|
|
385
|
+
}
|
|
386
|
+
};
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
export function renderPublishPackMarkdown(pack) {
|
|
390
|
+
const lines = [
|
|
391
|
+
"# GEO Publish Pack",
|
|
392
|
+
"",
|
|
393
|
+
`- 输入来源:\`${pack.source}\``,
|
|
394
|
+
`- 来源类型:\`${pack.sourceType}\``,
|
|
395
|
+
`- 输出目录:\`${pack.outputDir}\``,
|
|
396
|
+
`- 生成时间:${pack.generatedAt}`,
|
|
397
|
+
"",
|
|
398
|
+
"## 快速入口",
|
|
399
|
+
"",
|
|
400
|
+
`- 人类入口:\`${path.join(pack.outputDir, pack.quickLinks.startHere)}\``,
|
|
401
|
+
`- Agent 入口:\`${path.join(pack.outputDir, pack.quickLinks.agentStart)}\``,
|
|
402
|
+
`- 静态页面:\`${path.join(pack.outputDir, pack.quickLinks.indexHtml)}\``,
|
|
403
|
+
`- 机器索引:\`${path.join(pack.outputDir, pack.quickLinks.manifest)}\``,
|
|
404
|
+
"",
|
|
405
|
+
"## 目录",
|
|
406
|
+
"",
|
|
407
|
+
`- 文档:\`${pack.directories.documents}\``,
|
|
408
|
+
`- 数据:\`${pack.directories.data}\``,
|
|
409
|
+
`- 页面:\`${pack.directories.site}\``,
|
|
410
|
+
"",
|
|
411
|
+
"## 适合怎么用",
|
|
412
|
+
""
|
|
413
|
+
];
|
|
414
|
+
|
|
415
|
+
lines.push("- 先把 `site/index.html` 发给需要快速了解现状的人。");
|
|
416
|
+
lines.push("- 把 `documents/pm-brief.md`、`documents/owner-board.md`、`documents/exec-summary.md` 分别发给不同角色。");
|
|
417
|
+
lines.push("- 让下一位 agent 从 `AGENT-START.md` 和 `data/agent-handoff-bundle.json` 继续接手。");
|
|
418
|
+
|
|
419
|
+
return `${lines.join("\n")}\n`;
|
|
420
|
+
}
|
package/src/skills.js
ADDED
|
@@ -0,0 +1,169 @@
|
|
|
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-usage",
|
|
8
|
+
"geo-ai-search-optimization-agent-handoff",
|
|
9
|
+
"geo-ai-search-optimization-repair-loop",
|
|
10
|
+
"geo-ai-search-optimization-completion-report",
|
|
11
|
+
"geo-ai-search-optimization-handoff-bundle",
|
|
12
|
+
"geo-ai-search-optimization-share-pack",
|
|
13
|
+
"geo-ai-search-optimization-export-pack",
|
|
14
|
+
"geo-ai-search-optimization-html-pack",
|
|
15
|
+
"geo-ai-search-optimization-publish-pack"
|
|
16
|
+
];
|
|
17
|
+
|
|
18
|
+
const SKILL_CATEGORY = {
|
|
19
|
+
"geo-ai-search-optimization": "core",
|
|
20
|
+
"geo-ai-search-optimization-usage": "guidance",
|
|
21
|
+
"geo-ai-search-optimization-agent-handoff": "execution",
|
|
22
|
+
"geo-ai-search-optimization-repair-loop": "execution",
|
|
23
|
+
"geo-ai-search-optimization-completion-report": "execution",
|
|
24
|
+
"geo-ai-search-optimization-handoff-bundle": "execution",
|
|
25
|
+
"geo-ai-search-optimization-share-pack": "delivery",
|
|
26
|
+
"geo-ai-search-optimization-export-pack": "delivery",
|
|
27
|
+
"geo-ai-search-optimization-html-pack": "delivery",
|
|
28
|
+
"geo-ai-search-optimization-publish-pack": "delivery"
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
const CATEGORY_LABELS = {
|
|
32
|
+
core: "核心 GEO 能力",
|
|
33
|
+
guidance: "使用引导",
|
|
34
|
+
execution: "Agent 执行闭环",
|
|
35
|
+
delivery: "交付与分享"
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
function stripQuotes(value) {
|
|
39
|
+
return String(value).trim().replace(/^['"]|['"]$/g, "");
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function parseYamlBlock(raw) {
|
|
43
|
+
const record = {};
|
|
44
|
+
for (const line of raw.split(/\r?\n/)) {
|
|
45
|
+
const trimmed = line.trim();
|
|
46
|
+
if (!trimmed || trimmed.startsWith("#")) {
|
|
47
|
+
continue;
|
|
48
|
+
}
|
|
49
|
+
const separatorIndex = trimmed.indexOf(":");
|
|
50
|
+
if (separatorIndex === -1) {
|
|
51
|
+
continue;
|
|
52
|
+
}
|
|
53
|
+
const key = trimmed.slice(0, separatorIndex).trim();
|
|
54
|
+
const value = stripQuotes(trimmed.slice(separatorIndex + 1));
|
|
55
|
+
record[key] = value;
|
|
56
|
+
}
|
|
57
|
+
return record;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
async function readSkillMetadata(skillDir) {
|
|
61
|
+
const skillPath = path.join(skillDir, "SKILL.md");
|
|
62
|
+
const raw = await fs.readFile(skillPath, "utf8");
|
|
63
|
+
const frontmatterMatch = raw.match(/^---\n([\s\S]*?)\n---/);
|
|
64
|
+
const frontmatter = frontmatterMatch ? parseYamlBlock(frontmatterMatch[1]) : {};
|
|
65
|
+
const yamlPath = path.join(skillDir, "agents", "openai.yaml");
|
|
66
|
+
|
|
67
|
+
let interfaceMetadata = {};
|
|
68
|
+
try {
|
|
69
|
+
const yamlRaw = await fs.readFile(yamlPath, "utf8");
|
|
70
|
+
const interfaceMatch = yamlRaw.match(/interface:\n([\s\S]*)/);
|
|
71
|
+
if (interfaceMatch) {
|
|
72
|
+
interfaceMetadata = parseYamlBlock(interfaceMatch[1]);
|
|
73
|
+
}
|
|
74
|
+
} catch {
|
|
75
|
+
interfaceMetadata = {};
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const directoryName = path.basename(skillDir);
|
|
79
|
+
const name = frontmatter.name || directoryName;
|
|
80
|
+
|
|
81
|
+
return {
|
|
82
|
+
name,
|
|
83
|
+
directoryName,
|
|
84
|
+
description: frontmatter.description || "",
|
|
85
|
+
displayName: interfaceMetadata.display_name || name,
|
|
86
|
+
shortDescription: interfaceMetadata.short_description || frontmatter.description || "",
|
|
87
|
+
defaultPrompt: interfaceMetadata.default_prompt || "",
|
|
88
|
+
category: SKILL_CATEGORY[name] || "delivery",
|
|
89
|
+
skillPath
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function sortSkills(skills) {
|
|
94
|
+
return [...skills].sort((left, right) => {
|
|
95
|
+
const leftIndex = SKILL_ORDER.indexOf(left.name);
|
|
96
|
+
const rightIndex = SKILL_ORDER.indexOf(right.name);
|
|
97
|
+
if (leftIndex === -1 && rightIndex === -1) {
|
|
98
|
+
return left.name.localeCompare(right.name);
|
|
99
|
+
}
|
|
100
|
+
if (leftIndex === -1) {
|
|
101
|
+
return 1;
|
|
102
|
+
}
|
|
103
|
+
if (rightIndex === -1) {
|
|
104
|
+
return -1;
|
|
105
|
+
}
|
|
106
|
+
return leftIndex - rightIndex;
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
export async function listBundledSkills() {
|
|
111
|
+
const resourcesDir = path.join(getPackageRoot(), "resources");
|
|
112
|
+
const entries = await fs.readdir(resourcesDir, { withFileTypes: true });
|
|
113
|
+
const skillDirs = entries
|
|
114
|
+
.filter((entry) => entry.isDirectory())
|
|
115
|
+
.map((entry) => path.join(resourcesDir, entry.name));
|
|
116
|
+
|
|
117
|
+
const skills = [];
|
|
118
|
+
for (const skillDir of skillDirs) {
|
|
119
|
+
try {
|
|
120
|
+
await fs.access(path.join(skillDir, "SKILL.md"));
|
|
121
|
+
} catch {
|
|
122
|
+
continue;
|
|
123
|
+
}
|
|
124
|
+
skills.push(await readSkillMetadata(skillDir));
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const ordered = sortSkills(skills);
|
|
128
|
+
const grouped = Object.entries(CATEGORY_LABELS).map(([category, label]) => ({
|
|
129
|
+
category,
|
|
130
|
+
label,
|
|
131
|
+
skills: ordered.filter((skill) => skill.category === category)
|
|
132
|
+
}));
|
|
133
|
+
|
|
134
|
+
return {
|
|
135
|
+
kind: "geo-skill-bundle",
|
|
136
|
+
totalSkills: ordered.length,
|
|
137
|
+
skills: ordered,
|
|
138
|
+
groups: grouped.filter((group) => group.skills.length > 0)
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
export function renderBundledSkillsMarkdown(bundle) {
|
|
143
|
+
const lines = [
|
|
144
|
+
"# GEO Skill Bundle",
|
|
145
|
+
"",
|
|
146
|
+
`- 技能数量:\`${bundle.totalSkills}\``,
|
|
147
|
+
"",
|
|
148
|
+
"## 推荐理解顺序",
|
|
149
|
+
"",
|
|
150
|
+
"- 先看核心 GEO skill。",
|
|
151
|
+
"- 再看 usage skill,知道什么时候该跑哪个命令。",
|
|
152
|
+
"- 如果要交给 agent 执行,再进入 handoff / apply / completion 这一条执行链。",
|
|
153
|
+
"- 如果要产出给团队分发,再进入 share / export / html / publish 这一条交付链。",
|
|
154
|
+
""
|
|
155
|
+
];
|
|
156
|
+
|
|
157
|
+
for (const group of bundle.groups) {
|
|
158
|
+
lines.push(`## ${group.label}`, "");
|
|
159
|
+
for (const skill of group.skills) {
|
|
160
|
+
lines.push(`- ${skill.name}`);
|
|
161
|
+
lines.push(` - 显示名:${skill.displayName}`);
|
|
162
|
+
lines.push(` - 说明:${skill.shortDescription}`);
|
|
163
|
+
lines.push(` - 路径:\`${skill.skillPath}\``);
|
|
164
|
+
}
|
|
165
|
+
lines.push("");
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
return `${lines.join("\n")}\n`;
|
|
169
|
+
}
|