@inkeep/agents-cli 0.39.5 → 0.41.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/_virtual/rolldown_runtime.js +7 -0
- package/dist/api.js +185 -0
- package/dist/commands/add.js +139 -0
- package/dist/commands/config.js +86 -0
- package/dist/commands/dev.js +259 -0
- package/dist/commands/init.js +360 -0
- package/dist/commands/list-agents.js +56 -0
- package/dist/commands/login.js +179 -0
- package/dist/commands/logout.js +56 -0
- package/dist/commands/profile.js +276 -0
- package/dist/{component-parser2.js → commands/pull-v3/component-parser.js} +16 -3
- package/dist/commands/pull-v3/component-updater.js +710 -0
- package/dist/commands/pull-v3/components/agent-generator.js +241 -0
- package/dist/commands/pull-v3/components/artifact-component-generator.js +143 -0
- package/dist/commands/pull-v3/components/context-config-generator.js +190 -0
- package/dist/commands/pull-v3/components/credential-generator.js +89 -0
- package/dist/commands/pull-v3/components/data-component-generator.js +102 -0
- package/dist/commands/pull-v3/components/environment-generator.js +170 -0
- package/dist/commands/pull-v3/components/external-agent-generator.js +75 -0
- package/dist/commands/pull-v3/components/function-tool-generator.js +94 -0
- package/dist/commands/pull-v3/components/mcp-tool-generator.js +86 -0
- package/dist/commands/pull-v3/components/project-generator.js +145 -0
- package/dist/commands/pull-v3/components/status-component-generator.js +92 -0
- package/dist/commands/pull-v3/components/sub-agent-generator.js +285 -0
- package/dist/commands/pull-v3/index.js +510 -0
- package/dist/commands/pull-v3/introspect-generator.js +278 -0
- package/dist/commands/pull-v3/llm-content-merger.js +192 -0
- package/dist/{new-component-generator.js → commands/pull-v3/new-component-generator.js} +14 -3
- package/dist/commands/pull-v3/project-comparator.js +914 -0
- package/dist/{project-index-generator.js → commands/pull-v3/project-index-generator.js} +1 -2
- package/dist/{project-validator.js → commands/pull-v3/project-validator.js} +4 -4
- package/dist/commands/pull-v3/targeted-typescript-placeholders.js +173 -0
- package/dist/commands/pull-v3/utils/component-registry.js +369 -0
- package/dist/commands/pull-v3/utils/component-tracker.js +165 -0
- package/dist/commands/pull-v3/utils/generator-utils.js +146 -0
- package/dist/commands/pull-v3/utils/model-provider-detector.js +44 -0
- package/dist/commands/push.js +326 -0
- package/dist/commands/status.js +89 -0
- package/dist/commands/update.js +97 -0
- package/dist/commands/whoami.js +38 -0
- package/dist/config.js +0 -1
- package/dist/env.js +30 -0
- package/dist/exports.js +3 -0
- package/dist/index.js +28 -196514
- package/dist/instrumentation.js +47 -0
- package/dist/types/agent.js +1 -0
- package/dist/types/tsx.d.d.ts +1 -0
- package/dist/utils/background-version-check.js +19 -0
- package/dist/utils/ci-environment.js +87 -0
- package/dist/utils/cli-pipeline.js +158 -0
- package/dist/utils/config.js +290 -0
- package/dist/utils/credentials.js +132 -0
- package/dist/utils/environment-loader.js +28 -0
- package/dist/utils/file-finder.js +62 -0
- package/dist/utils/json-comparator.js +185 -0
- package/dist/utils/json-comparison.js +232 -0
- package/dist/utils/mcp-runner.js +120 -0
- package/dist/utils/model-config.js +182 -0
- package/dist/utils/package-manager.js +58 -0
- package/dist/utils/profile-config.js +85 -0
- package/dist/utils/profiles/index.js +4 -0
- package/dist/utils/profiles/profile-manager.js +219 -0
- package/dist/utils/profiles/types.js +62 -0
- package/dist/utils/project-directory.js +33 -0
- package/dist/utils/project-loader.js +29 -0
- package/dist/utils/schema-introspection.js +44 -0
- package/dist/utils/templates.js +198 -0
- package/dist/utils/tsx-loader.js +27 -0
- package/dist/utils/url.js +26 -0
- package/dist/utils/version-check.js +79 -0
- package/package.json +9 -24
- package/dist/component-parser.js +0 -4
- package/dist/component-updater.js +0 -4
- package/dist/config2.js +0 -4
- package/dist/credential-stores.js +0 -4
- package/dist/environment-generator.js +0 -4
- package/dist/nodefs.js +0 -27
- package/dist/opfs-ahp.js +0 -368
- package/dist/project-loader.js +0 -4
- package/dist/tsx-loader.js +0 -4
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
import { DEFAULT_STYLE } from "./utils/generator-utils.js";
|
|
2
|
+
import { generateAgentFile } from "./components/agent-generator.js";
|
|
3
|
+
import { generateArtifactComponentFile } from "./components/artifact-component-generator.js";
|
|
4
|
+
import { generateContextConfigFile } from "./components/context-config-generator.js";
|
|
5
|
+
import { generateCredentialFile } from "./components/credential-generator.js";
|
|
6
|
+
import { generateDataComponentFile } from "./components/data-component-generator.js";
|
|
7
|
+
import { generateEnvironmentFile, generateEnvironmentIndexFile } from "./components/environment-generator.js";
|
|
8
|
+
import { generateExternalAgentFile } from "./components/external-agent-generator.js";
|
|
9
|
+
import { generateFunctionToolFile } from "./components/function-tool-generator.js";
|
|
10
|
+
import { generateMcpToolFile } from "./components/mcp-tool-generator.js";
|
|
11
|
+
import { generateProjectFile } from "./components/project-generator.js";
|
|
12
|
+
import { generateStatusComponentFile } from "./components/status-component-generator.js";
|
|
13
|
+
import { generateSubAgentFile } from "./components/sub-agent-generator.js";
|
|
14
|
+
import { ComponentRegistry, registerAllComponents } from "./utils/component-registry.js";
|
|
15
|
+
import { mkdirSync, writeFileSync } from "node:fs";
|
|
16
|
+
import { dirname, join } from "node:path";
|
|
17
|
+
import chalk from "chalk";
|
|
18
|
+
|
|
19
|
+
//#region src/commands/pull-v3/introspect-generator.ts
|
|
20
|
+
/**
|
|
21
|
+
* Introspect Generator - Complete project regeneration
|
|
22
|
+
*
|
|
23
|
+
* This module handles the --introspect mode which regenerates all files
|
|
24
|
+
* from scratch without any comparison or diffing logic.
|
|
25
|
+
*/
|
|
26
|
+
/**
|
|
27
|
+
* Helper function to ensure directory exists
|
|
28
|
+
*/
|
|
29
|
+
function ensureDir(filePath) {
|
|
30
|
+
mkdirSync(dirname(filePath), { recursive: true });
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Check if an agent is complete enough for code generation
|
|
34
|
+
* An agent needs a name, defaultSubAgentId, and at least one sub-agent
|
|
35
|
+
*/
|
|
36
|
+
function isAgentComplete(agentId, agentData) {
|
|
37
|
+
if (!agentData.name) return {
|
|
38
|
+
complete: false,
|
|
39
|
+
reason: "missing name"
|
|
40
|
+
};
|
|
41
|
+
if (!agentData.defaultSubAgentId) return {
|
|
42
|
+
complete: false,
|
|
43
|
+
reason: "missing defaultSubAgentId (no sub-agents configured)"
|
|
44
|
+
};
|
|
45
|
+
if (!agentData.subAgents || Object.keys(agentData.subAgents).length === 0) return {
|
|
46
|
+
complete: false,
|
|
47
|
+
reason: "no sub-agents defined"
|
|
48
|
+
};
|
|
49
|
+
return { complete: true };
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Generate all files from scratch using deterministic generation
|
|
53
|
+
*/
|
|
54
|
+
async function introspectGenerate(project, paths, environment, debug, options = {}) {
|
|
55
|
+
if (debug) console.log(chalk.gray("🔍 Regenerating all project files..."));
|
|
56
|
+
const generatedFiles = [];
|
|
57
|
+
const style = {
|
|
58
|
+
...DEFAULT_STYLE,
|
|
59
|
+
...options.codeStyle
|
|
60
|
+
};
|
|
61
|
+
const skippedAgents = [];
|
|
62
|
+
const registry = new ComponentRegistry();
|
|
63
|
+
try {
|
|
64
|
+
registerAllComponents(project, registry);
|
|
65
|
+
if (project.credentialReferences) for (const [credId, credData] of Object.entries(project.credentialReferences)) {
|
|
66
|
+
const credentialFile = join(paths.credentialsDir, `${credId}.ts`);
|
|
67
|
+
const credentialContent = generateCredentialFile(credId, credData, style);
|
|
68
|
+
ensureDir(credentialFile);
|
|
69
|
+
writeFileSync(credentialFile, credentialContent, "utf-8");
|
|
70
|
+
generatedFiles.push(credentialFile);
|
|
71
|
+
}
|
|
72
|
+
const envFile = join(paths.environmentsDir, `${environment}.env.ts`);
|
|
73
|
+
const envContent = generateEnvironmentFile(environment, {
|
|
74
|
+
name: `${environment} Environment`,
|
|
75
|
+
description: `Environment configuration for ${environment}`,
|
|
76
|
+
credentials: project.credentialReferences ? Object.keys(project.credentialReferences) : []
|
|
77
|
+
}, style, registry);
|
|
78
|
+
ensureDir(envFile);
|
|
79
|
+
writeFileSync(envFile, envContent, "utf-8");
|
|
80
|
+
generatedFiles.push(envFile);
|
|
81
|
+
const envIndexFile = join(paths.environmentsDir, "index.ts");
|
|
82
|
+
const envIndexContent = generateEnvironmentIndexFile([environment], style);
|
|
83
|
+
ensureDir(envIndexFile);
|
|
84
|
+
writeFileSync(envIndexFile, envIndexContent, "utf-8");
|
|
85
|
+
generatedFiles.push(envIndexFile);
|
|
86
|
+
const functionToolsGenerated = /* @__PURE__ */ new Set();
|
|
87
|
+
if (project.functionTools) for (const [toolId, toolData] of Object.entries(project.functionTools)) {
|
|
88
|
+
const functionId = toolData.functionId;
|
|
89
|
+
const funcData = functionId ? project.functions?.[functionId] : void 0;
|
|
90
|
+
const mergedData = {
|
|
91
|
+
name: toolData.name,
|
|
92
|
+
description: toolData.description,
|
|
93
|
+
inputSchema: funcData?.inputSchema,
|
|
94
|
+
executeCode: funcData?.executeCode,
|
|
95
|
+
execute: funcData?.executeCode,
|
|
96
|
+
dependencies: funcData?.dependencies
|
|
97
|
+
};
|
|
98
|
+
const functionFile = join(paths.toolsDir, "functions", `${toolId}.ts`);
|
|
99
|
+
const functionContent = generateFunctionToolFile(toolId, mergedData, style);
|
|
100
|
+
ensureDir(functionFile);
|
|
101
|
+
writeFileSync(functionFile, functionContent, "utf-8");
|
|
102
|
+
generatedFiles.push(functionFile);
|
|
103
|
+
functionToolsGenerated.add(toolId);
|
|
104
|
+
}
|
|
105
|
+
if (project.agents) for (const [agentId, agentData] of Object.entries(project.agents)) {
|
|
106
|
+
const agentFunctionTools = agentData.functionTools;
|
|
107
|
+
const agentFunctions = agentData.functions;
|
|
108
|
+
if (agentFunctionTools) for (const [toolId, toolData] of Object.entries(agentFunctionTools)) {
|
|
109
|
+
if (functionToolsGenerated.has(toolId)) continue;
|
|
110
|
+
const functionId = toolData.functionId;
|
|
111
|
+
const funcData = functionId ? agentFunctions?.[functionId] || project.functions?.[functionId] : void 0;
|
|
112
|
+
const mergedData = {
|
|
113
|
+
name: toolData.name,
|
|
114
|
+
description: toolData.description,
|
|
115
|
+
inputSchema: funcData?.inputSchema,
|
|
116
|
+
executeCode: funcData?.executeCode,
|
|
117
|
+
execute: funcData?.executeCode,
|
|
118
|
+
dependencies: funcData?.dependencies
|
|
119
|
+
};
|
|
120
|
+
const functionFile = join(paths.toolsDir, "functions", `${toolId}.ts`);
|
|
121
|
+
const functionContent = generateFunctionToolFile(toolId, mergedData, style);
|
|
122
|
+
ensureDir(functionFile);
|
|
123
|
+
writeFileSync(functionFile, functionContent, "utf-8");
|
|
124
|
+
generatedFiles.push(functionFile);
|
|
125
|
+
functionToolsGenerated.add(toolId);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
if (project.functions) {
|
|
129
|
+
for (const [funcId, funcData] of Object.entries(project.functions)) if (!functionToolsGenerated.has(funcId)) {
|
|
130
|
+
if (!(Object.values(project.functionTools || {}).some((ft) => ft.functionId === funcId) || Object.values(project.agents || {}).some((agent) => Object.values(agent.functionTools || {}).some((ft) => ft.functionId === funcId))) && debug) console.log(chalk.yellow(`⚠️ Skipping orphaned function '${funcId}' - no functionTool references it`));
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
if (project.tools) for (const [toolId, toolData] of Object.entries(project.tools)) {
|
|
134
|
+
const toolFile = join(paths.toolsDir, `${toolId}.ts`);
|
|
135
|
+
const toolContent = generateMcpToolFile(toolId, toolData, style, registry);
|
|
136
|
+
ensureDir(toolFile);
|
|
137
|
+
writeFileSync(toolFile, toolContent, "utf-8");
|
|
138
|
+
generatedFiles.push(toolFile);
|
|
139
|
+
}
|
|
140
|
+
if (project.dataComponents) for (const [dataId, dataData] of Object.entries(project.dataComponents)) {
|
|
141
|
+
const dataFile = join(paths.dataComponentsDir, `${dataId}.ts`);
|
|
142
|
+
const dataContent = generateDataComponentFile(dataId, dataData, style);
|
|
143
|
+
ensureDir(dataFile);
|
|
144
|
+
writeFileSync(dataFile, dataContent, "utf-8");
|
|
145
|
+
generatedFiles.push(dataFile);
|
|
146
|
+
}
|
|
147
|
+
if (project.artifactComponents) for (const [artifactId, artifactData] of Object.entries(project.artifactComponents)) {
|
|
148
|
+
const artifactFile = join(paths.artifactComponentsDir, `${artifactId}.ts`);
|
|
149
|
+
const artifactContent = generateArtifactComponentFile(artifactId, artifactData, style);
|
|
150
|
+
ensureDir(artifactFile);
|
|
151
|
+
writeFileSync(artifactFile, artifactContent, "utf-8");
|
|
152
|
+
generatedFiles.push(artifactFile);
|
|
153
|
+
}
|
|
154
|
+
const registeredStatusComponents = registry.getAllComponents().filter((c) => c.type === "statusComponents");
|
|
155
|
+
if (registeredStatusComponents.length > 0) for (const statusComp of registeredStatusComponents) {
|
|
156
|
+
const statusData = findStatusComponentData(project, statusComp.id);
|
|
157
|
+
if (statusData) {
|
|
158
|
+
const statusFile = join(paths.statusComponentsDir, `${statusComp.id}.ts`);
|
|
159
|
+
const statusContent = generateStatusComponentFile(statusComp.id, statusData, style);
|
|
160
|
+
ensureDir(statusFile);
|
|
161
|
+
writeFileSync(statusFile, statusContent, "utf-8");
|
|
162
|
+
generatedFiles.push(statusFile);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
if (project.externalAgents) for (const [extAgentId, extAgentData] of Object.entries(project.externalAgents)) {
|
|
166
|
+
const extAgentFile = join(paths.externalAgentsDir, `${extAgentId}.ts`);
|
|
167
|
+
const extAgentContent = generateExternalAgentFile(extAgentId, extAgentData, style, registry);
|
|
168
|
+
ensureDir(extAgentFile);
|
|
169
|
+
writeFileSync(extAgentFile, extAgentContent, "utf-8");
|
|
170
|
+
generatedFiles.push(extAgentFile);
|
|
171
|
+
}
|
|
172
|
+
const registeredContextConfigs = registry.getAllComponents().filter((c) => c.type === "contextConfigs");
|
|
173
|
+
if (registeredContextConfigs.length > 0) for (const contextComp of registeredContextConfigs) {
|
|
174
|
+
const contextData = findContextConfigData(project, contextComp.id);
|
|
175
|
+
if (contextData) {
|
|
176
|
+
const contextFile = join(paths.contextConfigsDir, `${contextComp.id}.ts`);
|
|
177
|
+
const contextContent = generateContextConfigFile(contextComp.id, contextData, style, registry);
|
|
178
|
+
ensureDir(contextFile);
|
|
179
|
+
writeFileSync(contextFile, contextContent, "utf-8");
|
|
180
|
+
generatedFiles.push(contextFile);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
const completeAgentIds = /* @__PURE__ */ new Set();
|
|
184
|
+
if (project.agents) for (const [agentId, agentData] of Object.entries(project.agents)) {
|
|
185
|
+
const completeness = isAgentComplete(agentId, agentData);
|
|
186
|
+
if (completeness.complete) completeAgentIds.add(agentId);
|
|
187
|
+
else {
|
|
188
|
+
skippedAgents.push({
|
|
189
|
+
id: agentId,
|
|
190
|
+
reason: completeness.reason || "incomplete"
|
|
191
|
+
});
|
|
192
|
+
if (debug) console.log(chalk.yellow(`⚠️ Skipping incomplete agent '${agentId}': ${completeness.reason}`));
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
if (project.agents && Object.keys(project.agents).length > 0) {
|
|
196
|
+
let totalSubAgents = 0;
|
|
197
|
+
for (const [agentId, agentData] of Object.entries(project.agents)) {
|
|
198
|
+
if (!completeAgentIds.has(agentId)) continue;
|
|
199
|
+
if (agentData.subAgents) for (const [subAgentId, subAgentData] of Object.entries(agentData.subAgents)) totalSubAgents++;
|
|
200
|
+
}
|
|
201
|
+
if (totalSubAgents > 0) for (const [agentId, agentData] of Object.entries(project.agents)) {
|
|
202
|
+
if (!completeAgentIds.has(agentId)) continue;
|
|
203
|
+
if (agentData.subAgents) {
|
|
204
|
+
const contextConfigData = agentData.contextConfig?.id ? findContextConfigData(project, agentData.contextConfig.id) : void 0;
|
|
205
|
+
for (const [subAgentId, subAgentData] of Object.entries(agentData.subAgents)) {
|
|
206
|
+
const subAgentFile = join(paths.agentsDir, "sub-agents", `${subAgentId}.ts`);
|
|
207
|
+
const subAgentContent = generateSubAgentFile(subAgentId, subAgentData, style, registry, agentId, contextConfigData, agentData.models || project.models);
|
|
208
|
+
ensureDir(subAgentFile);
|
|
209
|
+
writeFileSync(subAgentFile, subAgentContent, "utf-8");
|
|
210
|
+
generatedFiles.push(subAgentFile);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
if (project.agents) for (const [agentId, agentData] of Object.entries(project.agents)) {
|
|
216
|
+
if (!completeAgentIds.has(agentId)) continue;
|
|
217
|
+
const agentFile = join(paths.agentsDir, `${agentId}.ts`);
|
|
218
|
+
const agentContent = generateAgentFile(agentId, agentData, style, registry, agentData.contextConfig?.id ? findContextConfigData(project, agentData.contextConfig.id) : void 0, project.models);
|
|
219
|
+
ensureDir(agentFile);
|
|
220
|
+
writeFileSync(agentFile, agentContent, "utf-8");
|
|
221
|
+
generatedFiles.push(agentFile);
|
|
222
|
+
}
|
|
223
|
+
const projectDataForGenerator = {
|
|
224
|
+
...project,
|
|
225
|
+
agents: project.agents ? Object.keys(project.agents).filter((id) => completeAgentIds.has(id)) : [],
|
|
226
|
+
tools: project.tools ? Object.keys(project.tools) : [],
|
|
227
|
+
externalAgents: project.externalAgents ? Object.keys(project.externalAgents) : [],
|
|
228
|
+
dataComponents: project.dataComponents ? Object.keys(project.dataComponents) : [],
|
|
229
|
+
artifactComponents: project.artifactComponents ? Object.keys(project.artifactComponents) : [],
|
|
230
|
+
credentialReferences: project.credentialReferences ? Object.keys(project.credentialReferences) : []
|
|
231
|
+
};
|
|
232
|
+
const projectFile = join(paths.projectRoot, "index.ts");
|
|
233
|
+
const projectContent = generateProjectFile(project.id, projectDataForGenerator, style, registry);
|
|
234
|
+
ensureDir(projectFile);
|
|
235
|
+
writeFileSync(projectFile, projectContent, "utf-8");
|
|
236
|
+
generatedFiles.push(projectFile);
|
|
237
|
+
if (debug) console.log(chalk.green(`✅ Generated ${generatedFiles.length} files`));
|
|
238
|
+
if (skippedAgents.length > 0) {
|
|
239
|
+
console.log(chalk.yellow(`\n⚠️ Skipped ${skippedAgents.length} incomplete agent(s):`));
|
|
240
|
+
for (const { id, reason } of skippedAgents) console.log(chalk.yellow(` • ${id}: ${reason}`));
|
|
241
|
+
console.log(chalk.gray(" To fix: Add at least one sub-agent to each agent in the UI and set it as default."));
|
|
242
|
+
}
|
|
243
|
+
} catch (error) {
|
|
244
|
+
console.error(chalk.red("\n❌ Introspect regeneration failed:"));
|
|
245
|
+
console.error(chalk.red(` Error: ${error instanceof Error ? error.message : "Unknown error"}`));
|
|
246
|
+
throw error;
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
/**
|
|
250
|
+
* Find context config data by agent-based ID from project agents
|
|
251
|
+
*/
|
|
252
|
+
function findContextConfigData(project, contextId) {
|
|
253
|
+
if (project.agents) {
|
|
254
|
+
for (const [agentId, agentData] of Object.entries(project.agents)) if (agentData.contextConfig) {
|
|
255
|
+
if (agentData.contextConfig.id === contextId) return agentData.contextConfig;
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
/**
|
|
260
|
+
* Find status component data by ID from project agents
|
|
261
|
+
*/
|
|
262
|
+
function findStatusComponentData(project, statusId) {
|
|
263
|
+
if (project.agents) {
|
|
264
|
+
for (const [agentId, agentData] of Object.entries(project.agents)) if (agentData.statusUpdates && agentData.statusUpdates.statusComponents) for (const statusComp of agentData.statusUpdates.statusComponents) {
|
|
265
|
+
let compId;
|
|
266
|
+
if (typeof statusComp === "string") compId = statusComp;
|
|
267
|
+
else if (typeof statusComp === "object" && statusComp) compId = statusComp.type;
|
|
268
|
+
if (compId === statusId) return typeof statusComp === "string" ? {
|
|
269
|
+
id: statusId,
|
|
270
|
+
type: statusId,
|
|
271
|
+
description: `Status component for ${statusId}`
|
|
272
|
+
} : statusComp;
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
//#endregion
|
|
278
|
+
export { introspectGenerate };
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
import { createTargetedTypeScriptPlaceholders, restoreTargetedTypeScriptPlaceholders } from "./targeted-typescript-placeholders.js";
|
|
2
|
+
import { getAvailableModel } from "./utils/model-provider-detector.js";
|
|
3
|
+
import chalk from "chalk";
|
|
4
|
+
import { generateText } from "ai";
|
|
5
|
+
|
|
6
|
+
//#region src/commands/pull-v3/llm-content-merger.ts
|
|
7
|
+
/**
|
|
8
|
+
* LLM Content Merger - Intelligently merge old and new component content
|
|
9
|
+
*
|
|
10
|
+
* Uses an LLM to selectively update modified components while preserving:
|
|
11
|
+
* - Original file structure and formatting
|
|
12
|
+
* - Code style consistency
|
|
13
|
+
* - Better zod schemas
|
|
14
|
+
* - Imports and other non-component code
|
|
15
|
+
*/
|
|
16
|
+
/**
|
|
17
|
+
* Strip code fences from LLM response if present
|
|
18
|
+
*/
|
|
19
|
+
function stripCodeFences(content) {
|
|
20
|
+
content = content.replace(/^```(?:typescript|ts|javascript|js)?\s*\n?/i, "");
|
|
21
|
+
content = content.replace(/\n?```\s*$/i, "");
|
|
22
|
+
return content;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Estimate tokens in text (rough approximation: 1 token ≈ 4 characters)
|
|
26
|
+
*/
|
|
27
|
+
function estimateTokens(text) {
|
|
28
|
+
return Math.ceil(text.length / 4);
|
|
29
|
+
}
|
|
30
|
+
function calculateCostEstimate(promptTokens, completionTokens) {
|
|
31
|
+
return promptTokens / 1e6 * 3 + completionTokens / 1e6 * 15;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Use LLM to intelligently merge old content with new component definitions
|
|
35
|
+
*/
|
|
36
|
+
async function mergeComponentsWithLLM(request) {
|
|
37
|
+
const { oldContent, newContent, modifiedComponents, filePath, newComponents, componentsToExport } = request;
|
|
38
|
+
const componentList = modifiedComponents.map((c) => `- ${c.componentType}:${c.componentId}`).join("\n");
|
|
39
|
+
const newComponentsList = newComponents && newComponents.length > 0 ? newComponents.map((c) => {
|
|
40
|
+
const currentDir = filePath.replace(/^.*\/([^/]+\/[^/]+)$/, "$1").replace(/\/[^/]+$/, "");
|
|
41
|
+
let componentPath = c.filePath;
|
|
42
|
+
if (componentPath.includes(".temp-")) componentPath = componentPath.replace(/^.*\.temp-[^/]+\//, "");
|
|
43
|
+
componentPath = componentPath.replace(/\.ts$/, "");
|
|
44
|
+
const importPath = calculateRelativeImportPath(currentDir, componentPath);
|
|
45
|
+
const variableName = c.componentId.replace(/-([a-z])/g, (_, letter) => letter.toUpperCase());
|
|
46
|
+
return `- ${c.componentType}:${c.componentId} (import as: import { ${variableName} } from '${importPath}')`;
|
|
47
|
+
}).join("\n") : "";
|
|
48
|
+
function calculateRelativeImportPath(fromDir, toPath) {
|
|
49
|
+
const fromParts = fromDir.split("/");
|
|
50
|
+
const toParts = toPath.split("/");
|
|
51
|
+
let commonLength = 0;
|
|
52
|
+
while (commonLength < fromParts.length && commonLength < toParts.length - 1 && fromParts[commonLength] === toParts[commonLength]) commonLength++;
|
|
53
|
+
const upLevels = fromParts.length - commonLength;
|
|
54
|
+
let relativePath = "";
|
|
55
|
+
for (let i = 0; i < upLevels; i++) relativePath += "../";
|
|
56
|
+
relativePath += toParts.slice(commonLength).join("/");
|
|
57
|
+
return relativePath.startsWith("../") ? relativePath : "./" + relativePath;
|
|
58
|
+
}
|
|
59
|
+
const componentsToExportList = componentsToExport && componentsToExport.length > 0 ? componentsToExport.map((c) => `- ${c.variableName} (${c.reason})`).join("\n") : "";
|
|
60
|
+
const prompt = `You are a TypeScript code expert tasked with intelligently merging component updates.
|
|
61
|
+
|
|
62
|
+
## Task
|
|
63
|
+
Merge the OLD file content with NEW component definitions, preserving the original file structure while updating only the modified components.
|
|
64
|
+
|
|
65
|
+
## Modified Components to Update
|
|
66
|
+
${componentList}
|
|
67
|
+
${newComponentsList ? `
|
|
68
|
+
## New Components Available (can be imported)
|
|
69
|
+
${newComponentsList}
|
|
70
|
+
` : ""}${componentsToExportList ? `
|
|
71
|
+
## Components That Need To Be Exported
|
|
72
|
+
The following existing components are referenced by new components and must be exported:
|
|
73
|
+
${componentsToExportList}
|
|
74
|
+
|
|
75
|
+
Ensure these components have export statements (convert \`const\` to \`export const\`, or add \`export\` to existing declarations).
|
|
76
|
+
` : ""}
|
|
77
|
+
## Instructions
|
|
78
|
+
0. **Please ensure you focus changes to minimize git diff size.** We want a clean git history.
|
|
79
|
+
1. **Preserve original structure**: Keep imports, exports, comments, and overall file organization
|
|
80
|
+
2. **Update only modified components**: Replace the specified components with their new versions
|
|
81
|
+
3. **Maintain code style**: Match the original formatting, indentation, and style
|
|
82
|
+
4. **Improve schemas**: Use better zod schemas from the new content where applicable. E.g. if the new content uses something like z.union([z.null(), z.string()]), use z.string().nullable() instead.
|
|
83
|
+
5. **Keep variable names**: Preserve original variable names and declarations
|
|
84
|
+
6. **Preserve non-component code**: Keep any non-component logic, comments, or utilities
|
|
85
|
+
7. **Smart import handling**:
|
|
86
|
+
- Please leave all imports at the top of the file. Don't use .js imports, use .ts imports instead. (import example from './example')
|
|
87
|
+
- Preserve all imports from the original content that are not modified, if a component is modified or no longer exists, remove the import.
|
|
88
|
+
- For NEW components listed above, add proper import statements
|
|
89
|
+
- For components that exist in the same file (modified components), DO NOT add import statements
|
|
90
|
+
- Remove any incorrect imports from the NEW component definitions that reference same-file components
|
|
91
|
+
- Use relative paths for imports (e.g., './example' not './example.js')
|
|
92
|
+
8. **Format JavaScript functions for maximum readability**:
|
|
93
|
+
- When you see compressed/minified function code like \`async({params})=>{...code...}\`, expand and prettify it
|
|
94
|
+
- Add proper line breaks, spacing, and indentation to make the function readable
|
|
95
|
+
- Ensure all braces \`{}\`, parentheses \`()\`, and syntax are properly balanced and valid
|
|
96
|
+
- Format the function code following TypeScript/JavaScript best practices
|
|
97
|
+
- Make sure the final code is compilable and syntactically correct
|
|
98
|
+
- Example: \`async({a,b})=>{return a+b}\` should become:
|
|
99
|
+
\`\`\`
|
|
100
|
+
async ({ a, b }) => {
|
|
101
|
+
return a + b;
|
|
102
|
+
}
|
|
103
|
+
\`\`\`
|
|
104
|
+
|
|
105
|
+
## OLD File to be updated with new component definitions:
|
|
106
|
+
\`\`\`typescript
|
|
107
|
+
${oldContent}
|
|
108
|
+
\`\`\`
|
|
109
|
+
|
|
110
|
+
## NEW Component Definitions to replace/add to the old file:
|
|
111
|
+
\`\`\`typescript
|
|
112
|
+
${newContent}
|
|
113
|
+
\`\`\`
|
|
114
|
+
|
|
115
|
+
## Output
|
|
116
|
+
Provide the merged TypeScript file that:
|
|
117
|
+
- Keeps the original file structure
|
|
118
|
+
- Updates ONLY the modified components listed above
|
|
119
|
+
- Maintains consistent code style
|
|
120
|
+
- Uses improved schemas where beneficial
|
|
121
|
+
- Preserves all imports, exports, and other code that are necessary to keep the file working.
|
|
122
|
+
- **Formats all function code beautifully with proper spacing, line breaks, and indentation**
|
|
123
|
+
- **Ensures all syntax is valid and compilable TypeScript/JavaScript**
|
|
124
|
+
- Start the code immidiately with the first line of the file, skip any backticks or other formatting announcing that it is a code block or typescript file.
|
|
125
|
+
- Please follow biomes.dev code style.
|
|
126
|
+
- Please remember the NEW component definitions are to replace/add to the old file.
|
|
127
|
+
|
|
128
|
+
Return only the merged TypeScript code without any explanation or markdown formatting.`;
|
|
129
|
+
try {
|
|
130
|
+
const oldPlaceholders = createTargetedTypeScriptPlaceholders(oldContent);
|
|
131
|
+
const newPlaceholders = createTargetedTypeScriptPlaceholders(newContent);
|
|
132
|
+
const processedPrompt = prompt.replace(oldContent, oldPlaceholders.processedContent).replace(newContent, newPlaceholders.processedContent);
|
|
133
|
+
const estimatedPromptTokens = estimateTokens(processedPrompt);
|
|
134
|
+
let mergedContent = (await generateText({
|
|
135
|
+
model: getAvailableModel(),
|
|
136
|
+
prompt: processedPrompt
|
|
137
|
+
})).text.trim();
|
|
138
|
+
mergedContent = stripCodeFences(mergedContent);
|
|
139
|
+
const estimatedCompletionTokens = estimateTokens(mergedContent);
|
|
140
|
+
const totalTokens = estimatedPromptTokens + estimatedCompletionTokens;
|
|
141
|
+
const estimatedCost = calculateCostEstimate(estimatedPromptTokens, estimatedCompletionTokens);
|
|
142
|
+
console.log(chalk.gray(` 💰 LLM usage: ~${totalTokens.toLocaleString()} tokens ($${estimatedCost.toFixed(4)})`));
|
|
143
|
+
const allReplacements = {
|
|
144
|
+
...oldPlaceholders.replacements,
|
|
145
|
+
...newPlaceholders.replacements
|
|
146
|
+
};
|
|
147
|
+
if (Object.keys(allReplacements).length > 0) mergedContent = restoreTargetedTypeScriptPlaceholders(mergedContent, allReplacements);
|
|
148
|
+
const changes = modifiedComponents.map((c) => `Updated ${c.componentType}:${c.componentId}`);
|
|
149
|
+
return {
|
|
150
|
+
mergedContent,
|
|
151
|
+
changes,
|
|
152
|
+
success: true,
|
|
153
|
+
usage: {
|
|
154
|
+
promptTokens: estimatedPromptTokens,
|
|
155
|
+
completionTokens: estimatedCompletionTokens,
|
|
156
|
+
totalTokens,
|
|
157
|
+
estimatedCost
|
|
158
|
+
}
|
|
159
|
+
};
|
|
160
|
+
} catch (error) {
|
|
161
|
+
return {
|
|
162
|
+
mergedContent: oldContent,
|
|
163
|
+
changes: [],
|
|
164
|
+
success: false,
|
|
165
|
+
error: error instanceof Error ? error.message : String(error)
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* Preview the merge result by showing a diff-like summary
|
|
171
|
+
*/
|
|
172
|
+
function previewMergeResult(oldContent, mergedContent, changes) {
|
|
173
|
+
console.log(chalk.cyan("\n🔍 Merge Preview:"));
|
|
174
|
+
console.log(chalk.gray(` Old content: ${oldContent.length} characters`));
|
|
175
|
+
console.log(chalk.gray(` New content: ${mergedContent.length} characters`));
|
|
176
|
+
console.log(chalk.yellow(` Changes applied:`));
|
|
177
|
+
changes.forEach((change) => {
|
|
178
|
+
console.log(chalk.gray(` • ${change}`));
|
|
179
|
+
});
|
|
180
|
+
const lines = mergedContent.split("\n");
|
|
181
|
+
const preview = lines.slice(0, 10).join("\n");
|
|
182
|
+
console.log(chalk.cyan("\n📄 Merged content preview (first 10 lines):"));
|
|
183
|
+
console.log(chalk.gray(" ┌─────"));
|
|
184
|
+
preview.split("\n").forEach((line, i) => {
|
|
185
|
+
console.log(chalk.gray(` │ ${String(i + 1).padStart(2, " ")}: ${line}`));
|
|
186
|
+
});
|
|
187
|
+
if (lines.length > 10) console.log(chalk.gray(` │ ... (${lines.length - 10} more lines)`));
|
|
188
|
+
console.log(chalk.gray(" └─────"));
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
//#endregion
|
|
192
|
+
export { mergeComponentsWithLLM, previewMergeResult };
|
|
@@ -1,5 +1,16 @@
|
|
|
1
|
-
|
|
2
|
-
import {
|
|
1
|
+
import { toCamelCase } from "./utils/generator-utils.js";
|
|
2
|
+
import { generateAgentFile } from "./components/agent-generator.js";
|
|
3
|
+
import { generateArtifactComponentFile } from "./components/artifact-component-generator.js";
|
|
4
|
+
import { generateContextConfigFile } from "./components/context-config-generator.js";
|
|
5
|
+
import { generateCredentialFile } from "./components/credential-generator.js";
|
|
6
|
+
import { generateDataComponentFile } from "./components/data-component-generator.js";
|
|
7
|
+
import { generateEnvironmentFile } from "./components/environment-generator.js";
|
|
8
|
+
import { generateExternalAgentFile } from "./components/external-agent-generator.js";
|
|
9
|
+
import { generateFunctionToolFile } from "./components/function-tool-generator.js";
|
|
10
|
+
import { generateMcpToolFile } from "./components/mcp-tool-generator.js";
|
|
11
|
+
import { generateStatusComponentFile } from "./components/status-component-generator.js";
|
|
12
|
+
import { generateSubAgentFile } from "./components/sub-agent-generator.js";
|
|
13
|
+
import { findSubAgentWithParent } from "./utils/component-registry.js";
|
|
3
14
|
import { existsSync, mkdirSync, writeFileSync } from "node:fs";
|
|
4
15
|
import { dirname, join } from "node:path";
|
|
5
16
|
import chalk from "chalk";
|
|
@@ -218,7 +229,7 @@ async function createNewComponents(comparison, remoteProject, localRegistry, pat
|
|
|
218
229
|
const createdEnvironments = results.filter((r) => r.success && r.componentType === "environments");
|
|
219
230
|
if (createdEnvironments.length > 0) try {
|
|
220
231
|
console.log(chalk.cyan("📝 Generating environments index file..."));
|
|
221
|
-
const { generateEnvironmentIndexFile } = await import("./environment-generator.js");
|
|
232
|
+
const { generateEnvironmentIndexFile } = await import("./components/environment-generator.js");
|
|
222
233
|
const indexContent = generateEnvironmentIndexFile(createdEnvironments.map((r) => r.componentId), {
|
|
223
234
|
quotes: "single",
|
|
224
235
|
indentation: " ",
|