Plugin Development 
Quick Start 
A Semo plugin is a standard Node module, but it needs to conform to certain directory and file structure conventions. Since these conventions are often hard to remember, we provide various auxiliary tools for plugin developers or tool users, such as code auto-generation. This describes the recommended plugin development process, but after becoming familiar with it, you can also manually build a plugin starting from an empty directory.
First Step: Create plugin directory based on template 
semo create semo-plugin-xyz --template=pluginThis uses the built-in plugin template. As mentioned in the configuration management section, we can completely override the repo and branch options, or override the --template option to avoid passing default parameters every time.
Second Step: Enter the plugin directory, execute the default command, prove everything is normal 
cd semo-plugin-xyz
semo hiThis is a command built into the plugin template. After initialization is complete, enter the directory to execute it, completing your first interaction with the plugin command. If you see it answer you Hey you!, it means it's ready, and you can start writing scripts that truly change the world.
Adding Commands 
Note that this plugin template is based on Typescript, so you need some Typescript foundation. During development, it's recommended to keep the pnpm watch command window open for real-time compilation, allowing development and testing simultaneously.
semo generate command xyzGenerally, the plugin name and the commands encapsulated by the plugin have some association. Here we add an xyz command, but you can also modify the previous hi command. Once you truly master plugin development, the default hi command should be deleted.
Implementing Hooks 
Implementing hooks is another purpose of developing plugins. Hooks are often defined by other plugins or business projects. By implementing hooks, you can influence and change the behavior of other plugins.
Query which hooks are supported in the current environment with this command:
semo hook listExample 1: Implement hook_create_project_template 
// src/hooks/index.ts
export const semo__hook_create_project_template = {
  demo_repo: {
    repo: 'demo_repo.git',
    branch: 'master',
    alias: ['demo'],
  },
}Through this hook, when the semo create [PROJECT] --template command is executed, we can select custom project templates. We only need to remember the alias, not the address. Another benefit is that we don't need to care how each engineer has set the global --repo option on their personal computer. As long as the specified plugin is installed, everyone can initialize projects using the same project alias.
Example 2: Implement hook_repl 
// src/hooks/index.ts
export const hook_repl = {
  semo: () => {
    // Note: Original code had `semo: () {` which is invalid JS/TS. Assuming it meant `semo: () => {` or `semo: function() {`
    return {
      add: async (a, b) => {
        return a + b
      },
      multiple: async (a, b) => {
        return a * b
      },
    }
  },
}Then in the REPL environment, you can use it:
TIP
Information returned by hook_repl is injected into the Semo.hooks.application object in the REPL.
semo repl
>>> Semo.hooks.application.add
[Function: add]
>>> await Semo.hooks.application.add(1, 2)
3
>>> Semo.hooks.application.multiple
[Function: multiple]
>>> await Semo.hooks.application.multiple(3, 4)
12Plugins and business projects have different starting points when implementing this hook. Business projects generally inject specific business logic, while plugins typically inject common methods with a certain degree of reusability, such as instance methods of underlying services, commonly used libraries, etc.
Exposing Methods 
Another fundamental purpose of implementing plugins is to act as a module, exposing instances, methods, or libraries externally. In this case, on one hand, we can define modules in a standard way, for example:
WARNING
Since Semo later introduced the run command, and this command relies on the entry file for location, Semo plugins are required to declare an entry point, regardless of whether this entry point exposes methods.
// package.json
{
  "main": "lib/index.js"
}// index.js
export const func = () => {}Publishing Plugins 
By extending through commands, hooks, or libraries, we have written a Semo plugin. If you want to share your plugin with others, some preparation is needed.
1. Upload code to a git repository 
If it's open source, you can choose Github. If it's an internal plugin, upload it to the internal repository, which might be a Github private repository or the company's Gitlab repository.
2. Modify package.json 
Mainly package name, version, license, repository URL, homepage address, etc.
If it's an internal plugin, you might modify the registry address in the .npmrc file.
3. Obtain an npm repository account and log in 
If it's an open source plugin, you can register at https://npmjs.org. If it's a privately deployed npm repository, you can get an account from the operations team.
npm login --registry=[YOUR_REGISTRY]4. Test the plugin package 
npm pack --dry-runThrough package testing, check if the package contains unnecessary files and adjust the configuration of the .npmignore file.
5. Publish your plugin 
npm version [patch|minor|major]
npm publish6. Promote the plugin, share development experience 
Even good wine needs promotion. Write good documentation and actively promote it to get others to use it and provide feedback.
7. Actively maintain 
Any npm package can potentially become outdated or have security risks. We need to actively maintain it to ensure the plugin performs its intended function.
Plugin Levels 
Semo's plugin system scans multiple locations to increase flexibility, with each level corresponding to different purposes and limitations.
- Installed globally via npm install -g semo-plugin-xxx. The installed plugin commands are globally available. This isnpm's default way of installing global packages.
- Installed to the home directory's .semo/home-plugin-cachedirectory viasemo plugin install semo-plugin-xxx. The installed plugin commands are also globally available. This method can be used in situations where the current user doesn't have permission to install globally using npm.
- Installed to the current project directory via npm install semo-plugin-xxx. Plugin commands installed this way only take effect within the current project.
Why would some plugins need to be installed globally? Because plugins can not only implement our project's business requirements but also our development toolchain, and even some non-business small functions. With imagination, any terminal function is possible. They can be entirely handwritten or encapsulate and integrate other excellent projects, which are not limited by language or language extension package repositories.
Running Remote Plugins Directly 
This is just an illusion; they still need to be downloaded locally, but the download directory is separate, so it won't interfere with your implementation. You can freely test any plugins you are interested in.
semo run semo-plugin-serveThis plugin's function is to provide a simple HTTP service. It will be downloaded on the first run, and subsequent runs will reuse the previously downloaded plugin. Use --force to force an update.
Special Home Directory Plugin 
This feature introduced in
v0.8.0
To add global configurations for Semo, we need to add a .semorc.yml configuration file in the ~/.semo directory. Once this configuration file is established, the .semo directory is automatically recognized as a global plugin (other global plugins are in the ~/.semo/home-plugin-cache directory). You can define some of your own commands, extend commands of other plugins, extend hooks of other plugins, etc., within this plugin. This special plugin is globally recognizable. Also, because it exists by default, if you have some logic that is commonly used locally and you don't want to publish it as an npm package, you can quickly start here. Of course, be aware that its globally available nature means errors can also affect the local global environment.
We haven't preset the implementation method for this special plugin, meaning you can write it in js or typescript. You can use the semo init command to initialize the basic directory structure, or use semo create .semo --template=plugin to regenerate a .semo directory using a template (you need to back up the .semo directory beforehand and then merge the contents back).
Recognizing Plugins in Arbitrary Directories 
We can see pluginDir in the configuration file. If this parameter is manually specified during command execution, it can achieve the purpose of arbitrary specification, and it also supports multiple directories:
semo help --plugin-dir=dir1 --plugin-dir=dir2Additionally, it supports specification via constants:
SEMO_PLUGIN_DIR=dir3 semo helpPlugin Active Registration Mechanism 
Introduced in
v1.3.0
Early Semo only supported the automatic registration mechanism for plugins. For flexibility, it could traverse multiple locations, incurring some IO performance loss. Therefore, the active registration mechanism was added. Once the active registration mechanism is used, the automatic registration mechanism is automatically disabled.
How to Enable 
Write key-value pairs for plugins under the $plugins.register section in .semorc.yml.
$plugins:
  register:
    plugin-a: /absolute/path
    plugin-b: ./relative/path
    plugin-c: ~relative/path/from/home # Or interpreted as module path
    plugin-d: true # Use Node.js module resolutionFour styles are supported: absolute paths and relative paths (starting with ./) are easy to understand. The third (~) typically refers to the home directory but might also be interpreted as part of a module path depending on implementation. The fourth (true) uses Node.js's module loading mechanism to declare. For the plugin name used as the key, the semo-plugin- prefix can be omitted here.