@assistkick/create 1.0.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 (178) hide show
  1. package/dist/bin/create.d.ts +2 -0
  2. package/dist/bin/create.js +25 -0
  3. package/dist/bin/create.js.map +1 -0
  4. package/dist/src/scaffolder.d.ts +22 -0
  5. package/dist/src/scaffolder.js +120 -0
  6. package/dist/src/scaffolder.js.map +1 -0
  7. package/package.json +24 -0
  8. package/templates/product-system/.env.example +8 -0
  9. package/templates/product-system/CLAUDE.md +45 -0
  10. package/templates/product-system/package.json +32 -0
  11. package/templates/product-system/packages/backend/package.json +37 -0
  12. package/templates/product-system/packages/backend/src/middleware/auth_middleware.test.ts +86 -0
  13. package/templates/product-system/packages/backend/src/middleware/auth_middleware.ts +35 -0
  14. package/templates/product-system/packages/backend/src/routes/auth.ts +463 -0
  15. package/templates/product-system/packages/backend/src/routes/coherence.ts +187 -0
  16. package/templates/product-system/packages/backend/src/routes/graph.ts +67 -0
  17. package/templates/product-system/packages/backend/src/routes/kanban.ts +201 -0
  18. package/templates/product-system/packages/backend/src/routes/pipeline.ts +41 -0
  19. package/templates/product-system/packages/backend/src/routes/projects.ts +122 -0
  20. package/templates/product-system/packages/backend/src/routes/users.ts +97 -0
  21. package/templates/product-system/packages/backend/src/server.ts +159 -0
  22. package/templates/product-system/packages/backend/src/services/auth_service.test.ts +115 -0
  23. package/templates/product-system/packages/backend/src/services/auth_service.ts +82 -0
  24. package/templates/product-system/packages/backend/src/services/coherence-review.ts +339 -0
  25. package/templates/product-system/packages/backend/src/services/email_service.ts +75 -0
  26. package/templates/product-system/packages/backend/src/services/init.ts +80 -0
  27. package/templates/product-system/packages/backend/src/services/invitation_service.test.ts +235 -0
  28. package/templates/product-system/packages/backend/src/services/invitation_service.ts +193 -0
  29. package/templates/product-system/packages/backend/src/services/password_reset_service.test.ts +151 -0
  30. package/templates/product-system/packages/backend/src/services/password_reset_service.ts +135 -0
  31. package/templates/product-system/packages/backend/src/services/project_service.test.ts +215 -0
  32. package/templates/product-system/packages/backend/src/services/project_service.ts +171 -0
  33. package/templates/product-system/packages/backend/src/services/pty_session_manager.test.ts +88 -0
  34. package/templates/product-system/packages/backend/src/services/pty_session_manager.ts +279 -0
  35. package/templates/product-system/packages/backend/src/services/terminal_ws_handler.ts +133 -0
  36. package/templates/product-system/packages/backend/src/services/user_management_service.test.ts +158 -0
  37. package/templates/product-system/packages/backend/src/services/user_management_service.ts +128 -0
  38. package/templates/product-system/packages/backend/tsconfig.json +22 -0
  39. package/templates/product-system/packages/frontend/index.html +13 -0
  40. package/templates/product-system/packages/frontend/package-lock.json +2666 -0
  41. package/templates/product-system/packages/frontend/package.json +30 -0
  42. package/templates/product-system/packages/frontend/public/favicon.svg +16 -0
  43. package/templates/product-system/packages/frontend/src/App.tsx +29 -0
  44. package/templates/product-system/packages/frontend/src/api/client.ts +386 -0
  45. package/templates/product-system/packages/frontend/src/api/client_projects.test.ts +104 -0
  46. package/templates/product-system/packages/frontend/src/api/client_refresh.test.ts +145 -0
  47. package/templates/product-system/packages/frontend/src/components/CoherenceView.tsx +414 -0
  48. package/templates/product-system/packages/frontend/src/components/GraphLegend.tsx +124 -0
  49. package/templates/product-system/packages/frontend/src/components/GraphSettings.tsx +112 -0
  50. package/templates/product-system/packages/frontend/src/components/GraphView.tsx +370 -0
  51. package/templates/product-system/packages/frontend/src/components/InviteUserDialog.tsx +85 -0
  52. package/templates/product-system/packages/frontend/src/components/KanbanView.tsx +470 -0
  53. package/templates/product-system/packages/frontend/src/components/LoginPage.tsx +116 -0
  54. package/templates/product-system/packages/frontend/src/components/ProjectSelector.tsx +187 -0
  55. package/templates/product-system/packages/frontend/src/components/QaIssueSheet.tsx +192 -0
  56. package/templates/product-system/packages/frontend/src/components/SidePanel.tsx +231 -0
  57. package/templates/product-system/packages/frontend/src/components/TerminalView.tsx +200 -0
  58. package/templates/product-system/packages/frontend/src/components/Toolbar.tsx +84 -0
  59. package/templates/product-system/packages/frontend/src/components/UsersView.tsx +249 -0
  60. package/templates/product-system/packages/frontend/src/constants/graph.ts +191 -0
  61. package/templates/product-system/packages/frontend/src/hooks/useAuth.tsx +54 -0
  62. package/templates/product-system/packages/frontend/src/hooks/useGraph.ts +27 -0
  63. package/templates/product-system/packages/frontend/src/hooks/useKanban.ts +21 -0
  64. package/templates/product-system/packages/frontend/src/hooks/useProjects.ts +86 -0
  65. package/templates/product-system/packages/frontend/src/hooks/useTheme.ts +26 -0
  66. package/templates/product-system/packages/frontend/src/hooks/useToast.tsx +62 -0
  67. package/templates/product-system/packages/frontend/src/hooks/use_projects_logic.test.ts +61 -0
  68. package/templates/product-system/packages/frontend/src/main.tsx +12 -0
  69. package/templates/product-system/packages/frontend/src/pages/accept_invitation_page.tsx +167 -0
  70. package/templates/product-system/packages/frontend/src/pages/forgot_password_page.tsx +100 -0
  71. package/templates/product-system/packages/frontend/src/pages/register_page.tsx +137 -0
  72. package/templates/product-system/packages/frontend/src/pages/reset_password_page.tsx +146 -0
  73. package/templates/product-system/packages/frontend/src/routes/ProtectedRoute.tsx +12 -0
  74. package/templates/product-system/packages/frontend/src/routes/accept_invitation.tsx +14 -0
  75. package/templates/product-system/packages/frontend/src/routes/dashboard.tsx +221 -0
  76. package/templates/product-system/packages/frontend/src/routes/forgot_password.tsx +13 -0
  77. package/templates/product-system/packages/frontend/src/routes/login.tsx +14 -0
  78. package/templates/product-system/packages/frontend/src/routes/register.tsx +14 -0
  79. package/templates/product-system/packages/frontend/src/routes/reset_password.tsx +13 -0
  80. package/templates/product-system/packages/frontend/src/styles/index.css +3358 -0
  81. package/templates/product-system/packages/frontend/src/utils/auth_validation.test.ts +51 -0
  82. package/templates/product-system/packages/frontend/src/utils/auth_validation.ts +19 -0
  83. package/templates/product-system/packages/frontend/src/utils/login_validation.test.ts +61 -0
  84. package/templates/product-system/packages/frontend/src/utils/login_validation.ts +24 -0
  85. package/templates/product-system/packages/frontend/src/utils/logout.test.ts +63 -0
  86. package/templates/product-system/packages/frontend/src/utils/node_sizing.test.ts +62 -0
  87. package/templates/product-system/packages/frontend/src/utils/node_sizing.ts +24 -0
  88. package/templates/product-system/packages/frontend/src/utils/task_status.test.ts +53 -0
  89. package/templates/product-system/packages/frontend/src/utils/task_status.ts +14 -0
  90. package/templates/product-system/packages/frontend/tsconfig.json +21 -0
  91. package/templates/product-system/packages/frontend/vite.config.ts +20 -0
  92. package/templates/product-system/packages/shared/.env.example +3 -0
  93. package/templates/product-system/packages/shared/README.md +1 -0
  94. package/templates/product-system/packages/shared/db/migrate.ts +32 -0
  95. package/templates/product-system/packages/shared/db/migrations/0000_dashing_gorgon.sql +128 -0
  96. package/templates/product-system/packages/shared/db/migrations/meta/0000_snapshot.json +819 -0
  97. package/templates/product-system/packages/shared/db/migrations/meta/_journal.json +13 -0
  98. package/templates/product-system/packages/shared/db/schema.ts +137 -0
  99. package/templates/product-system/packages/shared/drizzle.config.js +14 -0
  100. package/templates/product-system/packages/shared/lib/claude-service.ts +215 -0
  101. package/templates/product-system/packages/shared/lib/coherence.ts +278 -0
  102. package/templates/product-system/packages/shared/lib/completeness.ts +30 -0
  103. package/templates/product-system/packages/shared/lib/constants.ts +327 -0
  104. package/templates/product-system/packages/shared/lib/db.ts +81 -0
  105. package/templates/product-system/packages/shared/lib/git_workflow.ts +110 -0
  106. package/templates/product-system/packages/shared/lib/graph.ts +186 -0
  107. package/templates/product-system/packages/shared/lib/kanban.ts +161 -0
  108. package/templates/product-system/packages/shared/lib/markdown.ts +205 -0
  109. package/templates/product-system/packages/shared/lib/pipeline-state-store.ts +124 -0
  110. package/templates/product-system/packages/shared/lib/pipeline.ts +489 -0
  111. package/templates/product-system/packages/shared/lib/prompt_builder.ts +170 -0
  112. package/templates/product-system/packages/shared/lib/relevance_search.ts +159 -0
  113. package/templates/product-system/packages/shared/lib/session.ts +152 -0
  114. package/templates/product-system/packages/shared/lib/validator.ts +117 -0
  115. package/templates/product-system/packages/shared/lib/work_summary_parser.ts +130 -0
  116. package/templates/product-system/packages/shared/package.json +30 -0
  117. package/templates/product-system/packages/shared/scripts/assign-project.ts +52 -0
  118. package/templates/product-system/packages/shared/tools/add_edge.ts +61 -0
  119. package/templates/product-system/packages/shared/tools/add_node.ts +101 -0
  120. package/templates/product-system/packages/shared/tools/end_session.ts +87 -0
  121. package/templates/product-system/packages/shared/tools/get_gaps.ts +87 -0
  122. package/templates/product-system/packages/shared/tools/get_kanban.ts +125 -0
  123. package/templates/product-system/packages/shared/tools/get_node.ts +78 -0
  124. package/templates/product-system/packages/shared/tools/get_status.ts +98 -0
  125. package/templates/product-system/packages/shared/tools/migrate_to_turso.ts +385 -0
  126. package/templates/product-system/packages/shared/tools/move_card.ts +143 -0
  127. package/templates/product-system/packages/shared/tools/rebuild_index.ts +77 -0
  128. package/templates/product-system/packages/shared/tools/remove_edge.ts +59 -0
  129. package/templates/product-system/packages/shared/tools/remove_node.ts +96 -0
  130. package/templates/product-system/packages/shared/tools/resolve_question.ts +75 -0
  131. package/templates/product-system/packages/shared/tools/search_nodes.ts +106 -0
  132. package/templates/product-system/packages/shared/tools/start_session.ts +144 -0
  133. package/templates/product-system/packages/shared/tools/update_node.ts +133 -0
  134. package/templates/product-system/packages/shared/tsconfig.json +24 -0
  135. package/templates/product-system/pnpm-workspace.yaml +2 -0
  136. package/templates/product-system/smoke_test.ts +219 -0
  137. package/templates/product-system/tests/coherence_review.test.ts +562 -0
  138. package/templates/product-system/tests/db_sqlite_fallback.test.ts +75 -0
  139. package/templates/product-system/tests/edge_type_color_coding.test.ts +147 -0
  140. package/templates/product-system/tests/emit-tool-use-events.test.ts +85 -0
  141. package/templates/product-system/tests/feature_kind.test.ts +139 -0
  142. package/templates/product-system/tests/gap_indicators.test.ts +199 -0
  143. package/templates/product-system/tests/graceful_init.test.ts +142 -0
  144. package/templates/product-system/tests/graph_legend.test.ts +314 -0
  145. package/templates/product-system/tests/graph_settings_sheet.test.ts +804 -0
  146. package/templates/product-system/tests/hide_defined_filter.test.ts +205 -0
  147. package/templates/product-system/tests/kanban.test.ts +529 -0
  148. package/templates/product-system/tests/neighborhood_focus.test.ts +132 -0
  149. package/templates/product-system/tests/node_search.test.ts +340 -0
  150. package/templates/product-system/tests/node_sizing.test.ts +170 -0
  151. package/templates/product-system/tests/node_type_toggle_filters.test.ts +285 -0
  152. package/templates/product-system/tests/node_type_visual_encoding.test.ts +103 -0
  153. package/templates/product-system/tests/pipeline-state-store.test.ts +268 -0
  154. package/templates/product-system/tests/pipeline-unit.test.ts +593 -0
  155. package/templates/product-system/tests/pipeline.test.ts +195 -0
  156. package/templates/product-system/tests/pipeline_stats_all_cards.test.ts +193 -0
  157. package/templates/product-system/tests/play_all.test.ts +296 -0
  158. package/templates/product-system/tests/qa_issue_sheet.test.ts +464 -0
  159. package/templates/product-system/tests/relevance_search.test.ts +186 -0
  160. package/templates/product-system/tests/search_reorder.test.ts +88 -0
  161. package/templates/product-system/tests/serve_ui.test.ts +281 -0
  162. package/templates/product-system/tests/serve_ui_drizzle.test.ts +114 -0
  163. package/templates/product-system/tests/session_context_recall.test.ts +135 -0
  164. package/templates/product-system/tests/side_panel.test.ts +345 -0
  165. package/templates/product-system/tests/spec_completeness_label.test.ts +69 -0
  166. package/templates/product-system/tests/url_routing_test.ts +122 -0
  167. package/templates/product-system/tests/user_login.test.ts +150 -0
  168. package/templates/product-system/tests/user_registration.test.ts +205 -0
  169. package/templates/product-system/tests/web_terminal.test.ts +572 -0
  170. package/templates/product-system/tests/work_summary.test.ts +211 -0
  171. package/templates/product-system/tests/zoom_pan.test.ts +43 -0
  172. package/templates/product-system/tsconfig.json +24 -0
  173. package/templates/skills/product-bootstrap/SKILL.md +312 -0
  174. package/templates/skills/product-code-reviewer/SKILL.md +147 -0
  175. package/templates/skills/product-debugger/SKILL.md +206 -0
  176. package/templates/skills/product-debugger/references/agent-browser.md +1156 -0
  177. package/templates/skills/product-developer/SKILL.md +182 -0
  178. package/templates/skills/product-interview/SKILL.md +220 -0
