1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
|
# Changelog
## Unreleased
## 0.0.7 — 2026-09-14
Security and correctness hardening, from a threat model (spec §15.1) and the
tests that hold it.
- File names, paths and tool messages can no longer control the terminal:
control characters, bidirectional controls and invalid UTF-8 are printed
as escapes (`\x1b`, `\u202e`) everywhere krino prints them.
- A placeholder can no longer rename a file to "", "." or "..", or add a
".." to a destination; such a step is skipped with a reason.
- A file swapped between planning and applying - for a symlink, or for
another file with the same size and modification time - is not acted on.
- Undo refuses a trash entry name that is not a plain name inside the
Trash, and a trashinfo whose path is not absolute.
- Undo reverses a file its run renamed and then moved, moved twice, or
moved and then sent to the Trash. Before, undo judged the first step
against the disk as it is now, found its destination empty (the later
step had moved the file on) and refused the whole file as "missing". Found
by the new generated apply-and-undo test.
- Folding maps the capital sharp s (ẞ) to "SS", as the other capitals
already were: "STRAẞE" in a document now matches the keyword "straße".
- Folding replaces invalid UTF-8 with U+FFFD first, so a stray invalid byte
no longer stops the letter after it from losing its accent.
- `krino new` refuses a directory whose path is not valid UTF-8 with that
reason, instead of reporting a broken template.
- The keyword cache is also discarded when normalisation changes, so answers
cached before these fixes are recomputed once.
- Builds need Go 1.25 or newer and use the Go 1.26.8 toolchain, which an
older `go` downloads itself. `golang.org/x/text` is updated to v0.41.0.
This fixes an infinite loop a crafted file's text could cause
(GO-2026-5970) and, through the toolchain, the XML nesting guard
(GO-2026-6088) and the `os/exec` and `os` fixes govulncheck reported.
- Tests: fuzz targets for every decoder of outside data (`make fuzz`), a
generated apply-and-undo round trip (`KRINO_PROPERTY_RUNS`),
enum-completeness tests, `make race` and `make vulncheck`.
## 0.0.6 — 2026-09-14
- Choosing per file: `w` now applies what was decided and quits krino,
instead of going on to the next directory, so a long review can be done a
session at a time. Files never reached are not logged as declined; the
outcome line counts them as "not reviewed".
- Each choice is echoed on its own red line under the file (`→ yes`,
`→ trash`, ...).
- Enter is ignored at the review prompts instead of being reported as a
wrong key.
## 0.0.5 — 2026-09-14
- Keyword cache: for each file whose text it extracts, krino records which
content keywords the text contains, in `~/.cache/krino/NAME.cache`, and
answers content tests from it while the file is unchanged. A rerun over
the same files extracts nothing: on a real downloads folder a dry run
went from 12.8 s to 0.13 s. No text and no file names are stored. A
changed file, a new keyword, or a changed extraction tool means reading
again; failures are never cached. `krino check` shows where the cache is.
- Choosing per file: `t` sends the file to the Trash and `d` deletes it
permanently, after a `y` to confirm, instead of what its rules planned;
both are logged under the rule `(review)`. "Done, apply chosen so far"
moves from `d` to `w`, in undo's review too.
## 0.0.4 — 2026-09-14
- `max-size` setting: files larger than it are skipped as "too big", in
`(defaults ...)` or a directory file.
- `(exclude COND...)` sets files aside before any rule sees them, by type or
extension, name regex, content, or any other condition; all conditions in
one form must hold. In `krino.conf` it applies to every directory, in a
directory file to that directory. Excluded files are counted in the plan
and listed with the form that matched under `-v`; `explain` traces every
exclusion and `check` lists them.
- `--min-age DURATION` overrides every directory's `min-age` for one run,
e.g. `krino -n --min-age 0` to include files written a moment ago.
- `krino init` and `krino new` write commented examples of all three.
## 0.0.3 — 2026-09-14
- The plan is shown as one block per file instead of a table: the file's
name, each step, then the rule and the reason it matched. On a terminal
every line wraps to its width, with continuation lines indented under
their own column; piped output is never wrapped.
- Choosing per file (`c`) shows that same full block for each file, not only
its steps.
- `-P` / `--no-pager` prints the plan straight out instead of through the
pager.
- A duplicate's original is shown with `~` for the home directory.
## 0.0.2 — 2026-09-14
- Duplicates are found, never deleted. A rule combining `(duplicate)` with
`(delete)` or `(delete permanent)` is refused by `krino check` and every
run, and a file that is a duplicate under any duplicate scope its
directory's rules use gets no delete step from any rule. This replaces
0.0.1's known limitation: duplicate conditions with different scopes can no
longer delete every copy of a group, only move copies aside.
- Coloured output on a terminal, in the terminal's own 16-colour palette:
actions by kind, rule names, skipped steps and reasons, warnings, the
outcome counts and the prompt keys. `--no-color`, before or after any
subcommand, turns it off, as `NO_COLOR` does.
## 0.0.1 — 2026-09-13
- Per-directory rule files, a main file listing which directories run, and a
template for new ones.
- Conditions with `and`, `or` and `not` over type, name, path, content, size,
age and duplicates.
- Actions copy, move, rename and delete (to Trash by default), chained.
- Plan, review and approve — all files or per file — with `-y` to skip
review.
- A log of every step, and `krino undo`.
- Placeholders in destinations and new names.
- Builds for Linux, FreeBSD and OpenBSD.
Performance: no threshold or target for 0.0.1. These were measured during
development and are reported rather than promised — throughput is dominated
by external content extractors, not by krino itself:
- Full plans over a real folder of about 265 files, 164 of them needing text
extraction through `pdftotext` at roughly 80 ms each, measured between
about 13 and 20 s across runs.
- A synthetic benchmark during development planned a generated tree of 4,405
files needing no extraction in 1.09 s. That benchmark is not `make bench`,
whose generated tree is smaller.
Files that share a byte size but differ in content — six templated invoices,
for instance — are still told apart, because the duplicate check hashes the
first and last 64 KiB and then the whole file rather than trusting size.
Known limitation: `(duplicate)` conditions with different scopes can elect
different originals for the same content, and within one directory's rules
this can select every copy for deletion — see `krino.conf(5)`, DUPLICATES.
|