kiro-spec-engine 1.2.3 → 1.4.0

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.
Files changed (78) hide show
  1. package/CHANGELOG.md +135 -0
  2. package/README.md +239 -213
  3. package/README.zh.md +0 -330
  4. package/bin/kiro-spec-engine.js +62 -0
  5. package/docs/README.md +223 -0
  6. package/docs/agent-hooks-analysis.md +815 -0
  7. package/docs/command-reference.md +252 -0
  8. package/docs/cross-tool-guide.md +554 -0
  9. package/docs/examples/add-export-command/design.md +194 -0
  10. package/docs/examples/add-export-command/requirements.md +110 -0
  11. package/docs/examples/add-export-command/tasks.md +88 -0
  12. package/docs/examples/add-rest-api/design.md +855 -0
  13. package/docs/examples/add-rest-api/requirements.md +323 -0
  14. package/docs/examples/add-rest-api/tasks.md +355 -0
  15. package/docs/examples/add-user-dashboard/design.md +192 -0
  16. package/docs/examples/add-user-dashboard/requirements.md +143 -0
  17. package/docs/examples/add-user-dashboard/tasks.md +91 -0
  18. package/docs/faq.md +696 -0
  19. package/docs/integration-modes.md +525 -0
  20. package/docs/integration-philosophy.md +313 -0
  21. package/docs/manual-workflows-guide.md +417 -0
  22. package/docs/quick-start-with-ai-tools.md +374 -0
  23. package/docs/quick-start.md +711 -0
  24. package/docs/spec-workflow.md +453 -0
  25. package/docs/steering-strategy-guide.md +196 -0
  26. package/docs/tools/claude-guide.md +653 -0
  27. package/docs/tools/cursor-guide.md +705 -0
  28. package/docs/tools/generic-guide.md +445 -0
  29. package/docs/tools/kiro-guide.md +308 -0
  30. package/docs/tools/vscode-guide.md +444 -0
  31. package/docs/tools/windsurf-guide.md +390 -0
  32. package/docs/troubleshooting.md +795 -0
  33. package/docs/zh/README.md +275 -0
  34. package/docs/zh/quick-start.md +711 -0
  35. package/docs/zh/tools/claude-guide.md +348 -0
  36. package/docs/zh/tools/cursor-guide.md +280 -0
  37. package/docs/zh/tools/generic-guide.md +498 -0
  38. package/docs/zh/tools/kiro-guide.md +342 -0
  39. package/docs/zh/tools/vscode-guide.md +448 -0
  40. package/docs/zh/tools/windsurf-guide.md +377 -0
  41. package/lib/adoption/detection-engine.js +14 -4
  42. package/lib/commands/adopt.js +117 -3
  43. package/lib/commands/context.js +99 -0
  44. package/lib/commands/prompt.js +105 -0
  45. package/lib/commands/status.js +225 -0
  46. package/lib/commands/task.js +199 -0
  47. package/lib/commands/watch.js +569 -0
  48. package/lib/commands/workflows.js +240 -0
  49. package/lib/commands/workspace.js +189 -0
  50. package/lib/context/context-exporter.js +378 -0
  51. package/lib/context/prompt-generator.js +482 -0
  52. package/lib/steering/adoption-config.js +164 -0
  53. package/lib/steering/steering-manager.js +289 -0
  54. package/lib/task/task-claimer.js +430 -0
  55. package/lib/utils/tool-detector.js +383 -0
  56. package/lib/watch/action-executor.js +458 -0
  57. package/lib/watch/event-debouncer.js +323 -0
  58. package/lib/watch/execution-logger.js +550 -0
  59. package/lib/watch/file-watcher.js +499 -0
  60. package/lib/watch/presets.js +266 -0
  61. package/lib/watch/watch-manager.js +533 -0
  62. package/lib/workspace/workspace-manager.js +370 -0
  63. package/lib/workspace/workspace-sync.js +356 -0
  64. package/package.json +3 -1
  65. package/template/.kiro/tools/backup_manager.py +295 -0
  66. package/template/.kiro/tools/configuration_manager.py +218 -0
  67. package/template/.kiro/tools/document_evaluator.py +550 -0
  68. package/template/.kiro/tools/enhancement_logger.py +168 -0
  69. package/template/.kiro/tools/error_handler.py +335 -0
  70. package/template/.kiro/tools/improvement_identifier.py +444 -0
  71. package/template/.kiro/tools/modification_applicator.py +737 -0
  72. package/template/.kiro/tools/quality_gate_enforcer.py +207 -0
  73. package/template/.kiro/tools/quality_scorer.py +305 -0
  74. package/template/.kiro/tools/report_generator.py +154 -0
  75. package/template/.kiro/tools/ultrawork_enhancer_refactored.py +0 -0
  76. package/template/.kiro/tools/ultrawork_enhancer_v2.py +463 -0
  77. package/template/.kiro/tools/ultrawork_enhancer_v3.py +606 -0
  78. package/template/.kiro/tools/workflow_quality_gate.py +100 -0
