fix: Write context/index.json so agents can look up tasks by phase

Agents were bulk-reading all context task files (39 files) because
filenames are opaque IDs and there was no way to find phase-relevant
tasks without reading every file. Now writeInputFiles generates a
context/index.json with tasksByPhase mapping phaseId to task metadata
(file, id, name, status). Prompt updated to direct agents to read
the index first.
This commit is contained in:
Lukas May
2026-03-04 11:55:22 +01:00
parent 26ed9e0395
commit 5d8830d2d3
3 changed files with 56 additions and 3 deletions

View File

@@ -3,7 +3,7 @@
*/
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
import { mkdirSync, writeFileSync, rmSync, existsSync } from 'node:fs';
import { mkdirSync, writeFileSync, readFileSync, rmSync, existsSync } from 'node:fs';
import { join } from 'node:path';
import { tmpdir } from 'node:os';
import { randomUUID } from 'crypto';
@@ -116,6 +116,34 @@ describe('writeInputFiles', () => {
writeInputFiles({ agentWorkdir: testDir });
expect(existsSync(join(testDir, '.cw', 'input'))).toBe(true);
});
it('writes context/index.json grouping tasks by phaseId', () => {
writeInputFiles({
agentWorkdir: testDir,
tasks: [
{ id: 't1', name: 'Task A', phaseId: 'ph1', status: 'pending', category: 'execute', type: 'auto', priority: 'medium' } as Task,
{ id: 't2', name: 'Task B', phaseId: 'ph1', status: 'completed', category: 'execute', type: 'auto', priority: 'medium', summary: 'Done' } as Task,
{ id: 't3', name: 'Task C', phaseId: 'ph2', status: 'pending', category: 'research', type: 'auto', priority: 'high' } as Task,
],
});
const indexPath = join(testDir, '.cw', 'input', 'context', 'index.json');
expect(existsSync(indexPath)).toBe(true);
const index = JSON.parse(readFileSync(indexPath, 'utf-8'));
expect(index.tasksByPhase.ph1).toHaveLength(2);
expect(index.tasksByPhase.ph2).toHaveLength(1);
expect(index.tasksByPhase.ph1[0]).toEqual({
file: 'context/tasks/t1.md',
id: 't1',
name: 'Task A',
status: 'pending',
});
});
it('does not write context/index.json when no tasks', () => {
writeInputFiles({ agentWorkdir: testDir });
expect(existsSync(join(testDir, '.cw', 'input', 'context', 'index.json'))).toBe(false);
});
});
describe('readSummary', () => {

View File

@@ -262,6 +262,29 @@ export function writeInputFiles(options: WriteInputFilesOptions): void {
}
}
// Write context index — groups tasks by phaseId so agents can look up relevant files
// without bulk-reading every context file
if (options.tasks && options.tasks.length > 0) {
const tasksByPhase: Record<string, Array<{ file: string; id: string; name: string; status: string }>> = {};
for (const t of options.tasks) {
const phaseId = t.phaseId ?? '_unassigned';
if (!tasksByPhase[phaseId]) tasksByPhase[phaseId] = [];
tasksByPhase[phaseId].push({
file: `context/tasks/${t.id}.md`,
id: t.id,
name: t.name,
status: t.status,
});
}
const contextDir = join(inputDir, 'context');
mkdirSync(contextDir, { recursive: true });
writeFileSync(
join(contextDir, 'index.json'),
JSON.stringify({ tasksByPhase }, null, 2) + '\n',
'utf-8',
);
}
// Write manifest listing exactly which files were created
writeFileSync(
join(inputDir, 'manifest.json'),

View File

@@ -25,12 +25,14 @@ Read \`.cw/input/manifest.json\` first. It contains two arrays:
- \`pages/\` — one per page; frontmatter: title, parentPageId, sortOrder; body: markdown
**Context Files** (read-only, read on-demand)
- \`context/index.json\` — **read this first** when you need context. Contains \`tasksByPhase\`: a map of phaseId → array of \`{ file, id, name, status }\`. Use it to find relevant task files without bulk-reading.
- \`context/phases/\` — frontmatter: id, name, status, dependsOn; body: description
- \`context/tasks/\` — frontmatter: id, name, phaseId, parentTaskId, category, type, priority, status, summary; body: description
Completed tasks include a \`summary\` field with what the previous agent accomplished.
Context files provide awareness of the broader initiative. There may be dozens — do NOT bulk-read them.
When you need to check a specific phase or task, read that one file. Do not duplicate or contradict context file content in your output.
Context files provide awareness of the broader initiative. There may be dozens — do NOT bulk-read them all.
Use \`context/index.json\` to find which task files belong to a specific phase, then read only those.
Do not duplicate or contradict context file content in your output.
</input_files>`;
export const ID_GENERATION = `