letmecook 0.0.19 → 0.0.21

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/index.ts CHANGED
@@ -2,10 +2,17 @@
2
2
 
3
3
  import pkg from "./package.json";
4
4
  import { parseRepoSpec, type RepoSpec } from "./src/types";
5
- import { listSessions, getSession, updateLastAccessed, deleteAllSessions } from "./src/sessions";
5
+ import {
6
+ listSessions,
7
+ getSession,
8
+ updateLastAccessed,
9
+ deleteAllSessions,
10
+ deleteSession,
11
+ } from "./src/sessions";
6
12
  import { createRenderer, destroyRenderer } from "./src/ui/renderer";
7
13
  import { showNewSessionPrompt } from "./src/ui/new-session";
8
14
  import { showSessionList } from "./src/ui/list";
15
+ import { showNukeConfirm } from "./src/ui/confirm-nuke";
9
16
  import { createNewSession, resumeSession } from "./src/flows";
10
17
  import { handleTUIMode } from "./src/tui-mode";
11
18
 
@@ -19,8 +26,8 @@ Usage:
19
26
  letmecook <owner/repo> [owner/repo:branch...] Create or resume a session (CLI)
20
27
  letmecook --list List all sessions
21
28
  letmecook --resume <session-name> Resume a session
22
- letmecook --nuke <session-name> Delete a session
23
- letmecook --nuke-all Delete all sessions
29
+ letmecook --delete <session-name> Delete a session
30
+ letmecook --nuke [--yes] Nuke everything
24
31
  letmecook --why Show why this tool exists
25
32
  letmecook --help Show this help
26
33
  letmecook --version Show version
@@ -124,11 +131,16 @@ async function handleList(): Promise<void> {
124
131
  console.log("[TODO] Delete session flow");
125
132
  break;
126
133
 
127
- case "nuke-all":
128
- const count = await deleteAllSessions();
129
- destroyRenderer();
130
- console.log(`\nNuked ${count} session(s).`);
131
- return;
134
+ case "nuke": {
135
+ const choice = await showNukeConfirm(renderer, sessions.length);
136
+ if (choice === "confirm") {
137
+ const count = await deleteAllSessions();
138
+ destroyRenderer();
139
+ console.log(`\nNuked ${count} session(s) and all data.`);
140
+ return;
141
+ }
142
+ break;
143
+ }
132
144
 
133
145
  case "quit":
134
146
  destroyRenderer();
@@ -168,13 +180,52 @@ async function handleResume(sessionName: string): Promise<void> {
168
180
  });
169
181
  }
170
182
 
