@teamix-evo/mcp 0.4.0 → 0.4.3
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/dist/cli.js +64 -16
- package/dist/cli.js.map +1 -1
- package/dist/data/adr/0001-three-layer-alignment.md +11 -11
- package/dist/data/adr/0005-ui-no-variant.md +4 -4
- package/dist/data/adr/0006-ui-upgrade-no-baseline.md +6 -6
- package/dist/data/adr/0010-design-default-and-variants.md +1 -1
- package/dist/data/adr/0019-project-upgrade-flow.md +13 -13
- package/dist/data/adr/0021-semantic-color-api-unification.md +127 -0
- package/dist/data/adr/0022-preferences-css-boundary.md +75 -0
- package/dist/data/adr/0023-cursor-pointer-explicit-in-component-source.md +70 -0
- package/dist/data/adr/0024-scoped-css-radix-state-conflict.md +99 -0
- package/dist/data/adr/0025-component-props-explicit-declaration.md +145 -0
- package/dist/data/adr/0026-component-level-token-alias.md +107 -0
- package/dist/data/adr/0027-component-visual-token-alignment.md +127 -0
- package/dist/data/adr/0028-ui-component-categorization.md +112 -0
- package/dist/data/adr/0029-input-split-and-prefix-suffix-removal.md +68 -0
- package/dist/data/adr/0030-skill-uni-manager-uplift.md +56 -0
- package/dist/data/adr/0031-skill-templates-decoupling.md +77 -0
- package/dist/data/adr/0032-opentrek-v4.1-brand-token-alignment.md +129 -0
- package/dist/data/adr/0033-entry-skill-global-only-scope.md +64 -0
- package/dist/data/adr/0034-skills-cli-verb-alignment.md +61 -0
- package/dist/data/adr/0035-skills-update-scope-and-lock-gates.md +69 -0
- package/dist/data/adr/0036-ui-v2-shadcn-baseline-rebuild.md +146 -0
- package/dist/data/adr/0037-filter-bar-composable-architecture.md +426 -0
- package/dist/data/adr/0038-create-agents-md-skill-trigger-fallback.md +99 -0
- package/dist/data/adr/0040-component-source-layer-upgrade-flow.md +104 -0
- package/dist/data/adr/README.md +41 -28
- package/dist/index.js +64 -16
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
|
@@ -0,0 +1,426 @@
|
|
|
1
|
+
# 0037. FilterBar v2 重做:组合式架构 + shadcn 三段式子组件
|
|
2
|
+
|
|
3
|
+
- **Status**: Accepted
|
|
4
|
+
- **Date**: 2026-06-08
|
|
5
|
+
- **Region**: 0100–0999 协议与工具
|
|
6
|
+
- **Related ADR**: 0014(UI/Biz-UI/Templates 三层)、0023(cursor-pointer 显式声明)、0036(UI v2 shadcn 基线重建)
|
|
7
|
+
|
|
8
|
+
## Context
|
|
9
|
+
|
|
10
|
+
`@teamix-evo/ui` 当前的 [filter-bar/index.tsx](../../packages/ui/src/components/filter-bar/index.tsx) 仅 122 行,本质是一个"折叠 Flex 容器 + 重置按钮",并未承载"筛选栏"语义:
|
|
11
|
+
|
|
12
|
+
| 现状 | 问题 |
|
|
13
|
+
| --------------------------------------------- | --------------------------------------------------------------------------------------- |
|
|
14
|
+
| 不接表单状态(无 form prop / context) | 无法管理多字段、防抖、提交、重置 |
|
|
15
|
+
| 仅一个 `expanded` 布尔切壳,无形态体系 | 截图所示的 inline / panel / 紧凑搜索条 / 纯面板 / 自定义按钮组 5 种业务形态全部无法落地 |
|
|
16
|
+
| `maxRows` 已声明但未实现 | DEVELOPMENT_PLAN §14.3 已挂账技术债 |
|
|
17
|
+
| 仅 `FilterBarItem` 一个无 form 语义的视觉容器 | 与 ui v2 范式(data-slot + 复合子组件)严重脱节 |
|
|
18
|
+
|
|
19
|
+
参考资料:
|
|
20
|
+
|
|
21
|
+
- 旧实现 `packages/ui/_legacy/components/filter-bar/`(568 行,5 件套子组件 + RHF context + 栅格)— 架构方向正确但未对齐 shadcn v4 范式(\_legacy 归档目录已于 2026-06-10 删除,历史可通过 `git show 8cfeab8cb256:packages/ui/_legacy/components/filter-bar/filter-bar.tsx` 回溯)
|
|
22
|
+
- 业务源码 [teamix-pro/packages/pro/src/form/Filter/](file:///Users/lyca/Documents/workspace/teamix/teamix-pro/packages/pro/src/form/Filter/)(1404 行,Formily/alicloudfe 体系)— 能力齐全但技术栈不可直接搬
|
|
23
|
+
- 业务文档 [teamix-pro/.../docs/filter.md](file:///Users/lyca/Documents/workspace/teamix/teamix-pro/packages/pro/src/form/docs/filter.md):定义了 `mode='inline'|'panel'|'bar'` 三种模式 + 高级筛选 + 异步默认值 + bindUrl + filterCondition 等 14 项能力
|
|
24
|
+
- 5 张业务截图覆盖:单字段并排筛选 / 高级筛选默认收起 / 纯面板模式 / 紧凑搜索条 + 高级筛选 / 自定义按钮组
|
|
25
|
+
|
|
26
|
+
业界命名扫描:FilterBar(SAP Fiori / Linear)、QueryFilter(antd-pro)、DataTableToolbar(shadcn TanStack Table 示例)、Filters(Notion/Vercel)。**FilterBar 认知度最广**,但子组件命名缺乏行业共识。
|
|
27
|
+
|
|
28
|
+
shadcn v4 三段式范式(Card / Dialog / Sheet / AlertDialog 全用)已是事实标准:`<Root>` + `<RootHeader>` + `<RootContent>` + `<RootFooter>` + `<RootTrigger>`。
|
|
29
|
+
|
|
30
|
+
不能不做:
|
|
31
|
+
|
|
32
|
+
- ListPage 模板(biz-ui v0.8 主用场)需要稳定 FilterBar 语义
|
|
33
|
+
- DEVELOPMENT_PLAN §14.3 / §14.4 列为硬债待清
|
|
34
|
+
- 当前 122 行版本既"承诺了 maxRows 但未实现",又"占了 FilterBar 命名但未给出筛选语义",必须收敛
|
|
35
|
+
|
|
36
|
+
## Options Considered
|
|
37
|
+
|
|
38
|
+
| 选项 | 优点 | 缺点 |
|
|
39
|
+
| --------------------------------------------------- | ------------------------------------------------------------- | ------------------------------------------------------------------------ |
|
|
40
|
+
| A. 在现有 122 行基础上增量扩展 | 改动小、API 不破坏 | 当前架构无 form context,每加一项能力都是补丁;过渡期 API 不稳;放大债务 |
|
|
41
|
+
| B. **完全重写 + 组合式 + 三段式子组件**(推荐) | 契合 shadcn v4 范式、子组件可独立组合、API 干净一致、消除债务 | 一次性破坏当前 API(影响面:仅 stories 和已归档的旧 demo) |
|
|
42
|
+
| C. 保留现版本作 `FilterBarSimple`,新做 `FilterBar` | 共存、零破坏 | 两套并存增加 AI 心智负担;命名空间冲突;ADR 0036 全量重建精神不符 |
|
|
43
|
+
| D. 沿用 `mode='inline'\|'panel'\|'bar'` enum 切形态 | 与 antd-pro QueryFilter 直接对齐 | 反 shadcn 组合式理念;mode 分支膨胀;形态变化要改 enum 而非 children |
|
|
44
|
+
|
|
45
|
+
选 B:与 ADR 0036 的"全量重建 + shadcn v4 范式"一脉相承。
|
|
46
|
+
|
|
47
|
+
## Decision
|
|
48
|
+
|
|
49
|
+
### D1. 主组件名保留 `FilterBar`
|
|
50
|
+
|
|
51
|
+
业界认知度最高,与 ui v2 现有 `Sidebar` / `Menubar` / 未来 `Toolbar` 同一"-Bar"命名系。**不引入** `QueryBar` / `DataFilters` 等新概念。
|
|
52
|
+
|
|
53
|
+
### D2. 子组件命名采用 shadcn 三段式
|
|
54
|
+
|
|
55
|
+
| 子组件 | 职责 | 对齐范式 |
|
|
56
|
+
| ------------------ | ---------------------------------------------------------- | --------------------------------------- |
|
|
57
|
+
| `FilterBar` | 容器 Root,注入 form context + addonBefore/addonAfter 槽位 | `Card` / `Dialog` Root |
|
|
58
|
+
| `FilterBarHeader` | 顶部行内区(多字段并排,透明背景) | `Card.Header` / `Dialog.Header` |
|
|
59
|
+
| `FilterBarSearch` | 紧凑 key+value 搜索条(`fields` prop 切换搜索字段) | shadcn `Command` 语义近邻 |
|
|
60
|
+
| `FilterBarTrigger` | "高级筛选 ▽" 按钮,自动读 open 状态 | `Collapsible.Trigger` / `Sheet.Trigger` |
|
|
61
|
+
| `FilterBarContent` | 折叠面板(灰底 + 栅格),传入即显示 Trigger | `Collapsible.Content` |
|
|
62
|
+
| `FilterBarFooter` | 重置 + 搜索按钮组(支持 `actions` 自定义) | `Card.Footer` / `Dialog.Footer` |
|
|
63
|
+
| `FilterBarField` | 字段包装(label + control + message + 列宽) | `Form.Field` 语义近邻 |
|
|
64
|
+
|
|
65
|
+
**形态由 children 组合决定,不引入 `mode` enum**:
|
|
66
|
+
|
|
67
|
+
```tsx
|
|
68
|
+
// 形态 1:inline 单纯(截图 1)
|
|
69
|
+
<FilterBar form={form}>
|
|
70
|
+
<FilterBarHeader>{/* 多字段 */}</FilterBarHeader>
|
|
71
|
+
</FilterBar>
|
|
72
|
+
|
|
73
|
+
// 形态 2:inline + 高级筛选面板(截图 2)
|
|
74
|
+
<FilterBar form={form}>
|
|
75
|
+
<FilterBarHeader>
|
|
76
|
+
{/* 多字段 */}
|
|
77
|
+
<FilterBarTrigger />
|
|
78
|
+
</FilterBarHeader>
|
|
79
|
+
<FilterBarContent columns={3}>
|
|
80
|
+
<FilterBarField name="dateRange" label="起止时间">
|
|
81
|
+
<DateRangePicker />
|
|
82
|
+
</FilterBarField>
|
|
83
|
+
{/* ... */}
|
|
84
|
+
<FilterBarFooter />
|
|
85
|
+
</FilterBarContent>
|
|
86
|
+
</FilterBar>
|
|
87
|
+
|
|
88
|
+
// 形态 3:纯面板(截图 3 上)— 不写 Header
|
|
89
|
+
<FilterBar form={form} defaultOpen>
|
|
90
|
+
<FilterBarTrigger />
|
|
91
|
+
<FilterBarContent columns={3}>
|
|
92
|
+
{/* fields */}
|
|
93
|
+
<FilterBarFooter />
|
|
94
|
+
</FilterBarContent>
|
|
95
|
+
</FilterBar>
|
|
96
|
+
|
|
97
|
+
// 形态 4:紧凑搜索条 + 高级筛选(截图 4/5)
|
|
98
|
+
<FilterBar form={form}>
|
|
99
|
+
<FilterBarHeader>
|
|
100
|
+
<FilterBarSearch
|
|
101
|
+
fields={[
|
|
102
|
+
{ name: 'instanceId', label: '实例 ID' },
|
|
103
|
+
{ name: 'instanceName', label: '实例名称' },
|
|
104
|
+
]}
|
|
105
|
+
/>
|
|
106
|
+
<FilterBarTrigger />
|
|
107
|
+
</FilterBarHeader>
|
|
108
|
+
<FilterBarContent>{/* ... */}</FilterBarContent>
|
|
109
|
+
</FilterBar>
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
### D3. Props 命名对齐 shadcn / Tailwind 通用约定
|
|
113
|
+
|
|
114
|
+
| 决策 prop | 旧/参考来源 | 选定值 | 理由 |
|
|
115
|
+
| ---------- | --------------------------- | ----------------------------------------------------------- | ------------------------------------------ |
|
|
116
|
+
| 默认展开 | `defaultExpanded`(v1) | `defaultOpen` | 对齐 shadcn Collapsible/Sheet/Dialog |
|
|
117
|
+
| 受控展开 | `expanded` | `open` | 同上 |
|
|
118
|
+
| 展开变化 | `onExpandChange` | `onOpenChange` | 同上 |
|
|
119
|
+
| 防抖时间 | `filterDebounce`(pro) | `debounceMs` | 通用度高,与 React 生态 hook 命名一致 |
|
|
120
|
+
| 列数 | `columns`(v1) | `columns` 接受 `number \| { base, sm, md, lg }` | 直接接受响应式对象,对齐 Tailwind 断点别名 |
|
|
121
|
+
| 重置强清 | `forceClear`(pro) | `clearOnReset` | 更语义化 |
|
|
122
|
+
| 自定义按钮 | `buttonGroup` schema(pro) | `actions` ReactNode(在 `<FilterBarFooter actions={...}>`) | 用 React 组合替代 schema |
|
|
123
|
+
| URL 同步 | `bindUrl`(pro) | **不内置**,外置 `useFilterBarUrlSync` hook(未来) | 单一职责,避免组件内置路由依赖 |
|
|
124
|
+
|
|
125
|
+
事件 prop:`onFilter` / `onReset` / `onChange` / `onInit` / `onOpenChange`,全部基于 RHF `useForm` 实例语义。
|
|
126
|
+
|
|
127
|
+
### D4. 能力范围(A+B 档位)
|
|
128
|
+
|
|
129
|
+
**v0.x 首版必须实现**:
|
|
130
|
+
|
|
131
|
+
- ✅ 5 种核心形态(inline / inline+panel / 纯面板 / 紧凑搜索条+面板 / 自定义按钮组)
|
|
132
|
+
- ✅ form context(基于 `react-hook-form` + `FormProvider`)
|
|
133
|
+
- ✅ 行内区 onChange 防抖触发 onFilter(`debounceMs` 默认 300)
|
|
134
|
+
- ✅ 面板 open/close(受控 + 非受控)+ Trigger 联动
|
|
135
|
+
- ✅ 栅格(`columns: number | ResponsiveColumns`,5 档断点参考已归档 `_legacy/hooks/use-breakpoint.ts`(git history `8cfeab8cb256`),但**不依赖客户 Tailwind 配置**)
|
|
136
|
+
- ✅ `addonBefore` / `addonAfter` 槽位(截图 2 左 +创建任务 / 右 ⚙️)
|
|
137
|
+
- ✅ `FilterBarFooter` 默认渲染"重置 + 搜索",`actions` prop 可完全替换
|
|
138
|
+
- ✅ `FilterBarField` 替代 FormField + 自带 maxWidth 推断(多栏 400 / 单双栏 600,参 \_legacy 历史 git `8cfeab8cb256`)
|
|
139
|
+
- ✅ `clearOnReset` 强清(对齐 RHF `reset(undefined, { keepDefaultValues: false })`)
|
|
140
|
+
- ✅ `formRef` / `onInit` / `triggerFilter()` / `triggerReset()` / `setAsyncValues()`
|
|
141
|
+
|
|
142
|
+
### D5. 暂不做(明确 backlog)
|
|
143
|
+
|
|
144
|
+
- ❌ 已选 Tag 标签栏(`getFilterTagDataSource`) — 强依赖 ProField displayValue 协议,shadcn 体系下需先抽 Field meta 协议
|
|
145
|
+
- ❌ `bindUrl` URL 同步 — 拆为独立 hook(未来 ADR)
|
|
146
|
+
- ❌ `filterCondition` 保存搜索条件 — 业务功能,应放 biz-ui 或业务侧
|
|
147
|
+
- ❌ 国际化文案表 — 暂硬编码中文,待 ui v2 i18n 体系成熟统一接入
|
|
148
|
+
|
|
149
|
+
### D6. 工程模式严格对齐 ADR 0036
|
|
150
|
+
|
|
151
|
+
- 所有子组件函数式(默认无 forwardRef,需要时单独补)
|
|
152
|
+
- `data-slot` 属性:`filter-bar` / `filter-bar-header` / `filter-bar-search` / `filter-bar-trigger` / `filter-bar-content` / `filter-bar-footer` / `filter-bar-field`
|
|
153
|
+
- `cva` 仅在子组件确有视觉变体时引入(如 `FilterBarContent` 的 `compact` / `comfortable` 留给后续)
|
|
154
|
+
- `cn()` + `@/lib/utils` import
|
|
155
|
+
- 内联样式,**不抽 class 常量文件**
|
|
156
|
+
- 复用现有 ui 组件:[Collapsible](../../packages/ui/src/components/collapsible/index.tsx) 做面板、[Button](../../packages/ui/src/components/button/index.tsx) 做按钮、[Select](../../packages/ui/src/components/select/index.tsx) 做搜索条字段切换
|
|
157
|
+
- token 全走语义 utility:`bg-muted/40` / `border-border` / `ring-ring` / `text-muted-foreground`,无颜色字面量
|
|
158
|
+
- TSDoc 单源(`@description` + `@when`),`pnpm gen:meta` 自动生成 meta.md
|
|
159
|
+
|
|
160
|
+
### D7. Stories 七类法 + 5 个核心 Story
|
|
161
|
+
|
|
162
|
+
`数据录入 · Data Entry/FilterBar 筛选栏`,5 个 Story 一一对应 5 张业务截图:
|
|
163
|
+
|
|
164
|
+
1. `Inline` — 多字段并排实时筛选(截图 1)
|
|
165
|
+
2. `WithAdvancedPanel` — 行内 + 默认收起的高级筛选面板(截图 2)
|
|
166
|
+
3. `PurePanel` — 纯面板模式(截图 3 上半)
|
|
167
|
+
4. `WithKeySearch` — 紧凑搜索条 + 高级筛选(截图 4 / 5 下半)
|
|
168
|
+
5. `CustomActions` — 自定义按钮组(截图 5 上半)
|
|
169
|
+
|
|
170
|
+
### D8. 历史代码处理
|
|
171
|
+
|
|
172
|
+
- 现有 [packages/ui/src/components/filter-bar/](../../packages/ui/src/components/filter-bar/) 完全重写(不保留 122 行旧实现,不做兼容层)
|
|
173
|
+
- `_legacy/components/filter-bar/`(已归档删除,git `8cfeab8cb256`)仅作设计参考,不复制代码
|
|
174
|
+
- DEVELOPMENT_PLAN §14.3(FilterBar `maxRows` 补实现)随本 ADR 落地一并清账:移除 `maxRows`,由 `FilterBarContent` 的 `defaultOpen=false` + `Collapsible` 内置滚动替代
|
|
175
|
+
|
|
176
|
+
## Consequences
|
|
177
|
+
|
|
178
|
+
- **Positive**:
|
|
179
|
+
- 与 shadcn v4 范式(Card/Dialog/Sheet 三段式)完全统一,AI 生成与人工迁移成本最低
|
|
180
|
+
- 形态由 children 组合决定,未来新增形态(如 `FilterBarStatusFilter` 分面 chip)只需加子组件
|
|
181
|
+
- props 命名对齐 shadcn 通用约定(open/onOpenChange/defaultOpen),跨组件迁移肌肉记忆
|
|
182
|
+
- 一次性清账:FilterBar v0 含糊定义 + maxRows 历史债同时收敛
|
|
183
|
+
- 为 biz-ui ListPage / Templates 提供稳定基座
|
|
184
|
+
- **Negative**:
|
|
185
|
+
- 当前 122 行 `FilterBar` / `FilterBarItem` API 直接 break;但实际消费方仅 stories,影响极小
|
|
186
|
+
- 旧 `FilterBarItem` 名称下线(不再有视觉 Item,由 `FilterBarField` 接管语义)
|
|
187
|
+
- 紧凑搜索条 `FilterBarSearch` 是 v2 新增组件,初期 demo 需要打磨
|
|
188
|
+
- **Trade-off**:
|
|
189
|
+
- 用"一次性 API 重写"换"长期形态可扩展性 + AI 友好"
|
|
190
|
+
- 用"暂时不做 Tag/bindUrl/filterCondition"换"v0 首版聚焦核心 80% 场景"
|
|
191
|
+
- 用"内置 RHF 依赖"换"业务侧零样板代码"(biz-ui 已统一 RHF 栈,不引新依赖)
|
|
192
|
+
|
|
193
|
+
## Source
|
|
194
|
+
|
|
195
|
+
- 2026-06-08 用户提出 FilterBar 重做需求,附 5 张业务截图与 [teamix-pro/packages/pro/src/form/docs/filter.md](file:///Users/lyca/Documents/workspace/teamix/teamix-pro/packages/pro/src/form/docs/filter.md) 完整能力定义
|
|
196
|
+
- `packages/ui/_legacy/components/filter-bar/filter-bar.tsx` 568 行(已归档删除,git `8cfeab8cb256` 可回溯,架构方向参考)
|
|
197
|
+
- [teamix-pro Filter index.tsx / Layout.tsx / SimpleFilter.tsx / LightFilter.tsx / AdvancedFilter.tsx](file:///Users/lyca/Documents/workspace/teamix/teamix-pro/packages/pro/src/form/Filter/) 1404 行(能力锚点)
|
|
198
|
+
- shadcn v4 [Card](https://ui.shadcn.com/docs/components/card) / [Dialog](https://ui.shadcn.com/docs/components/dialog) / [Sheet](https://ui.shadcn.com/docs/components/sheet) / [Collapsible](https://ui.shadcn.com/docs/components/collapsible) 三段式范式
|
|
199
|
+
- [DEVELOPMENT_PLAN.md §14.3](../../packages/ui/DEVELOPMENT_PLAN.md) FilterBar maxRows 历史债
|
|
200
|
+
- [ADR 0036](./0036-ui-v2-shadcn-baseline-rebuild.md) UI v2 shadcn 基线重建(本 ADR 在其指导下落地)
|
|
201
|
+
|
|
202
|
+
## v2.1 增补(2026-06-08)· FilterBarField 双形态
|
|
203
|
+
|
|
204
|
+
### 问题
|
|
205
|
+
|
|
206
|
+
v2 首版 `FilterBarField` 仅提供 `render` prop(函数接受 `field` 后返回 JSX)。这个设计避免了 cloneElement 的 prop 名错配,但在 **Storybook dynamic source(Show code)** 展示时函数体会被序列化为 `() => {}`,导致 demo show code 看不到实际使用的筛选控件(Select / Input 等)。这与仓库 "AI 友好" 原则冲突:AI 抓 demo 主要依赖 show code 信号。
|
|
207
|
+
|
|
208
|
+
### 决策
|
|
209
|
+
|
|
210
|
+
`FilterBarField` 同时支持两种形态,按优先级使用:
|
|
211
|
+
|
|
212
|
+
1. **`children` 静态 JSX(首选)** — 推荐与默认写法,AI 友好
|
|
213
|
+
|
|
214
|
+
```tsx
|
|
215
|
+
<FilterBarField name="status" label="状态">
|
|
216
|
+
<Select>
|
|
217
|
+
<SelectTrigger>
|
|
218
|
+
<SelectValue placeholder="全部" />
|
|
219
|
+
</SelectTrigger>
|
|
220
|
+
<SelectContent>
|
|
221
|
+
<SelectItem value="running">运行中</SelectItem>
|
|
222
|
+
</SelectContent>
|
|
223
|
+
</Select>
|
|
224
|
+
</FilterBarField>
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
2. **`render` 函数(逆出舱)** — 适用于适配表不覆盖的自定义控件
|
|
228
|
+
```tsx
|
|
229
|
+
<FilterBarField
|
|
230
|
+
name="status"
|
|
231
|
+
render={({ field }) => <CustomPicker {...field} />}
|
|
232
|
+
/>
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
### prop 桥接适配表
|
|
236
|
+
|
|
237
|
+
`children` 模式下,内部用 `useController` 拿到 `field`,然后按子元素身份用 `cloneElement` 注入所需 prop:
|
|
238
|
+
|
|
239
|
+
| 子元素 识别键(`type.displayName \|\| type.name`) | 注入 prop |
|
|
240
|
+
| -------------------------------------------------- | ----------------------------------- |
|
|
241
|
+
| `Checkbox` / `Switch` | `checked` + `onCheckedChange` |
|
|
242
|
+
| `Select` / `RadioGroup` / `Slider` / `ToggleGroup` | `value` + `onValueChange` |
|
|
243
|
+
| `Input` / `Textarea` / `InputNumber` / 其它 | `value` + `onChange`(event-style) |
|
|
244
|
+
|
|
245
|
+
> RHF `field.onChange` 内部会统一提取 `event.target.value`,所以 event-style 与 value-style 可同一 callback 实例适配。
|
|
246
|
+
|
|
247
|
+
### 不在适配表中的子元素
|
|
248
|
+
|
|
249
|
+
- 仅注入 `value` + `onChange` 默认对,并在开发环境警告:“未识别的受控控件(displayName=X),请改用 `render` prop”
|
|
250
|
+
- 适配表后续可增量扩展(如 DatePicker / Combobox / Cascader),**不制定为封闭表**
|
|
251
|
+
|
|
252
|
+
### 互斥与优先级
|
|
253
|
+
|
|
254
|
+
- 同时传 `children` 与 `render`:`children` 优先,dev 环境警告可能的误用
|
|
255
|
+
- `children` 必须是单个 React 元素(`React.Children.only`);多子元素场景强制走 `render`
|
|
256
|
+
|
|
257
|
+
### Trade-off
|
|
258
|
+
|
|
259
|
+
- **增**:~30 行 prop 桥接逻躑 + 6 个子元素识别 key
|
|
260
|
+
- **收**:demo show code 直接展示 `<Select>` / `<Input>` 真实 JSX;business 写法更声明式;AI 抓的 meta + demo 信号不再丢失控件类型
|
|
261
|
+
- **负**:不在适配表中的自定义控件上需要识别的人工介入(退化到 render prop)— 可接受
|
|
262
|
+
- **不破坏性**:`render` prop 作为 v2 首版 API 保留,仅从必填变可选
|
|
263
|
+
|
|
264
|
+
### 升级路径
|
|
265
|
+
|
|
266
|
+
- @teamix-evo/ui: minor(API 拓展,向后兼容)
|
|
267
|
+
- 现有 `render` 写法零修改可运行
|
|
268
|
+
- 推荐逐步迁到 `children` 写法以获得 demo show code 可视性
|
|
269
|
+
|
|
270
|
+
## v2.2 增补(2026-06-08)· FilterBarSearch 三段化 + Footer→Actions 收敛
|
|
271
|
+
|
|
272
|
+
### 触发动因
|
|
273
|
+
|
|
274
|
+
v2.1 落地后 review 发现三处与「组合式架构」主线不一致的设计点,本轮统一收敛:
|
|
275
|
+
|
|
276
|
+
1. **FilterBarSearch 用 `fields` 配置式**:与 FilterBarField 双形态(首选 children)API 风格不一致;不支持声明式扩展(自定义按钮图标 / 替换 Key 选择器)
|
|
277
|
+
2. **FilterBarFooter 命名偏窄**:FilterBar 此容器只放按钮组(重置/搜索/自定义动作),语义是「操作区」而非 shadcn 通用的「页脚任意内容」。生态对齐应改 `Actions`
|
|
278
|
+
3. **FilterBarFooter 用 `actions` prop 配置式**:与 shadcn DialogFooter / CardFooter / SheetFooter 全部用 children 模式不一致,AI 生成易困惑
|
|
279
|
+
|
|
280
|
+
### 决策
|
|
281
|
+
|
|
282
|
+
#### D1. FilterBarSearch 拆三段子组件
|
|
283
|
+
|
|
284
|
+
废弃 `fields` prop,改为三段 children:
|
|
285
|
+
|
|
286
|
+
```tsx
|
|
287
|
+
<FilterBarSearch>
|
|
288
|
+
<FilterBarSearchKey
|
|
289
|
+
options={[
|
|
290
|
+
{ name: 'instanceId', label: '实例 ID' },
|
|
291
|
+
{ name: 'instanceName', label: '实例名称' },
|
|
292
|
+
]}
|
|
293
|
+
/>
|
|
294
|
+
<FilterBarSearchValue />
|
|
295
|
+
<FilterBarSearchAction />
|
|
296
|
+
</FilterBarSearch>
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
| 子组件 | 职责 |
|
|
300
|
+
| ----------------------- | ---------------------------------------------------------------------------------------------------------------- |
|
|
301
|
+
| `FilterBarSearch` | 紧凑容器(`h-8` + 边框 + focus-within ring),提供 `FilterBarSearchContext`(active 字段 / options / exclusive) |
|
|
302
|
+
| `FilterBarSearchKey` | 字段切换 Select;`options: { name, label, placeholder? }[]` 声明可切字段 |
|
|
303
|
+
| `FilterBarSearchValue` | 跟随 active 字段的 Input;自动取 `active.placeholder` 或「请输入{label}」 |
|
|
304
|
+
| `FilterBarSearchAction` | 默认 🔍 图标按钮 + `triggerFilter`;children 完全替换图标内容 |
|
|
305
|
+
|
|
306
|
+
实现要点:Key 的 `options` 通过 useEffect 注册到 SearchContext;Value 从 ctx 拿 active option 的 name+label+placeholder;三段共用 `useFilterBar` 拿 `form` / `triggerFilter`。
|
|
307
|
+
|
|
308
|
+
#### D2. FilterBarFooter 重命名为 FilterBarActions
|
|
309
|
+
|
|
310
|
+
`data-slot` 同步改为 `filter-bar-actions`。`FilterBarFooter` 命名移除(v2 未发版,无破坏负担)。理由:
|
|
311
|
+
|
|
312
|
+
- shadcn 生态:DialogFooter / CardFooter / SheetFooter 是「任意底部内容」语义
|
|
313
|
+
- FilterBar 此容器只放按钮组(业务语义 = 操作区)
|
|
314
|
+
- antd / TeamixPro 业务侧肌肉记忆全部叫 `Actions` / `FilterActions`
|
|
315
|
+
|
|
316
|
+
#### D3. FilterBarActions 改 children 模式
|
|
317
|
+
|
|
318
|
+
```tsx
|
|
319
|
+
// 默认(不写 children)
|
|
320
|
+
<FilterBarActions /> // → 渲染默认「重置 + 搜索」
|
|
321
|
+
|
|
322
|
+
// 自定义(与 DialogFooter / CardFooter 一致)
|
|
323
|
+
<FilterBarActions>
|
|
324
|
+
<Button variant="ghost">清空</Button>
|
|
325
|
+
<Button variant="outline">保存条件</Button>
|
|
326
|
+
<Button>查询</Button>
|
|
327
|
+
</FilterBarActions>
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
删除 `actions` / `showReset` / `showSubmit` / `resetText` / `submitText` 五个 prop —— children 完全替换语义可直接覆盖所有定制需求。
|
|
331
|
+
|
|
332
|
+
#### D4. FilterBarActions 在 Content 内保留 col-span-full + justify-end
|
|
333
|
+
|
|
334
|
+
| 选项 | 行为 | 选择 |
|
|
335
|
+
| --------------------------------------- | --------------------------------------------------------------------- | ------- |
|
|
336
|
+
| α. 当前:`col-span-full + justify-end` | 独占一行右对齐 | ✅ 选定 |
|
|
337
|
+
| β. `grid auto-flow` 自然填充 | Field 不满栅格时同行右对齐,但独占一行时变左对齐 — **破坏可预期视觉** | 否 |
|
|
338
|
+
| γ. React.Children 数 Field 数量条件判断 | 智能但响应式 columns(`{ base, md }`)下退化;额外运行时复杂度 | 否 |
|
|
339
|
+
|
|
340
|
+
理由:方案 α 100% 视觉可预期 + 无运行时开销 + 响应式断点下仍稳定;「Field 不满栅格」长尾场景业务侧可改 columns 自行填满。
|
|
341
|
+
|
|
342
|
+
### 受影响 API(v2 未发版,不计 breaking)
|
|
343
|
+
|
|
344
|
+
| 旧 API | 新 API |
|
|
345
|
+
| --------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------- |
|
|
346
|
+
| `<FilterBarSearch fields={[...]} />` | `<FilterBarSearch><FilterBarSearchKey options={[...]} /><FilterBarSearchValue /><FilterBarSearchAction /></FilterBarSearch>` |
|
|
347
|
+
| `<FilterBarFooter />` | `<FilterBarActions />` |
|
|
348
|
+
| `<FilterBarFooter actions={...} />` | `<FilterBarActions>...</FilterBarActions>` |
|
|
349
|
+
| `<FilterBarFooter showReset={false} />` | `<FilterBarActions><Button>查询</Button></FilterBarActions>` |
|
|
350
|
+
| `FilterBarSearchField` 类型 | `FilterBarSearchOption` 类型 |
|
|
351
|
+
|
|
352
|
+
### Trade-off
|
|
353
|
+
|
|
354
|
+
- **增**:3 个 Search 子组件 + 1 个 SearchContext(~80 行);Actions 命名变更影响所有 demo 与 changeset 升级指引
|
|
355
|
+
- **收**:完成「形态由 children 决定」的最后一公里收敛(除 FilterBar Root 的 Provider props 外,所有形态决策都在 children);与 shadcn 生态肌肉记忆 100% 对齐
|
|
356
|
+
- **不引入**:mode enum / 配置式 fields / 配置式 actions 三种形式
|
|
357
|
+
|
|
358
|
+
## v2.3 增补(2026-06-08)· FilterBarField 四 layout 模式
|
|
359
|
+
|
|
360
|
+
### 触发动因
|
|
361
|
+
|
|
362
|
+
v2.2 落地后业务对照原型截图发现 Header 内字段视觉与 FilterBarSearch 容器不一致:
|
|
363
|
+
|
|
364
|
+
- v2.0 实现的 Header 字段是「label 左外 + 控件横向(控件自带边框)」—— 即 inline 风格
|
|
365
|
+
- 业务原型是「label 嵌入外框内左侧(控件去边框)」—— 即 outline 风格,与 FilterBarSearch 容器同高 + 同圆角
|
|
366
|
+
|
|
367
|
+
同时 Content 内默认 horizontal(label 左 + 控件右)对齐业务成熟表单习惯,需要长表单形态时可显式切 vertical。
|
|
368
|
+
|
|
369
|
+
### 决策
|
|
370
|
+
|
|
371
|
+
`FilterBarField` 新增 `layout` prop,4 个枚举值:
|
|
372
|
+
|
|
373
|
+
| 值 | 视觉 | 适用 section |
|
|
374
|
+
| ------------ | ----------------------------------------- | ------------------- |
|
|
375
|
+
| `inline` | label 左外 + 控件横向(控件自带边框) | Header |
|
|
376
|
+
| `outline` | label 嵌入外框内左侧(控件去边框) | Header(**默认**) |
|
|
377
|
+
| `vertical` | label 上 + 控件下(控件自带边框) | Content |
|
|
378
|
+
| `horizontal` | label 左外 + 控件右(控件占栕格列剩余宽) | Content(**默认**) |
|
|
379
|
+
|
|
380
|
+
### 默认推断
|
|
381
|
+
|
|
382
|
+
- 不传 layout 时:`section === 'header'` → `outline`;`section === 'content'` → `vertical`
|
|
383
|
+
- 用户可显式传 `layout="..."` 覆盖
|
|
384
|
+
|
|
385
|
+
### outline / horizontal 控件去边框策略
|
|
386
|
+
|
|
387
|
+
outline 容器自带边框,子控件必须去除自身边框/背景/focus ring 以避免双边框。采用 Tailwind v4 子元素选择器在 outline 容器的 className 中声明,**不动 cloneElement**(与 RHF 桥接逻辑分离):
|
|
388
|
+
|
|
389
|
+
```
|
|
390
|
+
[&_[data-slot=select-trigger]]:border-0
|
|
391
|
+
[&_[data-slot=select-trigger]]:bg-transparent
|
|
392
|
+
[&_[data-slot=select-trigger]]:shadow-none
|
|
393
|
+
[&_[data-slot=select-trigger]]:h-full
|
|
394
|
+
[&_[data-slot=select-trigger]]:focus-visible:ring-0
|
|
395
|
+
[&_[data-slot=input]]:border-0
|
|
396
|
+
[&_[data-slot=input]]:bg-transparent
|
|
397
|
+
[&_[data-slot=input]]:shadow-none
|
|
398
|
+
[&_[data-slot=input]]:h-full
|
|
399
|
+
[&_[data-slot=input]]:focus-visible:ring-0
|
|
400
|
+
[&_[data-slot=textarea]]:border-0
|
|
401
|
+
[&_[data-slot=textarea]]:bg-transparent
|
|
402
|
+
[&_[data-slot=textarea]]:shadow-none
|
|
403
|
+
[&_[data-slot=input-number]]:border-0
|
|
404
|
+
[&_[data-slot=input-number]]:bg-transparent
|
|
405
|
+
[&_[data-slot=input-number]]:shadow-none
|
|
406
|
+
[&_[data-slot=combobox-trigger]]:border-0
|
|
407
|
+
[&_[data-slot=combobox-trigger]]:bg-transparent
|
|
408
|
+
[&_[data-slot=combobox-trigger]]:shadow-none
|
|
409
|
+
```
|
|
410
|
+
|
|
411
|
+
覆盖控件:Input / Textarea / Select / InputNumber / Combobox。Checkbox / Switch / RadioGroup / Slider 不适用 outline(语义不匹配,仅限 Input 类控件),这些控件请用 inline / vertical / horizontal。
|
|
412
|
+
|
|
413
|
+
### 容器与 label 样式表
|
|
414
|
+
|
|
415
|
+
| layout | 容器 className | label className |
|
|
416
|
+
| ------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------- |
|
|
417
|
+
| `inline` | `flex items-center gap-2` | `text-xs text-muted-foreground shrink-0` |
|
|
418
|
+
| `outline` | `flex h-8 items-center overflow-hidden rounded-md border border-input bg-card focus-within:border-ring focus-within:ring-1 focus-within:ring-ring` + outline 子选择器集 | `flex h-full shrink-0 items-center pl-3 pr-2 text-xs text-muted-foreground`(不画 border-r,只靠 padding) |
|
|
419
|
+
| `vertical` | `flex min-w-0 flex-col gap-1.5` | `text-xs font-medium text-foreground` |
|
|
420
|
+
| `horizontal` | `flex items-center gap-3` | `text-xs text-muted-foreground shrink-0` |
|
|
421
|
+
|
|
422
|
+
### Trade-off
|
|
423
|
+
|
|
424
|
+
- **增**:layout prop 与 4 分支渲染(~50 行);outline 子选择器 className 集(~18 个 utility)
|
|
425
|
+
- **收**:Header 默认与业务原型、FilterBarSearch 容器视觉 100% 对齐(同 h-8 + 同 rounded-md);4 种形态全可装配;与 RHF 桥接逻辑完全解耦
|
|
426
|
+
- **不破坏**:v2 未发版,不计 breaking;v2.0〔2.2 的 stories 默认行为发生变化(Header 字段从 inline → outline)是期望内
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
# 0038. create 工程根落地 AGENTS.md 作为 skill 触发兜底
|
|
2
|
+
|
|
3
|
+
- **Status**: Accepted
|
|
4
|
+
- **Date**: 2026-06-11
|
|
5
|
+
- **Region**: 0100–0999 协议与工具
|
|
6
|
+
- **Related ADR**: [0015](0015-skill-description-trigger-contract.md)(skill description trigger 契约)、[0020](0020-design-to-tokens-skill-fusion.md)(废止"承载知识"的 AGENTS.md)、[0033](0033-entry-skill-global-only-scope.md)(entry skill 全局 scope)
|
|
7
|
+
|
|
8
|
+
## Context
|
|
9
|
+
|
|
10
|
+
[ADR 0015](0015-skill-description-trigger-contract.md) 把 skill 触发依据收敛到 SKILL.md frontmatter 的 `description`(TRIGGER / SKIP / Coordinates 三段),让"何时进上下文"成为协议字段。但 description-based 触发存在固有的**欠触发风险**:
|
|
11
|
+
|
|
12
|
+
- Qoder / Claude Code / Cursor 各自对 description 的扫描时机、token 预算、缓存策略不同,跨 IDE 命中率不一致
|
|
13
|
+
- 用户 prompt 的措辞稍偏("调一下这个页面" / "改一下这个" / "顺便写个" 等模糊措辞)就可能命中阈值之下,design / code skill 静默不上
|
|
14
|
+
- 早期对话先聊需求再写代码时,trigger 关键词可能根本没出现;等真要写代码时上下文已长,skill 不一定还在窗口里
|
|
15
|
+
|
|
16
|
+
业界两种成熟兜底方案:
|
|
17
|
+
|
|
18
|
+
- **OpenAI Codex / Cursor / Sourcegraph 等正在收敛 `AGENTS.md`** —— 项目根的 AGENTS.md 被 IDE 默认扫描进上下文,用作"无论用户说什么,AI 在这个工程里都先看一眼"的元入口
|
|
19
|
+
- **Claude Code 同时支持 `CLAUDE.md` 与 `AGENTS.md`** —— 二者机制相同,AGENTS.md 是跨 IDE 收敛后的事实标准
|
|
20
|
+
|
|
21
|
+
[ADR 0020](0020-design-to-tokens-skill-fusion.md) 已明确**禁止**在装机产物里落 AGENTS.md,但其禁止的对象是"承载设计纲领 / 品牌 / patterns 的 AGENTS.md"——理由是双源失同步、装机产物臃肿、AI 单次读 skill 即可获完整上下文。**本 ADR 启用的是另一类性质完全不同的 AGENTS.md**:只列已装 skill 的触发摘要,不复制 skill 正文,不承载任何规则本身。
|
|
22
|
+
|
|
23
|
+
不能不做:
|
|
24
|
+
|
|
25
|
+
- create-teamix-evo 落地的工程默认装两个 variant skill(design + code),但用户实际编码时命中率不可观测
|
|
26
|
+
- 业务侧反馈:"AI 经常不主动读 skill,要手动 @ 一下才行"——正是 description 欠触发的症状
|
|
27
|
+
- 没有兜底机制时,skill 投入越多边际收益越低(写得再好不触发等于零)
|
|
28
|
+
|
|
29
|
+
## Options Considered
|
|
30
|
+
|
|
31
|
+
| 选项 | 优点 | 缺点 |
|
|
32
|
+
| --------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------- |
|
|
33
|
+
| A. 仅在 SKILL.md 的 description 里堆更多 trigger 关键词 | 不破坏 ADR 0020 字面意思 | 治标不治本;description 已有 ~400 字符压力(ADR 0015);跨 IDE 扫描差异仍在;过触发风险反而上升 |
|
|
34
|
+
| B. 落 CLAUDE.md + .qoder/AGENTS.md(IDE 各自子目录) | 各 IDE 各取所需 | 工程根失去通用入口;多文件需同步;AGENTS.md 收敛事实标准被绕开 |
|
|
35
|
+
| **C. 工程根落 AGENTS.md,内容动态从已装 SKILL.md frontmatter 抽取(选定)** | 真单源(skill 改 description 自动生效);与业界标准对齐;不重复 skill 正文,不违反 ADR 0020 实质边界 | 与 ADR 0020 字面冲突,需本 ADR 显式划清"指路型 ≠ 知识型"边界 |
|
|
36
|
+
|
|
37
|
+
## Decision
|
|
38
|
+
|
|
39
|
+
### 1. 性质定位:指路兜底,非知识承载
|
|
40
|
+
|
|
41
|
+
- 工程根 `AGENTS.md` **仅承载已装 skill 的触发摘要**,不复制任何 skill 正文
|
|
42
|
+
- 内容上限:≤ 50 行;每个 skill ≤ 10 行(一句能力声明 + TRIGGER + SKIP + 文件位置)
|
|
43
|
+
- 规则、生成流程、boundaries、checklist 等**全部仍在 skill 内**,AGENTS.md 只是"看到这些场景请先去读对应 skill"的索引
|
|
44
|
+
|
|
45
|
+
### 2. 与 ADR 0020 的边界
|
|
46
|
+
|
|
47
|
+
| 维度 | ADR 0020 禁止的 AGENTS.md | 本 ADR 启用的 AGENTS.md |
|
|
48
|
+
| ---------- | --------------------------------------- | ---------------------------------------------- |
|
|
49
|
+
| 内容 | 设计纲领 / 品牌 / patterns / philosophy | 已装 skill 的触发摘要(TRIGGER / SKIP) |
|
|
50
|
+
| 来源 | 模板手写 | 运行时从 SKILL.md frontmatter description 解析 |
|
|
51
|
+
| 单源关系 | 与 skill 双源失同步 | 与 skill description 单源(动态生成) |
|
|
52
|
+
| 体积 | 数百行 | ≤ 50 行 |
|
|
53
|
+
| 失同步代价 | skill 改了,AGENTS.md 没改 → 规则冲突 | 重新生成即对齐,无人工双写 |
|
|
54
|
+
|
|
55
|
+
ADR 0020 §1 / §6 / §7 的废止结论**不被本 ADR 覆盖**——设计纲领 / patterns / brand 仍只在 skill 中维护。
|
|
56
|
+
|
|
57
|
+
### 3. 内容范围
|
|
58
|
+
|
|
59
|
+
- **包含**:装在工程级(scope: project)的 variant skill —— `teamix-evo-design-<variant>`、`teamix-evo-code-<variant>`
|
|
60
|
+
- **不包含**:`teamix-evo-manage` —— 按 [ADR 0033](0033-entry-skill-global-only-scope.md),entry skill 是全局 scope 由用户独立装,不进工程级触发兜底
|
|
61
|
+
- 未来新增的工程级 skill 自动加入(generator 按 `.teamix-evo/skills/` 目录扫描)
|
|
62
|
+
|
|
63
|
+
### 4. 状态:regenerable
|
|
64
|
+
|
|
65
|
+
- 同 `.teamix-evo/manifest.json`,重跑 `npm create teamix-evo` 或 `teamix-evo skills add` 时刷新
|
|
66
|
+
- 用户的工程自定义内容**不写入此文件**——AGENTS.md 在本 ADR 下是"machine-owned"
|
|
67
|
+
|
|
68
|
+
### 5. 文件位置:项目根 `AGENTS.md`
|
|
69
|
+
|
|
70
|
+
- 不落 `CLAUDE.md`、`.cursor/rules/`、`.qoder/AGENTS.md` 等 IDE 专有路径
|
|
71
|
+
- 业界正在收敛 AGENTS.md 为跨 IDE 事实标准;Claude Code 已支持 AGENTS.md,单文件即可满足主流 IDE
|
|
72
|
+
- 如未来某 IDE 强制要求专有路径,再起独立 ADR
|
|
73
|
+
|
|
74
|
+
### 6. 落地范围(本期)
|
|
75
|
+
|
|
76
|
+
- create-teamix-evo orchestrator 在 skills add 之后插入 `generateAgentsMd` 步骤
|
|
77
|
+
- `teamix-evo skills add` CLI 主动重写 AGENTS.md(提到 core API 层)暂不在本期;本期只在 create 链路落,待 v0.7 跟随 skills CLI 演进同步扩展
|
|
78
|
+
|
|
79
|
+
## Consequences
|
|
80
|
+
|
|
81
|
+
- **Positive**:
|
|
82
|
+
- 欠触发兜底:IDE 默认扫工程根 AGENTS.md,用户 prompt 模糊时 skill 触发条件已被预热进上下文
|
|
83
|
+
- 真单源:内容从 SKILL.md frontmatter 抽取,skill 改 description 重新生成即对齐,无双写
|
|
84
|
+
- 跨 IDE 一致:AGENTS.md 是 OpenAI / Anthropic / Cursor / Qoder 等正在收敛的事实标准
|
|
85
|
+
- 与 ADR 0020 边界清晰:本 ADR 启用"指路型",0020 仍禁止"知识型",两者并存不冲突
|
|
86
|
+
- **Negative**:
|
|
87
|
+
- 工程根多一个文件(≤ 50 行,可接受)
|
|
88
|
+
- frontmatter 解析逻辑在 create 包内手写(~30 行简易 YAML 提取,未引入 gray-matter 依赖)
|
|
89
|
+
- 未来 SKILL.md frontmatter 结构变化时 generator 需同步更新(已有 ADR 0015 约束 description 三段结构,变化频率低)
|
|
90
|
+
- **Trade-off**:
|
|
91
|
+
- 用"工程根多一个 50 行的 regenerable 文件 + ADR 划边界成本"换"description 欠触发风险大幅缓解 + 跨 IDE 命中可观测"
|
|
92
|
+
- 放弃"装机产物绝对干净"的极简主义,换"AI 触发可靠性"
|
|
93
|
+
|
|
94
|
+
## Source
|
|
95
|
+
|
|
96
|
+
- 2026-06-11 用户与 AI 的设计讨论 —— 担忧用户 prompt 措辞偏移导致 design / code skill 欠触发,提议在 create 落地时加 AGENTS.md 引导
|
|
97
|
+
- [ADR 0015](0015-skill-description-trigger-contract.md):description 三段契约(TRIGGER / SKIP / Coordinates)
|
|
98
|
+
- [ADR 0020](0020-design-to-tokens-skill-fusion.md):废止"承载知识"的 AGENTS.md(本 ADR 划清边界,不覆盖 0020)
|
|
99
|
+
- [ADR 0033](0033-entry-skill-global-only-scope.md):entry skill 全局 scope,不进工程级触发兜底
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
# 0040. 组件源码层升级流程(ui / biz-ui staging-driven upgrade)
|
|
2
|
+
|
|
3
|
+
- **Status**: Accepted
|
|
4
|
+
- **Date**: 2026-06-11
|
|
5
|
+
- **Region**: 0100–0999 协议与工具
|
|
6
|
+
- **Related ADR**: 0014, 0019, 0036
|
|
7
|
+
|
|
8
|
+
## Context
|
|
9
|
+
|
|
10
|
+
ADR 0019 D7 落地了 tokens / skills 的升级链路,但显式注释中标注 `ui` 不进入 `runProjectUpdate` orchestrator —
|
|
11
|
+
`packages/cli/src/core/project-update.ts` L22-L25 写道:
|
|
12
|
+
|
|
13
|
+
> `ui` is intentionally NOT updated by this orchestrator — UI components are frozen (ADR 0019)…
|
|
14
|
+
|
|
15
|
+
ADR 0006(UI 升级无 baseline)已被 ADR 0036 supersede,但 0036 只重建了 ui 基线本身,没给出"装机后如何升级"的路径。
|
|
16
|
+
结果是:
|
|
17
|
+
|
|
18
|
+
1. 用户装了 `@teamix-evo/ui` v0.7 的 button.tsx 之后,新版 v0.8 改了 cva variants —— CLI 没法引导他升
|
|
19
|
+
2. 用户从 shadcn 原生项目接入 teamix-evo —— 检测到 `src/components/ui/` 但缺乏迁移路径
|
|
20
|
+
3. biz-ui 同上,且多了变体维度
|
|
21
|
+
|
|
22
|
+
约束:
|
|
23
|
+
|
|
24
|
+
- **frozen 边界不可破**:CLI 永不直接覆盖 `src/components/ui/**`([ADR 0019 §D4](0019-project-upgrade-flow.md))
|
|
25
|
+
- **目标用户是小白**:只能跟 AI 对话操作,AI 必须能在 1-2 句话说清"哪些组件要升、风险多大"
|
|
26
|
+
- **AI token 预算有限**:不能让 AI 现读全量源码做 diff
|
|
27
|
+
- 装机时 [ui-installer.ts](../../packages/cli/src/core/ui-installer.ts) 已经把"客户端 alias rewrite 后的 hash"
|
|
28
|
+
存进 `.teamix-evo/manifest.json` 的 `installed.resources[i].hash`,可作为升级基线
|
|
29
|
+
|
|
30
|
+
## Options Considered
|
|
31
|
+
|
|
32
|
+
| 选项 | 优点 | 缺点 |
|
|
33
|
+
| ------------------------------------------------------ | ------------------------------------ | -------------------------------------------------------------------------------- |
|
|
34
|
+
| A. registry 加 component 级 hash + version | CLI 不用读 source 也能判断"远端变了" | 装机时 CLI 必读 source(要做 import rewrite),加 hash 收益边际;schema 改动放大 |
|
|
35
|
+
| B. 不改 schema,CLI 现场算 latest hash 跟 installed 比 | 无 schema 改动;逻辑直白 | 每次 update 都要读全量 source(已经必读,无额外成本) |
|
|
36
|
+
| C. 全量源码丢给 AI 自己 diff | 零 CLI 工程量 | AI token 暴增;quest 节奏卡顿;判断不一致 |
|
|
37
|
+
|
|
38
|
+
## Decision
|
|
39
|
+
|
|
40
|
+
新增"组件源码层升级"闭环,由 CLI 产 staging、skill 引导落盘,分 6 段决策:
|
|
41
|
+
|
|
42
|
+
1. **D1 流派识别(installed manifest 真源)**:以 `.teamix-evo/manifest.json` 为强信号源,按"有无 ui 包记录 ×
|
|
43
|
+
有无 components.json × 有无未登记 ui 文件"四档判定 `teamix-evo` / `mixed` / `shadcn-native` / `custom-only`,
|
|
44
|
+
不靠 AST 启发式扫描。
|
|
45
|
+
|
|
46
|
+
2. **D2 基线对比策略**:复用 `installed.resources[i].hash`(pre-transform 后)。CLI 升级时把远端 latest source
|
|
47
|
+
按客户当前 `aliases` 做 `rewriteImports`、再 `computeHash`,与 installed 侧 hash 对比 —— 一致即 unchanged,
|
|
48
|
+
不一致即变更。**registry schema 不动**(采纳选项 B)。
|
|
49
|
+
|
|
50
|
+
3. **D3 risk-level 六档**:unchanged / upgradable-low / upgradable-medium / risky / breaking / foreign。CLI 用
|
|
51
|
+
regex 启发(export 增删、cva variants 增删)打分,AI 二次确认。AST 级判定推后,当前规模够用。
|
|
52
|
+
|
|
53
|
+
4. **D4 frozen 边界**:CLI 只产 staging 物料 + 元 manifest,**永不写** `src/components/ui/**`、
|
|
54
|
+
`src/components/biz-ui/**`。`teamix-evo ui upgrade --apply` 输出错误并指引用户走 skill。
|
|
55
|
+
|
|
56
|
+
5. **D5 命令面(双入口)**:
|
|
57
|
+
|
|
58
|
+
- `teamix-evo update` orchestrator 加 `ui` / `biz-ui` step(默认 dry-run-only,全量 staging)
|
|
59
|
+
- 独立子命令 `teamix-evo ui upgrade [id]` / `teamix-evo biz-ui upgrade [id]`(单组件 staging)
|
|
60
|
+
|
|
61
|
+
6. **D6 skill 接管语义层**:`teamix-evo-upgrade` skill 加 "Component Source Upgrade" 章节,按 risk 分批引导:
|
|
62
|
+
unchanged 跳过 / upgradable-low 批量 confirm / medium 单组件 confirm / risky+breaking 必逐个对话。
|
|
63
|
+
`teamix-evo-manage` 命令表加 ui/biz-ui upgrade 入口 + 新场景 `ui-source-upgrade`。
|
|
64
|
+
|
|
65
|
+
Staging 目录结构:
|
|
66
|
+
|
|
67
|
+
```
|
|
68
|
+
.teamix-evo/.upgrade-staging/<category>-<fs-iso-ts>/
|
|
69
|
+
├── meta.json # UpgradeStagingManifest(schemaVersion=1)
|
|
70
|
+
├── <id>/
|
|
71
|
+
│ ├── current.tsx # 从 installed.target 复制
|
|
72
|
+
│ └── incoming.tsx # 远端 source + alias rewrite
|
|
73
|
+
└── ...
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
> v1 不预生成 `diff.unified.patch`——`UpgradeStagingDiffSchema.diffRelPath` 保留为可选字段供后续 v2 引入;v1 由 skill 直接对 `current.tsx` 与 `incoming.tsx` diff。
|
|
77
|
+
|
|
78
|
+
新增 schema:`UpgradeStagingManifestSchema` 落到 `@teamix-evo/registry`,被 CLI / skill 共用。
|
|
79
|
+
|
|
80
|
+
## Consequences
|
|
81
|
+
|
|
82
|
+
- **Positive**:
|
|
83
|
+
|
|
84
|
+
- 升级闭环完整:用户能用 `teamix-evo ui upgrade` + skill 引导走完版本演进
|
|
85
|
+
- 守住 frozen:CLI 物料归 CLI,业务代码归用户 + AI,边界清晰
|
|
86
|
+
- quest 友好:AI 拿到的是已分级的 manifest,不需现读源码
|
|
87
|
+
- shadcn-native 项目有了迁移入口(lineage 报告 + skill 引导,而非自动覆盖)
|
|
88
|
+
|
|
89
|
+
- **Negative**:
|
|
90
|
+
|
|
91
|
+
- `.teamix-evo/.upgrade-staging/` 会占磁盘(每次 update 一份,需要 skill 主动归档到 `.processed/`)
|
|
92
|
+
- regex risk 启发有误判风险(cva 解析、export 检测都不严格)→ AI 兜底 + 后续 AST 升级
|
|
93
|
+
- biz-ui 多 variant 维度,staging 一次只覆盖一个 variant(用户切 variant 时另起 staging)
|
|
94
|
+
|
|
95
|
+
- **Trade-off**:
|
|
96
|
+
- 选 B 不改 registry schema:节省了协议工程量,代价是每次 update 必读 latest source(其实本来就要读)
|
|
97
|
+
- 选 dry-run 而非 `--apply`:守住边界,代价是必须依赖 skill 才能闭环(不能裸命令完成升级)
|
|
98
|
+
|
|
99
|
+
## Source
|
|
100
|
+
|
|
101
|
+
- 用户对话 2026-06-11:将 `teamix-evo-manage` 闭环扩展到 ui / biz-ui 升级
|
|
102
|
+
- ADR 0019 D7 留白
|
|
103
|
+
- ADR 0036(ui v2 baseline 重建后无升级路径)
|
|
104
|
+
- `packages/cli/src/core/project-update.ts` L22-L25(既有"ui not updated"注释将被本 ADR 推翻)
|