@ibotor/smart-trellis 0.5.22 → 0.5.24
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 +176 -153
- package/dist/cli/smart.js +3 -1
- package/dist/cli/smart.js.map +1 -1
- package/dist/commands/smart-init.d.ts +4 -1
- package/dist/commands/smart-init.d.ts.map +1 -1
- package/dist/commands/smart-init.js +96 -1
- package/dist/commands/smart-init.js.map +1 -1
- package/dist/configurators/shared.d.ts.map +1 -1
- package/dist/configurators/shared.js +2 -0
- package/dist/configurators/shared.js.map +1 -1
- package/dist/migrations/manifests/0.5.23.json +9 -0
- package/dist/templates/common/bundled-skills/trellis-dev-preflight/SKILL.md +162 -0
- package/dist/templates/common/bundled-skills/trellis-quality-review/SKILL.md +82 -0
- package/dist/templates/common/bundled-skills/trellis-quality-review/docs/overview.md +331 -0
- package/dist/templates/common/bundled-skills/trellis-quality-review/evals/evals.json +50 -0
- package/dist/templates/common/bundled-skills/trellis-quality-review/references/copy-fidelity.md +56 -0
- package/dist/templates/common/bundled-skills/trellis-quality-review/references/decision-tree.md +102 -0
- package/dist/templates/common/bundled-skills/trellis-quality-review/references/formatting-fidelity.md +37 -0
- package/dist/templates/common/bundled-skills/trellis-quality-review/references/framework-rules.md +59 -0
- package/dist/templates/common/bundled-skills/trellis-quality-review/references/report-format.md +23 -0
- package/dist/templates/common/bundled-skills/trellis-quality-review/references/safe-refactor-boundaries.md +158 -0
- package/dist/templates/common/bundled-skills/trellis-quality-review/references/scope-control.md +49 -0
- package/dist/templates/common/bundled-skills/trellis-quality-review/references/severity.md +105 -0
- package/dist/templates/common/bundled-skills/trellis-quality-review/references/small-change-fast-path.md +51 -0
- package/dist/templates/common/bundled-skills/trellis-quality-review/references/verification-selection.md +81 -0
- package/dist/templates/common/bundled-skills/trellis-quality-review/scripts/diff_scans.sh +145 -0
- package/dist/templates/common/bundled-skills/verification-before-completion/SKILL.md +139 -0
- package/dist/templates/common/commands/micro-task.md +33 -0
- package/dist/templates/trellis/workflow.md +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,113 +1,208 @@
|
|
|
1
1
|
# Smart Trellis
|
|
2
2
|
|
|
3
|
-
`smart-trellis`
|
|
3
|
+
`smart-trellis` 是 Trellis 的智能初始化与联调增强包。它保留原 Trellis 的完整项目结构,同时把常见初始化选项收束成更少、更明确的配置入口。
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
这个 npm 包提供三个命令:
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
```text
|
|
8
|
+
smart-trellis # 智能初始化入口
|
|
9
|
+
trellis # 原 Trellis CLI
|
|
10
|
+
tl # trellis 的短别名
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
当前 npm latest:`0.5.23`。
|
|
14
|
+
|
|
15
|
+
## 安装
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
npm install -g @ibotor/smart-trellis@latest
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
安装后,在项目根目录执行:
|
|
8
22
|
|
|
9
23
|
```bash
|
|
10
24
|
smart-trellis init
|
|
11
25
|
```
|
|
12
26
|
|
|
13
|
-
|
|
27
|
+
如果希望临时使用,也可以不全局安装:
|
|
14
28
|
|
|
15
|
-
|
|
16
|
-
-
|
|
17
|
-
|
|
18
|
-
- 自动检测开发者名称,优先使用 `--user`,其次使用 `git config user.name`,最后使用系统用户名。
|
|
19
|
-
- 默认工具为 `codex,cursor`。
|
|
20
|
-
- 如果没有传 `--tools`、没有传具体工具参数、也没有传 `--yes`,只会弹出一次工具选择。
|
|
29
|
+
```bash
|
|
30
|
+
npx -p @ibotor/smart-trellis smart-trellis init
|
|
31
|
+
```
|
|
21
32
|
|
|
22
|
-
##
|
|
33
|
+
## smart-trellis 迭代功能
|
|
23
34
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
| `smart-trellis init` | 希望快速初始化一个项目,并保留 Trellis 原完整结构 | 通常只确认工具,或完全无交互 |
|
|
35
|
+
### 1. `smart-trellis init`
|
|
36
|
+
|
|
37
|
+
`smart-trellis init` 用来简化 Trellis 初始化配置。它会调用原始 `trellis init`,但提前帮你处理常见默认值,让新项目更快进入可用状态。
|
|
28
38
|
|
|
29
|
-
|
|
39
|
+
核心能力:
|
|
30
40
|
|
|
31
|
-
|
|
41
|
+
| 能力 | 说明 | 常用参数 |
|
|
42
|
+
| --- | --- | --- |
|
|
43
|
+
| 简化 Trellis 初始化配置 | 默认启用非交互初始化,自动检测 monorepo、开发者名称和常用 AI 工具 | `--yes`、`--tools`、`--user` |
|
|
44
|
+
| 配置触发方式 | 支持用户主动触发,或让 Trellis 在开发请求中自动判断是否进入任务流程 | `--trellis-activation explicit`、`--trellis-activation auto` |
|
|
45
|
+
| 配置 `.gitignore` | 初始化时可选择是否把 `.trellis/` 和 AI 工具生成目录加入 `.gitignore` | `--gitignore-config`、`--no-gitignore-config` |
|
|
32
46
|
|
|
33
|
-
|
|
47
|
+
推荐的新项目初始化方式:
|
|
34
48
|
|
|
35
49
|
```bash
|
|
36
|
-
|
|
50
|
+
smart-trellis init \
|
|
51
|
+
--tools codex,cursor \
|
|
52
|
+
--user <developer-name> \
|
|
53
|
+
--trellis-activation explicit \
|
|
54
|
+
--gitignore-config
|
|
37
55
|
```
|
|
38
56
|
|
|
39
|
-
|
|
57
|
+
如果希望完全使用默认配置:
|
|
40
58
|
|
|
41
59
|
```bash
|
|
42
|
-
smart-trellis init
|
|
60
|
+
smart-trellis init --yes
|
|
43
61
|
```
|
|
44
62
|
|
|
45
|
-
|
|
63
|
+
`--yes` 会使用默认工具 `codex,cursor`,并把 `.trellis/`、`.codex/`、`.cursor/`、`.agents/skills/` 等生成目录写入 `.gitignore`。
|
|
64
|
+
|
|
65
|
+
#### 初始化时会生成什么
|
|
66
|
+
|
|
67
|
+
根据选择的工具不同,生成内容会略有差异。常见输出包括:
|
|
68
|
+
|
|
69
|
+
| 路径 | 作用 |
|
|
70
|
+
| --- | --- |
|
|
71
|
+
| `.trellis/` | Trellis workflow、spec、tasks、workspace、脚本等核心目录 |
|
|
72
|
+
| `.agents/skills/` | 跨工具复用的 Trellis skills |
|
|
73
|
+
| `.codex/` | Codex 专用配置、agents、hooks、skills |
|
|
74
|
+
| `.cursor/` | Cursor 专用 commands、skills、agents、hooks |
|
|
75
|
+
| `.github/` | GitHub Copilot prompts、agents、hooks 等 |
|
|
76
|
+
| `AGENTS.md` | 项目级 AI 助手协作说明 |
|
|
77
|
+
|
|
78
|
+
#### 触发方式配置
|
|
79
|
+
|
|
80
|
+
`smart-trellis init` 支持配置 Trellis 的 activation mode:
|
|
81
|
+
|
|
82
|
+
| 模式 | 行为 | 适合场景 |
|
|
83
|
+
| --- | --- | --- |
|
|
84
|
+
| `explicit` | 用户明确说 `/trellis start`、`$trellis-start`、`走 Trellis` 等才启动任务流程 | 希望普通问答和小改动不被 Trellis 接管 |
|
|
85
|
+
| `auto` | Trellis 根据当前开发请求自动判断是否进入任务流程 | 希望 AI 主动进入 Trellis 工作流 |
|
|
86
|
+
|
|
87
|
+
示例:
|
|
46
88
|
|
|
47
89
|
```bash
|
|
48
|
-
|
|
90
|
+
smart-trellis init --tools codex,cursor --trellis-activation explicit
|
|
49
91
|
```
|
|
50
92
|
|
|
51
|
-
|
|
93
|
+
旧项目可以先更新模板,再在 `.trellis/config.yaml` 里设置:
|
|
52
94
|
|
|
53
95
|
```bash
|
|
54
|
-
|
|
96
|
+
trellis update
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
```yaml
|
|
100
|
+
trellis:
|
|
101
|
+
activation_mode: explicit
|
|
55
102
|
```
|
|
56
103
|
|
|
57
|
-
|
|
104
|
+
#### `.gitignore` 配置
|
|
58
105
|
|
|
59
|
-
|
|
106
|
+
初始化时可以选择是否把生成目录加入项目 `.gitignore`。以 `codex,cursor` 为例,候选项通常是:
|
|
107
|
+
|
|
108
|
+
```text
|
|
109
|
+
.trellis/
|
|
110
|
+
.codex/
|
|
111
|
+
.agents/skills/
|
|
112
|
+
.cursor/
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
规则:
|
|
116
|
+
|
|
117
|
+
- 交互模式下会显示多选项,默认全选。
|
|
118
|
+
- 没有 `.gitignore` 时会创建。
|
|
119
|
+
- 已有 `.codex` 或 `.codex/` 时不会重复添加 `.codex/`。
|
|
120
|
+
- 已有 `.agents/` 时,不会再重复添加 `.agents/skills/`。
|
|
121
|
+
- 候选目录来自 Trellis 的 AI 工具注册表,不手写平台清单。
|
|
122
|
+
|
|
123
|
+
脚本模式下直接全选:
|
|
60
124
|
|
|
61
125
|
```bash
|
|
62
|
-
smart-trellis init
|
|
126
|
+
smart-trellis init --tools codex,cursor --gitignore-config
|
|
63
127
|
```
|
|
64
128
|
|
|
65
|
-
|
|
129
|
+
完全不修改 `.gitignore`:
|
|
66
130
|
|
|
67
131
|
```bash
|
|
68
|
-
smart-trellis init --
|
|
132
|
+
smart-trellis init --tools codex,cursor --no-gitignore-config
|
|
69
133
|
```
|
|
70
134
|
|
|
71
|
-
|
|
135
|
+
注意:如果把 `.trellis/` 加入 `.gitignore`,Trellis 的 spec、tasks、workspace 等状态默认不会被 Git 跟踪。适合把 Trellis 配置当本地工作区的团队;如果团队希望提交 `.trellis/spec/` 或任务记录,请在多选时取消 `.trellis/`。
|
|
72
136
|
|
|
73
|
-
|
|
74
|
-
- `explicit`:Trellis 只在你明确使用 `/trellis start` / `$trellis-start` / “走 Trellis” 时启动。
|
|
137
|
+
### 2. 前后端联调
|
|
75
138
|
|
|
76
|
-
|
|
139
|
+
前后端联调用来让 AI 根据 Apifox 接口链接读取接口详情,并和当前项目里的 API 封装、请求参数、响应字段、代理配置进行对照。
|
|
77
140
|
|
|
78
|
-
|
|
79
|
-
|
|
141
|
+
当前真实入口名是 `trellis-joint-debugger`:
|
|
142
|
+
|
|
143
|
+
```text
|
|
144
|
+
Cursor: /trellis-joint-debugger
|
|
145
|
+
Codex / .agents/skills: $trellis-joint-debugger
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
使用场景:
|
|
149
|
+
|
|
150
|
+
- 前端已经有页面或 API wrapper,但接口参数对不上。
|
|
151
|
+
- 后端接口文档在 Apifox,想让 AI 自动读取 method、path、query、body、response。
|
|
152
|
+
- 只想提供 Apifox 链接,让 AI 帮你定位本地代码里该改哪里。
|
|
153
|
+
|
|
154
|
+
支持的 Apifox 链接格式:
|
|
155
|
+
|
|
156
|
+
```text
|
|
157
|
+
app.apifox.com/link/project/{projectId}/apis/api-{apiId}
|
|
158
|
+
app.apifox.com/project/{projectId}/apis/api-{apiId}
|
|
80
159
|
```
|
|
81
160
|
|
|
82
|
-
|
|
161
|
+
第一次使用前,可以保存 Apifox token:
|
|
83
162
|
|
|
84
163
|
```bash
|
|
85
|
-
|
|
164
|
+
python3 ./.trellis/scripts/apifox_token.py save
|
|
86
165
|
```
|
|
87
166
|
|
|
88
|
-
|
|
167
|
+
也可以检查当前是否已配置:
|
|
89
168
|
|
|
90
169
|
```bash
|
|
91
|
-
|
|
170
|
+
python3 ./.trellis/scripts/apifox_token.py check
|
|
92
171
|
```
|
|
93
172
|
|
|
94
|
-
|
|
173
|
+
token 读取优先级:
|
|
174
|
+
|
|
175
|
+
1. `APIFOX_ACCESS_TOKEN` 环境变量
|
|
176
|
+
2. `~/.trellis/apifox.env`
|
|
177
|
+
3. 旧项目内 `.trellis/.runtime/apifox.env`
|
|
178
|
+
|
|
179
|
+
联调流程会做什么:
|
|
180
|
+
|
|
181
|
+
1. 从 Apifox 链接解析 `projectId` 和 `apiId`。
|
|
182
|
+
2. 请求 Apifox Web API 读取接口详情。
|
|
183
|
+
3. 提取 method、path、query/path/header/body、responses。
|
|
184
|
+
4. 展开请求体或响应体里的 schema 引用。
|
|
185
|
+
5. 搜索当前项目里的 API 封装、请求客户端、代理配置和环境配置。
|
|
186
|
+
6. 对比 Apifox 字段和本地代码字段。
|
|
187
|
+
7. 如需真实请求业务后端,会根据项目代理和环境配置执行;如果返回 `401` 或缺少登录态,会明确报告。
|
|
188
|
+
|
|
189
|
+
旧项目先运行:
|
|
95
190
|
|
|
96
191
|
```bash
|
|
97
|
-
trellis
|
|
192
|
+
trellis update
|
|
98
193
|
```
|
|
99
194
|
|
|
100
|
-
##
|
|
195
|
+
## 常用参数
|
|
101
196
|
|
|
102
|
-
|
|
197
|
+
### 指定工具
|
|
103
198
|
|
|
104
|
-
|
|
199
|
+
逗号形式:
|
|
105
200
|
|
|
106
201
|
```bash
|
|
107
202
|
smart-trellis init --tools codex,cursor,claude
|
|
108
203
|
```
|
|
109
204
|
|
|
110
|
-
|
|
205
|
+
独立参数形式:
|
|
111
206
|
|
|
112
207
|
```bash
|
|
113
208
|
smart-trellis init --codex --cursor --claude
|
|
@@ -132,21 +227,25 @@ droid
|
|
|
132
227
|
pi
|
|
133
228
|
```
|
|
134
229
|
|
|
135
|
-
|
|
230
|
+
### 指定开发者名称
|
|
136
231
|
|
|
137
|
-
|
|
232
|
+
```bash
|
|
233
|
+
smart-trellis init --tools codex,cursor --user adong
|
|
234
|
+
```
|
|
138
235
|
|
|
139
|
-
|
|
236
|
+
如果不传 `--user`,会优先读取 `git config user.name`,再回退到系统用户名。
|
|
140
237
|
|
|
141
|
-
|
|
238
|
+
### Registry 和模板
|
|
239
|
+
|
|
240
|
+
`smart-trellis init` 兼容原 `trellis init --registry` 能力,可以从自定义 GitHub、GitLab、Bitbucket 仓库拉取 spec。
|
|
142
241
|
|
|
143
242
|
```bash
|
|
144
|
-
smart-trellis init
|
|
243
|
+
smart-trellis init \
|
|
244
|
+
--tools codex,cursor \
|
|
245
|
+
--registry gh:myorg/myrepo/my-team-spec
|
|
145
246
|
```
|
|
146
247
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
如果 registry 目录里有 `index.json`,并且里面定义了多个模板,非交互模式下必须指定 `--template`:
|
|
248
|
+
Marketplace 模式:
|
|
150
249
|
|
|
151
250
|
```bash
|
|
152
251
|
smart-trellis init \
|
|
@@ -155,9 +254,7 @@ smart-trellis init \
|
|
|
155
254
|
--template my-template
|
|
156
255
|
```
|
|
157
256
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
### 指定分支
|
|
257
|
+
指定分支:
|
|
161
258
|
|
|
162
259
|
```bash
|
|
163
260
|
smart-trellis init \
|
|
@@ -165,122 +262,66 @@ smart-trellis init \
|
|
|
165
262
|
--registry gh:myorg/myrepo/specs#develop
|
|
166
263
|
```
|
|
167
264
|
|
|
168
|
-
|
|
265
|
+
GitLab 和 Bitbucket:
|
|
169
266
|
|
|
170
267
|
```bash
|
|
171
268
|
smart-trellis init --tools codex,cursor --registry gitlab:myorg/myrepo/specs
|
|
172
269
|
smart-trellis init --tools codex,cursor --registry bitbucket:myorg/myrepo/specs
|
|
173
270
|
```
|
|
174
271
|
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
`smart-trellis init` 默认会以安全方式写入文件。因为它内部会启用非交互模式,所以遇到已经存在的文件时默认跳过,避免覆盖已有配置。
|
|
178
|
-
|
|
179
|
-
如果你要强制覆盖已有文件:
|
|
180
|
-
|
|
181
|
-
```bash
|
|
182
|
-
smart-trellis init --tools codex,cursor --force
|
|
183
|
-
```
|
|
184
|
-
|
|
185
|
-
如果你希望明确跳过已有文件:
|
|
186
|
-
|
|
187
|
-
```bash
|
|
188
|
-
smart-trellis init --tools codex,cursor --skip-existing
|
|
189
|
-
```
|
|
190
|
-
|
|
191
|
-
使用远程模板时,也可以控制 spec 目录写入方式:
|
|
272
|
+
远程模板写入策略:
|
|
192
273
|
|
|
193
274
|
```bash
|
|
194
275
|
smart-trellis init --tools codex,cursor --registry gh:myorg/myrepo/specs --overwrite
|
|
195
276
|
smart-trellis init --tools codex,cursor --registry gh:myorg/myrepo/specs --append
|
|
196
277
|
```
|
|
197
278
|
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
默认情况下,`smart-trellis init` 会让 Trellis 自动检测当前项目是否为 monorepo。
|
|
279
|
+
### 文件覆盖和 Monorepo
|
|
201
280
|
|
|
202
|
-
|
|
281
|
+
默认写入策略偏安全:遇到已有文件会跳过,避免覆盖本地配置。
|
|
203
282
|
|
|
204
283
|
```bash
|
|
205
|
-
smart-trellis init --tools codex,cursor --
|
|
284
|
+
smart-trellis init --tools codex,cursor --force
|
|
285
|
+
smart-trellis init --tools codex,cursor --skip-existing
|
|
206
286
|
```
|
|
207
287
|
|
|
208
|
-
|
|
288
|
+
Monorepo 默认自动检测。也可以手动指定:
|
|
209
289
|
|
|
210
290
|
```bash
|
|
291
|
+
smart-trellis init --tools codex,cursor --monorepo
|
|
211
292
|
smart-trellis init --tools codex,cursor --no-monorepo
|
|
212
293
|
```
|
|
213
294
|
|
|
214
|
-
##
|
|
215
|
-
|
|
216
|
-
根据选择的工具不同,生成内容会略有差异。常见输出包括:
|
|
217
|
-
|
|
218
|
-
- `.trellis/`:Trellis workflow、spec、tasks、workspace 等核心目录。
|
|
219
|
-
- `.agents/skills/`:跨工具复用的 Trellis skills。
|
|
220
|
-
- `.codex/`:Codex 专用配置、skills、agents、hooks。
|
|
221
|
-
- `.cursor/`:Cursor 专用配置、commands、skills、agents、hooks。
|
|
222
|
-
- `AGENTS.md`:项目级 AI 助手协作说明。
|
|
223
|
-
|
|
224
|
-
所以,`smart-trellis init` 的目标不是只生成一个轻量配置,而是用更少确认步骤生成原 Trellis 初始化应有的完整结果。
|
|
225
|
-
|
|
226
|
-
## Apifox 前后端联调入口
|
|
227
|
-
|
|
228
|
-
初始化 Cursor / Codex 等平台后,会生成 `trellis-joint-debugger` 入口,用于根据 Apifox 接口链接自动解析接口详情并辅助前后端联调。
|
|
295
|
+
## 更新已有项目
|
|
229
296
|
|
|
230
|
-
|
|
297
|
+
已初始化项目通常只需要:
|
|
231
298
|
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
当用户提供以下 Apifox 链接,并提到“联调”“接口”“参数”“详情”“完成它”等开发意图时,入口会优先走 Apifox Web API,而不是默认打开浏览器页面:
|
|
236
|
-
|
|
237
|
-
```text
|
|
238
|
-
app.apifox.com/link/project/{projectId}/apis/api-{apiId}
|
|
239
|
-
app.apifox.com/project/{projectId}/apis/api-{apiId}
|
|
299
|
+
```bash
|
|
300
|
+
npm install -g @ibotor/smart-trellis@latest
|
|
301
|
+
trellis update
|
|
240
302
|
```
|
|
241
303
|
|
|
242
|
-
|
|
304
|
+
如果要使用 Apifox 联调能力,再配置一次用户级 token:
|
|
243
305
|
|
|
244
306
|
```bash
|
|
245
|
-
python3 ./.trellis/scripts/apifox_token.py check
|
|
246
307
|
python3 ./.trellis/scripts/apifox_token.py save
|
|
247
308
|
```
|
|
248
309
|
|
|
249
|
-
`save` 会使用隐藏输入,并把 token 保存到用户级配置:
|
|
250
|
-
|
|
251
|
-
```text
|
|
252
|
-
~/.trellis/apifox.env
|
|
253
|
-
```
|
|
254
|
-
|
|
255
|
-
因此同一台机器上只需要配置一次,后续所有 Trellis 项目都可以复用。运行时读取优先级为:
|
|
256
|
-
|
|
257
|
-
1. `APIFOX_ACCESS_TOKEN` 环境变量
|
|
258
|
-
2. `~/.trellis/apifox.env`
|
|
259
|
-
3. 旧项目内 `.trellis/.runtime/apifox.env`(仅兼容读取)
|
|
260
|
-
|
|
261
|
-
token 只用于请求 header,不会被命令打印。接口详情会通过:
|
|
262
|
-
|
|
263
|
-
```text
|
|
264
|
-
GET https://api.apifox.com/api/v1/projects/{projectId}/http-apis/{apiId}
|
|
265
|
-
```
|
|
266
|
-
|
|
267
|
-
如请求体或响应中出现 `$ref: #/definitions/{schemaId}`,会继续根据接口详情里的 `moduleId` 请求 data-schemas 并解析入参/出参字段。随后 Trellis 会将 method、path、入参、出参与当前项目中的 API 封装核对;如果要联调真实后端,再根据项目代理和环境配置请求业务后端。若缺少业务登录态,应明确报告 `401` / 未登录,不猜测数据。
|
|
268
|
-
|
|
269
310
|
## 本地开发验证
|
|
270
311
|
|
|
271
|
-
|
|
312
|
+
在本仓库开发时,先构建 CLI:
|
|
272
313
|
|
|
273
314
|
```bash
|
|
274
315
|
pnpm --filter @ibotor/smart-trellis build
|
|
275
316
|
```
|
|
276
317
|
|
|
277
|
-
|
|
318
|
+
直接用本地 bin 测试:
|
|
278
319
|
|
|
279
320
|
```bash
|
|
280
321
|
node packages/cli/bin/smart-trellis.js init --tools codex,cursor --user adong
|
|
281
322
|
```
|
|
282
323
|
|
|
283
|
-
|
|
324
|
+
也可以在临时项目里验证:
|
|
284
325
|
|
|
285
326
|
```bash
|
|
286
327
|
mkdir /tmp/smart-trellis-demo
|
|
@@ -289,29 +330,11 @@ echo '{"name":"smart-trellis-demo"}' > package.json
|
|
|
289
330
|
node /path/to/smart-trellis/packages/cli/bin/smart-trellis.js init --tools codex,cursor --user adong
|
|
290
331
|
```
|
|
291
332
|
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
团队成员在新项目里只需要执行:
|
|
295
|
-
|
|
296
|
-
```bash
|
|
297
|
-
smart-trellis init --tools codex,cursor --user <developer-name>
|
|
298
|
-
```
|
|
299
|
-
|
|
300
|
-
如果你维护了团队统一 spec registry,则推荐:
|
|
301
|
-
|
|
302
|
-
```bash
|
|
303
|
-
smart-trellis init \
|
|
304
|
-
--tools codex,cursor \
|
|
305
|
-
--user <developer-name> \
|
|
306
|
-
--registry gh:<org>/<repo>/<spec-path>
|
|
307
|
-
```
|
|
308
|
-
|
|
309
|
-
如果 registry 是 marketplace:
|
|
333
|
+
发布前建议至少运行:
|
|
310
334
|
|
|
311
335
|
```bash
|
|
312
|
-
smart-trellis
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
--template <template-id>
|
|
336
|
+
pnpm --filter @ibotor/smart-trellis test
|
|
337
|
+
pnpm --filter @ibotor/smart-trellis build
|
|
338
|
+
pnpm --filter @ibotor/smart-trellis lint
|
|
339
|
+
pnpm --filter @ibotor/smart-trellis typecheck
|
|
317
340
|
```
|
package/dist/cli/smart.js
CHANGED
|
@@ -22,7 +22,9 @@ const initCommand = program
|
|
|
22
22
|
.option("--overwrite", "Overwrite existing spec directory when using template")
|
|
23
23
|
.option("--append", "Only add missing files when using template")
|
|
24
24
|
.option("-r, --registry <source>", "Use a custom template registry (e.g., gh:myorg/myrepo/specs)")
|
|
25
|
-
.option("--trellis-activation <mode>", "Set no-task Trellis activation mode: auto or explicit")
|
|
25
|
+
.option("--trellis-activation <mode>", "Set no-task Trellis activation mode: auto or explicit")
|
|
26
|
+
.option("--gitignore-config", "Add generated config directories to .gitignore without prompting")
|
|
27
|
+
.option("--no-gitignore-config", "Do not add generated config directories to .gitignore");
|
|
26
28
|
for (const tool of getInitToolChoices()) {
|
|
27
29
|
initCommand.option(`--${tool.key}`, `Include ${tool.name}`);
|
|
28
30
|
}
|
package/dist/cli/smart.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"smart.js","sourceRoot":"","sources":["../../src/cli/smart.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,SAAS,EAAyB,MAAM,2BAA2B,CAAC;AAC7E,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAC/D,OAAO,EAAE,OAAO,EAAE,MAAM,yBAAyB,CAAC;AAElD,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE,CAAC;AAE9B,OAAO;KACJ,IAAI,CAAC,eAAe,CAAC;KACrB,WAAW,CAAC,wCAAwC,CAAC;KACrD,OAAO,CAAC,OAAO,EAAE,eAAe,EAAE,2BAA2B,CAAC,CAAC;AAElE,MAAM,WAAW,GAAG,OAAO;KACxB,OAAO,CAAC,MAAM,CAAC;KACf,WAAW,CAAC,gEAAgE,CAAC;KAC7E,MAAM,CAAC,iBAAiB,EAAE,6CAA6C,CAAC;KACxE,MAAM,CAAC,WAAW,EAAE,6CAA6C,CAAC;KAClE,MAAM,CACL,mBAAmB,EACnB,mDAAmD,CACpD;KACA,MAAM,CAAC,aAAa,EAAE,yCAAyC,CAAC;KAChE,MAAM,CAAC,qBAAqB,EAAE,oCAAoC,CAAC;KACnE,MAAM,CAAC,YAAY,EAAE,qBAAqB,CAAC;KAC3C,MAAM,CAAC,eAAe,EAAE,yBAAyB,CAAC;KAClD,MAAM,CACL,uBAAuB,EACvB,uDAAuD,CACxD;KACA,MAAM,CACL,aAAa,EACb,uDAAuD,CACxD;KACA,MAAM,CAAC,UAAU,EAAE,4CAA4C,CAAC;KAChE,MAAM,CACL,yBAAyB,EACzB,8DAA8D,CAC/D;KACA,MAAM,CACL,6BAA6B,EAC7B,uDAAuD,CACxD,CAAC;AAEJ,KAAK,MAAM,IAAI,IAAI,kBAAkB,EAAE,EAAE,CAAC;IACxC,WAAW,CAAC,MAAM,CAAC,KAAK,IAAI,CAAC,GAAG,EAAE,EAAE,WAAW,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;AAC9D,CAAC;AAED,WAAW,CAAC,MAAM,CAAC,KAAK,EAAE,OAAyB,EAAE,EAAE;IACrD,IAAI,CAAC;QACH,MAAM,SAAS,CAAC,OAAO,CAAC,CAAC;IAC3B,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,CAAC,KAAK,CACX,KAAK,CAAC,GAAG,CAAC,QAAQ,CAAC,EACnB,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,CAC/C,CAAC;QACF,IAAI,OAAO,CAAC,GAAG,CAAC,KAAK,IAAI,OAAO,CAAC,GAAG,CAAC,aAAa,EAAE,CAAC;YACnD,OAAO,CAAC,KAAK,CAAC,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;QAC9D,CAAC;QACD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;AACH,CAAC,CAAC,CAAC;AAEH,OAAO,CAAC,KAAK,EAAE,CAAC"}
|
|
1
|
+
{"version":3,"file":"smart.js","sourceRoot":"","sources":["../../src/cli/smart.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,SAAS,EAAyB,MAAM,2BAA2B,CAAC;AAC7E,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAC/D,OAAO,EAAE,OAAO,EAAE,MAAM,yBAAyB,CAAC;AAElD,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE,CAAC;AAE9B,OAAO;KACJ,IAAI,CAAC,eAAe,CAAC;KACrB,WAAW,CAAC,wCAAwC,CAAC;KACrD,OAAO,CAAC,OAAO,EAAE,eAAe,EAAE,2BAA2B,CAAC,CAAC;AAElE,MAAM,WAAW,GAAG,OAAO;KACxB,OAAO,CAAC,MAAM,CAAC;KACf,WAAW,CAAC,gEAAgE,CAAC;KAC7E,MAAM,CAAC,iBAAiB,EAAE,6CAA6C,CAAC;KACxE,MAAM,CAAC,WAAW,EAAE,6CAA6C,CAAC;KAClE,MAAM,CACL,mBAAmB,EACnB,mDAAmD,CACpD;KACA,MAAM,CAAC,aAAa,EAAE,yCAAyC,CAAC;KAChE,MAAM,CAAC,qBAAqB,EAAE,oCAAoC,CAAC;KACnE,MAAM,CAAC,YAAY,EAAE,qBAAqB,CAAC;KAC3C,MAAM,CAAC,eAAe,EAAE,yBAAyB,CAAC;KAClD,MAAM,CACL,uBAAuB,EACvB,uDAAuD,CACxD;KACA,MAAM,CACL,aAAa,EACb,uDAAuD,CACxD;KACA,MAAM,CAAC,UAAU,EAAE,4CAA4C,CAAC;KAChE,MAAM,CACL,yBAAyB,EACzB,8DAA8D,CAC/D;KACA,MAAM,CACL,6BAA6B,EAC7B,uDAAuD,CACxD;KACA,MAAM,CACL,oBAAoB,EACpB,kEAAkE,CACnE;KACA,MAAM,CACL,uBAAuB,EACvB,uDAAuD,CACxD,CAAC;AAEJ,KAAK,MAAM,IAAI,IAAI,kBAAkB,EAAE,EAAE,CAAC;IACxC,WAAW,CAAC,MAAM,CAAC,KAAK,IAAI,CAAC,GAAG,EAAE,EAAE,WAAW,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;AAC9D,CAAC;AAED,WAAW,CAAC,MAAM,CAAC,KAAK,EAAE,OAAyB,EAAE,EAAE;IACrD,IAAI,CAAC;QACH,MAAM,SAAS,CAAC,OAAO,CAAC,CAAC;IAC3B,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,CAAC,KAAK,CACX,KAAK,CAAC,GAAG,CAAC,QAAQ,CAAC,EACnB,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,CAC/C,CAAC;QACF,IAAI,OAAO,CAAC,GAAG,CAAC,KAAK,IAAI,OAAO,CAAC,GAAG,CAAC,aAAa,EAAE,CAAC;YACnD,OAAO,CAAC,KAAK,CAAC,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;QAC9D,CAAC;QACD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;AACH,CAAC,CAAC,CAAC;AAEH,OAAO,CAAC,KAAK,EAAE,CAAC"}
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { type InitOptions } from "./init.js";
|
|
2
|
-
import type
|
|
2
|
+
import { type CliFlag } from "../types/ai-tools.js";
|
|
3
3
|
type SmartToolFlagOptions = Partial<Record<CliFlag, boolean>>;
|
|
4
4
|
export type TrellisActivationMode = "auto" | "explicit";
|
|
5
5
|
export type SmartInitOptions = SmartToolFlagOptions & {
|
|
6
6
|
append?: boolean;
|
|
7
7
|
force?: boolean;
|
|
8
|
+
gitignoreConfig?: boolean;
|
|
8
9
|
monorepo?: boolean;
|
|
9
10
|
overwrite?: boolean;
|
|
10
11
|
registry?: string;
|
|
@@ -19,6 +20,8 @@ export declare const SMART_DEFAULT_TOOLS: CliFlag[];
|
|
|
19
20
|
export declare function parseSmartToolsOption(value: string): CliFlag[];
|
|
20
21
|
export declare function parseSmartTrellisActivationMode(value: string | undefined): TrellisActivationMode | undefined;
|
|
21
22
|
export declare function patchTrellisActivationConfig(content: string, mode: TrellisActivationMode): string;
|
|
23
|
+
export declare function getSmartGitignoreCandidates(tools: CliFlag[]): string[];
|
|
24
|
+
export declare function patchGitignoreConfigDirs(content: string, entries: string[]): string;
|
|
22
25
|
export declare function createSmartInitOptions(options: SmartInitOptions, tools: CliFlag[], detectedUser?: string): InitOptions;
|
|
23
26
|
export declare function detectDeveloperName(cwd?: string): string | undefined;
|
|
24
27
|
export declare function smartInit(options: SmartInitOptions): Promise<void>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"smart-init.d.ts","sourceRoot":"","sources":["../../src/commands/smart-init.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"smart-init.d.ts","sourceRoot":"","sources":["../../src/commands/smart-init.ts"],"names":[],"mappings":"AASA,OAAO,EAAQ,KAAK,WAAW,EAAE,MAAM,WAAW,CAAC;AACnD,OAAO,EAAmB,KAAK,OAAO,EAAE,MAAM,sBAAsB,CAAC;AAErE,KAAK,oBAAoB,GAAG,OAAO,CAAC,MAAM,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC;AAC9D,MAAM,MAAM,qBAAqB,GAAG,MAAM,GAAG,UAAU,CAAC;AAExD,MAAM,MAAM,gBAAgB,GAAG,oBAAoB,GAAG;IACpD,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,GAAG,CAAC,EAAE,OAAO,CAAC;CACf,CAAC;AAEF,eAAO,MAAM,mBAAmB,EAAE,OAAO,EAAwB,CAAC;AAiBlE,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,EAAE,CAkB9D;AAED,wBAAgB,+BAA+B,CAC7C,KAAK,EAAE,MAAM,GAAG,SAAS,GACxB,qBAAqB,GAAG,SAAS,CAWnC;AAED,wBAAgB,4BAA4B,CAC1C,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,qBAAqB,GAC1B,MAAM,CAwCR;AA2CD,wBAAgB,2BAA2B,CAAC,KAAK,EAAE,OAAO,EAAE,GAAG,MAAM,EAAE,CAUtE;AAED,wBAAgB,wBAAwB,CACtC,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,EAAE,GAChB,MAAM,CAmBR;AAiED,wBAAgB,sBAAsB,CACpC,OAAO,EAAE,gBAAgB,EACzB,KAAK,EAAE,OAAO,EAAE,EAChB,YAAY,CAAC,EAAE,MAAM,GACpB,WAAW,CA6Bb;AAED,wBAAgB,mBAAmB,CACjC,GAAG,GAAE,MAAsB,GAC1B,MAAM,GAAG,SAAS,CAoBpB;AAmGD,wBAAsB,SAAS,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC,CAgBxE"}
|
|
@@ -3,9 +3,11 @@ import fs from "node:fs";
|
|
|
3
3
|
import os from "node:os";
|
|
4
4
|
import path from "node:path";
|
|
5
5
|
import inquirer from "inquirer";
|
|
6
|
-
import { getInitToolChoices } from "../configurators/index.js";
|
|
6
|
+
import { getInitToolChoices, resolveCliFlag, } from "../configurators/index.js";
|
|
7
7
|
import { init } from "./init.js";
|
|
8
|
+
import { getManagedPaths } from "../types/ai-tools.js";
|
|
8
9
|
export const SMART_DEFAULT_TOOLS = ["codex", "cursor"];
|
|
10
|
+
const TRELLIS_GITIGNORE_ENTRY = ".trellis/";
|
|
9
11
|
function supportedToolFlags() {
|
|
10
12
|
return getInitToolChoices().map((tool) => tool.key);
|
|
11
13
|
}
|
|
@@ -71,6 +73,75 @@ export function patchTrellisActivationConfig(content, mode) {
|
|
|
71
73
|
const separator = content.endsWith("\n") ? "\n" : "\n\n";
|
|
72
74
|
return `${content}${separator}trellis:\n activation_mode: ${mode}\n`;
|
|
73
75
|
}
|
|
76
|
+
function normalizeGitignoreDirEntry(value) {
|
|
77
|
+
const trimmed = value.trim();
|
|
78
|
+
if (!trimmed || trimmed.startsWith("#") || trimmed.startsWith("!")) {
|
|
79
|
+
return undefined;
|
|
80
|
+
}
|
|
81
|
+
const normalized = trimmed
|
|
82
|
+
.replace(/\\/g, "/")
|
|
83
|
+
.replace(/^\/+/, "")
|
|
84
|
+
.replace(/\/+$/g, "");
|
|
85
|
+
if (!normalized) {
|
|
86
|
+
return undefined;
|
|
87
|
+
}
|
|
88
|
+
return `${normalized}/`;
|
|
89
|
+
}
|
|
90
|
+
function uniqueDirEntries(entries) {
|
|
91
|
+
const seen = new Set();
|
|
92
|
+
const unique = [];
|
|
93
|
+
for (const entry of entries) {
|
|
94
|
+
const normalized = normalizeGitignoreDirEntry(entry);
|
|
95
|
+
if (!normalized || seen.has(normalized)) {
|
|
96
|
+
continue;
|
|
97
|
+
}
|
|
98
|
+
seen.add(normalized);
|
|
99
|
+
unique.push(normalized);
|
|
100
|
+
}
|
|
101
|
+
return unique;
|
|
102
|
+
}
|
|
103
|
+
function gitignoreEntryCoversCandidate(gitignoreEntry, candidate) {
|
|
104
|
+
const normalized = normalizeGitignoreDirEntry(gitignoreEntry);
|
|
105
|
+
return (normalized === candidate ||
|
|
106
|
+
Boolean(normalized && candidate.startsWith(normalized)));
|
|
107
|
+
}
|
|
108
|
+
export function getSmartGitignoreCandidates(tools) {
|
|
109
|
+
const entries = [TRELLIS_GITIGNORE_ENTRY];
|
|
110
|
+
for (const tool of tools) {
|
|
111
|
+
const platformId = resolveCliFlag(tool);
|
|
112
|
+
if (!platformId) {
|
|
113
|
+
throw new Error(`Unknown tool "${tool}".`);
|
|
114
|
+
}
|
|
115
|
+
entries.push(...getManagedPaths(platformId));
|
|
116
|
+
}
|
|
117
|
+
return uniqueDirEntries(entries);
|
|
118
|
+
}
|
|
119
|
+
export function patchGitignoreConfigDirs(content, entries) {
|
|
120
|
+
const candidates = uniqueDirEntries(entries);
|
|
121
|
+
if (candidates.length === 0) {
|
|
122
|
+
return content;
|
|
123
|
+
}
|
|
124
|
+
const existingLines = content.split(/\r?\n/);
|
|
125
|
+
const missing = candidates.filter((candidate) => !existingLines.some((line) => gitignoreEntryCoversCandidate(line, candidate)));
|
|
126
|
+
if (missing.length === 0) {
|
|
127
|
+
return content;
|
|
128
|
+
}
|
|
129
|
+
const prefix = content && !content.endsWith("\n") ? `${content}\n` : content;
|
|
130
|
+
return `${prefix}${missing.join("\n")}\n`;
|
|
131
|
+
}
|
|
132
|
+
function applyGitignoreConfigDirs(cwd, entries) {
|
|
133
|
+
if (entries.length === 0) {
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
const gitignorePath = path.join(cwd, ".gitignore");
|
|
137
|
+
const content = fs.existsSync(gitignorePath)
|
|
138
|
+
? fs.readFileSync(gitignorePath, "utf-8")
|
|
139
|
+
: "";
|
|
140
|
+
const patched = patchGitignoreConfigDirs(content, entries);
|
|
141
|
+
if (patched !== content) {
|
|
142
|
+
fs.writeFileSync(gitignorePath, patched, "utf-8");
|
|
143
|
+
}
|
|
144
|
+
}
|
|
74
145
|
function applyTrellisActivationConfig(cwd, mode) {
|
|
75
146
|
if (!mode) {
|
|
76
147
|
return;
|
|
@@ -169,6 +240,28 @@ async function resolveSmartTools(options) {
|
|
|
169
240
|
]);
|
|
170
241
|
return answers.tools;
|
|
171
242
|
}
|
|
243
|
+
async function resolveSmartGitignoreConfigDirs(options, tools) {
|
|
244
|
+
const candidates = getSmartGitignoreCandidates(tools);
|
|
245
|
+
if (options.gitignoreConfig === false || candidates.length === 0) {
|
|
246
|
+
return [];
|
|
247
|
+
}
|
|
248
|
+
if (options.gitignoreConfig === true || options.yes) {
|
|
249
|
+
return candidates;
|
|
250
|
+
}
|
|
251
|
+
const answers = await inquirer.prompt([
|
|
252
|
+
{
|
|
253
|
+
type: "checkbox",
|
|
254
|
+
name: "gitignoreConfigDirs",
|
|
255
|
+
message: "Select generated config directories to add to .gitignore:",
|
|
256
|
+
choices: candidates.map((entry) => ({
|
|
257
|
+
checked: true,
|
|
258
|
+
name: entry,
|
|
259
|
+
value: entry,
|
|
260
|
+
})),
|
|
261
|
+
},
|
|
262
|
+
]);
|
|
263
|
+
return answers.gitignoreConfigDirs ?? [];
|
|
264
|
+
}
|
|
172
265
|
async function resolveSmartTrellisActivation(options) {
|
|
173
266
|
const parsed = parseSmartTrellisActivationMode(options.trellisActivation);
|
|
174
267
|
const hasExplicitTools = !!options.tools ||
|
|
@@ -198,9 +291,11 @@ async function resolveSmartTrellisActivation(options) {
|
|
|
198
291
|
}
|
|
199
292
|
export async function smartInit(options) {
|
|
200
293
|
const tools = await resolveSmartTools(options);
|
|
294
|
+
const gitignoreConfigDirs = await resolveSmartGitignoreConfigDirs(options, tools);
|
|
201
295
|
const activationMode = await resolveSmartTrellisActivation(options);
|
|
202
296
|
const initOptions = createSmartInitOptions(options, tools, detectDeveloperName());
|
|
203
297
|
await init(initOptions);
|
|
204
298
|
applyTrellisActivationConfig(process.cwd(), activationMode);
|
|
299
|
+
applyGitignoreConfigDirs(process.cwd(), gitignoreConfigDirs);
|
|
205
300
|
}
|
|
206
301
|
//# sourceMappingURL=smart-init.js.map
|