@minonl/dia 0.0.14 → 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/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,8 @@
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` |
95
+ | D0.64 | 复制过滤按相对路径判断 | **完成** | 绝对路径匹配 `node_modules` 会误伤 npm 全局安装(kit 整树被过滤 → `E_KIT_EMPTY`);改 `copyFilter(base)` 用相对路径,persona/kit 共用 |
93
96
  | D0.63 | 运行时数据 `.data/` 不进快照、不参与哈希 | **完成** | `TREE_SKIP`(kit/persona 共用):安装复制与 `hashTree`/verify 都跳过 `.data`,防运行时写入造成 verify 假漂移 |
94
97
  | D0.62 | persona 旧版技能软链视为受管 | **完成** | `linkManaged` 同时认 `~/.dia/kits/**` 与 `~/.dia/personas/**`;`persona update` 升级不再需要 `--force` |
95
98
  | D0.61 | kit 空内容拒绝 + doctor 只查引用 kit | **完成** | `kit add` 拒绝空清单/空树(E_MANIFEST/E_KIT_EMPTY);doctor 按 persona lock 的 kits 校验,孤儿不再拖红 |
@@ -0,0 +1,10 @@
1
+ {
2
+ "schema": 1,
3
+ "kind": "kit",
4
+ "id": "git",
5
+ "version": "0.1.0",
6
+ "root": ".",
7
+ "tags": ["git", "worktree", "hooks"],
8
+ "knowledge": ["knowledge"],
9
+ "requires": { "dia": ">=0.0.2" }
10
+ }
@@ -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,10 @@
1
+ {
2
+ "schema": 1,
3
+ "kind": "kit",
4
+ "id": "node",
5
+ "version": "0.1.0",
6
+ "root": ".",
7
+ "tags": ["node", "pnpm", "hooks"],
8
+ "knowledge": ["knowledge"],
9
+ "requires": { "dia": ">=0.0.2" }
10
+ }
@@ -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 页)。
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@minonl/dia",
3
- "version": "0.0.14",
3
+ "version": "0.0.16",
4
4
  "type": "module",
5
5
  "description": "dia (嗲) — lightweight agent framework: knowledge index, audit, session ingest",
6
6
  "bin": {
@@ -1251,6 +1251,8 @@ if [ "$adopted" = 1 ]; then
1251
1251
  fi
1252
1252
 
1253
1253
  # 链式:仓库自己的钩子(simple-git-hooks 等管理器生成在 <common>/hooks)
1254
+ # 若当前就是从管理器钩子里的 RC 进来的,再链回去会自递归 → 直接结束
1255
+ if [ "${DIA_WT_IN_MANAGER_HOOK:-0}" = "1" ]; then exit 0; fi
1254
1256
  orig="$common/hooks/$name"
1255
1257
  if [ -x "$orig" ] && [ "$orig" != "$0" ]; then
1256
1258
  if [ "$name" = "pre-push" ] && [ ${#push_lines[@]} -gt 0 ]; then
@@ -1435,10 +1437,22 @@ PYCHK
1435
1437
  # 由 simple-git-hooks 生成的钩子 source 本文件(SIMPLE_GIT_HOOKS_RC)
1436
1438
  # 只对已 adopt 的仓生效;未 adopt → 直接返回
1437
1439
  if command -v dia >/dev/null 2>&1; then
1440
+ export DIA_WT_IN_MANAGER_HOOK=1 # 防止守卫再链回管理器钩子(自递归)
1438
1441
  dia wt guard "$(basename "$0")" || exit $?
1439
1442
  fi
1440
1443
  RC
1441
1444
  wt_ensure_env_export SIMPLE_GIT_HOOKS_RC "$rcf"
1445
+ # 迁移残留:本地 hooksPath 指向已删除的目录(如刚从 husky 切过来)→ 撤掉,让管理器写 .git/hooks
1446
+ if [ -n "$prev" ]; then
1447
+ local resolved="$prev"
1448
+ case "$resolved" in /*) ;; *) resolved="$repo/$resolved" ;; esac
1449
+ if [ ! -d "$resolved" ]; then
1450
+ git -C "$repo" config --local --unset core.hooksPath 2>/dev/null || true
1451
+ echo "[dia-wt] 清理失效的本地 core.hooksPath($prev 已不存在)" >&2
1452
+ else
1453
+ echo "[dia-wt] ⚠️ 本仓仍设有 core.hooksPath=$prev(会覆盖管理器钩子),请确认后移除" >&2
1454
+ fi
1455
+ fi
1442
1456
  python3 - "$cfg" "$repo" "$def" "$template" "$prev" <<'PYG'
1443
1457
  import json, sys, datetime
1444
1458
  cfg, repo, br, tmpl, prev = sys.argv[1:6]
@@ -1551,6 +1565,7 @@ cmd_doctor() {
1551
1565
  cat > "$rcf" <<'RC'
1552
1566
  # 由 simple-git-hooks 生成的钩子 source 本文件(SIMPLE_GIT_HOOKS_RC)
1553
1567
  if command -v dia >/dev/null 2>&1; then
1568
+ export DIA_WT_IN_MANAGER_HOOK=1 # 防止守卫再链回管理器钩子(自递归)
1554
1569
  dia wt guard "$(basename "$0")" || exit $?
1555
1570
  fi
1556
1571
  RC
@@ -61,6 +61,14 @@ grep -q "SIMPLE_GIT_HOOKS_RC" "$HOME/.zshenv" 2>/dev/null && ok "shell rc 注入
61
61
  bash "$WT" doctor "$RN" >/dev/null 2>&1 && ok "npm 仓 doctor 通过" || bad "npm 仓 doctor 失败"
62
62
  (cd "$RN" && DIA_WT_GUARD_PROBE=1 bash "$WT" guard pre-commit >/dev/null 2>&1) && bad "guard 未拦截 main 提交" || ok "guard 拦截 main 提交"
63
63
 
64
+ # RC 注入不得自递归:模拟管理器钩子(source RC 后应直接结束,不再链回自己)
65
+ RC="$( wt_hooks_rc_dir 2>/dev/null || true )"; true
66
+ printf '#!/usr/bin/env bash\nset -e\nname=pre-commit\nsource "$SIMPLE_GIT_HOOKS_RC"\n' > "$TD/pre-commit"
67
+ chmod +x "$TD/pre-commit"
68
+ (cd "$RN" && timeout 10 env SIMPLE_GIT_HOOKS_RC="$DIA_ROOT/hooks/simple-git-hooks-rc.sh" "$TD/pre-commit" >/tmp/rc.log 2>&1)
69
+ rc=$?; [ "$rc" != 124 ] && ok "RC 注入不自递归(main 上被守卫拦截,退出码 $rc)" || bad "RC 注入自递归/超时"
70
+ cd - >/dev/null
71
+
64
72
  # 全局配置残留检查:旧版全局钩子目录 → doctor FAIL
65
73
  LEGACY="$TD/legacy-hooks"; mkdir -p "$LEGACY"
66
74
  printf '#!/usr/bin/env bash\n# dia-wt guard(旧)\nexit 0\n' > "$LEGACY/pre-commit"; chmod +x "$LEGACY/pre-commit"