@packmind/cli 0.21.0 → 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 +820 -377
  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.0",
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 {
@@ -5035,9 +5042,9 @@ var CHANGE_PROPOSAL_TYPE_LABELS = {
5035
5042
  ["createStandard" /* createStandard */]: "New standard",
5036
5043
  ["createCommand" /* createCommand */]: "New command",
5037
5044
  ["createSkill" /* createSkill */]: "New skill",
5038
- ["removeStandard" /* removeStandard */]: "Remove standard",
5039
- ["removeCommand" /* removeCommand */]: "Remove command",
5040
- ["removeSkill" /* removeSkill */]: "Remove skill"
5045
+ ["removeStandard" /* removeStandard */]: "Removed",
5046
+ ["removeCommand" /* removeCommand */]: "Removed",
5047
+ ["removeSkill" /* removeSkill */]: "Removed"
5041
5048
  };
5042
5049
 
5043
5050
  // packages/types/src/playbookChangeManagement/events/ChangeProposalAcceptedEvent.ts
@@ -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;
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;
@@ -11869,7 +12083,8 @@ var SubmitDiffsUseCase = class {
11869
12083
  artefactId: CREATION_TYPES.has(diff.type) ? null : diff.artifactId,
11870
12084
  payload: diff.payload,
11871
12085
  captureMode: "commit" /* commit */,
11872
- message
12086
+ message,
12087
+ targetId: diff.targetId
11873
12088
  }))
11874
12089
  });
11875
12090
  submitted += response.created;
@@ -11922,7 +12137,8 @@ var CheckDiffsUseCase = class {
11922
12137
  artefactId: diff.artifactId,
11923
12138
  payload: diff.payload,
11924
12139
  captureMode: "commit" /* commit */,
11925
- message: ""
12140
+ message: "",
12141
+ targetId: diff.targetId
11926
12142
  }))
11927
12143
  });
