@uehaj/sys1grep 0.5.0-next.0 → 0.5.0-next.2
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.ja.md +319 -32
- package/README.md +333 -39
- package/docs/sentence.svg +1 -1
- package/package.json +8 -2
- package/sys1grep.mjs +1334 -234
- package/templates/default.html +102 -0
- package/templates/print.html +51 -0
- package/templates/search.html +113 -0
- package/templates/terminal.html +57 -0
package/README.md
CHANGED
|
@@ -4,6 +4,8 @@
|
|
|
4
4
|
|
|
5
5
|
Background and design notes (Japanese): [Jevのキラーアプリ、「意味で探す grep」を作った](https://zenn.dev/uehaj/articles/jev-semgrep-grep-by-meaning) on Zenn.
|
|
6
6
|
|
|
7
|
+
> **Renamed from semgrep (0.5.0).** Two reasons. The name collided with the static-analysis tool [Semgrep](https://semgrep.dev/), a trademark. And the tool was never meant for Jev alone: it targets System 1 models in general, and I'd like it to work with any models TypeSafe AI may release after Jev under other names :). So from 0.5.0 this is **sys1grep** (System 1 + grep): npm package `@uehaj/sys1grep` (was `@uehaj/semgrep`), commands `sys1grep` / `git sys1grep`, environment `SYS1GREP_*`, config `~/.config/sys1grep/.env`. The old `SEMGREP_*` names and `~/.config/semgrep/.env` still work for one minor release, each use printing a deprecation line; 1.0.0 drops them. Details: [CHANGELOG](CHANGELOG.md#renamed). The Zenn article's URL keeps its old slug.
|
|
8
|
+
|
|
7
9
|
A grep that finds lines by **what they mean**, not by regular expressions.
|
|
8
10
|
Matching is done by **Jev**, the System One model from [TypeSafe AI](https://typesafe.ai/).
|
|
9
11
|
Jev does not generate text. It answers typed questions with probabilities, so for every line
|
|
@@ -14,9 +16,9 @@ and applies a threshold.
|
|
|
14
16
|
./sys1grep -n -e "customer is angry or frustrated" tickets.txt
|
|
15
17
|
```
|
|
16
18
|
|
|
17
|
-
[](https://
|
|
19
|
+
[](https://sys1grep.js.org/)
|
|
18
20
|
|
|
19
|
-
<sub>▶ Click the demo, or open <a href="https://
|
|
21
|
+
<sub>▶ Click the demo, or open <a href="https://sys1grep.js.org/">uehaj.github.io/sys1grep</a>, for the full demo on the landing page.</sub>
|
|
20
22
|
|
|
21
23
|
- Zero dependencies. One file, Node.js 20.16+ and `fetch`.
|
|
22
24
|
- Fast. 30 lines go into one request, requests run 8 at a time. A 210-line file finishes in under a second.
|
|
@@ -134,8 +136,7 @@ as a plain regex, with no request at all. It prefilters its AND term: only the l
|
|
|
134
136
|
ever ask that term's meanings, so a cheap regex in front of a meaning cuts both the bill and the wait. A line is sent
|
|
135
137
|
only if some term's regexes all hold for it (a term with no regex holds for every line), so with
|
|
136
138
|
`-e '/re/' -a A -e B` a line without `re` is still sent, asked `B` only. A query of regex terms alone
|
|
137
|
-
sends nothing,
|
|
138
|
-
break; use `--sentence=rules` to stay offline. Anything that isn't shaped like `/…/flags` is still a meaning, so
|
|
139
|
+
sends nothing, even with `--unit=sentence-by-jev`: the rules alone then decide where wrapped lines join. Anything that isn't shaped like `/…/flags` is still a meaning, so
|
|
139
140
|
`-e '/etc 以下のファイルを変更している'` (no closing `/`) is unaffected; a meaning that really starts
|
|
140
141
|
and ends with `/` can be written with a leading space to dodge the regex reading.
|
|
141
142
|
|
|
@@ -172,24 +173,50 @@ $ ./sys1grep -o -n -e '/[A-Z]+-\d+/' -a 'the ticket is still open' notes.txt #
|
|
|
172
173
|
Every line sent to Jev costs money and time, so the cheapest line is the one never sent. From the widest cut
|
|
173
174
|
to the narrowest:
|
|
174
175
|
|
|
175
|
-
- **Which files.** `-r` skips `.git`, `node_modules`, binary files, likely secrets
|
|
176
|
-
[`git sys1grep`](#as-a-git-subcommand-git-sys1grep) searches
|
|
177
|
-
(file-name globs) and `--changed-within` (`30m`, `7d`, `today`,
|
|
178
|
-
A meaning that restricts itself to a language or a time of change
|
|
179
|
-
[Scope from the meaning](#scope-from-the-meaning).
|
|
176
|
+
- **Which files.** `-r` skips `.git`, `node_modules`, binary files, likely secrets, generated files (source maps,
|
|
177
|
+
minified JS/CSS, lock files) and what git ignores; [`git sys1grep`](#as-a-git-subcommand-git-sys1grep) searches
|
|
178
|
+
tracked files only. `--include` / `--exclude` (file-name globs) and `--changed-within` (`30m`, `7d`, `today`,
|
|
179
|
+
`this-week`, a date) narrow them further. A meaning that restricts itself to a language or a time of change
|
|
180
|
+
narrows them by itself: see [Scope from the meaning](#scope-from-the-meaning).
|
|
180
181
|
- **Which lines.** A [regex term](#regex-terms) is matched locally, and only the lines it holds for are asked
|
|
181
|
-
its AND term's meanings. Blank lines are never sent.
|
|
182
|
+
its AND term's meanings. Blank lines are never sent. Only the first `-M`/`--max-columns` characters of a line
|
|
183
|
+
(default 2000, 8000 with `-z`) are sent: it is still searched and judged, just not past that cutoff.
|
|
182
184
|
- **How many times.** [`--dedup`](#one-line-per-template---dedup) judges one line per template: lines that
|
|
183
|
-
differ only in ids, numbers, times or paths share one answer.
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
185
|
+
differ only in ids, numbers, times or paths share one answer. `auto`/`always`/`never`, off by default for
|
|
186
|
+
now: a hint says when `--dedup=auto` would pay.
|
|
187
|
+
- **Check before paying.** `--dry-run` sends nothing and prints the settings the search would run with, the
|
|
188
|
+
files, how many lines each would send and every request with its questions. Its last line estimates the input
|
|
189
|
+
tokens and, for TypeSafe itself, the price (`~3178 input tokens, ~$0.000133`; within about 10%). `-i` shows the
|
|
190
|
+
same totals on the terminal and sends only after `y`.
|
|
191
|
+
Before the bulk of requests, every target is sized (`--max-filesize`, default 10M); one over it is skipped
|
|
192
|
+
outright, like `rg`'s own `--max-filesize`, named on stderr (`-y` does not affect it). A `.gz` is sized
|
|
193
|
+
decompressed (zlib stops at the limit, so an oversized one is never inflated in full). stdin is sized once it
|
|
194
|
+
is read (it is already in memory), and skipped the same way if it is over; a `--cached` or `<tree>:` target
|
|
195
|
+
is a blob rather than a file, sized from its content (already read in one `git cat-file --batch` call, not a
|
|
196
|
+
fresh git process per blob); `-g`'s commits stay out of this, since each is already bounded by `-M` when it
|
|
197
|
+
is sent. The input about to be sent is priced too (`--max-cost`,
|
|
198
|
+
default 1 USD); over it, one question asks to continue on the terminal, `-y` answers it yes, and without a
|
|
199
|
+
terminal it is exit 2 (this applies with `-q` too — scripts pass `-y`).
|
|
188
200
|
|
|
189
201
|
```sh
|
|
190
202
|
$ sys1grep --dry-run -r --include='*.log' --changed-within=today -e '/ERROR|FATAL/' -a 'a customer is affected' logs/
|
|
191
203
|
```
|
|
192
204
|
|
|
205
|
+
`--verbose` (or `--dry-run`) also shows *where* a setting that was not typed on the command line came from
|
|
206
|
+
(`SYS1GREP_OPTS`, an environment variable, `~/.config/sys1grep/.env`, or a preset's default), so a result that
|
|
207
|
+
surprises you can be traced back to its source. The key's value never appears, only which option or variable
|
|
208
|
+
supplied it:
|
|
209
|
+
|
|
210
|
+
```sh
|
|
211
|
+
$ SYS1GREP_OPTS='--level strict' sys1grep --verbose -e "the API key is read from a file" .
|
|
212
|
+
sys1grep: endpoint api.typesafe.ai/v1/systemone (default), model jev-latest (default)
|
|
213
|
+
sys1grep: key: SYS1GREP_API_KEY (~/.config/sys1grep/.env)
|
|
214
|
+
sys1grep: SYS1GREP_OPTS: --level strict
|
|
215
|
+
sys1grep: options: --level strict (SYS1GREP_OPTS) = -t 0.7 -T 0.3, --chunk 30, -j 8, scope on, -M 2000, --max-filesize 10M, --max-cost 1
|
|
216
|
+
sys1grep: file ./a.py: 120 lines, 120 to send
|
|
217
|
+
…
|
|
218
|
+
```
|
|
219
|
+
|
|
193
220
|
## Install
|
|
194
221
|
|
|
195
222
|
Two ways to use it: as a command-line tool (this section), or as a Claude Code skill
|
|
@@ -381,11 +408,18 @@ tests/tickets/sub/b.txt
|
|
|
381
408
|
```
|
|
382
409
|
|
|
383
410
|
`-r` walks directories in sorted order and skips `.git`, `node_modules`, `.ssh`, `.aws`, `.gnupg`, `.kube`,
|
|
384
|
-
`.docker`, binary files (a NUL byte in the first 8 KB, or a PDF; UTF-16 with a BOM is read as text)
|
|
411
|
+
`.docker`, binary files (a NUL byte in the first 8 KB, or a PDF; UTF-16 with a BOM is read as text), files
|
|
385
412
|
that usually hold secrets (`.env*`, `.netrc`, `.npmrc`, `.pypirc`, `.pgpass`, `.git-credentials`, `*.pem`,
|
|
386
|
-
`*.key`, `*.p12`, `*.pfx`, `*.jks`, `*.keystore`, `id_rsa*` and friends; names compared without case)
|
|
413
|
+
`*.key`, `*.p12`, `*.pfx`, `*.jks`, `*.keystore`, `id_rsa*` and friends; names compared without case) and
|
|
414
|
+
generated files (`*.map`, `*.min.js`, `*.min.css`, `package-lock.json`, `yarn.lock`, `pnpm-lock.yaml`,
|
|
415
|
+
`Cargo.lock`, `poetry.lock`, `composer.lock`, `Gemfile.lock`, `go.sum`). **Every line that is searched is sent
|
|
416
|
+
to the TypeSafe API**, so point `-r` at a directory
|
|
387
417
|
you mean to scan. Inside a git repository, `-r` also skips what git ignores (`.gitignore`, `.git/info/exclude`, the
|
|
388
418
|
global excludes file), so build output and local files stay home; a tracked file is searched even if it matches.
|
|
419
|
+
A file named `*.gz` (a rotated log: `app.log.1.gz`) is read decompressed, as `zgrep` does, and printed by its
|
|
420
|
+
name on disk (`app.log.1.gz:12:...`); `--include='*.gz'` picks those, the binary sniff sees the decompressed
|
|
421
|
+
bytes, a gzipped secret (`private.key.gz`) is skipped like the plain one, and a corrupt `.gz` is an unreadable file.
|
|
422
|
+
Only gzip, only by the name.
|
|
389
423
|
A file named explicitly on the command line is always searched, even if it matches the skip list or is ignored;
|
|
390
424
|
so is a directory that git ignores, when you name it (`sys1grep -r -e ... dist`). `-l` prints each
|
|
391
425
|
matching file once, in the order matches are found, and works with or without `-r`. `-c` prints the number of
|
|
@@ -453,6 +487,25 @@ applies even to tracked files. `--include`, `--exclude` and `--changed-within` f
|
|
|
453
487
|
by a pathspec included. `--changed-within` reads the working tree's modification times, not git history: right after
|
|
454
488
|
a clone or a checkout, every file it wrote counts as just changed. For help use `git sys1grep -h`: git takes `--help` itself and looks for a man page.
|
|
455
489
|
|
|
490
|
+
`--cached`, `--untracked` and a `<tree>...` choose what is searched instead of the working tree, as `git grep` has
|
|
491
|
+
them (only one of the three at a time):
|
|
492
|
+
|
|
493
|
+
```sh
|
|
494
|
+
$ git sys1grep --cached -e "a retry is attempted" # staged, uncommitted changes included
|
|
495
|
+
$ git sys1grep --untracked -e "a retry is attempted" # tracked files plus untracked ones (.gitignore still applies)
|
|
496
|
+
$ git sys1grep -n -e "a retry is attempted" main v0.3.1 -- '*.py'
|
|
497
|
+
main:src/job.py:42: retry(job, times=3)
|
|
498
|
+
v0.3.1:src/job.py:40: retry(job)
|
|
499
|
+
```
|
|
500
|
+
|
|
501
|
+
A `<tree>` (a branch, tag, commit or `@{u}`) is any argument before `--` that resolves as a revision; several may
|
|
502
|
+
be given, searched in the order given, each line prefixed with the name as typed (`@{u}:path`, not the branch it
|
|
503
|
+
resolves to). `--changed-within` needs the working tree: a blob (`--cached`, a `<tree>`) has no mtime of its own.
|
|
504
|
+
A `<tree>`'s own pathspec takes a glob too, same as elsewhere in sys1grep (`git diff-tree` against the empty
|
|
505
|
+
tree, not `git ls-tree`'s own literal/directory-prefix match); `--include` / `--exclude` (by name) still work.
|
|
506
|
+
**An old `<tree>` can hold a secret an ordinary file once carried and was later removed from**: the skip list
|
|
507
|
+
drops files by name only, not by what changed since.
|
|
508
|
+
|
|
456
509
|
### Everything that is *not* something
|
|
457
510
|
|
|
458
511
|
```sh
|
|
@@ -480,15 +533,15 @@ Matching records are printed NUL-terminated too, so pipe them through `tr '\0' '
|
|
|
480
533
|
File names (`-l`) and counts (`-c`) stay on newlines, as they do in grep. With `-z`, `-n` numbers records,
|
|
481
534
|
`-A` / `-B` / `-C` count neighbouring records, and `--chunk` counts records per request.
|
|
482
535
|
|
|
483
|
-
### One sentence at a time (`--sentence
|
|
536
|
+
### One sentence at a time (`--unit=sentence-by-*`)
|
|
484
537
|
|
|
485
|
-
`--sentence` judges each sentence instead of each line. The output is still lines, as in grep: every line a
|
|
538
|
+
`--unit=sentence-by-jev` (or `sentence-by-rule`, below) judges each sentence instead of each line. The output is still lines, as in grep: every line a
|
|
486
539
|
matching sentence touches is printed, and on a terminal the sentence itself is in bold yellow (a regex match inside it, in grep's bold red).
|
|
487
540
|
Wrapped lines are joined before splitting, so a sentence that runs over several lines is judged as one.
|
|
488
541
|
[`tests/prose.txt`](tests/prose.txt) wraps an English paragraph and a Japanese one:
|
|
489
542
|
|
|
490
543
|
```sh
|
|
491
|
-
$ ./sys1grep -n --sentence -e "the author admits they made a mistake" tests/prose.txt
|
|
544
|
+
$ ./sys1grep -n --unit=sentence-by-jev -e "the author admits they made a mistake" tests/prose.txt
|
|
492
545
|
1:I should have checked the input
|
|
493
546
|
2:before shipping, and that was my
|
|
494
547
|
3:mistake. Next time I will add a test
|
|
@@ -500,14 +553,14 @@ The sentence starts on line 1 and ends at `mistake.` on line 3; only that part i
|
|
|
500
553
|
On a terminal, with both meanings and `-C 3` for context, the colors show where each sentence starts and ends
|
|
501
554
|
inside a line: lines 3 and 9 are colored only up to the end of the matching sentence, and lines 4-7 are context (`-`):
|
|
502
555
|
|
|
503
|
-

|
|
556
|
+

|
|
504
557
|
|
|
505
558
|
`-o` prints only the matching sentences, one per line, as `grep -o` prints only the matching part.
|
|
506
559
|
`-n` then gives the line where the sentence starts. Japanese is joined without a space, as are Chinese,
|
|
507
560
|
Thai, Lao, Khmer, Myanmar and Tibetan, which do not put spaces between words:
|
|
508
561
|
|
|
509
562
|
```sh
|
|
510
|
-
$ ./sys1grep -n -o --sentence -e "the author admits they made a mistake" -e "customer is asking for a refund" tests/prose.txt
|
|
563
|
+
$ ./sys1grep -n -o --unit=sentence-by-jev -e "the author admits they made a mistake" -e "customer is asking for a refund" tests/prose.txt
|
|
511
564
|
1:I should have checked the input before shipping, and that was my mistake.
|
|
512
565
|
8:先週買った掃除機が初日から動かないので返金してほしいです。
|
|
513
566
|
```
|
|
@@ -515,20 +568,20 @@ $ ./sys1grep -n -o --sentence -e "the author admits they made a mistake" -e "cus
|
|
|
515
568
|
Without `-o`, `-c` counts lines and `-A` / `-B` / `-C` count lines, as usual. With `-o` they count sentences.
|
|
516
569
|
With `-z`, each record is split on its own and matching records are printed whole.
|
|
517
570
|
|
|
518
|
-
Jev finds a matching sentence inside a long line on its own, so `--sentence` is not needed for accuracy.
|
|
571
|
+
Jev finds a matching sentence inside a long line on its own, so `--unit=sentence-by-jev` is not needed for accuracy.
|
|
519
572
|
Use it to see which sentence matched, to get the sentences with `-o`, and when AND should hold within one
|
|
520
573
|
sentence: the expression is evaluated per sentence. For the same reason `-v X` alone prints every line
|
|
521
|
-
with at least one sentence that is not X; to find lines that are not X as a whole, leave `--
|
|
574
|
+
with at least one sentence that is not X; to find lines that are not X as a whole, leave `--unit` at `line`.
|
|
522
575
|
|
|
523
576
|
Japanese and Chinese entries often end without `。`: a chat message, a support ticket, a memo line. Joining
|
|
524
|
-
them would glue separate entries into one "sentence". So
|
|
525
|
-
`--sentence
|
|
577
|
+
them would glue separate entries into one "sentence". So with
|
|
578
|
+
`--unit=sentence-by-jev` sys1grep asks Jev about each unpunctuated break next to a script written without word
|
|
526
579
|
spaces: "does this line break end a sentence or entry, or is it a wrap inside a sentence?" It sends 30 lines
|
|
527
580
|
per request with one yes/no per break, and keeps the lines apart when the answer is 0.7 or more. On
|
|
528
581
|
[`tests/corpus.txt`](tests/corpus.txt) this keeps the four one-line Japanese tickets apart, so
|
|
529
|
-
`--sentence` finds the same refund requests (lines 14 and 18) as a line-by-line search, where the rules alone
|
|
582
|
+
`--unit=sentence-by-jev` finds the same refund requests (lines 14 and 18) as a line-by-line search, where the rules alone
|
|
530
583
|
merged the tickets and missed line 18. The extra requests cost about as much as one more meaning; use
|
|
531
|
-
`--sentence
|
|
584
|
+
`--unit=sentence-by-rule` to skip them. Breaks between English lines are never asked: joining them keeps a space,
|
|
532
585
|
and the full stop still ends the sentence.
|
|
533
586
|
|
|
534
587
|
Where a newline cannot be inside a sentence, lines are not joined: at a blank line, next to brackets or
|
|
@@ -538,6 +591,88 @@ own. Sentences are cut by `Intl.Segmenter` ([Unicode UAX #29](https://unicode.or
|
|
|
538
591
|
which splits at `.` `!` `?` `。` `!` `?` but also after abbreviations such as `Mr.`. Logs are not prose:
|
|
539
592
|
consecutive log lines that start with a letter, such as `WARN ...` after `ERROR ...`, get joined.
|
|
540
593
|
|
|
594
|
+
### One function at a time (`--unit=function`)
|
|
595
|
+
|
|
596
|
+
In code the question is usually about a function ("retries on network failure"), and one line of it rarely
|
|
597
|
+
says so. `--unit=function` judges each function and prints its lines, with `-n` giving file line numbers:
|
|
598
|
+
|
|
599
|
+
```sh
|
|
600
|
+
$ ./sys1grep -n --unit=function -e "retries on network failure" src/net.js
|
|
601
|
+
src/net.js:3:async function fetchWithRetry(url) {
|
|
602
|
+
src/net.js:4: for (let i = 0; i < 5; i++) {
|
|
603
|
+
src/net.js:5: try { return await fetch(url); }
|
|
604
|
+
src/net.js:6: catch { await sleep(2 ** i * 100); }
|
|
605
|
+
src/net.js:7: }
|
|
606
|
+
src/net.js:8:}
|
|
607
|
+
```
|
|
608
|
+
|
|
609
|
+
A function runs from a funcname line to the line before the next one, as `git grep -W` finds it; lines
|
|
610
|
+
before the first funcname line are one unit, and blank lines at a function's end are not printed. The
|
|
611
|
+
funcname lines are git's: the `diff=<driver>` attribute in `.gitattributes`, then `diff.<driver>.xfuncname`
|
|
612
|
+
in git config. Without one, sys1grep has its own rule for JavaScript/TypeScript (a top-level `function`,
|
|
613
|
+
`class`, or `const` / `let` / `var` bound to a function) and Python (`def` / `class`, nested ones too); a decorator line (`@retry`) starts
|
|
614
|
+
the function it decorates. Otherwise it uses git's default: a line starting with a letter, `_` or `$`. git's own builtin drivers
|
|
615
|
+
(`diff=python` and so on) are not read, so a driver without an `xfuncname` falls back the same way:
|
|
616
|
+
|
|
617
|
+
```sh
|
|
618
|
+
$ printf '*.go diff=golang\n' >> .gitattributes
|
|
619
|
+
$ git config diff.golang.xfuncname '^(func|type)[[:space:]]'
|
|
620
|
+
```
|
|
621
|
+
|
|
622
|
+
`-M` defaults to 8000 characters here, as with `-z`; a longer function is judged on its first 8000.
|
|
623
|
+
`--chunk` counts functions, `-A`/`-B`/`-C` and `-c` count lines, as with the sentence units. `-z`, `-g` and
|
|
624
|
+
`-o` are refused.
|
|
625
|
+
|
|
626
|
+
### From one function to another (`--step-to`)
|
|
627
|
+
|
|
628
|
+
A question about code often comes in two steps: where does `--summarize` hand the lines to the tool, and then,
|
|
629
|
+
what happens when that fails. The "that" in the second step is what the first one found. Multi-step matching
|
|
630
|
+
answers both in one run: the expression before `--step-to` finds the start, sys1grep walks the calls from it,
|
|
631
|
+
and the expression after `--step-to` picks the functions it reaches that match. Jev judges the two
|
|
632
|
+
expressions; no score decides which call to follow.
|
|
633
|
+
|
|
634
|
+
```sh
|
|
635
|
+
$ sys1grep -e "--summarize hands the lines to the tool" --step-to "what happens when the tool fails to start or answer" sys1grep.mjs
|
|
636
|
+
```
|
|
637
|
+
|
|
638
|
+
A meaning may start with a dash, as the first one does, on any search and for `-e`, `-a`, `-v` and `-Q`: a value that starts with `-` is taken as the meaning unless it is an option (an option holds no space).
|
|
639
|
+
|
|
640
|
+
Each path prints as a tree: the hop (the number of calls from the start), `file:line` and the function's name,
|
|
641
|
+
`:` after an end and `-` after a function on the way, as grep marks a match and its context. A regex-only
|
|
642
|
+
start and end send nothing, which makes a free reachability search. In Python 3.6's `json`, the functions
|
|
643
|
+
that `load` reaches and that hold a `raise`:
|
|
644
|
+
|
|
645
|
+
```
|
|
646
|
+
$ cd /usr/lib64/python3.6/json && sys1grep -e '/^\s*def load\b/' --step-to '/\braise\b/' *.py
|
|
647
|
+
sys1grep: walk: 1 at hop 0, 1 at hop 1, 3 at hop 2, 1 at hop 3, 1 at hop 4, 1 at hop 5; stopped: no new unit
|
|
648
|
+
0 __init__.py-274-load
|
|
649
|
+
1 __init__.py:302:loads
|
|
650
|
+
2 decoder.py:334:decode
|
|
651
|
+
3 decoder.py:345:raw_decode
|
|
652
|
+
4 scanner.py-65-scan_once
|
|
653
|
+
5 scanner.py:28:_scan_once
|
|
654
|
+
```
|
|
655
|
+
|
|
656
|
+
- **The expressions.** The expression before `--step-to` is the start's, as in any search; every `-e` / `-a` /
|
|
657
|
+
`-v` / `-Q` after it is the end's. `--step-to MEANING` and `--step-to '/RE/'` are `--step-to -e ...`; the file
|
|
658
|
+
names follow. A step is a search, not a hop: one `--step-to` is two steps, whatever the hops between them.
|
|
659
|
+
- **The edges.** Without `--edges` the unit is a function (`--unit=function`) and an edge is a call: `name(` in
|
|
660
|
+
a function's body, its comments, docstrings and strings left out, links to every function of that name. A name
|
|
661
|
+
comes from the funcname line (the `def` under a decorator). `--edges=FILE` walks any other relation instead,
|
|
662
|
+
one edge per line, `FROM_FILE:LINE<TAB>FROM_NAME<TAB>TO_FILE:LINE<TAB>TO_NAME`; a line stands for the unit that
|
|
663
|
+
holds it. `--reverse` walks the edges backwards, callee to caller.
|
|
664
|
+
- **The walk.** Breadth first from every start at once. A function's hop is its shortest distance from any
|
|
665
|
+
start, and each function is reached once, so a cycle ends. `--hops=N`, `--hops=M..N` and `--hops=M..` pick the
|
|
666
|
+
hops an end may be at (default `0..`: a start that matches the end expression is an end at hop 0). A path that
|
|
667
|
+
reaches no end is left out. stderr says how many functions each hop reached and why the walk stopped: no new
|
|
668
|
+
function, or `--hops`.
|
|
669
|
+
- **The cost.** Jev judges every function against the start expression in one batch, then the functions reached
|
|
670
|
+
within `--hops` against the end expression in another. `--max-cost` asks again before the second, counting the
|
|
671
|
+
first. `--dry-run` answers every question 0, so it walks from every function the start expression could hold
|
|
672
|
+
for and shows that bound.
|
|
673
|
+
- **Not with** `-z`, `-g`, `-o`, `-c`, `-l`, `-A`/`-B`/`-C`, `--rank`, `--summarize` or `--dedup`. `-p` adds the
|
|
674
|
+
end expression's scores to each function judged against it; `-q` prints nothing and exits 0 when there is an end.
|
|
675
|
+
|
|
541
676
|
### One line per template (`--dedup`)
|
|
542
677
|
|
|
543
678
|
Cost is proportional to the text sent, and machine-generated logs are mostly one skeleton with a different
|
|
@@ -545,6 +680,19 @@ id or number in it. `--dedup` masks ids, hashes, numbers, dates and times, paths
|
|
|
545
680
|
the result, sends one line per group and reuses its answer for the rest. What is sent is that line's
|
|
546
681
|
original text, and every line is printed as itself:
|
|
547
682
|
|
|
683
|
+
`--dedup` takes `auto`, `always` or `never`; a bare `--dedup` is `--dedup=always`. **Default is `never`, for
|
|
684
|
+
now** (#143): `auto` is held back until real-run stats say it should be the default. Every run still
|
|
685
|
+
estimates locally, before anything is sent, what folding every kind (the best case) would save, and prints
|
|
686
|
+
one stderr hint when that is worth at least twice the cost of `--dedup`'s own question:
|
|
687
|
+
|
|
688
|
+
```
|
|
689
|
+
$ sys1grep -e "a request failed" app.log
|
|
690
|
+
sys1grep: 2040 units fold to at most 31 templates; --dedup=auto would save ~68 requests (~44k tokens)
|
|
691
|
+
```
|
|
692
|
+
|
|
693
|
+
`--dedup=auto` asks and folds only when that estimate says it pays; a file that barely repeats sends every
|
|
694
|
+
line, asking nothing extra. `--verbose` / `--dry-run` print the decision and its numbers for every value.
|
|
695
|
+
|
|
548
696
|
```sh
|
|
549
697
|
$ ./sys1grep --dedup -n -e "a request failed" app.log
|
|
550
698
|
1:worker request 3fa9c1e27b failed: connection reset
|
|
@@ -561,7 +709,7 @@ could change a match, and those kinds are kept apart (one of the two requests ab
|
|
|
561
709
|
Measured on real logs, with nothing kept: a 43,071-line system log folds into 550 templates (1.2% of the
|
|
562
710
|
bytes), `install.log` to 21.1%, a Claude Code transcript (jsonl) only to 56.8%. It is for machine-generated
|
|
563
711
|
logs; prose has no shared skeleton, and a meaning that reads a timestamp folds almost nothing. With `-z` or
|
|
564
|
-
`--sentence
|
|
712
|
+
`--unit=sentence-by-*` the records or sentences fold instead of lines.
|
|
565
713
|
|
|
566
714
|
After a search, the summary line says what the folding saved: `(2 folded by --dedup, ~235 input tokens /
|
|
567
715
|
~$0.000010 saved, 21%)` above. It estimates what the folded lines would have cost as requests of their own and subtracts
|
|
@@ -589,15 +737,133 @@ The expensive model reads only what Jev kept. Asking why `./.env` is no longer r
|
|
|
589
737
|
`git log` (168 commits), Claude's input fell from 22,059 tokens to 1,356, the total cost with Jev's from
|
|
590
738
|
$0.094 to $0.013, with the same answer (#69). It pays when the answer sits in a few lines.
|
|
591
739
|
|
|
592
|
-
- The matching lines leave the machine a second time, to Anthropic.
|
|
740
|
+
- The matching lines leave the machine a second time, to Anthropic (or whatever TOOL talks to).
|
|
593
741
|
- `-n`, `-A/-B/-C`, `-p` and file names go in as they would print; colors never do. No match runs nothing (exit 1).
|
|
594
|
-
- `SYS1GREP_SUMMARIZER` picks the TOOL of a bare `--summarize
|
|
742
|
+
- `SYS1GREP_SUMMARIZER` picks the TOOL of a bare `--summarize`, `SYS1GREP_SUMMARIZER_MODEL` its model.
|
|
595
743
|
- `-q`, `-l` and `-c` print no lines, so they cannot be combined with it.
|
|
744
|
+
- In `SYS1GREP_OPTS` it summarizes every search, so every match goes to TOOL's provider too;
|
|
745
|
+
`--no-summarize` turns it off for one search.
|
|
746
|
+
- The answer is plain text: an LLM that is not told writes Markdown, noise on a terminal, so plain is asked for.
|
|
747
|
+
`--format=markdown` or `=html` asks for those instead (`… --format=html … > summary.html`).
|
|
748
|
+
The answer prints as it comes, unchecked. In `SYS1GREP_OPTS` it is a standing preference, ignored without `--summarize` or `--rank`.
|
|
596
749
|
- With `--dedup`, the TOOL gets what Jev got: each template's representative once, marked `(×N like it)`,
|
|
597
750
|
not every line its answer was reused for.
|
|
598
751
|
- More than 200 KB (about 50k tokens) is not sent at all: exit 2, with the size, before the TOOL is paid.
|
|
599
752
|
It is never cut short, since a summary of the first part would read as a summary of all of it.
|
|
600
753
|
|
|
754
|
+
`--summarize-prompt=TEXT` adds your own instruction after the fixed one (how long, what to focus on):
|
|
755
|
+
|
|
756
|
+
```sh
|
|
757
|
+
$ sys1grep -r -n --summarize --summarize-prompt="3 lines or fewer, just which file to fix" \
|
|
758
|
+
-e "the API key is read from a file" .
|
|
759
|
+
```
|
|
760
|
+
|
|
761
|
+
It needs `--summarize`; empty TEXT is the same as leaving it out. It comes after the `--format` sentence,
|
|
762
|
+
so TEXT can override the format.
|
|
763
|
+
|
|
764
|
+
Other TOOLs:
|
|
765
|
+
|
|
766
|
+
```sh
|
|
767
|
+
$ sys1grep --summarize=llm -e "..." FILE # Simon Willison's llm, tools off (no -T given)
|
|
768
|
+
$ sys1grep --summarize=pi -e "..." FILE # pi --print --no-tools --no-session ...
|
|
769
|
+
$ SYS1GREP_SUMMARIZER_MODEL=qwen3.5:9b sys1grep --summarize=ollama -e "..." FILE
|
|
770
|
+
$ SYS1GREP_SUMMARIZER_MODEL=some-id sys1grep --summarize=lmstudio -e "..." FILE # model id from GET /v1/models
|
|
771
|
+
$ SYS1GREP_SUMMARIZER_MODEL=some-id sys1grep --summarize=http://localhost:8080/v1 -e "..." FILE # llama.cpp, vLLM, LocalAI, a gateway
|
|
772
|
+
```
|
|
773
|
+
|
|
774
|
+
`ollama` and `lmstudio` talk to a local OpenAI-compatible server (`POST /v1/chat/completions`) by `fetch`, no
|
|
775
|
+
CLI: the matching lines never leave the machine a second time. A plain `http(s)://` URL is any other
|
|
776
|
+
OpenAI-compatible server. All three need `SYS1GREP_SUMMARIZER_MODEL`: none has a default model.
|
|
777
|
+
`SYS1GREP_SUMMARIZER_API_KEY` goes as `Authorization: Bearer` to a URL TOOL only (never `SYS1GREP_API_KEY`,
|
|
778
|
+
which is Jev's). `OLLAMA_HOST` moves ollama's host, as it does for the `ollama` CLI itself.
|
|
779
|
+
|
|
780
|
+
### Best first (`--rank`)
|
|
781
|
+
|
|
782
|
+
Matches print in file order, as grep prints them. With many, `--rank` prints the results best first, each under a
|
|
783
|
+
numbered header. A result is a match with its `-A/-B/-C` lines; matches whose context touches are one result.
|
|
784
|
+
|
|
785
|
+
```sh
|
|
786
|
+
$ sys1grep -n -C1 --rank -e "the refund was refused" tickets/
|
|
787
|
+
1. tickets/b.txt
|
|
788
|
+
tickets/b.txt-2-Order #1234, placed 2026-08-01.
|
|
789
|
+
tickets/b.txt:3:Your refund was declined: the order is older than 30 days.
|
|
790
|
+
tickets/b.txt-4-Please contact support.
|
|
791
|
+
|
|
792
|
+
2. tickets/a.txt
|
|
793
|
+
tickets/a.txt-11-Order #88 arrived.
|
|
794
|
+
tickets/a.txt:12:Refund request received.
|
|
795
|
+
tickets/a.txt-13-We will check it.
|
|
796
|
+
```
|
|
797
|
+
|
|
798
|
+
- `--rank` is `--rank=jev`: after the search, Jev is asked of each result, lines and context together, whether it
|
|
799
|
+
is relevant to the meanings that are not negated. One more question per result, `--chunk` results to a request;
|
|
800
|
+
`--dry-run` / `-i` show an upper bound, since which results there are is known only after the search.
|
|
801
|
+
- `--rank=match` sorts by each result's highest match probability, with no request. It is the answer to a yes/no
|
|
802
|
+
question on one line, so clear matches sit close together, and the context is not read.
|
|
803
|
+
- `-p` puts the score on the header (`1. [0.96] tickets/b.txt`). `-l` lists the files by their best result.
|
|
804
|
+
- `--format=markdown` writes a `## 1. tickets/b.txt` heading and a fenced block per result; `--format=html` one
|
|
805
|
+
self-contained HTML document from a template (below), light and dark, with a relevance bar per result. The lines
|
|
806
|
+
are as they print, escaped and uncolored.
|
|
807
|
+
- `--summarize` gets the results in ranked order; with `--dedup` a representative is one result.
|
|
808
|
+
- It needs a meaning (a regex, `!` or `-v` alone ranks nothing), and cannot be combined with `-c`, `-o` or `-q`.
|
|
809
|
+
`--no-rank` turns off an earlier one, from `SYS1GREP_OPTS` say.
|
|
810
|
+
|
|
811
|
+
### HTML templates (`--template`)
|
|
812
|
+
|
|
813
|
+
`--rank --format=html` and `--summarize --format=html` fill in a template. Four come with sys1grep: `default`, `print` (black on white, serif,
|
|
814
|
+
for paper and PDF), `search` (a results page like a web search engine's) and `terminal` (dark monospace).
|
|
815
|
+
`--template=NAME` picks one; `SYS1GREP_TEMPLATE`, in the
|
|
816
|
+
environment or `~/.config/sys1grep/.env`, sets the default.
|
|
817
|
+
|
|
818
|
+
```sh
|
|
819
|
+
$ sys1grep -r -n -C1 --rank -p --format=html --template=print -e "the refund was refused" tickets/ > refunds.html
|
|
820
|
+
```
|
|
821
|
+
|
|
822
|
+
A template is one HTML file. The part between `<!--result-->` and `<!--/result-->` is repeated for each result;
|
|
823
|
+
what is before and after it is written once. sys1grep replaces these, escaped, and leaves any other `{{…}}` as
|
|
824
|
+
written:
|
|
825
|
+
|
|
826
|
+
| placeholder | where | value |
|
|
827
|
+
|---|---|---|
|
|
828
|
+
| `{{title}}` | anywhere | `sys1grep: ` and the meanings |
|
|
829
|
+
| `{{query}}` | anywhere | the meanings, as in `"refund" and not "policy"` |
|
|
830
|
+
| `{{count}}` | anywhere | the number of results (with `--summarize`, of matching lines) |
|
|
831
|
+
| `{{answer}}` | anywhere | `--summarize`'s answer; empty with `--rank` |
|
|
832
|
+
| `{{rank}}` | per result | 1, 2, … |
|
|
833
|
+
| `{{score}}` | per result | the score as `-p` prints it (`0.96`); empty without `-p` |
|
|
834
|
+
| `{{score_pct}}` | per result | the score as a whole number 0 to 100, set even without `-p` (for a bar: `style="--s:{{score_pct}}"`) |
|
|
835
|
+
| `{{file}}` | per result | the file name; empty when one file is searched |
|
|
836
|
+
| `{{lines}}` | per result | the result's lines as they print |
|
|
837
|
+
|
|
838
|
+
`--template=NAME` reads `~/.config/sys1grep/templates/NAME.html` if it is there, else the bundled one, so your
|
|
839
|
+
copy of `default` replaces it. A value containing `/` or ending in `.html` is a file path. To make your own, copy
|
|
840
|
+
the bundled ones and edit:
|
|
841
|
+
|
|
842
|
+
```sh
|
|
843
|
+
$ sys1grep --install-templates # "copied PATH" for each file
|
|
844
|
+
$ cp ~/.config/sys1grep/templates/default.html ~/.config/sys1grep/templates/team.html
|
|
845
|
+
$ sys1grep -r --rank --format=html --template=team -e "..." src/ > out.html
|
|
846
|
+
$ sys1grep --template=list # one name a line, "(user)" after those under ~/.config
|
|
847
|
+
```
|
|
848
|
+
|
|
849
|
+
`{{score_pct}}` is set even without `-p`, so a bar can show the relevance without printing the number.
|
|
850
|
+
|
|
851
|
+
With `--summarize --format=html` the TOOL is asked for plain text, and that answer, escaped, goes into `{{answer}}`
|
|
852
|
+
once it has finished; the part between `<!--result-->` and `<!--/result-->` is not written. A template for
|
|
853
|
+
`--summarize` therefore needs `{{answer}}` outside that part (the bundled ones have it; with `--rank` it is empty).
|
|
854
|
+
|
|
855
|
+
```sh
|
|
856
|
+
$ sys1grep -r --summarize --format=html --template=print -e "why the refunds were refused" tickets/ > why.html
|
|
857
|
+
```
|
|
858
|
+
|
|
859
|
+
A template is trusted local input: a path you give, or a symlink in the templates directory, is read as it is;
|
|
860
|
+
only the results put into it are escaped.
|
|
861
|
+
|
|
862
|
+
`--install-templates` never overwrites a file that is already there (it prints `kept` for it). A missing
|
|
863
|
+
template, or one without exactly one `<!--result-->` before one `<!--/result-->`, is exit 2 naming the file.
|
|
864
|
+
`--template` on the command line needs `--format=html` with `--rank` or `--summarize` (not `-l`); `SYS1GREP_TEMPLATE` is simply unused elsewhere.
|
|
865
|
+
`--template=list` and `--install-templates` take no other arguments and are refused in `SYS1GREP_OPTS`.
|
|
866
|
+
|
|
601
867
|
## Use it from Claude Code
|
|
602
868
|
|
|
603
869
|
There is a Claude Code skill that runs sys1grep for you: describe what you are looking for in plain words
|
|
@@ -651,7 +917,11 @@ usage: sys1grep [OPTION]... -e MEANING|-Q QUESTION [-a MEANING] [-v MEANING]...
|
|
|
651
917
|
-T THRESH negative threshold: "not X" when probability < THRESH (overrides --level)
|
|
652
918
|
with -t 0.6 -T 0.3 a line at 0.3..0.6 matches neither X nor not-X
|
|
653
919
|
-r recurse into directories (current directory when FILE is omitted);
|
|
654
|
-
skips .git, node_modules, binary files, likely secrets and what git ignores
|
|
920
|
+
skips .git, node_modules, binary files, likely secrets, generated files and what git ignores
|
|
921
|
+
--cached git sys1grep only: search the index instead of the working tree, as git grep --cached
|
|
922
|
+
--untracked git sys1grep only: also search untracked files (.gitignore still applies), as git grep --untracked
|
|
923
|
+
<tree>... git sys1grep only: a branch, tag, commit or @{u} before -- searches that revision's tree
|
|
924
|
+
instead, as git grep does; output is prefixed <tree>: with the name as typed
|
|
655
925
|
--include=GLOB, --exclude=GLOB with -r and git sys1grep, only files whose name matches GLOB, or not
|
|
656
926
|
(with -r a file named on the command line is always searched; git sys1grep's pathspecs are filtered)
|
|
657
927
|
--changed-within=WHEN with -r and git sys1grep, only files modified within 30m / 2h / 7d / 2w, since a date
|
|
@@ -670,14 +940,38 @@ usage: sys1grep [OPTION]... -e MEANING|-Q QUESTION [-a MEANING] [-v MEANING]...
|
|
|
670
940
|
on ambiguous lines, not just speed
|
|
671
941
|
-j N concurrent requests (default 8)
|
|
672
942
|
-n print line numbers
|
|
673
|
-
|
|
674
|
-
--
|
|
675
|
-
|
|
943
|
+
--unit=UNIT the unit of judgement: line (default), zero, sentence-by-jev, sentence-by-rule or function
|
|
944
|
+
-z, --null-data --unit=zero: judge NUL-terminated records instead of lines, and print them NUL-terminated (see "Records that span several lines" above);
|
|
945
|
+
with --unit=sentence-by-*, each record is split into sentences
|
|
946
|
+
--unit=sentence-by-jev|sentence-by-rule judge each sentence instead of each line (see "One sentence at a time" above)
|
|
947
|
+
--unit=function judge each function and print its lines (see "One function at a time" above)
|
|
948
|
+
--step-to EXPRESSION print the paths from the functions the expression before it matches, along the
|
|
949
|
+
calls, to the functions the expression after it matches (see "From one function to another" above)
|
|
950
|
+
--edges=FILE walk the edges in FILE instead of the calls; --reverse walks them backwards
|
|
951
|
+
--hops=N|M..N|M.. the hops an end may be at (default 0..)
|
|
952
|
+
-o with --unit=sentence-by-*, print only the matching sentences
|
|
676
953
|
-p print each meaning's probability at the end of the line
|
|
677
|
-
--dry-run send nothing; print the
|
|
954
|
+
--dry-run send nothing; print the settings the search would run with (and their source, when not the
|
|
955
|
+
command line), each file searched and each request with its questions
|
|
678
956
|
--verbose print the same to stderr while searching
|
|
679
957
|
-i, --interactive show what --dry-run would send, and search only after y on the terminal
|
|
680
|
-
|
|
958
|
+
-M NUM, --max-columns=NUM send at most the first NUM characters of a line (or record, with -z); still
|
|
959
|
+
searched and judged, just not past that cutoff (default 2000, 8000 with -z)
|
|
960
|
+
--max-filesize=SIZE size every target first (K/M/G, default 10M); over it, skip it outright, like rg's own
|
|
961
|
+
--max-filesize (-y does not affect it). stdin is sized once read, and skipped the same way if
|
|
962
|
+
it is over; a --cached / <tree>: target is a blob, sized from its content; -g's commits stay
|
|
963
|
+
out of this (each is already bounded by -M when sent)
|
|
964
|
+
--max-cost=USD price the input about to be sent; over it (default 1), ask to continue on the terminal; -y
|
|
965
|
+
answers yes without asking; no terminal and the limit exceeded is exit 2, unchanged by -q
|
|
966
|
+
(scripts pass -y); -i already asks unconditionally and earlier, so this does not ask again
|
|
967
|
+
--dedup[=auto|always|never] judge one line per template and reuse its answer for the rest (default never,
|
|
968
|
+
for now; see "One line per template" above)
|
|
969
|
+
--rank[=jev|match] print the results (a match with its context) best first, under numbered headers; jev
|
|
970
|
+
(bare --rank) asks Jev of each result, match sorts by its best match probability (see "Best first"); --no-rank: file order
|
|
971
|
+
--template=NAME the document --rank or --summarize --format=html writes: ~/.config/sys1grep/templates/NAME.html, else the
|
|
972
|
+
bundled one (default, print, terminal), or a file; default SYS1GREP_TEMPLATE, else default.
|
|
973
|
+
--template=list prints the names (see "HTML templates")
|
|
974
|
+
--install-templates copy the bundled templates to ~/.config/sys1grep/templates, keeping existing files
|
|
681
975
|
--color[=WHEN] auto (default: color when stdout is a terminal) / always / never; bare --color means auto
|
|
682
976
|
file and line number use grep's colors; with -p, probabilities are
|
|
683
977
|
green at or above the positive threshold, red below the negative one,
|
|
@@ -766,14 +1060,14 @@ fmt -w 100000 essay.txt | sys1grep -n -e "the author admits they made a mistake"
|
|
|
766
1060
|
|
|
767
1061
|
Each output line is then a whole paragraph, and `-n` counts the lines of `fmt`'s output, not of the file.
|
|
768
1062
|
`fmt` puts a space where it joins two Japanese lines; Jev reads through it. To judge sentences across
|
|
769
|
-
wrapped lines you need neither: `--sentence` already joins them.
|
|
1063
|
+
wrapped lines you need neither: `--unit=sentence-by-jev` already joins them.
|
|
770
1064
|
|
|
771
1065
|
### How do I search a JSONL chat log one message at a time?
|
|
772
1066
|
|
|
773
1067
|
Take the text out with `jq` and end each message with NUL, then judge records with `-z`:
|
|
774
1068
|
|
|
775
1069
|
```sh
|
|
776
|
-
jq -j '.content + "\u0000"' chat.jsonl | sys1grep -z -n --sentence -e "the customer is asking for a refund" | tr '\0' '\n'
|
|
1070
|
+
jq -j '.content + "\u0000"' chat.jsonl | sys1grep -z -n --unit=sentence-by-jev -e "the customer is asking for a refund" | tr '\0' '\n'
|
|
777
1071
|
```
|
|
778
1072
|
|
|
779
1073
|
Adjust `.content` to where your log keeps the text. Each message becomes one record, so a sentence never
|
package/docs/sentence.svg
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<svg xmlns="http://www.w3.org/2000/svg" width="808" height="240" font-family="SFMono-Regular,Menlo,Consolas,monospace" font-size="13">
|
|
2
2
|
<rect width="100%" height="100%" rx="6" fill="#1e1e2e"/>
|
|
3
|
-
<text x="16" y="29" xml:space="preserve"><tspan fill="#cdd6f4">$ sys1grep -n --sentence -C 3 --color \</tspan></text>
|
|
3
|
+
<text x="16" y="29" xml:space="preserve"><tspan fill="#cdd6f4">$ sys1grep -n --unit=sentence-by-jev -C 3 --color \</tspan></text>
|
|
4
4
|
<text x="16" y="49" xml:space="preserve"><tspan fill="#cdd6f4"> -e "the author admits they made a mistake" -e "customer is asking for a refund" tests/prose.txt</tspan></text>
|
|
5
5
|
<text x="16" y="69" xml:space="preserve"><tspan fill="#8ce99a">1</tspan><tspan fill="#66d9e8">:</tspan><tspan fill="#ffd43b" font-weight="700">I should have checked the input</tspan></text>
|
|
6
6
|
<text x="16" y="89" xml:space="preserve"><tspan fill="#8ce99a">2</tspan><tspan fill="#66d9e8">:</tspan><tspan fill="#ffd43b" font-weight="700">before shipping, and that was my</tspan></text>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@uehaj/sys1grep",
|
|
3
|
-
"version": "0.5.0-next.
|
|
3
|
+
"version": "0.5.0-next.2",
|
|
4
4
|
"description": "grep by meaning, across languages. TypeSafe Jev scores every line against a meaning; combine meanings with AND/OR/NOT.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"grep",
|
|
@@ -29,7 +29,11 @@
|
|
|
29
29
|
"README.ja.md",
|
|
30
30
|
"docs/color.svg",
|
|
31
31
|
"docs/demo.svg",
|
|
32
|
-
"docs/sentence.svg"
|
|
32
|
+
"docs/sentence.svg",
|
|
33
|
+
"templates/default.html",
|
|
34
|
+
"templates/print.html",
|
|
35
|
+
"templates/search.html",
|
|
36
|
+
"templates/terminal.html"
|
|
33
37
|
],
|
|
34
38
|
"engines": {
|
|
35
39
|
"node": ">=20.16"
|
|
@@ -39,6 +43,8 @@
|
|
|
39
43
|
"test:offline": "sh tests/offline.sh",
|
|
40
44
|
"judge": "node --no-warnings --env-file-if-exists=.env tests/judge.mts",
|
|
41
45
|
"scope-eval": "node tests/scope-eval.mjs",
|
|
46
|
+
"dedup-eval": "node tests/dedup-eval.mjs",
|
|
47
|
+
"skill-eval": "bash tests/skill-eval/run.sh",
|
|
42
48
|
"release": "sh scripts/release.sh"
|
|
43
49
|
}
|
|
44
50
|
}
|