Skip to content

wip: feature branch for summarization, hooks, etc. - #2

Closed
sidkmenon wants to merge 43 commits into
mainfrom
wire-regions
Closed

sidkmenon wants to merge 43 commits into
mainfrom
wire-regions

Conversation

@sidkmenon

Copy link
Copy Markdown
Contributor

the config setup is broken - there's technically a command diffr config to add your api key but it doens't work that well.

instead you'll have to run diffr config set to actually set the API key for Gemini to create the pseudocode. sorry!!

sidkmenon and others added 30 commits September 11, 2026 01:00
Define the next NDJSON wire format as serde types in src/protocol.rs:
one header, one file record per changed file, one footer. Each file
carries both sources with per-side region trees paired across sides by
id, replacing hunks, aligned rows, and separate fold lists. Sides are a
presence-encoded Pairing; errors share one shape; syntax spans carry
tree-sitter capture names. Not wired to the CLI yet.

Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WzHbAqhxaLKNQbYyqfCyTp
Agent-Session: 0025c2a5-4c51-4e7e-be92-b0659a205823
Stats.structural is a Result so the structural-or-fallback rule cannot
be violated; Outcome converts to and from Result; Region carries a
SourceRange; Error is Problem to stay clear of std; Pairing gains map
and as_ref; one is_default helper; Eq everywhere it holds; plain pub
fields in a crate-private module.

Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WzHbAqhxaLKNQbYyqfCyTp
Agent-Session: 0025c2a5-4c51-4e7e-be92-b0659a205823
Project DiffResult onto protocol::Event: aligned rows become leaves that
tile each side, folds nest by containment with leaves split at their
edges (mirrored across paired leaves), long unchanged runs collapse to
the context width, and ids pair regions across sides. The manifest
carries git's delta sides (oid, mode), status, category and language.
Per-file failures are coded problems; a hook failure aborts the run.
--syntax adds tree-sitter capture spans from the highlights query. The
previous stream stays as --format ndjson-v1 while the frontends move.

Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WzHbAqhxaLKNQbYyqfCyTp
Agent-Session: 0025c2a5-4c51-4e7e-be92-b0659a205823
Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WzHbAqhxaLKNQbYyqfCyTp
Agent-Session: 0025c2a5-4c51-4e7e-be92-b0659a205823
The TUI validates diffr's v2 protocol, zips each side's leaves on their
ids into split and unified rows, colours tokens from --syntax capture
names, and treats every region the same way: folds and context gaps
collapse from one id set seeded by diffr's visibility, with multi-line
labels hanging under a collapsed header. Files hidden by default show a
Load diff placeholder. A settings screen over diffr config schema/show/
set opens from diffr config.

Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WzHbAqhxaLKNQbYyqfCyTp
Agent-Session: 0025c2a5-4c51-4e7e-be92-b0659a205823
Configuration is one schema-bearing struct resolved by figment from the
bundled defaults, the global file, the repository's diffr.toml, DIFFR_*
variables and --set overrides. `diffr config schema|show|set` are the
commands frontends drive; `diffr config [query]` opens the terminal
settings screen. Doc comments become schema descriptions.

Mutations run on the protocol types after projection, never on the diff
internals: file mutations on the manifest, fold mutations on each file's
region trees. Generated and test files start hidden, classified by git
attributes then built-in path rules; deleted bodies start collapsed with
a line count; new bodies of at least folds.min_lines lines are summarized
as pseudocode by a built-in Gemini client over reqwest, silently off
without a key, retried on transient failures and aborting the run
otherwise. The JSON-RPC hook is now a fold mutation on the same stack,
addresses folds by region id, and jsonrpsee is gone. JavaScript and
TypeScript test callbacks carry the `test` fold tag.

Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WzHbAqhxaLKNQbYyqfCyTp
Agent-Session: 0025c2a5-4c51-4e7e-be92-b0659a205823
The launcher's fixtures live under test/, which the new category rules
hide by default. The test exercises the viewer lifecycle, so pass
--set folds.collapse_tests=false.

Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WzHbAqhxaLKNQbYyqfCyTp
Agent-Session: 0025c2a5-4c51-4e7e-be92-b0659a205823
…ed folds

