kiro-memory 2.1.0 → 3.0.1
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.
- package/README.md +5 -1
- package/package.json +3 -3
- package/plugin/dist/cli/contextkit.js +2342 -183
- package/plugin/dist/hooks/agentSpawn.js +575 -52
- package/plugin/dist/hooks/kiro-hooks.js +575 -52
- package/plugin/dist/hooks/postToolUse.js +583 -59
- package/plugin/dist/hooks/stop.js +575 -52
- package/plugin/dist/hooks/userPromptSubmit.js +578 -53
- package/plugin/dist/index.js +576 -53
- package/plugin/dist/plugins/github/github-client.js +152 -0
- package/plugin/dist/plugins/github/index.js +412 -0
- package/plugin/dist/plugins/github/issue-parser.js +54 -0
- package/plugin/dist/plugins/slack/formatter.js +90 -0
- package/plugin/dist/plugins/slack/index.js +215 -0
- package/plugin/dist/sdk/index.js +575 -52
- package/plugin/dist/servers/mcp-server.js +4461 -397
- package/plugin/dist/services/search/EmbeddingService.js +64 -20
- package/plugin/dist/services/search/HybridSearch.js +380 -38
- package/plugin/dist/services/search/VectorSearch.js +65 -21
- package/plugin/dist/services/search/index.js +380 -38
- package/plugin/dist/services/sqlite/Backup.js +416 -0
- package/plugin/dist/services/sqlite/Database.js +98 -3
- package/plugin/dist/services/sqlite/ImportExport.js +452 -0
- package/plugin/dist/services/sqlite/Observations.js +291 -7
- package/plugin/dist/services/sqlite/Prompts.js +1 -1
- package/plugin/dist/services/sqlite/Search.js +10 -10
- package/plugin/dist/services/sqlite/Summaries.js +4 -4
- package/plugin/dist/services/sqlite/index.js +1350 -31
- package/plugin/dist/viewer.css +1 -1
- package/plugin/dist/viewer.js +16 -8
- package/plugin/dist/viewer.js.map +4 -4
- package/plugin/dist/worker-service.js +326 -75
- package/plugin/dist/worker-service.js.map +4 -4
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { createRequire } from 'module';const require = createRequire(import.meta.url);
|
|
2
|
+
|
|
3
|
+
// src/plugins/slack/formatter.ts
|
|
4
|
+
function truncateText(text, maxLength) {
|
|
5
|
+
if (text.length <= maxLength) return text;
|
|
6
|
+
return text.slice(0, maxLength - 3) + "...";
|
|
7
|
+
}
|
|
8
|
+
function buildHeaderBlock(project) {
|
|
9
|
+
return {
|
|
10
|
+
type: "header",
|
|
11
|
+
text: {
|
|
12
|
+
type: "plain_text",
|
|
13
|
+
text: `Sessione completata: ${project}`,
|
|
14
|
+
emoji: true
|
|
15
|
+
}
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
function buildStatsBlock(data) {
|
|
19
|
+
return {
|
|
20
|
+
type: "section",
|
|
21
|
+
fields: [
|
|
22
|
+
{
|
|
23
|
+
type: "mrkdwn",
|
|
24
|
+
text: `*Progetto:*
|
|
25
|
+
${data.project}`
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
type: "mrkdwn",
|
|
29
|
+
text: `*Sessione:*
|
|
30
|
+
\`${truncateText(data.sessionId, 12)}\``
|
|
31
|
+
}
|
|
32
|
+
],
|
|
33
|
+
text: {
|
|
34
|
+
type: "mrkdwn",
|
|
35
|
+
text: " "
|
|
36
|
+
// Campo obbligatorio ma non usato quando ci sono fields
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
function buildSummaryBlock(summary) {
|
|
41
|
+
const displayText = summary && summary.trim().length > 0 ? truncateText(summary.trim(), 2500) : "_Nessun sommario disponibile per questa sessione._";
|
|
42
|
+
return {
|
|
43
|
+
type: "section",
|
|
44
|
+
text: {
|
|
45
|
+
type: "mrkdwn",
|
|
46
|
+
text: `*Sommario:*
|
|
47
|
+
${displayText}`
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
function buildDivider() {
|
|
52
|
+
return { type: "divider" };
|
|
53
|
+
}
|
|
54
|
+
function buildContextBlock() {
|
|
55
|
+
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
56
|
+
return {
|
|
57
|
+
type: "context",
|
|
58
|
+
elements: [
|
|
59
|
+
{
|
|
60
|
+
type: "mrkdwn",
|
|
61
|
+
text: `Kiro Memory | ${timestamp}`
|
|
62
|
+
}
|
|
63
|
+
]
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
function buildSlackPayload(data) {
|
|
67
|
+
const blocks = [
|
|
68
|
+
buildHeaderBlock(data.project),
|
|
69
|
+
buildDivider(),
|
|
70
|
+
buildStatsBlock(data),
|
|
71
|
+
buildSummaryBlock(data.summary),
|
|
72
|
+
buildDivider(),
|
|
73
|
+
buildContextBlock()
|
|
74
|
+
];
|
|
75
|
+
const fallback = data.summary ? `Sessione ${data.project} completata: ${truncateText(data.summary, 200)}` : `Sessione ${data.project} completata.`;
|
|
76
|
+
return {
|
|
77
|
+
...data.channel ? { channel: data.channel } : {},
|
|
78
|
+
text: fallback,
|
|
79
|
+
blocks
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
export {
|
|
83
|
+
buildContextBlock,
|
|
84
|
+
buildDivider,
|
|
85
|
+
buildHeaderBlock,
|
|
86
|
+
buildSlackPayload,
|
|
87
|
+
buildStatsBlock,
|
|
88
|
+
buildSummaryBlock,
|
|
89
|
+
truncateText
|
|
90
|
+
};
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
import { createRequire } from 'module';const require = createRequire(import.meta.url);
|
|
2
|
+
|
|
3
|
+
// src/plugins/slack/formatter.ts
|
|
4
|
+
function truncateText(text, maxLength) {
|
|
5
|
+
if (text.length <= maxLength) return text;
|
|
6
|
+
return text.slice(0, maxLength - 3) + "...";
|
|
7
|
+
}
|
|
8
|
+
function buildHeaderBlock(project) {
|
|
9
|
+
return {
|
|
10
|
+
type: "header",
|
|
11
|
+
text: {
|
|
12
|
+
type: "plain_text",
|
|
13
|
+
text: `Sessione completata: ${project}`,
|
|
14
|
+
emoji: true
|
|
15
|
+
}
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
function buildStatsBlock(data) {
|
|
19
|
+
return {
|
|
20
|
+
type: "section",
|
|
21
|
+
fields: [
|
|
22
|
+
{
|
|
23
|
+
type: "mrkdwn",
|
|
24
|
+
text: `*Progetto:*
|
|
25
|
+
${data.project}`
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
type: "mrkdwn",
|
|
29
|
+
text: `*Sessione:*
|
|
30
|
+
\`${truncateText(data.sessionId, 12)}\``
|
|
31
|
+
}
|
|
32
|
+
],
|
|
33
|
+
text: {
|
|
34
|
+
type: "mrkdwn",
|
|
35
|
+
text: " "
|
|
36
|
+
// Campo obbligatorio ma non usato quando ci sono fields
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
function buildSummaryBlock(summary) {
|
|
41
|
+
const displayText = summary && summary.trim().length > 0 ? truncateText(summary.trim(), 2500) : "_Nessun sommario disponibile per questa sessione._";
|
|
42
|
+
return {
|
|
43
|
+
type: "section",
|
|
44
|
+
text: {
|
|
45
|
+
type: "mrkdwn",
|
|
46
|
+
text: `*Sommario:*
|
|
47
|
+
${displayText}`
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
function buildDivider() {
|
|
52
|
+
return { type: "divider" };
|
|
53
|
+
}
|
|
54
|
+
function buildContextBlock() {
|
|
55
|
+
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
56
|
+
return {
|
|
57
|
+
type: "context",
|
|
58
|
+
elements: [
|
|
59
|
+
{
|
|
60
|
+
type: "mrkdwn",
|
|
61
|
+
text: `Kiro Memory | ${timestamp}`
|
|
62
|
+
}
|
|
63
|
+
]
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
function buildSlackPayload(data) {
|
|
67
|
+
const blocks = [
|
|
68
|
+
buildHeaderBlock(data.project),
|
|
69
|
+
buildDivider(),
|
|
70
|
+
buildStatsBlock(data),
|
|
71
|
+
buildSummaryBlock(data.summary),
|
|
72
|
+
buildDivider(),
|
|
73
|
+
buildContextBlock()
|
|
74
|
+
];
|
|
75
|
+
const fallback = data.summary ? `Sessione ${data.project} completata: ${truncateText(data.summary, 200)}` : `Sessione ${data.project} completata.`;
|
|
76
|
+
return {
|
|
77
|
+
...data.channel ? { channel: data.channel } : {},
|
|
78
|
+
text: fallback,
|
|
79
|
+
blocks
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// src/plugins/slack/index.ts
|
|
84
|
+
var MAX_RETRIES = 3;
|
|
85
|
+
var INITIAL_RETRY_DELAY_MS = 1e3;
|
|
86
|
+
var FETCH_TIMEOUT_MS = 1e4;
|
|
87
|
+
function validateConfig(raw) {
|
|
88
|
+
const webhookUrl = raw.webhookUrl;
|
|
89
|
+
if (!webhookUrl || typeof webhookUrl !== "string") {
|
|
90
|
+
throw new Error('Configurazione Slack: "webhookUrl" \xE8 obbligatorio e deve essere una stringa');
|
|
91
|
+
}
|
|
92
|
+
if (!webhookUrl.startsWith("https://")) {
|
|
93
|
+
throw new Error('Configurazione Slack: "webhookUrl" deve iniziare con https://');
|
|
94
|
+
}
|
|
95
|
+
const channel = typeof raw.channel === "string" ? raw.channel : void 0;
|
|
96
|
+
const events = Array.isArray(raw.events) ? raw.events.filter((e) => typeof e === "string") : ["onSessionEnd"];
|
|
97
|
+
return { webhookUrl, channel, events };
|
|
98
|
+
}
|
|
99
|
+
async function sendWebhook(webhookUrl, payload, logger, fetchFn = globalThis.fetch) {
|
|
100
|
+
let retries = 0;
|
|
101
|
+
let delayMs = INITIAL_RETRY_DELAY_MS;
|
|
102
|
+
while (retries <= MAX_RETRIES) {
|
|
103
|
+
try {
|
|
104
|
+
const controller = new AbortController();
|
|
105
|
+
const timeoutId = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS);
|
|
106
|
+
const response = await fetchFn(webhookUrl, {
|
|
107
|
+
method: "POST",
|
|
108
|
+
headers: { "Content-Type": "application/json" },
|
|
109
|
+
body: JSON.stringify(payload),
|
|
110
|
+
signal: controller.signal
|
|
111
|
+
});
|
|
112
|
+
clearTimeout(timeoutId);
|
|
113
|
+
if (response.ok) {
|
|
114
|
+
if (retries > 0) {
|
|
115
|
+
logger.info(`Webhook inviato con successo dopo ${retries} retry`);
|
|
116
|
+
}
|
|
117
|
+
return { success: true, statusCode: response.status, retries };
|
|
118
|
+
}
|
|
119
|
+
if (response.status === 429) {
|
|
120
|
+
if (retries >= MAX_RETRIES) {
|
|
121
|
+
logger.warn(`Rate limited (429) dopo ${MAX_RETRIES} retry, rinuncio`);
|
|
122
|
+
return { success: false, statusCode: 429, retries, error: "Rate limited: superato il numero massimo di retry" };
|
|
123
|
+
}
|
|
124
|
+
const retryAfter = response.headers.get("Retry-After");
|
|
125
|
+
const waitMs = retryAfter ? Math.min(parseInt(retryAfter, 10) * 1e3, 3e4) : delayMs;
|
|
126
|
+
logger.warn(`Rate limited (429), retry ${retries + 1}/${MAX_RETRIES} tra ${waitMs}ms`);
|
|
127
|
+
await sleep(waitMs);
|
|
128
|
+
retries++;
|
|
129
|
+
delayMs *= 2;
|
|
130
|
+
continue;
|
|
131
|
+
}
|
|
132
|
+
const errorBody = await response.text().catch(() => "");
|
|
133
|
+
logger.error(`Webhook fallito con status ${response.status}: ${errorBody}`);
|
|
134
|
+
return {
|
|
135
|
+
success: false,
|
|
136
|
+
statusCode: response.status,
|
|
137
|
+
retries,
|
|
138
|
+
error: `HTTP ${response.status}: ${errorBody}`
|
|
139
|
+
};
|
|
140
|
+
} catch (err) {
|
|
141
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
142
|
+
logger.error(`Errore di rete nell'invio webhook: ${msg}`);
|
|
143
|
+
return { success: false, retries, error: `Errore di rete: ${msg}` };
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
return { success: false, retries, error: "Retry esauriti" };
|
|
147
|
+
}
|
|
148
|
+
function sleep(ms) {
|
|
149
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
150
|
+
}
|
|
151
|
+
function createSlackPlugin() {
|
|
152
|
+
let pluginConfig = null;
|
|
153
|
+
let pluginLogger = null;
|
|
154
|
+
const notifiedSessions = /* @__PURE__ */ new Set();
|
|
155
|
+
async function handleSessionEnd(session) {
|
|
156
|
+
if (!pluginConfig || !pluginLogger) {
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
if (notifiedSessions.has(session.id)) {
|
|
160
|
+
pluginLogger.info(`Sessione ${session.id} gi\xE0 notificata, skip`);
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
if (!pluginConfig.events?.includes("onSessionEnd")) {
|
|
164
|
+
pluginLogger.info("Hook onSessionEnd non abilitato nella configurazione");
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
const messageData = {
|
|
168
|
+
sessionId: session.id,
|
|
169
|
+
project: session.project,
|
|
170
|
+
summary: session.summary,
|
|
171
|
+
channel: pluginConfig.channel
|
|
172
|
+
};
|
|
173
|
+
const payload = buildSlackPayload(messageData);
|
|
174
|
+
const result = await sendWebhook(
|
|
175
|
+
pluginConfig.webhookUrl,
|
|
176
|
+
payload,
|
|
177
|
+
pluginLogger
|
|
178
|
+
);
|
|
179
|
+
if (result.success) {
|
|
180
|
+
notifiedSessions.add(session.id);
|
|
181
|
+
pluginLogger.info(`Notifica Slack inviata per sessione ${session.id}`);
|
|
182
|
+
} else {
|
|
183
|
+
pluginLogger.error(`Notifica Slack fallita per sessione ${session.id}: ${result.error}`);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
const plugin = {
|
|
187
|
+
name: "kiro-memory-plugin-slack",
|
|
188
|
+
version: "1.0.0",
|
|
189
|
+
description: "Notifiche Slack per sessioni Kiro Memory",
|
|
190
|
+
minKiroVersion: "2.0.0",
|
|
191
|
+
async init(context) {
|
|
192
|
+
pluginLogger = context.logger;
|
|
193
|
+
pluginConfig = validateConfig(context.config);
|
|
194
|
+
pluginLogger.info(`Inizializzato \u2014 webhook configurato, eventi: [${pluginConfig.events?.join(", ")}]`);
|
|
195
|
+
},
|
|
196
|
+
async destroy() {
|
|
197
|
+
notifiedSessions.clear();
|
|
198
|
+
pluginConfig = null;
|
|
199
|
+
pluginLogger?.info("Plugin Slack distrutto");
|
|
200
|
+
pluginLogger = null;
|
|
201
|
+
},
|
|
202
|
+
hooks: {
|
|
203
|
+
onSessionEnd: handleSessionEnd
|
|
204
|
+
}
|
|
205
|
+
};
|
|
206
|
+
return plugin;
|
|
207
|
+
}
|
|
208
|
+
var index_default = createSlackPlugin;
|
|
209
|
+
export {
|
|
210
|
+
createSlackPlugin,
|
|
211
|
+
index_default as default,
|
|
212
|
+
sendWebhook,
|
|
213
|
+
sleep,
|
|
214
|
+
validateConfig
|
|
215
|
+
};
|