Command line
The Rules block ships two top-level kis commands. rules runs a rule set over JSON inputs,
optionally with the bounding-box document and page image beside each one, and rules check checks a
product’s rules/ folder the way rules.svc loads it. bbox makes, checks and inspects the .bbox
documents the rules read, and reads form controls on a scanned page.
kis rules is the only command that runs rules.
| Command | What it does | Runs rules |
|---|---|---|
kis rules | Runs a rule set over JSON inputs, one execution per input or one over all of them | yes |
kis rules check | Checks a product’s rules/ folder as rules.svc loads it | no |
kis bbox create | Convert hOCR to .bbox JSON | no |
kis bbox validate | Check that .bbox files are structurally sound | no |
kis bbox checkbox | Detect checkboxes on a page and print their states | no |
kis bbox radio | Detect radio buttons on a page and print their states | no |
kis bbox probe | Show what a label-relative read sees around a label, and why it misses | no |
kis bbox tables | Detect and print every table | no |
kis bbox table | Print the one table nearest an anchor | no |
kis bbox phrase | Find a phrase, with page, coordinates and context | no |
kis bbox snippet | Extract text between two phrases | no |
kis bbox snippets | Extract several named snippets in one pass | no |
Documents are a command-line surface today. rules.svc serves no documents: a rule set that reads a
document (bbox.*) runs through kis rules, and a scope whose rules name bbox does not load on
the service. See The rules service.
Installing the commands
Section titled “Installing the commands”Both commands are proxy stubs on the kis binary. Each one forwards its arguments verbatim to the
separately installed kis-rules plugin executable, which is where the actual implementation lives.
kis looks for that executable in this order and takes the first executable match:
- The directory containing the running
kisbinary. ~/.kisai/bin/.- Every entry of
$KIS_PLUGIN_PATH. - Every entry of
$PATH.
kis also passes the program name you typed to the plugin, so kis rules --help prints
Usage: kis rules [flags].
Global flags
Section titled “Global flags”Because the proxy commands disable flag parsing, flags for kis itself must come before the
command name. Anything after it is forwarded to the plugin untouched.
kis --quiet rules -r rules/ -i pages/| Flag | Short | Type | Default | Meaning |
|---|---|---|---|---|
--quiet | -q | bool | false | Suppress non-error output |
--tenant | string | "" | Tenant override | |
--log-level | -l | string | error | trace, debug, info, warn, error |
--log-to | string | stderr | stderr, stdout, or a file path | |
--log-format | string | text | text or json | |
--env-file | string | "" | Env YAML file to use instead of ~/.kisai/env.yaml |
The plugin itself understands -l/--log-level and --env-file anywhere after the command name, so
kis rules -l debug ... works. The other four are interpreted only in the leading position.
Environment variables on this path: KIS_PLUGIN_PATH (extra plugin search directories), KIS_ENV
(selects the active env.yaml block), KIS_ENV_FILE (overrides the global env file), and
KIS_DEBUG, KIS_QUIET, KIS_TENANT, KIS_LOGLEVEL, KIS_LOGTO, KIS_LOGFORMAT, which seed the
context handed to the plugin.
Flag defaults from env.yaml
Section titled “Flag defaults from env.yaml”Before a command runs, any flag you did not type on the command line is filled from env.yaml. Keys
are the flag’s long name verbatim, lowercase, no prefix, no command nesting.
# ./env.yaml, or ~/.kisai/env.yamldefault: workers: 4 pattern: "page*.json" log-level: infoTwo files feed flag defaults, lowest precedence first: the global file ($KIS_ENV_FILE or
~/.kisai/env.yaml), then ./env.yaml. Within each file only the block named by $KIS_ENV is read,
falling back to default. Resolution is: explicit flag > env.yaml > compiled-in default. The
ambient OS environment never feeds flag values. env-file cannot be set this way, it selects the
file being loaded.
kis rules
Section titled “kis rules”kis rules -r <rulefile|ruledir> [input...]Runs a rule set over JSON inputs. Each input’s top-level keys become the facts a rule reads.
A positional argument may be a directory (globbed with -p), a .json or .bbox path (the
extension is stripped), or a bare name resolved against -i. With no positional arguments the -i
directory itself is globbed. Duplicates are removed. Input discovery is a single non-recursive glob;
-R affects rule file discovery only.
kis rules -r rules/ -i pages/kis rules -r rules/ pages/page1.jsonkis rules -r rules/ pages/*.jsonkis rules -r rules/ --with-bbox --with-image -i pages/kis rules -r rules/ --with-bbox --mode doc -o result.json pages/kis rules -r rules/ -i pages/ --updatekis rules -r rules/ --validate --strictkis rules -r products/shop --scope invoice-review -i invoices/| Flag | Short | Type | Default | Meaning |
|---|---|---|---|---|
--rules | -r | string slice | (required) | Rule file or directory of rules; with --scope, the product’s rules/ folder or its root |
--scope | string | "" | Run a product’s scope by name, as rules.svc does: a static scope of rules/rules.yaml or a top folder | |
--input | -i | string | . | Base directory for resolving inputs |
--pattern | -p | string | *.json | Basename glob for directory arguments, non-recursive |
--skip-pattern | string slice | nil | Extra basename globs to exclude | |
--mode | string | each | each runs once per input, doc merges every input and runs once | |
--output | -o | string | "" | Directory in --mode each, file in --mode doc; stdout when unset |
--update | bool | false | Merge results into the source .json (--mode each only) | |
--key | -k | string | "" | Nest the result under this key |
--ruleset | string | rules | Name the rule set is compiled and executed under | |
--workers | -w | int | 1 | Parallel executions in --mode each, clamped to [1, inputs] |
--facts-key | string | pages | In --mode doc, the key holding the per-input facts array | |
--facts | -f | string slice | nil | Extra fact file, key=path or bare path |
--vars | -v | string slice | nil | key=value facts, injected as strings |
--bbox | -b | string slice | nil | Bounding-box document to inject, [name=]path (default name bbox) |
--with-bbox | bool | false | Load <input>.bbox beside each input | |
--require-bbox | bool | false | Implies --with-bbox; an input without a sidecar is dropped | |
--bbox-name | string | per mode | Name the sidecar is injected under: page in each, doc in doc | |
--with-image | bool | false | Load the page image beside each input and attach it to the sidecar | |
--require-image | bool | false | Implies --with-image; an input without one is dropped | |
--image | string slice | nil | Page image for a named document, [name=]path | |
--schema | string slice | nil | A .schema file to register (repeatable) | |
--schemas | string slice | nil | Directory of *.schema files, non-recursive | |
--recursive | -R | bool | false | Recurse when discovering rule files |
--validate | bool | false | Check rule calls against the registered bindings; execute nothing | |
--strict | bool | false | With --validate, exit non-zero on any error diagnostic or unparsed file | |
--history | bool | false | Print the history of value changes | |
--audit | bool | false | Print the audit log | |
--history-audit | string | "" | Directory for the -hist-audit.json sidecars | |
--debug | bool | false | Print load, worker and write progress |
--rules, --facts, --vars, --bbox, --image, --schema and --schemas are repeatable and
also split on commas, so -r a.grl,b.grl is two values and a path containing a comma breaks. The
hidden spellings --recursive-rules and --with-bbox-name still parse, as -R and --bbox-name.
-r loads the .grl files and vocabulary files of a directory, or every one in the tree with -R,
into one rule set. A file named directly must be a .grl file or a vocabulary file: anything else
fails the load with a grule set is .grl and vocabulary files. Inside a directory, other files are
skipped silently.
*-hist-audit.json is always excluded from input matching, in addition to any --skip-pattern you
give. An invalid --skip-pattern glob fails up front with invalid --skip-pattern "<pat>": <err>.
How facts are bound
Section titled “How facts are bound”In --mode each, each input’s top-level keys are bound as facts, each under its own name. -f
files and -v values overlay them: -f key=path nests the file’s contents under key; a bare
-f path merges the file’s top-level keys into the fact root. -v values are always strings,
-v threshold=10 gives a rule the string "10", never the number. Variables overlay facts loaded
from -f.
kis rules -r rules/ -i pages/ -f ref=lookup.json -v env=prod -b mydoc=pages/page1.bboxA rule reads a nested fact with a dotted path through in, in.Str("ref.region", ""), as it would
on rules.svc. See Reading facts.
Running a product’s scope: —scope
Section titled “Running a product’s scope: —scope”--scope <name> runs one of a product’s scopes the way rules.svc does: a static scope of
rules/rules.yaml, or a top folder of rules/ with the folders under it. -r names the product’s
rules/ folder, or the product’s root holding one. The folder is loaded as rules.svc loads it, so a
set with a call that cannot resolve does not start, and each input is held to the scope’s declared
facts.
kis rules -r products/shop --scope invoice-review -i invoices/the product has no scope nope: no static scope of rules/rules.yaml and no folder rules/nope/A static scope that reads data through its sources runs on rules.svc only, where the sources are:
kis rules --scope refuses it. See Scopes.
Fan-out: —mode each and —mode doc
Section titled “Fan-out: —mode each and —mode doc”--mode each (the default) runs the rule set once per input, across --workers goroutines. Each
input’s own JSON keys are its facts.
--mode doc merges every input’s .bbox into one multi-page document under --bbox-name, injects
the per-input facts as an array under --facts-key, and runs the rule set exactly once. Merging
renumbers pages: each page’s number, and each word’s page, becomes a sequential index over the
whole merged document. There is no --workers. It is a single execution.
kis rules -r rules/ --with-bbox --mode doc -i pages/kis rules -r rules/ --with-bbox --mode doc --facts-key documents pages/A .bbox that does not parse fails the merge, naming the file.
Output modes
Section titled “Output modes”With neither -o nor --update, results go to stdout:
kis rules -r classify.grl -i pages --with-bbox --history --audit page1--- History for /work/pages/page1.json --- 1. [DetectInvoice] doctype = "invoice"
--- Audit for /work/pages/page1.json --- 1. [DetectInvoice] executed 2. [DetectInvoice] matched the phrase 'Invoice Number'
Result for /work/pages/page1.json:{ "doctype": "invoice"}Processed 1/1 input(s) successfullyThe executed audit entry is added by the engine for every rule that fires; the second line is the
rule’s own audit.Log call. Findings a rule raises with findings.* are not printed.
-o writes the rule results alone to the mirrored path under that directory, leaving the source
untouched. --update merges the results back into the source .json; an execution that produced an
empty result map writes nothing at all. Given both, -o wins and a warning says so:
warning: --update is redundant with -o and was ignored; -o writes the rule results and leaves the source untouchedkis rules -r rules/ -i pages/ -o results/ -k rules_resultkis rules -r rules/ -i pages/ --updateIn --mode doc, -o names a file, and a directory of that name is refused up front rather than
failing inside the write.
Documents: -b and —with-bbox
Section titled “Documents: -b and —with-bbox”For a sidecar per input, use --with-bbox, and name it to match what your rules call:
kis rules -r classify.grl -i pages/ --with-bbox --bbox-name pageAn explicitly named -b of the same name overrides the discovered sidecar rather than racing it.
| Invocation | Flag that sets the name | Default name |
|---|---|---|
kis rules -b [name=]file | the name= prefix | bbox |
kis rules --with-bbox | --bbox-name | page |
kis rules --with-bbox --mode doc | --bbox-name | doc |
A --require-bbox input whose sidecar is missing is dropped when it came from a directory glob or a
shell-expanded path, the glob simply matched a file that is not an input, and is an error when you
named the prefix yourself, since a name you typed cannot be glob noise. Either way the drop is
reported:
warning: skipped 1 input(s) with no .bbox sidecar: page3.jsonPage images: —with-image
Section titled “Page images: —with-image”--with-image loads the page image sitting beside each input, same basename, .png, .jpg,
.jpeg, .tif or .tiff, first match wins in that order, and attaches it to the sidecar document.
This is what makes checkbox and radio state read from ink rather than be guessed from OCR glyphs. It
requires --with-bbox, since the image attaches to a document, and it cannot combine with
--mode doc, since visual fusion is per page and that mode produces one multi-page document. Both
combinations are refused up front.
kis rules -r forms.grl -i pages/ --with-bbox --with-imagekis rules -r forms.grl -i pages/ --with-bbox --image page=scan-hi-res.pngAn image that is found but cannot be decoded is an error, not a silent downgrade to text-only:
decoding page image <path>: image: unknown format, a sidecar image that is simply absent leaves the
run text-only unless --require-image was given, which drops the input and reports it.
Registering schemas
Section titled “Registering schemas”kis rules -r rules/ --schema schemas/invoice.schema -i pages/kis rules -r rules/ --schemas schemas/ --mode doc pages/The filename minus .schema is the name rules call it by, so invoice.schema registers as
invoice. --schemas is non-recursive. Duplicate names across --schema and --schemas are a hard
error. See Schema-driven extraction.
Checking a rule set without running it: —validate
Section titled “Checking a rule set without running it: —validate”kis rules -r rules/ --validatekis rules -r rules/ --validate --strict--validate resolves every call in the rule set against the registered bindings and prints
diagnostics without reading any input, so no -i is needed. The bounding-box plugin is registered
unconditionally under --validate so bbox.* calls are checked too. Files that failed to parse are
listed first, they are not in the rule set to be checked at all, then error diagnostics with their
line, then warnings rolled up by the call they are about. Without --strict the command exits 0
whatever it found; --strict turns any error, or any unparsed file, into a non-zero exit.
1 file(s) did not parse at all (excluded below): /work/rules/dates.grl: got 1 error(s) in grl the script
CheckTotals:5: error: findings.Add takes 5 argument(s), called with 3 findings.Add("CheckTotals", "ERROR", "totals");
5 diagnostic(s): 1 error(s), 4 warning(s)
Warnings by kind: 3 in.GetStr 1 in.MemSetSee Validating a rule set for what is and is not checked.
kis rules check
Section titled “kis rules check”kis rules check [rules-dir|product-dir]Checks a product’s rules/ folder offline, with the same code rules.svc loads it with:
rules/rules.yaml read strictly, each static scope’s declarations, every folder of rules alone and
each top folder with the folders under it, every call that cannot resolve, and each static scope’s
literal act calls against its declared writes. Nothing runs and no input is read.
The argument is the rules/ folder, or a product’s root holding one; it defaults to rules.
kis rules checkkis rules check products/shoprules: 1 folder scope(s), 0 static scope(s): they load claim-triage folder, 1 rule file(s)A folder that does not load prints what rules.svc would refuse it for, and exits 1:
rules "claim-triage/**" do not validate:rules/claim-triage/typo.grl: rule Typo, line 3: strings has no method ToUper (did you mean ToUpper?)A file that does not parse is reported by name with the parser’s count,
rules/a/b.grl: got 1 error(s) in grl the script.
kis bbox
Section titled “kis bbox”Commands for making, checking and reading .bbox documents, the format the rules read: pages of
words, lines and paragraphs, each with coordinates and a confidence. No rules are loaded and nothing
is written back to an input.
The reading commands, tables, table, phrase, snippet, snippets and probe, concatenate
their repeatable -i/--input values with their positional file arguments and treat the result as
one input list, so shell globs and -i mix freely. All pages across all inputs are renumbered
sequentially into one merged document before analysis. -i, --snip and -H are taken literally.
They do not split on commas, so repeat the flag instead.
A miss is not an error. No matches found for phrase: "<p>", No snippet found between "<s>" and "<e>", No table found near anchor: <a> and No tables detected. are all printed to the output
stream, so they land in an -o file too, and the command exits 0. Of the checks these commands
run on what they found, only bbox table -H validation failure exits non-zero, an unreadable input
file always exits non-zero.
kis bbox create
Section titled “kis bbox create”kis bbox create [files-or-dirs...] [flags]Converts recognizer output into the .bbox JSON that every other command consumes. Today the only
input format is hOCR. Files and directories may be passed as arguments, with -i, or both; named
files are converted regardless of extension, and directories are scanned, non-recursively, for
*.hocr and *.hocr.html.
| Flag | Short | Type | Default | Meaning |
|---|---|---|---|---|
--input | -i | string | "" | A file or directory to convert, beside any arguments |
--output | -o | string | "" | Output file (single input) or directory (several inputs); beside the input when unset |
--format | string | auto | Input format: auto or hocr. The output is always .bbox | |
--workers | -w | int | 4 | Parallel conversions when processing a directory |
--incremental | bool | false | Skip inputs whose .bbox is already present and newer | |
--clean | bool | false | OCR text cleanup: normalise unicode, fix quotes and dashes, drop garbage characters | |
--clean-form | bool | false | Form-aware cleanup that preserves and normalises checkbox and radio indicators | |
--verbose | -v | bool | false | Warn about content the parser may have missed |
kis bbox create page.hocr --cleankis bbox create *.hocrkis bbox create -i scans/ -o bbox/ -w 8 --clean-form -vOutput is one pretty-printed .bbox file per input, named after the input basename with the
extension replaced. --clean-form takes precedence when both cleanup flags are given.
The produced document, abridged to one word, hOCR line and paragraph grouping is carried through as
lines and paragraphs arrays beside words on each page:
{ "pages": [ { "number": 1, "width": 1224, "height": 1584, "words": [ { "text": "Invoice", "box": { "x0": 100, "y0": 100, "x1": 260, "y1": 140 }, "confidence": 0.96, "page": 1, "line_id": 1 } ] } ]}With no input at all the command fails with
No input: pass one or more hocr files or directories as arguments (or with --input); inputs that
hold no hOCR file fail with no hocr files found in the given input(s), after a note naming what
the scan matched.
kis bbox validate
Section titled “kis bbox validate”kis bbox validate [files-or-dirs...] [flags]Checks .bbox files for the defects that make downstream extraction inexplicable rather than merely
wrong: non-finite or inverted boxes, indices pointing outside the lists they address, confidences
out of range. It exits non-zero when any file has an error; warnings, an empty page or a box off the
edge of its page, fail only with --strict.
| Flag | Short | Type | Default | Meaning |
|---|---|---|---|---|
--input | -i | string slice | nil | File or directory to check, beside any arguments |
--json | bool | false | Emit the report as JSON | |
--quiet | -q | bool | false | Print only files with findings |
--strict | bool | false | Treat warnings as failures |
ok page.bbox (1 page(s), 14 words, 0 lines, 0 paragraphs)kis bbox checkbox and kis bbox radio
Section titled “kis bbox checkbox and kis bbox radio”kis bbox checkbox <image|hocr|bbox>...kis bbox radio <image|hocr|bbox>...Detect form controls on a page and print their states as JSON. These run no rules. For a .hocr or
.bbox input the sibling .png, .jpg or .jpeg is used as the page image unless --image names
one, so state is read from ink rather than guessed from OCR glyphs.
| Flag | Type | Default | Meaning |
|---|---|---|---|
--image | string | the input, or its sibling image | Page image for visual fusion |
--label | string slice | nil | Answer only for these labels; needs the page image |
--min-confidence | float | 0 | Drop detections below this confidence |
--no-groups | bool | false | Omit the grouped view (yes/no pairs and the like) |
--no-visual | bool | false | Text-only detection |
--lang | string | eng | Tesseract language(s) |
--psm | int | -1 | Tesseract page segmentation mode |
--tesseract-bin | string | tesseract | Path to the tesseract binary |
--output, -o | string | stdout | Output file |
Full detail is in Extraction.
kis bbox probe
Section titled “kis bbox probe”kis bbox probe -a "<label>" -i <file.bbox>Shows what a rule’s label-relative reads, Right, Below, Left and Above, see around a label,
with the coercions applied to each, and why a read misses. --all lists every neighbour, not just
the nearest few.
LABEL "Loan Amount" matches: 1
READS Right "$412,000.00" Currency="412000.00" Number=412000 Date="" value at p1 (300,220) Left MISS label found but no value left of it: Loan Amountkis bbox tables
Section titled “kis bbox tables”kis bbox tables [files...]| Flag | Short | Type | Default | Meaning |
|---|---|---|---|---|
--input | -i | string array | nil | Path to a bounding-box file (repeatable) |
--format | -f | string | text | text, json or csv |
--output | -o | string | "" (stdout) | Output file path |
kis bbox tables data/page1.bboxkis bbox tables *.bbox -f csv -o tables.csv=== Table 1 (Page 1) ===Dimensions: 2 rows x 3 columns
Invoice | Number | INV-42Total | | 120.00Cells are padded to the widest value in their column, minimum width 3. When the table has detected
headers the text output adds a Headers: ... line and a dashed separator under the header rows.
-f json emits {"tables":[{index,page,rows,cols,header_rows,headers?,data}]}. -f csv prefixes
each table with a # Table N (Page P) comment line and separates tables with a blank line.
kis bbox table
Section titled “kis bbox table”kis bbox table -a "<anchor>" [files...]| Flag | Short | Type | Default | Meaning |
|---|---|---|---|---|
--anchor | -a | string | : (required) | Anchor text; the nearest table is returned |
--input | -i | string array | nil | Path to a bounding-box file (repeatable) |
--header | -H | string array | nil | An expected column header (repeatable) |
--format | -f | string | text | text, json or csv |
--output | -o | string | "" (stdout) | Output file path |
kis bbox table -a "Price List" -H "Item" -H "Price" -H "Qty" doc.bboxkis bbox table -a "Summary" -f json -o table.json doc.bboxEvery -H value must be present or the command prints
Header validation failed: missing headers: <list> (found: <list>) and exits non-zero, a table with
no detected header row fails the same way, with Header validation failed: table has no headers.
Comparison is case-insensitive and trims whitespace.
The JSON and CSV shapes differ from tables: table -f json emits a bare object with no index
field, and table -f csv emits bare rows with no # Table comment line. A consumer cannot treat the
two interchangeably.
kis bbox phrase
Section titled “kis bbox phrase”kis bbox phrase -p "<phrase>" [files...]| Flag | Short | Type | Default | Meaning |
|---|---|---|---|---|
--phrase | -p | string | : (required) | Phrase to search for; multi-word is supported |
--input | -i | string array | nil | Path to a bounding-box file (repeatable) |
--before | -B | int | 3 | Context lines before each match |
--after | -A | int | 3 | Context lines after each match |
--format | -f | string | text | text or json |
--output | -o | string | "" (stdout) | Output file path |
--case-insensitive | bool | false | Ignore case; matching is case-sensitive by default | |
--fuzzy | bool | false | Enable Levenshtein fuzzy matching | |
--max-dist-word | int | 2 | Max edit distance per word (fuzzy only) | |
--max-dist-total | int | 3 | Max total edit distance for the phrase (fuzzy only) |
kis bbox phrase -p "Invoice Number" data/page1.bboxkis bbox phrase -p "Inviice Numbr" --fuzzy --max-dist-word 2 --max-dist-total 3 doc.bboxFound 1 match(es) for phrase "Invoice Number"
=== Match 1 (Page 1) ===Location: (72.0, 100.0) - (195.0, 112.0)Line: 1
>> Invoice Number INV-42 Total 120.00-f json emits {phrase,count,matches:[{index,page,line_index,box:{x0,y0,x1,y1},matched_text, lines_before?,lines_after?}]}. Fuzzy mode marks the text header (fuzzy), adds Edit Distance: and
Matched Words: lines, and adds fuzzy, matched_words and total_distance to the JSON.
kis bbox snippet
Section titled “kis bbox snippet”kis bbox snippet -s "<start>" -e "<end>" [files...]| Flag | Short | Type | Default | Meaning |
|---|---|---|---|---|
--start | -s | string | : (required) | Starting phrase, included in the output |
--end | -e | string | : (required) | Ending phrase, excluded unless --include-end |
--input | -i | string array | nil | Path to a bounding-box file (repeatable) |
--format | -f | string | text | text or json |
--output | -o | string | "" (stdout) | Output file path |
--case-insensitive | bool | false | Ignore case when matching the two phrases | |
--include-end | bool | false | Include the ending phrase in the output | |
--fuzzy | int | 0 | Max Levenshtein distance per word; 0 is exact | |
--debug | bool | false | Dump phrase-matching diagnostics to the output stream |
kis bbox snippet -s "Invoice" -e "Total" --include-end data/page1.bboxkis bbox snippet -s "TERMS AND CONDITIONS" -e "SIGNATURE" --fuzzy 2 doc.bboxText output is === Snippet (Page A to B) ===, the two phrases, a blank line, then the extracted
lines. -f json emits {start_phrase,end_phrase,start_page,end_page,line_count,text,lines}, where
text is lines joined by newlines.
--debug writes line and page counts, every start- and end-phrase match, a direct case-insensitive
line scan, per-word block listings with their coordinates, and a spatial comparison of the first two
matched blocks, all to the output stream, so do not combine it with -o and a JSON consumer.
kis bbox snippets
Section titled “kis bbox snippets”kis bbox snippets --snip "name|start|end" [--snip ...] [files...]| Flag | Short | Type | Default | Meaning |
|---|---|---|---|---|
--snip | string array | : (required) | "name|start phrase|end phrase" (repeatable) | |
--input | -i | string array | nil | Path to a bounding-box file (repeatable) |
--output | -o | string | "" (stdout) | Output file path |
--case-insensitive | bool | false | Ignore case for every snippet | |
--include-end | bool | false | Include the ending phrase for every snippet | |
--fuzzy | int | 0 | Max Levenshtein distance per word; 0 is exact |
Each --snip value is split on the first two pipes, every part is whitespace-trimmed, and none may
be empty. Output is always a single JSON object mapping snippet name to extracted text, there is no
format flag.
kis bbox snippets --snip "hdr|Invoice Number|Total" data/page1.bboxkis bbox snippets --snip "header|Invoice|Total" --snip "footer|Terms|END" -i pages/page1.bbox{ "hdr": "Invoice Number INV-42"}A snippet that is not found maps to "" and the command still exits 0.
The history and audit sidecar
Section titled “The history and audit sidecar”--history and --audit print their section and also record it in a
<basename>-hist-audit.json sidecar. --history-audit <dir> routes the sidecars to a directory,
mirroring each input’s relative path from -i, and on its own asks for both sections, naming an
output directory is only meaningful if something is written there.
Nothing is written unless one of those three flags asked for it. --mode doc writes one sidecar,
named after the first resolved input.
{ "history": [ { "rule_id": "ClassifyStatement", "key": "document_type", "value": "BANK_STATEMENT" } ], "audit": [ { "rule_id": "ClassifyStatement", "message": "executed" }, { "rule_id": "ClassifyStatement", "message": "header matched" } ]}Exit codes and errors
Section titled “Exit codes and errors”Every command exits non-zero on failure. kis rules prints a per-input line, continues with the
remaining inputs, prints the count, then exits 1 if anything failed, partial success is still exit 1.
ERROR: /work/pages/page3.json: executing rules: executing ruleset rules: evaluating expression in rule 'ClassifyStatement' the when raised an error. got left hand expression error. got non existent key headerProcessed 2/3 input(s) successfully1 input(s) failedRule-evaluation errors arrive wrapped like that: the input, then executing rules:, then
executing ruleset <name>:, then the engine’s own message. Read the tail first. It names the rule
and the thing it could not resolve. What the rules wrote before the failure is not printed.
Those ERROR: lines go to stderr, and so do the warning: lines about dropped inputs and the final
failure count. Results and the Processed N/M input(s) successfully line go to stdout.
kis propagates the plugin’s exit code verbatim, forwards SIGINT, SIGTERM and SIGHUP to it,
and reports a signal-terminated plugin as 128+N. Usage text is suppressed on error at every level,
so a failing command prints only its error line.
| Message | Cause |
|---|---|
Error: '<command>' is not installed. | The rules plugin is not on any search path |
required flag(s) "rules" not set | -r omitted; same shape for anchor, phrase, start, end, snip |
flag needs an argument: --fuzzy | --fuzzy with no value on bbox snippet / bbox snippets |
no inputs resolved (...) | Nothing matched, a hard error, not a no-op. Under --require-bbox, usually every .json lacked its .bbox; the warning: line above names them |
loading rules: <file>: got N error(s) in grl the script | A rule file did not parse, or reintroduced a rule name already in the set |
loading rules: <file>: a grule set is .grl and vocabulary files | -r named a file that is neither |
the product has no scope <name>: ... | --scope named neither a static scope nor a top folder |
rules "<scope>" do not validate: | kis rules check or --scope found a call that cannot resolve |
at least one input file is required (use positional args or -i flag) | A kis bbox reading subcommand got neither -i nor a file |
invalid var format "<v>": expected key=value | A -v entry with no = |
invalid fact specification "<spec>": name and path must not be empty | A -f entry like =path or key= |
invalid bbox specification "<spec>": name and path must not be empty | A -b entry like =path or key= |
invalid snippet definition "<s>": expected "name|start phrase|end phrase" | A --snip value that did not split into three non-empty parts |
invalid --skip-pattern "<pat>": <err> | A --skip-pattern that is not a valid glob |
No input: pass one or more hocr files or directories ... | kis bbox create run with no input |
... the when raised an error. got non existent key <fact> | A rule read a fact the input did not supply |
... have no function named <Method> | A misspelt method, reached at run time; --validate finds it first |
Header validation failed: missing headers: <list> (found: <list>) | A bbox table -H value is absent |
Header validation failed: table has no headers | bbox table -H on a table with no detected header row |
Rule-evaluation failures surface here too, a missing fact key, a wrong document name or a bad function call aborts the whole execution for that input; see Troubleshooting.
Continue with
Section titled “Continue with”- Quickstart: the shortest path from a rule file to a result.
- Writing rules: rule anatomy, salience and what actually compiles.
- Validating a rule set: what
--validateandkis rules checkreport. - Extraction: fields, checkboxes and schema-driven extraction.
- Rules over documents: the
bboxnamespace a rule calls. - The rules service:
rules.svc, its routes and how rule sets arrive. - Error reference: external error codes.