@jcjeon/integration-cli 0.2.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 (264) hide show
  1. package/.gitignore +23 -0
  2. package/.npmignore +21 -0
  3. package/.prettierignore +6 -0
  4. package/.prettierrc +26 -0
  5. package/AGENTS.md +10 -0
  6. package/CLAUDE.md +10 -0
  7. package/README.md +384 -0
  8. package/apps/server/README.md +294 -0
  9. package/apps/server/eslint.config.mjs +20 -0
  10. package/apps/server/nest-cli.json +8 -0
  11. package/apps/server/package.json +89 -0
  12. package/apps/server/scripts/postinstall.js +53 -0
  13. package/apps/server/src/__mocks__/glob.js +6 -0
  14. package/apps/server/src/__mocks__/uuid.js +5 -0
  15. package/apps/server/src/app.controller.spec.ts +24 -0
  16. package/apps/server/src/app.controller.ts +13 -0
  17. package/apps/server/src/app.module.ts +18 -0
  18. package/apps/server/src/app.service.ts +8 -0
  19. package/apps/server/src/common/ji-paths.ts +41 -0
  20. package/apps/server/src/database/database.module.ts +27 -0
  21. package/apps/server/src/database/entities/agent-changelog.entity.ts +39 -0
  22. package/apps/server/src/database/entities/agent-session.entity.ts +29 -0
  23. package/apps/server/src/database/entities/conversation.entity.ts +41 -0
  24. package/apps/server/src/database/entities/session.entity.ts +16 -0
  25. package/apps/server/src/database/entities/task-agent-run.entity.ts +40 -0
  26. package/apps/server/src/database/entities/task-agent.entity.ts +42 -0
  27. package/apps/server/src/database/entities/task-requirement.entity.ts +27 -0
  28. package/apps/server/src/database/entities/task-run.entity.ts +41 -0
  29. package/apps/server/src/database/entities/task.entity.ts +44 -0
  30. package/apps/server/src/main.ts +65 -0
  31. package/apps/server/src/modules/agents/agent-model-settings.spec.ts +80 -0
  32. package/apps/server/src/modules/agents/agents.module.ts +11 -0
  33. package/apps/server/src/modules/agents/claude/claude-auth.manager.ts +83 -0
  34. package/apps/server/src/modules/agents/claude/claude-pty.manager.ts +380 -0
  35. package/apps/server/src/modules/agents/claude/claude.controller.ts +85 -0
  36. package/apps/server/src/modules/agents/claude/claude.gateway.ts +158 -0
  37. package/apps/server/src/modules/agents/claude/claude.module.ts +18 -0
  38. package/apps/server/src/modules/agents/claude/claude.service.ts +67 -0
  39. package/apps/server/src/modules/agents/claude/dto/create-session.dto.ts +24 -0
  40. package/apps/server/src/modules/agents/claude/dto/resize-session.dto.ts +13 -0
  41. package/apps/server/src/modules/agents/claude/dto/send-input.dto.ts +9 -0
  42. package/apps/server/src/modules/agents/claude/interfaces/claude-session.interface.ts +26 -0
  43. package/apps/server/src/modules/agents/claude/interfaces/pty-event.interface.ts +10 -0
  44. package/apps/server/src/modules/agents/claude/interfaces/stream-event.interface.ts +61 -0
  45. package/apps/server/src/modules/agents/codex/codex-auth.manager.ts +107 -0
  46. package/apps/server/src/modules/agents/codex/codex-session.manager.ts +357 -0
  47. package/apps/server/src/modules/agents/codex/codex.controller.ts +64 -0
  48. package/apps/server/src/modules/agents/codex/codex.gateway.ts +97 -0
  49. package/apps/server/src/modules/agents/codex/codex.module.ts +17 -0
  50. package/apps/server/src/modules/agents/codex/dto/configure-auth.dto.ts +7 -0
  51. package/apps/server/src/modules/agents/gemini/dto/configure-auth.dto.ts +15 -0
  52. package/apps/server/src/modules/agents/gemini/dto/create-session.dto.ts +9 -0
  53. package/apps/server/src/modules/agents/gemini/dto/send-input.dto.ts +9 -0
  54. package/apps/server/src/modules/agents/gemini/gemini-auth.manager.ts +157 -0
  55. package/apps/server/src/modules/agents/gemini/gemini-session.manager.ts +287 -0
  56. package/apps/server/src/modules/agents/gemini/gemini.controller.ts +93 -0
  57. package/apps/server/src/modules/agents/gemini/gemini.gateway.ts +149 -0
  58. package/apps/server/src/modules/agents/gemini/gemini.module.ts +17 -0
  59. package/apps/server/src/modules/agents/gemini/interfaces/gemini-session.interface.ts +18 -0
  60. package/apps/server/src/modules/agents/gemini/interfaces/stream-event.interface.ts +14 -0
  61. package/apps/server/src/modules/agents/session-termination.spec.ts +103 -0
  62. package/apps/server/src/modules/changelog/changelog.controller.ts +20 -0
  63. package/apps/server/src/modules/changelog/changelog.module.ts +14 -0
  64. package/apps/server/src/modules/changelog/changelog.service.spec.ts +531 -0
  65. package/apps/server/src/modules/changelog/changelog.service.ts +690 -0
  66. package/apps/server/src/modules/conversations/conversation.controller.spec.ts +106 -0
  67. package/apps/server/src/modules/conversations/conversation.controller.ts +60 -0
  68. package/apps/server/src/modules/conversations/conversation.module.ts +14 -0
  69. package/apps/server/src/modules/conversations/conversation.service.spec.ts +176 -0
  70. package/apps/server/src/modules/conversations/conversation.service.ts +54 -0
  71. package/apps/server/src/modules/conversations/dto/create-conversation.dto.ts +37 -0
  72. package/apps/server/src/modules/conversations/enums/conversation.enum.ts +13 -0
  73. package/apps/server/src/modules/fs/fs.controller.ts +29 -0
  74. package/apps/server/src/modules/fs/fs.module.ts +8 -0
  75. package/apps/server/src/modules/harness/dto/save-harness.dto.ts +9 -0
  76. package/apps/server/src/modules/harness/harness.controller.spec.ts +95 -0
  77. package/apps/server/src/modules/harness/harness.controller.ts +35 -0
  78. package/apps/server/src/modules/harness/harness.module.ts +11 -0
  79. package/apps/server/src/modules/harness/harness.service.spec.ts +217 -0
  80. package/apps/server/src/modules/harness/harness.service.ts +112 -0
  81. package/apps/server/src/modules/sessions/session.controller.spec.ts +68 -0
  82. package/apps/server/src/modules/sessions/session.controller.ts +43 -0
  83. package/apps/server/src/modules/sessions/session.module.ts +14 -0
  84. package/apps/server/src/modules/sessions/session.service.spec.ts +106 -0
  85. package/apps/server/src/modules/sessions/session.service.ts +35 -0
  86. package/apps/server/src/modules/tasks/dto/create-task.dto.ts +54 -0
  87. package/apps/server/src/modules/tasks/dto/execute-task.dto.ts +22 -0
  88. package/apps/server/src/modules/tasks/dto/merge-file.dto.ts +7 -0
  89. package/apps/server/src/modules/tasks/dto/rerun-task.dto.ts +14 -0
  90. package/apps/server/src/modules/tasks/dto/update-task.dto.ts +55 -0
  91. package/apps/server/src/modules/tasks/task-execution.service.ts +978 -0
  92. package/apps/server/src/modules/tasks/task.gateway.ts +140 -0
  93. package/apps/server/src/modules/tasks/tasks.controller.spec.ts +210 -0
  94. package/apps/server/src/modules/tasks/tasks.controller.ts +139 -0
  95. package/apps/server/src/modules/tasks/tasks.module.ts +30 -0
  96. package/apps/server/src/modules/tasks/tasks.service.spec.ts +552 -0
  97. package/apps/server/src/modules/tasks/tasks.service.ts +333 -0
  98. package/apps/server/test/app.e2e-spec.ts +28 -0
  99. package/apps/server/test/jest-e2e.json +9 -0
  100. package/apps/server/tsconfig.build.json +4 -0
  101. package/apps/server/tsconfig.json +13 -0
  102. package/apps/web/AGENTS.md +7 -0
  103. package/apps/web/CLAUDE.md +1 -0
  104. package/apps/web/README.md +36 -0
  105. package/apps/web/eslint.config.mjs +21 -0
  106. package/apps/web/next-env.d.ts +6 -0
  107. package/apps/web/next.config.ts +7 -0
  108. package/apps/web/package.json +49 -0
  109. package/apps/web/postcss.config.mjs +7 -0
  110. package/apps/web/public/file.svg +1 -0
  111. package/apps/web/public/globe.svg +1 -0
  112. package/apps/web/public/next.svg +1 -0
  113. package/apps/web/public/vercel.svg +1 -0
  114. package/apps/web/public/window.svg +1 -0
  115. package/apps/web/src/app/claude/page.tsx +5 -0
  116. package/apps/web/src/app/codex/page.tsx +126 -0
  117. package/apps/web/src/app/favicon.ico +0 -0
  118. package/apps/web/src/app/gemini/page.tsx +130 -0
  119. package/apps/web/src/app/globals.css +149 -0
  120. package/apps/web/src/app/layout.tsx +40 -0
  121. package/apps/web/src/app/login/page.tsx +67 -0
  122. package/apps/web/src/app/page.tsx +497 -0
  123. package/apps/web/src/app/task/[id]/page.tsx +11 -0
  124. package/apps/web/src/app/test/page.tsx +298 -0
  125. package/apps/web/src/components/ui/Modal.tsx +78 -0
  126. package/apps/web/src/components/ui/WorkingDirPicker.tsx +195 -0
  127. package/apps/web/src/components/ui/__tests__/Modal.test.tsx +68 -0
  128. package/apps/web/src/features/auth/api/__tests__/auth.api.test.ts +83 -0
  129. package/apps/web/src/features/auth/api/auth.api.ts +81 -0
  130. package/apps/web/src/features/auth/hooks/__tests__/useClaudeAuth.test.ts +166 -0
  131. package/apps/web/src/features/auth/hooks/__tests__/useCodexAuth.test.ts +127 -0
  132. package/apps/web/src/features/auth/hooks/__tests__/useGeminiAuth.test.ts +120 -0
  133. package/apps/web/src/features/auth/hooks/useClaudeAuth.ts +88 -0
  134. package/apps/web/src/features/auth/hooks/useCodexAuth.ts +149 -0
  135. package/apps/web/src/features/auth/hooks/useGeminiAuth.ts +125 -0
  136. package/apps/web/src/features/auth/ui/CodexLoginPanel.tsx +302 -0
  137. package/apps/web/src/features/auth/ui/GeminiLoginPanel.tsx +316 -0
  138. package/apps/web/src/features/auth/ui/LoginForm.tsx +190 -0
  139. package/apps/web/src/features/auth/ui/LoginPanel.tsx +114 -0
  140. package/apps/web/src/features/auth/ui/__tests__/LoginPanel.test.tsx +105 -0
  141. package/apps/web/src/features/chat/api/__tests__/sessions.api.test.ts +187 -0
  142. package/apps/web/src/features/chat/api/sessions.api.ts +161 -0
  143. package/apps/web/src/features/chat/container/ClaudePageContainer.tsx +152 -0
  144. package/apps/web/src/features/chat/hooks/__tests__/useCodexSessions.test.ts +131 -0
  145. package/apps/web/src/features/chat/hooks/__tests__/useGeminiSessions.test.ts +130 -0
  146. package/apps/web/src/features/chat/hooks/useAgentModelSettings.ts +54 -0
  147. package/apps/web/src/features/chat/hooks/useClaudeSessions.ts +323 -0
  148. package/apps/web/src/features/chat/hooks/useCodexSessions.ts +275 -0
  149. package/apps/web/src/features/chat/hooks/useGeminiSessions.ts +255 -0
  150. package/apps/web/src/features/chat/hooks/useSessionCommand.ts +66 -0
  151. package/apps/web/src/features/chat/hooks/useSessionRename.ts +61 -0
  152. package/apps/web/src/features/chat/hooks/useSessionWorkingDirectories.ts +34 -0
  153. package/apps/web/src/features/chat/hooks/useUnifiedSessions.ts +156 -0
  154. package/apps/web/src/features/chat/lib/agentModelOptions.ts +72 -0
  155. package/apps/web/src/features/chat/ui/AgentModelPicker.tsx +134 -0
  156. package/apps/web/src/features/chat/ui/AgentSelectModal.tsx +236 -0
  157. package/apps/web/src/features/chat/ui/ChatInput.tsx +162 -0
  158. package/apps/web/src/features/chat/ui/ChatMessage.tsx +204 -0
  159. package/apps/web/src/features/chat/ui/ChatWorkspace.tsx +207 -0
  160. package/apps/web/src/features/chat/ui/CheckingSkeleton.tsx +44 -0
  161. package/apps/web/src/features/chat/ui/ClaudeLoginView.tsx +44 -0
  162. package/apps/web/src/features/chat/ui/PermissionCard.tsx +37 -0
  163. package/apps/web/src/features/chat/ui/SessionSidebar.tsx +280 -0
  164. package/apps/web/src/features/chat/ui/__tests__/AgentSelectModal.test.tsx +58 -0
  165. package/apps/web/src/features/chat/ui/__tests__/ChatInput.test.tsx +134 -0
  166. package/apps/web/src/features/chat/ui/__tests__/ChatMessage.test.tsx +106 -0
  167. package/apps/web/src/features/chat/ui/__tests__/ChatWorkspace.test.tsx +66 -0
  168. package/apps/web/src/features/diff/ui/DiffFileRow.tsx +73 -0
  169. package/apps/web/src/features/diff/ui/DiffHunk.tsx +61 -0
  170. package/apps/web/src/features/diff/ui/FileChangeBadge.tsx +23 -0
  171. package/apps/web/src/features/diff/ui/__tests__/DiffFileRow.test.tsx +40 -0
  172. package/apps/web/src/features/diff/ui/__tests__/DiffHunk.test.tsx +24 -0
  173. package/apps/web/src/features/diff/ui/__tests__/FileChangeBadge.test.tsx +16 -0
  174. package/apps/web/src/features/fs/api/fs.api.ts +14 -0
  175. package/apps/web/src/features/fs/hooks/useDirBrowser.ts +50 -0
  176. package/apps/web/src/features/harness/api/__tests__/harness.api.test.ts +73 -0
  177. package/apps/web/src/features/harness/api/harness.api.ts +46 -0
  178. package/apps/web/src/features/harness/hooks/__tests__/useHarness.test.ts +65 -0
  179. package/apps/web/src/features/harness/hooks/useHarness.ts +66 -0
  180. package/apps/web/src/features/harness/ui/HarnessModal.tsx +171 -0
  181. package/apps/web/src/features/harness/ui/__tests__/HarnessModal.test.tsx +46 -0
  182. package/apps/web/src/features/status/ui/AgentStatusModal.tsx +267 -0
  183. package/apps/web/src/features/status/ui/__tests__/AgentStatusModal.test.tsx +71 -0
  184. package/apps/web/src/features/tasks/api/__tests__/changelog.api.test.ts +89 -0
  185. package/apps/web/src/features/tasks/api/__tests__/tasks.api.test.ts +282 -0
  186. package/apps/web/src/features/tasks/api/changelog.api.ts +52 -0
  187. package/apps/web/src/features/tasks/api/tasks.api.ts +175 -0
  188. package/apps/web/src/features/tasks/container/TaskDetailPageContainer.tsx +69 -0
  189. package/apps/web/src/features/tasks/hooks/__tests__/useChangelogCodeCopy.test.ts +48 -0
  190. package/apps/web/src/features/tasks/hooks/__tests__/useTaskChangelog.test.ts +48 -0
  191. package/apps/web/src/features/tasks/hooks/__tests__/useTaskCreate.test.ts +217 -0
  192. package/apps/web/src/features/tasks/hooks/__tests__/useTaskEdit.test.ts +152 -0
  193. package/apps/web/src/features/tasks/hooks/__tests__/useTaskExecution.test.ts +143 -0
  194. package/apps/web/src/features/tasks/hooks/__tests__/useTaskList.test.ts +168 -0
  195. package/apps/web/src/features/tasks/hooks/__tests__/useTaskNotification.test.ts +125 -0
  196. package/apps/web/src/features/tasks/hooks/__tests__/useTaskRuns.test.ts +51 -0
  197. package/apps/web/src/features/tasks/hooks/useChangelogCodeCopy.ts +52 -0
  198. package/apps/web/src/features/tasks/hooks/useCopyToClipboard.ts +47 -0
  199. package/apps/web/src/features/tasks/hooks/useTaskChangelog.ts +32 -0
  200. package/apps/web/src/features/tasks/hooks/useTaskCreate.ts +137 -0
  201. package/apps/web/src/features/tasks/hooks/useTaskDetail.ts +217 -0
  202. package/apps/web/src/features/tasks/hooks/useTaskEdit.ts +130 -0
  203. package/apps/web/src/features/tasks/hooks/useTaskExecution.ts +137 -0
  204. package/apps/web/src/features/tasks/hooks/useTaskList.ts +159 -0
  205. package/apps/web/src/features/tasks/hooks/useTaskNotification.ts +80 -0
  206. package/apps/web/src/features/tasks/hooks/useTaskRuns.ts +32 -0
  207. package/apps/web/src/features/tasks/ui/AgentOutputPanel.tsx +203 -0
  208. package/apps/web/src/features/tasks/ui/AgentRoleSelect.tsx +97 -0
  209. package/apps/web/src/features/tasks/ui/ChangelogPanel.tsx +321 -0
  210. package/apps/web/src/features/tasks/ui/RunHistoryPanel.tsx +193 -0
  211. package/apps/web/src/features/tasks/ui/TaskCreateModal.tsx +205 -0
  212. package/apps/web/src/features/tasks/ui/TaskDetailView.tsx +413 -0
  213. package/apps/web/src/features/tasks/ui/TaskEditModal.tsx +165 -0
  214. package/apps/web/src/features/tasks/ui/TaskListModal.tsx +591 -0
  215. package/apps/web/src/features/tasks/ui/__tests__/AgentRoleSelect.test.tsx +91 -0
  216. package/apps/web/src/features/tasks/ui/__tests__/ChangelogPanel.test.tsx +94 -0
  217. package/apps/web/src/features/tasks/ui/__tests__/RunHistoryPanel.test.tsx +71 -0
  218. package/apps/web/src/features/tasks/ui/__tests__/TaskCreateModal.test.tsx +153 -0
  219. package/apps/web/src/features/tasks/ui/__tests__/TaskEditModal.test.tsx +75 -0
  220. package/apps/web/src/features/tasks/ui/__tests__/TaskListModal.test.tsx +243 -0
  221. package/apps/web/src/hooks/useWorkingDir.ts +28 -0
  222. package/apps/web/src/lib/__tests__/ansi.test.ts +88 -0
  223. package/apps/web/src/lib/ansi.ts +105 -0
  224. package/apps/web/src/lib/constants.ts +4 -0
  225. package/apps/web/src/lib/quota.ts +22 -0
  226. package/apps/web/src/lib/theme.tsx +78 -0
  227. package/apps/web/src/lib/toast.tsx +175 -0
  228. package/apps/web/src/store/agentStatusStore.ts +38 -0
  229. package/apps/web/tsconfig.json +18 -0
  230. package/apps/web/vitest.config.ts +25 -0
  231. package/apps/web/vitest.setup.ts +10 -0
  232. package/package.json +85 -0
  233. package/packages/cli/dist/commands/check.d.ts +1 -0
  234. package/packages/cli/dist/commands/check.js +89 -0
  235. package/packages/cli/dist/commands/init.d.ts +5 -0
  236. package/packages/cli/dist/commands/init.js +183 -0
  237. package/packages/cli/dist/commands/start.d.ts +4 -0
  238. package/packages/cli/dist/commands/start.js +188 -0
  239. package/packages/cli/dist/index.d.ts +2 -0
  240. package/packages/cli/dist/index.js +71 -0
  241. package/packages/cli/dist/utils/agent-tools.d.ts +28 -0
  242. package/packages/cli/dist/utils/agent-tools.js +193 -0
  243. package/packages/cli/dist/utils/project-init.d.ts +12 -0
  244. package/packages/cli/dist/utils/project-init.js +258 -0
  245. package/packages/cli/dist/utils/proxy.d.ts +8 -0
  246. package/packages/cli/dist/utils/proxy.js +138 -0
  247. package/packages/cli/package.json +30 -0
  248. package/packages/cli/src/commands/check.ts +77 -0
  249. package/packages/cli/src/commands/init.ts +209 -0
  250. package/packages/cli/src/commands/start.ts +183 -0
  251. package/packages/cli/src/index.ts +91 -0
  252. package/packages/cli/src/utils/agent-tools.ts +201 -0
  253. package/packages/cli/src/utils/project-init.ts +252 -0
  254. package/packages/cli/src/utils/proxy.ts +123 -0
  255. package/packages/cli/tsconfig.json +14 -0
  256. package/packages/eslint-config/base.mjs +31 -0
  257. package/packages/eslint-config/nest.mjs +55 -0
  258. package/packages/eslint-config/next.mjs +23 -0
  259. package/packages/eslint-config/package.json +20 -0
  260. package/packages/typescript-config/base.json +16 -0
  261. package/packages/typescript-config/nestjs.json +17 -0
  262. package/packages/typescript-config/nextjs.json +15 -0
  263. package/packages/typescript-config/package.json +11 -0
  264. package/turbo.json +28 -0
