@minonl/dia 0.0.15 → 0.0.16
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 +6 -0
- package/dist/cli.js +1589 -31
- package/docs/cli.md +3 -2
- package/docs/design/graph-prio-dag.md +85 -0
- package/docs/design/graph-ui-m1.md +106 -0
- package/docs/design/graph-web-m1.md +113 -0
- package/docs/design/graph-web-m2.md +92 -0
- package/docs/design/graph-web-mockup.html +49 -25
- package/docs/design/graph-web.md +9 -4
- package/docs/design/seat-scheduling-m2.md +138 -0
- package/docs/index.md +8 -1
- package/docs/install-guard-design.md +148 -0
- package/docs/kit-install.md +7 -0
- package/docs/kit-layer.md +78 -0
- package/docs/migrations/data-root-2026-11.md +3 -2
- package/docs/status.md +2 -0
- package/kits/git/dia.json +10 -0
- package/kits/git/knowledge/hooks.md +30 -0
- package/kits/git/knowledge/worktree.md +34 -0
- package/kits/node/dia.json +10 -0
- package/kits/node/knowledge/hook-managers.md +17 -0
- package/kits/node/knowledge/pnpm.md +34 -0
- package/package.json +1 -1
- package/packages/wt/bin/dia-wt +15 -0
- package/packages/wt/tests/adopt.test.sh +8 -0
package/docs/design/graph-web.md
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
# dia graph Web 视觉设计稿(v0.3)
|
|
2
2
|
|
|
3
|
+
> **落地状态(2026-11-06)**:M1 已实现 `dia graph --export-html`(真实账本 → 单文件静态 HTML,无网络);
|
|
4
|
+
> 数据模型/契约/安全边界见 [graph-web-m1.md](graph-web-m1.md)。live 服务(M2)未做。
|
|
5
|
+
|
|
3
6
|
> 静态 mockup:[`graph-web-mockup.html`](graph-web-mockup.html)(浏览器直接打开,无网络/无依赖)
|
|
4
7
|
> 数据契约:`dia graph --json`(schema 1);时间线所需 start/end 为后续扩展字段。
|
|
5
8
|
|
|
@@ -144,10 +147,12 @@ mockup 数据改为 **100 个任务、时间跨度 30 天**,用来验证真实
|
|
|
144
147
|
|
|
145
148
|
- **时间窗(默认近 7 天,按开始时间,结束时间不限)**:只显示 `start >= 今天 - N` 的任务,
|
|
146
149
|
长任务即使结束在很久以后也照常显示;时间线轴自动从 `-N` 延伸到最晚的 `end`;
|
|
147
|
-
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
150
|
+
- **搜索即选择(token 输入框)**:聚焦就出下拉(列出当前时间窗全部任务,按优先级/开始时间排序),
|
|
151
|
+
输入 ID / 名称 / owner 实时过滤;选中项**回显成 tag**(`t-003 ×`)留在输入框里,可多选;
|
|
152
|
+
`↑↓` 选择、`Enter` 加入、`Esc` 收起、`Backspace` 删除最后一个 tag、点 `×` 删除单个;
|
|
153
|
+
已选 tag 用蓝底高亮,下拉里带 `✓`;选择状态写进 URL(`sel=t-003,t-006`,向后兼容旧的 `focus=`);
|
|
154
|
+
- **邻域聚焦(类 nx graph focus)**:选中任务后只保留这些任务的依赖邻域(多个取并集),
|
|
155
|
+
深度可切 `1 层 / 2 层 / 全链路`,`清空` 回全量;双击卡片等同于点选该任务;
|
|
151
156
|
- 被过滤掉的任务不计入概览统计,标题显示 `显示 N / 总数`。
|
|
152
157
|
|
|
153
158
|
## 11. 时间线滚动与缩放
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
# 固定席位 + 上下文亲和(M2 设计)
|
|
2
|
+
|
|
3
|
+
> 状态:设计定稿(2026-11-06,未实现)|依赖:`graph-prio-dag.md`(eff/rank)、`graph-ui-m1.md`(面板)
|
|
4
|
+
> 红线:**只读建议;不写 tasks 账本、不常驻守护、不抢占、不自动 force 接管**。
|
|
5
|
+
|
|
6
|
+
## 1. 目标 / 非目标
|
|
7
|
+
|
|
8
|
+
**目标**:席位(并发会话)数量固定时,回答「每个席位现在接哪件」——同时考虑价值(rank)、约束(依赖/claim)与**断链成本**(上下文)。
|
|
9
|
+
|
|
10
|
+
**非目标**:自动领取/自动开工;跨机席位发现;任务级分布式锁;把 rank 变成门禁。
|
|
11
|
+
|
|
12
|
+
## 2. 名词与事实源
|
|
13
|
+
|
|
14
|
+
| 名词 | 定义 | 事实源 |
|
|
15
|
+
|---|---|---|
|
|
16
|
+
| seat | 一个在线会话(opencode/dsh 进程) | `~/.dia/collab/sessions/*.json` 的 `last_seen` |
|
|
17
|
+
| presence | `now - last_seen <= TTL`(默认 30m,`--presence-ttl`) | 同上 |
|
|
18
|
+
| claim | **路径**租约(谁在改哪个文件) | `~/.dia/collab/claims.jsonl`(TTL 默认 4h) |
|
|
19
|
+
| owner | 任务声明的负责人(不发租约) | `tasks/ledger.jsonl` 的 add/plan |
|
|
20
|
+
| busy | 席位有 `doing` 任务 | 任务 status |
|
|
21
|
+
| 亲和 | 任务与该席位近期工作面的重叠度 | 任务 touches + 席位最近任务 |
|
|
22
|
+
|
|
23
|
+
**分工写死**:`rank` 只回答「先做什么」;`claim` 只回答「谁能改哪条路径」;`owner` 只回答「谁负责」,三者不得互相替代。
|
|
24
|
+
|
|
25
|
+
## 3. 席位状态机(全派生,零写入)
|
|
26
|
+
|
|
27
|
+
```
|
|
28
|
+
online & 无 doing online & doing(todo/doing)
|
|
29
|
+
┌──────── idle ◄────────┐ ┌──────── busy
|
|
30
|
+
│ │ │
|
|
31
|
+
│ (接新任务即 busy) └────────┤
|
|
32
|
+
└──────── holding ◄──────┘ doing 但 status∈{review,blocked}(释放执行位,保留上下文)
|
|
33
|
+
│
|
|
34
|
+
last_seen 超 TTL
|
|
35
|
+
▼
|
|
36
|
+
offline(不再分配;其 doing 任务标 stale,需人确认)
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
- `holding` 仍可接新任务(执行位空闲),但提示「上下文占用 N 个」;N>3 建议先 handoff;
|
|
40
|
+
- `offline` 的 `doing` 任务**回候选集**但标 `reason:'seat_offline'`,不自动改 status/owner;
|
|
41
|
+
- 无自动回收路径:busy→idle 只在任务状态变化时派生,进程退出即 offline(靠 TTL)。
|
|
42
|
+
|
|
43
|
+
## 4. 亲和度(affinity)
|
|
44
|
+
|
|
45
|
+
```
|
|
46
|
+
aff(t, seat) = 3 * ownerMatch # seat.session == t.owner
|
|
47
|
+
+ 2 * splitGroup # 与席位最近任务共享 split_from 或同一父
|
|
48
|
+
+ 2 * jaccard # touches 的 Jaccard,[0,1],0/0 = 0
|
|
49
|
+
+ 1 * moduleOverlap # 路径前 2 段前缀相同
|
|
50
|
+
cost(t, seat) = 1 - aff/8 # ∈ [0,1],越小越省
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
- `seatTouches` = 该席位最近 H=5 个任务(doing/review/done)的 touches 并集;
|
|
54
|
+
- 分量量纲固定:owner/split/module 为 0/1,jaccard 归一化到 [0,1];`aff` 最大 8;
|
|
55
|
+
- **算例**(必须进单测):
|
|
56
|
+
|
|
57
|
+
| 场景 | aff | cost | 结论 |
|
|
58
|
+
|---|---|---|---|
|
|
59
|
+
| 同 owner(3) | 3 | 0.625 | 可接,但不算强亲和 |
|
|
60
|
+
| 同 split 组(2)+ jaccard 0.5(1) | 3 | 0.625 | 同上 |
|
|
61
|
+
| 同 owner + 同模块(4) | 4 | 0.5 | 达到「续做」阈值 |
|
|
62
|
+
| 完全同 touches(jaccard=1,2) | 2 | 0.75 | 单靠路径重叠不续做 |
|
|
63
|
+
| owner+split+jaccard1+module(8) | 8 | 0 | 强续做 |
|
|
64
|
+
|
|
65
|
+
> 阈值:**续做** `cost ≤ 0.5`;**换人** 当 `cost > 0.5` 且存在另一席位 `cost 低 ≥ 0.25`。单靠「路径重叠」不足以免换人——避免把不相关任务硬塞给同一席位。
|
|
66
|
+
|
|
67
|
+
## 5. 分配算法(rank 优先,亲和定席位)
|
|
68
|
+
|
|
69
|
+
```
|
|
70
|
+
候选 = startable ∧ rank≠null ∧ 无「路径 claim 冲突」
|
|
71
|
+
对候选按 rank 升序:
|
|
72
|
+
在空闲/持有席位中,选 (cost, seat.id) 最小者
|
|
73
|
+
若该席位 busy → 跳过(不抢占)
|
|
74
|
+
输出 assignments;未被分配的进入 unassigned,带原因
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
- 复杂度 `O(N·S)`(N 候选、S 席位),500×3 < 1ms;
|
|
78
|
+
- 确定性 tiebreak:`(cost, declared prio index, task.id, seat.id)`;
|
|
79
|
+
- **不做**「全局最优分配」:席位小、账本变动频繁,贪心 + 可解释优先;
|
|
80
|
+
- 显式反例(写入文档,防止误读):rank 1 但亲和极差的任务仍会被先分配——这是「价值优先」的代价;若需要「上下文优先」模式,M3 再加 `--by=affinity`。
|
|
81
|
+
|
|
82
|
+
## 6. CLI 契约
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
dia graph --next [n] --seats 2 --presence-ttl 30m --now 2026-11-06T10:00:00 --json
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
```json
|
|
89
|
+
{
|
|
90
|
+
"schema": 1,
|
|
91
|
+
"policy": "seats-v1",
|
|
92
|
+
"seats": [{"seat":"ses_a","state":"idle","presence_at":"2026-11-06T09:58:12Z","focus":null}],
|
|
93
|
+
"assignments": [{"seat":"ses_a","task":"t-3c1f","rank":1,"eff":"P0","cost":0.25,"affinity":{"owner":1,"split":0,"jaccard":1,"module":0},"why":"同 owner + 路径重叠 1.0"}],
|
|
94
|
+
"unassigned": [{"task":"t-9","rank":4,"reason":"no_seat|seat_offline|claim_conflict"}],
|
|
95
|
+
"conflicts": [{"task":"t-9","kind":"claim_overlap","detail":"src/x.ts 被 ses_b claim"}],
|
|
96
|
+
"metrics": {"switches": 1, "handoff_rate": 0.0, "idle_slots": 0}
|
|
97
|
+
}
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
- **不含墙钟**:只出 `presence_at`(ISO);`--now` 仅用于计算 state/交接判断,不落输出;
|
|
101
|
+
- 同一 `--now` + 同一账本 → 字节相等;打乱任务顺序结果不变;
|
|
102
|
+
- 退出码:0 正常(含无任务)、2 参数错、3 依赖环(排序不可用)。
|
|
103
|
+
|
|
104
|
+
## 7. 链式续做与 handoff
|
|
105
|
+
|
|
106
|
+
默认**续做**(cost ≤ 0.5 且席位非 busy)。**必须打断**的四条:
|
|
107
|
+
1. 出现更高 `rank` 且其它席位都接不住的任务(价值让位);
|
|
108
|
+
2. 下一环 touches 与席位工作面**无重叠**(模块切换,cost > 0.5);
|
|
109
|
+
3. 当前任务进入 review/blocked(执行位空出,可换手);
|
|
110
|
+
4. 上下文将满(启发式:本会话完成任务数 ≥K 或已有 1 次 handoff 建议)。
|
|
111
|
+
|
|
112
|
+
handoff 内容固定五段:目标状态 / 已完成 / 未完成 / 触碰文件 / 下一步命令;通道用 `dia msg send --kind handoff`,交接后**人工重新 claim**,不自动转移。
|
|
113
|
+
|
|
114
|
+
## 8. 指标(只读派生)
|
|
115
|
+
|
|
116
|
+
- `switches`:同一任务换席位的次数;
|
|
117
|
+
- `handoff_rate`:有 handoff 消息的任务 / 被接手任务;
|
|
118
|
+
- `idle_slots`:在线但无任务的席位数;
|
|
119
|
+
- 采样窗口由调用方传 `--since`,不落账本;用于评估「固定席位是否够用」。
|
|
120
|
+
|
|
121
|
+
## 9. 边界
|
|
122
|
+
|
|
123
|
+
- **claim 与 presence 冲突**:claim 4h 有效但会话 30m 离线 → 标 `conflicts: claim_held_offline`,任务进 unassigned 且**不自动 force**(人工 `dia claim --force` 或等 TTL);
|
|
124
|
+
- 席位掉线:`offline`,其任务回候选并标 stale;
|
|
125
|
+
- S > 任务数:多余席位 idle,不为填满降低门槛;
|
|
126
|
+
- S=0:纯建议模式,全量 unassigned,exit 0;
|
|
127
|
+
- 跨机:只读本机 `~/.dia`,其它机器会话视为离线;
|
|
128
|
+
- 空 touches:jaccard=0,不除零;
|
|
129
|
+
- 环内/终态任务不进候选。
|
|
130
|
+
|
|
131
|
+
## 10. 验收
|
|
132
|
+
|
|
133
|
+
- 打乱任务顺序、固定 `--now` 两次输出字节一致;
|
|
134
|
+
- busy 席位不被二次分配;holding 可接新任务;
|
|
135
|
+
- affinity 五个算例硬断言(含 0/0);
|
|
136
|
+
- claim 冲突进 conflicts 且不抢占;
|
|
137
|
+
- `--seats 1` 与 `graph --next` 单项一致(同一排序源);
|
|
138
|
+
- e2e:两会话(`DIA_SESSION_ID`)跑 `whoami` + doing → 各得一任务;杀一个会话超 TTL 后任务回落。
|
package/docs/index.md
CHANGED
|
@@ -17,7 +17,9 @@ Agent 入口:[`llms.txt`](https://github.com/minonl/dia/blob/main/llms.txt)(
|
|
|
17
17
|
| [用户入口:人在哪进,agent 叫什么](entrypoints.md) | 一句话:入口仍是 opencode/dsh(和 yan 一样),但 agent 名是 dabo,不是 dia;dia 是控制面命令,不是人格。 |
|
|
18
18
|
| [进化:dia 的第一性定位](evolution-process.md) | 本文不是功能清单,是过程定义。 |
|
|
19
19
|
| [宿主控制面:黑盒边界与内部 seam(2026-10)](host-control-surface.md) | 问题:把 opencode(含 v2)与 dsh 当"黑盒宿主"时,我们能在哪些边界上控制它们? |
|
|
20
|
+
| [安装防呆:脚本安装不许抢走 npm 全局安装](install-guard-design.md) | 日期:2026-11-06 | 状态:已实现(D0.65) | 触发:同一台 Linux 机器 3 次出现 ~/.local/bin/dia 被指回脚本安装 |
|
|
20
21
|
| [Kit:内容包安装(persona 的第二个半边)](kit-install.md) | 前置:docs/persona-install.md(persona = 人格,kit = 内容)。 |
|
|
22
|
+
| [Kit 层:把「经验」从 persona 里抽出来(设计 v0.1)](kit-layer.md) | 2026-11-06 用户定调:dws-hub 作为 channel 层重构,同时把 git / node 这类通用工程经验抽成 kit 层, |
|
|
21
23
|
| [模型基准与 Sidecar 监控(A+B 一起)](model-baseline-sidecar.md) | A:首次使用先跑基准,用数据决定配置。 |
|
|
22
24
|
| [模型行为调控(客户端侧)](model-behavior.md) | 边界(用户定):只使用各平台提供的通用模型;不做本地构建/微调;不依赖多数模型不支持的推理与输入输出控制参数; |
|
|
23
25
|
| [dia monorepo:框架模块的归属](monorepo.md) | 结论:dia-job 回 dia monorepo,作为 packages/job;kb 作为 packages/kb。 |
|
|
@@ -92,5 +94,10 @@ Agent 入口:[`llms.txt`](https://github.com/minonl/dia/blob/main/llms.txt)(
|
|
|
92
94
|
|
|
93
95
|
| 文档 | 一句话 |
|
|
94
96
|
|---|---|
|
|
95
|
-
| [
|
|
97
|
+
| [优先级 × 依赖:任务编排语义(M1)](design/graph-prio-dag.md) | 状态:已实现(2026-11-06)|代码:src/plan.ts(派生纯函数)、src/task.ts(split)、src/graph.ts(契约) |
|
|
98
|
+
| [关系图界面:派生量上屏(M1 UI)](design/graph-ui-m1.md) | 状态:设计定稿(2026-11-06)|实现:src/graph-html/(待接)|上游:graph-prio-dag.md |
|
|
99
|
+
| [dia graph Web(M1)设计:真实数据 → 单文件静态导出](design/graph-web-m1.md) | 状态:M1 设计(2026-11-06)|前置:graph-web.md(视觉稿 v0.3)、graph-web-mockup.html(交互原型) |
|
|
100
|
+
| [graph live 服务(M2 设计,可选包)](design/graph-web-m2.md) | 状态:设计定稿(2026-11-06,未实现)|前置:graph-web-m1.md(静态导出)、graph-ui-m1.md、seat-scheduling-m2.md |
|
|
101
|
+
| [dia graph Web 视觉设计稿(v0.3)](design/graph-web.md) | 落地状态(2026-11-06):M1 已实现 dia graph --export-html(真实账本 → 单文件静态 HTML,无网络); |
|
|
102
|
+
| [固定席位 + 上下文亲和(M2 设计)](design/seat-scheduling-m2.md) | 状态:设计定稿(2026-11-06,未实现)|依赖:graph-prio-dag.md(eff/rank)、graph-ui-m1.md(面板) |
|
|
96
103
|
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
# 安装防呆:脚本安装不许抢走 npm 全局安装
|
|
2
|
+
|
|
3
|
+
> 日期:2026-11-06 | 状态:**已实现(D0.65)** | 触发:同一台 Linux 机器 3 次出现 `~/.local/bin/dia` 被指回脚本安装
|
|
4
|
+
> 上游:三名独立子会话的 grill(架构/契约、红队、兼容迁移),结论一致 | 判据:`bash tests/install-guard.test.sh`
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## 1. 现象与根因
|
|
9
|
+
|
|
10
|
+
**现象**:`~/.local/bin/dia` 从 npm 包 bin 被改成 `~/.local/share/dia/bin/dia.js`,随后:
|
|
11
|
+
|
|
12
|
+
- `paths.installDir()` 按 `import.meta.url` 反推安装根 → 落在脚本安装目录
|
|
13
|
+
- 内置能力包 `kits/dia-msg` 去旧目录找 → `missing-kit` / `E_KIT_EMPTY`
|
|
14
|
+
- `dia doctor` 红、`dia update` 走脚本模式(自己 `git pull && bun run build && bash install.sh`,再抢一次)
|
|
15
|
+
|
|
16
|
+
**根因**:`install.sh` 第 58 行 **无条件** `ln -sfn "$DIA_INSTALL_DIR/bin/dia.js" "$DIA_BIN_DIR/dia"`。
|
|
17
|
+
两种安装共存本身不是错误(npm 可以装在别的 prefix),错误是**后来者覆盖了正在生效的那条链接**。
|
|
18
|
+
|
|
19
|
+
```mermaid
|
|
20
|
+
graph LR
|
|
21
|
+
A[install.sh 运行] --> B{目标 bin 现在指向谁}
|
|
22
|
+
B -->|npm 包 bin| C[拒绝 exit 3<br/>不动任何文件]
|
|
23
|
+
B -->|脚本安装 / 不存在| D[正常安装]
|
|
24
|
+
D --> E[打印实际生效的 dia + version]
|
|
25
|
+
E --> F{生效路径 == 目标?}
|
|
26
|
+
F -->|否| G[warn:不在 PATH 或 PATH 顺序靠后]
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## 2. 被否掉的初版方案
|
|
30
|
+
|
|
31
|
+
初版设想是「检测到 npm 全局包存在就拒绝」。grill 三方一致否掉,理由:
|
|
32
|
+
|
|
33
|
+
| 问题 | 证据 |
|
|
34
|
+
|---|---|
|
|
35
|
+
| 两种安装可以合法共存 | 本机 `npm prefix -g` 有 `@minonl/dia`,同时 `~/.local/bin/dia` 是脚本安装,两者并存且脚本版更新 |
|
|
36
|
+
| npm 包可能是**旧残留** | npm 0.0.13 + 脚本 0.0.15 时,拒绝安装等于**永久锁死升级** |
|
|
37
|
+
| 多 Node 前缀 | fnm/nvm 切版本后 `npm ls -g` 结果就变,检测不稳定 |
|
|
38
|
+
| 依赖 npm 命令 | 无 npm 的容器/离线场景会误伤;且 `npm ls` 有 0.5s 启动成本 |
|
|
39
|
+
|
|
40
|
+
## 3. 决定(三条)
|
|
41
|
+
|
|
42
|
+
**D1 判据 = 抢链接,不是「包存在」**
|
|
43
|
+
|
|
44
|
+
```
|
|
45
|
+
pkgOwned(p):三种形态任一命中即算包管理器全局安装
|
|
46
|
+
A 软链/拷贝:逐级解析符号链接后,真实路径匹配 (/|\\)node_modules(/|\\)@minonl(/|\\)dia
|
|
47
|
+
B shim 脚本:目标文件不是软链,内容含 @minonl/dia 或 node_modules/(@minonl/)?dia/
|
|
48
|
+
C 二进制 shim:所在目录是已知包管理器全局 bin(~/.volta/bin、PNPM_HOME 等)
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
只在这些情况拒绝(且该路径正是 `command -v dia` 会命中的那条):
|
|
52
|
+
|
|
53
|
+
1. `$DIA_BIN_DIR/dia` 的 realpath 落在 `node_modules/@minonl/dia`(npm / bun 全局安装);
|
|
54
|
+
2. bin 是 **shim 脚本/拷贝**且内容带包路径(pnpm / yarn-berry);
|
|
55
|
+
3. bin 所在目录属于已知包管理器全局 bin(volta 这类不含包路径的同名 shim)。
|
|
56
|
+
|
|
57
|
+
包存在但 bin 未被占 → **只 warn 一行,继续安装**。
|
|
58
|
+
|
|
59
|
+
> 红队回灌(2026-11-06,实现后):`--home` 独占**不算**沙箱(链接仍写默认 bin,守卫照旧生效);
|
|
60
|
+
> 链接落位失败必须退 1(不再谎报装好);`--migrate` 改为**只搬数据**(不装、不改 bin)。
|
|
61
|
+
|
|
62
|
+
> 为什么要有 B/C:pnpm / yarn-berry / volta 的全局 bin 是 **shim 脚本或同名二进制**,不是指向
|
|
63
|
+
> `node_modules/@minonl/dia` 的软链。只认软链时守卫会把这些安装判成「脚本安装」并静默覆盖,
|
|
64
|
+
> 事故形态与 npm 完全一致(本仓库 packageManager 就是 pnpm)。
|
|
65
|
+
|
|
66
|
+
**D2 退出码 3 + 显式逃生舱**
|
|
67
|
+
|
|
68
|
+
| 码 | 含义 |
|
|
69
|
+
|---|---|
|
|
70
|
+
| 0 | 装好(含「已警告但仍安装」) |
|
|
71
|
+
| 1 | 前置缺失(缺 `dist/cli.js` / `bin/dia.js`) |
|
|
72
|
+
| 2 | usage |
|
|
73
|
+
| **3** | **守卫拒绝:未改动任何文件**(可安全重试,重试必然同样结果) |
|
|
74
|
+
|
|
75
|
+
放行条件(任一命中即跳过守卫):
|
|
76
|
+
|
|
77
|
+
- `DIA_ALLOW_SCRIPT_INSTALL=1`(人类显式意图)
|
|
78
|
+
- `--dry-run`(只演练)
|
|
79
|
+
- **生效 bin 目录被重定向**:`--bin DIR` / `DIA_BIN_DIR=DIR`,且不等于默认 `~/.local/bin`
|
|
80
|
+
|
|
81
|
+
> **只改安装目录不算沙箱**(`--home` / `--install-dir` / `DIA_INSTALL_DIR` / `DIA_HOME_INSTALL`):
|
|
82
|
+
> 链接仍写在默认 `~/.local/bin/dia`,照样会抢走生效安装,守卫必须照旧生效(exit 3)。
|
|
83
|
+
> 想要「不碰生效路径」的沙箱,必须同时指定 `--bin`。
|
|
84
|
+
|
|
85
|
+
**D3 `dia update` 不硬失败**
|
|
86
|
+
|
|
87
|
+
脚本模式下**保持现状**(打印升级指引、exit 0),不升级为错误:否则「脚本安装的机器」会彻底无法升级,且 CI 里 `update` 测试会红。只加一条:若检测到 npm 包与当前安装不一致,多打印一行 `dia doctor` 提示。
|
|
88
|
+
|
|
89
|
+
## 4. 配套
|
|
90
|
+
|
|
91
|
+
**D4 安装后自检**:`install.sh` 结束时打印
|
|
92
|
+
|
|
93
|
+
```
|
|
94
|
+
dia installed: <install dir> → <bin dir>/dia
|
|
95
|
+
effective: <realpath of $(command -v dia)> (<version>)
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
生效路径 != 目标时 warn(这正是「npm 装到不在 PATH 的 prefix」的可见化)。
|
|
99
|
+
|
|
100
|
+
**D5 `dia doctor` 新增 `install_shadow`(fail)**
|
|
101
|
+
|
|
102
|
+
判定:`installDir()` 不是包管理器安装 + `command -v dia` 解析到包管理器安装(或反过来)→ 报不一致,附修复命令。
|
|
103
|
+
包管理器安装的 bin 是 shim(pnpm/yarn)时按脚本内容识别,不只看 realpath:
|
|
104
|
+
|
|
105
|
+
```bash
|
|
106
|
+
ln -sfn "$(npm prefix -g)/bin/dia" ~/.local/bin/dia
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
**D6 原子写**(低成本顺带):`cp` 落 `<file>.tmp` 再 `mv -f`;`ln -sfn` 改「先建临时链接再 rename」,避免并发会话看到半截文件/短暂无命令。
|
|
110
|
+
|
|
111
|
+
## 5. 失败模式(红队保留项)
|
|
112
|
+
|
|
113
|
+
| 场景 | 处理 |
|
|
114
|
+
|---|---|
|
|
115
|
+
| npm 包在别的 Node 前缀 | 不检测 npm 列表,只判真实路径 → 天然不受影响 |
|
|
116
|
+
| pnpm/yarn/bun/volta 全局装 | 软链看 realpath(`node_modules/@minonl/dia`);shim 看内容(`@minonl/dia`);volta 二进制 shim 看全局 bin 目录归属 |
|
|
117
|
+
| 只改安装目录当沙箱 | 不算沙箱:链接仍写默认 bin,守卫照旧 exit 3;沙箱必须重定向 `--bin`/`DIA_BIN_DIR` |
|
|
118
|
+
| `readlink -f` 在 macOS 不存在 | 用 `cd "$(dirname x)" && pwd -P` 逐级解析,两边同构 |
|
|
119
|
+
| 本机测试被守卫误拦 | 测试全部用临时 `DIA_INSTALL_DIR` + 临时 `DIA_BIN_DIR`(= 沙箱放行),不探测真机环境;只给安装目录的用例应得 rc=3(`tests/install-guard-strict.test.sh` ①) |
|
|
120
|
+
| 上层脚本把 exit 3 当致命 | 文案给出三条出路(显式放行 / 回 npm / 换 `--bin` 沙箱);`docs/` 里所有 `bash install.sh` 用法补注「默认路径已被包管理器占用时会拒绝」 |
|
|
121
|
+
| 老版本 dia 没有防呆 | `doctor install_shadow` 负责发现,修复命令一条 |
|
|
122
|
+
|
|
123
|
+
## 6. 判据(可执行)
|
|
124
|
+
|
|
125
|
+
```bash
|
|
126
|
+
# 1) 目标 bin 被包管理器占用(**默认路径**)→ 拒绝,且链接字节不变
|
|
127
|
+
# 自动化等价物:tests/install-guard.test.sh ①(临时 HOME + 临时 PATH + 伪 npm 包)
|
|
128
|
+
# 1b) shim 形态(pnpm/yarn/volta)→ 同样拒绝;只改安装目录(--home 等)→ 同样拒绝
|
|
129
|
+
# 自动化等价物:tests/install-guard-strict.test.sh ①②
|
|
130
|
+
# 2) 真沙箱(**bin 目录**被重定向)→ 放行
|
|
131
|
+
DIA_INSTALL_DIR=/tmp/x DIA_BIN_DIR=/tmp/xbin bash install.sh; test $? -eq 0
|
|
132
|
+
# 3) npm 包存在但 bin 未被占 → warn 但安装(install-guard.test.sh ③)
|
|
133
|
+
# 4) doctor:真实路径与 installDir 不一致 → install_shadow fail
|
|
134
|
+
dia doctor | grep -q install_shadow
|
|
135
|
+
# 5) npm 安装的机器上:installDir 含 node_modules/@minonl,且 kits/dia-msg 可解析
|
|
136
|
+
dia doctor --paths | grep -q 'node_modules/@minonl'
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
## 7. 不做的
|
|
140
|
+
|
|
141
|
+
- **不做 `dia install repair`**:新增命令面大于收益;doctor 给一行可复制命令即可。
|
|
142
|
+
- **不改 `dia update` 退出码语义**:`--check` 的 0/1 语义保持不变。
|
|
143
|
+
- **不引入 npm 依赖检测**(`npm ls -g` / `npm prefix -g`):确定性优先。
|
|
144
|
+
|
|
145
|
+
## 8. 文档同步
|
|
146
|
+
|
|
147
|
+
`README.md`(安装章节)、`docs/cli.md`(`doctor` 检查项)、`docs/kit-install.md`(install.sh 分发说明)、
|
|
148
|
+
`docs/status.md`(新增条目)、`docs/index.md` + `llms.txt`(新文档索引,改完跑 `npm run docs:index`)。
|
package/docs/kit-install.md
CHANGED
|
@@ -30,6 +30,13 @@
|
|
|
30
30
|
|
|
31
31
|
结论:**persona 装完是"哑"的**;kit 就是把这些补上,且要能 `verify` / `remove`。
|
|
32
32
|
|
|
33
|
+
> **install.sh 分发守卫(2026-11-06)**:`~/.local/bin/dia` 当前属于包管理器全局安装
|
|
34
|
+
> (npm/bun 的 `node_modules/@minonl/dia` 软链、pnpm/yarn 的 shim 脚本、volta 的 bin)且正是 `command -v dia`
|
|
35
|
+
> 命中的那条时,`install.sh` **exit 3 且不改动任何文件**(避免脚本安装抢链接后 kit 去旧目录找、
|
|
36
|
+
> `missing-kit` / `E_KIT_EMPTY`)。逃生舱:`DIA_ALLOW_SCRIPT_INSTALL=1`、`--dry-run`,
|
|
37
|
+
> 或**把生效 bin 目录重定向**(`--bin DIR` / `DIA_BIN_DIR=DIR`)。注意:`--home` / `DIA_INSTALL_DIR`
|
|
38
|
+
> 只改安装目录,链接仍写默认 `~/.local/bin`,**不算沙箱**(守卫照旧生效)。包存在但 bin 未被占 → 只 warn 一行。
|
|
39
|
+
|
|
33
40
|
---
|
|
34
41
|
|
|
35
42
|
## 2. 清单:`dia.json`(kind=kit)
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# Kit 层:把「经验」从 persona 里抽出来(设计 v0.1)
|
|
2
|
+
|
|
3
|
+
> 2026-11-06 用户定调:dws-hub 作为 **channel 层**重构,同时把 git / node 这类**通用工程经验**抽成 **kit 层**,
|
|
4
|
+
> 让 yan-agent 更薄、更专业,dabo 等其它 persona 直接复用。本文定 kit 的边界、清单、检索、治理与迁移顺序。
|
|
5
|
+
|
|
6
|
+
## 0. 三层模型
|
|
7
|
+
|
|
8
|
+
| 层 | 回答的问题 | 例子 | 归属 |
|
|
9
|
+
|---|---|---|---|
|
|
10
|
+
| **persona** | 你是谁、怎么干活(身份/语气/工作流编排) | yan、dabo | 各自 persona 仓 |
|
|
11
|
+
| **kit** | 你会什么(可复用的经验、工具、知识页) | `kit/git`、`kit/node`、`kit/compose-delivery` | kit 源仓 / registry |
|
|
12
|
+
| **channel** | 消息怎么进出 | dws-hub(钉钉 channel+) | dws-hub 仓 |
|
|
13
|
+
|
|
14
|
+
约束:
|
|
15
|
+
|
|
16
|
+
- kit **不带人格**、不写人设与语气;只放「换个 agent/换个人也用得上」的内容。
|
|
17
|
+
- persona 只保留:身份、规则(must/must-not)、流程编排、**对 kit 的引用**。
|
|
18
|
+
- channel 只管传输与协议,不承载业务经验。
|
|
19
|
+
|
|
20
|
+
## 1. 双标签:一份内容、多领域可检索
|
|
21
|
+
|
|
22
|
+
同一条经验经常跨领域(本次实例:hook manager 共存 = git × node)。做法:
|
|
23
|
+
|
|
24
|
+
- kit 清单与知识页 front-matter 都支持 `tags: string[]`;
|
|
25
|
+
- 检索按 tag 命中**同一份内容**,不复制两套;
|
|
26
|
+
- tag 命名:`git` / `node` / `pnpm` / `hooks` / `worktree` / `channel` / `delivery` …(扁平,不做层级)。
|
|
27
|
+
|
|
28
|
+
示例:
|
|
29
|
+
|
|
30
|
+
```json
|
|
31
|
+
{ "id": "git", "version": "0.1.0", "tags": ["git", "worktree", "hooks"], "knowledge": ["knowledge/**"] }
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## 2. 清单(`dia.json`,kind=kit)增量
|
|
35
|
+
|
|
36
|
+
在现有字段(files / skills / bin / dirs / commands / knowledge / data_root / requires)上新增:
|
|
37
|
+
|
|
38
|
+
| 字段 | 说明 |
|
|
39
|
+
|---|---|
|
|
40
|
+
| `tags: string[]` | 检索标签(双/多标签) |
|
|
41
|
+
| `provides: string[]` | 提供的知识主题(供 persona 引用与 doctor 校验,如 `git.hooks`) |
|
|
42
|
+
| `requires: { "<kit>": "<semver>" }` | 依赖其它 kit(如 `dingtalk` requires `node`) |
|
|
43
|
+
|
|
44
|
+
## 3. 检索:kb 必须同时搜 persona KB 与已装 kit
|
|
45
|
+
|
|
46
|
+
现状:`dia kb` 只索引 persona 自带 knowledge。抽 kit 后若不同时索引 kit,会出现「装了解析不到」的假空。
|
|
47
|
+
|
|
48
|
+
要求:
|
|
49
|
+
|
|
50
|
+
- `dia kb query --tag git "hook"` → 命中 persona KB + 所有已装 kit 的 knowledge;
|
|
51
|
+
- 结果标注来源(`persona:yan` / `kit:git@0.1.0`),便于追踪;
|
|
52
|
+
- 0 命中时显式告警(沿用现规则,别当「没有知识」)。
|
|
53
|
+
|
|
54
|
+
## 4. 治理(doctor)
|
|
55
|
+
|
|
56
|
+
| 检查 | 级别 |
|
|
57
|
+
|---|---|
|
|
58
|
+
| kit 已安装且版本满足 persona 的 `requires` | FAIL |
|
|
59
|
+
| kit 内容哈希与 lock 一致(drift) | FAIL(可 `dia kit verify` 修复) |
|
|
60
|
+
| kit 声明的 bin/skills/files 落点仍在 | FAIL |
|
|
61
|
+
| tag 索引与实际知识页 front-matter 一致 | WARN |
|
|
62
|
+
| 同一 tag 下存在重复内容(hash 相同) | WARN(提示合并) |
|
|
63
|
+
|
|
64
|
+
## 5. 迁移顺序(小步,不搬家式重写)
|
|
65
|
+
|
|
66
|
+
1. **P0**:本设计 + `dia kit` 支持 `tags`(本 PR)
|
|
67
|
+
2. **P1(pilot)**:抽 `kit/git` 与 `kit/node`
|
|
68
|
+
- `kit/git`:worktree 纪律、adopt/doctor/gate、守卫模型、对外无逃生口原则
|
|
69
|
+
- `kit/node`:pnpm 11 `allowBuilds`、lockfile 最小变更、frozen-lockfile、hook manager(husky→simple-git-hooks)
|
|
70
|
+
- 交叉页(hook manager 共存)挂在 `kit/git`,`tags` 双写 `[git, node, hooks]`
|
|
71
|
+
3. **P2**:yan-agent 变薄 —— git/node 页面移入 kit,persona 只留身份 + 工作流 + 索引;`install.sh` 改 `dia kit add <src>`
|
|
72
|
+
4. **P3**:dabo 装同一套 kit,验证「换 persona 不换经验」;doctor 校验 kit 版本约束
|
|
73
|
+
|
|
74
|
+
## 6. 非目标
|
|
75
|
+
|
|
76
|
+
- 不做 kit 市场/评分;kit 来源先支持 git ref 与本地路径。
|
|
77
|
+
- 不把 persona 规则(must/must-not)塞进 kit —— 规则仍归 persona,kit 只提供「怎么做」与「踩过什么坑」。
|
|
78
|
+
- channel 层不因 kit 化而承担知识分发。
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
# 1) 备份(可选但建议)
|
|
10
10
|
mv ~/.local/share/dia ~/.local/share/dia.bak-$(date +%Y%m%d)
|
|
11
11
|
|
|
12
|
-
# 2)
|
|
12
|
+
# 2) 用新安装器迁数据(只 mv 旧数据根 → ~/.dia;不装、不改 bin 链接)
|
|
13
13
|
cd ~/repos/github/dia
|
|
14
14
|
bun run build
|
|
15
15
|
bash install.sh --migrate
|
|
@@ -52,9 +52,10 @@ tar czf ~/backups/dia-job-$TS.tgz -C ~ .local/share/dia-job
|
|
|
52
52
|
crontab -l > ~/backups/crontab-$TS.txt
|
|
53
53
|
# 把所有 `dia-job:` 行注释掉(记下原样,后面恢复)
|
|
54
54
|
|
|
55
|
-
# 1)
|
|
55
|
+
# 1) 只迁数据(2026-11-06 起 --migrate 不再顺带安装/改 bin 链接,避免抢走 npm 安装)
|
|
56
56
|
# 源码来自 mac 构建产物:tar 传 bin/dist/packages/docs/install.sh/package.json
|
|
57
57
|
bash install.sh --migrate
|
|
58
|
+
# 装新版走 npm(推荐):npm i -g @minonl/dia@latest
|
|
58
59
|
|
|
59
60
|
# 2) cache(install.sh 不搬)
|
|
60
61
|
mkdir -p ~/.dia/cache && [ -d ~/.cache/dia/index ] && mv ~/.cache/dia/index ~/.dia/cache/
|
package/docs/status.md
CHANGED
|
@@ -28,6 +28,7 @@
|
|
|
28
28
|
| `history/framework-bootstrap.md` | 早期启动与分发选型(已归档) | **归档**:方案未采用,实际分发是 `install.sh`,不要按它部署 |
|
|
29
29
|
| `entrypoints.md` | 用户入口矩阵 | **部分过时**:终端 dabo preset 已渲染;IM bridge 未写;`dia status` / `dia model` 不存在 |
|
|
30
30
|
| `kit-install.md` | kit 清单、渲染、生命周期 | **设计**:实现只有 `add/list/verify/remove`;`show/update/gc/create` 未做 |
|
|
31
|
+
| `install-guard-design.md` | **安装防呆**:脚本安装 vs npm 全局安装的抢链接判据、exit 3 逃生舱、`doctor install_shadow` | **实现**(D0.65 + `tests/install-guard.test.sh`) |
|
|
31
32
|
| `patching.md` | 本地 patch | **未实现**(`unknown command`) |
|
|
32
33
|
| `team-automation.md` | 计划→实现→验证全自动循环 | **未实现**(D0.6–D0.9 只跑通 mock / ACP 驱动) |
|
|
33
34
|
| `proposals.md` | `team next` 目标候选 | **v0 已实现**(`dia team next`,纯规则打分) |
|
|
@@ -90,6 +91,7 @@
|
|
|
90
91
|
| D0.21 | worktree 抽取 `packages/wt` | **完成** | 7 处 python→node;`dia wt`;4 套测试原样迁移全绿 |
|
|
91
92
|
| D0.23 | doctor 平台版本自检 | **完成** | `platform:opencode` / `platform:dsh` 报版本(缺宿主 = warn,不判红);`tests/active-doctor.test.ts` |
|
|
92
93
|
| D0.44 | 配置迁移走 LLM 任务 | **完成** | `docs/migrations/llm-migration.md`:升级只改声明;迁移=任务+判据+回滚;禁止代码里堆版本分支(安全敏感/原子场景例外) |
|
|
94
|
+
| D0.65 | 安装防呆:脚本安装不抢包管理器全局安装 | **完成** | `install.sh` 守卫(目标 bin 属于包管理器全局安装且为 `command -v dia` 命中项 → exit 3,零改动):npm/bun 软链看 `node_modules/@minonl/dia`,pnpm/yarn shim 看内容,volta 看 bin 目录;放行 = `DIA_ALLOW_SCRIPT_INSTALL=1` / `--dry-run` / **bin 目录被重定向**(`--home` 等只改安装目录不算沙箱);装完打印 `effective:`;`doctor install_shadow` 双向一致判定(含 shim);`tests/install-guard.test.sh` + `tests/install-guard-strict.test.sh` |
|
|
93
95
|
| D0.64 | 复制过滤按相对路径判断 | **完成** | 绝对路径匹配 `node_modules` 会误伤 npm 全局安装(kit 整树被过滤 → `E_KIT_EMPTY`);改 `copyFilter(base)` 用相对路径,persona/kit 共用 |
|
|
94
96
|
| D0.63 | 运行时数据 `.data/` 不进快照、不参与哈希 | **完成** | `TREE_SKIP`(kit/persona 共用):安装复制与 `hashTree`/verify 都跳过 `.data`,防运行时写入造成 verify 假漂移 |
|
|
95
97
|
| D0.62 | persona 旧版技能软链视为受管 | **完成** | `linkManaged` 同时认 `~/.dia/kits/**` 与 `~/.dia/personas/**`;`persona update` 升级不再需要 `--force` |
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: hook 模型与管理器共存(git × node)
|
|
3
|
+
tags: [git, node, hooks]
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# 守卫钩子与 hook 管理器共存
|
|
7
|
+
|
|
8
|
+
## 三种模式(dia wt adopt 自动判定)
|
|
9
|
+
|
|
10
|
+
| 仓类型 | 做法 | 效果 |
|
|
11
|
+
|---|---|---|
|
|
12
|
+
| 普通仓 | 钩子写 `<common>/dia/hooks`,仓库本地 `core.hooksPath` 指过去,并链式调用原钩子目录 | 守卫 + 原钩子都执行 |
|
|
13
|
+
| simple-git-hooks 仓 | 不改 `.git/hooks` / `package.json`;守卫写进 `SIMPLE_GIT_HOOKS_RC`,由管理器钩子 source | install 不会覆盖守卫 |
|
|
14
|
+
| 其它管理器(husky / `.githooks`) | 不抢占,doctor WARN | 守卫不生效,建议迁移 |
|
|
15
|
+
|
|
16
|
+
## 为什么不能接管 simple-git-hooks
|
|
17
|
+
|
|
18
|
+
它只读**仓库本地** `core.hooksPath`:有值就写到那里 → 一旦把 hooksPath 指向 dia 目录,`pnpm install` 就会把守卫文件覆盖掉。
|
|
19
|
+
**正确做法是 RC 注入**:钩子模板会 `source "$SIMPLE_GIT_HOOKS_RC"`,守卫在那一步执行。
|
|
20
|
+
|
|
21
|
+
## RC 自递归(已修复,务必保留标记)
|
|
22
|
+
|
|
23
|
+
管理器钩子 → source RC → `dia wt guard` → 守卫末尾又链回 `<common>/hooks/<name>`(同一文件)→ 死循环,表现为 `git commit` 卡死。
|
|
24
|
+
RC 必须 `export DIA_WT_IN_MANAGER_HOOK=1`,守卫看到标记只判定、不再链式。
|
|
25
|
+
|
|
26
|
+
## husky → simple-git-hooks 迁移
|
|
27
|
+
|
|
28
|
+
1. `.husky/*` 内容搬进 `package.json` 的 `simple-git-hooks` 配置;
|
|
29
|
+
2. 删 `.husky/` 与 husky 依赖,加 `simple-git-hooks` devDependency + `"postinstall": "simple-git-hooks"`;
|
|
30
|
+
3. 清理失效的本地 `core.hooksPath=.husky`,再 `pnpm install` 重建钩子。
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: worktree 纪律(adopt / doctor / gate)
|
|
3
|
+
tags: [git, worktree, discipline]
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# worktree 纪律
|
|
7
|
+
|
|
8
|
+
## 一次性接管
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
dia wt adopt <repo> # dry-run
|
|
12
|
+
dia wt adopt <repo> --apply
|
|
13
|
+
dia wt adopt --all --apply # 仓根下所有主 checkout(跳过 *-worktree--*)
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
- 策略写在 `<git-common-dir>/dia/config.json`(defaultBranch / template / hooksManager)
|
|
17
|
+
- 普通仓:钩子写 `<common>/dia/hooks`,仓库本地 `core.hooksPath` 指过去
|
|
18
|
+
- simple-git-hooks 仓:走 `SIMPLE_GIT_HOOKS_RC`(见 hooks 页),不改仓内文件
|
|
19
|
+
- **不写全局 git 配置**(外溢;D20 会检查残留)
|
|
20
|
+
|
|
21
|
+
## 巡检与门禁
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
dia wt doctor <repo>... [--all] [--fix]
|
|
25
|
+
dia wt gate <repo> <feature> # 提 MR 前:check → doctor → 工作区干净 → ahead>0
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
判据要点:D01 配置 / D02 钩子或 RC 就绪 / D03 守卫探针 / D04 主 checkout 干净 / D05–D07 worktree 卫生 / D11 默认分支 / D20 全局残留 / D21 本地 hooksPath 与模式一致。
|
|
29
|
+
|
|
30
|
+
## 纪律
|
|
31
|
+
|
|
32
|
+
- 任何 git 改动先 `dia wt create`;主 checkout 只 `pull --ff-only` / 看代码。
|
|
33
|
+
- 守卫没有「跳过」开关:钩子慢就先量化再优化(增量 lint、`nx affected`、缓存、下沉 CI)。
|
|
34
|
+
- worktree 内依赖必须**真装**(R20):跨 checkout 软链 `node_modules` 会让 pnpm 改写主仓链接,删 worktree 后整片悬空。
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: node 侧 hook 管理器(husky / simple-git-hooks)
|
|
3
|
+
tags: [node, git, hooks]
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# node 项目管理 git 钩子的两种主流方式
|
|
7
|
+
|
|
8
|
+
| 方案 | 落点 | 特点 |
|
|
9
|
+
|---|---|---|
|
|
10
|
+
| husky v6+ | `.husky/`(提交进仓),`core.hooksPath=.husky` | 钩子内容进版本库,团队一键生效;但会覆盖任何外部 hooksPath |
|
|
11
|
+
| simple-git-hooks | 生成到 `core.hooksPath`(默认 `.git/hooks`) | 配置在 `package.json`,`postinstall` 自动生成;有 `SIMPLE_GIT_HOOKS_RC` 注入点 |
|
|
12
|
+
|
|
13
|
+
## 统一建议
|
|
14
|
+
|
|
15
|
+
- 新仓/可改的仓:用 **simple-git-hooks**(配置集中、可被 RC 注入、不占 `.husky`)。
|
|
16
|
+
- 已有 husky 的仓:按 git kit 的「husky → simple-git-hooks 迁移」执行;迁移属于提交改动,团队仓走 MR。
|
|
17
|
+
- 任何情况下**不要**在主 checkout 用跳过开关绕钩子;钩子慢就优化(增量 lint / 缓存 / 下沉 CI)。
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: pnpm 11 与构建脚本(allowBuilds)
|
|
3
|
+
tags: [node, pnpm, ci]
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# pnpm 11 要点
|
|
7
|
+
|
|
8
|
+
## 构建脚本默认被忽略
|
|
9
|
+
|
|
10
|
+
pnpm 10+ 默认不执行依赖的 `postinstall`。表现:
|
|
11
|
+
|
|
12
|
+
- 本机:`ERR_PNPM_IGNORED_BUILDS`(退出码 1)
|
|
13
|
+
- CI:`Job failed: command terminated with exit code 1`
|
|
14
|
+
|
|
15
|
+
放行方式(**写在 `pnpm-workspace.yaml`**,pnpm 11 起 `package.json` 的 `pnpm` 字段不再读取):
|
|
16
|
+
|
|
17
|
+
```yaml
|
|
18
|
+
allowBuilds:
|
|
19
|
+
simple-git-hooks: true
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
旧版本写法是 `onlyBuiltDependencies: ["simple-git-hooks"]`。
|
|
23
|
+
|
|
24
|
+
## lockfile 只做最小增删
|
|
25
|
+
|
|
26
|
+
团队仓加/删依赖时,别用新版 pnpm 全量重解析(会重写上万行、把无关依赖升级):
|
|
27
|
+
|
|
28
|
+
1. 手工改 `pnpm-lock.yaml`:`importers` 段 + `packages` 段 + `snapshots` 段各一处;
|
|
29
|
+
2. 用 `pnpm install --frozen-lockfile`(不带 `--lockfile-only`)验证:通过即说明 lockfile 满足约束,且不会回写。
|
|
30
|
+
|
|
31
|
+
## 其它
|
|
32
|
+
|
|
33
|
+
- `pnpm install --frozen-lockfile` 是 CI 的硬门槛,本地也要真装(1~2s 很正常),不要用「借二进制」替代。
|
|
34
|
+
- worktree 里安装依赖 = 在**该 worktree** 内安装,不要软链主仓 `node_modules`(见 git kit 的 worktree 页)。
|