@@ -0,0 +1,482 @@
1
+ const fs = require('fs-extra');
2
+ const path = require('path');
3
+
4
+ /**
5
+ * PromptGenerator - 提示生成器
6
+ *
7
+ * 为特定任务生成 AI 编码助手的提示文件
8
+ */
9
+ class PromptGenerator {
10
+ constructor() {
11
+ this.promptsDir = 'prompts';
12
+ }
13
+
14
+ /**
15
+ * 生成任务提示
16
+ *
17
+ * @param {string} projectPath - 项目根目录路径
18
+ * @param {string} specName - Spec 名称
19
+ * @param {string} taskId - 任务 ID
20
+ * @param {Object} options - 生成选项
21
+ * @returns {Promise<Object>} 生成结果
22
+ */
23
+ async generatePrompt(projectPath, specName, taskId, options = {}) {
24
+ const {
25
+ targetTool = 'generic',
26
+ includeCodeContext = false,
27
+ maxContextLength = 10000
28
+ } = options;
29
+
30
+ try {
31
+ const specPath = path.join(projectPath, '.kiro/specs', specName);
32
+
33
+ // 检查 Spec 是否存在
34
+ const specExists = await fs.pathExists(specPath);
35
+ if (!specExists) {
36
+ return {
37
+ success: false,
38
+ error: `Spec not found: ${specName}`
39
+ };
40
+ }
41
+
42
+ // 读取 Spec 文件
43
+ const requirements = await this.readSpecFile(specPath, 'requirements.md');
44
+ const design = await this.readSpecFile(specPath, 'design.md');
45
+ const tasks = await this.readSpecFile(specPath, 'tasks.md');
46
+
47
+ if (!tasks) {
48
+ return {
49
+ success: false,
50
+ error: 'tasks.md not found'
51
+ };
52
+ }
53
+
54
+ // 提取任务信息
55
+ const taskInfo = this.extractTaskInfo(tasks, taskId);
56
+
57
+ if (!taskInfo) {
58
+ return {
59
+ success: false,
60
+ error: `Task not found: ${taskId}`
61
+ };
62
+ }
63
+
64
+ // 提取相关 Requirements
65
+ const relevantRequirements = this.extractRelevantRequirements(
66
+ requirements,
67
+ taskInfo
68
+ );
69
+
70
+ // 提取相关 Design
71
+ const relevantDesign = this.extractRelevantDesignSections(
72
+ design,
73
+ taskInfo
74
+ );
75
+
76
+ // 格式化提示
77
+ const prompt = this.formatPrompt({
78
+ specName,
79
+ taskId,
80
+ taskInfo,
81
+ relevantRequirements,
82
+ relevantDesign,
83
+ targetTool,
84
+ maxContextLength
85
+ });
86
+
87
+ // 保存提示文件
88
+ const promptPath = await this.savePrompt(specPath, taskId, prompt);
89
+
90
+ return {
91
+ success: true,
92
+ promptPath,
93
+ specName,
94
+ taskId,
95
+ size: Buffer.byteLength(prompt, 'utf8')
96
+ };
97
+ } catch (error) {
98
+ return {
99
+ success: false,
100
+ error: error.message
101
+ };
102
+ }
103
+ }
104
+
105
+ /**
106
+ * 读取 Spec 文件
107
+ *
108
+ * @param {string} specPath - Spec 目录路径
109
+ * @param {string} fileName - 文件名
110
+ * @returns {Promise<string|null>} 文件内容或 null
111
+ */
112
+ async readSpecFile(specPath, fileName) {
113
+ const filePath = path.join(specPath, fileName);
114
+
115
+ try {
116
+ const exists = await fs.pathExists(filePath);
117
+ if (!exists) {
118
+ return null;
119
+ }
120
+
121
+ return await fs.readFile(filePath, 'utf8');
122
+ } catch (error) {
123
+ return null;
124
+ }
125
+ }
126
+
127
+ /**
128
+ * 提取任务信息
129
+ *
130
+ * @param {string} tasksContent - tasks.md 内容
131
+ * @param {string} taskId - 任务 ID
132
+ * @returns {Object|null} 任务信息
133
+ */
134
+ extractTaskInfo(tasksContent, taskId) {
135
+ const lines = tasksContent.split('\n');
136
+ const taskPattern = new RegExp(`^-\\s*\\[[\\s\\-x~]\\]\\*?\\s+${taskId}\\s+(.+)$`);
137
+
138
+ let taskInfo = null;
139
+ let inTaskDetails = false;
140
+ const details = [];
141
+
142
+ for (let i = 0; i < lines.length; i++) {
143
+ const line = lines[i];
144
+ const match = line.match(taskPattern);
145
+
146
+ if (match) {
147
+ taskInfo = {
148
+ id: taskId,
149
+ title: match[1].replace(/\[@.+\]$/, '').trim(),
150
+ fullLine: line,
151
+ details: []
152
+ };
153
+ inTaskDetails = true;
154
+ continue;
155
+ }
156
+
157
+ if (inTaskDetails) {
158
+ // 收集任务详细信息(缩进的行)
159
+ if (line.trim() === '') {
160
+ continue;
161
+ }
162
+
163
+ if (line.startsWith(' ') || line.startsWith(' - ')) {
164
+ details.push(line.trim());
165
+ } else if (line.match(/^-\s*\[/)) {
166
+ // 遇到下一个任务,停止收集
167
+ break;
168
+ }
169
+ }
170
+ }
171
+
172
+ if (taskInfo) {
173
+ taskInfo.details = details;
174
+ }
175
+
176
+ return taskInfo;
177
+ }
178
+
179
+ /**
180
+ * 提取相关 Requirements
181
+ *
182
+ * @param {string} requirements - Requirements 内容
183
+ * @param {Object} taskInfo - 任务信息
184
+ * @returns {string} 相关 Requirements
185
+ */
186
+ extractRelevantRequirements(requirements, taskInfo) {
187
+ if (!requirements) {
188
+ return 'No requirements document found.';
189
+ }
190
+
191
+ // 从任务详情中提取 Requirements 引用
192
+ const reqReferences = [];
193
+ for (const detail of taskInfo.details) {
194
+ const match = detail.match(/_Requirements?:\s*([\d\.,\s]+)_/);
195
+ if (match) {
196
+ const refs = match[1].split(',').map(r => r.trim());
197
+ reqReferences.push(...refs);
198
+ }
199
+ }
200
+
201
+ if (reqReferences.length === 0) {
202
+ // 如果没有明确引用,返回所有 Requirements
203
+ return requirements;
204
+ }
205
+
206
+ // 提取引用的 Requirements 章节
207
+ const sections = this.extractSectionsByReferences(
208
+ requirements,
209
+ reqReferences,
210
+ 'Requirement'
211
+ );
212
+
213
+ return sections.length > 0 ? sections.join('\n\n') : requirements;
214
+ }
215
+
216
+ /**
217
+ * 提取相关 Design 章节
218
+ *
219
+ * @param {string} design - Design 内容
220
+ * @param {Object} taskInfo - 任务信息
221
+ * @returns {string} 相关 Design 章节
222
+ */
223
+ extractRelevantDesignSections(design, taskInfo) {
224
+ if (!design) {
225
+ return 'No design document found.';
226
+ }
227
+
228
+ // 从任务标题中提取关键词
229
+ const keywords = this.extractKeywords(taskInfo.title);
230
+
231
+ // 查找包含关键词的 Design 章节
232
+ const sections = this.extractSectionsByKeywords(design, keywords);
233
+
234
+ return sections.length > 0 ? sections.join('\n\n') : design;
235
+ }
236
+
237
+ /**
238
+ * 根据引用提取章节
239
+ *
240
+ * @param {string} content - 文档内容
241
+ * @param {Array<string>} references - 引用列表
242
+ * @param {string} prefix - 章节前缀
243
+ * @returns {Array<string>} 提取的章节
244
+ */
245
+ extractSectionsByReferences(content, references, prefix) {
246
+ const sections = [];
247
+ const lines = content.split('\n');
248
+
249
+ for (const ref of references) {
250
+ const pattern = new RegExp(`^###?\\s+${prefix}\\s+${ref}[:\\s]`, 'i');
251
+ let inSection = false;
252
+ let section = [];
253
+
254
+ for (const line of lines) {
255
+ if (pattern.test(line)) {
256
+ inSection = true;
257
+ section = [line];
258
+ } else if (inSection) {
259
+ if (line.match(/^###?\s+/)) {
260
+ // 遇到下一个章节,停止收集
261
+ sections.push(section.join('\n'));
262
+ break;
263
+ }
264
+ section.push(line);
265
+ }
266
+ }
267
+
268
+ if (section.length > 0) {
269
+ sections.push(section.join('\n'));
270
+ }
271
+ }
272
+
273
+ return sections;
274
+ }
275
+
276
+ /**
277
+ * 提取关键词
278
+ *
279
+ * @param {string} text - 文本
280
+ * @returns {Array<string>} 关键词列表
281
+ */
282
+ extractKeywords(text) {
283
+ // 移除常见词汇,提取有意义的关键词
284
+ const stopWords = ['implement', 'create', 'add', 'update', 'write', 'test', 'the', 'a', 'an', 'and', 'or', 'for', 'to', 'with'];
285
+ const words = text.toLowerCase()
286
+ .replace(/[^\w\s]/g, ' ')
287
+ .split(/\s+/)
288
+ .filter(w => w.length > 3 && !stopWords.includes(w));
289
+
290
+ return [...new Set(words)];
291
+ }
292
+
293
+ /**
294
+ * 根据关键词提取章节
295
+ *
296
+ * @param {string} content - 文档内容
297
+ * @param {Array<string>} keywords - 关键词列表
298
+ * @returns {Array<string>} 提取的章节
299
+ */
300
+ extractSectionsByKeywords(content, keywords) {
301
+ if (keywords.length === 0) {
302
+ return [];
303
+ }
304
+
305
+ const sections = [];
306
+ const lines = content.split('\n');
307
+ let currentSection = [];
308
+ let inRelevantSection = false;
309
+
310
+ for (const line of lines) {
311
+ if (line.match(/^###?\s+/)) {
312
+ // 检查是否是新章节
313
+ if (currentSection.length > 0 && inRelevantSection) {
314
+ sections.push(currentSection.join('\n'));
315
+ }
316
+
317
+ // 检查章节标题是否包含关键词
318
+ const lowerLine = line.toLowerCase();
319
+ inRelevantSection = keywords.some(kw => lowerLine.includes(kw));
320
+ currentSection = [line];
321
+ } else if (inRelevantSection) {
322
+ currentSection.push(line);
323
+ }
324
+ }
325
+
326
+ if (currentSection.length > 0 && inRelevantSection) {
327
+ sections.push(currentSection.join('\n'));
328
+ }
329
+
330
+ return sections;
331
+ }
332
+
333
+ /**
334
+ * 格式化提示
335
+ *
336
+ * @param {Object} params - 提示参数
337
+ * @returns {string} 格式化的提示
338
+ */
339
+ formatPrompt(params) {
340
+ const {
341
+ specName,
342
+ taskId,
343
+ taskInfo,
344
+ relevantRequirements,
345
+ relevantDesign,
346
+ targetTool,
347
+ maxContextLength
348
+ } = params;
349
+
350
+ const sections = [];
351
+
352
+ // 头部
353
+ sections.push(`# Task Prompt: ${specName} - Task ${taskId}`);
354
+ sections.push('');
355
+ sections.push(`**Generated**: ${new Date().toISOString()}`);
356
+ sections.push(`**Target Tool**: ${targetTool}`);
357
+ sections.push('');
358
+ sections.push('---');
359
+ sections.push('');
360
+
361
+ // 任务描述
362
+ sections.push('## 📋 Task Description');
363
+ sections.push('');
364
+ sections.push(`**Task ID**: ${taskId}`);
365
+ sections.push(`**Title**: ${taskInfo.title}`);
366
+ sections.push('');
367
+
368
+ if (taskInfo.details.length > 0) {
369
+ sections.push('**Details**:');
370
+ for (const detail of taskInfo.details) {
371
+ sections.push(`- ${detail}`);
372
+ }
373
+ sections.push('');
374
+ }
375
+
376
+ sections.push('---');
377
+ sections.push('');
378
+
379
+ // 相关 Requirements
380
+ sections.push('## 📖 Relevant Requirements');
381
+ sections.push('');
382
+ sections.push(this.truncateContent(relevantRequirements, maxContextLength * 0.4));
383
+ sections.push('');
384
+ sections.push('---');
385
+ sections.push('');
386
+
387
+ // 相关 Design
388
+ sections.push('## 🏗️ Relevant Design');
389
+ sections.push('');
390
+ sections.push(this.truncateContent(relevantDesign, maxContextLength * 0.4));
391
+ sections.push('');
392
+ sections.push('---');
393
+ sections.push('');
394
+
395
+ // 实现指南
396
+ sections.push('## 💡 Implementation Guidelines');
397
+ sections.push('');
398
+ sections.push('1. **Read the requirements** carefully to understand what needs to be implemented');
399
+ sections.push('2. **Review the design** to understand the architecture and interfaces');
400
+ sections.push('3. **Implement the functionality** following the task details');
401
+ sections.push('4. **Write tests** to verify the implementation');
402
+ sections.push('5. **Update task status** after completion');
403
+ sections.push('');
404
+ sections.push('---');
405
+ sections.push('');
406
+
407
+ // 任务状态更新说明
408
+ sections.push('## ✅ Task Status Update');
409
+ sections.push('');
410
+ sections.push('After completing this task, update the task status in `tasks.md`:');
411
+ sections.push('');
412
+ sections.push('```markdown');
413
+ sections.push(`- [x] ${taskId} ${taskInfo.title}`);
414
+ sections.push('```');
415
+ sections.push('');
416
+ sections.push('---');
417
+ sections.push('');
418
+
419
+ // 工具特定说明
420
+ sections.push('## 🔧 Tool-Specific Notes');
421
+ sections.push('');
422
+ sections.push(this.getToolSpecificNotes(targetTool));
423
+ sections.push('');
424
+
425
+ return sections.join('\n');
426
+ }
427
+
428
+ /**
429
+ * 截断内容
430
+ *
431
+ * @param {string} content - 内容
432
+ * @param {number} maxLength - 最大长度
433
+ * @returns {string} 截断后的内容
434
+ */
435
+ truncateContent(content, maxLength) {
436
+ if (content.length <= maxLength) {
437
+ return content;
438
+ }
439
+
440
+ return content.substring(0, maxLength) + '\n\n... (content truncated)';
441
+ }
442
+
443
+ /**
444
+ * 获取工具特定说明
445
+ *
446
+ * @param {string} targetTool - 目标工具
447
+ * @returns {string} 工具特定说明
448
+ */
449
+ getToolSpecificNotes(targetTool) {
450
+ const notes = {
451
+ 'generic': 'This prompt is compatible with any AI coding assistant.',
452
+ 'claude-code': 'Use this prompt with Claude Code by copying it into the chat.',
453
+ 'cursor': 'Use this prompt with Cursor by pasting it into the composer.',
454
+ 'codex': 'Use this prompt with GitHub Copilot by including it in your code comments.',
455
+ 'kiro': 'This prompt is optimized for Kiro IDE with automatic steering loading.'
456
+ };
457
+
458
+ return notes[targetTool] || notes['generic'];
459
+ }
460
+
461
+ /**
462
+ * 保存提示文件
463
+ *
464
+ * @param {string} specPath - Spec 目录路径
465
+ * @param {string} taskId - 任务 ID
466
+ * @param {string} prompt - 提示内容
467
+ * @returns {Promise<string>} 提示文件路径
468
+ */
469
+ async savePrompt(specPath, taskId, prompt) {
470
+ const promptsPath = path.join(specPath, this.promptsDir);
471
+ await fs.ensureDir(promptsPath);
472
+
473
+ const fileName = `task-${taskId.replace(/\./g, '-')}.md`;
474
+ const promptPath = path.join(promptsPath, fileName);
475
+
476
+ await fs.writeFile(promptPath, prompt, 'utf8');
477
+
478
+ return promptPath;
479
+ }
480
+ }
481
+
482
+ module.exports = PromptGenerator;
@@ -0,0 +1,164 @@
1
+ const fs = require('fs-extra');
2
+ const path = require('path');
3
+
4
+ /**
5
+ * AdoptionConfig - 管理 adoption-config.json 配置文件
6
+ *
7
+ * 记录项目采用 kse 时的配置选择
8
+ */
9
+ class AdoptionConfig {
10
+ constructor(projectPath) {
11
+ this.projectPath = projectPath;
12
+ this.configPath = path.join(projectPath, '.kiro', 'adoption-config.json');
13
+ }
14
+
15
+ /**
16
+ * 读取配置文件
17
+ *
18
+ * @returns {Promise<Object|null>} 配置对象,如果不存在则返回 null
19
+ */
20
+ async read() {
21
+ try {
22
+ const exists = await fs.pathExists(this.configPath);
23
+ if (!exists) {
24
+ return null;
25
+ }
26
+
27
+ const content = await fs.readFile(this.configPath, 'utf8');
28
+ return JSON.parse(content);
29
+ } catch (error) {
30
+ console.error(`Error reading adoption config: ${error.message}`);
31
+ return null;
32
+ }
33
+ }
34
+
35
+ /**
36
+ * 写入配置文件
37
+ *
38
+ * @param {Object} config - 配置对象
39
+ * @returns {Promise<boolean>} 是否成功
40
+ */
41
+ async write(config) {
42
+ try {
43
+ // 确保目录存在
44
+ await fs.ensureDir(path.dirname(this.configPath));
45
+
46
+ // 写入配置
47
+ await fs.writeFile(
48
+ this.configPath,
49
+ JSON.stringify(config, null, 2),
50
+ 'utf8'
51
+ );
52
+
53
+ return true;
54
+ } catch (error) {
55
+ console.error(`Error writing adoption config: ${error.message}`);
56
+ return false;
57
+ }
58
+ }
59
+
60
+ /**
61
+ * 更新 steering 策略配置
62
+ *
63
+ * @param {string} strategy - 策略 ('use-kse' | 'use-project')
64
+ * @param {string|null} backupId - 备份 ID(如果有)
65
+ * @returns {Promise<boolean>} 是否成功
66
+ */
67
+ async updateSteeringStrategy(strategy, backupId = null) {
68
+ // 读取现有配置
69
+ let config = await this.read();
70
+
71
+ if (!config) {
72
+ // 创建新配置
73
+ config = {
74
+ version: '1.0.0',
75
+ adoptedAt: new Date().toISOString(),
76
+ multiUserMode: false
77
+ };
78
+ }
79
+
80
+ // 更新 steering 策略
81
+ config.steeringStrategy = strategy;
82
+
83
+ if (backupId) {
84
+ config.steeringBackupId = backupId;
85
+ }
86
+
87
+ config.lastUpdated = new Date().toISOString();
88
+
89
+ // 写入配置
90
+ return await this.write(config);
91
+ }
92
+
93
+ /**
94
+ * 更新多用户模式配置
95
+ *
96
+ * @param {boolean} enabled - 是否启用多用户模式
97
+ * @returns {Promise<boolean>} 是否成功
98
+ */
99
+ async updateMultiUserMode(enabled) {
100
+ let config = await this.read();
101
+
102
+ if (!config) {
103
+ config = {
104
+ version: '1.0.0',
105
+ adoptedAt: new Date().toISOString()
106
+ };
107
+ }
108
+
109
+ config.multiUserMode = enabled;
110
+ config.lastUpdated = new Date().toISOString();
111
+
112
+ return await this.write(config);
113
+ }
114
+
115
+ /**
116
+ * 获取 steering 策略
117
+ *
118
+ * @returns {Promise<string|null>} 策略或 null
119
+ */
120
+ async getSteeringStrategy() {
121
+ const config = await this.read();
122
+ return config ? config.steeringStrategy : null;
123
+ }
124
+
125
+ /**
126
+ * 获取 steering 备份 ID
127
+ *
128
+ * @returns {Promise<string|null>} 备份 ID 或 null
129
+ */
130
+ async getSteeringBackupId() {
131
+ const config = await this.read();
132
+ return config ? config.steeringBackupId : null;
133
+ }
134
+
135
+ /**
136
+ * 检查是否启用多用户模式
137
+ *
138
+ * @returns {Promise<boolean>} 是否启用
139
+ */
140
+ async isMultiUserMode() {
141
+ const config = await this.read();
142
+ return config ? (config.multiUserMode || false) : false;
143
+ }
144
+
145
+ /**
146
+ * 创建初始配置
147
+ *
148
+ * @param {Object} options - 配置选项
149
+ * @returns {Promise<boolean>} 是否成功
150
+ */
151
+ async initialize(options = {}) {
152
+ const config = {
153
+ version: '1.0.0',
154
+ adoptedAt: new Date().toISOString(),
155
+ steeringStrategy: options.steeringStrategy || 'use-kse',
156
+ multiUserMode: options.multiUserMode || false,
157
+ ...options
158
+ };
159
+
160
+ return await this.write(config);
161
+ }
162
+ }
163
+
164
+ module.exports = AdoptionConfig;