Add infrastructure changelog system to task-mcp

Implements session-aware infrastructure change tracking for CF-179.

Changes:
- New changelog.ts with 3 MCP tools:
  - changelog_add: Add infrastructure changes
  - changelog_since_session: Query changes since last session
  - changelog_list: Time-based fallback query
- Integrated with session-start command via MCP
- Database-first architecture (PostgreSQL on infra VM)
- Session-relative queries (not fixed time windows)

Database schema:
- infrastructure_changelog table (6 fields)
- Migrated 5 historical changes
- Indexes on date, session_id, created_at

Task: CF-179

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
Christian Gick
2026-01-17 12:13:24 +02:00
parent 8db391f680
commit cc88b234ae
3 changed files with 169 additions and 0 deletions

View File

@@ -26,6 +26,7 @@ import { taskDelegations, taskDelegationQuery } from './tools/delegations.js';
import { projectLock, projectUnlock, projectLockStatus, projectContext } from './tools/locks.js';
import { versionAdd, versionList, versionShow, versionUpdate, versionRelease, versionAssignTask } from './tools/versions.js';
import { taskCommitAdd, taskCommitRemove, taskCommitsList, taskLinkCommits, sessionTasks } from './tools/commits.js';
import { changelogAdd, changelogSinceSession, changelogList } from './tools/changelog.js';
import {
componentRegister,
componentList,
@@ -287,6 +288,17 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
});
break;
// Infrastructure Changelog
case 'changelog_add':
result = await changelogAdd(a as any);
break;
case 'changelog_since_session':
result = await changelogSinceSession(a as any);
break;
case 'changelog_list':
result = await changelogList(a.days_back, a.limit);
break;
// Impact Analysis
case 'component_register':
result = JSON.stringify(await componentRegister(a.id, a.name, a.type, {

116
src/tools/changelog.ts Normal file
View File

@@ -0,0 +1,116 @@
import { query, queryOne } from '../db.js';
interface ChangelogAddArgs {
date: string; // YYYY-MM-DD
title: string;
change_description: string;
impact: string;
actions_required?: string;
session_id?: string;
task_ids?: string[];
}
interface ChangelogSinceSessionArgs {
project: string; // To find last session for this project
}
/**
* Add infrastructure changelog entry
*/
export async function changelogAdd(args: ChangelogAddArgs): Promise<string> {
const { date, title, change_description, impact, actions_required, session_id, task_ids } = args;
await query(
`INSERT INTO infrastructure_changelog
(date, title, change_description, impact, actions_required, session_id, task_ids)
VALUES ($1, $2, $3, $4, $5, $6, $7)
ON CONFLICT (date, title) DO NOTHING`,
[date, title, change_description, impact, actions_required || null, session_id || null, task_ids || null]
);
return `Changelog entry added: ${date} - ${title}`;
}
/**
* Get infrastructure changes since last session for a project
* Returns changes between last session end and now
*/
export async function changelogSinceSession(args: ChangelogSinceSessionArgs): Promise<string> {
const { project } = args;
// Find last completed session for this project
const lastSession = await queryOne<{ ended_at: string }>(
`SELECT ended_at
FROM sessions
WHERE project = $1 AND status = 'completed' AND ended_at IS NOT NULL
ORDER BY ended_at DESC
LIMIT 1`,
[project]
);
if (!lastSession || !lastSession.ended_at) {
// No previous session - show last 7 days as fallback
const changes = await query<any>(
`SELECT date, title, change_description, impact, actions_required
FROM infrastructure_changelog
WHERE date >= CURRENT_DATE - INTERVAL '7 days'
ORDER BY date DESC`,
[]
);
if (changes.length === 0) {
return '📋 **Infrastructure Changes:** None in last 7 days (no previous session found)';
}
let output = '📋 **Infrastructure Changes (last 7 days, no previous session):**\n';
changes.forEach((c: any) => {
output += `${c.date}: ${c.title}\n`;
});
return output;
}
// Query changes since last session ended
const changes = await query<any>(
`SELECT date, title, change_description, impact, actions_required, task_ids
FROM infrastructure_changelog
WHERE created_at > $1
ORDER BY date DESC`,
[lastSession.ended_at]
);
if (changes.length === 0) {
const lastDate = new Date(lastSession.ended_at).toISOString().split('T')[0];
return `📋 **Infrastructure Changes:** None since last session (${lastDate})`;
}
const lastDate = new Date(lastSession.ended_at).toISOString().split('T')[0];
let output = `📋 **Infrastructure Changes (since ${lastDate}):**\n`;
changes.forEach((c: any) => {
output += `${c.date}: ${c.title}\n`;
});
return output;
}
/**
* List recent infrastructure changes (fallback, time-based)
*/
export async function changelogList(days_back = 7, limit = 20): Promise<string> {
const changes = await query<any>(
`SELECT date, title, change_description, impact, actions_required, task_ids
FROM infrastructure_changelog
WHERE date >= CURRENT_DATE - INTERVAL '${days_back} days'
ORDER BY date DESC
LIMIT $1`,
[limit]
);
if (changes.length === 0) {
return `📋 **Infrastructure Changes:** None in last ${days_back} days`;
}
let output = `📋 **Infrastructure Changes (last ${days_back} days):**\n`;
changes.forEach((c: any) => {
output += `${c.date}: ${c.title}\n`;
});
return output;
}

View File

@@ -389,6 +389,47 @@ export const toolDefinitions = [
},
},
// Infrastructure Changelog Tools
{
name: 'changelog_add',
description: 'Add infrastructure change entry for session awareness',
inputSchema: {
type: 'object',
properties: {
date: { type: 'string', description: 'Change date (YYYY-MM-DD)' },
title: { type: 'string', description: 'Short title (max 100 chars)' },
change_description: { type: 'string', description: 'What changed' },
impact: { type: 'string', description: 'Effects on existing infrastructure' },
actions_required: { type: 'string', description: 'Steps developers need to take (optional)' },
session_id: { type: 'string', description: 'Session that implemented change (optional)' },
task_ids: { type: 'array', items: { type: 'string' }, description: 'Related task IDs (optional)' },
},
required: ['date', 'title', 'change_description', 'impact'],
},
},
{
name: 'changelog_since_session',
description: 'Get infrastructure changes since last session for a project. Use at session start.',
inputSchema: {
type: 'object',
properties: {
project: { type: 'string', description: 'Project key (e.g., CF, VPN) to find last session' },
},
required: ['project'],
},
},
{
name: 'changelog_list',
description: 'List recent infrastructure changes by time period (fallback)',
inputSchema: {
type: 'object',
properties: {
days_back: { type: 'number', description: 'Days to look back (default: 7)' },
limit: { type: 'number', description: 'Max results (default: 20)' },
},
},
},
// Project Lock Tools
{
name: 'project_lock',