Context gaps now follow difftastic's hunks, so the syntax context it
already selects (the enclosing function header) stays visible above a
deep change, and only unchanged stretches of three or more lines that no
hunk shows collapse. Folds that lie entirely inside a gap are dropped
from the region tree instead of splitting it, and slivers a fold edge
cuts off a gap stay open.

A last fold mutation merges adjacent gaps where both sides agree and
wraps runs of collapsed sibling folds (deleted or summarized bodies) in
one collapsed group fold whose label counts them.

Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WzHbAqhxaLKNQbYyqfCyTp
Agent-Session: 0025c2a5-4c51-4e7e-be92-b0659a205823
A new fold mutation, on by default at five lines, splits every lhs-only
leaf into an open first line, a collapsed middle labelled with its line
count and tagged removed, and an open last line, so a big red block reads
as red without filling the screen. Leaves under an already collapsed fold
are left alone. Configured by folds.collapse_removed_lines; 0 disables.

Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WzHbAqhxaLKNQbYyqfCyTp
Agent-Session: 0025c2a5-4c51-4e7e-be92-b0659a205823
An empty key resolves to no summarizer, so showing <redacted> for it
misled the user into thinking a key was set.

Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WzHbAqhxaLKNQbYyqfCyTp
Agent-Session: 0025c2a5-4c51-4e7e-be92-b0659a205823
Enter opens a key/value view with the description and default: booleans
and enums pick from a list, numbers and strings use a text field that is
masked for credential-looking keys. Enter saves through diffr config set
and returns to the refreshed list; Esc returns. Both views carry footer
hints, and empty values read as <unset>.

Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WzHbAqhxaLKNQbYyqfCyTp
Agent-Session: 0025c2a5-4c51-4e7e-be92-b0659a205823
Lines inside a novel leaf that carry no change span, such as blank lines
in a new block, now get the light line tint; the darker word tint stays
on the spans. Tests assert the two backgrounds are distinct in both
themes.

Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WzHbAqhxaLKNQbYyqfCyTp
Agent-Session: 0025c2a5-4c51-4e7e-be92-b0659a205823
A large rewrite of src/hook.rs exceeded difftastic's 3M graph limit, so
the streaming path fell back to a line diff with no folds, collapse
rules, or summaries. A [diff] table now carries byte_limit, graph_limit
and parse_error_limit, layered like every other key, with the DFT_*
variables and the command-line flags still overriding it. The graph
limit defaults to 30M, which keeps that file structural for about one
extra second in a debug build. Fallback reasons name the key to raise.

Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WzHbAqhxaLKNQbYyqfCyTp
Agent-Session: 0025c2a5-4c51-4e7e-be92-b0659a205823
Thirty million nodes across sixteen parallel files was too much memory.
The limit stays configurable; large rewrites fall back to a line diff by
default, and agent traces are the planned way around that.

Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WzHbAqhxaLKNQbYyqfCyTp
Agent-Session: 0025c2a5-4c51-4e7e-be92-b0659a205823
The domain tree is a tree of correlated ranges; the structural engine
and the text diff are two producers of the line alignment, and folds
are a layer over whichever one exists. When the AST match gives up
(graph limit, parse-error limit) the parsed trees still yield folds and
enclosing-header context, and the projection pairs folds on both
engines with one rule: header lines paired in the alignment, tags
equal. The parser's own fold pairing is no longer read. hook.rs goes
from no folds to 34/51 folds with 17 pairs and 5 summarizable bodies.

Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WzHbAqhxaLKNQbYyqfCyTp
Agent-Session: 0025c2a5-4c51-4e7e-be92-b0659a205823
Header lines pair when the alignment pairs them, changed or not, so a
function with an edited signature stays paired.

Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WzHbAqhxaLKNQbYyqfCyTp
Agent-Session: 0025c2a5-4c51-4e7e-be92-b0659a205823
Vendor four Helix themes (onedark and onelight as the defaults, gruvbox,
solarized_light) under tui/themes with their MPL-2.0 notice, parse the
TOML format with palette references and parent-scope fallback, and
build the painter's palette from ui.* keys with change tints mixed from
diff.plus and diff.minus. The theme comes from diffr config show at
startup: theme.path if set, else theme.name; unknown names error. The
t key toggles the two defaults.

Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WzHbAqhxaLKNQbYyqfCyTp
Agent-Session: 0025c2a5-4c51-4e7e-be92-b0659a205823
Function definitions gain a `function` fold tag next to `body`, so the
deleted-bodies rule and the summarizer act on real functions rather than
every brace block, and the summarizer takes the outermost qualifying body
only. `summarize.min_lines` (default 20) separates the summary threshold
from the collapse one. Test bodies of three or more lines start collapsed
on both sides and are never summarized. The prompt is one sentence of
pseudocode instructions; a summary longer than half the body is discarded
in code. Grouping no longer looks under a collapsed fold and labels a run
of test bodies as such.

Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WzHbAqhxaLKNQbYyqfCyTp
Agent-Session: 0025c2a5-4c51-4e7e-be92-b0659a205823
Syntax spans came only from the structural path, so a file that fell
back to a line diff rendered uncoloured. The language is guessed from the
path for fallbacks and both sides get spans when --syntax is set.

Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WzHbAqhxaLKNQbYyqfCyTp
Agent-Session: 0025c2a5-4c51-4e7e-be92-b0659a205823
A strip under the menubar names the comparison, the file count, and the
visible totals with GitHub's five-block bar. Headline counts are diffr's
stats.visible adjusted as folds, gaps, and files toggle; the i key or a
click on the totals opens a breakdown of visible, structural, and
textual counts for the comparison and the current file. stats.visible
is required on the wire.

Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WzHbAqhxaLKNQbYyqfCyTp
Agent-Session: 0025c2a5-4c51-4e7e-be92-b0659a205823
stats is textual, visible, and an optional fallback. The breakdown lists
visible and textual and notes a line-diff fallback by code; the file
header shows visible counts only.

Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WzHbAqhxaLKNQbYyqfCyTp
Agent-Session: 0025c2a5-4c51-4e7e-be92-b0659a205823
stats.visible counts the changed lines still on screen under the default
visibility, computed after the mutations run so configuration and hooks
change it. The matcher's own line count leaves the wire; stats.fallback
alone says when the AST match did not run.

Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WzHbAqhxaLKNQbYyqfCyTp
Agent-Session: 0025c2a5-4c51-4e7e-be92-b0659a205823
Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WzHbAqhxaLKNQbYyqfCyTp
Agent-Session: 0025c2a5-4c51-4e7e-be92-b0659a205823
A Rust mod carrying #[cfg(test)] is tagged test (and module) so the
whole module starts collapsed as one "test module" fold, with the test
bodies still foldable inside. tests.rs, test.rs, *_test.rs and
*_tests.rs count as test files, so src/review/tests.rs is hidden by
default like the other test files.

Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WzHbAqhxaLKNQbYyqfCyTp
Agent-Session: 0025c2a5-4c51-4e7e-be92-b0659a205823
Agent-Session: 9a68def0-2052-49c3-a656-a142d3152c20
Fold line spans are nested before the tree is built: when two parser
folds cross on one line, such as a collection whose closer sits on the
line that opens the next body, the earlier fold gives that line to the
later one, and a span left with one line is dropped. A fold's wire range
is now the hull of its children, whole lines, instead of the parser's
byte range, so every child lies inside its parent and siblings never
overlap. A debug assertion and the streaming checker walk every tree.
The terminal frontend no longer hides the line after a fold whose end
sits at column zero.

Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WzHbAqhxaLKNQbYyqfCyTp
Agent-Session: 0025c2a5-4c51-4e7e-be92-b0659a205823
Agent-Session: 9a68def0-2052-49c3-a656-a142d3152c20
Agent-Session: 6b254b74-b802-4554-b16d-6723bf8b5f1f
Agent-Session: 3ad992ce-8922-470c-a21f-ad2de0e31b10
Agent-Session: bf405baa-0eb9-435d-8f3d-0846840aff6c
Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WzHbAqhxaLKNQbYyqfCyTp
Agent-Session: 0025c2a5-4c51-4e7e-be92-b0659a205823
Agent-Session: 9a68def0-2052-49c3-a656-a142d3152c20
Agent-Session: 6b254b74-b802-4554-b16d-6723bf8b5f1f
Agent-Session: 3ad992ce-8922-470c-a21f-ad2de0e31b10
Agent-Session: bf405baa-0eb9-435d-8f3d-0846840aff6c
A function fold whose header line did not align could still hold paired
leaves; the deleted-bodies rule treated it as removed and hid a rewrite.
A region now counts as one-sided only when nothing under it has a
counterpart. Removed stretches split only when their nearest enclosing
function fold (or, outside any function, the nearest fold) is one-sided,
and a "functions removed" group never wraps a rewrite.

Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WzHbAqhxaLKNQbYyqfCyTp
alignment_id says what a region is across sides: the same value on the
other side is its visual counterpart, one-to-one. fold_state_id says what
it moves with: regions sharing it open and close together and may span
sides or bundle same-side regions. They coincide for every region today;
later mutations will bundle a docstring with its function.

