@packmind/cli 0.4.0 → 0.6.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.
- package/main.cjs +1613 -194
- package/package.json +1 -1
package/main.cjs
CHANGED
|
@@ -38,7 +38,7 @@ var require_package = __commonJS({
|
|
|
38
38
|
"apps/cli/package.json"(exports2, module2) {
|
|
39
39
|
module2.exports = {
|
|
40
40
|
name: "@packmind/cli",
|
|
41
|
-
version: "0.
|
|
41
|
+
version: "0.6.0",
|
|
42
42
|
description: "A command-line interface for Packmind linting and code quality checks",
|
|
43
43
|
private: false,
|
|
44
44
|
bin: {
|
|
@@ -76,7 +76,6 @@ var require_package = __commonJS({
|
|
|
76
76
|
});
|
|
77
77
|
|
|
78
78
|
// apps/cli/src/main.ts
|
|
79
|
-
var import_chalk2 = __toESM(require("chalk"));
|
|
80
79
|
var import_cmd_ts3 = require("cmd-ts");
|
|
81
80
|
|
|
82
81
|
// apps/cli/src/infra/commands/LinterCommand.ts
|
|
@@ -258,39 +257,64 @@ var createRuleId = brandedIdFactory();
|
|
|
258
257
|
// packages/types/src/standards/RuleExampleId.ts
|
|
259
258
|
var createRuleExampleId = brandedIdFactory();
|
|
260
259
|
|
|
260
|
+
// packages/types/src/events/PackmindEvent.ts
|
|
261
|
+
var PackmindEvent = class {
|
|
262
|
+
constructor(payload) {
|
|
263
|
+
this.payload = payload;
|
|
264
|
+
}
|
|
265
|
+
/**
|
|
266
|
+
* Get the event name from the static property.
|
|
267
|
+
* Used internally by the event emitter.
|
|
268
|
+
*/
|
|
269
|
+
get name() {
|
|
270
|
+
return this.constructor.eventName;
|
|
271
|
+
}
|
|
272
|
+
};
|
|
273
|
+
|
|
274
|
+
// packages/types/src/events/UserEvent.ts
|
|
275
|
+
var UserEvent = class extends PackmindEvent {
|
|
276
|
+
};
|
|
277
|
+
|
|
278
|
+
// packages/types/src/standards/events/StandardUpdatedEvent.ts
|
|
279
|
+
var StandardUpdatedEvent = class extends UserEvent {
|
|
280
|
+
static {
|
|
281
|
+
this.eventName = "standards.standard.updated";
|
|
282
|
+
}
|
|
283
|
+
};
|
|
284
|
+
|
|
261
285
|
// packages/types/src/languages/ProgrammingLanguage.ts
|
|
262
|
-
var ProgrammingLanguage = /* @__PURE__ */ ((
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
return
|
|
286
|
+
var ProgrammingLanguage = /* @__PURE__ */ ((ProgrammingLanguage4) => {
|
|
287
|
+
ProgrammingLanguage4["JAVASCRIPT"] = "JAVASCRIPT";
|
|
288
|
+
ProgrammingLanguage4["JAVASCRIPT_JSX"] = "JAVASCRIPT_JSX";
|
|
289
|
+
ProgrammingLanguage4["TYPESCRIPT"] = "TYPESCRIPT";
|
|
290
|
+
ProgrammingLanguage4["TYPESCRIPT_TSX"] = "TYPESCRIPT_TSX";
|
|
291
|
+
ProgrammingLanguage4["PYTHON"] = "PYTHON";
|
|
292
|
+
ProgrammingLanguage4["PHP"] = "PHP";
|
|
293
|
+
ProgrammingLanguage4["JAVA"] = "JAVA";
|
|
294
|
+
ProgrammingLanguage4["SCSS"] = "SCSS";
|
|
295
|
+
ProgrammingLanguage4["HTML"] = "HTML";
|
|
296
|
+
ProgrammingLanguage4["CSHARP"] = "CSHARP";
|
|
297
|
+
ProgrammingLanguage4["GENERIC"] = "GENERIC";
|
|
298
|
+
ProgrammingLanguage4["GO"] = "GO";
|
|
299
|
+
ProgrammingLanguage4["C"] = "C";
|
|
300
|
+
ProgrammingLanguage4["CPP"] = "CPP";
|
|
301
|
+
ProgrammingLanguage4["SQL"] = "SQL";
|
|
302
|
+
ProgrammingLanguage4["KOTLIN"] = "KOTLIN";
|
|
303
|
+
ProgrammingLanguage4["VUE"] = "VUE";
|
|
304
|
+
ProgrammingLanguage4["CSS"] = "CSS";
|
|
305
|
+
ProgrammingLanguage4["YAML"] = "YAML";
|
|
306
|
+
ProgrammingLanguage4["JSON"] = "JSON";
|
|
307
|
+
ProgrammingLanguage4["XML"] = "XML";
|
|
308
|
+
ProgrammingLanguage4["BASH"] = "BASH";
|
|
309
|
+
ProgrammingLanguage4["MARKDOWN"] = "MARKDOWN";
|
|
310
|
+
ProgrammingLanguage4["RUBY"] = "RUBY";
|
|
311
|
+
ProgrammingLanguage4["RUST"] = "RUST";
|
|
312
|
+
ProgrammingLanguage4["SAP_ABAP"] = "SAP_ABAP";
|
|
313
|
+
ProgrammingLanguage4["SAP_CDS"] = "SAP_CDS";
|
|
314
|
+
ProgrammingLanguage4["SAP_HANA_SQL"] = "SAP_HANA_SQL";
|
|
315
|
+
ProgrammingLanguage4["SWIFT"] = "SWIFT";
|
|
316
|
+
ProgrammingLanguage4["PROPERTIES"] = "PROPERTIES";
|
|
317
|
+
return ProgrammingLanguage4;
|
|
294
318
|
})(ProgrammingLanguage || {});
|
|
295
319
|
var ProgrammingLanguageDetails = {
|
|
296
320
|
["GENERIC" /* GENERIC */]: {
|
|
@@ -458,6 +482,262 @@ var createDetectionProgramId = brandedIdFactory();
|
|
|
458
482
|
// packages/types/src/linter/RuleDetectionAssessment.ts
|
|
459
483
|
var createRuleDetectionAssessmentId = brandedIdFactory();
|
|
460
484
|
|
|
485
|
+
// packages/types/src/llm/LLMProviderMetadata.ts
|
|
486
|
+
var DEFAULT_OPENAI_MODELS = {
|
|
487
|
+
model: "gpt-5.1",
|
|
488
|
+
fastestModel: "gpt-4.1-mini"
|
|
489
|
+
};
|
|
490
|
+
var DEFAULT_ANTHROPIC_MODELS = {
|
|
491
|
+
model: "claude-sonnet-4-5-20250929",
|
|
492
|
+
fastestModel: "claude-haiku-4-5-20251001"
|
|
493
|
+
};
|
|
494
|
+
var DEFAULT_GEMINI_MODELS = {
|
|
495
|
+
model: "gemini-3-pro-preview",
|
|
496
|
+
fastestModel: "gemini-2.5-flash"
|
|
497
|
+
};
|
|
498
|
+
var DEFAULT_AZURE_OPENAI_API_VERSION = "2024-12-01-preview";
|
|
499
|
+
var LLM_PROVIDER_METADATA = {
|
|
500
|
+
["openai" /* OPENAI */]: {
|
|
501
|
+
id: "openai" /* OPENAI */,
|
|
502
|
+
displayName: "OpenAI",
|
|
503
|
+
description: "OpenAI GPT models including GPT-4 and GPT-5. Requires an API key from OpenAI.",
|
|
504
|
+
defaultModel: DEFAULT_OPENAI_MODELS.model,
|
|
505
|
+
defaultFastModel: DEFAULT_OPENAI_MODELS.fastestModel,
|
|
506
|
+
documentationUrl: "https://platform.openai.com/docs",
|
|
507
|
+
fields: [
|
|
508
|
+
{
|
|
509
|
+
name: "apiKey",
|
|
510
|
+
label: "API Key",
|
|
511
|
+
type: "password",
|
|
512
|
+
defaultValue: "",
|
|
513
|
+
helpMessage: "Your OpenAI API key from platform.openai.com.",
|
|
514
|
+
optional: false,
|
|
515
|
+
placeholder: "sk-...",
|
|
516
|
+
secret: true
|
|
517
|
+
},
|
|
518
|
+
{
|
|
519
|
+
name: "model",
|
|
520
|
+
label: "Model",
|
|
521
|
+
type: "text",
|
|
522
|
+
defaultValue: DEFAULT_OPENAI_MODELS.model,
|
|
523
|
+
helpMessage: "The primary model to use for standard operations. Defaults to the latest recommended model.",
|
|
524
|
+
optional: true,
|
|
525
|
+
placeholder: "gpt-5.1",
|
|
526
|
+
secret: false
|
|
527
|
+
},
|
|
528
|
+
{
|
|
529
|
+
name: "fastestModel",
|
|
530
|
+
label: "Fast Model",
|
|
531
|
+
type: "text",
|
|
532
|
+
defaultValue: DEFAULT_OPENAI_MODELS.fastestModel,
|
|
533
|
+
helpMessage: "A faster, more economical model for less complex operations.",
|
|
534
|
+
optional: true,
|
|
535
|
+
placeholder: "gpt-4.1-mini",
|
|
536
|
+
secret: false
|
|
537
|
+
}
|
|
538
|
+
]
|
|
539
|
+
},
|
|
540
|
+
["anthropic" /* ANTHROPIC */]: {
|
|
541
|
+
id: "anthropic" /* ANTHROPIC */,
|
|
542
|
+
displayName: "Anthropic Claude",
|
|
543
|
+
description: "Anthropic Claude models known for safety and helpfulness. Requires an API key from Anthropic.",
|
|
544
|
+
defaultModel: DEFAULT_ANTHROPIC_MODELS.model,
|
|
545
|
+
defaultFastModel: DEFAULT_ANTHROPIC_MODELS.fastestModel,
|
|
546
|
+
documentationUrl: "https://docs.anthropic.com",
|
|
547
|
+
fields: [
|
|
548
|
+
{
|
|
549
|
+
name: "apiKey",
|
|
550
|
+
label: "API Key",
|
|
551
|
+
type: "password",
|
|
552
|
+
defaultValue: "",
|
|
553
|
+
helpMessage: "Your Anthropic API key from console.anthropic.com.",
|
|
554
|
+
optional: false,
|
|
555
|
+
placeholder: "sk-ant-...",
|
|
556
|
+
secret: true
|
|
557
|
+
},
|
|
558
|
+
{
|
|
559
|
+
name: "model",
|
|
560
|
+
label: "Model",
|
|
561
|
+
type: "text",
|
|
562
|
+
defaultValue: DEFAULT_ANTHROPIC_MODELS.model,
|
|
563
|
+
helpMessage: "The primary Claude model to use. Defaults to the latest Sonnet model.",
|
|
564
|
+
optional: true,
|
|
565
|
+
placeholder: "claude-sonnet-4-5-20250929",
|
|
566
|
+
secret: false
|
|
567
|
+
},
|
|
568
|
+
{
|
|
569
|
+
name: "fastestModel",
|
|
570
|
+
label: "Fast Model",
|
|
571
|
+
type: "text",
|
|
572
|
+
defaultValue: DEFAULT_ANTHROPIC_MODELS.fastestModel,
|
|
573
|
+
helpMessage: "A faster Claude model for less complex operations. Defaults to Haiku.",
|
|
574
|
+
optional: true,
|
|
575
|
+
placeholder: "claude-haiku-4-5-20251001",
|
|
576
|
+
secret: false
|
|
577
|
+
}
|
|
578
|
+
]
|
|
579
|
+
},
|
|
580
|
+
["gemini" /* GEMINI */]: {
|
|
581
|
+
id: "gemini" /* GEMINI */,
|
|
582
|
+
displayName: "Google Gemini",
|
|
583
|
+
description: "Google's Gemini models with multimodal capabilities. Requires an API key from Google AI Studio.",
|
|
584
|
+
defaultModel: DEFAULT_GEMINI_MODELS.model,
|
|
585
|
+
defaultFastModel: DEFAULT_GEMINI_MODELS.fastestModel,
|
|
586
|
+
documentationUrl: "https://ai.google.dev/docs",
|
|
587
|
+
fields: [
|
|
588
|
+
{
|
|
589
|
+
name: "apiKey",
|
|
590
|
+
label: "API Key",
|
|
591
|
+
type: "password",
|
|
592
|
+
defaultValue: "",
|
|
593
|
+
helpMessage: "Your Google AI API key from aistudio.google.com.",
|
|
594
|
+
optional: false,
|
|
595
|
+
placeholder: "AIza...",
|
|
596
|
+
secret: true
|
|
597
|
+
},
|
|
598
|
+
{
|
|
599
|
+
name: "model",
|
|
600
|
+
label: "Model",
|
|
601
|
+
type: "text",
|
|
602
|
+
defaultValue: DEFAULT_GEMINI_MODELS.model,
|
|
603
|
+
helpMessage: "The primary Gemini model to use. Defaults to the latest Pro model.",
|
|
604
|
+
optional: true,
|
|
605
|
+
placeholder: "gemini-3-pro-preview",
|
|
606
|
+
secret: false
|
|
607
|
+
},
|
|
608
|
+
{
|
|
609
|
+
name: "fastestModel",
|
|
610
|
+
label: "Fast Model",
|
|
611
|
+
type: "text",
|
|
612
|
+
defaultValue: DEFAULT_GEMINI_MODELS.fastestModel,
|
|
613
|
+
helpMessage: "A faster Gemini model for less complex operations. Defaults to Flash.",
|
|
614
|
+
optional: true,
|
|
615
|
+
placeholder: "gemini-2.5-flash",
|
|
616
|
+
secret: false
|
|
617
|
+
}
|
|
618
|
+
]
|
|
619
|
+
},
|
|
620
|
+
["azure-openai" /* AZURE_OPENAI */]: {
|
|
621
|
+
id: "azure-openai" /* AZURE_OPENAI */,
|
|
622
|
+
displayName: "Azure OpenAI",
|
|
623
|
+
description: "Microsoft Azure-hosted OpenAI models. Requires Azure deployment names and credentials.",
|
|
624
|
+
defaultModel: "",
|
|
625
|
+
defaultFastModel: "",
|
|
626
|
+
documentationUrl: "https://learn.microsoft.com/en-us/azure/ai-services/openai/",
|
|
627
|
+
fields: [
|
|
628
|
+
{
|
|
629
|
+
name: "model",
|
|
630
|
+
label: "Model Deployment Name",
|
|
631
|
+
type: "text",
|
|
632
|
+
defaultValue: "",
|
|
633
|
+
helpMessage: "The Azure deployment name for the primary model. This is the name you gave your deployment in Azure Portal.",
|
|
634
|
+
optional: false,
|
|
635
|
+
placeholder: "my-gpt-4-deployment",
|
|
636
|
+
secret: false
|
|
637
|
+
},
|
|
638
|
+
{
|
|
639
|
+
name: "fastestModel",
|
|
640
|
+
label: "Fast Model Deployment Name",
|
|
641
|
+
type: "text",
|
|
642
|
+
defaultValue: "",
|
|
643
|
+
helpMessage: "The Azure deployment name for the fast/economical model.",
|
|
644
|
+
optional: false,
|
|
645
|
+
placeholder: "my-gpt-35-turbo-deployment",
|
|
646
|
+
secret: false
|
|
647
|
+
},
|
|
648
|
+
{
|
|
649
|
+
name: "endpoint",
|
|
650
|
+
label: "Endpoint URL",
|
|
651
|
+
type: "url",
|
|
652
|
+
defaultValue: "",
|
|
653
|
+
helpMessage: "Your Azure OpenAI endpoint URL.",
|
|
654
|
+
optional: false,
|
|
655
|
+
placeholder: "https://your-resource.openai.azure.com",
|
|
656
|
+
secret: false
|
|
657
|
+
},
|
|
658
|
+
{
|
|
659
|
+
name: "apiKey",
|
|
660
|
+
label: "API Key",
|
|
661
|
+
type: "password",
|
|
662
|
+
defaultValue: "",
|
|
663
|
+
helpMessage: "Your Azure OpenAI API key.",
|
|
664
|
+
optional: false,
|
|
665
|
+
placeholder: "",
|
|
666
|
+
secret: true
|
|
667
|
+
},
|
|
668
|
+
{
|
|
669
|
+
name: "apiVersion",
|
|
670
|
+
label: "API Version",
|
|
671
|
+
type: "text",
|
|
672
|
+
defaultValue: DEFAULT_AZURE_OPENAI_API_VERSION,
|
|
673
|
+
helpMessage: "The Azure OpenAI API version to use. Defaults to the latest stable version.",
|
|
674
|
+
optional: true,
|
|
675
|
+
placeholder: "2024-12-01-preview",
|
|
676
|
+
secret: false
|
|
677
|
+
}
|
|
678
|
+
]
|
|
679
|
+
},
|
|
680
|
+
["openai-compatible" /* OPENAI_COMPATIBLE */]: {
|
|
681
|
+
id: "openai-compatible" /* OPENAI_COMPATIBLE */,
|
|
682
|
+
displayName: "OpenAI-Compatible",
|
|
683
|
+
description: "Any OpenAI-compatible API endpoint. Use this for local models (Ollama, LM Studio) or other compatible providers.",
|
|
684
|
+
defaultModel: "",
|
|
685
|
+
defaultFastModel: "",
|
|
686
|
+
documentationUrl: void 0,
|
|
687
|
+
fields: [
|
|
688
|
+
{
|
|
689
|
+
name: "llmEndpoint",
|
|
690
|
+
label: "Endpoint URL",
|
|
691
|
+
type: "url",
|
|
692
|
+
defaultValue: "",
|
|
693
|
+
helpMessage: "The base URL of the OpenAI-compatible API endpoint (e.g., http://localhost:11434/v1 for Ollama).",
|
|
694
|
+
optional: false,
|
|
695
|
+
placeholder: "http://localhost:11434/v1",
|
|
696
|
+
secret: false
|
|
697
|
+
},
|
|
698
|
+
{
|
|
699
|
+
name: "llmApiKey",
|
|
700
|
+
label: "API Key",
|
|
701
|
+
type: "password",
|
|
702
|
+
defaultValue: "",
|
|
703
|
+
helpMessage: "API key for authentication. Some local providers may not require this.",
|
|
704
|
+
optional: false,
|
|
705
|
+
placeholder: "",
|
|
706
|
+
secret: true
|
|
707
|
+
},
|
|
708
|
+
{
|
|
709
|
+
name: "model",
|
|
710
|
+
label: "Model",
|
|
711
|
+
type: "text",
|
|
712
|
+
defaultValue: "",
|
|
713
|
+
helpMessage: "The model identifier to use for standard operations (e.g., llama3, mistral).",
|
|
714
|
+
optional: false,
|
|
715
|
+
placeholder: "llama3",
|
|
716
|
+
secret: false
|
|
717
|
+
},
|
|
718
|
+
{
|
|
719
|
+
name: "fastestModel",
|
|
720
|
+
label: "Fast Model",
|
|
721
|
+
type: "text",
|
|
722
|
+
defaultValue: "",
|
|
723
|
+
helpMessage: "A faster model for less complex operations. Can be the same as the primary model.",
|
|
724
|
+
optional: false,
|
|
725
|
+
placeholder: "llama3",
|
|
726
|
+
secret: false
|
|
727
|
+
}
|
|
728
|
+
]
|
|
729
|
+
},
|
|
730
|
+
["packmind" /* PACKMIND */]: {
|
|
731
|
+
id: "packmind" /* PACKMIND */,
|
|
732
|
+
displayName: "Packmind (SaaS)",
|
|
733
|
+
description: "Packmind managed LLM service. Uses the platform default provider configuration.",
|
|
734
|
+
defaultModel: DEFAULT_OPENAI_MODELS.model,
|
|
735
|
+
defaultFastModel: DEFAULT_OPENAI_MODELS.fastestModel,
|
|
736
|
+
documentationUrl: void 0,
|
|
737
|
+
fields: []
|
|
738
|
+
}
|
|
739
|
+
};
|
|
740
|
+
|
|
461
741
|
// packages/types/src/sse/SSEEvent.ts
|
|
462
742
|
function createProgramStatusChangeEvent(ruleId, language) {
|
|
463
743
|
return {
|
|
@@ -537,33 +817,53 @@ var ExecuteSingleFileAstUseCase = class _ExecuteSingleFileAstUseCase {
|
|
|
537
817
|
// apps/cli/src/application/services/GitService.ts
|
|
538
818
|
var import_child_process = require("child_process");
|
|
539
819
|
var import_util = require("util");
|
|
820
|
+
var path = __toESM(require("path"));
|
|
540
821
|
var execAsync = (0, import_util.promisify)(import_child_process.exec);
|
|
541
822
|
var origin = "GitService";
|
|
542
823
|
var GitService = class {
|
|
543
824
|
constructor(logger2 = new PackmindLogger(origin)) {
|
|
544
825
|
this.logger = logger2;
|
|
545
826
|
}
|
|
546
|
-
async getGitRepositoryRoot(
|
|
827
|
+
async getGitRepositoryRoot(path8) {
|
|
547
828
|
try {
|
|
548
829
|
const { stdout } = await execAsync("git rev-parse --show-toplevel", {
|
|
549
|
-
cwd:
|
|
830
|
+
cwd: path8
|
|
550
831
|
});
|
|
551
832
|
const gitRoot = stdout.trim();
|
|
552
833
|
this.logger.debug("Resolved git repository root", {
|
|
553
|
-
inputPath:
|
|
834
|
+
inputPath: path8,
|
|
554
835
|
gitRoot
|
|
555
836
|
});
|
|
556
837
|
return gitRoot;
|
|
557
838
|
} catch (error) {
|
|
558
839
|
if (error instanceof Error) {
|
|
559
840
|
throw new Error(
|
|
560
|
-
`Failed to get Git repository root. The path '${
|
|
841
|
+
`Failed to get Git repository root. The path '${path8}' does not appear to be inside a Git repository.
|
|
561
842
|
${error.message}`
|
|
562
843
|
);
|
|
563
844
|
}
|
|
564
845
|
throw new Error("Failed to get Git repository root: Unknown error");
|
|
565
846
|
}
|
|
566
847
|
}
|
|
848
|
+
async tryGetGitRepositoryRoot(path8) {
|
|
849
|
+
try {
|
|
850
|
+
return await this.getGitRepositoryRoot(path8);
|
|
851
|
+
} catch {
|
|
852
|
+
return null;
|
|
853
|
+
}
|
|
854
|
+
}
|
|
855
|
+
getGitRepositoryRootSync(cwd) {
|
|
856
|
+
try {
|
|
857
|
+
const result = (0, import_child_process.execSync)("git rev-parse --show-toplevel", {
|
|
858
|
+
cwd,
|
|
859
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
860
|
+
encoding: "utf-8"
|
|
861
|
+
});
|
|
862
|
+
return result.trim();
|
|
863
|
+
} catch {
|
|
864
|
+
return null;
|
|
865
|
+
}
|
|
866
|
+
}
|
|
567
867
|
async getCurrentBranches(repoPath) {
|
|
568
868
|
try {
|
|
569
869
|
const { stdout } = await execAsync("git branch -a --contains HEAD", {
|
|
@@ -581,7 +881,7 @@ ${error.message}`
|
|
|
581
881
|
throw new Error("Failed to get Git branches: Unknown error");
|
|
582
882
|
}
|
|
583
883
|
}
|
|
584
|
-
async getGitRemoteUrl(repoPath,
|
|
884
|
+
async getGitRemoteUrl(repoPath, origin11) {
|
|
585
885
|
try {
|
|
586
886
|
const { stdout } = await execAsync("git remote -v", {
|
|
587
887
|
cwd: repoPath
|
|
@@ -591,10 +891,10 @@ ${error.message}`
|
|
|
591
891
|
throw new Error("No Git remotes found in the repository");
|
|
592
892
|
}
|
|
593
893
|
let selectedRemote;
|
|
594
|
-
if (
|
|
595
|
-
const foundRemote = remotes.find((remote) => remote.name ===
|
|
894
|
+
if (origin11) {
|
|
895
|
+
const foundRemote = remotes.find((remote) => remote.name === origin11);
|
|
596
896
|
if (!foundRemote) {
|
|
597
|
-
throw new Error(`Remote '${
|
|
897
|
+
throw new Error(`Remote '${origin11}' not found in repository`);
|
|
598
898
|
}
|
|
599
899
|
selectedRemote = foundRemote.url;
|
|
600
900
|
} else if (remotes.length === 1) {
|
|
@@ -664,18 +964,181 @@ ${error.message}`
|
|
|
664
964
|
normalizeGitUrl(url) {
|
|
665
965
|
const sshMatch = url.match(/^git@([^:]+):(.+)$/);
|
|
666
966
|
if (sshMatch) {
|
|
667
|
-
const [, host,
|
|
668
|
-
const cleanPath =
|
|
967
|
+
const [, host, urlPath] = sshMatch;
|
|
968
|
+
const cleanPath = urlPath.replace(/\.git$/, "");
|
|
669
969
|
return `${host}/${cleanPath}`;
|
|
670
970
|
}
|
|
671
971
|
const httpsMatch = url.match(/^https?:\/\/([^/]+)\/(.+)$/);
|
|
672
972
|
if (httpsMatch) {
|
|
673
|
-
const [, host,
|
|
674
|
-
const cleanPath =
|
|
973
|
+
const [, host, urlPath] = httpsMatch;
|
|
974
|
+
const cleanPath = urlPath.replace(/\.git$/, "");
|
|
675
975
|
return `${host}/${cleanPath}`;
|
|
676
976
|
}
|
|
677
977
|
return url;
|
|
678
978
|
}
|
|
979
|
+
/**
|
|
980
|
+
* Gets files that have been modified (staged + unstaged) compared to HEAD.
|
|
981
|
+
* Returns absolute file paths.
|
|
982
|
+
*/
|
|
983
|
+
async getModifiedFiles(repoPath) {
|
|
984
|
+
const gitRoot = await this.getGitRepositoryRoot(repoPath);
|
|
985
|
+
const trackedFiles = await this.getTrackedModifiedFiles(gitRoot);
|
|
986
|
+
const untrackedFiles = await this.getUntrackedFiles(gitRoot);
|
|
987
|
+
const allFiles = [.../* @__PURE__ */ new Set([...trackedFiles, ...untrackedFiles])];
|
|
988
|
+
this.logger.debug("Found modified files", {
|
|
989
|
+
trackedCount: trackedFiles.length,
|
|
990
|
+
untrackedCount: untrackedFiles.length,
|
|
991
|
+
totalCount: allFiles.length
|
|
992
|
+
});
|
|
993
|
+
return allFiles;
|
|
994
|
+
}
|
|
995
|
+
/**
|
|
996
|
+
* Gets tracked files that have been modified (staged + unstaged) compared to HEAD.
|
|
997
|
+
* Returns absolute file paths.
|
|
998
|
+
*/
|
|
999
|
+
async getTrackedModifiedFiles(gitRoot) {
|
|
1000
|
+
try {
|
|
1001
|
+
const { stdout } = await execAsync("git diff --name-only HEAD", {
|
|
1002
|
+
cwd: gitRoot
|
|
1003
|
+
});
|
|
1004
|
+
return stdout.trim().split("\n").filter((line) => line.length > 0).map((relativePath) => path.join(gitRoot, relativePath));
|
|
1005
|
+
} catch (error) {
|
|
1006
|
+
if (error instanceof Error && error.message.includes("unknown revision")) {
|
|
1007
|
+
this.logger.debug(
|
|
1008
|
+
"HEAD does not exist (first commit), getting staged files only"
|
|
1009
|
+
);
|
|
1010
|
+
return this.getStagedFilesWithoutHead(gitRoot);
|
|
1011
|
+
}
|
|
1012
|
+
throw error;
|
|
1013
|
+
}
|
|
1014
|
+
}
|
|
1015
|
+
/**
|
|
1016
|
+
* Gets staged files when HEAD doesn't exist (first commit scenario).
|
|
1017
|
+
*/
|
|
1018
|
+
async getStagedFilesWithoutHead(gitRoot) {
|
|
1019
|
+
const { stdout } = await execAsync("git diff --cached --name-only", {
|
|
1020
|
+
cwd: gitRoot
|
|
1021
|
+
});
|
|
1022
|
+
return stdout.trim().split("\n").filter((line) => line.length > 0).map((relativePath) => path.join(gitRoot, relativePath));
|
|
1023
|
+
}
|
|
1024
|
+
/**
|
|
1025
|
+
* Gets untracked files (new files not yet added to git).
|
|
1026
|
+
* Returns absolute file paths.
|
|
1027
|
+
*/
|
|
1028
|
+
async getUntrackedFiles(repoPath) {
|
|
1029
|
+
const gitRoot = await this.getGitRepositoryRoot(repoPath);
|
|
1030
|
+
const { stdout } = await execAsync(
|
|
1031
|
+
"git ls-files --others --exclude-standard",
|
|
1032
|
+
{
|
|
1033
|
+
cwd: gitRoot
|
|
1034
|
+
}
|
|
1035
|
+
);
|
|
1036
|
+
return stdout.trim().split("\n").filter((line) => line.length > 0).map((relativePath) => path.join(gitRoot, relativePath));
|
|
1037
|
+
}
|
|
1038
|
+
/**
|
|
1039
|
+
* Gets line-level diff information for modified files.
|
|
1040
|
+
* For untracked files, all lines are considered modified (new file).
|
|
1041
|
+
* Returns ModifiedLine objects with absolute file paths.
|
|
1042
|
+
*/
|
|
1043
|
+
async getModifiedLines(repoPath) {
|
|
1044
|
+
const gitRoot = await this.getGitRepositoryRoot(repoPath);
|
|
1045
|
+
const modifiedLines = [];
|
|
1046
|
+
const trackedModifications = await this.getTrackedModifiedLines(gitRoot);
|
|
1047
|
+
modifiedLines.push(...trackedModifications);
|
|
1048
|
+
const untrackedFiles = await this.getUntrackedFiles(gitRoot);
|
|
1049
|
+
for (const filePath of untrackedFiles) {
|
|
1050
|
+
const lineCount = await this.countFileLines(filePath);
|
|
1051
|
+
if (lineCount > 0) {
|
|
1052
|
+
modifiedLines.push({
|
|
1053
|
+
file: filePath,
|
|
1054
|
+
startLine: 1,
|
|
1055
|
+
lineCount
|
|
1056
|
+
});
|
|
1057
|
+
}
|
|
1058
|
+
}
|
|
1059
|
+
this.logger.debug("Found modified lines", {
|
|
1060
|
+
trackedEntries: trackedModifications.length,
|
|
1061
|
+
untrackedFiles: untrackedFiles.length,
|
|
1062
|
+
totalEntries: modifiedLines.length
|
|
1063
|
+
});
|
|
1064
|
+
return modifiedLines;
|
|
1065
|
+
}
|
|
1066
|
+
/**
|
|
1067
|
+
* Parses git diff output to extract line-level modifications.
|
|
1068
|
+
*/
|
|
1069
|
+
async getTrackedModifiedLines(gitRoot) {
|
|
1070
|
+
try {
|
|
1071
|
+
const { stdout } = await execAsync("git diff HEAD --unified=0", {
|
|
1072
|
+
cwd: gitRoot,
|
|
1073
|
+
maxBuffer: 50 * 1024 * 1024
|
|
1074
|
+
// 50MB buffer for large diffs
|
|
1075
|
+
});
|
|
1076
|
+
return this.parseDiffOutput(stdout, gitRoot);
|
|
1077
|
+
} catch (error) {
|
|
1078
|
+
if (error instanceof Error && error.message.includes("unknown revision")) {
|
|
1079
|
+
this.logger.debug(
|
|
1080
|
+
"HEAD does not exist (first commit), getting staged diff only"
|
|
1081
|
+
);
|
|
1082
|
+
return this.getStagedModifiedLinesWithoutHead(gitRoot);
|
|
1083
|
+
}
|
|
1084
|
+
throw error;
|
|
1085
|
+
}
|
|
1086
|
+
}
|
|
1087
|
+
/**
|
|
1088
|
+
* Gets modified lines from staged files when HEAD doesn't exist.
|
|
1089
|
+
*/
|
|
1090
|
+
async getStagedModifiedLinesWithoutHead(gitRoot) {
|
|
1091
|
+
const { stdout } = await execAsync("git diff --cached --unified=0", {
|
|
1092
|
+
cwd: gitRoot,
|
|
1093
|
+
maxBuffer: 50 * 1024 * 1024
|
|
1094
|
+
});
|
|
1095
|
+
return this.parseDiffOutput(stdout, gitRoot);
|
|
1096
|
+
}
|
|
1097
|
+
/**
|
|
1098
|
+
* Parses unified diff output to extract modified line ranges.
|
|
1099
|
+
* Format: @@ -oldStart,oldCount +newStart,newCount @@
|
|
1100
|
+
*/
|
|
1101
|
+
parseDiffOutput(diffOutput, gitRoot) {
|
|
1102
|
+
const modifiedLines = [];
|
|
1103
|
+
const lines = diffOutput.split("\n");
|
|
1104
|
+
let currentFile = null;
|
|
1105
|
+
for (const line of lines) {
|
|
1106
|
+
const fileMatch = line.match(/^diff --git a\/(.+) b\/(.+)$/);
|
|
1107
|
+
if (fileMatch) {
|
|
1108
|
+
currentFile = path.join(gitRoot, fileMatch[2]);
|
|
1109
|
+
continue;
|
|
1110
|
+
}
|
|
1111
|
+
const hunkMatch = line.match(/^@@ -\d+(?:,\d+)? \+(\d+)(?:,(\d+))? @@/);
|
|
1112
|
+
if (hunkMatch && currentFile) {
|
|
1113
|
+
const startLine = parseInt(hunkMatch[1], 10);
|
|
1114
|
+
const lineCount = hunkMatch[2] ? parseInt(hunkMatch[2], 10) : 1;
|
|
1115
|
+
if (lineCount > 0) {
|
|
1116
|
+
modifiedLines.push({
|
|
1117
|
+
file: currentFile,
|
|
1118
|
+
startLine,
|
|
1119
|
+
lineCount
|
|
1120
|
+
});
|
|
1121
|
+
}
|
|
1122
|
+
}
|
|
1123
|
+
}
|
|
1124
|
+
return modifiedLines;
|
|
1125
|
+
}
|
|
1126
|
+
/**
|
|
1127
|
+
* Counts the number of lines in a file.
|
|
1128
|
+
*/
|
|
1129
|
+
async countFileLines(filePath) {
|
|
1130
|
+
try {
|
|
1131
|
+
const { stdout } = await execAsync(`wc -l < "${filePath}"`);
|
|
1132
|
+
const count = parseInt(stdout.trim(), 10);
|
|
1133
|
+
if (count === 0) {
|
|
1134
|
+
const { stdout: content } = await execAsync(`head -c 1 "${filePath}"`);
|
|
1135
|
+
return content.length > 0 ? 1 : 0;
|
|
1136
|
+
}
|
|
1137
|
+
return count;
|
|
1138
|
+
} catch {
|
|
1139
|
+
return 0;
|
|
1140
|
+
}
|
|
1141
|
+
}
|
|
679
1142
|
};
|
|
680
1143
|
|
|
681
1144
|
// apps/cli/src/application/useCases/GetGitRemoteUrlUseCase.ts
|
|
@@ -684,14 +1147,14 @@ var GetGitRemoteUrlUseCase = class {
|
|
|
684
1147
|
this.gitRemoteUrlService = gitRemoteUrlService;
|
|
685
1148
|
}
|
|
686
1149
|
async execute(command3) {
|
|
687
|
-
const { path: repoPath, origin:
|
|
688
|
-
return this.gitRemoteUrlService.getGitRemoteUrl(repoPath,
|
|
1150
|
+
const { path: repoPath, origin: origin11 } = command3;
|
|
1151
|
+
return this.gitRemoteUrlService.getGitRemoteUrl(repoPath, origin11);
|
|
689
1152
|
}
|
|
690
1153
|
};
|
|
691
1154
|
|
|
692
1155
|
// apps/cli/src/application/services/ListFiles.ts
|
|
693
1156
|
var fs = __toESM(require("fs/promises"));
|
|
694
|
-
var
|
|
1157
|
+
var path2 = __toESM(require("path"));
|
|
695
1158
|
var ListFiles = class {
|
|
696
1159
|
async listFilesInDirectory(directoryPath, extensions, excludes = [], skipHidden = true) {
|
|
697
1160
|
const results = [];
|
|
@@ -713,7 +1176,7 @@ var ListFiles = class {
|
|
|
713
1176
|
try {
|
|
714
1177
|
const entries = await fs.readdir(directoryPath, { withFileTypes: true });
|
|
715
1178
|
for (const entry of entries) {
|
|
716
|
-
const fullPath =
|
|
1179
|
+
const fullPath = path2.join(directoryPath, entry.name);
|
|
717
1180
|
if (this.shouldExcludePath(fullPath, excludes)) {
|
|
718
1181
|
continue;
|
|
719
1182
|
}
|
|
@@ -730,7 +1193,7 @@ var ListFiles = class {
|
|
|
730
1193
|
skipHidden
|
|
731
1194
|
);
|
|
732
1195
|
} else if (entry.isFile()) {
|
|
733
|
-
const fileExtension =
|
|
1196
|
+
const fileExtension = path2.extname(entry.name);
|
|
734
1197
|
if (includeAllExtensions || extensions.includes(fileExtension)) {
|
|
735
1198
|
results.push({
|
|
736
1199
|
path: fullPath
|
|
@@ -746,7 +1209,7 @@ var ListFiles = class {
|
|
|
746
1209
|
if (excludes.length === 0) {
|
|
747
1210
|
return false;
|
|
748
1211
|
}
|
|
749
|
-
const normalizedPath =
|
|
1212
|
+
const normalizedPath = path2.normalize(filePath).replace(/\\/g, "/");
|
|
750
1213
|
for (const exclude of excludes) {
|
|
751
1214
|
if (this.matchesGlobPattern(normalizedPath, exclude)) {
|
|
752
1215
|
return true;
|
|
@@ -809,7 +1272,7 @@ var ListFilesInDirectoryUseCase = class {
|
|
|
809
1272
|
|
|
810
1273
|
// apps/cli/src/application/useCases/LintFilesInDirectoryUseCase.ts
|
|
811
1274
|
var import_minimatch = require("minimatch");
|
|
812
|
-
var
|
|
1275
|
+
var path3 = __toESM(require("path"));
|
|
813
1276
|
var fs2 = __toESM(require("fs/promises"));
|
|
814
1277
|
var origin2 = "LintFilesInDirectoryUseCase";
|
|
815
1278
|
var LintFilesInDirectoryUseCase = class {
|
|
@@ -876,12 +1339,13 @@ var LintFilesInDirectoryUseCase = class {
|
|
|
876
1339
|
draftMode,
|
|
877
1340
|
standardSlug,
|
|
878
1341
|
ruleId,
|
|
879
|
-
language
|
|
1342
|
+
language,
|
|
1343
|
+
diffMode
|
|
880
1344
|
} = command3;
|
|
881
1345
|
this.logger.debug(
|
|
882
|
-
`Starting linting: path="${userPath}", draftMode=${!!draftMode}, standardSlug="${standardSlug || "N/A"}", ruleId="${ruleId || "N/A"}", language="${language || "N/A"}"`
|
|
1346
|
+
`Starting linting: path="${userPath}", draftMode=${!!draftMode}, standardSlug="${standardSlug || "N/A"}", ruleId="${ruleId || "N/A"}", language="${language || "N/A"}", diffMode="${diffMode ?? "none"}"`
|
|
883
1347
|
);
|
|
884
|
-
const absoluteUserPath =
|
|
1348
|
+
const absoluteUserPath = path3.isAbsolute(userPath) ? userPath : path3.resolve(process.cwd(), userPath);
|
|
885
1349
|
let pathStats;
|
|
886
1350
|
try {
|
|
887
1351
|
pathStats = await fs2.stat(absoluteUserPath);
|
|
@@ -891,7 +1355,7 @@ var LintFilesInDirectoryUseCase = class {
|
|
|
891
1355
|
);
|
|
892
1356
|
}
|
|
893
1357
|
const isFile = pathStats.isFile();
|
|
894
|
-
const directoryForGitOps = isFile ?
|
|
1358
|
+
const directoryForGitOps = isFile ? path3.dirname(absoluteUserPath) : absoluteUserPath;
|
|
895
1359
|
this.logger.debug(
|
|
896
1360
|
`Path type: ${isFile ? "file" : "directory"}, gitOpsDir="${directoryForGitOps}"`
|
|
897
1361
|
);
|
|
@@ -907,11 +1371,60 @@ var LintFilesInDirectoryUseCase = class {
|
|
|
907
1371
|
this.logger.debug(
|
|
908
1372
|
`Resolved paths: gitRoot="${gitRepoRoot}", lintPath="${absoluteLintPath}"`
|
|
909
1373
|
);
|
|
910
|
-
|
|
1374
|
+
let modifiedFiles = null;
|
|
1375
|
+
let modifiedLines = null;
|
|
1376
|
+
if (diffMode) {
|
|
1377
|
+
if (diffMode === "files" /* FILES */) {
|
|
1378
|
+
modifiedFiles = await this.services.gitRemoteUrlService.getModifiedFiles(gitRepoRoot);
|
|
1379
|
+
this.logger.debug(`Found ${modifiedFiles.length} modified files`);
|
|
1380
|
+
if (modifiedFiles.length === 0) {
|
|
1381
|
+
const { gitRemoteUrl: gitRemoteUrl2 } = await this.services.gitRemoteUrlService.getGitRemoteUrl(
|
|
1382
|
+
gitRepoRoot
|
|
1383
|
+
);
|
|
1384
|
+
return {
|
|
1385
|
+
gitRemoteUrl: gitRemoteUrl2,
|
|
1386
|
+
violations: [],
|
|
1387
|
+
summary: {
|
|
1388
|
+
totalFiles: 0,
|
|
1389
|
+
violatedFiles: 0,
|
|
1390
|
+
totalViolations: 0,
|
|
1391
|
+
standardsChecked: []
|
|
1392
|
+
}
|
|
1393
|
+
};
|
|
1394
|
+
}
|
|
1395
|
+
} else if (diffMode === "lines" /* LINES */) {
|
|
1396
|
+
modifiedLines = await this.services.gitRemoteUrlService.getModifiedLines(gitRepoRoot);
|
|
1397
|
+
modifiedFiles = [...new Set(modifiedLines.map((ml) => ml.file))];
|
|
1398
|
+
this.logger.debug(
|
|
1399
|
+
`Found ${modifiedLines.length} modified line ranges in ${modifiedFiles.length} files`
|
|
1400
|
+
);
|
|
1401
|
+
if (modifiedFiles.length === 0) {
|
|
1402
|
+
const { gitRemoteUrl: gitRemoteUrl2 } = await this.services.gitRemoteUrlService.getGitRemoteUrl(
|
|
1403
|
+
gitRepoRoot
|
|
1404
|
+
);
|
|
1405
|
+
return {
|
|
1406
|
+
gitRemoteUrl: gitRemoteUrl2,
|
|
1407
|
+
violations: [],
|
|
1408
|
+
summary: {
|
|
1409
|
+
totalFiles: 0,
|
|
1410
|
+
violatedFiles: 0,
|
|
1411
|
+
totalViolations: 0,
|
|
1412
|
+
standardsChecked: []
|
|
1413
|
+
}
|
|
1414
|
+
};
|
|
1415
|
+
}
|
|
1416
|
+
}
|
|
1417
|
+
}
|
|
1418
|
+
let files = isFile ? [{ path: absoluteLintPath }] : await this.services.listFiles.listFilesInDirectory(
|
|
911
1419
|
absoluteLintPath,
|
|
912
1420
|
[],
|
|
913
1421
|
["node_modules", "dist", ".min.", ".map.", ".git"]
|
|
914
1422
|
);
|
|
1423
|
+
if (modifiedFiles) {
|
|
1424
|
+
const modifiedFilesSet = new Set(modifiedFiles);
|
|
1425
|
+
files = files.filter((file) => modifiedFilesSet.has(file.path));
|
|
1426
|
+
this.logger.debug(`Filtered to ${files.length} modified files`);
|
|
1427
|
+
}
|
|
915
1428
|
const { gitRemoteUrl } = await this.services.gitRemoteUrlService.getGitRemoteUrl(gitRepoRoot);
|
|
916
1429
|
const { branches } = await this.services.gitRemoteUrlService.getCurrentBranches(gitRepoRoot);
|
|
917
1430
|
this.logger.debug(
|
|
@@ -1108,7 +1621,17 @@ var LintFilesInDirectoryUseCase = class {
|
|
|
1108
1621
|
});
|
|
1109
1622
|
}
|
|
1110
1623
|
}
|
|
1111
|
-
|
|
1624
|
+
let filteredViolations = violations;
|
|
1625
|
+
if (diffMode === "lines" /* LINES */ && modifiedLines) {
|
|
1626
|
+
filteredViolations = this.services.diffViolationFilterService.filterByLines(
|
|
1627
|
+
violations,
|
|
1628
|
+
modifiedLines
|
|
1629
|
+
);
|
|
1630
|
+
this.logger.debug(
|
|
1631
|
+
`Filtered violations by lines: ${violations.length} -> ${filteredViolations.length}`
|
|
1632
|
+
);
|
|
1633
|
+
}
|
|
1634
|
+
const totalViolations = filteredViolations.reduce(
|
|
1112
1635
|
(sum, violation) => sum + violation.violations.length,
|
|
1113
1636
|
0
|
|
1114
1637
|
);
|
|
@@ -1121,10 +1644,10 @@ var LintFilesInDirectoryUseCase = class {
|
|
|
1121
1644
|
);
|
|
1122
1645
|
return {
|
|
1123
1646
|
gitRemoteUrl,
|
|
1124
|
-
violations,
|
|
1647
|
+
violations: filteredViolations,
|
|
1125
1648
|
summary: {
|
|
1126
1649
|
totalFiles: files.length,
|
|
1127
|
-
violatedFiles:
|
|
1650
|
+
violatedFiles: filteredViolations.length,
|
|
1128
1651
|
totalViolations,
|
|
1129
1652
|
standardsChecked
|
|
1130
1653
|
}
|
|
@@ -1150,6 +1673,307 @@ var LintFilesInDirectoryUseCase = class {
|
|
|
1150
1673
|
}
|
|
1151
1674
|
};
|
|
1152
1675
|
|
|
1676
|
+
// apps/cli/src/application/useCases/LintFilesLocallyUseCase.ts
|
|
1677
|
+
var import_minimatch2 = require("minimatch");
|
|
1678
|
+
var path4 = __toESM(require("path"));
|
|
1679
|
+
var fs3 = __toESM(require("fs/promises"));
|
|
1680
|
+
var origin3 = "LintFilesLocallyUseCase";
|
|
1681
|
+
var LintFilesLocallyUseCase = class {
|
|
1682
|
+
constructor(services, repositories, logger2 = new PackmindLogger(origin3)) {
|
|
1683
|
+
this.services = services;
|
|
1684
|
+
this.repositories = repositories;
|
|
1685
|
+
this.logger = logger2;
|
|
1686
|
+
this.detectionProgramsCache = /* @__PURE__ */ new Map();
|
|
1687
|
+
}
|
|
1688
|
+
fileMatchesTargetAndScope(filePath, targetPath, scopePatterns) {
|
|
1689
|
+
if (!scopePatterns || scopePatterns.length === 0) {
|
|
1690
|
+
const effectivePattern = this.buildEffectivePattern(targetPath, null);
|
|
1691
|
+
return (0, import_minimatch2.minimatch)(filePath, effectivePattern, { matchBase: false });
|
|
1692
|
+
}
|
|
1693
|
+
return scopePatterns.some((scopePattern) => {
|
|
1694
|
+
const effectivePattern = this.buildEffectivePattern(
|
|
1695
|
+
targetPath,
|
|
1696
|
+
scopePattern
|
|
1697
|
+
);
|
|
1698
|
+
return (0, import_minimatch2.minimatch)(filePath, effectivePattern, { matchBase: false });
|
|
1699
|
+
});
|
|
1700
|
+
}
|
|
1701
|
+
buildEffectivePattern(targetPath, scope) {
|
|
1702
|
+
const normalizedTarget = targetPath === "/" ? "/" : targetPath.replace(/\/$/, "");
|
|
1703
|
+
if (!scope) {
|
|
1704
|
+
return normalizedTarget === "/" ? "/**" : normalizedTarget + "/**";
|
|
1705
|
+
}
|
|
1706
|
+
if (scope.startsWith(normalizedTarget + "/") || scope === normalizedTarget) {
|
|
1707
|
+
return scope.endsWith("/") ? scope + "**" : scope;
|
|
1708
|
+
}
|
|
1709
|
+
const cleanScope = scope.startsWith("/") ? scope.substring(1) : scope;
|
|
1710
|
+
let pattern;
|
|
1711
|
+
if (normalizedTarget === "/") {
|
|
1712
|
+
pattern = "/" + cleanScope;
|
|
1713
|
+
} else {
|
|
1714
|
+
pattern = normalizedTarget + "/" + cleanScope;
|
|
1715
|
+
}
|
|
1716
|
+
if (pattern.endsWith("/")) {
|
|
1717
|
+
pattern = pattern + "**";
|
|
1718
|
+
}
|
|
1719
|
+
return pattern;
|
|
1720
|
+
}
|
|
1721
|
+
async execute(command3) {
|
|
1722
|
+
const { path: userPath, diffMode } = command3;
|
|
1723
|
+
this.logger.debug(
|
|
1724
|
+
`Starting local linting: path="${userPath}", diffMode="${diffMode ?? "none"}"`
|
|
1725
|
+
);
|
|
1726
|
+
this.detectionProgramsCache.clear();
|
|
1727
|
+
const absoluteUserPath = path4.isAbsolute(userPath) ? userPath : path4.resolve(process.cwd(), userPath);
|
|
1728
|
+
let pathStats;
|
|
1729
|
+
try {
|
|
1730
|
+
pathStats = await fs3.stat(absoluteUserPath);
|
|
1731
|
+
} catch {
|
|
1732
|
+
throw new Error(
|
|
1733
|
+
`The path "${absoluteUserPath}" does not exist or cannot be accessed`
|
|
1734
|
+
);
|
|
1735
|
+
}
|
|
1736
|
+
const isFile = pathStats.isFile();
|
|
1737
|
+
const directoryForConfig = isFile ? path4.dirname(absoluteUserPath) : absoluteUserPath;
|
|
1738
|
+
const gitRepoRoot = await this.services.gitRemoteUrlService.tryGetGitRepositoryRoot(
|
|
1739
|
+
directoryForConfig
|
|
1740
|
+
);
|
|
1741
|
+
let modifiedFiles = null;
|
|
1742
|
+
let modifiedLines = null;
|
|
1743
|
+
if (diffMode && gitRepoRoot) {
|
|
1744
|
+
if (diffMode === "files" /* FILES */) {
|
|
1745
|
+
modifiedFiles = await this.services.gitRemoteUrlService.getModifiedFiles(gitRepoRoot);
|
|
1746
|
+
this.logger.debug(`Found ${modifiedFiles.length} modified files`);
|
|
1747
|
+
if (modifiedFiles.length === 0) {
|
|
1748
|
+
return {
|
|
1749
|
+
violations: [],
|
|
1750
|
+
summary: {
|
|
1751
|
+
totalFiles: 0,
|
|
1752
|
+
violatedFiles: 0,
|
|
1753
|
+
totalViolations: 0,
|
|
1754
|
+
standardsChecked: []
|
|
1755
|
+
}
|
|
1756
|
+
};
|
|
1757
|
+
}
|
|
1758
|
+
} else if (diffMode === "lines" /* LINES */) {
|
|
1759
|
+
modifiedLines = await this.services.gitRemoteUrlService.getModifiedLines(gitRepoRoot);
|
|
1760
|
+
modifiedFiles = [...new Set(modifiedLines.map((ml) => ml.file))];
|
|
1761
|
+
this.logger.debug(
|
|
1762
|
+
`Found ${modifiedLines.length} modified line ranges in ${modifiedFiles.length} files`
|
|
1763
|
+
);
|
|
1764
|
+
if (modifiedFiles.length === 0) {
|
|
1765
|
+
return {
|
|
1766
|
+
violations: [],
|
|
1767
|
+
summary: {
|
|
1768
|
+
totalFiles: 0,
|
|
1769
|
+
violatedFiles: 0,
|
|
1770
|
+
totalViolations: 0,
|
|
1771
|
+
standardsChecked: []
|
|
1772
|
+
}
|
|
1773
|
+
};
|
|
1774
|
+
}
|
|
1775
|
+
}
|
|
1776
|
+
}
|
|
1777
|
+
const allConfigs = await this.repositories.configFileRepository.findAllConfigsInTree(
|
|
1778
|
+
directoryForConfig,
|
|
1779
|
+
gitRepoRoot
|
|
1780
|
+
);
|
|
1781
|
+
if (!allConfigs.hasConfigs) {
|
|
1782
|
+
const boundary = gitRepoRoot ?? "filesystem root";
|
|
1783
|
+
throw new Error(
|
|
1784
|
+
`No packmind.json found between ${directoryForConfig} and ${boundary}. Cannot use local linting.`
|
|
1785
|
+
);
|
|
1786
|
+
}
|
|
1787
|
+
const basePath = allConfigs.basePath;
|
|
1788
|
+
this.logger.debug(
|
|
1789
|
+
`Found ${allConfigs.configs.length} packmind.json file(s)`
|
|
1790
|
+
);
|
|
1791
|
+
for (const config of allConfigs.configs) {
|
|
1792
|
+
this.logger.debug(
|
|
1793
|
+
`Using config: ${config.absoluteTargetPath}/packmind.json (target: ${config.targetPath})`
|
|
1794
|
+
);
|
|
1795
|
+
}
|
|
1796
|
+
let files = isFile ? [{ path: absoluteUserPath }] : await this.services.listFiles.listFilesInDirectory(
|
|
1797
|
+
absoluteUserPath,
|
|
1798
|
+
[],
|
|
1799
|
+
["node_modules", "dist", ".min.", ".map.", ".git"]
|
|
1800
|
+
);
|
|
1801
|
+
if (modifiedFiles) {
|
|
1802
|
+
const modifiedFilesSet = new Set(modifiedFiles);
|
|
1803
|
+
files = files.filter((file) => modifiedFilesSet.has(file.path));
|
|
1804
|
+
this.logger.debug(`Filtered to ${files.length} modified files`);
|
|
1805
|
+
}
|
|
1806
|
+
this.logger.debug(`Found ${files.length} files to lint`);
|
|
1807
|
+
const violations = [];
|
|
1808
|
+
const allStandardsChecked = /* @__PURE__ */ new Set();
|
|
1809
|
+
for (const file of files) {
|
|
1810
|
+
const fileViolations = [];
|
|
1811
|
+
const relativeFilePath = file.path.startsWith(basePath) ? file.path.substring(basePath.length) : file.path;
|
|
1812
|
+
const normalizedFilePath = relativeFilePath.startsWith("/") ? relativeFilePath : "/" + relativeFilePath;
|
|
1813
|
+
this.logger.debug(
|
|
1814
|
+
`Processing file: absolute="${file.path}", relative="${normalizedFilePath}"`
|
|
1815
|
+
);
|
|
1816
|
+
const fileExtension = this.extractExtensionFromFile(file.path);
|
|
1817
|
+
const fileLanguage = this.resolveProgrammingLanguage(fileExtension);
|
|
1818
|
+
if (!fileLanguage) {
|
|
1819
|
+
continue;
|
|
1820
|
+
}
|
|
1821
|
+
const matchingTargets = this.findMatchingTargets(
|
|
1822
|
+
file.path,
|
|
1823
|
+
allConfigs.configs
|
|
1824
|
+
);
|
|
1825
|
+
const programsByLanguage = /* @__PURE__ */ new Map();
|
|
1826
|
+
for (const targetConfig of matchingTargets) {
|
|
1827
|
+
const detectionPrograms = await this.getDetectionProgramsForTarget(targetConfig);
|
|
1828
|
+
for (const target of detectionPrograms.targets) {
|
|
1829
|
+
for (const standard of target.standards) {
|
|
1830
|
+
if (!this.fileMatchesTargetAndScope(
|
|
1831
|
+
normalizedFilePath,
|
|
1832
|
+
targetConfig.targetPath,
|
|
1833
|
+
standard.scope
|
|
1834
|
+
)) {
|
|
1835
|
+
continue;
|
|
1836
|
+
}
|
|
1837
|
+
allStandardsChecked.add(standard.slug);
|
|
1838
|
+
for (const rule of standard.rules) {
|
|
1839
|
+
for (const activeProgram of rule.activeDetectionPrograms) {
|
|
1840
|
+
try {
|
|
1841
|
+
const programLanguage = this.resolveProgrammingLanguage(
|
|
1842
|
+
activeProgram.language
|
|
1843
|
+
);
|
|
1844
|
+
if (!programLanguage || programLanguage !== fileLanguage) {
|
|
1845
|
+
continue;
|
|
1846
|
+
}
|
|
1847
|
+
const programsForLanguage = programsByLanguage.get(programLanguage) ?? [];
|
|
1848
|
+
programsForLanguage.push({
|
|
1849
|
+
code: activeProgram.detectionProgram.code,
|
|
1850
|
+
ruleContent: rule.content,
|
|
1851
|
+
standardSlug: standard.slug,
|
|
1852
|
+
sourceCodeState: activeProgram.detectionProgram.sourceCodeState,
|
|
1853
|
+
language: fileLanguage
|
|
1854
|
+
});
|
|
1855
|
+
programsByLanguage.set(programLanguage, programsForLanguage);
|
|
1856
|
+
} catch (error) {
|
|
1857
|
+
console.error(
|
|
1858
|
+
`Error preparing program for file ${file.path}: ${error}`
|
|
1859
|
+
);
|
|
1860
|
+
}
|
|
1861
|
+
}
|
|
1862
|
+
}
|
|
1863
|
+
}
|
|
1864
|
+
}
|
|
1865
|
+
}
|
|
1866
|
+
if (programsByLanguage.size > 0) {
|
|
1867
|
+
try {
|
|
1868
|
+
const fileContent = await this.services.listFiles.readFileContent(
|
|
1869
|
+
file.path
|
|
1870
|
+
);
|
|
1871
|
+
for (const [language, programs] of programsByLanguage.entries()) {
|
|
1872
|
+
try {
|
|
1873
|
+
const result = await this.executeProgramsForFile({
|
|
1874
|
+
filePath: file.path,
|
|
1875
|
+
fileContent,
|
|
1876
|
+
language,
|
|
1877
|
+
programs
|
|
1878
|
+
});
|
|
1879
|
+
fileViolations.push(...result);
|
|
1880
|
+
} catch (error) {
|
|
1881
|
+
console.error(
|
|
1882
|
+
`Error executing programs for file ${file.path} (${language}): ${error}`
|
|
1883
|
+
);
|
|
1884
|
+
}
|
|
1885
|
+
}
|
|
1886
|
+
} catch (error) {
|
|
1887
|
+
console.error(
|
|
1888
|
+
`Error reading file content for ${file.path}: ${error}`
|
|
1889
|
+
);
|
|
1890
|
+
}
|
|
1891
|
+
}
|
|
1892
|
+
if (fileViolations.length > 0) {
|
|
1893
|
+
violations.push({
|
|
1894
|
+
file: file.path,
|
|
1895
|
+
violations: fileViolations
|
|
1896
|
+
});
|
|
1897
|
+
}
|
|
1898
|
+
}
|
|
1899
|
+
let filteredViolations = violations;
|
|
1900
|
+
if (diffMode === "lines" /* LINES */ && modifiedLines) {
|
|
1901
|
+
filteredViolations = this.services.diffViolationFilterService.filterByLines(
|
|
1902
|
+
violations,
|
|
1903
|
+
modifiedLines
|
|
1904
|
+
);
|
|
1905
|
+
this.logger.debug(
|
|
1906
|
+
`Filtered violations by lines: ${violations.length} -> ${filteredViolations.length}`
|
|
1907
|
+
);
|
|
1908
|
+
}
|
|
1909
|
+
const totalViolations = filteredViolations.reduce(
|
|
1910
|
+
(sum, violation) => sum + violation.violations.length,
|
|
1911
|
+
0
|
|
1912
|
+
);
|
|
1913
|
+
return {
|
|
1914
|
+
violations: filteredViolations,
|
|
1915
|
+
summary: {
|
|
1916
|
+
totalFiles: files.length,
|
|
1917
|
+
violatedFiles: filteredViolations.length,
|
|
1918
|
+
totalViolations,
|
|
1919
|
+
standardsChecked: Array.from(allStandardsChecked)
|
|
1920
|
+
}
|
|
1921
|
+
};
|
|
1922
|
+
}
|
|
1923
|
+
/**
|
|
1924
|
+
* Finds all targets (configs) that are ancestors of the given file path.
|
|
1925
|
+
* A target matches if the file is located within or under the target's directory.
|
|
1926
|
+
*/
|
|
1927
|
+
findMatchingTargets(absoluteFilePath, configs) {
|
|
1928
|
+
return configs.filter(
|
|
1929
|
+
(config) => absoluteFilePath.startsWith(config.absoluteTargetPath + "/") || absoluteFilePath === config.absoluteTargetPath
|
|
1930
|
+
);
|
|
1931
|
+
}
|
|
1932
|
+
/**
|
|
1933
|
+
* Gets detection programs for a target, using cache to avoid redundant API calls.
|
|
1934
|
+
* Cache key is the sorted package slugs to handle identical package sets.
|
|
1935
|
+
*/
|
|
1936
|
+
async getDetectionProgramsForTarget(targetConfig) {
|
|
1937
|
+
const packageSlugs = Object.keys(targetConfig.packages).sort(
|
|
1938
|
+
(a, b) => a.localeCompare(b)
|
|
1939
|
+
);
|
|
1940
|
+
const cacheKey = packageSlugs.join(",");
|
|
1941
|
+
const cached = this.detectionProgramsCache.get(cacheKey);
|
|
1942
|
+
if (cached) {
|
|
1943
|
+
this.logger.debug(
|
|
1944
|
+
`Using cached detection programs for packages: ${cacheKey}`
|
|
1945
|
+
);
|
|
1946
|
+
return cached;
|
|
1947
|
+
}
|
|
1948
|
+
this.logger.debug(
|
|
1949
|
+
`Fetching detection programs for packages: ${packageSlugs.join(", ")}`
|
|
1950
|
+
);
|
|
1951
|
+
const detectionPrograms = await this.repositories.packmindGateway.getDetectionProgramsForPackages({
|
|
1952
|
+
packagesSlugs: packageSlugs
|
|
1953
|
+
});
|
|
1954
|
+
this.detectionProgramsCache.set(cacheKey, detectionPrograms);
|
|
1955
|
+
return detectionPrograms;
|
|
1956
|
+
}
|
|
1957
|
+
resolveProgrammingLanguage(language) {
|
|
1958
|
+
try {
|
|
1959
|
+
return stringToProgrammingLanguage(language);
|
|
1960
|
+
} catch {
|
|
1961
|
+
return null;
|
|
1962
|
+
}
|
|
1963
|
+
}
|
|
1964
|
+
async executeProgramsForFile(command3) {
|
|
1965
|
+
const result = await this.services.linterExecutionUseCase.execute(command3);
|
|
1966
|
+
return result.violations;
|
|
1967
|
+
}
|
|
1968
|
+
extractExtensionFromFile(filePath) {
|
|
1969
|
+
const lastDotIndex = filePath.lastIndexOf(".");
|
|
1970
|
+
if (lastDotIndex === -1 || lastDotIndex === filePath.length - 1) {
|
|
1971
|
+
return "";
|
|
1972
|
+
}
|
|
1973
|
+
return filePath.substring(lastDotIndex + 1);
|
|
1974
|
+
}
|
|
1975
|
+
};
|
|
1976
|
+
|
|
1153
1977
|
// apps/cli/src/infra/repositories/PackmindGateway.ts
|
|
1154
1978
|
function decodeJwt(jwt) {
|
|
1155
1979
|
try {
|
|
@@ -1256,7 +2080,7 @@ var PackmindGateway = class {
|
|
|
1256
2080
|
);
|
|
1257
2081
|
}
|
|
1258
2082
|
throw new Error(
|
|
1259
|
-
`Failed to
|
|
2083
|
+
`Failed to fetch content: Error: ${err?.message || JSON.stringify(error)}`
|
|
1260
2084
|
);
|
|
1261
2085
|
}
|
|
1262
2086
|
};
|
|
@@ -1535,7 +2359,117 @@ var PackmindGateway = class {
|
|
|
1535
2359
|
`Failed to get package '${slug}': Error: ${err?.message || JSON.stringify(error)}`
|
|
1536
2360
|
);
|
|
1537
2361
|
}
|
|
1538
|
-
};
|
|
2362
|
+
};
|
|
2363
|
+
this.getDetectionProgramsForPackages = async (params) => {
|
|
2364
|
+
const decodedApiKey = decodeApiKey(this.apiKey);
|
|
2365
|
+
if (!decodedApiKey.isValid) {
|
|
2366
|
+
throw new Error(`Invalid API key: ${decodedApiKey.error}`);
|
|
2367
|
+
}
|
|
2368
|
+
const { host } = decodedApiKey.payload;
|
|
2369
|
+
const url = `${host}/api/v0/detection-programs-for-packages`;
|
|
2370
|
+
const payload = {
|
|
2371
|
+
packagesSlugs: params.packagesSlugs
|
|
2372
|
+
};
|
|
2373
|
+
try {
|
|
2374
|
+
const response = await fetch(url, {
|
|
2375
|
+
method: "POST",
|
|
2376
|
+
headers: {
|
|
2377
|
+
"Content-Type": "application/json",
|
|
2378
|
+
Authorization: `Bearer ${this.apiKey}`
|
|
2379
|
+
},
|
|
2380
|
+
body: JSON.stringify(payload)
|
|
2381
|
+
});
|
|
2382
|
+
if (!response.ok) {
|
|
2383
|
+
let errorMsg = `API request failed: ${response.status} ${response.statusText}`;
|
|
2384
|
+
try {
|
|
2385
|
+
const errorBody = await response.json();
|
|
2386
|
+
if (errorBody && errorBody.message) {
|
|
2387
|
+
errorMsg = `${errorBody.message}`;
|
|
2388
|
+
}
|
|
2389
|
+
} catch {
|
|
2390
|
+
}
|
|
2391
|
+
throw new Error(errorMsg);
|
|
2392
|
+
}
|
|
2393
|
+
const result = await response.json();
|
|
2394
|
+
return result;
|
|
2395
|
+
} catch (error) {
|
|
2396
|
+
const err = error;
|
|
2397
|
+
const code = err?.code || err?.cause?.code;
|
|
2398
|
+
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"))) {
|
|
2399
|
+
throw new Error(
|
|
2400
|
+
`Packmind server is not accessible at ${host}. Please check your network connection or the server URL.`
|
|
2401
|
+
);
|
|
2402
|
+
}
|
|
2403
|
+
throw new Error(
|
|
2404
|
+
`Failed to fetch detection programs for packages: Error: ${err?.message || JSON.stringify(error)}`
|
|
2405
|
+
);
|
|
2406
|
+
}
|
|
2407
|
+
};
|
|
2408
|
+
}
|
|
2409
|
+
};
|
|
2410
|
+
|
|
2411
|
+
// apps/cli/src/application/services/DiffViolationFilterService.ts
|
|
2412
|
+
var DiffViolationFilterService = class {
|
|
2413
|
+
/**
|
|
2414
|
+
* Filters violations to only include those in modified files.
|
|
2415
|
+
* @param violations - The list of violations to filter
|
|
2416
|
+
* @param modifiedFiles - The list of absolute paths of modified files
|
|
2417
|
+
* @returns Violations that occur in modified files
|
|
2418
|
+
*/
|
|
2419
|
+
filterByFiles(violations, modifiedFiles) {
|
|
2420
|
+
const modifiedFilesSet = new Set(modifiedFiles);
|
|
2421
|
+
return violations.filter(
|
|
2422
|
+
(violation) => modifiedFilesSet.has(violation.file)
|
|
2423
|
+
);
|
|
2424
|
+
}
|
|
2425
|
+
/**
|
|
2426
|
+
* Filters violations to only include those on modified lines.
|
|
2427
|
+
* @param violations - The list of violations to filter
|
|
2428
|
+
* @param modifiedLines - The list of modified line ranges
|
|
2429
|
+
* @returns Violations that occur on modified lines
|
|
2430
|
+
*/
|
|
2431
|
+
filterByLines(violations, modifiedLines) {
|
|
2432
|
+
const modifiedLinesByFile = this.groupModifiedLinesByFile(modifiedLines);
|
|
2433
|
+
return violations.map((violation) => {
|
|
2434
|
+
const fileModifications = modifiedLinesByFile.get(violation.file);
|
|
2435
|
+
if (!fileModifications) {
|
|
2436
|
+
return null;
|
|
2437
|
+
}
|
|
2438
|
+
const filteredViolations = violation.violations.filter(
|
|
2439
|
+
(singleViolation) => this.isLineInModifiedRanges(
|
|
2440
|
+
singleViolation.line,
|
|
2441
|
+
fileModifications
|
|
2442
|
+
)
|
|
2443
|
+
);
|
|
2444
|
+
if (filteredViolations.length === 0) {
|
|
2445
|
+
return null;
|
|
2446
|
+
}
|
|
2447
|
+
return {
|
|
2448
|
+
file: violation.file,
|
|
2449
|
+
violations: filteredViolations
|
|
2450
|
+
};
|
|
2451
|
+
}).filter((v) => v !== null);
|
|
2452
|
+
}
|
|
2453
|
+
/**
|
|
2454
|
+
* Groups modified lines by file path for efficient lookup.
|
|
2455
|
+
*/
|
|
2456
|
+
groupModifiedLinesByFile(modifiedLines) {
|
|
2457
|
+
const byFile = /* @__PURE__ */ new Map();
|
|
2458
|
+
for (const modification of modifiedLines) {
|
|
2459
|
+
const existing = byFile.get(modification.file) ?? [];
|
|
2460
|
+
existing.push(modification);
|
|
2461
|
+
byFile.set(modification.file, existing);
|
|
2462
|
+
}
|
|
2463
|
+
return byFile;
|
|
2464
|
+
}
|
|
2465
|
+
/**
|
|
2466
|
+
* Checks if a line number falls within any of the modified line ranges.
|
|
2467
|
+
*/
|
|
2468
|
+
isLineInModifiedRanges(line, modifications) {
|
|
2469
|
+
return modifications.some((mod) => {
|
|
2470
|
+
const endLine = mod.startLine + mod.lineCount - 1;
|
|
2471
|
+
return line >= mod.startLine && line <= endLine;
|
|
2472
|
+
});
|
|
1539
2473
|
}
|
|
1540
2474
|
};
|
|
1541
2475
|
|
|
@@ -3208,9 +4142,9 @@ var LinterAstAdapter = class {
|
|
|
3208
4142
|
var TreeSitter17 = __toESM(require("web-tree-sitter"));
|
|
3209
4143
|
|
|
3210
4144
|
// packages/linter-execution/src/application/useCases/ExecuteLinterProgramsUseCase.ts
|
|
3211
|
-
var
|
|
4145
|
+
var origin4 = "ExecuteLinterProgramsUseCase";
|
|
3212
4146
|
var ExecuteLinterProgramsUseCase = class {
|
|
3213
|
-
constructor(linterAstAdapter = new LinterAstAdapter(), logger2 = new PackmindLogger(
|
|
4147
|
+
constructor(linterAstAdapter = new LinterAstAdapter(), logger2 = new PackmindLogger(origin4)) {
|
|
3214
4148
|
this.linterAstAdapter = linterAstAdapter;
|
|
3215
4149
|
this.logger = logger2;
|
|
3216
4150
|
}
|
|
@@ -3341,9 +4275,9 @@ var ExecuteLinterProgramsUseCase = class {
|
|
|
3341
4275
|
let line;
|
|
3342
4276
|
let character = 0;
|
|
3343
4277
|
if (typeof value === "number" && Number.isFinite(value)) {
|
|
3344
|
-
line = value;
|
|
4278
|
+
line = value + 1;
|
|
3345
4279
|
} else if (this.isViolationLike(value)) {
|
|
3346
|
-
line = value.line;
|
|
4280
|
+
line = value.line + 1;
|
|
3347
4281
|
character = value.character ?? 0;
|
|
3348
4282
|
}
|
|
3349
4283
|
if (!this.isValidLine(line)) {
|
|
@@ -3405,14 +4339,30 @@ var ExecuteLinterProgramsUseCase = class {
|
|
|
3405
4339
|
}
|
|
3406
4340
|
};
|
|
3407
4341
|
|
|
3408
|
-
// packages/node-utils/src/
|
|
3409
|
-
var
|
|
4342
|
+
// packages/node-utils/src/dataSources/local.ts
|
|
4343
|
+
var import_typeorm = require("typeorm");
|
|
4344
|
+
var dataSource = makeDatasource();
|
|
4345
|
+
function makeDatasource() {
|
|
4346
|
+
try {
|
|
4347
|
+
return new import_typeorm.DataSource({
|
|
4348
|
+
type: "postgres",
|
|
4349
|
+
url: process.env["DATABASE_URL"],
|
|
4350
|
+
entities: [],
|
|
4351
|
+
migrations: []
|
|
4352
|
+
});
|
|
4353
|
+
} catch {
|
|
4354
|
+
return {};
|
|
4355
|
+
}
|
|
4356
|
+
}
|
|
4357
|
+
|
|
4358
|
+
// packages/node-utils/src/cache/Cache.ts
|
|
4359
|
+
var import_ioredis = __toESM(require("ioredis"));
|
|
3410
4360
|
|
|
3411
4361
|
// packages/node-utils/src/config/infra/Infisical/InfisicalConfig.ts
|
|
3412
4362
|
var import_sdk = require("@infisical/sdk");
|
|
3413
|
-
var
|
|
4363
|
+
var origin5 = "InfisicalConfig";
|
|
3414
4364
|
var InfisicalConfig = class {
|
|
3415
|
-
constructor(clientId, clientSecret, env, projectId, logger2 = new PackmindLogger(
|
|
4365
|
+
constructor(clientId, clientSecret, env, projectId, logger2 = new PackmindLogger(origin5)) {
|
|
3416
4366
|
this.clientId = clientId;
|
|
3417
4367
|
this.clientSecret = clientSecret;
|
|
3418
4368
|
this.env = env;
|
|
@@ -3487,7 +4437,7 @@ var InfisicalConfig = class {
|
|
|
3487
4437
|
};
|
|
3488
4438
|
|
|
3489
4439
|
// packages/node-utils/src/config/config/Configuration.ts
|
|
3490
|
-
var
|
|
4440
|
+
var origin6 = "Configuration";
|
|
3491
4441
|
var Configuration = class _Configuration {
|
|
3492
4442
|
constructor() {
|
|
3493
4443
|
this.initialized = false;
|
|
@@ -3495,7 +4445,7 @@ var Configuration = class _Configuration {
|
|
|
3495
4445
|
}
|
|
3496
4446
|
static {
|
|
3497
4447
|
this.logger = new PackmindLogger(
|
|
3498
|
-
|
|
4448
|
+
origin6,
|
|
3499
4449
|
"info" /* INFO */
|
|
3500
4450
|
);
|
|
3501
4451
|
}
|
|
@@ -3642,8 +4592,7 @@ var Configuration = class _Configuration {
|
|
|
3642
4592
|
};
|
|
3643
4593
|
|
|
3644
4594
|
// packages/node-utils/src/cache/Cache.ts
|
|
3645
|
-
var
|
|
3646
|
-
var origin6 = "Cache";
|
|
4595
|
+
var origin7 = "Cache";
|
|
3647
4596
|
var Cache = class _Cache {
|
|
3648
4597
|
constructor() {
|
|
3649
4598
|
this.initialized = false;
|
|
@@ -3655,7 +4604,7 @@ var Cache = class _Cache {
|
|
|
3655
4604
|
}
|
|
3656
4605
|
static {
|
|
3657
4606
|
this.logger = new PackmindLogger(
|
|
3658
|
-
|
|
4607
|
+
origin7,
|
|
3659
4608
|
"info" /* INFO */
|
|
3660
4609
|
);
|
|
3661
4610
|
}
|
|
@@ -3837,14 +4786,14 @@ var import_common2 = require("@nestjs/common");
|
|
|
3837
4786
|
|
|
3838
4787
|
// packages/node-utils/src/sse/RedisSSEClient.ts
|
|
3839
4788
|
var import_ioredis2 = __toESM(require("ioredis"));
|
|
3840
|
-
var
|
|
4789
|
+
var origin8 = "RedisSSEClient";
|
|
3841
4790
|
var RedisSSEClient = class _RedisSSEClient {
|
|
3842
4791
|
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
|
3843
4792
|
constructor() {
|
|
3844
4793
|
this.initialized = false;
|
|
3845
4794
|
}
|
|
3846
4795
|
static {
|
|
3847
|
-
this.logger = new PackmindLogger(
|
|
4796
|
+
this.logger = new PackmindLogger(origin8);
|
|
3848
4797
|
}
|
|
3849
4798
|
static getInstance() {
|
|
3850
4799
|
_RedisSSEClient.logger.debug("Getting RedisSSEClient instance");
|
|
@@ -4028,10 +4977,10 @@ function serializeSSERedisMessage(message) {
|
|
|
4028
4977
|
}
|
|
4029
4978
|
|
|
4030
4979
|
// packages/node-utils/src/sse/SSEEventPublisher.ts
|
|
4031
|
-
var
|
|
4980
|
+
var origin9 = "SSEEventPublisher";
|
|
4032
4981
|
var SSEEventPublisher = class _SSEEventPublisher {
|
|
4033
4982
|
static {
|
|
4034
|
-
this.logger = new PackmindLogger(
|
|
4983
|
+
this.logger = new PackmindLogger(origin9);
|
|
4035
4984
|
}
|
|
4036
4985
|
/**
|
|
4037
4986
|
* Get the singleton instance
|
|
@@ -4270,25 +5219,9 @@ ${sectionBlock}
|
|
|
4270
5219
|
return result;
|
|
4271
5220
|
}
|
|
4272
5221
|
|
|
4273
|
-
// packages/node-utils/src/dataSources/local.ts
|
|
4274
|
-
var import_typeorm = require("typeorm");
|
|
4275
|
-
var dataSource = makeDatasource();
|
|
4276
|
-
function makeDatasource() {
|
|
4277
|
-
try {
|
|
4278
|
-
return new import_typeorm.DataSource({
|
|
4279
|
-
type: "postgres",
|
|
4280
|
-
url: process.env["DATABASE_URL"],
|
|
4281
|
-
entities: [],
|
|
4282
|
-
migrations: []
|
|
4283
|
-
});
|
|
4284
|
-
} catch {
|
|
4285
|
-
return {};
|
|
4286
|
-
}
|
|
4287
|
-
}
|
|
4288
|
-
|
|
4289
5222
|
// apps/cli/src/application/useCases/PullDataUseCase.ts
|
|
4290
|
-
var
|
|
4291
|
-
var
|
|
5223
|
+
var fs4 = __toESM(require("fs/promises"));
|
|
5224
|
+
var path5 = __toESM(require("path"));
|
|
4292
5225
|
var PullDataUseCase = class {
|
|
4293
5226
|
constructor(packmindGateway) {
|
|
4294
5227
|
this.packmindGateway = packmindGateway;
|
|
@@ -4299,13 +5232,27 @@ var PullDataUseCase = class {
|
|
|
4299
5232
|
filesCreated: 0,
|
|
4300
5233
|
filesUpdated: 0,
|
|
4301
5234
|
filesDeleted: 0,
|
|
4302
|
-
errors: []
|
|
5235
|
+
errors: [],
|
|
5236
|
+
recipesCount: 0,
|
|
5237
|
+
standardsCount: 0
|
|
4303
5238
|
};
|
|
4304
5239
|
const response = await this.packmindGateway.getPullData({
|
|
4305
5240
|
packagesSlugs: command3.packagesSlugs
|
|
4306
5241
|
});
|
|
5242
|
+
const uniqueFilesMap = /* @__PURE__ */ new Map();
|
|
5243
|
+
for (const file of response.fileUpdates.createOrUpdate) {
|
|
5244
|
+
uniqueFilesMap.set(file.path, file);
|
|
5245
|
+
}
|
|
5246
|
+
const uniqueFiles = Array.from(uniqueFilesMap.values());
|
|
5247
|
+
for (const file of uniqueFiles) {
|
|
5248
|
+
if (file.path.includes(".packmind/recipes/") && file.path.endsWith(".md")) {
|
|
5249
|
+
result.recipesCount++;
|
|
5250
|
+
} else if (file.path.includes(".packmind/standards/") && file.path.endsWith(".md")) {
|
|
5251
|
+
result.standardsCount++;
|
|
5252
|
+
}
|
|
5253
|
+
}
|
|
4307
5254
|
try {
|
|
4308
|
-
for (const file of
|
|
5255
|
+
for (const file of uniqueFiles) {
|
|
4309
5256
|
try {
|
|
4310
5257
|
await this.createOrUpdateFile(baseDirectory, file, result);
|
|
4311
5258
|
} catch (error) {
|
|
@@ -4330,9 +5277,9 @@ var PullDataUseCase = class {
|
|
|
4330
5277
|
return result;
|
|
4331
5278
|
}
|
|
4332
5279
|
async createOrUpdateFile(baseDirectory, file, result) {
|
|
4333
|
-
const fullPath =
|
|
4334
|
-
const directory =
|
|
4335
|
-
await
|
|
5280
|
+
const fullPath = path5.join(baseDirectory, file.path);
|
|
5281
|
+
const directory = path5.dirname(fullPath);
|
|
5282
|
+
await fs4.mkdir(directory, { recursive: true });
|
|
4336
5283
|
const fileExists = await this.fileExists(fullPath);
|
|
4337
5284
|
if (file.content !== void 0) {
|
|
4338
5285
|
await this.handleFullContentUpdate(
|
|
@@ -4352,7 +5299,7 @@ var PullDataUseCase = class {
|
|
|
4352
5299
|
}
|
|
4353
5300
|
async handleFullContentUpdate(fullPath, content, fileExists, result) {
|
|
4354
5301
|
if (fileExists) {
|
|
4355
|
-
const existingContent = await
|
|
5302
|
+
const existingContent = await fs4.readFile(fullPath, "utf-8");
|
|
4356
5303
|
const commentMarker = this.extractCommentMarker(content);
|
|
4357
5304
|
let finalContent;
|
|
4358
5305
|
if (!commentMarker) {
|
|
@@ -4364,23 +5311,23 @@ var PullDataUseCase = class {
|
|
|
4364
5311
|
commentMarker
|
|
4365
5312
|
);
|
|
4366
5313
|
}
|
|
4367
|
-
await
|
|
5314
|
+
await fs4.writeFile(fullPath, finalContent, "utf-8");
|
|
4368
5315
|
result.filesUpdated++;
|
|
4369
5316
|
} else {
|
|
4370
|
-
await
|
|
5317
|
+
await fs4.writeFile(fullPath, content, "utf-8");
|
|
4371
5318
|
result.filesCreated++;
|
|
4372
5319
|
}
|
|
4373
5320
|
}
|
|
4374
5321
|
async handleSectionsUpdate(fullPath, sections, fileExists, result) {
|
|
4375
5322
|
let currentContent = "";
|
|
4376
5323
|
if (fileExists) {
|
|
4377
|
-
currentContent = await
|
|
5324
|
+
currentContent = await fs4.readFile(fullPath, "utf-8");
|
|
4378
5325
|
}
|
|
4379
5326
|
const mergedContent = mergeSectionsIntoFileContent(
|
|
4380
5327
|
currentContent,
|
|
4381
5328
|
sections
|
|
4382
5329
|
);
|
|
4383
|
-
await
|
|
5330
|
+
await fs4.writeFile(fullPath, mergedContent, "utf-8");
|
|
4384
5331
|
if (fileExists) {
|
|
4385
5332
|
result.filesUpdated++;
|
|
4386
5333
|
} else {
|
|
@@ -4388,16 +5335,16 @@ var PullDataUseCase = class {
|
|
|
4388
5335
|
}
|
|
4389
5336
|
}
|
|
4390
5337
|
async deleteFile(baseDirectory, filePath, result) {
|
|
4391
|
-
const fullPath =
|
|
5338
|
+
const fullPath = path5.join(baseDirectory, filePath);
|
|
4392
5339
|
const fileExists = await this.fileExists(fullPath);
|
|
4393
5340
|
if (fileExists) {
|
|
4394
|
-
await
|
|
5341
|
+
await fs4.unlink(fullPath);
|
|
4395
5342
|
result.filesDeleted++;
|
|
4396
5343
|
}
|
|
4397
5344
|
}
|
|
4398
5345
|
async fileExists(filePath) {
|
|
4399
5346
|
try {
|
|
4400
|
-
await
|
|
5347
|
+
await fs4.access(filePath);
|
|
4401
5348
|
return true;
|
|
4402
5349
|
} catch {
|
|
4403
5350
|
return false;
|
|
@@ -4470,6 +5417,235 @@ var GetPackageSummaryUseCase = class {
|
|
|
4470
5417
|
}
|
|
4471
5418
|
};
|
|
4472
5419
|
|
|
5420
|
+
// apps/cli/src/infra/repositories/ConfigFileRepository.ts
|
|
5421
|
+
var fs5 = __toESM(require("fs/promises"));
|
|
5422
|
+
var path6 = __toESM(require("path"));
|
|
5423
|
+
|
|
5424
|
+
// apps/cli/src/infra/utils/consoleLogger.ts
|
|
5425
|
+
var import_chalk = __toESM(require("chalk"));
|
|
5426
|
+
var CLI_PREFIX = "packmind-cli";
|
|
5427
|
+
function logWarningConsole(message) {
|
|
5428
|
+
console.warn(import_chalk.default.bgYellow.bold(CLI_PREFIX), import_chalk.default.yellow(message));
|
|
5429
|
+
}
|
|
5430
|
+
function logErrorConsole(message) {
|
|
5431
|
+
console.error(import_chalk.default.bgRed.bold(CLI_PREFIX), import_chalk.default.red(message));
|
|
5432
|
+
}
|
|
5433
|
+
function logSuccessConsole(message) {
|
|
5434
|
+
console.log(import_chalk.default.bgGreen.bold(CLI_PREFIX), import_chalk.default.green.bold(message));
|
|
5435
|
+
}
|
|
5436
|
+
function formatSlug(text) {
|
|
5437
|
+
return import_chalk.default.blue.bold(text);
|
|
5438
|
+
}
|
|
5439
|
+
function formatLabel(text) {
|
|
5440
|
+
return import_chalk.default.dim(text);
|
|
5441
|
+
}
|
|
5442
|
+
function formatError(text) {
|
|
5443
|
+
return import_chalk.default.red(text);
|
|
5444
|
+
}
|
|
5445
|
+
function formatBold(text) {
|
|
5446
|
+
return import_chalk.default.bold(text);
|
|
5447
|
+
}
|
|
5448
|
+
function formatFilePath(text) {
|
|
5449
|
+
return import_chalk.default.underline.gray(text);
|
|
5450
|
+
}
|
|
5451
|
+
|
|
5452
|
+
// apps/cli/src/infra/repositories/ConfigFileRepository.ts
|
|
5453
|
+
var ConfigFileRepository = class {
|
|
5454
|
+
constructor() {
|
|
5455
|
+
this.CONFIG_FILENAME = "packmind.json";
|
|
5456
|
+
this.warnedFiles = /* @__PURE__ */ new Set();
|
|
5457
|
+
this.EXCLUDED_DIRECTORIES = [
|
|
5458
|
+
"node_modules",
|
|
5459
|
+
".git",
|
|
5460
|
+
"dist",
|
|
5461
|
+
"build",
|
|
5462
|
+
"coverage",
|
|
5463
|
+
".nx"
|
|
5464
|
+
];
|
|
5465
|
+
}
|
|
5466
|
+
async writeConfig(baseDirectory, config) {
|
|
5467
|
+
const configPath = path6.join(baseDirectory, this.CONFIG_FILENAME);
|
|
5468
|
+
const configContent = JSON.stringify(config, null, 2) + "\n";
|
|
5469
|
+
await fs5.writeFile(configPath, configContent, "utf-8");
|
|
5470
|
+
}
|
|
5471
|
+
async readConfig(baseDirectory) {
|
|
5472
|
+
const configPath = path6.join(baseDirectory, this.CONFIG_FILENAME);
|
|
5473
|
+
try {
|
|
5474
|
+
const configContent = await fs5.readFile(configPath, "utf-8");
|
|
5475
|
+
const config = JSON.parse(configContent);
|
|
5476
|
+
if (!config.packages || typeof config.packages !== "object") {
|
|
5477
|
+
throw new Error(
|
|
5478
|
+
"Invalid packmind.json structure. Expected { packages: { ... } }"
|
|
5479
|
+
);
|
|
5480
|
+
}
|
|
5481
|
+
return config;
|
|
5482
|
+
} catch (error) {
|
|
5483
|
+
if (error.code === "ENOENT") {
|
|
5484
|
+
return null;
|
|
5485
|
+
}
|
|
5486
|
+
if (!this.warnedFiles.has(configPath)) {
|
|
5487
|
+
this.warnedFiles.add(configPath);
|
|
5488
|
+
logWarningConsole(`\u26A0 Skipping malformed config file: ${configPath}`);
|
|
5489
|
+
}
|
|
5490
|
+
return null;
|
|
5491
|
+
}
|
|
5492
|
+
}
|
|
5493
|
+
/**
|
|
5494
|
+
* Recursively finds all directories containing packmind.json in descendant folders.
|
|
5495
|
+
* Excludes common build/dependency directories (node_modules, .git, dist, etc.)
|
|
5496
|
+
*
|
|
5497
|
+
* @param directory - The root directory to search from
|
|
5498
|
+
* @returns Array of directory paths that contain a packmind.json file
|
|
5499
|
+
*/
|
|
5500
|
+
async findDescendantConfigs(directory) {
|
|
5501
|
+
const normalizedDir = path6.resolve(directory);
|
|
5502
|
+
const results = [];
|
|
5503
|
+
const searchRecursively = async (currentDir) => {
|
|
5504
|
+
let entries;
|
|
5505
|
+
try {
|
|
5506
|
+
entries = await fs5.readdir(currentDir, { withFileTypes: true });
|
|
5507
|
+
} catch {
|
|
5508
|
+
return;
|
|
5509
|
+
}
|
|
5510
|
+
for (const entry of entries) {
|
|
5511
|
+
if (!entry.isDirectory()) {
|
|
5512
|
+
continue;
|
|
5513
|
+
}
|
|
5514
|
+
if (this.EXCLUDED_DIRECTORIES.includes(entry.name)) {
|
|
5515
|
+
continue;
|
|
5516
|
+
}
|
|
5517
|
+
const entryPath = path6.join(currentDir, entry.name);
|
|
5518
|
+
const config = await this.readConfig(entryPath);
|
|
5519
|
+
if (config) {
|
|
5520
|
+
results.push(entryPath);
|
|
5521
|
+
}
|
|
5522
|
+
await searchRecursively(entryPath);
|
|
5523
|
+
}
|
|
5524
|
+
};
|
|
5525
|
+
await searchRecursively(normalizedDir);
|
|
5526
|
+
return results;
|
|
5527
|
+
}
|
|
5528
|
+
/**
|
|
5529
|
+
* Reads all packmind.json files from startDirectory up to stopDirectory (inclusive)
|
|
5530
|
+
* and merges their package configurations.
|
|
5531
|
+
*
|
|
5532
|
+
* @param startDirectory - Directory to start searching from (typically the lint target)
|
|
5533
|
+
* @param stopDirectory - Directory to stop searching at (typically git repo root), or null to walk to filesystem root
|
|
5534
|
+
* @returns Merged configuration from all found packmind.json files
|
|
5535
|
+
*/
|
|
5536
|
+
async readHierarchicalConfig(startDirectory, stopDirectory) {
|
|
5537
|
+
const configs = [];
|
|
5538
|
+
const configPaths = [];
|
|
5539
|
+
const normalizedStart = path6.resolve(startDirectory);
|
|
5540
|
+
const normalizedStop = stopDirectory ? path6.resolve(stopDirectory) : null;
|
|
5541
|
+
let currentDir = normalizedStart;
|
|
5542
|
+
while (true) {
|
|
5543
|
+
const config = await this.readConfig(currentDir);
|
|
5544
|
+
if (config) {
|
|
5545
|
+
configs.push(config);
|
|
5546
|
+
configPaths.push(path6.join(currentDir, this.CONFIG_FILENAME));
|
|
5547
|
+
}
|
|
5548
|
+
if (normalizedStop !== null && currentDir === normalizedStop) {
|
|
5549
|
+
break;
|
|
5550
|
+
}
|
|
5551
|
+
const parentDir = path6.dirname(currentDir);
|
|
5552
|
+
if (parentDir === currentDir) {
|
|
5553
|
+
break;
|
|
5554
|
+
}
|
|
5555
|
+
currentDir = parentDir;
|
|
5556
|
+
}
|
|
5557
|
+
const mergedPackages = {};
|
|
5558
|
+
for (const config of configs) {
|
|
5559
|
+
for (const [slug, version] of Object.entries(config.packages)) {
|
|
5560
|
+
if (!(slug in mergedPackages)) {
|
|
5561
|
+
mergedPackages[slug] = version;
|
|
5562
|
+
}
|
|
5563
|
+
}
|
|
5564
|
+
}
|
|
5565
|
+
return {
|
|
5566
|
+
packages: mergedPackages,
|
|
5567
|
+
configPaths,
|
|
5568
|
+
hasConfigs: configs.length > 0
|
|
5569
|
+
};
|
|
5570
|
+
}
|
|
5571
|
+
/**
|
|
5572
|
+
* Finds all packmind.json files in the tree (both ancestors and descendants)
|
|
5573
|
+
* and returns each config with its target path.
|
|
5574
|
+
*
|
|
5575
|
+
* @param startDirectory - Directory to start searching from (typically the lint target)
|
|
5576
|
+
* @param stopDirectory - Directory to stop ancestor search at (typically git repo root), also used as base for descendants search
|
|
5577
|
+
* @returns All configs found with their target paths
|
|
5578
|
+
*/
|
|
5579
|
+
async findAllConfigsInTree(startDirectory, stopDirectory) {
|
|
5580
|
+
const normalizedStart = path6.resolve(startDirectory);
|
|
5581
|
+
const normalizedStop = stopDirectory ? path6.resolve(stopDirectory) : null;
|
|
5582
|
+
const basePath = normalizedStop ?? normalizedStart;
|
|
5583
|
+
const configsMap = /* @__PURE__ */ new Map();
|
|
5584
|
+
let currentDir = normalizedStart;
|
|
5585
|
+
while (true) {
|
|
5586
|
+
const config = await this.readConfig(currentDir);
|
|
5587
|
+
if (config) {
|
|
5588
|
+
const targetPath = this.computeRelativeTargetPath(currentDir, basePath);
|
|
5589
|
+
configsMap.set(currentDir, {
|
|
5590
|
+
targetPath,
|
|
5591
|
+
absoluteTargetPath: currentDir,
|
|
5592
|
+
packages: config.packages
|
|
5593
|
+
});
|
|
5594
|
+
}
|
|
5595
|
+
if (normalizedStop !== null && currentDir === normalizedStop) {
|
|
5596
|
+
break;
|
|
5597
|
+
}
|
|
5598
|
+
const parentDir = path6.dirname(currentDir);
|
|
5599
|
+
if (parentDir === currentDir) {
|
|
5600
|
+
break;
|
|
5601
|
+
}
|
|
5602
|
+
currentDir = parentDir;
|
|
5603
|
+
}
|
|
5604
|
+
const searchRoot = normalizedStop ?? normalizedStart;
|
|
5605
|
+
const descendantDirs = await this.findDescendantConfigs(searchRoot);
|
|
5606
|
+
for (const descendantDir of descendantDirs) {
|
|
5607
|
+
if (configsMap.has(descendantDir)) {
|
|
5608
|
+
continue;
|
|
5609
|
+
}
|
|
5610
|
+
const config = await this.readConfig(descendantDir);
|
|
5611
|
+
if (config) {
|
|
5612
|
+
const targetPath = this.computeRelativeTargetPath(
|
|
5613
|
+
descendantDir,
|
|
5614
|
+
basePath
|
|
5615
|
+
);
|
|
5616
|
+
configsMap.set(descendantDir, {
|
|
5617
|
+
targetPath,
|
|
5618
|
+
absoluteTargetPath: descendantDir,
|
|
5619
|
+
packages: config.packages
|
|
5620
|
+
});
|
|
5621
|
+
}
|
|
5622
|
+
}
|
|
5623
|
+
if (!configsMap.has(searchRoot)) {
|
|
5624
|
+
const rootConfig = await this.readConfig(searchRoot);
|
|
5625
|
+
if (rootConfig) {
|
|
5626
|
+
configsMap.set(searchRoot, {
|
|
5627
|
+
targetPath: "/",
|
|
5628
|
+
absoluteTargetPath: searchRoot,
|
|
5629
|
+
packages: rootConfig.packages
|
|
5630
|
+
});
|
|
5631
|
+
}
|
|
5632
|
+
}
|
|
5633
|
+
const configs = Array.from(configsMap.values());
|
|
5634
|
+
return {
|
|
5635
|
+
configs,
|
|
5636
|
+
hasConfigs: configs.length > 0,
|
|
5637
|
+
basePath
|
|
5638
|
+
};
|
|
5639
|
+
}
|
|
5640
|
+
computeRelativeTargetPath(absolutePath, basePath) {
|
|
5641
|
+
if (absolutePath === basePath) {
|
|
5642
|
+
return "/";
|
|
5643
|
+
}
|
|
5644
|
+
const relativePath = absolutePath.substring(basePath.length);
|
|
5645
|
+
return relativePath.startsWith("/") ? relativePath : "/" + relativePath;
|
|
5646
|
+
}
|
|
5647
|
+
};
|
|
5648
|
+
|
|
4473
5649
|
// apps/cli/src/PackmindCliHexaFactory.ts
|
|
4474
5650
|
var PackmindCliHexaFactory = class {
|
|
4475
5651
|
constructor(logger2) {
|
|
@@ -4477,12 +5653,14 @@ var PackmindCliHexaFactory = class {
|
|
|
4477
5653
|
this.repositories = {
|
|
4478
5654
|
packmindGateway: new PackmindGateway(
|
|
4479
5655
|
process.env.PACKMIND_API_KEY_V3 || ""
|
|
4480
|
-
)
|
|
5656
|
+
),
|
|
5657
|
+
configFileRepository: new ConfigFileRepository()
|
|
4481
5658
|
};
|
|
4482
5659
|
this.services = {
|
|
4483
5660
|
listFiles: new ListFiles(),
|
|
4484
5661
|
gitRemoteUrlService: new GitService(this.logger),
|
|
4485
|
-
linterExecutionUseCase: new ExecuteLinterProgramsUseCase()
|
|
5662
|
+
linterExecutionUseCase: new ExecuteLinterProgramsUseCase(),
|
|
5663
|
+
diffViolationFilterService: new DiffViolationFilterService()
|
|
4486
5664
|
};
|
|
4487
5665
|
this.useCases = {
|
|
4488
5666
|
executeSingleFileAst: new ExecuteSingleFileAstUseCase(
|
|
@@ -4495,6 +5673,11 @@ var PackmindCliHexaFactory = class {
|
|
|
4495
5673
|
this.repositories,
|
|
4496
5674
|
this.logger
|
|
4497
5675
|
),
|
|
5676
|
+
lintFilesLocally: new LintFilesLocallyUseCase(
|
|
5677
|
+
this.services,
|
|
5678
|
+
this.repositories,
|
|
5679
|
+
this.logger
|
|
5680
|
+
),
|
|
4498
5681
|
pullData: new PullDataUseCase(this.repositories.packmindGateway),
|
|
4499
5682
|
listPackages: new ListPackagesUseCase(this.repositories.packmindGateway),
|
|
4500
5683
|
getPackageBySlug: new GetPackageSummaryUseCase(
|
|
@@ -4505,9 +5688,9 @@ var PackmindCliHexaFactory = class {
|
|
|
4505
5688
|
};
|
|
4506
5689
|
|
|
4507
5690
|
// apps/cli/src/PackmindCliHexa.ts
|
|
4508
|
-
var
|
|
5691
|
+
var origin10 = "PackmindCliHexa";
|
|
4509
5692
|
var PackmindCliHexa = class {
|
|
4510
|
-
constructor(logger2 = new PackmindLogger(
|
|
5693
|
+
constructor(logger2 = new PackmindLogger(origin10)) {
|
|
4511
5694
|
this.logger = logger2;
|
|
4512
5695
|
try {
|
|
4513
5696
|
this.hexa = new PackmindCliHexaFactory(this.logger);
|
|
@@ -4537,6 +5720,9 @@ var PackmindCliHexa = class {
|
|
|
4537
5720
|
async lintFilesInDirectory(command3) {
|
|
4538
5721
|
return this.hexa.useCases.lintFilesInDirectory.execute(command3);
|
|
4539
5722
|
}
|
|
5723
|
+
async lintFilesLocally(command3) {
|
|
5724
|
+
return this.hexa.useCases.lintFilesLocally.execute(command3);
|
|
5725
|
+
}
|
|
4540
5726
|
async pullData(command3) {
|
|
4541
5727
|
return this.hexa.useCases.pullData.execute(command3);
|
|
4542
5728
|
}
|
|
@@ -4546,6 +5732,57 @@ var PackmindCliHexa = class {
|
|
|
4546
5732
|
async getPackageBySlug(command3) {
|
|
4547
5733
|
return this.hexa.useCases.getPackageBySlug.execute(command3);
|
|
4548
5734
|
}
|
|
5735
|
+
async writeConfig(baseDirectory, packagesSlugs) {
|
|
5736
|
+
const config = {
|
|
5737
|
+
packages: packagesSlugs.reduce(
|
|
5738
|
+
(acc, slug) => {
|
|
5739
|
+
acc[slug] = "*";
|
|
5740
|
+
return acc;
|
|
5741
|
+
},
|
|
5742
|
+
{}
|
|
5743
|
+
)
|
|
5744
|
+
};
|
|
5745
|
+
await this.hexa.repositories.configFileRepository.writeConfig(
|
|
5746
|
+
baseDirectory,
|
|
5747
|
+
config
|
|
5748
|
+
);
|
|
5749
|
+
}
|
|
5750
|
+
async readConfig(baseDirectory) {
|
|
5751
|
+
const config = await this.hexa.repositories.configFileRepository.readConfig(
|
|
5752
|
+
baseDirectory
|
|
5753
|
+
);
|
|
5754
|
+
if (!config) return [];
|
|
5755
|
+
const hasNonWildcardVersions = Object.values(config.packages).some(
|
|
5756
|
+
(version) => version !== "*"
|
|
5757
|
+
);
|
|
5758
|
+
if (hasNonWildcardVersions) {
|
|
5759
|
+
logWarningConsole(
|
|
5760
|
+
"Package versions are not supported yet, getting the latest version"
|
|
5761
|
+
);
|
|
5762
|
+
}
|
|
5763
|
+
return Object.keys(config.packages);
|
|
5764
|
+
}
|
|
5765
|
+
async readHierarchicalConfig(startDirectory, stopDirectory) {
|
|
5766
|
+
return this.hexa.repositories.configFileRepository.readHierarchicalConfig(
|
|
5767
|
+
startDirectory,
|
|
5768
|
+
stopDirectory
|
|
5769
|
+
);
|
|
5770
|
+
}
|
|
5771
|
+
async findDescendantConfigs(directory) {
|
|
5772
|
+
return this.hexa.repositories.configFileRepository.findDescendantConfigs(
|
|
5773
|
+
directory
|
|
5774
|
+
);
|
|
5775
|
+
}
|
|
5776
|
+
async getGitRepositoryRoot(directory) {
|
|
5777
|
+
return this.hexa.services.gitRemoteUrlService.getGitRepositoryRoot(
|
|
5778
|
+
directory
|
|
5779
|
+
);
|
|
5780
|
+
}
|
|
5781
|
+
async tryGetGitRepositoryRoot(directory) {
|
|
5782
|
+
return this.hexa.services.gitRemoteUrlService.tryGetGitRepositoryRoot(
|
|
5783
|
+
directory
|
|
5784
|
+
);
|
|
5785
|
+
}
|
|
4549
5786
|
};
|
|
4550
5787
|
|
|
4551
5788
|
// apps/cli/src/infra/repositories/IDELintLogger.ts
|
|
@@ -4565,7 +5802,6 @@ var IDELintLogger = class {
|
|
|
4565
5802
|
};
|
|
4566
5803
|
|
|
4567
5804
|
// apps/cli/src/infra/repositories/HumanReadableLogger.ts
|
|
4568
|
-
var import_chalk = __toESM(require("chalk"));
|
|
4569
5805
|
var HumanReadableLogger = class {
|
|
4570
5806
|
logViolations(violations) {
|
|
4571
5807
|
violations.forEach((violation) => {
|
|
@@ -4576,29 +5812,117 @@ var HumanReadableLogger = class {
|
|
|
4576
5812
|
(acc, violation) => acc + violation.violations.length,
|
|
4577
5813
|
0
|
|
4578
5814
|
);
|
|
4579
|
-
|
|
4580
|
-
|
|
4581
|
-
import_chalk.default.red(
|
|
4582
|
-
`\u274C Found ${import_chalk.default.bold(totalViolationCount)} violation(s) in ${import_chalk.default.bold(violations.length)} file(s)`
|
|
4583
|
-
)
|
|
5815
|
+
logErrorConsole(
|
|
5816
|
+
`\u274C Found ${formatBold(String(totalViolationCount))} violation(s) in ${formatBold(String(violations.length))} file(s)`
|
|
4584
5817
|
);
|
|
4585
5818
|
} else {
|
|
4586
|
-
|
|
4587
|
-
import_chalk.default.bgGreen.bold("packmind-cli"),
|
|
4588
|
-
import_chalk.default.green.bold(`\u2705 No violations found`)
|
|
4589
|
-
);
|
|
5819
|
+
logSuccessConsole(`\u2705 No violations found`);
|
|
4590
5820
|
}
|
|
4591
5821
|
}
|
|
4592
5822
|
logViolation(violation) {
|
|
4593
|
-
console.log(
|
|
5823
|
+
console.log(formatFilePath(violation.file));
|
|
4594
5824
|
violation.violations.forEach(({ line, character, standard, rule }) => {
|
|
4595
5825
|
console.log(
|
|
4596
|
-
|
|
5826
|
+
formatError(` ${line}:${character} error @${standard}/${rule}`)
|
|
4597
5827
|
);
|
|
4598
5828
|
});
|
|
4599
5829
|
}
|
|
4600
5830
|
};
|
|
4601
5831
|
|
|
5832
|
+
// apps/cli/src/infra/commands/LinterCommand.ts
|
|
5833
|
+
var pathModule = __toESM(require("path"));
|
|
5834
|
+
|
|
5835
|
+
// apps/cli/src/infra/commands/lintHandler.ts
|
|
5836
|
+
var MISSING_API_KEY_ERROR = "Please set the PACKMIND_API_KEY_V3 environment variable";
|
|
5837
|
+
function isMissingApiKeyError(error) {
|
|
5838
|
+
if (error instanceof Error) {
|
|
5839
|
+
return error.message.includes(MISSING_API_KEY_ERROR);
|
|
5840
|
+
}
|
|
5841
|
+
return false;
|
|
5842
|
+
}
|
|
5843
|
+
async function lintHandler(args2, deps) {
|
|
5844
|
+
const {
|
|
5845
|
+
path: path8,
|
|
5846
|
+
draft,
|
|
5847
|
+
rule,
|
|
5848
|
+
language,
|
|
5849
|
+
logger: logger2,
|
|
5850
|
+
continueOnError,
|
|
5851
|
+
continueOnMissingKey,
|
|
5852
|
+
diff
|
|
5853
|
+
} = args2;
|
|
5854
|
+
const {
|
|
5855
|
+
packmindCliHexa,
|
|
5856
|
+
humanReadableLogger,
|
|
5857
|
+
ideLintLogger,
|
|
5858
|
+
resolvePath,
|
|
5859
|
+
exit
|
|
5860
|
+
} = deps;
|
|
5861
|
+
if (draft && !rule) {
|
|
5862
|
+
throw new Error("option --rule is required to use --draft mode");
|
|
5863
|
+
}
|
|
5864
|
+
const startedAt = Date.now();
|
|
5865
|
+
const targetPath = path8 ?? ".";
|
|
5866
|
+
const hasArguments = !!(draft || rule || language);
|
|
5867
|
+
const absolutePath = resolvePath(targetPath);
|
|
5868
|
+
if (diff) {
|
|
5869
|
+
const gitRoot = await packmindCliHexa.tryGetGitRepositoryRoot(absolutePath);
|
|
5870
|
+
if (!gitRoot) {
|
|
5871
|
+
throw new Error(
|
|
5872
|
+
"The --diff option requires the project to be in a Git repository"
|
|
5873
|
+
);
|
|
5874
|
+
}
|
|
5875
|
+
}
|
|
5876
|
+
let useLocalLinting = false;
|
|
5877
|
+
if (!hasArguments) {
|
|
5878
|
+
const stopDirectory = await packmindCliHexa.tryGetGitRepositoryRoot(absolutePath);
|
|
5879
|
+
const hierarchicalConfig = await packmindCliHexa.readHierarchicalConfig(
|
|
5880
|
+
absolutePath,
|
|
5881
|
+
stopDirectory
|
|
5882
|
+
);
|
|
5883
|
+
if (hierarchicalConfig.hasConfigs) {
|
|
5884
|
+
useLocalLinting = true;
|
|
5885
|
+
}
|
|
5886
|
+
}
|
|
5887
|
+
let violations = [];
|
|
5888
|
+
try {
|
|
5889
|
+
if (useLocalLinting) {
|
|
5890
|
+
const result = await packmindCliHexa.lintFilesLocally({
|
|
5891
|
+
path: absolutePath,
|
|
5892
|
+
diffMode: diff
|
|
5893
|
+
});
|
|
5894
|
+
violations = result.violations;
|
|
5895
|
+
} else {
|
|
5896
|
+
const result = await packmindCliHexa.lintFilesInDirectory({
|
|
5897
|
+
path: targetPath,
|
|
5898
|
+
draftMode: draft,
|
|
5899
|
+
standardSlug: rule?.standardSlug,
|
|
5900
|
+
ruleId: rule?.ruleId,
|
|
5901
|
+
language,
|
|
5902
|
+
diffMode: diff
|
|
5903
|
+
});
|
|
5904
|
+
violations = result.violations;
|
|
5905
|
+
}
|
|
5906
|
+
} catch (error) {
|
|
5907
|
+
if (isMissingApiKeyError(error) && continueOnMissingKey) {
|
|
5908
|
+
console.warn("Warning: No PACKMIND_API_KEY_V3 set, linting is skipped.");
|
|
5909
|
+
exit(0);
|
|
5910
|
+
return;
|
|
5911
|
+
}
|
|
5912
|
+
throw error;
|
|
5913
|
+
}
|
|
5914
|
+
(logger2 === "ide" /* ide */ ? ideLintLogger : humanReadableLogger).logViolations(
|
|
5915
|
+
violations
|
|
5916
|
+
);
|
|
5917
|
+
const durationSeconds = (Date.now() - startedAt) / 1e3;
|
|
5918
|
+
console.log(`Lint completed in ${durationSeconds.toFixed(2)}s`);
|
|
5919
|
+
if (violations.length > 0 && !continueOnError) {
|
|
5920
|
+
exit(1);
|
|
5921
|
+
} else {
|
|
5922
|
+
exit(0);
|
|
5923
|
+
}
|
|
5924
|
+
}
|
|
5925
|
+
|
|
4602
5926
|
// apps/cli/src/infra/commands/LinterCommand.ts
|
|
4603
5927
|
var Logger = {
|
|
4604
5928
|
from: async (input) => {
|
|
@@ -4627,6 +5951,19 @@ var RuleID = {
|
|
|
4627
5951
|
};
|
|
4628
5952
|
}
|
|
4629
5953
|
};
|
|
5954
|
+
var DiffModeType = {
|
|
5955
|
+
from: async (input) => {
|
|
5956
|
+
switch (input) {
|
|
5957
|
+
case "files":
|
|
5958
|
+
return "files" /* FILES */;
|
|
5959
|
+
case "lines":
|
|
5960
|
+
return "lines" /* LINES */;
|
|
5961
|
+
}
|
|
5962
|
+
throw new Error(
|
|
5963
|
+
`${input} is not a valid value for the --diff option. Expected values are: files, lines`
|
|
5964
|
+
);
|
|
5965
|
+
}
|
|
5966
|
+
};
|
|
4630
5967
|
var lintCommand = (0, import_cmd_ts.command)({
|
|
4631
5968
|
name: "lint",
|
|
4632
5969
|
description: "Lint code at the specified path",
|
|
@@ -4660,33 +5997,34 @@ var lintCommand = (0, import_cmd_ts.command)({
|
|
|
4660
5997
|
debug: (0, import_cmd_ts.flag)({
|
|
4661
5998
|
long: "debug",
|
|
4662
5999
|
description: "Enable debug logging"
|
|
6000
|
+
}),
|
|
6001
|
+
continueOnError: (0, import_cmd_ts.flag)({
|
|
6002
|
+
long: "continue-on-error",
|
|
6003
|
+
description: "Exit with status code 0 even if violations are found"
|
|
6004
|
+
}),
|
|
6005
|
+
continueOnMissingKey: (0, import_cmd_ts.flag)({
|
|
6006
|
+
long: "continue-on-missing-key",
|
|
6007
|
+
description: "Skip linting and exit with status code 0 if PACKMIND_API_KEY_V3 is not set"
|
|
6008
|
+
}),
|
|
6009
|
+
diff: (0, import_cmd_ts.option)({
|
|
6010
|
+
long: "diff",
|
|
6011
|
+
description: "Filter violations by git diff (files | lines)",
|
|
6012
|
+
type: (0, import_cmd_ts.optional)(DiffModeType)
|
|
4663
6013
|
})
|
|
4664
6014
|
},
|
|
4665
|
-
handler: async (
|
|
4666
|
-
if (draft && !rule) {
|
|
4667
|
-
throw new Error("option --rule is required to use --draft mode");
|
|
4668
|
-
}
|
|
4669
|
-
const startedAt = Date.now();
|
|
6015
|
+
handler: async (args2) => {
|
|
4670
6016
|
const packmindLogger = new PackmindLogger(
|
|
4671
6017
|
"PackmindCLI",
|
|
4672
|
-
debug ? "debug" /* DEBUG */ : "info" /* INFO */
|
|
6018
|
+
args2.debug ? "debug" /* DEBUG */ : "info" /* INFO */
|
|
4673
6019
|
);
|
|
4674
|
-
const
|
|
4675
|
-
|
|
4676
|
-
|
|
4677
|
-
|
|
4678
|
-
|
|
4679
|
-
|
|
4680
|
-
|
|
4681
|
-
|
|
4682
|
-
(logger2 === "ide" /* ide */ ? new IDELintLogger() : new HumanReadableLogger()).logViolations(violations);
|
|
4683
|
-
const durationSeconds = (Date.now() - startedAt) / 1e3;
|
|
4684
|
-
console.log(`Lint completed in ${durationSeconds.toFixed(2)}s`);
|
|
4685
|
-
if (violations.length > 0) {
|
|
4686
|
-
process.exit(1);
|
|
4687
|
-
} else {
|
|
4688
|
-
process.exit(0);
|
|
4689
|
-
}
|
|
6020
|
+
const deps = {
|
|
6021
|
+
packmindCliHexa: new PackmindCliHexa(packmindLogger),
|
|
6022
|
+
humanReadableLogger: new HumanReadableLogger(),
|
|
6023
|
+
ideLintLogger: new IDELintLogger(),
|
|
6024
|
+
resolvePath: (targetPath) => pathModule.isAbsolute(targetPath) ? targetPath : pathModule.resolve(process.cwd(), targetPath),
|
|
6025
|
+
exit: (code) => process.exit(code)
|
|
6026
|
+
};
|
|
6027
|
+
await lintHandler(args2, deps);
|
|
4690
6028
|
}
|
|
4691
6029
|
});
|
|
4692
6030
|
|
|
@@ -4746,8 +6084,8 @@ function extractWasmFiles() {
|
|
|
4746
6084
|
|
|
4747
6085
|
// apps/cli/src/main.ts
|
|
4748
6086
|
var import_dotenv = require("dotenv");
|
|
4749
|
-
var
|
|
4750
|
-
var
|
|
6087
|
+
var fs6 = __toESM(require("fs"));
|
|
6088
|
+
var path7 = __toESM(require("path"));
|
|
4751
6089
|
|
|
4752
6090
|
// apps/cli/src/infra/commands/PullCommand.ts
|
|
4753
6091
|
var import_cmd_ts2 = require("cmd-ts");
|
|
@@ -4782,10 +6120,28 @@ var pullCommand = (0, import_cmd_ts2.command)({
|
|
|
4782
6120
|
console.log("No packages found.");
|
|
4783
6121
|
process.exit(0);
|
|
4784
6122
|
}
|
|
4785
|
-
|
|
4786
|
-
|
|
4787
|
-
|
|
6123
|
+
const sortedPackages = [...packages].sort(
|
|
6124
|
+
(a, b) => a.slug.localeCompare(b.slug)
|
|
6125
|
+
);
|
|
6126
|
+
console.log("Available packages:\n");
|
|
6127
|
+
sortedPackages.forEach((pkg, index) => {
|
|
6128
|
+
console.log(`- ${formatSlug(pkg.slug)}`);
|
|
6129
|
+
console.log(` ${formatLabel("Name:")} ${pkg.name}`);
|
|
6130
|
+
if (pkg.description) {
|
|
6131
|
+
const descriptionLines = pkg.description.trim().split("\n").map((line) => line.trim()).filter((line) => line.length > 0);
|
|
6132
|
+
const [firstLine, ...restLines] = descriptionLines;
|
|
6133
|
+
console.log(` ${formatLabel("Description:")} ${firstLine}`);
|
|
6134
|
+
restLines.forEach((line) => {
|
|
6135
|
+
console.log(` ${line}`);
|
|
6136
|
+
});
|
|
6137
|
+
}
|
|
6138
|
+
if (index < sortedPackages.length - 1) {
|
|
6139
|
+
console.log("");
|
|
6140
|
+
}
|
|
4788
6141
|
});
|
|
6142
|
+
const exampleSlug = formatSlug(sortedPackages[0].slug);
|
|
6143
|
+
console.log("\nHow to install a package:\n");
|
|
6144
|
+
console.log(` $ packmind-cli install ${exampleSlug}`);
|
|
4789
6145
|
process.exit(0);
|
|
4790
6146
|
} catch (error) {
|
|
4791
6147
|
console.error("\n\u274C Failed to list packages:");
|
|
@@ -4841,30 +6197,59 @@ var pullCommand = (0, import_cmd_ts2.command)({
|
|
|
4841
6197
|
process.exit(1);
|
|
4842
6198
|
}
|
|
4843
6199
|
}
|
|
4844
|
-
|
|
4845
|
-
|
|
4846
|
-
|
|
6200
|
+
let configPackages;
|
|
6201
|
+
let configExists = false;
|
|
6202
|
+
try {
|
|
6203
|
+
configPackages = await packmindCliHexa.readConfig(process.cwd());
|
|
6204
|
+
configExists = configPackages.length > 0;
|
|
6205
|
+
} catch (error) {
|
|
6206
|
+
console.error("ERROR Failed to parse packmind.json");
|
|
6207
|
+
if (error instanceof Error) {
|
|
6208
|
+
console.error(`ERROR ${error.message}`);
|
|
6209
|
+
} else {
|
|
6210
|
+
console.error(`ERROR ${String(error)}`);
|
|
6211
|
+
}
|
|
6212
|
+
console.error(
|
|
6213
|
+
"\n\u{1F4A1} Please fix the packmind.json file or delete it to continue."
|
|
6214
|
+
);
|
|
6215
|
+
process.exit(1);
|
|
6216
|
+
}
|
|
6217
|
+
const allPackages = [.../* @__PURE__ */ new Set([...configPackages, ...packagesSlugs])];
|
|
6218
|
+
if (allPackages.length === 0) {
|
|
6219
|
+
logWarningConsole("config packmind.json not found");
|
|
6220
|
+
console.log(
|
|
6221
|
+
"Usage: packmind-cli install <package-slug> [package-slug...]"
|
|
6222
|
+
);
|
|
6223
|
+
console.log(" packmind-cli install --list");
|
|
4847
6224
|
console.log("");
|
|
4848
6225
|
console.log("Examples:");
|
|
4849
|
-
console.log(" packmind-cli
|
|
4850
|
-
console.log(" packmind-cli
|
|
4851
|
-
console.log(" packmind-cli
|
|
6226
|
+
console.log(" packmind-cli install backend");
|
|
6227
|
+
console.log(" packmind-cli install backend frontend");
|
|
6228
|
+
console.log(" packmind-cli install --list # Show available packages");
|
|
4852
6229
|
console.log("");
|
|
4853
|
-
console.log("
|
|
6230
|
+
console.log("Install recipes and standards from the specified packages.");
|
|
4854
6231
|
process.exit(0);
|
|
4855
6232
|
}
|
|
4856
|
-
|
|
4857
|
-
|
|
4858
|
-
|
|
6233
|
+
if (!configExists && packagesSlugs.length > 0) {
|
|
6234
|
+
console.log("INFO initializing packmind.json");
|
|
6235
|
+
}
|
|
4859
6236
|
try {
|
|
6237
|
+
const packageCount = allPackages.length;
|
|
6238
|
+
const packageWord = packageCount === 1 ? "package" : "packages";
|
|
6239
|
+
console.log(
|
|
6240
|
+
`Fetching ${packageCount} ${packageWord}: ${allPackages.join(", ")}...`
|
|
6241
|
+
);
|
|
4860
6242
|
const result = await packmindCliHexa.pullData({
|
|
4861
6243
|
baseDirectory: process.cwd(),
|
|
4862
|
-
packagesSlugs
|
|
6244
|
+
packagesSlugs: allPackages
|
|
4863
6245
|
});
|
|
4864
|
-
console.log(
|
|
4865
|
-
|
|
4866
|
-
|
|
4867
|
-
console.log(
|
|
6246
|
+
console.log(
|
|
6247
|
+
`Installing ${result.recipesCount} recipes and ${result.standardsCount} standards...`
|
|
6248
|
+
);
|
|
6249
|
+
console.log(
|
|
6250
|
+
`
|
|
6251
|
+
added ${result.filesCreated} files, changed ${result.filesUpdated} files, removed ${result.filesDeleted} files`
|
|
6252
|
+
);
|
|
4868
6253
|
if (result.errors.length > 0) {
|
|
4869
6254
|
console.log("\n\u26A0\uFE0F Errors encountered:");
|
|
4870
6255
|
result.errors.forEach((error) => {
|
|
@@ -4872,15 +6257,54 @@ var pullCommand = (0, import_cmd_ts2.command)({
|
|
|
4872
6257
|
});
|
|
4873
6258
|
process.exit(1);
|
|
4874
6259
|
}
|
|
6260
|
+
await packmindCliHexa.writeConfig(process.cwd(), allPackages);
|
|
4875
6261
|
} catch (error) {
|
|
4876
|
-
console.error("\n\u274C Failed to
|
|
6262
|
+
console.error("\n\u274C Failed to install content:");
|
|
4877
6263
|
if (error instanceof Error) {
|
|
4878
6264
|
const errorObj = error;
|
|
4879
6265
|
if (errorObj.statusCode === 404) {
|
|
4880
6266
|
console.error(` ${errorObj.message}`);
|
|
4881
|
-
|
|
4882
|
-
|
|
4883
|
-
|
|
6267
|
+
if (configExists && configPackages.length > 0) {
|
|
6268
|
+
const missingPackages = allPackages.filter(
|
|
6269
|
+
(pkg) => configPackages.includes(pkg)
|
|
6270
|
+
);
|
|
6271
|
+
if (missingPackages.length > 0) {
|
|
6272
|
+
console.error(
|
|
6273
|
+
"\n\u{1F4A1} Either remove the following package(s) from packmind.json:"
|
|
6274
|
+
);
|
|
6275
|
+
missingPackages.forEach((pkg) => {
|
|
6276
|
+
console.error(` "${pkg}"`);
|
|
6277
|
+
});
|
|
6278
|
+
console.error(" Or ensure that:");
|
|
6279
|
+
console.error(
|
|
6280
|
+
" - The package slug exists and is correctly spelled"
|
|
6281
|
+
);
|
|
6282
|
+
console.error(" - The package exists in your organization");
|
|
6283
|
+
console.error(" - You have the correct API key configured");
|
|
6284
|
+
} else {
|
|
6285
|
+
console.error("\n\u{1F4A1} Troubleshooting tips:");
|
|
6286
|
+
console.error(
|
|
6287
|
+
" - Check if the package slug exists and is correctly spelled"
|
|
6288
|
+
);
|
|
6289
|
+
console.error(
|
|
6290
|
+
" - Check that the package exists in your organization"
|
|
6291
|
+
);
|
|
6292
|
+
console.error(
|
|
6293
|
+
" - Ensure you have the correct API key configured"
|
|
6294
|
+
);
|
|
6295
|
+
}
|
|
6296
|
+
} else {
|
|
6297
|
+
console.error("\n\u{1F4A1} Troubleshooting tips:");
|
|
6298
|
+
console.error(
|
|
6299
|
+
" - Check if the package slug exists and is correctly spelled"
|
|
6300
|
+
);
|
|
6301
|
+
console.error(
|
|
6302
|
+
" - Check that the package exists in your organization"
|
|
6303
|
+
);
|
|
6304
|
+
console.error(
|
|
6305
|
+
" - Ensure you have the correct API key configured"
|
|
6306
|
+
);
|
|
6307
|
+
}
|
|
4884
6308
|
} else {
|
|
4885
6309
|
console.error(` ${errorObj.message}`);
|
|
4886
6310
|
const apiErrorObj = error;
|
|
@@ -4906,29 +6330,23 @@ var pullCommand = (0, import_cmd_ts2.command)({
|
|
|
4906
6330
|
// apps/cli/src/main.ts
|
|
4907
6331
|
var { version: CLI_VERSION } = require_package();
|
|
4908
6332
|
function findEnvFile() {
|
|
4909
|
-
|
|
4910
|
-
const
|
|
4911
|
-
|
|
6333
|
+
const currentDir = process.cwd();
|
|
6334
|
+
const gitService = new GitService();
|
|
6335
|
+
const gitRoot = gitService.getGitRepositoryRootSync(currentDir);
|
|
6336
|
+
const filesystemRoot = path7.parse(currentDir).root;
|
|
6337
|
+
const stopDir = gitRoot ?? filesystemRoot;
|
|
4912
6338
|
let searchDir = currentDir;
|
|
4913
|
-
|
|
4914
|
-
|
|
4915
|
-
|
|
4916
|
-
|
|
4917
|
-
}
|
|
4918
|
-
searchDir = path4.dirname(searchDir);
|
|
4919
|
-
}
|
|
4920
|
-
while (currentDir !== path4.parse(currentDir).root) {
|
|
4921
|
-
const envPath2 = path4.join(currentDir, ".env");
|
|
4922
|
-
if (fs4.existsSync(envPath2)) {
|
|
6339
|
+
let parentDir = path7.dirname(searchDir);
|
|
6340
|
+
while (searchDir !== parentDir) {
|
|
6341
|
+
const envPath2 = path7.join(searchDir, ".env");
|
|
6342
|
+
if (fs6.existsSync(envPath2)) {
|
|
4923
6343
|
return envPath2;
|
|
4924
6344
|
}
|
|
4925
|
-
if (
|
|
4926
|
-
|
|
4927
|
-
}
|
|
4928
|
-
if (!gitRootFound && currentDir !== startDir) {
|
|
4929
|
-
break;
|
|
6345
|
+
if (searchDir === stopDir) {
|
|
6346
|
+
return null;
|
|
4930
6347
|
}
|
|
4931
|
-
|
|
6348
|
+
searchDir = parentDir;
|
|
6349
|
+
parentDir = path7.dirname(searchDir);
|
|
4932
6350
|
}
|
|
4933
6351
|
return null;
|
|
4934
6352
|
}
|
|
@@ -4953,10 +6371,11 @@ var app = (0, import_cmd_ts3.subcommands)({
|
|
|
4953
6371
|
description: "Packmind CLI tool",
|
|
4954
6372
|
cmds: {
|
|
4955
6373
|
lint: lintCommand,
|
|
4956
|
-
pull: pullCommand
|
|
6374
|
+
pull: pullCommand,
|
|
6375
|
+
install: pullCommand
|
|
4957
6376
|
}
|
|
4958
6377
|
});
|
|
4959
6378
|
(0, import_cmd_ts3.run)(app, args).catch((error) => {
|
|
4960
|
-
|
|
6379
|
+
logErrorConsole(error.message);
|
|
4961
6380
|
process.exit(1);
|
|
4962
6381
|
});
|