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.
- package/bin/devbooks.js +104 -26
- 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
|
-
//
|
|
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,
|
|
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
|
-
//
|
|
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,
|
|
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
|
-
//
|
|
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,
|
|
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('✗') + `
|
|
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('⚠') + `
|
|
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('ℹ') + `
|
|
890
|
+
console.log(chalk.blue('ℹ') + ` Detected configured tools: ${toolNames.join(', ')}`);
|
|
823
891
|
|
|
824
|
-
//
|
|
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}:
|
|
896
|
+
console.log(chalk.green('✓') + ` ${result.tool} ${result.type}: updated ${result.count}/${result.total}`);
|
|
829
897
|
}
|
|
830
898
|
}
|
|
831
899
|
|
|
832
|
-
//
|
|
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
|
-
|
|
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
|
// ============================================================================
|