simple_english 0.5.0 → 0.13.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8021741a0615e3d936a8a9bdabe7579fb1fa9641ce5061bf2d3a0e2bb24dd4d9
4
- data.tar.gz: 87fb729f7e72ef9af5d3c63b305fdff9bea3b3aec034429ac52eaffb54639559
3
+ metadata.gz: 93340f34e689001d1b7d4c1efd03ed23468f93811010ecf19749f09c753c7573
4
+ data.tar.gz: 2c243f1af37438e4223aa7b9ce6706394a9000cae96177ca1df97917371fcb2f
5
5
  SHA512:
6
- metadata.gz: '058e1303247e2ca2aee046e1ff1c9341ff96fae26f4c4fcbb67410cd7652d0d806be163c0abb7c393ce81a725bc8b94a0a4faaeee09c89d8f04daf738a7dd677'
7
- data.tar.gz: 57e8431a0c71e31a3f14d3ab6f776dfe10e0f49f60a279c7b0d4a53f7d432594a4d79af76700cc6fbc45b26767258eca3a957817cd8574f05fd64c4cff09f223
6
+ metadata.gz: 61e26907b3efc837bf972f3c0da291146c626141c2e3c3225cb404de16bb66319f06eceda0da6dff6a6bfc92adf4c756cc333f6c454387ae471bb172fb6924f9
7
+ data.tar.gz: 818b641b03cb6c8227020ba879e85a334db14ce3894a2b26972f4c45063ea305aad1308d7736645ae050327460c241697d7ca4b294db2903016b850fbab2557e
data/README.md CHANGED
@@ -4,25 +4,57 @@
4
4
 
5
5
  # simple_english
6
6
 
7
- > Write for human readers, not for reviewers or another AI.
7
+ > Write plain English for humans. Keep the agent honest.
8
8
 
9
- AI writes your docs and code comments in seconds. This linter cuts
10
- the slop it leaves behind. Every finding says what to write
11
- instead.
9
+ You can teach an agent plain-English prose with a skill or a
10
+ prompt. It writes clean prose in the first paragraph and slop again
11
+ by the third. This linter keeps the agent honest: it reads what the
12
+ agent wrote and names every broken rule. Send the findings back, and
13
+ the agent corrects its own text.
12
14
 
13
- ```console
14
- $ printf "The config was written by setup — don't edit it; the daemon caches rules, making the first lint slow." > note.md
15
- $ se note.md
16
- note.md:1:12-26: [SE_ACTIVE_VOICE] "was written by" - Use the active voice. Say who does the action.
17
- note.md:1:73-81: [SE_ING_AFTER_COMMA] ", making" - Start a new sentence instead of the -ing phrase.
18
- note.md:1:37-40: [SE_NO_CONTRACTIONS] "n't" - Write the words in full. No contractions.
19
- note.md:1:33-34: [SE_NO_EMDASH] "—" - Write two sentences, or use a comma.
20
- note.md:1:48-49: [SE_NO_SEMICOLON] ";" - Write two sentences, or name the relation.
21
- ```
15
+ <table>
16
+ <tr>
17
+ <th width="50%">Before</th>
18
+ <th width="50%">After</th>
19
+ </tr>
20
+ <tr>
21
+ <td valign="top">
22
+
23
+ > Your API keys are rotated by the service every 90 days — you will need to update them if you get a 401; don't panic, simply re-run the setup command, which regenerates the credentials without any downtime. <!-- se: ignore -->
24
+
25
+ </td>
26
+ <td valign="top">
27
+
28
+ > The service rotates your API keys every 90 days. If you get a 401, update them. Do not panic. Re-run the setup command. It regenerates the credentials without any downtime.
22
29
 
