README.md

  1**NOTE:** This is a personal fork of
  2[Crush](https://github.com/charmbracelet/crush) fork where I'm
  3experimenting with prompts and other things. You'll also find my patches
  4merged here before they're merged into upstream. Additionally, I've yote
  5the CLA; devs shouldn't assign their copyrights to maintainers and hand
  6them the rug they'll yank from under you later.
  7
  8Install my fork either…
  9- Using [bin](https://github.com/marcosnils/bin) (highly recommended
 10  because it's one tool to manage and update myriad CLI tools
 11  distributed as statically-linked binaries, like _nasin pali_)
 12  ```bash
 13  bin install goinstall://git.secluded.site/crush@latest
 14  ```
 15- Or using the [go toolchain](https://go.dev/dl) (requires tracking updates
 16  manually)
 17  ```bash
 18  go install git.secluded.site/crush@latest
 19  ```
 20
 21I pull upstream changes into the `upstream` branch here, break my
 22branches off that, then merge them into my `dev` branch, which gets
 23rebased on `upstream` fairly often. `dev` history will get rewritten
 24frequently.
 25
 26Contributions are welcome (see following section) and I'll try to
 27maintain anything I accept here.
 28
 29## Contributions
 30
 31Patch requests are in [amolith/llm-projects] on [pr.pico.sh]. You don't
 32need a new account to contribute, you don't need to fork this repo, you
 33don't need to fiddle with `git send-email`, you don't need to faff with
 34your email client to get `git request-pull` working...
 35
 36You just need:
 37
 38- Git
 39- SSH
 40- An SSH key
 41
 42```sh
 43# Clone this repo, make your changes, and commit them
 44# Create a new patch request with
 45git format-patch origin/main --stdout | ssh pr.pico.sh pr create amolith/llm-projects
 46# After potential feedback, submit a revision to an existing patch request with
 47git format-patch origin/main --stdout | ssh pr.pico.sh pr add {prID}
 48# List patch requests
 49ssh pr.pico.sh pr ls amolith/llm-projects
 50```
 51
 52See "How do Patch Requests work?" on [pr.pico.sh]'s home page for a more
 53complete example workflow.
 54
 55[amolith/llm-projects]: https://pr.pico.sh/r/amolith/llm-projects
 56[pr.pico.sh]: https://pr.pico.sh
 57
 58---
 59
 60_Everything from here on is from the original Crush README._
 61
 62---
 63
 64# Crush
 65
 66<p align="center">
 67    <a href="https://stuff.charm.sh/crush/charm-crush.png"><img width="450" alt="Charm Crush Logo" src="https://github.com/user-attachments/assets/adc1a6f4-b284-4603-836c-59038caa2e8b" /></a><br />
 68    <a href="https://github.com/charmbracelet/crush/releases"><img src="https://img.shields.io/github/release/charmbracelet/crush" alt="Latest Release"></a>
 69    <a href="https://github.com/charmbracelet/crush/actions"><img src="https://github.com/charmbracelet/crush/actions/workflows/build.yml/badge.svg" alt="Build Status"></a>
 70</p>
 71
 72<p align="center">Your new coding bestie, now available in your favourite terminal.<br />Your tools, your code, and your workflows, wired into your LLM of choice.</p>
 73<p align="center">你的新编程伙伴,现在就在你最爱的终端中。<br />你的工具、代码和工作流,都与您选择的 LLM 模型紧密相连。</p>
 74
 75<p align="center"><img width="800" alt="Crush Demo" src="https://github.com/user-attachments/assets/58280caf-851b-470a-b6f7-d5c4ea8a1968" /></p>
 76
 77## Features
 78
 79- **Multi-Model:** choose from a wide range of LLMs or add your own via OpenAI- or Anthropic-compatible APIs
 80- **Flexible:** switch LLMs mid-session while preserving context
 81- **Session-Based:** maintain multiple work sessions and contexts per project
 82- **LSP-Enhanced:** Crush uses LSPs for additional context, just like you do
 83- **Extensible:** add capabilities via MCPs (`http`, `stdio`, and `sse`)
 84- **Works Everywhere:** first-class support in every terminal on macOS, Linux, Windows (PowerShell and WSL), FreeBSD, OpenBSD, and NetBSD
 85
 86## Installation
 87
 88Use a package manager:
 89
 90```bash
 91# Homebrew
 92brew install charmbracelet/tap/crush
 93
 94# NPM
 95npm install -g @charmland/crush
 96
 97# Arch Linux (btw)
 98yay -S crush-bin
 99
100# Nix
101nix run github:numtide/nix-ai-tools#crush
102```
103
104Windows users:
105
106```bash
107# Winget
108winget install charmbracelet.crush
109
110# Scoop
111scoop bucket add charm https://github.com/charmbracelet/scoop-bucket.git
112scoop install crush
113```
114
115<details>
116<summary><strong>Nix (NUR)</strong></summary>
117
118Crush is available via [NUR](https://github.com/nix-community/NUR) in `nur.repos.charmbracelet.crush`.
119
120You can also try out Crush via `nix-shell`:
121
122```bash
123# Add the NUR channel.
124nix-channel --add https://github.com/nix-community/NUR/archive/main.tar.gz nur
125nix-channel --update
126
127# Get Crush in a Nix shell.
128nix-shell -p '(import <nur> { pkgs = import <nixpkgs> {}; }).repos.charmbracelet.crush'
129```
130
131### NixOS & Home Manager Module Usage via NUR
132
133Crush provides NixOS and Home Manager modules via NUR.
134You can use these modules directly in your flake by importing them from NUR. Since it auto detects whether its a home manager or nixos context you can use the import the exact same way :)
135
136```nix
137{
138  inputs = {
139    nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
140    nur.url = "github:nix-community/NUR";
141  };
142
143  outputs = { self, nixpkgs, nur, ... }: {
144    nixosConfigurations.your-hostname = nixpkgs.lib.nixosSystem {
145      system = "x86_64-linux";
146      modules = [
147        nur.modules.nixos.default
148        nur.repos.charmbracelet.modules.crush
149        {
150          programs.crush = {
151            enable = true;
152            settings = {
153              providers = {
154                openai = {
155                  id = "openai";
156                  name = "OpenAI";
157                  base_url = "https://api.openai.com/v1";
158                  type = "openai";
159                  api_key = "sk-fake123456789abcdef...";
160                  models = [
161                    {
162                      id = "gpt-4";
163                      name = "GPT-4";
164                    }
165                  ];
166                };
167              };
168              lsp = {
169                go = { command = "gopls"; enabled = true; };
170                nix = { command = "nil"; enabled = true; };
171              };
172              options = {
173                context_paths = [ "/etc/nixos/configuration.nix" ];
174                tui = { compact_mode = true; };
175                debug = false;
176              };
177            };
178          };
179        }
180      ];
181    };
182  };
183}
184```
185
186</details>
187
188<details>
189<summary><strong>Debian/Ubuntu</strong></summary>
190
191```bash
192sudo mkdir -p /etc/apt/keyrings
193curl -fsSL https://repo.charm.sh/apt/gpg.key | sudo gpg --dearmor -o /etc/apt/keyrings/charm.gpg
194echo "deb [signed-by=/etc/apt/keyrings/charm.gpg] https://repo.charm.sh/apt/ * *" | sudo tee /etc/apt/sources.list.d/charm.list
195sudo apt update && sudo apt install crush
196```
197
198</details>
199
200<details>
201<summary><strong>Fedora/RHEL</strong></summary>
202
203```bash
204echo '[charm]
205name=Charm
206baseurl=https://repo.charm.sh/yum/
207enabled=1
208gpgcheck=1
209gpgkey=https://repo.charm.sh/yum/gpg.key' | sudo tee /etc/yum.repos.d/charm.repo
210sudo yum install crush
211```
212
213</details>
214
215Or, download it:
216
217- [Packages][releases] are available in Debian and RPM formats
218- [Binaries][releases] are available for Linux, macOS, Windows, FreeBSD, OpenBSD, and NetBSD
219
220[releases]: https://github.com/charmbracelet/crush/releases
221
222Or just install it with Go:
223
224```
225go install github.com/charmbracelet/crush@latest
226```
227
228> [!WARNING]
229> Productivity may increase when using Crush and you may find yourself nerd
230> sniped when first using the application. If the symptoms persist, join the
231> [Discord][discord] and nerd snipe the rest of us.
232
233## Getting Started
234
235The quickest way to get started is to grab an API key for your preferred
236provider such as Anthropic, OpenAI, Groq, or OpenRouter and just start
237Crush. You'll be prompted to enter your API key.
238
239That said, you can also set environment variables for preferred providers.
240
241| Environment Variable        | Provider                                           |
242| --------------------------- | -------------------------------------------------- |
243| `ANTHROPIC_API_KEY`         | Anthropic                                          |
244| `OPENAI_API_KEY`            | OpenAI                                             |
245| `OPENROUTER_API_KEY`        | OpenRouter                                         |
246| `GEMINI_API_KEY`            | Google Gemini                                      |
247| `CEREBRAS_API_KEY`          | Cerebras                                           |
248| `HF_TOKEN`                  | Huggingface Inference                              |
249| `VERTEXAI_PROJECT`          | Google Cloud VertexAI (Gemini)                     |
250| `VERTEXAI_LOCATION`         | Google Cloud VertexAI (Gemini)                     |
251| `GROQ_API_KEY`              | Groq                                               |
252| `AWS_ACCESS_KEY_ID`         | AWS Bedrock (Claude)                               |
253| `AWS_SECRET_ACCESS_KEY`     | AWS Bedrock (Claude)                               |
254| `AWS_REGION`                | AWS Bedrock (Claude)                               |
255| `AWS_PROFILE`               | AWS Bedrock (Custom Profile)                       |
256| `AWS_BEARER_TOKEN_BEDROCK`  | AWS Bedrock                                        |
257| `AZURE_OPENAI_API_ENDPOINT` | Azure OpenAI models                                |
258| `AZURE_OPENAI_API_KEY`      | Azure OpenAI models (optional when using Entra ID) |
259| `AZURE_OPENAI_API_VERSION`  | Azure OpenAI models                                |
260
261### By the Way
262
263Is there a provider you’d like to see in Crush? Is there an existing model that needs an update?
264
265Crush’s default model listing is managed in [Catwalk](https://github.com/charmbracelet/catwalk), a community-supported, open source repository of Crush-compatible models, and you’re welcome to contribute.
266
267<a href="https://github.com/charmbracelet/catwalk"><img width="174" height="174" alt="Catwalk Badge" src="https://github.com/user-attachments/assets/95b49515-fe82-4409-b10d-5beb0873787d" /></a>
268
269## Configuration
270
271Crush runs great with no configuration. That said, if you do need or want to
272customize Crush, configuration can be added either local to the project itself,
273or globally, with the following priority:
274
2751. `.crush.json`
2762. `crush.json`
2773. `$HOME/.config/crush/crush.json`
278
279Configuration itself is stored as a JSON object:
280
281```json
282{
283  "this-setting": { "this": "that" },
284  "that-setting": ["ceci", "cela"]
285}
286```
287
288As an additional note, Crush also stores ephemeral data, such as application state, in one additional location:
289
290```bash
291# Unix
292$HOME/.local/share/crush/crush.json
293
294# Windows
295%LOCALAPPDATA%\crush\crush.json
296```
297
298### LSPs
299
300Crush can use LSPs for additional context to help inform its decisions, just
301like you would. LSPs can be added manually like so:
302
303```json
304{
305  "$schema": "https://charm.land/crush.json",
306  "lsp": {
307    "go": {
308      "command": "gopls",
309      "env": {
310        "GOTOOLCHAIN": "go1.24.5"
311      }
312    },
313    "typescript": {
314      "command": "typescript-language-server",
315      "args": ["--stdio"]
316    },
317    "nix": {
318      "command": "nil"
319    }
320  }
321}
322```
323
324### MCPs
325
326Crush also supports Model Context Protocol (MCP) servers through three
327transport types: `stdio` for command-line servers, `http` for HTTP endpoints,
328and `sse` for Server-Sent Events. Environment variable expansion is supported
329using `$(echo $VAR)` syntax.
330
331```json
332{
333  "$schema": "https://charm.land/crush.json",
334  "mcp": {
335    "filesystem": {
336      "type": "stdio",
337      "command": "node",
338      "args": ["/path/to/mcp-server.js"],
339      "timeout": 120,
340      "disabled": false,
341      "env": {
342        "NODE_ENV": "production"
343      }
344    },
345    "github": {
346      "type": "http",
347      "url": "https://api.githubcopilot.com/mcp/",
348      "timeout": 120,
349      "disabled": false,
350      "headers": {
351        "Authorization": "Bearer $GH_PAT"
352      }
353    },
354    "streaming-service": {
355      "type": "sse",
356      "url": "https://example.com/mcp/sse",
357      "timeout": 120,
358      "disabled": false,
359      "headers": {
360        "API-Key": "$(echo $API_KEY)"
361      }
362    }
363  }
364}
365```
366
367### Memory
368
369Crush automatically includes two memory files for cross-project instructions.
370
371- `~/.config/crush/CRUSH.md`: Crush-specific rules that would confuse other
372  agentic coding tools. If you only use Crush, this is the only one you need to
373  edit.
374- `~/.config/AGENTS.md`: generic instructions that other coding tools might
375  read. Avoid referring to Crush-specific tools or workflows here.
376
377You can customize these paths using the `memory_paths` option in your
378configuration:
379
380```json
381{
382  "$schema": "https://charm.land/crush.json",
383  "options": {
384    "memory_paths": [
385      "/path/to/custom/memory/file.md",
386      "/path/to/folder/of/files/" // recursively load all .md files in folder
387    ]
388  }
389}
390```
391
392### Ignoring Files
393
394Crush respects `.gitignore` files by default, but you can also create a
395`.crushignore` file to specify additional files and directories that Crush
396should ignore. This is useful for excluding files that you want in version
397control but don't want Crush to consider when providing context.
398
399The `.crushignore` file uses the same syntax as `.gitignore` and can be placed
400in the root of your project or in subdirectories.
401
402### Allowing Tools
403
404By default, Crush will ask you for permission before running tool calls. If
405you'd like, you can allow tools to be executed without prompting you for
406permissions. Use this with care.
407
408```json
409{
410  "$schema": "https://charm.land/crush.json",
411  "permissions": {
412    "allowed_tools": [
413      "view",
414      "ls",
415      "grep",
416      "edit",
417      "mcp_context7_get-library-doc"
418    ]
419  }
420}
421```
422
423You can also skip all permission prompts entirely by running Crush with the
424`--yolo` flag. Be very, very careful with this feature.
425
426### Attribution Settings
427
428By default, Crush adds attribution information to Git commits and pull requests
429it creates. You can customize this behavior with the `attribution` option:
430
431```json
432{
433  "$schema": "https://charm.land/crush.json",
434  "options": {
435    "attribution": {
436      "trailer_style": "co-authored-by",
437      "generated_with": true
438    }
439  }
440}
441```
442
443- `trailer_style`: Controls the attribution trailer added to commit messages (default: `co-authored-by`)
444  - `co-authored-by`: Adds `Co-Authored-By: Crush <crush@charm.land>`
445  - `assisted-by`: Adds `Assisted-by: [Model Name] via Crush` (includes the model name)
446  - `none`: No attribution trailer
447- `generated_with`: When true (default), adds `💘 Generated with Crush` line to commit messages and PR descriptions
448
449### Custom Providers
450
451Crush supports custom provider configurations for both OpenAI-compatible and
452Anthropic-compatible APIs.
453
454> [!NOTE]
455> Note that we support two "types" for OpenAI. Make sure to choose the right one
456> to ensure the best experience!
457> * `openai` should be used when proxying or routing requests through OpenAI.
458> * `openai-compat` should be used when using non-OpenAI providers that have OpenAI-compatible APIs.
459
460#### OpenAI-Compatible APIs
461
462Here’s an example configuration for Deepseek, which uses an OpenAI-compatible
463API. Don't forget to set `DEEPSEEK_API_KEY` in your environment.
464
465```json
466{
467  "$schema": "https://charm.land/crush.json",
468  "providers": {
469    "deepseek": {
470      "type": "openai-compat",
471      "base_url": "https://api.deepseek.com/v1",
472      "api_key": "$DEEPSEEK_API_KEY",
473      "models": [
474        {
475          "id": "deepseek-chat",
476          "name": "Deepseek V3",
477          "cost_per_1m_in": 0.27,
478          "cost_per_1m_out": 1.1,
479          "cost_per_1m_in_cached": 0.07,
480          "cost_per_1m_out_cached": 1.1,
481          "context_window": 64000,
482          "default_max_tokens": 5000
483        }
484      ]
485    }
486  }
487}
488```
489
490#### Anthropic-Compatible APIs
491
492Custom Anthropic-compatible providers follow this format:
493
494```json
495{
496  "$schema": "https://charm.land/crush.json",
497  "providers": {
498    "custom-anthropic": {
499      "type": "anthropic",
500      "base_url": "https://api.anthropic.com/v1",
501      "api_key": "$ANTHROPIC_API_KEY",
502      "extra_headers": {
503        "anthropic-version": "2023-06-01"
504      },
505      "models": [
506        {
507          "id": "claude-sonnet-4-20250514",
508          "name": "Claude Sonnet 4",
509          "cost_per_1m_in": 3,
510          "cost_per_1m_out": 15,
511          "cost_per_1m_in_cached": 3.75,
512          "cost_per_1m_out_cached": 0.3,
513          "context_window": 200000,
514          "default_max_tokens": 50000,
515          "can_reason": true,
516          "supports_attachments": true
517        }
518      ]
519    }
520  }
521}
522```
523
524### Amazon Bedrock
525
526Crush currently supports running Anthropic models through Bedrock, with caching disabled.
527
528- A Bedrock provider will appear once you have AWS configured, i.e. `aws configure`
529- Crush also expects the `AWS_REGION` or `AWS_DEFAULT_REGION` to be set
530- To use a specific AWS profile set `AWS_PROFILE` in your environment, i.e. `AWS_PROFILE=myprofile crush`
531- Alternatively to `aws configure`, you can also just set `AWS_BEARER_TOKEN_BEDROCK`
532
533### Vertex AI Platform
534
535Vertex AI will appear in the list of available providers when `VERTEXAI_PROJECT` and `VERTEXAI_LOCATION` are set. You will also need to be authenticated:
536
537```bash
538gcloud auth application-default login
539```
540
541To add specific models to the configuration, configure as such:
542
543```json
544{
545  "$schema": "https://charm.land/crush.json",
546  "providers": {
547    "vertexai": {
548      "models": [
549        {
550          "id": "claude-sonnet-4@20250514",
551          "name": "VertexAI Sonnet 4",
552          "cost_per_1m_in": 3,
553          "cost_per_1m_out": 15,
554          "cost_per_1m_in_cached": 3.75,
555          "cost_per_1m_out_cached": 0.3,
556          "context_window": 200000,
557          "default_max_tokens": 50000,
558          "can_reason": true,
559          "supports_attachments": true
560        }
561      ]
562    }
563  }
564}
565```
566
567### Local Models
568
569Local models can also be configured via OpenAI-compatible API. Here are two common examples:
570
571#### Ollama
572
573```json
574{
575  "providers": {
576    "ollama": {
577      "name": "Ollama",
578      "base_url": "http://localhost:11434/v1/",
579      "type": "openai-compat",
580      "models": [
581        {
582          "name": "Qwen 3 30B",
583          "id": "qwen3:30b",
584          "context_window": 256000,
585          "default_max_tokens": 20000
586        }
587      ]
588    }
589  }
590}
591```
592
593#### LM Studio
594
595```json
596{
597  "providers": {
598    "lmstudio": {
599      "name": "LM Studio",
600      "base_url": "http://localhost:1234/v1/",
601      "type": "openai-compat",
602      "models": [
603        {
604          "name": "Qwen 3 30B",
605          "id": "qwen/qwen3-30b-a3b-2507",
606          "context_window": 256000,
607          "default_max_tokens": 20000
608        }
609      ]
610    }
611  }
612}
613```
614
615## Logging
616
617Sometimes you need to look at logs. Luckily, Crush logs all sorts of
618stuff. Logs are stored in `./.crush/logs/crush.log` relative to the project.
619
620The CLI also contains some helper commands to make perusing recent logs easier:
621
622```bash
623# Print the last 1000 lines
624crush logs
625
626# Print the last 500 lines
627crush logs --tail 500
628
629# Follow logs in real time
630crush logs --follow
631```
632
633Want more logging? Run `crush` with the `--debug` flag, or enable it in the
634config:
635
636```json
637{
638  "$schema": "https://charm.land/crush.json",
639  "options": {
640    "debug": true,
641    "debug_lsp": true
642  }
643}
644```
645
646## Desktop Notifications
647
648Crush defaults to sending desktop notifications that let you know:
649
650- When its turn is finished (automatically cancelled when you interact with the
651  chat interface)
652- When it's waiting for permission to execute a tool (automatically cancelled
653  when you interact with the permission dialog)
654
655### Disabling notifications
656
657If you prefer to work without desktop notifications, you can disable them in
658your `crush.json` config.
659
660```json
661{
662  "$schema": "https://charm.land/crush.json",
663  "options": {
664    "disable_notifications": true
665  }
666}
667```
668
669## Provider Auto-Updates
670
671By default, Crush automatically checks for the latest and greatest list of
672providers and models from [Catwalk](https://github.com/charmbracelet/catwalk),
673the open source Crush provider database. This means that when new providers and
674models are available, or when model metadata changes, Crush automatically
675updates your local configuration.
676
677### Disabling automatic provider updates
678
679For those with restricted internet access, or those who prefer to work in
680air-gapped environments, this might not be want you want, and this feature can
681be disabled.
682
683To disable automatic provider updates, set `disable_provider_auto_update` into
684your `crush.json` config:
685
686```json
687{
688  "$schema": "https://charm.land/crush.json",
689  "options": {
690    "disable_provider_auto_update": true
691  }
692}
693```
694
695Or set the `CRUSH_DISABLE_PROVIDER_AUTO_UPDATE` environment variable:
696
697```bash
698export CRUSH_DISABLE_PROVIDER_AUTO_UPDATE=1
699```
700
701### Manually updating providers
702
703Manually updating providers is possible with the `crush update-providers`
704command:
705
706```bash
707# Update providers remotely from Catwalk.
708crush update-providers
709
710# Update providers from a custom Catwalk base URL.
711crush update-providers https://example.com/
712
713# Update providers from a local file.
714crush update-providers /path/to/local-providers.json
715
716# Reset providers to the embedded version, embedded at crush at build time.
717crush update-providers embedded
718
719# For more info:
720crush update-providers --help
721```
722
723## Metrics
724
725Crush records pseudonymous usage metrics (tied to a device-specific hash),
726which maintainers rely on to inform development and support priorities. The
727metrics include solely usage metadata; prompts and responses are NEVER
728collected.
729
730Details on exactly what’s collected are in the source code ([here](https://github.com/charmbracelet/crush/tree/main/internal/event)
731and [here](https://github.com/charmbracelet/crush/blob/main/internal/llm/agent/event.go)).
732
733You can opt out of metrics collection at any time by setting the environment
734variable by setting the following in your environment:
735
736```bash
737export CRUSH_DISABLE_METRICS=1
738```
739
740Or by setting the following in your config:
741
742```json
743{
744  "options": {
745    "disable_metrics": true
746  }
747}
748```
749
750Crush also respects the [`DO_NOT_TRACK`](https://consoledonottrack.com)
751convention which can be enabled via `export DO_NOT_TRACK=1`.
752
753## A Note on Claude Max and GitHub Copilot
754
755Crush only supports model providers through official, compliant APIs. We do not
756support or endorse any methods that rely on personal Claude Max and GitHub
757Copilot accounts or OAuth workarounds, which violate Anthropic and
758Microsoft’s Terms of Service.
759
760We’re committed to building sustainable, trusted integrations with model
761providers. If you’re a provider interested in working with us,
762[reach out](mailto:vt100@charm.sh).
763
764## Contributing
765
766See the [contributing guide](https://github.com/charmbracelet/crush?tab=contributing-ov-file#contributing).
767
768## Whatcha think?
769
770We’d love to hear your thoughts on this project. Need help? We gotchu. You can find us on:
771
772- [Twitter](https://twitter.com/charmcli)
773- [Slack](https://charm.land/slack)
774- [Discord][discord]
775- [The Fediverse](https://mastodon.social/@charmcli)
776- [Bluesky](https://bsky.app/profile/charm.land)
777
778[discord]: https://charm.land/discord
779
780## License
781
782[FSL-1.1-MIT](https://github.com/charmbracelet/crush/raw/main/LICENSE.md)
783
784---
785
786Part of [Charm](https://charm.land).
787
788<a href="https://charm.land/"><img alt="The Charm logo" width="400" src="https://stuff.charm.sh/charm-banner-next.jpg" /></a>
789
790<!--prettier-ignore-->
791Charm热爱开源 • Charm loves open source