tdecollab 0.1.3 → 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.
package/dist/cli.js CHANGED
@@ -3,11 +3,25 @@ import {
3
3
  ConfluenceContentApi,
4
4
  ConfluenceSearchApi,
5
5
  ConfluenceSpaceApi,
6
+ GitlabBranchApi,
7
+ GitlabMergeRequestApi,
8
+ GitlabPipelineApi,
9
+ GitlabProjectApi,
10
+ GitlabRepositoryApi,
11
+ JiraCommentApi,
12
+ JiraIssueApi,
13
+ JiraProjectApi,
14
+ JiraSearchApi,
15
+ JiraTransitionApi,
6
16
  MarkdownToStorageConverter,
7
17
  StorageToMarkdownConverter,
8
18
  createConfluenceClient,
9
- loadConfluenceConfig
10
- } from "./chunk-K5LQBRHJ.js";
19
+ createGitlabClient,
20
+ createJiraClient,
21
+ loadConfluenceConfig,
22
+ loadGitlabConfig,
23
+ loadJiraConfig
24
+ } from "./chunk-2IQ4QMK3.js";
11
25
  import "./chunk-SJ7KPK6Q.js";
12
26
 
13
27
  // src/cli.ts
@@ -152,14 +166,571 @@ function registerConfluenceCommands(program2) {
152
166
  });
153
167
  }
154
168
 
