@a13xu/lucid 1.4.0 → 1.9.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 (46) hide show
  1. package/README.md +118 -14
  2. package/build/config.d.ts +37 -0
  3. package/build/config.js +45 -0
  4. package/build/database.d.ts +36 -1
  5. package/build/database.js +85 -1
  6. package/build/guardian/coding-analyzer.d.ts +11 -0
  7. package/build/guardian/coding-analyzer.js +393 -0
  8. package/build/guardian/coding-rules.d.ts +1 -0
  9. package/build/guardian/coding-rules.js +97 -0
  10. package/build/index.js +164 -3
  11. package/build/indexer/ast.d.ts +9 -0
  12. package/build/indexer/ast.js +158 -0
  13. package/build/indexer/project.js +21 -13
  14. package/build/memory/experience.d.ts +11 -0
  15. package/build/memory/experience.js +85 -0
  16. package/build/retrieval/context.d.ts +29 -0
  17. package/build/retrieval/context.js +219 -0
  18. package/build/retrieval/qdrant.d.ts +16 -0
  19. package/build/retrieval/qdrant.js +135 -0
  20. package/build/retrieval/tfidf.d.ts +14 -0
  21. package/build/retrieval/tfidf.js +64 -0
  22. package/build/security/alerts.d.ts +44 -0
  23. package/build/security/alerts.js +228 -0
  24. package/build/security/env.d.ts +24 -0
  25. package/build/security/env.js +85 -0
  26. package/build/security/guard.d.ts +35 -0
  27. package/build/security/guard.js +133 -0
  28. package/build/security/ratelimit.d.ts +34 -0
  29. package/build/security/ratelimit.js +105 -0
  30. package/build/security/smtp.d.ts +26 -0
  31. package/build/security/smtp.js +125 -0
  32. package/build/security/ssrf.d.ts +18 -0
  33. package/build/security/ssrf.js +109 -0
  34. package/build/security/waf.d.ts +33 -0
  35. package/build/security/waf.js +174 -0
  36. package/build/tools/coding-guard.d.ts +24 -0
  37. package/build/tools/coding-guard.js +82 -0
  38. package/build/tools/context.d.ts +39 -0
  39. package/build/tools/context.js +105 -0
  40. package/build/tools/init.d.ts +41 -1
  41. package/build/tools/init.js +124 -22
  42. package/build/tools/remember.d.ts +4 -4
  43. package/build/tools/reward.d.ts +29 -0
  44. package/build/tools/reward.js +154 -0
  45. package/build/tools/sync.js +15 -0
  46. package/package.json +9 -2
