@packmind/cli 0.17.0 → 0.19.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 +643 -220
  2. package/package.json +1 -1
package/main.cjs CHANGED
@@ -371,32 +371,32 @@ var init_supports_color = __esm({
371
371
  });
372
372
 
373
373
  // node_modules/chalk/source/utilities.js
374
- function stringReplaceAll(string11, substring, replacer) {
375
- let index = string11.indexOf(substring);
374
+ function stringReplaceAll(string12, substring, replacer) {
375
+ let index = string12.indexOf(substring);
376
376
  if (index === -1) {
377
- return string11;
377
+ return string12;
378
378
  }
379
379
  const substringLength = substring.length;
380
380
  let endIndex = 0;
381
381
  let returnValue = "";
382
382
  do {
383
- returnValue += string11.slice(endIndex, index) + substring + replacer;
383
+ returnValue += string12.slice(endIndex, index) + substring + replacer;
384
384
  endIndex = index + substringLength;
385
- index = string11.indexOf(substring, endIndex);
385
+ index = string12.indexOf(substring, endIndex);
386
386
  } while (index !== -1);
387
- returnValue += string11.slice(endIndex);
387
+ returnValue += string12.slice(endIndex);
388
388
  return returnValue;
389
389
  }
390
- function stringEncaseCRLFWithFirstIndex(string11, prefix, postfix, index) {
390
+ function stringEncaseCRLFWithFirstIndex(string12, prefix, postfix, index) {
391
391
  let endIndex = 0;
392
392
  let returnValue = "";
393
393
  do {
394
- const gotCR = string11[index - 1] === "\r";
395
- returnValue += string11.slice(endIndex, gotCR ? index - 1 : index) + prefix + (gotCR ? "\r\n" : "\n") + postfix;
394
+ const gotCR = string12[index - 1] === "\r";
395
+ returnValue += string12.slice(endIndex, gotCR ? index - 1 : index) + prefix + (gotCR ? "\r\n" : "\n") + postfix;
396
396
  endIndex = index + 1;
397
- index = string11.indexOf("\n", endIndex);
397
+ index = string12.indexOf("\n", endIndex);
398
398
  } while (index !== -1);
399
- returnValue += string11.slice(endIndex);
399
+ returnValue += string12.slice(endIndex);
400
400
  return returnValue;
401
401
  }
402
402
  var init_utilities = __esm({
@@ -555,26 +555,26 @@ var init_source = __esm({
555
555
  builder[IS_EMPTY] = _isEmpty;
556
556
  return builder;
557
557
  };
558
- applyStyle = (self, string11) => {
559
- if (self.level <= 0 || !string11) {
560
- return self[IS_EMPTY] ? "" : string11;
558
+ applyStyle = (self, string12) => {
559
+ if (self.level <= 0 || !string12) {
560
+ return self[IS_EMPTY] ? "" : string12;
561
561
  }
562
562
  let styler = self[STYLER];
563
563
  if (styler === void 0) {
564
- return string11;
564
+ return string12;
565
565
  }
566
566
  const { openAll, closeAll } = styler;
567
- if (string11.includes("\x1B")) {
567
+ if (string12.includes("\x1B")) {
568
568
  while (styler !== void 0) {
569
- string11 = stringReplaceAll(string11, styler.close, styler.open);
569
+ string12 = stringReplaceAll(string12, styler.close, styler.open);
570
570
  styler = styler.parent;
571
571
  }
572
572
  }
573
- const lfIndex = string11.indexOf("\n");
573
+ const lfIndex = string12.indexOf("\n");
574
574
  if (lfIndex !== -1) {
575
- string11 = stringEncaseCRLFWithFirstIndex(string11, closeAll, openAll, lfIndex);
575
+ string12 = stringEncaseCRLFWithFirstIndex(string12, closeAll, openAll, lfIndex);
576
576
  }
577
- return openAll + string11 + closeAll;
577
+ return openAll + string12 + closeAll;
578
578
  };
579
579
  Object.defineProperties(createChalk.prototype, styles2);
580
580
  chalk = createChalk();
@@ -767,9 +767,9 @@ var require_findOption = __commonJS({
767
767
  continue;
768
768
  }
769
769
  if (node.type === "shortOptions" && opts.shortNames.length) {
770
- for (const option7 of node.options) {
771
- if (opts.shortNames.includes(option7.key)) {
772
- result.push(option7);
770
+ for (const option8 of node.options) {
771
+ if (opts.shortNames.includes(option8.key)) {
772
+ result.push(option8);
773
773
  }
774
774
  }
775
775
  }
@@ -842,7 +842,7 @@ var require_types = __commonJS({
842
842
  "use strict";
843
843
  Object.defineProperty(exports2, "__esModule", { value: true });
844
844
  exports2.boolean = exports2.string = exports2.number = void 0;
845
- exports2.optional = optional5;
845
+ exports2.optional = optional8;
846
846
  exports2.array = array3;
847
847
  var type_1 = require_type();
848
848
  exports2.number = {
@@ -869,7 +869,7 @@ var require_types = __commonJS({
869
869
  return false;
870
870
  }
871
871
  };
872
- function optional5(t) {
872
+ function optional8(t) {
873
873
  return {
874
874
  ...t,
875
875
  defaultValue() {
@@ -1403,9 +1403,9 @@ var require_subcommands = __commonJS({
1403
1403
  let errorMessage = "Not a valid subcommand name";
1404
1404
  const closeOptions = (0, didyoumean_1.default)(str, flatMap(commands, (x) => x.names));
1405
1405
  if (closeOptions) {
1406
- const option7 = Array.isArray(closeOptions) ? closeOptions[0] : closeOptions;
1406
+ const option8 = Array.isArray(closeOptions) ? closeOptions[0] : closeOptions;
1407
1407
  errorMessage += `
1408
- Did you mean ${chalk_1.default.italic(option7)}?`;
1408
+ Did you mean ${chalk_1.default.italic(option8)}?`;
1409
1409
  }
1410
1410
  throw new Error(errorMessage);
1411
1411
  }
@@ -1578,11 +1578,11 @@ var strip_ansi_exports = {};
1578
1578
  __export(strip_ansi_exports, {
1579
1579
  default: () => stripAnsi
1580
1580
  });
1581
- function stripAnsi(string11) {
1582
- if (typeof string11 !== "string") {
1583
- throw new TypeError(`Expected a \`string\`, got \`${typeof string11}\``);
1581
+ function stripAnsi(string12) {
1582
+ if (typeof string12 !== "string") {
1583
+ throw new TypeError(`Expected a \`string\`, got \`${typeof string12}\``);
1584
1584
  }
1585
- return string11.replace(regex, "");
1585
+ return string12.replace(regex, "");
1586
1586
  }
1587
1587
  var regex;
1588
1588
  var init_strip_ansi = __esm({
@@ -1781,11 +1781,11 @@ var require_command = __commonJS({
1781
1781
  }
1782
1782
  if (node.type === "forcePositional") {
1783
1783
  } else if (node.type === "shortOptions") {
1784
- for (const option7 of node.options) {
1785
- if (context.visitedNodes.has(option7)) {
1784
+ for (const option8 of node.options) {
1785
+ if (context.visitedNodes.has(option8)) {
1786
1786
  continue;
1787
1787
  }
1788
- unknownArguments.push(option7);
1788
+ unknownArguments.push(option8);
1789
1789
  }
1790
1790
  } else {
1791
1791
  unknownArguments.push(node);
@@ -1864,7 +1864,7 @@ var require_option = __commonJS({
1864
1864
  return mod && mod.__esModule ? mod : { "default": mod };
1865
1865
  };
1866
1866
  Object.defineProperty(exports2, "__esModule", { value: true });
1867
- exports2.option = option7;
1867
+ exports2.option = option8;
1868
1868
  var chalk_1 = __importDefault((init_source(), __toCommonJS(source_exports)));
1869
1869
  var Result = __importStar(require_Result());
1870
1870
  var findOption_1 = require_findOption();
@@ -1940,11 +1940,11 @@ var require_option = __commonJS({
1940
1940
  const valueFromEnv = config.env ? process.env[config.env] : void 0;
1941
1941
  const defaultValueFn = config.defaultValue || config.type.defaultValue;
1942
1942
  const onMissingFn = config.onMissing || config.type.onMissing;
1943
- const option8 = options[0];
1943
+ const option9 = options[0];
1944
1944
  let rawValue;
1945
1945
  let envPrefix = "";
1946
- if (option8 === null || option8 === void 0 ? void 0 : option8.value) {
1947
- rawValue = option8.value.node.raw;
1946
+ if (option9 === null || option9 === void 0 ? void 0 : option9.value) {
1947
+ rawValue = option9.value.node.raw;
1948
1948
  } else if (valueFromEnv !== void 0) {
1949
1949
  rawValue = valueFromEnv;
1950
1950
  envPrefix = `env[${chalk_1.default.italic(config.env)}]: `;
@@ -1979,7 +1979,7 @@ var require_option = __commonJS({
1979
1979
  });
1980
1980
  }
1981
1981
  } else {
1982
- const raw = (option8 === null || option8 === void 0 ? void 0 : option8.type) === "shortOption" ? `-${option8 === null || option8 === void 0 ? void 0 : option8.key}` : `--${config.long}`;
1982
+ const raw = (option9 === null || option9 === void 0 ? void 0 : option9.type) === "shortOption" ? `-${option9 === null || option9 === void 0 ? void 0 : option9.key}` : `--${config.long}`;
1983
1983
  return Result.err({
1984
1984
  errors: [
1985
1985
  {
@@ -2001,7 +2001,7 @@ var require_option = __commonJS({
2001
2001
  }
2002
2002
  };
2003
2003
  }
2004
- function option7(config) {
2004
+ function option8(config) {
2005
2005
  return fullOption({
2006
2006
  type: types_1.string,
2007
2007
  ...config
@@ -2040,12 +2040,12 @@ var require_errorBox = __commonJS({
2040
2040
  if (node.type === "shortOptions") {
2041
2041
  let failed = false;
2042
2042
  let s = "";
2043
- for (const option7 of node.options) {
2044
- if (error.nodes.includes(option7)) {
2045
- s += chalk_1.default.red(option7.raw);
2043
+ for (const option8 of node.options) {
2044
+ if (error.nodes.includes(option8)) {
2045
+ s += chalk_1.default.red(option8.raw);
2046
2046
  failed = true;
2047
2047
  } else {
2048
- s += chalk_1.default.dim(option7.raw);
2048
+ s += chalk_1.default.dim(option8.raw);
2049
2049
  }
2050
2050
  }
2051
2051
  const prefix = failed ? chalk_1.default.red("-") : chalk_1.default.dim("-");
@@ -3084,8 +3084,8 @@ var require_tokenizer = __commonJS({
3084
3084
  tokens.push(token);
3085
3085
  overallIndex += token.raw.length;
3086
3086
  };
3087
- for (const [stringIndex, string11] of (0, utils_1.enumerate)(strings)) {
3088
- const chars = [...string11];
3087
+ for (const [stringIndex, string12] of (0, utils_1.enumerate)(strings)) {
3088
+ const chars = [...string12];
3089
3089
  for (let i = 0; i < chars.length; i++) {
3090
3090
  if (chars[i] === "-" && chars[i + 1] === "-") {
3091
3091
  push({ type: "longPrefix", raw: "--", index: overallIndex });
@@ -3346,15 +3346,15 @@ var require_multiflag = __commonJS({
3346
3346
  longNames: [config.long],
3347
3347
  shortNames: config.short ? [config.short] : []
3348
3348
  }).filter((x) => !visitedNodes.has(x));
3349
- for (const option7 of options) {
3350
- visitedNodes.add(option7);
3349
+ for (const option8 of options) {
3350
+ visitedNodes.add(option8);
3351
3351
  }
3352
3352
  const optionValues = [];
3353
3353
  const errors = [];
3354
- for (const option7 of options) {
3355
- const decoded = await Result.safeAsync(flag_1.boolean.from((_b = (_a = option7.value) === null || _a === void 0 ? void 0 : _a.node.raw) !== null && _b !== void 0 ? _b : "true"));
3354
+ for (const option8 of options) {
3355
+ const decoded = await Result.safeAsync(flag_1.boolean.from((_b = (_a = option8.value) === null || _a === void 0 ? void 0 : _a.node.raw) !== null && _b !== void 0 ? _b : "true"));
3356
3356
  if (Result.isErr(decoded)) {
3357
- errors.push({ nodes: [option7], message: decoded.error.message });
3357
+ errors.push({ nodes: [option8], message: decoded.error.message });
3358
3358
  } else {
3359
3359
  optionValues.push(decoded.value);
3360
3360
  }
@@ -3518,16 +3518,16 @@ var require_multioption = __commonJS({
3518
3518
  });
3519
3519
  }
3520
3520
  }
3521
- for (const option7 of options) {
3522
- visitedNodes.add(option7);
3521
+ for (const option8 of options) {
3522
+ visitedNodes.add(option8);
3523
3523
  }
3524
3524
  const optionValues = [];
3525
3525
  const errors = [];
3526
3526
  const flagNodes = [];
3527
- for (const option7 of options) {
3528
- const providedValue = (_a = option7.value) === null || _a === void 0 ? void 0 : _a.node.raw;
3527
+ for (const option8 of options) {
3528
+ const providedValue = (_a = option8.value) === null || _a === void 0 ? void 0 : _a.node.raw;
3529
3529
  if (providedValue === void 0) {
3530
- flagNodes.push(option7);
3530
+ flagNodes.push(option8);
3531
3531
  continue;
3532
3532
  }
3533
3533
  optionValues.push(providedValue);
@@ -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.17.0",
3855
+ version: "0.19.0",
3856
3856
  description: "A command-line interface for Packmind linting and code quality checks",
3857
3857
  private: false,
3858
3858
  bin: {
@@ -6399,6 +6399,16 @@ var ChangeProposalGateway = class {
6399
6399
  }
6400
6400
  );
6401
6401
  };
6402
+ this.check = async (command21) => {
6403
+ const { organizationId } = this.httpClient.getAuthContext();
6404
+ return this.httpClient.request(
6405
+ `/api/v0/organizations/${organizationId}/spaces/${command21.spaceId}/change-proposals/check`,
6406
+ {
6407
+ method: "POST",
6408
+ body: { proposals: command21.proposals }
6409
+ }
6410
+ );
6411
+ };
6402
6412
  }
6403
6413
  };
6404
6414
 
@@ -11427,6 +11437,70 @@ function normalizeScopeValue(rawValue) {
11427
11437
  return rawValue.replace(/(?:^["'])|(?:["']$)/g, "");
11428
11438
  }
11429
11439
 
11440
+ // apps/cli/src/application/utils/ruleSimilarity.ts
11441
+ var DEFAULT_SIMILARITY_THRESHOLD = 0.5;
11442
+ function matchUpdatedRules(deletedRules, addedRules, threshold = DEFAULT_SIMILARITY_THRESHOLD) {
11443
+ const updates = [];
11444
+ const usedDeleted = /* @__PURE__ */ new Set();
11445
+ const usedAdded = /* @__PURE__ */ new Set();
11446
+ const pairs = [];
11447
+ for (let d = 0; d < deletedRules.length; d++) {
11448
+ for (let a = 0; a < addedRules.length; a++) {
11449
+ const score = combinedSimilarity(deletedRules[d], addedRules[a]);
11450
+ if (score >= threshold) {
11451
+ pairs.push({ deletedIdx: d, addedIdx: a, score });
11452
+ }
11453
+ }
11454
+ }
11455
+ pairs.sort((a, b) => b.score - a.score);
11456
+ for (const pair of pairs) {
11457
+ if (usedDeleted.has(pair.deletedIdx) || usedAdded.has(pair.addedIdx))
11458
+ continue;
11459
+ usedDeleted.add(pair.deletedIdx);
11460
+ usedAdded.add(pair.addedIdx);
11461
+ updates.push({
11462
+ oldValue: deletedRules[pair.deletedIdx],
11463
+ newValue: addedRules[pair.addedIdx]
11464
+ });
11465
+ }
11466
+ const remainingDeleted = deletedRules.filter((_, i) => !usedDeleted.has(i));
11467
+ const remainingAdded = addedRules.filter((_, i) => !usedAdded.has(i));
11468
+ return { updates, remainingDeleted, remainingAdded };
11469
+ }
11470
+ function jaccardSimilarity(a, b) {
11471
+ const setA = new Set(a.toLowerCase().split(/\s+/).filter(Boolean));
11472
+ const setB = new Set(b.toLowerCase().split(/\s+/).filter(Boolean));
11473
+ if (setA.size === 0 && setB.size === 0) return 1;
11474
+ const intersection = new Set([...setA].filter((word) => setB.has(word)));
11475
+ const union = /* @__PURE__ */ new Set([...setA, ...setB]);
11476
+ return intersection.size / union.size;
11477
+ }
11478
+ function combinedSimilarity(a, b) {
11479
+ return Math.max(levenshteinSimilarity(a, b), jaccardSimilarity(a, b));
11480
+ }
11481
+ function levenshteinSimilarity(a, b) {
11482
+ const maxLen = Math.max(a.length, b.length);
11483
+ if (maxLen === 0) return 1;
11484
+ const matrix = [];
11485
+ for (let i = 0; i <= a.length; i++) {
11486
+ matrix[i] = [i];
11487
+ }
11488
+ for (let j = 0; j <= b.length; j++) {
11489
+ matrix[0][j] = j;
11490
+ }
11491
+ for (let i = 1; i <= a.length; i++) {
11492
+ for (let j = 1; j <= b.length; j++) {
11493
+ const cost = a[i - 1] === b[j - 1] ? 0 : 1;
11494
+ matrix[i][j] = Math.min(
11495
+ matrix[i - 1][j] + 1,
11496
+ matrix[i][j - 1] + 1,
11497
+ matrix[i - 1][j - 1] + cost
11498
+ );
11499
+ }
11500
+ }
11501
+ return 1 - matrix[a.length][b.length] / maxLen;
11502
+ }
11503
+
11430
11504
  // apps/cli/src/application/useCases/diffStrategies/StandardDiffStrategy.ts
11431
11505
  var StandardDiffStrategy = class {
11432
11506
  supports(file) {
@@ -11505,29 +11579,45 @@ var StandardDiffStrategy = class {
11505
11579
  }
11506
11580
  const serverRules = new Set(serverParsed.rules);
11507
11581
  const localRules = new Set(localParsed.rules);
11508
- for (const rule of serverRules) {
11509
- if (!localRules.has(rule)) {
11510
- const ruleId = createRuleId("unresolved");
11511
- diffs.push({
11512
- ...diffBase,
11513
- type: "deleteRule" /* deleteRule */,
11514
- payload: {
11515
- targetId: ruleId,
11516
- item: { id: ruleId, content: rule }
11517
- }
11518
- });
11519
- }
11582
+ const deletedRules = [...serverRules].filter(
11583
+ (rule) => !localRules.has(rule)
11584
+ );
11585
+ const addedRules = [...localRules].filter((rule) => !serverRules.has(rule));
11586
+ const { updates, remainingDeleted, remainingAdded } = matchUpdatedRules(
11587
+ deletedRules,
11588
+ addedRules
11589
+ );
11590
+ for (const update of updates) {
11591
+ const ruleId = createRuleId("unresolved");
11592
+ diffs.push({
11593
+ ...diffBase,
11594
+ type: "updateRule" /* updateRule */,
11595
+ payload: {
11596
+ targetId: ruleId,
11597
+ oldValue: update.oldValue,
11598
+ newValue: update.newValue
11599
+ }
11600
+ });
11520
11601
  }
11521
- for (const rule of localRules) {
11522
- if (!serverRules.has(rule)) {
11523
- diffs.push({
11524
- ...diffBase,
11525
- type: "addRule" /* addRule */,
11526
- payload: {
11527
- item: { content: rule }
11528
- }
11529
- });
11530
- }
11602
+ for (const rule of remainingDeleted) {
11603
+ const ruleId = createRuleId("unresolved");
11604
+ diffs.push({
11605
+ ...diffBase,
11606
+ type: "deleteRule" /* deleteRule */,
11607
+ payload: {
11608
+ targetId: ruleId,
11609
+ item: { id: ruleId, content: rule }
11610
+ }
11611
+ });
11612
+ }
11613
+ for (const rule of remainingAdded) {
11614
+ diffs.push({
11615
+ ...diffBase,
11616
+ type: "addRule" /* addRule */,
11617
+ payload: {
11618
+ item: { content: rule }
11619
+ }
11620
+ });
11531
11621
  }
11532
11622
  return diffs;
11533
11623
  }
@@ -11605,7 +11695,7 @@ var SubmitDiffsUseCase = class {
11605
11695
  this.packmindGateway = packmindGateway;
11606
11696
  }
11607
11697
  async execute(command21) {
11608
- const { groupedDiffs } = command21;
11698
+ const { groupedDiffs, message } = command21;
11609
11699
  const skipped = [];
11610
11700
  const validDiffs = [];
11611
11701
  for (const group of groupedDiffs) {
@@ -11647,7 +11737,8 @@ var SubmitDiffsUseCase = class {
11647
11737
  type: diff.type,
11648
11738
  artefactId: diff.artifactId,
11649
11739
  payload: diff.payload,
11650
- captureMode: "commit" /* commit */
11740
+ captureMode: "commit" /* commit */,
11741
+ message
11651
11742
  }))
11652
11743
  });
11653
11744
  submitted += response.created;
@@ -11665,6 +11756,68 @@ var SubmitDiffsUseCase = class {
11665
11756
  }
11666
11757
  };
11667
11758
 
11759
+ // apps/cli/src/application/useCases/CheckDiffsUseCase.ts
11760
+ var SUPPORTED_ARTIFACT_TYPES2 = /* @__PURE__ */ new Set(["command", "skill", "standard"]);
11761
+ var CheckDiffsUseCase = class {
11762
+ constructor(packmindGateway) {
11763
+ this.packmindGateway = packmindGateway;
11764
+ }
11765
+ async execute(command21) {
11766
+ const { groupedDiffs } = command21;
11767
+ const results = [];
11768
+ const validDiffs = [];
11769
+ const invalidDiffs = [];
11770
+ for (const group of groupedDiffs) {
11771
+ for (const diff of group) {
11772
+ if (!SUPPORTED_ARTIFACT_TYPES2.has(diff.artifactType) || !diff.artifactId || !diff.spaceId) {
11773
+ invalidDiffs.push(diff);
11774
+ continue;
11775
+ }
11776
+ validDiffs.push(diff);
11777
+ }
11778
+ }
11779
+ const diffsBySpaceId = /* @__PURE__ */ new Map();
11780
+ for (const diff of validDiffs) {
11781
+ const existing = diffsBySpaceId.get(diff.spaceId) ?? [];
11782
+ existing.push(diff);
11783
+ diffsBySpaceId.set(diff.spaceId, existing);
11784
+ }
11785
+ const checkedMap = /* @__PURE__ */ new Map();
11786
+ for (const [spaceId, diffs] of diffsBySpaceId) {
11787
+ const response = await this.packmindGateway.changeProposals.check({
11788
+ spaceId,
11789
+ proposals: diffs.map((diff) => ({
11790
+ type: diff.type,
11791
+ artefactId: diff.artifactId,
11792
+ payload: diff.payload,
11793
+ captureMode: "commit" /* commit */,
11794
+ message: ""
11795
+ }))
11796
+ });
11797
+ for (const result of response.results) {
11798
+ const diff = diffs[result.index];
11799
+ checkedMap.set(diff, {
11800
+ diff,
11801
+ exists: result.exists,
11802
+ createdAt: result.createdAt,
11803
+ message: result.message
11804
+ });
11805
+ }
11806
+ }
11807
+ for (const group of groupedDiffs) {
11808
+ for (const diff of group) {
11809
+ const checked = checkedMap.get(diff);
11810
+ if (checked) {
11811
+ results.push(checked);
11812
+ } else {
11813
+ results.push({ diff, exists: false, createdAt: null, message: null });
11814
+ }
11815
+ }
11816
+ }
11817
+ return { results };
11818
+ }
11819
+ };
11820
+
11668
11821
  // apps/cli/src/PackmindCliHexaFactory.ts
11669
11822
  var PackmindCliHexaFactory = class {
11670
11823
  constructor() {
@@ -11718,7 +11871,8 @@ var PackmindCliHexaFactory = class {
11718
11871
  diffArtefacts: new DiffArtefactsUseCase(
11719
11872
  this.repositories.packmindGateway
11720
11873
  ),
11721
- submitDiffs: new SubmitDiffsUseCase(this.repositories.packmindGateway)
11874
+ submitDiffs: new SubmitDiffsUseCase(this.repositories.packmindGateway),
11875
+ checkDiffs: new CheckDiffsUseCase(this.repositories.packmindGateway)
11722
11876
  };
11723
11877
  }
11724
11878
  };
@@ -11770,8 +11924,11 @@ var PackmindCliHexa = class {
11770
11924
  async diffArtefacts(command21) {
11771
11925
  return this.hexa.useCases.diffArtefacts.execute(command21);
11772
11926
  }
11773
- async submitDiffs(groupedDiffs) {
11774
- return this.hexa.useCases.submitDiffs.execute({ groupedDiffs });
11927
+ async submitDiffs(groupedDiffs, message) {
11928
+ return this.hexa.useCases.submitDiffs.execute({ groupedDiffs, message });
11929
+ }
11930
+ async checkDiffs(groupedDiffs) {
11931
+ return this.hexa.useCases.checkDiffs.execute({ groupedDiffs });
11775
11932
  }
11776
11933
  async listPackages(command21) {
11777
11934
  return this.hexa.useCases.listPackages.execute(command21);
@@ -13873,21 +14030,24 @@ function validatePlaybook(data) {
13873
14030
  }
13874
14031
 
13875
14032
  // apps/cli/src/infra/utils/readPlaybookFile.ts
14033
+ function parseAndValidatePlaybook(content) {
14034
+ let parsed;
14035
+ try {
14036
+ parsed = JSON.parse(content);
14037
+ } catch (e) {
14038
+ return {
14039
+ isValid: false,
14040
+ errors: [
14041
+ `Invalid JSON: ${e instanceof Error ? e.message : "Unknown error"}`
14042
+ ]
14043
+ };
14044
+ }
14045
+ return validatePlaybook(parsed);
14046
+ }
13876
14047
  async function readPlaybookFile(filePath) {
13877
14048
  try {
13878
14049
  const content = await fs16.readFile(filePath, "utf-8");
13879
- let parsed;
13880
- try {
13881
- parsed = JSON.parse(content);
13882
- } catch (e) {
13883
- return {
13884
- isValid: false,
13885
- errors: [
13886
- `Invalid JSON: ${e instanceof Error ? e.message : "Unknown error"}`
13887
- ]
13888
- };
13889
- }
13890
- return validatePlaybook(parsed);
14050
+ return parseAndValidatePlaybook(content);
13891
14051
  } catch (e) {
13892
14052
  return {
13893
14053
  isValid: false,
@@ -13898,9 +14058,40 @@ async function readPlaybookFile(filePath) {
13898
14058
  }
13899
14059
  }
13900
14060
 
14061
+ // apps/cli/src/infra/utils/readStdin.ts
14062
+ async function readStdin(input = process.stdin) {
14063
+ if ("isTTY" in input && input.isTTY) {
14064
+ throw new Error(
14065
+ "No piped input detected. Please provide content via stdin or specify a file path."
14066
+ );
14067
+ }
14068
+ const chunks = [];
14069
+ for await (const chunk of input) {
14070
+ chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
14071
+ }
14072
+ const content = Buffer.concat(chunks).toString("utf-8").trim();
14073
+ if (!content) {
14074
+ throw new Error("Stdin is empty. Please provide JSON content via pipe.");
14075
+ }
14076
+ return content;
14077
+ }
14078
+
13901
14079
  // apps/cli/src/infra/commands/createStandardHandler.ts
13902
14080
  async function createStandardHandler(filePath, useCase, originSkill) {
13903
- const readResult = await readPlaybookFile(filePath);
14081
+ let readResult;
14082
+ if (filePath) {
14083
+ readResult = await readPlaybookFile(filePath);
14084
+ } else {
14085
+ try {
14086
+ const content = await readStdin();
14087
+ readResult = parseAndValidatePlaybook(content);
14088
+ } catch (e) {
14089
+ return {
14090
+ success: false,
14091
+ error: e instanceof Error ? e.message : "Failed to read from stdin"
14092
+ };
14093
+ }
14094
+ }
13904
14095
  if (!readResult.isValid) {
13905
14096
  return {
13906
14097
  success: false,
@@ -13961,12 +14152,12 @@ var CreateStandardFromPlaybookUseCase = class {
13961
14152
  // apps/cli/src/infra/commands/CreateStandardCommand.ts
13962
14153
  var createStandardCommand = (0, import_cmd_ts13.command)({
13963
14154
  name: "create",
13964
- description: "Create a coding standard from a playbook JSON file",
14155
+ description: "Create a coding standard from a playbook JSON file or stdin",
13965
14156
  args: {
13966
14157
  file: (0, import_cmd_ts13.positional)({
13967
14158
  displayName: "file",
13968
- description: "Path to the playbook JSON file",
13969
- type: import_cmd_ts13.string
14159
+ description: "Path to the playbook JSON file (reads from stdin if omitted)",
14160
+ type: (0, import_cmd_ts13.optional)(import_cmd_ts13.string)
13970
14161
  }),
13971
14162
  originSkill: originSkillOption
13972
14163
  },
@@ -14127,21 +14318,24 @@ function validateCommandPlaybook(data) {
14127
14318
  }
14128
14319
 
14129
14320
  // apps/cli/src/infra/utils/readCommandPlaybookFile.ts
14321
+ function parseAndValidateCommandPlaybook(content) {
14322
+ let parsed;
14323
+ try {
14324
+ parsed = JSON.parse(content);
14325
+ } catch (e) {
14326
+ return {
14327
+ isValid: false,
14328
+ errors: [
14329
+ `Invalid JSON: ${e instanceof Error ? e.message : "Unknown error"}`
14330
+ ]
14331
+ };
14332
+ }
14333
+ return validateCommandPlaybook(parsed);
14334
+ }
14130
14335
  async function readCommandPlaybookFile(filePath) {
14131
14336
  try {
14132
14337
  const content = await fs17.readFile(filePath, "utf-8");
14133
- let parsed;
14134
- try {
14135
- parsed = JSON.parse(content);
14136
- } catch (e) {
14137
- return {
14138
- isValid: false,
14139
- errors: [
14140
- `Invalid JSON: ${e instanceof Error ? e.message : "Unknown error"}`
14141
- ]
14142
- };
14143
- }
14144
- return validateCommandPlaybook(parsed);
14338
+ return parseAndValidateCommandPlaybook(content);
14145
14339
  } catch (e) {
14146
14340
  return {
14147
14341
  isValid: false,
@@ -14157,7 +14351,20 @@ function buildWebappUrl(host, orgSlug, commandId) {
14157
14351
  return `${host}/org/${orgSlug}/space/global/commands/${commandId}`;
14158
14352
  }
14159
14353
  async function createCommandHandler(filePath, useCase, originSkill) {
14160
- const readResult = await readCommandPlaybookFile(filePath);
14354
+ let readResult;
14355
+ if (filePath) {
14356
+ readResult = await readCommandPlaybookFile(filePath);
14357
+ } else {
14358
+ try {
14359
+ const content = await readStdin();
14360
+ readResult = parseAndValidateCommandPlaybook(content);
14361
+ } catch (e) {
14362
+ return {
14363
+ success: false,
14364
+ error: e instanceof Error ? e.message : "Failed to read from stdin"
14365
+ };
14366
+ }
14367
+ }
14161
14368
  if (!readResult.isValid) {
14162
14369
  return {
14163
14370
  success: false,
@@ -14232,12 +14439,12 @@ var CreateCommandFromPlaybookUseCase = class {
14232
14439
  // apps/cli/src/infra/commands/CreateCommandCommand.ts
14233
14440
  var createCommandCommand = (0, import_cmd_ts16.command)({
14234
14441
  name: "create",
14235
- description: "Create a command from a playbook JSON file",
14442
+ description: "Create a command from a playbook JSON file or stdin",
14236
14443
  args: {
14237
14444
  file: (0, import_cmd_ts16.positional)({
14238
14445
  displayName: "file",
14239
- description: "Path to the command playbook JSON file",
14240
- type: import_cmd_ts16.string
14446
+ description: "Path to the command playbook JSON file (reads from stdin if omitted)",
14447
+ type: (0, import_cmd_ts16.optional)(import_cmd_ts16.string)
14241
14448
  }),
14242
14449
  originSkill: originSkillOption
14243
14450
  },
@@ -14379,6 +14586,51 @@ function formatContentDiff(oldContent, newContent) {
14379
14586
  };
14380
14587
  }
14381
14588
 
14589
+ // apps/cli/src/infra/utils/editorMessage.ts
14590
+ var import_child_process4 = require("child_process");
14591
+ var import_fs19 = require("fs");
14592
+ var import_os2 = require("os");
14593
+ var import_path4 = require("path");
14594
+ var MAX_MESSAGE_LENGTH = 1024;
14595
+ var EDITOR_TEMPLATE = `
14596
+ # Enter a message describing the intent behind these changes.
14597
+ # Lines starting with '#' will be ignored.
14598
+ # An empty message aborts the submission.
14599
+ `;
14600
+ function openEditorForMessage() {
14601
+ const tmpFile = (0, import_path4.join)((0, import_os2.tmpdir)(), `packmind-msg-${Date.now()}.txt`);
14602
+ try {
14603
+ (0, import_fs19.writeFileSync)(tmpFile, EDITOR_TEMPLATE, "utf-8");
14604
+ const editor = process.env.EDITOR || process.env.VISUAL || "vi";
14605
+ const result = (0, import_child_process4.spawnSync)(editor, [tmpFile], {
14606
+ stdio: "inherit"
14607
+ });
14608
+ if (result.status !== 0) {
14609
+ throw new Error(`Editor exited with status ${result.status}`);
14610
+ }
14611
+ const content = (0, import_fs19.readFileSync)(tmpFile, "utf-8");
14612
+ return content.split("\n").filter((line) => !line.startsWith("#")).join("\n").trim();
14613
+ } finally {
14614
+ try {
14615
+ (0, import_fs19.unlinkSync)(tmpFile);
14616
+ } catch {
14617
+ }
14618
+ }
14619
+ }
14620
+ function validateMessage(message) {
14621
+ const trimmed = message.trim();
14622
+ if (trimmed.length === 0) {
14623
+ return { valid: false, error: "Message cannot be empty." };
14624
+ }
14625
+ if (trimmed.length > MAX_MESSAGE_LENGTH) {
14626
+ return {
14627
+ valid: false,
14628
+ error: `Message exceeds maximum length of ${MAX_MESSAGE_LENGTH} characters (got ${trimmed.length}).`
14629
+ };
14630
+ }
14631
+ return { valid: true, message: trimmed };
14632
+ }
14633
+
14382
14634
  // apps/cli/src/infra/commands/diffArtefactsHandler.ts
14383
14635
  init_source();
14384
14636
  var ARTIFACT_TYPE_LABELS = {
@@ -14461,8 +14713,27 @@ function formatDiffPayload(diff, log) {
14461
14713
  log(line);
14462
14714
  }
14463
14715
  }
14464
- async function diffArtefactsHandler(deps) {
14465
- const { packmindCliHexa, exit, getCwd, log, error, submit } = deps;
14716
+ function formatSubmittedDate(isoDate) {
14717
+ const date = new Date(isoDate);
14718
+ const datePart = date.toLocaleDateString("en-US", {
14719
+ year: "numeric",
14720
+ month: "short",
14721
+ day: "numeric"
14722
+ });
14723
+ const timePart = date.toLocaleTimeString("en-US", {
14724
+ hour: "numeric",
14725
+ minute: "2-digit",
14726
+ hour12: true
14727
+ });
14728
+ return `${datePart} ${timePart}`;
14729
+ }
14730
+ function buildSubmittedFooter(submittedDiffs) {
14731
+ const proposalCount = submittedDiffs.length;
14732
+ const proposalWord = proposalCount === 1 ? "change proposal" : "change proposals";
14733
+ return `${proposalCount} ${proposalWord} ignored, run \`packmind-cli diff --include-submitted\` to see what's waiting for validation`;
14734
+ }
14735
+ async function readConfigAndPackages(deps) {
14736
+ const { packmindCliHexa, exit, getCwd, log, error } = deps;
14466
14737
  const cwd = getCwd();
14467
14738
  let configPackages;
14468
14739
  let configAgents;
@@ -14483,7 +14754,7 @@ async function diffArtefactsHandler(deps) {
14483
14754
  }
14484
14755
  error("\n\u{1F4A1} Please fix the packmind.json file or delete it to continue.");
14485
14756
  exit(1);
14486
- return { diffsFound: 0 };
14757
+ return null;
14487
14758
  }
14488
14759
  if (configPackages.length === 0) {
14489
14760
  log("Usage: packmind-cli diff");
@@ -14491,37 +14762,219 @@ async function diffArtefactsHandler(deps) {
14491
14762
  log("Compare local command files against the server.");
14492
14763
  log("Configure packages in packmind.json first.");
14493
14764
  exit(0);
14494
- return { diffsFound: 0 };
14765
+ return null;
14495
14766
  }
14496
- try {
14497
- let gitRemoteUrl;
14498
- let gitBranch;
14499
- let relativePath;
14500
- const gitRoot = await packmindCliHexa.tryGetGitRepositoryRoot(cwd);
14501
- if (gitRoot) {
14502
- try {
14503
- gitRemoteUrl = packmindCliHexa.getGitRemoteUrlFromPath(gitRoot);
14504
- gitBranch = packmindCliHexa.getCurrentBranch(gitRoot);
14505
- relativePath = cwd.startsWith(gitRoot) ? cwd.slice(gitRoot.length) : "/";
14506
- if (!relativePath.startsWith("/")) {
14507
- relativePath = "/" + relativePath;
14508
- }
14509
- if (!relativePath.endsWith("/")) {
14510
- relativePath = relativePath + "/";
14511
- }
14512
- } catch (err) {
14513
- logWarningConsole(
14514
- `Failed to collect git info: ${err instanceof Error ? err.message : String(err)}`
14515
- );
14767
+ return { configPackages, configAgents };
14768
+ }
14769
+ async function collectGitInfo(deps) {
14770
+ const { packmindCliHexa, exit, getCwd, error } = deps;
14771
+ const cwd = getCwd();
14772
+ let gitRemoteUrl;
14773
+ let gitBranch;
14774
+ let relativePath;
14775
+ const gitRoot = await packmindCliHexa.tryGetGitRepositoryRoot(cwd);
14776
+ if (gitRoot) {
14777
+ try {
14778
+ gitRemoteUrl = packmindCliHexa.getGitRemoteUrlFromPath(gitRoot);
14779
+ gitBranch = packmindCliHexa.getCurrentBranch(gitRoot);
14780
+ relativePath = cwd.startsWith(gitRoot) ? cwd.slice(gitRoot.length) : "/";
14781
+ if (!relativePath.startsWith("/")) {
14782
+ relativePath = "/" + relativePath;
14516
14783
  }
14784
+ if (!relativePath.endsWith("/")) {
14785
+ relativePath = relativePath + "/";
14786
+ }
14787
+ } catch (err) {
14788
+ logWarningConsole(
14789
+ `Failed to collect git info: ${err instanceof Error ? err.message : String(err)}`
14790
+ );
14791
+ }
14792
+ }
14793
+ if (!gitRemoteUrl || !gitBranch || !relativePath) {
14794
+ error(
14795
+ "\n\u274C Could not determine git repository info. The diff command requires a git repository with a remote configured."
14796
+ );
14797
+ exit(1);
14798
+ return null;
14799
+ }
14800
+ return { gitRemoteUrl, gitBranch, relativePath };
14801
+ }
14802
+ async function handleSubmission(params) {
14803
+ const { packmindCliHexa, unsubmittedItems, messageFlag, exit } = params;
14804
+ const unsubmittedDiffs = unsubmittedItems.map((r) => r.diff);
14805
+ if (unsubmittedDiffs.length === 0) {
14806
+ logInfoConsole("All changes already submitted.");
14807
+ return false;
14808
+ }
14809
+ let message;
14810
+ if (messageFlag !== void 0) {
14811
+ const validation = validateMessage(messageFlag);
14812
+ if (!validation.valid) {
14813
+ logErrorConsole(validation.error);
14814
+ exit(1);
14815
+ return true;
14517
14816
  }
14518
- if (!gitRemoteUrl || !gitBranch || !relativePath) {
14519
- error(
14520
- "\n\u274C Could not determine git repository info. The diff command requires a git repository with a remote configured."
14817
+ message = validation.message;
14818
+ } else if (process.stdin.isTTY) {
14819
+ const editorMessage = openEditorForMessage();
14820
+ const validation = validateMessage(editorMessage);
14821
+ if (!validation.valid) {
14822
+ logErrorConsole(
14823
+ "Aborting submission: empty message. Use -m to provide a message."
14521
14824
  );
14522
14825
  exit(1);
14826
+ return true;
14827
+ }
14828
+ message = validation.message;
14829
+ } else {
14830
+ logErrorConsole(
14831
+ 'Non-interactive mode requires -m flag. Use: packmind-cli diff --submit -m "your message"'
14832
+ );
14833
+ exit(1);
14834
+ return true;
14835
+ }
14836
+ const groupedUnsubmitted = Array.from(
14837
+ groupDiffsByArtefact(unsubmittedDiffs).values()
14838
+ );
14839
+ const result = await packmindCliHexa.submitDiffs(groupedUnsubmitted, message);
14840
+ for (const err of result.errors) {
14841
+ if (err.code === "ChangeProposalPayloadMismatchError") {
14842
+ logErrorConsole(
14843
+ `Failed to submit "${err.name}": ${err.artifactType ?? "artifact"} is outdated, please run \`packmind-cli install\` to update it`
14844
+ );
14845
+ } else {
14846
+ logErrorConsole(`Failed to submit "${err.name}": ${err.message}`);
14847
+ }
14848
+ }
14849
+ if (result.submitted > 0) {
14850
+ const truncatedMessage = message.length > 50 ? message.slice(0, 50) + "..." : message;
14851
+ logInfoConsole(`Message: "${truncatedMessage}"`);
14852
+ }
14853
+ const summaryParts = [];
14854
+ if (result.submitted > 0) {
14855
+ summaryParts.push(`${result.submitted} submitted`);
14856
+ }
14857
+ if (result.alreadySubmitted > 0) {
14858
+ summaryParts.push(`${result.alreadySubmitted} already submitted`);
14859
+ }
14860
+ if (result.errors.length > 0) {
14861
+ const errorWord = result.errors.length === 1 ? "error" : "errors";
14862
+ summaryParts.push(`${result.errors.length} ${errorWord}`);
14863
+ }
14864
+ if (summaryParts.length > 0) {
14865
+ const summaryMessage = `Summary: ${summaryParts.join(", ")}`;
14866
+ if (result.errors.length === 0 && result.alreadySubmitted === 0) {
14867
+ logSuccessConsole(summaryMessage);
14868
+ } else if (result.errors.length > 0 && result.submitted > 0 || result.alreadySubmitted > 0) {
14869
+ logWarningConsole(summaryMessage);
14870
+ } else {
14871
+ logErrorConsole(summaryMessage);
14872
+ }
14873
+ }
14874
+ return false;
14875
+ }
14876
+ function extractUniqueAndSortedArtefacts(groups) {
14877
+ const typeSortOrder = {
14878
+ command: 0,
14879
+ skill: 1,
14880
+ standard: 2
14881
+ };
14882
+ const uniqueArtefacts = /* @__PURE__ */ new Map();
14883
+ for (const [key, groupDiffs] of groups) {
14884
+ if (!uniqueArtefacts.has(key)) {
14885
+ uniqueArtefacts.set(key, {
14886
+ type: groupDiffs[0].artifactType,
14887
+ name: groupDiffs[0].artifactName
14888
+ });
14889
+ }
14890
+ }
14891
+ const sortedArtefacts = Array.from(uniqueArtefacts.values()).sort(
14892
+ (a, b) => typeSortOrder[a.type] - typeSortOrder[b.type] || a.name.localeCompare(b.name, void 0, { sensitivity: "base" })
14893
+ );
14894
+ return sortedArtefacts;
14895
+ }
14896
+ function displayDiffs(params) {
14897
+ const {
14898
+ diffsToDisplay,
14899
+ submittedLookup,
14900
+ includeSubmitted,
14901
+ unsubmittedItems,
14902
+ submittedItems,
14903
+ log
14904
+ } = params;
14905
+ log(formatHeader(`
14906
+ Changes found:
14907
+ `));
14908
+ const groups = groupDiffsByArtefact(diffsToDisplay);
14909
+ for (const [, groupDiffs] of groups) {
14910
+ const { artifactType, artifactName } = groupDiffs[0];
14911
+ const typeLabel = ARTIFACT_TYPE_LABELS[artifactType];
14912
+ log(formatBold(`${typeLabel} "${artifactName}"`));
14913
+ const subGroups = subGroupByChangeContent(groupDiffs);
14914
+ for (const subGroup of subGroups) {
14915
+ for (const diff of subGroup) {
14916
+ log(` ${formatFilePath(diff.filePath)}`);
14917
+ }
14918
+ const label = CHANGE_PROPOSAL_TYPE_LABELS[subGroup[0].type] ?? "content changed";
14919
+ const checkItem = submittedLookup.get(subGroup[0]);
14920
+ if (includeSubmitted && checkItem?.exists && checkItem.createdAt) {
14921
+ const dateStr = formatSubmittedDate(checkItem.createdAt);
14922
+ const messageSuffix = checkItem.message ? ` "${checkItem.message.length > 50 ? checkItem.message.slice(0, 50) + "..." : checkItem.message}"` : "";
14923
+ log(
14924
+ ` - ${label} ${source_default.dim(`[already submitted on ${dateStr}${messageSuffix}]`)}`
14925
+ );
14926
+ } else {
14927
+ log(` - ${label}`);
14928
+ }
14929
+ formatDiffPayload(subGroup[0], log);
14930
+ }
14931
+ log("");
14932
+ }
14933
+ const changeCount = diffsToDisplay.length;
14934
+ const changeWord = changeCount === 1 ? "change" : "changes";
14935
+ const sortedArtefacts = extractUniqueAndSortedArtefacts(groups);
14936
+ const artefactCount = sortedArtefacts.length;
14937
+ const artefactWord = artefactCount === 1 ? "artefact" : "artefacts";
14938
+ const allSubmittedSuffix = includeSubmitted && unsubmittedItems.length === 0 ? " (all already submitted)" : "";
14939
+ logWarningConsole(
14940
+ `Summary: ${changeCount} ${changeWord} found on ${artefactCount} ${artefactWord}${allSubmittedSuffix}:`
14941
+ );
14942
+ for (const artefact of sortedArtefacts) {
14943
+ const typeLabel = ARTIFACT_TYPE_LABELS[artefact.type];
14944
+ const key = `${artefact.type}:${artefact.name}`;
14945
+ const artefactDiffs = groups.get(key) ?? [];
14946
+ const allDiffsSubmitted = includeSubmitted && artefactDiffs.length > 0 && artefactDiffs.every((d) => submittedLookup.get(d)?.exists);
14947
+ const suffix = allDiffsSubmitted ? " (all already submitted)" : "";
14948
+ logWarningConsole(`* ${typeLabel} "${artefact.name}"${suffix}`);
14949
+ }
14950
+ if (!includeSubmitted && submittedItems.length > 0) {
14951
+ logInfoConsole(buildSubmittedFooter(submittedItems));
14952
+ }
14953
+ return changeCount;
14954
+ }
14955
+ async function diffArtefactsHandler(deps) {
14956
+ const {
14957
+ packmindCliHexa,
14958
+ exit,
14959
+ getCwd,
14960
+ log,
14961
+ error,
14962
+ submit,
14963
+ includeSubmitted,
14964
+ message: messageFlag
14965
+ } = deps;
14966
+ const cwd = getCwd();
14967
+ const config = await readConfigAndPackages(deps);
14968
+ if (!config) {
14969
+ return { diffsFound: 0 };
14970
+ }
14971
+ const { configPackages, configAgents } = config;
14972
+ try {
14973
+ const gitInfo = await collectGitInfo(deps);
14974
+ if (!gitInfo) {
14523
14975
  return { diffsFound: 0 };
14524
14976
  }
14977
+ const { gitRemoteUrl, gitBranch, relativePath } = gitInfo;
14525
14978
  const packageCount = configPackages.length;
14526
14979
  const packageWord = packageCount === 1 ? "package" : "packages";
14527
14980
  logInfoConsole(
@@ -14543,85 +14996,43 @@ async function diffArtefactsHandler(deps) {
14543
14996
  exit(0);
14544
14997
  return { diffsFound: 0 };
14545
14998
  }
14546
- log(formatHeader(`
14547
- Changes found:
14548
- `));
14549
- const groups = groupDiffsByArtefact(diffs);
14550
- for (const [, groupDiffs] of groups) {
14551
- const { artifactType, artifactName } = groupDiffs[0];
14552
- const typeLabel = ARTIFACT_TYPE_LABELS[artifactType];
14553
- log(formatBold(`${typeLabel} "${artifactName}"`));
14554
- const subGroups = subGroupByChangeContent(groupDiffs);
14555
- for (const subGroup of subGroups) {
14556
- for (const diff of subGroup) {
14557
- log(` ${formatFilePath(diff.filePath)}`);
14558
- }
14559
- const label = CHANGE_PROPOSAL_TYPE_LABELS[subGroup[0].type] ?? "content changed";
14560
- log(` - ${label}`);
14561
- formatDiffPayload(subGroup[0], log);
14562
- }
14563
- log("");
14999
+ const allGroupedDiffs = Array.from(groupDiffsByArtefact(diffs).values());
15000
+ const checkResult = await packmindCliHexa.checkDiffs(allGroupedDiffs);
15001
+ const submittedItems = checkResult.results.filter((r) => r.exists);
15002
+ const unsubmittedItems = checkResult.results.filter((r) => !r.exists);
15003
+ const diffsToDisplay = includeSubmitted ? diffs : unsubmittedItems.map((r) => r.diff);
15004
+ const submittedLookup = /* @__PURE__ */ new Map();
15005
+ for (const item of checkResult.results) {
15006
+ submittedLookup.set(item.diff, item);
14564
15007
  }
14565
- const changeCount = diffs.length;
14566
- const changeWord = changeCount === 1 ? "change" : "changes";
14567
- const typeSortOrder = {
14568
- command: 0,
14569
- skill: 1,
14570
- standard: 2
14571
- };
14572
- const uniqueArtefacts = /* @__PURE__ */ new Map();
14573
- for (const [key, groupDiffs] of groups) {
14574
- if (!uniqueArtefacts.has(key)) {
14575
- uniqueArtefacts.set(key, {
14576
- type: groupDiffs[0].artifactType,
14577
- name: groupDiffs[0].artifactName
14578
- });
15008
+ if (diffsToDisplay.length === 0) {
15009
+ log("No new changes found.");
15010
+ if (submittedItems.length > 0) {
15011
+ logInfoConsole(buildSubmittedFooter(submittedItems));
14579
15012
  }
15013
+ if (submit) {
15014
+ logInfoConsole("All changes already submitted.");
15015
+ }
15016
+ exit(0);
15017
+ return { diffsFound: 0 };
14580
15018
  }
14581
- const sortedArtefacts = Array.from(uniqueArtefacts.values()).sort(
14582
- (a, b) => typeSortOrder[a.type] - typeSortOrder[b.type] || a.name.localeCompare(b.name, void 0, { sensitivity: "base" })
14583
- );
14584
- const artefactCount = sortedArtefacts.length;
14585
- const artefactWord = artefactCount === 1 ? "artefact" : "artefacts";
14586
- logWarningConsole(
14587
- `Summary: ${changeCount} ${changeWord} found on ${artefactCount} ${artefactWord}:`
14588
- );
14589
- for (const artefact of sortedArtefacts) {
14590
- const typeLabel = ARTIFACT_TYPE_LABELS[artefact.type];
14591
- logWarningConsole(`* ${typeLabel} "${artefact.name}"`);
14592
- }
15019
+ const changeCount = displayDiffs({
15020
+ diffsToDisplay,
15021
+ submittedLookup,
15022
+ includeSubmitted,
15023
+ unsubmittedItems,
15024
+ submittedItems,
15025
+ log
15026
+ });
14593
15027
  if (submit) {
14594
- const groupedDiffs = Array.from(groupDiffsByArtefact(diffs).values());
14595
- const result = await packmindCliHexa.submitDiffs(groupedDiffs);
14596
- for (const err of result.errors) {
14597
- if (err.code === "ChangeProposalPayloadMismatchError") {
14598
- logErrorConsole(
14599
- `Failed to submit "${err.name}": ${err.artifactType ?? "artifact"} is outdated, please run \`packmind-cli install\` to update it`
14600
- );
14601
- } else {
14602
- logErrorConsole(`Failed to submit "${err.name}": ${err.message}`);
14603
- }
14604
- }
14605
- const summaryParts = [];
14606
- if (result.submitted > 0) {
14607
- summaryParts.push(`${result.submitted} submitted`);
14608
- }
14609
- if (result.alreadySubmitted > 0) {
14610
- summaryParts.push(`${result.alreadySubmitted} already submitted`);
14611
- }
14612
- if (result.errors.length > 0) {
14613
- const errorWord = result.errors.length === 1 ? "error" : "errors";
14614
- summaryParts.push(`${result.errors.length} ${errorWord}`);
14615
- }
14616
- if (summaryParts.length > 0) {
14617
- const summaryMessage = `Summary: ${summaryParts.join(", ")}`;
14618
- if (result.errors.length === 0 && result.alreadySubmitted === 0) {
14619
- logSuccessConsole(summaryMessage);
14620
- } else if (result.errors.length > 0 && result.submitted > 0 || result.alreadySubmitted > 0) {
14621
- logWarningConsole(summaryMessage);
14622
- } else {
14623
- logErrorConsole(summaryMessage);
14624
- }
15028
+ const aborted = await handleSubmission({
15029
+ packmindCliHexa,
15030
+ unsubmittedItems,
15031
+ messageFlag,
15032
+ exit
15033
+ });
15034
+ if (aborted) {
15035
+ return { diffsFound: changeCount };
14625
15036
  }
14626
15037
  }
14627
15038
  exit(0);
@@ -14646,9 +15057,19 @@ var diffCommand = (0, import_cmd_ts19.command)({
14646
15057
  submit: (0, import_cmd_ts19.flag)({
14647
15058
  long: "submit",
14648
15059
  description: "Submit detected changes as change proposals"
15060
+ }),
15061
+ includeSubmitted: (0, import_cmd_ts19.flag)({
15062
+ long: "include-submitted",
15063
+ description: "Include already submitted changes in the output"
15064
+ }),
15065
+ message: (0, import_cmd_ts19.option)({
15066
+ long: "message",
15067
+ short: "m",
15068
+ description: "Message describing the intent behind the changes (max 1024 chars)",
15069
+ type: (0, import_cmd_ts19.optional)(import_cmd_ts19.string)
14649
15070
  })
14650
15071
  },
14651
- handler: async ({ submit }) => {
15072
+ handler: async ({ submit, includeSubmitted, message }) => {
14652
15073
  const packmindLogger = new PackmindLogger("PackmindCLI", "info" /* INFO */);
14653
15074
  const packmindCliHexa = new PackmindCliHexa(packmindLogger);
14654
15075
  await diffArtefactsHandler({
@@ -14657,7 +15078,9 @@ var diffCommand = (0, import_cmd_ts19.command)({
14657
15078
  getCwd: () => process.cwd(),
14658
15079
  log: console.log,
14659
15080
  error: console.error,
14660
- submit
15081
+ submit,
15082
+ includeSubmitted,
15083
+ message
14661
15084
  });
14662
15085
  }
14663
15086
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@packmind/cli",
3
- "version": "0.17.0",
3
+ "version": "0.19.0",
4
4
  "description": "A command-line interface for Packmind linting and code quality checks",
5
5
  "private": false,
6
6
  "bin": {