workflow-ai 1.0.39 → 1.0.40
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
CHANGED
|
@@ -144,6 +144,13 @@ function checkBacklog(planId) {
|
|
|
144
144
|
|
|
145
145
|
for (const ticket of tickets) {
|
|
146
146
|
const { frontmatter, id } = ticket;
|
|
147
|
+
|
|
148
|
+
// Пропускаем тикеты, требующие ручного выполнения
|
|
149
|
+
if (frontmatter.type === 'human') {
|
|
150
|
+
console.log(`[INFO] ${id}: type is 'human', skipping (requires manual execution)`);
|
|
151
|
+
continue;
|
|
152
|
+
}
|
|
153
|
+
|
|
147
154
|
const conditions = frontmatter.conditions || [];
|
|
148
155
|
const dependencies = frontmatter.dependencies || [];
|
|
149
156
|
|
|
@@ -51,6 +51,12 @@ function moveToReady(ticketId) {
|
|
|
51
51
|
const content = fs.readFileSync(sourcePath, 'utf8');
|
|
52
52
|
const { frontmatter, body } = parseFrontmatter(content);
|
|
53
53
|
|
|
54
|
+
// Пропускаем тикеты, требующие ручного выполнения
|
|
55
|
+
if (frontmatter.type === 'human') {
|
|
56
|
+
console.log(`[INFO] ${ticketId}: type is 'human', skipping (requires manual execution)`);
|
|
57
|
+
return false;
|
|
58
|
+
}
|
|
59
|
+
|
|
54
60
|
frontmatter.updated_at = new Date().toISOString();
|
|
55
61
|
|
|
56
62
|
const newContent = serializeFrontmatter(frontmatter) + body;
|
|
@@ -523,6 +523,12 @@ function pickNextTicket(planId) {
|
|
|
523
523
|
const eligibleTickets = tickets.filter(ticket => {
|
|
524
524
|
const { frontmatter } = ticket;
|
|
525
525
|
|
|
526
|
+
// Пропускаем тикеты, требующие ручного выполнения
|
|
527
|
+
if (frontmatter.type === 'human') {
|
|
528
|
+
logger.info(`Skipping ticket ${ticket.id}: type is 'human' (requires manual execution)`);
|
|
529
|
+
return false;
|
|
530
|
+
}
|
|
531
|
+
|
|
526
532
|
// Проверка условий
|
|
527
533
|
const conditions = frontmatter.conditions || [];
|
|
528
534
|
const conditionsMet = conditions.every(checkCondition);
|
|
@@ -573,6 +579,61 @@ function pickNextTicket(planId) {
|
|
|
573
579
|
};
|
|
574
580
|
}
|
|
575
581
|
|
|
582
|
+
/**
|
|
583
|
+
* Архивирует все done-тикеты, принадлежащие архивным планам (plans/archive/).
|
|
584
|
+
* Сканирует все планы в plans/archive/, находит их тикеты в done/ и перемещает в archive/.
|
|
585
|
+
*/
|
|
586
|
+
function archiveTicketsOfArchivedPlans() {
|
|
587
|
+
const archivedPlansDir = path.join(WORKFLOW_DIR, 'plans', 'archive');
|
|
588
|
+
if (!fs.existsSync(archivedPlansDir)) return { archived: [] };
|
|
589
|
+
|
|
590
|
+
// Собираем ID всех архивных планов
|
|
591
|
+
const archivedPlanIds = new Set();
|
|
592
|
+
const planFiles = fs.readdirSync(archivedPlansDir).filter(f => f.endsWith('.md'));
|
|
593
|
+
for (const file of planFiles) {
|
|
594
|
+
const id = normalizePlanId(file);
|
|
595
|
+
if (id) archivedPlanIds.add(id);
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
if (archivedPlanIds.size === 0) return { archived: [] };
|
|
599
|
+
|
|
600
|
+
if (!fs.existsSync(DONE_DIR)) return { archived: [] };
|
|
601
|
+
|
|
602
|
+
if (!fs.existsSync(ARCHIVE_DIR)) {
|
|
603
|
+
fs.mkdirSync(ARCHIVE_DIR, { recursive: true });
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
const archived = [];
|
|
607
|
+
const files = fs.readdirSync(DONE_DIR).filter(f => f.endsWith('.md') && f !== '.gitkeep.md');
|
|
608
|
+
|
|
609
|
+
for (const file of files) {
|
|
610
|
+
const filePath = path.join(DONE_DIR, file);
|
|
611
|
+
try {
|
|
612
|
+
const content = fs.readFileSync(filePath, 'utf8');
|
|
613
|
+
const { frontmatter, body } = parseFrontmatter(content);
|
|
614
|
+
const ticketPlanId = normalizePlanId(frontmatter.parent_plan);
|
|
615
|
+
|
|
616
|
+
if (!ticketPlanId || !archivedPlanIds.has(ticketPlanId)) continue;
|
|
617
|
+
|
|
618
|
+
const ticketId = frontmatter.id || file.replace('.md', '');
|
|
619
|
+
|
|
620
|
+
frontmatter.updated_at = new Date().toISOString();
|
|
621
|
+
frontmatter.archived_at = new Date().toISOString();
|
|
622
|
+
|
|
623
|
+
const destPath = path.join(ARCHIVE_DIR, file);
|
|
624
|
+
fs.writeFileSync(destPath, serializeFrontmatter(frontmatter) + body, 'utf8');
|
|
625
|
+
fs.unlinkSync(filePath);
|
|
626
|
+
|
|
627
|
+
archived.push(ticketId);
|
|
628
|
+
logger.info(`[ARCHIVE] ${ticketId}: done → archive (plan ${ticketPlanId} is archived)`);
|
|
629
|
+
} catch (e) {
|
|
630
|
+
logger.warn(`Failed to archive ticket ${file}: ${e.message}`);
|
|
631
|
+
}
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
return { archived };
|
|
635
|
+
}
|
|
636
|
+
|
|
576
637
|
// Main entry point
|
|
577
638
|
async function main() {
|
|
578
639
|
const planId = extractPlanId();
|
|
@@ -596,6 +657,12 @@ async function main() {
|
|
|
596
657
|
logger.info(`Auto-corrected ${correctionResult.moved.length} ticket(s)`);
|
|
597
658
|
}
|
|
598
659
|
|
|
660
|
+
// Архивируем done-тикеты архивных планов
|
|
661
|
+
const archiveResult = archiveTicketsOfArchivedPlans();
|
|
662
|
+
if (archiveResult.archived.length > 0) {
|
|
663
|
+
logger.info(`Archived ${archiveResult.archived.length} ticket(s) from archived plans: ${archiveResult.archived.join(', ')}`);
|
|
664
|
+
}
|
|
665
|
+
|
|
599
666
|
if (planId) {
|
|
600
667
|
const closeResult = checkAndClosePlan(WORKFLOW_DIR, planId);
|
|
601
668
|
if (closeResult.closed) {
|