@fitlab-ai/agent-infra 0.5.6 → 0.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/README.md +92 -4
- package/README.zh-CN.md +92 -4
- package/bin/cli.js +28 -4
- package/lib/defaults.json +1 -0
- package/lib/init.js +68 -4
- package/lib/prompt.js +28 -1
- package/lib/render.js +1 -1
- package/lib/sandbox/commands/rm.js +6 -4
- package/lib/sandbox/commands/vm.js +43 -16
- package/lib/sandbox/config.js +5 -0
- package/lib/sandbox/engine.js +125 -16
- package/lib/sandbox/task-resolver.js +13 -6
- package/package.json +2 -2
- package/templates/.agents/QUICKSTART.en.md +17 -0
- package/templates/.agents/QUICKSTART.zh-CN.md +17 -0
- package/templates/.agents/README.en.md +70 -1
- package/templates/.agents/README.zh-CN.md +70 -1
- package/templates/.agents/rules/issue-pr-commands.en.md +5 -0
- package/templates/.agents/rules/issue-pr-commands.zh-CN.md +5 -0
- package/templates/.agents/rules/issue-sync.en.md +5 -0
- package/templates/.agents/rules/issue-sync.zh-CN.md +5 -0
- package/templates/.agents/rules/label-milestone-setup.en.md +5 -0
- package/templates/.agents/rules/label-milestone-setup.zh-CN.md +5 -0
- package/templates/.agents/rules/milestone-inference.en.md +5 -0
- package/templates/.agents/rules/milestone-inference.zh-CN.md +5 -0
- package/templates/.agents/rules/pr-sync.en.md +5 -0
- package/templates/.agents/rules/pr-sync.zh-CN.md +5 -0
- package/templates/.agents/rules/release-commands.en.md +5 -0
- package/templates/.agents/rules/release-commands.zh-CN.md +5 -0
- package/templates/.agents/rules/security-alerts.en.md +5 -0
- package/templates/.agents/rules/security-alerts.zh-CN.md +5 -0
- package/templates/.agents/scripts/platform-adapters/platform-sync.js +6 -0
- package/templates/.agents/skills/analyze-task/SKILL.en.md +2 -2
- package/templates/.agents/skills/analyze-task/SKILL.zh-CN.md +2 -2
- package/templates/.agents/skills/block-task/SKILL.en.md +1 -1
- package/templates/.agents/skills/block-task/SKILL.zh-CN.md +1 -1
- package/templates/.agents/skills/cancel-task/SKILL.en.md +1 -1
- package/templates/.agents/skills/cancel-task/SKILL.zh-CN.md +2 -2
- package/templates/.agents/skills/check-task/SKILL.en.md +1 -1
- package/templates/.agents/skills/check-task/SKILL.zh-CN.md +1 -1
- package/templates/.agents/skills/close-codescan/SKILL.en.md +1 -1
- package/templates/.agents/skills/close-codescan/SKILL.zh-CN.md +1 -1
- package/templates/.agents/skills/close-dependabot/SKILL.en.md +1 -1
- package/templates/.agents/skills/close-dependabot/SKILL.zh-CN.md +1 -1
- package/templates/.agents/skills/commit/SKILL.en.md +1 -1
- package/templates/.agents/skills/commit/SKILL.zh-CN.md +1 -1
- package/templates/.agents/skills/create-issue/SKILL.en.md +2 -2
- package/templates/.agents/skills/create-issue/SKILL.zh-CN.md +2 -2
- package/templates/.agents/skills/create-pr/SKILL.en.md +1 -1
- package/templates/.agents/skills/create-pr/SKILL.zh-CN.md +1 -1
- package/templates/.agents/skills/create-release-note/SKILL.en.md +8 -1
- package/templates/.agents/skills/create-release-note/SKILL.zh-CN.md +8 -1
- package/templates/.agents/skills/create-task/SKILL.en.md +2 -2
- package/templates/.agents/skills/create-task/SKILL.zh-CN.md +2 -2
- package/templates/.agents/skills/implement-task/SKILL.en.md +2 -2
- package/templates/.agents/skills/implement-task/SKILL.zh-CN.md +2 -2
- package/templates/.agents/skills/import-codescan/SKILL.en.md +2 -2
- package/templates/.agents/skills/import-codescan/SKILL.zh-CN.md +2 -2
- package/templates/.agents/skills/import-dependabot/SKILL.en.md +2 -2
- package/templates/.agents/skills/import-dependabot/SKILL.zh-CN.md +2 -2
- package/templates/.agents/skills/import-issue/SKILL.en.md +2 -2
- package/templates/.agents/skills/import-issue/SKILL.zh-CN.md +2 -2
- package/templates/.agents/skills/init-labels/SKILL.en.md +1 -1
- package/templates/.agents/skills/init-labels/SKILL.zh-CN.md +1 -1
- package/templates/.agents/skills/init-labels/scripts/init-labels.sh +6 -0
- package/templates/.agents/skills/init-milestones/SKILL.en.md +1 -1
- package/templates/.agents/skills/init-milestones/SKILL.zh-CN.md +1 -1
- package/templates/.agents/skills/init-milestones/scripts/init-milestones.sh +6 -0
- package/templates/.agents/skills/plan-task/SKILL.en.md +2 -2
- package/templates/.agents/skills/plan-task/SKILL.zh-CN.md +2 -2
- package/templates/.agents/skills/post-release/SKILL.en.md +95 -0
- package/templates/.agents/skills/post-release/SKILL.zh-CN.md +95 -0
- package/templates/.agents/skills/refine-task/SKILL.en.md +1 -1
- package/templates/.agents/skills/refine-task/SKILL.zh-CN.md +1 -1
- package/templates/.agents/skills/refine-title/SKILL.en.md +1 -1
- package/templates/.agents/skills/refine-title/SKILL.zh-CN.md +1 -1
- package/templates/.agents/skills/release/SKILL.en.md +6 -1
- package/templates/.agents/skills/release/SKILL.zh-CN.md +6 -1
- package/templates/.agents/skills/release/scripts/manage-milestones.sh +6 -0
- package/templates/.agents/skills/restore-task/SKILL.en.md +2 -2
- package/templates/.agents/skills/restore-task/SKILL.zh-CN.md +2 -2
- package/templates/.agents/skills/review-task/SKILL.en.md +2 -2
- package/templates/.agents/skills/review-task/SKILL.zh-CN.md +2 -2
- package/templates/.agents/skills/test/SKILL.en.md +1 -1
- package/templates/.agents/skills/test/SKILL.zh-CN.md +1 -1
- package/templates/.agents/skills/test-integration/SKILL.en.md +1 -1
- package/templates/.agents/skills/test-integration/SKILL.zh-CN.md +1 -1
- package/templates/.agents/skills/update-agent-infra/SKILL.en.md +10 -2
- package/templates/.agents/skills/update-agent-infra/SKILL.zh-CN.md +4 -2
- package/templates/.agents/skills/update-agent-infra/scripts/sync-templates.js +289 -12
- package/templates/.agents/skills/upgrade-dependency/SKILL.en.md +1 -1
- package/templates/.agents/skills/upgrade-dependency/SKILL.zh-CN.md +1 -1
- package/templates/.agents/templates/task.en.md +2 -2
- package/templates/.agents/templates/task.zh-CN.md +2 -2
- package/templates/.claude/commands/post-release.en.md +8 -0
- package/templates/.claude/commands/post-release.zh-CN.md +8 -0
- package/templates/.gemini/commands/_project_/post-release.en.toml +6 -0
- package/templates/.gemini/commands/_project_/post-release.zh-CN.toml +6 -0
- package/templates/.opencode/commands/post-release.en.md +9 -0
- package/templates/.opencode/commands/post-release.zh-CN.md +9 -0
|
@@ -24,6 +24,7 @@ const DEFAULTS = {
|
|
|
24
24
|
"type": "github"
|
|
25
25
|
},
|
|
26
26
|
"sandbox": {
|
|
27
|
+
"engine": null,
|
|
27
28
|
"runtimes": [
|
|
28
29
|
"node20"
|
|
29
30
|
],
|
|
@@ -78,7 +79,7 @@ const DEFAULTS = {
|
|
|
78
79
|
}
|
|
79
80
|
};
|
|
80
81
|
|
|
81
|
-
const INSTALLER_VERSION = "v0.5.
|
|
82
|
+
const INSTALLER_VERSION = "v0.5.7";
|
|
82
83
|
const PACKAGE_NAME = '@fitlab-ai/agent-infra';
|
|
83
84
|
// Add a new identifier here only after shipping matching .{platform}. template variants.
|
|
84
85
|
const KNOWN_PLATFORMS = new Set(['github']);
|
|
@@ -86,6 +87,21 @@ const KNOWN_LANGUAGES = new Set(['en', 'zh-CN']);
|
|
|
86
87
|
|
|
87
88
|
function norm(p) { return p.replace(/\\/g, '/'); }
|
|
88
89
|
|
|
90
|
+
function normDir(p) {
|
|
91
|
+
return norm(p).replace(/^\.\//, '').replace(/\/+$/, '');
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function isInsideProject(projectRoot, relativePath) {
|
|
95
|
+
if (typeof relativePath !== 'string' || relativePath.trim() === '' || path.isAbsolute(relativePath)) {
|
|
96
|
+
return false;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const root = path.resolve(projectRoot);
|
|
100
|
+
const resolved = path.resolve(projectRoot, relativePath);
|
|
101
|
+
const rel = path.relative(root, resolved);
|
|
102
|
+
return rel !== '' && !rel.startsWith('..') && !path.isAbsolute(rel);
|
|
103
|
+
}
|
|
104
|
+
|
|
89
105
|
function globMatch(pattern, filePath) {
|
|
90
106
|
const p = norm(pattern), f = norm(filePath);
|
|
91
107
|
const globstarDir = '__GLOBSTAR_DIR__';
|
|
@@ -192,17 +208,26 @@ function detectCustomSkills(projectRoot, templateSkillNames) {
|
|
|
192
208
|
.sort((left, right) => left.dirName.localeCompare(right.dirName));
|
|
193
209
|
}
|
|
194
210
|
|
|
195
|
-
function isCustomProtected(targetPath, customSkills, project) {
|
|
211
|
+
function isCustomProtected(targetPath, customSkills, project, customTUICommandTargets) {
|
|
196
212
|
const normalized = norm(targetPath);
|
|
197
213
|
|
|
198
214
|
return customSkills.some(({ dirName }) => (
|
|
199
215
|
normalized.startsWith(`.agents/skills/${dirName}/`) ||
|
|
200
216
|
normalized === `.claude/commands/${dirName}.md` ||
|
|
201
217
|
normalized === `.opencode/commands/${dirName}.md` ||
|
|
202
|
-
normalized === '.gemini/commands/' + project + '/' + dirName + '.toml'
|
|
218
|
+
normalized === '.gemini/commands/' + project + '/' + dirName + '.toml' ||
|
|
219
|
+
customTUICommandTargets.has(normalized)
|
|
203
220
|
));
|
|
204
221
|
}
|
|
205
222
|
|
|
223
|
+
function recordCustomTUISkipped(report, entry) {
|
|
224
|
+
report?.custom?.customTUIs?.skipped?.push(entry);
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
function recordCustomTUISkippedRef(report, entry) {
|
|
228
|
+
report?.custom?.customTUIs?.skippedRefs?.push(entry);
|
|
229
|
+
}
|
|
230
|
+
|
|
206
231
|
function expandHome(inputPath) {
|
|
207
232
|
if (inputPath === '~') return os.homedir();
|
|
208
233
|
if (inputPath.startsWith('~/')) {
|
|
@@ -212,6 +237,81 @@ function expandHome(inputPath) {
|
|
|
212
237
|
return path.resolve(inputPath);
|
|
213
238
|
}
|
|
214
239
|
|
|
240
|
+
function mergeTemplateSources(baseRoot, sources, report) {
|
|
241
|
+
const sourceMap = new Map();
|
|
242
|
+
const sourceMeta = new Map();
|
|
243
|
+
const conflictsByRel = new Map();
|
|
244
|
+
const baseRels = walkDir(baseRoot).map((filePath) => norm(path.relative(baseRoot, filePath)));
|
|
245
|
+
|
|
246
|
+
for (const rel of baseRels) {
|
|
247
|
+
sourceMap.set(rel, baseRoot);
|
|
248
|
+
sourceMeta.set(rel, { type: 'builtin' });
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
const recordConflict = (rel, winner, ignored) => {
|
|
252
|
+
const existing = conflictsByRel.get(rel);
|
|
253
|
+
if (existing) {
|
|
254
|
+
existing.winner = winner;
|
|
255
|
+
existing.ignored.push(...ignored);
|
|
256
|
+
return;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
const conflict = { rel, winner, ignored: [...ignored] };
|
|
260
|
+
conflictsByRel.set(rel, conflict);
|
|
261
|
+
report.templateSources.conflicts.push(conflict);
|
|
262
|
+
};
|
|
263
|
+
|
|
264
|
+
const templateSources = Array.isArray(sources) ? sources : [];
|
|
265
|
+
for (const [index, source] of templateSources.entries()) {
|
|
266
|
+
if (source?.type !== 'local') continue;
|
|
267
|
+
if (typeof source.path !== 'string' || source.path.trim() === '') {
|
|
268
|
+
report.templateSources.errors.push({
|
|
269
|
+
index,
|
|
270
|
+
type: String(source?.type || ''),
|
|
271
|
+
path: String(source?.path || ''),
|
|
272
|
+
reason: 'invalid path'
|
|
273
|
+
});
|
|
274
|
+
continue;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
const srcDir = expandHome(source.path);
|
|
278
|
+
if (!fs.existsSync(srcDir) || !fs.statSync(srcDir).isDirectory()) {
|
|
279
|
+
report.templateSources.errors.push({
|
|
280
|
+
index,
|
|
281
|
+
type: source.type,
|
|
282
|
+
path: source.path,
|
|
283
|
+
reason: 'directory not found'
|
|
284
|
+
});
|
|
285
|
+
continue;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
const extRels = walkDir(srcDir).map((filePath) => norm(path.relative(srcDir, filePath)));
|
|
289
|
+
const sourceInfo = { type: source.type, path: source.path };
|
|
290
|
+
for (const rel of extRels) {
|
|
291
|
+
const existing = sourceMeta.get(rel);
|
|
292
|
+
if (existing?.type === 'builtin') {
|
|
293
|
+
recordConflict(rel, existing, [sourceInfo]);
|
|
294
|
+
continue;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
if (existing) {
|
|
298
|
+
recordConflict(rel, sourceInfo, [existing]);
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
sourceMap.set(rel, srcDir);
|
|
302
|
+
sourceMeta.set(rel, sourceInfo);
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
report.templateSources.loaded += 1;
|
|
306
|
+
report.templateSources.files += extRels.length;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
return {
|
|
310
|
+
mergedRels: [...sourceMap.keys()],
|
|
311
|
+
sourceMap
|
|
312
|
+
};
|
|
313
|
+
}
|
|
314
|
+
|
|
215
315
|
function writeIfChanged(projectRoot, targetPath, content, reportBucket) {
|
|
216
316
|
const fullPath = path.join(projectRoot, targetPath);
|
|
217
317
|
const exists = fs.existsSync(fullPath);
|
|
@@ -388,7 +488,154 @@ function generateOpenCodeCommand(skill, lang) {
|
|
|
388
488
|
return `${lines.join('\n')}\n`;
|
|
389
489
|
}
|
|
390
490
|
|
|
391
|
-
function
|
|
491
|
+
function validateCustomTUIs(projectRoot, customTUIs, report) {
|
|
492
|
+
const tools = Array.isArray(customTUIs) ? customTUIs : [];
|
|
493
|
+
return tools
|
|
494
|
+
.map((tool, index) => {
|
|
495
|
+
if (typeof tool?.dir !== 'string' || tool.dir.trim() === '') {
|
|
496
|
+
recordCustomTUISkipped(report, {
|
|
497
|
+
index,
|
|
498
|
+
name: String(tool?.name || ''),
|
|
499
|
+
dir: String(tool?.dir || ''),
|
|
500
|
+
reason: 'invalid dir'
|
|
501
|
+
});
|
|
502
|
+
return null;
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
if (!isInsideProject(projectRoot, tool.dir)) {
|
|
506
|
+
recordCustomTUISkipped(report, {
|
|
507
|
+
index,
|
|
508
|
+
name: String(tool?.name || ''),
|
|
509
|
+
dir: tool.dir,
|
|
510
|
+
reason: 'dir must be a relative path inside the project root'
|
|
511
|
+
});
|
|
512
|
+
return null;
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
return { ...tool, index, dir: normDir(tool.dir) };
|
|
516
|
+
})
|
|
517
|
+
.filter(Boolean);
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
function customTUITargetPath(tool, refFile, refSkillName, skillName) {
|
|
521
|
+
const targetFile = refFile.includes(refSkillName)
|
|
522
|
+
? refFile.replaceAll(refSkillName, skillName)
|
|
523
|
+
: `${skillName}${path.extname(refFile)}`;
|
|
524
|
+
return norm(path.join(tool.dir, targetFile));
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
function findCustomTUIReference(projectRoot, tool, templateSkillNames, report, logSkipped = false) {
|
|
528
|
+
const cmdDir = path.join(projectRoot, tool.dir);
|
|
529
|
+
if (!fs.existsSync(cmdDir) || !fs.statSync(cmdDir).isDirectory()) {
|
|
530
|
+
if (logSkipped) {
|
|
531
|
+
recordCustomTUISkipped(report, {
|
|
532
|
+
index: tool.index,
|
|
533
|
+
name: String(tool.name || ''),
|
|
534
|
+
dir: tool.dir,
|
|
535
|
+
reason: 'directory not found'
|
|
536
|
+
});
|
|
537
|
+
}
|
|
538
|
+
return null;
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
const cmdFiles = fs.readdirSync(cmdDir)
|
|
542
|
+
.filter((file) => fs.statSync(path.join(cmdDir, file)).isFile())
|
|
543
|
+
.sort((left, right) => left.localeCompare(right));
|
|
544
|
+
if (cmdFiles.length === 0) {
|
|
545
|
+
if (logSkipped) {
|
|
546
|
+
recordCustomTUISkipped(report, {
|
|
547
|
+
index: tool.index,
|
|
548
|
+
name: String(tool.name || ''),
|
|
549
|
+
dir: tool.dir,
|
|
550
|
+
reason: 'no command files'
|
|
551
|
+
});
|
|
552
|
+
}
|
|
553
|
+
return null;
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
let sawKnownSkillReference = false;
|
|
557
|
+
|
|
558
|
+
for (const file of cmdFiles) {
|
|
559
|
+
const content = fs.readFileSync(path.join(cmdDir, file), 'utf8');
|
|
560
|
+
const match = content.match(/\.agents\/skills\/([^/]+)\/SKILL\.md/);
|
|
561
|
+
if (!match) continue;
|
|
562
|
+
|
|
563
|
+
const skillName = match[1];
|
|
564
|
+
if (!templateSkillNames.has(skillName)) continue;
|
|
565
|
+
|
|
566
|
+
const skillMd = path.join(projectRoot, '.agents/skills', skillName, 'SKILL.md');
|
|
567
|
+
if (!fs.existsSync(skillMd)) continue;
|
|
568
|
+
|
|
569
|
+
const meta = parseSkillFrontmatter(skillMd);
|
|
570
|
+
if (!meta.description) continue;
|
|
571
|
+
|
|
572
|
+
sawKnownSkillReference = true;
|
|
573
|
+
if (!content.includes(meta.description)) {
|
|
574
|
+
if (logSkipped) {
|
|
575
|
+
recordCustomTUISkippedRef(report, {
|
|
576
|
+
index: tool.index,
|
|
577
|
+
name: String(tool.name || ''),
|
|
578
|
+
dir: tool.dir,
|
|
579
|
+
file,
|
|
580
|
+
skill: skillName,
|
|
581
|
+
reason: 'description not found in reference command file'
|
|
582
|
+
});
|
|
583
|
+
}
|
|
584
|
+
continue;
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
return { content, file, skillName, skillDesc: meta.description };
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
if (logSkipped) {
|
|
591
|
+
recordCustomTUISkipped(report, {
|
|
592
|
+
index: tool.index,
|
|
593
|
+
name: String(tool.name || ''),
|
|
594
|
+
dir: tool.dir,
|
|
595
|
+
reason: sawKnownSkillReference
|
|
596
|
+
? 'no reference command file with matching description'
|
|
597
|
+
: 'no usable reference command file'
|
|
598
|
+
});
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
return null;
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
function buildCustomTUICommandTargets(projectRoot, customSkills, customTUIs, templateSkillNames) {
|
|
605
|
+
const targets = new Set();
|
|
606
|
+
for (const tool of customTUIs) {
|
|
607
|
+
const ref = findCustomTUIReference(projectRoot, tool, templateSkillNames, null, false);
|
|
608
|
+
if (!ref) continue;
|
|
609
|
+
|
|
610
|
+
for (const skill of customSkills) {
|
|
611
|
+
targets.add(customTUITargetPath(tool, ref.file, ref.skillName, skill.dirName));
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
return targets;
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
function learnAndGenerateCommands(projectRoot, customSkills, tool, templateSkillNames, report) {
|
|
619
|
+
const ref = findCustomTUIReference(projectRoot, tool, templateSkillNames, report, true);
|
|
620
|
+
if (!ref) return;
|
|
621
|
+
|
|
622
|
+
for (const skill of customSkills) {
|
|
623
|
+
const descToken = '__AGENT_INFRA_CUSTOM_SKILL_DESCRIPTION__';
|
|
624
|
+
const generated = ref.content
|
|
625
|
+
.replaceAll(ref.skillDesc, descToken)
|
|
626
|
+
.replaceAll(ref.skillName, skill.dirName)
|
|
627
|
+
.replaceAll(descToken, skill.description);
|
|
628
|
+
|
|
629
|
+
writeIfChanged(
|
|
630
|
+
projectRoot,
|
|
631
|
+
customTUITargetPath(tool, ref.file, ref.skillName, skill.dirName),
|
|
632
|
+
generated,
|
|
633
|
+
report.custom.commands
|
|
634
|
+
);
|
|
635
|
+
}
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
function generateCustomCommands(projectRoot, customSkills, project, lang, report, customTUIs, templateSkillNames) {
|
|
392
639
|
for (const skill of customSkills) {
|
|
393
640
|
writeIfChanged(
|
|
394
641
|
projectRoot,
|
|
@@ -409,6 +656,11 @@ function generateCustomCommands(projectRoot, customSkills, project, lang, report
|
|
|
409
656
|
report.custom.commands
|
|
410
657
|
);
|
|
411
658
|
}
|
|
659
|
+
|
|
660
|
+
const tools = Array.isArray(customTUIs) ? customTUIs : [];
|
|
661
|
+
for (const tool of tools) {
|
|
662
|
+
learnAndGenerateCommands(projectRoot, customSkills, tool, templateSkillNames, report);
|
|
663
|
+
}
|
|
412
664
|
}
|
|
413
665
|
|
|
414
666
|
function matchesAny(rel, patterns) {
|
|
@@ -698,6 +950,7 @@ function syncTemplates(projectRoot, templateRootOverride) {
|
|
|
698
950
|
|
|
699
951
|
const { project, org, language: lang = 'en' } = cfg;
|
|
700
952
|
const platformType = cfg.platform?.type || DEFAULTS.platform.type;
|
|
953
|
+
const customTUIsConfig = Array.isArray(cfg.customTUIs) ? cfg.customTUIs : [];
|
|
701
954
|
const vars = { project, org };
|
|
702
955
|
const templateSkillNames = listTemplateSkillNames(templateRoot);
|
|
703
956
|
const protectedCustomSkills = detectCustomSkills(projectRoot, templateSkillNames);
|
|
@@ -710,6 +963,13 @@ function syncTemplates(projectRoot, templateRootOverride) {
|
|
|
710
963
|
templateVersion: version,
|
|
711
964
|
templateRoot: norm(templateRoot),
|
|
712
965
|
registryAdded: [],
|
|
966
|
+
templateSources: {
|
|
967
|
+
configured: 0,
|
|
968
|
+
loaded: 0,
|
|
969
|
+
files: 0,
|
|
970
|
+
errors: [],
|
|
971
|
+
conflicts: []
|
|
972
|
+
},
|
|
713
973
|
managed: { written: [], created: [], unchanged: [], skippedMerged: [], removed: [] },
|
|
714
974
|
custom: {
|
|
715
975
|
detected: [],
|
|
@@ -718,6 +978,7 @@ function syncTemplates(projectRoot, templateRootOverride) {
|
|
|
718
978
|
unchanged: [],
|
|
719
979
|
removed: [],
|
|
720
980
|
sourceErrors: [],
|
|
981
|
+
customTUIs: { skipped: [], skippedRefs: [] },
|
|
721
982
|
commands: { generated: [], updated: [], unchanged: [] }
|
|
722
983
|
},
|
|
723
984
|
ejected: { created: [], skipped: [] },
|
|
@@ -725,6 +986,13 @@ function syncTemplates(projectRoot, templateRootOverride) {
|
|
|
725
986
|
configUpdated: false,
|
|
726
987
|
selfUpdate: false
|
|
727
988
|
};
|
|
989
|
+
const customTUIs = validateCustomTUIs(projectRoot, customTUIsConfig, report);
|
|
990
|
+
const customTUICommandTargets = buildCustomTUICommandTargets(
|
|
991
|
+
projectRoot,
|
|
992
|
+
protectedCustomSkills,
|
|
993
|
+
customTUIs,
|
|
994
|
+
templateSkillNames
|
|
995
|
+
);
|
|
728
996
|
|
|
729
997
|
const known = new Set([...managed, ...merged, ...ejected]);
|
|
730
998
|
for (const e of (DEFAULTS.files.managed || [])) {
|
|
@@ -734,7 +1002,10 @@ function syncTemplates(projectRoot, templateRootOverride) {
|
|
|
734
1002
|
if (!known.has(e)) { merged.push(e); known.add(e); report.registryAdded.push({ entry: e, list: 'merged' }); }
|
|
735
1003
|
}
|
|
736
1004
|
|
|
737
|
-
const
|
|
1005
|
+
const templateSources = Array.isArray(cfg.templates?.sources) ? cfg.templates.sources : [];
|
|
1006
|
+
report.templateSources.configured = templateSources.length;
|
|
1007
|
+
const { mergedRels, sourceMap } = mergeTemplateSources(templateRoot, templateSources, report);
|
|
1008
|
+
const allRels = mergedRels;
|
|
738
1009
|
const allSet = new Set(allRels);
|
|
739
1010
|
for (const entry of managed) {
|
|
740
1011
|
const isDir = entry.endsWith('/');
|
|
@@ -743,10 +1014,14 @@ function syncTemplates(projectRoot, templateRootOverride) {
|
|
|
743
1014
|
|
|
744
1015
|
if (isDir) {
|
|
745
1016
|
const dir = path.join(templateRoot, entry);
|
|
746
|
-
|
|
747
|
-
|
|
1017
|
+
const builtinRels = fs.existsSync(dir)
|
|
1018
|
+
? walkDir(dir).map((filePath) => norm(path.relative(templateRoot, filePath)))
|
|
1019
|
+
: [];
|
|
1020
|
+
const prefix = norm(entry);
|
|
1021
|
+
const externalRels = allRels.filter((rel) => rel.startsWith(prefix) && !builtinRels.includes(rel));
|
|
1022
|
+
entryRels = [...builtinRels, ...externalRels];
|
|
1023
|
+
if (!entryRels.length) continue;
|
|
748
1024
|
} else {
|
|
749
|
-
entryRels = [];
|
|
750
1025
|
entryRels = entryVariantRels(entry, allSet, platformType);
|
|
751
1026
|
if (!entryRels.length) continue;
|
|
752
1027
|
}
|
|
@@ -761,7 +1036,8 @@ function syncTemplates(projectRoot, templateRootOverride) {
|
|
|
761
1036
|
continue;
|
|
762
1037
|
}
|
|
763
1038
|
|
|
764
|
-
const
|
|
1039
|
+
const srcRoot = sourceMap.get(src) || templateRoot;
|
|
1040
|
+
const srcFull = path.join(srcRoot, src);
|
|
765
1041
|
const dstFull = path.join(projectRoot, tgt);
|
|
766
1042
|
const bin = isBinary(srcFull);
|
|
767
1043
|
const content = bin
|
|
@@ -795,7 +1071,7 @@ function syncTemplates(projectRoot, templateRootOverride) {
|
|
|
795
1071
|
for (const projFile of projFiles) {
|
|
796
1072
|
if (expectedTargets.has(projFile)) continue;
|
|
797
1073
|
if (projFile === configPathRel) continue;
|
|
798
|
-
if (isCustomProtected(projFile, protectedCustomSkills, project)) continue;
|
|
1074
|
+
if (isCustomProtected(projFile, protectedCustomSkills, project, customTUICommandTargets)) continue;
|
|
799
1075
|
if (matchesAny(projFile, merged) || matchesAny(projFile, ejected)) continue;
|
|
800
1076
|
|
|
801
1077
|
fs.unlinkSync(path.join(projectRoot, projFile));
|
|
@@ -816,7 +1092,7 @@ function syncTemplates(projectRoot, templateRootOverride) {
|
|
|
816
1092
|
|
|
817
1093
|
const customSkills = detectCustomSkills(projectRoot, templateSkillNames);
|
|
818
1094
|
report.custom.detected = customSkills.map((skill) => skill.dirName);
|
|
819
|
-
generateCustomCommands(projectRoot, customSkills, project, lang, report);
|
|
1095
|
+
generateCustomCommands(projectRoot, customSkills, project, lang, report, customTUIs, templateSkillNames);
|
|
820
1096
|
|
|
821
1097
|
for (const entry of ejected) {
|
|
822
1098
|
const dstFull = path.join(projectRoot, entry);
|
|
@@ -830,7 +1106,8 @@ function syncTemplates(projectRoot, templateRootOverride) {
|
|
|
830
1106
|
const src = selected.get(target);
|
|
831
1107
|
if (!src) continue;
|
|
832
1108
|
|
|
833
|
-
const
|
|
1109
|
+
const srcRoot = sourceMap.get(src) || templateRoot;
|
|
1110
|
+
const content = renderContent(fs.readFileSync(path.join(srcRoot, src), 'utf8'), vars);
|
|
834
1111
|
const dir = path.dirname(dstFull);
|
|
835
1112
|
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
|
|
836
1113
|
fs.writeFileSync(dstFull, content);
|
|
@@ -61,7 +61,7 @@ Report:
|
|
|
61
61
|
|
|
62
62
|
Suggest next step:
|
|
63
63
|
|
|
64
|
-
> **IMPORTANT**: All TUI command formats listed below must be output in full. Do not show only the format for the current AI agent.
|
|
64
|
+
> **IMPORTANT**: All TUI command formats listed below must be output in full. Do not show only the format for the current AI agent. If `.agents/.airc.json` configures custom TUIs (via `customTUIs`), read each tool's `name` and `invoke`, then add the matching command line in the same format (`${skillName}` becomes the skill name and `${projectName}` becomes the project name).
|
|
65
65
|
|
|
66
66
|
```
|
|
67
67
|
Next step - commit changes:
|
|
@@ -61,7 +61,7 @@ description: "升级项目依赖到新版本并验证"
|
|
|
61
61
|
|
|
62
62
|
建议下一步:
|
|
63
63
|
|
|
64
|
-
> **重要**:以下「下一步」中列出的所有 TUI 命令格式必须完整输出,不要只展示当前 AI
|
|
64
|
+
> **重要**:以下「下一步」中列出的所有 TUI 命令格式必须完整输出,不要只展示当前 AI 代理对应的格式。如果 `.agents/.airc.json` 中配置了自定义 TUI(`customTUIs`),读取每个工具的 `name` 和 `invoke`,按同样格式补充对应命令行(`${skillName}` 替换为技能名,`${projectName}` 替换为项目名)。
|
|
65
65
|
|
|
66
66
|
```
|
|
67
67
|
下一步 - 提交代码:
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
---
|
|
2
2
|
id: task-XXX
|
|
3
3
|
type: feature # feature | bugfix | refactor | docs | review
|
|
4
|
-
branch:
|
|
4
|
+
branch: # <project>-<type>-<slug>
|
|
5
5
|
workflow: feature-development # feature-development | bug-fix | code-review | refactoring
|
|
6
6
|
status: open # open | in-progress | review | blocked | completed
|
|
7
7
|
created_at: YYYY-MM-DDTHH:mm:ss±HH:MM
|
|
8
8
|
updated_at: YYYY-MM-DDTHH:mm:ss±HH:MM
|
|
9
9
|
current_step: analysis # analysis | design | implementation | review | fix | commit
|
|
10
|
-
assigned_to:
|
|
10
|
+
assigned_to: # claude | codex | gemini | opencode | human
|
|
11
11
|
---
|
|
12
12
|
|
|
13
13
|
# Task: [Title]
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
---
|
|
2
2
|
id: task-XXX
|
|
3
3
|
type: feature # feature | bugfix | refactor | docs | review
|
|
4
|
-
branch:
|
|
4
|
+
branch: # <project>-<type>-<slug>
|
|
5
5
|
workflow: feature-development # feature-development | bug-fix | code-review | refactoring
|
|
6
6
|
status: open # open | in-progress | review | blocked | completed
|
|
7
7
|
created_at: YYYY-MM-DDTHH:mm:ss±HH:MM
|
|
8
8
|
updated_at: YYYY-MM-DDTHH:mm:ss±HH:MM
|
|
9
9
|
current_step: analysis # analysis | design | implementation | review | fix | commit
|
|
10
|
-
assigned_to:
|
|
10
|
+
assigned_to: # claude | codex | gemini | opencode | human
|
|
11
11
|
---
|
|
12
12
|
|
|
13
13
|
# 任务:[标题]
|