@assistkick/create 1.0.1 → 1.3.0

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.
Files changed (194) hide show
  1. package/dist/src/scaffolder.d.ts +6 -1
  2. package/dist/src/scaffolder.js +20 -9
  3. package/dist/src/scaffolder.js.map +1 -1
  4. package/package.json +3 -2
  5. package/templates/{product-system → assistkick-product-system}/CLAUDE.md +4 -4
  6. package/templates/{product-system → assistkick-product-system}/package.json +5 -5
  7. package/templates/{product-system → assistkick-product-system}/packages/backend/package.json +2 -2
  8. package/templates/{product-system → assistkick-product-system}/packages/backend/src/routes/auth.ts +1 -1
  9. package/templates/{product-system → assistkick-product-system}/packages/backend/src/routes/coherence.ts +1 -1
  10. package/templates/assistkick-product-system/packages/backend/src/routes/git.ts +231 -0
  11. package/templates/{product-system → assistkick-product-system}/packages/backend/src/routes/graph.ts +3 -3
  12. package/templates/{product-system → assistkick-product-system}/packages/backend/src/routes/kanban.ts +6 -6
  13. package/templates/assistkick-product-system/packages/backend/src/routes/pipeline.ts +88 -0
  14. package/templates/assistkick-product-system/packages/backend/src/routes/terminal.ts +82 -0
  15. package/templates/{product-system → assistkick-product-system}/packages/backend/src/server.ts +23 -10
  16. package/templates/{product-system → assistkick-product-system}/packages/backend/src/services/coherence-review.ts +4 -4
  17. package/templates/assistkick-product-system/packages/backend/src/services/github_app_service.ts +146 -0
  18. package/templates/assistkick-product-system/packages/backend/src/services/init.ts +147 -0
  19. package/templates/{product-system → assistkick-product-system}/packages/backend/src/services/invitation_service.ts +1 -1
  20. package/templates/{product-system → assistkick-product-system}/packages/backend/src/services/password_reset_service.ts +1 -1
  21. package/templates/{product-system → assistkick-product-system}/packages/backend/src/services/project_service.ts +72 -1
  22. package/templates/assistkick-product-system/packages/backend/src/services/project_workspace_service.test.ts +87 -0
  23. package/templates/assistkick-product-system/packages/backend/src/services/project_workspace_service.ts +194 -0
  24. package/templates/assistkick-product-system/packages/backend/src/services/pty_session_manager.test.ts +159 -0
  25. package/templates/{product-system → assistkick-product-system}/packages/backend/src/services/pty_session_manager.ts +114 -39
  26. package/templates/{product-system → assistkick-product-system}/packages/backend/src/services/terminal_ws_handler.ts +28 -14
  27. package/templates/{product-system → assistkick-product-system}/packages/backend/src/services/user_management_service.ts +1 -1
  28. package/templates/{product-system → assistkick-product-system}/packages/frontend/package.json +1 -1
  29. package/templates/{product-system → assistkick-product-system}/packages/frontend/src/App.tsx +1 -1
  30. package/templates/{product-system → assistkick-product-system}/packages/frontend/src/api/client.ts +151 -0
  31. package/templates/assistkick-product-system/packages/frontend/src/components/GitRepoModal.tsx +352 -0
  32. package/templates/{product-system → assistkick-product-system}/packages/frontend/src/components/KanbanView.tsx +208 -95
  33. package/templates/{product-system → assistkick-product-system}/packages/frontend/src/components/ProjectSelector.tsx +17 -1
  34. package/templates/assistkick-product-system/packages/frontend/src/components/TerminalView.tsx +333 -0
  35. package/templates/{product-system → assistkick-product-system}/packages/frontend/src/components/Toolbar.tsx +15 -13
  36. package/templates/{product-system → assistkick-product-system}/packages/frontend/src/constants/graph.ts +1 -0
  37. package/templates/{product-system → assistkick-product-system}/packages/frontend/src/hooks/useProjects.ts +4 -0
  38. package/templates/{product-system → assistkick-product-system}/packages/frontend/src/routes/dashboard.tsx +22 -4
  39. package/templates/{product-system → assistkick-product-system}/packages/frontend/src/styles/index.css +486 -38
  40. package/templates/assistkick-product-system/packages/frontend/vite.config.ts +31 -0
  41. package/templates/assistkick-product-system/packages/shared/db/migrations/0001_vengeful_wallop.sql +1 -0
  42. package/templates/assistkick-product-system/packages/shared/db/migrations/0002_greedy_excalibur.sql +4 -0
  43. package/templates/assistkick-product-system/packages/shared/db/migrations/meta/0001_snapshot.json +826 -0
  44. package/templates/assistkick-product-system/packages/shared/db/migrations/meta/0002_snapshot.json +854 -0
  45. package/templates/assistkick-product-system/packages/shared/db/migrations/meta/_journal.json +27 -0
  46. package/templates/{product-system → assistkick-product-system}/packages/shared/db/schema.ts +5 -0
  47. package/templates/{product-system → assistkick-product-system}/packages/shared/lib/claude-service.ts +54 -1
  48. package/templates/{product-system → assistkick-product-system}/packages/shared/lib/db.ts +1 -1
  49. package/templates/{product-system → assistkick-product-system}/packages/shared/lib/git_workflow.ts +25 -0
  50. package/templates/{product-system → assistkick-product-system}/packages/shared/lib/pipeline-state-store.ts +4 -0
  51. package/templates/{product-system → assistkick-product-system}/packages/shared/lib/pipeline.ts +329 -89
  52. package/templates/assistkick-product-system/packages/shared/lib/pipeline_orchestrator.ts +186 -0
  53. package/templates/{product-system → assistkick-product-system}/packages/shared/lib/prompt_builder.ts +2 -2
  54. package/templates/{product-system → assistkick-product-system}/packages/shared/package.json +1 -1
  55. package/templates/assistkick-product-system/packages/shared/tools/db_explorer.ts +275 -0
  56. package/templates/{product-system → assistkick-product-system}/packages/shared/tools/get_kanban.ts +2 -1
  57. package/templates/{product-system → assistkick-product-system}/packages/shared/tools/move_card.ts +3 -2
  58. package/templates/{product-system → assistkick-product-system}/packages/shared/tools/update_node.ts +2 -2
  59. package/templates/{product-system → assistkick-product-system}/tests/db_sqlite_fallback.test.ts +1 -1
  60. package/templates/{product-system → assistkick-product-system}/tests/kanban.test.ts +1 -1
  61. package/templates/{product-system → assistkick-product-system}/tests/pipeline_stats_all_cards.test.ts +1 -1
  62. package/templates/{product-system → assistkick-product-system}/tests/web_terminal.test.ts +189 -150
  63. package/templates/skills/{product-bootstrap → assistkick-bootstrap}/SKILL.md +36 -28
  64. package/templates/skills/{product-code-reviewer → assistkick-code-reviewer}/SKILL.md +26 -18
  65. package/templates/skills/assistkick-db-explorer/SKILL.md +86 -0
  66. package/templates/skills/{product-debugger → assistkick-debugger}/SKILL.md +35 -27
  67. package/templates/skills/{product-developer → assistkick-developer}/SKILL.md +40 -32
  68. package/templates/skills/{product-interview → assistkick-interview}/SKILL.md +37 -29
  69. package/templates/product-system/packages/backend/src/routes/pipeline.ts +0 -41
  70. package/templates/product-system/packages/backend/src/services/init.ts +0 -80
  71. package/templates/product-system/packages/backend/src/services/pty_session_manager.test.ts +0 -88
  72. package/templates/product-system/packages/frontend/src/components/TerminalView.tsx +0 -200
  73. package/templates/product-system/packages/frontend/vite.config.ts +0 -20
  74. package/templates/product-system/packages/shared/db/migrations/meta/_journal.json +0 -13
  75. /package/templates/{product-system → assistkick-product-system}/.env.example +0 -0
  76. /package/templates/{product-system → assistkick-product-system}/packages/backend/src/middleware/auth_middleware.test.ts +0 -0
  77. /package/templates/{product-system → assistkick-product-system}/packages/backend/src/middleware/auth_middleware.ts +0 -0
  78. /package/templates/{product-system → assistkick-product-system}/packages/backend/src/routes/projects.ts +0 -0
  79. /package/templates/{product-system → assistkick-product-system}/packages/backend/src/routes/users.ts +0 -0
  80. /package/templates/{product-system → assistkick-product-system}/packages/backend/src/services/auth_service.test.ts +0 -0
  81. /package/templates/{product-system → assistkick-product-system}/packages/backend/src/services/auth_service.ts +0 -0
  82. /package/templates/{product-system → assistkick-product-system}/packages/backend/src/services/email_service.ts +0 -0
  83. /package/templates/{product-system → assistkick-product-system}/packages/backend/src/services/invitation_service.test.ts +0 -0
  84. /package/templates/{product-system → assistkick-product-system}/packages/backend/src/services/password_reset_service.test.ts +0 -0
  85. /package/templates/{product-system → assistkick-product-system}/packages/backend/src/services/project_service.test.ts +0 -0
  86. /package/templates/{product-system → assistkick-product-system}/packages/backend/src/services/user_management_service.test.ts +0 -0
  87. /package/templates/{product-system → assistkick-product-system}/packages/backend/tsconfig.json +0 -0
  88. /package/templates/{product-system → assistkick-product-system}/packages/frontend/index.html +0 -0
  89. /package/templates/{product-system → assistkick-product-system}/packages/frontend/package-lock.json +0 -0
  90. /package/templates/{product-system → assistkick-product-system}/packages/frontend/public/favicon.svg +0 -0
  91. /package/templates/{product-system → assistkick-product-system}/packages/frontend/src/api/client_projects.test.ts +0 -0
  92. /package/templates/{product-system → assistkick-product-system}/packages/frontend/src/api/client_refresh.test.ts +0 -0
  93. /package/templates/{product-system → assistkick-product-system}/packages/frontend/src/components/CoherenceView.tsx +0 -0
  94. /package/templates/{product-system → assistkick-product-system}/packages/frontend/src/components/GraphLegend.tsx +0 -0
  95. /package/templates/{product-system → assistkick-product-system}/packages/frontend/src/components/GraphSettings.tsx +0 -0
  96. /package/templates/{product-system → assistkick-product-system}/packages/frontend/src/components/GraphView.tsx +0 -0
  97. /package/templates/{product-system → assistkick-product-system}/packages/frontend/src/components/InviteUserDialog.tsx +0 -0
  98. /package/templates/{product-system → assistkick-product-system}/packages/frontend/src/components/LoginPage.tsx +0 -0
  99. /package/templates/{product-system → assistkick-product-system}/packages/frontend/src/components/QaIssueSheet.tsx +0 -0
  100. /package/templates/{product-system → assistkick-product-system}/packages/frontend/src/components/SidePanel.tsx +0 -0
  101. /package/templates/{product-system → assistkick-product-system}/packages/frontend/src/components/UsersView.tsx +0 -0
  102. /package/templates/{product-system → assistkick-product-system}/packages/frontend/src/hooks/useAuth.tsx +0 -0
  103. /package/templates/{product-system → assistkick-product-system}/packages/frontend/src/hooks/useGraph.ts +0 -0
  104. /package/templates/{product-system → assistkick-product-system}/packages/frontend/src/hooks/useKanban.ts +0 -0
  105. /package/templates/{product-system → assistkick-product-system}/packages/frontend/src/hooks/useTheme.ts +0 -0
  106. /package/templates/{product-system → assistkick-product-system}/packages/frontend/src/hooks/useToast.tsx +0 -0
  107. /package/templates/{product-system → assistkick-product-system}/packages/frontend/src/hooks/use_projects_logic.test.ts +0 -0
  108. /package/templates/{product-system → assistkick-product-system}/packages/frontend/src/main.tsx +0 -0
  109. /package/templates/{product-system → assistkick-product-system}/packages/frontend/src/pages/accept_invitation_page.tsx +0 -0
  110. /package/templates/{product-system → assistkick-product-system}/packages/frontend/src/pages/forgot_password_page.tsx +0 -0
  111. /package/templates/{product-system → assistkick-product-system}/packages/frontend/src/pages/register_page.tsx +0 -0
  112. /package/templates/{product-system → assistkick-product-system}/packages/frontend/src/pages/reset_password_page.tsx +0 -0
  113. /package/templates/{product-system → assistkick-product-system}/packages/frontend/src/routes/ProtectedRoute.tsx +0 -0
  114. /package/templates/{product-system → assistkick-product-system}/packages/frontend/src/routes/accept_invitation.tsx +0 -0
  115. /package/templates/{product-system → assistkick-product-system}/packages/frontend/src/routes/forgot_password.tsx +0 -0
  116. /package/templates/{product-system → assistkick-product-system}/packages/frontend/src/routes/login.tsx +0 -0
  117. /package/templates/{product-system → assistkick-product-system}/packages/frontend/src/routes/register.tsx +0 -0
  118. /package/templates/{product-system → assistkick-product-system}/packages/frontend/src/routes/reset_password.tsx +0 -0
  119. /package/templates/{product-system → assistkick-product-system}/packages/frontend/src/utils/auth_validation.test.ts +0 -0
  120. /package/templates/{product-system → assistkick-product-system}/packages/frontend/src/utils/auth_validation.ts +0 -0
  121. /package/templates/{product-system → assistkick-product-system}/packages/frontend/src/utils/login_validation.test.ts +0 -0
  122. /package/templates/{product-system → assistkick-product-system}/packages/frontend/src/utils/login_validation.ts +0 -0
  123. /package/templates/{product-system → assistkick-product-system}/packages/frontend/src/utils/logout.test.ts +0 -0
  124. /package/templates/{product-system → assistkick-product-system}/packages/frontend/src/utils/node_sizing.test.ts +0 -0
  125. /package/templates/{product-system → assistkick-product-system}/packages/frontend/src/utils/node_sizing.ts +0 -0
  126. /package/templates/{product-system → assistkick-product-system}/packages/frontend/src/utils/task_status.test.ts +0 -0
  127. /package/templates/{product-system → assistkick-product-system}/packages/frontend/src/utils/task_status.ts +0 -0
  128. /package/templates/{product-system → assistkick-product-system}/packages/frontend/tsconfig.json +0 -0
  129. /package/templates/{product-system → assistkick-product-system}/packages/shared/.env.example +0 -0
  130. /package/templates/{product-system → assistkick-product-system}/packages/shared/README.md +0 -0
  131. /package/templates/{product-system → assistkick-product-system}/packages/shared/db/migrate.ts +0 -0
  132. /package/templates/{product-system → assistkick-product-system}/packages/shared/db/migrations/0000_dashing_gorgon.sql +0 -0
  133. /package/templates/{product-system → assistkick-product-system}/packages/shared/db/migrations/meta/0000_snapshot.json +0 -0
  134. /package/templates/{product-system → assistkick-product-system}/packages/shared/drizzle.config.js +0 -0
  135. /package/templates/{product-system → assistkick-product-system}/packages/shared/lib/coherence.ts +0 -0
  136. /package/templates/{product-system → assistkick-product-system}/packages/shared/lib/completeness.ts +0 -0
  137. /package/templates/{product-system → assistkick-product-system}/packages/shared/lib/constants.ts +0 -0
  138. /package/templates/{product-system → assistkick-product-system}/packages/shared/lib/graph.ts +0 -0
  139. /package/templates/{product-system → assistkick-product-system}/packages/shared/lib/kanban.ts +0 -0
  140. /package/templates/{product-system → assistkick-product-system}/packages/shared/lib/markdown.ts +0 -0
  141. /package/templates/{product-system → assistkick-product-system}/packages/shared/lib/relevance_search.ts +0 -0
  142. /package/templates/{product-system → assistkick-product-system}/packages/shared/lib/session.ts +0 -0
  143. /package/templates/{product-system → assistkick-product-system}/packages/shared/lib/validator.ts +0 -0
  144. /package/templates/{product-system → assistkick-product-system}/packages/shared/lib/work_summary_parser.ts +0 -0
  145. /package/templates/{product-system → assistkick-product-system}/packages/shared/scripts/assign-project.ts +0 -0
  146. /package/templates/{product-system → assistkick-product-system}/packages/shared/tools/add_edge.ts +0 -0
  147. /package/templates/{product-system → assistkick-product-system}/packages/shared/tools/add_node.ts +0 -0
  148. /package/templates/{product-system → assistkick-product-system}/packages/shared/tools/end_session.ts +0 -0
  149. /package/templates/{product-system → assistkick-product-system}/packages/shared/tools/get_gaps.ts +0 -0
  150. /package/templates/{product-system → assistkick-product-system}/packages/shared/tools/get_node.ts +0 -0
  151. /package/templates/{product-system → assistkick-product-system}/packages/shared/tools/get_status.ts +0 -0
  152. /package/templates/{product-system → assistkick-product-system}/packages/shared/tools/migrate_to_turso.ts +0 -0
  153. /package/templates/{product-system → assistkick-product-system}/packages/shared/tools/rebuild_index.ts +0 -0
  154. /package/templates/{product-system → assistkick-product-system}/packages/shared/tools/remove_edge.ts +0 -0
  155. /package/templates/{product-system → assistkick-product-system}/packages/shared/tools/remove_node.ts +0 -0
  156. /package/templates/{product-system → assistkick-product-system}/packages/shared/tools/resolve_question.ts +0 -0
  157. /package/templates/{product-system → assistkick-product-system}/packages/shared/tools/search_nodes.ts +0 -0
  158. /package/templates/{product-system → assistkick-product-system}/packages/shared/tools/start_session.ts +0 -0
  159. /package/templates/{product-system → assistkick-product-system}/packages/shared/tsconfig.json +0 -0
  160. /package/templates/{product-system → assistkick-product-system}/pnpm-workspace.yaml +0 -0
  161. /package/templates/{product-system → assistkick-product-system}/smoke_test.ts +0 -0
  162. /package/templates/{product-system → assistkick-product-system}/tests/coherence_review.test.ts +0 -0
  163. /package/templates/{product-system → assistkick-product-system}/tests/edge_type_color_coding.test.ts +0 -0
  164. /package/templates/{product-system → assistkick-product-system}/tests/emit-tool-use-events.test.ts +0 -0
  165. /package/templates/{product-system → assistkick-product-system}/tests/feature_kind.test.ts +0 -0
  166. /package/templates/{product-system → assistkick-product-system}/tests/gap_indicators.test.ts +0 -0
  167. /package/templates/{product-system → assistkick-product-system}/tests/graceful_init.test.ts +0 -0
  168. /package/templates/{product-system → assistkick-product-system}/tests/graph_legend.test.ts +0 -0
  169. /package/templates/{product-system → assistkick-product-system}/tests/graph_settings_sheet.test.ts +0 -0
  170. /package/templates/{product-system → assistkick-product-system}/tests/hide_defined_filter.test.ts +0 -0
  171. /package/templates/{product-system → assistkick-product-system}/tests/neighborhood_focus.test.ts +0 -0
  172. /package/templates/{product-system → assistkick-product-system}/tests/node_search.test.ts +0 -0
  173. /package/templates/{product-system → assistkick-product-system}/tests/node_sizing.test.ts +0 -0
  174. /package/templates/{product-system → assistkick-product-system}/tests/node_type_toggle_filters.test.ts +0 -0
  175. /package/templates/{product-system → assistkick-product-system}/tests/node_type_visual_encoding.test.ts +0 -0
  176. /package/templates/{product-system → assistkick-product-system}/tests/pipeline-state-store.test.ts +0 -0
  177. /package/templates/{product-system → assistkick-product-system}/tests/pipeline-unit.test.ts +0 -0
  178. /package/templates/{product-system → assistkick-product-system}/tests/pipeline.test.ts +0 -0
  179. /package/templates/{product-system → assistkick-product-system}/tests/play_all.test.ts +0 -0
  180. /package/templates/{product-system → assistkick-product-system}/tests/qa_issue_sheet.test.ts +0 -0
  181. /package/templates/{product-system → assistkick-product-system}/tests/relevance_search.test.ts +0 -0
  182. /package/templates/{product-system → assistkick-product-system}/tests/search_reorder.test.ts +0 -0
  183. /package/templates/{product-system → assistkick-product-system}/tests/serve_ui.test.ts +0 -0
  184. /package/templates/{product-system → assistkick-product-system}/tests/serve_ui_drizzle.test.ts +0 -0
  185. /package/templates/{product-system → assistkick-product-system}/tests/session_context_recall.test.ts +0 -0
  186. /package/templates/{product-system → assistkick-product-system}/tests/side_panel.test.ts +0 -0
  187. /package/templates/{product-system → assistkick-product-system}/tests/spec_completeness_label.test.ts +0 -0
  188. /package/templates/{product-system → assistkick-product-system}/tests/url_routing_test.ts +0 -0
  189. /package/templates/{product-system → assistkick-product-system}/tests/user_login.test.ts +0 -0
  190. /package/templates/{product-system → assistkick-product-system}/tests/user_registration.test.ts +0 -0
  191. /package/templates/{product-system → assistkick-product-system}/tests/work_summary.test.ts +0 -0
  192. /package/templates/{product-system → assistkick-product-system}/tests/zoom_pan.test.ts +0 -0
  193. /package/templates/{product-system → assistkick-product-system}/tsconfig.json +0 -0
  194. /package/templates/skills/{product-debugger → assistkick-debugger}/references/agent-browser.md +0 -0