While assigning alignment ids, folds now also pair through their content:
a fold whose header line did not align pairs with the fold on the other
side that holds the counterpart of one of its leaves, most paired lines
first, never crossing an existing pair. A function whose signature was
reflowed onto more lines is the same function again instead of a removal
plus an addition. Across main..HEAD, unpaired function folds that still
held paired lines fell from 10 to 4; the remaining four moved into their
parent function, which is already paired. Wire version 3.

Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WzHbAqhxaLKNQbYyqfCyTp
Agent-Session: 0025c2a5-4c51-4e7e-be92-b0659a205823
The row zip pairs leaves by alignment_id; collapse state, fold headers,
nested and gap commands, and visible counts key on fold_state_id, so
regions sharing it toggle together on both sides by construction.
Accepts wire version 3; the fixture is regenerated.

Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WzHbAqhxaLKNQbYyqfCyTp
Agent-Session: 0025c2a5-4c51-4e7e-be92-b0659a205823
A file that was deleted outright starts collapsed behind a placeholder
labelled "Deleted file · hidden by default", whatever its category.
folds.collapse_deleted_files turns it off.

Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WzHbAqhxaLKNQbYyqfCyTp
Agent-Session: 0025c2a5-4c51-4e7e-be92-b0659a205823
sidkmenon and others added 13 commits September 12, 2026 20:48
A fold whose hull runs to the last line ends at (line count, 0), one
past the last line. The reader required that line to exist even though
an end at column 0 never reads it, so any file whose last function ran
to end of file stopped the whole view.

Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WzHbAqhxaLKNQbYyqfCyTp
Agent-Session: 0025c2a5-4c51-4e7e-be92-b0659a205823
File headers, the summary strip and the breakdown read stats.visible
and stats.textual from the wire and nothing else. Folding changes what
is on screen, never the numbers, so the local recount on every toggle
is gone.

Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WzHbAqhxaLKNQbYyqfCyTp
Agent-Session: 0025c2a5-4c51-4e7e-be92-b0659a205823
A region whose alignment_id counterpart the row zip has already passed
is a move. Both copies now paint in the theme's moved tint with no +/-,
under a label naming where the other copy starts (moved from / moved
to line N); edits inside a copy keep the word tint. Clicking the label
or pressing gm jumps to the other copy. g still goes home at once and
remembers the view, so gm jumps from where the reader was.

Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WzHbAqhxaLKNQbYyqfCyTp
Agent-Session: 0025c2a5-4c51-4e7e-be92-b0659a205823
Collapse state already keys on fold_state_id; recursive folding now
unions the nested ids of every region sharing it, so a docstring
bundled with its function opens and closes with it. A test covers a
docstring and body sharing one id: one toggle hides both, the empty
docstring label renders as a bare ellipsis, the signature stays.

Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WzHbAqhxaLKNQbYyqfCyTp
Agent-Session: 0025c2a5-4c51-4e7e-be92-b0659a205823
A fold whose closer sat inside a long unchanged stretch that continued in
its parent cut the gap into two collapsed leaves at different depths, so
Review drew two unchanged bands back to back. A fold that ends inside a
gap now extends to the gap's end, and a fold whose header would be hidden
inside one is dropped, but only when the cut would leave two pieces long
enough to collapse. The streaming checker asserts no two collapsed
unchanged leaves are adjacent.

Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WzHbAqhxaLKNQbYyqfCyTp
Agent-Session: 0025c2a5-4c51-4e7e-be92-b0659a205823
Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WzHbAqhxaLKNQbYyqfCyTp
Agent-Session: 0025c2a5-4c51-4e7e-be92-b0659a205823
A function's docstring is the run of comment lines just above it, with
only its signature and blank lines between, or in Python the string that
opens its body. A new fold mutation splits those lines into leaves tagged
docstring that share the function's fold_state_id, so frontends open and
close them together; a paired docstring keeps the bundle only when the
other side agrees. After the collapsing mutations run, a docstring whose
function starts collapsed collapses too, with an empty label.

