@aicqtools/guardrail 1.0.0-alpha.5 → 1.0.0-alpha.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. package/dist/index.d.ts +4 -2
  2. package/dist/index.d.ts.map +1 -1
  3. package/dist/index.js +2 -1
  4. package/dist/index.js.map +1 -1
  5. package/dist/matcher/yaml-rule.d.ts +5 -2
  6. package/dist/matcher/yaml-rule.d.ts.map +1 -1
  7. package/dist/matcher/yaml-rule.js +7 -5
  8. package/dist/matcher/yaml-rule.js.map +1 -1
  9. package/dist/rules-default/no-direct-anthropic.yaml +6 -0
  10. package/dist/rules-default/no-direct-openai.yaml +8 -0
  11. package/dist/rules-default/no-magic-number.d.ts.map +1 -1
  12. package/dist/rules-default/no-magic-number.js +109 -9
  13. package/dist/rules-default/no-magic-number.js.map +1 -1
  14. package/dist/rules-default/no-process-env-leak.d.ts.map +1 -1
  15. package/dist/rules-default/no-process-env-leak.js +5 -2
  16. package/dist/rules-default/no-process-env-leak.js.map +1 -1
  17. package/dist/runner/apply-rule-config.d.ts +26 -0
  18. package/dist/runner/apply-rule-config.d.ts.map +1 -0
  19. package/dist/runner/apply-rule-config.js +52 -0
  20. package/dist/runner/apply-rule-config.js.map +1 -0
  21. package/dist/runner/index.d.ts +3 -1
  22. package/dist/runner/index.d.ts.map +1 -1
  23. package/dist/runner/index.js +2 -1
  24. package/dist/runner/index.js.map +1 -1
  25. package/dist/runner/run-file.d.ts.map +1 -1
  26. package/dist/runner/run-file.js +13 -1
  27. package/dist/runner/run-file.js.map +1 -1
  28. package/dist/runner/run-project.d.ts +12 -0
  29. package/dist/runner/run-project.d.ts.map +1 -1
  30. package/dist/runner/run-project.js +40 -2
  31. package/dist/runner/run-project.js.map +1 -1
  32. package/dist/runner/run-rule.d.ts.map +1 -1
  33. package/dist/runner/run-rule.js +17 -0
  34. package/dist/runner/run-rule.js.map +1 -1
  35. package/dist/runner/suppressions.d.ts +37 -0
  36. package/dist/runner/suppressions.d.ts.map +1 -0
  37. package/dist/runner/suppressions.js +127 -0
  38. package/dist/runner/suppressions.js.map +1 -0
  39. package/dist/suggest/analyze.d.ts +10 -0
  40. package/dist/suggest/analyze.d.ts.map +1 -0
  41. package/dist/suggest/analyze.js +193 -0
  42. package/dist/suggest/analyze.js.map +1 -0
  43. package/dist/suggest/format.d.ts +13 -0
  44. package/dist/suggest/format.d.ts.map +1 -0
  45. package/dist/suggest/format.js +120 -0
  46. package/dist/suggest/format.js.map +1 -0
  47. package/dist/suggest/index.d.ts +5 -0
  48. package/dist/suggest/index.d.ts.map +1 -0
  49. package/dist/suggest/index.js +4 -0
  50. package/dist/suggest/index.js.map +1 -0
  51. package/dist/suggest/mine.d.ts +11 -0
  52. package/dist/suggest/mine.d.ts.map +1 -0
  53. package/dist/suggest/mine.js +207 -0
  54. package/dist/suggest/mine.js.map +1 -0
  55. package/dist/suggest/types.d.ts +74 -0
  56. package/dist/suggest/types.d.ts.map +1 -0
  57. package/dist/suggest/types.js +2 -0
  58. package/dist/suggest/types.js.map +1 -0
  59. package/package.json +5 -3
@@ -1,4 +1,5 @@
1
1
  import Parser from 'tree-sitter';
2
+ import micromatch from 'micromatch';
2
3
  import { traverse } from '../matcher/traverse.js';
3
4
  import { makeRuleContext } from './context.js';
4
5
  import { loadLanguage } from '@aicqtools/core';
@@ -6,6 +7,20 @@ function ruleAppliesTo(rule, language) {
6
7
  const langs = Array.isArray(rule.language) ? rule.language : [rule.language];
7
8
  return langs.includes(language);
8
9
  }
