@lousy-agents/cli 2.10.1 → 3.0.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/dist/index.js CHANGED
@@ -32630,6 +32630,15 @@ const RulesetSchema = schemas_object({
32630
32630
  enforcement: schemas_string(),
32631
32631
  rules: schemas_array(RulesetRuleSchema).optional()
32632
32632
  });
32633
+ const RepoSecuritySchema = schemas_object({
32634
+ // biome-ignore lint/style/useNamingConvention: GitHub API schema requires snake_case
32635
+ security_and_analysis: schemas_object({
32636
+ // biome-ignore lint/style/useNamingConvention: GitHub API schema requires snake_case
32637
+ advanced_security: schemas_object({
32638
+ status: schemas_string()
32639
+ })
32640
+ }).optional()
32641
+ });
32633
32642
  /**
32634
32643
  * Parses a GitHub remote URL to extract owner and repo name
32635
32644
  */ function parseRepoFromRemoteUrl(remoteUrl) {
@@ -32704,6 +32713,24 @@ function defaultExec(command, args, options) {
32704
32713
  return null;
32705
32714
  }
32706
32715
  }
32716
+ async hasAdvancedSecurity(owner, repo) {
32717
+ if (!this.octokit) {
32718
+ return false;
32719
+ }
32720
+ try {
32721
+ const { data } = await this.octokit.rest.repos.get({
32722
+ owner,
32723
+ repo
32724
+ });
32725
+ const parsed = RepoSecuritySchema.safeParse(data);
32726
+ if (!parsed.success) {
32727
+ return false;
32728
+ }
32729
+ return parsed.data.security_and_analysis?.advanced_security?.status === "enabled";
32730
+ } catch {
32731
+ return false;
32732
+ }
32733
+ }
32707
32734
  async listRulesets(owner, repo) {
32708
32735
  if (!this.octokit) {
32709
32736
  throw new Error("Not authenticated");
@@ -33671,8 +33698,44 @@ function isCopilotCodeScanningRule(rule) {
33671
33698
  return findCopilotRuleset(rulesets) !== undefined;
33672
33699
  }
33673
33700
  /**
33674
- * Builds a ruleset payload for enabling Copilot code review
33675
- */ function buildCopilotReviewRulesetPayload() {
33701
+ * Builds a ruleset payload for enabling Copilot code review.
33702
+ * Includes code_scanning rules configured with CodeQL and Copilot Autofix when GitHub Advanced Security is enabled.
33703
+ */ function buildCopilotReviewRulesetPayload(options) {
33704
+ const rules = [
33705
+ {
33706
+ type: "copilot_code_review",
33707
+ parameters: {
33708
+ // biome-ignore lint/style/useNamingConvention: GitHub API schema requires snake_case
33709
+ review_on_push: true,
33710
+ // biome-ignore lint/style/useNamingConvention: GitHub API schema requires snake_case
33711
+ review_draft_pull_requests: true
33712
+ }
33713
+ }
33714
+ ];
33715
+ if (options.advancedSecurityEnabled) {
33716
+ rules.push({
33717
+ type: "code_scanning",
33718
+ parameters: {
33719
+ // biome-ignore lint/style/useNamingConvention: GitHub API schema requires snake_case
33720
+ code_scanning_tools: [
33721
+ {
33722
+ tool: "CodeQL",
33723
+ // biome-ignore lint/style/useNamingConvention: GitHub API schema requires snake_case
33724
+ security_alerts_threshold: "high_or_higher",
33725
+ // biome-ignore lint/style/useNamingConvention: GitHub API schema requires snake_case
33726
+ alerts_threshold: "errors"
33727
+ },
33728
+ {
33729
+ tool: "Copilot Autofix",
33730
+ // biome-ignore lint/style/useNamingConvention: GitHub API schema requires snake_case
33731
+ security_alerts_threshold: "high_or_higher",
33732
+ // biome-ignore lint/style/useNamingConvention: GitHub API schema requires snake_case
33733
+ alerts_threshold: "errors"
33734
+ }
33735
+ ]
33736
+ }
33737
+ });
33738
+ }
33676
33739
  return {
33677
33740
  name: "Copilot Code Review",
33678
33741
  enforcement: "active",
@@ -33688,32 +33751,7 @@ function isCopilotCodeScanningRule(rule) {
33688
33751
  exclude: []
33689
33752
  }
33690
33753
  },
33691
- rules: [
33692
- {
33693
- type: "copilot_code_review",
33694
- parameters: {
33695
- // biome-ignore lint/style/useNamingConvention: GitHub API schema requires snake_case
33696
- review_on_push: true,
33697
- // biome-ignore lint/style/useNamingConvention: GitHub API schema requires snake_case
33698
- review_draft_pull_requests: true
33699
- }
33700
- },
33701
- {
33702
- type: "code_scanning",
33703
- parameters: {
33704
- // biome-ignore lint/style/useNamingConvention: GitHub API schema requires snake_case
33705
- code_scanning_tools: [
33706
- {
33707
- tool: "Copilot Autofix",
33708
- // biome-ignore lint/style/useNamingConvention: GitHub API schema requires snake_case
33709
- security_alerts_threshold: "high_or_higher",
33710
- // biome-ignore lint/style/useNamingConvention: GitHub API schema requires snake_case
33711
- alerts_threshold: "errors"
33712
- }
33713
- ]
33714
- }
33715
- }
33716
- ]
33754
+ rules
33717
33755
  };
33718
33756
  }
33719
33757
  /**
@@ -34683,7 +34721,10 @@ async function checkAndPromptRuleset(rulesetGateway, targetDir, prompt) {
34683
34721
  return;
34684
34722
  }
34685
34723
  try {
34686
- const payload = buildCopilotReviewRulesetPayload();
34724
+ const advancedSecurityEnabled = await rulesetGateway.hasAdvancedSecurity(repoInfo.owner, repoInfo.repo);
34725
+ const payload = buildCopilotReviewRulesetPayload({
34726
+ advancedSecurityEnabled
34727
+ });
34687
34728
  await rulesetGateway.createRuleset(repoInfo.owner, repoInfo.repo, payload);
34688
34729
  consola.success(`Created Copilot PR review ruleset: "${payload.name}"`);
34689
34730
  } catch (error) {
@@ -55118,6 +55159,102 @@ const remark = unified().use(remarkParse).use(remarkStringify).freeze()
55118
55159
  return new RemarkMarkdownAstGateway();
55119
55160
  }
55120
55161
 
55162
+ ;// CONCATENATED MODULE: ./src/entities/lint-rules.ts
55163
+ /**
55164
+ * Lint rule registry entity.
55165
+ * Defines all known lint rule IDs with their default severities, organized by target.
55166
+ */ /** Valid severity values for rule configuration */ /** Default severity levels for all known lint rules */ const DEFAULT_LINT_RULES = {
55167
+ agents: {
55168
+ "agent/missing-frontmatter": "error",
55169
+ "agent/invalid-frontmatter": "error",
55170
+ "agent/missing-name": "error",
55171
+ "agent/invalid-name-format": "error",
55172
+ "agent/name-mismatch": "error",
55173
+ "agent/missing-description": "error",
55174
+ "agent/invalid-description": "error",
55175
+ "agent/invalid-field": "warn"
55176
+ },
55177
+ instructions: {
55178
+ "instruction/parse-error": "warn",
55179
+ "instruction/command-not-in-code-block": "warn",
55180
+ "instruction/command-outside-section": "warn",
55181
+ "instruction/missing-error-handling": "warn"
55182
+ },
55183
+ skills: {
55184
+ "skill/invalid-frontmatter": "error",
55185
+ "skill/missing-frontmatter": "error",
55186
+ "skill/missing-name": "error",
55187
+ "skill/invalid-name-format": "error",
55188
+ "skill/name-mismatch": "error",
55189
+ "skill/missing-description": "error",
55190
+ "skill/invalid-description": "error",
55191
+ "skill/missing-allowed-tools": "warn"
55192
+ }
55193
+ };
55194
+
55195
+ ;// CONCATENATED MODULE: ./src/lib/lint-config.ts
55196
+ /**
55197
+ * Lint configuration loader.
55198
+ * Loads lint rule severity overrides from c12 config and merges with defaults.
55199
+ */
55200
+
55201
+
55202
+ /** Zod schema for a rule config map: rule IDs validated with regex to prevent prototype pollution */ const RuleConfigMapSchema = record(schemas_string().regex(/^[a-z]+\/[a-z]+(?:-[a-z]+)*$/), schemas_enum([
55203
+ "error",
55204
+ "warn",
55205
+ "off"
55206
+ ]));
55207
+ /** Zod schema for the lint.rules section of the config */ const LintRulesConfigSchema = schemas_object({
55208
+ agents: RuleConfigMapSchema.optional(),
55209
+ instructions: RuleConfigMapSchema.optional(),
55210
+ skills: RuleConfigMapSchema.optional()
55211
+ });
55212
+ /** Zod schema for the lint section of the config */ const LintConfigSchema = schemas_object({
55213
+ lint: schemas_object({
55214
+ rules: LintRulesConfigSchema.optional()
55215
+ }).optional()
55216
+ });
55217
+ /**
55218
+ * Merges user overrides with defaults for a single target.
55219
+ * Only known rule IDs (present in defaults) are applied; unknown IDs are discarded.
55220
+ */ function mergeTargetRules(defaults, overrides) {
55221
+ if (!overrides) {
55222
+ return defaults;
55223
+ }
55224
+ const merged = {
55225
+ ...defaults
55226
+ };
55227
+ for (const [ruleId, severity] of Object.entries(overrides)){
55228
+ if (Object.hasOwn(defaults, ruleId)) {
55229
+ merged[ruleId] = severity;
55230
+ }
55231
+ }
55232
+ return merged;
55233
+ }
55234
+ /**
55235
+ * Loads lint configuration from the target directory using c12.
55236
+ * Merges user overrides with default rule severities.
55237
+ * Throws on config load failures (syntax errors, permission denied, validation errors).
55238
+ */ async function loadLintConfig(targetDir) {
55239
+ const { config } = await loadConfig({
55240
+ name: "lousy-agents",
55241
+ cwd: targetDir
55242
+ });
55243
+ if (!config) {
55244
+ return DEFAULT_LINT_RULES;
55245
+ }
55246
+ const parsed = LintConfigSchema.parse(config);
55247
+ const rules = parsed.lint?.rules;
55248
+ if (!rules) {
55249
+ return DEFAULT_LINT_RULES;
55250
+ }
55251
+ return {
55252
+ agents: mergeTargetRules(DEFAULT_LINT_RULES.agents, rules.agents),
55253
+ instructions: mergeTargetRules(DEFAULT_LINT_RULES.instructions, rules.instructions),
55254
+ skills: mergeTargetRules(DEFAULT_LINT_RULES.skills, rules.skills)
55255
+ };
55256
+ }
55257
+
55121
55258
  ;// CONCATENATED MODULE: ./src/entities/instruction-quality.ts
55122
55259
  /**
55123
55260
  * Core domain entities for instruction quality analysis.
@@ -55178,7 +55315,9 @@ const remark = unified().use(remarkParse).use(remarkStringify).freeze()
55178
55315
  commandScores: [],
55179
55316
  overallQualityScore: 0,
55180
55317
  suggestions: [
55181
- "No agent instruction files found. Supported formats: .github/copilot-instructions.md, .github/instructions/*.md, .github/agents/*.md, AGENTS.md, CLAUDE.md"
55318
+ {
55319
+ message: "No agent instruction files found. Supported formats: .github/copilot-instructions.md, .github/instructions/*.md, .github/agents/*.md, AGENTS.md, CLAUDE.md"
55320
+ }
55182
55321
  ],
55183
55322
  parsingErrors: []
55184
55323
  },
@@ -55245,7 +55384,10 @@ const remark = unified().use(remarkParse).use(remarkStringify).freeze()
55245
55384
  const suggestions = this.generateSuggestions(commandScores);
55246
55385
  if (parsingErrors.length > 0) {
55247
55386
  const skippedFiles = parsingErrors.map((pe)=>pe.filePath).join(", ");
55248
- suggestions.push(`${parsingErrors.length} file(s) could not be parsed and were skipped: ${skippedFiles}. Analysis may be incomplete.`);
55387
+ suggestions.push({
55388
+ message: `${parsingErrors.length} file(s) could not be parsed and were skipped: ${skippedFiles}. Analysis may be incomplete.`,
55389
+ ruleId: "instruction/parse-error"
55390
+ });
55249
55391
  }
55250
55392
  return {
55251
55393
  result: {
@@ -55459,22 +55601,33 @@ const remark = unified().use(remarkParse).use(remarkStringify).freeze()
55459
55601
  const lowStructural = commandScores.filter((s)=>s.structuralContext === 0 && s.bestSourceFile !== "");
55460
55602
  if (lowStructural.length > 0) {
55461
55603
  const names = lowStructural.map((s)=>s.commandName).join(", ");
55462
- suggestions.push(`Commands not under a dedicated section: ${names}. Add a heading like "## Validation" or "## Feedback Loop" above these commands.`);
55604
+ suggestions.push({
55605
+ message: `Commands not under a dedicated section: ${names}. Add a heading like "## Validation" or "## Feedback Loop" above these commands.`,
55606
+ ruleId: "instruction/command-outside-section"
55607
+ });
55463
55608
  }
55464
55609
  const lowExecution = commandScores.filter((s)=>s.executionClarity === 0 && s.bestSourceFile !== "");
55465
55610
  if (lowExecution.length > 0) {
55466
55611
  const names = lowExecution.map((s)=>s.commandName).join(", ");
55467
- suggestions.push(`Commands not in code blocks: ${names}. Document these commands in fenced code blocks for clarity.`);
55612
+ suggestions.push({
55613
+ message: `Commands not in code blocks: ${names}. Document these commands in fenced code blocks for clarity.`,
55614
+ ruleId: "instruction/command-not-in-code-block"
55615
+ });
55468
55616
  }
55469
55617
  const lowLoop = commandScores.filter((s)=>s.loopCompleteness === 0 && s.executionClarity === 1 && s.bestSourceFile !== "");
55470
55618
  if (lowLoop.length > 0) {
55471
55619
  const names = lowLoop.map((s)=>s.commandName).join(", ");
55472
- suggestions.push(`Commands missing error handling guidance: ${names}. Add instructions for what to do if the command fails.`);
55620
+ suggestions.push({
55621
+ message: `Commands missing error handling guidance: ${names}. Add instructions for what to do if the command fails.`,
55622
+ ruleId: "instruction/missing-error-handling"
55623
+ });
55473
55624
  }
55474
55625
  const notFound = commandScores.filter((s)=>s.bestSourceFile === "");
55475
55626
  if (notFound.length > 0) {
55476
55627
  const names = notFound.map((s)=>s.commandName).join(", ");
55477
- suggestions.push(`Commands not found in any instruction file: ${names}. Document these feedback loop commands in your instruction files.`);
55628
+ suggestions.push({
55629
+ message: `Commands not found in any instruction file: ${names}. Document these feedback loop commands in your instruction files.`
55630
+ });
55478
55631
  }
55479
55632
  return suggestions;
55480
55633
  }
@@ -55612,14 +55765,8 @@ const remark = unified().use(remarkParse).use(remarkStringify).freeze()
55612
55765
  }
55613
55766
 
55614
55767
  ;// CONCATENATED MODULE: ./src/use-cases/lint-skill-frontmatter.ts
55615
- /**
55616
- * Use case for linting GitHub Copilot Agent Skill frontmatter.
55617
- * Validates required and recommended fields, name format, and directory naming.
55618
- */
55619
- /**
55620
- * Zod schema for validating agent skill frontmatter.
55621
- * Based on the agentskills.io specification.
55622
- */ const AgentSkillFrontmatterSchema = schemas_object({
55768
+
55769
+ const AgentSkillFrontmatterSchema = schemas_object({
55623
55770
  name: schemas_string().min(1, "Name is required").max(64, "Name must be 64 characters or fewer").regex(/^[a-z0-9]+(?:-[a-z0-9]+)*$/, "Name must contain only lowercase letters, numbers, and hyphens. It cannot start/end with a hyphen or contain consecutive hyphens."),
55624
55771
  description: schemas_string().min(1, "Description is required").max(1024, "Description must be 1024 characters or fewer").refine((s)=>s.trim().length > 0, {
55625
55772
  message: "Description cannot be empty or whitespace-only"
@@ -55629,14 +55776,13 @@ const remark = unified().use(remarkParse).use(remarkStringify).freeze()
55629
55776
  metadata: record(schemas_string(), schemas_string()).optional(),
55630
55777
  "allowed-tools": schemas_string().optional()
55631
55778
  });
55632
- /**
55633
- * Recommended (optional) fields that produce warnings when missing.
55634
- */ const RECOMMENDED_FIELDS = [
55779
+ const RECOMMENDED_FIELDS = [
55635
55780
  "allowed-tools"
55636
55781
  ];
55637
- /**
55638
- * Use case for linting skill frontmatter across a repository.
55639
- */ class LintSkillFrontmatterUseCase {
55782
+ const RECOMMENDED_FIELD_RULE_IDS = {
55783
+ "allowed-tools": "skill/missing-allowed-tools"
55784
+ };
55785
+ class LintSkillFrontmatterUseCase {
55640
55786
  gateway;
55641
55787
  constructor(gateway){
55642
55788
  this.gateway = gateway;
@@ -55672,16 +55818,20 @@ const remark = unified().use(remarkParse).use(remarkStringify).freeze()
55672
55818
  diagnostics.push({
55673
55819
  line: 1,
55674
55820
  severity: "error",
55675
- message: errorMessage
55821
+ message: errorMessage,
55822
+ ruleId: "skill/invalid-frontmatter"
55676
55823
  });
55677
55824
  }
55678
55825
  if (!parsed) {
55679
55826
  if (diagnostics.length === 0) {
55680
- const message = hasFrontmatterDelimiters(content) ? "Invalid YAML frontmatter. The content between --- delimiters could not be parsed as valid YAML." : "Missing YAML frontmatter. Skill files must begin with --- delimited YAML frontmatter.";
55827
+ const hasDelimiters = hasFrontmatterDelimiters(content);
55828
+ const message = hasDelimiters ? "Invalid YAML frontmatter. The content between --- delimiters could not be parsed as valid YAML." : "Missing YAML frontmatter. Skill files must begin with --- delimited YAML frontmatter.";
55829
+ const ruleId = hasDelimiters ? "skill/invalid-frontmatter" : "skill/missing-frontmatter";
55681
55830
  diagnostics.push({
55682
55831
  line: 1,
55683
55832
  severity: "error",
55684
- message
55833
+ message,
55834
+ ruleId
55685
55835
  });
55686
55836
  }
55687
55837
  return {
@@ -55702,47 +55852,58 @@ const remark = unified().use(remarkParse).use(remarkStringify).freeze()
55702
55852
  }
55703
55853
  validateFrontmatter(parsed, parentDirName) {
55704
55854
  const diagnostics = [];
55705
- // Validate against Zod schema
55706
55855
  const result = AgentSkillFrontmatterSchema.safeParse(parsed.data);
55707
55856
  if (!result.success) {
55708
55857
  for (const issue of result.error.issues){
55709
55858
  const fieldName = issue.path[0]?.toString();
55710
55859
  const line = fieldName ? parsed.fieldLines.get(fieldName) ?? parsed.frontmatterStartLine : parsed.frontmatterStartLine;
55860
+ const ruleId = this.getRuleIdForField(fieldName, issue.code, parsed.data);
55711
55861
  diagnostics.push({
55712
55862
  line,
55713
55863
  severity: "error",
55714
55864
  message: issue.message,
55715
- field: fieldName
55865
+ field: fieldName,
55866
+ ruleId
55716
55867
  });
55717
55868
  }
55718
55869
  }
55719
- // Check name matches parent directory
55720
55870
  if (result.success && result.data.name !== parentDirName) {
55721
55871
  const nameLine = parsed.fieldLines.get("name") ?? parsed.frontmatterStartLine;
55722
55872
  diagnostics.push({
55723
55873
  line: nameLine,
55724
55874
  severity: "error",
55725
55875
  message: `Frontmatter name '${result.data.name}' must match parent directory name '${parentDirName}'`,
55726
- field: "name"
55876
+ field: "name",
55877
+ ruleId: "skill/name-mismatch"
55727
55878
  });
55728
55879
  }
55729
- // Check recommended fields
55730
55880
  for (const field of RECOMMENDED_FIELDS){
55731
55881
  if (parsed.data[field] === undefined) {
55732
55882
  diagnostics.push({
55733
55883
  line: parsed.frontmatterStartLine,
55734
55884
  severity: "warning",
55735
55885
  message: `Recommended field '${field}' is missing`,
55736
- field
55886
+ field,
55887
+ ruleId: RECOMMENDED_FIELD_RULE_IDS[field]
55737
55888
  });
55738
55889
  }
55739
55890
  }
55740
55891
  return diagnostics;
55741
55892
  }
55893
+ getRuleIdForField(fieldName, issueCode, inputData) {
55894
+ // Check the actual input data for field presence rather than
55895
+ // relying on Zod message text which can change across versions.
55896
+ const isMissing = issueCode === "invalid_type" && (fieldName === undefined || !Object.hasOwn(inputData, fieldName));
55897
+ if (fieldName === "name") {
55898
+ return isMissing ? "skill/missing-name" : "skill/invalid-name-format";
55899
+ }
55900
+ if (fieldName === "description") {
55901
+ return isMissing ? "skill/missing-description" : "skill/invalid-description";
55902
+ }
55903
+ return "skill/invalid-frontmatter";
55904
+ }
55742
55905
  }
55743
- /**
55744
- * Checks whether content has opening and closing --- frontmatter delimiters.
55745
- */ function hasFrontmatterDelimiters(content) {
55906
+ function hasFrontmatterDelimiters(content) {
55746
55907
  const lines = content.split("\n");
55747
55908
  if (lines[0]?.trim() !== "---") {
55748
55909
  return false;
@@ -55772,6 +55933,7 @@ const remark = unified().use(remarkParse).use(remarkStringify).freeze()
55772
55933
 
55773
55934
 
55774
55935
 
55936
+
55775
55937
  /** Schema for validating target directory */ const TargetDirSchema = schemas_string().min(1, "Target directory is required");
55776
55938
  /**
55777
55939
  * Validates the target directory.
@@ -55796,6 +55958,7 @@ const remark = unified().use(remarkParse).use(remarkStringify).freeze()
55796
55958
  severity: d.severity,
55797
55959
  message: d.message,
55798
55960
  field: d.field,
55961
+ ruleId: d.ruleId,
55799
55962
  target: "skill"
55800
55963
  });
55801
55964
  }
@@ -55932,8 +56095,80 @@ const remark = unified().use(remarkParse).use(remarkStringify).freeze()
55932
56095
  consola.info(`Overall instruction quality score: ${result.overallQualityScore}%`);
55933
56096
  }
55934
56097
  for (const suggestion of result.suggestions){
55935
- consola.warn(suggestion);
56098
+ consola.warn(suggestion.message);
56099
+ }
56100
+ }
56101
+ /** Maps a lint target to its config key */ const TARGET_TO_CONFIG_KEY = {
56102
+ skill: "skills",
56103
+ agent: "agents",
56104
+ instruction: "instructions"
56105
+ };
56106
+ /**
56107
+ * Maps config-facing severity to diagnostic-facing severity.
56108
+ * "warn" → "warning", "error" → "error", "off" → null (drop).
56109
+ */ function lint_mapSeverity(configSeverity) {
56110
+ if (configSeverity === "off") {
56111
+ return null;
56112
+ }
56113
+ if (configSeverity === "warn") {
56114
+ return "warning";
55936
56115
  }
56116
+ return configSeverity;
56117
+ }
56118
+ /**
56119
+ * Filters instruction suggestions based on rule severity configuration.
56120
+ * Drops suggestions whose corresponding rule is "off".
56121
+ * Suggestions without a ruleId pass through unchanged.
56122
+ */ function filterInstructionSuggestions(suggestions, rules) {
56123
+ return suggestions.filter((suggestion)=>{
56124
+ if (!suggestion.ruleId) {
56125
+ return true;
56126
+ }
56127
+ return rules[suggestion.ruleId] !== "off";
56128
+ });
56129
+ }
56130
+ /**
56131
+ * Applies severity filtering to a LintOutput based on rule configuration.
56132
+ * Drops diagnostics for "off" rules, remaps severity for "warn"/"error" rules.
56133
+ * Diagnostics without a ruleId pass through unchanged.
56134
+ * For instruction targets, also filters qualityResult.suggestions.
56135
+ */ function applySeverityFilter(output, rulesConfig) {
56136
+ const configKey = TARGET_TO_CONFIG_KEY[output.target];
56137
+ const targetRules = rulesConfig[configKey];
56138
+ const filteredDiagnostics = [];
56139
+ for (const diagnostic of output.diagnostics){
56140
+ const configuredSeverity = diagnostic.ruleId ? targetRules[diagnostic.ruleId] : undefined;
56141
+ if (!configuredSeverity) {
56142
+ filteredDiagnostics.push(diagnostic);
56143
+ continue;
56144
+ }
56145
+ const mappedSeverity = lint_mapSeverity(configuredSeverity);
56146
+ if (mappedSeverity === null) {
56147
+ continue;
56148
+ }
56149
+ filteredDiagnostics.push({
56150
+ ...diagnostic,
56151
+ severity: mappedSeverity
56152
+ });
56153
+ }
56154
+ const totalErrors = filteredDiagnostics.filter((d)=>d.severity === "error").length;
56155
+ const totalWarnings = filteredDiagnostics.filter((d)=>d.severity === "warning").length;
56156
+ const totalInfos = filteredDiagnostics.filter((d)=>d.severity === "info").length;
56157
+ const filteredQualityResult = output.qualityResult && configKey === "instructions" ? {
56158
+ ...output.qualityResult,
56159
+ suggestions: filterInstructionSuggestions(output.qualityResult.suggestions, targetRules)
56160
+ } : output.qualityResult;
56161
+ return {
56162
+ ...output,
56163
+ diagnostics: filteredDiagnostics,
56164
+ qualityResult: filteredQualityResult,
56165
+ summary: {
56166
+ ...output.summary,
56167
+ totalErrors,
56168
+ totalWarnings,
56169
+ totalInfos
56170
+ }
56171
+ };
55937
56172
  }
55938
56173
  /**
55939
56174
  * The `lint` command for validating agent skills, custom agents, and instruction files.
@@ -55967,6 +56202,15 @@ const remark = unified().use(remarkParse).use(remarkStringify).freeze()
55967
56202
  run: async (context)=>{
55968
56203
  const rawTargetDir = typeof context.data?.targetDir === "string" ? context.data.targetDir : process.cwd();
55969
56204
  const targetDir = validateTargetDir(rawTargetDir);
56205
+ let rulesConfig;
56206
+ try {
56207
+ rulesConfig = await loadLintConfig(targetDir);
56208
+ } catch (error) {
56209
+ const message = error instanceof Error ? error.message : String(error);
56210
+ consola.error(`Failed to load lint configuration: ${message}`);
56211
+ process.exitCode = 1;
56212
+ return;
56213
+ }
55970
56214
  const lintSkillsFlag = context.args?.skills === true || context.data?.skills === true;
55971
56215
  const lintAgentsFlag = context.args?.agents === true || context.data?.agents === true;
55972
56216
  const lintInstructionsFlag = context.args?.instructions === true || context.data?.instructions === true;
@@ -55981,19 +56225,22 @@ const remark = unified().use(remarkParse).use(remarkStringify).freeze()
55981
56225
  let totalWarnings = 0;
55982
56226
  const allOutputs = [];
55983
56227
  if (noFlagProvided || lintSkillsFlag) {
55984
- const skillOutput = await lintSkills(targetDir);
56228
+ const rawOutput = await lintSkills(targetDir);
56229
+ const skillOutput = applySeverityFilter(rawOutput, rulesConfig);
55985
56230
  allOutputs.push(skillOutput);
55986
56231
  totalErrors += skillOutput.summary.totalErrors;
55987
56232
  totalWarnings += skillOutput.summary.totalWarnings;
55988
56233
  }
55989
56234
  if (noFlagProvided || lintAgentsFlag) {
55990
- const agentOutput = await lintAgents(targetDir);
56235
+ const rawOutput = await lintAgents(targetDir);
56236
+ const agentOutput = applySeverityFilter(rawOutput, rulesConfig);
55991
56237
  allOutputs.push(agentOutput);
55992
56238
  totalErrors += agentOutput.summary.totalErrors;
55993
56239
  totalWarnings += agentOutput.summary.totalWarnings;
55994
56240
  }
55995
56241
  if (noFlagProvided || lintInstructionsFlag) {
55996
- const instructionOutput = await lintInstructions(targetDir);
56242
+ const rawOutput = await lintInstructions(targetDir);
56243
+ const instructionOutput = applySeverityFilter(rawOutput, rulesConfig);
55997
56244
  allOutputs.push(instructionOutput);
55998
56245
  totalErrors += instructionOutput.summary.totalErrors;
55999
56246
  totalWarnings += instructionOutput.summary.totalWarnings;