geo-ai-search-optimization 1.3.6 → 1.3.8

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
@@ -879,6 +879,21 @@ geo-ai-search-optimization help
879
879
  - 主 `cli.js` 现在只保留 `version / help / command routing`
880
880
  - CLI 已经形成 `shell / flow / execution / planning-delivery / site-ops / shared` 六层 command adapter 结构
881
881
 
882
+ ## New in 1.3.7
883
+
884
+ - 开始把重复的 artifact 编排从 adapter 中抽成共享组合器
885
+ - `src/cli-shared.js` 新增 `createArtifactCommandHandler` 和 `createPackCommandHandler`
886
+ - `src/cli-agent-execution-commands.js` 与 `src/cli-planning-delivery-commands.js` 现在改用声明式组合,减少重复的 `create / render / write` handler 模板代码
887
+ - CLI 架构从“按命令族拆文件”进一步推进到“adapter + shared composition”模式
888
+
889
+ ## New in 1.3.8
890
+
891
+ - 继续把共享组合器推广到 `flow` 与 `site-ops` 两层
892
+ - `src/cli-shared.js` 新增 `createStructuredOutputCommandHandler`
893
+ - `src/cli-flow-commands.js` 现在统一通过组合器编排 `auto-flow / agent-orchestrator / agent-resume / agent-continue / agent-state-pack / agent-session`
894
+ - `src/cli-site-ops-commands.js` 现在统一通过组合器编排 `doctor / quick-start / onboard / onboard-url / audit / scan`
895
+ - CLI 架构进一步收敛到“命令族 adapter + shared composition primitives”的模式
896
+
882
897
  ## New in 1.2.20
883
898
 
