clawt 3.10.4 → 3.10.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 (48) hide show
  1. package/AGENTS.md +16 -0
  2. package/dist/index.js +228 -86
  3. package/dist/postinstall.js +27 -0
  4. package/docs/create.md +1 -0
  5. package/docs/list.md +21 -10
  6. package/docs/merge.md +1 -0
  7. package/docs/remove.md +2 -0
  8. package/docs/spec.md +4 -1
  9. package/docs/status.md +9 -1
  10. package/docs/superpowers/findings/2026-06-01-sync-validate-diverged-findings.md +203 -0
  11. package/docs/superpowers/findings/2026-06-09-worktree-base-branch-findings.md +58 -0
  12. package/docs/superpowers/plans/2026-06-01-validate-ignored-files-conflict.md +412 -0
  13. package/docs/superpowers/plans/2026-06-09-worktree-base-branch.md +386 -0
  14. package/docs/superpowers/specs/2026-06-01-validate-ignored-files-conflict-design.md +76 -0
  15. package/docs/superpowers/specs/2026-06-09-worktree-base-branch-design.md +169 -0
  16. package/docs/validate.md +42 -5
  17. package/package.json +1 -1
  18. package/src/commands/list.ts +5 -3
  19. package/src/commands/merge.ts +1 -1
  20. package/src/commands/remove.ts +3 -0
  21. package/src/commands/status.ts +5 -0
  22. package/src/constants/messages/validate.ts +17 -0
  23. package/src/types/status.ts +2 -0
  24. package/src/types/worktree.ts +12 -0
  25. package/src/utils/formatter.ts +22 -0
  26. package/src/utils/git-core.ts +23 -0
  27. package/src/utils/index.ts +4 -2
  28. package/src/utils/interactive-panel-render.ts +6 -3
  29. package/src/utils/validate-core.ts +52 -0
  30. package/src/utils/worktree-metadata.ts +82 -0
  31. package/src/utils/worktree.ts +29 -10
  32. package/tests/helpers/fixtures.ts +1 -0
  33. package/tests/unit/commands/cover-validate.test.ts +4 -4
  34. package/tests/unit/commands/create.test.ts +3 -3
  35. package/tests/unit/commands/list.test.ts +66 -3
  36. package/tests/unit/commands/merge.test.ts +1 -1
  37. package/tests/unit/commands/remove.test.ts +24 -18
  38. package/tests/unit/commands/resume.test.ts +21 -21
  39. package/tests/unit/commands/run.test.ts +17 -17
  40. package/tests/unit/commands/status.test.ts +85 -10
  41. package/tests/unit/commands/sync.test.ts +4 -4
  42. package/tests/unit/commands/validate.test.ts +1 -1
  43. package/tests/unit/utils/git-core.test.ts +43 -0
  44. package/tests/unit/utils/interactive-panel-render.test.ts +124 -0
  45. package/tests/unit/utils/validate-core.test.ts +60 -0
  46. package/tests/unit/utils/worktree-matcher.test.ts +2 -2
  47. package/tests/unit/utils/worktree-metadata.test.ts +91 -0
  48. package/tests/unit/utils/worktree.test.ts +65 -0
package/dist/index.js CHANGED
@@ -819,6 +819,33 @@ var VALIDATE_MESSAGES_I18N = {
819
819
  "zh-CN": (branch) => `\u53D8\u66F4\u8FC1\u79FB\u5931\u8D25\uFF1A\u76EE\u6807\u5206\u652F\u4E0E\u4E3B\u5206\u652F\u5DEE\u5F02\u8FC7\u5927
820
820
  \u8BF7\u5148\u6267\u884C clawt sync -b ${branch} \u540C\u6B65\u4E3B\u5206\u652F\u540E\u91CD\u8BD5`
821
821
  },
822
+ /** validate 检测到被 .gitignore 忽略的残留文件冲突 */
823
+ VALIDATE_IGNORED_FILES_CONFLICT: {
824
+ en: (files, cleanCommands) => {
825
+ const maxDisplay = 10;
826
+ const displayed = files.slice(0, maxDisplay).map((f) => ` - ${f}`).join("\n");
827
+ const more = files.length > maxDisplay ? `
828
+ ...(${files.length} files total)` : "";
829
+ const cmds = cleanCommands.map((c) => ` ${c}`).join("\n");
830
+ return `Ignored files left in main worktree are blocking patch apply:
831
+ ${displayed}${more}
832
+
833
+ Please clean up manually and retry:
834
+ ${cmds}`;
835
+ },
836
+ "zh-CN": (files, cleanCommands) => {
837
+ const maxDisplay = 10;
838
+ const displayed = files.slice(0, maxDisplay).map((f) => ` - ${f}`).join("\n");
839
+ const more = files.length > maxDisplay ? `
840
+ ...\uFF08\u5171 ${files.length} \u4E2A\u6587\u4EF6\uFF09` : "";
841
+ const cmds = cleanCommands.map((c) => ` ${c}`).join("\n");
842
+ return `\u68C0\u6D4B\u5230\u88AB .gitignore \u5FFD\u7565\u7684\u6587\u4EF6\u6B8B\u7559\u5728\u4E3B worktree \u4E2D\uFF0C\u5BFC\u81F4\u53D8\u66F4\u65E0\u6CD5\u5E94\u7528\uFF1A
843
+ ${displayed}${more}
844
+
845
+ \u8BF7\u624B\u52A8\u6E05\u7406\u540E\u91CD\u8BD5\uFF1A
846
+ ${cmds}`;
847
+ }
848
+ },
822
849
  /** validate 无可用 worktree */
