letmecook 0.0.21 → 0.0.23

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.
@@ -8,7 +8,6 @@ import { recordRepoHistory } from "../repo-history";
8
8
  import { showAgentProposal } from "../ui/agent-proposal";
9
9
  import { showConflictPrompt } from "../ui/conflict";
10
10
  import { runCommands, hideCommandRunner, type CommandTask } from "../ui/common/command-runner";
11
- import { ensureReferenceRepo, linkReferenceRepo, ensureReferencesDir } from "../reference-repo";
12
11
  import { rm } from "node:fs/promises";
13
12
 
14
13
  export interface NewSessionParams {
@@ -25,11 +24,7 @@ export interface NewSessionResult {
25
24
  }
26
25
 
27
26
  function reposToCommandTasks(repos: RepoSpec[], sessionPath: string): CommandTask[] {
28
- // Only create command tasks for non-reference repos
29
- // Reference repos are handled separately with ensureReferenceRepo + symlink
30
- const regularRepos = repos.filter((repo) => !repo.reference);
31
-
32
- return regularRepos.map((repo) => {
27
+ return repos.map((repo) => {
33
28
  const url = `https://github.com/${repo.owner}/${repo.name}.git`;
34
29
  const targetDir = join(sessionPath, repo.dir);
35
30
  const args = repo.branch
@@ -55,41 +50,6 @@ function reposToCommandTasks(repos: RepoSpec[], sessionPath: string): CommandTas
55
50
  });
56
51
  }
57
52
 
58
- async function setupReferenceRepos(
59
- repos: RepoSpec[],
60
- sessionPath: string,
61
- onProgress?: (repo: RepoSpec, status: "caching" | "linking" | "done" | "error") => void,
62
- ): Promise<{ successful: RepoSpec[]; failed: Array<{ repo: RepoSpec; error: string }> }> {
63
- const referenceRepos = repos.filter((repo) => repo.reference);
64
- const successful: RepoSpec[] = [];
65
- const failed: Array<{ repo: RepoSpec; error: string }> = [];
66
-
67
- if (referenceRepos.length === 0) {
68
- return { successful, failed };
69
- }
70
-
71
- await ensureReferencesDir();
72
-
73
- for (const repo of referenceRepos) {
74
- try {
75
- onProgress?.(repo, "caching");
76
- await ensureReferenceRepo(repo);
77
-
78
- onProgress?.(repo, "linking");
79
- await linkReferenceRepo(repo, sessionPath);
80
-
81
- onProgress?.(repo, "done");
82
- successful.push(repo);
83
- } catch (error) {
84
- const errorMsg = error instanceof Error ? error.message : String(error);
85
- onProgress?.(repo, "error");
86
- failed.push({ repo, error: errorMsg });
87
- }
88
- }
89
-
90
- return { successful, failed };
91
- }
92
-
93
53
  function skillsToCommandTasks(skills: string[], sessionPath: string): CommandTask[] {
94
54
  return skills.map((skill) => ({
95
55
  label: `Installing ${skill}`,
@@ -128,15 +88,22 @@ export async function createNewSession(
128
88
  }
129
89
  }
130
90
 
131
- const sessionName = await generateSessionName(repos, goal);
132
-
91
+ // Generate session name (show UI feedback during generation in TUI mode)
92
+ let sessionName: string;
133
93
  if (mode === "tui") {
134
- showAgentProposal(renderer, {
135
- sessionName,
94
+ const proposal = showAgentProposal(renderer, {
95
+ sessionName: "generating...",
136
96
  repos,
137
97
  goal,
138
98
  });
99
+ sessionName = await generateSessionName(repos, goal);
100
+ proposal.sessionNameText.content = `Session: ${sessionName}`;
101
+ renderer.requestRender();
102
+ } else {
103
+ sessionName = await generateSessionName(repos, goal);
104
+ }
139
105
 
106
+ if (mode === "tui") {
140
107
  await new Promise((resolve) => setTimeout(resolve, 3000));
141
108
 
142
109
  const session = await createSession(
@@ -146,89 +113,68 @@ export async function createNewSession(
146
113
  skills?.length ? skills : undefined,
147
114
  );
148
115
 
149
- // Setup reference repos first (symlinks from cache)
150
- const { successful: successfulRefs, failed: failedRefs } = await setupReferenceRepos(
151
- repos,
152
- session.path,
153
- );
154
-
155
- // Report reference repo failures
156
- if (failedRefs.length > 0) {
157
- console.error(`\n⚠️ ${failedRefs.length} reference repo(s) failed:`);
158
- failedRefs.forEach(({ repo, error }) => {
159
- console.error(` ✗ ${repo.owner}/${repo.name}: ${error}`);
160
- });
161
- }
162
-
163
- // Build tasks for regular repos + skills
116
+ // Build all tasks (repos + skills)
164
117
  const tasks: CommandTask[] = [
165
118
  ...reposToCommandTasks(repos, session.path),
166
119
  ...(skills && skills.length > 0 ? skillsToCommandTasks(skills, session.path) : []),
167
120
  ];
168
121
 
169
- let results: Awaited<ReturnType<typeof runCommands>> = [];
170
-
171
- if (tasks.length > 0) {
172
- results = await runCommands(renderer, {
173
- title: "Setting up session",
174
- tasks,
175
- showOutput: true,
176
- outputLines: 5,
177
- allowAbort: true,
178
- allowSkip: tasks.length > 1,
179
- allowBackground: true,
180
- sessionName,
181
- });
122
+ const results = await runCommands(renderer, {
123
+ title: "Setting up session",
124
+ tasks,
125
+ showOutput: true,
126
+ outputLines: 5,
127
+ allowAbort: true,
128
+ allowSkip: tasks.length > 1,
129
+ allowBackground: true,
130
+ sessionName,
131
+ });
182
132
 
183
- // Handle aborted operation - clean up and return null
184
- const wasAborted = results.some((r) => r.outcome === "aborted");
185
- if (wasAborted) {
186
- hideCommandRunner(renderer);
187
- // Clean up the partial session
188
- await deleteSession(session.name);
189
- return null;
190
- }
133
+ // Handle aborted operation - clean up and return null
134
+ const wasAborted = results.some((r) => r.outcome === "aborted");
135
+ if (wasAborted) {
136
+ hideCommandRunner(renderer);
137
+ // Clean up the partial session
138
+ await deleteSession(session.name);
139
+ return null;
140
+ }
191
141
 
192
- // Clean up skipped repo directories
193
- const skippedResults = results.filter((r) => r.outcome === "skipped");
194
- for (const skipped of skippedResults) {
195
- const repoName = skipped.task.label.replace("Cloning ", "").split("/")[1];
196
- if (repoName) {
197
- const repoPath = join(session.path, repoName);
198
- try {
199
- await rm(repoPath, { recursive: true, force: true });
200
- } catch {
201
- // Ignore cleanup errors
202
- }
142
+ // Clean up skipped repo directories
143
+ const skippedResults = results.filter((r) => r.outcome === "skipped");
144
+ for (const skipped of skippedResults) {
145
+ const repoName = skipped.task.label.replace("Cloning ", "").split("/")[1];
146
+ if (repoName) {
147
+ const repoPath = join(session.path, repoName);
148
+ try {
149
+ await rm(repoPath, { recursive: true, force: true });
150
+ } catch {
151
+ // Ignore cleanup errors
203
152
  }
204
153
  }
205
-
206
- // Check for errors (not skipped/aborted)
207
- const errors = results.filter((r) => r.outcome === "error");
208
- if (errors.length > 0) {
209
- console.error(`\n⚠️ ${errors.length} task(s) failed:`);
210
- errors.forEach((err) => {
211
- console.error(` ✗ ${err.task.label}`);
212
- if (err.error) {
213
- console.error(` ${err.error}`);
214
- }
215
- });
216
- }
217
154
  }
218
155
 
219
156
  // Filter out skipped repos from the session
220
157
  const successfulRepoResults = results.filter(
221
158
  (r) => r.outcome === "completed" && r.task.label.startsWith("Cloning "),
222
159
  );
223
- const successfulClonedSpecs = successfulRepoResults
160
+ const successfulRepoSpecs = successfulRepoResults
224
161
  .map((r) => {
225
162
  const repoSpec = r.task.label.replace("Cloning ", "");
226
163
  return repos.find((repo) => `${repo.owner}/${repo.name}` === repoSpec);
227
164
  })
228
165
  .filter((r): r is RepoSpec => r !== undefined);
229
166
 
230
- // Combine successful reference repos and cloned repos
231
- const successfulRepoSpecs = [...successfulRefs, ...successfulClonedSpecs];
167
+ // Check for errors (not skipped/aborted)
168
+ const errors = results.filter((r) => r.outcome === "error");
169
+ if (errors.length > 0) {
170
+ console.error(`\n⚠️ ${errors.length} task(s) failed:`);
171
+ errors.forEach((err) => {
172
+ console.error(` ✗ ${err.task.label}`);
173
+ if (err.error) {
174
+ console.error(` ${err.error}`);
175
+ }
176
+ });
177
+ }
232
178
 
233
179
  // Record only successful repos
234
180
  if (successfulRepoSpecs.length > 0) {
@@ -251,52 +197,30 @@ export async function createNewSession(
251
197
  skills?.length ? skills : undefined,
252
198
  );
253
199
 
254
- // Setup reference repos first (symlinks from cache)
255
- const referenceRepos = repos.filter((r) => r.reference);
256
- if (referenceRepos.length > 0) {
257
- console.log(`\nSetting up ${referenceRepos.length} reference repo(s)...`);
258
- const { successful: successfulRefs, failed: failedRefs } = await setupReferenceRepos(
259
- repos,
260
- session.path,
261
- );
262
- successfulRefs.forEach((repo) => {
263
- console.log(` ✓ ${repo.owner}/${repo.name} (linked)`);
264
- });
265
- failedRefs.forEach(({ repo, error }) => {
266
- console.log(` ✗ ${repo.owner}/${repo.name}: ${error}`);
267
- });
268
- }
269
-
270
- // Clone regular repos
271
- const regularRepos = repos.filter((r) => !r.reference);
272
- if (regularRepos.length > 0) {
273
- console.log(`\nCloning ${regularRepos.length} repository(ies)...`);
274
- }
200
+ console.log(`\nCloning ${repos.length} repository(ies)...`);
275
201
 
276
202
  const tasks: CommandTask[] = [
277
203
  ...reposToCommandTasks(repos, session.path),
278
204
  ...(skills && skills.length > 0 ? skillsToCommandTasks(skills, session.path) : []),
279
205
  ];
280
206
 
281
- if (tasks.length > 0) {
282
- const results = await runCommands(renderer, {
283
- title: "Setting up session",
284
- tasks,
285
- showOutput: false, // CLI mode doesn't need visual output
286
- });
207
+ const results = await runCommands(renderer, {
208
+ title: "Setting up session",
209
+ tasks,
210
+ showOutput: false, // CLI mode doesn't need visual output
211
+ });
287
212
 
288
- // Print results
289
- results.forEach((result) => {
290
- if (result.success) {
291
- console.log(` ✓ ${result.task.label}`);
292
- } else {
293
- console.log(` ✗ ${result.task.label}`);
294
- if (result.error) {
295
- console.log(` ${result.error}`);
296
- }
213
+ // Print results
214
+ results.forEach((result) => {
215
+ if (result.success) {
216
+ console.log(` ✓ ${result.task.label}`);
217
+ } else {
218
+ console.log(` ✗ ${result.task.label}`);
219
+ if (result.error) {
220
+ console.log(` ${result.error}`);
297
221
  }
298
- });
299
- }
222
+ }
223
+ });
300
224
 
301
225
  await writeAgentsMd(session);
302
226
  await createClaudeMdSymlink(session.path);
@@ -1,7 +1,7 @@
1
1
  import type { CliRenderer } from "@opentui/core";
2
2
  import type { Session } from "../types";
3
3
  import { updateSessionSettings } from "../sessions";
4
- import { refreshReferenceRepos } from "../git";
4
+ import { refreshLatestRepos } from "../git";
5
5
  import { updateSkills } from "../skills";
6
6
  import { handleSmartExit } from "../ui/exit";
7
7
  import { showSessionSettings } from "../ui/session-settings";
@@ -10,10 +10,10 @@ import { showSessionStartWarning } from "../ui/background-warning";
10
10
  import { showProgress, updateProgress, hideProgress } from "../ui/progress";
11
11
  import { showReclonePrompt } from "../ui/reclone-prompt";
12
12
  import { deleteSession } from "../sessions";
13
+ import { recloneRepo } from "../git";
13
14
  import { writeAgentsMd } from "../agents-md";
14
15
  import { createRenderer, destroyRenderer } from "../ui/renderer";
15
16
  import { getProcessesForSession } from "../process-registry";
16
- import { repairReferenceLink } from "../reference-repo";
17
17
 
18
18
  export interface ResumeSessionParams {
19
19
  session: Session;
@@ -49,10 +49,10 @@ export async function resumeSession(
49
49
  while (true) {
50
50
  if (mode === "tui" && shouldRefresh) {
51
51
  renderer = await createRenderer();
52
- await refreshReferenceBeforeResume(renderer, currentSession);
52
+ await refreshLatestBeforeResume(renderer, currentSession);
53
53
  destroyRenderer();
54
54
  } else if (shouldRefresh) {
55
- await refreshReferenceBeforeResumeSimple(currentSession);
55
+ await refreshLatestBeforeResumeSimple(currentSession);
56
56
  }
57
57
 
58
58
  await runOpencodeMode(mode, currentSession.path);
@@ -117,23 +117,20 @@ export async function resumeSession(
117
117
  }
118
118
  }
119
119
 
120
- async function refreshReferenceBeforeResume(
121
- renderer: CliRenderer,
122
- session: Session,
123
- ): Promise<void> {
124
- const referenceRepos = session.repos.filter((repo) => repo.reference);
125
- if (referenceRepos.length === 0) return;
120
+ async function refreshLatestBeforeResume(renderer: CliRenderer, session: Session): Promise<void> {
121
+ const readOnlyRepos = session.repos.filter((repo) => repo.readOnly);
122
+ if (readOnlyRepos.length === 0) return;
126
123
 
127
- const refreshProgressState = showProgress(renderer, referenceRepos, {
124
+ const refreshProgressState = showProgress(renderer, readOnlyRepos, {
128
125
  title: "Refreshing repositories",
129
- label: "Refreshing reference repositories:",
126
+ label: "Refreshing read-only repositories:",
130
127
  initialPhase: "refreshing",
131
128
  });
132
129
  refreshProgressState.sessionName = session.name;
133
130
  updateProgress(renderer, refreshProgressState);
134
131
 
135
- const refreshResults = await refreshReferenceRepos(
136
- referenceRepos,
132
+ const refreshResults = await refreshLatestRepos(
133
+ readOnlyRepos,
137
134
  session.path,
138
135
  (repoIndex, status, outputLines) => {
139
136
  const repoState = refreshProgressState.repos[repoIndex];
@@ -153,60 +150,59 @@ async function refreshReferenceBeforeResume(
153
150
  await new Promise((resolve) => setTimeout(resolve, 700));
154
151
  hideProgress(renderer);
155
152
 
156
- // For reference repos that failed, offer to repair the link
157
- const repairTargets = refreshResults.filter(
158
- (result) => result.status === "error" && result.repo.reference,
153
+ const recloneTargets = refreshResults.filter(
154
+ (result) => result.status === "error" && result.repo.readOnly,
159
155
  );
160
156
 
161
- for (const result of repairTargets) {
157
+ for (const result of recloneTargets) {
162
158
  const choice = await showReclonePrompt(renderer, result.repo);
163
159
 
164
160
  if (choice === "reclone") {
165
- const repairProgressState = showProgress(renderer, [result.repo], {
166
- title: "Repairing reference",
167
- label: "Repairing:",
161
+ const recloneProgressState = showProgress(renderer, [result.repo], {
162
+ title: "Recloning repository",
163
+ label: "Recloning:",
168
164
  initialPhase: "cloning",
169
165
  });
170
- repairProgressState.sessionName = session.name;
171
- updateProgress(renderer, repairProgressState);
166
+ recloneProgressState.sessionName = session.name;
167
+ updateProgress(renderer, recloneProgressState);
172
168
 
173
169
  try {
174
- await repairReferenceLink(result.repo, session.path, (status, outputLines) => {
175
- const repoState = repairProgressState.repos[0];
170
+ await recloneRepo(result.repo, session.path, (status, outputLines) => {
171
+ const repoState = recloneProgressState.repos[0];
176
172
  if (repoState) {
177
173
  repoState.status = status;
178
174
  if (outputLines) {
179
- repairProgressState.currentOutput = outputLines;
175
+ recloneProgressState.currentOutput = outputLines;
180
176
  }
181
- updateProgress(renderer, repairProgressState);
177
+ updateProgress(renderer, recloneProgressState);
182
178
  }
183
179
  });
184
180
  } catch (error) {
185
- const repoState = repairProgressState.repos[0];
181
+ const repoState = recloneProgressState.repos[0];
186
182
  if (repoState) {
187
183
  repoState.status = "error";
188
184
  }
189
- repairProgressState.currentOutput = [
185
+ recloneProgressState.currentOutput = [
190
186
  error instanceof Error ? error.message : String(error),
191
187
  ];
192
- updateProgress(renderer, repairProgressState);
188
+ updateProgress(renderer, recloneProgressState);
193
189
  }
194
190
 
195
- repairProgressState.phase = "done";
196
- updateProgress(renderer, repairProgressState);
191
+ recloneProgressState.phase = "done";
192
+ updateProgress(renderer, recloneProgressState);
197
193
  await new Promise((resolve) => setTimeout(resolve, 700));
198
194
  hideProgress(renderer);
199
195
  }
200
196
  }
201
197
  }
202
198
 
203
- async function refreshReferenceBeforeResumeSimple(session: Session): Promise<void> {
204
- const referenceRepos = session.repos.filter((repo) => repo.reference);
205
- if (referenceRepos.length === 0) return;
199
+ async function refreshLatestBeforeResumeSimple(session: Session): Promise<void> {
200
+ const readOnlyRepos = session.repos.filter((repo) => repo.readOnly);
201
+ if (readOnlyRepos.length === 0) return;
206
202
 
207
- console.log("\nRefreshing reference repositories...");
203
+ console.log("\nRefreshing read-only repositories...");
208
204
 
209
- const results = await refreshReferenceRepos(referenceRepos, session.path);
205
+ const results = await refreshLatestRepos(readOnlyRepos, session.path);
210
206
 
211
207
  if (results.length === 0) return;
212
208
 
package/src/git.ts CHANGED
@@ -193,101 +193,63 @@ export async function sessionHasUncommittedChanges(
193
193
  return { hasChanges: reposWithChanges.length > 0, reposWithChanges };
194
194
  }
195
195
 
196
- export async function refreshReferenceRepos(
196
+ export async function refreshLatestRepos(
197
197
  repos: RepoSpec[],
198
198
  sessionPath: string,
199
199
  onProgress?: (repoIndex: number, status: RefreshProgressStatus, outputLines?: string[]) => void,
200
200
  ): Promise<RefreshResult[]> {
201
- // Import reference-repo functions dynamically to avoid circular deps
202
- const { refreshReferenceRepo, verifyReferenceLink, repairReferenceLink, isSymlink } =
203
- await import("./reference-repo");
204
-
205
- const referenceRepos = repos.filter((repo) => repo.reference);
206
- if (referenceRepos.length === 0) return [];
201
+ const readOnlyRepos = repos.filter((repo) => repo.readOnly);
202
+ if (readOnlyRepos.length === 0) return [];
207
203
 
208
204
  const results: RefreshResult[] = [];
209
205
 
210
- for (const [repoIndex, repo] of referenceRepos.entries()) {
206
+ for (const [repoIndex, repo] of readOnlyRepos.entries()) {
211
207
  const repoPath = join(sessionPath, repo.dir);
208
+ const dirty = await hasUncommittedChanges(repoPath);
212
209
 
213
- // Check if this is a symlink (reference repo)
214
- const isRef = await isSymlink(repoPath);
215
-
216
- if (isRef) {
217
- // Verify symlink is valid, repair if needed
218
- const isValid = await verifyReferenceLink(repo, sessionPath);
219
- if (!isValid) {
220
- onProgress?.(repoIndex, "refreshing", [`Repairing link to ${repo.owner}/${repo.name}...`]);
221
- try {
222
- await repairReferenceLink(repo, sessionPath, (status, lines) => {
223
- if (status === "cloning") onProgress?.(repoIndex, "refreshing", lines);
224
- });
225
- } catch (error) {
226
- results.push({
227
- repo,
228
- status: "error",
229
- reason: error instanceof Error ? error.message : String(error),
230
- });
231
- onProgress?.(repoIndex, "error", [
232
- error instanceof Error ? error.message : String(error),
233
- ]);
234
- continue;
235
- }
236
- }
237
-
238
- // Refresh the cached reference repo
239
- const result = await refreshReferenceRepo(repo, (status, lines) => {
240
- onProgress?.(repoIndex, status, lines);
210
+ if (dirty) {
211
+ results.push({
212
+ repo,
213
+ status: "skipped",
214
+ reason: "uncommitted changes",
241
215
  });
242
- results.push(result);
243
- } else {
244
- // Non-symlink reference repo (legacy or converted) - use standard git pull
245
- const dirty = await hasUncommittedChanges(repoPath);
246
-
247
- if (dirty) {
248
- results.push({
249
- repo,
250
- status: "skipped",
251
- reason: "uncommitted changes",
252
- });
253
- onProgress?.(repoIndex, "skipped", ["Skipped: uncommitted changes"]);
254
- continue;
255
- }
216
+ onProgress?.(repoIndex, "skipped", ["Skipped: uncommitted changes"]);
217
+ continue;
218
+ }
256
219
 
257
- onProgress?.(repoIndex, "refreshing", [`Pulling ${repo.owner}/${repo.name}...`]);
220
+ onProgress?.(repoIndex, "refreshing", [`Pulling ${repo.owner}/${repo.name}...`]);
258
221
 
259
- const proc = Bun.spawn(["git", "-C", repoPath, "pull", "--ff-only", "--depth", "1"], {
260
- stdout: "pipe",
261
- stderr: "pipe",
262
- });
263
-
264
- const { success, output, fullOutput } = await readProcessOutputWithBuffer(proc, {
265
- maxBufferLines: 5,
266
- onBufferUpdate: (buffer) => onProgress?.(repoIndex, "refreshing", buffer),
267
- });
268
- const exitCode = success ? 0 : 1;
269
-
270
- if (exitCode !== 0) {
271
- const reason = fullOutput.trim() || `git pull exited with code ${exitCode}`;
272
- results.push({
273
- repo,
274
- status: "error",
275
- reason,
276
- });
277
- onProgress?.(repoIndex, "error", output.length > 0 ? [...output] : [reason]);
278
- continue;
279
- }
222
+ const proc = Bun.spawn(["git", "-C", repoPath, "pull", "--ff-only", "--depth", "1"], {
223
+ stdout: "pipe",
224
+ stderr: "pipe",
225
+ });
280
226
 
281
- const normalized = fullOutput.toLowerCase();
282
- const upToDate =
283
- normalized.includes("already up to date") || normalized.includes("already up-to-date");
227
+ const { success, output, fullOutput } = await readProcessOutputWithBuffer(proc, {
228
+ maxBufferLines: 5,
229
+ onBufferUpdate: (buffer) => onProgress?.(repoIndex, "refreshing", buffer),
230
+ });
231
+ const exitCode = success ? 0 : 1;
284
232
 
233
+ if (exitCode !== 0) {
234
+ const reason = fullOutput.trim() || `git pull exited with code ${exitCode}`;
285
235
  results.push({
286
236
  repo,
287
- status: upToDate ? "up-to-date" : "updated",
237
+ status: "error",
238
+ reason,
288
239
  });
289
- onProgress?.(repoIndex, upToDate ? "up-to-date" : "updated", [...output]);
240
+ onProgress?.(repoIndex, "error", output.length > 0 ? [...output] : [reason]);
241
+ continue;
290
242
  }
243
+
244
+ const normalized = fullOutput.toLowerCase();
245
+ const upToDate =
246
+ normalized.includes("already up to date") || normalized.includes("already up-to-date");
247
+
248
+ results.push({
249
+ repo,
250
+ status: upToDate ? "up-to-date" : "updated",
251
+ });
252
+ onProgress?.(repoIndex, upToDate ? "up-to-date" : "updated", [...output]);
291
253
  }
292
254
 
293
255
  return results;
package/src/naming.ts CHANGED
@@ -7,7 +7,7 @@ export async function generateSessionName(repos: RepoSpec[], goal?: string): Pro
7
7
 
8
8
  try {
9
9
  const { text } = await generateText({
10
- model: "xai/grok-4-fast-non-reasoning",
10
+ model: "xai/grok-3-mini-fast",
11
11
  temperature: 1.2, // Higher temperature for more creative/varied names
12
12
  prompt: `Generate a creative, memorable session name for a coding workspace.
13
13
 
@@ -49,7 +49,7 @@ Reply with ONLY the name, nothing else. No quotes, no explanation.`,
49
49
  }
50
50
  }
51
51
 
52
- function generateFallbackName(repos: RepoSpec[]): string {
52
+ export function generateFallbackName(repos: RepoSpec[]): string {
53
53
  // Simple fallback: first repo name + short hash
54
54
  const base = repos[0]?.name || "session";
55
55
  const hash = Math.random().toString(36).slice(2, 6);