feat: add all command handlers with inline keyboards
This commit is contained in:
@@ -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"),
|
||||
);
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user