171
- async function handleNuke(_sessionName: string): Promise<void> {
172
- console.log("[TODO] Nuke session flow");
183
+ async function handleDelete(sessionName: string): Promise<void> {
184
+ const session = await getSession(sessionName);
185
+
186
+ if (!session) {
187
+ console.error(`Session not found: ${sessionName}`);
188
+ const sessions = await listSessions();
189
+ if (sessions.length > 0) {
190
+ console.log("\nAvailable sessions:");
191
+ sessions.forEach((s) => console.log(` - ${s.name}`));
192
+ }
193
+ process.exit(1);
194
+ }
195
+
196
+ const deleted = await deleteSession(sessionName);
197
+ if (deleted) {
198
+ console.log(`Deleted session: ${sessionName}`);
199
+ } else {
200
+ console.error(`Failed to delete session: ${sessionName}`);
201
+ process.exit(1);
202
+ }
173
203
  }
174
204
 
175
- async function handleNukeAll(): Promise<void> {
176
- const count = await deleteAllSessions();
177
- console.log(`Nuked ${count} session(s).`);
205
+ async function handleNuke(skipConfirm = false): Promise<void> {
206
+ const sessions = await listSessions();
207
+ if (sessions.length === 0) {
208
+ console.log("Nothing to nuke.");
209
+ return;
210
+ }
211
+
212
+ // Skip confirmation if --yes flag or non-interactive (piped input)
213
+ if (skipConfirm || !process.stdin.isTTY) {
214
+ const count = await deleteAllSessions();
215
+ console.log(`Nuked ${count} session(s) and all data.`);
216
+ return;
217
+ }
218
+
219
+ const renderer = await createRenderer();
220
+ const choice = await showNukeConfirm(renderer, sessions.length);
221
+ destroyRenderer();
222
+
223
+ if (choice === "confirm") {
224
+ const count = await deleteAllSessions();
225
+ console.log(`Nuked ${count} session(s) and all data.`);
226
+ } else {
227
+ console.log("Cancelled.");
228
+ }
178
229
  }
179
230
 
180
231
  async function handleCLIMode(args: string[]): Promise<void> {
@@ -189,15 +240,16 @@ async function handleCLIMode(args: string[]): Promise<void> {
189
240
  process.exit(1);
190
241
  }
191
242
  await handleResume(sessionName);
192
- } else if (firstArg === "--nuke") {
243
+ } else if (firstArg === "--delete" || firstArg === "-d") {
193
244
  const sessionName = args[1];
194
245
  if (!sessionName) {
195
- console.error("Missing session name. Usage: letmecook --nuke <session-name>");
246
+ console.error("Missing session name. Usage: letmecook --delete <session-name>");
196
247
  process.exit(1);
197
248
  }
198
- await handleNuke(sessionName);
199
- } else if (firstArg === "--nuke-all") {
200
- await handleNukeAll();
249
+ await handleDelete(sessionName);
250
+ } else if (firstArg === "--nuke") {
251
+ const hasYes = args.includes("--yes") || args.includes("-y");
252
+ await handleNuke(hasYes);
201
253
  } else if (firstArg?.startsWith("-")) {
202
254
  console.error(`Unknown option: ${firstArg}`);
203
255
  printUsage();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "letmecook",
3
- "version": "0.0.19",
3
+ "version": "0.0.21",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "https://github.com/rustydotwtf/letmecook.git"
package/src/sessions.ts CHANGED
@@ -191,7 +191,8 @@ export async function deleteAllSessions(): Promise<number> {
191
191
  const count = sessions.length;
192
192
 
193
193
  try {
194
- await rm(SESSIONS_DIR, { recursive: true, force: true });
194
+ // Nuke entire .letmecook directory including SQLite history
195
+ await rm(LETMECOOK_DIR, { recursive: true, force: true });
195
196
  return count;
196
197
  } catch {
197
198
  return 0;
package/src/tui-mode.ts CHANGED
@@ -9,10 +9,17 @@ import { showMainMenu } from "./ui/main-menu";
9
9
  import { showSessionDetails } from "./ui/session-details";
10
10
  import { showSessionSettings } from "./ui/session-settings";
11
11
  import { showDeleteConfirm } from "./ui/confirm-delete";
12
+ import { showNukeConfirm } from "./ui/confirm-nuke";
12
13
  import { showQuitWarning } from "./ui/background-warning";
13
14
  import type { Session } from "./types";
14
15
  import { createNewSession, resumeSession } from "./flows";
15
- import { listSessions, deleteSession, updateLastAccessed, updateSessionSettings } from "./sessions";
16
+ import {
17
+ listSessions,
18
+ deleteSession,
19
+ deleteAllSessions,
20
+ updateLastAccessed,
21
+ updateSessionSettings,
22
+ } from "./sessions";
16
23
  import { getRunningProcesses, killAllProcesses } from "./process-registry";
17
24
 
18
25
  export async function handleTUIMode(): Promise<void> {
@@ -41,6 +48,17 @@ export async function handleTUIMode(): Promise<void> {
41
48
  break;
42
49
  }
43
50
 
51
+ case "nuke": {
52
+ const choice = await showNukeConfirm(renderer, sessions.length);
53
+ if (choice === "confirm") {
54
+ const count = await deleteAllSessions();
55
+ destroyRenderer();
56
+ console.log(`\nNuked ${count} session(s) and all data.`);
57
+ return;
58
+ }
59
+ break;
60
+ }
61
+
44
62
  case "quit": {
45
63
  const runningProcesses = await getRunningProcesses();
46
64
  if (runningProcesses.length > 0) {
@@ -0,0 +1,95 @@
1
+ import {
2
+ type CliRenderer,
3
+ TextRenderable,
4
+ SelectRenderable,
5
+ SelectRenderableEvents,
6
+ type KeyEvent,
7
+ } from "@opentui/core";
8
+ import { createBaseLayout, clearLayout } from "./renderer";
9
+ import { showFooter, hideFooter } from "./common/footer";
10
+ import { isEscape } from "./common/keyboard";
11
+
12
+ export type NukeConfirmChoice = "confirm" | "cancel";
13
+
14
+ export function showNukeConfirm(
15
+ renderer: CliRenderer,
16
+ sessionCount: number,
17
+ ): Promise<NukeConfirmChoice> {
18
+ return new Promise((resolve) => {
19
+ clearLayout(renderer);
20
+
21
+ const { content } = createBaseLayout(renderer, "Delete all sessions");
22
+
23
+ const countInfo = new TextRenderable(renderer, {
24
+ id: "count-info",
25
+ content: `${sessionCount} session${sessionCount === 1 ? "" : "s"} will be deleted`,
26
+ fg: "#38bdf8",
27
+ marginBottom: 1,
28
+ });
29
+ content.add(countInfo);
30
+
31
+ const warning = new TextRenderable(renderer, {
32
+ id: "warning",
33
+ content: "This permanently deletes all sessions, history, and data.",
34
+ fg: "#f59e0b",
35
+ marginBottom: 1,
36
+ });
37
+ content.add(warning);
38
+
39
+ const question = new TextRenderable(renderer, {
40
+ id: "question",
41
+ content: "Are you sure you want to delete all sessions?",
42
+ fg: "#e2e8f0",
43
+ });
44
+ content.add(question);
45
+
46
+ const select = new SelectRenderable(renderer, {
47
+ id: "nuke-confirm-select",
48
+ width: 38,
49
+ height: 2,
50
+ options: [
51
+ { name: "Cancel", description: "", value: "cancel" },
52
+ { name: "Delete all sessions", description: "", value: "confirm" },
53
+ ],
54
+ showDescription: false,
55
+ backgroundColor: "transparent",
56
+ focusedBackgroundColor: "transparent",
57
+ selectedBackgroundColor: "#334155",
58
+ textColor: "#e2e8f0",
59
+ selectedTextColor: "#38bdf8",
60
+ marginTop: 1,
61
+ });
62
+ content.add(select);
63
+
64
+ select.focus();
65
+
66
+ const handleSelect = (_index: number, option: { value: string }) => {
67
+ cleanup();
68
+ resolve(option.value as NukeConfirmChoice);
69
+ };
70
+
71
+ const handleKeypress = (key: KeyEvent) => {
72
+ if (isEscape(key)) {
73
+ cleanup();
74
+ resolve("cancel");
75
+ }
76
+ };
77
+
78
+ const cleanup = () => {
79
+ select.off(SelectRenderableEvents.ITEM_SELECTED, handleSelect);
80
+ renderer.keyInput.off("keypress", handleKeypress);
81
+ select.blur();
82
+ hideFooter(renderer);
83
+ clearLayout(renderer);
84
+ };
85
+
86
+ showFooter(renderer, content, {
87
+ navigate: true,
88
+ select: true,
89
+ back: true,
90
+ });
91
+
92
+ select.on(SelectRenderableEvents.ITEM_SELECTED, handleSelect);
93
+ renderer.keyInput.on("keypress", handleKeypress);
94
+ });
95
+ }
package/src/ui/list.ts CHANGED
@@ -14,7 +14,7 @@ import { isEscape, isArrowUp, isArrowDown } from "./common/keyboard";
14
14
  export type ListAction =
15
15
  | { type: "resume"; session: Session }
16
16
  | { type: "delete"; session: Session }
17
- | { type: "nuke-all" }
17
+ | { type: "nuke" }
18
18
  | { type: "quit" };
19
19
 
20
20
  export function showSessionList(renderer: CliRenderer, sessions: Session[]): Promise<ListAction> {
@@ -93,7 +93,7 @@ export function showSessionList(renderer: CliRenderer, sessions: Session[]): Pro
93
93
  }
94
94
  } else if (key.name === "a") {
95
95
  cleanup();
96
- resolve({ type: "nuke-all" });
96
+ resolve({ type: "nuke" });
97
97
  } else if (key.name === "q" || isEscape(key)) {
98
98
  cleanup();
99
99
  resolve({ type: "quit" });
@@ -112,7 +112,7 @@ export function showSessionList(renderer: CliRenderer, sessions: Session[]): Pro
112
112
  navigate: true,
113
113
  select: false,
114
114
  back: false,
115
- custom: ["Enter Resume", "d Delete", "a Nuke All", "q Quit"],
115
+ custom: ["Enter Resume", "d Delete", "a Nuke", "q Quit"],
116
116
  });
117
117
 
118
118
  select.on(SelectRenderableEvents.ITEM_SELECTED, handleSelect);
@@ -15,6 +15,7 @@ export type MainMenuAction =
15
15
  | { type: "new-session" }
16
16
  | { type: "resume"; session: Session }
17
17
  | { type: "delete"; session: Session }
18
+ | { type: "nuke" }
18
19
  | { type: "quit" };
19
20
 
20
21
  export function showMainMenu(renderer: CliRenderer, sessions: Session[]): Promise<MainMenuAction> {
@@ -94,6 +95,12 @@ export function showMainMenu(renderer: CliRenderer, sessions: Session[]): Promis
94
95
  return;
95
96
  }
96
97
 
98
+ if (key.name === "a" && sessions.length > 0) {
99
+ cleanup();
100
+ resolve({ type: "nuke" });
101
+ return;
102
+ }
103
+
97
104
  if (key.name === "q" || isEscape(key)) {
98
105
  cleanup();
99
106
  resolve({ type: "quit" });
@@ -113,7 +120,7 @@ export function showMainMenu(renderer: CliRenderer, sessions: Session[]): Promis
113
120
  // Show footer with context-aware actions
114
121
  const footerActions: string[] = [];
115
122
  if (sessions.length > 0) {
116
- footerActions.push("Enter Open", "n New", "d Delete");
123
+ footerActions.push("Enter Open", "n New", "d Delete", "a Nuke");
117
124
  } else {
118
125
  footerActions.push("n New");
119
126
  }