@@ -0,0 +1,333 @@
1
+ /**
2
+ * Terminal view — multi-session manager with chat-like layout.
3
+ * Left sidebar lists all terminal sessions; right panel shows the active session's terminal.
4
+ * Each session is permanently bound to a project chosen at creation time.
5
+ * Admin-only access.
6
+ */
7
+
8
+ import React, { useEffect, useRef, useCallback, useState } from 'react';
9
+ import { Terminal } from '@xterm/xterm';
10
+ import { FitAddon } from '@xterm/addon-fit';
11
+ import { WebLinksAddon } from '@xterm/addon-web-links';
12
+ import '@xterm/xterm/css/xterm.css';
13
+ import { apiClient } from '../api/client';
14
+ import type { Project } from '../hooks/useProjects';
15
+
16
+ interface TerminalViewProps {
17
+ visible: boolean;
18
+ projects: Project[];
19
+ }
20
+
21
+ interface SessionInfo {
22
+ id: string;
23
+ name: string;
24
+ projectId: string;
25
+ projectName: string;
26
+ state: 'idle' | 'running';
27
+ createdAt: string;
28
+ }
29
+
30
+ type ConnectionStatus = 'connecting' | 'connected' | 'disconnected' | 'error';
31
+
32
+ interface ActiveConnection {
33
+ sessionId: string;
34
+ ws: WebSocket;
35
+ terminal: Terminal;
36
+ fitAddon: FitAddon;
37
+ }
38
+
39
+ export function TerminalView({ visible, projects }: TerminalViewProps) {
40
+ const containerRef = useRef<HTMLDivElement>(null);
41
+ const connectionRef = useRef<ActiveConnection | null>(null);
42
+ const [sessions, setSessions] = useState<SessionInfo[]>([]);
43
+ const [activeSessionId, setActiveSessionId] = useState<string | null>(null);
44
+ const [status, setStatus] = useState<ConnectionStatus>('disconnected');
45
+ const [errorMsg, setErrorMsg] = useState('');
46
+ const [newSessionProjectId, setNewSessionProjectId] = useState<string>('');
47
+ const [creating, setCreating] = useState(false);
48
+
49
+ // Default new session project picker to first project
50
+ useEffect(() => {
51
+ if (projects.length > 0 && !newSessionProjectId) {
52
+ setNewSessionProjectId(projects[0].id);
53
+ }
54
+ }, [projects, newSessionProjectId]);
55
+
56
+ const fetchSessions = useCallback(async () => {
57
+ try {
58
+ const data = await apiClient.listTerminalSessions();
59
+ setSessions(data.sessions);
60
+ return data.sessions as SessionInfo[];
61
+ } catch {
62
+ return [] as SessionInfo[];
63
+ }
64
+ }, []);
65
+
66
+ // Load sessions when tab becomes visible
67
+ useEffect(() => {
68
+ if (visible) {
69
+ fetchSessions();
70
+ }
71
+ }, [visible, fetchSessions]);
72
+
73
+ const disconnectCurrent = useCallback(() => {
74
+ const conn = connectionRef.current;
75
+ if (!conn) return;
76
+ conn.ws.close();
77
+ conn.terminal.dispose();
78
+ connectionRef.current = null;
79
+ setStatus('disconnected');
80
+ setErrorMsg('');
81
+ }, []);
82
+
83
+ const connectToSession = useCallback((sessionId: string) => {
84
+ disconnectCurrent();
85
+
86
+ if (!containerRef.current) return;
87
+
88
+ const terminal = new Terminal({
89
+ cursorBlink: true,
90
+ fontSize: 14,
91
+ fontFamily: 'Menlo, Monaco, "Courier New", monospace',
92
+ theme: {
93
+ background: '#1e1e2e',
94
+ foreground: '#cdd6f4',
95
+ cursor: '#f5e0dc',
96
+ selectionBackground: '#585b7066',
97
+ black: '#45475a',
98
+ red: '#f38ba8',
99
+ green: '#a6e3a1',
100
+ yellow: '#f9e2af',
101
+ blue: '#89b4fa',
102
+ magenta: '#f5c2e7',
103
+ cyan: '#94e2d5',
104
+ white: '#bac2de',
105
+ brightBlack: '#585b70',
106
+ brightRed: '#f38ba8',
107
+ brightGreen: '#a6e3a1',
108
+ brightYellow: '#f9e2af',
109
+ brightBlue: '#89b4fa',
110
+ brightMagenta: '#f5c2e7',
111
+ brightCyan: '#94e2d5',
112
+ brightWhite: '#a6adc8',
113
+ },
114
+ });
115
+
116
+ const fitAddon = new FitAddon();
117
+ const webLinksAddon = new WebLinksAddon();
118
+ terminal.loadAddon(fitAddon);
119
+ terminal.loadAddon(webLinksAddon);
120
+ terminal.open(containerRef.current);
121
+ fitAddon.fit();
122
+
123
+ const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
124
+ const wsUrl = `${protocol}//${window.location.host}/api/terminal?sessionId=${encodeURIComponent(sessionId)}`;
125
+
126
+ setStatus('connecting');
127
+ setErrorMsg('');
128
+
129
+ const ws = new WebSocket(wsUrl);
130
+ connectionRef.current = { sessionId, ws, terminal, fitAddon };
131
+
132
+ ws.onopen = () => {
133
+ setStatus('connected');
134
+ fitAddon.fit();
135
+ const { cols, rows } = terminal;
136
+ ws.send(JSON.stringify({ type: 'resize', cols, rows }));
137
+ };
138
+
139
+ ws.onmessage = (event) => {
140
+ try {
141
+ const msg = JSON.parse(event.data);
142
+ if (msg.type === 'output') {
143
+ terminal.write(msg.data);
144
+ }
145
+ } catch {
146
+ // Ignore malformed messages
147
+ }
148
+ };
149
+
150
+ ws.onclose = (event) => {
151
+ if (connectionRef.current?.sessionId === sessionId) {
152
+ connectionRef.current = null;
153
+ }
154
+ if (event.code === 4001) {
155
+ setStatus('error');
156
+ setErrorMsg('Authentication required. Please log in.');
157
+ } else if (event.code === 4003) {
158
+ setStatus('error');
159
+ setErrorMsg('Access denied. Admin privileges required.');
160
+ } else if (event.code === 4004) {
161
+ setStatus('error');
162
+ setErrorMsg('Session not found. It may have been killed.');
163
+ fetchSessions();
164
+ } else {
165
+ setStatus('disconnected');
166
+ }
167
+ };
168
+
169
+ ws.onerror = () => {
170
+ setStatus('error');
171
+ setErrorMsg('Failed to connect to terminal session.');
172
+ if (connectionRef.current?.sessionId === sessionId) {
173
+ connectionRef.current = null;
174
+ }
175
+ };
176
+
177
+ // Forward user input to WebSocket
178
+ terminal.onData((data) => {
179
+ if (ws.readyState === WebSocket.OPEN) {
180
+ ws.send(JSON.stringify({ type: 'input', data }));
181
+ }
182
+ });
183
+ }, [disconnectCurrent, fetchSessions]);
184
+
185
+ const handleSelectSession = useCallback((sessionId: string) => {
186
+ setActiveSessionId(sessionId);
187
+ connectToSession(sessionId);
188
+ }, [connectToSession]);
189
+
190
+ const handleCreateSession = useCallback(async () => {
191
+ const projectId = newSessionProjectId || projects[0]?.id;
192
+ if (!projectId) return;
193
+
194
+ const project = projects.find(p => p.id === projectId) || projects[0];
195
+ if (!project) return;
196
+
197
+ setCreating(true);
198
+ try {
199
+ const data = await apiClient.createTerminalSession(project.id, project.name);
200
+ const newSession: SessionInfo = data.session;
201
+ const updated = await fetchSessions();
202
+ const created = updated.find(s => s.id === newSession.id) || newSession;
203
+ setActiveSessionId(created.id);
204
+ connectToSession(created.id);
205
+ } catch {
206
+ // silently ignore — session list stays as-is
207
+ } finally {
208
+ setCreating(false);
209
+ }
210
+ }, [newSessionProjectId, projects, fetchSessions, connectToSession]);
211
+
212
+ const handleKillSession = useCallback(async (sessionId: string, e: React.MouseEvent) => {
213
+ e.stopPropagation();
214
+ try {
215
+ await apiClient.killTerminalSession(sessionId);
216
+ if (activeSessionId === sessionId) {
217
+ disconnectCurrent();
218
+ setActiveSessionId(null);
219
+ }
220
+ await fetchSessions();
221
+ } catch {
222
+ // silently ignore
223
+ }
224
+ }, [activeSessionId, disconnectCurrent, fetchSessions]);
225
+
226
+ // Handle window resize — refit when visible
227
+ useEffect(() => {
228
+ if (!visible) return;
229
+
230
+ const handleResize = () => {
231
+ const conn = connectionRef.current;
232
+ if (!conn) return;
233
+ conn.fitAddon.fit();
234
+ const { cols, rows } = conn.terminal;
235
+ if (conn.ws.readyState === WebSocket.OPEN) {
236
+ conn.ws.send(JSON.stringify({ type: 'resize', cols, rows }));
237
+ }
238
+ };
239
+
240
+ handleResize();
241
+ window.addEventListener('resize', handleResize);
242
+ return () => window.removeEventListener('resize', handleResize);
243
+ }, [visible]);
244
+
245
+ // Clean up WebSocket on unmount
246
+ useEffect(() => {
247
+ return () => {
248
+ disconnectCurrent();
249
+ };
250
+ }, [disconnectCurrent]);
251
+
252
+ const activeSession = sessions.find(s => s.id === activeSessionId);
253
+
254
+ return (
255
+ <div className="terminal-view" style={{ display: visible ? 'flex' : 'none' }}>
256
+ {/* Left sidebar — session list */}
257
+ <div className="terminal-sidebar">
258
+ <div className="terminal-sidebar-header">Sessions</div>
259
+ <div className="terminal-session-list">
260
+ {sessions.length === 0 && (
261
+ <div className="terminal-no-sessions">No sessions yet</div>
262
+ )}
263
+ {sessions.map(session => (
264
+ <div
265
+ key={session.id}
266
+ className={`terminal-session-item${session.id === activeSessionId ? ' terminal-session-item--active' : ''}`}
267
+ onClick={() => handleSelectSession(session.id)}
268
+ >
269
+ <div className="terminal-session-name">{session.name}</div>
270
+ <div className="terminal-session-project">{session.projectName}</div>
271
+ <button
272
+ className="terminal-session-kill"
273
+ onClick={(e) => handleKillSession(session.id, e)}
274
+ title="Kill session"
275
+ >
276
+
277
+ </button>
278
+ </div>
279
+ ))}
280
+ </div>
281
+
282
+ {/* New session controls */}
283
+ <div className="terminal-new-session">
284
+ <select
285
+ className="terminal-project-picker"
286
+ value={newSessionProjectId}
287
+ onChange={e => setNewSessionProjectId(e.target.value)}
288
+ disabled={creating || projects.length === 0}
289
+ >
290
+ {projects.map(p => (
291
+ <option key={p.id} value={p.id}>{p.name}</option>
292
+ ))}
293
+ </select>
294
+ <button
295
+ className="terminal-new-session-btn"
296
+ onClick={handleCreateSession}
297
+ disabled={creating || projects.length === 0}
298
+ >
299
+ {creating ? '…' : '+ New'}
300
+ </button>
301
+ </div>
302
+ </div>
303
+
304
+ {/* Right panel — terminal content */}
305
+ <div className="terminal-panel">
306
+ {!activeSessionId && (
307
+ <div className="terminal-empty-state">
308
+ Select a session or create a new one
309
+ </div>
310
+ )}
311
+ {activeSession && errorMsg && (
312
+ <div className="terminal-error">
313
+ <span>{errorMsg}</span>
314
+ <button
315
+ className="terminal-reconnect-btn"
316
+ onClick={() => connectToSession(activeSessionId!)}
317
+ >
318
+ Reconnect
319
+ </button>
320
+ </div>
321
+ )}
322
+ {activeSession && status === 'connecting' && (
323
+ <div className="terminal-status">Connecting…</div>
324
+ )}
325
+ <div
326
+ ref={containerRef}
327
+ className="terminal-container"
328
+ style={{ display: activeSessionId ? 'flex' : 'none' }}
329
+ />
330
+ </div>
331
+ </div>
332
+ );
333
+ }
@@ -19,32 +19,24 @@ interface ToolbarProps {
19
19
  onProjectCreate: (name: string) => Promise<any>;
20
20
  onProjectRename: (id: string, name: string) => Promise<void>;
21
21
  onProjectArchive: (id: string) => Promise<void>;
22
+ onOpenGitModal?: (project: Project) => void;
22
23
  }