The summarizer sends the docstring with each body and may get back one
sentence from it; the sentence is kept only when it really occurs in the
docstring, and then leads the pseudocode. Compression is judged on the
pseudocode alone. folds.bundle_docstrings turns it off.

Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WzHbAqhxaLKNQbYyqfCyTp
Agent-Session: 0025c2a5-4c51-4e7e-be92-b0659a205823
diffr bundles a docstring leaf with its function: it starts collapsed,
carries an empty label, and shares the function's fold_state_id. A leaf
diffr starts collapsed is now foldable in its own right, and an empty
label renders as a bare ellipsis instead of an unchanged-lines count,
which only context gaps use.

Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WzHbAqhxaLKNQbYyqfCyTp
Agent-Session: 0025c2a5-4c51-4e7e-be92-b0659a205823
A docstring leaf sharing its function's fold state now starts that
function's unit, so a run of documented test bodies becomes one group
again and the group begins at the first docstring.

Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WzHbAqhxaLKNQbYyqfCyTp
Agent-Session: 0025c2a5-4c51-4e7e-be92-b0659a205823
A line diff pairs the lines of a changed block from the top. In
src/hook.rs that paired the new fn qualifies signature with an old doc
comment sixty lines above the old signature, leaving a long filler gap
above a body that still aligned. When a block's two sides differ in
length and its last lines read more alike by identifier tokens, pair
those instead. Only line-diff fallbacks are realigned; the structural
matcher anchors its rows on matched tokens.

Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WzHbAqhxaLKNQbYyqfCyTp
Agent-Session: 0025c2a5-4c51-4e7e-be92-b0659a205823
A collapsed fold, its multi-line label and a bare docstring row now take
the same change tint Review's bands use: the added tint for a region
whose alignment_id is absent on the left (a summarized new function),
the removed tint for one absent on the right (a removed body), and the
neutral fold background only for paired regions and unchanged gaps.

