feat: add all command handlers with inline keyboards

This commit is contained in:
neken
2026-04-10 15:12:45 +05:00
parent 79bcf2ff72
commit 4b67fdf885
+175
View File
@@ -0,0 +1,175 @@
import { type Context, InlineKeyboard } from "grammy";
import { SessionManager } from "../services/session-manager.js";
import { ClaudeService } from "../services/claude.js";
export function registerCommands(
manager: SessionManager,
claude: ClaudeService,
onCommand: Record<string, (ctx: Context) => Promise<void>>,
): void {
onCommand["start"] = async (ctx) => {
const arg = ctx.match as string;
if (!arg) {
await ctx.reply("Usage: /start <path or project name>");
return;
}
const resolved = manager.resolveProjectPath(arg);
if (!resolved) {
await ctx.reply("Could not resolve project path.");
return;
}
if (manager.getAllSessions()[resolved.name]) {
await ctx.reply(
`Session "${resolved.name}" already exists. Use /switch ${resolved.name}`,
);
return;
}
await ctx.reply(`Starting session "${resolved.name}" in ${resolved.path}...`);
try {
const { sessionId, result } = await claude.createSession(
"You are now connected via Telegram. Briefly confirm you're ready and state the project directory.",
resolved.path,
);
manager.createSession(resolved.name, resolved.path, sessionId);
await ctx.reply(`Session "${resolved.name}" created.\n\n${result}`);
} catch (err) {
await ctx.reply(`Failed to create session: ${err}`);
}
};
onCommand["stop"] = async (ctx) => {
const arg = ctx.match as string;
const name = arg || manager.activeSession;
if (!name) {
await ctx.reply("No active session. Nothing to stop.");
return;
}
if (manager.stopSession(name)) {
const active = manager.activeSession;
let msg = `Session "${name}" stopped.`;
if (active) msg += ` Switched to "${active}".`;
await ctx.reply(msg);
} else {
await ctx.reply(`Session "${name}" not found.`);
}
};
onCommand["stopall"] = async (ctx) => {
const count = manager.stopAll();
await ctx.reply(`Stopped ${count} session(s).`);
};
onCommand["switch"] = async (ctx) => {
const arg = ctx.match as string;
if (!arg) {
await ctx.reply("Usage: /switch <session name>");
return;
}
if (manager.switchSession(arg)) {
await ctx.reply(`Switched to "${arg}".`);
} else {
await ctx.reply(`Session "${arg}" not found. Use /sessions to see active sessions.`);
}
};
onCommand["sessions"] = async (ctx) => {
const sessions = manager.getAllSessions();
const names = Object.keys(sessions);
if (names.length === 0) {
await ctx.reply("No active sessions.");
return;
}
const active = manager.activeSession;
const lines = names.map((name) => {
const s = sessions[name];
const marker = name === active ? "►" : " ";
return `${marker} ${name}${s.cwd} (${s.status})`;
});
const keyboard = new InlineKeyboard();
for (const name of names) {
keyboard.text(name, `switch:${name}`);
}
await ctx.reply(lines.join("\n"), { reply_markup: keyboard });
};
onCommand["status"] = async (ctx) => {
const info = manager.activeSessionInfo;
if (!info) {
await ctx.reply("No active session.");
return;
}
const name = manager.activeSession!;
await ctx.reply(
`Session: ${name}\nPath: ${info.cwd}\nStatus: ${info.status}`,
);
};
onCommand["save"] = async (ctx) => {
const arg = ctx.match as string;
const parts = arg.split(/\s+/, 2);
if (parts.length < 2) {
await ctx.reply("Usage: /save <name> <path>");
return;
}
manager.saveProject(parts[0], parts[1]);
await ctx.reply(`Project "${parts[0]}" saved → ${parts[1]}`);
};
onCommand["remove"] = async (ctx) => {
const arg = ctx.match as string;
if (!arg) {
await ctx.reply("Usage: /remove <name>");
return;
}
if (manager.removeProject(arg)) {
await ctx.reply(`Project "${arg}" removed.`);
} else {
await ctx.reply(`Project "${arg}" not found.`);
}
};
onCommand["projects"] = async (ctx) => {
const projects = manager.getProjects();
const names = Object.keys(projects);
if (names.length === 0) {
await ctx.reply("No saved projects.");
return;
}
const lines = names.map((n) => `${n}${projects[n]}`);
const keyboard = new InlineKeyboard();
for (const name of names) {
keyboard.text(`Start ${name}`, `start:${name}`);
}
await ctx.reply(lines.join("\n"), { reply_markup: keyboard });
};
onCommand["help"] = async (ctx) => {
await ctx.reply(
[
"Session commands:",
" /start <path|name> — start new session",
" /stop [name] — stop session",
" /stopall — stop all sessions",
" /switch <name> — switch active session",
" /sessions — list sessions",
" /status — current session info",
"",
"Project bookmarks:",
" /save <name> <path> — save bookmark",
" /remove <name> — remove bookmark",
" /projects — list bookmarks",
"",
"Any other text is sent to the active session.",
].join("\n"),
);
};
}