Releases: iskandarputra/zyshell
Release list
zy v1.0.46
Official binary release for zy 1.0.46.
What's changed in 1.0.46
Added
-
An interactive
auditbrowser with analytics. Runningauditon a
terminal opens a full-screen, mouse-driven viewer of the command ledger.
A List view: scrollable rows with risk badges and exit codes, a detail
strip, live/search, risk and failed-only filters, in-place chain
verification that jumps to any broken sequence, and JSON export of the current
view. An Analysis dashboard: totals, failure rate, risk breakdown bars,
top programs, and per-day activity. Enter (or a second click) opens a
deep-detail overlay with every field and the full entry/prev hashes. Mouse
wheel scrolls, clicks select and switch tabs. Scripts andzy -c auditstill
print the text status. -
audit listand pipeable audit output.audit list [N],audit search,
andaudit risknow emit a structured table instead of plain text, so the
ledger flows through the pipeline:audit list 500 | where exit != 0,
audit list | where risk == destructive,audit list 50 | to csv.auditis
now a data-aware builtin, so it stays in-process across a pipe. -
audit pruneand automatic retention with a size cap.audit prune [--days N] [--vacuum]trims old entries and optionally reclaims disk space;
the oldest contiguous prefix is removed so the surviving chain still verifies
from its new genesis. The shell auto-prunes once a day to[audit] retention_days(default 30) and[audit] max_entries(default 50000),
whichever is stricter, so the database stays bounded even under heavy use
(0 disables either limit). -
policyrules now persist, and there's a manager TUI. Rules added with
policy addare saved to~/.zy/policy.confand auto-load at startup, so they
survive a restart (previously they vanished unless you passed--policy FILE
every launch). New subcommands:policy remove/enable/disable <n>,
policy save,policy test <cmd>(shows whether a command is allowed and
which rule matched), andaddnow takesrate:N/priority:N.policy list
emits a table, sopolicy list | where action == denyworks. Runningpolicy
on a terminal opens a full-screen manager: a live test field (type a command,
see allow/deny against the rules), action badges, toggle/remove, a
restricted-mode switch, mouse + keyboard. -
z/zinow learn the directory a terminal opens in. Before, a directory
only entered the frecency database when you activelycdinto it, so a shell
started directly inside a project (an IDE or file manager "open terminal here",
a new tmux or split pane) was invisible tozandziuntil you navigated
somewhere and back. The interactive shell now records its startup directory on
launch.zy -cand scripts still do not touch the database.
Changed
- Audit logging is now opt-in (off by default). It used to run for everyone
and grow silently (multi-hundred-MB ledgers were common). It is a
team/server/compliance feature, so it now stays off until you set[audit] enabled = truein~/.zy/config.toml. A ledger you already collected stays
readable, verifiable, and prunable after you disable it;auditwith nothing
to show points you at the switch. (--no-auditstill force-disables.) audit statusnow shows the DB size and retention window, andaudit verifypinpoints the break. Verification used to just say pass/fail; it now
reports the exact sequence number and reason (hash mismatch vs broken link)
when the chain is bad.audit.sqlite-walis also truncated on a clean exit so
it stops accumulating across many shells.customize's pill-shape pickers now preview the full prompt, and honor your
merge setting. The Library and Mix & Match tabs used to show a fixed two-pill
mock that always looked un-merged, no matter how your prompt was actually set
up. They now render the same three-pill sample prompt the main menu shows, using
the highlighted shape and your live "Merge pills" setting, so a merged ribbon
previews as a connected ribbon and separate caps preview as separate caps.
Fixed
- A
cdvisit could be silently dropped under concurrent-shell contention.
The frecency write-through ignored SQLite's return code, so if a write lost the
WAL lock past the busy timeout (common when several zy shells run at once), the
visit vanished with no error. It now retries the write a few times before
giving up. jump.sqlite-walno longer creeps up to its 4MB checkpoint threshold and
stays there. With many long-lived shells the default passive auto-checkpoint
kept losing the race to a concurrent reader, leaving a multi-megabyte WAL over a
table of a dozen rows. The shell now truncates the WAL back into the main
database on a clean exit.
Verify your download
curl -LO https://github.com/iskandarputra/zyshell/releases/download/v1.0.46/zy_1.0.46_amd64.deb
curl -LO https://github.com/iskandarputra/zyshell/releases/download/v1.0.46/SHA256SUMS
curl -LO https://github.com/iskandarputra/zyshell/releases/download/v1.0.46/SHA256SUMS.minisig
curl -LO https://github.com/iskandarputra/zyshell/releases/download/v1.0.46/zy-release.pub
minisign -Vm SHA256SUMS -p zy-release.pub # confirms maintainer signature
sha256sum -c SHA256SUMS --ignore-missing # confirms the .deb hash matchesFull signing policy: docs/ops/RELEASE_SIGNING.md
VirusTotal scan
This release was uploaded to VirusTotal at publish time. Click through to see the live verdict from 65+ AV engines:
(Scans run within ~5 minutes of upload. If the page shows "Item not found", wait a moment and refresh.)
Try without installing
docker run --rm -it ghcr.io/iskandarputra/zyshell:1.0.46Install (Debian / Ubuntu)
Download zy_1.0.46_amd64.deb from the assets below, then:
sudo dpkg -i zy_1.0.46_amd64.deb
sudo apt-get install -fzy v1.0.45
Official binary release for zy 1.0.45.
What's changed in 1.0.45
Added
explore's AI (Ctrl-Q) now researches your files itself. With
[ai] provider = "anthropic"set, asking a question no longer just sends
the focused file's head: the model searches the tree on its own (live
grep, file-name lookup, and multi-pattern search, all run inside zy
against the warm index and scoped to the directory you're in), then
answers with path:line citations. You watch each search happen, it stops after six
rounds, and the privacy default is unchanged: provider starts "off" and
nothing ever leaves the machine until you opt in. Other providers keep
the existing one-shot behavior.explorebrowses inside archives and SQLite databases. Enter on a
zip/tar/7z (any libarchive format) opens its contents like a folder:
members grouped by directory with sizes, and Enter on one extracts just
that member safely to /tmp and opens it in the viewer. Enter on a
.sqlite/.dbfile lists its tables with row counts, and Enter on a
table shows its rows as a real typed table. Everything inside a
container is read-only; Esc returns to the listing.explorereads an[explore]section from~/.zy/config.toml.
Set your startup defaults once:show_hidden = true,sort = "size"
(name/size/date/type),sort_reverse,columns = 1(the metadata
columns,cycles),max_entries, andgit_timeout_ms. Bad values
never break anything, they fall back or clamp with a one-line note
before the explorer opens. The existing[explore.keys]bindings and
environment variables keep working (env wins over config).explore's diff viewer stages hunks. Inside the diff view,a
stages the hunk you're looking at,runstages it, andX(with a
confirm) throws it away from the working tree. A lazygit-grade review
loop without leaving the file manager. The patch piped to git is a
byte-exact slice of git's own output, so it always applies cleanly; the
view refreshes after each action and closes itself when the last hunk
is dealt with.exploreexports a whole-directory (or whole-selection) HTML report.
Mark a set of entries (or just put the cursor on a folder) and press
H: one self-contained offline page with every file in the set, each
rendered the right way (syntax-highlighted source, CSV as a real table,
images inline, binaries as a size note), directories walked recursively,
with a sidebar of per-file links and open-in-editor links. A single
focused file keeps the existing rich document page.explore's grep pane jumps symbols across naming conventions. With
grep results open,*expands the pattern into its case variants
(snake_case, kebab-case, camelCase, PascalCase, SCREAMING_SNAKE) and
re-greps them all in one pass, so searchingparse_stringalso surfaces
everyparseString,ParseString, andPARSE_STRINGcallsite. Scope
chips likeext:care kept.explorecan diff any two things. Mark exactly two entries (two
files, two directories, in a repo or not) and press\: the side-by-side
diff viewer opens on the pair, with the same syntax highlighting,
word-level diff, andn/Nchange navigation the git diff viewer has,
andhexports the self-contained HTML diff page for the pair. Identical
files say so in one line. With anything other than two entries marked,
\keeps toggling the diff preview as before.exploregets a;command line: pipe the selection through real zy
builtins, or hand a command off to the prompt. Press;and type
| where size > 10mb | sort-by modifiedto run that pipeline over the
marked files in-process (they flow as the same typed name/type/size/modified
tablelsproduces, so every filter works verbatim), with the result
rendered right there. Type anything else as a template (%sthe selected
paths,%dthe directory,%tthe focused entry, all safely quoted) and
the explorer exits with the composed command waiting pre-filled at the zy
prompt, ready to edit before running. Works in grep-results mode over the
selected hit files too. Rebindable ascommand_linein[explore.keys].- Ranges are lazy.
1.. | first 3,seq 4 600000000 | first 5, and
1.. | skip 5 | first 2answer instantly at flat memory instead of
freezing the terminal or hitting the old "exceeds the 10000000 limit"
error.first,take,skip,last,length, andmath sumstream
(length/math sumgive the true answer for any bounded range, like
1..600000000 | math sum→180000000300000000);skipon a range
stays a range so laziness composes;describesaysrangeandto nuon
prints the1..10literal, both matching nu. Anything else sees the
range as a bounded list, so no command can be surprised into an OOM.
Unbounded ranges (1..) now parse. Smallseqoutput is unchanged. - The Nushell
exportkeyword family works.export def,export const,
export alias,export use,export module, andexport externparse as
module-member declarations, so the canonical nu module pattern runs:
module m { export def hello [] { "hi" } }; use m hello; helloprintshi,
andexport const X = 99imports cleanly.export-env { $env.FOO = "bar" }
is a new builtin whose block's$envchanges persist (top level and inside
modules).externsignature declarations parse and no-op instead of
erroring a module load.use mod *imports every member (the*is no
longer glob-expanded into filenames), and trailing names after a file
module are member selectors. Plain POSIXexport VAR=valis unchanged. fff-findcan filter by git status:git:modified,git:untracked,
git:conflicted,git:clean, andgit:dirty(anything but clean) work as
query constraints alongsideext:/path:/glob:/name:, with bare
aliases likeuntracked:and!git:...negation. So
fff-find 'git:modified'lists just the files you have touched since the
last commit, andfff-find --pick 'git:dirty'opens the picker on them.
Resolved from the in-process.git/indexcache (nogitsubprocess).fff-servegrep responses carry anexthint: a one-sentence
follow-up in plain language (read the top match at its line for context,
raisemax_resultsortime_budget_mswhen a limit tripped, try
find_fileson zero matches), so AI clients get the obvious next step
without re-deriving it from the truncation flags. Applies to bothgrep
andmulti_grep.- The interactive picker tolerates typos. When the live filter in any
zyfuzz picker (fff-find --pick,fzf, history search) matches nothing,
fuzzy terms of 3+ characters get a second pass that forgives one mistyped
character (two for 6+ character terms), sowhreestill narrows to
where.cinstead of emptying the list. Correctly-typed queries rank
exactly as before, and exact ('foo), prefix, suffix, and negated terms
are never typo-tolerated.
Changed
lslists entries sorted by name (plain byte-wise compare, the same
order nu uses) instead of raw directory order, sols | first,
ls | where …, and every other pipeline overlsis deterministic and
matches nu.ls -ashows dotfiles but no longer lists the.and..
pseudo-entries, and thecreated/accessed/modifiedcolumns now carry
nanosecond precision (2026-06-10T22:17:07.126711977+08:00in nuon,
byte-identical to nu).
Fixed
matchon a record no longer corrupts memory (ZY-070). Matching a
record pattern (match $x { {k: $val} => … }) freed each field it
examined even though the record still owned it, a latent use-after-free
that AddressSanitizer caught while gating the lazy-ranges work. The
field is a borrowed view now, freed exactly once with its record.- More Nushell-parity gaps, conversions and paths:
into binarymaps
over list input (ls | get size | into binary→ a list of binaries, not
the bytes of the rendered[…]text) and converts a filesize to its
8-byte count like nu (it used to stringify1.0 kB);path expandkeeps
the list shape (ls | get name | path expandstayslist<string>even
for one row);lsemits null for a non-symlink'stargetso
default 'nothing' targetfills it; and the bare$envrecord holds
PATHas a list of directories ($env | get home! path!matches nu
byte for byte). - A batch of Nushell-parity gaps in the filter family, each verified
against nu live:where <col> >= <value>no longer errors (the split
>=operator is re-joined, and a substituted(date now) - 2wkvalue
evaluates as datetime arithmetic);uniq-by --keep-last/-lreturns the
last occurrence per key;get name.2indexes the collected column and
get 2.nameworks; multi-keygroup-by lang year --to-tableproduces
nu's flat shape with key-named columns ([[lang, year, items]; …]);
barelaston a table returns the final row as a record likefirst;
eachover nothing passes the nothing through ($env.name? | each {…} | default "bye"→bye); a zero-row table serializes as[]in nuon; and
a nothing-valued interpolation segment renders as empty instead of the
literalnull($"a(if false {'x'})b"→ab). - Quoted table cells keep parens and brackets (ZY-069).
[[name]; ['(((((']]used to come out as garbage (name: ((((("" ]),
and'abc)'or'abc]'lost their last character, because the table
parser couldn't tell quoted content from the literal's own brackets.
Tokens now carry the quoted span's offsets, so anything inside quotes
stays data no matter how structural it looks. joinhonors--suffix/--prefixafter the positionals (ZY-067).
join <table> <key> --suffix _r, the way nu usually spells it, silently
dropped the flag and the colliding column came out asa_(the collision
fallback) instead ...
zy v1.0.44
Official binary release for zy 1.0.44.
What's changed in 1.0.44
Added
-
customize→ Show / hide pills gains bulk toggles. Pressato show every pill ordto hide every pill in one keystroke, instead of toggling 19 sections one at a time. The footer and preview pane advertise the keys. -
New
merged-segmentsprompt preset — the default look as one connected ribbon.prompt-preset merged-segmentsinstalls a version of the default prompt where the background pills (directory, git, languages, duration) join into a single Powerline ribbon via gradient seams. The interesting part is that it stays clean off-project: each pill is wrapped in a(…)conditional, so an absent module (no git repo, no Python, …) drops the whole pill including its seam, with no empty colored stub left behind, while the ribbon reconnects across the gap. Fg-only segments (git metrics, jobs) sit after the ribbon so they never punch a hole in it. Nerd Font required; like every preset it backs up your existing config first. -
pill_merge = truejoins your prompt pills into one connected chain. Set it at the TOML root and every run of adjacent colored pills renders as a continuous Powerline ribbon (directory git python) instead of separate floating pills, with each seam colored to flow from the previous pill into the next. The seam glyph followspill_shape(soslantgives slant seams, not just arrows), defaulting to the solid arrow. It's the one-key version of hand-writingfg:prev_bgcap glyphs between every pill, and renders byte-for-byte the same. Toggle it live incustomizeunder "Merge pills (connected chain)" — the preview shows the merged look. It acts on a top-level bare-pill format like[ $mod ](bg:..)(e.g.prompt-preset pastel-powerline); the shipped default prompt bakes caps inside each module, so it's a no-op there. Don't combine it with hand-written caps or they double up. -
astis implemented (was a "not implemented in zy" stub): it parses a pipeline string with zy's own lexer and recursive-descent parser and dumps zy's AST, the same tree the executor walks.ast 'hello'returns a{block, error}record whereblockis an indented tree;--json/-jreturns the AST as JSON (--minify/-mfor compact);--flatten/-freturns a{content, shape, span}table, one row per lexer token with its highlight shape (shape_internalcall,shape_pipe,shape_string,shape_flag, …) and byte span, matching nu's flatten view (ast "'hello'" --flattengivesshape_stringat span 0..7). It dumps zy's tree rather than nu's Rust AST types, and the flatten view reports what zy's shell tokenizer actually sees (e.g. a top-level>lexes asshape_redirection).
Changed
- The
customize"Merge pills" toggle now actually merges your prompt.pill_mergeis a runtime flag that only rewires a top-level bare-pill format; the default prompt bakes its caps inside each module, so enabling the toggle there used to do nothing to the live prompt (only the preview changed) and read as a bug. Now, on that per-module layout, turning the toggle on switches your prompt to the connectedmerged-segmentsribbon while keeping your theme and pill shape (its colors already match the default), and saves a one-toggle restore point; turning it off restores your previous prompt exactly. On a format that already has top-level pills it still just setspill_merge = true. - Top-level
(…)conditional groups now collapse when empty, like they already do inside a module. Previously a(…)in the top-levelformatalways rendered its contents; now it disappears when none of the$modulevariables inside it produce output, matching the documented (and starship) conditional semantic. This is what makes a stub-free merged ribbon possible (an absent pill drops its seam too). No shipped config used a top-level(…), so existing prompts are unaffected; if you relied on a top-level(…)rendering unconditionally, drop the parentheses. make testis now the fast core (C unit tests + validation + security, ~1 minute) for a quick edit→test loop. The slow, heavy suites (shell integration, nu-parity compat, PTY end-to-end) are opt-in viamake test-full(or./tests/run_tests.sh --full), which is what CI andmake test-allnow run. Sanitizer runs and--filterstill imply the full set so they never silently narrow coverage. The plain-run PTY timing scale defaults to 2x for explorer-render head-room.- The unit-test build is much faster after a source edit. About 34 unit tests link the whole object set, and each used to run its own ~20-second whole-program LTO link (the objects are slim LTO, so every link redid the codegen). The runner now does that codegen once into a single shared object blob and links each test against it in well under a second, so a cold
make testdropped from ~230s to ~50s andmake test-fullfrom ~330s to ~140s, with no change to the shipped binary. Sanitizer runs keep the per-test link; pass--no-blobto fall back to the old path.
Verify your download
curl -LO https://github.com/iskandarputra/zyshell/releases/download/v1.0.44/zy_1.0.44_amd64.deb
curl -LO https://github.com/iskandarputra/zyshell/releases/download/v1.0.44/SHA256SUMS
curl -LO https://github.com/iskandarputra/zyshell/releases/download/v1.0.44/SHA256SUMS.minisig
curl -LO https://github.com/iskandarputra/zyshell/releases/download/v1.0.44/zy-release.pub
minisign -Vm SHA256SUMS -p zy-release.pub # confirms maintainer signature
sha256sum -c SHA256SUMS --ignore-missing # confirms the .deb hash matchesFull signing policy: docs/ops/RELEASE_SIGNING.md
VirusTotal scan
This release was uploaded to VirusTotal at publish time. Click through to see the live verdict from 65+ AV engines:
(Scans run within ~5 minutes of upload. If the page shows "Item not found", wait a moment and refresh.)
Try without installing
docker run --rm -it ghcr.io/iskandarputra/zyshell:1.0.44Install (Debian / Ubuntu)
Download zy_1.0.44_amd64.deb from the assets below, then:
sudo dpkg -i zy_1.0.44_amd64.deb
sudo apt-get install -fzy v1.0.43
Official binary release for zy 1.0.43.
What's changed in 1.0.43
Changed
- Filesize units now match nushell (SI/metric):
1kbis 1000 bytes (was 1024),1mbis 10^6, and so on, while the IEC*ibforms stay binary (1kibis 1024). Display usesB/kB/MB/GB/TB/PBwith the value truncated to one decimal, like nu. If you relied onkb=1024, usekib.
Added
-
to msgpack/from msgpackround-trip values through the MessagePack binary format ([foo, 42, false]↔ the packed bytes). -
encode/decodesupport text charsets via iconv:shift-jis,euc-jp/euc-kr,utf-16/-le/-be,big5,gb18030,latin1,windows-1252, and more, with--ignore-errors.utf-16is decode-only. -
nu-checkvalidates nushell syntax with zy's own parser:'1 + 1' | nu-checkistrue,'if true {' | nu-checkisfalse;--debugprints the parse error. -
Typed
mathaggregations:math sum/avg/min/max/median/modepreserve the unit, so[1min 2min 3min] | math sumis6minand[5kb 10mb 200b] | math medianis5.0 kB;datetimesupportsmin/max. Mixed numeric input is unchanged. -
str length/str substring/str index-oftake--grapheme-clusters(-g) to count/index by extended grapheme cluster (flags, ZWJ sequences, combining marks) instead of bytes. -
More
ansicodes:cursor_on/cursor_off,cursor_blink_*, screen/line erase, cursor movement,attr_*/reset_*aliases, and nu's single-letter aliases (r g y b m p c w k i u d h s n). -
jobnamespace (nu-style closure-thread jobs):job spawn { … }runs a closure on a background thread and returns its id, withjob id,job list,job kill,job send/job recv(a thread-safe per-job mailbox), andjob flush. Closure execution is serialized against the main thread by a per-command executor lock (released while a worker blocks onsleep/job recv), and a worker's exit-status is thread-local, and the whole thing is TSan-clean. The bash-stylejobsprocess control is unaffected. -
describe -d/describe --detailedreturns nu's detailed record instead of the plain type string: a nested{type, detailed_type, value, …}withlengthfor lists and binary,columnsfor records, and a per-element table for lists, built recursively so each nested value is itself a detailed record. Thedetailed_typefield matches nu's full type spelling (table<a: int>for a list of records,list<oneof<int, string>>for mixed lists, recursiverecord<a: record<b: int>>). nu'srust_typefield is intentionally omitted (it exposes nushell's internal Rust types, meaningless in a C shell).--no-collect/-nis accepted but a no-op, since zy is eager and has no streams. The plaindescribe(no flag) is unchanged. -
into sqlite <path>writes the pipeline table into a sqlite database file (default tablemain,--table-nameto override), matching nu. -
Timezone-aware, sub-second datetimes (ZY-062): a datetime now carries its source UTC offset and nanoseconds, so
into datetimeround-trips+HH:MMand fractional seconds,into recordincludes millisecond/microsecond/nanosecond and the source timezone,into intgives the full nanosecond count, andinto stringmatches nu'sSat Oct 10 10:00:00 2020form.into datetimealso parses a{year..timezone}record, a list of strings,%Pam/pm,%zoffsets, and the Europeand.m.Yforms. -
transpose -d/--as-recordand the-l/--keep-last/-a/--keep-alldedup companions;rename --block {closure}renames each column by running the closure on its name. -
Digit separators in number literals:
1_000,1_234_567,3_141.59.
Fixed
-
A bare builtin statement before
;no longer prints its value (job id; 99,whoami; 99→ just99, matching nu):nu_outputnow honors force-capture likeinto_output. -
to jsonof a binary emits nu's byte-int array (0x[ff aa 01]→[255, 170, 1], indent-aware), not a quoted"0x[ff]"string. -
to nuonof a duration and binary match nu: a duration serializes as its raw nanosecond count (1day→86400000000000ns) and a binary as uppercase no-space hex (0x[ff]→0x[FF]), so they round-trip anddescribe -dof a duration/binary matches nu in every field. -
Duration and filesize arithmetic keep their unit:
(500day + 4hr + 5sec)is a duration (was a float), sointo recordbreaks it into week/day/hour;dur / duris a float ratio. -
flatten --allrecurses into nested list/table columns; a nested[[ ]]table literal inside a cell now parses. -
joinaccepts an inline[ … ]table as its right side and supports--suffix/--prefixfor colliding columns. -
insert/upsertwith a dotted cell-path key (a.0.b) descend instead of making a literal column;group-by --to-tableemits[{group, items}, …];findmatches list elements by value for numeric/filesize terms;seqhonours an explicit 3-arg step like nu (ZY-061);inspectas the last stage forwards its value to stdout (ZY-060). -
Empty quoted strings survive in records and tables (
['', 'a'],{a: '', b: 1},{a:'' b:1},[[a]; ['']]), and a$"…"value in a brace block (if c { $"label: (x)" }) no longer mis-parses as a record. -
to nuonquotes digit/dot/space-bearing strings like nu ({c1: 1}→{"c1": 1}). -
Cell-paths on list and record literals (ZY-053):
[10 20 30].1→ 20,{a:1}.a→ 1,{a:{b:2}}.a.b→ 2. -
$nu.os-infois a record (ZY-056) with name/arch/family/kernel_version, so| get nameand.archwork. -
String interpolation inside a record value (ZY-054):
{a: $"x ($n)"}evaluates the($n)block. -
bits shr --signedsign-extends and the width auto-sizes (ZY-044):-2 | bits shr 1 --signed→ -1
(was 127), and ints over 255 no longer wrap (256 | bits shr 1→ 128).-s/-nshort flags now parse (ZY-039). -
constaccepts a list or record value (ZY-038):const x = [1 2 3]binds the whole list, not just[1]. -
The fff MCP server advertises usage instructions so a fresh client knows the grep/find_files/multi_grep tools.
-
Whole-valued floats print with
.0(ZY-057), matching nu:10 / 2→5.0,9 | math sqrt→3.0,
string interpolation$"(10 / 2)"→5.0.into stringstill drops it (2.0 | into string→2).
math absof an int returns an int;math medianof an even-length list returns a float. -
constaccepts bothconst X=42(POSIX) andconst x = [1 2 3](nu) forms again. The fused form had
regressed to "not a valid identifier". -
A
deffunction now returns an integer value (ZY-042).def double [x] { return ($x * 2) }; double 5
gave nothing because an integerreturnwas always read as a POSIX exit code. Adefnow puts the value
on the pipeline like nu, while a POSIXname() { … }still uses the exit code. -
select / reject / update error on a missing column (ZY-045), like nu, instead of silently dropping it
or creating it. A trailing?marks a column optional (select a?); useupsertto add a column. -
wherehandles column arithmetic (ZY-055):where a mod 2 == 0,where a + b > 6and similar now
filter per row instead of returning nothing. -
A cell-path on a record literal works (ZY-053):
{a:1}.a→ 1,{a:{b:2}}.a.b→ 2. -
sortcompares by value type (ZY-043): a list of genuine strings sorts lexicographically like nu
("2 10 1" | split row " " | sort→ 1, 10, 2) instead of coercing to numbers. -
split charsandstr reverseare UTF-8 aware (ZY-040):'café' | split chars→ c a f é and
'café' | str reverse→éfac, no more byte mojibake. -
Records can mix spaced and compact colon entries (ZY-059):
{b: 4, a: 3, c:1}parses (and so
… | sort -v), in any order ofb: 4/c:1style. -
A huge
seqor range no longer freezes the terminal.seq 4 600000000and a
bare1..600000000used to allocate the whole list and exhaust memory; they now
error with a clear hint past 10M elements. This also removes the old silent
truncation of a range literal at 100k, so ranges up to 10M materialise fully
(1..200000 | lengthis200000, not100000). Lazy streaming (so big ranges
don't materialise at all, like nu) is planned separately. -
whereshorthand matches Nushell.where a > 1 and b > 1now honours the
whole and/or chain instead of just the first comparison;where p.age > 18
follows a dotted column path;where c in ["red" "green"]tests membership; and
where a >= 2/<= 25/== 2/!= 2work (the two-char operators were being
split).where $it mod 2 == 0over a list evaluates with full precedence. -
More commands take closures and map over lists.
sort-by {|x| …}and
group-by {|x| …}work on a plain list of values;into bool/into string/
into filesize/into recordmap element-wise over a list (andinto record
reads a list of[key value]pairs);parse --regexreturns every match per line
(not just the first) and names unnamed groupscapture0…. -
Records, ranges, and control flow.
let r = try { … } catch { … }assigns the
evaluated value; a bare(1 + 2)at the prompt prints3; a quoted string that
matches a command name (if false {"a"} else {"z"}) stays a string;
error make {msg: ("a" + "b")}evaluates the field; a runtime error binds
catch {|e| $e.msg}; the range1..3..9reads3as the second element (step 2)
like nu;matcharm bodies run a nestedmatchor a("x" | cmd)pipeline;
[[a b]]is a list-of-lists; anddef --wrapped foo […]definesfoo. -
String and value fixes.
mut s = "a"; $s = $s + "b"/+=/++=
concatenate;str kebab-case/snake-caseno longer double the separator on
CONST_NAME;$env.HOME?drops the optional marker; an empty string keeps its
type in a list/record/fold; `str su...
zy v1.0.42
Official binary release for zy 1.0.42.
What's changed in 1.0.42
Added
explorecan grep the whole tree into a navigable pane (pressL). Type a
pattern and instead of a throwaway popup you get a results pane: every hit grouped
under its file, each line shown with itsline:col(right-aligned so the snippets
line up) and a snippet, that you scroll like a directory (j/k/g/G). Press
Enter on a hit to open it at that line in$EDITOR, Space to mark hits,Yto
copy their paths, andHto export the whole result set as a self-contained
offline HTML report (hits grouped by file, syntax-highlighted, with a file-tree
sidebar and click-to-editor line links);Escreturns to the file list. You can
scope the search with
leading chips, e.g.ext:c path:src !path:vendor TODOgrepsTODOonly in.c
files undersrcand outsidevendor. File operations (delete, rename, move,
chmod) are disabled in the grep pane on purpose, since a hit is a line in a file
rather than a thing to delete, so a stray keystroke can't touch your files.explorehas a regex bulk-rename with a live preview (pressCtrl-R). Type a
s/pat/repl/flagsexpression and watch a before/after list of every selected file
update as you type; Enter applies it. It's real PCRE2, so you get$1backrefs,
\U/\Lcase changes,/gand/iflags, and a{n}1-based counter for
numbering. Renames happen in place through the same collision-safe two-pass staging
the editor bulk-rename uses, and the modal refuses to apply if two files would land
on the same name.explorekeys are remappable now. Add an[explore.keys]table to
~/.zy/config.tomlmapping an action to a key, e.g.info = "Z"or
compress_zip = "ctrl-z", and that key triggers the action while the default
still works. About 30 actions are bindable (the full list is in the action
registry); press?in explore for the syntax. Single-character andctrl-x
style keys are supported.explorepreviews PDFs as images now (and PostScript / video where the tools
are installed). Move the cursor onto a.pdfand the preview pane shows the
first page rendered as a picture instead of a wall of extracted text; PostScript
and.epsgo through ghostscript and videos through ffmpeg, each rendered to a
thumbnail and cached so a parked cursor doesn't re-run the tool. If the tool
isn't installed it quietly falls back to the old text preview.explore's sidebar now lists your mounted disks. A DISKS section shows each
real mounted filesystem (skipping snap/loop images) with a usage-percent chip
coloured green/amber/red by how full it is; Enter jumps to the mount. The sidebar
also scrolls now, so a long list of bookmarks, pinned folders, disks, and recents
stays reachable instead of clipping at the bottom.explorecan show detail columns: press,to cycle name-only → size →
+age → +permissions. The columns are right-aligned next to each row and
auto-drop when a pane gets too narrow to keep the filename readable, so the
list never looks cramped.explorecan create archives now:czips the selection,Cmakes a
.tar.zst. It compresses the marked files (or the focused one) into a new
archive in the current directory, named after the focused entry and never
overwriting an existing file. Directories are included recursively, with
permissions and symlinks preserved. (Needs a build with libarchive, same as the
existing extract; without it the keys are a no-op.)explorehas a rich file-info card now (pressI). It shows owner and
group, permissions (rwx + octal), size (computed recursively for a directory,
with the item count), modified/accessed/changed times, inode and link count, a
symlink's target, the binary format and architecture for executables (ELF /
PE / Mach-O, e.g. "ELF 64-bit x86-64"), the Linux inode attributes (lsattr),
and a SHA-256 for files up to 64 MiB. Read-only;j/kscroll a tall card and
any other key closes it.explorelisting got three upgrades: natural sort, a fuzzy filter, and a
sort picker. Files now sort in natural order, soimg2.pngcomes before
img10.pnginstead of after it. The in-panel filter ('/') is now fuzzy: the
letters you type only have to appear in order, somtcfindsmy_test.c, and
the closest matches rank to the top. Andsopens a small sort menu (Name /
Size / Modified / Type plus a direction toggle) instead of blind-cycling, so you
can see and pick the sort.n/s/m/tin that menu jump straight to a mode,
andrflips the direction.explorenow remembers your cursor position per directory. Scroll down to
a file deep in a long listing, step into a subdirectory, come back, and the
cursor is right where you left it instead of jumping to the top or landing on a
stale row. The position is kept for every directory you visit (and even across
separateexploreruns in the same shell session). Picking a specific file from
the Ctrl-K / Ctrl-P / Ctrl-G finders still lands you on that file, and each tab
keeps its own independent position.explorediffs now label each hunk with its enclosing function, and the
HTML diff pairs changed lines by similarity (first slice of adelta-inspired
pass, ZY-033). For code files, every@@ … @@hunk header, in both the
in-terminal diff viewer and the HTML page, now shows the enclosing function it
falls in (from git's hunk-header context), styled distinctly. And the HTML
side-by-side page pairs a
deletion with the most similar following addition (a width-weighted token
distance, ported from delta) instead of pairing by position, so unrelated lines
are no longer word-diffed against each other and genuine edits line up.- The in-terminal
explorediff viewer got the same delta-style upgrades
(ZY-033 cont.): the code is now syntax-highlighted (colours layered over the
red/green change tint),n/Njump to the next/previous change, the
filename in the header is a clickable link to the file, and the line-number
gutter auto-sizes to the file (tighter for small files, correct for large
ones). explorediffs highlight merge-conflict markers, and the terminal viewer can
wrap long lines (ZY-033 final).<<<<<<</=======/>>>>>>>conflict
markers are styled distinctly in both the terminal viewer and the HTML page; and
win the terminal diff viewer toggles soft-wrapping of long lines (with the
syntax colours carried across the wrap) instead of truncating them.- The HTML diff page's file list is now a collapsible directory tree
(github.dev-style) instead of a flat list of basenames. Files nest under their
folders, single-child folder chains collapse into one row
(zy_src/08_ui/src/explorer), each folder shows its combined +/- count, and
clicking a folder folds it. explore"open as HTML" can now render fully offline. The viewer'sh
(and the file list'sH) write a standalone HTML page and open it in your
browser. Previously every asset (highlight.js, KaTeX, Mermaid, web fonts) was
pulled from a CDN, so the page lost all syntax colour, math, and diagrams with
no network. zy now prefers a local bundle, resolved from~/.zy/assets,
/usr/share/zy/assets(the.deb), or the dev tree, and falls back to the CDN
only when no bundle is present. Populate the bundle once with
scripts/dev/fetch-html-assets.sh. SetZY_HTML_ASSETS=bundledto never touch
the network, orcdnto force the old behaviour.- The HTML markdown renderer gained real GFM coverage: nested ordered/unordered
lists (by indentation), task lists (- [ ]/- [x]), strikethrough
(~~text~~), bare and<angle>autolinks (including<email>), hard line
breaks (two trailing spaces),+bullets, consumed YAML frontmatter, setext
headings (===/---underlines), reference-style links ([text][ref]and
collapsed[text][]), and footnotes ([^id]references with a back-linked
notes section). Raw inline HTML stays escaped on purpose, since the source file
is untrusted. The header docstring previously claimed features that did not
exist; it now matches what the renderer actually does. explore's "open as HTML" now has dedicated rich renderers for more file
types instead of dumping everything into a<pre>: images show the actual
picture (with pixel dimensions) on a transparency checkerboard; SVGs render as
an image with a RENDER/SOURCE toggle (embedded scripts never execute); CSV/TSV
become a sortable, filterable table; JSON becomes a collapsible tree with a RAW
toggle; and PDFs are handed straight to the browser, which renders them
natively.- The HTML diff page is now a whole-changeset view with a file-tree
sidebar (like github.dev "Files changed"): pressingh/Hexports every
changed file, not just the focused one. The sidebar lists each file with its
+/−counts, jumps to it, marks it viewed (collapsing it, remembered in your
browser), and tracks the active file as you scroll; each file gets a sticky
in-table header you can collapse, and the page auto-scrolls to the file you
invoked it on. Plus inline comment threads: double-click a line to attach
review comments (multiple per line, stored locally per file+line, deletable). - The HTML diff page caught up to github.dev / VS Code. New: intra-line
word/character diff (only the changed tokens are tinted, on top of syntax
colour); jump to next/previous change withn/Nand an "i of N" counter;
a change-density minimap rail; find-in-diff (/); sticky hunk headers;
collapse/expand of unchanged context (the export now fetches a wide 25-line
band so you can reveal surrounding lines in-page); horizontal scrolling for
long lines (the side-by-side table grows to the widest line instead of
clipping it); ...
zy v1.0.41
Official binary release for zy 1.0.41.
What's changed in 1.0.41
Added
spinnerbuiltin and a reusable spinner engine. A newspinnercommand
drives a terminal spinner for long-running work. Wrap a command and it animates
until the command exits, then prints a green check or red cross and passes the
command's exit status through:spinner -s moon "Building" -- make -j8. It can
also spin for a fixed time (spinner -d 3 "Warming up"), list the presets
(spinner --list), or preview them animating (spinner --demo). The animation
draws to stderr, so a wrapped command's stdout stays clean for a pipe.
Forty-two presets ship across all six families: single-glyph spinners (dots,
braille, moon, dice, …), scrolling "linear" rows, sweeping "bar" arcs (including a
two-row Zed/Claude-style bar), and the 2D braille rings (a rotating arc around a
circle, or a filled square or rectangle). The bar, linear, and ring families render
live instead of from a fixed frame table, so the bar fills the terminal width (a
full-width sweep, re-filling on resize) and you can reshape any of them per run:
--width,--height(multi-row bar),--spin cw|ccw,
--motion bounce|loop|squeeze|radiate,--bar-style(sixteen glyph sets),
--track,--radius/--size/--arc/--fade,--centre filled|empty,
--direction/--flow,--slots/--lit, and--phasefor a flux travelling-wave.
The rings rotate smoothly (frames that would not change the rasterized output are
collapsed).spinner --listgroups them by family. They
ship in/usr/share/zy/spinners.tomland are also compiled into the binary, so
spinners work even without the file; a~/.zy/spinners.tomlcan override or add to
them, andscripts/dev/gen_spinners.pyis the single source that generates both.
The families are modeled on the MIT-licensed tui-spinner crate. The engine
(08_ui/src/spinner.cplus the pure rendererspinner_render.c) is shared, so
other zy TUIs can use it too.- Redesigned install banner. The post-install experience moved to a bolder,
on-brand look: the same braille spinner runs the setup steps, sections are drawn
as colored "pills" built from block glyphs (so they render without a Nerd Font,
which may not be installed yet), and the Terms and Setup screens share that
vocabulary. No prompts or behavior changed, just the presentation. - fff-grep inline path scoping. A leading
ext:/path:/glob:/name:
constraint (with!to negate) in the pattern now scopes the search to
matching files:fff-grep "ext:rs TODO"grepsTODOonly in.rsfiles,
fff-grep "!ext:md TODO"skips.md. A plain pattern is unchanged. This is
what AI/MCP callers reach for to avoid grepping the whole tree. - fff-grep context lines (
-A/-B/-C).fff-grep -C 2 foonow returns the
2 lines before and after each match (or-A/-Bfor one side), likegrep.
Context lines are taggedmatch_type = "context"so structured/MCP consumers
can distinguish them, and they're captured from the full file so a match near a
scan-chunk boundary still gets its surrounding lines. - fff-find query:
name:filter + case-insensitiveext:.name:foohard-
filters to files whose basename containsfoo;ext:now matches
case-insensitively (ext:RSfinds.rsfiles), and!ext:rsexcludes them. - fff differential rebuild (L.3). When a file is deleted or a new subdirectory
appears, the fff index used to re-walk the entire tree to refresh. It now re-walks
only the changed directory and copies the rest of the entries from the previous
snapshot, so a localized change no longer pays for a full-tree readdir/stat. The
result is published as a fresh immutable snapshot through the same path as a full
rebuild, so search results are identical. Renames and.gitignoreedits still do
a full rebuild (they can change filtering tree-wide). Covered by a new
thread-sanitizer gate,tests/unit/0714_fff_differential_rebuild.c. libzy_fff.so— the fff engine as a shared library (N).make libzy_fff.so
builds the recursive index + AI-grade grep + watcher into a self-contained shared
object that other tools can link, exporting just thefff_*engine API (the
shell-coupled CLI shims are left out). Output is byte-identical to the in-binary
engine, checked bytests/validation/fff_libzy_so.sh.- The
github_prpill is now recolorable per state. New[github_pr]keys
open_color,draft_color,merged_color,closed_color, andapproved_color
set the pill background for each PR state. Each takes a palette slot name like
color4or a hex like#9d7cd8, resolved against the active theme. To restyle
the whole pill at once, overridestyleas before. customizegained a "GitHub PR colors" picker. Under Appearance, it lets you
set each PR state's background with arrow keys (←/→ cycles the theme palette,d
resets to the default), with a live swatch and sample pill. It writes the
*_colorkeys above, so it is the first per-pill color editor in the wizard.
Changed
- fff-find ranking is sharper. A query that matches a file's name now ranks
that file above one where the match is buried in a parent directory, with an
exact-filename match (user→user.rs) weighted highest, and a gentle
path-depth tiebreaker that prefers the shallower of two otherwise-equal hits. - The fff frecency database now self-maintains. On first use per process it
ages out access history older than 90 days and hard-caps the table, so the
~/.zy/fff.dbaccess log can't grow without bound or accumulate dead rows for
long-deleted files over a long-lived install. Ranking is unchanged (the score
already decays old entries to ~1%). - fff-grep is now ReDoS-resistant and emits less fuzzy noise. Regex matching
is bounded by PCRE2 match + backtracking-depth limits, so a pathological pattern
(e.g.(a+)+$on a long line) can no longer hang the grep — it caps the work and
reports no match for that line instead. And the fuzzy grep path now rejects
widely-scattered matches (a needle whose characters are sprinkled across a wide
span), so low-signal noise doesn't flood results.
Fixed
- fff git-status now works in repos using index format v4. The
.git/index
parser previously bailed on version 4 (prefix-compressed path names) and left the
status cache empty, so the file explorer's git chips were blank for any repo that
enabled v4 (index.version=4/feature.manyFiles). It now decodes v4's varint
path compression, so modified/untracked/clean status resolves correctly. See
KNOWN_ISSUES ZY-028. - fff now honors nested
.gitignorefiles. Previously only the.gitignores
from the repo root down to the indexed directory were applied, so a.gitignore
inside a subdirectory was ignored. The walker now loads each directory's
.gitignoreas it descends and matches rules relative to that directory (the way
git does), in both the single-threaded and parallel walkers — sosub/.gitignore
hides files undersub/without affecting siblings, and root rules still apply
throughout. See KNOWN_ISSUES ZY-026. - fff: four correctness fixes surfaced by an architecture review against fff.nvim.
(1)glob:in a find query now actually globs viafnmatch(glob:*.rsmatches.rs
files by basename) instead of literal substring matching, which matched nothing.
(2)fff-grepwith a regex no longer drops matches past byte 8191 on a long/minified
line — the regex runs over the whole line, and a match-centered window shows long-line
hits correctly. (3) The file watcher now recovers fromIN_Q_OVERFLOW(a dropped-events
burst, e.g. a biggit checkout) by forcing a full rebuild instead of going silently
stale. (4)fff-findresults are now deterministic on score ties (stable path
tiebreaker). See KNOWN_ISSUES ZY-023..ZY-025, ZY-027. - The
github_prpill now actually changes color with PR state. It pushed the
state color into the runtime palette by its slot name (color8,color2, ...),
but the palette setter only accepts a hex literal, so the push was silently
dropped and the pill fell back to the terminal background, looking the same dark
color for every state. The color is now resolved to a hex first, so draft, open,
merged, and closed render distinct backgrounds. - Release notes now carry the "What's changed" block, and re-publishing refreshes
them. The release publisher read the CHANGELOG section after its orphan-branch
step had already wiped the working tree, so the GitHub release body fell back to
bare boilerplate. v1.0.40 hit this first and its notes were patched in afterward.
The section is now captured before the wipe. Re-publishing an existing release also
updates its notes and title now, instead of only replacing the assets.
Verify your download
curl -LO https://github.com/iskandarputra/zyshell/releases/download/v1.0.41/zy_1.0.41_amd64.deb
curl -LO https://github.com/iskandarputra/zyshell/releases/download/v1.0.41/SHA256SUMS
curl -LO https://github.com/iskandarputra/zyshell/releases/download/v1.0.41/SHA256SUMS.minisig
curl -LO https://github.com/iskandarputra/zyshell/releases/download/v1.0.41/zy-release.pub
minisign -Vm SHA256SUMS -p zy-release.pub # confirms maintainer signature
sha256sum -c SHA256SUMS --ignore-missing # confirms the .deb hash matchesFull signing policy: docs/ops/RELEASE_SIGNING.md
VirusTotal scan
This release was uploaded to VirusTotal at publish time. Click through to see the live verdict from 65+ AV engines:
[https://www.virustotal.com/gui/file/338ee07e6c27a04612111b6708daaa69ad437b700763a0b2091afdbdc9169228/detection](https://www.virustotal.com/gui/file/338ee07e6c27a04612111b6708daaa69ad437b700763a0b2091af...
zy v1.0.40
Official binary release for zy 1.0.40.
What's changed in 1.0.40
Added
- Lower glibc floor for the
.deb. Building the release package with
make deb-bookworm(Debian bookworm toolchain) drops the install floor to
GLIBC 2.35, so the.debinstalls on older distributions (Ubuntu 22.04,
Debian 12, and similar). Fixed several headers that were not self-contained
and blocked the older toolchain. - Changelog discipline. A user-visible change or resolved defect now gets a
[Unreleased]bullet in the same change, citing its tracker ID. This is documented
inCONTRIBUTING.mdandCLAUDE.md, andmake bumprolls[Unreleased]into the
dated release section. - Release notes now carry "what changed."
publish-release.shinjects the
CHANGELOG[VERSION]section into the GitHub release notes, above the
verify/install boilerplate. Passing--notes "..."still overrides it. check_changelog_public_safe.shguard. Since the[Unreleased]section ships
verbatim into the public release notes, this fails CI if it contains internal
source paths, commit hashes, archived-doc references, the private repo name, or
secrets (pre-commit, onCHANGELOG.md).- fff: streaming index publish. A large cold index walk now serves partial
search results as soon as it has indexed a checkpoint of entries, instead of
blocking until the whole tree is walked. The not-yet-released tail stays invisible
behind the count fence, and small or medium trees still publish once at the end as
before. Verified under ThreadSanitizer and the fff validation suites. ZY_FFF_STREAM_CHECKPOINTenv knob. Tunes the entry count at which a cold
index walk starts streaming partial results (default 100,000). Lower it on a huge
monorepo to surface results sooner. It also lets the test suite exercise the
streaming path without a 100k-file corpus.
Changed
check_doc_version.shalso guards the CHANGELOG. It fails CI unless the
releasedZYSHELL_VERSIONhas a matching## [x.y.z]section and an
## [Unreleased]buffer exists, so a version can't ship without its changelog entry.- System
/etc/zy/zyrcis now single-sourced. The.debships the default
system rc as/usr/share/zy/zyrc.systemand its post-install step installs that
file to/etc/zy/zyrc, instead of carrying a second embedded copy of the contents.
(make installalready copied the same file.) The installed/etc/zy/zyrcis
byte-for-byte identical to before, verified by a real install, upgrade, and purge of
the package in a Debian bookworm container. The old byte-compare drift guard is now
a single-source wiring guard.
Fixed
- fff: data race in the concurrent reader path. Under concurrent searches with
the file watcher live (notably thefff-serveMCP server while files are being
created), reader threads read the index's entry count without synchronisation
while the watcher appended new entries. Readers now observe entries through a
release/acquire count fence, so only fully-written entries are ever visible, with no
torn reads. This was the groundwork for streaming index publish, verified race-free
under ThreadSanitizer with a new concurrent-reader test. - fff: data race in the streaming index publish. When a freshly-walked index
was published, the file watcher was already armed on the still-live snapshot, so
the publish step's read of the entry count could race the watcher's concurrent
append. The publish now reads the count through the same release/acquire fence as
the rest of the read path. Caught by the concurrent-rebuild gate under
ThreadSanitizer, which now passes. - Test suite is now clean under ThreadSanitizer and AddressSanitizer (no
quarantine flag). Two fixes. First, the audit-policy tests run against an isolated,
throwaway$HOMEinstead of the real~/.zyledger, so running the suite no longer
writes test entries into (or races a live shell on) your actual audit database.
Second, the interactive PTY tests now scale their timing windows under a
sanitizer-slowed shell (ZY_TEST_TIMING_SCALE), which removes the timing flakes that
previously only appeared under sanitizers.
Security
- fff: use-after-free in the concurrent index acquire (ZY-022). Under
concurrent searches while the indexed tree changed (e.g. thefff-serveMCP
server), a reader could dereference an index snapshot that another thread freed
in the same instant, because the snapshot was validated before its reference was
taken. Acquisition now takes the reference under a publish lock held across the
swap, so a snapshot stays alive until the reader owns a reference. Found and
regression-guarded by new concurrent-reader ThreadSanitizer tests. migrate importrejects non-regular tar members (ZY-014 follow-up). On top of
refusing absolute or..-traversing paths, an imported archive that contains a
symlink, hardlink, or device/fifo/socket member is now refused before any
extraction or privileged copy. Only regular files and directories are accepted.
This closes the symlink-extraction and planted-link half of the import attack
surface that was noted when ZY-014 was first fixed.
Verify your download
curl -LO https://github.com/iskandarputra/zyshell/releases/download/v1.0.40/zy_1.0.40_amd64.deb
curl -LO https://github.com/iskandarputra/zyshell/releases/download/v1.0.40/SHA256SUMS
curl -LO https://github.com/iskandarputra/zyshell/releases/download/v1.0.40/SHA256SUMS.minisig
curl -LO https://github.com/iskandarputra/zyshell/releases/download/v1.0.40/zy-release.pub
minisign -Vm SHA256SUMS -p zy-release.pub # confirms maintainer signature
sha256sum -c SHA256SUMS --ignore-missing # confirms the .deb hash matchesFull signing policy: docs/ops/RELEASE_SIGNING.md
VirusTotal scan
This release was uploaded to VirusTotal at publish time. Click through to see the live verdict from 65+ AV engines:
(Scans run within ~5 minutes of upload. If the page shows "Item not found", wait a moment and refresh.)
Try without installing
docker run --rm -it ghcr.io/iskandarputra/zyshell:1.0.40Install (Debian / Ubuntu)
The recommended path is the zy APT repository which handles upgrades automatically:
curl -1sLf 'https://dl.cloudsmith.io/public/iskandarputra/zyshell/setup.deb.sh' | sudo -E bash
sudo apt install zyOr direct .deb install:
sudo dpkg -i zy_1.0.40_amd64.deb
sudo apt-get install -fzy v1.0.39
Official binary release for zy 1.0.39.
What's changed
Reliability & correctness
- Resolved 23 defects found in a full source-code review (ZY-001–021): memory-safety overflows in the parser and
stor create, a data race in thefffgrep engine, git prompt modules not rendering withasync = true, the two-argumentslice <start> <end>form, a JSON out-of-bounds read,with-envexit-status propagation, a safercp, valid YAML string escaping, and more.
Security
- Hardened
sys migrate importagainst shell-injection via crafted archive or path names. - Crash reports (
~/.zy/crash.log, owner-only) now capture recent log context for faster diagnosis — no additional data is exposed.
Packaging
- The Debian package now declares its complete runtime dependencies (derived from the binary with
dpkg-shlibdeps), so a cleandpkg -iorapt installpulls every library zy needs.
Under the hood
- Central structured logging with crash-context capture, a leaner documentation tree, and new maintainer release tooling.
Verify your download
curl -LO https://github.com/iskandarputra/zyshell/releases/download/v1.0.39/zy_1.0.39_amd64.deb
curl -LO https://github.com/iskandarputra/zyshell/releases/download/v1.0.39/SHA256SUMS
curl -LO https://github.com/iskandarputra/zyshell/releases/download/v1.0.39/SHA256SUMS.minisig
curl -LO https://github.com/iskandarputra/zyshell/releases/download/v1.0.39/zy-release.pub
minisign -Vm SHA256SUMS -p zy-release.pub # confirms maintainer signature
sha256sum -c SHA256SUMS --ignore-missing # confirms the .deb hash matchesFull signing policy: docs/ops/RELEASE_SIGNING.md
VirusTotal scan
This release was uploaded to VirusTotal at publish time. Click through to see the live verdict from 65+ AV engines:
(Scans run within ~5 minutes of upload. If the page shows "Item not found", wait a moment and refresh.)
Try without installing
docker run --rm -it ghcr.io/iskandarputra/zyshell:1.0.39Install (Debian / Ubuntu)
The recommended path is the zy APT repository which handles upgrades automatically:
curl -1sLf 'https://dl.cloudsmith.io/public/iskandarputra/zyshell/setup.deb.sh' | sudo -E bash
sudo apt install zyOr direct .deb install:
sudo dpkg -i zy_1.0.39_amd64.deb
sudo apt-get install -fzy v1.0.38
Official binary release for zy 1.0.38.
Verify your download
curl -LO https://github.com/iskandarputra/zyshell/releases/download/v1.0.38/zy_1.0.38_amd64.deb
curl -LO https://github.com/iskandarputra/zyshell/releases/download/v1.0.38/SHA256SUMS
curl -LO https://github.com/iskandarputra/zyshell/releases/download/v1.0.38/SHA256SUMS.minisig
curl -LO https://github.com/iskandarputra/zyshell/releases/download/v1.0.38/zy-release.pub
minisign -Vm SHA256SUMS -p zy-release.pub # confirms maintainer signature
sha256sum -c SHA256SUMS --ignore-missing # confirms the .deb hash matchesFull signing policy: docs/RELEASE_SIGNING.md
VirusTotal scan
This release was uploaded to VirusTotal at publish time. Click through to see the live verdict from 65+ AV engines:
(Scans run within ~5 minutes of upload. If the page shows "Item not found", wait a moment and refresh.)
Try without installing
docker run --rm -it ghcr.io/iskandarputra/zyshell:1.0.38Install (Debian / Ubuntu)
The recommended path is the zy APT repository which handles upgrades automatically:
curl -1sLf 'https://dl.cloudsmith.io/public/iskandarputra/zyshell/setup.deb.sh' | sudo -E bash
sudo apt install zyOr direct .deb install:
sudo dpkg -i zy_1.0.38_amd64.deb
sudo apt-get install -fzy v1.0.37
Initial public release. Signed manifest (key D108A9B9CA11369C), Cloudsmith APT repo, VirusTotal scanned, GHCR container image.