opengrammar-server 2.0.615350

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 (53) hide show
  1. package/README.npm.md +95 -0
  2. package/bin/opengrammar-server.js +111 -0
  3. package/dist/server.js +48639 -0
  4. package/package.json +80 -0
  5. package/server-node.ts +159 -0
  6. package/server.ts +15 -0
  7. package/src/analyzer.ts +542 -0
  8. package/src/dictionary.ts +1973 -0
  9. package/src/index.ts +978 -0
  10. package/src/nlp/nlp-engine.ts +17 -0
  11. package/src/nlp/tone-analyzer.ts +269 -0
  12. package/src/rephraser.ts +146 -0
  13. package/src/rules/categories/academic-writing.ts +182 -0
  14. package/src/rules/categories/adjectives-adverbs.ts +152 -0
  15. package/src/rules/categories/articles.ts +160 -0
  16. package/src/rules/categories/business-writing.ts +250 -0
  17. package/src/rules/categories/capitalization.ts +79 -0
  18. package/src/rules/categories/clarity.ts +117 -0
  19. package/src/rules/categories/common-errors.ts +601 -0
  20. package/src/rules/categories/confused-words.ts +219 -0
  21. package/src/rules/categories/conjunctions.ts +176 -0
  22. package/src/rules/categories/dangling-modifiers.ts +123 -0
  23. package/src/rules/categories/formality.ts +274 -0
  24. package/src/rules/categories/formatting-idioms.ts +323 -0
  25. package/src/rules/categories/gerund-infinitive.ts +274 -0
  26. package/src/rules/categories/grammar-advanced.ts +294 -0
  27. package/src/rules/categories/grammar.ts +286 -0
  28. package/src/rules/categories/inclusive-language.ts +280 -0
  29. package/src/rules/categories/nouns-pronouns.ts +233 -0
  30. package/src/rules/categories/prepositions-extended.ts +217 -0
  31. package/src/rules/categories/prepositions.ts +159 -0
  32. package/src/rules/categories/punctuation.ts +347 -0
  33. package/src/rules/categories/quantity-agreement.ts +200 -0
  34. package/src/rules/categories/readability.ts +293 -0
  35. package/src/rules/categories/sentence-structure.ts +100 -0
  36. package/src/rules/categories/spelling-advanced.ts +164 -0
  37. package/src/rules/categories/spelling.ts +119 -0
  38. package/src/rules/categories/style-tone.ts +511 -0
  39. package/src/rules/categories/style.ts +78 -0
  40. package/src/rules/categories/subject-verb-agreement.ts +201 -0
  41. package/src/rules/categories/tone-rules.ts +206 -0
  42. package/src/rules/categories/verb-tense.ts +582 -0
  43. package/src/rules/context-filter.ts +446 -0
  44. package/src/rules/index.ts +96 -0
  45. package/src/rules/ruleset-part1-cj-pu-sp.json +657 -0
  46. package/src/rules/ruleset-part1-np-ad-aa-pr.json +831 -0
  47. package/src/rules/ruleset-part1-ss-vt.json +907 -0
  48. package/src/rules/ruleset-part2-cw-st-nf.json +318 -0
  49. package/src/rules/ruleset-part3-aw-bw-il-rd.json +161 -0
  50. package/src/rules/types.ts +79 -0
  51. package/src/shared-types.ts +152 -0
  52. package/src/spellchecker.ts +418 -0
  53. package/tsconfig.json +25 -0