@@ -0,0 +1,333 @@
1
+ import * as fs from 'fs';
2
+
3
+ import { BadRequestException, Injectable, NotFoundException } from '@nestjs/common';
4
+ import { InjectRepository } from '@nestjs/typeorm';
5
+ import { Repository } from 'typeorm';
6
+ import { v4 as uuidv4 } from 'uuid';
7
+
8
+ import { TaskAgentEntity } from '../../database/entities/task-agent.entity';
9
+ import { TaskAgentRunEntity } from '../../database/entities/task-agent-run.entity';
10
+ import { TaskRequirementEntity } from '../../database/entities/task-requirement.entity';
11
+ import { TaskRunEntity } from '../../database/entities/task-run.entity';
12
+ import { TaskEntity } from '../../database/entities/task.entity';
13
+ import { GitChangelogService } from '../changelog/changelog.service';
14
+ import type { CreateTaskDto } from './dto/create-task.dto';
15
+ import type { AgentTestCodePreferenceDto } from './dto/execute-task.dto';
16
+ import type { UpdateTaskDto } from './dto/update-task.dto';
17
+ import { TaskExecutionService } from './task-execution.service';
18
+
19
+ interface SpawnOptions {
20
+ testCodeAgentIds?: number[];
21
+ }
22
+
23
+ @Injectable()
24
+ export class TasksService {
25
+ constructor(
26
+ @InjectRepository(TaskEntity)
27
+ private readonly taskRepo: Repository<TaskEntity>,
28
+ @InjectRepository(TaskRequirementEntity)
29
+ private readonly requirementRepo: Repository<TaskRequirementEntity>,
30
+ @InjectRepository(TaskAgentEntity)
31
+ private readonly agentRepo: Repository<TaskAgentEntity>,
32
+ @InjectRepository(TaskRunEntity)
33
+ private readonly runRepo: Repository<TaskRunEntity>,
34
+ @InjectRepository(TaskAgentRunEntity)
35
+ private readonly agentRunRepo: Repository<TaskAgentRunEntity>,
36
+ private readonly executionService: TaskExecutionService,
37
+ private readonly gitChangelogService: GitChangelogService,
38
+ ) {}
39
+
40
+ async create(dto: CreateTaskDto): Promise<TaskEntity> {
41
+ const id = uuidv4();
42
+ const task = this.taskRepo.create({
43
+ id,
44
+ title: dto.title,
45
+ workingDir: dto.workingDir ?? null,
46
+ status: 'pending',
47
+ });
48
+ await this.taskRepo.save(task);
49
+
50
+ if (dto.requirements?.length) {
51
+ await this.requirementRepo.save(
52
+ dto.requirements.map((r, i) =>
53
+ this.requirementRepo.create({
54
+ taskId: id,
55
+ content: r.content,
56
+ orderIndex: r.orderIndex ?? i,
57
+ status: 'pending',
58
+ }),
59
+ ),
60
+ );
61
+ }
62
+
63
+ if (dto.agents?.length) {
64
+ await this.agentRepo.save(
65
+ dto.agents.map((a) =>
66
+ this.agentRepo.create({
67
+ taskId: id,
68
+ agentType: (a.agentType ?? 'claude') as TaskAgentEntity['agentType'],
69
+ role: a.role as TaskAgentEntity['role'],
70
+ customRole: a.customRole ?? null,
71
+ status: 'pending',
72
+ }),
73
+ ),
74
+ );
75
+ }
76
+
77
+ return this.findOne(id);
78
+ }
79
+
80
+ async update(id: string, dto: UpdateTaskDto): Promise<TaskEntity> {
81
+ const task = await this.findOne(id);
82
+
83
+ if (dto.title !== undefined) task.title = dto.title;
84
+ if (dto.workingDir !== undefined) task.workingDir = dto.workingDir ?? null;
85
+ await this.taskRepo.save(task);
86
+
87
+ if (dto.requirements !== undefined) {
88
+ await this.requirementRepo.delete({ taskId: id });
89
+ if (dto.requirements.length) {
90
+ await this.requirementRepo.save(
91
+ dto.requirements.map((r, i) =>
92
+ this.requirementRepo.create({
93
+ taskId: id,
94
+ content: r.content,
95
+ orderIndex: r.orderIndex ?? i,
96
+ status: 'pending',
97
+ }),
98
+ ),
99
+ );
100
+ }
101
+ }
102
+
103
+ if (dto.agents !== undefined) {
104
+ await this.agentRepo.delete({ taskId: id });
105
+ if (dto.agents.length) {
106
+ await this.agentRepo.save(
107
+ dto.agents.map((a) =>
108
+ this.agentRepo.create({
109
+ taskId: id,
110
+ agentType: (a.agentType ?? 'claude') as TaskAgentEntity['agentType'],
111
+ role: a.role as TaskAgentEntity['role'],
112
+ customRole: a.customRole ?? null,
113
+ status: 'pending',
114
+ }),
115
+ ),
116
+ );
117
+ }
118
+ }
119
+
120
+ return this.findOne(id);
121
+ }
122
+
123
+ async execute(id: string, agentTestCodePreferences: AgentTestCodePreferenceDto[] = []): Promise<TaskEntity> {
124
+ const task = await this.findOne(id);
125
+ if (task.status === 'running') return task;
126
+
127
+ if (task.status !== 'pending') {
128
+ await this.resetTaskForRun(task);
129
+ }
130
+
131
+ const version = await this.getNextRunVersion(id);
132
+ const run = await this.createRun(id, version, null);
133
+ const executableTask = task.status === 'pending' ? task : await this.findOne(id);
134
+ await this.spawnWithRun(executableTask, undefined, run.id, {
135
+ testCodeAgentIds: this.getTestCodeAgentIds(agentTestCodePreferences),
136
+ });
137
+ return this.findOne(id);
138
+ }
139
+
140
+ async rerun(id: string, supplementNote?: string, writeTestCode = false): Promise<TaskEntity> {
141
+ const task = await this.findOne(id);
142
+ if (task.status === 'running') return task;
143
+
144
+ await this.resetTaskForRun(task);
145
+ const nextVersion = await this.getNextRunVersion(id);
146
+ const run = await this.createRun(id, nextVersion, supplementNote ?? null);
147
+
148
+ const refreshed = await this.findOne(id);
149
+ await this.spawnWithRun(refreshed, supplementNote, run.id, {
150
+ testCodeAgentIds: writeTestCode ? refreshed.agents.map((agent) => agent.id) : [],
151
+ });
152
+ return this.findOne(id);
153
+ }
154
+
155
+ async rerunAgent(id: string, agentId: number, supplementNote?: string, writeTestCode = false): Promise<TaskEntity> {
156
+ const task = await this.findOne(id);
157
+ if (task.status === 'running') return task;
158
+
159
+ const agent = task.agents.find((a) => a.id === agentId);
160
+ if (!agent) throw new NotFoundException(`Agent ${agentId} not found`);
161
+
162
+ await this.agentRepo.update(agent.id, { status: 'pending', claudeSessionId: null });
163
+ await this.taskRepo.update(task.id, { status: 'pending' });
164
+
165
+ const nextVersion = await this.getNextRunVersion(id);
166
+ const run = await this.createRun(id, nextVersion, supplementNote ?? null);
167
+ const agentOnlyTask = {
168
+ ...task,
169
+ status: 'pending',
170
+ agents: [{ ...agent, status: 'pending', claudeSessionId: null }],
171
+ } as TaskEntity;
172
+
173
+ await this.spawnWithRun(agentOnlyTask, supplementNote, run.id, {
174
+ testCodeAgentIds: writeTestCode ? [agent.id] : [],
175
+ });
176
+ return this.findOne(id);
177
+ }
178
+
179
+ async stop(id: string): Promise<TaskEntity> {
180
+ const task = await this.findOne(id);
181
+ await this.executionService.stopTask(task);
182
+
183
+ await this.runRepo.update(
184
+ { taskId: id, status: 'running' },
185
+ { status: 'stopped', completedAt: new Date() },
186
+ );
187
+
188
+ return this.findOne(id);
189
+ }
190
+
191
+ findAll(): Promise<TaskEntity[]> {
192
+ return this.taskRepo.find({
193
+ where: { archived: false },
194
+ relations: ['requirements', 'agents'],
195
+ order: { createdAt: 'DESC' },
196
+ });
197
+ }
198
+
199
+ async getRuns(taskId: string): Promise<TaskRunEntity[]> {
200
+ return this.runRepo.find({
201
+ where: { taskId },
202
+ relations: ['agentRuns'],
203
+ order: { version: 'DESC' },
204
+ });
205
+ }
206
+
207
+ async archive(id: string): Promise<void> {
208
+ await this.findOne(id);
209
+ await this.taskRepo.update(id, { archived: true });
210
+ }
211
+
212
+ async mergeAgentAll(taskId: string, agentId: number): Promise<{ success: boolean; message: string }> {
213
+ const [task, agent] = await Promise.all([
214
+ this.findOne(taskId),
215
+ this.agentRepo.findOne({ where: { id: agentId, taskId } }),
216
+ ]);
217
+ if (!agent) throw new NotFoundException(`Agent ${agentId} not found`);
218
+
219
+ const workingDir = this.resolveWorkingDir(task.workingDir);
220
+ const storedPatchResult = await this.gitChangelogService.mergeAllFromChangelog(taskId, agentId, workingDir);
221
+ if (storedPatchResult.success) return storedPatchResult;
222
+ if (!agent.worktreePath) return { success: false, message: 'worktree가 존재하지 않습니다.' };
223
+
224
+ return this.gitChangelogService.mergeAll(agent.worktreePath, workingDir);
225
+ }
226
+
227
+ async mergeAgentFile(taskId: string, agentId: number, filePath: string): Promise<{ success: boolean; message: string }> {
228
+ const [task, agent] = await Promise.all([
229
+ this.findOne(taskId),
230
+ this.agentRepo.findOne({ where: { id: agentId, taskId } }),
231
+ ]);
232
+ if (!agent) throw new NotFoundException(`Agent ${agentId} not found`);
233
+
234
+ const workingDir = this.resolveWorkingDir(task.workingDir);
235
+ const storedPatchResult = await this.gitChangelogService.mergeFileFromChangelog(taskId, agentId, workingDir, filePath);
236
+ if (storedPatchResult.success) return storedPatchResult;
237
+ if (!agent.worktreePath) return { success: false, message: 'worktree가 존재하지 않습니다.' };
238
+
239
+ await this.assertFileBelongsToAgentChangelog(taskId, agentId, filePath);
240
+ return this.gitChangelogService.mergeFile(agent.worktreePath, workingDir, filePath);
241
+ }
242
+
243
+ private async assertFileBelongsToAgentChangelog(taskId: string, agentId: number, filePath: string): Promise<void> {
244
+ try {
245
+ const changelogs = await this.gitChangelogService.getByTask(taskId);
246
+ const agentChangelog = changelogs.find((entry) => entry.agentId === agentId);
247
+ const hasFile = agentChangelog?.files.some((file) => file.filePath === filePath) ?? false;
248
+
249
+ if (!hasFile) {
250
+ throw new BadRequestException('변경 목록에 없는 파일은 병합할 수 없습니다.');
251
+ }
252
+ } catch (err) {
253
+ if (err instanceof BadRequestException) throw err;
254
+ throw new BadRequestException('변경 목록을 확인할 수 없습니다.');
255
+ }
256
+ }
257
+
258
+ private resolveWorkingDir(workingDir: string | null): string {
259
+ try {
260
+ if (!workingDir) {
261
+ return process.cwd();
262
+ }
263
+ if (!fs.existsSync(workingDir)) {
264
+ throw new BadRequestException(`작업 디렉토리가 존재하지 않습니다: ${workingDir}`);
265
+ }
266
+ if (!fs.statSync(workingDir).isDirectory()) {
267
+ throw new BadRequestException(`작업 디렉토리가 폴더가 아닙니다: ${workingDir}`);
268
+ }
269
+ return workingDir;
270
+ } catch (err) {
271
+ if (err instanceof BadRequestException) throw err;
272
+ throw new BadRequestException(`작업 디렉토리를 확인할 수 없습니다: ${workingDir}`);
273
+ }
274
+ }
275
+
276
+ async findOne(id: string): Promise<TaskEntity> {
277
+ const task = await this.taskRepo.findOne({ where: { id }, relations: ['requirements', 'agents'] });
278
+ if (!task) throw new NotFoundException(`Task ${id} not found`);
279
+ return task;
280
+ }
281
+
282
+ async remove(id: string): Promise<void> {
283
+ await this.taskRepo.delete(id);
284
+ }
285
+
286
+ private async resetTaskForRun(task: TaskEntity): Promise<void> {
287
+ for (const agent of task.agents) {
288
+ await this.agentRepo.update(agent.id, { status: 'pending', claudeSessionId: null });
289
+ }
290
+ await this.taskRepo.update(task.id, { status: 'pending' });
291
+ }
292
+
293
+ private async getNextRunVersion(taskId: string): Promise<number> {
294
+ const lastRun = await this.runRepo.findOne({
295
+ where: { taskId },
296
+ order: { version: 'DESC' },
297
+ });
298
+ return (lastRun?.version ?? 0) + 1;
299
+ }
300
+
301
+ private async spawnWithRun(
302
+ task: TaskEntity,
303
+ supplementNote: string | undefined,
304
+ runId: number,
305
+ options: SpawnOptions = {},
306
+ ): Promise<void> {
307
+ try {
308
+ await this.executionService.spawnTask(task, supplementNote, runId, options);
309
+ } catch (err) {
310
+ await Promise.all([
311
+ this.runRepo.update(runId, { status: 'error', completedAt: new Date() }),
312
+ this.taskRepo.update(task.id, { status: 'error' }),
313
+ ]);
314
+ throw err;
315
+ }
316
+ }
317
+
318
+ private async createRun(taskId: string, version: number, supplementNote: string | null): Promise<TaskRunEntity> {
319
+ const run = this.runRepo.create({
320
+ taskId,
321
+ version,
322
+ supplementNote,
323
+ status: 'running',
324
+ });
325
+ return this.runRepo.save(run);
326
+ }
327
+
328
+ private getTestCodeAgentIds(agentTestCodePreferences: AgentTestCodePreferenceDto[]): number[] {
329
+ return agentTestCodePreferences
330
+ .filter((preference) => preference.writeTestCode)
331
+ .map((preference) => preference.agentId);
332
+ }
333
+ }
@@ -0,0 +1,28 @@
1
+ import type { INestApplication } from '@nestjs/common';
2
+ import type { TestingModule } from '@nestjs/testing';
3
+ import { Test } from '@nestjs/testing';
4
+ import request from 'supertest';
5
+ import type { App } from 'supertest/types';
6
+
7
+ import { AppModule } from './../src/app.module';
8
+
9
+ describe('AppController (e2e)', () => {
10
+ let app: INestApplication<App>;
11
+
12
+ beforeEach(async () => {
13
+ const moduleFixture: TestingModule = await Test.createTestingModule({
14
+ imports: [AppModule],
15
+ }).compile();
16
+
17
+ app = moduleFixture.createNestApplication();
18
+ await app.init();
19
+ });
20
+
21
+ it('/ (GET)', () => {
22
+ return request(app.getHttpServer()).get('/').expect(200).expect('Hello World!');
23
+ });
24
+
25
+ afterEach(async () => {
26
+ await app.close();
27
+ });
28
+ });
@@ -0,0 +1,9 @@
1
+ {
2
+ "moduleFileExtensions": ["js", "json", "ts"],
3
+ "rootDir": ".",
4
+ "testEnvironment": "node",
5
+ "testRegex": ".e2e-spec.ts$",
6
+ "transform": {
7
+ "^.+\\.(t|j)s$": "ts-jest"
8
+ }
9
+ }
@@ -0,0 +1,4 @@
1
+ {
2
+ "extends": "./tsconfig.json",
3
+ "exclude": ["node_modules", "test", "dist", "**/*spec.ts"]
4
+ }
@@ -0,0 +1,13 @@
1
+ {
2
+ "extends": "@ji/typescript-config/nestjs.json",
3
+ "compilerOptions": {
4
+ "outDir": "./dist",
5
+ "baseUrl": "./",
6
+ "isolatedModules": true,
7
+ "paths": {
8
+ "@/*": ["./src/*"]
9
+ }
10
+ },
11
+ "include": ["src/**/*"],
12
+ "exclude": ["node_modules", "dist"]
13
+ }
@@ -0,0 +1,7 @@
1
+ <!-- BEGIN:nextjs-agent-rules -->
2
+
3
+ # This is NOT the Next.js you know
4
+
5
+ This version has breaking changes — APIs, conventions, and file structure may all differ from your training data. Read the relevant guide in `node_modules/next/dist/docs/` before writing any code. Heed deprecation notices.
6
+
7
+ <!-- END:nextjs-agent-rules -->
@@ -0,0 +1 @@
1
+ @AGENTS.md
@@ -0,0 +1,36 @@
1
+ This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app).
2
+
3
+ ## Getting Started
4
+
5
+ First, run the development server:
6
+
7
+ ```bash
8
+ npm run dev
9
+ # or
10
+ yarn dev
11
+ # or
12
+ pnpm dev
13
+ # or
14
+ bun dev
15
+ ```
16
+
17
+ Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
18
+
19
+ You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.
20
+
21
+ This project uses [`next/font`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel.
22
+
23
+ ## Learn More
24
+
25
+ To learn more about Next.js, take a look at the following resources:
26
+
27
+ - [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
28
+ - [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
29
+
30
+ You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome!
31
+
32
+ ## Deploy on Vercel
33
+
34
+ The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
35
+
36
+ Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details.
@@ -0,0 +1,21 @@
1
+ /*
2
+ * @Author: 전지창
3
+ * @Date: 2026-05-04 15:13:40
4
+ * @LastEditTime: 2026-05-04 18:12:10
5
+ * @LastEditors: 전지창
6
+ * @Description:
7
+ */
8
+ import nextConfig from "@ji/eslint-config/next";
9
+ import path from "node:path";
10
+
11
+ export default [
12
+ ...nextConfig,
13
+ {
14
+ languageOptions: {
15
+ parserOptions: {
16
+ projectService: true,
17
+ tsconfigRootDir: path.join(import.meta.dirname, ".."),
18
+ },
19
+ },
20
+ },
21
+ ];
@@ -0,0 +1,6 @@
1
+ /// <reference types="next" />
2
+ /// <reference types="next/image-types/global" />
3
+ import "./.next/dev/types/routes.d.ts";
4
+
5
+ // NOTE: This file should not be edited
6
+ // see https://nextjs.org/docs/app/api-reference/config/typescript for more information.
@@ -0,0 +1,7 @@
1
+ import type { NextConfig } from "next";
2
+
3
+ const nextConfig: NextConfig = {
4
+ /* config options here */
5
+ };
6
+
7
+ export default nextConfig;
@@ -0,0 +1,49 @@
1
+ {
2
+ "name": "@ji/web",
3
+ "version": "0.1.0",
4
+ "private": true,
5
+ "scripts": {
6
+ "dev": "next dev",
7
+ "build": "next build",
8
+ "start": "next start",
9
+ "lint": "eslint",
10
+ "lint:fix": "eslint --fix",
11
+ "clean": "rm -rf .next out",
12
+ "test": "vitest run",
13
+ "test:watch": "vitest",
14
+ "test:coverage": "vitest run --coverage"
15
+ },
16
+ "dependencies": {
17
+ "highlight.js": "^11.11.1",
18
+ "next": "16.2.4",
19
+ "react": "19.2.4",
20
+ "react-dom": "19.2.4",
21
+ "react-markdown": "^10.1.0",
22
+ "rehype-highlight": "^7.0.2",
23
+ "remark-gfm": "^4.0.1",
24
+ "socket.io-client": "^4.8.3",
25
+ "zustand": "^5.0.13"
26
+ },
27
+ "devDependencies": {
28
+ "@ji/eslint-config": "*",
29
+ "@ji/typescript-config": "*",
30
+ "@tailwindcss/postcss": "^4",
31
+ "@testing-library/jest-dom": "^6.9.1",
32
+ "@testing-library/react": "^16.3.2",
33
+ "@testing-library/user-event": "^14.6.1",
34
+ "@types/node": "^20",
35
+ "@types/react": "^19",
36
+ "@types/react-dom": "^19",
37
+ "@vitejs/plugin-react": "^4.7.0",
38
+ "@vitest/coverage-v8": "^2.1.9",
39
+ "eslint": "^9",
40
+ "eslint-config-next": "16.2.4",
41
+ "eslint-config-prettier": "^10.1.8",
42
+ "eslint-plugin-import": "^2.32.0",
43
+ "eslint-plugin-prettier": "^5.5.5",
44
+ "jsdom": "^24.1.3",
45
+ "tailwindcss": "^4",
46
+ "typescript": "^5",
47
+ "vitest": "^2.1.9"
48
+ }
49
+ }
@@ -0,0 +1,7 @@
1
+ const config = {
2
+ plugins: {
3
+ "@tailwindcss/postcss": {},
4
+ },
5
+ };
6
+
7
+ export default config;
@@ -0,0 +1 @@
1
+ <svg fill="none" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path d="M14.5 13.5V5.41a1 1 0 0 0-.3-.7L9.8.29A1 1 0 0 0 9.08 0H1.5v13.5A2.5 2.5 0 0 0 4 16h8a2.5 2.5 0 0 0 2.5-2.5m-1.5 0v-7H8v-5H3v12a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1M9.5 5V2.12L12.38 5zM5.13 5h-.62v1.25h2.12V5zm-.62 3h7.12v1.25H4.5zm.62 3h-.62v1.25h7.12V11z" clip-rule="evenodd" fill="#666" fill-rule="evenodd"/></svg>
@@ -0,0 +1 @@
1
+ <svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><g clip-path="url(#a)"><path fill-rule="evenodd" clip-rule="evenodd" d="M10.27 14.1a6.5 6.5 0 0 0 3.67-3.45q-1.24.21-2.7.34-.31 1.83-.97 3.1M8 16A8 8 0 1 0 8 0a8 8 0 0 0 0 16m.48-1.52a7 7 0 0 1-.96 0H7.5a4 4 0 0 1-.84-1.32q-.38-.89-.63-2.08a40 40 0 0 0 3.92 0q-.25 1.2-.63 2.08a4 4 0 0 1-.84 1.31zm2.94-4.76q1.66-.15 2.95-.43a7 7 0 0 0 0-2.58q-1.3-.27-2.95-.43a18 18 0 0 1 0 3.44m-1.27-3.54a17 17 0 0 1 0 3.64 39 39 0 0 1-4.3 0 17 17 0 0 1 0-3.64 39 39 0 0 1 4.3 0m1.1-1.17q1.45.13 2.69.34a6.5 6.5 0 0 0-3.67-3.44q.65 1.26.98 3.1M8.48 1.5l.01.02q.41.37.84 1.31.38.89.63 2.08a40 40 0 0 0-3.92 0q.25-1.2.63-2.08a4 4 0 0 1 .85-1.32 7 7 0 0 1 .96 0m-2.75.4a6.5 6.5 0 0 0-3.67 3.44 29 29 0 0 1 2.7-.34q.31-1.83.97-3.1M4.58 6.28q-1.66.16-2.95.43a7 7 0 0 0 0 2.58q1.3.27 2.95.43a18 18 0 0 1 0-3.44m.17 4.71q-1.45-.12-2.69-.34a6.5 6.5 0 0 0 3.67 3.44q-.65-1.27-.98-3.1" fill="#666"/></g><defs><clipPath id="a"><path fill="#fff" d="M0 0h16v16H0z"/></clipPath></defs></svg>
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 394 80"><path fill="#000" d="M262 0h68.5v12.7h-27.2v66.6h-13.6V12.7H262V0ZM149 0v12.7H94v20.4h44.3v12.6H94v21h55v12.6H80.5V0h68.7zm34.3 0h-17.8l63.8 79.4h17.9l-32-39.7 32-39.6h-17.9l-23 28.6-23-28.6zm18.3 56.7-9-11-27.1 33.7h17.8l18.3-22.7z"/><path fill="#000" d="M81 79.3 17 0H0v79.3h13.6V17l50.2 62.3H81Zm252.6-.4c-1 0-1.8-.4-2.5-1s-1.1-1.6-1.1-2.6.3-1.8 1-2.5 1.6-1 2.6-1 1.8.3 2.5 1a3.4 3.4 0 0 1 .6 4.3 3.7 3.7 0 0 1-3 1.8zm23.2-33.5h6v23.3c0 2.1-.4 4-1.3 5.5a9.1 9.1 0 0 1-3.8 3.5c-1.6.8-3.5 1.3-5.7 1.3-2 0-3.7-.4-5.3-1s-2.8-1.8-3.7-3.2c-.9-1.3-1.4-3-1.4-5h6c.1.8.3 1.6.7 2.2s1 1.2 1.6 1.5c.7.4 1.5.5 2.4.5 1 0 1.8-.2 2.4-.6a4 4 0 0 0 1.6-1.8c.3-.8.5-1.8.5-3V45.5zm30.9 9.1a4.4 4.4 0 0 0-2-3.3 7.5 7.5 0 0 0-4.3-1.1c-1.3 0-2.4.2-3.3.5-.9.4-1.6 1-2 1.6a3.5 3.5 0 0 0-.3 4c.3.5.7.9 1.3 1.2l1.8 1 2 .5 3.2.8c1.3.3 2.5.7 3.7 1.2a13 13 0 0 1 3.2 1.8 8.1 8.1 0 0 1 3 6.5c0 2-.5 3.7-1.5 5.1a10 10 0 0 1-4.4 3.5c-1.8.8-4.1 1.2-6.8 1.2-2.6 0-4.9-.4-6.8-1.2-2-.8-3.4-2-4.5-3.5a10 10 0 0 1-1.7-5.6h6a5 5 0 0 0 3.5 4.6c1 .4 2.2.6 3.4.6 1.3 0 2.5-.2 3.5-.6 1-.4 1.8-1 2.4-1.7a4 4 0 0 0 .8-2.4c0-.9-.2-1.6-.7-2.2a11 11 0 0 0-2.1-1.4l-3.2-1-3.8-1c-2.8-.7-5-1.7-6.6-3.2a7.2 7.2 0 0 1-2.4-5.7 8 8 0 0 1 1.7-5 10 10 0 0 1 4.3-3.5c2-.8 4-1.2 6.4-1.2 2.3 0 4.4.4 6.2 1.2 1.8.8 3.2 2 4.3 3.4 1 1.4 1.5 3 1.5 5h-5.8z"/></svg>
@@ -0,0 +1 @@
1
+ <svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1155 1000"><path d="m577.3 0 577.4 1000H0z" fill="#fff"/></svg>
@@ -0,0 +1 @@
1
+ <svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path fill-rule="evenodd" clip-rule="evenodd" d="M1.5 2.5h13v10a1 1 0 0 1-1 1h-11a1 1 0 0 1-1-1zM0 1h16v11.5a2.5 2.5 0 0 1-2.5 2.5h-11A2.5 2.5 0 0 1 0 12.5zm3.75 4.5a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5M7 4.75a.75.75 0 1 1-1.5 0 .75.75 0 0 1 1.5 0m1.75.75a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5" fill="#666"/></svg>
@@ -0,0 +1,5 @@
1
+ import { ClaudePageContainer } from "@/features/chat/container/ClaudePageContainer";
2
+
3
+ export default function ClaudePage() {
4
+ return <ClaudePageContainer />;
5
+ }