diff --git a/get-shit-done/bin/lib/intel.cjs b/get-shit-done/bin/lib/intel.cjs index ed4773be6..f7a3763ce 100644 --- a/get-shit-done/bin/lib/intel.cjs +++ b/get-shit-done/bin/lib/intel.cjs @@ -19,10 +19,10 @@ const crypto = require('crypto'); const INTEL_DIR = '.planning/intel'; const INTEL_FILES = { - files: 'files.json', - apis: 'apis.json', - deps: 'deps.json', - arch: 'arch.md', + files: 'file-roles.json', + apis: 'api-map.json', + deps: 'dependency-graph.json', + arch: 'arch-decisions.json', stack: 'stack.json' }; @@ -204,10 +204,8 @@ function intelQuery(term, planningDir) { const matches = []; let total = 0; - // Search JSON intel files + // Search all JSON intel files for (const [_key, filename] of Object.entries(INTEL_FILES)) { - if (filename.endsWith('.md')) continue; // Skip arch.md here - const filePath = intelFilePath(planningDir, filename); const data = safeReadJson(filePath); if (!data) continue; @@ -219,14 +217,6 @@ function intelQuery(term, planningDir) { } } - // Search arch.md - const archPath = intelFilePath(planningDir, INTEL_FILES.arch); - const archMatches = searchArchMd(archPath, term); - if (archMatches.length > 0) { - matches.push({ source: INTEL_FILES.arch, entries: archMatches }); - total += archMatches.length; - } - return { matches, term, total }; } @@ -257,20 +247,10 @@ function intelStatus(planningDir) { let updatedAt = null; - if (filename.endsWith('.md')) { - // For arch.md, use file mtime - try { - const stat = fs.statSync(filePath); - updatedAt = stat.mtime.toISOString(); - } catch (_e) { - // intentionally silent: fall through on error - } - } else { - // For JSON files, read _meta.updated_at - const data = safeReadJson(filePath); - if (data && data._meta && data._meta.updated_at) { - updatedAt = data._meta.updated_at; - } + // All intel files are JSON — read _meta.updated_at + const data = safeReadJson(filePath); + if (data && data._meta && data._meta.updated_at) { + updatedAt = data._meta.updated_at; } let stale = true; @@ -409,8 +389,7 @@ function intelValidate(planningDir) { continue; } - // Skip non-JSON files (arch.md) - if (filename.endsWith('.md')) continue; + // All intel files are JSON — validate _meta and entries structure // Parse JSON let data; diff --git a/tests/intel.test.cjs b/tests/intel.test.cjs index 057506a45..354918dd4 100644 --- a/tests/intel.test.cjs +++ b/tests/intel.test.cjs @@ -160,7 +160,7 @@ describe('intelQuery', () => { }); test('finds matches in JSON file keys', () => { - writeIntelJson(planningDir, 'files.json', { + writeIntelJson(planningDir, 'file-roles.json', { _meta: { updated_at: new Date().toISOString() }, entries: { 'src/auth/controller.ts': { size: 1024, type: 'typescript' }, @@ -170,12 +170,12 @@ describe('intelQuery', () => { const result = intelQuery('auth', planningDir); assert.strictEqual(result.total, 1); - assert.strictEqual(result.matches[0].source, 'files.json'); + assert.strictEqual(result.matches[0].source, 'file-roles.json'); assert.strictEqual(result.matches[0].entries[0].key, 'src/auth/controller.ts'); }); test('finds matches in JSON file values', () => { - writeIntelJson(planningDir, 'deps.json', { + writeIntelJson(planningDir, 'dependency-graph.json', { _meta: { updated_at: new Date().toISOString() }, entries: { express: { version: '4.18.0', type: 'runtime', used_by: ['src/server.ts'] }, @@ -188,7 +188,7 @@ describe('intelQuery', () => { }); test('search is case-insensitive', () => { - writeIntelJson(planningDir, 'files.json', { + writeIntelJson(planningDir, 'file-roles.json', { entries: { 'src/AuthController.ts': { type: 'typescript' }, }, @@ -198,24 +198,25 @@ describe('intelQuery', () => { assert.strictEqual(result.total, 1); }); - test('finds matches in arch.md text', () => { - writeIntelMd(planningDir, 'arch.md', [ - '# Architecture', - '', - 'The system uses a layered architecture with REST API endpoints.', - 'Authentication is handled by JWT tokens.', - ].join('\n')); + test('finds matches in arch-decisions.json entries', () => { + writeIntelJson(planningDir, 'arch-decisions.json', { + _meta: { updated_at: new Date().toISOString() }, + entries: { + 'jwt-auth': { decision: 'Use JWT tokens for stateless authentication', status: 'accepted' }, + 'rest-api': { decision: 'REST API endpoints for all services', status: 'accepted' }, + }, + }); const result = intelQuery('JWT', planningDir); assert.strictEqual(result.total, 1); - assert.strictEqual(result.matches[0].source, 'arch.md'); + assert.strictEqual(result.matches[0].source, 'arch-decisions.json'); }); test('searches across multiple intel files', () => { - writeIntelJson(planningDir, 'files.json', { + writeIntelJson(planningDir, 'file-roles.json', { entries: { 'src/auth.ts': { exports: ['authenticate'] } }, }); - writeIntelJson(planningDir, 'apis.json', { + writeIntelJson(planningDir, 'api-map.json', { entries: { '/api/auth': { method: 'POST', handler: 'authenticate' } }, }); @@ -244,30 +245,30 @@ describe('intelStatus', () => { test('reports missing files as stale', () => { const result = intelStatus(planningDir); assert.strictEqual(result.overall_stale, true); - assert.strictEqual(result.files['files.json'].exists, false); - assert.strictEqual(result.files['files.json'].stale, true); + assert.strictEqual(result.files['file-roles.json'].exists, false); + assert.strictEqual(result.files['file-roles.json'].stale, true); }); test('reports fresh files as not stale', () => { - writeIntelJson(planningDir, 'files.json', { + writeIntelJson(planningDir, 'file-roles.json', { _meta: { updated_at: new Date().toISOString() }, entries: {}, }); const result = intelStatus(planningDir); - assert.strictEqual(result.files['files.json'].exists, true); - assert.strictEqual(result.files['files.json'].stale, false); + assert.strictEqual(result.files['file-roles.json'].exists, true); + assert.strictEqual(result.files['file-roles.json'].stale, false); }); test('reports old files as stale', () => { const oldDate = new Date(Date.now() - 25 * 60 * 60 * 1000).toISOString(); - writeIntelJson(planningDir, 'files.json', { + writeIntelJson(planningDir, 'file-roles.json', { _meta: { updated_at: oldDate }, entries: {}, }); const result = intelStatus(planningDir); - assert.strictEqual(result.files['files.json'].stale, true); + assert.strictEqual(result.files['file-roles.json'].stale, true); assert.strictEqual(result.overall_stale, true); }); }); @@ -303,24 +304,24 @@ describe('intelDiff', () => { ); // Add a file after snapshot - writeIntelJson(planningDir, 'files.json', { entries: {} }); + writeIntelJson(planningDir, 'file-roles.json', { entries: {} }); const result = intelDiff(planningDir); - assert.ok(result.added.includes('files.json')); + assert.ok(result.added.includes('file-roles.json')); }); test('detects changed files since snapshot', () => { // Write initial file - writeIntelJson(planningDir, 'files.json', { entries: { a: 1 } }); + writeIntelJson(planningDir, 'file-roles.json', { entries: { a: 1 } }); // Take snapshot intelSnapshot(planningDir); // Modify file - writeIntelJson(planningDir, 'files.json', { entries: { a: 1, b: 2 } }); + writeIntelJson(planningDir, 'file-roles.json', { entries: { a: 1, b: 2 } }); const result = intelDiff(planningDir); - assert.ok(result.changed.includes('files.json')); + assert.ok(result.changed.includes('file-roles.json')); }); }); @@ -341,7 +342,7 @@ describe('intelSnapshot', () => { }); test('saves snapshot with file hashes', () => { - writeIntelJson(planningDir, 'files.json', { entries: {} }); + writeIntelJson(planningDir, 'file-roles.json', { entries: {} }); const result = intelSnapshot(planningDir); assert.strictEqual(result.saved, true); @@ -351,7 +352,7 @@ describe('intelSnapshot', () => { const snapshot = JSON.parse( fs.readFileSync(path.join(planningDir, 'intel', '.last-refresh.json'), 'utf8') ); - assert.ok(snapshot.hashes['files.json']); + assert.ok(snapshot.hashes['file-roles.json']); }); }); @@ -379,11 +380,11 @@ describe('intelValidate', () => { }); test('reports warnings for missing _meta.updated_at', () => { - writeIntelJson(planningDir, 'files.json', { entries: {} }); - writeIntelJson(planningDir, 'apis.json', { entries: {} }); - writeIntelJson(planningDir, 'deps.json', { entries: {} }); + writeIntelJson(planningDir, 'file-roles.json', { entries: {} }); + writeIntelJson(planningDir, 'api-map.json', { entries: {} }); + writeIntelJson(planningDir, 'dependency-graph.json', { entries: {} }); writeIntelJson(planningDir, 'stack.json', { entries: {} }); - writeIntelMd(planningDir, 'arch.md', '# Architecture\n'); + writeIntelJson(planningDir, 'arch-decisions.json', { entries: {} }); const result = intelValidate(planningDir); assert.strictEqual(result.valid, true); @@ -393,11 +394,11 @@ describe('intelValidate', () => { test('reports invalid JSON as error', () => { const intelPath = path.join(planningDir, 'intel'); fs.mkdirSync(intelPath, { recursive: true }); - fs.writeFileSync(path.join(intelPath, 'files.json'), 'not valid json', 'utf8'); - writeIntelJson(planningDir, 'apis.json', { entries: {} }); - writeIntelJson(planningDir, 'deps.json', { entries: {} }); + fs.writeFileSync(path.join(intelPath, 'file-roles.json'), 'not valid json', 'utf8'); + writeIntelJson(planningDir, 'api-map.json', { entries: {} }); + writeIntelJson(planningDir, 'dependency-graph.json', { entries: {} }); writeIntelJson(planningDir, 'stack.json', { entries: {} }); - writeIntelMd(planningDir, 'arch.md', '# Architecture\n'); + writeIntelJson(planningDir, 'arch-decisions.json', { entries: {} }); const result = intelValidate(planningDir); assert.strictEqual(result.valid, false); @@ -406,15 +407,15 @@ describe('intelValidate', () => { test('passes validation with complete fresh intel', () => { const now = new Date().toISOString(); - writeIntelJson(planningDir, 'files.json', { + writeIntelJson(planningDir, 'file-roles.json', { _meta: { updated_at: now }, entries: {}, }); - writeIntelJson(planningDir, 'apis.json', { + writeIntelJson(planningDir, 'api-map.json', { _meta: { updated_at: now }, entries: {}, }); - writeIntelJson(planningDir, 'deps.json', { + writeIntelJson(planningDir, 'dependency-graph.json', { _meta: { updated_at: now }, entries: {}, }); @@ -422,7 +423,10 @@ describe('intelValidate', () => { _meta: { updated_at: now }, entries: {}, }); - writeIntelMd(planningDir, 'arch.md', '# Architecture\n'); + writeIntelJson(planningDir, 'arch-decisions.json', { + _meta: { updated_at: now }, + entries: {}, + }); const result = intelValidate(planningDir); assert.strictEqual(result.valid, true); @@ -446,12 +450,12 @@ describe('intelPatchMeta', () => { }); test('patches _meta.updated_at and increments version', () => { - writeIntelJson(planningDir, 'files.json', { + writeIntelJson(planningDir, 'file-roles.json', { _meta: { updated_at: '2025-01-01T00:00:00Z', version: 1 }, entries: {}, }); - const filePath = path.join(planningDir, 'intel', 'files.json'); + const filePath = path.join(planningDir, 'intel', 'file-roles.json'); const result = intelPatchMeta(filePath); assert.strictEqual(result.patched, true); @@ -462,9 +466,9 @@ describe('intelPatchMeta', () => { }); test('creates _meta if missing', () => { - writeIntelJson(planningDir, 'files.json', { entries: {} }); + writeIntelJson(planningDir, 'file-roles.json', { entries: {} }); - const filePath = path.join(planningDir, 'intel', 'files.json'); + const filePath = path.join(planningDir, 'intel', 'file-roles.json'); const result = intelPatchMeta(filePath); assert.strictEqual(result.patched, true);