@gethmy/agent 1.0.0 → 1.0.1

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 (52) hide show
  1. package/README.md +5 -5
  2. package/dist/board-helpers.d.ts +23 -0
  3. package/dist/board-helpers.js +131 -0
  4. package/dist/cli.d.ts +2 -0
  5. package/dist/cli.js +2 -11761
  6. package/dist/completion.d.ts +7 -0
  7. package/dist/completion.js +132 -0
  8. package/dist/config.d.ts +23 -0
  9. package/dist/config.js +91 -0
  10. package/dist/git-pr.d.ts +25 -0
  11. package/dist/git-pr.js +305 -0
  12. package/dist/index.d.ts +1 -0
  13. package/dist/index.js +165 -11730
  14. package/dist/log.d.ts +10 -0
  15. package/dist/log.js +35 -0
  16. package/dist/merge-monitor.d.ts +23 -0
  17. package/dist/merge-monitor.js +155 -0
  18. package/dist/pm.d.ts +14 -0
  19. package/dist/pm.js +63 -0
  20. package/dist/pool.d.ts +36 -0
  21. package/dist/pool.js +134 -0
  22. package/dist/progress-tracker.d.ts +39 -0
  23. package/dist/progress-tracker.js +189 -0
  24. package/dist/prompt.d.ts +5 -0
  25. package/dist/prompt.js +40 -0
  26. package/dist/queue.d.ts +37 -0
  27. package/dist/queue.js +96 -0
  28. package/dist/reconcile.d.ts +21 -0
  29. package/dist/reconcile.js +107 -0
  30. package/dist/review-completion.d.ts +31 -0
  31. package/dist/review-completion.js +247 -0
  32. package/dist/review-knowledge.d.ts +14 -0
  33. package/dist/review-knowledge.js +89 -0
  34. package/dist/review-prompt.d.ts +12 -0
  35. package/dist/review-prompt.js +100 -0
  36. package/dist/review-worker.d.ts +35 -0
  37. package/dist/review-worker.js +302 -0
  38. package/dist/review-worktree.d.ts +12 -0
  39. package/dist/review-worktree.js +83 -0
  40. package/dist/stream-parser.d.ts +22 -0
  41. package/dist/stream-parser.js +81 -0
  42. package/dist/types.d.ts +74 -0
  43. package/dist/types.js +53 -0
  44. package/dist/verification.d.ts +16 -0
  45. package/dist/verification.js +251 -0
  46. package/dist/watcher.d.ts +21 -0
  47. package/dist/watcher.js +62 -0
  48. package/dist/worker.d.ts +34 -0
  49. package/dist/worker.js +268 -0
  50. package/dist/worktree.d.ts +13 -0
  51. package/dist/worktree.js +115 -0
  52. package/package.json +6 -5
