Add as-you-type autocompletion (inline hints) to the CLI by alexey-milovidov · Pull Request #108070 · ClickHouse/ClickHouse · GitHub
Skip to content

Add as-you-type autocompletion (inline hints) to the CLI#108070

Merged
alexey-milovidov merged 18 commits into
masterfrom
cli-autocompletion-hints
Jul 1, 2026
Merged

Add as-you-type autocompletion (inline hints) to the CLI#108070
alexey-milovidov merged 18 commits into
masterfrom
cli-autocompletion-hints

Conversation

@alexey-milovidov

@alexey-milovidov alexey-milovidov commented Jun 21, 2026

Copy link
Copy Markdown
Member

Brings the Web UI (play.html) autocompletion principle to clickhouse-client and clickhouse-local: the best matching suggestion is shown as inline "ghost" text as you type, instead of only when pressing Tab.

How

This reuses replxx's hint system, which ClickHouse had never wired up (no hint callback was registered). replxx already implements the desired UX natively:

  • a single match renders inline after the cursor (gray ghost text);
  • several matches render the selected one inline plus a navigable list of rows below.

Hints are shown when the cursor is at the end of the input (including the last line of a multi-line query). Mid-line hints were explored but dropped — see the note below.

Navigation & accept

  • Up/Down (and Ctrl-Up/Ctrl-Down) navigate the hint list. Down steps into and advances it; Up moves back. Up only navigates the hints once one is selected — before that it keeps recalling command history, so the hints never shadow it. This works on the last line of a multi-line query too.
  • Tab is the only key that opens the old-style completion list (multi-candidate list / common-prefix completion); it also accepts the chosen hint.
  • Right accepts the chosen hint — the single one shown, or the one selected by navigating — and never opens the completion list. Enter accepts a hint only once one has been explicitly selected by navigating; a single ghost shown while typing is not auto-accepted, so in the normal "type a query and press Enter" flow Enter still runs the query. When no hint is chosen, Right just moves the cursor and Enter runs the query / inserts a newline as usual.
  • Esc: intentionally not bound to dismiss the hints. The bundled replxx reads a lone Esc by blocking for the next byte (there is no escape-key timeout on POSIX), so a standalone Esc cannot be intercepted reliably; the Up-recalls-history behavior covers getting back to history instead.

Prioritization

Suggestions are ordered like in the Web UI:

  1. identifiers used in queries run earlier this session ("previously used");
  2. identifiers already present in the current query line (aliases, column names);
  3. the rest, in the existing sorted order.

The Tab completion callback and the hint callback are routed through a single shared Suggest::getMatchingWords, so they always return the same words in the same order — this is required because replxx accepts a hint by indexing the completion list with the hint selection index. Identifiers are extracted with the SQL lexer (Parsers/Lexer.h) so that string literals, numbers, and comments are not mistaken for identifiers.

Option

Controlled by a new --hints option (default on). It requires --highlight (hints need color), so --highlight 0 also disables hints; --hints 0 disables them while keeping highlighting.

Note: mid-line hints

Showing hints in the middle of a line / multi-line query was prototyped (it required patching the vendored replxx), but dropped: the terminal rendering tradeoffs were not worth it. No replxx change is included — the submodule is unchanged.

Testing

  • Added tests/queries/0_stateless/04402_clickhouse_client_autocomplete_inline_hints (PTY-based, modeled on 01676_clickhouse_client_autocomplete): ghost text appears as you type; Tab/Right/Enter accept; Down steps into the list; navigation works on the last line of a multi-line query; Right does not pop the completion list when nothing is chosen; --hints 0 shows no ghost; and an in-query identifier is hinted before alphabetically-earlier matches.
  • Verified manually: session-recency prioritization, Up recalls history before stepping into the list, a fully-typed word does not block Enter, and Tab completion is unaffected with --highlight 0.

Changelog category (leave one):

  • Improvement

Changelog entry (a user-readable short description of the changes that goes into CHANGELOG.md):

