@packmind/cli 0.3.4 → 0.5.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 +2382 -228
  2. package/package.json +4 -1
package/main.cjs CHANGED
@@ -13,6 +13,9 @@ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
13
13
  var __getOwnPropNames = Object.getOwnPropertyNames;
14
14
  var __getProtoOf = Object.getPrototypeOf;
15
15
  var __hasOwnProp = Object.prototype.hasOwnProperty;
16
+ var __commonJS = (cb, mod) => function __require() {
17
+ return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
18
+ };
16
19
  var __copyProps = (to, from, except, desc) => {
17
20
  if (from && typeof from === "object" || typeof from === "function") {
18
21
  for (let key of __getOwnPropNames(from))
@@ -30,6 +33,48 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
30
33
  mod
31
34
  ));
32
35
 
36
+ // apps/cli/package.json
37
+ var require_package = __commonJS({
38
+ "apps/cli/package.json"(exports2, module2) {
39
+ module2.exports = {
40
+ name: "@packmind/cli",
41
+ version: "0.5.0",
42
+ description: "A command-line interface for Packmind linting and code quality checks",
43
+ private: false,
44
+ bin: {
45
+ "packmind-cli": "./main.cjs"
46
+ },
47
+ main: "./main.cjs",
48
+ files: [
49
+ "main.cjs",
50
+ "*.wasm",
51
+ "stubs",
52
+ "CHANGELOG.MD"
53
+ ],
54
+ keywords: [
55
+ "packmind",
56
+ "linting",
57
+ "code-quality",
58
+ "cli",
59
+ "static-analysis"
60
+ ],
61
+ author: "Packmind",
62
+ license: "UNLICENSED",
63
+ repository: {
64
+ type: "git",
65
+ url: "git+https://github.com/PackmindHub/packmind.git",
66
+ directory: "apps/cli"
67
+ },
68
+ publishConfig: {
69
+ access: "public"
70
+ },
71
+ scripts: {
72
+ "packmind-cli": "ts-node --project tsconfig.app.json src/main.ts"
73
+ }
74
+ };
75
+ }
76
+ });
77
+
33
78
  // apps/cli/src/main.ts
34
79
  var import_chalk2 = __toESM(require("chalk"));
35
80
  var import_cmd_ts3 = require("cmd-ts");
@@ -183,6 +228,12 @@ var createRecipesDeploymentId = brandedIdFactory();
183
228
  // packages/types/src/deployments/StandardsDeploymentId.ts
184
229
  var createStandardsDeploymentId = brandedIdFactory();
185
230
 
231
+ // packages/types/src/deployments/PackagesDeploymentId.ts
232
+ var createPackagesDeploymentId = brandedIdFactory();
233
+
234
+ // packages/types/src/deployments/Package.ts
235
+ var createPackageId = brandedIdFactory();
236
+
186
237
  // packages/types/src/git/GitRepoId.ts
187
238
  var createGitRepoId = brandedIdFactory();
188
239
 
@@ -208,38 +259,38 @@ var createRuleId = brandedIdFactory();
208
259
  var createRuleExampleId = brandedIdFactory();
209
260
 
210
261
  // packages/types/src/languages/ProgrammingLanguage.ts