23
- Markdown prose plus code comments in Python, Ruby, JavaScript,
24
- TypeScript, Go, Rust, Java, C#, C++, Kotlin, bash, and YAML. Output as
25
- plain text, JSON, or SARIF.
30
+ </td>
31
+ </tr>
32
+ </table>
33
+
34
+ The linter covers Markdown prose plus code comments in Python, Ruby,
35
+ JavaScript, TypeScript, Go, Rust, Java, C, C++, and C#. It also
36
+ reads Objective-C, Kotlin, Swift, Scala, Dart, PHP, Perl, Elixir,
37
+ Lua, R, Haskell, Zig, SQL, and bash. It reads comments in config
38
+ formats and markup as well: YAML, TOML, INI, Makefile, Dockerfile,
39
+ HTML, and XML. Findings print as plain text, JSON, or SARIF.
40
+
41
+ If your language is not on the list, [open an issue](https://github.com/TonyCTHsu/simple-english/issues).
42
+
43
+ ## Agent integrations
44
+
45
+ Plug the linter into your coding agent. It writes plain English on
46
+ the first pass and checks its own draft before you read the file.
47
+
48
+ | Coding agent | Ships as | Docs |
49
+ |---|---|---|
50
+ | `pi` | npm package `pi-simple-english` | [README](integrations/pi/README.md) |
51
+ | `claude-code` | plugin `simple-english` | [README](integrations/claude-code/README.md) |
52
+ | `codex` | plugin `simple-english` | [README](integrations/codex/README.md) |
53
+ | `cursor` | plugin folder `simple-english` | [README](integrations/cursor/README.md) |
54
+ | `opencode` | plugin `simple-english` | [README](integrations/opencode/README.md) |
55
+ | `vscode-copilot` | config wiring | [README](integrations/vscode-copilot/README.md) |
56
+
57
+ If your agent is not on the list, [open an issue](https://github.com/TonyCTHsu/simple-english/issues).
26
58
 
27
59
  ## The rules
28
60
 
@@ -36,53 +68,52 @@ plain text, JSON, or SARIF.
36
68
  - **Code comments:** same pattern rules, with line and column range.
37
69
  - **Counts (Markdown only):** 20 words per sentence in list items, 25 in paragraphs, six sentences per paragraph at most.
38
70
 
39
- The full list, with a wrong and a right example for each rule:
40
- [docs/RULES.md](docs/RULES.md).
71
+ The full list, with a wrong and a right example for each rule [docs/RULES.md](docs/RULES.md).
41
72
 
42
73
  ## Install
43
74
 
44
- **Requirements:** On macOS, Homebrew covers everything. On Linux,
45
- Windows, or CI, you need Ruby 3.3 or newer and Java 11 or newer for
46
- LanguageTool. The Docker image bundles both.
75
+ ### Prerequisites
47
76
 
48
- ### Homebrew (macOS)
77
+ The gem needs Ruby 3.3 or newer. On a Mac, the system Ruby (2.6) is too
78
+ old. Install one, for example with Homebrew:
49
79
 
50
80
  ```bash
51
- brew install TonyCTHsu/tap/simple-english
52
- brew services start simple-english
81
+ brew install ruby
53
82
  ```
54
83
 
55
- Homebrew installs Ruby and Java alongside, so the machine needs
56
- neither. The service keeps a background daemon running. Its first
57
- start downloads LanguageTool (about 300 MB), and later lints take
58
- milliseconds. Run `brew services stop simple-english` to stop it.
59
-
60
- ### Ruby gem (Linux, Windows, CI)
84
+ Homebrew's Ruby is not on the `PATH` by default, and neither are gem
85
+ binaries. Put both in your shell profile:
61
86
 
62
87
  ```bash
63
- gem install simple_english
64
- se setup # run once: downloads LanguageTool, locates Java, verifies both
65
- se README.md
88
+ echo 'export PATH="/opt/homebrew/opt/ruby/bin:$PATH"' >> ~/.zshrc
89
+ echo 'export GEM_HOME="$HOME/.gem"' >> ~/.zshrc
90
+ echo 'export PATH="$GEM_HOME/bin:$PATH"' >> ~/.zshrc
66
91
  ```
67
92
 
68
- If `java` is not on PATH, set `SE_JAVA` to your java binary.
93
+ Then open a new terminal. A version manager such as `mise` or `rbenv`
94
+ works too.
69
95
 
70
- ### Container
96
+ ### Ruby gem (macOS arm64, Linux x86-64 and arm64)
71
97
 
72
- The image holds Ruby, Java, and LanguageTool, so it needs no setup:
98
+ ```bash
99
+ gem install simple_english
100
+ ```
101
+
102
+ ### Homebrew (macOS arm64)
73
103
 
74
104
  ```bash
75
- docker run -v "$PWD":/work ghcr.io/tonycthsu/simple-english:latest docs/
105
+ brew install TonyCTHsu/tap/simple-english
76
106
  ```
77
107
 
78
- ### Git repository
108
+ ### Container
79
109
 
80
- Run `bundle install`, then use `bin/se`.
110
+ ```bash
111
+ docker run --rm -v "$PWD":/work ghcr.io/tonycthsu/simple-english .
112
+ ```
81
113
 
82
114
  ## Usage
83
115
 
84
- Lint files, directories, or stdin. From a checkout, the same commands
85
- run through `bin/se`:
116
+ Lint files, directories, or stdin:
86
117
 
87
118
  ```bash
88
119
  se README.md
@@ -113,34 +144,72 @@ se --format sarif src/ > results.sarif
113
144
 
114
145
  - `0`: no findings
115
146
  - `1`: findings
116
- - `2`: setup error
147
+ - `2`: input, configuration, installation, or daemon error
148
+
149
+ ### MCP server
150
+
151
+ Start an MCP stdio server with one tool, `lint`. Any MCP
152
+ client can call it.
153
+
154
+ ```
155
+ se mcp
156
+ ```
117
157
 
118
158
  ### CI
119
159
 
120
- Gate the prose in the pull request that changes it. The plain run
121
- fails the build on findings, and the SARIF report puts them inline:
160
+ Gate the pull request's prose in GitHub Actions. The lint fails the build on findings:
122
161
 
123
162
  ```yaml
124
163
  name: lint-docs
125
164
  on: [pull_request]
126
- permissions:
127
- security-events: write
128
165
  jobs:
129
166
  lint:
130
167
  runs-on: ubuntu-latest
131
168
  steps:
132
169
  - uses: actions/checkout@v7
133
- - uses: ruby/setup-ruby@v1
134
- - run: gem install simple_english
135
- - run: se setup
136
- - run: se --format sarif . > lint.sarif
137
- - run: se .
170
+ - uses: TonyCTHsu/simple-english@v0.13.1
171
+ with:
172
+ files: |
173
+ docs/
174
+ README.md
175
+ ```
176
+
177
+ To see the findings inline in the pull request, lint with
178
+ `format: sarif` and upload the report. Add
179
+ `permissions: {security-events: write}` to the workflow:
180
+
181
+ ```yaml
182
+ - uses: TonyCTHsu/simple-english@v0.13.1
183
+ with:
184
+ format: sarif
138
185
  - uses: github/codeql-action/upload-sarif@v3
139
186
  with:
140
- sarif_file: lint.sarif
187
+ sarif_file: simple-english.sarif
141
188
  if: always()
142
189
  ```
143
190
 
191
+ Any CI can run the lint. Install the gem, run `se lint .`, and fail the
192
+ build on exit code 1.
193
+
194
+ ### Git hooks
195
+
196
+ Install the gem once, then wire your hook runner. The command to run
197
+ is `se lint`.
198
+
199
+ #### Pre-commit framework
200
+
201
+ ```yaml
202
+ # .pre-commit-config.yaml
203
+ repos:
204
+ - repo: https://github.com/TonyCTHsu/simple-english
205
+ rev: v0.13.1
206
+ hooks:
207
+ - id: se-lint
208
+ ```
209
+
210
+ For Husky with lint-staged, map every staged file to `se lint`. For
211
+ Lefthook, run `se lint {staged_files}` in a `pre-commit` command.
212
+
144
213
  ## Config and suppressions
145
214
 
146
215
  ### Config file
@@ -152,10 +221,27 @@ ignore:
152
221
  - vendor/**
153
222
  disabled-rules:
154
223
  - SE_NO_EMDASH
224
+ rule-ignores:
225
+ SE_MODAL_RESTRICTED:
226
+ - .agents/skills/**
155
227
  ```
156
228
 
157
229
  `ignore` globs: `**` crosses directories, `*` stays in one segment.
158
230
 
231
+ `disabled-rules` drops a rule from every file. `rule-ignores` drops
232
+ a rule only on the listed paths. The rule still fires everywhere
233
+ else. It covers prose that plays by different rules, like spec
234
+ text that quotes a standard's "SHOULD": <!-- se: ignore=SE_MODAL_RESTRICTED -->
235
+
236
+ ```yaml
237
+ rule-ignores:
238
+ SE_MODAL_RESTRICTED:
239
+ - .agents/skills/**
240
+ ```
241
+
242
+ With that config, `SE_MODAL_RESTRICTED` fires in `docs/` and the
243
+ README, and stays quiet under `.agents/skills/`.
244
+
159
245
  ### Inline suppressions
160
246
 
161
247
  A line containing `se: ignore` suppresses findings reported on that
@@ -175,57 +261,33 @@ In a code comment:
175
261
  # Don't touch this constant. se: ignore=SE_NO_CONTRACTIONS
176
262
  ```
177
263
 
178
- ## The background daemon
179
-
180
- The CLI runs a small background server on your machine. The first
181
- lint starts it, which takes about 15 seconds. Later lints take
182
- milliseconds.
183
-
184
- The first lint after a gem update prints a warning. Run `se serve --detached` then.
185
- It stops the old daemon and starts the new one in the
186
- background. You never talk to the daemon directly. Its HTTP
187
- interface is internal and can change in any release.
188
-
189
- ## Scope
190
-
191
- The rule set comes from the Plain-mode rules of the MIT-licensed
192
- SimpleEnglish project. This tool does not check ASD-STE100 compliance.
193
- This repo holds no ASD-STE100 text. If you need full compliance, read
194
- the free standard at <https://www.asd-ste100.org/>.
195
-
196
264
  ## FAQ
197
265
 
198
- ### Have you thought about an agent skill?
199
-
200
- The [SimpleEnglish project](https://github.com/AminBlg/SimpleEnglish)
201
- ships one. Its skill guides an agent while it writes. This tool does
202
- the other half of the work. It checks the result against fixed
203
- rules. Use both: the skill helps the first draft, and the linter
204
- catches what the agent missed.
205
-
206
266
  ### Why not Vale?
207
267
 
208
- We ported all 67 rules to Vale and ran the corpus on both engines.
268
+ We ported rules to Vale and ran the corpus on both engines.
209
269
  About 60 rules behave the same. Vale has no check for the em-dash
210
270
  and semicolon rules: its checks see words, not punctuation. Its
211
271
  tagger also mislabels verbs, so the condition-first rule stays
212
- silent. The rules here are LanguageTool XML with examples that CI
213
- verifies. Closing the Vale gaps needs scripts or an external
272
+ silent. Closing the Vale gaps needs scripts or an external
214
273
  tagger, and that erases Vale's main advantage: one binary with no
215
274
  service behind it.
216
275
 
217
- ## Agent integrations
276
+ ## Compliance
218
277
 
219
- Pi, Claude Code, and Codex can lint the prose they write. The adapters
220
- live under `integrations/`, unreleased. Try them from a checkout:
278
+ This tool does not check ASD-STE100 compliance. This repo holds no
279
+ ASD-STE100 text. For full compliance, read the free standard at
280
+ <https://www.asd-ste100.org/>.
221
281
 
222
- ```bash
223
- claude --plugin-dir integrations/claude-code # the hook feeds findings back
224
- pi install integrations/pi -l # the se_lint tool
225
- se mcp # an MCP server, for any MCP client
226
- ```
282
+ The tool checks no external style standard. The rules are this repo's
283
+ own, but they draw on the [GOV.UK A to Z style
284
+ guide](https://guidance.publishing.service.gov.uk/writing-to-gov-uk-standards/style-guides/a-to-z-style-guide/)
285
+ and the [US plain language
286
+ guide](https://digital.gov/guides/plain-language/).
227
287
 
228
- See [docs/DEVELOPMENT.md](docs/DEVELOPMENT.md) for the full story.
288
+ The rule sources are `rules/simple-english.xml` for pattern rules and
289
+ `lib/simple_english/lint/counts.rb` for counting rules. The full list
290
+ sits in [docs/RULES.md](docs/RULES.md).
229
291
 
230
292
  ## Develop
231
293
 
@@ -0,0 +1,72 @@
1
+ # Third-party notices
2
+
3
+ Files under `licenses/` carry the license texts and upstream notices
4
+ for third-party software distributed inside this gem. Simple English's
5
+ own code is MIT-licensed; see `LICENSE`.
6
+
7
+ ## Lint engine
8
+
9
+ Each platform gem ships a compiled lint engine at
10
+ `libexec/simple_english/languagetool-server`. The binary is built with
11
+ GraalVM Community Edition 25.0.2 and contains code from the
12
+ components below. The authoritative list of components inside a given
13
+ binary is the CycloneDX SBOM published with each release
14
+ (`dist/languagetool-server-<platform>.sbom.json`).
15
+
16
+ | Component | License | Notice |
17
+ |---|---|---|
18
+ | LanguageTool 6.6 (`languagetool-core`, `languagetool-server`, `languagetool-gui-commons`, `language-en`, `language-ca`, `language-es`, `language-pt`), unmodified except for a build-time copy of `rules/simple-english.xml` into the English rules | LGPL-2.1 | `licenses/LGPL-2.1.txt` |
19
+ | English word-list and POS data (12dicts, AGID, Moby/WordNet POS) | upstream terms, public domain | `licenses/languagetool-resource-en/` |
20
+ | `at.favre.lib:bcrypt` 0.10.2 | Apache-2.0 | |
21
+ | `ch.qos.logback:logback-classic` 1.5.18 | EPL-1.0 or LGPL-2.1 | |
22
+ | `co.elastic.logging:logback-ecs-encoder` 1.3.2 | Apache-2.0 | |
23
+ | `com.fasterxml.jackson.core:jackson-databind` 2.18.0 | Apache-2.0 | |
24
+ | `commons-cli:commons-cli` 1.9.0 | Apache-2.0 | |
25
+ | `commons-codec:commons-codec` 1.17.1 | Apache-2.0 | |
26
+ | `edu.washington.cs.knowitall:opennlp-*` models 1.5 | Apache-2.0 | |
27
+ | `io.lettuce:lettuce-core` 6.5.4 | MIT | |
28
+ | `io.prometheus:simpleclient_*` 0.16.0 | Apache-2.0 | |
29
+ | `org.apache.opennlp:opennlp-tools` 1.9.4 | Apache-2.0 | |
30
+ | `org.mariadb.jdbc:mariadb-java-client` 3.4.1 | LGPL-2.1 | `licenses/LGPL-2.1.txt` |
31
+ | `org.mybatis:mybatis` 3.5.16 | Apache-2.0 | |
32
+ | `org.slf4j:slf4j-api` 2.0.16 | MIT | |
33
+ | GraalVM and OpenJDK runtime code | GPLv2 with the Classpath Exception | `licenses/OpenJDK-LICENSE.txt` |
34
+
35
+ The engine uses logback under its LGPL-2.1 option, not its EPL-1.0
36
+ option.
37
+
38
+ ## LGPL 2.1 obligations
39
+
40
+ LanguageTool is licensed under the GNU Lesser General Public License
41
+ 2.1. This gem distributes a binary statically linked with
42
+ LanguageTool, which LGPL 2.1 section 6 permits when recipients can
43
+ study, modify, and rebuild the LGPL part:
44
+
45
+ - The license text ships with every gem (`licenses/LGPL-2.1.txt`).
46
+ - The corresponding LanguageTool source is the `v6.6` tag at
47
+ https://github.com/languagetool-org/languagetool/tree/v6.6
48
+ - The gem's MIT license permits reverse engineering, so you may
49
+ debug the binary and your modifications to it.
50
+ - You can rebuild the engine against a modified LanguageTool: change
51
+ the version in `native/languagetool/pom.xml` and follow the
52
+ rebuild steps in `native/languagetool/README.md` under "Changing
53
+ the native engine".
54
+
55
+ Written offer under section 6(c): we give any recipient the
56
+ machine-readable materials that section 6(a) names, for at least three
57
+ years after each release, at no charge. Ask in an issue at
58
+ https://github.com/TonyCTHsu/simple-english/issues. The materials are
59
+ the LanguageTool `v6.6` source, the resolved classpath of
60
+ `native/languagetool/pom.xml` as Maven Central artifacts, the
61
+ reachability metadata under `native/languagetool/config/`, and the
62
+ build scripts `native/languagetool/build.rb` and `Rakefile` with the
63
+ `rules/simple-english.xml` overlay. You need them to modify
64
+ LanguageTool and relink a working engine.
65
+
66
+ ## OpenJDK and GraalVM source
67
+
68
+ The binary embeds OpenJDK and GraalVM Community runtime code under
69
+ GPLv2 with the Classpath Exception. The corresponding source is
70
+ public: https://github.com/openjdk/jdk and
71
+ https://github.com/oracle/graal. The release SBOM names the versions
72
+ the binary embeds.
data/docs/RULES.md CHANGED
@@ -50,7 +50,7 @@ Use the simple past. Say when it happened.
50
50
 
51
51
  ### SE_ING_AFTER_COMMA: No -ing after a comma
52
52
 
53
- Start a new sentence instead of the -ing phrase.
53
+ Write a new sentence, or put the condition first. A trailing -ing phrase hides who does what and when.
54
54
 
55
55
  - Wrong: The tool runs, making it easy.
56
56
  - Right: The tool runs. It is easy.
@@ -477,6 +477,92 @@ Delete it.
477
477
  - Wrong: let's dive in. Read the log.
478
478
  - Right: Read the log.
479
479
 
480
+ ### SE_SLOP_EMPOWER: allow, not empower
481
+
482
+ Write allow.
483
+
484
+ - Wrong: The setting empowers the user to edit files.
485
+ - Right: The setting allows the user to edit files.
486
+
487
+ ### SE_SLOP_COLLABORATE: work with, not collaborate or liaise
488
+
489
+ Write work with.
490
+
491
+ - Wrong: The team collaborates with the owners.
492
+ - Right: The team works with the owners.
493
+
494
+ ### SE_SLOP_INITIATE: start, not initiate
495
+
496
+ Write start.
497
+
498
+ - Wrong: Initiate the build.
499
+ - Right: Start the build.
500
+
501
+ ### SE_SLOP_GOING_FORWARD: from now on, not going forward
502
+
503
+ Write from now on.
504
+
505
+ - Wrong: Going forward, read the log.
506
+ - Right: From now on, read the log.
507
+
508
+ ### SE_SLOP_TACKLE: solve, not tackle
509
+
510
+ Write solve or deal with.
511
+
512
+ - Wrong: The team tackled the problem.
513
+ - Right: The team solved the problem.
514
+
515
+ ### SE_HIDDEN_VERB_DECISION: decide, not make a decision
516
+
517
+ Write decide.
518
+
519
+ - Wrong: Make a decision before the release.
520
+ - Right: Decide before the release.
521
+
522
+ ### SE_HIDDEN_VERB_ASSISTANCE: help, not provide assistance
523
+
524
+ Write help.
525
+
526
+ - Wrong: The tool provides assistance to new users.
527
+ - Right: The tool helps new users.
528
+
529
+ ### SE_HIDDEN_VERB_CONSIDERATION: consider, not take into consideration
530
+
531
+ Write consider.
532
+
533
+ - Wrong: Take the failure into consideration before you restart.
534
+ - Right: Consider the failure before you restart.
535
+
536
+ ### SE_HIDDEN_VERB_ABLE: can, not is able to
537
+
538
+ Write can.
539
+
540
+ - Wrong: The tool is able to read the log.
541
+ - Right: The tool can read the log.
542
+
543
+ ### SE_NUMBER_WORDS: numerals, not compound number words
544
+
545
+ #### spelled with a space
546
+
547
+ Write the numeral, such as 24.
548
+
549
+ - Wrong: The log keeps twenty four hours.
550
+ - Right: The log keeps 24 hours.
551
+
552
+ #### spelled with a hyphen
553
+
554
+ Write the numeral, such as 24.
555
+
556
+ - Wrong: The job runs for twenty-four hours.
557
+ - Right: The job runs for 24 hours.
558
+
559
+ #### standalone teens and tens
560
+
561
+ Write the numeral, such as 12.
562
+
563
+ - Wrong: The retry waits fifteen seconds.
564
+ - Right: The retry waits 15 seconds.
565
+
480
566
  ## Counting rules
481
567
 
482
568
  These rules run in Ruby on Markdown text. Code comments do not use
@@ -1,8 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # The command-line interface: Thor commands for lint, serve, and
4
- # setup. bin/se is a thin runner over this, so the installed
5
- # gem's RubyGems shim can load it.
3
+ # The command-line interface: Thor commands for lint and serve, plus
4
+ # a no-op setup for older callers. bin/se is a thin runner over this,
5
+ # so the installed gem's RubyGems shim can load it.
6
6
 
7
7
  require "thor"
8
8
  require_relative "../simple_english"
@@ -15,6 +15,10 @@ module SimpleEnglish
15
15
  # names. Thor only falls back to the default task when the first
16
16
  # argument is an option, so route them to lint explicitly.
17
17
  def self.start(given_args = ARGV, config = {})
18
+ # GUI apps (Claude Desktop) and cron spawn this CLI without a
19
+ # locale, so Ruby reads stdin and files as US-ASCII and chokes on
20
+ # non-ASCII prose (issue 95). Prose is UTF-8 by spec.
21
+ Encoding.default_external = Encoding::UTF_8
18
22
  first = given_args.first
19
23
  if first && !all_commands.key?(first) &&
20
24
  (first == "-" || !first.start_with?("-"))
@@ -32,10 +36,6 @@ module SimpleEnglish
32
36
  start(argv)
33
37
  end
34
38
 
35
- # How each Install.java_source found its java, for `se setup`'s
36
- # report line.
37
- JAVA_SOURCES = {env: "SE_JAVA", path: "PATH", homebrew: "Homebrew fallback"}.freeze
38
-
39
39
  default_task :lint
40
40
  desc "lint FILE_OR_DIR...", "Lint Markdown prose and code comments (- reads stdin)"
41
41
  method_option :format, type: :string, default: "text", enum: %w[text json sarif],
@@ -59,18 +59,22 @@ module SimpleEnglish
59
59
  warn "error: file not readable: #{path}"
60
60
  return 2
61
61
  end
62
- text = (path == "-") ? $stdin.read : File.read(path)
63
- findings = SimpleEnglish.lint_file(path, text)
62
+ # The lintable gate in Linter reads lintable files itself. A
63
+ # non-lintable path must not cost a full read here.
64
+ findings =
65
+ if path == "-"
66
+ SimpleEnglish::Linter.lint_text($stdin.read, config: config)
67
+ else
68
+ SimpleEnglish::Linter.lint_file(path, config: config)
69
+ end
64
70
  return 2 if findings.nil? # already warned why
65
- SimpleEnglish::Config.filter(config, path, findings).each do |finding|
66
- results << [path, finding]
67
- end
71
+ findings.each { |finding| results << [path, finding] }
68
72
  end
69
73
  self.class.report(results, options[:format])
70
74
  results.empty? ? 0 : 1
71
75
  end
72
76
 
73
- desc "version", "Print the gem version. The container image tag is v plus this."
77
+ desc "version", "Print the gem version"
74
78
  def version
75
79
  puts SimpleEnglish::VERSION
76
80
  0
@@ -112,26 +116,9 @@ module SimpleEnglish
112
116
  2
113
117
  end
114
118
 
115
- desc "setup", "Download LanguageTool and locate Java. Idempotent."
119
+ desc "setup", "Do nothing; the lint engine ships with the gem"
116
120
  def setup
117
- install = SimpleEnglish::Install.from_env
118
- lt = SimpleEnglish::LanguageTool.install(install.cache_dir)
119
- return 2 unless lt
120
- puts "LanguageTool #{SimpleEnglish::LanguageTool::LT_VERSION}: " \
121
- "#{lt["downloaded"] ? "downloaded to" : "already present at"} #{lt["dir"]}"
122
- unless install.java?
123
- warn "error: #{install.java_message}"
124
- return 2
125
- end
126
- puts "Java: #{install.java} (#{JAVA_SOURCES[install.java_source]})"
127
- unless SimpleEnglish::LanguageTool.smoke(install)
128
- warn "error: LanguageTool smoke test failed. The download may be corrupt. " \
129
- "Delete #{lt["dir"]} and rerun `se setup`."
130
- return 2
131
- end
132
- puts "Smoke test: passed"
133
- puts
134
- puts "se finds them automatically. Nothing to export."
121
+ puts "nothing to set up; the lint engine ships with the gem"
135
122
  0
136
123
  end
137
124
 
@@ -141,8 +141,8 @@ module SimpleEnglish
141
141
  # Preflight everything the spawn needs: a missing prerequisite
142
142
  # must fail fast with the real blocker named, not spawn a
143
143
  # doomed child and wait out 90 s.
144
- unless File.exist?(install.server_jar) && install.java?
145
- warn(install.java? ? install.setup_error : install.java_message)
144
+ unless install.executable?
145
+ warn install.setup_error
146
146
  return false
147
147
  end
148
148
  # One boot at a time: a concurrent lint that also found the
@@ -153,7 +153,7 @@ module SimpleEnglish
153
153
  with_spawn_lock do
154
154
  daemon = info
155
155
  unless daemon
156
- warn "se: daemon not running; starting it (first lint takes ~15s)..."
156
+ warn "se: daemon not running; starting it (first lint may take a few seconds)..."
157
157
  spawn_daemon
158
158
  daemon = wait_for { info }
159
159
  end
@@ -185,10 +185,9 @@ module SimpleEnglish
185
185
  "(#{VERSION}). #{fix} Linting against it meanwhile."
186
186
  end
187
187
 
188
- # A per-user lock keyed by the port, independent of the
189
- # installation cache: daemon ownership is scoped to the port,
190
- # so two lints with different SE_CACHE_DIR values still
191
- # coordinate. flock releases when the block ends, even on
188
+ # A per-user lock keyed by the port: daemon ownership is scoped
189
+ # to the port, so concurrent lints coordinate across installs.
190
+ # flock releases when the block ends, even on
192
191
  # failure.
193
192
  def with_spawn_lock
194
193
  dir = File.join(Dir.home, ".cache", "simple_english")