@tarcisiopgs/lisa 1.19.3 → 1.20.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.
- package/dist/chunk-7CIXBENY.js +96 -0
- package/dist/{chunk-2SHKSRQZ.js → chunk-HDOVPYNL.js} +16 -0
- package/dist/chunk-LAJNLRXO.js +356 -0
- package/dist/detection-LALQE6Q3.js +27 -0
- package/dist/index.js +335 -518
- package/dist/{kanban-6TUHPX6U.js → kanban-TOKVY5AS.js} +21 -3
- package/dist/version-D2YFKS7Q.js +9 -0
- package/package.json +1 -1
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/version.ts
|
|
4
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
5
|
+
import { homedir, platform } from "os";
|
|
6
|
+
import { join } from "path";
|
|
7
|
+
var PACKAGE_NAME = "@tarcisiopgs/lisa";
|
|
8
|
+
var CHECK_INTERVAL_MS = 24 * 60 * 60 * 1e3;
|
|
9
|
+
var FETCH_TIMEOUT_MS = 3e3;
|
|
10
|
+
function getCachePath() {
|
|
11
|
+
let base;
|
|
12
|
+
if (process.env.XDG_CACHE_HOME) {
|
|
13
|
+
base = process.env.XDG_CACHE_HOME;
|
|
14
|
+
} else if (platform() === "darwin") {
|
|
15
|
+
base = join(homedir(), "Library", "Caches");
|
|
16
|
+
} else {
|
|
17
|
+
base = join(homedir(), ".cache");
|
|
18
|
+
}
|
|
19
|
+
return join(base, "lisa", "update-check.json");
|
|
20
|
+
}
|
|
21
|
+
function readCache() {
|
|
22
|
+
try {
|
|
23
|
+
const raw = readFileSync(getCachePath(), "utf-8");
|
|
24
|
+
return JSON.parse(raw);
|
|
25
|
+
} catch {
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
function writeCache(cache) {
|
|
30
|
+
try {
|
|
31
|
+
const path = getCachePath();
|
|
32
|
+
const dir = join(path, "..");
|
|
33
|
+
if (!existsSync(dir)) {
|
|
34
|
+
mkdirSync(dir, { recursive: true });
|
|
35
|
+
}
|
|
36
|
+
writeFileSync(path, JSON.stringify(cache));
|
|
37
|
+
} catch {
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
function compareVersions(current, latest) {
|
|
41
|
+
const [cMajor = 0, cMinor = 0, cPatch = 0] = current.split(".").map(Number);
|
|
42
|
+
const [lMajor = 0, lMinor = 0, lPatch = 0] = latest.split(".").map(Number);
|
|
43
|
+
if (lMajor > cMajor) return "major";
|
|
44
|
+
if (lMajor === cMajor && lMinor > cMinor) return "minor";
|
|
45
|
+
if (lMajor === cMajor && lMinor === cMinor && lPatch > cPatch) return "patch";
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
var cachedResult;
|
|
49
|
+
async function checkForUpdate(currentVersion) {
|
|
50
|
+
if (cachedResult !== void 0) return cachedResult;
|
|
51
|
+
try {
|
|
52
|
+
const cache = readCache();
|
|
53
|
+
if (cache && Date.now() - cache.lastCheck < CHECK_INTERVAL_MS) {
|
|
54
|
+
if (cache.latestVersion) {
|
|
55
|
+
const updateType = compareVersions(currentVersion, cache.latestVersion);
|
|
56
|
+
cachedResult = updateType ? { currentVersion, latestVersion: cache.latestVersion, updateType } : null;
|
|
57
|
+
return cachedResult;
|
|
58
|
+
}
|
|
59
|
+
cachedResult = null;
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
const controller = new AbortController();
|
|
63
|
+
const timeout = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS);
|
|
64
|
+
const response = await fetch(
|
|
65
|
+
`https://registry.npmjs.org/${encodeURIComponent(PACKAGE_NAME)}/latest`,
|
|
66
|
+
{ signal: controller.signal }
|
|
67
|
+
);
|
|
68
|
+
clearTimeout(timeout);
|
|
69
|
+
if (!response.ok) {
|
|
70
|
+
writeCache({ lastCheck: Date.now(), latestVersion: null });
|
|
71
|
+
cachedResult = null;
|
|
72
|
+
return null;
|
|
73
|
+
}
|
|
74
|
+
const data = await response.json();
|
|
75
|
+
const latestVersion = data.version ?? null;
|
|
76
|
+
writeCache({ lastCheck: Date.now(), latestVersion });
|
|
77
|
+
if (latestVersion) {
|
|
78
|
+
const updateType = compareVersions(currentVersion, latestVersion);
|
|
79
|
+
cachedResult = updateType ? { currentVersion, latestVersion, updateType } : null;
|
|
80
|
+
return cachedResult;
|
|
81
|
+
}
|
|
82
|
+
cachedResult = null;
|
|
83
|
+
return null;
|
|
84
|
+
} catch {
|
|
85
|
+
cachedResult = null;
|
|
86
|
+
return null;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
function getCachedUpdateInfo() {
|
|
90
|
+
return cachedResult ?? null;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export {
|
|
94
|
+
checkForUpdate,
|
|
95
|
+
getCachedUpdateInfo
|
|
96
|
+
};
|
|
@@ -80,6 +80,21 @@ function banner() {
|
|
|
80
80
|
console.log(pc.yellow(` \u2514${border}\u2518
|
|
81
81
|
`));
|
|
82
82
|
}
|
|
83
|
+
function updateNotice(update) {
|
|
84
|
+
if (outputMode !== "default") return;
|
|
85
|
+
const msg = `Update available ${pc.dim(update.currentVersion)} \u2192 ${pc.green(pc.bold(update.latestVersion))}`;
|
|
86
|
+
const cmd = `Run ${pc.cyan("npm i -g @tarcisiopgs/lisa")} to update`;
|
|
87
|
+
const lines = [msg, cmd];
|
|
88
|
+
const strip = (s) => s.replace(/\x1b\[[0-9;]*m/g, "");
|
|
89
|
+
const maxLen = Math.max(...lines.map((l) => strip(l).length));
|
|
90
|
+
const pad = (s) => s + " ".repeat(maxLen - strip(s).length);
|
|
91
|
+
console.log(pc.yellow(` \u250C${"\u2500".repeat(maxLen + 2)}\u2510`));
|
|
92
|
+
for (const line of lines) {
|
|
93
|
+
console.log(pc.yellow(" \u2502 ") + pad(line) + pc.yellow(" \u2502"));
|
|
94
|
+
}
|
|
95
|
+
console.log(pc.yellow(` \u2514${"\u2500".repeat(maxLen + 2)}\u2518
|
|
96
|
+
`));
|
|
97
|
+
}
|
|
83
98
|
|
|
84
99
|
// src/sources/github-issues.ts
|
|
85
100
|
var API_URL = "https://api.github.com";
|
|
@@ -838,6 +853,7 @@ export {
|
|
|
838
853
|
ok,
|
|
839
854
|
divider,
|
|
840
855
|
banner,
|
|
856
|
+
updateNotice,
|
|
841
857
|
GitHubIssuesSource,
|
|
842
858
|
GitLabIssuesSource,
|
|
843
859
|
kanbanEmitter,
|
|
@@ -0,0 +1,356 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/cli/detection.ts
|
|
4
|
+
import { execSync } from "child_process";
|
|
5
|
+
import { existsSync, readdirSync, readFileSync } from "fs";
|
|
6
|
+
import { tmpdir } from "os";
|
|
7
|
+
import { join, resolve as resolvePath } from "path";
|
|
8
|
+
import * as clack from "@clack/prompts";
|
|
9
|
+
|
|
10
|
+
// src/git/github.ts
|
|
11
|
+
import { execa } from "execa";
|
|
12
|
+
|
|
13
|
+
// src/git/pr-body.ts
|
|
14
|
+
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;
|
|
15
|
+
var AI_COAUTHOR_RE = /co-authored-by:[^\n]*(anthropic|claude|gemini|openai|codex|goose|aider|copilot|cursor|google)/i;
|
|
16
|
+
function stripProviderAttribution(body) {
|
|
17
|
+
let result = body;
|
|
18
|
+
while (true) {
|
|
19
|
+
const sepIndex = result.lastIndexOf("\n---");
|
|
20
|
+
if (sepIndex === -1) break;
|
|
21
|
+
const section = result.slice(sepIndex);
|
|
22
|
+
if (PROVIDER_ATTRIBUTION_RE.test(section) || AI_COAUTHOR_RE.test(section)) {
|
|
23
|
+
result = result.slice(0, sepIndex).trimEnd();
|
|
24
|
+
} else {
|
|
25
|
+
break;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
result = result.replace(
|
|
29
|
+
/\n+Co-Authored-By:[^\n]*(anthropic|claude|gemini|openai|codex|goose|aider|copilot|cursor|google)[^\n]*/gi,
|
|
30
|
+
""
|
|
31
|
+
);
|
|
32
|
+
return result.trimEnd();
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// src/git/github.ts
|
|
36
|
+
async function isGhCliAvailable() {
|
|
37
|
+
try {
|
|
38
|
+
await execa("gh", ["auth", "status"]);
|
|
39
|
+
return true;
|
|
40
|
+
} catch {
|
|
41
|
+
return false;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
var PROVIDER_DISPLAY_NAMES = {
|
|
45
|
+
claude: "Claude Code",
|
|
46
|
+
gemini: "Gemini CLI",
|
|
47
|
+
opencode: "OpenCode",
|
|
48
|
+
copilot: "GitHub Copilot CLI",
|
|
49
|
+
cursor: "Cursor Agent",
|
|
50
|
+
goose: "Goose",
|
|
51
|
+
aider: "Aider",
|
|
52
|
+
codex: "OpenAI Codex"
|
|
53
|
+
};
|
|
54
|
+
function formatProviderName(providerUsed) {
|
|
55
|
+
const providerKey = providerUsed.split("/")[0] ?? providerUsed;
|
|
56
|
+
return PROVIDER_DISPLAY_NAMES[providerKey] ?? providerKey;
|
|
57
|
+
}
|
|
58
|
+
async function deleteProviderComments(prUrl) {
|
|
59
|
+
try {
|
|
60
|
+
const match = prUrl.match(/github\.com\/([^/]+)\/([^/]+)\/pull\/(\d+)/);
|
|
61
|
+
if (!match) return;
|
|
62
|
+
const [, owner, repo, prNumber] = match;
|
|
63
|
+
const { stdout } = await execa("gh", [
|
|
64
|
+
"api",
|
|
65
|
+
"--paginate",
|
|
66
|
+
"--jq",
|
|
67
|
+
".[]",
|
|
68
|
+
`/repos/${owner}/${repo}/issues/${prNumber}/comments`
|
|
69
|
+
]);
|
|
70
|
+
const comments = stdout.trim().split("\n").filter(Boolean).map((line) => JSON.parse(line));
|
|
71
|
+
for (const comment of comments) {
|
|
72
|
+
if (PROVIDER_ATTRIBUTION_RE.test(comment.body)) {
|
|
73
|
+
try {
|
|
74
|
+
await execa("gh", [
|
|
75
|
+
"api",
|
|
76
|
+
"--method",
|
|
77
|
+
"DELETE",
|
|
78
|
+
`/repos/${owner}/${repo}/issues/comments/${comment.id}`
|
|
79
|
+
]);
|
|
80
|
+
} catch {
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
} catch {
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
async function appendPrAttribution(prUrl, providerUsed) {
|
|
88
|
+
await deleteProviderComments(prUrl);
|
|
89
|
+
try {
|
|
90
|
+
const { stdout: bodyJson } = await execa("gh", ["pr", "view", prUrl, "--json", "body"]);
|
|
91
|
+
const { body } = JSON.parse(bodyJson);
|
|
92
|
+
const providerName = formatProviderName(providerUsed);
|
|
93
|
+
const attribution = `
|
|
94
|
+
|
|
95
|
+
---
|
|
96
|
+
\u{1F916} Resolved by [lisa](https://github.com/tarcisiopgs/lisa) using **${providerName}**`;
|
|
97
|
+
const newBody = stripProviderAttribution(body ?? "") + attribution;
|
|
98
|
+
await execa("gh", ["pr", "edit", prUrl, "--body", newBody]);
|
|
99
|
+
} catch {
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// src/cli/detection.ts
|
|
104
|
+
function getVersion() {
|
|
105
|
+
try {
|
|
106
|
+
const pkgPath = resolvePath(new URL(".", import.meta.url).pathname, "../package.json");
|
|
107
|
+
const pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
|
|
108
|
+
return pkg.version;
|
|
109
|
+
} catch {
|
|
110
|
+
return "0.0.0";
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
var CURSOR_FREE_PLAN_ERROR = "Free plans can only use Auto";
|
|
114
|
+
async function isCursorFreePlan() {
|
|
115
|
+
const { mkdtempSync, unlinkSync, writeFileSync } = await import("fs");
|
|
116
|
+
const tmpDir = mkdtempSync(join(tmpdir(), "lisa-cursor-check-"));
|
|
117
|
+
const promptFile = join(tmpDir, "prompt.txt");
|
|
118
|
+
writeFileSync(promptFile, "test", "utf-8");
|
|
119
|
+
try {
|
|
120
|
+
const bin = ["agent", "cursor-agent"].find((b) => {
|
|
121
|
+
try {
|
|
122
|
+
execSync(`${b} --version`, { stdio: "ignore" });
|
|
123
|
+
return true;
|
|
124
|
+
} catch {
|
|
125
|
+
return false;
|
|
126
|
+
}
|
|
127
|
+
});
|
|
128
|
+
if (!bin) return false;
|
|
129
|
+
const output = execSync(`${bin} -p "$(cat '${promptFile}')" --output-format text`, {
|
|
130
|
+
cwd: process.cwd(),
|
|
131
|
+
encoding: "utf-8",
|
|
132
|
+
timeout: 3e4
|
|
133
|
+
});
|
|
134
|
+
return output.includes(CURSOR_FREE_PLAN_ERROR);
|
|
135
|
+
} catch (err) {
|
|
136
|
+
const errorOutput = err instanceof Error ? err.message : String(err);
|
|
137
|
+
return errorOutput.includes(CURSOR_FREE_PLAN_ERROR);
|
|
138
|
+
} finally {
|
|
139
|
+
try {
|
|
140
|
+
unlinkSync(promptFile);
|
|
141
|
+
} catch {
|
|
142
|
+
}
|
|
143
|
+
try {
|
|
144
|
+
execSync(`rm -rf ${tmpDir}`, { stdio: "ignore" });
|
|
145
|
+
} catch {
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
var CURSOR_PREFERRED_MODELS = [
|
|
150
|
+
"auto",
|
|
151
|
+
"composer-1.5",
|
|
152
|
+
"composer-1",
|
|
153
|
+
"gpt-5.3-codex",
|
|
154
|
+
"gpt-5.2",
|
|
155
|
+
"gpt-5.1-codex-max",
|
|
156
|
+
"opus-4.6-thinking",
|
|
157
|
+
"opus-4.6",
|
|
158
|
+
"sonnet-4.6-thinking",
|
|
159
|
+
"sonnet-4.6",
|
|
160
|
+
"gemini-3.1-pro",
|
|
161
|
+
"gemini-3-pro",
|
|
162
|
+
"grok",
|
|
163
|
+
"kimi-k2.5"
|
|
164
|
+
];
|
|
165
|
+
function fetchCursorModels() {
|
|
166
|
+
try {
|
|
167
|
+
const bin = ["agent", "cursor-agent"].find((b) => {
|
|
168
|
+
try {
|
|
169
|
+
execSync(`${b} --version`, { stdio: "ignore" });
|
|
170
|
+
return true;
|
|
171
|
+
} catch {
|
|
172
|
+
return false;
|
|
173
|
+
}
|
|
174
|
+
});
|
|
175
|
+
if (!bin) return CURSOR_PREFERRED_MODELS;
|
|
176
|
+
const raw = execSync(`${bin} --list-models`, { encoding: "utf-8", timeout: 1e4 });
|
|
177
|
+
const clean = raw.replace(/\x1b\[[0-9;]*[mGKHFA-Z]/g, "");
|
|
178
|
+
const all = clean.split("\n").map((l) => l.trim()).filter((l) => l.includes(" - ")).map((l) => (l.split(" - ")[0] ?? "").trim()).filter(Boolean);
|
|
179
|
+
const filtered = CURSOR_PREFERRED_MODELS.filter((m) => all.includes(m));
|
|
180
|
+
return filtered.length > 0 ? filtered : CURSOR_PREFERRED_MODELS;
|
|
181
|
+
} catch {
|
|
182
|
+
return CURSOR_PREFERRED_MODELS;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
function fetchOpenCodeModels() {
|
|
186
|
+
try {
|
|
187
|
+
const raw = execSync("opencode models", { encoding: "utf-8", timeout: 1e4 });
|
|
188
|
+
const clean = raw.replace(/\x1b\[[0-9;]*[mGKHFA-Z]/g, "");
|
|
189
|
+
return clean.split("\n").map((l) => l.trim()).filter((m) => /^[a-z0-9][\w.-]*\/.+/i.test(m));
|
|
190
|
+
} catch {
|
|
191
|
+
return [];
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
function detectPlatformFromRemoteUrl(remoteUrl) {
|
|
195
|
+
if (/github\.com/.test(remoteUrl)) return "cli";
|
|
196
|
+
if (/gitlab\./.test(remoteUrl)) return "gitlab";
|
|
197
|
+
if (/bitbucket\.org/.test(remoteUrl)) return "bitbucket";
|
|
198
|
+
return null;
|
|
199
|
+
}
|
|
200
|
+
async function detectPlatform() {
|
|
201
|
+
let detectedPlatform = null;
|
|
202
|
+
try {
|
|
203
|
+
const remoteUrl = execSync("git remote get-url origin", {
|
|
204
|
+
encoding: "utf-8",
|
|
205
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
206
|
+
}).trim();
|
|
207
|
+
detectedPlatform = detectPlatformFromRemoteUrl(remoteUrl);
|
|
208
|
+
if (detectedPlatform) {
|
|
209
|
+
const platformLabel = detectedPlatform === "cli" || detectedPlatform === "token" ? "GitHub" : detectedPlatform === "gitlab" ? "GitLab" : "Bitbucket";
|
|
210
|
+
clack.log.info(`Detected ${platformLabel} remote`);
|
|
211
|
+
}
|
|
212
|
+
} catch {
|
|
213
|
+
}
|
|
214
|
+
const initialValue = detectedPlatform ?? "cli";
|
|
215
|
+
const selected = await clack.select({
|
|
216
|
+
message: "How should Lisa create pull requests?",
|
|
217
|
+
initialValue,
|
|
218
|
+
options: [
|
|
219
|
+
{ value: "cli", label: "GitHub CLI", hint: "uses `gh pr create` \u2014 recommended for GitHub" },
|
|
220
|
+
{ value: "token", label: "GitHub API", hint: "uses GITHUB_TOKEN directly" },
|
|
221
|
+
{ value: "gitlab", label: "GitLab API", hint: "uses GITLAB_TOKEN (glab or REST API)" },
|
|
222
|
+
{ value: "bitbucket", label: "Bitbucket API", hint: "uses BITBUCKET_TOKEN (REST API)" }
|
|
223
|
+
]
|
|
224
|
+
});
|
|
225
|
+
if (clack.isCancel(selected)) return process.exit(0);
|
|
226
|
+
const platform = selected;
|
|
227
|
+
await verifyPlatformCredential(platform);
|
|
228
|
+
return platform;
|
|
229
|
+
}
|
|
230
|
+
async function verifyPlatformCredential(platform) {
|
|
231
|
+
if (platform === "cli") {
|
|
232
|
+
const hasCli = await isGhCliAvailable();
|
|
233
|
+
if (!hasCli) {
|
|
234
|
+
clack.log.warning(
|
|
235
|
+
"GitHub CLI (`gh`) is not authenticated. Run `gh auth login` before using Lisa."
|
|
236
|
+
);
|
|
237
|
+
}
|
|
238
|
+
return;
|
|
239
|
+
}
|
|
240
|
+
if (platform === "token") {
|
|
241
|
+
if (!process.env.GITHUB_TOKEN) {
|
|
242
|
+
clack.log.warning("GITHUB_TOKEN is not set. Add it to your shell profile.");
|
|
243
|
+
}
|
|
244
|
+
return;
|
|
245
|
+
}
|
|
246
|
+
if (platform === "gitlab") {
|
|
247
|
+
if (!process.env.GITLAB_TOKEN) {
|
|
248
|
+
clack.log.warning("GITLAB_TOKEN is not set. Add it to your shell profile.");
|
|
249
|
+
}
|
|
250
|
+
return;
|
|
251
|
+
}
|
|
252
|
+
if (platform === "bitbucket") {
|
|
253
|
+
if (!process.env.BITBUCKET_TOKEN) {
|
|
254
|
+
clack.log.warning("BITBUCKET_TOKEN is not set. Add it to your shell profile.");
|
|
255
|
+
}
|
|
256
|
+
return;
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
async function detectGitRepos(existingRepos = []) {
|
|
260
|
+
const cwd = process.cwd();
|
|
261
|
+
if (existsSync(join(cwd, ".git"))) {
|
|
262
|
+
clack.log.info("Found a git repository in the current directory.");
|
|
263
|
+
return [];
|
|
264
|
+
}
|
|
265
|
+
const entries = readdirSync(cwd, { withFileTypes: true });
|
|
266
|
+
const gitDirs = entries.filter((e) => e.isDirectory() && existsSync(join(cwd, e.name, ".git"))).map((e) => e.name);
|
|
267
|
+
const existingDirs = existingRepos.map((r) => r.path.replace(/^\.\//, ""));
|
|
268
|
+
const missingDirs = existingDirs.filter((d) => !gitDirs.includes(d));
|
|
269
|
+
if (gitDirs.length === 0 && missingDirs.length === 0) {
|
|
270
|
+
return [];
|
|
271
|
+
}
|
|
272
|
+
const options = [
|
|
273
|
+
...gitDirs.map((dir) => ({ value: dir, label: dir, disabled: false })),
|
|
274
|
+
...missingDirs.map((dir) => ({
|
|
275
|
+
value: dir,
|
|
276
|
+
label: dir,
|
|
277
|
+
hint: "(not found on disk)",
|
|
278
|
+
disabled: true
|
|
279
|
+
}))
|
|
280
|
+
];
|
|
281
|
+
const initialValues = existingDirs.filter((d) => gitDirs.includes(d));
|
|
282
|
+
const selected = await clack.multiselect({
|
|
283
|
+
message: "Multiple git repositories found \u2014 which ones should Lisa work on?",
|
|
284
|
+
options,
|
|
285
|
+
initialValues
|
|
286
|
+
});
|
|
287
|
+
if (clack.isCancel(selected)) return process.exit(0);
|
|
288
|
+
return selected.map((dir) => ({
|
|
289
|
+
name: getGitRepoName(join(cwd, dir)) ?? dir,
|
|
290
|
+
path: `./${dir}`,
|
|
291
|
+
match: existingRepos.find((r) => r.path === `./${dir}`)?.match ?? "",
|
|
292
|
+
base_branch: existingRepos.find((r) => r.path === `./${dir}`)?.base_branch ?? ""
|
|
293
|
+
}));
|
|
294
|
+
}
|
|
295
|
+
function detectDefaultBranch(repoPath) {
|
|
296
|
+
try {
|
|
297
|
+
const ref = execSync("git symbolic-ref refs/remotes/origin/HEAD --short", {
|
|
298
|
+
cwd: repoPath,
|
|
299
|
+
encoding: "utf-8"
|
|
300
|
+
}).trim();
|
|
301
|
+
return ref.replace("origin/", "");
|
|
302
|
+
} catch {
|
|
303
|
+
return "main";
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
function getGitRepoName(repoPath) {
|
|
307
|
+
try {
|
|
308
|
+
const url = execSync("git remote get-url origin", { cwd: repoPath, encoding: "utf-8" }).trim();
|
|
309
|
+
const match = url.match(/\/([^/]+?)(?:\.git)?$/) ?? url.match(/:([^/]+?)(?:\.git)?$/);
|
|
310
|
+
return match?.[1] ?? null;
|
|
311
|
+
} catch {
|
|
312
|
+
return null;
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
async function getMissingEnvVars(source) {
|
|
316
|
+
const missing = [];
|
|
317
|
+
if (!process.env.GITHUB_TOKEN) {
|
|
318
|
+
const ghAvailable = await isGhCliAvailable();
|
|
319
|
+
if (!ghAvailable) missing.push("GITHUB_TOKEN");
|
|
320
|
+
}
|
|
321
|
+
if (source === "linear") {
|
|
322
|
+
if (!process.env.LINEAR_API_KEY) missing.push("LINEAR_API_KEY");
|
|
323
|
+
} else if (source === "trello") {
|
|
324
|
+
if (!process.env.TRELLO_API_KEY) missing.push("TRELLO_API_KEY");
|
|
325
|
+
if (!process.env.TRELLO_TOKEN) missing.push("TRELLO_TOKEN");
|
|
326
|
+
} else if (source === "github-issues") {
|
|
327
|
+
} else if (source === "gitlab-issues") {
|
|
328
|
+
if (!process.env.GITLAB_TOKEN) missing.push("GITLAB_TOKEN");
|
|
329
|
+
} else if (source === "plane") {
|
|
330
|
+
if (!process.env.PLANE_API_TOKEN) missing.push("PLANE_API_TOKEN");
|
|
331
|
+
} else if (source === "shortcut") {
|
|
332
|
+
if (!process.env.SHORTCUT_API_TOKEN) missing.push("SHORTCUT_API_TOKEN");
|
|
333
|
+
} else if (source === "jira") {
|
|
334
|
+
if (!process.env.JIRA_BASE_URL) missing.push("JIRA_BASE_URL");
|
|
335
|
+
if (!process.env.JIRA_EMAIL) missing.push("JIRA_EMAIL");
|
|
336
|
+
if (!process.env.JIRA_API_TOKEN) missing.push("JIRA_API_TOKEN");
|
|
337
|
+
}
|
|
338
|
+
return missing;
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
export {
|
|
342
|
+
stripProviderAttribution,
|
|
343
|
+
isGhCliAvailable,
|
|
344
|
+
appendPrAttribution,
|
|
345
|
+
getVersion,
|
|
346
|
+
isCursorFreePlan,
|
|
347
|
+
fetchCursorModels,
|
|
348
|
+
fetchOpenCodeModels,
|
|
349
|
+
detectPlatformFromRemoteUrl,
|
|
350
|
+
detectPlatform,
|
|
351
|
+
verifyPlatformCredential,
|
|
352
|
+
detectGitRepos,
|
|
353
|
+
detectDefaultBranch,
|
|
354
|
+
getGitRepoName,
|
|
355
|
+
getMissingEnvVars
|
|
356
|
+
};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
detectDefaultBranch,
|
|
4
|
+
detectGitRepos,
|
|
5
|
+
detectPlatform,
|
|
6
|
+
detectPlatformFromRemoteUrl,
|
|
7
|
+
fetchCursorModels,
|
|
8
|
+
fetchOpenCodeModels,
|
|
9
|
+
getGitRepoName,
|
|
10
|
+
getMissingEnvVars,
|
|
11
|
+
getVersion,
|
|
12
|
+
isCursorFreePlan,
|
|
13
|
+
verifyPlatformCredential
|
|
14
|
+
} from "./chunk-LAJNLRXO.js";
|
|
15
|
+
export {
|
|
16
|
+
detectDefaultBranch,
|
|
17
|
+
detectGitRepos,
|
|
18
|
+
detectPlatform,
|
|
19
|
+
detectPlatformFromRemoteUrl,
|
|
20
|
+
fetchCursorModels,
|
|
21
|
+
fetchOpenCodeModels,
|
|
22
|
+
getGitRepoName,
|
|
23
|
+
getMissingEnvVars,
|
|
24
|
+
getVersion,
|
|
25
|
+
isCursorFreePlan,
|
|
26
|
+
verifyPlatformCredential
|
|
27
|
+
};
|