169
+ // src/jira/commands/index.ts
170
+ import chalk2 from "chalk";
171
+ import Table2 from "cli-table3";
172
+ function registerJiraCommands(program2) {
173
+ const jiraCmd = program2.command("jira").description("JIRA \uAD00\uB9AC");
174
+ const initClient = () => {
175
+ try {
176
+ const config = loadJiraConfig();
177
+ return createJiraClient(config);
178
+ } catch (e) {
179
+ console.error(chalk2.red(`\uC124\uC815 \uB85C\uB4DC \uC2E4\uD328: ${e.message}`));
180
+ process.exit(1);
181
+ }
182
+ };
183
+ const issueCmd = jiraCmd.command("issue").description("\uC774\uC288 \uAD00\uB9AC");
184
+ issueCmd.command("get <issueKey>").description("\uC774\uC288 \uC870\uD68C").action(async (issueKey) => {
185
+ const client = initClient();
186
+ const api = new JiraIssueApi(client);
187
+ try {
188
+ const issue = await api.getIssue(issueKey);
189
+ const f = issue.fields;
190
+ console.log(chalk2.bold(`[${issue.key}] ${f.summary}`));
191
+ console.log(chalk2.gray(`Status: ${f.status?.name || "N/A"}`));
192
+ console.log(`Type: ${f.issuetype?.name || "N/A"}`);
193
+ console.log(`Priority: ${f.priority?.name || "N/A"}`);
194
+ console.log(`Assignee: ${f.assignee?.displayName || "\uBBF8\uBC30\uC815"}`);
195
+ console.log(`Reporter: ${f.reporter?.displayName || "N/A"}`);
196
+ console.log(`Labels: ${f.labels?.join(", ") || "\uC5C6\uC74C"}`);
197
+ console.log(`Created: ${f.created || "N/A"}`);
198
+ console.log(`Updated: ${f.updated || "N/A"}`);
199
+ if (f.description) {
200
+ console.log(chalk2.dim("\n--- Description ---"));
201
+ console.log(f.description);
202
+ }
203
+ if (f.subtasks && f.subtasks.length > 0) {
204
+ console.log(chalk2.dim("\n--- Subtasks ---"));
205
+ f.subtasks.forEach((st) => {
206
+ console.log(` - [${st.key}] ${st.fields.summary} (${st.fields.status.name})`);
207
+ });
208
+ }
209
+ } catch (e) {
210
+ console.error(chalk2.red(`Error: ${e.message}`));
211
+ }
212
+ });
213
+ issueCmd.command("create").requiredOption("-p, --project <key>", "\uD504\uB85C\uC81D\uD2B8 \uD0A4").requiredOption("-s, --summary <summary>", "\uC774\uC288 \uC81C\uBAA9").requiredOption("-t, --type <type>", "\uC774\uC288 \uC720\uD615 (Task, Bug, Story \uB4F1)").option("-d, --description <desc>", "\uC774\uC288 \uC124\uBA85").option("-a, --assignee <name>", "\uB2F4\uB2F9\uC790").option("--priority <priority>", "\uC6B0\uC120\uC21C\uC704").option("-l, --labels <labels>", "\uB77C\uBCA8 (\uC27C\uD45C \uAD6C\uBD84)").option("--parent <key>", "\uC0C1\uC704 \uC774\uC288 \uD0A4 (Sub-task)").description("\uC774\uC288 \uC0DD\uC131").action(async (options) => {
214
+ const client = initClient();
215
+ const api = new JiraIssueApi(client);
216
+ try {
217
+ const issue = await api.createIssue({
218
+ projectKey: options.project,
219
+ summary: options.summary,
220
+ issueType: options.type,
221
+ description: options.description,
222
+ assignee: options.assignee,
223
+ priority: options.priority,
224
+ labels: options.labels?.split(",").map((l) => l.trim()),
225
+ parentKey: options.parent
226
+ });
227
+ const config = loadJiraConfig();
228
+ console.log(chalk2.green(`\uC774\uC288 \uC0DD\uC131 \uC644\uB8CC: ${issue.key}`));
229
+ console.log(`URL: ${config.baseUrl}/browse/${issue.key}`);
230
+ } catch (e) {
231
+ console.error(chalk2.red(`\uC0DD\uC131 \uC2E4\uD328: ${e.message}`));
232
+ }
233
+ });
234
+ issueCmd.command("update <issueKey>").option("-s, --summary <summary>", "\uC81C\uBAA9").option("-d, --description <desc>", "\uC124\uBA85").option("-a, --assignee <name>", "\uB2F4\uB2F9\uC790").option("--priority <priority>", "\uC6B0\uC120\uC21C\uC704").option("-l, --labels <labels>", "\uB77C\uBCA8 (\uC27C\uD45C \uAD6C\uBD84)").description("\uC774\uC288 \uC218\uC815").action(async (issueKey, options) => {
235
+ const client = initClient();
236
+ const api = new JiraIssueApi(client);
237
+ try {
238
+ await api.updateIssue(issueKey, {
239
+ summary: options.summary,
240
+ description: options.description,
241
+ assignee: options.assignee,
242
+ priority: options.priority,
243
+ labels: options.labels?.split(",").map((l) => l.trim())
244
+ });
245
+ console.log(chalk2.green(`\uC774\uC288 \uC218\uC815 \uC644\uB8CC: ${issueKey}`));
246
+ } catch (e) {
247
+ console.error(chalk2.red(`\uC218\uC815 \uC2E4\uD328: ${e.message}`));
248
+ }
249
+ });
250
+ issueCmd.command("transition <issueKey>").option("-l, --list", "\uAC00\uB2A5\uD55C \uD2B8\uB79C\uC9C0\uC158 \uC870\uD68C").option("-t, --transition <id>", "\uD2B8\uB79C\uC9C0\uC158 \uC2E4\uD589").description("\uC774\uC288 \uC0C1\uD0DC \uBCC0\uACBD").action(async (issueKey, options) => {
251
+ const client = initClient();
252
+ const api = new JiraTransitionApi(client);
253
+ try {
254
+ if (options.list || !options.transition) {
255
+ const transitions = await api.getTransitions(issueKey);
256
+ const table = new Table2({
257
+ head: ["ID", "Name", "To"],
258
+ style: { head: ["cyan"] }
259
+ });
260
+ transitions.forEach((t) => table.push([t.id, t.name, t.to.name]));
261
+ console.log(table.toString());
262
+ } else {
263
+ await api.doTransition(issueKey, options.transition);
264
+ console.log(chalk2.green(`\uD2B8\uB79C\uC9C0\uC158 \uC644\uB8CC: ${issueKey}`));
265
+ }
266
+ } catch (e) {
267
+ console.error(chalk2.red(`Error: ${e.message}`));
268
+ }
269
+ });
270
+ jiraCmd.command("search <jql>").option("-n, --max <number>", "\uCD5C\uB300 \uACB0\uACFC \uC218", "20").description("JQL \uAC80\uC0C9").action(async (jql, options) => {
271
+ const client = initClient();
272
+ const api = new JiraSearchApi(client);
273
+ try {
274
+ const result = await api.searchByJql(jql, 0, parseInt(options.max));
275
+ console.log(chalk2.bold(`\uAC80\uC0C9 \uACB0\uACFC: ${result.issues.length}\uAC74 (\uCD1D ${result.total}\uAC74)`));
276
+ const table = new Table2({
277
+ head: ["Key", "Summary", "Status", "Assignee"],
278
+ style: { head: ["cyan"] }
279
+ });
280
+ result.issues.forEach(
281
+ (i) => table.push([
282
+ i.key,
283
+ i.fields.summary.substring(0, 60),
284
+ i.fields.status?.name || "N/A",
285
+ i.fields.assignee?.displayName || "\uBBF8\uBC30\uC815"
286
+ ])
287
+ );
288
+ console.log(table.toString());
289
+ } catch (e) {
290
+ console.error(chalk2.red(`\uAC80\uC0C9 \uC2E4\uD328: ${e.message}`));
291
+ }
292
+ });
293
+ const commentCmd = jiraCmd.command("comment").description("\uCF54\uBA58\uD2B8 \uAD00\uB9AC");
294
+ commentCmd.command("list <issueKey>").description("\uCF54\uBA58\uD2B8 \uBAA9\uB85D \uC870\uD68C").action(async (issueKey) => {
295
+ const client = initClient();
296
+ const api = new JiraCommentApi(client);
297
+ try {
298
+ const result = await api.getComments(issueKey);
299
+ result.comments.forEach((c) => {
300
+ console.log(chalk2.bold(`[${c.id}] ${c.author.displayName} (${c.created})`));
301
+ console.log(c.body);
302
+ console.log(chalk2.dim("---"));
303
+ });
304
+ } catch (e) {
305
+ console.error(chalk2.red(`Error: ${e.message}`));
306
+ }
307
+ });
308
+ commentCmd.command("add <issueKey> <body>").description("\uCF54\uBA58\uD2B8 \uCD94\uAC00").action(async (issueKey, body) => {
309
+ const client = initClient();
310
+ const api = new JiraCommentApi(client);
311
+ try {
312
+ const comment = await api.addComment(issueKey, body);
313
+ console.log(chalk2.green(`\uCF54\uBA58\uD2B8 \uCD94\uAC00 \uC644\uB8CC (ID: ${comment.id})`));
314
+ } catch (e) {
315
+ console.error(chalk2.red(`Error: ${e.message}`));
316
+ }
317
+ });
318
+ const projectCmd = jiraCmd.command("project").description("\uD504\uB85C\uC81D\uD2B8 \uAD00\uB9AC");
319
+ projectCmd.command("list").description("\uD504\uB85C\uC81D\uD2B8 \uBAA9\uB85D \uC870\uD68C").action(async () => {
320
+ const client = initClient();
321
+ const api = new JiraProjectApi(client);
322
+ try {
323
+ const projects = await api.getProjects();
324
+ const table = new Table2({
325
+ head: ["Key", "Name", "Lead"],
326
+ style: { head: ["cyan"] }
327
+ });
328
+ projects.forEach(
329
+ (p) => table.push([p.key, p.name, p.lead?.displayName || "N/A"])
330
+ );
331
+ console.log(table.toString());
332
+ } catch (e) {
333
+ console.error(chalk2.red(`Error: ${e.message}`));
334
+ }
335
+ });
336
+ projectCmd.command("get <projectKey>").description("\uD504\uB85C\uC81D\uD2B8 \uC0C1\uC138 \uC870\uD68C").action(async (projectKey) => {
337
+ const client = initClient();
338
+ const api = new JiraProjectApi(client);
339
+ try {
340
+ const project = await api.getProject(projectKey);
341
+ console.log(chalk2.bold(`${project.name} (${project.key})`));
342
+ console.log(`Lead: ${project.lead?.displayName || "N/A"}`);
343
+ if (project.issueTypes) {
344
+ console.log(
345
+ `Issue Types: ${project.issueTypes.map((t) => t.name).join(", ")}`
346
+ );
347
+ }
348
+ } catch (e) {
349
+ console.error(chalk2.red(`Error: ${e.message}`));
350
+ }
351
+ });
352
+ const boardCmd = jiraCmd.command("board").description("Agile \uBCF4\uB4DC \uAD00\uB9AC");
353
+ boardCmd.command("list").option("-p, --project <key>", "\uD504\uB85C\uC81D\uD2B8 \uD0A4").description("\uBCF4\uB4DC \uBAA9\uB85D \uC870\uD68C").action(async (options) => {
354
+ const client = initClient();
355
+ const api = new JiraProjectApi(client);
356
+ try {
357
+ const result = await api.getBoards(options.project);
358
+ const table = new Table2({
359
+ head: ["ID", "Name", "Type"],
360
+ style: { head: ["cyan"] }
361
+ });
362
+ result.values.forEach((b) => table.push([b.id.toString(), b.name, b.type]));
363
+ console.log(table.toString());
364
+ } catch (e) {
365
+ console.error(chalk2.red(`Error: ${e.message}`));
366
+ }
367
+ });
368
+ boardCmd.command("sprints <boardId>").option("-s, --state <state>", "\uC0C1\uD0DC \uD544\uD130 (active, closed, future)").description("\uC2A4\uD504\uB9B0\uD2B8 \uBAA9\uB85D \uC870\uD68C").action(async (boardId, options) => {
369
+ const client = initClient();
370
+ const api = new JiraProjectApi(client);
371
+ try {
372
+ const result = await api.getSprints(parseInt(boardId), options.state);
373
+ const table = new Table2({
374
+ head: ["ID", "Name", "State", "Goal"],
375
+ style: { head: ["cyan"] }
376
+ });
377
+ result.values.forEach(
378
+ (s) => table.push([s.id.toString(), s.name, s.state, s.goal || ""])
379
+ );
380
+ console.log(table.toString());
381
+ } catch (e) {
382
+ console.error(chalk2.red(`Error: ${e.message}`));
383
+ }
384
+ });
385
+ }
386
+
387
+ // src/gitlab/commands/index.ts
388
+ import chalk3 from "chalk";
389
+ import Table3 from "cli-table3";
390
+ function registerGitlabCommands(program2) {
391
+ const gitlabCmd = program2.command("gitlab").description("GitLab \uAD00\uB9AC");
392
+ const initClient = () => {
393
+ try {
394
+ const config = loadGitlabConfig();
395
+ return createGitlabClient(config);
396
+ } catch (e) {
397
+ console.error(chalk3.red(`\uC124\uC815 \uB85C\uB4DC \uC2E4\uD328: ${e.message}`));
398
+ process.exit(1);
399
+ }
400
+ };
401
+ const projectCmd = gitlabCmd.command("project").description("\uD504\uB85C\uC81D\uD2B8 \uAD00\uB9AC");
402
+ projectCmd.command("list").option("-s, --search <query>", "\uD504\uB85C\uC81D\uD2B8\uBA85 \uAC80\uC0C9").option("--owned", "\uC18C\uC720 \uD504\uB85C\uC81D\uD2B8\uB9CC").option("--membership", "\uBA64\uBC84\uC2ED \uD504\uB85C\uC81D\uD2B8\uB9CC").description("\uD504\uB85C\uC81D\uD2B8 \uBAA9\uB85D \uC870\uD68C").action(async (options) => {
403
+ const client = initClient();
404
+ const api = new GitlabProjectApi(client);
405
+ try {
406
+ const projects = await api.getProjects({
407
+ search: options.search,
408
+ owned: options.owned,
409
+ membership: options.membership
410
+ });
411
+ const table = new Table3({
412
+ head: ["ID", "Name", "Visibility", "Default Branch", "Last Activity"],
413
+ style: { head: ["cyan"] }
414
+ });
415
+ projects.forEach(
416
+ (p) => table.push([
417
+ p.id.toString(),
418
+ p.name_with_namespace,
419
+ p.visibility,
420
+ p.default_branch || "N/A",
421
+ p.last_activity_at?.substring(0, 10) || "N/A"
422
+ ])
423
+ );
424
+ console.log(table.toString());
425
+ } catch (e) {
426
+ console.error(chalk3.red(`Error: ${e.message}`));
427
+ }
428
+ });
429
+ projectCmd.command("get <projectId>").description("\uD504\uB85C\uC81D\uD2B8 \uC0C1\uC138 \uC870\uD68C").action(async (projectId) => {
430
+ const client = initClient();
431
+ const api = new GitlabProjectApi(client);
432
+ try {
433
+ const p = await api.getProject(parseInt(projectId));
434
+ console.log(chalk3.bold(p.name_with_namespace));
435
+ console.log(`ID: ${p.id}`);
436
+ console.log(`Path: ${p.path_with_namespace}`);
437
+ console.log(`Default Branch: ${p.default_branch}`);
438
+ console.log(`Visibility: ${p.visibility}`);
439
+ console.log(`Web URL: ${p.web_url}`);
440
+ console.log(`SSH URL: ${p.ssh_url_to_repo}`);
441
+ console.log(`HTTP URL: ${p.http_url_to_repo}`);
442
+ console.log(`Last Activity: ${p.last_activity_at}`);
443
+ if (p.description) console.log(`Description: ${p.description}`);
444
+ } catch (e) {
445
+ console.error(chalk3.red(`Error: ${e.message}`));
446
+ }
447
+ });
448
+ const mrCmd = gitlabCmd.command("mr").description("Merge Request \uAD00\uB9AC");
449
+ mrCmd.command("list <projectId>").option("-s, --state <state>", "\uC0C1\uD0DC \uD544\uD130 (opened/closed/merged/all)", "opened").option("--scope <scope>", "\uBC94\uC704 (created_by_me/assigned_to_me)").description("MR \uBAA9\uB85D \uC870\uD68C").action(async (projectId, options) => {
450
+ const client = initClient();
451
+ const api = new GitlabMergeRequestApi(client);
452
+ try {
453
+ const mrs = await api.getMergeRequests(parseInt(projectId), {
454
+ state: options.state,
455
+ scope: options.scope
456
+ });
457
+ const table = new Table3({
458
+ head: ["IID", "Title", "State", "Source \u2192 Target", "Author"],
459
+ style: { head: ["cyan"] }
460
+ });
461
+ mrs.forEach(
462
+ (m) => table.push([
463
+ `!${m.iid}`,
464
+ m.title.substring(0, 50),
465
+ m.state,
466
+ `${m.source_branch} \u2192 ${m.target_branch}`,
467
+ m.author?.name || "N/A"
468
+ ])
469
+ );
470
+ console.log(table.toString());
471
+ } catch (e) {
472
+ console.error(chalk3.red(`Error: ${e.message}`));
473
+ }
474
+ });
475
+ mrCmd.command("get <projectId> <mrIid>").option("-c, --changes", "\uBCC0\uACBD \uD30C\uC77C \uD3EC\uD568").description("MR \uC0C1\uC138 \uC870\uD68C").action(async (projectId, mrIid, options) => {
476
+ const client = initClient();
477
+ const api = new GitlabMergeRequestApi(client);
478
+ try {
479
+ const mr = options.changes ? await api.getMergeRequestChanges(parseInt(projectId), parseInt(mrIid)) : await api.getMergeRequest(parseInt(projectId), parseInt(mrIid));
480
+ console.log(chalk3.bold(`[!${mr.iid}] ${mr.title}`));
481
+ console.log(`State: ${mr.state}`);
482
+ console.log(`Source: ${mr.source_branch} \u2192 Target: ${mr.target_branch}`);
483
+ console.log(`Author: ${mr.author?.name || "N/A"}`);
484
+ console.log(`Assignee: ${mr.assignee?.name || "\uBBF8\uBC30\uC815"}`);
485
+ console.log(`Merge Status: ${mr.merge_status}`);
486
+ console.log(`Has Conflicts: ${mr.has_conflicts}`);
487
+ console.log(`Pipeline: ${mr.pipeline?.status || "N/A"}`);
488
+ console.log(`URL: ${mr.web_url}`);
489
+ if (mr.description) {
490
+ console.log(chalk3.dim("\n--- Description ---"));
491
+ console.log(mr.description);
492
+ }
493
+ if (mr.changes && mr.changes.length > 0) {
494
+ console.log(chalk3.dim(`
495
+ --- Changes (${mr.changes.length}\uAC1C \uD30C\uC77C) ---`));
496
+ mr.changes.forEach((c) => {
497
+ const prefix = c.new_file ? "[NEW]" : c.deleted_file ? "[DEL]" : c.renamed_file ? "[REN]" : "[MOD]";
498
+ console.log(` ${prefix} ${c.new_path}`);
499
+ });
500
+ }
501
+ } catch (e) {
502
+ console.error(chalk3.red(`Error: ${e.message}`));
503
+ }
504
+ });
505
+ mrCmd.command("create <projectId>").requiredOption("--source <branch>", "\uC18C\uC2A4 \uBE0C\uB79C\uCE58").requiredOption("--target <branch>", "\uD0C0\uAC9F \uBE0C\uB79C\uCE58").requiredOption("--title <text>", "MR \uC81C\uBAA9").option("-d, --description <text>", "MR \uC124\uBA85").description("MR \uC0DD\uC131").action(async (projectId, options) => {
506
+ const client = initClient();
507
+ const api = new GitlabMergeRequestApi(client);
508
+ try {
509
+ const mr = await api.createMergeRequest(parseInt(projectId), {
510
+ source_branch: options.source,
511
+ target_branch: options.target,
512
+ title: options.title,
513
+ description: options.description
514
+ });
515
+ console.log(chalk3.green(`MR \uC0DD\uC131 \uC644\uB8CC: !${mr.iid}`));
516
+ console.log(`URL: ${mr.web_url}`);
517
+ } catch (e) {
518
+ console.error(chalk3.red(`\uC0DD\uC131 \uC2E4\uD328: ${e.message}`));
519
+ }
520
+ });
521
+ mrCmd.command("merge <projectId> <mrIid>").option("--squash", "\uC2A4\uCFFC\uC2DC \uBA38\uC9C0").option("--remove-source-branch", "\uC18C\uC2A4 \uBE0C\uB79C\uCE58 \uC0AD\uC81C").description("MR \uBA38\uC9C0").action(async (projectId, mrIid, options) => {
522
+ const client = initClient();
523
+ const api = new GitlabMergeRequestApi(client);
524
+ try {
525
+ const mr = await api.mergeMergeRequest(parseInt(projectId), parseInt(mrIid), {
526
+ squash: options.squash,
527
+ should_remove_source_branch: options.removeSourceBranch
528
+ });
529
+ console.log(chalk3.green(`MR !${mrIid} \uBA38\uC9C0 \uC644\uB8CC`));
530
+ console.log(`State: ${mr.state}`);
531
+ } catch (e) {
532
+ console.error(chalk3.red(`\uBA38\uC9C0 \uC2E4\uD328: ${e.message}`));
533
+ }
534
+ });
535
+ mrCmd.command("close <projectId> <mrIid>").description("MR \uB2EB\uAE30").action(async (projectId, mrIid) => {
536
+ const client = initClient();
537
+ const api = new GitlabMergeRequestApi(client);
538
+ try {
539
+ await api.updateMergeRequest(parseInt(projectId), parseInt(mrIid), {
540
+ state_event: "close"
541
+ });
542
+ console.log(chalk3.green(`MR !${mrIid} \uB2EB\uAE30 \uC644\uB8CC`));
543
+ } catch (e) {
544
+ console.error(chalk3.red(`Error: ${e.message}`));
545
+ }
546
+ });
547
+ mrCmd.command("comment <projectId> <mrIid>").requiredOption("-b, --body <text>", "\uCF54\uBA58\uD2B8 \uB0B4\uC6A9").description("MR \uCF54\uBA58\uD2B8 \uCD94\uAC00").action(async (projectId, mrIid, options) => {
548
+ const client = initClient();
549
+ const api = new GitlabMergeRequestApi(client);
550
+ try {
551
+ const note = await api.addMergeRequestNote(
552
+ parseInt(projectId),
553
+ parseInt(mrIid),
554
+ options.body
555
+ );
556
+ console.log(chalk3.green(`\uCF54\uBA58\uD2B8 \uCD94\uAC00 \uC644\uB8CC (ID: ${note.id})`));
557
+ } catch (e) {
558
+ console.error(chalk3.red(`Error: ${e.message}`));
559
+ }
560
+ });
561
+ const pipelineCmd = gitlabCmd.command("pipeline").description("\uD30C\uC774\uD504\uB77C\uC778 \uAD00\uB9AC");
562
+ pipelineCmd.command("list <projectId>").option("-s, --status <status>", "\uC0C1\uD0DC \uD544\uD130").option("-r, --ref <branch>", "\uBE0C\uB79C\uCE58/\uD0DC\uADF8 \uD544\uD130").description("\uD30C\uC774\uD504\uB77C\uC778 \uBAA9\uB85D \uC870\uD68C").action(async (projectId, options) => {
563
+ const client = initClient();
564
+ const api = new GitlabPipelineApi(client);
565
+ try {
566
+ const pipelines = await api.getPipelines(parseInt(projectId), {
567
+ status: options.status,
568
+ ref: options.ref
569
+ });
570
+ const table = new Table3({
571
+ head: ["ID", "Status", "Ref", "SHA", "Created"],
572
+ style: { head: ["cyan"] }
573
+ });
574
+ pipelines.forEach(
575
+ (p) => table.push([
576
+ p.id.toString(),
577
+ p.status,
578
+ p.ref,
579
+ p.sha.substring(0, 8),
580
+ p.created_at?.substring(0, 10) || "N/A"
581
+ ])
582
+ );
583
+ console.log(table.toString());
584
+ } catch (e) {
585
+ console.error(chalk3.red(`Error: ${e.message}`));
586
+ }
587
+ });
588
+ pipelineCmd.command("get <projectId> <pipelineId>").option("-j, --jobs", "\uC791\uC5C5 \uBAA9\uB85D \uD3EC\uD568").description("\uD30C\uC774\uD504\uB77C\uC778 \uC0C1\uC138 \uC870\uD68C").action(async (projectId, pipelineId, options) => {
589
+ const client = initClient();
590
+ const api = new GitlabPipelineApi(client);
591
+ try {
592
+ const p = await api.getPipeline(parseInt(projectId), parseInt(pipelineId));
593
+ console.log(chalk3.bold(`Pipeline #${p.id}`));
594
+ console.log(`Status: ${p.status}`);
595
+ console.log(`Ref: ${p.ref}`);
596
+ console.log(`SHA: ${p.sha}`);
597
+ console.log(`Duration: ${p.duration ? p.duration + "s" : "N/A"}`);
598
+ console.log(`URL: ${p.web_url}`);
599
+ if (options.jobs) {
600
+ const jobs = await api.getPipelineJobs(
601
+ parseInt(projectId),
602
+ parseInt(pipelineId)
603
+ );
604
+ console.log(chalk3.dim(`
605
+ --- Jobs (${jobs.length}\uAC1C) ---`));
606
+ const table = new Table3({
607
+ head: ["Stage", "Name", "Status", "Duration"],
608
+ style: { head: ["cyan"] }
609
+ });
610
+ jobs.forEach(
611
+ (j) => table.push([
612
+ j.stage,
613
+ j.name,
614
+ j.status,
615
+ j.duration ? j.duration + "s" : "N/A"
616
+ ])
617
+ );
618
+ console.log(table.toString());
619
+ }
620
+ } catch (e) {
621
+ console.error(chalk3.red(`Error: ${e.message}`));
622
+ }
623
+ });
624
+ const branchCmd = gitlabCmd.command("branch").description("\uBE0C\uB79C\uCE58 \uAD00\uB9AC");
625
+ branchCmd.command("list <projectId>").option("-s, --search <query>", "\uAC80\uC0C9 \uD0A4\uC6CC\uB4DC").description("\uBE0C\uB79C\uCE58 \uBAA9\uB85D \uC870\uD68C").action(async (projectId, options) => {
626
+ const client = initClient();
627
+ const api = new GitlabBranchApi(client);
628
+ try {
629
+ const branches = await api.getBranches(parseInt(projectId), {
630
+ search: options.search
631
+ });
632
+ const table = new Table3({
633
+ head: ["Name", "Commit", "Message", "Protected", "Default"],
634
+ style: { head: ["cyan"] }
635
+ });
636
+ branches.forEach(
637
+ (b) => table.push([
638
+ b.name,
639
+ b.commit.short_id,
640
+ b.commit.message.split("\n")[0].substring(0, 40),
641
+ b.protected ? "Yes" : "No",
642
+ b.default ? "Yes" : "No"
643
+ ])
644
+ );
645
+ console.log(table.toString());
646
+ } catch (e) {
647
+ console.error(chalk3.red(`Error: ${e.message}`));
648
+ }
649
+ });
650
+ branchCmd.command("get <projectId> <branchName>").description("\uBE0C\uB79C\uCE58 \uC0C1\uC138 \uC870\uD68C").action(async (projectId, branchName) => {
651
+ const client = initClient();
652
+ const api = new GitlabBranchApi(client);
653
+ try {
654
+ const b = await api.getBranch(parseInt(projectId), branchName);
655
+ console.log(chalk3.bold(b.name));
656
+ console.log(`Default: ${b.default}`);
657
+ console.log(`Protected: ${b.protected}`);
658
+ console.log(`Merged: ${b.merged}`);
659
+ console.log(`Commit: ${b.commit.id}`);
660
+ console.log(`Message: ${b.commit.message}`);
661
+ console.log(`Author: ${b.commit.author_name}`);
662
+ console.log(`Date: ${b.commit.authored_date}`);
663
+ } catch (e) {
664
+ console.error(chalk3.red(`Error: ${e.message}`));
665
+ }
666
+ });
667
+ branchCmd.command("create <projectId>").requiredOption("-n, --name <branch>", "\uBE0C\uB79C\uCE58 \uC774\uB984").requiredOption("-r, --ref <ref>", "\uAE30\uC900 ref").description("\uBE0C\uB79C\uCE58 \uC0DD\uC131").action(async (projectId, options) => {
668
+ const client = initClient();
669
+ const api = new GitlabBranchApi(client);
670
+ try {
671
+ const b = await api.createBranch(
672
+ parseInt(projectId),
673
+ options.name,
674
+ options.ref
675
+ );
676
+ console.log(chalk3.green(`\uBE0C\uB79C\uCE58 \uC0DD\uC131 \uC644\uB8CC: ${b.name}`));
677
+ } catch (e) {
678
+ console.error(chalk3.red(`\uC0DD\uC131 \uC2E4\uD328: ${e.message}`));
679
+ }
680
+ });
681
+ branchCmd.command("delete <projectId> <branchName>").description("\uBE0C\uB79C\uCE58 \uC0AD\uC81C").action(async (projectId, branchName) => {
682
+ const client = initClient();
683
+ const api = new GitlabBranchApi(client);
684
+ try {
685
+ await api.deleteBranch(parseInt(projectId), branchName);
686
+ console.log(chalk3.green(`\uBE0C\uB79C\uCE58 \uC0AD\uC81C \uC644\uB8CC: ${branchName}`));
687
+ } catch (e) {
688
+ console.error(chalk3.red(`\uC0AD\uC81C \uC2E4\uD328: ${e.message}`));
689
+ }
690
+ });
691
+ const fileCmd = gitlabCmd.command("file").description("\uD30C\uC77C \uAD00\uB9AC");
692
+ fileCmd.command("get <projectId> <filePath>").option("-r, --ref <ref>", "\uBE0C\uB79C\uCE58/\uD0DC\uADF8/\uCEE4\uBC0B").description("\uD30C\uC77C \uB0B4\uC6A9 \uC870\uD68C").action(async (projectId, filePath, options) => {
693
+ const client = initClient();
694
+ const api = new GitlabRepositoryApi(client);
695
+ try {
696
+ const file = await api.getFile(parseInt(projectId), filePath, options.ref);
697
+ console.log(chalk3.bold(file.file_path));
698
+ console.log(chalk3.gray(`Size: ${file.size} bytes | Ref: ${file.ref}`));
699
+ console.log(chalk3.dim("---"));
700
+ console.log(file.content);
701
+ } catch (e) {
702
+ console.error(chalk3.red(`Error: ${e.message}`));
703
+ }
704
+ });
705
+ fileCmd.command("tree <projectId>").option("-p, --path <dir>", "\uB514\uB809\uD1A0\uB9AC \uACBD\uB85C").option("-r, --ref <ref>", "\uBE0C\uB79C\uCE58/\uD0DC\uADF8").option("--recursive", "\uC7AC\uADC0 \uC870\uD68C").description("\uB514\uB809\uD1A0\uB9AC \uD2B8\uB9AC \uC870\uD68C").action(async (projectId, options) => {
706
+ const client = initClient();
707
+ const api = new GitlabRepositoryApi(client);
708
+ try {
709
+ const entries = await api.getTree(parseInt(projectId), {
710
+ path: options.path,
711
+ ref: options.ref,
712
+ recursive: options.recursive
713
+ });
714
+ entries.forEach((e) => {
715
+ const icon = e.type === "tree" ? "\u{1F4C1}" : "\u{1F4C4}";
716
+ console.log(`${icon} ${e.path}`);
717
+ });
718
+ } catch (e) {
719
+ console.error(chalk3.red(`Error: ${e.message}`));
720
+ }
721
+ });
722
+ }
723
+
155
724
  // src/cli.ts
156
725
  import dotenv from "dotenv";
157
726
  dotenv.config();
158
727
  var program = new Command();
159
728
  program.name("tdecollab").description("TDE Collaboration CLI").version("0.1.0");
160
729
  registerConfluenceCommands(program);
730
+ registerJiraCommands(program);
731
+ registerGitlabCommands(program);
161
732
  program.command("mcp").description("Run MCP Server").action(async () => {
162
- const { runServer } = await import("./server-VBNZQFGP.js");
733
+ const { runServer } = await import("./server-HS774DWY.js");
163
734
  await runServer();
164
735
  });
165
736
  program.parse(process.argv);