tarsk 0.3.38 → 0.3.39
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/index.js +357 -19
- package/dist/public/assets/index-C85yPisA.js +86 -0
- package/dist/public/assets/index-CfhNkIT6.css +1 -0
- package/dist/public/index.html +2 -2
- package/dist/public/pop.wav +0 -0
- package/package.json +1 -1
- package/dist/public/assets/index--zvOUkM-.js +0 -86
- package/dist/public/assets/index-CWAj_PFW.css +0 -1
package/dist/index.js
CHANGED
|
@@ -1131,6 +1131,118 @@ class ProjectManagerImpl {
|
|
|
1131
1131
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
1132
1132
|
import { join as join4 } from "path";
|
|
1133
1133
|
|
|
1134
|
+
// src/utils/random-words.ts
|
|
1135
|
+
var WORD_LIST = [
|
|
1136
|
+
"panda",
|
|
1137
|
+
"koala",
|
|
1138
|
+
"penguin",
|
|
1139
|
+
"bunny",
|
|
1140
|
+
"kitten",
|
|
1141
|
+
"puppy",
|
|
1142
|
+
"hamster",
|
|
1143
|
+
"otter",
|
|
1144
|
+
"hedgehog",
|
|
1145
|
+
"sloth",
|
|
1146
|
+
"dolphin",
|
|
1147
|
+
"owl",
|
|
1148
|
+
"fox",
|
|
1149
|
+
"deer",
|
|
1150
|
+
"koala",
|
|
1151
|
+
"walrus",
|
|
1152
|
+
"bouncy",
|
|
1153
|
+
"sparkly",
|
|
1154
|
+
"fluffy",
|
|
1155
|
+
"jolly",
|
|
1156
|
+
"zesty",
|
|
1157
|
+
"peppy",
|
|
1158
|
+
"snappy",
|
|
1159
|
+
"perky",
|
|
1160
|
+
"breezy",
|
|
1161
|
+
"chirpy",
|
|
1162
|
+
"dizzy",
|
|
1163
|
+
"fizzy",
|
|
1164
|
+
"glossy",
|
|
1165
|
+
"groggy",
|
|
1166
|
+
"loopy",
|
|
1167
|
+
"quirky",
|
|
1168
|
+
"umbrella",
|
|
1169
|
+
"taco",
|
|
1170
|
+
"pickle",
|
|
1171
|
+
"waffle",
|
|
1172
|
+
"bubble",
|
|
1173
|
+
"puzzle",
|
|
1174
|
+
"rocket",
|
|
1175
|
+
"tornado",
|
|
1176
|
+
"volcano",
|
|
1177
|
+
"rainbow",
|
|
1178
|
+
"diamond",
|
|
1179
|
+
"crystal",
|
|
1180
|
+
"lantern",
|
|
1181
|
+
"treasure",
|
|
1182
|
+
"whisper",
|
|
1183
|
+
"echo",
|
|
1184
|
+
"boing",
|
|
1185
|
+
"splat",
|
|
1186
|
+
"whoosh",
|
|
1187
|
+
"crunch",
|
|
1188
|
+
"splash",
|
|
1189
|
+
"jingle",
|
|
1190
|
+
"wiggle",
|
|
1191
|
+
"giggle",
|
|
1192
|
+
"scramble",
|
|
1193
|
+
"tumble",
|
|
1194
|
+
"fumble",
|
|
1195
|
+
"jumble",
|
|
1196
|
+
"rumble",
|
|
1197
|
+
"mumble",
|
|
1198
|
+
"stumble",
|
|
1199
|
+
"hustle",
|
|
1200
|
+
"castle",
|
|
1201
|
+
"island",
|
|
1202
|
+
"mountain",
|
|
1203
|
+
"forest",
|
|
1204
|
+
"meadow",
|
|
1205
|
+
"lagoon",
|
|
1206
|
+
"canyon",
|
|
1207
|
+
"geyser",
|
|
1208
|
+
"oasis",
|
|
1209
|
+
"glacier",
|
|
1210
|
+
"waterfall",
|
|
1211
|
+
"volcano",
|
|
1212
|
+
"tundra",
|
|
1213
|
+
"savanna",
|
|
1214
|
+
"jungle",
|
|
1215
|
+
"desert",
|
|
1216
|
+
"magic",
|
|
1217
|
+
"dream",
|
|
1218
|
+
"wonder",
|
|
1219
|
+
"spark",
|
|
1220
|
+
"glow",
|
|
1221
|
+
"shimmer",
|
|
1222
|
+
"twinkle",
|
|
1223
|
+
"radiance",
|
|
1224
|
+
"harmony",
|
|
1225
|
+
"melody",
|
|
1226
|
+
"rhythm",
|
|
1227
|
+
"symphony",
|
|
1228
|
+
"adventure",
|
|
1229
|
+
"journey",
|
|
1230
|
+
"quest",
|
|
1231
|
+
"odyssey"
|
|
1232
|
+
];
|
|
1233
|
+
function getRandomWord() {
|
|
1234
|
+
return WORD_LIST[Math.floor(Math.random() * WORD_LIST.length)];
|
|
1235
|
+
}
|
|
1236
|
+
function generateRandomThreadName() {
|
|
1237
|
+
const word1 = getRandomWord();
|
|
1238
|
+
let word2 = getRandomWord();
|
|
1239
|
+
while (word2 === word1) {
|
|
1240
|
+
word2 = getRandomWord();
|
|
1241
|
+
}
|
|
1242
|
+
return `${word1}-${word2}`;
|
|
1243
|
+
}
|
|
1244
|
+
|
|
1245
|
+
// src/managers/thread-manager.ts
|
|
1134
1246
|
class ThreadManagerImpl {
|
|
1135
1247
|
metadataManager;
|
|
1136
1248
|
gitManager;
|
|
@@ -1364,17 +1476,14 @@ class ThreadManagerImpl {
|
|
|
1364
1476
|
return join4(projectPath, threadId);
|
|
1365
1477
|
}
|
|
1366
1478
|
generateThreadTitle(existingTitles) {
|
|
1367
|
-
|
|
1368
|
-
let
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
if (n > maxNum)
|
|
1374
|
-
maxNum = n;
|
|
1375
|
-
}
|
|
1479
|
+
let threadTitle = generateRandomThreadName();
|
|
1480
|
+
let attempts = 0;
|
|
1481
|
+
const maxAttempts = 100;
|
|
1482
|
+
while (existingTitles.has(threadTitle) && attempts < maxAttempts) {
|
|
1483
|
+
threadTitle = generateRandomThreadName();
|
|
1484
|
+
attempts++;
|
|
1376
1485
|
}
|
|
1377
|
-
return
|
|
1486
|
+
return threadTitle;
|
|
1378
1487
|
}
|
|
1379
1488
|
async listFiles(threadId) {
|
|
1380
1489
|
const thread = await this.getThread(threadId);
|
|
@@ -7104,6 +7213,15 @@ new file mode 100644
|
|
|
7104
7213
|
} catch {
|
|
7105
7214
|
return c.json({ error: `Path is not a git repository: ${absolutePath}` }, 400);
|
|
7106
7215
|
}
|
|
7216
|
+
const currentBranch = await new Promise((resolve4) => {
|
|
7217
|
+
const proc = spawn8("git", ["rev-parse", "--abbrev-ref", "HEAD"], { cwd: gitRoot });
|
|
7218
|
+
let out = "";
|
|
7219
|
+
proc.stdout.on("data", (d) => {
|
|
7220
|
+
out += d.toString();
|
|
7221
|
+
});
|
|
7222
|
+
proc.on("close", () => resolve4(out.trim()));
|
|
7223
|
+
proc.on("error", () => resolve4(""));
|
|
7224
|
+
});
|
|
7107
7225
|
const diff = await new Promise((resolveDiff, reject) => {
|
|
7108
7226
|
const runPlainDiff = () => {
|
|
7109
7227
|
const proc2 = spawn8("git", ["diff"], { cwd: gitRoot });
|
|
@@ -7136,19 +7254,97 @@ new file mode 100644
|
|
|
7136
7254
|
});
|
|
7137
7255
|
proc.on("error", reject);
|
|
7138
7256
|
});
|
|
7139
|
-
|
|
7140
|
-
|
|
7257
|
+
const baseBranch = await new Promise((resolve4) => {
|
|
7258
|
+
const proc = spawn8("git", ["symbolic-ref", "refs/remotes/origin/HEAD"], { cwd: gitRoot });
|
|
7259
|
+
let out = "";
|
|
7260
|
+
proc.stdout.on("data", (d) => {
|
|
7261
|
+
out += d.toString();
|
|
7262
|
+
});
|
|
7263
|
+
proc.on("close", (code) => {
|
|
7264
|
+
if (code === 0 && out.trim()) {
|
|
7265
|
+
const match = out.trim().match(/refs\/remotes\/origin\/(.+)/);
|
|
7266
|
+
resolve4(match ? match[1] : null);
|
|
7267
|
+
} else {
|
|
7268
|
+
resolve4(null);
|
|
7269
|
+
}
|
|
7270
|
+
});
|
|
7271
|
+
proc.on("error", () => resolve4(null));
|
|
7272
|
+
});
|
|
7273
|
+
const effectiveBaseBranch = baseBranch || await (async () => {
|
|
7274
|
+
const candidates = ["main", "master", "develop", "staging"];
|
|
7275
|
+
for (const branch of candidates) {
|
|
7276
|
+
const exists = await new Promise((resolve4) => {
|
|
7277
|
+
const proc = spawn8("git", ["rev-parse", "--verify", `origin/${branch}`], { cwd: gitRoot });
|
|
7278
|
+
proc.on("close", (code) => resolve4(code === 0));
|
|
7279
|
+
proc.on("error", () => resolve4(false));
|
|
7280
|
+
});
|
|
7281
|
+
if (exists)
|
|
7282
|
+
return branch;
|
|
7283
|
+
}
|
|
7284
|
+
return null;
|
|
7285
|
+
})();
|
|
7286
|
+
let commitLog = "";
|
|
7287
|
+
if (effectiveBaseBranch && currentBranch !== effectiveBaseBranch) {
|
|
7288
|
+
commitLog = await new Promise((resolve4) => {
|
|
7289
|
+
const proc = spawn8("git", ["log", "--oneline", `${effectiveBaseBranch}..${currentBranch}`], { cwd: gitRoot });
|
|
7290
|
+
let out = "";
|
|
7291
|
+
proc.stdout.on("data", (d) => {
|
|
7292
|
+
out += d.toString();
|
|
7293
|
+
});
|
|
7294
|
+
proc.on("close", () => resolve4(out.trim()));
|
|
7295
|
+
proc.on("error", () => resolve4(""));
|
|
7296
|
+
});
|
|
7141
7297
|
}
|
|
7142
|
-
|
|
7298
|
+
let detailedCommits = "";
|
|
7299
|
+
if (effectiveBaseBranch && currentBranch !== effectiveBaseBranch && commitLog) {
|
|
7300
|
+
detailedCommits = await new Promise((resolve4) => {
|
|
7301
|
+
const proc = spawn8("git", ["log", `${effectiveBaseBranch}..${currentBranch}`, "--format=%h %s%n%b%n---"], { cwd: gitRoot });
|
|
7302
|
+
let out = "";
|
|
7303
|
+
proc.stdout.on("data", (d) => {
|
|
7304
|
+
out += d.toString();
|
|
7305
|
+
});
|
|
7306
|
+
proc.on("close", () => {
|
|
7307
|
+
resolve4(out.length > 2000 ? out.substring(0, 2000) + `
|
|
7308
|
+
...(more commits)` : out);
|
|
7309
|
+
});
|
|
7310
|
+
proc.on("error", () => resolve4(""));
|
|
7311
|
+
});
|
|
7312
|
+
}
|
|
7313
|
+
if (!diff.trim() && !commitLog.trim()) {
|
|
7314
|
+
return c.json({
|
|
7315
|
+
error: effectiveBaseBranch ? `No changes to generate PR info for. The current branch "${currentBranch}" has no uncommitted changes and no commits ahead of "${effectiveBaseBranch}".` : "No changes to generate PR info for. Make sure you have uncommitted changes or commits ahead of the base branch."
|
|
7316
|
+
}, 400);
|
|
7317
|
+
}
|
|
7318
|
+
let promptContent;
|
|
7319
|
+
if (commitLog.trim()) {
|
|
7320
|
+
promptContent = `Based on the following commits in branch "${currentBranch}" compared to "${effectiveBaseBranch}", generate a pull request title and description.
|
|
7321
|
+
|
|
7322
|
+
Commit log:
|
|
7323
|
+
${commitLog}
|
|
7324
|
+
|
|
7325
|
+
Detailed commit messages:
|
|
7326
|
+
${detailedCommits || "(no details available)"}
|
|
7327
|
+
|
|
7328
|
+
${diff.trim() ? `
|
|
7329
|
+
Additionally, there are uncommitted changes:
|
|
7330
|
+
${diff.length > 1500 ? diff.substring(0, 1500) + `
|
|
7331
|
+
...(truncated)` : diff}` : ""}`;
|
|
7332
|
+
} else {
|
|
7333
|
+
const truncatedDiff = diff.length > 3000 ? diff.substring(0, 3000) + `
|
|
7143
7334
|
...(truncated)` : diff;
|
|
7144
|
-
|
|
7335
|
+
promptContent = `Based on the following git diff, generate a pull request title and description.
|
|
7336
|
+
|
|
7337
|
+
Git diff:
|
|
7338
|
+
${truncatedDiff}`;
|
|
7339
|
+
}
|
|
7340
|
+
const prompt = `${promptContent}
|
|
7341
|
+
|
|
7342
|
+
Follow these guidelines:
|
|
7145
7343
|
- Title: Concise, descriptive, under 72 characters
|
|
7146
7344
|
- Description: Clear explanation of changes, why they were made, and any relevant context
|
|
7147
|
-
- Use markdown formatting for the description
|
|
7345
|
+
- Use markdown formatting for the description (bullet points, headers, etc.)
|
|
7148
7346
|
- Be professional and clear
|
|
7149
|
-
|
|
7150
|
-
Git diff:
|
|
7151
|
-
${truncatedDiff}
|
|
7347
|
+
- If multiple changes, group them logically in the description
|
|
7152
7348
|
|
|
7153
7349
|
Generate the response in this exact format:
|
|
7154
7350
|
TITLE: <title here>
|
|
@@ -7173,7 +7369,7 @@ DESCRIPTION: <description here>`;
|
|
|
7173
7369
|
const descriptionMatch = prInfo.match(/DESCRIPTION:\s*(.+?)$/s);
|
|
7174
7370
|
const title = titleMatch ? titleMatch[1].trim() : "Update";
|
|
7175
7371
|
const description = descriptionMatch ? descriptionMatch[1].trim() : "";
|
|
7176
|
-
return c.json({ title, description });
|
|
7372
|
+
return c.json({ title, description, baseBranch: effectiveBaseBranch, currentBranch });
|
|
7177
7373
|
} catch (error) {
|
|
7178
7374
|
const message = error instanceof Error ? error.message : "Failed to generate PR info";
|
|
7179
7375
|
return c.json({ error: message }, 500);
|
|
@@ -7290,6 +7486,148 @@ DESCRIPTION: <description here>`;
|
|
|
7290
7486
|
return c.json({ error: message }, 500);
|
|
7291
7487
|
}
|
|
7292
7488
|
});
|
|
7489
|
+
router.get("/github-status/:threadId", async (c) => {
|
|
7490
|
+
try {
|
|
7491
|
+
const threadId = c.req.param("threadId");
|
|
7492
|
+
const thread = await metadataManager.loadThreads().then((threads) => threads.find((t) => t.id === threadId));
|
|
7493
|
+
if (!thread) {
|
|
7494
|
+
return c.json({ error: "Thread not found" }, 404);
|
|
7495
|
+
}
|
|
7496
|
+
const repoPath = thread.path;
|
|
7497
|
+
if (!repoPath) {
|
|
7498
|
+
return c.json({ error: "Thread path not found" }, 404);
|
|
7499
|
+
}
|
|
7500
|
+
const absolutePath = resolveThreadPath(repoPath);
|
|
7501
|
+
let gitRoot;
|
|
7502
|
+
try {
|
|
7503
|
+
gitRoot = await getGitRoot(absolutePath);
|
|
7504
|
+
} catch {
|
|
7505
|
+
return c.json({ error: `Path is not a git repository: ${absolutePath}` }, 400);
|
|
7506
|
+
}
|
|
7507
|
+
const remoteUrl = await new Promise((resolve4) => {
|
|
7508
|
+
const proc = spawn8("git", ["remote", "get-url", "origin"], { cwd: gitRoot });
|
|
7509
|
+
let out = "";
|
|
7510
|
+
proc.stdout.on("data", (d) => {
|
|
7511
|
+
out += d.toString();
|
|
7512
|
+
});
|
|
7513
|
+
proc.on("close", (code) => {
|
|
7514
|
+
if (code === 0) {
|
|
7515
|
+
resolve4(out.trim());
|
|
7516
|
+
} else {
|
|
7517
|
+
resolve4("");
|
|
7518
|
+
}
|
|
7519
|
+
});
|
|
7520
|
+
proc.on("error", () => resolve4(""));
|
|
7521
|
+
});
|
|
7522
|
+
const isOnGitHub = remoteUrl.includes("github.com");
|
|
7523
|
+
let repoUrl = "";
|
|
7524
|
+
if (isOnGitHub) {
|
|
7525
|
+
if (remoteUrl.startsWith("https://")) {
|
|
7526
|
+
repoUrl = remoteUrl.replace(/\.git$/, "");
|
|
7527
|
+
} else if (remoteUrl.startsWith("git@")) {
|
|
7528
|
+
const match = remoteUrl.match(/git@github\.com:(.+?)\.git$/);
|
|
7529
|
+
if (match) {
|
|
7530
|
+
repoUrl = `https://github.com/${match[1]}`;
|
|
7531
|
+
}
|
|
7532
|
+
}
|
|
7533
|
+
}
|
|
7534
|
+
return c.json({
|
|
7535
|
+
isOnGitHub,
|
|
7536
|
+
remoteUrl,
|
|
7537
|
+
canCreateRepo: !isOnGitHub && remoteUrl.length === 0,
|
|
7538
|
+
repoUrl
|
|
7539
|
+
});
|
|
7540
|
+
} catch (error) {
|
|
7541
|
+
const message = error instanceof Error ? error.message : "Failed to check GitHub status";
|
|
7542
|
+
return c.json({ error: message }, 500);
|
|
7543
|
+
}
|
|
7544
|
+
});
|
|
7545
|
+
router.post("/create-repo/:threadId", async (c) => {
|
|
7546
|
+
try {
|
|
7547
|
+
const threadId = c.req.param("threadId");
|
|
7548
|
+
const body = await c.req.json().catch(() => ({}));
|
|
7549
|
+
const { repoName, description, isPrivate } = body;
|
|
7550
|
+
const thread = await metadataManager.loadThreads().then((threads) => threads.find((t) => t.id === threadId));
|
|
7551
|
+
if (!thread) {
|
|
7552
|
+
return c.json({ error: "Thread not found" }, 404);
|
|
7553
|
+
}
|
|
7554
|
+
const repoPath = thread.path;
|
|
7555
|
+
if (!repoPath) {
|
|
7556
|
+
return c.json({ error: "Thread path not found" }, 404);
|
|
7557
|
+
}
|
|
7558
|
+
const absolutePath = resolveThreadPath(repoPath);
|
|
7559
|
+
let gitRoot;
|
|
7560
|
+
try {
|
|
7561
|
+
gitRoot = await getGitRoot(absolutePath);
|
|
7562
|
+
} catch {
|
|
7563
|
+
return c.json({ error: `Path is not a git repository: ${absolutePath}` }, 400);
|
|
7564
|
+
}
|
|
7565
|
+
const remoteUrl = await new Promise((resolve4) => {
|
|
7566
|
+
const proc = spawn8("git", ["remote", "get-url", "origin"], { cwd: gitRoot });
|
|
7567
|
+
let out = "";
|
|
7568
|
+
proc.stdout.on("data", (d) => {
|
|
7569
|
+
out += d.toString();
|
|
7570
|
+
});
|
|
7571
|
+
proc.on("close", (code) => {
|
|
7572
|
+
if (code === 0) {
|
|
7573
|
+
resolve4(out.trim());
|
|
7574
|
+
} else {
|
|
7575
|
+
resolve4("");
|
|
7576
|
+
}
|
|
7577
|
+
});
|
|
7578
|
+
proc.on("error", () => resolve4(""));
|
|
7579
|
+
});
|
|
7580
|
+
if (remoteUrl) {
|
|
7581
|
+
return c.json({ error: "Repository already has a remote origin" }, 400);
|
|
7582
|
+
}
|
|
7583
|
+
const _currentBranch = await new Promise((resolve4, reject) => {
|
|
7584
|
+
const proc = spawn8("git", ["rev-parse", "--abbrev-ref", "HEAD"], { cwd: gitRoot });
|
|
7585
|
+
let out = "";
|
|
7586
|
+
proc.stdout.on("data", (d) => {
|
|
7587
|
+
out += d.toString();
|
|
7588
|
+
});
|
|
7589
|
+
proc.on("close", () => resolve4(out.trim()));
|
|
7590
|
+
proc.on("error", reject);
|
|
7591
|
+
});
|
|
7592
|
+
const repoUrl = await new Promise((resolve4, reject) => {
|
|
7593
|
+
const args = ["repo", "create"];
|
|
7594
|
+
if (repoName) {
|
|
7595
|
+
args.push(repoName);
|
|
7596
|
+
}
|
|
7597
|
+
if (description) {
|
|
7598
|
+
args.push("--description", description);
|
|
7599
|
+
}
|
|
7600
|
+
if (isPrivate) {
|
|
7601
|
+
args.push("--private");
|
|
7602
|
+
} else {
|
|
7603
|
+
args.push("--public");
|
|
7604
|
+
}
|
|
7605
|
+
args.push("--source", ".", "--push");
|
|
7606
|
+
const proc = spawn8("gh", args, { cwd: gitRoot });
|
|
7607
|
+
let out = "";
|
|
7608
|
+
let err = "";
|
|
7609
|
+
proc.stdout.on("data", (d) => {
|
|
7610
|
+
out += d.toString();
|
|
7611
|
+
});
|
|
7612
|
+
proc.stderr.on("data", (d) => {
|
|
7613
|
+
err += d.toString();
|
|
7614
|
+
});
|
|
7615
|
+
proc.on("close", (code) => {
|
|
7616
|
+
if (code === 0) {
|
|
7617
|
+
const urlMatch = out.match(/https:\/\/[^\s]+/);
|
|
7618
|
+
resolve4(urlMatch ? urlMatch[0] : out.trim());
|
|
7619
|
+
} else {
|
|
7620
|
+
reject(new Error(err || "Failed to create repository"));
|
|
7621
|
+
}
|
|
7622
|
+
});
|
|
7623
|
+
proc.on("error", () => reject(new Error("GitHub CLI (gh) not found. Please install it to create repositories.")));
|
|
7624
|
+
});
|
|
7625
|
+
return c.json({ success: true, repoUrl });
|
|
7626
|
+
} catch (error) {
|
|
7627
|
+
const message = error instanceof Error ? error.message : "Failed to create repository";
|
|
7628
|
+
return c.json({ error: message }, 500);
|
|
7629
|
+
}
|
|
7630
|
+
});
|
|
7293
7631
|
return router;
|
|
7294
7632
|
}
|
|
7295
7633
|
|