@locusai/locus-telegram 0.21.9 → 0.21.11

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/src/ui/format.ts DELETED
@@ -1,130 +0,0 @@
1
- /**
2
- * Output formatting utilities for Telegram messages.
3
- *
4
- * Telegram supports a subset of Markdown (MarkdownV2) and HTML.
5
- * We use HTML mode for reliability — fewer escaping issues than MarkdownV2.
6
- */
7
-
8
- /** Maximum Telegram message length (4096 chars). */
9
- const MAX_MESSAGE_LENGTH = 4096;
10
-
11
- /** Maximum length for a code block inside a message. */
12
- const MAX_CODE_LENGTH = 3800;
13
-
14
- /** Escape HTML special characters for Telegram HTML mode. */
15
- export function escapeHtml(text: string): string {
16
- return text
17
- .replace(/&/g, "&")
18
- .replace(/</g, "&lt;")
19
- .replace(/>/g, "&gt;");
20
- }
21
-
22
- /** Wrap text in a code block. */
23
- export function codeBlock(text: string, language = ""): string {
24
- const truncated = truncate(text, MAX_CODE_LENGTH);
25
- return `<pre><code${language ? ` class="language-${language}"` : ""}>${escapeHtml(truncated)}</code></pre>`;
26
- }
27
-
28
- /** Wrap text in inline code. */
29
- export function inlineCode(text: string): string {
30
- return `<code>${escapeHtml(text)}</code>`;
31
- }
32
-
33
- /** Bold text. */
34
- export function bold(text: string): string {
35
- return `<b>${text}</b>`;
36
- }
37
-
38
- /** Italic text. */
39
- export function italic(text: string): string {
40
- return `<i>${text}</i>`;
41
- }
42
-
43
- /** Truncate text to a max length, appending "..." if truncated. */
44
- export function truncate(text: string, maxLength = MAX_MESSAGE_LENGTH): string {
45
- if (text.length <= maxLength) return text;
46
- return `${text.slice(0, maxLength - 20)}\n\n... (truncated)`;
47
- }
48
-
49
- /** Split long output into multiple messages if needed. */
50
- export function splitMessage(text: string): string[] {
51
- if (text.length <= MAX_MESSAGE_LENGTH) return [text];
52
-
53
- const chunks: string[] = [];
54
- let remaining = text;
55
-
56
- while (remaining.length > 0) {
57
- if (remaining.length <= MAX_MESSAGE_LENGTH) {
58
- chunks.push(remaining);
59
- break;
60
- }
61
-
62
- // Try to split at a newline
63
- let splitIdx = remaining.lastIndexOf("\n", MAX_MESSAGE_LENGTH);
64
- if (splitIdx === -1 || splitIdx < MAX_MESSAGE_LENGTH / 2) {
65
- splitIdx = MAX_MESSAGE_LENGTH;
66
- }
67
-
68
- chunks.push(remaining.slice(0, splitIdx));
69
- remaining = remaining.slice(splitIdx);
70
- }
71
-
72
- return chunks;
73
- }
74
-
75
- /** Format a command result as a Telegram message. */
76
- export function formatCommandResult(
77
- command: string,
78
- output: string,
79
- exitCode: number
80
- ): string {
81
- const status = exitCode === 0 ? "✅" : "❌";
82
- const header = `${status} ${bold(escapeHtml(command))}`;
83
-
84
- if (!output.trim()) {
85
- return exitCode === 0
86
- ? `${header}\n\nCompleted successfully.`
87
- : `${header}\n\nFailed with exit code ${exitCode}.`;
88
- }
89
-
90
- return `${header}\n\n${codeBlock(output.trim())}`;
91
- }
92
-
93
- /** Format a streaming progress message. */
94
- export function formatStreamingMessage(
95
- command: string,
96
- output: string,
97
- isComplete: boolean
98
- ): string {
99
- const status = isComplete ? "✅" : "⏳";
100
- const header = `${status} ${bold(escapeHtml(command))}`;
101
-
102
- if (!output.trim()) {
103
- return `${header}\n\n${italic("Running...")}`;
104
- }
105
-
106
- // Take last N lines for streaming display
107
- const lines = output.trim().split("\n");
108
- const lastLines = lines.slice(-30).join("\n");
109
-
110
- return `${header}\n\n${codeBlock(lastLines)}`;
111
- }
112
-
113
- /** Format an error message. */
114
- export function formatError(message: string, detail?: string): string {
115
- let text = `❌ ${bold("Error")}\n\n${escapeHtml(message)}`;
116
- if (detail) {
117
- text += `\n\n${codeBlock(detail)}`;
118
- }
119
- return text;
120
- }
121
-
122
- /** Format a success message. */
123
- export function formatSuccess(message: string): string {
124
- return `✅ ${escapeHtml(message)}`;
125
- }
126
-
127
- /** Format an info message. */
128
- export function formatInfo(message: string): string {
129
- return `ℹ️ ${escapeHtml(message)}`;
130
- }
@@ -1,83 +0,0 @@
1
- /**
2
- * Inline keyboard builders for interactive Telegram responses.
3
- *
4
- * Keyboards are attached to messages to let users take actions
5
- * without typing commands manually.
6
- */
7
-
8
- import { InlineKeyboard } from "grammy";
9
-
10
- // ─── Callback Data Prefixes ─────────────────────────────────────────────────
11
-
12
- export const CB = {
13
- APPROVE_PLAN: "plan:approve",
14
- REJECT_PLAN: "plan:reject",
15
- SHOW_PLAN_DETAILS: "plan:details",
16
- CONFIRM_ACTION: "confirm:yes",
17
- CANCEL_ACTION: "confirm:no",
18
- VIEW_PR: "pr:view:",
19
- VIEW_LOGS: "logs:view",
20
- RUN_AGAIN: "run:again:",
21
- APPROVE_REVIEW: "review:approve:",
22
- REQUEST_CHANGES: "review:changes:",
23
- VIEW_DIFF: "review:diff:",
24
- RUN_SPRINT: "sprint:run",
25
- VIEW_ISSUES: "issues:view",
26
- STASH_POP: "stash:pop",
27
- STASH_LIST: "stash:list",
28
- STASH_DROP: "stash:drop",
29
- } as const;
30
-
31
- // ─── Keyboard Builders ──────────────────────────────────────────────────────
32
-
33
- /** Keyboard shown after plan creation. */
34
- export function planKeyboard(): InlineKeyboard {
35
- return new InlineKeyboard()
36
- .text("✅ Approve Plan", CB.APPROVE_PLAN)
37
- .text("❌ Reject Plan", CB.REJECT_PLAN)
38
- .row()
39
- .text("📋 Show Details", CB.SHOW_PLAN_DETAILS);
40
- }
41
-
42
- /** Keyboard shown after a run completes. */
43
- export function runCompleteKeyboard(issueNumber?: number): InlineKeyboard {
44
- const kb = new InlineKeyboard();
45
- kb.text("📄 View Logs", CB.VIEW_LOGS);
46
- if (issueNumber) {
47
- kb.text("🔄 Run Again", `${CB.RUN_AGAIN}${issueNumber}`);
48
- }
49
- return kb;
50
- }
51
-
52
- /** Keyboard shown after review. */
53
- export function reviewKeyboard(prNumber: number): InlineKeyboard {
54
- return new InlineKeyboard()
55
- .text("✅ Approve", `${CB.APPROVE_REVIEW}${prNumber}`)
56
- .text("🔄 Request Changes", `${CB.REQUEST_CHANGES}${prNumber}`)
57
- .row()
58
- .text("📝 View Diff", `${CB.VIEW_DIFF}${prNumber}`);
59
- }
60
-
61
- /** Confirmation keyboard for destructive operations. */
62
- export function confirmKeyboard(): InlineKeyboard {
63
- return new InlineKeyboard()
64
- .text("✅ Confirm", CB.CONFIRM_ACTION)
65
- .text("❌ Cancel", CB.CANCEL_ACTION);
66
- }
67
-
68
- /** Keyboard shown after status check. */
69
- export function statusKeyboard(): InlineKeyboard {
70
- return new InlineKeyboard()
71
- .text("🚀 Run Sprint", CB.RUN_SPRINT)
72
- .text("📋 Issues", CB.VIEW_ISSUES)
73
- .row()
74
- .text("📄 Logs", CB.VIEW_LOGS);
75
- }
76
-
77
- /** Keyboard for stash operations. */
78
- export function stashKeyboard(): InlineKeyboard {
79
- return new InlineKeyboard()
80
- .text("📤 Pop", CB.STASH_POP)
81
- .text("📋 List", CB.STASH_LIST)
82
- .text("🗑 Drop", CB.STASH_DROP);
83
- }
@@ -1,189 +0,0 @@
1
- /**
2
- * Predefined response message templates for common bot interactions.
3
- *
4
- * All messages use HTML formatting for Telegram.
5
- */
6
-
7
- import { bold, codeBlock, escapeHtml, italic } from "./format.js";
8
-
9
- // ─── Welcome & Help ─────────────────────────────────────────────────────────
10
-
11
- export function welcomeMessage(): string {
12
- return [
13
- `${bold("🤖 Locus Telegram Bot")}`,
14
- "",
15
- "Control your Locus agent directly from Telegram.",
16
- "",
17
- `${bold("Locus Commands")}`,
18
- "/run [issue#...] — Execute issues",
19
- "/status — Dashboard view",
20
- "/issues [filters] — List issues",
21
- "/issue &lt;#&gt; — Show issue details",
22
- "/sprint [sub] — Sprint management",
23
- "/plan [args] — AI planning",
24
- "/review &lt;pr#&gt; — Code review",
25
- "/iterate &lt;pr#&gt; — Re-execute with feedback",
26
- "/discuss [topic] — AI discussion",
27
- "/exec [prompt] — REPL / one-shot",
28
- "/logs — View logs",
29
- "/config [path] — View config",
30
- "/artifacts — View artifacts",
31
- "",
32
- `${bold("Git Commands")}`,
33
- "/gitstatus — Git status",
34
- "/stage [files|.] — Stage files",
35
- "/commit &lt;message&gt; — Commit changes",
36
- "/stash [pop|list|drop] — Stash operations",
37
- "/branch [name] — List/create branches",
38
- "/checkout &lt;branch&gt; — Switch branch",
39
- "/diff — Show diff",
40
- "/pr &lt;title&gt; — Create pull request",
41
- "",
42
- `${bold("Service Commands")}`,
43
- "/service start|stop|restart|status|logs",
44
- "",
45
- "/help — Show this message",
46
- ].join("\n");
47
- }
48
-
49
- // ─── Plan Messages ──────────────────────────────────────────────────────────
50
-
51
- export function planCreatedMessage(planOutput: string): string {
52
- return [
53
- `📋 ${bold("Plan Created")}`,
54
- "",
55
- codeBlock(planOutput),
56
- "",
57
- italic("Use the buttons below to approve or reject the plan."),
58
- ].join("\n");
59
- }
60
-
61
- export function planApprovedMessage(): string {
62
- return `✅ ${bold("Plan Approved")} — Proceeding with execution.`;
63
- }
64
-
65
- export function planRejectedMessage(): string {
66
- return `❌ ${bold("Plan Rejected")} — No changes will be made.`;
67
- }
68
-
69
- // ─── Run Messages ───────────────────────────────────────────────────────────
70
-
71
- export function runStartedMessage(target: string): string {
72
- return `🚀 ${bold("Run Started")} — ${escapeHtml(target)}`;
73
- }
74
-
75
- export function runCompletedMessage(target: string, prNumber?: number): string {
76
- const lines = [`✅ ${bold("Run Completed")} — ${escapeHtml(target)}`];
77
- if (prNumber) {
78
- lines.push("", `Pull Request: #${prNumber}`);
79
- }
80
- return lines.join("\n");
81
- }
82
-
83
- export function runFailedMessage(target: string, error: string): string {
84
- return [
85
- `❌ ${bold("Run Failed")} — ${escapeHtml(target)}`,
86
- "",
87
- codeBlock(error),
88
- ].join("\n");
89
- }
90
-
91
- // ─── Review Messages ────────────────────────────────────────────────────────
92
-
93
- export function reviewStartedMessage(prNumber: number): string {
94
- return `🔍 ${bold("Reviewing")} PR #${prNumber}...`;
95
- }
96
-
97
- export function reviewCompletedMessage(
98
- prNumber: number,
99
- output: string
100
- ): string {
101
- return [
102
- `✅ ${bold("Review Complete")} — PR #${prNumber}`,
103
- "",
104
- codeBlock(output),
105
- ].join("\n");
106
- }
107
-
108
- // ─── Git Messages ───────────────────────────────────────────────────────────
109
-
110
- export function gitCommitMessage(message: string, hash: string): string {
111
- return [
112
- `✅ ${bold("Committed")}`,
113
- "",
114
- `Message: ${escapeHtml(message)}`,
115
- `Hash: ${escapeHtml(hash)}`,
116
- ].join("\n");
117
- }
118
-
119
- export function gitBranchCreatedMessage(name: string): string {
120
- return `✅ Branch ${bold(escapeHtml(name))} created.`;
121
- }
122
-
123
- export function gitCheckoutMessage(branch: string): string {
124
- return `✅ Switched to branch ${bold(escapeHtml(branch))}.`;
125
- }
126
-
127
- export function gitStashMessage(action: string): string {
128
- return `✅ ${bold("Stash")} — ${escapeHtml(action)} completed.`;
129
- }
130
-
131
- export function prCreatedMessage(prNumber: number, url: string): string {
132
- return [
133
- `✅ ${bold("Pull Request Created")}`,
134
- "",
135
- `PR #${prNumber}: ${escapeHtml(url)}`,
136
- ].join("\n");
137
- }
138
-
139
- // ─── Service Messages ───────────────────────────────────────────────────────
140
-
141
- export function serviceStatusMessage(
142
- status: string,
143
- pid: number | null,
144
- uptime: number | null,
145
- memory: number | null,
146
- restarts: number
147
- ): string {
148
- const uptimeStr = uptime ? formatUptime(uptime) : "N/A";
149
- const memoryStr = memory ? formatMemory(memory) : "N/A";
150
-
151
- return [
152
- `🤖 ${bold("Locus Telegram Bot")}`,
153
- "",
154
- `Status: ${bold(status)}`,
155
- `PID: ${pid ?? "N/A"}`,
156
- `Uptime: ${uptimeStr}`,
157
- `Memory: ${memoryStr}`,
158
- `Restarts: ${restarts}`,
159
- ].join("\n");
160
- }
161
-
162
- export function serviceNotRunningMessage(): string {
163
- return [
164
- `⚠️ ${bold("Bot Not Running")}`,
165
- "",
166
- "Start the bot with:",
167
- `${bold("locus pkg telegram start")}`,
168
- ].join("\n");
169
- }
170
-
171
- // ─── Helpers ────────────────────────────────────────────────────────────────
172
-
173
- function formatUptime(startTimeMs: number): string {
174
- const diff = Date.now() - startTimeMs;
175
- const seconds = Math.floor(diff / 1000);
176
- const minutes = Math.floor(seconds / 60);
177
- const hours = Math.floor(minutes / 60);
178
- const days = Math.floor(hours / 24);
179
-
180
- if (days > 0) return `${days}d ${hours % 24}h`;
181
- if (hours > 0) return `${hours}h ${minutes % 60}m`;
182
- if (minutes > 0) return `${minutes}m ${seconds % 60}s`;
183
- return `${seconds}s`;
184
- }
185
-
186
- function formatMemory(bytes: number): string {
187
- const mb = bytes / (1024 * 1024);
188
- return `${mb.toFixed(1)} MB`;
189
- }
package/tsconfig.json DELETED
@@ -1,17 +0,0 @@
1
- {
2
- "compilerOptions": {
3
- "target": "ES2022",
4
- "module": "ESNext",
5
- "moduleResolution": "bundler",
6
- "strict": true,
7
- "skipLibCheck": true,
8
- "esModuleInterop": true,
9
- "isolatedModules": true,
10
- "resolveJsonModule": true,
11
- "declaration": true,
12
- "outDir": "./dist",
13
- "rootDir": "./src"
14
- },
15
- "include": ["src/**/*"],
16
- "exclude": ["node_modules", "dist"]
17
- }