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:
12
src/index.ts
12
src/index.ts
@@ -26,6 +26,7 @@ import { taskDelegations, taskDelegationQuery } from './tools/delegations.js';
|
|||||||
import { projectLock, projectUnlock, projectLockStatus, projectContext } from './tools/locks.js';
|
import { projectLock, projectUnlock, projectLockStatus, projectContext } from './tools/locks.js';
|
||||||
import { versionAdd, versionList, versionShow, versionUpdate, versionRelease, versionAssignTask } from './tools/versions.js';
|
import { versionAdd, versionList, versionShow, versionUpdate, versionRelease, versionAssignTask } from './tools/versions.js';
|
||||||
import { taskCommitAdd, taskCommitRemove, taskCommitsList, taskLinkCommits, sessionTasks } from './tools/commits.js';
|
import { taskCommitAdd, taskCommitRemove, taskCommitsList, taskLinkCommits, sessionTasks } from './tools/commits.js';
|
||||||
|
import { changelogAdd, changelogSinceSession, changelogList } from './tools/changelog.js';
|
||||||
import {
|
import {
|
||||||
componentRegister,
|
componentRegister,
|
||||||
componentList,
|
componentList,
|
||||||
@@ -287,6 +288,17 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|||||||
});
|
});
|
||||||
break;
|
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
|
// Impact Analysis
|
||||||
case 'component_register':
|
case 'component_register':
|
||||||
result = JSON.stringify(await componentRegister(a.id, a.name, a.type, {
|
result = JSON.stringify(await componentRegister(a.id, a.name, a.type, {
|
||||||
|
|||||||
116
src/tools/changelog.ts
Normal file
116
src/tools/changelog.ts
Normal 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;
|
||||||
|
}
|
||||||
@@ -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
|
// Project Lock Tools
|
||||||
{
|
{
|
||||||
name: 'project_lock',
|
name: 'project_lock',
|
||||||
|
|||||||
Reference in New Issue
Block a user