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.
Files changed (33) hide show
  1. package/README.md +5 -1
  2. package/package.json +3 -3
  3. package/plugin/dist/cli/contextkit.js +2342 -183
  4. package/plugin/dist/hooks/agentSpawn.js +575 -52
  5. package/plugin/dist/hooks/kiro-hooks.js +575 -52
  6. package/plugin/dist/hooks/postToolUse.js +583 -59
  7. package/plugin/dist/hooks/stop.js +575 -52
  8. package/plugin/dist/hooks/userPromptSubmit.js +578 -53
  9. package/plugin/dist/index.js +576 -53
  10. package/plugin/dist/plugins/github/github-client.js +152 -0
  11. package/plugin/dist/plugins/github/index.js +412 -0
  12. package/plugin/dist/plugins/github/issue-parser.js +54 -0
  13. package/plugin/dist/plugins/slack/formatter.js +90 -0
  14. package/plugin/dist/plugins/slack/index.js +215 -0
  15. package/plugin/dist/sdk/index.js +575 -52
  16. package/plugin/dist/servers/mcp-server.js +4461 -397
  17. package/plugin/dist/services/search/EmbeddingService.js +64 -20
  18. package/plugin/dist/services/search/HybridSearch.js +380 -38
  19. package/plugin/dist/services/search/VectorSearch.js +65 -21
  20. package/plugin/dist/services/search/index.js +380 -38
  21. package/plugin/dist/services/sqlite/Backup.js +416 -0
  22. package/plugin/dist/services/sqlite/Database.js +98 -3
  23. package/plugin/dist/services/sqlite/ImportExport.js +452 -0
  24. package/plugin/dist/services/sqlite/Observations.js +291 -7
  25. package/plugin/dist/services/sqlite/Prompts.js +1 -1
  26. package/plugin/dist/services/sqlite/Search.js +10 -10
  27. package/plugin/dist/services/sqlite/Summaries.js +4 -4
  28. package/plugin/dist/services/sqlite/index.js +1350 -31
  29. package/plugin/dist/viewer.css +1 -1
  30. package/plugin/dist/viewer.js +16 -8
  31. package/plugin/dist/viewer.js.map +4 -4
  32. package/plugin/dist/worker-service.js +326 -75
  33. 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
+ };