Tests cover the tint for inserted, removed and paired folds, and a
docstring bundled with its function opening and closing together from
either row by mouse and by zo, zc and za.

Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WzHbAqhxaLKNQbYyqfCyTp
Agent-Session: 0025c2a5-4c51-4e7e-be92-b0659a205823
A structural diff pairs folds exactly as the syntax matcher recorded
them: parse::folds::pair_matched maps each FoldMatch::Unchanged opposite
range to the fold on the other side. A line-diff fallback pairs folds
through the line alignment, by header line and then by the lines they
hold, in the new line_folds module, which also owns the fallback's
changed-block row alignment. DiffResult carries the result as
fold_pairs, and the projection only consumes it: pairing no longer lives
in protocol/project.rs.

Written with Claude Code.

Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WzHbAqhxaLKNQbYyqfCyTp
Agent-Session: 0de57442-ad9b-4c99-960e-4974e629b71d
Agent-Session: e046556c-20f0-4bba-832b-b6784a7783c5
Agent-Session: 0025c2a5-4c51-4e7e-be92-b0659a205823
DiffResult.fold_pairs held a second copy of fold correspondence as index
pairs. It existed because the line-diff path found partners but never
wrote them onto the folds the way the syntax matcher does. line_folds::pair
now sets FoldMatch::Unchanged { opposite } on both folds of each pair, the
field goes away, and the projection reads every file's partners from the
folds through parse::folds::pair_matched.

No output change: per-file function fold pairing across main..HEAD is
identical before and after.

Written with AI assistance (Claude Code).

Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WzHbAqhxaLKNQbYyqfCyTp
Agent-Session: 8837929e-0118-489c-9648-e47c1831ee8c
Agent-Session: 0025c2a5-4c51-4e7e-be92-b0659a205823
@sidkmenon sidkmenon closed this Sep 16, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant