@rezti/dsh-rez-suite 0.1.18 → 0.1.19

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 CHANGED
@@ -12,4 +12,4 @@ dsh plugin --profile web update @rezti/dsh-rez-suite
12
12
 
13
13
  包名是 `@rezti/dsh-rez-suite`,不是 `dsh-rezti-suite`。不要对同一 profile 再单独 add 子包,会重复注册。
14
14
 
15
- 拆分包(`dsh-rez-odoo` 等)给开发改代码用。Web 面板(含微信扫码)暂时仍在本包。老板三室(搞钱 / 产品 / 运营)也在本包内播种并登记侧栏,**不要**再装 `@rezti/dsh-rez-boss-desk`。
15
+ 拆分包(`dsh-rez-odoo` 等)给开发改代码用。Web 面板(含微信扫码)暂时仍在本包。设置里角色选「老板」后,才会在侧栏登记搞钱 / 产品 / 运营;切走老板会拿掉这三间(文件夹保留)。**不要**再装 `@rezti/dsh-rez-boss-desk`。
package/lib/index.d.ts CHANGED
@@ -1741,7 +1741,8 @@ declare const name = "rez-suite";
1741
1741
  declare const inject: string[];
1742
1742
  declare const REZ_SETTINGS_NAMESPACE: SettingsNamespace;
1743
1743
  declare const Config: Schema<RezConfig>;
1744
- declare const REZ_GUIDANCE: string;
1744
+ declare const REZ_GUIDANCE_CORE: string;
1745
+ declare function rezGuidanceFor(role: RezConfig['role']): string;
1745
1746
  declare function apply(ctx: Context, config?: RezConfig): void;
1746
1747
  //#endregion
1747
- export { Config, REZ_GUIDANCE, REZ_SETTINGS_NAMESPACE, apply, inject, name };
1748
+ export { Config, REZ_GUIDANCE_CORE, REZ_SETTINGS_NAMESPACE, apply, inject, name, rezGuidanceFor };
package/lib/index.js CHANGED
@@ -2,7 +2,7 @@ import { createRequire } from "node:module";
2
2
  import { homedir } from "node:os";
3
3
  import { dirname, join, relative } from "node:path";
4
4
  import { REZ_CREDENTIAL_REFS, REZ_DEFAULT_CONNECTIONS, REZ_SYSTEMS, lastMcpStartError, mergeConnections, secretConfigured, writeManagedCredential } from "@rezti/dsh-rez-sso";
5
- import { existsSync, mkdirSync, readFileSync, readdirSync, renameSync, statSync, writeFileSync } from "node:fs";
5
+ import { existsSync, mkdirSync, readFileSync, readdirSync, realpathSync, renameSync, statSync, writeFileSync } from "node:fs";
6
6
  import { DatabaseSync } from "node:sqlite";
7
7
  import { fileURLToPath } from "node:url";
8
8
  //#region ../../node_modules/.pnpm/@deepseek-ai+cosmokit@1.8.2/node_modules/@deepseek-ai/cosmokit/lib/index.js
@@ -7667,9 +7667,32 @@ function rezConfigTool(config) {
7667
7667
  }
7668
7668
  //#endregion
7669
7669
  //#region src/boss/register.ts
7670
+ /**
7671
+ * Register or drop the three boss rooms in the official workspace sidebar.
7672
+ * `workspaceRegistry.create` is idempotent: same canonical path returns the
7673
+ * existing record. New records are prepended, so we register in reverse to
7674
+ * keep 搞钱 → 产品 → 运营 at the top of the sidebar.
7675
+ * `delete` only drops the registry row — directories and session logs stay.
7676
+ */
7677
+ function canon(path) {
7678
+ try {
7679
+ return realpathSync(path);
7680
+ } catch {
7681
+ return path;
7682
+ }
7683
+ }
7684
+ function isRegisteredBossRoom(workspace, rooms) {
7685
+ return new Set(rooms.map((room) => canon(room.path))).has(canon(workspace.path));
7686
+ }
7670
7687
  async function registerBossWorkspaces(registry, rooms) {
7671
7688
  for (const room of [...rooms].reverse()) await registry.create(room.path, room.folder);
7672
7689
  }
7690
+ async function unregisterBossWorkspaces(registry, rooms) {
7691
+ for (const workspace of registry.list()) {
7692
+ if (!isRegisteredBossRoom(workspace, rooms)) continue;
7693
+ await registry.delete(workspace.id);
7694
+ }
7695
+ }
7673
7696
  //#endregion
