@nomad-e/bluma-cli 0.1.70 → 0.1.72
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/main.js +1062 -569
- package/package.json +1 -1
package/dist/main.js
CHANGED
|
@@ -1737,8 +1737,8 @@ var init_themes = __esm({
|
|
|
1737
1737
|
import React19 from "react";
|
|
1738
1738
|
import { render } from "ink";
|
|
1739
1739
|
import { EventEmitter as EventEmitter4 } from "events";
|
|
1740
|
-
import
|
|
1741
|
-
import
|
|
1740
|
+
import fs39 from "fs";
|
|
1741
|
+
import path44 from "path";
|
|
1742
1742
|
import { fileURLToPath as fileURLToPath6 } from "url";
|
|
1743
1743
|
import { spawn as spawn6 } from "child_process";
|
|
1744
1744
|
import { v4 as uuidv412 } from "uuid";
|
|
@@ -4438,12 +4438,12 @@ function EditToolDiffPanel({
|
|
|
4438
4438
|
maxHeight = EDIT_DIFF_PREVIEW_MAX_LINES,
|
|
4439
4439
|
fallbackSnippet
|
|
4440
4440
|
}) {
|
|
4441
|
-
const
|
|
4441
|
+
const path45 = filePath.trim() || "unknown file";
|
|
4442
4442
|
const diff = diffText?.trim() ?? "";
|
|
4443
4443
|
return /* @__PURE__ */ jsxs5(Box5, { flexDirection: "column", children: [
|
|
4444
4444
|
/* @__PURE__ */ jsx5(Box5, { flexDirection: "row", flexWrap: "wrap", children: /* @__PURE__ */ jsxs5(Text5, { color: isNewFile ? BLUMA_TERMINAL.success : void 0, children: [
|
|
4445
4445
|
isNewFile ? "Created " : "Wrote to ",
|
|
4446
|
-
/* @__PURE__ */ jsx5(Text5, { bold: true, children:
|
|
4446
|
+
/* @__PURE__ */ jsx5(Text5, { bold: true, children: path45 })
|
|
4447
4447
|
] }) }),
|
|
4448
4448
|
description ? /* @__PURE__ */ jsx5(Text5, { dimColor: true, wrap: "wrap", children: description }) : null,
|
|
4449
4449
|
diff.length > 0 ? /* @__PURE__ */ jsx5(Box5, { marginTop: 0, children: /* @__PURE__ */ jsx5(SimpleDiff, { text: diff, maxHeight, frame: true }) }) : fallbackSnippet ? /* @__PURE__ */ jsxs5(Box5, { flexDirection: "column", marginTop: 0, children: [
|
|
@@ -4772,7 +4772,7 @@ var renderFindByName = ({ args }) => {
|
|
|
4772
4772
|
var renderGrepSearch = ({ args }) => {
|
|
4773
4773
|
const parsed = parseArgs(args);
|
|
4774
4774
|
const query = parsed.query || "";
|
|
4775
|
-
const
|
|
4775
|
+
const path45 = parsed.path || ".";
|
|
4776
4776
|
return /* @__PURE__ */ jsxs8(Box8, { flexDirection: "row", flexWrap: "wrap", children: [
|
|
4777
4777
|
/* @__PURE__ */ jsxs8(Text8, { color: BLUMA_TERMINAL.muted, children: [
|
|
4778
4778
|
'"',
|
|
@@ -4781,7 +4781,7 @@ var renderGrepSearch = ({ args }) => {
|
|
|
4781
4781
|
] }),
|
|
4782
4782
|
/* @__PURE__ */ jsxs8(Text8, { color: BLUMA_TERMINAL.m3OnSurface, bold: true, children: [
|
|
4783
4783
|
" ",
|
|
4784
|
-
|
|
4784
|
+
path45
|
|
4785
4785
|
] })
|
|
4786
4786
|
] });
|
|
4787
4787
|
};
|
|
@@ -5207,12 +5207,12 @@ var ConfirmationPrompt = memo4(ConfirmationPromptComponent);
|
|
|
5207
5207
|
|
|
5208
5208
|
// src/app/agent/agent.ts
|
|
5209
5209
|
import * as dotenv from "dotenv";
|
|
5210
|
-
import
|
|
5210
|
+
import path42 from "path";
|
|
5211
5211
|
import os28 from "os";
|
|
5212
5212
|
|
|
5213
5213
|
// src/app/agent/tool_invoker.ts
|
|
5214
|
-
import { promises as
|
|
5215
|
-
import
|
|
5214
|
+
import { promises as fs26 } from "fs";
|
|
5215
|
+
import path28 from "path";
|
|
5216
5216
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
5217
5217
|
|
|
5218
5218
|
// src/app/agent/tools/natives/edit.ts
|
|
@@ -10267,7 +10267,7 @@ async function createProjectZip(projectDir, zipPath) {
|
|
|
10267
10267
|
}
|
|
10268
10268
|
return zipPath;
|
|
10269
10269
|
}
|
|
10270
|
-
async function uploadToSeverino(zipPath, severinoUrl, name, apiKey) {
|
|
10270
|
+
async function uploadToSeverino(zipPath, severinoUrl, name, apiKey, appId) {
|
|
10271
10271
|
const deployUrl = `${severinoUrl.replace(/\/$/, "")}/api/v1/deploy`;
|
|
10272
10272
|
const curlArgs = [
|
|
10273
10273
|
"-X",
|
|
@@ -10279,6 +10279,9 @@ async function uploadToSeverino(zipPath, severinoUrl, name, apiKey) {
|
|
|
10279
10279
|
if (name) {
|
|
10280
10280
|
curlArgs.push("-F", `name=${name}`);
|
|
10281
10281
|
}
|
|
10282
|
+
if (appId) {
|
|
10283
|
+
curlArgs.push("-F", `appId=${appId}`);
|
|
10284
|
+
}
|
|
10282
10285
|
if (apiKey) {
|
|
10283
10286
|
curlArgs.push("-H", `Authorization: Bearer ${apiKey}`);
|
|
10284
10287
|
curlArgs.push("-H", `X-API-Key: ${apiKey}`);
|
|
@@ -10304,24 +10307,94 @@ async function uploadToSeverino(zipPath, severinoUrl, name, apiKey) {
|
|
|
10304
10307
|
};
|
|
10305
10308
|
}
|
|
10306
10309
|
const data = response.data || {};
|
|
10310
|
+
const resolvedAppId = appId || data.appId;
|
|
10311
|
+
const appContext = data.appContext ? {
|
|
10312
|
+
appId: String(data.appContext.appId || resolvedAppId || "").trim(),
|
|
10313
|
+
tenantId: data.appContext.tenantId ?? null,
|
|
10314
|
+
projectId: data.appContext.projectId ?? null,
|
|
10315
|
+
workspaceId: data.appContext.workspaceId ?? null,
|
|
10316
|
+
revisionId: data.appContext.revisionId ?? null,
|
|
10317
|
+
deploymentId: data.appContext.deploymentId ?? null,
|
|
10318
|
+
buildJobId: data.appContext.buildJobId ?? null,
|
|
10319
|
+
manifestPath: data.appContext.manifestPath ?? "factorai.sh.json",
|
|
10320
|
+
manifestFile: data.appContext.manifestFile ?? "factorai.sh.json",
|
|
10321
|
+
appUrl: data.appContext.appUrl ?? `${severinoUrl.replace(/\/$/, "")}/app/${resolvedAppId}`
|
|
10322
|
+
} : {
|
|
10323
|
+
appId: String(resolvedAppId || "").trim(),
|
|
10324
|
+
tenantId: data.tenantId ?? null,
|
|
10325
|
+
projectId: data.projectId ?? null,
|
|
10326
|
+
workspaceId: data.workspaceId ?? null,
|
|
10327
|
+
revisionId: data.revisionId ?? null,
|
|
10328
|
+
deploymentId: data.deploymentId ?? null,
|
|
10329
|
+
buildJobId: data.buildJobId ?? null,
|
|
10330
|
+
manifestPath: "factorai.sh.json",
|
|
10331
|
+
manifestFile: "factorai.sh.json",
|
|
10332
|
+
appUrl: `${severinoUrl.replace(/\/$/, "")}/app/${resolvedAppId}`
|
|
10333
|
+
};
|
|
10307
10334
|
return {
|
|
10308
10335
|
success: true,
|
|
10309
|
-
appId:
|
|
10336
|
+
appId: resolvedAppId,
|
|
10310
10337
|
name: data.name,
|
|
10311
10338
|
status: data.status || "building",
|
|
10312
|
-
url: severinoUrl.replace(/\/$/, "") + `/app/${
|
|
10313
|
-
message: data.message || "Deploy iniciado"
|
|
10339
|
+
url: appContext.appUrl || severinoUrl.replace(/\/$/, "") + `/app/${resolvedAppId}`,
|
|
10340
|
+
message: data.message || "Deploy iniciado",
|
|
10341
|
+
isRedeploy: !!appId,
|
|
10342
|
+
appContext
|
|
10314
10343
|
};
|
|
10315
10344
|
} catch (parseError) {
|
|
10316
10345
|
throw new Error(`Failed to parse response: ${parseError.message}`);
|
|
10317
10346
|
}
|
|
10318
10347
|
}
|
|
10348
|
+
function buildFactorAiManifest(appContext, deployResult, appName) {
|
|
10349
|
+
return {
|
|
10350
|
+
version: 1,
|
|
10351
|
+
manifestFile: "factorai.sh.json",
|
|
10352
|
+
manifestPath: "factorai.sh.json",
|
|
10353
|
+
appContext: {
|
|
10354
|
+
appId: appContext.appId,
|
|
10355
|
+
tenantId: appContext.tenantId ?? null,
|
|
10356
|
+
projectId: appContext.projectId ?? null,
|
|
10357
|
+
workspaceId: appContext.workspaceId ?? null,
|
|
10358
|
+
revisionId: appContext.revisionId ?? null,
|
|
10359
|
+
deploymentId: appContext.deploymentId ?? null,
|
|
10360
|
+
buildJobId: appContext.buildJobId ?? null,
|
|
10361
|
+
manifestPath: "factorai.sh.json",
|
|
10362
|
+
manifestFile: "factorai.sh.json",
|
|
10363
|
+
appUrl: appContext.appUrl ?? deployResult.url ?? null
|
|
10364
|
+
},
|
|
10365
|
+
app: {
|
|
10366
|
+
name: appName,
|
|
10367
|
+
status: deployResult.status || "building",
|
|
10368
|
+
isRedeploy: deployResult.isRedeploy ?? false,
|
|
10369
|
+
url: deployResult.url || null,
|
|
10370
|
+
message: deployResult.message || null
|
|
10371
|
+
},
|
|
10372
|
+
agent: {
|
|
10373
|
+
provider: "bluma",
|
|
10374
|
+
mode: "tool-first",
|
|
10375
|
+
instructions: [
|
|
10376
|
+
"Read factorai.sh.json as the source of truth before editing files.",
|
|
10377
|
+
"Prefer incremental changes and preserve the existing deployment context.",
|
|
10378
|
+
"After edits, use the redeploy tool instead of rebuilding from scratch."
|
|
10379
|
+
]
|
|
10380
|
+
},
|
|
10381
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
10382
|
+
};
|
|
10383
|
+
}
|
|
10384
|
+
async function writeFactorAiManifest(projectDir, appContext, deployResult, appName) {
|
|
10385
|
+
const manifest = buildFactorAiManifest(appContext, deployResult, appName);
|
|
10386
|
+
const manifestPath = path26.join(projectDir, "factorai.sh.json");
|
|
10387
|
+
await fs24.writeFile(manifestPath, `${JSON.stringify(manifest, null, 2)}
|
|
10388
|
+
`, "utf-8");
|
|
10389
|
+
return manifest;
|
|
10390
|
+
}
|
|
10319
10391
|
async function deployApp(args) {
|
|
10320
10392
|
const envSeverinoUrl = process.env.SEVERINO_URL || "http://localhost:3000";
|
|
10321
10393
|
const envApiKey = process.env.SEVERINO_API_KEY || void 0;
|
|
10322
10394
|
const {
|
|
10323
10395
|
projectDir,
|
|
10324
10396
|
name,
|
|
10397
|
+
appId,
|
|
10325
10398
|
severinoUrl = envSeverinoUrl,
|
|
10326
10399
|
apiKey = envApiKey
|
|
10327
10400
|
} = args;
|
|
@@ -10376,7 +10449,7 @@ async function deployApp(args) {
|
|
|
10376
10449
|
}
|
|
10377
10450
|
console.log(`[deploy-app] ZIP size: ${zipSizeMB.toFixed(2)}MB`);
|
|
10378
10451
|
console.log(`[deploy-app] Uploading to ${severinoUrl}...`);
|
|
10379
|
-
const deployResult = await uploadToSeverino(zipPath, severinoUrl, appName, apiKey);
|
|
10452
|
+
const deployResult = await uploadToSeverino(zipPath, severinoUrl, appName, apiKey, appId);
|
|
10380
10453
|
try {
|
|
10381
10454
|
await fs24.unlink(zipPath);
|
|
10382
10455
|
await fs24.rmdir(tempDir);
|
|
@@ -10384,6 +10457,16 @@ async function deployApp(args) {
|
|
|
10384
10457
|
console.warn("[deploy-app] Cleanup warning:", e);
|
|
10385
10458
|
}
|
|
10386
10459
|
if (deployResult.success) {
|
|
10460
|
+
if (deployResult.appContext) {
|
|
10461
|
+
const manifest = await writeFactorAiManifest(
|
|
10462
|
+
resolvedProjectDir,
|
|
10463
|
+
deployResult.appContext,
|
|
10464
|
+
deployResult,
|
|
10465
|
+
appName
|
|
10466
|
+
);
|
|
10467
|
+
deployResult.factoraiManifest = manifest;
|
|
10468
|
+
deployResult.factoraiManifestPath = path26.join(resolvedProjectDir, "factorai.sh.json");
|
|
10469
|
+
}
|
|
10387
10470
|
console.log(`[deploy-app] Deploy iniciado: ${deployResult.appId}`);
|
|
10388
10471
|
}
|
|
10389
10472
|
return deployResult;
|
|
@@ -10396,8 +10479,389 @@ async function deployApp(args) {
|
|
|
10396
10479
|
}
|
|
10397
10480
|
}
|
|
10398
10481
|
|
|
10482
|
+
// src/app/agent/runtime/factorai_context.ts
|
|
10483
|
+
import fs25 from "fs";
|
|
10484
|
+
import path27 from "path";
|
|
10485
|
+
function normalizeContext(raw) {
|
|
10486
|
+
if (!raw || typeof raw.appId !== "string" || !raw.appId.trim()) {
|
|
10487
|
+
return null;
|
|
10488
|
+
}
|
|
10489
|
+
const appId = raw.appId.trim();
|
|
10490
|
+
return {
|
|
10491
|
+
appId,
|
|
10492
|
+
tenantId: typeof raw.tenantId === "string" ? raw.tenantId : null,
|
|
10493
|
+
projectId: typeof raw.projectId === "string" ? raw.projectId : null,
|
|
10494
|
+
workspaceId: typeof raw.workspaceId === "string" ? raw.workspaceId : null,
|
|
10495
|
+
revisionId: typeof raw.revisionId === "string" ? raw.revisionId : null,
|
|
10496
|
+
deploymentId: typeof raw.deploymentId === "string" ? raw.deploymentId : null,
|
|
10497
|
+
buildJobId: typeof raw.buildJobId === "string" ? raw.buildJobId : null,
|
|
10498
|
+
manifestPath: typeof raw.manifestPath === "string" ? raw.manifestPath : null,
|
|
10499
|
+
manifestFile: typeof raw.manifestFile === "string" ? raw.manifestFile : null,
|
|
10500
|
+
appUrl: typeof raw.appUrl === "string" ? raw.appUrl : null
|
|
10501
|
+
};
|
|
10502
|
+
}
|
|
10503
|
+
function readJsonFile(filePath) {
|
|
10504
|
+
try {
|
|
10505
|
+
if (!fs25.existsSync(filePath)) {
|
|
10506
|
+
return null;
|
|
10507
|
+
}
|
|
10508
|
+
const parsed = JSON.parse(fs25.readFileSync(filePath, "utf8"));
|
|
10509
|
+
if (parsed && typeof parsed === "object") {
|
|
10510
|
+
return parsed.appContext && typeof parsed.appContext === "object" ? parsed.appContext : parsed;
|
|
10511
|
+
}
|
|
10512
|
+
} catch {
|
|
10513
|
+
return null;
|
|
10514
|
+
}
|
|
10515
|
+
return null;
|
|
10516
|
+
}
|
|
10517
|
+
function readFactorAiWorkspaceManifest(projectDir = process.cwd()) {
|
|
10518
|
+
const manifestPath = path27.join(projectDir, "factorai.sh.json");
|
|
10519
|
+
try {
|
|
10520
|
+
if (!fs25.existsSync(manifestPath)) {
|
|
10521
|
+
return null;
|
|
10522
|
+
}
|
|
10523
|
+
const parsed = JSON.parse(fs25.readFileSync(manifestPath, "utf8"));
|
|
10524
|
+
return parsed && typeof parsed === "object" ? parsed : null;
|
|
10525
|
+
} catch {
|
|
10526
|
+
return null;
|
|
10527
|
+
}
|
|
10528
|
+
}
|
|
10529
|
+
function readContextFromWorkspace() {
|
|
10530
|
+
const candidate = path27.join(process.cwd(), "factorai.sh.json");
|
|
10531
|
+
const parsed = readJsonFile(candidate);
|
|
10532
|
+
if (!parsed) {
|
|
10533
|
+
return null;
|
|
10534
|
+
}
|
|
10535
|
+
const context = normalizeContext(parsed);
|
|
10536
|
+
return context;
|
|
10537
|
+
}
|
|
10538
|
+
function loadFactorAiAppContext() {
|
|
10539
|
+
return readContextFromWorkspace();
|
|
10540
|
+
}
|
|
10541
|
+
function formatFactorAiAppContextSummary(context) {
|
|
10542
|
+
if (!context) {
|
|
10543
|
+
return "";
|
|
10544
|
+
}
|
|
10545
|
+
const parts = [
|
|
10546
|
+
`appId=${context.appId}`,
|
|
10547
|
+
context.tenantId ? `tenantId=${context.tenantId}` : null,
|
|
10548
|
+
context.projectId ? `projectId=${context.projectId}` : null,
|
|
10549
|
+
context.workspaceId ? `workspaceId=${context.workspaceId}` : null,
|
|
10550
|
+
context.revisionId ? `revisionId=${context.revisionId}` : null,
|
|
10551
|
+
context.deploymentId ? `deploymentId=${context.deploymentId}` : null,
|
|
10552
|
+
context.buildJobId ? `buildJobId=${context.buildJobId}` : null,
|
|
10553
|
+
context.appUrl ? `appUrl=${context.appUrl}` : null
|
|
10554
|
+
].filter(Boolean);
|
|
10555
|
+
return parts.join(" | ");
|
|
10556
|
+
}
|
|
10557
|
+
function buildFactorAiWorkspaceManifest(input) {
|
|
10558
|
+
const projectDir = input.projectDir || process.cwd();
|
|
10559
|
+
const existing = readFactorAiWorkspaceManifest(projectDir) || {};
|
|
10560
|
+
const now2 = (/* @__PURE__ */ new Date()).toISOString();
|
|
10561
|
+
const nextAppContext = {
|
|
10562
|
+
...typeof existing.appContext === "object" && existing.appContext ? existing.appContext : {},
|
|
10563
|
+
appId: input.appContext.appId,
|
|
10564
|
+
tenantId: input.appContext.tenantId ?? null,
|
|
10565
|
+
projectId: input.appContext.projectId ?? null,
|
|
10566
|
+
workspaceId: input.appContext.workspaceId ?? null,
|
|
10567
|
+
revisionId: input.appContext.revisionId ?? null,
|
|
10568
|
+
deploymentId: input.appContext.deploymentId ?? null,
|
|
10569
|
+
buildJobId: input.appContext.buildJobId ?? null,
|
|
10570
|
+
manifestPath: "factorai.sh.json",
|
|
10571
|
+
manifestFile: "factorai.sh.json",
|
|
10572
|
+
appUrl: input.appContext.appUrl ?? null
|
|
10573
|
+
};
|
|
10574
|
+
return {
|
|
10575
|
+
...existing,
|
|
10576
|
+
version: 1,
|
|
10577
|
+
manifestFile: "factorai.sh.json",
|
|
10578
|
+
manifestPath: "factorai.sh.json",
|
|
10579
|
+
appContext: nextAppContext,
|
|
10580
|
+
app: {
|
|
10581
|
+
...typeof existing.app === "object" && existing.app ? existing.app : {},
|
|
10582
|
+
name: input.name ?? (typeof existing.app === "object" && existing.app ? existing.app.name : void 0) ?? null,
|
|
10583
|
+
status: input.status ?? (typeof existing.app === "object" && existing.app ? existing.app.status : void 0) ?? "building",
|
|
10584
|
+
isRedeploy: input.isRedeploy ?? (typeof existing.app === "object" && existing.app ? existing.app.isRedeploy : false),
|
|
10585
|
+
message: input.message ?? (typeof existing.app === "object" && existing.app ? existing.app.message : null),
|
|
10586
|
+
...input.app || {}
|
|
10587
|
+
},
|
|
10588
|
+
agent: {
|
|
10589
|
+
provider: "bluma",
|
|
10590
|
+
mode: "tool-first",
|
|
10591
|
+
instructions: [
|
|
10592
|
+
"Read factorai.sh.json as the source of truth before editing files.",
|
|
10593
|
+
"Prefer incremental changes and preserve the existing deployment context.",
|
|
10594
|
+
"After edits, use the redeploy tool instead of rebuilding from scratch."
|
|
10595
|
+
],
|
|
10596
|
+
...typeof existing.agent === "object" && existing.agent ? existing.agent : {},
|
|
10597
|
+
...input.agent || {}
|
|
10598
|
+
},
|
|
10599
|
+
sandbox: {
|
|
10600
|
+
...typeof existing.sandbox === "object" && existing.sandbox ? existing.sandbox : {},
|
|
10601
|
+
...input.sandbox || {}
|
|
10602
|
+
},
|
|
10603
|
+
source: {
|
|
10604
|
+
root: ".",
|
|
10605
|
+
publicDir: "./public",
|
|
10606
|
+
appDir: ".",
|
|
10607
|
+
...typeof existing.source === "object" && existing.source ? existing.source : {},
|
|
10608
|
+
...input.source || {}
|
|
10609
|
+
},
|
|
10610
|
+
updatedAt: now2,
|
|
10611
|
+
...input.extra || {}
|
|
10612
|
+
};
|
|
10613
|
+
}
|
|
10614
|
+
async function writeFactorAiWorkspaceManifest(input) {
|
|
10615
|
+
const projectDir = input.projectDir || process.cwd();
|
|
10616
|
+
const manifest = buildFactorAiWorkspaceManifest({ ...input, projectDir });
|
|
10617
|
+
const manifestPath = path27.join(projectDir, "factorai.sh.json");
|
|
10618
|
+
fs25.writeFileSync(manifestPath, `${JSON.stringify(manifest, null, 2)}
|
|
10619
|
+
`, "utf8");
|
|
10620
|
+
return { manifestPath, manifest };
|
|
10621
|
+
}
|
|
10622
|
+
|
|
10399
10623
|
// src/app/agent/runtime/native_tool_catalog.ts
|
|
10400
10624
|
init_sandbox_policy();
|
|
10625
|
+
function getFactorAiBaseUrl() {
|
|
10626
|
+
const value = process.env.FACTORAI_BASE_URL || process.env.FACTORAI_URL || "";
|
|
10627
|
+
const trimmed = value.trim();
|
|
10628
|
+
return trimmed || void 0;
|
|
10629
|
+
}
|
|
10630
|
+
function getFactorAiApiKey() {
|
|
10631
|
+
const value = process.env.FACTORAI_API_KEY || process.env.FACTORAI_TOKEN || "";
|
|
10632
|
+
const trimmed = value.trim();
|
|
10633
|
+
return trimmed || void 0;
|
|
10634
|
+
}
|
|
10635
|
+
function isFactorAiSandboxEnabled() {
|
|
10636
|
+
const policy = getSandboxPolicy();
|
|
10637
|
+
return policy.isSandbox && Boolean(getFactorAiBaseUrl());
|
|
10638
|
+
}
|
|
10639
|
+
async function requestFactorAi(pathname, init = {}) {
|
|
10640
|
+
const baseUrl = getFactorAiBaseUrl();
|
|
10641
|
+
if (!baseUrl) {
|
|
10642
|
+
throw new Error("FACTORAI_BASE_URL is not configured.");
|
|
10643
|
+
}
|
|
10644
|
+
const headers = new Headers(init.headers || {});
|
|
10645
|
+
headers.set("Content-Type", "application/json");
|
|
10646
|
+
const apiKey = getFactorAiApiKey();
|
|
10647
|
+
if (apiKey) {
|
|
10648
|
+
headers.set("Authorization", `Bearer ${apiKey}`);
|
|
10649
|
+
}
|
|
10650
|
+
const response = await fetch(new URL(pathname, baseUrl), {
|
|
10651
|
+
...init,
|
|
10652
|
+
headers
|
|
10653
|
+
});
|
|
10654
|
+
const text = await response.text();
|
|
10655
|
+
let payload = null;
|
|
10656
|
+
if (text) {
|
|
10657
|
+
try {
|
|
10658
|
+
payload = JSON.parse(text);
|
|
10659
|
+
} catch {
|
|
10660
|
+
payload = text;
|
|
10661
|
+
}
|
|
10662
|
+
}
|
|
10663
|
+
if (!response.ok) {
|
|
10664
|
+
const message2 = payload?.error?.message || payload?.message || payload?.error || `FactorAI request failed with status ${response.status}`;
|
|
10665
|
+
throw new Error(message2);
|
|
10666
|
+
}
|
|
10667
|
+
return payload;
|
|
10668
|
+
}
|
|
10669
|
+
async function factorAiGetAppStatus(args) {
|
|
10670
|
+
const appId = String(args?.appId || "").trim();
|
|
10671
|
+
if (!appId) {
|
|
10672
|
+
return { error: "appId is required." };
|
|
10673
|
+
}
|
|
10674
|
+
return requestFactorAi(`/api/v1/apps/${encodeURIComponent(appId)}`);
|
|
10675
|
+
}
|
|
10676
|
+
async function factorAiApplyAppChanges(args) {
|
|
10677
|
+
const appId = String(args?.appId || "").trim();
|
|
10678
|
+
if (!appId) {
|
|
10679
|
+
return { error: "appId is required." };
|
|
10680
|
+
}
|
|
10681
|
+
const files = Array.isArray(args?.files) ? args.files : Array.isArray(args?.changes) ? args.changes : [];
|
|
10682
|
+
if (files.length === 0) {
|
|
10683
|
+
return { error: "files or changes must be a non-empty array." };
|
|
10684
|
+
}
|
|
10685
|
+
const response = await requestFactorAi(`/api/v1/apps/${encodeURIComponent(appId)}/changes`, {
|
|
10686
|
+
method: "POST",
|
|
10687
|
+
body: JSON.stringify({
|
|
10688
|
+
files,
|
|
10689
|
+
deploy: args?.deploy !== false
|
|
10690
|
+
})
|
|
10691
|
+
});
|
|
10692
|
+
await persistFactorAiWorkspaceManifestFromResponse(response);
|
|
10693
|
+
return response;
|
|
10694
|
+
}
|
|
10695
|
+
async function factorAiRedeployApp(args) {
|
|
10696
|
+
const appId = String(args?.appId || "").trim();
|
|
10697
|
+
if (!appId) {
|
|
10698
|
+
return { error: "appId is required." };
|
|
10699
|
+
}
|
|
10700
|
+
const response = await requestFactorAi(`/api/v1/apps/${encodeURIComponent(appId)}/redeploy`, {
|
|
10701
|
+
method: "POST"
|
|
10702
|
+
});
|
|
10703
|
+
await persistFactorAiWorkspaceManifestFromResponse(response);
|
|
10704
|
+
return response;
|
|
10705
|
+
}
|
|
10706
|
+
function extractFactorAiResponseData(response) {
|
|
10707
|
+
if (response && typeof response === "object" && response.data && typeof response.data === "object") {
|
|
10708
|
+
return response.data;
|
|
10709
|
+
}
|
|
10710
|
+
return response;
|
|
10711
|
+
}
|
|
10712
|
+
async function persistFactorAiWorkspaceManifestFromResponse(response) {
|
|
10713
|
+
const data = extractFactorAiResponseData(response);
|
|
10714
|
+
const appContext = data?.appContext;
|
|
10715
|
+
if (!appContext || typeof appContext.appId !== "string" || !appContext.appId.trim()) {
|
|
10716
|
+
return;
|
|
10717
|
+
}
|
|
10718
|
+
await writeFactorAiWorkspaceManifest({
|
|
10719
|
+
projectDir: process.cwd(),
|
|
10720
|
+
appContext: {
|
|
10721
|
+
appId: String(appContext.appId).trim(),
|
|
10722
|
+
tenantId: appContext.tenantId ?? null,
|
|
10723
|
+
projectId: appContext.projectId ?? null,
|
|
10724
|
+
workspaceId: appContext.workspaceId ?? null,
|
|
10725
|
+
revisionId: appContext.revisionId ?? null,
|
|
10726
|
+
deploymentId: appContext.deploymentId ?? null,
|
|
10727
|
+
buildJobId: appContext.buildJobId ?? null,
|
|
10728
|
+
manifestPath: appContext.manifestPath ?? appContext.manifestFile ?? "factorai.sh.json",
|
|
10729
|
+
manifestFile: appContext.manifestFile ?? appContext.manifestPath ?? "factorai.sh.json",
|
|
10730
|
+
appUrl: appContext.appUrl ?? data?.url ?? null
|
|
10731
|
+
},
|
|
10732
|
+
name: data?.name ?? data?.app?.name ?? void 0,
|
|
10733
|
+
status: data?.status ?? "building",
|
|
10734
|
+
isRedeploy: Boolean(data?.isRedeploy),
|
|
10735
|
+
message: typeof data?.message === "string" ? data.message : null,
|
|
10736
|
+
app: {
|
|
10737
|
+
...typeof data?.app === "object" && data.app ? data.app : {},
|
|
10738
|
+
url: data?.url ?? data?.app?.url ?? null
|
|
10739
|
+
},
|
|
10740
|
+
agent: typeof data?.agent === "object" && data.agent ? data.agent : void 0,
|
|
10741
|
+
sandbox: typeof data?.sandbox === "object" && data.sandbox ? data.sandbox : void 0,
|
|
10742
|
+
source: typeof data?.source === "object" && data.source ? data.source : void 0,
|
|
10743
|
+
extra: {
|
|
10744
|
+
lastUpdatedFromTool: "factorai.sh.apply_app_changes"
|
|
10745
|
+
}
|
|
10746
|
+
});
|
|
10747
|
+
}
|
|
10748
|
+
function getFactorAiSandboxToolDefinitions() {
|
|
10749
|
+
if (!isFactorAiSandboxEnabled()) {
|
|
10750
|
+
return [];
|
|
10751
|
+
}
|
|
10752
|
+
return [
|
|
10753
|
+
{
|
|
10754
|
+
type: "function",
|
|
10755
|
+
function: {
|
|
10756
|
+
name: "factorai.sh.create_next_app",
|
|
10757
|
+
description: "Create a new Next.js project in the sandbox workspace for FactorAI deployments.",
|
|
10758
|
+
parameters: {
|
|
10759
|
+
type: "object",
|
|
10760
|
+
properties: {
|
|
10761
|
+
name: { type: "string", description: "Project name." },
|
|
10762
|
+
template: { type: "string", enum: ["minimal", "full"], description: "Project template." },
|
|
10763
|
+
directory: { type: "string", description: "Target directory for the project." }
|
|
10764
|
+
},
|
|
10765
|
+
required: ["name"],
|
|
10766
|
+
additionalProperties: false
|
|
10767
|
+
}
|
|
10768
|
+
}
|
|
10769
|
+
},
|
|
10770
|
+
{
|
|
10771
|
+
type: "function",
|
|
10772
|
+
function: {
|
|
10773
|
+
name: "factorai.sh.deploy_app",
|
|
10774
|
+
description: "Deploy a Next.js project from the sandbox to the FactorAI hosting backend. The returned metadata must be written into factorai.sh.json in the project root for future incremental edits.",
|
|
10775
|
+
parameters: {
|
|
10776
|
+
type: "object",
|
|
10777
|
+
properties: {
|
|
10778
|
+
projectDir: { type: "string", description: "Path to the Next.js project." },
|
|
10779
|
+
name: { type: "string", description: "App name." },
|
|
10780
|
+
severinoUrl: { type: "string", description: "Optional explicit backend URL." },
|
|
10781
|
+
apiKey: { type: "string", description: "Optional explicit API key." }
|
|
10782
|
+
},
|
|
10783
|
+
required: ["projectDir"],
|
|
10784
|
+
additionalProperties: false
|
|
10785
|
+
}
|
|
10786
|
+
}
|
|
10787
|
+
},
|
|
10788
|
+
{
|
|
10789
|
+
type: "function",
|
|
10790
|
+
function: {
|
|
10791
|
+
name: "factorai.sh.get_app_status",
|
|
10792
|
+
description: "Get the current status and contract for a FactorAI deployment in the sandbox.",
|
|
10793
|
+
parameters: {
|
|
10794
|
+
type: "object",
|
|
10795
|
+
properties: {
|
|
10796
|
+
appId: { type: "string", description: "FactorAI app identifier." }
|
|
10797
|
+
},
|
|
10798
|
+
required: ["appId"],
|
|
10799
|
+
additionalProperties: false
|
|
10800
|
+
}
|
|
10801
|
+
}
|
|
10802
|
+
},
|
|
10803
|
+
{
|
|
10804
|
+
type: "function",
|
|
10805
|
+
function: {
|
|
10806
|
+
name: "factorai.sh.apply_app_changes",
|
|
10807
|
+
description: "Apply incremental file changes to a FactorAI workspace and optionally redeploy.",
|
|
10808
|
+
parameters: {
|
|
10809
|
+
type: "object",
|
|
10810
|
+
properties: {
|
|
10811
|
+
appId: { type: "string", description: "FactorAI app identifier." },
|
|
10812
|
+
files: {
|
|
10813
|
+
type: "array",
|
|
10814
|
+
description: "List of changed files to apply.",
|
|
10815
|
+
items: {
|
|
10816
|
+
type: "object",
|
|
10817
|
+
properties: {
|
|
10818
|
+
path: { type: "string" },
|
|
10819
|
+
content: { type: "string" }
|
|
10820
|
+
},
|
|
10821
|
+
required: ["path", "content"],
|
|
10822
|
+
additionalProperties: false
|
|
10823
|
+
}
|
|
10824
|
+
},
|
|
10825
|
+
changes: {
|
|
10826
|
+
type: "array",
|
|
10827
|
+
description: "Alias for files.",
|
|
10828
|
+
items: {
|
|
10829
|
+
type: "object",
|
|
10830
|
+
properties: {
|
|
10831
|
+
path: { type: "string" },
|
|
10832
|
+
content: { type: "string" }
|
|
10833
|
+
},
|
|
10834
|
+
required: ["path", "content"],
|
|
10835
|
+
additionalProperties: false
|
|
10836
|
+
}
|
|
10837
|
+
},
|
|
10838
|
+
deploy: {
|
|
10839
|
+
type: "boolean",
|
|
10840
|
+
description: "Redeploy after applying changes. Defaults to true."
|
|
10841
|
+
}
|
|
10842
|
+
},
|
|
10843
|
+
required: ["appId"],
|
|
10844
|
+
additionalProperties: false
|
|
10845
|
+
}
|
|
10846
|
+
}
|
|
10847
|
+
},
|
|
10848
|
+
{
|
|
10849
|
+
type: "function",
|
|
10850
|
+
function: {
|
|
10851
|
+
name: "factorai.sh.redeploy_app",
|
|
10852
|
+
description: "Redeploy the current revision of a FactorAI app.",
|
|
10853
|
+
parameters: {
|
|
10854
|
+
type: "object",
|
|
10855
|
+
properties: {
|
|
10856
|
+
appId: { type: "string", description: "FactorAI app identifier." }
|
|
10857
|
+
},
|
|
10858
|
+
required: ["appId"],
|
|
10859
|
+
additionalProperties: false
|
|
10860
|
+
}
|
|
10861
|
+
}
|
|
10862
|
+
}
|
|
10863
|
+
];
|
|
10864
|
+
}
|
|
10401
10865
|
var NATIVE_TOOL_ENTRIES = [
|
|
10402
10866
|
{
|
|
10403
10867
|
metadata: {
|
|
@@ -10964,25 +11428,63 @@ var NATIVE_TOOL_ENTRIES = [
|
|
|
10964
11428
|
},
|
|
10965
11429
|
{
|
|
10966
11430
|
metadata: {
|
|
10967
|
-
name: "create_next_app",
|
|
11431
|
+
name: "factorai.sh.create_next_app",
|
|
10968
11432
|
category: "filesystem",
|
|
10969
11433
|
riskLevel: "write",
|
|
10970
11434
|
autoApproveInLocal: false,
|
|
10971
11435
|
autoApproveInSandbox: true,
|
|
11436
|
+
sandboxOnly: true,
|
|
10972
11437
|
description: "Create a new Next.js project instantly with App Router, shadcn/ui components, Tailwind CSS, and TypeScript. Templates: minimal (basic structure) or full (with pre-built UI components)."
|
|
10973
11438
|
},
|
|
10974
11439
|
implementation: createNextApp
|
|
10975
11440
|
},
|
|
10976
11441
|
{
|
|
10977
11442
|
metadata: {
|
|
10978
|
-
name: "deploy_app",
|
|
11443
|
+
name: "factorai.sh.deploy_app",
|
|
10979
11444
|
category: "execution",
|
|
10980
11445
|
riskLevel: "network",
|
|
10981
11446
|
autoApproveInLocal: false,
|
|
10982
11447
|
autoApproveInSandbox: true,
|
|
11448
|
+
sandboxOnly: true,
|
|
10983
11449
|
description: "Deploy a Next.js project to Severino. Zips the project (excluding node_modules, .next) and uploads to /api/v1/deploy. Returns appId and live URL."
|
|
10984
11450
|
},
|
|
10985
11451
|
implementation: deployApp
|
|
11452
|
+
},
|
|
11453
|
+
{
|
|
11454
|
+
metadata: {
|
|
11455
|
+
name: "factorai.sh.get_app_status",
|
|
11456
|
+
category: "knowledge",
|
|
11457
|
+
riskLevel: "network",
|
|
11458
|
+
autoApproveInLocal: false,
|
|
11459
|
+
autoApproveInSandbox: true,
|
|
11460
|
+
sandboxOnly: true,
|
|
11461
|
+
description: "Sandbox-only FactorAI tool. Fetch app status and sandbox contract from the deployed backend."
|
|
11462
|
+
},
|
|
11463
|
+
implementation: factorAiGetAppStatus
|
|
11464
|
+
},
|
|
11465
|
+
{
|
|
11466
|
+
metadata: {
|
|
11467
|
+
name: "factorai.sh.apply_app_changes",
|
|
11468
|
+
category: "filesystem",
|
|
11469
|
+
riskLevel: "write",
|
|
11470
|
+
autoApproveInLocal: false,
|
|
11471
|
+
autoApproveInSandbox: true,
|
|
11472
|
+
sandboxOnly: true,
|
|
11473
|
+
description: "Sandbox-only FactorAI tool. Apply file changes to a deployed app workspace and redeploy it."
|
|
11474
|
+
},
|
|
11475
|
+
implementation: factorAiApplyAppChanges
|
|
11476
|
+
},
|
|
11477
|
+
{
|
|
11478
|
+
metadata: {
|
|
11479
|
+
name: "factorai.sh.redeploy_app",
|
|
11480
|
+
category: "execution",
|
|
11481
|
+
riskLevel: "network",
|
|
11482
|
+
autoApproveInLocal: false,
|
|
11483
|
+
autoApproveInSandbox: true,
|
|
11484
|
+
sandboxOnly: true,
|
|
11485
|
+
description: "Sandbox-only FactorAI tool. Trigger redeploy for the current app revision."
|
|
11486
|
+
},
|
|
11487
|
+
implementation: factorAiRedeployApp
|
|
10986
11488
|
}
|
|
10987
11489
|
];
|
|
10988
11490
|
var TOOL_METADATA_MAP = new Map(
|
|
@@ -10998,11 +11500,10 @@ function getNativeToolImplementation(toolName) {
|
|
|
10998
11500
|
return TOOL_IMPLEMENTATION_MAP.get(toolName);
|
|
10999
11501
|
}
|
|
11000
11502
|
function getAllNativeToolMetadata() {
|
|
11001
|
-
|
|
11002
|
-
|
|
11003
|
-
|
|
11004
|
-
|
|
11005
|
-
return NATIVE_TOOL_ENTRIES.map((entry) => entry.metadata);
|
|
11503
|
+
return NATIVE_TOOL_ENTRIES.filter((entry) => !entry.metadata.sandboxOnly || isFactorAiSandboxEnabled()).map((entry) => entry.metadata);
|
|
11504
|
+
}
|
|
11505
|
+
function getSandboxOnlyNativeToolDefinitions() {
|
|
11506
|
+
return getFactorAiSandboxToolDefinitions();
|
|
11006
11507
|
}
|
|
11007
11508
|
function applyMetadataToToolDefinitions(toolDefinitions) {
|
|
11008
11509
|
return toolDefinitions.map((definition) => {
|
|
@@ -11026,12 +11527,14 @@ var ToolInvoker = class {
|
|
|
11026
11527
|
*/
|
|
11027
11528
|
async initialize() {
|
|
11028
11529
|
try {
|
|
11029
|
-
const
|
|
11030
|
-
const
|
|
11031
|
-
const configPath =
|
|
11032
|
-
const fileContent = await
|
|
11530
|
+
const currentFilePath = fileURLToPath2(import.meta.url);
|
|
11531
|
+
const currentDirPath = path28.dirname(currentFilePath);
|
|
11532
|
+
const configPath = path28.resolve(currentDirPath, "config", "native_tools.json");
|
|
11533
|
+
const fileContent = await fs26.readFile(configPath, "utf-8");
|
|
11033
11534
|
const config2 = JSON.parse(fileContent);
|
|
11034
11535
|
this.toolDefinitions = applyMetadataToToolDefinitions(config2.nativeTools);
|
|
11536
|
+
const sandboxOnlyTools = applyMetadataToToolDefinitions(getSandboxOnlyNativeToolDefinitions());
|
|
11537
|
+
this.toolDefinitions.push(...sandboxOnlyTools);
|
|
11035
11538
|
} catch (error) {
|
|
11036
11539
|
console.error("[ToolInvoker] Erro cr\xEDtico ao carregar 'native_tools.json'. As ferramentas nativas n\xE3o estar\xE3o dispon\xEDveis.", error);
|
|
11037
11540
|
this.toolDefinitions = [];
|
|
@@ -11071,8 +11574,8 @@ var ToolInvoker = class {
|
|
|
11071
11574
|
};
|
|
11072
11575
|
|
|
11073
11576
|
// src/app/agent/tools/mcp/mcp_client.ts
|
|
11074
|
-
import { promises as
|
|
11075
|
-
import
|
|
11577
|
+
import { promises as fs27 } from "fs";
|
|
11578
|
+
import path29 from "path";
|
|
11076
11579
|
import os16 from "os";
|
|
11077
11580
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
11078
11581
|
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
@@ -11100,9 +11603,9 @@ var MCPClient = class {
|
|
|
11100
11603
|
});
|
|
11101
11604
|
}
|
|
11102
11605
|
const __filename = fileURLToPath3(import.meta.url);
|
|
11103
|
-
const __dirname =
|
|
11104
|
-
const defaultConfigPath =
|
|
11105
|
-
const userConfigPath =
|
|
11606
|
+
const __dirname = path29.dirname(__filename);
|
|
11607
|
+
const defaultConfigPath = path29.resolve(__dirname, "config", "bluma-mcp.json");
|
|
11608
|
+
const userConfigPath = path29.join(os16.homedir(), ".bluma", "bluma-mcp.json");
|
|
11106
11609
|
const defaultConfig = await this.loadMcpConfig(defaultConfigPath, "Default");
|
|
11107
11610
|
const userConfig = await this.loadMcpConfig(userConfigPath, "User");
|
|
11108
11611
|
const mergedConfig = {
|
|
@@ -11136,7 +11639,7 @@ var MCPClient = class {
|
|
|
11136
11639
|
}
|
|
11137
11640
|
async loadMcpConfig(configPath, configType) {
|
|
11138
11641
|
try {
|
|
11139
|
-
const fileContent = await
|
|
11642
|
+
const fileContent = await fs27.readFile(configPath, "utf-8");
|
|
11140
11643
|
const processedContent = this.replaceEnvPlaceholders(fileContent);
|
|
11141
11644
|
return JSON.parse(processedContent);
|
|
11142
11645
|
} catch (error) {
|
|
@@ -11313,13 +11816,13 @@ PENALTY APPLIED: ${penalty.toFixed(1)} points deducted.
|
|
|
11313
11816
|
};
|
|
11314
11817
|
|
|
11315
11818
|
// src/app/agent/bluma/core/bluma.ts
|
|
11316
|
-
import
|
|
11819
|
+
import path39 from "path";
|
|
11317
11820
|
import { v4 as uuidv48 } from "uuid";
|
|
11318
11821
|
|
|
11319
11822
|
// src/app/agent/session_manager/session_manager.ts
|
|
11320
|
-
import
|
|
11823
|
+
import path30 from "path";
|
|
11321
11824
|
import os17 from "os";
|
|
11322
|
-
import { promises as
|
|
11825
|
+
import { promises as fs28 } from "fs";
|
|
11323
11826
|
var fileLocks = /* @__PURE__ */ new Map();
|
|
11324
11827
|
async function withFileLock(file, fn) {
|
|
11325
11828
|
const prev = fileLocks.get(file) || Promise.resolve();
|
|
@@ -11355,13 +11858,13 @@ function debouncedSave(sessionFile, history, memory) {
|
|
|
11355
11858
|
function expandHome(p) {
|
|
11356
11859
|
if (!p) return p;
|
|
11357
11860
|
if (p.startsWith("~")) {
|
|
11358
|
-
return
|
|
11861
|
+
return path30.join(os17.homedir(), p.slice(1));
|
|
11359
11862
|
}
|
|
11360
11863
|
return p;
|
|
11361
11864
|
}
|
|
11362
11865
|
function getPreferredAppDir() {
|
|
11363
|
-
const fixed =
|
|
11364
|
-
return
|
|
11866
|
+
const fixed = path30.join(os17.homedir(), ".bluma");
|
|
11867
|
+
return path30.resolve(expandHome(fixed));
|
|
11365
11868
|
}
|
|
11366
11869
|
async function safeRenameWithRetry(src, dest, maxRetries = 6) {
|
|
11367
11870
|
let attempt = 0;
|
|
@@ -11369,10 +11872,10 @@ async function safeRenameWithRetry(src, dest, maxRetries = 6) {
|
|
|
11369
11872
|
const isWin = process.platform === "win32";
|
|
11370
11873
|
while (attempt <= maxRetries) {
|
|
11371
11874
|
try {
|
|
11372
|
-
const dir =
|
|
11373
|
-
await
|
|
11875
|
+
const dir = path30.dirname(dest);
|
|
11876
|
+
await fs28.mkdir(dir, { recursive: true }).catch(() => {
|
|
11374
11877
|
});
|
|
11375
|
-
await
|
|
11878
|
+
await fs28.rename(src, dest);
|
|
11376
11879
|
return;
|
|
11377
11880
|
} catch (e) {
|
|
11378
11881
|
lastErr = e;
|
|
@@ -11385,13 +11888,13 @@ async function safeRenameWithRetry(src, dest, maxRetries = 6) {
|
|
|
11385
11888
|
}
|
|
11386
11889
|
}
|
|
11387
11890
|
try {
|
|
11388
|
-
await
|
|
11389
|
-
const data = await
|
|
11390
|
-
const dir =
|
|
11391
|
-
await
|
|
11891
|
+
await fs28.access(src);
|
|
11892
|
+
const data = await fs28.readFile(src);
|
|
11893
|
+
const dir = path30.dirname(dest);
|
|
11894
|
+
await fs28.mkdir(dir, { recursive: true }).catch(() => {
|
|
11392
11895
|
});
|
|
11393
|
-
await
|
|
11394
|
-
await
|
|
11896
|
+
await fs28.writeFile(dest, data);
|
|
11897
|
+
await fs28.unlink(src).catch(() => {
|
|
11395
11898
|
});
|
|
11396
11899
|
return;
|
|
11397
11900
|
} catch (fallbackErr) {
|
|
@@ -11404,16 +11907,16 @@ async function safeRenameWithRetry(src, dest, maxRetries = 6) {
|
|
|
11404
11907
|
}
|
|
11405
11908
|
async function ensureSessionDir() {
|
|
11406
11909
|
const appDir = getPreferredAppDir();
|
|
11407
|
-
const sessionDir =
|
|
11408
|
-
await
|
|
11910
|
+
const sessionDir = path30.join(appDir, "sessions");
|
|
11911
|
+
await fs28.mkdir(sessionDir, { recursive: true });
|
|
11409
11912
|
return sessionDir;
|
|
11410
11913
|
}
|
|
11411
11914
|
async function loadOrcreateSession(sessionId) {
|
|
11412
11915
|
const sessionDir = await ensureSessionDir();
|
|
11413
|
-
const sessionFile =
|
|
11916
|
+
const sessionFile = path30.join(sessionDir, `${sessionId}.json`);
|
|
11414
11917
|
try {
|
|
11415
|
-
await
|
|
11416
|
-
const fileContent = await
|
|
11918
|
+
await fs28.access(sessionFile);
|
|
11919
|
+
const fileContent = await fs28.readFile(sessionFile, "utf-8");
|
|
11417
11920
|
const sessionData = JSON.parse(fileContent);
|
|
11418
11921
|
const memory = {
|
|
11419
11922
|
historyAnchor: sessionData.history_anchor ?? null,
|
|
@@ -11426,7 +11929,7 @@ async function loadOrcreateSession(sessionId) {
|
|
|
11426
11929
|
created_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
11427
11930
|
conversation_history: []
|
|
11428
11931
|
};
|
|
11429
|
-
await
|
|
11932
|
+
await fs28.writeFile(sessionFile, JSON.stringify(newSessionData, null, 2), "utf-8");
|
|
11430
11933
|
const emptyMemory = {
|
|
11431
11934
|
historyAnchor: null,
|
|
11432
11935
|
compressedTurnSliceCount: 0
|
|
@@ -11438,12 +11941,12 @@ async function doSaveSessionHistory(sessionFile, history, memory) {
|
|
|
11438
11941
|
await withFileLock(sessionFile, async () => {
|
|
11439
11942
|
let sessionData;
|
|
11440
11943
|
try {
|
|
11441
|
-
const dir =
|
|
11442
|
-
await
|
|
11944
|
+
const dir = path30.dirname(sessionFile);
|
|
11945
|
+
await fs28.mkdir(dir, { recursive: true });
|
|
11443
11946
|
} catch {
|
|
11444
11947
|
}
|
|
11445
11948
|
try {
|
|
11446
|
-
const fileContent = await
|
|
11949
|
+
const fileContent = await fs28.readFile(sessionFile, "utf-8");
|
|
11447
11950
|
sessionData = JSON.parse(fileContent);
|
|
11448
11951
|
} catch (error) {
|
|
11449
11952
|
const code = error && error.code;
|
|
@@ -11454,14 +11957,14 @@ async function doSaveSessionHistory(sessionFile, history, memory) {
|
|
|
11454
11957
|
console.warn(`An unknown error occurred while reading ${sessionFile}. Re-initializing.`, error);
|
|
11455
11958
|
}
|
|
11456
11959
|
}
|
|
11457
|
-
const sessionId =
|
|
11960
|
+
const sessionId = path30.basename(sessionFile, ".json");
|
|
11458
11961
|
sessionData = {
|
|
11459
11962
|
session_id: sessionId,
|
|
11460
11963
|
created_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
11461
11964
|
conversation_history: []
|
|
11462
11965
|
};
|
|
11463
11966
|
try {
|
|
11464
|
-
await
|
|
11967
|
+
await fs28.writeFile(sessionFile, JSON.stringify(sessionData, null, 2), "utf-8");
|
|
11465
11968
|
} catch {
|
|
11466
11969
|
}
|
|
11467
11970
|
}
|
|
@@ -11477,7 +11980,7 @@ async function doSaveSessionHistory(sessionFile, history, memory) {
|
|
|
11477
11980
|
}
|
|
11478
11981
|
const tempSessionFile = `${sessionFile}.${Date.now()}.tmp`;
|
|
11479
11982
|
try {
|
|
11480
|
-
await
|
|
11983
|
+
await fs28.writeFile(tempSessionFile, JSON.stringify(sessionData, null, 2), "utf-8");
|
|
11481
11984
|
await safeRenameWithRetry(tempSessionFile, sessionFile);
|
|
11482
11985
|
} catch (writeError) {
|
|
11483
11986
|
if (writeError instanceof Error) {
|
|
@@ -11486,7 +11989,7 @@ async function doSaveSessionHistory(sessionFile, history, memory) {
|
|
|
11486
11989
|
console.error(`An unknown fatal error occurred while saving session to ${sessionFile}:`, writeError);
|
|
11487
11990
|
}
|
|
11488
11991
|
try {
|
|
11489
|
-
await
|
|
11992
|
+
await fs28.unlink(tempSessionFile);
|
|
11490
11993
|
} catch {
|
|
11491
11994
|
}
|
|
11492
11995
|
}
|
|
@@ -11504,13 +12007,13 @@ async function saveSessionHistory(sessionFile, history, memory) {
|
|
|
11504
12007
|
|
|
11505
12008
|
// src/app/agent/core/prompt/prompt_builder.ts
|
|
11506
12009
|
import os22 from "os";
|
|
11507
|
-
import
|
|
11508
|
-
import
|
|
12010
|
+
import fs35 from "fs";
|
|
12011
|
+
import path37 from "path";
|
|
11509
12012
|
import { execSync as execSync4 } from "child_process";
|
|
11510
12013
|
|
|
11511
12014
|
// src/app/agent/skills/skill_loader.ts
|
|
11512
|
-
import
|
|
11513
|
-
import
|
|
12015
|
+
import fs29 from "fs";
|
|
12016
|
+
import path31 from "path";
|
|
11514
12017
|
import os18 from "os";
|
|
11515
12018
|
import { fileURLToPath as fileURLToPath4 } from "node:url";
|
|
11516
12019
|
var SkillLoader = class _SkillLoader {
|
|
@@ -11520,8 +12023,8 @@ var SkillLoader = class _SkillLoader {
|
|
|
11520
12023
|
cache = /* @__PURE__ */ new Map();
|
|
11521
12024
|
conflicts = [];
|
|
11522
12025
|
constructor(projectRoot, bundledDir) {
|
|
11523
|
-
this.projectSkillsDir =
|
|
11524
|
-
this.globalSkillsDir =
|
|
12026
|
+
this.projectSkillsDir = path31.join(projectRoot, ".bluma", "skills");
|
|
12027
|
+
this.globalSkillsDir = path31.join(os18.homedir(), ".bluma", "skills");
|
|
11525
12028
|
this.bundledSkillsDir = bundledDir || _SkillLoader.resolveBundledDir();
|
|
11526
12029
|
}
|
|
11527
12030
|
/**
|
|
@@ -11530,48 +12033,48 @@ var SkillLoader = class _SkillLoader {
|
|
|
11530
12033
|
*/
|
|
11531
12034
|
static resolveBundledDir() {
|
|
11532
12035
|
if (process.env.JEST_WORKER_ID !== void 0 || process.env.NODE_ENV === "test") {
|
|
11533
|
-
return
|
|
12036
|
+
return path31.join(process.cwd(), "dist", "config", "skills");
|
|
11534
12037
|
}
|
|
11535
12038
|
const candidates = [];
|
|
11536
12039
|
const push = (p) => {
|
|
11537
|
-
const abs =
|
|
12040
|
+
const abs = path31.resolve(p);
|
|
11538
12041
|
if (!candidates.includes(abs)) {
|
|
11539
12042
|
candidates.push(abs);
|
|
11540
12043
|
}
|
|
11541
12044
|
};
|
|
11542
12045
|
let argvBundled = null;
|
|
11543
12046
|
try {
|
|
11544
|
-
const bundleDir =
|
|
11545
|
-
push(
|
|
12047
|
+
const bundleDir = path31.dirname(fileURLToPath4(import.meta.url));
|
|
12048
|
+
push(path31.join(bundleDir, "config", "skills"));
|
|
11546
12049
|
} catch {
|
|
11547
12050
|
}
|
|
11548
12051
|
const argv1 = process.argv[1];
|
|
11549
12052
|
if (argv1 && !argv1.startsWith("-")) {
|
|
11550
12053
|
try {
|
|
11551
12054
|
let resolved = argv1;
|
|
11552
|
-
if (
|
|
11553
|
-
resolved =
|
|
11554
|
-
} else if (!
|
|
11555
|
-
resolved =
|
|
12055
|
+
if (path31.isAbsolute(argv1) && fs29.existsSync(argv1)) {
|
|
12056
|
+
resolved = fs29.realpathSync(argv1);
|
|
12057
|
+
} else if (!path31.isAbsolute(argv1)) {
|
|
12058
|
+
resolved = path31.resolve(process.cwd(), argv1);
|
|
11556
12059
|
}
|
|
11557
|
-
const scriptDir =
|
|
11558
|
-
argvBundled =
|
|
12060
|
+
const scriptDir = path31.dirname(resolved);
|
|
12061
|
+
argvBundled = path31.join(scriptDir, "config", "skills");
|
|
11559
12062
|
push(argvBundled);
|
|
11560
12063
|
} catch {
|
|
11561
12064
|
}
|
|
11562
12065
|
}
|
|
11563
12066
|
for (const abs of candidates) {
|
|
11564
|
-
if (
|
|
12067
|
+
if (fs29.existsSync(abs)) {
|
|
11565
12068
|
return abs;
|
|
11566
12069
|
}
|
|
11567
12070
|
}
|
|
11568
12071
|
try {
|
|
11569
|
-
return
|
|
12072
|
+
return path31.join(path31.dirname(fileURLToPath4(import.meta.url)), "config", "skills");
|
|
11570
12073
|
} catch {
|
|
11571
12074
|
if (argvBundled) {
|
|
11572
12075
|
return argvBundled;
|
|
11573
12076
|
}
|
|
11574
|
-
return
|
|
12077
|
+
return path31.join(os18.homedir(), ".bluma", "__bundled_skills_unresolved__");
|
|
11575
12078
|
}
|
|
11576
12079
|
}
|
|
11577
12080
|
/**
|
|
@@ -11600,8 +12103,8 @@ var SkillLoader = class _SkillLoader {
|
|
|
11600
12103
|
this.conflicts.push({
|
|
11601
12104
|
name: skill.name,
|
|
11602
12105
|
userSource: source,
|
|
11603
|
-
userPath:
|
|
11604
|
-
bundledPath:
|
|
12106
|
+
userPath: path31.join(dir, skill.name, "SKILL.md"),
|
|
12107
|
+
bundledPath: path31.join(this.bundledSkillsDir, skill.name, "SKILL.md")
|
|
11605
12108
|
});
|
|
11606
12109
|
continue;
|
|
11607
12110
|
}
|
|
@@ -11609,20 +12112,20 @@ var SkillLoader = class _SkillLoader {
|
|
|
11609
12112
|
}
|
|
11610
12113
|
}
|
|
11611
12114
|
listFromDir(dir, source) {
|
|
11612
|
-
if (!
|
|
12115
|
+
if (!fs29.existsSync(dir)) return [];
|
|
11613
12116
|
try {
|
|
11614
|
-
return
|
|
11615
|
-
const fullPath =
|
|
11616
|
-
return
|
|
11617
|
-
}).map((d) => this.loadMetadataFromPath(
|
|
12117
|
+
return fs29.readdirSync(dir).filter((d) => {
|
|
12118
|
+
const fullPath = path31.join(dir, d);
|
|
12119
|
+
return fs29.statSync(fullPath).isDirectory() && fs29.existsSync(path31.join(fullPath, "SKILL.md"));
|
|
12120
|
+
}).map((d) => this.loadMetadataFromPath(path31.join(dir, d, "SKILL.md"), d, source)).filter((m) => m !== null);
|
|
11618
12121
|
} catch {
|
|
11619
12122
|
return [];
|
|
11620
12123
|
}
|
|
11621
12124
|
}
|
|
11622
12125
|
loadMetadataFromPath(skillPath, skillName, source) {
|
|
11623
|
-
if (!
|
|
12126
|
+
if (!fs29.existsSync(skillPath)) return null;
|
|
11624
12127
|
try {
|
|
11625
|
-
const raw =
|
|
12128
|
+
const raw = fs29.readFileSync(skillPath, "utf-8");
|
|
11626
12129
|
const parsed = this.parseFrontmatter(raw);
|
|
11627
12130
|
return {
|
|
11628
12131
|
name: parsed.name || skillName,
|
|
@@ -11644,12 +12147,12 @@ var SkillLoader = class _SkillLoader {
|
|
|
11644
12147
|
*/
|
|
11645
12148
|
load(name) {
|
|
11646
12149
|
if (this.cache.has(name)) return this.cache.get(name);
|
|
11647
|
-
const bundledPath =
|
|
11648
|
-
const projectPath =
|
|
11649
|
-
const globalPath =
|
|
11650
|
-
const existsBundled =
|
|
11651
|
-
const existsProject =
|
|
11652
|
-
const existsGlobal =
|
|
12150
|
+
const bundledPath = path31.join(this.bundledSkillsDir, name, "SKILL.md");
|
|
12151
|
+
const projectPath = path31.join(this.projectSkillsDir, name, "SKILL.md");
|
|
12152
|
+
const globalPath = path31.join(this.globalSkillsDir, name, "SKILL.md");
|
|
12153
|
+
const existsBundled = fs29.existsSync(bundledPath);
|
|
12154
|
+
const existsProject = fs29.existsSync(projectPath);
|
|
12155
|
+
const existsGlobal = fs29.existsSync(globalPath);
|
|
11653
12156
|
if (existsBundled && (existsProject || existsGlobal)) {
|
|
11654
12157
|
const conflictSource = existsProject ? "project" : "global";
|
|
11655
12158
|
const conflictPath = existsProject ? projectPath : globalPath;
|
|
@@ -11688,9 +12191,9 @@ var SkillLoader = class _SkillLoader {
|
|
|
11688
12191
|
}
|
|
11689
12192
|
loadFromPath(skillPath, name, source) {
|
|
11690
12193
|
try {
|
|
11691
|
-
const raw =
|
|
12194
|
+
const raw = fs29.readFileSync(skillPath, "utf-8");
|
|
11692
12195
|
const parsed = this.parseFrontmatter(raw);
|
|
11693
|
-
const skillDir =
|
|
12196
|
+
const skillDir = path31.dirname(skillPath);
|
|
11694
12197
|
return {
|
|
11695
12198
|
name: parsed.name || name,
|
|
11696
12199
|
description: parsed.description || "",
|
|
@@ -11699,22 +12202,22 @@ var SkillLoader = class _SkillLoader {
|
|
|
11699
12202
|
version: parsed.version,
|
|
11700
12203
|
author: parsed.author,
|
|
11701
12204
|
license: parsed.license,
|
|
11702
|
-
references: this.scanAssets(
|
|
11703
|
-
scripts: this.scanAssets(
|
|
12205
|
+
references: this.scanAssets(path31.join(skillDir, "references")),
|
|
12206
|
+
scripts: this.scanAssets(path31.join(skillDir, "scripts"))
|
|
11704
12207
|
};
|
|
11705
12208
|
} catch {
|
|
11706
12209
|
return null;
|
|
11707
12210
|
}
|
|
11708
12211
|
}
|
|
11709
12212
|
scanAssets(dir) {
|
|
11710
|
-
if (!
|
|
12213
|
+
if (!fs29.existsSync(dir)) return [];
|
|
11711
12214
|
try {
|
|
11712
|
-
return
|
|
11713
|
-
const fp =
|
|
11714
|
-
return
|
|
12215
|
+
return fs29.readdirSync(dir).filter((f) => {
|
|
12216
|
+
const fp = path31.join(dir, f);
|
|
12217
|
+
return fs29.statSync(fp).isFile();
|
|
11715
12218
|
}).map((f) => ({
|
|
11716
12219
|
name: f,
|
|
11717
|
-
path:
|
|
12220
|
+
path: path31.resolve(dir, f)
|
|
11718
12221
|
}));
|
|
11719
12222
|
} catch {
|
|
11720
12223
|
return [];
|
|
@@ -11771,10 +12274,10 @@ var SkillLoader = class _SkillLoader {
|
|
|
11771
12274
|
this.cache.clear();
|
|
11772
12275
|
}
|
|
11773
12276
|
exists(name) {
|
|
11774
|
-
const bundledPath =
|
|
11775
|
-
const projectPath =
|
|
11776
|
-
const globalPath =
|
|
11777
|
-
return
|
|
12277
|
+
const bundledPath = path31.join(this.bundledSkillsDir, name, "SKILL.md");
|
|
12278
|
+
const projectPath = path31.join(this.projectSkillsDir, name, "SKILL.md");
|
|
12279
|
+
const globalPath = path31.join(this.globalSkillsDir, name, "SKILL.md");
|
|
12280
|
+
return fs29.existsSync(bundledPath) || fs29.existsSync(projectPath) || fs29.existsSync(globalPath);
|
|
11778
12281
|
}
|
|
11779
12282
|
/**
|
|
11780
12283
|
* Retorna conflitos detetados (skills do utilizador com mesmo nome de nativas).
|
|
@@ -11806,13 +12309,13 @@ var SkillLoader = class _SkillLoader {
|
|
|
11806
12309
|
};
|
|
11807
12310
|
|
|
11808
12311
|
// src/app/agent/core/prompt/workspace_snapshot.ts
|
|
11809
|
-
import
|
|
11810
|
-
import
|
|
12312
|
+
import fs31 from "fs";
|
|
12313
|
+
import path33 from "path";
|
|
11811
12314
|
import { execSync as execSync3 } from "child_process";
|
|
11812
12315
|
|
|
11813
12316
|
// src/app/agent/utils/blumamd.ts
|
|
11814
|
-
import
|
|
11815
|
-
import
|
|
12317
|
+
import fs30 from "fs";
|
|
12318
|
+
import path32 from "path";
|
|
11816
12319
|
import os19 from "os";
|
|
11817
12320
|
import { execSync as execSync2 } from "child_process";
|
|
11818
12321
|
var MEMORY_INSTRUCTION_PROMPT = "Instru\xE7\xF5es de mem\xF3ria do BluMa (BLUMA.md) est\xE3o abaixo. Siga estas instru\xE7\xF5es exatamente como escritas. Estas instru\xE7\xF5es OVERRIDE qualquer comportamento padr\xE3o.";
|
|
@@ -11942,12 +12445,12 @@ var TEXT_FILE_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
|
11942
12445
|
function expandIncludePath(includePath, baseDir) {
|
|
11943
12446
|
const cleanPath = includePath.startsWith("@") ? includePath.slice(1) : includePath;
|
|
11944
12447
|
if (cleanPath.startsWith("~")) {
|
|
11945
|
-
return
|
|
12448
|
+
return path32.join(os19.homedir(), cleanPath.slice(1));
|
|
11946
12449
|
}
|
|
11947
|
-
if (
|
|
12450
|
+
if (path32.isAbsolute(cleanPath)) {
|
|
11948
12451
|
return cleanPath;
|
|
11949
12452
|
}
|
|
11950
|
-
return
|
|
12453
|
+
return path32.resolve(baseDir, cleanPath);
|
|
11951
12454
|
}
|
|
11952
12455
|
function processIncludes(content, baseDir, processedFiles) {
|
|
11953
12456
|
const lines = content.split("\n");
|
|
@@ -11956,20 +12459,20 @@ function processIncludes(content, baseDir, processedFiles) {
|
|
|
11956
12459
|
const includeMatch = line.match(/^@\s*([^\s]+)/);
|
|
11957
12460
|
if (includeMatch) {
|
|
11958
12461
|
const includePath = expandIncludePath(includeMatch[1], baseDir);
|
|
11959
|
-
const normalizedPath =
|
|
12462
|
+
const normalizedPath = path32.normalize(includePath);
|
|
11960
12463
|
if (processedFiles.has(normalizedPath)) {
|
|
11961
12464
|
result.push(`<!-- Circular include prevented: ${includeMatch[1]} -->`);
|
|
11962
12465
|
continue;
|
|
11963
12466
|
}
|
|
11964
|
-
const ext =
|
|
12467
|
+
const ext = path32.extname(includePath).toLowerCase();
|
|
11965
12468
|
if (!TEXT_FILE_EXTENSIONS.has(ext)) {
|
|
11966
12469
|
result.push(`<!-- Include skipped (unsupported extension): ${includeMatch[1]} -->`);
|
|
11967
12470
|
continue;
|
|
11968
12471
|
}
|
|
11969
12472
|
try {
|
|
11970
|
-
const includedContent =
|
|
12473
|
+
const includedContent = fs30.readFileSync(includePath, "utf-8");
|
|
11971
12474
|
processedFiles.add(normalizedPath);
|
|
11972
|
-
const processedContent = processIncludes(includedContent,
|
|
12475
|
+
const processedContent = processIncludes(includedContent, path32.dirname(includePath), processedFiles);
|
|
11973
12476
|
result.push(`
|
|
11974
12477
|
<!-- BEGIN INCLUDE ${includeMatch[1]} -->
|
|
11975
12478
|
`);
|
|
@@ -12015,9 +12518,9 @@ function parseFrontmatterPaths(paths) {
|
|
|
12015
12518
|
}
|
|
12016
12519
|
function readMemoryFile(filePath, type, includeBasePath) {
|
|
12017
12520
|
try {
|
|
12018
|
-
const rawContent =
|
|
12019
|
-
const baseDir = includeBasePath ||
|
|
12020
|
-
const processedFiles = /* @__PURE__ */ new Set([
|
|
12521
|
+
const rawContent = fs30.readFileSync(filePath, "utf-8");
|
|
12522
|
+
const baseDir = includeBasePath || path32.dirname(filePath);
|
|
12523
|
+
const processedFiles = /* @__PURE__ */ new Set([path32.normalize(filePath)]);
|
|
12021
12524
|
const { frontmatter, content: withoutFrontmatter } = parseFrontmatter(rawContent);
|
|
12022
12525
|
const globs = parseFrontmatterPaths(frontmatter.paths);
|
|
12023
12526
|
const processedContent = processIncludes(withoutFrontmatter, baseDir, processedFiles);
|
|
@@ -12039,15 +12542,15 @@ function readMemoryFile(filePath, type, includeBasePath) {
|
|
|
12039
12542
|
}
|
|
12040
12543
|
function findGitRoot(startDir) {
|
|
12041
12544
|
let current = startDir;
|
|
12042
|
-
while (current !==
|
|
12043
|
-
const gitPath =
|
|
12545
|
+
while (current !== path32.dirname(current)) {
|
|
12546
|
+
const gitPath = path32.join(current, ".git");
|
|
12044
12547
|
try {
|
|
12045
|
-
if (
|
|
12548
|
+
if (fs30.existsSync(gitPath)) {
|
|
12046
12549
|
return current;
|
|
12047
12550
|
}
|
|
12048
12551
|
} catch {
|
|
12049
12552
|
}
|
|
12050
|
-
current =
|
|
12553
|
+
current = path32.dirname(current);
|
|
12051
12554
|
}
|
|
12052
12555
|
return null;
|
|
12053
12556
|
}
|
|
@@ -12072,17 +12575,17 @@ function getGitUserInfo(cwd) {
|
|
|
12072
12575
|
}
|
|
12073
12576
|
function processRulesDirectory(rulesDir, type, processedPaths, conditionalRule = false) {
|
|
12074
12577
|
const result = [];
|
|
12075
|
-
if (!
|
|
12578
|
+
if (!fs30.existsSync(rulesDir)) {
|
|
12076
12579
|
return result;
|
|
12077
12580
|
}
|
|
12078
12581
|
try {
|
|
12079
|
-
const entries =
|
|
12582
|
+
const entries = fs30.readdirSync(rulesDir, { withFileTypes: true });
|
|
12080
12583
|
for (const entry of entries) {
|
|
12081
|
-
const entryPath =
|
|
12584
|
+
const entryPath = path32.join(rulesDir, entry.name);
|
|
12082
12585
|
if (entry.isDirectory()) {
|
|
12083
12586
|
result.push(...processRulesDirectory(entryPath, type, processedPaths, conditionalRule));
|
|
12084
12587
|
} else if (entry.isFile() && entry.name.endsWith(".md")) {
|
|
12085
|
-
const normalizedPath =
|
|
12588
|
+
const normalizedPath = path32.normalize(entryPath);
|
|
12086
12589
|
if (processedPaths.has(normalizedPath)) {
|
|
12087
12590
|
continue;
|
|
12088
12591
|
}
|
|
@@ -12118,13 +12621,13 @@ function loadManagedMemory() {
|
|
|
12118
12621
|
function loadUserMemory() {
|
|
12119
12622
|
const files = [];
|
|
12120
12623
|
const homeDir = os19.homedir();
|
|
12121
|
-
const userBlumaDir =
|
|
12122
|
-
const userBlumaMd =
|
|
12624
|
+
const userBlumaDir = path32.join(homeDir, ".bluma");
|
|
12625
|
+
const userBlumaMd = path32.join(userBlumaDir, "BLUMA.md");
|
|
12123
12626
|
const userFile = readMemoryFile(userBlumaMd, "User");
|
|
12124
12627
|
if (userFile && userFile.content.trim()) {
|
|
12125
12628
|
files.push(userFile);
|
|
12126
12629
|
}
|
|
12127
|
-
const userRulesDir =
|
|
12630
|
+
const userRulesDir = path32.join(userBlumaDir, "rules");
|
|
12128
12631
|
const processedPaths = /* @__PURE__ */ new Set();
|
|
12129
12632
|
files.push(...processRulesDirectory(userRulesDir, "User", processedPaths, false));
|
|
12130
12633
|
return files;
|
|
@@ -12134,16 +12637,20 @@ function loadProjectMemory(cwd) {
|
|
|
12134
12637
|
const gitRoot = findGitRoot(cwd) || cwd;
|
|
12135
12638
|
const dirs = [];
|
|
12136
12639
|
let currentDir = cwd;
|
|
12137
|
-
|
|
12640
|
+
const MAX_TRAVERSAL_DEPTH = 20;
|
|
12641
|
+
let depth = 0;
|
|
12642
|
+
const normalizedGitRoot = path32.resolve(gitRoot);
|
|
12643
|
+
while (currentDir !== path32.dirname(currentDir) && path32.resolve(currentDir).startsWith(normalizedGitRoot) && depth < MAX_TRAVERSAL_DEPTH) {
|
|
12138
12644
|
dirs.push(currentDir);
|
|
12139
|
-
currentDir =
|
|
12645
|
+
currentDir = path32.dirname(currentDir);
|
|
12646
|
+
depth++;
|
|
12140
12647
|
}
|
|
12141
12648
|
if (!dirs.includes(gitRoot)) {
|
|
12142
12649
|
dirs.push(gitRoot);
|
|
12143
12650
|
}
|
|
12144
12651
|
const processedPaths = /* @__PURE__ */ new Set();
|
|
12145
12652
|
for (const dir of dirs.reverse()) {
|
|
12146
|
-
const projectBlumaMd =
|
|
12653
|
+
const projectBlumaMd = path32.join(dir, "BLUMA.md");
|
|
12147
12654
|
if (!processedPaths.has(projectBlumaMd)) {
|
|
12148
12655
|
processedPaths.add(projectBlumaMd);
|
|
12149
12656
|
const projectFile = readMemoryFile(projectBlumaMd, "Project");
|
|
@@ -12151,7 +12658,7 @@ function loadProjectMemory(cwd) {
|
|
|
12151
12658
|
files.push(projectFile);
|
|
12152
12659
|
}
|
|
12153
12660
|
}
|
|
12154
|
-
const blumaDirBlumaMd =
|
|
12661
|
+
const blumaDirBlumaMd = path32.join(dir, ".bluma", "BLUMA.md");
|
|
12155
12662
|
if (!processedPaths.has(blumaDirBlumaMd)) {
|
|
12156
12663
|
processedPaths.add(blumaDirBlumaMd);
|
|
12157
12664
|
const blumaDirFile = readMemoryFile(blumaDirBlumaMd, "Project");
|
|
@@ -12159,10 +12666,10 @@ function loadProjectMemory(cwd) {
|
|
|
12159
12666
|
files.push(blumaDirFile);
|
|
12160
12667
|
}
|
|
12161
12668
|
}
|
|
12162
|
-
const rulesDir =
|
|
12669
|
+
const rulesDir = path32.join(dir, ".bluma", "rules");
|
|
12163
12670
|
files.push(...processRulesDirectory(rulesDir, "Project", processedPaths, false));
|
|
12164
12671
|
}
|
|
12165
|
-
const localBlumaMd =
|
|
12672
|
+
const localBlumaMd = path32.join(cwd, "BLUMA.local.md");
|
|
12166
12673
|
if (!processedPaths.has(localBlumaMd)) {
|
|
12167
12674
|
processedPaths.add(localBlumaMd);
|
|
12168
12675
|
const localFile = readMemoryFile(localBlumaMd, "Local");
|
|
@@ -12221,25 +12728,38 @@ function getGitUserContext(cwd = process.cwd()) {
|
|
|
12221
12728
|
|
|
12222
12729
|
// src/app/agent/core/prompt/workspace_snapshot.ts
|
|
12223
12730
|
var LIMITS = {
|
|
12731
|
+
/** README.md: generous limit to capture full project overview */
|
|
12224
12732
|
readme: 1e4,
|
|
12733
|
+
/** BLUMA.md: project memory instructions */
|
|
12225
12734
|
blumaMd: 12e3,
|
|
12735
|
+
/** CONTRIBUTING.md: contribution guidelines */
|
|
12226
12736
|
contributing: 4e3,
|
|
12737
|
+
/** CHANGELOG.md: recent version history */
|
|
12227
12738
|
changelog: 4e3,
|
|
12739
|
+
/** pyproject.toml: Python project config */
|
|
12228
12740
|
pyproject: 3500,
|
|
12741
|
+
/** tsconfig.json: TypeScript compiler options */
|
|
12229
12742
|
tsconfig: 2e3,
|
|
12743
|
+
/** Dockerfile / docker-compose: container config */
|
|
12230
12744
|
dockerfile: 2e3,
|
|
12745
|
+
/** CI/CD config: GitHub Actions, GitLab CI, etc. */
|
|
12231
12746
|
ciConfig: 2e3,
|
|
12747
|
+
/** git status: max lines of changed files */
|
|
12232
12748
|
gitStatusLines: 48,
|
|
12749
|
+
/** git log: recent commit history */
|
|
12233
12750
|
gitLogLines: 14,
|
|
12751
|
+
/** git diff --stat: summary of changes */
|
|
12234
12752
|
diffStatLines: 28,
|
|
12753
|
+
/** top-level directory entries */
|
|
12235
12754
|
topDirEntries: 96
|
|
12236
12755
|
};
|
|
12237
12756
|
function safeReadFile(filePath, maxChars) {
|
|
12238
12757
|
try {
|
|
12239
|
-
if (!
|
|
12240
|
-
const st =
|
|
12758
|
+
if (!fs31.existsSync(filePath)) return null;
|
|
12759
|
+
const st = fs31.statSync(filePath);
|
|
12241
12760
|
if (!st.isFile()) return null;
|
|
12242
|
-
const raw =
|
|
12761
|
+
const raw = fs31.readFileSync(filePath, "utf8");
|
|
12762
|
+
if (raw.includes("\0")) return null;
|
|
12243
12763
|
if (raw.length <= maxChars) return raw;
|
|
12244
12764
|
return `${raw.slice(0, maxChars)}
|
|
12245
12765
|
|
|
@@ -12250,7 +12770,7 @@ function safeReadFile(filePath, maxChars) {
|
|
|
12250
12770
|
}
|
|
12251
12771
|
function tryReadReadme(cwd) {
|
|
12252
12772
|
for (const name of ["README.md", "README.MD", "readme.md", "Readme.md"]) {
|
|
12253
|
-
const c = safeReadFile(
|
|
12773
|
+
const c = safeReadFile(path33.join(cwd, name), LIMITS.readme);
|
|
12254
12774
|
if (c) return `(${name})
|
|
12255
12775
|
${c}`;
|
|
12256
12776
|
}
|
|
@@ -12258,14 +12778,14 @@ ${c}`;
|
|
|
12258
12778
|
}
|
|
12259
12779
|
function tryReadBluMaMd(cwd) {
|
|
12260
12780
|
const paths = [
|
|
12261
|
-
|
|
12262
|
-
|
|
12263
|
-
|
|
12781
|
+
path33.join(cwd, "BluMa.md"),
|
|
12782
|
+
path33.join(cwd, "BLUMA.md"),
|
|
12783
|
+
path33.join(cwd, ".bluma", "BluMa.md")
|
|
12264
12784
|
];
|
|
12265
12785
|
for (const p of paths) {
|
|
12266
12786
|
const c = safeReadFile(p, LIMITS.blumaMd);
|
|
12267
12787
|
if (c) {
|
|
12268
|
-
const rel =
|
|
12788
|
+
const rel = path33.relative(cwd, p) || p;
|
|
12269
12789
|
return `(${rel})
|
|
12270
12790
|
${c}`;
|
|
12271
12791
|
}
|
|
@@ -12273,10 +12793,10 @@ ${c}`;
|
|
|
12273
12793
|
return null;
|
|
12274
12794
|
}
|
|
12275
12795
|
function summarizePackageJson(cwd) {
|
|
12276
|
-
const p =
|
|
12796
|
+
const p = path33.join(cwd, "package.json");
|
|
12277
12797
|
try {
|
|
12278
|
-
if (!
|
|
12279
|
-
const pkg = JSON.parse(
|
|
12798
|
+
if (!fs31.existsSync(p)) return null;
|
|
12799
|
+
const pkg = JSON.parse(fs31.readFileSync(p, "utf8"));
|
|
12280
12800
|
const scripts = pkg.scripts;
|
|
12281
12801
|
let scriptKeys = "";
|
|
12282
12802
|
if (scripts && typeof scripts === "object" && !Array.isArray(scripts)) {
|
|
@@ -12309,7 +12829,7 @@ function summarizePackageJson(cwd) {
|
|
|
12309
12829
|
}
|
|
12310
12830
|
function topLevelListing(cwd) {
|
|
12311
12831
|
try {
|
|
12312
|
-
const names =
|
|
12832
|
+
const names = fs31.readdirSync(cwd, { withFileTypes: true });
|
|
12313
12833
|
const sorted = [...names].sort((a, b) => a.name.localeCompare(b.name));
|
|
12314
12834
|
const limited = sorted.slice(0, LIMITS.topDirEntries);
|
|
12315
12835
|
const lines = limited.map((d) => `${d.name}${d.isDirectory() ? "/" : ""}`);
|
|
@@ -12367,7 +12887,7 @@ function buildWorkspaceSnapshot(cwd) {
|
|
|
12367
12887
|
parts.push(pkg);
|
|
12368
12888
|
parts.push("```\n");
|
|
12369
12889
|
}
|
|
12370
|
-
const py = safeReadFile(
|
|
12890
|
+
const py = safeReadFile(path33.join(cwd, "pyproject.toml"), LIMITS.pyproject);
|
|
12371
12891
|
if (py) {
|
|
12372
12892
|
parts.push("### pyproject.toml (excerpt)\n```toml");
|
|
12373
12893
|
parts.push(py);
|
|
@@ -12385,15 +12905,15 @@ function buildWorkspaceSnapshot(cwd) {
|
|
|
12385
12905
|
parts.push(bluma);
|
|
12386
12906
|
parts.push("```\n");
|
|
12387
12907
|
}
|
|
12388
|
-
const contrib = safeReadFile(
|
|
12908
|
+
const contrib = safeReadFile(path33.join(cwd, "CONTRIBUTING.md"), LIMITS.contributing);
|
|
12389
12909
|
if (contrib) {
|
|
12390
12910
|
parts.push("### CONTRIBUTING.md (excerpt)\n```markdown");
|
|
12391
12911
|
parts.push(contrib);
|
|
12392
12912
|
parts.push("```\n");
|
|
12393
12913
|
}
|
|
12394
|
-
const chlog = safeReadFile(
|
|
12914
|
+
const chlog = safeReadFile(path33.join(cwd, "CHANGELOG.md"), LIMITS.changelog);
|
|
12395
12915
|
if (!chlog) {
|
|
12396
|
-
const alt = safeReadFile(
|
|
12916
|
+
const alt = safeReadFile(path33.join(cwd, "CHANGES.md"), LIMITS.changelog);
|
|
12397
12917
|
if (alt) {
|
|
12398
12918
|
parts.push("### CHANGES.md (excerpt)\n```markdown");
|
|
12399
12919
|
parts.push(alt);
|
|
@@ -12429,14 +12949,14 @@ function buildWorkspaceSnapshot(cwd) {
|
|
|
12429
12949
|
} else {
|
|
12430
12950
|
parts.push("### Git\n(not a git work tree, or `git` unavailable)\n");
|
|
12431
12951
|
}
|
|
12432
|
-
const tsconfig = safeReadFile(
|
|
12952
|
+
const tsconfig = safeReadFile(path33.join(cwd, "tsconfig.json"), LIMITS.tsconfig);
|
|
12433
12953
|
if (tsconfig) {
|
|
12434
12954
|
parts.push("### tsconfig.json (excerpt)\n```json");
|
|
12435
12955
|
parts.push(tsconfig);
|
|
12436
12956
|
parts.push("```\n");
|
|
12437
12957
|
}
|
|
12438
12958
|
for (const name of ["Dockerfile", "dockerfile", "Dockerfile.prod", "Dockerfile.dev"]) {
|
|
12439
|
-
const df = safeReadFile(
|
|
12959
|
+
const df = safeReadFile(path33.join(cwd, name), LIMITS.dockerfile);
|
|
12440
12960
|
if (df) {
|
|
12441
12961
|
parts.push(`### ${name} (excerpt)
|
|
12442
12962
|
`);
|
|
@@ -12446,7 +12966,7 @@ function buildWorkspaceSnapshot(cwd) {
|
|
|
12446
12966
|
}
|
|
12447
12967
|
}
|
|
12448
12968
|
for (const name of ["docker-compose.yml", "docker-compose.yaml", "compose.yml", "compose.yaml"]) {
|
|
12449
|
-
const dc = safeReadFile(
|
|
12969
|
+
const dc = safeReadFile(path33.join(cwd, name), LIMITS.dockerfile);
|
|
12450
12970
|
if (dc) {
|
|
12451
12971
|
parts.push(`### ${name} (excerpt)
|
|
12452
12972
|
`);
|
|
@@ -12461,12 +12981,12 @@ function buildWorkspaceSnapshot(cwd) {
|
|
|
12461
12981
|
".circleci/config.yml",
|
|
12462
12982
|
"Jenkinsfile"
|
|
12463
12983
|
]) {
|
|
12464
|
-
const ciFile =
|
|
12465
|
-
if (
|
|
12466
|
-
const st =
|
|
12984
|
+
const ciFile = path33.join(cwd, ciPath);
|
|
12985
|
+
if (fs31.existsSync(ciFile)) {
|
|
12986
|
+
const st = fs31.statSync(ciFile);
|
|
12467
12987
|
if (st.isDirectory()) {
|
|
12468
12988
|
try {
|
|
12469
|
-
const wfFiles =
|
|
12989
|
+
const wfFiles = fs31.readdirSync(ciFile).filter((f) => f.endsWith(".yml") || f.endsWith(".yaml"));
|
|
12470
12990
|
if (wfFiles.length > 0) {
|
|
12471
12991
|
parts.push(`### GitHub Actions workflows
|
|
12472
12992
|
\`${wfFiles.join("`, `")}\`
|
|
@@ -12477,7 +12997,7 @@ function buildWorkspaceSnapshot(cwd) {
|
|
|
12477
12997
|
} else {
|
|
12478
12998
|
const ci = safeReadFile(ciFile, LIMITS.ciConfig);
|
|
12479
12999
|
if (ci) {
|
|
12480
|
-
parts.push(`### CI config (${
|
|
13000
|
+
parts.push(`### CI config (${path33.basename(ciPath)})
|
|
12481
13001
|
`);
|
|
12482
13002
|
parts.push(ci);
|
|
12483
13003
|
parts.push("\n");
|
|
@@ -12486,8 +13006,8 @@ function buildWorkspaceSnapshot(cwd) {
|
|
|
12486
13006
|
}
|
|
12487
13007
|
}
|
|
12488
13008
|
for (const depFile of ["requirements.txt", "Pipfile", "poetry.lock", "Gemfile", "go.sum", "Cargo.lock"]) {
|
|
12489
|
-
const depPath =
|
|
12490
|
-
if (
|
|
13009
|
+
const depPath = path33.join(cwd, depFile);
|
|
13010
|
+
if (fs31.existsSync(depPath)) {
|
|
12491
13011
|
const depContent = safeReadFile(depPath, 1500);
|
|
12492
13012
|
if (depContent) {
|
|
12493
13013
|
parts.push(`### ${depFile} (top entries)
|
|
@@ -12500,11 +13020,14 @@ function buildWorkspaceSnapshot(cwd) {
|
|
|
12500
13020
|
}
|
|
12501
13021
|
}
|
|
12502
13022
|
for (const envFile of [".env.example", ".env.sample", ".env.template"]) {
|
|
12503
|
-
const envPath =
|
|
12504
|
-
if (
|
|
13023
|
+
const envPath = path33.join(cwd, envFile);
|
|
13024
|
+
if (fs31.existsSync(envPath)) {
|
|
12505
13025
|
try {
|
|
12506
|
-
const raw =
|
|
12507
|
-
const keys = raw.split("\n").map((l) => l.trim()).filter((l) => l && !l.startsWith("#")).map((l) =>
|
|
13026
|
+
const raw = fs31.readFileSync(envPath, "utf-8");
|
|
13027
|
+
const keys = raw.split("\n").map((l) => l.trim()).filter((l) => l && !l.startsWith("#")).map((l) => {
|
|
13028
|
+
const eqIndex = l.indexOf("=");
|
|
13029
|
+
return eqIndex >= 0 ? l.slice(0, eqIndex).trim() : l.trim();
|
|
13030
|
+
}).filter((key) => /^[A-Z_][A-Z0-9_]*$/.test(key)).filter(Boolean);
|
|
12508
13031
|
if (keys.length > 0) {
|
|
12509
13032
|
parts.push(`### Environment variables (from ${envFile})
|
|
12510
13033
|
\`${keys.slice(0, 30).join("`, `")}\`
|
|
@@ -12523,15 +13046,15 @@ init_runtime_config();
|
|
|
12523
13046
|
|
|
12524
13047
|
// src/app/agent/runtime/plugin_registry.ts
|
|
12525
13048
|
init_sandbox_policy();
|
|
12526
|
-
import
|
|
13049
|
+
import fs32 from "fs";
|
|
12527
13050
|
import os20 from "os";
|
|
12528
|
-
import
|
|
13051
|
+
import path34 from "path";
|
|
12529
13052
|
function getProjectPluginsDir() {
|
|
12530
13053
|
const policy = getSandboxPolicy();
|
|
12531
|
-
return
|
|
13054
|
+
return path34.join(policy.workspaceRoot, ".bluma", "plugins");
|
|
12532
13055
|
}
|
|
12533
13056
|
function getGlobalPluginsDir() {
|
|
12534
|
-
return
|
|
13057
|
+
return path34.join(process.env.HOME || os20.homedir(), ".bluma", "plugins");
|
|
12535
13058
|
}
|
|
12536
13059
|
function getPluginDirs() {
|
|
12537
13060
|
return {
|
|
@@ -12540,11 +13063,11 @@ function getPluginDirs() {
|
|
|
12540
13063
|
};
|
|
12541
13064
|
}
|
|
12542
13065
|
function readManifest(manifestPath, fallbackName) {
|
|
12543
|
-
if (!
|
|
13066
|
+
if (!fs32.existsSync(manifestPath)) {
|
|
12544
13067
|
return null;
|
|
12545
13068
|
}
|
|
12546
13069
|
try {
|
|
12547
|
-
const parsed = JSON.parse(
|
|
13070
|
+
const parsed = JSON.parse(fs32.readFileSync(manifestPath, "utf-8"));
|
|
12548
13071
|
return {
|
|
12549
13072
|
name: typeof parsed.name === "string" && parsed.name.trim() ? parsed.name.trim() : fallbackName,
|
|
12550
13073
|
description: typeof parsed.description === "string" ? parsed.description.trim() : void 0,
|
|
@@ -12557,22 +13080,22 @@ function readManifest(manifestPath, fallbackName) {
|
|
|
12557
13080
|
}
|
|
12558
13081
|
function findManifestPath(pluginDir) {
|
|
12559
13082
|
const candidates = [
|
|
12560
|
-
|
|
12561
|
-
|
|
13083
|
+
path34.join(pluginDir, ".codex-plugin", "plugin.json"),
|
|
13084
|
+
path34.join(pluginDir, "plugin.json")
|
|
12562
13085
|
];
|
|
12563
13086
|
for (const candidate of candidates) {
|
|
12564
|
-
if (
|
|
13087
|
+
if (fs32.existsSync(candidate)) {
|
|
12565
13088
|
return candidate;
|
|
12566
13089
|
}
|
|
12567
13090
|
}
|
|
12568
13091
|
return null;
|
|
12569
13092
|
}
|
|
12570
13093
|
function listFromDir(baseDir, source) {
|
|
12571
|
-
if (!
|
|
13094
|
+
if (!fs32.existsSync(baseDir)) {
|
|
12572
13095
|
return [];
|
|
12573
13096
|
}
|
|
12574
|
-
return
|
|
12575
|
-
const pluginDir =
|
|
13097
|
+
return fs32.readdirSync(baseDir, { withFileTypes: true }).filter((entry) => entry.isDirectory()).flatMap((entry) => {
|
|
13098
|
+
const pluginDir = path34.join(baseDir, entry.name);
|
|
12576
13099
|
const manifestPath = findManifestPath(pluginDir);
|
|
12577
13100
|
if (!manifestPath) {
|
|
12578
13101
|
return [];
|
|
@@ -13015,13 +13538,23 @@ Since you are in an **isolated sandbox**, ALL tools are auto-approved:
|
|
|
13015
13538
|
- \\\`notebook_edit\\\` - Jupyter notebook editing (auto-approved)
|
|
13016
13539
|
- \\\`cron_create\\\` - Schedule reminders (auto-approved)
|
|
13017
13540
|
|
|
13541
|
+
### FactorAI Workspace Tools
|
|
13542
|
+
When a deployed app is attached to this sandbox, the following tools may appear and are sandbox-only:
|
|
13543
|
+
- \\\`factorai.sh.create_next_app\\\` - Create a new Next.js project in the sandbox
|
|
13544
|
+
- \\\`factorai.sh.deploy_app\\\` - Deploy a Next.js project from the sandbox to the hosting backend
|
|
13545
|
+
- \\\`factorai.sh.get_app_status\\\` - Read app status and contract context
|
|
13546
|
+
- \\\`factorai.sh.apply_app_changes\\\` - Apply incremental file changes to the app workspace
|
|
13547
|
+
- \\\`factorai.sh.redeploy_app\\\` - Redeploy the current app revision after edits
|
|
13548
|
+
|
|
13549
|
+
Use \\\`factorai.sh.json\\\` as a normal workspace file. Do not treat manifest reading as a tool call.
|
|
13550
|
+
|
|
13018
13551
|
---
|
|
13019
13552
|
|
|
13020
13553
|
## \u{1F680} NEXT.JS DEPLOY WORKFLOW - SEVERINO INTEGRATION
|
|
13021
13554
|
|
|
13022
13555
|
You have access to **two specialized tools** for creating and deploying Next.js apps to Severino:
|
|
13023
13556
|
|
|
13024
|
-
### Tool 1: \\\`create_next_app\\\`
|
|
13557
|
+
### Tool 1: \\\`factorai.sh.create_next_app\\\`
|
|
13025
13558
|
|
|
13026
13559
|
**Purpose:** Instant scaffold of a complete Next.js project with App Router, shadcn/ui, and Tailwind CSS.
|
|
13027
13560
|
|
|
@@ -13032,7 +13565,7 @@ You have access to **two specialized tools** for creating and deploying Next.js
|
|
|
13032
13565
|
|
|
13033
13566
|
**Example:**
|
|
13034
13567
|
\\\`\\\`\\\`typescript
|
|
13035
|
-
const result = await
|
|
13568
|
+
const result = await factorai.sh.create_next_app({
|
|
13036
13569
|
name: 'erp-dashboard',
|
|
13037
13570
|
template: 'full',
|
|
13038
13571
|
});
|
|
@@ -13053,7 +13586,7 @@ const result = await createNextApp({
|
|
|
13053
13586
|
|
|
13054
13587
|
---
|
|
13055
13588
|
|
|
13056
|
-
### Tool 2: \\\`deploy_app\\\`
|
|
13589
|
+
### Tool 2: \\\`factorai.sh.deploy_app\\\`
|
|
13057
13590
|
|
|
13058
13591
|
**Purpose:** Zip and deploy a Next.js project to Severino for hosting.
|
|
13059
13592
|
|
|
@@ -13065,7 +13598,7 @@ const result = await createNextApp({
|
|
|
13065
13598
|
|
|
13066
13599
|
**Example:**
|
|
13067
13600
|
\\\`\\\`\\\`typescript
|
|
13068
|
-
const result = await
|
|
13601
|
+
const result = await factorai.sh.deploy_app({
|
|
13069
13602
|
projectDir: './erp-dashboard',
|
|
13070
13603
|
name: 'erp-dashboard',
|
|
13071
13604
|
});
|
|
@@ -13086,10 +13619,17 @@ const result = await deployApp({
|
|
|
13086
13619
|
- O ZIP deve conter **apenas c\xF3digo fonte e configs**
|
|
13087
13620
|
- \\\`node_modules\\\` \xE9 recriado no servidor com \\\`npm install\\\`
|
|
13088
13621
|
- Incluir \\\`node_modules\\\` = ZIP de 200MB+ = deploy falha
|
|
13089
|
-
- A tool \\\`deploy_app\\\` j\xE1 exclui automaticamente, mas se fizeres ZIP manual: **exclui \\\`node_modules\\\`**
|
|
13622
|
+
- A tool \\\`factorai.sh.deploy_app\\\` j\xE1 exclui automaticamente, mas se fizeres ZIP manual: **exclui \\\`node_modules\\\`**
|
|
13090
13623
|
|
|
13091
13624
|
**Important:** The deploy is **asynchronous** - status starts as \\\`'building'\\\`. The app will be ready in ~30-60 seconds.
|
|
13092
13625
|
|
|
13626
|
+
**After this tool returns**
|
|
13627
|
+
- Read the returned metadata immediately.
|
|
13628
|
+
- Create or update \\\`factorai.sh.json\\\` in the project root.
|
|
13629
|
+
- Persist the deployment context there: \\\`appId\\\`, \\\`tenantId\\\`, \\\`projectId\\\`, \\\`workspaceId\\\`, \\\`revisionId\\\`, \\\`deploymentId\\\`, \\\`buildJobId\\\`, and \\\`appUrl\\\` when available.
|
|
13630
|
+
- Treat \\\`factorai.sh.json\\\` as the source of truth for future incremental edits and redeploys.
|
|
13631
|
+
- Do not invent a second manifest file or duplicate state elsewhere.
|
|
13632
|
+
|
|
13093
13633
|
---
|
|
13094
13634
|
|
|
13095
13635
|
### Complete Workflow Example
|
|
@@ -13097,7 +13637,7 @@ const result = await deployApp({
|
|
|
13097
13637
|
\\\`\\\`\\\`typescript
|
|
13098
13638
|
// Step 1: Create project
|
|
13099
13639
|
message({ message_type: 'info', content: 'Step 1/3: Creating Next.js project...' });
|
|
13100
|
-
const scaffold = await
|
|
13640
|
+
const scaffold = await factorai.sh.create_next_app({
|
|
13101
13641
|
name: 'erp-dashboard',
|
|
13102
13642
|
template: 'full',
|
|
13103
13643
|
});
|
|
@@ -13111,7 +13651,7 @@ await fileWrite({
|
|
|
13111
13651
|
|
|
13112
13652
|
// Step 3: Deploy
|
|
13113
13653
|
message({ message_type: 'info', content: 'Step 3/3: Deploying to Severino...' });
|
|
13114
|
-
const deploy = await
|
|
13654
|
+
const deploy = await factorai.sh.deploy_app({
|
|
13115
13655
|
projectDir: './erp-dashboard',
|
|
13116
13656
|
name: 'erp-dashboard',
|
|
13117
13657
|
});
|
|
@@ -13129,7 +13669,7 @@ message({
|
|
|
13129
13669
|
### \u26A0\uFE0F Critical Requirements for Deploy
|
|
13130
13670
|
|
|
13131
13671
|
1. **\\\`output: 'standalone'\\\` in \\\`next.config.js\\\`**
|
|
13132
|
-
- The \\\`create_next_app\\\` tool already includes this
|
|
13672
|
+
- The \\\`factorai.sh.create_next_app\\\` tool already includes this
|
|
13133
13673
|
- If you modify \\\`next.config.js\\\`, keep this setting
|
|
13134
13674
|
|
|
13135
13675
|
2. **Build before deploy (optional but recommended)**
|
|
@@ -13467,8 +14007,8 @@ function buildModelInfoSection(modelId) {
|
|
|
13467
14007
|
|
|
13468
14008
|
// src/app/agent/runtime/hook_registry.ts
|
|
13469
14009
|
init_sandbox_policy();
|
|
13470
|
-
import
|
|
13471
|
-
import
|
|
14010
|
+
import fs33 from "fs";
|
|
14011
|
+
import path35 from "path";
|
|
13472
14012
|
var DEFAULT_STATE = {
|
|
13473
14013
|
enabled: true,
|
|
13474
14014
|
maxEvents: 120,
|
|
@@ -13479,7 +14019,7 @@ var cache2 = null;
|
|
|
13479
14019
|
var cachePath2 = null;
|
|
13480
14020
|
function getStatePath() {
|
|
13481
14021
|
const policy = getSandboxPolicy();
|
|
13482
|
-
return
|
|
14022
|
+
return path35.join(policy.workspaceRoot, ".bluma", "hooks.json");
|
|
13483
14023
|
}
|
|
13484
14024
|
function getHookStatePath() {
|
|
13485
14025
|
return getStatePath();
|
|
@@ -13498,8 +14038,8 @@ function ensureLoaded2() {
|
|
|
13498
14038
|
return cache2;
|
|
13499
14039
|
}
|
|
13500
14040
|
try {
|
|
13501
|
-
if (
|
|
13502
|
-
const parsed = JSON.parse(
|
|
14041
|
+
if (fs33.existsSync(statePath)) {
|
|
14042
|
+
const parsed = JSON.parse(fs33.readFileSync(statePath, "utf-8"));
|
|
13503
14043
|
cache2 = {
|
|
13504
14044
|
enabled: typeof parsed.enabled === "boolean" ? parsed.enabled : DEFAULT_STATE.enabled,
|
|
13505
14045
|
maxEvents: typeof parsed.maxEvents === "number" && Number.isFinite(parsed.maxEvents) && parsed.maxEvents > 0 ? Math.floor(parsed.maxEvents) : DEFAULT_STATE.maxEvents,
|
|
@@ -13525,9 +14065,9 @@ function ensureLoaded2() {
|
|
|
13525
14065
|
}
|
|
13526
14066
|
function persist2(state) {
|
|
13527
14067
|
const statePath = getStatePath();
|
|
13528
|
-
|
|
14068
|
+
fs33.mkdirSync(path35.dirname(statePath), { recursive: true });
|
|
13529
14069
|
state.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
13530
|
-
|
|
14070
|
+
fs33.writeFileSync(statePath, JSON.stringify(state, null, 2), "utf-8");
|
|
13531
14071
|
cache2 = state;
|
|
13532
14072
|
cachePath2 = statePath;
|
|
13533
14073
|
}
|
|
@@ -13596,18 +14136,18 @@ function buildSystemRemindersSection() {
|
|
|
13596
14136
|
}
|
|
13597
14137
|
|
|
13598
14138
|
// src/app/agent/core/prompt/auto_memory.ts
|
|
13599
|
-
import
|
|
13600
|
-
import
|
|
14139
|
+
import fs34 from "fs";
|
|
14140
|
+
import path36 from "path";
|
|
13601
14141
|
import os21 from "os";
|
|
13602
|
-
var AUTO_MEMORY_FILE =
|
|
14142
|
+
var AUTO_MEMORY_FILE = path36.join(os21.homedir(), ".bluma", "auto_memory.md");
|
|
13603
14143
|
var MAX_AUTO_MEMORY_CHARS = 25e3;
|
|
13604
14144
|
var MAX_AUTO_MEMORY_LINES = 200;
|
|
13605
14145
|
function getAutoMemoryForPrompt() {
|
|
13606
14146
|
try {
|
|
13607
|
-
if (!
|
|
14147
|
+
if (!fs34.existsSync(AUTO_MEMORY_FILE)) {
|
|
13608
14148
|
return "";
|
|
13609
14149
|
}
|
|
13610
|
-
let content =
|
|
14150
|
+
let content = fs34.readFileSync(AUTO_MEMORY_FILE, "utf-8");
|
|
13611
14151
|
if (!content.trim()) {
|
|
13612
14152
|
return "";
|
|
13613
14153
|
}
|
|
@@ -13666,10 +14206,10 @@ function getGitBranch(dir) {
|
|
|
13666
14206
|
}
|
|
13667
14207
|
function getPackageManager(dir) {
|
|
13668
14208
|
try {
|
|
13669
|
-
if (
|
|
13670
|
-
if (
|
|
13671
|
-
if (
|
|
13672
|
-
if (
|
|
14209
|
+
if (fs35.existsSync(path37.join(dir, "pnpm-lock.yaml"))) return "pnpm";
|
|
14210
|
+
if (fs35.existsSync(path37.join(dir, "yarn.lock"))) return "yarn";
|
|
14211
|
+
if (fs35.existsSync(path37.join(dir, "bun.lockb"))) return "bun";
|
|
14212
|
+
if (fs35.existsSync(path37.join(dir, "package-lock.json"))) return "npm";
|
|
13673
14213
|
return "unknown";
|
|
13674
14214
|
} catch {
|
|
13675
14215
|
return "unknown";
|
|
@@ -13677,9 +14217,9 @@ function getPackageManager(dir) {
|
|
|
13677
14217
|
}
|
|
13678
14218
|
function getProjectType(dir) {
|
|
13679
14219
|
try {
|
|
13680
|
-
const files =
|
|
14220
|
+
const files = fs35.readdirSync(dir);
|
|
13681
14221
|
if (files.includes("package.json")) {
|
|
13682
|
-
const pkg = JSON.parse(
|
|
14222
|
+
const pkg = JSON.parse(fs35.readFileSync(path37.join(dir, "package.json"), "utf-8"));
|
|
13683
14223
|
if (pkg.dependencies?.next || pkg.devDependencies?.next) return "Next.js";
|
|
13684
14224
|
if (pkg.dependencies?.react || pkg.devDependencies?.react) return "React";
|
|
13685
14225
|
if (pkg.dependencies?.express || pkg.devDependencies?.express) return "Express";
|
|
@@ -13698,9 +14238,9 @@ function getProjectType(dir) {
|
|
|
13698
14238
|
}
|
|
13699
14239
|
function getTestFramework(dir) {
|
|
13700
14240
|
try {
|
|
13701
|
-
const pkgPath =
|
|
13702
|
-
if (
|
|
13703
|
-
const pkg = JSON.parse(
|
|
14241
|
+
const pkgPath = path37.join(dir, "package.json");
|
|
14242
|
+
if (fs35.existsSync(pkgPath)) {
|
|
14243
|
+
const pkg = JSON.parse(fs35.readFileSync(pkgPath, "utf-8"));
|
|
13704
14244
|
const deps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
13705
14245
|
if (deps.jest) return "jest";
|
|
13706
14246
|
if (deps.vitest) return "vitest";
|
|
@@ -13709,7 +14249,7 @@ function getTestFramework(dir) {
|
|
|
13709
14249
|
if (deps["@playwright/test"]) return "playwright";
|
|
13710
14250
|
if (deps.cypress) return "cypress";
|
|
13711
14251
|
}
|
|
13712
|
-
if (
|
|
14252
|
+
if (fs35.existsSync(path37.join(dir, "pytest.ini")) || fs35.existsSync(path37.join(dir, "conftest.py"))) return "pytest";
|
|
13713
14253
|
return "unknown";
|
|
13714
14254
|
} catch {
|
|
13715
14255
|
return "unknown";
|
|
@@ -13717,9 +14257,9 @@ function getTestFramework(dir) {
|
|
|
13717
14257
|
}
|
|
13718
14258
|
function getTestCommand(dir) {
|
|
13719
14259
|
try {
|
|
13720
|
-
const pkgPath =
|
|
13721
|
-
if (
|
|
13722
|
-
const pkg = JSON.parse(
|
|
14260
|
+
const pkgPath = path37.join(dir, "package.json");
|
|
14261
|
+
if (fs35.existsSync(pkgPath)) {
|
|
14262
|
+
const pkg = JSON.parse(fs35.readFileSync(pkgPath, "utf-8"));
|
|
13723
14263
|
if (pkg.scripts?.test) return `npm test`;
|
|
13724
14264
|
if (pkg.scripts?.["test:unit"]) return `npm run test:unit`;
|
|
13725
14265
|
}
|
|
@@ -13911,7 +14451,15 @@ async function getUnifiedSystemPrompt(availableSkills, options) {
|
|
|
13911
14451
|
const fromAgent = process.env.BLUMA_FROM_AGENT || "severino";
|
|
13912
14452
|
const action = process.env.BLUMA_ACTION || "unknown";
|
|
13913
14453
|
const sessionId = process.env.BLUMA_SESSION_ID || "unknown";
|
|
14454
|
+
const factorAiAppContext = loadFactorAiAppContext();
|
|
13914
14455
|
prompt = prompt.replaceAll("{from_agent}", fromAgent).replaceAll("{action}", action).replaceAll("{session_id}", sessionId).replaceAll("{workspace_root}", env.workdir);
|
|
14456
|
+
if (factorAiAppContext) {
|
|
14457
|
+
prompt += `
|
|
14458
|
+
|
|
14459
|
+
<factorai_app_context>
|
|
14460
|
+
${formatFactorAiAppContextSummary(factorAiAppContext)}
|
|
14461
|
+
</factorai_app_context>`;
|
|
14462
|
+
}
|
|
13915
14463
|
}
|
|
13916
14464
|
prompt += buildOutputStylePrompt(runtimeConfig.outputStyle);
|
|
13917
14465
|
prompt += buildPermissionModePrompt(runtimeConfig.permissionMode);
|
|
@@ -14011,8 +14559,8 @@ ${blumaMdContent}
|
|
|
14011
14559
|
}
|
|
14012
14560
|
function isGitRepo(dir) {
|
|
14013
14561
|
try {
|
|
14014
|
-
const gitPath =
|
|
14015
|
-
return
|
|
14562
|
+
const gitPath = path37.join(dir, ".git");
|
|
14563
|
+
return fs35.existsSync(gitPath) && fs35.lstatSync(gitPath).isDirectory();
|
|
14016
14564
|
} catch {
|
|
14017
14565
|
return false;
|
|
14018
14566
|
}
|
|
@@ -14220,7 +14768,8 @@ function defaultBlumaUserContextInput(sessionId, userMessage) {
|
|
|
14220
14768
|
userName: null,
|
|
14221
14769
|
userEmail: null,
|
|
14222
14770
|
companyId: null,
|
|
14223
|
-
companyName: null
|
|
14771
|
+
companyName: null,
|
|
14772
|
+
appContext: loadFactorAiAppContext()
|
|
14224
14773
|
};
|
|
14225
14774
|
}
|
|
14226
14775
|
function getPreferredMacAddress() {
|
|
@@ -14758,11 +15307,11 @@ function effectiveToolAutoApprove(toolCall, sessionId, options) {
|
|
|
14758
15307
|
}
|
|
14759
15308
|
|
|
14760
15309
|
// src/app/agent/tools/natives/coding_memory_consolidate.ts
|
|
14761
|
-
import * as
|
|
14762
|
-
import * as
|
|
15310
|
+
import * as fs36 from "fs";
|
|
15311
|
+
import * as path38 from "path";
|
|
14763
15312
|
import os24 from "os";
|
|
14764
15313
|
function memoryPath2() {
|
|
14765
|
-
return
|
|
15314
|
+
return path38.join(process.env.HOME || os24.homedir(), ".bluma", "coding_memory.json");
|
|
14766
15315
|
}
|
|
14767
15316
|
function normalizeNote2(note) {
|
|
14768
15317
|
return note.trim().toLowerCase().replace(/\s+/g, " ");
|
|
@@ -14772,18 +15321,18 @@ function uniqTags(a, b) {
|
|
|
14772
15321
|
}
|
|
14773
15322
|
function consolidateCodingMemoryFile() {
|
|
14774
15323
|
const p = memoryPath2();
|
|
14775
|
-
if (!
|
|
15324
|
+
if (!fs36.existsSync(p)) {
|
|
14776
15325
|
return { success: true, removedDuplicates: 0, message: "no coding_memory.json" };
|
|
14777
15326
|
}
|
|
14778
15327
|
const bak = `${p}.bak`;
|
|
14779
15328
|
try {
|
|
14780
|
-
|
|
15329
|
+
fs36.copyFileSync(p, bak);
|
|
14781
15330
|
} catch (e) {
|
|
14782
15331
|
return { success: false, removedDuplicates: 0, message: `backup failed: ${e.message}` };
|
|
14783
15332
|
}
|
|
14784
15333
|
let data;
|
|
14785
15334
|
try {
|
|
14786
|
-
data = JSON.parse(
|
|
15335
|
+
data = JSON.parse(fs36.readFileSync(p, "utf-8"));
|
|
14787
15336
|
} catch (e) {
|
|
14788
15337
|
return { success: false, removedDuplicates: 0, message: `invalid json: ${e.message}` };
|
|
14789
15338
|
}
|
|
@@ -14818,7 +15367,7 @@ function consolidateCodingMemoryFile() {
|
|
|
14818
15367
|
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
14819
15368
|
};
|
|
14820
15369
|
try {
|
|
14821
|
-
|
|
15370
|
+
fs36.writeFileSync(p, JSON.stringify(out, null, 2), "utf-8");
|
|
14822
15371
|
} catch (e) {
|
|
14823
15372
|
return { success: false, removedDuplicates: 0, message: `write failed: ${e.message}` };
|
|
14824
15373
|
}
|
|
@@ -14861,7 +15410,8 @@ function buildTurnStartBackendMessage(params) {
|
|
|
14861
15410
|
type: "turn_start",
|
|
14862
15411
|
turnId: params.turnId,
|
|
14863
15412
|
sessionId: params.sessionId,
|
|
14864
|
-
userPromptPreview: buildUserPromptPreview(params.inputText)
|
|
15413
|
+
userPromptPreview: buildUserPromptPreview(params.inputText),
|
|
15414
|
+
appContext: params.appContext ?? null
|
|
14865
15415
|
};
|
|
14866
15416
|
}
|
|
14867
15417
|
|
|
@@ -15043,7 +15593,8 @@ var BluMaAgent = class {
|
|
|
15043
15593
|
buildTurnStartBackendMessage({
|
|
15044
15594
|
turnId: this.activeTurnContext.turnId,
|
|
15045
15595
|
sessionId: this.activeTurnContext.sessionId,
|
|
15046
|
-
inputText
|
|
15596
|
+
inputText,
|
|
15597
|
+
appContext: this.activeTurnContext.appContext ?? null
|
|
15047
15598
|
})
|
|
15048
15599
|
);
|
|
15049
15600
|
if (inputText === "/init") {
|
|
@@ -15423,7 +15974,7 @@ var BluMaAgent = class {
|
|
|
15423
15974
|
|
|
15424
15975
|
${editData.error.display}`;
|
|
15425
15976
|
}
|
|
15426
|
-
const filename =
|
|
15977
|
+
const filename = path39.basename(toolArgs.file_path);
|
|
15427
15978
|
return createDiff(filename, editData.currentContent || "", editData.newContent);
|
|
15428
15979
|
} catch (e) {
|
|
15429
15980
|
return `An unexpected error occurred while generating the edit preview: ${e.message}`;
|
|
@@ -16399,16 +16950,16 @@ async function fullCompact(messages, targetTokens, summarizer, llmClient) {
|
|
|
16399
16950
|
}
|
|
16400
16951
|
|
|
16401
16952
|
// src/app/agent/core/memory/session_memory.ts
|
|
16402
|
-
import
|
|
16953
|
+
import fs37 from "fs";
|
|
16403
16954
|
import os27 from "os";
|
|
16404
|
-
import
|
|
16955
|
+
import path40 from "path";
|
|
16405
16956
|
import { v4 as uuidv49 } from "uuid";
|
|
16406
16957
|
var SessionMemoryExtractor = class {
|
|
16407
16958
|
llmClient;
|
|
16408
16959
|
memoryFile;
|
|
16409
16960
|
constructor(options = {}) {
|
|
16410
16961
|
this.llmClient = options.llmClient;
|
|
16411
|
-
this.memoryFile = options.memoryFile ||
|
|
16962
|
+
this.memoryFile = options.memoryFile || path40.join(os27.homedir(), ".bluma", "session_memory.json");
|
|
16412
16963
|
}
|
|
16413
16964
|
/**
|
|
16414
16965
|
* Extract memories from conversation using LLM
|
|
@@ -16465,15 +17016,15 @@ ${messages.slice(-50).map((m) => `${m.role}: ${m.content.slice(0, 500)}`).join("
|
|
|
16465
17016
|
);
|
|
16466
17017
|
unique.sort((a, b) => b.accessCount - a.accessCount);
|
|
16467
17018
|
const trimmed = unique.slice(0, 200);
|
|
16468
|
-
|
|
17019
|
+
fs37.writeFileSync(this.memoryFile, JSON.stringify(trimmed, null, 2));
|
|
16469
17020
|
}
|
|
16470
17021
|
/**
|
|
16471
17022
|
* Load memories from disk
|
|
16472
17023
|
*/
|
|
16473
17024
|
async loadMemories() {
|
|
16474
17025
|
try {
|
|
16475
|
-
if (!
|
|
16476
|
-
const data =
|
|
17026
|
+
if (!fs37.existsSync(this.memoryFile)) return [];
|
|
17027
|
+
const data = fs37.readFileSync(this.memoryFile, "utf-8");
|
|
16477
17028
|
return JSON.parse(data);
|
|
16478
17029
|
} catch {
|
|
16479
17030
|
return [];
|
|
@@ -17010,14 +17561,14 @@ var RouteManager = class {
|
|
|
17010
17561
|
this.subAgents = subAgents;
|
|
17011
17562
|
this.core = core;
|
|
17012
17563
|
}
|
|
17013
|
-
registerRoute(
|
|
17014
|
-
this.routeHandlers.set(
|
|
17564
|
+
registerRoute(path45, handler) {
|
|
17565
|
+
this.routeHandlers.set(path45, handler);
|
|
17015
17566
|
}
|
|
17016
17567
|
async handleRoute(payload) {
|
|
17017
17568
|
const inputText = String(payload.content || "").trim();
|
|
17018
17569
|
const { userContext } = payload;
|
|
17019
|
-
for (const [
|
|
17020
|
-
if (inputText ===
|
|
17570
|
+
for (const [path45, handler] of this.routeHandlers) {
|
|
17571
|
+
if (inputText === path45 || inputText.startsWith(`${path45} `)) {
|
|
17021
17572
|
return handler({ content: inputText, userContext });
|
|
17022
17573
|
}
|
|
17023
17574
|
}
|
|
@@ -17026,13 +17577,13 @@ var RouteManager = class {
|
|
|
17026
17577
|
};
|
|
17027
17578
|
|
|
17028
17579
|
// src/app/agent/runtime/plugin_runtime.ts
|
|
17029
|
-
import
|
|
17580
|
+
import path41 from "path";
|
|
17030
17581
|
import { pathToFileURL as pathToFileURL2 } from "url";
|
|
17031
17582
|
async function loadPluginsAtStartup() {
|
|
17032
17583
|
for (const p of listPlugins()) {
|
|
17033
17584
|
const entry = p.manifest.entry?.trim();
|
|
17034
17585
|
if (!entry) continue;
|
|
17035
|
-
const abs =
|
|
17586
|
+
const abs = path41.resolve(p.root, entry);
|
|
17036
17587
|
try {
|
|
17037
17588
|
const href = pathToFileURL2(abs).href;
|
|
17038
17589
|
const mod = await import(href);
|
|
@@ -17053,7 +17604,7 @@ async function loadPluginsAtStartup() {
|
|
|
17053
17604
|
}
|
|
17054
17605
|
|
|
17055
17606
|
// src/app/agent/agent.ts
|
|
17056
|
-
var globalEnvPath =
|
|
17607
|
+
var globalEnvPath = path42.join(os28.homedir(), ".bluma", ".env");
|
|
17057
17608
|
dotenv.config({ path: globalEnvPath });
|
|
17058
17609
|
var Agent = class {
|
|
17059
17610
|
sessionId;
|
|
@@ -17065,9 +17616,11 @@ var Agent = class {
|
|
|
17065
17616
|
core;
|
|
17066
17617
|
subAgents;
|
|
17067
17618
|
toolInvoker;
|
|
17619
|
+
factorAiAppContext;
|
|
17068
17620
|
constructor(sessionId, eventBus) {
|
|
17069
17621
|
this.sessionId = sessionId;
|
|
17070
17622
|
this.eventBus = eventBus;
|
|
17623
|
+
this.factorAiAppContext = loadFactorAiAppContext();
|
|
17071
17624
|
const nativeToolInvoker = new ToolInvoker();
|
|
17072
17625
|
this.toolInvoker = nativeToolInvoker;
|
|
17073
17626
|
this.mcpClient = new MCPClient(nativeToolInvoker, eventBus);
|
|
@@ -17183,6 +17736,9 @@ var Agent = class {
|
|
|
17183
17736
|
async processTurn(userInput, userContextInput) {
|
|
17184
17737
|
const inputText = String(userInput.content || "").trim();
|
|
17185
17738
|
const resolvedUserContext = userContextInput ?? defaultInteractiveCliUserContextInput(this.sessionId, inputText.slice(0, 300));
|
|
17739
|
+
if (this.factorAiAppContext && !resolvedUserContext.appContext) {
|
|
17740
|
+
resolvedUserContext.appContext = this.factorAiAppContext;
|
|
17741
|
+
}
|
|
17186
17742
|
if (inputText === "/init" || inputText.startsWith("/init ")) {
|
|
17187
17743
|
this.routeManager.registerRoute("/init", this.dispatchToSubAgent);
|
|
17188
17744
|
}
|
|
@@ -18574,6 +19130,7 @@ function buildDiagnosticsSnapshot(feedbackScore) {
|
|
|
18574
19130
|
// src/app/ui/components/SlashCommands.tsx
|
|
18575
19131
|
init_runtime_config();
|
|
18576
19132
|
import { Fragment as Fragment6, jsx as jsx18, jsxs as jsxs16 } from "react/jsx-runtime";
|
|
19133
|
+
var COMMAND_HEADER_COLOR = BLUMA_TERMINAL.accent;
|
|
18577
19134
|
var RECOMMENDED_MODELS = [
|
|
18578
19135
|
"auto",
|
|
18579
19136
|
"auto"
|
|
@@ -18592,7 +19149,7 @@ var SessionLivePanel = ({ sessionId, mode }) => {
|
|
|
18592
19149
|
}, [sessionId]);
|
|
18593
19150
|
if (!session) {
|
|
18594
19151
|
return /* @__PURE__ */ jsx18(ChatBlock, { marginBottom: 1, children: /* @__PURE__ */ jsxs16(Box17, { paddingLeft: 1, flexDirection: "column", children: [
|
|
18595
|
-
/* @__PURE__ */ jsx18(Text16, { bold: true, color:
|
|
19152
|
+
/* @__PURE__ */ jsx18(Text16, { bold: true, color: COMMAND_HEADER_COLOR, children: mode }),
|
|
18596
19153
|
/* @__PURE__ */ jsxs16(Text16, { dimColor: true, children: [
|
|
18597
19154
|
"Unknown session: ",
|
|
18598
19155
|
sessionId
|
|
@@ -18603,7 +19160,7 @@ var SessionLivePanel = ({ sessionId, mode }) => {
|
|
|
18603
19160
|
const recent = logs.slice(-16);
|
|
18604
19161
|
return /* @__PURE__ */ jsx18(ChatBlock, { marginBottom: 1, children: /* @__PURE__ */ jsxs16(Box17, { paddingLeft: 1, flexDirection: "column", children: [
|
|
18605
19162
|
/* @__PURE__ */ jsxs16(Box17, { marginBottom: 1, children: [
|
|
18606
|
-
/* @__PURE__ */ jsx18(Text16, { bold: true, color:
|
|
19163
|
+
/* @__PURE__ */ jsx18(Text16, { bold: true, color: COMMAND_HEADER_COLOR, children: mode }),
|
|
18607
19164
|
/* @__PURE__ */ jsxs16(Text16, { dimColor: true, children: [
|
|
18608
19165
|
" \xB7 ",
|
|
18609
19166
|
session.sessionId.slice(0, 8)
|
|
@@ -18637,7 +19194,7 @@ var SlashCommands = ({
|
|
|
18637
19194
|
const outBox = (children) => /* @__PURE__ */ jsx18(ChatBlock, { marginBottom: 1, children: /* @__PURE__ */ jsx18(Box17, { paddingLeft: 1, flexDirection: "column", children }) });
|
|
18638
19195
|
const usageBox = (title, body) => outBox(
|
|
18639
19196
|
/* @__PURE__ */ jsxs16(Fragment6, { children: [
|
|
18640
|
-
/* @__PURE__ */ jsx18(Text16, { bold: true, color:
|
|
19197
|
+
/* @__PURE__ */ jsx18(Text16, { bold: true, color: COMMAND_HEADER_COLOR, children: title }),
|
|
18641
19198
|
/* @__PURE__ */ jsx18(Text16, { dimColor: true, children: body })
|
|
18642
19199
|
] })
|
|
18643
19200
|
);
|
|
@@ -18670,7 +19227,7 @@ var SlashCommands = ({
|
|
|
18670
19227
|
return outBox(
|
|
18671
19228
|
/* @__PURE__ */ jsxs16(Fragment6, { children: [
|
|
18672
19229
|
/* @__PURE__ */ jsxs16(Box17, { marginBottom: 1, children: [
|
|
18673
|
-
/* @__PURE__ */ jsx18(Text16, { bold: true, color:
|
|
19230
|
+
/* @__PURE__ */ jsx18(Text16, { bold: true, color: COMMAND_HEADER_COLOR, children: "Sessions" }),
|
|
18674
19231
|
/* @__PURE__ */ jsxs16(Text16, { dimColor: true, children: [
|
|
18675
19232
|
" \xB7 ",
|
|
18676
19233
|
sessions.length,
|
|
@@ -18713,7 +19270,7 @@ var SlashCommands = ({
|
|
|
18713
19270
|
const alive = session.status === "running" ? isProcessAlive(session.pid) : false;
|
|
18714
19271
|
return outBox(
|
|
18715
19272
|
/* @__PURE__ */ jsxs16(Fragment6, { children: [
|
|
18716
|
-
/* @__PURE__ */ jsx18(Text16, { bold: true, color:
|
|
19273
|
+
/* @__PURE__ */ jsx18(Text16, { bold: true, color: COMMAND_HEADER_COLOR, children: "Session status" }),
|
|
18717
19274
|
/* @__PURE__ */ jsx18(Text16, { dimColor: true, children: session.sessionId }),
|
|
18718
19275
|
/* @__PURE__ */ jsxs16(Text16, { dimColor: true, children: [
|
|
18719
19276
|
session.kind,
|
|
@@ -18738,7 +19295,7 @@ var SlashCommands = ({
|
|
|
18738
19295
|
return outBox(
|
|
18739
19296
|
/* @__PURE__ */ jsxs16(Fragment6, { children: [
|
|
18740
19297
|
/* @__PURE__ */ jsxs16(Box17, { marginBottom: 1, children: [
|
|
18741
|
-
/* @__PURE__ */ jsx18(Text16, { bold: true, color:
|
|
19298
|
+
/* @__PURE__ */ jsx18(Text16, { bold: true, color: COMMAND_HEADER_COLOR, children: "Logs" }),
|
|
18742
19299
|
/* @__PURE__ */ jsxs16(Text16, { dimColor: true, children: [
|
|
18743
19300
|
" \xB7 ",
|
|
18744
19301
|
session.sessionId.slice(0, 8)
|
|
@@ -18784,7 +19341,7 @@ var SlashCommands = ({
|
|
|
18784
19341
|
return outBox(
|
|
18785
19342
|
/* @__PURE__ */ jsxs16(Fragment6, { children: [
|
|
18786
19343
|
/* @__PURE__ */ jsxs16(Box17, { marginBottom: 1, children: [
|
|
18787
|
-
/* @__PURE__ */ jsx18(Text16, { bold: true, color:
|
|
19344
|
+
/* @__PURE__ */ jsx18(Text16, { bold: true, color: COMMAND_HEADER_COLOR, children: "Tasks" }),
|
|
18788
19345
|
/* @__PURE__ */ jsxs16(Text16, { dimColor: true, children: [
|
|
18789
19346
|
" \xB7 ",
|
|
18790
19347
|
stats.total,
|
|
@@ -18839,7 +19396,7 @@ var SlashCommands = ({
|
|
|
18839
19396
|
return outBox(
|
|
18840
19397
|
/* @__PURE__ */ jsxs16(Fragment6, { children: [
|
|
18841
19398
|
/* @__PURE__ */ jsxs16(Box17, { marginBottom: 1, children: [
|
|
18842
|
-
/* @__PURE__ */ jsx18(Text16, { bold: true, color:
|
|
19399
|
+
/* @__PURE__ */ jsx18(Text16, { bold: true, color: COMMAND_HEADER_COLOR, children: "Permissions" }),
|
|
18843
19400
|
/* @__PURE__ */ jsxs16(Text16, { dimColor: true, children: [
|
|
18844
19401
|
" \xB7 ",
|
|
18845
19402
|
policy.isSandbox ? "sandbox" : "local"
|
|
@@ -18898,7 +19455,7 @@ var SlashCommands = ({
|
|
|
18898
19455
|
return outBox(
|
|
18899
19456
|
/* @__PURE__ */ jsxs16(Fragment6, { children: [
|
|
18900
19457
|
/* @__PURE__ */ jsxs16(Box17, { marginBottom: 1, children: [
|
|
18901
|
-
/* @__PURE__ */ jsx18(Text16, { bold: true, color:
|
|
19458
|
+
/* @__PURE__ */ jsx18(Text16, { bold: true, color: COMMAND_HEADER_COLOR, children: "Runtime" }),
|
|
18902
19459
|
/* @__PURE__ */ jsx18(Text16, { dimColor: true, children: " \xB7 model / effort" })
|
|
18903
19460
|
] }),
|
|
18904
19461
|
/* @__PURE__ */ jsxs16(Text16, { dimColor: true, children: [
|
|
@@ -18960,7 +19517,7 @@ var SlashCommands = ({
|
|
|
18960
19517
|
];
|
|
18961
19518
|
return outBox(
|
|
18962
19519
|
/* @__PURE__ */ jsxs16(Fragment6, { children: [
|
|
18963
|
-
/* @__PURE__ */ jsx18(Text16, { bold: true, color:
|
|
19520
|
+
/* @__PURE__ */ jsx18(Text16, { bold: true, color: COMMAND_HEADER_COLOR, children: "Statusline" }),
|
|
18964
19521
|
/* @__PURE__ */ jsx18(Text16, { dimColor: true, children: parts.filter(Boolean).join(" \xB7 ") })
|
|
18965
19522
|
] })
|
|
18966
19523
|
);
|
|
@@ -18971,7 +19528,7 @@ var SlashCommands = ({
|
|
|
18971
19528
|
return outBox(
|
|
18972
19529
|
/* @__PURE__ */ jsxs16(Fragment6, { children: [
|
|
18973
19530
|
/* @__PURE__ */ jsxs16(Box17, { marginBottom: 1, children: [
|
|
18974
|
-
/* @__PURE__ */ jsx18(Text16, { bold: true, color:
|
|
19531
|
+
/* @__PURE__ */ jsx18(Text16, { bold: true, color: COMMAND_HEADER_COLOR, children: "Bridge" }),
|
|
18975
19532
|
/* @__PURE__ */ jsxs16(Text16, { dimColor: true, children: [
|
|
18976
19533
|
" \xB7 ",
|
|
18977
19534
|
sessions.length,
|
|
@@ -18999,7 +19556,7 @@ var SlashCommands = ({
|
|
|
18999
19556
|
return outBox(
|
|
19000
19557
|
/* @__PURE__ */ jsxs16(Fragment6, { children: [
|
|
19001
19558
|
/* @__PURE__ */ jsxs16(Box17, { marginBottom: 1, children: [
|
|
19002
|
-
/* @__PURE__ */ jsx18(Text16, { bold: true, color:
|
|
19559
|
+
/* @__PURE__ */ jsx18(Text16, { bold: true, color: COMMAND_HEADER_COLOR, children: "Plugins" }),
|
|
19003
19560
|
/* @__PURE__ */ jsxs16(Text16, { dimColor: true, children: [
|
|
19004
19561
|
" \xB7 ",
|
|
19005
19562
|
plugins.length,
|
|
@@ -19032,7 +19589,7 @@ var SlashCommands = ({
|
|
|
19032
19589
|
return outBox(
|
|
19033
19590
|
/* @__PURE__ */ jsxs16(Fragment6, { children: [
|
|
19034
19591
|
/* @__PURE__ */ jsxs16(Box17, { marginBottom: 1, children: [
|
|
19035
|
-
/* @__PURE__ */ jsx18(Text16, { bold: true, color:
|
|
19592
|
+
/* @__PURE__ */ jsx18(Text16, { bold: true, color: COMMAND_HEADER_COLOR, children: "Diagnostics" }),
|
|
19036
19593
|
/* @__PURE__ */ jsx18(Text16, { dimColor: true, children: " \xB7 health snapshot" })
|
|
19037
19594
|
] }),
|
|
19038
19595
|
/* @__PURE__ */ jsxs16(Text16, { dimColor: true, children: [
|
|
@@ -19116,7 +19673,7 @@ var SlashCommands = ({
|
|
|
19116
19673
|
return outBox(
|
|
19117
19674
|
/* @__PURE__ */ jsxs16(Fragment6, { children: [
|
|
19118
19675
|
/* @__PURE__ */ jsxs16(Box17, { marginBottom: 1, children: [
|
|
19119
|
-
/* @__PURE__ */ jsx18(Text16, { bold: true, color:
|
|
19676
|
+
/* @__PURE__ */ jsx18(Text16, { bold: true, color: COMMAND_HEADER_COLOR, children: "Plugin" }),
|
|
19120
19677
|
/* @__PURE__ */ jsxs16(Text16, { dimColor: true, children: [
|
|
19121
19678
|
" \xB7 ",
|
|
19122
19679
|
plugin.name
|
|
@@ -19155,7 +19712,7 @@ var SlashCommands = ({
|
|
|
19155
19712
|
return outBox(
|
|
19156
19713
|
/* @__PURE__ */ jsxs16(Fragment6, { children: [
|
|
19157
19714
|
/* @__PURE__ */ jsxs16(Box17, { marginBottom: 1, children: [
|
|
19158
|
-
/* @__PURE__ */ jsx18(Text16, { bold: true, color:
|
|
19715
|
+
/* @__PURE__ */ jsx18(Text16, { bold: true, color: COMMAND_HEADER_COLOR, children: "Hooks" }),
|
|
19159
19716
|
/* @__PURE__ */ jsxs16(Text16, { dimColor: true, children: [
|
|
19160
19717
|
" \xB7 ",
|
|
19161
19718
|
state.enabled ? "enabled" : "disabled"
|
|
@@ -19230,7 +19787,7 @@ var SlashCommands = ({
|
|
|
19230
19787
|
const dirs = getPluginDirs();
|
|
19231
19788
|
return outBox(
|
|
19232
19789
|
/* @__PURE__ */ jsxs16(Fragment6, { children: [
|
|
19233
|
-
/* @__PURE__ */ jsx18(Text16, { bold: true, color:
|
|
19790
|
+
/* @__PURE__ */ jsx18(Text16, { bold: true, color: COMMAND_HEADER_COLOR, children: "Plugin paths" }),
|
|
19234
19791
|
/* @__PURE__ */ jsxs16(Text16, { dimColor: true, children: [
|
|
19235
19792
|
"project: ",
|
|
19236
19793
|
dirs.project
|
|
@@ -19391,7 +19948,7 @@ var SlashCommands = ({
|
|
|
19391
19948
|
return outBox(
|
|
19392
19949
|
/* @__PURE__ */ jsxs16(Fragment6, { children: [
|
|
19393
19950
|
/* @__PURE__ */ jsxs16(Box17, { marginBottom: 1, children: [
|
|
19394
|
-
/* @__PURE__ */ jsx18(Text16, { bold: true, color:
|
|
19951
|
+
/* @__PURE__ */ jsx18(Text16, { bold: true, color: COMMAND_HEADER_COLOR, children: "Model picker" }),
|
|
19395
19952
|
/* @__PURE__ */ jsx18(Text16, { dimColor: true, children: " \xB7 recommended" })
|
|
19396
19953
|
] }),
|
|
19397
19954
|
/* @__PURE__ */ jsx18(Box17, { flexDirection: "column", children: RECOMMENDED_MODELS.map((model) => /* @__PURE__ */ jsxs16(Text16, { dimColor: true, children: [
|
|
@@ -19405,7 +19962,7 @@ var SlashCommands = ({
|
|
|
19405
19962
|
const next = setRuntimeConfig({ model: value });
|
|
19406
19963
|
return outBox(
|
|
19407
19964
|
/* @__PURE__ */ jsxs16(Fragment6, { children: [
|
|
19408
|
-
/* @__PURE__ */ jsx18(Text16, { bold: true, color:
|
|
19965
|
+
/* @__PURE__ */ jsx18(Text16, { bold: true, color: COMMAND_HEADER_COLOR, children: "Model" }),
|
|
19409
19966
|
/* @__PURE__ */ jsxs16(Text16, { dimColor: true, children: [
|
|
19410
19967
|
"set to ",
|
|
19411
19968
|
next.model
|
|
@@ -19424,7 +19981,7 @@ var SlashCommands = ({
|
|
|
19424
19981
|
const next = setRuntimeConfig({ reasoningEffort: value });
|
|
19425
19982
|
return outBox(
|
|
19426
19983
|
/* @__PURE__ */ jsxs16(Fragment6, { children: [
|
|
19427
|
-
/* @__PURE__ */ jsx18(Text16, { bold: true, color:
|
|
19984
|
+
/* @__PURE__ */ jsx18(Text16, { bold: true, color: COMMAND_HEADER_COLOR, children: "Effort" }),
|
|
19428
19985
|
/* @__PURE__ */ jsxs16(Text16, { dimColor: true, children: [
|
|
19429
19986
|
"set to ",
|
|
19430
19987
|
next.reasoningEffort
|
|
@@ -19443,7 +20000,7 @@ var SlashCommands = ({
|
|
|
19443
20000
|
const next = setRuntimeConfig({ outputStyle: value });
|
|
19444
20001
|
return outBox(
|
|
19445
20002
|
/* @__PURE__ */ jsxs16(Fragment6, { children: [
|
|
19446
|
-
/* @__PURE__ */ jsx18(Text16, { bold: true, color:
|
|
20003
|
+
/* @__PURE__ */ jsx18(Text16, { bold: true, color: COMMAND_HEADER_COLOR, children: "Style" }),
|
|
19447
20004
|
/* @__PURE__ */ jsxs16(Text16, { dimColor: true, children: [
|
|
19448
20005
|
"set to ",
|
|
19449
20006
|
next.outputStyle
|
|
@@ -19462,7 +20019,7 @@ var SlashCommands = ({
|
|
|
19462
20019
|
const next = setRuntimeConfig({ sandboxEnabled: value === "on" });
|
|
19463
20020
|
return outBox(
|
|
19464
20021
|
/* @__PURE__ */ jsxs16(Fragment6, { children: [
|
|
19465
|
-
/* @__PURE__ */ jsx18(Text16, { bold: true, color:
|
|
20022
|
+
/* @__PURE__ */ jsx18(Text16, { bold: true, color: COMMAND_HEADER_COLOR, children: "Sandbox" }),
|
|
19466
20023
|
/* @__PURE__ */ jsx18(Text16, { dimColor: true, children: next.sandboxEnabled ? "on" : "off" })
|
|
19467
20024
|
] })
|
|
19468
20025
|
);
|
|
@@ -19475,7 +20032,7 @@ var SlashCommands = ({
|
|
|
19475
20032
|
const next = setRuntimeConfig({ workspaceRoot: value });
|
|
19476
20033
|
return outBox(
|
|
19477
20034
|
/* @__PURE__ */ jsxs16(Fragment6, { children: [
|
|
19478
|
-
/* @__PURE__ */ jsx18(Text16, { bold: true, color:
|
|
20035
|
+
/* @__PURE__ */ jsx18(Text16, { bold: true, color: COMMAND_HEADER_COLOR, children: "Worktree" }),
|
|
19479
20036
|
/* @__PURE__ */ jsx18(Text16, { dimColor: true, children: next.workspaceRoot })
|
|
19480
20037
|
] })
|
|
19481
20038
|
);
|
|
@@ -19489,7 +20046,7 @@ var SlashCommands = ({
|
|
|
19489
20046
|
return outBox(
|
|
19490
20047
|
/* @__PURE__ */ jsxs16(Fragment6, { children: [
|
|
19491
20048
|
/* @__PURE__ */ jsxs16(Box17, { marginBottom: 1, children: [
|
|
19492
|
-
/* @__PURE__ */ jsx18(Text16, { bold: true, color:
|
|
20049
|
+
/* @__PURE__ */ jsx18(Text16, { bold: true, color: COMMAND_HEADER_COLOR, children: "Slash commands \xB7 organized view" }),
|
|
19493
20050
|
/* @__PURE__ */ jsxs16(Text16, { dimColor: true, children: [
|
|
19494
20051
|
" ",
|
|
19495
20052
|
"\xB7 type any command to execute; use /help",
|
|
@@ -19503,7 +20060,7 @@ var SlashCommands = ({
|
|
|
19503
20060
|
/* @__PURE__ */ jsx18(Box17, { flexDirection: "column", marginTop: 1, children: groups.map((group) => /* @__PURE__ */ jsxs16(Box17, { marginBottom: 1, flexDirection: "column", children: [
|
|
19504
20061
|
/* @__PURE__ */ jsx18(Text16, { color: BLUMA_TERMINAL.suggestion, bold: true, children: group.label }),
|
|
19505
20062
|
/* @__PURE__ */ jsx18(Box17, { flexDirection: "column", marginTop: 0, children: group.commands.map((command) => /* @__PURE__ */ jsxs16(Box17, { flexDirection: "row", flexWrap: "wrap", children: [
|
|
19506
|
-
/* @__PURE__ */ jsx18(Text16, { color:
|
|
20063
|
+
/* @__PURE__ */ jsx18(Text16, { color: COMMAND_HEADER_COLOR, bold: true, children: command.name.padEnd(16, " ") }),
|
|
19507
20064
|
/* @__PURE__ */ jsx18(Text16, { dimColor: true, children: command.description })
|
|
19508
20065
|
] }, command.name)) })
|
|
19509
20066
|
] }, group.category)) }),
|
|
@@ -19519,7 +20076,7 @@ var SlashCommands = ({
|
|
|
19519
20076
|
if (entries.length === 0) {
|
|
19520
20077
|
return outBox(
|
|
19521
20078
|
/* @__PURE__ */ jsxs16(Fragment6, { children: [
|
|
19522
|
-
/* @__PURE__ */ jsx18(Text16, { bold: true, color:
|
|
20079
|
+
/* @__PURE__ */ jsx18(Text16, { bold: true, color: COMMAND_HEADER_COLOR, children: "Worker agents" }),
|
|
19523
20080
|
/* @__PURE__ */ jsx18(Text16, { dimColor: true, children: " No agent sessions in this CLI registry. Use the " }),
|
|
19524
20081
|
/* @__PURE__ */ jsx18(Text16, { bold: true, children: "spawn_agent" }),
|
|
19525
20082
|
/* @__PURE__ */ jsx18(Text16, { dimColor: true, children: " tool from chat, or " }),
|
|
@@ -19530,7 +20087,7 @@ var SlashCommands = ({
|
|
|
19530
20087
|
}
|
|
19531
20088
|
return outBox(
|
|
19532
20089
|
/* @__PURE__ */ jsxs16(Fragment6, { children: [
|
|
19533
|
-
/* @__PURE__ */ jsx18(Text16, { bold: true, color:
|
|
20090
|
+
/* @__PURE__ */ jsx18(Text16, { bold: true, color: COMMAND_HEADER_COLOR, children: "Worker agents" }),
|
|
19534
20091
|
/* @__PURE__ */ jsxs16(Text16, { dimColor: true, children: [
|
|
19535
20092
|
" ",
|
|
19536
20093
|
entries.length,
|
|
@@ -19674,7 +20231,7 @@ Usage: /features <key> on|off`
|
|
|
19674
20231
|
return outBox(
|
|
19675
20232
|
/* @__PURE__ */ jsxs16(Fragment6, { children: [
|
|
19676
20233
|
/* @__PURE__ */ jsxs16(Box17, { marginBottom: 1, children: [
|
|
19677
|
-
/* @__PURE__ */ jsx18(Text16, { bold: true, color:
|
|
20234
|
+
/* @__PURE__ */ jsx18(Text16, { bold: true, color: COMMAND_HEADER_COLOR, children: "Skills (load_skill)" }),
|
|
19678
20235
|
/* @__PURE__ */ jsxs16(Text16, { dimColor: true, children: [
|
|
19679
20236
|
" \xB7 ",
|
|
19680
20237
|
list.length,
|
|
@@ -19734,7 +20291,7 @@ Usage: /features <key> on|off`
|
|
|
19734
20291
|
return outBox(
|
|
19735
20292
|
/* @__PURE__ */ jsxs16(Fragment6, { children: [
|
|
19736
20293
|
/* @__PURE__ */ jsxs16(Box17, { marginBottom: 1, children: [
|
|
19737
|
-
/* @__PURE__ */ jsx18(Text16, { bold: true, color:
|
|
20294
|
+
/* @__PURE__ */ jsx18(Text16, { bold: true, color: COMMAND_HEADER_COLOR, children: "MCP Tools" }),
|
|
19738
20295
|
/* @__PURE__ */ jsx18(Text16, { dimColor: true, children: " \u2022 " }),
|
|
19739
20296
|
/* @__PURE__ */ jsxs16(Text16, { dimColor: true, children: [
|
|
19740
20297
|
tools.length,
|
|
@@ -19790,7 +20347,7 @@ Usage: /features <key> on|off`
|
|
|
19790
20347
|
const colSource = 18;
|
|
19791
20348
|
return outBox(
|
|
19792
20349
|
/* @__PURE__ */ jsxs16(Fragment6, { children: [
|
|
19793
|
-
/* @__PURE__ */ jsx18(Text16, { color:
|
|
20350
|
+
/* @__PURE__ */ jsx18(Text16, { color: COMMAND_HEADER_COLOR, bold: true, children: "Native Tools" }),
|
|
19794
20351
|
/* @__PURE__ */ jsxs16(Text16, { color: "gray", children: [
|
|
19795
20352
|
"Total Native: ",
|
|
19796
20353
|
tools.length,
|
|
@@ -19834,7 +20391,7 @@ Usage: /features <key> on|off`
|
|
|
19834
20391
|
const errored = agents.filter((a) => a.status === "error");
|
|
19835
20392
|
return outBox(
|
|
19836
20393
|
/* @__PURE__ */ jsxs16(Fragment6, { children: [
|
|
19837
|
-
/* @__PURE__ */ jsx18(Text16, { bold: true, color:
|
|
20394
|
+
/* @__PURE__ */ jsx18(Text16, { bold: true, color: COMMAND_HEADER_COLOR, children: "Debug Workers" }),
|
|
19838
20395
|
/* @__PURE__ */ jsxs16(Text16, { dimColor: true, children: [
|
|
19839
20396
|
"Total: ",
|
|
19840
20397
|
agents.length,
|
|
@@ -19868,7 +20425,7 @@ Usage: /features <key> on|off`
|
|
|
19868
20425
|
if (cmd === "compact") {
|
|
19869
20426
|
return outBox(
|
|
19870
20427
|
/* @__PURE__ */ jsxs16(Fragment6, { children: [
|
|
19871
|
-
/* @__PURE__ */ jsx18(Text16, { bold: true, color:
|
|
20428
|
+
/* @__PURE__ */ jsx18(Text16, { bold: true, color: COMMAND_HEADER_COLOR, children: "Context Compaction" }),
|
|
19872
20429
|
/* @__PURE__ */ jsx18(Text16, { dimColor: true, children: "Auto-compact triggers at 180k tokens (threshold: 150k micro, 180k full)" }),
|
|
19873
20430
|
/* @__PURE__ */ jsx18(Text16, { dimColor: true, children: "Target after compact: 100k tokens" }),
|
|
19874
20431
|
/* @__PURE__ */ jsx18(Text16, { dimColor: true, children: "Use /compact to manually trigger context compaction." })
|
|
@@ -19878,7 +20435,7 @@ Usage: /features <key> on|off`
|
|
|
19878
20435
|
if (cmd === "cost") {
|
|
19879
20436
|
return outBox(
|
|
19880
20437
|
/* @__PURE__ */ jsxs16(Fragment6, { children: [
|
|
19881
|
-
/* @__PURE__ */ jsx18(Text16, { bold: true, color:
|
|
20438
|
+
/* @__PURE__ */ jsx18(Text16, { bold: true, color: COMMAND_HEADER_COLOR, children: "Session Cost" }),
|
|
19882
20439
|
/* @__PURE__ */ jsx18(Text16, { dimColor: true, children: "Cost tracking is available via /stats for detailed session statistics." })
|
|
19883
20440
|
] })
|
|
19884
20441
|
);
|
|
@@ -19886,7 +20443,7 @@ Usage: /features <key> on|off`
|
|
|
19886
20443
|
if (cmd === "export") {
|
|
19887
20444
|
return outBox(
|
|
19888
20445
|
/* @__PURE__ */ jsxs16(Fragment6, { children: [
|
|
19889
|
-
/* @__PURE__ */ jsx18(Text16, { bold: true, color:
|
|
20446
|
+
/* @__PURE__ */ jsx18(Text16, { bold: true, color: COMMAND_HEADER_COLOR, children: "Export Session" }),
|
|
19890
20447
|
/* @__PURE__ */ jsx18(Text16, { dimColor: true, children: "Session logs are stored in ~/.bluma/sessions/" }),
|
|
19891
20448
|
/* @__PURE__ */ jsx18(Text16, { dimColor: true, children: "Use /logs to view recent logs for a session." })
|
|
19892
20449
|
] })
|
|
@@ -19895,7 +20452,7 @@ Usage: /features <key> on|off`
|
|
|
19895
20452
|
if (cmd === "memory") {
|
|
19896
20453
|
return outBox(
|
|
19897
20454
|
/* @__PURE__ */ jsxs16(Fragment6, { children: [
|
|
19898
|
-
/* @__PURE__ */ jsx18(Text16, { bold: true, color:
|
|
20455
|
+
/* @__PURE__ */ jsx18(Text16, { bold: true, color: COMMAND_HEADER_COLOR, children: "Session Memory" }),
|
|
19899
20456
|
/* @__PURE__ */ jsx18(Text16, { dimColor: true, children: "Memories are auto-extracted from conversations and stored in ~/.bluma/session_memory.json" }),
|
|
19900
20457
|
/* @__PURE__ */ jsx18(Text16, { dimColor: true, children: "Relevant memories are injected into context automatically." })
|
|
19901
20458
|
] })
|
|
@@ -19905,7 +20462,7 @@ Usage: /features <key> on|off`
|
|
|
19905
20462
|
const mem = process.memoryUsage();
|
|
19906
20463
|
return outBox(
|
|
19907
20464
|
/* @__PURE__ */ jsxs16(Fragment6, { children: [
|
|
19908
|
-
/* @__PURE__ */ jsx18(Text16, { bold: true, color:
|
|
20465
|
+
/* @__PURE__ */ jsx18(Text16, { bold: true, color: COMMAND_HEADER_COLOR, children: "Session Statistics" }),
|
|
19909
20466
|
/* @__PURE__ */ jsxs16(Text16, { dimColor: true, children: [
|
|
19910
20467
|
"Heap: ",
|
|
19911
20468
|
Math.round(mem.heapUsed / 1024 / 1024),
|
|
@@ -19969,7 +20526,7 @@ Usage: /features <key> on|off`
|
|
|
19969
20526
|
}
|
|
19970
20527
|
return outBox(
|
|
19971
20528
|
/* @__PURE__ */ jsxs16(Fragment6, { children: [
|
|
19972
|
-
/* @__PURE__ */ jsx18(Text16, { bold: true, color:
|
|
20529
|
+
/* @__PURE__ */ jsx18(Text16, { bold: true, color: COMMAND_HEADER_COLOR, children: "Available Themes" }),
|
|
19973
20530
|
themes.map((t) => /* @__PURE__ */ jsxs16(Text16, { color: "white", children: [
|
|
19974
20531
|
" ",
|
|
19975
20532
|
t.name,
|
|
@@ -19982,7 +20539,7 @@ Usage: /features <key> on|off`
|
|
|
19982
20539
|
if (cmd === "keybindings") {
|
|
19983
20540
|
return outBox(
|
|
19984
20541
|
/* @__PURE__ */ jsxs16(Fragment6, { children: [
|
|
19985
|
-
/* @__PURE__ */ jsx18(Text16, { bold: true, color:
|
|
20542
|
+
/* @__PURE__ */ jsx18(Text16, { bold: true, color: COMMAND_HEADER_COLOR, children: "Keybindings" }),
|
|
19986
20543
|
/* @__PURE__ */ jsx18(Text16, { dimColor: true, children: "Custom keybindings are configured in ~/.bluma/settings.json" }),
|
|
19987
20544
|
/* @__PURE__ */ jsx18(Text16, { dimColor: true, children: " Ctrl+V / Cmd+V \u2014 Paste image or text" }),
|
|
19988
20545
|
/* @__PURE__ */ jsx18(Text16, { dimColor: true, children: " Ctrl+Shift+I \u2014 Paste image" }),
|
|
@@ -19994,7 +20551,7 @@ Usage: /features <key> on|off`
|
|
|
19994
20551
|
if (cmd === "vim") {
|
|
19995
20552
|
return outBox(
|
|
19996
20553
|
/* @__PURE__ */ jsxs16(Fragment6, { children: [
|
|
19997
|
-
/* @__PURE__ */ jsx18(Text16, { bold: true, color:
|
|
20554
|
+
/* @__PURE__ */ jsx18(Text16, { bold: true, color: COMMAND_HEADER_COLOR, children: "Vim Mode" }),
|
|
19998
20555
|
/* @__PURE__ */ jsx18(Text16, { dimColor: true, children: "Vim mode provides normal/insert/command modes." }),
|
|
19999
20556
|
/* @__PURE__ */ jsx18(Text16, { dimColor: true, children: " Escape \u2014 Switch to normal mode" }),
|
|
20000
20557
|
/* @__PURE__ */ jsx18(Text16, { dimColor: true, children: " i/a/o \u2014 Switch to insert mode" }),
|
|
@@ -20323,7 +20880,7 @@ Report the release version, tag, changelog summary, and verification results whe
|
|
|
20323
20880
|
return outBox(
|
|
20324
20881
|
/* @__PURE__ */ jsxs16(Fragment6, { children: [
|
|
20325
20882
|
/* @__PURE__ */ jsxs16(Box17, { marginBottom: 1, children: [
|
|
20326
|
-
/* @__PURE__ */ jsx18(Text16, { bold: true, color:
|
|
20883
|
+
/* @__PURE__ */ jsx18(Text16, { bold: true, color: COMMAND_HEADER_COLOR, children: "Release" }),
|
|
20327
20884
|
/* @__PURE__ */ jsxs16(Text16, { dimColor: true, children: [
|
|
20328
20885
|
" \xB7 ",
|
|
20329
20886
|
bumpType,
|
|
@@ -20339,32 +20896,15 @@ Report the release version, tag, changelog summary, and verification results whe
|
|
|
20339
20896
|
if (cmd === "review") {
|
|
20340
20897
|
const target = args.join(" ") || "";
|
|
20341
20898
|
const isPR = target && /^\d+$/.test(target);
|
|
20342
|
-
if (!target && !isPR) {
|
|
20343
|
-
return outBox(
|
|
20344
|
-
/* @__PURE__ */ jsxs16(Fragment6, { children: [
|
|
20345
|
-
/* @__PURE__ */ jsx18(Text16, { bold: true, color: BLUMA_TERMINAL.accent, children: "/review" }),
|
|
20346
|
-
/* @__PURE__ */ jsx18(Text16, { dimColor: true, children: "Review Coordinator \u2014 spawn a team of specialized QA reviewers in parallel." }),
|
|
20347
|
-
/* @__PURE__ */ jsx18(Text16, { dimColor: true, children: 'Usage: /review [PR-number | file-path | "local changes"]' }),
|
|
20348
|
-
/* @__PURE__ */ jsx18(Text16, { dimColor: true, children: " " }),
|
|
20349
|
-
/* @__PURE__ */ jsx18(Text16, { dimColor: true, children: "The agent will coordinate a review team:" }),
|
|
20350
|
-
/* @__PURE__ */ jsx18(Text16, { dimColor: true, children: " \u2022 Security Reviewer \u2014 vulnerabilities, injection, auth flaws" }),
|
|
20351
|
-
/* @__PURE__ */ jsx18(Text16, { dimColor: true, children: " \u2022 Logic & Correctness \u2014 bugs, edge cases, wrong assumptions" }),
|
|
20352
|
-
/* @__PURE__ */ jsx18(Text16, { dimColor: true, children: " \u2022 Performance \u2014 N+1 queries, memory leaks, algorithmic issues" }),
|
|
20353
|
-
/* @__PURE__ */ jsx18(Text16, { dimColor: true, children: " \u2022 Code Quality \u2014 naming, structure, conventions, readability" }),
|
|
20354
|
-
/* @__PURE__ */ jsx18(Text16, { dimColor: true, children: " \u2022 Test Coverage \u2014 missing tests, weak assertions, untested paths" }),
|
|
20355
|
-
/* @__PURE__ */ jsx18(Text16, { dimColor: true, children: " \u2022 Architecture \u2014 coupling, abstraction leaks, design violations" })
|
|
20356
|
-
] })
|
|
20357
|
-
);
|
|
20358
|
-
}
|
|
20359
20899
|
(async () => {
|
|
20360
20900
|
try {
|
|
20361
|
-
const reviewTarget = isPR ? `PR #${target}` : target === "local" || target === "local changes" ? "current local changes (git diff HEAD)" : `the file/module: ${target}
|
|
20901
|
+
const reviewTarget = isPR ? `PR #${target}` : target === "local" || target === "local changes" ? "current local changes (git diff HEAD)" : target ? `the file/module: ${target}` : "current local changes (git diff HEAD)";
|
|
20362
20902
|
await agentRef.current?.processTurn({
|
|
20363
20903
|
content: `## REVIEW COORDINATOR MODE \u2014 Lead a Team of Senior QA Reviewers
|
|
20364
20904
|
|
|
20365
|
-
You are now the **Review Coordinator** \u2014 a Principal Engineer leading a team of senior, picky QA reviewers. Your job is to orchestrate a **thorough, line-by-line code review** where NOTHING slips through.
|
|
20905
|
+
You are now the **Review Coordinator** \u2014 a Principal Engineer leading a team of senior, picky QA reviewers. Your job is to orchestrate a **thorough, line-by-line code review** where NOTHING slips through.
|
|
20366
20906
|
|
|
20367
|
-
**NEVER be afraid to coordinate.** Spawning specialized reviewers is how you catch bugs that a single reviewer would miss.
|
|
20907
|
+
**NEVER be afraid to coordinate.** Spawning specialized reviewers is how you catch bugs that a single reviewer would miss.
|
|
20368
20908
|
|
|
20369
20909
|
**Review Target:** ${reviewTarget}
|
|
20370
20910
|
|
|
@@ -20378,17 +20918,20 @@ You are now the **Review Coordinator** \u2014 a Principal Engineer leading a tea
|
|
|
20378
20918
|
${target === "local" || target === "local changes" ? `- Run \`git diff HEAD\` for unstaged changes` : ""}
|
|
20379
20919
|
${target === "local" || target === "local changes" ? `- Run \`git diff --cached HEAD\` for staged changes` : ""}
|
|
20380
20920
|
2. Understand the SCOPE: how many files changed, what areas are affected
|
|
20381
|
-
3. Identify which review specialties are most relevant (security? performance? logic?)
|
|
20382
20921
|
|
|
20383
|
-
#### Step 2: Spawn Parallel Review Workers
|
|
20384
|
-
Launch **
|
|
20922
|
+
#### Step 2: Spawn 3 Parallel Review Workers
|
|
20923
|
+
Launch exactly **3 workers in parallel** \u2014 one for each core area.
|
|
20924
|
+
|
|
20925
|
+
**IMPORTANT:** Each worker MUST read EVERY changed file line by line. Do NOT report until you have examined all files. List each file you reviewed in your report.
|
|
20385
20926
|
|
|
20386
|
-
**Worker 1 \u2014 Security Reviewer
|
|
20927
|
+
**Worker 1 \u2014 Security Reviewer:**
|
|
20387
20928
|
\`\`\`
|
|
20388
20929
|
spawn_agent({
|
|
20389
20930
|
task: "SECURITY REVIEW: Thoroughly review ${reviewTarget} for security vulnerabilities.
|
|
20390
20931
|
|
|
20391
|
-
You are a Senior Security Engineer. Read
|
|
20932
|
+
You are a Senior Security Engineer. Read EVERY changed file line by line. Do NOT report until you have examined all files.
|
|
20933
|
+
|
|
20934
|
+
Look for:
|
|
20392
20935
|
- Injection vulnerabilities (SQL, XSS, command injection, template injection)
|
|
20393
20936
|
- Authentication/authorization flaws (missing auth checks, privilege escalation)
|
|
20394
20937
|
- Sensitive data exposure (secrets in logs, PII leaks, hardcoded credentials)
|
|
@@ -20405,20 +20948,24 @@ For EACH issue found:
|
|
|
20405
20948
|
- How to exploit it (brief)
|
|
20406
20949
|
- Recommended fix
|
|
20407
20950
|
|
|
20408
|
-
Be PICKY. If something looks suspicious, flag it.
|
|
20951
|
+
Be PICKY. If something looks suspicious, flag it.
|
|
20952
|
+
|
|
20953
|
+
Do NOT modify files. Report only.
|
|
20409
20954
|
|
|
20410
|
-
|
|
20955
|
+
At the end of your report, list ALL files you reviewed.",
|
|
20411
20956
|
title: "Security Review",
|
|
20412
20957
|
agent_type: "reviewer"
|
|
20413
20958
|
})
|
|
20414
20959
|
\`\`\`
|
|
20415
20960
|
|
|
20416
|
-
**Worker 2 \u2014 Logic & Correctness
|
|
20961
|
+
**Worker 2 \u2014 Logic & Correctness:**
|
|
20417
20962
|
\`\`\`
|
|
20418
20963
|
spawn_agent({
|
|
20419
20964
|
task: "LOGIC REVIEW: Thoroughly review ${reviewTarget} for bugs and logic errors.
|
|
20420
20965
|
|
|
20421
|
-
You are a Senior QA Engineer who finds bugs for a living. Read
|
|
20966
|
+
You are a Senior QA Engineer who finds bugs for a living. Read EVERY changed file line by line. Do NOT report until you have examined all files.
|
|
20967
|
+
|
|
20968
|
+
Look for:
|
|
20422
20969
|
- Logic errors (wrong conditions, off-by-one, inverted boolean, wrong operator)
|
|
20423
20970
|
- Null/undefined handling (missing null checks, unsafe property access)
|
|
20424
20971
|
- State management issues (stale state, missing initialization, race conditions)
|
|
@@ -20435,50 +20982,24 @@ For EACH issue found:
|
|
|
20435
20982
|
- How to trigger the bug
|
|
20436
20983
|
- Recommended fix
|
|
20437
20984
|
|
|
20438
|
-
Be RELENTLESS. Question every assumption.
|
|
20439
|
-
|
|
20440
|
-
Do NOT modify files. Report only.",
|
|
20441
|
-
title: "Logic & Correctness Review",
|
|
20442
|
-
agent_type: "reviewer"
|
|
20443
|
-
})
|
|
20444
|
-
\`\`\`
|
|
20445
|
-
|
|
20446
|
-
**Worker 3 \u2014 Performance (spawn if changes touch data flow, loops, queries, APIs):**
|
|
20447
|
-
\`\`\`
|
|
20448
|
-
spawn_agent({
|
|
20449
|
-
task: "PERFORMANCE REVIEW: Thoroughly review ${reviewTarget} for performance issues.
|
|
20450
|
-
|
|
20451
|
-
You are a Performance Engineer. Read every changed line. Look for:
|
|
20452
|
-
- N+1 query patterns (looping database calls)
|
|
20453
|
-
- Unnecessary computations (repeated calculations, missing memoization)
|
|
20454
|
-
- Memory leaks (unclosed resources, growing arrays, event listener accumulation)
|
|
20455
|
-
- Algorithmic complexity (O(n\xB2) where O(n) is possible, nested loops on large data)
|
|
20456
|
-
- Blocking operations (sync I/O in async context, heavy computation on main thread)
|
|
20457
|
-
- Missing caching opportunities (repeated expensive operations)
|
|
20458
|
-
- Bundle size issues (large imports, tree-shaking failures)
|
|
20459
|
-
- Network inefficiencies (unnecessary requests, missing batching, no pagination)
|
|
20460
|
-
|
|
20461
|
-
For EACH issue found:
|
|
20462
|
-
- Impact: HIGH / MEDIUM / LOW
|
|
20463
|
-
- File:line number
|
|
20464
|
-
- Current behavior vs optimized behavior
|
|
20465
|
-
- Estimated improvement
|
|
20466
|
-
- Recommended fix with code example
|
|
20985
|
+
Be RELENTLESS. Question every assumption.
|
|
20467
20986
|
|
|
20468
|
-
|
|
20987
|
+
Do NOT modify files. Report only.
|
|
20469
20988
|
|
|
20470
|
-
|
|
20471
|
-
title: "
|
|
20989
|
+
At the end of your report, list ALL files you reviewed.",
|
|
20990
|
+
title: "Logic & Correctness Review",
|
|
20472
20991
|
agent_type: "reviewer"
|
|
20473
20992
|
})
|
|
20474
20993
|
\`\`\`
|
|
20475
20994
|
|
|
20476
|
-
**Worker
|
|
20995
|
+
**Worker 3 \u2014 Code Quality:**
|
|
20477
20996
|
\`\`\`
|
|
20478
20997
|
spawn_agent({
|
|
20479
20998
|
task: "CODE QUALITY REVIEW: Thoroughly review ${reviewTarget} for code quality and convention violations.
|
|
20480
20999
|
|
|
20481
|
-
You are a Staff Engineer obsessed with clean code. Read
|
|
21000
|
+
You are a Staff Engineer obsessed with clean code. Read EVERY changed file line by line. Do NOT report until you have examined all files.
|
|
21001
|
+
|
|
21002
|
+
Look for:
|
|
20482
21003
|
- Naming issues (misleading names, abbreviations, inconsistent casing)
|
|
20483
21004
|
- Function length and complexity (too long, too many responsibilities, deep nesting)
|
|
20484
21005
|
- DRY violations (duplicated logic that should be extracted)
|
|
@@ -20496,72 +21017,22 @@ For EACH issue found:
|
|
|
20496
21017
|
|
|
20497
21018
|
Be PICKY about readability. Code is read 10x more than written.
|
|
20498
21019
|
|
|
20499
|
-
Do NOT modify files. Report only.
|
|
20500
|
-
title: "Code Quality Review",
|
|
20501
|
-
agent_type: "reviewer"
|
|
20502
|
-
})
|
|
20503
|
-
\`\`\`
|
|
21020
|
+
Do NOT modify files. Report only.
|
|
20504
21021
|
|
|
20505
|
-
|
|
20506
|
-
|
|
20507
|
-
spawn_agent({
|
|
20508
|
-
task: "TEST COVERAGE REVIEW: Thoroughly review ${reviewTarget} for test gaps.
|
|
20509
|
-
|
|
20510
|
-
You are a QA Lead. Look at:
|
|
20511
|
-
- Which files changed and whether they have corresponding tests
|
|
20512
|
-
- If tests exist: are they testing the RIGHT things (happy path only? missing edge cases?)
|
|
20513
|
-
- Assertion quality (weak assertions, missing error case tests)
|
|
20514
|
-
- Untested code paths (branches, error handlers, edge cases)
|
|
20515
|
-
- Mock quality (over-mocking, mocking the wrong things, unrealistic mocks)
|
|
20516
|
-
- Integration gaps (unit tests but no integration tests, or vice versa)
|
|
20517
|
-
- Test naming and organization (unclear test names, giant test files)
|
|
20518
|
-
|
|
20519
|
-
For EACH gap found:
|
|
20520
|
-
- File:line of untested code
|
|
20521
|
-
- What scenario is not tested
|
|
20522
|
-
- Why it matters
|
|
20523
|
-
- Suggested test case (with code)
|
|
20524
|
-
|
|
20525
|
-
Be DEMANDING. Untested code is broken code waiting for the right input.
|
|
20526
|
-
|
|
20527
|
-
Do NOT modify files. Report only.",
|
|
20528
|
-
title: "Test Coverage Review",
|
|
21022
|
+
At the end of your report, list ALL files you reviewed.",
|
|
21023
|
+
title: "Code Quality Review",
|
|
20529
21024
|
agent_type: "reviewer"
|
|
20530
21025
|
})
|
|
20531
21026
|
\`\`\`
|
|
20532
21027
|
|
|
20533
|
-
|
|
20534
|
-
|
|
20535
|
-
spawn_agent({
|
|
20536
|
-
task: "ARCHITECTURE REVIEW: Thoroughly review ${reviewTarget} for design and architectural issues.
|
|
20537
|
-
|
|
20538
|
-
You are a Principal Engineer. Look at the BIG picture:
|
|
20539
|
-
- Does this change fit the existing architecture or fight against it?
|
|
20540
|
-
- Are there new dependencies that shouldn't exist (circular deps, wrong layer imports)?
|
|
20541
|
-
- Is the abstraction level appropriate (too low-level, too abstract, leaky abstractions)?
|
|
20542
|
-
- Does this introduce coupling between previously independent modules?
|
|
20543
|
-
- Are there better design patterns that should be used?
|
|
20544
|
-
- Does this change break any existing contracts or APIs?
|
|
20545
|
-
- Is the change appropriately scoped (too big, mixing concerns)?
|
|
20546
|
-
- Does this create technical debt (quick hacks that will hurt later)?
|
|
21028
|
+
#### Step 3: Wait for Workers + Synthesize
|
|
21029
|
+
Wait for ALL 3 workers to complete. Use wait_agent with a large timeout (600000ms).
|
|
20547
21030
|
|
|
20548
|
-
|
|
20549
|
-
-
|
|
20550
|
-
-
|
|
20551
|
-
-
|
|
20552
|
-
-
|
|
20553
|
-
- Suggested alternative approach
|
|
20554
|
-
|
|
20555
|
-
Be VISIONARY. Think about what this code will look like in 6 months.
|
|
20556
|
-
|
|
20557
|
-
Do NOT modify files. Report only.",
|
|
20558
|
-
title: "Architecture Review",
|
|
20559
|
-
agent_type: "reviewer"
|
|
20560
|
-
})
|
|
20561
|
-
\`\`\`
|
|
20562
|
-
|
|
20563
|
-
#### Step 3: Synthesize (you do this \u2014 critical)
|
|
20564
|
-
Wait for ALL review workers to complete. Read every finding carefully.
|
|
21031
|
+
**If workers fail or sessions disappear:**
|
|
21032
|
+
- This can happen with fast-completing workers
|
|
21033
|
+
- Simply perform the review yourself by reading the changed files
|
|
21034
|
+
- Report: "Workers completed/unavailable \u2014 performing review directly"
|
|
21035
|
+
- Do NOT waste time retrying \u2014 just do the review
|
|
20565
21036
|
|
|
20566
21037
|
**NEVER write** "the review looks good" \u2014 that's lazy.
|
|
20567
21038
|
**ALWAYS synthesize**: Group findings by severity, cross-reference between reviewers, identify patterns.
|
|
@@ -20572,7 +21043,7 @@ Compile a comprehensive review report:
|
|
|
20572
21043
|
**REVIEW REPORT for ${reviewTarget}**
|
|
20573
21044
|
|
|
20574
21045
|
\u{1F534} CRITICAL / BLOCKER (must fix before merge):
|
|
20575
|
-
- [List all critical findings
|
|
21046
|
+
- [List all critical findings]
|
|
20576
21047
|
|
|
20577
21048
|
\u{1F7E1} HIGH / MAJOR (should fix):
|
|
20578
21049
|
- [List all high findings]
|
|
@@ -20581,27 +21052,25 @@ Compile a comprehensive review report:
|
|
|
20581
21052
|
- [List all medium findings]
|
|
20582
21053
|
|
|
20583
21054
|
\u2139\uFE0F OBSERVATIONS (no action needed):
|
|
20584
|
-
- [List observations, style notes
|
|
21055
|
+
- [List observations, style notes]
|
|
20585
21056
|
|
|
20586
21057
|
\u2705 POSITIVE FINDINGS (what's good):
|
|
20587
|
-
- [List well-written code, good patterns
|
|
21058
|
+
- [List well-written code, good patterns]
|
|
20588
21059
|
|
|
20589
21060
|
**Review Summary:**
|
|
20590
21061
|
- Total issues found: X critical, Y high, Z medium
|
|
20591
|
-
- Reviewers used: [list workers]
|
|
21062
|
+
- Reviewers used: [list workers or "direct review"]
|
|
20592
21063
|
- Recommendation: APPROVE / APPROVE WITH COMMENTS / REQUEST CHANGES
|
|
20593
21064
|
- Confidence level: HIGH / MEDIUM / LOW
|
|
20594
21065
|
|
|
20595
21066
|
### COORDINATOR RULES
|
|
20596
|
-
- **You are the brain, reviewers are the eyes** \u2014 synthesize, don't just copy-paste
|
|
20597
|
-
- **Spawn workers in parallel** \u2014
|
|
20598
|
-
- **
|
|
20599
|
-
- **
|
|
20600
|
-
- **NEVER
|
|
20601
|
-
- **NEVER fabricate results** \u2014 wait for workers, read their output, report truth
|
|
20602
|
-
- **If a worker finds nothing**, consider whether they had the right scope \u2014 maybe re-spawn with more specific instructions
|
|
21067
|
+
- **You are the brain, reviewers are the eyes** \u2014 synthesize, don't just copy-paste
|
|
21068
|
+
- **Spawn 3 workers in parallel** \u2014 Security, Logic, Code Quality
|
|
21069
|
+
- **If workers fail, do the review yourself** \u2014 no drama, just deliver
|
|
21070
|
+
- **NEVER rubber-stamp** \u2014 your job is to find issues
|
|
21071
|
+
- **NEVER fabricate results** \u2014 report truth
|
|
20603
21072
|
|
|
20604
|
-
Start coordinating now. Triage the changes, then spawn your
|
|
21073
|
+
Start coordinating now. Triage the changes, then spawn your 3 reviewers.`
|
|
20605
21074
|
});
|
|
20606
21075
|
} catch (e) {
|
|
20607
21076
|
setHistory((prev) => prev.concat({
|
|
@@ -20713,7 +21182,7 @@ Start coordinating now. Triage the changes, then spawn your review team.`
|
|
|
20713
21182
|
if (!messageText) {
|
|
20714
21183
|
return outBox(
|
|
20715
21184
|
/* @__PURE__ */ jsxs16(Fragment6, { children: [
|
|
20716
|
-
/* @__PURE__ */ jsx18(Text16, { bold: true, color:
|
|
21185
|
+
/* @__PURE__ */ jsx18(Text16, { bold: true, color: COMMAND_HEADER_COLOR, children: "/brief" }),
|
|
20717
21186
|
/* @__PURE__ */ jsx18(Text16, { dimColor: true, children: "Send a structured message to the user." }),
|
|
20718
21187
|
/* @__PURE__ */ jsx18(Text16, { dimColor: true, children: "Usage: /brief <message> [--proactive] [--attach file1 file2]" }),
|
|
20719
21188
|
/* @__PURE__ */ jsx18(Text16, { dimColor: true, children: " --proactive Mark as proactive notification" }),
|
|
@@ -20815,7 +21284,7 @@ Start coordinating now. Triage the changes, then spawn your review team.`
|
|
|
20815
21284
|
if (!target) {
|
|
20816
21285
|
return outBox(
|
|
20817
21286
|
/* @__PURE__ */ jsxs16(Fragment6, { children: [
|
|
20818
|
-
/* @__PURE__ */ jsx18(Text16, { bold: true, color:
|
|
21287
|
+
/* @__PURE__ */ jsx18(Text16, { bold: true, color: COMMAND_HEADER_COLOR, children: "/explain" }),
|
|
20819
21288
|
/* @__PURE__ */ jsx18(Text16, { dimColor: true, children: "Explain a file, function, or code snippet." }),
|
|
20820
21289
|
/* @__PURE__ */ jsx18(Text16, { dimColor: true, children: "Usage: /explain <file> [line-range or function-name]" })
|
|
20821
21290
|
] })
|
|
@@ -20866,7 +21335,7 @@ Start coordinating now. Triage the changes, then spawn your review team.`
|
|
|
20866
21335
|
if (!filePath) {
|
|
20867
21336
|
return outBox(
|
|
20868
21337
|
/* @__PURE__ */ jsxs16(Fragment6, { children: [
|
|
20869
|
-
/* @__PURE__ */ jsx18(Text16, { bold: true, color:
|
|
21338
|
+
/* @__PURE__ */ jsx18(Text16, { bold: true, color: COMMAND_HEADER_COLOR, children: "/editor" }),
|
|
20870
21339
|
/* @__PURE__ */ jsx18(Text16, { dimColor: true, children: "Open a file in the external editor ($EDITOR or configured editor)." }),
|
|
20871
21340
|
/* @__PURE__ */ jsx18(Text16, { dimColor: true, children: "Usage: /editor <file-path>" })
|
|
20872
21341
|
] })
|
|
@@ -20915,7 +21384,7 @@ Start coordinating now. Triage the changes, then spawn your review team.`
|
|
|
20915
21384
|
if (!description) {
|
|
20916
21385
|
return outBox(
|
|
20917
21386
|
/* @__PURE__ */ jsxs16(Fragment6, { children: [
|
|
20918
|
-
/* @__PURE__ */ jsx18(Text16, { bold: true, color:
|
|
21387
|
+
/* @__PURE__ */ jsx18(Text16, { bold: true, color: COMMAND_HEADER_COLOR, children: "/debug" }),
|
|
20919
21388
|
/* @__PURE__ */ jsx18(Text16, { dimColor: true, children: "Debug Coordinator mode \u2014 spawn workers to investigate, fix & verify in parallel." }),
|
|
20920
21389
|
/* @__PURE__ */ jsx18(Text16, { dimColor: true, children: "Usage: /debug <describe the problem, symptom, or error>" }),
|
|
20921
21390
|
/* @__PURE__ */ jsx18(Text16, { dimColor: true, children: " " }),
|
|
@@ -21096,7 +21565,7 @@ Start coordinating now. Triage the problem, then spawn your research workers.`
|
|
|
21096
21565
|
if (!description) {
|
|
21097
21566
|
return outBox(
|
|
21098
21567
|
/* @__PURE__ */ jsxs16(Fragment6, { children: [
|
|
21099
|
-
/* @__PURE__ */ jsx18(Text16, { bold: true, color:
|
|
21568
|
+
/* @__PURE__ */ jsx18(Text16, { bold: true, color: COMMAND_HEADER_COLOR, children: "/bug" }),
|
|
21100
21569
|
/* @__PURE__ */ jsx18(Text16, { dimColor: true, children: "Alias for /debug \u2014 Debug Coordinator mode." }),
|
|
21101
21570
|
/* @__PURE__ */ jsx18(Text16, { dimColor: true, children: "Usage: /bug <describe the problem> (or use /debug)" })
|
|
21102
21571
|
] })
|
|
@@ -21325,7 +21794,7 @@ Read the relevant files, identify optimization opportunities, and suggest specif
|
|
|
21325
21794
|
if (!target) {
|
|
21326
21795
|
return outBox(
|
|
21327
21796
|
/* @__PURE__ */ jsxs16(Fragment6, { children: [
|
|
21328
|
-
/* @__PURE__ */ jsx18(Text16, { bold: true, color:
|
|
21797
|
+
/* @__PURE__ */ jsx18(Text16, { bold: true, color: COMMAND_HEADER_COLOR, children: "/refactor" }),
|
|
21329
21798
|
/* @__PURE__ */ jsx18(Text16, { dimColor: true, children: "Refactor code to improve structure without changing behavior." }),
|
|
21330
21799
|
/* @__PURE__ */ jsx18(Text16, { dimColor: true, children: "Usage: /refactor <file or description of refactoring>" })
|
|
21331
21800
|
] })
|
|
@@ -21361,7 +21830,7 @@ Read the relevant files, identify optimization opportunities, and suggest specif
|
|
|
21361
21830
|
if (!target) {
|
|
21362
21831
|
return outBox(
|
|
21363
21832
|
/* @__PURE__ */ jsxs16(Fragment6, { children: [
|
|
21364
|
-
/* @__PURE__ */ jsx18(Text16, { bold: true, color:
|
|
21833
|
+
/* @__PURE__ */ jsx18(Text16, { bold: true, color: COMMAND_HEADER_COLOR, children: "/document" }),
|
|
21365
21834
|
/* @__PURE__ */ jsx18(Text16, { dimColor: true, children: "Generate documentation for a file, module, or function." }),
|
|
21366
21835
|
/* @__PURE__ */ jsx18(Text16, { dimColor: true, children: "Usage: /document <file or module> [--format jsdoc|markdown|readme]" })
|
|
21367
21836
|
] })
|
|
@@ -21422,7 +21891,7 @@ Read the relevant files, identify optimization opportunities, and suggest specif
|
|
|
21422
21891
|
const next = setRuntimeConfig({ agentMode: "default" });
|
|
21423
21892
|
return outBox(
|
|
21424
21893
|
/* @__PURE__ */ jsxs16(Fragment6, { children: [
|
|
21425
|
-
/* @__PURE__ */ jsx18(Text16, { bold: true, color:
|
|
21894
|
+
/* @__PURE__ */ jsx18(Text16, { bold: true, color: COMMAND_HEADER_COLOR, children: "Chat Mode" }),
|
|
21426
21895
|
/* @__PURE__ */ jsx18(Text16, { dimColor: true, children: "Switched to conversational chat mode." }),
|
|
21427
21896
|
/* @__PURE__ */ jsx18(Text16, { dimColor: true, children: "The agent will focus on conversation and will not execute code or edit files unless explicitly asked." }),
|
|
21428
21897
|
/* @__PURE__ */ jsx18(Text16, { dimColor: true, children: "Use /code to switch back to code mode." })
|
|
@@ -21433,7 +21902,7 @@ Read the relevant files, identify optimization opportunities, and suggest specif
|
|
|
21433
21902
|
const next = setRuntimeConfig({ agentMode: "default" });
|
|
21434
21903
|
return outBox(
|
|
21435
21904
|
/* @__PURE__ */ jsxs16(Fragment6, { children: [
|
|
21436
|
-
/* @__PURE__ */ jsx18(Text16, { bold: true, color:
|
|
21905
|
+
/* @__PURE__ */ jsx18(Text16, { bold: true, color: COMMAND_HEADER_COLOR, children: "Code Mode" }),
|
|
21437
21906
|
/* @__PURE__ */ jsx18(Text16, { dimColor: true, children: "Switched to code mode." }),
|
|
21438
21907
|
/* @__PURE__ */ jsx18(Text16, { dimColor: true, children: "The agent can now edit files, run commands, and execute code." }),
|
|
21439
21908
|
/* @__PURE__ */ jsx18(Text16, { dimColor: true, children: "Use /chat to switch back to conversational mode." })
|
|
@@ -21443,7 +21912,7 @@ Read the relevant files, identify optimization opportunities, and suggest specif
|
|
|
21443
21912
|
if (cmd === "terminal") {
|
|
21444
21913
|
return outBox(
|
|
21445
21914
|
/* @__PURE__ */ jsxs16(Fragment6, { children: [
|
|
21446
|
-
/* @__PURE__ */ jsx18(Text16, { bold: true, color:
|
|
21915
|
+
/* @__PURE__ */ jsx18(Text16, { bold: true, color: COMMAND_HEADER_COLOR, children: "Terminal" }),
|
|
21447
21916
|
/* @__PURE__ */ jsx18(Text16, { dimColor: true, children: "Interactive terminal sessions are not yet supported as a slash command." }),
|
|
21448
21917
|
/* @__PURE__ */ jsx18(Text16, { dimColor: true, children: "Use the agent's shell_command tool directly in chat instead." }),
|
|
21449
21918
|
/* @__PURE__ */ jsx18(Text16, { dimColor: true, children: 'Example: "run npm test and show me the output"' })
|
|
@@ -21455,7 +21924,7 @@ Read the relevant files, identify optimization opportunities, and suggest specif
|
|
|
21455
21924
|
if (!filePath) {
|
|
21456
21925
|
return outBox(
|
|
21457
21926
|
/* @__PURE__ */ jsxs16(Fragment6, { children: [
|
|
21458
|
-
/* @__PURE__ */ jsx18(Text16, { bold: true, color:
|
|
21927
|
+
/* @__PURE__ */ jsx18(Text16, { bold: true, color: COMMAND_HEADER_COLOR, children: "/file" }),
|
|
21459
21928
|
/* @__PURE__ */ jsx18(Text16, { dimColor: true, children: "Create, open, or navigate to a file." }),
|
|
21460
21929
|
/* @__PURE__ */ jsx18(Text16, { dimColor: true, children: "Usage: /file <path>" })
|
|
21461
21930
|
] })
|
|
@@ -21490,7 +21959,7 @@ Read the relevant files, identify optimization opportunities, and suggest specif
|
|
|
21490
21959
|
if (!query) {
|
|
21491
21960
|
return outBox(
|
|
21492
21961
|
/* @__PURE__ */ jsxs16(Fragment6, { children: [
|
|
21493
|
-
/* @__PURE__ */ jsx18(Text16, { bold: true, color:
|
|
21962
|
+
/* @__PURE__ */ jsx18(Text16, { bold: true, color: COMMAND_HEADER_COLOR, children: "/search" }),
|
|
21494
21963
|
/* @__PURE__ */ jsx18(Text16, { dimColor: true, children: "Search the codebase for a pattern." }),
|
|
21495
21964
|
/* @__PURE__ */ jsx18(Text16, { dimColor: true, children: "Usage: /search <query> [--files <glob>]" })
|
|
21496
21965
|
] })
|
|
@@ -21525,7 +21994,7 @@ Read the relevant files, identify optimization opportunities, and suggest specif
|
|
|
21525
21994
|
if (!sub || sub === "show" || sub === "status") {
|
|
21526
21995
|
return outBox(
|
|
21527
21996
|
/* @__PURE__ */ jsxs16(Fragment6, { children: [
|
|
21528
|
-
/* @__PURE__ */ jsx18(Text16, { bold: true, color:
|
|
21997
|
+
/* @__PURE__ */ jsx18(Text16, { bold: true, color: COMMAND_HEADER_COLOR, children: "Conversation Context" }),
|
|
21529
21998
|
/* @__PURE__ */ jsx18(Text16, { dimColor: true, children: "Use /ctx for detailed context inspection." }),
|
|
21530
21999
|
/* @__PURE__ */ jsx18(Text16, { dimColor: true, children: "Use /compact to manually trigger compaction." }),
|
|
21531
22000
|
/* @__PURE__ */ jsx18(Text16, { dimColor: true, children: "Use /snip to remove old snippets." }),
|
|
@@ -21541,7 +22010,7 @@ Read the relevant files, identify optimization opportunities, and suggest specif
|
|
|
21541
22010
|
if (cmd === "token") {
|
|
21542
22011
|
return outBox(
|
|
21543
22012
|
/* @__PURE__ */ jsxs16(Fragment6, { children: [
|
|
21544
|
-
/* @__PURE__ */ jsx18(Text16, { bold: true, color:
|
|
22013
|
+
/* @__PURE__ */ jsx18(Text16, { bold: true, color: COMMAND_HEADER_COLOR, children: "Token Usage" }),
|
|
21545
22014
|
/* @__PURE__ */ jsx18(Text16, { dimColor: true, children: "Token tracking is available via /stats and /cost for detailed session statistics." }),
|
|
21546
22015
|
/* @__PURE__ */ jsx18(Text16, { dimColor: true, children: "Auto-compact triggers at 180k tokens (threshold: 150k micro, 180k full)." }),
|
|
21547
22016
|
/* @__PURE__ */ jsx18(Text16, { dimColor: true, children: "Target after compact: 100k tokens." })
|
|
@@ -21568,7 +22037,7 @@ Read the relevant files, identify optimization opportunities, and suggest specif
|
|
|
21568
22037
|
const limit = parseInt(args[0] || "20", 10);
|
|
21569
22038
|
return outBox(
|
|
21570
22039
|
/* @__PURE__ */ jsxs16(Fragment6, { children: [
|
|
21571
|
-
/* @__PURE__ */ jsx18(Text16, { bold: true, color:
|
|
22040
|
+
/* @__PURE__ */ jsx18(Text16, { bold: true, color: COMMAND_HEADER_COLOR, children: "Command History" }),
|
|
21572
22041
|
/* @__PURE__ */ jsx18(Text16, { dimColor: true, children: "Recent commands are tracked in the session conversation." }),
|
|
21573
22042
|
/* @__PURE__ */ jsx18(Text16, { dimColor: true, children: "Use /export to export the full conversation as markdown." }),
|
|
21574
22043
|
/* @__PURE__ */ jsx18(Text16, { dimColor: true, children: "Session logs are stored in ~/.bluma/sessions/" })
|
|
@@ -21580,7 +22049,7 @@ Read the relevant files, identify optimization opportunities, and suggest specif
|
|
|
21580
22049
|
if (!sub || sub === "list" || sub === "show") {
|
|
21581
22050
|
return outBox(
|
|
21582
22051
|
/* @__PURE__ */ jsxs16(Fragment6, { children: [
|
|
21583
|
-
/* @__PURE__ */ jsx18(Text16, { bold: true, color:
|
|
22052
|
+
/* @__PURE__ */ jsx18(Text16, { bold: true, color: COMMAND_HEADER_COLOR, children: "Command Aliases" }),
|
|
21584
22053
|
/* @__PURE__ */ jsx18(Text16, { dimColor: true, children: "No custom aliases configured yet." }),
|
|
21585
22054
|
/* @__PURE__ */ jsx18(Text16, { dimColor: true, children: "Usage: /alias add <name> <command>" }),
|
|
21586
22055
|
/* @__PURE__ */ jsx18(Text16, { dimColor: true, children: " /alias remove <name>" })
|
|
@@ -21609,7 +22078,7 @@ Read the relevant files, identify optimization opportunities, and suggest specif
|
|
|
21609
22078
|
if (!name) {
|
|
21610
22079
|
return outBox(
|
|
21611
22080
|
/* @__PURE__ */ jsxs16(Fragment6, { children: [
|
|
21612
|
-
/* @__PURE__ */ jsx18(Text16, { bold: true, color:
|
|
22081
|
+
/* @__PURE__ */ jsx18(Text16, { bold: true, color: COMMAND_HEADER_COLOR, children: "Macros" }),
|
|
21613
22082
|
/* @__PURE__ */ jsx18(Text16, { dimColor: true, children: "No macros configured yet." }),
|
|
21614
22083
|
/* @__PURE__ */ jsx18(Text16, { dimColor: true, children: "Usage: /macro <name>" }),
|
|
21615
22084
|
/* @__PURE__ */ jsx18(Text16, { dimColor: true, children: 'Macros are defined in ~/.bluma/settings.json under "macros".' })
|
|
@@ -21640,7 +22109,7 @@ Read the relevant files, identify optimization opportunities, and suggest specif
|
|
|
21640
22109
|
if (!name) {
|
|
21641
22110
|
return outBox(
|
|
21642
22111
|
/* @__PURE__ */ jsxs16(Fragment6, { children: [
|
|
21643
|
-
/* @__PURE__ */ jsx18(Text16, { bold: true, color:
|
|
22112
|
+
/* @__PURE__ */ jsx18(Text16, { bold: true, color: COMMAND_HEADER_COLOR, children: "Templates" }),
|
|
21644
22113
|
/* @__PURE__ */ jsx18(Text16, { dimColor: true, children: "Create a project from a template." }),
|
|
21645
22114
|
/* @__PURE__ */ jsx18(Text16, { dimColor: true, children: "Usage: /template <name>" }),
|
|
21646
22115
|
/* @__PURE__ */ jsx18(Text16, { dimColor: true, children: "Available templates: react, nextjs, node-api, cli, library" })
|
|
@@ -21700,7 +22169,7 @@ Read the relevant files, identify optimization opportunities, and suggest specif
|
|
|
21700
22169
|
if (cmd === "copy") {
|
|
21701
22170
|
return outBox(
|
|
21702
22171
|
/* @__PURE__ */ jsxs16(Fragment6, { children: [
|
|
21703
|
-
/* @__PURE__ */ jsx18(Text16, { bold: true, color:
|
|
22172
|
+
/* @__PURE__ */ jsx18(Text16, { bold: true, color: COMMAND_HEADER_COLOR, children: "Copy" }),
|
|
21704
22173
|
/* @__PURE__ */ jsx18(Text16, { dimColor: true, children: "Clipboard copy is available via Ctrl+V / Cmd+V paste support." }),
|
|
21705
22174
|
/* @__PURE__ */ jsx18(Text16, { dimColor: true, children: "On Linux, ensure wl-clipboard or xclip is installed for clipboard support." }),
|
|
21706
22175
|
/* @__PURE__ */ jsx18(Text16, { dimColor: true, children: 'Use the agent to copy specific text: "copy the last test output to clipboard"' })
|
|
@@ -21721,16 +22190,16 @@ import latestVersion from "latest-version";
|
|
|
21721
22190
|
import semverGt from "semver/functions/gt.js";
|
|
21722
22191
|
import semverValid from "semver/functions/valid.js";
|
|
21723
22192
|
import { fileURLToPath as fileURLToPath5 } from "url";
|
|
21724
|
-
import
|
|
21725
|
-
import
|
|
22193
|
+
import path43 from "path";
|
|
22194
|
+
import fs38 from "fs";
|
|
21726
22195
|
var BLUMA_PACKAGE_NAME = "@nomad-e/bluma-cli";
|
|
21727
22196
|
function findBlumaPackageJson(startDir) {
|
|
21728
22197
|
let dir = startDir;
|
|
21729
22198
|
for (let i = 0; i < 12; i++) {
|
|
21730
|
-
const candidate =
|
|
21731
|
-
if (
|
|
22199
|
+
const candidate = path43.join(dir, "package.json");
|
|
22200
|
+
if (fs38.existsSync(candidate)) {
|
|
21732
22201
|
try {
|
|
21733
|
-
const raw =
|
|
22202
|
+
const raw = fs38.readFileSync(candidate, "utf8");
|
|
21734
22203
|
const parsed = JSON.parse(raw);
|
|
21735
22204
|
if (parsed?.name === BLUMA_PACKAGE_NAME && parsed?.version) {
|
|
21736
22205
|
return { name: parsed.name, version: String(parsed.version) };
|
|
@@ -21738,7 +22207,7 @@ function findBlumaPackageJson(startDir) {
|
|
|
21738
22207
|
} catch {
|
|
21739
22208
|
}
|
|
21740
22209
|
}
|
|
21741
|
-
const parent =
|
|
22210
|
+
const parent = path43.dirname(dir);
|
|
21742
22211
|
if (parent === dir) break;
|
|
21743
22212
|
dir = parent;
|
|
21744
22213
|
}
|
|
@@ -21747,13 +22216,13 @@ function findBlumaPackageJson(startDir) {
|
|
|
21747
22216
|
function resolveInstalledBlumaPackage() {
|
|
21748
22217
|
const tried = /* @__PURE__ */ new Set();
|
|
21749
22218
|
const tryFrom = (dir) => {
|
|
21750
|
-
const abs =
|
|
22219
|
+
const abs = path43.resolve(dir);
|
|
21751
22220
|
if (tried.has(abs)) return null;
|
|
21752
22221
|
tried.add(abs);
|
|
21753
22222
|
return findBlumaPackageJson(abs);
|
|
21754
22223
|
};
|
|
21755
22224
|
try {
|
|
21756
|
-
const fromBundle = tryFrom(
|
|
22225
|
+
const fromBundle = tryFrom(path43.dirname(fileURLToPath5(import.meta.url)));
|
|
21757
22226
|
if (fromBundle) return fromBundle;
|
|
21758
22227
|
} catch {
|
|
21759
22228
|
}
|
|
@@ -21761,12 +22230,12 @@ function resolveInstalledBlumaPackage() {
|
|
|
21761
22230
|
if (argv1 && !argv1.startsWith("-")) {
|
|
21762
22231
|
try {
|
|
21763
22232
|
let resolved = argv1;
|
|
21764
|
-
if (
|
|
21765
|
-
resolved =
|
|
22233
|
+
if (path43.isAbsolute(argv1) && fs38.existsSync(argv1)) {
|
|
22234
|
+
resolved = fs38.realpathSync(argv1);
|
|
21766
22235
|
} else {
|
|
21767
|
-
resolved =
|
|
22236
|
+
resolved = path43.resolve(process.cwd(), argv1);
|
|
21768
22237
|
}
|
|
21769
|
-
const fromArgv = tryFrom(
|
|
22238
|
+
const fromArgv = tryFrom(path43.dirname(resolved));
|
|
21770
22239
|
if (fromArgv) return fromArgv;
|
|
21771
22240
|
} catch {
|
|
21772
22241
|
}
|
|
@@ -22375,6 +22844,129 @@ var WorkerOverlay = ({
|
|
|
22375
22844
|
) });
|
|
22376
22845
|
};
|
|
22377
22846
|
|
|
22847
|
+
// src/app/ui/prompts/initCommandPrompt.ts
|
|
22848
|
+
function buildInitCommandPrompt(cwd) {
|
|
22849
|
+
return `Your task is to initialize the BluMa project memory system.
|
|
22850
|
+
|
|
22851
|
+
**Project root:** ${cwd}
|
|
22852
|
+
|
|
22853
|
+
The BluMa memory system uses multiple files:
|
|
22854
|
+
|
|
22855
|
+
1. **BLUMA.md** (project root) \u2014 Main project instructions for AI agents
|
|
22856
|
+
2. **.bluma/rules/*.md** \u2014 Granular rules (optional, for specific concerns)
|
|
22857
|
+
3. **BLUMA.local.md** \u2014 Private local instructions (gitignored)
|
|
22858
|
+
|
|
22859
|
+
**Step 1: Explore the project**
|
|
22860
|
+
- List top-level files and directories
|
|
22861
|
+
- Read package.json (name, version, description, scripts, dependencies)
|
|
22862
|
+
- Check tsconfig.json, build configs, test configs
|
|
22863
|
+
- Look at main entry points
|
|
22864
|
+
- Read README.md if it exists
|
|
22865
|
+
- Identify test framework and build system
|
|
22866
|
+
- Note coding patterns (imports, error handling, naming)
|
|
22867
|
+
|
|
22868
|
+
**Step 2: Create BLUMA.md** at ${cwd}/BLUMA.md using this EXACT format:
|
|
22869
|
+
|
|
22870
|
+
\`\`\`markdown
|
|
22871
|
+
# BLUMA.md
|
|
22872
|
+
|
|
22873
|
+
This file provides guidance to BluMa CLI when working with code in this repository.
|
|
22874
|
+
|
|
22875
|
+
## Build & Development Commands
|
|
22876
|
+
|
|
22877
|
+
\`\`\`bash
|
|
22878
|
+
# Install dependencies
|
|
22879
|
+
npm install
|
|
22880
|
+
|
|
22881
|
+
# Build the project
|
|
22882
|
+
npm run build
|
|
22883
|
+
|
|
22884
|
+
# Run in development
|
|
22885
|
+
npm start
|
|
22886
|
+
|
|
22887
|
+
# Run tests
|
|
22888
|
+
npm test
|
|
22889
|
+
|
|
22890
|
+
# Run tests in watch mode
|
|
22891
|
+
npm run test:watch
|
|
22892
|
+
|
|
22893
|
+
# Lint and fix
|
|
22894
|
+
npm run lint
|
|
22895
|
+
npm run lint:fix
|
|
22896
|
+
\`\`\`
|
|
22897
|
+
|
|
22898
|
+
## Code Architecture
|
|
22899
|
+
|
|
22900
|
+
<2-3 paragraphs: high-level architecture, main components, data flow, key abstractions. Focus on what an AI agent needs to navigate and modify the codebase.>
|
|
22901
|
+
|
|
22902
|
+
## Key Directories
|
|
22903
|
+
|
|
22904
|
+
- \`src/\` \u2014 <description>
|
|
22905
|
+
- \`tests/\` \u2014 <description>
|
|
22906
|
+
- \`scripts/\` \u2014 <description>
|
|
22907
|
+
|
|
22908
|
+
## Key Files
|
|
22909
|
+
|
|
22910
|
+
- \`package.json\` \u2014 Project metadata, scripts, dependencies
|
|
22911
|
+
- \`tsconfig.json\` \u2014 TypeScript configuration
|
|
22912
|
+
- \`src/index.ts\` \u2014 Application entry point
|
|
22913
|
+
|
|
22914
|
+
## Testing
|
|
22915
|
+
|
|
22916
|
+
- Test framework: <jest/vitest/etc>
|
|
22917
|
+
- Test location: \`tests/\`
|
|
22918
|
+
- Run tests: \`npm test\`
|
|
22919
|
+
|
|
22920
|
+
## Code Conventions
|
|
22921
|
+
|
|
22922
|
+
- **Language**: TypeScript/JavaScript
|
|
22923
|
+
- **Module system**: ES Modules or CommonJS
|
|
22924
|
+
- **Indentation**: 2 spaces
|
|
22925
|
+
- **Naming**: camelCase functions, PascalCase classes
|
|
22926
|
+
- **Error handling**: <patterns>
|
|
22927
|
+
- **Git commits**: Conventional Commits (feat:, fix:, docs:)
|
|
22928
|
+
|
|
22929
|
+
## Notes for BluMa
|
|
22930
|
+
|
|
22931
|
+
<Important context for effective agent work \u2014 non-obvious patterns, critical files, deployment procedures>
|
|
22932
|
+
\`\`\`
|
|
22933
|
+
|
|
22934
|
+
**Step 3: Create .bluma/rules/ directory** with an example rule file at ${cwd}/.bluma/rules/coding-standards.md:
|
|
22935
|
+
|
|
22936
|
+
\`\`\`markdown
|
|
22937
|
+
# Coding Standards
|
|
22938
|
+
|
|
22939
|
+
## TypeScript
|
|
22940
|
+
- Use strict mode
|
|
22941
|
+
- Prefer interfaces over type aliases
|
|
22942
|
+
- Export types explicitly
|
|
22943
|
+
|
|
22944
|
+
## Testing
|
|
22945
|
+
- Write tests for all new features
|
|
22946
|
+
- Use descriptive test names
|
|
22947
|
+
- Mock external dependencies
|
|
22948
|
+
|
|
22949
|
+
## Git
|
|
22950
|
+
- Use Conventional Commits
|
|
22951
|
+
- One logical change per commit
|
|
22952
|
+
\`\`\`
|
|
22953
|
+
|
|
22954
|
+
**Step 4: Create BLUMA.local.md** at ${cwd}/BLUMA.local.md (private, project-specific):
|
|
22955
|
+
|
|
22956
|
+
\`\`\`markdown
|
|
22957
|
+
# BLUMA.local.md
|
|
22958
|
+
|
|
22959
|
+
Private instructions for BluMa in this project.
|
|
22960
|
+
This file is gitignored and should not be committed.
|
|
22961
|
+
|
|
22962
|
+
Add project-specific notes here that are not suitable for the public BLUMA.md.
|
|
22963
|
+
\`\`\`
|
|
22964
|
+
|
|
22965
|
+
**Step 5: Add BLUMA.local.md to .gitignore** if .gitignore exists.
|
|
22966
|
+
|
|
22967
|
+
IMPORTANT: Use the file_write tool to create all these files. Be thorough but concise. Infer conventions from the actual codebase.`;
|
|
22968
|
+
}
|
|
22969
|
+
|
|
22378
22970
|
// src/app/ui/App.tsx
|
|
22379
22971
|
import { jsx as jsx28, jsxs as jsxs25 } from "react/jsx-runtime";
|
|
22380
22972
|
var HISTORY_EMERGENCY_LIMIT = 3;
|
|
@@ -22406,6 +22998,7 @@ var AGENT_WORK_COMMANDS = /* @__PURE__ */ new Set([
|
|
|
22406
22998
|
"chat"
|
|
22407
22999
|
]);
|
|
22408
23000
|
var BLOCKING_COMMANDS = /* @__PURE__ */ new Set(["init"]);
|
|
23001
|
+
var COMMAND_HEADER_COLOR2 = BLUMA_TERMINAL.accent;
|
|
22409
23002
|
function nextHistoryId(items) {
|
|
22410
23003
|
if (items.length === 0) return HEADER_PANEL_HISTORY_ID + 1;
|
|
22411
23004
|
let maxId = HEADER_PANEL_HISTORY_ID;
|
|
@@ -22599,126 +23192,26 @@ var AppComponent = ({ eventBus, sessionId, cliVersion }) => {
|
|
|
22599
23192
|
}
|
|
22600
23193
|
];
|
|
22601
23194
|
});
|
|
22602
|
-
const initPrompt =
|
|
22603
|
-
|
|
22604
|
-
|
|
22605
|
-
|
|
22606
|
-
|
|
22607
|
-
|
|
22608
|
-
|
|
22609
|
-
|
|
22610
|
-
|
|
22611
|
-
|
|
22612
|
-
|
|
22613
|
-
|
|
22614
|
-
|
|
22615
|
-
|
|
22616
|
-
|
|
22617
|
-
|
|
22618
|
-
|
|
22619
|
-
|
|
22620
|
-
|
|
22621
|
-
|
|
22622
|
-
|
|
22623
|
-
\`\`\`markdown
|
|
22624
|
-
# BLUMA.md
|
|
22625
|
-
|
|
22626
|
-
This file provides guidance to BluMa CLI when working with code in this repository.
|
|
22627
|
-
|
|
22628
|
-
## Build & Development Commands
|
|
22629
|
-
|
|
22630
|
-
\`\`\`bash
|
|
22631
|
-
# Install dependencies
|
|
22632
|
-
npm install
|
|
22633
|
-
|
|
22634
|
-
# Build the project
|
|
22635
|
-
npm run build
|
|
22636
|
-
|
|
22637
|
-
# Run in development
|
|
22638
|
-
npm start
|
|
22639
|
-
|
|
22640
|
-
# Run tests
|
|
22641
|
-
npm test
|
|
22642
|
-
|
|
22643
|
-
# Run tests in watch mode
|
|
22644
|
-
npm run test:watch
|
|
22645
|
-
|
|
22646
|
-
# Lint and fix
|
|
22647
|
-
npm run lint
|
|
22648
|
-
npm run lint:fix
|
|
22649
|
-
\`\`\`
|
|
22650
|
-
|
|
22651
|
-
## Code Architecture
|
|
22652
|
-
|
|
22653
|
-
<2-3 paragraphs: high-level architecture, main components, data flow, key abstractions. Focus on what an AI agent needs to navigate and modify the codebase.>
|
|
22654
|
-
|
|
22655
|
-
## Key Directories
|
|
22656
|
-
|
|
22657
|
-
- \`src/\` \u2014 <description>
|
|
22658
|
-
- \`tests/\` \u2014 <description>
|
|
22659
|
-
- \`scripts/\` \u2014 <description>
|
|
22660
|
-
|
|
22661
|
-
## Key Files
|
|
22662
|
-
|
|
22663
|
-
- \`package.json\` \u2014 Project metadata, scripts, dependencies
|
|
22664
|
-
- \`tsconfig.json\` \u2014 TypeScript configuration
|
|
22665
|
-
- \`src/index.ts\` \u2014 Application entry point
|
|
22666
|
-
|
|
22667
|
-
## Testing
|
|
22668
|
-
|
|
22669
|
-
- Test framework: <jest/vitest/etc>
|
|
22670
|
-
- Test location: \`tests/\`
|
|
22671
|
-
- Run tests: \`npm test\`
|
|
22672
|
-
|
|
22673
|
-
## Code Conventions
|
|
22674
|
-
|
|
22675
|
-
- **Language**: TypeScript/JavaScript
|
|
22676
|
-
- **Module system**: ES Modules or CommonJS
|
|
22677
|
-
- **Indentation**: 2 spaces
|
|
22678
|
-
- **Naming**: camelCase functions, PascalCase classes
|
|
22679
|
-
- **Error handling**: <patterns>
|
|
22680
|
-
- **Git commits**: Conventional Commits (feat:, fix:, docs:)
|
|
22681
|
-
|
|
22682
|
-
## Notes for BluMa
|
|
22683
|
-
|
|
22684
|
-
<Important context for effective agent work \u2014 non-obvious patterns, critical files, deployment procedures>
|
|
22685
|
-
\`\`\`
|
|
22686
|
-
|
|
22687
|
-
**Step 3: Create .bluma/rules/ directory** with an example rule file at ${cwd}/.bluma/rules/coding-standards.md:
|
|
22688
|
-
|
|
22689
|
-
\`\`\`markdown
|
|
22690
|
-
# Coding Standards
|
|
22691
|
-
|
|
22692
|
-
## TypeScript
|
|
22693
|
-
- Use strict mode
|
|
22694
|
-
- Prefer interfaces over type aliases
|
|
22695
|
-
- Export types explicitly
|
|
22696
|
-
|
|
22697
|
-
## Testing
|
|
22698
|
-
- Write tests for all new features
|
|
22699
|
-
- Use descriptive test names
|
|
22700
|
-
- Mock external dependencies
|
|
22701
|
-
|
|
22702
|
-
## Git
|
|
22703
|
-
- Use Conventional Commits
|
|
22704
|
-
- One logical change per commit
|
|
22705
|
-
\`\`\`
|
|
22706
|
-
|
|
22707
|
-
**Step 4: Create BLUMA.local.md** at ${cwd}/BLUMA.local.md (private, project-specific):
|
|
22708
|
-
|
|
22709
|
-
\`\`\`markdown
|
|
22710
|
-
# BLUMA.local.md
|
|
22711
|
-
|
|
22712
|
-
Private instructions for BluMa in this project.
|
|
22713
|
-
This file is gitignored and should not be committed.
|
|
22714
|
-
|
|
22715
|
-
Add project-specific notes here that are not suitable for the public BLUMA.md.
|
|
22716
|
-
\`\`\`
|
|
22717
|
-
|
|
22718
|
-
**Step 5: Add BLUMA.local.md to .gitignore** if .gitignore exists.
|
|
22719
|
-
|
|
22720
|
-
IMPORTANT: Use the file_write tool to create all these files. Be thorough but concise. Infer conventions from the actual codebase.`;
|
|
22721
|
-
agentInstance.current.processTurn({ content: initPrompt });
|
|
23195
|
+
const initPrompt = buildInitCommandPrompt(cwd);
|
|
23196
|
+
try {
|
|
23197
|
+
agentInstance.current.processTurn({ content: initPrompt });
|
|
23198
|
+
} catch (error) {
|
|
23199
|
+
setIsProcessing(false);
|
|
23200
|
+
setIsInitAgentActive(false);
|
|
23201
|
+
setHistory((prev) => {
|
|
23202
|
+
const id = nextHistoryId(prev);
|
|
23203
|
+
return [
|
|
23204
|
+
...prev,
|
|
23205
|
+
{
|
|
23206
|
+
id,
|
|
23207
|
+
component: /* @__PURE__ */ jsxs25(ChatMeta, { children: [
|
|
23208
|
+
"Failed to initialize: ",
|
|
23209
|
+
error instanceof Error ? error.message : String(error)
|
|
23210
|
+
] })
|
|
23211
|
+
}
|
|
23212
|
+
];
|
|
23213
|
+
});
|
|
23214
|
+
}
|
|
22722
23215
|
return;
|
|
22723
23216
|
}
|
|
22724
23217
|
if (isProcessing && !isSlashRoutingLine(trimmedForSlash)) {
|
|
@@ -23479,9 +23972,9 @@ async function runAgentMode() {
|
|
|
23479
23972
|
try {
|
|
23480
23973
|
if (inputFileIndex !== -1 && args[inputFileIndex + 1]) {
|
|
23481
23974
|
const filePath = args[inputFileIndex + 1];
|
|
23482
|
-
rawPayload =
|
|
23975
|
+
rawPayload = fs39.readFileSync(filePath, "utf-8");
|
|
23483
23976
|
} else {
|
|
23484
|
-
rawPayload =
|
|
23977
|
+
rawPayload = fs39.readFileSync(0, "utf-8");
|
|
23485
23978
|
}
|
|
23486
23979
|
} catch (err) {
|
|
23487
23980
|
writeAgentEvent(registrySessionId, {
|
|
@@ -23679,9 +24172,9 @@ async function runAgentMode() {
|
|
|
23679
24172
|
}
|
|
23680
24173
|
function readCliPackageVersion() {
|
|
23681
24174
|
try {
|
|
23682
|
-
const base =
|
|
23683
|
-
const pkgPath =
|
|
23684
|
-
const j = JSON.parse(
|
|
24175
|
+
const base = path44.dirname(fileURLToPath6(import.meta.url));
|
|
24176
|
+
const pkgPath = path44.join(base, "..", "package.json");
|
|
24177
|
+
const j = JSON.parse(fs39.readFileSync(pkgPath, "utf8"));
|
|
23685
24178
|
return String(j.version || "0.0.0");
|
|
23686
24179
|
} catch {
|
|
23687
24180
|
return "0.0.0";
|
|
@@ -23804,7 +24297,7 @@ function startBackgroundAgent() {
|
|
|
23804
24297
|
process.exit(1);
|
|
23805
24298
|
}
|
|
23806
24299
|
const filePath = args[inputFileIndex + 1];
|
|
23807
|
-
const rawPayload =
|
|
24300
|
+
const rawPayload = fs39.readFileSync(filePath, "utf-8");
|
|
23808
24301
|
const envelope = JSON.parse(rawPayload);
|
|
23809
24302
|
const sessionId = envelope.session_id || envelope.message_id || uuidv412();
|
|
23810
24303
|
registerSession({
|