clawt 3.10.5 → 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.
- package/AGENTS.md +16 -0
- package/dist/index.js +163 -89
- package/docs/create.md +1 -0
- package/docs/list.md +21 -10
- package/docs/merge.md +1 -0
- package/docs/remove.md +2 -0
- package/docs/spec.md +4 -1
- package/docs/status.md +9 -1
- package/docs/superpowers/findings/2026-06-09-worktree-base-branch-findings.md +58 -0
- package/docs/superpowers/plans/2026-06-09-worktree-base-branch.md +386 -0
- package/docs/superpowers/specs/2026-06-09-worktree-base-branch-design.md +169 -0
- package/package.json +1 -1
- package/src/commands/list.ts +5 -3
- package/src/commands/merge.ts +1 -1
- package/src/commands/remove.ts +3 -0
- package/src/commands/status.ts +5 -0
- package/src/types/status.ts +2 -0
- package/src/types/worktree.ts +12 -0
- package/src/utils/formatter.ts +22 -0
- package/src/utils/index.ts +2 -1
- package/src/utils/interactive-panel-render.ts +6 -3
- package/src/utils/worktree-metadata.ts +82 -0
- package/src/utils/worktree.ts +29 -10
- package/tests/helpers/fixtures.ts +1 -0
- package/tests/unit/commands/cover-validate.test.ts +4 -4
- package/tests/unit/commands/create.test.ts +3 -3
- package/tests/unit/commands/list.test.ts +66 -3
- package/tests/unit/commands/merge.test.ts +1 -1
- package/tests/unit/commands/remove.test.ts +24 -18
- package/tests/unit/commands/resume.test.ts +21 -21
- package/tests/unit/commands/run.test.ts +17 -17
- package/tests/unit/commands/status.test.ts +85 -10
- package/tests/unit/commands/sync.test.ts +4 -4
- package/tests/unit/commands/validate.test.ts +1 -1
- package/tests/unit/utils/interactive-panel-render.test.ts +124 -0
- package/tests/unit/utils/worktree-matcher.test.ts +2 -2
- package/tests/unit/utils/worktree-metadata.test.ts +91 -0
- package/tests/unit/utils/worktree.test.ts +65 -0
package/AGENTS.md
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# Project Memory
|
|
2
|
+
|
|
3
|
+
## 代码阅读规范(必须严格遵守的三步阅读流程)
|
|
4
|
+
|
|
5
|
+
每次接到任务时,**必须**按以下顺序渐进式地理解项目,禁止跳步:
|
|
6
|
+
|
|
7
|
+
1. **第一步:阅读 `docs/spec.md`** — 全面了解项目整体架构、核心概念与模块划分,建立全局认知。
|
|
8
|
+
2. **第二步:根据用户问题,阅读 `docs/` 下对应模块的详细文档** — 例如用户问 merge 相关功能,则阅读 `docs/merge.md`;问 config 相关则阅读 `docs/config.md` 和 `docs/config-file.md`,以此类推。
|
|
9
|
+
3. **第三步:阅读具体的源代码** — 在前两步建立充分上下文后,再深入代码实现,确保方案设计不会因阅读代码不全面而产生缺陷。
|
|
10
|
+
|
|
11
|
+
> **重要提示:文档可能滞后于代码。当文档描述与实际代码实现存在冲突时,以代码为准。**
|
|
12
|
+
|
|
13
|
+
## 编码规范
|
|
14
|
+
|
|
15
|
+
- JSON 序列化必须使用项目封装的 `safeStringify`(位于 `src/utils/json.ts`),禁止直接使用原生 `JSON.stringify`。`safeStringify` 已通过 `src/utils/index.js` 统一导出。
|
|
16
|
+
- 构造输出对象时,使用 `{ ...obj }` 解构展开而非逐字段列举,避免源类型新增字段后遗漏。
|
package/dist/index.js
CHANGED
|
@@ -3067,6 +3067,16 @@ function formatLocalISOString(date) {
|
|
|
3067
3067
|
const minutes = String(absMinutes % 60).padStart(2, "0");
|
|
3068
3068
|
return `${iso}${sign}${hours}:${minutes}`;
|
|
3069
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
|
+
}
|
|
3070
3080
|
function generateTaskFilename(prefix) {
|
|
3071
3081
|
const now = /* @__PURE__ */ new Date();
|
|
3072
3082
|
const pad = (n) => String(n).padStart(2, "0");
|
|
@@ -3395,45 +3405,100 @@ async function runPreChecks(options) {
|
|
|
3395
3405
|
}
|
|
3396
3406
|
|
|
3397
3407
|
// src/utils/worktree.ts
|
|
3398
|
-
import { join as
|
|
3399
|
-
import { existsSync as
|
|
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
|
|
3400
3458
|
function getProjectWorktreeDir() {
|
|
3401
3459
|
const projectName = getProjectName();
|
|
3402
|
-
return
|
|
3460
|
+
return join6(WORKTREES_DIR, projectName);
|
|
3403
3461
|
}
|
|
3404
3462
|
function createWorktrees(branchName, count) {
|
|
3405
3463
|
const sanitized = sanitizeBranchName(branchName);
|
|
3406
3464
|
const branchNames = generateBranchNames(sanitized, count);
|
|
3407
3465
|
validateBranchesNotExist(branchNames);
|
|
3408
|
-
const
|
|
3466
|
+
const projectName = getProjectName();
|
|
3467
|
+
const projectDir = join6(WORKTREES_DIR, projectName);
|
|
3409
3468
|
ensureDir(projectDir);
|
|
3469
|
+
const baseBranch = getCurrentBranch();
|
|
3410
3470
|
const results = [];
|
|
3411
3471
|
for (const name of branchNames) {
|
|
3412
|
-
const worktreePath =
|
|
3472
|
+
const worktreePath = join6(projectDir, name);
|
|
3413
3473
|
createWorktree(name, worktreePath);
|
|
3414
3474
|
createValidateBranch(name);
|
|
3415
|
-
|
|
3475
|
+
saveWorktreeMetadata(projectName, { branch: name, baseBranch, createdAt: (/* @__PURE__ */ new Date()).toISOString() });
|
|
3476
|
+
results.push({ path: worktreePath, branch: name, baseBranch });
|
|
3416
3477
|
logger.info(`worktree \u521B\u5EFA\u5B8C\u6210: ${worktreePath} (\u5206\u652F: ${name})`);
|
|
3417
3478
|
}
|
|
3418
3479
|
return results;
|
|
3419
3480
|
}
|
|
3420
3481
|
function createWorktreesByBranches(branchNames) {
|
|
3421
3482
|
validateBranchesNotExist(branchNames);
|
|
3422
|
-
const
|
|
3483
|
+
const projectName = getProjectName();
|
|
3484
|
+
const projectDir = join6(WORKTREES_DIR, projectName);
|
|
3423
3485
|
ensureDir(projectDir);
|
|
3486
|
+
const baseBranch = getCurrentBranch();
|
|
3424
3487
|
const results = [];
|
|
3425
3488
|
for (const name of branchNames) {
|
|
3426
|
-
const worktreePath =
|
|
3489
|
+
const worktreePath = join6(projectDir, name);
|
|
3427
3490
|
createWorktree(name, worktreePath);
|
|
3428
3491
|
createValidateBranch(name);
|
|
3429
|
-
|
|
3492
|
+
saveWorktreeMetadata(projectName, { branch: name, baseBranch, createdAt: (/* @__PURE__ */ new Date()).toISOString() });
|
|
3493
|
+
results.push({ path: worktreePath, branch: name, baseBranch });
|
|
3430
3494
|
logger.info(`worktree \u521B\u5EFA\u5B8C\u6210: ${worktreePath} (\u5206\u652F: ${name})`);
|
|
3431
3495
|
}
|
|
3432
3496
|
return results;
|
|
3433
3497
|
}
|
|
3434
3498
|
function getProjectWorktrees() {
|
|
3435
|
-
const
|
|
3436
|
-
|
|
3499
|
+
const projectName = getProjectName();
|
|
3500
|
+
const projectDir = join6(WORKTREES_DIR, projectName);
|
|
3501
|
+
if (!existsSync6(projectDir)) {
|
|
3437
3502
|
return [];
|
|
3438
3503
|
}
|
|
3439
3504
|
const worktreeListOutput = gitWorktreeList();
|
|
@@ -3446,29 +3511,33 @@ function getProjectWorktrees() {
|
|
|
3446
3511
|
if (!entry.isDirectory()) {
|
|
3447
3512
|
continue;
|
|
3448
3513
|
}
|
|
3449
|
-
const fullPath =
|
|
3514
|
+
const fullPath = join6(projectDir, entry.name);
|
|
3450
3515
|
if (registeredPaths.has(fullPath)) {
|
|
3516
|
+
const metadata = loadWorktreeMetadata(projectName, entry.name);
|
|
3451
3517
|
worktrees.push({
|
|
3452
3518
|
path: fullPath,
|
|
3453
|
-
branch: entry.name
|
|
3519
|
+
branch: entry.name,
|
|
3520
|
+
baseBranch: metadata?.baseBranch ?? null
|
|
3454
3521
|
});
|
|
3455
3522
|
}
|
|
3456
3523
|
}
|
|
3457
3524
|
return worktrees;
|
|
3458
3525
|
}
|
|
3459
3526
|
function cleanupWorktrees(worktrees) {
|
|
3527
|
+
const projectName = getProjectName();
|
|
3460
3528
|
for (const wt of worktrees) {
|
|
3461
3529
|
try {
|
|
3462
3530
|
removeWorktreeByPath(wt.path);
|
|
3463
3531
|
deleteBranch(wt.branch);
|
|
3464
3532
|
deleteValidateBranch(wt.branch);
|
|
3533
|
+
removeWorktreeMetadata(projectName, wt.branch);
|
|
3465
3534
|
logger.info(`\u5DF2\u6E05\u7406 worktree \u548C\u5206\u652F: ${wt.branch}`);
|
|
3466
3535
|
} catch (error) {
|
|
3467
3536
|
logger.error(`\u6E05\u7406 worktree \u5931\u8D25: ${wt.path} - ${error}`);
|
|
3468
3537
|
}
|
|
3469
3538
|
}
|
|
3470
3539
|
gitWorktreePrune();
|
|
3471
|
-
const projectDir =
|
|
3540
|
+
const projectDir = join6(WORKTREES_DIR, projectName);
|
|
3472
3541
|
removeEmptyDir(projectDir);
|
|
3473
3542
|
}
|
|
3474
3543
|
function getWorktreeStatus(worktree) {
|
|
@@ -3485,7 +3554,7 @@ function getWorktreeStatus(worktree) {
|
|
|
3485
3554
|
|
|
3486
3555
|
// src/utils/symlink-guard.ts
|
|
3487
3556
|
import { readdirSync as readdirSync3, lstatSync, unlinkSync, readlinkSync } from "fs";
|
|
3488
|
-
import { join as
|
|
3557
|
+
import { join as join7, relative, isAbsolute as isAbsolute2, resolve } from "path";
|
|
3489
3558
|
function isExternalSymlink(linkPath, worktreeRoot) {
|
|
3490
3559
|
try {
|
|
3491
3560
|
const target = readlinkSync(linkPath);
|
|
@@ -3502,7 +3571,7 @@ function removeExternalSymlinks(dir) {
|
|
|
3502
3571
|
const entries = readdirSync3(dir, { withFileTypes: true });
|
|
3503
3572
|
for (const entry of entries) {
|
|
3504
3573
|
if (!entry.isSymbolicLink()) continue;
|
|
3505
|
-
const fullPath =
|
|
3574
|
+
const fullPath = join7(dir, entry.name);
|
|
3506
3575
|
if (!isExternalSymlink(fullPath, dir)) continue;
|
|
3507
3576
|
try {
|
|
3508
3577
|
const stat = lstatSync(fullPath);
|
|
@@ -3537,14 +3606,14 @@ async function promptCommitMessage(promptMessage, nonInteractiveErrorMessage) {
|
|
|
3537
3606
|
|
|
3538
3607
|
// src/utils/claude.ts
|
|
3539
3608
|
import { spawnSync as spawnSync3 } from "child_process";
|
|
3540
|
-
import { existsSync as
|
|
3541
|
-
import { join as
|
|
3609
|
+
import { existsSync as existsSync8, readdirSync as readdirSync4 } from "fs";
|
|
3610
|
+
import { join as join8 } from "path";
|
|
3542
3611
|
|
|
3543
3612
|
// src/utils/terminal.ts
|
|
3544
3613
|
import { execFileSync as execFileSync3 } from "child_process";
|
|
3545
|
-
import { existsSync as
|
|
3614
|
+
import { existsSync as existsSync7 } from "fs";
|
|
3546
3615
|
function isITerm2Installed() {
|
|
3547
|
-
return
|
|
3616
|
+
return existsSync7(ITERM2_APP_PATH);
|
|
3548
3617
|
}
|
|
3549
3618
|
function isCmuxEnvironment() {
|
|
3550
3619
|
return !!process.env.CMUX_WORKSPACE_ID;
|
|
@@ -3682,8 +3751,8 @@ function encodeClaudeProjectPath(absolutePath) {
|
|
|
3682
3751
|
}
|
|
3683
3752
|
function hasClaudeSessionHistory(worktreePath) {
|
|
3684
3753
|
const encodedName = encodeClaudeProjectPath(worktreePath);
|
|
3685
|
-
const projectDir =
|
|
3686
|
-
if (!
|
|
3754
|
+
const projectDir = join8(CLAUDE_PROJECTS_DIR, encodedName);
|
|
3755
|
+
if (!existsSync8(projectDir)) {
|
|
3687
3756
|
return false;
|
|
3688
3757
|
}
|
|
3689
3758
|
const entries = readdirSync4(projectDir);
|
|
@@ -3736,23 +3805,23 @@ function launchInteractiveClaudeInNewTerminal(worktree, hasPreviousSession) {
|
|
|
3736
3805
|
}
|
|
3737
3806
|
|
|
3738
3807
|
// src/utils/validate-snapshot.ts
|
|
3739
|
-
import { join as
|
|
3740
|
-
import { existsSync as
|
|
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";
|
|
3741
3810
|
function getSnapshotPath(projectName, branchName) {
|
|
3742
|
-
return
|
|
3811
|
+
return join9(VALIDATE_SNAPSHOTS_DIR, projectName, `${branchName}.tree`);
|
|
3743
3812
|
}
|
|
3744
3813
|
function getSnapshotHeadPath(projectName, branchName) {
|
|
3745
|
-
return
|
|
3814
|
+
return join9(VALIDATE_SNAPSHOTS_DIR, projectName, `${branchName}.head`);
|
|
3746
3815
|
}
|
|
3747
3816
|
function getSnapshotStagedPath(projectName, branchName) {
|
|
3748
|
-
return
|
|
3817
|
+
return join9(VALIDATE_SNAPSHOTS_DIR, projectName, `${branchName}.staged`);
|
|
3749
3818
|
}
|
|
3750
3819
|
function hasSnapshot(projectName, branchName) {
|
|
3751
|
-
return
|
|
3820
|
+
return existsSync9(getSnapshotPath(projectName, branchName));
|
|
3752
3821
|
}
|
|
3753
3822
|
function getSnapshotModifiedTime(projectName, branchName) {
|
|
3754
3823
|
const snapshotPath = getSnapshotPath(projectName, branchName);
|
|
3755
|
-
if (!
|
|
3824
|
+
if (!existsSync9(snapshotPath)) return null;
|
|
3756
3825
|
const stat = statSync2(snapshotPath);
|
|
3757
3826
|
return stat.mtime.toISOString();
|
|
3758
3827
|
}
|
|
@@ -3761,22 +3830,22 @@ function readSnapshot(projectName, branchName) {
|
|
|
3761
3830
|
const headPath = getSnapshotHeadPath(projectName, branchName);
|
|
3762
3831
|
const stagedPath = getSnapshotStagedPath(projectName, branchName);
|
|
3763
3832
|
logger.debug(`\u8BFB\u53D6 validate \u5FEB\u7167: ${snapshotPath}`);
|
|
3764
|
-
const treeHash =
|
|
3765
|
-
const headCommitHash =
|
|
3766
|
-
const stagedTreeHash =
|
|
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() : "";
|
|
3767
3836
|
return { treeHash, headCommitHash, stagedTreeHash };
|
|
3768
3837
|
}
|
|
3769
3838
|
function writeSnapshot(projectName, branchName, treeHash, headCommitHash, stagedTreeHash) {
|
|
3770
|
-
const snapshotDir =
|
|
3839
|
+
const snapshotDir = join9(VALIDATE_SNAPSHOTS_DIR, projectName);
|
|
3771
3840
|
ensureDir(snapshotDir);
|
|
3772
3841
|
if (treeHash !== void 0) {
|
|
3773
|
-
|
|
3842
|
+
writeFileSync4(getSnapshotPath(projectName, branchName), treeHash, "utf-8");
|
|
3774
3843
|
}
|
|
3775
3844
|
if (headCommitHash !== void 0) {
|
|
3776
|
-
|
|
3845
|
+
writeFileSync4(getSnapshotHeadPath(projectName, branchName), headCommitHash, "utf-8");
|
|
3777
3846
|
}
|
|
3778
3847
|
if (stagedTreeHash !== void 0) {
|
|
3779
|
-
|
|
3848
|
+
writeFileSync4(getSnapshotStagedPath(projectName, branchName), stagedTreeHash, "utf-8");
|
|
3780
3849
|
}
|
|
3781
3850
|
logger.info(`\u5DF2\u5199\u5165 validate \u5FEB\u7167 (project=${projectName}, branch=${branchName})`);
|
|
3782
3851
|
}
|
|
@@ -3784,35 +3853,35 @@ function removeSnapshot(projectName, branchName) {
|
|
|
3784
3853
|
const snapshotPath = getSnapshotPath(projectName, branchName);
|
|
3785
3854
|
const headPath = getSnapshotHeadPath(projectName, branchName);
|
|
3786
3855
|
const stagedPath = getSnapshotStagedPath(projectName, branchName);
|
|
3787
|
-
if (
|
|
3856
|
+
if (existsSync9(snapshotPath)) {
|
|
3788
3857
|
unlinkSync2(snapshotPath);
|
|
3789
3858
|
logger.info(`\u5DF2\u5220\u9664 validate \u5FEB\u7167: ${snapshotPath}`);
|
|
3790
3859
|
}
|
|
3791
|
-
if (
|
|
3860
|
+
if (existsSync9(headPath)) {
|
|
3792
3861
|
unlinkSync2(headPath);
|
|
3793
3862
|
logger.info(`\u5DF2\u5220\u9664 validate \u5FEB\u7167: ${headPath}`);
|
|
3794
3863
|
}
|
|
3795
|
-
if (
|
|
3864
|
+
if (existsSync9(stagedPath)) {
|
|
3796
3865
|
unlinkSync2(stagedPath);
|
|
3797
3866
|
logger.info(`\u5DF2\u5220\u9664 validate \u5FEB\u7167: ${stagedPath}`);
|
|
3798
3867
|
}
|
|
3799
3868
|
}
|
|
3800
3869
|
function getProjectSnapshotBranches(projectName) {
|
|
3801
|
-
const projectDir =
|
|
3802
|
-
if (!
|
|
3870
|
+
const projectDir = join9(VALIDATE_SNAPSHOTS_DIR, projectName);
|
|
3871
|
+
if (!existsSync9(projectDir)) {
|
|
3803
3872
|
return [];
|
|
3804
3873
|
}
|
|
3805
3874
|
const files = readdirSync5(projectDir);
|
|
3806
3875
|
return files.filter((f) => f.endsWith(".tree")).map((f) => f.replace(/\.tree$/, ""));
|
|
3807
3876
|
}
|
|
3808
3877
|
function removeProjectSnapshots(projectName) {
|
|
3809
|
-
const projectDir =
|
|
3810
|
-
if (!
|
|
3878
|
+
const projectDir = join9(VALIDATE_SNAPSHOTS_DIR, projectName);
|
|
3879
|
+
if (!existsSync9(projectDir)) {
|
|
3811
3880
|
return;
|
|
3812
3881
|
}
|
|
3813
3882
|
const files = readdirSync5(projectDir);
|
|
3814
3883
|
for (const file of files) {
|
|
3815
|
-
unlinkSync2(
|
|
3884
|
+
unlinkSync2(join9(projectDir, file));
|
|
3816
3885
|
}
|
|
3817
3886
|
try {
|
|
3818
3887
|
rmdirSync2(projectDir);
|
|
@@ -4343,7 +4412,7 @@ var ProgressRenderer = class {
|
|
|
4343
4412
|
|
|
4344
4413
|
// src/utils/task-file.ts
|
|
4345
4414
|
import { resolve as resolve2 } from "path";
|
|
4346
|
-
import { existsSync as
|
|
4415
|
+
import { existsSync as existsSync10, readFileSync as readFileSync5 } from "fs";
|
|
4347
4416
|
var TASK_BLOCK_REGEX = /<!-- CLAWT-TASKS:START -->([\s\S]*?)<!-- CLAWT-TASKS:END -->/g;
|
|
4348
4417
|
var BRANCH_LINE_REGEX = /^#\s*branch:\s*(.+)$/;
|
|
4349
4418
|
var EMPTY_TASKS_MESSAGE = getCurrentLanguage() === "en" ? "Task list cannot be empty" : "\u4EFB\u52A1\u5217\u8868\u4E0D\u80FD\u4E3A\u7A7A";
|
|
@@ -4389,10 +4458,10 @@ function parseTaskFile(content, options) {
|
|
|
4389
4458
|
}
|
|
4390
4459
|
function loadTaskFile(filePath, options) {
|
|
4391
4460
|
const absolutePath = resolve2(filePath);
|
|
4392
|
-
if (!
|
|
4461
|
+
if (!existsSync10(absolutePath)) {
|
|
4393
4462
|
throw new ClawtError(MESSAGES.TASK_FILE_NOT_FOUND(absolutePath));
|
|
4394
4463
|
}
|
|
4395
|
-
const content =
|
|
4464
|
+
const content = readFileSync5(absolutePath, "utf-8");
|
|
4396
4465
|
const entries = parseTaskFile(content, options);
|
|
4397
4466
|
if (entries.length === 0) {
|
|
4398
4467
|
throw new ClawtError(MESSAGES.TASK_FILE_EMPTY);
|
|
@@ -4737,7 +4806,7 @@ async function executeBatchTasks(worktrees, tasks, concurrency, continueFlags) {
|
|
|
4737
4806
|
|
|
4738
4807
|
// src/utils/dry-run.ts
|
|
4739
4808
|
import chalk6 from "chalk";
|
|
4740
|
-
import { join as
|
|
4809
|
+
import { join as join10 } from "path";
|
|
4741
4810
|
var DRY_RUN_TASK_DESC_MAX_LENGTH = 80;
|
|
4742
4811
|
function truncateTaskDesc(task) {
|
|
4743
4812
|
const oneLine = task.replace(/\n/g, " ").trim();
|
|
@@ -4765,7 +4834,7 @@ function printDryRunPreview(branchNames, tasks, concurrency) {
|
|
|
4765
4834
|
let hasConflict = false;
|
|
4766
4835
|
for (let i = 0; i < branchNames.length; i++) {
|
|
4767
4836
|
const branch = branchNames[i];
|
|
4768
|
-
const worktreePath =
|
|
4837
|
+
const worktreePath = join10(projectDir, branch);
|
|
4769
4838
|
const exists = checkBranchExists(branch);
|
|
4770
4839
|
if (exists) hasConflict = true;
|
|
4771
4840
|
const indexLabel = `[${i + 1}/${branchNames.length}]`;
|
|
@@ -4920,14 +4989,14 @@ async function promptStringValue(key, currentValue) {
|
|
|
4920
4989
|
}
|
|
4921
4990
|
|
|
4922
4991
|
// src/utils/update-checker.ts
|
|
4923
|
-
import { readFileSync as
|
|
4992
|
+
import { readFileSync as readFileSync6, writeFileSync as writeFileSync5 } from "fs";
|
|
4924
4993
|
import { execSync as execSync4 } from "child_process";
|
|
4925
4994
|
import { request } from "https";
|
|
4926
4995
|
import chalk8 from "chalk";
|
|
4927
4996
|
import stringWidth2 from "string-width";
|
|
4928
4997
|
function readUpdateCache() {
|
|
4929
4998
|
try {
|
|
4930
|
-
const raw =
|
|
4999
|
+
const raw = readFileSync6(UPDATE_CHECK_PATH, "utf-8");
|
|
4931
5000
|
return JSON.parse(raw);
|
|
4932
5001
|
} catch {
|
|
4933
5002
|
return null;
|
|
@@ -4935,7 +5004,7 @@ function readUpdateCache() {
|
|
|
4935
5004
|
}
|
|
4936
5005
|
function writeUpdateCache(cache) {
|
|
4937
5006
|
try {
|
|
4938
|
-
|
|
5007
|
+
writeFileSync5(UPDATE_CHECK_PATH, JSON.stringify(cache, null, 2), "utf-8");
|
|
4939
5008
|
} catch {
|
|
4940
5009
|
}
|
|
4941
5010
|
}
|
|
@@ -5133,8 +5202,8 @@ async function executeRunCommand(command, mainWorktreePath) {
|
|
|
5133
5202
|
}
|
|
5134
5203
|
|
|
5135
5204
|
// src/utils/validate-core.ts
|
|
5136
|
-
import { existsSync as
|
|
5137
|
-
import { join as
|
|
5205
|
+
import { existsSync as existsSync11 } from "fs";
|
|
5206
|
+
import { join as join11 } from "path";
|
|
5138
5207
|
function buildCleanCommands(files) {
|
|
5139
5208
|
const dirs = /* @__PURE__ */ new Set();
|
|
5140
5209
|
for (const file of files) {
|
|
@@ -5238,7 +5307,7 @@ function detectIgnoredFilesInPatch(branchName, mainWorktreePath) {
|
|
|
5238
5307
|
const output = execCommand(`git diff --name-only HEAD...${branchName}`, { cwd: mainWorktreePath });
|
|
5239
5308
|
const patchFiles = output.split("\n").filter(Boolean);
|
|
5240
5309
|
if (patchFiles.length === 0) return [];
|
|
5241
|
-
return gitCheckIgnored(patchFiles, mainWorktreePath).filter((file) =>
|
|
5310
|
+
return gitCheckIgnored(patchFiles, mainWorktreePath).filter((file) => existsSync11(join11(mainWorktreePath, file)));
|
|
5242
5311
|
} catch {
|
|
5243
5312
|
return [];
|
|
5244
5313
|
}
|
|
@@ -5286,7 +5355,7 @@ function buildPanelFrame(statusResult, selectedIndex, scrollOffset, rows, cols,
|
|
|
5286
5355
|
return lines;
|
|
5287
5356
|
}
|
|
5288
5357
|
function buildDisplayOrder(worktrees) {
|
|
5289
|
-
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 }));
|
|
5290
5359
|
const groups = groupWorktreesByDate(worktreeInfos);
|
|
5291
5360
|
const branchIndexMap = /* @__PURE__ */ new Map();
|
|
5292
5361
|
for (let i = 0; i < worktrees.length; i++) {
|
|
@@ -5302,7 +5371,7 @@ function buildDisplayOrder(worktrees) {
|
|
|
5302
5371
|
}
|
|
5303
5372
|
function buildGroupedWorktreeLines(worktrees, selectedIndex) {
|
|
5304
5373
|
const panelLines = [];
|
|
5305
|
-
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 }));
|
|
5306
5375
|
const groups = groupWorktreesByDate(worktreeInfos);
|
|
5307
5376
|
const branchIndexMap = /* @__PURE__ */ new Map();
|
|
5308
5377
|
for (let i = 0; i < worktrees.length; i++) {
|
|
@@ -5360,6 +5429,7 @@ function renderWorktreeBlock(wt, isSelected) {
|
|
|
5360
5429
|
} else {
|
|
5361
5430
|
lines.push(`${indent}${chalk9.green(PANEL_SYNCED_WITH_MAIN)}`);
|
|
5362
5431
|
}
|
|
5432
|
+
lines.push(`${indent}${chalk9.gray(formatBaseBranchLine(wt.baseBranch))}`);
|
|
5363
5433
|
if (wt.createdAt) {
|
|
5364
5434
|
const relativeTime = formatRelativeTime(wt.createdAt);
|
|
5365
5435
|
if (relativeTime) {
|
|
@@ -6035,9 +6105,9 @@ async function handleMergeConflict(currentBranch, incomingBranch, cwd, autoFlag)
|
|
|
6035
6105
|
}
|
|
6036
6106
|
|
|
6037
6107
|
// src/hooks/post-create.ts
|
|
6038
|
-
import { existsSync as
|
|
6108
|
+
import { existsSync as existsSync12, accessSync, chmodSync, constants as fsConstants } from "fs";
|
|
6039
6109
|
import { spawn as spawn2 } from "child_process";
|
|
6040
|
-
import { join as
|
|
6110
|
+
import { join as join12 } from "path";
|
|
6041
6111
|
var POST_CREATE_SCRIPT_RELATIVE_PATH = ".clawt/postCreate.sh";
|
|
6042
6112
|
function isExecutable(filePath) {
|
|
6043
6113
|
try {
|
|
@@ -6069,8 +6139,8 @@ function resolvePostCreateHook() {
|
|
|
6069
6139
|
}
|
|
6070
6140
|
}
|
|
6071
6141
|
const mainWorktreePath = getMainWorktreePath();
|
|
6072
|
-
const scriptPath =
|
|
6073
|
-
if (
|
|
6142
|
+
const scriptPath = join12(mainWorktreePath, POST_CREATE_SCRIPT_RELATIVE_PATH);
|
|
6143
|
+
if (existsSync12(scriptPath)) {
|
|
6074
6144
|
if (!isExecutable(scriptPath)) {
|
|
6075
6145
|
autoFixExecutablePermission(scriptPath);
|
|
6076
6146
|
}
|
|
@@ -6169,7 +6239,8 @@ function printListAsJson(projectName, worktrees) {
|
|
|
6169
6239
|
total: worktrees.length,
|
|
6170
6240
|
worktrees: worktrees.map((wt) => ({
|
|
6171
6241
|
path: wt.path,
|
|
6172
|
-
branch: wt.branch
|
|
6242
|
+
branch: wt.branch,
|
|
6243
|
+
baseBranch: wt.baseBranch
|
|
6173
6244
|
}))
|
|
6174
6245
|
};
|
|
6175
6246
|
console.log(JSON.stringify(result, null, 2));
|
|
@@ -6185,7 +6256,7 @@ function printListAsText(projectName, worktrees) {
|
|
|
6185
6256
|
const status = getWorktreeStatus(wt);
|
|
6186
6257
|
const isIdle = status ? isWorktreeIdle(status) : false;
|
|
6187
6258
|
const pathDisplay = isIdle ? chalk10.hex("#FF8C00")(wt.path) : wt.path;
|
|
6188
|
-
printInfo(` ${pathDisplay} [${wt.branch}]`);
|
|
6259
|
+
printInfo(` ${pathDisplay} [${wt.branch}] ${formatBaseBranchInline(wt.baseBranch)}`);
|
|
6189
6260
|
if (status) {
|
|
6190
6261
|
printInfo(` ${formatWorktreeStatus(status)}`);
|
|
6191
6262
|
} else {
|
|
@@ -6285,6 +6356,7 @@ async function handleRemove(options) {
|
|
|
6285
6356
|
}
|
|
6286
6357
|
deleteValidateBranch(wt.branch);
|
|
6287
6358
|
removeSnapshot(projectName, wt.branch);
|
|
6359
|
+
removeWorktreeMetadata(projectName, wt.branch);
|
|
6288
6360
|
printSuccess(MESSAGES.WORKTREE_REMOVED(wt.path));
|
|
6289
6361
|
} catch (error) {
|
|
6290
6362
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
@@ -6825,7 +6897,7 @@ async function shouldCleanupAfterMerge(branchName) {
|
|
|
6825
6897
|
return confirmAction(MESSAGES.CONFIRM_DELETE_WORKTREE_BRANCH(branchName), true);
|
|
6826
6898
|
}
|
|
6827
6899
|
function cleanupWorktreeAndBranch(worktreePath, branchName) {
|
|
6828
|
-
cleanupWorktrees([{ path: worktreePath, branch: branchName }]);
|
|
6900
|
+
cleanupWorktrees([{ path: worktreePath, branch: branchName, baseBranch: null }]);
|
|
6829
6901
|
printSuccess(MESSAGES.WORKTREE_CLEANED(branchName));
|
|
6830
6902
|
}
|
|
6831
6903
|
async function handleMerge(options) {
|
|
@@ -7100,7 +7172,8 @@ async function collectWorktreeDetailedStatusAsync(worktree, projectName) {
|
|
|
7100
7172
|
snapshotTime: resolveSnapshotTime(projectName, worktree.branch),
|
|
7101
7173
|
insertions: diffStat.insertions,
|
|
7102
7174
|
deletions: diffStat.deletions,
|
|
7103
|
-
createdAt
|
|
7175
|
+
createdAt,
|
|
7176
|
+
baseBranch: worktree.baseBranch
|
|
7104
7177
|
};
|
|
7105
7178
|
}
|
|
7106
7179
|
function detectChangeStatusFromPorcelain(porcelain, commitsAhead) {
|
|
@@ -7225,6 +7298,7 @@ function printWorktreeItem(wt) {
|
|
|
7225
7298
|
} else {
|
|
7226
7299
|
printInfo(` ${chalk11.green(lang === "en" ? "In sync with main" : "\u4E0E\u4E3B\u5206\u652F\u540C\u6B65")}`);
|
|
7227
7300
|
}
|
|
7301
|
+
printInfo(` ${chalk11.gray(formatBaseBranchLine(wt.baseBranch))}`);
|
|
7228
7302
|
if (wt.createdAt) {
|
|
7229
7303
|
const relativeTime = formatRelativeTime(wt.createdAt);
|
|
7230
7304
|
if (relativeTime) {
|
|
@@ -7331,8 +7405,8 @@ function registerAliasCommand(program2) {
|
|
|
7331
7405
|
}
|
|
7332
7406
|
|
|
7333
7407
|
// src/commands/projects.ts
|
|
7334
|
-
import { existsSync as
|
|
7335
|
-
import { join as
|
|
7408
|
+
import { existsSync as existsSync13, readdirSync as readdirSync6, statSync as statSync4 } from "fs";
|
|
7409
|
+
import { join as join13 } from "path";
|
|
7336
7410
|
import chalk13 from "chalk";
|
|
7337
7411
|
function registerProjectsCommand(program2) {
|
|
7338
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) => {
|
|
@@ -7356,8 +7430,8 @@ function handleProjectsOverview(json) {
|
|
|
7356
7430
|
printProjectsOverviewAsText(result);
|
|
7357
7431
|
}
|
|
7358
7432
|
function handleProjectDetail(name, json) {
|
|
7359
|
-
const projectDir =
|
|
7360
|
-
if (!
|
|
7433
|
+
const projectDir = join13(WORKTREES_DIR, name);
|
|
7434
|
+
if (!existsSync13(projectDir)) {
|
|
7361
7435
|
printError(MESSAGES.PROJECTS_NOT_FOUND(name));
|
|
7362
7436
|
process.exit(1);
|
|
7363
7437
|
}
|
|
@@ -7370,7 +7444,7 @@ function handleProjectDetail(name, json) {
|
|
|
7370
7444
|
printProjectDetailAsText(result);
|
|
7371
7445
|
}
|
|
7372
7446
|
function collectProjectsOverview() {
|
|
7373
|
-
if (!
|
|
7447
|
+
if (!existsSync13(WORKTREES_DIR)) {
|
|
7374
7448
|
return { projects: [], totalProjects: 0, totalDiskUsage: 0 };
|
|
7375
7449
|
}
|
|
7376
7450
|
const entries = readdirSync6(WORKTREES_DIR, { withFileTypes: true });
|
|
@@ -7379,7 +7453,7 @@ function collectProjectsOverview() {
|
|
|
7379
7453
|
if (!entry.isDirectory()) {
|
|
7380
7454
|
continue;
|
|
7381
7455
|
}
|
|
7382
|
-
const projectDir =
|
|
7456
|
+
const projectDir = join13(WORKTREES_DIR, entry.name);
|
|
7383
7457
|
const overview = collectSingleProjectOverview(entry.name, projectDir);
|
|
7384
7458
|
projects.push(overview);
|
|
7385
7459
|
}
|
|
@@ -7396,7 +7470,7 @@ function collectSingleProjectOverview(name, projectDir) {
|
|
|
7396
7470
|
const worktreeDirs = subEntries.filter((e) => e.isDirectory());
|
|
7397
7471
|
const worktreeCount = worktreeDirs.length;
|
|
7398
7472
|
const diskUsage = calculateDirSize(projectDir);
|
|
7399
|
-
const lastActiveTime = resolveProjectLastActiveTime(projectDir, worktreeDirs.map((e) =>
|
|
7473
|
+
const lastActiveTime = resolveProjectLastActiveTime(projectDir, worktreeDirs.map((e) => join13(projectDir, e.name)));
|
|
7400
7474
|
return {
|
|
7401
7475
|
name,
|
|
7402
7476
|
worktreeCount,
|
|
@@ -7411,7 +7485,7 @@ function collectProjectDetail(name, projectDir) {
|
|
|
7411
7485
|
if (!entry.isDirectory()) {
|
|
7412
7486
|
continue;
|
|
7413
7487
|
}
|
|
7414
|
-
const wtPath =
|
|
7488
|
+
const wtPath = join13(projectDir, entry.name);
|
|
7415
7489
|
const detail = collectSingleWorktreeDetail(entry.name, wtPath);
|
|
7416
7490
|
worktrees.push(detail);
|
|
7417
7491
|
}
|
|
@@ -7511,7 +7585,7 @@ function printWorktreeDetailItem(wt) {
|
|
|
7511
7585
|
}
|
|
7512
7586
|
|
|
7513
7587
|
// src/commands/completion.ts
|
|
7514
|
-
import { readFileSync as
|
|
7588
|
+
import { readFileSync as readFileSync7, writeFileSync as writeFileSync6, existsSync as existsSync15 } from "fs";
|
|
7515
7589
|
import { resolve as resolve3 } from "path";
|
|
7516
7590
|
import { homedir as homedir2 } from "os";
|
|
7517
7591
|
|
|
@@ -7562,23 +7636,23 @@ compdef _clawt_completion clawt
|
|
|
7562
7636
|
}
|
|
7563
7637
|
|
|
7564
7638
|
// src/utils/completion-engine.ts
|
|
7565
|
-
import { existsSync as
|
|
7566
|
-
import { join as
|
|
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";
|
|
7567
7641
|
function completeFilePath(partial) {
|
|
7568
7642
|
const cwd = process.cwd();
|
|
7569
7643
|
const hasDir = partial.includes("/");
|
|
7570
|
-
const searchDir = hasDir ?
|
|
7644
|
+
const searchDir = hasDir ? join14(cwd, dirname2(partial)) : cwd;
|
|
7571
7645
|
const prefix = hasDir ? basename2(partial) : partial;
|
|
7572
|
-
if (!
|
|
7646
|
+
if (!existsSync14(searchDir)) {
|
|
7573
7647
|
return [];
|
|
7574
7648
|
}
|
|
7575
7649
|
const entries = readdirSync7(searchDir);
|
|
7576
7650
|
const results = [];
|
|
7577
|
-
const dirPrefix = hasDir ?
|
|
7651
|
+
const dirPrefix = hasDir ? dirname2(partial) + "/" : "";
|
|
7578
7652
|
for (const entry of entries) {
|
|
7579
7653
|
if (!entry.startsWith(prefix)) continue;
|
|
7580
7654
|
if (entry.startsWith(".")) continue;
|
|
7581
|
-
const fullPath =
|
|
7655
|
+
const fullPath = join14(searchDir, entry);
|
|
7582
7656
|
try {
|
|
7583
7657
|
const stat = statSync5(fullPath);
|
|
7584
7658
|
if (stat.isDirectory()) {
|
|
@@ -7667,15 +7741,15 @@ function generateCompletions(program2, args) {
|
|
|
7667
7741
|
|
|
7668
7742
|
// src/commands/completion.ts
|
|
7669
7743
|
function appendToFile(filePath, content) {
|
|
7670
|
-
if (
|
|
7671
|
-
const current =
|
|
7744
|
+
if (existsSync15(filePath)) {
|
|
7745
|
+
const current = readFileSync7(filePath, "utf-8");
|
|
7672
7746
|
if (current.includes("clawt completion")) {
|
|
7673
7747
|
printInfo(MESSAGES.COMPLETION_INSTALL_EXISTS + ": " + filePath);
|
|
7674
7748
|
return;
|
|
7675
7749
|
}
|
|
7676
7750
|
content = current + content;
|
|
7677
7751
|
}
|
|
7678
|
-
|
|
7752
|
+
writeFileSync6(filePath, content, "utf-8");
|
|
7679
7753
|
printSuccess(MESSAGES.COMPLETION_INSTALL_SUCCESS + ": " + filePath);
|
|
7680
7754
|
printInfo(MESSAGES.COMPLETION_INSTALL_RESTART(filePath));
|
|
7681
7755
|
}
|
|
@@ -7807,23 +7881,23 @@ async function handleHome() {
|
|
|
7807
7881
|
}
|
|
7808
7882
|
|
|
7809
7883
|
// src/commands/tasks.ts
|
|
7810
|
-
import { resolve as resolve4, dirname as
|
|
7811
|
-
import { existsSync as
|
|
7884
|
+
import { resolve as resolve4, dirname as dirname3, join as join15 } from "path";
|
|
7885
|
+
import { existsSync as existsSync16, writeFileSync as writeFileSync7 } from "fs";
|
|
7812
7886
|
function registerTasksCommand(program2) {
|
|
7813
7887
|
const taskCmd = program2.command("tasks").description(getCurrentLanguage() === "en" ? "Task file management" : "\u4EFB\u52A1\u6587\u4EF6\u7BA1\u7406");
|
|
7814
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) => {
|
|
7815
|
-
const filePath = path2 ??
|
|
7889
|
+
const filePath = path2 ?? join15(TASK_TEMPLATE_OUTPUT_DIR, generateTaskFilename(TASK_TEMPLATE_FILENAME_PREFIX));
|
|
7816
7890
|
await handleTasksInit(filePath);
|
|
7817
7891
|
});
|
|
7818
7892
|
}
|
|
7819
7893
|
async function handleTasksInit(filePath) {
|
|
7820
7894
|
const absolutePath = resolve4(filePath);
|
|
7821
7895
|
logger.info(`tasks init \u547D\u4EE4\u6267\u884C\uFF0C\u76EE\u6807\u6587\u4EF6: ${absolutePath}`);
|
|
7822
|
-
if (
|
|
7896
|
+
if (existsSync16(absolutePath)) {
|
|
7823
7897
|
throw new ClawtError(MESSAGES.TASK_INIT_FILE_EXISTS(filePath));
|
|
7824
7898
|
}
|
|
7825
|
-
ensureDir(
|
|
7826
|
-
|
|
7899
|
+
ensureDir(dirname3(absolutePath));
|
|
7900
|
+
writeFileSync7(absolutePath, getTaskTemplateContent(), "utf-8");
|
|
7827
7901
|
printSuccess(MESSAGES.TASK_INIT_SUCCESS(filePath));
|
|
7828
7902
|
printHint(MESSAGES.TASK_INIT_HINT(filePath));
|
|
7829
7903
|
}
|
package/docs/create.md
CHANGED
|
@@ -50,6 +50,7 @@ clawt create -b <branchName> [-n <count>] [--no-post-create]
|
|
|
50
50
|
- 若 `n > 1`:校验 `branchName-1` 到 `branchName-n`
|
|
51
51
|
- 所有分支名在创建任何 worktree **之前**完成全部校验
|
|
52
52
|
8. **批量创建 worktree + 验证分支**
|
|
53
|
+
- 创建前记录当前所在分支为来源分支(`baseBranch`),保存到 `~/.clawt/projects/<projectName>/worktrees/<branchName>.json`
|
|
53
54
|
- 若 `n = 1`:
|
|
54
55
|
```bash
|
|
55
56
|
git worktree add -b <branchName> ~/.clawt/worktrees/<project>/<branchName>
|