@vibe-coder/cli 1.0.0 → 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +76 -56
- package/README.zh.md +157 -0
- package/dist/cli.js +398 -181
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,141 +1,161 @@
|
|
|
1
|
-
|
|
2
1
|
<div align="center">
|
|
3
2
|
|
|
4
3
|
# 🌊 Vibe Coding CLI
|
|
5
4
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
[](./package.json)
|
|
9
|
-
[](../../LICENSE.md)
|
|
10
|
-
[](https://bun.sh)
|
|
5
|
+
**A vibe coding ecosystem builder tailored for OpenCode**
|
|
11
6
|
|
|
12
|
-
[
|
|
7
|
+
**English** · [简体中文](https://github.com/HelloGGX/skill/blob/main/packages/vibe/README.zh.md)
|
|
13
8
|
|
|
14
9
|
</div>
|
|
15
10
|
|
|
16
|
-
## 📖
|
|
11
|
+
## 📖 Introduction
|
|
17
12
|
|
|
18
|
-
`vibe-coding-cli`
|
|
13
|
+
`vibe-coding-cli` is a modern command-line scaffolding tool built specifically for the **OpenCode** platform. Its core objective is to quickly set up a Vibe Coding development environment and simplify resource management for specification-driven development.
|
|
19
14
|
|
|
20
|
-
|
|
15
|
+
With the `vibe` command, you can pull TypeScript/Python tool scripts or Markdown rule files from remote GitHub repositories with a single click. It automatically and seamlessly registers them into your OpenCode configuration and manages the underlying runtime dependency environments, allowing you to focus entirely on "co-creating code with AI".
|
|
21
16
|
|
|
22
|
-
## ✨
|
|
17
|
+
## ✨ Features
|
|
23
18
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
-
|
|
28
|
-
|
|
19
|
+
* 🛠 **Fully Automated Tool Management:** Quickly parse, select, and download `.ts` / `.py` scripts from any GitHub repository straight to your local `.opencode/tool/` directory, ready to use out of the box.
|
|
20
|
+
* 📜 **Integrates Everything You Need for Vibe Coding:** A unique ecosystem aggregation capability that perfectly blends the **Capabilities (tools and skills)** needed for Agent execution with the **Context (guidelines and best practices)**. Supports on-demand installation of `.md` rule files so the AI truly understands your architectural intent and coding standards.
|
|
21
|
+
* 📦 **Smart Configuration Injection:** Automatically intercepts and updates `.opencode/opencode.jsonc`, silently injecting tool enable-switches and Prompt instruction paths. Say goodbye to tedious manual configuration forever.
|
|
22
|
+
* ⚡ **Lightning-Fast Parallel Updates:** Designed with a concurrency model to simultaneously handle resource comparison and pulling from multiple source repositories, drastically reducing update wait times in multi-dependency scenarios.
|
|
23
|
+
* 🪄 **Standard Skills Aggregation:** Deeply integrated with Vercel's `pnpx skills` ecosystem, allowing you to manage standard Agent skill libraries alongside localized extension resources within a unified CLI workflow.
|
|
29
24
|
|
|
30
25
|
---
|
|
31
26
|
|
|
32
|
-
## 🚀
|
|
27
|
+
## 🚀 Quick Start
|
|
33
28
|
|
|
34
|
-
###
|
|
29
|
+
### Installation
|
|
35
30
|
|
|
36
|
-
|
|
31
|
+
Install as a global package (npm or bun recommended):
|
|
37
32
|
|
|
38
33
|
```bash
|
|
39
|
-
#
|
|
34
|
+
# Using npm
|
|
40
35
|
npm install -g vibe-coding-cli
|
|
41
36
|
|
|
42
|
-
#
|
|
37
|
+
# Using bun
|
|
43
38
|
bun add -g vibe-coding-cli
|
|
44
39
|
|
|
45
40
|
```
|
|
46
41
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
### 基础用法
|
|
42
|
+
### Basic Usage
|
|
50
43
|
|
|
51
|
-
|
|
44
|
+
Initialize and add an ecosystem library (e.g., `helloggx/skill` from this project):
|
|
52
45
|
|
|
53
46
|
```bash
|
|
54
47
|
vibe add helloggx/skill
|
|
55
48
|
|
|
56
49
|
```
|
|
57
50
|
|
|
58
|
-
|
|
51
|
+
*This command will pop up an interactive menu, allowing you to flexibly select the **Tools** and **Rules** you want to install. The CLI will automatically configure the entire environment for you.*
|
|
59
52
|
|
|
60
53
|
---
|
|
61
54
|
|
|
62
|
-
## 📚
|
|
55
|
+
## 📚 Commands
|
|
63
56
|
|
|
64
|
-
### 1.
|
|
57
|
+
### 1. Add Resources (`add` / `a`)
|
|
65
58
|
|
|
66
59
|
```bash
|
|
67
60
|
vibe add <repository>
|
|
68
61
|
|
|
69
62
|
```
|
|
70
63
|
|
|
71
|
-
|
|
64
|
+
**Execution Flow**:
|
|
72
65
|
|
|
73
|
-
1.
|
|
74
|
-
2.
|
|
75
|
-
3.
|
|
76
|
-
4.
|
|
66
|
+
1. Invokes native capabilities to install basic Agent skills from the target repository.
|
|
67
|
+
2. Clones and parses the `skill`, `tool`, and `rules` asset directories in the target repository.
|
|
68
|
+
3. Triggers an interactive multi-select list to pick specific tool scripts and rule documents on demand.
|
|
69
|
+
4. Automatically executes file copying, intelligently merges common rules, updates `opencode.jsonc`, and configures the Python dependency environment (if needed).
|
|
77
70
|
|
|
78
|
-
### 2.
|
|
71
|
+
### 2. List Installed Items (`list` / `ls`)
|
|
79
72
|
|
|
80
73
|
```bash
|
|
81
74
|
vibe list
|
|
82
75
|
|
|
83
76
|
```
|
|
84
77
|
|
|
85
|
-
|
|
86
|
-
|
|
78
|
+
**Function**:
|
|
79
|
+
Clearly prints a status map of all installed resources in the current project, including:
|
|
87
80
|
|
|
88
|
-
|
|
81
|
+
* 🛠️ Local Tools
|
|
82
|
+
* 📜 Injected Context Rules (Local Rules)
|
|
83
|
+
* 🪄 Global Standard Skills
|
|
84
|
+
|
|
85
|
+
### 3. One-Click Sync & Update (`update` / `up`)
|
|
89
86
|
|
|
90
87
|
```bash
|
|
91
88
|
vibe update
|
|
92
89
|
|
|
93
90
|
```
|
|
94
91
|
|
|
95
|
-
|
|
96
|
-
|
|
92
|
+
**Function**:
|
|
93
|
+
Executes a full workspace update with one click. The CLI will concurrently pull all source repositories recorded in `vibe-lock.json`, intelligently compare and overwrite the latest local scripts and rule files, and simultaneously trigger an upgrade of the standard skills library.
|
|
94
|
+
|
|
95
|
+
### 4. Remove Resources (`remove` / `rm`)
|
|
96
|
+
|
|
97
|
+
```bash
|
|
98
|
+
# Interactive mode: Pops up a UI list to select tools and rules to remove
|
|
99
|
+
vibe remove
|
|
100
|
+
|
|
101
|
+
# Shortcut mode: Directly specify the resource to remove (supports standard skills and local tools/rules)
|
|
102
|
+
vibe remove <resource>
|
|
103
|
+
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
**Execution Flow**:
|
|
107
|
+
|
|
108
|
+
1. Invokes native capabilities to remove standard Agent skills from the target repository (via `pnpx skills remove`).
|
|
109
|
+
2. Parses the list of installed resources in the local `vibe-lock.json`.
|
|
110
|
+
3. **Interactive Mode**: Pops up a multi-select list, allowing you to choose specific tools and rule categories for removal.
|
|
111
|
+
4. **Shortcut Mode**: Directly matches and removes local tool/rule files based on the passed arguments.
|
|
112
|
+
5. Automatically cleans up the corresponding physical files (`.opencode/tool/` and `.opencode/rules/`) and synchronously updates the `opencode.jsonc` configuration.
|
|
97
113
|
|
|
98
114
|
---
|
|
99
115
|
|
|
100
|
-
## 📂
|
|
116
|
+
## 📂 Workspace Structure
|
|
101
117
|
|
|
102
|
-
|
|
118
|
+
After running `vibe add`, the tool will automatically create and maintain the following standard Vibe Coding structure in your project's root directory:
|
|
103
119
|
|
|
104
120
|
```text
|
|
105
121
|
your-project/
|
|
106
122
|
├── .opencode/
|
|
107
|
-
│
|
|
108
|
-
│
|
|
109
|
-
│
|
|
110
|
-
│
|
|
111
|
-
│
|
|
112
|
-
|
|
113
|
-
|
|
123
|
+
│ ├── tool/ # Stores the pulled underlying .ts / .py tool scripts
|
|
124
|
+
│ │ ├── get_dsl.ts
|
|
125
|
+
│ │ └── ...
|
|
126
|
+
│ ├── rules/ # Stores the pulled .md rule files (categorized by type)
|
|
127
|
+
│ │ ├── common/
|
|
128
|
+
│ │ └── typescript/
|
|
129
|
+
│ ├── opencode.jsonc # Core OpenCode configuration file
|
|
130
|
+
│ │ # (vibe automatically manages "tools": {...} and "instructions": [...] inside)
|
|
131
|
+
│ └── vibe-lock.json # Internal state lock file, accurately recording resource sources, versions, and update timestamps
|
|
132
|
+
├── .venv/ # (Auto-created as needed) Isolated Python virtual environment
|
|
133
|
+
└── requirements.txt # (Auto-maintained as needed) Dependency list required for Python scripts
|
|
114
134
|
|
|
115
135
|
```
|
|
116
136
|
|
|
117
137
|
---
|
|
118
138
|
|
|
119
|
-
## 🛠️
|
|
139
|
+
## 🛠️ Development
|
|
120
140
|
|
|
121
|
-
|
|
141
|
+
This project is built on top of [Bun](https://bun.sh/), offering a lightning-fast execution and bundling experience.
|
|
122
142
|
|
|
123
143
|
```bash
|
|
124
|
-
# 1.
|
|
144
|
+
# 1. Install project dependencies
|
|
125
145
|
bun install
|
|
126
146
|
|
|
127
|
-
# 2.
|
|
147
|
+
# 2. Run CLI locally for debugging
|
|
128
148
|
bun run dev --help
|
|
129
149
|
|
|
130
|
-
# 3.
|
|
150
|
+
# 3. Strict type checking
|
|
131
151
|
bun run typecheck
|
|
132
152
|
|
|
133
|
-
# 4.
|
|
153
|
+
# 4. Build production version (outputs to ./dist)
|
|
134
154
|
bun run build
|
|
135
155
|
|
|
136
156
|
```
|
|
137
157
|
|
|
138
|
-
## 📄
|
|
158
|
+
## 📄 License
|
|
139
159
|
|
|
140
|
-
|
|
160
|
+
This project is open-sourced under the [MIT License](https://www.google.com/search?q=../../LICENSE.md).
|
|
141
161
|
© 2026 [HelloGGX](https://github.com/HelloGGX)
|
package/README.zh.md
ADDED
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
<div align="center">
|
|
2
|
+
|
|
3
|
+
# 🌊 Vibe Coding CLI
|
|
4
|
+
|
|
5
|
+
**专为 OpenCode 打造的 vibe coding 生态构建工具**
|
|
6
|
+
|
|
7
|
+
[](./package.json)
|
|
8
|
+
[](../../LICENSE.md)
|
|
9
|
+
[](https://bun.sh)
|
|
10
|
+
|
|
11
|
+
[English](https://github.com/HelloGGX/skill/blob/main/packages/vibe/README.md) · **简体中文**
|
|
12
|
+
|
|
13
|
+
</div>
|
|
14
|
+
|
|
15
|
+
## 📖 简介 (Introduction)
|
|
16
|
+
|
|
17
|
+
`vibe-coding-cli` 是一个专为 **OpenCode** 平台打造的现代命令行脚手架工具。它的核心目标是快速搭建 Vibe Coding 的开发环境,简化规范驱动开发的资源管理。
|
|
18
|
+
|
|
19
|
+
通过 `vibe` 命令,你可以一键拉取远程 GitHub 仓库中的 TypeScript/Python 工具脚本或 Markdown 规则文件,自动将其无缝注册到 OpenCode 配置中,并接管其底层的运行依赖环境,让你专注于“与 AI 共创代码”本身。
|
|
20
|
+
|
|
21
|
+
## ✨ 核心特性 (Features)
|
|
22
|
+
|
|
23
|
+
- 🛠 **全自动化工具管理**: 支持从任意 GitHub 仓库快速解析、选择并下载 `.ts` / `.py` 脚本至本地 `.opencode/tool/` 目录,开箱即用。
|
|
24
|
+
- 📜 **集成 Vibe Coding 所需的一切**: 独创的生态聚合能力,将 Agent 执行所需的 **Capabilities(工具与技能)** 与 **Context(行为准则与最佳实践)** 完美融合。支持按需安装 `.md` 规则文件,让 AI 真正懂你的架构意图与代码规范。
|
|
25
|
+
- 📦 **智能配置注入**: 自动拦截并更新 `.opencode/opencode.jsonc`,无感注入工具的启用开关与 Prompt 指令(instructions)路径,彻底告别繁琐的手动配置。
|
|
26
|
+
- ⚡ **并行极速更新**: 基于并发模型设计,同时处理多个源仓库的资源对比与拉取,大幅缩短多依赖场景下的更新等待时间。
|
|
27
|
+
- 🪄 **标准技能聚合**: 与 Vercel 的 `pnpx skills` 生态深度集成,在统一的 CLI 流程中同时管理标准 Agent 技能库和本地化扩展资源。
|
|
28
|
+
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
## 🚀 快速开始 (Quick Start)
|
|
32
|
+
|
|
33
|
+
### 安装
|
|
34
|
+
|
|
35
|
+
作为全局包安装(推荐使用 npm 或 bun):
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
# 使用 npm
|
|
39
|
+
npm install -g vibe-coding-cli
|
|
40
|
+
|
|
41
|
+
# 使用 bun
|
|
42
|
+
bun add -g vibe-coding-cli
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### 基础用法
|
|
46
|
+
|
|
47
|
+
初始化并添加一个生态库(例如本项目的 `helloggx/skill`):
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
vibe add helloggx/skill
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
*此命令将会弹出交互式菜单,允许你灵活多选想要安装的 **Tools (工具)** 和 **Rules (规则)**,CLI 将会自动为你完成所有环境的配置。*
|
|
54
|
+
|
|
55
|
+
---
|
|
56
|
+
|
|
57
|
+
## 📚 命令指南 (Commands)
|
|
58
|
+
|
|
59
|
+
### 1. 添加资源 (`add` / `a`)
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
vibe add <repository>
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
**执行流程**:
|
|
66
|
+
|
|
67
|
+
1. 调用原生能力,安装目标仓库中的基础 Agent 技能。
|
|
68
|
+
2. 克隆并解析目标仓库中的 `skill`、`tool` 和 `rules` 资产目录。
|
|
69
|
+
3. 唤起交互式多选列表,按需挑选具体的工具脚本和规则文档。
|
|
70
|
+
4. 自动执行文件拷贝、智能合并公共规则、更新 `opencode.jsonc` 并配置 Python 依赖环境(如需)。
|
|
71
|
+
|
|
72
|
+
### 2. 查看已安装项 (`list` / `ls`)
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
vibe list
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
**功能**:
|
|
79
|
+
清晰打印当前项目中安装的所有资源态势图,包含:
|
|
80
|
+
|
|
81
|
+
* 🛠️ 本地扩展工具 (Local Tools)
|
|
82
|
+
* 📜 注入的上下文规则 (Local Rules)
|
|
83
|
+
* 🪄 全局标准技能 (Standard Skills)
|
|
84
|
+
|
|
85
|
+
### 3. 一键同步更新 (`update` / `up`)
|
|
86
|
+
|
|
87
|
+
```bash
|
|
88
|
+
vibe update
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
**功能**:
|
|
92
|
+
一键执行工作区全量更新。CLI 会并发拉取 `vibe-lock.json` 中记录的所有源仓库,智能比对并覆盖最新的本地脚本和规则文件,同时触发标准技能库的升级。
|
|
93
|
+
|
|
94
|
+
### 4. 移除资源 (`remove` / `rm`)
|
|
95
|
+
|
|
96
|
+
```bash
|
|
97
|
+
# 交互式模式:弹出 UI 列表选择要移除的工具和规则
|
|
98
|
+
vibe remove
|
|
99
|
+
|
|
100
|
+
# 快捷模式:直接指定要移除的资源(支持标准 skills 和本地工具/规则)
|
|
101
|
+
vibe remove <resource>
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
**执行流程**:
|
|
105
|
+
|
|
106
|
+
1. 调用原生能力,移除目标仓库中的标准 Agent 技能(通过 `pnpx skills remove`)。
|
|
107
|
+
2. 解析本地 `vibe-lock.json` 中的已安装资源列表。
|
|
108
|
+
3. **交互模式**:弹出多选列表,允许你选择具体的工具和规则分类进行移除。
|
|
109
|
+
4. **快捷模式**:根据传入的参数直接匹配并移除本地工具/规则文件。
|
|
110
|
+
5. 自动清理对应的物理文件(`.opencode/tool/` 和 `.opencode/rules/`),并同步更新 `opencode.jsonc` 配置。
|
|
111
|
+
|
|
112
|
+
---
|
|
113
|
+
|
|
114
|
+
## 📂 目录与配置规范 (Workspace Structure)
|
|
115
|
+
|
|
116
|
+
运行 `vibe add` 后,工具将在你的项目根目录下自动创建并维护以下标准 Vibe Coding 结构:
|
|
117
|
+
|
|
118
|
+
```text
|
|
119
|
+
your-project/
|
|
120
|
+
├── .opencode/
|
|
121
|
+
│ ├── tool/ # 存放被拉取下来的底层 .ts / .py 工具脚本
|
|
122
|
+
│ │ ├── get_dsl.ts
|
|
123
|
+
│ │ └── ...
|
|
124
|
+
│ ├── rules/ # 存放被拉取下来的 .md 规则文件(按类别分类归档)
|
|
125
|
+
│ │ ├── common/
|
|
126
|
+
│ │ └── typescript/
|
|
127
|
+
│ ├── opencode.jsonc # OpenCode 核心配置文件
|
|
128
|
+
│ │ # (vibe 会自动管理其中的 "tools": {...} 和 "instructions": [...])
|
|
129
|
+
│ └── vibe-lock.json # 内部状态锁文件,精准记录资源来源仓库、版本与更新时间戳
|
|
130
|
+
├── .venv/ # (按需自动创建) 隔离的 Python 虚拟环境
|
|
131
|
+
└── requirements.txt # (按需自动维护) Python 脚本所需的依赖清单
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
---
|
|
135
|
+
|
|
136
|
+
## 🛠️ 开发者指南 (Development)
|
|
137
|
+
|
|
138
|
+
本项目底层基于 [Bun](https://bun.sh/) 构建,拥有极速的执行与打包体验。
|
|
139
|
+
|
|
140
|
+
```bash
|
|
141
|
+
# 1. 安装项目依赖
|
|
142
|
+
bun install
|
|
143
|
+
|
|
144
|
+
# 2. 本地调试运行 CLI
|
|
145
|
+
bun run dev --help
|
|
146
|
+
|
|
147
|
+
# 3. 严格类型检查
|
|
148
|
+
bun run typecheck
|
|
149
|
+
|
|
150
|
+
# 4. 构建生产版本 (输出至 ./dist)
|
|
151
|
+
bun run build
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
## 📄 许可证 (License)
|
|
155
|
+
|
|
156
|
+
本项目采用 [MIT License](https://www.google.com/search?q=../../LICENSE.md) 进行开源。
|
|
157
|
+
© 2026 [HelloGGX](https://github.com/HelloGGX)
|
package/dist/cli.js
CHANGED
|
@@ -1317,6 +1317,25 @@ class Vt extends x {
|
|
|
1317
1317
|
}
|
|
1318
1318
|
}
|
|
1319
1319
|
}
|
|
1320
|
+
|
|
1321
|
+
class kt extends x {
|
|
1322
|
+
get cursor() {
|
|
1323
|
+
return this.value ? 0 : 1;
|
|
1324
|
+
}
|
|
1325
|
+
get _value() {
|
|
1326
|
+
return this.cursor === 0;
|
|
1327
|
+
}
|
|
1328
|
+
constructor(e) {
|
|
1329
|
+
super(e, false), this.value = !!e.initialValue, this.on("userInput", () => {
|
|
1330
|
+
this.value = this._value;
|
|
1331
|
+
}), this.on("confirm", (s) => {
|
|
1332
|
+
this.output.write(import_sisteransi.cursor.move(0, -1)), this.value = s, this.state = "submit", this.close();
|
|
1333
|
+
}), this.on("cursor", () => {
|
|
1334
|
+
this.value = !this.value;
|
|
1335
|
+
});
|
|
1336
|
+
}
|
|
1337
|
+
}
|
|
1338
|
+
|
|
1320
1339
|
class yt extends x {
|
|
1321
1340
|
options;
|
|
1322
1341
|
cursor = 0;
|
|
@@ -1689,6 +1708,33 @@ var X2 = (t) => {
|
|
|
1689
1708
|
B2.push(w);
|
|
1690
1709
|
return h && B2.push(g), B2;
|
|
1691
1710
|
};
|
|
1711
|
+
var Re = (t) => {
|
|
1712
|
+
const r = t.active ?? "Yes", s = t.inactive ?? "No";
|
|
1713
|
+
return new kt({ active: r, inactive: s, signal: t.signal, input: t.input, output: t.output, initialValue: t.initialValue ?? true, render() {
|
|
1714
|
+
const i = t.withGuide ?? _.withGuide, a = `${i ? `${import_picocolors2.default.gray(d)}
|
|
1715
|
+
` : ""}${W2(this.state)} ${t.message}
|
|
1716
|
+
`, o = this.value ? r : s;
|
|
1717
|
+
switch (this.state) {
|
|
1718
|
+
case "submit": {
|
|
1719
|
+
const u = i ? `${import_picocolors2.default.gray(d)} ` : "";
|
|
1720
|
+
return `${a}${u}${import_picocolors2.default.dim(o)}`;
|
|
1721
|
+
}
|
|
1722
|
+
case "cancel": {
|
|
1723
|
+
const u = i ? `${import_picocolors2.default.gray(d)} ` : "";
|
|
1724
|
+
return `${a}${u}${import_picocolors2.default.strikethrough(import_picocolors2.default.dim(o))}${i ? `
|
|
1725
|
+
${import_picocolors2.default.gray(d)}` : ""}`;
|
|
1726
|
+
}
|
|
1727
|
+
default: {
|
|
1728
|
+
const u = i ? `${import_picocolors2.default.cyan(d)} ` : "", l = i ? import_picocolors2.default.cyan(x2) : "";
|
|
1729
|
+
return `${a}${u}${this.value ? `${import_picocolors2.default.green(Q2)} ${r}` : `${import_picocolors2.default.dim(H2)} ${import_picocolors2.default.dim(r)}`}${t.vertical ? i ? `
|
|
1730
|
+
${import_picocolors2.default.cyan(d)} ` : `
|
|
1731
|
+
` : ` ${import_picocolors2.default.dim("/")} `}${this.value ? `${import_picocolors2.default.dim(H2)} ${import_picocolors2.default.dim(s)}` : `${import_picocolors2.default.green(Q2)} ${s}`}
|
|
1732
|
+
${l}
|
|
1733
|
+
`;
|
|
1734
|
+
}
|
|
1735
|
+
}
|
|
1736
|
+
} }).prompt();
|
|
1737
|
+
};
|
|
1692
1738
|
var R2 = { message: (t = [], { symbol: r = import_picocolors2.default.gray(d), secondarySymbol: s = import_picocolors2.default.gray(d), output: i = process.stdout, spacing: a = 1, withGuide: o } = {}) => {
|
|
1693
1739
|
const u = [], l = o ?? _.withGuide, n = l ? s : "", c = l ? `${r} ` : "", g = l ? `${s} ` : "";
|
|
1694
1740
|
for (let p = 0;p < a; p++)
|
|
@@ -1868,10 +1914,10 @@ var bt2 = ({ indicator: t = "dots", onCancel: r, output: s = process.stdout, can
|
|
|
1868
1914
|
var zt = { light: C("─", "-"), heavy: C("━", "="), block: C("█", "#") };
|
|
1869
1915
|
var Qt = `${import_picocolors2.default.gray(d)} `;
|
|
1870
1916
|
|
|
1871
|
-
// src/add.ts
|
|
1872
|
-
import { execSync } from "child_process";
|
|
1873
|
-
import { existsSync, mkdirSync
|
|
1874
|
-
import
|
|
1917
|
+
// src/commands/add.ts
|
|
1918
|
+
import { execSync as execSync2 } from "child_process";
|
|
1919
|
+
import { existsSync as existsSync4, mkdirSync as mkdirSync3, readdirSync as readdirSync2 } from "fs";
|
|
1920
|
+
import path4 from "path";
|
|
1875
1921
|
|
|
1876
1922
|
// ../../node_modules/.bun/simple-git@3.32.2/node_modules/simple-git/dist/esm/index.js
|
|
1877
1923
|
var import_file_exists = __toESM(require_dist(), 1);
|
|
@@ -5958,6 +6004,10 @@ async function cleanupTempDir(dir) {
|
|
|
5958
6004
|
await rm(dir, { recursive: true, force: true });
|
|
5959
6005
|
}
|
|
5960
6006
|
|
|
6007
|
+
// src/utils/config.ts
|
|
6008
|
+
import { existsSync, mkdirSync, writeFileSync, readFileSync } from "fs";
|
|
6009
|
+
import path from "path";
|
|
6010
|
+
|
|
5961
6011
|
// src/constants.ts
|
|
5962
6012
|
var OPENCODE_DIR = ".opencode";
|
|
5963
6013
|
var TOOL_SUBDIR = "tool";
|
|
@@ -5973,7 +6023,29 @@ var YELLOW = "\x1B[33m";
|
|
|
5973
6023
|
var DIM = "\x1B[38;5;102m";
|
|
5974
6024
|
var TEXT = "\x1B[38;5;145m";
|
|
5975
6025
|
|
|
5976
|
-
// src/
|
|
6026
|
+
// src/utils/error.ts
|
|
6027
|
+
function handleExecError(error, context, severity = "warn" /* WARN */) {
|
|
6028
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
6029
|
+
const formatted = `${context}
|
|
6030
|
+
${YELLOW}${message}${RESET}`;
|
|
6031
|
+
switch (severity) {
|
|
6032
|
+
case "error" /* ERROR */:
|
|
6033
|
+
R2.error(formatted);
|
|
6034
|
+
process.exit(1);
|
|
6035
|
+
case "warn" /* WARN */:
|
|
6036
|
+
R2.warn(formatted);
|
|
6037
|
+
break;
|
|
6038
|
+
default:
|
|
6039
|
+
R2.info(formatted);
|
|
6040
|
+
}
|
|
6041
|
+
}
|
|
6042
|
+
|
|
6043
|
+
// src/utils/config.ts
|
|
6044
|
+
function parseJsonc(content) {
|
|
6045
|
+
let safeJsonStr = content.replace(/"(?:\\.|[^"\\])*"|\/\/[^\n]*|\/\*[\s\S]*?\*\//g, (m) => m.startsWith('"') ? m : "");
|
|
6046
|
+
safeJsonStr = safeJsonStr.replace(/,\s*([\]}])/g, "$1");
|
|
6047
|
+
return JSON.parse(safeJsonStr);
|
|
6048
|
+
}
|
|
5977
6049
|
function getLockFilePath() {
|
|
5978
6050
|
return path.join(process.cwd(), OPENCODE_DIR, LOCK_FILE);
|
|
5979
6051
|
}
|
|
@@ -5984,6 +6056,8 @@ function readLockFile() {
|
|
|
5984
6056
|
const parsed = JSON.parse(readFileSync(lockPath, "utf-8"));
|
|
5985
6057
|
if (!parsed.rules)
|
|
5986
6058
|
parsed.rules = {};
|
|
6059
|
+
if (!parsed.tools)
|
|
6060
|
+
parsed.tools = {};
|
|
5987
6061
|
return parsed;
|
|
5988
6062
|
}
|
|
5989
6063
|
} catch (e2) {}
|
|
@@ -6016,138 +6090,174 @@ function ensureOpencodeConfig() {
|
|
|
6016
6090
|
writeFileSync(configPath, jsoncContent, "utf-8");
|
|
6017
6091
|
}
|
|
6018
6092
|
}
|
|
6019
|
-
function
|
|
6020
|
-
if (newTools.length === 0)
|
|
6093
|
+
function updateOpencodeConfig(newTools, newRulePaths) {
|
|
6094
|
+
if (newTools.length === 0 && newRulePaths.length === 0)
|
|
6021
6095
|
return;
|
|
6022
6096
|
const configPath = path.join(process.cwd(), OPENCODE_DIR, CONFIG_FILE);
|
|
6023
6097
|
if (!existsSync(configPath))
|
|
6024
6098
|
return;
|
|
6025
6099
|
try {
|
|
6026
6100
|
const content = readFileSync(configPath, "utf-8");
|
|
6027
|
-
|
|
6028
|
-
safeJsonStr = safeJsonStr.replace(/,\s*([\]}])/g, "$1");
|
|
6029
|
-
const config = JSON.parse(safeJsonStr);
|
|
6030
|
-
config.tools = config.tools || {};
|
|
6101
|
+
const config = parseJsonc(content);
|
|
6031
6102
|
let updated = false;
|
|
6032
|
-
|
|
6033
|
-
|
|
6034
|
-
|
|
6035
|
-
|
|
6103
|
+
if (newTools.length > 0) {
|
|
6104
|
+
config.tools = config.tools || {};
|
|
6105
|
+
for (const tool of newTools) {
|
|
6106
|
+
if (!config.tools[tool]) {
|
|
6107
|
+
config.tools[tool] = true;
|
|
6108
|
+
updated = true;
|
|
6109
|
+
}
|
|
6110
|
+
}
|
|
6111
|
+
}
|
|
6112
|
+
if (newRulePaths.length > 0) {
|
|
6113
|
+
config.instructions = config.instructions || [];
|
|
6114
|
+
for (const rulePath of newRulePaths) {
|
|
6115
|
+
if (!config.instructions.includes(rulePath)) {
|
|
6116
|
+
config.instructions.push(rulePath);
|
|
6117
|
+
updated = true;
|
|
6118
|
+
}
|
|
6036
6119
|
}
|
|
6037
6120
|
}
|
|
6038
6121
|
if (updated)
|
|
6039
6122
|
writeFileSync(configPath, JSON.stringify(config, null, 2), "utf-8");
|
|
6040
6123
|
} catch (e2) {
|
|
6041
|
-
|
|
6042
|
-
${YELLOW}Warning: Failed to inject tools into opencode.jsonc. ${e2.message}${RESET}`);
|
|
6124
|
+
handleExecError(e2, "Failed to update opencode.jsonc", "warn" /* WARN */);
|
|
6043
6125
|
}
|
|
6044
6126
|
}
|
|
6045
|
-
function
|
|
6046
|
-
if (
|
|
6127
|
+
function removeOpencodeConfig(toolsToRemove, rulesToRemove) {
|
|
6128
|
+
if (toolsToRemove.length === 0 && rulesToRemove.length === 0)
|
|
6047
6129
|
return;
|
|
6048
6130
|
const configPath = path.join(process.cwd(), OPENCODE_DIR, CONFIG_FILE);
|
|
6049
6131
|
if (!existsSync(configPath))
|
|
6050
6132
|
return;
|
|
6051
6133
|
try {
|
|
6052
6134
|
const content = readFileSync(configPath, "utf-8");
|
|
6053
|
-
|
|
6054
|
-
safeJsonStr = safeJsonStr.replace(/,\s*([\]}])/g, "$1");
|
|
6055
|
-
const config = JSON.parse(safeJsonStr);
|
|
6056
|
-
config.instructions = config.instructions || [];
|
|
6135
|
+
const config = parseJsonc(content);
|
|
6057
6136
|
let updated = false;
|
|
6058
|
-
|
|
6059
|
-
|
|
6060
|
-
config.
|
|
6137
|
+
if (toolsToRemove.length > 0 && config.tools) {
|
|
6138
|
+
for (const tool of toolsToRemove) {
|
|
6139
|
+
if (tool in config.tools) {
|
|
6140
|
+
delete config.tools[tool];
|
|
6141
|
+
updated = true;
|
|
6142
|
+
}
|
|
6143
|
+
}
|
|
6144
|
+
}
|
|
6145
|
+
if (rulesToRemove.length > 0 && config.instructions) {
|
|
6146
|
+
const originalLength = config.instructions.length;
|
|
6147
|
+
config.instructions = config.instructions.filter((inst) => {
|
|
6148
|
+
return !rulesToRemove.some((rule) => inst.includes(`/${RULES_SUBDIR}/${rule}/`));
|
|
6149
|
+
});
|
|
6150
|
+
if (config.instructions.length !== originalLength) {
|
|
6061
6151
|
updated = true;
|
|
6062
6152
|
}
|
|
6063
6153
|
}
|
|
6064
6154
|
if (updated)
|
|
6065
6155
|
writeFileSync(configPath, JSON.stringify(config, null, 2), "utf-8");
|
|
6066
6156
|
} catch (e2) {
|
|
6067
|
-
|
|
6068
|
-
${YELLOW}Warning: Failed to inject instructions into opencode.jsonc. ${e2.message}${RESET}`);
|
|
6157
|
+
handleExecError(e2, "Failed to remove items from opencode.jsonc", "warn" /* WARN */);
|
|
6069
6158
|
}
|
|
6070
6159
|
}
|
|
6160
|
+
|
|
6161
|
+
// src/utils/file.ts
|
|
6162
|
+
import { existsSync as existsSync2, mkdirSync as mkdirSync2, cpSync, readdirSync, rmSync } from "fs";
|
|
6163
|
+
import path2 from "path";
|
|
6071
6164
|
function copyToolFiles(toolName, sourceDir, targetDir) {
|
|
6072
6165
|
let hasPython = false;
|
|
6073
6166
|
const tsFile = `${toolName}.ts`;
|
|
6074
6167
|
const pyFile = `${toolName}.py`;
|
|
6075
|
-
const srcTs =
|
|
6076
|
-
if (
|
|
6077
|
-
cpSync(srcTs,
|
|
6078
|
-
const srcPy =
|
|
6079
|
-
if (
|
|
6080
|
-
cpSync(srcPy,
|
|
6168
|
+
const srcTs = path2.join(sourceDir, tsFile);
|
|
6169
|
+
if (existsSync2(srcTs))
|
|
6170
|
+
cpSync(srcTs, path2.join(targetDir, tsFile), { recursive: true });
|
|
6171
|
+
const srcPy = path2.join(sourceDir, pyFile);
|
|
6172
|
+
if (existsSync2(srcPy)) {
|
|
6173
|
+
cpSync(srcPy, path2.join(targetDir, pyFile), { recursive: true });
|
|
6081
6174
|
hasPython = true;
|
|
6082
6175
|
}
|
|
6083
6176
|
return hasPython;
|
|
6084
6177
|
}
|
|
6085
6178
|
function installRules(categories, rulesSourceDir, targetRulesDir) {
|
|
6086
|
-
if (!
|
|
6087
|
-
|
|
6179
|
+
if (!existsSync2(targetRulesDir))
|
|
6180
|
+
mkdirSync2(targetRulesDir, { recursive: true });
|
|
6088
6181
|
const installedRulePaths = [];
|
|
6089
|
-
const
|
|
6090
|
-
|
|
6091
|
-
|
|
6092
|
-
|
|
6093
|
-
|
|
6094
|
-
const files = readdirSync(commonSource).filter((f) => f.endsWith(".md"));
|
|
6095
|
-
for (const file of files) {
|
|
6096
|
-
cpSync(path.join(commonSource, file), path.join(commonTarget, file));
|
|
6097
|
-
installedRulePaths.push(`./${RULES_SUBDIR}/common/${file}`);
|
|
6098
|
-
}
|
|
6099
|
-
}
|
|
6100
|
-
for (const category of categories) {
|
|
6101
|
-
const catSource = path.join(rulesSourceDir, category);
|
|
6102
|
-
const catTarget = path.join(targetRulesDir, category);
|
|
6103
|
-
if (existsSync(catSource)) {
|
|
6104
|
-
if (!existsSync(catTarget))
|
|
6105
|
-
mkdirSync(catTarget, { recursive: true });
|
|
6106
|
-
const files = readdirSync(catSource).filter((f) => f.endsWith(".md"));
|
|
6182
|
+
const copyDir = (srcFolder, targetFolder, relativePathPrefix) => {
|
|
6183
|
+
if (existsSync2(srcFolder)) {
|
|
6184
|
+
if (!existsSync2(targetFolder))
|
|
6185
|
+
mkdirSync2(targetFolder, { recursive: true });
|
|
6186
|
+
const files = readdirSync(srcFolder).filter((f) => f.endsWith(".md"));
|
|
6107
6187
|
for (const file of files) {
|
|
6108
|
-
cpSync(
|
|
6109
|
-
installedRulePaths.push(`./${RULES_SUBDIR}/${
|
|
6188
|
+
cpSync(path2.join(srcFolder, file), path2.join(targetFolder, file));
|
|
6189
|
+
installedRulePaths.push(`./${RULES_SUBDIR}/${relativePathPrefix}/${file}`);
|
|
6110
6190
|
}
|
|
6111
6191
|
}
|
|
6192
|
+
};
|
|
6193
|
+
copyDir(path2.join(rulesSourceDir, "common"), path2.join(targetRulesDir, "common"), "common");
|
|
6194
|
+
for (const category of categories) {
|
|
6195
|
+
copyDir(path2.join(rulesSourceDir, category), path2.join(targetRulesDir, category), category);
|
|
6112
6196
|
}
|
|
6113
6197
|
return installedRulePaths;
|
|
6114
6198
|
}
|
|
6199
|
+
function removeToolFiles(toolName, targetDir) {
|
|
6200
|
+
const tsFile = path2.join(targetDir, `${toolName}.ts`);
|
|
6201
|
+
const pyFile = path2.join(targetDir, `${toolName}.py`);
|
|
6202
|
+
if (existsSync2(tsFile))
|
|
6203
|
+
rmSync(tsFile, { force: true });
|
|
6204
|
+
if (existsSync2(pyFile))
|
|
6205
|
+
rmSync(pyFile, { force: true });
|
|
6206
|
+
}
|
|
6207
|
+
function removeRuleCategory(category, targetRulesDir) {
|
|
6208
|
+
const catDir = path2.join(targetRulesDir, category);
|
|
6209
|
+
if (existsSync2(catDir)) {
|
|
6210
|
+
rmSync(catDir, { recursive: true, force: true });
|
|
6211
|
+
}
|
|
6212
|
+
}
|
|
6213
|
+
|
|
6214
|
+
// src/utils/python.ts
|
|
6215
|
+
import { execSync } from "child_process";
|
|
6216
|
+
import { existsSync as existsSync3, writeFileSync as writeFileSync2, readFileSync as readFileSync2 } from "fs";
|
|
6217
|
+
import path3 from "path";
|
|
6115
6218
|
function setupPythonEnvironment(rootDir, spinner) {
|
|
6116
6219
|
spinner.message(`Initializing Python environment in ./.venv ...`);
|
|
6117
6220
|
try {
|
|
6118
|
-
const reqPath =
|
|
6221
|
+
const reqPath = path3.join(rootDir, "requirements.txt");
|
|
6119
6222
|
const reqContent = `# 核心依赖
|
|
6120
6223
|
requests>=2.28.0
|
|
6121
6224
|
urllib3>=1.26.0
|
|
6122
6225
|
python-dotenv>=0.19.0
|
|
6123
6226
|
`;
|
|
6124
|
-
if (!
|
|
6125
|
-
|
|
6227
|
+
if (!existsSync3(reqPath)) {
|
|
6228
|
+
writeFileSync2(reqPath, reqContent, "utf-8");
|
|
6126
6229
|
} else {
|
|
6127
|
-
const existingReq =
|
|
6230
|
+
const existingReq = readFileSync2(reqPath, "utf-8");
|
|
6128
6231
|
if (!existingReq.includes("requests>="))
|
|
6129
|
-
|
|
6232
|
+
writeFileSync2(reqPath, existingReq + `
|
|
6130
6233
|
` + reqContent, "utf-8");
|
|
6131
6234
|
}
|
|
6132
|
-
const venvPath =
|
|
6133
|
-
if (!
|
|
6235
|
+
const venvPath = path3.join(rootDir, ".venv");
|
|
6236
|
+
if (!existsSync3(venvPath)) {
|
|
6134
6237
|
try {
|
|
6135
6238
|
execSync(`python3 -m venv "${venvPath}"`, { stdio: "ignore" });
|
|
6136
6239
|
} catch {
|
|
6137
6240
|
execSync(`python -m venv "${venvPath}"`, { stdio: "ignore" });
|
|
6138
6241
|
}
|
|
6139
6242
|
}
|
|
6140
|
-
const pipCmd = process.platform === "win32" ?
|
|
6243
|
+
const pipCmd = process.platform === "win32" ? path3.join(venvPath, "Scripts", "pip") : path3.join(venvPath, "bin", "pip");
|
|
6141
6244
|
spinner.message(`Installing Python dependencies...`);
|
|
6142
6245
|
execSync(`"${pipCmd}" install -r "${reqPath}"`, { stdio: "ignore" });
|
|
6143
6246
|
} catch (pyError) {
|
|
6144
|
-
|
|
6247
|
+
spinner.stop();
|
|
6248
|
+
handleExecError(pyError, "Failed to initialize Python environment. Manual setup required.", "warn" /* WARN */);
|
|
6145
6249
|
}
|
|
6146
6250
|
}
|
|
6251
|
+
function getPythonActivationCmd() {
|
|
6252
|
+
const isWin = process.platform === "win32";
|
|
6253
|
+
return isWin ? ".\\.venv\\Scripts\\Activate.ps1" : "source .venv/bin/activate";
|
|
6254
|
+
}
|
|
6255
|
+
|
|
6256
|
+
// src/commands/add.ts
|
|
6147
6257
|
async function runAdd(args) {
|
|
6148
6258
|
const repository = args[0];
|
|
6149
6259
|
if (!repository) {
|
|
6150
|
-
|
|
6260
|
+
handleExecError(new Error("Repository name is missing"), "Argument Error", "error" /* ERROR */);
|
|
6151
6261
|
process.exit(1);
|
|
6152
6262
|
}
|
|
6153
6263
|
const repoUrl = `https://github.com/${repository}.git`;
|
|
@@ -6156,9 +6266,9 @@ async function runAdd(args) {
|
|
|
6156
6266
|
Target: ${CYAN}${OPENCODE_DIR}${RESET}`, "Initializing");
|
|
6157
6267
|
R2.step("Executing standard skills installer (pnpx skills add)...");
|
|
6158
6268
|
try {
|
|
6159
|
-
|
|
6160
|
-
} catch {
|
|
6161
|
-
|
|
6269
|
+
execSync2(`pnpx skills add ${repository} --agent opencode`, { stdio: "inherit" });
|
|
6270
|
+
} catch (err) {
|
|
6271
|
+
handleExecError(err, "Skills installer finished with warnings", "warn" /* WARN */);
|
|
6162
6272
|
}
|
|
6163
6273
|
const s = bt2();
|
|
6164
6274
|
s.start("Fetching remote repository...");
|
|
@@ -6166,89 +6276,85 @@ Target: ${CYAN}${OPENCODE_DIR}${RESET}`, "Initializing");
|
|
|
6166
6276
|
try {
|
|
6167
6277
|
tempDir = await cloneRepo(repoUrl);
|
|
6168
6278
|
s.stop("Remote repository parsed.");
|
|
6169
|
-
const toolDirPath =
|
|
6170
|
-
const rulesDirPath =
|
|
6171
|
-
const hasTools =
|
|
6172
|
-
const hasRules =
|
|
6173
|
-
if (!hasTools && !hasRules)
|
|
6174
|
-
return
|
|
6175
|
-
}
|
|
6279
|
+
const toolDirPath = path4.join(tempDir, "tool");
|
|
6280
|
+
const rulesDirPath = path4.join(tempDir, "rules");
|
|
6281
|
+
const hasTools = existsSync4(toolDirPath);
|
|
6282
|
+
const hasRules = existsSync4(rulesDirPath);
|
|
6283
|
+
if (!hasTools && !hasRules)
|
|
6284
|
+
return handleExecError(new Error(`Neither "tool' nor "rules' directory found in repository.`), "warn" /* WARN */);
|
|
6176
6285
|
let selectedTools = [];
|
|
6177
6286
|
let selectedRules = [];
|
|
6178
6287
|
if (hasTools) {
|
|
6179
|
-
const
|
|
6180
|
-
if (
|
|
6181
|
-
const
|
|
6288
|
+
const opts = readdirSync2(toolDirPath).filter((f) => f.endsWith(".ts")).map((f) => f.replace(/\.ts$/, ""));
|
|
6289
|
+
if (opts.length > 0) {
|
|
6290
|
+
const res = await je({
|
|
6182
6291
|
message: "Select tools to install (space to toggle)",
|
|
6183
|
-
options:
|
|
6292
|
+
options: opts.map((t) => ({ value: t, label: t })),
|
|
6184
6293
|
required: false
|
|
6185
6294
|
});
|
|
6186
|
-
if (Ct(
|
|
6295
|
+
if (Ct(res))
|
|
6187
6296
|
return Ne("Installation cancelled.");
|
|
6188
|
-
if (Array.isArray(
|
|
6189
|
-
selectedTools =
|
|
6297
|
+
if (Array.isArray(res))
|
|
6298
|
+
selectedTools = res;
|
|
6190
6299
|
}
|
|
6191
6300
|
}
|
|
6192
6301
|
if (hasRules) {
|
|
6193
|
-
const
|
|
6194
|
-
if (
|
|
6195
|
-
const
|
|
6196
|
-
message: "Select rule categories to install
|
|
6197
|
-
options:
|
|
6302
|
+
const opts = readdirSync2(rulesDirPath, { withFileTypes: true }).filter((d2) => d2.isDirectory() && d2.name !== "common").map((d2) => d2.name);
|
|
6303
|
+
if (opts.length > 0) {
|
|
6304
|
+
const res = await je({
|
|
6305
|
+
message: "Select rule categories to install",
|
|
6306
|
+
options: opts.map((r) => ({ value: r, label: r })),
|
|
6198
6307
|
required: false
|
|
6199
6308
|
});
|
|
6200
|
-
if (Ct(
|
|
6309
|
+
if (Ct(res))
|
|
6201
6310
|
return Ne("Installation cancelled.");
|
|
6202
|
-
if (Array.isArray(
|
|
6203
|
-
selectedRules =
|
|
6311
|
+
if (Array.isArray(res))
|
|
6312
|
+
selectedRules = res;
|
|
6204
6313
|
}
|
|
6205
6314
|
}
|
|
6206
|
-
if (selectedTools.length === 0 && selectedRules.length === 0)
|
|
6207
|
-
Ne("No tools or rules selected.");
|
|
6208
|
-
return;
|
|
6209
|
-
}
|
|
6315
|
+
if (selectedTools.length === 0 && selectedRules.length === 0)
|
|
6316
|
+
return Ne("No tools or rules selected.");
|
|
6210
6317
|
const installSpinner = bt2();
|
|
6211
6318
|
installSpinner.start(`Installing to ${OPENCODE_DIR}/ ...`);
|
|
6319
|
+
ensureOpencodeConfig();
|
|
6212
6320
|
const lockData = readLockFile();
|
|
6213
6321
|
const now = new Date().toISOString();
|
|
6214
6322
|
let requiresPython = false;
|
|
6215
|
-
|
|
6323
|
+
let installedRulePaths = [];
|
|
6216
6324
|
if (selectedTools.length > 0) {
|
|
6217
|
-
const targetToolDir =
|
|
6218
|
-
if (!
|
|
6219
|
-
|
|
6325
|
+
const targetToolDir = path4.join(process.cwd(), OPENCODE_DIR, TOOL_SUBDIR);
|
|
6326
|
+
if (!existsSync4(targetToolDir))
|
|
6327
|
+
mkdirSync3(targetToolDir, { recursive: true });
|
|
6220
6328
|
for (const tool of selectedTools) {
|
|
6221
|
-
|
|
6222
|
-
if (hasPy)
|
|
6329
|
+
if (copyToolFiles(tool, toolDirPath, targetToolDir))
|
|
6223
6330
|
requiresPython = true;
|
|
6224
6331
|
if (lockData.tools)
|
|
6225
6332
|
lockData.tools[tool] = { source: repoUrl, installedAt: now };
|
|
6226
6333
|
}
|
|
6227
|
-
updateOpencodeConfigTools(selectedTools);
|
|
6228
6334
|
}
|
|
6229
6335
|
if (selectedRules.length > 0) {
|
|
6230
|
-
const targetRulesDir =
|
|
6231
|
-
|
|
6336
|
+
const targetRulesDir = path4.join(process.cwd(), OPENCODE_DIR, RULES_SUBDIR);
|
|
6337
|
+
installedRulePaths = installRules(selectedRules, rulesDirPath, targetRulesDir);
|
|
6232
6338
|
for (const rule of selectedRules) {
|
|
6233
6339
|
if (!lockData.rules)
|
|
6234
6340
|
lockData.rules = {};
|
|
6235
6341
|
lockData.rules[rule] = { source: repoUrl, installedAt: now };
|
|
6236
6342
|
}
|
|
6237
|
-
updateOpencodeConfigInstructions(installedRulePaths);
|
|
6238
6343
|
}
|
|
6344
|
+
updateOpencodeConfig(selectedTools, installedRulePaths);
|
|
6239
6345
|
writeLockFile(lockData);
|
|
6240
|
-
if (requiresPython)
|
|
6346
|
+
if (requiresPython)
|
|
6241
6347
|
setupPythonEnvironment(process.cwd(), installSpinner);
|
|
6348
|
+
await new Promise((r) => setTimeout(r, 400));
|
|
6349
|
+
installSpinner.stop(`${GREEN}Successfully installed ${selectedTools.length + selectedRules.length} items.${RESET}`);
|
|
6350
|
+
if (requiresPython) {
|
|
6351
|
+
Ve(`Your Python tools are ready. Run:
|
|
6352
|
+
|
|
6353
|
+
${CYAN}${getPythonActivationCmd()}${RESET}`, "\uD83D\uDC0D Python Environment");
|
|
6242
6354
|
}
|
|
6243
|
-
const totalInstalled = selectedTools.length + selectedRules.length;
|
|
6244
|
-
installSpinner.stop(`${GREEN}Successfully installed ${totalInstalled} items.${RESET}`);
|
|
6245
6355
|
} catch (error) {
|
|
6246
6356
|
s.stop("Failed to fetch repository.");
|
|
6247
|
-
|
|
6248
|
-
R2.error(`${YELLOW}Git Error:${RESET}
|
|
6249
|
-
${error.message}`);
|
|
6250
|
-
else
|
|
6251
|
-
R2.error(`Error: ${error.message}`);
|
|
6357
|
+
handleExecError(error, "Repository Fetch Error", "error" /* ERROR */);
|
|
6252
6358
|
} finally {
|
|
6253
6359
|
if (tempDir)
|
|
6254
6360
|
await cleanupTempDir(tempDir).catch(() => {});
|
|
@@ -6256,57 +6362,48 @@ ${error.message}`);
|
|
|
6256
6362
|
Le(`✨ Workspace updated for ${CYAN}${OPENCODE_DIR}${RESET}`);
|
|
6257
6363
|
}
|
|
6258
6364
|
|
|
6259
|
-
// src/list.ts
|
|
6260
|
-
import { execSync as
|
|
6365
|
+
// src/commands/list.ts
|
|
6366
|
+
import { execSync as execSync3 } from "child_process";
|
|
6261
6367
|
async function runList(args) {
|
|
6262
6368
|
const lockData = readLockFile();
|
|
6263
6369
|
console.log(`
|
|
6264
6370
|
${BOLD}\uD83D\uDEE0️ Installed Tools (${OPENCODE_DIR}/${TOOL_SUBDIR}):${RESET}
|
|
6265
6371
|
`);
|
|
6266
6372
|
const tools = Object.keys(lockData.tools || {});
|
|
6267
|
-
if (tools.length === 0)
|
|
6373
|
+
if (tools.length === 0)
|
|
6268
6374
|
console.log(` ${DIM}No tools installed yet.${RESET}`);
|
|
6269
|
-
|
|
6270
|
-
tools.forEach((t) => {
|
|
6271
|
-
const source = lockData.tools[t]?.source || "unknown";
|
|
6272
|
-
console.log(` ${CYAN}◆${RESET} ${t} ${DIM}(${source})${RESET}`);
|
|
6273
|
-
});
|
|
6274
|
-
}
|
|
6375
|
+
else
|
|
6376
|
+
tools.forEach((t) => console.log(` ${CYAN}◆${RESET} ${t} ${DIM}(${lockData.tools[t]?.source || "unknown"})${RESET}`));
|
|
6275
6377
|
console.log(`
|
|
6276
6378
|
${BOLD}\uD83D\uDCDC Installed Rules (${OPENCODE_DIR}/${RULES_SUBDIR}):${RESET}
|
|
6277
6379
|
`);
|
|
6278
6380
|
const rules = Object.keys(lockData.rules || {});
|
|
6279
|
-
if (rules.length === 0)
|
|
6381
|
+
if (rules.length === 0)
|
|
6280
6382
|
console.log(` ${DIM}No rules installed yet.${RESET}`);
|
|
6281
|
-
|
|
6282
|
-
rules.forEach((r) => {
|
|
6283
|
-
const source = lockData.rules[r]?.source || "unknown";
|
|
6284
|
-
console.log(` ${CYAN}◆${RESET} ${r} ${DIM}(${source})${RESET}`);
|
|
6285
|
-
});
|
|
6286
|
-
}
|
|
6383
|
+
else
|
|
6384
|
+
rules.forEach((r) => console.log(` ${CYAN}◆${RESET} ${r} ${DIM}(${lockData.rules[r]?.source || "unknown"})${RESET}`));
|
|
6287
6385
|
console.log(`
|
|
6288
6386
|
${BOLD}\uD83E\uDE84 Installed Skills (Standard):${RESET}
|
|
6289
6387
|
`);
|
|
6290
6388
|
try {
|
|
6291
|
-
|
|
6389
|
+
execSync3("pnpx skills ls", { stdio: "inherit" });
|
|
6292
6390
|
} catch (error) {
|
|
6293
|
-
|
|
6391
|
+
handleExecError(error, "No standard skills found or failed to fetch", "warn" /* WARN */);
|
|
6294
6392
|
}
|
|
6295
|
-
console.log();
|
|
6296
6393
|
}
|
|
6297
6394
|
|
|
6298
|
-
// src/update.ts
|
|
6299
|
-
import { execSync as
|
|
6300
|
-
import { existsSync as
|
|
6301
|
-
import
|
|
6395
|
+
// src/commands/update.ts
|
|
6396
|
+
import { execSync as execSync4 } from "child_process";
|
|
6397
|
+
import { existsSync as existsSync5 } from "fs";
|
|
6398
|
+
import path5 from "path";
|
|
6302
6399
|
async function runUpdate(args) {
|
|
6303
6400
|
console.log(`
|
|
6304
6401
|
${BOLD}\uD83E\uDE84 Updating Standard Skills...${RESET}
|
|
6305
6402
|
`);
|
|
6306
6403
|
try {
|
|
6307
|
-
|
|
6404
|
+
execSync4("pnpx skills update", { stdio: "inherit" });
|
|
6308
6405
|
} catch (error) {
|
|
6309
|
-
|
|
6406
|
+
handleExecError(error, "Failed to update standard skills", "warn" /* WARN */);
|
|
6310
6407
|
}
|
|
6311
6408
|
console.log(`
|
|
6312
6409
|
${BOLD}\uD83D\uDCE6 Updating Local Tools & Rules...${RESET}
|
|
@@ -6315,73 +6412,174 @@ ${BOLD}\uD83D\uDCE6 Updating Local Tools & Rules...${RESET}
|
|
|
6315
6412
|
const toolNames = Object.keys(lockData.tools || {});
|
|
6316
6413
|
const ruleNames = Object.keys(lockData.rules || {});
|
|
6317
6414
|
if (toolNames.length === 0 && ruleNames.length === 0) {
|
|
6318
|
-
console.log(` ${DIM}No local
|
|
6415
|
+
return console.log(` ${DIM}No local items to update.${RESET}
|
|
6319
6416
|
`);
|
|
6320
|
-
return;
|
|
6321
6417
|
}
|
|
6322
6418
|
const itemsBySource = {};
|
|
6323
6419
|
toolNames.forEach((t) => {
|
|
6324
|
-
const
|
|
6325
|
-
if (
|
|
6326
|
-
itemsBySource[
|
|
6327
|
-
itemsBySource[
|
|
6420
|
+
const s2 = lockData.tools[t]?.source;
|
|
6421
|
+
if (s2) {
|
|
6422
|
+
itemsBySource[s2] = itemsBySource[s2] || { tools: [], rules: [] };
|
|
6423
|
+
itemsBySource[s2].tools.push(t);
|
|
6328
6424
|
}
|
|
6329
6425
|
});
|
|
6330
6426
|
ruleNames.forEach((r) => {
|
|
6331
|
-
const
|
|
6332
|
-
if (
|
|
6333
|
-
itemsBySource[
|
|
6334
|
-
itemsBySource[
|
|
6427
|
+
const s2 = lockData.rules[r]?.source;
|
|
6428
|
+
if (s2) {
|
|
6429
|
+
itemsBySource[s2] = itemsBySource[s2] || { tools: [], rules: [] };
|
|
6430
|
+
itemsBySource[s2].rules.push(r);
|
|
6335
6431
|
}
|
|
6336
6432
|
});
|
|
6337
|
-
const targetToolDir =
|
|
6338
|
-
const targetRulesDir =
|
|
6433
|
+
const targetToolDir = path5.join(process.cwd(), OPENCODE_DIR, TOOL_SUBDIR);
|
|
6434
|
+
const targetRulesDir = path5.join(process.cwd(), OPENCODE_DIR, RULES_SUBDIR);
|
|
6339
6435
|
const now = new Date().toISOString();
|
|
6340
|
-
|
|
6341
|
-
|
|
6342
|
-
|
|
6343
|
-
|
|
6436
|
+
const sourcesCount = Object.keys(itemsBySource).length;
|
|
6437
|
+
const s = bt2();
|
|
6438
|
+
s.start(`Fetching from ${CYAN}${sourcesCount}${RESET} source(s) concurrently...`);
|
|
6439
|
+
const updatePromises = Object.entries(itemsBySource).map(async ([source, items]) => {
|
|
6344
6440
|
let tempDir = null;
|
|
6441
|
+
let successCount = 0;
|
|
6442
|
+
const logs = [];
|
|
6345
6443
|
try {
|
|
6346
6444
|
tempDir = await cloneRepo(source);
|
|
6347
|
-
if (items.tools.length > 0) {
|
|
6348
|
-
const
|
|
6349
|
-
|
|
6350
|
-
|
|
6351
|
-
|
|
6352
|
-
|
|
6353
|
-
|
|
6354
|
-
successCount++;
|
|
6355
|
-
console.log(` ${GREEN}✓${RESET} Updated tool: ${tool}`);
|
|
6356
|
-
}
|
|
6445
|
+
if (items.tools.length > 0 && existsSync5(path5.join(tempDir, "tool"))) {
|
|
6446
|
+
for (const tool of items.tools) {
|
|
6447
|
+
copyToolFiles(tool, path5.join(tempDir, "tool"), targetToolDir);
|
|
6448
|
+
if (lockData.tools[tool])
|
|
6449
|
+
lockData.tools[tool].installedAt = now;
|
|
6450
|
+
successCount++;
|
|
6451
|
+
logs.push(` ${GREEN}✓${RESET} Updated tool: ${tool}`);
|
|
6357
6452
|
}
|
|
6358
6453
|
}
|
|
6359
|
-
if (items.rules.length > 0) {
|
|
6360
|
-
|
|
6361
|
-
|
|
6362
|
-
|
|
6363
|
-
|
|
6364
|
-
|
|
6365
|
-
|
|
6366
|
-
successCount++;
|
|
6367
|
-
console.log(` ${GREEN}✓${RESET} Updated rule: ${rule}`);
|
|
6368
|
-
}
|
|
6454
|
+
if (items.rules.length > 0 && existsSync5(path5.join(tempDir, "rules"))) {
|
|
6455
|
+
installRules(items.rules, path5.join(tempDir, "rules"), targetRulesDir);
|
|
6456
|
+
for (const rule of items.rules) {
|
|
6457
|
+
if (lockData.rules[rule])
|
|
6458
|
+
lockData.rules[rule].installedAt = now;
|
|
6459
|
+
successCount++;
|
|
6460
|
+
logs.push(` ${GREEN}✓${RESET} Updated rule: ${rule}`);
|
|
6369
6461
|
}
|
|
6370
6462
|
}
|
|
6371
|
-
|
|
6463
|
+
return { source, success: true, count: successCount, logs };
|
|
6372
6464
|
} catch (err) {
|
|
6373
|
-
|
|
6465
|
+
return { source, success: false, count: 0, error: err, logs: [] };
|
|
6374
6466
|
} finally {
|
|
6375
6467
|
if (tempDir)
|
|
6376
6468
|
await cleanupTempDir(tempDir).catch(() => {});
|
|
6377
6469
|
}
|
|
6470
|
+
});
|
|
6471
|
+
const results = await Promise.allSettled(updatePromises);
|
|
6472
|
+
s.stop(`Finished fetching from ${sourcesCount} source(s).`);
|
|
6473
|
+
let totalSuccessCount = 0;
|
|
6474
|
+
for (const result of results) {
|
|
6475
|
+
if (result.status === "fulfilled") {
|
|
6476
|
+
const { source, success, count, logs, error } = result.value;
|
|
6477
|
+
if (success) {
|
|
6478
|
+
totalSuccessCount += count;
|
|
6479
|
+
logs.forEach((log) => console.log(log));
|
|
6480
|
+
} else {
|
|
6481
|
+
handleExecError(error, `Failed to fetch from ${source}`, "warn" /* WARN */);
|
|
6482
|
+
}
|
|
6483
|
+
} else {
|
|
6484
|
+
handleExecError(result.reason, "Unexpected error during concurrent update", "error" /* ERROR */);
|
|
6485
|
+
}
|
|
6378
6486
|
}
|
|
6379
6487
|
writeLockFile(lockData);
|
|
6380
|
-
|
|
6381
|
-
|
|
6382
|
-
|
|
6488
|
+
if (totalSuccessCount > 0) {
|
|
6489
|
+
console.log(`${TEXT}✓ Successfully updated ${totalSuccessCount} local item(s)${RESET}
|
|
6490
|
+
`);
|
|
6383
6491
|
}
|
|
6384
|
-
|
|
6492
|
+
}
|
|
6493
|
+
|
|
6494
|
+
// src/commands/remove.ts
|
|
6495
|
+
import { execSync as execSync5 } from "child_process";
|
|
6496
|
+
import path6 from "path";
|
|
6497
|
+
async function runRemove(args) {
|
|
6498
|
+
We(`${BG_CYAN} vibe cli ${RESET}`);
|
|
6499
|
+
R2.step("Executing standard skills remover (pnpx skills remove)...");
|
|
6500
|
+
try {
|
|
6501
|
+
const cmdArgs = args.length > 0 ? args.join(" ") : "";
|
|
6502
|
+
execSync5(`pnpx skills remove ${cmdArgs}`.trim(), { stdio: "inherit" });
|
|
6503
|
+
} catch (err) {
|
|
6504
|
+
R2.warn("Skills remover finished with warnings or nothing to remove.");
|
|
6505
|
+
}
|
|
6506
|
+
const lockData = readLockFile();
|
|
6507
|
+
const installedTools = Object.keys(lockData.tools || {});
|
|
6508
|
+
const installedRules = Object.keys(lockData.rules || {});
|
|
6509
|
+
if (installedTools.length === 0 && installedRules.length === 0) {
|
|
6510
|
+
R2.info(`No local tools or rules found in ${OPENCODE_DIR}.`);
|
|
6511
|
+
return Le(`✨ Removal process completed.`);
|
|
6512
|
+
}
|
|
6513
|
+
let toolsToRemove = [];
|
|
6514
|
+
let rulesToRemove = [];
|
|
6515
|
+
if (args.length > 0) {
|
|
6516
|
+
for (const arg of args) {
|
|
6517
|
+
if (installedTools.includes(arg))
|
|
6518
|
+
toolsToRemove.push(arg);
|
|
6519
|
+
else if (installedRules.includes(arg))
|
|
6520
|
+
rulesToRemove.push(arg);
|
|
6521
|
+
else {
|
|
6522
|
+
R2.info(`Local item '${arg}' not found, skipping local cleanup.`);
|
|
6523
|
+
}
|
|
6524
|
+
}
|
|
6525
|
+
} else {
|
|
6526
|
+
if (installedTools.length > 0) {
|
|
6527
|
+
const res = await je({
|
|
6528
|
+
message: "Select local tools to remove (space to toggle)",
|
|
6529
|
+
options: installedTools.map((t) => ({ value: t, label: t })),
|
|
6530
|
+
required: false
|
|
6531
|
+
});
|
|
6532
|
+
if (Ct(res))
|
|
6533
|
+
return Ne("Operation cancelled.");
|
|
6534
|
+
if (Array.isArray(res))
|
|
6535
|
+
toolsToRemove = res;
|
|
6536
|
+
}
|
|
6537
|
+
if (installedRules.length > 0) {
|
|
6538
|
+
const res = await je({
|
|
6539
|
+
message: "Select rule categories to remove (space to toggle)",
|
|
6540
|
+
options: installedRules.map((r) => ({ value: r, label: r })),
|
|
6541
|
+
required: false
|
|
6542
|
+
});
|
|
6543
|
+
if (Ct(res))
|
|
6544
|
+
return Ne("Operation cancelled.");
|
|
6545
|
+
if (Array.isArray(res))
|
|
6546
|
+
rulesToRemove = res;
|
|
6547
|
+
}
|
|
6548
|
+
}
|
|
6549
|
+
if (toolsToRemove.length === 0 && rulesToRemove.length === 0) {
|
|
6550
|
+
if (args.length === 0)
|
|
6551
|
+
Ne("No local items selected for removal.");
|
|
6552
|
+
else
|
|
6553
|
+
Le(`✨ Removal process completed.`);
|
|
6554
|
+
return;
|
|
6555
|
+
}
|
|
6556
|
+
const confirm = await Re({
|
|
6557
|
+
message: `Are you sure you want to completely remove ${toolsToRemove.length} local tool(s) and ${rulesToRemove.length} local rule(s)?`
|
|
6558
|
+
});
|
|
6559
|
+
if (Ct(confirm) || !confirm) {
|
|
6560
|
+
return Ne("Operation cancelled.");
|
|
6561
|
+
}
|
|
6562
|
+
const s = bt2();
|
|
6563
|
+
s.start(`Cleaning up local workspace...`);
|
|
6564
|
+
try {
|
|
6565
|
+
const targetToolDir = path6.join(process.cwd(), OPENCODE_DIR, TOOL_SUBDIR);
|
|
6566
|
+
const targetRulesDir = path6.join(process.cwd(), OPENCODE_DIR, RULES_SUBDIR);
|
|
6567
|
+
for (const tool of toolsToRemove) {
|
|
6568
|
+
removeToolFiles(tool, targetToolDir);
|
|
6569
|
+
delete lockData.tools[tool];
|
|
6570
|
+
}
|
|
6571
|
+
for (const rule of rulesToRemove) {
|
|
6572
|
+
removeRuleCategory(rule, targetRulesDir);
|
|
6573
|
+
delete lockData.rules[rule];
|
|
6574
|
+
}
|
|
6575
|
+
removeOpencodeConfig(toolsToRemove, rulesToRemove);
|
|
6576
|
+
writeLockFile(lockData);
|
|
6577
|
+
s.stop(`${GREEN}Successfully removed selected local items.${RESET}`);
|
|
6578
|
+
} catch (e2) {
|
|
6579
|
+
s.stop("Failed to complete local removal.");
|
|
6580
|
+
handleExecError(e2, "Removal Error", "error" /* ERROR */);
|
|
6581
|
+
}
|
|
6582
|
+
Le(`✨ Workspace cleaned for ${CYAN}${OPENCODE_DIR}${RESET}`);
|
|
6385
6583
|
}
|
|
6386
6584
|
|
|
6387
6585
|
// src/cli.ts
|
|
@@ -6393,9 +6591,20 @@ var VIBE_LOGO = [
|
|
|
6393
6591
|
" ╚████╔╝ ██║██████╔╝███████╗",
|
|
6394
6592
|
" ╚═══╝ ╚═╝╚═════╝ ╚══════╝"
|
|
6395
6593
|
];
|
|
6594
|
+
var GRAYS = [
|
|
6595
|
+
"\x1B[38;5;250m",
|
|
6596
|
+
"\x1B[38;5;248m",
|
|
6597
|
+
"\x1B[38;5;245m",
|
|
6598
|
+
"\x1B[38;5;243m",
|
|
6599
|
+
"\x1B[38;5;240m",
|
|
6600
|
+
"\x1B[38;5;238m"
|
|
6601
|
+
];
|
|
6396
6602
|
function showLogo() {
|
|
6397
6603
|
console.log();
|
|
6398
|
-
VIBE_LOGO.forEach((line) =>
|
|
6604
|
+
VIBE_LOGO.forEach((line, i) => {
|
|
6605
|
+
const color = GRAYS[i] || GRAYS[GRAYS.length - 1];
|
|
6606
|
+
console.log(`${color}${line}${RESET}`);
|
|
6607
|
+
});
|
|
6399
6608
|
console.log();
|
|
6400
6609
|
}
|
|
6401
6610
|
function showBanner() {
|
|
@@ -6405,6 +6614,7 @@ function showBanner() {
|
|
|
6405
6614
|
console.log(` ${DIM}$${RESET} ${TEXT}vibe add <repository>${RESET} ${DIM}Add skills & tools${RESET}`);
|
|
6406
6615
|
console.log(` ${DIM}$${RESET} ${TEXT}vibe list${RESET} ${DIM}List installed tools & skills${RESET}`);
|
|
6407
6616
|
console.log(` ${DIM}$${RESET} ${TEXT}vibe update${RESET} ${DIM}Update installed tools & skills${RESET}`);
|
|
6617
|
+
console.log(` ${DIM}$${RESET} ${TEXT}vibe remove${RESET} ${DIM}Remove tools & rules${RESET}`);
|
|
6408
6618
|
console.log();
|
|
6409
6619
|
console.log(`${DIM}Example:${RESET} vibe add helloggx/skill`);
|
|
6410
6620
|
console.log();
|
|
@@ -6419,6 +6629,7 @@ ${BOLD}Manage Tools & Skills:${RESET}
|
|
|
6419
6629
|
https://github.com/helloggx/skill
|
|
6420
6630
|
list, ls List installed tools & skills for .opencode
|
|
6421
6631
|
update, up Update all local tools and standard skills
|
|
6632
|
+
remove, rm Remove selected tools & rules from .opencode
|
|
6422
6633
|
|
|
6423
6634
|
${BOLD}Options:${RESET}
|
|
6424
6635
|
--help, -h Show this help message
|
|
@@ -6427,6 +6638,7 @@ ${BOLD}Examples:${RESET}
|
|
|
6427
6638
|
${DIM}$${RESET} vibe add helloggx/skill
|
|
6428
6639
|
${DIM}$${RESET} vibe list ${DIM}# list installed tools and skills${RESET}
|
|
6429
6640
|
${DIM}$${RESET} vibe update ${DIM}# check and update all items${RESET}
|
|
6641
|
+
${DIM}$${RESET} vibe remove ${DIM}# remove selected tools and rules${RESET}
|
|
6430
6642
|
|
|
6431
6643
|
The Design-Driven Agent Skills Ecosystem
|
|
6432
6644
|
`);
|
|
@@ -6445,6 +6657,11 @@ async function main() {
|
|
|
6445
6657
|
showLogo();
|
|
6446
6658
|
await runAdd(args.slice(1));
|
|
6447
6659
|
break;
|
|
6660
|
+
case "rm":
|
|
6661
|
+
case "remove":
|
|
6662
|
+
console.clear();
|
|
6663
|
+
await runRemove(args.slice(1));
|
|
6664
|
+
break;
|
|
6448
6665
|
case "ls":
|
|
6449
6666
|
case "list":
|
|
6450
6667
|
await runList(args.slice(1));
|