mirror of
https://github.com/Ark0N/Codeman.git
synced 2026-10-03 05:59:43 +02:00
fix: address PR review findings for Gemini run mode + Ralph todo-config
Gemini (PR #134) blockers: - runGemini() now unwraps the {success,data} envelope: status check reads .data.available, quick-start reads data.data.sessionId (was reading the raw shape, so the Run-Gemini button could never start a session). - setGeminiEnvVars() now uses the socket-scoped ${this.tmux()} setenv instead of bare tmux — Gemini/Google auth env vars were targeting the wrong tmux server and silently failing on every install. Gemini parity polish: - gemini tab-mode badge ('gm') + .tab-mode.gemini CSS; kill-dialog label 'Kill Tmux & Gemini'; codeman doctor dependency-registry entry; export isGeminiAvailable from utils barrel; COLORTERM=truecolor + unset NO_COLOR; add gemini to isAltScreenStripMode (Ink TUI, repaints inline like Codex/Claude). - Revert 4 system-routes.test.ts envelope assertions weakened to (body.message ?? body.error) back to (body.success === false). - Add a runGemini() vm-sandbox test that drives the envelope path end-to-end. Ralph todo-config (PR #135): maxTodos/todoExpirationMinutes are now persisted and read back — surfaced via the loopState getter (RalphTrackerState) into toState()/SSE broadcast and restored in restoreState(), mirroring maxIterations. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -177,7 +177,7 @@ describe('system-routes', () => {
|
||||
});
|
||||
expect(res.statusCode).toBe(400);
|
||||
const body = JSON.parse(res.body);
|
||||
expect(body.message ?? body.error).toBeTruthy();
|
||||
expect(body.success).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -432,7 +432,7 @@ describe('system-routes', () => {
|
||||
});
|
||||
expect(res.statusCode).toBe(400);
|
||||
const body = JSON.parse(res.body);
|
||||
expect(body.message ?? body.error).toBeTruthy();
|
||||
expect(body.success).toBe(false);
|
||||
});
|
||||
|
||||
it('saves lastUsedCase as partial update without overwriting other settings', async () => {
|
||||
@@ -530,7 +530,7 @@ describe('system-routes', () => {
|
||||
});
|
||||
expect(res.statusCode).toBe(400);
|
||||
const body = JSON.parse(res.body);
|
||||
expect(body.message ?? body.error).toBeTruthy();
|
||||
expect(body.success).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -587,7 +587,7 @@ describe('system-routes', () => {
|
||||
});
|
||||
expect(res.statusCode).toBe(400);
|
||||
const body = JSON.parse(res.body);
|
||||
expect(body.message ?? body.error).toBeTruthy();
|
||||
expect(body.success).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -143,3 +143,56 @@ describe('Codex quick start settings', () => {
|
||||
expect(selected).toEqual(['sess-1']);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Gemini quick start', () => {
|
||||
// Regression guard for the ApiResponse-envelope unwrap in runGemini(): the
|
||||
// status check must read `.data.available` and the quick-start response must
|
||||
// read `.data.sessionId`. Reading the raw shape (pre-fix) silently bails on
|
||||
// the status check and never selects the new tab — exactly the two blockers
|
||||
// caught in PR #134 review.
|
||||
it('drives runGemini() through the {success,data} envelope and selects the new session', async () => {
|
||||
const elements: Record<string, any> = {
|
||||
quickStartCase: { value: 'gemini-case' },
|
||||
};
|
||||
const requests: Array<{ url: string; body?: any }> = [];
|
||||
const CodemanApp = function CodemanApp(this: any) {};
|
||||
|
||||
const context = vm.createContext({
|
||||
CodemanApp,
|
||||
localStorage: { getItem: () => null, setItem: () => {} },
|
||||
document: { getElementById: (id: string) => elements[id] ?? null },
|
||||
// Mock responses use the real wire shape: the server.ts preSerialization
|
||||
// hook wraps raw route payloads into the { success, data } envelope.
|
||||
fetch: async (url: string, init?: { body?: string }) => {
|
||||
requests.push({ url, body: init?.body ? JSON.parse(init.body) : undefined });
|
||||
if (url === '/api/gemini/status') return { json: async () => ({ success: true, data: { available: true } }) };
|
||||
if (url === '/api/quick-start')
|
||||
return { json: async () => ({ success: true, data: { sessionId: 'sess-gm' } }) };
|
||||
throw new Error(`unexpected fetch: ${url}`);
|
||||
},
|
||||
console,
|
||||
});
|
||||
|
||||
const sessionUi = readFileSync(resolve(import.meta.dirname, '../src/web/public/session-ui.js'), 'utf8');
|
||||
vm.runInContext(sessionUi, context, { filename: 'session-ui.js' });
|
||||
|
||||
const app = new (CodemanApp as any)();
|
||||
app.terminal = { clear: () => {}, writeln: () => {}, focus: () => {} };
|
||||
app.loadAppSettingsFromStorage = () => ({});
|
||||
app.getCaseSettings = () => ({});
|
||||
app.buildEnvOverrides = () => ({});
|
||||
const selected: string[] = [];
|
||||
app.selectSession = async (id: string) => {
|
||||
selected.push(id);
|
||||
};
|
||||
|
||||
await app.runGemini();
|
||||
|
||||
expect(requests.find((req) => req.url === '/api/quick-start')?.body).toMatchObject({
|
||||
caseName: 'gemini-case',
|
||||
mode: 'gemini',
|
||||
geminiConfig: { approvalMode: 'yolo' },
|
||||
});
|
||||
expect(selected).toEqual(['sess-gm']);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user