tarsk 0.3.37 → 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 +530 -49
- 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 +3 -2
- package/dist/public/assets/index-B10k61wG.css +0 -1
- package/dist/public/assets/index-CD8l28Py.js +0 -86
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);
|
|
@@ -3826,6 +3935,9 @@ class EventQueue {
|
|
|
3826
3935
|
this.resolver = null;
|
|
3827
3936
|
}
|
|
3828
3937
|
}
|
|
3938
|
+
notify() {
|
|
3939
|
+
this.notifyResolver();
|
|
3940
|
+
}
|
|
3829
3941
|
}
|
|
3830
3942
|
|
|
3831
3943
|
// src/managers/pi-prompt-loader.ts
|
|
@@ -3986,7 +4098,7 @@ class PiExecutorImpl {
|
|
|
3986
4098
|
});
|
|
3987
4099
|
const promptDone = agent.prompt(userPrompt).then(() => {
|
|
3988
4100
|
done = true;
|
|
3989
|
-
eventQueue.
|
|
4101
|
+
eventQueue.notify();
|
|
3990
4102
|
}).catch((err) => {
|
|
3991
4103
|
if (!eventQueue.errorOccurred) {
|
|
3992
4104
|
eventQueue.errorOccurred = true;
|
|
@@ -4004,7 +4116,7 @@ class PiExecutorImpl {
|
|
|
4004
4116
|
});
|
|
4005
4117
|
}
|
|
4006
4118
|
done = true;
|
|
4007
|
-
eventQueue.
|
|
4119
|
+
eventQueue.notify();
|
|
4008
4120
|
});
|
|
4009
4121
|
try {
|
|
4010
4122
|
while (!done || eventQueue.length > 0) {
|
|
@@ -5100,6 +5212,7 @@ function createChatRoutes(threadManager, agentExecutor, conversationManager, pro
|
|
|
5100
5212
|
if (!model || typeof model !== "string") {
|
|
5101
5213
|
return errorResponse(c, "INVALID_REQUEST", "model is required and must be a string", 400);
|
|
5102
5214
|
}
|
|
5215
|
+
console.log(`[chat] User message (thread: ${threadId}): ${content}`);
|
|
5103
5216
|
const thread = await threadManager.getThread(threadId);
|
|
5104
5217
|
if (!thread) {
|
|
5105
5218
|
return errorResponse(c, "THREAD_NOT_FOUND", `Thread not found: ${threadId}`, 404);
|
|
@@ -5183,6 +5296,7 @@ User: ${content}` : content;
|
|
|
5183
5296
|
capturedEvents.push(event);
|
|
5184
5297
|
if (event.type === "message" && event.content) {
|
|
5185
5298
|
fullContent += event.content;
|
|
5299
|
+
console.log(`[chat] Assistant (role: ${event.role ?? "assistant"}): ${event.content}`);
|
|
5186
5300
|
}
|
|
5187
5301
|
if (event.type === "message" && typeof event.content === "string" && (event.role === "tool" || isToolLikeContent(event.content))) {
|
|
5188
5302
|
yield { type: "thinking", content: event.content };
|
|
@@ -5220,7 +5334,11 @@ User: ${content}` : content;
|
|
|
5220
5334
|
processingStateManager.clearProcessing(threadId);
|
|
5221
5335
|
}
|
|
5222
5336
|
}
|
|
5223
|
-
|
|
5337
|
+
async function* withCompleteSignal() {
|
|
5338
|
+
yield* chatExecutionGenerator();
|
|
5339
|
+
yield { type: "complete" };
|
|
5340
|
+
}
|
|
5341
|
+
return streamAsyncGenerator(c, withCompleteSignal());
|
|
5224
5342
|
} catch (error) {
|
|
5225
5343
|
return errorResponse(c, "REQUEST_PARSE_ERROR", "Failed to parse request body", 400, error instanceof Error ? error.message : String(error));
|
|
5226
5344
|
}
|
|
@@ -5560,32 +5678,6 @@ import { Hono as Hono5 } from "hono";
|
|
|
5560
5678
|
|
|
5561
5679
|
// src/provider-data.ts
|
|
5562
5680
|
var PROVIDER_DATA = [
|
|
5563
|
-
{
|
|
5564
|
-
id: "aihubmix",
|
|
5565
|
-
models: [
|
|
5566
|
-
"DeepSeek-R1",
|
|
5567
|
-
"DeepSeek-V3",
|
|
5568
|
-
"claude-3-5-sonnet-20241022",
|
|
5569
|
-
"claude-3-7-sonnet-20250219",
|
|
5570
|
-
"claude-opus-4-1",
|
|
5571
|
-
"claude-opus-4-20250514",
|
|
5572
|
-
"claude-sonnet-4-20250514",
|
|
5573
|
-
"claude-sonnet-4-5",
|
|
5574
|
-
"gemini-2.5-flash",
|
|
5575
|
-
"gemini-2.5-flash-lite",
|
|
5576
|
-
"gemini-2.5-pro",
|
|
5577
|
-
"glm-4.6",
|
|
5578
|
-
"gpt-4",
|
|
5579
|
-
"gpt-4.1",
|
|
5580
|
-
"gpt-4o",
|
|
5581
|
-
"gpt-5",
|
|
5582
|
-
"gpt-5-mini",
|
|
5583
|
-
"kimi-k2-thinking",
|
|
5584
|
-
"kimi-k2-turbo-preview",
|
|
5585
|
-
"o3-mini",
|
|
5586
|
-
"o4-mini"
|
|
5587
|
-
]
|
|
5588
|
-
},
|
|
5589
5681
|
{
|
|
5590
5682
|
id: "anthropic",
|
|
5591
5683
|
models: [
|
|
@@ -6062,6 +6154,163 @@ async function getModelInfoForProvider(provider, modelIds, apiKey) {
|
|
|
6062
6154
|
return result;
|
|
6063
6155
|
}
|
|
6064
6156
|
|
|
6157
|
+
// src/generated-data.ts
|
|
6158
|
+
var GENERATED_DATA = [
|
|
6159
|
+
{
|
|
6160
|
+
id: "aihubmix",
|
|
6161
|
+
models: [
|
|
6162
|
+
"BAAI/bge-large-en-v1.5",
|
|
6163
|
+
"BAAI/bge-large-zh-v1.5",
|
|
6164
|
+
"DeepSeek-R1",
|
|
6165
|
+
"DeepSeek-V3",
|
|
6166
|
+
"DeepSeek-V3-Fast",
|
|
6167
|
+
"DeepSeek-V3.1-Fast",
|
|
6168
|
+
"DeepSeek-V3.1-Terminus",
|
|
6169
|
+
"DeepSeek-V3.1-Think",
|
|
6170
|
+
"DeepSeek-V3.2-Exp",
|
|
6171
|
+
"DeepSeek-V3.2-Exp-Think",
|
|
6172
|
+
"ERNIE-X1.1-Preview",
|
|
6173
|
+
"Kimi-K2-0905",
|
|
6174
|
+
"Qwen/QwQ-32B",
|
|
6175
|
+
"Qwen/Qwen2.5-VL-32B-Instruct",
|
|
6176
|
+
"baidu/ERNIE-4.5-300B-A47B",
|
|
6177
|
+
"bge-large-en",
|
|
6178
|
+
"bge-large-zh",
|
|
6179
|
+
"cc-MiniMax-M2",
|
|
6180
|
+
"cc-deepseek-v3.1",
|
|
6181
|
+
"cc-ernie-4.5-300b-a47b",
|
|
6182
|
+
"cc-kimi-k2-instruct",
|
|
6183
|
+
"cc-kimi-k2-instruct-0905",
|
|
6184
|
+
"cc-minimax-m2",
|
|
6185
|
+
"cc-minimax-m2.1",
|
|
6186
|
+
"cc-minimax-m2.5",
|
|
6187
|
+
"claude-3-5-haiku",
|
|
6188
|
+
"claude-3-5-sonnet",
|
|
6189
|
+
"claude-3-7-sonnet",
|
|
6190
|
+
"claude-haiku-4-5",
|
|
6191
|
+
"claude-opus-4-0",
|
|
6192
|
+
"claude-opus-4-1",
|
|
6193
|
+
"claude-opus-4-5",
|
|
6194
|
+
"claude-opus-4-5-think",
|
|
6195
|
+
"claude-opus-4-6",
|
|
6196
|
+
"claude-opus-4-6-think",
|
|
6197
|
+
"claude-sonnet-4-0",
|
|
6198
|
+
"claude-sonnet-4-5",
|
|
6199
|
+
"claude-sonnet-4-5-think",
|
|
6200
|
+
"claude-sonnet-4-6",
|
|
6201
|
+
"claude-sonnet-4-6-think",
|
|
6202
|
+
"coding-glm-4.6",
|
|
6203
|
+
"coding-glm-4.6-free",
|
|
6204
|
+
"coding-glm-4.7",
|
|
6205
|
+
"coding-glm-4.7-free",
|
|
6206
|
+
"coding-glm-5",
|
|
6207
|
+
"coding-glm-5-free",
|
|
6208
|
+
"coding-minimax-m2",
|
|
6209
|
+
"coding-minimax-m2-free",
|
|
6210
|
+
"coding-minimax-m2.1",
|
|
6211
|
+
"coding-minimax-m2.5",
|
|
6212
|
+
"deepseek-v3.2",
|
|
6213
|
+
"deepseek-v3.2-think",
|
|
6214
|
+
"doubao-seed-1-8",
|
|
6215
|
+
"doubao-seed-2-0-code-preview",
|
|
6216
|
+
"doubao-seed-2-0-lite",
|
|
6217
|
+
"doubao-seed-2-0-mini",
|
|
6218
|
+
"doubao-seed-2-0-pro",
|
|
6219
|
+
"ernie-4.5",
|
|
6220
|
+
"ernie-4.5-0.3b",
|
|
6221
|
+
"ernie-4.5-turbo-128k-preview",
|
|
6222
|
+
"ernie-4.5-turbo-latest",
|
|
6223
|
+
"ernie-4.5-turbo-vl",
|
|
6224
|
+
"ernie-irag-edit",
|
|
6225
|
+
"ernie-x1-turbo",
|
|
6226
|
+
"gemini-2.0-flash",
|
|
6227
|
+
"gemini-2.0-flash-exp-search",
|
|
6228
|
+
"gemini-2.0-flash-free",
|
|
6229
|
+
"gemini-2.5-flash",
|
|
6230
|
+
"gemini-2.5-flash-lite",
|
|
6231
|
+
"gemini-2.5-flash-lite-preview-09-2025",
|
|
6232
|
+
"gemini-2.5-flash-nothink",
|
|
6233
|
+
"gemini-2.5-flash-preview-05-20-nothink",
|
|
6234
|
+
"gemini-2.5-flash-preview-05-20-search",
|
|
6235
|
+
"gemini-2.5-flash-preview-09-2025",
|
|
6236
|
+
"gemini-2.5-flash-search",
|
|
6237
|
+
"gemini-2.5-pro",
|
|
6238
|
+
"gemini-2.5-pro-preview-03-25",
|
|
6239
|
+
"gemini-2.5-pro-preview-03-25-search",
|
|
6240
|
+
"gemini-2.5-pro-preview-06-05",
|
|
6241
|
+
"gemini-2.5-pro-preview-06-05-search",
|
|
6242
|
+
"gemini-2.5-pro-search",
|
|
6243
|
+
"gemini-3-flash-preview",
|
|
6244
|
+
"gemini-3-flash-preview-free",
|
|
6245
|
+
"gemini-3-flash-preview-search",
|
|
6246
|
+
"gemini-3-pro-preview",
|
|
6247
|
+
"gemini-3-pro-preview-search",
|
|
6248
|
+
"gemini-3.1-pro-preview",
|
|
6249
|
+
"gemini-3.1-pro-preview-customtools",
|
|
6250
|
+
"gemini-3.1-pro-preview-search",
|
|
6251
|
+
"glm-4.6",
|
|
6252
|
+
"glm-4.7",
|
|
6253
|
+
"glm-4.7-flash-free",
|
|
6254
|
+
"gpt-4.1",
|
|
6255
|
+
"gpt-4.1-free",
|
|
6256
|
+
"gpt-4.1-mini",
|
|
6257
|
+
"gpt-4.1-mini-free",
|
|
6258
|
+
"gpt-4.1-nano",
|
|
6259
|
+
"gpt-4.1-nano-free",
|
|
6260
|
+
"gpt-4o",
|
|
6261
|
+
"gpt-4o-free",
|
|
6262
|
+
"gpt-5",
|
|
6263
|
+
"gpt-5-codex",
|
|
6264
|
+
"gpt-5-mini",
|
|
6265
|
+
"gpt-5-nano",
|
|
6266
|
+
"gpt-5-pro",
|
|
6267
|
+
"gpt-5.1",
|
|
6268
|
+
"gpt-5.2",
|
|
6269
|
+
"gpt-5.2-high",
|
|
6270
|
+
"gpt-5.2-low",
|
|
6271
|
+
"gpt-5.2-pro",
|
|
6272
|
+
"grok-4-1-fast-non-reasoning",
|
|
6273
|
+
"grok-4-1-fast-reasoning",
|
|
6274
|
+
"grok-code-fast-1",
|
|
6275
|
+
"inclusionAI/Ling-1T",
|
|
6276
|
+
"inclusionAI/Ling-flash-2.0",
|
|
6277
|
+
"inclusionAI/Ling-mini-2.0",
|
|
6278
|
+
"inclusionAI/Ring-1T",
|
|
6279
|
+
"inclusionAI/Ring-flash-2.0",
|
|
6280
|
+
"kimi-for-coding-free",
|
|
6281
|
+
"kimi-k2-0711",
|
|
6282
|
+
"kimi-k2-thinking",
|
|
6283
|
+
"kimi-k2-turbo-preview",
|
|
6284
|
+
"llama-4-maverick",
|
|
6285
|
+
"llama-4-scout",
|
|
6286
|
+
"o3",
|
|
6287
|
+
"o3-pro",
|
|
6288
|
+
"qwen3-235b-a22b",
|
|
6289
|
+
"qwen3-235b-a22b-instruct-2507",
|
|
6290
|
+
"qwen3-235b-a22b-thinking-2507",
|
|
6291
|
+
"qwen3-coder-30b-a3b-instruct",
|
|
6292
|
+
"qwen3-coder-480b-a35b-instruct",
|
|
6293
|
+
"qwen3-coder-flash",
|
|
6294
|
+
"qwen3-coder-next",
|
|
6295
|
+
"qwen3-coder-plus",
|
|
6296
|
+
"qwen3-coder-plus-2025-07-22",
|
|
6297
|
+
"qwen3-max",
|
|
6298
|
+
"qwen3-max-preview",
|
|
6299
|
+
"qwen3-next-80b-a3b-instruct",
|
|
6300
|
+
"qwen3-next-80b-a3b-thinking",
|
|
6301
|
+
"qwen3-vl-235b-a22b-instruct",
|
|
6302
|
+
"qwen3-vl-235b-a22b-thinking",
|
|
6303
|
+
"qwen3-vl-30b-a3b-instruct",
|
|
6304
|
+
"qwen3-vl-30b-a3b-thinking",
|
|
6305
|
+
"qwen3-vl-flash",
|
|
6306
|
+
"qwen3-vl-flash-2026-01-22",
|
|
6307
|
+
"qwen3-vl-plus",
|
|
6308
|
+
"qwen3.5-397b-a17b",
|
|
6309
|
+
"qwen3.5-plus"
|
|
6310
|
+
]
|
|
6311
|
+
}
|
|
6312
|
+
];
|
|
6313
|
+
|
|
6065
6314
|
// src/managers/model-manager.ts
|
|
6066
6315
|
class ModelManager {
|
|
6067
6316
|
metadataManager;
|
|
@@ -6070,7 +6319,10 @@ class ModelManager {
|
|
|
6070
6319
|
}
|
|
6071
6320
|
async getAvailableModels(provider) {
|
|
6072
6321
|
const providerId = provider.toLowerCase();
|
|
6073
|
-
|
|
6322
|
+
let providerData = PROVIDER_DATA.find((p) => p.id === providerId);
|
|
6323
|
+
if (!providerData) {
|
|
6324
|
+
providerData = GENERATED_DATA.find((p) => p.id === providerId);
|
|
6325
|
+
}
|
|
6074
6326
|
if (!providerData) {
|
|
6075
6327
|
throw new Error(`Provider "${provider}" is not supported`);
|
|
6076
6328
|
}
|
|
@@ -6961,6 +7213,15 @@ new file mode 100644
|
|
|
6961
7213
|
} catch {
|
|
6962
7214
|
return c.json({ error: `Path is not a git repository: ${absolutePath}` }, 400);
|
|
6963
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
|
+
});
|
|
6964
7225
|
const diff = await new Promise((resolveDiff, reject) => {
|
|
6965
7226
|
const runPlainDiff = () => {
|
|
6966
7227
|
const proc2 = spawn8("git", ["diff"], { cwd: gitRoot });
|
|
@@ -6993,19 +7254,97 @@ new file mode 100644
|
|
|
6993
7254
|
});
|
|
6994
7255
|
proc.on("error", reject);
|
|
6995
7256
|
});
|
|
6996
|
-
|
|
6997
|
-
|
|
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
|
+
});
|
|
7297
|
+
}
|
|
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
|
+
});
|
|
6998
7312
|
}
|
|
6999
|
-
|
|
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) + `
|
|
7000
7334
|
...(truncated)` : diff;
|
|
7001
|
-
|
|
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:
|
|
7002
7343
|
- Title: Concise, descriptive, under 72 characters
|
|
7003
7344
|
- Description: Clear explanation of changes, why they were made, and any relevant context
|
|
7004
|
-
- Use markdown formatting for the description
|
|
7345
|
+
- Use markdown formatting for the description (bullet points, headers, etc.)
|
|
7005
7346
|
- Be professional and clear
|
|
7006
|
-
|
|
7007
|
-
Git diff:
|
|
7008
|
-
${truncatedDiff}
|
|
7347
|
+
- If multiple changes, group them logically in the description
|
|
7009
7348
|
|
|
7010
7349
|
Generate the response in this exact format:
|
|
7011
7350
|
TITLE: <title here>
|
|
@@ -7030,7 +7369,7 @@ DESCRIPTION: <description here>`;
|
|
|
7030
7369
|
const descriptionMatch = prInfo.match(/DESCRIPTION:\s*(.+?)$/s);
|
|
7031
7370
|
const title = titleMatch ? titleMatch[1].trim() : "Update";
|
|
7032
7371
|
const description = descriptionMatch ? descriptionMatch[1].trim() : "";
|
|
7033
|
-
return c.json({ title, description });
|
|
7372
|
+
return c.json({ title, description, baseBranch: effectiveBaseBranch, currentBranch });
|
|
7034
7373
|
} catch (error) {
|
|
7035
7374
|
const message = error instanceof Error ? error.message : "Failed to generate PR info";
|
|
7036
7375
|
return c.json({ error: message }, 500);
|
|
@@ -7147,6 +7486,148 @@ DESCRIPTION: <description here>`;
|
|
|
7147
7486
|
return c.json({ error: message }, 500);
|
|
7148
7487
|
}
|
|
7149
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
|
+
});
|
|
7150
7631
|
return router;
|
|
7151
7632
|
}
|
|
7152
7633
|
|