@devchangjun/ctm 0.1.0 → 0.1.6

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 (3) hide show
  1. package/README.md +3 -3
  2. package/dist/index.js +88 -39
  3. package/package.json +8 -2
package/README.md CHANGED
@@ -138,9 +138,9 @@ ctm clean # 현재 브런치 삭제
138
138
  ctm clean CGKR-1423 # 해당 이슈 브런치 삭제
139
139
  ```
140
140
 
141
- 로컈 브런치를 삭제하고, 원격 브런치도 함께 삭제할지 물어뼅니다.
142
- 현재 브런치를 삭제할 경우 base branch로 자동 전환됩니다.
143
- worktree가 연결되어 있으면 함께 제거할지 자동으로 물어뼅니다.
141
+ 로컬 브랜치를 삭제하고, 원격 브랜치도 함께 삭제할지 물어봅니다.
142
+ 현재 브랜치를 삭제할 경우 base branch로 자동 전환됩니다.
143
+ worktree가 연결되어 있으면 함께 제거할지 자동으로 물어봅니다.
144
144
 
145
145
  ---
146
146
 
package/dist/index.js CHANGED
@@ -876,8 +876,56 @@ async function cleanCommand(keyArg) {
876
876
  console.log();
877
877
  }
878
878
 
879
- // src/commands/help.ts
879
+ // src/commands/checkout.ts
880
880
  import chalk8 from "chalk";
881
+ import { select as select3, confirm as confirm4 } from "@inquirer/prompts";
882
+ async function checkoutCommand(keyArg) {
883
+ if (!keyArg) {
884
+ console.error(chalk8.red("Usage: ctm co <ticket-key>"));
885
+ process.exit(1);
886
+ }
887
+ const config = ensureConfig();
888
+ const issueKey = resolveIssueKey(keyArg, config.jiraProjectKey);
889
+ const allBranches = await getLocalBranches();
890
+ const matching = allBranches.filter((b) => b.toUpperCase().includes(issueKey));
891
+ if (matching.length === 0) {
892
+ console.error(chalk8.red(`No local branch found for ${issueKey}`));
893
+ process.exit(1);
894
+ }
895
+ let target;
896
+ if (matching.length === 1) {
897
+ target = matching[0];
898
+ } else {
899
+ target = await select3({
900
+ message: `Multiple branches found for ${chalk8.cyan(issueKey)}:`,
901
+ choices: matching.map((b) => ({ value: b, name: b }))
902
+ });
903
+ }
904
+ const currentBranch = await getCurrentBranch();
905
+ if (currentBranch === target) {
906
+ printWarn(`Already on ${chalk8.cyan(target)}`);
907
+ return;
908
+ }
909
+ if (await hasUncommittedChanges()) {
910
+ printWarn(`Uncommitted changes on "${currentBranch}".`);
911
+ const shouldStash = await confirm4({
912
+ message: "Stash them before switching?",
913
+ default: true
914
+ });
915
+ if (shouldStash) {
916
+ await stashChanges(`ctm: stash before ${target}`);
917
+ printSuccess("Changes stashed.");
918
+ } else {
919
+ printWarn("Checkout aborted to preserve uncommitted changes.");
920
+ return;
921
+ }
922
+ }
923
+ await checkoutBranch(target);
924
+ printSuccess(`Switched to ${chalk8.cyan(target)}`);
925
+ }
926
+
927
+ // src/commands/help.ts
928
+ import chalk9 from "chalk";
881
929
  var COMMANDS = [
882
930
  {
883
931
  name: "init",
@@ -938,7 +986,7 @@ var COMMANDS = [
938
986
  {
939
987
  name: "clean",
940
988
  usage: "ctm clean [key]",
941
- description: "Jira \uC774\uC288\uC5D0 \uC5F0\uACB0\uB41C \uB85C\uCEC8 \uBE0C\uB7F0\uCE58\uB97C \uC0AD\uC81C\uD569\uB2C8\uB2E4.\n \uC6D0\uACA9 \uBE0C\uB7F0\uCE58\uB3C4 \uD568\uAED8 \uC0AD\uC81C\uD560\uC9C0 \uBB3B\uC5B4\uBD05\uB2C8\uB2E4.\n worktree\uAC00 \uC788\uC73C\uBA74 \uD568\uAED8 \uC815\uB9AC\uD560\uC9C0 \uBB3B\uC5B4\uBF45\uB2C8\uB2E4.",
989
+ description: "Jira \uC774\uC288\uC5D0 \uC5F0\uACB0\uB41C \uB85C\uCEEC \uBE0C\uB79C\uCE58\uB97C \uC0AD\uC81C\uD569\uB2C8\uB2E4.\n \uC6D0\uACA9 \uBE0C\uB79C\uCE58\uB3C4 \uD568\uAED8 \uC0AD\uC81C\uD560\uC9C0 \uBB3C\uC5B4\uBD05\uB2C8\uB2E4.\n worktree\uAC00 \uC788\uC73C\uBA74 \uD568\uAED8 \uC815\uB9AC\uD560\uC9C0 \uBB3C\uC5B4\uBD05\uB2C8\uB2E4.",
942
990
  examples: [
943
991
  { cmd: "ctm clean", comment: "\uD604\uC7AC \uBE0C\uB7F0\uCE58 \uC0AD\uC81C" },
944
992
  { cmd: "ctm clean CGKR-1423", comment: "\uD2B9\uC815 \uC774\uC288 \uBE0C\uB7F0\uCE58 \uC0AD\uC81C" },
@@ -961,50 +1009,50 @@ var COMMANDS = [
961
1009
  ]
962
1010
  }
963
1011
  ];
964
- var DIM_RULE = chalk8.dim("\u2500".repeat(56));
1012
+ var DIM_RULE = chalk9.dim("\u2500".repeat(56));
965
1013
  function renderHeader() {
966
1014
  console.log();
967
- console.log(` ${chalk8.bold.cyan("ctm")} ${chalk8.dim("\u2014 Colo Ticket Manager")}`);
968
- console.log(` ${chalk8.dim("Jira \uD2F0\uCF13 \uAE30\uBC18 Git \uBE0C\uB79C\uCE58 \uAD00\uB9AC CLI")}`);
1015
+ console.log(` ${chalk9.bold.cyan("ctm")} ${chalk9.dim("\u2014 Colo Ticket Manager")}`);
1016
+ console.log(` ${chalk9.dim("Jira \uD2F0\uCF13 \uAE30\uBC18 Git \uBE0C\uB79C\uCE58 \uAD00\uB9AC CLI")}`);
969
1017
  console.log();
970
1018
  }
971
1019
  function renderUsageLine() {
972
- console.log(` ${chalk8.bold("\uC0AC\uC6A9\uBC95")} ${chalk8.cyan("ctm")} ${chalk8.dim("<command>")} ${chalk8.dim("[options]")}`);
1020
+ console.log(` ${chalk9.bold("\uC0AC\uC6A9\uBC95")} ${chalk9.cyan("ctm")} ${chalk9.dim("<command>")} ${chalk9.dim("[options]")}`);
973
1021
  console.log();
974
1022
  }
975
1023
  function renderCommandRow(cmd) {
976
- const aliases = cmd.aliases?.length ? chalk8.dim(` (${cmd.aliases.join(", ")})`) : "";
977
- const name = chalk8.cyan(cmd.name.padEnd(10)) + aliases;
1024
+ const aliases = cmd.aliases?.length ? chalk9.dim(` (${cmd.aliases.join(", ")})`) : "";
1025
+ const name = chalk9.cyan(cmd.name.padEnd(10)) + aliases;
978
1026
  const shortDesc = cmd.description.split("\n")[0];
979
1027
  console.log(` ${name} ${shortDesc}`);
980
1028
  }
981
1029
  function renderCommandDetail(cmd) {
982
- const aliases = cmd.aliases?.length ? " " + chalk8.dim(`alias: ${cmd.aliases.join(", ")}`) : "";
1030
+ const aliases = cmd.aliases?.length ? " " + chalk9.dim(`alias: ${cmd.aliases.join(", ")}`) : "";
983
1031
  console.log();
984
1032
  console.log(DIM_RULE);
985
- console.log(` ${chalk8.bold.cyan(cmd.name)}${aliases}`);
1033
+ console.log(` ${chalk9.bold.cyan(cmd.name)}${aliases}`);
986
1034
  console.log();
987
- console.log(` ${chalk8.bold("\uC0AC\uC6A9\uBC95")} ${chalk8.cyan(cmd.usage)}`);
1035
+ console.log(` ${chalk9.bold("\uC0AC\uC6A9\uBC95")} ${chalk9.cyan(cmd.usage)}`);
988
1036
  console.log();
989
1037
  for (const line of cmd.description.split("\n")) {
990
1038
  console.log(` ${line}`);
991
1039
  }
992
1040
  console.log();
993
1041
  if (cmd.options?.length) {
994
- console.log(` ${chalk8.bold("\uC635\uC158")}`);
1042
+ console.log(` ${chalk9.bold("\uC635\uC158")}`);
995
1043
  for (const opt of cmd.options) {
996
- console.log(` ${chalk8.green(opt.flag.padEnd(26))} ${opt.desc}`);
1044
+ console.log(` ${chalk9.green(opt.flag.padEnd(26))} ${opt.desc}`);
997
1045
  }
998
1046
  console.log();
999
1047
  }
1000
- console.log(` ${chalk8.bold("\uC608\uC2DC")}`);
1048
+ console.log(` ${chalk9.bold("\uC608\uC2DC")}`);
1001
1049
  for (const ex of cmd.examples) {
1002
- console.log(` ${chalk8.cyan(ex.cmd.padEnd(38))} ${chalk8.dim(ex.comment)}`);
1050
+ console.log(` ${chalk9.cyan(ex.cmd.padEnd(38))} ${chalk9.dim(ex.comment)}`);
1003
1051
  }
1004
1052
  }
1005
1053
  function renderWorkflow() {
1006
1054
  console.log();
1007
- console.log(` ${chalk8.bold("\uC77C\uBC18\uC801\uC778 \uC6CC\uD06C\uD50C\uB85C\uC6B0")}`);
1055
+ console.log(` ${chalk9.bold("\uC77C\uBC18\uC801\uC778 \uC6CC\uD06C\uD50C\uB85C\uC6B0")}`);
1008
1056
  console.log();
1009
1057
  const steps = [
1010
1058
  ["ctm init", "\uCD5C\uCD08 1\uD68C \u2014 Jira \uC5F0\uACB0 \uC124\uC815"],
@@ -1015,7 +1063,7 @@ function renderWorkflow() {
1015
1063
  ["ctm clean", "\uBA38\uC9C0 \uD6C4 \uBE0C\uB79C\uCE58 \uC815\uB9AC"]
1016
1064
  ];
1017
1065
  for (const [cmd, comment] of steps) {
1018
- console.log(` ${chalk8.cyan(cmd.padEnd(24))} ${chalk8.dim(comment)}`);
1066
+ console.log(` ${chalk9.cyan(cmd.padEnd(24))} ${chalk9.dim(comment)}`);
1019
1067
  }
1020
1068
  console.log();
1021
1069
  }
@@ -1026,10 +1074,10 @@ function helpCommand(commandName) {
1026
1074
  );
1027
1075
  if (!target) {
1028
1076
  console.error(
1029
- `${chalk8.red("\u2717")} \uC54C \uC218 \uC5C6\uB294 \uBA85\uB839\uC5B4: ${chalk8.bold(commandName)}
1077
+ `${chalk9.red("\u2717")} \uC54C \uC218 \uC5C6\uB294 \uBA85\uB839\uC5B4: ${chalk9.bold(commandName)}
1030
1078
  `