23
24
 
24
25
  export function Toolbar({
25
26
  activeTab, onTabChange, completeness, onFit,
26
27
  onSettingsToggle, settingsOpen, theme, onThemeToggle, isAdmin, onLogout,
27
- projects, selectedProjectId, onProjectSelect, onProjectCreate, onProjectRename, onProjectArchive,
28
+ projects, selectedProjectId, onProjectSelect, onProjectCreate, onProjectRename, onProjectArchive, onOpenGitModal,
28
29
  }: ToolbarProps) {
29
- const tabs = ['graph', 'kanban', 'coherence'];
30
+ const tabs = ['kanban', 'terminal', 'graph', 'coherence'];
30
31
  if (isAdmin) {
31
32
  tabs.push('users');
32
- tabs.push('terminal');
33
33
  }
34
34
 
35
+ const tabLabels: Record<string, string> = { terminal: 'Chat' };
36
+
35
37
  return (
36
38
  <div className="toolbar">
37
39
  <div className="tab-bar">
38
- {tabs.map(tab => (
39
- <button
40
- key={tab}
41
- className={`tab-btn${activeTab === tab ? ' active' : ''}`}
42
- onClick={() => onTabChange(tab)}
43
- >
44
- {tab.charAt(0).toUpperCase() + tab.slice(1)}
45
- </button>
46
- ))}
47
- <div className="tab-bar-separator" />
48
40
  <ProjectSelector
49
41
  projects={projects}
50
42
  selectedProjectId={selectedProjectId}
@@ -52,7 +44,17 @@ export function Toolbar({
52
44
  onCreate={onProjectCreate}
53
45
  onRename={onProjectRename}
54
46
  onArchive={onProjectArchive}
47
+ onOpenGitModal={onOpenGitModal}
55
48
  />
49
+ {tabs.map(tab => (
50
+ <button
51
+ key={tab}
52
+ className={`tab-btn${activeTab === tab ? ' active' : ''}`}
53
+ onClick={() => onTabChange(tab)}
54
+ >
55
+ {tabLabels[tab] || tab.charAt(0).toUpperCase() + tab.slice(1)}
56
+ </button>
57
+ ))}
56
58
  </div>
57
59
 
58
60
  <div className="toolbar-spacer" />
@@ -169,6 +169,7 @@ export const ALL_NODE_TYPES = [
169
169
  ];
170
170
 
171
171
  export const COLUMNS = [
172
+ { id: 'backlog', label: 'Backlog' },
172
173
  { id: 'todo', label: 'Todo' },
173
174
  { id: 'in_progress', label: 'In Progress' },
174
175
  { id: 'in_review', label: 'In Review' },
@@ -8,6 +8,10 @@ export interface Project {
8
8
  name: string;
9
9
  isDefault: number;
10
10
  archivedAt: string | null;
11
+ repoUrl: string | null;
12
+ githubInstallationId: string | null;
13
+ githubRepoFullName: string | null;
14
+ baseBranch: string | null;
11
15
  createdAt: string;
12
16
  updatedAt: string;
13
17
  }
@@ -11,6 +11,7 @@ import { TerminalView } from '../components/TerminalView';
11
11
  import { UsersView } from '../components/UsersView';
12
12
  import { SidePanel, openSidePanel, closeSidePanel } from '../components/SidePanel';
13
13
  import { QaIssueSheet } from '../components/QaIssueSheet';
14
+ import { GitRepoModal } from '../components/GitRepoModal';
14
15
  import { useTheme } from '../hooks/useTheme';
15
16
  import { useGraph } from '../hooks/useGraph';
16
17
  import { useAuth } from '../hooks/useAuth';
@@ -22,7 +23,7 @@ const VALID_TABS = new Set(['graph', 'kanban', 'coherence', 'users', 'terminal']
22
23
 
23
24
  function tabFromPath(pathname: string): string {
24
25
  const seg = pathname.replace(/^\//, '');
25
- return VALID_TABS.has(seg) ? seg : 'graph';
26
+ return VALID_TABS.has(seg) ? seg : 'kanban';
26
27
  }
27
28
 
28
29
  export function DashboardRoute() {
@@ -33,7 +34,7 @@ export function DashboardRoute() {
33
34
  const isAdmin = user?.role === 'admin';
34
35
  const {
35
36
  projects, selectedProjectId, selectProject,
36
- createProject, renameProject, archiveProject,
37
+ createProject, renameProject, archiveProject, refetchProjects,
37
38
  } = useProjects();
38
39
  const { graphData, error: graphError } = useGraph(selectedProjectId);
39
40
  const graphRef = useRef<GraphViewHandle>(null);
@@ -42,6 +43,13 @@ export function DashboardRoute() {
42
43
  const [legendVisible, setLegendVisible] = useState(true);
43
44
  const [hiddenTypes, setHiddenTypes] = useState<Set<string>>(new Set());
44
45
 
46
+ // Git Repo Modal state
47
+ const [gitModalProject, setGitModalProject] = useState<any>(null);
48
+
49
+ const handleOpenGitModal = useCallback((project: any) => {
50
+ setGitModalProject(project);
51
+ }, []);
52
+
45
53
  // QA Issue Sheet state
46
54
  const [qaOpen, setQaOpen] = useState(false);
47
55
  const [qaFeatureId, setQaFeatureId] = useState<string | null>(null);
@@ -146,6 +154,7 @@ export function DashboardRoute() {
146
154
  onProjectCreate={createProject}
147
155
  onProjectRename={renameProject}
148
156
  onProjectArchive={archiveProject}
157
+ onOpenGitModal={handleOpenGitModal}
149
158
  />
150
159
 
151
160
  <div className="main-content">
@@ -196,8 +205,8 @@ export function DashboardRoute() {
196
205
  {/* Users View (admin-only) */}
197
206
  {isAdmin && <UsersView visible={activeTab === 'users'} />}
198
207
 
199
- {/* Terminal View (admin-only) */}
200
- {isAdmin && <TerminalView visible={activeTab === 'terminal'} />}
208
+ {/* Chat View */}
209
+ <TerminalView visible={activeTab === 'terminal'} projects={projects} />
201
210
 
202
211
  {/* Side Panel */}
203
212
  <SidePanel graphData={graphData} onEdgeClick={handleEdgeClick} />
@@ -215,6 +224,15 @@ export function DashboardRoute() {
215
224
  />
216
225
  </div>
217
226
 
227
+ {/* Git Repo Modal */}
228
+ {gitModalProject && (
229
+ <GitRepoModal
230
+ project={gitModalProject}
231
+ onClose={() => setGitModalProject(null)}
232
+ onProjectUpdated={refetchProjects}
233
+ />
234
+ )}
235
+
218
236
  </div>
219
237
  </ToastProvider>
220
238
  );