@inkeep/agents-cli 0.39.4 → 0.40.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 (80) hide show
  1. package/dist/_virtual/rolldown_runtime.js +7 -0
  2. package/dist/api.js +185 -0
  3. package/dist/commands/add.js +139 -0
  4. package/dist/commands/config.js +86 -0
  5. package/dist/commands/dev.js +259 -0
  6. package/dist/commands/init.js +360 -0
  7. package/dist/commands/list-agents.js +56 -0
  8. package/dist/commands/login.js +179 -0
  9. package/dist/commands/logout.js +56 -0
  10. package/dist/commands/profile.js +276 -0
  11. package/dist/{component-parser2.js → commands/pull-v3/component-parser.js} +16 -3
  12. package/dist/commands/pull-v3/component-updater.js +710 -0
  13. package/dist/commands/pull-v3/components/agent-generator.js +241 -0
  14. package/dist/commands/pull-v3/components/artifact-component-generator.js +127 -0
  15. package/dist/commands/pull-v3/components/context-config-generator.js +190 -0
  16. package/dist/commands/pull-v3/components/credential-generator.js +89 -0
  17. package/dist/commands/pull-v3/components/data-component-generator.js +102 -0
  18. package/dist/commands/pull-v3/components/environment-generator.js +170 -0
  19. package/dist/commands/pull-v3/components/external-agent-generator.js +75 -0
  20. package/dist/commands/pull-v3/components/function-tool-generator.js +94 -0
  21. package/dist/commands/pull-v3/components/mcp-tool-generator.js +86 -0
  22. package/dist/commands/pull-v3/components/project-generator.js +145 -0
  23. package/dist/commands/pull-v3/components/status-component-generator.js +92 -0
  24. package/dist/commands/pull-v3/components/sub-agent-generator.js +285 -0
  25. package/dist/commands/pull-v3/index.js +510 -0
  26. package/dist/commands/pull-v3/introspect-generator.js +278 -0
  27. package/dist/commands/pull-v3/llm-content-merger.js +192 -0
  28. package/dist/{new-component-generator.js → commands/pull-v3/new-component-generator.js} +14 -3
  29. package/dist/commands/pull-v3/project-comparator.js +914 -0
  30. package/dist/{project-index-generator.js → commands/pull-v3/project-index-generator.js} +1 -2
  31. package/dist/{project-validator.js → commands/pull-v3/project-validator.js} +4 -4
  32. package/dist/commands/pull-v3/targeted-typescript-placeholders.js +173 -0
  33. package/dist/commands/pull-v3/utils/component-registry.js +369 -0
  34. package/dist/commands/pull-v3/utils/component-tracker.js +165 -0
  35. package/dist/commands/pull-v3/utils/generator-utils.js +146 -0
  36. package/dist/commands/pull-v3/utils/model-provider-detector.js +44 -0
  37. package/dist/commands/push.js +326 -0
  38. package/dist/commands/status.js +89 -0
  39. package/dist/commands/update.js +97 -0
  40. package/dist/commands/whoami.js +38 -0
  41. package/dist/config.js +0 -1
  42. package/dist/env.js +30 -0
  43. package/dist/exports.js +3 -0
  44. package/dist/index.js +28 -196514
  45. package/dist/instrumentation.js +47 -0
  46. package/dist/types/agent.js +1 -0
  47. package/dist/types/tsx.d.d.ts +1 -0
  48. package/dist/utils/background-version-check.js +19 -0
  49. package/dist/utils/ci-environment.js +87 -0
  50. package/dist/utils/cli-pipeline.js +158 -0
  51. package/dist/utils/config.js +290 -0
  52. package/dist/utils/credentials.js +132 -0
  53. package/dist/utils/environment-loader.js +28 -0
  54. package/dist/utils/file-finder.js +62 -0
  55. package/dist/utils/json-comparator.js +185 -0
  56. package/dist/utils/json-comparison.js +232 -0
  57. package/dist/utils/mcp-runner.js +120 -0
  58. package/dist/utils/model-config.js +182 -0
  59. package/dist/utils/package-manager.js +58 -0
  60. package/dist/utils/profile-config.js +85 -0
  61. package/dist/utils/profiles/index.js +4 -0
  62. package/dist/utils/profiles/profile-manager.js +219 -0
  63. package/dist/utils/profiles/types.js +62 -0
  64. package/dist/utils/project-directory.js +33 -0
  65. package/dist/utils/project-loader.js +29 -0
  66. package/dist/utils/schema-introspection.js +44 -0
  67. package/dist/utils/templates.js +198 -0
  68. package/dist/utils/tsx-loader.js +27 -0
  69. package/dist/utils/url.js +26 -0
  70. package/dist/utils/version-check.js +79 -0
  71. package/package.json +4 -19
  72. package/dist/component-parser.js +0 -4
  73. package/dist/component-updater.js +0 -4
  74. package/dist/config2.js +0 -4
  75. package/dist/credential-stores.js +0 -4
  76. package/dist/environment-generator.js +0 -4
  77. package/dist/nodefs.js +0 -27
  78. package/dist/opfs-ahp.js +0 -368
  79. package/dist/project-loader.js +0 -4
  80. package/dist/tsx-loader.js +0 -4
