diff --git a/src/core/counter.ts b/src/core/counter.ts new file mode 100644 index 0000000..f14bdd1 --- /dev/null +++ b/src/core/counter.ts @@ -0,0 +1,27 @@ +import { readFileSync, writeFileSync, existsSync, mkdirSync } from 'fs'; +import { getAgentHubDir, getCounterPath } from './paths.js'; + +const prefixes: Record = { + task: 'TSK', + handoff: 'HOF', + decision: 'DEC', + memory: 'MEM', +}; + +export type CounterType = keyof typeof prefixes; + +export function getNextId(cwd: string, type: CounterType): string { + const path = getCounterPath(cwd); + const counters: Record = existsSync(path) + ? JSON.parse(readFileSync(path, 'utf-8')) + : {}; + + const current = counters[type] ?? 0; + const next = current + 1; + counters[type] = next; + + mkdirSync(getAgentHubDir(cwd), { recursive: true }); + writeFileSync(path, JSON.stringify(counters, null, 2)); + + return `${prefixes[type]}-${String(next).padStart(4, '0')}`; +} diff --git a/tests/counter.test.ts b/tests/counter.test.ts new file mode 100644 index 0000000..195c2bf --- /dev/null +++ b/tests/counter.test.ts @@ -0,0 +1,28 @@ +import { describe, it, expect, beforeEach, afterEach } from 'vitest'; +import { mkdtempSync, rmSync } from 'fs'; +import { tmpdir } from 'os'; +import { join } from 'path'; +import { getNextId } from '../src/core/counter.js'; + +describe('counter', () => { + let cwd: string; + + beforeEach(() => { + cwd = mkdtempSync(join(tmpdir(), 'agenthub-')); + }); + + afterEach(() => { + rmSync(cwd, { recursive: true, force: true }); + }); + + it('generates sequential task ids', () => { + expect(getNextId(cwd, 'task')).toBe('TSK-0001'); + expect(getNextId(cwd, 'task')).toBe('TSK-0002'); + }); + + it('keeps counters separate by type', () => { + expect(getNextId(cwd, 'handoff')).toBe('HOF-0001'); + expect(getNextId(cwd, 'task')).toBe('TSK-0001'); + expect(getNextId(cwd, 'handoff')).toBe('HOF-0002'); + }); +});