clickhouse-client and clickhouse-local now show as-you-type autocompletion hints (inline "ghost" text) for the best matching suggestion when the cursor is at the end of the input. The most relevant suggestions (recently used and identifiers already present in the query) are ranked first. Navigate with Up/Down (or Ctrl-Up/Ctrl-Down); accept a single or selected hint with Tab or Right, and accept a selected hint with Enter; Tab also opens the classic completion list. Controlled by the new --hints option (on by default; requires --highlight).

Documentation entry for user-facing changes

  • Documentation is written (mandatory for new features)

Version info

  • Merged into: 26.7.1.332

Bring the Web UI (play.html) autocompletion principle to clickhouse-client and
clickhouse-local: show the best matching suggestion as inline "ghost" text as
you type, instead of only on Tab.

This reuses replxx's hint system, which ClickHouse never wired up. A single
match renders inline after the cursor (gray); several matches render the
selected one inline plus a navigable list (Ctrl-Up/Ctrl-Down). Tab accepts the
selected hint, and Right at the end of the line accepts it too.

Suggestions are prioritized like in the Web UI: identifiers used in queries run
earlier this session first, then identifiers already present in the current
query line, then the rest in the existing sorted order. The completion (Tab)
and hint callbacks are routed through one shared `Suggest::getMatchingWords` so
they always return the same words in the same order — required because replxx
accepts a hint by indexing the completion list with the hint selection.

Controlled by a new `--hints` option (default on); it requires `--highlight`
because hints need color. Identifiers are extracted with the SQL lexer so that
string literals, numbers, and comments are not mistaken for identifiers.

Adds a PTY test covering ghost text, Tab/Right accept, --hints 0, and the
in-query prioritization.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@clickhouse-gh

clickhouse-gh Bot commented Jun 21, 2026

Copy link
Copy Markdown
Contributor

@clickhouse-gh clickhouse-gh Bot added the pr-improvement Pull request with some product improvements label Jun 21, 2026
Comment thread src/Client/LineReader.cpp Outdated
Comment thread src/Client/ReplxxLineReader.cpp Outdated
Bind Up/Down (in addition to Ctrl-Up/Ctrl-Down) to navigate the as-you-type
hint list: Down steps into and advances the list, Up moves back through it,
and Right/Tab accept the selected hint. Up only navigates hints once the user
has stepped into the list with Down (tracked by `hint_active`), so before that
Up keeps recalling command history and the hints never shadow it. Hint
navigation is limited to single-line input with the cursor at the end, so
multi-line line movement is preserved.

Esc is intentionally not bound to dismiss the hints: the bundled replxx reads a
lone Esc by blocking for the next byte (there is no escape-key timeout on
POSIX), so a standalone Esc cannot be intercepted reliably. The Up-recalls-
history behavior covers the need to get back to history instead.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Comment thread src/Client/ReplxxLineReader.cpp Outdated
alexey-milovidov and others added 2 commits June 21, 2026 21:24
…gments

Previously the inline hints were shown only when the cursor was at the very end
of the input. Now they also appear when the cursor is right after an
identifier-like fragment in the middle of the query (e.g. editing a column name
before `FROM ...`), mirroring the Web UI trigger.

This bumps the `replxx` submodule to a fork commit that relaxes the
end-of-buffer gate: mid-line the suggestions are shown as the list below the
cursor (the inline "ghost" suffix is still only used at the end of the line,
where it can be appended after the whole input without disturbing the text
after the cursor). On the ClickHouse side, hint navigation with Up/Down now
applies to single-line input regardless of the cursor position, so the mid-line
suggestions are navigable too.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Fix Up/Down not working for hints shown in the middle of the line:

- Bump replxx so the mid-line hint list highlights the selected entry (the
  inline ghost is unavailable mid-line, and moving the selection into it made
  navigation invisible and hid the selected item).
- In the middle of the line, Up navigates the hints directly instead of
  recalling history (which would clobber the in-progress edit); at the end of
  the line Up still recalls history until the user steps into the list.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@clickhouse-gh clickhouse-gh Bot added the submodule changed At least one submodule changed in this PR. label Jun 21, 2026
