Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ dist-ssr
*.njsproj
*.sln
*.sw?
PRD/
TDD/

# Environment variables
.env
Expand Down
4 changes: 4 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,16 @@

Every agent working on this Commander project MUST follow these standards. No exceptions.

<<<<<<< HEAD
We use bun run tauri dev to run the app.

You always work on features that are configurable via the Settings Panel in the app. Every feature must be toggleable or adjustable through user preferences.
Before you write any code, you will write the PRD and save in the docs/ directory.

You write the TDD and then write the feature implementation.
=======
Every prompt or request by the user you will create a PRD and store it in the `PRD/` folder with a filename that matches the feature name. You will then follow the TDD and architecture patterns below to implement the feature.
>>>>>>> f5183e6 (fixing the ui for chat to be default)

## Architecture Pattern - STRICT COMPLIANCE

Expand Down
31 changes: 31 additions & 0 deletions PRD/chat_default_view_on_project_select.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# Product Requirements Document: Chat Default View on Project Select

## Overview
- **Problem:** When a user selects or opens a project, the UI shows the Code tab by default, forcing extra interaction to reach the chat workspace.
- **Outcome:** Selecting, opening, or creating a project must land the user directly in the Chat tab so conversational workflows remain primary.
- **Success Metrics:** Zero additional clicks required to reach Chat immediately after any project activation flow.

## Requirements
- Switching to any project (recents list, open dialog, menu, CLI-start, post-create) must activate the Chat tab by default.
- Returning to Welcome must *not* alter the chat-first preference when re-entering a project.
- Active tab state persists within a project session; manual tab switches must still function.
- No regressions to breadcrumb, sidebar, or history functionality.

## Constraints & Assumptions
- Applies only once a project becomes active; welcome screen remains unchanged.
- Tabs component already supports controlled `value`; only state transitions need adjusting.
- No backend changes required; behavior is purely frontend state management.

## User Journey
1. User launches Commander, opens an existing project → Chat tab visible immediately.
2. User selects a recent project from welcome list → Chat tab already active.
3. User creates or clones a project → Chat tab shows after project loads.
4. User reopens Commander from CLI project flag → Chat tab displays on load.

## Open Questions
- None identified.

