clean-code-tools 1.0.1

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 (77) hide show
  1. package/README.md +66 -0
  2. package/configs/eslint.clean-code.recommended.mjs +211 -0
  3. package/configs/python.clean-code.pyproject.toml +143 -0
  4. package/data/clean-code-patterns.jsonl +264 -0
  5. package/data/vector-record.schema.json +77 -0
  6. package/docs/README.md +29 -0
  7. package/docs/eslint-custom-rules.md +74 -0
  8. package/docs/eslint-recommended-config.md +87 -0
  9. package/docs/fastmcp-local-server.md +104 -0
  10. package/docs/publishing.md +125 -0
  11. package/docs/python-lint-recommended-config.md +57 -0
  12. package/docs/python-pylint-custom-rules.md +77 -0
  13. package/docs/semantic-weaviate.md +80 -0
  14. package/docs/static-trigger-semantic-review.md +97 -0
  15. package/evals/clean-code-retrieval.jsonl +13 -0
  16. package/ops/dev/weaviate/README.md +34 -0
  17. package/ops/dev/weaviate/compose.yaml +34 -0
  18. package/ops/dev/weaviate/smoke.sh +28 -0
  19. package/package.json +96 -0
  20. package/pyproject.toml +303 -0
  21. package/sample-apps/README.md +40 -0
  22. package/sample-apps/python-app/pyproject.toml +113 -0
  23. package/sample-apps/python-app/src/clean_pricing.py +10 -0
  24. package/sample-apps/python-app/src/smelly_pricing.py +8 -0
  25. package/sample-apps/ts-backend/eslint.config.mjs +3 -0
  26. package/sample-apps/ts-backend/package.json +18 -0
  27. package/sample-apps/ts-backend/src/clean-handler.ts +19 -0
  28. package/sample-apps/ts-backend/src/smelly-handler.ts +29 -0
  29. package/sample-apps/ts-backend/tsconfig.json +9 -0
  30. package/sample-apps/ts-frontend/eslint.config.mjs +3 -0
  31. package/sample-apps/ts-frontend/package.json +18 -0
  32. package/sample-apps/ts-frontend/src/CleanWidget.tsx +18 -0
  33. package/sample-apps/ts-frontend/src/SmellyWidget.tsx +27 -0
  34. package/sample-apps/ts-frontend/tsconfig.json +10 -0
  35. package/scripts/_mcp_app.py +21 -0
  36. package/scripts/check_clean_code_review_candidates.py +302 -0
  37. package/scripts/check_fastmcp_server.py +106 -0
  38. package/scripts/check_packages.py +137 -0
  39. package/scripts/check_python_config.py +130 -0
  40. package/scripts/check_repo_python_lint.py +46 -0
  41. package/scripts/check_retrieval_evals.py +132 -0
  42. package/scripts/check_sample_apps.py +169 -0
  43. package/scripts/check_semantic_search_tooling.py +102 -0
  44. package/scripts/clean_code_eslint_triggers.py +272 -0
  45. package/scripts/clean_code_mcp_server.py +7 -0
  46. package/scripts/clean_code_python_triggers.py +318 -0
  47. package/scripts/clean_code_review_candidates.py +291 -0
  48. package/scripts/clean_code_review_io.py +36 -0
  49. package/scripts/clean_code_review_models.py +43 -0
  50. package/scripts/clean_code_semantic.py +27 -0
  51. package/scripts/set_package_versions.py +82 -0
  52. package/scripts/weaviate_ingest_clean_code.py +44 -0
  53. package/scripts/weaviate_search_clean_code.py +51 -0
  54. package/skills/clean-code-mcp-reviewer/SKILL.md +209 -0
  55. package/skills/clean-code-mcp-reviewer/evals/evals.json +30 -0
  56. package/src/js/eslint-plugin-clean-code.mjs +758 -0
  57. package/src/python/clean_code_tools_pylint/__init__.py +14 -0
  58. package/src/python/clean_code_tools_pylint/ast_checker.py +122 -0
  59. package/src/python/clean_code_tools_pylint/comments.py +83 -0
  60. package/src/python/clean_code_tools_pylint/helpers.py +196 -0
  61. package/src/python/mcp_server/__init__.py +1 -0
  62. package/src/python/mcp_server/corpus.py +160 -0
  63. package/src/python/mcp_server/markdown.py +126 -0
  64. package/src/python/mcp_server/models.py +73 -0
  65. package/src/python/mcp_server/ranking.py +125 -0
  66. package/src/python/mcp_server/ranking_scoring.py +232 -0
  67. package/src/python/mcp_server/semantic.py +192 -0
  68. package/src/python/mcp_server/server.py +235 -0
  69. package/src/python/mcp_server/server_payloads.py +83 -0
  70. package/src/python/mcp_server/text.py +104 -0
  71. package/src/python/mcp_server/utils/__init__.py +1 -0
  72. package/src/python/mcp_server/utils/httpx_loader.py +14 -0
  73. package/src/python/mcp_server/utils/increment.py +7 -0
  74. package/src/python/mcp_server/utils/sha256_text.py +8 -0
  75. package/src/python/mcp_server/utils/unique_strings.py +15 -0
  76. package/src/python/mcp_server/weaviate.py +182 -0
  77. package/uv.lock +2012 -0
