@cloud411716/fancy-webnovel 0.1.1 → 0.1.3

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cloud411716/fancy-webnovel",
3
- "version": "0.1.1",
3
+ "version": "0.1.3",
4
4
  "type": "module",
5
5
  "main": "plugins/fancy-bootstrap/index.js",
6
6
  "exports": {
@@ -0,0 +1,53 @@
1
+ /**
2
+ * fancy-bootstrap DSH plugin
3
+ */
4
+ import { spawn } from 'child_process';
5
+ import { fileURLToPath } from 'url';
6
+ import { dirname, join } from 'path';
7
+
8
+ const __filename = fileURLToPath(import.meta.url);
9
+ const __dirname = dirname(__filename);
10
+
11
+ export const name = 'fancy-bootstrap';
12
+ export const inject = ['commands', 'userQuestions'];
13
+
14
+ export async function apply(ctx) {
15
+ ctx.commands.register({
16
+ name: '/fancy-bootstrap',
17
+ description: 'Initialize a fancy-webnovel project',
18
+ handler: async (ctx, params) => {
19
+ const projectRoot = params?.projectRoot;
20
+ if (!projectRoot) {
21
+ return { content: [{ type: 'text', text: 'Usage: /fancy-bootstrap <project-root>' }] };
22
+ }
23
+ const result = await runInit(projectRoot);
24
+ if (result.ok) {
25
+ return { content: [{ type: 'text', text: `Done: ${result.output}` }] };
26
+ } else {
27
+ return { content: [{ type: 'text', text: `Error: ${result.error}` }] };
28
+ }
29
+ }
30
+ });
31
+ }
32
+
33
+ function runInit(projectRoot) {
34
+ return new Promise((resolve) => {
35
+ const scriptPath = join(__dirname, 'scripts', 'init.js');
36
+ const proc = spawn('npx', ['tsx', scriptPath, projectRoot], { timeout: 60000 });
37
+ let stdout = '';
38
+ let stderr = '';
39
+ proc.stdout?.on('data', (data) => { stdout += data.toString(); });
40
+ proc.stderr?.on('data', (data) => { stderr += data.toString(); });
41
+ proc.on('close', (code) => {
42
+ try {
43
+ const parsed = JSON.parse(stdout);
44
+ resolve({ ok: parsed.ok === true, output: stdout, error: stderr || undefined });
45
+ } catch {
46
+ resolve({ ok: false, output: stdout, error: stderr || `exit code ${code}` });
47
+ }
48
+ });
49
+ proc.on('error', (err) => {
50
+ resolve({ ok: false, output: '', error: err.message });
51
+ });
52
+ });
53
+ }
@@ -1,9 +1,8 @@
1
1
  /**
2
- * fancy-bootstrap init.ts — 项目初始化
2
+ * fancy-bootstrap init — 项目初始化
3
3
  */
4
-
5
4
  import { writeFileSync, mkdirSync, existsSync } from 'fs';
6
- import { expandHome, resolvePath, joinPath } from './path-utils.ts';
5
+ import { expandHome, resolvePath, joinPath } from './path-utils.js';
7
6
 
8
7
  const DIRS = [
9
8
  '设定/角色', '设定/势力', '设定/物品', '设定/地点',
@@ -13,23 +12,22 @@ const DIRS = [
13
12
  '调试', 'RAG索引',
14
13
  ];
15
14
 
16
- function mkdirp(dir: string): void {
15
+ function mkdirp(dir) {
17
16
  mkdirSync(dir, { recursive: true });
18
17
  }
19
18
 
20
- function writeJson(filePath: string, data: unknown): void {
19
+ function writeJson(filePath, data) {
21
20
  writeFileSync(filePath, JSON.stringify(data), 'utf-8');
22
21
  }
23
22
 
24
- function nowIso(): string {
23
+ function nowIso() {
25
24
  return new Date().toISOString().replace('.000Z', 'Z');
26
25
  }
27
26
 
28
27
  function main() {
29
28
  const projectRootRaw = process.argv[2] ?? '';
30
29
 
31
- // 路径合法性检查
32
- if (projectRootRaw.includes('..') || /[<>:"|?*]/.test(projectRootRaw) || /[\x00-\x1f]/.test(projectRootRaw)) {
30
+ if (projectRootRaw.includes('..') || /[<>:"'|?*]/.test(projectRootRaw) || /[\x00-\x1f]/.test(projectRootRaw)) {
33
31
  console.log(JSON.stringify({ ok: false, error: { code: 1, code_name: 'EC001', message: '路径不合法' } }));
34
32
  process.exit(1);
35
33
  }
@@ -49,16 +47,13 @@ function main() {
49
47
  const now = nowIso();
50
48
 
51
49
  try {
52
- // 创建目录
53
50
  for (const d of DIRS) mkdirp(joinPath(projectRoot, d));
54
51
 
55
- // .fancy-deployed
56
52
  writeJson(deployed, {
57
53
  schema_version: '2.0.0', fancy_skills_version: '2.0.0', deployed_at: now,
58
54
  project_root_abs: projectRoot, project_root_rel: '.', current_book: '', books: [], active_book_index: 0,
59
55
  });
60
56
 
61
- // truth files
62
57
  const archive = joinPath(projectRoot, '故事档案');
63
58
  writeJson(joinPath(archive, '_story_state.json'), {
64
59
  schema_version: '2.0.0', last_modified: now,
@@ -77,7 +72,6 @@ function main() {
77
72
  writeJson(joinPath(archive, '_foreshadows.json'), { schema_version: '2.0.0', last_modified: now, foreshadows: [], type_distribution: {}, open_count: 0 });
78
73
  writeJson(joinPath(archive, '_chapter_index.json'), { schema_version: '2.0.0', last_modified: now, chapters: [] });
79
74
 
80
- // author intent 模板
81
75
  const intentMd = [
82
76
  '# 作者长期意图(Author Intent)',
83
77
  '',
@@ -100,7 +94,7 @@ function main() {
100
94
  ].join('\n');
101
95
  writeFileSync(joinPath(archive, '_author_intent.md'), intentMd, 'utf-8');
102
96
 
103
- } catch (e: unknown) {
97
+ } catch (e) {
104
98
  console.log(JSON.stringify({ ok: false, error: { code: 17, code_name: 'EC017', message: `初始化失败: ${e instanceof Error ? e.message : String(e)}` } }));
105
99
  process.exit(17);
106
100
  }
@@ -1,17 +1,17 @@
1
1
  /**
2
2
  * 路径工具
3
3
  */
4
- export function expandHome(pathStr: string): string {
4
+ export function expandHome(pathStr) {
5
5
  if (pathStr.startsWith('~/') || pathStr === '~') {
6
6
  return pathStr === '~' ? (process.env.HOME ?? '/root') : pathStr.replace('~', process.env.HOME ?? '/root');
7
7
  }
8
8
  return pathStr;
9
9
  }
10
10
 
11
- export function resolvePath(pathStr: string): string {
12
- return expandHome(pathStr).replace(/\/+$/, '');
11
+ export function resolvePath(pathStr) {
12
+ return expandHome(pathStr).replace(/\\+$/, '');
13
13
  }
14
14
 
15
- export function joinPath(...parts: string[]): string {
15
+ export function joinPath(...parts) {
16
16
  return parts.join('/').replace(/\/+/g, '/');
17
17
  }
@@ -1,103 +0,0 @@
1
- /**
2
- * fancy-bootstrap DSH plugin
3
- */
4
- import { createUserMessage } from '@deepseek-ai/dsh-llm';
5
- import { spawn } from 'child_process';
6
- import { fileURLToPath } from 'url';
7
- import { dirname, join } from 'path';
8
-
9
- const __filename = fileURLToPath(import.meta.url);
10
- const __dirname = dirname(__filename);
11
-
12
- export const name = 'fancy-bootstrap';
13
- export const inject = ['commands', 'userQuestions'] as const;
14
-
15
- function runInit(projectRoot: string): Promise<{ ok: boolean; output: string; error?: string }> {
16
- return new Promise((resolve) => {
17
- const scriptPath = join(__dirname, 'scripts', 'init.ts');
18
- const proc = spawn('npx', ['tsx', scriptPath, projectRoot], { timeout: 60000 });
19
- let stdout = '';
20
- let stderr = '';
21
- proc.stdout?.on('data', (data) => { stdout += data.toString(); });
22
- proc.stderr?.on('data', (data) => { stderr += data.toString(); });
23
- proc.on('close', (code) => {
24
- try {
25
- const parsed = JSON.parse(stdout);
26
- resolve({ ok: parsed.ok === true, output: stdout, error: stderr || undefined });
27
- } catch {
28
- resolve({ ok: false, output: stdout, error: stderr || `exit code ${code}` });
29
- }
30
- });
31
- proc.on('error', (err) => { resolve({ ok: false, output: '', error: err.message }); });
32
- });
33
- }
34
-
35
- function notify(session: any, text: string) {
36
- queueMicrotask(() => {
37
- session.append(
38
- "user/message",
39
- createUserMessage({ content: [{ type: 'text', text }], source: { kind: 'user' } }),
40
- { surfaceOp: "append" }
41
- );
42
- });
43
- }
44
-
45
- export function apply(ctx: any) {
46
- ctx.effect(function* () {
47
- yield ctx.commands.register({
48
- name: 'fancy-bootstrap',
49
- description: '初始化网文项目 (Usage: /fancy-bootstrap [项目根])',
50
- handler: async (invocation: any) => {
51
- const session = invocation.agent.session;
52
- const rawInput = invocation.rawInput.trim();
53
-
54
- // 1. 获取项目根路径
55
- let projectRoot = rawInput;
56
- if (!projectRoot) {
57
- const result = await ctx.userQuestions.ask({
58
- questions: [{ id: 'path', question: '请输入项目根路径' }],
59
- });
60
- projectRoot = result.answers[0]?.custom?.trim();
61
- if (!projectRoot) {
62
- return { kind: 'success', text: '' };
63
- }
64
- }
65
-
66
- // 2. 二次确认(隐藏自定义输入框)
67
- const confirm = await ctx.userQuestions.ask({
68
- questions: [{
69
- id: 'confirm',
70
- question: `确认将以下路径作为项目根?`,
71
- detail: projectRoot,
72
- hideCustomInput: true,
73
- options: [
74
- { label: '确认创建', description: '开始在此目录初始化项目' },
75
- { label: '取消', description: '取消本次操作' },
76
- ],
77
- }],
78
- });
79
- const chosen = confirm.answers[0]?.selected?.[0] || confirm.answers[0]?.custom;
80
- if (chosen === '取消') {
81
- return { kind: 'success', text: '' };
82
- }
83
-
84
- // 3. 调用 init.ts(脚本内自行验证)
85
- const { ok, output, error } = await runInit(projectRoot);
86
-
87
- if (!ok) {
88
- let detail = '';
89
- try {
90
- const parsed = JSON.parse(output);
91
- if (parsed.error?.message) detail = parsed.error.message;
92
- } catch { detail = error || output || '未知错误'; }
93
- notify(session, `❌ 初始化失败:${detail}`);
94
- return { kind: 'success', text: '' };
95
- }
96
-
97
- // 4. 成功
98
- notify(session, `✅ 项目初始化完成:${projectRoot}\n\n下一步:/fancy-scan 扫榜分析`);
99
- return { kind: 'success', text: '' };
100
- }
101
- });
102
- }, 'fancy-bootstrap:register');
103
- }