11928
12144
  for (const result of response.results) {
@@ -11954,7 +12170,8 @@ var PackmindCliHexaFactory = class {
11954
12170
  constructor() {
11955
12171
  this.repositories = {
11956
12172
  packmindGateway: new PackmindGateway(loadApiKey()),
11957
- configFileRepository: new ConfigFileRepository()
12173
+ configFileRepository: new ConfigFileRepository(),
12174
+ lockFileRepository: new LockFileRepository()
11958
12175
  };
11959
12176
  this.services = {
11960
12177
  listFiles: new ListFiles(),
@@ -11977,7 +12194,8 @@ var PackmindCliHexaFactory = class {
11977
12194
  this.repositories
11978
12195
  ),
11979
12196
  installPackages: new InstallPackagesUseCase(
11980
- this.repositories.packmindGateway
12197
+ this.repositories.packmindGateway,
12198
+ this.repositories.lockFileRepository
11981
12199
  ),
11982
12200
  installDefaultSkills: new InstallDefaultSkillsUseCase(this.repositories),
11983
12201
  listPackages: new ListPackagesUseCase(this.repositories.packmindGateway),
@@ -12000,7 +12218,8 @@ var PackmindCliHexaFactory = class {
12000
12218
  gateway: this.repositories.packmindGateway
12001
12219
  }),
12002
12220
  diffArtefacts: new DiffArtefactsUseCase(
12003
- this.repositories.packmindGateway
12221
+ this.repositories.packmindGateway,
12222
+ this.repositories.lockFileRepository
12004
12223
  ),
12005
12224
  submitDiffs: new SubmitDiffsUseCase(this.repositories.packmindGateway),
12006
12225
  checkDiffs: new CheckDiffsUseCase(this.repositories.packmindGateway)
@@ -12185,6 +12404,9 @@ var PackmindCliHexa = class {
12185
12404
  async installDefaultSkills(command22) {
12186
12405
  return this.hexa.useCases.installDefaultSkills.execute(command22);
12187
12406
  }
12407
+ async readLockFile(baseDirectory) {
12408
+ return this.hexa.repositories.lockFileRepository.read(baseDirectory);
12409
+ }
12188
12410
  getPackmindGateway() {
12189
12411
  return this.hexa.repositories.packmindGateway;
12190
12412
  }
@@ -12283,8 +12505,55 @@ var HumanReadableLogger = class {
12283
12505
  };
12284
12506
 
12285
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"));
12286
12512
  var pathModule = __toESM(require("path"));
12287
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
+
12288
12557
  // apps/cli/src/infra/commands/lintHandler.ts
12289
12558
  var SEVERITY_LEVELS = {
12290
12559
  ["warning" /* WARNING */]: 0,
@@ -12295,7 +12564,7 @@ function isNotLoggedInError(error) {
12295
12564
  }
12296
12565
  async function lintHandler(args2, deps) {
12297
12566
  const {
12298
- path: path22,
12567
+ path: path25,
12299
12568
  draft,
12300
12569
  rule,
12301
12570
  language,
@@ -12310,20 +12579,52 @@ async function lintHandler(args2, deps) {
12310
12579
  humanReadableLogger,
12311
12580
  ideLintLogger,
12312
12581
  resolvePath,
12313
- exit
12582
+ exit,
12583
+ packmindIgnoreReader = new PackmindIgnoreReader()
12314
12584
  } = deps;
12315
12585
  if (draft && !rule) {
12316
12586
  throw new Error("option --rule is required to use --draft mode");
12317
12587
  }
12318
12588
  const startedAt = Date.now();
12319
- const targetPath = path22 ?? ".";
12589
+ const targetPath = path25 ?? ".";
12320
12590
  const absolutePath = resolvePath(targetPath);
12321
- if (diff) {
12322
- const gitRoot = await packmindCliHexa.tryGetGitRepositoryRoot(absolutePath);
12323
- if (!gitRoot) {
12324
- throw new Error(
12325
- "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`
12326
12625
  );
12626
+ exit(0);
12627
+ return;
12327
12628
  }
12328
12629
  }
12329
12630
  let violations = [];
@@ -12335,14 +12636,14 @@ async function lintHandler(args2, deps) {
12335
12636
  standardSlug: rule?.standardSlug,
12336
12637
  ruleId: rule?.ruleId,
12337
12638
  language,
12338
- diffMode: diff
12639
+ diffMode: diff,
12640
+ ignorePatterns
12339
12641
  });
12340
12642
  violations = result.violations;
12341
12643
  } else {
12342
- const stopDirectory = await packmindCliHexa.tryGetGitRepositoryRoot(absolutePath);
12343
12644
  const hierarchicalConfig = await packmindCliHexa.readHierarchicalConfig(
12344
- absolutePath,
12345
- stopDirectory
12645
+ directoryForOps,
12646
+ gitRoot
12346
12647
  );
12347
12648
  if (!hierarchicalConfig.hasConfigs) {
12348
12649
  throw new Error(
@@ -12351,7 +12652,8 @@ async function lintHandler(args2, deps) {
12351
12652
  }
12352
12653
  const result = await packmindCliHexa.lintFilesFromConfig({
12353
12654
  path: absolutePath,
12354
- diffMode: diff
12655
+ diffMode: diff,
12656
+ ignorePatterns
12355
12657
  });
12356
12658
  violations = result.violations;
12357
12659
  }
@@ -12535,7 +12837,7 @@ var lintCommand = (0, import_cmd_ts.command)({
12535
12837
  packmindCliHexa: new PackmindCliHexa(packmindLogger),
12536
12838
  humanReadableLogger: new HumanReadableLogger(),
12537
12839
  ideLintLogger: new IDELintLogger(),
12538
- resolvePath: (targetPath) => pathModule.isAbsolute(targetPath) ? targetPath : pathModule.resolve(process.cwd(), targetPath),
12840
+ resolvePath: (targetPath) => pathModule2.isAbsolute(targetPath) ? targetPath : pathModule2.resolve(process.cwd(), targetPath),
12539
12841
  exit: (code) => process.exit(code)
12540
12842
  };
12541
12843
  await lintHandler({ ...args2, diff }, deps);
@@ -12543,7 +12845,7 @@ var lintCommand = (0, import_cmd_ts.command)({
12543
12845
  });
12544
12846
 
12545
12847
  // apps/cli/src/wasm-runtime.ts
12546
- var import_fs18 = require("fs");
12848
+ var import_fs19 = require("fs");
12547
12849
  var import_path3 = require("path");
12548
12850
  var import_os = require("os");
12549
12851
 
@@ -12575,20 +12877,20 @@ function hasEmbeddedWasmFiles() {
12575
12877
  return Object.keys(EMBEDDED_WASM_FILES).length > 0;
12576
12878
  }
12577
12879
  function extractWasmFiles() {
12578
- if (wasmExtractedDir && (0, import_fs18.existsSync)(wasmExtractedDir)) {
12880
+ if (wasmExtractedDir && (0, import_fs19.existsSync)(wasmExtractedDir)) {
12579
12881
  return wasmExtractedDir;
12580
12882
  }
12581
12883
  const tempDir = (0, import_path3.join)((0, import_os.tmpdir)(), `packmind-wasm-${process.pid}`);
12582
- if (!(0, import_fs18.existsSync)(tempDir)) {
12583
- (0, import_fs18.mkdirSync)(tempDir, { recursive: true });
12884
+ if (!(0, import_fs19.existsSync)(tempDir)) {
12885
+ (0, import_fs19.mkdirSync)(tempDir, { recursive: true });
12584
12886
  }
12585
12887
  for (const [filename, base64Data] of Object.entries(EMBEDDED_WASM_FILES)) {
12586
12888
  const wasmPath = (0, import_path3.join)(tempDir, filename);
12587
- if ((0, import_fs18.existsSync)(wasmPath)) {
12889
+ if ((0, import_fs19.existsSync)(wasmPath)) {
12588
12890
  continue;
12589
12891
  }
12590
12892
  const wasmBuffer = Buffer.from(base64Data, "base64");
12591
- (0, import_fs18.writeFileSync)(wasmPath, wasmBuffer);
12893
+ (0, import_fs19.writeFileSync)(wasmPath, wasmBuffer);
12592
12894
  }
12593
12895
  wasmExtractedDir = tempDir;
12594
12896
  process.on("exit", () => {
@@ -12598,8 +12900,8 @@ function extractWasmFiles() {
12598
12900
 
12599
12901
  // apps/cli/src/main.ts
12600
12902
  var import_dotenv = require("dotenv");
12601
- var fs19 = __toESM(require("fs"));
12602
- var path21 = __toESM(require("path"));
12903
+ var fs23 = __toESM(require("fs"));
12904
+ var path24 = __toESM(require("path"));
12603
12905
 
12604
12906
  // apps/cli/src/infra/commands/InstallCommand.ts
12605
12907
  var import_cmd_ts2 = __toESM(require_cjs());
@@ -12882,8 +13184,9 @@ async function executeInstallForDirectory(directory, deps) {
12882
13184
  packagesSlugs: configPackages,
12883
13185
  previousPackagesSlugs: configPackages,
12884
13186
  // Pass for consistency
12885
- agents: configAgents
13187
+ agents: configAgents,
12886
13188
  // Pass agents from config if present
13189
+ cliVersion: CLI_VERSION
12887
13190
  });
12888
13191
  const parts = [];
12889
13192
  if (result.recipesCount > 0) parts.push(`${result.recipesCount} commands`);
@@ -13036,8 +13339,9 @@ async function installPackagesHandler(args2, deps) {
13036
13339
  gitRemoteUrl,
13037
13340
  gitBranch,
13038
13341
  relativePath,
13039
- agents: configAgents
13342
+ agents: configAgents,
13040
13343
  // Pass agents from config if present (overrides org-level)
13344
+ cliVersion: CLI_VERSION
13041
13345
  });
13042
13346
  const parts = [];
13043
13347
  if (result.recipesCount > 0) parts.push(`${result.recipesCount} commands`);
@@ -13178,11 +13482,17 @@ async function uninstallPackagesHandler(args2, deps) {
13178
13482
  };
13179
13483
  }
13180
13484
  let configPackages;
13485
+ let configAgents;
13181
13486
  let configFileExists = false;
13182
13487
  try {
13183
13488
  configFileExists = await packmindCliHexa.configExists(cwd);
13184
- const config = await packmindCliHexa.readConfig(cwd);
13185
- 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
+ }
13186
13496
  } catch (err) {
13187
13497
  error("\u274C Failed to read packmind.json");
13188
13498
  if (err instanceof Error) {
@@ -13251,7 +13561,9 @@ async function uninstallPackagesHandler(args2, deps) {
13251
13561
  const result = await packmindCliHexa.installPackages({
13252
13562
  baseDirectory: cwd,
13253
13563
  packagesSlugs: [],
13254
- previousPackagesSlugs: Object.keys(configPackages)
13564
+ previousPackagesSlugs: Object.keys(configPackages),
13565
+ agents: configAgents,
13566
+ cliVersion: CLI_VERSION
13255
13567
  });
13256
13568
  log(`
13257
13569
  removed ${result.filesDeleted} files`);
@@ -13271,7 +13583,9 @@ removed ${result.filesDeleted} files`);
13271
13583
  const result = await packmindCliHexa.installPackages({
13272
13584
  baseDirectory: cwd,
13273
13585
  packagesSlugs: remainingPackages,
13274
- previousPackagesSlugs: Object.keys(configPackages)
13586
+ previousPackagesSlugs: Object.keys(configPackages),
13587
+ agents: configAgents,
13588
+ cliVersion: CLI_VERSION
13275
13589
  });
13276
13590
  if (result.recipesCount > 0 || result.standardsCount > 0) {
13277
13591
  log(
@@ -13674,13 +13988,13 @@ Credentials are loaded from (in order of priority):`);
13674
13988
 
13675
13989
  // apps/cli/src/infra/commands/SetupMcpCommand.ts
13676
13990
  var import_cmd_ts7 = __toESM(require_cjs());
13677
- var fs15 = __toESM(require("fs"));
13991
+ var fs18 = __toESM(require("fs"));
13678
13992
  var readline2 = __toESM(require("readline"));
13679
13993
  var inquirer = __toESM(require("inquirer"));
13680
13994
 
13681
13995
  // apps/cli/src/application/services/AgentDetectionService.ts
13682
- var fs14 = __toESM(require("fs"));
13683
- var path15 = __toESM(require("path"));
13996
+ var fs17 = __toESM(require("fs"));
13997
+ var path17 = __toESM(require("path"));
13684
13998
  var os4 = __toESM(require("os"));
13685
13999
  var import_child_process3 = require("child_process");
13686
14000
  var AgentDetectionService = class {
@@ -13707,16 +14021,16 @@ var AgentDetectionService = class {
13707
14021
  return this.isCommandAvailable("claude");
13708
14022
  }
13709
14023
  isCursorAvailable() {
13710
- const cursorConfigDir = path15.join(os4.homedir(), ".cursor");
13711
- return fs14.existsSync(cursorConfigDir);
14024
+ const cursorConfigDir = path17.join(os4.homedir(), ".cursor");
14025
+ return fs17.existsSync(cursorConfigDir);
13712
14026
  }
13713
14027
  isVSCodeAvailable() {
13714
- const vscodeDir = path15.join(this.projectDir, ".vscode");
13715
- return fs14.existsSync(vscodeDir);
14028
+ const vscodeDir = path17.join(this.projectDir, ".vscode");
14029
+ return fs17.existsSync(vscodeDir);
13716
14030
  }
13717
14031
  isContinueAvailable() {
13718
- const continueDir = path15.join(this.projectDir, ".continue");
13719
- return fs14.existsSync(continueDir);
14032
+ const continueDir = path17.join(this.projectDir, ".continue");
14033
+ return fs17.existsSync(continueDir);
13720
14034
  }
13721
14035
  isCommandAvailable(command22) {
13722
14036
  try {
@@ -13755,8 +14069,8 @@ var ALL_AGENTS2 = [
13755
14069
  { type: "continue", name: "Continue.dev" }
13756
14070
  ];
13757
14071
  async function promptAgentsWithReadline(choices) {
13758
- const input = fs15.createReadStream("/dev/tty");
13759
- const output = fs15.createWriteStream("/dev/tty");
14072
+ const input = fs18.createReadStream("/dev/tty");
14073
+ const output = fs18.createWriteStream("/dev/tty");
13760
14074
  const rl = readline2.createInterface({
13761
14075
  input,
13762
14076
  output
@@ -13770,7 +14084,7 @@ async function promptAgentsWithReadline(choices) {
13770
14084
  output.write("\n");
13771
14085
  const preselected = choices.map((c, i) => c.checked ? i + 1 : null).filter((i) => i !== null);
13772
14086
  const defaultValue = preselected.length > 0 ? preselected.join(",") : "1,2,3";
13773
- return new Promise((resolve9) => {
14087
+ return new Promise((resolve11) => {
13774
14088
  rl.question(
13775
14089
  `Enter numbers separated by commas (default: ${defaultValue}): `,
13776
14090
  (answer) => {
@@ -13781,7 +14095,7 @@ async function promptAgentsWithReadline(choices) {
13781
14095
  const numbersStr = trimmed === "" ? defaultValue : trimmed;
13782
14096
  const numbers = numbersStr.split(",").map((s) => parseInt(s.trim(), 10)).filter((n) => !isNaN(n) && n >= 1 && n <= choices.length);
13783
14097
  const selectedAgents = numbers.map((n) => choices[n - 1].value);
13784
- resolve9(selectedAgents);
14098
+ resolve11(selectedAgents);
13785
14099
  }
13786
14100
  );
13787
14101
  });
@@ -14124,7 +14438,7 @@ var import_cmd_ts15 = __toESM(require_cjs());
14124
14438
  var import_cmd_ts13 = __toESM(require_cjs());
14125
14439
 
14126
14440
  // apps/cli/src/infra/utils/readPlaybookFile.ts
14127
- var fs16 = __toESM(require("fs/promises"));
14441
+ var fs19 = __toESM(require("fs/promises"));
14128
14442
 
14129
14443
  // apps/cli/src/domain/entities/PlaybookDTO.ts
14130
14444
  var import_zod = require("zod");
@@ -14177,7 +14491,7 @@ function parseAndValidatePlaybook(content) {
14177
14491
  }
14178
14492
  async function readPlaybookFile(filePath) {
14179
14493
  try {
14180
- const content = await fs16.readFile(filePath, "utf-8");
14494
+ const content = await fs19.readFile(filePath, "utf-8");
14181
14495
  return parseAndValidatePlaybook(content);
14182
14496
  } catch (e) {
14183
14497
  return {
@@ -14413,7 +14727,7 @@ var import_cmd_ts18 = __toESM(require_cjs());
14413
14727
  var import_cmd_ts16 = __toESM(require_cjs());
14414
14728
 
14415
14729
  // apps/cli/src/infra/utils/readCommandPlaybookFile.ts
14416
- var fs17 = __toESM(require("fs/promises"));
14730
+ var fs20 = __toESM(require("fs/promises"));
14417
14731
 
14418
14732
  // apps/cli/src/domain/entities/CommandPlaybookDTO.ts
14419
14733
  var import_zod2 = require("zod");
@@ -14465,7 +14779,7 @@ function parseAndValidateCommandPlaybook(content) {
14465
14779
  }
14466
14780
  async function readCommandPlaybookFile(filePath) {
14467
14781
  try {
14468
- const content = await fs17.readFile(filePath, "utf-8");
14782
+ const content = await fs20.readFile(filePath, "utf-8");
14469
14783
  return parseAndValidateCommandPlaybook(content);
14470
14784
  } catch (e) {
14471
14785
  return {
@@ -14690,9 +15004,13 @@ var commandsCommand = (0, import_cmd_ts18.subcommands)({
14690
15004
  });
14691
15005
 
14692
15006
  // apps/cli/src/infra/commands/DiffCommand.ts
14693
- var import_fs20 = require("fs");
15007
+ var import_fs21 = require("fs");
14694
15008
  var import_cmd_ts19 = __toESM(require_cjs());
14695
15009
 
15010
+ // apps/cli/src/infra/commands/diffArtefactsHandler.ts
15011
+ var nodePath = __toESM(require("path"));
15012
+ var fs21 = __toESM(require("fs/promises"));
15013
+
14696
15014
  // apps/cli/src/infra/utils/diffFormatter.ts
14697
15015
  var import_diff4 = require("diff");
14698
15016
  init_source();
@@ -14720,7 +15038,7 @@ function formatContentDiff(oldContent, newContent) {
14720
15038
 
14721
15039
  // apps/cli/src/infra/utils/editorMessage.ts
14722
15040
  var import_child_process4 = require("child_process");
14723
- var import_fs19 = require("fs");
15041
+ var import_fs20 = require("fs");
14724
15042
  var import_os2 = require("os");
14725
15043
  var import_path4 = require("path");
14726
15044
  var MAX_MESSAGE_LENGTH = 1024;
@@ -14732,7 +15050,7 @@ var EDITOR_TEMPLATE = `
14732
15050
  function openEditorForMessage() {
14733
15051
  const tmpFile = (0, import_path4.join)((0, import_os2.tmpdir)(), `packmind-msg-${Date.now()}.txt`);
14734
15052
  try {
14735
- (0, import_fs19.writeFileSync)(tmpFile, EDITOR_TEMPLATE, "utf-8");
15053
+ (0, import_fs20.writeFileSync)(tmpFile, EDITOR_TEMPLATE, "utf-8");
14736
15054
  const editor = process.env.EDITOR || process.env.VISUAL || "vi";
14737
15055
  const result = (0, import_child_process4.spawnSync)(editor, [tmpFile], {
14738
15056
  stdio: "inherit"
@@ -14740,11 +15058,11 @@ function openEditorForMessage() {
14740
15058
  if (result.status !== 0) {
14741
15059
  throw new Error(`Editor exited with status ${result.status}`);
14742
15060
  }
14743
- const content = (0, import_fs19.readFileSync)(tmpFile, "utf-8");
15061
+ const content = (0, import_fs20.readFileSync)(tmpFile, "utf-8");
14744
15062
  return content.split("\n").filter((line) => !line.startsWith("#")).join("\n").trim();
14745
15063
  } finally {
14746
15064
  try {
14747
- (0, import_fs19.unlinkSync)(tmpFile);
15065
+ (0, import_fs20.unlinkSync)(tmpFile);
14748
15066
  } catch {
14749
15067
  }
14750
15068
  }
@@ -14864,72 +15182,49 @@ function buildSubmittedFooter(submittedDiffs) {
14864
15182
  const proposalWord = proposalCount === 1 ? "change proposal" : "change proposals";
14865
15183
  return `${proposalCount} ${proposalWord} ignored, run \`packmind-cli diff --include-submitted\` to see what's waiting for validation`;
14866
15184
  }
14867
- async function readConfigAndPackages(deps) {
14868
- const { packmindCliHexa, exit, getCwd, log, error } = deps;
14869
- const cwd = getCwd();
14870
- let configPackages;
14871
- let configAgents;
14872
- try {
14873
- const fullConfig = await packmindCliHexa.readFullConfig(cwd);
14874
- if (fullConfig) {
14875
- configPackages = Object.keys(fullConfig.packages);
14876
- configAgents = fullConfig.agents;
14877
- } else {
14878
- configPackages = [];
14879
- }
14880
- } catch (err) {
14881
- error("ERROR Failed to parse packmind.json");
14882
- if (err instanceof Error) {
14883
- error(`ERROR ${err.message}`);
14884
- } else {
14885
- error(`ERROR ${String(err)}`);
14886
- }
14887
- error("\n\u{1F4A1} Please fix the packmind.json file or delete it to continue.");
14888
- exit(1);
14889
- 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);
14890
15190
  }
14891
- if (configPackages.length === 0) {
14892
- log("Usage: packmind-cli diff");
14893
- log("");
14894
- log("Compare local command files against the server.");
14895
- log("Configure packages in packmind.json first.");
14896
- exit(0);
14897
- 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
+ }
14898
15196
  }
14899
- 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}/` : "/";
14900
15203
  }
14901
15204
  async function collectGitInfo(deps) {
14902
- const { packmindCliHexa, exit, getCwd, error } = deps;
15205
+ const { packmindCliHexa, exit, getCwd } = deps;
14903
15206
  const cwd = getCwd();
14904
15207
  let gitRemoteUrl;
14905
15208
  let gitBranch;
14906
- let relativePath;
14907
15209
  const gitRoot = await packmindCliHexa.tryGetGitRepositoryRoot(cwd);
14908
15210
  if (gitRoot) {
14909
15211
  try {
14910
15212
  gitRemoteUrl = packmindCliHexa.getGitRemoteUrlFromPath(gitRoot);
14911
15213
  gitBranch = packmindCliHexa.getCurrentBranch(gitRoot);
14912
- relativePath = cwd.startsWith(gitRoot) ? cwd.slice(gitRoot.length) : "/";
14913
- if (!relativePath.startsWith("/")) {
14914
- relativePath = "/" + relativePath;
14915
- }
14916
- if (!relativePath.endsWith("/")) {
14917
- relativePath = relativePath + "/";
14918
- }
14919
15214
  } catch (err) {
14920
15215
  logWarningConsole(
14921
15216
  `Failed to collect git info: ${err instanceof Error ? err.message : String(err)}`
14922
15217
  );
14923
15218
  }
14924
15219
  }
14925
- if (!gitRemoteUrl || !gitBranch || !relativePath) {
14926
- error(
14927
- "\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."
14928
15223
  );
14929
15224
  exit(1);
14930
15225
  return null;
14931
15226
  }
14932
- return { gitRemoteUrl, gitBranch, relativePath };
15227
+ return { gitRemoteUrl, gitBranch, gitRoot };
14933
15228
  }
14934
15229
  async function handleSubmission(params) {
14935
15230
  const { packmindCliHexa, unsubmittedItems, messageFlag, exit } = params;
@@ -15028,6 +15323,7 @@ function extractUniqueAndSortedArtefacts(groups) {
15028
15323
  function displayDiffs(params) {
15029
15324
  const {
15030
15325
  diffsToDisplay,
15326
+ displayPathMap,
15031
15327
  submittedLookup,
15032
15328
  includeSubmitted,
15033
15329
  unsubmittedItems,
@@ -15045,7 +15341,7 @@ Changes found:
15045
15341
  const subGroups = subGroupByChangeContent(groupDiffs);
15046
15342
  for (const subGroup of subGroups) {
15047
15343
  for (const diff of subGroup) {
15048
- log(` ${formatFilePath(diff.filePath)}`);
15344
+ log(` ${formatFilePath(displayPathMap.get(diff) ?? diff.filePath)}`);
15049
15345
  }
15050
15346
  const label = CHANGE_PROPOSAL_TYPE_LABELS[subGroup[0].type] ?? "content changed";
15051
15347
  const checkItem = submittedLookup.get(subGroup[0]);
@@ -15090,37 +15386,96 @@ async function diffArtefactsHandler(deps) {
15090
15386
  exit,
15091
15387
  getCwd,
15092
15388
  log,
15093
- error,
15094
15389
  submit,
15095
15390
  includeSubmitted,
15096
15391
  message: messageFlag
15097
15392
  } = deps;
15098
15393
  const cwd = getCwd();
15099
- const config = await readConfigAndPackages(deps);
15100
- 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
+ }
15101
15428
  return { diffsFound: 0 };
15102
15429
  }
15103
- const { configPackages, configAgents } = config;
15104
15430
  try {
15105
15431
  const gitInfo = await collectGitInfo(deps);
15106
15432
  if (!gitInfo) {
15107
15433
  return { diffsFound: 0 };
15108
15434
  }
15109
- const { gitRemoteUrl, gitBranch, relativePath } = gitInfo;
15110
- const packageCount = configPackages.length;
15111
- const packageWord = packageCount === 1 ? "package" : "packages";
15112
- logInfoConsole(
15113
- `Comparing ${packageCount} ${packageWord}: ${configPackages.join(", ")}...`
15114
- );
15115
- const diffs = await packmindCliHexa.diffArtefacts({
15116
- baseDirectory: cwd,
15117
- packagesSlugs: configPackages,
15118
- gitRemoteUrl,
15119
- gitBranch,
15120
- relativePath,
15121
- agents: configAgents
15122
- });
15123
- 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) {
15124
15479
  log("No changes found.");
15125
15480
  if (submit) {
15126
15481
  logInfoConsole("No changes to submit.");
@@ -15128,11 +15483,11 @@ async function diffArtefactsHandler(deps) {
15128
15483
  exit(0);
15129
15484
  return { diffsFound: 0 };
15130
15485
  }
15131
- const allGroupedDiffs = Array.from(groupDiffsByArtefact(diffs).values());
15486
+ const allGroupedDiffs = Array.from(groupDiffsByArtefact(allDiffs).values());
15132
15487
  const checkResult = await packmindCliHexa.checkDiffs(allGroupedDiffs);
15133
15488
  const submittedItems = checkResult.results.filter((r) => r.exists);
15134
15489
  const unsubmittedItems = checkResult.results.filter((r) => !r.exists);
15135
- const diffsToDisplay = includeSubmitted ? diffs : unsubmittedItems.map((r) => r.diff);
15490
+ const diffsToDisplay = includeSubmitted ? allDiffs : unsubmittedItems.map((r) => r.diff);
15136
15491
  const submittedLookup = /* @__PURE__ */ new Map();
15137
15492
  for (const item of checkResult.results) {
15138
15493
  submittedLookup.set(item.diff, item);
@@ -15150,6 +15505,7 @@ async function diffArtefactsHandler(deps) {
15150
15505
  }
15151
15506
  const changeCount = displayDiffs({
15152
15507
  diffsToDisplay,
15508
+ displayPathMap,
15153
15509
  submittedLookup,
15154
15510
  includeSubmitted,
15155
15511
  unsubmittedItems,
@@ -15170,11 +15526,10 @@ async function diffArtefactsHandler(deps) {
15170
15526
  exit(0);
15171
15527
  return { diffsFound: changeCount };
15172
15528
  } catch (err) {
15173
- error("\n\u274C Failed to diff:");
15174
15529
  if (err instanceof Error) {
15175
- error(` ${err.message}`);
15530
+ logErrorConsole(err.message);
15176
15531
  } else {
15177
- error(` ${String(err)}`);
15532
+ logErrorConsole(String(err));
15178
15533
  }
15179
15534
  exit(1);
15180
15535
  return { diffsFound: 0 };
@@ -15182,40 +15537,10 @@ async function diffArtefactsHandler(deps) {
15182
15537
  }
15183
15538
 
15184
15539
  // apps/cli/src/infra/commands/diffAddHandler.ts
15185
- var path17 = __toESM(require("path"));
15186
-
15187
- // apps/cli/src/application/utils/resolveArtefactFromPath.ts
15188
- function resolveArtefactFromPath(filePath) {
15189
- const normalized = normalizePath(filePath);
15190
- for (const [agent, paths] of Object.entries(CODING_AGENT_ARTEFACT_PATHS)) {
15191
- if (paths.command && normalized.includes(paths.command)) {
15192
- return {
15193
- artifactType: "command",
15194
- codingAgent: agent
15195
- };
15196
- }
15197
- }
15198
- for (const [agent, paths] of Object.entries(CODING_AGENT_ARTEFACT_PATHS)) {
15199
- if (paths.standard && normalized.includes(paths.standard)) {
15200
- return {
15201
- artifactType: "standard",
15202
- codingAgent: agent
15203
- };
15204
- }
15205
- }
15206
- for (const [agent, paths] of Object.entries(CODING_AGENT_ARTEFACT_PATHS)) {
15207
- if (paths.skill && normalized.includes(paths.skill)) {
15208
- return {
15209
- artifactType: "skill",
15210
- codingAgent: agent
15211
- };
15212
- }
15213
- }
15214
- return null;
15215
- }
15540
+ var path20 = __toESM(require("path"));
15216
15541
 
15217
15542
  // apps/cli/src/application/utils/parseCommandFile.ts
15218
- var path16 = __toESM(require("path"));
15543
+ var path18 = __toESM(require("path"));
15219
15544
  var FRONTMATTER_DELIMITER3 = "---";
15220
15545
  function parseCommandFile(content, filePath) {
15221
15546
  content = normalizeLineEndings(content);
@@ -15267,7 +15592,7 @@ function stripYamlQuotes2(value) {
15267
15592
  return value;
15268
15593
  }
15269
15594
  function extractFilenameSlug(filePath) {
15270
- let basename2 = path16.basename(filePath);
15595
+ let basename2 = path18.basename(filePath);
15271
15596
  if (basename2.endsWith(".prompt.md")) {
15272
15597
  basename2 = basename2.slice(0, -".prompt.md".length);
15273
15598
  } else if (basename2.endsWith(".md")) {
@@ -15348,6 +15673,23 @@ function parseSkillDirectory(files) {
15348
15673
  return { success: true, payload };
15349
15674
  }
15350
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
+
15351
15693
  // apps/cli/src/infra/commands/diffAddHandler.ts
15352
15694
  async function diffAddHandler(deps) {
15353
15695
  const {
@@ -15356,7 +15698,7 @@ async function diffAddHandler(deps) {
15356
15698
  message: messageFlag,
15357
15699
  exit,
15358
15700
  getCwd,
15359
- readFile: readFile10,
15701
+ readFile: readFile12,
15360
15702
  readSkillDirectory: readSkillDirectory2
15361
15703
  } = deps;
15362
15704
  if (!filePath) {
@@ -15364,7 +15706,7 @@ async function diffAddHandler(deps) {
15364
15706
  exit(1);
15365
15707
  return;
15366
15708
  }
15367
- const absolutePath = path17.resolve(getCwd(), filePath);
15709
+ const absolutePath = path20.resolve(getCwd(), filePath);
15368
15710
  const artefactResult = resolveArtefactFromPath(absolutePath);
15369
15711
  if (!artefactResult) {
15370
15712
  logErrorConsole(
@@ -15375,7 +15717,7 @@ async function diffAddHandler(deps) {
15375
15717
  }
15376
15718
  let diffResult;
15377
15719
  if (artefactResult.artifactType === "skill") {
15378
- const dirPath = absolutePath.endsWith("SKILL.md") ? path17.dirname(absolutePath) : absolutePath;
15720
+ const dirPath = absolutePath.endsWith("SKILL.md") ? path20.dirname(absolutePath) : absolutePath;
15379
15721
  let files;
15380
15722
  try {
15381
15723
  files = await readSkillDirectory2(dirPath);
@@ -15403,7 +15745,7 @@ async function diffAddHandler(deps) {
15403
15745
  } else {
15404
15746
  let content;
15405
15747
  try {
15406
- content = readFile10(absolutePath);
15748
+ content = readFile12(absolutePath);
15407
15749
  } catch (err) {
15408
15750
  if (isErrnoException(err) && err.code === "EISDIR") {
15409
15751
  logErrorConsole(
@@ -15457,10 +15799,41 @@ async function diffAddHandler(deps) {
15457
15799
  return;
15458
15800
  }
15459
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
+ }
15811
+ let targetId;
15812
+ try {
15813
+ const fullConfig = await packmindCliHexa.readFullConfig(targetDir);
15814
+ const configPackages = fullConfig ? Object.keys(fullConfig.packages) : [];
15815
+ const gitRoot = await packmindCliHexa.tryGetGitRepositoryRoot(targetDir);
15816
+ if (gitRoot && configPackages.length > 0) {
15817
+ const gitRemoteUrl = packmindCliHexa.getGitRemoteUrlFromPath(gitRoot);
15818
+ const gitBranch = packmindCliHexa.getCurrentBranch(gitRoot);
15819
+ const rel = path20.relative(gitRoot, targetDir);
15820
+ const relativePath = rel.startsWith("..") ? "/" : rel ? `/${rel}/` : "/";
15821
+ const deployedContent = await packmindCliHexa.getPackmindGateway().deployment.getDeployed({
15822
+ packagesSlugs: configPackages,
15823
+ gitRemoteUrl,
15824
+ gitBranch,
15825
+ relativePath,
15826
+ agents: fullConfig?.agents
15827
+ });
15828
+ targetId = deployedContent.targetId;
15829
+ }
15830
+ } catch {
15831
+ }
15460
15832
  const diff = {
15461
15833
  ...diffResult.diff,
15462
15834
  filePath: absolutePath,
15463
- spaceId: space.id
15835
+ spaceId: space.id,
15836
+ targetId
15464
15837
  };
15465
15838
  const result = await packmindCliHexa.submitDiffs([[diff]], message);
15466
15839
  for (const err of result.errors) {
@@ -15562,12 +15935,46 @@ function isErrnoException(err) {
15562
15935
  }
15563
15936
 
15564
15937
  // apps/cli/src/infra/commands/diffRemoveHandler.ts
15565
- var path18 = __toESM(require("path"));
15938
+ var path21 = __toESM(require("path"));
15566
15939
  var ARTIFACT_TYPE_LABELS2 = {
15567
15940
  command: "command",
15568
15941
  standard: "standard",
15569
15942
  skill: "skill"
15570
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
+ }
15571
15978
  async function diffRemoveHandler(deps) {
15572
15979
  const {
15573
15980
  packmindCliHexa,
@@ -15587,7 +15994,7 @@ async function diffRemoveHandler(deps) {
15587
15994
  return;
15588
15995
  }
15589
15996
  const cwd = getCwd();
15590
- const absolutePath = path18.resolve(cwd, filePath);
15997
+ const absolutePath = path21.resolve(cwd, filePath);
15591
15998
  const artefactResult = resolveArtefactFromPath(absolutePath);
15592
15999
  if (!artefactResult) {
15593
16000
  logErrorConsole(
@@ -15596,18 +16003,27 @@ async function diffRemoveHandler(deps) {
15596
16003
  exit(1);
15597
16004
  return;
15598
16005
  }
15599
- const matchPath = artefactResult.artifactType === "skill" ? absolutePath.endsWith("SKILL.md") ? absolutePath : path18.join(absolutePath, "SKILL.md") : absolutePath;
15600
- 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;
15601
16008
  const existsCheckPath = skillDirPath ?? absolutePath;
15602
16009
  if (!existsSync25(existsCheckPath)) {
15603
16010
  logErrorConsole(`File or directory does not exist: ${filePath}`);
15604
16011
  exit(1);
15605
16012
  return;
15606
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
+ }
15607
16023
  let configPackages;
15608
16024
  let configAgents;
15609
16025
  try {
15610
- const fullConfig = await packmindCliHexa.readFullConfig(cwd);
16026
+ const fullConfig = await packmindCliHexa.readFullConfig(targetDir);
15611
16027
  if (fullConfig) {
15612
16028
  configPackages = Object.keys(fullConfig.packages);
15613
16029
  configAgents = fullConfig.agents;
@@ -15634,68 +16050,87 @@ async function diffRemoveHandler(deps) {
15634
16050
  exit(1);
15635
16051
  return;
15636
16052
  }
15637
- let gitRemoteUrl;
15638
- let gitBranch;
15639
- let relativePath;
15640
- const gitRoot = await packmindCliHexa.tryGetGitRepositoryRoot(cwd);
15641
- 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;
15642
16075
  try {
15643
16076
  gitRemoteUrl = packmindCliHexa.getGitRemoteUrlFromPath(gitRoot);
15644
16077
  gitBranch = packmindCliHexa.getCurrentBranch(gitRoot);
15645
- relativePath = cwd.startsWith(gitRoot) ? cwd.slice(gitRoot.length) : "/";
15646
- if (!relativePath.startsWith("/")) {
15647
- relativePath = "/" + relativePath;
15648
- }
15649
- if (!relativePath.endsWith("/")) {
15650
- relativePath = relativePath + "/";
15651
- }
16078
+ const rel = path21.relative(gitRoot, targetDir);
16079
+ relativePath = rel.startsWith("..") ? "/" : rel ? `/${rel}/` : "/";
15652
16080
  } catch (err) {
15653
16081
  logErrorConsole(
15654
16082
  `Failed to collect git info: ${err instanceof Error ? err.message : String(err)}`
15655
16083
  );
15656
16084
  }
15657
- }
15658
- if (!gitRemoteUrl || !gitBranch || !relativePath) {
15659
- logErrorConsole(
15660
- "\n\u274C Could not determine git repository info. The diff command requires a git repository with a remote configured."
15661
- );
15662
- exit(1);
15663
- return;
15664
- }
15665
- const deployedContent = await packmindCliHexa.getPackmindGateway().deployment.getDeployed({
15666
- packagesSlugs: configPackages,
15667
- gitRemoteUrl,
15668
- gitBranch,
15669
- relativePath,
15670
- agents: configAgents
15671
- });
15672
- const relativeToGitRoot = matchPath.startsWith(gitRoot) ? matchPath.slice(gitRoot.length) : matchPath;
15673
- let normalizedFilePath = normalizePath(relativeToGitRoot);
15674
- if (normalizedFilePath.startsWith("/")) {
15675
- normalizedFilePath = normalizedFilePath.slice(1);
15676
- }
15677
- const deployedFile = deployedContent.fileUpdates.createOrUpdate.find(
15678
- (file) => normalizePath(file.path) === normalizedFilePath
15679
- );
15680
- if (!deployedFile) {
15681
- const artifactTypeLabel = ARTIFACT_TYPE_LABELS2[artefactResult.artifactType];
15682
- logErrorConsole(`This ${artifactTypeLabel} does not come from Packmind`);
15683
- exit(1);
15684
- return;
15685
- }
15686
- if (!deployedFile.artifactId || !deployedFile.spaceId) {
15687
- logErrorConsole(
15688
- "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
15689
16103
  );
15690
- exit(1);
15691
- return;
15692
- }
15693
- if (!deployedContent.targetId || !deployedContent.packageIds) {
15694
- logErrorConsole(
15695
- "Missing target or package information. Cannot create change proposal for removal."
16104
+ const deployedFile = deployedContent.fileUpdates.createOrUpdate.find(
16105
+ (file) => normalizePath(file.path) === filePathForComparison
15696
16106
  );
15697
- exit(1);
15698
- 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
+ };
15699
16134
  }
15700
16135
  let message;
15701
16136
  if (messageFlag !== void 0) {
@@ -15729,13 +16164,13 @@ async function diffRemoveHandler(deps) {
15729
16164
  filePath: absolutePath,
15730
16165
  type: changeProposalType,
15731
16166
  payload: {
15732
- targetId: deployedContent.targetId,
15733
- packageIds: deployedContent.packageIds
16167
+ packageIds: metadata.packageIds.map(createPackageId)
15734
16168
  },
15735
- artifactName: deployedFile.artifactName || artefactResult.artifactType,
16169
+ artifactName: metadata.artifactName,
15736
16170
  artifactType: artefactResult.artifactType,
15737
- artifactId: deployedFile.artifactId,
15738
- spaceId: deployedFile.spaceId
16171
+ artifactId: metadata.artifactId,
16172
+ spaceId: metadata.spaceId,
16173
+ targetId: createTargetId(metadata.targetId)
15739
16174
  };
15740
16175
  const result = await packmindCliHexa.submitDiffs([[diff]], message);
15741
16176
  for (const err of result.errors) {
@@ -15787,37 +16222,45 @@ var diffCommand = (0, import_cmd_ts19.command)({
15787
16222
  description: "Message describing the intent behind the changes (max 1024 chars)",
15788
16223
  type: (0, import_cmd_ts19.optional)(import_cmd_ts19.string)
15789
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
+ }),
15790
16231
  positionals: (0, import_cmd_ts19.restPositionals)({
15791
16232
  type: import_cmd_ts19.string,
15792
16233
  displayName: "args",
15793
16234
  description: "Subcommand and arguments (e.g., add <path>, remove <path>)"
15794
16235
  })
15795
16236
  },
15796
- handler: async ({ submit, includeSubmitted, message, positionals }) => {
16237
+ handler: async ({ submit, includeSubmitted, message, path: path25, positionals }) => {
15797
16238
  const packmindLogger = new PackmindLogger("PackmindCLI", "info" /* INFO */);
15798
16239
  const packmindCliHexa = new PackmindCliHexa(packmindLogger);
15799
16240
  if (positionals[0] === "add") {
16241
+ const addFilePath = path25 && positionals[1] ? `${path25}/${positionals[1]}`.replace(/\/+/g, "/") : positionals[1];
15800
16242
  await diffAddHandler({
15801
16243
  packmindCliHexa,
15802
- filePath: positionals[1],
16244
+ filePath: addFilePath,
15803
16245
  message,
15804
16246
  exit: process.exit,
15805
16247
  getCwd: () => process.cwd(),
15806
- readFile: (p) => (0, import_fs20.readFileSync)(p, "utf-8"),
16248
+ readFile: (p) => (0, import_fs21.readFileSync)(p, "utf-8"),
15807
16249
  readSkillDirectory
15808
16250
  });
15809
16251
  return;
15810
16252
  }
15811
16253
  if (positionals[0] === "remove" || positionals[0] === "rm") {
16254
+ const removeFilePath = path25 && positionals[1] ? `${path25}/${positionals[1]}`.replace(/\/+/g, "/") : positionals[1];
15812
16255
  await diffRemoveHandler({
15813
16256
  packmindCliHexa,
15814
- filePath: positionals[1],
16257
+ filePath: removeFilePath,
15815
16258
  message,
15816
16259
  exit: process.exit,
15817
16260
  getCwd: () => process.cwd(),
15818
- existsSync: (p) => (0, import_fs20.existsSync)(p),
15819
- unlinkSync: (p) => (0, import_fs20.unlinkSync)(p),
15820
- 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)
15821
16264
  });
15822
16265
  return;
15823
16266
  }
@@ -15826,10 +16269,10 @@ var diffCommand = (0, import_cmd_ts19.command)({
15826
16269
  exit: process.exit,
15827
16270
  getCwd: () => process.cwd(),
15828
16271
  log: console.log,
15829
- error: console.error,
15830
16272
  submit,
15831
16273
  includeSubmitted,
15832
- message
16274
+ message,
16275
+ path: path25
15833
16276
  });
15834
16277
  }
15835
16278
  });
@@ -16207,8 +16650,8 @@ var import_cmd_ts25 = __toESM(require_cjs());
16207
16650
  var import_cmd_ts24 = __toESM(require_cjs());
16208
16651
 
16209
16652
  // apps/cli/src/application/services/AgentArtifactDetectionService.ts
16210
- var fs18 = __toESM(require("fs/promises"));
16211
- var path19 = __toESM(require("path"));
16653
+ var fs22 = __toESM(require("fs/promises"));
16654
+ var path22 = __toESM(require("path"));
16212
16655
  var AGENT_ARTIFACT_CHECKS = [
16213
16656
  { agent: "claude", paths: [".claude"] },
16214
16657
  { agent: "cursor", paths: [".cursor"] },
@@ -16226,7 +16669,7 @@ var AgentArtifactDetectionService = class {
16226
16669
  const detected = [];
16227
16670
  for (const check of AGENT_ARTIFACT_CHECKS) {
16228
16671
  for (const relativePath of check.paths) {
16229
- const fullPath = path19.join(baseDirectory, relativePath);
16672
+ const fullPath = path22.join(baseDirectory, relativePath);
16230
16673
  const exists = await this.pathExists(fullPath);
16231
16674
  if (exists) {
16232
16675
  detected.push({
@@ -16241,7 +16684,7 @@ var AgentArtifactDetectionService = class {
16241
16684
  }
16242
16685
  async pathExists(filePath) {
16243
16686
  try {
16244
- await fs18.access(filePath);
16687
+ await fs22.access(filePath);
16245
16688
  return true;
16246
16689
  } catch {
16247
16690
  return false;
@@ -16323,7 +16766,7 @@ async function promptAgentsWithReadline2(choices) {
16323
16766
  output.write("\n");
16324
16767
  const preselected = choices.map((c, i) => c.checked ? i + 1 : null).filter((i) => i !== null);
16325
16768
  const defaultValue = preselected.length > 0 ? preselected.join(",") : "1,2,3";
16326
- return new Promise((resolve9) => {
16769
+ return new Promise((resolve11) => {
16327
16770
  rl.question(
16328
16771
  `Enter numbers separated by commas (default: ${defaultValue}): `,
16329
16772
  (answer) => {
@@ -16332,7 +16775,7 @@ async function promptAgentsWithReadline2(choices) {
16332
16775
  const numbersStr = trimmed === "" ? defaultValue : trimmed;
16333
16776
  const numbers = numbersStr.split(",").map((s) => parseInt(s.trim(), 10)).filter((n) => !isNaN(n) && n >= 1 && n <= choices.length);
16334
16777
  const selectedAgents = numbers.map((n) => choices[n - 1].value);
16335
- resolve9(selectedAgents);
16778
+ resolve11(selectedAgents);
16336
16779
  }
16337
16780
  );
16338
16781
  });
@@ -16483,7 +16926,7 @@ var import_cmd_ts27 = __toESM(require_cjs());
16483
16926
  // apps/cli/src/infra/commands/updateHandler.ts
16484
16927
  var import_path5 = __toESM(require("path"));
16485
16928
  var import_child_process5 = require("child_process");
16486
- var import_fs21 = require("fs");
16929
+ var import_fs22 = require("fs");
16487
16930
  var import_promises2 = require("stream/promises");
16488
16931
  var import_stream = require("stream");
16489
16932
  var import_semver = __toESM(require("semver"));
@@ -16550,11 +16993,11 @@ URL: ${url}`
16550
16993
  throw new Error("No response body received");
16551
16994
  }
16552
16995
  const nodeReadable = import_stream.Readable.fromWeb(res.body);
16553
- const fileStream = (0, import_fs21.createWriteStream)(targetPath);
16996
+ const fileStream = (0, import_fs22.createWriteStream)(targetPath);
16554
16997
  await (0, import_promises2.pipeline)(nodeReadable, fileStream);
16555
- const stats = (0, import_fs21.statSync)(targetPath);
16998
+ const stats = (0, import_fs22.statSync)(targetPath);
16556
16999
  if (stats.size < 1e6) {
16557
- (0, import_fs21.unlinkSync)(targetPath);
17000
+ (0, import_fs22.unlinkSync)(targetPath);
16558
17001
  throw new Error(
16559
17002
  `Downloaded file is too small (${stats.size} bytes). The download may have failed.`
16560
17003
  );
@@ -16575,13 +17018,13 @@ async function updateViaExecutableReplace(deps, version) {
16575
17018
  const tempPath = currentPath + ".update-tmp";
16576
17019
  try {
16577
17020
  await downloadExecutable(deps.fetchFn, version, platformSuffix, tempPath);
16578
- (0, import_fs21.renameSync)(tempPath, currentPath);
17021
+ (0, import_fs22.renameSync)(tempPath, currentPath);
16579
17022
  if (deps.platform !== "win32") {
16580
- (0, import_fs21.chmodSync)(currentPath, 493);
17023
+ (0, import_fs22.chmodSync)(currentPath, 493);
16581
17024
  }
16582
17025
  } catch (error) {
16583
17026
  try {
16584
- (0, import_fs21.unlinkSync)(tempPath);
17027
+ (0, import_fs22.unlinkSync)(tempPath);
16585
17028
  } catch {
16586
17029
  }
16587
17030
  throw error;
@@ -16593,7 +17036,7 @@ function isLocalNpmPackage(scriptPath) {
16593
17036
  }
16594
17037
  function isHomebrewInstall(executablePath) {
16595
17038
  try {
16596
- const realPath = (0, import_fs21.realpathSync)(executablePath);
17039
+ const realPath = (0, import_fs22.realpathSync)(executablePath);
16597
17040
  return realPath.includes("/Cellar/");
16598
17041
  } catch {
16599
17042
  return false;
@@ -16703,20 +17146,20 @@ function findEnvFile() {
16703
17146
  const currentDir = process.cwd();
16704
17147
  const gitService = new GitService();
16705
17148
  const gitRoot = gitService.getGitRepositoryRootSync(currentDir);
16706
- const filesystemRoot = path21.parse(currentDir).root;
17149
+ const filesystemRoot = path24.parse(currentDir).root;
16707
17150
  const stopDir = gitRoot ?? filesystemRoot;
16708
17151
  let searchDir = currentDir;
16709
- let parentDir = path21.dirname(searchDir);
17152
+ let parentDir = path24.dirname(searchDir);
16710
17153
  while (searchDir !== parentDir) {
16711
- const envPath2 = path21.join(searchDir, ".env");
16712
- if (fs19.existsSync(envPath2)) {
17154
+ const envPath2 = path24.join(searchDir, ".env");
17155
+ if (fs23.existsSync(envPath2)) {
16713
17156
  return envPath2;
16714
17157
  }
16715
17158
  if (searchDir === stopDir) {
16716
17159
  return null;
16717
17160
  }
16718
17161
  searchDir = parentDir;
16719
- parentDir = path21.dirname(searchDir);
17162
+ parentDir = path24.dirname(searchDir);
16720
17163
  }
16721
17164
  return null;
16722
17165
  }