@@ -0,0 +1,393 @@
1
+ import { extname } from "path";
2
+ const SEVERITY_ORDER = {
3
+ high: 0, medium: 1, low: 2,
4
+ };
5
+ const SEVERITY_ICON = {
6
+ high: "🔴", medium: "🟠", low: "🔵",
7
+ };
8
+ // ---------------------------------------------------------------------------
9
+ // Helpers
10
+ // ---------------------------------------------------------------------------
11
+ function isFrontend(filepath, lang) {
12
+ if (lang === "vue")
13
+ return true;
14
+ const ext = extname(filepath).toLowerCase();
15
+ return ext === ".tsx" || ext === ".jsx" || ext === ".vue";
16
+ }
17
+ /** Returns true if the line looks like a function definition with an opening brace. */
18
+ function isFuncDefLine(line) {
19
+ const trimmed = line.trim();
20
+ if (trimmed.startsWith("//") || trimmed.startsWith("*") || trimmed.startsWith("#"))
21
+ return false;
22
+ // Exclude control-flow keywords
23
+ if (/^\s*(?:if|for|while|switch|try|catch|else)\b/.test(line))
24
+ return false;
25
+ return (/\bfunction\b/.test(line) ||
26
+ /=>\s*\{/.test(line) ||
27
+ /\)\s*(?::\s*[\w<>[\]|\s,&]+)?\s*\{/.test(line)) && line.includes("{");
28
+ }
29
+ /** Extract function name from a definition line, or null. */
30
+ function extractFuncName(line) {
31
+ // function foo(
32
+ let m = line.match(/\bfunction\s+(\w+)\s*\(/);
33
+ if (m)
34
+ return m[1] ?? null;
35
+ // const/let/var foo =
36
+ m = line.match(/(?:const|let|var)\s+(\w+)\s*=/);
37
+ if (m)
38
+ return m[1] ?? null;
39
+ // method foo(: TypeScript class method
40
+ m = line.match(/^\s*(?:async\s+)?(\w+)\s*\(/);
41
+ if (m && !["if", "for", "while", "switch", "try", "catch", "else", "return"].includes(m[1])) {
42
+ return m[1] ?? null;
43
+ }
44
+ return null;
45
+ }
46
+ // ---------------------------------------------------------------------------
47
+ // General quality rules (all languages)
48
+ // ---------------------------------------------------------------------------
49
+ function analyzeGeneral(filepath, lines) {
50
+ const issues = [];
51
+ const totalLines = lines.length;
52
+ // GR001-FILE-SIZE
53
+ if (totalLines > 500) {
54
+ issues.push({
55
+ file: filepath, line: 1, severity: "high", ruleId: "GR001-FILE-SIZE",
56
+ message: `File is ${totalLines} lines (max: 500)`,
57
+ suggestion: "Split into smaller modules or extract helpers.",
58
+ });
59
+ }
60
+ // GR002-FUNC-LENGTH: brace-counting, max 400-line scan
61
+ {
62
+ let i = 0;
63
+ while (i < lines.length) {
64
+ const line = lines[i];
65
+ if (isFuncDefLine(line)) {
66
+ let depth = 0;
67
+ let end = i;
68
+ let found = false;
69
+ for (let j = i; j < Math.min(i + 400, lines.length); j++) {
70
+ for (const ch of lines[j]) {
71
+ if (ch === "{")
72
+ depth++;
73
+ else if (ch === "}")
74
+ depth--;
75
+ }
76
+ if (depth === 0 && j > i) {
77
+ const funcLen = j - i;
78
+ if (funcLen > 60) {
79
+ const name = extractFuncName(line);
80
+ const label = name ? `"${name}"` : "Anonymous function";
81
+ issues.push({
82
+ file: filepath, line: i + 1,
83
+ severity: funcLen > 100 ? "high" : "medium",
84
+ ruleId: "GR002-FUNC-LENGTH",
85
+ message: `${label} is ${funcLen} lines (max: 60)`,
86
+ suggestion: "Break into smaller functions. Each function should do ONE thing.",
87
+ });
88
+ }
89
+ end = j;
90
+ found = true;
91
+ break;
92
+ }
93
+ }
94
+ i = found ? end + 1 : i + 1;
95
+ }
96
+ else {
97
+ i++;
98
+ }
99
+ }
100
+ }
101
+ // GR003 — naming issues (single pass over all lines)
102
+ const BAD_VAR_NAMES = /\b(?:const|let|var)\s+(x|y|z|tmp|temp|data2|result2|val|obj|foo|bar)\s*=/;
103
+ const VAGUE_FUNC_NAMES = /\b(handleStuff|doSomething|processData|doWork|manage)\b/;
104
+ const AND_OR_IN_NAME = /\b\w*(?:And|Or)\w+\b/;
105
+ for (let i = 0; i < lines.length; i++) {
106
+ const line = lines[i];
107
+ const trimmed = line.trim();
108
+ const num = i + 1;
109
+ if (trimmed.startsWith("//") || trimmed.startsWith("*") || trimmed.startsWith("#"))
110
+ continue;
111
+ // GR003-BAD-VAR
112
+ const badVarMatch = line.match(BAD_VAR_NAMES);
113
+ if (badVarMatch) {
114
+ issues.push({
115
+ file: filepath, line: num, severity: "medium", ruleId: "GR003-BAD-VAR",
116
+ message: `Vague variable name "${badVarMatch[1]}"`,
117
+ suggestion: "Use descriptive names that explain purpose, not shape.",
118
+ });
119
+ }
120
+ // GR003-VAGUE-FUNC (on definition lines only)
121
+ if (isFuncDefLine(line)) {
122
+ const vagueMatch = line.match(VAGUE_FUNC_NAMES);
123
+ if (vagueMatch) {
124
+ issues.push({
125
+ file: filepath, line: num, severity: "medium", ruleId: "GR003-VAGUE-FUNC",
126
+ message: `Vague function name "${vagueMatch[1]}"`,
127
+ suggestion: "Name functions after their specific action and object (e.g. parseUserResponse).",
128
+ });
129
+ }
130
+ // GR003-AND-NAME
131
+ const funcName = extractFuncName(line);
132
+ if (funcName && AND_OR_IN_NAME.test(funcName)) {
133
+ issues.push({
134
+ file: filepath, line: num, severity: "low", ruleId: "GR003-AND-NAME",
135
+ message: `Function "${funcName}" suggests multiple responsibilities`,
136
+ suggestion: "Split into two functions, one per responsibility.",
137
+ });
138
+ }
139
+ }
140
+ // GR004-DEEP-NEST: ≥16 leading spaces or ≥4 tabs
141
+ if (trimmed.length > 0) {
142
+ const leadingSpaces = line.match(/^( +)/)?.[1]?.length ?? 0;
143
+ const leadingTabs = line.match(/^(\t+)/)?.[1]?.length ?? 0;
144
+ if (leadingSpaces >= 16 || leadingTabs >= 4) {
145
+ issues.push({
146
+ file: filepath, line: num, severity: "medium", ruleId: "GR004-DEEP-NEST",
147
+ message: "Code nested 4+ levels deep",
148
+ suggestion: "Extract nested logic into helper functions or use early returns.",
149
+ });
150
+ }
151
+ }
152
+ }
153
+ // GR005-DEAD-CODE: 3+ consecutive commented lines containing code-like tokens
154
+ {
155
+ let blockStart = 0;
156
+ let blockLen = 0;
157
+ for (let i = 0; i < lines.length; i++) {
158
+ const trimmed = lines[i].trim();
159
+ const isCommentedCode = (trimmed.startsWith("//") || trimmed.startsWith("#")) &&
160
+ /[=({;]/.test(trimmed);
161
+ if (isCommentedCode) {
162
+ if (blockLen === 0)
163
+ blockStart = i + 1;
164
+ blockLen++;
165
+ if (blockLen === 3) {
166
+ issues.push({
167
+ file: filepath, line: blockStart, severity: "low", ruleId: "GR005-DEAD-CODE",
168
+ message: "3+ consecutive commented-out code lines",
169
+ suggestion: "Remove dead code. Use version control (git) to track history.",
170
+ });
171
+ }
172
+ }
173
+ else {
174
+ blockLen = 0;
175
+ }
176
+ }
177
+ }
178
+ return issues;
179
+ }
180
+ // ---------------------------------------------------------------------------
181
+ // Frontend rules (.tsx, .jsx, .vue)
182
+ // ---------------------------------------------------------------------------
183
+ function analyzeFrontend(filepath, lines) {
184
+ const issues = [];
185
+ const totalLines = lines.length;
186
+ // FE001-COMP-SIZE
187
+ if (totalLines > 300) {
188
+ issues.push({
189
+ file: filepath, line: 1, severity: "high", ruleId: "FE001-COMP-SIZE",
190
+ message: `Component is ${totalLines} lines (max: 300)`,
191
+ suggestion: "Extract sub-components or custom hooks.",
192
+ });
193
+ }
194
+ // FE002-PROP-COUNT: component destructures > 8 props
195
+ for (let i = 0; i < lines.length; i++) {
196
+ const line = lines[i];
197
+ if (!/(?:function\s+[A-Z]|const\s+[A-Z])\w*.*\(\s*\{/.test(line))
198
+ continue;
199
+ // Collect lines until the destructuring block closes
200
+ let collected = line;
201
+ let j = i;
202
+ let opens = (collected.match(/\{/g) ?? []).length;
203
+ let closes = (collected.match(/\}/g) ?? []).length;
204
+ while (opens > closes && j < lines.length - 1) {
205
+ j++;
206
+ const next = lines[j];
207
+ collected += " " + next;
208
+ opens += (next.match(/\{/g) ?? []).length;
209
+ closes += (next.match(/\}/g) ?? []).length;
210
+ }
211
+ // Extract content between ({ and first })
212
+ const propBlock = collected.match(/\(\s*\{([^}]*)\}/);
213
+ if (propBlock) {
214
+ const props = propBlock[1]
215
+ .split(",")
216
+ .map((s) => s.trim())
217
+ .filter((s) => s.length > 0 && !s.startsWith("//"));
218
+ if (props.length > 8) {
219
+ issues.push({
220
+ file: filepath, line: i + 1, severity: "medium", ruleId: "FE002-PROP-COUNT",
221
+ message: `Component destructures ${props.length} props`,
222
+ suggestion: "Group related props into objects. Apply Rule #14: Prefer composition.",
223
+ });
224
+ }
225
+ }
226
+ }
227
+ // FE003-BOOL-PROPS: 3+ is/has/can/should-prefixed boolean props in a Props interface/type
228
+ {
229
+ let inProps = false;
230
+ let depth = 0;
231
+ let boolCount = 0;
232
+ let propsStartLine = 0;
233
+ for (let i = 0; i < lines.length; i++) {
234
+ const line = lines[i];
235
+ if (!inProps && /(?:type|interface)\s+\w*Props\s*(?:=\s*\{|\{)/.test(line)) {
236
+ inProps = true;
237
+ depth = 0;
238
+ boolCount = 0;
239
+ propsStartLine = i + 1;
240
+ }
241
+ if (inProps) {
242
+ for (const ch of line) {
243
+ if (ch === "{")
244
+ depth++;
245
+ else if (ch === "}")
246
+ depth--;
247
+ }
248
+ if (/^\s*(is|has|can|should)[A-Z]\w*[?:].*boolean/.test(line)) {
249
+ boolCount++;
250
+ }
251
+ if (depth <= 0 && i > propsStartLine - 1) {
252
+ if (boolCount >= 3) {
253
+ issues.push({
254
+ file: filepath, line: propsStartLine, severity: "medium", ruleId: "FE003-BOOL-PROPS",
255
+ message: `${boolCount} boolean props found (is/has/can/should prefix)`,
256
+ suggestion: "Replace with a single `status` prop or `variant` enum.",
257
+ });
258
+ }
259
+ inProps = false;
260
+ }
261
+ }
262
+ }
263
+ }
264
+ // FE004-INLINE-STYLE: style={{ in JSX or Vue template
265
+ for (let i = 0; i < lines.length; i++) {
266
+ if (/style=\{\{/.test(lines[i])) {
267
+ issues.push({
268
+ file: filepath, line: i + 1, severity: "medium", ruleId: "FE004-INLINE-STYLE",
269
+ message: "Inline style object in JSX/Vue template",
270
+ suggestion: "Move to a CSS/SCSS module, styled-components, or Tailwind class.",
271
+ });
272
+ }
273
+ }
274
+ // FE005-FETCH-IN-COMP: fetch/axios at component body depth 1 (not inside hook/effect)
275
+ {
276
+ let inComponent = false;
277
+ let compBraceDepth = 0;
278
+ for (let i = 0; i < lines.length; i++) {
279
+ const line = lines[i];
280
+ if (!inComponent && /(?:function\s+[A-Z]\w*|const\s+[A-Z]\w*\s*=)/.test(line)) {
281
+ inComponent = true;
282
+ compBraceDepth = 0;
283
+ }
284
+ if (inComponent) {
285
+ for (const ch of line) {
286
+ if (ch === "{")
287
+ compBraceDepth++;
288
+ else if (ch === "}")
289
+ compBraceDepth--;
290
+ }
291
+ // depth 1 = top-level body of the component function (not inside a hook/callback)
292
+ if (compBraceDepth === 1 && /(?:fetch\s*\(|axios\.)/.test(line)) {
293
+ issues.push({
294
+ file: filepath, line: i + 1, severity: "high", ruleId: "FE005-FETCH-IN-COMP",
295
+ message: "fetch() or axios called directly in component body",
296
+ suggestion: "Move to a custom hook (e.g. useUserData) or a service layer.",
297
+ });
298
+ }
299
+ if (compBraceDepth <= 0) {
300
+ inComponent = false;
301
+ compBraceDepth = 0;
302
+ }
303
+ }
304
+ }
305
+ }
306
+ // FE006-DIRECT-DOM: document.querySelector / getElementById in component file
307
+ for (let i = 0; i < lines.length; i++) {
308
+ if (/document\.querySelector|document\.getElementById/.test(lines[i])) {
309
+ issues.push({
310
+ file: filepath, line: i + 1, severity: "high", ruleId: "FE006-DIRECT-DOM",
311
+ message: "Direct DOM access in component file",
312
+ suggestion: "Use refs (useRef in React, ref= in Vue) instead of direct DOM queries.",
313
+ });
314
+ }
315
+ }
316
+ // FE007-MIXED-STYLE: Tailwind + styled-components + CSS module all in same file
317
+ {
318
+ const hasTailwind = lines.some((l) => /className=["'][^"']*(?:flex|grid|text-|bg-|p-\d|m-\d)/.test(l));
319
+ const hasStyled = lines.some((l) => /import.*from\s+['"]styled-components['"]/.test(l));
320
+ const hasCssModule = lines.some((l) => /import.*from\s+['"].*\.module\.css['"]/.test(l));
321
+ if (hasTailwind && hasStyled && hasCssModule) {
322
+ issues.push({
323
+ file: filepath, line: 1, severity: "low", ruleId: "FE007-MIXED-STYLE",
324
+ message: "Three styling systems in one file (Tailwind + styled-components + CSS module)",
325
+ suggestion: "Choose one styling approach per project and be consistent.",
326
+ });
327
+ }
328
+ }
329
+ // FE008-JSX-TERNARY: nested ternary on a JSX-bearing line
330
+ for (let i = 0; i < lines.length; i++) {
331
+ const line = lines[i];
332
+ // JSX line: contains < followed by uppercase letter or /
333
+ if (!/<[A-Z/]/.test(line) && !/<\//.test(line))
334
+ continue;
335
+ // Nested ternary: two ? ... : patterns on the same line
336
+ if (/\?[^?:]+:[^?:]*\?/.test(line)) {
337
+ issues.push({
338
+ file: filepath, line: i + 1, severity: "medium", ruleId: "FE008-JSX-TERNARY",
339
+ message: "Nested ternary expression in JSX",
340
+ suggestion: "Extract condition to a variable or sub-component for readability.",
341
+ });
342
+ }
343
+ }
344
+ return issues;
345
+ }
346
+ // ---------------------------------------------------------------------------
347
+ // Public API
348
+ // ---------------------------------------------------------------------------
349
+ export function analyzeCodeQuality(filepath, source, lang) {
350
+ const lines = source.split("\n");
351
+ const issues = [
352
+ ...analyzeGeneral(filepath, lines),
353
+ ...(isFrontend(filepath, lang) ? analyzeFrontend(filepath, lines) : []),
354
+ ];
355
+ issues.sort((a, b) => {
356
+ const sevDiff = SEVERITY_ORDER[a.severity] - SEVERITY_ORDER[b.severity];
357
+ return sevDiff !== 0 ? sevDiff : a.line - b.line;
358
+ });
359
+ return issues;
360
+ }
361
+ export function formatQualityReport(filepath, issues) {
362
+ const out = [
363
+ "=".repeat(60),
364
+ "🏛️ CODE QUALITY GUARD — Report",
365
+ "=".repeat(60),
366
+ `File: ${filepath}`,
367
+ `Issues: ${issues.length}`,
368
+ "",
369
+ ];
370
+ if (issues.length === 0) {
371
+ out.push("✅ All quality checks passed.");
372
+ out.push(" Review the full checklist with the coding_rules tool before marking done.");
373
+ }
374
+ else {
375
+ for (const sev of ["high", "medium", "low"]) {
376
+ const group = issues.filter((i) => i.severity === sev);
377
+ if (group.length === 0)
378
+ continue;
379
+ out.push(`--- ${sev.toUpperCase()} (${group.length}) ---`);
380
+ for (const issue of group) {
381
+ out.push(`${SEVERITY_ICON[sev]} [${issue.ruleId}] line ${issue.line} — ${issue.message}`);
382
+ out.push(` 💡 ${issue.suggestion}`);
383
+ }
384
+ out.push("");
385
+ }
386
+ const hasHigh = issues.some((i) => i.severity === "high");
387
+ out.push(hasHigh
388
+ ? "❌ FAIL — Fix high-severity issues before proceeding."
389
+ : "⚠️ WARN — Medium/low issues found. Review before marking done.");
390
+ }
391
+ out.push("=".repeat(60));
392
+ return out.join("\n");
393
+ }
@@ -0,0 +1 @@
1
+ export declare const CODING_RULES = "# 25 Golden Rules \u2014 Code Quality Checklist\n\n## Section 1: General Quality (Rules 1\u201310)\n\n- [ ] **Rule 1 \u2014 File Size**: File is under 500 lines (components under 300).\n PASS: file has fewer lines. FAIL: file exceeds limit \u2014 split into modules.\n\n- [ ] **Rule 2 \u2014 Function Length**: Every function/method is under 60 lines.\n PASS: function is focused and short. FAIL: function exceeds 60 lines \u2014 break it up.\n\n- [ ] **Rule 3 \u2014 Meaningful Names**: No vague variable names (x, tmp, data, val, obj, foo, bar).\n No vague function names (doSomething, handleStuff, processData, manage, doWork).\n PASS: names explain purpose. FAIL: name is a placeholder \u2014 rename to intent.\n\n- [ ] **Rule 4 \u2014 Single Responsibility**: Functions/methods do exactly ONE thing.\n If the name contains \"And\" or \"Or\", it is doing two things.\n PASS: one verb, one concept. FAIL: split into two functions.\n\n- [ ] **Rule 5 \u2014 No Dead Code**: No commented-out code blocks (3+ consecutive lines with =, (, {, ;).\n PASS: code is live. FAIL: remove dead code \u2014 use version control for history.\n\n- [ ] **Rule 6 \u2014 Explicit Error Handling**: Errors are caught, logged, or re-thrown.\n No silent swallowing (catch {} or except: pass).\n PASS: every error path is handled. FAIL: add logging or re-throw.\n\n- [ ] **Rule 7 \u2014 No Magic Numbers**: Numeric literals are replaced with named constants.\n PASS: constants have descriptive names. FAIL: extract to a named constant.\n\n- [ ] **Rule 8 \u2014 Max 3 Nesting Levels**: Code is not nested more than 3 levels deep.\n PASS: deepest indent is 3 levels. FAIL: extract logic or use early returns.\n\n- [ ] **Rule 9 \u2014 DRY (Don't Repeat Yourself)**: No near-duplicate blocks of code.\n PASS: logic is extracted into a shared function. FAIL: refactor duplicates.\n\n- [ ] **Rule 10 \u2014 Explicit Return Types**: Typed languages declare return types on public functions.\n PASS: all public functions have explicit return types. FAIL: add return type annotation.\n\n## Section 2: Frontend Components (Rules 11\u201318)\n\n- [ ] **Rule 11 \u2014 Component Size**: UI components are under 300 lines.\n PASS: component is focused. FAIL: extract sub-components or custom hooks.\n\n- [ ] **Rule 12 \u2014 No Inline Styles**: No style={{ }} in JSX/Vue templates.\n PASS: styles are in CSS/SCSS/CSS-in-JS. FAIL: move to stylesheet or styled component.\n\n- [ ] **Rule 13 \u2014 Prop Limit**: Components accept at most 8 props.\n PASS: props are few and cohesive. FAIL: group related props into an object.\n\n- [ ] **Rule 14 \u2014 Prefer Composition**: Large components are split into sub-components.\n No \"god components\" handling layout + data + formatting + interaction.\n PASS: each component has one visual/logical role. FAIL: extract a sub-component.\n\n- [ ] **Rule 15 \u2014 No Direct DOM Access**: No document.querySelector / getElementById in components.\n PASS: refs are used (useRef, ref=). FAIL: replace with ref mechanism.\n\n- [ ] **Rule 16 \u2014 Data Fetching in Services/Hooks**: No fetch() or axios calls in component body.\n PASS: data fetching is in a custom hook or service. FAIL: extract to useXxx hook.\n\n- [ ] **Rule 17 \u2014 One Styling System**: File uses only one styling approach\n (Tailwind OR styled-components OR CSS modules \u2014 not all three).\n PASS: consistent styling. FAIL: standardize on one approach.\n\n- [ ] **Rule 18 \u2014 No Nested Ternaries in JSX**: JSX conditionals use if/else or variables, not nested ternaries.\n PASS: conditions are readable. FAIL: extract condition to a variable or early return.\n\n## Section 3: Architecture (Rules 19\u201325)\n\n- [ ] **Rule 19 \u2014 Single Source of Truth**: State is not duplicated across multiple stores or components.\n PASS: one authoritative source for each piece of state. FAIL: remove duplication.\n\n- [ ] **Rule 20 \u2014 UI/Logic Separation**: Business logic is not inside UI components.\n PASS: components call services/hooks; logic lives elsewhere. FAIL: extract to service.\n\n- [ ] **Rule 21 \u2014 Dependency Abstraction**: External APIs/SDKs are wrapped in adapter/service layers.\n PASS: swapping a library touches one file. FAIL: add an abstraction layer.\n\n- [ ] **Rule 22 \u2014 No Circular Imports**: Module dependency graph is a DAG (no cycles).\n PASS: import graph has no cycles. FAIL: restructure modules to remove the cycle.\n\n- [ ] **Rule 23 \u2014 Config in Dedicated Files**: Magic strings and environment-specific values are in config files.\n PASS: config is centralized. FAIL: extract to config.ts or .env.\n\n- [ ] **Rule 24 \u2014 Public API Coverage**: Every exported function has a corresponding test.\n PASS: public API is covered by tests. FAIL: write a test for the new export.\n\n- [ ] **Rule 25 \u2014 No God Objects**: Classes/modules are focused \u2014 no object that knows everything.\n PASS: objects have clear, narrow responsibilities. FAIL: split the god object.\n\n---\n\n## Quick Check Before Marking Done\n\n1. Run `check_code_quality` on modified files \u2014 fix HIGH severity issues.\n2. Run `validate_file` (Logic Guardian) \u2014 fix correctness bugs first.\n3. Verify rules 3, 4, 8 manually (naming and nesting are hard to auto-detect fully).\n4. For frontend work, verify rules 12, 15, 16 \u2014 these are the most common oversights.\n";
@@ -0,0 +1,97 @@
1
+ export const CODING_RULES = `# 25 Golden Rules — Code Quality Checklist
2
+
3
+ ## Section 1: General Quality (Rules 1–10)
4
+
5
+ - [ ] **Rule 1 — File Size**: File is under 500 lines (components under 300).
6
+ PASS: file has fewer lines. FAIL: file exceeds limit — split into modules.
7
+
8
+ - [ ] **Rule 2 — Function Length**: Every function/method is under 60 lines.
9
+ PASS: function is focused and short. FAIL: function exceeds 60 lines — break it up.
10
+
11
+ - [ ] **Rule 3 — Meaningful Names**: No vague variable names (x, tmp, data, val, obj, foo, bar).
12
+ No vague function names (doSomething, handleStuff, processData, manage, doWork).
13
+ PASS: names explain purpose. FAIL: name is a placeholder — rename to intent.
14
+
15
+ - [ ] **Rule 4 — Single Responsibility**: Functions/methods do exactly ONE thing.
16
+ If the name contains "And" or "Or", it is doing two things.
17
+ PASS: one verb, one concept. FAIL: split into two functions.
18
+
19
+ - [ ] **Rule 5 — No Dead Code**: No commented-out code blocks (3+ consecutive lines with =, (, {, ;).
20
+ PASS: code is live. FAIL: remove dead code — use version control for history.
21
+
22
+ - [ ] **Rule 6 — Explicit Error Handling**: Errors are caught, logged, or re-thrown.
23
+ No silent swallowing (catch {} or except: pass).
24
+ PASS: every error path is handled. FAIL: add logging or re-throw.
25
+
26
+ - [ ] **Rule 7 — No Magic Numbers**: Numeric literals are replaced with named constants.
27
+ PASS: constants have descriptive names. FAIL: extract to a named constant.
28
+
29
+ - [ ] **Rule 8 — Max 3 Nesting Levels**: Code is not nested more than 3 levels deep.
30
+ PASS: deepest indent is 3 levels. FAIL: extract logic or use early returns.
31
+
32
+ - [ ] **Rule 9 — DRY (Don't Repeat Yourself)**: No near-duplicate blocks of code.
33
+ PASS: logic is extracted into a shared function. FAIL: refactor duplicates.
34
+
35
+ - [ ] **Rule 10 — Explicit Return Types**: Typed languages declare return types on public functions.
36
+ PASS: all public functions have explicit return types. FAIL: add return type annotation.
37
+
38
+ ## Section 2: Frontend Components (Rules 11–18)
39
+
40
+ - [ ] **Rule 11 — Component Size**: UI components are under 300 lines.
41
+ PASS: component is focused. FAIL: extract sub-components or custom hooks.
42
+
43
+ - [ ] **Rule 12 — No Inline Styles**: No style={{ }} in JSX/Vue templates.
44
+ PASS: styles are in CSS/SCSS/CSS-in-JS. FAIL: move to stylesheet or styled component.
45
+
46
+ - [ ] **Rule 13 — Prop Limit**: Components accept at most 8 props.
47
+ PASS: props are few and cohesive. FAIL: group related props into an object.
48
+
49
+ - [ ] **Rule 14 — Prefer Composition**: Large components are split into sub-components.
50
+ No "god components" handling layout + data + formatting + interaction.
51
+ PASS: each component has one visual/logical role. FAIL: extract a sub-component.
52
+
53
+ - [ ] **Rule 15 — No Direct DOM Access**: No document.querySelector / getElementById in components.
54
+ PASS: refs are used (useRef, ref=). FAIL: replace with ref mechanism.
55
+
56
+ - [ ] **Rule 16 — Data Fetching in Services/Hooks**: No fetch() or axios calls in component body.
57
+ PASS: data fetching is in a custom hook or service. FAIL: extract to useXxx hook.
58
+
59
+ - [ ] **Rule 17 — One Styling System**: File uses only one styling approach
60
+ (Tailwind OR styled-components OR CSS modules — not all three).
61
+ PASS: consistent styling. FAIL: standardize on one approach.
62
+
63
+ - [ ] **Rule 18 — No Nested Ternaries in JSX**: JSX conditionals use if/else or variables, not nested ternaries.
64
+ PASS: conditions are readable. FAIL: extract condition to a variable or early return.
65
+
66
+ ## Section 3: Architecture (Rules 19–25)
67
+
68
+ - [ ] **Rule 19 — Single Source of Truth**: State is not duplicated across multiple stores or components.
69
+ PASS: one authoritative source for each piece of state. FAIL: remove duplication.
70
+
71
+ - [ ] **Rule 20 — UI/Logic Separation**: Business logic is not inside UI components.
72
+ PASS: components call services/hooks; logic lives elsewhere. FAIL: extract to service.
73
+
74
+ - [ ] **Rule 21 — Dependency Abstraction**: External APIs/SDKs are wrapped in adapter/service layers.
75
+ PASS: swapping a library touches one file. FAIL: add an abstraction layer.
76
+
77
+ - [ ] **Rule 22 — No Circular Imports**: Module dependency graph is a DAG (no cycles).
78
+ PASS: import graph has no cycles. FAIL: restructure modules to remove the cycle.
79
+
80
+ - [ ] **Rule 23 — Config in Dedicated Files**: Magic strings and environment-specific values are in config files.
81
+ PASS: config is centralized. FAIL: extract to config.ts or .env.
82
+
83
+ - [ ] **Rule 24 — Public API Coverage**: Every exported function has a corresponding test.
84
+ PASS: public API is covered by tests. FAIL: write a test for the new export.
85
+
86
+ - [ ] **Rule 25 — No God Objects**: Classes/modules are focused — no object that knows everything.
87
+ PASS: objects have clear, narrow responsibilities. FAIL: split the god object.
88
+
89
+ ---
90
+
91
+ ## Quick Check Before Marking Done
92
+
93
+ 1. Run \`check_code_quality\` on modified files — fix HIGH severity issues.
94
+ 2. Run \`validate_file\` (Logic Guardian) — fix correctness bugs first.
95
+ 3. Verify rules 3, 4, 8 manually (naming and nesting are hard to auto-detect fully).
96
+ 4. For frontend work, verify rules 12, 15, 16 — these are the most common oversights.
97
+ `;