claude-teammate 0.1.261 → 0.1.262

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-teammate",
3
- "version": "0.1.261",
3
+ "version": "0.1.262",
4
4
  "description": "CLI bootstrapper for Claude Teammate.",
5
5
  "license": "MIT",
6
6
  "type": "module",
package/src/claude.js CHANGED
@@ -820,3 +820,38 @@ export async function runClaudeRepoExtraction(input) {
820
820
  }
821
821
  );
822
822
  }
823
+
824
+ const BRANCH_SLUG_SCHEMA = {
825
+ type: "object",
826
+ properties: {
827
+ slug: { type: "string" }
828
+ },
829
+ required: ["slug"],
830
+ additionalProperties: false
831
+ };
832
+
833
+ export async function runClaudeBranchSlug(title) {
834
+ try {
835
+ const result = await invokeClaudeTask(
836
+ BRANCH_SLUG_SCHEMA,
837
+ "Generate a short English branch name slug: 3-5 lowercase words joined by hyphens that summarize the issue title. Output only the slug field. Example outputs: 'add-user-auth', 'fix-login-redirect', 'manage-maker-account-requests'.",
838
+ `Issue title: ${title}`,
839
+ {
840
+ model: "haiku",
841
+ permissionMode: "bypassPermissions",
842
+ effort: "low",
843
+ runOpts: {
844
+ timeout: 15_000,
845
+ phase: "branch-slug"
846
+ }
847
+ }
848
+ );
849
+ return String(result?.slug || "")
850
+ .toLowerCase()
851
+ .replace(/[^a-z0-9-]/gu, "")
852
+ .replace(/^-+|-+$/gu, "")
853
+ .slice(0, 40);
854
+ } catch {
855
+ return "";
856
+ }
857
+ }
@@ -1,4 +1,4 @@
1
- import { isClaudeCliError, runClaudeGitHubIssueReview } from "../claude.js";
1
+ import { isClaudeCliError, runClaudeBranchSlug, runClaudeGitHubIssueReview } from "../claude.js";
2
2
  import { deriveEpicMemoryPath, saveIssueMemory } from "../memory.js";
3
3
  import { ensureBranchFromDefault, ensureWorktreeForBranch, pullLatest, removeWorktree } from "../repo.js";
4
4
  import {
@@ -207,7 +207,9 @@ export async function processGitHubIssue({
207
207
  const approvalComment = isApprovalComment(latestComment.body);
208
208
 
209
209
  if (approvalComment) {
210
- const nextBranchName = githubIssueMemory.branch_name || buildImplementationBranchName(detail.title, detail.number);
210
+ const englishSlug = githubIssueMemory.branch_name ? "" : await runClaudeBranchSlug(detail.title);
211
+ const nextBranchName =
212
+ githubIssueMemory.branch_name || buildImplementationBranchName(detail.title, detail.number, englishSlug);
211
213
  const existingPr = await github.findPullRequestByHead(githubIssueMemory.repo_url, nextBranchName);
212
214
  if (!existingPr) {
213
215
  githubIssueMemory.pr_url = "";
@@ -301,16 +301,35 @@ export function isPullRequestReviewRequestComment(body, githubBotUser) {
301
301
  return normalizedBody.includes(`requested review from @${botLogin}`);
302
302
  }
303
303
 
304
- export function buildImplementationBranchName(title, issueNumber) {
304
+ export function buildImplementationBranchName(title, issueNumber, englishSlug = "") {
305
305
  const jiraKey = extractJiraKey(title);
306
- const slug = normalizePlanTitleText(title)
307
- .toLowerCase()
308
- .replace(/\[[^\]]+\]/gu, "")
309
- .replace(/[^a-z0-9]+/gu, "-")
310
- .replace(/^-+|-+$/gu, "")
311
- .slice(0, 48);
306
+ const type = detectBranchType(title);
307
+ const id = (jiraKey || `gh-${issueNumber}`).toLowerCase();
308
+ const suffix = englishSlug ? `-${englishSlug}` : "";
309
+ return `${type}/${id}${suffix}`;
310
+ }
312
311
 
313
- return `${(jiraKey || `gh-${issueNumber}`).toLowerCase()}-${slug || "plan"}`;
312
+ function detectBranchType(title) {
313
+ const t = String(title || "");
314
+
315
+ const prefixMatch =
316
+ /^\s*(?:\[[A-Z][A-Z0-9]+-\d+\]\s*)?(fix|feat|refactor|perf|docs|ci|build|revert|chore|vendor|security|other)\s*:/iu.exec(
317
+ t
318
+ );
319
+ if (prefixMatch) return prefixMatch[1].toLowerCase();
320
+
321
+ const lower = t.toLowerCase();
322
+ if (/\b(fix|hotfix|patch)\b|sửa lỗi|bug fix/.test(lower)) return "fix";
323
+ if (/\brefactor\b|tái cấu trúc/.test(lower)) return "refactor";
324
+ if (/\b(perf|performance)\b|hiệu năng|hiệu suất|tối ưu/.test(lower)) return "perf";
325
+ if (/\b(docs?|document|readme)\b|tài liệu/.test(lower)) return "docs";
326
+ if (/\bci\b|pipeline/.test(lower)) return "ci";
327
+ if (/\bbuild\b/.test(lower)) return "build";
328
+ if (/\b(revert|rollback)\b|hoàn tác/.test(lower)) return "revert";
329
+ if (/\bchore\b|cấu hình/.test(lower)) return "chore";
330
+ if (/\b(vendor|dependency|npm|yarn)\b|thư viện/.test(lower)) return "vendor";
331
+ if (/\b(security|sonarqube)\b|bảo mật/.test(lower)) return "security";
332
+ return "feat";
314
333
  }
315
334
 
316
335
  export async function buildPullRequestRepoAccessPlan({ repo, pullRequestBody, latestCommentBody = "", epicMemory }) {