@tarcisiopgs/lisa 1.17.3 → 1.18.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.
|
@@ -7,6 +7,9 @@ import {
|
|
|
7
7
|
import { EventEmitter } from "events";
|
|
8
8
|
import { useEffect, useState } from "react";
|
|
9
9
|
|
|
10
|
+
// src/sources/github-issues.ts
|
|
11
|
+
import { execa } from "execa";
|
|
12
|
+
|
|
10
13
|
// src/output/logger.ts
|
|
11
14
|
import { appendFileSync, existsSync, mkdirSync, writeFileSync } from "fs";
|
|
12
15
|
import { dirname } from "path";
|
|
@@ -83,9 +86,17 @@ var API_URL = "https://api.github.com";
|
|
|
83
86
|
var REQUEST_TIMEOUT_MS = 3e4;
|
|
84
87
|
var PRIORITY_LABELS = ["p1", "p2", "p3"];
|
|
85
88
|
var DEPENDENCY_PATTERN = /(?:depends\s+on|blocked\s+by)\s+#(\d+)/gi;
|
|
86
|
-
function
|
|
87
|
-
|
|
88
|
-
|
|
89
|
+
async function getToken() {
|
|
90
|
+
if (process.env.GITHUB_TOKEN) return process.env.GITHUB_TOKEN;
|
|
91
|
+
try {
|
|
92
|
+
const { stdout } = await execa("gh", ["auth", "token"]);
|
|
93
|
+
if (stdout.trim()) return stdout.trim();
|
|
94
|
+
} catch {
|
|
95
|
+
}
|
|
96
|
+
throw new Error("GitHub authentication required: set GITHUB_TOKEN or run `gh auth login`");
|
|
97
|
+
}
|
|
98
|
+
async function getAuthHeaders() {
|
|
99
|
+
const token = await getToken();
|
|
89
100
|
return {
|
|
90
101
|
Authorization: `Bearer ${token}`,
|
|
91
102
|
Accept: "application/vnd.github+json",
|
|
@@ -95,7 +106,7 @@ function getAuthHeaders() {
|
|
|
95
106
|
async function githubFetch(method, path, body) {
|
|
96
107
|
const url = `${API_URL}${path}`;
|
|
97
108
|
const headers = {
|
|
98
|
-
...getAuthHeaders(),
|
|
109
|
+
...await getAuthHeaders(),
|
|
99
110
|
"Content-Type": "application/json"
|
|
100
111
|
};
|
|
101
112
|
const res = await fetch(url, {
|
package/dist/index.js
CHANGED
|
@@ -32,7 +32,7 @@ import {
|
|
|
32
32
|
ok,
|
|
33
33
|
setOutputMode,
|
|
34
34
|
warn
|
|
35
|
-
} from "./chunk-
|
|
35
|
+
} from "./chunk-KDKCASVH.js";
|
|
36
36
|
import {
|
|
37
37
|
notify,
|
|
38
38
|
resetTitle,
|
|
@@ -257,10 +257,103 @@ import { resolve as resolvePath2 } from "path";
|
|
|
257
257
|
import * as clack2 from "@clack/prompts";
|
|
258
258
|
import pc from "picocolors";
|
|
259
259
|
|
|
260
|
+
// src/git/github.ts
|
|
261
|
+
import { execa } from "execa";
|
|
262
|
+
|
|
263
|
+
// src/git/pr-body.ts
|
|
264
|
+
var PROVIDER_ATTRIBUTION_RE = /claude\.ai|claude\s+code|gemini\s+cli|openai\s+codex|\bgoose\b|\baider\b|github\s+copilot|cursor\s+agent|\bopencode\b/i;
|
|
265
|
+
var AI_COAUTHOR_RE = /co-authored-by:[^\n]*(anthropic|claude|gemini|openai|codex|goose|aider|copilot|cursor|google)/i;
|
|
266
|
+
function stripProviderAttribution(body) {
|
|
267
|
+
let result = body;
|
|
268
|
+
while (true) {
|
|
269
|
+
const sepIndex = result.lastIndexOf("\n---");
|
|
270
|
+
if (sepIndex === -1) break;
|
|
271
|
+
const section = result.slice(sepIndex);
|
|
272
|
+
if (PROVIDER_ATTRIBUTION_RE.test(section) || AI_COAUTHOR_RE.test(section)) {
|
|
273
|
+
result = result.slice(0, sepIndex).trimEnd();
|
|
274
|
+
} else {
|
|
275
|
+
break;
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
result = result.replace(
|
|
279
|
+
/\n+Co-Authored-By:[^\n]*(anthropic|claude|gemini|openai|codex|goose|aider|copilot|cursor|google)[^\n]*/gi,
|
|
280
|
+
""
|
|
281
|
+
);
|
|
282
|
+
return result.trimEnd();
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
// src/git/github.ts
|
|
286
|
+
async function isGhCliAvailable() {
|
|
287
|
+
try {
|
|
288
|
+
await execa("gh", ["auth", "status"]);
|
|
289
|
+
return true;
|
|
290
|
+
} catch {
|
|
291
|
+
return false;
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
var PROVIDER_DISPLAY_NAMES = {
|
|
295
|
+
claude: "Claude Code",
|
|
296
|
+
gemini: "Gemini CLI",
|
|
297
|
+
opencode: "OpenCode",
|
|
298
|
+
copilot: "GitHub Copilot CLI",
|
|
299
|
+
cursor: "Cursor Agent",
|
|
300
|
+
goose: "Goose",
|
|
301
|
+
aider: "Aider",
|
|
302
|
+
codex: "OpenAI Codex"
|
|
303
|
+
};
|
|
304
|
+
function formatProviderName(providerUsed) {
|
|
305
|
+
const providerKey = providerUsed.split("/")[0] ?? providerUsed;
|
|
306
|
+
return PROVIDER_DISPLAY_NAMES[providerKey] ?? providerKey;
|
|
307
|
+
}
|
|
308
|
+
async function deleteProviderComments(prUrl) {
|
|
309
|
+
try {
|
|
310
|
+
const match = prUrl.match(/github\.com\/([^/]+)\/([^/]+)\/pull\/(\d+)/);
|
|
311
|
+
if (!match) return;
|
|
312
|
+
const [, owner, repo, prNumber] = match;
|
|
313
|
+
const { stdout } = await execa("gh", [
|
|
314
|
+
"api",
|
|
315
|
+
"--paginate",
|
|
316
|
+
"--jq",
|
|
317
|
+
".[]",
|
|
318
|
+
`/repos/${owner}/${repo}/issues/${prNumber}/comments`
|
|
319
|
+
]);
|
|
320
|
+
const comments = stdout.trim().split("\n").filter(Boolean).map((line) => JSON.parse(line));
|
|
321
|
+
for (const comment of comments) {
|
|
322
|
+
if (PROVIDER_ATTRIBUTION_RE.test(comment.body)) {
|
|
323
|
+
try {
|
|
324
|
+
await execa("gh", [
|
|
325
|
+
"api",
|
|
326
|
+
"--method",
|
|
327
|
+
"DELETE",
|
|
328
|
+
`/repos/${owner}/${repo}/issues/comments/${comment.id}`
|
|
329
|
+
]);
|
|
330
|
+
} catch {
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
} catch {
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
async function appendPrAttribution(prUrl, providerUsed) {
|
|
338
|
+
await deleteProviderComments(prUrl);
|
|
339
|
+
try {
|
|
340
|
+
const { stdout: bodyJson } = await execa("gh", ["pr", "view", prUrl, "--json", "body"]);
|
|
341
|
+
const { body } = JSON.parse(bodyJson);
|
|
342
|
+
const providerName = formatProviderName(providerUsed);
|
|
343
|
+
const attribution = `
|
|
344
|
+
|
|
345
|
+
---
|
|
346
|
+
\u{1F916} Resolved by [lisa](https://github.com/tarcisiopgs/lisa) using **${providerName}**`;
|
|
347
|
+
const newBody = stripProviderAttribution(body ?? "") + attribution;
|
|
348
|
+
await execa("gh", ["pr", "edit", prUrl, "--body", newBody]);
|
|
349
|
+
} catch {
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
|
|
260
353
|
// src/git/worktree.ts
|
|
261
354
|
import { appendFileSync, existsSync as existsSync2, readFileSync as readFileSync2, rmSync } from "fs";
|
|
262
355
|
import { join, resolve as resolve2 } from "path";
|
|
263
|
-
import { execa } from "execa";
|
|
356
|
+
import { execa as execa2 } from "execa";
|
|
264
357
|
var WORKTREES_DIR = ".worktrees";
|
|
265
358
|
function generateBranchName(issueId, title) {
|
|
266
359
|
const slug = title.toLowerCase().replace(/[^a-z0-9]+/g, "-").substring(0, 40).replace(/^-|-$/g, "");
|
|
@@ -268,7 +361,7 @@ function generateBranchName(issueId, title) {
|
|
|
268
361
|
return `feat/${safeId}-${slug}`;
|
|
269
362
|
}
|
|
270
363
|
async function cleanupOrphanedWorktree(repoRoot, branchName) {
|
|
271
|
-
const { stdout: branchList } = await
|
|
364
|
+
const { stdout: branchList } = await execa2("git", ["branch", "--list", branchName], {
|
|
272
365
|
cwd: repoRoot,
|
|
273
366
|
reject: false
|
|
274
367
|
});
|
|
@@ -276,41 +369,41 @@ async function cleanupOrphanedWorktree(repoRoot, branchName) {
|
|
|
276
369
|
return false;
|
|
277
370
|
}
|
|
278
371
|
const worktreePath = join(repoRoot, WORKTREES_DIR, branchName);
|
|
279
|
-
const { stdout: worktreeList } = await
|
|
372
|
+
const { stdout: worktreeList } = await execa2("git", ["worktree", "list", "--porcelain"], {
|
|
280
373
|
cwd: repoRoot,
|
|
281
374
|
reject: false
|
|
282
375
|
});
|
|
283
376
|
if (worktreeList.includes(worktreePath)) {
|
|
284
|
-
await
|
|
285
|
-
await
|
|
377
|
+
await execa2("git", ["worktree", "remove", worktreePath, "--force"], { cwd: repoRoot });
|
|
378
|
+
await execa2("git", ["worktree", "prune"], { cwd: repoRoot });
|
|
286
379
|
}
|
|
287
|
-
await
|
|
380
|
+
await execa2("git", ["branch", "-D", branchName], { cwd: repoRoot });
|
|
288
381
|
return true;
|
|
289
382
|
}
|
|
290
383
|
async function createWorktree(repoRoot, branchName, baseBranch) {
|
|
291
384
|
const worktreePath = join(repoRoot, WORKTREES_DIR, branchName);
|
|
292
385
|
await cleanupOrphanedWorktree(repoRoot, branchName);
|
|
293
386
|
if (existsSync2(worktreePath)) {
|
|
294
|
-
await
|
|
387
|
+
await execa2("git", ["worktree", "remove", worktreePath, "--force"], {
|
|
295
388
|
cwd: repoRoot,
|
|
296
389
|
reject: false
|
|
297
390
|
});
|
|
298
|
-
await
|
|
391
|
+
await execa2("git", ["worktree", "prune"], { cwd: repoRoot, reject: false });
|
|
299
392
|
if (existsSync2(worktreePath)) {
|
|
300
393
|
rmSync(worktreePath, { recursive: true, force: true });
|
|
301
394
|
}
|
|
302
395
|
}
|
|
303
|
-
await
|
|
304
|
-
await
|
|
396
|
+
await execa2("git", ["fetch", "origin", baseBranch], { cwd: repoRoot });
|
|
397
|
+
await execa2("git", ["worktree", "add", "-b", branchName, worktreePath, `origin/${baseBranch}`], {
|
|
305
398
|
cwd: repoRoot
|
|
306
399
|
});
|
|
307
400
|
return worktreePath;
|
|
308
401
|
}
|
|
309
402
|
async function removeWorktree(repoRoot, worktreePath) {
|
|
310
|
-
await
|
|
403
|
+
await execa2("git", ["worktree", "remove", worktreePath, "--force"], {
|
|
311
404
|
cwd: repoRoot
|
|
312
405
|
});
|
|
313
|
-
await
|
|
406
|
+
await execa2("git", ["worktree", "prune"], { cwd: repoRoot });
|
|
314
407
|
}
|
|
315
408
|
function ensureWorktreeGitignore(repoRoot) {
|
|
316
409
|
const gitignorePath = join(repoRoot, ".gitignore");
|
|
@@ -328,21 +421,21 @@ function ensureWorktreeGitignore(repoRoot) {
|
|
|
328
421
|
}
|
|
329
422
|
async function findBranchByIssueId(repoRoot, issueId) {
|
|
330
423
|
const needle = issueId.toLowerCase();
|
|
331
|
-
const { stdout: local } = await
|
|
424
|
+
const { stdout: local } = await execa2(
|
|
332
425
|
"git",
|
|
333
426
|
["for-each-ref", "--sort=-committerdate", "--format=%(refname:short)", "refs/heads/"],
|
|
334
427
|
{ cwd: repoRoot }
|
|
335
428
|
);
|
|
336
429
|
const localMatch = local.split("\n").map((b) => b.trim()).filter(Boolean).find((b) => b.toLowerCase().includes(needle));
|
|
337
430
|
if (localMatch) return localMatch;
|
|
338
|
-
const { stdout: remote } = await
|
|
431
|
+
const { stdout: remote } = await execa2(
|
|
339
432
|
"git",
|
|
340
433
|
["for-each-ref", "--sort=-committerdate", "--format=%(refname:short)", "refs/remotes/origin/"],
|
|
341
434
|
{ cwd: repoRoot }
|
|
342
435
|
);
|
|
343
436
|
const remoteMatch = remote.split("\n").map((b) => b.trim()).filter(Boolean).find((b) => b.toLowerCase().includes(needle));
|
|
344
437
|
if (remoteMatch) return remoteMatch.replace("origin/", "");
|
|
345
|
-
const { stdout: lsRemote } = await
|
|
438
|
+
const { stdout: lsRemote } = await execa2("git", ["ls-remote", "--heads", "origin"], {
|
|
346
439
|
cwd: repoRoot
|
|
347
440
|
});
|
|
348
441
|
const lsMatch = lsRemote.split("\n").map((l) => l.trim()).filter(Boolean).map((l) => l.split(" ")[1]?.replace("refs/heads/", "") ?? "").find((b) => b.toLowerCase().includes(needle));
|
|
@@ -365,7 +458,7 @@ function determineRepoPath(repos, issue2, workspace) {
|
|
|
365
458
|
}
|
|
366
459
|
async function hasCodeChanges(repoPath, baseBranch) {
|
|
367
460
|
try {
|
|
368
|
-
const { stdout } = await
|
|
461
|
+
const { stdout } = await execa2("git", ["diff", "--stat", `${baseBranch}..HEAD`], {
|
|
369
462
|
cwd: repoPath,
|
|
370
463
|
reject: false
|
|
371
464
|
});
|
|
@@ -1624,101 +1717,6 @@ import { existsSync as existsSync3, readdirSync, readFileSync as readFileSync3 }
|
|
|
1624
1717
|
import { tmpdir as tmpdir9 } from "os";
|
|
1625
1718
|
import { join as join10, resolve as resolvePath } from "path";
|
|
1626
1719
|
import * as clack from "@clack/prompts";
|
|
1627
|
-
|
|
1628
|
-
// src/git/github.ts
|
|
1629
|
-
import { execa as execa2 } from "execa";
|
|
1630
|
-
|
|
1631
|
-
// src/git/pr-body.ts
|
|
1632
|
-
var PROVIDER_ATTRIBUTION_RE = /claude\.ai|claude\s+code|gemini\s+cli|openai\s+codex|\bgoose\b|\baider\b|github\s+copilot|cursor\s+agent|\bopencode\b/i;
|
|
1633
|
-
var AI_COAUTHOR_RE = /co-authored-by:[^\n]*(anthropic|claude|gemini|openai|codex|goose|aider|copilot|cursor|google)/i;
|
|
1634
|
-
function stripProviderAttribution(body) {
|
|
1635
|
-
let result = body;
|
|
1636
|
-
while (true) {
|
|
1637
|
-
const sepIndex = result.lastIndexOf("\n---");
|
|
1638
|
-
if (sepIndex === -1) break;
|
|
1639
|
-
const section = result.slice(sepIndex);
|
|
1640
|
-
if (PROVIDER_ATTRIBUTION_RE.test(section) || AI_COAUTHOR_RE.test(section)) {
|
|
1641
|
-
result = result.slice(0, sepIndex).trimEnd();
|
|
1642
|
-
} else {
|
|
1643
|
-
break;
|
|
1644
|
-
}
|
|
1645
|
-
}
|
|
1646
|
-
result = result.replace(
|
|
1647
|
-
/\n+Co-Authored-By:[^\n]*(anthropic|claude|gemini|openai|codex|goose|aider|copilot|cursor|google)[^\n]*/gi,
|
|
1648
|
-
""
|
|
1649
|
-
);
|
|
1650
|
-
return result.trimEnd();
|
|
1651
|
-
}
|
|
1652
|
-
|
|
1653
|
-
// src/git/github.ts
|
|
1654
|
-
async function isGhCliAvailable() {
|
|
1655
|
-
try {
|
|
1656
|
-
await execa2("gh", ["auth", "status"]);
|
|
1657
|
-
return true;
|
|
1658
|
-
} catch {
|
|
1659
|
-
return false;
|
|
1660
|
-
}
|
|
1661
|
-
}
|
|
1662
|
-
var PROVIDER_DISPLAY_NAMES = {
|
|
1663
|
-
claude: "Claude Code",
|
|
1664
|
-
gemini: "Gemini CLI",
|
|
1665
|
-
opencode: "OpenCode",
|
|
1666
|
-
copilot: "GitHub Copilot CLI",
|
|
1667
|
-
cursor: "Cursor Agent",
|
|
1668
|
-
goose: "Goose",
|
|
1669
|
-
aider: "Aider",
|
|
1670
|
-
codex: "OpenAI Codex"
|
|
1671
|
-
};
|
|
1672
|
-
function formatProviderName(providerUsed) {
|
|
1673
|
-
const providerKey = providerUsed.split("/")[0] ?? providerUsed;
|
|
1674
|
-
return PROVIDER_DISPLAY_NAMES[providerKey] ?? providerKey;
|
|
1675
|
-
}
|
|
1676
|
-
async function deleteProviderComments(prUrl) {
|
|
1677
|
-
try {
|
|
1678
|
-
const match = prUrl.match(/github\.com\/([^/]+)\/([^/]+)\/pull\/(\d+)/);
|
|
1679
|
-
if (!match) return;
|
|
1680
|
-
const [, owner, repo, prNumber] = match;
|
|
1681
|
-
const { stdout } = await execa2("gh", [
|
|
1682
|
-
"api",
|
|
1683
|
-
"--paginate",
|
|
1684
|
-
"--jq",
|
|
1685
|
-
".[]",
|
|
1686
|
-
`/repos/${owner}/${repo}/issues/${prNumber}/comments`
|
|
1687
|
-
]);
|
|
1688
|
-
const comments = stdout.trim().split("\n").filter(Boolean).map((line) => JSON.parse(line));
|
|
1689
|
-
for (const comment of comments) {
|
|
1690
|
-
if (PROVIDER_ATTRIBUTION_RE.test(comment.body)) {
|
|
1691
|
-
try {
|
|
1692
|
-
await execa2("gh", [
|
|
1693
|
-
"api",
|
|
1694
|
-
"--method",
|
|
1695
|
-
"DELETE",
|
|
1696
|
-
`/repos/${owner}/${repo}/issues/comments/${comment.id}`
|
|
1697
|
-
]);
|
|
1698
|
-
} catch {
|
|
1699
|
-
}
|
|
1700
|
-
}
|
|
1701
|
-
}
|
|
1702
|
-
} catch {
|
|
1703
|
-
}
|
|
1704
|
-
}
|
|
1705
|
-
async function appendPrAttribution(prUrl, providerUsed) {
|
|
1706
|
-
await deleteProviderComments(prUrl);
|
|
1707
|
-
try {
|
|
1708
|
-
const { stdout: bodyJson } = await execa2("gh", ["pr", "view", prUrl, "--json", "body"]);
|
|
1709
|
-
const { body } = JSON.parse(bodyJson);
|
|
1710
|
-
const providerName = formatProviderName(providerUsed);
|
|
1711
|
-
const attribution = `
|
|
1712
|
-
|
|
1713
|
-
---
|
|
1714
|
-
\u{1F916} Resolved by [lisa](https://github.com/tarcisiopgs/lisa) using **${providerName}**`;
|
|
1715
|
-
const newBody = stripProviderAttribution(body ?? "") + attribution;
|
|
1716
|
-
await execa2("gh", ["pr", "edit", prUrl, "--body", newBody]);
|
|
1717
|
-
} catch {
|
|
1718
|
-
}
|
|
1719
|
-
}
|
|
1720
|
-
|
|
1721
|
-
// src/cli/detection.ts
|
|
1722
1720
|
function getVersion() {
|
|
1723
1721
|
try {
|
|
1724
1722
|
const pkgPath = resolvePath(new URL(".", import.meta.url).pathname, "../package.json");
|
|
@@ -2118,48 +2116,70 @@ remove them or set the file to ${pc.cyan("{}")} \u2014 MCP tools can cause OpenC
|
|
|
2118
2116
|
if (clack2.isCancel(modelSelection)) return process.exit(0);
|
|
2119
2117
|
selectedModels = modelSelection ?? [];
|
|
2120
2118
|
}
|
|
2119
|
+
const ghCliAvailable = await isGhCliAvailable();
|
|
2121
2120
|
const source = await clack2.select({
|
|
2122
2121
|
message: "Where do your issues come from?",
|
|
2123
2122
|
initialValue: initial?.source,
|
|
2124
2123
|
options: [
|
|
2125
|
-
{
|
|
2124
|
+
{
|
|
2125
|
+
value: "linear",
|
|
2126
|
+
label: "Linear",
|
|
2127
|
+
apiHint: "GraphQL API",
|
|
2128
|
+
envVars: ["LINEAR_API_KEY"],
|
|
2129
|
+
ghCliFallback: false
|
|
2130
|
+
},
|
|
2126
2131
|
{
|
|
2127
2132
|
value: "trello",
|
|
2128
2133
|
label: "Trello",
|
|
2129
2134
|
apiHint: "REST API",
|
|
2130
|
-
envVars: ["TRELLO_API_KEY", "TRELLO_TOKEN"]
|
|
2135
|
+
envVars: ["TRELLO_API_KEY", "TRELLO_TOKEN"],
|
|
2136
|
+
ghCliFallback: false
|
|
2131
2137
|
},
|
|
2132
2138
|
{
|
|
2133
2139
|
value: "github-issues",
|
|
2134
2140
|
label: "GitHub Issues",
|
|
2135
2141
|
apiHint: "REST API",
|
|
2136
|
-
envVars: ["GITHUB_TOKEN"]
|
|
2142
|
+
envVars: ["GITHUB_TOKEN"],
|
|
2143
|
+
ghCliFallback: true
|
|
2137
2144
|
},
|
|
2138
2145
|
{
|
|
2139
2146
|
value: "gitlab-issues",
|
|
2140
2147
|
label: "GitLab Issues",
|
|
2141
2148
|
apiHint: "REST API",
|
|
2142
|
-
envVars: ["GITLAB_TOKEN"]
|
|
2149
|
+
envVars: ["GITLAB_TOKEN"],
|
|
2150
|
+
ghCliFallback: false
|
|
2151
|
+
},
|
|
2152
|
+
{
|
|
2153
|
+
value: "plane",
|
|
2154
|
+
label: "Plane",
|
|
2155
|
+
apiHint: "REST API",
|
|
2156
|
+
envVars: ["PLANE_API_TOKEN"],
|
|
2157
|
+
ghCliFallback: false
|
|
2143
2158
|
},
|
|
2144
|
-
{ value: "plane", label: "Plane", apiHint: "REST API", envVars: ["PLANE_API_TOKEN"] },
|
|
2145
2159
|
{
|
|
2146
2160
|
value: "shortcut",
|
|
2147
2161
|
label: "Shortcut",
|
|
2148
2162
|
apiHint: "REST API",
|
|
2149
|
-
envVars: ["SHORTCUT_API_TOKEN"]
|
|
2163
|
+
envVars: ["SHORTCUT_API_TOKEN"],
|
|
2164
|
+
ghCliFallback: false
|
|
2150
2165
|
},
|
|
2151
2166
|
{
|
|
2152
2167
|
value: "jira",
|
|
2153
2168
|
label: "Jira",
|
|
2154
2169
|
apiHint: "REST API",
|
|
2155
|
-
envVars: ["JIRA_BASE_URL", "JIRA_EMAIL", "JIRA_API_TOKEN"]
|
|
2170
|
+
envVars: ["JIRA_BASE_URL", "JIRA_EMAIL", "JIRA_API_TOKEN"],
|
|
2171
|
+
ghCliFallback: false
|
|
2172
|
+
}
|
|
2173
|
+
].map(({ value, label: label2, apiHint, envVars, ghCliFallback }) => {
|
|
2174
|
+
let missing2 = envVars.filter((v) => !process.env[v]);
|
|
2175
|
+
if (ghCliFallback && ghCliAvailable) {
|
|
2176
|
+
missing2 = missing2.filter((v) => v !== "GITHUB_TOKEN");
|
|
2156
2177
|
}
|
|
2157
|
-
|
|
2158
|
-
const missing2 = envVars.filter((v) => !process.env[v]);
|
|
2178
|
+
const usingGhCli = ghCliFallback && ghCliAvailable && !process.env.GITHUB_TOKEN;
|
|
2159
2179
|
return {
|
|
2160
2180
|
value,
|
|
2161
2181
|
label: label2,
|
|
2162
|
-
hint: missing2.length > 0 ? `missing: ${missing2.join(", ")}` : apiHint,
|
|
2182
|
+
hint: missing2.length > 0 ? `missing: ${missing2.join(", ")}` : usingGhCli ? "via gh CLI" : apiHint,
|
|
2163
2183
|
disabled: missing2.length > 0
|
|
2164
2184
|
};
|
|
2165
2185
|
})
|
|
@@ -7248,7 +7268,7 @@ var run = defineCommand5({
|
|
|
7248
7268
|
if (isTTY) {
|
|
7249
7269
|
const { render } = await import("ink");
|
|
7250
7270
|
const { createElement } = await import("react");
|
|
7251
|
-
const { KanbanApp } = await import("./kanban-
|
|
7271
|
+
const { KanbanApp } = await import("./kanban-PTPW3HO2.js");
|
|
7252
7272
|
const demoConfig = {
|
|
7253
7273
|
provider: "claude",
|
|
7254
7274
|
source: "linear",
|
|
@@ -7323,7 +7343,7 @@ Add them to your ${shell} and run: source ${shell}`));
|
|
|
7323
7343
|
if (isTTY) {
|
|
7324
7344
|
const { render } = await import("ink");
|
|
7325
7345
|
const { createElement } = await import("react");
|
|
7326
|
-
const { KanbanApp } = await import("./kanban-
|
|
7346
|
+
const { KanbanApp } = await import("./kanban-PTPW3HO2.js");
|
|
7327
7347
|
render(createElement(KanbanApp, { config: merged }), { exitOnCtrlC: false });
|
|
7328
7348
|
}
|
|
7329
7349
|
await runLoop(merged, {
|