@@ -0,0 +1,286 @@
1
+ import { createRegexRule, type Rule } from '../types.js';
2
+
3
+ export const basicGrammarRules: Rule[] = [
4
+ createRegexRule({
5
+ id: 'subject-pronouns',
6
+ category: 'grammar',
7
+ pattern: /\b(me|him|her|them|us)\s+and\s+(I|he|she|they|we)\b/i,
8
+ suggestion: (match) =>
9
+ match[0].replace(/\b(me|him|her|them|us)\s+and\s+(I|he|she|they|we)\b/i, '$2 and $1'),
10
+ reason: 'Use subject pronouns (I, he, she, they, we) when they are part of the subject.',
11
+ }),
12
+ createRegexRule({
13
+ id: 'me-and-him',
14
+ category: 'grammar',
15
+ pattern: /\b(me\s+and\s+him|him\s+and\s+me)\b/i,
16
+ suggestion: 'he and I',
17
+ reason: 'When acting as the subject, use "He and I".',
18
+ }),
19
+ createRegexRule({
20
+ id: 'me-and-her',
21
+ category: 'grammar',
22
+ pattern: /\b(me\s+and\s+her|her\s+and\s+me)\b/i,
23
+ suggestion: 'she and I',
24
+ reason: 'When acting as the subject, use "She and I".',
25
+ }),
26
+ createRegexRule({
27
+ id: 'beside-you-and-i',
28
+ category: 'grammar',
29
+ pattern: /\bbeside\s+you\s+and\s+I\b/i,
30
+ suggestion: 'beside you and me',
31
+ reason: 'Use object pronouns after prepositions ("between you and me").',
32
+ }),
33
+ createRegexRule({
34
+ id: 'between-you-and-i',
35
+ category: 'grammar',
36
+ pattern: /\bbetween\s+you\s+and\s+I\b/i,
37
+ suggestion: 'between you and me',
38
+ reason: 'Use object pronouns after prepositions ("between you and me").',
39
+ }),
40
+ createRegexRule({
41
+ id: 'arrived-to',
42
+ category: 'grammar',
43
+ pattern: /\barrived\s+to\b/i,
44
+ suggestion: 'arrived in / at',
45
+ reason: 'Use "arrived in" for cities/countries and "arrived at" for specific places/events.',
46
+ }),
47
+ createRegexRule({
48
+ id: 'could-care-less',
49
+ category: 'grammar',
50
+ pattern: /\bcould\s+care\s+less\b/i,
51
+ suggestion: "couldn't care less",
52
+ reason: 'The idiom is "couldn\'t care less" (meaning you care zero percent).',
53
+ }),
54
+ createRegexRule({
55
+ id: 'on-accident',
56
+ category: 'grammar',
57
+ pattern: /\bon\s+accident\b/i,
58
+ suggestion: 'by accident',
59
+ reason: 'The correct idiom is "by accident", not "on accident".',
60
+ }),
61
+ createRegexRule({
62
+ id: 'based-off-of',
63
+ category: 'grammar',
64
+ pattern: /\bbased\s+off\s+of\b/i,
65
+ suggestion: 'based on',
66
+ reason: 'Use "based on" instead of "based off of".',
67
+ }),
68
+ createRegexRule({
69
+ id: 'intents-purposes',
70
+ category: 'grammar',
71
+ pattern: /\bfor\s+all\s+intensive\s+purposes\b/i,
72
+ suggestion: 'for all intents and purposes',
73
+ reason: 'The correct idiom is "for all intents and purposes".',
74
+ }),
75
+ createRegexRule({
76
+ id: 'alot',
77
+ category: 'spelling',
78
+ pattern: /\balot\b/i,
79
+ suggestion: 'a lot',
80
+ reason: '"A lot" is always two words.',
81
+ }),
82
+ createRegexRule({
83
+ id: 'object-pronouns',
84
+ category: 'grammar',
85
+ pattern: /\b(I|he|she|they|we)\s+and\s+(me|him|her|them|us)\b/i,
86
+ suggestion: (match) =>
87
+ match[0].replace(/\b(I|he|she|they|we)\s+and\s+(me|him|her|them|us)\b/i, '$2 and $1'),
88
+ reason: 'Use object pronouns (me, him, her, them, us) when they are part of the object.',
89
+ }),
90
+ createRegexRule({
91
+ id: 'buyed',
92
+ category: 'grammar',
93
+ pattern: /\bbuyed\b/i,
94
+ suggestion: 'bought',
95
+ reason: '"Buyed" is not a word. The past tense of "buy" is "bought".',
96
+ }),
97
+ createRegexRule({
98
+ id: 'runned',
99
+ category: 'grammar',
100
+ pattern: /\brunned\b/i,
101
+ suggestion: 'ran',
102
+ reason: '"Runned" is not a word. The past tense of "run" is "ran".',
103
+ }),
104
+ createRegexRule({
105
+ id: 'goed',
106
+ category: 'grammar',
107
+ pattern: /\bgoed\b/i,
108
+ suggestion: 'went',
109
+ reason: '"Goed" is not a word. The past tense of "go" is "went".',
110
+ }),
111
+ createRegexRule({
112
+ id: 'eated',
113
+ category: 'grammar',
114
+ pattern: /\beated\b/i,
115
+ suggestion: 'ate',
116
+ reason: '"Eated" is not a word. The past tense of "eat" is "ate".',
117
+ }),
118
+ createRegexRule({
119
+ id: 'comed',
120
+ category: 'grammar',
121
+ pattern: /\bcomed\b/i,
122
+ suggestion: 'came',
123
+ reason: '"Comed" is not a word. The past tense of "come" is "came".',
124
+ }),
125
+ createRegexRule({
126
+ id: 'taked',
127
+ category: 'grammar',
128
+ pattern: /\btaked\b/i,
129
+ suggestion: 'took',
130
+ reason: '"Taked" is not a word. The past tense of "take" is "took".',
131
+ }),
132
+ createRegexRule({
133
+ id: 'bringed',
134
+ category: 'grammar',
135
+ pattern: /\bbringed\b/i,
136
+ suggestion: 'brought',
137
+ reason: '"Bringed" is not a word. The past tense of "bring" is "brought".',
138
+ }),
139
+ createRegexRule({
140
+ id: 'thinked',
141
+ category: 'grammar',
142
+ pattern: /\bthinked\b/i,
143
+ suggestion: 'thought',
144
+ reason: '"Thinked" is not a word. The past tense of "think" is "thought".',
145
+ }),
146
+ createRegexRule({
147
+ id: 'knowed',
148
+ category: 'grammar',
149
+ pattern: /\bknowed\b/i,
150
+ suggestion: 'knew',
151
+ reason: '"Knowed" is not a word. The past tense of "know" is "knew".',
152
+ }),
153
+ createRegexRule({
154
+ id: 'has-got',
155
+ category: 'grammar',
156
+ pattern: /\bhas\s+got\b/i,
157
+ suggestion: 'has',
158
+ reason: '"Has got" is redundant. Use "has" instead.',
159
+ }),
160
+ createRegexRule({
161
+ id: 'having-got',
162
+ category: 'grammar',
163
+ pattern: /\bhaving\s+got\b/i,
164
+ suggestion: 'have',
165
+ reason: '"Having got" is awkward. Use "have" instead.',
166
+ }),
167
+ createRegexRule({
168
+ id: 'wanna',
169
+ category: 'grammar',
170
+ pattern: /\bwanna\b/i,
171
+ suggestion: 'want to',
172
+ reason: '"Wanna" is informal. Use "want to" in formal writing.',
173
+ }),
174
+ createRegexRule({
175
+ id: 'gotta',
176
+ category: 'grammar',
177
+ pattern: /\bgotta\b/i,
178
+ suggestion: 'have to',
179
+ reason: '"Gotta" is informal. Use "have to" in formal writing.',
180
+ }),
181
+ createRegexRule({
182
+ id: 'kinda',
183
+ category: 'grammar',
184
+ pattern: /\bkinda\b/i,
185
+ suggestion: 'kind of',
186
+ reason: '"Kinda" is informal. Use "kind of" in formal writing.',
187
+ }),
188
+ createRegexRule({
189
+ id: 'could-of',
190
+ category: 'grammar',
191
+ pattern: /\bcould\s+of\b/i,
192
+ suggestion: 'could have',
193
+ reason: '"Could of" is incorrect. Use "could have".',
194
+ }),
195
+ createRegexRule({
196
+ id: 'would-of',
197
+ category: 'grammar',
198
+ pattern: /\bwould\s+of\b/i,
199
+ suggestion: 'would have',
200
+ reason: '"Would of" is incorrect. Use "would have".',
201
+ }),
202
+ createRegexRule({
203
+ id: 'should-of',
204
+ category: 'grammar',
205
+ pattern: /\bshould\s+of\b/i,
206
+ suggestion: 'should have',
207
+ reason: '"Should of" is incorrect. Use "should have".',
208
+ }),
209
+ createRegexRule({
210
+ id: 'might-of',
211
+ category: 'grammar',
212
+ pattern: /\bmight\s+of\b/i,
213
+ suggestion: 'might have',
214
+ reason: '"Might of" is incorrect. Use "might have".',
215
+ }),
216
+ createRegexRule({
217
+ id: 'must-of',
218
+ category: 'grammar',
219
+ pattern: /\bmust\s+of\b/i,
220
+ suggestion: 'must have',
221
+ reason: '"Must of" is incorrect. Use "must have".',
222
+ }),
223
+ createRegexRule({
224
+ id: 'shouldnt-missing-apos',
225
+ category: 'grammar',
226
+ pattern: /\bshouldnt\b/i,
227
+ suggestion: "shouldn't",
228
+ reason: 'Missing apostrophe.',
229
+ }),
230
+ createRegexRule({
231
+ id: 'couldnt-missing-apos',
232
+ category: 'grammar',
233
+ pattern: /\bcouldnt\b/i,
234
+ suggestion: "couldn't",
235
+ reason: 'Missing apostrophe.',
236
+ }),
237
+ createRegexRule({
238
+ id: 'wouldnt-missing-apos',
239
+ category: 'grammar',
240
+ pattern: /\bwouldnt\b/i,
241
+ suggestion: "wouldn't",
242
+ reason: 'Missing apostrophe.',
243
+ }),
244
+ createRegexRule({
245
+ id: 'didnt-missing-apos',
246
+ category: 'grammar',
247
+ pattern: /\bdidnt\b/i,
248
+ suggestion: "didn't",
249
+ reason: 'Missing apostrophe.',
250
+ }),
251
+ createRegexRule({
252
+ id: 'doesnt-missing-apos',
253
+ category: 'grammar',
254
+ pattern: /\bdoesnt\b/i,
255
+ suggestion: "doesn't",
256
+ reason: 'Missing apostrophe.',
257
+ }),
258
+ createRegexRule({
259
+ id: 'isnt-missing-apos',
260
+ category: 'grammar',
261
+ pattern: /\bisnt\b/i,
262
+ suggestion: "isn't",
263
+ reason: 'Missing apostrophe.',
264
+ }),
265
+ createRegexRule({
266
+ id: 'arent-missing-apos',
267
+ category: 'grammar',
268
+ pattern: /\barent\b/i,
269
+ suggestion: "aren't",
270
+ reason: 'Missing apostrophe.',
271
+ }),
272
+ createRegexRule({
273
+ id: 'wasnt-missing-apos',
274
+ category: 'grammar',
275
+ pattern: /\bwasnt\b/i,
276
+ suggestion: "wasn't",
277
+ reason: 'Missing apostrophe.',
278
+ }),
279
+ createRegexRule({
280
+ id: 'werent-missing-apos',
281
+ category: 'grammar',
282
+ pattern: /\bwerent\b/i,
283
+ suggestion: "weren't",
284
+ reason: 'Missing apostrophe.',
285
+ }),
286
+ ];
@@ -0,0 +1,280 @@
1
+ import type { Issue } from '../../shared-types.js';
2
+ import { createRegexRule, type Rule } from '../types.js';
3
+
4
+ /**
5
+ * ═══════════════════════════════════════════════════
6
+ * Inclusive Language (IL)
7
+ * Gender-neutral, disability-aware, culturally sensitive
8
+ * ═══════════════════════════════════════════════════
9
+ */
10
+ export const inclusiveLanguageRules: Rule[] = [
11
+ // ═══ Gendered Job Titles (IL_001) ═══
12
+ createRegexRule({
13
+ id: 'IL_chairman',
14
+ category: 'style',
15
+ pattern: /\bchairman\b/i,
16
+ suggestion: 'chairperson',
17
+ reason: '"Chairman" is gendered. Use "chairperson", "chair", or "presiding officer".',
18
+ }),
19
+ createRegexRule({
20
+ id: 'IL_chairwoman',
21
+ category: 'style',
22
+ pattern: /\bchairwoman\b/i,
23
+ suggestion: 'chairperson',
24
+ reason: '"Chairwoman" is gendered. Use "chairperson" or "chair".',
25
+ }),
26
+ createRegexRule({
27
+ id: 'IL_fireman',
28
+ category: 'style',
29
+ pattern: /\bfireman\b/i,
30
+ suggestion: 'firefighter',
31
+ reason: '"Fireman" is gendered. Use "firefighter".',
32
+ }),
33
+ createRegexRule({
34
+ id: 'IL_firemen',
35
+ category: 'style',
36
+ pattern: /\bfiremen\b/i,
37
+ suggestion: 'firefighters',
38
+ reason: '"Firemen" is gendered. Use "firefighters".',
39
+ }),
40
+ createRegexRule({
41
+ id: 'IL_policeman',
42
+ category: 'style',
43
+ pattern: /\bpoliceman\b/i,
44
+ suggestion: 'police officer',
45
+ reason: '"Policeman" is gendered. Use "police officer".',
46
+ }),
47
+ createRegexRule({
48
+ id: 'IL_policemen',
49
+ category: 'style',
50
+ pattern: /\bpolicemen\b/i,
51
+ suggestion: 'police officers',
52
+ reason: '"Policemen" is gendered. Use "police officers".',
53
+ }),
54
+ createRegexRule({
55
+ id: 'IL_policewoman',
56
+ category: 'style',
57
+ pattern: /\bpolicewoman\b/i,
58
+ suggestion: 'police officer',
59
+ reason: '"Policewoman" is gendered. Use "police officer".',
60
+ }),
61
+ createRegexRule({
62
+ id: 'IL_stewardess',
63
+ category: 'style',
64
+ pattern: /\bstewardess\b/i,
65
+ suggestion: 'flight attendant',
66
+ reason: '"Stewardess" is gendered. Use "flight attendant".',
67
+ }),
68
+ createRegexRule({
69
+ id: 'IL_mailman',
70
+ category: 'style',
71
+ pattern: /\bmailman\b/i,
72
+ suggestion: 'mail carrier',
73
+ reason: '"Mailman" is gendered. Use "mail carrier" or "postal worker".',
74
+ }),
75
+ createRegexRule({
76
+ id: 'IL_mankind',
77
+ category: 'style',
78
+ pattern: /\bmankind\b/i,
79
+ suggestion: 'humankind',
80
+ reason: '"Mankind" is gendered. Use "humankind", "humanity", or "people".',
81
+ }),
82
+ createRegexRule({
83
+ id: 'IL_manpower',
84
+ category: 'style',
85
+ pattern: /\bmanpower\b/i,
86
+ suggestion: 'workforce',
87
+ reason: '"Manpower" is gendered. Use "workforce", "staff", or "personnel".',
88
+ }),
89
+ createRegexRule({
90
+ id: 'IL_man_made',
91
+ category: 'style',
92
+ pattern: /\bman[\s-]made\b/i,
93
+ suggestion: 'artificial',
94
+ reason: '"Man-made" is gendered. Use "artificial", "synthetic", or "manufactured".',
95
+ }),
96
+ createRegexRule({
97
+ id: 'IL_businessman',
98
+ category: 'style',
99
+ pattern: /\bbusinessman\b/i,
100
+ suggestion: 'businessperson',
101
+ reason: '"Businessman" is gendered. Use "businessperson", "executive", or "professional".',
102
+ }),
103
+ createRegexRule({
104
+ id: 'IL_businessmen',
105
+ category: 'style',
106
+ pattern: /\bbusinessmen\b/i,
107
+ suggestion: 'businesspeople',
108
+ reason: '"Businessmen" is gendered. Use "businesspeople" or "professionals".',
109
+ }),
110
+ createRegexRule({
111
+ id: 'IL_spokesman',
112
+ category: 'style',
113
+ pattern: /\bspokesman\b/i,
114
+ suggestion: 'spokesperson',
115
+ reason: '"Spokesman" is gendered. Use "spokesperson" or "representative".',
116
+ }),
117
+ createRegexRule({
118
+ id: 'IL_congressmen',
119
+ category: 'style',
120
+ pattern: /\bcongressman\b/i,
121
+ suggestion: 'congress member',
122
+ reason: '"Congressman" is gendered. Use "congress member" or "representative".',
123
+ }),
124
+ createRegexRule({
125
+ id: 'IL_manhole',
126
+ category: 'style',
127
+ pattern: /\bmanhole\b/i,
128
+ suggestion: 'maintenance hole',
129
+ reason: '"Manhole" is gendered. Use "maintenance hole" or "utility access".',
130
+ }),
131
+ createRegexRule({
132
+ id: 'IL_craftsman',
133
+ category: 'style',
134
+ pattern: /\bcraftsman\b/i,
135
+ suggestion: 'craftsperson',
136
+ reason: '"Craftsman" is gendered. Use "craftsperson" or "artisan".',
137
+ }),
138
+ createRegexRule({
139
+ id: 'IL_salesman',
140
+ category: 'style',
141
+ pattern: /\bsalesman\b/i,
142
+ suggestion: 'salesperson',
143
+ reason: '"Salesman" is gendered. Use "salesperson" or "sales representative".',
144
+ }),
145
+ createRegexRule({
146
+ id: 'IL_waitress',
147
+ category: 'style',
148
+ pattern: /\bwaitress\b/i,
149
+ suggestion: 'server',
150
+ reason: '"Waitress" is gendered. Use "server".',
151
+ }),
152
+ createRegexRule({
153
+ id: 'IL_actress',
154
+ category: 'style',
155
+ pattern: /\bactress\b/i,
156
+ suggestion: 'actor',
157
+ reason: 'Modern usage prefers "actor" for all genders in professional contexts.',
158
+ }),
159
+ createRegexRule({
160
+ id: 'IL_housewife',
161
+ category: 'style',
162
+ pattern: /\bhousewife\b/i,
163
+ suggestion: 'homemaker',
164
+ reason: '"Housewife" is gendered. Use "homemaker" or "stay-at-home parent".',
165
+ }),
166
+
167
+ // ═══ Person-First Language (IL_003) ═══
168
+ createRegexRule({
169
+ id: 'IL_disabled_person',
170
+ category: 'style',
171
+ pattern:
172
+ /\b(a\s+)?disabled\s+(person|people|individual|individuals|child|children|man|woman|student|students|employee|employees|worker|workers)\b/i,
173
+ suggestion: (m) => `${m[1] || ''}${m[2]} with a disability`,
174
+ reason: 'Use person-first language: "person with a disability" rather than "disabled person".',
175
+ }),
176
+ createRegexRule({
177
+ id: 'IL_handicapped',
178
+ category: 'style',
179
+ pattern: /\b(the\s+)?handicapped\b/i,
180
+ suggestion: 'people with disabilities',
181
+ reason:
182
+ '"Handicapped" is outdated. Use "people with disabilities" or "accessible" (for facilities).',
183
+ }),
184
+ createRegexRule({
185
+ id: 'IL_wheelchair_bound',
186
+ category: 'style',
187
+ pattern: /\b(wheelchair[\s-]bound|confined\s+to\s+a\s+wheelchair)\b/i,
188
+ suggestion: 'uses a wheelchair',
189
+ reason: 'People are not "bound" or "confined" by wheelchairs. Say "uses a wheelchair".',
190
+ }),
191
+
192
+ // ═══ Ableist Metaphors (IL_004) ═══
193
+ createRegexRule({
194
+ id: 'IL_lame_excuse',
195
+ category: 'style',
196
+ pattern: /\blame\s+(excuse|argument|reason|attempt|joke|effort)\b/i,
197
+ suggestion: (m) => `weak ${m[1]}`,
198
+ reason:
199
+ '"Lame" as a pejorative derives from disability language. Use "weak", "poor", or "unconvincing".',
200
+ }),
201
+ createRegexRule({
202
+ id: 'IL_crippled_by',
203
+ category: 'style',
204
+ pattern: /\bcrippled\s+by\b/i,
205
+ suggestion: 'severely affected by',
206
+ reason:
207
+ '"Crippled" as a metaphor is ableist. Use "severely affected by", "hampered by", or "hindered by".',
208
+ }),
209
+ createRegexRule({
210
+ id: 'IL_suffers_from',
211
+ category: 'style',
212
+ pattern:
213
+ /\b(suffers?|suffering)\s+from\s+(autism|ADHD|depression|anxiety|dyslexia|epilepsy|diabetes|asthma|cancer|a\s+disability)\b/i,
214
+ suggestion: (m) => `has ${m[2]}`,
215
+ reason: '"Suffers from" implies victimhood. Use "has" or "lives with" for neutral framing.',
216
+ }),
217
+ createRegexRule({
218
+ id: 'IL_tone_deaf',
219
+ category: 'style',
220
+ pattern: /\btone[\s-]deaf\b(?!\s+(person|singer|musician))/i,
221
+ suggestion: 'insensitive',
222
+ reason:
223
+ '"Tone-deaf" as a metaphor for insensitivity can be ableist. Use "insensitive" or "out of touch".',
224
+ }),
225
+ createRegexRule({
226
+ id: 'IL_turn_blind',
227
+ category: 'style',
228
+ pattern: /\bturn(ing|ed)?\s+a\s+blind\s+eye\b/i,
229
+ suggestion: 'ignoring',
230
+ reason: '"Turning a blind eye" can be considered ableist. Use "ignoring" or "overlooking".',
231
+ }),
232
+ createRegexRule({
233
+ id: 'IL_falling_deaf',
234
+ category: 'style',
235
+ pattern: /\bfalling\s+on\s+deaf\s+ears\b/i,
236
+ suggestion: 'being ignored',
237
+ reason:
238
+ '"Falling on deaf ears" can be considered ableist. Use "being ignored" or "not being heard".',
239
+ }),
240
+ createRegexRule({
241
+ id: 'IL_crazy',
242
+ category: 'style',
243
+ pattern: /\b(that's|it's|this\s+is|how)\s+crazy\b/i,
244
+ suggestion: (m) => `${m[1]} wild`,
245
+ reason:
246
+ '"Crazy" as an intensifier trivializes mental health. Use "wild", "incredible", or "unbelievable".',
247
+ }),
248
+ createRegexRule({
249
+ id: 'IL_insane',
250
+ category: 'style',
251
+ pattern: /\b(that's|it's|this\s+is|how)\s+insane\b/i,
252
+ suggestion: (m) => `${m[1]} unbelievable`,
253
+ reason:
254
+ '"Insane" as an intensifier trivializes mental health. Use "unbelievable" or "extraordinary".',
255
+ }),
256
+
257
+ // ═══ Age-Related (IL_005) ═══
258
+ createRegexRule({
259
+ id: 'IL_elderly',
260
+ category: 'style',
261
+ pattern: /\b(the\s+)?elderly\b/i,
262
+ suggestion: 'older adults',
263
+ reason: '"Elderly" can be patronizing. Use "older adults" or "older people".',
264
+ }),
265
+ createRegexRule({
266
+ id: 'IL_senior_citizen',
267
+ category: 'style',
268
+ pattern: /\bsenior\s+citizen(s)?\b/i,
269
+ suggestion: (m) => (m[1] ? 'older adults' : 'older adult'),
270
+ reason: '"Senior citizen" is outdated. Use "older adult(s)".',
271
+ }),
272
+ createRegexRule({
273
+ id: 'IL_young_man',
274
+ category: 'style',
275
+ pattern: /\byoung\s+(lady|girl)\b/i,
276
+ suggestion: 'young woman',
277
+ reason:
278
+ '"Young lady/girl" can be patronizing when referring to adult women. Use "young woman".',
279
+ }),
280
+ ];