jjb-cmd 2.5.6 → 2.5.7
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 +1 -1
- package/src/ai-pull.js +99 -67
package/package.json
CHANGED
package/src/ai-pull.js
CHANGED
|
@@ -18,7 +18,7 @@ const CURSOR_DB_PATH = path.join(
|
|
|
18
18
|
// Git 仓库地址
|
|
19
19
|
const AI_REPO_URL = 'http://192.168.1.242:10985/root/jjb-ai.git';
|
|
20
20
|
// Git 分支/标签(默认值)
|
|
21
|
-
const DEFAULT_BRANCH = '
|
|
21
|
+
const DEFAULT_BRANCH = 'v_2.0.0';
|
|
22
22
|
|
|
23
23
|
/**
|
|
24
24
|
* 删除目录(回调方式,使用递归删除)
|
|
@@ -241,40 +241,72 @@ module.exports = (branch) => {
|
|
|
241
241
|
});
|
|
242
242
|
console.log(`✓ 仓库代码拉取成功(分支: ${targetBranch})`);
|
|
243
243
|
|
|
244
|
-
// 步骤2:
|
|
245
|
-
console.log('步骤2: 正在复制 .
|
|
246
|
-
const
|
|
247
|
-
const
|
|
244
|
+
// 步骤2: 将仓库 admin 下所有子文件夹同步到当前项目 .cursor/(与 admin 同名的子目录)
|
|
245
|
+
console.log('步骤2: 正在复制 admin 下的文件夹到 .cursor/...');
|
|
246
|
+
const adminPath = path.join(cloneDir, 'admin');
|
|
247
|
+
const cursorDir = path.join(root_path, '.cursor');
|
|
248
248
|
|
|
249
|
-
if (!fs.existsSync(
|
|
250
|
-
console.log('【Warning】:仓库中未找到
|
|
249
|
+
if (!fs.existsSync(adminPath)) {
|
|
250
|
+
console.log('【Warning】:仓库中未找到 admin 文件夹');
|
|
251
251
|
step3();
|
|
252
252
|
} else {
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
253
|
+
let subdirs;
|
|
254
|
+
try {
|
|
255
|
+
subdirs = fs.readdirSync(adminPath).filter((name) => {
|
|
256
|
+
const p = path.join(adminPath, name);
|
|
257
|
+
try {
|
|
258
|
+
return fs.statSync(p).isDirectory();
|
|
259
|
+
} catch (e) {
|
|
260
|
+
return false;
|
|
261
|
+
}
|
|
262
|
+
});
|
|
263
|
+
} catch (e) {
|
|
264
|
+
console.error('【Error】:读取 admin 目录失败', e.message);
|
|
265
|
+
cleanupAndExit(1);
|
|
266
|
+
return;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
if (subdirs.length === 0) {
|
|
270
|
+
console.log('【Warning】:admin 下没有子文件夹可同步');
|
|
271
|
+
step3();
|
|
272
|
+
} else {
|
|
273
|
+
try {
|
|
274
|
+
if (!fs.existsSync(cursorDir)) {
|
|
275
|
+
fs.mkdirSync(cursorDir, { recursive: true });
|
|
276
|
+
}
|
|
277
|
+
} catch (e) {
|
|
278
|
+
console.error('【Error】:创建 .cursor 目录失败', e.message);
|
|
279
|
+
cleanupAndExit(1);
|
|
280
|
+
return;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
function copyAdminDirAt(i) {
|
|
284
|
+
if (i >= subdirs.length) {
|
|
285
|
+
console.log(`✓ 已将 admin 下 ${subdirs.length} 个文件夹复制到 .cursor/(${subdirs.join(', ')})`);
|
|
286
|
+
step3();
|
|
287
|
+
return;
|
|
288
|
+
}
|
|
289
|
+
const name = subdirs[i];
|
|
290
|
+
const srcPath = path.join(adminPath, name);
|
|
291
|
+
const destPath = path.join(cursorDir, name);
|
|
292
|
+
const afterCopy = (err) => {
|
|
258
293
|
if (err) {
|
|
259
|
-
console.error(
|
|
294
|
+
console.error(`【Error】:复制 admin/${name} 失败`, err.message);
|
|
260
295
|
cleanupAndExit(1);
|
|
261
296
|
return;
|
|
262
297
|
}
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
console.error('【Error】:复制 .ai 文件夹失败', err.message);
|
|
272
|
-
cleanupAndExit(1);
|
|
273
|
-
return;
|
|
298
|
+
copyAdminDirAt(i + 1);
|
|
299
|
+
};
|
|
300
|
+
if (fs.existsSync(destPath)) {
|
|
301
|
+
deleteDir(destPath, () => {
|
|
302
|
+
copyFolder(srcPath, destPath, afterCopy);
|
|
303
|
+
});
|
|
304
|
+
} else {
|
|
305
|
+
copyFolder(srcPath, destPath, afterCopy);
|
|
274
306
|
}
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
copyAdminDirAt(0);
|
|
278
310
|
}
|
|
279
311
|
}
|
|
280
312
|
} catch (error) {
|
|
@@ -283,26 +315,26 @@ module.exports = (branch) => {
|
|
|
283
315
|
}
|
|
284
316
|
});
|
|
285
317
|
|
|
286
|
-
// 步骤3:
|
|
318
|
+
// 步骤3: 将 admin/rules/PROJECT.md 写入 Cursor user rules (SQLite 数据库)
|
|
287
319
|
function step3() {
|
|
288
320
|
console.log('步骤3: 正在更新 Cursor user rules (SQLite 数据库)...');
|
|
289
|
-
const
|
|
321
|
+
const projectRulesPath = path.join(cloneDir, 'admin', 'rules', 'PROJECT.md');
|
|
290
322
|
|
|
291
323
|
try {
|
|
292
|
-
if (!fs.existsSync(
|
|
293
|
-
console.log('【Warning】:仓库中未找到
|
|
324
|
+
if (!fs.existsSync(projectRulesPath)) {
|
|
325
|
+
console.log('【Warning】:仓库中未找到 admin/rules/PROJECT.md 文件');
|
|
294
326
|
step4();
|
|
295
327
|
return;
|
|
296
328
|
}
|
|
297
329
|
|
|
298
|
-
// 读取
|
|
299
|
-
const
|
|
330
|
+
// 读取 PROJECT.md 内容
|
|
331
|
+
const rulesContent = fs.readFileSync(projectRulesPath, 'utf8');
|
|
300
332
|
|
|
301
333
|
// 检查数据库文件是否存在
|
|
302
334
|
if (!fs.existsSync(CURSOR_DB_PATH)) {
|
|
303
335
|
console.log('【Warning】:Cursor 数据库文件不存在,可能 Cursor 未安装或路径不正确');
|
|
304
336
|
console.log(`【路径】:${CURSOR_DB_PATH}`);
|
|
305
|
-
console.log('【建议】:请确保已安装 Cursor 编辑器,或手动复制
|
|
337
|
+
console.log('【建议】:请确保已安装 Cursor 编辑器,或手动复制 admin/rules/PROJECT.md 内容');
|
|
306
338
|
step4();
|
|
307
339
|
return;
|
|
308
340
|
}
|
|
@@ -348,7 +380,7 @@ module.exports = (branch) => {
|
|
|
348
380
|
try {
|
|
349
381
|
const sqlite3 = require('sqlite3');
|
|
350
382
|
// sqlite3 是异步的,需要使用回调或 Promise
|
|
351
|
-
console.log(`【调试】:准备更新数据库,内容长度: ${
|
|
383
|
+
console.log(`【调试】:准备更新数据库,内容长度: ${rulesContent.length} 字符`);
|
|
352
384
|
|
|
353
385
|
const db = new sqlite3.Database(CURSOR_DB_PATH, (err) => {
|
|
354
386
|
if (err) {
|
|
@@ -379,8 +411,8 @@ module.exports = (branch) => {
|
|
|
379
411
|
oldContent = String(row.value);
|
|
380
412
|
}
|
|
381
413
|
|
|
382
|
-
if (oldContent ===
|
|
383
|
-
console.log('✓
|
|
414
|
+
if (oldContent === rulesContent) {
|
|
415
|
+
console.log('✓ admin/rules/PROJECT.md 内容未变化,无需更新');
|
|
384
416
|
console.log('【提示】:请重启 Cursor 编辑器以使规则生效');
|
|
385
417
|
db.close();
|
|
386
418
|
step4();
|
|
@@ -388,7 +420,7 @@ module.exports = (branch) => {
|
|
|
388
420
|
}
|
|
389
421
|
|
|
390
422
|
// 更新现有记录
|
|
391
|
-
db.run("UPDATE ItemTable SET value = ? WHERE key = ?", [
|
|
423
|
+
db.run("UPDATE ItemTable SET value = ? WHERE key = ?", [rulesContent, 'aicontext.personalContext'], function(err) {
|
|
392
424
|
if (err) {
|
|
393
425
|
console.error('【Error】:更新数据库失败', err.message);
|
|
394
426
|
if (err.code === 'SQLITE_BUSY' || err.message.includes('locked')) {
|
|
@@ -422,15 +454,15 @@ module.exports = (branch) => {
|
|
|
422
454
|
verifyContent = String(verifyRow.value);
|
|
423
455
|
}
|
|
424
456
|
|
|
425
|
-
if (verifyContent ===
|
|
426
|
-
console.log(`✓
|
|
457
|
+
if (verifyContent === rulesContent) {
|
|
458
|
+
console.log(`✓ PROJECT.md 已更新到 Cursor user rules (${verifyRow.len} 字节)`);
|
|
427
459
|
// 显示前几行内容确认
|
|
428
460
|
const previewLines = verifyContent.split('\n').slice(0, 3).join('\n');
|
|
429
461
|
console.log(`【预览】:内容前3行:\n${previewLines}${verifyContent.split('\n').length > 3 ? '...' : ''}`);
|
|
430
462
|
} else {
|
|
431
463
|
console.error(`【Error】:更新后内容不匹配!`);
|
|
432
|
-
console.error(` 期望长度: ${
|
|
433
|
-
console.error(` 期望前100字符: ${
|
|
464
|
+
console.error(` 期望长度: ${rulesContent.length}, 实际长度: ${verifyContent.length}`);
|
|
465
|
+
console.error(` 期望前100字符: ${rulesContent.substring(0, 100)}`);
|
|
434
466
|
console.error(` 实际前100字符: ${verifyContent.substring(0, 100)}`);
|
|
435
467
|
}
|
|
436
468
|
} else {
|
|
@@ -445,7 +477,7 @@ module.exports = (branch) => {
|
|
|
445
477
|
} else {
|
|
446
478
|
console.log('【调试】:记录不存在,将插入新记录');
|
|
447
479
|
// 插入新记录
|
|
448
|
-
db.run("INSERT INTO ItemTable (key, value) VALUES (?, ?)", ['aicontext.personalContext',
|
|
480
|
+
db.run("INSERT INTO ItemTable (key, value) VALUES (?, ?)", ['aicontext.personalContext', rulesContent], function(err) {
|
|
449
481
|
if (err) {
|
|
450
482
|
console.error('【Error】:插入数据库失败', err.message);
|
|
451
483
|
if (err.code === 'SQLITE_BUSY' || err.message.includes('locked')) {
|
|
@@ -478,14 +510,14 @@ module.exports = (branch) => {
|
|
|
478
510
|
verifyContent = String(verifyRow.value);
|
|
479
511
|
}
|
|
480
512
|
|
|
481
|
-
if (verifyContent ===
|
|
482
|
-
console.log(`✓
|
|
513
|
+
if (verifyContent === rulesContent) {
|
|
514
|
+
console.log(`✓ PROJECT.md 已添加到 Cursor user rules (${verifyRow.len} 字节)`);
|
|
483
515
|
// 显示前几行内容确认
|
|
484
516
|
const previewLines = verifyContent.split('\n').slice(0, 3).join('\n');
|
|
485
517
|
console.log(`【预览】:内容前3行:\n${previewLines}${verifyContent.split('\n').length > 3 ? '...' : ''}`);
|
|
486
518
|
} else {
|
|
487
519
|
console.error(`【Error】:插入后内容不匹配!`);
|
|
488
|
-
console.error(` 期望长度: ${
|
|
520
|
+
console.error(` 期望长度: ${rulesContent.length}, 实际长度: ${verifyContent.length}`);
|
|
489
521
|
}
|
|
490
522
|
} else {
|
|
491
523
|
console.error('【Error】:插入后验证失败,值为空');
|
|
@@ -513,7 +545,7 @@ module.exports = (branch) => {
|
|
|
513
545
|
const db = new Database(CURSOR_DB_PATH, { readonly: false });
|
|
514
546
|
|
|
515
547
|
try {
|
|
516
|
-
console.log(`【调试】:准备更新数据库,内容长度: ${
|
|
548
|
+
console.log(`【调试】:准备更新数据库,内容长度: ${rulesContent.length} 字符`);
|
|
517
549
|
|
|
518
550
|
// 先查询是否存在该键
|
|
519
551
|
const row = db.prepare("SELECT value, length(value) as len FROM ItemTable WHERE key = ?").get('aicontext.personalContext');
|
|
@@ -529,8 +561,8 @@ module.exports = (branch) => {
|
|
|
529
561
|
oldContent = String(row.value);
|
|
530
562
|
}
|
|
531
563
|
|
|
532
|
-
if (oldContent ===
|
|
533
|
-
console.log('✓
|
|
564
|
+
if (oldContent === rulesContent) {
|
|
565
|
+
console.log('✓ admin/rules/PROJECT.md 内容未变化,无需更新');
|
|
534
566
|
console.log('【提示】:请重启 Cursor 编辑器以使规则生效');
|
|
535
567
|
db.close();
|
|
536
568
|
step4();
|
|
@@ -539,7 +571,7 @@ module.exports = (branch) => {
|
|
|
539
571
|
|
|
540
572
|
// 更新现有记录
|
|
541
573
|
const updateStmt = db.prepare("UPDATE ItemTable SET value = ? WHERE key = ?");
|
|
542
|
-
const result = updateStmt.run(
|
|
574
|
+
const result = updateStmt.run(rulesContent, 'aicontext.personalContext');
|
|
543
575
|
console.log(`【调试】:更新影响行数: ${result.changes}`);
|
|
544
576
|
|
|
545
577
|
if (result.changes === 0) {
|
|
@@ -560,15 +592,15 @@ module.exports = (branch) => {
|
|
|
560
592
|
verifyContent = String(verifyRow.value);
|
|
561
593
|
}
|
|
562
594
|
|
|
563
|
-
if (verifyContent ===
|
|
564
|
-
console.log(`✓
|
|
595
|
+
if (verifyContent === rulesContent) {
|
|
596
|
+
console.log(`✓ PROJECT.md 已更新到 Cursor user rules (${verifyRow.len} 字节)`);
|
|
565
597
|
// 显示前几行内容确认
|
|
566
598
|
const previewLines = verifyContent.split('\n').slice(0, 3).join('\n');
|
|
567
599
|
console.log(`【预览】:内容前3行:\n${previewLines}${verifyContent.split('\n').length > 3 ? '...' : ''}`);
|
|
568
600
|
} else {
|
|
569
601
|
console.error(`【Error】:更新后内容不匹配!`);
|
|
570
|
-
console.error(` 期望长度: ${
|
|
571
|
-
console.error(` 期望前100字符: ${
|
|
602
|
+
console.error(` 期望长度: ${rulesContent.length}, 实际长度: ${verifyContent.length}`);
|
|
603
|
+
console.error(` 期望前100字符: ${rulesContent.substring(0, 100)}`);
|
|
572
604
|
console.error(` 实际前100字符: ${verifyContent.substring(0, 100)}`);
|
|
573
605
|
}
|
|
574
606
|
} else {
|
|
@@ -578,7 +610,7 @@ module.exports = (branch) => {
|
|
|
578
610
|
console.log('【调试】:记录不存在,将插入新记录');
|
|
579
611
|
// 插入新记录
|
|
580
612
|
const insertStmt = db.prepare("INSERT INTO ItemTable (key, value) VALUES (?, ?)");
|
|
581
|
-
const result = insertStmt.run('aicontext.personalContext',
|
|
613
|
+
const result = insertStmt.run('aicontext.personalContext', rulesContent);
|
|
582
614
|
console.log(`【调试】:插入结果,最后插入ID: ${result.lastInsertRowid}`);
|
|
583
615
|
|
|
584
616
|
if (!result.lastInsertRowid) {
|
|
@@ -598,14 +630,14 @@ module.exports = (branch) => {
|
|
|
598
630
|
verifyContent = String(verifyRow.value);
|
|
599
631
|
}
|
|
600
632
|
|
|
601
|
-
if (verifyContent ===
|
|
602
|
-
console.log(`✓
|
|
633
|
+
if (verifyContent === rulesContent) {
|
|
634
|
+
console.log(`✓ PROJECT.md 已添加到 Cursor user rules (${verifyRow.len} 字节)`);
|
|
603
635
|
// 显示前几行内容确认
|
|
604
636
|
const previewLines = verifyContent.split('\n').slice(0, 3).join('\n');
|
|
605
637
|
console.log(`【预览】:内容前3行:\n${previewLines}${verifyContent.split('\n').length > 3 ? '...' : ''}`);
|
|
606
638
|
} else {
|
|
607
639
|
console.error(`【Error】:插入后内容不匹配!`);
|
|
608
|
-
console.error(` 期望长度: ${
|
|
640
|
+
console.error(` 期望长度: ${rulesContent.length}, 实际长度: ${verifyContent.length}`);
|
|
609
641
|
}
|
|
610
642
|
} else {
|
|
611
643
|
console.error('【Error】:插入后验证失败,值为空');
|
|
@@ -630,8 +662,8 @@ module.exports = (branch) => {
|
|
|
630
662
|
|
|
631
663
|
step4();
|
|
632
664
|
} catch (error) {
|
|
633
|
-
console.error('【Error】:处理
|
|
634
|
-
console.log('【Warning】:可能 Cursor 未安装或路径不正确,请手动复制
|
|
665
|
+
console.error('【Error】:处理 admin/rules/PROJECT.md 失败', error.message);
|
|
666
|
+
console.log('【Warning】:可能 Cursor 未安装或路径不正确,请手动复制 admin/rules/PROJECT.md 内容');
|
|
635
667
|
step4();
|
|
636
668
|
}
|
|
637
669
|
}
|
|
@@ -644,23 +676,23 @@ module.exports = (branch) => {
|
|
|
644
676
|
try {
|
|
645
677
|
if (fs.existsSync(gitignorePath)) {
|
|
646
678
|
let gitignoreContent = fs.readFileSync(gitignorePath, 'utf8');
|
|
647
|
-
const
|
|
679
|
+
const cursorIgnorePattern = /^\.cursor\/?\s*$/m;
|
|
648
680
|
|
|
649
|
-
if (!
|
|
681
|
+
if (!cursorIgnorePattern.test(gitignoreContent)) {
|
|
650
682
|
// 如果文件末尾没有换行,先添加换行
|
|
651
683
|
if (!gitignoreContent.endsWith('\n')) {
|
|
652
684
|
gitignoreContent += '\n';
|
|
653
685
|
}
|
|
654
|
-
gitignoreContent += '.
|
|
686
|
+
gitignoreContent += '.cursor/\n';
|
|
655
687
|
fs.writeFileSync(gitignorePath, gitignoreContent, 'utf8');
|
|
656
|
-
console.log('✓ 已在 .gitignore 中添加 .
|
|
688
|
+
console.log('✓ 已在 .gitignore 中添加 .cursor/ 忽略规则');
|
|
657
689
|
} else {
|
|
658
|
-
console.log('✓ .gitignore 中已存在 .
|
|
690
|
+
console.log('✓ .gitignore 中已存在 .cursor 忽略规则');
|
|
659
691
|
}
|
|
660
692
|
} else {
|
|
661
693
|
// 如果 .gitignore 不存在,创建它
|
|
662
|
-
fs.writeFileSync(gitignorePath, '.
|
|
663
|
-
console.log('✓ 已创建 .gitignore 并添加 .
|
|
694
|
+
fs.writeFileSync(gitignorePath, '.cursor/\n', 'utf8');
|
|
695
|
+
console.log('✓ 已创建 .gitignore 并添加 .cursor/ 忽略规则');
|
|
664
696
|
}
|
|
665
697
|
} catch (error) {
|
|
666
698
|
console.error('【Error】:更新 .gitignore 失败', error.message);
|