884
899
  - 新增 `agent-continue`
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "geo-ai-search-optimization",
3
- "version": "1.3.6",
3
+ "version": "1.3.8",
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": {
@@ -20,10 +20,9 @@ import { createAgentRetrospective, renderAgentRetrospectiveMarkdown, writeAgentR
20
20
  import { createAgentStatusBoard, renderAgentStatusBoardMarkdown, writeAgentStatusBoardOutput } from "./agent-status-board.js";
21
21
  import { createAgentRunbook, renderAgentRunbookMarkdown, writeAgentRunbookOutput } from "./agent-runbook.js";
22
22
  import {
23
+ createArtifactCommandHandler,
23
24
  getFlagValue,
24
- getJsonCapableFormat,
25
- getRequiredInput,
26
- writeOrPrintArtifact
25
+ getJsonCapableFormat
27
26
  } from "./cli-shared.js";
28
27
 
29
28
  export const AGENT_EXECUTION_HELP_LINES = [
@@ -38,164 +37,126 @@ export const AGENT_EXECUTION_HELP_LINES = [
38
37
  " geo-ai-search-optimization agent-playbook-pack <input> [--task <id>] [--format <markdown|json>] [--out <file>]"
39
38
  ];
40
39
 
41
- async function handleAgentRunbook(args) {
42
- const input = getRequiredInput(args, "agent-runbook");
43
- const format = getFlagValue(args, "--format");
44
- const artifact = await createAgentRunbook(input, {
40
+ const handleAgentRunbook = createArtifactCommandHandler({
41
+ commandName: "agent-runbook",
42
+ commandLabel: "agent runbook",
43
+ createArtifact: createAgentRunbook,
44
+ renderMarkdown: renderAgentRunbookMarkdown,
45
+ writeOutput: writeAgentRunbookOutput,
46
+ getOptions: (args) => ({
45
47
  intent: getFlagValue(args, "--intent"),
46
- format,
48
+ format: getFlagValue(args, "--format"),
47
49
  taskId: getFlagValue(args, "--task")
48
- });
49
- return writeOrPrintArtifact({
50
- args,
51
- commandName: "agent runbook",
52
- artifact,
53
- renderMarkdown: renderAgentRunbookMarkdown,
54
- writeOutput: writeAgentRunbookOutput,
55
- outputJson: format === "json"
56
- });
57
- }
50
+ })
51
+ });
58
52
 
59
- async function handleAgentExecutor(args) {
60
- const input = getRequiredInput(args, "agent-executor");
61
- const format = getFlagValue(args, "--format");
62
- const artifact = await createAgentExecutor(input, {
53
+ const handleAgentExecutor = createArtifactCommandHandler({
54
+ commandName: "agent-executor",
55
+ commandLabel: "agent executor",
56
+ createArtifact: createAgentExecutor,
57
+ renderMarkdown: renderAgentExecutorMarkdown,
58
+ writeOutput: writeAgentExecutorOutput,
59
+ getOptions: (args) => ({
63
60
  intent: getFlagValue(args, "--intent"),
64
- format,
61
+ format: getFlagValue(args, "--format"),
65
62
  taskId: getFlagValue(args, "--task")
66
- });
67
- return writeOrPrintArtifact({
68
- args,
69
- commandName: "agent executor",
70
- artifact,
71
- renderMarkdown: renderAgentExecutorMarkdown,
72
- writeOutput: writeAgentExecutorOutput,
73
- outputJson: format === "json"
74
- });
75
- }
63
+ })
64
+ });
76
65
 
77
- async function handleAgentBatchExecutor(args) {
78
- const input = getRequiredInput(args, "agent-batch-executor");
79
- const artifact = await createAgentBatchExecutor(input, {
66
+ const handleAgentBatchExecutor = createArtifactCommandHandler({
67
+ commandName: "agent-batch-executor",
68
+ commandLabel: "agent batch executor",
69
+ createArtifact: createAgentBatchExecutor,
70
+ renderMarkdown: renderAgentBatchExecutorMarkdown,
71
+ writeOutput: writeAgentBatchExecutorOutput,
72
+ getOptions: (args) => ({
80
73
  intent: getFlagValue(args, "--intent"),
81
74
  format: getJsonCapableFormat(args),
82
75
  taskId: getFlagValue(args, "--task"),
83
76
  count: getFlagValue(args, "--count")
84
- });
85
- return writeOrPrintArtifact({
86
- args,
87
- commandName: "agent batch executor",
88
- artifact,
89
- renderMarkdown: renderAgentBatchExecutorMarkdown,
90
- writeOutput: writeAgentBatchExecutorOutput,
91
- outputJson: artifact.format === "json"
92
- });
93
- }
77
+ })
78
+ });
94
79
 
95
- async function handleAgentProgressTracker(args) {
96
- const input = getRequiredInput(args, "agent-progress-tracker");
97
- const artifact = await createAgentProgressTracker(input, {
80
+ const handleAgentProgressTracker = createArtifactCommandHandler({
81
+ commandName: "agent-progress-tracker",
82
+ commandLabel: "agent progress tracker",
83
+ createArtifact: createAgentProgressTracker,
84
+ renderMarkdown: renderAgentProgressTrackerMarkdown,
85
+ writeOutput: writeAgentProgressTrackerOutput,
86
+ getOptions: (args) => ({
98
87
  format: getJsonCapableFormat(args),
99
88
  currentTaskId: getFlagValue(args, "--current"),
100
89
  completedPacketIds: getFlagValue(args, "--completed"),
101
90
  blockedReasons: getFlagValue(args, "--blocked")
102
- });
103
- return writeOrPrintArtifact({
104
- args,
105
- commandName: "agent progress tracker",
106
- artifact,
107
- renderMarkdown: renderAgentProgressTrackerMarkdown,
108
- writeOutput: writeAgentProgressTrackerOutput,
109
- outputJson: artifact.format === "json"
110
- });
111
- }
91
+ })
92
+ });
112
93
 
113
- async function handleAgentStatusBoard(args) {
114
- const input = getRequiredInput(args, "agent-status-board");
115
- const artifact = await createAgentStatusBoard(input, {
94
+ const handleAgentStatusBoard = createArtifactCommandHandler({
95
+ commandName: "agent-status-board",
96
+ commandLabel: "agent status board",
97
+ createArtifact: createAgentStatusBoard,
98
+ renderMarkdown: renderAgentStatusBoardMarkdown,
99
+ writeOutput: writeAgentStatusBoardOutput,
100
+ getOptions: (args) => ({
116
101
  format: getJsonCapableFormat(args),
117
102
  currentTaskId: getFlagValue(args, "--current"),
118
103
  completedPacketIds: getFlagValue(args, "--completed"),
119
104
  blockedReasons: getFlagValue(args, "--blocked")
120
- });
121
- return writeOrPrintArtifact({
122
- args,
123
- commandName: "agent status board",
124
- artifact,
125
- renderMarkdown: renderAgentStatusBoardMarkdown,
126
- writeOutput: writeAgentStatusBoardOutput,
127
- outputJson: artifact.format === "json"
128
- });
129
- }
105
+ })
106
+ });
130
107
 
131
- async function handleAgentCheckpoint(args) {
132
- const input = getRequiredInput(args, "agent-checkpoint");
133
- const artifact = await createAgentCheckpoint(input, {
108
+ const handleAgentCheckpoint = createArtifactCommandHandler({
109
+ commandName: "agent-checkpoint",
110
+ commandLabel: "agent checkpoint",
111
+ createArtifact: createAgentCheckpoint,
112
+ renderMarkdown: renderAgentCheckpointMarkdown,
113
+ writeOutput: writeAgentCheckpointOutput,
114
+ getOptions: (args) => ({
134
115
  format: getJsonCapableFormat(args),
135
116
  currentTaskId: getFlagValue(args, "--current"),
136
117
  completedPacketIds: getFlagValue(args, "--completed"),
137
118
  blockedReasons: getFlagValue(args, "--blocked")
138
- });
139
- return writeOrPrintArtifact({
140
- args,
141
- commandName: "agent checkpoint",
142
- artifact,
143
- renderMarkdown: renderAgentCheckpointMarkdown,
144
- writeOutput: writeAgentCheckpointOutput,
145
- outputJson: artifact.format === "json"
146
- });
147
- }
119
+ })
120
+ });
148
121
 
149
- async function handleAgentDecisionLog(args) {
150
- const input = getRequiredInput(args, "agent-decision-log");
151
- const artifact = await createAgentDecisionLog(input, {
122
+ const handleAgentDecisionLog = createArtifactCommandHandler({
123
+ commandName: "agent-decision-log",
124
+ commandLabel: "agent decision log",
125
+ createArtifact: createAgentDecisionLog,
126
+ renderMarkdown: renderAgentDecisionLogMarkdown,
127
+ writeOutput: writeAgentDecisionLogOutput,
128
+ getOptions: (args) => ({
152
129
  format: getJsonCapableFormat(args),
153
130
  appendFrom: getFlagValue(args, "--append-from"),
154
131
  note: getFlagValue(args, "--note"),
155
132
  currentTaskId: getFlagValue(args, "--current"),
156
133
  completedPacketIds: getFlagValue(args, "--completed"),
157
134
  blockedReasons: getFlagValue(args, "--blocked")
158
- });
159
- return writeOrPrintArtifact({
160
- args,
161
- commandName: "agent decision log",
162
- artifact,
163
- renderMarkdown: renderAgentDecisionLogMarkdown,
164
- writeOutput: writeAgentDecisionLogOutput,
165
- outputJson: artifact.format === "json"
166
- });
167
- }
135
+ })
136
+ });
168
137
 
169
- async function handleAgentRetrospective(args) {
170
- const input = getRequiredInput(args, "agent-retrospective");
171
- const artifact = await createAgentRetrospective(input, {
138
+ const handleAgentRetrospective = createArtifactCommandHandler({
139
+ commandName: "agent-retrospective",
140
+ commandLabel: "agent retrospective",
141
+ createArtifact: createAgentRetrospective,
142
+ renderMarkdown: renderAgentRetrospectiveMarkdown,
143
+ writeOutput: writeAgentRetrospectiveOutput,
144
+ getOptions: (args) => ({
172
145
  format: getJsonCapableFormat(args)
173
- });
174
- return writeOrPrintArtifact({
175
- args,
176
- commandName: "agent retrospective",
177
- artifact,
178
- renderMarkdown: renderAgentRetrospectiveMarkdown,
179
- writeOutput: writeAgentRetrospectiveOutput,
180
- outputJson: artifact.format === "json"
181
- });
182
- }
146
+ })
147
+ });
183
148
 
184
- async function handleAgentPlaybookPack(args) {
185
- const input = getRequiredInput(args, "agent-playbook-pack");
186
- const artifact = await createAgentPlaybookPack(input, {
149
+ const handleAgentPlaybookPack = createArtifactCommandHandler({
150
+ commandName: "agent-playbook-pack",
151
+ commandLabel: "agent playbook pack",
152
+ createArtifact: createAgentPlaybookPack,
153
+ renderMarkdown: renderAgentPlaybookPackMarkdown,
154
+ writeOutput: writeAgentPlaybookPackOutput,
155
+ getOptions: (args) => ({
187
156
  format: getJsonCapableFormat(args),
188
157
  taskId: getFlagValue(args, "--task")
189
- });
190
- return writeOrPrintArtifact({
191
- args,
192
- commandName: "agent playbook pack",
193
- artifact,
194
- renderMarkdown: renderAgentPlaybookPackMarkdown,
195
- writeOutput: writeAgentPlaybookPackOutput,
196
- outputJson: artifact.format === "json"
197
- });
198
- }
158
+ })
159
+ });
199
160
 
200
161
  export const AGENT_EXECUTION_COMMAND_HANDLERS = {
201
162
  "agent-runbook": handleAgentRunbook,
@@ -17,11 +17,12 @@ import {
17
17
  } from "./agent-state-pack.js";
18
18
  import { createAgentSession, renderAgentSessionMarkdown, writeAgentSessionOutput } from "./agent-session.js";
19
19
  import {
20
+ createArtifactCommandHandler,
21
+ createStructuredOutputCommandHandler,
20
22
  getFlagValue,
21
23
  getJsonCapableFormat,
22
24
  getRequiredInput,
23
- hasFlag,
24
- writeOrPrintArtifact
25
+ hasFlag
25
26
  } from "./cli-shared.js";
26
27
 
27
28
  export const FLOW_HELP_LINES = [
@@ -33,105 +34,80 @@ export const FLOW_HELP_LINES = [
33
34
  " geo-ai-search-optimization agent-session <input> [--intent <auto|diagnose|guide|execute|share|closeout>] [--json] [--out <file>]"
34
35
  ];
35
36
 
36
- async function handleAutoFlow(args) {
37
- const input = getRequiredInput(args, "auto-flow");
38
- const flow = await createAutoFlow(input, {
39
- intent: getFlagValue(args, "--intent")
40
- });
41
- const outputJson = hasFlag(args, "--json");
42
- const renderedOutput = outputJson ? `${JSON.stringify(flow, null, 2)}\n` : renderAutoFlowMarkdown(flow);
43
- const outputPath = getFlagValue(args, "--out");
44
-
45
- if (outputPath) {
46
- const resolvedOutputPath = await writeAutoFlowOutput(outputPath, renderedOutput);
47
- process.stdout.write(`已保存 auto-flow 结果:${resolvedOutputPath}\n`);
48
- return;
49
- }
50
-
51
- process.stdout.write(renderedOutput);
52
- }
37
+ const handleAutoFlow = createStructuredOutputCommandHandler({
38
+ commandLabel: "auto-flow",
39
+ execute: async (args) => {
40
+ const input = getRequiredInput(args, "auto-flow");
41
+ return createAutoFlow(input, {
42
+ intent: getFlagValue(args, "--intent")
43
+ });
44
+ },
45
+ renderMarkdown: renderAutoFlowMarkdown,
46
+ writeOutput: writeAutoFlowOutput,
47
+ getOutputJson: (args) => hasFlag(args, "--json")
48
+ });
53
49
 
54
- async function handleAgentOrchestrator(args) {
55
- const input = getRequiredInput(args, "agent-orchestrator");
56
- const artifact = await createAgentOrchestrator(input, {
50
+ const handleAgentOrchestrator = createArtifactCommandHandler({
51
+ commandName: "agent-orchestrator",
52
+ commandLabel: "agent-orchestrator",
53
+ createArtifact: createAgentOrchestrator,
54
+ renderMarkdown: renderAgentOrchestratorMarkdown,
55
+ writeOutput: writeAgentOrchestratorOutput,
56
+ getOptions: (args) => ({
57
57
  intent: getFlagValue(args, "--intent"),
58
58
  format: getJsonCapableFormat(args)
59
- });
60
- return writeOrPrintArtifact({
61
- args,
62
- commandName: "agent-orchestrator",
63
- artifact,
64
- renderMarkdown: renderAgentOrchestratorMarkdown,
65
- writeOutput: writeAgentOrchestratorOutput,
66
- outputJson: artifact.format === "json"
67
- });
68
- }
59
+ })
60
+ });
69
61
 
70
- async function handleAgentResume(args) {
71
- const input = getRequiredInput(args, "agent-resume");
72
- const artifact = await createAgentResume(input, {
62
+ const handleAgentResume = createArtifactCommandHandler({
63
+ commandName: "agent-resume",
64
+ commandLabel: "agent-resume",
65
+ createArtifact: createAgentResume,
66
+ renderMarkdown: renderAgentResumeMarkdown,
67
+ writeOutput: writeAgentResumeOutput,
68
+ getOptions: (args) => ({
73
69
  intent: getFlagValue(args, "--intent"),
74
70
  format: getJsonCapableFormat(args)
75
- });
76
- return writeOrPrintArtifact({
77
- args,
78
- commandName: "agent-resume",
79
- artifact,
80
- renderMarkdown: renderAgentResumeMarkdown,
81
- writeOutput: writeAgentResumeOutput,
82
- outputJson: artifact.format === "json"
83
- });
84
- }
71
+ })
72
+ });
85
73
 
86
- async function handleAgentContinue(args) {
87
- const input = getRequiredInput(args, "agent-continue");
88
- const artifact = await createAgentContinue(input, {
74
+ const handleAgentContinue = createArtifactCommandHandler({
75
+ commandName: "agent-continue",
76
+ commandLabel: "agent-continue",
77
+ createArtifact: createAgentContinue,
78
+ renderMarkdown: renderAgentContinueMarkdown,
79
+ writeOutput: writeAgentContinueOutput,
80
+ getOptions: (args) => ({
89
81
  intent: getFlagValue(args, "--intent"),
90
82
  format: getJsonCapableFormat(args)
91
- });
92
- return writeOrPrintArtifact({
93
- args,
94
- commandName: "agent-continue",
95
- artifact,
96
- renderMarkdown: renderAgentContinueMarkdown,
97
- writeOutput: writeAgentContinueOutput,
98
- outputJson: artifact.format === "json"
99
- });
100
- }
83
+ })
84
+ });
101
85
 
102
- async function handleAgentStatePack(args) {
103
- const input = getRequiredInput(args, "agent-state-pack");
104
- const artifact = await createAgentStatePack(input, {
86
+ const handleAgentStatePack = createArtifactCommandHandler({
87
+ commandName: "agent-state-pack",
88
+ commandLabel: "agent-state-pack",
89
+ createArtifact: createAgentStatePack,
90
+ renderMarkdown: renderAgentStatePackMarkdown,
91
+ writeOutput: writeAgentStatePackOutput,
92
+ getOptions: (args) => ({
105
93
  intent: getFlagValue(args, "--intent"),
106
94
  format: getJsonCapableFormat(args),
107
95
  currentTaskId: getFlagValue(args, "--current"),
108
96
  completedPacketIds: getFlagValue(args, "--completed"),
109
97
  blockedReasons: getFlagValue(args, "--blocked")
110
- });
111
- return writeOrPrintArtifact({
112
- args,
113
- commandName: "agent-state-pack",
114
- artifact,
115
- renderMarkdown: renderAgentStatePackMarkdown,
116
- writeOutput: writeAgentStatePackOutput,
117
- outputJson: artifact.format === "json"
118
- });
119
- }
98
+ })
99
+ });
120
100
 
121
- async function handleAgentSession(args) {
122
- const input = getRequiredInput(args, "agent-session");
123
- const artifact = await createAgentSession(input, {
101
+ const handleAgentSession = createArtifactCommandHandler({
102
+ commandName: "agent-session",
103
+ commandLabel: "agent-session",
104
+ createArtifact: createAgentSession,
105
+ renderMarkdown: renderAgentSessionMarkdown,
106
+ writeOutput: writeAgentSessionOutput,
107
+ getOptions: (args) => ({
124
108
  intent: getFlagValue(args, "--intent")
125
- });
126
- return writeOrPrintArtifact({
127
- args,
128
- commandName: "agent-session",
129
- artifact,
130
- renderMarkdown: renderAgentSessionMarkdown,
131
- writeOutput: writeAgentSessionOutput,
132
- outputJson: hasFlag(args, "--json")
133
- });
134
- }
109
+ })
110
+ });
135
111
 
136
112
  export const FLOW_COMMAND_HANDLERS = {
137
113
  "auto-flow": handleAutoFlow,
@@ -17,7 +17,13 @@ import { renderPublishPackMarkdown, writePublishPack } from "./publish-pack.js";
17
17
  import { generateReport, writeReportOutput } from "./report.js";
18
18
  import { createRoadmap, renderRoadmapMarkdown, writeRoadmapOutput } from "./roadmap.js";
19
19
  import { createSharePack, renderSharePackMarkdown, writeSharePackOutput } from "./share-pack.js";
20
- import { getFlagValue, getRequiredInput, parsePositiveInteger, writeOrPrintArtifact } from "./cli-shared.js";
20
+ import {
21
+ createArtifactCommandHandler,
22
+ createPackCommandHandler,
23
+ getFlagValue,
24
+ getRequiredInput,
25
+ parsePositiveInteger
26
+ } from "./cli-shared.js";
21
27
 
22
28
  export const PLANNING_DELIVERY_HELP_LINES = [
23
29
  " geo-ai-search-optimization agent-handoff <input> [--format <markdown|json>] [--out <file>]",
@@ -37,196 +43,160 @@ export const PLANNING_DELIVERY_HELP_LINES = [
37
43
  " geo-ai-search-optimization report <input> [--mode <auto|audit|onboarding|scan>] [--format <markdown|html|json>] [--out <file>]"
38
44
  ];
39
45
 
40
- async function handleAgentHandoff(args) {
41
- const input = getRequiredInput(args, "agent-handoff");
42
- const format = getFlagValue(args, "--format");
43
- const artifact = await createAgentHandoff(input, { format });
44
- return writeOrPrintArtifact({
45
- args,
46
- commandName: "agent handoff",
47
- artifact,
48
- renderMarkdown: renderAgentHandoffMarkdown,
49
- writeOutput: writeAgentHandoffOutput,
50
- outputJson: format === "json"
51
- });
52
- }
53
-
54
- async function handleApplyPlan(args) {
55
- const input = getRequiredInput(args, "apply-plan");
56
- const format = getFlagValue(args, "--format");
57
- const artifact = await createApplyPlan(input, {
58
- format,
46
+ const handleAgentHandoff = createArtifactCommandHandler({
47
+ commandName: "agent-handoff",
48
+ commandLabel: "agent handoff",
49
+ createArtifact: createAgentHandoff,
50
+ renderMarkdown: renderAgentHandoffMarkdown,
51
+ writeOutput: writeAgentHandoffOutput,
52
+ getOptions: (args) => ({
53
+ format: getFlagValue(args, "--format")
54
+ })
55
+ });
56
+
57
+ const handleApplyPlan = createArtifactCommandHandler({
58
+ commandName: "apply-plan",
59
+ commandLabel: "apply plan",
60
+ createArtifact: createApplyPlan,
61
+ renderMarkdown: renderApplyPlanMarkdown,
62
+ writeOutput: writeApplyPlanOutput,
63
+ getOptions: (args) => ({
64
+ format: getFlagValue(args, "--format"),
59
65
  taskId: getFlagValue(args, "--task")
60
- });
61
- return writeOrPrintArtifact({
62
- args,
63
- commandName: "apply plan",
64
- artifact,
65
- renderMarkdown: renderApplyPlanMarkdown,
66
- writeOutput: writeApplyPlanOutput,
67
- outputJson: format === "json"
68
- });
69
- }
70
-
71
- async function handleCompletionReport(args) {
72
- const input = getRequiredInput(args, "completion-report");
73
- const format = getFlagValue(args, "--format");
74
- const artifact = await createCompletionReport(input, { format });
75
- return writeOrPrintArtifact({
76
- args,
77
- commandName: "completion report",
78
- artifact,
79
- renderMarkdown: renderCompletionReportMarkdown,
80
- writeOutput: writeCompletionReportOutput,
81
- outputJson: format === "json"
82
- });
83
- }
84
-
85
- async function handleHandoffBundle(args) {
86
- const input = getRequiredInput(args, "handoff-bundle");
87
- const format = getFlagValue(args, "--format");
88
- const artifact = await createHandoffBundle(input, {
89
- format,
66
+ })
67
+ });
68
+
69
+ const handleCompletionReport = createArtifactCommandHandler({
70
+ commandName: "completion-report",
71
+ commandLabel: "completion report",
72
+ createArtifact: createCompletionReport,
73
+ renderMarkdown: renderCompletionReportMarkdown,
74
+ writeOutput: writeCompletionReportOutput,
75
+ getOptions: (args) => ({
76
+ format: getFlagValue(args, "--format")
77
+ })
78
+ });
79
+
80
+ const handleHandoffBundle = createArtifactCommandHandler({
81
+ commandName: "handoff-bundle",
82
+ commandLabel: "handoff bundle",
83
+ createArtifact: createHandoffBundle,
84
+ renderMarkdown: renderHandoffBundleMarkdown,
85
+ writeOutput: writeHandoffBundleOutput,
86
+ getOptions: (args) => ({
87
+ format: getFlagValue(args, "--format"),
90
88
  taskId: getFlagValue(args, "--task")
91
- });
92
- return writeOrPrintArtifact({
93
- args,
94
- commandName: "handoff bundle",
95
- artifact,
96
- renderMarkdown: renderHandoffBundleMarkdown,
97
- writeOutput: writeHandoffBundleOutput,
98
- outputJson: format === "json"
99
- });
100
- }
101
-
102
- async function handleSharePack(args) {
103
- const input = getRequiredInput(args, "share-pack");
104
- const format = getFlagValue(args, "--format");
105
- const artifact = await createSharePack(input, {
106
- format,
89
+ })
90
+ });
91
+
92
+ const handleSharePack = createArtifactCommandHandler({
93
+ commandName: "share-pack",
94
+ commandLabel: "share pack",
95
+ createArtifact: createSharePack,
96
+ renderMarkdown: renderSharePackMarkdown,
97
+ writeOutput: writeSharePackOutput,
98
+ getOptions: (args) => ({
99
+ format: getFlagValue(args, "--format"),
107
100
  taskId: getFlagValue(args, "--task")
108
- });
109
- return writeOrPrintArtifact({
110
- args,
111
- commandName: "share pack",
112
- artifact,
113
- renderMarkdown: renderSharePackMarkdown,
114
- writeOutput: writeSharePackOutput,
115
- outputJson: format === "json"
116
- });
117
- }
118
-
119
- async function handleExportPack(args) {
120
- const input = getRequiredInput(args, "export-pack");
121
- const pack = await writeExportPack(input, {
101
+ })
102
+ });
103
+
104
+ const handleExportPack = createPackCommandHandler({
105
+ commandName: "export-pack",
106
+ writePack: writeExportPack,
107
+ renderMarkdown: renderExportPackMarkdown,
108
+ getOptions: (args) => ({
122
109
  format: getFlagValue(args, "--format"),
123
110
  taskId: getFlagValue(args, "--task"),
124
111
  outputDir: getFlagValue(args, "--out-dir")
125
- });
126
- process.stdout.write(renderExportPackMarkdown(pack));
127
- }
128
-
129
- async function handleHtmlPack(args) {
130
- const input = getRequiredInput(args, "html-pack");
131
- const pack = await writeHtmlPack(input, {
112
+ })
113
+ });
114
+
115
+ const handleHtmlPack = createPackCommandHandler({
116
+ commandName: "html-pack",
117
+ writePack: writeHtmlPack,
118
+ renderMarkdown: renderHtmlPackMarkdown,
119
+ getOptions: (args) => ({
132
120
  taskId: getFlagValue(args, "--task"),
133
121
  outputDir: getFlagValue(args, "--out-dir")
134
- });
135
- process.stdout.write(renderHtmlPackMarkdown(pack));
136
- }
137
-
138
- async function handlePublishPack(args) {
139
- const input = getRequiredInput(args, "publish-pack");
140
- const pack = await writePublishPack(input, {
122
+ })
123
+ });
124
+
125
+ const handlePublishPack = createPackCommandHandler({
126
+ commandName: "publish-pack",
127
+ writePack: writePublishPack,
128
+ renderMarkdown: renderPublishPackMarkdown,
129
+ getOptions: (args) => ({
141
130
  taskId: getFlagValue(args, "--task"),
142
131
  outputDir: getFlagValue(args, "--out-dir")
143
- });
144
- process.stdout.write(renderPublishPackMarkdown(pack));
145
- }
146
-
147
- async function handleExecSummary(args) {
148
- const input = getRequiredInput(args, "exec-summary");
149
- const format = getFlagValue(args, "--format");
150
- const artifact = await createExecSummary(input, { format });
151
- return writeOrPrintArtifact({
152
- args,
153
- commandName: "exec summary",
154
- artifact,
155
- renderMarkdown: renderExecSummaryMarkdown,
156
- writeOutput: writeExecSummaryOutput,
157
- outputJson: format === "json"
158
- });
159
- }
160
-
161
- async function handleFixPlan(args) {
162
- const input = getRequiredInput(args, "fix-plan");
163
- const format = getFlagValue(args, "--format");
164
- const artifact = await createFixPlan(input, { format });
165
- return writeOrPrintArtifact({
166
- args,
167
- commandName: "fix plan",
168
- artifact,
169
- renderMarkdown: renderFixPlanMarkdown,
170
- writeOutput: writeFixPlanOutput,
171
- outputJson: format === "json"
172
- });
173
- }
174
-
175
- async function handleOwnerBoard(args) {
176
- const input = getRequiredInput(args, "owner-board");
177
- const format = getFlagValue(args, "--format");
178
- const artifact = await createOwnerBoard(input, { format });
179
- return writeOrPrintArtifact({
180
- args,
181
- commandName: "owner board",
182
- artifact,
183
- renderMarkdown: renderOwnerBoardMarkdown,
184
- writeOutput: writeOwnerBoardOutput,
185
- outputJson: format === "json"
186
- });
187
- }
188
-
189
- async function handleMeetingPack(args) {
190
- const input = getRequiredInput(args, "meeting-pack");
191
- const format = getFlagValue(args, "--format");
192
- const artifact = await createMeetingPack(input, { format });
193
- return writeOrPrintArtifact({
194
- args,
195
- commandName: "meeting pack",
196
- artifact,
197
- renderMarkdown: renderMeetingPackMarkdown,
198
- writeOutput: writeMeetingPackOutput,
199
- outputJson: format === "json"
200
- });
201
- }
202
-
203
- async function handlePmBrief(args) {
204
- const input = getRequiredInput(args, "pm-brief");
205
- const format = getFlagValue(args, "--format");
206
- const artifact = await createPmBrief(input, { format });
207
- return writeOrPrintArtifact({
208
- args,
209
- commandName: "PM brief",
210
- artifact,
211
- renderMarkdown: renderPmBriefMarkdown,
212
- writeOutput: writePmBriefOutput,
213
- outputJson: format === "json"
214
- });
215
- }
216
-
217
- async function handleRoadmap(args) {
218
- const input = getRequiredInput(args, "roadmap");
219
- const format = getFlagValue(args, "--format");
220
- const artifact = await createRoadmap(input, { format });
221
- return writeOrPrintArtifact({
222
- args,
223
- commandName: "roadmap",
224
- artifact,
225
- renderMarkdown: renderRoadmapMarkdown,
226
- writeOutput: writeRoadmapOutput,
227
- outputJson: format === "json"
228
- });
229
- }
132
+ })
133
+ });
134
+
135
+ const handleExecSummary = createArtifactCommandHandler({
136
+ commandName: "exec-summary",
137
+ commandLabel: "exec summary",
138
+ createArtifact: createExecSummary,
139
+ renderMarkdown: renderExecSummaryMarkdown,
140
+ writeOutput: writeExecSummaryOutput,
141
+ getOptions: (args) => ({
142
+ format: getFlagValue(args, "--format")
143
+ })
144
+ });
145
+
146
+ const handleFixPlan = createArtifactCommandHandler({
147
+ commandName: "fix-plan",
148
+ commandLabel: "fix plan",
149
+ createArtifact: createFixPlan,
150
+ renderMarkdown: renderFixPlanMarkdown,
151
+ writeOutput: writeFixPlanOutput,
152
+ getOptions: (args) => ({
153
+ format: getFlagValue(args, "--format")
154
+ })
155
+ });
156
+
157
+ const handleOwnerBoard = createArtifactCommandHandler({
158
+ commandName: "owner-board",
159
+ commandLabel: "owner board",
160
+ createArtifact: createOwnerBoard,
161
+ renderMarkdown: renderOwnerBoardMarkdown,
162
+ writeOutput: writeOwnerBoardOutput,
163
+ getOptions: (args) => ({
164
+ format: getFlagValue(args, "--format")
165
+ })
166
+ });
167
+
168
+ const handleMeetingPack = createArtifactCommandHandler({
169
+ commandName: "meeting-pack",
170
+ commandLabel: "meeting pack",
171
+ createArtifact: createMeetingPack,
172
+ renderMarkdown: renderMeetingPackMarkdown,
173
+ writeOutput: writeMeetingPackOutput,
174
+ getOptions: (args) => ({
175
+ format: getFlagValue(args, "--format")
176
+ })
177
+ });
178
+
179
+ const handlePmBrief = createArtifactCommandHandler({
180
+ commandName: "pm-brief",
181
+ commandLabel: "PM brief",
182
+ createArtifact: createPmBrief,
183
+ renderMarkdown: renderPmBriefMarkdown,
184
+ writeOutput: writePmBriefOutput,
185
+ getOptions: (args) => ({
186
+ format: getFlagValue(args, "--format")
187
+ })
188
+ });
189
+
190
+ const handleRoadmap = createArtifactCommandHandler({
191
+ commandName: "roadmap",
192
+ commandLabel: "roadmap",
193
+ createArtifact: createRoadmap,
194
+ renderMarkdown: renderRoadmapMarkdown,
195
+ writeOutput: writeRoadmapOutput,
196
+ getOptions: (args) => ({
197
+ format: getFlagValue(args, "--format")
198
+ })
199
+ });
230
200
 
231
201
  async function handleReport(args) {
232
202
  const input = getRequiredInput(args, "report");
package/src/cli-shared.js CHANGED
@@ -52,3 +52,64 @@ export async function writeOrPrintArtifact({
52
52
 
53
53
  process.stdout.write(renderedOutput);
54
54
  }
55
+
56
+ export function createArtifactCommandHandler({
57
+ commandName,
58
+ commandLabel,
59
+ createArtifact,
60
+ renderMarkdown,
61
+ writeOutput,
62
+ getOptions
63
+ }) {
64
+ return async function artifactCommandHandler(args) {
65
+ const input = getRequiredInput(args, commandName);
66
+ const options = getOptions ? getOptions(args) : {};
67
+ const artifact = await createArtifact(input, options);
68
+ const outputJson = options.format === "json" || hasFlag(args, "--json");
69
+
70
+ return writeOrPrintArtifact({
71
+ args,
72
+ commandName: commandLabel,
73
+ artifact,
74
+ renderMarkdown,
75
+ writeOutput,
76
+ outputJson
77
+ });
78
+ };
79
+ }
80
+
81
+ export function createPackCommandHandler({
82
+ commandName,
83
+ writePack,
84
+ renderMarkdown,
85
+ getOptions
86
+ }) {
87
+ return async function packCommandHandler(args) {
88
+ const input = getRequiredInput(args, commandName);
89
+ const pack = await writePack(input, getOptions ? getOptions(args) : {});
90
+ process.stdout.write(renderMarkdown(pack));
91
+ };
92
+ }
93
+
94
+ export function createStructuredOutputCommandHandler({
95
+ commandLabel,
96
+ execute,
97
+ renderMarkdown,
98
+ writeOutput,
99
+ getOutputJson
100
+ }) {
101
+ return async function structuredOutputCommandHandler(args) {
102
+ const result = await execute(args);
103
+ const outputJson = getOutputJson ? getOutputJson(args, result) : hasFlag(args, "--json");
104
+ const renderedOutput = outputJson ? `${JSON.stringify(result, null, 2)}\n` : renderMarkdown(result);
105
+ const outputPath = getFlagValue(args, "--out");
106
+
107
+ if (outputPath) {
108
+ const resolvedOutputPath = await writeOutput(outputPath, renderedOutput);
109
+ process.stdout.write(`已保存 ${commandLabel} 结果:${resolvedOutputPath}\n`);
110
+ return;
111
+ }
112
+
113
+ process.stdout.write(renderedOutput);
114
+ };
115
+ }
@@ -1,5 +1,10 @@
1
1
  import { auditProject, renderAuditMarkdown, writeAuditOutput } from "./audit.js";
2
- import { getFlagValue, hasFlag, parsePositiveInteger } from "./cli-shared.js";
2
+ import {
3
+ createStructuredOutputCommandHandler,
4
+ getFlagValue,
5
+ hasFlag,
6
+ parsePositiveInteger
7
+ } from "./cli-shared.js";
3
8
  import { renderDoctorMarkdown, runDoctor } from "./doctor.js";
4
9
  import {
5
10
  renderInteractiveOnboardingMarkdown,
@@ -23,74 +28,51 @@ export const SITE_OPS_HELP_LINES = [
23
28
  " geo-ai-search-optimization scan <project-path> [--json] [--out <file>] [--max-file-size <bytes>] [--max-examples <count>]"
24
29
  ];
25
30
 
26
- async function handleDoctor(args) {
27
- const report = await runDoctor();
28
- if (hasFlag(args, "--json")) {
29
- process.stdout.write(`${JSON.stringify(report, null, 2)}\n`);
30
- return;
31
- }
32
-
33
- process.stdout.write(renderDoctorMarkdown(report));
34
- }
35
-
36
- async function handleQuickStart(args) {
37
- const outputJson = hasFlag(args, "--json");
38
- const plan = createQuickStartPlan({});
39
- const renderedOutput = outputJson ? `${JSON.stringify(plan, null, 2)}\n` : renderQuickStartMarkdown(plan);
40
- const outputPath = getFlagValue(args, "--out");
41
-
42
- if (outputPath) {
43
- const resolvedOutputPath = await writeQuickStartOutput(outputPath, renderedOutput);
44
- process.stdout.write(`已保存 quick start 输出:${resolvedOutputPath}\n`);
45
- return;
46
- }
47
-
48
- process.stdout.write(renderedOutput);
49
- }
50
-
51
- async function handleInteractiveOnboard(args) {
52
- const report = await runInteractiveOnboarding({
53
- url: getFlagValue(args, "--url"),
54
- goal: getFlagValue(args, "--goal"),
55
- existingAssets: getFlagValue(args, "--existing-assets")
56
- });
57
-
58
- const outputJson = hasFlag(args, "--json");
59
- const renderedOutput = outputJson
60
- ? `${JSON.stringify(report, null, 2)}\n`
61
- : renderInteractiveOnboardingMarkdown(report);
62
- const outputPath = getFlagValue(args, "--out");
63
-
64
- if (outputPath) {
65
- const resolvedOutputPath = await writeInteractiveOnboardingOutput(outputPath, renderedOutput);
66
- process.stdout.write(`已保存交互式 onboarding 结果:${resolvedOutputPath}\n`);
67
- return;
68
- }
69
-
70
- process.stdout.write(renderedOutput);
71
- }
72
-
73
- async function handleOnboardUrl(args) {
74
- const inputUrl = args.find((value) => !value.startsWith("-"));
75
- if (!inputUrl) {
76
- throw new Error("onboard-url requires a website URL");
77
- }
78
-
79
- const report = await analyzeWebsiteUrl(inputUrl, {});
80
- const outputJson = hasFlag(args, "--json");
81
- const renderedOutput = outputJson
82
- ? `${JSON.stringify(report, null, 2)}\n`
83
- : `${renderWebsiteOnboardingMarkdown(report)}\n`;
84
- const outputPath = getFlagValue(args, "--out");
85
-
86
- if (outputPath) {
87
- const resolvedOutputPath = await writeWebsiteOnboardingOutput(outputPath, renderedOutput);
88
- process.stdout.write(`已保存 onboarding 结果:${resolvedOutputPath}\n`);
89
- return;
90
- }
91
-
92
- process.stdout.write(renderedOutput);
93
- }
31
+ const passthroughWriteOutput = async (outputPath) => outputPath;
32
+
33
+ const handleDoctor = createStructuredOutputCommandHandler({
34
+ commandLabel: "doctor",
35
+ execute: async () => runDoctor(),
36
+ renderMarkdown: renderDoctorMarkdown,
37
+ writeOutput: passthroughWriteOutput,
38
+ getOutputJson: (args) => hasFlag(args, "--json")
39
+ });
40
+
41
+ const handleQuickStart = createStructuredOutputCommandHandler({
42
+ commandLabel: "quick start",
43
+ execute: async () => createQuickStartPlan({}),
44
+ renderMarkdown: renderQuickStartMarkdown,
45
+ writeOutput: writeQuickStartOutput,
46
+ getOutputJson: (args) => hasFlag(args, "--json")
47
+ });
48
+
49
+ const handleInteractiveOnboard = createStructuredOutputCommandHandler({
50
+ commandLabel: "交互式 onboarding",
51
+ execute: async (args) =>
52
+ runInteractiveOnboarding({
53
+ url: getFlagValue(args, "--url"),
54
+ goal: getFlagValue(args, "--goal"),
55
+ existingAssets: getFlagValue(args, "--existing-assets")
56
+ }),
57
+ renderMarkdown: renderInteractiveOnboardingMarkdown,
58
+ writeOutput: writeInteractiveOnboardingOutput,
59
+ getOutputJson: (args) => hasFlag(args, "--json")
60
+ });
61
+
62
+ const handleOnboardUrl = createStructuredOutputCommandHandler({
63
+ commandLabel: "onboarding",
64
+ execute: async (args) => {
65
+ const inputUrl = args.find((value) => !value.startsWith("-"));
66
+ if (!inputUrl) {
67
+ throw new Error("onboard-url requires a website URL");
68
+ }
69
+
70
+ return analyzeWebsiteUrl(inputUrl, {});
71
+ },
72
+ renderMarkdown: (report) => `${renderWebsiteOnboardingMarkdown(report)}\n`,
73
+ writeOutput: writeWebsiteOnboardingOutput,
74
+ getOutputJson: (args) => hasFlag(args, "--json")
75
+ });
94
76
 
95
77
  async function handleInitLlms(args) {
96
78
  const targetDir = args.find((value) => !value.startsWith("-")) || ".";
@@ -133,61 +115,48 @@ async function handleInitSchema(args) {
133
115
  process.stdout.write(`Created ${result.schemaType} schema template at ${result.outputPath}\n`);
134
116
  }
135
117
 
136
- async function handleAudit(args) {
137
- const projectPath = args.find((value) => !value.startsWith("-"));
138
- if (!projectPath) {
139
- throw new Error("audit requires a project path");
140
- }
141
-
142
- const report = await auditProject(projectPath, {
143
- maxFileSize: getFlagValue(args, "--max-file-size")
144
- ? parsePositiveInteger(getFlagValue(args, "--max-file-size"), "--max-file-size")
145
- : undefined,
146
- maxExamples: getFlagValue(args, "--max-examples")
147
- ? parsePositiveInteger(getFlagValue(args, "--max-examples"), "--max-examples")
148
- : undefined
149
- });
150
-
151
- const outputJson = hasFlag(args, "--json");
152
- const renderedOutput = outputJson
153
- ? `${JSON.stringify(report, null, 2)}\n`
154
- : `${renderAuditMarkdown(report)}\n`;
155
- const outputPath = getFlagValue(args, "--out");
156
-
157
- if (outputPath) {
158
- const resolvedOutputPath = await writeAuditOutput(outputPath, renderedOutput);
159
- process.stdout.write(`已保存审计结果:${resolvedOutputPath}\n`);
160
- return;
161
- }
162
-
163
- process.stdout.write(renderedOutput);
164
- }
165
-
166
- async function handleScan(args) {
167
- const projectPath = args.find((value) => !value.startsWith("-"));
168
- if (!projectPath) {
169
- throw new Error("scan requires a project path");
170
- }
171
-
172
- const maxFileSizeValue = getFlagValue(args, "--max-file-size");
173
- const maxExamplesValue = getFlagValue(args, "--max-examples");
174
- const summary = await scanProject(projectPath, {
175
- maxFileSize: maxFileSizeValue ? parsePositiveInteger(maxFileSizeValue, "--max-file-size") : undefined,
176
- maxExamples: maxExamplesValue ? parsePositiveInteger(maxExamplesValue, "--max-examples") : undefined
177
- });
178
-
179
- const outputJson = hasFlag(args, "--json");
180
- const renderedOutput = outputJson ? `${JSON.stringify(summary, null, 2)}\n` : renderScanMarkdown(summary);
181
- const outputPath = getFlagValue(args, "--out");
182
-
183
- if (outputPath) {
184
- const resolvedOutputPath = await writeScanOutput(outputPath, renderedOutput);
185
- process.stdout.write(`已保存扫描结果:${resolvedOutputPath}\n`);
186
- return;
187
- }
188
-
189
- process.stdout.write(renderedOutput);
190
- }
118
+ const handleAudit = createStructuredOutputCommandHandler({
119
+ commandLabel: "审计",
120
+ execute: async (args) => {
121
+ const projectPath = args.find((value) => !value.startsWith("-"));
122
+ if (!projectPath) {
123
+ throw new Error("audit requires a project path");
124
+ }
125
+
126
+ return auditProject(projectPath, {
127
+ maxFileSize: getFlagValue(args, "--max-file-size")
128
+ ? parsePositiveInteger(getFlagValue(args, "--max-file-size"), "--max-file-size")
129
+ : undefined,
130
+ maxExamples: getFlagValue(args, "--max-examples")
131
+ ? parsePositiveInteger(getFlagValue(args, "--max-examples"), "--max-examples")
132
+ : undefined
133
+ });
134
+ },
135
+ renderMarkdown: (report) => `${renderAuditMarkdown(report)}\n`,
136
+ writeOutput: writeAuditOutput,
137
+ getOutputJson: (args) => hasFlag(args, "--json")
138
+ });
139
+
140
+ const handleScan = createStructuredOutputCommandHandler({
141
+ commandLabel: "扫描",
142
+ execute: async (args) => {
143
+ const projectPath = args.find((value) => !value.startsWith("-"));
144
+ if (!projectPath) {
145
+ throw new Error("scan requires a project path");
146
+ }
147
+
148
+ const maxFileSizeValue = getFlagValue(args, "--max-file-size");
149
+ const maxExamplesValue = getFlagValue(args, "--max-examples");
150
+
151
+ return scanProject(projectPath, {
152
+ maxFileSize: maxFileSizeValue ? parsePositiveInteger(maxFileSizeValue, "--max-file-size") : undefined,
153
+ maxExamples: maxExamplesValue ? parsePositiveInteger(maxExamplesValue, "--max-examples") : undefined
154
+ });
155
+ },
156
+ renderMarkdown: renderScanMarkdown,
157
+ writeOutput: writeScanOutput,
158
+ getOutputJson: (args) => hasFlag(args, "--json")
159
+ });
191
160
 
192
161
  export const SITE_OPS_COMMAND_HANDLERS = {
193
162
  doctor: handleDoctor,