@codemieai/cdk 0.1.632 → 0.1.633

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/cli/index.js CHANGED
@@ -2,10 +2,10 @@
2
2
  import ora from 'ora';
3
3
  import * as crypto from 'crypto';
4
4
  import { DataSourceType, CodeMieClient, SkillVisibility } from 'codemie-sdk';
5
- import * as fs6 from 'fs';
6
- import * as path6 from 'path';
7
- import path6__default from 'path';
8
- import * as yaml5 from 'yaml';
5
+ import * as fs11 from 'fs';
6
+ import * as path16 from 'path';
7
+ import path16__default from 'path';
8
+ import * as yaml6 from 'yaml';
9
9
  import { program } from '@commander-js/extra-typings';
10
10
  import { z } from 'zod';
11
11
  import 'dotenv/config';
@@ -751,15 +751,15 @@ function sanitizeFileName(name, maxLength = 255) {
751
751
  }
752
752
  function validateBackupDirectory(backupDir, minSpaceGB = 1) {
753
753
  try {
754
- const parentDir = path6.dirname(backupDir);
755
- if (!fs6.existsSync(parentDir)) {
756
- fs6.mkdirSync(parentDir, { recursive: true });
754
+ const parentDir = path16.dirname(backupDir);
755
+ if (!fs11.existsSync(parentDir)) {
756
+ fs11.mkdirSync(parentDir, { recursive: true });
757
757
  }
758
- const testFile = path6.join(parentDir, ".write-test");
759
- fs6.writeFileSync(testFile, "test");
760
- fs6.unlinkSync(testFile);
758
+ const testFile = path16.join(parentDir, ".write-test");
759
+ fs11.writeFileSync(testFile, "test");
760
+ fs11.unlinkSync(testFile);
761
761
  try {
762
- const stats = fs6.statfsSync(parentDir);
762
+ const stats = fs11.statfsSync(parentDir);
763
763
  const availableGB = stats.bavail * stats.bsize / BYTES_IN_GB;
764
764
  if (availableGB < minSpaceGB) {
765
765
  throw new Error(`Insufficient disk space: ${availableGB.toFixed(2)}GB available, need ${minSpaceGB}GB`);
@@ -775,7 +775,7 @@ function validateBackupDirectory(backupDir, minSpaceGB = 1) {
775
775
  }
776
776
  function moveAtomically(tempPath, finalPath) {
777
777
  try {
778
- fs6.renameSync(tempPath, finalPath);
778
+ fs11.renameSync(tempPath, finalPath);
779
779
  } catch (error) {
780
780
  const err = error;
781
781
  if (err.code === "EEXIST") {
@@ -785,14 +785,14 @@ function moveAtomically(tempPath, finalPath) {
785
785
  }
786
786
  }
787
787
  function cleanupDirectory(dirPath) {
788
- if (fs6.existsSync(dirPath)) {
789
- fs6.rmSync(dirPath, { recursive: true, force: true });
788
+ if (fs11.existsSync(dirPath)) {
789
+ fs11.rmSync(dirPath, { recursive: true, force: true });
790
790
  }
791
791
  }
792
792
  function ensureDirectoryExists(filePath) {
793
- const dir = path6.dirname(filePath);
794
- if (!fs6.existsSync(dir)) {
795
- fs6.mkdirSync(dir, { recursive: true });
793
+ const dir = path16.dirname(filePath);
794
+ if (!fs11.existsSync(dir)) {
795
+ fs11.mkdirSync(dir, { recursive: true });
796
796
  }
797
797
  }
798
798
  var BYTES_IN_GB;
@@ -931,7 +931,7 @@ function prepareWorkflowForYaml(workflow, state, assistants, backupDir) {
931
931
  let finalYamlContent = yamlConfig || "";
932
932
  if (yamlConfig) {
933
933
  try {
934
- const workflowYaml = yaml5.parse(yamlConfig);
934
+ const workflowYaml = yaml6.parse(yamlConfig);
935
935
  const transformedAssistants = workflowYaml.assistants?.map((assistant) => {
936
936
  if (assistant.assistant_id && typeof assistant.assistant_id === "string") {
937
937
  const assistantId = assistant.assistant_id;
@@ -946,10 +946,10 @@ function prepareWorkflowForYaml(workflow, state, assistants, backupDir) {
946
946
  if (transformedAssistants) {
947
947
  const transformedYaml = { ...workflowYaml, assistants: transformedAssistants };
948
948
  const fileName = `${sanitizeFileName(workflow.name)}.yaml`;
949
- const filePath = path6.join(backupDir, "workflows", fileName);
949
+ const filePath = path16.join(backupDir, "workflows", fileName);
950
950
  ensureDirectoryExists(filePath);
951
- finalYamlContent = yaml5.stringify(transformedYaml);
952
- fs6.writeFileSync(filePath, finalYamlContent, "utf8");
951
+ finalYamlContent = yaml6.stringify(transformedYaml);
952
+ fs11.writeFileSync(filePath, finalYamlContent, "utf8");
953
953
  }
954
954
  } catch (error) {
955
955
  logger.warn(
@@ -1023,7 +1023,7 @@ var init_backupTransformers = __esm({
1023
1023
 
1024
1024
  // package.json
1025
1025
  var package_default = {
1026
- version: "0.1.632"};
1026
+ version: "0.1.633"};
1027
1027
  var appConfigSchema = z.object({
1028
1028
  rootDir: z.string(),
1029
1029
  codemieConfig: z.string(),
@@ -1041,23 +1041,49 @@ var defaultConfig = {
1041
1041
  };
1042
1042
 
1043
1043
  // src/appConfig/configLoader.ts
1044
- function loadAppConfig(userConfigPath) {
1045
- if (!userConfigPath) {
1046
- return defaultConfig;
1044
+ init_logger();
1045
+ var PROJECT_MARKER_FILE = "codemie.yaml";
1046
+ function loadAppConfig(userConfigPath, rootDir) {
1047
+ if (userConfigPath) {
1048
+ const userConfig = loadUserConfig(userConfigPath);
1049
+ const sanitizedUserConfig = removeEmptyFields(userConfig);
1050
+ const merged = { ...defaultConfig, ...sanitizedUserConfig };
1051
+ if (sanitizedUserConfig.rootDir === void 0 && rootDir) {
1052
+ merged.rootDir = path16.resolve(rootDir);
1053
+ }
1054
+ return appConfigSchema.parse(merged);
1055
+ }
1056
+ if (rootDir) {
1057
+ return { ...defaultConfig, rootDir: path16.resolve(rootDir) };
1058
+ }
1059
+ const detectedRoot = findProjectRoot(process.cwd());
1060
+ if (detectedRoot) {
1061
+ logger.info(`Using detected project root: ${detectedRoot}`);
1062
+ return { ...defaultConfig, rootDir: detectedRoot };
1063
+ }
1064
+ return defaultConfig;
1065
+ }
1066
+ function findProjectRoot(startDir) {
1067
+ let dir = path16.resolve(startDir);
1068
+ while (true) {
1069
+ if (fs11.existsSync(path16.join(dir, PROJECT_MARKER_FILE))) {
1070
+ return dir;
1071
+ }
1072
+ const parentDir = path16.dirname(dir);
1073
+ if (parentDir === dir || dir === path16.parse(dir).root) {
1074
+ return null;
1075
+ }
1076
+ dir = parentDir;
1047
1077
  }
1048
- const userConfig = loadUserConfig(userConfigPath);
1049
- const sanitizedUserConfig = removeEmptyFields(userConfig);
1050
- const merged = { ...defaultConfig, ...sanitizedUserConfig };
1051
- return appConfigSchema.parse(merged);
1052
1078
  }
1053
1079
  function loadUserConfig(configPath) {
1054
- const resolvedPath = path6.resolve(process.cwd(), configPath);
1055
- if (!fs6.existsSync(resolvedPath)) {
1080
+ const resolvedPath = path16.resolve(process.cwd(), configPath);
1081
+ if (!fs11.existsSync(resolvedPath)) {
1056
1082
  throw new Error(`Config file not found: ${resolvedPath}`);
1057
1083
  }
1058
1084
  let raw;
1059
1085
  try {
1060
- raw = fs6.readFileSync(resolvedPath, "utf8");
1086
+ raw = fs11.readFileSync(resolvedPath, "utf8");
1061
1087
  } catch (error) {
1062
1088
  throw new Error(`Failed to read config file: ${resolvedPath}`, { cause: error });
1063
1089
  }
@@ -1119,9 +1145,9 @@ var BackupTransaction = class {
1119
1145
  isDirty = false;
1120
1146
  saveTimer;
1121
1147
  constructor(backupDir, transactionId) {
1122
- this.transactionPath = path6.join(backupDir, "transaction.json");
1148
+ this.transactionPath = path16.join(backupDir, "transaction.json");
1123
1149
  try {
1124
- const content = fs6.readFileSync(this.transactionPath, "utf8");
1150
+ const content = fs11.readFileSync(this.transactionPath, "utf8");
1125
1151
  this.data = JSON.parse(content);
1126
1152
  logger.info(`\u{1F4C2} Resuming backup transaction ${this.data.id}`);
1127
1153
  } catch (error) {
@@ -1153,9 +1179,9 @@ var BackupTransaction = class {
1153
1179
  }
1154
1180
  writeTransactionExclusively(data) {
1155
1181
  try {
1156
- const fd = fs6.openSync(this.transactionPath, "wx");
1157
- fs6.writeSync(fd, JSON.stringify(data, null, 2));
1158
- fs6.closeSync(fd);
1182
+ const fd = fs11.openSync(this.transactionPath, "wx");
1183
+ fs11.writeSync(fd, JSON.stringify(data, null, 2));
1184
+ fs11.closeSync(fd);
1159
1185
  } catch (writeError) {
1160
1186
  const writeErr = writeError;
1161
1187
  if (writeErr.code === "EEXIST") {
@@ -1174,7 +1200,7 @@ var BackupTransaction = class {
1174
1200
  if (delayMs > 0) {
1175
1201
  busyWaitDelay(delayMs);
1176
1202
  }
1177
- const content = fs6.readFileSync(this.transactionPath, "utf8");
1203
+ const content = fs11.readFileSync(this.transactionPath, "utf8");
1178
1204
  this.data = JSON.parse(content);
1179
1205
  logger.info(`\u{1F4C2} Resuming backup transaction ${this.data.id} (created by concurrent process)`);
1180
1206
  break;
@@ -1197,7 +1223,7 @@ var BackupTransaction = class {
1197
1223
  */
1198
1224
  async save() {
1199
1225
  ensureDirectoryExists(this.transactionPath);
1200
- const savePromise = fs6.promises.writeFile(this.transactionPath, JSON.stringify(this.data, null, 2), "utf8");
1226
+ const savePromise = fs11.promises.writeFile(this.transactionPath, JSON.stringify(this.data, null, 2), "utf8");
1201
1227
  let timeoutId;
1202
1228
  const timeoutPromise = new Promise((_, reject) => {
1203
1229
  timeoutId = setTimeout(
@@ -1223,7 +1249,7 @@ var BackupTransaction = class {
1223
1249
  */
1224
1250
  saveSyncInternal() {
1225
1251
  ensureDirectoryExists(this.transactionPath);
1226
- fs6.writeFileSync(this.transactionPath, JSON.stringify(this.data, null, 2), "utf8");
1252
+ fs11.writeFileSync(this.transactionPath, JSON.stringify(this.data, null, 2), "utf8");
1227
1253
  this.isDirty = false;
1228
1254
  }
1229
1255
  /**
@@ -1334,8 +1360,8 @@ var BackupTransaction = class {
1334
1360
  * Clean up transaction file after successful completion
1335
1361
  */
1336
1362
  cleanup() {
1337
- if (fs6.existsSync(this.transactionPath)) {
1338
- fs6.unlinkSync(this.transactionPath);
1363
+ if (fs11.existsSync(this.transactionPath)) {
1364
+ fs11.unlinkSync(this.transactionPath);
1339
1365
  }
1340
1366
  }
1341
1367
  };
@@ -1425,10 +1451,10 @@ function generateCodemieYaml(backup, projectName, backupDir, integrationSpecPath
1425
1451
  }
1426
1452
  }
1427
1453
  };
1428
- return yaml5.stringify(config);
1454
+ return yaml6.stringify(config);
1429
1455
  }
1430
1456
  function saveIntegrationOpenApiSpecs(backupData, backupDir) {
1431
- const specsDir = path6.join(backupDir, "openapi_specs");
1457
+ const specsDir = path16.join(backupDir, "openapi_specs");
1432
1458
  let specsCount = 0;
1433
1459
  const integrationSpecPaths = /* @__PURE__ */ new Map();
1434
1460
  for (const integration of backupData.resources.integrations) {
@@ -1441,8 +1467,8 @@ function saveIntegrationOpenApiSpecs(backupData, backupDir) {
1441
1467
  if (specEntry && typeof specEntry.value === "string" && specEntry.value) {
1442
1468
  const alias = integration.alias || integration.id;
1443
1469
  const fileName = String(alias).toLowerCase().replaceAll(/[^a-z0-9]+/g, "-");
1444
- if (!fs6.existsSync(specsDir)) {
1445
- fs6.mkdirSync(specsDir, { recursive: true });
1470
+ if (!fs11.existsSync(specsDir)) {
1471
+ fs11.mkdirSync(specsDir, { recursive: true });
1446
1472
  }
1447
1473
  const specContent = specEntry.value;
1448
1474
  let fileExtension = ".yaml";
@@ -1455,8 +1481,8 @@ function saveIntegrationOpenApiSpecs(backupData, backupDir) {
1455
1481
  fileExtension = ".yaml";
1456
1482
  }
1457
1483
  const specFileName = `${fileName}${fileExtension}`;
1458
- const specPath = path6.join(specsDir, specFileName);
1459
- fs6.writeFileSync(specPath, contentToSave, "utf8");
1484
+ const specPath = path16.join(specsDir, specFileName);
1485
+ fs11.writeFileSync(specPath, contentToSave, "utf8");
1460
1486
  integrationSpecPaths.set(integration.id, `openapi_specs/${specFileName}`);
1461
1487
  specsCount++;
1462
1488
  }
@@ -1469,15 +1495,15 @@ function saveIntegrationOpenApiSpecs(backupData, backupDir) {
1469
1495
  function saveBackupFiles(backupData, backupDir, projectName) {
1470
1496
  logger.info("\u{1F4BE} Writing backup files...\n");
1471
1497
  const integrationSpecPaths = saveIntegrationOpenApiSpecs(backupData, backupDir);
1472
- const codemieYamlPath = path6.join(backupDir, "codemie.yaml");
1498
+ const codemieYamlPath = path16.join(backupDir, "codemie.yaml");
1473
1499
  const codemieYaml = generateCodemieYaml(backupData, projectName, backupDir, integrationSpecPaths);
1474
- fs6.writeFileSync(codemieYamlPath, codemieYaml, "utf8");
1500
+ fs11.writeFileSync(codemieYamlPath, codemieYaml, "utf8");
1475
1501
  logger.info(` \u2713 Saved config file: codemie.yaml`);
1476
- const backupJsonPath = path6.join(backupDir, "backup.json");
1477
- fs6.writeFileSync(backupJsonPath, JSON.stringify(backupData, null, 2), "utf8");
1502
+ const backupJsonPath = path16.join(backupDir, "backup.json");
1503
+ fs11.writeFileSync(backupJsonPath, JSON.stringify(backupData, null, 2), "utf8");
1478
1504
  logger.info(` \u2713 Saved full backup: backup.json`);
1479
- const statePath = path6.join(backupDir, "state.json");
1480
- fs6.writeFileSync(statePath, JSON.stringify(backupData.state, null, 2), "utf8");
1505
+ const statePath = path16.join(backupDir, "state.json");
1506
+ fs11.writeFileSync(statePath, JSON.stringify(backupData.state, null, 2), "utf8");
1481
1507
  logger.info(` \u2713 Saved state file: state.json`);
1482
1508
  }
1483
1509
  async function createClient(config) {
@@ -1507,6 +1533,141 @@ function namesEqual(a, b) {
1507
1533
  return a.toLowerCase() === b.toLowerCase();
1508
1534
  }
1509
1535
 
1536
+ // src/lib/configImports.ts
1537
+ function isPlainObject3(value) {
1538
+ return typeof value === "object" && value !== null && !Array.isArray(value);
1539
+ }
1540
+ function resolveImports(obj, baseDir, visitedFiles = /* @__PURE__ */ new Set()) {
1541
+ if (!isPlainObject3(obj)) {
1542
+ return;
1543
+ }
1544
+ for (const key in obj) {
1545
+ const value = obj[key];
1546
+ if (isPlainObject3(value) && "$import" in value) {
1547
+ const importDirective = value;
1548
+ const importPath = path16.join(baseDir, importDirective.$import);
1549
+ const normalizedPath = path16.resolve(importPath);
1550
+ if (visitedFiles.has(normalizedPath)) {
1551
+ throw new Error(
1552
+ `Circular import detected: ${normalizedPath}
1553
+ Import chain: ${[...visitedFiles].join(" \u2192 ")} \u2192 ${normalizedPath}`
1554
+ );
1555
+ }
1556
+ visitedFiles.add(normalizedPath);
1557
+ const imported = loadYamlFile(importPath);
1558
+ obj[key] = imported;
1559
+ resolveImports(obj[key], path16.dirname(importPath), visitedFiles);
1560
+ visitedFiles.delete(normalizedPath);
1561
+ } else if (Array.isArray(value)) {
1562
+ obj[key] = resolveArrayImports(value, baseDir, visitedFiles);
1563
+ } else if (isPlainObject3(value)) {
1564
+ resolveImports(value, baseDir, visitedFiles);
1565
+ }
1566
+ }
1567
+ }
1568
+ function resolveArrayImports(arr, baseDir, visitedFiles = /* @__PURE__ */ new Set()) {
1569
+ const result = [];
1570
+ for (const item of arr) {
1571
+ if (isPlainObject3(item) && "$import" in item) {
1572
+ const importDirective = item;
1573
+ const importPath = path16.join(baseDir, importDirective.$import);
1574
+ const normalizedPath = path16.resolve(importPath);
1575
+ if (visitedFiles.has(normalizedPath)) {
1576
+ throw new Error(
1577
+ `Circular import detected: ${normalizedPath}
1578
+ Import chain: ${[...visitedFiles].join(" \u2192 ")} \u2192 ${normalizedPath}`
1579
+ );
1580
+ }
1581
+ visitedFiles.add(normalizedPath);
1582
+ const imported = loadYamlFile(importPath);
1583
+ if (Array.isArray(imported)) {
1584
+ const resolvedArray = resolveArrayImports(imported, path16.dirname(importPath), visitedFiles);
1585
+ for (const resolvedItem of resolvedArray) {
1586
+ result.push(resolvedItem);
1587
+ }
1588
+ } else if (isPlainObject3(imported)) {
1589
+ result.push(imported);
1590
+ resolveImports(imported, path16.dirname(importPath), visitedFiles);
1591
+ } else {
1592
+ throw new TypeError(
1593
+ `Import file ${importPath} must contain either an object or an array. Got: ${typeof imported}`
1594
+ );
1595
+ }
1596
+ visitedFiles.delete(normalizedPath);
1597
+ } else {
1598
+ result.push(item);
1599
+ if (isPlainObject3(item)) {
1600
+ resolveImports(item, baseDir, visitedFiles);
1601
+ }
1602
+ }
1603
+ }
1604
+ return result;
1605
+ }
1606
+ function loadYamlFile(filePath) {
1607
+ if (!fs11.existsSync(filePath)) {
1608
+ throw new Error(`Import file not found: ${filePath}`);
1609
+ }
1610
+ try {
1611
+ const content = fs11.readFileSync(filePath, "utf8");
1612
+ const parsed = yaml6.parse(content);
1613
+ if (parsed === null || parsed === void 0) {
1614
+ throw new TypeError(`Import file ${filePath} is empty`);
1615
+ }
1616
+ return parsed;
1617
+ } catch (error) {
1618
+ if (error instanceof Error) {
1619
+ throw new TypeError(`Failed to parse YAML file ${filePath}: ${error.message}`);
1620
+ }
1621
+ throw error;
1622
+ }
1623
+ }
1624
+ function findResourceDefinition(rootDir, codemieConfigPath, plural, name) {
1625
+ const configPath = path16.join(rootDir, codemieConfigPath);
1626
+ const config = loadYamlFile(configPath);
1627
+ if (!isPlainObject3(config) || !isPlainObject3(config.resources)) {
1628
+ return void 0;
1629
+ }
1630
+ const list = config.resources[plural];
1631
+ if (!Array.isArray(list)) {
1632
+ return void 0;
1633
+ }
1634
+ return findResourceInList(list, rootDir, null, name, /* @__PURE__ */ new Set());
1635
+ }
1636
+ function findResourceInList(items, baseDir, currentFile, name, visitedFiles) {
1637
+ for (const item of items) {
1638
+ if (!isPlainObject3(item)) {
1639
+ continue;
1640
+ }
1641
+ if (typeof item.$import === "string") {
1642
+ const importPath = path16.join(baseDir, item.$import);
1643
+ const normalizedPath = path16.resolve(importPath);
1644
+ if (visitedFiles.has(normalizedPath)) {
1645
+ throw new Error(
1646
+ `Circular import detected: ${normalizedPath}
1647
+ Import chain: ${[...visitedFiles].join(" \u2192 ")} \u2192 ${normalizedPath}`
1648
+ );
1649
+ }
1650
+ visitedFiles.add(normalizedPath);
1651
+ const loaded = loadYamlFile(importPath);
1652
+ let found;
1653
+ if (Array.isArray(loaded)) {
1654
+ found = findResourceInList(loaded, path16.dirname(importPath), normalizedPath, name, visitedFiles);
1655
+ } else if (isPlainObject3(loaded) && typeof loaded.name === "string" && namesEqual(loaded.name, name)) {
1656
+ found = { file: normalizedPath, resource: loaded, inList: false };
1657
+ }
1658
+ visitedFiles.delete(normalizedPath);
1659
+ if (found) {
1660
+ return found;
1661
+ }
1662
+ continue;
1663
+ }
1664
+ if (typeof item.name === "string" && namesEqual(item.name, name)) {
1665
+ return { file: currentFile, resource: item, inList: true };
1666
+ }
1667
+ }
1668
+ return void 0;
1669
+ }
1670
+
1510
1671
  // src/lib/codemieConfigLoader.ts
1511
1672
  var CodemieConfigLoader = class {
1512
1673
  appConfig;
@@ -1518,13 +1679,13 @@ var CodemieConfigLoader = class {
1518
1679
  */
1519
1680
  loadConfig() {
1520
1681
  const { rootDir, codemieConfig } = this.appConfig;
1521
- const configPath = path6.join(rootDir, codemieConfig);
1522
- if (!fs6.existsSync(configPath)) {
1682
+ const configPath = path16.join(rootDir, codemieConfig);
1683
+ if (!fs11.existsSync(configPath)) {
1523
1684
  throw new Error(`Configuration file not found: ${configPath}`);
1524
1685
  }
1525
- const content = fs6.readFileSync(configPath, "utf8");
1526
- const config = yaml5.parse(content);
1527
- this.resolveImports(config, rootDir);
1686
+ const content = fs11.readFileSync(configPath, "utf8");
1687
+ const config = yaml6.parse(content);
1688
+ resolveImports(config, rootDir);
1528
1689
  this.substituteEnvVars(config);
1529
1690
  this.applyDatasourceDefaults(config);
1530
1691
  this.resolveReferencesRecursive(config, config);
@@ -1536,34 +1697,34 @@ var CodemieConfigLoader = class {
1536
1697
  */
1537
1698
  loadPrompt(promptPath) {
1538
1699
  const { rootDir } = this.appConfig;
1539
- const fullPath = path6.join(rootDir, promptPath);
1540
- if (!fs6.existsSync(fullPath)) {
1700
+ const fullPath = path16.join(rootDir, promptPath);
1701
+ if (!fs11.existsSync(fullPath)) {
1541
1702
  throw new Error(`Prompt file not found: ${fullPath}`);
1542
1703
  }
1543
- return fs6.readFileSync(fullPath, "utf8");
1704
+ return fs11.readFileSync(fullPath, "utf8");
1544
1705
  }
1545
1706
  /**
1546
1707
  * Load skill content file (.skill.md)
1547
1708
  */
1548
1709
  loadSkillContent(contentPath) {
1549
1710
  const { rootDir } = this.appConfig;
1550
- const fullPath = path6.join(rootDir, contentPath);
1551
- if (!fs6.existsSync(fullPath)) {
1711
+ const fullPath = path16.join(rootDir, contentPath);
1712
+ if (!fs11.existsSync(fullPath)) {
1552
1713
  throw new Error(`Skill content file not found: ${fullPath}`);
1553
1714
  }
1554
- return fs6.readFileSync(fullPath, "utf8");
1715
+ return fs11.readFileSync(fullPath, "utf8");
1555
1716
  }
1556
1717
  /**
1557
1718
  * Load assistant configuration file
1558
1719
  */
1559
1720
  loadAssistantConfig(configPath) {
1560
1721
  const { rootDir } = this.appConfig;
1561
- const fullPath = path6.join(rootDir, configPath);
1562
- if (!fs6.existsSync(fullPath)) {
1722
+ const fullPath = path16.join(rootDir, configPath);
1723
+ if (!fs11.existsSync(fullPath)) {
1563
1724
  throw new Error(`Config file not found: ${fullPath}`);
1564
1725
  }
1565
- const content = fs6.readFileSync(fullPath, "utf8");
1566
- return yaml5.parse(content);
1726
+ const content = fs11.readFileSync(fullPath, "utf8");
1727
+ return yaml6.parse(content);
1567
1728
  }
1568
1729
  /**
1569
1730
  * Validate that all referenced files exist
@@ -1573,13 +1734,14 @@ var CodemieConfigLoader = class {
1573
1734
  const { rootDir } = this.appConfig;
1574
1735
  if (config.resources.assistants) {
1575
1736
  for (const assistant of config.resources.assistants) {
1576
- const promptPath = path6.join(rootDir, assistant.prompt);
1577
- if (!fs6.existsSync(promptPath)) {
1737
+ if (!assistant.prompt) {
1738
+ errors.push(`Missing 'prompt' for assistant: ${assistant.name}`);
1739
+ } else if (!fs11.existsSync(path16.join(rootDir, assistant.prompt))) {
1578
1740
  errors.push(`Prompt file not found for ${assistant.name}: ${assistant.prompt}`);
1579
1741
  }
1580
1742
  if (assistant.config) {
1581
- const configPath = path6.join(rootDir, assistant.config);
1582
- if (!fs6.existsSync(configPath)) {
1743
+ const configPath = path16.join(rootDir, assistant.config);
1744
+ if (!fs11.existsSync(configPath)) {
1583
1745
  errors.push(`Config file not found for ${assistant.name}: ${assistant.config}`);
1584
1746
  }
1585
1747
  }
@@ -1593,8 +1755,9 @@ var CodemieConfigLoader = class {
1593
1755
  }
1594
1756
  if (config.resources.skills) {
1595
1757
  for (const skill of config.resources.skills) {
1596
- const contentPath = path6.join(rootDir, skill.file);
1597
- if (!fs6.existsSync(contentPath)) {
1758
+ if (!skill.file) {
1759
+ errors.push(`Missing 'file' for skill: ${skill.name}`);
1760
+ } else if (!fs11.existsSync(path16.join(rootDir, skill.file))) {
1598
1761
  errors.push(`Content file not found for skill ${skill.name}: ${skill.file}`);
1599
1762
  }
1600
1763
  if (!skill.description) {
@@ -1615,109 +1778,6 @@ var CodemieConfigLoader = class {
1615
1778
  errors
1616
1779
  };
1617
1780
  }
1618
- /**
1619
- * Resolve $import directives recursively
1620
- * Automatically handles:
1621
- * 1. Single resource import (object)
1622
- * 2. Multiple resources import (array)
1623
- * 3. Mixed inline + imports
1624
- * Includes circular import detection to prevent infinite recursion
1625
- */
1626
- resolveImports(obj, baseDir, visitedFiles = /* @__PURE__ */ new Set()) {
1627
- if (!this.isPlainObject(obj)) {
1628
- return;
1629
- }
1630
- for (const key in obj) {
1631
- const value = obj[key];
1632
- if (this.isPlainObject(value) && "$import" in value) {
1633
- const importDirective = value;
1634
- const importPath = path6.join(baseDir, importDirective.$import);
1635
- const normalizedPath = path6.resolve(importPath);
1636
- if (visitedFiles.has(normalizedPath)) {
1637
- throw new Error(
1638
- `Circular import detected: ${normalizedPath}
1639
- Import chain: ${[...visitedFiles].join(" \u2192 ")} \u2192 ${normalizedPath}`
1640
- );
1641
- }
1642
- visitedFiles.add(normalizedPath);
1643
- const imported = this.loadYamlFile(importPath);
1644
- obj[key] = imported;
1645
- this.resolveImports(obj[key], path6.dirname(importPath), visitedFiles);
1646
- visitedFiles.delete(normalizedPath);
1647
- } else if (Array.isArray(value)) {
1648
- obj[key] = this.resolveArrayImports(value, baseDir, visitedFiles);
1649
- } else if (this.isPlainObject(value)) {
1650
- this.resolveImports(value, baseDir, visitedFiles);
1651
- }
1652
- }
1653
- }
1654
- /**
1655
- * Resolve imports in arrays
1656
- * Intelligently handles:
1657
- * - Inline objects (keep as-is)
1658
- * - $import with single object (add to array)
1659
- * - $import with array (flatten into parent array)
1660
- * Includes circular import detection
1661
- */
1662
- resolveArrayImports(arr, baseDir, visitedFiles = /* @__PURE__ */ new Set()) {
1663
- const result = [];
1664
- for (const item of arr) {
1665
- if (this.isPlainObject(item) && "$import" in item) {
1666
- const importDirective = item;
1667
- const importPath = path6.join(baseDir, importDirective.$import);
1668
- const normalizedPath = path6.resolve(importPath);
1669
- if (visitedFiles.has(normalizedPath)) {
1670
- throw new Error(
1671
- `Circular import detected: ${normalizedPath}
1672
- Import chain: ${[...visitedFiles].join(" \u2192 ")} \u2192 ${normalizedPath}`
1673
- );
1674
- }
1675
- visitedFiles.add(normalizedPath);
1676
- const imported = this.loadYamlFile(importPath);
1677
- if (Array.isArray(imported)) {
1678
- const resolvedArray = this.resolveArrayImports(imported, path6.dirname(importPath), visitedFiles);
1679
- for (const resolvedItem of resolvedArray) {
1680
- result.push(resolvedItem);
1681
- }
1682
- } else if (this.isPlainObject(imported)) {
1683
- result.push(imported);
1684
- this.resolveImports(imported, path6.dirname(importPath), visitedFiles);
1685
- } else {
1686
- throw new TypeError(
1687
- `Import file ${importPath} must contain either an object or an array. Got: ${typeof imported}`
1688
- );
1689
- }
1690
- visitedFiles.delete(normalizedPath);
1691
- } else {
1692
- result.push(item);
1693
- if (this.isPlainObject(item)) {
1694
- this.resolveImports(item, baseDir, visitedFiles);
1695
- }
1696
- }
1697
- }
1698
- return result;
1699
- }
1700
- /**
1701
- * Load YAML file with error handling
1702
- */
1703
- loadYamlFile(filePath) {
1704
- if (!fs6.existsSync(filePath)) {
1705
- throw new Error(`Import file not found: ${filePath}`);
1706
- }
1707
- try {
1708
- const content = fs6.readFileSync(filePath, "utf8");
1709
- const parsed = yaml5.parse(content);
1710
- if (parsed === null || parsed === void 0) {
1711
- throw new TypeError(`Import file ${filePath} is empty`);
1712
- }
1713
- return parsed;
1714
- } catch (error) {
1715
- if (error instanceof Error) {
1716
- throw new TypeError(`Failed to parse YAML file ${filePath}: ${error.message}`);
1717
- }
1718
- throw error;
1719
- }
1720
- }
1721
1781
  /**
1722
1782
  * Recursively resolve all $ref in an object/array structure
1723
1783
  * Handles both object references { $ref: "path" } and string references "$ref:path"
@@ -1726,28 +1786,28 @@ Import chain: ${[...visitedFiles].join(" \u2192 ")} \u2192 ${normalizedPath}`
1726
1786
  * @param rootConfig - Root config object (constant reference for resolving paths like "imported.integrations.xxx")
1727
1787
  * @param path - Current path in config tree (for error messages, e.g., "resources.assistants[0].toolkits")
1728
1788
  */
1729
- resolveReferencesRecursive(current, rootConfig, path16 = "") {
1789
+ resolveReferencesRecursive(current, rootConfig, path17 = "") {
1730
1790
  if (!current || typeof current !== "object") {
1731
1791
  return;
1732
1792
  }
1733
1793
  if (Array.isArray(current)) {
1734
- this.resolveArrayReferences(current, rootConfig, path16);
1794
+ this.resolveArrayReferences(current, rootConfig, path17);
1735
1795
  return;
1736
1796
  }
1737
1797
  if ("$ref" in current && typeof current.$ref === "string") {
1738
- this.resolveObjectReference(current, rootConfig, path16);
1798
+ this.resolveObjectReference(current, rootConfig, path17);
1739
1799
  return;
1740
1800
  }
1741
- this.resolveObjectProperties(current, rootConfig, path16);
1801
+ this.resolveObjectProperties(current, rootConfig, path17);
1742
1802
  }
1743
1803
  /**
1744
1804
  * Resolve $ref items in arrays and flatten if they point to arrays
1745
1805
  * Example: [{ $ref: "context_definitions.repos" }] where repos is [item1, item2]
1746
1806
  * becomes [item1, item2]
1747
1807
  */
1748
- resolveArrayReferences(arr, rootConfig, path16) {
1808
+ resolveArrayReferences(arr, rootConfig, path17) {
1749
1809
  const result = arr.flatMap((item, i) => {
1750
- const contextPath = `${path16}[${i}]`;
1810
+ const contextPath = `${path17}[${i}]`;
1751
1811
  if (!this.isRefObject(item) || item.$ref.startsWith("#")) {
1752
1812
  this.resolveReferencesRecursive(item, rootConfig, contextPath);
1753
1813
  return [item];
@@ -1765,9 +1825,9 @@ Import chain: ${[...visitedFiles].join(" \u2192 ")} \u2192 ${normalizedPath}`
1765
1825
  * Resolve object reference: { $ref: "path" }
1766
1826
  * Replaces object with resolved data (in-place mutation)
1767
1827
  */
1768
- resolveObjectReference(current, rootConfig, path16) {
1828
+ resolveObjectReference(current, rootConfig, path17) {
1769
1829
  const refPath = current.$ref;
1770
- const contextPath = path16 || "root";
1830
+ const contextPath = path17 || "root";
1771
1831
  if (refPath.startsWith("#")) {
1772
1832
  return;
1773
1833
  }
@@ -1777,24 +1837,24 @@ Import chain: ${[...visitedFiles].join(" \u2192 ")} \u2192 ${normalizedPath}`
1777
1837
  delete current[key];
1778
1838
  }
1779
1839
  Object.assign(current, filteredResolved);
1780
- this.resolveReferencesRecursive(current, rootConfig, path16);
1840
+ this.resolveReferencesRecursive(current, rootConfig, path17);
1781
1841
  }
1782
1842
  /**
1783
1843
  * Resolve object properties recursively
1784
1844
  * Handles both nested objects and "$ref:path" string references
1785
1845
  */
1786
- resolveObjectProperties(current, rootConfig, path16) {
1846
+ resolveObjectProperties(current, rootConfig, path17) {
1787
1847
  for (const [key, value] of Object.entries(current)) {
1788
1848
  if (typeof value === "string" && value.startsWith("$ref:")) {
1789
1849
  const refPath = value.slice(5);
1790
1850
  if (refPath.startsWith("#")) {
1791
1851
  continue;
1792
1852
  }
1793
- const contextPath = path16 ? `${path16}.${key}` : key;
1853
+ const contextPath = path17 ? `${path17}.${key}` : key;
1794
1854
  const resolved = this.resolveReference(rootConfig, refPath, contextPath);
1795
1855
  current[key] = resolved;
1796
1856
  } else {
1797
- this.resolveReferencesRecursive(value, rootConfig, path16 ? `${path16}.${key}` : key);
1857
+ this.resolveReferencesRecursive(value, rootConfig, path17 ? `${path17}.${key}` : key);
1798
1858
  }
1799
1859
  }
1800
1860
  }
@@ -2045,9 +2105,9 @@ async function saveAssistantToBackup(assistant, client, backupData, backupDir) {
2045
2105
  backupData.resources.assistants.push(full);
2046
2106
  if (full.system_prompt) {
2047
2107
  const fileName = `${full.slug || sanitizeFileName(full.name)}.prompt.md`;
2048
- const filePath = path6.join(backupDir, "system_prompts", fileName);
2108
+ const filePath = path16.join(backupDir, "system_prompts", fileName);
2049
2109
  ensureDirectoryExists(filePath);
2050
- fs6.writeFileSync(filePath, full.system_prompt, "utf8");
2110
+ fs11.writeFileSync(filePath, full.system_prompt, "utf8");
2051
2111
  }
2052
2112
  }
2053
2113
  async function backupAssistants(client, backupData, backupDir, transaction) {
@@ -2173,9 +2233,9 @@ async function backupWorkflows(client, backupData, backupDir, transaction) {
2173
2233
  backupData.resources.workflows.push(full);
2174
2234
  if (full.yaml_config) {
2175
2235
  const fileName = `${sanitizeFileName(full.name)}.yaml`;
2176
- const filePath = path6.join(backupDir, "workflows", fileName);
2236
+ const filePath = path16.join(backupDir, "workflows", fileName);
2177
2237
  ensureDirectoryExists(filePath);
2178
- fs6.writeFileSync(filePath, full.yaml_config, "utf8");
2238
+ fs11.writeFileSync(filePath, full.yaml_config, "utf8");
2179
2239
  }
2180
2240
  transaction.markCompleted("workflows", workflow.id);
2181
2241
  } catch (error) {
@@ -2278,9 +2338,9 @@ async function backupSkills(client, backupData, backupDir, transaction, projectN
2278
2338
  backupData.resources.skills.push(full);
2279
2339
  if (fullIsProjectOwned) {
2280
2340
  const fileName = `${sanitizeFileName(skill.name)}.skill.md`;
2281
- const filePath = path6.join(backupDir, "skills", fileName);
2341
+ const filePath = path16.join(backupDir, "skills", fileName);
2282
2342
  ensureDirectoryExists(filePath);
2283
- fs6.writeFileSync(filePath, full.content || "", "utf8");
2343
+ fs11.writeFileSync(filePath, full.content || "", "utf8");
2284
2344
  } else {
2285
2345
  logger.info(` \u21B7 Skipping content file for imported skill ${skill.name} (owner: ${full.project})`);
2286
2346
  }
@@ -2302,17 +2362,17 @@ async function backupSkills(client, backupData, backupDir, transaction, projectN
2302
2362
  }
2303
2363
  function getUniqueBackupDir(baseDir, timestamp) {
2304
2364
  let counter = 0;
2305
- let finalBackupDir = path6.join(baseDir, timestamp);
2306
- while (fs6.existsSync(finalBackupDir)) {
2365
+ let finalBackupDir = path16.join(baseDir, timestamp);
2366
+ while (fs11.existsSync(finalBackupDir)) {
2307
2367
  counter++;
2308
- finalBackupDir = path6.join(baseDir, `${timestamp}-${counter}`);
2368
+ finalBackupDir = path16.join(baseDir, `${timestamp}-${counter}`);
2309
2369
  }
2310
2370
  const dirSuffix = counter > 0 ? `${timestamp}-${counter}` : timestamp;
2311
- const tempBackupDir = path6.join(baseDir, `${BACKUP.TEMP_DIR_PREFIX}${dirSuffix}`);
2371
+ const tempBackupDir = path16.join(baseDir, `${BACKUP.TEMP_DIR_PREFIX}${dirSuffix}`);
2312
2372
  return { finalDir: finalBackupDir, tempDir: tempBackupDir };
2313
2373
  }
2314
2374
  function performCleanup(tempBackupDir, transaction) {
2315
- if (fs6.existsSync(tempBackupDir)) {
2375
+ if (fs11.existsSync(tempBackupDir)) {
2316
2376
  try {
2317
2377
  logger.info(`\u{1F9F9} Rolling back: cleaning up ${tempBackupDir}...`);
2318
2378
  cleanupDirectory(tempBackupDir);
@@ -2341,7 +2401,7 @@ async function backupResources(options) {
2341
2401
  const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/\.\d{3}Z$/, "").replaceAll(":", "-");
2342
2402
  const { appConfig } = options;
2343
2403
  const { rootDir, backupsDirectory } = appConfig;
2344
- const backupBaseDir = path6.join(rootDir, backupsDirectory);
2404
+ const backupBaseDir = path16.join(rootDir, backupsDirectory);
2345
2405
  const { finalDir: finalBackupDir, tempDir: tempBackupDir } = getUniqueBackupDir(backupBaseDir, timestamp);
2346
2406
  let transaction = null;
2347
2407
  try {
@@ -2351,8 +2411,8 @@ async function backupResources(options) {
2351
2411
  logger.info("\u{1F50C} Connecting to Codemie API...");
2352
2412
  const client = await createClient(config);
2353
2413
  logger.info("\u2713 Connected to Codemie API\n");
2354
- if (!fs6.existsSync(tempBackupDir)) {
2355
- fs6.mkdirSync(tempBackupDir, { recursive: true });
2414
+ if (!fs11.existsSync(tempBackupDir)) {
2415
+ fs11.mkdirSync(tempBackupDir, { recursive: true });
2356
2416
  }
2357
2417
  logger.info(`\u{1F4C1} Temporary backup directory: ${tempBackupDir}
2358
2418
  `);
@@ -2933,16 +2993,16 @@ var StateManager = class {
2933
2993
  statePath;
2934
2994
  constructor(appConfig) {
2935
2995
  const { rootDir, codemieState } = appConfig;
2936
- this.statePath = path6.join(rootDir, codemieState);
2996
+ this.statePath = path16.join(rootDir, codemieState);
2937
2997
  }
2938
2998
  /**
2939
2999
  * Load state file
2940
3000
  */
2941
3001
  loadState() {
2942
- if (!fs6.existsSync(this.statePath)) {
3002
+ if (!fs11.existsSync(this.statePath)) {
2943
3003
  return this.createEmptyState();
2944
3004
  }
2945
- const content = fs6.readFileSync(this.statePath, "utf8");
3005
+ const content = fs11.readFileSync(this.statePath, "utf8");
2946
3006
  const state = JSON.parse(content);
2947
3007
  if (!state.resources) {
2948
3008
  state.resources = {
@@ -2970,12 +3030,12 @@ var StateManager = class {
2970
3030
  * Save state file
2971
3031
  */
2972
3032
  saveState(state) {
2973
- const dir = path6.dirname(this.statePath);
2974
- if (!fs6.existsSync(dir)) {
2975
- fs6.mkdirSync(dir, { recursive: true });
3033
+ const dir = path16.dirname(this.statePath);
3034
+ if (!fs11.existsSync(dir)) {
3035
+ fs11.mkdirSync(dir, { recursive: true });
2976
3036
  }
2977
3037
  state.lastSync = (/* @__PURE__ */ new Date()).toISOString();
2978
- fs6.writeFileSync(this.statePath, JSON.stringify(state, null, 2));
3038
+ fs11.writeFileSync(this.statePath, JSON.stringify(state, null, 2));
2979
3039
  }
2980
3040
  /**
2981
3041
  * Create empty state structure
@@ -3674,12 +3734,12 @@ async function deployWorkflows(config, client, stateManager, rootDir = process.c
3674
3734
  for (const workflow of config.resources.workflows) {
3675
3735
  try {
3676
3736
  logger.info(`Processing: ${workflow.name}`);
3677
- const yamlPath = path6.join(rootDir, workflow.definition);
3678
- if (!fs6.existsSync(yamlPath)) {
3737
+ const yamlPath = path16.join(rootDir, workflow.definition);
3738
+ if (!fs11.existsSync(yamlPath)) {
3679
3739
  throw new Error(`Workflow definition file not found: ${workflow.definition}`);
3680
3740
  }
3681
- const yamlConfigContent = fs6.readFileSync(yamlPath, "utf8");
3682
- const workflowYaml = yaml5.parse(yamlConfigContent);
3741
+ const yamlConfigContent = fs11.readFileSync(yamlPath, "utf8");
3742
+ const workflowYaml = yaml6.parse(yamlConfigContent);
3683
3743
  const referencesToResolve = [];
3684
3744
  for (const assistant of workflowYaml.assistants ?? []) {
3685
3745
  const isInline = !assistant.assistant_name && assistant.model && assistant.system_prompt;
@@ -3720,7 +3780,7 @@ async function deployWorkflows(config, client, stateManager, rootDir = process.c
3720
3780
  });
3721
3781
  }
3722
3782
  }
3723
- const yamlConfig = yaml5.stringify(workflowYaml);
3783
+ const yamlConfig = yaml6.stringify(workflowYaml);
3724
3784
  const workflowYamlChecksum = calculateChecksum(yamlConfigContent);
3725
3785
  const configChecksum = calculateWorkflowConfigChecksum(workflow);
3726
3786
  const existingState = stateManager.getWorkflowState(workflow.name);
@@ -4050,14 +4110,34 @@ async function main3(options) {
4050
4110
  }
4051
4111
 
4052
4112
  // src/import.ts
4113
+ init_checksumUtils();
4114
+ init_codemieConfigChecksums();
4053
4115
  init_converters();
4054
- init_skillUtils();
4055
4116
  init_fileUtils();
4056
4117
  init_logger();
4057
- init_checksumUtils();
4058
- init_codemieConfigChecksums();
4118
+
4119
+ // src/lib/resourceTypes.ts
4059
4120
  var VALID_RESOURCE_TYPES = ["assistant", "datasource", "workflow", "skill"];
4121
+ var PLURAL_BY_RESOURCE_TYPE = {
4122
+ assistant: "assistants",
4123
+ datasource: "datasources",
4124
+ workflow: "workflows",
4125
+ skill: "skills"
4126
+ };
4127
+ function pluralTypeOf(resourceType) {
4128
+ return PLURAL_BY_RESOURCE_TYPE[resourceType];
4129
+ }
4130
+
4131
+ // src/import.ts
4132
+ init_skillUtils();
4060
4133
  async function findAssistant(client, slug) {
4134
+ if (UUID_RE.test(slug)) {
4135
+ return withTimeout(
4136
+ client.assistants.get(slug),
4137
+ TIMEOUTS_MS.ASSISTANT_FETCH,
4138
+ `Timeout fetching assistant "${slug}"`
4139
+ );
4140
+ }
4061
4141
  try {
4062
4142
  const assistant = await withTimeout(
4063
4143
  client.assistants.getBySlug(slug),
@@ -4141,7 +4221,7 @@ Please use a more specific name.`);
4141
4221
  return matches[0];
4142
4222
  }
4143
4223
  function checkResourceExists2(config, resourceType, name) {
4144
- const pluralType = resourceType === "assistant" ? "assistants" : resourceType === "datasource" ? "datasources" : resourceType === "workflow" ? "workflows" : "skills";
4224
+ const pluralType = pluralTypeOf(resourceType);
4145
4225
  const resources = config.resources[pluralType] || [];
4146
4226
  if (resources.some((r) => namesEqual(r.name, name))) {
4147
4227
  return true;
@@ -4152,68 +4232,252 @@ function checkResourceExists2(config, resourceType, name) {
4152
4232
  }
4153
4233
  return false;
4154
4234
  }
4235
+ function findManagedNameById(stateManager, resourceType, id) {
4236
+ const state = stateManager.loadState();
4237
+ const pluralType = pluralTypeOf(resourceType);
4238
+ const resources = state.resources[pluralType] || {};
4239
+ const matches = Object.keys(resources).filter((name) => resources[name]?.id === id);
4240
+ if (matches.length > 1) {
4241
+ throw new Error(
4242
+ `state.json is ambiguous: ${matches.length} ${pluralType} entries share platform id ${id} (${matches.map((name) => `"${name}"`).join(", ")}). Remove the duplicate entries so this ${resourceType} can be matched unambiguously.`
4243
+ );
4244
+ }
4245
+ return matches[0];
4246
+ }
4247
+ function nameClashError(resourceType, name) {
4248
+ return new Error(
4249
+ `A ${resourceType} named "${name}" already exists in the configuration. Re-run with --update to replace it with the platform version, or remove it first.`
4250
+ );
4251
+ }
4252
+ function assertResolvedNameIsFree(config, stateManager, resourceType, slug, resolvedName, id, update) {
4253
+ if (!update && !namesEqual(slug, resolvedName) && checkResourceExists2(config, resourceType, resolvedName)) {
4254
+ throw nameClashError(resourceType, resolvedName);
4255
+ }
4256
+ const managedName = findManagedNameById(stateManager, resourceType, id);
4257
+ if (!update && managedName !== void 0 && !namesEqual(managedName, resolvedName)) {
4258
+ throw new Error(
4259
+ `This ${resourceType} (id ${id}) is already managed by this project under the name "${managedName}", but is now named "${resolvedName}" on the platform.`
4260
+ );
4261
+ }
4262
+ return managedName;
4263
+ }
4155
4264
  function writeResourceFiles(rootDir, resourceType, resource, apiResponse) {
4156
4265
  const safeName = sanitizeFileName(resource.name);
4266
+ const target = resource;
4157
4267
  if (resourceType === "workflow") {
4158
4268
  const workflow = apiResponse;
4159
4269
  const workflowResource = resource;
4160
4270
  if (workflow.yaml_config) {
4161
4271
  const defFileName = `${safeName}.yaml`;
4162
- const defPath = path6.join(rootDir, "workflows", defFileName);
4272
+ const defPath = path16.join(rootDir, "workflows", defFileName);
4163
4273
  ensureDirectoryExists(defPath);
4164
- fs6.writeFileSync(defPath, workflow.yaml_config, "utf8");
4274
+ fs11.writeFileSync(defPath, workflow.yaml_config, "utf8");
4165
4275
  logger.info(` \u{1F4CB} Saved workflow definition: workflows/${defFileName}`);
4166
4276
  workflowResource.definition = `workflows/${defFileName}`;
4277
+ } else {
4278
+ delete target.definition;
4167
4279
  }
4168
- const configPath = path6.join(rootDir, "configs", "workflows", `${safeName}.yaml`);
4280
+ const configPath = path16.join(rootDir, "configs", "workflows", `${safeName}.yaml`);
4169
4281
  const relativeConfigPath = `configs/workflows/${safeName}.yaml`;
4170
4282
  ensureDirectoryExists(configPath);
4171
- const yamlContent2 = yaml5.stringify(workflowResource, { lineWidth: 120 });
4172
- fs6.writeFileSync(configPath, yamlContent2, "utf8");
4283
+ const yamlContent2 = yaml6.stringify(workflowResource, { lineWidth: 120 });
4284
+ fs11.writeFileSync(configPath, yamlContent2, "utf8");
4173
4285
  logger.info(` \u{1F4C4} Saved workflow config: ${relativeConfigPath}`);
4174
4286
  return relativeConfigPath;
4175
4287
  }
4176
- const pluralType = resourceType === "assistant" ? "assistants" : resourceType === "datasource" ? "datasources" : "skills";
4177
- const resourceDir = path6.join(rootDir, "configs", "imported-resources", pluralType);
4178
- const resourceFilePath = path6.join(resourceDir, `${safeName}.yaml`);
4288
+ const pluralType = pluralTypeOf(resourceType);
4289
+ const resourceDir = path16.join(rootDir, "configs", "imported-resources", pluralType);
4290
+ const resourceFilePath = path16.join(resourceDir, `${safeName}.yaml`);
4179
4291
  const relativeResourcePath = `configs/imported-resources/${pluralType}/${safeName}.yaml`;
4180
4292
  if (resourceType === "assistant") {
4181
4293
  const assistant = apiResponse;
4182
4294
  const assistantResource = resource;
4183
4295
  if (assistant.system_prompt) {
4184
4296
  const promptFileName = assistant.slug || safeName;
4185
- const promptPath = path6.join(rootDir, "system_prompts", `${promptFileName}.prompt.md`);
4297
+ const promptPath = path16.join(rootDir, "system_prompts", `${promptFileName}.prompt.md`);
4186
4298
  ensureDirectoryExists(promptPath);
4187
- fs6.writeFileSync(promptPath, assistant.system_prompt, "utf8");
4299
+ fs11.writeFileSync(promptPath, assistant.system_prompt, "utf8");
4188
4300
  logger.info(` \u{1F4DD} Saved prompt: system_prompts/${promptFileName}.prompt.md`);
4189
4301
  assistantResource.prompt = `system_prompts/${promptFileName}.prompt.md`;
4302
+ } else {
4303
+ delete target.prompt;
4190
4304
  }
4191
4305
  } else if (resourceType === "skill") {
4192
4306
  const skill = apiResponse;
4193
4307
  const skillResource = resource;
4194
4308
  if (skill.content) {
4195
4309
  const skillFileName = `${safeName}.skill.md`;
4196
- const skillPath = path6.join(rootDir, "skills", skillFileName);
4310
+ const skillPath = path16.join(rootDir, "skills", skillFileName);
4197
4311
  ensureDirectoryExists(skillPath);
4198
- fs6.writeFileSync(skillPath, skill.content, "utf8");
4312
+ fs11.writeFileSync(skillPath, skill.content, "utf8");
4199
4313
  logger.info(` \u{1F9E0} Saved skill content: skills/${skillFileName}`);
4200
4314
  skillResource.file = `skills/${skillFileName}`;
4315
+ } else {
4316
+ delete target.file;
4201
4317
  }
4202
4318
  }
4203
4319
  ensureDirectoryExists(resourceFilePath);
4204
- const yamlContent = yaml5.stringify(resource, { lineWidth: 120 });
4205
- fs6.writeFileSync(resourceFilePath, yamlContent, "utf8");
4320
+ const yamlContent = yaml6.stringify(resource, { lineWidth: 120 });
4321
+ fs11.writeFileSync(resourceFilePath, yamlContent, "utf8");
4206
4322
  logger.info(` \u{1F4C4} Saved resource config: ${relativeResourcePath}`);
4207
4323
  return relativeResourcePath;
4208
4324
  }
4325
+ function planCompanionWrite(rootDir, target, key, configuredValue, content, defaultPath, label) {
4326
+ const configuredPath = typeof configuredValue === "string" ? configuredValue : void 0;
4327
+ if (!content) {
4328
+ if (configuredPath === void 0) {
4329
+ delete target[key];
4330
+ } else {
4331
+ target[key] = configuredPath;
4332
+ }
4333
+ return void 0;
4334
+ }
4335
+ const relPath = configuredPath ?? defaultPath;
4336
+ target[key] = relPath;
4337
+ return { absPath: path16.join(rootDir, relPath), content, log: ` ${label}: ${relPath}` };
4338
+ }
4339
+ var IN_LIST_ITEM_INDENT = 2;
4340
+ function itemCarriesAnchorAliasOrMergeKey(node) {
4341
+ if (node.anchor !== void 0) {
4342
+ return true;
4343
+ }
4344
+ return node.items.some((pair) => {
4345
+ const key = pair.key;
4346
+ const keyValue = yaml6.isScalar(key) ? key.value : key;
4347
+ if (keyValue === "<<") {
4348
+ return true;
4349
+ }
4350
+ const value = pair.value;
4351
+ if (yaml6.isAlias(value)) {
4352
+ return true;
4353
+ }
4354
+ return yaml6.isNode(value) && value.anchor !== void 0;
4355
+ });
4356
+ }
4357
+ function spliceResourceIntoList(filePath, relativeResourcePath, resource, existingName) {
4358
+ const source = fs11.readFileSync(filePath, "utf8");
4359
+ const doc = yaml6.parseDocument(source);
4360
+ const refusal = (reason) => new Error(
4361
+ `Cannot safely update "${existingName}" inside ${relativeResourcePath}: ${reason}. Update ${relativeResourcePath} manually.`
4362
+ );
4363
+ if (!yaml6.isSeq(doc.contents) || doc.contents.flow) {
4364
+ throw refusal("the file is not a block-style YAML sequence");
4365
+ }
4366
+ const node = doc.contents.items.find((item) => {
4367
+ if (!yaml6.isMap(item)) {
4368
+ return false;
4369
+ }
4370
+ const itemName = item.get("name");
4371
+ return typeof itemName === "string" && namesEqual(itemName, existingName);
4372
+ });
4373
+ if (!node?.range) {
4374
+ throw refusal("its entry could not be located as a block mapping in the sequence");
4375
+ }
4376
+ if (node.flow) {
4377
+ throw refusal("its entry uses flow style ({ ... })");
4378
+ }
4379
+ if (itemCarriesAnchorAliasOrMergeKey(node)) {
4380
+ throw refusal("its entry uses a YAML anchor, alias or merge key");
4381
+ }
4382
+ const [start, valueEnd] = node.range;
4383
+ const indent = start - (source.lastIndexOf("\n", start - 1) + 1);
4384
+ if (indent !== IN_LIST_ITEM_INDENT) {
4385
+ throw refusal(`its entry is indented by ${indent} spaces instead of ${IN_LIST_ITEM_INDENT}`);
4386
+ }
4387
+ const rendered = yaml6.stringify(resource, { lineWidth: 120 }).trimEnd();
4388
+ const replacement = rendered.split("\n").map((line, index) => index === 0 || !line ? line : `${" ".repeat(IN_LIST_ITEM_INDENT)}${line}`).join("\n");
4389
+ const tail = source.slice(valueEnd);
4390
+ const separator = tail.startsWith("\n") ? "" : "\n";
4391
+ return source.slice(0, start) + replacement + separator + tail;
4392
+ }
4393
+ function updateResourceFiles(rootDir, resourceType, resource, apiResponse, existing) {
4394
+ const filePath = existing.file;
4395
+ if (!filePath) {
4396
+ throw new Error("updateResourceFiles requires an existing resource backed by a file");
4397
+ }
4398
+ const existingResource = existing.resource;
4399
+ const target = resource;
4400
+ const safeName = sanitizeFileName(resource.name);
4401
+ for (const [key, value] of Object.entries(existingResource)) {
4402
+ if (key === "skill_ids" || key in target) {
4403
+ continue;
4404
+ }
4405
+ target[key] = value;
4406
+ }
4407
+ let companion;
4408
+ if (resourceType === "assistant") {
4409
+ const assistant = apiResponse;
4410
+ companion = planCompanionWrite(
4411
+ rootDir,
4412
+ target,
4413
+ "prompt",
4414
+ existingResource.prompt,
4415
+ assistant.system_prompt,
4416
+ `system_prompts/${assistant.slug || safeName}.prompt.md`,
4417
+ "\u{1F4DD} Updated prompt"
4418
+ );
4419
+ } else if (resourceType === "skill") {
4420
+ const skill = apiResponse;
4421
+ companion = planCompanionWrite(
4422
+ rootDir,
4423
+ target,
4424
+ "file",
4425
+ existingResource.file,
4426
+ skill.content,
4427
+ `skills/${safeName}.skill.md`,
4428
+ "\u{1F9E0} Updated skill content"
4429
+ );
4430
+ } else if (resourceType === "workflow") {
4431
+ const workflow = apiResponse;
4432
+ companion = planCompanionWrite(
4433
+ rootDir,
4434
+ target,
4435
+ "definition",
4436
+ existingResource.definition,
4437
+ workflow.yaml_config,
4438
+ `workflows/${safeName}.yaml`,
4439
+ "\u{1F4CB} Updated workflow definition"
4440
+ );
4441
+ }
4442
+ const relativeResourcePath = path16.relative(rootDir, filePath);
4443
+ const newResourceFileContent = existing.inList ? spliceResourceIntoList(filePath, relativeResourcePath, resource, String(existingResource.name)) : yaml6.stringify(resource, { lineWidth: 120 });
4444
+ if (companion) {
4445
+ ensureDirectoryExists(companion.absPath);
4446
+ fs11.writeFileSync(companion.absPath, companion.content, "utf8");
4447
+ logger.info(companion.log);
4448
+ }
4449
+ fs11.writeFileSync(filePath, newResourceFileContent, "utf8");
4450
+ logger.info(` \u{1F4C4} Updated resource config: ${relativeResourcePath}`);
4451
+ return relativeResourcePath;
4452
+ }
4453
+ function deleteStateEntry(stateManager, resourceType, name) {
4454
+ switch (resourceType) {
4455
+ case "assistant": {
4456
+ stateManager.deleteAssistantState(name);
4457
+ break;
4458
+ }
4459
+ case "datasource": {
4460
+ stateManager.deleteDatasourceState(name);
4461
+ break;
4462
+ }
4463
+ case "workflow": {
4464
+ stateManager.deleteWorkflowState(name);
4465
+ break;
4466
+ }
4467
+ case "skill": {
4468
+ stateManager.deleteSkillState(name);
4469
+ break;
4470
+ }
4471
+ }
4472
+ }
4209
4473
  function addImportToCodemieYaml(rootDir, codemieConfigPath, resourceType, relativeResourcePath) {
4210
- const configPath = path6.join(rootDir, codemieConfigPath);
4211
- if (!fs6.existsSync(configPath)) {
4474
+ const configPath = path16.join(rootDir, codemieConfigPath);
4475
+ if (!fs11.existsSync(configPath)) {
4212
4476
  throw new Error(`Configuration file not found: ${configPath}`);
4213
4477
  }
4214
- const content = fs6.readFileSync(configPath, "utf8");
4215
- const doc = yaml5.parseDocument(content);
4216
- const pluralType = resourceType === "assistant" ? "assistants" : resourceType === "datasource" ? "datasources" : resourceType === "workflow" ? "workflows" : "skills";
4478
+ const content = fs11.readFileSync(configPath, "utf8");
4479
+ const doc = yaml6.parseDocument(content);
4480
+ const pluralType = pluralTypeOf(resourceType);
4217
4481
  let resourcesNode = doc.get("resources");
4218
4482
  if (!resourcesNode) {
4219
4483
  resourcesNode = doc.createNode({});
@@ -4226,7 +4490,7 @@ function addImportToCodemieYaml(rootDir, codemieConfigPath, resourceType, relati
4226
4490
  }
4227
4491
  const importDirective = { $import: relativeResourcePath };
4228
4492
  const existingImports = typeArray.items.filter((item) => {
4229
- if (yaml5.isMap(item)) {
4493
+ if (yaml6.isMap(item)) {
4230
4494
  const importValue = item.get("$import");
4231
4495
  return importValue === relativeResourcePath;
4232
4496
  }
@@ -4238,16 +4502,16 @@ function addImportToCodemieYaml(rootDir, codemieConfigPath, resourceType, relati
4238
4502
  }
4239
4503
  const newNode = doc.createNode(importDirective);
4240
4504
  typeArray.add(newNode);
4241
- fs6.writeFileSync(configPath, doc.toString(), "utf8");
4505
+ fs11.writeFileSync(configPath, doc.toString(), "utf8");
4242
4506
  logger.info(` \u2705 Added $import to ${codemieConfigPath} \u2192 resources.${pluralType}`);
4243
4507
  }
4244
4508
  function addImportedSkillToCodemieYaml(rootDir, codemieConfigPath, skill) {
4245
- const configPath = path6.join(rootDir, codemieConfigPath);
4246
- if (!fs6.existsSync(configPath)) {
4509
+ const configPath = path16.join(rootDir, codemieConfigPath);
4510
+ if (!fs11.existsSync(configPath)) {
4247
4511
  throw new Error(`Configuration file not found: ${configPath}`);
4248
4512
  }
4249
- const content = fs6.readFileSync(configPath, "utf8");
4250
- const doc = yaml5.parseDocument(content);
4513
+ const content = fs11.readFileSync(configPath, "utf8");
4514
+ const doc = yaml6.parseDocument(content);
4251
4515
  let importedNode = doc.get("imported");
4252
4516
  if (!importedNode) {
4253
4517
  importedNode = doc.createNode({ assistants: [], datasources: [], skills: [], integrations: [] });
@@ -4259,7 +4523,7 @@ function addImportedSkillToCodemieYaml(rootDir, codemieConfigPath, skill) {
4259
4523
  doc.setIn(["imported", "skills"], skillsArray);
4260
4524
  }
4261
4525
  const existingSkill = skillsArray.items.find((item) => {
4262
- if (!yaml5.isMap(item)) {
4526
+ if (!yaml6.isMap(item)) {
4263
4527
  return false;
4264
4528
  }
4265
4529
  const existingName = item.get("name");
@@ -4276,9 +4540,47 @@ function addImportedSkillToCodemieYaml(rootDir, codemieConfigPath, skill) {
4276
4540
  return;
4277
4541
  }
4278
4542
  skillsArray.add(doc.createNode({ name: skill.name, id: skill.id }));
4279
- fs6.writeFileSync(configPath, doc.toString(), "utf8");
4543
+ fs11.writeFileSync(configPath, doc.toString(), "utf8");
4280
4544
  logger.info(` \u2705 Added skill to ${codemieConfigPath} \u2192 imported.skills`);
4281
4545
  }
4546
+ function updateImportedSkillInCodemieYaml(rootDir, codemieConfigPath, skill) {
4547
+ const configPath = path16.join(rootDir, codemieConfigPath);
4548
+ if (!fs11.existsSync(configPath)) {
4549
+ throw new Error(`Configuration file not found: ${configPath}`);
4550
+ }
4551
+ const content = fs11.readFileSync(configPath, "utf8");
4552
+ const doc = yaml6.parseDocument(content);
4553
+ const skillsArray = doc.getIn(["imported", "skills"]);
4554
+ if (!skillsArray) {
4555
+ return false;
4556
+ }
4557
+ const items = skillsArray.items.filter((item) => yaml6.isMap(item));
4558
+ const entryById = items.find((item) => {
4559
+ const existingId = item.get("id");
4560
+ return typeof existingId === "string" && existingId === skill.id;
4561
+ });
4562
+ const entry = entryById ?? items.find((item) => {
4563
+ const existingName = item.get("name");
4564
+ return typeof existingName === "string" && namesEqual(existingName, skill.name);
4565
+ });
4566
+ if (!entry) {
4567
+ return false;
4568
+ }
4569
+ entry.set("name", skill.name);
4570
+ entry.set("id", skill.id);
4571
+ fs11.writeFileSync(configPath, doc.toString(), "utf8");
4572
+ return true;
4573
+ }
4574
+ function upsertImportedSkill(rootDir, codemieConfigPath, skill, update) {
4575
+ if (update) {
4576
+ if (updateImportedSkillInCodemieYaml(rootDir, codemieConfigPath, skill)) {
4577
+ logger.info(` \u267B\uFE0F Refreshed imported.skills entry for external skill "${skill.name}"`);
4578
+ return;
4579
+ }
4580
+ logger.info(` No existing skill found in the configuration \u2014 importing as new`);
4581
+ }
4582
+ addImportedSkillToCodemieYaml(rootDir, codemieConfigPath, skill);
4583
+ }
4282
4584
  function ensureExternalSkillDoesNotConflictWithManagedSkill(config, skillName) {
4283
4585
  const managedSkill = config.resources.skills?.find((skill) => namesEqual(skill.name, skillName));
4284
4586
  if (!managedSkill) {
@@ -4331,7 +4633,7 @@ function buildIntegrationAliasMap(config) {
4331
4633
  return map;
4332
4634
  }
4333
4635
  async function importResource(options) {
4334
- const { appConfig, resourceType, slug } = options;
4636
+ const { appConfig, resourceType, slug, update = false } = options;
4335
4637
  if (!VALID_RESOURCE_TYPES.includes(resourceType)) {
4336
4638
  throw new Error(`Invalid resource type: "${resourceType}". Must be one of: ${VALID_RESOURCE_TYPES.join(", ")}`);
4337
4639
  }
@@ -4339,10 +4641,8 @@ async function importResource(options) {
4339
4641
  const config = configLoader.loadConfig();
4340
4642
  const rootDir = appConfig.rootDir;
4341
4643
  const stateManager = new StateManager(appConfig);
4342
- if (checkResourceExists2(config, resourceType, slug)) {
4343
- throw new Error(
4344
- `A ${resourceType} named "${slug}" already exists in the configuration. Remove it first or use a different name.`
4345
- );
4644
+ if (!update && checkResourceExists2(config, resourceType, slug)) {
4645
+ throw nameClashError(resourceType, slug);
4346
4646
  }
4347
4647
  logger.info(`
4348
4648
  \u{1F50D} Importing ${resourceType}: "${slug}"
@@ -4352,11 +4652,21 @@ async function importResource(options) {
4352
4652
  const integrationAliasMap = buildIntegrationAliasMap(config);
4353
4653
  let resource;
4354
4654
  let apiResponse;
4655
+ let managedName;
4355
4656
  switch (resourceType) {
4356
4657
  case "assistant": {
4357
4658
  logger.info(` Searching for assistant "${slug}"...`);
4358
4659
  const assistant = await findAssistant(client, slug);
4359
4660
  logger.info(` \u2713 Found: ${assistant.name} (${assistant.id})`);
4661
+ managedName = assertResolvedNameIsFree(
4662
+ config,
4663
+ stateManager,
4664
+ resourceType,
4665
+ slug,
4666
+ assistant.name,
4667
+ assistant.id,
4668
+ update
4669
+ );
4360
4670
  apiResponse = assistant;
4361
4671
  resource = assistantResponseToResource(assistant);
4362
4672
  if (integrationAliasMap.size > 0) {
@@ -4390,7 +4700,7 @@ async function importResource(options) {
4390
4700
  logger.info(` \u2713 Resolved skill ${skillId.slice(0, 8)}... \u2192 "${skill.name}"`);
4391
4701
  if (!isProjectOwnedSkill(skill, config.project.name)) {
4392
4702
  ensureExternalSkillDoesNotConflictWithManagedSkill(config, skill.name);
4393
- addImportedSkillToCodemieYaml(rootDir, appConfig.codemieConfig, { name: skill.name, id: skill.id });
4703
+ upsertImportedSkill(rootDir, appConfig.codemieConfig, { name: skill.name, id: skill.id }, update);
4394
4704
  }
4395
4705
  }
4396
4706
  if (skillNames.length > 0) {
@@ -4403,6 +4713,15 @@ async function importResource(options) {
4403
4713
  logger.info(` Searching for datasource "${slug}"...`);
4404
4714
  const datasource = await findDatasource(client, slug);
4405
4715
  logger.info(` \u2713 Found: ${datasource.name} (${datasource.id})`);
4716
+ managedName = assertResolvedNameIsFree(
4717
+ config,
4718
+ stateManager,
4719
+ resourceType,
4720
+ slug,
4721
+ datasource.name,
4722
+ datasource.id,
4723
+ update
4724
+ );
4406
4725
  apiResponse = datasource;
4407
4726
  const settingId = datasource.setting_id || "";
4408
4727
  const integrationAlias = integrationAliasMap.get(settingId);
@@ -4413,11 +4732,15 @@ async function importResource(options) {
4413
4732
  logger.info(` Searching for workflow "${slug}"...`);
4414
4733
  const workflow = await findWorkflow(client, slug);
4415
4734
  logger.info(` \u2713 Found: ${workflow.name} (${workflow.id})`);
4416
- if (workflow.name !== slug && checkResourceExists2(config, resourceType, workflow.name)) {
4417
- throw new Error(
4418
- `A ${resourceType} named "${workflow.name}" already exists in the configuration. Remove it first or use a different name.`
4419
- );
4420
- }
4735
+ managedName = assertResolvedNameIsFree(
4736
+ config,
4737
+ stateManager,
4738
+ resourceType,
4739
+ slug,
4740
+ workflow.name,
4741
+ workflow.id,
4742
+ update
4743
+ );
4421
4744
  apiResponse = workflow;
4422
4745
  resource = workflowResponseToResource(workflow);
4423
4746
  break;
@@ -4426,24 +4749,44 @@ async function importResource(options) {
4426
4749
  logger.info(` Searching for skill "${slug}"...`);
4427
4750
  const skill = await findSkill(client, slug);
4428
4751
  logger.info(` \u2713 Found: ${skill.name} (${skill.id})`);
4429
- if (skill.name !== slug && checkResourceExists2(config, resourceType, skill.name)) {
4430
- throw new Error(
4431
- `A ${resourceType} named "${skill.name}" already exists in the configuration. Remove it first or use a different name.`
4432
- );
4433
- }
4752
+ managedName = assertResolvedNameIsFree(config, stateManager, resourceType, slug, skill.name, skill.id, update);
4434
4753
  if (isProjectOwnedSkill(skill, config.project.name)) {
4435
4754
  logger.info(` Project-owned skill \u2014 importing into resources.skills`);
4436
4755
  apiResponse = skill;
4437
4756
  resource = skillResponseToResource(skill);
4438
4757
  break;
4439
4758
  }
4440
- addImportedSkillToCodemieYaml(rootDir, appConfig.codemieConfig, { name: skill.name, id: skill.id });
4759
+ upsertImportedSkill(rootDir, appConfig.codemieConfig, { name: skill.name, id: skill.id }, update);
4441
4760
  logger.info(`
4442
4761
  \u2705 Successfully imported external skill "${skill.name}" into imported.skills
4443
4762
  `);
4444
4763
  return;
4445
4764
  }
4446
4765
  }
4766
+ if (update) {
4767
+ const pluralType = pluralTypeOf(resourceType);
4768
+ const lookupName = managedName ?? resource.name;
4769
+ const existing = findResourceDefinition(rootDir, appConfig.codemieConfig, pluralType, lookupName);
4770
+ if (existing) {
4771
+ if (existing.file === null) {
4772
+ throw new Error(
4773
+ `Cannot update "${resource.name}": it is not defined in a file referenced by codemie.yaml. Update it manually.`
4774
+ );
4775
+ }
4776
+ logger.info(` \u267B\uFE0F Updating existing ${resourceType} config in place...`);
4777
+ updateResourceFiles(rootDir, resourceType, resource, apiResponse, existing);
4778
+ if (managedName !== void 0 && !namesEqual(managedName, resource.name)) {
4779
+ logger.info(` \u{1F3F7}\uFE0F Renamed from "${managedName}" to "${resource.name}" \u2014 updating state.json`);
4780
+ deleteStateEntry(stateManager, resourceType, managedName);
4781
+ }
4782
+ updateState(stateManager, resourceType, resource, apiResponse);
4783
+ logger.info(`
4784
+ \u2705 Successfully imported ${resourceType} "${resource.name}"
4785
+ `);
4786
+ return;
4787
+ }
4788
+ logger.info(` No existing ${resourceType} found in the configuration \u2014 importing as new`);
4789
+ }
4447
4790
  const relativeResourcePath = writeResourceFiles(rootDir, resourceType, resource, apiResponse);
4448
4791
  addImportToCodemieYaml(rootDir, appConfig.codemieConfig, resourceType, relativeResourcePath);
4449
4792
  updateState(stateManager, resourceType, resource, apiResponse);
@@ -4464,7 +4807,7 @@ async function main4(options) {
4464
4807
  }
4465
4808
  init_logger();
4466
4809
  function fileExists(filePath) {
4467
- return fs6.existsSync(filePath);
4810
+ return fs11.existsSync(filePath);
4468
4811
  }
4469
4812
  function generateEnvTemplate() {
4470
4813
  return `# Codemie API Configuration
@@ -4543,14 +4886,14 @@ function compareConfigWithDefaults(userConfig) {
4543
4886
  }
4544
4887
  async function saveConfigFile(config, defaultFilename = "config.json") {
4545
4888
  const configFilename = await promptUser(`Enter config file path`, defaultFilename);
4546
- const configPath = path6.resolve(process.cwd(), configFilename);
4889
+ const configPath = path16.resolve(process.cwd(), configFilename);
4547
4890
  if (fileExists(configPath)) {
4548
4891
  const shouldOverwrite = await confirmOverwrite(configFilename);
4549
4892
  if (!shouldOverwrite) {
4550
4893
  throw new Error(`Cancelled: Configuration file not saved.`);
4551
4894
  }
4552
4895
  }
4553
- fs6.writeFileSync(configPath, JSON.stringify(config, null, 2), "utf-8");
4896
+ fs11.writeFileSync(configPath, JSON.stringify(config, null, 2), "utf-8");
4554
4897
  return configFilename;
4555
4898
  }
4556
4899
  function suggestConfigUsage(configFileName) {
@@ -4569,8 +4912,8 @@ async function initProject(options) {
4569
4912
  logger.info("\u{1F680} Initialize Codemie IaC Project\n\n");
4570
4913
  try {
4571
4914
  const { appConfig } = options;
4572
- const envPath = path6.resolve(appConfig.rootDir, ".env");
4573
- const configPath = path6.resolve(appConfig.rootDir, appConfig.codemieConfig);
4915
+ const envPath = path16.resolve(appConfig.rootDir, ".env");
4916
+ const configPath = path16.resolve(appConfig.rootDir, appConfig.codemieConfig);
4574
4917
  logger.info("This wizard will create the following files:");
4575
4918
  logger.info(` \u{1F4C4} .env - Environment variables with API credentials`);
4576
4919
  logger.info(` \u{1F4C4} ${appConfig.codemieConfig} - Codemie IaC configuration
@@ -4623,7 +4966,7 @@ async function initProject(options) {
4623
4966
  logger.info("\n\u{1F4C1} Creating configuration files...\n");
4624
4967
  if (shouldCreateEnv) {
4625
4968
  const envContent = generateEnvTemplate();
4626
- fs6.writeFileSync(envPath, envContent, "utf-8");
4969
+ fs11.writeFileSync(envPath, envContent, "utf-8");
4627
4970
  logger.info(`\u2713 Created .env`);
4628
4971
  logger.warn(` \u26A0\uFE0F Remember to fill in your actual credentials!`);
4629
4972
  logger.warn(` \u26A0\uFE0F DO NOT commit .env to Git!
@@ -4631,7 +4974,7 @@ async function initProject(options) {
4631
4974
  }
4632
4975
  if (shouldCreateConfig) {
4633
4976
  const configContent = generateMinimalCodemieYaml(projectName, projectDescription);
4634
- fs6.writeFileSync(configPath, configContent, "utf-8");
4977
+ fs11.writeFileSync(configPath, configContent, "utf-8");
4635
4978
  logger.info(`\u2713 Created ${appConfig.codemieConfig}`);
4636
4979
  logger.info(` See examples/ directory for detailed configuration examples
4637
4980
 
@@ -4665,6 +5008,7 @@ async function main5(options) {
4665
5008
  // src/cli/index.ts
4666
5009
  init_commandSpinner();
4667
5010
  init_logger();
5011
+ init_logger();
4668
5012
 
4669
5013
  // src/lib/previewUtils.ts
4670
5014
  init_checksumUtils();
@@ -4895,11 +5239,11 @@ async function previewWorkflows(workflows, stateManager, client, rootDir = proce
4895
5239
  const projectClause = project ? ` in project ${project}` : "";
4896
5240
  const staleDetails = `ID from state not found; will adopt a same-name workflow${projectClause} if one exists, otherwise create`;
4897
5241
  for (const workflow of workflows) {
4898
- const yamlPath = path6.join(rootDir, workflow.definition);
4899
- if (!fs6.existsSync(yamlPath)) {
5242
+ const yamlPath = path16.join(rootDir, workflow.definition);
5243
+ if (!fs11.existsSync(yamlPath)) {
4900
5244
  throw new Error(`Workflow definition file not found: ${workflow.definition}`);
4901
5245
  }
4902
- const yamlConfig = fs6.readFileSync(yamlPath, "utf8");
5246
+ const yamlConfig = fs11.readFileSync(yamlPath, "utf8");
4903
5247
  const change = await previewResource(
4904
5248
  workflow,
4905
5249
  "workflow",
@@ -5087,10 +5431,10 @@ var DependencyValidator = class {
5087
5431
  const errors = [];
5088
5432
  const visited = /* @__PURE__ */ new Set();
5089
5433
  const recursionStack = /* @__PURE__ */ new Set();
5090
- const dfs = (name, path16) => {
5434
+ const dfs = (name, path17) => {
5091
5435
  if (recursionStack.has(name)) {
5092
- const cycleStart = path16.indexOf(name);
5093
- const cycle = [...path16.slice(cycleStart), name];
5436
+ const cycleStart = path17.indexOf(name);
5437
+ const cycle = [...path17.slice(cycleStart), name];
5094
5438
  errors.push(`Cyclic dependency detected: ${cycle.join(" \u2192 ")}`);
5095
5439
  return true;
5096
5440
  }
@@ -5099,14 +5443,14 @@ var DependencyValidator = class {
5099
5443
  }
5100
5444
  visited.add(name);
5101
5445
  recursionStack.add(name);
5102
- path16.push(name);
5446
+ path17.push(name);
5103
5447
  const assistant = assistantMap.get(name);
5104
5448
  const subAssistantRefs = assistant?.sub_assistants || [];
5105
5449
  for (const subRef of subAssistantRefs) {
5106
5450
  if (!assistantMap.has(subRef)) {
5107
5451
  continue;
5108
5452
  }
5109
- if (dfs(subRef, [...path16])) {
5453
+ if (dfs(subRef, [...path17])) {
5110
5454
  return true;
5111
5455
  }
5112
5456
  }
@@ -5165,7 +5509,7 @@ var DependencyValidator = class {
5165
5509
  }
5166
5510
  for (const workflow of workflows) {
5167
5511
  try {
5168
- const workflowYaml = yaml5.parse(workflow.definition);
5512
+ const workflowYaml = yaml6.parse(workflow.definition);
5169
5513
  if (!workflowYaml.assistants) {
5170
5514
  continue;
5171
5515
  }
@@ -5305,12 +5649,12 @@ async function validateConfig(options) {
5305
5649
  }
5306
5650
  if (config.resources.workflows && config.resources.workflows.length > 0) {
5307
5651
  const workflowsWithContent = config.resources.workflows.map((workflow) => {
5308
- const workflowPath = path6.resolve(workflow.definition);
5309
- if (!fs6.existsSync(workflowPath)) {
5652
+ const workflowPath = path16.resolve(workflow.definition);
5653
+ if (!fs11.existsSync(workflowPath)) {
5310
5654
  errors.push(`Workflow definition file not found: ${workflow.definition}`);
5311
5655
  return { ...workflow, definition: "" };
5312
5656
  }
5313
- const definition = fs6.readFileSync(workflowPath, "utf8");
5657
+ const definition = fs11.readFileSync(workflowPath, "utf8");
5314
5658
  return { ...workflow, definition };
5315
5659
  });
5316
5660
  const workflowErrors = DependencyValidator.validateWorkflowAssistantReferences(
@@ -5374,55 +5718,62 @@ async function main7(options) {
5374
5718
 
5375
5719
  // src/cli/index.ts
5376
5720
  program.name("codemie").description("Infrastructure as Code for Codemie platform").version(package_default.version);
5377
- program.command("init").description("Initialize a new Codemie IaC project").action(() => {
5378
- const appConfig = loadAppConfig();
5721
+ program.command("init").description("Initialize a new Codemie IaC project").option("-d, --root-dir <path>", "Project root directory (containing codemie.yaml)").action((options) => {
5722
+ const rootDir = options.rootDir ? path16__default.resolve(options.rootDir) : void 0;
5723
+ const appConfig = loadAppConfig(void 0, rootDir ?? process.cwd());
5379
5724
  void main5({ appConfig });
5380
5725
  });
5381
- program.command("deploy").description("Deploy resources to Codemie platform").option("-p, --prune", "Delete orphaned resources").option("--skip-datasources", "Skip the datasource stage entirely (no create/update/delete, no indexing)").option("-c, --config <path>", "Configuration file").action(async (options) => {
5382
- const configPath = options.config ? path6__default.resolve(options.config) : void 0;
5383
- const appConfig = loadAppConfig(configPath);
5726
+ program.command("deploy").description("Deploy resources to Codemie platform").option("-p, --prune", "Delete orphaned resources").option("--skip-datasources", "Skip the datasource stage entirely (no create/update/delete, no indexing)").option("-d, --root-dir <path>", "Project root directory (containing codemie.yaml)").option("-c, --config <path>", "Configuration file").action(async (options) => {
5727
+ const configPath = options.config ? path16__default.resolve(options.config) : void 0;
5728
+ const rootDir = options.rootDir ? path16__default.resolve(options.rootDir) : void 0;
5729
+ const appConfig = loadAppConfig(configPath, rootDir);
5384
5730
  await runCommandWithSpinner(
5385
5731
  "Deploying Codemie resources",
5386
5732
  () => main2({ appConfig, prune: options.prune, skipDatasources: options.skipDatasources })
5387
5733
  );
5388
5734
  });
5389
- program.command("backup").description("Backup all resources from Codemie platform").option("-c, --config <path>", "Configuration file").action(async (options) => {
5390
- const configPath = options.config ? path6__default.resolve(options.config) : void 0;
5391
- const appConfig = loadAppConfig(configPath);
5735
+ program.command("backup").description("Backup all resources from Codemie platform").option("-d, --root-dir <path>", "Project root directory (containing codemie.yaml)").option("-c, --config <path>", "Configuration file").action(async (options) => {
5736
+ const configPath = options.config ? path16__default.resolve(options.config) : void 0;
5737
+ const rootDir = options.rootDir ? path16__default.resolve(options.rootDir) : void 0;
5738
+ const appConfig = loadAppConfig(configPath, rootDir);
5392
5739
  await runCommandWithSpinner("Backing up Codemie resources", () => main({ appConfig }));
5393
5740
  });
5394
- program.command("destroy").description("Destroy all IaC-managed resources").option("-f, --force", "Skip confirmation prompt").option("-c, --config <path>", "Configuration file").action(async (options) => {
5395
- const configPath = options.config ? path6__default.resolve(options.config) : void 0;
5396
- const appConfig = loadAppConfig(configPath);
5741
+ program.command("destroy").description("Destroy all IaC-managed resources").option("-f, --force", "Skip confirmation prompt").option("-d, --root-dir <path>", "Project root directory (containing codemie.yaml)").option("-c, --config <path>", "Configuration file").action(async (options) => {
5742
+ const configPath = options.config ? path16__default.resolve(options.config) : void 0;
5743
+ const rootDir = options.rootDir ? path16__default.resolve(options.rootDir) : void 0;
5744
+ const appConfig = loadAppConfig(configPath, rootDir);
5397
5745
  await runCommandWithSpinner("Destroying Codemie resources", () => main3({ appConfig, force: options.force }));
5398
5746
  });
5399
- program.command("preview").description("Preview changes without applying them").option("--skip-datasources", "Exclude datasources from preview (mirrors deploy --skip-datasources)").option("-c, --config <path>", "Configuration file").action(async (options) => {
5400
- const appConfigPath = options.config ? path6__default.resolve(options.config) : void 0;
5401
- const appConfig = loadAppConfig(appConfigPath);
5747
+ program.command("preview").description("Preview changes without applying them").option("--skip-datasources", "Exclude datasources from preview (mirrors deploy --skip-datasources)").option("-d, --root-dir <path>", "Project root directory (containing codemie.yaml)").option("-c, --config <path>", "Configuration file").action(async (options) => {
5748
+ const appConfigPath = options.config ? path16__default.resolve(options.config) : void 0;
5749
+ const rootDir = options.rootDir ? path16__default.resolve(options.rootDir) : void 0;
5750
+ const appConfig = loadAppConfig(appConfigPath, rootDir);
5402
5751
  await runCommandWithSpinner(
5403
5752
  "Generating Codemie preview",
5404
5753
  () => main6({ appConfig, skipDatasources: options.skipDatasources })
5405
5754
  );
5406
5755
  });
5407
- program.command("validate").description("Validate configuration").option("-a, --check-api", "Check for API conflicts").option("-c, --config <path>", "Configuration file").action(async (options) => {
5408
- const configPath = options.config ? path6__default.resolve(options.config) : void 0;
5409
- const appConfig = loadAppConfig(configPath);
5756
+ program.command("validate").description("Validate configuration").option("-a, --check-api", "Check for API conflicts").option("-d, --root-dir <path>", "Project root directory (containing codemie.yaml)").option("-c, --config <path>", "Configuration file").action(async (options) => {
5757
+ const configPath = options.config ? path16__default.resolve(options.config) : void 0;
5758
+ const rootDir = options.rootDir ? path16__default.resolve(options.rootDir) : void 0;
5759
+ const appConfig = loadAppConfig(configPath, rootDir);
5410
5760
  await runCommandWithSpinner(
5411
5761
  "Validating Codemie configuration",
5412
5762
  () => main7({ appConfig, checkApi: options.checkApi })
5413
5763
  );
5414
5764
  });
5415
- program.command("import").description("Import a single resource from the CodeMie platform into the local IaC configuration").argument("<type>", "Resource type: assistant, datasource, workflow, or skill").argument("<slug>", "Resource slug or name to import").option("-c, --config <path>", "Configuration file").action(async (type, slug, options) => {
5416
- const validTypes = ["assistant", "datasource", "workflow", "skill"];
5417
- if (!validTypes.includes(type)) {
5765
+ program.command("import").description("Import a single resource from the CodeMie platform into the local IaC configuration").argument("<type>", "Resource type: assistant, datasource, workflow, or skill").argument("<slug>", "Resource slug or name (a platform UUID also works for assistant and workflow)").option("-d, --root-dir <path>", "Project root directory (containing codemie.yaml)").option("-c, --config <path>", "Configuration file").option("-u, --update", "Refresh an already-managed resource in place instead of failing on a conflict").action(async (type, slug, options) => {
5766
+ if (!VALID_RESOURCE_TYPES.includes(type)) {
5767
+ logger.error(`\u274C Invalid resource type: "${type}". Must be one of: ${VALID_RESOURCE_TYPES.join(", ")}`);
5418
5768
  process.exitCode = 1;
5419
5769
  return;
5420
5770
  }
5421
- const configPath = options.config ? path6__default.resolve(options.config) : void 0;
5422
- const appConfig = loadAppConfig(configPath);
5771
+ const configPath = options.config ? path16__default.resolve(options.config) : void 0;
5772
+ const rootDir = options.rootDir ? path16__default.resolve(options.rootDir) : void 0;
5773
+ const appConfig = loadAppConfig(configPath, rootDir);
5423
5774
  await runCommandWithSpinner(
5424
5775
  `Importing ${type}`,
5425
- () => main4({ appConfig, resourceType: type, slug })
5776
+ () => main4({ appConfig, resourceType: type, slug, update: options.update })
5426
5777
  );
5427
5778
  });
5428
5779
  program.parse();