@@ -0,0 +1,169 @@
1
+ #!/usr/bin/env python3
2
+ from __future__ import annotations
3
+
4
+ import os
5
+ import shutil
6
+ import subprocess
7
+ import tempfile
8
+ from pathlib import Path
9
+
10
+ ROOT = Path(__file__).resolve().parents[1]
11
+ PYTHON_SRC = ROOT / "src" / "python"
12
+
13
+
14
+ def run(command: list[str], cwd: Path, check: bool = True) -> subprocess.CompletedProcess[str]:
15
+ env = os.environ.copy()
16
+ env["PYTHONPATH"] = f"{PYTHON_SRC}:{env.get('PYTHONPATH', '')}".rstrip(":")
17
+ completed = subprocess.run(
18
+ command,
19
+ cwd=cwd,
20
+ env=env,
21
+ check=False,
22
+ text=True,
23
+ stdout=subprocess.PIPE,
24
+ stderr=subprocess.STDOUT,
25
+ )
26
+ if check and completed.returncode != 0:
27
+ print(completed.stdout)
28
+ raise SystemExit(completed.returncode)
29
+ return completed
30
+
31
+
32
+ def check_python_app() -> None:
33
+ with tempfile.TemporaryDirectory(prefix="clean-code-python-sample-"):
34
+ app = ROOT / "sample-apps" / "python-app"
35
+
36
+ run(
37
+ [
38
+ "uv",
39
+ "run",
40
+ "--project",
41
+ str(ROOT),
42
+ "--group",
43
+ "lint",
44
+ "ruff",
45
+ "check",
46
+ "src/clean_pricing.py",
47
+ ],
48
+ cwd=app,
49
+ )
50
+ run(
51
+ [
52
+ "uv",
53
+ "run",
54
+ "--project",
55
+ str(ROOT),
56
+ "--group",
57
+ "lint",
58
+ "pylint",
59
+ "--rcfile=pyproject.toml",
60
+ "src/clean_pricing.py",
61
+ ],
62
+ cwd=app,
63
+ )
64
+
65
+ ruff_smelly = run(
66
+ [
67
+ "uv",
68
+ "run",
69
+ "--project",
70
+ str(ROOT),
71
+ "--group",
72
+ "lint",
73
+ "ruff",
74
+ "check",
75
+ "src/smelly_pricing.py",
76
+ ],
77
+ cwd=app,
78
+ check=False,
79
+ )
80
+ pylint_smelly = run(
81
+ [
82
+ "uv",
83
+ "run",
84
+ "--project",
85
+ str(ROOT),
86
+ "--group",
87
+ "lint",
88
+ "pylint",
89
+ "--rcfile=pyproject.toml",
90
+ "src/smelly_pricing.py",
91
+ ],
92
+ cwd=app,
93
+ check=False,
94
+ )
95
+ if ruff_smelly.returncode == 0 or pylint_smelly.returncode == 0:
96
+ print(ruff_smelly.stdout)
97
+ print(pylint_smelly.stdout)
98
+ raise SystemExit("Expected Python smelly sample to fail linting")
99
+
100
+ for code in ["TD002", "TD003", "ERA001", "ARG001"]:
101
+ if code not in ruff_smelly.stdout:
102
+ print(ruff_smelly.stdout)
103
+ raise SystemExit(f"Expected Python smelly Ruff output to include {code}")
104
+ for code in ["R0913", "C9001", "C9002", "C9007"]:
105
+ if code not in pylint_smelly.stdout:
106
+ print(pylint_smelly.stdout)
107
+ raise SystemExit(f"Expected Python smelly Pylint output to include {code}")
108
+ if "bad-plugin-value" in pylint_smelly.stdout or "unknown-option-value" in pylint_smelly.stdout:
109
+ print(pylint_smelly.stdout)
110
+ raise SystemExit("Expected Python smelly Pylint plugin to load cleanly")
111
+
112
+
113
+ def check_ts_app(app_name: str, clean_script: str, smelly_script: str, expected_rule_ids: list[str]) -> None:
114
+ with tempfile.TemporaryDirectory(prefix=f"clean-code-{app_name}-") as raw_tmp:
115
+ tmp = Path(raw_tmp)
116
+ package_json = ROOT / "sample-apps" / app_name / "package.json"
117
+ package_text = package_json.read_text()
118
+ packed = run(["npm", "pack", "--silent"], cwd=ROOT).stdout.strip().splitlines()[-1]
119
+ tarball = ROOT / packed
120
+ try:
121
+ shutil.copytree(ROOT / "sample-apps" / app_name, tmp / app_name)
122
+ isolated_app = tmp / app_name
123
+ isolated_package = package_text.replace('"clean-code-tools": "file:../.."', f'"clean-code-tools": "{tarball}"')
124
+ (isolated_app / "package.json").write_text(isolated_package)
125
+ run(["bun", "install"], cwd=isolated_app)
126
+ run(["bun", "run", clean_script], cwd=isolated_app)
127
+ smelly = run(["bun", "run", smelly_script], cwd=isolated_app, check=False)
128
+ if smelly.returncode == 0:
129
+ print(smelly.stdout)
130
+ raise SystemExit(f"Expected isolated {app_name} smelly script to fail linting")
131
+ for rule_id in expected_rule_ids:
132
+ if rule_id not in smelly.stdout:
133
+ print(smelly.stdout)
134
+ raise SystemExit(f"Expected isolated {app_name} output to include {rule_id}")
135
+ finally:
136
+ tarball.unlink(missing_ok=True)
137
+
138
+
139
+ def main() -> None:
140
+ check_python_app()
141
+ check_ts_app(
142
+ "ts-backend",
143
+ "lint:clean",
144
+ "lint:smelly",
145
+ [
146
+ "clean-code/todo-format",
147
+ "clean-code/no-commented-out-code",
148
+ "clean-code/no-boolean-flag-arguments",
149
+ "clean-code/no-business-policy-literals",
150
+ "clean-code/no-train-wrecks",
151
+ ],
152
+ )
153
+ check_ts_app(
154
+ "ts-frontend",
155
+ "lint:clean",
156
+ "lint:smelly",
157
+ [
158
+ "clean-code/todo-format",
159
+ "clean-code/no-commented-out-code",
160
+ "clean-code/no-output-argument-mutation",
161
+ "clean-code/no-business-policy-literals",
162
+ "clean-code/no-train-wrecks",
163
+ ],
164
+ )
165
+ print("sample_apps_check=ok")
166
+
167
+
168
+ if __name__ == "__main__":
169
+ main()
@@ -0,0 +1,102 @@
1
+ #!/usr/bin/env python3
2
+ from __future__ import annotations
3
+
4
+ import tempfile
5
+ import uuid
6
+ from pathlib import Path
7
+
8
+ from _mcp_app import load_semantic_module
9
+
10
+ ROOT = Path(__file__).resolve().parents[1]
11
+
12
+
13
+ def main() -> None:
14
+ semantic = load_semantic_module()
15
+
16
+ chunks = semantic.build_chunks()
17
+
18
+ by_kind = {}
19
+ for chunk in chunks:
20
+ by_kind[chunk.chunk_kind] = by_kind.get(chunk.chunk_kind, 0) + 1
21
+ assert by_kind["pattern_record"] == 264
22
+ assert by_kind["markdown_section"] >= 40
23
+ assert len({chunk.chunk_id for chunk in chunks}) == len(chunks)
24
+ assert len({chunk.object_id for chunk in chunks}) == len(chunks)
25
+ for chunk in chunks:
26
+ uuid.UUID(chunk.object_id)
27
+ assert chunk.embedding_text
28
+ assert chunk.text_hash == semantic.sha256_text(chunk.embedding_text)
29
+
30
+ by_chunk_id = {chunk.chunk_id: chunk for chunk in chunks}
31
+ assert "pattern:CC-043" in by_chunk_id
32
+ assert "pattern:CC-068" in by_chunk_id
33
+ assert "pattern:CC-107" in by_chunk_id
34
+ assert by_chunk_id["pattern:CC-043"].languages == ("typescript", "python")
35
+ assert "flag argument" in " ".join(by_chunk_id["pattern:CC-043"].aliases)
36
+
37
+ schema = semantic.create_schema_payload()
38
+ assert schema["class"] == semantic.COLLECTION_NAME
39
+ assert schema["vectorConfig"][semantic.VECTOR_NAME]["vectorizer"] == {"none": {}}
40
+ property_names = {property_config["name"] for property_config in schema["properties"]}
41
+ for required in (
42
+ "chunkId",
43
+ "recordId",
44
+ "sourceFile",
45
+ "sourceKind",
46
+ "sectionPath",
47
+ "ruleFamily",
48
+ "lintability",
49
+ "embeddingText",
50
+ "textHash",
51
+ "embeddingModel",
52
+ ):
53
+ assert required in property_names
54
+
55
+ query = semantic.build_search_graphql_query(
56
+ collection_name=semantic.COLLECTION_NAME,
57
+ vector=[0.1, 0.2, 0.3],
58
+ limit=5,
59
+ )
60
+ assert "nearVector" in query
61
+ assert 'targetVectors: ["content"]' in query
62
+ assert "chunkId recordId sourceFile" in query
63
+
64
+ payload = {"data": {"Get": {semantic.COLLECTION_NAME: [{"chunkId": "pattern:CC-043"}]}}}
65
+ assert semantic.search_rows_from_payload(
66
+ payload,
67
+ collection_name=semantic.COLLECTION_NAME,
68
+ ) == [{"chunkId": "pattern:CC-043"}]
69
+ assert semantic.search_rows_from_payload({}, collection_name=semantic.COLLECTION_NAME) == []
70
+ try:
71
+ semantic.search_rows_from_payload(
72
+ {"errors": [{"message": "bad query"}]},
73
+ collection_name=semantic.COLLECTION_NAME,
74
+ )
75
+ except RuntimeError as exc:
76
+ assert "bad query" in str(exc)
77
+ else:
78
+ raise AssertionError("expected Weaviate GraphQL errors to raise")
79
+
80
+ with tempfile.TemporaryDirectory() as raw_tmp:
81
+ tmp = Path(raw_tmp)
82
+ doc = tmp / "sample.md"
83
+ doc.write_text(
84
+ "# Root\n\n"
85
+ "Intro.\n\n"
86
+ "## Section\n\n"
87
+ "Before.\n\n"
88
+ "```ts\n"
89
+ "const heading = '## not a heading';\n"
90
+ "```\n\n"
91
+ "After.\n"
92
+ )
93
+ sections = semantic.markdown_sections(doc, root=tmp)
94
+ assert len(sections) == 2
95
+ assert sections[1].heading == "Section"
96
+ assert "## not a heading" in sections[1].body
97
+
98
+ print("semantic_search_tooling_check=ok")
99
+
100
+
101
+ if __name__ == "__main__":
102
+ main()
@@ -0,0 +1,272 @@
1
+ from __future__ import annotations
2
+
3
+ from clean_code_review_models import TriggerRule
4
+
5
+ ESLINT_TRIGGERS = {
6
+ "@typescript-eslint/naming-convention": TriggerRule(
7
+ questions=(
8
+ "Is the name hiding domain meaning or using inconsistent terminology?",
9
+ "Would renaming improve call-site readability without changing behavior?",
10
+ ),
11
+ mcp_query="typescript naming consistency intention revealing names",
12
+ ),
13
+ "@typescript-eslint/consistent-type-imports": TriggerRule(
14
+ questions=(
15
+ "Are runtime imports being used only for types?",
16
+ "Would type-only imports clarify the module's runtime dependency boundary?",
17
+ ),
18
+ mcp_query="typescript type only imports runtime dependency boundary",
19
+ ),
20
+ "@typescript-eslint/no-confusing-void-expression": TriggerRule(
21
+ questions=(
22
+ "Is this expression mixing side effects with value flow?",
23
+ "Would separating the side effect from the return value clarify intent?",
24
+ ),
25
+ mcp_query="typescript confusing void expression separate side effects values",
26
+ ),
27
+ "@typescript-eslint/no-magic-numbers": TriggerRule(
28
+ questions=(
29
+ "Does this number encode business policy or a domain threshold?",
30
+ "Would a named constant make the rule searchable and easier to change?",
31
+ ),
32
+ mcp_query="typescript magic number named constant business policy",
33
+ ),
34
+ "@typescript-eslint/no-unnecessary-condition": TriggerRule(
35
+ questions=(
36
+ "Is this branch dead or protecting against an impossible state?",
37
+ "Would removing it clarify the real contract?",
38
+ ),
39
+ mcp_query="typescript unnecessary condition dead branch explicit contract",
40
+ ),
41
+ "@typescript-eslint/no-unnecessary-type-assertion": TriggerRule(
42
+ questions=(
43
+ "Is the assertion hiding a type contract that is already known?",
44
+ "Would removing it make the trusted shape clearer?",
45
+ ),
46
+ mcp_query="typescript unnecessary type assertion trusted shape contract",
47
+ ),
48
+ "@typescript-eslint/no-unused-vars": TriggerRule(
49
+ questions=(
50
+ "Is this dead code or a half-removed responsibility?",
51
+ "Can it be deleted safely without changing behavior?",
52
+ ),
53
+ mcp_query="typescript unused variable dead code remove safely",
54
+ ),
55
+ "@typescript-eslint/prefer-nullish-coalescing": TriggerRule(
56
+ questions=(
57
+ "Is falsy handling being confused with absence handling?",
58
+ "Would explicit nullish logic make the data contract clearer?",
59
+ ),
60
+ mcp_query="typescript nullish coalescing absence contract",
61
+ ),
62
+ "@typescript-eslint/strict-boolean-expressions": TriggerRule(
63
+ questions=(
64
+ "Is truthiness hiding the distinction between absence, empty values, and false?",
65
+ "Would an explicit predicate or comparison make the condition safer to read?",
66
+ ),
67
+ mcp_query="typescript strict boolean expressions explicit predicate truthiness",
68
+ ),
69
+ "@typescript-eslint/switch-exhaustiveness-check": TriggerRule(
70
+ questions=(
71
+ "Is variant handling incomplete or scattered?",
72
+ "Would an exhaustive branch make the domain states explicit?",
73
+ ),
74
+ mcp_query="typescript exhaustive switch discriminated union explicit states",
75
+ ),
76
+ "clean-code/no-boolean-flag-arguments": TriggerRule(
77
+ questions=(
78
+ "Does the boolean select behavior rather than represent plain data?",
79
+ "Would intention-revealing functions make call sites clearer?",
80
+ ),
81
+ mcp_query="typescript boolean flag argument intention revealing functions",
82
+ ),
83
+ "clean-code/no-business-policy-literals": TriggerRule(
84
+ questions=(
85
+ "Does this literal encode business policy or domain state?",
86
+ "Would a named constant or typed domain value make changes safer?",
87
+ ),
88
+ mcp_query="typescript business policy literal named constant domain state",
89
+ ),
90
+ "clean-code/no-commented-out-code": TriggerRule(
91
+ questions=(
92
+ "Is this commented code obsolete implementation detail?",
93
+ "Can it be removed in favor of version control history or a tracked task?",
94
+ ),
95
+ mcp_query="typescript commented out code remove obsolete implementation",
96
+ ),
97
+ "clean-code/no-noisy-comments": TriggerRule(
98
+ questions=(
99
+ "Is the comment adding constraint/context or just visual noise?",
100
+ "Would deleting or replacing it with a precise explanation improve scanability?",
101
+ ),
102
+ mcp_query="typescript noisy comments remove visual separators byline comments",
103
+ ),
104
+ "clean-code/no-output-argument-mutation": TriggerRule(
105
+ questions=(
106
+ "Is mutation hidden behind an output parameter?",
107
+ "Would returning a value make the contract clearer?",
108
+ ),
109
+ mcp_query="typescript output argument mutation return value contract",
110
+ ),
111
+ "clean-code/no-redundant-comment": TriggerRule(
112
+ questions=(
113
+ "Does the comment repeat the next line instead of explaining why?",
114
+ "Can clearer naming or deletion remove the need for the comment?",
115
+ ),
116
+ mcp_query="typescript redundant comment intention revealing code",
117
+ ),
118
+ "clean-code/no-train-wrecks": TriggerRule(
119
+ questions=(
120
+ "Is this chain exposing too much object structure to the caller?",
121
+ "Would a named query/helper or domain method reduce coupling?",
122
+ ),
123
+ mcp_query="typescript train wreck law of demeter object navigation",
124
+ ),
125
+ "clean-code/todo-format": TriggerRule(
126
+ questions=(
127
+ "Is this TODO actionable and traceable?",
128
+ "Should the comment include a tracked issue or be converted into code/tests?",
129
+ ),
130
+ mcp_query="typescript todo comment issue id actionable technical debt",
131
+ ),
132
+ "complexity": TriggerRule(
133
+ questions=(
134
+ "Is this function mixing validation, transformation, branching, and side effects?",
135
+ "Can guard clauses or cohesive helper extraction reduce branching?",
136
+ ),
137
+ mcp_query="typescript high complexity function mixed responsibilities guard clauses",
138
+ ),
139
+ "max-depth": TriggerRule(
140
+ questions=(
141
+ "Is rightward drift hiding the normal path?",
142
+ "Can early returns, named predicates, or smaller functions flatten the flow?",
143
+ ),
144
+ mcp_query="typescript deeply nested conditionals guard clauses readability",
145
+ ),
146
+ "max-lines": TriggerRule(
147
+ questions=(
148
+ "Does this file contain multiple reasons to change?",
149
+ "Are there cohesive module boundaries that preserve the public API?",
150
+ ),
151
+ mcp_query="typescript large file single responsibility extraction boundaries",
152
+ ),
153
+ "max-lines-per-function": TriggerRule(
154
+ questions=(
155
+ "Does this function do more than one job?",
156
+ "Can stable parsing, validation, transformation, or side-effect steps be separated?",
157
+ ),
158
+ mcp_query="typescript long function split validation transformation side effects",
159
+ ),
160
+ "max-params": TriggerRule(
161
+ questions=(
162
+ "Do these parameters travel together as a named concept?",
163
+ "Would a typed object parameter improve call-site clarity?",
164
+ ),
165
+ mcp_query="typescript long parameter list object parameter call site clarity",
166
+ ),
167
+ "no-empty": TriggerRule(
168
+ questions=(
169
+ "Is this empty block hiding ignored behavior or an incomplete branch?",
170
+ "Should the code handle, document, or remove the branch explicitly?",
171
+ ),
172
+ mcp_query="typescript empty block ignored behavior explicit handling",
173
+ ),
174
+ "no-negated-condition": TriggerRule(
175
+ questions=(
176
+ "Is the negative branch making the normal path harder to follow?",
177
+ "Would a positive predicate or guard clause clarify the condition?",
178
+ ),
179
+ mcp_query="typescript negated condition positive predicate guard clause",
180
+ ),
181
+ "no-nested-ternary": TriggerRule(
182
+ questions=(
183
+ "Is dense expression logic hiding a decision table or branching policy?",
184
+ "Would named intermediate values or ordinary branches improve readability?",
185
+ ),
186
+ mcp_query="typescript nested ternary decision logic readability",
187
+ ),
188
+ "no-restricted-syntax": TriggerRule(
189
+ questions=(
190
+ "Is this import list signaling an overly broad module boundary?",
191
+ "Would a narrower module, namespace import, or split dependency improve scanability?",
192
+ ),
193
+ mcp_query="typescript long import list module boundary readability",
194
+ ),
195
+ "no-useless-return": TriggerRule(
196
+ questions=(
197
+ "Is redundant control flow adding noise to the function ending?",
198
+ "Can the return path be simplified without changing behavior?",
199
+ ),
200
+ mcp_query="typescript useless return redundant control flow",
201
+ ),
202
+ "sonarjs/cognitive-complexity": TriggerRule(
203
+ questions=(
204
+ "Which branches force a reader to retain too much context?",
205
+ "Can conditions be named or decision logic isolated?",
206
+ ),
207
+ mcp_query="typescript cognitive complexity named predicates decision logic",
208
+ ),
209
+ "sonarjs/no-dead-store": TriggerRule(
210
+ questions=(
211
+ "Is this assignment dead code or evidence of an incomplete refactor?",
212
+ "Can the unused state or surrounding responsibility be removed?",
213
+ ),
214
+ mcp_query="typescript dead store incomplete refactor unused state",
215
+ ),
216
+ "sonarjs/no-duplicate-string": TriggerRule(
217
+ questions=(
218
+ "Is this repeated string a domain value, message, or policy token?",
219
+ "Would naming it avoid drift between copies?",
220
+ ),
221
+ mcp_query="typescript duplicate string named constant policy drift",
222
+ ),
223
+ "sonarjs/no-duplicated-branches": TriggerRule(
224
+ questions=(
225
+ "Is duplicated branching hiding a missing abstraction or a bug?",
226
+ "Can the condition or branch body be simplified safely?",
227
+ ),
228
+ mcp_query="typescript duplicated branches conditional simplification",
229
+ ),
230
+ "sonarjs/no-identical-conditions": TriggerRule(
231
+ questions=(
232
+ "Is repeated conditional logic hiding a copy/paste mistake or missing predicate?",
233
+ "Can the branching model be simplified safely?",
234
+ ),
235
+ mcp_query="typescript identical conditions duplicated predicate branch bug",
236
+ ),
237
+ "sonarjs/no-identical-functions": TriggerRule(
238
+ questions=(
239
+ "Is this real duplication or a coincidental framework shape?",
240
+ "Can a shared helper remove policy drift without hiding intent?",
241
+ ),
242
+ mcp_query="typescript duplicated functions shared helper policy drift",
243
+ ),
244
+ "sonarjs/no-inverted-boolean-check": TriggerRule(
245
+ questions=(
246
+ "Is inverted boolean logic making the condition harder to read?",
247
+ "Would a positive predicate or renamed boolean clarify intent?",
248
+ ),
249
+ mcp_query="typescript inverted boolean check positive predicate naming",
250
+ ),
251
+ "unicorn/explicit-length-check": TriggerRule(
252
+ questions=(
253
+ "Is collection emptiness being expressed indirectly?",
254
+ "Would an explicit length comparison clarify the condition?",
255
+ ),
256
+ mcp_query="typescript explicit length check collection emptiness",
257
+ ),
258
+ "unicorn/no-negated-condition": TriggerRule(
259
+ questions=(
260
+ "Is the negative branch making the normal path harder to follow?",
261
+ "Would a positive predicate or guard clause clarify the condition?",
262
+ ),
263
+ mcp_query="typescript negated condition positive predicate guard clause",
264
+ ),
265
+ "unicorn/no-null": TriggerRule(
266
+ questions=(
267
+ "Is null being used as an ambiguous absence value?",
268
+ "Does the surrounding code prefer undefined, option-like objects, or explicit states?",
269
+ ),
270
+ mcp_query="typescript null absence value explicit state contract",
271
+ ),
272
+ }
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env python3
2
+ from __future__ import annotations
3
+
4
+ from _mcp_app import load_server_module
5
+
6
+ if __name__ == "__main__":
7
+ load_server_module().main()