tarsk 0.4.22 → 0.4.24
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 +491 -200
- package/dist/public/assets/{account-view-pOQZWJfl.js → account-view-D5WVMsrD.js} +1 -1
- package/dist/public/assets/{alert-dialog-BeLER45U.js → alert-dialog-JOYww-on.js} +1 -1
- package/dist/public/assets/{api-Bagaj1rQ.js → api-XK7H2gI0.js} +1 -1
- package/dist/public/assets/{browser-tab-DmootUHa.js → browser-tab-xojjEFYj.js} +1 -1
- package/dist/public/assets/chat-input-container-fUw0AY1R.js +1 -0
- package/dist/public/assets/{context-menu-BlG_bTqN.js → context-menu-5lS4H_pS.js} +1 -1
- package/dist/public/assets/conversation-history-view-IVxKEbP_.js +1 -0
- package/dist/public/assets/{dialogs-config-Dwpv4SNp.js → dialogs-config-3Hr2GRdt.js} +2 -2
- package/dist/public/assets/{diff-view-B8Grak1p.js → diff-view-DWNDi8zT.js} +2 -2
- package/dist/public/assets/{explorer-tab-view-lFzzjVe8.js → explorer-tab-view-BtTXy75I.js} +2 -2
- package/dist/public/assets/explorer-tree-DdPiBm7Y.js +1 -0
- package/dist/public/assets/{explorer-view-DjN4dtgh.js → explorer-view-06kjYKL2.js} +1 -1
- package/dist/public/assets/history-view-Cyhu-ped.js +1 -0
- package/dist/public/assets/index-CoryOY5D.css +1 -0
- package/dist/public/assets/index-DJNTxm7E.js +50 -0
- package/dist/public/assets/onboarding-DzgFi5ix.js +1 -0
- package/dist/public/assets/onboarding-dialog-lFFrB1GZ.js +1 -0
- package/dist/public/assets/{page-toolbar-CBE5_3E3.js → page-toolbar-De9hbhUD.js} +1 -1
- package/dist/public/assets/{project-settings-view-B13GlkU6.js → project-settings-view-DF-F7P8K.js} +1 -1
- package/dist/public/assets/{provider-details-view-BQQKYvR8.js → provider-details-view-CbGbbmaI.js} +1 -1
- package/dist/public/assets/{providers-sidebar-C2RknYQn.js → providers-sidebar-Dc3Gk1we.js} +1 -1
- package/dist/public/assets/{react-vendor-CYvpNUsM.js → react-vendor-BX34Jw0L.js} +5 -5
- package/dist/public/assets/resizable-asBmgm9X.js +1 -0
- package/dist/public/assets/{run-stop-button-1ECLRujs.js → run-stop-button-MczHUMXL.js} +2 -2
- package/dist/public/assets/{settings-view-CxiVTevy.js → settings-view-CtiXQd-N.js} +2 -2
- package/dist/public/assets/{side-panel-container-B9Dn9qD2.js → side-panel-container-5txNmqNe.js} +2 -2
- package/dist/public/assets/{standard-list-item-BJWg4I9s.js → standard-list-item-DUKJ8HEG.js} +1 -1
- package/dist/public/assets/store-xK3nfJ3V.js +2 -0
- package/dist/public/assets/{tab-context-COCYkRNN.js → tab-context-D2auNMAI.js} +1 -1
- package/dist/public/assets/tabs-B7OXJglU.js +1 -0
- package/dist/public/assets/{terminal-panel-Cea0pLdX.js → terminal-panel-D3OZDoCd.js} +2 -2
- package/dist/public/assets/{textarea-C5IMo2ya.js → textarea-Dg4M60Yx.js} +1 -1
- package/dist/public/assets/todos-view-CzW8DtQ9.js +1 -0
- package/dist/public/assets/{use-toast-rlEtMnzM.js → use-toast-C3jyoS6i.js} +1 -1
- package/dist/public/assets/{utils-C3gmypZX.js → utils-4yrqj6yI.js} +1 -1
- package/dist/public/index.html +19 -19
- package/package.json +1 -1
- package/dist/public/assets/chat-input-container-CfXgimBQ.js +0 -1
- package/dist/public/assets/conversation-history-view-DjO711iz.js +0 -1
- package/dist/public/assets/explorer-tree-CyWyxQvY.js +0 -1
- package/dist/public/assets/history-view-BvAOaO8q.js +0 -1
- package/dist/public/assets/index-BPF49zet.css +0 -1
- package/dist/public/assets/index-C7dA1l3u.js +0 -50
- package/dist/public/assets/onboarding-dialog-DSmKEXk4.js +0 -1
- package/dist/public/assets/onboarding-vGz3IczM.js +0 -1
- package/dist/public/assets/resizable-C82J5y6K.js +0 -1
- package/dist/public/assets/store-Cn1lT5YJ.js +0 -2
- package/dist/public/assets/tabs-M7nUZf6O.js +0 -1
- package/dist/public/assets/todos-view-DDVfCfgF.js +0 -1
package/dist/index.js
CHANGED
|
@@ -188,6 +188,7 @@ async function initializeSchema(db) {
|
|
|
188
188
|
description TEXT NOT NULL,
|
|
189
189
|
status TEXT NOT NULL DEFAULT 'pending',
|
|
190
190
|
assignedTo TEXT NOT NULL DEFAULT 'agent',
|
|
191
|
+
passes INTEGER NOT NULL DEFAULT 0,
|
|
191
192
|
createdAt TEXT NOT NULL,
|
|
192
193
|
updatedAt TEXT NOT NULL,
|
|
193
194
|
FOREIGN KEY (threadId) REFERENCES threads(id) ON DELETE CASCADE
|
|
@@ -638,6 +639,25 @@ async function runMigrations(db) {
|
|
|
638
639
|
await db.execute(`ALTER TABLE projects ADD COLUMN testPrompt TEXT`);
|
|
639
640
|
await db.execute(`ALTER TABLE projects ADD COLUMN reviewPrompt TEXT`);
|
|
640
641
|
}
|
|
642
|
+
const todosInfo = await db.execute(`PRAGMA table_info(todos)`);
|
|
643
|
+
const hasTodoPasses = todosInfo.rows.some(
|
|
644
|
+
(col) => col.name === "passes"
|
|
645
|
+
);
|
|
646
|
+
if (!hasTodoPasses) {
|
|
647
|
+
console.log("[db] Running migration: Adding passes column to todos");
|
|
648
|
+
await db.execute(`ALTER TABLE todos ADD COLUMN passes INTEGER NOT NULL DEFAULT 0`);
|
|
649
|
+
}
|
|
650
|
+
const hasRalphMode = convInfo.rows.some(
|
|
651
|
+
(col) => col.name === "request_ralphMode"
|
|
652
|
+
);
|
|
653
|
+
if (!hasRalphMode) {
|
|
654
|
+
console.log(
|
|
655
|
+
"[db] Running migration: Adding request_ralphMode column to conversation_history"
|
|
656
|
+
);
|
|
657
|
+
await db.execute(
|
|
658
|
+
`ALTER TABLE conversation_history ADD COLUMN request_ralphMode INTEGER NOT NULL DEFAULT 0`
|
|
659
|
+
);
|
|
660
|
+
}
|
|
641
661
|
const hasValidationScript = projectsInfo.rows.some(
|
|
642
662
|
(col) => col.name === "validationScript"
|
|
643
663
|
);
|
|
@@ -3179,11 +3199,14 @@ init_database();
|
|
|
3179
3199
|
import { randomUUID } from "crypto";
|
|
3180
3200
|
async function fetchAllTodos(db, threadId) {
|
|
3181
3201
|
const result = await db.execute({
|
|
3182
|
-
sql: `SELECT id, threadId, description, status, assignedTo, createdAt, updatedAt
|
|
3202
|
+
sql: `SELECT id, threadId, description, status, assignedTo, passes, createdAt, updatedAt
|
|
3183
3203
|
FROM todos WHERE threadId = ? ORDER BY createdAt ASC`,
|
|
3184
3204
|
args: [threadId]
|
|
3185
3205
|
});
|
|
3186
|
-
return result.rows
|
|
3206
|
+
return result.rows.map((row) => ({
|
|
3207
|
+
...row,
|
|
3208
|
+
passes: row.passes === 1
|
|
3209
|
+
}));
|
|
3187
3210
|
}
|
|
3188
3211
|
async function addTodos(threadId, descriptions, assignedTo = "agent") {
|
|
3189
3212
|
const db = await getDatabase();
|
|
@@ -3192,8 +3215,8 @@ async function addTodos(threadId, descriptions, assignedTo = "agent") {
|
|
|
3192
3215
|
const id = randomUUID();
|
|
3193
3216
|
const ts = new Date(baseTime + i).toISOString();
|
|
3194
3217
|
await db.execute({
|
|
3195
|
-
sql: `INSERT INTO todos (id, threadId, description, status, assignedTo, createdAt, updatedAt)
|
|
3196
|
-
VALUES (?, ?, ?, 'pending', ?, ?, ?)`,
|
|
3218
|
+
sql: `INSERT INTO todos (id, threadId, description, status, assignedTo, passes, createdAt, updatedAt)
|
|
3219
|
+
VALUES (?, ?, ?, 'pending', ?, 0, ?, ?)`,
|
|
3197
3220
|
args: [id, threadId, descriptions[i].trim(), assignedTo, ts, ts]
|
|
3198
3221
|
});
|
|
3199
3222
|
}
|
|
@@ -3206,13 +3229,71 @@ async function clearTodos(threadId) {
|
|
|
3206
3229
|
args: [threadId]
|
|
3207
3230
|
});
|
|
3208
3231
|
}
|
|
3232
|
+
async function clearTodosIfAllDone(db, threadId) {
|
|
3233
|
+
const result = await db.execute({
|
|
3234
|
+
sql: `SELECT status FROM todos WHERE threadId = ?`,
|
|
3235
|
+
args: [threadId]
|
|
3236
|
+
});
|
|
3237
|
+
if (result.rows.length === 0) {
|
|
3238
|
+
return false;
|
|
3239
|
+
}
|
|
3240
|
+
const allDone = result.rows.every(
|
|
3241
|
+
(row) => row.status === "done"
|
|
3242
|
+
);
|
|
3243
|
+
if (!allDone) {
|
|
3244
|
+
return false;
|
|
3245
|
+
}
|
|
3246
|
+
await db.execute({
|
|
3247
|
+
sql: "DELETE FROM todos WHERE threadId = ?",
|
|
3248
|
+
args: [threadId]
|
|
3249
|
+
});
|
|
3250
|
+
return true;
|
|
3251
|
+
}
|
|
3252
|
+
|
|
3253
|
+
// src/features/ralph/ralph.progress.ts
|
|
3254
|
+
import { readFile as readFile3, appendFile, writeFile } from "fs/promises";
|
|
3255
|
+
import { join as join6 } from "path";
|
|
3256
|
+
var PROGRESS_FILE = "progress.txt";
|
|
3257
|
+
async function readProgress(threadPath) {
|
|
3258
|
+
try {
|
|
3259
|
+
return await readFile3(join6(threadPath, PROGRESS_FILE), "utf-8");
|
|
3260
|
+
} catch (err) {
|
|
3261
|
+
if (err.code === "ENOENT") {
|
|
3262
|
+
return "";
|
|
3263
|
+
}
|
|
3264
|
+
throw err;
|
|
3265
|
+
}
|
|
3266
|
+
}
|
|
3267
|
+
async function appendProgress(threadPath, learnings) {
|
|
3268
|
+
const filePath = join6(threadPath, PROGRESS_FILE);
|
|
3269
|
+
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
3270
|
+
const entry = `
|
|
3271
|
+
--- ${timestamp} ---
|
|
3272
|
+
${learnings}
|
|
3273
|
+
`;
|
|
3274
|
+
try {
|
|
3275
|
+
await appendFile(filePath, entry, "utf-8");
|
|
3276
|
+
} catch (err) {
|
|
3277
|
+
if (err.code === "ENOENT") {
|
|
3278
|
+
await writeFile(filePath, entry, "utf-8");
|
|
3279
|
+
} else {
|
|
3280
|
+
throw err;
|
|
3281
|
+
}
|
|
3282
|
+
}
|
|
3283
|
+
}
|
|
3209
3284
|
|
|
3210
3285
|
// src/tools/todo.ts
|
|
3211
3286
|
var todoSchema = Type12.Object({
|
|
3212
3287
|
action: Type12.Union(
|
|
3213
|
-
[
|
|
3288
|
+
[
|
|
3289
|
+
Type12.Literal("add"),
|
|
3290
|
+
Type12.Literal("update"),
|
|
3291
|
+
Type12.Literal("list"),
|
|
3292
|
+
Type12.Literal("delete"),
|
|
3293
|
+
Type12.Literal("learnings")
|
|
3294
|
+
],
|
|
3214
3295
|
{
|
|
3215
|
-
description: "Operation to perform: 'add' creates new items, 'update' changes status, 'list' returns all items, 'delete' removes items."
|
|
3296
|
+
description: "Operation to perform: 'add' creates new items, 'update' changes status, 'list' returns all items, 'delete' removes items, 'learnings' appends text to progress.txt for cross-iteration memory."
|
|
3216
3297
|
}
|
|
3217
3298
|
),
|
|
3218
3299
|
descriptions: Type12.Optional(
|
|
@@ -3234,15 +3315,25 @@ var todoSchema = Type12.Object({
|
|
|
3234
3315
|
Type12.String({
|
|
3235
3316
|
description: "Who items are assigned to. Defaults to 'agent'. Applied to all specified items."
|
|
3236
3317
|
})
|
|
3318
|
+
),
|
|
3319
|
+
passes: Type12.Optional(
|
|
3320
|
+
Type12.Boolean({
|
|
3321
|
+
description: "Whether the story's quality checks passed. Used in Ralph mode to mark stories as verified. Set to true when checks pass after implementation."
|
|
3322
|
+
})
|
|
3323
|
+
),
|
|
3324
|
+
learnings: Type12.Optional(
|
|
3325
|
+
Type12.String({
|
|
3326
|
+
description: "Text to append to progress.txt. Required for 'learnings' action. Records patterns, gotchas, and context discovered during implementation."
|
|
3327
|
+
})
|
|
3237
3328
|
)
|
|
3238
3329
|
});
|
|
3239
|
-
function createTodoTool(threadId) {
|
|
3330
|
+
function createTodoTool(threadId, threadPath) {
|
|
3240
3331
|
return {
|
|
3241
3332
|
name: "todo",
|
|
3242
3333
|
label: "todo",
|
|
3243
3334
|
description: "Manage todo items to break down the current task into 2 more items. Use 'add' to create items when planning work, 'update' to change an item's status as you progress (pending \u2192 working \u2192 done), 'list' to see all current items, and 'delete' to remove one. Marking an item done does not end the conversation \u2014 continue working on remaining items. Call 'list' at the end so the user sees a completion checklist.",
|
|
3244
3335
|
parameters: todoSchema,
|
|
3245
|
-
execute: async (_toolCallId, { action, descriptions, ids, status, assignedTo }) => {
|
|
3336
|
+
execute: async (_toolCallId, { action, descriptions, ids, status, assignedTo, passes, learnings }) => {
|
|
3246
3337
|
const db = await getDatabase();
|
|
3247
3338
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
3248
3339
|
switch (action) {
|
|
@@ -3294,6 +3385,10 @@ function createTodoTool(threadId) {
|
|
|
3294
3385
|
updates.push("assignedTo = ?");
|
|
3295
3386
|
args2.push(assignedTo);
|
|
3296
3387
|
}
|
|
3388
|
+
if (passes !== void 0) {
|
|
3389
|
+
updates.push("passes = ?");
|
|
3390
|
+
args2.push(passes ? 1 : 0);
|
|
3391
|
+
}
|
|
3297
3392
|
updates.push("updatedAt = ?");
|
|
3298
3393
|
args2.push(now);
|
|
3299
3394
|
args2.push(id, threadId);
|
|
@@ -3360,6 +3455,40 @@ function createTodoTool(threadId) {
|
|
|
3360
3455
|
details: void 0
|
|
3361
3456
|
};
|
|
3362
3457
|
}
|
|
3458
|
+
case "learnings": {
|
|
3459
|
+
if (!learnings?.trim()) {
|
|
3460
|
+
return {
|
|
3461
|
+
content: [
|
|
3462
|
+
{
|
|
3463
|
+
type: "text",
|
|
3464
|
+
text: "Error: learnings text is required for 'learnings' action"
|
|
3465
|
+
}
|
|
3466
|
+
],
|
|
3467
|
+
details: void 0
|
|
3468
|
+
};
|
|
3469
|
+
}
|
|
3470
|
+
if (!threadPath) {
|
|
3471
|
+
return {
|
|
3472
|
+
content: [
|
|
3473
|
+
{
|
|
3474
|
+
type: "text",
|
|
3475
|
+
text: "Error: threadPath is not available, cannot write progress.txt"
|
|
3476
|
+
}
|
|
3477
|
+
],
|
|
3478
|
+
details: void 0
|
|
3479
|
+
};
|
|
3480
|
+
}
|
|
3481
|
+
await appendProgress(threadPath, learnings.trim());
|
|
3482
|
+
return {
|
|
3483
|
+
content: [
|
|
3484
|
+
{
|
|
3485
|
+
type: "text",
|
|
3486
|
+
text: "Learnings appended to progress.txt successfully."
|
|
3487
|
+
}
|
|
3488
|
+
],
|
|
3489
|
+
details: void 0
|
|
3490
|
+
};
|
|
3491
|
+
}
|
|
3363
3492
|
default:
|
|
3364
3493
|
return {
|
|
3365
3494
|
content: [
|
|
@@ -3377,16 +3506,16 @@ function createTodoTool(threadId) {
|
|
|
3377
3506
|
var todoTool = createTodoTool("");
|
|
3378
3507
|
|
|
3379
3508
|
// src/features/mcp/mcp.config.ts
|
|
3380
|
-
import { readFile as
|
|
3381
|
-
import { join as
|
|
3509
|
+
import { readFile as readFile4, stat, access } from "fs/promises";
|
|
3510
|
+
import { join as join7 } from "path";
|
|
3382
3511
|
var MCP_CONFIG_PATHS = [".agents/mcp.json", "mcp.json"];
|
|
3383
3512
|
async function loadMCPConfig(projectPath) {
|
|
3384
3513
|
for (const configPath of MCP_CONFIG_PATHS) {
|
|
3385
|
-
const fullPath =
|
|
3514
|
+
const fullPath = join7(projectPath, configPath);
|
|
3386
3515
|
try {
|
|
3387
3516
|
await access(fullPath);
|
|
3388
3517
|
const fileStats = await stat(fullPath);
|
|
3389
|
-
const fileContent = await
|
|
3518
|
+
const fileContent = await readFile4(fullPath, "utf-8");
|
|
3390
3519
|
const rawConfig = JSON.parse(fileContent);
|
|
3391
3520
|
const serversData = rawConfig.servers || rawConfig.mcpServers || {};
|
|
3392
3521
|
const config = {
|
|
@@ -3411,7 +3540,7 @@ async function loadMCPConfig(projectPath) {
|
|
|
3411
3540
|
}
|
|
3412
3541
|
async function getMCPConfigModificationTime(projectPath) {
|
|
3413
3542
|
for (const configPath of MCP_CONFIG_PATHS) {
|
|
3414
|
-
const fullPath =
|
|
3543
|
+
const fullPath = join7(projectPath, configPath);
|
|
3415
3544
|
try {
|
|
3416
3545
|
await access(fullPath);
|
|
3417
3546
|
const fileStats = await stat(fullPath);
|
|
@@ -4734,7 +4863,7 @@ async function createCodingTools(cwd, options) {
|
|
|
4734
4863
|
createEditTool(cwd),
|
|
4735
4864
|
createWriteTool(cwd),
|
|
4736
4865
|
createAskUserTool(),
|
|
4737
|
-
createTodoTool(options?.threadId ?? ""),
|
|
4866
|
+
createTodoTool(options?.threadId ?? "", options?.threadPath),
|
|
4738
4867
|
createFindTool(cwd),
|
|
4739
4868
|
createCodeSearchTool(cwd, options?.threadId ?? ""),
|
|
4740
4869
|
createRunWebWorkerTool()
|
|
@@ -5274,10 +5403,10 @@ function createAgentTool(options) {
|
|
|
5274
5403
|
}
|
|
5275
5404
|
|
|
5276
5405
|
// src/core/paths.ts
|
|
5277
|
-
import { join as
|
|
5406
|
+
import { join as join8 } from "path";
|
|
5278
5407
|
import { homedir as homedir3 } from "os";
|
|
5279
|
-
var APP_SUPPORT_DIR =
|
|
5280
|
-
var DATA_DIR =
|
|
5408
|
+
var APP_SUPPORT_DIR = join8(homedir3(), "Library", "Application Support", "Tarsk");
|
|
5409
|
+
var DATA_DIR = join8(APP_SUPPORT_DIR, "data");
|
|
5281
5410
|
function getDataDir() {
|
|
5282
5411
|
return DATA_DIR;
|
|
5283
5412
|
}
|
|
@@ -5288,7 +5417,7 @@ import { readFileSync as readFileSync4 } from "fs";
|
|
|
5288
5417
|
|
|
5289
5418
|
// src/project-analyzer.ts
|
|
5290
5419
|
import { readFileSync as readFileSync3, existsSync as existsSync7 } from "fs";
|
|
5291
|
-
import { join as
|
|
5420
|
+
import { join as join9 } from "path";
|
|
5292
5421
|
var ProjectAnalyzer = class {
|
|
5293
5422
|
projectPath;
|
|
5294
5423
|
constructor(projectPath = process.cwd()) {
|
|
@@ -5299,7 +5428,7 @@ var ProjectAnalyzer = class {
|
|
|
5299
5428
|
return this.generateDescription(info);
|
|
5300
5429
|
}
|
|
5301
5430
|
getProjectInfo() {
|
|
5302
|
-
const packageJsonPath =
|
|
5431
|
+
const packageJsonPath = join9(this.projectPath, "package.json");
|
|
5303
5432
|
const info = { description: "" };
|
|
5304
5433
|
if (existsSync7(packageJsonPath)) {
|
|
5305
5434
|
const packageJson = JSON.parse(readFileSync3(packageJsonPath, "utf-8"));
|
|
@@ -5341,15 +5470,15 @@ var ProjectAnalyzer = class {
|
|
|
5341
5470
|
}
|
|
5342
5471
|
}
|
|
5343
5472
|
detectBuildTool(_scripts = {}, deps, info) {
|
|
5344
|
-
if (deps.vite || existsSync7(
|
|
5473
|
+
if (deps.vite || existsSync7(join9(this.projectPath, "vite.config.js")) || existsSync7(join9(this.projectPath, "vite.config.ts"))) {
|
|
5345
5474
|
info.buildTool = "Vite";
|
|
5346
5475
|
return;
|
|
5347
5476
|
}
|
|
5348
|
-
if (deps.webpack || existsSync7(
|
|
5477
|
+
if (deps.webpack || existsSync7(join9(this.projectPath, "webpack.config.js"))) {
|
|
5349
5478
|
info.buildTool = "Webpack";
|
|
5350
5479
|
return;
|
|
5351
5480
|
}
|
|
5352
|
-
if (deps.rollup || existsSync7(
|
|
5481
|
+
if (deps.rollup || existsSync7(join9(this.projectPath, "rollup.config.js"))) {
|
|
5353
5482
|
info.buildTool = "Rollup";
|
|
5354
5483
|
return;
|
|
5355
5484
|
}
|
|
@@ -5399,40 +5528,40 @@ var ProjectAnalyzer = class {
|
|
|
5399
5528
|
info.uiLibraries = [...new Set(uiLibs)].filter(Boolean);
|
|
5400
5529
|
}
|
|
5401
5530
|
detectProjectType(info) {
|
|
5402
|
-
if (existsSync7(
|
|
5531
|
+
if (existsSync7(join9(this.projectPath, ".xcodeproj")) || existsSync7(join9(this.projectPath, ".xcworkspace")) || existsSync7(join9(this.projectPath, "project.pbxproj"))) {
|
|
5403
5532
|
info.projectType = "Xcode";
|
|
5404
|
-
} else if (existsSync7(
|
|
5533
|
+
} else if (existsSync7(join9(this.projectPath, "build.gradle")) || existsSync7(join9(this.projectPath, "build.gradle.kts")) || existsSync7(join9(this.projectPath, "app/build.gradle")) || existsSync7(join9(this.projectPath, "settings.gradle"))) {
|
|
5405
5534
|
info.projectType = "Android Studio";
|
|
5406
|
-
} else if (existsSync7(
|
|
5535
|
+
} else if (existsSync7(join9(this.projectPath, "pubspec.yaml"))) {
|
|
5407
5536
|
info.projectType = "Flutter";
|
|
5408
|
-
} else if (existsSync7(
|
|
5537
|
+
} else if (existsSync7(join9(this.projectPath, "go.mod"))) {
|
|
5409
5538
|
info.projectType = "Go";
|
|
5410
|
-
} else if (existsSync7(
|
|
5539
|
+
} else if (existsSync7(join9(this.projectPath, "Cargo.toml"))) {
|
|
5411
5540
|
info.projectType = "Rust";
|
|
5412
|
-
} else if (existsSync7(
|
|
5541
|
+
} else if (existsSync7(join9(this.projectPath, "requirements.txt")) || existsSync7(join9(this.projectPath, "pyproject.toml")) || existsSync7(join9(this.projectPath, "setup.py"))) {
|
|
5413
5542
|
info.projectType = "Python";
|
|
5414
|
-
} else if (existsSync7(
|
|
5543
|
+
} else if (existsSync7(join9(this.projectPath, "Gemfile"))) {
|
|
5415
5544
|
info.projectType = "Ruby";
|
|
5416
|
-
} else if (existsSync7(
|
|
5545
|
+
} else if (existsSync7(join9(this.projectPath, "composer.json"))) {
|
|
5417
5546
|
info.projectType = "PHP";
|
|
5418
|
-
} else if (existsSync7(
|
|
5547
|
+
} else if (existsSync7(join9(this.projectPath, "pom.xml")) || existsSync7(join9(this.projectPath, "build.xml"))) {
|
|
5419
5548
|
info.projectType = "Java";
|
|
5420
|
-
} else if (existsSync7(
|
|
5549
|
+
} else if (existsSync7(join9(this.projectPath, ".csproj")) || existsSync7(join9(this.projectPath, "project.json"))) {
|
|
5421
5550
|
info.projectType = ".NET";
|
|
5422
5551
|
}
|
|
5423
5552
|
}
|
|
5424
5553
|
detectPackageManager(info) {
|
|
5425
|
-
if (existsSync7(
|
|
5554
|
+
if (existsSync7(join9(this.projectPath, "bun.lockb"))) {
|
|
5426
5555
|
info.packageManager = "Bun";
|
|
5427
|
-
} else if (existsSync7(
|
|
5556
|
+
} else if (existsSync7(join9(this.projectPath, "bun.lock"))) {
|
|
5428
5557
|
info.packageManager = "Bun";
|
|
5429
|
-
} else if (existsSync7(
|
|
5558
|
+
} else if (existsSync7(join9(this.projectPath, "pnpm-lock.yaml"))) {
|
|
5430
5559
|
info.packageManager = "pnpm";
|
|
5431
|
-
} else if (existsSync7(
|
|
5560
|
+
} else if (existsSync7(join9(this.projectPath, "yarn.lock"))) {
|
|
5432
5561
|
info.packageManager = "Yarn";
|
|
5433
|
-
} else if (existsSync7(
|
|
5562
|
+
} else if (existsSync7(join9(this.projectPath, "package-lock.json"))) {
|
|
5434
5563
|
info.packageManager = "npm";
|
|
5435
|
-
} else if (existsSync7(
|
|
5564
|
+
} else if (existsSync7(join9(this.projectPath, "npm-shrinkwrap.json"))) {
|
|
5436
5565
|
info.packageManager = "npm";
|
|
5437
5566
|
}
|
|
5438
5567
|
}
|
|
@@ -5494,8 +5623,8 @@ function analyzeProject(projectPath) {
|
|
|
5494
5623
|
}
|
|
5495
5624
|
|
|
5496
5625
|
// src/features/rules/rules.manager.ts
|
|
5497
|
-
import { readdir as readdir3, readFile as
|
|
5498
|
-
import { join as
|
|
5626
|
+
import { readdir as readdir3, readFile as readFile5 } from "fs/promises";
|
|
5627
|
+
import { join as join10, relative as relative4 } from "path";
|
|
5499
5628
|
import { existsSync as existsSync8 } from "fs";
|
|
5500
5629
|
import { homedir as homedir4 } from "os";
|
|
5501
5630
|
function parseRuleFrontmatter(markdown) {
|
|
@@ -5539,10 +5668,10 @@ function parseRuleFrontmatter(markdown) {
|
|
|
5539
5668
|
return { metadata, content };
|
|
5540
5669
|
}
|
|
5541
5670
|
function getGlobalRulesDir() {
|
|
5542
|
-
return
|
|
5671
|
+
return join10(homedir4(), ".agents", "rules");
|
|
5543
5672
|
}
|
|
5544
5673
|
function getProjectRulesDir(threadPath) {
|
|
5545
|
-
return
|
|
5674
|
+
return join10(threadPath, ".agents", "rules");
|
|
5546
5675
|
}
|
|
5547
5676
|
var RuleManager = class {
|
|
5548
5677
|
/**
|
|
@@ -5575,13 +5704,13 @@ var RuleManager = class {
|
|
|
5575
5704
|
try {
|
|
5576
5705
|
const entries = await readdir3(dir, { withFileTypes: true });
|
|
5577
5706
|
for (const entry of entries) {
|
|
5578
|
-
const fullPath =
|
|
5707
|
+
const fullPath = join10(dir, entry.name);
|
|
5579
5708
|
if (entry.isDirectory()) {
|
|
5580
5709
|
const subRules = await this.loadRulesFromDir(fullPath, threadPath, scope);
|
|
5581
5710
|
rules.push(...subRules);
|
|
5582
5711
|
} else if (entry.isFile() && (entry.name.endsWith(".md") || entry.name.endsWith(".mdc"))) {
|
|
5583
5712
|
try {
|
|
5584
|
-
const fileContent = await
|
|
5713
|
+
const fileContent = await readFile5(fullPath, "utf-8");
|
|
5585
5714
|
const { metadata, content } = parseRuleFrontmatter(fileContent);
|
|
5586
5715
|
const ruleName = entry.name.replace(/\.(md|mdc)$/, "");
|
|
5587
5716
|
const relativePath = relative4(threadPath, fullPath);
|
|
@@ -5741,6 +5870,43 @@ Structure your response as:
|
|
|
5741
5870
|
YOU MUST use the ask_user tool to ask clarifying questions if needed and to confirm implementation of the plan.
|
|
5742
5871
|
|
|
5743
5872
|
Remember: You are ONLY planning. Do NOT execute any changes.`;
|
|
5873
|
+
var RALPH_MODE_INSTRUCTIONS = `
|
|
5874
|
+
|
|
5875
|
+
# Ralph Mode (ACTIVE)
|
|
5876
|
+
|
|
5877
|
+
You are currently in RALPH MODE. This is an autonomous iterative loop that implements one story per iteration with fresh context between iterations.
|
|
5878
|
+
|
|
5879
|
+
## How Ralph Mode Works
|
|
5880
|
+
- You receive a list of stories (managed via the todo tool) and implement ONE story per iteration.
|
|
5881
|
+
- After each iteration, your context is cleared. Memory persists via git history, progress.txt, and the todo list state.
|
|
5882
|
+
- The loop continues automatically until all stories pass or the iteration limit is reached.
|
|
5883
|
+
|
|
5884
|
+
## What you MUST do each iteration:
|
|
5885
|
+
1. Call the todo tool with action 'list' to see all current stories and their status.
|
|
5886
|
+
2. Pick the highest-priority story where passes is false (earliest in the list).
|
|
5887
|
+
3. Implement ONLY that one story. Do not work on multiple stories.
|
|
5888
|
+
4. After implementation, run the project's quality checks (typecheck, tests, lint). Use the project's validation script or the check commands from the developer context (e.g. bun run check:all).
|
|
5889
|
+
5. If checks pass:
|
|
5890
|
+
- Mark the story as done with passes=true using the todo tool: action='update', ids=[storyId], status='done', passes=true
|
|
5891
|
+
- Commit your changes with a clear commit message describing the story.
|
|
5892
|
+
- Use the todo tool with action='learnings' to record what you learned (patterns discovered, gotchas, useful context).
|
|
5893
|
+
6. If checks fail: fix the issues and re-run checks. Keep iterating until they pass.
|
|
5894
|
+
7. After completing the story and recording learnings, output the marker: <ralph-iteration-complete/>
|
|
5895
|
+
|
|
5896
|
+
## What you MUST NOT do:
|
|
5897
|
+
- Do NOT work on more than one story per iteration.
|
|
5898
|
+
- Do NOT skip running quality checks.
|
|
5899
|
+
- Do NOT use emojis.
|
|
5900
|
+
|
|
5901
|
+
## Story sizing:
|
|
5902
|
+
Each story should be small enough to implement within one context window. If a story is too large, break it down before implementing.
|
|
5903
|
+
|
|
5904
|
+
## Progress and learnings:
|
|
5905
|
+
Use the todo tool's 'learnings' action to record:
|
|
5906
|
+
- Patterns discovered (e.g. "this codebase uses X for Y")
|
|
5907
|
+
- Gotchas (e.g. "do not forget to update Z when changing W")
|
|
5908
|
+
- Useful context for future iterations
|
|
5909
|
+
`;
|
|
5744
5910
|
function loadDeveloperContext(threadPath) {
|
|
5745
5911
|
try {
|
|
5746
5912
|
const agentsPath = resolve(threadPath, "agents.md");
|
|
@@ -5879,7 +6045,7 @@ function formatAgentsSection(agents) {
|
|
|
5879
6045
|
);
|
|
5880
6046
|
return section;
|
|
5881
6047
|
}
|
|
5882
|
-
async function loadAgentSystemPrompt(threadPath, tools, skills, planMode, agents, deferredTools) {
|
|
6048
|
+
async function loadAgentSystemPrompt(threadPath, tools, skills, planMode, agents, deferredTools, ralphMode) {
|
|
5883
6049
|
let prompt = buildDefaultPrompt(tools);
|
|
5884
6050
|
const agentsContent = loadDeveloperContext(threadPath);
|
|
5885
6051
|
if (agentsContent) {
|
|
@@ -5900,6 +6066,13 @@ async function loadAgentSystemPrompt(threadPath, tools, skills, planMode, agents
|
|
|
5900
6066
|
if (planMode) {
|
|
5901
6067
|
prompt += PLAN_MODE_INSTRUCTIONS;
|
|
5902
6068
|
}
|
|
6069
|
+
if (ralphMode) {
|
|
6070
|
+
prompt += RALPH_MODE_INSTRUCTIONS;
|
|
6071
|
+
const progressContent = await readProgress(threadPath);
|
|
6072
|
+
if (progressContent.trim()) {
|
|
6073
|
+
prompt += "\n\n# Prior Learnings (from progress.txt)\n\n" + progressContent;
|
|
6074
|
+
}
|
|
6075
|
+
}
|
|
5903
6076
|
const skillsSection = formatSkillsSection(skills ?? []);
|
|
5904
6077
|
if (skillsSection) {
|
|
5905
6078
|
prompt += skillsSection;
|
|
@@ -6008,6 +6181,7 @@ var PiExecutorImpl = class {
|
|
|
6008
6181
|
const { tools, deferredToolNames } = await createCodingTools(cwd, {
|
|
6009
6182
|
skills: context.skills,
|
|
6010
6183
|
threadId: context.threadId,
|
|
6184
|
+
threadPath: cwd,
|
|
6011
6185
|
metadataManager: this.metadataManager
|
|
6012
6186
|
});
|
|
6013
6187
|
const systemPrompt = await loadAgentSystemPrompt(
|
|
@@ -6016,7 +6190,8 @@ var PiExecutorImpl = class {
|
|
|
6016
6190
|
context.skills,
|
|
6017
6191
|
context.planMode,
|
|
6018
6192
|
context.agents,
|
|
6019
|
-
deferredToolNames
|
|
6193
|
+
deferredToolNames,
|
|
6194
|
+
context.ralphMode
|
|
6020
6195
|
);
|
|
6021
6196
|
console.log(
|
|
6022
6197
|
"[ai] System prompt loaded from thread path",
|
|
@@ -6458,8 +6633,8 @@ import { Hono as Hono2 } from "hono";
|
|
|
6458
6633
|
import { randomUUID as randomUUID4 } from "crypto";
|
|
6459
6634
|
|
|
6460
6635
|
// src/features/skills/skills.manager.ts
|
|
6461
|
-
import { readdir as readdir4, readFile as
|
|
6462
|
-
import { join as
|
|
6636
|
+
import { readdir as readdir4, readFile as readFile6 } from "fs/promises";
|
|
6637
|
+
import { join as join11 } from "path";
|
|
6463
6638
|
import { existsSync as existsSync9 } from "fs";
|
|
6464
6639
|
import { homedir as homedir5 } from "os";
|
|
6465
6640
|
function parseFrontmatter(markdown) {
|
|
@@ -6507,10 +6682,10 @@ function parseFrontmatter(markdown) {
|
|
|
6507
6682
|
return { metadata, content };
|
|
6508
6683
|
}
|
|
6509
6684
|
function getGlobalSkillsDir() {
|
|
6510
|
-
return
|
|
6685
|
+
return join11(homedir5(), ".agents", "skills");
|
|
6511
6686
|
}
|
|
6512
6687
|
function getProjectSkillsDir(threadPath) {
|
|
6513
|
-
return
|
|
6688
|
+
return join11(threadPath, ".agents", "skills");
|
|
6514
6689
|
}
|
|
6515
6690
|
function validateSkillName(name) {
|
|
6516
6691
|
if (!name || name.length === 0 || name.length > 64) {
|
|
@@ -6563,14 +6738,14 @@ var SkillManager = class {
|
|
|
6563
6738
|
for (const entry of entries) {
|
|
6564
6739
|
if (!entry.isDirectory()) continue;
|
|
6565
6740
|
const skillDirName = entry.name;
|
|
6566
|
-
const skillPath =
|
|
6567
|
-
const skillFilePath =
|
|
6741
|
+
const skillPath = join11(dir, skillDirName);
|
|
6742
|
+
const skillFilePath = join11(skillPath, "SKILL.md");
|
|
6568
6743
|
if (!existsSync9(skillFilePath)) {
|
|
6569
6744
|
console.warn(`Skipping skill directory ${skillDirName}: SKILL.md not found`);
|
|
6570
6745
|
continue;
|
|
6571
6746
|
}
|
|
6572
6747
|
try {
|
|
6573
|
-
const fileContent = await
|
|
6748
|
+
const fileContent = await readFile6(skillFilePath, "utf-8");
|
|
6574
6749
|
const { metadata, content } = parseFrontmatter(fileContent);
|
|
6575
6750
|
if (!metadata.name) {
|
|
6576
6751
|
console.warn(`Skipping skill in ${skillDirName}: missing 'name' in frontmatter`);
|
|
@@ -6743,8 +6918,8 @@ async function activateSkills(allSkills, taskDescription, thread, options) {
|
|
|
6743
6918
|
}
|
|
6744
6919
|
|
|
6745
6920
|
// src/features/agents/agents.manager.ts
|
|
6746
|
-
import { readdir as readdir5, readFile as
|
|
6747
|
-
import { join as
|
|
6921
|
+
import { readdir as readdir5, readFile as readFile7 } from "fs/promises";
|
|
6922
|
+
import { join as join12 } from "path";
|
|
6748
6923
|
import { existsSync as existsSync10 } from "fs";
|
|
6749
6924
|
import { homedir as homedir6 } from "os";
|
|
6750
6925
|
function parseFrontmatter2(markdown) {
|
|
@@ -6802,10 +6977,10 @@ function validateDescription2(description) {
|
|
|
6802
6977
|
return !!description && description.length > 0 && description.length <= 1024;
|
|
6803
6978
|
}
|
|
6804
6979
|
function getGlobalAgentsDir() {
|
|
6805
|
-
return
|
|
6980
|
+
return join12(homedir6(), ".agents", "agents");
|
|
6806
6981
|
}
|
|
6807
6982
|
function getProjectAgentsDir(threadPath) {
|
|
6808
|
-
return
|
|
6983
|
+
return join12(threadPath, ".agents", "agents");
|
|
6809
6984
|
}
|
|
6810
6985
|
var AgentsManager = class {
|
|
6811
6986
|
/**
|
|
@@ -6837,14 +7012,14 @@ var AgentsManager = class {
|
|
|
6837
7012
|
for (const entry of entries) {
|
|
6838
7013
|
if (!entry.isDirectory()) continue;
|
|
6839
7014
|
const agentDirName = entry.name;
|
|
6840
|
-
const agentPath =
|
|
6841
|
-
const agentFilePath =
|
|
7015
|
+
const agentPath = join12(dir, agentDirName);
|
|
7016
|
+
const agentFilePath = join12(agentPath, "AGENT.md");
|
|
6842
7017
|
if (!existsSync10(agentFilePath)) {
|
|
6843
7018
|
console.warn(`[agents] Skipping agent directory ${agentDirName}: AGENT.md not found`);
|
|
6844
7019
|
continue;
|
|
6845
7020
|
}
|
|
6846
7021
|
try {
|
|
6847
|
-
const fileContent = await
|
|
7022
|
+
const fileContent = await readFile7(agentFilePath, "utf-8");
|
|
6848
7023
|
const { metadata, content } = parseFrontmatter2(fileContent);
|
|
6849
7024
|
if (!metadata.name) {
|
|
6850
7025
|
console.warn(
|
|
@@ -7024,8 +7199,8 @@ function extractAssistantContent(events, fallback) {
|
|
|
7024
7199
|
|
|
7025
7200
|
// src/features/chat/chat-post.route.ts
|
|
7026
7201
|
init_database();
|
|
7027
|
-
import { readFile as
|
|
7028
|
-
import { join as
|
|
7202
|
+
import { readFile as readFile8, unlink } from "fs/promises";
|
|
7203
|
+
import { join as join13 } from "path";
|
|
7029
7204
|
|
|
7030
7205
|
// src/features/project-todos/project-todos.database.ts
|
|
7031
7206
|
init_database();
|
|
@@ -7592,7 +7767,15 @@ function runValidationScript(script, cwd) {
|
|
|
7592
7767
|
async function postChatMessage(c, threadManager, agentExecutor, conversationManager, processingStateManager) {
|
|
7593
7768
|
try {
|
|
7594
7769
|
const body = await c.req.json();
|
|
7595
|
-
const {
|
|
7770
|
+
const {
|
|
7771
|
+
threadId,
|
|
7772
|
+
content,
|
|
7773
|
+
model: baseModel,
|
|
7774
|
+
provider,
|
|
7775
|
+
attachments,
|
|
7776
|
+
planMode,
|
|
7777
|
+
ralphMode
|
|
7778
|
+
} = body;
|
|
7596
7779
|
let model = baseModel;
|
|
7597
7780
|
if (provider && typeof provider === "string") {
|
|
7598
7781
|
const providerPrefix = provider.toLowerCase();
|
|
@@ -7716,6 +7899,7 @@ async function postChatMessage(c, threadManager, agentExecutor, conversationMana
|
|
|
7716
7899
|
provider,
|
|
7717
7900
|
attachments,
|
|
7718
7901
|
planMode,
|
|
7902
|
+
ralphMode,
|
|
7719
7903
|
skills: activatedSkills,
|
|
7720
7904
|
agents
|
|
7721
7905
|
};
|
|
@@ -7741,7 +7925,9 @@ async function postChatMessage(c, threadManager, agentExecutor, conversationMana
|
|
|
7741
7925
|
content,
|
|
7742
7926
|
model,
|
|
7743
7927
|
attachments,
|
|
7744
|
-
planMode
|
|
7928
|
+
planMode,
|
|
7929
|
+
void 0,
|
|
7930
|
+
ralphMode
|
|
7745
7931
|
);
|
|
7746
7932
|
const history = await conversationManager.getConversationHistoryByConversationId(
|
|
7747
7933
|
threadId,
|
|
@@ -7854,7 +8040,9 @@ ${result.output}`;
|
|
|
7854
8040
|
validationPrompt,
|
|
7855
8041
|
model,
|
|
7856
8042
|
void 0,
|
|
7857
|
-
planMode
|
|
8043
|
+
planMode,
|
|
8044
|
+
void 0,
|
|
8045
|
+
ralphMode
|
|
7858
8046
|
);
|
|
7859
8047
|
const validationCaptured = { events: [], fullContent: "" };
|
|
7860
8048
|
yield* executeAgent(
|
|
@@ -7890,6 +8078,82 @@ ${result.output}`;
|
|
|
7890
8078
|
yield maxAttemptsEvent;
|
|
7891
8079
|
}
|
|
7892
8080
|
}
|
|
8081
|
+
if (ralphMode) {
|
|
8082
|
+
const MAX_RALPH_ITERATIONS = 10;
|
|
8083
|
+
let ralphIteration = 1;
|
|
8084
|
+
while (ralphIteration < MAX_RALPH_ITERATIONS) {
|
|
8085
|
+
if (abortController.signal.aborted) break;
|
|
8086
|
+
const db2 = await getDatabase();
|
|
8087
|
+
const todos = await fetchAllTodos(db2, threadId);
|
|
8088
|
+
const remaining = todos.filter((t) => !t.passes);
|
|
8089
|
+
if (remaining.length === 0) {
|
|
8090
|
+
const completeEvent = {
|
|
8091
|
+
type: "thinking",
|
|
8092
|
+
content: `[Ralph] All ${todos.length} stories completed successfully.`
|
|
8093
|
+
};
|
|
8094
|
+
processingStateManager.pushEvent(threadId, completeEvent);
|
|
8095
|
+
yield completeEvent;
|
|
8096
|
+
break;
|
|
8097
|
+
}
|
|
8098
|
+
ralphIteration++;
|
|
8099
|
+
const iterationEvent = {
|
|
8100
|
+
type: "thinking",
|
|
8101
|
+
content: `[Ralph] Iteration ${ralphIteration}/${MAX_RALPH_ITERATIONS} - ${remaining.length} stories remaining. Clearing context and starting next story...`
|
|
8102
|
+
};
|
|
8103
|
+
processingStateManager.pushEvent(threadId, iterationEvent);
|
|
8104
|
+
yield iterationEvent;
|
|
8105
|
+
const newConversationId = randomUUID4();
|
|
8106
|
+
await threadManager.updateThread(threadId, {
|
|
8107
|
+
currentConversationId: newConversationId
|
|
8108
|
+
});
|
|
8109
|
+
const ralphPrompt = "Continue the Ralph loop. List all stories, pick the next incomplete one (passes=false), and implement it. Follow the Ralph mode instructions.";
|
|
8110
|
+
const iterMessageId = await conversationManager.startMessage(
|
|
8111
|
+
threadId,
|
|
8112
|
+
threadPath,
|
|
8113
|
+
newConversationId,
|
|
8114
|
+
ralphPrompt,
|
|
8115
|
+
model,
|
|
8116
|
+
void 0,
|
|
8117
|
+
void 0,
|
|
8118
|
+
void 0,
|
|
8119
|
+
true
|
|
8120
|
+
);
|
|
8121
|
+
const iterCaptured = { events: [], fullContent: "" };
|
|
8122
|
+
const iterContext = { ...context, attachments: void 0 };
|
|
8123
|
+
yield* executeAgent(
|
|
8124
|
+
ralphPrompt,
|
|
8125
|
+
iterContext,
|
|
8126
|
+
abortController.signal,
|
|
8127
|
+
[],
|
|
8128
|
+
// Empty history - fresh context
|
|
8129
|
+
iterCaptured
|
|
8130
|
+
);
|
|
8131
|
+
const iterContent = extractAssistantContent(
|
|
8132
|
+
iterCaptured.events,
|
|
8133
|
+
iterCaptured.fullContent
|
|
8134
|
+
);
|
|
8135
|
+
await conversationManager.completeMessage(
|
|
8136
|
+
iterMessageId,
|
|
8137
|
+
iterContent,
|
|
8138
|
+
iterCaptured.events
|
|
8139
|
+
);
|
|
8140
|
+
decrementBalance().catch((err) => {
|
|
8141
|
+
console.error("[ChatRoute] Failed to decrement balance:", err);
|
|
8142
|
+
});
|
|
8143
|
+
}
|
|
8144
|
+
if (ralphIteration >= MAX_RALPH_ITERATIONS) {
|
|
8145
|
+
const finalTodos = await fetchAllTodos(db, threadId);
|
|
8146
|
+
const remaining = finalTodos.filter((todo) => todo.status !== "done");
|
|
8147
|
+
if (remaining.length > 0) {
|
|
8148
|
+
const limitEvent = {
|
|
8149
|
+
type: "thinking",
|
|
8150
|
+
content: `[Ralph] Reached maximum iteration limit (${MAX_RALPH_ITERATIONS}). Stopping.`
|
|
8151
|
+
};
|
|
8152
|
+
processingStateManager.pushEvent(threadId, limitEvent);
|
|
8153
|
+
yield limitEvent;
|
|
8154
|
+
}
|
|
8155
|
+
}
|
|
8156
|
+
}
|
|
7893
8157
|
} catch (error) {
|
|
7894
8158
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
7895
8159
|
const errorEvent = {
|
|
@@ -7923,12 +8187,15 @@ ${result.output}`;
|
|
|
7923
8187
|
const db = await getDatabase();
|
|
7924
8188
|
await invalidateGitStatusCache(db, threadId);
|
|
7925
8189
|
await clearWorkingByThreadId(db, threadId);
|
|
8190
|
+
if (!ralphMode) {
|
|
8191
|
+
await clearTodosIfAllDone(db, threadId);
|
|
8192
|
+
}
|
|
7926
8193
|
try {
|
|
7927
8194
|
const todo = await getTodoByThreadId(db, threadId);
|
|
7928
8195
|
if (todo && todo.status === "Plan") {
|
|
7929
|
-
const planFilePath =
|
|
8196
|
+
const planFilePath = join13(threadPath, `${todo.id}-plan.md`);
|
|
7930
8197
|
try {
|
|
7931
|
-
const planContent = await
|
|
8198
|
+
const planContent = await readFile8(planFilePath, "utf-8");
|
|
7932
8199
|
await updateTodo(db, todo.id, { description: planContent.trim() });
|
|
7933
8200
|
await unlink(planFilePath);
|
|
7934
8201
|
} catch {
|
|
@@ -8094,7 +8361,7 @@ init_database();
|
|
|
8094
8361
|
|
|
8095
8362
|
// src/features/conversations/conversations.database.ts
|
|
8096
8363
|
import { randomUUID as randomUUID6 } from "crypto";
|
|
8097
|
-
async function insertMessage(db, threadId, conversationId, message, model, attachments, planMode, imageMode) {
|
|
8364
|
+
async function insertMessage(db, threadId, conversationId, message, model, attachments, planMode, imageMode, ralphMode) {
|
|
8098
8365
|
try {
|
|
8099
8366
|
const messageId = randomUUID6();
|
|
8100
8367
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -8102,9 +8369,9 @@ async function insertMessage(db, threadId, conversationId, message, model, attac
|
|
|
8102
8369
|
`
|
|
8103
8370
|
INSERT INTO conversation_history (
|
|
8104
8371
|
id, threadId, conversationId, timestamp, status,
|
|
8105
|
-
request_message, request_model, request_attachments, request_planMode, request_imageMode
|
|
8372
|
+
request_message, request_model, request_attachments, request_planMode, request_ralphMode, request_imageMode
|
|
8106
8373
|
)
|
|
8107
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
8374
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
8108
8375
|
`,
|
|
8109
8376
|
[
|
|
8110
8377
|
messageId,
|
|
@@ -8116,6 +8383,7 @@ async function insertMessage(db, threadId, conversationId, message, model, attac
|
|
|
8116
8383
|
model,
|
|
8117
8384
|
attachments ? JSON.stringify(attachments) : null,
|
|
8118
8385
|
planMode ? 1 : 0,
|
|
8386
|
+
ralphMode ? 1 : 0,
|
|
8119
8387
|
imageMode ? 1 : 0
|
|
8120
8388
|
]
|
|
8121
8389
|
);
|
|
@@ -8289,6 +8557,7 @@ function deserializeMessage(row) {
|
|
|
8289
8557
|
model: row.request_model,
|
|
8290
8558
|
...row.request_attachments && { attachments: JSON.parse(row.request_attachments) },
|
|
8291
8559
|
...row.request_planMode === 1 && { planMode: true },
|
|
8560
|
+
...row.request_ralphMode === 1 && { ralphMode: true },
|
|
8292
8561
|
...row.request_imageMode === 1 && { imageMode: true }
|
|
8293
8562
|
},
|
|
8294
8563
|
response: row.response_content != null ? {
|
|
@@ -8368,7 +8637,7 @@ var ConversationManagerImpl = class {
|
|
|
8368
8637
|
/**
|
|
8369
8638
|
* Starts a new conversation message
|
|
8370
8639
|
*/
|
|
8371
|
-
async startMessage(threadId, _threadPath, conversationId, message, model, attachments, planMode, imageMode) {
|
|
8640
|
+
async startMessage(threadId, _threadPath, conversationId, message, model, attachments, planMode, imageMode, ralphMode) {
|
|
8372
8641
|
try {
|
|
8373
8642
|
if (!this.db) {
|
|
8374
8643
|
await this.initialize();
|
|
@@ -8381,7 +8650,8 @@ var ConversationManagerImpl = class {
|
|
|
8381
8650
|
model,
|
|
8382
8651
|
attachments,
|
|
8383
8652
|
planMode,
|
|
8384
|
-
imageMode
|
|
8653
|
+
imageMode,
|
|
8654
|
+
ralphMode
|
|
8385
8655
|
);
|
|
8386
8656
|
} catch (error) {
|
|
8387
8657
|
console.error("Failed to start conversation message:", error);
|
|
@@ -10662,12 +10932,12 @@ async function handleCheckRunning(c, projectManager) {
|
|
|
10662
10932
|
}
|
|
10663
10933
|
|
|
10664
10934
|
// src/core/run-command-detector.ts
|
|
10665
|
-
import { readFile as
|
|
10666
|
-
import { join as
|
|
10935
|
+
import { readFile as readFile9 } from "fs/promises";
|
|
10936
|
+
import { join as join14 } from "path";
|
|
10667
10937
|
async function detectPackageManager(projectPath) {
|
|
10668
10938
|
try {
|
|
10669
|
-
const packageJsonPath =
|
|
10670
|
-
const content = await
|
|
10939
|
+
const packageJsonPath = join14(projectPath, "package.json");
|
|
10940
|
+
const content = await readFile9(packageJsonPath, "utf-8");
|
|
10671
10941
|
const packageJson = JSON.parse(content);
|
|
10672
10942
|
if (packageJson.packageManager) {
|
|
10673
10943
|
const pm = packageJson.packageManager.split("@")[0];
|
|
@@ -10682,8 +10952,8 @@ async function detectPackageManager(projectPath) {
|
|
|
10682
10952
|
}
|
|
10683
10953
|
async function detectRunScripts(projectPath) {
|
|
10684
10954
|
try {
|
|
10685
|
-
const packageJsonPath =
|
|
10686
|
-
const content = await
|
|
10955
|
+
const packageJsonPath = join14(projectPath, "package.json");
|
|
10956
|
+
const content = await readFile9(packageJsonPath, "utf-8");
|
|
10687
10957
|
const packageJson = JSON.parse(content);
|
|
10688
10958
|
const scripts = packageJson.scripts ?? {};
|
|
10689
10959
|
return {
|
|
@@ -10728,8 +10998,8 @@ async function suggestRunCommand(projectPath) {
|
|
|
10728
10998
|
}
|
|
10729
10999
|
|
|
10730
11000
|
// src/features/projects/projects-package-scripts.route.ts
|
|
10731
|
-
import { readFile as
|
|
10732
|
-
import { join as
|
|
11001
|
+
import { readFile as readFile10 } from "fs/promises";
|
|
11002
|
+
import { join as join15 } from "path";
|
|
10733
11003
|
import { glob } from "glob";
|
|
10734
11004
|
function formatScriptName(scriptName) {
|
|
10735
11005
|
return scriptName.replace(/[-:_]/g, " ").replace(/\b\w/g, (l) => l.toUpperCase());
|
|
@@ -10757,8 +11027,8 @@ async function findPackageScripts(projectPath) {
|
|
|
10757
11027
|
const packageManager = await detectPackageManager(projectPath);
|
|
10758
11028
|
for (const filePath of packageJsonFiles) {
|
|
10759
11029
|
try {
|
|
10760
|
-
const fullPath =
|
|
10761
|
-
const content = await
|
|
11030
|
+
const fullPath = join15(projectPath, filePath);
|
|
11031
|
+
const content = await readFile10(fullPath, "utf-8");
|
|
10762
11032
|
const packageJson = JSON.parse(content);
|
|
10763
11033
|
if (packageJson.scripts) {
|
|
10764
11034
|
for (const [scriptName] of Object.entries(packageJson.scripts)) {
|
|
@@ -10805,8 +11075,8 @@ async function handleGetPackageScripts(c, projectManager) {
|
|
|
10805
11075
|
}
|
|
10806
11076
|
|
|
10807
11077
|
// src/features/projects/projects-ai-files.route.ts
|
|
10808
|
-
import { readdir as readdir6, readFile as
|
|
10809
|
-
import { join as
|
|
11078
|
+
import { readdir as readdir6, readFile as readFile11, writeFile as writeFile2, mkdir, access as access2, rm } from "fs/promises";
|
|
11079
|
+
import { join as join16, extname as extname3, basename } from "path";
|
|
10810
11080
|
import { existsSync as existsSync11 } from "fs";
|
|
10811
11081
|
var IGNORED_DIRS = /* @__PURE__ */ new Set([
|
|
10812
11082
|
".git",
|
|
@@ -10825,8 +11095,8 @@ async function buildFullTree(dirPath, relativeDirPath) {
|
|
|
10825
11095
|
try {
|
|
10826
11096
|
const entries = await readdir6(dirPath, { withFileTypes: true });
|
|
10827
11097
|
for (const entry of entries) {
|
|
10828
|
-
const entryRelPath =
|
|
10829
|
-
const entryAbsPath =
|
|
11098
|
+
const entryRelPath = join16(relativeDirPath, entry.name);
|
|
11099
|
+
const entryAbsPath = join16(dirPath, entry.name);
|
|
10830
11100
|
if (entry.isDirectory()) {
|
|
10831
11101
|
const children = await buildFullTree(entryAbsPath, entryRelPath);
|
|
10832
11102
|
nodes.push({
|
|
@@ -10854,8 +11124,8 @@ async function buildMarkdownFilteredTree(dirPath, relativeDirPath) {
|
|
|
10854
11124
|
try {
|
|
10855
11125
|
const entries = await readdir6(dirPath, { withFileTypes: true });
|
|
10856
11126
|
for (const entry of entries) {
|
|
10857
|
-
const entryRelPath =
|
|
10858
|
-
const entryAbsPath =
|
|
11127
|
+
const entryRelPath = join16(relativeDirPath, entry.name);
|
|
11128
|
+
const entryAbsPath = join16(dirPath, entry.name);
|
|
10859
11129
|
if (entry.isDirectory()) {
|
|
10860
11130
|
if (IGNORED_DIRS.has(entry.name)) continue;
|
|
10861
11131
|
const children = await buildMarkdownFilteredTree(entryAbsPath, entryRelPath);
|
|
@@ -10890,8 +11160,8 @@ async function buildAIFileTree(projectPath) {
|
|
|
10890
11160
|
{ key: "agents", label: "Agents" }
|
|
10891
11161
|
];
|
|
10892
11162
|
for (const { key, label } of agentsFolders) {
|
|
10893
|
-
const relPath =
|
|
10894
|
-
const absPath =
|
|
11163
|
+
const relPath = join16(".agents", key);
|
|
11164
|
+
const absPath = join16(projectPath, relPath);
|
|
10895
11165
|
const exists = existsSync11(absPath);
|
|
10896
11166
|
if (exists) {
|
|
10897
11167
|
const children = await buildFullTree(absPath, relPath);
|
|
@@ -10926,7 +11196,7 @@ async function buildAIFileTree(projectPath) {
|
|
|
10926
11196
|
if (entry.name === ".agents" || entry.name === "AGENTS.md" || IGNORED_DIRS.has(entry.name))
|
|
10927
11197
|
continue;
|
|
10928
11198
|
const entryRelPath = entry.name;
|
|
10929
|
-
const entryAbsPath =
|
|
11199
|
+
const entryAbsPath = join16(projectPath, entry.name);
|
|
10930
11200
|
if (entry.isFile() && MARKDOWN_EXTS.has(extname3(entry.name))) {
|
|
10931
11201
|
nodes.push({
|
|
10932
11202
|
id: entryRelPath,
|
|
@@ -10953,7 +11223,7 @@ async function buildAIFileTree(projectPath) {
|
|
|
10953
11223
|
}
|
|
10954
11224
|
function validateFilePath(projectPath, filePath) {
|
|
10955
11225
|
if (!filePath) return null;
|
|
10956
|
-
const abs =
|
|
11226
|
+
const abs = join16(projectPath, filePath);
|
|
10957
11227
|
if (!abs.startsWith(projectPath + "/") && abs !== projectPath) return null;
|
|
10958
11228
|
return abs;
|
|
10959
11229
|
}
|
|
@@ -11040,7 +11310,7 @@ Links to important documentation, tools, or references.
|
|
|
11040
11310
|
} catch {
|
|
11041
11311
|
return c.json({ error: { code: "NOT_FOUND", message: `File not found: ${filePath}` } }, 404);
|
|
11042
11312
|
}
|
|
11043
|
-
const content = await
|
|
11313
|
+
const content = await readFile11(absPath, "utf-8");
|
|
11044
11314
|
return c.json({ content, path: filePath });
|
|
11045
11315
|
} catch (error) {
|
|
11046
11316
|
return errorResponse(
|
|
@@ -11079,9 +11349,9 @@ async function handleSaveAIFile(c, projectManager) {
|
|
|
11079
11349
|
if (!absPath) {
|
|
11080
11350
|
return c.json({ error: { code: "BAD_REQUEST", message: "Invalid file path" } }, 400);
|
|
11081
11351
|
}
|
|
11082
|
-
const parentDir =
|
|
11352
|
+
const parentDir = join16(absPath, "..");
|
|
11083
11353
|
await mkdir(parentDir, { recursive: true });
|
|
11084
|
-
await
|
|
11354
|
+
await writeFile2(absPath, content, "utf-8");
|
|
11085
11355
|
return c.json({ success: true, path: filePath });
|
|
11086
11356
|
} catch (error) {
|
|
11087
11357
|
return errorResponse(
|
|
@@ -11179,10 +11449,10 @@ async function handleCreateSkill(c, projectManager) {
|
|
|
11179
11449
|
if (!project) {
|
|
11180
11450
|
return errorResponse(c, ErrorCodes.PROJECT_NOT_FOUND, `Project not found: ${projectId}`, 404);
|
|
11181
11451
|
}
|
|
11182
|
-
const skillRelPath =
|
|
11183
|
-
const skillAbsPath =
|
|
11184
|
-
const skillFileRelPath =
|
|
11185
|
-
const skillFileAbsPath =
|
|
11452
|
+
const skillRelPath = join16(".agents", "skills", name);
|
|
11453
|
+
const skillAbsPath = join16(project.path, skillRelPath);
|
|
11454
|
+
const skillFileRelPath = join16(skillRelPath, "SKILL.md");
|
|
11455
|
+
const skillFileAbsPath = join16(skillAbsPath, "SKILL.md");
|
|
11186
11456
|
if (existsSync11(skillAbsPath)) {
|
|
11187
11457
|
return c.json(
|
|
11188
11458
|
{ error: { code: "CONFLICT", message: `Skill '${name}' already exists` } },
|
|
@@ -11197,7 +11467,7 @@ description: ${description}
|
|
|
11197
11467
|
|
|
11198
11468
|
Place your skill instructions here as markdown. This will be used when Tarsk is prompted with a request related to the description of this skill.
|
|
11199
11469
|
`;
|
|
11200
|
-
await
|
|
11470
|
+
await writeFile2(skillFileAbsPath, skillContent, "utf-8");
|
|
11201
11471
|
return c.json({
|
|
11202
11472
|
success: true,
|
|
11203
11473
|
path: skillFileRelPath,
|
|
@@ -11284,7 +11554,7 @@ function createProjectRoutes(projectManager, threadManager) {
|
|
|
11284
11554
|
|
|
11285
11555
|
// src/features/projects/projects.manager.ts
|
|
11286
11556
|
init_utils();
|
|
11287
|
-
import { join as
|
|
11557
|
+
import { join as join19 } from "path";
|
|
11288
11558
|
import { realpathSync as realpathSync2 } from "fs";
|
|
11289
11559
|
import { rm as rm3 } from "fs/promises";
|
|
11290
11560
|
|
|
@@ -11505,14 +11775,14 @@ var ProcessManager = class extends EventEmitter {
|
|
|
11505
11775
|
// src/features/projects/projects.creator.ts
|
|
11506
11776
|
init_utils();
|
|
11507
11777
|
import { randomUUID as randomUUID7 } from "crypto";
|
|
11508
|
-
import { basename as basename2, join as
|
|
11778
|
+
import { basename as basename2, join as join18, relative as relative5 } from "path";
|
|
11509
11779
|
import { access as access3, stat as stat3, readdir as readdir8 } from "fs/promises";
|
|
11510
11780
|
|
|
11511
11781
|
// src/features/scaffold/scaffold.runner.ts
|
|
11512
11782
|
init_utils();
|
|
11513
11783
|
import { existsSync as existsSync12 } from "fs";
|
|
11514
|
-
import { join as
|
|
11515
|
-
import { mkdir as mkdir2, readdir as readdir7, stat as stat2, rename, rm as rm2, writeFile as
|
|
11784
|
+
import { join as join17 } from "path";
|
|
11785
|
+
import { mkdir as mkdir2, readdir as readdir7, stat as stat2, rename, rm as rm2, writeFile as writeFile3 } from "fs/promises";
|
|
11516
11786
|
import { createInterface as createInterface2 } from "readline";
|
|
11517
11787
|
|
|
11518
11788
|
// src/scaffold-templates.json
|
|
@@ -12233,9 +12503,9 @@ function loadCatalog() {
|
|
|
12233
12503
|
}
|
|
12234
12504
|
async function writeAgentsMd(projectPath, agentsMd) {
|
|
12235
12505
|
if (!agentsMd) return;
|
|
12236
|
-
const agentsPath =
|
|
12506
|
+
const agentsPath = join17(projectPath, "AGENTS.md");
|
|
12237
12507
|
if (existsSync12(agentsPath)) return;
|
|
12238
|
-
await
|
|
12508
|
+
await writeFile3(agentsPath, agentsMd, "utf-8");
|
|
12239
12509
|
}
|
|
12240
12510
|
function getProjectName(name) {
|
|
12241
12511
|
return name.toLowerCase().replace(/\s/g, "-").replace(/[^a-z0-9-]/g, "").replace(/-(\d)/g, "-a$1");
|
|
@@ -12391,7 +12661,7 @@ async function* runCommandStreaming(cwd, command) {
|
|
|
12391
12661
|
}
|
|
12392
12662
|
}
|
|
12393
12663
|
async function* createScaffoldedProjectStreaming(options) {
|
|
12394
|
-
const projectPath =
|
|
12664
|
+
const projectPath = join17(options.parentDir, options.threadId);
|
|
12395
12665
|
if (!existsSync12(options.parentDir)) {
|
|
12396
12666
|
yield {
|
|
12397
12667
|
type: "result",
|
|
@@ -12481,7 +12751,7 @@ async function* createScaffoldedProjectStreaming(options) {
|
|
|
12481
12751
|
}
|
|
12482
12752
|
try {
|
|
12483
12753
|
const projectName2 = getProjectName(options.projectName);
|
|
12484
|
-
const projectSubDir =
|
|
12754
|
+
const projectSubDir = join17(projectPath, projectName2);
|
|
12485
12755
|
try {
|
|
12486
12756
|
const subDirStat = await stat2(projectSubDir);
|
|
12487
12757
|
if (subDirStat.isDirectory()) {
|
|
@@ -12491,8 +12761,8 @@ async function* createScaffoldedProjectStreaming(options) {
|
|
|
12491
12761
|
};
|
|
12492
12762
|
const items = await readdir7(projectSubDir);
|
|
12493
12763
|
for (const item of items) {
|
|
12494
|
-
const oldPath =
|
|
12495
|
-
const newPath =
|
|
12764
|
+
const oldPath = join17(projectSubDir, item);
|
|
12765
|
+
const newPath = join17(projectPath, item);
|
|
12496
12766
|
await rename(oldPath, newPath);
|
|
12497
12767
|
}
|
|
12498
12768
|
await rm2(projectSubDir, { recursive: true, force: true });
|
|
@@ -12960,7 +13230,7 @@ var ProjectCreator = class {
|
|
|
12960
13230
|
return name;
|
|
12961
13231
|
}
|
|
12962
13232
|
generateThreadPath(_projectId, threadId) {
|
|
12963
|
-
return
|
|
13233
|
+
return join18(this.rootFolder, threadId);
|
|
12964
13234
|
}
|
|
12965
13235
|
async *createProjectFromFolder() {
|
|
12966
13236
|
await loadUtils();
|
|
@@ -13110,19 +13380,19 @@ var ProjectCreator = class {
|
|
|
13110
13380
|
}
|
|
13111
13381
|
async findAllPackageJsonFiles(rootPath, currentPath, setupScripts) {
|
|
13112
13382
|
try {
|
|
13113
|
-
await access3(
|
|
13383
|
+
await access3(join18(currentPath, "package.json"));
|
|
13114
13384
|
const relativePath = relative5(rootPath, currentPath);
|
|
13115
13385
|
let installCommand = "";
|
|
13116
13386
|
try {
|
|
13117
|
-
await access3(
|
|
13387
|
+
await access3(join18(currentPath, "yarn.lock"));
|
|
13118
13388
|
installCommand = "yarn install";
|
|
13119
13389
|
} catch {
|
|
13120
13390
|
try {
|
|
13121
|
-
await access3(
|
|
13391
|
+
await access3(join18(currentPath, "bun.lock"));
|
|
13122
13392
|
installCommand = "bun install";
|
|
13123
13393
|
} catch {
|
|
13124
13394
|
try {
|
|
13125
|
-
await access3(
|
|
13395
|
+
await access3(join18(currentPath, "pnpm-lock.yaml"));
|
|
13126
13396
|
installCommand = "pnpm install";
|
|
13127
13397
|
} catch {
|
|
13128
13398
|
installCommand = "npm install";
|
|
@@ -13139,7 +13409,7 @@ var ProjectCreator = class {
|
|
|
13139
13409
|
try {
|
|
13140
13410
|
const entries = await readdir8(currentPath);
|
|
13141
13411
|
for (const entry of entries) {
|
|
13142
|
-
const entryPath =
|
|
13412
|
+
const entryPath = join18(currentPath, entry);
|
|
13143
13413
|
try {
|
|
13144
13414
|
const stats = await stat3(entryPath);
|
|
13145
13415
|
if (stats.isDirectory() && !entry.startsWith(".") && entry !== "node_modules") {
|
|
@@ -13476,7 +13746,7 @@ var ProjectManagerImpl = class {
|
|
|
13476
13746
|
const session = this.terminalSessionManager.getOrCreateSession(threadId, thread.path);
|
|
13477
13747
|
let workingDir;
|
|
13478
13748
|
if (cwd) {
|
|
13479
|
-
workingDir = cwd.startsWith("/") ? cwd :
|
|
13749
|
+
workingDir = cwd.startsWith("/") ? cwd : join19(thread.path, cwd);
|
|
13480
13750
|
this.terminalSessionManager.updateWorkingDirectory(threadId, workingDir);
|
|
13481
13751
|
} else {
|
|
13482
13752
|
workingDir = session.currentWorkingDirectory;
|
|
@@ -13681,9 +13951,9 @@ import { Hono as Hono7 } from "hono";
|
|
|
13681
13951
|
|
|
13682
13952
|
// src/core/env-manager.ts
|
|
13683
13953
|
import { promises as fs } from "fs";
|
|
13684
|
-
import { join as
|
|
13954
|
+
import { join as join20 } from "path";
|
|
13685
13955
|
async function updateEnvFile(keyNames) {
|
|
13686
|
-
const envPath =
|
|
13956
|
+
const envPath = join20(process.cwd(), ".env");
|
|
13687
13957
|
let content = "";
|
|
13688
13958
|
try {
|
|
13689
13959
|
content = await fs.readFile(envPath, "utf-8");
|
|
@@ -13712,7 +13982,7 @@ async function updateEnvFile(keyNames) {
|
|
|
13712
13982
|
await fs.writeFile(envPath, newLines.join("\n") + "\n", "utf-8");
|
|
13713
13983
|
}
|
|
13714
13984
|
async function readEnvFile() {
|
|
13715
|
-
const envPath =
|
|
13985
|
+
const envPath = join20(process.cwd(), ".env");
|
|
13716
13986
|
const envMap = {};
|
|
13717
13987
|
try {
|
|
13718
13988
|
const content = await fs.readFile(envPath, "utf-8");
|
|
@@ -14182,8 +14452,8 @@ function createScaffoldRoutes(projectManager) {
|
|
|
14182
14452
|
}
|
|
14183
14453
|
|
|
14184
14454
|
// src/features/slash-commands/slash-commands.manager.ts
|
|
14185
|
-
import { readdir as readdir9, readFile as
|
|
14186
|
-
import { join as
|
|
14455
|
+
import { readdir as readdir9, readFile as readFile12, mkdir as mkdir3, writeFile as writeFile4, unlink as unlink2 } from "fs/promises";
|
|
14456
|
+
import { join as join21, basename as basename3, extname as extname4 } from "path";
|
|
14187
14457
|
import { existsSync as existsSync13 } from "fs";
|
|
14188
14458
|
import { homedir as homedir7 } from "os";
|
|
14189
14459
|
function slugify(filename) {
|
|
@@ -14229,16 +14499,32 @@ function parseFrontmatter3(markdown) {
|
|
|
14229
14499
|
return { metadata, content };
|
|
14230
14500
|
}
|
|
14231
14501
|
function getGlobalCommandsDir() {
|
|
14232
|
-
return
|
|
14502
|
+
return join21(homedir7(), ".agents", "commands");
|
|
14233
14503
|
}
|
|
14234
14504
|
function getProjectCommandsDir(threadPath) {
|
|
14235
|
-
return
|
|
14505
|
+
return join21(threadPath, ".agents", "commands");
|
|
14236
14506
|
}
|
|
14237
14507
|
var SlashCommandManager = class _SlashCommandManager {
|
|
14238
14508
|
/**
|
|
14239
14509
|
* Built-in commands that cannot be overridden
|
|
14240
14510
|
*/
|
|
14241
|
-
static BUILT_IN_COMMANDS = /* @__PURE__ */ new Set(["init"]);
|
|
14511
|
+
static BUILT_IN_COMMANDS = /* @__PURE__ */ new Set(["init", "ralph"]);
|
|
14512
|
+
/**
|
|
14513
|
+
* Built-in command definitions injected programmatically
|
|
14514
|
+
*/
|
|
14515
|
+
static BUILT_IN_COMMAND_DEFS = [
|
|
14516
|
+
{
|
|
14517
|
+
name: "ralph",
|
|
14518
|
+
content: "Start the Ralph autonomous loop. Add your stories as todo items, then Ralph will implement them one by one with fresh context between iterations. Each story should be small enough to complete in one iteration.",
|
|
14519
|
+
metadata: {
|
|
14520
|
+
description: "Start the Ralph autonomous implementation loop",
|
|
14521
|
+
mode: "ralph"
|
|
14522
|
+
},
|
|
14523
|
+
scope: "global",
|
|
14524
|
+
filePath: "(built-in)",
|
|
14525
|
+
isBuiltIn: true
|
|
14526
|
+
}
|
|
14527
|
+
];
|
|
14242
14528
|
/**
|
|
14243
14529
|
* Load all available commands for a given thread
|
|
14244
14530
|
* Project commands override global ones with the same name
|
|
@@ -14262,6 +14548,11 @@ var SlashCommandManager = class _SlashCommandManager {
|
|
|
14262
14548
|
}
|
|
14263
14549
|
}
|
|
14264
14550
|
}
|
|
14551
|
+
for (const builtIn of _SlashCommandManager.BUILT_IN_COMMAND_DEFS) {
|
|
14552
|
+
if (!commands.has(builtIn.name)) {
|
|
14553
|
+
commands.set(builtIn.name, builtIn);
|
|
14554
|
+
}
|
|
14555
|
+
}
|
|
14265
14556
|
return Array.from(commands.values());
|
|
14266
14557
|
}
|
|
14267
14558
|
/**
|
|
@@ -14273,8 +14564,8 @@ var SlashCommandManager = class _SlashCommandManager {
|
|
|
14273
14564
|
const files = await readdir9(dir);
|
|
14274
14565
|
for (const file of files) {
|
|
14275
14566
|
if (!file.endsWith(".md")) continue;
|
|
14276
|
-
const filePath =
|
|
14277
|
-
const fileContent = await
|
|
14567
|
+
const filePath = join21(dir, file);
|
|
14568
|
+
const fileContent = await readFile12(filePath, "utf-8");
|
|
14278
14569
|
const { metadata, content } = parseFrontmatter3(fileContent);
|
|
14279
14570
|
const nameWithoutExt = basename3(file, extname4(file));
|
|
14280
14571
|
const commandName = slugify(nameWithoutExt);
|
|
@@ -14308,7 +14599,7 @@ var SlashCommandManager = class _SlashCommandManager {
|
|
|
14308
14599
|
await mkdir3(dir, { recursive: true });
|
|
14309
14600
|
}
|
|
14310
14601
|
const filename = `${name}.md`;
|
|
14311
|
-
const filePath =
|
|
14602
|
+
const filePath = join21(dir, filename);
|
|
14312
14603
|
if (existsSync13(filePath)) {
|
|
14313
14604
|
throw new Error(`Command already exists: ${name}`);
|
|
14314
14605
|
}
|
|
@@ -14330,7 +14621,7 @@ var SlashCommandManager = class _SlashCommandManager {
|
|
|
14330
14621
|
markdown += "---\n\n";
|
|
14331
14622
|
}
|
|
14332
14623
|
markdown += content;
|
|
14333
|
-
await
|
|
14624
|
+
await writeFile4(filePath, markdown, "utf-8");
|
|
14334
14625
|
return {
|
|
14335
14626
|
name,
|
|
14336
14627
|
content,
|
|
@@ -14364,7 +14655,7 @@ var SlashCommandManager = class _SlashCommandManager {
|
|
|
14364
14655
|
markdown += "---\n\n";
|
|
14365
14656
|
}
|
|
14366
14657
|
markdown += content;
|
|
14367
|
-
await
|
|
14658
|
+
await writeFile4(filePath, markdown, "utf-8");
|
|
14368
14659
|
}
|
|
14369
14660
|
/**
|
|
14370
14661
|
* Delete a command
|
|
@@ -14903,7 +15194,7 @@ function createRuleRoutes(router, projectManager) {
|
|
|
14903
15194
|
// src/features/threads/threads.manager.ts
|
|
14904
15195
|
init_utils();
|
|
14905
15196
|
import { randomUUID as randomUUID8 } from "crypto";
|
|
14906
|
-
import { join as
|
|
15197
|
+
import { join as join22 } from "path";
|
|
14907
15198
|
import { execSync as execSync3 } from "child_process";
|
|
14908
15199
|
import { rm as rm4, stat as stat4, mkdir as mkdir4 } from "fs/promises";
|
|
14909
15200
|
init_database();
|
|
@@ -15307,7 +15598,7 @@ var ThreadManagerImpl = class {
|
|
|
15307
15598
|
* - 7.4 - THE CLI SHALL ensure each Thread clone is stored in a unique directory path
|
|
15308
15599
|
*/
|
|
15309
15600
|
generateThreadPath(_projectId, threadId) {
|
|
15310
|
-
return
|
|
15601
|
+
return join22(this.rootFolder, threadId);
|
|
15311
15602
|
}
|
|
15312
15603
|
/**
|
|
15313
15604
|
* Generates a thread title if not provided
|
|
@@ -15500,9 +15791,9 @@ async function handleDeleteThread(c, threadManager) {
|
|
|
15500
15791
|
}
|
|
15501
15792
|
|
|
15502
15793
|
// src/core/project-inspector.ts
|
|
15503
|
-
import { readFile as
|
|
15794
|
+
import { readFile as readFile13, readdir as readdir10 } from "fs/promises";
|
|
15504
15795
|
import { existsSync as existsSync14 } from "fs";
|
|
15505
|
-
import { join as
|
|
15796
|
+
import { join as join23, basename as basename4, relative as relative6 } from "path";
|
|
15506
15797
|
import { glob as glob2 } from "glob";
|
|
15507
15798
|
|
|
15508
15799
|
// src/features/project-scripts/project-scripts.database.ts
|
|
@@ -15546,13 +15837,13 @@ async function inspectProject(projectPath, projectId) {
|
|
|
15546
15837
|
return scripts;
|
|
15547
15838
|
}
|
|
15548
15839
|
async function detectPackageManager2(projectPath) {
|
|
15549
|
-
if (existsSync14(
|
|
15840
|
+
if (existsSync14(join23(projectPath, "bun.lockb")) || existsSync14(join23(projectPath, "bun.lock"))) {
|
|
15550
15841
|
return "bun";
|
|
15551
15842
|
}
|
|
15552
|
-
if (existsSync14(
|
|
15843
|
+
if (existsSync14(join23(projectPath, "pnpm-lock.yaml"))) {
|
|
15553
15844
|
return "pnpm";
|
|
15554
15845
|
}
|
|
15555
|
-
if (existsSync14(
|
|
15846
|
+
if (existsSync14(join23(projectPath, "yarn.lock"))) {
|
|
15556
15847
|
return "yarn";
|
|
15557
15848
|
}
|
|
15558
15849
|
try {
|
|
@@ -15571,8 +15862,8 @@ async function detectPackageManager2(projectPath) {
|
|
|
15571
15862
|
return "npm";
|
|
15572
15863
|
}
|
|
15573
15864
|
async function detectMonoRepoType(projectPath) {
|
|
15574
|
-
const hasPnpmWorkspace = existsSync14(
|
|
15575
|
-
if (existsSync14(
|
|
15865
|
+
const hasPnpmWorkspace = existsSync14(join23(projectPath, "pnpm-workspace.yaml"));
|
|
15866
|
+
if (existsSync14(join23(projectPath, "nx.json"))) {
|
|
15576
15867
|
return "nx";
|
|
15577
15868
|
}
|
|
15578
15869
|
const pkg = await readPackageJson(projectPath);
|
|
@@ -15586,10 +15877,10 @@ async function detectMonoRepoType(projectPath) {
|
|
|
15586
15877
|
if (hasPnpmWorkspace) {
|
|
15587
15878
|
return "pnpm";
|
|
15588
15879
|
}
|
|
15589
|
-
if (existsSync14(
|
|
15880
|
+
if (existsSync14(join23(projectPath, "lerna.json"))) {
|
|
15590
15881
|
return "lerna";
|
|
15591
15882
|
}
|
|
15592
|
-
if (existsSync14(
|
|
15883
|
+
if (existsSync14(join23(projectPath, "turbo.json"))) {
|
|
15593
15884
|
return "turbo";
|
|
15594
15885
|
}
|
|
15595
15886
|
const folderWorkspaces = await detectFolderBasedWorkspaces(projectPath);
|
|
@@ -15630,8 +15921,8 @@ async function resolvePackageJsonWorkspaces(projectPath) {
|
|
|
15630
15921
|
});
|
|
15631
15922
|
const workspaces = [];
|
|
15632
15923
|
for (const folder of folders) {
|
|
15633
|
-
const absPath =
|
|
15634
|
-
if (existsSync14(
|
|
15924
|
+
const absPath = join23(projectPath, folder);
|
|
15925
|
+
if (existsSync14(join23(absPath, "package.json"))) {
|
|
15635
15926
|
workspaces.push({
|
|
15636
15927
|
name: basename4(folder),
|
|
15637
15928
|
folder: absPath,
|
|
@@ -15646,12 +15937,12 @@ async function resolvePackageJsonWorkspaces(projectPath) {
|
|
|
15646
15937
|
return workspaces;
|
|
15647
15938
|
}
|
|
15648
15939
|
async function resolvePnpmWorkspaces(projectPath) {
|
|
15649
|
-
const yamlPath =
|
|
15940
|
+
const yamlPath = join23(projectPath, "pnpm-workspace.yaml");
|
|
15650
15941
|
if (!existsSync14(yamlPath)) {
|
|
15651
15942
|
return [{ name: "root", folder: projectPath, relativePath: "." }];
|
|
15652
15943
|
}
|
|
15653
15944
|
try {
|
|
15654
|
-
const yaml = await
|
|
15945
|
+
const yaml = await readFile13(yamlPath, "utf-8");
|
|
15655
15946
|
const patterns = [];
|
|
15656
15947
|
for (const line of yaml.split("\n")) {
|
|
15657
15948
|
const trimmed = line.trim();
|
|
@@ -15672,8 +15963,8 @@ async function resolvePnpmWorkspaces(projectPath) {
|
|
|
15672
15963
|
});
|
|
15673
15964
|
const workspaces = [];
|
|
15674
15965
|
for (const folder of folders) {
|
|
15675
|
-
const absPath =
|
|
15676
|
-
if (existsSync14(
|
|
15966
|
+
const absPath = join23(projectPath, folder);
|
|
15967
|
+
if (existsSync14(join23(absPath, "package.json"))) {
|
|
15677
15968
|
const wsPkg = await readPackageJson(absPath);
|
|
15678
15969
|
workspaces.push({
|
|
15679
15970
|
name: wsPkg?.name ?? basename4(folder),
|
|
@@ -15692,12 +15983,12 @@ async function resolvePnpmWorkspaces(projectPath) {
|
|
|
15692
15983
|
}
|
|
15693
15984
|
}
|
|
15694
15985
|
async function resolveLernaWorkspaces(projectPath) {
|
|
15695
|
-
const lernaPath =
|
|
15986
|
+
const lernaPath = join23(projectPath, "lerna.json");
|
|
15696
15987
|
if (!existsSync14(lernaPath)) {
|
|
15697
15988
|
return [{ name: "root", folder: projectPath, relativePath: "." }];
|
|
15698
15989
|
}
|
|
15699
15990
|
try {
|
|
15700
|
-
const content = await
|
|
15991
|
+
const content = await readFile13(lernaPath, "utf-8");
|
|
15701
15992
|
const lerna = JSON.parse(content);
|
|
15702
15993
|
const patterns = lerna.packages ?? ["packages/*"];
|
|
15703
15994
|
const folders = await glob2(patterns, {
|
|
@@ -15706,8 +15997,8 @@ async function resolveLernaWorkspaces(projectPath) {
|
|
|
15706
15997
|
});
|
|
15707
15998
|
const workspaces = [];
|
|
15708
15999
|
for (const folder of folders) {
|
|
15709
|
-
const absPath =
|
|
15710
|
-
if (existsSync14(
|
|
16000
|
+
const absPath = join23(projectPath, folder);
|
|
16001
|
+
if (existsSync14(join23(absPath, "package.json"))) {
|
|
15711
16002
|
const wsPkg = await readPackageJson(absPath);
|
|
15712
16003
|
workspaces.push({
|
|
15713
16004
|
name: wsPkg?.name ?? basename4(folder),
|
|
@@ -15727,17 +16018,17 @@ async function resolveLernaWorkspaces(projectPath) {
|
|
|
15727
16018
|
}
|
|
15728
16019
|
async function resolveNxWorkspaces(projectPath) {
|
|
15729
16020
|
const workspaces = [];
|
|
15730
|
-
const workspaceJsonPath =
|
|
16021
|
+
const workspaceJsonPath = join23(projectPath, "workspace.json");
|
|
15731
16022
|
if (existsSync14(workspaceJsonPath)) {
|
|
15732
16023
|
try {
|
|
15733
|
-
const content = await
|
|
16024
|
+
const content = await readFile13(workspaceJsonPath, "utf-8");
|
|
15734
16025
|
const wsJson = JSON.parse(content);
|
|
15735
16026
|
for (const [name, value] of Object.entries(wsJson.projects ?? {})) {
|
|
15736
16027
|
const folder = typeof value === "string" ? value : value.root;
|
|
15737
16028
|
if (folder) {
|
|
15738
16029
|
workspaces.push({
|
|
15739
16030
|
name,
|
|
15740
|
-
folder:
|
|
16031
|
+
folder: join23(projectPath, folder),
|
|
15741
16032
|
relativePath: folder
|
|
15742
16033
|
});
|
|
15743
16034
|
}
|
|
@@ -15752,13 +16043,13 @@ async function resolveNxWorkspaces(projectPath) {
|
|
|
15752
16043
|
});
|
|
15753
16044
|
for (const file of projectJsonFiles) {
|
|
15754
16045
|
try {
|
|
15755
|
-
const content = await
|
|
16046
|
+
const content = await readFile13(join23(projectPath, file), "utf-8");
|
|
15756
16047
|
const project = JSON.parse(content);
|
|
15757
16048
|
if (project.name) {
|
|
15758
16049
|
const folder = file.replace(/\/project\.json$/, "");
|
|
15759
16050
|
workspaces.push({
|
|
15760
16051
|
name: project.name,
|
|
15761
|
-
folder:
|
|
16052
|
+
folder: join23(projectPath, folder),
|
|
15762
16053
|
relativePath: folder
|
|
15763
16054
|
});
|
|
15764
16055
|
}
|
|
@@ -15766,15 +16057,15 @@ async function resolveNxWorkspaces(projectPath) {
|
|
|
15766
16057
|
}
|
|
15767
16058
|
}
|
|
15768
16059
|
if (workspaces.length > 0) return workspaces;
|
|
15769
|
-
const appsDir =
|
|
16060
|
+
const appsDir = join23(projectPath, "apps");
|
|
15770
16061
|
if (existsSync14(appsDir)) {
|
|
15771
16062
|
const entries = await readdir10(appsDir, { withFileTypes: true });
|
|
15772
16063
|
for (const entry of entries) {
|
|
15773
16064
|
if (entry.isDirectory() && !entry.name.startsWith(".")) {
|
|
15774
|
-
const folder =
|
|
16065
|
+
const folder = join23("apps", entry.name);
|
|
15775
16066
|
workspaces.push({
|
|
15776
16067
|
name: entry.name,
|
|
15777
|
-
folder:
|
|
16068
|
+
folder: join23(projectPath, folder),
|
|
15778
16069
|
relativePath: folder
|
|
15779
16070
|
});
|
|
15780
16071
|
}
|
|
@@ -15808,13 +16099,13 @@ async function detectFolderBasedWorkspaces(projectPath) {
|
|
|
15808
16099
|
const results = [];
|
|
15809
16100
|
for (const entry of entries) {
|
|
15810
16101
|
if (entry.isDirectory() && !entry.name.startsWith(".") && entry.name !== "node_modules" && entry.name !== "dist" && entry.name !== "build") {
|
|
15811
|
-
const pkgPath =
|
|
16102
|
+
const pkgPath = join23(projectPath, entry.name, "package.json");
|
|
15812
16103
|
if (existsSync14(pkgPath)) {
|
|
15813
16104
|
try {
|
|
15814
|
-
const content = await
|
|
16105
|
+
const content = await readFile13(pkgPath, "utf-8");
|
|
15815
16106
|
const pkg = JSON.parse(content);
|
|
15816
16107
|
if (pkg.dependencies || pkg.devDependencies || pkg.scripts) {
|
|
15817
|
-
results.push({ name: entry.name, absPath:
|
|
16108
|
+
results.push({ name: entry.name, absPath: join23(projectPath, entry.name) });
|
|
15818
16109
|
}
|
|
15819
16110
|
} catch {
|
|
15820
16111
|
}
|
|
@@ -15870,7 +16161,7 @@ function pmRunCommand(packageManager, scriptName) {
|
|
|
15870
16161
|
}
|
|
15871
16162
|
async function readPackageJson(dir) {
|
|
15872
16163
|
try {
|
|
15873
|
-
const content = await
|
|
16164
|
+
const content = await readFile13(join23(dir, "package.json"), "utf-8");
|
|
15874
16165
|
return JSON.parse(content);
|
|
15875
16166
|
} catch {
|
|
15876
16167
|
return null;
|
|
@@ -16091,8 +16382,8 @@ async function handleListThreadFiles(c, threadManager) {
|
|
|
16091
16382
|
}
|
|
16092
16383
|
|
|
16093
16384
|
// src/features/threads/threads-explorer.route.ts
|
|
16094
|
-
import { readdir as readdir11, readFile as
|
|
16095
|
-
import { join as
|
|
16385
|
+
import { readdir as readdir11, readFile as readFile14, rename as rename2, writeFile as writeFile5, mkdir as mkdir5, rm as rm5, access as access4 } from "fs/promises";
|
|
16386
|
+
import { join as join24 } from "path";
|
|
16096
16387
|
import { spawn as spawn2 } from "child_process";
|
|
16097
16388
|
var Utils3 = null;
|
|
16098
16389
|
var utilsLoaded3 = false;
|
|
@@ -16155,7 +16446,7 @@ async function handleListThreadExplorerDir(c, threadManager) {
|
|
|
16155
16446
|
const entries = dirents.filter((d) => !(isRoot && d.name === ".git")).map((d) => ({
|
|
16156
16447
|
name: d.name,
|
|
16157
16448
|
type: d.isDirectory() ? "folder" : "file",
|
|
16158
|
-
path: queryPath ?
|
|
16449
|
+
path: queryPath ? join24(queryPath, d.name) : d.name
|
|
16159
16450
|
})).sort((a, b) => {
|
|
16160
16451
|
if (a.type !== b.type) return a.type === "folder" ? -1 : 1;
|
|
16161
16452
|
return a.name.localeCompare(b.name);
|
|
@@ -16301,7 +16592,7 @@ async function handleCreateExplorerFile(c, threadManager) {
|
|
|
16301
16592
|
absParent = validated;
|
|
16302
16593
|
}
|
|
16303
16594
|
await mkdir5(absParent, { recursive: true });
|
|
16304
|
-
await
|
|
16595
|
+
await writeFile5(join24(absParent, name), "", "utf-8");
|
|
16305
16596
|
const relPath = dirPath ? `${dirPath}/${name}` : name;
|
|
16306
16597
|
return c.json({ success: true, path: relPath });
|
|
16307
16598
|
} catch (error) {
|
|
@@ -16358,7 +16649,7 @@ async function handleGetExplorerMedia(c, threadManager) {
|
|
|
16358
16649
|
}
|
|
16359
16650
|
const ext = filePath.split(".").pop()?.toLowerCase() ?? "";
|
|
16360
16651
|
const contentType = MEDIA_MIME_TYPES[ext] ?? "application/octet-stream";
|
|
16361
|
-
const data = await
|
|
16652
|
+
const data = await readFile14(absPath);
|
|
16362
16653
|
return new Response(data, { headers: { "Content-Type": contentType } });
|
|
16363
16654
|
} catch (error) {
|
|
16364
16655
|
return errorResponse(
|
|
@@ -16403,7 +16694,7 @@ async function handleCreateExplorerFolder(c, threadManager) {
|
|
|
16403
16694
|
}
|
|
16404
16695
|
absParent = validated;
|
|
16405
16696
|
}
|
|
16406
|
-
await mkdir5(
|
|
16697
|
+
await mkdir5(join24(absParent, name), { recursive: true });
|
|
16407
16698
|
const relPath = dirPath ? `${dirPath}/${name}` : name;
|
|
16408
16699
|
return c.json({ success: true, path: relPath });
|
|
16409
16700
|
} catch (error) {
|
|
@@ -16513,14 +16804,14 @@ async function handleOpenThread(c, threadManager) {
|
|
|
16513
16804
|
}
|
|
16514
16805
|
|
|
16515
16806
|
// src/features/threads/threads-conversation-folder-path.route.ts
|
|
16516
|
-
import { join as
|
|
16807
|
+
import { join as join25 } from "path";
|
|
16517
16808
|
async function handleGetConversationFolderPath(c) {
|
|
16518
16809
|
try {
|
|
16519
16810
|
const threadId = c.req.param("id");
|
|
16520
16811
|
if (!threadId) {
|
|
16521
16812
|
return errorResponse(c, ErrorCodes.INVALID_REQUEST, "Thread ID is required", 400);
|
|
16522
16813
|
}
|
|
16523
|
-
const path6 =
|
|
16814
|
+
const path6 = join25(getDataDir(), threadId);
|
|
16524
16815
|
return successResponse(c, { path: path6 });
|
|
16525
16816
|
} catch (error) {
|
|
16526
16817
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
@@ -16574,13 +16865,13 @@ async function handleFixComments(c, threadManager) {
|
|
|
16574
16865
|
}
|
|
16575
16866
|
|
|
16576
16867
|
// src/features/threads/threads-ai-files.route.ts
|
|
16577
|
-
import { readFile as
|
|
16578
|
-
import { join as
|
|
16868
|
+
import { readFile as readFile15, writeFile as writeFile7, mkdir as mkdir7, access as access5, rm as rm6 } from "fs/promises";
|
|
16869
|
+
import { join as join27 } from "path";
|
|
16579
16870
|
import { existsSync as existsSync15 } from "fs";
|
|
16580
16871
|
|
|
16581
16872
|
// src/features/git/git-download-folder.ts
|
|
16582
|
-
import { mkdir as mkdir6, writeFile as
|
|
16583
|
-
import { join as
|
|
16873
|
+
import { mkdir as mkdir6, writeFile as writeFile6 } from "fs/promises";
|
|
16874
|
+
import { join as join26, dirname as dirname4 } from "path";
|
|
16584
16875
|
async function downloadGithubFolder(repoUrl, srcPath, destPath, options = {}) {
|
|
16585
16876
|
const { token, ref } = options;
|
|
16586
16877
|
const match = repoUrl.replace(/\.git$/, "").match(/github\.com\/([^/]+)\/([^/]+)/);
|
|
@@ -16616,7 +16907,7 @@ async function downloadGithubFolder(repoUrl, srcPath, destPath, options = {}) {
|
|
|
16616
16907
|
await Promise.all(
|
|
16617
16908
|
files.map(async (file) => {
|
|
16618
16909
|
const relativePath = file.path.slice(prefix.length);
|
|
16619
|
-
const localPath =
|
|
16910
|
+
const localPath = join26(destPath, relativePath);
|
|
16620
16911
|
await mkdir6(dirname4(localPath), { recursive: true });
|
|
16621
16912
|
const rawUrl = `https://raw.githubusercontent.com/${owner}/${repo}/${refParam}/${file.path}`;
|
|
16622
16913
|
const fileRes = await fetch(rawUrl, { headers });
|
|
@@ -16625,7 +16916,7 @@ async function downloadGithubFolder(repoUrl, srcPath, destPath, options = {}) {
|
|
|
16625
16916
|
}
|
|
16626
16917
|
let content = Buffer.from(await fileRes.arrayBuffer()).toString("utf8");
|
|
16627
16918
|
content = content.replace(/Claude/gi, "Tarsk");
|
|
16628
|
-
await
|
|
16919
|
+
await writeFile6(localPath, content, "utf8");
|
|
16629
16920
|
console.log(` \u2713 ${relativePath}`);
|
|
16630
16921
|
})
|
|
16631
16922
|
);
|
|
@@ -16719,7 +17010,7 @@ Links to important documentation, tools, or references.
|
|
|
16719
17010
|
} catch {
|
|
16720
17011
|
return c.json({ error: { code: "NOT_FOUND", message: `File not found: ${filePath}` } }, 404);
|
|
16721
17012
|
}
|
|
16722
|
-
const content = await
|
|
17013
|
+
const content = await readFile15(absPath, "utf-8");
|
|
16723
17014
|
return c.json({ content, path: filePath });
|
|
16724
17015
|
} catch (error) {
|
|
16725
17016
|
return errorResponse(
|
|
@@ -16758,9 +17049,9 @@ async function handleSaveThreadAIFile(c, threadManager) {
|
|
|
16758
17049
|
if (!absPath) {
|
|
16759
17050
|
return c.json({ error: { code: "BAD_REQUEST", message: "Invalid file path" } }, 400);
|
|
16760
17051
|
}
|
|
16761
|
-
const parentDir =
|
|
17052
|
+
const parentDir = join27(absPath, "..");
|
|
16762
17053
|
await mkdir7(parentDir, { recursive: true });
|
|
16763
|
-
await
|
|
17054
|
+
await writeFile7(absPath, content, "utf-8");
|
|
16764
17055
|
return c.json({ success: true, path: filePath });
|
|
16765
17056
|
} catch (error) {
|
|
16766
17057
|
return errorResponse(
|
|
@@ -16847,10 +17138,10 @@ async function handleCreateThreadAgent(c, threadManager) {
|
|
|
16847
17138
|
if (!thread) {
|
|
16848
17139
|
return errorResponse(c, ErrorCodes.THREAD_NOT_FOUND, `Thread not found: ${threadId}`, 404);
|
|
16849
17140
|
}
|
|
16850
|
-
const agentRelPath =
|
|
16851
|
-
const agentAbsPath =
|
|
16852
|
-
const agentFileRelPath =
|
|
16853
|
-
const agentFileAbsPath =
|
|
17141
|
+
const agentRelPath = join27(".agents", "agents", name);
|
|
17142
|
+
const agentAbsPath = join27(thread.path, agentRelPath);
|
|
17143
|
+
const agentFileRelPath = join27(agentRelPath, "AGENT.md");
|
|
17144
|
+
const agentFileAbsPath = join27(agentAbsPath, "AGENT.md");
|
|
16854
17145
|
if (existsSync15(agentAbsPath)) {
|
|
16855
17146
|
return c.json(
|
|
16856
17147
|
{ error: { code: "CONFLICT", message: `Agent '${name}' already exists` } },
|
|
@@ -16867,7 +17158,7 @@ description: ${description}${toolsLine}
|
|
|
16867
17158
|
|
|
16868
17159
|
Place your agent system prompt here as markdown. This will be used as the system prompt when this agent is invoked as a subagent.
|
|
16869
17160
|
`;
|
|
16870
|
-
await
|
|
17161
|
+
await writeFile7(agentFileAbsPath, agentContent, "utf-8");
|
|
16871
17162
|
return c.json({
|
|
16872
17163
|
success: true,
|
|
16873
17164
|
path: agentFileRelPath,
|
|
@@ -16917,10 +17208,10 @@ async function handleCreateThreadSkill(c, threadManager) {
|
|
|
16917
17208
|
if (!thread) {
|
|
16918
17209
|
return errorResponse(c, ErrorCodes.THREAD_NOT_FOUND, `Thread not found: ${threadId}`, 404);
|
|
16919
17210
|
}
|
|
16920
|
-
const skillRelPath =
|
|
16921
|
-
const skillAbsPath =
|
|
16922
|
-
const skillFileRelPath =
|
|
16923
|
-
const skillFileAbsPath =
|
|
17211
|
+
const skillRelPath = join27(".agents", "skills", name);
|
|
17212
|
+
const skillAbsPath = join27(thread.path, skillRelPath);
|
|
17213
|
+
const skillFileRelPath = join27(skillRelPath, "SKILL.md");
|
|
17214
|
+
const skillFileAbsPath = join27(skillAbsPath, "SKILL.md");
|
|
16924
17215
|
if (existsSync15(skillAbsPath)) {
|
|
16925
17216
|
return c.json(
|
|
16926
17217
|
{ error: { code: "CONFLICT", message: `Skill '${name}' already exists` } },
|
|
@@ -16935,7 +17226,7 @@ description: ${description}
|
|
|
16935
17226
|
|
|
16936
17227
|
Place your skill instructions here as markdown. This will be used when Tarsk is prompted with a request related to the description of this skill.
|
|
16937
17228
|
`;
|
|
16938
|
-
await
|
|
17229
|
+
await writeFile7(skillFileAbsPath, skillContent, "utf-8");
|
|
16939
17230
|
return c.json({
|
|
16940
17231
|
success: true,
|
|
16941
17232
|
path: skillFileRelPath,
|
|
@@ -17604,7 +17895,7 @@ import { existsSync as existsSync17 } from "fs";
|
|
|
17604
17895
|
// src/features/git/git.utils.ts
|
|
17605
17896
|
init_utils();
|
|
17606
17897
|
import { existsSync as existsSync16, readFileSync as readFileSync5, statSync as statSync4 } from "fs";
|
|
17607
|
-
import { isAbsolute as isAbsolute3, normalize as normalize2, resolve as resolve3, join as
|
|
17898
|
+
import { isAbsolute as isAbsolute3, normalize as normalize2, resolve as resolve3, join as join28 } from "path";
|
|
17608
17899
|
import { completeSimple } from "@mariozechner/pi-ai";
|
|
17609
17900
|
async function resolveModelAndKey(provider, modelId, metadataManager) {
|
|
17610
17901
|
const providerConfig = await resolveProviderConfig(provider);
|
|
@@ -17861,7 +18152,7 @@ async function getUntrackedFilesDiff(gitRoot) {
|
|
|
17861
18152
|
const parts = [];
|
|
17862
18153
|
const maxFileSize = 1e5;
|
|
17863
18154
|
for (const relPath of untrackedPaths) {
|
|
17864
|
-
const fullPath =
|
|
18155
|
+
const fullPath = join28(gitRoot, relPath);
|
|
17865
18156
|
if (!existsSync16(fullPath)) continue;
|
|
17866
18157
|
try {
|
|
17867
18158
|
if (statSync4(fullPath).isDirectory()) continue;
|
|
@@ -20230,8 +20521,8 @@ function createUpdateRoutes(updater) {
|
|
|
20230
20521
|
|
|
20231
20522
|
// src/features/mcp/mcp.routes.ts
|
|
20232
20523
|
import { Hono as Hono13 } from "hono";
|
|
20233
|
-
import { readFile as
|
|
20234
|
-
import { join as
|
|
20524
|
+
import { readFile as readFile16, writeFile as writeFile8, mkdir as mkdir9, access as access6 } from "fs/promises";
|
|
20525
|
+
import { join as join29, dirname as dirname6 } from "path";
|
|
20235
20526
|
|
|
20236
20527
|
// src/features/mcp/mcp.popular.json
|
|
20237
20528
|
var mcp_popular_default = [
|
|
@@ -20342,10 +20633,10 @@ var mcp_popular_default = [
|
|
|
20342
20633
|
var MCP_CONFIG_PATHS2 = [".agents/mcp.json", "mcp.json"];
|
|
20343
20634
|
async function readMCPConfig(projectPath) {
|
|
20344
20635
|
for (const configPath of MCP_CONFIG_PATHS2) {
|
|
20345
|
-
const fullPath =
|
|
20636
|
+
const fullPath = join29(projectPath, configPath);
|
|
20346
20637
|
try {
|
|
20347
20638
|
await access6(fullPath);
|
|
20348
|
-
const content = await
|
|
20639
|
+
const content = await readFile16(fullPath, "utf-8");
|
|
20349
20640
|
const config = JSON.parse(content);
|
|
20350
20641
|
return { config, filePath: fullPath };
|
|
20351
20642
|
} catch {
|
|
@@ -20356,9 +20647,9 @@ async function readMCPConfig(projectPath) {
|
|
|
20356
20647
|
}
|
|
20357
20648
|
async function writeMCPConfig(projectPath, config) {
|
|
20358
20649
|
const existing = await readMCPConfig(projectPath);
|
|
20359
|
-
const targetPath = existing?.filePath ??
|
|
20650
|
+
const targetPath = existing?.filePath ?? join29(projectPath, ".agents/mcp.json");
|
|
20360
20651
|
await mkdir9(dirname6(targetPath), { recursive: true });
|
|
20361
|
-
await
|
|
20652
|
+
await writeFile8(targetPath, JSON.stringify(config, null, 2), "utf-8");
|
|
20362
20653
|
}
|
|
20363
20654
|
function createMCPRoutes() {
|
|
20364
20655
|
const router = new Hono13();
|
|
@@ -21030,7 +21321,7 @@ async function clipboardWriteImage(c) {
|
|
|
21030
21321
|
}
|
|
21031
21322
|
|
|
21032
21323
|
// src/features/image/image-save.route.ts
|
|
21033
|
-
import { join as
|
|
21324
|
+
import { join as join30 } from "path";
|
|
21034
21325
|
var Utils5 = null;
|
|
21035
21326
|
var utilsLoaded5 = false;
|
|
21036
21327
|
async function loadUtils5() {
|
|
@@ -21067,7 +21358,7 @@ async function saveImage(c) {
|
|
|
21067
21358
|
return c.json({ error: "imageUrl or imageData is required" }, 400);
|
|
21068
21359
|
}
|
|
21069
21360
|
const name = filename ?? `generated-image-${Date.now()}.png`;
|
|
21070
|
-
const savePath =
|
|
21361
|
+
const savePath = join30(Utils5.paths.downloads, name);
|
|
21071
21362
|
await Bun.write(savePath, data);
|
|
21072
21363
|
return c.json({ success: true, path: savePath });
|
|
21073
21364
|
} catch (error) {
|