@assistkick/create 1.2.0 → 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 (49) hide show
  1. package/package.json +2 -1
  2. package/templates/assistkick-product-system/packages/backend/src/routes/git.ts +231 -0
  3. package/templates/assistkick-product-system/packages/backend/src/routes/kanban.ts +4 -4
  4. package/templates/assistkick-product-system/packages/backend/src/routes/pipeline.ts +49 -2
  5. package/templates/assistkick-product-system/packages/backend/src/routes/terminal.ts +82 -0
  6. package/templates/assistkick-product-system/packages/backend/src/server.ts +19 -6
  7. package/templates/assistkick-product-system/packages/backend/src/services/github_app_service.ts +146 -0
  8. package/templates/assistkick-product-system/packages/backend/src/services/init.ts +69 -2
  9. package/templates/assistkick-product-system/packages/backend/src/services/project_service.ts +71 -0
  10. package/templates/assistkick-product-system/packages/backend/src/services/project_workspace_service.test.ts +87 -0
  11. package/templates/assistkick-product-system/packages/backend/src/services/project_workspace_service.ts +194 -0
  12. package/templates/assistkick-product-system/packages/backend/src/services/pty_session_manager.test.ts +88 -17
  13. package/templates/assistkick-product-system/packages/backend/src/services/pty_session_manager.ts +114 -39
  14. package/templates/assistkick-product-system/packages/backend/src/services/terminal_ws_handler.ts +28 -14
  15. package/templates/assistkick-product-system/packages/frontend/src/App.tsx +1 -1
  16. package/templates/assistkick-product-system/packages/frontend/src/api/client.ts +151 -0
  17. package/templates/assistkick-product-system/packages/frontend/src/components/GitRepoModal.tsx +352 -0
  18. package/templates/assistkick-product-system/packages/frontend/src/components/KanbanView.tsx +208 -95
  19. package/templates/assistkick-product-system/packages/frontend/src/components/ProjectSelector.tsx +17 -1
  20. package/templates/assistkick-product-system/packages/frontend/src/components/TerminalView.tsx +238 -105
  21. package/templates/assistkick-product-system/packages/frontend/src/components/Toolbar.tsx +15 -13
  22. package/templates/assistkick-product-system/packages/frontend/src/constants/graph.ts +1 -0
  23. package/templates/assistkick-product-system/packages/frontend/src/hooks/useProjects.ts +4 -0
  24. package/templates/assistkick-product-system/packages/frontend/src/routes/dashboard.tsx +22 -4
  25. package/templates/assistkick-product-system/packages/frontend/src/styles/index.css +486 -38
  26. package/templates/assistkick-product-system/packages/shared/db/migrations/0001_vengeful_wallop.sql +1 -0
  27. package/templates/assistkick-product-system/packages/shared/db/migrations/0002_greedy_excalibur.sql +4 -0
  28. package/templates/assistkick-product-system/packages/shared/db/migrations/meta/0001_snapshot.json +826 -0
  29. package/templates/assistkick-product-system/packages/shared/db/migrations/meta/0002_snapshot.json +854 -0
  30. package/templates/assistkick-product-system/packages/shared/db/migrations/meta/_journal.json +14 -0
  31. package/templates/assistkick-product-system/packages/shared/db/schema.ts +5 -0
  32. package/templates/assistkick-product-system/packages/shared/lib/claude-service.ts +54 -1
  33. package/templates/assistkick-product-system/packages/shared/lib/git_workflow.ts +25 -0
  34. package/templates/assistkick-product-system/packages/shared/lib/pipeline-state-store.ts +4 -0
  35. package/templates/assistkick-product-system/packages/shared/lib/pipeline.ts +329 -89
  36. package/templates/assistkick-product-system/packages/shared/lib/pipeline_orchestrator.ts +186 -0
  37. package/templates/assistkick-product-system/packages/shared/tools/db_explorer.ts +275 -0
  38. package/templates/assistkick-product-system/packages/shared/tools/get_kanban.ts +2 -1
  39. package/templates/assistkick-product-system/packages/shared/tools/move_card.ts +3 -2
  40. package/templates/assistkick-product-system/packages/shared/tools/update_node.ts +2 -2
  41. package/templates/assistkick-product-system/tests/kanban.test.ts +1 -1
  42. package/templates/assistkick-product-system/tests/pipeline_stats_all_cards.test.ts +1 -1
  43. package/templates/assistkick-product-system/tests/web_terminal.test.ts +189 -150
  44. package/templates/skills/assistkick-bootstrap/SKILL.md +33 -25
  45. package/templates/skills/assistkick-code-reviewer/SKILL.md +23 -15
  46. package/templates/skills/assistkick-db-explorer/SKILL.md +86 -0
  47. package/templates/skills/assistkick-debugger/SKILL.md +30 -22
  48. package/templates/skills/assistkick-developer/SKILL.md +37 -29
  49. package/templates/skills/assistkick-interview/SKILL.md +34 -26
