chore(工作区): 全量入库 + 补齐 .gitignore(以工作区为准)
- 变更规模:新增 514 / 修改 62 / 重命名 155 / 删除 4(归档重组与文档轮次) - .gitignore 修:`归档/**/db-cwd归一-备份-*/` —— 原规则写绝对层级(归档/db-cwd归一-…), 目录搬进 归档/配置与备份/ 后**静默失效**,43 MB 的 DB 备份又变成未跟踪 - .gitignore 补:嵌套 git 内部数据(归档/内嵌git-20261008/、归档/skills-git-旧线-20261007/dotgit-原样移出/) - .gitignore 补:运行态与部署副本(.workbuddy/collab/、.workbuddy/tools/、.workbuddy/.load-pending、.workbuddy/tmp-*) - .gitignore 补:备份件(*.bak-*) - 未跟踪文件从 2190 降到 890(其余为 归档/ 归档件与 .workbuddy/memory/ 知识文件,按口径入库)
This commit is contained in:
1 parent
30b46dbd0c
commit
c1b5e4d966
735 files changed
+153192
-2415
No files matched your search
@@ -7,16 +7,19 @@
|
||||
python state.py --online # 追加:与 Gitea 远端比对基线(需 SSH)
|
||||
|
||||
设计原则(2026-09-16 立):
|
||||
① 只输出「能决定下一步」的内容,目标 ≤ 22 行。**大输出本身就是成本。**
|
||||
② 只读,永不写。执行它不需要抢锁。
|
||||
① 只输出「能决定下一步」的内容。**大输出本身就是成本。**
|
||||
② 只读宿主库 / 远端,**不写它们**。执行它不需要抢锁。
|
||||
⚠️ **唯一例外**(2026-10-02 用户加):**会话规则机制体检**会**写一份自己的产物**
|
||||
`<WS>/.workbuddy/collab/session-rules.json`(=用户要求的那枚「状态标记」)——
|
||||
它只落在**本工作区自己的协作件目录**里,原子替换、不碰宿主库,故仍不改变本项免锁的性质。
|
||||
③ 每条状态带「判据」——让读者不必再自己取证。
|
||||
④ 默认不联网、不 SSH —— 那些是慢操作,要就显式加 --online。
|
||||
"""
|
||||
import io, os, re, subprocess, sys, datetime
|
||||
import io, json, os, re, subprocess, sys, datetime
|
||||
|
||||
REPO = r"D:/github/dsh_shenxian"
|
||||
DOCS = os.path.join(REPO, "dsh-server-docs")
|
||||
WS = r"E:/ProgramData/AIProject/aliyun-dsh-server"
|
||||
WS = r"E:/ProgramData/AIProject/ai1net-dsh-server"
|
||||
# 🔑 2026-09-21 加 `--ws <path>`:让**其他工作区**用同一份脚本取**自己那条线**的状态。
|
||||
# 用法 = python "<本脚本绝对路径>" --ws "<自己的工作区>"(本工作区无脚本时不必复制第二份)。
|
||||
# ⛔ 跨工作区调用**不改变 cwds** —— 接续任务一律建在**自己的工作区**(归属三律,见技能 §0.6)。
|
||||
@@ -200,7 +203,12 @@ if ALIEN:
|
||||
t = read(ENTRY)
|
||||
if t:
|
||||
print(" ——下面只展开最新的那条(%s)§2——" % os.path.basename(ENTRY))
|
||||
sec = re.search(r"##\s*2\.[^\n]*\n(.*?)(?=\n##\s|\Z)", t, re.S)
|
||||
# ⚠️ 2026-09-25 修:原正则只认 `## 2.`,而本工作区的入口一律写 `## §2 xxx`
|
||||
# ⇒ 一直落到下面的「§2 未解析出条目」(实测:4 条入口**全部**解析为空 ——
|
||||
# `[入口] §2` 段是每棒开机第一个信号,它空着 = 下一棒拿不到动作)。
|
||||
# 现兼容三种写法:`## 2.` / `## §2 ` / `## 2、`;⚠️ 用 lookahead 卡住分隔符,
|
||||
# ⛔ 不能让 `## 2026-…` 这类以 2 开头的标题被误当成 §2。
|
||||
sec = re.search(r"##\s*(?:§)?\s*2(?=[.\s、]|$)[^\n]*\n(.*?)(?=\n##\s|\Z)", t, re.S)
|
||||
body = sec.group(1) if sec else ""
|
||||
items = re.findall(r"^\s*(?:\d+[.)]|[-*])\s+(.{6,120})", body, re.M)
|
||||
for i, it in enumerate(items[:6], 1):
|
||||
@@ -245,6 +253,163 @@ if lg:
|
||||
else:
|
||||
line("[收口]", "⚠️ 今日日志不存在:%s" % today)
|
||||
|
||||
# ── 5.5 钩子闸门自检(2026-09-30 加)────────────────────────────
|
||||
# 为什么必须有:`settings.json.bak-decision-20260927-233116`(mtime 09-26 21:17)里 **hooks 段是空的**
|
||||
# ⇒ 那次改动把 `stop-dialog-guard` 的 UserPromptSubmit 钩子(=「上下文到量 ⇒ 强制收口:先落盘、
|
||||
# 出接续包、登记接续棒」的**唯一触发源**)连同其它闸门**整段清掉**;09-27~09-30 只补回了
|
||||
# decision_bridge / wb-result-hook ⇒ 该机制**静默失效 4 天**(guard 日志最后一条 09-26 19:19),
|
||||
# 直到 09-30 才被人发现。⇒ 判据不能靠"记得去数":开工第 0 步(本文件)就把它数出来。
|
||||
# ⚠️ 读配置必须认 `CODEBUDDY_CONFIG_DIR`(实测本机 = `E:\ProgramData\.workbuddy`);guard 自带的
|
||||
# `path_health()` 读的是 `WORKBUDDY_CONFIG_DIR`/`~/.workbuddy` ⇒ 本机**永远读空 ⇒ 恒静默**。
|
||||
GATES = [
|
||||
("UserPromptSubmit", "stop-dialog-guard.py", "水位/收口触发源(接续机制的起点)"),
|
||||
("UserPromptSubmit", "skill-load-guard.py", "用户点名方法 ⇒ 强制加载技能"),
|
||||
("PreToolUse", "lock-guard-hook.py", "无锁不许改文档库 / 代码库"),
|
||||
("PreToolUse", "bash-output-guard.py", "拦下会灌爆上下文的读命令"),
|
||||
]
|
||||
|
||||
|
||||
def _tail_time(p):
|
||||
"""只读文件尾 8 KB 取最后一行前 16 字符(⛔ 不整读 —— lock-hook.log 已 3.6 MB)。"""
|
||||
try:
|
||||
with io.open(p, "rb") as f:
|
||||
f.seek(0, 2)
|
||||
n = min(8192, f.tell())
|
||||
f.seek(-n, 2)
|
||||
rows = [x for x in f.read().decode("utf-8", "replace").split("\n") if x.strip()]
|
||||
return (rows[-1] or "")[:16] if rows else "空"
|
||||
except Exception:
|
||||
return "缺"
|
||||
|
||||
|
||||
_WSROOT = os.path.dirname(os.path.abspath(__file__))
|
||||
GATE_LOGS = [("收口", os.path.join(_WSROOT, ".workbuddy", "stop-dialog-guard.log")),
|
||||
("技能", os.path.join(_WSROOT, ".workbuddy", "skill-load-guard.log")),
|
||||
("限流", os.path.join(_WSROOT, ".workbuddy", "bash-guard.log")),
|
||||
("锁", os.path.join(os.path.dirname(DOCS), ".workbuddy", "lock-hook.log"))]
|
||||
_CFG = os.path.join(os.environ.get("CODEBUDDY_CONFIG_DIR")
|
||||
or os.path.join(os.path.expanduser("~"), ".workbuddy"), "settings.json")
|
||||
try:
|
||||
_HK = (json.load(io.open(_CFG, encoding="utf-8")) or {}).get("hooks") or {}
|
||||
_cmds = [(ev, str(h.get("command") or ""))
|
||||
for ev, arr in _HK.items() for blk in (arr or [])
|
||||
for h in ((blk or {}).get("hooks") or [])]
|
||||
except Exception:
|
||||
_HK, _cmds = None, []
|
||||
if _HK is None:
|
||||
line("[闸门]", "⚠️ 读不到 hooks 配置:%s" % _CFG)
|
||||
else:
|
||||
_miss = [g for g in GATES if not any(g[0] == ev and g[1] in c for ev, c in _cmds)]
|
||||
if _miss:
|
||||
line("[闸门]", "🔴 关键闸门**不在册** ⇒ 该机制现在不会运行:")
|
||||
for _ev, _f, _why in _miss:
|
||||
print(" · %s(应挂 %s)—— %s" % (_f, _ev, _why))
|
||||
else:
|
||||
_cnt = {}
|
||||
for _ev, _c in _cmds:
|
||||
_cnt[_ev] = _cnt.get(_ev, 0) + 1
|
||||
line("[闸门]", "✅ 关键闸门在册 | %s"
|
||||
% " ".join("%s×%d" % (k, v) for k, v in sorted(_cnt.items())))
|
||||
_bad = []
|
||||
for _ev, _c in _cmds:
|
||||
_m = re.search(r'([A-Za-z]:[\\/][^"\']*?\.py)', _c)
|
||||
if _m and not os.path.exists(_m.group(1)):
|
||||
_bad.append("%s(%s)" % (os.path.basename(_m.group(1)), _ev))
|
||||
if _bad:
|
||||
print(" ⚠️ 路径自检:%s 不存在 ⇒ 该钩子**静默失效**"
|
||||
% "、".join(sorted(set(_bad))[:4]))
|
||||
# 每条闸门日志的「最后一行时间」= 它最后一次真被调用的时刻(陈旧 ⇒ 该闸门已掉线)
|
||||
print(" · 各闸门日志最后一行:%s"
|
||||
% " ".join("%s=%s" % (t, _tail_time(p)) for t, p in GATE_LOGS))
|
||||
|
||||
# ── 5b. 会话规则机制体检(🔴 用户 2026-10-02 口径:开会话后**第一件事**)────────
|
||||
# 用户原话逐字(**两句,第二句是纠正**):
|
||||
# ① 「这个会话和协作会话的技能包 运行的第一件事 ,就应该是检查清楚
|
||||
# 所有会话规划是否配置完整且生效,然后标记一个状态」
|
||||
# ② 「就应该是检查清楚 **所有会话规则机制** 是否配置完整且生效,**不是规划 是 规则**」
|
||||
# ⇒ 对象=**规则机制**(钩子 / 闸门 / 技能指针 / 常驻 / 编排…),⛔ 不是"排期规划"。
|
||||
# 首版按①的字面写成「会话规划体检」、只查排期那一面 ⇒ 当天实测出来的三类失效
|
||||
# (钩子指向已退役技能名 / 快照写进幽灵目录 / 每轮注入的记忆指针悬空)**一条都查不到**
|
||||
# ⇒ 按②重做,⚠️ 旧脚本已退役到 `归档/技能包-旧件-20261002/`。
|
||||
# 为什么放在这里:本文件是**所有会话的开工第 0 步**(见文末口令),把它挂在这儿
|
||||
# 才能保证"第一件事"真的发生在第一步,而不是靠人记得。
|
||||
# ⚠️ 逻辑**在技能包里**(skill 就是 skill)—— 这里只做**调用 + 摘要**;
|
||||
# ⛔ 找不到技能包时**显式告警**,⛔ 绝不静默跳过(静默跳过=本项目反复踩的那个坑)。
|
||||
print()
|
||||
def _skills_root():
|
||||
for c in (os.environ.get("DSH_SKILLS_ROOT"),
|
||||
os.path.join(os.environ.get("CODEBUDDY_CONFIG_DIR") or "", "skills"),
|
||||
os.path.join(os.path.expanduser("~"), ".workbuddy", "skills")):
|
||||
if c and os.path.isdir(c):
|
||||
return c
|
||||
return None
|
||||
|
||||
|
||||
_sk = _skills_root()
|
||||
_spc = os.path.join(_sk, "session-mechanism", "scripts", "session-rules-check.py") if _sk else ""
|
||||
if not (_spc and os.path.isfile(_spc)):
|
||||
line("[会话规则]", "⚠️ 体检脚本不在 ⇒ 本项**未跑**(⛔ 不是「没问题」):"
|
||||
"skills/session-mechanism/scripts/session-rules-check.py")
|
||||
else:
|
||||
try:
|
||||
_pr = subprocess.run([sys.executable, _spc, "--ws", WS],
|
||||
capture_output=True, timeout=40)
|
||||
_txt = (_pr.stdout or b"").decode("utf-8", "replace").strip()
|
||||
for _l in _txt.split("\n"):
|
||||
print(" " + _l)
|
||||
if not _txt:
|
||||
_err = (_pr.stderr or b"").decode("utf-8", "replace").strip()
|
||||
line("[会话规则]", "⚠️ 体检无输出(rc=%d)%s"
|
||||
% (_pr.returncode, (":" + _err[:100]) if _err else ""))
|
||||
except Exception as _e:
|
||||
line("[会话规则]", "⚠️ 体检自身异常(⛔ 不影响开工):%s" % str(_e)[:120])
|
||||
|
||||
# ── 5c. 🔴🔴 钩子环境体检(2026-10-02 用户定案· 技能必须先检查环境配置)────────
|
||||
# 为什么单列一项(真实代价:另一工作区为「钩子静默零输出」绕了两轮):
|
||||
# 2026-10-02 实测 —— `reply-style-guard.py` 被注册成文档库里的旧副本,
|
||||
# 它沿 `__file__` 上溯定位技能库失败 ⇒ 落进 `os.path.expanduser('~/.workbuddy/skills')`
|
||||
# 而 **Windows 上 `~` 不是真配置目录** ⇒ 静默零输出,日志只有一行 `core=0 字符`
|
||||
# ⇒ **「机制坏了」与「没配规则」表现完全一样**(原钩子设计判据就是「读不到就静默」)。
|
||||
# 本项判**两样**:① 技能库根能否定位(⛔ 已删 `~` 回落)② 已注册钩子**逐条目标是否存在**。
|
||||
# ⛔ 本项**只读**,⛔ 不写环境标记(标记由 `hooks/_env.py --stamp` 显式写)。
|
||||
_envpy = os.path.join(_sk, "session-mechanism", "scripts", "hooks", "_env.py") if _sk else ""
|
||||
if not (_envpy and os.path.isfile(_envpy)):
|
||||
line("[钩子环境]", "⚠️ 自检模块不在 ⇒ 本项**未跑**(⛔ 不是「没问题」):"
|
||||
"skills/session-mechanism/scripts/hooks/_env.py")
|
||||
else:
|
||||
try:
|
||||
_er = subprocess.run([sys.executable, _envpy, "--ws", WS],
|
||||
capture_output=True, timeout=30)
|
||||
_et = (_er.stdout or b"").decode("utf-8", "replace").strip()
|
||||
for _l in _et.split("\n"):
|
||||
print(" " + _l)
|
||||
if _er.returncode != 0:
|
||||
_es = (_er.stderr or b"").decode("utf-8", "replace").strip()
|
||||
line("[钩子环境]", "❌ 环境**未配好** ⇒ 钩子会静默失效(⛔ 先修再开工,"
|
||||
"否则每轮规则注入都是空的)%s" % ((":" + _es[:140]) if _es else ""))
|
||||
except Exception as _e2:
|
||||
line("[钩子环境]", "⚠️ 体检自身异常(⛔ 不影响开工):%s" % str(_e2)[:120])
|
||||
# 环境标记(用户 2026-10-02 要求:「在对应文件夹创建状态变量和时间作为标记」):
|
||||
# ⛔ 标记缺失/过期**只提示不阻塞**(它是"上次何时体检过"的记录,不是闸门)。
|
||||
# 🔴🔴 2026-10-02 用户拍板 **A 案:装全局,一处配好所有工作区共用** ⇒ 读**全局**那份
|
||||
# (`<配置目录>/env-stamp.json`),⛔ 不再只读工作区那份(那是旧口径的残留)。
|
||||
_st = os.path.join(os.environ.get("CODEBUDDY_CONFIG_DIR") or r"E:\ProgramData\.workbuddy",
|
||||
"env-stamp.json")
|
||||
_st_old = os.path.join(WS, ".workbuddy", "env-stamp.json")
|
||||
if os.path.isfile(_st):
|
||||
try:
|
||||
_sj = json.loads(open(_st, encoding="utf-8").read())
|
||||
print(" · 环境标记(全局口径):%s | scope=%s | ok=%s"
|
||||
% (_sj.get("checked_at", "?"), _sj.get("scope", "?"), _sj.get("ok", "?")))
|
||||
if os.path.isfile(_st_old):
|
||||
print(" ⚠️ 本工作区还留着一份旧 `scope=workspace` 标记 ⇒ **读数不认它**"
|
||||
"(现行口径=全局,用户 2026-10-02 拍板)。⛔ 不影响生效,可删:%s" % _st_old)
|
||||
except Exception:
|
||||
print(" · 全局环境标记存在但**读不出**(⛔ 损坏)⇒ 建议重跑 `_env.py --stamp`")
|
||||
else:
|
||||
print(" · 全局环境标记:**无** ⇒ 首次使用请跑 "
|
||||
"`python \"%s\" --ws \"%s\" --stamp --scope global`" % (_envpy, WS))
|
||||
|
||||
# ── 6. 下一步 ─────────────────────────────────────────────────
|
||||
print()
|
||||
print(" ⇒ 下一步:按**你那条线**(见下面「口令」)的 [入口] §2 第 1 条开工;"
|
||||
@@ -252,4 +417,4 @@ print(" ⇒ 下一步:按**你那条线**(见下面「口令」)的 [入
|
||||
print(" ⇒ 口令(复制给新会话):跑 `state.py`,按 **%s** 那段 §2 第 1 条开工"
|
||||
% os.path.basename(ENTRY).replace("接续入口_", "").replace(".md", "")
|
||||
.rsplit("_", 1)[0])
|
||||
print(" ⇒ 动手前:bash \"%s/scripts/handoff-guard.sh\" --claim-exec \"<你的会话名>\"" % DOCS)
|
||||
print(" ⇒ 动手前:bash \"%s/07-scripts/handoff-guard.sh\" --claim-exec \"<你的会话名>\"" % DOCS)
|
||||
Reference in new issue
Block a user