@uehaj/sys1grep 0.5.0-next.1 → 0.5.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/README.ja.md +521 -95
- package/README.md +522 -93
- package/examples/app.js +49 -0
- package/examples/app.py +54 -0
- package/examples/logs.txt +19 -0
- package/examples/prose.txt +10 -0
- package/examples/tickets.txt +9 -0
- package/lib/sentences.mjs +49 -0
- package/options.mjs +101 -0
- package/package.json +29 -5
- package/serve.mjs +1121 -0
- package/settings.mjs +58 -0
- package/sys1grep.mjs +1260 -442
- package/templates/default.html +102 -0
- package/templates/print.html +51 -0
- package/templates/search.html +114 -0
- package/templates/terminal.html +57 -0
package/examples/app.js
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
// A small demo service: an entry point calls through a few layers down to where things can go wrong.
|
|
2
|
+
export async function main() {
|
|
3
|
+
await handleRequest();
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
async function handleRequest() {
|
|
7
|
+
try {
|
|
8
|
+
await processOrder();
|
|
9
|
+
} catch (err) {
|
|
10
|
+
throw err;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
async function processOrder() {
|
|
15
|
+
await chargeCard();
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
async function chargeCard() {
|
|
19
|
+
if (!cardIsValid()) fatal('card declined');
|
|
20
|
+
throw new Error('card declined');
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function cardIsValid() {
|
|
24
|
+
return false;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function fatal(message) {
|
|
28
|
+
console.error(message);
|
|
29
|
+
process.exit(1);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// reads the API key from the environment first, then a local settings file
|
|
33
|
+
function loadApiKey() {
|
|
34
|
+
return process.env.SYS1GREP_API_KEY || readSettingsFile().apiKey;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function readSettingsFile() {
|
|
38
|
+
return { apiKey: '' };
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function tryOptionalStep() {
|
|
42
|
+
try {
|
|
43
|
+
riskyStep();
|
|
44
|
+
} catch (err) {} // ignored: this step is optional and failing it is not fatal
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function riskyStep() {
|
|
48
|
+
return true;
|
|
49
|
+
}
|
package/examples/app.py
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"""A small demo CLI: the same kind of call chain as app.js, in Python."""
|
|
2
|
+
import os
|
|
3
|
+
import sys
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def main():
|
|
7
|
+
run_job()
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def run_job():
|
|
11
|
+
try:
|
|
12
|
+
execute()
|
|
13
|
+
except Exception:
|
|
14
|
+
raise
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def execute():
|
|
18
|
+
if not is_ready():
|
|
19
|
+
raise RuntimeError('not ready')
|
|
20
|
+
charge()
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def is_ready():
|
|
24
|
+
return False
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def charge():
|
|
28
|
+
if not confirm_charge():
|
|
29
|
+
sys.exit(1)
|
|
30
|
+
raise RuntimeError('charge failed')
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def confirm_charge():
|
|
34
|
+
return False
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def load_api_key():
|
|
38
|
+
# reads the API key from the environment first, then a local settings file
|
|
39
|
+
return os.environ.get('SYS1GREP_API_KEY') or read_settings_file()
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def read_settings_file():
|
|
43
|
+
return ''
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def try_optional_step():
|
|
47
|
+
try:
|
|
48
|
+
risky_step()
|
|
49
|
+
except Exception:
|
|
50
|
+
pass # ignored: optional, not fatal
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def risky_step():
|
|
54
|
+
return True
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
2026-01-05 03:12:01 INFO server started, listening on port 8080
|
|
2
|
+
2026-01-05 03:12:04 INFO connected to the primary database
|
|
3
|
+
2026-01-05 03:14:40 WARN slow query took 2800ms: SELECT * FROM orders WHERE status = 'pending'
|
|
4
|
+
2026-01-05 03:20:11 ERROR connection reset by peer while calling the payment service
|
|
5
|
+
2026-01-05 03:20:12 INFO retrying the payment request (attempt 2 of 3)
|
|
6
|
+
2026-01-05 03:20:18 ERROR timeout after 5000ms waiting for the payment service
|
|
7
|
+
2026-01-05 03:20:19 INFO retrying the payment request (attempt 3 of 3)
|
|
8
|
+
2026-01-05 03:20:25 FATAL giving up after 3 attempts: the payment service is unreachable
|
|
9
|
+
2026-01-05 04:02:00 INFO nightly backup completed
|
|
10
|
+
2026-01-05 04:05:30 WARN disk usage at 92% on /var/log
|
|
11
|
+
2026-01-05 04:10:44 ERROR DNS lookup failed for the billing API
|
|
12
|
+
2026-01-05 04:10:46 INFO DNS lookup succeeded on retry
|
|
13
|
+
2026-01-05 05:00:00 ERROR health check failed: no response from the application server
|
|
14
|
+
2026-01-05 05:00:05 ERROR health check failed again: no response from the application server
|
|
15
|
+
2026-01-05 05:00:10 FATAL marking the application server as down after 2 failed health checks
|
|
16
|
+
2026-01-05 05:30:00 INFO the application server is back and answering health checks
|
|
17
|
+
2026-01-05 06:00:00 WARN an admin ran DROP TABLE sessions; on the staging database by mistake
|
|
18
|
+
2026-01-05 06:05:00 INFO restored the sessions table from last night's backup
|
|
19
|
+
2026-01-05 07:00:00 INFO it is a quiet morning, nothing else to report
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
I have to admit, the bug was my fault: I shipped the migration without testing it on a copy of production data first.
|
|
2
|
+
Looking back, I should have reviewed the diff more carefully before merging; the mistake was mine, not the reviewer's.
|
|
3
|
+
The weather today is mild, with a light breeze from the west and clear skies through the evening.
|
|
4
|
+
Lunch today was a simple rice bowl with grilled vegetables from the place around the corner.
|
|
5
|
+
Next Friday is a public holiday, so the office will be closed for the long weekend.
|
|
6
|
+
会議は明日の10時からに変更になりました。資料は前日までに共有してください。
|
|
7
|
+
今日の天気は晴れ、最高気温は28度の見込みです。
|
|
8
|
+
料金プランの変更方法については、設定画面の「プラン」から行えます。
|
|
9
|
+
振り返ると、あのバグは自分のミスでした。テストを十分にせずにリリースしてしまったのが原因です。
|
|
10
|
+
新機能のリリースは来月に延期されましたが、理由は品質を優先したためです。
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
Ticket #4821 (support, EN): I want my money back. The item arrived broken and nobody answered my emails.
|
|
2
|
+
Ticket #4822 (support, EN): Could you change the delivery address for order #910? I moved last week.
|
|
3
|
+
Ticket #4823 (support, EN): This is infuriating. Three days with no reply and my account is still locked.
|
|
4
|
+
Ticket #4824 (support, EN): Thanks so much, the replacement part fixed it and the team was lovely.
|
|
5
|
+
Ticket #4825 (support, EN): The app crashed once but it worked fine the second time, no need for a refund.
|
|
6
|
+
チケット #4826(サポート, JA): 注文した覚えのない請求が来ています。至急確認してください。本当に困っています。
|
|
7
|
+
チケット #4827(サポート, JA): 配送先の住所を変更したいです。来週には新しい住所に届けてほしいです。
|
|
8
|
+
チケット #4828(サポート, JA): 対応いただきありがとうございました。問題は解決しました。
|
|
9
|
+
チケット #4829(サポート, JA): サポートに何度連絡しても返事がなく、とても腹が立っています。
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
// Scripts written without spaces between words: joining their wrapped lines must not add a space between them,
|
|
2
|
+
// and sys1grep's token estimate prices them per character (cjkBytesOf).
|
|
3
|
+
export const CJK = /[\p{Script=Han}\p{Script=Hiragana}\p{Script=Katakana}\p{Script=Thai}\p{Script=Lao}\p{Script=Khmer}\p{Script=Myanmar}\p{Script=Tibetan}ー、。]/u;
|
|
4
|
+
|
|
5
|
+
// --unit=sentence-by-*: the unit is a sentence. Lines are joined as wrapped prose, except where a newline cannot be inside
|
|
6
|
+
// a sentence: at a blank line, next to structure characters (JSON, code), or before a list item, heading, quote or
|
|
7
|
+
// number. Each joined piece is then split by Intl.Segmenter (Unicode UAX #29 sentence boundaries).
|
|
8
|
+
const SENTENCES = new Intl.Segmenter(undefined, { granularity: 'sentence' });
|
|
9
|
+
const hardBreak = (prev, next) => !prev || !next || /[{}\[\]<>|;]$/.test(prev) || /^[{}\[\]<>|"\-*+#>\d]/.test(next)
|
|
10
|
+
|| /[{}\[\];]$/.test(next); // a line ending like code is not a continuation of prose either
|
|
11
|
+
// lines -> [{ text, spans }]. A span [unit, from, to] is where the sentence lies in the original units: the unit
|
|
12
|
+
// is the 1-based line (or record) number from unitOf, from/to are character offsets inside it (baseOf shifts a
|
|
13
|
+
// line's offset inside its record with --unit=zero). Output maps matching sentences back to these units.
|
|
14
|
+
export function toSentences(lines, unitOf, baseOf, split = new Set()) { // split: breaks Jev judged to end an entry
|
|
15
|
+
const out = [];
|
|
16
|
+
let text = '', marks = []; // marks: { o: offset in text, unit, base: offset in the unit, len }
|
|
17
|
+
const flush = () => {
|
|
18
|
+
for (const seg of SENTENCES.segment(text)) {
|
|
19
|
+
const t = seg.segment.trim();
|
|
20
|
+
if (!t) continue;
|
|
21
|
+
const start = seg.index + seg.segment.length - seg.segment.trimStart().length, end = start + t.length;
|
|
22
|
+
const spans = [];
|
|
23
|
+
for (const m of marks) {
|
|
24
|
+
const a = Math.max(start, m.o), b = Math.min(end, m.o + m.len);
|
|
25
|
+
if (a < b) spans.push([m.unit, m.base + a - m.o, m.base + b - m.o]);
|
|
26
|
+
}
|
|
27
|
+
out.push({ text: t, spans });
|
|
28
|
+
}
|
|
29
|
+
text = ''; marks = [];
|
|
30
|
+
};
|
|
31
|
+
lines.forEach((line, i) => {
|
|
32
|
+
const prev = text.trimEnd(), next = line.trim();
|
|
33
|
+
const mark = { unit: unitOf(i), base: baseOf(i) + line.length - line.trimStart().length, len: next.length };
|
|
34
|
+
if (hardBreak(prev, next) || split.has(i)) { flush(); if (next) { text = next; marks.push({ ...mark, o: 0 }); } return; }
|
|
35
|
+
text = prev + (CJK.test(prev.at(-1)) && CJK.test(next[0]) ? '' : ' '); // no space for scripts without word spaces
|
|
36
|
+
marks.push({ ...mark, o: text.length });
|
|
37
|
+
text += next;
|
|
38
|
+
});
|
|
39
|
+
flush();
|
|
40
|
+
return out;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Where the rules would join, a break still needs judging if a script without word spaces touches it and the line
|
|
44
|
+
// does not end a sentence: in Japanese or Chinese, entries often end without 。, and joining leaves no trace of the
|
|
45
|
+
// break. A line starting with closing punctuation continues the previous one (kinsoku), and so does a line
|
|
46
|
+
// ending in 、.
|
|
47
|
+
const CLOSING = /^[。、,.」』)】〕!?]/;
|
|
48
|
+
export const needsJudging = (prev, next) => prev && next && !hardBreak(prev, next) && !/[。!?!?、,]$/.test(prev) && !CLOSING.test(next)
|
|
49
|
+
&& (CJK.test(prev.at(-1)) || CJK.test(next[0]));
|
package/options.mjs
ADDED
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
// sys1grep.mjs's own node:util.parseArgs option table and default-options gate, pulled out so serve.mjs's
|
|
2
|
+
// settings-panel opts validation runs the exact same parser instead of a hand-rolled regex/cluster check that kept
|
|
3
|
+
// missing forms the parser itself already understands (#199: -nr, -rC1, --r, -h all slipped past earlier regexes
|
|
4
|
+
// in turn; parseArgs splits a cluster, resolves a glued value and a long alias for a one-letter name correctly by
|
|
5
|
+
// construction, so there is nothing left to keep in sync by hand).
|
|
6
|
+
import { parseArgs } from 'node:util';
|
|
7
|
+
|
|
8
|
+
export const OPTIONS = {
|
|
9
|
+
e: { type: 'string', multiple: true },
|
|
10
|
+
P: { type: 'string', multiple: true }, // a regex term (/RE/flags), OR'd like -e; -a/-v after it AND/NOT onto it
|
|
11
|
+
a: { type: 'string', multiple: true },
|
|
12
|
+
v: { type: 'string', multiple: true },
|
|
13
|
+
question: { type: 'string', multiple: true, short: 'Q' },
|
|
14
|
+
level: { type: 'string', default: 'normal' }, // strictness preset: loose / normal / strict
|
|
15
|
+
r: { type: 'boolean', default: false }, // recurse into directories
|
|
16
|
+
cached: { type: 'boolean', default: false }, // git sys1grep only: search the index instead of the working tree
|
|
17
|
+
untracked: { type: 'boolean', default: false }, // git sys1grep only: also search untracked files (.gitignore still applies)
|
|
18
|
+
l: { type: 'boolean', default: false }, // print only matching file names
|
|
19
|
+
'with-filename': { type: 'boolean', short: 'H' }, // prefix file names even for one file; --no-filename: never
|
|
20
|
+
t: { type: 'string' }, // positive threshold: match when p >= t (default from preset)
|
|
21
|
+
T: { type: 'string' }, // negative threshold: "not X" when p < T (default from preset)
|
|
22
|
+
chunk: { type: 'string', default: '30' }, // lines per request
|
|
23
|
+
c: { type: 'boolean', default: false }, // count of matching lines per file (grep -c)
|
|
24
|
+
quiet: { type: 'boolean', short: 'q', default: false }, // print nothing, exit status only (grep -q)
|
|
25
|
+
j: { type: 'string', default: '8' }, // concurrent requests
|
|
26
|
+
A: { type: 'string' }, // N lines of trailing context
|
|
27
|
+
B: { type: 'string' }, // N lines of leading context
|
|
28
|
+
C: { type: 'string' }, // N lines of context on both sides
|
|
29
|
+
n: { type: 'boolean', default: false }, // line numbers
|
|
30
|
+
z: { type: 'boolean', default: false }, // records are NUL-terminated, on input and output (grep -z); --unit=zero
|
|
31
|
+
unit: { type: 'string', default: 'line' }, // line / zero / sentence-by-jev / sentence-by-rule / function
|
|
32
|
+
o: { type: 'boolean', default: false }, // with --unit=sentence-by-*, print only the matching sentences (grep -o)
|
|
33
|
+
p: { type: 'boolean', default: false }, // print each meaning's probability
|
|
34
|
+
dedup: { type: 'string', default: 'never' }, // auto|always|never: judge one representative per template, reuse its answer
|
|
35
|
+
rank: { type: 'string' }, // jev|match: print the results best first, scored by Jev's answer on each or by its best match
|
|
36
|
+
// multi-step matching (#163): the expression before --step-to finds the start, the one after it the end;
|
|
37
|
+
// --step-reverse-to walks the edges backwards (callee to caller)
|
|
38
|
+
'step-to': { type: 'boolean', multiple: true },
|
|
39
|
+
'step-reverse-to': { type: 'boolean', multiple: true },
|
|
40
|
+
edges: { type: 'string' }, // FILE: the edges to walk, one per line; default: the calls between functions
|
|
41
|
+
reverse: { type: 'boolean' }, // removed (--step-reverse-to): parsed only to say so
|
|
42
|
+
hops: { type: 'string', default: '0..' }, // N, M..N or M..: the hops an end may be at
|
|
43
|
+
'dry-run': { type: 'boolean', default: false }, // print the files and requests, send nothing
|
|
44
|
+
verbose: { type: 'boolean', default: false }, // print the files and requests to stderr while searching
|
|
45
|
+
interactive: { type: 'boolean', short: 'i', default: false }, // show what --dry-run would send, search on a yes
|
|
46
|
+
yes: { type: 'boolean', short: 'y', default: false }, // skip the size/cost guard's question, answering yes (#58)
|
|
47
|
+
'max-columns': { type: 'string', short: 'M' }, // a unit past this many characters is skipped (rg's -M/--max-columns)
|
|
48
|
+
'max-filesize': { type: 'string' }, // a target past this size (K/M/G) is listed and confirmed (rg's name)
|
|
49
|
+
'max-cost': { type: 'string', default: '1' }, // ask before sending when the estimated price is over this many USD
|
|
50
|
+
// which files -r finds and git sys1grep lists; with -r a file named on the command line is always searched
|
|
51
|
+
include: { type: 'string', multiple: true }, // only names matching one of these globs
|
|
52
|
+
exclude: { type: 'string', multiple: true }, // not names matching one of these globs
|
|
53
|
+
'changed-within': { type: 'string' }, // only files modified within 30m / 2h / 7d / 2w, since a date, today, ...
|
|
54
|
+
gitlog: { type: 'boolean', short: 'g', default: false }, // search git log's commits, one record each; auto-scope picks which
|
|
55
|
+
'auto-scope': { type: 'boolean', default: true }, // narrow those files by what Jev says a meaning restricts to; --no-auto-scope: don't
|
|
56
|
+
color: { type: 'string', default: 'auto' }, // auto / always / never
|
|
57
|
+
summarize: { type: 'string' }, // pipe what would print to this LLM CLI and print its answer instead
|
|
58
|
+
'summarize-prompt': { type: 'string' }, // the user's own instruction, added after the fixed one
|
|
59
|
+
format: { type: 'string', default: 'plain' }, // plain / markdown / html: --rank's output, or asked of the summarizer
|
|
60
|
+
template: { type: 'string' }, // --rank's or --summarize's --format=html document: a NAME under the templates dirs, or a file; list: the names
|
|
61
|
+
'install-templates': { type: 'boolean', default: false }, // copy the bundled templates to ~/.config/sys1grep/templates
|
|
62
|
+
cache: { type: 'string' }, // on|off|only: reuse Jev's stored answers (#252); clear: empty the cache
|
|
63
|
+
'summarize-format': { type: 'string' }, // removed (--format): parsed only to say so
|
|
64
|
+
// the API settings, each overriding its environment variable
|
|
65
|
+
'sys1-model': { type: 'string' }, // SYS1GREP_MODEL
|
|
66
|
+
'sys1-url': { type: 'string' }, // SYS1GREP_URL
|
|
67
|
+
'sys1-api-key': { type: 'string' }, // SYS1GREP_API_KEY / TYPESAFE_API_KEY
|
|
68
|
+
help: { type: 'boolean', short: 'h', default: false },
|
|
69
|
+
version: { type: 'boolean', short: 'V', default: false },
|
|
70
|
+
lang: { type: 'string' }, // en|ja: --help's language, and --serve's page default (SYS1GREP_LANG, settings.json lang)
|
|
71
|
+
};
|
|
72
|
+
// --no-rank / --no-summarize: parseArgs negates booleans only, so a bare one becomes a value no real argument can
|
|
73
|
+
// hold (this NUL), cleared by the caller once parsed; the later of --no-rank/--rank=X (or --no-summarize/
|
|
74
|
+
// --summarize=X) wins, as for any option.
|
|
75
|
+
export const OFF = '\0';
|
|
76
|
+
export const REVERSE_REMOVED = '--reverse was replaced by --step-reverse-to';
|
|
77
|
+
// A bare --color / --dedup / --rank / --summarize / --cache means an implied default value, as grep's bare --color does;
|
|
78
|
+
// parseArgs cannot express an optional value, so this fills it in before parseArgs ever sees it. --no-filename is
|
|
79
|
+
// grep's name for --no-with-filename, --null-data grep's name for -z. summarizer: the TOOL a bare --summarize
|
|
80
|
+
// fills in (sys1grep.mjs's own SYS1GREP_SUMMARIZER-or-"claude"; a caller that only validates shape, never runs the
|
|
81
|
+
// result, may pass any placeholder).
|
|
82
|
+
export const fill = (a, summarizer) => (a === '--color' ? '--color=auto' : a === '--null-data' ? '-z' : a === '--no-filename' ? '--no-with-filename'
|
|
83
|
+
: a === '--cache' ? '--cache=on' : a === '--summarize' ? `--summarize=${summarizer || 'claude'}` : a === '--dedup' ? '--dedup=always' : a === '--rank' ? '--rank=jev'
|
|
84
|
+
: a === '--no-rank' ? `--rank=${OFF}` : a === '--no-summarize' ? `--summarize=${OFF}` : a);
|
|
85
|
+
// Parses a default-options array (SYS1GREP_OPTS / settings.json opts), already fill()ed, with sys1grep.mjs's own
|
|
86
|
+
// parseArgs configuration, and refuses anything that is not a plain search option: a positional (a target or any
|
|
87
|
+
// other stray value, #196), a meaning or expression (-e/-a/-v/-Q/--step-to go on the command line, not in
|
|
88
|
+
// defaults), or a command (--install-templates, --template=list, --cache=clear: these do something instead of searching). Shared
|
|
89
|
+
// by sys1grep.mjs's own gate and serve.mjs's settings-panel validation, so the two can never accept something the
|
|
90
|
+
// other would refuse. Throws (a parseArgs error, or the refusal above) with sys1grep's own wording either way;
|
|
91
|
+
// returns { tokens } — every parsed token, so a caller can read what option each one named (its canonical
|
|
92
|
+
// OPTIONS key, however it was spelled: -r, --r, or inside a cluster like -rC1 or -nr) — on success.
|
|
93
|
+
export function parseOpts(filledArgs) {
|
|
94
|
+
const { tokens } = parseArgs({ args: filledArgs, options: OPTIONS, allowPositionals: true, allowNegative: true, tokens: true });
|
|
95
|
+
const command = k => k.name === 'install-templates' || (k.name === 'template' && k.value === 'list') || (k.name === 'cache' && k.value === 'clear');
|
|
96
|
+
if (tokens.some(k => k.name === 'reverse')) throw new Error(REVERSE_REMOVED);
|
|
97
|
+
const bad = tokens.find(k => k.kind !== 'option' || ['e', 'P', 'a', 'v', 'question', 'step-to', 'step-reverse-to', 'cached', 'untracked'].includes(k.name) || command(k));
|
|
98
|
+
const reason = n => (n.startsWith('step-') ? 'expressions go on the command line' : n === 'P' ? 'a regex goes on the command line' : 'meanings go on the command line');
|
|
99
|
+
if (bad) throw new Error(bad.kind !== 'option' ? `'${bad.value ?? '--'}' is not an option` : command(bad) ? `${bad.rawName}${bad.value === undefined ? '' : `=${bad.value}`} is not allowed (it does something instead of searching)` : ['cached', 'untracked'].includes(bad.name) ? `--${bad.name} is not allowed (what is searched goes on the command line)` : `${bad.name.length > 1 ? '--' : '-'}${bad.name} is not allowed (${reason(bad.name)})`);
|
|
100
|
+
return { tokens };
|
|
101
|
+
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@uehaj/sys1grep",
|
|
3
|
-
"version": "0.5.0
|
|
4
|
-
"description": "grep by meaning, across languages. TypeSafe Jev scores
|
|
3
|
+
"version": "0.5.0",
|
|
4
|
+
"description": "grep by meaning, across languages. TypeSafe Jev scores each line (or record, sentence, function) against a meaning; combine meanings and regexes with AND/OR/NOT. Includes git sys1grep and a local search page (--serve).",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"grep",
|
|
7
7
|
"semantic",
|
|
@@ -9,7 +9,10 @@
|
|
|
9
9
|
"typesafe",
|
|
10
10
|
"jev",
|
|
11
11
|
"cli",
|
|
12
|
-
"japanese"
|
|
12
|
+
"japanese",
|
|
13
|
+
"git",
|
|
14
|
+
"regex",
|
|
15
|
+
"llm"
|
|
13
16
|
],
|
|
14
17
|
"license": "MIT",
|
|
15
18
|
"author": "Junji UEHARA",
|
|
@@ -24,12 +27,25 @@
|
|
|
24
27
|
},
|
|
25
28
|
"files": [
|
|
26
29
|
"sys1grep.mjs",
|
|
30
|
+
"serve.mjs",
|
|
31
|
+
"settings.mjs",
|
|
32
|
+
"options.mjs",
|
|
27
33
|
"git-sys1grep.mjs",
|
|
34
|
+
"lib/sentences.mjs",
|
|
35
|
+
"examples/app.js",
|
|
36
|
+
"examples/app.py",
|
|
37
|
+
"examples/logs.txt",
|
|
38
|
+
"examples/prose.txt",
|
|
39
|
+
"examples/tickets.txt",
|
|
28
40
|
"README.md",
|
|
29
41
|
"README.ja.md",
|
|
30
42
|
"docs/color.svg",
|
|
31
43
|
"docs/demo.svg",
|
|
32
|
-
"docs/sentence.svg"
|
|
44
|
+
"docs/sentence.svg",
|
|
45
|
+
"templates/default.html",
|
|
46
|
+
"templates/print.html",
|
|
47
|
+
"templates/search.html",
|
|
48
|
+
"templates/terminal.html"
|
|
33
49
|
],
|
|
34
50
|
"engines": {
|
|
35
51
|
"node": ">=20.16"
|
|
@@ -39,6 +55,14 @@
|
|
|
39
55
|
"test:offline": "sh tests/offline.sh",
|
|
40
56
|
"judge": "node --no-warnings --env-file-if-exists=.env tests/judge.mts",
|
|
41
57
|
"scope-eval": "node tests/scope-eval.mjs",
|
|
42
|
-
"
|
|
58
|
+
"dedup-eval": "node tests/dedup-eval.mjs",
|
|
59
|
+
"cause-eval": "node tests/cause-eval.mjs",
|
|
60
|
+
"skill-eval": "bash tests/skill-eval/run.sh",
|
|
61
|
+
"serve": "node sys1grep.mjs --serve",
|
|
62
|
+
"demo": "node scripts/demo-serve.mjs",
|
|
63
|
+
"serve:portless": "portless run --name sys1grep npm run serve:port --",
|
|
64
|
+
"serve:port": "node sys1grep.mjs --serve=$PORT",
|
|
65
|
+
"release": "sh scripts/release.sh",
|
|
66
|
+
"smoke": "node --no-warnings tests/smoke.mts"
|
|
43
67
|
}
|
|
44
68
|
}
|