Files
workbuddy_skills/session-mechanism/scripts/lock/handoff-status.py
T
admin d81f75f173 技能包:配置与代码分家 —— 每包自建 env/ 放本机专属配置,scripts/ 不再写死本机路径
用户 2026-10-11 原话:
· 19:22「这个环境都是针对一台机器的,同步到别的机器把环境都搞坏了」
· 19:28「不用挪出 用env文件夹区分就行,重点是 脚本是脚本 配置是配置」
· 19:29「每个技能 需要环境配置的 都有自己的 env文件夹」
· 19:30「修改完成后要仔细检查 避免调整后 各技能脚本运行失败」

改动:
1、session-mechanism 新增 env/(roots.env=本机真身不入库 + roots.env.example=样例入库),
   install.py 的 ROOTS_ENV 改指 env/roots.env;全包脚本/hook 的配置定位统一为
   「向上 4 层 × env/roots.env 优先 → 包根 roots.env 兼容 → 技能库那份兜底」。
2、去硬编码:collabctl / keepalive / supervise-launch / board-launch 里的本机路径与本机工作区
   清单全清,改为 DSH_AIROOT(兜底 dirname(DSH_WS_ROOT))+ 扫 <AIROOT>/*/.workbuddy/collab/
   自动发现;保活任务名按 collabd-keepalive-<工作区名> 现推;启动器默认值改 ~/.workbuddy 形态。
3、browser-harness:bh_chrome_keepalive.py 默认值全部改 env 驱动,新增 env/bh.env.example。
4、.gitignore:env/ 下只放行 *.example(三行三种层级)+ 兜住旧位置的包根 roots.env。
5、改包纪律.md 新增「四、包结构:脚本是脚本,配置是配置」,立为全仓通用规矩。
6、文档同步:SKILL.md / deploy.md(新增 §3b 配置层级、§6 换机器必查 6 件事)/ rules.md /
   pitfalls.md / supervise-persistence.md。

验收(本机):py_compile 34 个 py 全过;selftest PASS 109 / FAIL 0;
逐文件实证 8 个 loader 命中 env/roots.env;preflight-lock.sh 同目录实测解出两个根;
install.py --manifest 77 份、语法失败 0。

异机验证(包拷成「新克隆」(只有 *.example)+ 另造假工作区 ws-alpha/ws-beta):
install.py --apply 自生成该机 env/roots.env;collabctl/keepalive 自动发现的确实是
ws-alpha/ws-beta、任务名现推(写死清单确已消除);部署副本本地无 roots.env 时靠
CODEBUDDY_CONFIG_DIR 兜底读到技能库那份;整套 selftest 有宿主库时 PASS 108 / FAIL 1
(唯一 FAIL=该区没有常驻心跳,环境所致;本机常驻在跑时同一套为 PASS 109 / FAIL 0)。
2026-10-11 21:49:28 +08:00

95 lines
4.2 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""handoff-status.py — 一条命令看清 05-交接单 里哪些单子【待执行】/【已完成】/【无状态】。
为什么需要它:`05-交接单/交接单-已完成/` 那层目录摊平后,**"目录位置"不再表达状态**。
状态改由**机读字段**承载(与布局解耦,摊平/搬家都不失效):
① 单子头部 `- 状态:…` 行(模板已有)→ ② 回退取 `05-交接单/README.md` 台账表的「状态」列
用法:python 07-scripts/handoff-status.py
"""
# [session-mechanism] roots.env 外置(由 install.py 生成 ⇒ `<包>/env/roots.env`)
# 查找顺序:向上 4 层 × (env/roots.env 优先 → 包根 roots.env 兼容) → 技能库那份。
def _sm_load_roots():
import os as _os
_here = _os.path.dirname(_os.path.abspath(__file__))
_cands = []
for _up in range(4):
_base = _os.path.normpath(_os.path.join(_here, *([".."] * _up)))
for _rel in (("env", "roots.env"), ("roots.env",)):
_cands.append(_os.path.join(_base, *_rel))
_conf = _os.environ.get("CODEBUDDY_CONFIG_DIR") or _os.path.expanduser("~/.workbuddy")
_cands.append(_os.path.join(_conf, "skills", "session-mechanism", "env", "roots.env"))
for _p in _cands:
if _os.path.isfile(_p):
try:
with open(_p, encoding="utf-8") as _f:
for _ln in _f:
_ln = _ln.strip()
if _ln and not _ln.startswith("#") and "=" in _ln:
_k, _v = _ln.split("=", 1)
_os.environ.setdefault(_k.strip(), _v.strip())
except Exception:
pass
return
_sm_load_roots()
import os
import re
ROOT = os.environ.get("DSH_DOCS_ROOT") or os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
D = os.path.join(ROOT, "05-交接单")
README = os.path.join(D, "README.md")
HEAD_STATUS = re.compile(r"^[\s>*\-|#]*\*{0,2}状态\*{0,2}\s*[::]\s*(.+?)\s*$", re.M)
DONE_KW = ("已完成", "已落地", "已归档", "已收口", "已闭合", "已实施", "已上线",
"已修复", "已释放", "已结束", "已通过", "全部完成", "全绿", "✅")
WAIT_KW = ("待执行", "待开工", "进行中", "在途", "待验证", "待跑", "规划中", "未开工",
"部分执行", "受阻", "未做", "设计定稿", "🔄", "⏳", "⏸", "🟡")
table = {}
if os.path.exists(README):
with open(README, encoding="utf-8") as fh:
for line in fh:
if not line.startswith("|"):
continue
cells = [c.strip() for c in line.strip().strip("|").split("|")]
if len(cells) < 2 or ".md" not in cells[0]:
continue
table[cells[0].strip("`").split("/")[-1].strip()] = cells[1]
groups = {"待执行": [], "已完成": [], "无状态": []}
targets = []
for root2, dirs2, _f in os.walk(D):
dirs2[:] = [x for x in dirs2 if not x.startswith(".")]
for f2 in _f:
if f2.endswith(".md") and f2 != "README.md" and not f2.startswith("."):
rel = os.path.relpath(os.path.join(root2, f2), D).replace("\\", "/")
targets.append((rel, os.path.join(root2, f2)))
for n, path in sorted(targets):
base = n.split("/")[-1]
with open(path, encoding="utf-8", errors="ignore") as fh:
head = "".join(fh.readlines()[:30])
m = HEAD_STATUS.search(head)
if m:
s, src = m.group(1)[:44], "头部"
elif base in table:
s, src = table[base][:44], "台账表"
else:
s, src = "", ""
if not s:
groups["无状态"].append((n, "—"))
elif any(k in s for k in DONE_KW):
groups["已完成"].append((n, "%s ·%s" % (s, src)))
elif any(k in s for k in WAIT_KW):
groups["待执行"].append((n, "%s ·%s" % (s, src)))
else:
groups["无状态"].append((n, "%s ·%s未识别" % (s, src)))
for g in ("待执行", "已完成", "无状态"):
print("【%s】%d 个" % (g, len(groups[g])))
for n, s in groups[g]:
print(" %-44s %s" % (n[:44], s))
print()
print("合计 %d 个单子(不含 README.md)" % sum(len(v) for v in groups.values()))