@mingmaple/dsh-handover 0.0.0-stage → 0.9.12
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/LICENSE +21 -0
- package/README.md +149 -2
- package/docs/migrate.md +61 -0
- package/docs/troubleshoot.md +77 -0
- package/package.json +70 -5
- package/scripts/blank-dsh.mjs +203 -0
- package/scripts/blank-smoke.mjs +981 -0
- package/scripts/build.mjs +61 -0
- package/scripts/check-docs.mjs +74 -0
- package/scripts/e2e-migration.mjs +127 -0
- package/scripts/lib/register.mjs +103 -0
- package/src/client.js +945 -0
- package/src/cordis.patch.yml +16 -0
- package/src/index.mjs +726 -0
- package/src/lib/absrefs.mjs +112 -0
- package/src/lib/api.mjs +291 -0
- package/src/lib/archive.mjs +198 -0
- package/src/lib/brief.mjs +74 -0
- package/src/lib/charter.mjs +28 -0
- package/src/lib/config.mjs +37 -0
- package/src/lib/consent.mjs +84 -0
- package/src/lib/drift.mjs +65 -0
- package/src/lib/events.mjs +89 -0
- package/src/lib/extra-prompt.mjs +41 -0
- package/src/lib/facts.mjs +70 -0
- package/src/lib/gates.mjs +148 -0
- package/src/lib/guide.mjs +103 -0
- package/src/lib/handover-io.mjs +69 -0
- package/src/lib/lint.mjs +152 -0
- package/src/lib/manual.mjs +124 -0
- package/src/lib/mover.mjs +663 -0
- package/src/lib/names.mjs +33 -0
- package/src/lib/output.mjs +11 -0
- package/src/lib/pack-ops.mjs +332 -0
- package/src/lib/peer.mjs +260 -0
- package/src/lib/phase.mjs +36 -0
- package/src/lib/profile.mjs +204 -0
- package/src/lib/prompt.mjs +18 -0
- package/src/lib/registry.mjs +53 -0
- package/src/lib/retarget-helper.tmpl +46 -0
- package/src/lib/role.mjs +251 -0
- package/src/lib/rpc-carrier.mjs +65 -0
- package/src/lib/scaffold.mjs +118 -0
- package/src/lib/schedule.mjs +223 -0
- package/src/lib/session-create.mjs +42 -0
- package/src/lib/session-ops.mjs +408 -0
- package/src/lib/sessions.mjs +143 -0
- package/src/lib/sevenzip.mjs +48 -0
- package/src/lib/status.mjs +177 -0
- package/src/lib/text.mjs +101 -0
- package/src/lib/webroute.mjs +130 -0
- package/src/lib/workspace.mjs +23 -0
- package/src/lib/zstd-log.mjs +65 -0
- package/src/selftest.mjs +84 -0
- package/src/templates/Changelog.skeleton.md +10 -0
- package/src/templates/Charter.skeleton.md +53 -0
- package/src/templates/Handover.global.skeleton.md +56 -0
- package/src/templates/Prompt_Templates.md +96 -0
- package/src/templates/Runtime_Manual.md +166 -0
- package/src/templates/rules/Handover_Rules.md +44 -0
- package/src/templates/rules/Migration_Rules.md +32 -0
- package/src/templates/rules/Misc_Task_Standard.md +23 -0
- package/src/templates/rules/Writing_Rules.md +24 -0
- package/src/tools/ho_brief.mjs +57 -0
- package/src/tools/ho_changelog.mjs +59 -0
- package/src/tools/ho_charter.mjs +89 -0
- package/src/tools/ho_checkpoint.mjs +132 -0
- package/src/tools/ho_diag.mjs +87 -0
- package/src/tools/ho_drift.mjs +41 -0
- package/src/tools/ho_gate.mjs +69 -0
- package/src/tools/ho_handover.mjs +84 -0
- package/src/tools/ho_init.mjs +82 -0
- package/src/tools/ho_pack.mjs +38 -0
- package/src/tools/ho_phase.mjs +37 -0
- package/src/tools/ho_profile.mjs +105 -0
- package/src/tools/ho_sessions.mjs +115 -0
- package/src/tools/ho_snapshot.mjs +104 -0
- package/src/tools/ho_tokens.mjs +57 -0
- package/src/tools/ho_workspace.mjs +217 -0
- package/src/tools/peer_list.mjs +37 -0
- package/src/tools/peer_new.mjs +75 -0
- package/src/tools/peer_peek.mjs +41 -0
- package/src/tools/peer_rename.mjs +31 -0
- package/src/tools/peer_send.mjs +52 -0
- package/src/tools/peer_status.mjs +46 -0
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# 全局 交接文档
|
|
2
|
+
|
|
3
|
+
> 本文件由 **dsh-handover 首次安装向导**生成。§7 里那行 session id 是**占位**——第一个全局会话接手时把它换成本机真实的 id。
|
|
4
|
+
|
|
5
|
+
## 0. 身份与寻址
|
|
6
|
+
|
|
7
|
+
| 项 | 值 |
|
|
8
|
+
|---|---|
|
|
9
|
+
| 会话类型 | 全局 |
|
|
10
|
+
| 本任 | 待第一个全局会话接手 |
|
|
11
|
+
| 链内代数 | 第 1 代(本机序号,接手时核对) |
|
|
12
|
+
| 接任 | 无(首任) |
|
|
13
|
+
| 上级、报告对象 | 用户 |
|
|
14
|
+
| 本文件 | _Control_Center/records/Global_Handover.md |
|
|
15
|
+
| 冲突以谁为准 | rules/Workspace_Charter.md |
|
|
16
|
+
| 修订 | 首次安装向导生成 |
|
|
17
|
+
|
|
18
|
+
## 1. 正在做的事
|
|
19
|
+
|
|
20
|
+
| 项 | 现状 |
|
|
21
|
+
|---|---|
|
|
22
|
+
| 初始化 | 工作区骨架已生成;等第一个标题含「全局」的会话接手 |
|
|
23
|
+
|
|
24
|
+
## 2. 下一步
|
|
25
|
+
|
|
26
|
+
| # | 动作 | 落点 | 完成判据 |
|
|
27
|
+
|---|---|---|---|
|
|
28
|
+
| 1 | 全局会话接手:核闸门、把 §0 身份信息填实 | 本文件 §0 | ho_gate 三项全绿 |
|
|
29
|
+
| 2 | 按需新建项目库 | Projects/ | 各库 RULES.md 24/24 + 上下文水位自查 |
|
|
30
|
+
|
|
31
|
+
## 3. 待用户决定
|
|
32
|
+
|
|
33
|
+
(暂无)
|
|
34
|
+
|
|
35
|
+
## 4. 用户要求逐条落位
|
|
36
|
+
|
|
37
|
+
| # | 用户原话要点 | 状态 | 落在哪 |
|
|
38
|
+
|---|---|---|---|
|
|
39
|
+
| 1 | 安装插件并在指定目录建好全局根 | 已落地 | 本文件 |
|
|
40
|
+
|
|
41
|
+
## 5. 坑点与禁区
|
|
42
|
+
|
|
43
|
+
- 禁区:改 §8.1.1.4 那 24 行正文会让所有项目库对不上——要改就同步改全部库。
|
|
44
|
+
|
|
45
|
+
## 6. 关键路径
|
|
46
|
+
|
|
47
|
+
- 交接:_Control_Center/records/Global_Handover.md
|
|
48
|
+
- 规则:_Control_Center/rules/Workspace_Charter.md
|
|
49
|
+
- 台账:_Control_Center/records/Global_Changelog.md
|
|
50
|
+
- 提示词模板:_Control_Center/templates/Prompt_Templates.md
|
|
51
|
+
|
|
52
|
+
## 7. 会话 id 对照
|
|
53
|
+
|
|
54
|
+
| 角色 | session id | 状态 |
|
|
55
|
+
|---|---|---|
|
|
56
|
+
| 全局 | session-00000000-0000-0000-0000-000000000000 | 占位:接手时换成本机真实的全局会话 id |
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
> **用法**:这是跨会话消息的**唯一来源**,复制后填空即发。**规则**说「何时发」(见 `rules/Handover_Rules.md`、`Migration_Rules.md`),本文件只管「发什么」。行内 `|` 之后是写给人看的提示,**发送前连同竖线一起删掉**。(本行也删)
|
|
2
|
+
|
|
3
|
+
# 会话消息模板
|
|
4
|
+
|
|
5
|
+
## 1 共用骨架(所有消息都由它起头)
|
|
6
|
+
|
|
7
|
+
```text
|
|
8
|
+
你是<角色>,由全局管理对话创建并交接。
|
|
9
|
+
第一条要读的:<绝对路径>(读毕再开始作业)。| 项目→ _Project_Meta/<项目名>/Handover.md;全局→ records/Global_Handover.md;杂项首任→ rules/Misc_Task_Standard.md
|
|
10
|
+
你是平级会话:跑完一轮不会有任何自动通知。会话之间可直接 `peer_send`,不必经全局转述;**默认沉默**:只在三种时机各发一条给现任全局——≥32% 且已写交接、需用户决定、发现全局口径有误;**任务完成不向全局报告**(任务属本会话内部事务);**不得每轮回执,亦不得发送「收到」「已完成」一类确认**(全局不看回执,只看文件末态)。
|
|
11
|
+
你的 session id:<本会话 id>。确需向全局发消息时,以 `peer_send` 发至 <全局 id>。| 不得据 peer_list 推测 id
|
|
12
|
+
水位:≥32% 应先写毕交接、再通知全局,并提醒用户换会话;>35% 不得新开任务。
|
|
13
|
+
边界:只读写 <范围>。删除或覆盖前须先留快照,**删除须经用户确认**。对用户可见一律中文(含工具 description 字段)。
|
|
14
|
+
时间一律**带时区偏移**书写(如 2026-10-21 07:58 +0800),不写「今天、几点」这种会漂移的说法;本机时区 Asia/Shanghai(+08:00),历史记录里的 +00:00 与新记录按**绝对时刻**比较。
|
|
15
|
+
简单任务直接执行、直接答复;工具正常返回即予采信,不重复核验。子智能体按成本使用:单个子智能体自身亦需十余步,五个约等于另开一整个会话。仅当单块 ≥15 步且可机械验收、或确需隔离时方可派发,并发 ≤3;一文件一派、将机械批处理拆成 N 份均属反模式;可脚本批量并抽样验收者应先写脚本。判断不清者默认自行完成(Charter §十一)。
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
> 7 行是**上限**,不是起点:宜少不宜多,只保留「不写就会做错」的条目。
|
|
19
|
+
|
|
20
|
+
## 2 消息一 · 新会话启动
|
|
21
|
+
|
|
22
|
+
### 2.1 全局(接任或首任)
|
|
23
|
+
|
|
24
|
+
```text
|
|
25
|
+
<骨架 6 行>
|
|
26
|
+
你是第 <N> 任全局,标题「全局<N>」,链内代数第 <M> 代。
|
|
27
|
+
作业顺序:读交接 → 核对 §6 的路径与命令是否仍然有效 → 不一致时先对齐再行动。
|
|
28
|
+
阅读范围(按需取用,不要求预先通读):先读交接;全局流程与自己这个角色的硬边界见 Workspace_Charter.md(§7.2 会话与交接、§8 边界与铁律);**写文档时才读** user/Writing_Rules.md 与 templates/ 下对应文件。
|
|
29
|
+
你的职责边界在 Workspace_Charter.md;交接只对「在途状态」有权威。
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
### 2.2 项目(首任或接任)
|
|
33
|
+
|
|
34
|
+
```text
|
|
35
|
+
<骨架 6 行>
|
|
36
|
+
你是「<项目名>」项目的执行对话,标题「<项目名><N>」。
|
|
37
|
+
库根:Projects/<项目名>/<项目名>库/ ;元数据:_Project_Meta/<项目名>/ ;快照:_Temp/<项目名>/Snapshots/ ;版本冻结:_Archives/<项目名>/Backups/
|
|
38
|
+
开始作业前应先读:_Project_Meta/<项目名>/Handover.md → 库根 SKILL.md →(修改文档时)_Control_Center/rules/ 下对应规范。
|
|
39
|
+
两件必查:库内 RULES.md 里必须有「运行期省 token」与「上下文水位自查」两条;缺者应照 Workspace_Charter.md 原文补上(唯一来源,不得自行改动数字)。
|
|
40
|
+
换任由全局执行,不由你执行:进入预警区后只写交接、通知全局并提醒用户,**不得自行调用 peer_new**。
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
### 2.3 杂项(只在首任时由全局发,此后自理)
|
|
44
|
+
|
|
45
|
+
```text
|
|
46
|
+
<骨架 6 行>
|
|
47
|
+
你是杂项任务会话,标题「<任务短名>」,不编号。
|
|
48
|
+
任务目录:_Misc/tasks/<任务短名>/(产物、脚本、日志都放这里)
|
|
49
|
+
三件文件:Project_Overview.md(是什么/边界/验收判据)· Changelog.md(环境事实/变更/回滚,只追加)· Handover.md(在途)
|
|
50
|
+
换任自己办:peer_new({ workspace: 'Misc', title: '<自拟名称>' }) → 查重 → 新 id 写进交接 §0 → 不通知全局。
|
|
51
|
+
本任务:<任务正文>| 用户原话照抄,不改写、不缩范围、不替用户补验收判据
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## 3 消息二 · 项目 → 全局:交接请求
|
|
55
|
+
|
|
56
|
+
```text
|
|
57
|
+
【交接请求】<项目名>|<本会话 id>|水位 <xx>%
|
|
58
|
+
已写完 _Project_Meta/<项目名>/Handover.md(<行数> 行,已归档旧版到 _Archives/<项目名>/Handover/)。
|
|
59
|
+
在途:<有/无>;一句话:<现在卡在哪>。
|
|
60
|
+
请建接任会话。
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## 4 消息三 · 全局 → 项目:已交接
|
|
64
|
+
|
|
65
|
+
```text
|
|
66
|
+
【已交接】<项目名>|接任已建:<项目名><N>|session id <新 id>
|
|
67
|
+
你的交接已归档,可以停止操作:不得新开任务,不得修改文件。
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## 5 消息四 · 全局 → 项目:新任务派发
|
|
71
|
+
|
|
72
|
+
```text
|
|
73
|
+
【新任务】<一句话说清要做什么>
|
|
74
|
+
判据:<跑一次看到什么即算完成>
|
|
75
|
+
落点:<允许改的目录/文件>
|
|
76
|
+
约束:<本次不许碰的东西;无则删本行>
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
> 派活前先 `peer_list`:已有会话且 <32% **必须复用它**,不许新建;≥32% 先让其写完交接再新建。
|
|
80
|
+
|
|
81
|
+
## 6 消息五 · 迁移通知(打包前刷交接)
|
|
82
|
+
|
|
83
|
+
```text
|
|
84
|
+
【平台迁移通知】用户要把整个工作区搬到另一个平台,打包前需要你刷一次交接。
|
|
85
|
+
只做三件事,1 分钟内完成;**不要开新任务、不要跑全量自检、不要改库内文件**:
|
|
86
|
+
1) 把 _Project_Meta/<项目名>/Handover.md 的「在途、下一步、关键路径」刷成最新;
|
|
87
|
+
2) 绝对路径改成 <工作区根>/... 的相对写法(新平台的根不同);
|
|
88
|
+
3) 回一行:迁移交接完成|<项目名>|在途:<有/无>|<一句话>
|
|
89
|
+
搬库不带对话记录,Handover.md 是唯一的连续性来源。
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
## 7 发送纪律
|
|
93
|
+
|
|
94
|
+
> 时机与纪律(不寒暄、一次一件事、并发 ≤3、默认沉默、未被要求勿唤醒、不转述)见 `rules/Workspace_Charter.md` §7.2.4 第 9 条,本节不重复。
|
|
95
|
+
>
|
|
96
|
+
> 本节只留一条**模板自身**的要求:发送前删掉所有 `|` 之后的提示与 `<>` 占位,只留实值。
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
# dsh-handover 运行期完整手册
|
|
2
|
+
|
|
3
|
+
> 这是**按需取回**的全文:系统提示里只留短段与指针,需要细节时用 `ho_charter(doc="runtime")` 取本文。
|
|
4
|
+
>
|
|
5
|
+
> **内置即默认**:本文、Charter 骨架、交接、台账骨架、提示词模板、四份规则文档都随插件发布(`dist/templates/`)。工作区里的同名文件只是「本地化覆盖」;删掉就自动回到内置,功能不降级。`ho_init` 是把默认口径落盘方便修改,**不是使用前提**。
|
|
6
|
+
> 口径冲突时以工作区的 `_Control_Center/rules/` 为准;本文只讲「插件怎么用、角色怎么分」。
|
|
7
|
+
|
|
8
|
+
## 1. 角色四档
|
|
9
|
+
|
|
10
|
+
| 角色 | 判定顺序 | 交接文件 | 可见工具 |
|
|
11
|
+
|---|---|---|---|
|
|
12
|
+
| 全局 | 角色表 → 标题含「全局」 → 全局工作区 | `_Control_Center/records/Global_Handover.md` | 22 个(全部) |
|
|
13
|
+
| 项目 | 角色表(带项目名)→ cwd 在 `Projects/<名>/` → 首条提示含 `Projects/<名>/` | `_Project_Meta/<项目>/Handover.md` | 9 个 |
|
|
14
|
+
| 杂项 | 角色表(带任务名)→ cwd 在 `_Misc/tasks/<任务>/` | `_Misc/tasks/<任务>/Handover.md` | 9 个 |
|
|
15
|
+
| 普通 | 子会话,或都不中 | 无(不参与交接) | 0 个(不注入提示段) |
|
|
16
|
+
|
|
17
|
+
角色表在 `~/.dsh/storages/dsh-handover.roles.json`,是**本机状态**,不进仓库、不进迁移包。子会话默认普通;需要时由全局在角色表里指定。
|
|
18
|
+
设定入口:设置 → dsh-handover →「会话角色」;或对全局说「把 Demo1 设成项目角色」,它用 `ho_sessions(action="role")` 办。
|
|
19
|
+
|
|
20
|
+
## 2. 工具清单(22 个)
|
|
21
|
+
|
|
22
|
+
### 只读
|
|
23
|
+
- `ho_brief` —— 起始与收束的单条命令:水位、闸门、会话清点、阶段建议。
|
|
24
|
+
- `ho_gate` —— 通用约束条闸门(按骨架档案的库型判定)+交接文档体检。
|
|
25
|
+
- `ho_sessions` —— 会话清点;也用来设定角色、建会话、出提示词、归档、解归档(`action` 分流)。
|
|
26
|
+
- `ho_phase` —— 本会话水位、已压缩次数、锁状态、建议动作。
|
|
27
|
+
- `ho_charter` —— 按节取规则正文:`doc`=`charter`(缺省)、`handover_rules`、`migration_rules`、`misc_standard`、`writing_rules`、`runtime`(本文全文);`section` 留空只列目录。**工作区没有对应规则文件时退回插件内置默认口径**(输出会注明),所以换到空白机器也不会变空壳。
|
|
28
|
+
- `ho_drift` —— 交接漂移:Δ=当前轮−交接轮,>3 需刷新。
|
|
29
|
+
- `ho_diag` —— 插件自检:服务、调度器、压缩计数、工作区、角色判定。
|
|
30
|
+
- `ho_tokens` —— 会话 token 体检:轮数、步数、每步 token、最贵单条返回。
|
|
31
|
+
- `peer_list` / `peer_status` / `peer_peek` —— 平级会话清点与近期对话(peek 会花本会话 token)。
|
|
32
|
+
|
|
33
|
+
### 写
|
|
34
|
+
- `ho_handover` —— 读、写、追加、归档**本会话自己的**交接(写前自动备份、写后按角色体检)。
|
|
35
|
+
- `ho_checkpoint` —— 写交接 → 体检 → 登记压缩 → 轮末自动压缩。`resume=continue`(缺省)压缩后续跑;`resume=wait` 表示任务已收尾,压缩后不唤醒。
|
|
36
|
+
- `ho_snapshot` —— 库快照:逐字节复制、sha256 清单、来源说明。
|
|
37
|
+
- `ho_changelog` —— 按固定表格追加一行台账。
|
|
38
|
+
- `ho_pack` —— `action`:`check`(打包前置闸门)、`pack`(打包,缺省)、`verify`(校验包是否仍是现版)、`restore`(默认预演,覆盖前备份,从不删除目标文件)。`format=auto|7z|tar`:有 7-Zip 出 `.7z`,没有就出 `.tar.gz`(内置归档器,功能等价);解包按魔数识别,包内路径必须是相对路径且不含 `..`,否则整体中止。
|
|
39
|
+
- `ho_workspace` —— 工作区 list、create、delete(删除只删注册)与换盘动作。
|
|
40
|
+
- `ho_init` —— 首次安装:在指定目录下建整套全局文件体系,可顺带注册工作区。
|
|
41
|
+
- `ho_profile` —— 项目或杂项的骨架注册:由所属会话通读自己的库后写入并校验。
|
|
42
|
+
- `peer_send` / `peer_new` / `peer_rename` —— 投递消息、建平级会话、只改标题。
|
|
43
|
+
|
|
44
|
+
### ho_sessions 的 action
|
|
45
|
+
| action | 作用 | 关键参数 |
|
|
46
|
+
|---|---|---|
|
|
47
|
+
| `list`(缺省) | 会话清点 | `limit`、`warn`、`include_subagents` |
|
|
48
|
+
| `role` | 设定会话角色(写角色表+当场收窄+可选投整理指令) | `session_id`、`role`、`project`、`task`、`notify` |
|
|
49
|
+
| `new` | 建下一代会话并唤醒,自动投启动提示词(按接任者角色取**接任者自己的**交接路径) | `title`、`workspace`、`cwd`、`message`、`project`、`force` |
|
|
50
|
+
| `order` | 远程给某个会话下指令:`order=handover` 让它写交接再换任;`order=compact` 给它排压缩(目标必须在内存、要有合格交接、未压满) | `session_id`、`order`、`resume`、`extra`、`force` |
|
|
51
|
+
| `prompt` | 按 `templates/Prompt_Templates.md` 渲染可粘贴的启动提示词 | `kind`、`project`、`title`、`sessionId`、`globalSessionId` |
|
|
52
|
+
| `place` | 把已存在的会话移进工作区(会话 cwd 必须正是该工作区根) | `session_id`、`workspace` |
|
|
53
|
+
| `archive` | 归档会话(相当于图形界面的删除) | `session_id`、`force` |
|
|
54
|
+
| `unarchive` | 解除归档 | `session_id` |
|
|
55
|
+
|
|
56
|
+
## 3. 水位与交接协议
|
|
57
|
+
|
|
58
|
+
- 32% 提醒、35% 交接线、85% 硬顶;同一会话最多压缩 3 次(共 4 段使用)。数值来自 `config.mjs`,提示段与设置页由 `lib/facts.mjs` 统一渲染。
|
|
59
|
+
- 到线流程:`ho_handover` 写交接(体检不通过就拒绝压缩)→ `ho_checkpoint` 登记(`resume=continue|wait`)→ 轮末自动压缩 → 继续时投续跑消息;收尾时不唤醒,等用户派新任务。
|
|
60
|
+
- 第 4 段到线:写交接 + 全局用 `ho_sessions(action="new")` 建下一任;项目会话自己**不要**建会话(换任由全局做)。
|
|
61
|
+
- 关键步骤不要压:`ho_checkpoint` 传 `at_risk=true`,它只提醒不压;硬顶 85% 除外。
|
|
62
|
+
- 压缩不可逆:历史会被摘要替换,所以「先把交接写合格」是硬前提。
|
|
63
|
+
|
|
64
|
+
## 4. 交接文件的结构要求
|
|
65
|
+
|
|
66
|
+
- **全局**(`_Control_Center/records/Global_Handover.md`):必须有 §0 身份与寻址(含 7 个字段)、§1 正在做的事、§2 下一步、§3 待用户决定(早期文档沿用旧标题,亦判通过)、§4 用户要求逐条落位、§5 坑点与禁区、§6 关键路径、§7 会话 id 对照(至少一行带 session id,且不能有「待回填」);不得残留模板占位符。
|
|
67
|
+
- **项目、杂项**:必须有「正在做的事」(实质内容 ≥30 字)、「下一步待办」(至少一条表格行或列表项);不得残留占位符。
|
|
68
|
+
- 体检命令即工具:`ho_gate`(全部)、`ho_handover` 写后自动体检。
|
|
69
|
+
|
|
70
|
+
## 5. 派发与成本纪律(判据来源 Charter §十一)
|
|
71
|
+
|
|
72
|
+
1. **先核算成本**:每个子智能体都是一个完整会话——自带系统提示与任务提示,自身亦需十余步,结论还须回主会话再审读一遍。实测一次「分模块清理注释」派 5 个,各自占用 16%–20% 上下文,合计约等于另开一整个会话。
|
|
73
|
+
2. **三条判据全部满足方可派发**:① 单块 ≥15 步或需读 ≥10 个文件;② 确需隔离或并行等待;③ 结果可机械验收(能以一句「通过、不通过」判定)。任缺其一即应自行完成;判断不清者亦自行完成。
|
|
74
|
+
3. **反模式(禁止)**:一文件、一模块、一函数一子智能体;将机械批处理拆成 N 份(注释清理、逐文件润色、重命名、格式统一、补全文档);先派发再拟定验收标准;同一任务并发超过 3。
|
|
75
|
+
4. **优先「脚本 + 抽检」**:可脚本批量处理、再抽 3 个样本目视检查者,一律先写脚本。子智能体仅用于无法脚本化的判断。
|
|
76
|
+
5. **派发前先报预算**:以「拟派 N 个子智能体,每个 ≤M 步」的形式告知用户;N>3 或合计超过一个会话的量时,须先征得用户同意。
|
|
77
|
+
6. **续用优先:工序延续不另行建立会话**。子智能体完成后仅转入静止状态(inactive),其对话、已读文件与既定选择均予保留,一次 `send_message` 即可继续。因此**同一批文件的下一道工序**——改尺寸、换文案、出占位版、补变体、修一处细节——必须交回**原作者**,不得另行建立子智能体。另行建立等于令新会话重读同一批文件、重新推断结构,无异于将上一轮的上下文再购置一遍。
|
|
78
|
+
- 检索既有子智能体:以 `list_agents`(仅列直接子项)确认其状态为 inactive,并对照其上一轮交付的文件清单决定派发对象。
|
|
79
|
+
- 续用消息宜简:说明工序、给出脚本或约束、列出文件清单即可,不必重述背景——对方保有原上下文。
|
|
80
|
+
- 仅在三种情形下方可另行建立:原作者上下文将满;新工序与其产物无关;需要**独立复核**其结论(此时应刻意改派他人,并声明属复核)。
|
|
81
|
+
|
|
82
|
+
## 6. 给项目会话发提示词的硬要求
|
|
83
|
+
|
|
84
|
+
- 必须以 `_Control_Center/templates/Prompt_Templates.md` §1 共用骨架起头,保留「默认沉默·三种时机」那一行。
|
|
85
|
+
- **不要索要回执、不要写「每轮报告」**;写完即为完成,证据看文件末态。
|
|
86
|
+
- 换任提示词必须带:新会话自己的 session id、现任全局 session id、它自己的交接绝对路径。
|
|
87
|
+
|
|
88
|
+
## 7. 闸门口径
|
|
89
|
+
|
|
90
|
+
- 通用约束条只在骨架档案标为 `library` 形态的项目库里强制:逐行落进该库 `RULES.md`,并有「上下文水位自查」;构建型、动画型等项目可设 `gate.libraryBlock=false`,不再套用。
|
|
91
|
+
- 骨架档案由各项目会话自己通读本库后注册(`ho_profile(action=register)`);未注册时按默认骨架运行,`ho_gate` 标「待注册」,不判红。
|
|
92
|
+
- 有缺只**派发**给该项目会话,全局**不代写**项目库内文件。
|
|
93
|
+
|
|
94
|
+
## 8. 迁移与恢复
|
|
95
|
+
|
|
96
|
+
1. `ho_pack({action:"check", items:[...]})` 先过前置闸门(会话静止、交接到位)。
|
|
97
|
+
2. `ho_pack({action:"pack", kind:"project"|"task"|"global", name:"…"})` 出包,含 manifest、CHECKSUMS、MIGRATION.md 与绝对路径体检。
|
|
98
|
+
3. 换机:解压包 → 装插件 → `ho_pack({action:"verify", pack:"<包>"})` 看是否仍是现版。
|
|
99
|
+
4. `ho_pack({action:"restore", pack:"<包>", dry_run:true})` 预演,确认后 `apply=true`。
|
|
100
|
+
5. **会话本身不迁移**:跨机连续性只靠交接文档。
|
|
101
|
+
|
|
102
|
+
## 9. 设置页与搬迁
|
|
103
|
+
|
|
104
|
+
- 设置页数据走**宿主认证的 `/api` 载体**(`/api/dsh-handover`),旧 HTTP 路由只作回退;页面里裸 fetch 在本机 GUI 上会被 403 挡掉。
|
|
105
|
+
- 换盘四步:`ho_workspace(action="move-probe", to=…)` → `action="move"`(复制并逐文件 sha256 校验)→ `action="move-switch"`(派后台助手,退出 DSH 后自动完成改名与建链接;也可双击生成的 `dsh-handover.move.cmd`)→ `action="move-cleanup", confirm=true`。
|
|
106
|
+
- 切换后原路径成为指向新位置的链接(Windows 为 junction,Linux、macOS 为符号链接),注册表、profile 与会话工作目录均无需修改;回退方式为删除链接、并将 `.old-*` 改回原名。
|
|
107
|
+
- 切换必须在 DSH 退出后执行:源目录在 DSH 运行期间被占用,目录改名与建立链接均会失败。
|
|
108
|
+
|
|
109
|
+
## 10. 硬规定
|
|
110
|
+
|
|
111
|
+
- 对用户可见的一切一律中文(工具 description、选项、清单、表格状态词都算)。
|
|
112
|
+
- 时间一律带偏移书写:`2026-10-23 21:40 +0800`。
|
|
113
|
+
- 不虚构、不越权、不擅自删除;项目库内文件只由该项目会话自己改。
|
|
114
|
+
|
|
115
|
+
## 11. 首次安装与工作区
|
|
116
|
+
|
|
117
|
+
- 两个入口**等价**:设置页「首次安装」(粘贴已存在目录 → 建立全局文件体系 → 注册为工作区);或在对话里执行 `ho_init(parent=…, register=true)`。
|
|
118
|
+
- 未接入工作区时,闸门、交接体检、会话水位三项无数据;插件本体仍可用。
|
|
119
|
+
- 注册工作区亦可单独执行 `ho_workspace(action="create", path=…, title=…)`;删除只删注册,目录与日志不动。
|
|
120
|
+
- 项目与杂项接入后先读自己的库,再写 `project.profile.json`、`task.profile.json` 并 `ho_profile(action=register)` 注册骨架。
|
|
121
|
+
- 会话标题规范(全局、项目、杂项)见工作区 Charter §6.2;新建会话必须当场给标题。
|
|
122
|
+
|
|
123
|
+
## 12. 提示词预算与增删规则
|
|
124
|
+
|
|
125
|
+
### 12.1 分层(谁在每轮付费)
|
|
126
|
+
|
|
127
|
+
| 层 | 内容 | 何时进入上下文 | 体积上限(加权 token) |
|
|
128
|
+
|---|---|---|---|
|
|
129
|
+
| L0 常驻 | 角色段(全局五节;项目、杂项九行) | **每一轮** | 全局 320;项目 300;长路径与迁移提示档 360、340 |
|
|
130
|
+
| L0 用户内容 | 全局前置提示词(用户开关,默认关闭) | 开启且非空时每轮 | 由用户自管,不计入本插件预算 |
|
|
131
|
+
| L1 手册 | 本文件 | 按需取回,可**按节**取 | 不设上限(不进每轮) |
|
|
132
|
+
| L2 规则文档 | 工作区 `_Control_Center/rules/` | 按需 | 由工作区自管 |
|
|
133
|
+
|
|
134
|
+
> 加权 token 的算法:CJK 与全角标点每字符 1 token,其余每 4 字符 1 token。上限集中在 `lib/facts.mjs` 的 `TOKEN_BUDGET`,冒烟断言直接引用该表。工具描述单条上限 70 token。
|
|
135
|
+
>
|
|
136
|
+
> L0 是成本主体:它按轮次乘算,所以只放「每轮都要用到的判定」。任何「一个会话生命周期里只用几次」的流程,一律以指针落到 L1。
|
|
137
|
+
|
|
138
|
+
### 12.2 布局(改动必须落在既有骨架内)
|
|
139
|
+
|
|
140
|
+
- **全局段**:五节固定——身份、权限、节奏、交接、按需取用。
|
|
141
|
+
- **项目、杂项段**:九行固定——写入范围、库根、骨架、临时与归档、交接、水位与压缩、对外通信、指针;有迁移时另加一行。
|
|
142
|
+
- **普通段**:不注册任何提示段。
|
|
143
|
+
- 新增内容先在既有节内找位置;找不到位置,说明该内容属于 L1,而不是新增一节。
|
|
144
|
+
|
|
145
|
+
### 12.3 准入四条(进 L0 的门槛)
|
|
146
|
+
|
|
147
|
+
1. **每轮都要用到的判定**才可进 L0;有明确触发时机的一次性流程,一律写指针。
|
|
148
|
+
2. 同一规则**只写一处**;由所在节表达作用域,不靠重复强调。
|
|
149
|
+
3. 进 L0 必须指明所属节;无法归属则不进。
|
|
150
|
+
4. 每句都须回答:能否换成指针?不能替代的理由是什么(写入 CHANGELOG)。
|
|
151
|
+
|
|
152
|
+
### 12.4 增删流程
|
|
153
|
+
|
|
154
|
+
- **增**:判定是否每轮需要 → 若否,写进 L1 并在「按需取用」加一行指针 → 若必须进 L0,指明节次 → 跑 `blank-smoke`(体积与结构断言)→ CHANGELOG 记「加了什么、为什么不能是指针、体积变化」。
|
|
155
|
+
- **删**:先查引用(冒烟断言、手册同项、其他角色段)→ 同步删除 L1 与 L0 的重复表述,避免「常驻删了、文档没补」→ 再跑冒烟。
|
|
156
|
+
- **改**:一次改一件事;改完对比体积,超上限即回退,不允许「先合并、下次再压」。
|
|
157
|
+
|
|
158
|
+
### 12.5 检查清单(每次动提示词后照着过)
|
|
159
|
+
|
|
160
|
+
1. 体积:全局段 ≤320、项目段 ≤300(含迁移提示与真实长路径时按 360、340 档)。
|
|
161
|
+
2. 结构:全局五节、项目九行各出现一次,顺序不变。
|
|
162
|
+
3. 一处一规则:会话间通信规则、压缩授权、交接线各只出现一次。
|
|
163
|
+
4. 禁止回灌:工作流细节(`ho_pack`、`move-probe`、具体命令步骤)不得出现在 L0。
|
|
164
|
+
5. 手册同步:凡 L0 指向的 section 必须在本文中存在且标题可匹配。
|
|
165
|
+
|
|
166
|
+
> 增长护栏由测试执行,不靠自觉:断言失败即视为缺陷,不允许带病提交。
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# 交接文档规范(插件自带默认口径)
|
|
2
|
+
|
|
3
|
+
> 这份是 dsh-handover 首次安装时铺下的默认版本,可以按自己的工作方式改写。
|
|
4
|
+
> 插件只做两件硬事:**写前备份、写后按结构体检**;结构不达标时 `ho_checkpoint` 拒绝排压缩。
|
|
5
|
+
|
|
6
|
+
## 1. 谁的位置
|
|
7
|
+
|
|
8
|
+
| 角色 | 交接文件 |
|
|
9
|
+
|---|---|
|
|
10
|
+
| 全局 | `_Control_Center/records/Global_Handover.md` |
|
|
11
|
+
| 项目 | `_Project_Meta/<项目名>/Handover.md` |
|
|
12
|
+
| 杂项 | `_Misc/tasks/<任务名>/Handover.md` |
|
|
13
|
+
|
|
14
|
+
**绝不串用**:项目会话读到全局交接,等于把别人的状态当成自己的(0.2.x 的真实故障)。
|
|
15
|
+
|
|
16
|
+
## 2. 什么时候写
|
|
17
|
+
|
|
18
|
+
- 水位 **32%**:开始收口,别开新活。
|
|
19
|
+
- 水位 **35%**:必须写完交接 → `ho_checkpoint` 登记 → 轮末自动压缩 → 续跑。
|
|
20
|
+
- 水位 **85%**:硬顶,不压也得压。
|
|
21
|
+
- 同一会话最多压 3 次;第 4 段写交接并通知全局换任(**项目会话不要自己建会话**)。
|
|
22
|
+
|
|
23
|
+
## 3. 必须写清(缺一项就会在换任时丢信息)
|
|
24
|
+
|
|
25
|
+
1. **正在做什么、到哪一步**(版本号、产物路径、构建是否通过)。
|
|
26
|
+
2. **下一步按顺序**:每一步写「做什么、动哪个文件、完成判据」。
|
|
27
|
+
3. **环境与路径**:工具版本、安装位置、用的源。
|
|
28
|
+
4. **已完成待验收** 与 **要求了没落地** 分开写,不要混。
|
|
29
|
+
5. **坑点与禁区**:踩过的错、不能再动的文件。
|
|
30
|
+
6. **会话 id 对照**:在任会话、已归档的前任。
|
|
31
|
+
|
|
32
|
+
## 4. 硬规矩
|
|
33
|
+
|
|
34
|
+
- **机密只写位置,不写值**:密码、token、证书写在哪个文件,不要抄进交接正文。
|
|
35
|
+
- 时间一律带偏移(例 `2026-10-24 22:40 +0800`)。
|
|
36
|
+
- 不写「大概」「应该」这类没核过的话;没核就写「未核,待确认」。
|
|
37
|
+
- 写交接用 `ho_handover(action="write")`、`action="append"`,不要手改文件(会绕过备份与体检)。
|
|
38
|
+
- 归档命名:`_Archives/<范围>/Handover/<原文件名>_<YYYYMMDD_HHMMSS>.md`(例 `Handover_20261108_144307.md`);杂项归档自包含在 `_Misc/tasks/<任务>/_archive/`。
|
|
39
|
+
|
|
40
|
+
## 5. 体检判据(插件自动跑)
|
|
41
|
+
|
|
42
|
+
- 有标题、有「正在做的事」实质内容、有「下一步」条目、无残留占位符。
|
|
43
|
+
- 全局交接另需 §0 的七个字段与 §7 至少一行 session id。
|
|
44
|
+
- 体检结论:`通过` / `缺节` / `不合格`;`缺节` 允许压缩,`不合格` 必须补完再压。
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# 迁移规范(插件自带默认口径)
|
|
2
|
+
|
|
3
|
+
> 迁移靠 `ho_pack` 四件套:`check` → `pack` → `verify` → `restore`。
|
|
4
|
+
|
|
5
|
+
## 1. 前置检查 `action="check"`
|
|
6
|
+
|
|
7
|
+
- 在役会话必须**静止**(默认 90 分钟无活动、且不在跑)。
|
|
8
|
+
- 交接必须存在且体检通过。
|
|
9
|
+
- 不通过就等,不要「先打包再说」。
|
|
10
|
+
|
|
11
|
+
## 2. 打包 `action="pack"`
|
|
12
|
+
|
|
13
|
+
- 目标:`kind=global|project|task` + `name`。
|
|
14
|
+
- 产物含 `manifest.json`(逐文件 sha256)、`CHECKSUMS.sha256`、`MIGRATION.md`。
|
|
15
|
+
- 打包格式:本机有 7-Zip 时出 `.7z`;没有时自动出 `.tar.gz`(插件自带归档器,二者功能等价)。
|
|
16
|
+
- 包内**只报告绝对路径、不改写文件内容**——迁移后由人或项目会话按新机器改。
|
|
17
|
+
|
|
18
|
+
## 3. 校验 `action="verify"`
|
|
19
|
+
|
|
20
|
+
- 逐文件比对 sha256:一致 / 漂移 / 缺失。
|
|
21
|
+
- 漂移≠0 ⇒ 包已作废,必须重打。
|
|
22
|
+
|
|
23
|
+
## 4. 恢复 `action="restore"`
|
|
24
|
+
|
|
25
|
+
- **默认预演**:只报「新增 N / 覆盖 M」,不动任何文件。
|
|
26
|
+
- `apply=true` 才写;被覆盖的旧文件先备份到 `_Temp/_Global/Snapshots/restore-<时间>/`。
|
|
27
|
+
- **从不删除**目标处原有文件。
|
|
28
|
+
- 包内路径若是绝对路径或带 `..`,直接拒绝(防越界写盘)。
|
|
29
|
+
|
|
30
|
+
## 5. 会话不迁移
|
|
31
|
+
|
|
32
|
+
连续性只靠交接文档;新机器上由 `ho_sessions(action="new")` 建新会话,读交接后继续作业。
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# 杂项任务规范(插件自带默认口径)
|
|
2
|
+
|
|
3
|
+
## 1. 什么算杂项
|
|
4
|
+
|
|
5
|
+
一次性或短期的活:不建项目库、不进 `_Project_Meta/Registry.md`。落点 `_Misc/tasks/<任务短名>/`。
|
|
6
|
+
|
|
7
|
+
## 2. 目录约定
|
|
8
|
+
|
|
9
|
+
| 路径 | 用途 |
|
|
10
|
+
|---|---|
|
|
11
|
+
| `_Misc/tasks/<任务>/` | 工作文件、`RULES.md`、`Handover.md` |
|
|
12
|
+
| `_Temp/_Misc/<任务>/Snapshots/` | 覆盖前快照 |
|
|
13
|
+
| `_Archives/_Misc/<任务>/` | 归档 |
|
|
14
|
+
|
|
15
|
+
## 3. 边界
|
|
16
|
+
|
|
17
|
+
- 只写上面三处;`_Control_Center`、`Projects`、其他任务目录都不碰。
|
|
18
|
+
- 接任:杂项可以自建 Misc 换任(`ho_sessions(action="new")` 指向 Misc 工作区),项目会话不许。
|
|
19
|
+
- 交接与水位协议同项目(32% 提醒 / 35% 交接线 / 85% 硬顶)。
|
|
20
|
+
|
|
21
|
+
## 4. 升级为项目
|
|
22
|
+
|
|
23
|
+
任务变成长期项目时:建 `Projects/<名>/<名>库/`,移入工作文件,写 `_Project_Meta/<名>/`,再由全局登记进 `Registry.md`。
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# 写作规范(插件自带默认口径)
|
|
2
|
+
|
|
3
|
+
## 1. 语言与时间
|
|
4
|
+
|
|
5
|
+
- 对用户可见的一切一律**中文**;专有名词、代码、命令保留原文。
|
|
6
|
+
- **一律使用标准书面语**:不得出现口语、俚俗与拟人化表达(例如「搞定」「弄好」「别忘」「收工」「点单」「拍板」「跑一遍」「看一眼」「咱们」);命令式统一写作「应当、不得、须、宜」。
|
|
7
|
+
- 时间一律带偏移:`2026-10-24 22:40 +0800`。不带偏移的时间视为不完整。
|
|
8
|
+
|
|
9
|
+
## 2. 结构
|
|
10
|
+
|
|
11
|
+
- 先结论、再证据、最后待办;能用表格就不用长段落。
|
|
12
|
+
- 一个文档只有一个主题;分节用 `##`,不要超过三级。
|
|
13
|
+
- 路径写相对工作区根的形式(`Projects/<项目>/...`),不要写死本机盘符。
|
|
14
|
+
|
|
15
|
+
## 3. 事实纪律
|
|
16
|
+
|
|
17
|
+
- 不虚构:没跑过的验证不写「已验证」,没读过的文件不写「已检查」。
|
|
18
|
+
- 区分「已完成待验收」与「要求了没落地」。
|
|
19
|
+
- 引用别人的话要标出处;引用自己的判断要标「推测」。
|
|
20
|
+
|
|
21
|
+
## 4. 台账
|
|
22
|
+
|
|
23
|
+
- 结构性改动追加一行到 `_Control_Center/records/Global_Changelog.md`(`ho_changelog`)。
|
|
24
|
+
- 一行说清「改了什么、为什么、影响面」,不复述过程。
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/** ho_brief —— 一条命令汇总:水位 + 闸门 + 会话清点 + 阶段建议。 */
|
|
2
|
+
import { textOutput } from '../lib/output.mjs';
|
|
3
|
+
import { nameOf } from '../lib/names.mjs';
|
|
4
|
+
import { NL } from '../lib/text.mjs';
|
|
5
|
+
import { syncCheck } from '../lib/gates.mjs';
|
|
6
|
+
import { scanSessions, readSelfPressure } from '../lib/sessions.mjs';
|
|
7
|
+
import { composeBrief } from '../lib/brief.mjs';
|
|
8
|
+
import { decide } from '../lib/phase.mjs';
|
|
9
|
+
import { computeDrift } from '../lib/drift.mjs';
|
|
10
|
+
import { watermark } from '../lib/facts.mjs';
|
|
11
|
+
|
|
12
|
+
export function pmBriefTool(env) {
|
|
13
|
+
const w = watermark(env.cfg);
|
|
14
|
+
return {
|
|
15
|
+
name: nameOf('brief'),
|
|
16
|
+
description:
|
|
17
|
+
'起始与收束的单条命令:水位、闸门、会话清点、阶段建议。只读。',
|
|
18
|
+
parameters: {
|
|
19
|
+
type: 'object',
|
|
20
|
+
additionalProperties: false,
|
|
21
|
+
properties: {
|
|
22
|
+
threshold: { type: 'number', description: '预警阈值百分比(默认 ' + w.warnValue + ')' },
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
output: textOutput(),
|
|
26
|
+
async execute(args, exec) {
|
|
27
|
+
const ws = env.resolveWs(exec);
|
|
28
|
+
const warn = typeof args.threshold === "number" ? args.threshold : w.warnValue;
|
|
29
|
+
const gate = syncCheck(ws);
|
|
30
|
+
const meId = env.selfId(exec);
|
|
31
|
+
const self = readSelfPressure(env.cacheDir(), meId);
|
|
32
|
+
const scan = scanSessions({
|
|
33
|
+
dir: env.cacheDir(),
|
|
34
|
+
meId,
|
|
35
|
+
meCwd: env.selfCwd(exec),
|
|
36
|
+
warn,
|
|
37
|
+
silentHours: env.cfg.sessionScanHours,
|
|
38
|
+
});
|
|
39
|
+
const drift = computeDrift({ ws, cacheDir: env.cacheDir(), slack: env.cfg.driftSlack, all: false });
|
|
40
|
+
const ratio = self.used === null || self.win === null ? null : self.used / self.win;
|
|
41
|
+
const adv = decide({ ratio, compactionsUsed: 0, cfg: env.cfg, lockState: "unknown" });
|
|
42
|
+
const selfPct = self.used === null || self.win === null ? null : (self.used / self.win) * 100;
|
|
43
|
+
const ok = gate.ok && (selfPct === null || selfPct < warn);
|
|
44
|
+
const lines = composeBrief({
|
|
45
|
+
cfg: env.cfg,
|
|
46
|
+
selfUsed: self.used,
|
|
47
|
+
selfWin: self.win,
|
|
48
|
+
gate,
|
|
49
|
+
scan,
|
|
50
|
+
drift,
|
|
51
|
+
advice: { text: adv.action + " —— " + adv.reason },
|
|
52
|
+
ok,
|
|
53
|
+
});
|
|
54
|
+
return { text: lines.join(NL) };
|
|
55
|
+
},
|
|
56
|
+
};
|
|
57
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/** ho_changelog —— 按表格格式追加一行到 Global_Changelog.md。 */
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
import { nameOf } from '../lib/names.mjs';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
import { textOutput } from '../lib/output.mjs';
|
|
6
|
+
import { NL, readUtf8, isFile } from '../lib/text.mjs';
|
|
7
|
+
|
|
8
|
+
function stamp() {
|
|
9
|
+
const d = new Date();
|
|
10
|
+
const p = (n) => (n < 10 ? '0' + n : '' + n);
|
|
11
|
+
return d.getFullYear() + '-' + p(d.getMonth() + 1) + '-' + p(d.getDate());
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function pmChangelogTool(env) {
|
|
15
|
+
return {
|
|
16
|
+
name: nameOf('changelog'),
|
|
17
|
+
description:
|
|
18
|
+
'按固定表格追加一行台账。',
|
|
19
|
+
parameters: {
|
|
20
|
+
type: 'object',
|
|
21
|
+
additionalProperties: false,
|
|
22
|
+
required: ['summary'],
|
|
23
|
+
properties: {
|
|
24
|
+
summary: { type: 'string', description: '这一行写什么(中文)' },
|
|
25
|
+
executor: { type: 'string', description: '执行者(默认本会话 id)' },
|
|
26
|
+
date: { type: 'string', description: '日期 YYYY-MM-DD(默认今天)' },
|
|
27
|
+
},
|
|
28
|
+
},
|
|
29
|
+
output: textOutput(),
|
|
30
|
+
async execute(args, exec) {
|
|
31
|
+
const ws = env.resolveWs(exec);
|
|
32
|
+
const file = path.join(ws, '_Control_Center', 'records', 'Global_Changelog.md');
|
|
33
|
+
// 台账不存在就从插件自带的骨架建一份(不依赖旧全局留下的文件)。
|
|
34
|
+
if (!isFile(file)) {
|
|
35
|
+
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
36
|
+
fs.writeFileSync(file, readUtf8(new URL('../templates/Changelog.skeleton.md', import.meta.url)), 'utf8');
|
|
37
|
+
}
|
|
38
|
+
let text = readUtf8(file);
|
|
39
|
+
let lines = text.split(NL);
|
|
40
|
+
let sepIdx = lines.findIndex((l) => l.trim() === '|---|---|---|');
|
|
41
|
+
if (sepIdx === -1) {
|
|
42
|
+
// 骨架被改坏(没有表):补一张新表再写,绝不因此拒绝记账。
|
|
43
|
+
const extra = ['', '| 日期 | 变更 | 执行者 |', '|---|---|---|'];
|
|
44
|
+
lines = lines.concat(extra);
|
|
45
|
+
sepIdx = lines.length - 1;
|
|
46
|
+
text = lines.join(NL);
|
|
47
|
+
}
|
|
48
|
+
const date = args.date || stamp();
|
|
49
|
+
const who = args.executor || env.selfId(exec) || '?';
|
|
50
|
+
const summary = typeof args.summary === 'string' ? args.summary.trim() : '';
|
|
51
|
+
if (!summary) throw new Error('summary 不能为空(写清「改了什么、为什么」)');
|
|
52
|
+
const safe = summary.split('|').join(String.fromCharCode(92) + '|');
|
|
53
|
+
const row = '| ' + date + ' | ' + safe + ' | ' + who + ' |';
|
|
54
|
+
lines.splice(sepIdx + 1, 0, row);
|
|
55
|
+
fs.writeFileSync(file, lines.join(NL), 'utf8');
|
|
56
|
+
return { text: '已追加到 ' + file + NL + row.slice(0, 240) };
|
|
57
|
+
},
|
|
58
|
+
};
|
|
59
|
+
}
|