agent-simple-english 0.1.0 → 0.2.0
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/.claude-plugin/marketplace.json +2 -2
- package/.claude-plugin/plugin.json +2 -2
- package/README.md +174 -78
- package/commands/ste.md +1 -1
- package/hooks/hooks.json +1 -1
- package/package.json +13 -2
- package/src/adapter/feedback.ts +2 -1
- package/src/adapter/rule-summary.ts +46 -5
- package/src/cli/hook.ts +148 -52
- package/src/cli/main.ts +81 -9
- package/src/cli/observation-log.ts +257 -0
- package/src/cli/session-command.ts +14 -6
- package/src/cli/session-state.ts +3 -8
- package/src/cli/state-directory.ts +9 -0
- package/src/config/schema.ts +23 -0
- package/src/dictionary/README.md +61 -7
- package/src/dictionary/bundled-rule-data.ts +13 -0
- package/src/dictionary/configured.ts +16 -0
- package/src/dictionary/data/adjectival-participles.json +15 -0
- package/src/dictionary/data/hedging.json +22 -0
- package/src/dictionary/data/marketing.json +40 -0
- package/src/dictionary/data/phrasal-verbs.json +55 -0
- package/src/dictionary/form.ts +2 -0
- package/src/dictionary/load.ts +84 -6
- package/src/dictionary/rule-data.ts +12 -0
- package/src/dictionary/schema.ts +18 -1
- package/src/engine/case-fold.ts +17 -0
- package/src/engine/comments.ts +273 -23
- package/src/engine/diff-match.ts +189 -0
- package/src/engine/kinds.ts +19 -2
- package/src/engine/lint.ts +209 -238
- package/src/engine/markdown-syntax.ts +171 -0
- package/src/engine/markdown.ts +591 -245
- package/src/engine/paragraphs.ts +47 -6
- package/src/engine/phrase-matcher.ts +62 -0
- package/src/engine/rules/dictionary.ts +38 -3
- package/src/engine/rules/hedging.ts +19 -16
- package/src/engine/rules/marketing.ts +122 -56
- package/src/engine/rules/paragraph-length.ts +6 -1
- package/src/engine/rules/phrasal-verb.ts +36 -41
- package/src/engine/rules/registry.ts +1 -0
- package/src/engine/rules/verb-form.ts +26 -3
- package/src/engine/sentences.ts +359 -20
- package/src/engine/suppression.ts +174 -0
- package/src/engine/tokens.ts +2 -2
- package/src/engine/types.ts +19 -5
- package/src/extension/index.ts +63 -35
|
@@ -10,8 +10,8 @@
|
|
|
10
10
|
"plugins": [
|
|
11
11
|
{
|
|
12
12
|
"name": "simple-english",
|
|
13
|
-
"description": "Apply
|
|
14
|
-
"version": "0.
|
|
13
|
+
"description": "Apply technical and house-style writing rules to writes, edits, and git commit messages.",
|
|
14
|
+
"version": "0.2.0",
|
|
15
15
|
"author": {
|
|
16
16
|
"name": "JIA YI"
|
|
17
17
|
},
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://anthropic.com/claude-code/plugin.schema.json",
|
|
3
3
|
"name": "simple-english",
|
|
4
|
-
"version": "0.
|
|
5
|
-
"description": "Apply
|
|
4
|
+
"version": "0.2.0",
|
|
5
|
+
"description": "Apply technical and house-style writing rules to writes, edits, and git commit messages.",
|
|
6
6
|
"repository": "https://github.com/jyooi/agent-simple-english",
|
|
7
7
|
"license": "MIT",
|
|
8
8
|
"keywords": ["simplified-technical-english", "lint", "hooks"],
|
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# agent-simple-english
|
|
2
2
|
|
|
3
|
-
`agent-simple-english` checks ASD-STE100 Simplified Technical English (STE).
|
|
3
|
+
`agent-simple-english` checks rules derived from ASD-STE100 Simplified Technical English (STE) and two enabled house-style rules.
|
|
4
4
|
It supplies one Engine, one CLI, a pi Adapter, and a Claude Code Adapter.
|
|
5
5
|
The Claude Code plugin uses CLI Hook mode to enforce the same rules.
|
|
6
6
|
|
|
@@ -9,7 +9,7 @@ It does not rewrite text because an automatic rewrite can change its meaning.
|
|
|
9
9
|
|
|
10
10
|
## What the pi Adapter does
|
|
11
11
|
|
|
12
|
-
The pi Adapter adds its active
|
|
12
|
+
The pi Adapter adds its active writing rules to the model prompt before each agent turn.
|
|
13
13
|
It then applies the rules at three layers.
|
|
14
14
|
|
|
15
15
|
1. **Write and edit gate.**
|
|
@@ -33,7 +33,7 @@ It then applies the rules at three layers.
|
|
|
33
33
|
|
|
34
34
|
The pi Adapter checks Markdown prose and source comments according to the [content kinds](#content-kinds).
|
|
35
35
|
It gives the line, column, rule ID, and suggested correction for a blocked tool call.
|
|
36
|
-
A config or
|
|
36
|
+
A config, dictionary, or rule-data load error makes enabled write, edit, and commit gates fail closed.
|
|
37
37
|
|
|
38
38
|
## Install the Claude Code Adapter
|
|
39
39
|
|
|
@@ -52,7 +52,7 @@ The repository supplies the local marketplace manifest.
|
|
|
52
52
|
Marketplace publication is outside this package release.
|
|
53
53
|
|
|
54
54
|
At `SessionStart`, the Adapter loads the merged config for an enabled session.
|
|
55
|
-
It reads the config from the session working directory and adds the active
|
|
55
|
+
It reads the config from the session working directory and adds the active writing-rule summary to context.
|
|
56
56
|
The summary honors `hard`, `soft`, and `off` rule settings plus `maxSentenceWords`.
|
|
57
57
|
|
|
58
58
|
The `PreToolUse` gate checks `Write`, `Edit`, and `Bash` events.
|
|
@@ -166,8 +166,44 @@ In strict mode, it blocks a reply that has hard violations.
|
|
|
166
166
|
A `UserPromptSubmit` event adds pending feedback to context and clears that feedback.
|
|
167
167
|
Every hook reads the current session mode before it applies a gate.
|
|
168
168
|
Malformed JSON returns a non-blocking error so Claude Code can continue.
|
|
169
|
-
The hook also allows the event when configuration, dictionary, tagger, transcript, state, or file processing fails.
|
|
170
|
-
It adds warning text.
|
|
169
|
+
The hook also allows the event when configuration, dictionary, rule-data, tagger, transcript, state, or file processing fails.
|
|
170
|
+
It adds warning text for these operational failures.
|
|
171
|
+
Observation-write failures are silent and do not change the hook output or decision.
|
|
172
|
+
|
|
173
|
+
### Review hook observations
|
|
174
|
+
|
|
175
|
+
Enabled Hook mode logs every write, edit, static commit-message, and reply lint decision to the local XDG state directory.
|
|
176
|
+
Observation logging is on by default, and the log includes clean allows and soft Findings.
|
|
177
|
+
Plain lint runs, disabled hook sessions, and the pi Adapter do not write Observations.
|
|
178
|
+
Set `SIMPLE_ENGLISH_OBSERVE=0` to stop observation logging.
|
|
179
|
+
|
|
180
|
+
Monthly Observation files use `$XDG_STATE_HOME/simple-english/observations/YYYY-MM.jsonl`.
|
|
181
|
+
The default base directory is `~/.local/state`.
|
|
182
|
+
Each Finding stores its offending snippet for later review.
|
|
183
|
+
Verdicts use the separate `$XDG_STATE_HOME/simple-english/verdicts.jsonl` file.
|
|
184
|
+
These global, host-local records can contain snippets, working directories, and file paths.
|
|
185
|
+
New state directories use mode `0700`, and new JSONL files use mode `0600`.
|
|
186
|
+
Each record uses one append-mode write without a lock, so concurrent hooks can safely add complete lines.
|
|
187
|
+
|
|
188
|
+
Review each unjudged Finding:
|
|
189
|
+
|
|
190
|
+
```sh
|
|
191
|
+
simple-english observe review
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
Press `t` for a true positive or `f` for a false positive.
|
|
195
|
+
Press `s` to leave a Finding unjudged or `q` to quit.
|
|
196
|
+
A Verdict can include an optional note.
|
|
197
|
+
A false positive exists only after a human records that Verdict.
|
|
198
|
+
The latest Verdict for a Finding wins.
|
|
199
|
+
|
|
200
|
+
Show fire counts, judged counts, and false-positive rates for each rule:
|
|
201
|
+
|
|
202
|
+
```sh
|
|
203
|
+
simple-english observe stats
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
The report also shows total Observations and clean allows.
|
|
171
207
|
|
|
172
208
|
### Lint files and standard input
|
|
173
209
|
|
|
@@ -214,6 +250,7 @@ Soft violations can appear with exit code 0.
|
|
|
214
250
|
### CLI flags
|
|
215
251
|
|
|
216
252
|
- `--json` writes one JSON report with `violations` and `summary` fields.
|
|
253
|
+
Each violation includes its offending sentence or paragraph as `snippet`.
|
|
217
254
|
|
|
218
255
|
- `--config <path>` uses only that config file and disables config discovery.
|
|
219
256
|
|
|
@@ -221,6 +258,12 @@ Soft violations can appear with exit code 0.
|
|
|
221
258
|
Valid values are `prose-file`, `slash-source`, `hash-source`, and `commit-message`.
|
|
222
259
|
The form `--kind=<kind>` also works.
|
|
223
260
|
|
|
261
|
+
- `--help` writes the command usage.
|
|
262
|
+
|
|
263
|
+
- `--version` writes the package version.
|
|
264
|
+
|
|
265
|
+
- The command rejects an unknown flag as an argument error.
|
|
266
|
+
|
|
224
267
|
- A path of `-` reads standard input.
|
|
225
268
|
With no paths, the command also reads standard input.
|
|
226
269
|
|
|
@@ -238,10 +281,39 @@ It is the default for standard input, extensionless paths, and file types that h
|
|
|
238
281
|
`commit-message` checks the complete input as a commit message.
|
|
239
282
|
|
|
240
283
|
File extension matching does not depend on letter case.
|
|
241
|
-
Source kinds ignore comment markers inside string
|
|
284
|
+
Source kinds ignore comment markers inside supported string literal forms.
|
|
242
285
|
All kinds preserve the original line and column.
|
|
243
|
-
They ignore identifiers
|
|
244
|
-
|
|
286
|
+
They ignore identifiers, YAML frontmatter, valid GFM tables, and fenced, indented, and inline Markdown code.
|
|
287
|
+
|
|
288
|
+
### Inline suppression
|
|
289
|
+
|
|
290
|
+
A suppression directive names one or more registered rule IDs and applies only to the next physical line.
|
|
291
|
+
Use this Markdown comment form in prose files:
|
|
292
|
+
|
|
293
|
+
```md
|
|
294
|
+
<!-- ste-disable-next-line marketing -->
|
|
295
|
+
The robust estimator uses this sample.
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
Use the matching plain comment form in slash and hash source files:
|
|
299
|
+
|
|
300
|
+
```ts
|
|
301
|
+
// ste-disable-next-line marketing
|
|
302
|
+
// The robust estimator uses this sample.
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
```py
|
|
306
|
+
# ste-disable-next-line marketing
|
|
307
|
+
# The robust estimator uses this sample.
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
Separate multiple rule IDs with whitespace.
|
|
311
|
+
Commas are also accepted as separators.
|
|
312
|
+
A missing or unknown rule ID produces an `invalid-suppression` violation, which is hard by default.
|
|
313
|
+
|
|
314
|
+
Directive recognition uses lightweight per-language comment extraction rather than full language parsers.
|
|
315
|
+
Known accepted losses are JavaScript regular expressions inside template expressions, Ruby and Perl regular-expression literals containing directive text, Perl `<<~` heredocs, adjacent multiline YAML flow scalars, and compact nested YAML block scalars with explicit indentation.
|
|
316
|
+
Engine fixtures pin these limitations pending per-language lexers.
|
|
245
317
|
|
|
246
318
|
## Configuration
|
|
247
319
|
|
|
@@ -261,7 +333,6 @@ The global fallback is `simple-english.json` in the pi agent config directory.
|
|
|
261
333
|
The default pi agent config directory is `~/.pi/agent`.
|
|
262
334
|
`PI_CODING_AGENT_DIR` can change that directory.
|
|
263
335
|
|
|
264
|
-
The loader resolves a relative value from the working directory that requested the config.
|
|
265
336
|
The loader reads a fallback file only when the new file at the same level is absent.
|
|
266
337
|
|
|
267
338
|
This example contains every config key and every rule:
|
|
@@ -269,10 +340,19 @@ This example contains every config key and every rule:
|
|
|
269
340
|
```json
|
|
270
341
|
{
|
|
271
342
|
"maxSentenceWords": 25,
|
|
343
|
+
"exemptBlockQuotes": true,
|
|
344
|
+
"approvedWordsPath": "./approved-words.json",
|
|
345
|
+
"ruleDataExtensions": {
|
|
346
|
+
"phrasal-verb": ["config/phrasal-verbs.json"],
|
|
347
|
+
"hedging": ["config/hedging.json"],
|
|
348
|
+
"marketing": ["config/marketing.json"],
|
|
349
|
+
"adjectival-participle": ["config/adjectival-participles.json"]
|
|
350
|
+
},
|
|
272
351
|
"rules": {
|
|
273
352
|
"contraction": "hard",
|
|
274
353
|
"dictionary-not-approved-word": "hard",
|
|
275
354
|
"hedging": "soft",
|
|
355
|
+
"invalid-suppression": "hard",
|
|
276
356
|
"marketing": "soft",
|
|
277
357
|
"paragraph-length": "hard",
|
|
278
358
|
"phrasal-verb": "hard",
|
|
@@ -291,117 +371,122 @@ A soft violation produces a report or warning but does not block the action.
|
|
|
291
371
|
The `off` value disables that rule.
|
|
292
372
|
|
|
293
373
|
`maxSentenceWords` must be a positive integer and has a default value of 25.
|
|
374
|
+
`exemptBlockQuotes` must be a boolean and has a default value of `false`.
|
|
375
|
+
When it is omitted or `false`, block quotes receive the same checks as other prose.
|
|
376
|
+
When it is `true`, CommonMark block quote content is exempt from `dictionary-not-approved-word`, `contraction`, `phrasal-verb`, `hedging`, and `marketing` checks.
|
|
377
|
+
All other rules continue to check block quote content.
|
|
378
|
+
`approvedWordsPath` selects a user-owned approved-word list.
|
|
379
|
+
The loader resolves a relative path from the working directory that requested the config.
|
|
380
|
+
This list replaces the bundled not-approved sample and any `SIMPLE_ENGLISH_DICTIONARY` replacement.
|
|
381
|
+
A missing, unreadable, or invalid list causes lint exit code 2.
|
|
382
|
+
The enabled pi Adapter gates fail closed after this error.
|
|
383
|
+
Claude Code Hook mode reports a warning and allows the event, as it does for other load errors.
|
|
294
384
|
Unknown keys, unknown rule IDs, and invalid values cause a config error.
|
|
295
385
|
|
|
386
|
+
`ruleDataExtensions` maps each list-backed data set to more JSON data files.
|
|
387
|
+
The loader adds entries from these files after the bundled entries.
|
|
388
|
+
The loader resolves relative paths from the current working directory.
|
|
389
|
+
Each file must use the [package dictionary data format](src/dictionary/README.md).
|
|
390
|
+
An `adjectival-participle` extension adds exact tagged-token forms that the progressive and passive rules allow after a form of `be`.
|
|
391
|
+
A lint command reports an extension load error and continues with the bundled rule data.
|
|
392
|
+
The enabled pi Adapter fails closed after that error, while Claude Code Hook mode allows the event and adds a warning.
|
|
393
|
+
|
|
296
394
|
## Rule reference
|
|
297
395
|
|
|
298
|
-
###
|
|
396
|
+
### Rules derived from ASD-STE100
|
|
397
|
+
|
|
398
|
+
#### `contraction`
|
|
299
399
|
|
|
300
400
|
Default: hard.
|
|
301
401
|
Reports apostrophe contractions such as forms that end in `n't`, `'re`, `'ve`, `'ll`, `'d`, or `'m`.
|
|
302
402
|
It also reports unambiguous forms that end in `'s`.
|
|
303
403
|
|
|
304
|
-
|
|
404
|
+
#### `dictionary-not-approved-word`
|
|
305
405
|
|
|
306
406
|
Default: hard.
|
|
307
|
-
|
|
308
|
-
Part-of-speech data limits applicable entries when that data exists.
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
Default: soft.
|
|
314
|
-
Reports these phrases: `it is important to note`, `it should be noted`, `it is worth noting`, `please note that`, `as mentioned`, `as noted above`.
|
|
315
|
-
A phrase match stays on one source line.
|
|
407
|
+
Without an approved-word list, this rule reports forms from the bundled not-approved sample and supplies approved alternatives.
|
|
408
|
+
Part-of-speech data limits applicable sample entries when that data exists.
|
|
409
|
+
With an approved-word list, this rule reports each prose token that the list does not contain.
|
|
410
|
+
Matching uses exact surface forms without regard to case.
|
|
411
|
+
See [Configuration](#configuration) for list selection, precedence, and load errors.
|
|
316
412
|
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
Default: soft.
|
|
320
|
-
Reports the first listed term in each token.
|
|
321
|
-
Matching does not depend on letter case, and it also examines components of hyphenated tokens.
|
|
322
|
-
|
|
323
|
-
- `seamless`.
|
|
324
|
-
- `seamlessly`.
|
|
325
|
-
- `robust`.
|
|
326
|
-
- `powerful`.
|
|
327
|
-
- `cutting-edge`.
|
|
328
|
-
- `effortless`.
|
|
329
|
-
- `effortlessly`.
|
|
330
|
-
- `world-class`.
|
|
331
|
-
- `next-generation`.
|
|
332
|
-
- `revolutionary`.
|
|
333
|
-
- `blazing`.
|
|
334
|
-
- `lightning-fast`.
|
|
335
|
-
- `elegant`.
|
|
336
|
-
- `delightful`.
|
|
337
|
-
- `turnkey`.
|
|
338
|
-
- `best-in-class`.
|
|
339
|
-
- `state-of-the-art`.
|
|
340
|
-
- `game-changing`.
|
|
341
|
-
- `battle-tested`.
|
|
342
|
-
- `enterprise-grade`.
|
|
343
|
-
- `supercharge`.
|
|
344
|
-
- `unleash`.
|
|
345
|
-
- `empower`.
|
|
346
|
-
- `empowers`.
|
|
347
|
-
|
|
348
|
-
### `paragraph-length`
|
|
413
|
+
#### `paragraph-length`
|
|
349
414
|
|
|
350
415
|
Default: hard.
|
|
351
416
|
Reports a prose paragraph that has more than six sentences.
|
|
352
417
|
Markdown block boundaries and list items start separate paragraphs.
|
|
418
|
+
Sentence counts use the [`sentence-length`](#sentence-length) segmentation rules.
|
|
353
419
|
|
|
354
|
-
|
|
420
|
+
#### `phrasal-verb`
|
|
355
421
|
|
|
356
422
|
Default: hard.
|
|
357
|
-
Reports
|
|
358
|
-
|
|
359
|
-
| Forms | Suggestion |
|
|
360
|
-
| --- | --- |
|
|
361
|
-
| `carry out`, `carries out`, `carried out`, `carrying out` | `do`. |
|
|
362
|
-
| `spin up`, `spins up`, `spun up`, `spinning up` | `start`. |
|
|
363
|
-
| `spin down`, `spins down`, `spun down`, `spinning down` | `stop`. |
|
|
364
|
-
| `tear down`, `tears down`, `tore down`, `torn down`, `tearing down` | `remove`. |
|
|
365
|
-
| `reach out`, `reaches out`, `reached out`, `reaching out` | `ask`. |
|
|
366
|
-
| `dive into`, `dives into`, `dived into`, `dove into`, `diving into` | `examine`. |
|
|
367
|
-
| `kick off`, `kicks off`, `kicked off`, `kicking off` | `start`. |
|
|
368
|
-
| `roll out`, `rolls out`, `rolled out`, `rolling out` | `release`. |
|
|
369
|
-
| `ramp up`, `ramps up`, `ramped up`, `ramping up` | `increase`. |
|
|
370
|
-
| `circle back`, `circles back`, `circled back`, `circling back` | `return`. |
|
|
371
|
-
| `drill down`, `drills down`, `drilled down`, `drilling down` | `examine`. |
|
|
423
|
+
Reports listed forms and supplies their suggestion.
|
|
424
|
+
The bundled forms and suggestions are in [`src/dictionary/data/phrasal-verbs.json`](src/dictionary/data/phrasal-verbs.json).
|
|
372
425
|
|
|
373
426
|
Matching does not depend on letter case.
|
|
374
427
|
A phrase match stays on one source line.
|
|
375
428
|
|
|
376
|
-
|
|
429
|
+
#### `semicolon`
|
|
377
430
|
|
|
378
431
|
Default: hard.
|
|
379
|
-
Reports each semicolon and asks for two sentences.
|
|
380
|
-
This rule also checks semicolons inside inline Markdown code.
|
|
432
|
+
Reports each prose semicolon and asks for two sentences.
|
|
381
433
|
|
|
382
|
-
|
|
434
|
+
#### `sentence-length`
|
|
383
435
|
|
|
384
436
|
Default: hard.
|
|
385
437
|
Reports a sentence above `maxSentenceWords`.
|
|
386
438
|
The default maximum is 25 words.
|
|
439
|
+
Sentence segmentation recognizes only `e.g.`, `i.e.`, `etc.`, `vs.`, `Fig.`, `No.`, and a single capital letter followed by a period as abbreviation forms.
|
|
440
|
+
It keeps an abbreviation in the current sentence when lowercase prose follows it.
|
|
441
|
+
It preserves a sentence boundary before capitalized prose, except after a single capital letter or before a short `Fig.` or `No.` designator such as `Fig. A` or `No. 7`.
|
|
442
|
+
Markdown block boundaries stop abbreviation lookahead.
|
|
387
443
|
|
|
388
|
-
|
|
444
|
+
#### `verb-progressive`
|
|
389
445
|
|
|
390
446
|
Default: hard.
|
|
391
447
|
Reports a form of `be` followed by an `-ing` verb, with optional adverbs or `not` between them.
|
|
392
448
|
|
|
393
|
-
|
|
449
|
+
#### `verb-passive`
|
|
394
450
|
|
|
395
451
|
Default: soft.
|
|
396
452
|
Reports a form of `be` followed by a past participle, with optional adverbs or `not` between them.
|
|
397
453
|
|
|
398
|
-
|
|
454
|
+
Before either rule reports a finding, it checks the exact tagged token against the [bundled adjectival-participle allowlist](src/dictionary/data/adjectival-participles.json).
|
|
455
|
+
The `ruleDataExtensions.adjectival-participle` configuration extends that allowlist.
|
|
456
|
+
|
|
457
|
+
#### `verb-perfect`
|
|
399
458
|
|
|
400
459
|
Default: hard.
|
|
401
460
|
Reports auxiliary `have` followed by a past participle, with optional adverbs or `not` between them.
|
|
402
461
|
|
|
403
462
|
The three verb rules and applicable dictionary entries use the bundled English part-of-speech tagger.
|
|
404
463
|
|
|
464
|
+
### Directive validation
|
|
465
|
+
|
|
466
|
+
#### `invalid-suppression`
|
|
467
|
+
|
|
468
|
+
Default: hard.
|
|
469
|
+
Reports a suppression directive that has no rule ID or names an unknown rule ID.
|
|
470
|
+
|
|
471
|
+
### House-style rules
|
|
472
|
+
|
|
473
|
+
These rules define package house style.
|
|
474
|
+
They do not come from ASD-STE100.
|
|
475
|
+
The default config enables both rules.
|
|
476
|
+
|
|
477
|
+
#### `hedging`
|
|
478
|
+
|
|
479
|
+
Default: soft.
|
|
480
|
+
Reports the phrases in [`src/dictionary/data/hedging.json`](src/dictionary/data/hedging.json).
|
|
481
|
+
A phrase match stays on one source line.
|
|
482
|
+
|
|
483
|
+
#### `marketing`
|
|
484
|
+
|
|
485
|
+
Default: soft.
|
|
486
|
+
Reports complete listed forms from [`src/dictionary/data/marketing.json`](src/dictionary/data/marketing.json).
|
|
487
|
+
A multi-word form stays on one source line.
|
|
488
|
+
Matching does not depend on letter case, and the rule also reports the first listed single-token component of a hyphenated token.
|
|
489
|
+
|
|
405
490
|
## Dictionary and attribution
|
|
406
491
|
|
|
407
492
|
The package vendors dictionary data converted from Cameron Moore's MIT-licensed [`ctotheameron/pi-ste`](https://github.com/ctotheameron/pi-ste).
|
|
@@ -412,8 +497,18 @@ This package does not include the official specification or the complete ASD dic
|
|
|
412
497
|
Get the current official specification from the [ASD-STE100 site](https://www.asd-ste100.org/).
|
|
413
498
|
This project has no affiliation with or endorsement from ASD.
|
|
414
499
|
|
|
415
|
-
The package dictionary format and
|
|
416
|
-
|
|
500
|
+
The package dictionary format and token rules are in [`src/dictionary/README.md`](src/dictionary/README.md).
|
|
501
|
+
|
|
502
|
+
To create an approved-word list, use your own licensed copy of the current ASD-STE100 specification.
|
|
503
|
+
Extract the approved words into the [package approved-word list format](src/dictionary/README.md#approved-word-list).
|
|
504
|
+
Follow that format's exact surface-form requirements.
|
|
505
|
+
Add source metadata that identifies your licensed revision and extraction record.
|
|
506
|
+
Repeat the extraction and update the metadata when you adopt a new licensed revision.
|
|
507
|
+
Keep the list private unless your license lets you distribute it.
|
|
508
|
+
This package does not extract, include, or distribute that data.
|
|
509
|
+
See [Configuration](#configuration) to select the list.
|
|
510
|
+
|
|
511
|
+
Set `SIMPLE_ENGLISH_DICTIONARY` to replace only the bundled not-approved sample.
|
|
417
512
|
Hook mode resolves a relative replacement path from the session working directory.
|
|
418
513
|
A lint command reports a replacement dictionary load error and continues with all other rules.
|
|
419
514
|
The enabled pi Adapter fails closed after that error.
|
|
@@ -423,6 +518,7 @@ The enabled pi Adapter fails closed after that error.
|
|
|
423
518
|
```sh
|
|
424
519
|
bun install
|
|
425
520
|
bun run test
|
|
521
|
+
bun run bench:markdown
|
|
426
522
|
bun run lint
|
|
427
523
|
bun run typecheck
|
|
428
524
|
npm publish --dry-run
|
package/commands/ste.md
CHANGED
package/hooks/hooks.json
CHANGED
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agent-simple-english",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "Technical and house-style English lint engine, CLI, and host adapters",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"author": {
|
|
@@ -33,11 +33,22 @@
|
|
|
33
33
|
},
|
|
34
34
|
"scripts": {
|
|
35
35
|
"test": "vitest run",
|
|
36
|
+
"bench:markdown": "vitest bench test/engine/markdown.bench.ts",
|
|
36
37
|
"lint": "biome check .",
|
|
37
38
|
"typecheck": "tsc --noEmit"
|
|
38
39
|
},
|
|
39
40
|
"dependencies": {
|
|
41
|
+
"@lezer/html": "^1.3.12",
|
|
42
|
+
"@lezer/markdown": "^1.7.2",
|
|
40
43
|
"effect": "^3.14.0",
|
|
44
|
+
"micromark": "^4.0.2",
|
|
45
|
+
"micromark-extension-frontmatter": "^2.0.0",
|
|
46
|
+
"micromark-extension-gfm-table": "^2.1.1",
|
|
47
|
+
"micromark-util-character": "^2.1.1",
|
|
48
|
+
"micromark-util-html-tag-name": "^2.0.1",
|
|
49
|
+
"micromark-util-normalize-identifier": "^2.0.1",
|
|
50
|
+
"micromark-util-types": "^2.0.2",
|
|
51
|
+
"unicode-case-folding": "^1.1.1",
|
|
41
52
|
"wink-eng-lite-web-model": "^1.8.1",
|
|
42
53
|
"wink-nlp": "^2.4.0"
|
|
43
54
|
},
|
package/src/adapter/feedback.ts
CHANGED
|
@@ -8,8 +8,9 @@ function suggestedFix(violation: Violation): string {
|
|
|
8
8
|
}
|
|
9
9
|
const fixes: Readonly<Record<RuleId, string>> = {
|
|
10
10
|
contraction: "Write the contracted words in full.",
|
|
11
|
-
"dictionary-not-approved-word": "
|
|
11
|
+
"dictionary-not-approved-word": "Use a word from the approved-word list.",
|
|
12
12
|
hedging: "Delete the hedging phrase.",
|
|
13
|
+
"invalid-suppression": "Name one or more registered rule IDs.",
|
|
13
14
|
marketing: "Replace the phrase with factual language.",
|
|
14
15
|
"paragraph-length": "Split the paragraph into shorter paragraphs.",
|
|
15
16
|
"phrasal-verb": "Replace the phrasal verb with one approved verb.",
|
|
@@ -7,6 +7,7 @@ const DEFAULT_RULE_SETTINGS: Readonly<Record<RuleId, RuleSetting>> = {
|
|
|
7
7
|
contraction: "hard",
|
|
8
8
|
"dictionary-not-approved-word": "hard",
|
|
9
9
|
hedging: "soft",
|
|
10
|
+
"invalid-suppression": "hard",
|
|
10
11
|
marketing: "soft",
|
|
11
12
|
"paragraph-length": "hard",
|
|
12
13
|
"phrasal-verb": "hard",
|
|
@@ -21,6 +22,7 @@ export const RULE_SUMMARIES: Readonly<Record<RuleId, string>> = {
|
|
|
21
22
|
contraction: "Do not use contractions. Write the words in full.",
|
|
22
23
|
"dictionary-not-approved-word": "Use approved words from the STE dictionary.",
|
|
23
24
|
hedging: "Remove hedging phrases.",
|
|
25
|
+
"invalid-suppression": "Name registered rule IDs in suppression directives.",
|
|
24
26
|
marketing: "Use factual language instead of marketing language.",
|
|
25
27
|
"paragraph-length": "Use no more than six sentences in one paragraph.",
|
|
26
28
|
"phrasal-verb": "Use an approved single-word verb instead of a phrasal verb.",
|
|
@@ -63,9 +65,16 @@ export function formatStatusSummary(
|
|
|
63
65
|
].join("\n")
|
|
64
66
|
}
|
|
65
67
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
68
|
+
const HOUSE_STYLE_RULE_IDS: ReadonlySet<RuleId> = new Set(["hedging", "marketing"])
|
|
69
|
+
const DIRECTIVE_RULE_IDS: ReadonlySet<RuleId> = new Set(["invalid-suppression"])
|
|
70
|
+
|
|
71
|
+
function formatRuleGroup(
|
|
72
|
+
heading: string,
|
|
73
|
+
ruleIds: readonly RuleId[],
|
|
74
|
+
config: SteConfig,
|
|
75
|
+
maxSentenceWords: number,
|
|
76
|
+
): string | undefined {
|
|
77
|
+
const rules = ruleIds
|
|
69
78
|
.filter((ruleId) => resolvedRuleSetting(config, ruleId) !== "off")
|
|
70
79
|
.map((ruleId) => {
|
|
71
80
|
const summary =
|
|
@@ -74,6 +83,38 @@ export function ruleSummary(config: SteConfig): string {
|
|
|
74
83
|
: RULE_SUMMARIES[ruleId]
|
|
75
84
|
return `- [${resolvedRuleSetting(config, ruleId)}] ${summary}`
|
|
76
85
|
})
|
|
77
|
-
|
|
78
|
-
return
|
|
86
|
+
if (rules.length === 0) return undefined
|
|
87
|
+
return `### ${heading}\n\n${rules.join("\n")}`
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export function ruleSummary(config: SteConfig): string {
|
|
91
|
+
const maxSentenceWords = config.maxSentenceWords ?? DEFAULT_MAX_SENTENCE_WORDS
|
|
92
|
+
const ruleIds = Object.keys(RULE_SUMMARIES) as RuleId[]
|
|
93
|
+
const sections = [
|
|
94
|
+
formatRuleGroup(
|
|
95
|
+
"Rules derived from ASD-STE100 Simplified Technical English",
|
|
96
|
+
ruleIds.filter(
|
|
97
|
+
(ruleId) => !HOUSE_STYLE_RULE_IDS.has(ruleId) && !DIRECTIVE_RULE_IDS.has(ruleId),
|
|
98
|
+
),
|
|
99
|
+
config,
|
|
100
|
+
maxSentenceWords,
|
|
101
|
+
),
|
|
102
|
+
formatRuleGroup(
|
|
103
|
+
"Directive validation",
|
|
104
|
+
ruleIds.filter((ruleId) => DIRECTIVE_RULE_IDS.has(ruleId)),
|
|
105
|
+
config,
|
|
106
|
+
maxSentenceWords,
|
|
107
|
+
),
|
|
108
|
+
formatRuleGroup(
|
|
109
|
+
"House-style rules",
|
|
110
|
+
ruleIds.filter((ruleId) => HOUSE_STYLE_RULE_IDS.has(ruleId)),
|
|
111
|
+
config,
|
|
112
|
+
maxSentenceWords,
|
|
113
|
+
),
|
|
114
|
+
].filter((section): section is string => section !== undefined)
|
|
115
|
+
const rules =
|
|
116
|
+
sections.length === 0
|
|
117
|
+
? "No writing rules are enabled."
|
|
118
|
+
: `Apply these enabled rules to prose that you write or edit:\n\n${sections.join("\n\n")}`
|
|
119
|
+
return `## Writing rules\n\n${rules}\n\nWrites, edits, and git commit messages reject hard violations. Correct the reported text and retry. Soft violations produce warnings.`
|
|
79
120
|
}
|