(cwd: string)
| 38 | } |
| 39 | |
| 40 | export function listProjects(cwd: string): Project[] { |
| 41 | ensureChatStateSchema() |
| 42 | const db = getThreadStateDatabase() |
| 43 | ensureProject(cwd) |
| 44 | |
| 45 | const rows = db |
| 46 | .prepare( |
| 47 | ` |
| 48 | SELECT |
| 49 | projects.cwd AS id, |
| 50 | COALESCE(projects.custom_name, projects.name) AS name, |
| 51 | projects.order_index AS orderIndex, |
| 52 | projects.pinned AS pinned, |
| 53 | projects.collapsed AS collapsed, |
| 54 | projects.repo_origin_url AS repoOriginUrl, |
| 55 | projects.repo_origin_checked AS repoOriginChecked, |
| 56 | projects.git_ops_mode AS gitOpsMode, |
| 57 | COUNT(threads.id) AS threadCount, |
| 58 | COALESCE(MAX(threads.last_modified_ms), 0) AS latestModifiedMs |
| 59 | FROM projects |
| 60 | LEFT JOIN threads |
| 61 | ON threads.cwd = projects.cwd |
| 62 | AND threads.archived = 0 |
| 63 | AND threads.session_path NOT LIKE ? |
| 64 | AND NOT EXISTS ( |
| 65 | SELECT 1 FROM chat_threads WHERE chat_threads.session_path = threads.session_path |
| 66 | ) |
| 67 | WHERE projects.hidden = 0 |
| 68 | GROUP BY |
| 69 | projects.cwd, |
| 70 | COALESCE(projects.custom_name, projects.name), |
| 71 | projects.order_index, |
| 72 | projects.pinned, |
| 73 | projects.collapsed, |
| 74 | projects.repo_origin_url, |
| 75 | projects.repo_origin_checked, |
| 76 | projects.git_ops_mode |
| 77 | ORDER BY |
| 78 | projects.pinned DESC, |
| 79 | CASE WHEN projects.order_index IS NULL THEN 1 ELSE 0 END, |
| 80 | projects.order_index ASC, |
| 81 | CASE WHEN projects.order_index IS NULL AND projects.cwd = ? THEN 0 ELSE 1 END, |
| 82 | latestModifiedMs DESC, |
| 83 | projects.name COLLATE NOCASE ASC |
| 84 | `, |
| 85 | ) |
| 86 | .all(getChatSessionLikePattern(), cwd) as ProjectRow[] |
| 87 | |
| 88 | return rows.map(mapProjectRow) |
| 89 | } |
| 90 | |
| 91 | export function hasProject(projectId: string) { |
| 92 | const db = getThreadStateDatabase() |
no test coverage detected