@simonfestl/husky-cli 0.9.5 → 0.9.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/dist/commands/llm-context.js +7 -3
- package/dist/commands/service-account.d.ts +2 -0
- package/dist/commands/service-account.js +180 -0
- package/dist/commands/services.d.ts +2 -0
- package/dist/commands/services.js +381 -0
- package/dist/commands/vm.js +230 -6
- package/dist/commands/worktree.js +481 -1
- package/dist/index.js +4 -0
- package/dist/lib/agent-templates.d.ts +20 -0
- package/dist/lib/agent-templates.js +142 -0
- package/dist/lib/worktree.d.ts +26 -0
- package/dist/lib/worktree.js +127 -0
- package/package.json +1 -1
package/dist/lib/worktree.d.ts
CHANGED
|
@@ -33,6 +33,11 @@ export interface MergeOptions {
|
|
|
33
33
|
deleteAfter?: boolean;
|
|
34
34
|
message?: string;
|
|
35
35
|
}
|
|
36
|
+
export interface ConflictCheckResult {
|
|
37
|
+
hasConflicts: boolean;
|
|
38
|
+
conflictFiles: string[];
|
|
39
|
+
checkedAt: Date;
|
|
40
|
+
}
|
|
36
41
|
export declare class WorktreeManager {
|
|
37
42
|
private projectDir;
|
|
38
43
|
private baseBranch;
|
|
@@ -130,4 +135,25 @@ export declare class WorktreeManager {
|
|
|
130
135
|
* Get the worktrees directory.
|
|
131
136
|
*/
|
|
132
137
|
getWorktreesDir(): string;
|
|
138
|
+
/**
|
|
139
|
+
* Check if a worktree branch would have merge conflicts with base branch.
|
|
140
|
+
* Uses git merge-tree to simulate the merge without actually doing it.
|
|
141
|
+
*/
|
|
142
|
+
checkMergeConflicts(sessionName: string): ConflictCheckResult;
|
|
143
|
+
/**
|
|
144
|
+
* Push the worktree branch to remote.
|
|
145
|
+
*/
|
|
146
|
+
pushWorktreeBranch(sessionName: string, force?: boolean): boolean;
|
|
147
|
+
/**
|
|
148
|
+
* Create a PR for a worktree branch using gh CLI.
|
|
149
|
+
*/
|
|
150
|
+
createPullRequest(sessionName: string, options: {
|
|
151
|
+
title: string;
|
|
152
|
+
body?: string;
|
|
153
|
+
draft?: boolean;
|
|
154
|
+
}): {
|
|
155
|
+
success: boolean;
|
|
156
|
+
prUrl?: string;
|
|
157
|
+
error?: string;
|
|
158
|
+
};
|
|
133
159
|
}
|
package/dist/lib/worktree.js
CHANGED
|
@@ -470,4 +470,131 @@ export class WorktreeManager {
|
|
|
470
470
|
getWorktreesDir() {
|
|
471
471
|
return this.worktreesDir;
|
|
472
472
|
}
|
|
473
|
+
/**
|
|
474
|
+
* Check if a worktree branch would have merge conflicts with base branch.
|
|
475
|
+
* Uses git merge-tree to simulate the merge without actually doing it.
|
|
476
|
+
*/
|
|
477
|
+
checkMergeConflicts(sessionName) {
|
|
478
|
+
const worktreePath = this.getWorktreePath(sessionName);
|
|
479
|
+
const branchName = this.getBranchName(sessionName);
|
|
480
|
+
if (!fs.existsSync(worktreePath)) {
|
|
481
|
+
return {
|
|
482
|
+
hasConflicts: false,
|
|
483
|
+
conflictFiles: [],
|
|
484
|
+
checkedAt: new Date(),
|
|
485
|
+
};
|
|
486
|
+
}
|
|
487
|
+
// Get the merge base
|
|
488
|
+
const mergeBaseResult = this.runGit([
|
|
489
|
+
"merge-base",
|
|
490
|
+
this.baseBranch,
|
|
491
|
+
branchName,
|
|
492
|
+
]);
|
|
493
|
+
if (mergeBaseResult.status !== 0) {
|
|
494
|
+
// No common ancestor, can't check conflicts
|
|
495
|
+
return {
|
|
496
|
+
hasConflicts: false,
|
|
497
|
+
conflictFiles: [],
|
|
498
|
+
checkedAt: new Date(),
|
|
499
|
+
};
|
|
500
|
+
}
|
|
501
|
+
const mergeBase = mergeBaseResult.stdout.trim();
|
|
502
|
+
// Use git merge-tree to simulate the merge
|
|
503
|
+
const mergeTreeResult = this.runGit([
|
|
504
|
+
"merge-tree",
|
|
505
|
+
mergeBase,
|
|
506
|
+
this.baseBranch,
|
|
507
|
+
branchName,
|
|
508
|
+
]);
|
|
509
|
+
// Parse the output for conflicts
|
|
510
|
+
const output = mergeTreeResult.stdout;
|
|
511
|
+
const conflictFiles = [];
|
|
512
|
+
// Look for conflict markers in merge-tree output
|
|
513
|
+
// Format: "changed in both" or conflict sections
|
|
514
|
+
const lines = output.split("\n");
|
|
515
|
+
let inConflict = false;
|
|
516
|
+
for (const line of lines) {
|
|
517
|
+
if (line.includes("changed in both") || line.includes("CONFLICT")) {
|
|
518
|
+
inConflict = true;
|
|
519
|
+
}
|
|
520
|
+
// Extract file paths from conflict sections
|
|
521
|
+
if (inConflict && line.match(/^\+\+\+|^---|^@@/)) {
|
|
522
|
+
const fileMatch = line.match(/^\+\+\+ b\/(.+)$/) || line.match(/^--- a\/(.+)$/);
|
|
523
|
+
if (fileMatch && fileMatch[1] && !conflictFiles.includes(fileMatch[1])) {
|
|
524
|
+
conflictFiles.push(fileMatch[1]);
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
// Alternative: use diff to find files changed in both branches
|
|
529
|
+
if (conflictFiles.length === 0 && output.includes("<<<<<<<")) {
|
|
530
|
+
// Parse conflict markers directly
|
|
531
|
+
const conflictMatches = output.matchAll(/\+\+\+ b\/([^\n]+)/g);
|
|
532
|
+
for (const match of conflictMatches) {
|
|
533
|
+
if (match[1] && !conflictFiles.includes(match[1])) {
|
|
534
|
+
conflictFiles.push(match[1]);
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
return {
|
|
539
|
+
hasConflicts: conflictFiles.length > 0 || output.includes("<<<<<<<") || output.includes("CONFLICT"),
|
|
540
|
+
conflictFiles,
|
|
541
|
+
checkedAt: new Date(),
|
|
542
|
+
};
|
|
543
|
+
}
|
|
544
|
+
/**
|
|
545
|
+
* Push the worktree branch to remote.
|
|
546
|
+
*/
|
|
547
|
+
pushWorktreeBranch(sessionName, force = false) {
|
|
548
|
+
const branchName = this.getBranchName(sessionName);
|
|
549
|
+
const pushArgs = ["push", "-u", "origin", branchName];
|
|
550
|
+
if (force) {
|
|
551
|
+
pushArgs.splice(1, 0, "--force");
|
|
552
|
+
}
|
|
553
|
+
const result = this.runGit(pushArgs);
|
|
554
|
+
if (result.status !== 0) {
|
|
555
|
+
console.error(`Failed to push branch: ${result.stderr}`);
|
|
556
|
+
return false;
|
|
557
|
+
}
|
|
558
|
+
console.log(`Pushed ${branchName} to origin`);
|
|
559
|
+
return true;
|
|
560
|
+
}
|
|
561
|
+
/**
|
|
562
|
+
* Create a PR for a worktree branch using gh CLI.
|
|
563
|
+
*/
|
|
564
|
+
createPullRequest(sessionName, options) {
|
|
565
|
+
const branchName = this.getBranchName(sessionName);
|
|
566
|
+
// Check if gh CLI is available
|
|
567
|
+
const ghCheck = spawnSync("which", ["gh"], { encoding: "utf-8" });
|
|
568
|
+
if (ghCheck.status !== 0) {
|
|
569
|
+
return { success: false, error: "GitHub CLI (gh) not installed" };
|
|
570
|
+
}
|
|
571
|
+
// Create PR
|
|
572
|
+
const prArgs = [
|
|
573
|
+
"pr",
|
|
574
|
+
"create",
|
|
575
|
+
"--base",
|
|
576
|
+
this.baseBranch,
|
|
577
|
+
"--head",
|
|
578
|
+
branchName,
|
|
579
|
+
"--title",
|
|
580
|
+
options.title,
|
|
581
|
+
];
|
|
582
|
+
if (options.body) {
|
|
583
|
+
prArgs.push("--body", options.body);
|
|
584
|
+
}
|
|
585
|
+
if (options.draft) {
|
|
586
|
+
prArgs.push("--draft");
|
|
587
|
+
}
|
|
588
|
+
const result = spawnSync("gh", prArgs, {
|
|
589
|
+
cwd: this.projectDir,
|
|
590
|
+
encoding: "utf-8",
|
|
591
|
+
timeout: 60000,
|
|
592
|
+
});
|
|
593
|
+
if (result.status !== 0) {
|
|
594
|
+
return { success: false, error: result.stderr || "Failed to create PR" };
|
|
595
|
+
}
|
|
596
|
+
// Extract PR URL from output
|
|
597
|
+
const prUrl = result.stdout.trim();
|
|
598
|
+
return { success: true, prUrl };
|
|
599
|
+
}
|
|
473
600
|
}
|