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.
- package/AGENTS.md +16 -0
- package/dist/index.js +228 -86
- package/dist/postinstall.js +27 -0
- 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-01-sync-validate-diverged-findings.md +203 -0
- package/docs/superpowers/findings/2026-06-09-worktree-base-branch-findings.md +58 -0
- package/docs/superpowers/plans/2026-06-01-validate-ignored-files-conflict.md +412 -0
- package/docs/superpowers/plans/2026-06-09-worktree-base-branch.md +386 -0
- package/docs/superpowers/specs/2026-06-01-validate-ignored-files-conflict-design.md +76 -0
- package/docs/superpowers/specs/2026-06-09-worktree-base-branch-design.md +169 -0
- package/docs/validate.md +42 -5
- 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/constants/messages/validate.ts +17 -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/git-core.ts +23 -0
- package/src/utils/index.ts +4 -2
- package/src/utils/interactive-panel-render.ts +6 -3
- package/src/utils/validate-core.ts +52 -0
- 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/git-core.test.ts +43 -0
- package/tests/unit/utils/interactive-panel-render.test.ts +124 -0
- package/tests/unit/utils/validate-core.test.ts +60 -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/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
|
|
3359
|
-
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
|
|
3360
3458
|
function getProjectWorktreeDir() {
|
|
3361
3459
|
const projectName = getProjectName();
|
|
3362
|
-
return
|
|
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
|
|
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 =
|
|
3472
|
+
const worktreePath = join6(projectDir, name);
|
|
3373
3473
|
createWorktree(name, worktreePath);
|
|
3374
3474
|
createValidateBranch(name);
|
|
3375
|
-
|
|
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
|
|
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 =
|
|
3489
|
+
const worktreePath = join6(projectDir, name);
|
|
3387
3490
|
createWorktree(name, worktreePath);
|
|
3388
3491
|
createValidateBranch(name);
|
|
3389
|
-
|
|
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
|
|
3396
|
-
|
|
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 =
|
|
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 =
|
|
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
|
|
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 =
|
|
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
|
|
3501
|
-
import { join as
|
|
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
|
|
3614
|
+
import { existsSync as existsSync7 } from "fs";
|
|
3506
3615
|
function isITerm2Installed() {
|
|
3507
|
-
return
|
|
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 =
|
|
3646
|
-
if (!
|
|
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
|
|
3700
|
-
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";
|
|
3701
3810
|
function getSnapshotPath(projectName, branchName) {
|
|
3702
|
-
return
|
|
3811
|
+
return join9(VALIDATE_SNAPSHOTS_DIR, projectName, `${branchName}.tree`);
|
|
3703
3812
|
}
|
|
3704
3813
|
function getSnapshotHeadPath(projectName, branchName) {
|
|
3705
|
-
return
|
|
3814
|
+
return join9(VALIDATE_SNAPSHOTS_DIR, projectName, `${branchName}.head`);
|
|
3706
3815
|
}
|
|
3707
3816
|
function getSnapshotStagedPath(projectName, branchName) {
|
|
3708
|
-
return
|
|
3817
|
+
return join9(VALIDATE_SNAPSHOTS_DIR, projectName, `${branchName}.staged`);
|
|
3709
3818
|
}
|
|
3710
3819
|
function hasSnapshot(projectName, branchName) {
|
|
3711
|
-
return
|
|
3820
|
+
return existsSync9(getSnapshotPath(projectName, branchName));
|
|
3712
3821
|
}
|
|
3713
3822
|
function getSnapshotModifiedTime(projectName, branchName) {
|
|
3714
3823
|
const snapshotPath = getSnapshotPath(projectName, branchName);
|
|
3715
|
-
if (!
|
|
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 =
|
|
3725
|
-
const headCommitHash =
|
|
3726
|
-
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() : "";
|
|
3727
3836
|
return { treeHash, headCommitHash, stagedTreeHash };
|
|
3728
3837
|
}
|
|
3729
3838
|
function writeSnapshot(projectName, branchName, treeHash, headCommitHash, stagedTreeHash) {
|
|
3730
|
-
const snapshotDir =
|
|
3839
|
+
const snapshotDir = join9(VALIDATE_SNAPSHOTS_DIR, projectName);
|
|
3731
3840
|
ensureDir(snapshotDir);
|
|
3732
3841
|
if (treeHash !== void 0) {
|
|
3733
|
-
|
|
3842
|
+
writeFileSync4(getSnapshotPath(projectName, branchName), treeHash, "utf-8");
|
|
3734
3843
|
}
|
|
3735
3844
|
if (headCommitHash !== void 0) {
|
|
3736
|
-
|
|
3845
|
+
writeFileSync4(getSnapshotHeadPath(projectName, branchName), headCommitHash, "utf-8");
|
|
3737
3846
|
}
|
|
3738
3847
|
if (stagedTreeHash !== void 0) {
|
|
3739
|
-
|
|
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 (
|
|
3856
|
+
if (existsSync9(snapshotPath)) {
|
|
3748
3857
|
unlinkSync2(snapshotPath);
|
|
3749
3858
|
logger.info(`\u5DF2\u5220\u9664 validate \u5FEB\u7167: ${snapshotPath}`);
|
|
3750
3859
|
}
|
|
3751
|
-
if (
|
|
3860
|
+
if (existsSync9(headPath)) {
|
|
3752
3861
|
unlinkSync2(headPath);
|
|
3753
3862
|
logger.info(`\u5DF2\u5220\u9664 validate \u5FEB\u7167: ${headPath}`);
|
|
3754
3863
|
}
|
|
3755
|
-
if (
|
|
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 =
|
|
3762
|
-
if (!
|
|
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 =
|
|
3770
|
-
if (!
|
|
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(
|
|
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
|
|
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 (!
|
|
4461
|
+
if (!existsSync10(absolutePath)) {
|
|
4353
4462
|
throw new ClawtError(MESSAGES.TASK_FILE_NOT_FOUND(absolutePath));
|
|
4354
4463
|
}
|
|
4355
|
-
const content =
|
|
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
|
|
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 =
|
|
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
|
|
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 =
|
|
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
|
-
|
|
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
|
|
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
|
|
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 =
|
|
6005
|
-
if (
|
|
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
|
|
7267
|
-
import { join as
|
|
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 =
|
|
7292
|
-
if (!
|
|
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 (!
|
|
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 =
|
|
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) =>
|
|
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 =
|
|
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
|
|
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
|
|
7498
|
-
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";
|
|
7499
7641
|
function completeFilePath(partial) {
|
|
7500
7642
|
const cwd = process.cwd();
|
|
7501
7643
|
const hasDir = partial.includes("/");
|
|
7502
|
-
const searchDir = hasDir ?
|
|
7644
|
+
const searchDir = hasDir ? join14(cwd, dirname2(partial)) : cwd;
|
|
7503
7645
|
const prefix = hasDir ? basename2(partial) : partial;
|
|
7504
|
-
if (!
|
|
7646
|
+
if (!existsSync14(searchDir)) {
|
|
7505
7647
|
return [];
|
|
7506
7648
|
}
|
|
7507
7649
|
const entries = readdirSync7(searchDir);
|
|
7508
7650
|
const results = [];
|
|
7509
|
-
const dirPrefix = hasDir ?
|
|
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 =
|
|
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 (
|
|
7603
|
-
const current =
|
|
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
|
-
|
|
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
|
|
7743
|
-
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";
|
|
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 ??
|
|
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 (
|
|
7896
|
+
if (existsSync16(absolutePath)) {
|
|
7755
7897
|
throw new ClawtError(MESSAGES.TASK_INIT_FILE_EXISTS(filePath));
|
|
7756
7898
|
}
|
|
7757
|
-
ensureDir(
|
|
7758
|
-
|
|
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
|
}
|