command hq: delete a commander, from the grid card and the detail bar
DELETE /command-hq/commanders/:sessionId — deletes the transcript itself via deleteClaudeSession (the same function DELETE /chat/sessions/:id calls, so a /clear chain goes with it, every part), then the command_hq_sessions row via deleteCommandHqSession (existing, exported, never previously called from a route — its docstring said as much: "no product path to this yet"). Deliberately real deletion, not an unfile-back-to-/chat path — there still isn't one of those, and this doesn't create one. Documented next to "The /chat <-> /command-hq relationship" so it doesn't read as contradicting the "a session never moves the other direction" rule directly above it. Frontend: both the grid card and the detail bar's meta row get an inline confirm (trash icon -> check/cancel), same shape as ChatHistory's own SessionRow delete. The grid card's delete control is a sibling of the card's <Link>, not nested inside it, per docs/navigation-audit.md — the card is a stretched full-cover Link (absolute inset-0, default stacking) with the delete control in its own stacking context (z-10) on top of it, so the card stays click-anywhere except where the trash icon sits. Deleting the commander currently open in the detail panel navigates back to the bare list rather than leaving it pointed at a 404. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -10,6 +10,7 @@ import {
|
||||
listClaudeSessions,
|
||||
loadClaudeSessionById,
|
||||
renameClaudeSession,
|
||||
deleteClaudeSession,
|
||||
type ChatIdentity,
|
||||
type ClaudeSessionSummary,
|
||||
} from '../chat/claude-sessions';
|
||||
@@ -24,6 +25,7 @@ import {
|
||||
getCommandHqSession,
|
||||
setCommandHqSessionCategory,
|
||||
purgeCommandHqSessions,
|
||||
deleteCommandHqSession,
|
||||
disarmCommandHqWatcher,
|
||||
getCommandHqCostSummary,
|
||||
listCommandHqTurns,
|
||||
@@ -446,6 +448,34 @@ commandHqRouter.patch('/commanders/:sessionId', async (ctx) => {
|
||||
return ctx.json({ ok: true });
|
||||
});
|
||||
|
||||
/**
|
||||
* DELETE /command-hq/commanders/:sessionId
|
||||
*
|
||||
* Deletes the commander outright: the transcript itself (via `deleteClaudeSession`, the same function
|
||||
* `DELETE /chat/sessions/:id` calls — a `/clear` chain goes with it, every part), then the
|
||||
* `command_hq_sessions` row. Unlike everywhere else in this file, this is NOT the "row disagrees with
|
||||
* disk, so hide it" pattern `purgeCommandHqSessions` exists for — it is the one place that makes the two
|
||||
* agree by deleting the thing itself, on purpose, because the owner asked to.
|
||||
*
|
||||
* A watcher still armed at the time of deletion is left as-is rather than disarmed first: the transcript
|
||||
* is gone either way, `armCommandHqWatcher`/`recordCommandHqWatcherFired` can no longer find a row to
|
||||
* update once this returns, and the running background process (if any) dies on its own the same way it
|
||||
* would if the harness process were killed out from under any other conversation — not a new failure
|
||||
* mode this route has to account for.
|
||||
*/
|
||||
commandHqRouter.delete('/commanders/:sessionId', async (ctx) => {
|
||||
const user = ctx.get('user');
|
||||
const who = await commandHqIdentity(user);
|
||||
const sessionId = ctx.req.param('sessionId');
|
||||
|
||||
const row = await getCommandHqSession(user.id, sessionId);
|
||||
if (!row) throw errors.NOT_FOUND('not a commander');
|
||||
|
||||
deleteClaudeSession(who, getCommandersDir(who.email), sessionId);
|
||||
await deleteCommandHqSession(user.id, sessionId);
|
||||
return ctx.json({ ok: true });
|
||||
});
|
||||
|
||||
/**
|
||||
* POST /command-hq/purge
|
||||
*
|
||||
|
||||
Reference in New Issue
Block a user