Comment thread src/Client/ReplxxLineReader.cpp Outdated
alexey-milovidov and others added 3 commits June 21, 2026 21:46
… line

Two fixes for the as-you-type hints in multi-line queries:

- Up/Down now navigate the hints whenever the hint list is shown, including on
  any line of a multi-line query (previously navigation was restricted to
  single-line input, so Up/Down kept moving between lines and the hints could
  not be navigated).
- The hint list is shown right below the cursor's line (temporarily covering
  the lines below), instead of below the whole query, so it stays next to what
  is being completed. This bumps the `replxx` submodule.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Revert the mid-line / multi-line hint work (the `replxx` submodule changes and
the related navigation tweaks), going back to showing the as-you-type hints
only when the cursor is at the end of the input. The submodule returns to its
original upstream commit, so no `replxx` patch is needed.

End-of-line behavior is unchanged: ghost text as you type, Up/Down (and
Ctrl-Up/Ctrl-Down) navigation, Tab/Right to accept, the `--hints` option, and
the priority ordering all remain.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The as-you-type hints are shown at the end of the input, which for a multi-line
query is the last line. Navigation was restricted to single-line input, so
Up/Down moved between lines there instead of choosing a hint. Drop that
restriction: since hints are only shown at the end of the buffer, Up/Down keep
their normal line/history movement everywhere else and only navigate the hints
where they are actually shown.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Comment thread src/Client/ReplxxLineReader.cpp Outdated
- Right now accepts the chosen hint (the single one shown, or the one selected
  by navigating) and never falls back to the old-style completion list — only
  Tab does that. When no hint is chosen, Right just moves the cursor.
- Enter also accepts a chosen hint (like Tab and Right) instead of running the
  query / inserting a newline; when no hint is chosen it runs the query as
  before.

To distinguish "a hint is chosen" from "several matches with nothing selected",
the selected hint index and count are tracked in sync with replxx's internal
selection. `hints_visible` now requires a hint with a non-empty suffix, so a
fully-typed word (which matches itself with an empty suffix) does not capture
Right/Enter.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Comment thread src/Client/ReplxxLineReader.cpp Outdated
Comment thread src/Client/ClientBase.cpp Outdated
@alexey-milovidov alexey-milovidov removed the submodule changed At least one submodule changed in this PR. label Jun 21, 2026
CI showed an interactive expect test (02049_clickhouse_local_merge_tree) timing
out: the as-you-type hints interfered with tests that drive the client in a PTY.
Two fixes, keeping hints on by default:

