@saccolabs/tars 1.0.7

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 (99) hide show
  1. package/README.md +93 -0
  2. package/context/GEMINI.md +13 -0
  3. package/context/config/settings.json-template +36 -0
  4. package/context/skills/create-extension/SKILL.md +90 -0
  5. package/context/skills/create-skill/SKILL.md +33 -0
  6. package/context/skills/manage-extensions/SKILL.md +69 -0
  7. package/context/skills/tars-ops/SKILL.md +89 -0
  8. package/dist/cli/commands/discord.d.ts +4 -0
  9. package/dist/cli/commands/discord.js +48 -0
  10. package/dist/cli/commands/discord.js.map +1 -0
  11. package/dist/cli/commands/export.d.ts +3 -0
  12. package/dist/cli/commands/export.js +39 -0
  13. package/dist/cli/commands/export.js.map +1 -0
  14. package/dist/cli/commands/import.d.ts +1 -0
  15. package/dist/cli/commands/import.js +45 -0
  16. package/dist/cli/commands/import.js.map +1 -0
  17. package/dist/cli/commands/logs.d.ts +4 -0
  18. package/dist/cli/commands/logs.js +23 -0
  19. package/dist/cli/commands/logs.js.map +1 -0
  20. package/dist/cli/commands/memory.d.ts +1 -0
  21. package/dist/cli/commands/memory.js +35 -0
  22. package/dist/cli/commands/memory.js.map +1 -0
  23. package/dist/cli/commands/secret.d.ts +6 -0
  24. package/dist/cli/commands/secret.js +46 -0
  25. package/dist/cli/commands/secret.js.map +1 -0
  26. package/dist/cli/commands/setup.d.ts +4 -0
  27. package/dist/cli/commands/setup.js +329 -0
  28. package/dist/cli/commands/setup.js.map +1 -0
  29. package/dist/cli/commands/start.d.ts +1 -0
  30. package/dist/cli/commands/start.js +42 -0
  31. package/dist/cli/commands/start.js.map +1 -0
  32. package/dist/cli/commands/status.d.ts +1 -0
  33. package/dist/cli/commands/status.js +56 -0
  34. package/dist/cli/commands/status.js.map +1 -0
  35. package/dist/cli/commands/stop.d.ts +1 -0
  36. package/dist/cli/commands/stop.js +38 -0
  37. package/dist/cli/commands/stop.js.map +1 -0
  38. package/dist/cli/commands/uninstall.d.ts +1 -0
  39. package/dist/cli/commands/uninstall.js +91 -0
  40. package/dist/cli/commands/uninstall.js.map +1 -0
  41. package/dist/cli/index.d.ts +2 -0
  42. package/dist/cli/index.js +54 -0
  43. package/dist/cli/index.js.map +1 -0
  44. package/dist/config/config.d.ts +14 -0
  45. package/dist/config/config.js +69 -0
  46. package/dist/config/config.js.map +1 -0
  47. package/dist/discord/discord-bot.d.ts +32 -0
  48. package/dist/discord/discord-bot.js +151 -0
  49. package/dist/discord/discord-bot.js.map +1 -0
  50. package/dist/discord/message-formatter.d.ts +95 -0
  51. package/dist/discord/message-formatter.js +448 -0
  52. package/dist/discord/message-formatter.js.map +1 -0
  53. package/dist/memory/knowledge-store.d.ts +24 -0
  54. package/dist/memory/knowledge-store.js +126 -0
  55. package/dist/memory/knowledge-store.js.map +1 -0
  56. package/dist/memory/memory-manager.d.ts +24 -0
  57. package/dist/memory/memory-manager.js +101 -0
  58. package/dist/memory/memory-manager.js.map +1 -0
  59. package/dist/scripts/debug-cli.d.ts +1 -0
  60. package/dist/scripts/debug-cli.js +52 -0
  61. package/dist/scripts/debug-cli.js.map +1 -0
  62. package/dist/supervisor/gemini-cli.d.ts +28 -0
  63. package/dist/supervisor/gemini-cli.js +315 -0
  64. package/dist/supervisor/gemini-cli.js.map +1 -0
  65. package/dist/supervisor/heartbeat-service.d.ts +21 -0
  66. package/dist/supervisor/heartbeat-service.js +143 -0
  67. package/dist/supervisor/heartbeat-service.js.map +1 -0
  68. package/dist/supervisor/main.d.ts +1 -0
  69. package/dist/supervisor/main.js +242 -0
  70. package/dist/supervisor/main.js.map +1 -0
  71. package/dist/supervisor/session-manager.d.ts +47 -0
  72. package/dist/supervisor/session-manager.js +118 -0
  73. package/dist/supervisor/session-manager.js.map +1 -0
  74. package/dist/supervisor/supervisor.d.ts +32 -0
  75. package/dist/supervisor/supervisor.js +98 -0
  76. package/dist/supervisor/supervisor.js.map +1 -0
  77. package/dist/types/index.d.ts +42 -0
  78. package/dist/types/index.js +5 -0
  79. package/dist/types/index.js.map +1 -0
  80. package/dist/utils/attachment-processor.d.ts +22 -0
  81. package/dist/utils/attachment-processor.js +79 -0
  82. package/dist/utils/attachment-processor.js.map +1 -0
  83. package/dist/utils/logger.d.ts +6 -0
  84. package/dist/utils/logger.js +15 -0
  85. package/dist/utils/logger.js.map +1 -0
  86. package/dist/utils/secrets-manager.d.ts +27 -0
  87. package/dist/utils/secrets-manager.js +79 -0
  88. package/dist/utils/secrets-manager.js.map +1 -0
  89. package/dist/utils/version.d.ts +3 -0
  90. package/dist/utils/version.js +23 -0
  91. package/dist/utils/version.js.map +1 -0
  92. package/extensions/tasks/gemini-extension.json +14 -0
  93. package/extensions/tasks/package-lock.json +1209 -0
  94. package/extensions/tasks/package.json +19 -0
  95. package/extensions/tasks/src/server.ts +265 -0
  96. package/extensions/tasks/src/store.ts +92 -0
  97. package/extensions/tasks/tsconfig.json +14 -0
  98. package/package.json +55 -0
  99. package/src/prompts/system.md +25 -0
