@nomad-e/bluma-cli 0.11.2 → 0.13.0
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/config/skills/factorai-sh/SKILL.md +5 -4
- package/dist/main.js +775 -1018
- package/package.json +1 -1
package/dist/main.js
CHANGED
|
@@ -2684,11 +2684,11 @@ var NodeFsOperations = {
|
|
|
2684
2684
|
if (fd) fs.closeSync(fd);
|
|
2685
2685
|
}
|
|
2686
2686
|
},
|
|
2687
|
-
appendFileSync(
|
|
2688
|
-
const _ = slowLogging`fs.appendFileSync(${
|
|
2687
|
+
appendFileSync(path58, data, options) {
|
|
2688
|
+
const _ = slowLogging`fs.appendFileSync(${path58}, ${data.length} chars)`;
|
|
2689
2689
|
if (options?.mode !== void 0) {
|
|
2690
2690
|
try {
|
|
2691
|
-
const fd = fs.openSync(
|
|
2691
|
+
const fd = fs.openSync(path58, "ax", options.mode);
|
|
2692
2692
|
try {
|
|
2693
2693
|
fs.appendFileSync(fd, data);
|
|
2694
2694
|
} finally {
|
|
@@ -2699,35 +2699,35 @@ var NodeFsOperations = {
|
|
|
2699
2699
|
if (getErrnoCode(e) !== "EEXIST") throw e;
|
|
2700
2700
|
}
|
|
2701
2701
|
}
|
|
2702
|
-
fs.appendFileSync(
|
|
2702
|
+
fs.appendFileSync(path58, data);
|
|
2703
2703
|
},
|
|
2704
2704
|
copyFileSync(src, dest) {
|
|
2705
2705
|
const _ = slowLogging`fs.copyFileSync(${src} → ${dest})`;
|
|
2706
2706
|
fs.copyFileSync(src, dest);
|
|
2707
2707
|
},
|
|
2708
|
-
unlinkSync(
|
|
2709
|
-
const _ = slowLogging`fs.unlinkSync(${
|
|
2710
|
-
fs.unlinkSync(
|
|
2708
|
+
unlinkSync(path58) {
|
|
2709
|
+
const _ = slowLogging`fs.unlinkSync(${path58})`;
|
|
2710
|
+
fs.unlinkSync(path58);
|
|
2711
2711
|
},
|
|
2712
2712
|
renameSync(oldPath, newPath) {
|
|
2713
2713
|
const _ = slowLogging`fs.renameSync(${oldPath} → ${newPath})`;
|
|
2714
2714
|
fs.renameSync(oldPath, newPath);
|
|
2715
2715
|
},
|
|
2716
|
-
linkSync(target,
|
|
2717
|
-
const _ = slowLogging`fs.linkSync(${target} → ${
|
|
2718
|
-
fs.linkSync(target,
|
|
2716
|
+
linkSync(target, path58) {
|
|
2717
|
+
const _ = slowLogging`fs.linkSync(${target} → ${path58})`;
|
|
2718
|
+
fs.linkSync(target, path58);
|
|
2719
2719
|
},
|
|
2720
|
-
symlinkSync(target,
|
|
2721
|
-
const _ = slowLogging`fs.symlinkSync(${target} → ${
|
|
2722
|
-
fs.symlinkSync(target,
|
|
2720
|
+
symlinkSync(target, path58, type) {
|
|
2721
|
+
const _ = slowLogging`fs.symlinkSync(${target} → ${path58})`;
|
|
2722
|
+
fs.symlinkSync(target, path58, type);
|
|
2723
2723
|
},
|
|
2724
|
-
readlinkSync(
|
|
2725
|
-
const _ = slowLogging`fs.readlinkSync(${
|
|
2726
|
-
return fs.readlinkSync(
|
|
2724
|
+
readlinkSync(path58) {
|
|
2725
|
+
const _ = slowLogging`fs.readlinkSync(${path58})`;
|
|
2726
|
+
return fs.readlinkSync(path58);
|
|
2727
2727
|
},
|
|
2728
|
-
realpathSync(
|
|
2729
|
-
const _ = slowLogging`fs.realpathSync(${
|
|
2730
|
-
return fs.realpathSync(
|
|
2728
|
+
realpathSync(path58) {
|
|
2729
|
+
const _ = slowLogging`fs.realpathSync(${path58})`;
|
|
2730
|
+
return fs.realpathSync(path58).normalize("NFC");
|
|
2731
2731
|
},
|
|
2732
2732
|
mkdirSync(dirPath, options) {
|
|
2733
2733
|
const _ = slowLogging`fs.mkdirSync(${dirPath})`;
|
|
@@ -2760,12 +2760,12 @@ var NodeFsOperations = {
|
|
|
2760
2760
|
const _ = slowLogging`fs.rmdirSync(${dirPath})`;
|
|
2761
2761
|
fs.rmdirSync(dirPath);
|
|
2762
2762
|
},
|
|
2763
|
-
rmSync(
|
|
2764
|
-
const _ = slowLogging`fs.rmSync(${
|
|
2765
|
-
fs.rmSync(
|
|
2763
|
+
rmSync(path58, options) {
|
|
2764
|
+
const _ = slowLogging`fs.rmSync(${path58})`;
|
|
2765
|
+
fs.rmSync(path58, options);
|
|
2766
2766
|
},
|
|
2767
|
-
createWriteStream(
|
|
2768
|
-
return fs.createWriteStream(
|
|
2767
|
+
createWriteStream(path58) {
|
|
2768
|
+
return fs.createWriteStream(path58);
|
|
2769
2769
|
},
|
|
2770
2770
|
async readFileBytes(fsPath, maxBytes) {
|
|
2771
2771
|
if (maxBytes === void 0) {
|
|
@@ -2872,12 +2872,12 @@ function shouldLogDebugMessage(message2) {
|
|
|
2872
2872
|
var hasFormattedOutput = false;
|
|
2873
2873
|
var debugWriter = null;
|
|
2874
2874
|
var pendingWrite = Promise.resolve();
|
|
2875
|
-
async function appendAsync(needMkdir, dir,
|
|
2875
|
+
async function appendAsync(needMkdir, dir, path58, content) {
|
|
2876
2876
|
if (needMkdir) {
|
|
2877
2877
|
await mkdir(dir, { recursive: true }).catch(() => {
|
|
2878
2878
|
});
|
|
2879
2879
|
}
|
|
2880
|
-
await appendFile(
|
|
2880
|
+
await appendFile(path58, content);
|
|
2881
2881
|
void updateLatestDebugLogSymlink();
|
|
2882
2882
|
}
|
|
2883
2883
|
function noop() {
|
|
@@ -2887,8 +2887,8 @@ function getDebugWriter() {
|
|
|
2887
2887
|
let ensuredDir = null;
|
|
2888
2888
|
debugWriter = createBufferedWriter({
|
|
2889
2889
|
writeFn: (content) => {
|
|
2890
|
-
const
|
|
2891
|
-
const dir = dirname(
|
|
2890
|
+
const path58 = getDebugLogPath();
|
|
2891
|
+
const dir = dirname(path58);
|
|
2892
2892
|
const needMkdir = ensuredDir !== dir;
|
|
2893
2893
|
ensuredDir = dir;
|
|
2894
2894
|
if (isDebugMode()) {
|
|
@@ -2898,11 +2898,11 @@ function getDebugWriter() {
|
|
|
2898
2898
|
} catch {
|
|
2899
2899
|
}
|
|
2900
2900
|
}
|
|
2901
|
-
getFsImplementation().appendFileSync(
|
|
2901
|
+
getFsImplementation().appendFileSync(path58, content);
|
|
2902
2902
|
void updateLatestDebugLogSymlink();
|
|
2903
2903
|
return;
|
|
2904
2904
|
}
|
|
2905
|
-
pendingWrite = pendingWrite.then(appendAsync.bind(null, needMkdir, dir,
|
|
2905
|
+
pendingWrite = pendingWrite.then(appendAsync.bind(null, needMkdir, dir, path58, content)).catch(noop);
|
|
2906
2906
|
},
|
|
2907
2907
|
flushIntervalMs: 1e3,
|
|
2908
2908
|
maxBufferSize: 100,
|
|
@@ -9089,8 +9089,8 @@ import codeExcerpt from "code-excerpt";
|
|
|
9089
9089
|
import { readFileSync as readFileSync2 } from "fs";
|
|
9090
9090
|
import StackUtils from "stack-utils";
|
|
9091
9091
|
import { jsx as jsx6, jsxs } from "react/jsx-runtime";
|
|
9092
|
-
var cleanupPath = (
|
|
9093
|
-
return
|
|
9092
|
+
var cleanupPath = (path58) => {
|
|
9093
|
+
return path58?.replace(`file://${process.cwd()}/`, "");
|
|
9094
9094
|
};
|
|
9095
9095
|
var stackUtils;
|
|
9096
9096
|
function getStackUtils() {
|
|
@@ -13067,9 +13067,9 @@ var getInstance = (stdout, createInstance) => {
|
|
|
13067
13067
|
|
|
13068
13068
|
// src/main.ts
|
|
13069
13069
|
import { EventEmitter as EventEmitter7 } from "events";
|
|
13070
|
-
import
|
|
13071
|
-
import
|
|
13072
|
-
import { fileURLToPath as
|
|
13070
|
+
import fs51 from "fs";
|
|
13071
|
+
import path57 from "path";
|
|
13072
|
+
import { fileURLToPath as fileURLToPath8 } from "url";
|
|
13073
13073
|
import { spawn as spawn6 } from "child_process";
|
|
13074
13074
|
import { v4 as uuidv412 } from "uuid";
|
|
13075
13075
|
import chalk3 from "chalk";
|
|
@@ -13962,13 +13962,13 @@ function cancelSlashSubmenu() {
|
|
|
13962
13962
|
|
|
13963
13963
|
// src/app/agent/agent.ts
|
|
13964
13964
|
import * as dotenv from "dotenv";
|
|
13965
|
-
import
|
|
13965
|
+
import path48 from "path";
|
|
13966
13966
|
import os30 from "os";
|
|
13967
13967
|
|
|
13968
13968
|
// src/app/agent/tool_invoker.ts
|
|
13969
|
-
import { promises as
|
|
13970
|
-
import
|
|
13971
|
-
import { fileURLToPath } from "url";
|
|
13969
|
+
import { promises as fs26 } from "fs";
|
|
13970
|
+
import path29 from "path";
|
|
13971
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
13972
13972
|
|
|
13973
13973
|
// src/app/agent/tools/EditTool/EditTool.ts
|
|
13974
13974
|
init_sandbox_policy();
|
|
@@ -18664,8 +18664,9 @@ async function context_collapse(args = {}) {
|
|
|
18664
18664
|
|
|
18665
18665
|
// src/app/agent/tools/CreateNextAppTool/CreateNextAppTool.ts
|
|
18666
18666
|
init_sandbox_policy();
|
|
18667
|
-
import { promises as
|
|
18668
|
-
import
|
|
18667
|
+
import { promises as fs24 } from "fs";
|
|
18668
|
+
import path26 from "path";
|
|
18669
|
+
import { fileURLToPath } from "url";
|
|
18669
18670
|
|
|
18670
18671
|
// src/app/agent/tools/ShellCommandTool/ShellCommandTool.ts
|
|
18671
18672
|
init_sandbox_policy();
|
|
@@ -18966,516 +18967,271 @@ function formatResult(result, verbose) {
|
|
|
18966
18967
|
return JSON.stringify(output, null, 2);
|
|
18967
18968
|
}
|
|
18968
18969
|
|
|
18969
|
-
// src/app/agent/tools/CreateNextAppTool/
|
|
18970
|
-
|
|
18971
|
-
|
|
18972
|
-
|
|
18973
|
-
|
|
18974
|
-
"
|
|
18975
|
-
|
|
18976
|
-
"
|
|
18977
|
-
"
|
|
18978
|
-
"
|
|
18979
|
-
"
|
|
18980
|
-
|
|
18981
|
-
|
|
18982
|
-
"
|
|
18983
|
-
"
|
|
18984
|
-
"
|
|
18985
|
-
|
|
18986
|
-
|
|
18987
|
-
|
|
18988
|
-
"
|
|
18989
|
-
"
|
|
18990
|
-
"
|
|
18991
|
-
"
|
|
18992
|
-
"
|
|
18993
|
-
"
|
|
18994
|
-
"
|
|
18995
|
-
"
|
|
18996
|
-
|
|
18997
|
-
|
|
18998
|
-
|
|
18999
|
-
|
|
19000
|
-
|
|
19001
|
-
|
|
19002
|
-
"
|
|
19003
|
-
"
|
|
19004
|
-
"
|
|
19005
|
-
"
|
|
19006
|
-
"
|
|
19007
|
-
"
|
|
19008
|
-
"
|
|
19009
|
-
"
|
|
19010
|
-
"
|
|
19011
|
-
"
|
|
19012
|
-
"
|
|
19013
|
-
"
|
|
19014
|
-
|
|
19015
|
-
|
|
19016
|
-
|
|
19017
|
-
|
|
19018
|
-
"
|
|
19019
|
-
|
|
18970
|
+
// src/app/agent/tools/CreateNextAppTool/scaffold_agent_guide.ts
|
|
18971
|
+
import { promises as fs23 } from "fs";
|
|
18972
|
+
import path25 from "path";
|
|
18973
|
+
var DESIGN_DOC_FILENAME = "DESIGN.md";
|
|
18974
|
+
var UI_GROUPS = {
|
|
18975
|
+
"Actions & inputs": [
|
|
18976
|
+
"button",
|
|
18977
|
+
"input",
|
|
18978
|
+
"textarea",
|
|
18979
|
+
"checkbox",
|
|
18980
|
+
"radio-group",
|
|
18981
|
+
"switch",
|
|
18982
|
+
"slider",
|
|
18983
|
+
"select",
|
|
18984
|
+
"input-otp",
|
|
18985
|
+
"form",
|
|
18986
|
+
"label"
|
|
18987
|
+
],
|
|
18988
|
+
"Layout & navigation": [
|
|
18989
|
+
"card",
|
|
18990
|
+
"separator",
|
|
18991
|
+
"tabs",
|
|
18992
|
+
"accordion",
|
|
18993
|
+
"collapsible",
|
|
18994
|
+
"breadcrumb",
|
|
18995
|
+
"navigation-menu",
|
|
18996
|
+
"menubar",
|
|
18997
|
+
"pagination",
|
|
18998
|
+
"resizable",
|
|
18999
|
+
"scroll-area",
|
|
19000
|
+
"aspect-ratio"
|
|
19001
|
+
],
|
|
19002
|
+
"Overlays & feedback": [
|
|
19003
|
+
"dialog",
|
|
19004
|
+
"alert-dialog",
|
|
19005
|
+
"sheet",
|
|
19006
|
+
"drawer",
|
|
19007
|
+
"popover",
|
|
19008
|
+
"hover-card",
|
|
19009
|
+
"tooltip",
|
|
19010
|
+
"alert",
|
|
19011
|
+
"toast",
|
|
19012
|
+
"toaster",
|
|
19013
|
+
"sonner",
|
|
19014
|
+
"progress",
|
|
19015
|
+
"skeleton"
|
|
19016
|
+
],
|
|
19017
|
+
"Data & media": [
|
|
19018
|
+
"table",
|
|
19019
|
+
"chart",
|
|
19020
|
+
"calendar",
|
|
19021
|
+
"carousel",
|
|
19022
|
+
"avatar",
|
|
19023
|
+
"badge",
|
|
19024
|
+
"command",
|
|
19025
|
+
"context-menu",
|
|
19026
|
+
"dropdown-menu"
|
|
19027
|
+
]
|
|
19028
|
+
};
|
|
19029
|
+
async function listUiComponents(projectPath) {
|
|
19030
|
+
const uiDir = path25.join(projectPath, "components", "ui");
|
|
19031
|
+
try {
|
|
19032
|
+
const entries = await fs23.readdir(uiDir);
|
|
19033
|
+
return entries.filter((f) => f.endsWith(".jsx") || f.endsWith(".tsx")).map((f) => f.replace(/\.(jsx|tsx)$/, "")).sort();
|
|
19034
|
+
} catch {
|
|
19035
|
+
return [];
|
|
19036
|
+
}
|
|
19037
|
+
}
|
|
19038
|
+
function hslTokenRows() {
|
|
19039
|
+
return [
|
|
19040
|
+
{ name: "Canvas", varName: "--background", role: "Page background (`bg-background`)" },
|
|
19041
|
+
{ name: "Frost", varName: "--foreground", role: "Primary text (`text-foreground`)" },
|
|
19042
|
+
{ name: "Card", varName: "--card", role: "Elevated panels (`bg-card`, `text-card-foreground`)" },
|
|
19043
|
+
{ name: "Muted", varName: "--muted", role: "Secondary surfaces (`bg-muted`, `text-muted-foreground`)" },
|
|
19044
|
+
{ name: "Primary", varName: "--primary", role: "Primary actions (`bg-primary`, `text-primary-foreground`)" },
|
|
19045
|
+
{ name: "Accent", varName: "--accent", role: "Hover states (`bg-accent`, `text-accent-foreground`)" },
|
|
19046
|
+
{ name: "Border", varName: "--border", role: "Dividers and outlines (`border-border`)" },
|
|
19047
|
+
{ name: "Ring", varName: "--ring", role: "Focus rings (`ring-ring`)" },
|
|
19048
|
+
{ name: "Destructive", varName: "--destructive", role: "Errors and destructive actions" }
|
|
19049
|
+
];
|
|
19050
|
+
}
|
|
19051
|
+
function componentInventoryMarkdown(components) {
|
|
19052
|
+
const lines = [];
|
|
19053
|
+
for (const [group, names] of Object.entries(UI_GROUPS)) {
|
|
19054
|
+
const present = names.filter((n) => components.includes(n));
|
|
19055
|
+
if (present.length === 0) continue;
|
|
19056
|
+
lines.push(`### ${group}`);
|
|
19057
|
+
for (const n of present) {
|
|
19058
|
+
lines.push(`- \`@/components/ui/${n}\` \u2014 \`${toPascal(n)}\``);
|
|
19020
19059
|
}
|
|
19021
|
-
|
|
19022
|
-
|
|
19023
|
-
|
|
19060
|
+
lines.push("");
|
|
19061
|
+
}
|
|
19062
|
+
const uncategorized = components.filter(
|
|
19063
|
+
(c) => !Object.values(UI_GROUPS).flat().includes(c)
|
|
19064
|
+
);
|
|
19065
|
+
if (uncategorized.length > 0) {
|
|
19066
|
+
lines.push("### Other");
|
|
19067
|
+
for (const n of uncategorized) {
|
|
19068
|
+
lines.push(`- \`@/components/ui/${n}\``);
|
|
19069
|
+
}
|
|
19070
|
+
lines.push("");
|
|
19071
|
+
}
|
|
19072
|
+
return lines.join("\n");
|
|
19024
19073
|
}
|
|
19025
|
-
|
|
19026
|
-
"
|
|
19027
|
-
const nextConfig = {
|
|
19028
|
-
output: 'standalone',
|
|
19029
|
-
reactStrictMode: true,
|
|
19074
|
+
function toPascal(slug) {
|
|
19075
|
+
return slug.split("-").map((p) => p.charAt(0).toUpperCase() + p.slice(1)).join("");
|
|
19030
19076
|
}
|
|
19077
|
+
async function buildScaffoldAgentGuide(projectName, projectPath) {
|
|
19078
|
+
const uiComponents = await listUiComponents(projectPath);
|
|
19079
|
+
const uiCount = uiComponents.length;
|
|
19080
|
+
const tokenTable = hslTokenRows().map((t) => `| ${t.name} | \`hsl(var(${t.varName}))\` | ${t.role} |`).join("\n");
|
|
19081
|
+
const markdown = `# ${projectName} \u2014 FactorAI App Scaffold
|
|
19031
19082
|
|
|
19032
|
-
|
|
19033
|
-
`,
|
|
19034
|
-
"tailwind.config.ts": `import type { Config } from 'tailwindcss'
|
|
19083
|
+
> Dark command-center aesthetic: near-black canvas, high-contrast type, restrained accents. Default home is a **Refero-style landing** (hero, feature cards, nav) \u2014 customize \`app/page.tsx\` for your product.
|
|
19035
19084
|
|
|
19036
|
-
|
|
19037
|
-
|
|
19038
|
-
|
|
19039
|
-
'./components/**/*.{js,ts,jsx,tsx,mdx}',
|
|
19040
|
-
'./app/**/*.{js,ts,jsx,tsx,mdx}',
|
|
19041
|
-
],
|
|
19042
|
-
theme: {
|
|
19043
|
-
extend: {
|
|
19044
|
-
backgroundImage: {
|
|
19045
|
-
'gradient-radial': 'radial-gradient(var(--tw-gradient-stops))',
|
|
19046
|
-
'gradient-conic':
|
|
19047
|
-
'conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))',
|
|
19048
|
-
},
|
|
19049
|
-
},
|
|
19050
|
-
},
|
|
19051
|
-
plugins: [],
|
|
19052
|
-
}
|
|
19053
|
-
export default config
|
|
19054
|
-
`,
|
|
19055
|
-
"postcss.config.js": `module.exports = {
|
|
19056
|
-
plugins: {
|
|
19057
|
-
tailwindcss: {},
|
|
19058
|
-
autoprefixer: {},
|
|
19059
|
-
},
|
|
19060
|
-
}
|
|
19061
|
-
`,
|
|
19062
|
-
".eslintrc.json": `{
|
|
19063
|
-
"extends": "next/core-web-vitals"
|
|
19064
|
-
}
|
|
19065
|
-
`,
|
|
19066
|
-
".gitignore": `# dependencies
|
|
19067
|
-
/node_modules
|
|
19068
|
-
/.pnp
|
|
19069
|
-
.pnp.js
|
|
19085
|
+
**Theme:** dark
|
|
19086
|
+
**Stack:** Next.js 14 App Router \xB7 \`output: 'standalone'\` (FactorAI deploy) \xB7 Tailwind 3 \xB7 shadcn/ui (${uiCount} components)
|
|
19087
|
+
**Path alias:** \`@/*\` \u2192 project root
|
|
19070
19088
|
|
|
19071
|
-
|
|
19072
|
-
/coverage
|
|
19089
|
+
---
|
|
19073
19090
|
|
|
19074
|
-
|
|
19075
|
-
/.next/
|
|
19076
|
-
/out/
|
|
19091
|
+
## Architecture
|
|
19077
19092
|
|
|
19078
|
-
|
|
19079
|
-
|
|
19093
|
+
| Area | Path | Notes |
|
|
19094
|
+
|------|------|-------|
|
|
19095
|
+
| Home (starter UI) | \`app/page.tsx\` | Dark landing: hero glow, badge, CTAs, 3 feature cards, footer |
|
|
19096
|
+
| Root layout | \`app/layout.tsx\` | Metadata title = project name |
|
|
19097
|
+
| Global styles | \`app/globals.css\` | shadcn HSL tokens (see below) |
|
|
19098
|
+
| UI kit | \`components/ui/*\` | Same set as factorai.sh \u2014 import, do not recreate |
|
|
19099
|
+
| Utilities | \`lib/utils.ts\` | \`cn()\` for class merging |
|
|
19100
|
+
| Toasts | \`hooks/use-toast.ts\` | Pair with \`components/ui/toaster\` or \`sonner\` |
|
|
19101
|
+
| Config | \`next.config.js\`, \`tailwind.config.ts\` | Standalone build required for hosting |
|
|
19080
19102
|
|
|
19081
|
-
|
|
19082
|
-
.DS_Store
|
|
19083
|
-
*.pem
|
|
19103
|
+
---
|
|
19084
19104
|
|
|
19085
|
-
|
|
19086
|
-
npm-debug.log*
|
|
19087
|
-
yarn-debug.log*
|
|
19088
|
-
yarn-error.log*
|
|
19105
|
+
## Tokens \u2014 Colors
|
|
19089
19106
|
|
|
19090
|
-
|
|
19091
|
-
|
|
19092
|
-
|
|
19107
|
+
| Name | Value | Role |
|
|
19108
|
+
|------|-------|------|
|
|
19109
|
+
${tokenTable}
|
|
19093
19110
|
|
|
19094
|
-
|
|
19095
|
-
*.tsbuildinfo
|
|
19096
|
-
next-env.d.ts
|
|
19097
|
-
`,
|
|
19098
|
-
// App Router mínimo (obrigatório para next build / deploy no Severino)
|
|
19099
|
-
"app/layout.tsx": `import type { Metadata } from 'next';
|
|
19100
|
-
import './globals.css';
|
|
19111
|
+
Use Tailwind semantic classes (\`bg-background\`, \`text-muted-foreground\`, \`border-border\`) \u2014 not raw hex in JSX unless defining a one-off gradient.
|
|
19101
19112
|
|
|
19102
|
-
|
|
19103
|
-
title: '{{NAME}}',
|
|
19104
|
-
description: 'Generated by create-next-app',
|
|
19105
|
-
};
|
|
19113
|
+
---
|
|
19106
19114
|
|
|
19107
|
-
|
|
19108
|
-
children,
|
|
19109
|
-
}: Readonly<{
|
|
19110
|
-
children: React.ReactNode;
|
|
19111
|
-
}>) {
|
|
19112
|
-
return (
|
|
19113
|
-
<html lang="en">
|
|
19114
|
-
<body>{children}</body>
|
|
19115
|
-
</html>
|
|
19116
|
-
);
|
|
19117
|
-
}
|
|
19118
|
-
`,
|
|
19119
|
-
"app/page.tsx": `export default function Home() {
|
|
19120
|
-
return (
|
|
19121
|
-
<main className="flex min-h-screen flex-col items-center justify-center p-8">
|
|
19122
|
-
<h1 className="text-2xl font-semibold">{{NAME}}</h1>
|
|
19123
|
-
<p className="mt-2 text-sm text-gray-600">Next.js app ready for deploy.</p>
|
|
19124
|
-
</main>
|
|
19125
|
-
);
|
|
19126
|
-
}
|
|
19127
|
-
`,
|
|
19128
|
-
"app/globals.css": `@tailwind base;
|
|
19129
|
-
@tailwind components;
|
|
19130
|
-
@tailwind utilities;
|
|
19131
|
-
`
|
|
19132
|
-
};
|
|
19133
|
-
var FULL_FILES = {
|
|
19134
|
-
...MINIMAL_FILES,
|
|
19135
|
-
// App Router structure
|
|
19136
|
-
"app/layout.tsx": `import type { Metadata } from "next";
|
|
19137
|
-
import { Inter } from "next/font/google";
|
|
19138
|
-
import "./globals.css";
|
|
19139
|
-
|
|
19140
|
-
const inter = Inter({ subsets: ["latin"] });
|
|
19141
|
-
|
|
19142
|
-
export const metadata: Metadata = {
|
|
19143
|
-
title: "{{NAME}}",
|
|
19144
|
-
description: "Generated by create-next-app",
|
|
19145
|
-
};
|
|
19115
|
+
## Tokens \u2014 Typography & spacing
|
|
19146
19116
|
|
|
19147
|
-
|
|
19148
|
-
|
|
19149
|
-
|
|
19150
|
-
|
|
19151
|
-
}>) {
|
|
19152
|
-
return (
|
|
19153
|
-
<html lang="en">
|
|
19154
|
-
<body className={inter.className}>{children}</body>
|
|
19155
|
-
</html>
|
|
19156
|
-
);
|
|
19157
|
-
}
|
|
19158
|
-
`,
|
|
19159
|
-
"app/page.tsx": `export default function Home() {
|
|
19160
|
-
return (
|
|
19161
|
-
<main className="flex min-h-screen flex-col items-center justify-between p-24">
|
|
19162
|
-
<div className="z-10 w-full max-w-5xl items-center justify-between font-mono text-sm lg:flex">
|
|
19163
|
-
<p className="fixed left-0 top-0 flex w-full justify-center border-b border-gray-300 bg-gradient-to-b from-zinc-200 pb-6 pt-8 backdrop-blur-2xl dark:border-neutral-800 dark:bg-zinc-800/30 dark:from-inherit lg:static lg:w-auto lg:rounded-xl lg:border lg:bg-gray-200 lg:p-4 lg:dark:bg-zinc-800/30">
|
|
19164
|
-
Welcome to <code className="font-mono font-bold">{{NAME}}</code>
|
|
19165
|
-
</p>
|
|
19166
|
-
</div>
|
|
19167
|
-
|
|
19168
|
-
<div className="mb-32 grid text-center lg:max-w-5xl lg:w-full lg:mb-0 lg:grid-cols-4 lg:text-left">
|
|
19169
|
-
<a
|
|
19170
|
-
href="https://nextjs.org/docs"
|
|
19171
|
-
className="group rounded-lg border border-transparent px-5 py-4 transition-colors hover:border-gray-300 hover:bg-gray-100 hover:dark:border-neutral-700 hover:dark:bg-neutral-800/30"
|
|
19172
|
-
target="_blank"
|
|
19173
|
-
rel="noopener noreferrer"
|
|
19174
|
-
>
|
|
19175
|
-
<h2 className={\`mb-3 text-2xl font-semibold\`}>
|
|
19176
|
-
Docs{" "}
|
|
19177
|
-
<span className="inline-block transition-transform group-hover:translate-x-1 motion-reduce:transform-none">
|
|
19178
|
-
->
|
|
19179
|
-
</span>
|
|
19180
|
-
</h2>
|
|
19181
|
-
<p className={\`m-0 max-w-[30ch] text-sm opacity-50\`}>
|
|
19182
|
-
Find in-depth information about Next.js features and API.
|
|
19183
|
-
</p>
|
|
19184
|
-
</a>
|
|
19185
|
-
|
|
19186
|
-
<a
|
|
19187
|
-
href="https://nextjs.org/learn"
|
|
19188
|
-
className="group rounded-lg border border-transparent px-5 py-4 transition-colors hover:border-gray-300 hover:bg-gray-100 hover:dark:border-neutral-700 hover:dark:bg-neutral-800/30"
|
|
19189
|
-
target="_blank"
|
|
19190
|
-
rel="noopener noreferrer"
|
|
19191
|
-
>
|
|
19192
|
-
<h2 className={\`mb-3 text-2xl font-semibold\`}>
|
|
19193
|
-
Learn{" "}
|
|
19194
|
-
<span className="inline-block transition-transform group-hover:translate-x-1 motion-reduce:transform-none">
|
|
19195
|
-
->
|
|
19196
|
-
</span>
|
|
19197
|
-
</h2>
|
|
19198
|
-
<p className={\`m-0 max-w-[30ch] text-sm opacity-50\`}>
|
|
19199
|
-
Learn about Next.js in an interactive course with quizzes!
|
|
19200
|
-
</p>
|
|
19201
|
-
</a>
|
|
19202
|
-
|
|
19203
|
-
<a
|
|
19204
|
-
href="https://vercel.com/templates"
|
|
19205
|
-
className="group rounded-lg border border-transparent px-5 py-4 transition-colors hover:border-gray-300 hover:bg-gray-100 hover:dark:border-neutral-700 hover:dark:bg-neutral-800/30"
|
|
19206
|
-
target="_blank"
|
|
19207
|
-
rel="noopener noreferrer"
|
|
19208
|
-
>
|
|
19209
|
-
<h2 className={\`mb-3 text-2xl font-semibold\`}>
|
|
19210
|
-
Templates{" "}
|
|
19211
|
-
<span className="inline-block transition-transform group-hover:translate-x-1 motion-reduce:transform-none">
|
|
19212
|
-
->
|
|
19213
|
-
</span>
|
|
19214
|
-
</h2>
|
|
19215
|
-
<p className={\`m-0 max-w-[30ch] text-sm opacity-50\`}>
|
|
19216
|
-
Explore starter templates for Next.js.
|
|
19217
|
-
</p>
|
|
19218
|
-
</a>
|
|
19219
|
-
|
|
19220
|
-
<a
|
|
19221
|
-
href="https://vercel.com/new"
|
|
19222
|
-
className="group rounded-lg border border-transparent px-5 py-4 transition-colors hover:border-gray-300 hover:bg-gray-100 hover:dark:border-neutral-700 hover:dark:bg-neutral-800/30"
|
|
19223
|
-
target="_blank"
|
|
19224
|
-
rel="noopener noreferrer"
|
|
19225
|
-
>
|
|
19226
|
-
<h2 className={\`mb-3 text-2xl font-semibold\`}>
|
|
19227
|
-
Deploy{" "}
|
|
19228
|
-
<span className="inline-block transition-transform group-hover:translate-x-1 motion-reduce:transform-none">
|
|
19229
|
-
->
|
|
19230
|
-
</span>
|
|
19231
|
-
</h2>
|
|
19232
|
-
<p className={\`m-0 max-w-[30ch] text-sm opacity-50 text-balance\`}>
|
|
19233
|
-
Instantly deploy your Next.js site to a shareable URL with Vercel.
|
|
19234
|
-
</p>
|
|
19235
|
-
</a>
|
|
19236
|
-
</div>
|
|
19237
|
-
</main>
|
|
19238
|
-
);
|
|
19239
|
-
}
|
|
19240
|
-
`,
|
|
19241
|
-
"app/globals.css": `@tailwind base;
|
|
19242
|
-
@tailwind components;
|
|
19243
|
-
@tailwind utilities;
|
|
19117
|
+
- **Fonts:** system UI stack (add Inter/Geist in \`layout.tsx\` if you want a product-specific voice).
|
|
19118
|
+
- **Scale:** \`text-sm\` body, \`text-lg\`/\`text-xl\` subheads, \`text-3xl\`\u2013\`text-5xl\` hero with \`tracking-tight\`.
|
|
19119
|
+
- **Density:** comfortable \u2014 \`section\` gaps \`py-16\`\u2013\`py-24\`, card padding \`p-6\`, inline gaps \`gap-3\`/\`gap-4\`.
|
|
19120
|
+
- **Radius:** \`--radius: 0.5rem\` \u2014 buttons/inputs \`rounded-md\`, pills \`rounded-full\`.
|
|
19244
19121
|
|
|
19245
|
-
|
|
19246
|
-
--foreground-rgb: 0, 0, 0;
|
|
19247
|
-
--background-start-rgb: 214, 219, 220;
|
|
19248
|
-
--background-end-rgb: 255, 255, 255;
|
|
19249
|
-
}
|
|
19122
|
+
---
|
|
19250
19123
|
|
|
19251
|
-
|
|
19252
|
-
:root {
|
|
19253
|
-
--foreground-rgb: 255, 255, 255;
|
|
19254
|
-
--background-start-rgb: 0, 0, 0;
|
|
19255
|
-
--background-end-rgb: 0, 0, 0;
|
|
19256
|
-
}
|
|
19257
|
-
}
|
|
19124
|
+
## UI components available (${uiCount})
|
|
19258
19125
|
|
|
19259
|
-
|
|
19260
|
-
color: rgb(var(--foreground-rgb));
|
|
19261
|
-
background: linear-gradient(
|
|
19262
|
-
to bottom,
|
|
19263
|
-
transparent,
|
|
19264
|
-
rgb(var(--background-end-rgb))
|
|
19265
|
-
)
|
|
19266
|
-
rgb(var(--background-start-rgb));
|
|
19267
|
-
}
|
|
19126
|
+
Import from \`@/components/ui/<file>\`. Compose with \`cn()\` from \`@/lib/utils\`.
|
|
19268
19127
|
|
|
19269
|
-
|
|
19270
|
-
|
|
19271
|
-
|
|
19272
|
-
|
|
19128
|
+
${componentInventoryMarkdown(uiComponents)}
|
|
19129
|
+
|
|
19130
|
+
**Quick import examples:**
|
|
19131
|
+
|
|
19132
|
+
\`\`\`tsx
|
|
19133
|
+
import { Button } from "@/components/ui/button";
|
|
19134
|
+
import { Card, CardContent, CardHeader, CardTitle, CardDescription } from "@/components/ui/card";
|
|
19135
|
+
import { Input } from "@/components/ui/input";
|
|
19136
|
+
import { Badge } from "@/components/ui/badge";
|
|
19137
|
+
import { Separator } from "@/components/ui/separator";
|
|
19138
|
+
\`\`\`
|
|
19139
|
+
|
|
19140
|
+
---
|
|
19141
|
+
|
|
19142
|
+
## Do's and Don'ts (polished interfaces)
|
|
19143
|
+
|
|
19144
|
+
### Do
|
|
19145
|
+
- Keep **one dominant background** (\`bg-background\`) and **one accent discipline** (primary buttons + ring on focus).
|
|
19146
|
+
- Use **clear hierarchy**: display headline \u2192 subheading (\`text-muted-foreground\`) \u2192 body; max width \`max-w-2xl\` or \`max-w-3xl\` for prose.
|
|
19147
|
+
- Build **sections** with \`py-16 md:py-24\` and centered containers \`mx-auto max-w-6xl px-4 md:px-6\`.
|
|
19148
|
+
- Prefer **Card** for feature groupings; use **ghost/outline** buttons for secondary actions.
|
|
19149
|
+
- Add **generous whitespace**; empty space signals quality (see [Refero Styles](https://styles.refero.design/) references).
|
|
19150
|
+
- Use **Sonner** or **Toast** for async feedback after deploy-worthy flows.
|
|
19151
|
+
- Run \`npm run build\` before deploy; keep standalone output intact.
|
|
19152
|
+
|
|
19153
|
+
### Don't
|
|
19154
|
+
- Customize the default landing copy and sections for your product before production.
|
|
19155
|
+
- Keep the starter layout patterns (hero glow, pill buttons, card grid) when extending \u2014 do not regress to generic Next.js marketing grids.
|
|
19156
|
+
- Do not invent parallel design systems \u2014 use existing \`components/ui\` tokens.
|
|
19157
|
+
- Do not saturate the UI with many accent colors; stay monochromatic + primary/ destructive only.
|
|
19158
|
+
- Do not use tiny touch targets \u2014 buttons \`h-10\` minimum, adequate \`px-4\`.
|
|
19159
|
+
- Do not add new npm UI libraries without strong reason \u2014 kit is already complete.
|
|
19160
|
+
|
|
19161
|
+
---
|
|
19162
|
+
|
|
19163
|
+
## Layout patterns (recommended)
|
|
19164
|
+
|
|
19165
|
+
1. **Hero:** full-width \`min-h-[80vh]\`, centered headline, single primary CTA, optional subtle gradient \`bg-gradient-to-b from-background to-muted/30\`.
|
|
19166
|
+
2. **Feature grid:** \`grid md:grid-cols-2 lg:grid-cols-3 gap-6\` of Cards.
|
|
19167
|
+
3. **Split section:** \`grid lg:grid-cols-2 gap-12 items-center\` text + visual.
|
|
19168
|
+
4. **Sticky nav:** \`border-b border-border/40 backdrop-blur\` bar with ghost nav + one filled CTA.
|
|
19169
|
+
|
|
19170
|
+
---
|
|
19171
|
+
|
|
19172
|
+
## Agent prompt guide (copy into your planning)
|
|
19173
|
+
|
|
19174
|
+
**Quick color reference:** text \`text-foreground\`, background \`bg-background\`, borders \`border-border\`, primary CTA \`bg-primary text-primary-foreground\`.
|
|
19175
|
+
|
|
19176
|
+
**Example prompts for this codebase:**
|
|
19177
|
+
|
|
19178
|
+
1. *Hero:* \`min-h-screen bg-background flex flex-col items-center justify-center px-6\`. Headline \`text-4xl md:text-6xl font-medium tracking-tight text-foreground\`. Subtext \`text-muted-foreground text-lg mt-4 max-w-xl text-center\`. Primary \`Button\` mt-8.
|
|
19179
|
+
2. *Feature card:* \`Card\` with \`CardHeader\` + \`CardTitle\` + \`CardDescription\`; \`CardContent\` with short copy; optional \`Badge\` for labels.
|
|
19180
|
+
3. *Form block:* \`Input\` + \`Label\` in a \`Card\`; submit \`Button\` full width on mobile; inline \`text-sm text-muted-foreground\` helpers.
|
|
19181
|
+
|
|
19182
|
+
For external visual direction, browse dark/productivity references on [Refero Styles](https://styles.refero.design/) (e.g. Linear, xAI, Stripe) \u2014 adapt tokens above, do not copy unrelated fonts wholesale.
|
|
19183
|
+
|
|
19184
|
+
---
|
|
19185
|
+
|
|
19186
|
+
## What to do next
|
|
19187
|
+
|
|
19188
|
+
1. Edit \`app/page.tsx\` (and add routes under \`app/\` as needed).
|
|
19189
|
+
2. \`npm run dev\` \u2014 preview locally.
|
|
19190
|
+
3. \`factorai.sh.deploy_app\` when ready \u2014 ZIP excludes \`node_modules\` and \`.next\`.
|
|
19191
|
+
|
|
19192
|
+
---
|
|
19193
|
+
|
|
19194
|
+
*Generated by BluMa \`factorai.sh.create_next_app\` \xB7 re-read this file at \`${DESIGN_DOC_FILENAME}\` in the project root.*
|
|
19195
|
+
`;
|
|
19196
|
+
return { markdown, uiComponents };
|
|
19197
|
+
}
|
|
19198
|
+
async function writeDesignDoc(projectPath, markdown) {
|
|
19199
|
+
const docPath = path25.join(projectPath, DESIGN_DOC_FILENAME);
|
|
19200
|
+
await fs23.writeFile(docPath, markdown, "utf-8");
|
|
19201
|
+
return docPath;
|
|
19273
19202
|
}
|
|
19274
|
-
`,
|
|
19275
|
-
// shadcn/ui base components (sem @radix-ui/react-slot — evita falha de build no deploy)
|
|
19276
|
-
"components/ui/button.tsx": `import * as React from "react"
|
|
19277
|
-
import { cva, type VariantProps } from "class-variance-authority"
|
|
19278
|
-
import { cn } from "@/lib/utils"
|
|
19279
19203
|
|
|
19280
|
-
|
|
19281
|
-
|
|
19282
|
-
|
|
19283
|
-
|
|
19284
|
-
|
|
19285
|
-
|
|
19286
|
-
|
|
19287
|
-
|
|
19288
|
-
|
|
19289
|
-
|
|
19290
|
-
|
|
19291
|
-
}
|
|
19292
|
-
|
|
19293
|
-
|
|
19294
|
-
|
|
19295
|
-
|
|
19296
|
-
|
|
19297
|
-
|
|
19298
|
-
|
|
19299
|
-
|
|
19300
|
-
|
|
19301
|
-
|
|
19302
|
-
|
|
19303
|
-
|
|
19304
|
-
|
|
19305
|
-
|
|
19306
|
-
export interface ButtonProps
|
|
19307
|
-
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
|
|
19308
|
-
VariantProps<typeof buttonVariants> {}
|
|
19309
|
-
|
|
19310
|
-
const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
|
|
19311
|
-
({ className, variant, size, ...props }, ref) => (
|
|
19312
|
-
<button
|
|
19313
|
-
className={cn(buttonVariants({ variant, size, className }))}
|
|
19314
|
-
ref={ref}
|
|
19315
|
-
{...props}
|
|
19316
|
-
/>
|
|
19317
|
-
)
|
|
19318
|
-
)
|
|
19319
|
-
Button.displayName = "Button"
|
|
19320
|
-
|
|
19321
|
-
export { Button, buttonVariants }
|
|
19322
|
-
`,
|
|
19323
|
-
"components/ui/card.tsx": `import * as React from "react"
|
|
19324
|
-
import { cn } from "@/lib/utils"
|
|
19325
|
-
|
|
19326
|
-
const Card = React.forwardRef<
|
|
19327
|
-
HTMLDivElement,
|
|
19328
|
-
React.HTMLAttributes<HTMLDivElement>
|
|
19329
|
-
>(({ className, ...props }, ref) => (
|
|
19330
|
-
<div
|
|
19331
|
-
ref={ref}
|
|
19332
|
-
className={cn(
|
|
19333
|
-
"rounded-lg border border-zinc-200 bg-white text-zinc-950 shadow-sm",
|
|
19334
|
-
className
|
|
19335
|
-
)}
|
|
19336
|
-
{...props}
|
|
19337
|
-
/>
|
|
19338
|
-
))
|
|
19339
|
-
Card.displayName = "Card"
|
|
19340
|
-
|
|
19341
|
-
const CardHeader = React.forwardRef<
|
|
19342
|
-
HTMLDivElement,
|
|
19343
|
-
React.HTMLAttributes<HTMLDivElement>
|
|
19344
|
-
>(({ className, ...props }, ref) => (
|
|
19345
|
-
<div
|
|
19346
|
-
ref={ref}
|
|
19347
|
-
className={cn("flex flex-col space-y-1.5 p-6", className)}
|
|
19348
|
-
{...props}
|
|
19349
|
-
/>
|
|
19350
|
-
))
|
|
19351
|
-
CardHeader.displayName = "CardHeader"
|
|
19352
|
-
|
|
19353
|
-
const CardTitle = React.forwardRef<
|
|
19354
|
-
HTMLParagraphElement,
|
|
19355
|
-
React.HTMLAttributes<HTMLHeadingElement>
|
|
19356
|
-
>(({ className, ...props }, ref) => (
|
|
19357
|
-
<h3
|
|
19358
|
-
ref={ref}
|
|
19359
|
-
className={cn(
|
|
19360
|
-
"text-2xl font-semibold leading-none tracking-tight",
|
|
19361
|
-
className
|
|
19362
|
-
)}
|
|
19363
|
-
{...props}
|
|
19364
|
-
/>
|
|
19365
|
-
))
|
|
19366
|
-
CardTitle.displayName = "CardTitle"
|
|
19367
|
-
|
|
19368
|
-
const CardDescription = React.forwardRef<
|
|
19369
|
-
HTMLParagraphElement,
|
|
19370
|
-
React.HTMLAttributes<HTMLParagraphElement>
|
|
19371
|
-
>(({ className, ...props }, ref) => (
|
|
19372
|
-
<p
|
|
19373
|
-
ref={ref}
|
|
19374
|
-
className={cn("text-sm text-zinc-500", className)}
|
|
19375
|
-
{...props}
|
|
19376
|
-
/>
|
|
19377
|
-
))
|
|
19378
|
-
CardDescription.displayName = "CardDescription"
|
|
19379
|
-
|
|
19380
|
-
const CardContent = React.forwardRef<
|
|
19381
|
-
HTMLDivElement,
|
|
19382
|
-
React.HTMLAttributes<HTMLDivElement>
|
|
19383
|
-
>(({ className, ...props }, ref) => (
|
|
19384
|
-
<div ref={ref} className={cn("p-6 pt-0", className)} {...props} />
|
|
19385
|
-
))
|
|
19386
|
-
CardContent.displayName = "CardContent"
|
|
19387
|
-
|
|
19388
|
-
const CardFooter = React.forwardRef<
|
|
19389
|
-
HTMLDivElement,
|
|
19390
|
-
React.HTMLAttributes<HTMLDivElement>
|
|
19391
|
-
>(({ className, ...props }, ref) => (
|
|
19392
|
-
<div
|
|
19393
|
-
ref={ref}
|
|
19394
|
-
className={cn("flex items-center p-6 pt-0", className)}
|
|
19395
|
-
{...props}
|
|
19396
|
-
/>
|
|
19397
|
-
))
|
|
19398
|
-
CardFooter.displayName = "CardFooter"
|
|
19399
|
-
|
|
19400
|
-
export { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent }
|
|
19401
|
-
`,
|
|
19402
|
-
"components/ui/input.tsx": `import * as React from "react"
|
|
19403
|
-
import { cn } from "@/lib/utils"
|
|
19404
|
-
|
|
19405
|
-
export interface InputProps
|
|
19406
|
-
extends React.InputHTMLAttributes<HTMLInputElement> {}
|
|
19407
|
-
|
|
19408
|
-
const Input = React.forwardRef<HTMLInputElement, InputProps>(
|
|
19409
|
-
({ className, type, ...props }, ref) => {
|
|
19410
|
-
return (
|
|
19411
|
-
<input
|
|
19412
|
-
type={type}
|
|
19413
|
-
className={cn(
|
|
19414
|
-
"flex h-10 w-full rounded-md border border-zinc-200 bg-white px-3 py-2 text-sm file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-zinc-400 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-zinc-400 focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
|
|
19415
|
-
className
|
|
19416
|
-
)}
|
|
19417
|
-
ref={ref}
|
|
19418
|
-
{...props}
|
|
19419
|
-
/>
|
|
19420
|
-
)
|
|
19421
|
-
}
|
|
19422
|
-
)
|
|
19423
|
-
Input.displayName = "Input"
|
|
19424
|
-
|
|
19425
|
-
export { Input }
|
|
19426
|
-
`,
|
|
19427
|
-
"lib/utils.ts": `import { type ClassValue, clsx } from "clsx"
|
|
19428
|
-
import { twMerge } from "tailwind-merge"
|
|
19429
|
-
|
|
19430
|
-
export function cn(...inputs: ClassValue[]) {
|
|
19431
|
-
return twMerge(clsx(inputs))
|
|
19432
|
-
}
|
|
19433
|
-
`,
|
|
19434
|
-
// Add clsx and tailwind-merge to package.json dependencies
|
|
19435
|
-
"package.json.full": `{
|
|
19436
|
-
"name": "{{NAME}}",
|
|
19437
|
-
"version": "0.1.0",
|
|
19438
|
-
"private": true,
|
|
19439
|
-
"scripts": {
|
|
19440
|
-
"dev": "next dev",
|
|
19441
|
-
"build": "next build",
|
|
19442
|
-
"start": "next start",
|
|
19443
|
-
"lint": "next lint"
|
|
19444
|
-
},
|
|
19445
|
-
"dependencies": {
|
|
19446
|
-
"next": "^14.1.0",
|
|
19447
|
-
"react": "^18.2.0",
|
|
19448
|
-
"react-dom": "^18.2.0",
|
|
19449
|
-
"class-variance-authority": "^0.7.0",
|
|
19450
|
-
"clsx": "^2.1.0",
|
|
19451
|
-
"tailwind-merge": "^2.2.0"
|
|
19452
|
-
},
|
|
19453
|
-
"devDependencies": {
|
|
19454
|
-
"@types/node": "^20.11.0",
|
|
19455
|
-
"@types/react": "^18.2.0",
|
|
19456
|
-
"@types/react-dom": "^18.2.0",
|
|
19457
|
-
"typescript": "^5.3.0",
|
|
19458
|
-
"tailwindcss": "^3.4.0",
|
|
19459
|
-
"postcss": "^8.4.0",
|
|
19460
|
-
"autoprefixer": "^10.4.0",
|
|
19461
|
-
"eslint": "^8.56.0",
|
|
19462
|
-
"eslint-config-next": "^14.1.0"
|
|
19204
|
+
// src/app/agent/tools/CreateNextAppTool/CreateNextAppTool.ts
|
|
19205
|
+
var SCAFFOLD_DIR = path26.join(
|
|
19206
|
+
path26.dirname(fileURLToPath(import.meta.url)),
|
|
19207
|
+
"scaffold"
|
|
19208
|
+
);
|
|
19209
|
+
async function copyScaffoldTree(projectPath, projectName) {
|
|
19210
|
+
const filesCreated = [];
|
|
19211
|
+
async function walk(relativeDir) {
|
|
19212
|
+
const srcDir = path26.join(SCAFFOLD_DIR, relativeDir);
|
|
19213
|
+
const entries = await fs24.readdir(srcDir, { withFileTypes: true });
|
|
19214
|
+
for (const entry of entries) {
|
|
19215
|
+
const relPath = relativeDir ? `${relativeDir}/${entry.name}` : entry.name;
|
|
19216
|
+
const srcPath = path26.join(SCAFFOLD_DIR, relPath);
|
|
19217
|
+
const destPath = path26.join(projectPath, relPath);
|
|
19218
|
+
if (entry.isDirectory()) {
|
|
19219
|
+
await fs24.mkdir(destPath, { recursive: true });
|
|
19220
|
+
await walk(relPath);
|
|
19221
|
+
continue;
|
|
19222
|
+
}
|
|
19223
|
+
const raw = await fs24.readFile(srcPath, "utf-8");
|
|
19224
|
+
const content = raw.replace(/\{\{NAME\}\}/g, projectName);
|
|
19225
|
+
await fs24.mkdir(path26.dirname(destPath), { recursive: true });
|
|
19226
|
+
await fs24.writeFile(destPath, content, "utf-8");
|
|
19227
|
+
filesCreated.push(relPath);
|
|
19228
|
+
}
|
|
19463
19229
|
}
|
|
19464
|
-
|
|
19465
|
-
|
|
19466
|
-
};
|
|
19467
|
-
async function createFile(filePath, content, projectName) {
|
|
19468
|
-
const dir = path25.dirname(filePath);
|
|
19469
|
-
await fs23.mkdir(dir, { recursive: true });
|
|
19470
|
-
const replacedContent = content.replace(/{{NAME}}/g, projectName);
|
|
19471
|
-
await fs23.writeFile(filePath, replacedContent, "utf-8");
|
|
19230
|
+
await walk("");
|
|
19231
|
+
return filesCreated;
|
|
19472
19232
|
}
|
|
19473
19233
|
async function createNextApp(args) {
|
|
19474
|
-
const {
|
|
19475
|
-
name,
|
|
19476
|
-
directory,
|
|
19477
|
-
template = "full"
|
|
19478
|
-
} = args;
|
|
19234
|
+
const { name, directory } = args;
|
|
19479
19235
|
try {
|
|
19480
19236
|
if (!name || typeof name !== "string") {
|
|
19481
19237
|
return { success: false, error: "name is required and must be a string" };
|
|
@@ -19487,73 +19243,66 @@ async function createNextApp(args) {
|
|
|
19487
19243
|
};
|
|
19488
19244
|
}
|
|
19489
19245
|
const baseDir = directory ? resolveWorkspacePath(directory) : resolveWorkspacePath(".");
|
|
19490
|
-
const projectPath =
|
|
19246
|
+
const projectPath = path26.join(baseDir, name);
|
|
19491
19247
|
try {
|
|
19492
|
-
await
|
|
19248
|
+
await fs24.access(projectPath);
|
|
19493
19249
|
return {
|
|
19494
19250
|
success: false,
|
|
19495
19251
|
error: `Directory already exists: ${projectPath}. Remove it first or choose a different name.`
|
|
19496
19252
|
};
|
|
19497
19253
|
} catch {
|
|
19498
19254
|
}
|
|
19499
|
-
await
|
|
19500
|
-
const
|
|
19501
|
-
const filesCreated = [];
|
|
19502
|
-
for (const [relativePath, content] of Object.entries(filesToCreate)) {
|
|
19503
|
-
if (relativePath === "package.json.full") continue;
|
|
19504
|
-
const fullPath = path25.join(projectPath, relativePath);
|
|
19505
|
-
await createFile(fullPath, content, name);
|
|
19506
|
-
filesCreated.push(relativePath);
|
|
19507
|
-
}
|
|
19508
|
-
if (template === "full") {
|
|
19509
|
-
const packageJsonPath = path25.join(projectPath, "package.json");
|
|
19510
|
-
const fullPackageJson = FULL_FILES["package.json.full"];
|
|
19511
|
-
if (fullPackageJson) {
|
|
19512
|
-
await createFile(packageJsonPath, fullPackageJson, name);
|
|
19513
|
-
}
|
|
19514
|
-
}
|
|
19255
|
+
await fs24.mkdir(projectPath, { recursive: true });
|
|
19256
|
+
const filesCreated = await copyScaffoldTree(projectPath, name);
|
|
19515
19257
|
console.log(`[create-next-app] Installing dependencies in ${projectPath}...`);
|
|
19516
19258
|
const installResult = await shellCommand({
|
|
19517
19259
|
command: "npm install",
|
|
19518
19260
|
cwd: projectPath,
|
|
19519
19261
|
timeout: 300
|
|
19520
|
-
// 5 minutos
|
|
19521
19262
|
});
|
|
19522
19263
|
const installJson = JSON.parse(installResult);
|
|
19523
19264
|
if (installJson.status !== "success") {
|
|
19524
19265
|
console.warn("[create-next-app] npm install warnings:", installJson.stderr);
|
|
19525
19266
|
}
|
|
19267
|
+
const { markdown: agentGuide, uiComponents } = await buildScaffoldAgentGuide(
|
|
19268
|
+
name,
|
|
19269
|
+
projectPath
|
|
19270
|
+
);
|
|
19271
|
+
const designDocPath = await writeDesignDoc(projectPath, agentGuide);
|
|
19526
19272
|
const nextSteps = [
|
|
19527
|
-
`
|
|
19528
|
-
|
|
19529
|
-
|
|
19530
|
-
|
|
19273
|
+
`Read ${DESIGN_DOC_FILENAME} (or tool result agentGuide) before editing UI`,
|
|
19274
|
+
`cd ${name} && npm run dev \u2014 default landing shows "Power by FactorAI"`,
|
|
19275
|
+
`Edit app/page.tsx; use @/components/ui/* (${uiComponents.length} components)`,
|
|
19276
|
+
"Deploy with factorai.sh.deploy_app when ready"
|
|
19531
19277
|
];
|
|
19278
|
+
const summary = `Created "${name}" at ${projectPath}: Next.js 14 + ${uiComponents.length} shadcn/ui components, dark FactorAI tokens, standalone deploy. Default landing is ready \u2014 customize app/page.tsx for your product.`;
|
|
19532
19279
|
console.log(`[create-next-app] Projeto criado: ${projectPath}`);
|
|
19533
19280
|
return {
|
|
19534
19281
|
success: true,
|
|
19535
19282
|
projectPath,
|
|
19536
19283
|
directory: name,
|
|
19537
19284
|
filesCreated,
|
|
19285
|
+
agentGuide,
|
|
19286
|
+
designDocPath,
|
|
19287
|
+
uiComponentCount: uiComponents.length,
|
|
19288
|
+
summary,
|
|
19538
19289
|
nextSteps
|
|
19539
19290
|
};
|
|
19540
19291
|
} catch (error) {
|
|
19541
|
-
|
|
19542
|
-
|
|
19543
|
-
|
|
19544
|
-
error: error.message || String(error)
|
|
19545
|
-
};
|
|
19292
|
+
const message2 = error instanceof Error ? error.message : String(error);
|
|
19293
|
+
console.error("[create-next-app] Error:", message2);
|
|
19294
|
+
return { success: false, error: message2 };
|
|
19546
19295
|
}
|
|
19547
19296
|
}
|
|
19548
19297
|
|
|
19549
19298
|
// src/app/agent/tools/DeployAppTool/DeployAppTool.ts
|
|
19550
19299
|
init_sandbox_policy();
|
|
19551
|
-
import { promises as
|
|
19552
|
-
import
|
|
19300
|
+
import { promises as fs25 } from "fs";
|
|
19301
|
+
import path28 from "path";
|
|
19553
19302
|
|
|
19554
19303
|
// src/app/agent/tools/DeployAppTool/createDeployProjectZip.ts
|
|
19555
19304
|
import { lstat, readdir, readFile as readFile2, writeFile } from "fs/promises";
|
|
19556
|
-
import
|
|
19305
|
+
import path27 from "path";
|
|
19557
19306
|
import { minimatch as minimatch2 } from "minimatch";
|
|
19558
19307
|
var DEPLOY_ZIP_EXCLUDE_PATTERNS = [
|
|
19559
19308
|
"node_modules",
|
|
@@ -19578,7 +19327,7 @@ function shouldExcludeFromDeployZip(relativePosix, isDirectory) {
|
|
|
19578
19327
|
const rel = relativePosix.replace(/\\/g, "/").replace(/^\.\//, "");
|
|
19579
19328
|
if (!rel) return false;
|
|
19580
19329
|
const segments = pathSegments(rel);
|
|
19581
|
-
const baseName =
|
|
19330
|
+
const baseName = path27.posix.basename(rel);
|
|
19582
19331
|
for (const pattern of DEPLOY_ZIP_EXCLUDE_PATTERNS) {
|
|
19583
19332
|
if (pattern.includes("*")) {
|
|
19584
19333
|
if (minimatch2(rel, pattern) || minimatch2(baseName, pattern)) {
|
|
@@ -19596,7 +19345,7 @@ function shouldExcludeFromDeployZip(relativePosix, isDirectory) {
|
|
|
19596
19345
|
return false;
|
|
19597
19346
|
}
|
|
19598
19347
|
async function collectDeployZipEntries(projectDir, files) {
|
|
19599
|
-
const base =
|
|
19348
|
+
const base = path27.resolve(projectDir);
|
|
19600
19349
|
async function walk(currentDir) {
|
|
19601
19350
|
let entries;
|
|
19602
19351
|
try {
|
|
@@ -19605,8 +19354,8 @@ async function collectDeployZipEntries(projectDir, files) {
|
|
|
19605
19354
|
return;
|
|
19606
19355
|
}
|
|
19607
19356
|
for (const entry of entries) {
|
|
19608
|
-
const fullPath =
|
|
19609
|
-
const rel =
|
|
19357
|
+
const fullPath = path27.join(currentDir, entry.name);
|
|
19358
|
+
const rel = path27.relative(base, fullPath).replace(/\\/g, "/");
|
|
19610
19359
|
if (shouldExcludeFromDeployZip(rel, entry.isDirectory())) {
|
|
19611
19360
|
continue;
|
|
19612
19361
|
}
|
|
@@ -19650,12 +19399,12 @@ async function createDeployProjectZip(projectDir, zipPath) {
|
|
|
19650
19399
|
// src/app/agent/tools/DeployAppTool/DeployAppTool.ts
|
|
19651
19400
|
async function uploadToSeverino(zipPath, severinoUrl, name, apiKey, appId) {
|
|
19652
19401
|
const deployUrl = `${severinoUrl.replace(/\/$/, "")}/api/v1/deploy`;
|
|
19653
|
-
const zipBytes = await
|
|
19402
|
+
const zipBytes = await fs25.readFile(zipPath);
|
|
19654
19403
|
const form = new FormData();
|
|
19655
19404
|
form.append(
|
|
19656
19405
|
"file",
|
|
19657
19406
|
new Blob([zipBytes], { type: "application/zip" }),
|
|
19658
|
-
|
|
19407
|
+
path28.basename(zipPath)
|
|
19659
19408
|
);
|
|
19660
19409
|
if (name) form.append("name", name);
|
|
19661
19410
|
if (appId) form.append("appId", appId);
|
|
@@ -19764,8 +19513,8 @@ function buildFactorAiManifest(appContext, deployResult, appName) {
|
|
|
19764
19513
|
}
|
|
19765
19514
|
async function writeFactorAiManifest(projectDir, appContext, deployResult, appName) {
|
|
19766
19515
|
const manifest = buildFactorAiManifest(appContext, deployResult, appName);
|
|
19767
|
-
const manifestPath =
|
|
19768
|
-
await
|
|
19516
|
+
const manifestPath = path28.join(projectDir, "factorai.sh.json");
|
|
19517
|
+
await fs25.writeFile(manifestPath, `${JSON.stringify(manifest, null, 2)}
|
|
19769
19518
|
`, "utf-8");
|
|
19770
19519
|
return manifest;
|
|
19771
19520
|
}
|
|
@@ -19785,23 +19534,23 @@ async function deployApp(args) {
|
|
|
19785
19534
|
}
|
|
19786
19535
|
const resolvedProjectDir = resolveWorkspacePath(projectDir);
|
|
19787
19536
|
try {
|
|
19788
|
-
await
|
|
19537
|
+
await fs25.access(resolvedProjectDir);
|
|
19789
19538
|
} catch {
|
|
19790
19539
|
return {
|
|
19791
19540
|
success: false,
|
|
19792
19541
|
error: `Project directory not found: ${resolvedProjectDir}`
|
|
19793
19542
|
};
|
|
19794
19543
|
}
|
|
19795
|
-
const packageJsonPath =
|
|
19544
|
+
const packageJsonPath = path28.join(resolvedProjectDir, "package.json");
|
|
19796
19545
|
try {
|
|
19797
|
-
await
|
|
19546
|
+
await fs25.access(packageJsonPath);
|
|
19798
19547
|
} catch {
|
|
19799
19548
|
return {
|
|
19800
19549
|
success: false,
|
|
19801
19550
|
error: "Not a Next.js project: package.json not found"
|
|
19802
19551
|
};
|
|
19803
19552
|
}
|
|
19804
|
-
const packageJsonContent = await
|
|
19553
|
+
const packageJsonContent = await fs25.readFile(packageJsonPath, "utf-8");
|
|
19805
19554
|
const packageJson = JSON.parse(packageJsonContent);
|
|
19806
19555
|
const hasNext = packageJson.dependencies?.next || packageJson.devDependencies?.next;
|
|
19807
19556
|
if (!hasNext) {
|
|
@@ -19810,18 +19559,18 @@ async function deployApp(args) {
|
|
|
19810
19559
|
error: "Not a Next.js project: next not found in dependencies"
|
|
19811
19560
|
};
|
|
19812
19561
|
}
|
|
19813
|
-
const appName = name || packageJson.name ||
|
|
19814
|
-
const tempDir =
|
|
19815
|
-
await
|
|
19816
|
-
const zipPath =
|
|
19562
|
+
const appName = name || packageJson.name || path28.basename(resolvedProjectDir);
|
|
19563
|
+
const tempDir = path28.join(resolvedProjectDir, ".tmp");
|
|
19564
|
+
await fs25.mkdir(tempDir, { recursive: true });
|
|
19565
|
+
const zipPath = path28.join(tempDir, `${appName}-${Date.now()}.zip`);
|
|
19817
19566
|
console.log(`[deploy-app] Creating ZIP: ${zipPath}`);
|
|
19818
19567
|
await createDeployProjectZip(resolvedProjectDir, zipPath);
|
|
19819
|
-
const zipStats = await
|
|
19568
|
+
const zipStats = await fs25.stat(zipPath);
|
|
19820
19569
|
const zipSizeMB = zipStats.size / 1024 / 1024;
|
|
19821
19570
|
if (zipSizeMB > 50) {
|
|
19822
|
-
await
|
|
19571
|
+
await fs25.unlink(zipPath).catch(() => {
|
|
19823
19572
|
});
|
|
19824
|
-
await
|
|
19573
|
+
await fs25.rmdir(tempDir).catch(() => {
|
|
19825
19574
|
});
|
|
19826
19575
|
return {
|
|
19827
19576
|
success: false,
|
|
@@ -19832,8 +19581,8 @@ async function deployApp(args) {
|
|
|
19832
19581
|
console.log(`[deploy-app] Uploading to ${severinoUrl}...`);
|
|
19833
19582
|
const deployResult = await uploadToSeverino(zipPath, severinoUrl, appName, apiKey, appId);
|
|
19834
19583
|
try {
|
|
19835
|
-
await
|
|
19836
|
-
await
|
|
19584
|
+
await fs25.unlink(zipPath);
|
|
19585
|
+
await fs25.rmdir(tempDir);
|
|
19837
19586
|
} catch (e) {
|
|
19838
19587
|
console.warn("[deploy-app] Cleanup warning:", e);
|
|
19839
19588
|
}
|
|
@@ -19846,7 +19595,7 @@ async function deployApp(args) {
|
|
|
19846
19595
|
appName
|
|
19847
19596
|
);
|
|
19848
19597
|
deployResult.factoraiManifest = manifest;
|
|
19849
|
-
deployResult.factoraiManifestPath =
|
|
19598
|
+
deployResult.factoraiManifestPath = path28.join(resolvedProjectDir, "factorai.sh.json");
|
|
19850
19599
|
}
|
|
19851
19600
|
console.log(`[deploy-app] Deploy iniciado: ${deployResult.appId}`);
|
|
19852
19601
|
}
|
|
@@ -19994,12 +19743,11 @@ function getFactorAiSandboxToolDefinitions() {
|
|
|
19994
19743
|
type: "function",
|
|
19995
19744
|
function: {
|
|
19996
19745
|
name: "factorai.sh.create_next_app",
|
|
19997
|
-
description: "Create a
|
|
19746
|
+
description: "Create a Next.js 14 project (App Router, standalone, full shadcn/ui kit). Returns agentGuide (Refero-style DESIGN brief: tokens, UI inventory, do/don't) and writes DESIGN.md \u2014 read agentGuide before editing UI.",
|
|
19998
19747
|
parameters: {
|
|
19999
19748
|
type: "object",
|
|
20000
19749
|
properties: {
|
|
20001
19750
|
name: { type: "string", description: "Project name." },
|
|
20002
|
-
template: { type: "string", enum: ["minimal", "full"], description: "Project template." },
|
|
20003
19751
|
directory: { type: "string", description: "Target directory for the project." }
|
|
20004
19752
|
},
|
|
20005
19753
|
required: ["name"],
|
|
@@ -20672,7 +20420,7 @@ var NATIVE_TOOL_ENTRIES = [
|
|
|
20672
20420
|
autoApproveInLocal: false,
|
|
20673
20421
|
autoApproveInSandbox: true,
|
|
20674
20422
|
sandboxOnly: true,
|
|
20675
|
-
description: "Create
|
|
20423
|
+
description: "Create Next.js 14 + full shadcn/ui kit. Returns agentGuide + DESIGN.md. Default page: polished dark landing (Refero-style) \u2014 read agentGuide before extending UI."
|
|
20676
20424
|
},
|
|
20677
20425
|
implementation: createNextApp
|
|
20678
20426
|
},
|
|
@@ -20927,10 +20675,10 @@ var ToolInvoker = class {
|
|
|
20927
20675
|
*/
|
|
20928
20676
|
async initialize() {
|
|
20929
20677
|
try {
|
|
20930
|
-
const currentFilePath =
|
|
20931
|
-
const currentDirPath =
|
|
20932
|
-
const configPath =
|
|
20933
|
-
const fileContent = await
|
|
20678
|
+
const currentFilePath = fileURLToPath2(import.meta.url);
|
|
20679
|
+
const currentDirPath = path29.dirname(currentFilePath);
|
|
20680
|
+
const configPath = path29.resolve(currentDirPath, "config", "native_tools.json");
|
|
20681
|
+
const fileContent = await fs26.readFile(configPath, "utf-8");
|
|
20934
20682
|
const config3 = JSON.parse(fileContent);
|
|
20935
20683
|
this.toolDefinitions = applyMetadataToToolDefinitions(config3.nativeTools);
|
|
20936
20684
|
const sandboxOnlyTools = applyMetadataToToolDefinitions(getSandboxOnlyNativeToolDefinitions());
|
|
@@ -20977,10 +20725,10 @@ var ToolInvoker = class {
|
|
|
20977
20725
|
};
|
|
20978
20726
|
|
|
20979
20727
|
// src/app/agent/tools/mcp/mcp_client.ts
|
|
20980
|
-
import { promises as
|
|
20981
|
-
import
|
|
20728
|
+
import { promises as fs27 } from "fs";
|
|
20729
|
+
import path30 from "path";
|
|
20982
20730
|
import os19 from "os";
|
|
20983
|
-
import { fileURLToPath as
|
|
20731
|
+
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
20984
20732
|
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
20985
20733
|
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
|
|
20986
20734
|
var MCPClient = class {
|
|
@@ -21005,10 +20753,10 @@ var MCPClient = class {
|
|
|
21005
20753
|
originalName: toolName
|
|
21006
20754
|
});
|
|
21007
20755
|
}
|
|
21008
|
-
const __filename =
|
|
21009
|
-
const __dirname2 =
|
|
21010
|
-
const defaultConfigPath =
|
|
21011
|
-
const userConfigPath =
|
|
20756
|
+
const __filename = fileURLToPath3(import.meta.url);
|
|
20757
|
+
const __dirname2 = path30.dirname(__filename);
|
|
20758
|
+
const defaultConfigPath = path30.resolve(__dirname2, "config", "bluma-mcp.json");
|
|
20759
|
+
const userConfigPath = path30.join(os19.homedir(), ".bluma", "bluma-mcp.json");
|
|
21012
20760
|
const defaultConfig = await this.loadMcpConfig(defaultConfigPath, "Default");
|
|
21013
20761
|
const userConfig = await this.loadMcpConfig(userConfigPath, "User");
|
|
21014
20762
|
const mergedConfig = {
|
|
@@ -21042,7 +20790,7 @@ var MCPClient = class {
|
|
|
21042
20790
|
}
|
|
21043
20791
|
async loadMcpConfig(configPath, configType) {
|
|
21044
20792
|
try {
|
|
21045
|
-
const fileContent = await
|
|
20793
|
+
const fileContent = await fs27.readFile(configPath, "utf-8");
|
|
21046
20794
|
const processedContent = this.replaceEnvPlaceholders(fileContent);
|
|
21047
20795
|
return JSON.parse(processedContent);
|
|
21048
20796
|
} catch (error) {
|
|
@@ -21254,24 +21002,24 @@ PENALTY APPLIED: ${penalty.toFixed(1)} points deducted.
|
|
|
21254
21002
|
};
|
|
21255
21003
|
|
|
21256
21004
|
// src/app/agent/bluma/core/bluma.ts
|
|
21257
|
-
import
|
|
21258
|
-
import
|
|
21005
|
+
import path45 from "path";
|
|
21006
|
+
import fs40 from "fs";
|
|
21259
21007
|
import { v4 as uuidv48 } from "uuid";
|
|
21260
21008
|
|
|
21261
21009
|
// src/app/agent/session_manager/session_manager.ts
|
|
21262
|
-
import
|
|
21263
|
-
import { promises as
|
|
21010
|
+
import path33 from "path";
|
|
21011
|
+
import { promises as fs30 } from "fs";
|
|
21264
21012
|
|
|
21265
21013
|
// src/app/agent/session_manager/agent_session_paths.ts
|
|
21266
21014
|
init_bluma_app_dir();
|
|
21267
|
-
import
|
|
21268
|
-
import { promises as
|
|
21015
|
+
import path32 from "path";
|
|
21016
|
+
import { promises as fs29 } from "fs";
|
|
21269
21017
|
|
|
21270
21018
|
// src/app/agent/session_manager/session_index_db.ts
|
|
21271
21019
|
init_bluma_app_dir();
|
|
21272
|
-
import
|
|
21020
|
+
import path31 from "path";
|
|
21273
21021
|
import { mkdirSync as mkdirSync3 } from "fs";
|
|
21274
|
-
import { promises as
|
|
21022
|
+
import { promises as fs28 } from "fs";
|
|
21275
21023
|
import { createRequire } from "module";
|
|
21276
21024
|
var AGENT_SESSION_PATHS_JSONL = "agent_session_paths.jsonl";
|
|
21277
21025
|
var BLUMA_SESSION_DB_FILE = "bluma.sqlite";
|
|
@@ -21284,21 +21032,21 @@ function loadSqlite() {
|
|
|
21284
21032
|
return nodeRequire("better-sqlite3");
|
|
21285
21033
|
}
|
|
21286
21034
|
function getSessionDbPath(appDir = getPreferredAppDir()) {
|
|
21287
|
-
return
|
|
21035
|
+
return path31.join(appDir, BLUMA_SESSION_DB_FILE);
|
|
21288
21036
|
}
|
|
21289
21037
|
function normalizeRelativePath(relativePath) {
|
|
21290
|
-
return relativePath.split(
|
|
21038
|
+
return relativePath.split(path31.sep).join("/");
|
|
21291
21039
|
}
|
|
21292
21040
|
async function walkSessionJsonFiles(dir, onFile) {
|
|
21293
21041
|
let entries;
|
|
21294
21042
|
try {
|
|
21295
|
-
entries = await
|
|
21043
|
+
entries = await fs28.readdir(dir, { withFileTypes: true });
|
|
21296
21044
|
} catch {
|
|
21297
21045
|
return;
|
|
21298
21046
|
}
|
|
21299
21047
|
for (const e of entries) {
|
|
21300
21048
|
const name = String(e.name);
|
|
21301
|
-
const full =
|
|
21049
|
+
const full = path31.join(dir, name);
|
|
21302
21050
|
if (e.isDirectory()) {
|
|
21303
21051
|
await walkSessionJsonFiles(full, onFile);
|
|
21304
21052
|
} else if (e.isFile() && name.endsWith(".json") && !name.includes(".tmp")) {
|
|
@@ -21308,10 +21056,10 @@ async function walkSessionJsonFiles(dir, onFile) {
|
|
|
21308
21056
|
}
|
|
21309
21057
|
async function loadJsonlEntries(appDir) {
|
|
21310
21058
|
const map = /* @__PURE__ */ new Map();
|
|
21311
|
-
const indexFile =
|
|
21059
|
+
const indexFile = path31.join(appDir, AGENT_SESSION_PATHS_JSONL);
|
|
21312
21060
|
let raw;
|
|
21313
21061
|
try {
|
|
21314
|
-
raw = await
|
|
21062
|
+
raw = await fs28.readFile(indexFile, "utf-8");
|
|
21315
21063
|
} catch {
|
|
21316
21064
|
return map;
|
|
21317
21065
|
}
|
|
@@ -21350,7 +21098,7 @@ async function readSessionMetaFromJson(absPath) {
|
|
|
21350
21098
|
updatedAt: (/* @__PURE__ */ new Date(0)).toISOString()
|
|
21351
21099
|
};
|
|
21352
21100
|
try {
|
|
21353
|
-
const raw = await
|
|
21101
|
+
const raw = await fs28.readFile(absPath, "utf-8");
|
|
21354
21102
|
const data = JSON.parse(raw);
|
|
21355
21103
|
const createdAt = data.created_at ?? fallback.createdAt;
|
|
21356
21104
|
const updatedAt = data.last_updated ?? data.created_at ?? fallback.updatedAt;
|
|
@@ -21361,7 +21109,7 @@ async function readSessionMetaFromJson(absPath) {
|
|
|
21361
21109
|
};
|
|
21362
21110
|
} catch {
|
|
21363
21111
|
try {
|
|
21364
|
-
const st = await
|
|
21112
|
+
const st = await fs28.stat(absPath);
|
|
21365
21113
|
const iso = new Date(st.mtimeMs).toISOString();
|
|
21366
21114
|
return { preview: fallback.preview, createdAt: iso, updatedAt: iso };
|
|
21367
21115
|
} catch {
|
|
@@ -21388,7 +21136,7 @@ async function runMigrationV1(db, appDir) {
|
|
|
21388
21136
|
const insertFromPath = async (sessionId, absPath, jsonlUpdatedAt) => {
|
|
21389
21137
|
let rel;
|
|
21390
21138
|
try {
|
|
21391
|
-
rel = normalizeRelativePath(
|
|
21139
|
+
rel = normalizeRelativePath(path31.relative(appDir, absPath));
|
|
21392
21140
|
} catch {
|
|
21393
21141
|
return;
|
|
21394
21142
|
}
|
|
@@ -21403,9 +21151,9 @@ async function runMigrationV1(db, appDir) {
|
|
|
21403
21151
|
});
|
|
21404
21152
|
};
|
|
21405
21153
|
for (const [sessionId, entry] of jsonl) {
|
|
21406
|
-
const full =
|
|
21154
|
+
const full = path31.join(appDir, entry.relativePath.replace(/\//g, path31.sep));
|
|
21407
21155
|
try {
|
|
21408
|
-
await
|
|
21156
|
+
await fs28.access(full);
|
|
21409
21157
|
await insertFromPath(sessionId, full, entry.updatedAt);
|
|
21410
21158
|
} catch {
|
|
21411
21159
|
upsert.run({
|
|
@@ -21417,11 +21165,11 @@ async function runMigrationV1(db, appDir) {
|
|
|
21417
21165
|
});
|
|
21418
21166
|
}
|
|
21419
21167
|
}
|
|
21420
|
-
const sessionsRoot =
|
|
21168
|
+
const sessionsRoot = path31.join(appDir, "sessions");
|
|
21421
21169
|
const existing = db.prepare("SELECT session_id FROM agent_sessions").all();
|
|
21422
21170
|
const seen = new Set(existing.map((r) => r.session_id));
|
|
21423
21171
|
await walkSessionJsonFiles(sessionsRoot, async (absPath) => {
|
|
21424
|
-
const sessionId =
|
|
21172
|
+
const sessionId = path31.basename(absPath, ".json");
|
|
21425
21173
|
if (!sessionId || seen.has(sessionId)) return;
|
|
21426
21174
|
seen.add(sessionId);
|
|
21427
21175
|
await insertFromPath(sessionId, absPath);
|
|
@@ -21456,7 +21204,7 @@ async function ensureSessionIndexDb(appDir = getPreferredAppDir()) {
|
|
|
21456
21204
|
}
|
|
21457
21205
|
const BetterSqlite3 = loadSqlite();
|
|
21458
21206
|
const dbPath = getSessionDbPath(appDir);
|
|
21459
|
-
mkdirSync3(
|
|
21207
|
+
mkdirSync3(path31.dirname(dbPath), { recursive: true });
|
|
21460
21208
|
const db = new BetterSqlite3(dbPath);
|
|
21461
21209
|
db.pragma("journal_mode = WAL");
|
|
21462
21210
|
applySchema(db);
|
|
@@ -21492,7 +21240,7 @@ async function upsertSessionIndexRow(row, appDir = getPreferredAppDir()) {
|
|
|
21492
21240
|
async function getSessionPathFromIndex(sessionId, appDir = getPreferredAppDir()) {
|
|
21493
21241
|
const db = await ensureSessionIndexDb(appDir);
|
|
21494
21242
|
const row = db.prepare("SELECT relative_path FROM agent_sessions WHERE session_id = ?").get(sessionId);
|
|
21495
|
-
return row?.relative_path?.replace(/\//g,
|
|
21243
|
+
return row?.relative_path?.replace(/\//g, path31.sep) ?? null;
|
|
21496
21244
|
}
|
|
21497
21245
|
async function listSessionsFromIndex(limit = 50, appDir = getPreferredAppDir()) {
|
|
21498
21246
|
const db = await ensureSessionIndexDb(appDir);
|
|
@@ -21504,7 +21252,7 @@ async function listSessionsFromIndex(limit = 50, appDir = getPreferredAppDir())
|
|
|
21504
21252
|
).all(limit);
|
|
21505
21253
|
return rows.map((r) => ({
|
|
21506
21254
|
sessionId: r.session_id,
|
|
21507
|
-
relativePath: r.relative_path.replace(/\//g,
|
|
21255
|
+
relativePath: r.relative_path.replace(/\//g, path31.sep),
|
|
21508
21256
|
createdAt: r.created_at,
|
|
21509
21257
|
updatedAt: r.updated_at,
|
|
21510
21258
|
preview: r.preview
|
|
@@ -21516,7 +21264,7 @@ var AGENT_SESSION_PATHS_JSONL2 = "agent_session_paths.jsonl";
|
|
|
21516
21264
|
async function appendAgentSessionPath(sessionId, relativePath) {
|
|
21517
21265
|
const app = getPreferredAppDir();
|
|
21518
21266
|
const now2 = (/* @__PURE__ */ new Date()).toISOString();
|
|
21519
|
-
const rel = relativePath.split(
|
|
21267
|
+
const rel = relativePath.split(path32.sep).join("/");
|
|
21520
21268
|
await upsertSessionIndexRow({
|
|
21521
21269
|
sessionId,
|
|
21522
21270
|
relativePath: rel,
|
|
@@ -21526,10 +21274,10 @@ async function appendAgentSessionPath(sessionId, relativePath) {
|
|
|
21526
21274
|
});
|
|
21527
21275
|
}
|
|
21528
21276
|
async function resolveSessionRelativePathFromJsonl(sessionId) {
|
|
21529
|
-
const indexFile =
|
|
21277
|
+
const indexFile = path32.join(getPreferredAppDir(), AGENT_SESSION_PATHS_JSONL2);
|
|
21530
21278
|
let raw;
|
|
21531
21279
|
try {
|
|
21532
|
-
raw = await
|
|
21280
|
+
raw = await fs29.readFile(indexFile, "utf-8");
|
|
21533
21281
|
} catch {
|
|
21534
21282
|
return null;
|
|
21535
21283
|
}
|
|
@@ -21538,7 +21286,7 @@ async function resolveSessionRelativePathFromJsonl(sessionId) {
|
|
|
21538
21286
|
try {
|
|
21539
21287
|
const row = JSON.parse(lines[i]);
|
|
21540
21288
|
if (row.sessionId === sessionId && typeof row.relativePath === "string") {
|
|
21541
|
-
return row.relativePath.replace(/\//g,
|
|
21289
|
+
return row.relativePath.replace(/\//g, path32.sep);
|
|
21542
21290
|
}
|
|
21543
21291
|
} catch {
|
|
21544
21292
|
}
|
|
@@ -21553,13 +21301,13 @@ async function resolveSessionRelativePathFromIndex(sessionId) {
|
|
|
21553
21301
|
async function walkSessionJsonFiles2(dir, onFile) {
|
|
21554
21302
|
let entries;
|
|
21555
21303
|
try {
|
|
21556
|
-
entries = await
|
|
21304
|
+
entries = await fs29.readdir(dir, { withFileTypes: true });
|
|
21557
21305
|
} catch {
|
|
21558
21306
|
return;
|
|
21559
21307
|
}
|
|
21560
21308
|
for (const e of entries) {
|
|
21561
21309
|
const name = String(e.name);
|
|
21562
|
-
const full =
|
|
21310
|
+
const full = path32.join(dir, name);
|
|
21563
21311
|
if (e.isDirectory()) {
|
|
21564
21312
|
await walkSessionJsonFiles2(full, onFile);
|
|
21565
21313
|
} else if (e.isFile() && name.endsWith(".json") && !name.includes(".tmp")) {
|
|
@@ -21568,12 +21316,12 @@ async function walkSessionJsonFiles2(dir, onFile) {
|
|
|
21568
21316
|
}
|
|
21569
21317
|
}
|
|
21570
21318
|
async function findSessionFileGlobFallback(sessionId) {
|
|
21571
|
-
const sessionsRoot =
|
|
21319
|
+
const sessionsRoot = path32.join(getPreferredAppDir(), "sessions");
|
|
21572
21320
|
const state2 = { best: null };
|
|
21573
21321
|
await walkSessionJsonFiles2(sessionsRoot, async (abs) => {
|
|
21574
|
-
if (
|
|
21322
|
+
if (path32.basename(abs, ".json") !== sessionId) return;
|
|
21575
21323
|
try {
|
|
21576
|
-
const st = await
|
|
21324
|
+
const st = await fs29.stat(abs);
|
|
21577
21325
|
if (!st.isFile()) return;
|
|
21578
21326
|
if (!state2.best || st.mtimeMs > state2.best.m) {
|
|
21579
21327
|
state2.best = { p: abs, m: st.mtimeMs };
|
|
@@ -21584,11 +21332,11 @@ async function findSessionFileGlobFallback(sessionId) {
|
|
|
21584
21332
|
return state2.best !== null ? state2.best.p : null;
|
|
21585
21333
|
}
|
|
21586
21334
|
async function loadLatestIndexEntriesBySessionId() {
|
|
21587
|
-
const indexFile =
|
|
21335
|
+
const indexFile = path32.join(getPreferredAppDir(), AGENT_SESSION_PATHS_JSONL2);
|
|
21588
21336
|
const map = /* @__PURE__ */ new Map();
|
|
21589
21337
|
let raw;
|
|
21590
21338
|
try {
|
|
21591
|
-
raw = await
|
|
21339
|
+
raw = await fs29.readFile(indexFile, "utf-8");
|
|
21592
21340
|
} catch {
|
|
21593
21341
|
return map;
|
|
21594
21342
|
}
|
|
@@ -21624,11 +21372,11 @@ function dateFolderFromRelativePath(relativePath) {
|
|
|
21624
21372
|
async function enrichCandidateFromFile(absPath, sessionId, lastMtimeMs, opts) {
|
|
21625
21373
|
let rel;
|
|
21626
21374
|
try {
|
|
21627
|
-
rel =
|
|
21375
|
+
rel = path32.relative(getPreferredAppDir(), absPath);
|
|
21628
21376
|
} catch {
|
|
21629
21377
|
rel = absPath;
|
|
21630
21378
|
}
|
|
21631
|
-
const dateFolder = dateFolderFromRelativePath(rel.split(
|
|
21379
|
+
const dateFolder = dateFolderFromRelativePath(rel.split(path32.sep).join("/"));
|
|
21632
21380
|
let preview = opts?.preview ?? "(no messages)";
|
|
21633
21381
|
let lastActivityMs = lastMtimeMs;
|
|
21634
21382
|
if (opts?.updatedAtIso) {
|
|
@@ -21639,7 +21387,7 @@ async function enrichCandidateFromFile(absPath, sessionId, lastMtimeMs, opts) {
|
|
|
21639
21387
|
}
|
|
21640
21388
|
if (!opts?.preview || opts.preview === "(no messages)") {
|
|
21641
21389
|
try {
|
|
21642
|
-
const raw = await
|
|
21390
|
+
const raw = await fs29.readFile(absPath, "utf-8");
|
|
21643
21391
|
const data = JSON.parse(raw);
|
|
21644
21392
|
preview = previewFromConversationHistory(data.conversation_history);
|
|
21645
21393
|
const iso = data.last_updated || data.created_at;
|
|
@@ -21667,9 +21415,9 @@ async function listAgentSessionsForResume(limit = 50) {
|
|
|
21667
21415
|
const fromIndex = await listSessionsFromIndex(limit * 2, appDir);
|
|
21668
21416
|
const map = /* @__PURE__ */ new Map();
|
|
21669
21417
|
for (const row of fromIndex) {
|
|
21670
|
-
const full =
|
|
21418
|
+
const full = path32.join(appDir, row.relativePath);
|
|
21671
21419
|
try {
|
|
21672
|
-
const st = await
|
|
21420
|
+
const st = await fs29.stat(full);
|
|
21673
21421
|
if (!st.isFile()) continue;
|
|
21674
21422
|
map.set(row.sessionId, {
|
|
21675
21423
|
path: full,
|
|
@@ -21680,12 +21428,12 @@ async function listAgentSessionsForResume(limit = 50) {
|
|
|
21680
21428
|
} catch {
|
|
21681
21429
|
}
|
|
21682
21430
|
}
|
|
21683
|
-
const sessionsRoot =
|
|
21431
|
+
const sessionsRoot = path32.join(appDir, "sessions");
|
|
21684
21432
|
await walkSessionJsonFiles2(sessionsRoot, async (absPath) => {
|
|
21685
|
-
const id =
|
|
21433
|
+
const id = path32.basename(absPath, ".json");
|
|
21686
21434
|
if (!id) return;
|
|
21687
21435
|
try {
|
|
21688
|
-
const st = await
|
|
21436
|
+
const st = await fs29.stat(absPath);
|
|
21689
21437
|
if (!st.isFile()) return;
|
|
21690
21438
|
const cur = map.get(id);
|
|
21691
21439
|
if (!cur || st.mtimeMs > cur.lastMtimeMs) {
|
|
@@ -21697,9 +21445,9 @@ async function listAgentSessionsForResume(limit = 50) {
|
|
|
21697
21445
|
const index = await loadLatestIndexEntriesBySessionId();
|
|
21698
21446
|
for (const [sessionId, entry] of index) {
|
|
21699
21447
|
if (map.has(sessionId)) continue;
|
|
21700
|
-
const full =
|
|
21448
|
+
const full = path32.join(appDir, entry.relativePath.replace(/\//g, path32.sep));
|
|
21701
21449
|
try {
|
|
21702
|
-
const st = await
|
|
21450
|
+
const st = await fs29.stat(full);
|
|
21703
21451
|
if (st.isFile()) {
|
|
21704
21452
|
map.set(sessionId, { path: full, lastMtimeMs: st.mtimeMs });
|
|
21705
21453
|
}
|
|
@@ -21758,10 +21506,10 @@ async function safeRenameWithRetry(src, dest, maxRetries = 6) {
|
|
|
21758
21506
|
const isWin = process.platform === "win32";
|
|
21759
21507
|
while (attempt <= maxRetries) {
|
|
21760
21508
|
try {
|
|
21761
|
-
const dir =
|
|
21762
|
-
await
|
|
21509
|
+
const dir = path33.dirname(dest);
|
|
21510
|
+
await fs30.mkdir(dir, { recursive: true }).catch(() => {
|
|
21763
21511
|
});
|
|
21764
|
-
await
|
|
21512
|
+
await fs30.rename(src, dest);
|
|
21765
21513
|
return;
|
|
21766
21514
|
} catch (e) {
|
|
21767
21515
|
lastErr = e;
|
|
@@ -21774,13 +21522,13 @@ async function safeRenameWithRetry(src, dest, maxRetries = 6) {
|
|
|
21774
21522
|
}
|
|
21775
21523
|
}
|
|
21776
21524
|
try {
|
|
21777
|
-
await
|
|
21778
|
-
const data = await
|
|
21779
|
-
const dir =
|
|
21780
|
-
await
|
|
21525
|
+
await fs30.access(src);
|
|
21526
|
+
const data = await fs30.readFile(src);
|
|
21527
|
+
const dir = path33.dirname(dest);
|
|
21528
|
+
await fs30.mkdir(dir, { recursive: true }).catch(() => {
|
|
21781
21529
|
});
|
|
21782
|
-
await
|
|
21783
|
-
await
|
|
21530
|
+
await fs30.writeFile(dest, data);
|
|
21531
|
+
await fs30.unlink(src).catch(() => {
|
|
21784
21532
|
});
|
|
21785
21533
|
return;
|
|
21786
21534
|
} catch (fallbackErr) {
|
|
@@ -21793,23 +21541,23 @@ async function safeRenameWithRetry(src, dest, maxRetries = 6) {
|
|
|
21793
21541
|
}
|
|
21794
21542
|
async function ensureSessionDir() {
|
|
21795
21543
|
const appDir = getPreferredAppDir();
|
|
21796
|
-
const sessionDir =
|
|
21797
|
-
await
|
|
21544
|
+
const sessionDir = path33.join(appDir, "sessions");
|
|
21545
|
+
await fs30.mkdir(sessionDir, { recursive: true });
|
|
21798
21546
|
return sessionDir;
|
|
21799
21547
|
}
|
|
21800
21548
|
async function resolveAgentSessionFilePath(sessionId) {
|
|
21801
21549
|
const appDir = getPreferredAppDir();
|
|
21802
|
-
const legacy =
|
|
21550
|
+
const legacy = path33.join(appDir, "sessions", `${sessionId}.json`);
|
|
21803
21551
|
try {
|
|
21804
|
-
await
|
|
21552
|
+
await fs30.access(legacy);
|
|
21805
21553
|
return legacy;
|
|
21806
21554
|
} catch {
|
|
21807
21555
|
}
|
|
21808
21556
|
const rel = await resolveSessionRelativePathFromIndex(sessionId);
|
|
21809
21557
|
if (rel) {
|
|
21810
|
-
const full =
|
|
21558
|
+
const full = path33.join(appDir, rel);
|
|
21811
21559
|
try {
|
|
21812
|
-
await
|
|
21560
|
+
await fs30.access(full);
|
|
21813
21561
|
return full;
|
|
21814
21562
|
} catch {
|
|
21815
21563
|
}
|
|
@@ -21822,7 +21570,7 @@ async function loadSession(sessionId) {
|
|
|
21822
21570
|
return null;
|
|
21823
21571
|
}
|
|
21824
21572
|
try {
|
|
21825
|
-
const fileContent = await
|
|
21573
|
+
const fileContent = await fs30.readFile(sessionFile, "utf-8");
|
|
21826
21574
|
const sessionData = JSON.parse(fileContent);
|
|
21827
21575
|
const memory = {
|
|
21828
21576
|
historyAnchor: sessionData.history_anchor ?? null,
|
|
@@ -21843,16 +21591,16 @@ async function loadOrcreateSession(sessionId) {
|
|
|
21843
21591
|
const y = String(now2.getFullYear());
|
|
21844
21592
|
const mo = String(now2.getMonth() + 1).padStart(2, "0");
|
|
21845
21593
|
const d = String(now2.getDate()).padStart(2, "0");
|
|
21846
|
-
const datedDir =
|
|
21847
|
-
await
|
|
21848
|
-
const sessionFile =
|
|
21594
|
+
const datedDir = path33.join(sessionsRoot, y, mo, d);
|
|
21595
|
+
await fs30.mkdir(datedDir, { recursive: true });
|
|
21596
|
+
const sessionFile = path33.join(datedDir, `${sessionId}.json`);
|
|
21849
21597
|
const newSessionData = {
|
|
21850
21598
|
session_id: sessionId,
|
|
21851
21599
|
created_at: now2.toISOString(),
|
|
21852
21600
|
conversation_history: []
|
|
21853
21601
|
};
|
|
21854
|
-
await
|
|
21855
|
-
const relToApp =
|
|
21602
|
+
await fs30.writeFile(sessionFile, JSON.stringify(newSessionData, null, 2), "utf-8");
|
|
21603
|
+
const relToApp = path33.relative(getPreferredAppDir(), sessionFile);
|
|
21856
21604
|
await appendAgentSessionPath(sessionId, relToApp);
|
|
21857
21605
|
const emptyMemory = {
|
|
21858
21606
|
historyAnchor: null,
|
|
@@ -21864,12 +21612,12 @@ async function doSaveSessionHistory(sessionFile, history, memory) {
|
|
|
21864
21612
|
await withFileLock(sessionFile, async () => {
|
|
21865
21613
|
let sessionData;
|
|
21866
21614
|
try {
|
|
21867
|
-
const dir =
|
|
21868
|
-
await
|
|
21615
|
+
const dir = path33.dirname(sessionFile);
|
|
21616
|
+
await fs30.mkdir(dir, { recursive: true });
|
|
21869
21617
|
} catch {
|
|
21870
21618
|
}
|
|
21871
21619
|
try {
|
|
21872
|
-
const fileContent = await
|
|
21620
|
+
const fileContent = await fs30.readFile(sessionFile, "utf-8");
|
|
21873
21621
|
sessionData = JSON.parse(fileContent);
|
|
21874
21622
|
} catch (error) {
|
|
21875
21623
|
const code = error && error.code;
|
|
@@ -21880,14 +21628,14 @@ async function doSaveSessionHistory(sessionFile, history, memory) {
|
|
|
21880
21628
|
console.warn(`An unknown error occurred while reading ${sessionFile}. Re-initializing.`, error);
|
|
21881
21629
|
}
|
|
21882
21630
|
}
|
|
21883
|
-
const sessionId =
|
|
21631
|
+
const sessionId = path33.basename(sessionFile, ".json");
|
|
21884
21632
|
sessionData = {
|
|
21885
21633
|
session_id: sessionId,
|
|
21886
21634
|
created_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
21887
21635
|
conversation_history: []
|
|
21888
21636
|
};
|
|
21889
21637
|
try {
|
|
21890
|
-
await
|
|
21638
|
+
await fs30.writeFile(sessionFile, JSON.stringify(sessionData, null, 2), "utf-8");
|
|
21891
21639
|
} catch {
|
|
21892
21640
|
}
|
|
21893
21641
|
}
|
|
@@ -21903,14 +21651,14 @@ async function doSaveSessionHistory(sessionFile, history, memory) {
|
|
|
21903
21651
|
}
|
|
21904
21652
|
const tempSessionFile = `${sessionFile}.${Date.now()}.tmp`;
|
|
21905
21653
|
try {
|
|
21906
|
-
await
|
|
21654
|
+
await fs30.writeFile(tempSessionFile, JSON.stringify(sessionData, null, 2), "utf-8");
|
|
21907
21655
|
await safeRenameWithRetry(tempSessionFile, sessionFile);
|
|
21908
|
-
const sessionId = sessionData.session_id ??
|
|
21656
|
+
const sessionId = sessionData.session_id ?? path33.basename(sessionFile, ".json");
|
|
21909
21657
|
const updatedAt = sessionData.last_updated ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
21910
21658
|
const createdAt = sessionData.created_at ?? updatedAt;
|
|
21911
21659
|
let relToApp;
|
|
21912
21660
|
try {
|
|
21913
|
-
relToApp =
|
|
21661
|
+
relToApp = path33.relative(getPreferredAppDir(), sessionFile);
|
|
21914
21662
|
} catch {
|
|
21915
21663
|
relToApp = sessionFile;
|
|
21916
21664
|
}
|
|
@@ -21930,7 +21678,7 @@ async function doSaveSessionHistory(sessionFile, history, memory) {
|
|
|
21930
21678
|
console.error(`An unknown fatal error occurred while saving session to ${sessionFile}:`, writeError);
|
|
21931
21679
|
}
|
|
21932
21680
|
try {
|
|
21933
|
-
await
|
|
21681
|
+
await fs30.unlink(tempSessionFile);
|
|
21934
21682
|
} catch {
|
|
21935
21683
|
}
|
|
21936
21684
|
}
|
|
@@ -21962,15 +21710,15 @@ async function saveSessionHistoryNow(sessionFile, history, memory) {
|
|
|
21962
21710
|
|
|
21963
21711
|
// src/app/agent/core/prompt/prompt_builder.ts
|
|
21964
21712
|
import os23 from "os";
|
|
21965
|
-
import
|
|
21966
|
-
import
|
|
21713
|
+
import fs36 from "fs";
|
|
21714
|
+
import path39 from "path";
|
|
21967
21715
|
import { execSync as execSync3 } from "child_process";
|
|
21968
21716
|
|
|
21969
21717
|
// src/app/agent/skills/skill_loader.ts
|
|
21970
|
-
import
|
|
21971
|
-
import
|
|
21718
|
+
import fs31 from "fs";
|
|
21719
|
+
import path34 from "path";
|
|
21972
21720
|
import os20 from "os";
|
|
21973
|
-
import { fileURLToPath as
|
|
21721
|
+
import { fileURLToPath as fileURLToPath4 } from "node:url";
|
|
21974
21722
|
var SkillLoader = class _SkillLoader {
|
|
21975
21723
|
bundledSkillsDir;
|
|
21976
21724
|
projectSkillsDir;
|
|
@@ -21978,8 +21726,8 @@ var SkillLoader = class _SkillLoader {
|
|
|
21978
21726
|
cache = /* @__PURE__ */ new Map();
|
|
21979
21727
|
conflicts = [];
|
|
21980
21728
|
constructor(projectRoot, bundledDir) {
|
|
21981
|
-
this.projectSkillsDir =
|
|
21982
|
-
this.globalSkillsDir =
|
|
21729
|
+
this.projectSkillsDir = path34.join(projectRoot, ".bluma", "skills");
|
|
21730
|
+
this.globalSkillsDir = path34.join(os20.homedir(), ".bluma", "skills");
|
|
21983
21731
|
this.bundledSkillsDir = bundledDir || _SkillLoader.resolveBundledDir();
|
|
21984
21732
|
}
|
|
21985
21733
|
/**
|
|
@@ -21988,48 +21736,48 @@ var SkillLoader = class _SkillLoader {
|
|
|
21988
21736
|
*/
|
|
21989
21737
|
static resolveBundledDir() {
|
|
21990
21738
|
if (process.env.JEST_WORKER_ID !== void 0 || process.env.NODE_ENV === "test") {
|
|
21991
|
-
return
|
|
21739
|
+
return path34.join(process.cwd(), "dist", "config", "skills");
|
|
21992
21740
|
}
|
|
21993
21741
|
const candidates = [];
|
|
21994
21742
|
const push = (p) => {
|
|
21995
|
-
const abs =
|
|
21743
|
+
const abs = path34.resolve(p);
|
|
21996
21744
|
if (!candidates.includes(abs)) {
|
|
21997
21745
|
candidates.push(abs);
|
|
21998
21746
|
}
|
|
21999
21747
|
};
|
|
22000
21748
|
let argvBundled = null;
|
|
22001
21749
|
try {
|
|
22002
|
-
const bundleDir =
|
|
22003
|
-
push(
|
|
21750
|
+
const bundleDir = path34.dirname(fileURLToPath4(import.meta.url));
|
|
21751
|
+
push(path34.join(bundleDir, "config", "skills"));
|
|
22004
21752
|
} catch {
|
|
22005
21753
|
}
|
|
22006
21754
|
const argv1 = process.argv[1];
|
|
22007
21755
|
if (argv1 && !argv1.startsWith("-")) {
|
|
22008
21756
|
try {
|
|
22009
21757
|
let resolved = argv1;
|
|
22010
|
-
if (
|
|
22011
|
-
resolved =
|
|
22012
|
-
} else if (!
|
|
22013
|
-
resolved =
|
|
21758
|
+
if (path34.isAbsolute(argv1) && fs31.existsSync(argv1)) {
|
|
21759
|
+
resolved = fs31.realpathSync(argv1);
|
|
21760
|
+
} else if (!path34.isAbsolute(argv1)) {
|
|
21761
|
+
resolved = path34.resolve(process.cwd(), argv1);
|
|
22014
21762
|
}
|
|
22015
|
-
const scriptDir =
|
|
22016
|
-
argvBundled =
|
|
21763
|
+
const scriptDir = path34.dirname(resolved);
|
|
21764
|
+
argvBundled = path34.join(scriptDir, "config", "skills");
|
|
22017
21765
|
push(argvBundled);
|
|
22018
21766
|
} catch {
|
|
22019
21767
|
}
|
|
22020
21768
|
}
|
|
22021
21769
|
for (const abs of candidates) {
|
|
22022
|
-
if (
|
|
21770
|
+
if (fs31.existsSync(abs)) {
|
|
22023
21771
|
return abs;
|
|
22024
21772
|
}
|
|
22025
21773
|
}
|
|
22026
21774
|
try {
|
|
22027
|
-
return
|
|
21775
|
+
return path34.join(path34.dirname(fileURLToPath4(import.meta.url)), "config", "skills");
|
|
22028
21776
|
} catch {
|
|
22029
21777
|
if (argvBundled) {
|
|
22030
21778
|
return argvBundled;
|
|
22031
21779
|
}
|
|
22032
|
-
return
|
|
21780
|
+
return path34.join(os20.homedir(), ".bluma", "__bundled_skills_unresolved__");
|
|
22033
21781
|
}
|
|
22034
21782
|
}
|
|
22035
21783
|
/**
|
|
@@ -22058,8 +21806,8 @@ var SkillLoader = class _SkillLoader {
|
|
|
22058
21806
|
this.conflicts.push({
|
|
22059
21807
|
name: skill.name,
|
|
22060
21808
|
userSource: source,
|
|
22061
|
-
userPath:
|
|
22062
|
-
bundledPath:
|
|
21809
|
+
userPath: path34.join(dir, skill.name, "SKILL.md"),
|
|
21810
|
+
bundledPath: path34.join(this.bundledSkillsDir, skill.name, "SKILL.md")
|
|
22063
21811
|
});
|
|
22064
21812
|
continue;
|
|
22065
21813
|
}
|
|
@@ -22067,20 +21815,20 @@ var SkillLoader = class _SkillLoader {
|
|
|
22067
21815
|
}
|
|
22068
21816
|
}
|
|
22069
21817
|
listFromDir(dir, source) {
|
|
22070
|
-
if (!
|
|
21818
|
+
if (!fs31.existsSync(dir)) return [];
|
|
22071
21819
|
try {
|
|
22072
|
-
return
|
|
22073
|
-
const fullPath =
|
|
22074
|
-
return
|
|
22075
|
-
}).map((d) => this.loadMetadataFromPath(
|
|
21820
|
+
return fs31.readdirSync(dir).filter((d) => {
|
|
21821
|
+
const fullPath = path34.join(dir, d);
|
|
21822
|
+
return fs31.statSync(fullPath).isDirectory() && fs31.existsSync(path34.join(fullPath, "SKILL.md"));
|
|
21823
|
+
}).map((d) => this.loadMetadataFromPath(path34.join(dir, d, "SKILL.md"), d, source)).filter((m) => m !== null);
|
|
22076
21824
|
} catch {
|
|
22077
21825
|
return [];
|
|
22078
21826
|
}
|
|
22079
21827
|
}
|
|
22080
21828
|
loadMetadataFromPath(skillPath, skillName, source) {
|
|
22081
|
-
if (!
|
|
21829
|
+
if (!fs31.existsSync(skillPath)) return null;
|
|
22082
21830
|
try {
|
|
22083
|
-
const raw =
|
|
21831
|
+
const raw = fs31.readFileSync(skillPath, "utf-8");
|
|
22084
21832
|
const parsed = this.parseFrontmatter(raw);
|
|
22085
21833
|
return {
|
|
22086
21834
|
name: parsed.name || skillName,
|
|
@@ -22102,12 +21850,12 @@ var SkillLoader = class _SkillLoader {
|
|
|
22102
21850
|
*/
|
|
22103
21851
|
load(name) {
|
|
22104
21852
|
if (this.cache.has(name)) return this.cache.get(name);
|
|
22105
|
-
const bundledPath =
|
|
22106
|
-
const projectPath =
|
|
22107
|
-
const globalPath =
|
|
22108
|
-
const existsBundled =
|
|
22109
|
-
const existsProject =
|
|
22110
|
-
const existsGlobal =
|
|
21853
|
+
const bundledPath = path34.join(this.bundledSkillsDir, name, "SKILL.md");
|
|
21854
|
+
const projectPath = path34.join(this.projectSkillsDir, name, "SKILL.md");
|
|
21855
|
+
const globalPath = path34.join(this.globalSkillsDir, name, "SKILL.md");
|
|
21856
|
+
const existsBundled = fs31.existsSync(bundledPath);
|
|
21857
|
+
const existsProject = fs31.existsSync(projectPath);
|
|
21858
|
+
const existsGlobal = fs31.existsSync(globalPath);
|
|
22111
21859
|
if (existsBundled && (existsProject || existsGlobal)) {
|
|
22112
21860
|
const conflictSource = existsProject ? "project" : "global";
|
|
22113
21861
|
const conflictPath = existsProject ? projectPath : globalPath;
|
|
@@ -22146,9 +21894,9 @@ var SkillLoader = class _SkillLoader {
|
|
|
22146
21894
|
}
|
|
22147
21895
|
loadFromPath(skillPath, name, source) {
|
|
22148
21896
|
try {
|
|
22149
|
-
const raw =
|
|
21897
|
+
const raw = fs31.readFileSync(skillPath, "utf-8");
|
|
22150
21898
|
const parsed = this.parseFrontmatter(raw);
|
|
22151
|
-
const skillDir =
|
|
21899
|
+
const skillDir = path34.dirname(skillPath);
|
|
22152
21900
|
return {
|
|
22153
21901
|
name: parsed.name || name,
|
|
22154
21902
|
description: parsed.description || "",
|
|
@@ -22157,22 +21905,22 @@ var SkillLoader = class _SkillLoader {
|
|
|
22157
21905
|
version: parsed.version,
|
|
22158
21906
|
author: parsed.author,
|
|
22159
21907
|
license: parsed.license,
|
|
22160
|
-
references: this.scanAssets(
|
|
22161
|
-
scripts: this.scanAssets(
|
|
21908
|
+
references: this.scanAssets(path34.join(skillDir, "references")),
|
|
21909
|
+
scripts: this.scanAssets(path34.join(skillDir, "scripts"))
|
|
22162
21910
|
};
|
|
22163
21911
|
} catch {
|
|
22164
21912
|
return null;
|
|
22165
21913
|
}
|
|
22166
21914
|
}
|
|
22167
21915
|
scanAssets(dir) {
|
|
22168
|
-
if (!
|
|
21916
|
+
if (!fs31.existsSync(dir)) return [];
|
|
22169
21917
|
try {
|
|
22170
|
-
return
|
|
22171
|
-
const fp =
|
|
22172
|
-
return
|
|
21918
|
+
return fs31.readdirSync(dir).filter((f) => {
|
|
21919
|
+
const fp = path34.join(dir, f);
|
|
21920
|
+
return fs31.statSync(fp).isFile();
|
|
22173
21921
|
}).map((f) => ({
|
|
22174
21922
|
name: f,
|
|
22175
|
-
path:
|
|
21923
|
+
path: path34.resolve(dir, f)
|
|
22176
21924
|
}));
|
|
22177
21925
|
} catch {
|
|
22178
21926
|
return [];
|
|
@@ -22229,10 +21977,10 @@ var SkillLoader = class _SkillLoader {
|
|
|
22229
21977
|
this.cache.clear();
|
|
22230
21978
|
}
|
|
22231
21979
|
exists(name) {
|
|
22232
|
-
const bundledPath =
|
|
22233
|
-
const projectPath =
|
|
22234
|
-
const globalPath =
|
|
22235
|
-
return
|
|
21980
|
+
const bundledPath = path34.join(this.bundledSkillsDir, name, "SKILL.md");
|
|
21981
|
+
const projectPath = path34.join(this.projectSkillsDir, name, "SKILL.md");
|
|
21982
|
+
const globalPath = path34.join(this.globalSkillsDir, name, "SKILL.md");
|
|
21983
|
+
return fs31.existsSync(bundledPath) || fs31.existsSync(projectPath) || fs31.existsSync(globalPath);
|
|
22236
21984
|
}
|
|
22237
21985
|
/**
|
|
22238
21986
|
* Retorna conflitos detetados (skills do utilizador com mesmo nome de nativas).
|
|
@@ -22264,13 +22012,13 @@ var SkillLoader = class _SkillLoader {
|
|
|
22264
22012
|
};
|
|
22265
22013
|
|
|
22266
22014
|
// src/app/agent/core/prompt/workspace_snapshot.ts
|
|
22267
|
-
import
|
|
22268
|
-
import
|
|
22015
|
+
import fs33 from "fs";
|
|
22016
|
+
import path36 from "path";
|
|
22269
22017
|
import { execSync as execSync2 } from "child_process";
|
|
22270
22018
|
|
|
22271
22019
|
// src/app/agent/utils/blumamd.ts
|
|
22272
|
-
import
|
|
22273
|
-
import
|
|
22020
|
+
import fs32 from "fs";
|
|
22021
|
+
import path35 from "path";
|
|
22274
22022
|
import os21 from "os";
|
|
22275
22023
|
import { execSync } from "child_process";
|
|
22276
22024
|
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.";
|
|
@@ -22400,12 +22148,12 @@ var TEXT_FILE_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
|
22400
22148
|
function expandIncludePath(includePath, baseDir) {
|
|
22401
22149
|
const cleanPath = includePath.startsWith("@") ? includePath.slice(1) : includePath;
|
|
22402
22150
|
if (cleanPath.startsWith("~")) {
|
|
22403
|
-
return
|
|
22151
|
+
return path35.join(os21.homedir(), cleanPath.slice(1));
|
|
22404
22152
|
}
|
|
22405
|
-
if (
|
|
22153
|
+
if (path35.isAbsolute(cleanPath)) {
|
|
22406
22154
|
return cleanPath;
|
|
22407
22155
|
}
|
|
22408
|
-
return
|
|
22156
|
+
return path35.resolve(baseDir, cleanPath);
|
|
22409
22157
|
}
|
|
22410
22158
|
function processIncludes(content, baseDir, processedFiles) {
|
|
22411
22159
|
const lines = content.split("\n");
|
|
@@ -22414,20 +22162,20 @@ function processIncludes(content, baseDir, processedFiles) {
|
|
|
22414
22162
|
const includeMatch = line.match(/^@\s*([^\s]+)/);
|
|
22415
22163
|
if (includeMatch) {
|
|
22416
22164
|
const includePath = expandIncludePath(includeMatch[1], baseDir);
|
|
22417
|
-
const normalizedPath =
|
|
22165
|
+
const normalizedPath = path35.normalize(includePath);
|
|
22418
22166
|
if (processedFiles.has(normalizedPath)) {
|
|
22419
22167
|
result.push(`<!-- Circular include prevented: ${includeMatch[1]} -->`);
|
|
22420
22168
|
continue;
|
|
22421
22169
|
}
|
|
22422
|
-
const ext =
|
|
22170
|
+
const ext = path35.extname(includePath).toLowerCase();
|
|
22423
22171
|
if (!TEXT_FILE_EXTENSIONS.has(ext)) {
|
|
22424
22172
|
result.push(`<!-- Include skipped (unsupported extension): ${includeMatch[1]} -->`);
|
|
22425
22173
|
continue;
|
|
22426
22174
|
}
|
|
22427
22175
|
try {
|
|
22428
|
-
const includedContent =
|
|
22176
|
+
const includedContent = fs32.readFileSync(includePath, "utf-8");
|
|
22429
22177
|
processedFiles.add(normalizedPath);
|
|
22430
|
-
const processedContent = processIncludes(includedContent,
|
|
22178
|
+
const processedContent = processIncludes(includedContent, path35.dirname(includePath), processedFiles);
|
|
22431
22179
|
result.push(`
|
|
22432
22180
|
<!-- BEGIN INCLUDE ${includeMatch[1]} -->
|
|
22433
22181
|
`);
|
|
@@ -22473,9 +22221,9 @@ function parseFrontmatterPaths(paths2) {
|
|
|
22473
22221
|
}
|
|
22474
22222
|
function readMemoryFile(filePath, type, includeBasePath) {
|
|
22475
22223
|
try {
|
|
22476
|
-
const rawContent =
|
|
22477
|
-
const baseDir = includeBasePath ||
|
|
22478
|
-
const processedFiles = /* @__PURE__ */ new Set([
|
|
22224
|
+
const rawContent = fs32.readFileSync(filePath, "utf-8");
|
|
22225
|
+
const baseDir = includeBasePath || path35.dirname(filePath);
|
|
22226
|
+
const processedFiles = /* @__PURE__ */ new Set([path35.normalize(filePath)]);
|
|
22479
22227
|
const { frontmatter, content: withoutFrontmatter } = parseFrontmatter(rawContent);
|
|
22480
22228
|
const globs = parseFrontmatterPaths(frontmatter.paths);
|
|
22481
22229
|
const processedContent = processIncludes(withoutFrontmatter, baseDir, processedFiles);
|
|
@@ -22497,15 +22245,15 @@ function readMemoryFile(filePath, type, includeBasePath) {
|
|
|
22497
22245
|
}
|
|
22498
22246
|
function findGitRoot(startDir) {
|
|
22499
22247
|
let current = startDir;
|
|
22500
|
-
while (current !==
|
|
22501
|
-
const gitPath =
|
|
22248
|
+
while (current !== path35.dirname(current)) {
|
|
22249
|
+
const gitPath = path35.join(current, ".git");
|
|
22502
22250
|
try {
|
|
22503
|
-
if (
|
|
22251
|
+
if (fs32.existsSync(gitPath)) {
|
|
22504
22252
|
return current;
|
|
22505
22253
|
}
|
|
22506
22254
|
} catch {
|
|
22507
22255
|
}
|
|
22508
|
-
current =
|
|
22256
|
+
current = path35.dirname(current);
|
|
22509
22257
|
}
|
|
22510
22258
|
return null;
|
|
22511
22259
|
}
|
|
@@ -22530,17 +22278,17 @@ function getGitUserInfo(cwd2) {
|
|
|
22530
22278
|
}
|
|
22531
22279
|
function processRulesDirectory(rulesDir, type, processedPaths, conditionalRule = false) {
|
|
22532
22280
|
const result = [];
|
|
22533
|
-
if (!
|
|
22281
|
+
if (!fs32.existsSync(rulesDir)) {
|
|
22534
22282
|
return result;
|
|
22535
22283
|
}
|
|
22536
22284
|
try {
|
|
22537
|
-
const entries =
|
|
22285
|
+
const entries = fs32.readdirSync(rulesDir, { withFileTypes: true });
|
|
22538
22286
|
for (const entry of entries) {
|
|
22539
|
-
const entryPath =
|
|
22287
|
+
const entryPath = path35.join(rulesDir, entry.name);
|
|
22540
22288
|
if (entry.isDirectory()) {
|
|
22541
22289
|
result.push(...processRulesDirectory(entryPath, type, processedPaths, conditionalRule));
|
|
22542
22290
|
} else if (entry.isFile() && entry.name.endsWith(".md")) {
|
|
22543
|
-
const normalizedPath =
|
|
22291
|
+
const normalizedPath = path35.normalize(entryPath);
|
|
22544
22292
|
if (processedPaths.has(normalizedPath)) {
|
|
22545
22293
|
continue;
|
|
22546
22294
|
}
|
|
@@ -22576,13 +22324,13 @@ function loadManagedMemory() {
|
|
|
22576
22324
|
function loadUserMemory() {
|
|
22577
22325
|
const files = [];
|
|
22578
22326
|
const homeDir = os21.homedir();
|
|
22579
|
-
const userBlumaDir =
|
|
22580
|
-
const userBlumaMd =
|
|
22327
|
+
const userBlumaDir = path35.join(homeDir, ".bluma");
|
|
22328
|
+
const userBlumaMd = path35.join(userBlumaDir, "BLUMA.md");
|
|
22581
22329
|
const userFile = readMemoryFile(userBlumaMd, "User");
|
|
22582
22330
|
if (userFile && userFile.content.trim()) {
|
|
22583
22331
|
files.push(userFile);
|
|
22584
22332
|
}
|
|
22585
|
-
const userRulesDir =
|
|
22333
|
+
const userRulesDir = path35.join(userBlumaDir, "rules");
|
|
22586
22334
|
const processedPaths = /* @__PURE__ */ new Set();
|
|
22587
22335
|
files.push(...processRulesDirectory(userRulesDir, "User", processedPaths, false));
|
|
22588
22336
|
return files;
|
|
@@ -22594,10 +22342,10 @@ function loadProjectMemory(cwd2) {
|
|
|
22594
22342
|
let currentDir = cwd2;
|
|
22595
22343
|
const MAX_TRAVERSAL_DEPTH = 20;
|
|
22596
22344
|
let depth = 0;
|
|
22597
|
-
const normalizedGitRoot =
|
|
22598
|
-
while (currentDir !==
|
|
22345
|
+
const normalizedGitRoot = path35.resolve(gitRoot);
|
|
22346
|
+
while (currentDir !== path35.dirname(currentDir) && path35.resolve(currentDir).startsWith(normalizedGitRoot) && depth < MAX_TRAVERSAL_DEPTH) {
|
|
22599
22347
|
dirs.push(currentDir);
|
|
22600
|
-
currentDir =
|
|
22348
|
+
currentDir = path35.dirname(currentDir);
|
|
22601
22349
|
depth++;
|
|
22602
22350
|
}
|
|
22603
22351
|
if (!dirs.includes(gitRoot)) {
|
|
@@ -22605,7 +22353,7 @@ function loadProjectMemory(cwd2) {
|
|
|
22605
22353
|
}
|
|
22606
22354
|
const processedPaths = /* @__PURE__ */ new Set();
|
|
22607
22355
|
for (const dir of dirs.reverse()) {
|
|
22608
|
-
const projectBlumaMd =
|
|
22356
|
+
const projectBlumaMd = path35.join(dir, "BLUMA.md");
|
|
22609
22357
|
if (!processedPaths.has(projectBlumaMd)) {
|
|
22610
22358
|
processedPaths.add(projectBlumaMd);
|
|
22611
22359
|
const projectFile = readMemoryFile(projectBlumaMd, "Project");
|
|
@@ -22613,7 +22361,7 @@ function loadProjectMemory(cwd2) {
|
|
|
22613
22361
|
files.push(projectFile);
|
|
22614
22362
|
}
|
|
22615
22363
|
}
|
|
22616
|
-
const blumaDirBlumaMd =
|
|
22364
|
+
const blumaDirBlumaMd = path35.join(dir, ".bluma", "BLUMA.md");
|
|
22617
22365
|
if (!processedPaths.has(blumaDirBlumaMd)) {
|
|
22618
22366
|
processedPaths.add(blumaDirBlumaMd);
|
|
22619
22367
|
const blumaDirFile = readMemoryFile(blumaDirBlumaMd, "Project");
|
|
@@ -22621,10 +22369,10 @@ function loadProjectMemory(cwd2) {
|
|
|
22621
22369
|
files.push(blumaDirFile);
|
|
22622
22370
|
}
|
|
22623
22371
|
}
|
|
22624
|
-
const rulesDir =
|
|
22372
|
+
const rulesDir = path35.join(dir, ".bluma", "rules");
|
|
22625
22373
|
files.push(...processRulesDirectory(rulesDir, "Project", processedPaths, false));
|
|
22626
22374
|
}
|
|
22627
|
-
const localBlumaMd =
|
|
22375
|
+
const localBlumaMd = path35.join(cwd2, "BLUMA.local.md");
|
|
22628
22376
|
if (!processedPaths.has(localBlumaMd)) {
|
|
22629
22377
|
processedPaths.add(localBlumaMd);
|
|
22630
22378
|
const localFile = readMemoryFile(localBlumaMd, "Local");
|
|
@@ -22710,10 +22458,10 @@ var LIMITS = {
|
|
|
22710
22458
|
};
|
|
22711
22459
|
function safeReadFile(filePath, maxChars) {
|
|
22712
22460
|
try {
|
|
22713
|
-
if (!
|
|
22714
|
-
const st =
|
|
22461
|
+
if (!fs33.existsSync(filePath)) return null;
|
|
22462
|
+
const st = fs33.statSync(filePath);
|
|
22715
22463
|
if (!st.isFile()) return null;
|
|
22716
|
-
const raw =
|
|
22464
|
+
const raw = fs33.readFileSync(filePath, "utf8");
|
|
22717
22465
|
if (raw.includes("\0")) return null;
|
|
22718
22466
|
if (raw.length <= maxChars) return raw;
|
|
22719
22467
|
return `${raw.slice(0, maxChars)}
|
|
@@ -22725,7 +22473,7 @@ function safeReadFile(filePath, maxChars) {
|
|
|
22725
22473
|
}
|
|
22726
22474
|
function tryReadReadme(cwd2) {
|
|
22727
22475
|
for (const name of ["README.md", "README.MD", "readme.md", "Readme.md"]) {
|
|
22728
|
-
const c = safeReadFile(
|
|
22476
|
+
const c = safeReadFile(path36.join(cwd2, name), LIMITS.readme);
|
|
22729
22477
|
if (c) return `(${name})
|
|
22730
22478
|
${c}`;
|
|
22731
22479
|
}
|
|
@@ -22733,14 +22481,14 @@ ${c}`;
|
|
|
22733
22481
|
}
|
|
22734
22482
|
function tryReadBluMaMd(cwd2) {
|
|
22735
22483
|
const paths2 = [
|
|
22736
|
-
|
|
22737
|
-
|
|
22738
|
-
|
|
22484
|
+
path36.join(cwd2, "BluMa.md"),
|
|
22485
|
+
path36.join(cwd2, "BLUMA.md"),
|
|
22486
|
+
path36.join(cwd2, ".bluma", "BluMa.md")
|
|
22739
22487
|
];
|
|
22740
22488
|
for (const p of paths2) {
|
|
22741
22489
|
const c = safeReadFile(p, LIMITS.blumaMd);
|
|
22742
22490
|
if (c) {
|
|
22743
|
-
const rel =
|
|
22491
|
+
const rel = path36.relative(cwd2, p) || p;
|
|
22744
22492
|
return `(${rel})
|
|
22745
22493
|
${c}`;
|
|
22746
22494
|
}
|
|
@@ -22748,10 +22496,10 @@ ${c}`;
|
|
|
22748
22496
|
return null;
|
|
22749
22497
|
}
|
|
22750
22498
|
function summarizePackageJson(cwd2) {
|
|
22751
|
-
const p =
|
|
22499
|
+
const p = path36.join(cwd2, "package.json");
|
|
22752
22500
|
try {
|
|
22753
|
-
if (!
|
|
22754
|
-
const pkg = JSON.parse(
|
|
22501
|
+
if (!fs33.existsSync(p)) return null;
|
|
22502
|
+
const pkg = JSON.parse(fs33.readFileSync(p, "utf8"));
|
|
22755
22503
|
const scripts = pkg.scripts;
|
|
22756
22504
|
let scriptKeys = "";
|
|
22757
22505
|
if (scripts && typeof scripts === "object" && !Array.isArray(scripts)) {
|
|
@@ -22784,7 +22532,7 @@ function summarizePackageJson(cwd2) {
|
|
|
22784
22532
|
}
|
|
22785
22533
|
function topLevelListing(cwd2) {
|
|
22786
22534
|
try {
|
|
22787
|
-
const names =
|
|
22535
|
+
const names = fs33.readdirSync(cwd2, { withFileTypes: true });
|
|
22788
22536
|
const sorted = [...names].sort((a, b) => a.name.localeCompare(b.name));
|
|
22789
22537
|
const limited = sorted.slice(0, LIMITS.topDirEntries);
|
|
22790
22538
|
const lines = limited.map((d) => `${d.name}${d.isDirectory() ? "/" : ""}`);
|
|
@@ -22842,7 +22590,7 @@ function buildWorkspaceSnapshot(cwd2) {
|
|
|
22842
22590
|
parts.push(pkg);
|
|
22843
22591
|
parts.push("```\n");
|
|
22844
22592
|
}
|
|
22845
|
-
const py = safeReadFile(
|
|
22593
|
+
const py = safeReadFile(path36.join(cwd2, "pyproject.toml"), LIMITS.pyproject);
|
|
22846
22594
|
if (py) {
|
|
22847
22595
|
parts.push("### pyproject.toml (excerpt)\n```toml");
|
|
22848
22596
|
parts.push(py);
|
|
@@ -22860,15 +22608,15 @@ function buildWorkspaceSnapshot(cwd2) {
|
|
|
22860
22608
|
parts.push(bluma);
|
|
22861
22609
|
parts.push("```\n");
|
|
22862
22610
|
}
|
|
22863
|
-
const contrib = safeReadFile(
|
|
22611
|
+
const contrib = safeReadFile(path36.join(cwd2, "CONTRIBUTING.md"), LIMITS.contributing);
|
|
22864
22612
|
if (contrib) {
|
|
22865
22613
|
parts.push("### CONTRIBUTING.md (excerpt)\n```markdown");
|
|
22866
22614
|
parts.push(contrib);
|
|
22867
22615
|
parts.push("```\n");
|
|
22868
22616
|
}
|
|
22869
|
-
const chlog = safeReadFile(
|
|
22617
|
+
const chlog = safeReadFile(path36.join(cwd2, "CHANGELOG.md"), LIMITS.changelog);
|
|
22870
22618
|
if (!chlog) {
|
|
22871
|
-
const alt = safeReadFile(
|
|
22619
|
+
const alt = safeReadFile(path36.join(cwd2, "CHANGES.md"), LIMITS.changelog);
|
|
22872
22620
|
if (alt) {
|
|
22873
22621
|
parts.push("### CHANGES.md (excerpt)\n```markdown");
|
|
22874
22622
|
parts.push(alt);
|
|
@@ -22904,14 +22652,14 @@ function buildWorkspaceSnapshot(cwd2) {
|
|
|
22904
22652
|
} else {
|
|
22905
22653
|
parts.push("### Git\n(not a git work tree, or `git` unavailable)\n");
|
|
22906
22654
|
}
|
|
22907
|
-
const tsconfig = safeReadFile(
|
|
22655
|
+
const tsconfig = safeReadFile(path36.join(cwd2, "tsconfig.json"), LIMITS.tsconfig);
|
|
22908
22656
|
if (tsconfig) {
|
|
22909
22657
|
parts.push("### tsconfig.json (excerpt)\n```json");
|
|
22910
22658
|
parts.push(tsconfig);
|
|
22911
22659
|
parts.push("```\n");
|
|
22912
22660
|
}
|
|
22913
22661
|
for (const name of ["Dockerfile", "dockerfile", "Dockerfile.prod", "Dockerfile.dev"]) {
|
|
22914
|
-
const df = safeReadFile(
|
|
22662
|
+
const df = safeReadFile(path36.join(cwd2, name), LIMITS.dockerfile);
|
|
22915
22663
|
if (df) {
|
|
22916
22664
|
parts.push(`### ${name} (excerpt)
|
|
22917
22665
|
`);
|
|
@@ -22921,7 +22669,7 @@ function buildWorkspaceSnapshot(cwd2) {
|
|
|
22921
22669
|
}
|
|
22922
22670
|
}
|
|
22923
22671
|
for (const name of ["docker-compose.yml", "docker-compose.yaml", "compose.yml", "compose.yaml"]) {
|
|
22924
|
-
const dc = safeReadFile(
|
|
22672
|
+
const dc = safeReadFile(path36.join(cwd2, name), LIMITS.dockerfile);
|
|
22925
22673
|
if (dc) {
|
|
22926
22674
|
parts.push(`### ${name} (excerpt)
|
|
22927
22675
|
`);
|
|
@@ -22936,12 +22684,12 @@ function buildWorkspaceSnapshot(cwd2) {
|
|
|
22936
22684
|
".circleci/config.yml",
|
|
22937
22685
|
"Jenkinsfile"
|
|
22938
22686
|
]) {
|
|
22939
|
-
const ciFile =
|
|
22940
|
-
if (
|
|
22941
|
-
const st =
|
|
22687
|
+
const ciFile = path36.join(cwd2, ciPath);
|
|
22688
|
+
if (fs33.existsSync(ciFile)) {
|
|
22689
|
+
const st = fs33.statSync(ciFile);
|
|
22942
22690
|
if (st.isDirectory()) {
|
|
22943
22691
|
try {
|
|
22944
|
-
const wfFiles =
|
|
22692
|
+
const wfFiles = fs33.readdirSync(ciFile).filter((f) => f.endsWith(".yml") || f.endsWith(".yaml"));
|
|
22945
22693
|
if (wfFiles.length > 0) {
|
|
22946
22694
|
parts.push(`### GitHub Actions workflows
|
|
22947
22695
|
\`${wfFiles.join("`, `")}\`
|
|
@@ -22952,7 +22700,7 @@ function buildWorkspaceSnapshot(cwd2) {
|
|
|
22952
22700
|
} else {
|
|
22953
22701
|
const ci = safeReadFile(ciFile, LIMITS.ciConfig);
|
|
22954
22702
|
if (ci) {
|
|
22955
|
-
parts.push(`### CI config (${
|
|
22703
|
+
parts.push(`### CI config (${path36.basename(ciPath)})
|
|
22956
22704
|
`);
|
|
22957
22705
|
parts.push(ci);
|
|
22958
22706
|
parts.push("\n");
|
|
@@ -22961,8 +22709,8 @@ function buildWorkspaceSnapshot(cwd2) {
|
|
|
22961
22709
|
}
|
|
22962
22710
|
}
|
|
22963
22711
|
for (const depFile of ["requirements.txt", "Pipfile", "poetry.lock", "Gemfile", "go.sum", "Cargo.lock"]) {
|
|
22964
|
-
const depPath =
|
|
22965
|
-
if (
|
|
22712
|
+
const depPath = path36.join(cwd2, depFile);
|
|
22713
|
+
if (fs33.existsSync(depPath)) {
|
|
22966
22714
|
const depContent = safeReadFile(depPath, 1500);
|
|
22967
22715
|
if (depContent) {
|
|
22968
22716
|
parts.push(`### ${depFile} (top entries)
|
|
@@ -22975,10 +22723,10 @@ function buildWorkspaceSnapshot(cwd2) {
|
|
|
22975
22723
|
}
|
|
22976
22724
|
}
|
|
22977
22725
|
for (const envFile of [".env.example", ".env.sample", ".env.template"]) {
|
|
22978
|
-
const envPath =
|
|
22979
|
-
if (
|
|
22726
|
+
const envPath = path36.join(cwd2, envFile);
|
|
22727
|
+
if (fs33.existsSync(envPath)) {
|
|
22980
22728
|
try {
|
|
22981
|
-
const raw =
|
|
22729
|
+
const raw = fs33.readFileSync(envPath, "utf-8");
|
|
22982
22730
|
const keys = raw.split("\n").map((l) => l.trim()).filter((l) => l && !l.startsWith("#")).map((l) => {
|
|
22983
22731
|
const eqIndex = l.indexOf("=");
|
|
22984
22732
|
return eqIndex >= 0 ? l.slice(0, eqIndex).trim() : l.trim();
|
|
@@ -23001,15 +22749,15 @@ init_runtime_config();
|
|
|
23001
22749
|
|
|
23002
22750
|
// src/app/agent/runtime/plugin_registry.ts
|
|
23003
22751
|
init_sandbox_policy();
|
|
23004
|
-
import
|
|
22752
|
+
import fs34 from "fs";
|
|
23005
22753
|
import os22 from "os";
|
|
23006
|
-
import
|
|
22754
|
+
import path37 from "path";
|
|
23007
22755
|
function getProjectPluginsDir() {
|
|
23008
22756
|
const policy = getSandboxPolicy();
|
|
23009
|
-
return
|
|
22757
|
+
return path37.join(policy.workspaceRoot, ".bluma", "plugins");
|
|
23010
22758
|
}
|
|
23011
22759
|
function getGlobalPluginsDir() {
|
|
23012
|
-
return
|
|
22760
|
+
return path37.join(process.env.HOME || os22.homedir(), ".bluma", "plugins");
|
|
23013
22761
|
}
|
|
23014
22762
|
function getPluginDirs() {
|
|
23015
22763
|
return {
|
|
@@ -23018,11 +22766,11 @@ function getPluginDirs() {
|
|
|
23018
22766
|
};
|
|
23019
22767
|
}
|
|
23020
22768
|
function readManifest(manifestPath, fallbackName) {
|
|
23021
|
-
if (!
|
|
22769
|
+
if (!fs34.existsSync(manifestPath)) {
|
|
23022
22770
|
return null;
|
|
23023
22771
|
}
|
|
23024
22772
|
try {
|
|
23025
|
-
const parsed = JSON.parse(
|
|
22773
|
+
const parsed = JSON.parse(fs34.readFileSync(manifestPath, "utf-8"));
|
|
23026
22774
|
return {
|
|
23027
22775
|
name: typeof parsed.name === "string" && parsed.name.trim() ? parsed.name.trim() : fallbackName,
|
|
23028
22776
|
description: typeof parsed.description === "string" ? parsed.description.trim() : void 0,
|
|
@@ -23035,22 +22783,22 @@ function readManifest(manifestPath, fallbackName) {
|
|
|
23035
22783
|
}
|
|
23036
22784
|
function findManifestPath(pluginDir) {
|
|
23037
22785
|
const candidates = [
|
|
23038
|
-
|
|
23039
|
-
|
|
22786
|
+
path37.join(pluginDir, ".codex-plugin", "plugin.json"),
|
|
22787
|
+
path37.join(pluginDir, "plugin.json")
|
|
23040
22788
|
];
|
|
23041
22789
|
for (const candidate of candidates) {
|
|
23042
|
-
if (
|
|
22790
|
+
if (fs34.existsSync(candidate)) {
|
|
23043
22791
|
return candidate;
|
|
23044
22792
|
}
|
|
23045
22793
|
}
|
|
23046
22794
|
return null;
|
|
23047
22795
|
}
|
|
23048
22796
|
function listFromDir(baseDir, source) {
|
|
23049
|
-
if (!
|
|
22797
|
+
if (!fs34.existsSync(baseDir)) {
|
|
23050
22798
|
return [];
|
|
23051
22799
|
}
|
|
23052
|
-
return
|
|
23053
|
-
const pluginDir =
|
|
22800
|
+
return fs34.readdirSync(baseDir, { withFileTypes: true }).filter((entry) => entry.isDirectory()).flatMap((entry) => {
|
|
22801
|
+
const pluginDir = path37.join(baseDir, entry.name);
|
|
23054
22802
|
const manifestPath = findManifestPath(pluginDir);
|
|
23055
22803
|
if (!manifestPath) {
|
|
23056
22804
|
return [];
|
|
@@ -23495,8 +23243,8 @@ function buildModelInfoSection(modelId) {
|
|
|
23495
23243
|
|
|
23496
23244
|
// src/app/agent/runtime/hook_registry.ts
|
|
23497
23245
|
init_sandbox_policy();
|
|
23498
|
-
import
|
|
23499
|
-
import
|
|
23246
|
+
import fs35 from "fs";
|
|
23247
|
+
import path38 from "path";
|
|
23500
23248
|
var DEFAULT_STATE = {
|
|
23501
23249
|
enabled: true,
|
|
23502
23250
|
maxEvents: 120,
|
|
@@ -23507,7 +23255,7 @@ var cache3 = null;
|
|
|
23507
23255
|
var cachePath2 = null;
|
|
23508
23256
|
function getStatePath() {
|
|
23509
23257
|
const policy = getSandboxPolicy();
|
|
23510
|
-
return
|
|
23258
|
+
return path38.join(policy.workspaceRoot, ".bluma", "hooks.json");
|
|
23511
23259
|
}
|
|
23512
23260
|
function getHookStatePath() {
|
|
23513
23261
|
return getStatePath();
|
|
@@ -23526,8 +23274,8 @@ function ensureLoaded2() {
|
|
|
23526
23274
|
return cache3;
|
|
23527
23275
|
}
|
|
23528
23276
|
try {
|
|
23529
|
-
if (
|
|
23530
|
-
const parsed = JSON.parse(
|
|
23277
|
+
if (fs35.existsSync(statePath)) {
|
|
23278
|
+
const parsed = JSON.parse(fs35.readFileSync(statePath, "utf-8"));
|
|
23531
23279
|
cache3 = {
|
|
23532
23280
|
enabled: typeof parsed.enabled === "boolean" ? parsed.enabled : DEFAULT_STATE.enabled,
|
|
23533
23281
|
maxEvents: typeof parsed.maxEvents === "number" && Number.isFinite(parsed.maxEvents) && parsed.maxEvents > 0 ? Math.floor(parsed.maxEvents) : DEFAULT_STATE.maxEvents,
|
|
@@ -23553,9 +23301,9 @@ function ensureLoaded2() {
|
|
|
23553
23301
|
}
|
|
23554
23302
|
function persist2(state2) {
|
|
23555
23303
|
const statePath = getStatePath();
|
|
23556
|
-
|
|
23304
|
+
fs35.mkdirSync(path38.dirname(statePath), { recursive: true });
|
|
23557
23305
|
state2.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
23558
|
-
|
|
23306
|
+
fs35.writeFileSync(statePath, JSON.stringify(state2, null, 2), "utf-8");
|
|
23559
23307
|
cache3 = state2;
|
|
23560
23308
|
cachePath2 = statePath;
|
|
23561
23309
|
}
|
|
@@ -23820,12 +23568,12 @@ init_session_memory_paths();
|
|
|
23820
23568
|
import { readFile as readFile3, writeFile as writeFile2 } from "fs/promises";
|
|
23821
23569
|
async function loadSessionMemoryForPrompt(sessionId, cwd2 = process.cwd()) {
|
|
23822
23570
|
if (!sessionId?.trim()) return "";
|
|
23823
|
-
const
|
|
23571
|
+
const path58 = getSessionMemoryPath(sessionId, cwd2);
|
|
23824
23572
|
try {
|
|
23825
|
-
const content = await readFile3(
|
|
23573
|
+
const content = await readFile3(path58, "utf-8");
|
|
23826
23574
|
if (!content.trim()) return "";
|
|
23827
23575
|
return `<session_memory>
|
|
23828
|
-
Notes for this conversation (${
|
|
23576
|
+
Notes for this conversation (${path58}):
|
|
23829
23577
|
|
|
23830
23578
|
${content.trim()}
|
|
23831
23579
|
</session_memory>`;
|
|
@@ -23835,11 +23583,11 @@ ${content.trim()}
|
|
|
23835
23583
|
}
|
|
23836
23584
|
async function initSessionMemoryFile(sessionId, cwd2 = process.cwd()) {
|
|
23837
23585
|
await ensureSessionMemoryDir(sessionId, cwd2);
|
|
23838
|
-
const
|
|
23586
|
+
const path58 = getSessionMemoryPath(sessionId, cwd2);
|
|
23839
23587
|
try {
|
|
23840
|
-
await readFile3(
|
|
23588
|
+
await readFile3(path58, "utf-8");
|
|
23841
23589
|
} catch {
|
|
23842
|
-
await writeFile2(
|
|
23590
|
+
await writeFile2(path58, `${DEFAULT_SESSION_MEMORY_TEMPLATE}
|
|
23843
23591
|
`, "utf-8");
|
|
23844
23592
|
}
|
|
23845
23593
|
}
|
|
@@ -23878,8 +23626,8 @@ The orchestrator configures these \u2014 **use the values already set**. Do not
|
|
|
23878
23626
|
|
|
23879
23627
|
## Flow A \u2014 New app (no \`factorai.sh.json\` yet)
|
|
23880
23628
|
|
|
23881
|
-
1. \`factorai.sh.create_next_app({ name
|
|
23882
|
-
2. Customize with \`edit_tool\` / \`file_write\` / \`shell_command\`
|
|
23629
|
+
1. \`factorai.sh.create_next_app({ name })\` \u2014 scaffold completo (App Router + \`components/ui/*\` factorai.sh). **L\xEA o campo \`agentGuide\` do resultado** (ou \`DESIGN.md\` na raiz) antes de editar UI. A home j\xE1 \xE9 uma landing escura polida (hero + cards); personaliza \`app/page.tsx\` para o produto (ver Refero Styles para refer\xEAncia visual).
|
|
23630
|
+
2. Customize with \`edit_tool\` / \`file_write\` / \`shell_command\` usando \`@/components/ui/*\` e classes sem\xE2nticas (\`bg-background\`, \`text-muted-foreground\`, etc.).
|
|
23883
23631
|
3. Optional: \`npx tsc --noEmit\` to catch TS errors \u2014 **avoid** \`npm run build\` before deploy (creates \`.next/\` and inflates the ZIP).
|
|
23884
23632
|
4. \`factorai.sh.deploy_app({ projectDir: "./{name}", name })\` \u2014 tool excludes \`node_modules\`, \`.next\`, \`.env*\` automatically. **Never** manual \`zip\` including \`node_modules\` or \`.next\`.
|
|
23885
23633
|
5. Persist returned IDs into \`factorai.sh.json\` (tool does this when possible). Report live URL in \`message(result)\` via \`factor-sh-url-app\` (from manifest \`appUrl\`, absolute URL \u2014 prefix with \`SEVERINO_URL\` only when manifest has a path).
|
|
@@ -23972,17 +23720,17 @@ function getGitBranch(dir) {
|
|
|
23972
23720
|
}
|
|
23973
23721
|
}
|
|
23974
23722
|
function getPackageManager(dir) {
|
|
23975
|
-
if (
|
|
23976
|
-
if (
|
|
23977
|
-
if (
|
|
23978
|
-
if (
|
|
23723
|
+
if (fs36.existsSync(path39.join(dir, "pnpm-lock.yaml"))) return "pnpm";
|
|
23724
|
+
if (fs36.existsSync(path39.join(dir, "yarn.lock"))) return "yarn";
|
|
23725
|
+
if (fs36.existsSync(path39.join(dir, "bun.lockb"))) return "bun";
|
|
23726
|
+
if (fs36.existsSync(path39.join(dir, "package-lock.json"))) return "npm";
|
|
23979
23727
|
return "unknown";
|
|
23980
23728
|
}
|
|
23981
23729
|
function getProjectType(dir) {
|
|
23982
23730
|
try {
|
|
23983
|
-
const files =
|
|
23731
|
+
const files = fs36.readdirSync(dir);
|
|
23984
23732
|
if (files.includes("package.json")) {
|
|
23985
|
-
const pkg = JSON.parse(
|
|
23733
|
+
const pkg = JSON.parse(fs36.readFileSync(path39.join(dir, "package.json"), "utf-8"));
|
|
23986
23734
|
const deps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
23987
23735
|
if (deps.next) return "Next.js";
|
|
23988
23736
|
if (deps.react) return "React";
|
|
@@ -24002,9 +23750,9 @@ function getProjectType(dir) {
|
|
|
24002
23750
|
}
|
|
24003
23751
|
function getTestFramework(dir) {
|
|
24004
23752
|
try {
|
|
24005
|
-
const pkgPath =
|
|
24006
|
-
if (
|
|
24007
|
-
const pkg = JSON.parse(
|
|
23753
|
+
const pkgPath = path39.join(dir, "package.json");
|
|
23754
|
+
if (fs36.existsSync(pkgPath)) {
|
|
23755
|
+
const pkg = JSON.parse(fs36.readFileSync(pkgPath, "utf-8"));
|
|
24008
23756
|
const deps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
24009
23757
|
if (deps.jest) return "jest";
|
|
24010
23758
|
if (deps.vitest) return "vitest";
|
|
@@ -24013,7 +23761,7 @@ function getTestFramework(dir) {
|
|
|
24013
23761
|
if (deps["@playwright/test"]) return "playwright";
|
|
24014
23762
|
if (deps.cypress) return "cypress";
|
|
24015
23763
|
}
|
|
24016
|
-
if (
|
|
23764
|
+
if (fs36.existsSync(path39.join(dir, "pytest.ini")) || fs36.existsSync(path39.join(dir, "conftest.py"))) return "pytest";
|
|
24017
23765
|
return "unknown";
|
|
24018
23766
|
} catch {
|
|
24019
23767
|
return "unknown";
|
|
@@ -24021,9 +23769,9 @@ function getTestFramework(dir) {
|
|
|
24021
23769
|
}
|
|
24022
23770
|
function getTestCommand(dir) {
|
|
24023
23771
|
try {
|
|
24024
|
-
const pkgPath =
|
|
24025
|
-
if (
|
|
24026
|
-
const pkg = JSON.parse(
|
|
23772
|
+
const pkgPath = path39.join(dir, "package.json");
|
|
23773
|
+
if (fs36.existsSync(pkgPath)) {
|
|
23774
|
+
const pkg = JSON.parse(fs36.readFileSync(pkgPath, "utf-8"));
|
|
24027
23775
|
if (pkg.scripts?.test) return "npm test";
|
|
24028
23776
|
if (pkg.scripts?.["test:unit"]) return "npm run test:unit";
|
|
24029
23777
|
}
|
|
@@ -24038,8 +23786,8 @@ function getTestCommand(dir) {
|
|
|
24038
23786
|
}
|
|
24039
23787
|
function isGitRepo(dir) {
|
|
24040
23788
|
try {
|
|
24041
|
-
const p =
|
|
24042
|
-
return
|
|
23789
|
+
const p = path39.join(dir, ".git");
|
|
23790
|
+
return fs36.existsSync(p) && fs36.lstatSync(p).isDirectory();
|
|
24043
23791
|
} catch {
|
|
24044
23792
|
return false;
|
|
24045
23793
|
}
|
|
@@ -25472,10 +25220,10 @@ var LLMService = class {
|
|
|
25472
25220
|
};
|
|
25473
25221
|
|
|
25474
25222
|
// src/app/agent/utils/user_message_images.ts
|
|
25475
|
-
import
|
|
25223
|
+
import fs37 from "fs";
|
|
25476
25224
|
import os25 from "os";
|
|
25477
|
-
import
|
|
25478
|
-
import { fileURLToPath as
|
|
25225
|
+
import path40 from "path";
|
|
25226
|
+
import { fileURLToPath as fileURLToPath5 } from "url";
|
|
25479
25227
|
var IMAGE_EXT = /\.(png|jpe?g|gif|webp|bmp)$/i;
|
|
25480
25228
|
var MAX_IMAGE_BYTES = 4 * 1024 * 1024;
|
|
25481
25229
|
var MAX_IMAGES = 6;
|
|
@@ -25490,22 +25238,22 @@ var MIME = {
|
|
|
25490
25238
|
function expandUserPath(p) {
|
|
25491
25239
|
const t = p.trim();
|
|
25492
25240
|
if (t.startsWith("~")) {
|
|
25493
|
-
return
|
|
25241
|
+
return path40.join(os25.homedir(), t.slice(1).replace(/^\//, ""));
|
|
25494
25242
|
}
|
|
25495
25243
|
return t;
|
|
25496
25244
|
}
|
|
25497
25245
|
function isPathAllowed(absResolved, cwd2) {
|
|
25498
|
-
const resolved =
|
|
25499
|
-
const cwdR =
|
|
25500
|
-
const homeR =
|
|
25501
|
-
const tmpR =
|
|
25502
|
-
const underCwd = resolved === cwdR || resolved.startsWith(cwdR +
|
|
25503
|
-
const underHome = resolved === homeR || resolved.startsWith(homeR +
|
|
25504
|
-
const underTmp = resolved === tmpR || resolved.startsWith(tmpR +
|
|
25246
|
+
const resolved = path40.normalize(path40.resolve(absResolved));
|
|
25247
|
+
const cwdR = path40.normalize(path40.resolve(cwd2));
|
|
25248
|
+
const homeR = path40.normalize(path40.resolve(os25.homedir()));
|
|
25249
|
+
const tmpR = path40.normalize(path40.resolve(os25.tmpdir()));
|
|
25250
|
+
const underCwd = resolved === cwdR || resolved.startsWith(cwdR + path40.sep);
|
|
25251
|
+
const underHome = resolved === homeR || resolved.startsWith(homeR + path40.sep);
|
|
25252
|
+
const underTmp = resolved === tmpR || resolved.startsWith(tmpR + path40.sep);
|
|
25505
25253
|
return underCwd || underHome || underTmp;
|
|
25506
25254
|
}
|
|
25507
25255
|
function mimeFor(abs) {
|
|
25508
|
-
const ext =
|
|
25256
|
+
const ext = path40.extname(abs).toLowerCase();
|
|
25509
25257
|
return MIME[ext] || "application/octet-stream";
|
|
25510
25258
|
}
|
|
25511
25259
|
var IMAGE_EXT_SRC = String.raw`(?:png|jpe?g|gif|webp|bmp)`;
|
|
@@ -25549,10 +25297,10 @@ function collectImagePathStrings(raw) {
|
|
|
25549
25297
|
}
|
|
25550
25298
|
function resolveImagePath(candidate, cwd2) {
|
|
25551
25299
|
const expanded = expandUserPath(candidate);
|
|
25552
|
-
const abs =
|
|
25300
|
+
const abs = path40.isAbsolute(expanded) ? path40.normalize(expanded) : path40.normalize(path40.resolve(cwd2, expanded));
|
|
25553
25301
|
if (!isPathAllowed(abs, cwd2)) return null;
|
|
25554
25302
|
try {
|
|
25555
|
-
if (!
|
|
25303
|
+
if (!fs37.existsSync(abs) || !fs37.statSync(abs).isFile()) return null;
|
|
25556
25304
|
} catch {
|
|
25557
25305
|
return null;
|
|
25558
25306
|
}
|
|
@@ -25564,7 +25312,7 @@ function trySingleLineFileUriOrBareImagePath(line, cwd2) {
|
|
|
25564
25312
|
if (!s) return null;
|
|
25565
25313
|
if (s.startsWith("file:")) {
|
|
25566
25314
|
try {
|
|
25567
|
-
s =
|
|
25315
|
+
s = fileURLToPath5(s);
|
|
25568
25316
|
} catch {
|
|
25569
25317
|
return null;
|
|
25570
25318
|
}
|
|
@@ -25572,11 +25320,11 @@ function trySingleLineFileUriOrBareImagePath(line, cwd2) {
|
|
|
25572
25320
|
if (s.startsWith('"') && s.endsWith('"') || s.startsWith("'") && s.endsWith("'")) {
|
|
25573
25321
|
s = s.slice(1, -1).trim();
|
|
25574
25322
|
}
|
|
25575
|
-
if (!IMAGE_EXT.test(
|
|
25323
|
+
if (!IMAGE_EXT.test(path40.extname(s))) return null;
|
|
25576
25324
|
const abs = resolveImagePath(s, cwd2);
|
|
25577
25325
|
if (!abs) return null;
|
|
25578
25326
|
try {
|
|
25579
|
-
const st =
|
|
25327
|
+
const st = fs37.statSync(abs);
|
|
25580
25328
|
if (!st.isFile() || st.size > MAX_IMAGE_BYTES) {
|
|
25581
25329
|
return null;
|
|
25582
25330
|
}
|
|
@@ -25611,7 +25359,7 @@ function tryPasteChunkAsSingleImagePath(chunk, cwd2) {
|
|
|
25611
25359
|
return null;
|
|
25612
25360
|
}
|
|
25613
25361
|
try {
|
|
25614
|
-
const st =
|
|
25362
|
+
const st = fs37.statSync(abs);
|
|
25615
25363
|
if (!st.isFile() || st.size > MAX_IMAGE_BYTES) {
|
|
25616
25364
|
return null;
|
|
25617
25365
|
}
|
|
@@ -25640,7 +25388,7 @@ function buildUserMessageContent(raw, cwd2) {
|
|
|
25640
25388
|
const abs = resolveImagePath(c, cwd2);
|
|
25641
25389
|
if (!abs) continue;
|
|
25642
25390
|
try {
|
|
25643
|
-
const st =
|
|
25391
|
+
const st = fs37.statSync(abs);
|
|
25644
25392
|
if (st.size > MAX_IMAGE_BYTES) continue;
|
|
25645
25393
|
} catch {
|
|
25646
25394
|
continue;
|
|
@@ -25657,7 +25405,7 @@ function buildUserMessageContent(raw, cwd2) {
|
|
|
25657
25405
|
}
|
|
25658
25406
|
const parts = [{ type: "text", text: textPart }];
|
|
25659
25407
|
for (const abs of resolvedAbs) {
|
|
25660
|
-
const buf =
|
|
25408
|
+
const buf = fs37.readFileSync(abs);
|
|
25661
25409
|
const b64 = buf.toString("base64");
|
|
25662
25410
|
const mime = mimeFor(abs);
|
|
25663
25411
|
parts.push({
|
|
@@ -25675,7 +25423,7 @@ function buildUserMessageContent(raw, cwd2) {
|
|
|
25675
25423
|
init_sandbox_policy();
|
|
25676
25424
|
init_runtime_config();
|
|
25677
25425
|
init_permission_rules();
|
|
25678
|
-
import
|
|
25426
|
+
import path41 from "path";
|
|
25679
25427
|
var LOCAL_EDIT_TOOL_NAMES = /* @__PURE__ */ new Set(["edit_tool", "file_write", "notebook_edit"]);
|
|
25680
25428
|
function getToolPermissionLayer(metadata) {
|
|
25681
25429
|
if (metadata.riskLevel === "safe") return "read";
|
|
@@ -25690,11 +25438,11 @@ function checkFilePermissionRules(toolName, filePath, policy) {
|
|
|
25690
25438
|
if (!filePath) {
|
|
25691
25439
|
return { allowed: false, reason: "No file path provided for permission check." };
|
|
25692
25440
|
}
|
|
25693
|
-
const resolvedPath =
|
|
25441
|
+
const resolvedPath = path41.resolve(filePath);
|
|
25694
25442
|
if (!isPathInsideWorkspace(resolvedPath, policy)) {
|
|
25695
25443
|
return { allowed: false, reason: `File path "${filePath}" is outside workspace root.` };
|
|
25696
25444
|
}
|
|
25697
|
-
const relativePath =
|
|
25445
|
+
const relativePath = path41.relative(policy.workspaceRoot, resolvedPath);
|
|
25698
25446
|
const toolPattern = `${toolName}(${relativePath})`;
|
|
25699
25447
|
const ruleDecision = permissionRulesEngine.checkPermission(toolPattern, { filepath: filePath });
|
|
25700
25448
|
if (ruleDecision === "deny") {
|
|
@@ -25703,7 +25451,7 @@ function checkFilePermissionRules(toolName, filePath, policy) {
|
|
|
25703
25451
|
if (ruleDecision === "allow") {
|
|
25704
25452
|
return { allowed: true, reason: `File "${filePath}" allowed by permission rules.` };
|
|
25705
25453
|
}
|
|
25706
|
-
const dirPath =
|
|
25454
|
+
const dirPath = path41.dirname(relativePath);
|
|
25707
25455
|
const dirPattern = `${toolName}(${dirPath}/**)`;
|
|
25708
25456
|
const dirRuleDecision = permissionRulesEngine.checkPermission(dirPattern, { filepath: filePath });
|
|
25709
25457
|
if (dirRuleDecision === "allow") {
|
|
@@ -25920,11 +25668,11 @@ function effectiveToolAutoApprove(toolCall, sessionId, options) {
|
|
|
25920
25668
|
}
|
|
25921
25669
|
|
|
25922
25670
|
// src/app/agent/tools/CodingMemoryTool/CodingMemoryConsolidate.ts
|
|
25923
|
-
import * as
|
|
25924
|
-
import * as
|
|
25671
|
+
import * as fs38 from "fs";
|
|
25672
|
+
import * as path42 from "path";
|
|
25925
25673
|
import os26 from "os";
|
|
25926
25674
|
function memoryPath2() {
|
|
25927
|
-
return
|
|
25675
|
+
return path42.join(process.env.HOME || os26.homedir(), ".bluma", "coding_memory.json");
|
|
25928
25676
|
}
|
|
25929
25677
|
function normalizeNote2(note) {
|
|
25930
25678
|
return note.trim().toLowerCase().replace(/\s+/g, " ");
|
|
@@ -25934,18 +25682,18 @@ function uniqTags(a, b) {
|
|
|
25934
25682
|
}
|
|
25935
25683
|
function consolidateCodingMemoryFile() {
|
|
25936
25684
|
const p = memoryPath2();
|
|
25937
|
-
if (!
|
|
25685
|
+
if (!fs38.existsSync(p)) {
|
|
25938
25686
|
return { success: true, removedDuplicates: 0, message: "no coding_memory.json" };
|
|
25939
25687
|
}
|
|
25940
25688
|
const bak = `${p}.bak`;
|
|
25941
25689
|
try {
|
|
25942
|
-
|
|
25690
|
+
fs38.copyFileSync(p, bak);
|
|
25943
25691
|
} catch (e) {
|
|
25944
25692
|
return { success: false, removedDuplicates: 0, message: `backup failed: ${e.message}` };
|
|
25945
25693
|
}
|
|
25946
25694
|
let data;
|
|
25947
25695
|
try {
|
|
25948
|
-
data = JSON.parse(
|
|
25696
|
+
data = JSON.parse(fs38.readFileSync(p, "utf-8"));
|
|
25949
25697
|
} catch (e) {
|
|
25950
25698
|
return { success: false, removedDuplicates: 0, message: `invalid json: ${e.message}` };
|
|
25951
25699
|
}
|
|
@@ -25980,7 +25728,7 @@ function consolidateCodingMemoryFile() {
|
|
|
25980
25728
|
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
25981
25729
|
};
|
|
25982
25730
|
try {
|
|
25983
|
-
|
|
25731
|
+
fs38.writeFileSync(p, JSON.stringify(out, null, 2), "utf-8");
|
|
25984
25732
|
} catch (e) {
|
|
25985
25733
|
return { success: false, removedDuplicates: 0, message: `write failed: ${e.message}` };
|
|
25986
25734
|
}
|
|
@@ -26486,18 +26234,18 @@ var BluMaToolRunner = class {
|
|
|
26486
26234
|
|
|
26487
26235
|
// src/app/agent/session_manager/session_archive.ts
|
|
26488
26236
|
init_bluma_app_dir();
|
|
26489
|
-
import
|
|
26490
|
-
import { promises as
|
|
26237
|
+
import path43 from "path";
|
|
26238
|
+
import { promises as fs39 } from "fs";
|
|
26491
26239
|
async function archivePrunedConversationMessages(sessionId, messages) {
|
|
26492
26240
|
if (!sessionId || messages.length === 0) {
|
|
26493
26241
|
return null;
|
|
26494
26242
|
}
|
|
26495
26243
|
const appDir = getPreferredAppDir();
|
|
26496
|
-
const dir =
|
|
26497
|
-
await
|
|
26498
|
-
const archiveFile =
|
|
26244
|
+
const dir = path43.join(appDir, "sessions", "archive", sessionId);
|
|
26245
|
+
await fs39.mkdir(dir, { recursive: true });
|
|
26246
|
+
const archiveFile = path43.join(dir, `${Date.now()}.jsonl`);
|
|
26499
26247
|
const lines = messages.map((m) => JSON.stringify(m)).join("\n") + "\n";
|
|
26500
|
-
await
|
|
26248
|
+
await fs39.appendFile(archiveFile, lines, "utf-8");
|
|
26501
26249
|
return archiveFile;
|
|
26502
26250
|
}
|
|
26503
26251
|
|
|
@@ -27358,7 +27106,7 @@ Update existing files instead of duplicating.` : "";
|
|
|
27358
27106
|
|
|
27359
27107
|
// src/app/agent/memory/memory_tool_policy.ts
|
|
27360
27108
|
init_paths();
|
|
27361
|
-
import
|
|
27109
|
+
import path44 from "path";
|
|
27362
27110
|
var MEMORY_READ_TOOLS = /* @__PURE__ */ new Set([
|
|
27363
27111
|
"read_file_lines",
|
|
27364
27112
|
"grep_search",
|
|
@@ -27385,7 +27133,7 @@ function isReadOnlyShellCommand(command) {
|
|
|
27385
27133
|
return READ_ONLY_SHELL.test(trimmed);
|
|
27386
27134
|
}
|
|
27387
27135
|
function createAutoMemToolGate(memoryDir) {
|
|
27388
|
-
const memRoot = memoryDir.endsWith(
|
|
27136
|
+
const memRoot = memoryDir.endsWith(path44.sep) ? memoryDir : memoryDir + path44.sep;
|
|
27389
27137
|
return (toolName, args) => {
|
|
27390
27138
|
if (MEMORY_READ_TOOLS.has(toolName)) {
|
|
27391
27139
|
return { allowed: true };
|
|
@@ -27398,7 +27146,7 @@ function createAutoMemToolGate(memoryDir) {
|
|
|
27398
27146
|
}
|
|
27399
27147
|
if (MEMORY_WRITE_TOOLS.has(toolName)) {
|
|
27400
27148
|
const fp = extractFilePath(args);
|
|
27401
|
-
if (fp && isAutoMemPath(
|
|
27149
|
+
if (fp && isAutoMemPath(path44.resolve(fp))) {
|
|
27402
27150
|
return { allowed: true };
|
|
27403
27151
|
}
|
|
27404
27152
|
return { allowed: false, reason: `Writes must stay under ${memRoot}` };
|
|
@@ -27407,18 +27155,18 @@ function createAutoMemToolGate(memoryDir) {
|
|
|
27407
27155
|
};
|
|
27408
27156
|
}
|
|
27409
27157
|
function createSessionMemoryToolGate(sessionMemoryPath) {
|
|
27410
|
-
const resolvedTarget =
|
|
27158
|
+
const resolvedTarget = path44.resolve(sessionMemoryPath);
|
|
27411
27159
|
return (toolName, args) => {
|
|
27412
27160
|
if (toolName === "read_file_lines") {
|
|
27413
27161
|
const fp = extractFilePath(args);
|
|
27414
|
-
if (fp &&
|
|
27162
|
+
if (fp && path44.resolve(fp) === resolvedTarget) {
|
|
27415
27163
|
return { allowed: true };
|
|
27416
27164
|
}
|
|
27417
27165
|
return { allowed: false, reason: "Session memory subagent may only read the session summary file" };
|
|
27418
27166
|
}
|
|
27419
27167
|
if (toolName === "edit_tool") {
|
|
27420
27168
|
const fp = extractFilePath(args);
|
|
27421
|
-
if (fp &&
|
|
27169
|
+
if (fp && path44.resolve(fp) === resolvedTarget) {
|
|
27422
27170
|
return { allowed: true };
|
|
27423
27171
|
}
|
|
27424
27172
|
return { allowed: false, reason: "Session memory subagent may only edit the session summary file" };
|
|
@@ -27445,7 +27193,7 @@ function hasAutoMemWritesSinceHistory(history, sinceIndex) {
|
|
|
27445
27193
|
continue;
|
|
27446
27194
|
}
|
|
27447
27195
|
const fp = extractFilePath(args);
|
|
27448
|
-
if (fp && isAutoMemPath(
|
|
27196
|
+
if (fp && isAutoMemPath(path44.resolve(fp))) {
|
|
27449
27197
|
return true;
|
|
27450
27198
|
}
|
|
27451
27199
|
}
|
|
@@ -27913,13 +27661,13 @@ var BluMaAgent = class {
|
|
|
27913
27661
|
if (!this.sessionFile) return;
|
|
27914
27662
|
try {
|
|
27915
27663
|
const sessionData = {
|
|
27916
|
-
session_id:
|
|
27664
|
+
session_id: path45.basename(this.sessionFile, ".json"),
|
|
27917
27665
|
created_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
27918
27666
|
conversation_history: this.history,
|
|
27919
27667
|
last_updated: (/* @__PURE__ */ new Date()).toISOString(),
|
|
27920
27668
|
...this.compressor.getSnapshot()
|
|
27921
27669
|
};
|
|
27922
|
-
|
|
27670
|
+
fs40.writeFileSync(this.sessionFile, JSON.stringify(sessionData, null, 2), "utf-8");
|
|
27923
27671
|
} catch (error) {
|
|
27924
27672
|
console.error("[Bluma] Failed to persist session synchronously:", error);
|
|
27925
27673
|
}
|
|
@@ -28125,7 +27873,7 @@ var BluMaAgent = class {
|
|
|
28125
27873
|
|
|
28126
27874
|
${editData.error.display}`;
|
|
28127
27875
|
}
|
|
28128
|
-
const filename =
|
|
27876
|
+
const filename = path45.basename(toolArgs.file_path);
|
|
28129
27877
|
return createDiff(filename, editData.currentContent || "", editData.newContent);
|
|
28130
27878
|
} catch (e) {
|
|
28131
27879
|
return `An unexpected error occurred while generating the edit preview: ${e.message}`;
|
|
@@ -28859,16 +28607,16 @@ async function fullCompact(messages, targetTokens, summarizer, llmClient) {
|
|
|
28859
28607
|
}
|
|
28860
28608
|
|
|
28861
28609
|
// src/app/agent/core/memory/session_memory.ts
|
|
28862
|
-
import
|
|
28610
|
+
import fs41 from "fs";
|
|
28863
28611
|
import os29 from "os";
|
|
28864
|
-
import
|
|
28612
|
+
import path46 from "path";
|
|
28865
28613
|
import { v4 as uuidv49 } from "uuid";
|
|
28866
28614
|
var SessionMemoryExtractor = class {
|
|
28867
28615
|
llmClient;
|
|
28868
28616
|
memoryFile;
|
|
28869
28617
|
constructor(options = {}) {
|
|
28870
28618
|
this.llmClient = options.llmClient;
|
|
28871
|
-
this.memoryFile = options.memoryFile ||
|
|
28619
|
+
this.memoryFile = options.memoryFile || path46.join(os29.homedir(), ".bluma", "session_memory.json");
|
|
28872
28620
|
}
|
|
28873
28621
|
/**
|
|
28874
28622
|
* Extract memories from conversation using LLM
|
|
@@ -28925,15 +28673,15 @@ ${messages.slice(-50).map((m) => `${m.role}: ${m.content.slice(0, 500)}`).join("
|
|
|
28925
28673
|
);
|
|
28926
28674
|
unique.sort((a, b) => b.accessCount - a.accessCount);
|
|
28927
28675
|
const trimmed = unique.slice(0, 200);
|
|
28928
|
-
|
|
28676
|
+
fs41.writeFileSync(this.memoryFile, JSON.stringify(trimmed, null, 2));
|
|
28929
28677
|
}
|
|
28930
28678
|
/**
|
|
28931
28679
|
* Load memories from disk
|
|
28932
28680
|
*/
|
|
28933
28681
|
async loadMemories() {
|
|
28934
28682
|
try {
|
|
28935
|
-
if (!
|
|
28936
|
-
const data =
|
|
28683
|
+
if (!fs41.existsSync(this.memoryFile)) return [];
|
|
28684
|
+
const data = fs41.readFileSync(this.memoryFile, "utf-8");
|
|
28937
28685
|
return JSON.parse(data);
|
|
28938
28686
|
} catch {
|
|
28939
28687
|
return [];
|
|
@@ -29477,14 +29225,14 @@ var RouteManager = class {
|
|
|
29477
29225
|
this.subAgents = subAgents;
|
|
29478
29226
|
this.core = core;
|
|
29479
29227
|
}
|
|
29480
|
-
registerRoute(
|
|
29481
|
-
this.routeHandlers.set(
|
|
29228
|
+
registerRoute(path58, handler) {
|
|
29229
|
+
this.routeHandlers.set(path58, handler);
|
|
29482
29230
|
}
|
|
29483
29231
|
async handleRoute(payload) {
|
|
29484
29232
|
const inputText = String(payload.content || "").trim();
|
|
29485
29233
|
const { userContext, options } = payload;
|
|
29486
|
-
for (const [
|
|
29487
|
-
if (inputText ===
|
|
29234
|
+
for (const [path58, handler] of this.routeHandlers) {
|
|
29235
|
+
if (inputText === path58 || inputText.startsWith(`${path58} `)) {
|
|
29488
29236
|
return handler({ content: inputText, userContext });
|
|
29489
29237
|
}
|
|
29490
29238
|
}
|
|
@@ -29493,13 +29241,13 @@ var RouteManager = class {
|
|
|
29493
29241
|
};
|
|
29494
29242
|
|
|
29495
29243
|
// src/app/agent/runtime/plugin_runtime.ts
|
|
29496
|
-
import
|
|
29244
|
+
import path47 from "path";
|
|
29497
29245
|
import { pathToFileURL as pathToFileURL2 } from "url";
|
|
29498
29246
|
async function loadPluginsAtStartup() {
|
|
29499
29247
|
for (const p of listPlugins()) {
|
|
29500
29248
|
const entry = p.manifest.entry?.trim();
|
|
29501
29249
|
if (!entry) continue;
|
|
29502
|
-
const abs =
|
|
29250
|
+
const abs = path47.resolve(p.root, entry);
|
|
29503
29251
|
try {
|
|
29504
29252
|
const href = pathToFileURL2(abs).href;
|
|
29505
29253
|
const mod = await import(href);
|
|
@@ -29520,7 +29268,7 @@ async function loadPluginsAtStartup() {
|
|
|
29520
29268
|
}
|
|
29521
29269
|
|
|
29522
29270
|
// src/app/agent/agent.ts
|
|
29523
|
-
var globalEnvPath =
|
|
29271
|
+
var globalEnvPath = path48.join(os30.homedir(), ".bluma", ".env");
|
|
29524
29272
|
dotenv.config({ path: globalEnvPath });
|
|
29525
29273
|
var Agent = class {
|
|
29526
29274
|
sessionId;
|
|
@@ -31376,10 +31124,10 @@ function resolveToolPayload(result) {
|
|
|
31376
31124
|
|
|
31377
31125
|
// src/app/ui/components/FilePathLink.tsx
|
|
31378
31126
|
import { pathToFileURL as pathToFileURL3 } from "node:url";
|
|
31379
|
-
import
|
|
31127
|
+
import path50 from "node:path";
|
|
31380
31128
|
|
|
31381
31129
|
// src/app/ui/utils/pathDisplay.ts
|
|
31382
|
-
import
|
|
31130
|
+
import path49 from "node:path";
|
|
31383
31131
|
import os31 from "node:os";
|
|
31384
31132
|
function formatPathForDisplay(pathInput, cwd2 = process.cwd()) {
|
|
31385
31133
|
let s = String(pathInput ?? "").trim();
|
|
@@ -31387,17 +31135,17 @@ function formatPathForDisplay(pathInput, cwd2 = process.cwd()) {
|
|
|
31387
31135
|
s = s.replace(/[/\\]+$/, "");
|
|
31388
31136
|
}
|
|
31389
31137
|
if (!s) return "";
|
|
31390
|
-
const abs =
|
|
31391
|
-
const resolvedCwd =
|
|
31392
|
-
const rel =
|
|
31393
|
-
if (rel === "" || !rel.startsWith("..") && !
|
|
31138
|
+
const abs = path49.isAbsolute(s) ? path49.normalize(s) : path49.resolve(cwd2, s);
|
|
31139
|
+
const resolvedCwd = path49.resolve(cwd2);
|
|
31140
|
+
const rel = path49.relative(resolvedCwd, abs);
|
|
31141
|
+
if (rel === "" || !rel.startsWith("..") && !path49.isAbsolute(rel)) {
|
|
31394
31142
|
return rel === "" ? "." : rel;
|
|
31395
31143
|
}
|
|
31396
|
-
const home =
|
|
31397
|
-
if (abs === home || abs.startsWith(home +
|
|
31144
|
+
const home = path49.normalize(os31.homedir());
|
|
31145
|
+
if (abs === home || abs.startsWith(home + path49.sep)) {
|
|
31398
31146
|
return "~" + abs.slice(home.length);
|
|
31399
31147
|
}
|
|
31400
|
-
return
|
|
31148
|
+
return path49.basename(abs);
|
|
31401
31149
|
}
|
|
31402
31150
|
|
|
31403
31151
|
// src/app/ui/components/FilePathLink.tsx
|
|
@@ -31407,7 +31155,7 @@ function FilePathLink({ filePath, children, cwd: cwd2 = process.cwd(), color })
|
|
|
31407
31155
|
if (!raw) {
|
|
31408
31156
|
return null;
|
|
31409
31157
|
}
|
|
31410
|
-
const abs =
|
|
31158
|
+
const abs = path50.isAbsolute(raw) ? path50.normalize(raw) : path50.resolve(cwd2, raw);
|
|
31411
31159
|
const href = pathToFileURL3(abs).href;
|
|
31412
31160
|
const label = formatPathForDisplay(abs, cwd2);
|
|
31413
31161
|
const text = typeof children === "string" ? children : label;
|
|
@@ -33359,12 +33107,12 @@ function patchToUnifiedDiffText(filePath, patch) {
|
|
|
33359
33107
|
}
|
|
33360
33108
|
|
|
33361
33109
|
// src/app/ui/utils/readEditContext.ts
|
|
33362
|
-
import { promises as
|
|
33110
|
+
import { promises as fs42 } from "fs";
|
|
33363
33111
|
var CHUNK_SIZE = 64 * 1024;
|
|
33364
33112
|
var CONTEXT_LINES2 = 3;
|
|
33365
33113
|
async function openForScan(filePath) {
|
|
33366
33114
|
try {
|
|
33367
|
-
return await
|
|
33115
|
+
return await fs42.open(filePath, "r");
|
|
33368
33116
|
} catch (e) {
|
|
33369
33117
|
if (e.code === "ENOENT") return null;
|
|
33370
33118
|
throw e;
|
|
@@ -34042,13 +33790,13 @@ function EditToolDiffPanel({
|
|
|
34042
33790
|
newString,
|
|
34043
33791
|
replaceAll = false
|
|
34044
33792
|
}) {
|
|
34045
|
-
const
|
|
33793
|
+
const path58 = filePath.trim() || "unknown file";
|
|
34046
33794
|
const hasPreviewArgs = oldString !== void 0 && newString !== void 0;
|
|
34047
33795
|
const hasDiffText = diffText && diffText.trim().length > 0;
|
|
34048
33796
|
return /* @__PURE__ */ jsx43(Box_default, { flexDirection: "column", children: hasPreviewArgs ? /* @__PURE__ */ jsx43(Box_default, { marginTop: 0, children: /* @__PURE__ */ jsx43(
|
|
34049
33797
|
FileEditToolDiff,
|
|
34050
33798
|
{
|
|
34051
|
-
filePath:
|
|
33799
|
+
filePath: path58,
|
|
34052
33800
|
oldString,
|
|
34053
33801
|
newString,
|
|
34054
33802
|
replaceAll,
|
|
@@ -34086,7 +33834,7 @@ function renderToolUseMessage12({ args }) {
|
|
|
34086
33834
|
return /* @__PURE__ */ jsx44(Text, { color: BLUMA_TERMINAL.blue, children: p });
|
|
34087
33835
|
}
|
|
34088
33836
|
function renderToolHeader12({ args }) {
|
|
34089
|
-
const
|
|
33837
|
+
const path58 = args?.file_path ?? ".";
|
|
34090
33838
|
const oldText = typeof args?.old_string === "string" ? args.old_string : "";
|
|
34091
33839
|
const newText = typeof args?.new_string === "string" ? args.new_string : "";
|
|
34092
33840
|
const counts = countLineDiff(oldText, newText);
|
|
@@ -34096,7 +33844,7 @@ function renderToolHeader12({ args }) {
|
|
|
34096
33844
|
action,
|
|
34097
33845
|
/* @__PURE__ */ jsxs27(Text, { dimColor: true, children: [
|
|
34098
33846
|
" ",
|
|
34099
|
-
/* @__PURE__ */ jsx44(FilePathLink, { filePath:
|
|
33847
|
+
/* @__PURE__ */ jsx44(FilePathLink, { filePath: path58, color: BLUMA_TERMINAL.dim })
|
|
34100
33848
|
] })
|
|
34101
33849
|
] }),
|
|
34102
33850
|
/* @__PURE__ */ jsxs27(Text, { dimColor: true, children: [
|
|
@@ -34488,11 +34236,11 @@ function userFacingName13() {
|
|
|
34488
34236
|
}
|
|
34489
34237
|
function renderToolUseMessage14({ args }) {
|
|
34490
34238
|
const q = args?.query ? `"${args.query}"` : "...";
|
|
34491
|
-
const
|
|
34239
|
+
const path58 = args?.path || ".";
|
|
34492
34240
|
return /* @__PURE__ */ jsxs30(Box_default, { flexDirection: "row", flexWrap: "wrap", alignItems: "flex-end", children: [
|
|
34493
34241
|
/* @__PURE__ */ jsx47(Text, { color: BLUMA_TERMINAL.blue, children: q }),
|
|
34494
34242
|
/* @__PURE__ */ jsx47(Text, { color: BLUMA_TERMINAL.dim, children: " " }),
|
|
34495
|
-
/* @__PURE__ */ jsx47(FilePathLink, { filePath:
|
|
34243
|
+
/* @__PURE__ */ jsx47(FilePathLink, { filePath: path58, color: BLUMA_TERMINAL.dim })
|
|
34496
34244
|
] });
|
|
34497
34245
|
}
|
|
34498
34246
|
function renderToolHeader14({ args }) {
|
|
@@ -35014,7 +34762,7 @@ var loadSkillTool = createTool({
|
|
|
35014
34762
|
});
|
|
35015
34763
|
|
|
35016
34764
|
// src/app/agent/tools/FileWriteTool/UI.tsx
|
|
35017
|
-
import
|
|
34765
|
+
import fs43 from "fs";
|
|
35018
34766
|
import { diffLines as diffLines3 } from "diff";
|
|
35019
34767
|
import { jsx as jsx54, jsxs as jsxs37 } from "react/jsx-runtime";
|
|
35020
34768
|
function getFilePath(args) {
|
|
@@ -35029,7 +34777,7 @@ function getFilePath(args) {
|
|
|
35029
34777
|
function readExistingFileText(filePath) {
|
|
35030
34778
|
if (!filePath) return "";
|
|
35031
34779
|
try {
|
|
35032
|
-
return
|
|
34780
|
+
return fs43.readFileSync(filePath, "utf-8");
|
|
35033
34781
|
} catch {
|
|
35034
34782
|
return "";
|
|
35035
34783
|
}
|
|
@@ -36345,7 +36093,7 @@ function renderToolUseMessage35({ args }) {
|
|
|
36345
36093
|
function renderToolHeader35({ args }) {
|
|
36346
36094
|
return /* @__PURE__ */ jsxs51(Box_default, { flexDirection: "row", gap: 1, alignItems: "flex-end", children: [
|
|
36347
36095
|
/* @__PURE__ */ jsx68(Text, { bold: true, color: BLUMA_TERMINAL.m3OnSurface, children: "Create App" }),
|
|
36348
|
-
/* @__PURE__ */ jsx68(Text, { color: BLUMA_TERMINAL.dim, children:
|
|
36096
|
+
/* @__PURE__ */ jsx68(Text, { color: BLUMA_TERMINAL.dim, children: "FactorAI scaffold" })
|
|
36349
36097
|
] });
|
|
36350
36098
|
}
|
|
36351
36099
|
function renderToolMessage34({
|
|
@@ -36370,11 +36118,20 @@ function renderToolResultMessage35(result) {
|
|
|
36370
36118
|
] });
|
|
36371
36119
|
}
|
|
36372
36120
|
return /* @__PURE__ */ jsxs51(Box_default, { flexDirection: "column", children: [
|
|
36121
|
+
result.summary && /* @__PURE__ */ jsx68(Text, { color: BLUMA_TERMINAL.m3OnSurface, wrap: "wrap", children: result.summary }),
|
|
36373
36122
|
result.projectPath && /* @__PURE__ */ jsxs51(Box_default, { flexDirection: "row", children: [
|
|
36374
36123
|
/* @__PURE__ */ jsx68(Text, { color: BLUMA_TERMINAL.dim, children: "\u2514\u2500 " }),
|
|
36375
36124
|
/* @__PURE__ */ jsx68(FilePathLink, { filePath: result.projectPath, color: BLUMA_TERMINAL.dim })
|
|
36376
36125
|
] }),
|
|
36377
|
-
|
|
36126
|
+
typeof result.uiComponentCount === "number" && /* @__PURE__ */ jsxs51(Text, { color: BLUMA_TERMINAL.dim, children: [
|
|
36127
|
+
result.uiComponentCount,
|
|
36128
|
+
" UI components \xB7 DESIGN.md written"
|
|
36129
|
+
] }),
|
|
36130
|
+
result.designDocPath && /* @__PURE__ */ jsxs51(Box_default, { flexDirection: "row", children: [
|
|
36131
|
+
/* @__PURE__ */ jsx68(Text, { color: BLUMA_TERMINAL.dim, children: "\u2514\u2500 " }),
|
|
36132
|
+
/* @__PURE__ */ jsx68(FilePathLink, { filePath: result.designDocPath, color: BLUMA_TERMINAL.dim })
|
|
36133
|
+
] }),
|
|
36134
|
+
Array.isArray(result.filesCreated) && result.filesCreated.length > 0 && /* @__PURE__ */ jsxs51(Text, { color: BLUMA_TERMINAL.dim, children: [
|
|
36378
36135
|
result.filesCreated.length,
|
|
36379
36136
|
" files created"
|
|
36380
36137
|
] }),
|
|
@@ -37959,8 +37716,8 @@ import {
|
|
|
37959
37716
|
|
|
37960
37717
|
// src/app/ui/hooks/useAtCompletion.ts
|
|
37961
37718
|
import { useEffect as useEffect11, useRef as useRef4, useState as useState13 } from "react";
|
|
37962
|
-
import
|
|
37963
|
-
import
|
|
37719
|
+
import fs44 from "fs";
|
|
37720
|
+
import path51 from "path";
|
|
37964
37721
|
var MAX_RESULTS3 = 50;
|
|
37965
37722
|
var DEFAULT_RECURSIVE_DEPTH = 2;
|
|
37966
37723
|
function listPathSuggestions(baseDir, pattern) {
|
|
@@ -37968,7 +37725,7 @@ function listPathSuggestions(baseDir, pattern) {
|
|
|
37968
37725
|
const patternEndsWithSlash = raw.endsWith("/");
|
|
37969
37726
|
const relDir = raw.replace(/^\/+|\/+$/g, "");
|
|
37970
37727
|
const filterPrefix = patternEndsWithSlash ? "" : relDir.split("/").slice(-1)[0] || "";
|
|
37971
|
-
const listDir =
|
|
37728
|
+
const listDir = path51.resolve(baseDir, relDir || ".");
|
|
37972
37729
|
const results = [];
|
|
37973
37730
|
const IGNORED_DIRS = ["node_modules", ".git", ".venv", "dist", "build"];
|
|
37974
37731
|
const IGNORED_EXTS = [".pyc", ".class", ".o", ".map", ".log", ".tmp"];
|
|
@@ -37985,7 +37742,7 @@ function listPathSuggestions(baseDir, pattern) {
|
|
|
37985
37742
|
}
|
|
37986
37743
|
function pushEntry(entryPath, label, isDir) {
|
|
37987
37744
|
if (results.length >= MAX_RESULTS3) return;
|
|
37988
|
-
const clean = label.split(
|
|
37745
|
+
const clean = label.split(path51.sep).join("/").replace(/[]+/g, "");
|
|
37989
37746
|
results.push({ label: clean + (isDir ? "/" : ""), fullPath: entryPath, isDir });
|
|
37990
37747
|
}
|
|
37991
37748
|
try {
|
|
@@ -37994,11 +37751,11 @@ function listPathSuggestions(baseDir, pattern) {
|
|
|
37994
37751
|
while (queue.length && results.length < MAX_RESULTS3) {
|
|
37995
37752
|
const node = queue.shift();
|
|
37996
37753
|
try {
|
|
37997
|
-
const entries =
|
|
37754
|
+
const entries = fs44.readdirSync(node.dir, { withFileTypes: true });
|
|
37998
37755
|
for (const entry of entries) {
|
|
37999
37756
|
if (isIgnoredName(entry.name)) continue;
|
|
38000
|
-
const entryAbs =
|
|
38001
|
-
const entryRel = node.rel ?
|
|
37757
|
+
const entryAbs = path51.join(node.dir, entry.name);
|
|
37758
|
+
const entryRel = node.rel ? path51.posix.join(node.rel, entry.name) : entry.name;
|
|
38002
37759
|
if (entryRel.split("/").includes("node_modules")) continue;
|
|
38003
37760
|
if (!entry.isDirectory() && isIgnoredFile(entry.name)) continue;
|
|
38004
37761
|
pushEntry(entryAbs, entryRel, entry.isDirectory());
|
|
@@ -38011,13 +37768,13 @@ function listPathSuggestions(baseDir, pattern) {
|
|
|
38011
37768
|
}
|
|
38012
37769
|
}
|
|
38013
37770
|
} else {
|
|
38014
|
-
const entries =
|
|
37771
|
+
const entries = fs44.readdirSync(listDir, { withFileTypes: true });
|
|
38015
37772
|
for (const entry of entries) {
|
|
38016
37773
|
if (filterPrefix && !entry.name.startsWith(filterPrefix)) continue;
|
|
38017
37774
|
if (isIgnoredName(entry.name)) continue;
|
|
38018
37775
|
if (!entry.isDirectory() && isIgnoredFile(entry.name)) continue;
|
|
38019
|
-
const entryAbs =
|
|
38020
|
-
const label = relDir ?
|
|
37776
|
+
const entryAbs = path51.join(listDir, entry.name);
|
|
37777
|
+
const label = relDir ? path51.posix.join(relDir, entry.name) : entry.name;
|
|
38021
37778
|
pushEntry(entryAbs, label, entry.isDirectory());
|
|
38022
37779
|
if (results.length >= MAX_RESULTS3) break;
|
|
38023
37780
|
}
|
|
@@ -38218,9 +37975,9 @@ var SlashSubmenuInlineComponent = ({ menu }) => {
|
|
|
38218
37975
|
var SlashSubmenuInline = memo15(SlashSubmenuInlineComponent);
|
|
38219
37976
|
|
|
38220
37977
|
// src/app/ui/utils/clipboardImage.ts
|
|
38221
|
-
import
|
|
37978
|
+
import fs45 from "fs";
|
|
38222
37979
|
import os32 from "os";
|
|
38223
|
-
import
|
|
37980
|
+
import path52 from "path";
|
|
38224
37981
|
import { spawn as spawn5, execFile as execFileCb, execSync as execSync4 } from "child_process";
|
|
38225
37982
|
import { promisify as promisify2 } from "util";
|
|
38226
37983
|
|
|
@@ -38228,12 +37985,12 @@ import { promisify as promisify2 } from "util";
|
|
|
38228
37985
|
import { existsSync as existsSync7 } from "fs";
|
|
38229
37986
|
import { createRequire as createRequire2 } from "module";
|
|
38230
37987
|
import { dirname as dirname4, join as join13 } from "path";
|
|
38231
|
-
import { fileURLToPath as
|
|
37988
|
+
import { fileURLToPath as fileURLToPath6 } from "url";
|
|
38232
37989
|
var __dirname;
|
|
38233
37990
|
function getDirname() {
|
|
38234
37991
|
if (!__dirname) {
|
|
38235
37992
|
try {
|
|
38236
|
-
__dirname = dirname4(
|
|
37993
|
+
__dirname = dirname4(fileURLToPath6(import.meta.url));
|
|
38237
37994
|
} catch {
|
|
38238
37995
|
__dirname = process.cwd();
|
|
38239
37996
|
}
|
|
@@ -38349,8 +38106,8 @@ function commandOnPath(cmd) {
|
|
|
38349
38106
|
function unixClipboardHelperDirs() {
|
|
38350
38107
|
const h = os32.homedir();
|
|
38351
38108
|
return [
|
|
38352
|
-
|
|
38353
|
-
|
|
38109
|
+
path52.join(h, ".local", "bin"),
|
|
38110
|
+
path52.join(h, "bin"),
|
|
38354
38111
|
"/usr/bin",
|
|
38355
38112
|
"/usr/local/bin",
|
|
38356
38113
|
"/bin",
|
|
@@ -38368,16 +38125,16 @@ function resolveHelperBinary(cmd) {
|
|
|
38368
38125
|
return cmd;
|
|
38369
38126
|
}
|
|
38370
38127
|
for (const dir of unixClipboardHelperDirs()) {
|
|
38371
|
-
const full =
|
|
38128
|
+
const full = path52.join(dir, cmd);
|
|
38372
38129
|
try {
|
|
38373
|
-
|
|
38130
|
+
fs45.accessSync(full, fs45.constants.X_OK);
|
|
38374
38131
|
return full;
|
|
38375
38132
|
} catch {
|
|
38376
38133
|
}
|
|
38377
38134
|
}
|
|
38378
38135
|
for (const dir of unixClipboardHelperDirs()) {
|
|
38379
|
-
const full =
|
|
38380
|
-
if (
|
|
38136
|
+
const full = path52.join(dir, cmd);
|
|
38137
|
+
if (fs45.existsSync(full)) {
|
|
38381
38138
|
return full;
|
|
38382
38139
|
}
|
|
38383
38140
|
}
|
|
@@ -38418,17 +38175,17 @@ function writeBufferIfImage(baseDir, buf) {
|
|
|
38418
38175
|
if (!ext) {
|
|
38419
38176
|
return null;
|
|
38420
38177
|
}
|
|
38421
|
-
const out =
|
|
38178
|
+
const out = path52.join(
|
|
38422
38179
|
baseDir,
|
|
38423
38180
|
`clip-${Date.now()}-${Math.random().toString(36).slice(2, 8)}${ext}`
|
|
38424
38181
|
);
|
|
38425
|
-
|
|
38182
|
+
fs45.writeFileSync(out, buf);
|
|
38426
38183
|
return out;
|
|
38427
38184
|
}
|
|
38428
38185
|
function unlinkQuiet(p) {
|
|
38429
38186
|
try {
|
|
38430
|
-
if (
|
|
38431
|
-
|
|
38187
|
+
if (fs45.existsSync(p)) {
|
|
38188
|
+
fs45.unlinkSync(p);
|
|
38432
38189
|
}
|
|
38433
38190
|
} catch {
|
|
38434
38191
|
}
|
|
@@ -38445,25 +38202,25 @@ async function tryDarwinClipboardy(baseDir) {
|
|
|
38445
38202
|
return null;
|
|
38446
38203
|
}
|
|
38447
38204
|
for (const src of tmpPaths) {
|
|
38448
|
-
if (!src || !
|
|
38205
|
+
if (!src || !fs45.existsSync(src)) {
|
|
38449
38206
|
continue;
|
|
38450
38207
|
}
|
|
38451
38208
|
let st;
|
|
38452
38209
|
try {
|
|
38453
|
-
st =
|
|
38210
|
+
st = fs45.statSync(src);
|
|
38454
38211
|
} catch {
|
|
38455
38212
|
continue;
|
|
38456
38213
|
}
|
|
38457
38214
|
if (st.size < 80 || st.size > CLIPBOARD_MAX_BYTES) {
|
|
38458
38215
|
continue;
|
|
38459
38216
|
}
|
|
38460
|
-
const ext =
|
|
38217
|
+
const ext = path52.extname(src).toLowerCase();
|
|
38461
38218
|
const safeExt = ext && /^\.(png|jpe?g|gif|webp)$/i.test(ext) ? ext : ".png";
|
|
38462
|
-
const out =
|
|
38219
|
+
const out = path52.join(
|
|
38463
38220
|
baseDir,
|
|
38464
38221
|
`clip-${Date.now()}-${Math.random().toString(36).slice(2, 8)}${safeExt}`
|
|
38465
38222
|
);
|
|
38466
|
-
|
|
38223
|
+
fs45.copyFileSync(src, out);
|
|
38467
38224
|
for (const p of tmpPaths) {
|
|
38468
38225
|
unlinkQuiet(p);
|
|
38469
38226
|
}
|
|
@@ -38477,14 +38234,14 @@ async function tryDarwinClipboardy(baseDir) {
|
|
|
38477
38234
|
return null;
|
|
38478
38235
|
}
|
|
38479
38236
|
async function tryWindowsPowerShell(outFile) {
|
|
38480
|
-
const ps = process.env.SystemRoot != null ?
|
|
38237
|
+
const ps = process.env.SystemRoot != null ? path52.join(
|
|
38481
38238
|
process.env.SystemRoot,
|
|
38482
38239
|
"System32",
|
|
38483
38240
|
"WindowsPowerShell",
|
|
38484
38241
|
"v1.0",
|
|
38485
38242
|
"powershell.exe"
|
|
38486
38243
|
) : "powershell.exe";
|
|
38487
|
-
if (!
|
|
38244
|
+
if (!fs45.existsSync(ps)) {
|
|
38488
38245
|
return false;
|
|
38489
38246
|
}
|
|
38490
38247
|
const script = "$ErrorActionPreference='Stop';Add-Type -AssemblyName System.Windows.Forms;Add-Type -AssemblyName System.Drawing;$img=[System.Windows.Forms.Clipboard]::GetImage();if($null -eq $img){exit 2};$img.Save($env:BLUMA_CLIP_OUT,[System.Drawing.Imaging.ImageFormat]::Png);exit 0";
|
|
@@ -38506,7 +38263,7 @@ async function tryWindowsPowerShell(outFile) {
|
|
|
38506
38263
|
return false;
|
|
38507
38264
|
}
|
|
38508
38265
|
try {
|
|
38509
|
-
const st =
|
|
38266
|
+
const st = fs45.statSync(outFile);
|
|
38510
38267
|
return st.size >= 80 && st.size <= CLIPBOARD_MAX_BYTES;
|
|
38511
38268
|
} catch {
|
|
38512
38269
|
return false;
|
|
@@ -38578,8 +38335,8 @@ function parseClipboardTextAsImagePath(raw) {
|
|
|
38578
38335
|
s = s.slice(1, -1);
|
|
38579
38336
|
}
|
|
38580
38337
|
s = s.trim();
|
|
38581
|
-
if (!CLIPBOARD_PATH_IMAGE_EXT.test(
|
|
38582
|
-
const abs =
|
|
38338
|
+
if (!CLIPBOARD_PATH_IMAGE_EXT.test(path52.extname(s))) return null;
|
|
38339
|
+
const abs = path52.isAbsolute(s) ? path52.normalize(s) : path52.resolve(process.cwd(), s);
|
|
38583
38340
|
return abs;
|
|
38584
38341
|
}
|
|
38585
38342
|
async function tryClipboardTextAsImageFile(baseDir) {
|
|
@@ -38593,19 +38350,19 @@ async function tryClipboardTextAsImageFile(baseDir) {
|
|
|
38593
38350
|
const abs = parseClipboardTextAsImagePath(t);
|
|
38594
38351
|
if (!abs) return null;
|
|
38595
38352
|
try {
|
|
38596
|
-
if (!
|
|
38597
|
-
const st =
|
|
38353
|
+
if (!fs45.existsSync(abs)) return null;
|
|
38354
|
+
const st = fs45.statSync(abs);
|
|
38598
38355
|
if (!st.isFile() || st.size > CLIPBOARD_MAX_BYTES || st.size < 20) return null;
|
|
38599
38356
|
} catch {
|
|
38600
38357
|
return null;
|
|
38601
38358
|
}
|
|
38602
|
-
const ext =
|
|
38603
|
-
const out =
|
|
38359
|
+
const ext = path52.extname(abs).toLowerCase();
|
|
38360
|
+
const out = path52.join(
|
|
38604
38361
|
baseDir,
|
|
38605
38362
|
`clip-${Date.now()}-${Math.random().toString(36).slice(2, 8)}${ext}`
|
|
38606
38363
|
);
|
|
38607
38364
|
try {
|
|
38608
|
-
|
|
38365
|
+
fs45.copyFileSync(abs, out);
|
|
38609
38366
|
return out;
|
|
38610
38367
|
} catch {
|
|
38611
38368
|
return null;
|
|
@@ -38615,7 +38372,7 @@ async function tryLinuxShellPipelineSave(baseDir) {
|
|
|
38615
38372
|
if (process.platform !== "linux" && process.platform !== "freebsd") {
|
|
38616
38373
|
return null;
|
|
38617
38374
|
}
|
|
38618
|
-
const outPath =
|
|
38375
|
+
const outPath = path52.join(
|
|
38619
38376
|
baseDir,
|
|
38620
38377
|
`clip-${Date.now()}-${Math.random().toString(36).slice(2, 8)}.tmp`
|
|
38621
38378
|
);
|
|
@@ -38637,11 +38394,11 @@ printf '%s' "$OUT"
|
|
|
38637
38394
|
maxBuffer: 4096
|
|
38638
38395
|
});
|
|
38639
38396
|
const written = String(stdout ?? "").trim();
|
|
38640
|
-
if (written !== outPath || !
|
|
38397
|
+
if (written !== outPath || !fs45.existsSync(outPath)) {
|
|
38641
38398
|
unlinkQuiet(outPath);
|
|
38642
38399
|
return null;
|
|
38643
38400
|
}
|
|
38644
|
-
const buf =
|
|
38401
|
+
const buf = fs45.readFileSync(outPath);
|
|
38645
38402
|
unlinkQuiet(outPath);
|
|
38646
38403
|
return writeBufferIfImage(baseDir, buf);
|
|
38647
38404
|
} catch {
|
|
@@ -38662,8 +38419,8 @@ async function tryNativeClipboardImage() {
|
|
|
38662
38419
|
}
|
|
38663
38420
|
try {
|
|
38664
38421
|
const result = readClipboardImageNative();
|
|
38665
|
-
if (
|
|
38666
|
-
const st =
|
|
38422
|
+
if (fs45.existsSync(result.path)) {
|
|
38423
|
+
const st = fs45.statSync(result.path);
|
|
38667
38424
|
if (st.size >= 80 && st.size <= CLIPBOARD_MAX_BYTES) {
|
|
38668
38425
|
return result.path;
|
|
38669
38426
|
}
|
|
@@ -38673,8 +38430,8 @@ async function tryNativeClipboardImage() {
|
|
|
38673
38430
|
return null;
|
|
38674
38431
|
}
|
|
38675
38432
|
async function readClipboardImageToTempFile() {
|
|
38676
|
-
const baseDir =
|
|
38677
|
-
|
|
38433
|
+
const baseDir = path52.join(os32.homedir(), ".cache", "bluma", "clipboard");
|
|
38434
|
+
fs45.mkdirSync(baseDir, { recursive: true });
|
|
38678
38435
|
const nativeResult = await tryNativeClipboardImage();
|
|
38679
38436
|
if (nativeResult) {
|
|
38680
38437
|
return nativeResult;
|
|
@@ -38692,7 +38449,7 @@ async function readClipboardImageToTempFile() {
|
|
|
38692
38449
|
}
|
|
38693
38450
|
}
|
|
38694
38451
|
if (process.platform === "win32") {
|
|
38695
|
-
const outFile =
|
|
38452
|
+
const outFile = path52.join(
|
|
38696
38453
|
baseDir,
|
|
38697
38454
|
`clip-${Date.now()}-${Math.random().toString(36).slice(2, 8)}.png`
|
|
38698
38455
|
);
|
|
@@ -38733,7 +38490,7 @@ function expandLargePastePlaceholder(value, pending) {
|
|
|
38733
38490
|
}
|
|
38734
38491
|
|
|
38735
38492
|
// src/app/ui/components/InputPrompt.tsx
|
|
38736
|
-
import
|
|
38493
|
+
import fs46 from "fs";
|
|
38737
38494
|
import { Fragment as Fragment7, jsx as jsx79, jsxs as jsxs62 } from "react/jsx-runtime";
|
|
38738
38495
|
var persistedInputState = { text: "", cursorPosition: 0 };
|
|
38739
38496
|
var StaticCursor = () => /* @__PURE__ */ jsx79(Box_default, { flexDirection: "row", flexWrap: "nowrap", children: /* @__PURE__ */ jsx79(Text, { bold: true, color: BLUMA_TERMINAL.m3OnSurface, children: "\u2588 " }) });
|
|
@@ -38900,7 +38657,7 @@ var InputPrompt = memo16(({
|
|
|
38900
38657
|
return;
|
|
38901
38658
|
}
|
|
38902
38659
|
if (routeText.startsWith("/")) {
|
|
38903
|
-
const isFilePath = map.size > 0 &&
|
|
38660
|
+
const isFilePath = map.size > 0 && fs46.existsSync(routeText.split(/\s+/)[0]);
|
|
38904
38661
|
if (isFilePath) {
|
|
38905
38662
|
uiEventBus.emit("user_overlay", {
|
|
38906
38663
|
kind: "message",
|
|
@@ -41181,8 +40938,8 @@ var renderCode = () => {
|
|
|
41181
40938
|
|
|
41182
40939
|
// src/app/agent/core/thread/thread_store.ts
|
|
41183
40940
|
init_bluma_app_dir();
|
|
41184
|
-
import
|
|
41185
|
-
import { promises as
|
|
40941
|
+
import path53 from "path";
|
|
40942
|
+
import { promises as fs47 } from "fs";
|
|
41186
40943
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
41187
40944
|
var INDEX_VERSION = 1;
|
|
41188
40945
|
var fileLocks2 = /* @__PURE__ */ new Map();
|
|
@@ -41207,9 +40964,9 @@ var ThreadStore = class {
|
|
|
41207
40964
|
packageVersion;
|
|
41208
40965
|
constructor() {
|
|
41209
40966
|
const appDir = getPreferredAppDir();
|
|
41210
|
-
this.threadsDir =
|
|
41211
|
-
this.archiveDir =
|
|
41212
|
-
this.indexPath =
|
|
40967
|
+
this.threadsDir = path53.join(appDir, "threads");
|
|
40968
|
+
this.archiveDir = path53.join(this.threadsDir, "archive");
|
|
40969
|
+
this.indexPath = path53.join(this.threadsDir, "index.json");
|
|
41213
40970
|
this.packageVersion = process.env.npm_package_version || "0.0.0";
|
|
41214
40971
|
}
|
|
41215
40972
|
// ==================== Inicialização ====================
|
|
@@ -41217,10 +40974,10 @@ var ThreadStore = class {
|
|
|
41217
40974
|
* Inicializa o diretório de threads
|
|
41218
40975
|
*/
|
|
41219
40976
|
async initialize() {
|
|
41220
|
-
await
|
|
41221
|
-
await
|
|
40977
|
+
await fs47.mkdir(this.threadsDir, { recursive: true });
|
|
40978
|
+
await fs47.mkdir(this.archiveDir, { recursive: true });
|
|
41222
40979
|
try {
|
|
41223
|
-
await
|
|
40980
|
+
await fs47.access(this.indexPath);
|
|
41224
40981
|
} catch {
|
|
41225
40982
|
await this.saveIndex({ version: INDEX_VERSION, threads: [], lastUpdated: (/* @__PURE__ */ new Date()).toISOString() });
|
|
41226
40983
|
}
|
|
@@ -41249,7 +41006,7 @@ var ThreadStore = class {
|
|
|
41249
41006
|
async loadIndex() {
|
|
41250
41007
|
return withFileLock2(this.indexPath, async () => {
|
|
41251
41008
|
try {
|
|
41252
|
-
const content = await
|
|
41009
|
+
const content = await fs47.readFile(this.indexPath, "utf-8");
|
|
41253
41010
|
return JSON.parse(content);
|
|
41254
41011
|
} catch {
|
|
41255
41012
|
return { version: INDEX_VERSION, threads: [], lastUpdated: (/* @__PURE__ */ new Date()).toISOString() };
|
|
@@ -41260,8 +41017,8 @@ var ThreadStore = class {
|
|
|
41260
41017
|
return withFileLock2(this.indexPath, async () => {
|
|
41261
41018
|
index.lastUpdated = (/* @__PURE__ */ new Date()).toISOString();
|
|
41262
41019
|
const tempPath = `${this.indexPath}.${Date.now()}.tmp`;
|
|
41263
|
-
await
|
|
41264
|
-
await
|
|
41020
|
+
await fs47.writeFile(tempPath, JSON.stringify(index, null, 2), "utf-8");
|
|
41021
|
+
await fs47.rename(tempPath, this.indexPath);
|
|
41265
41022
|
});
|
|
41266
41023
|
}
|
|
41267
41024
|
// ==================== Git Info ====================
|
|
@@ -41331,7 +41088,7 @@ var ThreadStore = class {
|
|
|
41331
41088
|
messages: params.initialMessages || []
|
|
41332
41089
|
};
|
|
41333
41090
|
const historyPath = this.buildDatedThreadHistoryPath(threadId);
|
|
41334
|
-
await
|
|
41091
|
+
await fs47.mkdir(path53.dirname(historyPath), { recursive: true });
|
|
41335
41092
|
await this.saveHistoryAtPath(historyPath, history);
|
|
41336
41093
|
const index = await this.loadIndex();
|
|
41337
41094
|
index.threads.unshift({
|
|
@@ -41386,7 +41143,7 @@ var ThreadStore = class {
|
|
|
41386
41143
|
compressedSliceCount: source.history.compressedSliceCount
|
|
41387
41144
|
};
|
|
41388
41145
|
const historyPath = this.buildDatedThreadHistoryPath(newThreadId);
|
|
41389
|
-
await
|
|
41146
|
+
await fs47.mkdir(path53.dirname(historyPath), { recursive: true });
|
|
41390
41147
|
await this.saveHistoryAtPath(historyPath, history);
|
|
41391
41148
|
const index = await this.loadIndex();
|
|
41392
41149
|
index.threads.unshift({
|
|
@@ -41459,9 +41216,9 @@ var ThreadStore = class {
|
|
|
41459
41216
|
const entry = index.threads[entryIndex];
|
|
41460
41217
|
if (entry.status === "archived") return true;
|
|
41461
41218
|
const oldPath = entry.historyPath || this.getLegacyHistoryPath(threadId);
|
|
41462
|
-
const newPath =
|
|
41219
|
+
const newPath = path53.join(this.archiveDir, `${threadId}.jsonl`);
|
|
41463
41220
|
try {
|
|
41464
|
-
await
|
|
41221
|
+
await fs47.rename(oldPath, newPath);
|
|
41465
41222
|
} catch (e) {
|
|
41466
41223
|
if (e.code !== "ENOENT") throw e;
|
|
41467
41224
|
}
|
|
@@ -41482,11 +41239,11 @@ var ThreadStore = class {
|
|
|
41482
41239
|
if (entryIndex === -1) return false;
|
|
41483
41240
|
const entry = index.threads[entryIndex];
|
|
41484
41241
|
if (entry.status === "active") return true;
|
|
41485
|
-
const oldPath =
|
|
41242
|
+
const oldPath = path53.join(this.archiveDir, `${threadId}.jsonl`);
|
|
41486
41243
|
const newPath = this.buildDatedThreadHistoryPath(threadId);
|
|
41487
|
-
await
|
|
41244
|
+
await fs47.mkdir(path53.dirname(newPath), { recursive: true });
|
|
41488
41245
|
try {
|
|
41489
|
-
await
|
|
41246
|
+
await fs47.rename(oldPath, newPath);
|
|
41490
41247
|
} catch (e) {
|
|
41491
41248
|
if (e.code !== "ENOENT") throw e;
|
|
41492
41249
|
}
|
|
@@ -41507,7 +41264,7 @@ var ThreadStore = class {
|
|
|
41507
41264
|
if (entryIndex === -1) return false;
|
|
41508
41265
|
const entry = index.threads[entryIndex];
|
|
41509
41266
|
try {
|
|
41510
|
-
await
|
|
41267
|
+
await fs47.unlink(entry.historyPath);
|
|
41511
41268
|
} catch {
|
|
41512
41269
|
}
|
|
41513
41270
|
index.threads.splice(entryIndex, 1);
|
|
@@ -41517,28 +41274,28 @@ var ThreadStore = class {
|
|
|
41517
41274
|
}
|
|
41518
41275
|
// ==================== Histórico ====================
|
|
41519
41276
|
getLegacyHistoryPath(threadId) {
|
|
41520
|
-
return
|
|
41277
|
+
return path53.join(this.threadsDir, `${threadId}.jsonl`);
|
|
41521
41278
|
}
|
|
41522
41279
|
/** ~/.bluma/threads/YYYY/MM/DD/<threadId>.jsonl (data local de criação). */
|
|
41523
41280
|
buildDatedThreadHistoryPath(threadId, at = /* @__PURE__ */ new Date()) {
|
|
41524
41281
|
const y = String(at.getFullYear());
|
|
41525
41282
|
const mo = String(at.getMonth() + 1).padStart(2, "0");
|
|
41526
41283
|
const d = String(at.getDate()).padStart(2, "0");
|
|
41527
|
-
return
|
|
41284
|
+
return path53.join(this.threadsDir, y, mo, d, `${threadId}.jsonl`);
|
|
41528
41285
|
}
|
|
41529
41286
|
async resolveHistoryPath(threadId) {
|
|
41530
41287
|
const index = await this.loadIndex();
|
|
41531
41288
|
const entry = index.threads.find((t) => t.threadId === threadId);
|
|
41532
41289
|
if (entry?.historyPath) {
|
|
41533
41290
|
try {
|
|
41534
|
-
await
|
|
41291
|
+
await fs47.access(entry.historyPath);
|
|
41535
41292
|
return entry.historyPath;
|
|
41536
41293
|
} catch {
|
|
41537
41294
|
}
|
|
41538
41295
|
}
|
|
41539
41296
|
const legacy = this.getLegacyHistoryPath(threadId);
|
|
41540
41297
|
try {
|
|
41541
|
-
await
|
|
41298
|
+
await fs47.access(legacy);
|
|
41542
41299
|
return legacy;
|
|
41543
41300
|
} catch {
|
|
41544
41301
|
return entry?.historyPath ?? legacy;
|
|
@@ -41555,9 +41312,9 @@ var ThreadStore = class {
|
|
|
41555
41312
|
for (const msg of history.messages) {
|
|
41556
41313
|
lines.push(JSON.stringify({ type: "message", ...msg }));
|
|
41557
41314
|
}
|
|
41558
|
-
await
|
|
41315
|
+
await fs47.mkdir(path53.dirname(historyPath), { recursive: true }).catch(() => {
|
|
41559
41316
|
});
|
|
41560
|
-
await
|
|
41317
|
+
await fs47.writeFile(historyPath, lines.join("\n") + "\n", "utf-8");
|
|
41561
41318
|
}
|
|
41562
41319
|
/**
|
|
41563
41320
|
* Guarda o histórico de uma thread
|
|
@@ -41579,7 +41336,7 @@ var ThreadStore = class {
|
|
|
41579
41336
|
].filter((p, i, arr) => Boolean(p) && arr.indexOf(p) === i);
|
|
41580
41337
|
for (const historyPath of pathsToTry) {
|
|
41581
41338
|
try {
|
|
41582
|
-
const content = await
|
|
41339
|
+
const content = await fs47.readFile(historyPath, "utf-8");
|
|
41583
41340
|
const lines = content.split("\n").filter(Boolean);
|
|
41584
41341
|
const history = {
|
|
41585
41342
|
threadId,
|
|
@@ -41614,7 +41371,7 @@ var ThreadStore = class {
|
|
|
41614
41371
|
const entry = index.threads.find((t) => t.threadId === threadId);
|
|
41615
41372
|
if (!entry) throw new Error(`Thread not found: ${threadId}`);
|
|
41616
41373
|
const lines = messages.map((msg) => JSON.stringify({ type: "message", ...msg }));
|
|
41617
|
-
await
|
|
41374
|
+
await fs47.appendFile(entry.historyPath, lines.join("\n") + "\n", "utf-8");
|
|
41618
41375
|
entry.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
41619
41376
|
await this.saveIndex(index);
|
|
41620
41377
|
}
|
|
@@ -44486,17 +44243,17 @@ var SlashCommands_default = SlashCommands;
|
|
|
44486
44243
|
import latestVersion from "latest-version";
|
|
44487
44244
|
import semverGt from "semver/functions/gt.js";
|
|
44488
44245
|
import semverValid from "semver/functions/valid.js";
|
|
44489
|
-
import { fileURLToPath as
|
|
44490
|
-
import
|
|
44491
|
-
import
|
|
44246
|
+
import { fileURLToPath as fileURLToPath7 } from "url";
|
|
44247
|
+
import path54 from "path";
|
|
44248
|
+
import fs48 from "fs";
|
|
44492
44249
|
var BLUMA_PACKAGE_NAME = "@nomad-e/bluma-cli";
|
|
44493
44250
|
function findBlumaPackageJson(startDir) {
|
|
44494
44251
|
let dir = startDir;
|
|
44495
44252
|
for (let i = 0; i < 12; i++) {
|
|
44496
|
-
const candidate =
|
|
44497
|
-
if (
|
|
44253
|
+
const candidate = path54.join(dir, "package.json");
|
|
44254
|
+
if (fs48.existsSync(candidate)) {
|
|
44498
44255
|
try {
|
|
44499
|
-
const raw =
|
|
44256
|
+
const raw = fs48.readFileSync(candidate, "utf8");
|
|
44500
44257
|
const parsed = JSON.parse(raw);
|
|
44501
44258
|
if (parsed?.name === BLUMA_PACKAGE_NAME && parsed?.version) {
|
|
44502
44259
|
return { name: parsed.name, version: String(parsed.version) };
|
|
@@ -44504,7 +44261,7 @@ function findBlumaPackageJson(startDir) {
|
|
|
44504
44261
|
} catch {
|
|
44505
44262
|
}
|
|
44506
44263
|
}
|
|
44507
|
-
const parent =
|
|
44264
|
+
const parent = path54.dirname(dir);
|
|
44508
44265
|
if (parent === dir) break;
|
|
44509
44266
|
dir = parent;
|
|
44510
44267
|
}
|
|
@@ -44513,13 +44270,13 @@ function findBlumaPackageJson(startDir) {
|
|
|
44513
44270
|
function resolveInstalledBlumaPackage() {
|
|
44514
44271
|
const tried = /* @__PURE__ */ new Set();
|
|
44515
44272
|
const tryFrom = (dir) => {
|
|
44516
|
-
const abs =
|
|
44273
|
+
const abs = path54.resolve(dir);
|
|
44517
44274
|
if (tried.has(abs)) return null;
|
|
44518
44275
|
tried.add(abs);
|
|
44519
44276
|
return findBlumaPackageJson(abs);
|
|
44520
44277
|
};
|
|
44521
44278
|
try {
|
|
44522
|
-
const fromBundle = tryFrom(
|
|
44279
|
+
const fromBundle = tryFrom(path54.dirname(fileURLToPath7(import.meta.url)));
|
|
44523
44280
|
if (fromBundle) return fromBundle;
|
|
44524
44281
|
} catch {
|
|
44525
44282
|
}
|
|
@@ -44527,12 +44284,12 @@ function resolveInstalledBlumaPackage() {
|
|
|
44527
44284
|
if (argv1 && !argv1.startsWith("-")) {
|
|
44528
44285
|
try {
|
|
44529
44286
|
let resolved = argv1;
|
|
44530
|
-
if (
|
|
44531
|
-
resolved =
|
|
44287
|
+
if (path54.isAbsolute(argv1) && fs48.existsSync(argv1)) {
|
|
44288
|
+
resolved = fs48.realpathSync(argv1);
|
|
44532
44289
|
} else {
|
|
44533
|
-
resolved =
|
|
44290
|
+
resolved = path54.resolve(process.cwd(), argv1);
|
|
44534
44291
|
}
|
|
44535
|
-
const fromArgv = tryFrom(
|
|
44292
|
+
const fromArgv = tryFrom(path54.dirname(resolved));
|
|
44536
44293
|
if (fromArgv) return fromArgv;
|
|
44537
44294
|
} catch {
|
|
44538
44295
|
}
|
|
@@ -45351,16 +45108,16 @@ function usePlanMode() {
|
|
|
45351
45108
|
|
|
45352
45109
|
// src/app/hooks/useAgentMode.ts
|
|
45353
45110
|
import { useState as useState22, useEffect as useEffect21, useCallback as useCallback9 } from "react";
|
|
45354
|
-
import * as
|
|
45355
|
-
import * as
|
|
45111
|
+
import * as fs49 from "fs";
|
|
45112
|
+
import * as path55 from "path";
|
|
45356
45113
|
import { homedir as homedir4 } from "os";
|
|
45357
|
-
var SETTINGS_PATH =
|
|
45114
|
+
var SETTINGS_PATH = path55.join(homedir4(), ".bluma", "settings.json");
|
|
45358
45115
|
function readAgentModeFromFile() {
|
|
45359
45116
|
try {
|
|
45360
|
-
if (!
|
|
45117
|
+
if (!fs49.existsSync(SETTINGS_PATH)) {
|
|
45361
45118
|
return "default";
|
|
45362
45119
|
}
|
|
45363
|
-
const content =
|
|
45120
|
+
const content = fs49.readFileSync(SETTINGS_PATH, "utf-8");
|
|
45364
45121
|
const settings = JSON.parse(content);
|
|
45365
45122
|
return settings.agentMode || "default";
|
|
45366
45123
|
} catch (error) {
|
|
@@ -45379,16 +45136,16 @@ function useAgentMode() {
|
|
|
45379
45136
|
}, []);
|
|
45380
45137
|
const updateAgentMode = useCallback9((mode) => {
|
|
45381
45138
|
try {
|
|
45382
|
-
if (!
|
|
45383
|
-
|
|
45139
|
+
if (!fs49.existsSync(SETTINGS_PATH)) {
|
|
45140
|
+
fs49.mkdirSync(path55.dirname(SETTINGS_PATH), { recursive: true });
|
|
45384
45141
|
}
|
|
45385
45142
|
let settings = {};
|
|
45386
|
-
if (
|
|
45387
|
-
const content =
|
|
45143
|
+
if (fs49.existsSync(SETTINGS_PATH)) {
|
|
45144
|
+
const content = fs49.readFileSync(SETTINGS_PATH, "utf-8");
|
|
45388
45145
|
settings = JSON.parse(content);
|
|
45389
45146
|
}
|
|
45390
45147
|
settings.agentMode = mode;
|
|
45391
|
-
|
|
45148
|
+
fs49.writeFileSync(SETTINGS_PATH, JSON.stringify(settings, null, 2), "utf-8");
|
|
45392
45149
|
setAgentMode(mode);
|
|
45393
45150
|
} catch (error) {
|
|
45394
45151
|
console.error("Failed to update agent mode:", error);
|
|
@@ -46652,10 +46409,10 @@ import { memo as memo26, useCallback as useCallback11, useEffect as useEffect23,
|
|
|
46652
46409
|
|
|
46653
46410
|
// src/app/agent/session_manager/session_resume_browser.ts
|
|
46654
46411
|
init_bluma_app_dir();
|
|
46655
|
-
import
|
|
46656
|
-
import { promises as
|
|
46412
|
+
import path56 from "path";
|
|
46413
|
+
import { promises as fs50 } from "fs";
|
|
46657
46414
|
function getSessionsRoot() {
|
|
46658
|
-
return
|
|
46415
|
+
return path56.join(getPreferredAppDir(), "sessions");
|
|
46659
46416
|
}
|
|
46660
46417
|
function normalizeSessionsCwd(cwd2) {
|
|
46661
46418
|
return cwd2.replace(/\\/g, "/").split("/").filter((p) => p && p !== "." && p !== "..").join("/");
|
|
@@ -46676,9 +46433,9 @@ async function sessionEntryFromFile(absPath, sessionId) {
|
|
|
46676
46433
|
let preview = "(no messages)";
|
|
46677
46434
|
let lastActivityMs = Date.now();
|
|
46678
46435
|
try {
|
|
46679
|
-
const st = await
|
|
46436
|
+
const st = await fs50.stat(absPath);
|
|
46680
46437
|
lastActivityMs = st.mtimeMs;
|
|
46681
|
-
const raw = await
|
|
46438
|
+
const raw = await fs50.readFile(absPath, "utf-8");
|
|
46682
46439
|
const data = JSON.parse(raw);
|
|
46683
46440
|
preview = previewFromHistory(data.conversation_history);
|
|
46684
46441
|
const iso = data.last_updated || data.created_at;
|
|
@@ -46706,15 +46463,15 @@ function compareDirNames(a, b) {
|
|
|
46706
46463
|
async function listSessionBrowserEntries(cwdRel) {
|
|
46707
46464
|
const cwd2 = normalizeSessionsCwd(cwdRel);
|
|
46708
46465
|
const root = getSessionsRoot();
|
|
46709
|
-
const absDir = cwd2 ?
|
|
46466
|
+
const absDir = cwd2 ? path56.join(root, ...cwd2.split("/")) : root;
|
|
46710
46467
|
const out = [];
|
|
46711
46468
|
if (cwd2) {
|
|
46712
46469
|
out.push({ kind: "up", label: ".." });
|
|
46713
46470
|
}
|
|
46714
46471
|
let dirents;
|
|
46715
46472
|
try {
|
|
46716
|
-
await
|
|
46717
|
-
dirents = await
|
|
46473
|
+
await fs50.mkdir(absDir, { recursive: true });
|
|
46474
|
+
dirents = await fs50.readdir(absDir, { withFileTypes: true });
|
|
46718
46475
|
} catch {
|
|
46719
46476
|
return out;
|
|
46720
46477
|
}
|
|
@@ -46723,7 +46480,7 @@ async function listSessionBrowserEntries(cwdRel) {
|
|
|
46723
46480
|
for (const e of dirents) {
|
|
46724
46481
|
const name = String(e.name);
|
|
46725
46482
|
if (name.startsWith(".")) continue;
|
|
46726
|
-
const full =
|
|
46483
|
+
const full = path56.join(absDir, name);
|
|
46727
46484
|
if (e.isDirectory()) {
|
|
46728
46485
|
dirNames.push(name);
|
|
46729
46486
|
} else if (e.isFile() && name.endsWith(".json") && !name.includes(".tmp")) {
|
|
@@ -46733,7 +46490,7 @@ async function listSessionBrowserEntries(cwdRel) {
|
|
|
46733
46490
|
dirNames.sort(compareDirNames);
|
|
46734
46491
|
const sessions = [];
|
|
46735
46492
|
for (const { name, full } of sessionFiles) {
|
|
46736
|
-
const sessionId =
|
|
46493
|
+
const sessionId = path56.basename(name, ".json");
|
|
46737
46494
|
sessions.push(await sessionEntryFromFile(full, sessionId));
|
|
46738
46495
|
}
|
|
46739
46496
|
sessions.sort((a, b) => {
|
|
@@ -47098,9 +46855,9 @@ async function runAgentMode() {
|
|
|
47098
46855
|
try {
|
|
47099
46856
|
if (inputFileIndex !== -1 && args[inputFileIndex + 1]) {
|
|
47100
46857
|
const filePath = args[inputFileIndex + 1];
|
|
47101
|
-
rawPayload =
|
|
46858
|
+
rawPayload = fs51.readFileSync(filePath, "utf-8");
|
|
47102
46859
|
} else {
|
|
47103
|
-
rawPayload =
|
|
46860
|
+
rawPayload = fs51.readFileSync(0, "utf-8");
|
|
47104
46861
|
}
|
|
47105
46862
|
} catch (err) {
|
|
47106
46863
|
writeAgentEvent(registrySessionId, {
|
|
@@ -47341,9 +47098,9 @@ async function runAgentMode() {
|
|
|
47341
47098
|
}
|
|
47342
47099
|
function readCliPackageVersion() {
|
|
47343
47100
|
try {
|
|
47344
|
-
const base =
|
|
47345
|
-
const pkgPath =
|
|
47346
|
-
const j = JSON.parse(
|
|
47101
|
+
const base = path57.dirname(fileURLToPath8(import.meta.url));
|
|
47102
|
+
const pkgPath = path57.join(base, "..", "package.json");
|
|
47103
|
+
const j = JSON.parse(fs51.readFileSync(pkgPath, "utf8"));
|
|
47347
47104
|
return String(j.version || "0.0.0");
|
|
47348
47105
|
} catch {
|
|
47349
47106
|
return "0.0.0";
|
|
@@ -47469,7 +47226,7 @@ function startBackgroundAgent() {
|
|
|
47469
47226
|
process.exit(1);
|
|
47470
47227
|
}
|
|
47471
47228
|
const filePath = args[inputFileIndex + 1];
|
|
47472
|
-
const rawPayload =
|
|
47229
|
+
const rawPayload = fs51.readFileSync(filePath, "utf-8");
|
|
47473
47230
|
const envelope = JSON.parse(rawPayload);
|
|
47474
47231
|
const sessionId = envelope.session_id || envelope.message_id || uuidv412();
|
|
47475
47232
|
registerSession({
|