feat(KB-336): rename data directory from .kb to .fusion
- Rename data directory from .kb to .fusion across core, CLI, dashboard, and engine - Update all path references in source files, tests, and CLI output - Remove deprecated central-core infrastructure (central-core, central-db, central-integration) - Remove obsolete test files (PlanningModeModal.test.tsx, ScheduleForm.test.tsx) - Add changeset for the directory rename breaking change
This commit is contained in:
8
.changeset/rename-data-directory.md
Normal file
8
.changeset/rename-data-directory.md
Normal file
@@ -0,0 +1,8 @@
|
||||
---
|
||||
"@gsxdsm/fusion": minor
|
||||
"@fusion/core": minor
|
||||
"@fusion/dashboard": minor
|
||||
"@fusion/engine": minor
|
||||
---
|
||||
|
||||
Rename data directory from .kb to .fusion
|
||||
@@ -28,7 +28,7 @@ if (isBunBinary) {
|
||||
writeFileSync(
|
||||
join(tmp, "package.json"),
|
||||
JSON.stringify(
|
||||
{ name: "kb", version: "0.1.0", type: "module", piConfig: { name: "kb", configDir: ".kb" } },
|
||||
{ name: "kb", version: "0.1.0", type: "module", piConfig: { name: "kb", configDir: ".fusion" } },
|
||||
null,
|
||||
2,
|
||||
) + "\n",
|
||||
|
||||
@@ -687,7 +687,7 @@ export async function runDashboard(port: number, opts: { paused?: boolean; dev?:
|
||||
console.log(` ────────────────────────`);
|
||||
console.log(` → http://localhost:${actualPort}`);
|
||||
console.log();
|
||||
console.log(` Tasks stored in .kb/tasks/`);
|
||||
console.log(` Tasks stored in .fusion/tasks/`);
|
||||
console.log(` Merge: AI-assisted (conflict resolution + commit messages)`);
|
||||
if (opts.dev) {
|
||||
console.log(` AI engine: ✗ disabled (dev mode)`);
|
||||
|
||||
@@ -43,7 +43,7 @@ export async function runTaskCreate(descriptionArg?: string, attachFiles?: strin
|
||||
if (task.dependencies.length > 0) {
|
||||
console.log(` Dependencies: ${task.dependencies.join(", ")}`);
|
||||
}
|
||||
console.log(` Path: .kb/tasks/${task.id}/`);
|
||||
console.log(` Path: .fusion/tasks/${task.id}/`);
|
||||
|
||||
if (attachFiles && attachFiles.length > 0) {
|
||||
const { readFile } = await import("node:fs/promises");
|
||||
@@ -241,7 +241,7 @@ export async function runTaskLogs(id: string, options: LogsOptions = {}) {
|
||||
// Follow mode: watch for new entries
|
||||
if (options.follow) {
|
||||
const cwd = process.cwd();
|
||||
const logPath = join(cwd, ".kb", "tasks", id, "agent.log");
|
||||
const logPath = join(cwd, ".fusion", "tasks", id, "agent.log");
|
||||
|
||||
if (!existsSync(logPath)) {
|
||||
console.log(`\n Waiting for log file to be created...`);
|
||||
@@ -434,7 +434,7 @@ export async function runTaskAttach(id: string, filePath: string) {
|
||||
console.log();
|
||||
console.log(` ✓ Attached to ${id}: ${attachment.originalName}`);
|
||||
console.log(` File: ${attachment.filename} (${sizeKB} KB)`);
|
||||
console.log(` Path: .kb/tasks/${id}/attachments/${attachment.filename}`);
|
||||
console.log(` Path: .fusion/tasks/${id}/attachments/${attachment.filename}`);
|
||||
console.log();
|
||||
}
|
||||
|
||||
@@ -477,7 +477,7 @@ export async function runTaskDuplicate(id: string) {
|
||||
|
||||
console.log();
|
||||
console.log(` ✓ Duplicated ${id} → ${newTask.id}`);
|
||||
console.log(` Path: .kb/tasks/${newTask.id}/`);
|
||||
console.log(` Path: .fusion/tasks/${newTask.id}/`);
|
||||
console.log();
|
||||
}
|
||||
|
||||
@@ -509,7 +509,7 @@ export async function runTaskRefine(id: string, feedbackArg?: string) {
|
||||
console.log(` ✓ Created refinement ${newTask.id} for ${id}`);
|
||||
console.log(` Column: triage`);
|
||||
console.log(` Dependency: ${id}`);
|
||||
console.log(` Path: .kb/tasks/${newTask.id}/`);
|
||||
console.log(` Path: .fusion/tasks/${newTask.id}/`);
|
||||
console.log();
|
||||
}
|
||||
|
||||
@@ -1463,7 +1463,7 @@ export async function runTaskPlan(initialPlanArg?: string, yesFlag = false): Pro
|
||||
if (task.dependencies.length > 0) {
|
||||
console.log(` Dependencies: ${task.dependencies.join(", ")}`);
|
||||
}
|
||||
console.log(` Path: .kb/tasks/${task.id}/`);
|
||||
console.log(` Path: .fusion/tasks/${task.id}/`);
|
||||
console.log();
|
||||
} else {
|
||||
console.log("\n Task creation cancelled.\n");
|
||||
|
||||
@@ -99,7 +99,7 @@ export default function kbExtension(pi: ExtensionAPI) {
|
||||
(task.dependencies.length
|
||||
? `Dependencies: ${task.dependencies.join(", ")}\n`
|
||||
: "") +
|
||||
`Path: .kb/tasks/${task.id}/`,
|
||||
`Path: .fusion/tasks/${task.id}/`,
|
||||
},
|
||||
],
|
||||
details: { taskId: task.id, column: task.column, dependencies: task.dependencies },
|
||||
@@ -287,7 +287,7 @@ export default function kbExtension(pi: ExtensionAPI) {
|
||||
type: "text",
|
||||
text:
|
||||
`Attached to ${params.id}: ${attachment.originalName} (${sizeKB} KB)\n` +
|
||||
`Path: .kb/tasks/${params.id}/attachments/${attachment.filename}`,
|
||||
`Path: .fusion/tasks/${params.id}/attachments/${attachment.filename}`,
|
||||
},
|
||||
],
|
||||
details: { taskId: params.id, attachment },
|
||||
|
||||
@@ -32,7 +32,7 @@ describe("TaskStore.listTasks() sort order", () => {
|
||||
|
||||
// Force identical createdAt by rewriting the task.json files
|
||||
const { readFile, writeFile } = await import("node:fs/promises");
|
||||
const tasksDir = join(rootDir, ".kb", "tasks");
|
||||
const tasksDir = join(rootDir, ".fusion", "tasks");
|
||||
const sameTimestamp = "2026-06-01T00:00:00Z";
|
||||
|
||||
for (const t of [t1, t2, t3]) {
|
||||
|
||||
@@ -84,7 +84,7 @@ export class AgentStore extends EventEmitter {
|
||||
|
||||
constructor(options: AgentStoreOptions = {}) {
|
||||
super();
|
||||
this.rootDir = options.rootDir ?? ".kb";
|
||||
this.rootDir = options.rootDir ?? ".fusion";
|
||||
this.agentsDir = join(this.rootDir, "agents");
|
||||
}
|
||||
|
||||
|
||||
@@ -40,14 +40,14 @@ describe("AutomationStore", () => {
|
||||
|
||||
describe("init", () => {
|
||||
it("creates the automations directory", async () => {
|
||||
const dir = join(rootDir, ".kb", "automations");
|
||||
const dir = join(rootDir, ".fusion", "automations");
|
||||
expect(existsSync(dir)).toBe(true);
|
||||
});
|
||||
|
||||
it("is idempotent", async () => {
|
||||
await store.init();
|
||||
await store.init();
|
||||
const dir = join(rootDir, ".kb", "automations");
|
||||
const dir = join(rootDir, ".fusion", "automations");
|
||||
expect(existsSync(dir)).toBe(true);
|
||||
});
|
||||
});
|
||||
@@ -182,7 +182,7 @@ describe("AutomationStore", () => {
|
||||
scheduleType: "weekly",
|
||||
});
|
||||
|
||||
const filePath = join(rootDir, ".kb", "automations", `${schedule.id}.json`);
|
||||
const filePath = join(rootDir, ".fusion", "automations", `${schedule.id}.json`);
|
||||
expect(existsSync(filePath)).toBe(true);
|
||||
});
|
||||
|
||||
@@ -364,7 +364,7 @@ describe("AutomationStore", () => {
|
||||
const deleted = await store.deleteSchedule(schedule.id);
|
||||
expect(deleted.id).toBe(schedule.id);
|
||||
|
||||
const filePath = join(rootDir, ".kb", "automations", `${schedule.id}.json`);
|
||||
const filePath = join(rootDir, ".fusion", "automations", `${schedule.id}.json`);
|
||||
expect(existsSync(filePath)).toBe(false);
|
||||
});
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@ export class AutomationStore extends EventEmitter<AutomationStoreEvents> {
|
||||
|
||||
constructor(private rootDir: string) {
|
||||
super();
|
||||
this.automationsDir = join(rootDir, ".kb", "automations");
|
||||
this.automationsDir = join(rootDir, ".fusion", "automations");
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -38,7 +38,7 @@ export class AutomationStore extends EventEmitter<AutomationStoreEvents> {
|
||||
*/
|
||||
private get db(): Database {
|
||||
if (!this._db) {
|
||||
const kbDir = join(this.rootDir, ".kb");
|
||||
const kbDir = join(this.rootDir, ".fusion");
|
||||
this._db = new Database(kbDir);
|
||||
this._db.init();
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ describe("BackupManager", () => {
|
||||
|
||||
beforeEach(async () => {
|
||||
tempDir = mkdtempSync(join(tmpdir(), "kb-backup-test-"));
|
||||
kbDir = join(tempDir, ".kb");
|
||||
kbDir = join(tempDir, ".fusion");
|
||||
await mkdir(kbDir, { recursive: true });
|
||||
// Create a dummy database file
|
||||
writeFileSync(join(kbDir, "kb.db"), "dummy database content");
|
||||
@@ -103,7 +103,7 @@ describe("BackupManager", () => {
|
||||
await backupManager.createBackup();
|
||||
|
||||
// Create some non-backup files
|
||||
const backupDir = join(tempDir, ".kb/backups");
|
||||
const backupDir = join(tempDir, ".fusion/backups");
|
||||
await writeFile(join(backupDir, "not-a-backup.txt"), "content");
|
||||
await writeFile(join(backupDir, "random.db"), "content");
|
||||
|
||||
@@ -265,7 +265,7 @@ describe("validateBackupRetention", () => {
|
||||
|
||||
describe("validateBackupDir", () => {
|
||||
it("should return true for valid relative paths", () => {
|
||||
expect(validateBackupDir(".kb/backups")).toBe(true);
|
||||
expect(validateBackupDir(".fusion/backups")).toBe(true);
|
||||
expect(validateBackupDir("backups")).toBe(true);
|
||||
expect(validateBackupDir("data/backups/kb")).toBe(true);
|
||||
});
|
||||
@@ -277,7 +277,7 @@ describe("validateBackupDir", () => {
|
||||
|
||||
it("should return false for paths with parent traversal", () => {
|
||||
expect(validateBackupDir("../backups")).toBe(false);
|
||||
expect(validateBackupDir(".kb/../backups")).toBe(false);
|
||||
expect(validateBackupDir(".fusion/../backups")).toBe(false);
|
||||
expect(validateBackupDir("data/../../backups")).toBe(false);
|
||||
});
|
||||
|
||||
@@ -289,13 +289,13 @@ describe("validateBackupDir", () => {
|
||||
|
||||
describe("createBackupManager", () => {
|
||||
it("should create manager with default options when no settings provided", () => {
|
||||
const manager = createBackupManager("/tmp/.kb");
|
||||
const manager = createBackupManager("/tmp/.fusion");
|
||||
expect(manager).toBeInstanceOf(BackupManager);
|
||||
});
|
||||
|
||||
it("should use settings when provided", async () => {
|
||||
const tempDir = mkdtempSync(join(tmpdir(), "kb-backup-test-"));
|
||||
const kbDir = join(tempDir, ".kb");
|
||||
const kbDir = join(tempDir, ".fusion");
|
||||
await mkdir(kbDir, { recursive: true });
|
||||
writeFileSync(join(kbDir, "kb.db"), "test");
|
||||
|
||||
@@ -326,7 +326,7 @@ describe("runBackupCommand", () => {
|
||||
|
||||
beforeEach(async () => {
|
||||
tempDir = mkdtempSync(join(tmpdir(), "kb-backup-test-"));
|
||||
kbDir = join(tempDir, ".kb");
|
||||
kbDir = join(tempDir, ".fusion");
|
||||
await mkdir(kbDir, { recursive: true });
|
||||
writeFileSync(join(kbDir, "kb.db"), "dummy database content");
|
||||
});
|
||||
|
||||
@@ -22,7 +22,7 @@ export interface BackupInfo {
|
||||
* Options for configuring the backup manager.
|
||||
*/
|
||||
export interface BackupOptions {
|
||||
/** Directory for backup files, relative to the project root. Default: ".kb/backups" */
|
||||
/** Directory for backup files, relative to the project root. Default: ".fusion/backups" */
|
||||
backupDir?: string;
|
||||
/** Number of backups to retain. Default: 7 */
|
||||
retention?: number;
|
||||
@@ -44,7 +44,7 @@ export class BackupManager {
|
||||
*/
|
||||
constructor(kbDir: string, options?: BackupOptions) {
|
||||
this.kbDir = kbDir;
|
||||
this.backupDir = options?.backupDir ?? ".kb/backups";
|
||||
this.backupDir = options?.backupDir ?? ".fusion/backups";
|
||||
this.retention = options?.retention ?? 7;
|
||||
}
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ describe("detectLegacyData", () => {
|
||||
|
||||
beforeEach(() => {
|
||||
tmpDir = makeTmpDir();
|
||||
kbDir = join(tmpDir, ".kb");
|
||||
kbDir = join(tmpDir, ".fusion");
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
@@ -77,7 +77,7 @@ describe("getMigrationStatus", () => {
|
||||
|
||||
beforeEach(() => {
|
||||
tmpDir = makeTmpDir();
|
||||
kbDir = join(tmpDir, ".kb");
|
||||
kbDir = join(tmpDir, ".fusion");
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
@@ -121,7 +121,7 @@ describe("migrateFromLegacy", () => {
|
||||
|
||||
beforeEach(async () => {
|
||||
tmpDir = makeTmpDir();
|
||||
kbDir = join(tmpDir, ".kb");
|
||||
kbDir = join(tmpDir, ".fusion");
|
||||
await mkdir(kbDir, { recursive: true });
|
||||
db = new Database(kbDir);
|
||||
db.init();
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/**
|
||||
* Migration from legacy file-based storage to SQLite.
|
||||
*
|
||||
* Detects legacy data (.kb/tasks/, .kb/config.json, etc.) and migrates
|
||||
* Detects legacy data (.fusion/tasks/, .fusion/config.json, etc.) and migrates
|
||||
* it to the SQLite database. After successful migration, original files
|
||||
* are renamed with .bak suffix as backups.
|
||||
*
|
||||
@@ -443,7 +443,7 @@ async function migrateAgents(kbDir: string, db: Database): Promise<void> {
|
||||
|
||||
/**
|
||||
* Create backups of legacy files by renaming them with .bak suffix.
|
||||
* Note: .kb/tasks/ is NOT renamed because blob files (PROMPT.md, agent.log,
|
||||
* Note: .fusion/tasks/ is NOT renamed because blob files (PROMPT.md, agent.log,
|
||||
* attachments) remain on the filesystem. Only task.json files inside each
|
||||
* task directory are the "migrated" data now in SQLite. We rename individual
|
||||
* task.json files to task.json.bak instead.
|
||||
|
||||
@@ -17,7 +17,7 @@ describe("Database", () => {
|
||||
|
||||
beforeEach(() => {
|
||||
tmpDir = makeTmpDir();
|
||||
kbDir = join(tmpDir, ".kb");
|
||||
kbDir = join(tmpDir, ".fusion");
|
||||
db = new Database(kbDir);
|
||||
db.init(); // Explicit init required — createDatabase() does not auto-init
|
||||
});
|
||||
@@ -36,7 +36,7 @@ describe("Database", () => {
|
||||
expect(existsSync(join(kbDir, "kb.db"))).toBe(true);
|
||||
});
|
||||
|
||||
it("creates the .kb directory if missing", () => {
|
||||
it("creates the .fusion directory if missing", () => {
|
||||
expect(existsSync(kbDir)).toBe(true);
|
||||
});
|
||||
|
||||
@@ -598,7 +598,7 @@ describe("schema migrations", () => {
|
||||
|
||||
it("migrates a v1 database by adding missing columns", () => {
|
||||
tmpDir = makeTmpDir();
|
||||
const kbDir = join(tmpDir, ".kb");
|
||||
const kbDir = join(tmpDir, ".fusion");
|
||||
|
||||
// Create a v1 database manually (without comments and mergeDetails columns)
|
||||
const db = new Database(kbDir);
|
||||
@@ -705,7 +705,7 @@ describe("schema migrations", () => {
|
||||
|
||||
it("skips migration if already at target version", () => {
|
||||
tmpDir = makeTmpDir();
|
||||
const kbDir = join(tmpDir, ".kb");
|
||||
const kbDir = join(tmpDir, ".fusion");
|
||||
const db = new Database(kbDir);
|
||||
db.init();
|
||||
|
||||
@@ -720,7 +720,7 @@ describe("schema migrations", () => {
|
||||
|
||||
it("migrates a v2 database by adding missionId and sliceId columns", () => {
|
||||
tmpDir = makeTmpDir();
|
||||
const kbDir = join(tmpDir, ".kb");
|
||||
const kbDir = join(tmpDir, ".fusion");
|
||||
|
||||
// Create a v2 database manually (without missionId and sliceId columns)
|
||||
const db = new Database(kbDir);
|
||||
@@ -847,7 +847,7 @@ describe("createDatabase factory", () => {
|
||||
|
||||
it("creates a database instance without auto-init", () => {
|
||||
tmpDir = makeTmpDir();
|
||||
const kbDir = join(tmpDir, ".kb");
|
||||
const kbDir = join(tmpDir, ".fusion");
|
||||
const db = createDatabase(kbDir);
|
||||
|
||||
// DB file exists (created on open) but schema not initialized
|
||||
@@ -860,7 +860,7 @@ describe("createDatabase factory", () => {
|
||||
|
||||
it("works after explicit init()", () => {
|
||||
tmpDir = makeTmpDir();
|
||||
const kbDir = join(tmpDir, ".kb");
|
||||
const kbDir = join(tmpDir, ".fusion");
|
||||
const db = createDatabase(kbDir);
|
||||
db.init();
|
||||
|
||||
@@ -872,7 +872,7 @@ describe("createDatabase factory", () => {
|
||||
|
||||
it("getPath returns the database file path", () => {
|
||||
tmpDir = makeTmpDir();
|
||||
const kbDir = join(tmpDir, ".kb");
|
||||
const kbDir = join(tmpDir, ".fusion");
|
||||
const db = createDatabase(kbDir);
|
||||
|
||||
expect(db.getPath()).toBe(join(kbDir, "kb.db"));
|
||||
@@ -882,7 +882,7 @@ describe("createDatabase factory", () => {
|
||||
|
||||
it("is idempotent when init() called multiple times", () => {
|
||||
tmpDir = makeTmpDir();
|
||||
const kbDir = join(tmpDir, ".kb");
|
||||
const kbDir = join(tmpDir, ".fusion");
|
||||
|
||||
// First call
|
||||
const db1 = createDatabase(kbDir);
|
||||
|
||||
@@ -253,7 +253,7 @@ export class Database {
|
||||
constructor(private kbDir: string) {
|
||||
this.dbPath = join(kbDir, "kb.db");
|
||||
|
||||
// Ensure .kb directory exists
|
||||
// Ensure .fusion directory exists
|
||||
if (!existsSync(kbDir)) {
|
||||
mkdirSync(kbDir, { recursive: true });
|
||||
}
|
||||
@@ -466,7 +466,7 @@ export class Database {
|
||||
/**
|
||||
* Create a new Database instance (does NOT initialize schema).
|
||||
* Callers must call `db.init()` separately.
|
||||
* @param kbDir - Path to the `.kb` directory (e.g., `/path/to/project/.kb`)
|
||||
* @param kbDir - Path to the `.fusion` directory (e.g., `/path/to/project/.fusion`)
|
||||
* @returns Database instance (not yet initialized)
|
||||
*/
|
||||
export function createDatabase(kbDir: string): Database {
|
||||
|
||||
@@ -30,7 +30,7 @@ describe("MissionStore", () => {
|
||||
|
||||
beforeEach(() => {
|
||||
tmpDir = makeTmpDir();
|
||||
kbDir = join(tmpDir, ".kb");
|
||||
kbDir = join(tmpDir, ".fusion");
|
||||
db = new Database(kbDir);
|
||||
db.init();
|
||||
store = new MissionStore(kbDir, db);
|
||||
|
||||
@@ -20,7 +20,7 @@ import {
|
||||
// Helper to create a temporary test environment
|
||||
function createTestEnv() {
|
||||
const tempDir = mkdtempSync(join(tmpdir(), "kb-settings-test-"));
|
||||
const kbDir = join(tempDir, ".kb");
|
||||
const kbDir = join(tempDir, ".fusion");
|
||||
const tasksDir = join(kbDir, "tasks");
|
||||
const globalSettingsDir = join(tempDir, "global-settings");
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@ describe("TaskStore", () => {
|
||||
async function createTaskWithSteps(): Promise<Task> {
|
||||
const task = await store.createTask({ description: "Task with steps" });
|
||||
// Write a PROMPT.md with steps so updateStep works
|
||||
const dir = join(rootDir, ".kb", "tasks", task.id);
|
||||
const dir = join(rootDir, ".fusion", "tasks", task.id);
|
||||
await writeFile(
|
||||
join(dir, "PROMPT.md"),
|
||||
`# ${task.id}: Task with steps
|
||||
@@ -59,7 +59,7 @@ describe("TaskStore", () => {
|
||||
}
|
||||
|
||||
async function deleteTaskDir(taskId: string): Promise<string> {
|
||||
const dir = join(rootDir, ".kb", "tasks", taskId);
|
||||
const dir = join(rootDir, ".fusion", "tasks", taskId);
|
||||
await rm(dir, { recursive: true, force: true });
|
||||
return dir;
|
||||
}
|
||||
@@ -174,7 +174,7 @@ describe("TaskStore", () => {
|
||||
await Promise.all(promises);
|
||||
|
||||
// Read back and verify valid JSON
|
||||
const taskJsonPath = join(rootDir, ".kb", "tasks", id, "task.json");
|
||||
const taskJsonPath = join(rootDir, ".fusion", "tasks", id, "task.json");
|
||||
const raw = await readFile(taskJsonPath, "utf-8");
|
||||
const result = JSON.parse(raw) as Task;
|
||||
|
||||
@@ -189,7 +189,7 @@ describe("TaskStore", () => {
|
||||
describe("defensive JSON parsing", () => {
|
||||
it("reads from SQLite even if task.json on disk is corrupted", async () => {
|
||||
const task = await createTestTask();
|
||||
const taskJsonPath = join(rootDir, ".kb", "tasks", task.id, "task.json");
|
||||
const taskJsonPath = join(rootDir, ".fusion", "tasks", task.id, "task.json");
|
||||
|
||||
// Corrupt the file: append duplicate trailing content
|
||||
const validJson = await readFile(taskJsonPath, "utf-8");
|
||||
@@ -203,7 +203,7 @@ describe("TaskStore", () => {
|
||||
|
||||
it("reads from SQLite even if task.json contains invalid content", async () => {
|
||||
const task = await createTestTask();
|
||||
const taskJsonPath = join(rootDir, ".kb", "tasks", task.id, "task.json");
|
||||
const taskJsonPath = join(rootDir, ".fusion", "tasks", task.id, "task.json");
|
||||
|
||||
// Write completely invalid content
|
||||
await writeFile(taskJsonPath, "not json at all {{{");
|
||||
@@ -219,7 +219,7 @@ describe("TaskStore", () => {
|
||||
describe("atomic writes", () => {
|
||||
it("produces valid JSON after write with no .tmp files left behind", async () => {
|
||||
const task = await createTestTask();
|
||||
const dir = join(rootDir, ".kb", "tasks", task.id);
|
||||
const dir = join(rootDir, ".fusion", "tasks", task.id);
|
||||
|
||||
// Perform a write
|
||||
await store.logEntry(task.id, "atomic test");
|
||||
@@ -253,13 +253,13 @@ describe("TaskStore", () => {
|
||||
expect(sortedIds).toEqual(["FN-001", "FN-002", "FN-003", "FN-004", "FN-005"]);
|
||||
|
||||
// config.json should be valid JSON with nextId = 6
|
||||
const configPath = join(rootDir, ".kb", "config.json");
|
||||
const configPath = join(rootDir, ".fusion", "config.json");
|
||||
const raw = await readFile(configPath, "utf-8");
|
||||
const config = JSON.parse(raw);
|
||||
expect(config.nextId).toBe(6);
|
||||
|
||||
// No .tmp files left behind
|
||||
const haiDir = join(rootDir, ".kb");
|
||||
const haiDir = join(rootDir, ".fusion");
|
||||
const files = await readdir(haiDir);
|
||||
expect(files.filter((f) => f.endsWith(".tmp"))).toHaveLength(0);
|
||||
});
|
||||
@@ -288,7 +288,7 @@ describe("TaskStore", () => {
|
||||
expect(updated.attachments![0].filename).toBe(attachment.filename);
|
||||
|
||||
// Verify file on disk
|
||||
const filePath = join(rootDir, ".kb", "tasks", task.id, "attachments", attachment.filename);
|
||||
const filePath = join(rootDir, ".fusion", "tasks", task.id, "attachments", attachment.filename);
|
||||
const content = await readFile(filePath);
|
||||
expect(content).toEqual(TINY_PNG);
|
||||
});
|
||||
@@ -344,7 +344,7 @@ describe("TaskStore", () => {
|
||||
expect(updated.attachments).toBeUndefined();
|
||||
|
||||
// Verify file removed from disk
|
||||
const filePath = join(rootDir, ".kb", "tasks", task.id, "attachments", attachment.filename);
|
||||
const filePath = join(rootDir, ".fusion", "tasks", task.id, "attachments", attachment.filename);
|
||||
expect(existsSync(filePath)).toBe(false);
|
||||
});
|
||||
|
||||
@@ -452,7 +452,7 @@ describe("TaskStore", () => {
|
||||
expect(settings.themeMode).toBe("dark");
|
||||
|
||||
// Verify the project config doesn't contain themeMode
|
||||
const configRaw = await readFile(join(rootDir, ".kb", "config.json"), "utf-8");
|
||||
const configRaw = await readFile(join(rootDir, ".fusion", "config.json"), "utf-8");
|
||||
const config = JSON.parse(configRaw);
|
||||
expect(config.settings.themeMode).toBeUndefined();
|
||||
});
|
||||
@@ -840,7 +840,7 @@ describe("TaskStore", () => {
|
||||
|
||||
it("updateStep recreates missing task directory and persists regenerated task.json", async () => {
|
||||
const task = await createTaskWithSteps();
|
||||
const promptDir = join(rootDir, ".kb", "tasks", task.id);
|
||||
const promptDir = join(rootDir, ".fusion", "tasks", task.id);
|
||||
const prompt = await readFile(join(promptDir, "PROMPT.md"), "utf-8");
|
||||
const dir = await deleteTaskDir(task.id);
|
||||
await mkdir(dir, { recursive: true });
|
||||
@@ -919,7 +919,7 @@ describe("TaskStore", () => {
|
||||
const task = await createTestTask();
|
||||
|
||||
const duplicate = await store.duplicateTask(task.id);
|
||||
const duplicateDir = join(rootDir, ".kb", "tasks", duplicate.id);
|
||||
const duplicateDir = join(rootDir, ".fusion", "tasks", duplicate.id);
|
||||
|
||||
expect(existsSync(duplicateDir)).toBe(true);
|
||||
expect(existsSync(join(duplicateDir, "PROMPT.md"))).toBe(true);
|
||||
@@ -1703,7 +1703,7 @@ describe("TaskStore", () => {
|
||||
await Promise.all(promises);
|
||||
|
||||
// Read back and verify valid JSON
|
||||
const taskJsonPath = join(rootDir, ".kb", "tasks", task.id, "task.json");
|
||||
const taskJsonPath = join(rootDir, ".fusion", "tasks", task.id, "task.json");
|
||||
const raw = await readFile(taskJsonPath, "utf-8");
|
||||
const result = JSON.parse(raw) as Task;
|
||||
|
||||
@@ -1731,7 +1731,7 @@ describe("TaskStore", () => {
|
||||
describe("parseDependenciesFromPrompt", () => {
|
||||
it("returns single dependency from PROMPT.md", async () => {
|
||||
const task = await store.createTask({ description: "Task with dep" });
|
||||
const dir = join(rootDir, ".kb", "tasks", task.id);
|
||||
const dir = join(rootDir, ".fusion", "tasks", task.id);
|
||||
await writeFile(
|
||||
join(dir, "PROMPT.md"),
|
||||
`# ${task.id}: Task with dep
|
||||
@@ -1753,7 +1753,7 @@ describe("TaskStore", () => {
|
||||
|
||||
it("returns multiple dependencies in order", async () => {
|
||||
const task = await store.createTask({ description: "Task with deps" });
|
||||
const dir = join(rootDir, ".kb", "tasks", task.id);
|
||||
const dir = join(rootDir, ".fusion", "tasks", task.id);
|
||||
await writeFile(
|
||||
join(dir, "PROMPT.md"),
|
||||
`# ${task.id}: Task with deps
|
||||
@@ -1777,7 +1777,7 @@ describe("TaskStore", () => {
|
||||
|
||||
it("returns empty array when dependencies section says None", async () => {
|
||||
const task = await store.createTask({ description: "No deps" });
|
||||
const dir = join(rootDir, ".kb", "tasks", task.id);
|
||||
const dir = join(rootDir, ".fusion", "tasks", task.id);
|
||||
await writeFile(
|
||||
join(dir, "PROMPT.md"),
|
||||
`# ${task.id}: No deps
|
||||
@@ -1799,7 +1799,7 @@ describe("TaskStore", () => {
|
||||
|
||||
it("returns empty array when no Dependencies section exists", async () => {
|
||||
const task = await store.createTask({ description: "No section" });
|
||||
const dir = join(rootDir, ".kb", "tasks", task.id);
|
||||
const dir = join(rootDir, ".fusion", "tasks", task.id);
|
||||
await writeFile(
|
||||
join(dir, "PROMPT.md"),
|
||||
`# ${task.id}: No section
|
||||
@@ -1817,7 +1817,7 @@ describe("TaskStore", () => {
|
||||
|
||||
it("returns empty array when task has no PROMPT.md file", async () => {
|
||||
const task = await store.createTask({ description: "No prompt" });
|
||||
const dir = join(rootDir, ".kb", "tasks", task.id);
|
||||
const dir = join(rootDir, ".fusion", "tasks", task.id);
|
||||
// Delete the PROMPT.md that createTask generates
|
||||
await unlink(join(dir, "PROMPT.md"));
|
||||
|
||||
@@ -1837,7 +1837,7 @@ describe("TaskStore", () => {
|
||||
describe("parseFileScopeFromPrompt", () => {
|
||||
it("returns paths when File Scope is followed by another heading", async () => {
|
||||
const task = await store.createTask({ description: "Mid-file scope" });
|
||||
const dir = join(rootDir, ".kb", "tasks", task.id);
|
||||
const dir = join(rootDir, ".fusion", "tasks", task.id);
|
||||
await writeFile(
|
||||
join(dir, "PROMPT.md"),
|
||||
`# ${task.id}: Mid-file scope
|
||||
@@ -1865,7 +1865,7 @@ describe("TaskStore", () => {
|
||||
const task = await store.createTask({
|
||||
description: "End-of-file scope",
|
||||
});
|
||||
const dir = join(rootDir, ".kb", "tasks", task.id);
|
||||
const dir = join(rootDir, ".fusion", "tasks", task.id);
|
||||
await writeFile(
|
||||
join(dir, "PROMPT.md"),
|
||||
`# ${task.id}: End-of-file scope
|
||||
@@ -1893,7 +1893,7 @@ describe("TaskStore", () => {
|
||||
|
||||
it("returns empty array when no File Scope section exists", async () => {
|
||||
const task = await store.createTask({ description: "No scope" });
|
||||
const dir = join(rootDir, ".kb", "tasks", task.id);
|
||||
const dir = join(rootDir, ".fusion", "tasks", task.id);
|
||||
await writeFile(
|
||||
join(dir, "PROMPT.md"),
|
||||
`# ${task.id}: No scope
|
||||
@@ -1911,7 +1911,7 @@ describe("TaskStore", () => {
|
||||
|
||||
it("returns empty array when PROMPT.md does not exist", async () => {
|
||||
const task = await store.createTask({ description: "No prompt" });
|
||||
const dir = join(rootDir, ".kb", "tasks", task.id);
|
||||
const dir = join(rootDir, ".fusion", "tasks", task.id);
|
||||
await unlink(join(dir, "PROMPT.md"));
|
||||
|
||||
const paths = await store.parseFileScopeFromPrompt(task.id);
|
||||
@@ -1928,7 +1928,7 @@ describe("TaskStore", () => {
|
||||
|
||||
it("handles glob patterns in backtick-quoted paths", async () => {
|
||||
const task = await store.createTask({ description: "Glob scope" });
|
||||
const dir = join(rootDir, ".kb", "tasks", task.id);
|
||||
const dir = join(rootDir, ".fusion", "tasks", task.id);
|
||||
await writeFile(
|
||||
join(dir, "PROMPT.md"),
|
||||
`# ${task.id}: Glob scope
|
||||
@@ -2512,7 +2512,7 @@ describe("TaskStore", () => {
|
||||
const refined = await store.refineTask(task.id, "Need improvements");
|
||||
|
||||
// Verify file exists in new task directory
|
||||
const attachDir = join(rootDir, ".kb", "tasks", refined.id, "attachments");
|
||||
const attachDir = join(rootDir, ".fusion", "tasks", refined.id, "attachments");
|
||||
const files = await readdir(attachDir);
|
||||
expect(files.length).toBe(1);
|
||||
|
||||
@@ -3006,7 +3006,7 @@ describe("TaskStore", () => {
|
||||
await store.moveTask(task.id, "done");
|
||||
await store.archiveTask(task.id);
|
||||
|
||||
const dir = join(rootDir, ".kb", "tasks", task.id);
|
||||
const dir = join(rootDir, ".fusion", "tasks", task.id);
|
||||
expect(existsSync(dir)).toBe(true);
|
||||
|
||||
await store.cleanupArchivedTasks();
|
||||
@@ -3144,7 +3144,7 @@ describe("TaskStore", () => {
|
||||
await store.archiveTask(task.id);
|
||||
await store.cleanupArchivedTasks();
|
||||
|
||||
const dir = join(rootDir, ".kb", "tasks", task.id);
|
||||
const dir = join(rootDir, ".fusion", "tasks", task.id);
|
||||
expect(existsSync(dir)).toBe(false);
|
||||
|
||||
// Unarchive should restore from archive
|
||||
@@ -3201,7 +3201,7 @@ describe("TaskStore", () => {
|
||||
await store.archiveTask(task.id);
|
||||
|
||||
// Delete directory without archiving
|
||||
const dir = join(rootDir, ".kb", "tasks", task.id);
|
||||
const dir = join(rootDir, ".fusion", "tasks", task.id);
|
||||
const { rm } = await import("node:fs/promises");
|
||||
await rm(dir, { recursive: true, force: true });
|
||||
|
||||
@@ -3252,7 +3252,7 @@ describe("TaskStore", () => {
|
||||
await store.archiveTask(task.id);
|
||||
await store.cleanupArchivedTasks();
|
||||
|
||||
const dir = join(rootDir, ".kb", "tasks", task.id);
|
||||
const dir = join(rootDir, ".fusion", "tasks", task.id);
|
||||
expect(existsSync(dir)).toBe(false);
|
||||
|
||||
await store.unarchiveTask(task.id);
|
||||
@@ -3275,7 +3275,7 @@ describe("TaskStore", () => {
|
||||
expect(archived.column).toBe("archived");
|
||||
|
||||
// Directory should be gone immediately
|
||||
const dir = join(rootDir, ".kb", "tasks", task.id);
|
||||
const dir = join(rootDir, ".fusion", "tasks", task.id);
|
||||
expect(existsSync(dir)).toBe(false);
|
||||
|
||||
// Should be in archive.jsonl
|
||||
@@ -3294,7 +3294,7 @@ describe("TaskStore", () => {
|
||||
const archived = await store.archiveTaskAndCleanup(task.id);
|
||||
expect(archived.column).toBe("archived");
|
||||
|
||||
const dir = join(rootDir, ".kb", "tasks", task.id);
|
||||
const dir = join(rootDir, ".fusion", "tasks", task.id);
|
||||
expect(existsSync(dir)).toBe(false);
|
||||
});
|
||||
|
||||
@@ -3309,7 +3309,7 @@ describe("TaskStore", () => {
|
||||
expect(archived.column).toBe("archived");
|
||||
|
||||
// Directory should still exist
|
||||
const dir = join(rootDir, ".kb", "tasks", task.id);
|
||||
const dir = join(rootDir, ".fusion", "tasks", task.id);
|
||||
expect(existsSync(dir)).toBe(true);
|
||||
});
|
||||
|
||||
@@ -3324,7 +3324,7 @@ describe("TaskStore", () => {
|
||||
expect(archived.column).toBe("archived");
|
||||
|
||||
// Directory should still exist (default is false)
|
||||
const dir = join(rootDir, ".kb", "tasks", task.id);
|
||||
const dir = join(rootDir, ".fusion", "tasks", task.id);
|
||||
expect(existsSync(dir)).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -61,7 +61,7 @@ export class TaskStore extends EventEmitter<TaskStoreEvents> {
|
||||
constructor(private rootDir: string, globalSettingsDir?: string) {
|
||||
super();
|
||||
this.setMaxListeners(100);
|
||||
this.kbDir = join(rootDir, ".kb");
|
||||
this.kbDir = join(rootDir, ".fusion");
|
||||
this.tasksDir = join(this.kbDir, "tasks");
|
||||
this.configPath = join(this.kbDir, "config.json");
|
||||
this.archiveLogPath = join(this.kbDir, "archive.jsonl");
|
||||
|
||||
@@ -14,7 +14,7 @@ import { applyPresetToSelection, generatePresetId, validatePresetId } from "../u
|
||||
* Each section groups related settings fields under a sidebar nav item.
|
||||
* Sections have a `scope` to indicate where their settings are stored:
|
||||
* - "global": User-level settings stored in ~/.pi/kb/settings.json (shared across projects)
|
||||
* - "project": Project-specific settings stored in .kb/config.json
|
||||
* - "project": Project-specific settings stored in .fusion/config.json
|
||||
* - undefined: Section operates independently of settings storage (e.g. authentication)
|
||||
*
|
||||
* To add a new section:
|
||||
@@ -1256,8 +1256,8 @@ export function SettingsModal({
|
||||
<input
|
||||
id="autoBackupDir"
|
||||
type="text"
|
||||
placeholder=".kb/backups"
|
||||
value={form.autoBackupDir || ".kb/backups"}
|
||||
placeholder=".fusion/backups"
|
||||
value={form.autoBackupDir || ".fusion/backups"}
|
||||
onChange={(e) =>
|
||||
setForm((f) => ({ ...f, autoBackupDir: e.target.value }))
|
||||
}
|
||||
|
||||
@@ -455,7 +455,7 @@ describe("task file operations", () => {
|
||||
await readFile(mockStore, "FN-123", "PROMPT.md");
|
||||
|
||||
expect(mockReadFile).toHaveBeenCalledWith(
|
||||
"/project/.kb/tasks/KB-123/PROMPT.md",
|
||||
"/project/.fusion/tasks/KB-123/PROMPT.md",
|
||||
"utf-8",
|
||||
);
|
||||
});
|
||||
@@ -476,7 +476,7 @@ describe("task file operations", () => {
|
||||
await readFile(mockStore, "FN-123", "PROMPT.md");
|
||||
|
||||
expect(mockReadFile).toHaveBeenCalledWith(
|
||||
"/project/.kb/tasks/KB-123/PROMPT.md",
|
||||
"/project/.fusion/tasks/KB-123/PROMPT.md",
|
||||
"utf-8",
|
||||
);
|
||||
});
|
||||
@@ -597,7 +597,7 @@ describe("workspace operations", () => {
|
||||
|
||||
expect(result.content).toBe("Task description");
|
||||
expect(mockReadFile).toHaveBeenCalledWith(
|
||||
"/project/.kb/tasks/KB-123/PROMPT.md",
|
||||
"/project/.fusion/tasks/KB-123/PROMPT.md",
|
||||
"utf-8",
|
||||
);
|
||||
});
|
||||
@@ -641,7 +641,7 @@ describe("workspace operations", () => {
|
||||
|
||||
expect(result.success).toBe(true);
|
||||
expect(mockWriteFile).toHaveBeenCalledWith(
|
||||
"/project/.kb/tasks/KB-123/output.txt",
|
||||
"/project/.fusion/tasks/KB-123/output.txt",
|
||||
"Task output",
|
||||
"utf-8",
|
||||
);
|
||||
|
||||
@@ -96,7 +96,7 @@ async function getTaskBasePath(store: TaskStore, taskId: string): Promise<string
|
||||
}
|
||||
// Fall back to task directory
|
||||
const rootDir = store.getRootDir();
|
||||
return resolve(join(rootDir, ".kb", "tasks", taskId));
|
||||
return resolve(join(rootDir, ".fusion", "tasks", taskId));
|
||||
} catch (err: any) {
|
||||
if (err.code === "ENOENT" || err.message?.includes("not found")) {
|
||||
throw new FileServiceError(`Task ${taskId} not found`, "ENOTASK");
|
||||
|
||||
@@ -1949,7 +1949,7 @@ export function createApiRoutes(store: TaskStore, options?: ServerOptions): Rout
|
||||
// Remove PROMPT.md to force regeneration
|
||||
const { rm } = await import("node:fs/promises");
|
||||
const { join } = await import("node:path");
|
||||
const promptPath = join(store.getRootDir(), ".kb", "tasks", task.id, "PROMPT.md");
|
||||
const promptPath = join(store.getRootDir(), ".fusion", "tasks", task.id, "PROMPT.md");
|
||||
await rm(promptPath, { force: true });
|
||||
|
||||
const updated = await store.getTask(task.id);
|
||||
|
||||
@@ -1391,7 +1391,7 @@ describe("buildExecutionPrompt", () => {
|
||||
|
||||
expect(result).toContain("## Attachments");
|
||||
expect(result).toContain("**screenshot.png** (screenshot)");
|
||||
expect(result).toContain("/home/user/project/.kb/tasks/KB-001/attachments/abc123-screenshot.png");
|
||||
expect(result).toContain("/home/user/project/.fusion/tasks/KB-001/attachments/abc123-screenshot.png");
|
||||
});
|
||||
|
||||
it("includes attachment section with absolute paths for text attachments", () => {
|
||||
@@ -1405,7 +1405,7 @@ describe("buildExecutionPrompt", () => {
|
||||
expect(result).toContain("## Attachments");
|
||||
expect(result).toContain("**error.log** (text/plain)");
|
||||
expect(result).toContain("read for context");
|
||||
expect(result).toContain("/home/user/project/.kb/tasks/KB-001/attachments/def456-error.log");
|
||||
expect(result).toContain("/home/user/project/.fusion/tasks/KB-001/attachments/def456-error.log");
|
||||
});
|
||||
|
||||
it("includes both image and text attachments", () => {
|
||||
|
||||
@@ -1677,7 +1677,7 @@ git log --oneline
|
||||
const IMAGE_MIMES = new Set(["image/png", "image/jpeg", "image/gif", "image/webp"]);
|
||||
const lines = ["## Attachments", ""];
|
||||
for (const att of task.attachments) {
|
||||
const absPath = `${rootDir}/.kb/tasks/${task.id}/attachments/${att.filename}`;
|
||||
const absPath = `${rootDir}/.fusion/tasks/${task.id}/attachments/${att.filename}`;
|
||||
if (IMAGE_MIMES.has(att.mimeType)) {
|
||||
lines.push(`- **${att.originalName}** (screenshot): \`${absPath}\``);
|
||||
} else {
|
||||
|
||||
@@ -173,7 +173,7 @@ export class Scheduler {
|
||||
* @returns Object with `valid: true` if checks pass, or `valid: false` with a `reason` string if they fail
|
||||
*/
|
||||
private async validateTaskFilesystem(id: string): Promise<{ valid: boolean; reason?: string }> {
|
||||
const taskDir = join(this.store.getRootDir(), ".kb", "tasks", id);
|
||||
const taskDir = join(this.store.getRootDir(), ".fusion", "tasks", id);
|
||||
|
||||
// Check if task directory exists
|
||||
if (!existsSync(taskDir)) {
|
||||
|
||||
@@ -63,14 +63,14 @@ describe("buildSpecificationPrompt", () => {
|
||||
it("generates basic specification prompt", () => {
|
||||
const prompt = buildSpecificationPrompt(
|
||||
baseTask,
|
||||
".kb/tasks/KB-001/PROMPT.md",
|
||||
".fusion/tasks/KB-001/PROMPT.md",
|
||||
);
|
||||
|
||||
expect(prompt).toContain("Specify this task");
|
||||
expect(prompt).toContain("FN-001");
|
||||
expect(prompt).toContain("Test Task");
|
||||
expect(prompt).toContain("Test task description");
|
||||
expect(prompt).toContain(".kb/tasks/KB-001/PROMPT.md");
|
||||
expect(prompt).toContain(".fusion/tasks/KB-001/PROMPT.md");
|
||||
});
|
||||
|
||||
it("includes project commands when provided", () => {
|
||||
@@ -86,7 +86,7 @@ describe("buildSpecificationPrompt", () => {
|
||||
|
||||
const prompt = buildSpecificationPrompt(
|
||||
baseTask,
|
||||
".kb/tasks/KB-001/PROMPT.md",
|
||||
".fusion/tasks/KB-001/PROMPT.md",
|
||||
settings,
|
||||
);
|
||||
|
||||
@@ -101,7 +101,7 @@ describe("buildSpecificationPrompt", () => {
|
||||
|
||||
const prompt = buildSpecificationPrompt(
|
||||
baseTask,
|
||||
".kb/tasks/KB-001/PROMPT.md",
|
||||
".fusion/tasks/KB-001/PROMPT.md",
|
||||
undefined,
|
||||
[],
|
||||
existingPrompt,
|
||||
@@ -133,7 +133,7 @@ describe("buildSpecificationPrompt", () => {
|
||||
|
||||
const prompt = buildSpecificationPrompt(
|
||||
baseTask,
|
||||
".kb/tasks/KB-001/PROMPT.md",
|
||||
".fusion/tasks/KB-001/PROMPT.md",
|
||||
undefined,
|
||||
attachments,
|
||||
);
|
||||
@@ -152,7 +152,7 @@ describe("buildSpecificationPrompt", () => {
|
||||
|
||||
const prompt = buildSpecificationPrompt(
|
||||
taskWithDeps,
|
||||
".kb/tasks/KB-001/PROMPT.md",
|
||||
".fusion/tasks/KB-001/PROMPT.md",
|
||||
);
|
||||
|
||||
expect(prompt).toContain("Dependencies");
|
||||
@@ -167,7 +167,7 @@ describe("buildSpecificationPrompt", () => {
|
||||
|
||||
const prompt = buildSpecificationPrompt(
|
||||
taskWithoutTitle,
|
||||
".kb/tasks/KB-001/PROMPT.md",
|
||||
".fusion/tasks/KB-001/PROMPT.md",
|
||||
);
|
||||
|
||||
expect(prompt).toContain("(none)");
|
||||
@@ -176,7 +176,7 @@ describe("buildSpecificationPrompt", () => {
|
||||
it("includes proactive subtask guidance when breakdown was not explicitly requested", () => {
|
||||
const prompt = buildSpecificationPrompt(
|
||||
baseTask,
|
||||
".kb/tasks/KB-001/PROMPT.md",
|
||||
".fusion/tasks/KB-001/PROMPT.md",
|
||||
);
|
||||
|
||||
expect(prompt).toContain("## Subtask Consideration");
|
||||
@@ -191,7 +191,7 @@ describe("buildSpecificationPrompt", () => {
|
||||
...baseTask,
|
||||
breakIntoSubtasks: true,
|
||||
},
|
||||
".kb/tasks/KB-001/PROMPT.md",
|
||||
".fusion/tasks/KB-001/PROMPT.md",
|
||||
);
|
||||
|
||||
expect(prompt).toContain("## Subtask Breakdown Requested");
|
||||
@@ -221,7 +221,7 @@ describe("readAttachmentContents", () => {
|
||||
beforeEach(async () => {
|
||||
// Clean up and create test directory
|
||||
await rm(testDir, { recursive: true, force: true });
|
||||
await mkdir(join(testDir, ".kb", "tasks", taskId, "attachments"), {
|
||||
await mkdir(join(testDir, ".fusion", "tasks", taskId, "attachments"), {
|
||||
recursive: true,
|
||||
});
|
||||
});
|
||||
@@ -253,7 +253,7 @@ describe("readAttachmentContents", () => {
|
||||
|
||||
const content = "Test notes content";
|
||||
await writeFile(
|
||||
join(testDir, ".kb", "tasks", taskId, "attachments", "1234567890-notes.txt"),
|
||||
join(testDir, ".fusion", "tasks", taskId, "attachments", "1234567890-notes.txt"),
|
||||
content,
|
||||
);
|
||||
|
||||
@@ -278,7 +278,7 @@ describe("readAttachmentContents", () => {
|
||||
|
||||
const largeContent = "a".repeat(60 * 1024); // 60KB
|
||||
await writeFile(
|
||||
join(testDir, ".kb", "tasks", taskId, "attachments", "1234567890-large.txt"),
|
||||
join(testDir, ".fusion", "tasks", taskId, "attachments", "1234567890-large.txt"),
|
||||
largeContent,
|
||||
);
|
||||
|
||||
@@ -302,7 +302,7 @@ describe("readAttachmentContents", () => {
|
||||
// Write fake PNG data (just some bytes)
|
||||
const imageData = Buffer.from([0x89, 0x50, 0x4e, 0x47]); // PNG magic bytes
|
||||
await writeFile(
|
||||
join(testDir, ".kb", "tasks", taskId, "attachments", "1234567890-image.png"),
|
||||
join(testDir, ".fusion", "tasks", taskId, "attachments", "1234567890-image.png"),
|
||||
imageData,
|
||||
);
|
||||
|
||||
@@ -444,7 +444,7 @@ describe("requirePlanApproval setting", () => {
|
||||
});
|
||||
|
||||
it("sets awaiting-approval status instead of moving to todo when requirePlanApproval is true", async () => {
|
||||
const taskDir = join(rootDir, ".kb", "tasks", "FN-001");
|
||||
const taskDir = join(rootDir, ".fusion", "tasks", "FN-001");
|
||||
await mkdir(taskDir, { recursive: true });
|
||||
await writeFile(
|
||||
join(taskDir, "task.json"),
|
||||
|
||||
@@ -410,7 +410,7 @@ export class TriageProcessor {
|
||||
try {
|
||||
const detail = await this.store.getTask(task.id);
|
||||
const settings = await this.store.getSettings();
|
||||
const promptPath = `.kb/tasks/${task.id}/PROMPT.md`;
|
||||
const promptPath = `.fusion/tasks/${task.id}/PROMPT.md`;
|
||||
|
||||
const agentWork = async () => {
|
||||
// Set status only after the semaphore slot has been acquired, so
|
||||
@@ -1056,7 +1056,7 @@ export async function readAttachmentContents(
|
||||
for (const att of attachments) {
|
||||
const filePath = join(
|
||||
rootDir,
|
||||
".kb",
|
||||
".fusion",
|
||||
"tasks",
|
||||
taskId,
|
||||
"attachments",
|
||||
|
||||
Reference in New Issue
Block a user