@@ -0,0 +1,448 @@
1
+ /**
2
+ * Discord Message Formatter
3
+ *
4
+ * Transforms Gemini CLI output (GitHub Flavored Markdown)
5
+ * into Discord-compatible formatting.
6
+ *
7
+ * Discord supports:
8
+ * - Bold: **text**
9
+ * - Italic: *text* or _text_
10
+ * - Underline: __text__
11
+ * - Strikethrough: ~~text~~
12
+ * - Code inline: `text`
13
+ * - Code block: ```lang\ncode\n```
14
+ * - Blockquotes: > text
15
+ * - Headers: # (only #, ##, ###)
16
+ *
17
+ * Discord does NOT support:
18
+ * - Markdown tables (we instruct the LLM to avoid these)
19
+ * - #### or deeper headers
20
+ * - Small text (-#)
21
+ */
22
+ export class MessageFormatter {
23
+ static MAX_MESSAGE_LENGTH = 1990;
24
+ /**
25
+ * Format text for Discord
26
+ */
27
+ static format(text) {
28
+ if (!text)
29
+ return text;
30
+ let formatted = text;
31
+ // Step 1: Fix broken asterisks FIRST (creates patterns that need spacing)
32
+ formatted = this.fixAsterisks(formatted);
33
+ // Step 2: CRITICAL - Fix spacing issues (LLM often merges words)
34
+ formatted = this.fixSpacing(formatted);
35
+ // Step 3: Normalize bullets (* item -> • item)
36
+ formatted = this.normalizeBullets(formatted);
37
+ // Step 4: Normalize headers (#### -> ### or bold)
38
+ formatted = this.normalizeHeaders(formatted);
39
+ // Step 5: Fix blockquotes (ensure they render properly)
40
+ formatted = this.fixBlockquotes(formatted);
41
+ // Step 6: Format JSON blocks in code blocks
42
+ formatted = this.formatJsonBlocks(formatted);
43
+ // Step 7: Strip any remaining tables (fallback if LLM still generates them)
44
+ formatted = this.stripTables(formatted);
45
+ // Step 8: Fix small text markers (-#) that Discord doesn't support
46
+ formatted = this.fixSmallText(formatted);
47
+ // Step 9: Clean up excessive whitespace
48
+ formatted = formatted.replace(/\n{3,}/g, '\n\n');
49
+ return formatted.trim();
50
+ }
51
+ /**
52
+ * Fix critical spacing issues from LLM output
53
+ * These are the most common formatting bugs that break Discord rendering
54
+ */
55
+ static fixSpacing(text) {
56
+ let result = text;
57
+ // 1. Ensure space AFTER closing bold before next word
58
+ result = result.replace(/\*\*([^*]+)\*\*(?=[a-zA-Z0-9])/g, '**$1** ');
59
+ // 2. Ensure space BEFORE opening bold after a word
60
+ result = result.replace(/([a-zA-Z0-9])\*\*([^*]+)\*\*/g, '$1 **$2**');
61
+ // 3. Ensure space after colon before bold
62
+ result = result.replace(/([^*]):\*\*/g, '$1: **');
63
+ // 4. Ensure space around middle dots
64
+ result = result.replace(/([a-zA-Z0-9])·([a-zA-Z0-9])/g, '$1 · $2');
65
+ // 5. Ensure DOUBLE NEWLINE before section emojis
66
+ // This ensures headers like "📊 Signal Check" always start on a new paragraph
67
+ // REMOVED: ✅, ❌, ⚠️, 💡, 🛡️ (often used in lists/inline)
68
+ // KEPT: 🔍, 📊, 🧠, 📈, ⚖️, 🎯, 📋, 🔄, 📅 (Major section headers)
69
+ const sectionEmojis = '🔍|📊|🧠|📈|⚖️|🎯|📋|🔄|📅';
70
+ // Look for [not newline][spaces?][likely emoji] -> replace with [char]\n\n[emoji]
71
+ result = result.replace(new RegExp(`([^\\n])\\s*(${sectionEmojis})`, 'g'), '$1\n\n$2');
72
+ // 6. Ensure space after emojis before bold
73
+ result = result.replace(new RegExp(`(${sectionEmojis})\\*\\*`, 'g'), '$1 **');
74
+ // 7. Fix "set for**time**" pattern
75
+ result = result.replace(/for\*\*/g, 'for **');
76
+ // 8. Fix bullet points that run together
77
+ // "• Item1• Item2" -> "• Item1\n• Item2"
78
+ // Also ensure bullets appearing in middle of text get a newline
79
+ result = result.replace(/([^\n])\s*• /g, '$1\n• ');
80
+ return result;
81
+ }
82
+ /**
83
+ * Fix broken asterisks patterns
84
+ * Only fixes obviously broken patterns, avoids aggressive matching
85
+ */
86
+ static fixAsterisks(text) {
87
+ let result = text;
88
+ // Fix 4+ asterisks down to 2 (****text**** -> **text**)
89
+ result = result.replace(/\*{4,}(.+?)\*{4,}/g, '**$1**');
90
+ // Fix trailing-only ** at end of line (text** -> **text**)
91
+ // Only applies when line starts with a letter/number (no leading **)
92
+ // AND has exactly trailing ** (indicative of broken bold)
93
+ result = result.replace(/^([^*\n][^*]*)(\*\*)$/gm, (match, content, trail) => {
94
+ // Check if content already has opening ** - skip if so
95
+ if (content.includes('**'))
96
+ return match;
97
+ return `**${content.trim()}**`;
98
+ });
99
+ // Fix: Close unclosed bold tags at start of line (**Text -> **Text**)
100
+ result = result.replace(/^(\*\*[^*]+?)(?<!\*\*)(\s*[:.,!?-]?)$/gm, (match, content, punct) => {
101
+ // If already closed, don't touch
102
+ if (content.trim().endsWith('**'))
103
+ return match;
104
+ return `${content.trim()}${punct}**`;
105
+ });
106
+ // Also handle cases specifically ending with punctuation on the line
107
+ result = result.replace(/^(\*\*[^*]+)(?=\s*([:.,!?-]))/gm, (match, content, punct) => {
108
+ if (match.endsWith('**'))
109
+ return match;
110
+ // Regex lookahead logic is tricky here, simplifiying:
111
+ // The previous regex fixes the EOL case.
112
+ // This regex handles mid-line punctuation if we want to support it?
113
+ // Actually, the previous regex handles `punctuation at the end of the line OR content`.
114
+ // If we have `**Header: content`, the first regex won't catch it unless it matches newline?
115
+ // Let's rely on the first regex which is robust for EOL.
116
+ // For mid-line, if there's no closing `**`, it's hard to guess where it ends.
117
+ // So we'll skip this second aggressive block to avoid regressions.
118
+ return match;
119
+ });
120
+ return result;
121
+ }
122
+ /**
123
+ * Normalize bullet points for Discord
124
+ */
125
+ static normalizeBullets(text) {
126
+ return (text
127
+ // Convert "* item" to "• item" (but not inside code blocks)
128
+ .replace(/^(\s*)\* /gm, '$1• ')
129
+ // Convert "- item" that's not a separator line or subtext
130
+ .replace(/^(\s*)- (?![-#]+)/gm, '$1• '));
131
+ }
132
+ /**
133
+ * Normalize markdown headers to Discord-friendly format
134
+ * Discord supports #, ##, ### natively now.
135
+ */
136
+ static normalizeHeaders(text) {
137
+ return (text
138
+ // Ensure space after hashes
139
+ .replace(/^(#{1,3})(?=[^#\s])/gm, '$1 ')
140
+ // Convert #### and deeper to bold
141
+ .replace(/^#{4,}\s+(.+)$/gm, '**$1**'));
142
+ }
143
+ /**
144
+ * Fix blockquote formatting
145
+ */
146
+ static fixBlockquotes(text) {
147
+ const lines = text.split('\n');
148
+ const result = [];
149
+ let inBlockquote = false;
150
+ for (const line of lines) {
151
+ if (line.startsWith('> ')) {
152
+ inBlockquote = true;
153
+ result.push(line);
154
+ }
155
+ else if (line.startsWith('>') && line.length === 1) {
156
+ // Empty blockquote line - skip it
157
+ continue;
158
+ }
159
+ else if (inBlockquote && line.trim() === '') {
160
+ inBlockquote = false;
161
+ result.push(line);
162
+ }
163
+ else {
164
+ inBlockquote = false;
165
+ result.push(line);
166
+ }
167
+ }
168
+ return result.join('\n');
169
+ }
170
+ /**
171
+ * Detect and wrap JSON-like content in code blocks
172
+ */
173
+ static formatJsonBlocks(text) {
174
+ if (text.includes('```json') || text.includes('```\n{')) {
175
+ return text;
176
+ }
177
+ const jsonPattern = /(?:^|\n)([\[{][\s\S]*?[\]}])(?=\n|$)/g;
178
+ return text.replace(jsonPattern, (match, json) => {
179
+ try {
180
+ const trimmed = json.trim();
181
+ if ((trimmed.startsWith('{') || trimmed.startsWith('[')) && trimmed.length > 20) {
182
+ const parsed = JSON.parse(trimmed);
183
+ const pretty = JSON.stringify(parsed, null, 2);
184
+ return '\n```json\n' + pretty + '\n```';
185
+ }
186
+ }
187
+ catch {
188
+ // Not valid JSON
189
+ }
190
+ return match;
191
+ });
192
+ }
193
+ /**
194
+ * Strip markdown tables - they don't render well on mobile Discord
195
+ * This is a fallback; the LLM should be instructed not to generate tables
196
+ */
197
+ static stripTables(text) {
198
+ // Detect table patterns and convert to simple list
199
+ const lines = text.split('\n');
200
+ const result = [];
201
+ let inTable = false;
202
+ let tableRows = [];
203
+ for (const line of lines) {
204
+ // Check if this is a table separator line (|---|---|)
205
+ if (line.match(/^\|?\s*[-:]+[-:\s|]*\|?\s*$/)) {
206
+ inTable = true;
207
+ continue;
208
+ }
209
+ // Check if this is a table row
210
+ if (line.includes('|') && (line.startsWith('|') || line.match(/\w+\s*\|/))) {
211
+ inTable = true;
212
+ const cells = line
213
+ .split('|')
214
+ .map((c) => c.trim())
215
+ .filter((c) => c.length > 0);
216
+ if (cells.length > 0) {
217
+ tableRows.push(cells);
218
+ }
219
+ continue;
220
+ }
221
+ // End of table - convert to list
222
+ if (inTable && tableRows.length > 0) {
223
+ // First row is usually header
224
+ const header = tableRows[0];
225
+ const dataRows = tableRows.slice(1);
226
+ for (const row of dataRows) {
227
+ const parts = [];
228
+ for (let i = 0; i < row.length && i < header.length; i++) {
229
+ if (i === 0) {
230
+ parts.push(`**${row[i]}**`);
231
+ }
232
+ else {
233
+ parts.push(row[i]);
234
+ }
235
+ }
236
+ result.push(`• ${parts.join(' · ')}`);
237
+ }
238
+ tableRows = [];
239
+ inTable = false;
240
+ }
241
+ result.push(line);
242
+ }
243
+ // Handle any remaining table at end of text
244
+ if (tableRows.length > 0) {
245
+ const header = tableRows[0];
246
+ const dataRows = tableRows.slice(1);
247
+ for (const row of dataRows) {
248
+ const parts = [];
249
+ for (let i = 0; i < row.length && i < header.length; i++) {
250
+ if (i === 0) {
251
+ parts.push(`**${row[i]}**`);
252
+ }
253
+ else {
254
+ parts.push(row[i]);
255
+ }
256
+ }
257
+ result.push(`• ${parts.join(' · ')}`);
258
+ }
259
+ }
260
+ return result.join('\n');
261
+ }
262
+ /**
263
+ * Fix small text markers (-#) that Discord doesn't support
264
+ */
265
+ static fixSmallText(text) {
266
+ // Convert -# prefix to regular text or remove it
267
+ return text.replace(/^-#\s*/gm, '');
268
+ }
269
+ /**
270
+ * Split long messages into Discord-safe chunks intelligently
271
+ * Respects semantic boundaries: headers, code blocks, paragraphs
272
+ */
273
+ static split(text, maxLength = this.MAX_MESSAGE_LENGTH) {
274
+ if (text.length <= maxLength)
275
+ return [text];
276
+ const chunks = [];
277
+ let remaining = text;
278
+ while (remaining.length > 0) {
279
+ if (remaining.length <= maxLength) {
280
+ chunks.push(remaining);
281
+ break;
282
+ }
283
+ // Find the best semantic split point
284
+ const splitIndex = this.findSemanticSplitPoint(remaining, maxLength);
285
+ chunks.push(remaining.substring(0, splitIndex).trim());
286
+ remaining = remaining.substring(splitIndex).trim();
287
+ }
288
+ return chunks;
289
+ }
290
+ /**
291
+ * Find the optimal split point respecting semantic boundaries
292
+ * Priority: Header > Code block boundary > Paragraph > Sentence > Hard cut
293
+ */
294
+ static findSemanticSplitPoint(text, maxLength) {
295
+ // 1. Try to split BEFORE a markdown header (## or ###)
296
+ const headerPattern = /\n(##+ )/g;
297
+ let match;
298
+ let lastHeaderBefore = -1;
299
+ while ((match = headerPattern.exec(text)) !== null) {
300
+ const pos = match.index;
301
+ if (pos > maxLength)
302
+ break;
303
+ if (pos > maxLength / 2) {
304
+ lastHeaderBefore = pos;
305
+ break;
306
+ }
307
+ }
308
+ if (lastHeaderBefore > maxLength / 2) {
309
+ return lastHeaderBefore;
310
+ }
311
+ // 2. Try to split AFTER a complete code block
312
+ const codeBlockEndPattern = /```\n/g;
313
+ let lastCodeEnd = -1;
314
+ while ((match = codeBlockEndPattern.exec(text)) !== null) {
315
+ const pos = match.index + match[0].length;
316
+ if (pos > maxLength)
317
+ break;
318
+ if (pos > maxLength / 2) {
319
+ lastCodeEnd = pos;
320
+ break;
321
+ }
322
+ }
323
+ if (lastCodeEnd > maxLength / 2) {
324
+ return lastCodeEnd;
325
+ }
326
+ // 3. Try to split at paragraph boundary (double newline)
327
+ let splitIndex = text.lastIndexOf('\n\n', maxLength);
328
+ if (splitIndex > maxLength / 2) {
329
+ return splitIndex + 2;
330
+ }
331
+ // 4. Try to split at single newline
332
+ splitIndex = text.lastIndexOf('\n', maxLength);
333
+ if (splitIndex > maxLength / 3) {
334
+ return splitIndex + 1;
335
+ }
336
+ // 5. Try to split at sentence boundary (. followed by space)
337
+ splitIndex = text.lastIndexOf('. ', maxLength);
338
+ if (splitIndex > maxLength / 3) {
339
+ return splitIndex + 2;
340
+ }
341
+ // 6. Last resort: hard cut at maxLength
342
+ return maxLength;
343
+ }
344
+ /**
345
+ * Format and split in one operation
346
+ * Ensures summary line (first line with actionable emoji) stays at the top
347
+ */
348
+ static formatAndSplit(text) {
349
+ const formatted = this.format(text);
350
+ // If text fits in one message, no special handling needed
351
+ if (formatted.length <= this.MAX_MESSAGE_LENGTH) {
352
+ return [formatted];
353
+ }
354
+ // Extract summary line (first line starting with key emoji)
355
+ const { summary, rest } = this.extractSummaryLine(formatted);
356
+ // Split the remaining content
357
+ const chunks = this.split(rest);
358
+ // If we found a summary, prepend it to the first chunk
359
+ if (summary && chunks.length > 0) {
360
+ // Check if adding summary would exceed limit
361
+ const firstChunkWithSummary = `${summary}\n\n${chunks[0]}`;
362
+ if (firstChunkWithSummary.length <= this.MAX_MESSAGE_LENGTH) {
363
+ chunks[0] = firstChunkWithSummary;
364
+ }
365
+ else {
366
+ // Summary + first chunk too long, send summary as its own message
367
+ chunks.unshift(summary);
368
+ }
369
+ }
370
+ return chunks;
371
+ }
372
+ /**
373
+ * Extract the summary line from the beginning of formatted text
374
+ * Summary lines start with key actionable emojis: 🎯 ⚖️ 📊 ⚠️ ✅ ❓
375
+ */
376
+ static extractSummaryLine(text) {
377
+ const lines = text.split('\n');
378
+ const summaryEmojis = ['🎯', '⚖️', '📊', '⚠️', '✅', '❓', '❌'];
379
+ // Check first few lines for summary pattern
380
+ for (let i = 0; i < Math.min(3, lines.length); i++) {
381
+ const line = lines[i].trim();
382
+ // Check if line starts with a summary emoji
383
+ if (summaryEmojis.some((emoji) => line.startsWith(emoji))) {
384
+ // Found summary line - extract it and return rest
385
+ const summary = line;
386
+ const restLines = [...lines.slice(0, i), ...lines.slice(i + 1)];
387
+ const rest = restLines.join('\n').trim();
388
+ return { summary, rest };
389
+ }
390
+ // Also check for bold emoji pattern: **🎯 or similar
391
+ if (line.match(/^\*\*[🎯⚖️📊⚠️✅❓❌]/)) {
392
+ const summary = line;
393
+ const restLines = [...lines.slice(0, i), ...lines.slice(i + 1)];
394
+ const rest = restLines.join('\n').trim();
395
+ return { summary, rest };
396
+ }
397
+ }
398
+ return { summary: null, rest: text };
399
+ }
400
+ /**
401
+ * Parse markdown into sections based on headers (##)
402
+ */
403
+ static parseSections(text) {
404
+ const sections = [];
405
+ const lines = text.split('\n');
406
+ let currentTitle = 'Summary';
407
+ let currentContent = [];
408
+ for (const line of lines) {
409
+ const headerMatch = line.match(/^##\s+(.+)$/);
410
+ if (headerMatch) {
411
+ if (currentContent.length > 0 || currentTitle !== 'Summary') {
412
+ sections.push({
413
+ title: currentTitle,
414
+ content: currentContent.join('\n').trim()
415
+ });
416
+ }
417
+ currentTitle = headerMatch[1];
418
+ currentContent = [];
419
+ }
420
+ else {
421
+ currentContent.push(line);
422
+ }
423
+ }
424
+ if (currentContent.length > 0 || sections.length === 0) {
425
+ sections.push({
426
+ title: currentTitle,
427
+ content: currentContent.join('\n').trim()
428
+ });
429
+ }
430
+ return sections;
431
+ }
432
+ /**
433
+ * Format a data object as a clean Discord-friendly list
434
+ */
435
+ static formatDataAsEmbed(title, data) {
436
+ const lines = [`**${title}**`];
437
+ for (const [key, value] of Object.entries(data)) {
438
+ if (typeof value === 'object') {
439
+ lines.push(`• **${key}:** \`${JSON.stringify(value)}\``);
440
+ }
441
+ else {
442
+ lines.push(`• **${key}:** ${value}`);
443
+ }
444
+ }
445
+ return lines.join('\n');
446
+ }
447
+ }
448
+ //# sourceMappingURL=message-formatter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"message-formatter.js","sourceRoot":"","sources":["../../src/discord/message-formatter.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,24 @@
1
+ import { Config } from '../config/config.js';
2
+ export interface MemoryResult {
3
+ path: string;
4
+ content: string;
5
+ score: number;
6
+ startLine: number;
7
+ }
8
+ /**
9
+ * KnowledgeStore - Local memory using SQLite FTS5.
10
+ * Uses a classic keyword inverted index approach for high-speed, authless search.
11
+ */
12
+ export declare class KnowledgeStore {
13
+ private db;
14
+ constructor(config: Config);
15
+ private initialize;
16
+ /**
17
+ * Add or update a file in the knowledge base.
18
+ */
19
+ indexFile(filePath: string, content: string): Promise<void>;
20
+ /**
21
+ * Search for relevant knowledge using keyword match (BM25 ranking).
22
+ */
23
+ search(query: string, limit?: number): Promise<MemoryResult[]>;
24
+ }
@@ -0,0 +1,126 @@
1
+ import { DatabaseSync } from 'node:sqlite';
2
+ import path from 'path';
3
+ import fs from 'fs';
4
+ import logger from '../utils/logger.js';
5
+ import crypto from 'crypto';
6
+ /**
7
+ * KnowledgeStore - Local memory using SQLite FTS5.
8
+ * Uses a classic keyword inverted index approach for high-speed, authless search.
9
+ */
10
+ export class KnowledgeStore {
11
+ db;
12
+ constructor(config) {
13
+ const dbPath = path.join(config.homeDir, 'data', 'knowledge.db');
14
+ const dbDir = path.dirname(dbPath);
15
+ if (!fs.existsSync(dbDir)) {
16
+ fs.mkdirSync(dbDir, { recursive: true });
17
+ }
18
+ // FTS5 is built-in to modern SQLite, no need for allowExtension
19
+ this.db = new DatabaseSync(dbPath);
20
+ this.initialize();
21
+ }
22
+ initialize() {
23
+ try {
24
+ // Initialize Tables
25
+ this.db.exec(`
26
+ CREATE TABLE IF NOT EXISTS files (
27
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
28
+ path TEXT UNIQUE,
29
+ hash TEXT,
30
+ updated_at INTEGER
31
+ );
32
+
33
+ CREATE TABLE IF NOT EXISTS chunks (
34
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
35
+ file_id INTEGER,
36
+ content TEXT,
37
+ start_line INTEGER,
38
+ end_line INTEGER,
39
+ FOREIGN KEY(file_id) REFERENCES files(id) ON DELETE CASCADE
40
+ );
41
+
42
+ -- Initialize FTS5 Search Table (Internal Storage)
43
+ CREATE VIRTUAL TABLE IF NOT EXISTS chunks_fts USING fts5(
44
+ content
45
+ );
46
+
47
+ -- Triggers to keep FTS in sync with chunks table
48
+ CREATE TRIGGER IF NOT EXISTS chunks_ai AFTER INSERT ON chunks BEGIN
49
+ INSERT INTO chunks_fts(rowid, content) VALUES (new.id, new.content);
50
+ END;
51
+ CREATE TRIGGER IF NOT EXISTS chunks_ad AFTER DELETE ON chunks BEGIN
52
+ DELETE FROM chunks_fts WHERE rowid = old.id;
53
+ END;
54
+ CREATE TRIGGER IF NOT EXISTS chunks_au AFTER UPDATE ON chunks BEGIN
55
+ DELETE FROM chunks_fts WHERE rowid = old.id;
56
+ INSERT INTO chunks_fts(rowid, content) VALUES (new.id, new.content);
57
+ END;
58
+ `);
59
+ logger.info('🧠 KnowledgeStore: Local keyword index initialized');
60
+ }
61
+ catch (error) {
62
+ logger.error(`❌ KnowledgeStore init failed: ${error.message}`);
63
+ throw error;
64
+ }
65
+ }
66
+ /**
67
+ * Add or update a file in the knowledge base.
68
+ */
69
+ async indexFile(filePath, content) {
70
+ const hash = crypto.createHash('sha256').update(content).digest('hex');
71
+ // 1. Check if file has changed
72
+ const existing = this.db
73
+ .prepare('SELECT id, hash FROM files WHERE path = ?')
74
+ .get(filePath);
75
+ if (existing && existing.hash === hash) {
76
+ return; // No changes
77
+ }
78
+ logger.info(`📝 Indexing: ${filePath}`);
79
+ // 2. Clear old file data (Cascade delete handles chunks and triggers handle FTS)
80
+ if (existing) {
81
+ this.db.prepare('DELETE FROM files WHERE id = ?').run(existing.id);
82
+ }
83
+ // 3. Insert File Record
84
+ const fileResult = this.db
85
+ .prepare('INSERT INTO files (path, hash, updated_at) VALUES (?, ?, ?)')
86
+ .run(filePath, hash, Date.now());
87
+ const fileId = fileResult.lastInsertRowid;
88
+ // 4. Chunk Content (Simple paragraph split)
89
+ const paragraphs = content.split(/\n\s*\n/).filter((p) => p.trim().length > 20);
90
+ // 5. Store Chunks (FTS is updated automatically via triggers)
91
+ for (const p of paragraphs) {
92
+ this.db
93
+ .prepare('INSERT INTO chunks (file_id, content, start_line) VALUES (?, ?, ?)')
94
+ .run(fileId, p, 0);
95
+ }
96
+ }
97
+ /**
98
+ * Search for relevant knowledge using keyword match (BM25 ranking).
99
+ */
100
+ async search(query, limit = 5) {
101
+ try {
102
+ const results = this.db
103
+ .prepare(`
104
+ SELECT f.path, c.content, c.start_line, rank as score
105
+ FROM chunks_fts fts
106
+ JOIN chunks c ON fts.rowid = c.id
107
+ JOIN files f ON c.file_id = f.id
108
+ WHERE chunks_fts MATCH ?
109
+ ORDER BY rank
110
+ LIMIT ?
111
+ `)
112
+ .all(query, limit);
113
+ return results.map((r) => ({
114
+ path: r.path,
115
+ content: r.content,
116
+ startLine: r.start_line,
117
+ score: 1 / (1 + Math.abs(r.score)) // Normalize FTS rank to 0-1 range
118
+ }));
119
+ }
120
+ catch (err) {
121
+ logger.warn(`⚠️ Search failed: ${query}`);
122
+ return [];
123
+ }
124
+ }
125
+ }
126
+ //# sourceMappingURL=knowledge-store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"knowledge-store.js","sourceRoot":"","sources":["../../src/memory/knowledge-store.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,24 @@
1
+ import { Config } from '../config/config.js';
2
+ /**
3
+ * MemoryManager - High-level interface for Tars' memory systems.
4
+ * Manages the transition from flat GEMINI.md to indexed storage.
5
+ */
6
+ export declare class MemoryManager {
7
+ private knowledgeStore;
8
+ private config;
9
+ constructor(config: Config);
10
+ /**
11
+ * Initial sync of the "Brain" into the knowledge store.
12
+ * This includes GEMINI.md, skills, and session transcripts.
13
+ */
14
+ fullSync(): Promise<void>;
15
+ /**
16
+ * Finds and indexes past session conversations for episodic memory.
17
+ */
18
+ private syncSessions;
19
+ private syncDir;
20
+ /**
21
+ * Search memory for relevant snippets.
22
+ */
23
+ search(query: string, limit?: number): Promise<import("./knowledge-store.js").MemoryResult[]>;
24
+ }