@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,188 @@
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.runStart = runStart;
40
+ const fs_1 = require("fs");
41
+ const path = __importStar(require("path"));
42
+ const child_process_1 = require("child_process");
43
+ const chalk_1 = __importDefault(require("chalk"));
44
+ const ora_1 = __importDefault(require("ora"));
45
+ const project_init_1 = require("../utils/project-init");
46
+ const agent_tools_1 = require("../utils/agent-tools");
47
+ const proxy_1 = require("../utils/proxy");
48
+ const DEFAULT_PORT = 3020;
49
+ const UPSTREAM_READY_TIMEOUT_MS = 180_000;
50
+ async function runStart(options = {}) {
51
+ const platform = (0, agent_tools_1.getPlatform)();
52
+ const port = parsePort(options.port);
53
+ if (port === null) {
54
+ console.error(chalk_1.default.red(`✗ 잘못된 포트입니다: ${options.port}`));
55
+ process.exitCode = 1;
56
+ return;
57
+ }
58
+ const projectRoot = (0, project_init_1.findProjectRoot)(process.cwd());
59
+ if (!projectRoot) {
60
+ console.error(chalk_1.default.red('✗ jccli 프로젝트를 찾을 수 없습니다.'));
61
+ console.error(chalk_1.default.gray(' 먼저 jccli init 으로 프로젝트를 초기화한 뒤, 프로젝트 폴더 안에서 실행해 주세요.'));
62
+ process.exitCode = 1;
63
+ return;
64
+ }
65
+ if (!(0, fs_1.existsSync)(path.join(projectRoot, 'node_modules'))) {
66
+ console.error(chalk_1.default.red('✗ 프로젝트 의존성이 설치되어 있지 않습니다.'));
67
+ console.error(chalk_1.default.gray(` cd ${projectRoot} && jccli init 을 먼저 실행해 주세요.`));
68
+ process.exitCode = 1;
69
+ return;
70
+ }
71
+ const serverPort = port + 1;
72
+ const webPort = port + 2;
73
+ console.log(chalk_1.default.bold('\n🚀 jccli start\n'));
74
+ console.log(chalk_1.default.gray(`프로젝트: ${projectRoot}`));
75
+ console.log(chalk_1.default.gray(`포트: ${port} (내부: server ${serverPort}, web ${webPort})\n`));
76
+ const children = [];
77
+ let shuttingDown = false;
78
+ const shutdown = (code) => {
79
+ if (shuttingDown)
80
+ return;
81
+ shuttingDown = true;
82
+ for (const child of children) {
83
+ try {
84
+ child.kill();
85
+ }
86
+ catch {
87
+ // 이미 종료된 프로세스는 무시
88
+ }
89
+ }
90
+ process.exit(code);
91
+ };
92
+ process.on('SIGINT', () => shutdown(0));
93
+ process.on('SIGTERM', () => shutdown(0));
94
+ try {
95
+ const proxyServer = await (0, proxy_1.startProxyServer)({ port, serverPort, webPort });
96
+ proxyServer.on('error', (error) => {
97
+ console.error(chalk_1.default.red(`프록시 서버 오류: ${error.message}`));
98
+ });
99
+ }
100
+ catch (error) {
101
+ console.error(chalk_1.default.red(`✗ 포트 ${port} 를 사용할 수 없습니다. 이미 사용 중인지 확인해 주세요.`));
102
+ console.error(chalk_1.default.red(error instanceof Error ? error.message : String(error)));
103
+ process.exitCode = 1;
104
+ return;
105
+ }
106
+ const serverChild = spawnWorkspace({
107
+ label: 'server',
108
+ color: chalk_1.default.magenta,
109
+ npmArgs: ['run', 'dev', '--workspace', '@ji/server'],
110
+ env: { PORT: String(serverPort) },
111
+ projectRoot,
112
+ platform,
113
+ onExit: (code) => {
114
+ if (!shuttingDown) {
115
+ console.error(chalk_1.default.red(`\n[server] 프로세스가 종료되었습니다 (code: ${code ?? 'unknown'})`));
116
+ shutdown(1);
117
+ }
118
+ },
119
+ });
120
+ children.push(serverChild);
121
+ const webChild = spawnWorkspace({
122
+ label: 'web',
123
+ color: chalk_1.default.cyan,
124
+ npmArgs: ['run', 'dev', '--workspace', '@ji/web', '--', '--port', String(webPort)],
125
+ env: { NEXT_PUBLIC_SERVER_URL: `http://localhost:${port}` },
126
+ projectRoot,
127
+ platform,
128
+ onExit: (code) => {
129
+ if (!shuttingDown) {
130
+ console.error(chalk_1.default.red(`\n[web] 프로세스가 종료되었습니다 (code: ${code ?? 'unknown'})`));
131
+ shutdown(1);
132
+ }
133
+ },
134
+ });
135
+ children.push(webChild);
136
+ const spinner = (0, ora_1.default)({ text: '서버와 웹 앱을 시작하는 중...', color: 'cyan' }).start();
137
+ const [serverReady, webReady] = await Promise.all([
138
+ (0, proxy_1.waitForPort)(serverPort, UPSTREAM_READY_TIMEOUT_MS),
139
+ (0, proxy_1.waitForPort)(webPort, UPSTREAM_READY_TIMEOUT_MS),
140
+ ]);
141
+ if (!serverReady || !webReady) {
142
+ spinner.fail('시작 시간 초과');
143
+ const failed = [!serverReady && 'server', !webReady && 'web'].filter(Boolean).join(', ');
144
+ console.error(chalk_1.default.red(`✗ 다음 프로세스가 시간 안에 시작되지 않았습니다: ${failed}`));
145
+ console.error(chalk_1.default.gray(' 위에 출력된 로그를 확인해 주세요.'));
146
+ shutdown(1);
147
+ return;
148
+ }
149
+ spinner.succeed('모든 프로세스 시작 완료');
150
+ console.log();
151
+ console.log(chalk_1.default.bold.green(` ▶ http://localhost:${port}`));
152
+ console.log();
153
+ console.log(chalk_1.default.gray(' 종료하려면 Ctrl+C 를 누르세요.\n'));
154
+ }
155
+ function spawnWorkspace(options) {
156
+ const { label, color, npmArgs, env, projectRoot, platform, onExit } = options;
157
+ const child = (0, child_process_1.spawn)((0, agent_tools_1.getNpmCommand)(platform), npmArgs, {
158
+ cwd: projectRoot,
159
+ env: { ...process.env, ...env },
160
+ stdio: ['ignore', 'pipe', 'pipe'],
161
+ shell: platform === 'windows',
162
+ windowsHide: true,
163
+ });
164
+ const prefix = color(`[${label}]`);
165
+ const forward = (chunk) => {
166
+ for (const line of chunk.toString().split('\n')) {
167
+ if (line.trim().length === 0)
168
+ continue;
169
+ console.log(`${prefix} ${line}`);
170
+ }
171
+ };
172
+ child.stdout?.on('data', forward);
173
+ child.stderr?.on('data', forward);
174
+ child.on('exit', onExit);
175
+ child.on('error', (error) => {
176
+ console.error(chalk_1.default.red(`[${label}] 실행 오류: ${error.message}`));
177
+ onExit(null);
178
+ });
179
+ return child;
180
+ }
181
+ function parsePort(value) {
182
+ if (value === undefined)
183
+ return DEFAULT_PORT;
184
+ const parsed = Number(value);
185
+ if (!Number.isInteger(parsed) || parsed < 1 || parsed > 65533)
186
+ return null;
187
+ return parsed;
188
+ }
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
@@ -0,0 +1,71 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ const commander_1 = require("commander");
5
+ const init_1 = require("./commands/init");
6
+ const check_1 = require("./commands/check");
7
+ const start_1 = require("./commands/start");
8
+ async function main() {
9
+ const args = process.argv.slice(2);
10
+ // 단축 플래그 처리
11
+ if (args.includes('--init')) {
12
+ await (0, init_1.runInit)(parseInitArgs(args));
13
+ return;
14
+ }
15
+ if (args.includes('--check')) {
16
+ (0, check_1.runCheck)();
17
+ return;
18
+ }
19
+ const program = new commander_1.Command();
20
+ program
21
+ .name('jccli')
22
+ .description('Claude Code, Gemini CLI, and Codex web integration CLI')
23
+ .version('0.2.0');
24
+ program
25
+ .command('init [dir]')
26
+ .description('프로젝트를 복사/초기화하고 Claude Code, Gemini CLI, Codex 설치 상태를 확인합니다')
27
+ .option('-f, --force', '비어 있지 않은 대상 폴더에도 프로젝트 파일을 복사합니다')
28
+ .option('--skip-install', '프로젝트 npm install을 건너뜁니다')
29
+ .option('--skip-agents', '에이전트 CLI 설치 확인을 건너뜁니다')
30
+ .action(async (dir, options) => {
31
+ await (0, init_1.runInit)({
32
+ targetDir: dir,
33
+ force: options.force,
34
+ skipInstall: options.skipInstall,
35
+ skipAgents: options.skipAgents,
36
+ });
37
+ });
38
+ program
39
+ .command('start')
40
+ .description('서버와 웹 앱을 실행하고 단일 포트(기본 3020)에서 서비스합니다')
41
+ .option('-p, --port <port>', '서비스 포트', '3020')
42
+ .action(async (options) => {
43
+ await (0, start_1.runStart)({ port: options.port });
44
+ });
45
+ program
46
+ .command('check')
47
+ .description('Claude Code, Gemini CLI, Codex 설치 상태를 확인합니다')
48
+ .action(() => {
49
+ (0, check_1.runCheck)();
50
+ });
51
+ program.addHelpText('after', '\nExamples:\n jccli --init\n jccli init\n jccli init my-app\n jccli init --skip-install\n jccli start\n jccli start --port 4000\n jccli check\n');
52
+ if (args.length === 0) {
53
+ program.outputHelp();
54
+ return;
55
+ }
56
+ await program.parseAsync(process.argv);
57
+ }
58
+ function parseInitArgs(args) {
59
+ const initIndex = args[0] === 'init' ? 1 : 0;
60
+ const positional = args.slice(initIndex).find((arg) => !arg.startsWith('-'));
61
+ return {
62
+ targetDir: positional,
63
+ force: args.includes('--force') || args.includes('-f'),
64
+ skipInstall: args.includes('--skip-install'),
65
+ skipAgents: args.includes('--skip-agents'),
66
+ };
67
+ }
68
+ main().catch((err) => {
69
+ console.error(err);
70
+ process.exit(1);
71
+ });
@@ -0,0 +1,28 @@
1
+ export type SupportedPlatform = 'mac' | 'windows' | 'linux';
2
+ export interface AgentTool {
3
+ name: string;
4
+ command: string;
5
+ packageName: string;
6
+ requiresWindowsGitBash?: boolean;
7
+ }
8
+ export interface CommandLookup {
9
+ command: string;
10
+ path: string;
11
+ }
12
+ export interface CommandResult {
13
+ ok: boolean;
14
+ stdout: string;
15
+ stderr: string;
16
+ error?: string;
17
+ }
18
+ export declare const MIN_NODE_MAJOR = 18;
19
+ export declare const AGENT_TOOLS: AgentTool[];
20
+ export declare function getPlatform(): SupportedPlatform;
21
+ export declare function getNodeMajorVersion(): number;
22
+ export declare function getNpmCommand(platform: SupportedPlatform): string;
23
+ export declare function resolveCommand(command: string, platform?: SupportedPlatform): CommandLookup | null;
24
+ export declare function isCommandAvailable(command: string, platform?: SupportedPlatform): boolean;
25
+ export declare function runCommand(command: string, args: string[], platform?: SupportedPlatform, timeout?: number): CommandResult;
26
+ export declare function getCommandVersion(command: string, platform?: SupportedPlatform): string | null;
27
+ export declare function installGlobalPackage(packageName: string, platform?: SupportedPlatform): CommandResult;
28
+ export declare function findWindowsGitBashPath(): string | null;
@@ -0,0 +1,193 @@
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
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.AGENT_TOOLS = exports.MIN_NODE_MAJOR = void 0;
37
+ exports.getPlatform = getPlatform;
38
+ exports.getNodeMajorVersion = getNodeMajorVersion;
39
+ exports.getNpmCommand = getNpmCommand;
40
+ exports.resolveCommand = resolveCommand;
41
+ exports.isCommandAvailable = isCommandAvailable;
42
+ exports.runCommand = runCommand;
43
+ exports.getCommandVersion = getCommandVersion;
44
+ exports.installGlobalPackage = installGlobalPackage;
45
+ exports.findWindowsGitBashPath = findWindowsGitBashPath;
46
+ const fs_1 = require("fs");
47
+ const os = __importStar(require("os"));
48
+ const child_process_1 = require("child_process");
49
+ exports.MIN_NODE_MAJOR = 18;
50
+ exports.AGENT_TOOLS = [
51
+ {
52
+ name: 'Claude Code',
53
+ command: 'claude',
54
+ packageName: '@anthropic-ai/claude-code',
55
+ requiresWindowsGitBash: true,
56
+ },
57
+ {
58
+ name: 'Gemini CLI',
59
+ command: 'gemini',
60
+ packageName: '@google/gemini-cli',
61
+ },
62
+ {
63
+ name: 'Codex',
64
+ command: 'codex',
65
+ packageName: '@openai/codex',
66
+ },
67
+ ];
68
+ function getPlatform() {
69
+ const platform = os.platform();
70
+ if (platform === 'darwin')
71
+ return 'mac';
72
+ if (platform === 'win32')
73
+ return 'windows';
74
+ return 'linux';
75
+ }
76
+ function getNodeMajorVersion() {
77
+ return Number.parseInt(process.versions.node.split('.')[0] ?? '0', 10);
78
+ }
79
+ function getNpmCommand(platform) {
80
+ return platform === 'windows' ? 'npm.cmd' : 'npm';
81
+ }
82
+ function resolveCommand(command, platform = getPlatform()) {
83
+ try {
84
+ if (platform === 'windows') {
85
+ const candidates = command.match(/\.(cmd|exe|bat)$/i)
86
+ ? [command]
87
+ : [command, `${command}.cmd`, `${command}.exe`];
88
+ for (const candidate of candidates) {
89
+ const result = (0, child_process_1.spawnSync)('where.exe', [candidate], {
90
+ encoding: 'utf8',
91
+ windowsHide: true,
92
+ });
93
+ const path = result.stdout
94
+ ?.split(/\r?\n/)
95
+ .map((line) => line.trim())
96
+ .find(Boolean);
97
+ if (result.status === 0 && path) {
98
+ return { command, path };
99
+ }
100
+ }
101
+ return null;
102
+ }
103
+ const result = (0, child_process_1.spawnSync)('sh', ['-lc', `command -v ${quoteForPosixShell(command)}`], {
104
+ encoding: 'utf8',
105
+ });
106
+ const path = result.stdout.trim().split(/\r?\n/)[0];
107
+ if (result.status === 0 && path) {
108
+ return { command, path };
109
+ }
110
+ return null;
111
+ }
112
+ catch {
113
+ return null;
114
+ }
115
+ }
116
+ function isCommandAvailable(command, platform = getPlatform()) {
117
+ return resolveCommand(command, platform) !== null;
118
+ }
119
+ function runCommand(command, args, platform = getPlatform(), timeout = 5000) {
120
+ try {
121
+ const result = (0, child_process_1.spawnSync)(command, args, {
122
+ encoding: 'utf8',
123
+ shell: platform === 'windows',
124
+ timeout,
125
+ windowsHide: true,
126
+ });
127
+ return {
128
+ ok: result.status === 0 && !result.error,
129
+ stdout: result.stdout?.trim() ?? '',
130
+ stderr: result.stderr?.trim() ?? '',
131
+ error: result.error?.message,
132
+ };
133
+ }
134
+ catch (error) {
135
+ return {
136
+ ok: false,
137
+ stdout: '',
138
+ stderr: '',
139
+ error: error instanceof Error ? error.message : 'Unknown command error',
140
+ };
141
+ }
142
+ }
143
+ function getCommandVersion(command, platform = getPlatform()) {
144
+ const result = runCommand(command, ['--version'], platform);
145
+ if (!result.ok)
146
+ return null;
147
+ return [result.stdout, result.stderr].filter(Boolean).join('\n').trim() || null;
148
+ }
149
+ function installGlobalPackage(packageName, platform = getPlatform()) {
150
+ try {
151
+ const result = (0, child_process_1.spawnSync)(getNpmCommand(platform), ['install', '-g', packageName], {
152
+ stdio: ['inherit', 'pipe', 'pipe'],
153
+ encoding: 'utf8',
154
+ shell: platform === 'windows',
155
+ windowsHide: true,
156
+ });
157
+ return {
158
+ ok: result.status === 0 && !result.error,
159
+ stdout: result.stdout?.trim() ?? '',
160
+ stderr: result.stderr?.trim() ?? '',
161
+ error: result.error?.message,
162
+ };
163
+ }
164
+ catch (error) {
165
+ return {
166
+ ok: false,
167
+ stdout: '',
168
+ stderr: '',
169
+ error: error instanceof Error ? error.message : 'Unknown npm install error',
170
+ };
171
+ }
172
+ }
173
+ function findWindowsGitBashPath() {
174
+ if (getPlatform() !== 'windows')
175
+ return null;
176
+ const fromPath = resolveCommand('bash', 'windows');
177
+ if (fromPath)
178
+ return fromPath.path;
179
+ const candidates = [
180
+ process.env.CLAUDE_CODE_GIT_BASH_PATH,
181
+ 'C:\\Program Files\\Git\\bin\\bash.exe',
182
+ 'C:\\Program Files (x86)\\Git\\bin\\bash.exe',
183
+ ].filter((value) => Boolean(value));
184
+ try {
185
+ return candidates.find((candidate) => (0, fs_1.existsSync)(candidate)) ?? null;
186
+ }
187
+ catch {
188
+ return null;
189
+ }
190
+ }
191
+ function quoteForPosixShell(value) {
192
+ return `'${value.replace(/'/g, "'\\''")}'`;
193
+ }
@@ -0,0 +1,12 @@
1
+ import { type SupportedPlatform } from './agent-tools';
2
+ export interface InitProjectOptions {
3
+ targetDir?: string;
4
+ force?: boolean;
5
+ skipInstall?: boolean;
6
+ }
7
+ export interface ProjectReadyResult {
8
+ ok: boolean;
9
+ projectRoot: string;
10
+ }
11
+ export declare function ensureProjectReady(options: InitProjectOptions, platform: SupportedPlatform): ProjectReadyResult;
12
+ export declare function findProjectRoot(startDir: string): string | null;