dsh-plugin-t-expert 0.3.6 → 0.3.24
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 +99 -276
- package/lib/client.js +366 -85
- package/lib/index.js +41 -2
- package/lib/remote-schemas.js +7 -1
- package/lib/remote.js +22 -1
- package/lib/schedule.js +14 -1
- package/lib/skill.js +21 -5
- package/package.json +3 -2
- package/vendor/third-party-licenses/README.md +1 -1
- package/skills/t-expert-manager/SKILL.md +0 -125
- package/skills/t-expert-manager/references/ops-reference.md +0 -100
package/lib/index.js
CHANGED
|
@@ -12,8 +12,8 @@
|
|
|
12
12
|
*/
|
|
13
13
|
import { homedir } from "node:os";
|
|
14
14
|
import { existsSync, readFileSync, readdirSync } from "node:fs";
|
|
15
|
-
import { stat, rmdir } from "node:fs/promises";
|
|
16
|
-
import { dirname, join } from "node:path";
|
|
15
|
+
import { mkdir, stat, rmdir } from "node:fs/promises";
|
|
16
|
+
import { basename, dirname, join, resolve } from "node:path";
|
|
17
17
|
import schema from "@deepseek-ai/schemastery";
|
|
18
18
|
import { defineTool } from "@deepseek-ai/dsh-tools";
|
|
19
19
|
import {
|
|
@@ -111,6 +111,16 @@ export const Config = schema.object({
|
|
|
111
111
|
memberModel: schema.string(),
|
|
112
112
|
/** 一支团队最多几名成员。 */
|
|
113
113
|
maxMembers: schema.natural().min(1).default(8),
|
|
114
|
+
/**
|
|
115
|
+
* 定时任务默认绑定的工作区(首次使用时自动建/复用)。
|
|
116
|
+
*
|
|
117
|
+
* 为什么要有它:定时任务跑出来的会话如果"不绑定工作区",在侧栏里就没有归属,
|
|
118
|
+
* 一堆自动会话会散在工作区列表外面;给它们一个固定去处(默认「定时任务」工作区、
|
|
119
|
+
* 目录 <默认目录>/tesk)最好找。目录不存在会被建出来 —— 宿主的工作区 create
|
|
120
|
+
* 只接受已存在的目录。
|
|
121
|
+
*/
|
|
122
|
+
scheduleWorkspaceTitle: schema.string().default("定时任务"),
|
|
123
|
+
scheduleWorkspaceDir: schema.string().default(join(homedir(), "tesk")),
|
|
114
124
|
});
|
|
115
125
|
|
|
116
126
|
/** 供 remote 服务读取名册的服务名。 */
|
|
@@ -247,12 +257,41 @@ export function apply(ctx, config) {
|
|
|
247
257
|
defaultCwd: process.cwd(),
|
|
248
258
|
// 提交前的提示词改写:提示词里挑中的 `@专家名` 由它在执行时落成召唤指令。
|
|
249
259
|
expandPrompt: (text) => expandSchedulePrompt(text),
|
|
260
|
+
// 工作区需求:定时任务默认绑到「定时任务」工作区(目录 <默认目录>/tesk),也可自定义目录。
|
|
261
|
+
ensureWorkspace: (path, title) => ensureWorkspace(path, title),
|
|
250
262
|
// 先按「开」起来;settings 就绪后立刻按真实偏好对齐(见下面的 inject 回调)。
|
|
251
263
|
armed: true,
|
|
252
264
|
});
|
|
253
265
|
ctx.effect(() => () => scheduleEngine.dispose(), "t-team: schedule jobs");
|
|
254
266
|
ctx.reflect.provide(SCHEDULE_SERVICE, scheduleEngine);
|
|
255
267
|
|
|
268
|
+
/**
|
|
269
|
+
* 确保一个工作区存在,并返回 { id, title, path }。
|
|
270
|
+
*
|
|
271
|
+
* 顺序:目录不存在先 mkdir(宿主的工作区 create **只接受已存在的目录**)→ 交给
|
|
272
|
+
* workspaceRegistry.create:它内部对同路径工作区是**幂等复用**(createCanonical 第一件事
|
|
273
|
+
* 就是按 path 查找已有实体),所以重复调用不会造出第二个「定时任务」工作区。
|
|
274
|
+
*
|
|
275
|
+
* @param path - 目录绝对路径;空串表示用配置里的 scheduleWorkspaceDir。
|
|
276
|
+
* @param title - 工作区标题;缺省时:默认工作区用配置里的 scheduleWorkspaceTitle,自定义目录用目录名。
|
|
277
|
+
*/
|
|
278
|
+
async function ensureWorkspace(path, title) {
|
|
279
|
+
const registry = ctx.get("workspaceRegistry") ?? ctx.get("workspace");
|
|
280
|
+
if (registry === undefined || typeof registry.create !== "function") {
|
|
281
|
+
throw new Error("宿主没有可用的工作区服务(workspaceRegistry.create),无法准备定时任务工作区。");
|
|
282
|
+
}
|
|
283
|
+
const raw = typeof path === "string" && path.trim() !== "" ? path.trim() : config.scheduleWorkspaceDir;
|
|
284
|
+
const custom = typeof path === "string" && path.trim() !== "";
|
|
285
|
+
const target = resolve(raw);
|
|
286
|
+
await mkdir(target, { recursive: true });
|
|
287
|
+
// 自定义目录的工作区标题缺省取目录名:否则几个自定义目录会挤成同名「定时任务」,侧栏里分不出来。
|
|
288
|
+
const label = title !== undefined && title !== ""
|
|
289
|
+
? title
|
|
290
|
+
: (custom ? basename(target) : config.scheduleWorkspaceTitle);
|
|
291
|
+
const entity = await registry.create(target, label);
|
|
292
|
+
return { id: String(entity.id), title: String(entity.title ?? ""), path: String(entity.path ?? target) };
|
|
293
|
+
}
|
|
294
|
+
|
|
256
295
|
/**
|
|
257
296
|
* 把定时任务提示词里点名的专家落成**明确的召唤指令**(支持同时点名多位)。
|
|
258
297
|
*
|
package/lib/remote-schemas.js
CHANGED
|
@@ -209,7 +209,8 @@ export const scheduleSnapshotSchema = z.object({
|
|
|
209
209
|
/** 正在执行中的事项 id(面板据此显示「执行中…」)。 */
|
|
210
210
|
running: z.array(z.string()),
|
|
211
211
|
capabilities: z.object({ agents: z.boolean(), presets: z.boolean() }),
|
|
212
|
-
|
|
212
|
+
/** 可选工作区(下拉选项);path 是目录,面板显示给用户看落地位置。 */
|
|
213
|
+
workspaces: z.array(z.object({ id: z.string(), title: z.string(), path: z.string().optional() })),
|
|
213
214
|
});
|
|
214
215
|
|
|
215
216
|
/** 新建/编辑表单提交的字段。 */
|
|
@@ -230,3 +231,8 @@ export const schedulePreviewSchema = z.object({ fires: z.array(z.string()) });
|
|
|
230
231
|
|
|
231
232
|
/** `setArmed` 的结果。 */
|
|
232
233
|
export const scheduleArmedSchema = z.object({ armed: z.boolean() });
|
|
234
|
+
|
|
235
|
+
/** `ensureScheduleWorkspace` 的返回:定时任务要绑定的工作区(默认「定时任务」工作区或自定义目录)。 */
|
|
236
|
+
export const scheduleWorkspaceSchema = z.object({
|
|
237
|
+
workspace: z.object({ id: z.string(), title: z.string(), path: z.string() }),
|
|
238
|
+
});
|
package/lib/remote.js
CHANGED
|
@@ -32,6 +32,7 @@ import {
|
|
|
32
32
|
scheduleInputSchema,
|
|
33
33
|
schedulePatchSchema,
|
|
34
34
|
schedulePreviewSchema,
|
|
35
|
+
scheduleWorkspaceSchema,
|
|
35
36
|
scheduleSnapshotSchema,
|
|
36
37
|
squadInputSchema,
|
|
37
38
|
squadMemberSchema,
|
|
@@ -121,6 +122,14 @@ const DESCRIPTORS = [
|
|
|
121
122
|
codec: { mode: "strict", typeSymbol: "string", schema: z.string().min(1).max(120) },
|
|
122
123
|
},
|
|
123
124
|
], "TTeamSchedulePreview", schedulePreviewSchema),
|
|
125
|
+
descriptor("ensureScheduleWorkspace", [
|
|
126
|
+
{
|
|
127
|
+
name: "path",
|
|
128
|
+
wire: "path",
|
|
129
|
+
source: "json",
|
|
130
|
+
codec: { mode: "strict", typeSymbol: "string", schema: z.string().max(1024) },
|
|
131
|
+
},
|
|
132
|
+
], "TTeamScheduleWorkspace", scheduleWorkspaceSchema),
|
|
124
133
|
descriptor("setEnabled", [
|
|
125
134
|
{
|
|
126
135
|
name: "enabled",
|
|
@@ -436,6 +445,18 @@ class TTeamRemote extends TypertRemoteService {
|
|
|
436
445
|
}
|
|
437
446
|
}
|
|
438
447
|
|
|
448
|
+
/**
|
|
449
|
+
* 确保定时任务要绑定的工作区存在:空 path = 配置里的默认「定时任务」工作区(目录 <默认目录>/tesk);
|
|
450
|
+
* 给了 path = 面板里填的自定义目录(不存在会建出来)。宿主的工作区 create 对同路径是幂等复用。
|
|
451
|
+
*/
|
|
452
|
+
async ensureScheduleWorkspace(path) {
|
|
453
|
+
try {
|
|
454
|
+
return { workspace: await this.schedule().ensureWorkspace(String(path ?? ""), undefined) };
|
|
455
|
+
} catch (error) {
|
|
456
|
+
throw this.businessError(error, "tTeam/schedule-workspace-failed");
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
|
|
439
460
|
/** 预览某个 cron 表达式接下来的 5 次触发时间(宿主用 croner 算,客户端不打包 cron 解析器)。 */
|
|
440
461
|
async previewSchedule(cron) {
|
|
441
462
|
try {
|
|
@@ -639,7 +660,7 @@ class TTeamRemote extends TypertRemoteService {
|
|
|
639
660
|
exposeRemoteMethods(TTeamRemote, [
|
|
640
661
|
"getCatalog", "setEnabled", "getPrompt", "getSquads", "saveSquads", "getTeams", "stopTeam", "startSquad",
|
|
641
662
|
"getUiPrefs", "setUiPrefs",
|
|
642
|
-
"getSchedule", "createScheduleItem", "updateScheduleItem", "deleteScheduleItem", "runScheduleItem", "previewSchedule",
|
|
663
|
+
"getSchedule", "createScheduleItem", "updateScheduleItem", "deleteScheduleItem", "runScheduleItem", "previewSchedule", "ensureScheduleWorkspace",
|
|
643
664
|
"createExpert", "updateExpert", "deleteExpert",
|
|
644
665
|
"createCategory", "updateCategory", "deleteCategory",
|
|
645
666
|
]);
|
package/lib/schedule.js
CHANGED
|
@@ -110,10 +110,14 @@ export function normalizeInput(input, t) {
|
|
|
110
110
|
* @param {string} options.defaultCwd - 不绑定工作区时的会话工作目录。
|
|
111
111
|
* @param {boolean} [options.armed] - 初始是否启用调度(= 界面开关的当前值)。
|
|
112
112
|
* @param {(text: string) => Promise<string>} [options.expandPrompt] - 提交前的提示词改写
|
|
113
|
-
* (提示词里挑中的
|
|
113
|
+
* (提示词里挑中的 `@专家名` 由它落成召唤指令)。缺省就是原文。
|
|
114
|
+
* @param {(path: string, title?: string) => Promise<{ id: string, title: string, path: string }>} [options.ensureWorkspace]
|
|
115
|
+
* - 准备定时任务的工作区(默认「定时任务」工作区,或面板里填的自定义目录)。缺省则面板该项不可用。
|
|
114
116
|
*/
|
|
115
117
|
export function createScheduleEngine(options) {
|
|
116
118
|
const { ctx, file, logger, t, defaultCwd } = options;
|
|
119
|
+
/** 工作区准备(默认「定时任务」工作区 / 自定义目录),由 host 注入。 */
|
|
120
|
+
const ensureWorkspace = typeof options.ensureWorkspace === "function" ? options.ensureWorkspace : undefined;
|
|
117
121
|
/** 提交前的提示词改写:没有注入就原样提交(定时任务本身不依赖它)。 */
|
|
118
122
|
const expandPrompt = typeof options.expandPrompt === "function"
|
|
119
123
|
? options.expandPrompt
|
|
@@ -320,6 +324,8 @@ export function createScheduleEngine(options) {
|
|
|
320
324
|
workspaces: list.map((workspace) => ({
|
|
321
325
|
id: String(workspace.id ?? ""),
|
|
322
326
|
title: String(workspace.title ?? workspace.path ?? ""),
|
|
327
|
+
// 目录:面板把它显示在表单里,用户能看清这条任务落在哪个文件夹。
|
|
328
|
+
path: typeof workspace.path === "string" ? workspace.path : "",
|
|
323
329
|
})).filter((workspace) => workspace.id !== ""),
|
|
324
330
|
};
|
|
325
331
|
}
|
|
@@ -412,6 +418,13 @@ export function createScheduleEngine(options) {
|
|
|
412
418
|
remove,
|
|
413
419
|
runNow,
|
|
414
420
|
preview: (cron) => ({ fires: previewRuns(cron, t) }),
|
|
421
|
+
/** 给面板用:确保定时任务的默认工作区(或自定义目录)存在,返回 { id, title, path }。 */
|
|
422
|
+
ensureWorkspace: (path, title) => {
|
|
423
|
+
if (ensureWorkspace === undefined) {
|
|
424
|
+
return Promise.reject(new Error("宿主没有提供工作区准备能力(ensureWorkspace)。"));
|
|
425
|
+
}
|
|
426
|
+
return ensureWorkspace(path, title);
|
|
427
|
+
},
|
|
415
428
|
setArmed,
|
|
416
429
|
isArmed: () => armed,
|
|
417
430
|
/** 当前真正挂着定时器的事项 id(自检与诊断用:能看出"任务在、但没排定"这种失效)。 */
|
package/lib/skill.js
CHANGED
|
@@ -33,10 +33,20 @@ export const SKILLS_ROOT = resolve(dirname(fileURLToPath(import.meta.url)), ".."
|
|
|
33
33
|
* 随包发布的 skill 清单(目录名 = 宿主路由用的 skill 名)。
|
|
34
34
|
*
|
|
35
35
|
* 顺序即注册顺序。缺文件的条目会被跳过并逐条告警,而不是让整段注册失败 ——
|
|
36
|
-
* 一个 skill
|
|
36
|
+
* 一个 skill 文件缺失不该带走另外两个(见 {@link LOCAL_ONLY_SKILL_NAMES} 的例外)。
|
|
37
37
|
*/
|
|
38
38
|
export const BUNDLED_SKILL_NAMES = ["t-expert-manager", "dsh-harness-project", "dsh-harness-languages"];
|
|
39
39
|
|
|
40
|
+
/**
|
|
41
|
+
* **只在本机布局里存在**的 skill:不进 npm 包(见 `package.json` 的 `files`)。
|
|
42
|
+
*
|
|
43
|
+
* 为什么:`t-expert-manager` 的正文整篇都是 `tz.sh` 运维流程,而 `ops/`、`tools/`、`tz.sh`
|
|
44
|
+
* 都不随包发布 —— 从 npm 装的人本来就不该有它,拿了也是一份跑不动的手册。所以它缺席是**预期**,
|
|
45
|
+
* 不能按"缺文件"告警(否则每次启动一条"未注册",看着像装坏了)。本机从源码装(`tz.sh install`
|
|
46
|
+
* 会把整个 `skills/` 复制进运行副本)时它照常在,运维流程一字不少。
|
|
47
|
+
*/
|
|
48
|
+
export const LOCAL_ONLY_SKILL_NAMES = ["t-expert-manager"];
|
|
49
|
+
|
|
40
50
|
/** 只有运维 skill 的正文带 `{{TZ}}` / `{{OPS_ROOT}}` 占位符,其余按原样注册。 */
|
|
41
51
|
const PLACEHOLDER_SKILL = "t-expert-manager";
|
|
42
52
|
|
|
@@ -161,17 +171,23 @@ function loadBundledSkill(name, config = {}) {
|
|
|
161
171
|
/**
|
|
162
172
|
* 逐个构建随包 skill,并**记下**哪些条目没建成以及为什么。
|
|
163
173
|
*
|
|
164
|
-
* 注册方靠 `missing` 逐条告警 ——
|
|
174
|
+
* 注册方靠 `missing` 逐条告警 —— 缺一个文件不该静默、也不该带走另外两个;
|
|
175
|
+
* 唯一的例外是 {@link LOCAL_ONLY_SKILL_NAMES}:它们不随 npm 包发布,缺席是预期。
|
|
165
176
|
* @param config - 插件配置。
|
|
166
|
-
* @returns `{ skills, missing }
|
|
177
|
+
* @returns `{ skills, missing }`;`skills` 顺序与 {@link BUNDLED_SKILL_NAMES} 一致。
|
|
167
178
|
*/
|
|
168
179
|
function collectBundledSkills(config = {}) {
|
|
169
180
|
const skills = [];
|
|
170
181
|
const missing = [];
|
|
171
182
|
for (const name of BUNDLED_SKILL_NAMES) {
|
|
172
183
|
const loaded = loadBundledSkill(name, config);
|
|
173
|
-
if (loaded.skill
|
|
174
|
-
|
|
184
|
+
if (loaded.skill !== undefined) {
|
|
185
|
+
skills.push(loaded.skill);
|
|
186
|
+
continue;
|
|
187
|
+
}
|
|
188
|
+
// 本机专有的 skill(npm 包里本来就没有):缺席是预期,既不告警也不进 missing。
|
|
189
|
+
if (LOCAL_ONLY_SKILL_NAMES.includes(name)) continue;
|
|
190
|
+
missing.push({ name, reason: loaded.reason });
|
|
175
191
|
}
|
|
176
192
|
return { skills, missing };
|
|
177
193
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-plugin-t-expert",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.24",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "T Expert — a 316-expert, 22-division roster with full Chinese translations, as a standalone DeepSeek Harness plugin, with a built-in multi-agent team engine (T Team).",
|
|
6
6
|
"license": "MIT",
|
|
@@ -42,7 +42,8 @@
|
|
|
42
42
|
"files": [
|
|
43
43
|
"lib",
|
|
44
44
|
"data",
|
|
45
|
-
"skills",
|
|
45
|
+
"skills/dsh-harness-project",
|
|
46
|
+
"skills/dsh-harness-languages",
|
|
46
47
|
"cordis.patch.yml",
|
|
47
48
|
"README.md",
|
|
48
49
|
"LICENSE",
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
|
|
6
6
|
| 文件 | 覆盖的随包内容 | 来源 |
|
|
7
7
|
| --- | --- | --- |
|
|
8
|
-
| `agency-agents.LICENSE` | `data/experts/`(22 分区 / 316 位;其中
|
|
8
|
+
| `agency-agents.LICENSE` | `data/experts/`(22 分区 / 316 位;其中 314 位来自第三方 —— 279 位为上游仓库逐字节镜像、35 位来自名册来源包快照;余 2 位为本仓自建,不由本许可覆盖) | The Agency / AgentLand 名册快照,MIT |
|
|
9
9
|
| `agency-agents-zh.LICENSE` | `data/zh/` 中的中文名字、简介与人格正文 | `agency-agents-zh` 中文翻译与本地化资产,MIT |
|
|
10
10
|
|
|
11
11
|
两份文本都是从实际用于生成快照的来源包内**逐字节复制**的,未经改写:
|
|
@@ -1,125 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: t-expert-manager
|
|
3
|
-
description: T专家 运维台 —— 316 位专家 / 22 分区的名册增删、名册一致性校验、统计与中文覆盖、小队(/t)成员编辑、装机到 DSH Desktop、发布到 npm。Use when the user asks to add/remove/validate/count T专家 experts, 新增专家 / 删除专家 / 校验名册 / 名册统计 / 改小队 / 重装插件 / 发布插件.
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# T专家 运维(expert-manager)
|
|
7
|
-
|
|
8
|
-
运维入口只有一个:`bash "{{TZ}}"`。本机运维台根目录:`{{OPS_ROOT}}`。
|
|
9
|
-
|
|
10
|
-
这套流程解决的是「你是原作者,要维护自己的名册和插件」的场景;只想**用**专家或拉小队,不要走这里
|
|
11
|
-
(用 `list_t_experts` / `summon_t_expert`,小队用 `/t`)。
|
|
12
|
-
|
|
13
|
-
## 铁律
|
|
14
|
-
|
|
15
|
-
1. **只用 tz.sh**。不要手改 `data/experts/`、`plugin/`、`~/.t-team/`,也不要手改 `package.json` 的版本号 ——
|
|
16
|
-
这些动作脚本都代劳了,绕开脚本换来的就是"源码加了、运行时没加"这类半成品状态。
|
|
17
|
-
2. **`data/experts/` 是真源**。增删由 `add-expert.py`(tz.sh 代跑)执行,它**同时写**源码与运行时
|
|
18
|
-
`~/.t-team/experts/`,两边永远一致。
|
|
19
|
-
3. **统计必须递归**。有专家落在嵌套子目录(如 `game-development/unreal-engine/…`),
|
|
20
|
-
只看分类目录的第一层会漏掉它们。位数一律以 `tz.sh status` 的递归结果为准 ——
|
|
21
|
-
正文里不写死位数,因为每加一位专家它就会过期。
|
|
22
|
-
4. **数据更新不用重装、不用重启**(宿主按 mtime 指纹自动重载名册);**改插件代码**才需要
|
|
23
|
-
`tz.sh build` → `tz.sh install` → 重启 DSH Desktop。
|
|
24
|
-
5. **小队改完分两条路,只有设置页那条会热重载**:
|
|
25
|
-
- **设置页「队伍」保存**(推荐):`squads.save()` 编译成功后**就地热重载引擎配置 → 立即生效、不用重启**。
|
|
26
|
-
- **`tz.sh squads`(= 菜单 11)**:只是 CLI 直跑编译器(`python3 data/team-profiles.py`)把 `teams.json`
|
|
27
|
-
编译成引擎配置,**不经过插件、不会热重载**——运行中的引擎仍用旧 profiles,**必须重启 DSH**
|
|
28
|
-
(或再到设置页保存一次)才生效。所以别对用户说「跑完 tz.sh squads 就已经生效」。
|
|
29
|
-
两条路的失败语义一致:编译失败会**回滚**,盘上仍是旧配置(按旧小队建队)。
|
|
30
|
-
只有**改插件代码**才需要重启(见第 4 条)。
|
|
31
|
-
6. **`data/zh/` 已冻结**:不要新增或"补译"中文。新专家没有中文名/简介会回退英文,这是预期行为。
|
|
32
|
-
7. **破坏性动作先确认**:删除专家、卸载插件、发布 npm 之前,把影响讲给用户听,得到明确同意再执行。
|
|
33
|
-
|
|
34
|
-
## 先看状态
|
|
35
|
-
|
|
36
|
-
```bash
|
|
37
|
-
bash "{{TZ}}" status
|
|
38
|
-
```
|
|
39
|
-
|
|
40
|
-
一眼给出:名册位数 / 分类数 / 中文覆盖 / 插件版本 vs DSH 里装的版本 / 仓库是否干净。
|
|
41
|
-
|
|
42
|
-
## 工作流
|
|
43
|
-
|
|
44
|
-
### A. 新增专家
|
|
45
|
-
|
|
46
|
-
1. 先确认 slug 不冲突(`bash "{{TZ}}" roster` 看现有分类与位数)。
|
|
47
|
-
2. **先 dry-run**:
|
|
48
|
-
```bash
|
|
49
|
-
bash "{{TZ}}" experts add --category <分区> --slug <slug> --file </绝对路径/xxx.md> --dry-run
|
|
50
|
-
```
|
|
51
|
-
3. 确认路径无误后去掉 `--dry-run` 正式写入。源文件缺 frontmatter 时补 `--name` / `--description`;
|
|
52
|
-
目标已存在要覆盖时加 `--force`。
|
|
53
|
-
4. 收尾校验:`bash "{{TZ}}" experts check`,必须报"三处一致"。
|
|
54
|
-
|
|
55
|
-
### B. 删除专家
|
|
56
|
-
|
|
57
|
-
先 `--dry-run` 看清要删的两条路径,再执行:
|
|
58
|
-
|
|
59
|
-
```bash
|
|
60
|
-
bash "{{TZ}}" experts remove --category <分区> --slug <slug> --yes
|
|
61
|
-
```
|
|
62
|
-
|
|
63
|
-
源码与运行时会被一并删除(`--yes` 跳过交互确认,所以**先跟用户确认过**再用)。
|
|
64
|
-
|
|
65
|
-
### C. 校验名册
|
|
66
|
-
|
|
67
|
-
```bash
|
|
68
|
-
bash "{{TZ}}" experts check # 源码 ↔ 运行时 ↔ 清单,逐文件 sha256;退出码 1 = 有漂移
|
|
69
|
-
bash "{{TZ}}" verify # check + 插件自检(verify.mjs),改过代码后必跑
|
|
70
|
-
```
|
|
71
|
-
|
|
72
|
-
### D. 统计与中文覆盖
|
|
73
|
-
|
|
74
|
-
```bash
|
|
75
|
-
bash "{{TZ}}" experts stats
|
|
76
|
-
```
|
|
77
|
-
|
|
78
|
-
汇报时给"分类数 / 专家数 / 中文覆盖",不要把整张分类表贴给用户。
|
|
79
|
-
|
|
80
|
-
### E. 修改小队(`/t`)
|
|
81
|
-
|
|
82
|
-
1. 小队定义在 `data/teams.json`(人工维护):成员写专家 slug 字符串,或 `{slug, role}` 对象。
|
|
83
|
-
2. 用编辑器改完后编译成引擎配置:
|
|
84
|
-
```bash
|
|
85
|
-
bash "{{TZ}}" squads
|
|
86
|
-
```
|
|
87
|
-
3. 明确告诉用户:**`tz.sh squads` 只是 CLI 直跑编译器**——它把 `teams.json` 编译成引擎配置,
|
|
88
|
-
**不会让运行中的插件重载**。所以:**要让新小队真正生效,需重启 DSH Desktop**(或再到设置页「队伍」保存一次,
|
|
89
|
-
那条路才会就地热重载、立即生效)。若 `/t` 里仍看不到新小队,先确认编译没报错(编译失败会回滚,盘上仍是旧配置)。
|
|
90
|
-
4. 想免重启就直接走设置页:设置 → T专家 → 队伍 → 改完保存(`squads.save()` 编译成功后触发引擎热重载,立即生效)。
|
|
91
|
-
|
|
92
|
-
### F. 装机(只在改过插件代码时需要)
|
|
93
|
-
|
|
94
|
-
```bash
|
|
95
|
-
bash "{{TZ}}" verify # 先自检
|
|
96
|
-
bash "{{TZ}}" build # 构建 client bundle
|
|
97
|
-
bash "{{TZ}}" install # 装到 profile(默认 desktop);可先 --dry-run
|
|
98
|
-
```
|
|
99
|
-
|
|
100
|
-
装机后仍需**重启 DSH Desktop**。装错/装坏时 `bash "{{TZ}}" install-restore` 可还原被接管的依赖。
|
|
101
|
-
|
|
102
|
-
### G. 发布到 npm
|
|
103
|
-
|
|
104
|
-
```bash
|
|
105
|
-
bash "{{TZ}}" publish-dry # 预演:只跑预检与打包预览
|
|
106
|
-
bash "{{TZ}}" publish --current # 直接发布 package.json 里的当前版本(不升版)—— 版本号在开发期就写好了
|
|
107
|
-
bash "{{TZ}}" publish # 交互选版本(菜单里选项 1 就是"发布当前版本");也可 --patch / --minor / --major / --version X.Y.Z
|
|
108
|
-
```
|
|
109
|
-
|
|
110
|
-
> 版本号的单一来源是 `package.json`:菜单 15 会把**当前版本号自动读出来**并作为首选,
|
|
111
|
-
> 客户端构建版本(`CLIENT_BUILD_VERSION`)与它必须一致(verify 有断言盯着),所以通常不需要在发布时再 bump。
|
|
112
|
-
> 回车=取消(发布不可逆,没有"回车即发"的默认)。
|
|
113
|
-
> 选「发布当前版本」不跑 `npm version`,但会**给该版本补一个 tag** —— 发出去的版本要能对应到具体提交,失败后也才能用 `--retry` 重试。
|
|
114
|
-
|
|
115
|
-
**发布前必须**:(a) 得到用户明确同意;(b) `experts check` 与 `verify` 全绿;(c) 工作区没有未提交的意外改动。
|
|
116
|
-
|
|
117
|
-
## 汇报约定
|
|
118
|
-
|
|
119
|
-
- 给"执行的命令 + 结尾结论",不要贴整篇日志。
|
|
120
|
-
- 失败就贴出错那一段(含退出码)并说明下一步;不要静默重试或换个命令重来。
|
|
121
|
-
- 名册位数永远按递归结果报(见铁律 3)。
|
|
122
|
-
|
|
123
|
-
## 参考
|
|
124
|
-
|
|
125
|
-
- `references/ops-reference.md` —— 完整子命令 / 菜单号 / 参数表,以及"哪份文件是真源"的地图。
|
|
@@ -1,100 +0,0 @@
|
|
|
1
|
-
# T专家 运维参考
|
|
2
|
-
|
|
3
|
-
本文件是 `SKILL.md` 的展开版:完整命令、菜单号、参数与"哪份文件是真源"的地图。
|
|
4
|
-
命令里的 `<ops>` = 运维台根目录(`tz.sh` 所在目录)。
|
|
5
|
-
|
|
6
|
-
## 一、子命令(脚本化用,agent 优先用这一组)
|
|
7
|
-
|
|
8
|
-
| 子命令 | 作用 | 备注 |
|
|
9
|
-
| --- | --- | --- |
|
|
10
|
-
| `tz.sh status` | 状态头:名册/中文/版本/仓库是否干净 | 只读 |
|
|
11
|
-
| `tz.sh experts add` | 新增专家 | 参数见下;无参数 = 交互 |
|
|
12
|
-
| `tz.sh experts remove` | 删除专家(源码 + 运行时) | 先 `--dry-run` |
|
|
13
|
-
| `tz.sh experts stats` | 名册统计(分类 / 专家数 / 中文覆盖) | 递归统计 |
|
|
14
|
-
| `tz.sh experts check` | 名册一致性(源码 ↔ 运行时 ↔ 清单) | 退出码 1 = 漂移 |
|
|
15
|
-
| `tz.sh roster` | 同 `experts stats` | |
|
|
16
|
-
| `tz.sh verify` | `experts check` + `verify.mjs` 插件自检 | 改代码后必跑 |
|
|
17
|
-
| `tz.sh squads` | 刷新小队配置(`teams.json` → 引擎 profiles,= 菜单 11) | **CLI 直跑编译器,不触发热重载**:运行中的引擎仍用旧 profiles → 需重启 DSH Desktop(或改走设置页保存) |
|
|
18
|
-
| `tz.sh build` | 构建 client bundle | `npm run build` |
|
|
19
|
-
| `tz.sh install` | 安装/重装到 profile | 见参数;`--dry-run` 预演 |
|
|
20
|
-
| `tz.sh uninstall` | 卸载(保留 `experts/` 与 `zh/`) | |
|
|
21
|
-
| `tz.sh install-restore` | 还原被接管的 `@nanmicoder/dsh-agent-teams` | 有接管记录时可用 |
|
|
22
|
-
| `tz.sh sync-data` | 把运行时数据快照同步回包内 `data/` | 发布前用 |
|
|
23
|
-
| `tz.sh publish-dry` / `publish` | 发布预演 / 发布到 npm | 见参数 |
|
|
24
|
-
| `tz.sh open` | 打开运行时数据目录 | |
|
|
25
|
-
|
|
26
|
-
## 二、交互菜单号(`tz.sh <编号>` 等价)
|
|
27
|
-
|
|
28
|
-
| # | 动作 | # | 动作 |
|
|
29
|
-
| --- | --- | --- | --- |
|
|
30
|
-
| 1 | 新增专家 | 9 | 卸载插件(保留数据) |
|
|
31
|
-
| 2 | 删除专家 | 10 | 校验:名册一致性 + 插件自检 |
|
|
32
|
-
| 3 | 名册统计 | 11 | 刷新小队配置 |
|
|
33
|
-
| 4 | 打开源码专家目录 `data/experts` | 12 | 构建插件产物 |
|
|
34
|
-
| 5 | 名册一致性校验 | 13 | (已移除)内置引擎同步 |
|
|
35
|
-
| 6 | 中文覆盖报告(已冻结) | 14 | 发布预演(dry-run) |
|
|
36
|
-
| 7 | 打开运行时数据目录 | 15 | 一键发布到 npm |
|
|
37
|
-
| 8 | 安装/重装到 profile | 16 | 同步包内数据快照 |
|
|
38
|
-
|
|
39
|
-
## 三、`experts add` / `experts remove` 参数
|
|
40
|
-
|
|
41
|
-
`add`(`--repo` / `--runtime` 由 tz.sh 自动注入,不要手写):
|
|
42
|
-
|
|
43
|
-
| 参数 | 说明 |
|
|
44
|
-
| --- | --- |
|
|
45
|
-
| `--category <分区>` | 目标分类(已存在或新建) |
|
|
46
|
-
| `--slug <slug>` | 文件名即 slug:`a-z 0-9 -`;中文名必须显式给 `--slug` |
|
|
47
|
-
| `--file <路径>` | 源 Markdown(绝对路径最稳) |
|
|
48
|
-
| `--label <显示名>` | 新建分类时的中文显示名 |
|
|
49
|
-
| `--name` / `--description` / `--emoji` | 源文件缺 frontmatter 时补齐 |
|
|
50
|
-
| `--force` | 目标已存在时覆盖 |
|
|
51
|
-
| `--yes` | 跳过交互确认 |
|
|
52
|
-
| `--dry-run` | 只打印将写入的路径,不落盘 |
|
|
53
|
-
|
|
54
|
-
`remove`:`--category` + `--slug`(+ `--yes` / `--dry-run`)。
|
|
55
|
-
`slug` 就是文件名;工具会在分类树下**递归**定位它,嵌套子目录也能删对。
|
|
56
|
-
|
|
57
|
-
## 四、`install` / `publish` 参数
|
|
58
|
-
|
|
59
|
-
```
|
|
60
|
-
tz.sh install [--profile <名或路径>] [--dry-run] [--keep-agent-teams]
|
|
61
|
-
tz.sh install-restore
|
|
62
|
-
tz.sh uninstall
|
|
63
|
-
tz.sh publish-dry | publish [--patch|--minor|--major|--version X.Y.Z] [--retry] [--push|--no-push]
|
|
64
|
-
```
|
|
65
|
-
|
|
66
|
-
- 默认 profile:`~/.dsh/profiles/desktop`。
|
|
67
|
-
- `install` 会:把仓库复制成运行副本 `plugin/`(STAGE)→ 复制到 profile 的 `node_modules/` →
|
|
68
|
-
改 profile 的 `package.json` 依赖/bundles。**必须重启 DSH Desktop 才生效**。
|
|
69
|
-
- `publish` 默认 patch 升版;`--retry` 用于发布中断后重试;`--no-push` 不推 git tag。
|
|
70
|
-
|
|
71
|
-
## 五、真源地图(改哪份才是改对了)
|
|
72
|
-
|
|
73
|
-
| 内容 | 真源 | 说明 |
|
|
74
|
-
| --- | --- | --- |
|
|
75
|
-
| 专家名册 | `<ops>/dsh-plugin-t-expert/data/experts/` | 22 分类 / 316 位;运行时是它的同步副本 |
|
|
76
|
-
| 中文侧车 | `<ops>/dsh-plugin-t-expert/data/zh/` | **已冻结**,只读不写 |
|
|
77
|
-
| 小队定义 | 数据目录 `teams.json` | 人工维护;`data/` 与 `~/.t-team/` 同一份(软链) |
|
|
78
|
-
| 小队编译产物 | 数据目录 `t-team.config.json` | 由 `team-profiles.py` 生成,**不要手改** |
|
|
79
|
-
| 面板自建专家 | `<数据目录>/custom/custom/<slug>.md` | 不属于快照,同步脚本永不碰它 |
|
|
80
|
-
| 团队引擎源码 | `<ops>/dsh-plugin-t-expert/lib/teams/` | 手工维护的源码 |
|
|
81
|
-
| 运行副本 | `<ops>/plugin/` | 安装产物,删了重跑 `tz.sh install` 即可重建 |
|
|
82
|
-
|
|
83
|
-
## 六、环境变量(覆盖路径)
|
|
84
|
-
|
|
85
|
-
| 变量 | 默认 | 作用 |
|
|
86
|
-
| --- | --- | --- |
|
|
87
|
-
| `T_TEAM_REPO` | `<ops>/dsh-plugin-t-expert` | 插件仓库位置 |
|
|
88
|
-
| `T_TEAM_DATA` | `<ops>/data` | 数据目录 |
|
|
89
|
-
| `T_TEAM_STAGE` | `<ops>/plugin` | 运行副本目录 |
|
|
90
|
-
|
|
91
|
-
## 七、常见故障
|
|
92
|
-
|
|
93
|
-
| 现象 | 原因 / 处理 |
|
|
94
|
-
| --- | --- |
|
|
95
|
-
| 名册少了几位 | 只数了分类目录的第一层(嵌套子目录里的专家被漏掉)—— 必须递归,以 `tz.sh status` 为准 |
|
|
96
|
-
| `experts check` 报"运行时缺/多出" | 绕开脚本手工拷贝过文件;用 `experts add` 重做那一条 |
|
|
97
|
-
| 新增专家后面板看不到 | 面板按 mtime 指纹自动重载;仍看不到就查该专家是否被**启用**(设置页 T专家 标签,默认全禁用) |
|
|
98
|
-
| 改了小队但 `/t` 里没生效 | 先看编译报错(失败会回滚,盘上仍是旧配置);若编译成功却是走 `tz.sh squads` 的,**运行中的引擎不会热重载 → 需重启 DSH Desktop**,或改走设置页「队伍」保存(那条立即生效) |
|
|
99
|
-
| 装了新版本但没变化 | 只跑了 `build` 没跑 `install`,或没重启 |
|
|
100
|
-
| `找不到插件仓库` | 设 `T_TEAM_REPO`,或用 `--repo` 指定 |
|