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 { 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
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
|
||||
{
|
||||
name: 'project_lock',
|
||||
|
||||
Reference in New Issue
Block a user