@tarcisiopgs/lisa 1.31.0 → 1.33.0
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/README.md +8 -0
- package/dist/{chunk-45ZNECZ5.js → chunk-23Z5BBRT.js} +26 -2
- package/dist/{chunk-YTHUJQKB.js → chunk-6XC7GQUI.js} +43 -27
- package/dist/{chunk-IQDRQXFK.js → chunk-R6D5VH65.js} +1 -1
- package/dist/{chunk-RQTH257A.js → chunk-SC222P5D.js} +221 -2
- package/dist/{chunk-66TB6NMR.js → chunk-XLAY4P45.js} +11 -3
- package/dist/{chunk-72DVXSHO.js → chunk-YMV4CBQE.js} +94 -1
- package/dist/{detection-PC7EMUHY.js → detection-H5QJR5XI.js} +2 -2
- package/dist/index.js +40 -11
- package/dist/{kanban-TXO3LZON.js → kanban-X4OR4ZFI.js} +87 -25
- package/dist/{loop-INGME5Y3.js → loop-XEJHKZHO.js} +4 -4
- package/dist/{merge-LRFZXFV2.js → merge-CFQO7VU4.js} +3 -1
- package/dist/{tui-bridge-KSE7MJDX.js → tui-bridge-G6BBFZFH.js} +7 -4
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -49,6 +49,7 @@ If something fails — pre-push hooks, quota limits, stuck processes — Lisa ha
|
|
|
49
49
|
- **CI monitoring** — polls CI after PR creation, re-invokes the agent to fix failures automatically
|
|
50
50
|
- **Progress comments** — posts real-time status updates on issues as Lisa works through stages
|
|
51
51
|
- **Context enrichment** — greps for issue-related files and surfaces them in the agent prompt
|
|
52
|
+
- **PR reviewers & assignees** — auto-request reviews and assign PRs via config; `self` keyword resolves to the authenticated user
|
|
52
53
|
- **Self-healing** — orphan recovery on startup, push failure retry, stuck process detection
|
|
53
54
|
- **Guardrails** — past failures are injected into future prompts to avoid repeating mistakes
|
|
54
55
|
- **Project context** — auto-generates `.lisa/context.md` with your stack, conventions, and constraints
|
|
@@ -254,6 +255,13 @@ ci_monitor:
|
|
|
254
255
|
progress_comments:
|
|
255
256
|
enabled: true # post real-time status on issues
|
|
256
257
|
|
|
258
|
+
pr:
|
|
259
|
+
reviewers: # auto-request reviews on every PR
|
|
260
|
+
- octocat
|
|
261
|
+
- hubot
|
|
262
|
+
assignees: # auto-assign PRs ("self" = authenticated user)
|
|
263
|
+
- self
|
|
264
|
+
|
|
257
265
|
hooks:
|
|
258
266
|
before_run: "./scripts/setup.sh"
|
|
259
267
|
after_run: "./scripts/cleanup.sh"
|
|
@@ -5,6 +5,7 @@ import {
|
|
|
5
5
|
appendPlatformAttribution,
|
|
6
6
|
appendPlatformProofOfWork,
|
|
7
7
|
appendPlatformSpecCompliance,
|
|
8
|
+
applyPrReviewersAndAssignees,
|
|
8
9
|
buildCompliancePrompt,
|
|
9
10
|
buildComplianceRecoveryPrompt,
|
|
10
11
|
buildContinuationPrompt,
|
|
@@ -29,7 +30,7 @@ import {
|
|
|
29
30
|
resolveModels,
|
|
30
31
|
runValidationCommands,
|
|
31
32
|
runWithFallback
|
|
32
|
-
} from "./chunk-
|
|
33
|
+
} from "./chunk-SC222P5D.js";
|
|
33
34
|
import {
|
|
34
35
|
divider,
|
|
35
36
|
error,
|
|
@@ -38,7 +39,7 @@ import {
|
|
|
38
39
|
log,
|
|
39
40
|
ok,
|
|
40
41
|
warn
|
|
41
|
-
} from "./chunk-
|
|
42
|
+
} from "./chunk-XLAY4P45.js";
|
|
42
43
|
import {
|
|
43
44
|
appendRawEntry,
|
|
44
45
|
migrateGuardrails
|
|
@@ -192,6 +193,23 @@ function findConfigDir(startDir = process.cwd()) {
|
|
|
192
193
|
dir = parent;
|
|
193
194
|
}
|
|
194
195
|
}
|
|
196
|
+
var USERNAME_RE = /^[a-zA-Z0-9][a-zA-Z0-9_.-]{0,38}$/;
|
|
197
|
+
function isValidPrUsername(s) {
|
|
198
|
+
return s === "self" || USERNAME_RE.test(s);
|
|
199
|
+
}
|
|
200
|
+
function isStringArray(v) {
|
|
201
|
+
return Array.isArray(v) && v.every((item) => typeof item === "string");
|
|
202
|
+
}
|
|
203
|
+
function parsePrConfig(raw) {
|
|
204
|
+
if (!raw) return void 0;
|
|
205
|
+
const reviewers = isStringArray(raw.reviewers) ? raw.reviewers.filter(isValidPrUsername) : void 0;
|
|
206
|
+
const assignees = isStringArray(raw.assignees) ? raw.assignees.filter(isValidPrUsername) : void 0;
|
|
207
|
+
if (!reviewers?.length && !assignees?.length) return void 0;
|
|
208
|
+
return {
|
|
209
|
+
reviewers: reviewers?.length ? reviewers : void 0,
|
|
210
|
+
assignees: assignees?.length ? assignees : void 0
|
|
211
|
+
};
|
|
212
|
+
}
|
|
195
213
|
function loadConfig(cwd = process.cwd()) {
|
|
196
214
|
const configPath = getConfigPath(cwd);
|
|
197
215
|
if (!existsSync(configPath)) {
|
|
@@ -234,6 +252,7 @@ function loadConfig(cwd = process.cwd()) {
|
|
|
234
252
|
const rawCiMonitor = parsed.ci_monitor;
|
|
235
253
|
const rawSpecCompliance = parsed.spec_compliance;
|
|
236
254
|
const rawProgress = parsed.progress_comments;
|
|
255
|
+
const rawPr = parsed.pr;
|
|
237
256
|
const config = {
|
|
238
257
|
...DEFAULT_CONFIG,
|
|
239
258
|
...parsedWithoutLogs,
|
|
@@ -279,6 +298,7 @@ function loadConfig(cwd = process.cwd()) {
|
|
|
279
298
|
block_on_failure: rawSpecCompliance.block_on_failure
|
|
280
299
|
} : void 0,
|
|
281
300
|
progress_comments: rawProgress ? { enabled: rawProgress.enabled ?? false } : void 0,
|
|
301
|
+
pr: parsePrConfig(rawPr),
|
|
282
302
|
provider_options: {
|
|
283
303
|
...DEFAULT_CONFIG.provider_options || {},
|
|
284
304
|
...parsed.provider_options ?? {}
|
|
@@ -2577,6 +2597,7 @@ ${contResult.output}
|
|
|
2577
2597
|
await executeHook("before_remove", config.hooks, worktreePath, hookEnv);
|
|
2578
2598
|
await cleanupWorktree(repoPath, worktreePath);
|
|
2579
2599
|
await appendPlatformAttribution(prUrl, result.providerUsed, config.platform);
|
|
2600
|
+
await applyPrReviewersAndAssignees(prUrl, config.pr, config.platform);
|
|
2580
2601
|
ok(`Step ${stepNum} complete: ${repoPath} \u2014 PR: ${prUrl}`);
|
|
2581
2602
|
return {
|
|
2582
2603
|
success: true,
|
|
@@ -2746,6 +2767,7 @@ async function runNativeWorktreeSession(config, issue, logFile, session, models,
|
|
|
2746
2767
|
}
|
|
2747
2768
|
ok(`PR created by provider: ${prUrl}`);
|
|
2748
2769
|
await appendPlatformAttribution(prUrl, result.providerUsed, config.platform);
|
|
2770
|
+
await applyPrReviewersAndAssignees(prUrl, config.pr, config.platform);
|
|
2749
2771
|
if (isCiMonitorEnabled(config.ci_monitor)) {
|
|
2750
2772
|
const manifestBranch = manifest?.branch;
|
|
2751
2773
|
if (manifestBranch) {
|
|
@@ -2967,6 +2989,7 @@ async function runManualWorktreeSession(config, issue, logFile, session, models,
|
|
|
2967
2989
|
}
|
|
2968
2990
|
ok(`PR created by provider: ${prUrl}`);
|
|
2969
2991
|
await appendPlatformAttribution(prUrl, result.providerUsed, config.platform);
|
|
2992
|
+
await applyPrReviewersAndAssignees(prUrl, config.pr, config.platform);
|
|
2970
2993
|
if (validationResults) {
|
|
2971
2994
|
await appendPlatformProofOfWork(prUrl, validationResults, config.platform);
|
|
2972
2995
|
}
|
|
@@ -3470,6 +3493,7 @@ async function runBranchSession(config, issue, logFile, session, models, source,
|
|
|
3470
3493
|
}
|
|
3471
3494
|
ok(`PR created by provider: ${prUrl}`);
|
|
3472
3495
|
await appendPlatformAttribution(prUrl, result.providerUsed, config.platform);
|
|
3496
|
+
await applyPrReviewersAndAssignees(prUrl, config.pr, config.platform);
|
|
3473
3497
|
if (validationResults) {
|
|
3474
3498
|
await appendPlatformProofOfWork(prUrl, validationResults, config.platform);
|
|
3475
3499
|
}
|
|
@@ -4,14 +4,14 @@ import {
|
|
|
4
4
|
readContext,
|
|
5
5
|
resolveModels,
|
|
6
6
|
runWithFallback
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-SC222P5D.js";
|
|
8
8
|
import {
|
|
9
9
|
error,
|
|
10
10
|
log,
|
|
11
11
|
normalizeLabels,
|
|
12
12
|
ok,
|
|
13
13
|
warn
|
|
14
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-XLAY4P45.js";
|
|
15
15
|
|
|
16
16
|
// src/cli/error.ts
|
|
17
17
|
var CliError = class extends Error {
|
|
@@ -153,6 +153,16 @@ function extractCleanText(text2) {
|
|
|
153
153
|
}
|
|
154
154
|
|
|
155
155
|
// src/plan/create.ts
|
|
156
|
+
function ensureAcceptanceCriteria(description, criteria) {
|
|
157
|
+
if (!criteria.length) return description;
|
|
158
|
+
if (/- \[ \]/.test(description)) return description;
|
|
159
|
+
const checklist = criteria.map((c) => `- [ ] ${c}`).join("\n");
|
|
160
|
+
return `${description}
|
|
161
|
+
|
|
162
|
+
## Acceptance Criteria
|
|
163
|
+
|
|
164
|
+
${checklist}`;
|
|
165
|
+
}
|
|
156
166
|
async function createPlanIssues(source, config, plan) {
|
|
157
167
|
if (!source.createIssue) {
|
|
158
168
|
throw new Error(`Source "${source.name}" does not support createIssue`);
|
|
@@ -163,7 +173,7 @@ async function createPlanIssues(source, config, plan) {
|
|
|
163
173
|
const createdIds = [];
|
|
164
174
|
const orderToId = /* @__PURE__ */ new Map();
|
|
165
175
|
for (const issue of sorted) {
|
|
166
|
-
let description = issue.description;
|
|
176
|
+
let description = ensureAcceptanceCriteria(issue.description, issue.acceptanceCriteria);
|
|
167
177
|
if (issue.dependsOn.length > 0 && !source.linkDependency) {
|
|
168
178
|
const depRefs = issue.dependsOn.map((depOrder) => {
|
|
169
179
|
const depId = orderToId.get(depOrder);
|
|
@@ -174,33 +184,39 @@ async function createPlanIssues(source, config, plan) {
|
|
|
174
184
|
---
|
|
175
185
|
_Depends on: ${depRefs}_`;
|
|
176
186
|
}
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
const
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
187
|
+
try {
|
|
188
|
+
const id = await source.createIssue(
|
|
189
|
+
{
|
|
190
|
+
title: issue.title,
|
|
191
|
+
description,
|
|
192
|
+
status: config.pick_from,
|
|
193
|
+
label: primaryLabel,
|
|
194
|
+
order: issue.order,
|
|
195
|
+
parentId: plan.sourceIssueId
|
|
196
|
+
},
|
|
197
|
+
config
|
|
198
|
+
);
|
|
199
|
+
createdIds.push(id);
|
|
200
|
+
orderToId.set(issue.order, id);
|
|
201
|
+
ok(`${id}: ${issue.title}`);
|
|
202
|
+
if (source.linkDependency && issue.dependsOn.length > 0) {
|
|
203
|
+
for (const depOrder of issue.dependsOn) {
|
|
204
|
+
const depId = orderToId.get(depOrder);
|
|
205
|
+
if (depId) {
|
|
206
|
+
try {
|
|
207
|
+
await source.linkDependency(id, depId);
|
|
208
|
+
} catch (err) {
|
|
209
|
+
warn(
|
|
210
|
+
`Could not link dependency ${id} \u2192 ${depId}: ${err instanceof Error ? err.message : String(err)}`
|
|
211
|
+
);
|
|
212
|
+
}
|
|
201
213
|
}
|
|
202
214
|
}
|
|
203
215
|
}
|
|
216
|
+
} catch (err) {
|
|
217
|
+
warn(
|
|
218
|
+
`Failed to create issue "${issue.title}": ${err instanceof Error ? err.message : String(err)}`
|
|
219
|
+
);
|
|
204
220
|
}
|
|
205
221
|
}
|
|
206
222
|
return createdIds;
|
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
normalizeLabels,
|
|
12
12
|
ok,
|
|
13
13
|
warn
|
|
14
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-XLAY4P45.js";
|
|
15
15
|
import {
|
|
16
16
|
appendEntry,
|
|
17
17
|
buildGuardrailsSection,
|
|
@@ -23,10 +23,15 @@ import {
|
|
|
23
23
|
getPlanPath
|
|
24
24
|
} from "./chunk-7OCDGYDM.js";
|
|
25
25
|
import {
|
|
26
|
+
addAssignees,
|
|
27
|
+
addReviewers,
|
|
26
28
|
appendPrAttribution,
|
|
27
29
|
appendPrBody,
|
|
30
|
+
getAuthenticatedUser,
|
|
31
|
+
parseBitbucketPrUrl,
|
|
32
|
+
parseGitLabMrUrl,
|
|
28
33
|
stripProviderAttribution
|
|
29
|
-
} from "./chunk-
|
|
34
|
+
} from "./chunk-YMV4CBQE.js";
|
|
30
35
|
import {
|
|
31
36
|
formatError
|
|
32
37
|
} from "./chunk-7JT7DTSS.js";
|
|
@@ -3348,6 +3353,69 @@ async function appendPrBody2(prUrl, content) {
|
|
|
3348
3353
|
} catch {
|
|
3349
3354
|
}
|
|
3350
3355
|
}
|
|
3356
|
+
var accountIdCache = /* @__PURE__ */ new Map();
|
|
3357
|
+
async function resolveAccountIds(usernames) {
|
|
3358
|
+
const result = /* @__PURE__ */ new Map();
|
|
3359
|
+
const uncached = usernames.filter((u) => {
|
|
3360
|
+
const cached = accountIdCache.get(u);
|
|
3361
|
+
if (cached) {
|
|
3362
|
+
result.set(u, cached);
|
|
3363
|
+
return false;
|
|
3364
|
+
}
|
|
3365
|
+
return true;
|
|
3366
|
+
});
|
|
3367
|
+
if (uncached.length === 0) return result;
|
|
3368
|
+
const resolutions = await Promise.allSettled(
|
|
3369
|
+
uncached.map(async (username) => {
|
|
3370
|
+
const res = await fetch(`${API_URL3}/users/${encodeURIComponent(username)}`, {
|
|
3371
|
+
headers: { Authorization: getAuthHeader() },
|
|
3372
|
+
signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS2)
|
|
3373
|
+
});
|
|
3374
|
+
if (!res.ok) return;
|
|
3375
|
+
const data = await res.json();
|
|
3376
|
+
if (data.account_id) {
|
|
3377
|
+
accountIdCache.set(username, data.account_id);
|
|
3378
|
+
result.set(username, data.account_id);
|
|
3379
|
+
}
|
|
3380
|
+
})
|
|
3381
|
+
);
|
|
3382
|
+
return result;
|
|
3383
|
+
}
|
|
3384
|
+
async function addPrReviewers(prUrl, reviewers) {
|
|
3385
|
+
if (!reviewers.length) return;
|
|
3386
|
+
const parsed = parseBitbucketPrUrl(prUrl);
|
|
3387
|
+
if (!parsed) return;
|
|
3388
|
+
const authHeader = getAuthHeader();
|
|
3389
|
+
const accountIds = await resolveAccountIds(reviewers);
|
|
3390
|
+
const getRes = await fetch(
|
|
3391
|
+
`${API_URL3}/repositories/${parsed.workspace}/${parsed.repoSlug}/pullrequests/${parsed.id}`,
|
|
3392
|
+
{
|
|
3393
|
+
headers: { Authorization: authHeader },
|
|
3394
|
+
signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS2)
|
|
3395
|
+
}
|
|
3396
|
+
);
|
|
3397
|
+
if (!getRes.ok) throw new Error(`Bitbucket API error (${getRes.status})`);
|
|
3398
|
+
const prData = await getRes.json();
|
|
3399
|
+
const existingIds = new Set((prData.reviewers ?? []).map((r) => r.account_id));
|
|
3400
|
+
for (const [, accountId] of accountIds) {
|
|
3401
|
+
existingIds.add(accountId);
|
|
3402
|
+
}
|
|
3403
|
+
const authorId = prData.author?.account_id;
|
|
3404
|
+
if (authorId) existingIds.delete(authorId);
|
|
3405
|
+
const mergedReviewers = [...existingIds].map((account_id) => ({ account_id }));
|
|
3406
|
+
await fetch(
|
|
3407
|
+
`${API_URL3}/repositories/${parsed.workspace}/${parsed.repoSlug}/pullrequests/${parsed.id}`,
|
|
3408
|
+
{
|
|
3409
|
+
method: "PUT",
|
|
3410
|
+
headers: {
|
|
3411
|
+
Authorization: authHeader,
|
|
3412
|
+
"Content-Type": "application/json"
|
|
3413
|
+
},
|
|
3414
|
+
body: JSON.stringify({ title: prData.title, reviewers: mergedReviewers }),
|
|
3415
|
+
signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS2)
|
|
3416
|
+
}
|
|
3417
|
+
);
|
|
3418
|
+
}
|
|
3351
3419
|
|
|
3352
3420
|
// src/git/gitlab.ts
|
|
3353
3421
|
import { execa as execa3 } from "execa";
|
|
@@ -3366,6 +3434,11 @@ function formatProviderName2(providerUsed) {
|
|
|
3366
3434
|
const providerKey = providerUsed.split("/")[0] ?? providerUsed;
|
|
3367
3435
|
return PROVIDER_DISPLAY_NAMES2[providerKey] ?? providerKey;
|
|
3368
3436
|
}
|
|
3437
|
+
function getToken() {
|
|
3438
|
+
const token = process.env.GITLAB_TOKEN;
|
|
3439
|
+
if (!token) throw new Error("GITLAB_TOKEN is not set");
|
|
3440
|
+
return token;
|
|
3441
|
+
}
|
|
3369
3442
|
function buildApiBase(host) {
|
|
3370
3443
|
return `https://${host}/api/v4`;
|
|
3371
3444
|
}
|
|
@@ -3432,6 +3505,105 @@ async function appendMrBody(mrUrl, content) {
|
|
|
3432
3505
|
} catch {
|
|
3433
3506
|
}
|
|
3434
3507
|
}
|
|
3508
|
+
var authenticatedUserCache = null;
|
|
3509
|
+
async function getGitLabAuthenticatedUser() {
|
|
3510
|
+
if (authenticatedUserCache) return authenticatedUserCache;
|
|
3511
|
+
const token = getToken();
|
|
3512
|
+
const res = await fetch("https://gitlab.com/api/v4/user", {
|
|
3513
|
+
headers: { "PRIVATE-TOKEN": token },
|
|
3514
|
+
signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS3)
|
|
3515
|
+
});
|
|
3516
|
+
if (!res.ok) throw new Error(`GitLab API error (${res.status})`);
|
|
3517
|
+
const data = await res.json();
|
|
3518
|
+
authenticatedUserCache = data.username;
|
|
3519
|
+
return authenticatedUserCache;
|
|
3520
|
+
}
|
|
3521
|
+
var memberIdCache = /* @__PURE__ */ new Map();
|
|
3522
|
+
async function resolveUserIds(usernames, host, projectPath) {
|
|
3523
|
+
const result = /* @__PURE__ */ new Map();
|
|
3524
|
+
const uncached = usernames.filter((u) => {
|
|
3525
|
+
const cached = memberIdCache.get(u);
|
|
3526
|
+
if (cached !== void 0) {
|
|
3527
|
+
result.set(u, cached);
|
|
3528
|
+
return false;
|
|
3529
|
+
}
|
|
3530
|
+
return true;
|
|
3531
|
+
});
|
|
3532
|
+
if (uncached.length === 0) return result;
|
|
3533
|
+
const token = getToken();
|
|
3534
|
+
const apiBase = buildApiBase(host);
|
|
3535
|
+
const encodedPath = encodeURIComponent(projectPath);
|
|
3536
|
+
try {
|
|
3537
|
+
const membersRes = await fetch(`${apiBase}/projects/${encodedPath}/members/all?per_page=100`, {
|
|
3538
|
+
headers: { "PRIVATE-TOKEN": token },
|
|
3539
|
+
signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS3)
|
|
3540
|
+
});
|
|
3541
|
+
if (membersRes.ok) {
|
|
3542
|
+
const members = await membersRes.json();
|
|
3543
|
+
for (const m of members) {
|
|
3544
|
+
memberIdCache.set(m.username, m.id);
|
|
3545
|
+
if (uncached.includes(m.username)) {
|
|
3546
|
+
result.set(m.username, m.id);
|
|
3547
|
+
}
|
|
3548
|
+
}
|
|
3549
|
+
}
|
|
3550
|
+
} catch {
|
|
3551
|
+
}
|
|
3552
|
+
const stillMissing = uncached.filter((u) => !result.has(u));
|
|
3553
|
+
const resolutions = await Promise.allSettled(
|
|
3554
|
+
stillMissing.map(async (username) => {
|
|
3555
|
+
const res = await fetch(`${apiBase}/users?username=${encodeURIComponent(username)}`, {
|
|
3556
|
+
headers: { "PRIVATE-TOKEN": token },
|
|
3557
|
+
signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS3)
|
|
3558
|
+
});
|
|
3559
|
+
if (!res.ok) return;
|
|
3560
|
+
const users = await res.json();
|
|
3561
|
+
if (users[0]) {
|
|
3562
|
+
memberIdCache.set(users[0].username, users[0].id);
|
|
3563
|
+
result.set(username, users[0].id);
|
|
3564
|
+
}
|
|
3565
|
+
})
|
|
3566
|
+
);
|
|
3567
|
+
return result;
|
|
3568
|
+
}
|
|
3569
|
+
async function addMrReviewersAndAssignees(mrUrl, reviewerUsernames, assigneeUsernames) {
|
|
3570
|
+
if (!reviewerUsernames.length && !assigneeUsernames.length) return;
|
|
3571
|
+
const parsed = parseGitLabMrUrl(mrUrl);
|
|
3572
|
+
if (!parsed) return;
|
|
3573
|
+
const token = getToken();
|
|
3574
|
+
const apiBase = buildApiBase(parsed.host);
|
|
3575
|
+
const encodedPath = encodeURIComponent(parsed.projectPath);
|
|
3576
|
+
const allUsernames = [.../* @__PURE__ */ new Set([...reviewerUsernames, ...assigneeUsernames])];
|
|
3577
|
+
const idMap = await resolveUserIds(allUsernames, parsed.host, parsed.projectPath);
|
|
3578
|
+
const getRes = await fetch(`${apiBase}/projects/${encodedPath}/merge_requests/${parsed.iid}`, {
|
|
3579
|
+
headers: { "PRIVATE-TOKEN": token },
|
|
3580
|
+
signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS3)
|
|
3581
|
+
});
|
|
3582
|
+
if (!getRes.ok) throw new Error(`GitLab API error (${getRes.status})`);
|
|
3583
|
+
const mrData = await getRes.json();
|
|
3584
|
+
const existingReviewerIds = new Set((mrData.reviewers ?? []).map((r) => r.id));
|
|
3585
|
+
const existingAssigneeIds = new Set((mrData.assignees ?? []).map((a) => a.id));
|
|
3586
|
+
for (const username of reviewerUsernames) {
|
|
3587
|
+
const id = idMap.get(username);
|
|
3588
|
+
if (id) existingReviewerIds.add(id);
|
|
3589
|
+
}
|
|
3590
|
+
for (const username of assigneeUsernames) {
|
|
3591
|
+
const id = idMap.get(username);
|
|
3592
|
+
if (id) existingAssigneeIds.add(id);
|
|
3593
|
+
}
|
|
3594
|
+
const body = {};
|
|
3595
|
+
if (reviewerUsernames.length) body.reviewer_ids = [...existingReviewerIds];
|
|
3596
|
+
if (assigneeUsernames.length) body.assignee_ids = [...existingAssigneeIds];
|
|
3597
|
+
await fetch(`${apiBase}/projects/${encodedPath}/merge_requests/${parsed.iid}`, {
|
|
3598
|
+
method: "PUT",
|
|
3599
|
+
headers: {
|
|
3600
|
+
"PRIVATE-TOKEN": token,
|
|
3601
|
+
"Content-Type": "application/json"
|
|
3602
|
+
},
|
|
3603
|
+
body: JSON.stringify(body),
|
|
3604
|
+
signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS3)
|
|
3605
|
+
});
|
|
3606
|
+
}
|
|
3435
3607
|
|
|
3436
3608
|
// src/git/platform.ts
|
|
3437
3609
|
async function appendPlatformAttribution(prUrl, providerUsed, platform2) {
|
|
@@ -3508,6 +3680,52 @@ function buildPrCreateInstruction(platform2, targetBranch) {
|
|
|
3508
3680
|
\`gh pr create --title "<conventional-commit-title>" --body "<markdown-summary>"${base}\`
|
|
3509
3681
|
Capture the PR URL from the output.`;
|
|
3510
3682
|
}
|
|
3683
|
+
async function resolveAuthenticatedUser(platform2) {
|
|
3684
|
+
try {
|
|
3685
|
+
if (platform2 === "gitlab") {
|
|
3686
|
+
return await getGitLabAuthenticatedUser();
|
|
3687
|
+
}
|
|
3688
|
+
if (platform2 === "bitbucket") {
|
|
3689
|
+
return null;
|
|
3690
|
+
}
|
|
3691
|
+
return await getAuthenticatedUser(platform2);
|
|
3692
|
+
} catch {
|
|
3693
|
+
return null;
|
|
3694
|
+
}
|
|
3695
|
+
}
|
|
3696
|
+
async function applyPrReviewersAndAssignees(prUrl, prConfig, platform2) {
|
|
3697
|
+
if (!prConfig) return;
|
|
3698
|
+
const rawReviewers = prConfig.reviewers ?? [];
|
|
3699
|
+
const rawAssignees = prConfig.assignees ?? [];
|
|
3700
|
+
if (!rawReviewers.length && !rawAssignees.length) return;
|
|
3701
|
+
try {
|
|
3702
|
+
const hasSelf = rawReviewers.includes("self") || rawAssignees.includes("self");
|
|
3703
|
+
let selfUsername = null;
|
|
3704
|
+
if (hasSelf) {
|
|
3705
|
+
selfUsername = await resolveAuthenticatedUser(platform2);
|
|
3706
|
+
}
|
|
3707
|
+
const reviewers = rawReviewers.filter((r) => r !== "self").filter((r) => r !== selfUsername);
|
|
3708
|
+
const assignees = rawAssignees.map((a) => {
|
|
3709
|
+
if (a === "self") return selfUsername;
|
|
3710
|
+
return a;
|
|
3711
|
+
}).filter((a) => a !== null);
|
|
3712
|
+
if (selfUsername && rawReviewers.includes("self")) {
|
|
3713
|
+
warn("Filtered 'self' from reviewers \u2014 cannot request review from yourself");
|
|
3714
|
+
}
|
|
3715
|
+
if (platform2 === "gitlab") {
|
|
3716
|
+
await addMrReviewersAndAssignees(prUrl, reviewers, assignees);
|
|
3717
|
+
} else if (platform2 === "bitbucket") {
|
|
3718
|
+
if (reviewers.length) await addPrReviewers(prUrl, reviewers);
|
|
3719
|
+
} else {
|
|
3720
|
+
const tasks = [];
|
|
3721
|
+
if (reviewers.length) tasks.push(addReviewers(prUrl, reviewers));
|
|
3722
|
+
if (assignees.length) tasks.push(addAssignees(prUrl, assignees));
|
|
3723
|
+
await Promise.allSettled(tasks);
|
|
3724
|
+
}
|
|
3725
|
+
} catch (err) {
|
|
3726
|
+
warn(`Failed to add reviewers/assignees: ${formatError(err)}`);
|
|
3727
|
+
}
|
|
3728
|
+
}
|
|
3511
3729
|
|
|
3512
3730
|
// src/prompt.ts
|
|
3513
3731
|
function detectPackageManager(cwd) {
|
|
@@ -4109,6 +4327,7 @@ export {
|
|
|
4109
4327
|
appendPlatformAttribution,
|
|
4110
4328
|
appendPlatformProofOfWork,
|
|
4111
4329
|
appendPlatformSpecCompliance,
|
|
4330
|
+
applyPrReviewersAndAssignees,
|
|
4112
4331
|
detectPackageManager,
|
|
4113
4332
|
detectTestRunner,
|
|
4114
4333
|
buildImplementPrompt,
|
|
@@ -772,6 +772,7 @@ function registerBellListeners(bellEnabled) {
|
|
|
772
772
|
function useKanbanState(bellEnabled, initialCards = []) {
|
|
773
773
|
const [cards, setCards] = useState(initialCards);
|
|
774
774
|
const [isEmpty, setIsEmpty] = useState(false);
|
|
775
|
+
const [isFetching, setIsFetching] = useState(initialCards.length === 0);
|
|
775
776
|
const [isWatching, setIsWatching] = useState(false);
|
|
776
777
|
const [isWatchPrompt, setIsWatchPrompt] = useState(false);
|
|
777
778
|
const [workComplete, setWorkComplete] = useState(
|
|
@@ -780,6 +781,7 @@ function useKanbanState(bellEnabled, initialCards = []) {
|
|
|
780
781
|
const [modelInUse, setModelInUse] = useState(null);
|
|
781
782
|
useEffect(() => {
|
|
782
783
|
const onQueued = (issue) => {
|
|
784
|
+
setIsFetching(false);
|
|
783
785
|
setCards((prev) => {
|
|
784
786
|
if (prev.some((c) => c.id === issue.id)) return prev;
|
|
785
787
|
const maxOrder = prev.reduce((max, c) => Math.max(max, c.queueOrder ?? 0), 0);
|
|
@@ -933,10 +935,16 @@ function useKanbanState(bellEnabled, initialCards = []) {
|
|
|
933
935
|
kanbanEmitter.on("issue:output", onOutput);
|
|
934
936
|
const onModelChanged = (model) => setModelInUse(model);
|
|
935
937
|
kanbanEmitter.on("provider:model-changed", onModelChanged);
|
|
936
|
-
const onEmpty = () =>
|
|
938
|
+
const onEmpty = () => {
|
|
939
|
+
setIsEmpty(true);
|
|
940
|
+
setIsFetching(false);
|
|
941
|
+
};
|
|
937
942
|
const onResumed = () => setIsEmpty(false);
|
|
938
943
|
const onComplete = (data) => setWorkComplete(data);
|
|
939
|
-
const onWatching = () =>
|
|
944
|
+
const onWatching = () => {
|
|
945
|
+
setIsWatching(true);
|
|
946
|
+
setIsFetching(false);
|
|
947
|
+
};
|
|
940
948
|
const onWatchResume = () => setIsWatching(false);
|
|
941
949
|
const onWatchPrompt = () => {
|
|
942
950
|
setIsWatchPrompt(true);
|
|
@@ -992,7 +1000,7 @@ function useKanbanState(bellEnabled, initialCards = []) {
|
|
|
992
1000
|
}
|
|
993
1001
|
}
|
|
994
1002
|
}, []);
|
|
995
|
-
return { cards, isEmpty, isWatching, isWatchPrompt, workComplete, modelInUse };
|
|
1003
|
+
return { cards, isEmpty, isFetching, isWatching, isWatchPrompt, workComplete, modelInUse };
|
|
996
1004
|
}
|
|
997
1005
|
|
|
998
1006
|
export {
|
|
@@ -25,7 +25,32 @@ function stripProviderAttribution(body) {
|
|
|
25
25
|
return result.trimEnd();
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
+
// src/git/url-parser.ts
|
|
29
|
+
function parseGitHubPrUrl(prUrl) {
|
|
30
|
+
const match = prUrl.match(/github\.com\/([^/]+)\/([^/]+)\/pull\/(\d+)/);
|
|
31
|
+
if (!match) return null;
|
|
32
|
+
const num = Number.parseInt(match[3] ?? "0", 10);
|
|
33
|
+
if (num <= 0) return null;
|
|
34
|
+
return { owner: match[1] ?? "", repo: match[2] ?? "", number: num };
|
|
35
|
+
}
|
|
36
|
+
function parseGitLabMrUrl(mrUrl) {
|
|
37
|
+
const match = mrUrl.match(/https?:\/\/([^/]+)\/(.+?)\/-\/merge_requests\/(\d+)/);
|
|
38
|
+
if (!match) return null;
|
|
39
|
+
const iid = Number.parseInt(match[3] ?? "0", 10);
|
|
40
|
+
if (iid <= 0) return null;
|
|
41
|
+
return { host: match[1] ?? "", projectPath: match[2] ?? "", iid };
|
|
42
|
+
}
|
|
43
|
+
function parseBitbucketPrUrl(prUrl) {
|
|
44
|
+
const match = prUrl.match(/bitbucket\.org\/([^/]+)\/([^/]+)\/pull-requests\/(\d+)/);
|
|
45
|
+
if (!match) return null;
|
|
46
|
+
const id = Number.parseInt(match[3] ?? "0", 10);
|
|
47
|
+
if (id <= 0) return null;
|
|
48
|
+
return { workspace: match[1] ?? "", repoSlug: match[2] ?? "", id };
|
|
49
|
+
}
|
|
50
|
+
|
|
28
51
|
// src/git/github.ts
|
|
52
|
+
var API_URL = "https://api.github.com";
|
|
53
|
+
var REQUEST_TIMEOUT_MS = 3e4;
|
|
29
54
|
async function isGhCliAvailable() {
|
|
30
55
|
try {
|
|
31
56
|
await execa("gh", ["auth", "status"]);
|
|
@@ -34,6 +59,11 @@ async function isGhCliAvailable() {
|
|
|
34
59
|
return false;
|
|
35
60
|
}
|
|
36
61
|
}
|
|
62
|
+
function getToken() {
|
|
63
|
+
const token = process.env.GITHUB_TOKEN;
|
|
64
|
+
if (!token) throw new Error("GITHUB_TOKEN is not set");
|
|
65
|
+
return token;
|
|
66
|
+
}
|
|
37
67
|
var PROVIDER_DISPLAY_NAMES = {
|
|
38
68
|
claude: "Claude Code",
|
|
39
69
|
gemini: "Gemini CLI",
|
|
@@ -107,10 +137,73 @@ async function appendPrBody(prUrl, content) {
|
|
|
107
137
|
} catch {
|
|
108
138
|
}
|
|
109
139
|
}
|
|
140
|
+
var authenticatedUserCache = null;
|
|
141
|
+
async function getAuthenticatedUser(method = "cli") {
|
|
142
|
+
if (authenticatedUserCache) return authenticatedUserCache;
|
|
143
|
+
if (method === "cli" || method === "token") {
|
|
144
|
+
if (method === "cli") {
|
|
145
|
+
const { stdout } = await execa("gh", ["api", "/user", "--jq", ".login"]);
|
|
146
|
+
authenticatedUserCache = stdout.trim();
|
|
147
|
+
} else {
|
|
148
|
+
const res = await fetch(`${API_URL}/user`, {
|
|
149
|
+
headers: { Authorization: `Bearer ${getToken()}`, Accept: "application/vnd.github+json" },
|
|
150
|
+
signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS)
|
|
151
|
+
});
|
|
152
|
+
if (!res.ok) throw new Error(`GitHub API error (${res.status})`);
|
|
153
|
+
const data = await res.json();
|
|
154
|
+
authenticatedUserCache = data.login;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
if (!authenticatedUserCache) throw new Error("Could not resolve authenticated GitHub user");
|
|
158
|
+
return authenticatedUserCache;
|
|
159
|
+
}
|
|
160
|
+
async function addReviewers(prUrl, reviewers) {
|
|
161
|
+
if (!reviewers.length) return;
|
|
162
|
+
const parsed = parseGitHubPrUrl(prUrl);
|
|
163
|
+
if (!parsed) return;
|
|
164
|
+
await execa(
|
|
165
|
+
"gh",
|
|
166
|
+
[
|
|
167
|
+
"api",
|
|
168
|
+
"--method",
|
|
169
|
+
"POST",
|
|
170
|
+
`/repos/${parsed.owner}/${parsed.repo}/pulls/${parsed.number}/requested_reviewers`,
|
|
171
|
+
"--input",
|
|
172
|
+
"-"
|
|
173
|
+
],
|
|
174
|
+
{
|
|
175
|
+
input: JSON.stringify({ reviewers })
|
|
176
|
+
}
|
|
177
|
+
);
|
|
178
|
+
}
|
|
179
|
+
async function addAssignees(prUrl, assignees) {
|
|
180
|
+
if (!assignees.length) return;
|
|
181
|
+
const parsed = parseGitHubPrUrl(prUrl);
|
|
182
|
+
if (!parsed) return;
|
|
183
|
+
await execa(
|
|
184
|
+
"gh",
|
|
185
|
+
[
|
|
186
|
+
"api",
|
|
187
|
+
"--method",
|
|
188
|
+
"POST",
|
|
189
|
+
`/repos/${parsed.owner}/${parsed.repo}/issues/${parsed.number}/assignees`,
|
|
190
|
+
"--input",
|
|
191
|
+
"-"
|
|
192
|
+
],
|
|
193
|
+
{
|
|
194
|
+
input: JSON.stringify({ assignees })
|
|
195
|
+
}
|
|
196
|
+
);
|
|
197
|
+
}
|
|
110
198
|
|
|
111
199
|
export {
|
|
112
200
|
stripProviderAttribution,
|
|
201
|
+
parseGitLabMrUrl,
|
|
202
|
+
parseBitbucketPrUrl,
|
|
113
203
|
isGhCliAvailable,
|
|
114
204
|
appendPrAttribution,
|
|
115
|
-
appendPrBody
|
|
205
|
+
appendPrBody,
|
|
206
|
+
getAuthenticatedUser,
|
|
207
|
+
addReviewers,
|
|
208
|
+
addAssignees
|
|
116
209
|
};
|
|
@@ -12,8 +12,8 @@ import {
|
|
|
12
12
|
getVersion,
|
|
13
13
|
isCursorFreePlan,
|
|
14
14
|
verifyPlatformCredential
|
|
15
|
-
} from "./chunk-
|
|
16
|
-
import "./chunk-
|
|
15
|
+
} from "./chunk-R6D5VH65.js";
|
|
16
|
+
import "./chunk-YMV4CBQE.js";
|
|
17
17
|
import "./chunk-7JT7DTSS.js";
|
|
18
18
|
export {
|
|
19
19
|
detectDefaultBranch,
|
package/dist/index.js
CHANGED
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
parseStructuredOutput,
|
|
8
8
|
runPlanWizard,
|
|
9
9
|
savePlan
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-6XC7GQUI.js";
|
|
11
11
|
import {
|
|
12
12
|
detectDefaultBranch,
|
|
13
13
|
detectGitRepos,
|
|
@@ -18,7 +18,7 @@ import {
|
|
|
18
18
|
getMissingEnvVars,
|
|
19
19
|
getVersion,
|
|
20
20
|
isCursorFreePlan
|
|
21
|
-
} from "./chunk-
|
|
21
|
+
} from "./chunk-R6D5VH65.js";
|
|
22
22
|
import {
|
|
23
23
|
getCachedUpdateInfo
|
|
24
24
|
} from "./chunk-7CIXBENY.js";
|
|
@@ -36,7 +36,7 @@ import {
|
|
|
36
36
|
runLoop,
|
|
37
37
|
saveConfig,
|
|
38
38
|
validateConfig
|
|
39
|
-
} from "./chunk-
|
|
39
|
+
} from "./chunk-23Z5BBRT.js";
|
|
40
40
|
import {
|
|
41
41
|
buildContextMdBlock,
|
|
42
42
|
createProvider,
|
|
@@ -48,7 +48,7 @@ import {
|
|
|
48
48
|
readContext,
|
|
49
49
|
resolveModels,
|
|
50
50
|
runWithFallback
|
|
51
|
-
} from "./chunk-
|
|
51
|
+
} from "./chunk-SC222P5D.js";
|
|
52
52
|
import {
|
|
53
53
|
banner,
|
|
54
54
|
error,
|
|
@@ -58,7 +58,7 @@ import {
|
|
|
58
58
|
setLogLevel,
|
|
59
59
|
setOutputMode,
|
|
60
60
|
updateNotice
|
|
61
|
-
} from "./chunk-
|
|
61
|
+
} from "./chunk-XLAY4P45.js";
|
|
62
62
|
import "./chunk-3EOEDL3T.js";
|
|
63
63
|
import {
|
|
64
64
|
getKanbanStatePath,
|
|
@@ -67,7 +67,7 @@ import {
|
|
|
67
67
|
import "./chunk-72CYGBT4.js";
|
|
68
68
|
import {
|
|
69
69
|
isGhCliAvailable
|
|
70
|
-
} from "./chunk-
|
|
70
|
+
} from "./chunk-YMV4CBQE.js";
|
|
71
71
|
import {
|
|
72
72
|
formatError
|
|
73
73
|
} from "./chunk-7JT7DTSS.js";
|
|
@@ -1001,6 +1001,23 @@ function runAdvancedChecks(config2) {
|
|
|
1001
1001
|
suggestion: existsSync(contextPath) ? void 0 : 'Run "lisa context refresh" to generate .lisa/context.md for better planning.',
|
|
1002
1002
|
category: "advanced"
|
|
1003
1003
|
});
|
|
1004
|
+
if (config2.pr) {
|
|
1005
|
+
if (config2.platform === "bitbucket" && config2.pr.assignees?.length) {
|
|
1006
|
+
results.push({
|
|
1007
|
+
passed: false,
|
|
1008
|
+
label: "PR assignees compatible with platform",
|
|
1009
|
+
suggestion: "Bitbucket does not support PR assignees. Remove pr.assignees or switch platform.",
|
|
1010
|
+
category: "advanced"
|
|
1011
|
+
});
|
|
1012
|
+
}
|
|
1013
|
+
if (config2.pr.reviewers?.length || config2.pr.assignees?.length) {
|
|
1014
|
+
results.push({
|
|
1015
|
+
passed: true,
|
|
1016
|
+
label: `PR config: ${config2.pr.reviewers?.length ?? 0} reviewer(s), ${config2.pr.assignees?.length ?? 0} assignee(s)`,
|
|
1017
|
+
category: "advanced"
|
|
1018
|
+
});
|
|
1019
|
+
}
|
|
1020
|
+
}
|
|
1004
1021
|
return results;
|
|
1005
1022
|
}
|
|
1006
1023
|
var doctor = defineCommand3({
|
|
@@ -1527,7 +1544,7 @@ async function reviewAndCreate(plan2, planPath, opts) {
|
|
|
1527
1544
|
log("Run `lisa run` when ready.");
|
|
1528
1545
|
return;
|
|
1529
1546
|
}
|
|
1530
|
-
const { runLoop: runLoop2 } = await import("./loop-
|
|
1547
|
+
const { runLoop: runLoop2 } = await import("./loop-XEJHKZHO.js");
|
|
1531
1548
|
await runLoop2(config2, {
|
|
1532
1549
|
once: false,
|
|
1533
1550
|
watch: false,
|
|
@@ -1891,7 +1908,7 @@ async function executeRun(args) {
|
|
|
1891
1908
|
if (isTTY) {
|
|
1892
1909
|
const { render } = await import("ink");
|
|
1893
1910
|
const { createElement } = await import("react");
|
|
1894
|
-
const { KanbanApp } = await import("./kanban-
|
|
1911
|
+
const { KanbanApp } = await import("./kanban-X4OR4ZFI.js");
|
|
1895
1912
|
const demoConfig = {
|
|
1896
1913
|
provider: "claude",
|
|
1897
1914
|
source: "linear",
|
|
@@ -1991,7 +2008,7 @@ Add them to your ${shell} and run: source ${shell}`));
|
|
|
1991
2008
|
const initialCards = persistence.load();
|
|
1992
2009
|
persistedCards = initialCards;
|
|
1993
2010
|
persistence.start();
|
|
1994
|
-
const { registerPlanBridge } = await import("./tui-bridge-
|
|
2011
|
+
const { registerPlanBridge } = await import("./tui-bridge-G6BBFZFH.js");
|
|
1995
2012
|
const cleanupPlan = registerPlanBridge(merged);
|
|
1996
2013
|
onBeforeExit = () => {
|
|
1997
2014
|
persistence.stop();
|
|
@@ -1999,7 +2016,7 @@ Add them to your ${shell} and run: source ${shell}`));
|
|
|
1999
2016
|
};
|
|
2000
2017
|
const { render } = await import("ink");
|
|
2001
2018
|
const { createElement } = await import("react");
|
|
2002
|
-
const { KanbanApp } = await import("./kanban-
|
|
2019
|
+
const { KanbanApp } = await import("./kanban-X4OR4ZFI.js");
|
|
2003
2020
|
render(createElement(KanbanApp, { config: merged, initialCards }), { exitOnCtrlC: false });
|
|
2004
2021
|
}
|
|
2005
2022
|
await runLoop(merged, {
|
|
@@ -2047,6 +2064,7 @@ var status = defineCommand9({
|
|
|
2047
2064
|
label: formatLabels(config2.source_config),
|
|
2048
2065
|
scope: config2.source_config.scope,
|
|
2049
2066
|
platform: config2.platform,
|
|
2067
|
+
pr: config2.pr ?? null,
|
|
2050
2068
|
logsDir,
|
|
2051
2069
|
sessionCount
|
|
2052
2070
|
},
|
|
@@ -2070,6 +2088,17 @@ var status = defineCommand9({
|
|
|
2070
2088
|
console.log(` Pick from: ${pc7.bold(config2.source_config.pick_from)}`);
|
|
2071
2089
|
console.log(` In progress: ${pc7.bold(config2.source_config.in_progress)}`);
|
|
2072
2090
|
console.log(` Done: ${pc7.bold(config2.source_config.done)}`);
|
|
2091
|
+
if (config2.pr) {
|
|
2092
|
+
const reviewers = config2.pr.reviewers?.join(", ") ?? pc7.dim("none");
|
|
2093
|
+
const assignees = config2.pr.assignees?.join(", ") ?? pc7.dim("none");
|
|
2094
|
+
console.log(` Reviewers: ${pc7.bold(reviewers)}`);
|
|
2095
|
+
console.log(` Assignees: ${pc7.bold(assignees)}`);
|
|
2096
|
+
if (config2.platform === "bitbucket" && config2.pr.assignees?.length) {
|
|
2097
|
+
console.log(
|
|
2098
|
+
` ${pc7.yellow("!")} Bitbucket does not support assignees \u2014 only reviewers will be added`
|
|
2099
|
+
);
|
|
2100
|
+
}
|
|
2101
|
+
}
|
|
2073
2102
|
console.log(` Logs: ${pc7.dim(logsDir)}`);
|
|
2074
2103
|
if (sessionCount > 0) {
|
|
2075
2104
|
console.log(`
|
|
@@ -2111,7 +2140,7 @@ process.on("unhandledRejection", (err) => {
|
|
|
2111
2140
|
}
|
|
2112
2141
|
process.exit(1);
|
|
2113
2142
|
});
|
|
2114
|
-
import("./detection-
|
|
2143
|
+
import("./detection-H5QJR5XI.js").then(
|
|
2115
2144
|
({ getVersion: getVersion2 }) => import("./version-D2YFKS7Q.js").then(({ checkForUpdate }) => checkForUpdate(getVersion2()))
|
|
2116
2145
|
);
|
|
2117
2146
|
runCli();
|
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
import {
|
|
6
6
|
kanbanEmitter,
|
|
7
7
|
useKanbanState
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-XLAY4P45.js";
|
|
9
9
|
import {
|
|
10
10
|
resetTitle,
|
|
11
11
|
startSpinner,
|
|
@@ -18,6 +18,7 @@ import { useEffect as useEffect5, useState as useState6 } from "react";
|
|
|
18
18
|
|
|
19
19
|
// src/ui/board.tsx
|
|
20
20
|
import { Box as Box3, Text as Text3 } from "ink";
|
|
21
|
+
import Spinner2 from "ink-spinner";
|
|
21
22
|
|
|
22
23
|
// src/ui/column.tsx
|
|
23
24
|
import { Box as Box2, Text as Text2 } from "ink";
|
|
@@ -130,9 +131,12 @@ function Card({
|
|
|
130
131
|
card.column === "in_progress" ? /* @__PURE__ */ jsxs(Box, { flexDirection: "row", marginTop: 0, children: [
|
|
131
132
|
isPausedInProgress ? /* @__PURE__ */ jsx(Text, { color: "gray", children: "\u23F8" }) : /* @__PURE__ */ jsx(Text, { color: "yellow", children: /* @__PURE__ */ jsx(Spinner, { type: "dots" }) }),
|
|
132
133
|
/* @__PURE__ */ jsx(Text, { color: isPausedInProgress ? "gray" : "yellow", dimColor: isPausedInProgress, children: elapsedMs !== null ? ` ${formatElapsed(elapsedMs)}` : "" })
|
|
133
|
-
] }) : card.column === "done" && card.startedAt !== void 0 && card.finishedAt !== void 0 ? /* @__PURE__ */ jsxs(
|
|
134
|
-
"
|
|
135
|
-
|
|
134
|
+
] }) : card.column === "done" && card.startedAt !== void 0 && card.finishedAt !== void 0 ? /* @__PURE__ */ jsxs(Box, { flexDirection: "row", justifyContent: "space-between", children: [
|
|
135
|
+
/* @__PURE__ */ jsxs(Text, { color: card.merged ? "magenta" : "green", children: [
|
|
136
|
+
"\u2714 ",
|
|
137
|
+
formatElapsed(card.finishedAt - card.startedAt)
|
|
138
|
+
] }),
|
|
139
|
+
card.prUrls.length > 0 && /* @__PURE__ */ jsx(Text, { color: card.merged ? "magenta" : "yellow", dimColor: true, children: card.merged ? "PR\u2714" : "PR" })
|
|
136
140
|
] }) : card.killed ? /* @__PURE__ */ jsx(Text, { color: "red", children: "KILLED" }) : card.skipped ? /* @__PURE__ */ jsx(Text, { color: "gray", children: "SKIPPED" }) : card.hasError && !card.killed && !card.skipped ? /* @__PURE__ */ jsx(Text, { color: "red", children: "FAILED" }) : (
|
|
137
141
|
// Empty row for backlog and done-without-timing — maintains CARD_HEIGHT
|
|
138
142
|
/* @__PURE__ */ jsx(Text, { children: " ".repeat(cardWidth) })
|
|
@@ -254,6 +258,7 @@ function Board({
|
|
|
254
258
|
columns,
|
|
255
259
|
labels,
|
|
256
260
|
isEmpty,
|
|
261
|
+
isFetching = false,
|
|
257
262
|
isWatching = false,
|
|
258
263
|
isWatchPrompt = false,
|
|
259
264
|
workComplete,
|
|
@@ -262,6 +267,28 @@ function Board({
|
|
|
262
267
|
paused = false
|
|
263
268
|
}) {
|
|
264
269
|
const { backlog, inProgress, done } = columns;
|
|
270
|
+
if (isFetching) {
|
|
271
|
+
return /* @__PURE__ */ jsx3(Box3, { flexGrow: 1, alignItems: "center", justifyContent: "center", children: /* @__PURE__ */ jsxs3(
|
|
272
|
+
Box3,
|
|
273
|
+
{
|
|
274
|
+
flexDirection: "column",
|
|
275
|
+
borderStyle: "single",
|
|
276
|
+
borderColor: "yellow",
|
|
277
|
+
paddingX: 3,
|
|
278
|
+
paddingY: 1,
|
|
279
|
+
children: [
|
|
280
|
+
/* @__PURE__ */ jsxs3(Box3, { flexDirection: "row", children: [
|
|
281
|
+
/* @__PURE__ */ jsx3(Text3, { color: "yellow", children: /* @__PURE__ */ jsx3(Spinner2, { type: "dots" }) }),
|
|
282
|
+
/* @__PURE__ */ jsx3(Text3, { color: "yellow", bold: true, children: " FETCHING ISSUES..." })
|
|
283
|
+
] }),
|
|
284
|
+
/* @__PURE__ */ jsx3(Box3, { height: 1 }),
|
|
285
|
+
/* @__PURE__ */ jsx3(Text3, { color: "white", dimColor: true, children: "Querying the issue tracker for matching items." }),
|
|
286
|
+
/* @__PURE__ */ jsx3(Box3, { height: 1 }),
|
|
287
|
+
/* @__PURE__ */ jsx3(Text3, { color: "gray", dimColor: true, children: "Press [q] to quit" })
|
|
288
|
+
]
|
|
289
|
+
}
|
|
290
|
+
) });
|
|
291
|
+
}
|
|
265
292
|
if (isWatching) {
|
|
266
293
|
return /* @__PURE__ */ jsx3(Box3, { flexGrow: 1, alignItems: "center", justifyContent: "center", children: /* @__PURE__ */ jsxs3(
|
|
267
294
|
Box3,
|
|
@@ -375,7 +402,7 @@ function Board({
|
|
|
375
402
|
// src/ui/detail.tsx
|
|
376
403
|
import { exec } from "child_process";
|
|
377
404
|
import { Box as Box4, Text as Text4, useInput } from "ink";
|
|
378
|
-
import
|
|
405
|
+
import Spinner3 from "ink-spinner";
|
|
379
406
|
import { useEffect as useEffect3, useMemo, useRef, useState as useState3 } from "react";
|
|
380
407
|
|
|
381
408
|
// src/output/line-color.ts
|
|
@@ -390,7 +417,7 @@ function logLineColor(line) {
|
|
|
390
417
|
}
|
|
391
418
|
|
|
392
419
|
// src/ui/detail.tsx
|
|
393
|
-
import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
420
|
+
import { Fragment as Fragment2, jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
394
421
|
function openUrl(url) {
|
|
395
422
|
const platform = process.platform;
|
|
396
423
|
let command;
|
|
@@ -443,7 +470,7 @@ function statusLabel(column, hasError, killed, skipped, merged) {
|
|
|
443
470
|
if (column === "done") return { text: "DONE", color: "green" };
|
|
444
471
|
return { text: "QUEUED", color: "white" };
|
|
445
472
|
}
|
|
446
|
-
function IssueDetail({ card, onBack }) {
|
|
473
|
+
function IssueDetail({ card, onBack, reviewers, assignees }) {
|
|
447
474
|
const [now, setNow] = useState3(Date.now());
|
|
448
475
|
const [logScrollOffset, setLogScrollOffset] = useState3(0);
|
|
449
476
|
const [userScrolled, setUserScrolled] = useState3(false);
|
|
@@ -488,7 +515,9 @@ function IssueDetail({ card, onBack }) {
|
|
|
488
515
|
const maxLineWidth = Math.max(1, terminalCols - SIDEBAR_TOTAL_WIDTH4 - 4);
|
|
489
516
|
const prCount = card.prUrls.length > 0 ? card.prUrls.length : 0;
|
|
490
517
|
const logFileRow = card.logFile ? 1 : 0;
|
|
491
|
-
const
|
|
518
|
+
const hasPrMeta = card.column === "done" && (reviewers?.length || assignees?.length);
|
|
519
|
+
const prMetaRow = hasPrMeta ? 1 : 0;
|
|
520
|
+
const headerOverhead = 6 + prCount + logFileRow + prMetaRow;
|
|
492
521
|
const bodyRows = Math.max(1, terminalRows - headerOverhead);
|
|
493
522
|
const lines = useMemo(() => processOutputLines(card.outputLog), [card.outputLog]);
|
|
494
523
|
const startLine = Math.max(0, lines.length - bodyRows - logScrollOffset);
|
|
@@ -530,7 +559,7 @@ function IssueDetail({ card, onBack }) {
|
|
|
530
559
|
] }),
|
|
531
560
|
/* @__PURE__ */ jsxs4(Box4, { flexDirection: "row", children: [
|
|
532
561
|
isRunning && elapsedDisplay && /* @__PURE__ */ jsxs4(Box4, { flexDirection: "row", marginRight: 2, children: [
|
|
533
|
-
/* @__PURE__ */ jsx4(Text4, { color: "yellow", children: /* @__PURE__ */ jsx4(
|
|
562
|
+
/* @__PURE__ */ jsx4(Text4, { color: "yellow", children: /* @__PURE__ */ jsx4(Spinner3, { type: "dots" }) }),
|
|
534
563
|
/* @__PURE__ */ jsx4(Text4, { color: "yellow", bold: true, children: ` ${elapsedDisplay}` })
|
|
535
564
|
] }),
|
|
536
565
|
isPausedInProgress && elapsedDisplay && /* @__PURE__ */ jsxs4(Box4, { flexDirection: "row", marginRight: 2, children: [
|
|
@@ -548,6 +577,17 @@ function IssueDetail({ card, onBack }) {
|
|
|
548
577
|
/* @__PURE__ */ jsx4(Text4, { color: "yellow", dimColor: true, children: card.prUrls.length === 1 ? "PR: " : `PR ${i + 1}: ` }),
|
|
549
578
|
/* @__PURE__ */ jsx4(Text4, { color: "yellow", wrap: "truncate", children: hyperlink(url, truncateLine(url, maxLineWidth - 5)) })
|
|
550
579
|
] }, url)),
|
|
580
|
+
hasPrMeta && /* @__PURE__ */ jsxs4(Box4, { marginTop: 0, flexDirection: "row", children: [
|
|
581
|
+
reviewers?.length ? /* @__PURE__ */ jsxs4(Fragment2, { children: [
|
|
582
|
+
/* @__PURE__ */ jsx4(Text4, { color: "cyan", dimColor: true, children: "REVIEWERS: " }),
|
|
583
|
+
/* @__PURE__ */ jsx4(Text4, { color: "cyan", children: reviewers.join(", ") })
|
|
584
|
+
] }) : null,
|
|
585
|
+
reviewers?.length && assignees?.length ? /* @__PURE__ */ jsx4(Text4, { color: "gray", dimColor: true, children: " \u2502 " }) : null,
|
|
586
|
+
assignees?.length ? /* @__PURE__ */ jsxs4(Fragment2, { children: [
|
|
587
|
+
/* @__PURE__ */ jsx4(Text4, { color: "green", dimColor: true, children: "ASSIGNEES: " }),
|
|
588
|
+
/* @__PURE__ */ jsx4(Text4, { color: "green", children: assignees.map((a) => a === "self" ? "you" : a).join(", ") })
|
|
589
|
+
] }) : null
|
|
590
|
+
] }),
|
|
551
591
|
card.logFile && /* @__PURE__ */ jsx4(Box4, { marginTop: 0, children: /* @__PURE__ */ jsx4(Text4, { color: "gray", dimColor: true, wrap: "truncate", children: `LOG: ${truncateLine(card.logFile, maxLineWidth - 5)}` }) }),
|
|
552
592
|
/* @__PURE__ */ jsx4(Box4, { children: /* @__PURE__ */ jsx4(Text4, { color: "yellow", dimColor: true, children: separator }) }),
|
|
553
593
|
/* @__PURE__ */ jsxs4(Box4, { flexDirection: "row", justifyContent: "space-between", children: [
|
|
@@ -559,7 +599,7 @@ function IssueDetail({ card, onBack }) {
|
|
|
559
599
|
!userScrolled && totalLines > bodyRows && /* @__PURE__ */ jsx4(Text4, { color: "gray", dimColor: true, children: "live" })
|
|
560
600
|
] }),
|
|
561
601
|
/* @__PURE__ */ jsx4(Box4, { height: bodyRows, flexDirection: "column", overflow: "hidden", children: card.outputLog.length === 0 ? /* @__PURE__ */ jsxs4(Box4, { flexDirection: "row", marginTop: 1, children: [
|
|
562
|
-
/* @__PURE__ */ jsx4(Text4, { color: "yellow", children: /* @__PURE__ */ jsx4(
|
|
602
|
+
/* @__PURE__ */ jsx4(Text4, { color: "yellow", children: /* @__PURE__ */ jsx4(Spinner3, { type: "dots" }) }),
|
|
563
603
|
/* @__PURE__ */ jsx4(Text4, { color: "gray", dimColor: true, children: " Waiting for provider output..." })
|
|
564
604
|
] }) : visibleLines.map((line, i) => {
|
|
565
605
|
const color = logLineColor(line);
|
|
@@ -575,7 +615,7 @@ function IssueDetail({ card, onBack }) {
|
|
|
575
615
|
|
|
576
616
|
// src/ui/plan-chat.tsx
|
|
577
617
|
import { Box as Box5, Text as Text5, useInput as useInput2 } from "ink";
|
|
578
|
-
import
|
|
618
|
+
import Spinner4 from "ink-spinner";
|
|
579
619
|
import { useEffect as useEffect4, useState as useState4 } from "react";
|
|
580
620
|
import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
581
621
|
var SIDEBAR_TOTAL_WIDTH = 30;
|
|
@@ -702,7 +742,7 @@ function PlanChat({ messages, isThinking, onSend, onCancel }) {
|
|
|
702
742
|
);
|
|
703
743
|
}),
|
|
704
744
|
isThinking && /* @__PURE__ */ jsxs5(Box5, { flexDirection: "row", marginTop: 0, children: [
|
|
705
|
-
/* @__PURE__ */ jsx5(Text5, { color: "yellow", children: /* @__PURE__ */ jsx5(
|
|
745
|
+
/* @__PURE__ */ jsx5(Text5, { color: "yellow", children: /* @__PURE__ */ jsx5(Spinner4, { type: "dots" }) }),
|
|
706
746
|
/* @__PURE__ */ jsx5(Text5, { color: "gray", dimColor: true, children: " Analyzing..." })
|
|
707
747
|
] })
|
|
708
748
|
] }),
|
|
@@ -875,7 +915,7 @@ function PlanReview({ goal, issues, selectedIndex }) {
|
|
|
875
915
|
import { existsSync } from "fs";
|
|
876
916
|
import { basename, join } from "path";
|
|
877
917
|
import { Box as Box8, Text as Text8 } from "ink";
|
|
878
|
-
import { Fragment as
|
|
918
|
+
import { Fragment as Fragment3, jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
879
919
|
function Sidebar({
|
|
880
920
|
provider,
|
|
881
921
|
model,
|
|
@@ -890,7 +930,9 @@ function Sidebar({
|
|
|
890
930
|
mergeConfirm = null,
|
|
891
931
|
merging = null,
|
|
892
932
|
updateInfo = null,
|
|
893
|
-
workComplete = null
|
|
933
|
+
workComplete = null,
|
|
934
|
+
reviewers,
|
|
935
|
+
assignees
|
|
894
936
|
}) {
|
|
895
937
|
const dir = basename(cwd).toUpperCase();
|
|
896
938
|
const cwdLabel = existsSync(join(cwd, ".git")) ? "REPOSITORY" : "WORKSPACE";
|
|
@@ -916,10 +958,10 @@ function Sidebar({
|
|
|
916
958
|
] }),
|
|
917
959
|
/* @__PURE__ */ jsx8(Text8, { color: "yellow", children: "\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D" })
|
|
918
960
|
] }),
|
|
919
|
-
/* @__PURE__ */ jsx8(Box8, { marginBottom: 1, children: activeView === "idle" || activeView === "empty" ? /* @__PURE__ */ jsxs8(
|
|
961
|
+
/* @__PURE__ */ jsx8(Box8, { marginBottom: 1, children: activeView === "idle" || activeView === "empty" ? /* @__PURE__ */ jsxs8(Fragment3, { children: [
|
|
920
962
|
/* @__PURE__ */ jsx8(Text8, { color: "gray", children: "\u25C7 " }),
|
|
921
963
|
/* @__PURE__ */ jsx8(Text8, { color: "gray", bold: true, children: "IDLE" })
|
|
922
|
-
] }) : /* @__PURE__ */ jsxs8(
|
|
964
|
+
] }) : /* @__PURE__ */ jsxs8(Fragment3, { children: [
|
|
923
965
|
/* @__PURE__ */ jsx8(Text8, { color: paused ? "yellow" : "green", children: paused ? "\u23F8 " : "\u25B6 " }),
|
|
924
966
|
/* @__PURE__ */ jsx8(Text8, { color: paused ? "yellow" : "green", bold: true, children: paused ? "PAUSED" : "RUNNING" })
|
|
925
967
|
] }) }),
|
|
@@ -989,7 +1031,24 @@ function Sidebar({
|
|
|
989
1031
|
canMerge && !merging && !mergeConfirm && /* @__PURE__ */ jsx8(Text8, { dimColor: true, children: "[m] merge" }),
|
|
990
1032
|
merging && /* @__PURE__ */ jsx8(Text8, { color: "yellow", children: "\u23F3 merging..." }),
|
|
991
1033
|
mergeConfirm && /* @__PURE__ */ jsx8(Text8, { color: "yellow", children: "\u26A0 CI not passed\n merge? [y/n]" }),
|
|
992
|
-
/* @__PURE__ */ jsx8(Text8, { dimColor: true, children: "[Esc] back" })
|
|
1034
|
+
/* @__PURE__ */ jsx8(Text8, { dimColor: true, children: "[Esc] back" }),
|
|
1035
|
+
hasPrUrl && (reviewers?.length || assignees?.length) ? /* @__PURE__ */ jsxs8(Box8, { marginTop: 1, flexDirection: "column", children: [
|
|
1036
|
+
/* @__PURE__ */ jsx8(Text8, { color: "yellow", children: "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500" }),
|
|
1037
|
+
reviewers?.length ? /* @__PURE__ */ jsx8(
|
|
1038
|
+
Text8,
|
|
1039
|
+
{
|
|
1040
|
+
dimColor: true,
|
|
1041
|
+
children: `\u{1F440} ${reviewers.length} reviewer${reviewers.length > 1 ? "s" : ""}`
|
|
1042
|
+
}
|
|
1043
|
+
) : null,
|
|
1044
|
+
assignees?.length ? /* @__PURE__ */ jsx8(
|
|
1045
|
+
Text8,
|
|
1046
|
+
{
|
|
1047
|
+
dimColor: true,
|
|
1048
|
+
children: `\u{1F464} ${assignees.map((a) => a === "self" ? "you" : a).join(", ")}`
|
|
1049
|
+
}
|
|
1050
|
+
) : null
|
|
1051
|
+
] }) : null
|
|
993
1052
|
] }),
|
|
994
1053
|
activeView === "watching" && /* @__PURE__ */ jsx8(Box8, { marginTop: 1, flexDirection: "column", children: /* @__PURE__ */ jsx8(Text8, { dimColor: true, children: "[q] quit" }) }),
|
|
995
1054
|
activeView === "watch-prompt" && /* @__PURE__ */ jsxs8(Box8, { marginTop: 1, flexDirection: "column", children: [
|
|
@@ -1027,10 +1086,7 @@ function Sidebar({
|
|
|
1027
1086
|
import { jsx as jsx9, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
1028
1087
|
function KanbanApp({ config, initialCards = [] }) {
|
|
1029
1088
|
const { exit } = useApp();
|
|
1030
|
-
const { cards, isEmpty, isWatching, isWatchPrompt, workComplete, modelInUse } = useKanbanState(
|
|
1031
|
-
config.bell ?? true,
|
|
1032
|
-
initialCards
|
|
1033
|
-
);
|
|
1089
|
+
const { cards, isEmpty, isFetching, isWatching, isWatchPrompt, workComplete, modelInUse } = useKanbanState(config.bell ?? true, initialCards);
|
|
1034
1090
|
const { rows } = useTerminalSize();
|
|
1035
1091
|
const [activeView, setActiveView] = useState6("board");
|
|
1036
1092
|
const [activeColIndex, setActiveColIndex] = useState6(0);
|
|
@@ -1150,7 +1206,7 @@ function KanbanApp({ config, initialCards = [] }) {
|
|
|
1150
1206
|
}, [cards, selectedCardId, activeView]);
|
|
1151
1207
|
const selectedCard = activeView === "detail" && selectedCardId ? cards.find((c) => c.id === selectedCardId) ?? null : null;
|
|
1152
1208
|
async function handleMergeRequest(card) {
|
|
1153
|
-
const { checkPrCiStatus } = await import("./merge-
|
|
1209
|
+
const { checkPrCiStatus } = await import("./merge-CFQO7VU4.js");
|
|
1154
1210
|
const prUrl = card.prUrls[0];
|
|
1155
1211
|
const ciStatus = await checkPrCiStatus(prUrl);
|
|
1156
1212
|
if (ciStatus === "passing" || ciStatus === "unknown") {
|
|
@@ -1163,7 +1219,7 @@ function KanbanApp({ config, initialCards = [] }) {
|
|
|
1163
1219
|
const card = cards.find((c) => c.id === issueId);
|
|
1164
1220
|
if (!card || card.prUrls.length === 0) return;
|
|
1165
1221
|
setMerging(issueId);
|
|
1166
|
-
const { mergePr } = await import("./merge-
|
|
1222
|
+
const { mergePr } = await import("./merge-CFQO7VU4.js");
|
|
1167
1223
|
let allSuccess = true;
|
|
1168
1224
|
for (const prUrl of card.prUrls) {
|
|
1169
1225
|
const result = await mergePr(prUrl);
|
|
@@ -1378,6 +1434,7 @@ Merge failed: ${result.error}
|
|
|
1378
1434
|
let sidebarMode = "board";
|
|
1379
1435
|
if (isWatchPrompt) sidebarMode = "watch-prompt";
|
|
1380
1436
|
else if (isWatching) sidebarMode = "watching";
|
|
1437
|
+
else if (isFetching && activeView === "board") sidebarMode = "empty";
|
|
1381
1438
|
else if (isEmpty && activeView === "board" && cards.length === 0) sidebarMode = "empty";
|
|
1382
1439
|
else if (isEmpty && activeView === "board" && cards.length > 0) sidebarMode = "idle";
|
|
1383
1440
|
else if (activeView === "plan-chat") sidebarMode = "plan-chat";
|
|
@@ -1402,7 +1459,9 @@ Merge failed: ${result.error}
|
|
|
1402
1459
|
mergeConfirm,
|
|
1403
1460
|
merging,
|
|
1404
1461
|
updateInfo,
|
|
1405
|
-
workComplete
|
|
1462
|
+
workComplete,
|
|
1463
|
+
reviewers: config.pr?.reviewers,
|
|
1464
|
+
assignees: config.pr?.assignees
|
|
1406
1465
|
}
|
|
1407
1466
|
),
|
|
1408
1467
|
activeView === "plan-chat" ? /* @__PURE__ */ jsx9(
|
|
@@ -1459,6 +1518,7 @@ Merge failed: ${result.error}
|
|
|
1459
1518
|
columns: { backlog, inProgress, done },
|
|
1460
1519
|
labels,
|
|
1461
1520
|
isEmpty,
|
|
1521
|
+
isFetching,
|
|
1462
1522
|
isWatching,
|
|
1463
1523
|
isWatchPrompt,
|
|
1464
1524
|
workComplete,
|
|
@@ -1473,7 +1533,9 @@ Merge failed: ${result.error}
|
|
|
1473
1533
|
onBack: () => {
|
|
1474
1534
|
setActiveView("board");
|
|
1475
1535
|
setSelectedCardId(null);
|
|
1476
|
-
}
|
|
1536
|
+
},
|
|
1537
|
+
reviewers: config.pr?.reviewers,
|
|
1538
|
+
assignees: config.pr?.assignees
|
|
1477
1539
|
}
|
|
1478
1540
|
)
|
|
1479
1541
|
] });
|
|
@@ -3,15 +3,15 @@ import {
|
|
|
3
3
|
checkoutBaseBranches,
|
|
4
4
|
runDemoLoop,
|
|
5
5
|
runLoop
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-23Z5BBRT.js";
|
|
7
7
|
import {
|
|
8
8
|
WATCH_POLL_INTERVAL_MS
|
|
9
|
-
} from "./chunk-
|
|
10
|
-
import "./chunk-
|
|
9
|
+
} from "./chunk-SC222P5D.js";
|
|
10
|
+
import "./chunk-XLAY4P45.js";
|
|
11
11
|
import "./chunk-3EOEDL3T.js";
|
|
12
12
|
import "./chunk-7OCDGYDM.js";
|
|
13
13
|
import "./chunk-72CYGBT4.js";
|
|
14
|
-
import "./chunk-
|
|
14
|
+
import "./chunk-YMV4CBQE.js";
|
|
15
15
|
import "./chunk-7JT7DTSS.js";
|
|
16
16
|
import "./chunk-DGL33SDZ.js";
|
|
17
17
|
export {
|
|
@@ -71,7 +71,9 @@ async function mergePr(prUrl) {
|
|
|
71
71
|
}
|
|
72
72
|
async function mergeGitHubPr(prUrl) {
|
|
73
73
|
try {
|
|
74
|
-
await execa("gh", ["pr", "merge", prUrl, "--delete-branch"], {
|
|
74
|
+
await execa("gh", ["pr", "merge", prUrl, "--squash", "--delete-branch"], {
|
|
75
|
+
timeout: 3e4
|
|
76
|
+
});
|
|
75
77
|
return { success: true };
|
|
76
78
|
} catch (err) {
|
|
77
79
|
return { success: false, error: formatError(err) };
|
|
@@ -6,23 +6,23 @@ import {
|
|
|
6
6
|
markdownToIssue,
|
|
7
7
|
parseStructuredOutput,
|
|
8
8
|
savePlan
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-6XC7GQUI.js";
|
|
10
10
|
import {
|
|
11
11
|
createSource,
|
|
12
12
|
resolveModels,
|
|
13
13
|
runWithFallback
|
|
14
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-SC222P5D.js";
|
|
15
15
|
import {
|
|
16
16
|
error,
|
|
17
17
|
kanbanEmitter,
|
|
18
18
|
log,
|
|
19
19
|
ok,
|
|
20
20
|
warn
|
|
21
|
-
} from "./chunk-
|
|
21
|
+
} from "./chunk-XLAY4P45.js";
|
|
22
22
|
import "./chunk-3EOEDL3T.js";
|
|
23
23
|
import "./chunk-7OCDGYDM.js";
|
|
24
24
|
import "./chunk-72CYGBT4.js";
|
|
25
|
-
import "./chunk-
|
|
25
|
+
import "./chunk-YMV4CBQE.js";
|
|
26
26
|
import "./chunk-7JT7DTSS.js";
|
|
27
27
|
|
|
28
28
|
// src/plan/tui-bridge.ts
|
|
@@ -133,6 +133,9 @@ async function handleApproval(config, issues, goal) {
|
|
|
133
133
|
} catch (err) {
|
|
134
134
|
warn(`Could not refresh kanban: ${err instanceof Error ? err.message : String(err)}`);
|
|
135
135
|
}
|
|
136
|
+
if (createdIds.length > 0) {
|
|
137
|
+
kanbanEmitter.emit("loop:run");
|
|
138
|
+
}
|
|
136
139
|
}
|
|
137
140
|
function buildChatPrompt(goal, config, chatHistory) {
|
|
138
141
|
const basePrompt = buildPlanningPrompt(goal, config);
|