211
- var ProgrammingLanguage = /* @__PURE__ */ ((ProgrammingLanguage3) => {
212
- ProgrammingLanguage3["JAVASCRIPT"] = "JAVASCRIPT";
213
- ProgrammingLanguage3["JAVASCRIPT_JSX"] = "JAVASCRIPT_JSX";
214
- ProgrammingLanguage3["TYPESCRIPT"] = "TYPESCRIPT";
215
- ProgrammingLanguage3["TYPESCRIPT_TSX"] = "TYPESCRIPT_TSX";
216
- ProgrammingLanguage3["PYTHON"] = "PYTHON";
217
- ProgrammingLanguage3["PHP"] = "PHP";
218
- ProgrammingLanguage3["JAVA"] = "JAVA";
219
- ProgrammingLanguage3["SCSS"] = "SCSS";
220
- ProgrammingLanguage3["HTML"] = "HTML";
221
- ProgrammingLanguage3["CSHARP"] = "CSHARP";
222
- ProgrammingLanguage3["GENERIC"] = "GENERIC";
223
- ProgrammingLanguage3["GO"] = "GO";
224
- ProgrammingLanguage3["C"] = "C";
225
- ProgrammingLanguage3["CPP"] = "CPP";
226
- ProgrammingLanguage3["SQL"] = "SQL";
227
- ProgrammingLanguage3["KOTLIN"] = "KOTLIN";
228
- ProgrammingLanguage3["VUE"] = "VUE";
229
- ProgrammingLanguage3["CSS"] = "CSS";
230
- ProgrammingLanguage3["YAML"] = "YAML";
231
- ProgrammingLanguage3["JSON"] = "JSON";
232
- ProgrammingLanguage3["XML"] = "XML";
233
- ProgrammingLanguage3["BASH"] = "BASH";
234
- ProgrammingLanguage3["MARKDOWN"] = "MARKDOWN";
235
- ProgrammingLanguage3["RUBY"] = "RUBY";
236
- ProgrammingLanguage3["RUST"] = "RUST";
237
- ProgrammingLanguage3["SAP_ABAP"] = "SAP_ABAP";
238
- ProgrammingLanguage3["SAP_CDS"] = "SAP_CDS";
239
- ProgrammingLanguage3["SAP_HANA_SQL"] = "SAP_HANA_SQL";
240
- ProgrammingLanguage3["SWIFT"] = "SWIFT";
241
- ProgrammingLanguage3["PROPERTIES"] = "PROPERTIES";
242
- return ProgrammingLanguage3;
262
+ var ProgrammingLanguage = /* @__PURE__ */ ((ProgrammingLanguage4) => {
263
+ ProgrammingLanguage4["JAVASCRIPT"] = "JAVASCRIPT";
264
+ ProgrammingLanguage4["JAVASCRIPT_JSX"] = "JAVASCRIPT_JSX";
265
+ ProgrammingLanguage4["TYPESCRIPT"] = "TYPESCRIPT";
266
+ ProgrammingLanguage4["TYPESCRIPT_TSX"] = "TYPESCRIPT_TSX";
267
+ ProgrammingLanguage4["PYTHON"] = "PYTHON";
268
+ ProgrammingLanguage4["PHP"] = "PHP";
269
+ ProgrammingLanguage4["JAVA"] = "JAVA";
270
+ ProgrammingLanguage4["SCSS"] = "SCSS";
271
+ ProgrammingLanguage4["HTML"] = "HTML";
272
+ ProgrammingLanguage4["CSHARP"] = "CSHARP";
273
+ ProgrammingLanguage4["GENERIC"] = "GENERIC";
274
+ ProgrammingLanguage4["GO"] = "GO";
275
+ ProgrammingLanguage4["C"] = "C";
276
+ ProgrammingLanguage4["CPP"] = "CPP";
277
+ ProgrammingLanguage4["SQL"] = "SQL";
278
+ ProgrammingLanguage4["KOTLIN"] = "KOTLIN";
279
+ ProgrammingLanguage4["VUE"] = "VUE";
280
+ ProgrammingLanguage4["CSS"] = "CSS";
281
+ ProgrammingLanguage4["YAML"] = "YAML";
282
+ ProgrammingLanguage4["JSON"] = "JSON";
283
+ ProgrammingLanguage4["XML"] = "XML";
284
+ ProgrammingLanguage4["BASH"] = "BASH";
285
+ ProgrammingLanguage4["MARKDOWN"] = "MARKDOWN";
286
+ ProgrammingLanguage4["RUBY"] = "RUBY";
287
+ ProgrammingLanguage4["RUST"] = "RUST";
288
+ ProgrammingLanguage4["SAP_ABAP"] = "SAP_ABAP";
289
+ ProgrammingLanguage4["SAP_CDS"] = "SAP_CDS";
290
+ ProgrammingLanguage4["SAP_HANA_SQL"] = "SAP_HANA_SQL";
291
+ ProgrammingLanguage4["SWIFT"] = "SWIFT";
292
+ ProgrammingLanguage4["PROPERTIES"] = "PROPERTIES";
293
+ return ProgrammingLanguage4;
243
294
  })(ProgrammingLanguage || {});
244
295
  var ProgrammingLanguageDetails = {
245
296
  ["GENERIC" /* GENERIC */]: {
@@ -407,6 +458,304 @@ var createDetectionProgramId = brandedIdFactory();
407
458
  // packages/types/src/linter/RuleDetectionAssessment.ts
408
459
  var createRuleDetectionAssessmentId = brandedIdFactory();
409
460
 
461
+ // packages/types/src/llm/LLMProviderMetadata.ts
462
+ var DEFAULT_OPENAI_MODELS = {
463
+ model: "gpt-5.1",
464
+ fastestModel: "gpt-4.1-mini"
465
+ };
466
+ var DEFAULT_ANTHROPIC_MODELS = {
467
+ model: "claude-sonnet-4-5-20250929",
468
+ fastestModel: "claude-haiku-4-5-20251001"
469
+ };
470
+ var DEFAULT_GEMINI_MODELS = {
471
+ model: "gemini-3-pro-preview",
472
+ fastestModel: "gemini-2.5-flash"
473
+ };
474
+ var DEFAULT_AZURE_OPENAI_API_VERSION = "2024-12-01-preview";
475
+ var LLM_PROVIDER_METADATA = {
476
+ ["openai" /* OPENAI */]: {
477
+ id: "openai" /* OPENAI */,
478
+ displayName: "OpenAI",
479
+ description: "OpenAI GPT models including GPT-4 and GPT-5. Requires an API key from OpenAI.",
480
+ defaultModel: DEFAULT_OPENAI_MODELS.model,
481
+ defaultFastModel: DEFAULT_OPENAI_MODELS.fastestModel,
482
+ documentationUrl: "https://platform.openai.com/docs",
483
+ fields: [
484
+ {
485
+ name: "apiKey",
486
+ label: "API Key",
487
+ type: "password",
488
+ defaultValue: "",
489
+ helpMessage: "Your OpenAI API key from platform.openai.com.",
490
+ optional: false,
491
+ placeholder: "sk-...",
492
+ secret: true
493
+ },
494
+ {
495
+ name: "model",
496
+ label: "Model",
497
+ type: "text",
498
+ defaultValue: DEFAULT_OPENAI_MODELS.model,
499
+ helpMessage: "The primary model to use for standard operations. Defaults to the latest recommended model.",
500
+ optional: true,
501
+ placeholder: "gpt-5.1",
502
+ secret: false
503
+ },
504
+ {
505
+ name: "fastestModel",
506
+ label: "Fast Model",
507
+ type: "text",
508
+ defaultValue: DEFAULT_OPENAI_MODELS.fastestModel,
509
+ helpMessage: "A faster, more economical model for less complex operations.",
510
+ optional: true,
511
+ placeholder: "gpt-4.1-mini",
512
+ secret: false
513
+ }
514
+ ]
515
+ },
516
+ ["anthropic" /* ANTHROPIC */]: {
517
+ id: "anthropic" /* ANTHROPIC */,
518
+ displayName: "Anthropic Claude",
519
+ description: "Anthropic Claude models known for safety and helpfulness. Requires an API key from Anthropic.",
520
+ defaultModel: DEFAULT_ANTHROPIC_MODELS.model,
521
+ defaultFastModel: DEFAULT_ANTHROPIC_MODELS.fastestModel,
522
+ documentationUrl: "https://docs.anthropic.com",
523
+ fields: [
524
+ {
525
+ name: "apiKey",
526
+ label: "API Key",
527
+ type: "password",
528
+ defaultValue: "",
529
+ helpMessage: "Your Anthropic API key from console.anthropic.com.",
530
+ optional: false,
531
+ placeholder: "sk-ant-...",
532
+ secret: true
533
+ },
534
+ {
535
+ name: "model",
536
+ label: "Model",
537
+ type: "text",
538
+ defaultValue: DEFAULT_ANTHROPIC_MODELS.model,
539
+ helpMessage: "The primary Claude model to use. Defaults to the latest Sonnet model.",
540
+ optional: true,
541
+ placeholder: "claude-sonnet-4-5-20250929",
542
+ secret: false
543
+ },
544
+ {
545
+ name: "fastestModel",
546
+ label: "Fast Model",
547
+ type: "text",
548
+ defaultValue: DEFAULT_ANTHROPIC_MODELS.fastestModel,
549
+ helpMessage: "A faster Claude model for less complex operations. Defaults to Haiku.",
550
+ optional: true,
551
+ placeholder: "claude-haiku-4-5-20251001",
552
+ secret: false
553
+ }
554
+ ]
555
+ },
556
+ ["gemini" /* GEMINI */]: {
557
+ id: "gemini" /* GEMINI */,
558
+ displayName: "Google Gemini",
559
+ description: "Google's Gemini models with multimodal capabilities. Requires an API key from Google AI Studio.",
560
+ defaultModel: DEFAULT_GEMINI_MODELS.model,
561
+ defaultFastModel: DEFAULT_GEMINI_MODELS.fastestModel,
562
+ documentationUrl: "https://ai.google.dev/docs",
563
+ fields: [
564
+ {
565
+ name: "apiKey",
566
+ label: "API Key",
567
+ type: "password",
568
+ defaultValue: "",
569
+ helpMessage: "Your Google AI API key from aistudio.google.com.",
570
+ optional: false,
571
+ placeholder: "AIza...",
572
+ secret: true
573
+ },
574
+ {
575
+ name: "model",
576
+ label: "Model",
577
+ type: "text",
578
+ defaultValue: DEFAULT_GEMINI_MODELS.model,
579
+ helpMessage: "The primary Gemini model to use. Defaults to the latest Pro model.",
580
+ optional: true,
581
+ placeholder: "gemini-3-pro-preview",
582
+ secret: false
583
+ },
584
+ {
585
+ name: "fastestModel",
586
+ label: "Fast Model",
587
+ type: "text",
588
+ defaultValue: DEFAULT_GEMINI_MODELS.fastestModel,
589
+ helpMessage: "A faster Gemini model for less complex operations. Defaults to Flash.",
590
+ optional: true,
591
+ placeholder: "gemini-2.5-flash",
592
+ secret: false
593
+ }
594
+ ]
595
+ },
596
+ ["azure-openai" /* AZURE_OPENAI */]: {
597
+ id: "azure-openai" /* AZURE_OPENAI */,
598
+ displayName: "Azure OpenAI",
599
+ description: "Microsoft Azure-hosted OpenAI models. Requires Azure deployment names and credentials.",
600
+ defaultModel: "",
601
+ defaultFastModel: "",
602
+ documentationUrl: "https://learn.microsoft.com/en-us/azure/ai-services/openai/",
603
+ fields: [
604
+ {
605
+ name: "model",
606
+ label: "Model Deployment Name",
607
+ type: "text",
608
+ defaultValue: "",
609
+ helpMessage: "The Azure deployment name for the primary model. This is the name you gave your deployment in Azure Portal.",
610
+ optional: false,
611
+ placeholder: "my-gpt-4-deployment",
612
+ secret: false
613
+ },
614
+ {
615
+ name: "fastestModel",
616
+ label: "Fast Model Deployment Name",
617
+ type: "text",
618
+ defaultValue: "",
619
+ helpMessage: "The Azure deployment name for the fast/economical model.",
620
+ optional: false,
621
+ placeholder: "my-gpt-35-turbo-deployment",
622
+ secret: false
623
+ },
624
+ {
625
+ name: "endpoint",
626
+ label: "Endpoint URL",
627
+ type: "url",
628
+ defaultValue: "",
629
+ helpMessage: "Your Azure OpenAI endpoint URL.",
630
+ optional: false,
631
+ placeholder: "https://your-resource.openai.azure.com",
632
+ secret: false
633
+ },
634
+ {
635
+ name: "apiKey",
636
+ label: "API Key",
637
+ type: "password",
638
+ defaultValue: "",
639
+ helpMessage: "Your Azure OpenAI API key.",
640
+ optional: false,
641
+ placeholder: "",
642
+ secret: true
643
+ },
644
+ {
645
+ name: "apiVersion",
646
+ label: "API Version",
647
+ type: "text",
648
+ defaultValue: DEFAULT_AZURE_OPENAI_API_VERSION,
649
+ helpMessage: "The Azure OpenAI API version to use. Defaults to the latest stable version.",
650
+ optional: true,
651
+ placeholder: "2024-12-01-preview",
652
+ secret: false
653
+ }
654
+ ]
655
+ },
656
+ ["openai-compatible" /* OPENAI_COMPATIBLE */]: {
657
+ id: "openai-compatible" /* OPENAI_COMPATIBLE */,
658
+ displayName: "OpenAI-Compatible",
659
+ description: "Any OpenAI-compatible API endpoint. Use this for local models (Ollama, LM Studio) or other compatible providers.",
660
+ defaultModel: "",
661
+ defaultFastModel: "",
662
+ documentationUrl: void 0,
663
+ fields: [
664
+ {
665
+ name: "llmEndpoint",
666
+ label: "Endpoint URL",
667
+ type: "url",
668
+ defaultValue: "",
669
+ helpMessage: "The base URL of the OpenAI-compatible API endpoint (e.g., http://localhost:11434/v1 for Ollama).",
670
+ optional: false,
671
+ placeholder: "http://localhost:11434/v1",
672
+ secret: false
673
+ },
674
+ {
675
+ name: "llmApiKey",
676
+ label: "API Key",
677
+ type: "password",
678
+ defaultValue: "",
679
+ helpMessage: "API key for authentication. Some local providers may not require this.",
680
+ optional: false,
681
+ placeholder: "",
682
+ secret: true
683
+ },
684
+ {
685
+ name: "model",
686
+ label: "Model",
687
+ type: "text",
688
+ defaultValue: "",
689
+ helpMessage: "The model identifier to use for standard operations (e.g., llama3, mistral).",
690
+ optional: false,
691
+ placeholder: "llama3",
692
+ secret: false
693
+ },
694
+ {
695
+ name: "fastestModel",
696
+ label: "Fast Model",
697
+ type: "text",
698
+ defaultValue: "",
699
+ helpMessage: "A faster model for less complex operations. Can be the same as the primary model.",
700
+ optional: false,
701
+ placeholder: "llama3",
702
+ secret: false
703
+ }
704
+ ]
705
+ },
706
+ ["packmind" /* PACKMIND */]: {
707
+ id: "packmind" /* PACKMIND */,
708
+ displayName: "Packmind (SaaS)",
709
+ description: "Packmind managed LLM service. Uses the platform default provider configuration.",
710
+ defaultModel: DEFAULT_OPENAI_MODELS.model,
711
+ defaultFastModel: DEFAULT_OPENAI_MODELS.fastestModel,
712
+ documentationUrl: void 0,
713
+ fields: []
714
+ }
715
+ };
716
+
717
+ // packages/types/src/sse/SSEEvent.ts
718
+ function createProgramStatusChangeEvent(ruleId, language) {
719
+ return {
720
+ type: "PROGRAM_STATUS_CHANGE",
721
+ data: { ruleId, language },
722
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
723
+ };
724
+ }
725
+ function createAssessmentStatusChangeEvent(ruleId, language) {
726
+ return {
727
+ type: "ASSESSMENT_STATUS_CHANGE",
728
+ data: {
729
+ ruleId,
730
+ language
731
+ },
732
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
733
+ };
734
+ }
735
+ function createDetectionHeuristicsUpdatedEvent(ruleId, language, detectionHeuristicsId) {
736
+ return {
737
+ type: "DETECTION_HEURISTICS_UPDATED",
738
+ data: {
739
+ ruleId,
740
+ language,
741
+ detectionHeuristicsId
742
+ },
743
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
744
+ };
745
+ }
746
+ function createUserContextChangeEvent(userId, organizationId, changeType, role) {
747
+ return {
748
+ type: "USER_CONTEXT_CHANGE",
749
+ data: {
750
+ userId,
751
+ organizationId,
752
+ changeType,
753
+ role
754
+ },
755
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
756
+ };
757
+ }
758
+
410
759
  // apps/cli/src/application/useCases/ExecuteSingleFileAstUseCase.ts
411
760
  var ExecuteSingleFileAstUseCase = class _ExecuteSingleFileAstUseCase {
412
761
  constructor(linterExecutionUseCase) {
@@ -447,30 +796,37 @@ var import_util = require("util");
447
796
  var execAsync = (0, import_util.promisify)(import_child_process.exec);
448
797
  var origin = "GitService";
449
798
  var GitService = class {
450
- constructor(logger = new PackmindLogger(origin)) {
451
- this.logger = logger;
799
+ constructor(logger2 = new PackmindLogger(origin)) {
800
+ this.logger = logger2;
452
801
  }
453
- async getGitRepositoryRoot(path5) {
802
+ async getGitRepositoryRoot(path7) {
454
803
  try {
455
804
  const { stdout } = await execAsync("git rev-parse --show-toplevel", {
456
- cwd: path5
805
+ cwd: path7
457
806
  });
458
807
  const gitRoot = stdout.trim();
459
808
  this.logger.debug("Resolved git repository root", {
460
- inputPath: path5,
809
+ inputPath: path7,
461
810
  gitRoot
462
811
  });
463
812
  return gitRoot;
464
813
  } catch (error) {
465
814
  if (error instanceof Error) {
466
815
  throw new Error(
467
- `Failed to get Git repository root. The path '${path5}' does not appear to be inside a Git repository.
816
+ `Failed to get Git repository root. The path '${path7}' does not appear to be inside a Git repository.
468
817
  ${error.message}`
469
818
  );
470
819
  }
471
820
  throw new Error("Failed to get Git repository root: Unknown error");
472
821
  }
473
822
  }
823
+ async tryGetGitRepositoryRoot(path7) {
824
+ try {
825
+ return await this.getGitRepositoryRoot(path7);
826
+ } catch {
827
+ return null;
828
+ }
829
+ }
474
830
  async getCurrentBranches(repoPath) {
475
831
  try {
476
832
  const { stdout } = await execAsync("git branch -a --contains HEAD", {
@@ -488,7 +844,7 @@ ${error.message}`
488
844
  throw new Error("Failed to get Git branches: Unknown error");
489
845
  }
490
846
  }
491
- async getGitRemoteUrl(repoPath, origin5) {
847
+ async getGitRemoteUrl(repoPath, origin11) {
492
848
  try {
493
849
  const { stdout } = await execAsync("git remote -v", {
494
850
  cwd: repoPath
@@ -498,10 +854,10 @@ ${error.message}`
498
854
  throw new Error("No Git remotes found in the repository");
499
855
  }
500
856
  let selectedRemote;
501
- if (origin5) {
502
- const foundRemote = remotes.find((remote) => remote.name === origin5);
857
+ if (origin11) {
858
+ const foundRemote = remotes.find((remote) => remote.name === origin11);
503
859
  if (!foundRemote) {
504
- throw new Error(`Remote '${origin5}' not found in repository`);
860
+ throw new Error(`Remote '${origin11}' not found in repository`);
505
861
  }
506
862
  selectedRemote = foundRemote.url;
507
863
  } else if (remotes.length === 1) {
@@ -571,14 +927,14 @@ ${error.message}`
571
927
  normalizeGitUrl(url) {
572
928
  const sshMatch = url.match(/^git@([^:]+):(.+)$/);
573
929
  if (sshMatch) {
574
- const [, host, path5] = sshMatch;
575
- const cleanPath = path5.replace(/\.git$/, "");
930
+ const [, host, path7] = sshMatch;
931
+ const cleanPath = path7.replace(/\.git$/, "");
576
932
  return `${host}/${cleanPath}`;
577
933
  }
578
934
  const httpsMatch = url.match(/^https?:\/\/([^/]+)\/(.+)$/);
579
935
  if (httpsMatch) {
580
- const [, host, path5] = httpsMatch;
581
- const cleanPath = path5.replace(/\.git$/, "");
936
+ const [, host, path7] = httpsMatch;
937
+ const cleanPath = path7.replace(/\.git$/, "");
582
938
  return `${host}/${cleanPath}`;
583
939
  }
584
940
  return url;
@@ -591,8 +947,8 @@ var GetGitRemoteUrlUseCase = class {
591
947
  this.gitRemoteUrlService = gitRemoteUrlService;
592
948
  }
593
949
  async execute(command3) {
594
- const { path: repoPath, origin: origin5 } = command3;
595
- return this.gitRemoteUrlService.getGitRemoteUrl(repoPath, origin5);
950
+ const { path: repoPath, origin: origin11 } = command3;
951
+ return this.gitRemoteUrlService.getGitRemoteUrl(repoPath, origin11);
596
952
  }
597
953
  };
598
954
 
@@ -720,10 +1076,10 @@ var path2 = __toESM(require("path"));
720
1076
  var fs2 = __toESM(require("fs/promises"));
721
1077
  var origin2 = "LintFilesInDirectoryUseCase";
722
1078
  var LintFilesInDirectoryUseCase = class {
723
- constructor(services, repositories, logger = new PackmindLogger(origin2)) {
1079
+ constructor(services, repositories, logger2 = new PackmindLogger(origin2)) {
724
1080
  this.services = services;
725
1081
  this.repositories = repositories;
726
- this.logger = logger;
1082
+ this.logger = logger2;
727
1083
  }
728
1084
  fileMatchesScope(filePath, scopePatterns) {
729
1085
  if (!scopePatterns || scopePatterns.length === 0) {
@@ -1057,6 +1413,224 @@ var LintFilesInDirectoryUseCase = class {
1057
1413
  }
1058
1414
  };
1059
1415
 
1416
+ // apps/cli/src/application/useCases/LintFilesLocallyUseCase.ts
1417
+ var import_minimatch2 = require("minimatch");
1418
+ var path3 = __toESM(require("path"));
1419
+ var fs3 = __toESM(require("fs/promises"));
1420
+ var origin3 = "LintFilesLocallyUseCase";
1421
+ var LintFilesLocallyUseCase = class {
1422
+ constructor(services, repositories, logger2 = new PackmindLogger(origin3)) {
1423
+ this.services = services;
1424
+ this.repositories = repositories;
1425
+ this.logger = logger2;
1426
+ }
1427
+ fileMatchesTargetAndScope(filePath, targetPath, scopePatterns) {
1428
+ if (!scopePatterns || scopePatterns.length === 0) {
1429
+ const effectivePattern = this.buildEffectivePattern(targetPath, null);
1430
+ return (0, import_minimatch2.minimatch)(filePath, effectivePattern, { matchBase: false });
1431
+ }
1432
+ return scopePatterns.some((scopePattern) => {
1433
+ const effectivePattern = this.buildEffectivePattern(
1434
+ targetPath,
1435
+ scopePattern
1436
+ );
1437
+ return (0, import_minimatch2.minimatch)(filePath, effectivePattern, { matchBase: false });
1438
+ });
1439
+ }
1440
+ buildEffectivePattern(targetPath, scope) {
1441
+ const normalizedTarget = targetPath === "/" ? "/" : targetPath.replace(/\/$/, "");
1442
+ if (!scope) {
1443
+ return normalizedTarget === "/" ? "/**" : normalizedTarget + "/**";
1444
+ }
1445
+ if (scope.startsWith(normalizedTarget + "/") || scope === normalizedTarget) {
1446
+ return scope.endsWith("/") ? scope + "**" : scope;
1447
+ }
1448
+ const cleanScope = scope.startsWith("/") ? scope.substring(1) : scope;
1449
+ let pattern;
1450
+ if (normalizedTarget === "/") {
1451
+ pattern = "/" + cleanScope;
1452
+ } else {
1453
+ pattern = normalizedTarget + "/" + cleanScope;
1454
+ }
1455
+ if (pattern.endsWith("/")) {
1456
+ pattern = pattern + "**";
1457
+ }
1458
+ return pattern;
1459
+ }
1460
+ async execute(command3) {
1461
+ const { path: userPath } = command3;
1462
+ this.logger.debug(`Starting local linting: path="${userPath}"`);
1463
+ const absoluteUserPath = path3.isAbsolute(userPath) ? userPath : path3.resolve(process.cwd(), userPath);
1464
+ let pathStats;
1465
+ try {
1466
+ pathStats = await fs3.stat(absoluteUserPath);
1467
+ } catch {
1468
+ throw new Error(
1469
+ `The path "${absoluteUserPath}" does not exist or cannot be accessed`
1470
+ );
1471
+ }
1472
+ const isFile = pathStats.isFile();
1473
+ const directoryForConfig = isFile ? path3.dirname(absoluteUserPath) : absoluteUserPath;
1474
+ const gitRepoRoot = await this.services.gitRemoteUrlService.tryGetGitRepositoryRoot(
1475
+ directoryForConfig
1476
+ );
1477
+ const hierarchicalConfig = await this.repositories.configFileRepository.readHierarchicalConfig(
1478
+ directoryForConfig,
1479
+ gitRepoRoot
1480
+ );
1481
+ if (!hierarchicalConfig.hasConfigs) {
1482
+ const boundary = gitRepoRoot ?? "filesystem root";
1483
+ throw new Error(
1484
+ `No packmind.json found between ${directoryForConfig} and ${boundary}. Cannot use local linting.`
1485
+ );
1486
+ }
1487
+ const basePath = gitRepoRoot ?? directoryForConfig;
1488
+ this.logger.debug(
1489
+ `Found ${hierarchicalConfig.configPaths.length} packmind.json file(s)`
1490
+ );
1491
+ for (const configPath of hierarchicalConfig.configPaths) {
1492
+ this.logger.debug(`Using config: ${configPath}`);
1493
+ }
1494
+ const packageSlugs = Object.keys(hierarchicalConfig.packages);
1495
+ this.logger.debug(
1496
+ `Merged ${packageSlugs.length} packages from configuration files`
1497
+ );
1498
+ const detectionPrograms = await this.repositories.packmindGateway.getDetectionProgramsForPackages({
1499
+ packagesSlugs: packageSlugs
1500
+ });
1501
+ this.logger.debug(
1502
+ `Retrieved detection programs: targetsCount=${detectionPrograms.targets.length}`
1503
+ );
1504
+ const files = isFile ? [{ path: absoluteUserPath }] : await this.services.listFiles.listFilesInDirectory(
1505
+ absoluteUserPath,
1506
+ [],
1507
+ ["node_modules", "dist", ".min.", ".map.", ".git"]
1508
+ );
1509
+ this.logger.debug(`Found ${files.length} files to lint`);
1510
+ const violations = [];
1511
+ for (const file of files) {
1512
+ const fileViolations = [];
1513
+ const relativeFilePath = file.path.startsWith(basePath) ? file.path.substring(basePath.length) : file.path;
1514
+ const normalizedFilePath = relativeFilePath.startsWith("/") ? relativeFilePath : "/" + relativeFilePath;
1515
+ this.logger.debug(
1516
+ `Processing file: absolute="${file.path}", relative="${normalizedFilePath}"`
1517
+ );
1518
+ const fileExtension = this.extractExtensionFromFile(file.path);
1519
+ const fileLanguage = this.resolveProgrammingLanguage(fileExtension);
1520
+ if (!fileLanguage) {
1521
+ continue;
1522
+ }
1523
+ const programsByLanguage = /* @__PURE__ */ new Map();
1524
+ for (const target of detectionPrograms.targets) {
1525
+ for (const standard of target.standards) {
1526
+ if (!this.fileMatchesTargetAndScope(
1527
+ normalizedFilePath,
1528
+ target.path,
1529
+ standard.scope
1530
+ )) {
1531
+ continue;
1532
+ }
1533
+ for (const rule of standard.rules) {
1534
+ for (const activeProgram of rule.activeDetectionPrograms) {
1535
+ try {
1536
+ const programLanguage = this.resolveProgrammingLanguage(
1537
+ activeProgram.language
1538
+ );
1539
+ if (!programLanguage || programLanguage !== fileLanguage) {
1540
+ continue;
1541
+ }
1542
+ const programsForLanguage = programsByLanguage.get(programLanguage) ?? [];
1543
+ programsForLanguage.push({
1544
+ code: activeProgram.detectionProgram.code,
1545
+ ruleContent: rule.content,
1546
+ standardSlug: standard.slug,
1547
+ sourceCodeState: activeProgram.detectionProgram.sourceCodeState,
1548
+ language: fileLanguage
1549
+ });
1550
+ programsByLanguage.set(programLanguage, programsForLanguage);
1551
+ } catch (error) {
1552
+ console.error(
1553
+ `Error preparing program for file ${file.path}: ${error}`
1554
+ );
1555
+ }
1556
+ }
1557
+ }
1558
+ }
1559
+ }
1560
+ if (programsByLanguage.size > 0) {
1561
+ try {
1562
+ const fileContent = await this.services.listFiles.readFileContent(
1563
+ file.path
1564
+ );
1565
+ for (const [language, programs] of programsByLanguage.entries()) {
1566
+ try {
1567
+ const result = await this.executeProgramsForFile({
1568
+ filePath: file.path,
1569
+ fileContent,
1570
+ language,
1571
+ programs
1572
+ });
1573
+ fileViolations.push(...result);
1574
+ } catch (error) {
1575
+ console.error(
1576
+ `Error executing programs for file ${file.path} (${language}): ${error}`
1577
+ );
1578
+ }
1579
+ }
1580
+ } catch (error) {
1581
+ console.error(
1582
+ `Error reading file content for ${file.path}: ${error}`
1583
+ );
1584
+ }
1585
+ }
1586
+ if (fileViolations.length > 0) {
1587
+ violations.push({
1588
+ file: file.path,
1589
+ violations: fileViolations
1590
+ });
1591
+ }
1592
+ }
1593
+ const totalViolations = violations.reduce(
1594
+ (sum, violation) => sum + violation.violations.length,
1595
+ 0
1596
+ );
1597
+ const standardsChecked = Array.from(
1598
+ new Set(
1599
+ detectionPrograms.targets.flatMap(
1600
+ (target) => target.standards.map((standard) => standard.slug)
1601
+ )
1602
+ )
1603
+ );
1604
+ return {
1605
+ violations,
1606
+ summary: {
1607
+ totalFiles: files.length,
1608
+ violatedFiles: violations.length,
1609
+ totalViolations,
1610
+ standardsChecked
1611
+ }
1612
+ };
1613
+ }
1614
+ resolveProgrammingLanguage(language) {
1615
+ try {
1616
+ return stringToProgrammingLanguage(language);
1617
+ } catch {
1618
+ return null;
1619
+ }
1620
+ }
1621
+ async executeProgramsForFile(command3) {
1622
+ const result = await this.services.linterExecutionUseCase.execute(command3);
1623
+ return result.violations;
1624
+ }
1625
+ extractExtensionFromFile(filePath) {
1626
+ const lastDotIndex = filePath.lastIndexOf(".");
1627
+ if (lastDotIndex === -1 || lastDotIndex === filePath.length - 1) {
1628
+ return "";
1629
+ }
1630
+ return filePath.substring(lastDotIndex + 1);
1631
+ }
1632
+ };
1633
+
1060
1634
  // apps/cli/src/infra/repositories/PackmindGateway.ts
1061
1635
  function decodeJwt(jwt) {
1062
1636
  try {
@@ -1113,7 +1687,7 @@ function decodeApiKey(apiKey) {
1113
1687
  var PackmindGateway = class {
1114
1688
  constructor(apiKey) {
1115
1689
  this.apiKey = apiKey;
1116
- this.getPullData = async () => {
1690
+ this.getPullData = async (command3) => {
1117
1691
  const decodedApiKey = decodeApiKey(this.apiKey);
1118
1692
  if (!decodedApiKey.isValid) {
1119
1693
  throw new Error(`Invalid API key: ${decodedApiKey.error}`);
@@ -1124,7 +1698,13 @@ var PackmindGateway = class {
1124
1698
  throw new Error("Invalid JWT: missing organizationId");
1125
1699
  }
1126
1700
  const organizationId = jwtPayload.organization.id;
1127
- const url = `${host}/api/v0/organizations/${organizationId}/pull`;
1701
+ const queryParams = new URLSearchParams();
1702
+ if (command3.packagesSlugs && command3.packagesSlugs.length > 0) {
1703
+ command3.packagesSlugs.forEach((slug) => {
1704
+ queryParams.append("packageSlug", slug);
1705
+ });
1706
+ }
1707
+ const url = `${host}/api/v0/organizations/${organizationId}/pull?${queryParams.toString()}`;
1128
1708
  try {
1129
1709
  const response = await fetch(url, {
1130
1710
  method: "GET",
@@ -1142,7 +1722,9 @@ var PackmindGateway = class {
1142
1722
  }
1143
1723
  } catch {
1144
1724
  }
1145
- throw new Error(errorMsg);
1725
+ const error = new Error(errorMsg);
1726
+ error.statusCode = response.status;
1727
+ throw error;
1146
1728
  }
1147
1729
  const result = await response.json();
1148
1730
  return result;
@@ -1155,29 +1737,29 @@ var PackmindGateway = class {
1155
1737
  );
1156
1738
  }
1157
1739
  throw new Error(
1158
- `Failed to pull content: Error: ${err?.message || JSON.stringify(error)}`
1740
+ `Failed to fetch content: Error: ${err?.message || JSON.stringify(error)}`
1159
1741
  );
1160
1742
  }
1161
1743
  };
1162
- this.listExecutionPrograms = async (params) => {
1744
+ this.listPackages = async () => {
1163
1745
  const decodedApiKey = decodeApiKey(this.apiKey);
1164
1746
  if (!decodedApiKey.isValid) {
1165
1747
  throw new Error(`Invalid API key: ${decodedApiKey.error}`);
1166
1748
  }
1167
- const { host } = decodedApiKey.payload;
1168
- const url = `${host}/api/v0/list-detection-program`;
1169
- const payload = {
1170
- gitRemoteUrl: params.gitRemoteUrl,
1171
- branches: params.branches
1172
- };
1749
+ const { host, jwt } = decodedApiKey.payload;
1750
+ const jwtPayload = decodeJwt(jwt);
1751
+ if (!jwtPayload?.organization?.id) {
1752
+ throw new Error("Invalid JWT: missing organizationId");
1753
+ }
1754
+ const organizationId = jwtPayload.organization.id;
1755
+ const url = `${host}/api/v0/organizations/${organizationId}/packages`;
1173
1756
  try {
1174
1757
  const response = await fetch(url, {
1175
- method: "POST",
1758
+ method: "GET",
1176
1759
  headers: {
1177
1760
  "Content-Type": "application/json",
1178
1761
  Authorization: `Bearer ${this.apiKey}`
1179
- },
1180
- body: JSON.stringify(payload)
1762
+ }
1181
1763
  });
1182
1764
  if (!response.ok) {
1183
1765
  let errorMsg = `API request failed: ${response.status} ${response.statusText}`;
@@ -1188,10 +1770,12 @@ var PackmindGateway = class {
1188
1770
  }
1189
1771
  } catch {
1190
1772
  }
1191
- throw new Error(errorMsg);
1773
+ const error = new Error(errorMsg);
1774
+ error.statusCode = response.status;
1775
+ throw error;
1192
1776
  }
1193
1777
  const result = await response.json();
1194
- return result;
1778
+ return result.packages;
1195
1779
  } catch (error) {
1196
1780
  const err = error;
1197
1781
  const code = err?.code || err?.cause?.code;
@@ -1201,22 +1785,68 @@ var PackmindGateway = class {
1201
1785
  );
1202
1786
  }
1203
1787
  throw new Error(
1204
- `Failed to fetch detection programs: Error: ${err?.message || JSON.stringify(error)}`
1788
+ `Failed to list packages: Error: ${err?.message || JSON.stringify(error)}`
1205
1789
  );
1206
1790
  }
1207
1791
  };
1208
- this.getDraftDetectionProgramsForRule = async (params) => {
1792
+ this.listExecutionPrograms = async (params) => {
1209
1793
  const decodedApiKey = decodeApiKey(this.apiKey);
1210
1794
  if (!decodedApiKey.isValid) {
1211
1795
  throw new Error(`Invalid API key: ${decodedApiKey.error}`);
1212
1796
  }
1213
1797
  const { host } = decodedApiKey.payload;
1214
- const url = `${host}/api/v0/list-draft-detection-program`;
1798
+ const url = `${host}/api/v0/list-detection-program`;
1215
1799
  const payload = {
1216
- standardSlug: params.standardSlug,
1217
- ruleId: params.ruleId
1800
+ gitRemoteUrl: params.gitRemoteUrl,
1801
+ branches: params.branches
1218
1802
  };
1219
- if (params.language) {
1803
+ try {
1804
+ const response = await fetch(url, {
1805
+ method: "POST",
1806
+ headers: {
1807
+ "Content-Type": "application/json",
1808
+ Authorization: `Bearer ${this.apiKey}`
1809
+ },
1810
+ body: JSON.stringify(payload)
1811
+ });
1812
+ if (!response.ok) {
1813
+ let errorMsg = `API request failed: ${response.status} ${response.statusText}`;
1814
+ try {
1815
+ const errorBody = await response.json();
1816
+ if (errorBody && errorBody.message) {
1817
+ errorMsg = `${errorBody.message}`;
1818
+ }
1819
+ } catch {
1820
+ }
1821
+ throw new Error(errorMsg);
1822
+ }
1823
+ const result = await response.json();
1824
+ return result;
1825
+ } catch (error) {
1826
+ const err = error;
1827
+ const code = err?.code || err?.cause?.code;
1828
+ if (code === "ECONNREFUSED" || code === "ENOTFOUND" || err?.name === "FetchError" || typeof err?.message === "string" && (err.message.includes("Failed to fetch") || err.message.includes("network") || err.message.includes("NetworkError"))) {
1829
+ throw new Error(
1830
+ `Packmind server is not accessible at ${host}. Please check your network connection or the server URL.`
1831
+ );
1832
+ }
1833
+ throw new Error(
1834
+ `Failed to fetch detection programs: Error: ${err?.message || JSON.stringify(error)}`
1835
+ );
1836
+ }
1837
+ };
1838
+ this.getDraftDetectionProgramsForRule = async (params) => {
1839
+ const decodedApiKey = decodeApiKey(this.apiKey);
1840
+ if (!decodedApiKey.isValid) {
1841
+ throw new Error(`Invalid API key: ${decodedApiKey.error}`);
1842
+ }
1843
+ const { host } = decodedApiKey.payload;
1844
+ const url = `${host}/api/v0/list-draft-detection-program`;
1845
+ const payload = {
1846
+ standardSlug: params.standardSlug,
1847
+ ruleId: params.ruleId
1848
+ };
1849
+ if (params.language) {
1220
1850
  payload.language = params.language;
1221
1851
  }
1222
1852
  try {
@@ -1337,6 +1967,101 @@ var PackmindGateway = class {
1337
1967
  );
1338
1968
  }
1339
1969
  };
1970
+ this.getPackageSummary = async ({
1971
+ slug
1972
+ }) => {
1973
+ const decodedApiKey = decodeApiKey(this.apiKey);
1974
+ if (!decodedApiKey.isValid) {
1975
+ throw new Error(`Invalid API key: ${decodedApiKey.error}`);
1976
+ }
1977
+ const { host, jwt } = decodedApiKey.payload;
1978
+ const jwtPayload = decodeJwt(jwt);
1979
+ if (!jwtPayload?.organization?.id) {
1980
+ throw new Error("Invalid JWT: missing organizationId");
1981
+ }
1982
+ const organizationId = jwtPayload.organization.id;
1983
+ const url = `${host}/api/v0/organizations/${organizationId}/packages/${encodeURIComponent(slug)}`;
1984
+ try {
1985
+ const response = await fetch(url, {
1986
+ method: "GET",
1987
+ headers: {
1988
+ "Content-Type": "application/json",
1989
+ Authorization: `Bearer ${this.apiKey}`
1990
+ }
1991
+ });
1992
+ if (!response.ok) {
1993
+ let errorMsg = `API request failed: ${response.status} ${response.statusText}`;
1994
+ try {
1995
+ const errorBody = await response.json();
1996
+ if (errorBody && errorBody.message) {
1997
+ errorMsg = `${errorBody.message}`;
1998
+ }
1999
+ } catch {
2000
+ }
2001
+ const error = new Error(errorMsg);
2002
+ error.statusCode = response.status;
2003
+ throw error;
2004
+ }
2005
+ const result = await response.json();
2006
+ return result;
2007
+ } catch (error) {
2008
+ const err = error;
2009
+ const code = err?.code || err?.cause?.code;
2010
+ if (code === "ECONNREFUSED" || code === "ENOTFOUND" || err?.name === "FetchError" || typeof err?.message === "string" && (err.message.includes("Failed to fetch") || err.message.includes("network") || err.message.includes("NetworkError"))) {
2011
+ throw new Error(
2012
+ `Packmind server is not accessible at ${host}. Please check your network connection or the server URL.`
2013
+ );
2014
+ }
2015
+ throw new Error(
2016
+ `Failed to get package '${slug}': Error: ${err?.message || JSON.stringify(error)}`
2017
+ );
2018
+ }
2019
+ };
2020
+ this.getDetectionProgramsForPackages = async (params) => {
2021
+ const decodedApiKey = decodeApiKey(this.apiKey);
2022
+ if (!decodedApiKey.isValid) {
2023
+ throw new Error(`Invalid API key: ${decodedApiKey.error}`);
2024
+ }
2025
+ const { host } = decodedApiKey.payload;
2026
+ const url = `${host}/api/v0/detection-programs-for-packages`;
2027
+ const payload = {
2028
+ packagesSlugs: params.packagesSlugs
2029
+ };
2030
+ try {
2031
+ const response = await fetch(url, {
2032
+ method: "POST",
2033
+ headers: {
2034
+ "Content-Type": "application/json",
2035
+ Authorization: `Bearer ${this.apiKey}`
2036
+ },
2037
+ body: JSON.stringify(payload)
2038
+ });
2039
+ if (!response.ok) {
2040
+ let errorMsg = `API request failed: ${response.status} ${response.statusText}`;
2041
+ try {
2042
+ const errorBody = await response.json();
2043
+ if (errorBody && errorBody.message) {
2044
+ errorMsg = `${errorBody.message}`;
2045
+ }
2046
+ } catch {
2047
+ }
2048
+ throw new Error(errorMsg);
2049
+ }
2050
+ const result = await response.json();
2051
+ return result;
2052
+ } catch (error) {
2053
+ const err = error;
2054
+ const code = err?.code || err?.cause?.code;
2055
+ if (code === "ECONNREFUSED" || code === "ENOTFOUND" || err?.name === "FetchError" || typeof err?.message === "string" && (err.message.includes("Failed to fetch") || err.message.includes("network") || err.message.includes("NetworkError"))) {
2056
+ throw new Error(
2057
+ `Packmind server is not accessible at ${host}. Please check your network connection or the server URL.`
2058
+ );
2059
+ }
2060
+ throw new Error(
2061
+ `Failed to fetch detection programs for packages: Error: ${err?.message || JSON.stringify(error)}`
2062
+ );
2063
+ }
2064
+ };
1340
2065
  }
1341
2066
  };
1342
2067
 
@@ -3009,11 +3734,11 @@ var LinterAstAdapter = class {
3009
3734
  var TreeSitter17 = __toESM(require("web-tree-sitter"));
3010
3735
 
3011
3736
  // packages/linter-execution/src/application/useCases/ExecuteLinterProgramsUseCase.ts
3012
- var origin3 = "ExecuteLinterProgramsUseCase";
3737
+ var origin4 = "ExecuteLinterProgramsUseCase";
3013
3738
  var ExecuteLinterProgramsUseCase = class {
3014
- constructor(linterAstAdapter = new LinterAstAdapter(), logger = new PackmindLogger(origin3)) {
3739
+ constructor(linterAstAdapter = new LinterAstAdapter(), logger2 = new PackmindLogger(origin4)) {
3015
3740
  this.linterAstAdapter = linterAstAdapter;
3016
- this.logger = logger;
3741
+ this.logger = logger2;
3017
3742
  }
3018
3743
  async execute(command3) {
3019
3744
  const { filePath, fileContent, language, programs } = command3;
@@ -3206,149 +3931,1340 @@ var ExecuteLinterProgramsUseCase = class {
3206
3931
  }
3207
3932
  };
3208
3933
 
3209
- // apps/cli/src/application/useCases/PullDataUseCase.ts
3210
- var fs3 = __toESM(require("fs/promises"));
3211
- var path3 = __toESM(require("path"));
3212
- var PullDataUseCase = class {
3213
- constructor(packmindGateway) {
3214
- this.packmindGateway = packmindGateway;
3934
+ // packages/node-utils/src/dataSources/local.ts
3935
+ var import_typeorm = require("typeorm");
3936
+ var dataSource = makeDatasource();
3937
+ function makeDatasource() {
3938
+ try {
3939
+ return new import_typeorm.DataSource({
3940
+ type: "postgres",
3941
+ url: process.env["DATABASE_URL"],
3942
+ entities: [],
3943
+ migrations: []
3944
+ });
3945
+ } catch {
3946
+ return {};
3215
3947
  }
3216
- async execute(command3) {
3217
- const baseDirectory = command3.baseDirectory || process.cwd();
3218
- const result = {
3219
- filesCreated: 0,
3220
- filesUpdated: 0,
3221
- filesDeleted: 0,
3222
- errors: []
3223
- };
3948
+ }
3949
+
3950
+ // packages/node-utils/src/cache/Cache.ts
3951
+ var import_ioredis = __toESM(require("ioredis"));
3952
+
3953
+ // packages/node-utils/src/config/infra/Infisical/InfisicalConfig.ts
3954
+ var import_sdk = require("@infisical/sdk");
3955
+ var origin5 = "InfisicalConfig";
3956
+ var InfisicalConfig = class {
3957
+ constructor(clientId, clientSecret, env, projectId, logger2 = new PackmindLogger(origin5)) {
3958
+ this.clientId = clientId;
3959
+ this.clientSecret = clientSecret;
3960
+ this.env = env;
3961
+ this.projectId = projectId;
3962
+ this.logger = logger2;
3963
+ this.logger.info("Initializing InfisicalConfig", { env, projectId });
3224
3964
  try {
3225
- const response = await this.packmindGateway.getPullData({});
3226
- for (const file of response.fileUpdates.createOrUpdate) {
3227
- try {
3228
- await this.createOrUpdateFile(
3229
- baseDirectory,
3230
- file.path,
3231
- file.content,
3232
- result
3233
- );
3234
- } catch (error) {
3235
- const errorMsg = error instanceof Error ? error.message : String(error);
3236
- result.errors.push(
3237
- `Failed to create/update ${file.path}: ${errorMsg}`
3238
- );
3239
- }
3240
- }
3241
- for (const file of response.fileUpdates.delete) {
3242
- try {
3243
- await this.deleteFile(baseDirectory, file.path, result);
3244
- } catch (error) {
3245
- const errorMsg = error instanceof Error ? error.message : String(error);
3246
- result.errors.push(`Failed to delete ${file.path}: ${errorMsg}`);
3247
- }
3248
- }
3965
+ this.client = new import_sdk.InfisicalSDK({
3966
+ siteUrl: "https://eu.infisical.com"
3967
+ // Optional, defaults to https://app.infisical.com
3968
+ });
3969
+ this.logger.info("InfisicalSDK client created successfully", {
3970
+ siteUrl: "https://eu.infisical.com"
3971
+ });
3249
3972
  } catch (error) {
3250
- const errorMsg = error instanceof Error ? error.message : String(error);
3251
- result.errors.push(`Failed to pull data: ${errorMsg}`);
3252
- }
3253
- return result;
3254
- }
3255
- async createOrUpdateFile(baseDirectory, filePath, content, result) {
3256
- const fullPath = path3.join(baseDirectory, filePath);
3257
- const directory = path3.dirname(fullPath);
3258
- await fs3.mkdir(directory, { recursive: true });
3259
- const fileExists = await this.fileExists(fullPath);
3260
- if (fileExists) {
3261
- await fs3.appendFile(fullPath, content, "utf-8");
3262
- result.filesUpdated++;
3263
- } else {
3264
- await fs3.writeFile(fullPath, content, "utf-8");
3265
- result.filesCreated++;
3266
- }
3267
- }
3268
- async deleteFile(baseDirectory, filePath, result) {
3269
- const fullPath = path3.join(baseDirectory, filePath);
3270
- const fileExists = await this.fileExists(fullPath);
3271
- if (fileExists) {
3272
- await fs3.unlink(fullPath);
3273
- result.filesDeleted++;
3973
+ this.logger.error("Failed to create InfisicalSDK client", {
3974
+ error: error instanceof Error ? error.message : String(error)
3975
+ });
3976
+ throw error;
3274
3977
  }
3275
3978
  }
3276
- async fileExists(filePath) {
3979
+ async initClient() {
3980
+ this.logger.info("Initializing Infisical client authentication");
3277
3981
  try {
3278
- await fs3.access(filePath);
3279
- return true;
3280
- } catch {
3281
- return false;
3982
+ this.logger.debug("Authenticating with Infisical using universal auth");
3983
+ await this.client.auth().universalAuth.login({
3984
+ clientId: this.clientId,
3985
+ clientSecret: this.clientSecret
3986
+ });
3987
+ this.logger.info("Infisical client authenticated successfully");
3988
+ } catch (error) {
3989
+ this.logger.error("Failed to authenticate Infisical client", {
3990
+ error: error instanceof Error ? error.message : String(error)
3991
+ });
3992
+ throw error;
3282
3993
  }
3283
3994
  }
3284
- };
3285
-
3286
- // apps/cli/src/PackmindCliHexaFactory.ts
3287
- var PackmindCliHexaFactory = class {
3288
- constructor(logger) {
3289
- this.logger = logger;
3290
- this.repositories = {
3291
- packmindGateway: new PackmindGateway(
3292
- process.env.PACKMIND_API_KEY_V3 || ""
3293
- )
3294
- };
3295
- this.services = {
3296
- listFiles: new ListFiles(),
3297
- gitRemoteUrlService: new GitService(this.logger),
3298
- linterExecutionUseCase: new ExecuteLinterProgramsUseCase()
3299
- };
3300
- this.useCases = {
3301
- executeSingleFileAst: new ExecuteSingleFileAstUseCase(
3302
- this.services.linterExecutionUseCase
3303
- ),
3304
- getGitRemoteUrl: new GetGitRemoteUrlUseCase(),
3305
- listFilesInDirectoryUseCase: new ListFilesInDirectoryUseCase(),
3306
- lintFilesInDirectory: new LintFilesInDirectoryUseCase(
3307
- this.services,
3308
- this.repositories,
3309
- this.logger
3310
- ),
3311
- pullData: new PullDataUseCase(this.repositories.packmindGateway)
3312
- };
3313
- }
3314
- };
3315
-
3316
- // apps/cli/src/PackmindCliHexa.ts
3317
- var origin4 = "PackmindCliHexa";
3318
- var PackmindCliHexa = class {
3319
- constructor(logger = new PackmindLogger(origin4)) {
3320
- this.logger = logger;
3995
+ async getValue(secretName) {
3996
+ this.logger.info("Retrieving secret from Infisical", {
3997
+ secretName,
3998
+ env: this.env,
3999
+ projectId: this.projectId
4000
+ });
3321
4001
  try {
3322
- this.hexa = new PackmindCliHexaFactory(this.logger);
4002
+ this.logger.debug("Fetching secret from Infisical API", { secretName });
4003
+ const nameSecret = await this.client.secrets().getSecret({
4004
+ projectId: this.projectId,
4005
+ environment: this.env,
4006
+ secretName
4007
+ });
4008
+ if (nameSecret?.secretValue) {
4009
+ this.logger.info("Secret retrieved from Infisical successfully", {
4010
+ secretName
4011
+ });
4012
+ return nameSecret.secretValue;
4013
+ } else {
4014
+ this.logger.warn("Secret not found or has no value in Infisical", {
4015
+ secretName
4016
+ });
4017
+ return null;
4018
+ }
3323
4019
  } catch (error) {
3324
- this.logger.error("Failed to initialize PackmindCliHexa", {
4020
+ this.logger.warn("Failed to retrieve secret from Infisical", {
4021
+ secretName,
4022
+ env: this.env,
4023
+ projectId: this.projectId,
3325
4024
  error: error instanceof Error ? error.message : String(error)
3326
4025
  });
3327
4026
  throw error;
3328
4027
  }
3329
4028
  }
3330
- /**
3331
- * Destroys the DeploymentsHexa and cleans up resources
3332
- */
3333
- destroy() {
3334
- this.logger.info("Destroying PackmindCliHexa");
3335
- this.logger.info("PackmindCliHexa destroyed");
3336
- }
3337
- async getGitRemoteUrl(command3) {
3338
- return this.hexa.useCases.getGitRemoteUrl.execute(command3);
4029
+ };
4030
+
4031
+ // packages/node-utils/src/config/config/Configuration.ts
4032
+ var origin6 = "Configuration";
4033
+ var Configuration = class _Configuration {
4034
+ constructor() {
4035
+ this.initialized = false;
4036
+ this.initializationPromise = null;
3339
4037
  }
3340
- async executeSingleFileAst(command3) {
3341
- return this.hexa.useCases.executeSingleFileAst.execute(command3);
4038
+ static {
4039
+ this.logger = new PackmindLogger(
4040
+ origin6,
4041
+ "info" /* INFO */
4042
+ );
3342
4043
  }
3343
- async listFilesInDirectory(command3) {
3344
- return this.hexa.useCases.listFilesInDirectoryUseCase.execute(command3);
4044
+ static getInstance(logger2) {
4045
+ if (logger2) {
4046
+ _Configuration.logger = logger2;
4047
+ }
4048
+ _Configuration.logger.debug("Getting Configuration instance");
4049
+ if (!_Configuration.instance) {
4050
+ _Configuration.logger.info("Creating new Configuration instance");
4051
+ _Configuration.instance = new _Configuration();
4052
+ }
4053
+ return _Configuration.instance;
4054
+ }
4055
+ async initialize(env) {
4056
+ if (this.initialized) {
4057
+ _Configuration.logger.debug("Configuration already initialized, skipping");
4058
+ return;
4059
+ }
4060
+ if (this.initializationPromise) {
4061
+ _Configuration.logger.debug(
4062
+ "Configuration initialization already in progress, waiting for completion"
4063
+ );
4064
+ await this.initializationPromise;
4065
+ return;
4066
+ }
4067
+ this.initializationPromise = this.performInitialization(env);
4068
+ try {
4069
+ await this.initializationPromise;
4070
+ } finally {
4071
+ this.initializationPromise = null;
4072
+ }
4073
+ }
4074
+ async performInitialization(env) {
4075
+ _Configuration.logger.info("Initializing Configuration");
4076
+ const configurationMode = env["CONFIGURATION"]?.toLowerCase();
4077
+ _Configuration.logger.debug("Configuration mode detected", {
4078
+ mode: configurationMode
4079
+ });
4080
+ if (configurationMode === "infisical") {
4081
+ _Configuration.logger.info("Initializing Infisical configuration");
4082
+ const clientId = env["INFISICAL_CLIENT_ID"];
4083
+ const clientSecret = env["INFISICAL_CLIENT_SECRET"];
4084
+ const infisicalEnv = env["INFISICAL_ENV"];
4085
+ const projectId = env["INFISICAL_PROJECT_ID"];
4086
+ if (!clientId || !clientSecret || !infisicalEnv || !projectId) {
4087
+ _Configuration.logger.error(
4088
+ "Infisical configuration is incomplete, falling back to environment variables only",
4089
+ {
4090
+ hasClientId: !!clientId,
4091
+ hasClientSecret: !!clientSecret,
4092
+ hasInfisicalEnv: !!infisicalEnv,
4093
+ hasProjectId: !!projectId
4094
+ }
4095
+ );
4096
+ this.infisicalConfig = void 0;
4097
+ } else {
4098
+ try {
4099
+ this.infisicalConfig = new InfisicalConfig(
4100
+ clientId,
4101
+ clientSecret,
4102
+ infisicalEnv,
4103
+ projectId
4104
+ );
4105
+ _Configuration.logger.debug("Initializing Infisical client");
4106
+ await this.infisicalConfig.initClient();
4107
+ _Configuration.logger.info(
4108
+ "Infisical configuration initialized successfully"
4109
+ );
4110
+ } catch (error) {
4111
+ _Configuration.logger.error(
4112
+ "Failed to initialize Infisical, falling back to environment variables only",
4113
+ {
4114
+ error: error instanceof Error ? error.message : String(error)
4115
+ }
4116
+ );
4117
+ this.infisicalConfig = void 0;
4118
+ }
4119
+ }
4120
+ } else {
4121
+ _Configuration.logger.info(
4122
+ "Using environment variables only (no Infisical)"
4123
+ );
4124
+ }
4125
+ this.initialized = true;
4126
+ _Configuration.logger.info("Configuration initialization completed");
4127
+ }
4128
+ static async getConfigWithDefault(key, defaultValue) {
4129
+ const value = await _Configuration.getConfig(key);
4130
+ return value ?? defaultValue;
4131
+ }
4132
+ static async getConfig(key, env = process.env, logger2) {
4133
+ if (logger2) {
4134
+ _Configuration.logger = logger2;
4135
+ }
4136
+ _Configuration.logger.info("Getting configuration value", { key });
4137
+ try {
4138
+ const instance = _Configuration.getInstance();
4139
+ await instance.initialize(env);
4140
+ const envValue = env[key];
4141
+ if (envValue) {
4142
+ _Configuration.logger.debug(
4143
+ "Configuration value found in environment variables",
4144
+ { key }
4145
+ );
4146
+ return envValue;
4147
+ }
4148
+ if (instance.infisicalConfig) {
4149
+ _Configuration.logger.debug(
4150
+ "Checking Infisical for configuration value",
4151
+ { key }
4152
+ );
4153
+ try {
4154
+ const infisicalValue = await instance.infisicalConfig.getValue(key);
4155
+ if (infisicalValue) {
4156
+ _Configuration.logger.debug(
4157
+ "Configuration value found in Infisical",
4158
+ {
4159
+ key
4160
+ }
4161
+ );
4162
+ return infisicalValue;
4163
+ }
4164
+ } catch (error) {
4165
+ _Configuration.logger.error(
4166
+ "Failed to retrieve value from Infisical, value not available",
4167
+ {
4168
+ key,
4169
+ error: error instanceof Error ? error.message : String(error)
4170
+ }
4171
+ );
4172
+ }
4173
+ }
4174
+ _Configuration.logger.warn("Configuration value not found", { key });
4175
+ return null;
4176
+ } catch (error) {
4177
+ _Configuration.logger.warn("Failed to get configuration value", {
4178
+ key,
4179
+ error: error instanceof Error ? error.message : String(error)
4180
+ });
4181
+ return null;
4182
+ }
4183
+ }
4184
+ };
4185
+
4186
+ // packages/node-utils/src/cache/Cache.ts
4187
+ var origin7 = "Cache";
4188
+ var Cache = class _Cache {
4189
+ constructor() {
4190
+ this.initialized = false;
4191
+ this.connectionConfig = {
4192
+ host: "redis",
4193
+ port: 6379,
4194
+ maxRetriesPerRequest: 3
4195
+ };
4196
+ }
4197
+ static {
4198
+ this.logger = new PackmindLogger(
4199
+ origin7,
4200
+ "info" /* INFO */
4201
+ );
4202
+ }
4203
+ static {
4204
+ // Default cache expiration time in seconds (5 minutes)
4205
+ this.DEFAULT_EXPIRATION_SECONDS = 300;
4206
+ }
4207
+ /**
4208
+ * Get the singleton instance of Cache
4209
+ */
4210
+ static getInstance() {
4211
+ _Cache.logger.debug("Getting Cache instance");
4212
+ if (!_Cache.instance) {
4213
+ _Cache.logger.info("Creating new Cache instance");
4214
+ _Cache.instance = new _Cache();
4215
+ }
4216
+ return _Cache.instance;
4217
+ }
4218
+ /**
4219
+ * Initialize the Redis client with configuration
4220
+ * This should be called during application startup
4221
+ */
4222
+ async initialize() {
4223
+ if (this.initialized) {
4224
+ return;
4225
+ }
4226
+ _Cache.logger.info("Initializing Redis cache client");
4227
+ try {
4228
+ const redisUri = await Configuration.getConfig("REDIS_URI");
4229
+ if (!redisUri) {
4230
+ throw new Error("REDIS_URI configuration is required");
4231
+ }
4232
+ _Cache.logger.info("Using REDIS_URI configuration");
4233
+ this.client = new import_ioredis.default(redisUri, {
4234
+ maxRetriesPerRequest: 3
4235
+ });
4236
+ this.client.on("error", (error) => {
4237
+ _Cache.logger.error("Redis cache client error", {
4238
+ error: error instanceof Error ? error.message : String(error)
4239
+ });
4240
+ });
4241
+ this.client.on("connect", () => {
4242
+ _Cache.logger.info("Redis cache client connected successfully");
4243
+ });
4244
+ this.initialized = true;
4245
+ _Cache.logger.info("Cache initialization completed");
4246
+ } catch (error) {
4247
+ _Cache.logger.error("Failed to initialize cache", {
4248
+ error: error instanceof Error ? error.message : String(error)
4249
+ });
4250
+ throw new Error(
4251
+ `Cache initialization failed: ${error instanceof Error ? error.message : String(error)}`
4252
+ );
4253
+ }
4254
+ }
4255
+ /**
4256
+ * Set a value in the cache with optional expiration
4257
+ * @param key - The cache key
4258
+ * @param value - The value to cache (will be JSON serialized)
4259
+ * @param expirationSeconds - Expiration time in seconds (default: 300s)
4260
+ */
4261
+ async set(key, value, expirationSeconds = _Cache.DEFAULT_EXPIRATION_SECONDS) {
4262
+ if (!this.initialized || !this.client) {
4263
+ _Cache.logger.warn("Cache not initialized, skipping set operation", {
4264
+ key
4265
+ });
4266
+ return;
4267
+ }
4268
+ try {
4269
+ const serializedValue = JSON.stringify(value);
4270
+ await this.client.setex(key, expirationSeconds, serializedValue);
4271
+ } catch (error) {
4272
+ _Cache.logger.warn(
4273
+ "Failed to set cache value, continuing without caching",
4274
+ {
4275
+ key,
4276
+ error: error instanceof Error ? error.message : String(error)
4277
+ }
4278
+ );
4279
+ }
4280
+ }
4281
+ /**
4282
+ * Get a value from the cache
4283
+ * @param key - The cache key
4284
+ * @returns The cached value or null if not found, expired, or error occurred
4285
+ */
4286
+ async get(key) {
4287
+ if (!this.initialized || !this.client) {
4288
+ _Cache.logger.warn(
4289
+ "Cache not initialized, returning null for get operation",
4290
+ { key }
4291
+ );
4292
+ return null;
4293
+ }
4294
+ try {
4295
+ const serializedValue = await this.client.get(key);
4296
+ if (serializedValue === null) {
4297
+ return null;
4298
+ }
4299
+ const value = JSON.parse(serializedValue);
4300
+ return value;
4301
+ } catch (error) {
4302
+ _Cache.logger.warn("Failed to get cache value, returning null", {
4303
+ key,
4304
+ error: error instanceof Error ? error.message : String(error)
4305
+ });
4306
+ return null;
4307
+ }
4308
+ }
4309
+ /**
4310
+ * Invalidate (delete) a cache entry
4311
+ * @param key - The cache key to invalidate
4312
+ */
4313
+ async invalidate(key) {
4314
+ if (!this.initialized || !this.client) {
4315
+ _Cache.logger.warn(
4316
+ "Cache not initialized, skipping invalidate operation",
4317
+ { key }
4318
+ );
4319
+ return;
4320
+ }
4321
+ try {
4322
+ await this.client.del(key);
4323
+ } catch (error) {
4324
+ _Cache.logger.warn(
4325
+ "Failed to invalidate cache key, continuing without invalidation",
4326
+ {
4327
+ key,
4328
+ error: error instanceof Error ? error.message : String(error)
4329
+ }
4330
+ );
4331
+ }
4332
+ }
4333
+ /**
4334
+ * Disconnect the Redis client (for cleanup during shutdown)
4335
+ */
4336
+ async disconnect() {
4337
+ _Cache.logger.info("Disconnecting cache client");
4338
+ if (this.client) {
4339
+ try {
4340
+ await this.client.disconnect();
4341
+ _Cache.logger.info("Cache client disconnected successfully");
4342
+ } catch (error) {
4343
+ _Cache.logger.error("Error disconnecting cache client", {
4344
+ error: error instanceof Error ? error.message : String(error)
4345
+ });
4346
+ }
4347
+ }
4348
+ this.initialized = false;
4349
+ }
4350
+ /**
4351
+ * Get cache statistics (for monitoring/debugging)
4352
+ */
4353
+ async getStats() {
4354
+ return {
4355
+ connected: this.client?.status === "ready",
4356
+ initialized: this.initialized
4357
+ };
4358
+ }
4359
+ };
4360
+
4361
+ // packages/node-utils/src/jobs/infra/bullMQ/WorkerQueue.ts
4362
+ var import_bullmq2 = require("bullmq");
4363
+
4364
+ // packages/node-utils/src/jobs/infra/bullMQ/AbstractQueue.ts
4365
+ var import_bullmq = require("bullmq");
4366
+
4367
+ // packages/node-utils/src/jobs/infra/DelayedJobsFactory.ts
4368
+ var logger = new PackmindLogger("DelayedJobsFactory");
4369
+
4370
+ // packages/node-utils/src/mail/SmtpMailService.ts
4371
+ var import_nodemailer = __toESM(require("nodemailer"));
4372
+
4373
+ // packages/node-utils/src/nest/Public.ts
4374
+ var import_common = require("@nestjs/common");
4375
+
4376
+ // packages/node-utils/src/repositories/AbstractRepository.ts
4377
+ var import_common2 = require("@nestjs/common");
4378
+
4379
+ // packages/node-utils/src/sse/RedisSSEClient.ts
4380
+ var import_ioredis2 = __toESM(require("ioredis"));
4381
+ var origin8 = "RedisSSEClient";
4382
+ var RedisSSEClient = class _RedisSSEClient {
4383
+ // eslint-disable-next-line @typescript-eslint/no-empty-function
4384
+ constructor() {
4385
+ this.initialized = false;
4386
+ }
4387
+ static {
4388
+ this.logger = new PackmindLogger(origin8);
4389
+ }
4390
+ static getInstance() {
4391
+ _RedisSSEClient.logger.debug("Getting RedisSSEClient instance");
4392
+ if (!_RedisSSEClient.instance) {
4393
+ _RedisSSEClient.logger.info("Creating new RedisSSEClient instance");
4394
+ _RedisSSEClient.instance = new _RedisSSEClient();
4395
+ }
4396
+ return _RedisSSEClient.instance;
4397
+ }
4398
+ /**
4399
+ * Initialize Redis clients using the same configuration as BullMQ
4400
+ */
4401
+ async initialize() {
4402
+ if (this.initialized) return;
4403
+ _RedisSSEClient.logger.info("Initializing Redis SSE clients");
4404
+ try {
4405
+ const redisURI = await Configuration.getConfig("REDIS_URI") || "redis";
4406
+ this.publisherClient = new import_ioredis2.default(redisURI);
4407
+ this.subscriberClient = new import_ioredis2.default(redisURI);
4408
+ this.publisherClient.on("error", (error) => {
4409
+ _RedisSSEClient.logger.error("Redis publisher client error", {
4410
+ error: error.message
4411
+ });
4412
+ });
4413
+ this.subscriberClient.on("error", (error) => {
4414
+ _RedisSSEClient.logger.error("Redis subscriber client error", {
4415
+ error: error.message
4416
+ });
4417
+ });
4418
+ await this.publisherClient.ping();
4419
+ await this.subscriberClient.ping();
4420
+ this.initialized = true;
4421
+ _RedisSSEClient.logger.info("Redis SSE clients initialized successfully");
4422
+ } catch (error) {
4423
+ _RedisSSEClient.logger.error("Failed to initialize Redis SSE clients", {
4424
+ error: error instanceof Error ? error.message : String(error)
4425
+ });
4426
+ throw error;
4427
+ }
4428
+ }
4429
+ /**
4430
+ * Publish a message to a Redis channel
4431
+ */
4432
+ async publish(channel, message) {
4433
+ await this.initialize();
4434
+ if (!this.publisherClient) {
4435
+ throw new Error("Publisher client not initialized");
4436
+ }
4437
+ _RedisSSEClient.logger.debug("Publishing message to Redis channel", {
4438
+ channel,
4439
+ messageLength: message.length
4440
+ });
4441
+ try {
4442
+ const subscriberCount = await this.publisherClient.publish(
4443
+ channel,
4444
+ message
4445
+ );
4446
+ _RedisSSEClient.logger.debug("Message published successfully", {
4447
+ channel,
4448
+ subscriberCount
4449
+ });
4450
+ return subscriberCount;
4451
+ } catch (error) {
4452
+ _RedisSSEClient.logger.error("Failed to publish message", {
4453
+ channel,
4454
+ error: error instanceof Error ? error.message : String(error)
4455
+ });
4456
+ throw error;
4457
+ }
4458
+ }
4459
+ /**
4460
+ * Subscribe to a Redis channel
4461
+ */
4462
+ async subscribe(channel, callback) {
4463
+ await this.initialize();
4464
+ if (!this.subscriberClient) {
4465
+ throw new Error("Subscriber client not initialized");
4466
+ }
4467
+ _RedisSSEClient.logger.info("Subscribing to Redis channel", { channel });
4468
+ try {
4469
+ this.subscriberClient.on("message", (receivedChannel, message) => {
4470
+ if (receivedChannel === channel) {
4471
+ _RedisSSEClient.logger.debug("Received message from Redis channel", {
4472
+ channel: receivedChannel,
4473
+ messageLength: message.length
4474
+ });
4475
+ callback(message);
4476
+ }
4477
+ });
4478
+ await this.subscriberClient.subscribe(channel);
4479
+ _RedisSSEClient.logger.info("Successfully subscribed to Redis channel", {
4480
+ channel
4481
+ });
4482
+ } catch (error) {
4483
+ _RedisSSEClient.logger.error("Failed to subscribe to Redis channel", {
4484
+ channel,
4485
+ error: error instanceof Error ? error.message : String(error)
4486
+ });
4487
+ throw error;
4488
+ }
4489
+ }
4490
+ /**
4491
+ * Unsubscribe from a Redis channel
4492
+ */
4493
+ async unsubscribe(channel) {
4494
+ if (!this.subscriberClient) {
4495
+ return;
4496
+ }
4497
+ _RedisSSEClient.logger.info("Unsubscribing from Redis channel", { channel });
4498
+ try {
4499
+ await this.subscriberClient.unsubscribe(channel);
4500
+ _RedisSSEClient.logger.info(
4501
+ "Successfully unsubscribed from Redis channel",
4502
+ { channel }
4503
+ );
4504
+ } catch (error) {
4505
+ _RedisSSEClient.logger.error("Failed to unsubscribe from Redis channel", {
4506
+ channel,
4507
+ error: error instanceof Error ? error.message : String(error)
4508
+ });
4509
+ }
4510
+ }
4511
+ /**
4512
+ * Clean up Redis connections
4513
+ */
4514
+ async disconnect() {
4515
+ _RedisSSEClient.logger.info("Disconnecting Redis SSE clients");
4516
+ try {
4517
+ if (this.publisherClient) {
4518
+ this.publisherClient.disconnect();
4519
+ }
4520
+ if (this.subscriberClient) {
4521
+ this.subscriberClient.disconnect();
4522
+ }
4523
+ this.initialized = false;
4524
+ _RedisSSEClient.logger.info("Redis SSE clients disconnected successfully");
4525
+ } catch (error) {
4526
+ _RedisSSEClient.logger.error("Error disconnecting Redis SSE clients", {
4527
+ error: error instanceof Error ? error.message : String(error)
4528
+ });
4529
+ }
4530
+ }
4531
+ /**
4532
+ * Get connection status
4533
+ */
4534
+ isConnected() {
4535
+ return this.initialized && this.publisherClient?.status === "ready" && this.subscriberClient?.status === "ready";
4536
+ }
4537
+ };
4538
+
4539
+ // packages/node-utils/src/sse/types.ts
4540
+ var SSE_REDIS_CHANNELS = {
4541
+ /**
4542
+ * Channel for subscription management messages
4543
+ * Used when clients subscribe/unsubscribe to specific event types
4544
+ */
4545
+ SUBSCRIPTIONS: "sse:subscriptions",
4546
+ /**
4547
+ * Channel for event notifications
4548
+ * Used to broadcast SSE events to all API instances
4549
+ */
4550
+ EVENTS: "sse:events"
4551
+ };
4552
+ function createSSEEventMessage(eventType, params, data, targetUserIds) {
4553
+ return {
4554
+ eventType,
4555
+ params,
4556
+ data,
4557
+ targetUserIds,
4558
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
4559
+ };
4560
+ }
4561
+ function serializeSSERedisMessage(message) {
4562
+ try {
4563
+ return JSON.stringify(message);
4564
+ } catch (error) {
4565
+ throw new Error(
4566
+ `Failed to serialize SSE Redis message: ${error instanceof Error ? error.message : String(error)}`
4567
+ );
4568
+ }
4569
+ }
4570
+
4571
+ // packages/node-utils/src/sse/SSEEventPublisher.ts
4572
+ var origin9 = "SSEEventPublisher";
4573
+ var SSEEventPublisher = class _SSEEventPublisher {
4574
+ static {
4575
+ this.logger = new PackmindLogger(origin9);
4576
+ }
4577
+ /**
4578
+ * Get the singleton instance
4579
+ */
4580
+ static getInstance() {
4581
+ _SSEEventPublisher.logger.debug("Getting SSEEventPublisher instance");
4582
+ if (!_SSEEventPublisher.instance) {
4583
+ _SSEEventPublisher.logger.info("Creating new SSEEventPublisher instance");
4584
+ _SSEEventPublisher.instance = new _SSEEventPublisher();
4585
+ _SSEEventPublisher.redisClient = RedisSSEClient.getInstance();
4586
+ }
4587
+ return _SSEEventPublisher.instance;
4588
+ }
4589
+ constructor() {
4590
+ }
4591
+ /**
4592
+ * Publish a program status change event for cache invalidation
4593
+ * This triggers React Query to refetch the program data
4594
+ */
4595
+ static async publishProgramStatusEvent(programId, ruleId, language, userId, organizationId) {
4596
+ _SSEEventPublisher.logger.info("Publishing program status change event", {
4597
+ programId,
4598
+ ruleId,
4599
+ language,
4600
+ userId,
4601
+ organizationId
4602
+ });
4603
+ try {
4604
+ const event = createProgramStatusChangeEvent(ruleId, language);
4605
+ await _SSEEventPublisher.publishEvent(
4606
+ "program_status_change",
4607
+ [ruleId, language],
4608
+ event,
4609
+ userId ? [userId] : void 0
4610
+ );
4611
+ _SSEEventPublisher.logger.debug(
4612
+ "Successfully published program status change event",
4613
+ { programId, ruleId, language }
4614
+ );
4615
+ } catch (error) {
4616
+ _SSEEventPublisher.logger.error(
4617
+ "Failed to publish program status change event",
4618
+ {
4619
+ programId,
4620
+ userId,
4621
+ error: error instanceof Error ? error.message : String(error)
4622
+ }
4623
+ );
4624
+ throw error;
4625
+ }
4626
+ }
4627
+ /**
4628
+ * Publish an assessment status change event for cache invalidation
4629
+ * This triggers React Query to refetch the assessment data
4630
+ */
4631
+ static async publishAssessmentStatusEvent(ruleId, language, userId, organizationId) {
4632
+ _SSEEventPublisher.logger.info("Publishing assessment status change event", {
4633
+ ruleId,
4634
+ language,
4635
+ userId,
4636
+ organizationId
4637
+ });
4638
+ try {
4639
+ const event = createAssessmentStatusChangeEvent(ruleId, language);
4640
+ await _SSEEventPublisher.publishEvent(
4641
+ "assessment_status_change",
4642
+ [ruleId, language],
4643
+ event,
4644
+ userId ? [userId] : void 0
4645
+ );
4646
+ _SSEEventPublisher.logger.debug(
4647
+ "Successfully published assessment status change event",
4648
+ { ruleId, language }
4649
+ );
4650
+ } catch (error) {
4651
+ _SSEEventPublisher.logger.error(
4652
+ "Failed to publish assessment status change event",
4653
+ {
4654
+ userId,
4655
+ error: error instanceof Error ? error.message : String(error)
4656
+ }
4657
+ );
4658
+ throw error;
4659
+ }
4660
+ }
4661
+ /**
4662
+ * Publish a detection heuristics updated event for cache invalidation
4663
+ * This triggers React Query to refetch the heuristics data
4664
+ */
4665
+ static async publishDetectionHeuristicsUpdatedEvent(ruleId, language, detectionHeuristicsId, userId, organizationId) {
4666
+ _SSEEventPublisher.logger.info(
4667
+ "Publishing detection heuristics updated event",
4668
+ {
4669
+ ruleId,
4670
+ language,
4671
+ detectionHeuristicsId,
4672
+ userId,
4673
+ organizationId
4674
+ }
4675
+ );
4676
+ try {
4677
+ const event = createDetectionHeuristicsUpdatedEvent(
4678
+ ruleId,
4679
+ language,
4680
+ detectionHeuristicsId
4681
+ );
4682
+ await _SSEEventPublisher.publishEvent(
4683
+ "detection_heuristics_updated",
4684
+ [ruleId, language, detectionHeuristicsId],
4685
+ event,
4686
+ userId ? [userId] : void 0
4687
+ );
4688
+ _SSEEventPublisher.logger.debug(
4689
+ "Successfully published detection heuristics updated event",
4690
+ { ruleId, language, detectionHeuristicsId }
4691
+ );
4692
+ } catch (error) {
4693
+ _SSEEventPublisher.logger.error(
4694
+ "Failed to publish detection heuristics updated event",
4695
+ {
4696
+ ruleId,
4697
+ language,
4698
+ detectionHeuristicsId,
4699
+ userId,
4700
+ error: error instanceof Error ? error.message : String(error)
4701
+ }
4702
+ );
4703
+ throw error;
4704
+ }
4705
+ }
4706
+ /**
4707
+ * Publish an event to notify a user that their context (role or membership) changed
4708
+ * This should trigger a refetch of the /me route on the frontend
4709
+ */
4710
+ static async publishUserContextChangeEvent(userId, organizationId, changeType, role) {
4711
+ _SSEEventPublisher.logger.info("Publishing user context change event", {
4712
+ userId,
4713
+ organizationId,
4714
+ changeType,
4715
+ role
4716
+ });
4717
+ try {
4718
+ const event = createUserContextChangeEvent(
4719
+ userId,
4720
+ organizationId,
4721
+ changeType,
4722
+ role
4723
+ );
4724
+ await _SSEEventPublisher.publishEvent("user_context_change", [], event, [
4725
+ userId
4726
+ ]);
4727
+ _SSEEventPublisher.logger.debug(
4728
+ "Successfully published user context change event",
4729
+ {
4730
+ userId,
4731
+ organizationId,
4732
+ changeType
4733
+ }
4734
+ );
4735
+ } catch (error) {
4736
+ _SSEEventPublisher.logger.error(
4737
+ "Failed to publish user context change event",
4738
+ {
4739
+ userId,
4740
+ organizationId,
4741
+ changeType,
4742
+ error: error instanceof Error ? error.message : String(error)
4743
+ }
4744
+ );
4745
+ throw error;
4746
+ }
4747
+ }
4748
+ /**
4749
+ * Generic method to publish any SSE event type to Redis pub/sub
4750
+ */
4751
+ static async publishEvent(eventType, params, data, targetUserIds) {
4752
+ _SSEEventPublisher.logger.info("Publishing SSE event", {
4753
+ eventType,
4754
+ params,
4755
+ targetUserIds: targetUserIds?.length || "all"
4756
+ });
4757
+ try {
4758
+ _SSEEventPublisher.getInstance();
4759
+ const redisClient = _SSEEventPublisher.redisClient;
4760
+ const message = createSSEEventMessage(
4761
+ eventType,
4762
+ params,
4763
+ data,
4764
+ targetUserIds
4765
+ );
4766
+ const serializedMessage = serializeSSERedisMessage(message);
4767
+ await redisClient.publish(SSE_REDIS_CHANNELS.EVENTS, serializedMessage);
4768
+ _SSEEventPublisher.logger.debug("Successfully published SSE event", {
4769
+ eventType,
4770
+ params
4771
+ });
4772
+ } catch (error) {
4773
+ _SSEEventPublisher.logger.error("Failed to publish SSE event", {
4774
+ eventType,
4775
+ params,
4776
+ error: error instanceof Error ? error.message : String(error)
4777
+ });
4778
+ throw error;
4779
+ }
4780
+ }
4781
+ };
4782
+
4783
+ // packages/node-utils/src/text/sectionMerge.ts
4784
+ function mergeSectionsIntoFileContent(existingContent, sections) {
4785
+ let result = existingContent;
4786
+ for (const section of sections) {
4787
+ const startMarker = `<!-- start: ${section.key} -->`;
4788
+ const endMarker = `<!-- end: ${section.key} -->`;
4789
+ const startIndex = result.indexOf(startMarker);
4790
+ const endIndex = result.indexOf(endMarker);
4791
+ if (startIndex !== -1 && endIndex !== -1 && endIndex > startIndex) {
4792
+ const before = result.substring(0, startIndex + startMarker.length);
4793
+ const after = result.substring(endIndex);
4794
+ result = `${before}
4795
+ ${section.content}
4796
+ ${after}`;
4797
+ } else {
4798
+ const sectionBlock = `${startMarker}
4799
+ ${section.content}
4800
+ ${endMarker}`;
4801
+ if (result.trim() === "") {
4802
+ result = sectionBlock;
4803
+ } else {
4804
+ result = result.endsWith("\n") ? `${result}${sectionBlock}
4805
+ ` : `${result}
4806
+ ${sectionBlock}
4807
+ `;
4808
+ }
4809
+ }
4810
+ }
4811
+ return result;
4812
+ }
4813
+
4814
+ // apps/cli/src/application/useCases/PullDataUseCase.ts
4815
+ var fs4 = __toESM(require("fs/promises"));
4816
+ var path4 = __toESM(require("path"));
4817
+ var PullDataUseCase = class {
4818
+ constructor(packmindGateway) {
4819
+ this.packmindGateway = packmindGateway;
4820
+ }
4821
+ async execute(command3) {
4822
+ const baseDirectory = command3.baseDirectory || process.cwd();
4823
+ const result = {
4824
+ filesCreated: 0,
4825
+ filesUpdated: 0,
4826
+ filesDeleted: 0,
4827
+ errors: [],
4828
+ recipesCount: 0,
4829
+ standardsCount: 0
4830
+ };
4831
+ const response = await this.packmindGateway.getPullData({
4832
+ packagesSlugs: command3.packagesSlugs
4833
+ });
4834
+ const uniqueFilesMap = /* @__PURE__ */ new Map();
4835
+ for (const file of response.fileUpdates.createOrUpdate) {
4836
+ uniqueFilesMap.set(file.path, file);
4837
+ }
4838
+ const uniqueFiles = Array.from(uniqueFilesMap.values());
4839
+ for (const file of uniqueFiles) {
4840
+ if (file.path.includes(".packmind/recipes/") && file.path.endsWith(".md")) {
4841
+ result.recipesCount++;
4842
+ } else if (file.path.includes(".packmind/standards/") && file.path.endsWith(".md")) {
4843
+ result.standardsCount++;
4844
+ }
4845
+ }
4846
+ try {
4847
+ for (const file of uniqueFiles) {
4848
+ try {
4849
+ await this.createOrUpdateFile(baseDirectory, file, result);
4850
+ } catch (error) {
4851
+ const errorMsg = error instanceof Error ? error.message : String(error);
4852
+ result.errors.push(
4853
+ `Failed to create/update ${file.path}: ${errorMsg}`
4854
+ );
4855
+ }
4856
+ }
4857
+ for (const file of response.fileUpdates.delete) {
4858
+ try {
4859
+ await this.deleteFile(baseDirectory, file.path, result);
4860
+ } catch (error) {
4861
+ const errorMsg = error instanceof Error ? error.message : String(error);
4862
+ result.errors.push(`Failed to delete ${file.path}: ${errorMsg}`);
4863
+ }
4864
+ }
4865
+ } catch (error) {
4866
+ const errorMsg = error instanceof Error ? error.message : String(error);
4867
+ result.errors.push(`Failed to pull data: ${errorMsg}`);
4868
+ }
4869
+ return result;
4870
+ }
4871
+ async createOrUpdateFile(baseDirectory, file, result) {
4872
+ const fullPath = path4.join(baseDirectory, file.path);
4873
+ const directory = path4.dirname(fullPath);
4874
+ await fs4.mkdir(directory, { recursive: true });
4875
+ const fileExists = await this.fileExists(fullPath);
4876
+ if (file.content !== void 0) {
4877
+ await this.handleFullContentUpdate(
4878
+ fullPath,
4879
+ file.content,
4880
+ fileExists,
4881
+ result
4882
+ );
4883
+ } else if (file.sections !== void 0) {
4884
+ await this.handleSectionsUpdate(
4885
+ fullPath,
4886
+ file.sections,
4887
+ fileExists,
4888
+ result
4889
+ );
4890
+ }
4891
+ }
4892
+ async handleFullContentUpdate(fullPath, content, fileExists, result) {
4893
+ if (fileExists) {
4894
+ const existingContent = await fs4.readFile(fullPath, "utf-8");
4895
+ const commentMarker = this.extractCommentMarker(content);
4896
+ let finalContent;
4897
+ if (!commentMarker) {
4898
+ finalContent = content;
4899
+ } else {
4900
+ finalContent = this.mergeContentWithMarkers(
4901
+ existingContent,
4902
+ content,
4903
+ commentMarker
4904
+ );
4905
+ }
4906
+ await fs4.writeFile(fullPath, finalContent, "utf-8");
4907
+ result.filesUpdated++;
4908
+ } else {
4909
+ await fs4.writeFile(fullPath, content, "utf-8");
4910
+ result.filesCreated++;
4911
+ }
4912
+ }
4913
+ async handleSectionsUpdate(fullPath, sections, fileExists, result) {
4914
+ let currentContent = "";
4915
+ if (fileExists) {
4916
+ currentContent = await fs4.readFile(fullPath, "utf-8");
4917
+ }
4918
+ const mergedContent = mergeSectionsIntoFileContent(
4919
+ currentContent,
4920
+ sections
4921
+ );
4922
+ await fs4.writeFile(fullPath, mergedContent, "utf-8");
4923
+ if (fileExists) {
4924
+ result.filesUpdated++;
4925
+ } else {
4926
+ result.filesCreated++;
4927
+ }
4928
+ }
4929
+ async deleteFile(baseDirectory, filePath, result) {
4930
+ const fullPath = path4.join(baseDirectory, filePath);
4931
+ const fileExists = await this.fileExists(fullPath);
4932
+ if (fileExists) {
4933
+ await fs4.unlink(fullPath);
4934
+ result.filesDeleted++;
4935
+ }
4936
+ }
4937
+ async fileExists(filePath) {
4938
+ try {
4939
+ await fs4.access(filePath);
4940
+ return true;
4941
+ } catch {
4942
+ return false;
4943
+ }
4944
+ }
4945
+ /**
4946
+ * Extracts the comment marker from content if it's wrapped with HTML comments.
4947
+ * E.g., "<!-- start: Packmind recipes -->" returns "Packmind recipes"
4948
+ */
4949
+ extractCommentMarker(content) {
4950
+ const startMarkerPattern = /<!--\s*start:\s*([^-]+?)\s*-->/;
4951
+ const match = content.match(startMarkerPattern);
4952
+ return match ? match[1].trim() : null;
4953
+ }
4954
+ /**
4955
+ * Merges new content with existing content using comment markers.
4956
+ * If the section exists, replaces it; otherwise, appends it.
4957
+ */
4958
+ mergeContentWithMarkers(existingContent, newContent, commentMarker) {
4959
+ const startMarker = `<!-- start: ${commentMarker} -->`;
4960
+ const endMarker = `<!-- end: ${commentMarker} -->`;
4961
+ const newSectionPattern = new RegExp(
4962
+ `${this.escapeRegex(startMarker)}([\\s\\S]*?)${this.escapeRegex(endMarker)}`
4963
+ );
4964
+ const newSectionMatch = newContent.match(newSectionPattern);
4965
+ const newSectionContent = newSectionMatch ? newSectionMatch[1].trim() : newContent;
4966
+ const existingSectionPattern = new RegExp(
4967
+ `${this.escapeRegex(startMarker)}[\\s\\S]*?${this.escapeRegex(endMarker)}`,
4968
+ "g"
4969
+ );
4970
+ if (existingSectionPattern.test(existingContent)) {
4971
+ return existingContent.replace(
4972
+ existingSectionPattern,
4973
+ `${startMarker}
4974
+ ${newSectionContent}
4975
+ ${endMarker}`
4976
+ );
4977
+ } else {
4978
+ return `${existingContent}
4979
+ ${startMarker}
4980
+ ${newSectionContent}
4981
+ ${endMarker}`;
4982
+ }
4983
+ }
4984
+ /**
4985
+ * Escapes special regex characters in a string
4986
+ */
4987
+ escapeRegex(str) {
4988
+ return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
4989
+ }
4990
+ };
4991
+
4992
+ // apps/cli/src/application/useCases/ListPackagesUseCase.ts
4993
+ var ListPackagesUseCase = class {
4994
+ constructor(packmindGateway) {
4995
+ this.packmindGateway = packmindGateway;
4996
+ }
4997
+ async execute() {
4998
+ return this.packmindGateway.listPackages({});
4999
+ }
5000
+ };
5001
+
5002
+ // apps/cli/src/application/useCases/GetPackageSummaryUseCase.ts
5003
+ var GetPackageSummaryUseCase = class {
5004
+ constructor(gateway) {
5005
+ this.gateway = gateway;
5006
+ }
5007
+ async execute(command3) {
5008
+ return this.gateway.getPackageSummary(command3);
5009
+ }
5010
+ };
5011
+
5012
+ // apps/cli/src/infra/repositories/ConfigFileRepository.ts
5013
+ var fs5 = __toESM(require("fs/promises"));
5014
+ var path5 = __toESM(require("path"));
5015
+ var ConfigFileRepository = class {
5016
+ constructor() {
5017
+ this.CONFIG_FILENAME = "packmind.json";
5018
+ this.EXCLUDED_DIRECTORIES = [
5019
+ "node_modules",
5020
+ ".git",
5021
+ "dist",
5022
+ "build",
5023
+ "coverage",
5024
+ ".nx"
5025
+ ];
5026
+ }
5027
+ async writeConfig(baseDirectory, config) {
5028
+ const configPath = path5.join(baseDirectory, this.CONFIG_FILENAME);
5029
+ const configContent = JSON.stringify(config, null, 2) + "\n";
5030
+ await fs5.writeFile(configPath, configContent, "utf-8");
5031
+ }
5032
+ async readConfig(baseDirectory) {
5033
+ const configPath = path5.join(baseDirectory, this.CONFIG_FILENAME);
5034
+ try {
5035
+ const configContent = await fs5.readFile(configPath, "utf-8");
5036
+ const config = JSON.parse(configContent);
5037
+ if (!config.packages || typeof config.packages !== "object") {
5038
+ throw new Error(
5039
+ "Invalid packmind.json structure. Expected { packages: { ... } }"
5040
+ );
5041
+ }
5042
+ return config;
5043
+ } catch (error) {
5044
+ if (error.code === "ENOENT") {
5045
+ return null;
5046
+ }
5047
+ throw new Error(
5048
+ `Failed to read packmind.json: ${error.message}`
5049
+ );
5050
+ }
5051
+ }
5052
+ /**
5053
+ * Recursively finds all directories containing packmind.json in descendant folders.
5054
+ * Excludes common build/dependency directories (node_modules, .git, dist, etc.)
5055
+ *
5056
+ * @param directory - The root directory to search from
5057
+ * @returns Array of directory paths that contain a packmind.json file
5058
+ */
5059
+ async findDescendantConfigs(directory) {
5060
+ const normalizedDir = path5.resolve(directory);
5061
+ const results = [];
5062
+ const searchRecursively = async (currentDir) => {
5063
+ let entries;
5064
+ try {
5065
+ entries = await fs5.readdir(currentDir, { withFileTypes: true });
5066
+ } catch {
5067
+ return;
5068
+ }
5069
+ for (const entry of entries) {
5070
+ if (!entry.isDirectory()) {
5071
+ continue;
5072
+ }
5073
+ if (this.EXCLUDED_DIRECTORIES.includes(entry.name)) {
5074
+ continue;
5075
+ }
5076
+ const entryPath = path5.join(currentDir, entry.name);
5077
+ const config = await this.readConfig(entryPath);
5078
+ if (config) {
5079
+ results.push(entryPath);
5080
+ }
5081
+ await searchRecursively(entryPath);
5082
+ }
5083
+ };
5084
+ await searchRecursively(normalizedDir);
5085
+ return results;
5086
+ }
5087
+ /**
5088
+ * Reads all packmind.json files from startDirectory up to stopDirectory (inclusive)
5089
+ * and merges their package configurations.
5090
+ *
5091
+ * @param startDirectory - Directory to start searching from (typically the lint target)
5092
+ * @param stopDirectory - Directory to stop searching at (typically git repo root), or null to walk to filesystem root
5093
+ * @returns Merged configuration from all found packmind.json files
5094
+ */
5095
+ async readHierarchicalConfig(startDirectory, stopDirectory) {
5096
+ const configs = [];
5097
+ const configPaths = [];
5098
+ const normalizedStart = path5.resolve(startDirectory);
5099
+ const normalizedStop = stopDirectory ? path5.resolve(stopDirectory) : null;
5100
+ let currentDir = normalizedStart;
5101
+ while (true) {
5102
+ const config = await this.readConfig(currentDir);
5103
+ if (config) {
5104
+ configs.push(config);
5105
+ configPaths.push(path5.join(currentDir, this.CONFIG_FILENAME));
5106
+ }
5107
+ if (normalizedStop !== null && currentDir === normalizedStop) {
5108
+ break;
5109
+ }
5110
+ const parentDir = path5.dirname(currentDir);
5111
+ if (parentDir === currentDir) {
5112
+ break;
5113
+ }
5114
+ currentDir = parentDir;
5115
+ }
5116
+ const mergedPackages = {};
5117
+ for (const config of configs) {
5118
+ for (const [slug, version] of Object.entries(config.packages)) {
5119
+ if (!(slug in mergedPackages)) {
5120
+ mergedPackages[slug] = version;
5121
+ }
5122
+ }
5123
+ }
5124
+ return {
5125
+ packages: mergedPackages,
5126
+ configPaths,
5127
+ hasConfigs: configs.length > 0
5128
+ };
5129
+ }
5130
+ };
5131
+
5132
+ // apps/cli/src/PackmindCliHexaFactory.ts
5133
+ var PackmindCliHexaFactory = class {
5134
+ constructor(logger2) {
5135
+ this.logger = logger2;
5136
+ this.repositories = {
5137
+ packmindGateway: new PackmindGateway(
5138
+ process.env.PACKMIND_API_KEY_V3 || ""
5139
+ ),
5140
+ configFileRepository: new ConfigFileRepository()
5141
+ };
5142
+ this.services = {
5143
+ listFiles: new ListFiles(),
5144
+ gitRemoteUrlService: new GitService(this.logger),
5145
+ linterExecutionUseCase: new ExecuteLinterProgramsUseCase()
5146
+ };
5147
+ this.useCases = {
5148
+ executeSingleFileAst: new ExecuteSingleFileAstUseCase(
5149
+ this.services.linterExecutionUseCase
5150
+ ),
5151
+ getGitRemoteUrl: new GetGitRemoteUrlUseCase(),
5152
+ listFilesInDirectoryUseCase: new ListFilesInDirectoryUseCase(),
5153
+ lintFilesInDirectory: new LintFilesInDirectoryUseCase(
5154
+ this.services,
5155
+ this.repositories,
5156
+ this.logger
5157
+ ),
5158
+ lintFilesLocally: new LintFilesLocallyUseCase(
5159
+ this.services,
5160
+ this.repositories,
5161
+ this.logger
5162
+ ),
5163
+ pullData: new PullDataUseCase(this.repositories.packmindGateway),
5164
+ listPackages: new ListPackagesUseCase(this.repositories.packmindGateway),
5165
+ getPackageBySlug: new GetPackageSummaryUseCase(
5166
+ this.repositories.packmindGateway
5167
+ )
5168
+ };
5169
+ }
5170
+ };
5171
+
5172
+ // apps/cli/src/PackmindCliHexa.ts
5173
+ var origin10 = "PackmindCliHexa";
5174
+ var PackmindCliHexa = class {
5175
+ constructor(logger2 = new PackmindLogger(origin10)) {
5176
+ this.logger = logger2;
5177
+ try {
5178
+ this.hexa = new PackmindCliHexaFactory(this.logger);
5179
+ } catch (error) {
5180
+ this.logger.error("Failed to initialize PackmindCliHexa", {
5181
+ error: error instanceof Error ? error.message : String(error)
5182
+ });
5183
+ throw error;
5184
+ }
5185
+ }
5186
+ /**
5187
+ * Destroys the DeploymentsHexa and cleans up resources
5188
+ */
5189
+ destroy() {
5190
+ this.logger.info("Destroying PackmindCliHexa");
5191
+ this.logger.info("PackmindCliHexa destroyed");
5192
+ }
5193
+ async getGitRemoteUrl(command3) {
5194
+ return this.hexa.useCases.getGitRemoteUrl.execute(command3);
5195
+ }
5196
+ async executeSingleFileAst(command3) {
5197
+ return this.hexa.useCases.executeSingleFileAst.execute(command3);
5198
+ }
5199
+ async listFilesInDirectory(command3) {
5200
+ return this.hexa.useCases.listFilesInDirectoryUseCase.execute(command3);
3345
5201
  }
3346
5202
  async lintFilesInDirectory(command3) {
3347
5203
  return this.hexa.useCases.lintFilesInDirectory.execute(command3);
3348
5204
  }
5205
+ async lintFilesLocally(command3) {
5206
+ return this.hexa.useCases.lintFilesLocally.execute(command3);
5207
+ }
3349
5208
  async pullData(command3) {
3350
5209
  return this.hexa.useCases.pullData.execute(command3);
3351
5210
  }
5211
+ async listPackages(command3) {
5212
+ return this.hexa.useCases.listPackages.execute(command3);
5213
+ }
5214
+ async getPackageBySlug(command3) {
5215
+ return this.hexa.useCases.getPackageBySlug.execute(command3);
5216
+ }
5217
+ async writeConfig(baseDirectory, packagesSlugs) {
5218
+ const config = {
5219
+ packages: packagesSlugs.reduce(
5220
+ (acc, slug) => {
5221
+ acc[slug] = "*";
5222
+ return acc;
5223
+ },
5224
+ {}
5225
+ )
5226
+ };
5227
+ await this.hexa.repositories.configFileRepository.writeConfig(
5228
+ baseDirectory,
5229
+ config
5230
+ );
5231
+ }
5232
+ async readConfig(baseDirectory) {
5233
+ const config = await this.hexa.repositories.configFileRepository.readConfig(
5234
+ baseDirectory
5235
+ );
5236
+ if (!config) return [];
5237
+ const hasNonWildcardVersions = Object.values(config.packages).some(
5238
+ (version) => version !== "*"
5239
+ );
5240
+ if (hasNonWildcardVersions) {
5241
+ console.log(
5242
+ "WARN: Package versions are not supported yet, getting the latest version"
5243
+ );
5244
+ }
5245
+ return Object.keys(config.packages);
5246
+ }
5247
+ async readHierarchicalConfig(startDirectory, stopDirectory) {
5248
+ return this.hexa.repositories.configFileRepository.readHierarchicalConfig(
5249
+ startDirectory,
5250
+ stopDirectory
5251
+ );
5252
+ }
5253
+ async findDescendantConfigs(directory) {
5254
+ return this.hexa.repositories.configFileRepository.findDescendantConfigs(
5255
+ directory
5256
+ );
5257
+ }
5258
+ async getGitRepositoryRoot(directory) {
5259
+ return this.hexa.services.gitRemoteUrlService.getGitRepositoryRoot(
5260
+ directory
5261
+ );
5262
+ }
5263
+ async tryGetGitRepositoryRoot(directory) {
5264
+ return this.hexa.services.gitRemoteUrlService.tryGetGitRepositoryRoot(
5265
+ directory
5266
+ );
5267
+ }
3352
5268
  };
3353
5269
 
3354
5270
  // apps/cli/src/infra/repositories/IDELintLogger.ts
@@ -3403,6 +5319,7 @@ var HumanReadableLogger = class {
3403
5319
  };
3404
5320
 
3405
5321
  // apps/cli/src/infra/commands/LinterCommand.ts
5322
+ var pathModule = __toESM(require("path"));
3406
5323
  var Logger = {
3407
5324
  from: async (input) => {
3408
5325
  switch (input) {
@@ -3465,7 +5382,7 @@ var lintCommand = (0, import_cmd_ts.command)({
3465
5382
  description: "Enable debug logging"
3466
5383
  })
3467
5384
  },
3468
- handler: async ({ path: path5, draft, rule, debug, language, logger }) => {
5385
+ handler: async ({ path: path7, draft, rule, debug, language, logger: logger2 }) => {
3469
5386
  if (draft && !rule) {
3470
5387
  throw new Error("option --rule is required to use --draft mode");
3471
5388
  }
@@ -3475,14 +5392,53 @@ var lintCommand = (0, import_cmd_ts.command)({
3475
5392
  debug ? "debug" /* DEBUG */ : "info" /* INFO */
3476
5393
  );
3477
5394
  const packmindCliHexa = new PackmindCliHexa(packmindLogger);
3478
- const { violations } = await packmindCliHexa.lintFilesInDirectory({
3479
- path: path5 ?? ".",
3480
- draftMode: draft,
3481
- standardSlug: rule?.standardSlug,
3482
- ruleId: rule?.ruleId,
3483
- language
3484
- });
3485
- (logger === "ide" /* ide */ ? new IDELintLogger() : new HumanReadableLogger()).logViolations(violations);
5395
+ const targetPath = path7 ?? ".";
5396
+ const hasArguments = !!(draft || rule || language);
5397
+ const absolutePath = pathModule.isAbsolute(targetPath) ? targetPath : pathModule.resolve(process.cwd(), targetPath);
5398
+ let useLocalLinting = false;
5399
+ let lintTargets = [];
5400
+ if (!hasArguments) {
5401
+ const stopDirectory = await packmindCliHexa.tryGetGitRepositoryRoot(absolutePath);
5402
+ const hierarchicalConfig = await packmindCliHexa.readHierarchicalConfig(
5403
+ absolutePath,
5404
+ stopDirectory
5405
+ );
5406
+ if (hierarchicalConfig.hasConfigs) {
5407
+ useLocalLinting = true;
5408
+ const rootConfig = await packmindCliHexa.readHierarchicalConfig(
5409
+ absolutePath,
5410
+ absolutePath
5411
+ );
5412
+ if (rootConfig.hasConfigs) {
5413
+ lintTargets.push(absolutePath);
5414
+ }
5415
+ const descendantTargets = await packmindCliHexa.findDescendantConfigs(absolutePath);
5416
+ lintTargets = [...lintTargets, ...descendantTargets];
5417
+ if (lintTargets.length === 0) {
5418
+ lintTargets.push(absolutePath);
5419
+ }
5420
+ }
5421
+ }
5422
+ let violations = [];
5423
+ if (useLocalLinting && lintTargets.length > 0) {
5424
+ for (const target of lintTargets) {
5425
+ packmindLogger.debug(`Linting target: ${target}`);
5426
+ const result = await packmindCliHexa.lintFilesLocally({
5427
+ path: target
5428
+ });
5429
+ violations = [...violations, ...result.violations];
5430
+ }
5431
+ } else {
5432
+ const result = await packmindCliHexa.lintFilesInDirectory({
5433
+ path: targetPath,
5434
+ draftMode: draft,
5435
+ standardSlug: rule?.standardSlug,
5436
+ ruleId: rule?.ruleId,
5437
+ language
5438
+ });
5439
+ violations = result.violations;
5440
+ }
5441
+ (logger2 === "ide" /* ide */ ? new IDELintLogger() : new HumanReadableLogger()).logViolations(violations);
3486
5442
  const durationSeconds = (Date.now() - startedAt) / 1e3;
3487
5443
  console.log(`Lint completed in ${durationSeconds.toFixed(2)}s`);
3488
5444
  if (violations.length > 0) {
@@ -3549,27 +5505,158 @@ function extractWasmFiles() {
3549
5505
 
3550
5506
  // apps/cli/src/main.ts
3551
5507
  var import_dotenv = require("dotenv");
3552
- var fs4 = __toESM(require("fs"));
3553
- var path4 = __toESM(require("path"));
5508
+ var fs6 = __toESM(require("fs"));
5509
+ var path6 = __toESM(require("path"));
3554
5510
 
3555
5511
  // apps/cli/src/infra/commands/PullCommand.ts
3556
5512
  var import_cmd_ts2 = require("cmd-ts");
3557
5513
  var pullCommand = (0, import_cmd_ts2.command)({
3558
5514
  name: "pull",
3559
- description: "Pull recipes and standards from Packmind and save them to the current directory",
3560
- args: {},
3561
- handler: async () => {
3562
- console.log("Pulling content from Packmind...");
5515
+ description: "Pull recipes and standards from specified packages and save them to the current directory",
5516
+ args: {
5517
+ list: (0, import_cmd_ts2.flag)({
5518
+ long: "list",
5519
+ description: "List available packages"
5520
+ }),
5521
+ show: (0, import_cmd_ts2.option)({
5522
+ type: import_cmd_ts2.string,
5523
+ long: "show",
5524
+ description: "Show details of a specific package",
5525
+ defaultValue: () => ""
5526
+ }),
5527
+ packagesSlugs: (0, import_cmd_ts2.restPositionals)({
5528
+ type: import_cmd_ts2.string,
5529
+ displayName: "packages",
5530
+ description: "Package slugs to pull content from (e.g., backend frontend)"
5531
+ })
5532
+ },
5533
+ handler: async ({ list, show, packagesSlugs }) => {
3563
5534
  const packmindLogger = new PackmindLogger("PackmindCLI", "info" /* INFO */);
3564
5535
  const packmindCliHexa = new PackmindCliHexa(packmindLogger);
5536
+ if (list) {
5537
+ try {
5538
+ console.log("Fetching available packages...\n");
5539
+ const packages = await packmindCliHexa.listPackages({});
5540
+ if (packages.length === 0) {
5541
+ console.log("No packages found.");
5542
+ process.exit(0);
5543
+ }
5544
+ console.log("Available packages:");
5545
+ packages.forEach((pkg) => {
5546
+ console.log(`- ${pkg.name} (${pkg.slug})`);
5547
+ if (pkg.description) {
5548
+ console.log(` ${pkg.description}`);
5549
+ console.log("");
5550
+ }
5551
+ });
5552
+ process.exit(0);
5553
+ } catch (error) {
5554
+ console.error("\n\u274C Failed to list packages:");
5555
+ if (error instanceof Error) {
5556
+ console.error(` ${error.message}`);
5557
+ } else {
5558
+ console.error(` ${String(error)}`);
5559
+ }
5560
+ process.exit(1);
5561
+ }
5562
+ }
5563
+ if (show) {
5564
+ try {
5565
+ console.log(`Fetching package details for '${show}'...
5566
+ `);
5567
+ const pkg = await packmindCliHexa.getPackageBySlug({ slug: show });
5568
+ console.log(`${pkg.name} (${pkg.slug}):
5569
+ `);
5570
+ if (pkg.description) {
5571
+ console.log(`${pkg.description}
5572
+ `);
5573
+ }
5574
+ if (pkg.standards && pkg.standards.length > 0) {
5575
+ console.log("Standards:");
5576
+ pkg.standards.forEach((standard) => {
5577
+ if (standard.summary) {
5578
+ console.log(` - ${standard.name}: ${standard.summary}`);
5579
+ } else {
5580
+ console.log(` - ${standard.name}`);
5581
+ }
5582
+ });
5583
+ console.log("");
5584
+ }
5585
+ if (pkg.recipes && pkg.recipes.length > 0) {
5586
+ console.log("Recipes:");
5587
+ pkg.recipes.forEach((recipe) => {
5588
+ if (recipe.summary) {
5589
+ console.log(` - ${recipe.name}: ${recipe.summary}`);
5590
+ } else {
5591
+ console.log(` - ${recipe.name}`);
5592
+ }
5593
+ });
5594
+ console.log("");
5595
+ }
5596
+ process.exit(0);
5597
+ } catch (error) {
5598
+ console.error("\n\u274C Failed to fetch package details:");
5599
+ if (error instanceof Error) {
5600
+ console.error(` ${error.message}`);
5601
+ } else {
5602
+ console.error(` ${String(error)}`);
5603
+ }
5604
+ process.exit(1);
5605
+ }
5606
+ }
5607
+ let configPackages;
5608
+ let configExists = false;
3565
5609
  try {
5610
+ configPackages = await packmindCliHexa.readConfig(process.cwd());
5611
+ configExists = configPackages.length > 0;
5612
+ } catch (error) {
5613
+ console.error("ERROR Failed to parse packmind.json");
5614
+ if (error instanceof Error) {
5615
+ console.error(`ERROR ${error.message}`);
5616
+ } else {
5617
+ console.error(`ERROR ${String(error)}`);
5618
+ }
5619
+ console.error(
5620
+ "\n\u{1F4A1} Please fix the packmind.json file or delete it to continue."
5621
+ );
5622
+ process.exit(1);
5623
+ }
5624
+ const allPackages = [.../* @__PURE__ */ new Set([...configPackages, ...packagesSlugs])];
5625
+ if (allPackages.length === 0) {
5626
+ console.log("WARN config packmind.json not found");
5627
+ console.log(
5628
+ "Usage: packmind-cli install <package-slug> [package-slug...]"
5629
+ );
5630
+ console.log(" packmind-cli install --list");
5631
+ console.log("");
5632
+ console.log("Examples:");
5633
+ console.log(" packmind-cli install backend");
5634
+ console.log(" packmind-cli install backend frontend");
5635
+ console.log(" packmind-cli install --list # Show available packages");
5636
+ console.log("");
5637
+ console.log("Install recipes and standards from the specified packages.");
5638
+ process.exit(0);
5639
+ }
5640
+ if (!configExists && packagesSlugs.length > 0) {
5641
+ console.log("INFO initializing packmind.json");
5642
+ }
5643
+ try {
5644
+ const packageCount = allPackages.length;
5645
+ const packageWord = packageCount === 1 ? "package" : "packages";
5646
+ console.log(
5647
+ `Fetching ${packageCount} ${packageWord}: ${allPackages.join(", ")}...`
5648
+ );
3566
5649
  const result = await packmindCliHexa.pullData({
3567
- baseDirectory: process.cwd()
5650
+ baseDirectory: process.cwd(),
5651
+ packagesSlugs: allPackages
3568
5652
  });
3569
- console.log("\n\u2705 Pull completed successfully!");
3570
- console.log(` Files created: ${result.filesCreated}`);
3571
- console.log(` Files updated: ${result.filesUpdated}`);
3572
- console.log(` Files deleted: ${result.filesDeleted}`);
5653
+ console.log(
5654
+ `Installing ${result.recipesCount} recipes and ${result.standardsCount} standards...`
5655
+ );
5656
+ console.log(
5657
+ `
5658
+ added ${result.filesCreated} files, changed ${result.filesUpdated} files, removed ${result.filesDeleted} files`
5659
+ );
3573
5660
  if (result.errors.length > 0) {
3574
5661
  console.log("\n\u26A0\uFE0F Errors encountered:");
3575
5662
  result.errors.forEach((error) => {
@@ -3577,41 +5664,102 @@ var pullCommand = (0, import_cmd_ts2.command)({
3577
5664
  });
3578
5665
  process.exit(1);
3579
5666
  }
5667
+ await packmindCliHexa.writeConfig(process.cwd(), allPackages);
3580
5668
  } catch (error) {
3581
- console.error("\n\u274C Failed to pull content:");
3582
- console.error(
3583
- ` ${error instanceof Error ? error.message : String(error)}`
3584
- );
5669
+ console.error("\n\u274C Failed to install content:");
5670
+ if (error instanceof Error) {
5671
+ const errorObj = error;
5672
+ if (errorObj.statusCode === 404) {
5673
+ console.error(` ${errorObj.message}`);
5674
+ if (configExists && configPackages.length > 0) {
5675
+ const missingPackages = allPackages.filter(
5676
+ (pkg) => configPackages.includes(pkg)
5677
+ );
5678
+ if (missingPackages.length > 0) {
5679
+ console.error(
5680
+ "\n\u{1F4A1} Either remove the following package(s) from packmind.json:"
5681
+ );
5682
+ missingPackages.forEach((pkg) => {
5683
+ console.error(` "${pkg}"`);
5684
+ });
5685
+ console.error(" Or ensure that:");
5686
+ console.error(
5687
+ " - The package slug exists and is correctly spelled"
5688
+ );
5689
+ console.error(" - The package exists in your organization");
5690
+ console.error(" - You have the correct API key configured");
5691
+ } else {
5692
+ console.error("\n\u{1F4A1} Troubleshooting tips:");
5693
+ console.error(
5694
+ " - Check if the package slug exists and is correctly spelled"
5695
+ );
5696
+ console.error(
5697
+ " - Check that the package exists in your organization"
5698
+ );
5699
+ console.error(
5700
+ " - Ensure you have the correct API key configured"
5701
+ );
5702
+ }
5703
+ } else {
5704
+ console.error("\n\u{1F4A1} Troubleshooting tips:");
5705
+ console.error(
5706
+ " - Check if the package slug exists and is correctly spelled"
5707
+ );
5708
+ console.error(
5709
+ " - Check that the package exists in your organization"
5710
+ );
5711
+ console.error(
5712
+ " - Ensure you have the correct API key configured"
5713
+ );
5714
+ }
5715
+ } else {
5716
+ console.error(` ${errorObj.message}`);
5717
+ const apiErrorObj = error;
5718
+ if (apiErrorObj.response?.data?.message) {
5719
+ console.error(`
5720
+ Details: ${apiErrorObj.response.data.message}`);
5721
+ }
5722
+ console.error("\n\u{1F4A1} Troubleshooting tips:");
5723
+ console.error(" - Verify that the package slugs are correct");
5724
+ console.error(
5725
+ " - Check that the packages exist in your organization"
5726
+ );
5727
+ console.error(" - Ensure you have the correct API key configured");
5728
+ }
5729
+ } else {
5730
+ console.error(` ${String(error)}`);
5731
+ }
3585
5732
  process.exit(1);
3586
5733
  }
3587
5734
  }
3588
5735
  });
3589
5736
 
3590
5737
  // apps/cli/src/main.ts
5738
+ var { version: CLI_VERSION } = require_package();
3591
5739
  function findEnvFile() {
3592
5740
  let currentDir = process.cwd();
3593
5741
  const startDir = currentDir;
3594
5742
  let gitRootFound = false;
3595
5743
  let searchDir = currentDir;
3596
- while (searchDir !== path4.parse(searchDir).root) {
3597
- if (fs4.existsSync(path4.join(searchDir, ".git"))) {
5744
+ while (searchDir !== path6.parse(searchDir).root) {
5745
+ if (fs6.existsSync(path6.join(searchDir, ".git"))) {
3598
5746
  gitRootFound = true;
3599
5747
  break;
3600
5748
  }
3601
- searchDir = path4.dirname(searchDir);
5749
+ searchDir = path6.dirname(searchDir);
3602
5750
  }
3603
- while (currentDir !== path4.parse(currentDir).root) {
3604
- const envPath2 = path4.join(currentDir, ".env");
3605
- if (fs4.existsSync(envPath2)) {
5751
+ while (currentDir !== path6.parse(currentDir).root) {
5752
+ const envPath2 = path6.join(currentDir, ".env");
5753
+ if (fs6.existsSync(envPath2)) {
3606
5754
  return envPath2;
3607
5755
  }
3608
- if (gitRootFound && fs4.existsSync(path4.join(currentDir, ".git"))) {
5756
+ if (gitRootFound && fs6.existsSync(path6.join(currentDir, ".git"))) {
3609
5757
  break;
3610
5758
  }
3611
5759
  if (!gitRootFound && currentDir !== startDir) {
3612
5760
  break;
3613
5761
  }
3614
- currentDir = path4.dirname(currentDir);
5762
+ currentDir = path6.dirname(currentDir);
3615
5763
  }
3616
5764
  return null;
3617
5765
  }
@@ -3626,15 +5774,21 @@ if (hasEmbeddedWasmFiles()) {
3626
5774
  } catch {
3627
5775
  }
3628
5776
  }
5777
+ var args = process.argv.slice(2);
5778
+ if (args.includes("--version") || args.includes("-v")) {
5779
+ console.log(`packmind-cli version ${CLI_VERSION}`);
5780
+ process.exit(0);
5781
+ }
3629
5782
  var app = (0, import_cmd_ts3.subcommands)({
3630
5783
  name: "packmind-cli",
3631
5784
  description: "Packmind CLI tool",
3632
5785
  cmds: {
3633
5786
  lint: lintCommand,
3634
- pull: pullCommand
5787
+ pull: pullCommand,
5788
+ install: pullCommand
3635
5789
  }
3636
5790
  });
3637
- (0, import_cmd_ts3.run)(app, process.argv.slice(2)).catch((error) => {
5791
+ (0, import_cmd_ts3.run)(app, args).catch((error) => {
3638
5792
  console.error(import_chalk2.default.bgRed.bold("packmind-cli"), import_chalk2.default.red(error.message));
3639
5793
  process.exit(1);
3640
5794
  });