@@ -0,0 +1,205 @@
1
+ /**
2
+ * Unit tests for User Registration (feat_064).
3
+ * Tests AuthService logic and registration route data flow
4
+ * without requiring a real database or HTTP server.
5
+ */
6
+ import { describe, it, mock } from 'node:test';
7
+ import assert from 'node:assert/strict';
8
+
9
+ // --- AuthService unit tests ---
10
+
11
+ describe('AuthService password hashing', () => {
12
+ it('hashPassword returns a bcrypt hash string', async () => {
13
+ // bcrypt hashes start with $2a$ or $2b$
14
+ const { hash } = await import('bcryptjs');
15
+ const result = await hash('TestPassword123', 12);
16
+ assert.ok(result.startsWith('$2'));
17
+ assert.ok(result.length > 50);
18
+ });
19
+
20
+ it('verifyPassword returns true for matching password', async () => {
21
+ const { hash, compare } = await import('bcryptjs');
22
+ const hashed = await hash('TestPassword123', 12);
23
+ const result = await compare('TestPassword123', hashed);
24
+ assert.equal(result, true);
25
+ });
26
+
27
+ it('verifyPassword returns false for wrong password', async () => {
28
+ const { hash, compare } = await import('bcryptjs');
29
+ const hashed = await hash('TestPassword123', 12);
30
+ const result = await compare('WrongPassword', hashed);
31
+ assert.equal(result, false);
32
+ });
33
+ });
34
+
35
+ describe('AuthService JWT generation', () => {
36
+ it('generateAccessToken creates a valid JWT with correct claims', async () => {
37
+ const { SignJWT, jwtVerify } = await import('jose');
38
+ const secret = new TextEncoder().encode('test-secret-key-for-unit-tests');
39
+
40
+ const token = await new SignJWT({ sub: 'user-123', email: 'test@example.com', role: 'admin' })
41
+ .setProtectedHeader({ alg: 'HS256' })
42
+ .setIssuedAt()
43
+ .setExpirationTime('30m')
44
+ .sign(secret);
45
+
46
+ assert.ok(typeof token === 'string');
47
+ assert.ok(token.split('.').length === 3); // JWT has 3 parts
48
+
49
+ const { payload } = await jwtVerify(token, secret);
50
+ assert.equal(payload.sub, 'user-123');
51
+ assert.equal(payload.email, 'test@example.com');
52
+ assert.equal(payload.role, 'admin');
53
+ assert.ok(payload.exp > payload.iat);
54
+ });
55
+
56
+ it('generateRefreshToken includes type=refresh claim', async () => {
57
+ const { SignJWT, jwtVerify } = await import('jose');
58
+ const secret = new TextEncoder().encode('test-secret-key-for-unit-tests');
59
+
60
+ const token = await new SignJWT({ sub: 'user-123', type: 'refresh' })
61
+ .setProtectedHeader({ alg: 'HS256' })
62
+ .setIssuedAt()
63
+ .setExpirationTime('7d')
64
+ .sign(secret);
65
+
66
+ const { payload } = await jwtVerify(token, secret);
67
+ assert.equal(payload.sub, 'user-123');
68
+ assert.equal(payload.type, 'refresh');
69
+ });
70
+
71
+ it('verifyToken rejects expired tokens', async () => {
72
+ const { SignJWT, jwtVerify } = await import('jose');
73
+ const secret = new TextEncoder().encode('test-secret-key-for-unit-tests');
74
+
75
+ const token = await new SignJWT({ sub: 'user-123' })
76
+ .setProtectedHeader({ alg: 'HS256' })
77
+ .setIssuedAt(Math.floor(Date.now() / 1000) - 3600) // issued 1 hour ago
78
+ .setExpirationTime(Math.floor(Date.now() / 1000) - 1800) // expired 30 min ago
79
+ .sign(secret);
80
+
81
+ await assert.rejects(
82
+ () => jwtVerify(token, secret),
83
+ { code: 'ERR_JWT_EXPIRED' }
84
+ );
85
+ });
86
+
87
+ it('verifyToken rejects tokens with wrong secret', async () => {
88
+ const { SignJWT, jwtVerify } = await import('jose');
89
+ const secret1 = new TextEncoder().encode('secret-one');
90
+ const secret2 = new TextEncoder().encode('secret-two');
91
+
92
+ const token = await new SignJWT({ sub: 'user-123' })
93
+ .setProtectedHeader({ alg: 'HS256' })
94
+ .setIssuedAt()
95
+ .setExpirationTime('30m')
96
+ .sign(secret1);
97
+
98
+ await assert.rejects(
99
+ () => jwtVerify(token, secret2),
100
+ { code: 'ERR_JWS_SIGNATURE_VERIFICATION_FAILED' }
101
+ );
102
+ });
103
+ });
104
+
105
+ // --- Registration route logic tests ---
106
+
107
+ describe('Registration route validation', () => {
108
+ it('rejects missing email', () => {
109
+ const body = { password: 'TestPassword123' };
110
+ const isValid = body.email && body.password;
111
+ assert.equal(isValid, undefined);
112
+ });
113
+
114
+ it('rejects missing password', () => {
115
+ const body = { email: 'test@example.com' };
116
+ const isValid = body.email && (body as any).password;
117
+ assert.equal(isValid, undefined);
118
+ });
119
+
120
+ it('rejects invalid email format', () => {
121
+ const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
122
+ assert.equal(emailRegex.test('not-an-email'), false);
123
+ assert.equal(emailRegex.test('missing@domain'), false);
124
+ assert.equal(emailRegex.test('@no-local.com'), false);
125
+ assert.equal(emailRegex.test('valid@email.com'), true);
126
+ assert.equal(emailRegex.test('user@sub.domain.com'), true);
127
+ });
128
+
129
+ it('rejects passwords shorter than 8 characters', () => {
130
+ assert.equal('short'.length < 8, true);
131
+ assert.equal('abcdefgh'.length < 8, false);
132
+ });
133
+
134
+ it('normalizes email to lowercase', () => {
135
+ const email = 'Test@EXAMPLE.com';
136
+ assert.equal(email.toLowerCase(), 'test@example.com');
137
+ });
138
+ });
139
+
140
+ describe('Registration first-user-is-admin logic', () => {
141
+ it('assigns admin role when user count is 0', () => {
142
+ const userCount = 0;
143
+ const role = userCount === 0 ? 'admin' : 'user';
144
+ assert.equal(role, 'admin');
145
+ });
146
+
147
+ it('rejects registration when users already exist', () => {
148
+ const userCount = 1;
149
+ const isRegistrationOpen = userCount === 0;
150
+ assert.equal(isRegistrationOpen, false);
151
+ });
152
+
153
+ it('returns 403 status concept for closed registration', () => {
154
+ const userCount = 5;
155
+ const statusCode = userCount > 0 ? 403 : 201;
156
+ assert.equal(statusCode, 403);
157
+ });
158
+ });
159
+
160
+ describe('Registration cookie settings', () => {
161
+ it('sets correct cookie options for production', () => {
162
+ const isProduction = true;
163
+ const options = {
164
+ httpOnly: true,
165
+ secure: isProduction,
166
+ sameSite: 'strict' as const,
167
+ };
168
+ assert.equal(options.httpOnly, true);
169
+ assert.equal(options.secure, true);
170
+ assert.equal(options.sameSite, 'strict');
171
+ });
172
+
173
+ it('sets secure=false in development', () => {
174
+ const isProduction = false;
175
+ const options = {
176
+ httpOnly: true,
177
+ secure: isProduction,
178
+ sameSite: 'strict' as const,
179
+ };
180
+ assert.equal(options.secure, false);
181
+ });
182
+
183
+ it('access token cookie has 30 minute max age', () => {
184
+ const ACCESS_COOKIE_MAX_AGE = 30 * 60 * 1000;
185
+ assert.equal(ACCESS_COOKIE_MAX_AGE, 1800000);
186
+ });
187
+
188
+ it('refresh token cookie has 7 day max age', () => {
189
+ const REFRESH_COOKIE_MAX_AGE = 7 * 24 * 60 * 60 * 1000;
190
+ assert.equal(REFRESH_COOKIE_MAX_AGE, 604800000);
191
+ });
192
+ });
193
+
194
+ describe('Registration response shape', () => {
195
+ it('returns user object with id, email, and role on success', () => {
196
+ const response = {
197
+ user: { id: 'uuid-123', email: 'test@example.com', role: 'admin' },
198
+ };
199
+ assert.ok(response.user.id);
200
+ assert.ok(response.user.email);
201
+ assert.equal(response.user.role, 'admin');
202
+ // Should not include passwordHash
203
+ assert.equal((response.user as any).passwordHash, undefined);
204
+ });
205
+ });