dev-playbooks 1.0.16 → 1.0.17

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 (2) hide show
  1. package/bin/devbooks.js +104 -26
  2. package/package.json +1 -1
package/bin/devbooks.js CHANGED
@@ -179,6 +179,50 @@ function expandPath(p) {
179
179
  return p;
180
180
  }
181
181
 
182
+ /**
183
+ * Update content between DEVBOOKS:START/END markers in a file
184
+ * Preserves user customizations outside the markers
185
+ */
186
+ function updateManagedContent(filePath, newManagedContent) {
187
+ if (!fs.existsSync(filePath)) {
188
+ return false;
189
+ }
190
+
191
+ const content = fs.readFileSync(filePath, 'utf-8');
192
+ const startMarker = DEVBOOKS_MARKERS.start;
193
+ const endMarker = DEVBOOKS_MARKERS.end;
194
+
195
+ const startIdx = content.indexOf(startMarker);
196
+ const endIdx = content.indexOf(endMarker);
197
+
198
+ if (startIdx === -1 || endIdx === -1 || startIdx >= endIdx) {
199
+ // No valid markers found, cannot update
200
+ return false;
201
+ }
202
+
203
+ // Extract the managed block from new content
204
+ const newStartIdx = newManagedContent.indexOf(startMarker);
205
+ const newEndIdx = newManagedContent.indexOf(endMarker);
206
+
207
+ if (newStartIdx === -1 || newEndIdx === -1) {
208
+ return false;
209
+ }
210
+
211
+ const newManagedBlock = newManagedContent.slice(newStartIdx, newEndIdx + endMarker.length);
212
+
213
+ // Replace the old managed block
214
+ const before = content.slice(0, startIdx);
215
+ const after = content.slice(endIdx + endMarker.length);
216
+ const updatedContent = before + newManagedBlock + after;
217
+
218
+ if (updatedContent !== content) {
219
+ fs.writeFileSync(filePath, updatedContent);
220
+ return true;
221
+ }
222
+
223
+ return false;
224
+ }
225
+
182
226
  function copyDirSync(src, dest) {
183
227
  if (!fs.existsSync(src)) return 0;
184
228
  fs.mkdirSync(dest, { recursive: true });
@@ -376,7 +420,7 @@ function installSkills(toolIds, update = false) {
376
420
  // 安装 Rules(Cursor, Windsurf, Gemini, Antigravity, OpenCode, Continue)
377
421
  // ============================================================================
378
422
 
379
- function installRules(toolIds, projectDir) {
423
+ function installRules(toolIds, projectDir, update = false) {
380
424
  const results = [];
381
425
 
382
426
  for (const toolId of toolIds) {
@@ -387,14 +431,20 @@ function installRules(toolIds, projectDir) {
387
431
  const rulesDestDir = path.join(projectDir, tool.rulesDir);
388
432
  fs.mkdirSync(rulesDestDir, { recursive: true });
389
433
 
390
- // 创建 devbooks.md 规则文件
434
+ // Create devbooks.md rule file
391
435
  const ruleContent = generateRuleContent(toolId);
392
436
  const ruleFileName = toolId === 'gemini' ? 'GEMINI.md' : 'devbooks.md';
393
437
  const rulePath = path.join(rulesDestDir, ruleFileName);
394
438
 
395
439
  if (!fs.existsSync(rulePath)) {
396
440
  fs.writeFileSync(rulePath, ruleContent);
397
- results.push({ tool: tool.name, type: 'rules', path: rulePath });
441
+ results.push({ tool: tool.name, type: 'rules', path: rulePath, action: 'created' });
442
+ } else if (update) {
443
+ // Update existing file's DEVBOOKS:START/END content
444
+ const updated = updateManagedContent(rulePath, ruleContent);
445
+ if (updated) {
446
+ results.push({ tool: tool.name, type: 'rules', path: rulePath, action: 'updated' });
447
+ }
398
448
  }
399
449
  }
400
450
  }
@@ -456,38 +506,56 @@ ${DEVBOOKS_MARKERS.end}
456
506
  // 安装自定义指令文件
457
507
  // ============================================================================
458
508
 
459
- function installInstructionFiles(toolIds, projectDir) {
509
+ function installInstructionFiles(toolIds, projectDir, update = false) {
460
510
  const results = [];
461
511
 
462
512
  for (const toolId of toolIds) {
463
513
  const tool = AI_TOOLS.find(t => t.id === toolId);
464
514
  if (!tool) continue;
465
515
 
466
- // GitHub Copilot 特殊处理
516
+ // GitHub Copilot special handling
467
517
  if (toolId === 'github-copilot') {
468
518
  const instructionsDir = path.join(projectDir, '.github', 'instructions');
469
519
  fs.mkdirSync(instructionsDir, { recursive: true });
470
520
 
471
521
  const copilotInstructionPath = path.join(projectDir, '.github', 'copilot-instructions.md');
522
+ const copilotContent = generateCopilotInstructions();
472
523
  if (!fs.existsSync(copilotInstructionPath)) {
473
- fs.writeFileSync(copilotInstructionPath, generateCopilotInstructions());
474
- results.push({ tool: 'GitHub Copilot', type: 'instructions', path: copilotInstructionPath });
524
+ fs.writeFileSync(copilotInstructionPath, copilotContent);
525
+ results.push({ tool: 'GitHub Copilot', type: 'instructions', path: copilotInstructionPath, action: 'created' });
526
+ } else if (update) {
527
+ const updated = updateManagedContent(copilotInstructionPath, copilotContent);
528
+ if (updated) {
529
+ results.push({ tool: 'GitHub Copilot', type: 'instructions', path: copilotInstructionPath, action: 'updated' });
530
+ }
475
531
  }
476
532
 
477
- // 创建 devbooks.instructions.md
533
+ // Create devbooks.instructions.md
478
534
  const devbooksInstructionPath = path.join(instructionsDir, 'devbooks.instructions.md');
535
+ const devbooksContent = generateCopilotDevbooksInstructions();
479
536
  if (!fs.existsSync(devbooksInstructionPath)) {
480
- fs.writeFileSync(devbooksInstructionPath, generateCopilotDevbooksInstructions());
481
- results.push({ tool: 'GitHub Copilot', type: 'instructions', path: devbooksInstructionPath });
537
+ fs.writeFileSync(devbooksInstructionPath, devbooksContent);
538
+ results.push({ tool: 'GitHub Copilot', type: 'instructions', path: devbooksInstructionPath, action: 'created' });
539
+ } else if (update) {
540
+ const updated = updateManagedContent(devbooksInstructionPath, devbooksContent);
541
+ if (updated) {
542
+ results.push({ tool: 'GitHub Copilot', type: 'instructions', path: devbooksInstructionPath, action: 'updated' });
543
+ }
482
544
  }
483
545
  }
484
546
 
485
- // 创建 AGENTS.md / CLAUDE.md / GEMINI.md
547
+ // Create AGENTS.md / CLAUDE.md / GEMINI.md
486
548
  if (tool.instructionFile && !tool.instructionFile.includes('/')) {
487
549
  const instructionPath = path.join(projectDir, tool.instructionFile);
550
+ const instructionContent = generateAgentsContent(tool.instructionFile);
488
551
  if (!fs.existsSync(instructionPath)) {
489
- fs.writeFileSync(instructionPath, generateAgentsContent(tool.instructionFile));
490
- results.push({ tool: tool.name, type: 'instruction', path: instructionPath });
552
+ fs.writeFileSync(instructionPath, instructionContent);
553
+ results.push({ tool: tool.name, type: 'instruction', path: instructionPath, action: 'created' });
554
+ } else if (update) {
555
+ const updated = updateManagedContent(instructionPath, instructionContent);
556
+ if (updated) {
557
+ results.push({ tool: tool.name, type: 'instruction', path: instructionPath, action: 'updated' });
558
+ }
491
559
  }
492
560
  }
493
561
  }
@@ -791,27 +859,27 @@ async function initCommand(projectDir, options) {
791
859
  }
792
860
 
793
861
  // ============================================================================
794
- // Update 命令
862
+ // Update Command
795
863
  // ============================================================================
796
864
 
797
865
  async function updateCommand(projectDir) {
798
866
  console.log();
799
- console.log(chalk.bold('DevBooks 更新'));
867
+ console.log(chalk.bold('DevBooks Update'));
800
868
  console.log();
801
869
 
802
- // 检查是否已初始化
870
+ // Check if initialized
803
871
  const configPath = path.join(projectDir, '.devbooks', 'config.yaml');
804
872
  if (!fs.existsSync(configPath)) {
805
- console.log(chalk.red('✗') + ` 未找到 DevBooks 配置。请先运行 \`${CLI_COMMAND} init\`。`);
873
+ console.log(chalk.red('✗') + ` DevBooks config not found. Please run \`${CLI_COMMAND} init\` first.`);
806
874
  process.exit(1);
807
875
  }
808
876
 
809
- // 加载配置
877
+ // Load config
810
878
  const config = loadConfig(projectDir);
811
879
  const configuredTools = config.aiTools;
812
880
 
813
881
  if (configuredTools.length === 0) {
814
- console.log(chalk.yellow('⚠') + ` 未配置任何 AI 工具。运行 \`${CLI_COMMAND} init\` 进行配置。`);
882
+ console.log(chalk.yellow('⚠') + ` No AI tools configured. Run \`${CLI_COMMAND} init\` to configure.`);
815
883
  return;
816
884
  }
817
885
 
@@ -819,31 +887,41 @@ async function updateCommand(projectDir) {
819
887
  const tool = AI_TOOLS.find(t => t.id === id);
820
888
  return tool ? tool.name : id;
821
889
  });
822
- console.log(chalk.blue('ℹ') + ` 检测到已配置的工具: ${toolNames.join(', ')}`);
890
+ console.log(chalk.blue('ℹ') + ` Detected configured tools: ${toolNames.join(', ')}`);
823
891
 
824
- // 更新 Skills
892
+ // Update Skills (global directory)
825
893
  const skillsResults = installSkills(configuredTools, true);
826
894
  for (const result of skillsResults) {
827
895
  if (result.count > 0) {
828
- console.log(chalk.green('✓') + ` ${result.tool} ${result.type}: 更新了 ${result.count}/${result.total} 个`);
896
+ console.log(chalk.green('✓') + ` ${result.tool} ${result.type}: updated ${result.count}/${result.total}`);
829
897
  }
830
898
  }
831
899
 
832
- // 更新 Rules
900
+ // Update Rules (project directory)
833
901
  const rulesTools = configuredTools.filter(id => {
834
902
  const tool = AI_TOOLS.find(t => t.id === id);
835
903
  return tool && tool.skillsSupport === SKILLS_SUPPORT.RULES;
836
904
  });
837
905
 
838
906
  if (rulesTools.length > 0) {
839
- const rulesResults = installRules(rulesTools, projectDir);
907
+ const rulesResults = installRules(rulesTools, projectDir, true);
840
908
  for (const result of rulesResults) {
841
- console.log(chalk.green('') + ` ${result.tool}: 更新了规则文件`);
909
+ if (result.action === 'updated') {
910
+ console.log(chalk.green('✓') + ` ${result.tool}: updated rule file`);
911
+ }
912
+ }
913
+ }
914
+
915
+ // Update instruction files (project directory)
916
+ const instructionResults = installInstructionFiles(configuredTools, projectDir, true);
917
+ for (const result of instructionResults) {
918
+ if (result.action === 'updated') {
919
+ console.log(chalk.green('✓') + ` ${result.tool}: updated instruction file ${path.relative(projectDir, result.path)}`);
842
920
  }
843
921
  }
844
922
 
845
923
  console.log();
846
- console.log(chalk.green('✓') + ' 更新完成!');
924
+ console.log(chalk.green('✓') + ' Update complete!');
847
925
  }
848
926
 
849
927
  // ============================================================================
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dev-playbooks",
3
- "version": "1.0.16",
3
+ "version": "1.0.17",
4
4
  "description": "AI-powered spec-driven development workflow",
5
5
  "keywords": [
6
6
  "devbooks",