823
850
  VALIDATE_NO_WORKTREES: {
824
851
  en: "No worktrees available, please create one with clawt run or clawt create first",
@@ -2817,6 +2844,19 @@ function gitMergeContinue(cwd) {
2817
2844
  function buildAutoSaveCommitMessage(mainBranch, branch) {
2818
2845
  return `${AUTO_SAVE_COMMIT_MESSAGE_PREFIX} ${mainBranch} into ${branch}`;
2819
2846
  }
2847
+ function gitCheckIgnored(paths, cwd) {
2848
+ if (paths.length === 0) return [];
2849
+ try {
2850
+ const output = execFileSync2("git", ["check-ignore", "--", ...paths], {
2851
+ cwd,
2852
+ encoding: "utf-8",
2853
+ stdio: ["pipe", "pipe", "pipe"]
2854
+ });
2855
+ return output.trim().split("\n").filter(Boolean);
2856
+ } catch {
2857
+ return [];
2858
+ }
2859
+ }
2820
2860
 
2821
2861
  // src/utils/git-branch.ts
2822
2862
  function checkBranchExists(branchName, cwd) {
@@ -3027,6 +3067,16 @@ function formatLocalISOString(date) {
3027
3067
  const minutes = String(absMinutes % 60).padStart(2, "0");
3028
3068
  return `${iso}${sign}${hours}:${minutes}`;
3029
3069
  }
3070
+ function formatBaseBranchLine(baseBranch) {
3071
+ const lang = getCurrentLanguage();
3072
+ const label = lang === "en" ? "Base branch" : "\u6765\u6E90\u5206\u652F";
3073
+ const fallback = lang === "en" ? "Not recorded" : "\u672A\u8BB0\u5F55";
3074
+ return `${label}: ${baseBranch ?? fallback}`;
3075
+ }
3076
+ function formatBaseBranchInline(baseBranch) {
3077
+ const fallback = getCurrentLanguage() === "en" ? "Not recorded" : "\u672A\u8BB0\u5F55";
3078
+ return `<- ${baseBranch ?? fallback}`;
3079
+ }
3030
3080
  function generateTaskFilename(prefix) {
3031
3081
  const now = /* @__PURE__ */ new Date();
3032
3082
  const pad = (n) => String(n).padStart(2, "0");
@@ -3355,45 +3405,100 @@ async function runPreChecks(options) {
3355
3405
  }
3356
3406
 
3357
3407
  // src/utils/worktree.ts
3358
- import { join as join5 } from "path";
3359
- import { existsSync as existsSync5, readdirSync as readdirSync2 } from "fs";
3408
+ import { join as join6 } from "path";
3409
+ import { existsSync as existsSync6, readdirSync as readdirSync2 } from "fs";
3410
+
3411
+ // src/utils/worktree-metadata.ts
3412
+ import { existsSync as existsSync5, readFileSync as readFileSync3, rmSync, writeFileSync as writeFileSync3 } from "fs";
3413
+ import { dirname, join as join5 } from "path";
3414
+ function getWorktreeMetadataPath(projectName, branchName) {
3415
+ return join5(PROJECTS_CONFIG_DIR, projectName, "worktrees", `${branchName}.json`);
3416
+ }
3417
+ function saveWorktreeMetadata(projectName, metadata) {
3418
+ const metadataPath = getWorktreeMetadataPath(projectName, metadata.branch);
3419
+ const metadataDir = dirname(metadataPath);
3420
+ try {
3421
+ ensureDir(metadataDir);
3422
+ writeFileSync3(metadataPath, safeStringify(metadata), "utf-8");
3423
+ } catch (error) {
3424
+ logger.error(`\u4FDD\u5B58 worktree \u5143\u6570\u636E\u5931\u8D25: ${metadataPath}`, error);
3425
+ throw error;
3426
+ }
3427
+ }
3428
+ function loadWorktreeMetadata(projectName, branchName) {
3429
+ const metadataPath = getWorktreeMetadataPath(projectName, branchName);
3430
+ if (!existsSync5(metadataPath)) {
3431
+ return null;
3432
+ }
3433
+ try {
3434
+ const content = readFileSync3(metadataPath, "utf-8");
3435
+ const parsed = JSON.parse(content);
3436
+ if (!parsed || typeof parsed !== "object" || !parsed.branch || !parsed.baseBranch) {
3437
+ logger.warn(`worktree \u5143\u6570\u636E\u683C\u5F0F\u65E0\u6548: ${metadataPath}`);
3438
+ return null;
3439
+ }
3440
+ return parsed;
3441
+ } catch (error) {
3442
+ logger.warn(`\u89E3\u6790 worktree \u5143\u6570\u636E\u5931\u8D25: ${metadataPath}`, error);
3443
+ return null;
3444
+ }
3445
+ }
3446
+ function removeWorktreeMetadata(projectName, branchName) {
3447
+ const metadataPath = getWorktreeMetadataPath(projectName, branchName);
3448
+ try {
3449
+ if (existsSync5(metadataPath)) {
3450
+ rmSync(metadataPath);
3451
+ }
3452
+ } catch (error) {
3453
+ logger.error(`\u5220\u9664 worktree \u5143\u6570\u636E\u5931\u8D25: ${metadataPath}`, error);
3454
+ }
3455
+ }
3456
+
3457
+ // src/utils/worktree.ts
3360
3458
  function getProjectWorktreeDir() {
3361
3459
  const projectName = getProjectName();
3362
- return join5(WORKTREES_DIR, projectName);
3460
+ return join6(WORKTREES_DIR, projectName);
3363
3461
  }
3364
3462
  function createWorktrees(branchName, count) {
3365
3463
  const sanitized = sanitizeBranchName(branchName);
3366
3464
  const branchNames = generateBranchNames(sanitized, count);
3367
3465
  validateBranchesNotExist(branchNames);
3368
- const projectDir = getProjectWorktreeDir();
3466
+ const projectName = getProjectName();
3467
+ const projectDir = join6(WORKTREES_DIR, projectName);
3369
3468
  ensureDir(projectDir);
3469
+ const baseBranch = getCurrentBranch();
3370
3470
  const results = [];
3371
3471
  for (const name of branchNames) {
3372
- const worktreePath = join5(projectDir, name);
3472
+ const worktreePath = join6(projectDir, name);
3373
3473
  createWorktree(name, worktreePath);
3374
3474
  createValidateBranch(name);
3375
- results.push({ path: worktreePath, branch: name });
3475
+ saveWorktreeMetadata(projectName, { branch: name, baseBranch, createdAt: (/* @__PURE__ */ new Date()).toISOString() });
3476
+ results.push({ path: worktreePath, branch: name, baseBranch });
3376
3477
  logger.info(`worktree \u521B\u5EFA\u5B8C\u6210: ${worktreePath} (\u5206\u652F: ${name})`);
3377
3478
  }
3378
3479
  return results;
3379
3480
  }
3380
3481
  function createWorktreesByBranches(branchNames) {
3381
3482
  validateBranchesNotExist(branchNames);
3382
- const projectDir = getProjectWorktreeDir();
3483
+ const projectName = getProjectName();
3484
+ const projectDir = join6(WORKTREES_DIR, projectName);
3383
3485
  ensureDir(projectDir);
3486
+ const baseBranch = getCurrentBranch();
3384
3487
  const results = [];
3385
3488
  for (const name of branchNames) {
3386
- const worktreePath = join5(projectDir, name);
3489
+ const worktreePath = join6(projectDir, name);
3387
3490
  createWorktree(name, worktreePath);
3388
3491
  createValidateBranch(name);
3389
- results.push({ path: worktreePath, branch: name });
3492
+ saveWorktreeMetadata(projectName, { branch: name, baseBranch, createdAt: (/* @__PURE__ */ new Date()).toISOString() });
3493
+ results.push({ path: worktreePath, branch: name, baseBranch });
3390
3494
  logger.info(`worktree \u521B\u5EFA\u5B8C\u6210: ${worktreePath} (\u5206\u652F: ${name})`);
3391
3495
  }
3392
3496
  return results;
3393
3497
  }
3394
3498
  function getProjectWorktrees() {
3395
- const projectDir = getProjectWorktreeDir();
3396
- if (!existsSync5(projectDir)) {
3499
+ const projectName = getProjectName();
3500
+ const projectDir = join6(WORKTREES_DIR, projectName);
3501
+ if (!existsSync6(projectDir)) {
3397
3502
  return [];
3398
3503
  }
3399
3504
  const worktreeListOutput = gitWorktreeList();
@@ -3406,29 +3511,33 @@ function getProjectWorktrees() {
3406
3511
  if (!entry.isDirectory()) {
3407
3512
  continue;
3408
3513
  }
3409
- const fullPath = join5(projectDir, entry.name);
3514
+ const fullPath = join6(projectDir, entry.name);
3410
3515
  if (registeredPaths.has(fullPath)) {
3516
+ const metadata = loadWorktreeMetadata(projectName, entry.name);
3411
3517
  worktrees.push({
3412
3518
  path: fullPath,
3413
- branch: entry.name
3519
+ branch: entry.name,
3520
+ baseBranch: metadata?.baseBranch ?? null
3414
3521
  });
3415
3522
  }
3416
3523
  }
3417
3524
  return worktrees;
3418
3525
  }
3419
3526
  function cleanupWorktrees(worktrees) {
3527
+ const projectName = getProjectName();
3420
3528
  for (const wt of worktrees) {
3421
3529
  try {
3422
3530
  removeWorktreeByPath(wt.path);
3423
3531
  deleteBranch(wt.branch);
3424
3532
  deleteValidateBranch(wt.branch);
3533
+ removeWorktreeMetadata(projectName, wt.branch);
3425
3534
  logger.info(`\u5DF2\u6E05\u7406 worktree \u548C\u5206\u652F: ${wt.branch}`);
3426
3535
  } catch (error) {
3427
3536
  logger.error(`\u6E05\u7406 worktree \u5931\u8D25: ${wt.path} - ${error}`);
3428
3537
  }
3429
3538
  }
3430
3539
  gitWorktreePrune();
3431
- const projectDir = getProjectWorktreeDir();
3540
+ const projectDir = join6(WORKTREES_DIR, projectName);
3432
3541
  removeEmptyDir(projectDir);
3433
3542
  }
3434
3543
  function getWorktreeStatus(worktree) {
@@ -3445,7 +3554,7 @@ function getWorktreeStatus(worktree) {
3445
3554
 
3446
3555
  // src/utils/symlink-guard.ts
3447
3556
  import { readdirSync as readdirSync3, lstatSync, unlinkSync, readlinkSync } from "fs";
3448
- import { join as join6, relative, isAbsolute as isAbsolute2, resolve } from "path";
3557
+ import { join as join7, relative, isAbsolute as isAbsolute2, resolve } from "path";
3449
3558
  function isExternalSymlink(linkPath, worktreeRoot) {
3450
3559
  try {
3451
3560
  const target = readlinkSync(linkPath);
@@ -3462,7 +3571,7 @@ function removeExternalSymlinks(dir) {
3462
3571
  const entries = readdirSync3(dir, { withFileTypes: true });
3463
3572
  for (const entry of entries) {
3464
3573
  if (!entry.isSymbolicLink()) continue;
3465
- const fullPath = join6(dir, entry.name);
3574
+ const fullPath = join7(dir, entry.name);
3466
3575
  if (!isExternalSymlink(fullPath, dir)) continue;
3467
3576
  try {
3468
3577
  const stat = lstatSync(fullPath);
@@ -3497,14 +3606,14 @@ async function promptCommitMessage(promptMessage, nonInteractiveErrorMessage) {
3497
3606
 
3498
3607
  // src/utils/claude.ts
3499
3608
  import { spawnSync as spawnSync3 } from "child_process";
3500
- import { existsSync as existsSync7, readdirSync as readdirSync4 } from "fs";
3501
- import { join as join7 } from "path";
3609
+ import { existsSync as existsSync8, readdirSync as readdirSync4 } from "fs";
3610
+ import { join as join8 } from "path";
3502
3611
 
3503
3612
  // src/utils/terminal.ts
3504
3613
  import { execFileSync as execFileSync3 } from "child_process";
3505
- import { existsSync as existsSync6 } from "fs";
3614
+ import { existsSync as existsSync7 } from "fs";
3506
3615
  function isITerm2Installed() {
3507
- return existsSync6(ITERM2_APP_PATH);
3616
+ return existsSync7(ITERM2_APP_PATH);
3508
3617
  }
3509
3618
  function isCmuxEnvironment() {
3510
3619
  return !!process.env.CMUX_WORKSPACE_ID;
@@ -3642,8 +3751,8 @@ function encodeClaudeProjectPath(absolutePath) {
3642
3751
  }
3643
3752
  function hasClaudeSessionHistory(worktreePath) {
3644
3753
  const encodedName = encodeClaudeProjectPath(worktreePath);
3645
- const projectDir = join7(CLAUDE_PROJECTS_DIR, encodedName);
3646
- if (!existsSync7(projectDir)) {
3754
+ const projectDir = join8(CLAUDE_PROJECTS_DIR, encodedName);
3755
+ if (!existsSync8(projectDir)) {
3647
3756
  return false;
3648
3757
  }
3649
3758
  const entries = readdirSync4(projectDir);
@@ -3696,23 +3805,23 @@ function launchInteractiveClaudeInNewTerminal(worktree, hasPreviousSession) {
3696
3805
  }
3697
3806
 
3698
3807
  // src/utils/validate-snapshot.ts
3699
- import { join as join8 } from "path";
3700
- import { existsSync as existsSync8, readFileSync as readFileSync3, writeFileSync as writeFileSync3, unlinkSync as unlinkSync2, readdirSync as readdirSync5, rmdirSync as rmdirSync2, statSync as statSync2 } from "fs";
3808
+ import { join as join9 } from "path";
3809
+ import { existsSync as existsSync9, readFileSync as readFileSync4, writeFileSync as writeFileSync4, unlinkSync as unlinkSync2, readdirSync as readdirSync5, rmdirSync as rmdirSync2, statSync as statSync2 } from "fs";
3701
3810
  function getSnapshotPath(projectName, branchName) {
3702
- return join8(VALIDATE_SNAPSHOTS_DIR, projectName, `${branchName}.tree`);
3811
+ return join9(VALIDATE_SNAPSHOTS_DIR, projectName, `${branchName}.tree`);
3703
3812
  }
3704
3813
  function getSnapshotHeadPath(projectName, branchName) {
3705
- return join8(VALIDATE_SNAPSHOTS_DIR, projectName, `${branchName}.head`);
3814
+ return join9(VALIDATE_SNAPSHOTS_DIR, projectName, `${branchName}.head`);
3706
3815
  }
3707
3816
  function getSnapshotStagedPath(projectName, branchName) {
3708
- return join8(VALIDATE_SNAPSHOTS_DIR, projectName, `${branchName}.staged`);
3817
+ return join9(VALIDATE_SNAPSHOTS_DIR, projectName, `${branchName}.staged`);
3709
3818
  }
3710
3819
  function hasSnapshot(projectName, branchName) {
3711
- return existsSync8(getSnapshotPath(projectName, branchName));
3820
+ return existsSync9(getSnapshotPath(projectName, branchName));
3712
3821
  }
3713
3822
  function getSnapshotModifiedTime(projectName, branchName) {
3714
3823
  const snapshotPath = getSnapshotPath(projectName, branchName);
3715
- if (!existsSync8(snapshotPath)) return null;
3824
+ if (!existsSync9(snapshotPath)) return null;
3716
3825
  const stat = statSync2(snapshotPath);
3717
3826
  return stat.mtime.toISOString();
3718
3827
  }
@@ -3721,22 +3830,22 @@ function readSnapshot(projectName, branchName) {
3721
3830
  const headPath = getSnapshotHeadPath(projectName, branchName);
3722
3831
  const stagedPath = getSnapshotStagedPath(projectName, branchName);
3723
3832
  logger.debug(`\u8BFB\u53D6 validate \u5FEB\u7167: ${snapshotPath}`);
3724
- const treeHash = existsSync8(snapshotPath) ? readFileSync3(snapshotPath, "utf-8").trim() : "";
3725
- const headCommitHash = existsSync8(headPath) ? readFileSync3(headPath, "utf-8").trim() : "";
3726
- const stagedTreeHash = existsSync8(stagedPath) ? readFileSync3(stagedPath, "utf-8").trim() : "";
3833
+ const treeHash = existsSync9(snapshotPath) ? readFileSync4(snapshotPath, "utf-8").trim() : "";
3834
+ const headCommitHash = existsSync9(headPath) ? readFileSync4(headPath, "utf-8").trim() : "";
3835
+ const stagedTreeHash = existsSync9(stagedPath) ? readFileSync4(stagedPath, "utf-8").trim() : "";
3727
3836
  return { treeHash, headCommitHash, stagedTreeHash };
3728
3837
  }
3729
3838
  function writeSnapshot(projectName, branchName, treeHash, headCommitHash, stagedTreeHash) {
3730
- const snapshotDir = join8(VALIDATE_SNAPSHOTS_DIR, projectName);
3839
+ const snapshotDir = join9(VALIDATE_SNAPSHOTS_DIR, projectName);
3731
3840
  ensureDir(snapshotDir);
3732
3841
  if (treeHash !== void 0) {
3733
- writeFileSync3(getSnapshotPath(projectName, branchName), treeHash, "utf-8");
3842
+ writeFileSync4(getSnapshotPath(projectName, branchName), treeHash, "utf-8");
3734
3843
  }
3735
3844
  if (headCommitHash !== void 0) {
3736
- writeFileSync3(getSnapshotHeadPath(projectName, branchName), headCommitHash, "utf-8");
3845
+ writeFileSync4(getSnapshotHeadPath(projectName, branchName), headCommitHash, "utf-8");
3737
3846
  }
3738
3847
  if (stagedTreeHash !== void 0) {
3739
- writeFileSync3(getSnapshotStagedPath(projectName, branchName), stagedTreeHash, "utf-8");
3848
+ writeFileSync4(getSnapshotStagedPath(projectName, branchName), stagedTreeHash, "utf-8");
3740
3849
  }
3741
3850
  logger.info(`\u5DF2\u5199\u5165 validate \u5FEB\u7167 (project=${projectName}, branch=${branchName})`);
3742
3851
  }
@@ -3744,35 +3853,35 @@ function removeSnapshot(projectName, branchName) {
3744
3853
  const snapshotPath = getSnapshotPath(projectName, branchName);
3745
3854
  const headPath = getSnapshotHeadPath(projectName, branchName);
3746
3855
  const stagedPath = getSnapshotStagedPath(projectName, branchName);
3747
- if (existsSync8(snapshotPath)) {
3856
+ if (existsSync9(snapshotPath)) {
3748
3857
  unlinkSync2(snapshotPath);
3749
3858
  logger.info(`\u5DF2\u5220\u9664 validate \u5FEB\u7167: ${snapshotPath}`);
3750
3859
  }
3751
- if (existsSync8(headPath)) {
3860
+ if (existsSync9(headPath)) {
3752
3861
  unlinkSync2(headPath);
3753
3862
  logger.info(`\u5DF2\u5220\u9664 validate \u5FEB\u7167: ${headPath}`);
3754
3863
  }
3755
- if (existsSync8(stagedPath)) {
3864
+ if (existsSync9(stagedPath)) {
3756
3865
  unlinkSync2(stagedPath);
3757
3866
  logger.info(`\u5DF2\u5220\u9664 validate \u5FEB\u7167: ${stagedPath}`);
3758
3867
  }
3759
3868
  }
3760
3869
  function getProjectSnapshotBranches(projectName) {
3761
- const projectDir = join8(VALIDATE_SNAPSHOTS_DIR, projectName);
3762
- if (!existsSync8(projectDir)) {
3870
+ const projectDir = join9(VALIDATE_SNAPSHOTS_DIR, projectName);
3871
+ if (!existsSync9(projectDir)) {
3763
3872
  return [];
3764
3873
  }
3765
3874
  const files = readdirSync5(projectDir);
3766
3875
  return files.filter((f) => f.endsWith(".tree")).map((f) => f.replace(/\.tree$/, ""));
3767
3876
  }
3768
3877
  function removeProjectSnapshots(projectName) {
3769
- const projectDir = join8(VALIDATE_SNAPSHOTS_DIR, projectName);
3770
- if (!existsSync8(projectDir)) {
3878
+ const projectDir = join9(VALIDATE_SNAPSHOTS_DIR, projectName);
3879
+ if (!existsSync9(projectDir)) {
3771
3880
  return;
3772
3881
  }
3773
3882
  const files = readdirSync5(projectDir);
3774
3883
  for (const file of files) {
3775
- unlinkSync2(join8(projectDir, file));
3884
+ unlinkSync2(join9(projectDir, file));
3776
3885
  }
3777
3886
  try {
3778
3887
  rmdirSync2(projectDir);
@@ -4303,7 +4412,7 @@ var ProgressRenderer = class {
4303
4412
 
4304
4413
  // src/utils/task-file.ts
4305
4414
  import { resolve as resolve2 } from "path";
4306
- import { existsSync as existsSync9, readFileSync as readFileSync4 } from "fs";
4415
+ import { existsSync as existsSync10, readFileSync as readFileSync5 } from "fs";
4307
4416
  var TASK_BLOCK_REGEX = /<!-- CLAWT-TASKS:START -->([\s\S]*?)<!-- CLAWT-TASKS:END -->/g;
4308
4417
  var BRANCH_LINE_REGEX = /^#\s*branch:\s*(.+)$/;
4309
4418
  var EMPTY_TASKS_MESSAGE = getCurrentLanguage() === "en" ? "Task list cannot be empty" : "\u4EFB\u52A1\u5217\u8868\u4E0D\u80FD\u4E3A\u7A7A";
@@ -4349,10 +4458,10 @@ function parseTaskFile(content, options) {
4349
4458
  }
4350
4459
  function loadTaskFile(filePath, options) {
4351
4460
  const absolutePath = resolve2(filePath);
4352
- if (!existsSync9(absolutePath)) {
4461
+ if (!existsSync10(absolutePath)) {
4353
4462
  throw new ClawtError(MESSAGES.TASK_FILE_NOT_FOUND(absolutePath));
4354
4463
  }
4355
- const content = readFileSync4(absolutePath, "utf-8");
4464
+ const content = readFileSync5(absolutePath, "utf-8");
4356
4465
  const entries = parseTaskFile(content, options);
4357
4466
  if (entries.length === 0) {
4358
4467
  throw new ClawtError(MESSAGES.TASK_FILE_EMPTY);
@@ -4697,7 +4806,7 @@ async function executeBatchTasks(worktrees, tasks, concurrency, continueFlags) {
4697
4806
 
4698
4807
  // src/utils/dry-run.ts
4699
4808
  import chalk6 from "chalk";
4700
- import { join as join9 } from "path";
4809
+ import { join as join10 } from "path";
4701
4810
  var DRY_RUN_TASK_DESC_MAX_LENGTH = 80;
4702
4811
  function truncateTaskDesc(task) {
4703
4812
  const oneLine = task.replace(/\n/g, " ").trim();
@@ -4725,7 +4834,7 @@ function printDryRunPreview(branchNames, tasks, concurrency) {
4725
4834
  let hasConflict = false;
4726
4835
  for (let i = 0; i < branchNames.length; i++) {
4727
4836
  const branch = branchNames[i];
4728
- const worktreePath = join9(projectDir, branch);
4837
+ const worktreePath = join10(projectDir, branch);
4729
4838
  const exists = checkBranchExists(branch);
4730
4839
  if (exists) hasConflict = true;
4731
4840
  const indexLabel = `[${i + 1}/${branchNames.length}]`;
@@ -4880,14 +4989,14 @@ async function promptStringValue(key, currentValue) {
4880
4989
  }
4881
4990
 
4882
4991
  // src/utils/update-checker.ts
4883
- import { readFileSync as readFileSync5, writeFileSync as writeFileSync4 } from "fs";
4992
+ import { readFileSync as readFileSync6, writeFileSync as writeFileSync5 } from "fs";
4884
4993
  import { execSync as execSync4 } from "child_process";
4885
4994
  import { request } from "https";
4886
4995
  import chalk8 from "chalk";
4887
4996
  import stringWidth2 from "string-width";
4888
4997
  function readUpdateCache() {
4889
4998
  try {
4890
- const raw = readFileSync5(UPDATE_CHECK_PATH, "utf-8");
4999
+ const raw = readFileSync6(UPDATE_CHECK_PATH, "utf-8");
4891
5000
  return JSON.parse(raw);
4892
5001
  } catch {
4893
5002
  return null;
@@ -4895,7 +5004,7 @@ function readUpdateCache() {
4895
5004
  }
4896
5005
  function writeUpdateCache(cache) {
4897
5006
  try {
4898
- writeFileSync4(UPDATE_CHECK_PATH, JSON.stringify(cache, null, 2), "utf-8");
5007
+ writeFileSync5(UPDATE_CHECK_PATH, JSON.stringify(cache, null, 2), "utf-8");
4899
5008
  } catch {
4900
5009
  }
4901
5010
  }
@@ -5093,6 +5202,17 @@ async function executeRunCommand(command, mainWorktreePath) {
5093
5202
  }
5094
5203
 
5095
5204
  // src/utils/validate-core.ts
5205
+ import { existsSync as existsSync11 } from "fs";
5206
+ import { join as join11 } from "path";
5207
+ function buildCleanCommands(files) {
5208
+ const dirs = /* @__PURE__ */ new Set();
5209
+ for (const file of files) {
5210
+ const lastSlash = file.lastIndexOf("/");
5211
+ const dir = lastSlash > 0 ? file.substring(0, lastSlash) : ".";
5212
+ dirs.add(dir);
5213
+ }
5214
+ return Array.from(dirs).map((dir) => `git clean -fdx ${dir}/`);
5215
+ }
5096
5216
  function migrateChangesViaPatch(targetWorktreePath, mainWorktreePath, branchName, hasUncommitted) {
5097
5217
  let didTempCommit = false;
5098
5218
  try {
@@ -5101,6 +5221,13 @@ function migrateChangesViaPatch(targetWorktreePath, mainWorktreePath, branchName
5101
5221
  gitCommit("clawt:temp-commit-for-validate", targetWorktreePath);
5102
5222
  didTempCommit = true;
5103
5223
  }
5224
+ const ignoredFiles = detectIgnoredFilesInPatch(branchName, mainWorktreePath);
5225
+ if (ignoredFiles.length > 0) {
5226
+ const cleanCommands = buildCleanCommands(ignoredFiles);
5227
+ logger.warn(`\u68C0\u6D4B\u5230 ${ignoredFiles.length} \u4E2A\u88AB\u5FFD\u7565\u7684\u6B8B\u7559\u6587\u4EF6\u51B2\u7A81`);
5228
+ printWarning(MESSAGES.VALIDATE_IGNORED_FILES_CONFLICT(ignoredFiles, cleanCommands));
5229
+ return { success: false };
5230
+ }
5104
5231
  const patch = gitDiffBinaryAgainstBranch(branchName, mainWorktreePath);
5105
5232
  if (patch.length > 0) {
5106
5233
  try {
@@ -5175,6 +5302,16 @@ function switchToValidateBranch(branchName, mainWorktreePath) {
5175
5302
  }
5176
5303
  return validateBranchName;
5177
5304
  }
5305
+ function detectIgnoredFilesInPatch(branchName, mainWorktreePath) {
5306
+ try {
5307
+ const output = execCommand(`git diff --name-only HEAD...${branchName}`, { cwd: mainWorktreePath });
5308
+ const patchFiles = output.split("\n").filter(Boolean);
5309
+ if (patchFiles.length === 0) return [];
5310
+ return gitCheckIgnored(patchFiles, mainWorktreePath).filter((file) => existsSync11(join11(mainWorktreePath, file)));
5311
+ } catch {
5312
+ return [];
5313
+ }
5314
+ }
5178
5315
 
5179
5316
  // src/utils/interactive-panel.ts
5180
5317
  import { createInterface as createInterface2 } from "readline";
@@ -5218,7 +5355,7 @@ function buildPanelFrame(statusResult, selectedIndex, scrollOffset, rows, cols,
5218
5355
  return lines;
5219
5356
  }
5220
5357
  function buildDisplayOrder(worktrees) {
5221
- const worktreeInfos = worktrees.map((wt) => ({ path: wt.path, branch: wt.branch }));
5358
+ const worktreeInfos = worktrees.map((wt) => ({ path: wt.path, branch: wt.branch, baseBranch: wt.baseBranch }));
5222
5359
  const groups = groupWorktreesByDate(worktreeInfos);
5223
5360
  const branchIndexMap = /* @__PURE__ */ new Map();
5224
5361
  for (let i = 0; i < worktrees.length; i++) {
@@ -5234,7 +5371,7 @@ function buildDisplayOrder(worktrees) {
5234
5371
  }
5235
5372
  function buildGroupedWorktreeLines(worktrees, selectedIndex) {
5236
5373
  const panelLines = [];
5237
- const worktreeInfos = worktrees.map((wt) => ({ path: wt.path, branch: wt.branch }));
5374
+ const worktreeInfos = worktrees.map((wt) => ({ path: wt.path, branch: wt.branch, baseBranch: wt.baseBranch }));
5238
5375
  const groups = groupWorktreesByDate(worktreeInfos);
5239
5376
  const branchIndexMap = /* @__PURE__ */ new Map();
5240
5377
  for (let i = 0; i < worktrees.length; i++) {
@@ -5292,6 +5429,7 @@ function renderWorktreeBlock(wt, isSelected) {
5292
5429
  } else {
5293
5430
  lines.push(`${indent}${chalk9.green(PANEL_SYNCED_WITH_MAIN)}`);
5294
5431
  }
5432
+ lines.push(`${indent}${chalk9.gray(formatBaseBranchLine(wt.baseBranch))}`);
5295
5433
  if (wt.createdAt) {
5296
5434
  const relativeTime = formatRelativeTime(wt.createdAt);
5297
5435
  if (relativeTime) {
@@ -5967,9 +6105,9 @@ async function handleMergeConflict(currentBranch, incomingBranch, cwd, autoFlag)
5967
6105
  }
5968
6106
 
5969
6107
  // src/hooks/post-create.ts
5970
- import { existsSync as existsSync10, accessSync, chmodSync, constants as fsConstants } from "fs";
6108
+ import { existsSync as existsSync12, accessSync, chmodSync, constants as fsConstants } from "fs";
5971
6109
  import { spawn as spawn2 } from "child_process";
5972
- import { join as join10 } from "path";
6110
+ import { join as join12 } from "path";
5973
6111
  var POST_CREATE_SCRIPT_RELATIVE_PATH = ".clawt/postCreate.sh";
5974
6112
  function isExecutable(filePath) {
5975
6113
  try {
@@ -6001,8 +6139,8 @@ function resolvePostCreateHook() {
6001
6139
  }
6002
6140
  }
6003
6141
  const mainWorktreePath = getMainWorktreePath();
6004
- const scriptPath = join10(mainWorktreePath, POST_CREATE_SCRIPT_RELATIVE_PATH);
6005
- if (existsSync10(scriptPath)) {
6142
+ const scriptPath = join12(mainWorktreePath, POST_CREATE_SCRIPT_RELATIVE_PATH);
6143
+ if (existsSync12(scriptPath)) {
6006
6144
  if (!isExecutable(scriptPath)) {
6007
6145
  autoFixExecutablePermission(scriptPath);
6008
6146
  }
@@ -6101,7 +6239,8 @@ function printListAsJson(projectName, worktrees) {
6101
6239
  total: worktrees.length,
6102
6240
  worktrees: worktrees.map((wt) => ({
6103
6241
  path: wt.path,
6104
- branch: wt.branch
6242
+ branch: wt.branch,
6243
+ baseBranch: wt.baseBranch
6105
6244
  }))
6106
6245
  };
6107
6246
  console.log(JSON.stringify(result, null, 2));
@@ -6117,7 +6256,7 @@ function printListAsText(projectName, worktrees) {
6117
6256
  const status = getWorktreeStatus(wt);
6118
6257
  const isIdle = status ? isWorktreeIdle(status) : false;
6119
6258
  const pathDisplay = isIdle ? chalk10.hex("#FF8C00")(wt.path) : wt.path;
6120
- printInfo(` ${pathDisplay} [${wt.branch}]`);
6259
+ printInfo(` ${pathDisplay} [${wt.branch}] ${formatBaseBranchInline(wt.baseBranch)}`);
6121
6260
  if (status) {
6122
6261
  printInfo(` ${formatWorktreeStatus(status)}`);
6123
6262
  } else {
@@ -6217,6 +6356,7 @@ async function handleRemove(options) {
6217
6356
  }
6218
6357
  deleteValidateBranch(wt.branch);
6219
6358
  removeSnapshot(projectName, wt.branch);
6359
+ removeWorktreeMetadata(projectName, wt.branch);
6220
6360
  printSuccess(MESSAGES.WORKTREE_REMOVED(wt.path));
6221
6361
  } catch (error) {
6222
6362
  const errorMessage = error instanceof Error ? error.message : String(error);
@@ -6757,7 +6897,7 @@ async function shouldCleanupAfterMerge(branchName) {
6757
6897
  return confirmAction(MESSAGES.CONFIRM_DELETE_WORKTREE_BRANCH(branchName), true);
6758
6898
  }
6759
6899
  function cleanupWorktreeAndBranch(worktreePath, branchName) {
6760
- cleanupWorktrees([{ path: worktreePath, branch: branchName }]);
6900
+ cleanupWorktrees([{ path: worktreePath, branch: branchName, baseBranch: null }]);
6761
6901
  printSuccess(MESSAGES.WORKTREE_CLEANED(branchName));
6762
6902
  }
6763
6903
  async function handleMerge(options) {
@@ -7032,7 +7172,8 @@ async function collectWorktreeDetailedStatusAsync(worktree, projectName) {
7032
7172
  snapshotTime: resolveSnapshotTime(projectName, worktree.branch),
7033
7173
  insertions: diffStat.insertions,
7034
7174
  deletions: diffStat.deletions,
7035
- createdAt
7175
+ createdAt,
7176
+ baseBranch: worktree.baseBranch
7036
7177
  };
7037
7178
  }
7038
7179
  function detectChangeStatusFromPorcelain(porcelain, commitsAhead) {
@@ -7157,6 +7298,7 @@ function printWorktreeItem(wt) {
7157
7298
  } else {
7158
7299
  printInfo(` ${chalk11.green(lang === "en" ? "In sync with main" : "\u4E0E\u4E3B\u5206\u652F\u540C\u6B65")}`);
7159
7300
  }
7301
+ printInfo(` ${chalk11.gray(formatBaseBranchLine(wt.baseBranch))}`);
7160
7302
  if (wt.createdAt) {
7161
7303
  const relativeTime = formatRelativeTime(wt.createdAt);
7162
7304
  if (relativeTime) {
@@ -7263,8 +7405,8 @@ function registerAliasCommand(program2) {
7263
7405
  }
7264
7406
 
7265
7407
  // src/commands/projects.ts
7266
- import { existsSync as existsSync11, readdirSync as readdirSync6, statSync as statSync4 } from "fs";
7267
- import { join as join11 } from "path";
7408
+ import { existsSync as existsSync13, readdirSync as readdirSync6, statSync as statSync4 } from "fs";
7409
+ import { join as join13 } from "path";
7268
7410
  import chalk13 from "chalk";
7269
7411
  function registerProjectsCommand(program2) {
7270
7412
  program2.command("projects [name]").description(getCurrentLanguage() === "en" ? "Show worktree overview across projects, or view details for a specific project" : "\u5C55\u793A\u6240\u6709\u9879\u76EE\u7684 worktree \u6982\u89C8\uFF0C\u6216\u67E5\u770B\u6307\u5B9A\u9879\u76EE\u7684 worktree \u8BE6\u60C5").option("--json", getCurrentLanguage() === "en" ? "Output in JSON format" : "\u4EE5 JSON \u683C\u5F0F\u8F93\u51FA").action((name, options) => {
@@ -7288,8 +7430,8 @@ function handleProjectsOverview(json) {
7288
7430
  printProjectsOverviewAsText(result);
7289
7431
  }
7290
7432
  function handleProjectDetail(name, json) {
7291
- const projectDir = join11(WORKTREES_DIR, name);
7292
- if (!existsSync11(projectDir)) {
7433
+ const projectDir = join13(WORKTREES_DIR, name);
7434
+ if (!existsSync13(projectDir)) {
7293
7435
  printError(MESSAGES.PROJECTS_NOT_FOUND(name));
7294
7436
  process.exit(1);
7295
7437
  }
@@ -7302,7 +7444,7 @@ function handleProjectDetail(name, json) {
7302
7444
  printProjectDetailAsText(result);
7303
7445
  }
7304
7446
  function collectProjectsOverview() {
7305
- if (!existsSync11(WORKTREES_DIR)) {
7447
+ if (!existsSync13(WORKTREES_DIR)) {
7306
7448
  return { projects: [], totalProjects: 0, totalDiskUsage: 0 };
7307
7449
  }
7308
7450
  const entries = readdirSync6(WORKTREES_DIR, { withFileTypes: true });
@@ -7311,7 +7453,7 @@ function collectProjectsOverview() {
7311
7453
  if (!entry.isDirectory()) {
7312
7454
  continue;
7313
7455
  }
7314
- const projectDir = join11(WORKTREES_DIR, entry.name);
7456
+ const projectDir = join13(WORKTREES_DIR, entry.name);
7315
7457
  const overview = collectSingleProjectOverview(entry.name, projectDir);
7316
7458
  projects.push(overview);
7317
7459
  }
@@ -7328,7 +7470,7 @@ function collectSingleProjectOverview(name, projectDir) {
7328
7470
  const worktreeDirs = subEntries.filter((e) => e.isDirectory());
7329
7471
  const worktreeCount = worktreeDirs.length;
7330
7472
  const diskUsage = calculateDirSize(projectDir);
7331
- const lastActiveTime = resolveProjectLastActiveTime(projectDir, worktreeDirs.map((e) => join11(projectDir, e.name)));
7473
+ const lastActiveTime = resolveProjectLastActiveTime(projectDir, worktreeDirs.map((e) => join13(projectDir, e.name)));
7332
7474
  return {
7333
7475
  name,
7334
7476
  worktreeCount,
@@ -7343,7 +7485,7 @@ function collectProjectDetail(name, projectDir) {
7343
7485
  if (!entry.isDirectory()) {
7344
7486
  continue;
7345
7487
  }
7346
- const wtPath = join11(projectDir, entry.name);
7488
+ const wtPath = join13(projectDir, entry.name);
7347
7489
  const detail = collectSingleWorktreeDetail(entry.name, wtPath);
7348
7490
  worktrees.push(detail);
7349
7491
  }
@@ -7443,7 +7585,7 @@ function printWorktreeDetailItem(wt) {
7443
7585
  }
7444
7586
 
7445
7587
  // src/commands/completion.ts
7446
- import { readFileSync as readFileSync6, writeFileSync as writeFileSync5, existsSync as existsSync13 } from "fs";
7588
+ import { readFileSync as readFileSync7, writeFileSync as writeFileSync6, existsSync as existsSync15 } from "fs";
7447
7589
  import { resolve as resolve3 } from "path";
7448
7590
  import { homedir as homedir2 } from "os";
7449
7591
 
@@ -7494,23 +7636,23 @@ compdef _clawt_completion clawt
7494
7636
  }
7495
7637
 
7496
7638
  // src/utils/completion-engine.ts
7497
- import { existsSync as existsSync12, readdirSync as readdirSync7, statSync as statSync5 } from "fs";
7498
- import { join as join12, dirname, basename as basename2 } from "path";
7639
+ import { existsSync as existsSync14, readdirSync as readdirSync7, statSync as statSync5 } from "fs";
7640
+ import { join as join14, dirname as dirname2, basename as basename2 } from "path";
7499
7641
  function completeFilePath(partial) {
7500
7642
  const cwd = process.cwd();
7501
7643
  const hasDir = partial.includes("/");
7502
- const searchDir = hasDir ? join12(cwd, dirname(partial)) : cwd;
7644
+ const searchDir = hasDir ? join14(cwd, dirname2(partial)) : cwd;
7503
7645
  const prefix = hasDir ? basename2(partial) : partial;
7504
- if (!existsSync12(searchDir)) {
7646
+ if (!existsSync14(searchDir)) {
7505
7647
  return [];
7506
7648
  }
7507
7649
  const entries = readdirSync7(searchDir);
7508
7650
  const results = [];
7509
- const dirPrefix = hasDir ? dirname(partial) + "/" : "";
7651
+ const dirPrefix = hasDir ? dirname2(partial) + "/" : "";
7510
7652
  for (const entry of entries) {
7511
7653
  if (!entry.startsWith(prefix)) continue;
7512
7654
  if (entry.startsWith(".")) continue;
7513
- const fullPath = join12(searchDir, entry);
7655
+ const fullPath = join14(searchDir, entry);
7514
7656
  try {
7515
7657
  const stat = statSync5(fullPath);
7516
7658
  if (stat.isDirectory()) {
@@ -7599,15 +7741,15 @@ function generateCompletions(program2, args) {
7599
7741
 
7600
7742
  // src/commands/completion.ts
7601
7743
  function appendToFile(filePath, content) {
7602
- if (existsSync13(filePath)) {
7603
- const current = readFileSync6(filePath, "utf-8");
7744
+ if (existsSync15(filePath)) {
7745
+ const current = readFileSync7(filePath, "utf-8");
7604
7746
  if (current.includes("clawt completion")) {
7605
7747
  printInfo(MESSAGES.COMPLETION_INSTALL_EXISTS + ": " + filePath);
7606
7748
  return;
7607
7749
  }
7608
7750
  content = current + content;
7609
7751
  }
7610
- writeFileSync5(filePath, content, "utf-8");
7752
+ writeFileSync6(filePath, content, "utf-8");
7611
7753
  printSuccess(MESSAGES.COMPLETION_INSTALL_SUCCESS + ": " + filePath);
7612
7754
  printInfo(MESSAGES.COMPLETION_INSTALL_RESTART(filePath));
7613
7755
  }
@@ -7739,23 +7881,23 @@ async function handleHome() {
7739
7881
  }
7740
7882
 
7741
7883
  // src/commands/tasks.ts
7742
- import { resolve as resolve4, dirname as dirname2, join as join13 } from "path";
7743
- import { existsSync as existsSync14, writeFileSync as writeFileSync6 } from "fs";
7884
+ import { resolve as resolve4, dirname as dirname3, join as join15 } from "path";
7885
+ import { existsSync as existsSync16, writeFileSync as writeFileSync7 } from "fs";
7744
7886
  function registerTasksCommand(program2) {
7745
7887
  const taskCmd = program2.command("tasks").description(getCurrentLanguage() === "en" ? "Task file management" : "\u4EFB\u52A1\u6587\u4EF6\u7BA1\u7406");
7746
7888
  taskCmd.command("init").description(getCurrentLanguage() === "en" ? "Generate task template file" : "\u751F\u6210\u4EFB\u52A1\u6A21\u677F\u6587\u4EF6").argument("[path]", getCurrentLanguage() === "en" ? "Output file path" : "\u8F93\u51FA\u6587\u4EF6\u8DEF\u5F84").action(async (path2) => {
7747
- const filePath = path2 ?? join13(TASK_TEMPLATE_OUTPUT_DIR, generateTaskFilename(TASK_TEMPLATE_FILENAME_PREFIX));
7889
+ const filePath = path2 ?? join15(TASK_TEMPLATE_OUTPUT_DIR, generateTaskFilename(TASK_TEMPLATE_FILENAME_PREFIX));
7748
7890
  await handleTasksInit(filePath);
7749
7891
  });
7750
7892
  }
7751
7893
  async function handleTasksInit(filePath) {
7752
7894
  const absolutePath = resolve4(filePath);
7753
7895
  logger.info(`tasks init \u547D\u4EE4\u6267\u884C\uFF0C\u76EE\u6807\u6587\u4EF6: ${absolutePath}`);
7754
- if (existsSync14(absolutePath)) {
7896
+ if (existsSync16(absolutePath)) {
7755
7897
  throw new ClawtError(MESSAGES.TASK_INIT_FILE_EXISTS(filePath));
7756
7898
  }
7757
- ensureDir(dirname2(absolutePath));
7758
- writeFileSync6(absolutePath, getTaskTemplateContent(), "utf-8");
7899
+ ensureDir(dirname3(absolutePath));
7900
+ writeFileSync7(absolutePath, getTaskTemplateContent(), "utf-8");
7759
7901
  printSuccess(MESSAGES.TASK_INIT_SUCCESS(filePath));
7760
7902
  printHint(MESSAGES.TASK_INIT_HINT(filePath));
7761
7903
  }