1031
1079
  );
1032
- console.log(` \uC0AC\uC6A9 \uAC00\uB2A5\uD55C \uBA85\uB839\uC5B4: ${COMMANDS.map((c) => chalk8.cyan(c.name)).join(", ")}`);
1080
+ console.log(` \uC0AC\uC6A9 \uAC00\uB2A5\uD55C \uBA85\uB839\uC5B4: ${COMMANDS.map((c) => chalk9.cyan(c.name)).join(", ")}`);
1033
1081
  console.log();
1034
1082
  process.exit(1);
1035
1083
  }
@@ -1042,21 +1090,21 @@ function helpCommand(commandName) {
1042
1090
  }
1043
1091
  renderHeader();
1044
1092
  renderUsageLine();
1045
- console.log(` ${chalk8.bold("\uBA85\uB839\uC5B4")}`);
1093
+ console.log(` ${chalk9.bold("\uBA85\uB839\uC5B4")}`);
1046
1094
  console.log();
1047
1095
  for (const cmd of COMMANDS) {
1048
1096
  renderCommandRow(cmd);
1049
1097
  }
1050
1098
  renderWorkflow();
1051
1099
  console.log(
1052
- ` ${chalk8.dim(`\uC790\uC138\uD55C \uB3C4\uC6C0\uB9D0: ${chalk8.cyan("ctm help <command>")}`)}`
1100
+ ` ${chalk9.dim(`\uC790\uC138\uD55C \uB3C4\uC6C0\uB9D0: ${chalk9.cyan("ctm help <command>")}`)}`
1053
1101
  );
