@packmind/cli 0.12.0 → 0.13.1

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 (2) hide show
  1. package/main.cjs +254 -62
  2. package/package.json +1 -1
package/main.cjs CHANGED
@@ -1379,13 +1379,13 @@ var require_subcommands = __commonJS({
1379
1379
  return mod && mod.__esModule ? mod : { "default": mod };
1380
1380
  };
1381
1381
  Object.defineProperty(exports2, "__esModule", { value: true });
1382
- exports2.subcommands = subcommands2;
1382
+ exports2.subcommands = subcommands3;
1383
1383
  var chalk_1 = __importDefault((init_source(), __toCommonJS(source_exports)));
1384
1384
  var didyoumean_1 = __importDefault(require_didYouMean_1_2_1());
1385
1385
  var Result = __importStar(require_Result());
1386
1386
  var circuitbreaker_1 = require_circuitbreaker();
1387
1387
  var positional_1 = require_positional();
1388
- function subcommands2(config) {
1388
+ function subcommands3(config) {
1389
1389
  const circuitbreaker = (0, circuitbreaker_1.createCircuitBreaker)(!!config.version);
1390
1390
  const type = {
1391
1391
  async from(str) {
@@ -3852,7 +3852,7 @@ var require_package = __commonJS({
3852
3852
  "apps/cli/package.json"(exports2, module2) {
3853
3853
  module2.exports = {
3854
3854
  name: "@packmind/cli",
3855
- version: "0.12.0",
3855
+ version: "0.13.1",
3856
3856
  description: "A command-line interface for Packmind linting and code quality checks",
3857
3857
  private: false,
3858
3858
  bin: {
@@ -3894,7 +3894,7 @@ var require_package = __commonJS({
3894
3894
  });
3895
3895
 
3896
3896
  // apps/cli/src/main.ts
3897
- var import_cmd_ts9 = __toESM(require_cjs());
3897
+ var import_cmd_ts10 = __toESM(require_cjs());
3898
3898
 
3899
3899
  // apps/cli/src/infra/commands/LinterCommand.ts
3900
3900
  var import_cmd_ts = __toESM(require_cjs());
@@ -4021,28 +4021,28 @@ var UserEvent = class extends PackmindEvent {
4021
4021
  };
4022
4022
 
4023
4023
  // packages/types/src/accounts/events/UserSignedUpEvent.ts
4024
- var UserSignedUpEvent = class extends PackmindEvent {
4024
+ var UserSignedUpEvent = class extends UserEvent {
4025
4025
  static {
4026
- this.eventName = "accounts.user.signed_up";
4026
+ this.eventName = "accounts.user.signed-up";
4027
4027
  }
4028
4028
  };
4029
4029
 
4030
4030
  // packages/types/src/accounts/events/UserJoinedOrganizationEvent.ts
4031
- var UserJoinedOrganizationEvent = class extends PackmindEvent {
4031
+ var UserJoinedOrganizationEvent = class extends UserEvent {
4032
4032
  static {
4033
- this.eventName = "accounts.user.joined_organization";
4033
+ this.eventName = "accounts.user.joined-organization";
4034
4034
  }
4035
4035
  };
4036
4036
 
4037
4037
  // packages/types/src/accounts/events/AnonymousTrialStartedEvent.ts
4038
- var AnonymousTrialStartedEvent = class extends PackmindEvent {
4038
+ var AnonymousTrialStartedEvent = class extends UserEvent {
4039
4039
  static {
4040
4040
  this.eventName = "accounts.anonymous-trial.started";
4041
4041
  }
4042
4042
  };
4043
4043
 
4044
4044
  // packages/types/src/accounts/events/AnonymousTrialAccountActivatedEvent.ts
4045
- var AnonymousTrialAccountActivatedEvent = class extends PackmindEvent {
4045
+ var AnonymousTrialAccountActivatedEvent = class extends UserEvent {
4046
4046
  static {
4047
4047
  this.eventName = "accounts.anonymous-trial.activated";
4048
4048
  }
@@ -4195,6 +4195,20 @@ var RuleAddedEvent = class extends UserEvent {
4195
4195
  }
4196
4196
  };
4197
4197
 
4198
+ // packages/types/src/standards/events/RuleDeletedEvent.ts
4199
+ var RuleDeletedEvent = class extends UserEvent {
4200
+ static {
4201
+ this.eventName = "standards.rule.deleted";
4202
+ }
4203
+ };
4204
+
4205
+ // packages/types/src/standards/events/RuleUpdatedEvent.ts
4206
+ var RuleUpdatedEvent = class extends UserEvent {
4207
+ static {
4208
+ this.eventName = "standards.rule.updated";
4209
+ }
4210
+ };
4211
+
4198
4212
  // packages/types/src/standards/events/StandardCreatedEvent.ts
4199
4213
  var StandardCreatedEvent = class extends UserEvent {
4200
4214
  static {
@@ -4759,6 +4773,18 @@ var ExecuteSingleFileAstUseCase = class _ExecuteSingleFileAstUseCase {
4759
4773
  // apps/cli/src/application/services/GitService.ts
4760
4774
  var import_child_process = require("child_process");
4761
4775
  var path = __toESM(require("path"));
4776
+
4777
+ // apps/cli/src/application/utils/pathUtils.ts
4778
+ function normalizePath(p) {
4779
+ return p.replace(/\\/g, "/");
4780
+ }
4781
+ function pathStartsWith(filePath, prefix) {
4782
+ const normalizedFile = normalizePath(filePath);
4783
+ const normalizedPrefix = normalizePath(prefix);
4784
+ return normalizedFile.startsWith(normalizedPrefix + "/") || normalizedFile === normalizedPrefix;
4785
+ }
4786
+
4787
+ // apps/cli/src/application/services/GitService.ts
4762
4788
  var origin = "GitService";
4763
4789
  var GitService = class {
4764
4790
  constructor(logger2 = new PackmindLogger(origin), gitRunner = (cmd, opts) => {
@@ -4958,7 +4984,7 @@ ${error.message}`
4958
4984
  const { stdout } = this.gitRunner("diff --name-only HEAD", {
4959
4985
  cwd: gitRoot
4960
4986
  });
4961
- return stdout.trim().split("\n").filter((line) => line.length > 0).map((relativePath) => path.join(gitRoot, relativePath));
4987
+ return stdout.trim().split("\n").filter((line) => line.length > 0).map((relativePath) => normalizePath(path.join(gitRoot, relativePath)));
4962
4988
  } catch (error) {
4963
4989
  if (error instanceof Error && error.message.includes("unknown revision")) {
4964
4990
  this.logger.debug(
@@ -4976,7 +5002,7 @@ ${error.message}`
4976
5002
  const { stdout } = this.gitRunner("diff --cached --name-only", {
4977
5003
  cwd: gitRoot
4978
5004
  });
4979
- return stdout.trim().split("\n").filter((line) => line.length > 0).map((relativePath) => path.join(gitRoot, relativePath));
5005
+ return stdout.trim().split("\n").filter((line) => line.length > 0).map((relativePath) => normalizePath(path.join(gitRoot, relativePath)));
4980
5006
  }
4981
5007
  /**
4982
5008
  * Gets untracked files (new files not yet added to git).
@@ -4987,7 +5013,7 @@ ${error.message}`
4987
5013
  const { stdout } = this.gitRunner("ls-files --others --exclude-standard", {
4988
5014
  cwd: gitRoot
4989
5015
  });
4990
- return stdout.trim().split("\n").filter((line) => line.length > 0).map((relativePath) => path.join(gitRoot, relativePath));
5016
+ return stdout.trim().split("\n").filter((line) => line.length > 0).map((relativePath) => normalizePath(path.join(gitRoot, relativePath)));
4991
5017
  }
4992
5018
  /**
4993
5019
  * Gets line-level diff information for modified files.
@@ -5059,7 +5085,7 @@ ${error.message}`
5059
5085
  for (const line of lines) {
5060
5086
  const fileMatch = line.match(/^diff --git a\/(.+) b\/(.+)$/);
5061
5087
  if (fileMatch) {
5062
- currentFile = path.join(gitRoot, fileMatch[2]);
5088
+ currentFile = normalizePath(path.join(gitRoot, fileMatch[2]));
5063
5089
  continue;
5064
5090
  }
5065
5091
  const hunkMatch = line.match(/^@@ -\d+(?:,\d+)? \+(\d+)(?:,(\d+))? @@/);
@@ -5319,7 +5345,7 @@ var LintFilesInDirectoryUseCase = class {
5319
5345
  directoryForGitOps
5320
5346
  );
5321
5347
  const absoluteLintPath = absoluteUserPath;
5322
- if (!absoluteLintPath.startsWith(gitRepoRoot)) {
5348
+ if (!pathStartsWith(absoluteLintPath, gitRepoRoot)) {
5323
5349
  throw new Error(
5324
5350
  `The path "${absoluteLintPath}" is not within the git repository at "${gitRepoRoot}"`
5325
5351
  );
@@ -5477,7 +5503,7 @@ var LintFilesInDirectoryUseCase = class {
5477
5503
  const violations = [];
5478
5504
  for (const file of files) {
5479
5505
  const fileViolations = [];
5480
- const relativeFilePath = file.path.startsWith(gitRepoRoot) ? file.path.substring(gitRepoRoot.length) : file.path;
5506
+ const relativeFilePath = pathStartsWith(file.path, gitRepoRoot) ? file.path.substring(gitRepoRoot.length) : file.path;
5481
5507
  const normalizedFilePath = relativeFilePath.startsWith("/") ? relativeFilePath : "/" + relativeFilePath;
5482
5508
  this.logger.debug(
5483
5509
  `Processing file: absolute="${file.path}", relative="${normalizedFilePath}"`
@@ -5764,7 +5790,7 @@ var LintFilesLocallyUseCase = class {
5764
5790
  const allStandardsChecked = /* @__PURE__ */ new Set();
5765
5791
  for (const file of files) {
5766
5792
  const fileViolations = [];
5767
- const relativeFilePath = file.path.startsWith(basePath) ? file.path.substring(basePath.length) : file.path;
5793
+ const relativeFilePath = pathStartsWith(file.path, basePath) ? file.path.substring(basePath.length) : file.path;
5768
5794
  const normalizedFilePath = relativeFilePath.startsWith("/") ? relativeFilePath : "/" + relativeFilePath;
5769
5795
  this.logger.debug(
5770
5796
  `Processing file: absolute="${file.path}", relative="${normalizedFilePath}"`
@@ -5882,7 +5908,7 @@ var LintFilesLocallyUseCase = class {
5882
5908
  */
5883
5909
  findMatchingTargets(absoluteFilePath, configs) {
5884
5910
  return configs.filter(
5885
- (config) => absoluteFilePath.startsWith(config.absoluteTargetPath + "/") || absoluteFilePath === config.absoluteTargetPath
5911
+ (config) => pathStartsWith(absoluteFilePath, config.absoluteTargetPath)
5886
5912
  );
5887
5913
  }
5888
5914
  /**
@@ -5933,6 +5959,92 @@ var LintFilesLocallyUseCase = class {
5933
5959
  // apps/cli/src/infra/utils/readSkillDirectory.ts
5934
5960
  var import_promises = __toESM(require("fs/promises"));
5935
5961
  var import_path = __toESM(require("path"));
5962
+ var import_minimatch3 = require("minimatch");
5963
+ function normalizePath2(filePath) {
5964
+ let normalized = filePath.replace(/\\/g, "/");
5965
+ if (normalized.startsWith("/") || normalized.startsWith("\\")) {
5966
+ normalized = normalized.substring(1);
5967
+ }
5968
+ return normalized;
5969
+ }
5970
+ function normalizeLineEndings(content) {
5971
+ return content.replace(/\r\n/g, "\n").replace(/\r/g, "\n");
5972
+ }
5973
+ var BINARY_EXTENSIONS = /* @__PURE__ */ new Set([
5974
+ // Images
5975
+ ".png",
5976
+ ".jpg",
5977
+ ".jpeg",
5978
+ ".gif",
5979
+ ".bmp",
5980
+ ".ico",
5981
+ ".webp",
5982
+ ".tiff",
5983
+ ".tif",
5984
+ ".heic",
5985
+ ".heif",
5986
+ ".avif",
5987
+ // Documents
5988
+ ".pdf",
5989
+ // Archives
5990
+ ".zip",
5991
+ ".tar",
5992
+ ".gz",
5993
+ ".rar",
5994
+ ".7z",
5995
+ ".bz2",
5996
+ ".xz",
5997
+ // Audio
5998
+ ".mp3",
5999
+ ".wav",
6000
+ ".ogg",
6001
+ ".flac",
6002
+ ".m4a",
6003
+ ".aac",
6004
+ // Video
6005
+ ".mp4",
6006
+ ".avi",
6007
+ ".mkv",
6008
+ ".mov",
6009
+ ".webm",
6010
+ ".wmv",
6011
+ // Executables/Libraries
6012
+ ".exe",
6013
+ ".dll",
6014
+ ".so",
6015
+ ".dylib",
6016
+ // Fonts
6017
+ ".ttf",
6018
+ ".otf",
6019
+ ".woff",
6020
+ ".woff2",
6021
+ ".eot",
6022
+ // Other binary formats
6023
+ ".bin",
6024
+ ".dat",
6025
+ ".db",
6026
+ ".sqlite",
6027
+ ".sqlite3"
6028
+ ]);
6029
+ function isBinaryExtension(filePath) {
6030
+ const ext = import_path.default.extname(filePath).toLowerCase();
6031
+ return BINARY_EXTENSIONS.has(ext);
6032
+ }
6033
+ function isBinaryBuffer(buffer) {
6034
+ return buffer.subarray(0, 8e3).includes(0);
6035
+ }
6036
+ function isBinaryFile(filePath, buffer) {
6037
+ return isBinaryExtension(filePath) || isBinaryBuffer(buffer);
6038
+ }
6039
+ var MAX_FILE_SIZE_BYTES = 10 * 1024 * 1024;
6040
+ var MAX_FILE_SIZE_MB = 10;
6041
+ var BLACKLIST_PATTERNS = ["**/.DS_Store"];
6042
+ function isBlacklisted(relativePath) {
6043
+ const normalizedPath = relativePath.replace(/\\/g, "/");
6044
+ return BLACKLIST_PATTERNS.some(
6045
+ (pattern) => (0, import_minimatch3.minimatch)(normalizedPath, pattern, { dot: true })
6046
+ );
6047
+ }
5936
6048
  async function readSkillDirectory(dirPath) {
5937
6049
  const files = [];
5938
6050
  async function readDir(currentPath, basePath) {
@@ -5940,17 +6052,36 @@ async function readSkillDirectory(dirPath) {
5940
6052
  for (const entry of entries) {
5941
6053
  const fullPath = import_path.default.join(currentPath, entry.name);
5942
6054
  const relativePath = import_path.default.relative(basePath, fullPath);
6055
+ if (isBlacklisted(relativePath)) {
6056
+ continue;
6057
+ }
5943
6058
  if (entry.isDirectory()) {
5944
6059
  await readDir(fullPath, basePath);
5945
6060
  } else if (entry.isFile()) {
5946
- const content = await import_promises.default.readFile(fullPath, "utf-8");
6061
+ const stat4 = await import_promises.default.stat(fullPath);
6062
+ if (stat4.size > MAX_FILE_SIZE_BYTES) {
6063
+ const fileSizeMB = (stat4.size / (1024 * 1024)).toFixed(2);
6064
+ throw new Error(
6065
+ `File "${relativePath}" is ${fileSizeMB} MB which exceeds the maximum allowed size of ${MAX_FILE_SIZE_MB} MB per file.`
6066
+ );
6067
+ }
6068
+ const buffer = await import_promises.default.readFile(fullPath);
6069
+ const isBinary = isBinaryFile(fullPath, buffer);
6070
+ const normalizedPath = normalizePath2(relativePath);
6071
+ let content;
6072
+ if (isBinary) {
6073
+ content = buffer.toString("base64");
6074
+ } else {
6075
+ content = normalizeLineEndings(buffer.toString("utf-8"));
6076
+ }
5947
6077
  files.push({
5948
6078
  path: fullPath,
5949
- relativePath,
6079
+ relativePath: normalizedPath,
5950
6080
  content,
5951
- size: Buffer.byteLength(content, "utf-8"),
5952
- permissions: "rw-r--r--"
6081
+ size: Buffer.byteLength(content, isBinary ? "base64" : "utf-8"),
6082
+ permissions: "rw-r--r--",
5953
6083
  // Simple default
6084
+ isBase64: isBinary
5954
6085
  });
5955
6086
  }
5956
6087
  }
@@ -6628,6 +6759,12 @@ var PackmindGateway = class {
6628
6759
  if (!files.find((f) => f.relativePath === "SKILL.md")) {
6629
6760
  throw new Error("SKILL.md not found in skill directory");
6630
6761
  }
6762
+ const MAX_FILES = 100;
6763
+ if (files.length > MAX_FILES) {
6764
+ throw new Error(
6765
+ `Skill contains ${files.length} files, but maximum allowed is ${MAX_FILES}`
6766
+ );
6767
+ }
6631
6768
  const totalSize = files.reduce((sum, f) => sum + f.size, 0);
6632
6769
  if (totalSize > 10 * 1024 * 1024) {
6633
6770
  throw new Error(`Skill size (${totalSize} bytes) exceeds 10MB limit`);
@@ -6636,7 +6773,8 @@ var PackmindGateway = class {
6636
6773
  files: files.map((f) => ({
6637
6774
  path: f.relativePath,
6638
6775
  content: f.content,
6639
- permissions: f.permissions || "rw-r--r--"
6776
+ permissions: f.permissions || "rw-r--r--",
6777
+ isBase64: f.isBase64
6640
6778
  }))
6641
6779
  };
6642
6780
  const url = `${host}/api/v0/organizations/${organizationId}/spaces/${spaceId}/skills/upload`;
@@ -6658,12 +6796,19 @@ var PackmindGateway = class {
6658
6796
  }
6659
6797
  } catch {
6660
6798
  }
6799
+ if (response.status === 409) {
6800
+ throw new Error(`Skill already exists: ${errorMsg}`);
6801
+ }
6661
6802
  throw new Error(errorMsg);
6662
6803
  }
6804
+ const isNewSkill = response.status === 201;
6663
6805
  const result = await response.json();
6664
6806
  return {
6665
- skillId: result.id,
6666
- name: result.name,
6807
+ skillId: result.skill.id,
6808
+ name: result.skill.name,
6809
+ version: result.skill.version,
6810
+ isNewSkill,
6811
+ versionCreated: result.versionCreated,
6667
6812
  fileCount: files.length,
6668
6813
  totalSize
6669
6814
  };
@@ -9527,7 +9672,8 @@ var InstallPackagesUseCase = class {
9527
9672
  filesDeleted: 0,
9528
9673
  errors: [],
9529
9674
  recipesCount: 0,
9530
- standardsCount: 0
9675
+ standardsCount: 0,
9676
+ skillsCount: 0
9531
9677
  };
9532
9678
  const response = await this.packmindGateway.getPullData({
9533
9679
  packagesSlugs: command9.packagesSlugs,
@@ -9543,6 +9689,8 @@ var InstallPackagesUseCase = class {
9543
9689
  result.recipesCount++;
9544
9690
  } else if (file.path.includes(".packmind/standards/") && file.path.endsWith(".md")) {
9545
9691
  result.standardsCount++;
9692
+ } else if (file.path.includes(".packmind/skills/") && file.path.endsWith(".md")) {
9693
+ result.skillsCount++;
9546
9694
  }
9547
9695
  }
9548
9696
  try {
@@ -9580,7 +9728,8 @@ var InstallPackagesUseCase = class {
9580
9728
  fullPath,
9581
9729
  file.content,
9582
9730
  fileExists,
9583
- result
9731
+ result,
9732
+ file.isBase64
9584
9733
  );
9585
9734
  } else if (file.sections !== void 0) {
9586
9735
  await this.handleSectionsUpdate(
@@ -9591,7 +9740,17 @@ var InstallPackagesUseCase = class {
9591
9740
  );
9592
9741
  }
9593
9742
  }
9594
- async handleFullContentUpdate(fullPath, content, fileExists, result) {
9743
+ async handleFullContentUpdate(fullPath, content, fileExists, result, isBase64) {
9744
+ if (isBase64) {
9745
+ const buffer = Buffer.from(content, "base64");
9746
+ await fs5.writeFile(fullPath, buffer);
9747
+ if (fileExists) {
9748
+ result.filesUpdated++;
9749
+ } else {
9750
+ result.filesCreated++;
9751
+ }
9752
+ return;
9753
+ }
9595
9754
  if (fileExists) {
9596
9755
  const existingContent = await fs5.readFile(fullPath, "utf-8");
9597
9756
  const commentMarker = this.extractCommentMarker(content);
@@ -10402,7 +10561,7 @@ var ConfigFileRepository = class {
10402
10561
  * @returns Array of directory paths that contain a packmind.json file
10403
10562
  */
10404
10563
  async findDescendantConfigs(directory) {
10405
- const normalizedDir = path9.resolve(directory);
10564
+ const normalizedDir = normalizePath(path9.resolve(directory));
10406
10565
  const results = [];
10407
10566
  const searchRecursively = async (currentDir) => {
10408
10567
  let entries;
@@ -10418,7 +10577,7 @@ var ConfigFileRepository = class {
10418
10577
  if (this.EXCLUDED_DIRECTORIES.includes(entry.name)) {
10419
10578
  continue;
10420
10579
  }
10421
- const entryPath = path9.join(currentDir, entry.name);
10580
+ const entryPath = normalizePath(path9.join(currentDir, entry.name));
10422
10581
  const config = await this.readConfig(entryPath);
10423
10582
  if (config) {
10424
10583
  results.push(entryPath);
@@ -10440,19 +10599,21 @@ var ConfigFileRepository = class {
10440
10599
  async readHierarchicalConfig(startDirectory, stopDirectory) {
10441
10600
  const configs = [];
10442
10601
  const configPaths = [];
10443
- const normalizedStart = path9.resolve(startDirectory);
10444
- const normalizedStop = stopDirectory ? path9.resolve(stopDirectory) : null;
10602
+ const normalizedStart = normalizePath(path9.resolve(startDirectory));
10603
+ const normalizedStop = stopDirectory ? normalizePath(path9.resolve(stopDirectory)) : null;
10445
10604
  let currentDir = normalizedStart;
10446
10605
  while (true) {
10447
10606
  const config = await this.readConfig(currentDir);
10448
10607
  if (config) {
10449
10608
  configs.push(config);
10450
- configPaths.push(path9.join(currentDir, this.CONFIG_FILENAME));
10609
+ configPaths.push(
10610
+ normalizePath(path9.join(currentDir, this.CONFIG_FILENAME))
10611
+ );
10451
10612
  }
10452
10613
  if (normalizedStop !== null && currentDir === normalizedStop) {
10453
10614
  break;
10454
10615
  }
10455
- const parentDir = path9.dirname(currentDir);
10616
+ const parentDir = normalizePath(path9.dirname(currentDir));
10456
10617
  if (parentDir === currentDir) {
10457
10618
  break;
10458
10619
  }
@@ -10481,8 +10642,8 @@ var ConfigFileRepository = class {
10481
10642
  * @returns All configs found with their target paths
10482
10643
  */
10483
10644
  async findAllConfigsInTree(startDirectory, stopDirectory) {
10484
- const normalizedStart = path9.resolve(startDirectory);
10485
- const normalizedStop = stopDirectory ? path9.resolve(stopDirectory) : null;
10645
+ const normalizedStart = normalizePath(path9.resolve(startDirectory));
10646
+ const normalizedStop = stopDirectory ? normalizePath(path9.resolve(stopDirectory)) : null;
10486
10647
  const basePath = normalizedStop ?? normalizedStart;
10487
10648
  const configsMap = /* @__PURE__ */ new Map();
10488
10649
  let currentDir = normalizedStart;
@@ -10499,7 +10660,7 @@ var ConfigFileRepository = class {
10499
10660
  if (normalizedStop !== null && currentDir === normalizedStop) {
10500
10661
  break;
10501
10662
  }
10502
- const parentDir = path9.dirname(currentDir);
10663
+ const parentDir = normalizePath(path9.dirname(currentDir));
10503
10664
  if (parentDir === currentDir) {
10504
10665
  break;
10505
10666
  }
@@ -10508,18 +10669,19 @@ var ConfigFileRepository = class {
10508
10669
  const searchRoot = normalizedStop ?? normalizedStart;
10509
10670
  const descendantDirs = await this.findDescendantConfigs(searchRoot);
10510
10671
  for (const descendantDir of descendantDirs) {
10511
- if (configsMap.has(descendantDir)) {
10672
+ const normalizedDescendantDir = normalizePath(descendantDir);
10673
+ if (configsMap.has(normalizedDescendantDir)) {
10512
10674
  continue;
10513
10675
  }
10514
- const config = await this.readConfig(descendantDir);
10676
+ const config = await this.readConfig(normalizedDescendantDir);
10515
10677
  if (config) {
10516
10678
  const targetPath = this.computeRelativeTargetPath(
10517
- descendantDir,
10679
+ normalizedDescendantDir,
10518
10680
  basePath
10519
10681
  );
10520
- configsMap.set(descendantDir, {
10682
+ configsMap.set(normalizedDescendantDir, {
10521
10683
  targetPath,
10522
- absoluteTargetPath: descendantDir,
10684
+ absoluteTargetPath: normalizedDescendantDir,
10523
10685
  packages: config.packages
10524
10686
  });
10525
10687
  }
@@ -10542,10 +10704,12 @@ var ConfigFileRepository = class {
10542
10704
  };
10543
10705
  }
10544
10706
  computeRelativeTargetPath(absolutePath, basePath) {
10545
- if (absolutePath === basePath) {
10707
+ const normalizedAbsolute = normalizePath(absolutePath);
10708
+ const normalizedBase = normalizePath(basePath);
10709
+ if (normalizedAbsolute === normalizedBase) {
10546
10710
  return "/";
10547
10711
  }
10548
- const relativePath = absolutePath.substring(basePath.length);
10712
+ const relativePath = normalizedAbsolute.substring(normalizedBase.length);
10549
10713
  return relativePath.startsWith("/") ? relativePath : "/" + relativePath;
10550
10714
  }
10551
10715
  };
@@ -11289,9 +11453,12 @@ async function executeInstallForDirectory(directory, deps) {
11289
11453
  previousPackagesSlugs: configPackages
11290
11454
  // Pass for consistency
11291
11455
  });
11292
- log(
11293
- ` Installing ${result.recipesCount} commands and ${result.standardsCount} standards...`
11294
- );
11456
+ const parts = [];
11457
+ if (result.recipesCount > 0) parts.push(`${result.recipesCount} commands`);
11458
+ if (result.standardsCount > 0)
11459
+ parts.push(`${result.standardsCount} standards`);
11460
+ if (result.skillsCount > 0) parts.push(`${result.skillsCount} skills`);
11461
+ log(` Installing ${parts.join(", ") || "artifacts"}...`);
11295
11462
  log(
11296
11463
  ` added ${result.filesCreated} files, changed ${result.filesUpdated} files, removed ${result.filesDeleted} files`
11297
11464
  );
@@ -11400,9 +11567,12 @@ async function installPackagesHandler(args2, deps) {
11400
11567
  previousPackagesSlugs: configPackages
11401
11568
  // Pass previous config for change detection
11402
11569
  });
11403
- log(
11404
- `Installing ${result.recipesCount} commands and ${result.standardsCount} standards...`
11405
- );
11570
+ const parts = [];
11571
+ if (result.recipesCount > 0) parts.push(`${result.recipesCount} commands`);
11572
+ if (result.standardsCount > 0)
11573
+ parts.push(`${result.standardsCount} standards`);
11574
+ if (result.skillsCount > 0) parts.push(`${result.skillsCount} skills`);
11575
+ log(`Installing ${parts.join(", ") || "artifacts"}...`);
11406
11576
  log(
11407
11577
  `
11408
11578
  added ${result.filesCreated} files, changed ${result.filesUpdated} files, removed ${result.filesDeleted} files`
@@ -12258,11 +12428,14 @@ Credentials are loaded from (in order of priority):`);
12258
12428
  }
12259
12429
  });
12260
12430
 
12261
- // apps/cli/src/infra/commands/UploadSkillCommand.ts
12431
+ // apps/cli/src/infra/commands/SkillsCommand.ts
12432
+ var import_cmd_ts9 = __toESM(require_cjs());
12433
+
12434
+ // apps/cli/src/infra/commands/skills/AddSkillCommand.ts
12262
12435
  var import_cmd_ts8 = __toESM(require_cjs());
12263
- var uploadSkillCommand = (0, import_cmd_ts8.command)({
12264
- name: "upload-skill",
12265
- description: "Upload a skill from a local directory to a Packmind organization",
12436
+ var addSkillCommand = (0, import_cmd_ts8.command)({
12437
+ name: "add",
12438
+ description: "Add a skill from a local directory to a Packmind organization",
12266
12439
  args: {
12267
12440
  skillPath: (0, import_cmd_ts8.positional)({
12268
12441
  type: import_cmd_ts8.string,
@@ -12280,11 +12453,21 @@ var uploadSkillCommand = (0, import_cmd_ts8.command)({
12280
12453
  organizationId: "",
12281
12454
  userId: ""
12282
12455
  });
12283
- logSuccessConsole("Skill uploaded successfully!");
12284
- console.log(` Skill ID: ${result.skillId}`);
12285
- console.log(` Name: ${result.name}`);
12286
- console.log(` Files: ${result.fileCount}`);
12287
- console.log(` Total size: ${(result.totalSize / 1024).toFixed(2)} KB`);
12456
+ if (result.isNewSkill) {
12457
+ logSuccessConsole("Skill created successfully!");
12458
+ } else if (!result.versionCreated) {
12459
+ logInfoConsole(
12460
+ `Skill content is identical to version ${result.version}, no new version created.`
12461
+ );
12462
+ } else {
12463
+ logSuccessConsole(`Skill updated to version ${result.version}!`);
12464
+ }
12465
+ logInfoConsole(` Name: ${result.name}`);
12466
+ logInfoConsole(` Version: ${result.version}`);
12467
+ logInfoConsole(` Files: ${result.fileCount}`);
12468
+ logInfoConsole(
12469
+ ` Total size: ${(result.totalSize / 1024).toFixed(2)} KB`
12470
+ );
12288
12471
  } catch (error) {
12289
12472
  if (error instanceof Error) {
12290
12473
  logErrorConsole(`Upload failed: ${error.message}`);
@@ -12296,6 +12479,15 @@ var uploadSkillCommand = (0, import_cmd_ts8.command)({
12296
12479
  }
12297
12480
  });
12298
12481
 
12482
+ // apps/cli/src/infra/commands/SkillsCommand.ts
12483
+ var skillsCommand = (0, import_cmd_ts9.subcommands)({
12484
+ name: "skills",
12485
+ description: "Manage skills in your Packmind organization",
12486
+ cmds: {
12487
+ add: addSkillCommand
12488
+ }
12489
+ });
12490
+
12299
12491
  // apps/cli/src/main.ts
12300
12492
  var { version: CLI_VERSION } = require_package();
12301
12493
  function findEnvFile() {
@@ -12335,7 +12527,7 @@ if (args.includes("--version") || args.includes("-v")) {
12335
12527
  console.log(`packmind-cli version ${CLI_VERSION}`);
12336
12528
  process.exit(0);
12337
12529
  }
12338
- var app = (0, import_cmd_ts9.subcommands)({
12530
+ var app = (0, import_cmd_ts10.subcommands)({
12339
12531
  name: "packmind-cli",
12340
12532
  description: "Packmind CLI tool",
12341
12533
  cmds: {
@@ -12348,10 +12540,10 @@ var app = (0, import_cmd_ts9.subcommands)({
12348
12540
  logout: logoutCommand,
12349
12541
  whoami: whoamiCommand,
12350
12542
  "setup-mcp": setupMcpCommand,
12351
- "upload-skill": uploadSkillCommand
12543
+ skills: skillsCommand
12352
12544
  }
12353
12545
  });
12354
- (0, import_cmd_ts9.run)(app, args).catch((error) => {
12546
+ (0, import_cmd_ts10.run)(app, args).catch((error) => {
12355
12547
  logErrorConsole(error.message);
12356
12548
  process.exit(1);
12357
12549
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@packmind/cli",
3
- "version": "0.12.0",
3
+ "version": "0.13.1",
4
4
  "description": "A command-line interface for Packmind linting and code quality checks",
5
5
  "private": false,
6
6
  "bin": {