@@ -0,0 +1,189 @@
1
+ import { log } from "./log.js";
2
+ import { AGENT_NAME, agentIdentifier } from "./types.js";
3
+ const TAG = "progress-tracker";
4
+ const THROTTLE_MS = 5_000;
5
+ const HEARTBEAT_MS = 60_000;
6
+ const PHASES = {
7
+ exploring: { min: 10, max: 25, task: "Reading codebase..." },
8
+ implementing: { min: 25, max: 55, task: "Implementing changes..." },
9
+ testing: { min: 55, max: 65, task: "Running tests..." },
10
+ committing: { min: 65, max: 70, task: "Committing changes..." },
11
+ finishing: { min: 70, max: 75, task: "Finalizing..." },
12
+ };
13
+ // Tools that indicate exploring phase
14
+ const _EXPLORE_TOOLS = new Set(["Read", "Glob", "Grep", "Agent"]);
15
+ // Tools that indicate implementation
16
+ const EDIT_TOOLS = new Set(["Write", "Edit", "MultiEdit", "NotebookEdit"]);
17
+ export class ProgressTracker {
18
+ client;
19
+ cardId;
20
+ workerId;
21
+ phase = "exploring";
22
+ progress = 10;
23
+ toolCallCount = 0;
24
+ hasEdited = false;
25
+ lastUpdateAt = 0;
26
+ pendingUpdate = null;
27
+ heartbeatTimer = null;
28
+ stopped = false;
29
+ // Subtask tracking
30
+ subtaskTotal;
31
+ subtaskCompleted;
32
+ subtaskMode;
33
+ constructor(client, cardId, workerId, subtasks) {
34
+ this.client = client;
35
+ this.cardId = cardId;
36
+ this.workerId = workerId;
37
+ this.subtaskTotal = subtasks.length;
38
+ this.subtaskCompleted = subtasks.filter((s) => s.completed).length;
39
+ this.subtaskMode = subtasks.length > 0;
40
+ }
41
+ /**
42
+ * Wire up the parser events and start the heartbeat.
43
+ */
44
+ attach(parser) {
45
+ parser.on("tool_start", (name, input) => {
46
+ this.onToolStart(name, input);
47
+ });
48
+ parser.on("tool_end", (name) => {
49
+ this.onToolEnd(name);
50
+ });
51
+ this.startHeartbeat();
52
+ }
53
+ /**
54
+ * Stop all timers and flush any pending update.
55
+ */
56
+ stop() {
57
+ this.stopped = true;
58
+ if (this.pendingUpdate) {
59
+ clearTimeout(this.pendingUpdate);
60
+ this.pendingUpdate = null;
61
+ }
62
+ if (this.heartbeatTimer) {
63
+ clearTimeout(this.heartbeatTimer);
64
+ this.heartbeatTimer = null;
65
+ }
66
+ }
67
+ onToolStart(name, input) {
68
+ this.toolCallCount++;
69
+ log.debug(TAG, `Tool: ${name} (count: ${this.toolCallCount}, phase: ${this.phase})`);
70
+ // Detect phase transitions
71
+ if (!this.hasEdited && EDIT_TOOLS.has(name)) {
72
+ this.hasEdited = true;
73
+ this.transitionTo("implementing");
74
+ }
75
+ else if (this.hasEdited && name === "Bash") {
76
+ const cmd = this.extractBashCommand(input);
77
+ if (cmd && /\bgit\s+commit\b/.test(cmd)) {
78
+ this.transitionTo("committing");
79
+ }
80
+ else if (cmd &&
81
+ /\b(test|build|lint|check|tsc|vitest|jest|(?:bun|npm|pnpm|yarn) run (?:build|lint))\b/.test(cmd)) {
82
+ this.transitionTo("testing");
83
+ }
84
+ }
85
+ else if (name.startsWith("mcp__harmony__harmony_end_agent_session")) {
86
+ this.transitionTo("finishing");
87
+ }
88
+ // Handle subtask toggling — override heuristic progress
89
+ if (name === "mcp__harmony__harmony_toggle_subtask" && this.subtaskMode) {
90
+ this.subtaskCompleted = Math.min(this.subtaskCompleted + 1, this.subtaskTotal);
91
+ const subtaskProgress = Math.round(10 + (this.subtaskCompleted / this.subtaskTotal) * 60);
92
+ this.progress = Math.max(this.progress, subtaskProgress);
93
+ this.scheduleUpdate(`Completed subtask ${this.subtaskCompleted}/${this.subtaskTotal}`);
94
+ return;
95
+ }
96
+ // Increment progress within current phase bounds
97
+ this.incrementProgress();
98
+ }
99
+ onToolEnd(_name) {
100
+ // Reset heartbeat on any activity
101
+ this.startHeartbeat();
102
+ }
103
+ transitionTo(newPhase) {
104
+ if (this.phaseOrder(newPhase) <= this.phaseOrder(this.phase))
105
+ return;
106
+ log.info(TAG, `Phase: ${this.phase} → ${newPhase}`);
107
+ this.phase = newPhase;
108
+ this.progress = Math.max(this.progress, PHASES[newPhase].min);
109
+ this.scheduleUpdate(PHASES[newPhase].task);
110
+ }
111
+ incrementProgress() {
112
+ const config = PHASES[this.phase];
113
+ const range = config.max - config.min;
114
+ // Each tool call moves ~2% within the phase, so roughly 7-15 calls fill a phase
115
+ const step = Math.max(1, Math.round(range / 12));
116
+ const newProgress = Math.min(this.progress + step, config.max);
117
+ if (newProgress > this.progress) {
118
+ this.progress = newProgress;
119
+ this.scheduleUpdate(this.currentTaskLabel());
120
+ }
121
+ }
122
+ currentTaskLabel() {
123
+ const config = PHASES[this.phase];
124
+ return config.task;
125
+ }
126
+ scheduleUpdate(currentTask) {
127
+ if (this.stopped)
128
+ return;
129
+ const now = Date.now();
130
+ const elapsed = now - this.lastUpdateAt;
131
+ if (elapsed >= THROTTLE_MS) {
132
+ // Can send immediately
133
+ this.sendUpdate(currentTask);
134
+ }
135
+ else if (!this.pendingUpdate) {
136
+ // Schedule for after throttle window
137
+ const delay = THROTTLE_MS - elapsed;
138
+ this.pendingUpdate = setTimeout(() => {
139
+ this.pendingUpdate = null;
140
+ if (!this.stopped) {
141
+ this.sendUpdate(currentTask);
142
+ }
143
+ }, delay);
144
+ }
145
+ // If there's already a pending update, it will fire soon — skip
146
+ }
147
+ sendUpdate(currentTask) {
148
+ this.lastUpdateAt = Date.now();
149
+ log.debug(TAG, `Progress: ${this.progress}% — ${currentTask}`);
150
+ this.client
151
+ .updateAgentProgress(this.cardId, {
152
+ agentIdentifier: agentIdentifier(this.workerId),
153
+ agentName: AGENT_NAME,
154
+ status: "working",
155
+ currentTask,
156
+ progressPercent: this.progress,
157
+ })
158
+ .catch((err) => {
159
+ log.warn(TAG, `Failed to send progress update: ${err}`);
160
+ });
161
+ }
162
+ startHeartbeat() {
163
+ if (this.heartbeatTimer) {
164
+ clearTimeout(this.heartbeatTimer);
165
+ }
166
+ this.heartbeatTimer = setTimeout(() => {
167
+ if (!this.stopped) {
168
+ this.sendUpdate("Still working...");
169
+ this.startHeartbeat();
170
+ }
171
+ }, HEARTBEAT_MS);
172
+ }
173
+ phaseOrder(phase) {
174
+ const order = [
175
+ "exploring",
176
+ "implementing",
177
+ "testing",
178
+ "committing",
179
+ "finishing",
180
+ ];
181
+ return order.indexOf(phase);
182
+ }
183
+ extractBashCommand(input) {
184
+ if (typeof input === "object" && input !== null && "command" in input) {
185
+ return String(input.command);
186
+ }
187
+ return null;
188
+ }
189
+ }
@@ -0,0 +1,5 @@
1
+ import type { EnrichedCard } from "./types.js";
2
+ /**
3
+ * Build the prompt that gets passed to Claude CLI for working on a card.
4
+ */
5
+ export declare function buildPrompt(enriched: EnrichedCard, branchName: string, worktreePath: string): string;
package/dist/prompt.js ADDED
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Build the prompt that gets passed to Claude CLI for working on a card.
3
+ */
4
+ export function buildPrompt(enriched, branchName, worktreePath) {
5
+ const { card, column, labels, subtasks } = enriched;
6
+ const labelStr = labels.length > 0 ? labels.map((l) => l.name).join(", ") : "none";
7
+ const subtaskStr = subtasks.length > 0
8
+ ? subtasks
9
+ .map((s) => `- [${s.completed ? "x" : " "}] ${s.title}`)
10
+ .join("\n")
11
+ : "No subtasks defined.";
12
+ const description = card.description?.trim() || "No description provided.";
13
+ return `You are an AI agent working on a task from the Harmony project board.
14
+
15
+ ## Card: #${card.short_id} - ${card.title}
16
+ **Labels**: ${labelStr}
17
+ **Column**: ${column.name}
18
+ **Priority**: ${card.priority}
19
+
20
+ ## Description
21
+ ${description}
22
+
23
+ ## Subtasks
24
+ ${subtaskStr}
25
+
26
+ ## Instructions
27
+ 1. Read the codebase and understand the context needed for this task
28
+ 2. Report progress via harmony_update_agent_progress at key milestones:
29
+ - After reading codebase and forming a plan (~20%)
30
+ - After each major implementation step (~30-60%)
31
+ - After completing each subtask (also toggle via harmony_toggle_subtask)
32
+ - Before committing (~65%)
33
+ Include a brief currentTask description.
34
+ 3. Implement the changes on branch \`${branchName}\`
35
+ 4. Commit your work with clear, descriptive commit messages
36
+ 5. When finished, call harmony_end_agent_session with status="completed"
37
+
38
+ You are working in a git worktree at \`${worktreePath}\` on branch \`${branchName}\`.
39
+ Do NOT push to main. All your work stays on \`${branchName}\`.`;
40
+ }
@@ -0,0 +1,37 @@
1
+ import type { Card, Column, Label } from "@harmony/shared";
2
+ import type { AgentConfig, QueueItem, WorkMode } from "./types.js";
3
+ /**
4
+ * Priority queue for cards waiting to be worked on.
5
+ * Sorted by: label priority boost > column position boost > enqueue time (FIFO).
6
+ */
7
+ export declare class PriorityQueue {
8
+ private config;
9
+ private items;
10
+ constructor(config: AgentConfig);
11
+ /**
12
+ * Calculate priority score for a card.
13
+ */
14
+ scoreCard(_card: Card, column: Column, labels: Label[]): number;
15
+ /**
16
+ * Add a card to the queue. If already present, update its priority.
17
+ */
18
+ enqueue(card: Card, column: Column, labels: Label[], mode?: WorkMode): void;
19
+ /**
20
+ * Remove and return the highest-priority item.
21
+ */
22
+ dequeue(): QueueItem | null;
23
+ /**
24
+ * Remove a specific card from the queue.
25
+ */
26
+ remove(cardId: string): QueueItem | null;
27
+ /**
28
+ * Check if a card is in the queue.
29
+ */
30
+ has(cardId: string): boolean;
31
+ /**
32
+ * Get all queued card IDs.
33
+ */
34
+ cardIds(): string[];
35
+ get length(): number;
36
+ peek(): QueueItem | null;
37
+ }
package/dist/queue.js ADDED
@@ -0,0 +1,96 @@
1
+ import { log } from "./log.js";
2
+ const TAG = "queue";
3
+ /**
4
+ * Priority queue for cards waiting to be worked on.
5
+ * Sorted by: label priority boost > column position boost > enqueue time (FIFO).
6
+ */
7
+ export class PriorityQueue {
8
+ config;
9
+ items = [];
10
+ constructor(config) {
11
+ this.config = config;
12
+ }
13
+ /**
14
+ * Calculate priority score for a card.
15
+ */
16
+ scoreCard(_card, column, labels) {
17
+ let score = 0;
18
+ // Label boost: highest matching label wins
19
+ for (const label of labels) {
20
+ const boost = this.config.priorityLabels[label.name.toLowerCase()] ?? 0;
21
+ if (boost > score)
22
+ score = boost;
23
+ }
24
+ // Column position boost: leftmost columns get higher priority
25
+ if (this.config.columnBoost) {
26
+ score += Math.max(0, 100 - column.position * 10);
27
+ }
28
+ return score;
29
+ }
30
+ /**
31
+ * Add a card to the queue. If already present, update its priority.
32
+ */
33
+ enqueue(card, column, labels, mode = "implement") {
34
+ const existing = this.items.findIndex((i) => i.cardId === card.id);
35
+ if (existing !== -1) {
36
+ log.debug(TAG, `Card #${card.short_id} already queued, updating priority`);
37
+ this.items.splice(existing, 1);
38
+ }
39
+ const priority = this.scoreCard(card, column, labels);
40
+ const item = {
41
+ cardId: card.id,
42
+ shortId: card.short_id,
43
+ title: card.title,
44
+ priority,
45
+ enqueuedAt: Date.now(),
46
+ mode,
47
+ };
48
+ // Insert in sorted position (highest priority first, FIFO tiebreak)
49
+ let insertIdx = this.items.length;
50
+ for (let i = 0; i < this.items.length; i++) {
51
+ if (priority > this.items[i].priority ||
52
+ (priority === this.items[i].priority &&
53
+ item.enqueuedAt < this.items[i].enqueuedAt)) {
54
+ insertIdx = i;
55
+ break;
56
+ }
57
+ }
58
+ this.items.splice(insertIdx, 0, item);
59
+ log.info(TAG, `Enqueued #${card.short_id} "${card.title}" (priority=${priority}, pos=${insertIdx}, queue=${this.items.length})`);
60
+ }
61
+ /**
62
+ * Remove and return the highest-priority item.
63
+ */
64
+ dequeue() {
65
+ return this.items.shift() ?? null;
66
+ }
67
+ /**
68
+ * Remove a specific card from the queue.
69
+ */
70
+ remove(cardId) {
71
+ const idx = this.items.findIndex((i) => i.cardId === cardId);
72
+ if (idx === -1)
73
+ return null;
74
+ const [item] = this.items.splice(idx, 1);
75
+ log.info(TAG, `Removed #${item.shortId} from queue`);
76
+ return item;
77
+ }
78
+ /**
79
+ * Check if a card is in the queue.
80
+ */
81
+ has(cardId) {
82
+ return this.items.some((i) => i.cardId === cardId);
83
+ }
84
+ /**
85
+ * Get all queued card IDs.
86
+ */
87
+ cardIds() {
88
+ return this.items.map((i) => i.cardId);
89
+ }
90
+ get length() {
91
+ return this.items.length;
92
+ }
93
+ peek() {
94
+ return this.items[0] ?? null;
95
+ }
96
+ }
@@ -0,0 +1,21 @@
1
+ import type { HarmonyApiClient } from "@gethmy/mcp/src/api-client.js";
2
+ import type { Pool } from "./pool.js";
3
+ /**
4
+ * Reconciliation heartbeat: polls the board every `intervalMs` to catch
5
+ * missed realtime events and sync state.
6
+ */
7
+ export declare class Reconciler {
8
+ private client;
9
+ private pool;
10
+ private projectId;
11
+ private agentUserId;
12
+ private pickupColumns;
13
+ private reviewColumns;
14
+ private approvedLabel;
15
+ private intervalMs;
16
+ private timer;
17
+ constructor(client: HarmonyApiClient, pool: Pool, projectId: string, agentUserId: string, pickupColumns: string[], reviewColumns: string[], approvedLabel: string, intervalMs?: number);
18
+ start(): void;
19
+ stop(): void;
20
+ private tick;
21
+ }
@@ -0,0 +1,107 @@
1
+ import { hasApprovedLabel } from "./board-helpers.js";
2
+ import { log } from "./log.js";
3
+ const TAG = "reconcile";
4
+ /**
5
+ * Reconciliation heartbeat: polls the board every `intervalMs` to catch
6
+ * missed realtime events and sync state.
7
+ */
8
+ export class Reconciler {
9
+ client;
10
+ pool;
11
+ projectId;
12
+ agentUserId;
13
+ pickupColumns;
14
+ reviewColumns;
15
+ approvedLabel;
16
+ intervalMs;
17
+ timer = null;
18
+ constructor(client, pool, projectId, agentUserId, pickupColumns, reviewColumns, approvedLabel, intervalMs = 60_000) {
19
+ this.client = client;
20
+ this.pool = pool;
21
+ this.projectId = projectId;
22
+ this.agentUserId = agentUserId;
23
+ this.pickupColumns = pickupColumns;
24
+ this.reviewColumns = reviewColumns;
25
+ this.approvedLabel = approvedLabel;
26
+ this.intervalMs = intervalMs;
27
+ }
28
+ start() {
29
+ log.info(TAG, `Heartbeat every ${this.intervalMs / 1000}s`);
30
+ // Run immediately, then on interval
31
+ this.tick();
32
+ this.timer = setInterval(() => this.tick(), this.intervalMs);
33
+ }
34
+ stop() {
35
+ if (this.timer) {
36
+ clearInterval(this.timer);
37
+ this.timer = null;
38
+ }
39
+ log.info(TAG, "Heartbeat stopped");
40
+ }
41
+ async tick() {
42
+ try {
43
+ const board = await this.client.getBoard(this.projectId);
44
+ const cards = (board.cards ?? []);
45
+ const columns = board.columns;
46
+ const _labels = (board.labels ?? []);
47
+ // Build a lookup of columns by ID
48
+ const columnMap = new Map();
49
+ for (const col of columns) {
50
+ columnMap.set(col.id, col);
51
+ }
52
+ // Build column ID sets for both modes
53
+ const pickupColumnIds = new Set(columns
54
+ .filter((c) => this.pickupColumns.some((name) => name.toLowerCase() === c.name.toLowerCase()))
55
+ .map((c) => c.id));
56
+ const reviewColumnIds = new Set(columns
57
+ .filter((c) => this.reviewColumns.some((name) => name.toLowerCase() === c.name.toLowerCase()))
58
+ .map((c) => c.id));
59
+ // Find cards assigned to our agent in either pickup or review columns
60
+ const assignedCards = cards.filter((c) => c.assignee_id === this.agentUserId &&
61
+ !c.archived_at &&
62
+ (pickupColumnIds.has(c.column_id) ||
63
+ reviewColumnIds.has(c.column_id)));
64
+ const knownCardIds = this.pool.knownCardIds();
65
+ // All cards still assigned to the agent (any column) — used to detect
66
+ // genuine unassigns without false-positiving on cards the worker moved
67
+ // to "In Progress" or other non-pickup columns.
68
+ const allAgentCardIds = new Set(cards
69
+ .filter((c) => c.assignee_id === this.agentUserId && !c.archived_at)
70
+ .map((c) => c.id));
71
+ // Cards assigned but NOT in queue/active → enqueue (missed event)
72
+ for (const card of assignedCards) {
73
+ if (!knownCardIds.has(card.id)) {
74
+ const column = columnMap.get(card.column_id);
75
+ if (!column)
76
+ continue;
77
+ const cardLabels = card.labels ?? [];
78
+ const subtasks = card.subtasks ?? [];
79
+ // Determine mode based on which column set the card is in
80
+ const mode = reviewColumnIds.has(card.column_id)
81
+ ? "review"
82
+ : "implement";
83
+ // Skip already-approved cards in review mode
84
+ if (mode === "review" &&
85
+ this.approvedLabel &&
86
+ hasApprovedLabel(cardLabels, this.approvedLabel)) {
87
+ log.debug(TAG, `Skipping #${card.short_id} — already has "${this.approvedLabel}" label`);
88
+ continue;
89
+ }
90
+ log.info(TAG, `Missed assignment: #${card.short_id} "${card.title}" (${mode}) — enqueueing`);
91
+ this.pool.enqueue(card, column, cardLabels, subtasks, mode);
92
+ }
93
+ }
94
+ // Cards in queue/active but no longer assigned to agent → cancel/remove
95
+ for (const knownId of knownCardIds) {
96
+ if (!allAgentCardIds.has(knownId)) {
97
+ log.info(TAG, `Missed unassign: ${knownId} — removing`);
98
+ await this.pool.removeCard(knownId);
99
+ }
100
+ }
101
+ log.debug(TAG, `Reconciled: ${assignedCards.length} assigned, ${knownCardIds.size} known`);
102
+ }
103
+ catch (err) {
104
+ log.error(TAG, `Heartbeat failed: ${err instanceof Error ? err.message : err}`);
105
+ }
106
+ }
107
+ }
@@ -0,0 +1,31 @@
1
+ import type { HarmonyApiClient } from "@gethmy/mcp/src/api-client.js";
2
+ import type { Card } from "@harmony/shared";
3
+ import type { AgentConfig } from "./types.js";
4
+ export interface ReviewFinding {
5
+ severity: "critical" | "major" | "minor";
6
+ title: string;
7
+ description: string;
8
+ category?: string;
9
+ location?: string;
10
+ }
11
+ export interface ScopeCheck {
12
+ status: "clean" | "drift" | "missing";
13
+ notes?: string;
14
+ }
15
+ export interface ReviewResult {
16
+ verdict: "approved" | "rejected";
17
+ summary: string;
18
+ scopeCheck?: ScopeCheck;
19
+ findings: ReviewFinding[];
20
+ }
21
+ /**
22
+ * Parse Claude's review output into a structured ReviewResult.
23
+ * Looks for a JSON block in the output.
24
+ */
25
+ export declare function parseReviewOutput(stdout: string): ReviewResult;
26
+ /**
27
+ * Post-review completion pipeline.
28
+ * Handles approved/rejected verdicts, creates subtasks for findings,
29
+ * and moves the card to the appropriate column.
30
+ */
31
+ export declare function runReviewCompletion(client: HarmonyApiClient, card: Card, result: ReviewResult, config: AgentConfig, worktreePath: string, branchName: string): Promise<void>;