@packmind/cli 0.21.1 → 0.22.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/main.cjs +794 -380
  2. package/package.json +1 -1
package/main.cjs CHANGED
@@ -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.21.1",
3855
+ version: "0.22.0",
3856
3856
  description: "A command-line interface for Packmind linting and code quality checks",
3857
3857
  private: false,
3858
3858
  bin: {
@@ -4265,6 +4265,13 @@ var createDistributionId = brandedIdFactory();
4265
4265
  // packages/types/src/deployments/DistributedPackageId.ts
4266
4266
  var createDistributedPackageId = brandedIdFactory();
4267
4267
 
4268
+ // packages/types/src/deployments/events/ArtefactRemovedFromPackageEvent.ts
4269
+ var ArtefactRemovedFromPackageEvent = class extends UserEvent {
4270
+ static {
4271
+ this.eventName = "deployments.artefact.removed";
4272
+ }
4273
+ };
4274
+
4268
4275
  // packages/types/src/deployments/events/ArtifactsPulledEvent.ts
4269
4276
  var ArtifactsPulledEvent = class extends UserEvent {
4270
4277
  static {
@@ -5131,6 +5138,7 @@ var ExecuteSingleFileAstUseCase = class _ExecuteSingleFileAstUseCase {
5131
5138
 
5132
5139
  // apps/cli/src/application/services/GitService.ts
5133
5140
  var import_child_process = require("child_process");
5141
+ var import_fs = require("fs");
5134
5142
  var path = __toESM(require("path"));
5135
5143
 
5136
5144
  // apps/cli/src/application/utils/pathUtils.ts
@@ -5157,30 +5165,30 @@ var GitService = class {
5157
5165
  this.gitRunner = gitRunner;
5158
5166
  this.logger = logger2;
5159
5167
  }
5160
- getGitRepositoryRoot(path22) {
5168
+ getGitRepositoryRoot(path25) {
5161
5169
  try {
5162
5170
  const { stdout } = this.gitRunner("rev-parse --show-toplevel", {
5163
- cwd: path22
5171
+ cwd: path25
5164
5172
  });
5165
5173
  const gitRoot = stdout.trim();
5166
5174
  this.logger.debug("Resolved git repository root", {
5167
- inputPath: path22,
5175
+ inputPath: path25,
5168
5176
  gitRoot
5169
5177
  });
5170
5178
  return gitRoot;
5171
5179
  } catch (error) {
5172
5180
  if (error instanceof Error) {
5173
5181
  throw new Error(
5174
- `Failed to get Git repository root. The path '${path22}' does not appear to be inside a Git repository.
5182
+ `Failed to get Git repository root. The path '${path25}' does not appear to be inside a Git repository.
5175
5183
  ${error.message}`
5176
5184
  );
5177
5185
  }
5178
5186
  throw new Error("Failed to get Git repository root: Unknown error");
5179
5187
  }
5180
5188
  }
5181
- tryGetGitRepositoryRoot(path22) {
5189
+ tryGetGitRepositoryRoot(path25) {
5182
5190
  try {
5183
- return this.getGitRepositoryRoot(path22);
5191
+ return this.getGitRepositoryRoot(path25);
5184
5192
  } catch {
5185
5193
  return null;
5186
5194
  }
@@ -5467,15 +5475,10 @@ ${error.message}`
5467
5475
  */
5468
5476
  countFileLines(filePath) {
5469
5477
  try {
5470
- const stdout = (0, import_child_process.execSync)(`wc -l < "${filePath}"`, { encoding: "utf-8" });
5471
- const count = parseInt(stdout.trim(), 10);
5472
- if (count === 0) {
5473
- const content = (0, import_child_process.execSync)(`head -c 1 "${filePath}"`, {
5474
- encoding: "utf-8"
5475
- });
5476
- return content.length > 0 ? 1 : 0;
5477
- }
5478
- return count;
5478
+ const content = (0, import_fs.readFileSync)(filePath, "utf-8");
5479
+ if (content.length === 0) return 0;
5480
+ const newlines = (content.match(/\n/g) ?? []).length;
5481
+ return content.endsWith("\n") ? newlines : newlines + 1;
5479
5482
  } catch {
5480
5483
  return 0;
5481
5484
  }
@@ -5541,6 +5544,13 @@ function formatCommand(text) {
5541
5544
  }
5542
5545
 
5543
5546
  // apps/cli/src/application/services/ListFiles.ts
5547
+ var DEFAULT_EXCLUDES = [
5548
+ "node_modules",
5549
+ "dist",
5550
+ "*.min.*",
5551
+ "*.map",
5552
+ ".git"
5553
+ ];
5544
5554
  var ListFiles = class {
5545
5555
  async listFilesInDirectory(directoryPath, extensions, excludes = [], skipHidden = true) {
5546
5556
  const results = [];
@@ -5605,28 +5615,28 @@ var ListFiles = class {
5605
5615
  }
5606
5616
  const normalizedPath = path2.normalize(filePath).replace(/\\/g, "/");
5607
5617
  for (const exclude of excludes) {
5608
- if (this.matchesGlobPattern(normalizedPath, exclude)) {
5618
+ if (matchesGlobPattern(normalizedPath, exclude)) {
5609
5619
  return true;
5610
5620
  }
5611
5621
  }
5612
5622
  return false;
5613
5623
  }
5614
- matchesGlobPattern(filePath, pattern) {
5615
- if (!pattern.includes("*") && !pattern.includes("/")) {
5616
- const pathSegments = filePath.split("/");
5617
- return pathSegments.some((segment) => segment === pattern);
5618
- }
5619
- let regexPattern = pattern.replace(/\*\*/g, "__DOUBLE_STAR__").replace(/\*/g, "[^/]*").replace(/__DOUBLE_STAR__/g, ".*").replace(/\//g, "\\/");
5620
- if (!pattern.startsWith("**/")) {
5621
- regexPattern = "(^|/)" + regexPattern;
5622
- }
5623
- if (!pattern.endsWith("/**")) {
5624
- regexPattern = regexPattern + "($|/)";
5625
- }
5626
- const regex2 = new RegExp(regexPattern);
5627
- return regex2.test(filePath);
5628
- }
5629
5624
  };
5625
+ function matchesGlobPattern(filePath, pattern) {
5626
+ if (!pattern.includes("*") && !pattern.includes("/")) {
5627
+ const pathSegments = filePath.split("/");
5628
+ return pathSegments.some((segment) => segment === pattern);
5629
+ }
5630
+ let regexPattern = pattern.replace(/[.[\](){}^$+?|\\]/g, "\\$&").replace(/\*\*/g, "__DOUBLE_STAR__").replace(/\*/g, "[^/]*").replace(/__DOUBLE_STAR__/g, ".*").replace(/\//g, "\\/");
5631
+ if (!pattern.startsWith("**/")) {
5632
+ regexPattern = "(^|/)" + regexPattern;
5633
+ }
5634
+ if (!pattern.endsWith("/**")) {
5635
+ regexPattern = regexPattern + "($|/)";
5636
+ }
5637
+ const regex2 = new RegExp(regexPattern);
5638
+ return regex2.test(filePath);
5639
+ }
5630
5640
 
5631
5641
  // apps/cli/src/application/useCases/ListFilesInDirectoryUseCase.ts
5632
5642
  var ListFilesInDirectoryUseCase = class {
@@ -5746,7 +5756,8 @@ var LintFilesAgainstRuleUseCase = class {
5746
5756
  standardSlug,
5747
5757
  ruleId,
5748
5758
  language,
5749
- diffMode
5759
+ diffMode,
5760
+ ignorePatterns
5750
5761
  } = command22;
5751
5762
  this.logger.debug(
5752
5763
  `Starting linting: path="${userPath}", draftMode=${!!draftMode}, standardSlug="${standardSlug || "N/A"}", ruleId="${ruleId || "N/A"}", language="${language || "N/A"}", diffMode="${diffMode ?? "none"}"`
@@ -5814,10 +5825,14 @@ var LintFilesAgainstRuleUseCase = class {
5814
5825
  }
5815
5826
  }
5816
5827
  }
5828
+ const excludes = [...DEFAULT_EXCLUDES];
5829
+ if (ignorePatterns?.length) {
5830
+ excludes.push(...ignorePatterns);
5831
+ }
5817
5832
  let files = isFile ? [{ path: absoluteLintPath }] : await this.services.listFiles.listFilesInDirectory(
5818
5833
  absoluteLintPath,
5819
5834
  [],
5820
- ["node_modules", "dist", ".min.", ".map.", ".git"]
5835
+ excludes
5821
5836
  );
5822
5837
  if (modifiedFiles) {
5823
5838
  const modifiedFilesSet = new Set(modifiedFiles);
@@ -5911,6 +5926,7 @@ var LintFilesAgainstRuleUseCase = class {
5911
5926
  }).catch(() => {
5912
5927
  });
5913
5928
  const violations = [];
5929
+ let scopeMatchCount = 0;
5914
5930
  for (const file of files) {
5915
5931
  const fileViolations = [];
5916
5932
  const relativeFilePath = pathStartsWith(file.path, effectiveRoot) ? file.path.substring(effectiveRoot.length) : file.path;
@@ -5947,6 +5963,7 @@ var LintFilesAgainstRuleUseCase = class {
5947
5963
  `File "${normalizedFilePath}" matches target/scope - processing standard "${standard.name}"`
5948
5964
  );
5949
5965
  }
5966
+ scopeMatchCount++;
5950
5967
  for (const rule of standard.rules) {
5951
5968
  for (const activeProgram of rule.activeDetectionPrograms) {
5952
5969
  try {
@@ -6014,6 +6031,11 @@ var LintFilesAgainstRuleUseCase = class {
6014
6031
  });
6015
6032
  }
6016
6033
  }
6034
+ if (isFile && scopeMatchCount === 0) {
6035
+ logInfoConsole(
6036
+ `File "${absoluteUserPath}" is out of scope for the requested rule/standard \u2014 no violations will be reported.`
6037
+ );
6038
+ }
6017
6039
  let filteredViolations = violations;
6018
6040
  if (diffMode === "lines" /* LINES */ && modifiedLines) {
6019
6041
  filteredViolations = this.services.diffViolationFilterService.filterByLines(
@@ -6111,7 +6133,7 @@ var LintFilesFromConfigUseCase = class {
6111
6133
  return pattern;
6112
6134
  }
6113
6135
  async execute(command22) {
6114
- const { path: userPath, diffMode } = command22;
6136
+ const { path: userPath, diffMode, ignorePatterns } = command22;
6115
6137
  this.logger.debug(
6116
6138
  `Starting local linting: path="${userPath}", diffMode="${diffMode ?? "none"}"`
6117
6139
  );
@@ -6185,10 +6207,14 @@ var LintFilesFromConfigUseCase = class {
6185
6207
  `Using config: ${config.absoluteTargetPath}/packmind.json (target: ${config.targetPath})`
6186
6208
  );
6187
6209
  }
6210
+ const excludes = [...DEFAULT_EXCLUDES];
6211
+ if (ignorePatterns?.length) {
6212
+ excludes.push(...ignorePatterns);
6213
+ }
6188
6214
  let files = isFile ? [{ path: absoluteUserPath }] : await this.services.listFiles.listFilesInDirectory(
6189
6215
  absoluteUserPath,
6190
6216
  [],
6191
- ["node_modules", "dist", ".min.", ".map.", ".git"]
6217
+ excludes
6192
6218
  );
6193
6219
  if (modifiedFiles) {
6194
6220
  const modifiedFilesSet = new Set(modifiedFiles);
@@ -6434,10 +6460,10 @@ var PackmindHttpClient = class {
6434
6460
  return null;
6435
6461
  }
6436
6462
  }
6437
- async request(path22, options = {}) {
6463
+ async request(path25, options = {}) {
6438
6464
  const { host } = this.getAuthContext();
6439
6465
  const { method = "GET", body } = options;
6440
- const url = `${host}${path22}`;
6466
+ const url = `${host}${path25}`;
6441
6467
  try {
6442
6468
  const response = await fetch(url, {
6443
6469
  method,
@@ -6831,6 +6857,19 @@ var DeploymentGateway = class {
6831
6857
  }
6832
6858
  );
6833
6859
  };
6860
+ this.getContentByVersions = async (command22) => {
6861
+ const { organizationId } = this.httpClient.getAuthContext();
6862
+ return this.httpClient.request(
6863
+ `/api/v0/organizations/${organizationId}/content-by-versions`,
6864
+ {
6865
+ method: "POST",
6866
+ body: {
6867
+ artifacts: command22.artifacts,
6868
+ ...command22.agents !== void 0 && { agents: command22.agents }
6869
+ }
6870
+ }
6871
+ );
6872
+ };
6834
6873
  this.notifyDistribution = async (command22) => {
6835
6874
  const { organizationId } = this.httpClient.getAuthContext();
6836
6875
  return this.httpClient.request(
@@ -6950,7 +6989,7 @@ var ParserInitializationError = class extends Error {
6950
6989
 
6951
6990
  // packages/linter-ast/src/core/BaseParser.ts
6952
6991
  var import_path = require("path");
6953
- var import_fs = require("fs");
6992
+ var import_fs2 = require("fs");
6954
6993
  var BaseParser = class _BaseParser {
6955
6994
  constructor() {
6956
6995
  this.initialized = false;
@@ -6998,7 +7037,7 @@ var BaseParser = class _BaseParser {
6998
7037
  return (fileName) => {
6999
7038
  for (const dir of wasmDirs) {
7000
7039
  const fullPath = (0, import_path.join)(dir, fileName);
7001
- if ((0, import_fs.existsSync)(fullPath)) {
7040
+ if ((0, import_fs2.existsSync)(fullPath)) {
7002
7041
  return fullPath;
7003
7042
  }
7004
7043
  }
@@ -7036,7 +7075,7 @@ var BaseParser = class _BaseParser {
7036
7075
 
7037
7076
  // packages/linter-ast/src/parsers/TypeScriptParser.ts
7038
7077
  var TreeSitter = __toESM(require("web-tree-sitter"));
7039
- var import_fs2 = require("fs");
7078
+ var import_fs3 = require("fs");
7040
7079
  var TypeScriptParser = class extends BaseParser {
7041
7080
  async initialize() {
7042
7081
  try {
@@ -7046,7 +7085,7 @@ var TypeScriptParser = class extends BaseParser {
7046
7085
  });
7047
7086
  const wasmPaths = BaseParser.getLanguageWasmPaths("typescript");
7048
7087
  for (const wasmPath of wasmPaths) {
7049
- if ((0, import_fs2.existsSync)(wasmPath)) {
7088
+ if ((0, import_fs3.existsSync)(wasmPath)) {
7050
7089
  try {
7051
7090
  lang = await TreeSitter.Language.load(wasmPath);
7052
7091
  break;
@@ -7129,7 +7168,7 @@ var TypeScriptParser = class extends BaseParser {
7129
7168
 
7130
7169
  // packages/linter-ast/src/parsers/JavaScriptParser.ts
7131
7170
  var TreeSitter2 = __toESM(require("web-tree-sitter"));
7132
- var import_fs3 = require("fs");
7171
+ var import_fs4 = require("fs");
7133
7172
  var JavaScriptParser = class extends BaseParser {
7134
7173
  async initialize() {
7135
7174
  try {
@@ -7139,7 +7178,7 @@ var JavaScriptParser = class extends BaseParser {
7139
7178
  });
7140
7179
  const wasmPaths = BaseParser.getLanguageWasmPaths("javascript");
7141
7180
  for (const wasmPath of wasmPaths) {
7142
- if ((0, import_fs3.existsSync)(wasmPath)) {
7181
+ if ((0, import_fs4.existsSync)(wasmPath)) {
7143
7182
  try {
7144
7183
  lang = await TreeSitter2.Language.load(wasmPath);
7145
7184
  break;
@@ -7222,7 +7261,7 @@ var JavaScriptParser = class extends BaseParser {
7222
7261
 
7223
7262
  // packages/linter-ast/src/parsers/CPPParser.ts
7224
7263
  var TreeSitter3 = __toESM(require("web-tree-sitter"));
7225
- var import_fs4 = require("fs");
7264
+ var import_fs5 = require("fs");
7226
7265
  var CPPParser = class extends BaseParser {
7227
7266
  async initialize() {
7228
7267
  try {
@@ -7232,7 +7271,7 @@ var CPPParser = class extends BaseParser {
7232
7271
  });
7233
7272
  const wasmPaths = BaseParser.getLanguageWasmPaths("cpp");
7234
7273
  for (const wasmPath of wasmPaths) {
7235
- if ((0, import_fs4.existsSync)(wasmPath)) {
7274
+ if ((0, import_fs5.existsSync)(wasmPath)) {
7236
7275
  try {
7237
7276
  lang = await TreeSitter3.Language.load(wasmPath);
7238
7277
  break;
@@ -7303,7 +7342,7 @@ var CPPParser = class extends BaseParser {
7303
7342
 
7304
7343
  // packages/linter-ast/src/parsers/GoParser.ts
7305
7344
  var TreeSitter4 = __toESM(require("web-tree-sitter"));
7306
- var import_fs5 = require("fs");
7345
+ var import_fs6 = require("fs");
7307
7346
  var GoParser = class extends BaseParser {
7308
7347
  async initialize() {
7309
7348
  try {
@@ -7313,7 +7352,7 @@ var GoParser = class extends BaseParser {
7313
7352
  });
7314
7353
  const wasmPaths = BaseParser.getLanguageWasmPaths("go");
7315
7354
  for (const wasmPath of wasmPaths) {
7316
- if ((0, import_fs5.existsSync)(wasmPath)) {
7355
+ if ((0, import_fs6.existsSync)(wasmPath)) {
7317
7356
  try {
7318
7357
  lang = await TreeSitter4.Language.load(wasmPath);
7319
7358
  break;
@@ -7384,7 +7423,7 @@ var GoParser = class extends BaseParser {
7384
7423
 
7385
7424
  // packages/linter-ast/src/parsers/KotlinParser.ts
7386
7425
  var TreeSitter5 = __toESM(require("web-tree-sitter"));
7387
- var import_fs6 = require("fs");
7426
+ var import_fs7 = require("fs");
7388
7427
  var KotlinParser = class extends BaseParser {
7389
7428
  async initialize() {
7390
7429
  try {
@@ -7394,7 +7433,7 @@ var KotlinParser = class extends BaseParser {
7394
7433
  });
7395
7434
  const wasmPaths = BaseParser.getLanguageWasmPaths("kotlin");
7396
7435
  for (const wasmPath of wasmPaths) {
7397
- if ((0, import_fs6.existsSync)(wasmPath)) {
7436
+ if ((0, import_fs7.existsSync)(wasmPath)) {
7398
7437
  try {
7399
7438
  lang = await TreeSitter5.Language.load(wasmPath);
7400
7439
  break;
@@ -7471,7 +7510,7 @@ var KotlinParser = class extends BaseParser {
7471
7510
 
7472
7511
  // packages/linter-ast/src/parsers/CSSParser.ts
7473
7512
  var TreeSitter6 = __toESM(require("web-tree-sitter"));
7474
- var import_fs7 = require("fs");
7513
+ var import_fs8 = require("fs");
7475
7514
  var CSSParser = class extends BaseParser {
7476
7515
  async initialize() {
7477
7516
  try {
@@ -7481,7 +7520,7 @@ var CSSParser = class extends BaseParser {
7481
7520
  });
7482
7521
  const wasmPaths = BaseParser.getLanguageWasmPaths("css");
7483
7522
  for (const wasmPath of wasmPaths) {
7484
- if ((0, import_fs7.existsSync)(wasmPath)) {
7523
+ if ((0, import_fs8.existsSync)(wasmPath)) {
7485
7524
  try {
7486
7525
  lang = await TreeSitter6.Language.load(wasmPath);
7487
7526
  break;
@@ -7552,7 +7591,7 @@ var CSSParser = class extends BaseParser {
7552
7591
 
7553
7592
  // packages/linter-ast/src/parsers/CSharpParser.ts
7554
7593
  var TreeSitter7 = __toESM(require("web-tree-sitter"));
7555
- var import_fs8 = require("fs");
7594
+ var import_fs9 = require("fs");
7556
7595
  var CSharpParser = class extends BaseParser {
7557
7596
  async initialize() {
7558
7597
  try {
@@ -7562,7 +7601,7 @@ var CSharpParser = class extends BaseParser {
7562
7601
  });
7563
7602
  const wasmPaths = BaseParser.getLanguageWasmPaths("c_sharp");
7564
7603
  for (const wasmPath of wasmPaths) {
7565
- if ((0, import_fs8.existsSync)(wasmPath)) {
7604
+ if ((0, import_fs9.existsSync)(wasmPath)) {
7566
7605
  try {
7567
7606
  lang = await TreeSitter7.Language.load(wasmPath);
7568
7607
  break;
@@ -7639,7 +7678,7 @@ var CSharpParser = class extends BaseParser {
7639
7678
 
7640
7679
  // packages/linter-ast/src/parsers/PHPParser.ts
7641
7680
  var TreeSitter8 = __toESM(require("web-tree-sitter"));
7642
- var import_fs9 = require("fs");
7681
+ var import_fs10 = require("fs");
7643
7682
  var PHPParser = class extends BaseParser {
7644
7683
  async initialize() {
7645
7684
  try {
@@ -7651,7 +7690,7 @@ var PHPParser = class extends BaseParser {
7651
7690
  BaseParser.getLanguageWasmPaths("php")
7652
7691
  );
7653
7692
  for (const wasmPath of wasmPaths) {
7654
- if ((0, import_fs9.existsSync)(wasmPath)) {
7693
+ if ((0, import_fs10.existsSync)(wasmPath)) {
7655
7694
  try {
7656
7695
  lang = await TreeSitter8.Language.load(wasmPath);
7657
7696
  break;
@@ -7723,7 +7762,7 @@ var PHPParser = class extends BaseParser {
7723
7762
 
7724
7763
  // packages/linter-ast/src/parsers/PythonParser.ts
7725
7764
  var TreeSitter9 = __toESM(require("web-tree-sitter"));
7726
- var import_fs10 = require("fs");
7765
+ var import_fs11 = require("fs");
7727
7766
  var PythonParser = class extends BaseParser {
7728
7767
  async initialize() {
7729
7768
  try {
@@ -7733,7 +7772,7 @@ var PythonParser = class extends BaseParser {
7733
7772
  });
7734
7773
  const wasmPaths = BaseParser.getLanguageWasmPaths("python");
7735
7774
  for (const wasmPath of wasmPaths) {
7736
- if ((0, import_fs10.existsSync)(wasmPath)) {
7775
+ if ((0, import_fs11.existsSync)(wasmPath)) {
7737
7776
  try {
7738
7777
  lang = await TreeSitter9.Language.load(wasmPath);
7739
7778
  break;
@@ -7810,7 +7849,7 @@ var PythonParser = class extends BaseParser {
7810
7849
 
7811
7850
  // packages/linter-ast/src/parsers/RubyParser.ts
7812
7851
  var TreeSitter10 = __toESM(require("web-tree-sitter"));
7813
- var import_fs11 = require("fs");
7852
+ var import_fs12 = require("fs");
7814
7853
  var RubyParser = class extends BaseParser {
7815
7854
  async initialize() {
7816
7855
  try {
@@ -7820,7 +7859,7 @@ var RubyParser = class extends BaseParser {
7820
7859
  });
7821
7860
  const wasmPaths = BaseParser.getLanguageWasmPaths("ruby");
7822
7861
  for (const wasmPath of wasmPaths) {
7823
- if ((0, import_fs11.existsSync)(wasmPath)) {
7862
+ if ((0, import_fs12.existsSync)(wasmPath)) {
7824
7863
  try {
7825
7864
  lang = await TreeSitter10.Language.load(wasmPath);
7826
7865
  break;
@@ -7897,7 +7936,7 @@ var RubyParser = class extends BaseParser {
7897
7936
 
7898
7937
  // packages/linter-ast/src/parsers/JSONParser.ts
7899
7938
  var TreeSitter11 = __toESM(require("web-tree-sitter"));
7900
- var import_fs12 = require("fs");
7939
+ var import_fs13 = require("fs");
7901
7940
  var JSONParser = class extends BaseParser {
7902
7941
  async initialize() {
7903
7942
  try {
@@ -7907,7 +7946,7 @@ var JSONParser = class extends BaseParser {
7907
7946
  });
7908
7947
  const wasmPaths = BaseParser.getLanguageWasmPaths("json");
7909
7948
  for (const wasmPath of wasmPaths) {
7910
- if ((0, import_fs12.existsSync)(wasmPath)) {
7949
+ if ((0, import_fs13.existsSync)(wasmPath)) {
7911
7950
  try {
7912
7951
  lang = await TreeSitter11.Language.load(wasmPath);
7913
7952
  break;
@@ -7984,7 +8023,7 @@ var JSONParser = class extends BaseParser {
7984
8023
 
7985
8024
  // packages/linter-ast/src/parsers/HTMLParser.ts
7986
8025
  var TreeSitter12 = __toESM(require("web-tree-sitter"));
7987
- var import_fs13 = require("fs");
8026
+ var import_fs14 = require("fs");
7988
8027
  var HTMLParser = class extends BaseParser {
7989
8028
  async initialize() {
7990
8029
  try {
@@ -7994,7 +8033,7 @@ var HTMLParser = class extends BaseParser {
7994
8033
  });
7995
8034
  const wasmPaths = BaseParser.getLanguageWasmPaths("html");
7996
8035
  for (const wasmPath of wasmPaths) {
7997
- if ((0, import_fs13.existsSync)(wasmPath)) {
8036
+ if ((0, import_fs14.existsSync)(wasmPath)) {
7998
8037
  try {
7999
8038
  lang = await TreeSitter12.Language.load(wasmPath);
8000
8039
  break;
@@ -8071,7 +8110,7 @@ var HTMLParser = class extends BaseParser {
8071
8110
 
8072
8111
  // packages/linter-ast/src/parsers/JavaParser.ts
8073
8112
  var TreeSitter13 = __toESM(require("web-tree-sitter"));
8074
- var import_fs14 = require("fs");
8113
+ var import_fs15 = require("fs");
8075
8114
  var JavaParser = class extends BaseParser {
8076
8115
  async initialize() {
8077
8116
  try {
@@ -8081,7 +8120,7 @@ var JavaParser = class extends BaseParser {
8081
8120
  });
8082
8121
  const wasmPaths = BaseParser.getLanguageWasmPaths("java");
8083
8122
  for (const wasmPath of wasmPaths) {
8084
- if ((0, import_fs14.existsSync)(wasmPath)) {
8123
+ if ((0, import_fs15.existsSync)(wasmPath)) {
8085
8124
  try {
8086
8125
  lang = await TreeSitter13.Language.load(wasmPath);
8087
8126
  break;
@@ -8158,7 +8197,7 @@ var JavaParser = class extends BaseParser {
8158
8197
 
8159
8198
  // packages/linter-ast/src/parsers/SwiftParser.ts
8160
8199
  var TreeSitter14 = __toESM(require("web-tree-sitter"));
8161
- var import_fs15 = require("fs");
8200
+ var import_fs16 = require("fs");
8162
8201
  var SwiftParser = class extends BaseParser {
8163
8202
  async initialize() {
8164
8203
  try {
@@ -8168,7 +8207,7 @@ var SwiftParser = class extends BaseParser {
8168
8207
  });
8169
8208
  const wasmPaths = BaseParser.getLanguageWasmPaths("swift");
8170
8209
  for (const wasmPath of wasmPaths) {
8171
- if ((0, import_fs15.existsSync)(wasmPath)) {
8210
+ if ((0, import_fs16.existsSync)(wasmPath)) {
8172
8211
  try {
8173
8212
  lang = await TreeSitter14.Language.load(wasmPath);
8174
8213
  break;
@@ -8245,7 +8284,7 @@ var SwiftParser = class extends BaseParser {
8245
8284
 
8246
8285
  // packages/linter-ast/src/parsers/SCSSParser.ts
8247
8286
  var TreeSitter15 = __toESM(require("web-tree-sitter"));
8248
- var import_fs16 = require("fs");
8287
+ var import_fs17 = require("fs");
8249
8288
  var SCSSParser = class extends BaseParser {
8250
8289
  async initialize() {
8251
8290
  try {
@@ -8255,7 +8294,7 @@ var SCSSParser = class extends BaseParser {
8255
8294
  });
8256
8295
  const wasmPaths = BaseParser.getLanguageWasmPaths("scss");
8257
8296
  for (const wasmPath of wasmPaths) {
8258
- if ((0, import_fs16.existsSync)(wasmPath)) {
8297
+ if ((0, import_fs17.existsSync)(wasmPath)) {
8259
8298
  try {
8260
8299
  lang = await TreeSitter15.Language.load(wasmPath);
8261
8300
  break;
@@ -8332,7 +8371,7 @@ var SCSSParser = class extends BaseParser {
8332
8371
 
8333
8372
  // packages/linter-ast/src/parsers/YAMLParser.ts
8334
8373
  var TreeSitter16 = __toESM(require("web-tree-sitter"));
8335
- var import_fs17 = require("fs");
8374
+ var import_fs18 = require("fs");
8336
8375
  var YAMLParser = class extends BaseParser {
8337
8376
  async initialize() {
8338
8377
  try {
@@ -8342,7 +8381,7 @@ var YAMLParser = class extends BaseParser {
8342
8381
  });
8343
8382
  const wasmPaths = BaseParser.getLanguageWasmPaths("yaml");
8344
8383
  for (const wasmPath of wasmPaths) {
8345
- if ((0, import_fs17.existsSync)(wasmPath)) {
8384
+ if ((0, import_fs18.existsSync)(wasmPath)) {
8346
8385
  try {
8347
8386
  lang = await TreeSitter16.Language.load(wasmPath);
8348
8387
  break;
@@ -9346,6 +9385,36 @@ function parseSkillMd(content) {
9346
9385
  };
9347
9386
  }
9348
9387
 
9388
+ // apps/cli/src/application/utils/resolveArtefactFromPath.ts
9389
+ function resolveArtefactFromPath(filePath) {
9390
+ const normalized = normalizePath(filePath);
9391
+ for (const [agent, paths] of Object.entries(CODING_AGENT_ARTEFACT_PATHS)) {
9392
+ if (paths.command && normalized.includes(paths.command)) {
9393
+ return {
9394
+ artifactType: "command",
9395
+ codingAgent: agent
9396
+ };
9397
+ }
9398
+ }
9399
+ for (const [agent, paths] of Object.entries(CODING_AGENT_ARTEFACT_PATHS)) {
9400
+ if (paths.standard && normalized.includes(paths.standard)) {
9401
+ return {
9402
+ artifactType: "standard",
9403
+ codingAgent: agent
9404
+ };
9405
+ }
9406
+ }
9407
+ for (const [agent, paths] of Object.entries(CODING_AGENT_ARTEFACT_PATHS)) {
9408
+ if (paths.skill && normalized.includes(paths.skill)) {
9409
+ return {
9410
+ artifactType: "skill",
9411
+ codingAgent: agent
9412
+ };
9413
+ }
9414
+ }
9415
+ return null;
9416
+ }
9417
+
9349
9418
  // apps/cli/src/application/useCases/InstallPackagesUseCase.ts
9350
9419
  var fs4 = __toESM(require("fs/promises"));
9351
9420
  var path5 = __toESM(require("path"));
@@ -9382,8 +9451,9 @@ function parsePermissionString(permString) {
9382
9451
 
9383
9452
  // apps/cli/src/application/useCases/InstallPackagesUseCase.ts
9384
9453
  var InstallPackagesUseCase = class {
9385
- constructor(packmindGateway) {
9454
+ constructor(packmindGateway, lockFileRepository) {
9386
9455
  this.packmindGateway = packmindGateway;
9456
+ this.lockFileRepository = lockFileRepository;
9387
9457
  }
9388
9458
  async execute(command22) {
9389
9459
  const baseDirectory = command22.baseDirectory || process.cwd();
@@ -9450,12 +9520,76 @@ var InstallPackagesUseCase = class {
9450
9520
  result.errors.push(`Failed to delete ${file.path}: ${errorMsg}`);
9451
9521
  }
9452
9522
  }
9523
+ const lockFile = this.buildLockFile(
9524
+ filteredCreateOrUpdate,
9525
+ command22.packagesSlugs,
9526
+ command22.cliVersion,
9527
+ command22.agents,
9528
+ response.targetId,
9529
+ response.resolvedAgents
9530
+ );
9531
+ if (Object.keys(lockFile.artifacts).length > 0) {
9532
+ try {
9533
+ await this.lockFileRepository.write(baseDirectory, lockFile);
9534
+ } catch (error) {
9535
+ const errorMsg = error instanceof Error ? error.message : String(error);
9536
+ result.errors.push(`Failed to write lock file: ${errorMsg}`);
9537
+ }
9538
+ } else {
9539
+ try {
9540
+ await this.lockFileRepository.delete(baseDirectory);
9541
+ } catch (error) {
9542
+ const errorMsg = error instanceof Error ? error.message : String(error);
9543
+ result.errors.push(`Failed to delete lock file: ${errorMsg}`);
9544
+ }
9545
+ }
9453
9546
  } catch (error) {
9454
9547
  const errorMsg = error instanceof Error ? error.message : String(error);
9455
9548
  result.errors.push(`Failed to install packages: ${errorMsg}`);
9456
9549
  }
9457
9550
  return result;
9458
9551
  }
9552
+ buildLockFile(files, packageSlugs, cliVersion, commandAgents, targetId, resolvedAgents) {
9553
+ const artifacts = {};
9554
+ for (const file of files) {
9555
+ if (!file.artifactType || !file.artifactName || !file.artifactSlug || !file.artifactId || file.artifactVersion === void 0 || !file.spaceId) {
9556
+ continue;
9557
+ }
9558
+ const artifactKey = `${file.artifactType}:${file.artifactSlug}`;
9559
+ if (!artifacts[artifactKey]) {
9560
+ artifacts[artifactKey] = {
9561
+ type: file.artifactType,
9562
+ name: file.artifactName,
9563
+ id: file.artifactId,
9564
+ version: file.artifactVersion,
9565
+ spaceId: file.spaceId,
9566
+ packageIds: file.packageIds ?? [],
9567
+ files: []
9568
+ };
9569
+ }
9570
+ const resolved = resolveArtefactFromPath(file.path);
9571
+ if (resolved) {
9572
+ const isSkillDefinition = resolved.artifactType === "skill" && !file.skillFileId;
9573
+ artifacts[artifactKey].files.push({
9574
+ path: file.path,
9575
+ agent: resolved.codingAgent,
9576
+ ...isSkillDefinition ? { isSkillDefinition: true } : {}
9577
+ });
9578
+ }
9579
+ }
9580
+ const agents = [
9581
+ ...commandAgents?.length ? commandAgents : resolvedAgents ?? []
9582
+ ].sort((a, b) => a.localeCompare(b));
9583
+ return {
9584
+ lockfileVersion: 1,
9585
+ packageSlugs: [...packageSlugs].sort((a, b) => a.localeCompare(b)),
9586
+ agents,
9587
+ installedAt: (/* @__PURE__ */ new Date()).toISOString(),
9588
+ cliVersion: cliVersion ?? "unknown",
9589
+ ...targetId ? { targetId } : {},
9590
+ artifacts
9591
+ };
9592
+ }
9459
9593
  async createOrUpdateFile(baseDirectory, file, result, skillFilePermissions) {
9460
9594
  const fullPath = path5.join(baseDirectory, file.path);
9461
9595
  const directory = path5.dirname(fullPath);
@@ -9541,12 +9675,12 @@ var InstallPackagesUseCase = class {
9541
9675
  }
9542
9676
  async deleteFile(baseDirectory, filePath, result) {
9543
9677
  const fullPath = path5.join(baseDirectory, filePath);
9544
- const stat5 = await fs4.stat(fullPath).catch(() => null);
9545
- if (stat5?.isDirectory()) {
9678
+ const stat7 = await fs4.stat(fullPath).catch(() => null);
9679
+ if (stat7?.isDirectory()) {
9546
9680
  await fs4.rm(fullPath, { recursive: true, force: true });
9547
9681
  result.filesDeleted++;
9548
9682
  await this.removeEmptyParentDirectories(fullPath, baseDirectory);
9549
- } else if (stat5?.isFile()) {
9683
+ } else if (stat7?.isFile()) {
9550
9684
  await fs4.unlink(fullPath);
9551
9685
  result.filesDeleted++;
9552
9686
  await this.removeEmptyParentDirectories(fullPath, baseDirectory);
@@ -9958,10 +10092,10 @@ async function defaultPromptForCode() {
9958
10092
  input: process.stdin,
9959
10093
  output: process.stdout
9960
10094
  });
9961
- return new Promise((resolve9) => {
10095
+ return new Promise((resolve11) => {
9962
10096
  rl.question("Enter the login code from the browser: ", (answer) => {
9963
10097
  rl.close();
9964
- resolve9(answer.trim());
10098
+ resolve11(answer.trim());
9965
10099
  });
9966
10100
  });
9967
10101
  }
@@ -9995,7 +10129,7 @@ async function defaultExchangeCodeForApiKey(code, host) {
9995
10129
  return await response.json();
9996
10130
  }
9997
10131
  function defaultStartCallbackServer() {
9998
- return new Promise((resolve9, reject) => {
10132
+ return new Promise((resolve11, reject) => {
9999
10133
  let timeoutId = null;
10000
10134
  const server = http.createServer((req, res) => {
10001
10135
  res.setHeader("Access-Control-Allow-Origin", "*");
@@ -10008,7 +10142,7 @@ function defaultStartCallbackServer() {
10008
10142
  if (timeoutId) {
10009
10143
  clearTimeout(timeoutId);
10010
10144
  }
10011
- resolve9(code);
10145
+ resolve11(code);
10012
10146
  setImmediate(() => {
10013
10147
  server.close();
10014
10148
  });
@@ -10085,8 +10219,8 @@ var LogoutUseCase = class {
10085
10219
  constructor(deps) {
10086
10220
  this.deps = {
10087
10221
  getCredentialsPath: deps?.getCredentialsPath ?? getCredentialsPath,
10088
- fileExists: deps?.fileExists ?? ((path22) => fs7.existsSync(path22)),
10089
- deleteFile: deps?.deleteFile ?? ((path22) => fs7.unlinkSync(path22)),
10222
+ fileExists: deps?.fileExists ?? ((path25) => fs7.existsSync(path25)),
10223
+ deleteFile: deps?.deleteFile ?? ((path25) => fs7.unlinkSync(path25)),
10090
10224
  hasEnvVar: deps?.hasEnvVar ?? (() => !!process.env[ENV_VAR_NAME2])
10091
10225
  };
10092
10226
  }
@@ -10666,6 +10800,61 @@ var ConfigFileRepository = class {
10666
10800
  }
10667
10801
  };
10668
10802
 
10803
+ // apps/cli/src/infra/repositories/LockFileRepository.ts
10804
+ var fs10 = __toESM(require("fs/promises"));
10805
+ var path10 = __toESM(require("path"));
10806
+ var LockFileRepository = class {
10807
+ constructor() {
10808
+ this.LOCK_FILENAME = "packmind-lock.json";
10809
+ }
10810
+ async read(baseDirectory) {
10811
+ const lockFilePath = this.getLockFilePath(baseDirectory);
10812
+ try {
10813
+ const content = await fs10.readFile(lockFilePath, "utf-8");
10814
+ const parsed = JSON.parse(content);
10815
+ if (!this.isValidLockFile(parsed)) {
10816
+ logWarningConsole(`Malformed lock file: ${lockFilePath}`);
10817
+ return null;
10818
+ }
10819
+ return parsed;
10820
+ } catch (error) {
10821
+ if (error.code === "ENOENT") {
10822
+ return null;
10823
+ }
10824
+ if (error instanceof SyntaxError) {
10825
+ logWarningConsole(`Malformed lock file: ${lockFilePath}`);
10826
+ return null;
10827
+ }
10828
+ throw error;
10829
+ }
10830
+ }
10831
+ async write(baseDirectory, lockFile) {
10832
+ const lockFilePath = this.getLockFilePath(baseDirectory);
10833
+ const content = JSON.stringify(lockFile, null, 2) + "\n";
10834
+ await fs10.writeFile(lockFilePath, content, "utf-8");
10835
+ }
10836
+ async delete(baseDirectory) {
10837
+ const lockFilePath = this.getLockFilePath(baseDirectory);
10838
+ try {
10839
+ await fs10.unlink(lockFilePath);
10840
+ } catch (error) {
10841
+ if (error.code !== "ENOENT") {
10842
+ throw error;
10843
+ }
10844
+ }
10845
+ }
10846
+ isValidLockFile(data) {
10847
+ if (typeof data !== "object" || data === null || Array.isArray(data)) {
10848
+ return false;
10849
+ }
10850
+ const obj = data;
10851
+ return typeof obj.cliVersion === "string" && typeof obj.installedAt === "string" && Array.isArray(obj.packageSlugs) && Array.isArray(obj.agents) && (obj.targetId === void 0 || typeof obj.targetId === "string") && typeof obj.artifacts === "object" && obj.artifacts !== null && !Array.isArray(obj.artifacts);
10852
+ }
10853
+ getLockFilePath(baseDirectory) {
10854
+ return path10.join(baseDirectory, this.LOCK_FILENAME);
10855
+ }
10856
+ };
10857
+
10669
10858
  // apps/cli/src/application/useCases/ListStandardsUseCase.ts
10670
10859
  var ListStandardsUseCase = class {
10671
10860
  constructor(packmindGateway) {
@@ -10723,7 +10912,7 @@ function normalizeLineEndings(content) {
10723
10912
  }
10724
10913
 
10725
10914
  // apps/cli/src/infra/utils/binaryDetection.ts
10726
- var path10 = __toESM(require("path"));
10915
+ var path11 = __toESM(require("path"));
10727
10916
  var BINARY_EXTENSIONS = /* @__PURE__ */ new Set([
10728
10917
  // Images
10729
10918
  ".png",
@@ -10781,7 +10970,7 @@ var BINARY_EXTENSIONS = /* @__PURE__ */ new Set([
10781
10970
  ".sqlite3"
10782
10971
  ]);
10783
10972
  function isBinaryExtension(filePath) {
10784
- const ext = path10.extname(filePath).toLowerCase();
10973
+ const ext = path11.extname(filePath).toLowerCase();
10785
10974
  return BINARY_EXTENSIONS.has(ext);
10786
10975
  }
10787
10976
  function isBinaryBuffer(buffer) {
@@ -10821,9 +11010,9 @@ async function readSkillDirectory(dirPath) {
10821
11010
  if (entry.isDirectory()) {
10822
11011
  await readDir(fullPath, basePath);
10823
11012
  } else if (entry.isFile()) {
10824
- const stat5 = await import_promises.default.stat(fullPath);
10825
- if (stat5.size > MAX_FILE_SIZE_BYTES) {
10826
- const fileSizeMB = (stat5.size / (1024 * 1024)).toFixed(2);
11013
+ const stat7 = await import_promises.default.stat(fullPath);
11014
+ if (stat7.size > MAX_FILE_SIZE_BYTES) {
11015
+ const fileSizeMB = (stat7.size / (1024 * 1024)).toFixed(2);
10827
11016
  throw new Error(
10828
11017
  `File "${relativePath}" is ${fileSizeMB} MB which exceeds the maximum allowed size of ${MAX_FILE_SIZE_MB} MB per file.`
10829
11018
  );
@@ -10842,7 +11031,7 @@ async function readSkillDirectory(dirPath) {
10842
11031
  relativePath: normalizedPath,
10843
11032
  content,
10844
11033
  size: Buffer.byteLength(content, isBinary ? "base64" : "utf-8"),
10845
- permissions: modeToPermissionStringOrDefault(stat5.mode),
11034
+ permissions: modeToPermissionStringOrDefault(stat7.mode),
10846
11035
  isBase64: isBinary
10847
11036
  });
10848
11037
  }
@@ -10900,8 +11089,8 @@ var UploadSkillUseCase = class {
10900
11089
 
10901
11090
  // apps/cli/src/application/useCases/diffStrategies/CommandDiffStrategy.ts
10902
11091
  var import_diff2 = require("diff");
10903
- var fs11 = __toESM(require("fs/promises"));
10904
- var path12 = __toESM(require("path"));
11092
+ var fs12 = __toESM(require("fs/promises"));
11093
+ var path13 = __toESM(require("path"));
10905
11094
 
10906
11095
  // apps/cli/src/application/utils/stripFrontmatter.ts
10907
11096
  var FRONTMATTER_DELIMITER2 = "---";
@@ -10928,10 +11117,10 @@ var CommandDiffStrategy = class {
10928
11117
  return file.artifactType === "command";
10929
11118
  }
10930
11119
  async diff(file, baseDirectory) {
10931
- const fullPath = path12.join(baseDirectory, file.path);
11120
+ const fullPath = path13.join(baseDirectory, file.path);
10932
11121
  let localContent;
10933
11122
  try {
10934
- localContent = await fs11.readFile(fullPath, "utf-8");
11123
+ localContent = await fs12.readFile(fullPath, "utf-8");
10935
11124
  } catch {
10936
11125
  return [];
10937
11126
  }
@@ -10963,8 +11152,8 @@ var CommandDiffStrategy = class {
10963
11152
 
10964
11153
  // apps/cli/src/application/useCases/diffStrategies/SkillDiffStrategy.ts
10965
11154
  var import_diff3 = require("diff");
10966
- var fs12 = __toESM(require("fs/promises"));
10967
- var path13 = __toESM(require("path"));
11155
+ var fs13 = __toESM(require("fs/promises"));
11156
+ var path14 = __toESM(require("path"));
10968
11157
  var SkillDiffStrategy = class {
10969
11158
  supports(file) {
10970
11159
  return file.artifactType === "skill";
@@ -10978,7 +11167,7 @@ var SkillDiffStrategy = class {
10978
11167
  async diffNewFiles(skillFolders, serverFiles, baseDirectory) {
10979
11168
  const diffs = [];
10980
11169
  for (const folder of skillFolders) {
10981
- const folderPath = path13.join(baseDirectory, folder);
11170
+ const folderPath = path14.join(baseDirectory, folder);
10982
11171
  const localFiles = await this.listFilesRecursively(folderPath);
10983
11172
  const serverPathsInFolder = new Set(
10984
11173
  serverFiles.filter((f) => f.path.startsWith(folder + "/")).map((f) => f.path)
@@ -10997,7 +11186,7 @@ var SkillDiffStrategy = class {
10997
11186
  if (serverPathsInFolder.has(filePath)) {
10998
11187
  continue;
10999
11188
  }
11000
- const fullPath = path13.join(baseDirectory, filePath);
11189
+ const fullPath = path14.join(baseDirectory, filePath);
11001
11190
  const localRead = await this.tryReadFileBinaryAware(fullPath);
11002
11191
  if (localRead === null) {
11003
11192
  continue;
@@ -11024,7 +11213,7 @@ var SkillDiffStrategy = class {
11024
11213
  return diffs;
11025
11214
  }
11026
11215
  async diffSkillMd(file, baseDirectory) {
11027
- const fullPath = path13.join(baseDirectory, file.path);
11216
+ const fullPath = path14.join(baseDirectory, file.path);
11028
11217
  const localContent = await this.tryReadFile(fullPath);
11029
11218
  if (localContent === null) {
11030
11219
  return [];
@@ -11076,7 +11265,7 @@ var SkillDiffStrategy = class {
11076
11265
  return [];
11077
11266
  }
11078
11267
  const skillFileId = createSkillFileId(file.skillFileId);
11079
- const fullPath = path13.join(baseDirectory, file.path);
11268
+ const fullPath = path14.join(baseDirectory, file.path);
11080
11269
  const localRead = await this.tryReadFileBinaryAware(fullPath);
11081
11270
  const fileRelativePath = this.computeRelativePath(file.path, skillFolders);
11082
11271
  if (localRead === null) {
@@ -11261,14 +11450,14 @@ var SkillDiffStrategy = class {
11261
11450
  }
11262
11451
  async tryReadFile(filePath) {
11263
11452
  try {
11264
- return await fs12.readFile(filePath, "utf-8");
11453
+ return await fs13.readFile(filePath, "utf-8");
11265
11454
  } catch {
11266
11455
  return null;
11267
11456
  }
11268
11457
  }
11269
11458
  async tryReadFileBinaryAware(filePath) {
11270
11459
  try {
11271
- const buffer = await fs12.readFile(filePath);
11460
+ const buffer = await fs13.readFile(filePath);
11272
11461
  if (isBinaryFile(filePath, buffer)) {
11273
11462
  return { content: buffer.toString("base64"), isBase64: true };
11274
11463
  }
@@ -11280,19 +11469,19 @@ var SkillDiffStrategy = class {
11280
11469
  async listFilesRecursively(dirPath, prefix = "") {
11281
11470
  let entries;
11282
11471
  try {
11283
- entries = await fs12.readdir(dirPath);
11472
+ entries = await fs13.readdir(dirPath);
11284
11473
  } catch {
11285
11474
  return [];
11286
11475
  }
11287
11476
  const files = [];
11288
11477
  for (const entry of entries) {
11289
- const fullPath = path13.join(dirPath, entry);
11290
- const stat5 = await this.tryStatFile(fullPath);
11291
- if (!stat5) {
11478
+ const fullPath = path14.join(dirPath, entry);
11479
+ const stat7 = await this.tryStatFile(fullPath);
11480
+ if (!stat7) {
11292
11481
  continue;
11293
11482
  }
11294
11483
  const relativePath = prefix ? `${prefix}/${entry}` : entry;
11295
- if (stat5.isDirectory) {
11484
+ if (stat7.isDirectory) {
11296
11485
  const subFiles = await this.listFilesRecursively(
11297
11486
  fullPath,
11298
11487
  relativePath
@@ -11306,16 +11495,16 @@ var SkillDiffStrategy = class {
11306
11495
  }
11307
11496
  async tryStatFile(filePath) {
11308
11497
  try {
11309
- const stat5 = await fs12.stat(filePath);
11310
- return { isDirectory: stat5.isDirectory() };
11498
+ const stat7 = await fs13.stat(filePath);
11499
+ return { isDirectory: stat7.isDirectory() };
11311
11500
  } catch {
11312
11501
  return null;
11313
11502
  }
11314
11503
  }
11315
11504
  async tryGetPermissions(filePath) {
11316
11505
  try {
11317
- const stat5 = await fs12.stat(filePath);
11318
- return modeToPermissionStringOrDefault(stat5.mode);
11506
+ const stat7 = await fs13.stat(filePath);
11507
+ return modeToPermissionStringOrDefault(stat7.mode);
11319
11508
  } catch {
11320
11509
  return null;
11321
11510
  }
@@ -11330,8 +11519,8 @@ var SkillDiffStrategy = class {
11330
11519
  };
11331
11520
 
11332
11521
  // apps/cli/src/application/useCases/diffStrategies/StandardDiffStrategy.ts
11333
- var fs13 = __toESM(require("fs/promises"));
11334
- var path14 = __toESM(require("path"));
11522
+ var fs14 = __toESM(require("fs/promises"));
11523
+ var path15 = __toESM(require("path"));
11335
11524
 
11336
11525
  // apps/cli/src/application/utils/parseStandardMd.ts
11337
11526
  var DEPLOYER_PARSERS = [
@@ -11626,10 +11815,10 @@ var StandardDiffStrategy = class {
11626
11815
  return file.artifactType === "standard";
11627
11816
  }
11628
11817
  async diff(file, baseDirectory) {
11629
- const fullPath = path14.join(baseDirectory, file.path);
11818
+ const fullPath = path15.join(baseDirectory, file.path);
11630
11819
  let localContent;
11631
11820
  try {
11632
- localContent = await fs13.readFile(fullPath, "utf-8");
11821
+ localContent = await fs14.readFile(fullPath, "utf-8");
11633
11822
  } catch {
11634
11823
  return [];
11635
11824
  }
@@ -11744,8 +11933,9 @@ var StandardDiffStrategy = class {
11744
11933
 
11745
11934
  // apps/cli/src/application/useCases/DiffArtefactsUseCase.ts
11746
11935
  var DiffArtefactsUseCase = class {
11747
- constructor(packmindGateway) {
11936
+ constructor(packmindGateway, lockFileRepository) {
11748
11937
  this.packmindGateway = packmindGateway;
11938
+ this.lockFileRepository = lockFileRepository;
11749
11939
  this.strategies = [
11750
11940
  new CommandDiffStrategy(),
11751
11941
  new SkillDiffStrategy(),
@@ -11754,13 +11944,7 @@ var DiffArtefactsUseCase = class {
11754
11944
  }
11755
11945
  async execute(command22) {
11756
11946
  const baseDirectory = command22.baseDirectory || process.cwd();
11757
- const response = await this.packmindGateway.deployment.getDeployed({
11758
- packagesSlugs: command22.packagesSlugs,
11759
- gitRemoteUrl: command22.gitRemoteUrl,
11760
- gitBranch: command22.gitBranch,
11761
- relativePath: command22.relativePath,
11762
- agents: command22.agents
11763
- });
11947
+ const response = await this.fetchContent(command22, baseDirectory);
11764
11948
  const filteredFiles = response.fileUpdates.createOrUpdate.filter(
11765
11949
  (file) => file.path !== "packmind.json"
11766
11950
  );
@@ -11795,7 +11979,37 @@ var DiffArtefactsUseCase = class {
11795
11979
  diffs.push(...newFileDiffs);
11796
11980
  }
11797
11981
  }
11798
- return diffs.map((diff) => ({ ...diff, targetId: response.targetId }));
11982
+ return diffs.map((diff) => ({
11983
+ ...diff,
11984
+ targetId: response.targetId ? createTargetId(response.targetId) : void 0
11985
+ }));
11986
+ }
11987
+ async fetchContent(command22, baseDirectory) {
11988
+ const lockFile = await this.lockFileRepository.read(baseDirectory);
11989
+ if (lockFile) {
11990
+ try {
11991
+ const response = await this.packmindGateway.deployment.getContentByVersions({
11992
+ artifacts: Object.values(lockFile.artifacts),
11993
+ agents: lockFile.agents
11994
+ });
11995
+ return {
11996
+ ...response,
11997
+ targetId: lockFile.targetId
11998
+ };
11999
+ } catch (error) {
12000
+ const statusCode = error.statusCode;
12001
+ if (statusCode !== 404) {
12002
+ throw error;
12003
+ }
12004
+ }
12005
+ }
12006
+ return this.packmindGateway.deployment.getDeployed({
12007
+ packagesSlugs: command22.packagesSlugs,
12008
+ gitRemoteUrl: command22.gitRemoteUrl,
12009
+ gitBranch: command22.gitBranch,
12010
+ relativePath: command22.relativePath,
12011
+ agents: command22.agents
12012
+ });
11799
12013
  }
11800
12014
  prefixSkillFolders(skillFolders, relativePath) {
11801
12015
  if (!relativePath) return skillFolders;
@@ -11956,7 +12170,8 @@ var PackmindCliHexaFactory = class {
11956
12170
  constructor() {
11957
12171
  this.repositories = {
11958
12172
  packmindGateway: new PackmindGateway(loadApiKey()),
11959
- configFileRepository: new ConfigFileRepository()
12173
+ configFileRepository: new ConfigFileRepository(),
12174
+ lockFileRepository: new LockFileRepository()
11960
12175
  };
11961
12176
  this.services = {
11962
12177
  listFiles: new ListFiles(),
@@ -11979,7 +12194,8 @@ var PackmindCliHexaFactory = class {
11979
12194
  this.repositories
11980
12195
  ),
11981
12196
  installPackages: new InstallPackagesUseCase(
11982
- this.repositories.packmindGateway
12197
+ this.repositories.packmindGateway,
12198
+ this.repositories.lockFileRepository
11983
12199
  ),
11984
12200
  installDefaultSkills: new InstallDefaultSkillsUseCase(this.repositories),
11985
12201
  listPackages: new ListPackagesUseCase(this.repositories.packmindGateway),
@@ -12002,7 +12218,8 @@ var PackmindCliHexaFactory = class {
12002
12218
  gateway: this.repositories.packmindGateway
12003
12219
  }),
12004
12220
  diffArtefacts: new DiffArtefactsUseCase(
12005
- this.repositories.packmindGateway
12221
+ this.repositories.packmindGateway,
12222
+ this.repositories.lockFileRepository
12006
12223
  ),
12007
12224
  submitDiffs: new SubmitDiffsUseCase(this.repositories.packmindGateway),
12008
12225
  checkDiffs: new CheckDiffsUseCase(this.repositories.packmindGateway)
@@ -12187,6 +12404,9 @@ var PackmindCliHexa = class {
12187
12404
  async installDefaultSkills(command22) {
12188
12405
  return this.hexa.useCases.installDefaultSkills.execute(command22);
12189
12406
  }
12407
+ async readLockFile(baseDirectory) {
12408
+ return this.hexa.repositories.lockFileRepository.read(baseDirectory);
12409
+ }
12190
12410
  getPackmindGateway() {
12191
12411
  return this.hexa.repositories.packmindGateway;
12192
12412
  }
@@ -12285,8 +12505,55 @@ var HumanReadableLogger = class {
12285
12505
  };
12286
12506
 
12287
12507
  // apps/cli/src/infra/commands/LinterCommand.ts
12508
+ var pathModule2 = __toESM(require("path"));
12509
+
12510
+ // apps/cli/src/infra/commands/lintHandler.ts
12511
+ var fs16 = __toESM(require("fs/promises"));
12288
12512
  var pathModule = __toESM(require("path"));
12289
12513
 
12514
+ // apps/cli/src/application/services/PackmindIgnoreReader.ts
12515
+ var fs15 = __toESM(require("fs/promises"));
12516
+ var path16 = __toESM(require("path"));
12517
+ var IGNORE_FILENAME = ".packmindignore";
12518
+ var PackmindIgnoreReader = class {
12519
+ async readIgnorePatterns(startDirectory, stopDirectory) {
12520
+ const patterns = [];
12521
+ const normalizedStart = path16.resolve(startDirectory);
12522
+ const normalizedStop = stopDirectory ? path16.resolve(stopDirectory) : null;
12523
+ if (normalizedStop === null) {
12524
+ const ignoreFile = path16.join(normalizedStart, IGNORE_FILENAME);
12525
+ return this.parseIgnoreFile(ignoreFile);
12526
+ }
12527
+ let currentDir = normalizedStart;
12528
+ while (true) {
12529
+ const ignoreFile = path16.join(currentDir, IGNORE_FILENAME);
12530
+ const filePatterns = await this.parseIgnoreFile(ignoreFile);
12531
+ patterns.push(...filePatterns);
12532
+ if (currentDir === normalizedStop) {
12533
+ break;
12534
+ }
12535
+ const parentDir = path16.dirname(currentDir);
12536
+ if (parentDir === currentDir) {
12537
+ break;
12538
+ }
12539
+ currentDir = parentDir;
12540
+ }
12541
+ return patterns;
12542
+ }
12543
+ async parseIgnoreFile(filePath) {
12544
+ let content;
12545
+ try {
12546
+ content = await fs15.readFile(filePath, "utf-8");
12547
+ } catch (err) {
12548
+ if (err.code === "ENOENT") {
12549
+ return [];
12550
+ }
12551
+ throw err;
12552
+ }
12553
+ return content.split("\n").map((line) => line.trim()).filter((line) => line.length > 0 && !line.startsWith("#"));
12554
+ }
12555
+ };
12556
+
12290
12557
  // apps/cli/src/infra/commands/lintHandler.ts
12291
12558
  var SEVERITY_LEVELS = {
12292
12559
  ["warning" /* WARNING */]: 0,
@@ -12297,7 +12564,7 @@ function isNotLoggedInError(error) {
12297
12564
  }
12298
12565
  async function lintHandler(args2, deps) {
12299
12566
  const {
12300
- path: path22,
12567
+ path: path25,
12301
12568
  draft,
12302
12569
  rule,
12303
12570
  language,
@@ -12312,20 +12579,52 @@ async function lintHandler(args2, deps) {
12312
12579
  humanReadableLogger,
12313
12580
  ideLintLogger,
12314
12581
  resolvePath,
12315
- exit
12582
+ exit,
12583
+ packmindIgnoreReader = new PackmindIgnoreReader()
12316
12584
  } = deps;
12317
12585
  if (draft && !rule) {
12318
12586
  throw new Error("option --rule is required to use --draft mode");
12319
12587
  }
12320
12588
  const startedAt = Date.now();
12321
- const targetPath = path22 ?? ".";
12589
+ const targetPath = path25 ?? ".";
12322
12590
  const absolutePath = resolvePath(targetPath);
12323
- if (diff) {
12324
- const gitRoot = await packmindCliHexa.tryGetGitRepositoryRoot(absolutePath);
12325
- if (!gitRoot) {
12326
- throw new Error(
12327
- "The --changed-files and --changed-lines options require the project to be in a Git repository"
12591
+ let stats;
12592
+ try {
12593
+ stats = await fs16.stat(absolutePath);
12594
+ } catch (err) {
12595
+ const isNotFound = err.code === "ENOENT";
12596
+ const message = isNotFound ? `File or directory "${absolutePath}" does not exist` : `Cannot access "${absolutePath}": ${err.message}`;
12597
+ logErrorConsole(message);
12598
+ exit(1);
12599
+ return;
12600
+ }
12601
+ const isFile = stats.isFile();
12602
+ const directoryForOps = isFile ? pathModule.dirname(absolutePath) : absolutePath;
12603
+ const gitRoot = await packmindCliHexa.tryGetGitRepositoryRoot(directoryForOps);
12604
+ if (diff && !gitRoot) {
12605
+ throw new Error(
12606
+ "The --changed-files and --changed-lines options require the project to be in a Git repository"
12607
+ );
12608
+ }
12609
+ let ignorePatterns = [];
12610
+ try {
12611
+ ignorePatterns = await packmindIgnoreReader.readIgnorePatterns(
12612
+ directoryForOps,
12613
+ gitRoot
12614
+ );
12615
+ } catch (err) {
12616
+ logWarningConsole(
12617
+ `Failed to read .packmindignore: ${err.message}`
12618
+ );
12619
+ }
12620
+ if (isFile && ignorePatterns.length > 0) {
12621
+ const relPath = pathModule.relative(gitRoot ?? directoryForOps, absolutePath).replace(/\\/g, "/");
12622
+ if (ignorePatterns.some((p) => matchesGlobPattern(relPath, p))) {
12623
+ logWarningConsole(
12624
+ `File "${absolutePath}" is excluded by .packmindignore`
12328
12625
  );
12626
+ exit(0);
12627
+ return;
12329
12628
  }
12330
12629
  }
12331
12630
  let violations = [];
@@ -12337,14 +12636,14 @@ async function lintHandler(args2, deps) {
12337
12636
  standardSlug: rule?.standardSlug,
12338
12637
  ruleId: rule?.ruleId,
12339
12638
  language,
12340
- diffMode: diff
12639
+ diffMode: diff,
12640
+ ignorePatterns
12341
12641
  });
12342
12642
  violations = result.violations;
12343
12643
  } else {
12344
- const stopDirectory = await packmindCliHexa.tryGetGitRepositoryRoot(absolutePath);
12345
12644
  const hierarchicalConfig = await packmindCliHexa.readHierarchicalConfig(
12346
- absolutePath,
12347
- stopDirectory
12645
+ directoryForOps,
12646
+ gitRoot
12348
12647
  );
12349
12648
  if (!hierarchicalConfig.hasConfigs) {
12350
12649
  throw new Error(
@@ -12353,7 +12652,8 @@ async function lintHandler(args2, deps) {
12353
12652
  }
12354
12653
  const result = await packmindCliHexa.lintFilesFromConfig({
12355
12654
  path: absolutePath,
12356
- diffMode: diff
12655
+ diffMode: diff,
12656
+ ignorePatterns
12357
12657
  });
12358
12658
  violations = result.violations;
12359
12659
  }
@@ -12537,7 +12837,7 @@ var lintCommand = (0, import_cmd_ts.command)({
12537
12837
  packmindCliHexa: new PackmindCliHexa(packmindLogger),
12538
12838
  humanReadableLogger: new HumanReadableLogger(),
12539
12839
  ideLintLogger: new IDELintLogger(),
12540
- resolvePath: (targetPath) => pathModule.isAbsolute(targetPath) ? targetPath : pathModule.resolve(process.cwd(), targetPath),
12840
+ resolvePath: (targetPath) => pathModule2.isAbsolute(targetPath) ? targetPath : pathModule2.resolve(process.cwd(), targetPath),
12541
12841
  exit: (code) => process.exit(code)
12542
12842
  };
12543
12843
  await lintHandler({ ...args2, diff }, deps);
@@ -12545,7 +12845,7 @@ var lintCommand = (0, import_cmd_ts.command)({
12545
12845
  });
12546
12846
 
12547
12847
  // apps/cli/src/wasm-runtime.ts
12548
- var import_fs18 = require("fs");
12848
+ var import_fs19 = require("fs");
12549
12849
  var import_path3 = require("path");
12550
12850
  var import_os = require("os");
12551
12851
 
@@ -12577,20 +12877,20 @@ function hasEmbeddedWasmFiles() {
12577
12877
  return Object.keys(EMBEDDED_WASM_FILES).length > 0;
12578
12878
  }
12579
12879
  function extractWasmFiles() {
12580
- if (wasmExtractedDir && (0, import_fs18.existsSync)(wasmExtractedDir)) {
12880
+ if (wasmExtractedDir && (0, import_fs19.existsSync)(wasmExtractedDir)) {
12581
12881
  return wasmExtractedDir;
12582
12882
  }
12583
12883
  const tempDir = (0, import_path3.join)((0, import_os.tmpdir)(), `packmind-wasm-${process.pid}`);
12584
- if (!(0, import_fs18.existsSync)(tempDir)) {
12585
- (0, import_fs18.mkdirSync)(tempDir, { recursive: true });
12884
+ if (!(0, import_fs19.existsSync)(tempDir)) {
12885
+ (0, import_fs19.mkdirSync)(tempDir, { recursive: true });
12586
12886
  }
12587
12887
  for (const [filename, base64Data] of Object.entries(EMBEDDED_WASM_FILES)) {
12588
12888
  const wasmPath = (0, import_path3.join)(tempDir, filename);
12589
- if ((0, import_fs18.existsSync)(wasmPath)) {
12889
+ if ((0, import_fs19.existsSync)(wasmPath)) {
12590
12890
  continue;
12591
12891
  }
12592
12892
  const wasmBuffer = Buffer.from(base64Data, "base64");
12593
- (0, import_fs18.writeFileSync)(wasmPath, wasmBuffer);
12893
+ (0, import_fs19.writeFileSync)(wasmPath, wasmBuffer);
12594
12894
  }
12595
12895
  wasmExtractedDir = tempDir;
12596
12896
  process.on("exit", () => {
@@ -12600,8 +12900,8 @@ function extractWasmFiles() {
12600
12900
 
12601
12901
  // apps/cli/src/main.ts
12602
12902
  var import_dotenv = require("dotenv");
12603
- var fs19 = __toESM(require("fs"));
12604
- var path21 = __toESM(require("path"));
12903
+ var fs23 = __toESM(require("fs"));
12904
+ var path24 = __toESM(require("path"));
12605
12905
 
12606
12906
  // apps/cli/src/infra/commands/InstallCommand.ts
12607
12907
  var import_cmd_ts2 = __toESM(require_cjs());
@@ -12884,8 +13184,9 @@ async function executeInstallForDirectory(directory, deps) {
12884
13184
  packagesSlugs: configPackages,
12885
13185
  previousPackagesSlugs: configPackages,
12886
13186
  // Pass for consistency
12887
- agents: configAgents
13187
+ agents: configAgents,
12888
13188
  // Pass agents from config if present
13189
+ cliVersion: CLI_VERSION
12889
13190
  });
12890
13191
  const parts = [];
12891
13192
  if (result.recipesCount > 0) parts.push(`${result.recipesCount} commands`);
@@ -13038,8 +13339,9 @@ async function installPackagesHandler(args2, deps) {
13038
13339
  gitRemoteUrl,
13039
13340
  gitBranch,
13040
13341
  relativePath,
13041
- agents: configAgents
13342
+ agents: configAgents,
13042
13343
  // Pass agents from config if present (overrides org-level)
13344
+ cliVersion: CLI_VERSION
13043
13345
  });
13044
13346
  const parts = [];
13045
13347
  if (result.recipesCount > 0) parts.push(`${result.recipesCount} commands`);
@@ -13180,11 +13482,17 @@ async function uninstallPackagesHandler(args2, deps) {
13180
13482
  };
13181
13483
  }
13182
13484
  let configPackages;
13485
+ let configAgents;
13183
13486
  let configFileExists = false;
13184
13487
  try {
13185
13488
  configFileExists = await packmindCliHexa.configExists(cwd);
13186
- const config = await packmindCliHexa.readConfig(cwd);
13187
- configPackages = config.packages;
13489
+ const fullConfig = await packmindCliHexa.readFullConfig(cwd);
13490
+ if (fullConfig) {
13491
+ configPackages = fullConfig.packages;
13492
+ configAgents = fullConfig.agents;
13493
+ } else {
13494
+ configPackages = {};
13495
+ }
13188
13496
  } catch (err) {
13189
13497
  error("\u274C Failed to read packmind.json");
13190
13498
  if (err instanceof Error) {
@@ -13253,7 +13561,9 @@ async function uninstallPackagesHandler(args2, deps) {
13253
13561
  const result = await packmindCliHexa.installPackages({
13254
13562
  baseDirectory: cwd,
13255
13563
  packagesSlugs: [],
13256
- previousPackagesSlugs: Object.keys(configPackages)
13564
+ previousPackagesSlugs: Object.keys(configPackages),
13565
+ agents: configAgents,
13566
+ cliVersion: CLI_VERSION
13257
13567
  });
13258
13568
  log(`
13259
13569
  removed ${result.filesDeleted} files`);
@@ -13273,7 +13583,9 @@ removed ${result.filesDeleted} files`);
13273
13583
  const result = await packmindCliHexa.installPackages({
13274
13584
  baseDirectory: cwd,
13275
13585
  packagesSlugs: remainingPackages,
13276
- previousPackagesSlugs: Object.keys(configPackages)
13586
+ previousPackagesSlugs: Object.keys(configPackages),
13587
+ agents: configAgents,
13588
+ cliVersion: CLI_VERSION
13277
13589
  });
13278
13590
  if (result.recipesCount > 0 || result.standardsCount > 0) {
13279
13591
  log(
@@ -13676,13 +13988,13 @@ Credentials are loaded from (in order of priority):`);
13676
13988
 
13677
13989
  // apps/cli/src/infra/commands/SetupMcpCommand.ts
13678
13990
  var import_cmd_ts7 = __toESM(require_cjs());
13679
- var fs15 = __toESM(require("fs"));
13991
+ var fs18 = __toESM(require("fs"));
13680
13992
  var readline2 = __toESM(require("readline"));
13681
13993
  var inquirer = __toESM(require("inquirer"));
13682
13994
 
13683
13995
  // apps/cli/src/application/services/AgentDetectionService.ts
13684
- var fs14 = __toESM(require("fs"));
13685
- var path15 = __toESM(require("path"));
13996
+ var fs17 = __toESM(require("fs"));
13997
+ var path17 = __toESM(require("path"));
13686
13998
  var os4 = __toESM(require("os"));
13687
13999
  var import_child_process3 = require("child_process");
13688
14000
  var AgentDetectionService = class {
@@ -13709,16 +14021,16 @@ var AgentDetectionService = class {
13709
14021
  return this.isCommandAvailable("claude");
13710
14022
  }
13711
14023
  isCursorAvailable() {
13712
- const cursorConfigDir = path15.join(os4.homedir(), ".cursor");
13713
- return fs14.existsSync(cursorConfigDir);
14024
+ const cursorConfigDir = path17.join(os4.homedir(), ".cursor");
14025
+ return fs17.existsSync(cursorConfigDir);
13714
14026
  }
13715
14027
  isVSCodeAvailable() {
13716
- const vscodeDir = path15.join(this.projectDir, ".vscode");
13717
- return fs14.existsSync(vscodeDir);
14028
+ const vscodeDir = path17.join(this.projectDir, ".vscode");
14029
+ return fs17.existsSync(vscodeDir);
13718
14030
  }
13719
14031
  isContinueAvailable() {
13720
- const continueDir = path15.join(this.projectDir, ".continue");
13721
- return fs14.existsSync(continueDir);
14032
+ const continueDir = path17.join(this.projectDir, ".continue");
14033
+ return fs17.existsSync(continueDir);
13722
14034
  }
13723
14035
  isCommandAvailable(command22) {
13724
14036
  try {
@@ -13757,8 +14069,8 @@ var ALL_AGENTS2 = [
13757
14069
  { type: "continue", name: "Continue.dev" }
13758
14070
  ];
13759
14071
  async function promptAgentsWithReadline(choices) {
13760
- const input = fs15.createReadStream("/dev/tty");
13761
- const output = fs15.createWriteStream("/dev/tty");
14072
+ const input = fs18.createReadStream("/dev/tty");
14073
+ const output = fs18.createWriteStream("/dev/tty");
13762
14074
  const rl = readline2.createInterface({
13763
14075
  input,
13764
14076
  output
@@ -13772,7 +14084,7 @@ async function promptAgentsWithReadline(choices) {
13772
14084
  output.write("\n");
13773
14085
  const preselected = choices.map((c, i) => c.checked ? i + 1 : null).filter((i) => i !== null);
13774
14086
  const defaultValue = preselected.length > 0 ? preselected.join(",") : "1,2,3";
13775
- return new Promise((resolve9) => {
14087
+ return new Promise((resolve11) => {
13776
14088
  rl.question(
13777
14089
  `Enter numbers separated by commas (default: ${defaultValue}): `,
13778
14090
  (answer) => {
@@ -13783,7 +14095,7 @@ async function promptAgentsWithReadline(choices) {
13783
14095
  const numbersStr = trimmed === "" ? defaultValue : trimmed;
13784
14096
  const numbers = numbersStr.split(",").map((s) => parseInt(s.trim(), 10)).filter((n) => !isNaN(n) && n >= 1 && n <= choices.length);
13785
14097
  const selectedAgents = numbers.map((n) => choices[n - 1].value);
13786
- resolve9(selectedAgents);
14098
+ resolve11(selectedAgents);
13787
14099
  }
13788
14100
  );
13789
14101
  });
@@ -14126,7 +14438,7 @@ var import_cmd_ts15 = __toESM(require_cjs());
14126
14438
  var import_cmd_ts13 = __toESM(require_cjs());
14127
14439
 
14128
14440
  // apps/cli/src/infra/utils/readPlaybookFile.ts
14129
- var fs16 = __toESM(require("fs/promises"));
14441
+ var fs19 = __toESM(require("fs/promises"));
14130
14442
 
14131
14443
  // apps/cli/src/domain/entities/PlaybookDTO.ts
14132
14444
  var import_zod = require("zod");
@@ -14179,7 +14491,7 @@ function parseAndValidatePlaybook(content) {
14179
14491
  }
14180
14492
  async function readPlaybookFile(filePath) {
14181
14493
  try {
14182
- const content = await fs16.readFile(filePath, "utf-8");
14494
+ const content = await fs19.readFile(filePath, "utf-8");
14183
14495
  return parseAndValidatePlaybook(content);
14184
14496
  } catch (e) {
14185
14497
  return {
@@ -14415,7 +14727,7 @@ var import_cmd_ts18 = __toESM(require_cjs());
14415
14727
  var import_cmd_ts16 = __toESM(require_cjs());
14416
14728
 
14417
14729
  // apps/cli/src/infra/utils/readCommandPlaybookFile.ts
14418
- var fs17 = __toESM(require("fs/promises"));
14730
+ var fs20 = __toESM(require("fs/promises"));
14419
14731
 
14420
14732
  // apps/cli/src/domain/entities/CommandPlaybookDTO.ts
14421
14733
  var import_zod2 = require("zod");
@@ -14467,7 +14779,7 @@ function parseAndValidateCommandPlaybook(content) {
14467
14779
  }
14468
14780
  async function readCommandPlaybookFile(filePath) {
14469
14781
  try {
14470
- const content = await fs17.readFile(filePath, "utf-8");
14782
+ const content = await fs20.readFile(filePath, "utf-8");
14471
14783
  return parseAndValidateCommandPlaybook(content);
14472
14784
  } catch (e) {
14473
14785
  return {
@@ -14692,9 +15004,13 @@ var commandsCommand = (0, import_cmd_ts18.subcommands)({
14692
15004
  });
14693
15005
 
14694
15006
  // apps/cli/src/infra/commands/DiffCommand.ts
14695
- var import_fs20 = require("fs");
15007
+ var import_fs21 = require("fs");
14696
15008
  var import_cmd_ts19 = __toESM(require_cjs());
14697
15009
 
15010
+ // apps/cli/src/infra/commands/diffArtefactsHandler.ts
15011
+ var nodePath = __toESM(require("path"));
15012
+ var fs21 = __toESM(require("fs/promises"));
15013
+
14698
15014
  // apps/cli/src/infra/utils/diffFormatter.ts
14699
15015
  var import_diff4 = require("diff");
14700
15016
  init_source();
@@ -14722,7 +15038,7 @@ function formatContentDiff(oldContent, newContent) {
14722
15038
 
14723
15039
  // apps/cli/src/infra/utils/editorMessage.ts
14724
15040
  var import_child_process4 = require("child_process");
14725
- var import_fs19 = require("fs");
15041
+ var import_fs20 = require("fs");
14726
15042
  var import_os2 = require("os");
14727
15043
  var import_path4 = require("path");
14728
15044
  var MAX_MESSAGE_LENGTH = 1024;
@@ -14734,7 +15050,7 @@ var EDITOR_TEMPLATE = `
14734
15050
  function openEditorForMessage() {
14735
15051
  const tmpFile = (0, import_path4.join)((0, import_os2.tmpdir)(), `packmind-msg-${Date.now()}.txt`);
14736
15052
  try {
14737
- (0, import_fs19.writeFileSync)(tmpFile, EDITOR_TEMPLATE, "utf-8");
15053
+ (0, import_fs20.writeFileSync)(tmpFile, EDITOR_TEMPLATE, "utf-8");
14738
15054
  const editor = process.env.EDITOR || process.env.VISUAL || "vi";
14739
15055
  const result = (0, import_child_process4.spawnSync)(editor, [tmpFile], {
14740
15056
  stdio: "inherit"
@@ -14742,11 +15058,11 @@ function openEditorForMessage() {
14742
15058
  if (result.status !== 0) {
14743
15059
  throw new Error(`Editor exited with status ${result.status}`);
14744
15060
  }
14745
- const content = (0, import_fs19.readFileSync)(tmpFile, "utf-8");
15061
+ const content = (0, import_fs20.readFileSync)(tmpFile, "utf-8");
14746
15062
  return content.split("\n").filter((line) => !line.startsWith("#")).join("\n").trim();
14747
15063
  } finally {
14748
15064
  try {
14749
- (0, import_fs19.unlinkSync)(tmpFile);
15065
+ (0, import_fs20.unlinkSync)(tmpFile);
14750
15066
  } catch {
14751
15067
  }
14752
15068
  }
@@ -14866,72 +15182,49 @@ function buildSubmittedFooter(submittedDiffs) {
14866
15182
  const proposalWord = proposalCount === 1 ? "change proposal" : "change proposals";
14867
15183
  return `${proposalCount} ${proposalWord} ignored, run \`packmind-cli diff --include-submitted\` to see what's waiting for validation`;
14868
15184
  }
14869
- async function readConfigAndPackages(deps) {
14870
- const { packmindCliHexa, exit, getCwd, log, error } = deps;
14871
- const cwd = getCwd();
14872
- let configPackages;
14873
- let configAgents;
14874
- try {
14875
- const fullConfig = await packmindCliHexa.readFullConfig(cwd);
14876
- if (fullConfig) {
14877
- configPackages = Object.keys(fullConfig.packages);
14878
- configAgents = fullConfig.agents;
14879
- } else {
14880
- configPackages = [];
14881
- }
14882
- } catch (err) {
14883
- error("ERROR Failed to parse packmind.json");
14884
- if (err instanceof Error) {
14885
- error(`ERROR ${err.message}`);
14886
- } else {
14887
- error(`ERROR ${String(err)}`);
14888
- }
14889
- error("\n\u{1F4A1} Please fix the packmind.json file or delete it to continue.");
14890
- exit(1);
14891
- return null;
15185
+ async function findTargetDirectories(searchPath, packmindCliHexa) {
15186
+ const targets = [];
15187
+ const exists = await packmindCliHexa.configExists(searchPath);
15188
+ if (exists) {
15189
+ targets.push(searchPath);
14892
15190
  }
14893
- if (configPackages.length === 0) {
14894
- log("Usage: packmind-cli diff");
14895
- log("");
14896
- log("Compare local command files against the server.");
14897
- log("Configure packages in packmind.json first.");
14898
- exit(0);
14899
- return null;
15191
+ const descendants = await packmindCliHexa.findDescendantConfigs(searchPath);
15192
+ for (const dir of descendants) {
15193
+ if (!targets.includes(dir)) {
15194
+ targets.push(dir);
15195
+ }
14900
15196
  }
14901
- return { configPackages, configAgents };
15197
+ return targets;
15198
+ }
15199
+ function computeRelativePath(targetAbsDir, gitRoot) {
15200
+ const rel = nodePath.relative(gitRoot, targetAbsDir);
15201
+ if (rel.startsWith("..")) return "/";
15202
+ return rel ? `/${rel}/` : "/";
14902
15203
  }
14903
15204
  async function collectGitInfo(deps) {
14904
- const { packmindCliHexa, exit, getCwd, error } = deps;
15205
+ const { packmindCliHexa, exit, getCwd } = deps;
14905
15206
  const cwd = getCwd();
14906
15207
  let gitRemoteUrl;
14907
15208
  let gitBranch;
14908
- let relativePath;
14909
15209
  const gitRoot = await packmindCliHexa.tryGetGitRepositoryRoot(cwd);
14910
15210
  if (gitRoot) {
14911
15211
  try {
14912
15212
  gitRemoteUrl = packmindCliHexa.getGitRemoteUrlFromPath(gitRoot);
14913
15213
  gitBranch = packmindCliHexa.getCurrentBranch(gitRoot);
14914
- relativePath = cwd.startsWith(gitRoot) ? cwd.slice(gitRoot.length) : "/";
14915
- if (!relativePath.startsWith("/")) {
14916
- relativePath = "/" + relativePath;
14917
- }
14918
- if (!relativePath.endsWith("/")) {
14919
- relativePath = relativePath + "/";
14920
- }
14921
15214
  } catch (err) {
14922
15215
  logWarningConsole(
14923
15216
  `Failed to collect git info: ${err instanceof Error ? err.message : String(err)}`
14924
15217
  );
14925
15218
  }
14926
15219
  }
14927
- if (!gitRemoteUrl || !gitBranch || !relativePath) {
14928
- error(
14929
- "\n\u274C Could not determine git repository info. The diff command requires a git repository with a remote configured."
15220
+ if (!gitRemoteUrl || !gitBranch || !gitRoot) {
15221
+ logErrorConsole(
15222
+ "Could not determine git repository info. The diff command requires a git repository with a remote configured."
14930
15223
  );
14931
15224
  exit(1);
14932
15225
  return null;
14933
15226
  }
14934
- return { gitRemoteUrl, gitBranch, relativePath };
15227
+ return { gitRemoteUrl, gitBranch, gitRoot };
14935
15228
  }
14936
15229
  async function handleSubmission(params) {
14937
15230
  const { packmindCliHexa, unsubmittedItems, messageFlag, exit } = params;
@@ -15030,6 +15323,7 @@ function extractUniqueAndSortedArtefacts(groups) {
15030
15323
  function displayDiffs(params) {
15031
15324
  const {
15032
15325
  diffsToDisplay,
15326
+ displayPathMap,
15033
15327
  submittedLookup,
15034
15328
  includeSubmitted,
15035
15329
  unsubmittedItems,
@@ -15047,7 +15341,7 @@ Changes found:
15047
15341
  const subGroups = subGroupByChangeContent(groupDiffs);
15048
15342
  for (const subGroup of subGroups) {
15049
15343
  for (const diff of subGroup) {
15050
- log(` ${formatFilePath(diff.filePath)}`);
15344
+ log(` ${formatFilePath(displayPathMap.get(diff) ?? diff.filePath)}`);
15051
15345
  }
15052
15346
  const label = CHANGE_PROPOSAL_TYPE_LABELS[subGroup[0].type] ?? "content changed";
15053
15347
  const checkItem = submittedLookup.get(subGroup[0]);
@@ -15092,37 +15386,96 @@ async function diffArtefactsHandler(deps) {
15092
15386
  exit,
15093
15387
  getCwd,
15094
15388
  log,
15095
- error,
15096
15389
  submit,
15097
15390
  includeSubmitted,
15098
15391
  message: messageFlag
15099
15392
  } = deps;
15100
15393
  const cwd = getCwd();
15101
- const config = await readConfigAndPackages(deps);
15102
- if (!config) {
15394
+ const searchPath = nodePath.resolve(cwd, deps.path ?? ".");
15395
+ if (deps.path !== void 0) {
15396
+ try {
15397
+ await fs21.stat(searchPath);
15398
+ } catch {
15399
+ logErrorConsole(`Path does not exist: ${searchPath}`);
15400
+ exit(1);
15401
+ return { diffsFound: 0 };
15402
+ }
15403
+ }
15404
+ let targetDirs;
15405
+ try {
15406
+ targetDirs = await findTargetDirectories(searchPath, packmindCliHexa);
15407
+ } catch (err) {
15408
+ logErrorConsole(
15409
+ `Failed to discover target directories: ${err instanceof Error ? err.message : String(err)}`
15410
+ );
15411
+ exit(1);
15412
+ return { diffsFound: 0 };
15413
+ }
15414
+ if (targetDirs.length === 0) {
15415
+ const hierarchicalConfig = await packmindCliHexa.readHierarchicalConfig(
15416
+ cwd,
15417
+ null
15418
+ );
15419
+ if (!hierarchicalConfig.hasConfigs) {
15420
+ logErrorConsole(
15421
+ "Not inside a Packmind project. No packmind.json found in the current directory or any parent directory."
15422
+ );
15423
+ exit(1);
15424
+ } else {
15425
+ log("No Packmind targets found under the current directory.");
15426
+ exit(0);
15427
+ }
15103
15428
  return { diffsFound: 0 };
15104
15429
  }
15105
- const { configPackages, configAgents } = config;
15106
15430
  try {
15107
15431
  const gitInfo = await collectGitInfo(deps);
15108
15432
  if (!gitInfo) {
15109
15433
  return { diffsFound: 0 };
15110
15434
  }
15111
- const { gitRemoteUrl, gitBranch, relativePath } = gitInfo;
15112
- const packageCount = configPackages.length;
15113
- const packageWord = packageCount === 1 ? "package" : "packages";
15114
- logInfoConsole(
15115
- `Comparing ${packageCount} ${packageWord}: ${configPackages.join(", ")}...`
15116
- );
15117
- const diffs = await packmindCliHexa.diffArtefacts({
15118
- baseDirectory: cwd,
15119
- packagesSlugs: configPackages,
15120
- gitRemoteUrl,
15121
- gitBranch,
15122
- relativePath,
15123
- agents: configAgents
15124
- });
15125
- if (diffs.length === 0) {
15435
+ const { gitRemoteUrl, gitBranch, gitRoot } = gitInfo;
15436
+ const targetResults = [];
15437
+ for (const targetDir of targetDirs) {
15438
+ const config = await packmindCliHexa.readFullConfig(targetDir);
15439
+ if (!config) continue;
15440
+ const configPackages = Object.keys(config.packages);
15441
+ if (configPackages.length === 0) continue;
15442
+ const relativePath = computeRelativePath(targetDir, gitRoot);
15443
+ const targetRelativePath = nodePath.relative(cwd, targetDir);
15444
+ if (targetDirs.length > 1) {
15445
+ log("");
15446
+ logInfoConsole(`Target: ${targetRelativePath}`);
15447
+ }
15448
+ const packageCount = configPackages.length;
15449
+ const packageWord = packageCount === 1 ? "package" : "packages";
15450
+ logInfoConsole(
15451
+ `Comparing ${packageCount} ${packageWord}: ${configPackages.join(", ")}...`
15452
+ );
15453
+ const diffs = await packmindCliHexa.diffArtefacts({
15454
+ baseDirectory: targetDir,
15455
+ packagesSlugs: configPackages,
15456
+ gitRemoteUrl,
15457
+ gitBranch,
15458
+ relativePath,
15459
+ agents: config.agents
15460
+ });
15461
+ targetResults.push({ targetRelativePath, diffs });
15462
+ }
15463
+ if (targetResults.length === 0) {
15464
+ log("No packages configured in any target.");
15465
+ exit(0);
15466
+ return { diffsFound: 0 };
15467
+ }
15468
+ const displayPathMap = /* @__PURE__ */ new Map();
15469
+ for (const { targetRelativePath, diffs } of targetResults) {
15470
+ for (const diff of diffs) {
15471
+ displayPathMap.set(
15472
+ diff,
15473
+ targetRelativePath ? `${targetRelativePath}/${diff.filePath}` : diff.filePath
15474
+ );
15475
+ }
15476
+ }
15477
+ const allDiffs = targetResults.flatMap((r) => r.diffs);
15478
+ if (allDiffs.length === 0) {
15126
15479
  log("No changes found.");
15127
15480
  if (submit) {
15128
15481
  logInfoConsole("No changes to submit.");
@@ -15130,11 +15483,11 @@ async function diffArtefactsHandler(deps) {
15130
15483
  exit(0);
15131
15484
  return { diffsFound: 0 };
15132
15485
  }
15133
- const allGroupedDiffs = Array.from(groupDiffsByArtefact(diffs).values());
15486
+ const allGroupedDiffs = Array.from(groupDiffsByArtefact(allDiffs).values());
15134
15487
  const checkResult = await packmindCliHexa.checkDiffs(allGroupedDiffs);
15135
15488
  const submittedItems = checkResult.results.filter((r) => r.exists);
15136
15489
  const unsubmittedItems = checkResult.results.filter((r) => !r.exists);
15137
- const diffsToDisplay = includeSubmitted ? diffs : unsubmittedItems.map((r) => r.diff);
15490
+ const diffsToDisplay = includeSubmitted ? allDiffs : unsubmittedItems.map((r) => r.diff);
15138
15491
  const submittedLookup = /* @__PURE__ */ new Map();
15139
15492
  for (const item of checkResult.results) {
15140
15493
  submittedLookup.set(item.diff, item);
@@ -15152,6 +15505,7 @@ async function diffArtefactsHandler(deps) {
15152
15505
  }
15153
15506
  const changeCount = displayDiffs({
15154
15507
  diffsToDisplay,
15508
+ displayPathMap,
15155
15509
  submittedLookup,
15156
15510
  includeSubmitted,
15157
15511
  unsubmittedItems,
@@ -15172,11 +15526,10 @@ async function diffArtefactsHandler(deps) {
15172
15526
  exit(0);
15173
15527
  return { diffsFound: changeCount };
15174
15528
  } catch (err) {
15175
- error("\n\u274C Failed to diff:");
15176
15529
  if (err instanceof Error) {
15177
- error(` ${err.message}`);
15530
+ logErrorConsole(err.message);
15178
15531
  } else {
15179
- error(` ${String(err)}`);
15532
+ logErrorConsole(String(err));
15180
15533
  }
15181
15534
  exit(1);
15182
15535
  return { diffsFound: 0 };
@@ -15184,40 +15537,10 @@ async function diffArtefactsHandler(deps) {
15184
15537
  }
15185
15538
 
15186
15539
  // apps/cli/src/infra/commands/diffAddHandler.ts
15187
- var path17 = __toESM(require("path"));
15188
-
15189
- // apps/cli/src/application/utils/resolveArtefactFromPath.ts
15190
- function resolveArtefactFromPath(filePath) {
15191
- const normalized = normalizePath(filePath);
15192
- for (const [agent, paths] of Object.entries(CODING_AGENT_ARTEFACT_PATHS)) {
15193
- if (paths.command && normalized.includes(paths.command)) {
15194
- return {
15195
- artifactType: "command",
15196
- codingAgent: agent
15197
- };
15198
- }
15199
- }
15200
- for (const [agent, paths] of Object.entries(CODING_AGENT_ARTEFACT_PATHS)) {
15201
- if (paths.standard && normalized.includes(paths.standard)) {
15202
- return {
15203
- artifactType: "standard",
15204
- codingAgent: agent
15205
- };
15206
- }
15207
- }
15208
- for (const [agent, paths] of Object.entries(CODING_AGENT_ARTEFACT_PATHS)) {
15209
- if (paths.skill && normalized.includes(paths.skill)) {
15210
- return {
15211
- artifactType: "skill",
15212
- codingAgent: agent
15213
- };
15214
- }
15215
- }
15216
- return null;
15217
- }
15540
+ var path20 = __toESM(require("path"));
15218
15541
 
15219
15542
  // apps/cli/src/application/utils/parseCommandFile.ts
15220
- var path16 = __toESM(require("path"));
15543
+ var path18 = __toESM(require("path"));
15221
15544
  var FRONTMATTER_DELIMITER3 = "---";
15222
15545
  function parseCommandFile(content, filePath) {
15223
15546
  content = normalizeLineEndings(content);
@@ -15269,7 +15592,7 @@ function stripYamlQuotes2(value) {
15269
15592
  return value;
15270
15593
  }
15271
15594
  function extractFilenameSlug(filePath) {
15272
- let basename2 = path16.basename(filePath);
15595
+ let basename2 = path18.basename(filePath);
15273
15596
  if (basename2.endsWith(".prompt.md")) {
15274
15597
  basename2 = basename2.slice(0, -".prompt.md".length);
15275
15598
  } else if (basename2.endsWith(".md")) {
@@ -15350,6 +15673,23 @@ function parseSkillDirectory(files) {
15350
15673
  return { success: true, payload };
15351
15674
  }
15352
15675
 
15676
+ // apps/cli/src/application/utils/findNearestConfigDir.ts
15677
+ var path19 = __toESM(require("path"));
15678
+ async function findNearestConfigDir(startDir, packmindCliHexa) {
15679
+ let current = startDir;
15680
+ while (true) {
15681
+ const exists = await packmindCliHexa.configExists(current);
15682
+ if (exists) {
15683
+ return current;
15684
+ }
15685
+ const parent = path19.dirname(current);
15686
+ if (parent === current) {
15687
+ return null;
15688
+ }
15689
+ current = parent;
15690
+ }
15691
+ }
15692
+
15353
15693
  // apps/cli/src/infra/commands/diffAddHandler.ts
15354
15694
  async function diffAddHandler(deps) {
15355
15695
  const {
@@ -15358,7 +15698,7 @@ async function diffAddHandler(deps) {
15358
15698
  message: messageFlag,
15359
15699
  exit,
15360
15700
  getCwd,
15361
- readFile: readFile10,
15701
+ readFile: readFile12,
15362
15702
  readSkillDirectory: readSkillDirectory2
15363
15703
  } = deps;
15364
15704
  if (!filePath) {
@@ -15366,7 +15706,7 @@ async function diffAddHandler(deps) {
15366
15706
  exit(1);
15367
15707
  return;
15368
15708
  }
15369
- const absolutePath = path17.resolve(getCwd(), filePath);
15709
+ const absolutePath = path20.resolve(getCwd(), filePath);
15370
15710
  const artefactResult = resolveArtefactFromPath(absolutePath);
15371
15711
  if (!artefactResult) {
15372
15712
  logErrorConsole(
@@ -15377,7 +15717,7 @@ async function diffAddHandler(deps) {
15377
15717
  }
15378
15718
  let diffResult;
15379
15719
  if (artefactResult.artifactType === "skill") {
15380
- const dirPath = absolutePath.endsWith("SKILL.md") ? path17.dirname(absolutePath) : absolutePath;
15720
+ const dirPath = absolutePath.endsWith("SKILL.md") ? path20.dirname(absolutePath) : absolutePath;
15381
15721
  let files;
15382
15722
  try {
15383
15723
  files = await readSkillDirectory2(dirPath);
@@ -15405,7 +15745,7 @@ async function diffAddHandler(deps) {
15405
15745
  } else {
15406
15746
  let content;
15407
15747
  try {
15408
- content = readFile10(absolutePath);
15748
+ content = readFile12(absolutePath);
15409
15749
  } catch (err) {
15410
15750
  if (isErrnoException(err) && err.code === "EISDIR") {
15411
15751
  logErrorConsole(
@@ -15459,18 +15799,25 @@ async function diffAddHandler(deps) {
15459
15799
  return;
15460
15800
  }
15461
15801
  const space = await packmindCliHexa.getPackmindGateway().spaces.getGlobal();
15802
+ const fileDir = artefactResult.artifactType === "skill" ? absolutePath.endsWith("SKILL.md") ? path20.dirname(path20.dirname(absolutePath)) : path20.dirname(absolutePath) : path20.dirname(absolutePath);
15803
+ const targetDir = await findNearestConfigDir(fileDir, packmindCliHexa);
15804
+ if (!targetDir) {
15805
+ logErrorConsole(
15806
+ "Not inside a Packmind project. No packmind.json found in any parent directory."
15807
+ );
15808
+ exit(1);
15809
+ return;
15810
+ }
15462
15811
  let targetId;
15463
15812
  try {
15464
- const cwd = getCwd();
15465
- const fullConfig = await packmindCliHexa.readFullConfig(cwd);
15813
+ const fullConfig = await packmindCliHexa.readFullConfig(targetDir);
15466
15814
  const configPackages = fullConfig ? Object.keys(fullConfig.packages) : [];
15467
- const gitRoot = await packmindCliHexa.tryGetGitRepositoryRoot(cwd);
15815
+ const gitRoot = await packmindCliHexa.tryGetGitRepositoryRoot(targetDir);
15468
15816
  if (gitRoot && configPackages.length > 0) {
15469
15817
  const gitRemoteUrl = packmindCliHexa.getGitRemoteUrlFromPath(gitRoot);
15470
15818
  const gitBranch = packmindCliHexa.getCurrentBranch(gitRoot);
15471
- let relativePath = cwd.startsWith(gitRoot) ? cwd.slice(gitRoot.length) : "/";
15472
- if (!relativePath.startsWith("/")) relativePath = "/" + relativePath;
15473
- if (!relativePath.endsWith("/")) relativePath = relativePath + "/";
15819
+ const rel = path20.relative(gitRoot, targetDir);
15820
+ const relativePath = rel.startsWith("..") ? "/" : rel ? `/${rel}/` : "/";
15474
15821
  const deployedContent = await packmindCliHexa.getPackmindGateway().deployment.getDeployed({
15475
15822
  packagesSlugs: configPackages,
15476
15823
  gitRemoteUrl,
@@ -15588,12 +15935,46 @@ function isErrnoException(err) {
15588
15935
  }
15589
15936
 
15590
15937
  // apps/cli/src/infra/commands/diffRemoveHandler.ts
15591
- var path18 = __toESM(require("path"));
15938
+ var path21 = __toESM(require("path"));
15592
15939
  var ARTIFACT_TYPE_LABELS2 = {
15593
15940
  command: "command",
15594
15941
  standard: "standard",
15595
15942
  skill: "skill"
15596
15943
  };
15944
+ function findArtifactInLockFile(lockFile, filePathForComparison, expectedArtifactType) {
15945
+ if (!lockFile.targetId) {
15946
+ return null;
15947
+ }
15948
+ for (const entry of Object.values(lockFile.artifacts)) {
15949
+ if (entry.type !== expectedArtifactType) {
15950
+ continue;
15951
+ }
15952
+ const matchingFile = entry.files.find(
15953
+ (f) => normalizePath(f.path) === filePathForComparison
15954
+ );
15955
+ if (matchingFile && entry.packageIds.length > 0) {
15956
+ return {
15957
+ artifactId: entry.id,
15958
+ spaceId: entry.spaceId,
15959
+ packageIds: entry.packageIds,
15960
+ artifactName: entry.name,
15961
+ targetId: lockFile.targetId
15962
+ };
15963
+ }
15964
+ }
15965
+ return null;
15966
+ }
15967
+ function computeFilePathRelativeToTarget(absoluteFilePath, cwd, gitRoot) {
15968
+ const relGitRoot = path21.relative(gitRoot, absoluteFilePath);
15969
+ const relativeToGitRoot = relGitRoot.startsWith("..") ? absoluteFilePath : relGitRoot;
15970
+ let normalizedFilePath = normalizePath(relativeToGitRoot);
15971
+ if (normalizedFilePath.startsWith("/")) {
15972
+ normalizedFilePath = normalizedFilePath.slice(1);
15973
+ }
15974
+ const relCwd = path21.relative(gitRoot, cwd);
15975
+ const relativePathPrefix = relCwd.startsWith("..") ? "" : relCwd ? relCwd + "/" : "";
15976
+ return relativePathPrefix.length > 0 && normalizedFilePath.startsWith(relativePathPrefix) ? normalizedFilePath.slice(relativePathPrefix.length) : normalizedFilePath;
15977
+ }
15597
15978
  async function diffRemoveHandler(deps) {
15598
15979
  const {
15599
15980
  packmindCliHexa,
@@ -15613,7 +15994,7 @@ async function diffRemoveHandler(deps) {
15613
15994
  return;
15614
15995
  }
15615
15996
  const cwd = getCwd();
15616
- const absolutePath = path18.resolve(cwd, filePath);
15997
+ const absolutePath = path21.resolve(cwd, filePath);
15617
15998
  const artefactResult = resolveArtefactFromPath(absolutePath);
15618
15999
  if (!artefactResult) {
15619
16000
  logErrorConsole(
@@ -15622,18 +16003,27 @@ async function diffRemoveHandler(deps) {
15622
16003
  exit(1);
15623
16004
  return;
15624
16005
  }
15625
- const matchPath = artefactResult.artifactType === "skill" ? absolutePath.endsWith("SKILL.md") ? absolutePath : path18.join(absolutePath, "SKILL.md") : absolutePath;
15626
- const skillDirPath = artefactResult.artifactType === "skill" ? absolutePath.endsWith("SKILL.md") ? path18.dirname(absolutePath) : absolutePath : void 0;
16006
+ const matchPath = artefactResult.artifactType === "skill" ? absolutePath.endsWith("SKILL.md") ? absolutePath : path21.join(absolutePath, "SKILL.md") : absolutePath;
16007
+ const skillDirPath = artefactResult.artifactType === "skill" ? absolutePath.endsWith("SKILL.md") ? path21.dirname(absolutePath) : absolutePath : void 0;
15627
16008
  const existsCheckPath = skillDirPath ?? absolutePath;
15628
16009
  if (!existsSync25(existsCheckPath)) {
15629
16010
  logErrorConsole(`File or directory does not exist: ${filePath}`);
15630
16011
  exit(1);
15631
16012
  return;
15632
16013
  }
16014
+ const fileDir = skillDirPath ? path21.dirname(skillDirPath) : path21.dirname(absolutePath);
16015
+ const targetDir = await findNearestConfigDir(fileDir, packmindCliHexa);
16016
+ if (!targetDir) {
16017
+ logErrorConsole(
16018
+ "Not inside a Packmind project. No packmind.json found in any parent directory."
16019
+ );
16020
+ exit(1);
16021
+ return;
16022
+ }
15633
16023
  let configPackages;
15634
16024
  let configAgents;
15635
16025
  try {
15636
- const fullConfig = await packmindCliHexa.readFullConfig(cwd);
16026
+ const fullConfig = await packmindCliHexa.readFullConfig(targetDir);
15637
16027
  if (fullConfig) {
15638
16028
  configPackages = Object.keys(fullConfig.packages);
15639
16029
  configAgents = fullConfig.agents;
@@ -15660,70 +16050,87 @@ async function diffRemoveHandler(deps) {
15660
16050
  exit(1);
15661
16051
  return;
15662
16052
  }
15663
- let gitRemoteUrl;
15664
- let gitBranch;
15665
- let relativePath;
15666
- const gitRoot = await packmindCliHexa.tryGetGitRepositoryRoot(cwd);
15667
- if (gitRoot) {
16053
+ const gitRoot = await packmindCliHexa.tryGetGitRepositoryRoot(targetDir);
16054
+ let metadata = null;
16055
+ const lockFile = await packmindCliHexa.readLockFile(targetDir);
16056
+ if (lockFile) {
16057
+ const lockFilePathForComparison = gitRoot ? computeFilePathRelativeToTarget(matchPath, targetDir, gitRoot) : normalizePath(path21.relative(targetDir, matchPath));
16058
+ metadata = findArtifactInLockFile(
16059
+ lockFile,
16060
+ lockFilePathForComparison,
16061
+ artefactResult.artifactType
16062
+ );
16063
+ }
16064
+ if (!metadata) {
16065
+ if (!gitRoot) {
16066
+ logErrorConsole(
16067
+ "\n\u274C Could not determine git repository info. The diff command requires a git repository with a remote configured."
16068
+ );
16069
+ exit(1);
16070
+ return;
16071
+ }
16072
+ let gitRemoteUrl;
16073
+ let gitBranch;
16074
+ let relativePath;
15668
16075
  try {
15669
16076
  gitRemoteUrl = packmindCliHexa.getGitRemoteUrlFromPath(gitRoot);
15670
16077
  gitBranch = packmindCliHexa.getCurrentBranch(gitRoot);
15671
- relativePath = cwd.startsWith(gitRoot) ? cwd.slice(gitRoot.length) : "/";
15672
- if (!relativePath.startsWith("/")) {
15673
- relativePath = "/" + relativePath;
15674
- }
15675
- if (!relativePath.endsWith("/")) {
15676
- relativePath = relativePath + "/";
15677
- }
16078
+ const rel = path21.relative(gitRoot, targetDir);
16079
+ relativePath = rel.startsWith("..") ? "/" : rel ? `/${rel}/` : "/";
15678
16080
  } catch (err) {
15679
16081
  logErrorConsole(
15680
16082
  `Failed to collect git info: ${err instanceof Error ? err.message : String(err)}`
15681
16083
  );
15682
16084
  }
15683
- }
15684
- if (!gitRemoteUrl || !gitBranch || !relativePath) {
15685
- logErrorConsole(
15686
- "\n\u274C Could not determine git repository info. The diff command requires a git repository with a remote configured."
15687
- );
15688
- exit(1);
15689
- return;
15690
- }
15691
- const deployedContent = await packmindCliHexa.getPackmindGateway().deployment.getDeployed({
15692
- packagesSlugs: configPackages,
15693
- gitRemoteUrl,
15694
- gitBranch,
15695
- relativePath,
15696
- agents: configAgents
15697
- });
15698
- const relativeToGitRoot = matchPath.startsWith(gitRoot) ? matchPath.slice(gitRoot.length) : matchPath;
15699
- let normalizedFilePath = normalizePath(relativeToGitRoot);
15700
- if (normalizedFilePath.startsWith("/")) {
15701
- normalizedFilePath = normalizedFilePath.slice(1);
15702
- }
15703
- const relativePathPrefix = relativePath.startsWith("/") ? relativePath.slice(1) : relativePath;
15704
- const filePathForComparison = relativePathPrefix.length > 0 && normalizedFilePath.startsWith(relativePathPrefix) ? normalizedFilePath.slice(relativePathPrefix.length) : normalizedFilePath;
15705
- const deployedFile = deployedContent.fileUpdates.createOrUpdate.find(
15706
- (file) => normalizePath(file.path) === filePathForComparison
15707
- );
15708
- if (!deployedFile) {
15709
- const artifactTypeLabel = ARTIFACT_TYPE_LABELS2[artefactResult.artifactType];
15710
- logErrorConsole(`This ${artifactTypeLabel} does not come from Packmind`);
15711
- exit(1);
15712
- return;
15713
- }
15714
- if (!deployedFile.artifactId || !deployedFile.spaceId) {
15715
- logErrorConsole(
15716
- "Missing artifact metadata. Cannot create change proposal for removal."
16085
+ if (!gitRemoteUrl || !gitBranch || !relativePath) {
16086
+ logErrorConsole(
16087
+ "\n\u274C Could not determine git repository info. The diff command requires a git repository with a remote configured."
16088
+ );
16089
+ exit(1);
16090
+ return;
16091
+ }
16092
+ const deployedContent = await packmindCliHexa.getPackmindGateway().deployment.getDeployed({
16093
+ packagesSlugs: configPackages,
16094
+ gitRemoteUrl,
16095
+ gitBranch,
16096
+ relativePath,
16097
+ agents: configAgents
16098
+ });
16099
+ const filePathForComparison = computeFilePathRelativeToTarget(
16100
+ matchPath,
16101
+ targetDir,
16102
+ gitRoot
15717
16103
  );
15718
- exit(1);
15719
- return;
15720
- }
15721
- if (!deployedContent.targetId || !deployedFile.packageIds) {
15722
- logErrorConsole(
15723
- "Missing target or package information. Cannot create change proposal for removal."
16104
+ const deployedFile = deployedContent.fileUpdates.createOrUpdate.find(
16105
+ (file) => normalizePath(file.path) === filePathForComparison
15724
16106
  );
15725
- exit(1);
15726
- return;
16107
+ if (!deployedFile) {
16108
+ const artifactTypeLabel = ARTIFACT_TYPE_LABELS2[artefactResult.artifactType];
16109
+ logErrorConsole(`This ${artifactTypeLabel} does not come from Packmind`);
16110
+ exit(1);
16111
+ return;
16112
+ }
16113
+ if (!deployedFile.artifactId || !deployedFile.spaceId) {
16114
+ logErrorConsole(
16115
+ "Missing artifact metadata. Cannot create change proposal for removal."
16116
+ );
16117
+ exit(1);
16118
+ return;
16119
+ }
16120
+ if (!deployedContent.targetId || !deployedFile.packageIds) {
16121
+ logErrorConsole(
16122
+ "Missing target or package information. Cannot create change proposal for removal."
16123
+ );
16124
+ exit(1);
16125
+ return;
16126
+ }
16127
+ metadata = {
16128
+ artifactId: deployedFile.artifactId,
16129
+ spaceId: deployedFile.spaceId,
16130
+ packageIds: deployedFile.packageIds,
16131
+ artifactName: deployedFile.artifactName || artefactResult.artifactType,
16132
+ targetId: deployedContent.targetId
16133
+ };
15727
16134
  }
15728
16135
  let message;
15729
16136
  if (messageFlag !== void 0) {
@@ -15757,14 +16164,13 @@ async function diffRemoveHandler(deps) {
15757
16164
  filePath: absolutePath,
15758
16165
  type: changeProposalType,
15759
16166
  payload: {
15760
- targetId: deployedContent.targetId,
15761
- packageIds: deployedFile.packageIds.map(createPackageId)
16167
+ packageIds: metadata.packageIds.map(createPackageId)
15762
16168
  },
15763
- artifactName: deployedFile.artifactName || artefactResult.artifactType,
16169
+ artifactName: metadata.artifactName,
15764
16170
  artifactType: artefactResult.artifactType,
15765
- artifactId: deployedFile.artifactId,
15766
- spaceId: deployedFile.spaceId,
15767
- targetId: deployedContent.targetId
16171
+ artifactId: metadata.artifactId,
16172
+ spaceId: metadata.spaceId,
16173
+ targetId: createTargetId(metadata.targetId)
15768
16174
  };
15769
16175
  const result = await packmindCliHexa.submitDiffs([[diff]], message);
15770
16176
  for (const err of result.errors) {
@@ -15816,37 +16222,45 @@ var diffCommand = (0, import_cmd_ts19.command)({
15816
16222
  description: "Message describing the intent behind the changes (max 1024 chars)",
15817
16223
  type: (0, import_cmd_ts19.optional)(import_cmd_ts19.string)
15818
16224
  }),
16225
+ path: (0, import_cmd_ts19.option)({
16226
+ long: "path",
16227
+ short: "p",
16228
+ description: "Path to analyze (relative to current directory)",
16229
+ type: (0, import_cmd_ts19.optional)(import_cmd_ts19.string)
16230
+ }),
15819
16231
  positionals: (0, import_cmd_ts19.restPositionals)({
15820
16232
  type: import_cmd_ts19.string,
15821
16233
  displayName: "args",
15822
16234
  description: "Subcommand and arguments (e.g., add <path>, remove <path>)"
15823
16235
  })
15824
16236
  },
15825
- handler: async ({ submit, includeSubmitted, message, positionals }) => {
16237
+ handler: async ({ submit, includeSubmitted, message, path: path25, positionals }) => {
15826
16238
  const packmindLogger = new PackmindLogger("PackmindCLI", "info" /* INFO */);
15827
16239
  const packmindCliHexa = new PackmindCliHexa(packmindLogger);
15828
16240
  if (positionals[0] === "add") {
16241
+ const addFilePath = path25 && positionals[1] ? `${path25}/${positionals[1]}`.replace(/\/+/g, "/") : positionals[1];
15829
16242
  await diffAddHandler({
15830
16243
  packmindCliHexa,
15831
- filePath: positionals[1],
16244
+ filePath: addFilePath,
15832
16245
  message,
15833
16246
  exit: process.exit,
15834
16247
  getCwd: () => process.cwd(),
15835
- readFile: (p) => (0, import_fs20.readFileSync)(p, "utf-8"),
16248
+ readFile: (p) => (0, import_fs21.readFileSync)(p, "utf-8"),
15836
16249
  readSkillDirectory
15837
16250
  });
15838
16251
  return;
15839
16252
  }
15840
16253
  if (positionals[0] === "remove" || positionals[0] === "rm") {
16254
+ const removeFilePath = path25 && positionals[1] ? `${path25}/${positionals[1]}`.replace(/\/+/g, "/") : positionals[1];
15841
16255
  await diffRemoveHandler({
15842
16256
  packmindCliHexa,
15843
- filePath: positionals[1],
16257
+ filePath: removeFilePath,
15844
16258
  message,
15845
16259
  exit: process.exit,
15846
16260
  getCwd: () => process.cwd(),
15847
- existsSync: (p) => (0, import_fs20.existsSync)(p),
15848
- unlinkSync: (p) => (0, import_fs20.unlinkSync)(p),
15849
- rmSync: (p, opts) => (0, import_fs20.rmSync)(p, opts)
16261
+ existsSync: (p) => (0, import_fs21.existsSync)(p),
16262
+ unlinkSync: (p) => (0, import_fs21.unlinkSync)(p),
16263
+ rmSync: (p, opts) => (0, import_fs21.rmSync)(p, opts)
15850
16264
  });
15851
16265
  return;
15852
16266
  }
@@ -15855,10 +16269,10 @@ var diffCommand = (0, import_cmd_ts19.command)({
15855
16269
  exit: process.exit,
15856
16270
  getCwd: () => process.cwd(),
15857
16271
  log: console.log,
15858
- error: console.error,
15859
16272
  submit,
15860
16273
  includeSubmitted,
15861
- message
16274
+ message,
16275
+ path: path25
15862
16276
  });
15863
16277
  }
15864
16278
  });
@@ -16236,8 +16650,8 @@ var import_cmd_ts25 = __toESM(require_cjs());
16236
16650
  var import_cmd_ts24 = __toESM(require_cjs());
16237
16651
 
16238
16652
  // apps/cli/src/application/services/AgentArtifactDetectionService.ts
16239
- var fs18 = __toESM(require("fs/promises"));
16240
- var path19 = __toESM(require("path"));
16653
+ var fs22 = __toESM(require("fs/promises"));
16654
+ var path22 = __toESM(require("path"));
16241
16655
  var AGENT_ARTIFACT_CHECKS = [
16242
16656
  { agent: "claude", paths: [".claude"] },
16243
16657
  { agent: "cursor", paths: [".cursor"] },
@@ -16255,7 +16669,7 @@ var AgentArtifactDetectionService = class {
16255
16669
  const detected = [];
16256
16670
  for (const check of AGENT_ARTIFACT_CHECKS) {
16257
16671
  for (const relativePath of check.paths) {
16258
- const fullPath = path19.join(baseDirectory, relativePath);
16672
+ const fullPath = path22.join(baseDirectory, relativePath);
16259
16673
  const exists = await this.pathExists(fullPath);
16260
16674
  if (exists) {
16261
16675
  detected.push({
@@ -16270,7 +16684,7 @@ var AgentArtifactDetectionService = class {
16270
16684
  }
16271
16685
  async pathExists(filePath) {
16272
16686
  try {
16273
- await fs18.access(filePath);
16687
+ await fs22.access(filePath);
16274
16688
  return true;
16275
16689
  } catch {
16276
16690
  return false;
@@ -16352,7 +16766,7 @@ async function promptAgentsWithReadline2(choices) {
16352
16766
  output.write("\n");
16353
16767
  const preselected = choices.map((c, i) => c.checked ? i + 1 : null).filter((i) => i !== null);
16354
16768
  const defaultValue = preselected.length > 0 ? preselected.join(",") : "1,2,3";
16355
- return new Promise((resolve9) => {
16769
+ return new Promise((resolve11) => {
16356
16770
  rl.question(
16357
16771
  `Enter numbers separated by commas (default: ${defaultValue}): `,
16358
16772
  (answer) => {
@@ -16361,7 +16775,7 @@ async function promptAgentsWithReadline2(choices) {
16361
16775
  const numbersStr = trimmed === "" ? defaultValue : trimmed;
16362
16776
  const numbers = numbersStr.split(",").map((s) => parseInt(s.trim(), 10)).filter((n) => !isNaN(n) && n >= 1 && n <= choices.length);
16363
16777
  const selectedAgents = numbers.map((n) => choices[n - 1].value);
16364
- resolve9(selectedAgents);
16778
+ resolve11(selectedAgents);
16365
16779
  }
16366
16780
  );
16367
16781
  });
@@ -16512,7 +16926,7 @@ var import_cmd_ts27 = __toESM(require_cjs());
16512
16926
  // apps/cli/src/infra/commands/updateHandler.ts
16513
16927
  var import_path5 = __toESM(require("path"));
16514
16928
  var import_child_process5 = require("child_process");
16515
- var import_fs21 = require("fs");
16929
+ var import_fs22 = require("fs");
16516
16930
  var import_promises2 = require("stream/promises");
16517
16931
  var import_stream = require("stream");
16518
16932
  var import_semver = __toESM(require("semver"));
@@ -16579,11 +16993,11 @@ URL: ${url}`
16579
16993
  throw new Error("No response body received");
16580
16994
  }
16581
16995
  const nodeReadable = import_stream.Readable.fromWeb(res.body);
16582
- const fileStream = (0, import_fs21.createWriteStream)(targetPath);
16996
+ const fileStream = (0, import_fs22.createWriteStream)(targetPath);
16583
16997
  await (0, import_promises2.pipeline)(nodeReadable, fileStream);
16584
- const stats = (0, import_fs21.statSync)(targetPath);
16998
+ const stats = (0, import_fs22.statSync)(targetPath);
16585
16999
  if (stats.size < 1e6) {
16586
- (0, import_fs21.unlinkSync)(targetPath);
17000
+ (0, import_fs22.unlinkSync)(targetPath);
16587
17001
  throw new Error(
16588
17002
  `Downloaded file is too small (${stats.size} bytes). The download may have failed.`
16589
17003
  );
@@ -16604,13 +17018,13 @@ async function updateViaExecutableReplace(deps, version) {
16604
17018
  const tempPath = currentPath + ".update-tmp";
16605
17019
  try {
16606
17020
  await downloadExecutable(deps.fetchFn, version, platformSuffix, tempPath);
16607
- (0, import_fs21.renameSync)(tempPath, currentPath);
17021
+ (0, import_fs22.renameSync)(tempPath, currentPath);
16608
17022
  if (deps.platform !== "win32") {
16609
- (0, import_fs21.chmodSync)(currentPath, 493);
17023
+ (0, import_fs22.chmodSync)(currentPath, 493);
16610
17024
  }
16611
17025
  } catch (error) {
16612
17026
  try {
16613
- (0, import_fs21.unlinkSync)(tempPath);
17027
+ (0, import_fs22.unlinkSync)(tempPath);
16614
17028
  } catch {
16615
17029
  }
16616
17030
  throw error;
@@ -16622,7 +17036,7 @@ function isLocalNpmPackage(scriptPath) {
16622
17036
  }
16623
17037
  function isHomebrewInstall(executablePath) {
16624
17038
  try {
16625
- const realPath = (0, import_fs21.realpathSync)(executablePath);
17039
+ const realPath = (0, import_fs22.realpathSync)(executablePath);
16626
17040
  return realPath.includes("/Cellar/");
16627
17041
  } catch {
16628
17042
  return false;
@@ -16732,20 +17146,20 @@ function findEnvFile() {
16732
17146
  const currentDir = process.cwd();
16733
17147
  const gitService = new GitService();
16734
17148
  const gitRoot = gitService.getGitRepositoryRootSync(currentDir);
16735
- const filesystemRoot = path21.parse(currentDir).root;
17149
+ const filesystemRoot = path24.parse(currentDir).root;
16736
17150
  const stopDir = gitRoot ?? filesystemRoot;
16737
17151
  let searchDir = currentDir;
16738
- let parentDir = path21.dirname(searchDir);
17152
+ let parentDir = path24.dirname(searchDir);
16739
17153
  while (searchDir !== parentDir) {
16740
- const envPath2 = path21.join(searchDir, ".env");
16741
- if (fs19.existsSync(envPath2)) {
17154
+ const envPath2 = path24.join(searchDir, ".env");
17155
+ if (fs23.existsSync(envPath2)) {
16742
17156
  return envPath2;
16743
17157
  }
16744
17158
  if (searchDir === stopDir) {
16745
17159
  return null;
16746
17160
  }
16747
17161
  searchDir = parentDir;
16748
- parentDir = path21.dirname(searchDir);
17162
+ parentDir = path24.dirname(searchDir);
16749
17163
  }
16750
17164
  return null;
16751
17165
  }