@cloud411716/fancy-webnovel 0.1.36 → 0.1.38

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/index.js CHANGED
@@ -1,241 +1,10 @@
1
- /**
2
- * registry.js — 合并注册所有命令
3
- * fancy-bootstrap + fancy-scan
4
- *
5
- * 所有消息模板 → scripts/templates.js
6
- * 所有基础设施 → scripts/infra.js
7
- */
8
- import { fileURLToPath } from 'url';
9
- import { dirname, join } from 'path';
10
- import { existsSync } from 'fs';
11
- import { notify, llmFollowup, spawnScript } from './infra.js';
12
- import { scan, bootstrap, PLATFORM_TABLE, platformLabel } from './templates.js';
13
-
14
- const __dirname = dirname(fileURLToPath(import.meta.url));
15
- const SCRIPTS_DIR = join(__dirname, 'plugins', 'fancy-bootstrap', 'scripts');
16
-
17
- // ---------------------------------------------------------------------------
18
- // 路径工具
19
- // ---------------------------------------------------------------------------
20
-
21
- function isValidPath(p) {
22
- return !p.includes('..') && !/[<>:"'|?*[\x00-\x1f]/.test(p) && !/\s/.test(p);
23
- }
24
-
25
- // ---------------------------------------------------------------------------
26
- // fancy-bootstrap
27
- // ---------------------------------------------------------------------------
28
-
29
- async function applyBootstrap(ctx) {
30
- ctx.commands.register({
31
- name: 'fancy-bootstrap',
32
- description: '📁 初始化项目根 ( usage: /fancy-bootstrap [项目根] )',
33
- handler: async (invocation) => {
34
- const session = invocation.agent.session;
35
- const rawInput = invocation.rawInput ? invocation.rawInput.trim() : '';
36
-
37
- let projectRoot = rawInput;
38
-
39
- // 无参数 → 弹窗
40
- if (!projectRoot) {
41
- const result = await ctx.userQuestions.ask({
42
- questions: [bootstrap.askPath()],
43
- });
44
- projectRoot = result.answers[0]?.custom?.trim() || '';
45
- }
46
-
47
- if (!projectRoot) {
48
- notify(session, bootstrap.notify({ type: 'no_path' }));
49
- return { kind: 'success', text: '' };
50
- }
51
-
52
- if (projectRoot === '.') {
53
- projectRoot = process.cwd();
54
- notify(session, bootstrap.notify({ type: 'using_cwd', projectRoot }));
55
- }
56
-
57
- if (!isValidPath(projectRoot)) {
58
- notify(session, bootstrap.notify({ type: 'invalid_path' }));
59
- return { kind: 'success', text: '' };
60
- }
61
-
62
- if (existsSync(join(projectRoot, '.fancy-deployed'))) {
63
- notify(session, bootstrap.notify({ type: 'already_initialized' }));
64
- return { kind: 'success', text: '' };
65
- }
66
-
67
- const confirm = await ctx.userQuestions.ask({
68
- questions: [{
69
- id: 'confirm',
70
- question: bootstrap.notify({ type: 'confirm', projectRoot }),
71
- detail: projectRoot,
72
- hideCustomInput: true,
73
- options: bootstrap.confirmOptions(),
74
- }],
75
- });
76
- const ans = confirm.answers[0];
77
- const chosen = ans?.selected?.[0] ?? ans?.custom;
78
- if (chosen !== '确认创建') {
79
- notify(session, bootstrap.notify({ type: 'cancelled' }));
80
- return { kind: 'success', text: '' };
81
- }
82
-
83
- notify(session, bootstrap.notify({ type: 'initializing', projectRoot }));
84
- const result = await spawnScript('node', [join(SCRIPTS_DIR, 'init.js'), projectRoot], { timeout: 30000 });
85
-
86
- if (result.ok) {
87
- notify(session, bootstrap.notify({ type: 'success' }));
88
- } else {
89
- const err = (typeof result.error === 'object' && result.error !== null)
90
- ? (result.error.message || JSON.stringify(result.error))
91
- : String(result.error || '未知错误');
92
- notify(session, bootstrap.notify({ type: 'error', err }));
93
- }
94
-
95
- return { kind: 'success', text: '' };
96
- }
97
- });
98
- }
99
-
100
- // ---------------------------------------------------------------------------
101
- // fancy-scan
102
- // ---------------------------------------------------------------------------
103
-
104
- async function applyScan(ctx) {
105
- ctx.commands.register({
106
- name: 'fancy-scan',
107
- description: '📊 扫榜分析 ( usage: /fancy-scan [平台] [篇幅] )',
108
- handler: async (invocation) => {
109
- const session = invocation.agent.session;
110
- const rawInput = invocation.rawInput ? invocation.rawInput.trim() : '';
111
-
112
- // 获取项目根
113
- let projectRoot = rawInput ? rawInput.split(/\s+/)[0] : '';
114
- if (!projectRoot) projectRoot = process.cwd();
115
-
116
- if (!existsSync(join(projectRoot, '.fancy-deployed'))) {
117
- notify(session, scan.notify({ type: 'not_initialized' }));
118
- return { kind: 'success', text: '' };
119
- }
120
-
121
- // 解析参数
122
- let platform = '', length = '';
123
- if (rawInput) {
124
- const parts = rawInput.split(/\s+/);
125
- platform = parts[0] || '';
126
- length = parts[1] || '';
127
- const numMatch = platform.match(/^(\d+)$/);
128
- if (numMatch) {
129
- const row = PLATFORM_TABLE.find(r => r[0] === numMatch[1]);
130
- if (row) {
131
- platform = row[1];
132
- if (platform === 'zhihu' || platform === 'qimao') {
133
- length = parts[1] || 'long';
134
- } else {
135
- length = row[2] === '长篇' ? 'long' : (row[2] === '短篇' ? 'short' : 'long');
136
- }
137
- }
138
- }
139
- }
140
-
141
- // 参数不全 → 弹窗选择
142
- if (!platform || !length || !PLATFORM_TABLE.find(r => r[1] === platform)) {
143
- notify(session, scan.platformList());
144
-
145
- const choice = await ctx.userQuestions.ask({
146
- questions: [scan.askPlatform()],
147
- });
148
- const answer = choice.answers[0]?.custom?.trim();
149
- if (!answer) {
150
- notify(session, scan.notify({ type: 'cancelled_with_hint' }));
151
- return { kind: 'success', text: '' };
152
- }
153
- const numMatch = answer.match(/^(\d+)$/);
154
- const row = numMatch ? PLATFORM_TABLE.find(r => r[0] === numMatch[1]) : null;
155
- if (!row) {
156
- notify(session, scan.notify({ type: 'invalid_choice' }));
157
- return { kind: 'success', text: '' };
158
- }
159
- platform = row[1];
160
- length = (platform === 'zhihu' || platform === 'qimao')
161
- ? null
162
- : (row[2] === '长篇' ? 'long' : 'short');
163
- }
164
-
165
- // 知乎/七猫二次选择篇幅
166
- if (platform === 'zhihu' || platform === 'qimao') {
167
- if (!length || !['long', 'short'].includes(length)) {
168
- const lenResult = await ctx.userQuestions.ask({
169
- questions: [scan.askLength(platform)],
170
- });
171
- const lenAnswer = lenResult.answers[0]?.selected?.[0] ?? lenResult.answers[0]?.custom;
172
- if (!lenAnswer || lenAnswer === '取消') {
173
- notify(session, scan.notify({ type: 'cancelled' }));
174
- return { kind: 'success', text: '' };
175
- }
176
- length = (lenAnswer === '长篇') ? 'long' : 'short';
177
- }
178
- } else if (!length) {
179
- length = 'long';
180
- }
181
-
182
- notify(session, scan.notify({ type: 'collecting', platform, length }));
183
-
184
- const scriptResult = await spawnScript(
185
- 'node',
186
- [join(SCRIPTS_DIR, '..', 'fancy-scan', 'scripts', 'run-scan.js'),
187
- '--project-root', projectRoot, '--platform', platform, '--length', length],
188
- { timeout: 120000 }
189
- );
190
-
191
- if (!scriptResult.ok) {
192
- const err = (typeof scriptResult.error === 'object' && scriptResult.error !== null)
193
- ? (scriptResult.error.message || JSON.stringify(scriptResult.error))
194
- : String(scriptResult.error || '未知错误');
195
- notify(session, scan.notify({ type: 'failed', err }));
196
- return { kind: 'success', text: '' };
197
- }
198
-
199
- // 解析 receipt
200
- let receipt;
201
- try { receipt = JSON.parse(scriptResult.output); } catch (_) {
202
- notify(session, scan.notify({ type: 'parse_error' }));
203
- return { kind: 'success', text: '' };
204
- }
205
-
206
- const files = receipt.scan_files || [];
207
- const topicFile = receipt.topic_decision || '';
208
-
209
- // 通知用户 + 触发 LLM 分析
210
- notify(session, scan.notify({ type: 'handover', platform, length, files, topicFile, projectRoot }));
211
-
212
- const prompt = scan.llmAnalysisPrompt({
213
- platform, length,
214
- date: receipt.date || '',
215
- files,
216
- topicFile,
217
- projectRoot,
218
- });
219
- llmFollowup(invocation, prompt);
220
-
221
- return { kind: 'success', text: '' };
222
- }
223
- });
224
- }
225
-
226
- // ---------------------------------------------------------------------------
227
- // 导出
228
- // ---------------------------------------------------------------------------
1
+ // @cloud411716/fancy-webnovel unified entry point
2
+ import { apply as applyBootstrap } from './plugins/fancy-bootstrap/index.js';
3
+ import { apply as applyScan } from './plugins/fancy-scan/index.js';
229
4
 
230
5
  export const inject = ['commands', 'userQuestions'];
231
6
 
232
7
  export async function apply(ctx) {
233
- try {
234
- await applyBootstrap(ctx);
235
- await applyScan(ctx);
236
- } catch (e) {
237
- const { writeFileSync } = await import('fs');
238
- writeFileSync('/tmp/dsh-apply-error.txt', String(e) + '\n' + e.stack, 'utf-8');
239
- throw e;
240
- }
8
+ await applyBootstrap(ctx);
9
+ await applyScan(ctx);
241
10
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cloud411716/fancy-webnovel",
3
- "version": "0.1.36",
3
+ "version": "0.1.38",
4
4
  "type": "module",
5
5
  "main": "index.js",
6
6
  "exports": {
@@ -0,0 +1,96 @@
1
+ /**
2
+ * fancy-bootstrap plugin entry
3
+ */
4
+ import { fileURLToPath } from 'url';
5
+ import { dirname, join } from 'path';
6
+ import { existsSync } from 'fs';
7
+ import { notify, spawnScript } from '../../infra.js';
8
+ import { bootstrap } from './templates.js';
9
+
10
+ export const inject = ['commands', 'userQuestions'];
11
+
12
+ const __dirname = dirname(fileURLToPath(import.meta.url));
13
+ const SCRIPTS_DIR = join(__dirname, 'scripts');
14
+
15
+ // ---------------------------------------------------------------------------
16
+ // 路径工具
17
+ // ---------------------------------------------------------------------------
18
+
19
+ function isValidPath(p) {
20
+ return !p.includes('..') && !/[<>:"'|?*[\x00-\x1f]/.test(p) && !/\s/.test(p);
21
+ }
22
+
23
+ // ---------------------------------------------------------------------------
24
+ // 注册
25
+ // ---------------------------------------------------------------------------
26
+
27
+ export async function apply(ctx) {
28
+ ctx.commands.register({
29
+ name: 'fancy-bootstrap',
30
+ description: '📁 初始化项目根 ( usage: /fancy-bootstrap [项目根] )',
31
+ handler: async (invocation) => {
32
+ const session = invocation.agent.session;
33
+ const rawInput = invocation.rawInput ? invocation.rawInput.trim() : '';
34
+
35
+ let projectRoot = rawInput;
36
+
37
+ // 无参数 → 弹窗
38
+ if (!projectRoot) {
39
+ const result = await ctx.userQuestions.ask({
40
+ questions: [bootstrap.askPath()],
41
+ });
42
+ projectRoot = result.answers[0]?.custom?.trim() || '';
43
+ }
44
+
45
+ if (!projectRoot) {
46
+ notify(session, bootstrap.notify({ type: 'no_path' }));
47
+ return { kind: 'success', text: '' };
48
+ }
49
+
50
+ if (projectRoot === '.') {
51
+ projectRoot = process.cwd();
52
+ notify(session, bootstrap.notify({ type: 'using_cwd', projectRoot }));
53
+ }
54
+
55
+ if (!isValidPath(projectRoot)) {
56
+ notify(session, bootstrap.notify({ type: 'invalid_path' }));
57
+ return { kind: 'success', text: '' };
58
+ }
59
+
60
+ if (existsSync(join(projectRoot, '.fancy-deployed'))) {
61
+ notify(session, bootstrap.notify({ type: 'already_initialized' }));
62
+ return { kind: 'success', text: '' };
63
+ }
64
+
65
+ const confirm = await ctx.userQuestions.ask({
66
+ questions: [{
67
+ id: 'confirm',
68
+ question: bootstrap.notify({ type: 'confirm', projectRoot }),
69
+ detail: projectRoot,
70
+ hideCustomInput: true,
71
+ options: bootstrap.confirmOptions(),
72
+ }],
73
+ });
74
+ const ans = confirm.answers[0];
75
+ const chosen = ans?.selected?.[0] ?? ans?.custom;
76
+ if (chosen !== '确认创建') {
77
+ notify(session, bootstrap.notify({ type: 'cancelled' }));
78
+ return { kind: 'success', text: '' };
79
+ }
80
+
81
+ notify(session, bootstrap.notify({ type: 'initializing', projectRoot }));
82
+ const result = await spawnScript('node', [join(SCRIPTS_DIR, 'init.js'), projectRoot], { timeout: 30000 });
83
+
84
+ if (result.ok) {
85
+ notify(session, bootstrap.notify({ type: 'success' }));
86
+ } else {
87
+ const err = (typeof result.error === 'object' && result.error !== null)
88
+ ? (result.error.message || JSON.stringify(result.error))
89
+ : String(result.error || '未知错误');
90
+ notify(session, bootstrap.notify({ type: 'error', err }));
91
+ }
92
+
93
+ return { kind: 'success', text: '' };
94
+ }
95
+ });
96
+ }
@@ -0,0 +1,41 @@
1
+ /**
2
+ * bootstrap 模板 — 仅 fancy-bootstrap 使用
3
+ */
4
+
5
+ export const bootstrap = {
6
+ askPath(question = '📂 请输入项目根路径 ( 或者输入 . 代表当前路径 )') {
7
+ return { question };
8
+ },
9
+
10
+ notify({ type, projectRoot }) {
11
+ switch (type) {
12
+ case 'no_path':
13
+ return '⚠️ 未提供路径,已取消';
14
+ case 'using_cwd':
15
+ return `📂 使用当前目录:${projectRoot}`;
16
+ case 'invalid_path':
17
+ return '❌ 路径不合法(不能含空格、.. 或特殊字符)';
18
+ case 'already_initialized':
19
+ return '⚠️ 该目录已初始化(.fancy-deployed 已存在)';
20
+ case 'confirm':
21
+ return `❓ 确认将以下路径作为项目根?\n${projectRoot}`;
22
+ case 'cancelled':
23
+ return '🚫 已取消';
24
+ case 'initializing':
25
+ return `⏳ 正在初始化 ${projectRoot}...`;
26
+ case 'success':
27
+ return '✅ 项目初始化完成!\n下一步:运行 /fancy-scan 扫榜';
28
+ case 'error':
29
+ return (err) => '❌ 初始化失败:' + err;
30
+ default:
31
+ return String(type);
32
+ }
33
+ },
34
+
35
+ confirmOptions() {
36
+ return [
37
+ { label: '确认创建', description: '开始在此目录初始化项目' },
38
+ { label: '取消', description: '取消本次操作' },
39
+ ];
40
+ },
41
+ };
@@ -0,0 +1,135 @@
1
+ /**
2
+ * fancy-scan plugin entry
3
+ */
4
+ import { fileURLToPath } from 'url';
5
+ import { dirname, join } from 'path';
6
+ import { existsSync } from 'fs';
7
+ import { notify, llmFollowup, spawnScript } from '../../infra.js';
8
+ import { scan, PLATFORM_TABLE, platformLabel } from './templates.js';
9
+
10
+ export const inject = ['commands', 'userQuestions'];
11
+
12
+ const __dirname = dirname(fileURLToPath(import.meta.url));
13
+ const SCAN_SCRIPTS_DIR = join(__dirname, 'scripts');
14
+
15
+ export async function apply(ctx) {
16
+ ctx.commands.register({
17
+ name: 'fancy-scan',
18
+ description: '📊 扫榜分析 ( usage: /fancy-scan [平台] [篇幅] )',
19
+ handler: async (invocation) => {
20
+ const session = invocation.agent.session;
21
+ const rawInput = invocation.rawInput ? invocation.rawInput.trim() : '';
22
+
23
+ // 获取项目根
24
+ let projectRoot = rawInput ? rawInput.split(/\s+/)[0] : '';
25
+ if (!projectRoot) projectRoot = process.cwd();
26
+
27
+ if (!existsSync(join(projectRoot, '.fancy-deployed'))) {
28
+ notify(session, scan.notify({ type: 'not_initialized' }));
29
+ return { kind: 'success', text: '' };
30
+ }
31
+
32
+ // 解析参数
33
+ let platform = '', length = '';
34
+ if (rawInput) {
35
+ const parts = rawInput.split(/\s+/);
36
+ platform = parts[0] || '';
37
+ length = parts[1] || '';
38
+ const numMatch = platform.match(/^(\d+)$/);
39
+ if (numMatch) {
40
+ const row = PLATFORM_TABLE.find(r => r[0] === numMatch[1]);
41
+ if (row) {
42
+ platform = row[1];
43
+ if (platform === 'zhihu' || platform === 'qimao') {
44
+ length = parts[1] || 'long';
45
+ } else {
46
+ length = row[2] === '长篇' ? 'long' : (row[2] === '短篇' ? 'short' : 'long');
47
+ }
48
+ }
49
+ }
50
+ }
51
+
52
+ // 参数不全 → 弹窗选择
53
+ if (!platform || !length || !PLATFORM_TABLE.find(r => r[1] === platform)) {
54
+ notify(session, scan.platformList());
55
+
56
+ const choice = await ctx.userQuestions.ask({
57
+ questions: [scan.askPlatform()],
58
+ });
59
+ const answer = choice.answers[0]?.custom?.trim();
60
+ if (!answer) {
61
+ notify(session, scan.notify({ type: 'cancelled_with_hint' }));
62
+ return { kind: 'success', text: '' };
63
+ }
64
+ const numMatch = answer.match(/^(\d+)$/);
65
+ const row = numMatch ? PLATFORM_TABLE.find(r => r[0] === numMatch[1]) : null;
66
+ if (!row) {
67
+ notify(session, scan.notify({ type: 'invalid_choice' }));
68
+ return { kind: 'success', text: '' };
69
+ }
70
+ platform = row[1];
71
+ length = (platform === 'zhihu' || platform === 'qimao')
72
+ ? null
73
+ : (row[2] === '长篇' ? 'long' : 'short');
74
+ }
75
+
76
+ // 知乎/七猫二次选择篇幅
77
+ if (platform === 'zhihu' || platform === 'qimao') {
78
+ if (!length || !['long', 'short'].includes(length)) {
79
+ const lenResult = await ctx.userQuestions.ask({
80
+ questions: [scan.askLength(platform)],
81
+ });
82
+ const lenAnswer = lenResult.answers[0]?.selected?.[0] ?? lenResult.answers[0]?.custom;
83
+ if (!lenAnswer || lenAnswer === '取消') {
84
+ notify(session, scan.notify({ type: 'cancelled' }));
85
+ return { kind: 'success', text: '' };
86
+ }
87
+ length = (lenAnswer === '长篇') ? 'long' : 'short';
88
+ }
89
+ } else if (!length) {
90
+ length = 'long';
91
+ }
92
+
93
+ notify(session, scan.notify({ type: 'collecting', platform, length }));
94
+
95
+ const scriptResult = await spawnScript(
96
+ 'node',
97
+ [join(SCAN_SCRIPTS_DIR, 'run-scan.js'),
98
+ '--project-root', projectRoot, '--platform', platform, '--length', length],
99
+ { timeout: 120000 }
100
+ );
101
+
102
+ if (!scriptResult.ok) {
103
+ const err = (typeof scriptResult.error === 'object' && scriptResult.error !== null)
104
+ ? (scriptResult.error.message || JSON.stringify(scriptResult.error))
105
+ : String(scriptResult.error || '未知错误');
106
+ notify(session, scan.notify({ type: 'failed', err }));
107
+ return { kind: 'success', text: '' };
108
+ }
109
+
110
+ // 解析 receipt
111
+ let receipt;
112
+ try { receipt = JSON.parse(scriptResult.output); } catch (_) {
113
+ notify(session, scan.notify({ type: 'parse_error' }));
114
+ return { kind: 'success', text: '' };
115
+ }
116
+
117
+ const files = receipt.scan_files || [];
118
+ const topicFile = receipt.topic_decision || '';
119
+
120
+ // 通知用户 + 触发 LLM 分析
121
+ notify(session, scan.notify({ type: 'handover', platform, length, files, topicFile, projectRoot }));
122
+
123
+ const prompt = scan.llmAnalysisPrompt({
124
+ platform, length,
125
+ date: receipt.date || '',
126
+ files,
127
+ topicFile,
128
+ projectRoot,
129
+ });
130
+ llmFollowup(invocation, prompt);
131
+
132
+ return { kind: 'success', text: '' };
133
+ }
134
+ });
135
+ }
@@ -1,13 +1,5 @@
1
1
  /**
2
- * templates.js所有插件模板字符串
3
- *
4
- * 包括:
5
- * bootstrap 提示消息
6
- * scan 提示消息
7
- * LLM 分析 prompt 模板
8
- *
9
- * 原则:所有展示给用户的文字、发送给 LLM 的 prompt
10
- * 均从此文件导出,插件主代码只做逻辑和拼装。
2
+ * scan 模板 仅 fancy-scan 使用
11
3
  */
12
4
 
13
5
  // ---------------------------------------------------------------------------
@@ -29,49 +21,7 @@ export function platformLabel(platform) {
29
21
  }
30
22
 
31
23
  // ---------------------------------------------------------------------------
32
- // fancy-bootstrap 模板
33
- // ---------------------------------------------------------------------------
34
-
35
- export const bootstrap = {
36
- askPath(question = '📂 请输入项目根路径 ( 或者输入 . 代表当前路径 )') {
37
- return { question };
38
- },
39
-
40
- notify({ type, projectRoot }) {
41
- switch (type) {
42
- case 'no_path':
43
- return '⚠️ 未提供路径,已取消';
44
- case 'using_cwd':
45
- return `📂 使用当前目录:${projectRoot}`;
46
- case 'invalid_path':
47
- return '❌ 路径不合法(不能含空格、.. 或特殊字符)';
48
- case 'already_initialized':
49
- return '⚠️ 该目录已初始化(.fancy-deployed 已存在)';
50
- case 'confirm':
51
- return `❓ 确认将以下路径作为项目根?\n${projectRoot}`;
52
- case 'cancelled':
53
- return '🚫 已取消';
54
- case 'initializing':
55
- return `⏳ 正在初始化 ${projectRoot}...`;
56
- case 'success':
57
- return '✅ 项目初始化完成!\n下一步:运行 /fancy-scan 扫榜';
58
- case 'error':
59
- return (err) => '❌ 初始化失败:' + err;
60
- default:
61
- return String(type);
62
- }
63
- },
64
-
65
- confirmOptions() {
66
- return [
67
- { label: '确认创建', description: '开始在此目录初始化项目' },
68
- { label: '取消', description: '取消本次操作' },
69
- ];
70
- },
71
- };
72
-
73
- // ---------------------------------------------------------------------------
74
- // fancy-scan 模板
24
+ // 模板
75
25
  // ---------------------------------------------------------------------------
76
26
 
77
27
  export const scan = {
@@ -91,7 +41,7 @@ export const scan = {
91
41
 
92
42
  askLength(platform) {
93
43
  return {
94
- question: `${platformLabel(platform)} 同时支持长篇和短篇,请选择:` ,
44
+ question: `${platformLabel(platform)} 同时支持长篇和短篇,请选择:`,
95
45
  hideCustomInput: true,
96
46
  options: [
97
47
  { label: '长篇', description: '' },
@@ -143,16 +93,6 @@ export const scan = {
143
93
  }
144
94
  },
145
95
 
146
- /**
147
- * 发送给 LLM 的分析 prompt 模板
148
- * @param {object} params
149
- * @param params.platform
150
- * @param params.length
151
- * @param params.date
152
- * @param params.files string[] — 完整绝对路径列表
153
- * @param params.topicFile string — topic_decision 文件完整路径
154
- * @param params.projectRoot string
155
- */
156
96
  llmAnalysisPrompt({ platform, length, date, files, topicFile, projectRoot }) {
157
97
  const pLabel = platformLabel(platform);
158
98
  const lenStr = length === 'long' ? '长篇' : '短篇';