@@ -0,0 +1,352 @@
1
+ import React, { useState, useEffect, useCallback } from 'react';
2
+ import { apiClient } from '../api/client';
3
+ import type { Project } from '../hooks/useProjects';
4
+
5
+ interface GitRepoModalProps {
6
+ project: Project;
7
+ onClose: () => void;
8
+ onProjectUpdated: () => void;
9
+ }
10
+
11
+ interface Installation {
12
+ id: number;
13
+ account: string;
14
+ accountType: string;
15
+ }
16
+
17
+ interface Repo {
18
+ id: number;
19
+ fullName: string;
20
+ private: boolean;
21
+ defaultBranch: string;
22
+ cloneUrl: string;
23
+ }
24
+
25
+ type Tab = 'status' | 'github' | 'url' | 'init';
26
+
27
+ export function GitRepoModal({ project, onClose, onProjectUpdated }: GitRepoModalProps) {
28
+ const [tab, setTab] = useState<Tab>('status');
29
+ const [loading, setLoading] = useState(false);
30
+ const [error, setError] = useState<string | null>(null);
31
+ const [success, setSuccess] = useState<string | null>(null);
32
+
33
+ // Status tab
34
+ const [gitStatus, setGitStatus] = useState<any>(null);
35
+
36
+ // GitHub tab
37
+ const [installations, setInstallations] = useState<Installation[]>([]);
38
+ const [selectedInstallation, setSelectedInstallation] = useState<string>('');
39
+ const [repos, setRepos] = useState<Repo[]>([]);
40
+ const [selectedRepo, setSelectedRepo] = useState<string>('');
41
+ const [githubConfigured, setGithubConfigured] = useState(false);
42
+
43
+ // URL tab
44
+ const [cloneUrl, setCloneUrl] = useState('');
45
+
46
+ const loadStatus = useCallback(async () => {
47
+ try {
48
+ const data = await apiClient.getGitStatus(project.id);
49
+ setGitStatus(data);
50
+ setGithubConfigured(data.githubAppConfigured);
51
+ } catch (err: any) {
52
+ setError(err.message);
53
+ }
54
+ }, [project.id]);
55
+
56
+ useEffect(() => {
57
+ loadStatus();
58
+ }, [loadStatus]);
59
+
60
+ const handleTestConnection = async () => {
61
+ setLoading(true);
62
+ setError(null);
63
+ try {
64
+ const data = await apiClient.testGitHubApp(project.id);
65
+ if (!data.configured) {
66
+ setError(data.error || 'GitHub App not configured');
67
+ } else if (data.error) {
68
+ setError(data.error);
69
+ } else {
70
+ setInstallations(data.installations || []);
71
+ setSuccess('GitHub App connection successful');
72
+ }
73
+ } catch (err: any) {
74
+ setError(err.message);
75
+ } finally {
76
+ setLoading(false);
77
+ }
78
+ };
79
+
80
+ const handleLoadInstallations = async () => {
81
+ setLoading(true);
82
+ setError(null);
83
+ try {
84
+ const data = await apiClient.listGitHubInstallations(project.id);
85
+ setInstallations(data.installations || []);
86
+ setGithubConfigured(data.configured);
87
+ } catch (err: any) {
88
+ setError(err.message);
89
+ } finally {
90
+ setLoading(false);
91
+ }
92
+ };
93
+
94
+ const handleLoadRepos = async (installationId: string) => {
95
+ setSelectedInstallation(installationId);
96
+ setLoading(true);
97
+ setError(null);
98
+ try {
99
+ const data = await apiClient.listGitHubRepos(project.id, installationId);
100
+ setRepos(data.repos || []);
101
+ } catch (err: any) {
102
+ setError(err.message);
103
+ } finally {
104
+ setLoading(false);
105
+ }
106
+ };
107
+
108
+ const handleConnectGitHub = async () => {
109
+ if (!selectedRepo || !selectedInstallation) return;
110
+ const repo = repos.find(r => r.fullName === selectedRepo);
111
+ if (!repo) return;
112
+
113
+ setLoading(true);
114
+ setError(null);
115
+ try {
116
+ await apiClient.connectGitRepo(project.id, {
117
+ githubInstallationId: selectedInstallation,
118
+ githubRepoFullName: repo.fullName,
119
+ baseBranch: repo.defaultBranch,
120
+ });
121
+ setSuccess(`Connected to ${repo.fullName}`);
122
+ onProjectUpdated();
123
+ await loadStatus();
124
+ } catch (err: any) {
125
+ setError(err.message);
126
+ } finally {
127
+ setLoading(false);
128
+ }
129
+ };
130
+
131
+ const handleConnectUrl = async () => {
132
+ if (!cloneUrl.trim()) return;
133
+ setLoading(true);
134
+ setError(null);
135
+ try {
136
+ await apiClient.connectGitRepo(project.id, { repoUrl: cloneUrl.trim() });
137
+ setSuccess('Repository connected successfully');
138
+ onProjectUpdated();
139
+ await loadStatus();
140
+ } catch (err: any) {
141
+ setError(err.message);
142
+ } finally {
143
+ setLoading(false);
144
+ }
145
+ };
146
+
147
+ const handleInit = async () => {
148
+ setLoading(true);
149
+ setError(null);
150
+ try {
151
+ await apiClient.initGitRepo(project.id);
152
+ setSuccess('Local git repository initialized');
153
+ onProjectUpdated();
154
+ await loadStatus();
155
+ } catch (err: any) {
156
+ setError(err.message);
157
+ } finally {
158
+ setLoading(false);
159
+ }
160
+ };
161
+
162
+ const handleDisconnect = async () => {
163
+ setLoading(true);
164
+ setError(null);
165
+ try {
166
+ await apiClient.disconnectGitRepo(project.id);
167
+ setSuccess('Repository disconnected');
168
+ onProjectUpdated();
169
+ await loadStatus();
170
+ } catch (err: any) {
171
+ setError(err.message);
172
+ } finally {
173
+ setLoading(false);
174
+ }
175
+ };
176
+
177
+ return (
178
+ <div className="git-modal-overlay" onClick={onClose}>
179
+ <div className="git-modal" onClick={e => e.stopPropagation()}>
180
+ <div className="git-modal-header">
181
+ <h3>Git Repository &mdash; {project.name}</h3>
182
+ <button className="git-modal-close" onClick={onClose}>&times;</button>
183
+ </div>
184
+
185
+ {error && <div className="git-modal-error">{error}</div>}
186
+ {success && <div className="git-modal-success">{success}</div>}
187
+
188
+ <div className="git-modal-tabs">
189
+ <button className={`git-modal-tab${tab === 'status' ? ' active' : ''}`} onClick={() => { setTab('status'); setError(null); setSuccess(null); }}>Status</button>
190
+ <button className={`git-modal-tab${tab === 'github' ? ' active' : ''}`} onClick={() => { setTab('github'); setError(null); setSuccess(null); handleLoadInstallations(); }}>GitHub</button>
191
+ <button className={`git-modal-tab${tab === 'url' ? ' active' : ''}`} onClick={() => { setTab('url'); setError(null); setSuccess(null); }}>Clone URL</button>
192
+ <button className={`git-modal-tab${tab === 'init' ? ' active' : ''}`} onClick={() => { setTab('init'); setError(null); setSuccess(null); }}>Init</button>
193
+ </div>
194
+
195
+ <div className="git-modal-body">
196
+ {tab === 'status' && (
197
+ <div className="git-modal-status">
198
+ {gitStatus ? (
199
+ <>
200
+ <div className="git-status-row">
201
+ <span className="git-status-label">Workspace:</span>
202
+ <span className={`git-status-value ${gitStatus.hasWorkspace ? 'connected' : ''}`}>
203
+ {gitStatus.hasWorkspace ? 'Configured' : 'Not configured'}
204
+ </span>
205
+ </div>
206
+ {gitStatus.hasWorkspace && (
207
+ <>
208
+ <div className="git-status-row">
209
+ <span className="git-status-label">Remote:</span>
210
+ <span className={`git-status-value ${gitStatus.hasRemote ? 'connected' : ''}`}>
211
+ {gitStatus.hasRemote ? 'Connected' : 'Local only'}
212
+ </span>
213
+ </div>
214
+ <div className="git-status-row">
215
+ <span className="git-status-label">Branch:</span>
216
+ <span className="git-status-value">{gitStatus.currentBranch || '-'}</span>
217
+ </div>
218
+ </>
219
+ )}
220
+ {project.githubRepoFullName && (
221
+ <div className="git-status-row">
222
+ <span className="git-status-label">GitHub Repo:</span>
223
+ <span className="git-status-value">{project.githubRepoFullName}</span>
224
+ </div>
225
+ )}
226
+ {project.repoUrl && !project.githubRepoFullName && (
227
+ <div className="git-status-row">
228
+ <span className="git-status-label">Clone URL:</span>
229
+ <span className="git-status-value git-status-url">{project.repoUrl}</span>
230
+ </div>
231
+ )}
232
+ <div className="git-status-row">
233
+ <span className="git-status-label">GitHub App:</span>
234
+ <span className={`git-status-value ${gitStatus.githubAppConfigured ? 'connected' : ''}`}>
235
+ {gitStatus.githubAppConfigured ? 'Configured' : 'Not configured'}
236
+ </span>
237
+ </div>
238
+ {gitStatus.hasWorkspace && (
239
+ <button className="git-modal-btn git-modal-btn-danger" onClick={handleDisconnect} disabled={loading}>
240
+ Disconnect
241
+ </button>
242
+ )}
243
+ </>
244
+ ) : (
245
+ <div className="git-modal-loading">Loading...</div>
246
+ )}
247
+ </div>
248
+ )}
249
+
250
+ {tab === 'github' && (
251
+ <div className="git-modal-github">
252
+ {!githubConfigured ? (
253
+ <div className="git-modal-info">
254
+ GitHub App not configured. Set GITHUB_APP_ID and GITHUB_APP_PRIVATE_KEY environment variables.
255
+ <button className="git-modal-btn" onClick={handleTestConnection} disabled={loading}>
256
+ Test Connection
257
+ </button>
258
+ </div>
259
+ ) : (
260
+ <>
261
+ {installations.length === 0 ? (
262
+ <div className="git-modal-info">
263
+ No GitHub App installations found. Install the app on your GitHub organization or account.
264
+ </div>
265
+ ) : (
266
+ <div className="git-modal-installations">
267
+ <label className="git-modal-label">Installation:</label>
268
+ <select
269
+ className="git-modal-select"
270
+ value={selectedInstallation}
271
+ onChange={e => handleLoadRepos(e.target.value)}
272
+ >
273
+ <option value="">Select installation...</option>
274
+ {installations.map(i => (
275
+ <option key={i.id} value={String(i.id)}>
276
+ {i.account} ({i.accountType})
277
+ </option>
278
+ ))}
279
+ </select>
280
+ </div>
281
+ )}
282
+
283
+ {repos.length > 0 && (
284
+ <div className="git-modal-repos">
285
+ <label className="git-modal-label">Repository:</label>
286
+ <select
287
+ className="git-modal-select"
288
+ value={selectedRepo}
289
+ onChange={e => setSelectedRepo(e.target.value)}
290
+ >
291
+ <option value="">Select repository...</option>
292
+ {repos.map(r => (
293
+ <option key={r.id} value={r.fullName}>
294
+ {r.fullName} ({r.defaultBranch})
295
+ </option>
296
+ ))}
297
+ </select>
298
+ <button
299
+ className="git-modal-btn git-modal-btn-primary"
300
+ onClick={handleConnectGitHub}
301
+ disabled={loading || !selectedRepo}
302
+ >
303
+ {loading ? 'Connecting...' : 'Connect'}
304
+ </button>
305
+ </div>
306
+ )}
307
+ </>
308
+ )}
309
+ </div>
310
+ )}
311
+
312
+ {tab === 'url' && (
313
+ <div className="git-modal-url">
314
+ <label className="git-modal-label">Git clone URL:</label>
315
+ <input
316
+ className="git-modal-input"
317
+ type="text"
318
+ placeholder="https://github.com/org/repo.git"
319
+ value={cloneUrl}
320
+ onChange={e => setCloneUrl(e.target.value)}
321
+ onKeyDown={e => { if (e.key === 'Enter') handleConnectUrl(); }}
322
+ />
323
+ <button
324
+ className="git-modal-btn git-modal-btn-primary"
325
+ onClick={handleConnectUrl}
326
+ disabled={loading || !cloneUrl.trim()}
327
+ >
328
+ {loading ? 'Cloning...' : 'Clone & Connect'}
329
+ </button>
330
+ </div>
331
+ )}
332
+
333
+ {tab === 'init' && (
334
+ <div className="git-modal-init">
335
+ <p className="git-modal-info">
336
+ Initialize a new empty local git repository for this project.
337
+ You can connect it to a remote later.
338
+ </p>
339
+ <button
340
+ className="git-modal-btn git-modal-btn-primary"
341
+ onClick={handleInit}
342
+ disabled={loading}
343
+ >
344
+ {loading ? 'Initializing...' : 'Initialize Repository'}
345
+ </button>
346
+ </div>
347
+ )}
348
+ </div>
349
+ </div>
350
+ </div>
351
+ );
352
+ }