opengrammar-server 2.0.644277
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.npm.md +95 -0
- package/bin/opengrammar-server.js +111 -0
- package/dist/server.js +48639 -0
- package/package.json +80 -0
- package/server-node.ts +159 -0
- package/server.ts +15 -0
- package/src/analyzer.ts +542 -0
- package/src/dictionary.ts +1973 -0
- package/src/index.ts +978 -0
- package/src/nlp/nlp-engine.ts +17 -0
- package/src/nlp/tone-analyzer.ts +269 -0
- package/src/rephraser.ts +146 -0
- package/src/rules/categories/academic-writing.ts +182 -0
- package/src/rules/categories/adjectives-adverbs.ts +152 -0
- package/src/rules/categories/articles.ts +160 -0
- package/src/rules/categories/business-writing.ts +250 -0
- package/src/rules/categories/capitalization.ts +79 -0
- package/src/rules/categories/clarity.ts +117 -0
- package/src/rules/categories/common-errors.ts +601 -0
- package/src/rules/categories/confused-words.ts +219 -0
- package/src/rules/categories/conjunctions.ts +176 -0
- package/src/rules/categories/dangling-modifiers.ts +123 -0
- package/src/rules/categories/formality.ts +274 -0
- package/src/rules/categories/formatting-idioms.ts +323 -0
- package/src/rules/categories/gerund-infinitive.ts +274 -0
- package/src/rules/categories/grammar-advanced.ts +294 -0
- package/src/rules/categories/grammar.ts +286 -0
- package/src/rules/categories/inclusive-language.ts +280 -0
- package/src/rules/categories/nouns-pronouns.ts +233 -0
- package/src/rules/categories/prepositions-extended.ts +217 -0
- package/src/rules/categories/prepositions.ts +159 -0
- package/src/rules/categories/punctuation.ts +347 -0
- package/src/rules/categories/quantity-agreement.ts +200 -0
- package/src/rules/categories/readability.ts +293 -0
- package/src/rules/categories/sentence-structure.ts +100 -0
- package/src/rules/categories/spelling-advanced.ts +164 -0
- package/src/rules/categories/spelling.ts +119 -0
- package/src/rules/categories/style-tone.ts +511 -0
- package/src/rules/categories/style.ts +78 -0
- package/src/rules/categories/subject-verb-agreement.ts +201 -0
- package/src/rules/categories/tone-rules.ts +206 -0
- package/src/rules/categories/verb-tense.ts +582 -0
- package/src/rules/context-filter.ts +446 -0
- package/src/rules/index.ts +96 -0
- package/src/rules/ruleset-part1-cj-pu-sp.json +657 -0
- package/src/rules/ruleset-part1-np-ad-aa-pr.json +831 -0
- package/src/rules/ruleset-part1-ss-vt.json +907 -0
- package/src/rules/ruleset-part2-cw-st-nf.json +318 -0
- package/src/rules/ruleset-part3-aw-bw-il-rd.json +161 -0
- package/src/rules/types.ts +79 -0
- package/src/shared-types.ts +152 -0
- package/src/spellchecker.ts +418 -0
- package/tsconfig.json +25 -0
|
@@ -0,0 +1,511 @@
|
|
|
1
|
+
import type { Issue } from '../../shared-types.js';
|
|
2
|
+
import { createRegexRule, type Rule } from '../types.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* ═══════════════════════════════════════════════════
|
|
6
|
+
* Style & Tone (ST)
|
|
7
|
+
* Wordiness, hedging, filler, nominalizations, clichés
|
|
8
|
+
* ═══════════════════════════════════════════════════
|
|
9
|
+
*/
|
|
10
|
+
export const styleToneRules: Rule[] = [
|
|
11
|
+
// ═══ Redundant Pairs (ST_001) ═══
|
|
12
|
+
createRegexRule({
|
|
13
|
+
id: 'ST_RP_each_every',
|
|
14
|
+
category: 'clarity',
|
|
15
|
+
pattern: /\beach\s+and\s+every\b/i,
|
|
16
|
+
suggestion: 'every',
|
|
17
|
+
reason: '"Each and every" is redundant. Use "each" or "every".',
|
|
18
|
+
}),
|
|
19
|
+
createRegexRule({
|
|
20
|
+
id: 'ST_RP_first_foremost',
|
|
21
|
+
category: 'clarity',
|
|
22
|
+
pattern: /\bfirst\s+and\s+foremost\b/i,
|
|
23
|
+
suggestion: 'first',
|
|
24
|
+
reason: '"First and foremost" is redundant. Use "first".',
|
|
25
|
+
}),
|
|
26
|
+
createRegexRule({
|
|
27
|
+
id: 'ST_RP_null_void',
|
|
28
|
+
category: 'clarity',
|
|
29
|
+
pattern: /\bnull\s+and\s+void\b/i,
|
|
30
|
+
suggestion: 'void',
|
|
31
|
+
reason: '"Null and void" is redundant in general writing. Use "void" or "invalid".',
|
|
32
|
+
}),
|
|
33
|
+
createRegexRule({
|
|
34
|
+
id: 'ST_RP_true_accurate',
|
|
35
|
+
category: 'clarity',
|
|
36
|
+
pattern: /\btrue\s+and\s+accurate\b/i,
|
|
37
|
+
suggestion: 'accurate',
|
|
38
|
+
reason: '"True and accurate" is redundant. Use "accurate".',
|
|
39
|
+
}),
|
|
40
|
+
createRegexRule({
|
|
41
|
+
id: 'ST_RP_any_all',
|
|
42
|
+
category: 'clarity',
|
|
43
|
+
pattern: /\bany\s+and\s+all\b/i,
|
|
44
|
+
suggestion: 'all',
|
|
45
|
+
reason: '"Any and all" is redundant. Use "all" or "any".',
|
|
46
|
+
}),
|
|
47
|
+
createRegexRule({
|
|
48
|
+
id: 'ST_RP_full_complete',
|
|
49
|
+
category: 'clarity',
|
|
50
|
+
pattern: /\bfull\s+and\s+complete\b/i,
|
|
51
|
+
suggestion: 'complete',
|
|
52
|
+
reason: '"Full and complete" is redundant. Use "complete".',
|
|
53
|
+
}),
|
|
54
|
+
createRegexRule({
|
|
55
|
+
id: 'ST_RP_aid_abet',
|
|
56
|
+
category: 'clarity',
|
|
57
|
+
pattern: /\baid\s+and\s+abet\b/i,
|
|
58
|
+
suggestion: 'aid',
|
|
59
|
+
reason: '"Aid and abet" is a legal doublet. In general writing, use "aid" or "help".',
|
|
60
|
+
}),
|
|
61
|
+
createRegexRule({
|
|
62
|
+
id: 'ST_RP_cease_desist',
|
|
63
|
+
category: 'clarity',
|
|
64
|
+
pattern: /\bcease\s+and\s+desist\b/i,
|
|
65
|
+
suggestion: 'cease',
|
|
66
|
+
reason: '"Cease and desist" is a legal doublet. In general writing, use "stop".',
|
|
67
|
+
}),
|
|
68
|
+
createRegexRule({
|
|
69
|
+
id: 'ST_RP_one_only',
|
|
70
|
+
category: 'clarity',
|
|
71
|
+
pattern: /\bone\s+and\s+only\b/i,
|
|
72
|
+
suggestion: 'only',
|
|
73
|
+
reason: '"One and only" is redundant outside emphasis.',
|
|
74
|
+
}),
|
|
75
|
+
createRegexRule({
|
|
76
|
+
id: 'ST_RP_hopes_dreams',
|
|
77
|
+
category: 'clarity',
|
|
78
|
+
pattern: /\bhopes\s+and\s+dreams\b/i,
|
|
79
|
+
suggestion: 'aspirations',
|
|
80
|
+
reason: '"Hopes and dreams" is a cliché. Consider "aspirations" or "ambitions".',
|
|
81
|
+
}),
|
|
82
|
+
createRegexRule({
|
|
83
|
+
id: 'ST_RP_basic_fundamental',
|
|
84
|
+
category: 'clarity',
|
|
85
|
+
pattern: /\bbasic\s+and\s+fundamental\b/i,
|
|
86
|
+
suggestion: 'fundamental',
|
|
87
|
+
reason: '"Basic and fundamental" is redundant.',
|
|
88
|
+
}),
|
|
89
|
+
createRegexRule({
|
|
90
|
+
id: 'ST_RP_plain_simple',
|
|
91
|
+
category: 'clarity',
|
|
92
|
+
pattern: /\bplain\s+and\s+simple\b/i,
|
|
93
|
+
suggestion: 'simply',
|
|
94
|
+
reason: '"Plain and simple" is redundant. Use "simply".',
|
|
95
|
+
}),
|
|
96
|
+
|
|
97
|
+
// ═══ Nominalizations — Hidden Verbs (ST_005) ═══
|
|
98
|
+
createRegexRule({
|
|
99
|
+
id: 'ST_NOM_decision',
|
|
100
|
+
category: 'clarity',
|
|
101
|
+
pattern: /\bmake\s+a\s+decision\b/i,
|
|
102
|
+
suggestion: 'decide',
|
|
103
|
+
reason: 'Nominalizations hide the action. Use "decide" instead of "make a decision".',
|
|
104
|
+
}),
|
|
105
|
+
createRegexRule({
|
|
106
|
+
id: 'ST_NOM_consideration',
|
|
107
|
+
category: 'clarity',
|
|
108
|
+
pattern: /\bgive\s+consideration\s+to\b/i,
|
|
109
|
+
suggestion: 'consider',
|
|
110
|
+
reason: 'Use "consider" instead of "give consideration to".',
|
|
111
|
+
}),
|
|
112
|
+
createRegexRule({
|
|
113
|
+
id: 'ST_NOM_improvement',
|
|
114
|
+
category: 'clarity',
|
|
115
|
+
pattern: /\bmake\s+an?\s+improvement\b/i,
|
|
116
|
+
suggestion: 'improve',
|
|
117
|
+
reason: 'Use "improve" instead of "make an improvement".',
|
|
118
|
+
}),
|
|
119
|
+
createRegexRule({
|
|
120
|
+
id: 'ST_NOM_assistance',
|
|
121
|
+
category: 'clarity',
|
|
122
|
+
pattern: /\bprovide\s+assistance\b/i,
|
|
123
|
+
suggestion: 'assist',
|
|
124
|
+
reason: 'Use "assist" or "help" instead of "provide assistance".',
|
|
125
|
+
}),
|
|
126
|
+
createRegexRule({
|
|
127
|
+
id: 'ST_NOM_investigation',
|
|
128
|
+
category: 'clarity',
|
|
129
|
+
pattern: /\bconduct\s+an?\s+investigation\b/i,
|
|
130
|
+
suggestion: 'investigate',
|
|
131
|
+
reason: 'Use "investigate" instead of "conduct an investigation".',
|
|
132
|
+
}),
|
|
133
|
+
createRegexRule({
|
|
134
|
+
id: 'ST_NOM_analysis',
|
|
135
|
+
category: 'clarity',
|
|
136
|
+
pattern: /\bperform\s+an?\s+analysis\b/i,
|
|
137
|
+
suggestion: 'analyze',
|
|
138
|
+
reason: 'Use "analyze" instead of "perform an analysis".',
|
|
139
|
+
}),
|
|
140
|
+
createRegexRule({
|
|
141
|
+
id: 'ST_NOM_discussion',
|
|
142
|
+
category: 'clarity',
|
|
143
|
+
pattern: /\bhave\s+a\s+discussion\b/i,
|
|
144
|
+
suggestion: 'discuss',
|
|
145
|
+
reason: 'Use "discuss" instead of "have a discussion".',
|
|
146
|
+
}),
|
|
147
|
+
createRegexRule({
|
|
148
|
+
id: 'ST_NOM_recommendation',
|
|
149
|
+
category: 'clarity',
|
|
150
|
+
pattern: /\bmake\s+a\s+recommendation\b/i,
|
|
151
|
+
suggestion: 'recommend',
|
|
152
|
+
reason: 'Use "recommend" instead of "make a recommendation".',
|
|
153
|
+
}),
|
|
154
|
+
createRegexRule({
|
|
155
|
+
id: 'ST_NOM_indication',
|
|
156
|
+
category: 'clarity',
|
|
157
|
+
pattern: /\bgive\s+an?\s+indication\b/i,
|
|
158
|
+
suggestion: 'indicate',
|
|
159
|
+
reason: 'Use "indicate" instead of "give an indication".',
|
|
160
|
+
}),
|
|
161
|
+
createRegexRule({
|
|
162
|
+
id: 'ST_NOM_explanation',
|
|
163
|
+
category: 'clarity',
|
|
164
|
+
pattern: /\bprovide\s+an?\s+explanation\b/i,
|
|
165
|
+
suggestion: 'explain',
|
|
166
|
+
reason: 'Use "explain" instead of "provide an explanation".',
|
|
167
|
+
}),
|
|
168
|
+
createRegexRule({
|
|
169
|
+
id: 'ST_NOM_determination',
|
|
170
|
+
category: 'clarity',
|
|
171
|
+
pattern: /\bmake\s+a\s+determination\b/i,
|
|
172
|
+
suggestion: 'determine',
|
|
173
|
+
reason: 'Use "determine" instead of "make a determination".',
|
|
174
|
+
}),
|
|
175
|
+
createRegexRule({
|
|
176
|
+
id: 'ST_NOM_presentation',
|
|
177
|
+
category: 'clarity',
|
|
178
|
+
pattern: /\bgive\s+a\s+presentation\b/i,
|
|
179
|
+
suggestion: 'present',
|
|
180
|
+
reason: 'Use "present" instead of "give a presentation".',
|
|
181
|
+
}),
|
|
182
|
+
createRegexRule({
|
|
183
|
+
id: 'ST_NOM_measurement',
|
|
184
|
+
category: 'clarity',
|
|
185
|
+
pattern: /\btake\s+a\s+measurement\b/i,
|
|
186
|
+
suggestion: 'measure',
|
|
187
|
+
reason: 'Use "measure" instead of "take a measurement".',
|
|
188
|
+
}),
|
|
189
|
+
createRegexRule({
|
|
190
|
+
id: 'ST_NOM_commitment',
|
|
191
|
+
category: 'clarity',
|
|
192
|
+
pattern: /\bmake\s+a\s+commitment\b/i,
|
|
193
|
+
suggestion: 'commit',
|
|
194
|
+
reason: 'Use "commit" instead of "make a commitment".',
|
|
195
|
+
}),
|
|
196
|
+
createRegexRule({
|
|
197
|
+
id: 'ST_NOM_adjustment',
|
|
198
|
+
category: 'clarity',
|
|
199
|
+
pattern: /\bmake\s+an?\s+adjustment\b/i,
|
|
200
|
+
suggestion: 'adjust',
|
|
201
|
+
reason: 'Use "adjust" instead of "make an adjustment".',
|
|
202
|
+
}),
|
|
203
|
+
createRegexRule({
|
|
204
|
+
id: 'ST_NOM_observation',
|
|
205
|
+
category: 'clarity',
|
|
206
|
+
pattern: /\bmake\s+an?\s+observation\b/i,
|
|
207
|
+
suggestion: 'observe',
|
|
208
|
+
reason: 'Use "observe" instead of "make an observation".',
|
|
209
|
+
}),
|
|
210
|
+
createRegexRule({
|
|
211
|
+
id: 'ST_NOM_contribution',
|
|
212
|
+
category: 'clarity',
|
|
213
|
+
pattern: /\bmake\s+a\s+contribution\b/i,
|
|
214
|
+
suggestion: 'contribute',
|
|
215
|
+
reason: 'Use "contribute" instead of "make a contribution".',
|
|
216
|
+
}),
|
|
217
|
+
createRegexRule({
|
|
218
|
+
id: 'ST_NOM_selection',
|
|
219
|
+
category: 'clarity',
|
|
220
|
+
pattern: /\bmake\s+a\s+selection\b/i,
|
|
221
|
+
suggestion: 'select',
|
|
222
|
+
reason: 'Use "select" or "choose" instead of "make a selection".',
|
|
223
|
+
}),
|
|
224
|
+
createRegexRule({
|
|
225
|
+
id: 'ST_NOM_comparison',
|
|
226
|
+
category: 'clarity',
|
|
227
|
+
pattern: /\bmake\s+a\s+comparison\b/i,
|
|
228
|
+
suggestion: 'compare',
|
|
229
|
+
reason: 'Use "compare" instead of "make a comparison".',
|
|
230
|
+
}),
|
|
231
|
+
createRegexRule({
|
|
232
|
+
id: 'ST_NOM_assessment',
|
|
233
|
+
category: 'clarity',
|
|
234
|
+
pattern: /\bconduct\s+an?\s+assessment\b/i,
|
|
235
|
+
suggestion: 'assess',
|
|
236
|
+
reason: 'Use "assess" instead of "conduct an assessment".',
|
|
237
|
+
}),
|
|
238
|
+
createRegexRule({
|
|
239
|
+
id: 'ST_NOM_review',
|
|
240
|
+
category: 'clarity',
|
|
241
|
+
pattern: /\bconduct\s+a\s+review\b/i,
|
|
242
|
+
suggestion: 'review',
|
|
243
|
+
reason: 'Use "review" instead of "conduct a review".',
|
|
244
|
+
}),
|
|
245
|
+
|
|
246
|
+
// ═══ Business Clichés (ST_006) ═══
|
|
247
|
+
createRegexRule({
|
|
248
|
+
id: 'ST_CLI_think_box',
|
|
249
|
+
category: 'style',
|
|
250
|
+
pattern: /\bthink\s+outside\s+the\s+box\b/i,
|
|
251
|
+
suggestion: 'be creative',
|
|
252
|
+
reason: '"Think outside the box" is an overused cliché. Try "be creative" or "innovate".',
|
|
253
|
+
}),
|
|
254
|
+
createRegexRule({
|
|
255
|
+
id: 'ST_CLI_low_hanging',
|
|
256
|
+
category: 'style',
|
|
257
|
+
pattern: /\blow[\s-]+hanging\s+fruit\b/i,
|
|
258
|
+
suggestion: 'easy wins',
|
|
259
|
+
reason: '"Low-hanging fruit" is an overused cliché. Try "easy wins" or "quick opportunities".',
|
|
260
|
+
}),
|
|
261
|
+
createRegexRule({
|
|
262
|
+
id: 'ST_CLI_move_needle',
|
|
263
|
+
category: 'style',
|
|
264
|
+
pattern: /\bmove\s+the\s+needle\b/i,
|
|
265
|
+
suggestion: 'make an impact',
|
|
266
|
+
reason: '"Move the needle" is an overused cliché. Try "make an impact" or "show progress".',
|
|
267
|
+
}),
|
|
268
|
+
createRegexRule({
|
|
269
|
+
id: 'ST_CLI_paradigm',
|
|
270
|
+
category: 'style',
|
|
271
|
+
pattern: /\bparadigm\s+shift\b/i,
|
|
272
|
+
suggestion: 'fundamental change',
|
|
273
|
+
reason: '"Paradigm shift" is overused. Try "fundamental change" or "transformation".',
|
|
274
|
+
}),
|
|
275
|
+
createRegexRule({
|
|
276
|
+
id: 'ST_CLI_next_level',
|
|
277
|
+
category: 'style',
|
|
278
|
+
pattern: /\btake\s+it\s+to\s+the\s+next\s+level\b/i,
|
|
279
|
+
suggestion: 'improve significantly',
|
|
280
|
+
reason: '"Take it to the next level" is a cliché. Be specific about the improvement.',
|
|
281
|
+
}),
|
|
282
|
+
createRegexRule({
|
|
283
|
+
id: 'ST_CLI_game_changer',
|
|
284
|
+
category: 'style',
|
|
285
|
+
pattern: /\bgame[\s-]+changer\b/i,
|
|
286
|
+
suggestion: 'breakthrough',
|
|
287
|
+
reason: '"Game-changer" is overused. Try "breakthrough" or "transformative development".',
|
|
288
|
+
}),
|
|
289
|
+
createRegexRule({
|
|
290
|
+
id: 'ST_CLI_deep_dive',
|
|
291
|
+
category: 'style',
|
|
292
|
+
pattern: /\bdeep\s+dive\b/i,
|
|
293
|
+
suggestion: 'thorough analysis',
|
|
294
|
+
reason: '"Deep dive" is an overused cliché. Try "thorough analysis" or "detailed examination".',
|
|
295
|
+
}),
|
|
296
|
+
createRegexRule({
|
|
297
|
+
id: 'ST_CLI_circle_back',
|
|
298
|
+
category: 'style',
|
|
299
|
+
pattern: /\bcircle\s+back\b/i,
|
|
300
|
+
suggestion: 'revisit',
|
|
301
|
+
reason: '"Circle back" is business jargon. Try "revisit" or "return to".',
|
|
302
|
+
}),
|
|
303
|
+
createRegexRule({
|
|
304
|
+
id: 'ST_CLI_touch_base',
|
|
305
|
+
category: 'style',
|
|
306
|
+
pattern: /\btouch\s+base\b/i,
|
|
307
|
+
suggestion: 'connect',
|
|
308
|
+
reason: '"Touch base" is business jargon. Try "connect" or "check in".',
|
|
309
|
+
}),
|
|
310
|
+
createRegexRule({
|
|
311
|
+
id: 'ST_CLI_same_page',
|
|
312
|
+
category: 'style',
|
|
313
|
+
pattern: /\bon\s+the\s+same\s+page\b/i,
|
|
314
|
+
suggestion: 'in agreement',
|
|
315
|
+
reason: '"On the same page" is a cliché. Try "in agreement" or "aligned".',
|
|
316
|
+
}),
|
|
317
|
+
createRegexRule({
|
|
318
|
+
id: 'ST_CLI_push_envelope',
|
|
319
|
+
category: 'style',
|
|
320
|
+
pattern: /\bpush\s+the\s+envelope\b/i,
|
|
321
|
+
suggestion: 'push boundaries',
|
|
322
|
+
reason: '"Push the envelope" is a cliché. Try "push boundaries" or "innovate".',
|
|
323
|
+
}),
|
|
324
|
+
createRegexRule({
|
|
325
|
+
id: 'ST_CLI_going_forward',
|
|
326
|
+
category: 'style',
|
|
327
|
+
pattern: /\bgoing\s+forward\b/i,
|
|
328
|
+
suggestion: 'from now on',
|
|
329
|
+
reason: '"Going forward" is overused. Try "from now on", "in the future", or simply delete it.',
|
|
330
|
+
}),
|
|
331
|
+
createRegexRule({
|
|
332
|
+
id: 'ST_CLI_leverage_verb',
|
|
333
|
+
category: 'style',
|
|
334
|
+
pattern: /\bleverage\s+(the|our|your|this|that|these|those|existing|current)\b/i,
|
|
335
|
+
suggestion: (m) => `use ${m[1]}`,
|
|
336
|
+
reason: '"Leverage" as a verb is business jargon. Try "use", "employ", or "take advantage of".',
|
|
337
|
+
}),
|
|
338
|
+
createRegexRule({
|
|
339
|
+
id: 'ST_CLI_drill_down',
|
|
340
|
+
category: 'style',
|
|
341
|
+
pattern: /\bdrill\s+down\b/i,
|
|
342
|
+
suggestion: 'examine closely',
|
|
343
|
+
reason: '"Drill down" is business jargon. Try "examine closely" or "analyze in detail".',
|
|
344
|
+
}),
|
|
345
|
+
createRegexRule({
|
|
346
|
+
id: 'ST_CLI_bandwidth',
|
|
347
|
+
category: 'style',
|
|
348
|
+
pattern:
|
|
349
|
+
/\b(have|has|had|no|enough|more)\s+bandwidth\b(?!\s*(of|to\s+handle\s+data|to\s+transfer|speed|Mbps|Gbps|rate))/i,
|
|
350
|
+
suggestion: (m) => `${m[1]} capacity`,
|
|
351
|
+
reason:
|
|
352
|
+
'Using "bandwidth" for personal capacity is jargon. Try "capacity", "time", or "resources".',
|
|
353
|
+
}),
|
|
354
|
+
createRegexRule({
|
|
355
|
+
id: 'ST_CLI_value_add',
|
|
356
|
+
category: 'style',
|
|
357
|
+
pattern: /\bvalue[\s-]+add\b/i,
|
|
358
|
+
suggestion: 'benefit',
|
|
359
|
+
reason: '"Value-add" is business jargon. Try "benefit" or "advantage".',
|
|
360
|
+
}),
|
|
361
|
+
createRegexRule({
|
|
362
|
+
id: 'ST_CLI_end_of_day',
|
|
363
|
+
category: 'style',
|
|
364
|
+
pattern: /\bat\s+the\s+end\s+of\s+the\s+day\b/i,
|
|
365
|
+
suggestion: 'ultimately',
|
|
366
|
+
reason: '"At the end of the day" is a cliché. Try "ultimately" or "in conclusion".',
|
|
367
|
+
}),
|
|
368
|
+
createRegexRule({
|
|
369
|
+
id: 'ST_CLI_actionable',
|
|
370
|
+
category: 'style',
|
|
371
|
+
pattern:
|
|
372
|
+
/\bactionable\s+(insights?|items?|steps?|takeaways?|recommendations?|feedback|data|intelligence|information|strategies?)\b/i,
|
|
373
|
+
suggestion: (m) => `practical ${m[1]}`,
|
|
374
|
+
reason: '"Actionable" is overused business jargon. Try "practical" or "useful".',
|
|
375
|
+
}),
|
|
376
|
+
|
|
377
|
+
// ═══ Inflated / Filler Phrases (ST_002 + ST_004) ═══
|
|
378
|
+
createRegexRule({
|
|
379
|
+
id: 'ST_INF_goes_without',
|
|
380
|
+
category: 'clarity',
|
|
381
|
+
pattern: /\bit\s+goes\s+without\s+saying\b/i,
|
|
382
|
+
suggestion: '',
|
|
383
|
+
reason: "If it goes without saying, don't say it! Remove this filler.",
|
|
384
|
+
}),
|
|
385
|
+
createRegexRule({
|
|
386
|
+
id: 'ST_INF_needless',
|
|
387
|
+
category: 'clarity',
|
|
388
|
+
pattern: /\bneedless\s+to\s+say\b/i,
|
|
389
|
+
suggestion: '',
|
|
390
|
+
reason: "If it's needless to say, just say it directly. Remove this filler.",
|
|
391
|
+
}),
|
|
392
|
+
createRegexRule({
|
|
393
|
+
id: 'ST_INF_worth_noting',
|
|
394
|
+
category: 'clarity',
|
|
395
|
+
pattern: /\bit\s+is\s+worth\s+noting\s+that\b/i,
|
|
396
|
+
suggestion: '',
|
|
397
|
+
reason: '"It is worth noting that" is filler. State the point directly.',
|
|
398
|
+
}),
|
|
399
|
+
createRegexRule({
|
|
400
|
+
id: 'ST_INF_matter_fact',
|
|
401
|
+
category: 'clarity',
|
|
402
|
+
pattern: /\bas\s+a\s+matter\s+of\s+fact\b/i,
|
|
403
|
+
suggestion: 'in fact',
|
|
404
|
+
reason: '"As a matter of fact" is wordy. Use "in fact" or "actually".',
|
|
405
|
+
}),
|
|
406
|
+
createRegexRule({
|
|
407
|
+
id: 'ST_INF_final_analysis',
|
|
408
|
+
category: 'clarity',
|
|
409
|
+
pattern: /\bin\s+the\s+final\s+analysis\b/i,
|
|
410
|
+
suggestion: 'finally',
|
|
411
|
+
reason: '"In the final analysis" is wordy. Use "finally" or "ultimately".',
|
|
412
|
+
}),
|
|
413
|
+
createRegexRule({
|
|
414
|
+
id: 'ST_INF_point_fact',
|
|
415
|
+
category: 'clarity',
|
|
416
|
+
pattern: /\bas\s+a\s+point\s+of\s+fact\b/i,
|
|
417
|
+
suggestion: 'in fact',
|
|
418
|
+
reason: '"As a point of fact" is wordy. Use "in fact".',
|
|
419
|
+
}),
|
|
420
|
+
createRegexRule({
|
|
421
|
+
id: 'ST_INF_close_proximity',
|
|
422
|
+
category: 'clarity',
|
|
423
|
+
pattern: /\bclose\s+proximity\b/i,
|
|
424
|
+
suggestion: 'near',
|
|
425
|
+
reason: '"Close proximity" is redundant. "Proximity" already means nearness. Use "near".',
|
|
426
|
+
}),
|
|
427
|
+
createRegexRule({
|
|
428
|
+
id: 'ST_INF_future_plans',
|
|
429
|
+
category: 'clarity',
|
|
430
|
+
pattern: /\bfuture\s+plans\b/i,
|
|
431
|
+
suggestion: 'plans',
|
|
432
|
+
reason: '"Future plans" is redundant — all plans are for the future.',
|
|
433
|
+
}),
|
|
434
|
+
createRegexRule({
|
|
435
|
+
id: 'ST_INF_past_history',
|
|
436
|
+
category: 'clarity',
|
|
437
|
+
pattern: /\bpast\s+history\b/i,
|
|
438
|
+
suggestion: 'history',
|
|
439
|
+
reason: '"Past history" is redundant — history is always in the past.',
|
|
440
|
+
}),
|
|
441
|
+
createRegexRule({
|
|
442
|
+
id: 'ST_INF_new_innovation',
|
|
443
|
+
category: 'clarity',
|
|
444
|
+
pattern: /\bnew\s+innovation\b/i,
|
|
445
|
+
suggestion: 'innovation',
|
|
446
|
+
reason: '"New innovation" is redundant — innovations are inherently new.',
|
|
447
|
+
}),
|
|
448
|
+
createRegexRule({
|
|
449
|
+
id: 'ST_INF_unexpected_surprise',
|
|
450
|
+
category: 'clarity',
|
|
451
|
+
pattern: /\bunexpected\s+surprise\b/i,
|
|
452
|
+
suggestion: 'surprise',
|
|
453
|
+
reason: '"Unexpected surprise" is redundant — surprises are unexpected by definition.',
|
|
454
|
+
}),
|
|
455
|
+
createRegexRule({
|
|
456
|
+
id: 'ST_INF_free_gift',
|
|
457
|
+
category: 'clarity',
|
|
458
|
+
pattern: /\bfree\s+gift\b/i,
|
|
459
|
+
suggestion: 'gift',
|
|
460
|
+
reason: '"Free gift" is redundant — a gift is free by definition.',
|
|
461
|
+
}),
|
|
462
|
+
createRegexRule({
|
|
463
|
+
id: 'ST_INF_advance_warning',
|
|
464
|
+
category: 'clarity',
|
|
465
|
+
pattern: /\badvance\s+warning\b/i,
|
|
466
|
+
suggestion: 'warning',
|
|
467
|
+
reason: '"Advance warning" is redundant — a warning is given in advance.',
|
|
468
|
+
}),
|
|
469
|
+
createRegexRule({
|
|
470
|
+
id: 'ST_INF_added_bonus',
|
|
471
|
+
category: 'clarity',
|
|
472
|
+
pattern: /\badded\s+bonus\b/i,
|
|
473
|
+
suggestion: 'bonus',
|
|
474
|
+
reason: '"Added bonus" is redundant — a bonus is an addition.',
|
|
475
|
+
}),
|
|
476
|
+
createRegexRule({
|
|
477
|
+
id: 'ST_INF_completely_eliminate',
|
|
478
|
+
category: 'clarity',
|
|
479
|
+
pattern: /\bcompletely\s+eliminate\b/i,
|
|
480
|
+
suggestion: 'eliminate',
|
|
481
|
+
reason: '"Completely eliminate" is redundant — elimination is total by nature.',
|
|
482
|
+
}),
|
|
483
|
+
createRegexRule({
|
|
484
|
+
id: 'ST_INF_final_outcome',
|
|
485
|
+
category: 'clarity',
|
|
486
|
+
pattern: /\bfinal\s+outcome\b/i,
|
|
487
|
+
suggestion: 'outcome',
|
|
488
|
+
reason: '"Final outcome" is redundant.',
|
|
489
|
+
}),
|
|
490
|
+
createRegexRule({
|
|
491
|
+
id: 'ST_INF_joint_collaboration',
|
|
492
|
+
category: 'clarity',
|
|
493
|
+
pattern: /\bjoint\s+collaboration\b/i,
|
|
494
|
+
suggestion: 'collaboration',
|
|
495
|
+
reason: '"Joint collaboration" is redundant — collaboration already implies working together.',
|
|
496
|
+
}),
|
|
497
|
+
createRegexRule({
|
|
498
|
+
id: 'ST_INF_past_experience',
|
|
499
|
+
category: 'clarity',
|
|
500
|
+
pattern: /\bpast\s+experience\b/i,
|
|
501
|
+
suggestion: 'experience',
|
|
502
|
+
reason: '"Past experience" is redundant — experience is always from the past.',
|
|
503
|
+
}),
|
|
504
|
+
createRegexRule({
|
|
505
|
+
id: 'ST_INF_brief_summary',
|
|
506
|
+
category: 'clarity',
|
|
507
|
+
pattern: /\bbrief\s+summary\b/i,
|
|
508
|
+
suggestion: 'summary',
|
|
509
|
+
reason: '"Brief summary" is redundant — summaries are inherently brief.',
|
|
510
|
+
}),
|
|
511
|
+
];
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import type { Issue } from '../../shared-types.js';
|
|
2
|
+
import { createRegexRule, type Rule } from '../types.js';
|
|
3
|
+
|
|
4
|
+
export const styleRules: Rule[] = [
|
|
5
|
+
// Passive Voice via Regex (legacy)
|
|
6
|
+
createRegexRule({
|
|
7
|
+
id: 'passive-voice-ed',
|
|
8
|
+
category: 'style',
|
|
9
|
+
pattern: /\b(am|are|is|was|were|be|been|being)\s+(\w+ed)\b/i,
|
|
10
|
+
suggestion: 'Consider using active voice',
|
|
11
|
+
reason: 'Passive voice can make sentences weaker and less direct.',
|
|
12
|
+
}),
|
|
13
|
+
createRegexRule({
|
|
14
|
+
id: 'passive-voice-en',
|
|
15
|
+
category: 'style',
|
|
16
|
+
pattern: /\b(am|are|is|was|were|be|been|being)\s+(\w+en)\b/i,
|
|
17
|
+
suggestion: 'Consider using active voice',
|
|
18
|
+
reason: 'Passive voice can make sentences weaker and less direct.',
|
|
19
|
+
}),
|
|
20
|
+
|
|
21
|
+
// NEW NLP Rule: Detect consecutive nouns (Noun String) which are hard to read
|
|
22
|
+
{
|
|
23
|
+
id: 'noun-string',
|
|
24
|
+
type: 'nlp',
|
|
25
|
+
category: 'clarity',
|
|
26
|
+
reason: 'Long strings of nouns reduce clarity. Try using prepositions to separate them.',
|
|
27
|
+
suggestion: 'Consider rephrasing',
|
|
28
|
+
check: (text: string, doc: any): Issue[] => {
|
|
29
|
+
const issues: Issue[] = [];
|
|
30
|
+
const matches = doc.match('#Noun #Noun #Noun #Noun+');
|
|
31
|
+
matches.forEach((m: any) => {
|
|
32
|
+
const str = m.text();
|
|
33
|
+
const offset = text.indexOf(str);
|
|
34
|
+
if (offset !== -1) {
|
|
35
|
+
issues.push({
|
|
36
|
+
id: `noun-string-${offset}`,
|
|
37
|
+
type: 'clarity',
|
|
38
|
+
original: str,
|
|
39
|
+
suggestion: 'Consider rephrasing',
|
|
40
|
+
reason: 'Long strings of nouns are hard to read.',
|
|
41
|
+
offset,
|
|
42
|
+
length: str.length,
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
return issues;
|
|
47
|
+
},
|
|
48
|
+
},
|
|
49
|
+
|
|
50
|
+
// NEW NLP Rule: Avoid starting sentences with coordinating conjunctions heavily
|
|
51
|
+
{
|
|
52
|
+
id: 'conjunction-start',
|
|
53
|
+
type: 'nlp',
|
|
54
|
+
category: 'style',
|
|
55
|
+
reason: 'Starting a sentence with "And" or "But" can make writing less formal.',
|
|
56
|
+
suggestion: 'Consider merging sentences or removing',
|
|
57
|
+
check: (text: string, doc: any): Issue[] => {
|
|
58
|
+
const issues: Issue[] = [];
|
|
59
|
+
const matches = doc.match('^#Conjunction');
|
|
60
|
+
matches.forEach((m: any) => {
|
|
61
|
+
const str = m.text();
|
|
62
|
+
const offset = text.indexOf(str);
|
|
63
|
+
if (offset !== -1 && (str.toLowerCase() === 'and ' || str.toLowerCase() === 'but ')) {
|
|
64
|
+
issues.push({
|
|
65
|
+
id: `conjunction-start-${offset}`,
|
|
66
|
+
type: 'style',
|
|
67
|
+
original: str.trim(),
|
|
68
|
+
suggestion: 'Additionally, / However,',
|
|
69
|
+
reason: 'Starting sentences with coordinating conjunctions is informal.',
|
|
70
|
+
offset,
|
|
71
|
+
length: str.trim().length,
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
return issues;
|
|
76
|
+
},
|
|
77
|
+
},
|
|
78
|
+
];
|