From fb3f6c3e556b3f90e5aa77651c8d062c9907453e Mon Sep 17 00:00:00 2001 From: zenchantlive Date: Wed, 11 Feb 2026 19:44:55 -0800 Subject: [PATCH] feat: attach project context to read issues --- src/lib/read-issues.ts | 19 +++++++++++++++---- tests/lib/read-issues.test.ts | 8 +++++++- 2 files changed, 22 insertions(+), 5 deletions(-) diff --git a/src/lib/read-issues.ts b/src/lib/read-issues.ts index 6dca081..482fbda 100644 --- a/src/lib/read-issues.ts +++ b/src/lib/read-issues.ts @@ -3,11 +3,14 @@ import path from 'node:path'; import { parseIssuesJsonl } from './parser'; import { canonicalizeWindowsPath } from './pathing'; -import type { BeadIssue } from './types'; +import { buildProjectContext } from './project-context'; +import type { BeadIssueWithProject, ProjectSource } from './types'; export interface ReadIssuesOptions { projectRoot?: string; includeTombstones?: boolean; + projectSource?: ProjectSource; + projectAddedAt?: string | null; } export function resolveIssuesJsonlPathCandidates(projectRoot: string = process.cwd()): string[] { @@ -21,15 +24,23 @@ export function resolveIssuesJsonlPath(projectRoot: string = process.cwd()): str return resolveIssuesJsonlPathCandidates(projectRoot)[0]; } -export async function readIssuesFromDisk(options: ReadIssuesOptions = {}): Promise { - const candidates = resolveIssuesJsonlPathCandidates(options.projectRoot); +export async function readIssuesFromDisk(options: ReadIssuesOptions = {}): Promise { + const projectRoot = options.projectRoot ?? process.cwd(); + const candidates = resolveIssuesJsonlPathCandidates(projectRoot); + const project = buildProjectContext(projectRoot, { + source: options.projectSource ?? 'local', + addedAt: options.projectAddedAt ?? null, + }); for (const issuesPath of candidates) { try { const jsonl = await fs.readFile(issuesPath, 'utf8'); return parseIssuesJsonl(jsonl, { includeTombstones: options.includeTombstones ?? false, - }); + }).map((issue) => ({ + ...issue, + project, + })); } catch (error) { if ((error as NodeJS.ErrnoException).code === 'ENOENT') { continue; diff --git a/tests/lib/read-issues.test.ts b/tests/lib/read-issues.test.ts index 1aa4b70..586c703 100644 --- a/tests/lib/read-issues.test.ts +++ b/tests/lib/read-issues.test.ts @@ -5,7 +5,7 @@ import os from 'node:os'; import path from 'node:path'; import { readIssuesFromDisk, resolveIssuesJsonlPath, resolveIssuesJsonlPathCandidates } from '../../src/lib/read-issues'; -import { sameWindowsPath } from '../../src/lib/pathing'; +import { canonicalizeWindowsPath, sameWindowsPath, toDisplayPath, windowsPathKey } from '../../src/lib/pathing'; test('resolveIssuesJsonlPath appends .beads/issues.jsonl using windows-safe pathing', () => { const resolved = resolveIssuesJsonlPath('C:/Repo/Project'); @@ -38,6 +38,12 @@ test('readIssuesFromDisk parses JSONL issues from disk', async () => { assert.equal(issues.length, 1); assert.equal(issues[0].id, 'bb-1'); assert.equal(issues[0].priority, 0); + assert.equal(issues[0].project.root, canonicalizeWindowsPath(root)); + assert.equal(issues[0].project.key, windowsPathKey(root)); + assert.equal(issues[0].project.displayPath, toDisplayPath(root)); + assert.equal(issues[0].project.name, path.basename(canonicalizeWindowsPath(root))); + assert.equal(issues[0].project.source, 'local'); + assert.equal(issues[0].project.addedAt, null); }); test('readIssuesFromDisk returns empty list when issues file does not exist', async () => {