2026-02-11 17:55:26 -08:00
|
|
|
import fs from 'node:fs/promises';
|
|
|
|
|
import path from 'node:path';
|
|
|
|
|
|
|
|
|
|
import { parseIssuesJsonl } from './parser';
|
|
|
|
|
import { canonicalizeWindowsPath } from './pathing';
|
|
|
|
|
import type { BeadIssue } from './types';
|
|
|
|
|
|
|
|
|
|
export interface ReadIssuesOptions {
|
|
|
|
|
projectRoot?: string;
|
|
|
|
|
includeTombstones?: boolean;
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-11 18:38:51 -08:00
|
|
|
export function resolveIssuesJsonlPathCandidates(projectRoot: string = process.cwd()): string[] {
|
|
|
|
|
const baseDir = path.resolve(projectRoot, '.beads');
|
|
|
|
|
const primary = canonicalizeWindowsPath(path.join(baseDir, 'issues.jsonl'));
|
|
|
|
|
const fallback = canonicalizeWindowsPath(path.join(baseDir, 'issues.jsonl.new'));
|
|
|
|
|
return [primary, fallback];
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-11 17:55:26 -08:00
|
|
|
export function resolveIssuesJsonlPath(projectRoot: string = process.cwd()): string {
|
2026-02-11 18:38:51 -08:00
|
|
|
return resolveIssuesJsonlPathCandidates(projectRoot)[0];
|
2026-02-11 17:55:26 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export async function readIssuesFromDisk(options: ReadIssuesOptions = {}): Promise<BeadIssue[]> {
|
2026-02-11 18:38:51 -08:00
|
|
|
const candidates = resolveIssuesJsonlPathCandidates(options.projectRoot);
|
2026-02-11 17:55:26 -08:00
|
|
|
|
2026-02-11 18:38:51 -08:00
|
|
|
for (const issuesPath of candidates) {
|
|
|
|
|
try {
|
|
|
|
|
const jsonl = await fs.readFile(issuesPath, 'utf8');
|
|
|
|
|
return parseIssuesJsonl(jsonl, {
|
|
|
|
|
includeTombstones: options.includeTombstones ?? false,
|
|
|
|
|
});
|
|
|
|
|
} catch (error) {
|
|
|
|
|
if ((error as NodeJS.ErrnoException).code === 'ENOENT') {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
throw error;
|
|
|
|
|
}
|
2026-02-11 17:55:26 -08:00
|
|
|
}
|
2026-02-11 18:38:51 -08:00
|
|
|
|
|
|
|
|
return [];
|
2026-02-11 17:55:26 -08:00
|
|
|
}
|