@mindstudio-ai/remy 0.1.249 → 0.1.251
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/headless.js +233 -228
- package/dist/index.js +258 -249
- package/dist/prompt/compiled/agent-interfaces.md +2 -0
- package/dist/prompt/compiled/sdk-actions.md +1 -1
- package/dist/prompt/compiled/task-agents.md +40 -9
- package/dist/prompt/static/coding.md +1 -1
- package/dist/prompt/static/intake.md +1 -1
- package/dist/subagents/codeSanityCheck/prompt.md +9 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -4479,11 +4479,34 @@ var init_runner = __esm({
|
|
|
4479
4479
|
}
|
|
4480
4480
|
});
|
|
4481
4481
|
|
|
4482
|
+
// src/subagents/common/tools.ts
|
|
4483
|
+
var COMMON_READ_TOOLS, COMMON_READ_TOOL_NAMES;
|
|
4484
|
+
var init_tools = __esm({
|
|
4485
|
+
"src/subagents/common/tools.ts"() {
|
|
4486
|
+
"use strict";
|
|
4487
|
+
init_readFile();
|
|
4488
|
+
init_listDir();
|
|
4489
|
+
init_grep();
|
|
4490
|
+
init_glob();
|
|
4491
|
+
COMMON_READ_TOOLS = [
|
|
4492
|
+
readFileTool.definition,
|
|
4493
|
+
listDirTool.definition,
|
|
4494
|
+
grepTool.definition,
|
|
4495
|
+
globTool.definition
|
|
4496
|
+
];
|
|
4497
|
+
COMMON_READ_TOOL_NAMES = new Set(
|
|
4498
|
+
COMMON_READ_TOOLS.map((t) => t.name)
|
|
4499
|
+
);
|
|
4500
|
+
}
|
|
4501
|
+
});
|
|
4502
|
+
|
|
4482
4503
|
// src/subagents/browserAutomation/tools.ts
|
|
4483
4504
|
var BROWSER_TOOLS, BROWSER_EXTERNAL_TOOLS;
|
|
4484
|
-
var
|
|
4505
|
+
var init_tools2 = __esm({
|
|
4485
4506
|
"src/subagents/browserAutomation/tools.ts"() {
|
|
4486
4507
|
"use strict";
|
|
4508
|
+
init_tools();
|
|
4509
|
+
init_readSpec();
|
|
4487
4510
|
BROWSER_TOOLS = [
|
|
4488
4511
|
{
|
|
4489
4512
|
clearable: false,
|
|
@@ -4626,24 +4649,199 @@ var init_tools = __esm({
|
|
|
4626
4649
|
}
|
|
4627
4650
|
}
|
|
4628
4651
|
}
|
|
4629
|
-
}
|
|
4652
|
+
},
|
|
4653
|
+
// Read tools so the QA agent can pull full spec detail on demand — the spec
|
|
4654
|
+
// context in its prompt is a lightweight index (see prompt.ts) that points
|
|
4655
|
+
// here. Routed to the global executeTool in index.ts, mirroring specSync.
|
|
4656
|
+
...COMMON_READ_TOOLS,
|
|
4657
|
+
readSpecTool.definition
|
|
4630
4658
|
];
|
|
4631
4659
|
BROWSER_EXTERNAL_TOOLS = /* @__PURE__ */ new Set(["browserCommand"]);
|
|
4632
4660
|
}
|
|
4633
4661
|
});
|
|
4634
4662
|
|
|
4635
|
-
// src/subagents/
|
|
4663
|
+
// src/subagents/common/context.ts
|
|
4636
4664
|
import fs15 from "fs";
|
|
4637
|
-
|
|
4665
|
+
import path8 from "path";
|
|
4666
|
+
function walkMdFiles(dir, skip) {
|
|
4667
|
+
const files = [];
|
|
4638
4668
|
try {
|
|
4639
|
-
const
|
|
4640
|
-
|
|
4669
|
+
for (const entry of fs15.readdirSync(dir, { withFileTypes: true })) {
|
|
4670
|
+
const full = path8.join(dir, entry.name);
|
|
4671
|
+
if (entry.isDirectory()) {
|
|
4672
|
+
if (!skip?.has(entry.name)) {
|
|
4673
|
+
files.push(...walkMdFiles(full, skip));
|
|
4674
|
+
}
|
|
4675
|
+
} else if (entry.name.endsWith(".md")) {
|
|
4676
|
+
files.push(full);
|
|
4677
|
+
}
|
|
4678
|
+
}
|
|
4679
|
+
} catch {
|
|
4680
|
+
}
|
|
4681
|
+
return files.sort();
|
|
4682
|
+
}
|
|
4683
|
+
function parseFrontmatter(filePath) {
|
|
4684
|
+
try {
|
|
4685
|
+
const content = fs15.readFileSync(filePath, "utf-8");
|
|
4686
|
+
const match = content.match(/^---\n([\s\S]*?)\n---/);
|
|
4687
|
+
if (!match) {
|
|
4688
|
+
return {};
|
|
4689
|
+
}
|
|
4690
|
+
const fm = {};
|
|
4691
|
+
for (const line of match[1].split("\n")) {
|
|
4692
|
+
const sep = line.indexOf(":");
|
|
4693
|
+
if (sep > 0) {
|
|
4694
|
+
const key = line.slice(0, sep).trim();
|
|
4695
|
+
const val = line.slice(sep + 1).trim();
|
|
4696
|
+
fm[key] = val;
|
|
4697
|
+
}
|
|
4698
|
+
}
|
|
4699
|
+
return fm;
|
|
4700
|
+
} catch {
|
|
4701
|
+
return {};
|
|
4702
|
+
}
|
|
4703
|
+
}
|
|
4704
|
+
function loadSpecIndex() {
|
|
4705
|
+
const files = walkMdFiles("src", /* @__PURE__ */ new Set(["roadmap"]));
|
|
4706
|
+
if (files.length === 0) {
|
|
4707
|
+
return "";
|
|
4708
|
+
}
|
|
4709
|
+
const lines = files.map((f) => {
|
|
4710
|
+
const fm = parseFrontmatter(f);
|
|
4711
|
+
let line = `- ${f}`;
|
|
4712
|
+
if (fm.name) {
|
|
4713
|
+
line += ` \u2014 "${fm.name}"`;
|
|
4714
|
+
}
|
|
4715
|
+
if (fm.description) {
|
|
4716
|
+
line += ` \u2014 ${fm.description}`;
|
|
4717
|
+
}
|
|
4718
|
+
return line;
|
|
4719
|
+
});
|
|
4720
|
+
return `<spec_files>
|
|
4721
|
+
## Project Spec Files
|
|
4722
|
+
Use readFile to access full contents.
|
|
4641
4723
|
|
|
4642
|
-
|
|
4724
|
+
${lines.join("\n")}
|
|
4725
|
+
</spec_files>`;
|
|
4726
|
+
}
|
|
4727
|
+
function loadRoadmapIndex() {
|
|
4728
|
+
const parts = [];
|
|
4729
|
+
try {
|
|
4730
|
+
const indexJson = JSON.parse(
|
|
4731
|
+
fs15.readFileSync("src/roadmap/index.json", "utf-8")
|
|
4732
|
+
);
|
|
4733
|
+
if (indexJson.lanes?.length > 0) {
|
|
4734
|
+
const laneLines = indexJson.lanes.map(
|
|
4735
|
+
(l) => `- **${l.name}**: ${l.narrative || ""} (${l.items?.length || 0} items)`
|
|
4736
|
+
);
|
|
4737
|
+
parts.push(`### Lanes
|
|
4738
|
+
${laneLines.join("\n")}`);
|
|
4739
|
+
}
|
|
4740
|
+
if (indexJson.standalone?.length > 0) {
|
|
4741
|
+
parts.push(
|
|
4742
|
+
`### Standalone
|
|
4743
|
+
${indexJson.standalone.map((s) => `- ${s}`).join("\n")}`
|
|
4744
|
+
);
|
|
4745
|
+
}
|
|
4746
|
+
} catch {
|
|
4747
|
+
}
|
|
4748
|
+
const files = walkMdFiles("src/roadmap");
|
|
4749
|
+
if (files.length > 0) {
|
|
4750
|
+
const lines = files.map((f) => {
|
|
4751
|
+
const fm = parseFrontmatter(f);
|
|
4752
|
+
let line = `- ${f}`;
|
|
4753
|
+
if (fm.name) {
|
|
4754
|
+
line += ` \u2014 "${fm.name}"`;
|
|
4755
|
+
}
|
|
4756
|
+
if (fm.status) {
|
|
4757
|
+
line += ` (${fm.status})`;
|
|
4758
|
+
}
|
|
4759
|
+
if (fm.description) {
|
|
4760
|
+
line += ` \u2014 ${fm.description}`;
|
|
4761
|
+
}
|
|
4762
|
+
return line;
|
|
4763
|
+
});
|
|
4764
|
+
parts.push(`### Items
|
|
4765
|
+
${lines.join("\n")}`);
|
|
4766
|
+
}
|
|
4767
|
+
if (parts.length === 0) {
|
|
4768
|
+
return "";
|
|
4769
|
+
}
|
|
4770
|
+
return `<current_roadmap>
|
|
4771
|
+
## Roadmap
|
|
4772
|
+
Use readFile to access full contents.
|
|
4773
|
+
|
|
4774
|
+
${parts.join("\n\n")}
|
|
4775
|
+
</current_roadmap>`;
|
|
4776
|
+
}
|
|
4777
|
+
function loadPlatformBrief() {
|
|
4778
|
+
return `<platform_brief>
|
|
4779
|
+
## What is a Remy app?
|
|
4780
|
+
|
|
4781
|
+
A Remy app is a managed full-stack TypeScript project with three layers: a spec (natural language in src/), a backend contract (methods, tables, roles in dist/), and one or more interfaces (web, API, bots, cron, etc.). The spec is the source of truth; code is derived from it.
|
|
4782
|
+
|
|
4783
|
+
This is a capable, stable platform used in production by 100k+ users. Build with confidence \u2014 you're building production-grade apps, not fragile prototypes.
|
|
4784
|
+
|
|
4785
|
+
## What people build
|
|
4786
|
+
|
|
4787
|
+
- Business tools \u2014 client portals, approval workflows, admin panels with role-based access
|
|
4788
|
+
- AI-powered apps \u2014 document processors, image/video tools, content generators, conversational agents that take actions
|
|
4789
|
+
- Full-stack web apps \u2014 social platforms, membership sites, marketplaces, booking systems, community hubs \u2014 multi-user apps with auth, data, UI
|
|
4790
|
+
- Automations with no UI \u2014 cron jobs, webhook handlers, email processors, data sync pipelines
|
|
4791
|
+
- Marketing & launch pages \u2014 landing pages, waitlist pages with referral mechanics, product sites with scroll animations
|
|
4792
|
+
- Agent tools \u2014 MCP tool servers for AI assistants
|
|
4793
|
+
- Creative/interactive projects \u2014 browser games with p5.js or Three.js, interactive visualizations, generative art, portfolio sites
|
|
4794
|
+
- API services \u2014 backend logic exposed as REST endpoints
|
|
4795
|
+
- Simple static sites \u2014 no backend needed, just a web interface with a build step
|
|
4796
|
+
|
|
4797
|
+
An app can be any combination of these.
|
|
4798
|
+
|
|
4799
|
+
## Interfaces
|
|
4643
4800
|
|
|
4644
|
-
|
|
4645
|
-
|
|
4646
|
-
|
|
4801
|
+
Each interface type invokes the same backend methods. Methods don't know which interface called them.
|
|
4802
|
+
|
|
4803
|
+
- Web \u2014 any TypeScript project with a build command. Framework-agnostic (React, Vue, Svelte, vanilla, anything). The frontend SDK provides typed RPC to backend methods.
|
|
4804
|
+
- API \u2014 auto-generated REST endpoints for every method
|
|
4805
|
+
- Cron \u2014 scheduled jobs on a configurable interval
|
|
4806
|
+
- Webhook \u2014 HTTP endpoints that trigger methods
|
|
4807
|
+
- Email \u2014 inbound email processing
|
|
4808
|
+
- MCP \u2014 tool servers for AI assistants
|
|
4809
|
+
- Agent \u2014 conversational LLM interface with tool access to backend methods
|
|
4810
|
+
|
|
4811
|
+
## Backend
|
|
4812
|
+
|
|
4813
|
+
TypeScript running in a sandboxed environment. Any npm package can be installed. Key capabilities:
|
|
4814
|
+
|
|
4815
|
+
- Managed SQLite database with typed schemas and automatic migrations. Define a TypeScript interface, push, and the platform handles diffing and migrating.
|
|
4816
|
+
- Built-in app-managed auth. Opt-in via manifest \u2014 developer builds login UI, platform handles verification codes (email-code, sms-code) and cookie sessions. API key auth for programmatic access. No OAuth, no social login (no Apple, Google, Facebook, or GitHub sign-in). Backend methods use auth.requireRole() for access control.
|
|
4817
|
+
- Encrypted secrets with separate dev/prod values, injected as process.env. For third-party service credentials not covered by the SDK.
|
|
4818
|
+
- Git-native deployment. Push to default branch to deploy.
|
|
4819
|
+
|
|
4820
|
+
## MindStudio SDK
|
|
4821
|
+
|
|
4822
|
+
The first-party SDK (@mindstudio-ai/agent) provides access to 200+ AI models (OpenAI, Anthropic, Google, Meta, Mistral, and more) and 1000+ integrations (email, SMS, Slack, HubSpot, Google Workspace, web scraping, image/video generation, media processing, and much more) with zero configuration \u2014 credentials are handled automatically in the execution environment. No API keys needed. This SDK is robust and battle-tested in production.
|
|
4823
|
+
|
|
4824
|
+
## What Remy apps are NOT good for
|
|
4825
|
+
|
|
4826
|
+
- Native mobile apps (iOS/Android). Mobile-responsive web apps are fine.
|
|
4827
|
+
- Real-time multiplayer with persistent connections (no WebSocket support). Turn-based or async patterns work.
|
|
4828
|
+
</platform_brief>`;
|
|
4829
|
+
}
|
|
4830
|
+
var init_context = __esm({
|
|
4831
|
+
"src/subagents/common/context.ts"() {
|
|
4832
|
+
"use strict";
|
|
4833
|
+
}
|
|
4834
|
+
});
|
|
4835
|
+
|
|
4836
|
+
// src/subagents/browserAutomation/prompt.ts
|
|
4837
|
+
function getBrowserAutomationPrompt() {
|
|
4838
|
+
try {
|
|
4839
|
+
const specIndex = loadSpecIndex();
|
|
4840
|
+
const parts = [BASE_PROMPT, "<!-- cache_breakpoint -->"];
|
|
4841
|
+
if (specIndex) {
|
|
4842
|
+
parts.push(specIndex);
|
|
4843
|
+
}
|
|
4844
|
+
return parts.join("\n\n");
|
|
4647
4845
|
} catch {
|
|
4648
4846
|
return BASE_PROMPT;
|
|
4649
4847
|
}
|
|
@@ -4653,6 +4851,7 @@ var init_prompt = __esm({
|
|
|
4653
4851
|
"src/subagents/browserAutomation/prompt.ts"() {
|
|
4654
4852
|
"use strict";
|
|
4655
4853
|
init_assets();
|
|
4854
|
+
init_context();
|
|
4656
4855
|
BASE_PROMPT = readAsset("subagents/browserAutomation", "prompt.md");
|
|
4657
4856
|
}
|
|
4658
4857
|
});
|
|
@@ -4707,6 +4906,9 @@ async function runBrowserAutomation(task, context, opts) {
|
|
|
4707
4906
|
return `Error taking screenshot: ${err.message}`;
|
|
4708
4907
|
}
|
|
4709
4908
|
}
|
|
4909
|
+
if (COMMON_READ_TOOL_NAMES.has(name) || name === readSpecTool.definition.name) {
|
|
4910
|
+
return executeTool(name, _input, context);
|
|
4911
|
+
}
|
|
4710
4912
|
return `Error: unknown local tool "${name}"`;
|
|
4711
4913
|
},
|
|
4712
4914
|
apiConfig: context.apiConfig,
|
|
@@ -4794,8 +4996,11 @@ var log8, browserAutomationTool;
|
|
|
4794
4996
|
var init_browserAutomation = __esm({
|
|
4795
4997
|
"src/subagents/browserAutomation/index.ts"() {
|
|
4796
4998
|
"use strict";
|
|
4999
|
+
init_tools8();
|
|
4797
5000
|
init_runner();
|
|
5001
|
+
init_tools2();
|
|
4798
5002
|
init_tools();
|
|
5003
|
+
init_readSpec();
|
|
4799
5004
|
init_prompt();
|
|
4800
5005
|
init_sidecar();
|
|
4801
5006
|
init_browserLock();
|
|
@@ -4941,27 +5146,6 @@ var init_screenshot2 = __esm({
|
|
|
4941
5146
|
}
|
|
4942
5147
|
});
|
|
4943
5148
|
|
|
4944
|
-
// src/subagents/common/tools.ts
|
|
4945
|
-
var COMMON_READ_TOOLS, COMMON_READ_TOOL_NAMES;
|
|
4946
|
-
var init_tools2 = __esm({
|
|
4947
|
-
"src/subagents/common/tools.ts"() {
|
|
4948
|
-
"use strict";
|
|
4949
|
-
init_readFile();
|
|
4950
|
-
init_listDir();
|
|
4951
|
-
init_grep();
|
|
4952
|
-
init_glob();
|
|
4953
|
-
COMMON_READ_TOOLS = [
|
|
4954
|
-
readFileTool.definition,
|
|
4955
|
-
listDirTool.definition,
|
|
4956
|
-
grepTool.definition,
|
|
4957
|
-
globTool.definition
|
|
4958
|
-
];
|
|
4959
|
-
COMMON_READ_TOOL_NAMES = new Set(
|
|
4960
|
-
COMMON_READ_TOOLS.map((t) => t.name)
|
|
4961
|
-
);
|
|
4962
|
-
}
|
|
4963
|
-
});
|
|
4964
|
-
|
|
4965
5149
|
// src/subagents/designExpert/tools/searchGoogle.ts
|
|
4966
5150
|
var searchGoogle_exports = {};
|
|
4967
5151
|
__export(searchGoogle_exports, {
|
|
@@ -5631,185 +5815,12 @@ var init_editImages = __esm({
|
|
|
5631
5815
|
}
|
|
5632
5816
|
});
|
|
5633
5817
|
|
|
5634
|
-
// src/subagents/common/context.ts
|
|
5635
|
-
import fs16 from "fs";
|
|
5636
|
-
import path8 from "path";
|
|
5637
|
-
function walkMdFiles(dir, skip) {
|
|
5638
|
-
const files = [];
|
|
5639
|
-
try {
|
|
5640
|
-
for (const entry of fs16.readdirSync(dir, { withFileTypes: true })) {
|
|
5641
|
-
const full = path8.join(dir, entry.name);
|
|
5642
|
-
if (entry.isDirectory()) {
|
|
5643
|
-
if (!skip?.has(entry.name)) {
|
|
5644
|
-
files.push(...walkMdFiles(full, skip));
|
|
5645
|
-
}
|
|
5646
|
-
} else if (entry.name.endsWith(".md")) {
|
|
5647
|
-
files.push(full);
|
|
5648
|
-
}
|
|
5649
|
-
}
|
|
5650
|
-
} catch {
|
|
5651
|
-
}
|
|
5652
|
-
return files.sort();
|
|
5653
|
-
}
|
|
5654
|
-
function parseFrontmatter(filePath) {
|
|
5655
|
-
try {
|
|
5656
|
-
const content = fs16.readFileSync(filePath, "utf-8");
|
|
5657
|
-
const match = content.match(/^---\n([\s\S]*?)\n---/);
|
|
5658
|
-
if (!match) {
|
|
5659
|
-
return {};
|
|
5660
|
-
}
|
|
5661
|
-
const fm = {};
|
|
5662
|
-
for (const line of match[1].split("\n")) {
|
|
5663
|
-
const sep = line.indexOf(":");
|
|
5664
|
-
if (sep > 0) {
|
|
5665
|
-
const key = line.slice(0, sep).trim();
|
|
5666
|
-
const val = line.slice(sep + 1).trim();
|
|
5667
|
-
fm[key] = val;
|
|
5668
|
-
}
|
|
5669
|
-
}
|
|
5670
|
-
return fm;
|
|
5671
|
-
} catch {
|
|
5672
|
-
return {};
|
|
5673
|
-
}
|
|
5674
|
-
}
|
|
5675
|
-
function loadSpecIndex() {
|
|
5676
|
-
const files = walkMdFiles("src", /* @__PURE__ */ new Set(["roadmap"]));
|
|
5677
|
-
if (files.length === 0) {
|
|
5678
|
-
return "";
|
|
5679
|
-
}
|
|
5680
|
-
const lines = files.map((f) => {
|
|
5681
|
-
const fm = parseFrontmatter(f);
|
|
5682
|
-
let line = `- ${f}`;
|
|
5683
|
-
if (fm.name) {
|
|
5684
|
-
line += ` \u2014 "${fm.name}"`;
|
|
5685
|
-
}
|
|
5686
|
-
if (fm.description) {
|
|
5687
|
-
line += ` \u2014 ${fm.description}`;
|
|
5688
|
-
}
|
|
5689
|
-
return line;
|
|
5690
|
-
});
|
|
5691
|
-
return `<spec_files>
|
|
5692
|
-
## Project Spec Files
|
|
5693
|
-
Use readFile to access full contents.
|
|
5694
|
-
|
|
5695
|
-
${lines.join("\n")}
|
|
5696
|
-
</spec_files>`;
|
|
5697
|
-
}
|
|
5698
|
-
function loadRoadmapIndex() {
|
|
5699
|
-
const parts = [];
|
|
5700
|
-
try {
|
|
5701
|
-
const indexJson = JSON.parse(
|
|
5702
|
-
fs16.readFileSync("src/roadmap/index.json", "utf-8")
|
|
5703
|
-
);
|
|
5704
|
-
if (indexJson.lanes?.length > 0) {
|
|
5705
|
-
const laneLines = indexJson.lanes.map(
|
|
5706
|
-
(l) => `- **${l.name}**: ${l.narrative || ""} (${l.items?.length || 0} items)`
|
|
5707
|
-
);
|
|
5708
|
-
parts.push(`### Lanes
|
|
5709
|
-
${laneLines.join("\n")}`);
|
|
5710
|
-
}
|
|
5711
|
-
if (indexJson.standalone?.length > 0) {
|
|
5712
|
-
parts.push(
|
|
5713
|
-
`### Standalone
|
|
5714
|
-
${indexJson.standalone.map((s) => `- ${s}`).join("\n")}`
|
|
5715
|
-
);
|
|
5716
|
-
}
|
|
5717
|
-
} catch {
|
|
5718
|
-
}
|
|
5719
|
-
const files = walkMdFiles("src/roadmap");
|
|
5720
|
-
if (files.length > 0) {
|
|
5721
|
-
const lines = files.map((f) => {
|
|
5722
|
-
const fm = parseFrontmatter(f);
|
|
5723
|
-
let line = `- ${f}`;
|
|
5724
|
-
if (fm.name) {
|
|
5725
|
-
line += ` \u2014 "${fm.name}"`;
|
|
5726
|
-
}
|
|
5727
|
-
if (fm.status) {
|
|
5728
|
-
line += ` (${fm.status})`;
|
|
5729
|
-
}
|
|
5730
|
-
if (fm.description) {
|
|
5731
|
-
line += ` \u2014 ${fm.description}`;
|
|
5732
|
-
}
|
|
5733
|
-
return line;
|
|
5734
|
-
});
|
|
5735
|
-
parts.push(`### Items
|
|
5736
|
-
${lines.join("\n")}`);
|
|
5737
|
-
}
|
|
5738
|
-
if (parts.length === 0) {
|
|
5739
|
-
return "";
|
|
5740
|
-
}
|
|
5741
|
-
return `<current_roadmap>
|
|
5742
|
-
## Roadmap
|
|
5743
|
-
Use readFile to access full contents.
|
|
5744
|
-
|
|
5745
|
-
${parts.join("\n\n")}
|
|
5746
|
-
</current_roadmap>`;
|
|
5747
|
-
}
|
|
5748
|
-
function loadPlatformBrief() {
|
|
5749
|
-
return `<platform_brief>
|
|
5750
|
-
## What is a Remy app?
|
|
5751
|
-
|
|
5752
|
-
A Remy app is a managed full-stack TypeScript project with three layers: a spec (natural language in src/), a backend contract (methods, tables, roles in dist/), and one or more interfaces (web, API, bots, cron, etc.). The spec is the source of truth; code is derived from it.
|
|
5753
|
-
|
|
5754
|
-
This is a capable, stable platform used in production by 100k+ users. Build with confidence \u2014 you're building production-grade apps, not fragile prototypes.
|
|
5755
|
-
|
|
5756
|
-
## What people build
|
|
5757
|
-
|
|
5758
|
-
- Business tools \u2014 client portals, approval workflows, admin panels with role-based access
|
|
5759
|
-
- AI-powered apps \u2014 document processors, image/video tools, content generators, conversational agents that take actions
|
|
5760
|
-
- Full-stack web apps \u2014 social platforms, membership sites, marketplaces, booking systems, community hubs \u2014 multi-user apps with auth, data, UI
|
|
5761
|
-
- Automations with no UI \u2014 cron jobs, webhook handlers, email processors, data sync pipelines
|
|
5762
|
-
- Marketing & launch pages \u2014 landing pages, waitlist pages with referral mechanics, product sites with scroll animations
|
|
5763
|
-
- Agent tools \u2014 MCP tool servers for AI assistants
|
|
5764
|
-
- Creative/interactive projects \u2014 browser games with p5.js or Three.js, interactive visualizations, generative art, portfolio sites
|
|
5765
|
-
- API services \u2014 backend logic exposed as REST endpoints
|
|
5766
|
-
- Simple static sites \u2014 no backend needed, just a web interface with a build step
|
|
5767
|
-
|
|
5768
|
-
An app can be any combination of these.
|
|
5769
|
-
|
|
5770
|
-
## Interfaces
|
|
5771
|
-
|
|
5772
|
-
Each interface type invokes the same backend methods. Methods don't know which interface called them.
|
|
5773
|
-
|
|
5774
|
-
- Web \u2014 any TypeScript project with a build command. Framework-agnostic (React, Vue, Svelte, vanilla, anything). The frontend SDK provides typed RPC to backend methods.
|
|
5775
|
-
- API \u2014 auto-generated REST endpoints for every method
|
|
5776
|
-
- Cron \u2014 scheduled jobs on a configurable interval
|
|
5777
|
-
- Webhook \u2014 HTTP endpoints that trigger methods
|
|
5778
|
-
- Email \u2014 inbound email processing
|
|
5779
|
-
- MCP \u2014 tool servers for AI assistants
|
|
5780
|
-
- Agent \u2014 conversational LLM interface with tool access to backend methods
|
|
5781
|
-
|
|
5782
|
-
## Backend
|
|
5783
|
-
|
|
5784
|
-
TypeScript running in a sandboxed environment. Any npm package can be installed. Key capabilities:
|
|
5785
|
-
|
|
5786
|
-
- Managed SQLite database with typed schemas and automatic migrations. Define a TypeScript interface, push, and the platform handles diffing and migrating.
|
|
5787
|
-
- Built-in app-managed auth. Opt-in via manifest \u2014 developer builds login UI, platform handles verification codes (email-code, sms-code) and cookie sessions. API key auth for programmatic access. No OAuth, no social login (no Apple, Google, Facebook, or GitHub sign-in). Backend methods use auth.requireRole() for access control.
|
|
5788
|
-
- Encrypted secrets with separate dev/prod values, injected as process.env. For third-party service credentials not covered by the SDK.
|
|
5789
|
-
- Git-native deployment. Push to default branch to deploy.
|
|
5790
|
-
|
|
5791
|
-
## MindStudio SDK
|
|
5792
|
-
|
|
5793
|
-
The first-party SDK (@mindstudio-ai/agent) provides access to 200+ AI models (OpenAI, Anthropic, Google, Meta, Mistral, and more) and 1000+ integrations (email, SMS, Slack, HubSpot, Google Workspace, web scraping, image/video generation, media processing, and much more) with zero configuration \u2014 credentials are handled automatically in the execution environment. No API keys needed. This SDK is robust and battle-tested in production.
|
|
5794
|
-
|
|
5795
|
-
## What Remy apps are NOT good for
|
|
5796
|
-
|
|
5797
|
-
- Native mobile apps (iOS/Android). Mobile-responsive web apps are fine.
|
|
5798
|
-
- Real-time multiplayer with persistent connections (no WebSocket support). Turn-based or async patterns work.
|
|
5799
|
-
</platform_brief>`;
|
|
5800
|
-
}
|
|
5801
|
-
var init_context = __esm({
|
|
5802
|
-
"src/subagents/common/context.ts"() {
|
|
5803
|
-
"use strict";
|
|
5804
|
-
}
|
|
5805
|
-
});
|
|
5806
|
-
|
|
5807
5818
|
// src/subagents/copyEditor/tools.ts
|
|
5808
5819
|
var COPY_EDITOR_TOOLS;
|
|
5809
5820
|
var init_tools3 = __esm({
|
|
5810
5821
|
"src/subagents/copyEditor/tools.ts"() {
|
|
5811
5822
|
"use strict";
|
|
5812
|
-
|
|
5823
|
+
init_tools();
|
|
5813
5824
|
COPY_EDITOR_TOOLS = [...COMMON_READ_TOOLS];
|
|
5814
5825
|
}
|
|
5815
5826
|
});
|
|
@@ -5922,7 +5933,7 @@ var init_tools4 = __esm({
|
|
|
5922
5933
|
"src/subagents/designExpert/tools/index.ts"() {
|
|
5923
5934
|
"use strict";
|
|
5924
5935
|
init_tools8();
|
|
5925
|
-
|
|
5936
|
+
init_tools();
|
|
5926
5937
|
init_searchGoogle2();
|
|
5927
5938
|
init_scrapeWebUrl();
|
|
5928
5939
|
init_analyzeDesign();
|
|
@@ -6012,7 +6023,7 @@ var init_orgContext = __esm({
|
|
|
6012
6023
|
});
|
|
6013
6024
|
|
|
6014
6025
|
// src/subagents/designExpert/data/sampleCache.ts
|
|
6015
|
-
import
|
|
6026
|
+
import fs16 from "fs";
|
|
6016
6027
|
function generateIndices(poolSize, sampleSize) {
|
|
6017
6028
|
const n = Math.min(sampleSize, poolSize);
|
|
6018
6029
|
const indices = Array.from({ length: poolSize }, (_, i) => i);
|
|
@@ -6024,14 +6035,14 @@ function generateIndices(poolSize, sampleSize) {
|
|
|
6024
6035
|
}
|
|
6025
6036
|
function load() {
|
|
6026
6037
|
try {
|
|
6027
|
-
return JSON.parse(
|
|
6038
|
+
return JSON.parse(fs16.readFileSync(SAMPLE_FILE, "utf-8"));
|
|
6028
6039
|
} catch {
|
|
6029
6040
|
return null;
|
|
6030
6041
|
}
|
|
6031
6042
|
}
|
|
6032
6043
|
function save(indices) {
|
|
6033
6044
|
try {
|
|
6034
|
-
|
|
6045
|
+
fs16.writeFileSync(SAMPLE_FILE, JSON.stringify(indices));
|
|
6035
6046
|
} catch {
|
|
6036
6047
|
}
|
|
6037
6048
|
}
|
|
@@ -6353,7 +6364,7 @@ var init_designExpert = __esm({
|
|
|
6353
6364
|
init_tools8();
|
|
6354
6365
|
init_runner();
|
|
6355
6366
|
init_tools4();
|
|
6356
|
-
|
|
6367
|
+
init_tools();
|
|
6357
6368
|
init_prompt2();
|
|
6358
6369
|
init_history();
|
|
6359
6370
|
init_surfaces();
|
|
@@ -6411,7 +6422,7 @@ var VISION_TOOLS;
|
|
|
6411
6422
|
var init_tools5 = __esm({
|
|
6412
6423
|
"src/subagents/productVision/tools.ts"() {
|
|
6413
6424
|
"use strict";
|
|
6414
|
-
|
|
6425
|
+
init_tools();
|
|
6415
6426
|
VISION_TOOLS = [
|
|
6416
6427
|
...COMMON_READ_TOOLS,
|
|
6417
6428
|
{
|
|
@@ -6465,7 +6476,7 @@ var init_tools5 = __esm({
|
|
|
6465
6476
|
});
|
|
6466
6477
|
|
|
6467
6478
|
// src/subagents/productVision/executor.ts
|
|
6468
|
-
import
|
|
6479
|
+
import fs17 from "fs";
|
|
6469
6480
|
import path9 from "path";
|
|
6470
6481
|
function resolve(filePath) {
|
|
6471
6482
|
return path9.join(ROADMAP_DIR, filePath);
|
|
@@ -6475,13 +6486,13 @@ async function executeVisionTool(name, input, context) {
|
|
|
6475
6486
|
case "writeFile": {
|
|
6476
6487
|
const filePath = resolve(input.path);
|
|
6477
6488
|
try {
|
|
6478
|
-
|
|
6489
|
+
fs17.mkdirSync(ROADMAP_DIR, { recursive: true });
|
|
6479
6490
|
let oldContent = null;
|
|
6480
6491
|
try {
|
|
6481
|
-
oldContent =
|
|
6492
|
+
oldContent = fs17.readFileSync(filePath, "utf-8");
|
|
6482
6493
|
} catch {
|
|
6483
6494
|
}
|
|
6484
|
-
|
|
6495
|
+
fs17.writeFileSync(filePath, input.content, "utf-8");
|
|
6485
6496
|
const lineCount = input.content.split("\n").length;
|
|
6486
6497
|
const label = oldContent !== null ? "Wrote" : "Created";
|
|
6487
6498
|
return `${label} ${filePath} (${lineCount} lines)
|
|
@@ -6493,11 +6504,11 @@ ${unifiedDiff(filePath, oldContent ?? "", input.content)}`;
|
|
|
6493
6504
|
case "deleteFile": {
|
|
6494
6505
|
const filePath = resolve(input.path);
|
|
6495
6506
|
try {
|
|
6496
|
-
if (!
|
|
6507
|
+
if (!fs17.existsSync(filePath)) {
|
|
6497
6508
|
return `Error: ${filePath} does not exist`;
|
|
6498
6509
|
}
|
|
6499
|
-
const oldContent =
|
|
6500
|
-
|
|
6510
|
+
const oldContent = fs17.readFileSync(filePath, "utf-8");
|
|
6511
|
+
fs17.unlinkSync(filePath);
|
|
6501
6512
|
return `Deleted ${filePath}
|
|
6502
6513
|
${unifiedDiff(filePath, oldContent, "")}`;
|
|
6503
6514
|
} catch (err) {
|
|
@@ -6510,9 +6521,9 @@ ${unifiedDiff(filePath, oldContent, "")}`;
|
|
|
6510
6521
|
}
|
|
6511
6522
|
const filePath = resolve("pitch.html");
|
|
6512
6523
|
try {
|
|
6513
|
-
|
|
6514
|
-
const exists =
|
|
6515
|
-
const before = exists ?
|
|
6524
|
+
fs17.mkdirSync(ROADMAP_DIR, { recursive: true });
|
|
6525
|
+
const exists = fs17.existsSync(filePath);
|
|
6526
|
+
const before = exists ? fs17.statSync(filePath).mtimeMs : null;
|
|
6516
6527
|
const delivery = exists ? `### Your deliverable
|
|
6517
6528
|
The pitch deck already exists at \`${filePath}\`. Read it, then update it for the new <pitch_content>, keeping the presentation scaffolding intact \u2014 change only what needs to change.
|
|
6518
6529
|
|
|
@@ -6542,11 +6553,11 @@ Maintain the bones of the presentation scaffolding. Always keep the progress bar
|
|
|
6542
6553
|
${delivery}`;
|
|
6543
6554
|
const result = await runDesignExpertRender({ task }, context);
|
|
6544
6555
|
context.subAgentMessages?.set(context.toolCallId, result.messages);
|
|
6545
|
-
if (!
|
|
6556
|
+
if (!fs17.existsSync(filePath)) {
|
|
6546
6557
|
return `Error: the design expert did not write ${filePath}. Its reply was:
|
|
6547
6558
|
${result.text}`;
|
|
6548
6559
|
}
|
|
6549
|
-
if (before !== null &&
|
|
6560
|
+
if (before !== null && fs17.statSync(filePath).mtimeMs === before) {
|
|
6550
6561
|
return `Error: the pitch deck at ${filePath} was not modified. The design expert's reply was:
|
|
6551
6562
|
${result.text}`;
|
|
6552
6563
|
}
|
|
@@ -6566,7 +6577,6 @@ var init_executor = __esm({
|
|
|
6566
6577
|
init_diff();
|
|
6567
6578
|
init_designExpert();
|
|
6568
6579
|
init_assets();
|
|
6569
|
-
init_context();
|
|
6570
6580
|
ROADMAP_DIR = "src/roadmap";
|
|
6571
6581
|
PITCH_DECK_SHELL = readAsset(
|
|
6572
6582
|
"subagents/productVision",
|
|
@@ -6594,7 +6604,6 @@ var init_prompt3 = __esm({
|
|
|
6594
6604
|
"src/subagents/productVision/prompt.ts"() {
|
|
6595
6605
|
"use strict";
|
|
6596
6606
|
init_assets();
|
|
6597
|
-
init_executor();
|
|
6598
6607
|
init_context();
|
|
6599
6608
|
BASE_PROMPT3 = readAsset("subagents/productVision", "prompt.md");
|
|
6600
6609
|
}
|
|
@@ -6608,7 +6617,7 @@ var init_productVision = __esm({
|
|
|
6608
6617
|
init_tools8();
|
|
6609
6618
|
init_runner();
|
|
6610
6619
|
init_tools5();
|
|
6611
|
-
|
|
6620
|
+
init_tools();
|
|
6612
6621
|
init_executor();
|
|
6613
6622
|
init_prompt3();
|
|
6614
6623
|
init_history();
|
|
@@ -6682,7 +6691,7 @@ var SANITY_CHECK_TOOLS;
|
|
|
6682
6691
|
var init_tools6 = __esm({
|
|
6683
6692
|
"src/subagents/codeSanityCheck/tools.ts"() {
|
|
6684
6693
|
"use strict";
|
|
6685
|
-
|
|
6694
|
+
init_tools();
|
|
6686
6695
|
SANITY_CHECK_TOOLS = [
|
|
6687
6696
|
...COMMON_READ_TOOLS,
|
|
6688
6697
|
{
|
|
@@ -6800,7 +6809,7 @@ var SPEC_SYNC_TOOLS;
|
|
|
6800
6809
|
var init_tools7 = __esm({
|
|
6801
6810
|
"src/subagents/specSync/tools.ts"() {
|
|
6802
6811
|
"use strict";
|
|
6803
|
-
|
|
6812
|
+
init_tools();
|
|
6804
6813
|
init_readSpec();
|
|
6805
6814
|
init_editSpec();
|
|
6806
6815
|
init_writeSpec();
|
|
@@ -6974,7 +6983,7 @@ var init_scrapeWebUrl2 = __esm({
|
|
|
6974
6983
|
});
|
|
6975
6984
|
|
|
6976
6985
|
// src/tools/spec/writeBuildOverview.ts
|
|
6977
|
-
import
|
|
6986
|
+
import fs18 from "fs";
|
|
6978
6987
|
function initialDelivery() {
|
|
6979
6988
|
return `### Your deliverable
|
|
6980
6989
|
Write the complete Build Overview to \`${OVERVIEW_FILE}\`. The file does not exist yet \u2014 start from this scaffold, which carries the technical hygiene for the iframed render context (keep the viewport meta and the reset). Layout, sections, type, and styling are yours to compose.
|
|
@@ -7059,7 +7068,7 @@ The copy in <overview_copy> is final \u2014 it was authored and edited before it
|
|
|
7059
7068
|
if (!content) {
|
|
7060
7069
|
return "Error: writeBuildOverview requires non-empty `content` (the overview copy).";
|
|
7061
7070
|
}
|
|
7062
|
-
const exists =
|
|
7071
|
+
const exists = fs18.existsSync(OVERVIEW_FILE);
|
|
7063
7072
|
const task = `<overview_copy>${content}</overview_copy>
|
|
7064
7073
|
|
|
7065
7074
|
${DESIGN_BRIEF}
|
|
@@ -7077,7 +7086,7 @@ ${exists ? refreshDelivery() : initialDelivery()}`;
|
|
|
7077
7086
|
}
|
|
7078
7087
|
const result = await runDesignExpertRender({ task }, context);
|
|
7079
7088
|
context.subAgentMessages?.set(context.toolCallId, result.messages);
|
|
7080
|
-
if (!
|
|
7089
|
+
if (!fs18.existsSync(OVERVIEW_FILE)) {
|
|
7081
7090
|
return `Error: the design expert did not write ${OVERVIEW_FILE}. Its reply was:
|
|
7082
7091
|
${result.text}`;
|
|
7083
7092
|
}
|
|
@@ -7416,7 +7425,7 @@ var init_errors = __esm({
|
|
|
7416
7425
|
});
|
|
7417
7426
|
|
|
7418
7427
|
// src/brandExtraction/index.ts
|
|
7419
|
-
import
|
|
7428
|
+
import fs19 from "fs";
|
|
7420
7429
|
import path10 from "path";
|
|
7421
7430
|
import { createHash } from "crypto";
|
|
7422
7431
|
async function runExtraction(apiConfig, model) {
|
|
@@ -7463,7 +7472,7 @@ function sha256(input) {
|
|
|
7463
7472
|
}
|
|
7464
7473
|
function readSafe(filePath) {
|
|
7465
7474
|
try {
|
|
7466
|
-
return
|
|
7475
|
+
return fs19.readFileSync(filePath, "utf-8");
|
|
7467
7476
|
} catch {
|
|
7468
7477
|
return "";
|
|
7469
7478
|
}
|
|
@@ -7471,7 +7480,7 @@ function readSafe(filePath) {
|
|
|
7471
7480
|
function walkMdFiles2(dir) {
|
|
7472
7481
|
const results = [];
|
|
7473
7482
|
try {
|
|
7474
|
-
const entries =
|
|
7483
|
+
const entries = fs19.readdirSync(dir, { withFileTypes: true });
|
|
7475
7484
|
for (const entry of entries) {
|
|
7476
7485
|
const full = path10.join(dir, entry.name);
|
|
7477
7486
|
if (entry.isDirectory()) {
|
|
@@ -7486,7 +7495,7 @@ function walkMdFiles2(dir) {
|
|
|
7486
7495
|
}
|
|
7487
7496
|
function parseFrontmatter2(filePath) {
|
|
7488
7497
|
try {
|
|
7489
|
-
const content =
|
|
7498
|
+
const content = fs19.readFileSync(filePath, "utf-8");
|
|
7490
7499
|
const match = content.match(/^---\n([\s\S]*?)\n---/);
|
|
7491
7500
|
if (!match) {
|
|
7492
7501
|
return { type: "" };
|
|
@@ -7683,14 +7692,14 @@ function pickFont(raw) {
|
|
|
7683
7692
|
}
|
|
7684
7693
|
function persistBrand(brand, inputHash) {
|
|
7685
7694
|
const tmp = `${BRAND_FILE}.tmp`;
|
|
7686
|
-
|
|
7687
|
-
|
|
7695
|
+
fs19.writeFileSync(tmp, JSON.stringify(brand, null, 2), "utf-8");
|
|
7696
|
+
fs19.renameSync(tmp, BRAND_FILE);
|
|
7688
7697
|
const cache = { inputHash, generatedAt: Date.now() };
|
|
7689
|
-
|
|
7698
|
+
fs19.writeFileSync(CACHE_FILE, JSON.stringify(cache, null, 2), "utf-8");
|
|
7690
7699
|
}
|
|
7691
7700
|
function readCache() {
|
|
7692
7701
|
try {
|
|
7693
|
-
const raw =
|
|
7702
|
+
const raw = fs19.readFileSync(CACHE_FILE, "utf-8");
|
|
7694
7703
|
const parsed = JSON.parse(raw);
|
|
7695
7704
|
if (parsed && typeof parsed.inputHash === "string" && typeof parsed.generatedAt === "number") {
|
|
7696
7705
|
return parsed;
|
|
@@ -8388,7 +8397,7 @@ var init_agent = __esm({
|
|
|
8388
8397
|
});
|
|
8389
8398
|
|
|
8390
8399
|
// src/prompt/static/projectContext.ts
|
|
8391
|
-
import
|
|
8400
|
+
import fs20 from "fs";
|
|
8392
8401
|
import path11 from "path";
|
|
8393
8402
|
function loadProjectRoot() {
|
|
8394
8403
|
return `
|
|
@@ -8399,7 +8408,7 @@ File paths are relative to this directory. Every tool operates here and bash com
|
|
|
8399
8408
|
}
|
|
8400
8409
|
function loadProjectManifest() {
|
|
8401
8410
|
try {
|
|
8402
|
-
const manifest =
|
|
8411
|
+
const manifest = fs20.readFileSync("mindstudio.json", "utf-8");
|
|
8403
8412
|
return `
|
|
8404
8413
|
## Project Manifest (mindstudio.json)
|
|
8405
8414
|
\`\`\`json
|
|
@@ -8440,7 +8449,7 @@ ${entries.join("\n")}`;
|
|
|
8440
8449
|
function walkMdFiles3(dir) {
|
|
8441
8450
|
const results = [];
|
|
8442
8451
|
try {
|
|
8443
|
-
const entries =
|
|
8452
|
+
const entries = fs20.readdirSync(dir, { withFileTypes: true });
|
|
8444
8453
|
for (const entry of entries) {
|
|
8445
8454
|
const full = path11.join(dir, entry.name);
|
|
8446
8455
|
if (entry.isDirectory()) {
|
|
@@ -8455,7 +8464,7 @@ function walkMdFiles3(dir) {
|
|
|
8455
8464
|
}
|
|
8456
8465
|
function parseFrontmatter3(filePath) {
|
|
8457
8466
|
try {
|
|
8458
|
-
const content =
|
|
8467
|
+
const content = fs20.readFileSync(filePath, "utf-8");
|
|
8459
8468
|
const match = content.match(/^---\n([\s\S]*?)\n---/);
|
|
8460
8469
|
if (!match) {
|
|
8461
8470
|
return { name: "", description: "", type: "" };
|
|
@@ -8471,7 +8480,7 @@ function parseFrontmatter3(filePath) {
|
|
|
8471
8480
|
}
|
|
8472
8481
|
function loadPlanStatus(onboardingState) {
|
|
8473
8482
|
try {
|
|
8474
|
-
const content =
|
|
8483
|
+
const content = fs20.readFileSync(".remy-plan.md", "utf-8");
|
|
8475
8484
|
const match = content.match(/^---\n([\s\S]*?)\n---/);
|
|
8476
8485
|
const status = match?.[1]?.match(/^status:\s*(.+)$/m)?.[1]?.trim();
|
|
8477
8486
|
if (status === "pending" && onboardingState === "intake") {
|
|
@@ -8499,7 +8508,7 @@ The user has approved your implementation plan in .remy-plan.md. You may referen
|
|
|
8499
8508
|
}
|
|
8500
8509
|
function loadProjectFileListing() {
|
|
8501
8510
|
try {
|
|
8502
|
-
const entries =
|
|
8511
|
+
const entries = fs20.readdirSync(".", { withFileTypes: true });
|
|
8503
8512
|
const listing = entries.filter((e) => e.name !== ".git" && e.name !== "node_modules").sort((a, b) => {
|
|
8504
8513
|
if (a.isDirectory() && !b.isDirectory()) {
|
|
8505
8514
|
return -1;
|
|
@@ -8682,12 +8691,12 @@ var init_prompt4 = __esm({
|
|
|
8682
8691
|
});
|
|
8683
8692
|
|
|
8684
8693
|
// src/config.ts
|
|
8685
|
-
import
|
|
8694
|
+
import fs21 from "fs";
|
|
8686
8695
|
import path12 from "path";
|
|
8687
8696
|
import os from "os";
|
|
8688
8697
|
function loadConfigFile() {
|
|
8689
8698
|
try {
|
|
8690
|
-
const raw =
|
|
8699
|
+
const raw = fs21.readFileSync(CONFIG_PATH, "utf-8");
|
|
8691
8700
|
log13.debug("Loaded config file", { path: CONFIG_PATH });
|
|
8692
8701
|
return JSON.parse(raw);
|
|
8693
8702
|
} catch (err) {
|
|
@@ -10008,7 +10017,7 @@ var init_headless = __esm({
|
|
|
10008
10017
|
// src/index.tsx
|
|
10009
10018
|
import { render } from "ink";
|
|
10010
10019
|
import os2 from "os";
|
|
10011
|
-
import
|
|
10020
|
+
import fs22 from "fs";
|
|
10012
10021
|
import path13 from "path";
|
|
10013
10022
|
|
|
10014
10023
|
// src/tui/App.tsx
|
|
@@ -10330,7 +10339,7 @@ for (let i = 0; i < args.length; i++) {
|
|
|
10330
10339
|
var startupLog = createLogger("startup");
|
|
10331
10340
|
function printDebugInfo(config) {
|
|
10332
10341
|
const pkg = JSON.parse(
|
|
10333
|
-
|
|
10342
|
+
fs22.readFileSync(
|
|
10334
10343
|
path13.join(import.meta.dirname, "..", "package.json"),
|
|
10335
10344
|
"utf-8"
|
|
10336
10345
|
)
|