edsger 0.28.1 → 0.28.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -62,12 +62,32 @@ export async function processHumanMessages(messages, featureId, config, verbose)
|
|
|
62
62
|
return `[Message ${i + 1} from ${m.sender_name}]: ${m.content}`;
|
|
63
63
|
});
|
|
64
64
|
const userPrompt = messageParts.join('\n\n');
|
|
65
|
-
// On first message: include full context so the session
|
|
66
|
-
//
|
|
65
|
+
// On first message: include full context + chat history so the session
|
|
66
|
+
// starts informed (covers CLI restart where in-memory session is lost).
|
|
67
|
+
// On resume: just send the new message — the session already has context.
|
|
67
68
|
let fullPrompt;
|
|
68
69
|
if (!existingSessionId) {
|
|
69
70
|
const context = await buildChatContext(featureId, channelId, verbose);
|
|
70
71
|
const contextStr = formatContextForAI(context);
|
|
72
|
+
// Load recent chat history so the AI has conversational continuity
|
|
73
|
+
const recentMessages = await listChatMessages(channelId, { limit: 30 }, verbose);
|
|
74
|
+
// Exclude the messages we're about to process (they'll be in userPrompt)
|
|
75
|
+
const currentIds = new Set(messages.map((m) => m.id));
|
|
76
|
+
const historyMessages = recentMessages.filter((m) => !currentIds.has(m.id));
|
|
77
|
+
let historySection = '';
|
|
78
|
+
if (historyMessages.length > 0) {
|
|
79
|
+
const historyLines = historyMessages.map((m) => {
|
|
80
|
+
const role = m.sender_type === 'ai' ? 'AI' : m.sender_type === 'system' ? 'System' : (m.sender_name || 'User');
|
|
81
|
+
return `[${role}]: ${m.content}`;
|
|
82
|
+
});
|
|
83
|
+
historySection = [
|
|
84
|
+
`## Previous Chat History`,
|
|
85
|
+
`The following is the recent conversation history in this channel. Continue the conversation naturally.`,
|
|
86
|
+
'',
|
|
87
|
+
...historyLines,
|
|
88
|
+
'',
|
|
89
|
+
].join('\n');
|
|
90
|
+
}
|
|
71
91
|
fullPrompt = [
|
|
72
92
|
`## Current Feature Context`,
|
|
73
93
|
contextStr,
|
|
@@ -75,6 +95,7 @@ export async function processHumanMessages(messages, featureId, config, verbose)
|
|
|
75
95
|
`## Channel ID: ${channelId}`,
|
|
76
96
|
`## Feature ID: ${featureId}`,
|
|
77
97
|
'',
|
|
98
|
+
historySection,
|
|
78
99
|
`## Human Message`,
|
|
79
100
|
userPrompt,
|
|
80
101
|
'',
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
import { createSdkMcpServer, tool } from '@anthropic-ai/claude-agent-sdk';
|
|
9
9
|
import { z } from 'zod';
|
|
10
10
|
import { callMcpEndpoint } from '../../api/mcp-client.js';
|
|
11
|
-
import { sendAiMessage } from '../../api/chat.js';
|
|
11
|
+
import { sendAiMessage, listChatMessages } from '../../api/chat.js';
|
|
12
12
|
/**
|
|
13
13
|
* Create an in-process MCP server with chat-specific tools.
|
|
14
14
|
* Pass the returned config to query() options.mcpServers.
|
|
@@ -59,8 +59,11 @@ export function createChatMcpServer() {
|
|
|
59
59
|
content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
|
|
60
60
|
};
|
|
61
61
|
}),
|
|
62
|
-
tool('update_user_stories', 'Create, update, or delete user stories based on feedback.', {
|
|
62
|
+
tool('update_user_stories', 'Create, update, or delete user stories based on feedback. Always provide a reason explaining why the change is being made.', {
|
|
63
63
|
feature_id: z.string().describe('Feature ID'),
|
|
64
|
+
reason: z
|
|
65
|
+
.string()
|
|
66
|
+
.describe('Why this change is being made — e.g. user feedback, bug found, requirement changed. Required for audit trail.'),
|
|
64
67
|
actions: z.array(z.object({
|
|
65
68
|
action: z.enum(['create', 'update', 'delete']),
|
|
66
69
|
user_story_id: z
|
|
@@ -98,6 +101,7 @@ export function createChatMcpServer() {
|
|
|
98
101
|
await callMcpEndpoint('user_stories/update_status', {
|
|
99
102
|
user_story_id: action.user_story_id,
|
|
100
103
|
status: action.status || 'draft',
|
|
104
|
+
reason: args.reason,
|
|
101
105
|
});
|
|
102
106
|
results.push({ action: 'update', success: true });
|
|
103
107
|
}
|
|
@@ -117,8 +121,11 @@ export function createChatMcpServer() {
|
|
|
117
121
|
],
|
|
118
122
|
};
|
|
119
123
|
}),
|
|
120
|
-
tool('update_test_cases', 'Create, update, or delete test cases based on feedback.', {
|
|
124
|
+
tool('update_test_cases', 'Create, update, or delete test cases based on feedback. Always provide a reason explaining why the change is being made.', {
|
|
121
125
|
feature_id: z.string().describe('Feature ID'),
|
|
126
|
+
reason: z
|
|
127
|
+
.string()
|
|
128
|
+
.describe('Why this change is being made — e.g. user feedback, bug found, requirement changed. Required for audit trail.'),
|
|
122
129
|
actions: z.array(z.object({
|
|
123
130
|
action: z.enum(['create', 'update', 'delete']),
|
|
124
131
|
test_case_id: z
|
|
@@ -156,6 +163,7 @@ export function createChatMcpServer() {
|
|
|
156
163
|
await callMcpEndpoint('test_cases/update_status', {
|
|
157
164
|
test_case_id: action.test_case_id,
|
|
158
165
|
status: 'draft',
|
|
166
|
+
reason: args.reason,
|
|
159
167
|
});
|
|
160
168
|
results.push({ action: 'update', success: true });
|
|
161
169
|
}
|
|
@@ -241,6 +249,36 @@ export function createChatMcpServer() {
|
|
|
241
249
|
],
|
|
242
250
|
};
|
|
243
251
|
}),
|
|
252
|
+
tool('get_chat_history', 'Retrieve older chat messages from the channel. Use this when you need more context about what was discussed earlier — for example, to understand referenced bugs, prior decisions, or feedback the user gave in earlier messages.', {
|
|
253
|
+
channel_id: z.string().describe('Chat channel ID'),
|
|
254
|
+
limit: z
|
|
255
|
+
.number()
|
|
256
|
+
.optional()
|
|
257
|
+
.describe('Number of messages to retrieve (default 50, max 100)'),
|
|
258
|
+
before: z
|
|
259
|
+
.string()
|
|
260
|
+
.optional()
|
|
261
|
+
.describe('Fetch messages before this ISO timestamp for pagination'),
|
|
262
|
+
}, async (args) => {
|
|
263
|
+
const limit = Math.min(args.limit || 50, 100);
|
|
264
|
+
const messages = await listChatMessages(args.channel_id, { limit, ...(args.before ? { since: args.before } : {}) });
|
|
265
|
+
const formatted = messages.map((m) => ({
|
|
266
|
+
id: m.id,
|
|
267
|
+
sender: m.sender_type === 'ai' ? 'AI' : m.sender_type === 'system' ? 'System' : (m.sender_name || 'User'),
|
|
268
|
+
sender_type: m.sender_type,
|
|
269
|
+
content: m.content,
|
|
270
|
+
message_type: m.message_type,
|
|
271
|
+
created_at: m.created_at,
|
|
272
|
+
}));
|
|
273
|
+
return {
|
|
274
|
+
content: [
|
|
275
|
+
{
|
|
276
|
+
type: 'text',
|
|
277
|
+
text: JSON.stringify({ messages: formatted, count: formatted.length }, null, 2),
|
|
278
|
+
},
|
|
279
|
+
],
|
|
280
|
+
};
|
|
281
|
+
}),
|
|
244
282
|
tool('trigger_phase_rerun', 'Reset a workflow phase to pending so it will re-run.', {
|
|
245
283
|
feature_id: z.string().describe('Feature ID'),
|
|
246
284
|
phase: z.string().describe('Phase name to reset'),
|