- `--disable_suggestion` now also disables the hints (it means "no
  autocompletion"). This covers all expect tests that use the standard
  CLICKHOUSE_CLIENT_EXPECT_OPT (which already passes --disable_suggestion and
  --highlight 0) as well as those that pass it directly.
- Enter accepts a hint only when one is explicitly selected by navigating; a
  single ghost shown while typing no longer captures Enter, so "type a command
  and press Enter" keeps running the query. This was the actual cause of the
  02049 timeout.

The few interactive tests that test highlighting with --hilite/--highlight on
(03566, 03591) now pass --hints 0, since they need color but not the hints.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Comment thread src/Client/ClientBase.cpp Outdated
alexey-milovidov and others added 7 commits June 27, 2026 19:53
`Suggest::getHints` computed the matched/ranked word list via
`getMatchingWords` and only then dropped it when the last word was empty.
Because the hint callback runs on every repaint with a zero delay, an empty
hint context (e.g. the cursor right after `SELECT `) made an empty last word
match every suggestion and fold/stable-sort the whole dictionary on each
keystroke, just to discard it.

Determine that the last word is empty before doing any matching, mirroring the
existing last-word computation in `getMatchingWords`, and return early.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
`commit_action` accepts a hint with `Enter` only when one is explicitly
selected (`hint_selection >= 0`); a single inline ghost hint leaves
`hint_selection == -1`, so `Enter` runs the typed query as usual. `Tab` and
`Right` accept the single inline hint. The `--hints` help text lumped all three
keys together and overstated `Enter`. Qualify it to match the intentional
behavior.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The priority assertion compared the bare word `ReplicatedMergeTree`, which is
also part of the typed input, so `rfind` could match the echoed input before
the first rendered `ReplacingMergeTree` hint and pass even if the in-query
prioritization were removed.

Only the rendered hints are prefixed with the gray `HINT_COLOR` escape (the
echoed input is syntax-highlighted differently), so anchor the comparison on
the colored hint text. Now the test fails if `ReplicatedMergeTree` is not
ranked before the alphabetically-earlier `ReplacingMergeTree` among the
rendered hints.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The new `--hints` option was missing from the user-facing option lists.
Document it next to `--highlight` in the `clickhouse-client` reference
(`docs/en/interfaces/client.md`) and the `clickhouse-local` reference
(`docs/en/operations/utilities/clickhouse-local.md`), including the default,
the navigation/accept keys, and the `--highlight` / `--disable_suggestion`
dependencies.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The CLI as-you-type hints are accepted by routing through `replxx`'s
`COMPLETE_LINE`, which indexes the *completion* list with the hint
selection. Two paths (raised in review on `src/Client/ReplxxLineReader.cpp`)
could make the accepted word differ from the displayed one:

1. The hint and completion callbacks recomputed the matches
   independently, so the background `Suggest::load` thread could insert a
   word that sorts before the displayed hint between display and
   acceptance, shifting the indexed completion. The matches are now
   computed once in the hint callback and cached (`hint_completions`),
   and the completion callback reuses that exact snapshot when the
   context matches, so the accepted word is always the one shown. The
   now-unused `Suggest::getHints` is removed.

2. `replxx` drops its internal hint selection on every line-regenerating
   action (any cursor movement) but only re-invokes the hint callback on
   text changes, so a plain cursor move (e.g. Left then Right back to the
   end of the line) left our `hint_selection` mirror stale — Right/Enter
   then treated a no-longer-shown hint as chosen and got swallowed by
   completion instead of running the query. A `replxx` modify callback
   (which runs on every dispatched action) now resets the mirror to track
   `replxx`; the hint-navigation keys re-apply the selection after invoking.

Adds a PTY regression test for the cursor-movement path: after Down,
Left, Right back to the end of the line, Enter must commit the query (a
fresh prompt appears) rather than pop the completion list.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@alexey-milovidov

Copy link
Copy Markdown
Member Author

@groeneai, please investigate this flaky integration test and provide a fix in a separate PR (or link an existing one):

test_restore_db_replica/test.py::test_restore_db_replica_with_diffrent_table_metadata[restore node2-node1]

Report: https://s3.amazonaws.com/clickhouse-test-reports/json.html?PR=108070&sha=dcc81e774145856abad05c8a797469cd612f167e&name_0=PR&name_1=Integration%20tests%20%28amd_llvm_coverage%2C%205%2F8%29

It is the only failure on this PR, and it is unrelated to the change here (CLI autocompletion in ReplxxLineReader/LineReader, nothing touching DatabaseReplicated backup/restore). The assertion is a replication-timing race (SELECT count(*) returns 90 instead of 100). CIDB shows it failing sporadically across unrelated PRs over the last 30 days (#108070, #100752, #107566, #107442 — roughly once per day on scattered days), with no open tracking issue, which is consistent with an environmental flake rather than a regression.

Comment thread src/Client/LineReader.cpp
@groeneai

Copy link
Copy Markdown
Contributor

Confirmed: this is an environmental flake, unrelated to the CLI autocompletion change here.

Root cause: in test_restore_db_replica/test.py::test_restore_db_replica_with_diffrent_table_metadata, after SYSTEM RESTORE DATABASE REPLICA the assertion checks that test_table_1 has 100 rows on both replicas using a bare SELECT count(*) polled by query_with_retry (retry_count=20, sleep_time=0.5s, a fixed ~10s budget, no replica sync). The replica that did not originally hold the data must fetch all parts in the background after the restore; on slow builds the last part lands after that budget expires, so the poll returns 90 (9 of the 10 PARTITION BY n % 10 parts) and the assert fails. It is a timing race, not a logic error.

CIDB evidence (30 days): this count(*) returns 90 instead of 100 on 2 unrelated PRs (this one on Integration tests (amd_llvm_coverage, 5/8), plus one on Integration tests (arm_binary, distributed plan, 2/4)), different authors and different build flavors, and 0 times on master. The failing report for this PR is the one you linked: https://s3.amazonaws.com/clickhouse-test-reports/json.html?PR=108070&sha=dcc81e774145856abad05c8a797469cd612f167e&name_0=PR&name_1=Integration%20tests%20%28amd_llvm_coverage%2C%205%2F8%29

Fix (separate PR): #108860. It replaces the fixed-budget count poll with the existing check_contains_table helper, which issues SYSTEM SYNC REPLICA first and then asserts the count, so it waits for the background fetch to finish instead of timing out. That is the same barrier every other count check in this test file already uses; the assertion (100 rows on both replicas) is unchanged. Verified locally: passes 6/6 across both parametrizations.

alexey-milovidov and others added 2 commits June 30, 2026 14:39
The `stale_selection_enter_commits` subtest of
`04402_clickhouse_client_autocomplete_inline_hints` was flaky on slow
(sanitizer) builds: after the final `Enter` commits the query, the fresh
`:)` prompt only appears once the query has executed server-side, which can
take longer than the fixed 0.4s inter-keystroke drain. The test then
collected the output before the prompt arrived and reported `FAIL`.

Keep reading until the check predicate passes (bounded by `FINAL_WAIT_SECONDS`)
instead of giving up after a single drain window. The wait is skipped entirely
when the check already passed, so fast cases are unaffected, and a genuine
logic regression (the prompt never appearing) still fails - it just waits the
bound first - so this does not mask real bugs.

CI report: https://s3.amazonaws.com/clickhouse-test-reports/json.html?PR=108070&sha=09bef5015ac0b14eb241ac45cd259271e364c71f&name_0=PR

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Addresses the review finding on `LineReader.cpp`: the PR documents that
identifiers already present in the current query (aliases, column names) and
identifiers used earlier this session participate in the hints, but
`getMatchingWords` only used `priority_words`/`recently_used` to re-rank
entries that were already in the loaded suggestion dictionary. A query-only
alias such as `SELECT veryUniqueAlias AS x, veryU` was therefore never offered
unless it also existed in `system.completions`.

Make those identifiers eligible candidates: after collecting the dictionary
matches, also add the `priority_words` and recently-used words that match the
typed prefix (same case-folding as the dictionary lookup), skipping the token
currently being typed and deduplicating against the dictionary matches and each
other. The existing tier ranking then applies, so they appear with the right
priority for both `Tab` completion and the inline hints.

Adds a PTY regression test (`in_query_alias_candidate`) where the expected hint
comes from a query-local alias that is not in the suggestion dictionary.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@alexey-milovidov

Copy link
Copy Markdown
Member Author

Pushed two follow-up commits (09bef5015ac..24bee960700):

  1. Fixed the flaky stale_selection_enter_commits subtest of 04402_clickhouse_client_autocomplete_inline_hints, which was the dominant CI failure (it failed across amd_tsan/amd_msan/amd_asan_ubsan/arm_asan_ubsan runs). Root cause: after the final Enter commits the query, the fresh :) prompt only appears once the query has executed server-side, which on slow (sanitizer) builds can outlast the fixed 0.4s inter-keystroke drain — so the harness collected the output before the prompt arrived. The harness now keeps reading until the check passes (bounded by FINAL_WAIT_SECONDS); the wait is skipped when the check already passed, so fast cases are unaffected, and a genuine logic regression (prompt never appearing) still fails after the bound, so it cannot mask real bugs. Verified locally: 15/15 consecutive green runs.

  2. Addressed the AI-review "Request changes" finding on LineReader.cpp: current-query identifiers (aliases/column names) and previously-used identifiers are now eligible hint/completion candidates, not only re-ranking signals — getMatchingWords merges the priority_words/recent words that match the typed prefix into the candidate set (same case folding/dedup, excluding the token being typed). Added a PTY regression test in_query_alias_candidate where the hint comes from a query-local alias absent from system.completions. Built and ran the full test locally — all 11 subtests pass and match the reference.

The remaining red on the previous run, AST fuzzer (amd_debug, targeted)Logical error: Not-ready Set is passed as the second argument for function 'A (STID: 0250-41a5) — is the known, tracked flake #107619 (testing, fuzz; siblings #104130/#102803/#102981). Its stack is pure query-execution (PipelineExecutor/PullingAsyncPipelineExecutor) with no Client/LineReader frames, so it cannot be caused by this client-side line-editing change.

@alexey-milovidov

Copy link
Copy Markdown
Member Author

CI completed for 24bee960 and the AI Review has flipped to ✅ Approve (the alias-candidate finding is now fixed and resolved); there are no unresolved review threads.

All remaining red checks are known, widespread flakes that are server-side and cannot be caused by this client-only (interactive replxx hints) change. Verified against master/CIDB history:

  • Stateless (amd_debug, parallel)02875_parallel_replicas_cluster_all_replicas: Unexpected packet from server ... (expected TablesStatusResponse, got ProfileInfo) (UNEXPECTED_PACKET_FROM_SERVER). The CI reruns already confirmed it: "Runs: 181, Failed: 0 — All reruns passed. The failure is not reproducible." This is the distributed TablesStatus desync flake (hit 4 distinct PRs on 06-30).
  • AST fuzzer (arm_asan_ubsan)Block structure mismatch in A stream (STID 0993-27f0): fuzzer-found server-side flake hitting 8–31 distinct PRs/day over the last 30 days.
  • Stress test (amd_debug)Inconsistent AST formatting (STID 1941-26fa): fuzzer-found server-side flake hitting 3–7 distinct PRs/day.
  • Upgrade check (amd_release)Code: 395 FUNCTION_THROW_IF_VALUE_IS_NON_ZERO in a background MutatePlainMergeTreeTask (a stateless test leaves a throwIf mutation in clickhouse-server.log): hits 14–23 distinct PRs/day.

I re-triggered the failed jobs (the fuzzer/upgrade-check runs usually come back green on a clean rerun).

The CH Inc sync is still in progress; its only failures so far are Cloud-only lanes (db disk / distributed cache / meta in keeper / SharedCatalog), which an interactive-CLI change cannot exercise.

No re-merge with master: the merge-base is ~2 days old (< 1 week), the branch is MERGEABLE with no conflicts, and the red checks fail on master too (so they are not fixable by merging).

@clickhouse-gh

clickhouse-gh Bot commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

LLVM Coverage Report

Metric Baseline Current Δ
Lines 85.50% 85.40% -0.10%
Functions 92.70% 92.70% +0.00%
Branches 77.80% 77.70% -0.10%

Changed lines: Changed C/C++ lines covered: 186/216 (86.11%) · Uncovered code

Full report · Diff report

@alexey-milovidov

Copy link
Copy Markdown
Member Author

@alexey-milovidov alexey-milovidov left a comment

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good.

@alexey-milovidov alexey-milovidov self-assigned this Jul 1, 2026
@alexey-milovidov alexey-milovidov merged commit 3445b65 into master Jul 1, 2026
508 of 514 checks passed
@alexey-milovidov alexey-milovidov deleted the cli-autocompletion-hints branch July 1, 2026 07:40
@robot-ch-test-poll robot-ch-test-poll added the pr-synced-to-cloud The PR is synced to the cloud repo label Jul 1, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

pr-improvement Pull request with some product improvements pr-synced-to-cloud The PR is synced to the cloud repo

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants