@inkeep/agents-cli 0.41.2 → 0.42.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.
Files changed (41) hide show
  1. package/README.md +13 -20
  2. package/dist/api.js +8 -8
  3. package/dist/commands/init.js +19 -39
  4. package/dist/commands/list-agents.js +1 -1
  5. package/dist/commands/login.js +3 -3
  6. package/dist/commands/profile.js +7 -26
  7. package/dist/commands/pull-v3/component-parser.js +0 -2
  8. package/dist/commands/pull-v3/component-updater.js +122 -64
  9. package/dist/commands/pull-v3/components/agent-generator.js +17 -3
  10. package/dist/commands/pull-v3/components/artifact-component-generator.js +7 -7
  11. package/dist/commands/pull-v3/components/context-config-generator.js +6 -6
  12. package/dist/commands/pull-v3/components/credential-generator.js +4 -4
  13. package/dist/commands/pull-v3/components/data-component-generator.js +4 -4
  14. package/dist/commands/pull-v3/components/environment-generator.js +10 -7
  15. package/dist/commands/pull-v3/components/function-tool-generator.js +5 -7
  16. package/dist/commands/pull-v3/components/mcp-tool-generator.js +5 -5
  17. package/dist/commands/pull-v3/components/project-generator.js +4 -4
  18. package/dist/commands/pull-v3/components/status-component-generator.js +6 -6
  19. package/dist/commands/pull-v3/components/trigger-generator.js +114 -0
  20. package/dist/commands/pull-v3/index.js +25 -30
  21. package/dist/commands/pull-v3/introspect-generator.js +15 -7
  22. package/dist/commands/pull-v3/llm-content-merger.js +1 -1
  23. package/dist/commands/pull-v3/new-component-generator.js +5 -16
  24. package/dist/commands/pull-v3/project-comparator.js +49 -49
  25. package/dist/commands/pull-v3/project-validator.js +5 -4
  26. package/dist/commands/pull-v3/targeted-typescript-placeholders.js +2 -2
  27. package/dist/commands/pull-v3/utils/component-registry.js +9 -7
  28. package/dist/commands/pull-v3/utils/component-tracker.js +1 -1
  29. package/dist/commands/pull-v3/utils/generator-utils.js +2 -2
  30. package/dist/commands/push.js +9 -9
  31. package/dist/commands/status.js +4 -8
  32. package/dist/index.js +2 -2
  33. package/dist/utils/ci-environment.js +4 -6
  34. package/dist/utils/cli-pipeline.js +8 -12
  35. package/dist/utils/config.js +17 -25
  36. package/dist/utils/json-comparison.js +3 -3
  37. package/dist/utils/profile-config.js +4 -6
  38. package/dist/utils/profiles/profile-manager.js +1 -1
  39. package/dist/utils/profiles/types.js +6 -9
  40. package/dist/utils/templates.js +3 -5
  41. package/package.json +7 -6
@@ -4,10 +4,13 @@ const DEFAULT_STYLE = {
4
4
  semicolons: true,
5
5
  indentation: " "
6
6
  };
7
+ /**
8
+ * Utility functions
9
+ */
7
10
  function formatString(str, quote = "'", multiline = false) {
8
11
  if (!str) return `${quote}${quote}`;
9
12
  if (multiline && (str.includes("\n") || str.length > 80)) return `\`${str.replace(/`/g, "\\`")}\``;
10
- return `${quote}${str.replace(new RegExp(quote, "g"), "\\" + quote)}${quote}`;
13
+ return `${quote}${str.replace(new RegExp(quote, "g"), `\\${quote}`)}${quote}`;
11
14
  }
12
15
  /**
13
16
  * Format credentials object for environment settings
@@ -55,7 +58,7 @@ function formatCredentialsObject(credentials, style, indentLevel) {
55
58
  function generateEnvironmentSettingsDefinition(environmentName, environmentData, style = DEFAULT_STYLE) {
56
59
  if (!environmentName || typeof environmentName !== "string") throw new Error("environmentName is required and must be a string");
57
60
  if (!environmentData || typeof environmentData !== "object") throw new Error(`environmentData is required for environment '${environmentName}'`);
58
- const { quotes, semicolons, indentation } = style;
61
+ const { semicolons, indentation } = style;
59
62
  const semi = semicolons ? ";" : "";
60
63
  const lines = [];
61
64
  lines.push(`export const ${environmentName} = registerEnvironmentSettings({`);
@@ -71,7 +74,7 @@ function generateEnvironmentSettingsDefinition(environmentName, environmentData,
71
74
  * Generate Main Environment Settings Index (index.ts)
72
75
  */
73
76
  function generateEnvironmentIndexDefinition(environments, style = DEFAULT_STYLE) {
74
- const { quotes, semicolons, indentation } = style;
77
+ const { semicolons, indentation } = style;
75
78
  const semi = semicolons ? ";" : "";
76
79
  const lines = [];
77
80
  lines.push(`export const envSettings = createEnvironmentSettings({`);
@@ -83,7 +86,7 @@ function generateEnvironmentIndexDefinition(environments, style = DEFAULT_STYLE)
83
86
  /**
84
87
  * Generate imports for individual environment settings file
85
88
  */
86
- function generateEnvironmentSettingsImports(environmentName, environmentData, style = DEFAULT_STYLE) {
89
+ function generateEnvironmentSettingsImports(environmentData, style = DEFAULT_STYLE) {
87
90
  const { quotes, semicolons } = style;
88
91
  const q = quotes === "single" ? "'" : "\"";
89
92
  const semi = semicolons ? ";" : "";
@@ -112,9 +115,9 @@ function generateEnvironmentIndexImports(environments, style = DEFAULT_STYLE) {
112
115
  * Generate complete environment settings file
113
116
  */
114
117
  function generateEnvironmentSettingsFile(environmentName, environmentData, style = DEFAULT_STYLE) {
115
- const imports = generateEnvironmentSettingsImports(environmentName, environmentData, style);
118
+ const imports = generateEnvironmentSettingsImports(environmentData, style);
116
119
  const definition = generateEnvironmentSettingsDefinition(environmentName, environmentData, style);
117
- return imports.join("\n") + "\n\n" + definition + "\n";
120
+ return `${imports.join("\n")}\n\n${definition}\n`;
118
121
  }
119
122
  /**
120
123
  * Generate complete environment index file
@@ -122,7 +125,7 @@ function generateEnvironmentSettingsFile(environmentName, environmentData, style
122
125
  function generateEnvironmentIndexFile(environments, style = DEFAULT_STYLE) {
123
126
  const imports = generateEnvironmentIndexImports(environments, style);
124
127
  const definition = generateEnvironmentIndexDefinition(environments, style);
125
- return imports.join("\n") + "\n\n" + definition + "\n";
128
+ return `${imports.join("\n")}\n\n${definition}\n`;
126
129
  }
127
130
  /**
128
131
  * Generate environment file (alias for generateEnvironmentSettingsFile)
@@ -1,5 +1,3 @@
1
- import { jsonSchemaToZod } from "json-schema-to-zod";
2
-
3
1
  //#region src/commands/pull-v3/components/function-tool-generator.ts
4
2
  const DEFAULT_STYLE = {
5
3
  quotes: "single",
@@ -15,7 +13,7 @@ function toCamelCase(str) {
15
13
  function formatString(str, quote = "'", multiline = false) {
16
14
  if (!str) return `${quote}${quote}`;
17
15
  if (multiline && (str.includes("\n") || str.length > 80)) return `\`${str.replace(/`/g, "\\`")}\``;
18
- return `${quote}${str.replace(new RegExp(quote, "g"), "\\" + quote)}${quote}`;
16
+ return `${quote}${str.replace(new RegExp(quote, "g"), `\\${quote}`)}${quote}`;
19
17
  }
20
18
  /**
21
19
  * Format JavaScript function code with proper indentation
@@ -54,7 +52,7 @@ function generateFunctionToolDefinition(toolId, toolData, style = DEFAULT_STYLE)
54
52
  if (index === 0) return `${indentation}inputSchema: ${line}`;
55
53
  return `${indentation}${line}`;
56
54
  }).join("\n");
57
- lines.push(formattedSchema + ",");
55
+ lines.push(`${formattedSchema},`);
58
56
  }
59
57
  const executeCode = toolData.executeCode || toolData.execute;
60
58
  if (executeCode) {
@@ -73,7 +71,7 @@ function generateFunctionToolDefinition(toolId, toolData, style = DEFAULT_STYLE)
73
71
  /**
74
72
  * Generate imports needed for a function tool file
75
73
  */
76
- function generateFunctionToolImports(toolId, toolData, style = DEFAULT_STYLE) {
74
+ function generateFunctionToolImports(style = DEFAULT_STYLE) {
77
75
  const { quotes, semicolons } = style;
78
76
  const q = quotes === "single" ? "'" : "\"";
79
77
  const semi = semicolons ? ";" : "";
@@ -85,9 +83,9 @@ function generateFunctionToolImports(toolId, toolData, style = DEFAULT_STYLE) {
85
83
  * Generate complete function tool file (imports + definition)
86
84
  */
87
85
  function generateFunctionToolFile(toolId, toolData, style = DEFAULT_STYLE) {
88
- const imports = generateFunctionToolImports(toolId, toolData, style);
86
+ const imports = generateFunctionToolImports(style);
89
87
  const definition = generateFunctionToolDefinition(toolId, toolData, style);
90
- return imports.join("\n") + "\n\n" + definition + "\n";
88
+ return `${imports.join("\n")}\n\n${definition}\n`;
91
89
  }
92
90
 
93
91
  //#endregion
@@ -13,7 +13,7 @@ function toCamelCase(str) {
13
13
  function formatString(str, quote = "'", multiline = false) {
14
14
  if (!str) return `${quote}${quote}`;
15
15
  if (multiline && (str.includes("\n") || str.length > 80)) return `\`${str.replace(/`/g, "\\`")}\``;
16
- return `${quote}${str.replace(new RegExp(quote, "g"), "\\" + quote)}${quote}`;
16
+ return `${quote}${str.replace(new RegExp(quote, "g"), `\\${quote}`)}${quote}`;
17
17
  }
18
18
  /**
19
19
  * Generate MCP Tool Definition using mcpTool() builder function
@@ -47,7 +47,7 @@ function generateMcpToolDefinition(toolId, toolData, style = DEFAULT_STYLE, regi
47
47
  if (index === 0) return `${indentation}headers: ${line}`;
48
48
  return `${indentation}${line}`;
49
49
  }).join("\n");
50
- lines.push(formattedHeaders + ",");
50
+ lines.push(`${formattedHeaders},`);
51
51
  }
52
52
  if (toolData.credential) if (typeof toolData.credential === "object") {
53
53
  const credentialStr = JSON.stringify(toolData.credential);
@@ -64,7 +64,7 @@ function generateMcpToolDefinition(toolId, toolData, style = DEFAULT_STYLE, regi
64
64
  /**
65
65
  * Generate imports needed for an MCP tool file
66
66
  */
67
- function generateMcpToolImports(toolId, toolData, style = DEFAULT_STYLE) {
67
+ function generateMcpToolImports(toolData, style = DEFAULT_STYLE) {
68
68
  const { quotes, semicolons } = style;
69
69
  const q = quotes === "single" ? "'" : "\"";
70
70
  const semi = semicolons ? ";" : "";
@@ -77,9 +77,9 @@ function generateMcpToolImports(toolId, toolData, style = DEFAULT_STYLE) {
77
77
  * Generate complete MCP tool file (imports + definition)
78
78
  */
79
79
  function generateMcpToolFile(toolId, toolData, style = DEFAULT_STYLE, registry) {
80
- const imports = generateMcpToolImports(toolId, toolData, style);
80
+ const imports = generateMcpToolImports(toolData, style);
81
81
  const definition = generateMcpToolDefinition(toolId, toolData, style, registry);
82
- return imports.join("\n") + "\n\n" + definition + "\n";
82
+ return `${imports.join("\n")}\n\n${definition}\n`;
83
83
  }
84
84
 
85
85
  //#endregion
@@ -62,7 +62,7 @@ function generateProjectDefinition(projectId, projectData, style = DEFAULT_STYLE
62
62
  /**
63
63
  * Generate imports needed for a project file
64
64
  */
65
- function generateProjectImports(projectId, projectData, style = DEFAULT_STYLE, registry) {
65
+ function generateProjectImports(projectData, style = DEFAULT_STYLE, registry) {
66
66
  const imports = [];
67
67
  imports.push(generateImport(["project"], "@inkeep/agents-sdk", style));
68
68
  if (registry) {
@@ -83,8 +83,8 @@ function generateProjectImports(projectId, projectData, style = DEFAULT_STYLE, r
83
83
  else if (typeof projectData.tools === "object") toolIds = Object.keys(projectData.tools);
84
84
  for (const toolId of toolIds) {
85
85
  let componentType = "tools";
86
- if (registry && registry.get(toolId, "functionTools")) componentType = "functionTools";
87
- else if (registry && registry.get(toolId, "tools")) componentType = "tools";
86
+ if (registry?.get(toolId, "functionTools")) componentType = "functionTools";
87
+ else if (registry?.get(toolId, "tools")) componentType = "tools";
88
88
  referencedComponents.push({
89
89
  id: toolId,
90
90
  type: componentType
@@ -138,7 +138,7 @@ function generateProjectImports(projectId, projectData, style = DEFAULT_STYLE, r
138
138
  * Generate complete project file (imports + definition)
139
139
  */
140
140
  function generateProjectFile(projectId, projectData, style = DEFAULT_STYLE, registry) {
141
- return generateFileContent(generateProjectImports(projectId, projectData, style, registry), [generateProjectDefinition(projectId, projectData, style, registry)]);
141
+ return generateFileContent(generateProjectImports(projectData, style, registry), [generateProjectDefinition(projectId, projectData, style, registry)]);
142
142
  }
143
143
 
144
144
  //#endregion
@@ -21,7 +21,7 @@ function toCamelCase(str) {
21
21
  function formatString(str, quote = "'", multiline = false) {
22
22
  if (!str) return `${quote}${quote}`;
23
23
  if (multiline && (str.includes("\n") || str.length > 80)) return `\`${str.replace(/`/g, "\\`")}\``;
24
- return `${quote}${str.replace(new RegExp(quote, "g"), "\\" + quote)}${quote}`;
24
+ return `${quote}${str.replace(new RegExp(quote, "g"), `\\${quote}`)}${quote}`;
25
25
  }
26
26
  /**
27
27
  * Convert JSON Schema to Zod schema using existing utility
@@ -58,9 +58,9 @@ function generateStatusComponentDefinition(componentId, componentData, style = D
58
58
  const schemaLines = zodSchema.split("\n");
59
59
  lines.push(`${indentation}detailsSchema: ${schemaLines[0]}`);
60
60
  schemaLines.slice(1, -1).forEach((line) => {
61
- lines[lines.length - 1] += "\n" + indentation + line;
61
+ lines[lines.length - 1] += `\n${indentation}${line}`;
62
62
  });
63
- lines[lines.length - 1] += "\n" + indentation + schemaLines[schemaLines.length - 1] + ",";
63
+ lines[lines.length - 1] += `\n${indentation}${schemaLines[schemaLines.length - 1]},`;
64
64
  } else lines.push(`${indentation}detailsSchema: ${zodSchema},`);
65
65
  }
66
66
  if (lines.length > 0 && lines[lines.length - 1].endsWith(",")) lines[lines.length - 1] = lines[lines.length - 1].slice(0, -1);
@@ -70,7 +70,7 @@ function generateStatusComponentDefinition(componentId, componentData, style = D
70
70
  /**
71
71
  * Generate imports needed for a status component file
72
72
  */
73
- function generateStatusComponentImports(componentId, componentData, style = DEFAULT_STYLE) {
73
+ function generateStatusComponentImports(componentData, style = DEFAULT_STYLE) {
74
74
  const { quotes, semicolons } = style;
75
75
  const q = quotes === "single" ? "'" : "\"";
76
76
  const semi = semicolons ? ";" : "";
@@ -83,9 +83,9 @@ function generateStatusComponentImports(componentId, componentData, style = DEFA
83
83
  * Generate complete status component file (imports + definition)
84
84
  */
85
85
  function generateStatusComponentFile(componentId, componentData, style = DEFAULT_STYLE) {
86
- const imports = generateStatusComponentImports(componentId, componentData, style);
86
+ const imports = generateStatusComponentImports(componentData, style);
87
87
  const definition = generateStatusComponentDefinition(componentId, componentData, style);
88
- return imports.join("\n") + "\n\n" + definition + "\n";
88
+ return `${imports.join("\n")}\n\n${definition}\n`;
89
89
  }
90
90
 
91
91
  //#endregion
@@ -0,0 +1,114 @@
1
+ import { DEFAULT_STYLE, formatString, generateFileContent, generateImport, toCamelCase } from "../utils/generator-utils.js";
2
+
3
+ //#region src/commands/pull-v3/components/trigger-generator.ts
4
+ /**
5
+ * Trigger Generator - Generate trigger definitions
6
+ *
7
+ * Generates triggers using the Trigger class from @inkeep/agents-sdk
8
+ * Triggers are webhooks that can invoke agent conversations
9
+ */
10
+ /**
11
+ * Format authentication configuration
12
+ * New format uses headers array: { headers: [{ name, valueHash, valuePrefix }] }
13
+ * We generate code that uses environment variables for the secret values
14
+ */
15
+ function formatAuthentication(auth, style, indentLevel) {
16
+ if (!auth) return "";
17
+ const { indentation, quotes } = style;
18
+ const q = quotes === "single" ? "'" : "\"";
19
+ const indent = indentation.repeat(indentLevel);
20
+ const innerIndent = indentation.repeat(indentLevel + 1);
21
+ const headerIndent = indentation.repeat(indentLevel + 2);
22
+ const lines = [];
23
+ if (auth.headers && Array.isArray(auth.headers) && auth.headers.length > 0) {
24
+ lines.push(`${indent}authentication: {`);
25
+ lines.push(`${innerIndent}headers: [`);
26
+ for (const header of auth.headers) {
27
+ const envVarName = `TRIGGER_AUTH_${header.name.toUpperCase().replace(/[^A-Z0-9]/g, "_")}`;
28
+ lines.push(`${headerIndent}{`);
29
+ lines.push(`${headerIndent}${indentation}name: ${q}${header.name}${q},`);
30
+ lines.push(`${headerIndent}${indentation}value: process.env.${envVarName} || ${q}${q},`);
31
+ lines.push(`${headerIndent}},`);
32
+ }
33
+ lines.push(`${innerIndent}],`);
34
+ lines.push(`${indent}},`);
35
+ }
36
+ return lines.join("\n");
37
+ }
38
+ /**
39
+ * Format output transform configuration
40
+ */
41
+ function formatOutputTransform(transform, style, indentLevel) {
42
+ if (!transform) return "";
43
+ const { indentation } = style;
44
+ const indent = indentation.repeat(indentLevel);
45
+ const innerIndent = indentation.repeat(indentLevel + 1);
46
+ const lines = [];
47
+ lines.push(`${indent}outputTransform: {`);
48
+ if (transform.jmespath) lines.push(`${innerIndent}jmespath: '${transform.jmespath}',`);
49
+ if (transform.objectTransformation) {
50
+ const formattedTransform = JSON.stringify(transform.objectTransformation, null, 2).split("\n").map((line, index) => {
51
+ if (index === 0) return `${innerIndent}objectTransformation: ${line}`;
52
+ return `${innerIndent}${line}`;
53
+ }).join("\n");
54
+ lines.push(`${formattedTransform},`);
55
+ }
56
+ if (lines.length > 1 && lines[lines.length - 1].endsWith(",")) lines[lines.length - 1] = lines[lines.length - 1].slice(0, -1);
57
+ lines.push(`${indent}},`);
58
+ return lines.join("\n");
59
+ }
60
+ /**
61
+ * Generate Trigger Definition using Trigger class
62
+ */
63
+ function generateTriggerDefinition(triggerId, triggerData, style = DEFAULT_STYLE) {
64
+ if (!triggerId || typeof triggerId !== "string") throw new Error("triggerId is required and must be a string");
65
+ if (!triggerData || typeof triggerData !== "object") throw new Error(`triggerData is required for trigger '${triggerId}'`);
66
+ const missingFields = ["name", "messageTemplate"].filter((field) => !triggerData[field] || triggerData[field] === null || triggerData[field] === void 0);
67
+ if (missingFields.length > 0) throw new Error(`Missing required fields for trigger '${triggerId}': ${missingFields.join(", ")}`);
68
+ const { quotes, semicolons, indentation } = style;
69
+ const q = quotes === "single" ? "'" : "\"";
70
+ const semi = semicolons ? ";" : "";
71
+ const triggerVarName = toCamelCase(triggerId);
72
+ const lines = [];
73
+ lines.push(`export const ${triggerVarName} = new Trigger({`);
74
+ lines.push(`${indentation}id: ${formatString(triggerId, q)},`);
75
+ lines.push(`${indentation}name: ${formatString(triggerData.name, q)},`);
76
+ if (triggerData.description) lines.push(`${indentation}description: ${formatString(triggerData.description, q, true)},`);
77
+ if (triggerData.enabled !== void 0 && triggerData.enabled !== null) lines.push(`${indentation}enabled: ${triggerData.enabled},`);
78
+ lines.push(`${indentation}messageTemplate: ${formatString(triggerData.messageTemplate, q, true)},`);
79
+ if (triggerData.inputSchema) {
80
+ const formattedSchema = JSON.stringify(triggerData.inputSchema, null, 2).split("\n").map((line, index) => {
81
+ if (index === 0) return `${indentation}inputSchema: ${line}`;
82
+ return `${indentation}${line}`;
83
+ }).join("\n");
84
+ lines.push(`${formattedSchema},`);
85
+ }
86
+ if (triggerData.outputTransform) {
87
+ const outputTransformFormatted = formatOutputTransform(triggerData.outputTransform, style, 1);
88
+ if (outputTransformFormatted) lines.push(outputTransformFormatted);
89
+ }
90
+ if (triggerData.authentication) {
91
+ const authFormatted = formatAuthentication(triggerData.authentication, style, 1);
92
+ if (authFormatted) lines.push(authFormatted);
93
+ }
94
+ if (lines.length > 0 && lines[lines.length - 1].endsWith(",")) lines[lines.length - 1] = lines[lines.length - 1].slice(0, -1);
95
+ lines.push(`})${semi}`);
96
+ return lines.join("\n");
97
+ }
98
+ /**
99
+ * Generate imports needed for a trigger file
100
+ */
101
+ function generateTriggerImports(style = DEFAULT_STYLE) {
102
+ const imports = [];
103
+ imports.push(generateImport(["Trigger"], "@inkeep/agents-sdk", style));
104
+ return imports;
105
+ }
106
+ /**
107
+ * Generate complete trigger file (imports + definition)
108
+ */
109
+ function generateTriggerFile(triggerId, triggerData, style = DEFAULT_STYLE) {
110
+ return generateFileContent(generateTriggerImports(style), [generateTriggerDefinition(triggerId, triggerData, style)]);
111
+ }
112
+
113
+ //#endregion
114
+ export { generateTriggerDefinition, generateTriggerFile, generateTriggerImports };
@@ -1,15 +1,13 @@
1
1
  import { ManagementApiClient } from "../../api.js";
2
- import "../../utils/config.js";
3
2
  import { initializeCommand } from "../../utils/cli-pipeline.js";
4
3
  import { performBackgroundVersionCheck } from "../../utils/background-version-check.js";
5
- import "../../utils/json-comparison.js";
6
4
  import { loadProject } from "../../utils/project-loader.js";
7
5
  import { extractSubAgents } from "./utils/component-registry.js";
8
- import { compareProjects } from "./project-comparator.js";
9
6
  import { checkAndPromptForStaleComponentCleanup, cleanupStaleComponents, copyProjectToTemp } from "./component-updater.js";
10
7
  import { introspectGenerate } from "./introspect-generator.js";
8
+ import { compareProjects } from "./project-comparator.js";
11
9
  import { existsSync, mkdirSync } from "node:fs";
12
- import { basename, dirname, join, resolve } from "node:path";
10
+ import { join, resolve } from "node:path";
13
11
  import * as p from "@clack/prompts";
14
12
  import chalk from "chalk";
15
13
  import { EventEmitter } from "node:events";
@@ -38,7 +36,7 @@ function resetStdinState() {
38
36
  /**
39
37
  * Create project directory structure
40
38
  */
41
- function createProjectStructure(projectDir, projectId) {
39
+ function createProjectStructure(projectDir) {
42
40
  const projectRoot = projectDir;
43
41
  const paths = {
44
42
  projectRoot,
@@ -60,11 +58,11 @@ function createProjectStructure(projectDir, projectId) {
60
58
  /**
61
59
  * Enrich canDelegateTo references with component type information
62
60
  */
63
- function enrichCanDelegateToWithTypes(project, debug = false) {
61
+ function enrichCanDelegateToWithTypes(project) {
64
62
  const agentIds = new Set(project.agents ? Object.keys(project.agents) : []);
65
63
  const subAgentIds = new Set(Object.keys(extractSubAgents(project)));
66
64
  const externalAgentIds = new Set(project.externalAgents ? Object.keys(project.externalAgents) : []);
67
- const enrichCanDelegateToArray = (canDelegateTo, context) => {
65
+ const enrichCanDelegateToArray = (canDelegateTo) => {
68
66
  if (!Array.isArray(canDelegateTo)) return;
69
67
  for (let i = 0; i < canDelegateTo.length; i++) {
70
68
  const item = canDelegateTo[i];
@@ -79,15 +77,15 @@ function enrichCanDelegateToWithTypes(project, debug = false) {
79
77
  }
80
78
  };
81
79
  if (project.agents) {
82
- for (const [_, agentData] of Object.entries(project.agents)) if (agentData.subAgents) {
83
- for (const [subAgentId, subAgentData] of Object.entries(agentData.subAgents)) if (subAgentData.canDelegateTo) enrichCanDelegateToArray(subAgentData.canDelegateTo, `subAgent:${subAgentId}`);
80
+ for (const agentData of Object.values(project.agents)) if (agentData.subAgents) {
81
+ for (const subAgentData of Object.values(agentData.subAgents)) if (subAgentData.canDelegateTo) enrichCanDelegateToArray(subAgentData.canDelegateTo);
84
82
  }
85
83
  }
86
84
  }
87
85
  /**
88
86
  * Read existing project from filesystem if it exists
89
87
  */
90
- async function readExistingProject(projectRoot, debug = false) {
88
+ async function readExistingProject(projectRoot) {
91
89
  if (!existsSync(join(projectRoot, "index.ts"))) return null;
92
90
  try {
93
91
  const { loadProject: loadProject$1 } = await import("../../utils/project-loader.js");
@@ -97,9 +95,7 @@ async function readExistingProject(projectRoot, debug = false) {
97
95
  reject(/* @__PURE__ */ new Error("getFullDefinition() timed out after 30 seconds - likely circular reference or infinite loop in local project"));
98
96
  }, 3e4);
99
97
  })]);
100
- } catch (error) {
101
- const errorMessage = error instanceof Error ? error.message : String(error);
102
- errorMessage.includes("Credential") && errorMessage.includes("not found");
98
+ } catch {
103
99
  return null;
104
100
  }
105
101
  }
@@ -125,7 +121,7 @@ async function pullV3Command(options) {
125
121
  else console.log(chalk.gray(" Smart comparison • Detect all changes • Targeted updates"));
126
122
  const s = p.spinner();
127
123
  try {
128
- const { config, profile, isCI } = await initializeCommand({
124
+ const { config, isCI } = await initializeCommand({
129
125
  configPath: options.config,
130
126
  profileName: options.profile,
131
127
  tag: options.tag,
@@ -192,7 +188,7 @@ async function pullV3Command(options) {
192
188
  }
193
189
  }
194
190
  s.start(`Fetching project: ${projectId}`);
195
- const remoteProject = await (await ManagementApiClient.create(config.agentsManageApiUrl, options.config, config.tenantId, projectId, isCI, config.agentsManageApiKey)).getFullProject(projectId);
191
+ const remoteProject = await (await ManagementApiClient.create(config.agentsApiUrl, options.config, config.tenantId, projectId, isCI, config.agentsApiKey)).getFullProject(projectId);
196
192
  if (options.debug && remoteProject.functions) {
197
193
  console.log(chalk.gray(" 📋 Project-level functions from API:"), Object.keys(remoteProject.functions));
198
194
  Object.entries(remoteProject.functions).forEach(([id, data]) => {
@@ -219,21 +215,20 @@ async function pullV3Command(options) {
219
215
  }
220
216
  if (remoteProject.agents && remoteProject.tools) {
221
217
  const projectToolIds = Object.keys(remoteProject.tools);
222
- for (const [agentId, agentData] of Object.entries(remoteProject.agents)) if (agentData.tools) {
223
- Object.keys(agentData.tools).length;
218
+ for (const agentData of Object.values(remoteProject.agents)) if (agentData.tools) {
224
219
  const agentSpecificTools = Object.fromEntries(Object.entries(agentData.tools).filter(([toolId]) => !projectToolIds.includes(toolId)));
225
220
  if (Object.keys(agentSpecificTools).length > 0) agentData.tools = agentSpecificTools;
226
221
  else delete agentData.tools;
227
222
  }
228
223
  }
229
- enrichCanDelegateToWithTypes(remoteProject, options.debug);
224
+ enrichCanDelegateToWithTypes(remoteProject);
230
225
  s.message("Project data fetched");
231
226
  if (options.json) {
232
227
  console.log(JSON.stringify(remoteProject, null, 2));
233
228
  restoreLogLevel();
234
229
  return;
235
230
  }
236
- const paths = createProjectStructure(projectDir, projectId);
231
+ const paths = createProjectStructure(projectDir);
237
232
  if (options.introspect) {
238
233
  console.log(chalk.yellow("\n🔍 Introspect mode: Regenerating all files from scratch"));
239
234
  s.start("Generating all files deterministically...");
@@ -247,7 +242,7 @@ async function pullV3Command(options) {
247
242
  if (batchMode) return { success: true };
248
243
  process.exit(0);
249
244
  }
250
- const localProject = await readExistingProject(paths.projectRoot, options.debug);
245
+ const localProject = await readExistingProject(paths.projectRoot);
251
246
  if (!localProject) s.message("No existing project found - treating as new project");
252
247
  else s.message("Existing project loaded");
253
248
  s.start("Building component registry from local files...");
@@ -261,7 +256,7 @@ async function pullV3Command(options) {
261
256
  const nameGroups = /* @__PURE__ */ new Map();
262
257
  for (const comp of allComponents) {
263
258
  if (!nameGroups.has(comp.name)) nameGroups.set(comp.name, []);
264
- nameGroups.get(comp.name).push(comp);
259
+ nameGroups.get(comp.name)?.push(comp);
265
260
  }
266
261
  for (const [varName, components] of nameGroups.entries()) if (components.length > 1) {
267
262
  console.log(chalk.red(` ❌ Variable name conflict: "${varName}"`));
@@ -269,7 +264,7 @@ async function pullV3Command(options) {
269
264
  } else console.log(chalk.gray(` ✅ ${varName} (${components[0].type}:${components[0].id})`));
270
265
  }
271
266
  s.start("Comparing projects for changes...");
272
- const comparison = await compareProjects(localProject, remoteProject, localRegistry, options.debug);
267
+ const comparison = await compareProjects(localProject, remoteProject, options.debug);
273
268
  if (!comparison.hasChanges && !options.force) {
274
269
  s.stop();
275
270
  console.log(chalk.green("✅ Project is already up to date"));
@@ -289,7 +284,7 @@ async function pullV3Command(options) {
289
284
  copyProjectToTemp(paths.projectRoot, tempDirName);
290
285
  console.log(chalk.green(`✅ Existing project copied to temp directory`));
291
286
  s.start("Checking for stale components...");
292
- const shouldCleanupStale = await checkAndPromptForStaleComponentCleanup(paths.projectRoot, remoteProject, localRegistry);
287
+ const shouldCleanupStale = await checkAndPromptForStaleComponentCleanup(remoteProject, localRegistry);
293
288
  resetStdinState();
294
289
  if (shouldCleanupStale) {
295
290
  s.start("Cleaning up stale components from temp directory...");
@@ -315,7 +310,7 @@ async function pullV3Command(options) {
315
310
  const nameGroups = /* @__PURE__ */ new Map();
316
311
  for (const comp of allComponents) {
317
312
  if (!nameGroups.has(comp.name)) nameGroups.set(comp.name, []);
318
- nameGroups.get(comp.name).push(comp);
313
+ nameGroups.get(comp.name)?.push(comp);
319
314
  }
320
315
  for (const [varName, components] of nameGroups.entries()) if (components.length > 1) {
321
316
  console.log(chalk.red(` ❌ Variable name conflict: "${varName}"`));
@@ -333,9 +328,9 @@ async function pullV3Command(options) {
333
328
  const newComponentsForContext = newComponentResults && newComponentResults.length > 0 ? newComponentResults.filter((result) => result.success).map((result) => ({
334
329
  componentId: result.componentId,
335
330
  componentType: result.componentType,
336
- filePath: result.filePath.replace(paths.projectRoot + "/", "")
331
+ filePath: result.filePath.replace(`${paths.projectRoot}/`, "")
337
332
  })) : void 0;
338
- await updateModifiedComponents(comparison, remoteProject, localRegistry, paths.projectRoot, options.env || "development", options.debug, tempDirName, newComponentsForContext);
333
+ await updateModifiedComponents(comparison, remoteProject, localRegistry, paths.projectRoot, options.debug, tempDirName, newComponentsForContext);
339
334
  }
340
335
  s.start("Generating project index file in temp directory...");
341
336
  const { generateProjectIndex } = await import("./project-index-generator.js");
@@ -380,7 +375,7 @@ async function pullAllProjects(options) {
380
375
  console.log(chalk.gray(" • Existing projects: Smart comparison + LLM merging + confirmation prompts"));
381
376
  console.log(chalk.gray(" • New projects: Fresh generation with introspect mode\n"));
382
377
  performBackgroundVersionCheck();
383
- const { config, profile, isCI } = await initializeCommand({
378
+ const { config, isCI } = await initializeCommand({
384
379
  configPath: options.config,
385
380
  profileName: options.profile,
386
381
  tag: options.tag,
@@ -392,7 +387,7 @@ async function pullAllProjects(options) {
392
387
  const s = p.spinner();
393
388
  try {
394
389
  s.start("Fetching project list from API...");
395
- const projects = await (await ManagementApiClient.create(config.agentsManageApiUrl, options.config, config.tenantId, void 0, isCI, config.agentsManageApiKey)).listAllProjects();
390
+ const projects = await (await ManagementApiClient.create(config.agentsApiUrl, options.config, config.tenantId, void 0, isCI, config.agentsApiKey)).listAllProjects();
396
391
  s.stop(`Found ${projects.length} project(s)`);
397
392
  if (projects.length === 0) {
398
393
  console.log(chalk.yellow("No projects found for this tenant."));
@@ -486,8 +481,8 @@ async function pullSingleProject(projectId, projectName, options, config, isCI)
486
481
  if (originalLogLevel !== void 0) process.env.LOG_LEVEL = originalLogLevel;
487
482
  else delete process.env.LOG_LEVEL;
488
483
  };
489
- const remoteProject = await (await ManagementApiClient.create(config.agentsManageApiUrl, options.config, config.tenantId, projectId, isCI, config.agentsManageApiKey)).getFullProject(projectId);
490
- await introspectGenerate(remoteProject, createProjectStructure(targetDir, projectId), options.env || "development", false);
484
+ const remoteProject = await (await ManagementApiClient.create(config.agentsApiUrl, options.config, config.tenantId, projectId, isCI, config.agentsApiKey)).getFullProject(projectId);
485
+ await introspectGenerate(remoteProject, createProjectStructure(targetDir), options.env || "development", false);
491
486
  restoreLogLevel();
492
487
  return {
493
488
  projectId,
@@ -12,6 +12,7 @@ import { generateProjectFile } from "./components/project-generator.js";
12
12
  import { generateStatusComponentFile } from "./components/status-component-generator.js";
13
13
  import { generateSubAgentFile } from "./components/sub-agent-generator.js";
14
14
  import { ComponentRegistry, registerAllComponents } from "./utils/component-registry.js";
15
+ import { generateTriggerFile } from "./components/trigger-generator.js";
15
16
  import { mkdirSync, writeFileSync } from "node:fs";
16
17
  import { dirname, join } from "node:path";
17
18
  import chalk from "chalk";
@@ -33,7 +34,7 @@ function ensureDir(filePath) {
33
34
  * Check if an agent is complete enough for code generation
34
35
  * An agent needs a name, defaultSubAgentId, and at least one sub-agent
35
36
  */
36
- function isAgentComplete(agentId, agentData) {
37
+ function isAgentComplete(agentData) {
37
38
  if (!agentData.name) return {
38
39
  complete: false,
39
40
  reason: "missing name"
@@ -102,7 +103,7 @@ async function introspectGenerate(project, paths, environment, debug, options =
102
103
  generatedFiles.push(functionFile);
103
104
  functionToolsGenerated.add(toolId);
104
105
  }
105
- if (project.agents) for (const [agentId, agentData] of Object.entries(project.agents)) {
106
+ if (project.agents) for (const agentData of Object.values(project.agents)) {
106
107
  const agentFunctionTools = agentData.functionTools;
107
108
  const agentFunctions = agentData.functions;
108
109
  if (agentFunctionTools) for (const [toolId, toolData] of Object.entries(agentFunctionTools)) {
@@ -126,7 +127,7 @@ async function introspectGenerate(project, paths, environment, debug, options =
126
127
  }
127
128
  }
128
129
  if (project.functions) {
129
- for (const [funcId, funcData] of Object.entries(project.functions)) if (!functionToolsGenerated.has(funcId)) {
130
+ for (const funcId of Object.keys(project.functions)) if (!functionToolsGenerated.has(funcId)) {
130
131
  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
  }
132
133
  }
@@ -182,7 +183,7 @@ async function introspectGenerate(project, paths, environment, debug, options =
182
183
  }
183
184
  const completeAgentIds = /* @__PURE__ */ new Set();
184
185
  if (project.agents) for (const [agentId, agentData] of Object.entries(project.agents)) {
185
- const completeness = isAgentComplete(agentId, agentData);
186
+ const completeness = isAgentComplete(agentData);
186
187
  if (completeness.complete) completeAgentIds.add(agentId);
187
188
  else {
188
189
  skippedAgents.push({
@@ -196,7 +197,7 @@ async function introspectGenerate(project, paths, environment, debug, options =
196
197
  let totalSubAgents = 0;
197
198
  for (const [agentId, agentData] of Object.entries(project.agents)) {
198
199
  if (!completeAgentIds.has(agentId)) continue;
199
- if (agentData.subAgents) for (const [subAgentId, subAgentData] of Object.entries(agentData.subAgents)) totalSubAgents++;
200
+ if (agentData.subAgents) for (const _subAgentId of Object.keys(agentData.subAgents)) totalSubAgents++;
200
201
  }
201
202
  if (totalSubAgents > 0) for (const [agentId, agentData] of Object.entries(project.agents)) {
202
203
  if (!completeAgentIds.has(agentId)) continue;
@@ -219,6 +220,13 @@ async function introspectGenerate(project, paths, environment, debug, options =
219
220
  ensureDir(agentFile);
220
221
  writeFileSync(agentFile, agentContent, "utf-8");
221
222
  generatedFiles.push(agentFile);
223
+ if (agentData.triggers && Object.keys(agentData.triggers).length > 0) for (const [triggerId, triggerData] of Object.entries(agentData.triggers)) {
224
+ const triggerFile = join(paths.agentsDir, "triggers", `${triggerId}.ts`);
225
+ const triggerContent = generateTriggerFile(triggerId, triggerData, style);
226
+ ensureDir(triggerFile);
227
+ writeFileSync(triggerFile, triggerContent, "utf-8");
228
+ generatedFiles.push(triggerFile);
229
+ }
222
230
  }
223
231
  const projectDataForGenerator = {
224
232
  ...project,
@@ -251,7 +259,7 @@ async function introspectGenerate(project, paths, environment, debug, options =
251
259
  */
252
260
  function findContextConfigData(project, contextId) {
253
261
  if (project.agents) {
254
- for (const [agentId, agentData] of Object.entries(project.agents)) if (agentData.contextConfig) {
262
+ for (const agentData of Object.values(project.agents)) if (agentData.contextConfig) {
255
263
  if (agentData.contextConfig.id === contextId) return agentData.contextConfig;
256
264
  }
257
265
  }
@@ -261,7 +269,7 @@ function findContextConfigData(project, contextId) {
261
269
  */
262
270
  function findStatusComponentData(project, statusId) {
263
271
  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) {
272
+ for (const agentData of Object.values(project.agents)) if (agentData.statusUpdates?.statusComponents) for (const statusComp of agentData.statusUpdates.statusComponents) {
265
273
  let compId;
266
274
  if (typeof statusComp === "string") compId = statusComp;
267
275
  else if (typeof statusComp === "object" && statusComp) compId = statusComp.type;
@@ -54,7 +54,7 @@ async function mergeComponentsWithLLM(request) {
54
54
  let relativePath = "";
55
55
  for (let i = 0; i < upLevels; i++) relativePath += "../";
56
56
  relativePath += toParts.slice(commonLength).join("/");
57
- return relativePath.startsWith("../") ? relativePath : "./" + relativePath;
57
+ return relativePath.startsWith("../") ? relativePath : `./${relativePath}`;
58
58
  }
59
59
  const componentsToExportList = componentsToExport && componentsToExport.length > 0 ? componentsToExport.map((c) => `- ${c.variableName} (${c.reason})`).join("\n") : "";
60
60
  const prompt = `You are a TypeScript code expert tasked with intelligently merging component updates.