@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,91 @@
1
+ import pm2 from 'pm2';
2
+ import chalk from 'chalk';
3
+ import inquirer from 'inquirer';
4
+ import fs from 'fs/promises';
5
+ import { existsSync } from 'fs';
6
+ import os from 'os';
7
+ import path from 'path';
8
+ import { execSync } from 'child_process';
9
+ import ora from 'ora';
10
+ export async function uninstall() {
11
+ console.log(chalk.red.bold('\n⚠️ DANGER ZONE: Uninstall Tars ⚠️\n'));
12
+ console.log(chalk.white('This action will:'));
13
+ console.log(chalk.red(' 1. Stop and remove the Tars background supervisor'));
14
+ console.log(chalk.red(' 2. PERMANENTLY DELETE ~/.tars (Your Brain, Memories, and Data)'));
15
+ console.log(chalk.red(' 3. Remove all configuration and logs\n'));
16
+ const { confirm } = await inquirer.prompt([
17
+ {
18
+ type: 'confirm',
19
+ name: 'confirm',
20
+ message: 'Are you absolutely sure you want to proceed?',
21
+ default: false
22
+ }
23
+ ]);
24
+ if (!confirm) {
25
+ console.log(chalk.cyan('\nUninstall cancelled.'));
26
+ return;
27
+ }
28
+ const { finalConfirm } = await inquirer.prompt([
29
+ {
30
+ type: 'input',
31
+ name: 'finalConfirm',
32
+ message: 'Type "delete" to confirm complete removal:',
33
+ validate: (input) => (input === 'delete' ? true : 'You must type "delete" to confirm.')
34
+ }
35
+ ]);
36
+ if (finalConfirm !== 'delete') {
37
+ console.log(chalk.cyan('\nUninstall cancelled.'));
38
+ return;
39
+ }
40
+ console.log('\nStarting uninstallation...\n');
41
+ // 1. Stop Tars
42
+ const stopSpinner = ora('Stopping Tars services...').start();
43
+ await new Promise((resolve) => {
44
+ pm2.connect((err) => {
45
+ if (err) {
46
+ // If PM2 fails, we just try to kill manually
47
+ forceKill();
48
+ resolve();
49
+ return;
50
+ }
51
+ pm2.delete('tars-supervisor', (delErr) => {
52
+ pm2.disconnect();
53
+ // Even if delete fails (e.g. not running), force kill
54
+ forceKill();
55
+ resolve();
56
+ });
57
+ });
58
+ });
59
+ stopSpinner.succeed('Tars services stopped.');
60
+ // 2. Remove ~/.tars
61
+ const cleanSpinner = ora('Removing ~/.tars directory...').start();
62
+ const tarsHome = process.env.TARS_HOME || path.join(process.env.REAL_HOME || os.homedir(), '.tars');
63
+ if (existsSync(tarsHome)) {
64
+ try {
65
+ await fs.rm(tarsHome, { recursive: true, force: true });
66
+ cleanSpinner.succeed('Data directory (~/.tars) permanently removed.');
67
+ }
68
+ catch (error) {
69
+ cleanSpinner.fail(`Failed to remove ~/.tars: ${error.message}`);
70
+ console.log(chalk.yellow('\nYou may need to manually remove the directory using: rm -rf ~/.tars'));
71
+ }
72
+ }
73
+ else {
74
+ cleanSpinner.info('~/.tars directory not found (already clean).');
75
+ }
76
+ // 3. Final message
77
+ console.log(chalk.green.bold('\n✅ Tars has been scrubbed from this system.'));
78
+ console.log(chalk.white('\nTo complete the removal, uninstall the CLI package:'));
79
+ console.log(chalk.cyan(' npm uninstall -g @saccolabs/tars'));
80
+ console.log(chalk.dim('\nGoodbye! 👋'));
81
+ process.exit(0);
82
+ }
83
+ function forceKill() {
84
+ try {
85
+ execSync('pkill -9 -f "supervisor/main.js" || true', { stdio: 'ignore' });
86
+ }
87
+ catch (e) {
88
+ // Ignore
89
+ }
90
+ }
91
+ //# sourceMappingURL=uninstall.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"uninstall.js","sourceRoot":"","sources":["../../../src/cli/commands/uninstall.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
@@ -0,0 +1,54 @@
1
+ #!/usr/bin/env node
2
+ import { Command } from 'commander';
3
+ import { setup } from './commands/setup.js';
4
+ import { start } from './commands/start.js';
5
+ import { stop } from './commands/stop.js';
6
+ import { status } from './commands/status.js';
7
+ import { exportBrain } from './commands/export.js';
8
+ import { importBrain } from './commands/import.js';
9
+ import { logs } from './commands/logs.js';
10
+ import { discord } from './commands/discord.js';
11
+ import { secret } from './commands/secret.js';
12
+ import { memory } from './commands/memory.js';
13
+ import { uninstall } from './commands/uninstall.js';
14
+ import { versionString } from '../utils/version.js';
15
+ const program = new Command();
16
+ program.name('tars').description('Tars — Personal AI Assistant').version(versionString);
17
+ program
18
+ .command('setup')
19
+ .description('Interactive onboarding wizard to configure Tars')
20
+ .action(setup);
21
+ program.command('start').description('Start Tars supervisor in the background').action(start);
22
+ program.command('stop').description('Stop the Tars supervisor').action(stop);
23
+ program.command('status').description('Check the status of Tars supervisor').action(status);
24
+ program
25
+ .command('export')
26
+ .description('Export your brain (memories, tasks, extensions)')
27
+ .option('-o, --output <path>', 'Output path for the archive')
28
+ .action(exportBrain);
29
+ program
30
+ .command('import')
31
+ .description('Import a brain from an archive')
32
+ .argument('<path>', 'Path to the brain archive (.tar.gz)')
33
+ .action(importBrain);
34
+ program.command('logs').description('View real-time logs from the Tars supervisor').action(logs);
35
+ program
36
+ .command('discord')
37
+ .description('View instructions for Discord bot setup and invitation')
38
+ .action(discord);
39
+ program
40
+ .command('secret')
41
+ .description('Manage secure environment variables for extensions')
42
+ .argument('<action>', 'Action to perform (set, list, remove)')
43
+ .argument('[key]', 'Secret key')
44
+ .argument('[value]', 'Secret value (required for set)')
45
+ .action(secret);
46
+ program
47
+ .command('memory')
48
+ .description('Search or sync your brain knowledge')
49
+ .argument('<action>', 'Action to perform (search, sync)')
50
+ .argument('[query...]', 'Search query')
51
+ .action((action, queryArgs) => memory(action, ...queryArgs));
52
+ program.command('uninstall').description('Uninstall Tars and remove all data').action(uninstall);
53
+ program.parse();
54
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ export declare class Config {
2
+ private static instance;
3
+ readonly homeDir: string;
4
+ readonly taskFilePath: string;
5
+ readonly sessionFilePath: string;
6
+ readonly configFilePath: string;
7
+ readonly memoryDbPath: string;
8
+ readonly discordToken: string;
9
+ readonly geminiModel: string;
10
+ readonly heartbeatIntervalMs: number;
11
+ readonly systemPromptPath: string;
12
+ private constructor();
13
+ static getInstance(): Config;
14
+ }
@@ -0,0 +1,69 @@
1
+ import path from 'path';
2
+ import os from 'os';
3
+ import fs from 'fs';
4
+ import dotenv from 'dotenv';
5
+ import logger from '../utils/logger.js';
6
+ import { SecretsManager } from '../utils/secrets-manager.js';
7
+ dotenv.config();
8
+ export class Config {
9
+ static instance;
10
+ // Paths
11
+ homeDir;
12
+ taskFilePath;
13
+ sessionFilePath;
14
+ configFilePath;
15
+ memoryDbPath;
16
+ // Discord
17
+ discordToken;
18
+ // Gemini
19
+ geminiModel;
20
+ heartbeatIntervalMs;
21
+ // System Prompt
22
+ systemPromptPath;
23
+ constructor() {
24
+ // 1. Establish Home Directory (~/.tars)
25
+ // Hardcode the base to be the real user home to avoid recursion if HOME is changed for subprocesses
26
+ const realUserHome = process.env.REAL_HOME || os.homedir();
27
+ this.homeDir =
28
+ process.env.TARS_HOME || path.join(realUserHome.replace('/.tars', ''), '.tars');
29
+ this.configFilePath = path.join(this.homeDir, 'config.json');
30
+ // 1.5 Load Secrets into environment
31
+ const secretsManager = new SecretsManager(this.homeDir);
32
+ const secrets = secretsManager.load();
33
+ for (const [key, value] of Object.entries(secrets)) {
34
+ // Load into process.env so they are available globally
35
+ process.env[key] = value;
36
+ }
37
+ // 2. Load JSON Config if exists
38
+ let jsonConfig = {};
39
+ try {
40
+ if (fs.existsSync(this.configFilePath)) {
41
+ jsonConfig = JSON.parse(fs.readFileSync(this.configFilePath, 'utf-8'));
42
+ }
43
+ }
44
+ catch (error) {
45
+ logger.warn(`Could not read config file: ${this.configFilePath}`);
46
+ }
47
+ // 3. Set values (Env vars override JSON config)
48
+ this.discordToken = process.env.DISCORD_TOKEN || jsonConfig.discordToken || '';
49
+ this.geminiModel = process.env.GEMINI_MODEL || jsonConfig.geminiModel || 'auto';
50
+ const hbSec = process.env.HEARTBEAT_INTERVAL_SEC || jsonConfig.heartbeatIntervalSec || '300';
51
+ this.heartbeatIntervalMs = parseInt(String(hbSec), 10) * 1000;
52
+ // 4. Derived Paths
53
+ this.taskFilePath = path.join(this.homeDir, 'data', 'tasks.json');
54
+ this.sessionFilePath = path.join(this.homeDir, 'data', 'session.json');
55
+ this.systemPromptPath = path.join(this.homeDir, '.gemini', 'system.md');
56
+ this.memoryDbPath = path.join(this.homeDir, 'data', 'knowledge.db');
57
+ // Note: validation happens in specific services if needed, but we can do a basic check
58
+ if (!this.discordToken) {
59
+ logger.warn('⚠️ No Discord Token found. Please run `tars setup`.');
60
+ }
61
+ }
62
+ static getInstance() {
63
+ if (!Config.instance) {
64
+ Config.instance = new Config();
65
+ }
66
+ return Config.instance;
67
+ }
68
+ }
69
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/config/config.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,32 @@
1
+ import { Config } from '../config/config.js';
2
+ import { Supervisor } from '../supervisor/supervisor.js';
3
+ /**
4
+ * Discord bot wrapper for Tars
5
+ */
6
+ export declare class DiscordBot {
7
+ private readonly config;
8
+ private readonly client;
9
+ private readonly supervisor;
10
+ private readonly processor;
11
+ constructor(supervisor: Supervisor, config: Config);
12
+ /**
13
+ * Start the Discord bot
14
+ */
15
+ start(): Promise<void>;
16
+ /**
17
+ * Stop the Discord bot
18
+ */
19
+ stop(): Promise<void>;
20
+ /**
21
+ * Setup event handlers
22
+ */
23
+ private setupEventHandlers;
24
+ /**
25
+ * Handle incoming messages
26
+ */
27
+ private handleMessage;
28
+ /**
29
+ * Extract prompt and handle prefix !tars
30
+ */
31
+ private extractPrompt;
32
+ }
@@ -0,0 +1,151 @@
1
+ import { Client, GatewayIntentBits, ChannelType, Partials } from 'discord.js';
2
+ import logger from '../utils/logger.js';
3
+ import { MessageFormatter } from './message-formatter.js';
4
+ import { AttachmentProcessor } from '../utils/attachment-processor.js';
5
+ /**
6
+ * Discord bot wrapper for Tars
7
+ */
8
+ export class DiscordBot {
9
+ config;
10
+ client;
11
+ supervisor;
12
+ processor;
13
+ constructor(supervisor, config) {
14
+ this.config = config;
15
+ this.supervisor = supervisor;
16
+ this.processor = new AttachmentProcessor(config);
17
+ this.client = new Client({
18
+ intents: [
19
+ GatewayIntentBits.Guilds,
20
+ GatewayIntentBits.GuildMessages,
21
+ GatewayIntentBits.MessageContent,
22
+ GatewayIntentBits.DirectMessages
23
+ ],
24
+ partials: [Partials.Channel, Partials.Message]
25
+ });
26
+ this.setupEventHandlers();
27
+ }
28
+ /**
29
+ * Start the Discord bot
30
+ */
31
+ async start() {
32
+ await this.client.login(this.config.discordToken);
33
+ }
34
+ /**
35
+ * Stop the Discord bot
36
+ */
37
+ async stop() {
38
+ this.client.destroy();
39
+ }
40
+ /**
41
+ * Setup event handlers
42
+ */
43
+ setupEventHandlers() {
44
+ this.client.once('clientReady', (c) => {
45
+ logger.info(`🚀 Tars online as ${c.user.tag}`);
46
+ logger.info(`🧠 Gemini Model: ${this.config.geminiModel}`);
47
+ });
48
+ this.client.on('messageCreate', this.handleMessage.bind(this));
49
+ }
50
+ /**
51
+ * Handle incoming messages
52
+ */
53
+ async handleMessage(message) {
54
+ if (message.author.bot)
55
+ return;
56
+ const userPrompt = this.extractPrompt(message);
57
+ // If null, message wasn't for us. If empty string, check for attachments.
58
+ if (userPrompt === null)
59
+ return;
60
+ if (!userPrompt && message.attachments.size === 0)
61
+ return;
62
+ logger.info(`Received request from ${message.author.tag}: "${userPrompt || '[Attachment Only]'}"`);
63
+ // Handle Attachments
64
+ let attachmentContext = '';
65
+ if (message.attachments.size > 0) {
66
+ for (const [id, attachment] of message.attachments) {
67
+ try {
68
+ const filePath = await this.processor.download(attachment);
69
+ attachmentContext += `\n[User attached file (${attachment.contentType}): ${filePath}]`;
70
+ }
71
+ catch (err) {
72
+ logger.error(`Failed to download attachment: ${err.message}`);
73
+ await message.reply(`⚠️ Failed to download ${attachment.name}: ${err.message}`);
74
+ }
75
+ }
76
+ }
77
+ const fullPrompt = `${userPrompt}${attachmentContext}`.trim();
78
+ if (!fullPrompt)
79
+ return;
80
+ let typingInterval = null;
81
+ // Start typing indicator loop (Discord typing status lasts 10s)
82
+ if ('sendTyping' in message.channel) {
83
+ // Initial typing
84
+ await message.channel.sendTyping().catch(() => { });
85
+ // Loop every 9s to keep it active
86
+ typingInterval = setInterval(() => {
87
+ if ('sendTyping' in message.channel) {
88
+ message.channel.sendTyping().catch(() => { });
89
+ }
90
+ }, 9000);
91
+ }
92
+ try {
93
+ let fullResponse = '';
94
+ await this.supervisor.run(fullPrompt, async (event) => {
95
+ if ((event.type === 'text' || event.type === 'message') &&
96
+ event.content &&
97
+ event.role !== 'user') {
98
+ fullResponse += event.content;
99
+ }
100
+ else if (event.type === 'error') {
101
+ await message.reply(`❌ **Error:** ${event.error}`);
102
+ }
103
+ else if (event.type === 'done') {
104
+ if (fullResponse.trim()) {
105
+ const formatted = MessageFormatter.format(fullResponse);
106
+ if (formatted.length > 1900) {
107
+ const filePath = this.processor.saveResponse(fullResponse, 'md');
108
+ await message.reply({
109
+ content: `📄 **Response too long** (${formatted.length} chars). See attached file:`,
110
+ files: [filePath]
111
+ });
112
+ }
113
+ else {
114
+ const chunks = MessageFormatter.split(formatted);
115
+ for (const chunk of chunks) {
116
+ await message.reply(chunk);
117
+ }
118
+ }
119
+ }
120
+ }
121
+ });
122
+ }
123
+ catch (error) {
124
+ logger.error(`Discord handling error: ${error.message}`);
125
+ await message.reply(`❌ **Supervisor Error:** ${error.message}`);
126
+ }
127
+ finally {
128
+ if (typingInterval)
129
+ clearInterval(typingInterval);
130
+ }
131
+ }
132
+ /**
133
+ * Extract prompt and handle prefix !tars
134
+ */
135
+ extractPrompt(message) {
136
+ const isDM = message.channel.type === ChannelType.DM;
137
+ const isMentioned = this.client.user && message.mentions.has(this.client.user);
138
+ const hasCommand = message.content.startsWith('!tars');
139
+ if (!isDM && !isMentioned && !hasCommand)
140
+ return null;
141
+ let prompt = message.content;
142
+ if (hasCommand) {
143
+ prompt = prompt.replace('!tars', '');
144
+ }
145
+ if (isMentioned && this.client.user) {
146
+ prompt = prompt.replace(new RegExp(`<@!?${this.client.user.id}>`, 'g'), '');
147
+ }
148
+ return prompt.trim();
149
+ }
150
+ }
151
+ //# sourceMappingURL=discord-bot.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"discord-bot.js","sourceRoot":"","sources":["../../src/discord/discord-bot.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,95 @@
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 declare class MessageFormatter {
23
+ private static readonly MAX_MESSAGE_LENGTH;
24
+ /**
25
+ * Format text for Discord
26
+ */
27
+ static format(text: string): string;
28
+ /**
29
+ * Fix critical spacing issues from LLM output
30
+ * These are the most common formatting bugs that break Discord rendering
31
+ */
32
+ private static fixSpacing;
33
+ /**
34
+ * Fix broken asterisks patterns
35
+ * Only fixes obviously broken patterns, avoids aggressive matching
36
+ */
37
+ private static fixAsterisks;
38
+ /**
39
+ * Normalize bullet points for Discord
40
+ */
41
+ private static normalizeBullets;
42
+ /**
43
+ * Normalize markdown headers to Discord-friendly format
44
+ * Discord supports #, ##, ### natively now.
45
+ */
46
+ private static normalizeHeaders;
47
+ /**
48
+ * Fix blockquote formatting
49
+ */
50
+ private static fixBlockquotes;
51
+ /**
52
+ * Detect and wrap JSON-like content in code blocks
53
+ */
54
+ private static formatJsonBlocks;
55
+ /**
56
+ * Strip markdown tables - they don't render well on mobile Discord
57
+ * This is a fallback; the LLM should be instructed not to generate tables
58
+ */
59
+ private static stripTables;
60
+ /**
61
+ * Fix small text markers (-#) that Discord doesn't support
62
+ */
63
+ private static fixSmallText;
64
+ /**
65
+ * Split long messages into Discord-safe chunks intelligently
66
+ * Respects semantic boundaries: headers, code blocks, paragraphs
67
+ */
68
+ static split(text: string, maxLength?: number): string[];
69
+ /**
70
+ * Find the optimal split point respecting semantic boundaries
71
+ * Priority: Header > Code block boundary > Paragraph > Sentence > Hard cut
72
+ */
73
+ private static findSemanticSplitPoint;
74
+ /**
75
+ * Format and split in one operation
76
+ * Ensures summary line (first line with actionable emoji) stays at the top
77
+ */
78
+ static formatAndSplit(text: string): string[];
79
+ /**
80
+ * Extract the summary line from the beginning of formatted text
81
+ * Summary lines start with key actionable emojis: 🎯 ⚖️ 📊 ⚠️ ✅ ❓
82
+ */
83
+ private static extractSummaryLine;
84
+ /**
85
+ * Parse markdown into sections based on headers (##)
86
+ */
87
+ static parseSections(text: string): {
88
+ title: string;
89
+ content: string;
90
+ }[];
91
+ /**
92
+ * Format a data object as a clean Discord-friendly list
93
+ */
94
+ static formatDataAsEmbed(title: string, data: Record<string, unknown>): string;
95
+ }