1054
1102
  console.log();
1055
1103
  }
1056
1104
 
1057
1105
  // src/commands/worktree.ts
1058
- import chalk9 from "chalk";
1059
- import { confirm as confirm4 } from "@inquirer/prompts";
1106
+ import chalk10 from "chalk";
1107
+ import { confirm as confirm5 } from "@inquirer/prompts";
1060
1108
  import ora7 from "ora";
1061
1109
  async function worktreeListCommand() {
1062
1110
  let trees;
@@ -1068,23 +1116,23 @@ async function worktreeListCommand() {
1068
1116
  }
1069
1117
  console.log();
1070
1118
  if (trees.length === 0) {
1071
- console.log(chalk9.dim(" No worktrees found.\n"));
1119
+ console.log(chalk10.dim(" No worktrees found.\n"));
1072
1120
  return;
1073
1121
  }
1074
1122
  console.log(
1075
1123
  ` ${"PATH".padEnd(52)} ${"BRANCH".padEnd(30)} HEAD`
1076
1124
  );
1077
- console.log(chalk9.dim(` ${"\u2500".repeat(52)} ${"\u2500".repeat(30)} ${"\u2500".repeat(7)}`));
1125
+ console.log(chalk10.dim(` ${"\u2500".repeat(52)} ${"\u2500".repeat(30)} ${"\u2500".repeat(7)}`));
1078
1126
  for (const t of trees) {
1079
- const pathLabel = t.isMain ? `${t.path} ${chalk9.dim("(main)")}` : t.path;
1127
+ const pathLabel = t.isMain ? `${t.path} ${chalk10.dim("(main)")}` : t.path;
1080
1128
  const branchDisplay = t.branch.replace("refs/heads/", "");
1081
1129
  const shortHead = t.head.slice(0, 7);
1082
- const pathCol = chalk9.cyan(pathLabel.length > 50 ? "\u2026" + pathLabel.slice(-49) : pathLabel);
1083
- const branchCol = t.isMain ? chalk9.dim(branchDisplay) : chalk9.white(branchDisplay);
1084
- console.log(` ${pathCol.padEnd(52)} ${branchCol.padEnd(30)} ${chalk9.dim(shortHead)}`);
1130
+ const pathCol = chalk10.cyan(pathLabel.length > 50 ? "\u2026" + pathLabel.slice(-49) : pathLabel);
1131
+ const branchCol = t.isMain ? chalk10.dim(branchDisplay) : chalk10.white(branchDisplay);
1132
+ console.log(` ${pathCol.padEnd(52)} ${branchCol.padEnd(30)} ${chalk10.dim(shortHead)}`);
1085
1133
  }
1086
1134
  console.log();
1087
- console.log(chalk9.dim(` ${trees.length} worktree(s) \xB7 ctm wt rm [key] to remove`));
1135
+ console.log(chalk10.dim(` ${trees.length} worktree(s) \xB7 ctm wt rm [key] to remove`));
1088
1136
  console.log();
1089
1137
  }