## Test Plan
- Add a UI test verifying that selecting a recent project results in the Chat tab trigger being active.
- Ensure regression tests confirming welcome recents rendering continue to pass.

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
},
"dependencies": {
"@openai/codex": "^0.44.0",
"@openai/codex-sdk": "^0.44.0",
"@openai/codex-sdk": "^0.45.0",
"@phosphor-icons/react": "^2.1.10",
"@radix-ui/react-accordion": "^1.2.12",
"@radix-ui/react-alert-dialog": "^1.1.15",
Expand All @@ -30,6 +30,7 @@
"@radix-ui/react-separator": "^1.1.7",
"@radix-ui/react-slot": "^1.2.3",
"@radix-ui/react-switch": "^1.2.6",
"@radix-ui/react-tabs": "^1.1.13",
"@radix-ui/react-toggle": "^1.1.10",
"@radix-ui/react-tooltip": "^1.2.8",
"@radix-ui/react-use-controllable-state": "^1.2.2",
Expand Down
87 changes: 87 additions & 0 deletions scripts/codex-sdk-core.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import { pathToFileURL } from 'url'
import fs from 'fs'

export async function runCodex(options = {}, io = defaultIO) {
const {
sessionId,
prompt = '',
workingDirectory,
sandboxMode,
model,
skipGitRepoCheck,
} = options

let CodexModule
const distPath = process.env.CODEX_SDK_DIST_PATH
if (distPath && fs.existsSync(distPath)) {
CodexModule = await import(pathToFileURL(distPath).href)
} else {
CodexModule = await import('@openai/codex-sdk')
}
const { Codex } = CodexModule

const codexOptions = workingDirectory ? { workingDirectory } : {}
const codex = new Codex(codexOptions)

const threadOptions = {
...(model ? { model } : {}),
...(sandboxMode ? { sandboxMode } : {}),
...(workingDirectory ? { workingDirectory } : {}),
skipGitRepoCheck: skipGitRepoCheck !== false,
}

const thread = codex.startThread(threadOptions)

try {
const { events } = await thread.runStreamed(prompt)
for await (const event of events) {
await io.write(
JSON.stringify({
sessionId,
content: JSON.stringify(event),
finished: false,
})
)
}

await io.write(
JSON.stringify({
sessionId,
content: '',
finished: true,
})
)
} catch (error) {
const message = error instanceof Error ? error.message : String(error)
if (process.env.DEBUG_CodexSDK === '1') {
console.error('[codex-sdk-core] error:', message)
if (error && error.stack) {
console.error(error.stack)
}
}
const payload = JSON.stringify({ sessionId, error: message, finished: true })
if (io.writeError) {
await io.writeError(payload)
} else {
await io.write(payload)
}
}
}

export async function readStdin() {
const chunks = []
for await (const chunk of process.stdin) {
chunks.push(chunk)
}
if (!chunks.length) return ''
return Buffer.concat(chunks.map((c) => (typeof c === 'string' ? Buffer.from(c) : c))).toString('utf8')
}

export const defaultIO = {
write: async (msg) => {
process.stdout.write(msg + '\n')
},
writeError: async (msg) => {
process.stderr.write(msg + '\n')
},
}
83 changes: 3 additions & 80 deletions scripts/codex-sdk-runner.mjs
Original file line number Diff line number Diff line change
@@ -1,76 +1,8 @@
#!/usr/bin/env node
import fs from 'fs'
import { pathToFileURL, fileURLToPath } from 'url'

export async function runCodex(options = {}, io = defaultIO) {
const {
sessionId,
prompt = '',
workingDirectory,
sandboxMode,
model,
skipGitRepoCheck,
} = options

let CodexModule
const distPath = process.env.CODEX_SDK_DIST_PATH
if (distPath && fs.existsSync(distPath)) {
CodexModule = await import(pathToFileURL(distPath).href)
} else {
CodexModule = await import('@openai/codex-sdk')
}
const { Codex } = CodexModule

const codexOptions = workingDirectory ? { workingDirectory } : {}
const codex = new Codex(codexOptions)

const threadOptions = {
...(model ? { model } : {}),
...(sandboxMode ? { sandboxMode } : {}),
...(workingDirectory ? { workingDirectory } : {}),
skipGitRepoCheck: skipGitRepoCheck !== false,
}

const thread = codex.startThread(threadOptions)

try {
const { events } = await thread.runStreamed(prompt)
for await (const event of events) {
await io.write(
JSON.stringify({
sessionId,
content: JSON.stringify(event),
finished: false,
})
)
}

await io.write(
JSON.stringify({
sessionId,
content: '',
finished: true,
})
)
} catch (error) {
const message = error instanceof Error ? error.message : String(error)
const payload = JSON.stringify({ sessionId, error: message, finished: true })
if (io.writeError) {
await io.writeError(payload)
} else {
await io.write(payload)
}
}
}

async function readStdin() {
const chunks = []
for await (const chunk of process.stdin) {
chunks.push(chunk)
}
if (!chunks.length) return ''
return Buffer.concat(chunks.map((c) => (typeof c === 'string' ? Buffer.from(c) : c))).toString('utf8')
}
import { fileURLToPath } from 'url'
import { runCodex, readStdin } from './codex-sdk-core.mjs'
export { runCodex } from './codex-sdk-core.mjs'

async function main() {
const stdin = await readStdin()
Expand All @@ -85,15 +17,6 @@ async function main() {
await runCodex(payload)
}

const defaultIO = {
write: async (msg) => {
process.stdout.write(msg + '\n')
},
writeError: async (msg) => {
process.stderr.write(msg + '\n')
},
}

// Check if this script is being run directly
// Use fs.realpathSync to resolve symlinks (like /var -> /private/var on macOS)
const scriptPath = process.argv[1] ? fs.realpathSync(process.argv[1]) : null
Expand Down
3 changes: 3 additions & 0 deletions src-tauri/src/commands/chat_migration_commands.rs
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,9 @@ pub async fn save_enhanced_chat_message(
content,
timestamp: chrono::Utc::now().timestamp(),
agent: Some(agent),
conversation_id: None,
status: None,
steps: None,
};

// Load existing legacy messages and append
Expand Down
Loading
Loading