@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,175 @@
1
+ "use client";
2
+
3
+ import {
4
+ createContext,
5
+ useCallback,
6
+ useContext,
7
+ useEffect,
8
+ useRef,
9
+ useState,
10
+ } from "react";
11
+ import { createPortal } from "react-dom";
12
+
13
+ // ─── Types ────────────────────────────────────────────────────────────────────
14
+
15
+ export type ToastType = "success" | "error" | "info";
16
+
17
+ export interface ToastItem {
18
+ id: string;
19
+ type: ToastType;
20
+ title: string;
21
+ message?: string;
22
+ }
23
+
24
+ interface ToastCtx {
25
+ addToast: (toast: Omit<ToastItem, "id">) => void;
26
+ removeToast: (id: string) => void;
27
+ }
28
+
29
+ const AUTO_DISMISS_MS = 4500;
30
+
31
+ // ─── Context ──────────────────────────────────────────────────────────────────
32
+
33
+ const ToastContext = createContext<ToastCtx | null>(null);
34
+
35
+ export function useToast() {
36
+ const ctx = useContext(ToastContext);
37
+ if (!ctx) throw new Error("useToast must be used within ToastProvider");
38
+ return ctx;
39
+ }
40
+
41
+ // ─── Single Toast Item ────────────────────────────────────────────────────────
42
+
43
+ const TYPE_STYLES: Record<
44
+ ToastType,
45
+ { wrap: string; iconColor: string; icon: React.ReactNode }
46
+ > = {
47
+ success: {
48
+ wrap: "border-emerald-500/30 bg-emerald-500/[0.08] text-emerald-800 dark:text-emerald-200",
49
+ iconColor: "text-emerald-500",
50
+ icon: (
51
+ <svg viewBox="0 0 16 16" fill="currentColor" className="h-4 w-4">
52
+ <path
53
+ fillRule="evenodd"
54
+ d="M12.416 3.376a.75.75 0 01.208 1.04l-5 7.5a.75.75 0 01-1.154.114l-3-3a.75.75 0 011.06-1.06l2.353 2.353 4.493-6.74a.75.75 0 011.04-.207z"
55
+ clipRule="evenodd"
56
+ />
57
+ </svg>
58
+ ),
59
+ },
60
+ error: {
61
+ wrap: "border-red-500/30 bg-red-500/[0.08] text-red-800 dark:text-red-200",
62
+ iconColor: "text-red-500",
63
+ icon: (
64
+ <svg viewBox="0 0 16 16" fill="currentColor" className="h-4 w-4">
65
+ <path
66
+ fillRule="evenodd"
67
+ d="M8 1a7 7 0 100 14A7 7 0 008 1zM6.47 6.47a.75.75 0 011.06 0L8 6.94l.47-.47a.75.75 0 111.06 1.06L9.06 8l.47.47a.75.75 0 11-1.06 1.06L8 9.06l-.47.47a.75.75 0 01-1.06-1.06L6.94 8l-.47-.47a.75.75 0 010-1.06z"
68
+ clipRule="evenodd"
69
+ />
70
+ </svg>
71
+ ),
72
+ },
73
+ info: {
74
+ wrap: "border-blue-500/30 bg-blue-500/[0.08] text-blue-800 dark:text-blue-200",
75
+ iconColor: "text-blue-500",
76
+ icon: (
77
+ <svg viewBox="0 0 16 16" fill="currentColor" className="h-4 w-4">
78
+ <path
79
+ fillRule="evenodd"
80
+ d="M8 1a7 7 0 100 14A7 7 0 008 1zm.75 4.5a.75.75 0 00-1.5 0v3.75a.75.75 0 001.5 0V5.5zm-.75 7.25a1 1 0 100-2 1 1 0 000 2z"
81
+ clipRule="evenodd"
82
+ />
83
+ </svg>
84
+ ),
85
+ },
86
+ };
87
+
88
+ interface ToastItemProps {
89
+ toast: ToastItem;
90
+ onRemove: () => void;
91
+ }
92
+
93
+ function ToastEntry({ toast, onRemove }: ToastItemProps) {
94
+ const [leaving, setLeaving] = useState(false);
95
+ const style = TYPE_STYLES[toast.type];
96
+
97
+ const handleRemove = useCallback(() => {
98
+ setLeaving(true);
99
+ setTimeout(onRemove, 190);
100
+ }, [onRemove]);
101
+
102
+ return (
103
+ <div
104
+ className={[
105
+ "flex items-start gap-3 rounded-xl border px-3.5 py-3 shadow-lg",
106
+ style.wrap,
107
+ leaving ? "animate-toast-out" : "animate-toast-in",
108
+ ].join(" ")}
109
+ >
110
+ <span className={`mt-0.5 shrink-0 ${style.iconColor}`}>{style.icon}</span>
111
+ <div className="flex min-w-0 flex-1 flex-col gap-0.5">
112
+ <p className="text-sm font-semibold leading-tight">{toast.title}</p>
113
+ {toast.message && (
114
+ <p className="text-xs opacity-60 leading-snug">{toast.message}</p>
115
+ )}
116
+ </div>
117
+ <button
118
+ type="button"
119
+ onClick={handleRemove}
120
+ className="mt-0.5 shrink-0 opacity-35 transition-opacity hover:opacity-70"
121
+ aria-label="λ‹«κΈ°"
122
+ >
123
+ <svg viewBox="0 0 16 16" fill="currentColor" className="h-3.5 w-3.5">
124
+ <path d="M3.72 3.72a.75.75 0 011.06 0L8 6.94l3.22-3.22a.75.75 0 111.06 1.06L9.06 8l3.22 3.22a.75.75 0 11-1.06 1.06L8 9.06l-3.22 3.22a.75.75 0 01-1.06-1.06L6.94 8 3.72 4.78a.75.75 0 010-1.06z" />
125
+ </svg>
126
+ </button>
127
+ </div>
128
+ );
129
+ }
130
+
131
+ // ─── Provider ─────────────────────────────────────────────────────────────────
132
+
133
+ export function ToastProvider({ children }: { children: React.ReactNode }) {
134
+ const [toasts, setToasts] = useState<ToastItem[]>([]);
135
+ const timersRef = useRef<Map<string, ReturnType<typeof setTimeout>>>(new Map());
136
+ const [mounted, setMounted] = useState(false);
137
+
138
+ useEffect(() => { setMounted(true); }, []);
139
+
140
+ const removeToast = useCallback((id: string) => {
141
+ setToasts((prev) => prev.filter((t) => t.id !== id));
142
+ const timer = timersRef.current.get(id);
143
+ if (timer) {
144
+ clearTimeout(timer);
145
+ timersRef.current.delete(id);
146
+ }
147
+ }, []);
148
+
149
+ const addToast = useCallback(
150
+ (toast: Omit<ToastItem, "id">) => {
151
+ const id = `${Date.now()}-${Math.random().toString(36).slice(2, 7)}`;
152
+ setToasts((prev) => [...prev, { ...toast, id }]);
153
+ const timer = setTimeout(() => removeToast(id), AUTO_DISMISS_MS);
154
+ timersRef.current.set(id, timer);
155
+ },
156
+ [removeToast],
157
+ );
158
+
159
+ return (
160
+ <ToastContext.Provider value={{ addToast, removeToast }}>
161
+ {children}
162
+ {mounted &&
163
+ createPortal(
164
+ <div className="pointer-events-none fixed right-4 top-4 z-[9999] flex w-80 flex-col gap-2">
165
+ {toasts.map((toast) => (
166
+ <div key={toast.id} className="pointer-events-auto">
167
+ <ToastEntry toast={toast} onRemove={() => removeToast(toast.id)} />
168
+ </div>
169
+ ))}
170
+ </div>,
171
+ document.body,
172
+ )}
173
+ </ToastContext.Provider>
174
+ );
175
+ }
@@ -0,0 +1,38 @@
1
+ import { create } from "zustand";
2
+
3
+ import { getAuthStatus, getCodexAuthStatus, getGeminiAuthStatus } from "@/features/auth/api/auth.api";
4
+ import type { AgentId } from "@/features/chat/ui/AgentSelectModal";
5
+ import type { ConnectionStatus } from "@/features/chat/hooks/useUnifiedSessions";
6
+
7
+ interface AgentStatusState {
8
+ statusByAgent: Record<AgentId, ConnectionStatus>;
9
+ loading: boolean;
10
+ refresh: () => Promise<void>;
11
+ }
12
+
13
+ export const useAgentStatusStore = create<AgentStatusState>((set) => ({
14
+ statusByAgent: {
15
+ claude: "disconnected",
16
+ gemini: "disconnected",
17
+ codex: "disconnected",
18
+ opencode: "disconnected",
19
+ },
20
+ loading: false,
21
+ refresh: async () => {
22
+ set({ loading: true });
23
+ const [claude, gemini, codex] = await Promise.allSettled([
24
+ getAuthStatus(),
25
+ getGeminiAuthStatus(),
26
+ getCodexAuthStatus(),
27
+ ]);
28
+ set({
29
+ loading: false,
30
+ statusByAgent: {
31
+ claude: claude.status === "fulfilled" && claude.value.loggedIn ? "connected" : "disconnected",
32
+ gemini: gemini.status === "fulfilled" && gemini.value.loggedIn && gemini.value.installed ? "connected" : "disconnected",
33
+ codex: codex.status === "fulfilled" && codex.value.loggedIn && codex.value.installed ? "connected" : "disconnected",
34
+ opencode: "disconnected",
35
+ },
36
+ });
37
+ },
38
+ }));
@@ -0,0 +1,18 @@
1
+ {
2
+ "extends": "@ji/typescript-config/nextjs.json",
3
+ "compilerOptions": {
4
+ "lib": ["dom", "dom.iterable", "esnext"],
5
+ "jsx": "react-jsx",
6
+ "paths": {
7
+ "@/*": ["./src/*"]
8
+ }
9
+ },
10
+ "include": [
11
+ "next-env.d.ts",
12
+ "**/*.ts",
13
+ "**/*.tsx",
14
+ ".next/types/**/*.ts",
15
+ ".next/dev/types/**/*.ts"
16
+ ],
17
+ "exclude": ["node_modules"]
18
+ }
@@ -0,0 +1,25 @@
1
+ import path from "path";
2
+
3
+ import react from "@vitejs/plugin-react";
4
+ import { defineConfig } from "vitest/config";
5
+
6
+ export default defineConfig({
7
+ plugins: [react()],
8
+ test: {
9
+ environment: "jsdom",
10
+ setupFiles: ["./vitest.setup.ts"],
11
+ globals: true,
12
+ css: false,
13
+ coverage: {
14
+ provider: "v8",
15
+ reporter: ["text", "lcov"],
16
+ include: ["src/features/**", "src/lib/**", "src/components/**"],
17
+ exclude: ["**/__tests__/**", "**/*.d.ts"],
18
+ },
19
+ },
20
+ resolve: {
21
+ alias: {
22
+ "@": path.resolve(__dirname, "./src"),
23
+ },
24
+ },
25
+ });
@@ -0,0 +1,10 @@
1
+ import "@testing-library/jest-dom";
2
+
3
+ // crypto.randomUUID polyfill for jsdom
4
+ if (!globalThis.crypto.randomUUID) {
5
+ (globalThis.crypto as { randomUUID?: unknown }).randomUUID = () =>
6
+ "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => {
7
+ const r = Math.floor(Math.random() * 16);
8
+ return (c === "x" ? r : (r & 0x3) | 0x8).toString(16);
9
+ });
10
+ }
package/package.json ADDED
@@ -0,0 +1,85 @@
1
+ {
2
+ "name": "@jcjeon/integration-cli",
3
+ "version": "0.2.0",
4
+ "private": false,
5
+ "license": "MIT",
6
+ "publishConfig": {
7
+ "access": "public"
8
+ },
9
+ "description": "Local web workspace for Claude Code, Gemini CLI, and Codex tasks",
10
+ "bin": {
11
+ "jccli": "./packages/cli/dist/index.js"
12
+ },
13
+ "files": [
14
+ ".gitignore",
15
+ ".npmignore",
16
+ ".prettierignore",
17
+ ".prettierrc",
18
+ "AGENTS.md",
19
+ "CLAUDE.md",
20
+ "README.md",
21
+ "apps/server/.gitignore",
22
+ "apps/server/README.md",
23
+ "apps/server/eslint.config.mjs",
24
+ "apps/server/nest-cli.json",
25
+ "apps/server/package.json",
26
+ "apps/server/scripts",
27
+ "apps/server/src",
28
+ "apps/server/test",
29
+ "apps/server/tsconfig.build.json",
30
+ "apps/server/tsconfig.json",
31
+ "apps/web/.gitignore",
32
+ "apps/web/AGENTS.md",
33
+ "apps/web/CLAUDE.md",
34
+ "apps/web/README.md",
35
+ "apps/web/eslint.config.mjs",
36
+ "apps/web/next-env.d.ts",
37
+ "apps/web/next.config.ts",
38
+ "apps/web/package.json",
39
+ "apps/web/postcss.config.mjs",
40
+ "apps/web/public",
41
+ "apps/web/src",
42
+ "apps/web/tsconfig.json",
43
+ "apps/web/vitest.config.ts",
44
+ "apps/web/vitest.setup.ts",
45
+ "packages/cli/dist",
46
+ "packages/cli/package.json",
47
+ "packages/cli/src",
48
+ "packages/cli/tsconfig.json",
49
+ "packages/eslint-config",
50
+ "packages/typescript-config/base.json",
51
+ "packages/typescript-config/nestjs.json",
52
+ "packages/typescript-config/nextjs.json",
53
+ "packages/typescript-config/package.json",
54
+ "turbo.json"
55
+ ],
56
+ "workspaces": [
57
+ "apps/*",
58
+ "packages/*"
59
+ ],
60
+ "scripts": {
61
+ "prepack": "npm run build --workspace @ji/cli",
62
+ "dev": "turbo run dev",
63
+ "build": "turbo run build",
64
+ "lint": "turbo run lint",
65
+ "lint:fix": "turbo run lint:fix",
66
+ "format": "prettier --write \"**/*.{ts,tsx,js,mjs,json,md,css}\"",
67
+ "format:check": "prettier --check \"**/*.{ts,tsx,js,mjs,json,md,css}\"",
68
+ "test": "turbo run test",
69
+ "clean": "turbo run clean && rm -rf node_modules"
70
+ },
71
+ "dependencies": {
72
+ "chalk": "^5.4.1",
73
+ "commander": "^13.1.0",
74
+ "ora": "^8.2.0"
75
+ },
76
+ "devDependencies": {
77
+ "prettier": "^3.4.2",
78
+ "prettier-plugin-tailwindcss": "^0.8.0",
79
+ "turbo": "^2.5.4"
80
+ },
81
+ "engines": {
82
+ "node": ">=20"
83
+ },
84
+ "packageManager": "npm@10.9.7"
85
+ }
@@ -0,0 +1 @@
1
+ export declare function runCheck(): void;
@@ -0,0 +1,89 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ var __importDefault = (this && this.__importDefault) || function (mod) {
36
+ return (mod && mod.__esModule) ? mod : { "default": mod };
37
+ };
38
+ Object.defineProperty(exports, "__esModule", { value: true });
39
+ exports.runCheck = runCheck;
40
+ const os = __importStar(require("os"));
41
+ const chalk_1 = __importDefault(require("chalk"));
42
+ const agent_tools_1 = require("../utils/agent-tools");
43
+ function runCheck() {
44
+ const platform = (0, agent_tools_1.getPlatform)();
45
+ const results = agent_tools_1.AGENT_TOOLS.map((tool) => getToolStatus(tool, platform));
46
+ console.log(chalk_1.default.bold('\nπŸ” μ—μ΄μ „νŠΈ CLI μ„€μΉ˜ μƒνƒœ 확인\n'));
47
+ console.log(chalk_1.default.gray(`ν”Œλž«νΌ: ${platform} (${os.arch()})\n`));
48
+ for (const result of results) {
49
+ if (result.installed) {
50
+ console.log(chalk_1.default.green(` βœ“ ${result.name.padEnd(14)} ${result.version ?? ''}`));
51
+ continue;
52
+ }
53
+ console.log(chalk_1.default.red(` βœ— ${result.name.padEnd(14)} λ―Έμ„€μΉ˜`));
54
+ console.log(chalk_1.default.gray(` μ„€μΉ˜: npm install -g ${result.packageName}`));
55
+ }
56
+ printWindowsClaudeNotice(platform);
57
+ printSummary(results);
58
+ }
59
+ function getToolStatus(tool, platform) {
60
+ const installed = (0, agent_tools_1.isCommandAvailable)(tool.command, platform);
61
+ return {
62
+ name: tool.name,
63
+ packageName: tool.packageName,
64
+ installed,
65
+ version: installed ? (0, agent_tools_1.getCommandVersion)(tool.command, platform) : null,
66
+ };
67
+ }
68
+ function printWindowsClaudeNotice(platform) {
69
+ if (platform !== 'windows')
70
+ return;
71
+ const gitBashPath = (0, agent_tools_1.findWindowsGitBashPath)();
72
+ if (gitBashPath) {
73
+ console.log(chalk_1.default.gray(`\nClaude Code Git Bash 감지: ${gitBashPath}`));
74
+ return;
75
+ }
76
+ console.log(chalk_1.default.yellow('\nClaude Code μ‹€ν–‰μ—λŠ” Git for Windows λ˜λŠ” WSL이 ν•„μš”ν•  수 μžˆμŠ΅λ‹ˆλ‹€.'));
77
+ }
78
+ function printSummary(results) {
79
+ const missing = results.filter((result) => !result.installed);
80
+ console.log();
81
+ if (missing.length === 0) {
82
+ console.log(chalk_1.default.bold.green('λͺ¨λ“  도ꡬ가 μ„€μΉ˜λ˜μ–΄ μžˆμŠ΅λ‹ˆλ‹€.'));
83
+ }
84
+ else {
85
+ console.log(chalk_1.default.yellow(`λ―Έμ„€μΉ˜ 도ꡬ: ${missing.map((result) => result.name).join(', ')}`));
86
+ console.log(chalk_1.default.gray(' jccli init 으둜 ν•œ λ²ˆμ— μ„€μΉ˜ν•  수 μžˆμŠ΅λ‹ˆλ‹€.'));
87
+ }
88
+ console.log();
89
+ }
@@ -0,0 +1,5 @@
1
+ import { type InitProjectOptions } from '../utils/project-init';
2
+ export interface RunInitOptions extends InitProjectOptions {
3
+ skipAgents?: boolean;
4
+ }
5
+ export declare function runInit(options?: RunInitOptions): Promise<void>;
@@ -0,0 +1,183 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ var __importDefault = (this && this.__importDefault) || function (mod) {
36
+ return (mod && mod.__esModule) ? mod : { "default": mod };
37
+ };
38
+ Object.defineProperty(exports, "__esModule", { value: true });
39
+ exports.runInit = runInit;
40
+ const os = __importStar(require("os"));
41
+ const chalk_1 = __importDefault(require("chalk"));
42
+ const ora_1 = __importDefault(require("ora"));
43
+ const project_init_1 = require("../utils/project-init");
44
+ const agent_tools_1 = require("../utils/agent-tools");
45
+ async function runInit(options = {}) {
46
+ const platform = (0, agent_tools_1.getPlatform)();
47
+ const failures = [];
48
+ console.log(chalk_1.default.bold('\nπŸš€ jccli init - ν”„λ‘œμ νŠΈ μ΄ˆκΈ°ν™”\n'));
49
+ console.log(chalk_1.default.gray(`ν”Œλž«νΌ: ${platform} (${os.arch()})`));
50
+ console.log(chalk_1.default.gray(`Node.js: ${process.version}\n`));
51
+ if (!validateNodeVersion()) {
52
+ process.exitCode = 1;
53
+ return;
54
+ }
55
+ if (!validateNpm(platform)) {
56
+ process.exitCode = 1;
57
+ return;
58
+ }
59
+ const projectReady = (0, project_init_1.ensureProjectReady)(options, platform);
60
+ if (!projectReady.ok) {
61
+ process.exitCode = 1;
62
+ return;
63
+ }
64
+ if (options.skipAgents) {
65
+ printProjectOnlySummary(projectReady.projectRoot);
66
+ return;
67
+ }
68
+ printWindowsClaudeNotice(platform);
69
+ for (const tool of agent_tools_1.AGENT_TOOLS) {
70
+ const ok = ensureToolInstalled(tool, platform);
71
+ if (!ok)
72
+ failures.push(tool.name);
73
+ }
74
+ printSummary(failures, platform, projectReady.projectRoot);
75
+ if (failures.length > 0) {
76
+ process.exitCode = 1;
77
+ }
78
+ }
79
+ function validateNodeVersion() {
80
+ const nodeMajor = (0, agent_tools_1.getNodeMajorVersion)();
81
+ if (nodeMajor >= agent_tools_1.MIN_NODE_MAJOR) {
82
+ console.log(chalk_1.default.green(`βœ“ Node.js ${process.version}`));
83
+ return true;
84
+ }
85
+ console.error(chalk_1.default.red(`βœ— Node.js v${agent_tools_1.MIN_NODE_MAJOR}+ 이상이 ν•„μš”ν•©λ‹ˆλ‹€. ν˜„μž¬: ${process.version}`));
86
+ console.error(chalk_1.default.yellow(' https://nodejs.org μ—μ„œ μ΅œμ‹  Node.jsλ₯Ό μ„€μΉ˜ν•΄ μ£Όμ„Έμš”.'));
87
+ return false;
88
+ }
89
+ function validateNpm(platform) {
90
+ if (!(0, agent_tools_1.isCommandAvailable)('npm', platform)) {
91
+ console.error(chalk_1.default.red('βœ— npm이 μ„€μΉ˜λ˜μ–΄ μžˆμ§€ μ•ŠμŠ΅λ‹ˆλ‹€.'));
92
+ console.error(chalk_1.default.yellow(' Node.js μž¬μ„€μΉ˜ λ˜λŠ” npm을 λ¨Όμ € μ„€μΉ˜ν•΄ μ£Όμ„Έμš”.'));
93
+ return false;
94
+ }
95
+ const npmVersion = (0, agent_tools_1.getCommandVersion)((0, agent_tools_1.getNpmCommand)(platform), platform) ?? 'unknown';
96
+ console.log(chalk_1.default.green(`βœ“ npm v${npmVersion}\n`));
97
+ return true;
98
+ }
99
+ function printWindowsClaudeNotice(platform) {
100
+ if (platform !== 'windows')
101
+ return;
102
+ const gitBashPath = (0, agent_tools_1.findWindowsGitBashPath)();
103
+ if (gitBashPath) {
104
+ console.log(chalk_1.default.gray(`Claude Code Git Bash 감지: ${gitBashPath}\n`));
105
+ return;
106
+ }
107
+ console.log(chalk_1.default.yellow('Claude CodeλŠ” Windows λ„€μ΄ν‹°λΈŒ μ‹€ν–‰ μ‹œ Git for Windowsκ°€ ν•„μš”ν•  수 μžˆμŠ΅λ‹ˆλ‹€.'));
108
+ console.log(chalk_1.default.gray(' μ„€μΉ˜ 후에도 claude 싀행이 μ‹€νŒ¨ν•˜λ©΄ Git for Windowsλ₯Ό μ„€μΉ˜ν•˜κ±°λ‚˜'));
109
+ console.log(chalk_1.default.gray(' CLAUDE_CODE_GIT_BASH_PATH ν™˜κ²½ λ³€μˆ˜λ₯Ό bash.exe 경둜둜 μ„€μ •ν•΄ μ£Όμ„Έμš”.\n'));
110
+ }
111
+ function ensureToolInstalled(tool, platform) {
112
+ console.log(chalk_1.default.bold(`[ ${tool.name} ]`));
113
+ if ((0, agent_tools_1.isCommandAvailable)(tool.command, platform)) {
114
+ const version = (0, agent_tools_1.getCommandVersion)(tool.command, platform) ?? 'version unknown';
115
+ console.log(chalk_1.default.green(`βœ“ 이미 μ„€μΉ˜λ¨: ${version}\n`));
116
+ return true;
117
+ }
118
+ console.log(chalk_1.default.yellow(` λ―Έμ„€μΉ˜ μƒνƒœμž…λ‹ˆλ‹€. ${tool.packageName} μ„€μΉ˜λ₯Ό μ‹œμž‘ν•©λ‹ˆλ‹€.`));
119
+ const spinner = (0, ora_1.default)({
120
+ text: `${tool.packageName} μ „μ—­ μ„€μΉ˜ 쀑...`,
121
+ color: 'cyan',
122
+ }).start();
123
+ const result = (0, agent_tools_1.installGlobalPackage)(tool.packageName, platform);
124
+ if (!result.ok) {
125
+ spinner.fail('μ„€μΉ˜ μ‹€νŒ¨');
126
+ printInstallError(result.stderr, result.error, tool, platform);
127
+ return false;
128
+ }
129
+ spinner.succeed('μ„€μΉ˜ μ™„λ£Œ');
130
+ const verifySpinner = (0, ora_1.default)('μ„€μΉ˜ 확인 쀑...').start();
131
+ if (!(0, agent_tools_1.isCommandAvailable)(tool.command, platform)) {
132
+ verifySpinner.fail(`${tool.command} λͺ…령을 찾을 수 μ—†μŠ΅λ‹ˆλ‹€.`);
133
+ console.log(chalk_1.default.yellow(' μƒˆ 터미널을 μ—΄κ±°λ‚˜ PATH 섀정을 확인해 μ£Όμ„Έμš”.\n'));
134
+ return false;
135
+ }
136
+ const version = (0, agent_tools_1.getCommandVersion)(tool.command, platform) ?? 'version unknown';
137
+ verifySpinner.succeed(`${tool.name} μ„€μΉ˜ 확인: ${version}`);
138
+ console.log();
139
+ return true;
140
+ }
141
+ function printInstallError(stderr, error, tool, platform) {
142
+ console.error(chalk_1.default.red('\nμ„€μΉ˜ 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€:'));
143
+ if (stderr)
144
+ console.error(chalk_1.default.red(stderr));
145
+ if (error)
146
+ console.error(chalk_1.default.red(error));
147
+ console.error(chalk_1.default.yellow('\nμˆ˜λ™ μ„€μΉ˜ λͺ…λ Ή:'));
148
+ console.error(chalk_1.default.gray(` npm install -g ${tool.packageName}`));
149
+ if (platform === 'windows' && tool.requiresWindowsGitBash) {
150
+ console.error(chalk_1.default.gray(' Claude Code μ‹€ν–‰μ—λŠ” Git for Windows λ˜λŠ” WSL이 ν•„μš”ν•  수 μžˆμŠ΅λ‹ˆλ‹€.'));
151
+ }
152
+ if (platform !== 'windows') {
153
+ console.error(chalk_1.default.gray(' κΆŒν•œ 였λ₯˜κ°€ λ‚˜λ©΄ npm global prefix λ˜λŠ” Node μ„€μΉ˜ 방식을 확인해 μ£Όμ„Έμš”.'));
154
+ }
155
+ console.log();
156
+ }
157
+ function printProjectOnlySummary(projectRoot) {
158
+ console.log(chalk_1.default.bold.green('ν”„λ‘œμ νŠΈ μ΄ˆκΈ°ν™”κ°€ μ™„λ£Œλ˜μ—ˆμŠ΅λ‹ˆλ‹€.\n'));
159
+ console.log(chalk_1.default.cyan('λ‹€μŒ λͺ…λ ΉμœΌλ‘œ μ‹€ν–‰ν•  수 μžˆμŠ΅λ‹ˆλ‹€:'));
160
+ console.log(chalk_1.default.gray(` cd ${projectRoot}`));
161
+ console.log(chalk_1.default.gray(' jccli start # http://localhost:3020\n'));
162
+ }
163
+ function printSummary(failures, platform, projectRoot) {
164
+ if (failures.length === 0) {
165
+ console.log(chalk_1.default.bold.green('λͺ¨λ“  μ—μ΄μ „νŠΈ CLI μ„€μΉ˜ 확인이 μ™„λ£Œλ˜μ—ˆμŠ΅λ‹ˆλ‹€.\n'));
166
+ console.log(chalk_1.default.cyan('λ‹€μŒ λͺ…λ ΉμœΌλ‘œ μ‹œμž‘ν•  수 μžˆμŠ΅λ‹ˆλ‹€:'));
167
+ console.log(chalk_1.default.gray(` cd ${projectRoot}`));
168
+ console.log(chalk_1.default.gray(' jccli start # http://localhost:3020'));
169
+ console.log();
170
+ console.log(chalk_1.default.cyan('μ—μ΄μ „νŠΈ CLI 직접 μ‹€ν–‰:'));
171
+ console.log(chalk_1.default.gray(' claude # Claude Code'));
172
+ console.log(chalk_1.default.gray(' gemini # Gemini CLI'));
173
+ console.log(chalk_1.default.gray(' codex # OpenAI Codex\n'));
174
+ if (platform !== 'windows') {
175
+ console.log(chalk_1.default.yellow('λͺ…령을 찾을 수 μ—†μœΌλ©΄ 터미널을 λ‹€μ‹œ μ—΄κ±°λ‚˜ μ…Έ 섀정을 λ‹€μ‹œ λ‘œλ“œν•΄ μ£Όμ„Έμš”.'));
176
+ console.log(chalk_1.default.gray(' source ~/.zshrc # λ˜λŠ” ~/.bashrc\n'));
177
+ }
178
+ return;
179
+ }
180
+ console.log(chalk_1.default.bold.yellow('일뢀 μ—μ΄μ „νŠΈ CLI μ„€μΉ˜ λ˜λŠ” 확인에 μ‹€νŒ¨ν–ˆμŠ΅λ‹ˆλ‹€.'));
181
+ console.log(chalk_1.default.yellow(`μ‹€νŒ¨ ν•­λͺ©: ${failures.join(', ')}`));
182
+ console.log(chalk_1.default.gray('μœ„ μ•ˆλ‚΄μ— 따라 μˆ˜λ™ μ„€μΉ˜ λ˜λŠ” PATH 섀정을 확인해 μ£Όμ„Έμš”.\n'));
183
+ }
@@ -0,0 +1,4 @@
1
+ export interface RunStartOptions {
2
+ port?: string;
3
+ }
4
+ export declare function runStart(options?: RunStartOptions): Promise<void>;