@adversity/coding-tool-x 2.2.0 → 2.3.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.
- package/CHANGELOG.md +20 -0
- package/README.md +4 -14
- package/dist/web/assets/index-dhun1bYQ.js +3555 -0
- package/dist/web/assets/index-hHb7DAda.css +41 -0
- package/dist/web/index.html +2 -2
- package/package.json +5 -4
- package/src/index.js +2 -2
- package/src/server/api/agents.js +188 -0
- package/src/server/api/commands.js +261 -0
- package/src/server/api/config-templates.js +20 -5
- package/src/server/api/permissions.js +347 -0
- package/src/server/api/rules.js +188 -0
- package/src/server/api/skills.js +66 -14
- package/src/server/api/workspaces.js +30 -55
- package/src/server/index.js +3 -0
- package/src/server/services/agents-service.js +179 -1
- package/src/server/services/commands-service.js +231 -47
- package/src/server/services/config-templates-service.js +457 -106
- package/src/server/services/format-converter.js +506 -0
- package/src/server/services/repo-scanner-base.js +678 -0
- package/src/server/services/rules-service.js +179 -1
- package/src/server/services/skill-service.js +114 -61
- package/src/server/services/workspace-service.js +110 -1
- package/dist/web/assets/index-D1AYlFLZ.js +0 -3220
- package/dist/web/assets/index-aL3cKxSK.css +0 -41
- package/docs/CHANGELOG.md +0 -582
- package/docs/DIRECTORY_MIGRATION.md +0 -112
- package/docs/PROJECT_STRUCTURE.md +0 -396
package/src/server/api/agents.js
CHANGED
|
@@ -245,4 +245,192 @@ router.delete('/:scope/:fileName', (req, res) => {
|
|
|
245
245
|
}
|
|
246
246
|
});
|
|
247
247
|
|
|
248
|
+
// ==================== 仓库管理 API ====================
|
|
249
|
+
|
|
250
|
+
/**
|
|
251
|
+
* 获取所有代理(包括远程仓库)
|
|
252
|
+
* GET /api/agents/all
|
|
253
|
+
* Query: projectPath, refresh=1 强制刷新缓存
|
|
254
|
+
*/
|
|
255
|
+
router.get('/all', async (req, res) => {
|
|
256
|
+
try {
|
|
257
|
+
const { projectPath, refresh } = req.query;
|
|
258
|
+
const forceRefresh = refresh === '1';
|
|
259
|
+
const result = await agentsService.listAllAgents(projectPath || null, forceRefresh);
|
|
260
|
+
|
|
261
|
+
res.json({
|
|
262
|
+
success: true,
|
|
263
|
+
...result
|
|
264
|
+
});
|
|
265
|
+
} catch (err) {
|
|
266
|
+
console.error('[Agents API] List all agents error:', err);
|
|
267
|
+
res.status(500).json({
|
|
268
|
+
success: false,
|
|
269
|
+
message: err.message
|
|
270
|
+
});
|
|
271
|
+
}
|
|
272
|
+
});
|
|
273
|
+
|
|
274
|
+
/**
|
|
275
|
+
* 获取仓库列表
|
|
276
|
+
* GET /api/agents/repos
|
|
277
|
+
*/
|
|
278
|
+
router.get('/repos', (req, res) => {
|
|
279
|
+
try {
|
|
280
|
+
const repos = agentsService.getRepos();
|
|
281
|
+
res.json({
|
|
282
|
+
success: true,
|
|
283
|
+
repos
|
|
284
|
+
});
|
|
285
|
+
} catch (err) {
|
|
286
|
+
console.error('[Agents API] Get repos error:', err);
|
|
287
|
+
res.status(500).json({
|
|
288
|
+
success: false,
|
|
289
|
+
message: err.message
|
|
290
|
+
});
|
|
291
|
+
}
|
|
292
|
+
});
|
|
293
|
+
|
|
294
|
+
/**
|
|
295
|
+
* 添加仓库
|
|
296
|
+
* POST /api/agents/repos
|
|
297
|
+
* Body: { owner, name, branch, directory, enabled }
|
|
298
|
+
*/
|
|
299
|
+
router.post('/repos', (req, res) => {
|
|
300
|
+
try {
|
|
301
|
+
const { owner, name, branch = 'main', directory = '', enabled = true } = req.body;
|
|
302
|
+
|
|
303
|
+
if (!owner || !name) {
|
|
304
|
+
return res.status(400).json({
|
|
305
|
+
success: false,
|
|
306
|
+
message: 'Missing owner or name'
|
|
307
|
+
});
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
const repos = agentsService.addRepo({ owner, name, branch, directory, enabled });
|
|
311
|
+
|
|
312
|
+
res.json({
|
|
313
|
+
success: true,
|
|
314
|
+
repos
|
|
315
|
+
});
|
|
316
|
+
} catch (err) {
|
|
317
|
+
console.error('[Agents API] Add repo error:', err);
|
|
318
|
+
res.status(500).json({
|
|
319
|
+
success: false,
|
|
320
|
+
message: err.message
|
|
321
|
+
});
|
|
322
|
+
}
|
|
323
|
+
});
|
|
324
|
+
|
|
325
|
+
/**
|
|
326
|
+
* 删除仓库
|
|
327
|
+
* DELETE /api/agents/repos/:owner/:name
|
|
328
|
+
* Query: directory - 可选,子目录路径
|
|
329
|
+
*/
|
|
330
|
+
router.delete('/repos/:owner/:name', (req, res) => {
|
|
331
|
+
try {
|
|
332
|
+
const { owner, name } = req.params;
|
|
333
|
+
const { directory = '' } = req.query;
|
|
334
|
+
const repos = agentsService.removeRepo(owner, name, directory);
|
|
335
|
+
|
|
336
|
+
res.json({
|
|
337
|
+
success: true,
|
|
338
|
+
repos
|
|
339
|
+
});
|
|
340
|
+
} catch (err) {
|
|
341
|
+
console.error('[Agents API] Remove repo error:', err);
|
|
342
|
+
res.status(500).json({
|
|
343
|
+
success: false,
|
|
344
|
+
message: err.message
|
|
345
|
+
});
|
|
346
|
+
}
|
|
347
|
+
});
|
|
348
|
+
|
|
349
|
+
/**
|
|
350
|
+
* 切换仓库启用状态
|
|
351
|
+
* PUT /api/agents/repos/:owner/:name/toggle
|
|
352
|
+
* Body: { enabled, directory }
|
|
353
|
+
*/
|
|
354
|
+
router.put('/repos/:owner/:name/toggle', (req, res) => {
|
|
355
|
+
try {
|
|
356
|
+
const { owner, name } = req.params;
|
|
357
|
+
const { enabled, directory = '' } = req.body;
|
|
358
|
+
|
|
359
|
+
const repos = agentsService.toggleRepo(owner, name, directory, enabled);
|
|
360
|
+
|
|
361
|
+
res.json({
|
|
362
|
+
success: true,
|
|
363
|
+
repos
|
|
364
|
+
});
|
|
365
|
+
} catch (err) {
|
|
366
|
+
console.error('[Agents API] Toggle repo error:', err);
|
|
367
|
+
res.status(500).json({
|
|
368
|
+
success: false,
|
|
369
|
+
message: err.message
|
|
370
|
+
});
|
|
371
|
+
}
|
|
372
|
+
});
|
|
373
|
+
|
|
374
|
+
/**
|
|
375
|
+
* 从远程仓库安装代理
|
|
376
|
+
* POST /api/agents/install
|
|
377
|
+
* Body: agent object from listAllAgents
|
|
378
|
+
*/
|
|
379
|
+
router.post('/install', async (req, res) => {
|
|
380
|
+
try {
|
|
381
|
+
const agent = req.body;
|
|
382
|
+
|
|
383
|
+
if (!agent || !agent.repoOwner || !agent.repoName) {
|
|
384
|
+
return res.status(400).json({
|
|
385
|
+
success: false,
|
|
386
|
+
message: 'Missing agent info or repo info'
|
|
387
|
+
});
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
const result = await agentsService.installFromRemote(agent);
|
|
391
|
+
|
|
392
|
+
res.json({
|
|
393
|
+
success: true,
|
|
394
|
+
...result
|
|
395
|
+
});
|
|
396
|
+
} catch (err) {
|
|
397
|
+
console.error('[Agents API] Install agent error:', err);
|
|
398
|
+
res.status(500).json({
|
|
399
|
+
success: false,
|
|
400
|
+
message: err.message
|
|
401
|
+
});
|
|
402
|
+
}
|
|
403
|
+
});
|
|
404
|
+
|
|
405
|
+
/**
|
|
406
|
+
* 卸载代理
|
|
407
|
+
* POST /api/agents/uninstall
|
|
408
|
+
* Body: { fileName } - 代理的文件名(不含扩展名)
|
|
409
|
+
*/
|
|
410
|
+
router.post('/uninstall', (req, res) => {
|
|
411
|
+
try {
|
|
412
|
+
const { fileName } = req.body;
|
|
413
|
+
|
|
414
|
+
if (!fileName) {
|
|
415
|
+
return res.status(400).json({
|
|
416
|
+
success: false,
|
|
417
|
+
message: 'Missing fileName'
|
|
418
|
+
});
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
const result = agentsService.uninstallAgent(fileName);
|
|
422
|
+
|
|
423
|
+
res.json({
|
|
424
|
+
success: true,
|
|
425
|
+
...result
|
|
426
|
+
});
|
|
427
|
+
} catch (err) {
|
|
428
|
+
console.error('[Agents API] Uninstall agent error:', err);
|
|
429
|
+
res.status(500).json({
|
|
430
|
+
success: false,
|
|
431
|
+
message: err.message
|
|
432
|
+
});
|
|
433
|
+
}
|
|
434
|
+
});
|
|
435
|
+
|
|
248
436
|
module.exports = router;
|
|
@@ -242,4 +242,265 @@ router.delete('/:scope/:name', (req, res) => {
|
|
|
242
242
|
}
|
|
243
243
|
});
|
|
244
244
|
|
|
245
|
+
// ==================== 仓库管理 API ====================
|
|
246
|
+
|
|
247
|
+
/**
|
|
248
|
+
* 获取所有命令(包括远程仓库)
|
|
249
|
+
* GET /api/commands/all
|
|
250
|
+
* Query: projectPath, refresh=1 强制刷新缓存
|
|
251
|
+
*/
|
|
252
|
+
router.get('/all', async (req, res) => {
|
|
253
|
+
try {
|
|
254
|
+
const { projectPath, refresh } = req.query;
|
|
255
|
+
const forceRefresh = refresh === '1';
|
|
256
|
+
const result = await commandsService.listAllCommands(projectPath || null, forceRefresh);
|
|
257
|
+
|
|
258
|
+
res.json({
|
|
259
|
+
success: true,
|
|
260
|
+
...result
|
|
261
|
+
});
|
|
262
|
+
} catch (err) {
|
|
263
|
+
console.error('[Commands API] List all commands error:', err);
|
|
264
|
+
res.status(500).json({
|
|
265
|
+
success: false,
|
|
266
|
+
message: err.message
|
|
267
|
+
});
|
|
268
|
+
}
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* 获取仓库列表
|
|
273
|
+
* GET /api/commands/repos
|
|
274
|
+
*/
|
|
275
|
+
router.get('/repos', (req, res) => {
|
|
276
|
+
try {
|
|
277
|
+
const repos = commandsService.getRepos();
|
|
278
|
+
res.json({
|
|
279
|
+
success: true,
|
|
280
|
+
repos
|
|
281
|
+
});
|
|
282
|
+
} catch (err) {
|
|
283
|
+
console.error('[Commands API] Get repos error:', err);
|
|
284
|
+
res.status(500).json({
|
|
285
|
+
success: false,
|
|
286
|
+
message: err.message
|
|
287
|
+
});
|
|
288
|
+
}
|
|
289
|
+
});
|
|
290
|
+
|
|
291
|
+
/**
|
|
292
|
+
* 添加仓库
|
|
293
|
+
* POST /api/commands/repos
|
|
294
|
+
* Body: { owner, name, branch, directory, enabled }
|
|
295
|
+
*/
|
|
296
|
+
router.post('/repos', (req, res) => {
|
|
297
|
+
try {
|
|
298
|
+
const { owner, name, branch = 'main', directory = '', enabled = true } = req.body;
|
|
299
|
+
|
|
300
|
+
if (!owner || !name) {
|
|
301
|
+
return res.status(400).json({
|
|
302
|
+
success: false,
|
|
303
|
+
message: 'Missing owner or name'
|
|
304
|
+
});
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
const repos = commandsService.addRepo({ owner, name, branch, directory, enabled });
|
|
308
|
+
|
|
309
|
+
res.json({
|
|
310
|
+
success: true,
|
|
311
|
+
repos
|
|
312
|
+
});
|
|
313
|
+
} catch (err) {
|
|
314
|
+
console.error('[Commands API] Add repo error:', err);
|
|
315
|
+
res.status(500).json({
|
|
316
|
+
success: false,
|
|
317
|
+
message: err.message
|
|
318
|
+
});
|
|
319
|
+
}
|
|
320
|
+
});
|
|
321
|
+
|
|
322
|
+
/**
|
|
323
|
+
* 删除仓库
|
|
324
|
+
* DELETE /api/commands/repos/:owner/:name
|
|
325
|
+
* Query: directory - 可选,子目录路径
|
|
326
|
+
*/
|
|
327
|
+
router.delete('/repos/:owner/:name', (req, res) => {
|
|
328
|
+
try {
|
|
329
|
+
const { owner, name } = req.params;
|
|
330
|
+
const { directory = '' } = req.query;
|
|
331
|
+
const repos = commandsService.removeRepo(owner, name, directory);
|
|
332
|
+
|
|
333
|
+
res.json({
|
|
334
|
+
success: true,
|
|
335
|
+
repos
|
|
336
|
+
});
|
|
337
|
+
} catch (err) {
|
|
338
|
+
console.error('[Commands API] Remove repo error:', err);
|
|
339
|
+
res.status(500).json({
|
|
340
|
+
success: false,
|
|
341
|
+
message: err.message
|
|
342
|
+
});
|
|
343
|
+
}
|
|
344
|
+
});
|
|
345
|
+
|
|
346
|
+
/**
|
|
347
|
+
* 切换仓库启用状态
|
|
348
|
+
* PUT /api/commands/repos/:owner/:name/toggle
|
|
349
|
+
* Body: { enabled, directory }
|
|
350
|
+
*/
|
|
351
|
+
router.put('/repos/:owner/:name/toggle', (req, res) => {
|
|
352
|
+
try {
|
|
353
|
+
const { owner, name } = req.params;
|
|
354
|
+
const { enabled, directory = '' } = req.body;
|
|
355
|
+
|
|
356
|
+
const repos = commandsService.toggleRepo(owner, name, directory, enabled);
|
|
357
|
+
|
|
358
|
+
res.json({
|
|
359
|
+
success: true,
|
|
360
|
+
repos
|
|
361
|
+
});
|
|
362
|
+
} catch (err) {
|
|
363
|
+
console.error('[Commands API] Toggle repo error:', err);
|
|
364
|
+
res.status(500).json({
|
|
365
|
+
success: false,
|
|
366
|
+
message: err.message
|
|
367
|
+
});
|
|
368
|
+
}
|
|
369
|
+
});
|
|
370
|
+
|
|
371
|
+
/**
|
|
372
|
+
* 从远程仓库安装命令
|
|
373
|
+
* POST /api/commands/install
|
|
374
|
+
* Body: command object from listAllCommands
|
|
375
|
+
*/
|
|
376
|
+
router.post('/install', async (req, res) => {
|
|
377
|
+
try {
|
|
378
|
+
const command = req.body;
|
|
379
|
+
|
|
380
|
+
if (!command || !command.repoOwner || !command.repoName) {
|
|
381
|
+
return res.status(400).json({
|
|
382
|
+
success: false,
|
|
383
|
+
message: 'Missing command info or repo info'
|
|
384
|
+
});
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
const result = await commandsService.installFromRemote(command);
|
|
388
|
+
|
|
389
|
+
res.json({
|
|
390
|
+
success: true,
|
|
391
|
+
...result
|
|
392
|
+
});
|
|
393
|
+
} catch (err) {
|
|
394
|
+
console.error('[Commands API] Install command error:', err);
|
|
395
|
+
res.status(500).json({
|
|
396
|
+
success: false,
|
|
397
|
+
message: err.message
|
|
398
|
+
});
|
|
399
|
+
}
|
|
400
|
+
});
|
|
401
|
+
|
|
402
|
+
/**
|
|
403
|
+
* 卸载命令
|
|
404
|
+
* POST /api/commands/uninstall
|
|
405
|
+
* Body: { path } - 命令的相对路径
|
|
406
|
+
*/
|
|
407
|
+
router.post('/uninstall', (req, res) => {
|
|
408
|
+
try {
|
|
409
|
+
const { path } = req.body;
|
|
410
|
+
|
|
411
|
+
if (!path) {
|
|
412
|
+
return res.status(400).json({
|
|
413
|
+
success: false,
|
|
414
|
+
message: 'Missing path'
|
|
415
|
+
});
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
const result = commandsService.uninstallCommand(path);
|
|
419
|
+
|
|
420
|
+
res.json({
|
|
421
|
+
success: true,
|
|
422
|
+
...result
|
|
423
|
+
});
|
|
424
|
+
} catch (err) {
|
|
425
|
+
console.error('[Commands API] Uninstall command error:', err);
|
|
426
|
+
res.status(500).json({
|
|
427
|
+
success: false,
|
|
428
|
+
message: err.message
|
|
429
|
+
});
|
|
430
|
+
}
|
|
431
|
+
});
|
|
432
|
+
|
|
433
|
+
// ==================== 格式转换 API ====================
|
|
434
|
+
|
|
435
|
+
/**
|
|
436
|
+
* 转换命令格式
|
|
437
|
+
* POST /api/commands/convert
|
|
438
|
+
* Body: { content, targetFormat }
|
|
439
|
+
* - content: 命令内容
|
|
440
|
+
* - targetFormat: 目标格式 ('claude' | 'codex')
|
|
441
|
+
*/
|
|
442
|
+
router.post('/convert', (req, res) => {
|
|
443
|
+
try {
|
|
444
|
+
const { content, targetFormat } = req.body;
|
|
445
|
+
|
|
446
|
+
if (!content) {
|
|
447
|
+
return res.status(400).json({
|
|
448
|
+
success: false,
|
|
449
|
+
message: '请提供命令内容'
|
|
450
|
+
});
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
if (!['claude', 'codex'].includes(targetFormat)) {
|
|
454
|
+
return res.status(400).json({
|
|
455
|
+
success: false,
|
|
456
|
+
message: '目标格式必须是 claude 或 codex'
|
|
457
|
+
});
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
const result = commandsService.convertCommandFormat(content, targetFormat);
|
|
461
|
+
|
|
462
|
+
res.json({
|
|
463
|
+
success: true,
|
|
464
|
+
...result
|
|
465
|
+
});
|
|
466
|
+
} catch (err) {
|
|
467
|
+
console.error('[Commands API] Convert command error:', err);
|
|
468
|
+
res.status(500).json({
|
|
469
|
+
success: false,
|
|
470
|
+
message: err.message
|
|
471
|
+
});
|
|
472
|
+
}
|
|
473
|
+
});
|
|
474
|
+
|
|
475
|
+
/**
|
|
476
|
+
* 检测命令格式
|
|
477
|
+
* POST /api/commands/detect-format
|
|
478
|
+
* Body: { content }
|
|
479
|
+
*/
|
|
480
|
+
router.post('/detect-format', (req, res) => {
|
|
481
|
+
try {
|
|
482
|
+
const { content } = req.body;
|
|
483
|
+
|
|
484
|
+
if (!content) {
|
|
485
|
+
return res.status(400).json({
|
|
486
|
+
success: false,
|
|
487
|
+
message: '请提供命令内容'
|
|
488
|
+
});
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
const format = commandsService.detectFormat(content);
|
|
492
|
+
|
|
493
|
+
res.json({
|
|
494
|
+
success: true,
|
|
495
|
+
format
|
|
496
|
+
});
|
|
497
|
+
} catch (err) {
|
|
498
|
+
console.error('[Commands API] Detect format error:', err);
|
|
499
|
+
res.status(500).json({
|
|
500
|
+
success: false,
|
|
501
|
+
message: err.message
|
|
502
|
+
});
|
|
503
|
+
}
|
|
504
|
+
});
|
|
505
|
+
|
|
245
506
|
module.exports = router;
|
|
@@ -46,8 +46,15 @@ router.get('/available-configs', (req, res) => {
|
|
|
46
46
|
* GET /api/config-templates/:id
|
|
47
47
|
* 获取单个配置模板详情
|
|
48
48
|
*/
|
|
49
|
-
|
|
49
|
+
// 注意:此路由需要放在所有静态子路由之后,避免把 /available-configs 等路径当成 id
|
|
50
|
+
router.get('/:id', (req, res, next) => {
|
|
50
51
|
try {
|
|
52
|
+
// 兜底:即便路由顺序被改动,也避免把保留路径当成模板 ID
|
|
53
|
+
const reservedIds = new Set(['available-configs']);
|
|
54
|
+
if (reservedIds.has(req.params.id)) {
|
|
55
|
+
return next();
|
|
56
|
+
}
|
|
57
|
+
|
|
51
58
|
const template = templatesService.getTemplateById(req.params.id);
|
|
52
59
|
if (!template) {
|
|
53
60
|
return res.status(404).json({
|
|
@@ -132,14 +139,18 @@ router.delete('/:id', (req, res) => {
|
|
|
132
139
|
*/
|
|
133
140
|
router.post('/:id/apply', (req, res) => {
|
|
134
141
|
try {
|
|
135
|
-
const { targetPath } = req.body;
|
|
142
|
+
const { targetPath, aiConfigType } = req.body;
|
|
136
143
|
if (!targetPath) {
|
|
137
144
|
return res.status(400).json({
|
|
138
145
|
success: false,
|
|
139
146
|
message: '目标路径不能为空'
|
|
140
147
|
});
|
|
141
148
|
}
|
|
142
|
-
const
|
|
149
|
+
const options = {};
|
|
150
|
+
if (aiConfigType) {
|
|
151
|
+
options.aiConfigType = aiConfigType;
|
|
152
|
+
}
|
|
153
|
+
const result = templatesService.applyTemplateToProject(targetPath, req.params.id, options);
|
|
143
154
|
res.json({
|
|
144
155
|
success: true,
|
|
145
156
|
message: '模板应用成功',
|
|
@@ -159,14 +170,18 @@ router.post('/:id/apply', (req, res) => {
|
|
|
159
170
|
*/
|
|
160
171
|
router.post('/:id/preview', (req, res) => {
|
|
161
172
|
try {
|
|
162
|
-
const { targetPath } = req.body;
|
|
173
|
+
const { targetPath, aiConfigType } = req.body;
|
|
163
174
|
if (!targetPath) {
|
|
164
175
|
return res.status(400).json({
|
|
165
176
|
success: false,
|
|
166
177
|
message: '目标路径不能为空'
|
|
167
178
|
});
|
|
168
179
|
}
|
|
169
|
-
const
|
|
180
|
+
const options = {};
|
|
181
|
+
if (aiConfigType) {
|
|
182
|
+
options.aiConfigType = aiConfigType;
|
|
183
|
+
}
|
|
184
|
+
const preview = templatesService.previewTemplateApplication(targetPath, req.params.id, options);
|
|
170
185
|
res.json({
|
|
171
186
|
success: true,
|
|
172
187
|
data: preview
|