@@ -0,0 +1,510 @@
1
+ import { ManagementApiClient } from "../../api.js";
2
+ import "../../utils/config.js";
3
+ import { initializeCommand } from "../../utils/cli-pipeline.js";
4
+ import { performBackgroundVersionCheck } from "../../utils/background-version-check.js";
5
+ import "../../utils/json-comparison.js";
6
+ import { loadProject } from "../../utils/project-loader.js";
7
+ import { extractSubAgents } from "./utils/component-registry.js";
8
+ import { compareProjects } from "./project-comparator.js";
9
+ import { checkAndPromptForStaleComponentCleanup, cleanupStaleComponents, copyProjectToTemp } from "./component-updater.js";
10
+ import { introspectGenerate } from "./introspect-generator.js";
11
+ import { existsSync, mkdirSync } from "node:fs";
12
+ import { basename, dirname, join, resolve } from "node:path";
13
+ import * as p from "@clack/prompts";
14
+ import chalk from "chalk";
15
+ import { EventEmitter } from "node:events";
16
+
17
+ //#region src/commands/pull-v3/index.ts
18
+ /**
19
+ * Pull v3 - Clean, efficient project generation
20
+ *
21
+ * Step 1: Validate and compile existing code
22
+ * Step 2: Compare project with DB to detect ALL changes
23
+ * Step 3: Classify changes as new vs modified components
24
+ * Step 4: Generate new components deterministically
25
+ * Step 5: Use LLM to correct modified components
26
+ */
27
+ EventEmitter.defaultMaxListeners = 20;
28
+ /**
29
+ * Clean up stdin state between interactive prompts to prevent listener leaks
30
+ */
31
+ function resetStdinState() {
32
+ process.stdin.removeAllListeners("data");
33
+ process.stdin.removeAllListeners("keypress");
34
+ process.stdin.removeAllListeners("end");
35
+ if (process.stdin.isTTY && process.stdin.isRaw) process.stdin.setRawMode(false);
36
+ if (!process.stdin.isPaused()) process.stdin.pause();
37
+ }
38
+ /**
39
+ * Create project directory structure
40
+ */
41
+ function createProjectStructure(projectDir, projectId) {
42
+ const projectRoot = projectDir;
43
+ const paths = {
44
+ projectRoot,
45
+ agentsDir: join(projectRoot, "agents"),
46
+ toolsDir: join(projectRoot, "tools"),
47
+ dataComponentsDir: join(projectRoot, "data-components"),
48
+ artifactComponentsDir: join(projectRoot, "artifact-components"),
49
+ statusComponentsDir: join(projectRoot, "status-components"),
50
+ environmentsDir: join(projectRoot, "environments"),
51
+ credentialsDir: join(projectRoot, "credentials"),
52
+ contextConfigsDir: join(projectRoot, "context-configs"),
53
+ externalAgentsDir: join(projectRoot, "external-agents")
54
+ };
55
+ Object.values(paths).forEach((dir) => {
56
+ if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
57
+ });
58
+ return paths;
59
+ }
60
+ /**
61
+ * Enrich canDelegateTo references with component type information
62
+ */
63
+ function enrichCanDelegateToWithTypes(project, debug = false) {
64
+ const agentIds = new Set(project.agents ? Object.keys(project.agents) : []);
65
+ const subAgentIds = new Set(Object.keys(extractSubAgents(project)));
66
+ const externalAgentIds = new Set(project.externalAgents ? Object.keys(project.externalAgents) : []);
67
+ const enrichCanDelegateToArray = (canDelegateTo, context) => {
68
+ if (!Array.isArray(canDelegateTo)) return;
69
+ for (let i = 0; i < canDelegateTo.length; i++) {
70
+ const item = canDelegateTo[i];
71
+ if (typeof item !== "string") continue;
72
+ const id = item;
73
+ let enrichedItem = null;
74
+ if (agentIds.has(id)) enrichedItem = { agentId: id };
75
+ else if (subAgentIds.has(id)) enrichedItem = { subAgentId: id };
76
+ else if (externalAgentIds.has(id)) enrichedItem = { externalAgentId: id };
77
+ else continue;
78
+ canDelegateTo[i] = enrichedItem;
79
+ }
80
+ };
81
+ 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}`);
84
+ }
85
+ }
86
+ }
87
+ /**
88
+ * Read existing project from filesystem if it exists
89
+ */
90
+ async function readExistingProject(projectRoot, debug = false) {
91
+ if (!existsSync(join(projectRoot, "index.ts"))) return null;
92
+ try {
93
+ const { loadProject: loadProject$1 } = await import("../../utils/project-loader.js");
94
+ const project = await loadProject$1(projectRoot);
95
+ return await Promise.race([project.getFullDefinition(), new Promise((_, reject) => {
96
+ setTimeout(() => {
97
+ reject(/* @__PURE__ */ new Error("getFullDefinition() timed out after 30 seconds - likely circular reference or infinite loop in local project"));
98
+ }, 3e4);
99
+ })]);
100
+ } catch (error) {
101
+ const errorMessage = error instanceof Error ? error.message : String(error);
102
+ errorMessage.includes("Credential") && errorMessage.includes("not found");
103
+ return null;
104
+ }
105
+ }
106
+ /**
107
+ * Main pull-v3 command
108
+ * @returns PullResult when in batch mode, otherwise void (exits process)
109
+ */
110
+ async function pullV3Command(options) {
111
+ if (options.all) {
112
+ await pullAllProjects(options);
113
+ return;
114
+ }
115
+ const batchMode = options._batchMode ?? false;
116
+ const originalLogLevel = process.env.LOG_LEVEL;
117
+ process.env.LOG_LEVEL = "silent";
118
+ const restoreLogLevel = () => {
119
+ if (originalLogLevel !== void 0) process.env.LOG_LEVEL = originalLogLevel;
120
+ else delete process.env.LOG_LEVEL;
121
+ };
122
+ if (!batchMode) performBackgroundVersionCheck();
123
+ console.log(chalk.blue("\nInkeep Pull:"));
124
+ if (options.introspect) console.log(chalk.gray(" Introspect mode • Complete regeneration • No comparison needed"));
125
+ else console.log(chalk.gray(" Smart comparison • Detect all changes • Targeted updates"));
126
+ const s = p.spinner();
127
+ try {
128
+ const { config, profile, isCI } = await initializeCommand({
129
+ configPath: options.config,
130
+ profileName: options.profile,
131
+ tag: options.tag,
132
+ showSpinner: true,
133
+ spinnerText: "Loading configuration...",
134
+ logConfig: true,
135
+ quiet: options.quiet
136
+ });
137
+ s.start("Detecting project...");
138
+ let projectDir;
139
+ let projectId;
140
+ let localProjectForId = null;
141
+ const currentDir = process.cwd();
142
+ if (existsSync(join(currentDir, "index.ts"))) {
143
+ projectDir = currentDir;
144
+ s.start("Loading local project...");
145
+ try {
146
+ localProjectForId = await loadProject(projectDir);
147
+ const localProjectId = localProjectForId.getId();
148
+ if (options.project) {
149
+ if (localProjectId !== options.project) {
150
+ s.stop("Project ID mismatch");
151
+ console.error(chalk.red(`Local project ID "${localProjectId}" doesn't match --project "${options.project}"`));
152
+ console.error(chalk.yellow("Either remove --project flag or ensure it matches the local project ID"));
153
+ if (batchMode) return {
154
+ success: false,
155
+ error: "Project ID mismatch"
156
+ };
157
+ process.exit(1);
158
+ }
159
+ }
160
+ projectId = localProjectId;
161
+ s.stop(`Using local project: ${projectId}`);
162
+ } catch (error) {
163
+ s.stop("Failed to load local project");
164
+ throw new Error(`Could not load local project: ${error instanceof Error ? error.message : String(error)}`);
165
+ }
166
+ } else {
167
+ if (!options.project) {
168
+ s.stop("No index.ts found in current directory");
169
+ console.error(chalk.yellow("Please run this command from a directory containing index.ts or use --project <project-id>"));
170
+ if (batchMode) return {
171
+ success: false,
172
+ error: "No index.ts found and no --project specified"
173
+ };
174
+ process.exit(1);
175
+ }
176
+ const projectPath = resolve(currentDir, options.project);
177
+ if (existsSync(join(projectPath, "index.ts"))) {
178
+ projectDir = projectPath;
179
+ s.start("Loading project from specified path...");
180
+ try {
181
+ localProjectForId = await loadProject(projectDir);
182
+ projectId = localProjectForId.getId();
183
+ s.stop(`Using project from path: ${projectId}`);
184
+ } catch (error) {
185
+ s.stop("Failed to load project from path");
186
+ throw new Error(`Could not load project from ${projectPath}: ${error instanceof Error ? error.message : String(error)}`);
187
+ }
188
+ } else {
189
+ projectId = options.project;
190
+ projectDir = join(currentDir, projectId);
191
+ s.stop(`Creating new project directory: ${projectDir}`);
192
+ }
193
+ }
194
+ s.start(`Fetching project: ${projectId}`);
195
+ const remoteProject = await (await ManagementApiClient.create(config.agentsManageApiUrl, options.config, config.tenantId, projectId, isCI, config.agentsManageApiKey)).getFullProject(projectId);
196
+ if (options.debug && remoteProject.functions) {
197
+ console.log(chalk.gray(" 📋 Project-level functions from API:"), Object.keys(remoteProject.functions));
198
+ Object.entries(remoteProject.functions).forEach(([id, data]) => {
199
+ console.log(chalk.gray(` ${id}: has name=${!!data.name}, has description=${!!data.description}`));
200
+ });
201
+ }
202
+ if (remoteProject.agents) for (const [agentId, agentData] of Object.entries(remoteProject.agents)) {
203
+ if (agentData.functionTools) {
204
+ remoteProject.functionTools = remoteProject.functionTools || {};
205
+ Object.assign(remoteProject.functionTools, agentData.functionTools);
206
+ if (options.debug) console.log(chalk.gray(` Hoisted functionTools from agent ${agentId}: ${Object.keys(agentData.functionTools).join(", ")}`));
207
+ }
208
+ if (agentData.functions) {
209
+ remoteProject.functions = remoteProject.functions || {};
210
+ Object.entries(agentData.functions).forEach(([funcId, funcData]) => {
211
+ if (!remoteProject.functions[funcId]) remoteProject.functions[funcId] = {
212
+ id: funcData.id,
213
+ inputSchema: funcData.inputSchema,
214
+ executeCode: funcData.executeCode,
215
+ dependencies: funcData.dependencies
216
+ };
217
+ });
218
+ }
219
+ }
220
+ if (remoteProject.agents && remoteProject.tools) {
221
+ 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;
224
+ const agentSpecificTools = Object.fromEntries(Object.entries(agentData.tools).filter(([toolId]) => !projectToolIds.includes(toolId)));
225
+ if (Object.keys(agentSpecificTools).length > 0) agentData.tools = agentSpecificTools;
226
+ else delete agentData.tools;
227
+ }
228
+ }
229
+ enrichCanDelegateToWithTypes(remoteProject, options.debug);
230
+ s.message("Project data fetched");
231
+ if (options.json) {
232
+ console.log(JSON.stringify(remoteProject, null, 2));
233
+ restoreLogLevel();
234
+ return;
235
+ }
236
+ const paths = createProjectStructure(projectDir, projectId);
237
+ if (options.introspect) {
238
+ console.log(chalk.yellow("\n🔍 Introspect mode: Regenerating all files from scratch"));
239
+ s.start("Generating all files deterministically...");
240
+ await introspectGenerate(remoteProject, paths, options.env || "development", options.debug || false);
241
+ s.stop("All files generated");
242
+ console.log(chalk.green("\n✅ Project regenerated successfully with introspect mode!"));
243
+ console.log(chalk.gray(` 📁 Location: ${paths.projectRoot}`));
244
+ console.log(chalk.gray(` 🌍 Environment: ${options.env || "development"}`));
245
+ console.log(chalk.gray(` 🚀 Mode: Complete regeneration (no comparison)`));
246
+ restoreLogLevel();
247
+ if (batchMode) return { success: true };
248
+ process.exit(0);
249
+ }
250
+ const localProject = await readExistingProject(paths.projectRoot, options.debug);
251
+ if (!localProject) s.message("No existing project found - treating as new project");
252
+ else s.message("Existing project loaded");
253
+ s.start("Building component registry from local files...");
254
+ const { buildComponentRegistryFromParsing } = await import("./component-parser.js");
255
+ const localRegistry = buildComponentRegistryFromParsing(paths.projectRoot, options.debug);
256
+ s.message("Component registry built");
257
+ if (options.debug) {
258
+ console.log(chalk.cyan("\n🔍 Component Registry Debug:"));
259
+ const allComponents = localRegistry.getAllComponents();
260
+ console.log(chalk.gray(" Total components registered:"), allComponents.length);
261
+ const nameGroups = /* @__PURE__ */ new Map();
262
+ for (const comp of allComponents) {
263
+ if (!nameGroups.has(comp.name)) nameGroups.set(comp.name, []);
264
+ nameGroups.get(comp.name).push(comp);
265
+ }
266
+ for (const [varName, components] of nameGroups.entries()) if (components.length > 1) {
267
+ console.log(chalk.red(` ❌ Variable name conflict: "${varName}"`));
268
+ for (const comp of components) console.log(chalk.gray(` - ${comp.type}:${comp.id} -> ${comp.filePath}`));
269
+ } else console.log(chalk.gray(` ✅ ${varName} (${components[0].type}:${components[0].id})`));
270
+ }
271
+ s.start("Comparing projects for changes...");
272
+ const comparison = await compareProjects(localProject, remoteProject, localRegistry, options.debug);
273
+ if (!comparison.hasChanges && !options.force) {
274
+ s.stop();
275
+ console.log(chalk.green("✅ Project is already up to date"));
276
+ console.log(chalk.gray(" No differences detected between local and remote projects"));
277
+ restoreLogLevel();
278
+ if (batchMode) return {
279
+ success: true,
280
+ upToDate: true
281
+ };
282
+ process.exit(0);
283
+ }
284
+ s.message(`Detected ${comparison.changeCount} differences`);
285
+ const tempDirName = `.temp-validation-${Date.now()}`;
286
+ s.start("Preparing temp directory...");
287
+ let performedCleanup = false;
288
+ if (localProject) {
289
+ copyProjectToTemp(paths.projectRoot, tempDirName);
290
+ console.log(chalk.green(`✅ Existing project copied to temp directory`));
291
+ s.start("Checking for stale components...");
292
+ const shouldCleanupStale = await checkAndPromptForStaleComponentCleanup(paths.projectRoot, remoteProject, localRegistry);
293
+ resetStdinState();
294
+ if (shouldCleanupStale) {
295
+ s.start("Cleaning up stale components from temp directory...");
296
+ await cleanupStaleComponents(paths.projectRoot, tempDirName, remoteProject, localRegistry);
297
+ console.log(chalk.green(`✅ Stale components cleaned up from temp directory`));
298
+ performedCleanup = true;
299
+ }
300
+ } else {
301
+ mkdirSync(join(paths.projectRoot, tempDirName), { recursive: true });
302
+ console.log(chalk.green(`✅ Empty temp directory created for new project`));
303
+ }
304
+ s.message("Temp directory prepared");
305
+ const newComponentCount = Object.values(comparison.componentChanges).reduce((sum, changes) => sum + changes.added.length, 0);
306
+ let newComponentResults = [];
307
+ if (newComponentCount > 0) {
308
+ s.start("Creating new component files in temp directory...");
309
+ const { createNewComponents } = await import("./new-component-generator.js");
310
+ newComponentResults = await createNewComponents(comparison, remoteProject, localRegistry, paths, options.env || "development", tempDirName);
311
+ if (options.debug) {
312
+ console.log(chalk.cyan("\n🔍 Component Registry After Generation:"));
313
+ const allComponents = localRegistry.getAllComponents();
314
+ console.log(chalk.gray(" Total components registered:"), allComponents.length);
315
+ const nameGroups = /* @__PURE__ */ new Map();
316
+ for (const comp of allComponents) {
317
+ if (!nameGroups.has(comp.name)) nameGroups.set(comp.name, []);
318
+ nameGroups.get(comp.name).push(comp);
319
+ }
320
+ for (const [varName, components] of nameGroups.entries()) if (components.length > 1) {
321
+ console.log(chalk.red(` ❌ Variable name conflict: "${varName}"`));
322
+ for (const comp of components) console.log(chalk.gray(` - ${comp.type}:${comp.id} -> ${comp.filePath}`));
323
+ } else console.log(chalk.gray(` ✅ ${varName} (${components[0].type}:${components[0].id})`));
324
+ }
325
+ const successful = newComponentResults.filter((r) => r.success);
326
+ console.log(chalk.green(`✅ Added ${successful.length} new components to temp directory`));
327
+ s.message("New component files created");
328
+ }
329
+ const modifiedCount = Object.values(comparison.componentChanges).reduce((sum, changes) => sum + changes.modified.length, 0);
330
+ if (modifiedCount > 0) {
331
+ s.stop();
332
+ const { updateModifiedComponents } = await import("./component-updater.js");
333
+ const newComponentsForContext = newComponentResults && newComponentResults.length > 0 ? newComponentResults.filter((result) => result.success).map((result) => ({
334
+ componentId: result.componentId,
335
+ componentType: result.componentType,
336
+ filePath: result.filePath.replace(paths.projectRoot + "/", "")
337
+ })) : void 0;
338
+ await updateModifiedComponents(comparison, remoteProject, localRegistry, paths.projectRoot, options.env || "development", options.debug, tempDirName, newComponentsForContext);
339
+ }
340
+ s.start("Generating project index file in temp directory...");
341
+ const { generateProjectIndex } = await import("./project-index-generator.js");
342
+ await generateProjectIndex(join(paths.projectRoot, tempDirName), remoteProject, localRegistry, projectId);
343
+ s.message("Project index file created");
344
+ if (newComponentCount > 0 || modifiedCount > 0 || performedCleanup) {
345
+ s.stop("Running validation on complete project...");
346
+ const { validateTempDirectory } = await import("./project-validator.js");
347
+ const validationResult = await validateTempDirectory(paths.projectRoot, tempDirName, remoteProject, { skipExit: batchMode });
348
+ if (batchMode) {
349
+ restoreLogLevel();
350
+ return {
351
+ success: validationResult.success,
352
+ upToDate: validationResult.upToDate
353
+ };
354
+ }
355
+ } else {
356
+ s.stop();
357
+ console.log(chalk.green("\n✅ No changes detected - project is up to date"));
358
+ }
359
+ restoreLogLevel();
360
+ if (batchMode) return { success: true };
361
+ process.exit(0);
362
+ } catch (error) {
363
+ s.stop();
364
+ console.error(chalk.red(`\nError: ${error instanceof Error ? error.message : String(error)}`));
365
+ if (options.debug && error instanceof Error) console.error(chalk.red(error.stack || ""));
366
+ restoreLogLevel();
367
+ if (batchMode) return {
368
+ success: false,
369
+ error: error instanceof Error ? error.message : String(error)
370
+ };
371
+ process.exit(1);
372
+ }
373
+ }
374
+ /**
375
+ * Pull all projects for the current tenant
376
+ * Uses smart comparison with LLM merging for existing projects, introspect for new projects
377
+ */
378
+ async function pullAllProjects(options) {
379
+ console.log(chalk.blue("\n🔄 Batch Pull: Sequential processing with smart comparison\n"));
380
+ console.log(chalk.gray(" • Existing projects: Smart comparison + LLM merging + confirmation prompts"));
381
+ console.log(chalk.gray(" • New projects: Fresh generation with introspect mode\n"));
382
+ performBackgroundVersionCheck();
383
+ const { config, profile, isCI } = await initializeCommand({
384
+ configPath: options.config,
385
+ profileName: options.profile,
386
+ tag: options.tag,
387
+ showSpinner: true,
388
+ spinnerText: "Loading configuration...",
389
+ logConfig: true,
390
+ quiet: options.quiet
391
+ });
392
+ const s = p.spinner();
393
+ try {
394
+ 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();
396
+ s.stop(`Found ${projects.length} project(s)`);
397
+ if (projects.length === 0) {
398
+ console.log(chalk.yellow("No projects found for this tenant."));
399
+ process.exit(0);
400
+ }
401
+ const existingProjects = [];
402
+ const newProjects = [];
403
+ for (const project of projects) if (existsSync(join(join(process.cwd(), project.id), "index.ts"))) existingProjects.push(project);
404
+ else newProjects.push(project);
405
+ console.log(chalk.gray("\nProjects to pull:\n"));
406
+ if (existingProjects.length > 0) {
407
+ console.log(chalk.cyan(" Existing (smart comparison):"));
408
+ for (const project of existingProjects) console.log(chalk.gray(` • ${project.name || project.id} (${project.id})`));
409
+ }
410
+ if (newProjects.length > 0) {
411
+ console.log(chalk.cyan(" New (introspect):"));
412
+ for (const project of newProjects) console.log(chalk.gray(` • ${project.name || project.id} (${project.id})`));
413
+ }
414
+ console.log();
415
+ const results = [];
416
+ const total = projects.length;
417
+ for (let i = 0; i < projects.length; i++) {
418
+ const project = projects[i];
419
+ const progress = `[${i + 1}/${total}]`;
420
+ console.log(chalk.cyan(`\n${"─".repeat(60)}`));
421
+ console.log(chalk.cyan(`${progress} Pulling ${project.name || project.id}...`));
422
+ const result = await pullSingleProject(project.id, project.name, options, config, isCI);
423
+ results.push(result);
424
+ if (result.success) console.log(chalk.green(`\n ✓ ${result.projectName || result.projectId} → ${result.targetDir}`));
425
+ else console.log(chalk.red(`\n ✗ ${result.projectName || result.projectId}: ${result.error}`));
426
+ }
427
+ const succeeded = results.filter((r) => r.success).length;
428
+ const failed = results.filter((r) => !r.success).length;
429
+ console.log(chalk.cyan(`\n${"═".repeat(60)}`));
430
+ console.log(chalk.cyan("📊 Batch Pull Summary:"));
431
+ console.log(chalk.green(` ✓ Succeeded: ${succeeded}`));
432
+ if (failed > 0) {
433
+ console.log(chalk.red(` ✗ Failed: ${failed}`));
434
+ console.log(chalk.red("\nFailed projects:"));
435
+ for (const result of results) if (!result.success) console.log(chalk.red(` • ${result.projectId}: ${result.error}`));
436
+ }
437
+ process.exit(failed > 0 ? 1 : 0);
438
+ } catch (error) {
439
+ s.stop();
440
+ console.error(chalk.red(`\nError: ${error instanceof Error ? error.message : String(error)}`));
441
+ process.exit(1);
442
+ }
443
+ }
444
+ /**
445
+ * Pull a single project (used by batch operations)
446
+ * Uses smart comparison flow for existing projects, introspect for new projects
447
+ */
448
+ async function pullSingleProject(projectId, projectName, options, config, isCI) {
449
+ const targetDir = join(process.cwd(), projectId);
450
+ const hasExistingProject = existsSync(join(targetDir, "index.ts"));
451
+ try {
452
+ if (hasExistingProject) {
453
+ console.log(chalk.gray(` 📂 Existing project found - using smart comparison mode`));
454
+ const originalDir = process.cwd();
455
+ process.chdir(targetDir);
456
+ try {
457
+ const result = await pullV3Command({
458
+ ...options,
459
+ project: projectId,
460
+ all: false,
461
+ _batchMode: true
462
+ });
463
+ process.chdir(originalDir);
464
+ if (result && typeof result === "object") return {
465
+ projectId,
466
+ projectName,
467
+ targetDir,
468
+ success: result.success,
469
+ error: result.error
470
+ };
471
+ return {
472
+ projectId,
473
+ projectName,
474
+ targetDir,
475
+ success: true
476
+ };
477
+ } catch (error) {
478
+ process.chdir(originalDir);
479
+ throw error;
480
+ }
481
+ }
482
+ console.log(chalk.gray(` 🆕 New project - using introspect mode`));
483
+ const originalLogLevel = process.env.LOG_LEVEL;
484
+ process.env.LOG_LEVEL = "silent";
485
+ const restoreLogLevel = () => {
486
+ if (originalLogLevel !== void 0) process.env.LOG_LEVEL = originalLogLevel;
487
+ else delete process.env.LOG_LEVEL;
488
+ };
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);
491
+ restoreLogLevel();
492
+ return {
493
+ projectId,
494
+ projectName: projectName || remoteProject.name,
495
+ targetDir,
496
+ success: true
497
+ };
498
+ } catch (error) {
499
+ return {
500
+ projectId,
501
+ projectName,
502
+ targetDir,
503
+ success: false,
504
+ error: error instanceof Error ? error.message : String(error)
505
+ };
506
+ }
507
+ }
508
+
509
+ //#endregion
510
+ export { enrichCanDelegateToWithTypes, pullV3Command };