diff --git a/scripts/reimport-tasks.mjs b/scripts/reimport-tasks.mjs new file mode 100644 index 000000000..935f922a6 --- /dev/null +++ b/scripts/reimport-tasks.mjs @@ -0,0 +1,140 @@ +// One-off: re-import specific FN-* tasks from .fusion/tasks//task.json into +// the live SQLite DB. Uses INSERT OR IGNORE — never overwrites existing rows. +// Mirrors the column list used by packages/core/src/db-migrate.ts:migrateTasks. + +import { DatabaseSync } from "node:sqlite"; +import { readFileSync, existsSync } from "node:fs"; +import { join } from "node:path"; + +const FUSION_DIR = join(process.cwd(), ".fusion"); +const DB_PATH = join(FUSION_DIR, "fusion.db"); +const TASKS_DIR = join(FUSION_DIR, "tasks"); + +const TARGET_IDS = process.argv.slice(2); +if (TARGET_IDS.length === 0) { + console.error("usage: node scripts/reimport-tasks.mjs FN-5414 FN-5415 ..."); + process.exit(2); +} + +const toJson = (v) => JSON.stringify(v ?? []); +const toJsonNullable = (v) => (v == null ? null : JSON.stringify(v)); + +const db = new DatabaseSync(DB_PATH); +db.exec("PRAGMA busy_timeout = 5000"); +db.exec("PRAGMA foreign_keys = ON"); + +const insertStmt = db.prepare(` + INSERT OR IGNORE INTO tasks ( + id, title, description, priority, "column", status, size, reviewLevel, currentStep, + worktree, blockedBy, paused, baseBranch, branch, executionStartBranch, baseCommitSha, modelPresetId, + modelProvider, modelId, validatorModelProvider, validatorModelId, + mergeRetries, recoveryRetryCount, nextRecoveryAt, + error, summary, thinkingLevel, createdAt, updatedAt, + columnMovedAt, dependencies, steps, log, attachments, steeringComments, + comments, workflowStepResults, prInfo, issueInfo, + sourceIssueProvider, sourceIssueRepository, sourceIssueExternalIssueId, sourceIssueNumber, sourceIssueUrl, + mergeDetails, breakIntoSubtasks, noCommitsExpected, enabledWorkflowSteps, modifiedFiles, sliceId + ) VALUES ( + ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, + ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ? + ) +`); + +const existsStmt = db.prepare(`SELECT 1 AS hit FROM tasks WHERE id = ?`); + +let imported = 0; +let skipped = 0; +let missing = 0; + +for (const id of TARGET_IDS) { + const path = join(TASKS_DIR, id, "task.json"); + if (!existsSync(path)) { + console.log(`[skip] ${id}: no task.json on disk`); + missing++; + continue; + } + + if (existsStmt.get(id)) { + console.log(`[skip] ${id}: already exists in DB`); + skipped++; + continue; + } + + let task; + try { + task = JSON.parse(readFileSync(path, "utf-8")); + } catch (err) { + console.warn(`[fail] ${id}: invalid JSON — ${err.message}`); + skipped++; + continue; + } + + const priority = + task.priority === "critical" || task.priority === "high" || task.priority === "low" + ? task.priority + : "normal"; + + // Migration normalizes steeringComments + comments together; for fresh + // imports it's safe to just trust the persisted JSON. + const steeringComments = task.steeringComments ?? []; + const comments = task.comments ?? []; + + insertStmt.run( + task.id, + task.title ?? null, + task.description ?? "", + priority, + task.column, + task.status ?? null, + task.size ?? null, + task.reviewLevel ?? null, + task.currentStep || 0, + task.worktree ?? null, + task.blockedBy ?? null, + task.paused ? 1 : 0, + task.baseBranch ?? null, + task.branch ?? null, + task.executionStartBranch ?? null, + task.baseCommitSha ?? null, + task.modelPresetId ?? null, + task.modelProvider ?? null, + task.modelId ?? null, + task.validatorModelProvider ?? null, + task.validatorModelId ?? null, + task.mergeRetries ?? null, + task.recoveryRetryCount ?? null, + task.nextRecoveryAt ?? null, + task.error ?? null, + task.summary ?? null, + task.thinkingLevel ?? null, + task.createdAt, + task.updatedAt, + task.columnMovedAt ?? null, + toJson(task.dependencies), + toJson(task.steps), + toJson(task.log), + toJson(task.attachments), + toJson(steeringComments), + toJson(comments), + toJson(task.workflowStepResults), + toJsonNullable(task.prInfo), + toJsonNullable(task.issueInfo), + task.sourceIssue?.provider ?? null, + task.sourceIssue?.repository ?? null, + task.sourceIssue?.externalIssueId ?? null, + task.sourceIssue?.issueNumber ?? null, + task.sourceIssue?.url ?? null, + toJsonNullable(task.mergeDetails), + task.breakIntoSubtasks ? 1 : 0, + task.noCommitsExpected ? 1 : 0, + toJson(task.enabledWorkflowSteps), + toJson(task.modifiedFiles), + task.sliceId ?? null, + ); + + console.log(`[ok] ${id}: imported (col=${task.column} status=${task.status ?? "-"})`); + imported++; +} + +db.close(); +console.log(`\ndone: imported=${imported} skipped=${skipped} missing=${missing}`);