10
+ /**
11
+ * Honor `rule.pathExclude` — a list of micromatch globs. Matching is performed against the
12
+ * file path as supplied (absolute by default in normal runs, fixture-relative in unit tests),
13
+ * and also against the path with backslashes normalized to forward slashes so authors can write
14
+ * `**\/llm/client.*` without worrying about Windows separators. The match is "any glob" — one
15
+ * match is enough to skip the rule for the file.
16
+ */
17
+ function isPathExcluded(rule, filePath) {
18
+ if (!rule.pathExclude || rule.pathExclude.length === 0)
19
+ return false;
20
+ const globs = [...rule.pathExclude];
21
+ const normalized = filePath.replace(/\\/g, '/');
22
+ return micromatch.isMatch(normalized, globs, { dot: true }) || micromatch.isMatch(filePath, globs, { dot: true });
23
+ }
9
24
  function runFunctionRule(rule, run, tree) {
10
25
  const ctx = makeRuleContext(run, rule);
11
26
  traverse(tree.rootNode, (node) => {
@@ -28,6 +43,8 @@ function runPatternRule(rule, run, tree) {
28
43
  export function runRule(rule, run, tree) {
29
44
  if (!ruleAppliesTo(rule, run.language))
30
45
  return;
46
+ if (isPathExcluded(rule, run.filePath))
47
+ return;
31
48
  if (rule.kind === 'function')
32
49
  runFunctionRule(rule, run, tree);
33
50
  else
@@ -1 +1 @@
1
- {"version":3,"file":"run-rule.js","sourceRoot":"","sources":["../../src/runner/run-rule.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,aAAa,CAAC;AAGjC,OAAO,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AAClD,OAAO,EAAE,eAAe,EAAmB,MAAM,cAAc,CAAC;AAChE,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAE/C,SAAS,aAAa,CAAC,IAAU,EAAE,QAAkB;IACnD,MAAM,KAAK,GAAG,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IAC7E,OAAO,KAAK,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;AAClC,CAAC;AAED,SAAS,eAAe,CAAC,IAAkB,EAAE,GAAe,EAAE,IAAiB;IAC7E,MAAM,GAAG,GAAG,eAAe,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;IACvC,QAAQ,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC,IAAI,EAAE,EAAE;QAC/B,MAAM,OAAO,GAAG,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACzC,IAAI,OAAO;YAAE,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;IAClC,CAAC,CAAC,CAAC;AACL,CAAC;AAED,SAAS,cAAc,CAAC,IAAiB,EAAE,GAAe,EAAE,IAAiB;IAC3E,MAAM,IAAI,GAAG,YAAY,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IACxC,MAAM,KAAK,GAAG,IAAI,MAAM,CAAC,KAAK,CAAC,IAAI,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC;IACjD,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IAC7C,MAAM,GAAG,GAAG,eAAe,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;IACvC,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;QAC5B,MAAM,MAAM,GAAG,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC;QACvC,IAAI,MAAM;YAAE,GAAG,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC;IAC3C,CAAC;AACH,CAAC;AAED,MAAM,UAAU,OAAO,CAAC,IAAU,EAAE,GAAe,EAAE,IAAiB;IACpE,IAAI,CAAC,aAAa,CAAC,IAAI,EAAE,GAAG,CAAC,QAAQ,CAAC;QAAE,OAAO;IAC/C,IAAI,IAAI,CAAC,IAAI,KAAK,UAAU;QAAE,eAAe,CAAC,IAAI,EAAE,GAAG,EAAE,IAAI,CAAC,CAAC;;QAC1D,cAAc,CAAC,IAAI,EAAE,GAAG,EAAE,IAAI,CAAC,CAAC;AACvC,CAAC"}
1
+ {"version":3,"file":"run-rule.js","sourceRoot":"","sources":["../../src/runner/run-rule.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,37 @@
1
+ import type Parser from 'tree-sitter';
2
+ import type { Diagnostic, Language } from '@aicqtools/core';
3
+ /**
4
+ * Inline suppression directives — ESLint-style comment markers that suppress diagnostics
5
+ * for a specific line, the next line, or the entire file. Supported syntaxes:
6
+ *
7
+ * // aicq-disable-next-line <rule-id>[, <rule-id>...]
8
+ * // aicq-disable-line <rule-id>[, ...]
9
+ * // aicq-disable-file <rule-id>[, ...]
10
+ *
11
+ * Python uses `#` instead of `//`. Block comments (`/* ...\*\/`) are also recognized.
12
+ * The trailing rule-id list may be omitted — a bare directive suppresses *all* rules for that
13
+ * scope. Multiple ids may be separated by commas, whitespace, or both. Unknown ids are silently
14
+ * accepted (a project may rename rules; we don't want a stale directive to crash the run).
15
+ */
16
+ export type Scope = number | 'file';
17
+ export interface Suppressions {
18
+ /** Per-line maps: line number → set of rule ids ('*' = all). */
19
+ readonly byLine: Map<number, Set<string> | '*'>;
20
+ /** File-wide suppressions. */
21
+ readonly fileLevel: Set<string> | '*' | null;
22
+ }
23
+ /**
24
+ * Walk the tree's comment nodes and produce a Suppressions map. Never throws — if the grammar
25
+ * yields no comment nodes for a given language, the result is empty (and applySuppressions is
26
+ * a no-op). The `language` parameter is reserved for future per-language tweaks; today it's
27
+ * only used to recognize that block comments may span multiple lines.
28
+ */
29
+ export declare function parseSuppressions(tree: Parser.Tree, source: string, language: Language): Suppressions;
30
+ /**
31
+ * Filter diagnostics through the parsed suppressions. Diagnostics whose `ruleId` is suppressed
32
+ * either file-wide or on its specific line are removed. `@aicq/parse-failed` (and any other
33
+ * synthetic diagnostic emitted by the runner) is treated like any other rule and can be
34
+ * suppressed by id or with a bare directive.
35
+ */
36
+ export declare function applySuppressions(diagnostics: readonly Diagnostic[], suppressions: Suppressions): Diagnostic[];
37
+ //# sourceMappingURL=suppressions.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"suppressions.d.ts","sourceRoot":"","sources":["../../src/runner/suppressions.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,MAAM,aAAa,CAAC;AACtC,OAAO,KAAK,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAG5D;;;;;;;;;;;;GAYG;AAEH,MAAM,MAAM,KAAK,GAAG,MAAM,GAAG,MAAM,CAAC;AAEpC,MAAM,WAAW,YAAY;IAC3B,gEAAgE;IAChE,QAAQ,CAAC,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,MAAM,CAAC,GAAG,GAAG,CAAC,CAAC;IAChD,8BAA8B;IAC9B,QAAQ,CAAC,SAAS,EAAE,GAAG,CAAC,MAAM,CAAC,GAAG,GAAG,GAAG,IAAI,CAAC;CAC9C;AAMD;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAC/B,IAAI,EAAE,MAAM,CAAC,IAAI,EACjB,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,QAAQ,GACjB,YAAY,CA0Dd;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAC/B,WAAW,EAAE,SAAS,UAAU,EAAE,EAClC,YAAY,EAAE,YAAY,GACzB,UAAU,EAAE,CAed"}
@@ -0,0 +1,127 @@
1
+ import { traverse } from '../matcher/traverse.js';
2
+ const COMMENT_NODE_TYPES = new Set(['comment', 'block_comment', 'line_comment']);
3
+ // Capture group 1: directive verb. Group 2: optional rule-id list.
4
+ const DIRECTIVE_RE = /aicq-disable-(next-line|line|file)\b\s*(.*)/i;
5
+ /**
6
+ * Walk the tree's comment nodes and produce a Suppressions map. Never throws — if the grammar
7
+ * yields no comment nodes for a given language, the result is empty (and applySuppressions is
8
+ * a no-op). The `language` parameter is reserved for future per-language tweaks; today it's
9
+ * only used to recognize that block comments may span multiple lines.
10
+ */
11
+ export function parseSuppressions(tree, source, language) {
12
+ void language; // currently uniform across supported languages
13
+ const byLine = new Map();
14
+ let fileLevel = null;
15
+ const mergeAtLine = (line, ids) => {
16
+ const existing = byLine.get(line);
17
+ if (existing === undefined) {
18
+ byLine.set(line, ids === '*' ? '*' : new Set(ids));
19
+ return;
20
+ }
21
+ if (existing === '*' || ids === '*') {
22
+ byLine.set(line, '*');
23
+ return;
24
+ }
25
+ for (const id of ids)
26
+ existing.add(id);
27
+ };
28
+ const mergeFile = (ids) => {
29
+ if (fileLevel === '*' || ids === '*') {
30
+ fileLevel = '*';
31
+ return;
32
+ }
33
+ if (fileLevel === null) {
34
+ fileLevel = new Set(ids);
35
+ return;
36
+ }
37
+ for (const id of ids)
38
+ fileLevel.add(id);
39
+ };
40
+ traverse(tree.rootNode, (node) => {
41
+ if (!COMMENT_NODE_TYPES.has(node.type))
42
+ return;
43
+ const raw = source.slice(node.startIndex, node.endIndex);
44
+ // Strip comment delimiters before regex match so the body is what we scan.
45
+ const body = stripCommentDelimiters(raw);
46
+ const m = DIRECTIVE_RE.exec(body);
47
+ if (!m)
48
+ return;
49
+ const verb = m[1]?.toLowerCase();
50
+ if (!verb)
51
+ return;
52
+ const ids = parseRuleIds(m[2] ?? '');
53
+ if (verb === 'file') {
54
+ mergeFile(ids);
55
+ return;
56
+ }
57
+ // Lines are 1-based to match Diagnostic.range.start.line
58
+ const startLine = node.startPosition.row + 1;
59
+ const endLine = node.endPosition.row + 1;
60
+ if (verb === 'next-line') {
61
+ mergeAtLine(endLine + 1, ids);
62
+ }
63
+ else {
64
+ // 'line' — for a leading standalone comment, apply to the comment's own line(s).
65
+ // For a trailing comment (e.g. `code; // aicq-disable-line`), the comment is on the
66
+ // same line as the code, so this also applies correctly.
67
+ for (let l = startLine; l <= endLine; l++)
68
+ mergeAtLine(l, ids);
69
+ }
70
+ });
71
+ return { byLine, fileLevel };
72
+ }
73
+ /**
74
+ * Filter diagnostics through the parsed suppressions. Diagnostics whose `ruleId` is suppressed
75
+ * either file-wide or on its specific line are removed. `@aicq/parse-failed` (and any other
76
+ * synthetic diagnostic emitted by the runner) is treated like any other rule and can be
77
+ * suppressed by id or with a bare directive.
78
+ */
79
+ export function applySuppressions(diagnostics, suppressions) {
80
+ if (suppressions.fileLevel === '*')
81
+ return [];
82
+ const fileSet = suppressions.fileLevel instanceof Set ? suppressions.fileLevel : null;
83
+ const hasAnyLine = suppressions.byLine.size > 0;
84
+ if (!fileSet && !hasAnyLine)
85
+ return [...diagnostics];
86
+ const out = [];
87
+ for (const d of diagnostics) {
88
+ if (fileSet && fileSet.has(d.ruleId))
89
+ continue;
90
+ const lineMap = suppressions.byLine.get(d.range.start.line);
91
+ if (lineMap === '*')
92
+ continue;
93
+ if (lineMap && lineMap.has(d.ruleId))
94
+ continue;
95
+ out.push(d);
96
+ }
97
+ return out;
98
+ }
99
+ function stripCommentDelimiters(raw) {
100
+ // `//foo` → `foo`, `# foo` → `foo`, `/* foo */` → `foo`, `<!-- foo -->` (just in case) → `foo`
101
+ return raw
102
+ .replace(/^\s*\/\*/, '')
103
+ .replace(/\*\/\s*$/, '')
104
+ .replace(/^\s*\/\//, '')
105
+ .replace(/^\s*#/, '')
106
+ .replace(/^\s*<!--/, '')
107
+ .replace(/-->\s*$/, '')
108
+ .trim();
109
+ }
110
+ /**
111
+ * Split the directive tail into a set of rule ids. Empty tail → '*' (bare directive).
112
+ * Accepts commas, whitespace, or both as separators; ignores empty tokens; lowercases nothing
113
+ * (rule ids are case-sensitive). Returns `'*'` when the tail is empty.
114
+ */
115
+ function parseRuleIds(tail) {
116
+ const trimmed = tail.trim();
117
+ if (trimmed.length === 0)
118
+ return '*';
119
+ const tokens = trimmed
120
+ .split(/[,\s]+/)
121
+ .map((s) => s.trim())
122
+ .filter((s) => s.length > 0);
123
+ if (tokens.length === 0)
124
+ return '*';
125
+ return new Set(tokens);
126
+ }
127
+ //# sourceMappingURL=suppressions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"suppressions.js","sourceRoot":"","sources":["../../src/runner/suppressions.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ import type { AnalyzeRepoOptions, RuleSuggestionReport } from './types.js';
2
+ /**
3
+ * Approach A — built-in rule recommender. Scans the repo with all supplied rules,
4
+ * ranks them by how many violations they would flag, and emits a paste-ready
5
+ * `aicq.config.yaml` enable-snippet. Also reads `package.json` / `requirements.txt`
6
+ * (no AST) and flags built-in rules whose id/docs/message mentions a declared
7
+ * dependency ("stack match").
8
+ */
9
+ export declare function analyzeRepo(opts: AnalyzeRepoOptions): Promise<RuleSuggestionReport>;
10
+ //# sourceMappingURL=analyze.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"analyze.d.ts","sourceRoot":"","sources":["../../src/suggest/analyze.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EACV,kBAAkB,EAGlB,oBAAoB,EAErB,MAAM,YAAY,CAAC;AAiBpB;;;;;;GAMG;AACH,wBAAsB,WAAW,CAAC,IAAI,EAAE,kBAAkB,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAmFzF"}
@@ -0,0 +1,193 @@
1
+ import { existsSync } from 'node:fs';
2
+ import { readFile } from 'node:fs/promises';
3
+ import { resolve } from 'node:path';
4
+ import fastGlob from 'fast-glob';
5
+ import { detectLanguage } from '@aicqtools/core';
6
+ import { resolveIgnores, runProject } from '../runner/run-project.js';
7
+ import { buildConfigSnippet } from './format.js';
8
+ const PARSE_FAILED_ID = '@aicq/parse-failed';
9
+ const DEFAULT_TOP = 10;
10
+ const DEFAULT_MIN_HITS = 1;
11
+ const MAX_SAMPLE_LOCATIONS = 2;
12
+ const MIN_DEP_NAME_LENGTH = 4;
13
+ /** When a rule has > NOISY_RATIO times the hits of the next-ranked rule, mark it noisy. */
14
+ const NOISY_RATIO = 10;
15
+ /** Absolute floor: any info-severity rule that fires more than this is treated as noisy regardless of gap. */
16
+ const NOISY_ABS_THRESHOLD = 200;
17
+ /**
18
+ * Approach A — built-in rule recommender. Scans the repo with all supplied rules,
19
+ * ranks them by how many violations they would flag, and emits a paste-ready
20
+ * `aicq.config.yaml` enable-snippet. Also reads `package.json` / `requirements.txt`
21
+ * (no AST) and flags built-in rules whose id/docs/message mentions a declared
22
+ * dependency ("stack match").
23
+ */
24
+ export async function analyzeRepo(opts) {
25
+ const top = Math.max(1, opts.top ?? DEFAULT_TOP);
26
+ const minHits = Math.max(1, opts.minHits ?? DEFAULT_MIN_HITS);
27
+ const result = await runProject({
28
+ cwd: opts.cwd,
29
+ include: opts.include,
30
+ exclude: opts.exclude,
31
+ rules: opts.rules,
32
+ ...(opts.cache ? { cache: opts.cache } : {}),
33
+ ...(opts.respectGitignore ? { respectGitignore: true } : {}),
34
+ });
35
+ const byRule = new Map();
36
+ for (const d of result.diagnostics) {
37
+ if (d.ruleId === PARSE_FAILED_ID)
38
+ continue;
39
+ let bucket = byRule.get(d.ruleId);
40
+ if (!bucket) {
41
+ bucket = { hits: 0, samples: [] };
42
+ byRule.set(d.ruleId, bucket);
43
+ }
44
+ bucket.hits += 1;
45
+ if (bucket.samples.length < MAX_SAMPLE_LOCATIONS) {
46
+ bucket.samples.push({ file: d.file, line: d.range.start.line, column: d.range.start.column });
47
+ }
48
+ }
49
+ const ruleById = new Map();
50
+ for (const rule of opts.rules)
51
+ ruleById.set(rule.id, rule);
52
+ const detectedDependencies = await detectDependencies(opts.cwd);
53
+ const depNames = new Set(detectedDependencies
54
+ .map((d) => bareDepName(d.name).toLowerCase())
55
+ .filter((n) => n.length >= MIN_DEP_NAME_LENGTH));
56
+ const stackMatched = new Set();
57
+ for (const rule of opts.rules) {
58
+ if (ruleMentionsDependency(rule, depNames))
59
+ stackMatched.add(rule.id);
60
+ }
61
+ const suggestions = [];
62
+ for (const [ruleId, bucket] of byRule) {
63
+ const rule = ruleById.get(ruleId);
64
+ if (!rule)
65
+ continue;
66
+ if (bucket.hits < minHits && !stackMatched.has(ruleId))
67
+ continue;
68
+ suggestions.push(makeSuggestion(rule, bucket.hits, bucket.samples, stackMatched.has(ruleId)));
69
+ }
70
+ for (const ruleId of stackMatched) {
71
+ if (byRule.has(ruleId))
72
+ continue;
73
+ const rule = ruleById.get(ruleId);
74
+ if (rule)
75
+ suggestions.push(makeSuggestion(rule, 0, [], true));
76
+ }
77
+ suggestions.sort((a, b) => b.hits - a.hits || a.ruleId.localeCompare(b.ruleId));
78
+ // Mark "noisy" suggestions: those whose hit count is dramatically larger than the next
79
+ // rule's (gap criterion) OR any info-severity rule above the absolute threshold. This
80
+ // information drives both the text reporter's flag and the YAML snippet's comment-out
81
+ // behavior, so we compute it once here.
82
+ const capped = applyNoisyFlag(suggestions.slice(0, top));
83
+ const ignore = await resolveIgnores(opts.cwd, opts.exclude, opts.respectGitignore ?? false);
84
+ const files = await fastGlob([...opts.include], {
85
+ cwd: opts.cwd,
86
+ ignore,
87
+ absolute: true,
88
+ onlyFiles: true,
89
+ dot: false,
90
+ });
91
+ const langSet = new Set();
92
+ for (const file of files) {
93
+ const lang = detectLanguage(file);
94
+ if (lang)
95
+ langSet.add(lang);
96
+ }
97
+ const languagesPresent = [...langSet].sort();
98
+ return {
99
+ filesScanned: result.filesScanned,
100
+ languagesPresent,
101
+ durationMs: result.durationMs,
102
+ suggestions: capped,
103
+ detectedDependencies,
104
+ configSnippet: buildConfigSnippet(capped),
105
+ };
106
+ }
107
+ function makeSuggestion(rule, hits, samples, stackMatch) {
108
+ return {
109
+ ruleId: rule.id,
110
+ hits,
111
+ severity: rule.severity,
112
+ message: rule.message,
113
+ ...(rule.messageKo ? { messageKo: rule.messageKo } : {}),
114
+ ...(rule.docs ? { docs: rule.docs } : {}),
115
+ sampleLocations: samples,
116
+ ...(stackMatch ? { stackMatch: true } : {}),
117
+ };
118
+ }
119
+ function applyNoisyFlag(suggestions) {
120
+ const out = suggestions.map((s) => ({ ...s }));
121
+ for (let i = 0; i < out.length; i++) {
122
+ const cur = out[i];
123
+ if (!cur)
124
+ continue;
125
+ const next = out[i + 1];
126
+ const gap = cur.hits > 0 && next && next.hits > 0 && cur.hits > next.hits * NOISY_RATIO;
127
+ const absInfo = cur.severity === 'info' && cur.hits > NOISY_ABS_THRESHOLD;
128
+ if (gap || absInfo)
129
+ cur.noisy = true;
130
+ }
131
+ return out;
132
+ }
133
+ function bareDepName(name) {
134
+ return name.startsWith('@') ? (name.split('/').pop() ?? name) : name;
135
+ }
136
+ function ruleMentionsDependency(rule, depNames) {
137
+ if (depNames.size === 0)
138
+ return false;
139
+ const idTokens = new Set(rule.id.toLowerCase().split(/[^a-z0-9]+/).filter(Boolean));
140
+ const firstWord = rule.message.toLowerCase().split(/\s+/)[0] ?? '';
141
+ const docs = (rule.docs ?? '').toLowerCase();
142
+ for (const dep of depNames) {
143
+ if (idTokens.has(dep) || firstWord === dep || (docs.length > 0 && docs.includes(dep)))
144
+ return true;
145
+ }
146
+ return false;
147
+ }
148
+ async function detectDependencies(cwd) {
149
+ const out = [];
150
+ const seen = new Set();
151
+ const add = (rawName, source) => {
152
+ const name = rawName.trim();
153
+ if (!name || seen.has(`${source}:${name}`))
154
+ return;
155
+ seen.add(`${source}:${name}`);
156
+ out.push({ name, source });
157
+ };
158
+ const pkgPath = resolve(cwd, 'package.json');
159
+ if (existsSync(pkgPath)) {
160
+ try {
161
+ const pkg = JSON.parse(await readFile(pkgPath, 'utf-8'));
162
+ for (const key of ['dependencies', 'devDependencies', 'peerDependencies', 'optionalDependencies']) {
163
+ const deps = pkg[key];
164
+ if (deps && typeof deps === 'object' && !Array.isArray(deps)) {
165
+ for (const name of Object.keys(deps))
166
+ add(name, 'package.json');
167
+ }
168
+ }
169
+ }
170
+ catch {
171
+ /* ignore unreadable / invalid package.json */
172
+ }
173
+ }
174
+ const reqPath = resolve(cwd, 'requirements.txt');
175
+ if (existsSync(reqPath)) {
176
+ try {
177
+ const txt = await readFile(reqPath, 'utf-8');
178
+ for (const line of txt.split(/\r?\n/)) {
179
+ const trimmed = line.trim();
180
+ if (!trimmed || trimmed.startsWith('#') || trimmed.startsWith('-'))
181
+ continue;
182
+ const name = trimmed.split(/[<>=!~;[\s]/)[0];
183
+ if (name)
184
+ add(name, 'requirements.txt');
185
+ }
186
+ }
187
+ catch {
188
+ /* ignore unreadable requirements.txt */
189
+ }
190
+ }
191
+ return out;
192
+ }
193
+ //# sourceMappingURL=analyze.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"analyze.js","sourceRoot":"","sources":["../../src/suggest/analyze.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ import type { Locale } from '@aicqtools/core';
2
+ import type { RuleSuggestion, RuleSuggestionReport } from './types.js';
3
+ /**
4
+ * A paste-ready `aicq.config.yaml` fragment that enables the suggested rules.
5
+ *
6
+ * Info-severity and `noisy`-flagged rules are emitted as **commented-out** lines with a note —
7
+ * pasting the snippet as-is never floods the project with low-signal diagnostics, but the
8
+ * reader sees that those rules exist and can uncomment them after tuning.
9
+ */
10
+ export declare function buildConfigSnippet(suggestions: readonly RuleSuggestion[]): string;
11
+ export declare function formatSuggestText(report: RuleSuggestionReport, locale: Locale): string;
12
+ export declare function formatSuggestYaml(report: RuleSuggestionReport, locale?: Locale): string;
13
+ //# sourceMappingURL=format.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"format.d.ts","sourceRoot":"","sources":["../../src/suggest/format.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAC;AAE9C,OAAO,KAAK,EAAoB,cAAc,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAIzF;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,WAAW,EAAE,SAAS,cAAc,EAAE,GAAG,MAAM,CAejF;AAcD,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,oBAAoB,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAgDtF;AAED,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,oBAAoB,EAAE,MAAM,GAAE,MAAa,GAAG,MAAM,CAY7F"}
@@ -0,0 +1,120 @@
1
+ import { stringify as stringifyYaml } from 'yaml';
2
+ import { t } from '@aicqtools/core';
3
+ const MESSAGE_WIDTH = 64;
4
+ /**
5
+ * A paste-ready `aicq.config.yaml` fragment that enables the suggested rules.
6
+ *
7
+ * Info-severity and `noisy`-flagged rules are emitted as **commented-out** lines with a note —
8
+ * pasting the snippet as-is never floods the project with low-signal diagnostics, but the
9
+ * reader sees that those rules exist and can uncomment them after tuning.
10
+ */
11
+ export function buildConfigSnippet(suggestions) {
12
+ if (suggestions.length === 0)
13
+ return '';
14
+ const lines = ['modules:', ' guardrail:', ' rules:'];
15
+ for (const s of suggestions) {
16
+ const level = s.severity === 'error' ? 'error' : 'warn';
17
+ const noteParts = [];
18
+ if (s.hits > 0)
19
+ noteParts.push(`${s.hits} hit${s.hits === 1 ? '' : 's'}`);
20
+ if (s.stackMatch)
21
+ noteParts.push('stack match');
22
+ const commentOut = s.severity === 'info' || s.noisy === true;
23
+ if (commentOut)
24
+ noteParts.push(`${s.severity} severity, likely noisy — review & tune before enabling`);
25
+ const note = noteParts.length > 0 ? ` # ${noteParts.join(', ')}` : '';
26
+ const prefix = commentOut ? '# ' : ' ';
27
+ lines.push(`${prefix}${s.ruleId}: ${level}${note}`);
28
+ }
29
+ return lines.join('\n');
30
+ }
31
+ /**
32
+ * Renders a multi-line banner suitable for placing above `buildConfigSnippet`'s output.
33
+ * Uses YAML `#` comments so the entire result is paste-safe.
34
+ */
35
+ function snippetBanner(locale) {
36
+ const date = new Date().toISOString().slice(0, 10);
37
+ return t(locale, 'cli.rules.suggest.snippetBanner', { date })
38
+ .split('\n')
39
+ .map((line) => `# ${line}`)
40
+ .join('\n');
41
+ }
42
+ export function formatSuggestText(report, locale) {
43
+ const lines = [];
44
+ lines.push(t(locale, 'cli.rules.suggest.header', {
45
+ files: report.filesScanned,
46
+ langs: report.languagesPresent.length > 0 ? report.languagesPresent.join(', ') : '-',
47
+ ms: report.durationMs,
48
+ }));
49
+ lines.push('');
50
+ if (report.suggestions.length === 0) {
51
+ lines.push(t(locale, 'cli.rules.suggest.none'));
52
+ }
53
+ else {
54
+ lines.push(t(locale, 'cli.rules.suggest.tableHeader'));
55
+ report.suggestions.forEach((s) => {
56
+ const flags = [];
57
+ if (s.stackMatch)
58
+ flags.push(t(locale, 'cli.rules.suggest.stackMatchNote'));
59
+ if (s.noisy)
60
+ flags.push(t(locale, 'cli.rules.suggest.noisyNote'));
61
+ const flagStr = flags.length > 0 ? ` ${flags.join(' ')}` : '';
62
+ lines.push(` ${s.ruleId} ${s.hits} ${s.severity} ${truncate(s.message, MESSAGE_WIDTH)}${flagStr}`);
63
+ for (const loc of s.sampleLocations)
64
+ lines.push(` ${loc.file}:${loc.line}:${loc.column}`);
65
+ });
66
+ }
67
+ if (report.detectedDependencies.length > 0) {
68
+ lines.push('');
69
+ lines.push(t(locale, 'cli.rules.suggest.detectedStack', {
70
+ deps: report.detectedDependencies.map((d) => d.name).join(', '),
71
+ }));
72
+ }
73
+ if (report.configSnippet) {
74
+ lines.push('');
75
+ lines.push(t(locale, 'cli.rules.suggest.configHint'));
76
+ lines.push(snippetBanner(locale));
77
+ lines.push(report.configSnippet);
78
+ }
79
+ if (report.patternDrafts && report.patternDrafts.length > 0) {
80
+ lines.push('');
81
+ lines.push(t(locale, 'cli.rules.suggest.patternDraftsHeader', { count: report.patternDrafts.length }));
82
+ lines.push(formatPatternDraftsYaml(report.patternDrafts, locale));
83
+ }
84
+ return lines.join('\n');
85
+ }
86
+ export function formatSuggestYaml(report, locale = 'en') {
87
+ const parts = [];
88
+ if (report.configSnippet) {
89
+ parts.push(snippetBanner(locale));
90
+ parts.push(report.configSnippet);
91
+ }
92
+ if (report.patternDrafts && report.patternDrafts.length > 0) {
93
+ if (parts.length > 0)
94
+ parts.push('');
95
+ parts.push(`# ${t(locale, 'cli.rules.suggest.patternDraftsBanner')}`);
96
+ parts.push(formatPatternDraftsYaml(report.patternDrafts, locale));
97
+ }
98
+ return parts.length > 0 ? parts.join('\n') : '# No suggestions.';
99
+ }
100
+ function formatPatternDraftsYaml(drafts, locale) {
101
+ const expHeader = t(locale, 'cli.rules.suggest.experimentalLabel');
102
+ return drafts
103
+ .map((d, i) => `# --- ${expHeader} draft ${i + 1}/${drafts.length}: ${d.id} (${d.meta.count}x across ${d.meta.files} file${d.meta.files === 1 ? '' : 's'}) ---\n${patternDraftToYaml(d)}`)
104
+ .join('---\n');
105
+ }
106
+ function patternDraftToYaml(draft) {
107
+ return stringifyYaml({
108
+ id: draft.id,
109
+ language: draft.language,
110
+ severity: draft.severity,
111
+ message: draft.message,
112
+ messageKo: draft.messageKo,
113
+ query: draft.query,
114
+ }, { lineWidth: 0 });
115
+ }
116
+ function truncate(value, max) {
117
+ const oneLine = value.replace(/\s+/g, ' ').trim();
118
+ return oneLine.length <= max ? oneLine : `${oneLine.slice(0, max - 1)}…`;
119
+ }
120
+ //# sourceMappingURL=format.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"format.js","sourceRoot":"","sources":["../../src/suggest/format.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,IAAI,aAAa,EAAE,MAAM,MAAM,CAAC;AAElD,OAAO,EAAE,CAAC,EAAE,MAAM,iBAAiB,CAAC;AAGpC,MAAM,aAAa,GAAG,EAAE,CAAC;AAEzB;;;;;;GAMG;AACH,MAAM,UAAU,kBAAkB,CAAC,WAAsC;IACvE,IAAI,WAAW,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IACxC,MAAM,KAAK,GAAG,CAAC,UAAU,EAAE,cAAc,EAAE,YAAY,CAAC,CAAC;IACzD,KAAK,MAAM,CAAC,IAAI,WAAW,EAAE,CAAC;QAC5B,MAAM,KAAK,GAAG,CAAC,CAAC,QAAQ,KAAK,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC;QACxD,MAAM,SAAS,GAAa,EAAE,CAAC;QAC/B,IAAI,CAAC,CAAC,IAAI,GAAG,CAAC;YAAE,SAAS,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,OAAO,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC;QAC1E,IAAI,CAAC,CAAC,UAAU;YAAE,SAAS,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;QAChD,MAAM,UAAU,GAAG,CAAC,CAAC,QAAQ,KAAK,MAAM,IAAI,CAAC,CAAC,KAAK,KAAK,IAAI,CAAC;QAC7D,IAAI,UAAU;YAAE,SAAS,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,QAAQ,yDAAyD,CAAC,CAAC;QACvG,MAAM,IAAI,GAAG,SAAS,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACvE,MAAM,MAAM,GAAG,UAAU,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,QAAQ,CAAC;QACjD,KAAK,CAAC,IAAI,CAAC,GAAG,MAAM,GAAG,CAAC,CAAC,MAAM,KAAK,KAAK,GAAG,IAAI,EAAE,CAAC,CAAC;IACtD,CAAC;IACD,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC1B,CAAC;AAED;;;GAGG;AACH,SAAS,aAAa,CAAC,MAAc;IACnC,MAAM,IAAI,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IACnD,OAAO,CAAC,CAAC,MAAM,EAAE,iCAAiC,EAAE,EAAE,IAAI,EAAE,CAAC;SAC1D,KAAK,CAAC,IAAI,CAAC;SACX,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,KAAK,IAAI,EAAE,CAAC;SAC1B,IAAI,CAAC,IAAI,CAAC,CAAC;AAChB,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,MAA4B,EAAE,MAAc;IAC5E,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,KAAK,CAAC,IAAI,CACR,CAAC,CAAC,MAAM,EAAE,0BAA0B,EAAE;QACpC,KAAK,EAAE,MAAM,CAAC,YAAY;QAC1B,KAAK,EAAE,MAAM,CAAC,gBAAgB,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,gBAAgB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,GAAG;QACpF,EAAE,EAAE,MAAM,CAAC,UAAU;KACtB,CAAC,CACH,CAAC;IACF,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAEf,IAAI,MAAM,CAAC,WAAW,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACpC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,EAAE,wBAAwB,CAAC,CAAC,CAAC;IAClD,CAAC;SAAM,CAAC;QACN,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,EAAE,+BAA+B,CAAC,CAAC,CAAC;QACvD,MAAM,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE;YAC/B,MAAM,KAAK,GAAa,EAAE,CAAC;YAC3B,IAAI,CAAC,CAAC,UAAU;gBAAE,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,EAAE,kCAAkC,CAAC,CAAC,CAAC;YAC5E,IAAI,CAAC,CAAC,KAAK;gBAAE,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,EAAE,6BAA6B,CAAC,CAAC,CAAC;YAClE,MAAM,OAAO,GAAG,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YAC9D,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,QAAQ,KAAK,QAAQ,CAAC,CAAC,CAAC,OAAO,EAAE,aAAa,CAAC,GAAG,OAAO,EAAE,CAAC,CAAC;YACvG,KAAK,MAAM,GAAG,IAAI,CAAC,CAAC,eAAe;gBAAE,KAAK,CAAC,IAAI,CAAC,SAAS,GAAG,CAAC,IAAI,IAAI,GAAG,CAAC,IAAI,IAAI,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC;QACjG,CAAC,CAAC,CAAC;IACL,CAAC;IAED,IAAI,MAAM,CAAC,oBAAoB,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC3C,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACf,KAAK,CAAC,IAAI,CACR,CAAC,CAAC,MAAM,EAAE,iCAAiC,EAAE;YAC3C,IAAI,EAAE,MAAM,CAAC,oBAAoB,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC;SAChE,CAAC,CACH,CAAC;IACJ,CAAC;IAED,IAAI,MAAM,CAAC,aAAa,EAAE,CAAC;QACzB,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACf,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,EAAE,8BAA8B,CAAC,CAAC,CAAC;QACtD,KAAK,CAAC,IAAI,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC,CAAC;QAClC,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC;IACnC,CAAC;IAED,IAAI,MAAM,CAAC,aAAa,IAAI,MAAM,CAAC,aAAa,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC5D,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACf,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,EAAE,uCAAuC,EAAE,EAAE,KAAK,EAAE,MAAM,CAAC,aAAa,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;QACvG,KAAK,CAAC,IAAI,CAAC,uBAAuB,CAAC,MAAM,CAAC,aAAa,EAAE,MAAM,CAAC,CAAC,CAAC;IACpE,CAAC;IAED,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC1B,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,MAA4B,EAAE,SAAiB,IAAI;IACnF,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,IAAI,MAAM,CAAC,aAAa,EAAE,CAAC;QACzB,KAAK,CAAC,IAAI,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC,CAAC;QAClC,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC;IACnC,CAAC;IACD,IAAI,MAAM,CAAC,aAAa,IAAI,MAAM,CAAC,aAAa,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC5D,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC;YAAE,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACrC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,MAAM,EAAE,uCAAuC,CAAC,EAAE,CAAC,CAAC;QACtE,KAAK,CAAC,IAAI,CAAC,uBAAuB,CAAC,MAAM,CAAC,aAAa,EAAE,MAAM,CAAC,CAAC,CAAC;IACpE,CAAC;IACD,OAAO,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,mBAAmB,CAAC;AACnE,CAAC;AAED,SAAS,uBAAuB,CAAC,MAAmC,EAAE,MAAc;IAClF,MAAM,SAAS,GAAG,CAAC,CAAC,MAAM,EAAE,qCAAqC,CAAC,CAAC;IACnE,OAAO,MAAM;SACV,GAAG,CACF,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CACP,SAAS,SAAS,UAAU,CAAC,GAAG,CAAC,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,IAAI,CAAC,KAAK,YAAY,CAAC,CAAC,IAAI,CAAC,KAAK,QAAQ,CAAC,CAAC,IAAI,CAAC,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,UAAU,kBAAkB,CAAC,CAAC,CAAC,EAAE,CAC7K;SACA,IAAI,CAAC,OAAO,CAAC,CAAC;AACnB,CAAC;AAED,SAAS,kBAAkB,CAAC,KAAuB;IACjD,OAAO,aAAa,CAClB;QACE,EAAE,EAAE,KAAK,CAAC,EAAE;QACZ,QAAQ,EAAE,KAAK,CAAC,QAAQ;QACxB,QAAQ,EAAE,KAAK,CAAC,QAAQ;QACxB,OAAO,EAAE,KAAK,CAAC,OAAO;QACtB,SAAS,EAAE,KAAK,CAAC,SAAS;QAC1B,KAAK,EAAE,KAAK,CAAC,KAAK;KACnB,EACD,EAAE,SAAS,EAAE,CAAC,EAAE,CACjB,CAAC;AACJ,CAAC;AAED,SAAS,QAAQ,CAAC,KAAa,EAAE,GAAW;IAC1C,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC;IAClD,OAAO,OAAO,CAAC,MAAM,IAAI,GAAG,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,GAAG,CAAC,CAAC,GAAG,CAAC;AAC3E,CAAC"}
@@ -0,0 +1,5 @@
1
+ export { analyzeRepo } from './analyze.js';
2
+ export { minePatterns } from './mine.js';
3
+ export { buildConfigSnippet, formatSuggestText, formatSuggestYaml } from './format.js';
4
+ export type { AnalyzeRepoOptions, DependencySource, DetectedDependency, MinePatternsOptions, PatternRuleDraft, RuleSuggestion, RuleSuggestionReport, SuggestSampleLocation, } from './types.js';
5
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/suggest/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAC3C,OAAO,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AACzC,OAAO,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AACvF,YAAY,EACV,kBAAkB,EAClB,gBAAgB,EAChB,kBAAkB,EAClB,mBAAmB,EACnB,gBAAgB,EAChB,cAAc,EACd,oBAAoB,EACpB,qBAAqB,GACtB,MAAM,YAAY,CAAC"}
@@ -0,0 +1,4 @@
1
+ export { analyzeRepo } from './analyze.js';
2
+ export { minePatterns } from './mine.js';
3
+ export { buildConfigSnippet, formatSuggestText, formatSuggestYaml } from './format.js';
4
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/suggest/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAC3C,OAAO,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AACzC,OAAO,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC"}
@@ -0,0 +1,11 @@
1
+ import type { MinePatternsOptions, PatternRuleDraft } from './types.js';
2
+ /**
3
+ * Approach B (early prototype) — mine the AST for frequently-occurring
4
+ * `new X(...)` / `obj.method(...)` shapes and turn the top ones into draft
5
+ * tree-sitter pattern rules. `severity` is conservatively `info` and the
6
+ * `message`/`messageKo` are TODO placeholders — these are seeds a human edits,
7
+ * not finished rules. Every generated query is compiled against the language
8
+ * grammar before it is emitted; ones that don't compile are dropped.
9
+ */
10
+ export declare function minePatterns(opts: MinePatternsOptions): Promise<PatternRuleDraft[]>;
11
+ //# sourceMappingURL=mine.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mine.d.ts","sourceRoot":"","sources":["../../src/suggest/mine.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,mBAAmB,EAAE,gBAAgB,EAAyB,MAAM,YAAY,CAAC;AAuC/F;;;;;;;GAOG;AACH,wBAAsB,YAAY,CAAC,IAAI,EAAE,mBAAmB,GAAG,OAAO,CAAC,gBAAgB,EAAE,CAAC,CA0EzF"}