1090
1138
  async function worktreeRemoveCommand(keyArg, options) {
@@ -1101,7 +1149,7 @@ async function worktreeRemoveCommand(keyArg, options) {
1101
1149
  branchName = match;
1102
1150
  } else {
1103
1151
  printError("Please specify an issue key: ctm wt rm <key>");
1104
- console.log(chalk9.dim(" Example: ctm wt rm CGKR-1423 or ctm wt rm 1423"));
1152
+ console.log(chalk10.dim(" Example: ctm wt rm CGKR-1423 or ctm wt rm 1423"));
1105
1153
  process.exit(1);
1106
1154
  }
1107
1155
  const tree = await findWorktreeForBranch(branchName);
@@ -1113,7 +1161,7 @@ async function worktreeRemoveCommand(keyArg, options) {
1113
1161
  );
1114
1162
  if (!fuzzy) {
1115
1163
  printWarn(`No worktree found for branch "${branchName}".`);
1116
- console.log(chalk9.dim(" Use ctm wt to see existing worktrees."));
1164
+ console.log(chalk10.dim(" Use ctm wt to see existing worktrees."));
1117
1165
  process.exit(0);
1118
1166
  }
1119
1167
  return doRemove(fuzzy, branchName, options);
@@ -1123,10 +1171,10 @@ async function worktreeRemoveCommand(keyArg, options) {
1123
1171
  async function doRemove(tree, branchName, options) {
1124
1172
  const branchDisplay = tree.branch.replace("refs/heads/", "");
1125
1173
  console.log();
1126
- console.log(` ${chalk9.bold("Worktree:")} ${chalk9.cyan(tree.path)}`);
1127
- console.log(` ${chalk9.bold("Branch:")} ${chalk9.cyan(branchDisplay)}`);
1174
+ console.log(` ${chalk10.bold("Worktree:")} ${chalk10.cyan(tree.path)}`);
1175
+ console.log(` ${chalk10.bold("Branch:")} ${chalk10.cyan(branchDisplay)}`);
1128
1176
  console.log();
1129
- const confirmed = await confirm4({
1177
+ const confirmed = await confirm5({
1130
1178
  message: `Remove worktree at "${tree.path}"?`,
1131
1179
  default: false
1132
1180
  });
@@ -1134,13 +1182,13 @@ async function doRemove(tree, branchName, options) {
1134
1182
  const spinner = ora7("Removing worktree\u2026").start();
1135
1183
  try {
1136
1184
  await removeWorktree(tree.path, options.force);
1137
- spinner.succeed(`Worktree removed: ${chalk9.cyan(tree.path)}`);
1185
+ spinner.succeed(`Worktree removed: ${chalk10.cyan(tree.path)}`);
1138
1186
  } catch (err) {
1139
1187
  spinner.fail(`Failed to remove worktree: ${String(err)}`);
1140
- console.log(chalk9.dim(" Try: ctm wt rm <key> --force"));
1188
+ console.log(chalk10.dim(" Try: ctm wt rm <key> --force"));
1141
1189
  process.exit(1);
1142
1190
  }
1143
- const deleteBranch = options.branch ?? await confirm4({
1191
+ const deleteBranch = options.branch ?? await confirm5({
1144
1192
  message: `Also delete branch "${branchDisplay}"?`,
1145
1193
  default: false
1146
1194
  });
@@ -1151,7 +1199,7 @@ async function doRemove(tree, branchName, options) {
1151
1199
  } catch (err) {
1152
1200
  printWarn(`Could not delete local branch: ${String(err)}`);
1153
1201
  }
1154
- const deleteRemote = await confirm4({
1202
+ const deleteRemote = await confirm5({
1155
1203
  message: `Also delete remote branch origin/${branchDisplay}?`,
1156
1204
  default: false
1157
1205
  });
@@ -1177,6 +1225,7 @@ program.command("issues").alias("ls").description("List Jira issues assigned to
1177
1225
  program.command("start [key]").description("Create and checkout a branch for a Jira issue (e.g. CTM-123 or just 123)").option("-w, --worktree", "Create a linked worktree instead of switching branches").action(startCommand);
1178
1226
  program.command("done [key]").description("Push branch, create PR, and mark the Jira issue as Done").action(doneCommand);
1179
1227
  program.command("status").alias("st").description("Show current branch status and linked Jira issue").action(statusCommand);
1228
+ program.command("checkout [key]").alias("co").description("Checkout a branch by Jira issue key (e.g. CTM-123 or just 123)").action(checkoutCommand);
1180
1229
  program.command("clean [key]").description("Delete local (and optionally remote) branch for a Jira issue").action(cleanCommand);
1181
1230
  program.command("help [command]").description("\uBA85\uB839\uC5B4 \uB3C4\uC6C0\uB9D0 \uCD9C\uB825").action(helpCommand);
1182
1231
  var wt = program.command("worktree").alias("wt").description("Manage git worktrees created by ctm start --worktree");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@devchangjun/ctm",
3
- "version": "0.1.0",
3
+ "version": "0.1.6",
4
4
  "description": "Colo Ticket Manager — Jira-driven branch management CLI",
5
5
  "type": "module",
6
6
  "bin": {
@@ -16,7 +16,13 @@
16
16
  "typecheck": "tsc --noEmit",
17
17
  "format": "prettier --write src/"
18
18
  },
19
- "keywords": ["jira", "git", "branch", "cli", "ticket"],
19
+ "keywords": [
20
+ "jira",
21
+ "git",
22
+ "branch",
23
+ "cli",
24
+ "ticket"
25
+ ],
20
26
  "author": "changjun",
21
27
  "license": "MIT",
22
28
  "repository": {