7674
7697
  //#region src/boss/seed.ts
7675
7698
  const BOSS_ROOMS = [
@@ -7766,26 +7789,47 @@ function listFiles(root) {
7766
7789
  }
7767
7790
  //#endregion
7768
7791
  //#region src/boss/mount.ts
7792
+ const BOSS_ROLE_HINT = "设置 → 插件 → ReZ-TI → 角色选「老板」后,左侧会出现搞钱 / 产品 / 运营三室。不必手动「添加工作区」。";
7769
7793
  const BOSS_GUIDANCE = [
7770
- "老板三室已播种并登记为侧栏工作区:~/.dsh/workspaces/搞钱、产品、运营。不必手动「添加工作区」。",
7771
- "设置 → 插件 → ReZ-TI → 角色选「老板」。每个文件夹是独立作战室,官方 dsh 会注入该目录 AGENTS.md。",
7794
+ "当前角色是老板。侧栏已登记三室:~/.dsh/workspaces/搞钱、产品、运营。不必手动「添加工作区」。",
7795
+ "每个文件夹是独立作战室,官方 dsh 会注入该目录 AGENTS.md。",
7772
7796
  "若该室仍有 BOOTSTRAP.md:本会话先做短问三拍,沉淀到 PRIORITIES.md / MEMORY.md,然后删除 BOOTSTRAP.md。",
7773
7797
  "已初始化后:读 PRIORITIES.md + MEMORY.md + 今日/昨日 memory/;回答固定四行:结论 → 数字(哪家公司、哪段时间)→ 拍板 1~3 件事 → 未执行任何过账/付款/对外发送。",
7774
7798
  "微信文件夹仍是收件箱,不要在那边查账或砍 SKU。"
7775
7799
  ].join("");
7800
+ function bossGuidanceFor(role) {
7801
+ return role === "boss" ? BOSS_GUIDANCE : BOSS_ROLE_HINT;
7802
+ }
7776
7803
  function destRoot(env = process.env) {
7777
7804
  if (env.REZ_BOSS_WORKSPACES !== void 0 && env.REZ_BOSS_WORKSPACES !== "") return env.REZ_BOSS_WORKSPACES;
7778
7805
  return join(env.DSH_HOME !== void 0 && env.DSH_HOME !== "" ? env.DSH_HOME : join(homedir(), ".dsh"), "workspaces");
7779
7806
  }
7780
- /** Seed files, then register official workspaces once workspaceRegistry is ready. */
7781
- function mountBossDesk(ctx) {
7782
- const seeded = seedBossWorkspaces({ destRoot: destRoot() });
7783
- ctx.inject(["workspaceRegistry"], (inner) => {
7784
- registerBossWorkspaces(inner.get("workspaceRegistry"), seeded.rooms).catch((error) => {
7785
- const why = error instanceof Error ? error.message : String(error);
7786
- console.error("[dsh-rez-suite] workspaceRegistry.create failed:", why);
7807
+ function expectedBossRooms(env = process.env) {
7808
+ const root = destRoot(env);
7809
+ return BOSS_ROOMS.map((room) => ({
7810
+ path: join(root, room.folder),
7811
+ folder: room.folder
7812
+ }));
7813
+ }
7814
+ async function syncBossWorkspaces(registry, role, env = process.env) {
7815
+ if (role === "boss") {
7816
+ await registerBossWorkspaces(registry, seedBossWorkspaces({ destRoot: destRoot(env) }).rooms);
7817
+ return;
7818
+ }
7819
+ await unregisterBossWorkspaces(registry, expectedBossRooms(env));
7820
+ }
7821
+ /** Seed/register 三室 only for role=boss; drop sidebar rows otherwise. Returns a re-sync function. */
7822
+ function mountBossDesk(ctx, getRole) {
7823
+ const run = () => {
7824
+ ctx.inject(["workspaceRegistry"], (inner) => {
7825
+ syncBossWorkspaces(inner.get("workspaceRegistry"), getRole()).catch((error) => {
7826
+ const why = error instanceof Error ? error.message : String(error);
7827
+ console.error("[dsh-rez-suite] boss workspace sync failed:", why);
7828
+ });
7787
7829
  });
7788
- });
7830
+ };
7831
+ run();
7832
+ return run;
7789
7833
  }
7790
7834
  //#endregion
7791
7835
  //#region src/index.ts
@@ -7845,15 +7889,17 @@ const Config = Schema.object({
7845
7889
  })
7846
7890
  });
7847
7891
  const SECTION_ORDER = 150;
7848
- const REZ_GUIDANCE = [
7892
+ const REZ_GUIDANCE_CORE = [
7849
7893
  "本机已安装 ReZ-TI 拆分包:工作身份由 dsh-rez-sso 提供,MCP 由官方 dsh-mcp-client 接入 Odoo / Nextcloud / 企业微信 / Home Assistant。",
7850
7894
  "工具名 mcp__odoo__*、mcp__nextcloud__*、mcp__wechat__*、mcp__homeassistant__*。",
7851
7895
  "密钥:REZ_ODOO_TOKEN、REZ_NEXTCLOUD_PASSWORD、REZ_WECHAT_WEBHOOK、REZ_HA_TOKEN。",
7852
7896
  "个人微信(QClaw/ClawBot):打开 设置 → 插件 → ReZ-TI → 微信 扫码,然后直接在微信里说话。消息进入网页左侧「微信」文件夹并延续同一会话,模型跟网页默认。网页归档了再从微信说话会自动恢复到侧栏。发「重启对话」会新开一条可见会话并把旧会话归档。网页端必须开着。不必再跑命令、也不要调用微信 MCP 工具。",
7853
7897
  "意图用 rez_set_intent(erp/files/chat/home/auto)。审计用 rez_audit。",
7854
- "涉及资金、对外发送、设备控制、生产写入时先征得用户批准。",
7855
- BOSS_GUIDANCE
7898
+ "涉及资金、对外发送、设备控制、生产写入时先征得用户批准。"
7856
7899
  ].join("");
7900
+ function rezGuidanceFor(role) {
7901
+ return REZ_GUIDANCE_CORE + bossGuidanceFor(role);
7902
+ }
7857
7903
  function normalize(input) {
7858
7904
  return mergeConfig(defaultConfig(), input ?? {});
7859
7905
  }
@@ -7873,8 +7919,8 @@ function delegatedHost(ctx, getConfig) {
7873
7919
  };
7874
7920
  }
7875
7921
  function apply(ctx, config) {
7876
- mountBossDesk(ctx);
7877
7922
  let current = () => normalize(config);
7923
+ const syncBossDesk = mountBossDesk(ctx, () => current().role);
7878
7924
  const tokens = new TokenManager(joinTokenDb());
7879
7925
  const host = delegatedHost(ctx, () => current());
7880
7926
  ctx.effect(() => () => {
@@ -7887,6 +7933,7 @@ function apply(ctx, config) {
7887
7933
  const value = normalize(next);
7888
7934
  saveConfig(value);
7889
7935
  current = () => value;
7936
+ syncBossDesk();
7890
7937
  try {
7891
7938
  const settings = ctx.get("settings");
7892
7939
  if (settings?.update !== void 0) await settings.update(REZ_SETTINGS_NAMESPACE, value);
@@ -7923,7 +7970,7 @@ function apply(ctx, config) {
7923
7970
  if (value.announceToAgent) disposeSection = ctx.systemPrompt.section({
7924
7971
  name: "plugin:dsh-rez-suite",
7925
7972
  order: SECTION_ORDER,
7926
- text: REZ_GUIDANCE
7973
+ text: rezGuidanceFor(value.role)
7927
7974
  });
7928
7975
  disposeRoutes = ctx.effect(() => {
7929
7976
  const disposers = routes.map((route) => ctx.webServer.register(route));
@@ -7942,8 +7989,12 @@ function apply(ctx, config) {
7942
7989
  setSource: (source) => {
7943
7990
  current = source;
7944
7991
  sync();
7992
+ syncBossDesk();
7945
7993
  },
7946
- onChange: sync
7994
+ onChange: () => {
7995
+ sync();
7996
+ syncBossDesk();
7997
+ }
7947
7998
  });
7948
7999
  sync();
7949
8000
  }
@@ -7951,4 +8002,4 @@ function joinTokenDb() {
7951
8002
  return join(homedir(), ".dsh", "dsh-rez-token-manager.sqlite");
7952
8003
  }
7953
8004
  //#endregion
7954
- export { Config, REZ_GUIDANCE, REZ_SETTINGS_NAMESPACE, apply, inject, name };
8005
+ export { Config, REZ_GUIDANCE_CORE, REZ_SETTINGS_NAMESPACE, apply, inject, name, rezGuidanceFor };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@rezti/dsh-rez-suite",
3
3
  "description": "ReZ-TI one-package install for DeepSeek Harness. Update with: dsh plugin --profile web update @rezti/dsh-rez-suite",
4
- "version": "0.1.18",
4
+ "version": "0.1.19",
5
5
  "type": "module",
6
6
  "engines": {
7
7
  "node": "^22.19.0 || >=24.0.0"
@@ -39,12 +39,12 @@
39
39
  "@modelcontextprotocol/sdk": "^1.30.0",
40
40
  "zod": "^4.4.3",
41
41
  "@rezti/dsh-rez-ha-bridge": "0.1.7",
42
+ "@rezti/dsh-rez-odoo": "0.1.7",
42
43
  "@rezti/dsh-rez-intent": "0.1.4",
43
44
  "@rezti/dsh-rez-nextcloud": "0.1.7",
44
- "@rezti/dsh-rez-token-manager": "0.1.4",
45
+ "@rezti/dsh-rez-sso": "0.1.7",
45
46
  "@rezti/dsh-rez-wechat": "0.1.11",
46
- "@rezti/dsh-rez-odoo": "0.1.7",
47
- "@rezti/dsh-rez-sso": "0.1.7"
47
+ "@rezti/dsh-rez-token-manager": "0.1.4"
48
48
  },
49
49
  "peerDependencies": {
50
50
  "react": "^18.2.0",
package/src/boss/mount.ts CHANGED
@@ -1,31 +1,64 @@
1
1
  import { homedir } from 'node:os'
2
2
  import { join } from 'node:path'
3
3
  import type { Context } from '@deepseek-ai/cordis'
4
- import { registerBossWorkspaces, type WorkspaceRegistrar } from './register.ts'
5
- import { seedBossWorkspaces } from './seed.ts'
4
+ import type { RezRoleId } from '../protocol.ts'
5
+ import {
6
+ registerBossWorkspaces,
7
+ unregisterBossWorkspaces,
8
+ type BossRoomRef,
9
+ type WorkspaceRegistrar,
10
+ } from './register.ts'
11
+ import { BOSS_ROOMS, seedBossWorkspaces } from './seed.ts'
12
+
13
+ export const BOSS_ROLE_HINT = '设置 → 插件 → ReZ-TI → 角色选「老板」后,左侧会出现搞钱 / 产品 / 运营三室。不必手动「添加工作区」。'
6
14
 
7
15
  export const BOSS_GUIDANCE = [
8
- '老板三室已播种并登记为侧栏工作区:~/.dsh/workspaces/搞钱、产品、运营。不必手动「添加工作区」。',
9
- '设置 → 插件 → ReZ-TI → 角色选「老板」。每个文件夹是独立作战室,官方 dsh 会注入该目录 AGENTS.md。',
16
+ '当前角色是老板。侧栏已登记三室:~/.dsh/workspaces/搞钱、产品、运营。不必手动「添加工作区」。',
17
+ '每个文件夹是独立作战室,官方 dsh 会注入该目录 AGENTS.md。',
10
18
  '若该室仍有 BOOTSTRAP.md:本会话先做短问三拍,沉淀到 PRIORITIES.md / MEMORY.md,然后删除 BOOTSTRAP.md。',
11
19
  '已初始化后:读 PRIORITIES.md + MEMORY.md + 今日/昨日 memory/;回答固定四行:结论 → 数字(哪家公司、哪段时间)→ 拍板 1~3 件事 → 未执行任何过账/付款/对外发送。',
12
20
  '微信文件夹仍是收件箱,不要在那边查账或砍 SKU。',
13
21
  ].join('')
14
22
 
23
+ export function bossGuidanceFor(role: RezRoleId): string {
24
+ return role === 'boss' ? BOSS_GUIDANCE : BOSS_ROLE_HINT
25
+ }
26
+
15
27
  function destRoot(env: NodeJS.ProcessEnv = process.env): string {
16
28
  if (env.REZ_BOSS_WORKSPACES !== undefined && env.REZ_BOSS_WORKSPACES !== '') return env.REZ_BOSS_WORKSPACES
17
29
  const home = env.DSH_HOME !== undefined && env.DSH_HOME !== '' ? env.DSH_HOME : join(homedir(), '.dsh')
18
30
  return join(home, 'workspaces')
19
31
  }
20
32
 
21
- /** Seed files, then register official workspaces once workspaceRegistry is ready. */
22
- export function mountBossDesk(ctx: Context): void {
23
- const seeded = seedBossWorkspaces({ destRoot: destRoot() })
24
- ctx.inject(['workspaceRegistry'], (inner) => {
25
- const registry = inner.get('workspaceRegistry') as WorkspaceRegistrar
26
- void registerBossWorkspaces(registry, seeded.rooms).catch((error: unknown) => {
27
- const why = error instanceof Error ? error.message : String(error)
28
- console.error('[dsh-rez-suite] workspaceRegistry.create failed:', why)
33
+ export function expectedBossRooms(env: NodeJS.ProcessEnv = process.env): BossRoomRef[] {
34
+ const root = destRoot(env)
35
+ return BOSS_ROOMS.map((room) => ({ path: join(root, room.folder), folder: room.folder }))
36
+ }
37
+
38
+ export async function syncBossWorkspaces(
39
+ registry: WorkspaceRegistrar,
40
+ role: RezRoleId,
41
+ env: NodeJS.ProcessEnv = process.env,
42
+ ): Promise<void> {
43
+ if (role === 'boss') {
44
+ const seeded = seedBossWorkspaces({ destRoot: destRoot(env) })
45
+ await registerBossWorkspaces(registry, seeded.rooms)
46
+ return
47
+ }
48
+ await unregisterBossWorkspaces(registry, expectedBossRooms(env))
49
+ }
50
+
51
+ /** Seed/register 三室 only for role=boss; drop sidebar rows otherwise. Returns a re-sync function. */
52
+ export function mountBossDesk(ctx: Context, getRole: () => RezRoleId): () => void {
53
+ const run = (): void => {
54
+ ctx.inject(['workspaceRegistry'], (inner) => {
55
+ const registry = inner.get('workspaceRegistry') as WorkspaceRegistrar
56
+ void syncBossWorkspaces(registry, getRole()).catch((error: unknown) => {
57
+ const why = error instanceof Error ? error.message : String(error)
58
+ console.error('[dsh-rez-suite] boss workspace sync failed:', why)
59
+ })
29
60
  })
30
- })
61
+ }
62
+ run()
63
+ return run
31
64
  }
@@ -1,18 +1,54 @@
1
1
  /**
2
- * Register seeded room directories as official dsh workspaces.
2
+ * Register or drop the three boss rooms in the official workspace sidebar.
3
3
  * `workspaceRegistry.create` is idempotent: same canonical path returns the
4
4
  * existing record. New records are prepended, so we register in reverse to
5
5
  * keep 搞钱 → 产品 → 运营 at the top of the sidebar.
6
+ * `delete` only drops the registry row — directories and session logs stay.
6
7
  */
8
+ import { realpathSync } from 'node:fs'
9
+
10
+ export interface WorkspaceRecord {
11
+ id: string
12
+ path: string
13
+ title: string
14
+ }
15
+
7
16
  export interface WorkspaceRegistrar {
8
17
  create(path: string, title?: string): Promise<unknown>
18
+ list(): WorkspaceRecord[]
19
+ delete(id: string): Promise<boolean>
20
+ }
21
+
22
+ export type BossRoomRef = { path: string; folder: string }
23
+
24
+ function canon(path: string): string {
25
+ try {
26
+ return realpathSync(path)
27
+ } catch {
28
+ return path
29
+ }
30
+ }
31
+
32
+ export function isRegisteredBossRoom(workspace: WorkspaceRecord, rooms: ReadonlyArray<BossRoomRef>): boolean {
33
+ const wanted = new Set(rooms.map((room) => canon(room.path)))
34
+ return wanted.has(canon(workspace.path))
9
35
  }
10
36
 
11
37
  export async function registerBossWorkspaces(
12
38
  registry: WorkspaceRegistrar,
13
- rooms: ReadonlyArray<{ path: string; folder: string }>,
39
+ rooms: ReadonlyArray<BossRoomRef>,
14
40
  ): Promise<void> {
15
41
  for (const room of [...rooms].reverse()) {
16
42
  await registry.create(room.path, room.folder)
17
43
  }
18
44
  }
45
+
46
+ export async function unregisterBossWorkspaces(
47
+ registry: WorkspaceRegistrar,
48
+ rooms: ReadonlyArray<BossRoomRef>,
49
+ ): Promise<void> {
50
+ for (const workspace of registry.list()) {
51
+ if (!isRegisteredBossRoom(workspace, rooms)) continue
52
+ await registry.delete(workspace.id)
53
+ }
54
+ }
package/src/index.ts CHANGED
@@ -27,7 +27,7 @@ import { defaultConfig, mergeConfig, saveConfig } from './store.ts'
27
27
  import { TokenManager } from './token-manager.ts'
28
28
  import { rezConfigTool, rezStatusTool } from './tools.ts'
29
29
  import type { RezConfig } from './protocol.ts'
30
- import { BOSS_GUIDANCE, mountBossDesk } from './boss/mount.ts'
30
+ import { bossGuidanceFor, mountBossDesk } from './boss/mount.ts'
31
31
 
32
32
  export const name = 'rez-suite'
33
33
 
@@ -79,16 +79,19 @@ export const Config: z<RezConfig> = z.object({
79
79
 
80
80
  const SECTION_ORDER = 150
81
81
 
82
- export const REZ_GUIDANCE = [
82
+ export const REZ_GUIDANCE_CORE = [
83
83
  '本机已安装 ReZ-TI 拆分包:工作身份由 dsh-rez-sso 提供,MCP 由官方 dsh-mcp-client 接入 Odoo / Nextcloud / 企业微信 / Home Assistant。',
84
84
  '工具名 mcp__odoo__*、mcp__nextcloud__*、mcp__wechat__*、mcp__homeassistant__*。',
85
85
  '密钥:REZ_ODOO_TOKEN、REZ_NEXTCLOUD_PASSWORD、REZ_WECHAT_WEBHOOK、REZ_HA_TOKEN。',
86
86
  '个人微信(QClaw/ClawBot):打开 设置 → 插件 → ReZ-TI → 微信 扫码,然后直接在微信里说话。消息进入网页左侧「微信」文件夹并延续同一会话,模型跟网页默认。网页归档了再从微信说话会自动恢复到侧栏。发「重启对话」会新开一条可见会话并把旧会话归档。网页端必须开着。不必再跑命令、也不要调用微信 MCP 工具。',
87
87
  '意图用 rez_set_intent(erp/files/chat/home/auto)。审计用 rez_audit。',
88
88
  '涉及资金、对外发送、设备控制、生产写入时先征得用户批准。',
89
- BOSS_GUIDANCE,
90
89
  ].join('')
91
90
 
91
+ export function rezGuidanceFor(role: RezConfig['role']): string {
92
+ return REZ_GUIDANCE_CORE + bossGuidanceFor(role)
93
+ }
94
+
92
95
  function normalize(input: Partial<RezConfig> | undefined): RezConfig {
93
96
  return mergeConfig(defaultConfig(), input ?? {})
94
97
  }
@@ -110,8 +113,8 @@ function delegatedHost(ctx: Context, getConfig: () => RezConfig): McpHost {
110
113
  }
111
114
 
112
115
  export function apply(ctx: Context, config?: RezConfig): void {
113
- mountBossDesk(ctx)
114
116
  let current: () => RezConfig = () => normalize(config)
117
+ const syncBossDesk = mountBossDesk(ctx, () => current().role)
115
118
 
116
119
  const tokens = new TokenManager(joinTokenDb())
117
120
  const host = delegatedHost(ctx, () => current())
@@ -128,6 +131,7 @@ export function apply(ctx: Context, config?: RezConfig): void {
128
131
  const value = normalize(next as Partial<RezConfig>)
129
132
  saveConfig(value)
130
133
  current = () => value
134
+ syncBossDesk()
131
135
  try {
132
136
  const settings = ctx.get('settings') as { update?: (namespace: unknown, value: unknown) => Promise<void> } | undefined
133
137
  if (settings?.update !== undefined) await settings.update(REZ_SETTINGS_NAMESPACE, value)
@@ -160,7 +164,7 @@ export function apply(ctx: Context, config?: RezConfig): void {
160
164
  disposeSection = ctx.systemPrompt.section({
161
165
  name: 'plugin:dsh-rez-suite',
162
166
  order: SECTION_ORDER,
163
- text: REZ_GUIDANCE,
167
+ text: rezGuidanceFor(value.role),
164
168
  })
165
169
  }
166
170
 
@@ -179,8 +183,12 @@ export function apply(ctx: Context, config?: RezConfig): void {
179
183
  setSource: (source) => {
180
184
  current = source
181
185
  sync()
186
+ syncBossDesk()
187
+ },
188
+ onChange: () => {
189
+ sync()
190
+ syncBossDesk()
182
191
  },
183
- onChange: sync,
184
192
  })
185
193
 
186
194
  sync()