@yolk_vat-y/dsh-project-memory 0.5.6 → 0.5.8

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.
Files changed (47) hide show
  1. package/CHANGELOG.md +153 -0
  2. package/README.md +37 -34
  3. package/README.zh-CN.md +35 -34
  4. package/client/client.js +458 -107
  5. package/client/client.js.map +1 -1
  6. package/package.json +3 -6
  7. package/src/audit.js +63 -0
  8. package/src/auto-inject.js +447 -286
  9. package/src/chunker.js +14 -6
  10. package/src/client/MemoryView.tsx +12 -4
  11. package/src/client/TaskCommandNode.tsx +1 -1
  12. package/src/client/TaskComponents.tsx +3 -2
  13. package/src/client/TaskPanel.tsx +13 -13
  14. package/src/client/client.ts +93 -21
  15. package/src/client/icons.ts +63 -0
  16. package/src/client/locales.ts +11 -0
  17. package/src/client/session-id.js +88 -0
  18. package/src/client/slash.ts +184 -0
  19. package/src/commands/insight-actions.js +31 -16
  20. package/src/commands/invocation.js +36 -0
  21. package/src/commands/task-actions.js +36 -40
  22. package/src/commands/tasks.js +18 -13
  23. package/src/commands/workflow.js +54 -0
  24. package/src/enhancer.js +11 -2
  25. package/src/index-pipeline.js +119 -0
  26. package/src/index.js +24 -8
  27. package/src/insight-store.js +96 -28
  28. package/src/lazy.js +15 -46
  29. package/src/link.js +10 -1
  30. package/src/parsers/pdfjs-parser.js +2 -4
  31. package/src/project-profile.js +12 -5
  32. package/src/readiness.js +27 -3
  33. package/src/recall.js +28 -8
  34. package/src/setup/taskbridge.js +11 -9
  35. package/src/store.js +56 -17
  36. package/src/symbols.js +58 -19
  37. package/src/tools/forget.js +2 -1
  38. package/src/tools/index-doc.js +4 -1
  39. package/src/tools/index-repo.js +33 -86
  40. package/src/tools/lesson-tools.js +2 -1
  41. package/src/tools/query-memory.js +29 -4
  42. package/src/tools/remember.js +3 -1
  43. package/src/tools/task-tools.js +4 -1
  44. package/src/tools/watch-repo.js +9 -5
  45. package/src/util/fs.js +14 -0
  46. package/src/util/session-cache.js +72 -0
  47. package/src/watch.js +39 -81
package/client/client.js CHANGED
@@ -4,9 +4,84 @@ window.__ModuleLoader__.load({
4
4
  var module = { exports: {} };
5
5
  var exports = module.exports;
6
6
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
7
+ //#region \0rolldown/runtime.js
8
+ var __create = Object.create;
9
+ var __defProp = Object.defineProperty;
10
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
11
+ var __getOwnPropNames = Object.getOwnPropertyNames;
12
+ var __getProtoOf = Object.getPrototypeOf;
13
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
14
+ var __copyProps = (to, from, except, desc) => {
15
+ if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
16
+ key = keys[i];
17
+ if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
18
+ get: ((k) => from[k]).bind(null, key),
19
+ enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
20
+ });
21
+ }
22
+ return to;
23
+ };
24
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule || !__hasOwnProp.call(mod, "default") ? __defProp(target, "default", {
25
+ value: mod,
26
+ enumerable: true
27
+ }) : target, mod));
28
+ //#endregion
7
29
  let react = require("react");
8
30
  let _deepseek_ai_dsh_client_ui_primitives = require("@deepseek-ai/dsh-client-ui-primitives");
31
+ _deepseek_ai_dsh_client_ui_primitives = __toESM(_deepseek_ai_dsh_client_ui_primitives, 1);
9
32
  let react_jsx_runtime = require("react/jsx-runtime");
33
+ //#region src/client/icons.ts
34
+ /**
35
+ * 宿主图标兼容层(dsh 0.1.5 ↔ 0.1.7+)。
36
+ *
37
+ * 命名契约变过一次:
38
+ * - dsh 0.1.5(含 0.1.5-rc.x):尺寸写进名字,如 `IconFolderOpenOutline16`;
39
+ * - dsh 0.1.7+:去掉尺寸后缀、改为字重后缀,如 `IconFolderOpenOutlineRegular` /
40
+ * `IconFolderOpenOutlineMedium`,尺寸改由 `size` prop 传入。
41
+ * ui-primitives 是 web shell 的冻结 seed 模块(不是插件行),插件无法固定它,
42
+ * 同一份产物会拿到宿主那一版的导出;直接按某一版的名字解构会在另一版上得到
43
+ * `undefined`,React 渲染时抛 "Element type is invalid" 把面板整个打崩。
44
+ *
45
+ * 因此这里按旧名导出同名组件,运行时在两个名字里取存在的那个,并把旧名里的
46
+ * 尺寸作为默认 `size`(两版的图标都接受 `size` prop,0.1.5 的默认值正是旧名后缀)。
47
+ * 两版都没有该名字时降级为空组件并告警,而不是让面板崩溃。
48
+ */
49
+ const table = _deepseek_ai_dsh_client_ui_primitives;
50
+ function resolveIcon(names) {
51
+ for (const name of names) {
52
+ const candidate = table[name];
53
+ if (typeof candidate === "function" || typeof candidate === "object" && candidate !== null) return candidate;
54
+ }
55
+ return null;
56
+ }
57
+ function icon(legacyName, currentName, defaultSize) {
58
+ const impl = resolveIcon([legacyName, currentName]);
59
+ if (impl === null) {
60
+ console.warn(`[dsh-project-memory] host ui-primitives exports neither ${legacyName} nor ${currentName}; that icon renders nothing`);
61
+ return function MissingIcon() {
62
+ return null;
63
+ };
64
+ }
65
+ function CompatIcon({ size = defaultSize, ...rest }) {
66
+ return (0, react.createElement)(impl, {
67
+ size,
68
+ ...rest
69
+ });
70
+ }
71
+ CompatIcon.displayName = legacyName;
72
+ return CompatIcon;
73
+ }
74
+ const IconChevronDownOutline14 = icon("IconChevronDownOutline14", "IconChevronDownOutlineRegular", 14);
75
+ const IconChevronUpOutline14 = icon("IconChevronUpOutline14", "IconChevronUpOutlineRegular", 14);
76
+ const IconQuestionOutline14 = icon("IconQuestionOutline14", "IconQuestionOutlineRegular", 14);
77
+ const IconCloseOutline16 = icon("IconCloseOutline16", "IconCloseOutlineRegular", 16);
78
+ const IconFolderOpenOutline16 = icon("IconFolderOpenOutline16", "IconFolderOpenOutlineRegular", 16);
79
+ const IconCheckOutline16 = icon("IconCheckOutline16", "IconCheckOutlineRegular", 16);
80
+ const IconPlayOutline16 = icon("IconPlayOutline16", "IconPlayOutlineRegular", 16);
81
+ const IconChecklistOutline16 = icon("IconChecklistOutline14", "IconChecklistOutlineRegular", 14);
82
+ const IconLightOutline16 = icon("IconLightOutline16", "IconLightOutlineRegular", 16);
83
+ const IconGlobeOutline16 = icon("IconGlobeOutline14", "IconGlobeOutlineRegular", 14);
84
+ //#endregion
10
85
  //#region src/client/locales.ts
11
86
  /**
12
87
  * Locale dictionaries for Task Panel
@@ -83,7 +158,11 @@ window.__ModuleLoader__.load({
83
158
  "mem.f.trigger": "作为 Skill:触发关键词(逗号分隔)",
84
159
  "mem.saved": "已保存",
85
160
  "mem.edit": "编辑",
86
- "mem.section-label": "任务记忆"
161
+ "mem.section-label": "任务记忆",
162
+ "slash.group": "工作流",
163
+ "slash.tasks-desc": "任务清单:步骤进度、涉及文件、当前会话绑定",
164
+ "slash.project-desc": "项目记忆:本项目的教训 / 决策 / 流程",
165
+ "slash.global-desc": "全局记忆:跨项目的经验条目"
87
166
  };
88
167
  const en = {
89
168
  "panel.title": "Task Flow",
@@ -157,7 +236,11 @@ window.__ModuleLoader__.load({
157
236
  "mem.f.trigger": "As Skill: trigger keywords (comma separated)",
158
237
  "mem.saved": "Saved",
159
238
  "mem.edit": "Edit",
160
- "mem.section-label": "Task memory"
239
+ "mem.section-label": "Task memory",
240
+ "slash.group": "Workflow",
241
+ "slash.tasks-desc": "Task list: progress, involved files, current session binding",
242
+ "slash.project-desc": "Project memory: this project’s lessons / decisions / procedures",
243
+ "slash.global-desc": "Global memory: experience entries shared across projects"
161
244
  };
162
245
  function createTranslate(dict) {
163
246
  return (key, params) => {
@@ -585,92 +668,92 @@ window.__ModuleLoader__.load({
585
668
  document.head.appendChild(tag);
586
669
  }
587
670
  var TaskPanel_module_css_default = {
588
- "hintsOff": "vZSZnG_hintsOff",
671
+ "emptyTitle": "vZSZnG_emptyTitle",
672
+ "stepRow": "vZSZnG_stepRow",
673
+ "dragGhostClose": "vZSZnG_dragGhostClose",
674
+ "counts": "vZSZnG_counts",
675
+ "dragGhostContent": "vZSZnG_dragGhostContent",
676
+ "dropTargetBefore": "vZSZnG_dropTargetBefore",
677
+ "stepContent": "vZSZnG_stepContent",
678
+ "memRowArchived": "vZSZnG_memRowArchived",
679
+ "memCount": "vZSZnG_memCount",
680
+ "stepDone": "vZSZnG_stepDone",
681
+ "headerIconBtn": "vZSZnG_headerIconBtn",
682
+ "cardHead": "vZSZnG_cardHead",
683
+ "memTitle": "vZSZnG_memTitle",
684
+ "syncHint": "vZSZnG_syncHint",
685
+ "stepInput": "vZSZnG_stepInput",
686
+ "dragHandle": "vZSZnG_dragHandle",
687
+ "section": "vZSZnG_section",
688
+ "fileRow": "vZSZnG_fileRow",
589
689
  "memoryPane": "vZSZnG_memoryPane",
590
- "panelIn": "vZSZnG_panelIn",
591
- "memRow": "vZSZnG_memRow",
592
- "kindBadge": "vZSZnG_kindBadge",
593
- "memLabel": "vZSZnG_memLabel",
594
- "stepRun": "vZSZnG_stepRun",
595
690
  "dragGhost": "vZSZnG_dragGhost",
691
+ "memBody": "vZSZnG_memBody",
692
+ "badge": "vZSZnG_badge",
693
+ "boundaryFallback": "vZSZnG_boundaryFallback",
596
694
  "card": "vZSZnG_card",
597
- "cardBound": "vZSZnG_cardBound",
598
- "dropTargetAfter": "vZSZnG_dropTargetAfter",
599
- "cardTitle": "vZSZnG_cardTitle",
600
- "sectionLabel": "vZSZnG_sectionLabel",
601
- "stepContentDone": "vZSZnG_stepContentDone",
602
695
  "sectionCount": "vZSZnG_sectionCount",
603
- "stepContentRun": "vZSZnG_stepContentRun",
604
- "filesList": "vZSZnG_filesList",
605
- "stepRow": "vZSZnG_stepRow",
606
- "cardFooter": "vZSZnG_cardFooter",
607
- "dragHandle": "vZSZnG_dragHandle",
608
- "memCount": "vZSZnG_memCount",
696
+ "stepsList": "vZSZnG_stepsList",
697
+ "stepRun": "vZSZnG_stepRun",
609
698
  "dragGhostCard": "vZSZnG_dragGhostCard",
610
- "emptyTitle": "vZSZnG_emptyTitle",
611
- "stepEditable": "vZSZnG_stepEditable",
699
+ "hintsOff": "vZSZnG_hintsOff",
700
+ "stepToggle": "vZSZnG_stepToggle",
701
+ "taskList": "vZSZnG_taskList",
702
+ "memActs": "vZSZnG_memActs",
703
+ "chevron": "vZSZnG_chevron",
704
+ "cardFooter": "vZSZnG_cardFooter",
705
+ "memToolbar": "vZSZnG_memToolbar",
706
+ "memList": "vZSZnG_memList",
707
+ "memNew": "vZSZnG_memNew",
612
708
  "header": "vZSZnG_header",
613
- "cardHead": "vZSZnG_cardHead",
614
- "track": "vZSZnG_track",
615
- "headerIcon": "vZSZnG_headerIcon",
616
- "updated": "vZSZnG_updated",
617
- "stepsList": "vZSZnG_stepsList",
618
- "syncHint": "vZSZnG_syncHint",
619
- "stepContent": "vZSZnG_stepContent",
620
- "headerIconBtn": "vZSZnG_headerIconBtn",
621
- "dropPulse": "vZSZnG_dropPulse",
622
- "memRowMain": "vZSZnG_memRowMain",
709
+ "filePath": "vZSZnG_filePath",
710
+ "cardTitleRow": "vZSZnG_cardTitleRow",
711
+ "cardTitleEditable": "vZSZnG_cardTitleEditable",
712
+ "boundBadge": "vZSZnG_boundBadge",
713
+ "cardMeta": "vZSZnG_cardMeta",
714
+ "progressText": "vZSZnG_progressText",
715
+ "stepContentRun": "vZSZnG_stepContentRun",
623
716
  "headerTitle": "vZSZnG_headerTitle",
717
+ "editorArea": "vZSZnG_editorArea",
718
+ "muted": "vZSZnG_muted",
719
+ "cardTitle": "vZSZnG_cardTitle",
624
720
  "emptyDesc": "vZSZnG_emptyDesc",
625
721
  "cardBody": "vZSZnG_cardBody",
626
- "cardTitleEditable": "vZSZnG_cardTitleEditable",
627
- "badgeArchived": "vZSZnG_badgeArchived",
628
- "memToolbarActs": "vZSZnG_memToolbarActs",
629
- "stepToggle": "vZSZnG_stepToggle",
630
- "miniIcon": "vZSZnG_miniIcon",
631
- "stepInput": "vZSZnG_stepInput",
632
- "memMeta": "vZSZnG_memMeta",
633
- "counts": "vZSZnG_counts",
634
- "stepPending": "vZSZnG_stepPending",
635
- "memNew": "vZSZnG_memNew",
722
+ "sectionLabel": "vZSZnG_sectionLabel",
723
+ "fileLine": "vZSZnG_fileLine",
724
+ "track": "vZSZnG_track",
725
+ "dropPulse": "vZSZnG_dropPulse",
726
+ "kindBadge": "vZSZnG_kindBadge",
727
+ "filesList": "vZSZnG_filesList",
728
+ "trackFill": "vZSZnG_trackFill",
636
729
  "memNewRow": "vZSZnG_memNewRow",
730
+ "headerIcon": "vZSZnG_headerIcon",
637
731
  "headerLeft": "vZSZnG_headerLeft",
638
- "panel": "vZSZnG_panel",
639
- "taskList": "vZSZnG_taskList",
640
- "filePath": "vZSZnG_filePath",
641
- "progressText": "vZSZnG_progressText",
642
- "boundaryFallback": "vZSZnG_boundaryFallback",
643
- "memToolbar": "vZSZnG_memToolbar",
644
- "memActs": "vZSZnG_memActs",
645
732
  "notice": "vZSZnG_notice",
646
- "emptyState": "vZSZnG_emptyState",
647
- "handleGrip": "vZSZnG_handleGrip",
648
- "chevron": "vZSZnG_chevron",
733
+ "stepDragging": "vZSZnG_stepDragging",
649
734
  "miniText": "vZSZnG_miniText",
650
- "dragGhostClose": "vZSZnG_dragGhostClose",
651
- "dragGhostContent": "vZSZnG_dragGhostContent",
652
- "boundBadge": "vZSZnG_boundBadge",
735
+ "stepPending": "vZSZnG_stepPending",
736
+ "miniIcon": "vZSZnG_miniIcon",
653
737
  "fileDot": "vZSZnG_fileDot",
654
- "memRowArchived": "vZSZnG_memRowArchived",
655
- "dropTargetBefore": "vZSZnG_dropTargetBefore",
656
- "fileLine": "vZSZnG_fileLine",
657
- "memList": "vZSZnG_memList",
738
+ "stepEditable": "vZSZnG_stepEditable",
739
+ "miniBar": "vZSZnG_miniBar",
740
+ "memLabel": "vZSZnG_memLabel",
741
+ "memMeta": "vZSZnG_memMeta",
742
+ "panelIn": "vZSZnG_panelIn",
743
+ "emptyState": "vZSZnG_emptyState",
744
+ "memRowMain": "vZSZnG_memRowMain",
745
+ "badgeArchived": "vZSZnG_badgeArchived",
746
+ "panel": "vZSZnG_panel",
747
+ "stepContentDone": "vZSZnG_stepContentDone",
748
+ "memRow": "vZSZnG_memRow",
749
+ "cardBound": "vZSZnG_cardBound",
750
+ "badgeDraft": "vZSZnG_badgeDraft",
751
+ "handleGrip": "vZSZnG_handleGrip",
658
752
  "headerRight": "vZSZnG_headerRight",
659
- "muted": "vZSZnG_muted",
660
- "badge": "vZSZnG_badge",
661
- "section": "vZSZnG_section",
662
- "memBody": "vZSZnG_memBody",
663
- "stepDone": "vZSZnG_stepDone",
664
- "cardTitleRow": "vZSZnG_cardTitleRow",
665
- "cardMeta": "vZSZnG_cardMeta",
666
- "fileRow": "vZSZnG_fileRow",
753
+ "updated": "vZSZnG_updated",
667
754
  "miniChevron": "vZSZnG_miniChevron",
668
- "stepDragging": "vZSZnG_stepDragging",
669
- "trackFill": "vZSZnG_trackFill",
670
- "memTitle": "vZSZnG_memTitle",
671
- "badgeDraft": "vZSZnG_badgeDraft",
672
- "editorArea": "vZSZnG_editorArea",
673
- "miniBar": "vZSZnG_miniBar"
755
+ "dropTargetAfter": "vZSZnG_dropTargetAfter",
756
+ "memToolbarActs": "vZSZnG_memToolbarActs"
674
757
  };
675
758
  //#endregion
676
759
  //#region src/client/TaskComponents.tsx
@@ -697,8 +780,8 @@ window.__ModuleLoader__.load({
697
780
  return `${Math.floor(h / 24)}天前`;
698
781
  }
699
782
  function StepIcon({ status }) {
700
- if (status === "completed") return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconCheckOutline16, { className: TaskPanel_module_css_default.stepDone });
701
- if (status === "in_progress") return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconPlayOutline16, { className: TaskPanel_module_css_default.stepRun });
783
+ if (status === "completed") return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconCheckOutline16, { className: TaskPanel_module_css_default.stepDone });
784
+ if (status === "in_progress") return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconPlayOutline16, { className: TaskPanel_module_css_default.stepRun });
702
785
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: TaskPanel_module_css_default.stepPending });
703
786
  }
704
787
  /**
@@ -797,12 +880,12 @@ window.__ModuleLoader__.load({
797
880
  title: showHints ? hint : void 0,
798
881
  "aria-label": hint,
799
882
  children: [
800
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconFolderOpenOutline16, { className: TaskPanel_module_css_default.miniIcon }),
883
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconFolderOpenOutline16, { className: TaskPanel_module_css_default.miniIcon }),
801
884
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
802
885
  className: TaskPanel_module_css_default.miniText,
803
886
  children: label
804
887
  }),
805
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronUpOutline14, { className: TaskPanel_module_css_default.miniChevron })
888
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconChevronUpOutline14, { className: TaskPanel_module_css_default.miniChevron })
806
889
  ]
807
890
  });
808
891
  }
@@ -902,7 +985,7 @@ window.__ModuleLoader__.load({
902
985
  }),
903
986
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
904
987
  className: TaskPanel_module_css_default.chevron,
905
- children: expanded ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronUpOutline14, {}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronDownOutline14, {})
988
+ children: expanded ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconChevronUpOutline14, {}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconChevronDownOutline14, {})
906
989
  })
907
990
  ]
908
991
  })]
@@ -1205,6 +1288,12 @@ window.__ModuleLoader__.load({
1205
1288
  const inflightRef = (0, react.useRef)(false);
1206
1289
  const lastRunRef = (0, react.useRef)(0);
1207
1290
  const refresh = (0, react.useCallback)(async (force = false) => {
1291
+ if (!sessionId) {
1292
+ setPayload(null);
1293
+ setError(null);
1294
+ setLoading(false);
1295
+ return;
1296
+ }
1208
1297
  if (inflightRef.current) return;
1209
1298
  const now = Date.now();
1210
1299
  if (!force && now - lastRunRef.current < 800) return;
@@ -1213,7 +1302,7 @@ window.__ModuleLoader__.load({
1213
1302
  setLoading(true);
1214
1303
  setError(null);
1215
1304
  try {
1216
- const res = await runLine(`/insight list ${scope}`);
1305
+ const res = await runLine(`/tasks insight list ${scope}`);
1217
1306
  if (!res.ok) {
1218
1307
  setError(res.text);
1219
1308
  setPayload(null);
@@ -1235,7 +1324,7 @@ window.__ModuleLoader__.load({
1235
1324
  setBusy(true);
1236
1325
  setError(null);
1237
1326
  try {
1238
- const line = `/insight ${action} ${scope} ${id}${action === "demote" && scope === "project" && boundTaskId ? ` ${boundTaskId}` : ""}`;
1327
+ const line = `/tasks insight ${action} ${scope} ${id}${action === "demote" && scope === "project" && boundTaskId ? ` ${boundTaskId}` : ""}`;
1239
1328
  const res = await runLine(line);
1240
1329
  if (!res.ok) setError(res.text);
1241
1330
  else refresh(true);
@@ -1311,7 +1400,7 @@ window.__ModuleLoader__.load({
1311
1400
  }
1312
1401
  if (form.kind === "procedure" && form.steps.trim()) fields.steps = form.steps.split("\n").map((s) => s.trim()).filter(Boolean);
1313
1402
  if (form.kind === "procedure" && form.trigger.trim()) fields.trigger = { keywords: form.trigger.split(/[,,]/).map((s) => s.trim()).filter(Boolean) };
1314
- const line = editingId ? `/insight edit ${scope} ${editingId} ${JSON.stringify(fields)}` : `/insight save ${scope} ${JSON.stringify(fields)}`;
1403
+ const line = editingId ? `/tasks insight edit ${scope} ${editingId} ${JSON.stringify(fields)}` : `/tasks insight save ${scope} ${JSON.stringify(fields)}`;
1315
1404
  const res = await runLine(line);
1316
1405
  if (!res.ok) setError(res.text);
1317
1406
  else {
@@ -1589,6 +1678,86 @@ window.__ModuleLoader__.load({
1589
1678
  });
1590
1679
  }
1591
1680
  //#endregion
1681
+ //#region src/client/session-id.js
1682
+ /**
1683
+ * 会话列表快照 → 当前会话 id(宿主版本兼容层)。
1684
+ *
1685
+ * 快照结构被宿主改过一次,旧写法只认第一种:
1686
+ * - dsh ≤0.1.6:`{ current?: id, items: [{ sessionId, blank }] }`
1687
+ * - dsh 0.1.7+:`{ ids: id[], byId: Record<id, SessionSummary>, phase, projectionsBySession }`
1688
+ * —— `current` 与 `items` **都不存在了**。
1689
+ *
1690
+ * 后果一(永远 null):0.1.7 上 `snap.current` 和 `snap.items` 都是 undefined → 旧代码永远
1691
+ * 返回 null → 面板显示「还没有会话」、记忆视图报 `同步失败: no session / commands service`。
1692
+ * 而**任务视图渲染的是 task-data-store 里的缓存快照**,看起来还正常,所以这个故障很容易被
1693
+ * 误读成"数据格式/旧版本兼容"问题。
1694
+ *
1695
+ * 后果二(不跟随切换):光"取一个非 null 的 id"不够 —— 兜底取的是宿主列表里第一个非 blank 的
1696
+ * 会话,它**不随用户切换对话而变化**,面板会一直停在同一个会话(从而显示错项目的任务)。
1697
+ * 0.1.7 判断"当前会话"的正式依据是 `SessionSummary.retainedBy.mainView > 0`:
1698
+ * 主视图正在 retain 的那个就是用户正在看的那个(第一方同款判据,见 ui-layout/DocumentTitle.tsx
1699
+ * 与 ui-session)。它就在**列表快照的 byId 行上**,而 retain 计数变化会 `list.set(...)` 重新发布
1700
+ * 快照(session-controller/.../sessions/service.ts 的 publishRetention),所以订阅
1701
+ * `ctx.sessions.list` 的组件会在切换会话时自动重渲染 —— 这条必须走快照内字段,不能另开
1702
+ * `retainInfo()` 订阅,否则切换不会触发重渲染。
1703
+ *
1704
+ * 实测形状见 `packages/api/session-controller/src/client/sessions/service.ts` 的
1705
+ * `SessionListState` / `SessionSummary`。
1706
+ */
1707
+ /** 正数才算持有(retain 计数缺失/0/负数一律当没有)。 */
1708
+ function heldCount(value) {
1709
+ return typeof value === "number" && value > 0 ? value : 0;
1710
+ }
1711
+ /** 从一行 summary 抽出本模块关心的三个事实。 */
1712
+ function rowFacts(row) {
1713
+ return {
1714
+ blank: row?.blank === true,
1715
+ /** 主视图对这条会话的 retain 计数;> 0 即"用户正在看它"。 */
1716
+ mainView: heldCount(row?.retainedBy?.mainView)
1717
+ };
1718
+ }
1719
+ /**
1720
+ * 把两种快照形状统一成 `{ id, blank, mainView }` 列表,顺序保持宿主给的顺序。
1721
+ * @param {object|undefined|null} snap - `ctx.sessions.list.getSnapshot()`
1722
+ * @returns {Array<{id: string, blank: boolean, mainView: number}>} 认不出的形状返回空数组
1723
+ */
1724
+ function sessionRows(snap) {
1725
+ if (!snap || typeof snap !== "object") return [];
1726
+ if (Array.isArray(snap.items)) return snap.items.map((row) => ({
1727
+ id: row?.sessionId,
1728
+ ...rowFacts(row)
1729
+ })).filter((row) => typeof row.id === "string" && row.id !== "");
1730
+ const byId = snap.byId && typeof snap.byId === "object" ? snap.byId : null;
1731
+ if (Array.isArray(snap.ids)) return snap.ids.map((id) => ({
1732
+ id,
1733
+ ...rowFacts(byId?.[id])
1734
+ })).filter((row) => typeof row.id === "string" && row.id !== "");
1735
+ if (byId) return Object.keys(byId).map((id) => ({
1736
+ id,
1737
+ ...rowFacts(byId[id])
1738
+ })).filter((row) => typeof row.id === "string" && row.id !== "");
1739
+ return [];
1740
+ }
1741
+ /**
1742
+ * 选出一个可用于执行命令的会话 id(即"用户正在看的那个")。
1743
+ *
1744
+ * 优先级:
1745
+ * 1. 旧形状的显式 `current`(≤0.1.6 的权威字段,保持原语义);
1746
+ * 2. `retainedBy.mainView > 0` 的会话(0.1.7 的权威判据,**跟随会话切换**);
1747
+ * 3. 第一个非 blank → 第一个(对两种形状都适用的兜底)。
1748
+ * @param {object|undefined|null} snap - `ctx.sessions.list.getSnapshot()`
1749
+ * @returns {string|null} 会话 id;拿不到返回 null(调用方据此显示「还没有会话」)
1750
+ */
1751
+ function pickSessionId(snap) {
1752
+ if (!snap || typeof snap !== "object") return null;
1753
+ if (typeof snap.current === "string" && snap.current !== "") return snap.current;
1754
+ const rows = sessionRows(snap);
1755
+ const live = rows.find((row) => row.mainView > 0);
1756
+ if (live) return live.id;
1757
+ const chosen = rows.find((row) => !row.blank) ?? rows[0];
1758
+ return chosen ? chosen.id : null;
1759
+ }
1760
+ //#endregion
1592
1761
  //#region src/client/TaskPanel.tsx
1593
1762
  /**
1594
1763
  * Task Panel — dsh web `shell.overlay` 浮动任务面板。
@@ -1615,10 +1784,7 @@ window.__ModuleLoader__.load({
1615
1784
  if (!list || typeof list.subscribe !== "function") return;
1616
1785
  return list.subscribe(() => force((n) => n + 1));
1617
1786
  }, [ctx]);
1618
- const snap = ctx?.sessions?.list?.getSnapshot?.();
1619
- if (!snap) return null;
1620
- if (snap.current) return snap.current;
1621
- return (Array.isArray(snap.items) ? snap.items.find((s) => !s.blank) ?? snap.items[0] : void 0)?.sessionId ?? null;
1787
+ return pickSessionId(ctx?.sessions?.list?.getSnapshot?.());
1622
1788
  }
1623
1789
  var PanelErrorBoundary = class extends react.Component {
1624
1790
  state = { failed: false };
@@ -1702,7 +1868,7 @@ window.__ModuleLoader__.load({
1702
1868
  const prevBoundId = data.boundTaskId;
1703
1869
  await runLine("/tasks");
1704
1870
  const newBoundId = taskDataStore.getSnapshot().boundTaskId;
1705
- if (newBoundId && newBoundId !== prevBoundId) await runLine(`/task switch ${newBoundId}`);
1871
+ if (newBoundId && newBoundId !== prevBoundId) await runLine(`/tasks switch ${newBoundId}`);
1706
1872
  } finally {
1707
1873
  setSyncing(false);
1708
1874
  }
@@ -1711,7 +1877,7 @@ window.__ModuleLoader__.load({
1711
1877
  if (syncing) return;
1712
1878
  setSyncing(true);
1713
1879
  try {
1714
- await runLine(`/task ${verb} ${taskId}`);
1880
+ await runLine(`/tasks ${verb} ${taskId}`);
1715
1881
  } finally {
1716
1882
  setSyncing(false);
1717
1883
  }
@@ -1720,14 +1886,14 @@ window.__ModuleLoader__.load({
1720
1886
  if (syncing) return;
1721
1887
  setSyncing(true);
1722
1888
  try {
1723
- await runLine(`/insight ${action} task ${id}`);
1889
+ await runLine(`/tasks insight ${action} task ${id}`);
1724
1890
  await refresh();
1725
1891
  } finally {
1726
1892
  setSyncing(false);
1727
1893
  }
1728
1894
  };
1729
1895
  const pushSteps = (taskId, steps) => {
1730
- runLine(`/task todos ${JSON.stringify(steps.map((s) => ({
1896
+ runLine(`/tasks todos ${JSON.stringify(steps.map((s) => ({
1731
1897
  content: s.content,
1732
1898
  status: s.status
1733
1899
  })))}`);
@@ -1771,7 +1937,7 @@ window.__ModuleLoader__.load({
1771
1937
  const trimmed = value.trim();
1772
1938
  if (!task) return;
1773
1939
  if (task.title === trimmed || !trimmed) return;
1774
- runLine(`/task rename ${taskId} ${JSON.stringify(trimmed)}`);
1940
+ runLine(`/tasks rename ${taskId} ${JSON.stringify(trimmed)}`);
1775
1941
  };
1776
1942
  const expand = () => {
1777
1943
  uiActions.open();
@@ -1881,7 +2047,7 @@ window.__ModuleLoader__.load({
1881
2047
  onClick: cycleTheme,
1882
2048
  title: showHints ? styleLabel : void 0,
1883
2049
  "aria-label": styleLabel,
1884
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconFolderOpenOutline16, { className: TaskPanel_module_css_default.headerIcon })
2050
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconFolderOpenOutline16, { className: TaskPanel_module_css_default.headerIcon })
1885
2051
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("h2", {
1886
2052
  className: TaskPanel_module_css_default.headerTitle,
1887
2053
  children: viewTitle
@@ -1936,7 +2102,7 @@ window.__ModuleLoader__.load({
1936
2102
  "aria-pressed": !showHints,
1937
2103
  "aria-label": showHints ? t("panel.hints-off") : t("panel.hints-on"),
1938
2104
  title: showHints ? t("panel.hints-off") : t("panel.hints-on"),
1939
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconQuestionOutline14, { size: 16 })
2105
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconQuestionOutline14, { size: 16 })
1940
2106
  }),
1941
2107
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
1942
2108
  variant: "ghost",
@@ -1944,7 +2110,7 @@ window.__ModuleLoader__.load({
1944
2110
  onClick: () => uiActions.minimize(),
1945
2111
  "aria-label": t("panel.minimize"),
1946
2112
  title: showHints ? t("panel.minimize") : void 0,
1947
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronDownOutline14, {})
2113
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconChevronDownOutline14, {})
1948
2114
  }),
1949
2115
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
1950
2116
  variant: "ghost",
@@ -1952,7 +2118,7 @@ window.__ModuleLoader__.load({
1952
2118
  onClick: () => uiActions.close(),
1953
2119
  "aria-label": t("panel.close"),
1954
2120
  title: showHints ? t("panel.close") : void 0,
1955
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconCloseOutline16, {})
2121
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconCloseOutline16, {})
1956
2122
  })
1957
2123
  ]
1958
2124
  })]
@@ -2004,7 +2170,7 @@ window.__ModuleLoader__.load({
2004
2170
  expanded,
2005
2171
  onToggleExpand: () => uiActions.toggleTaskExpanded(task.id),
2006
2172
  onSwitch: () => handleAction("switch", task.id),
2007
- onUnbind: () => runLine("/task unbind"),
2173
+ onUnbind: () => runLine("/tasks unbind"),
2008
2174
  onArchive: () => handleAction("archive", task.id),
2009
2175
  onRename: (value) => commitTitle(task.id, value),
2010
2176
  onEditStep: (index, value) => commitStepText(task.id, index, value),
@@ -2035,8 +2201,8 @@ window.__ModuleLoader__.load({
2035
2201
  document.head.appendChild(tag);
2036
2202
  }
2037
2203
  var TaskCommandNode_module_css_default = {
2038
- "icon": "TPe-xa_icon",
2039
- "row": "TPe-xa_row"
2204
+ "row": "TPe-xa_row",
2205
+ "icon": "TPe-xa_icon"
2040
2206
  };
2041
2207
  //#endregion
2042
2208
  //#region src/client/TaskCommandNode.tsx
@@ -2092,7 +2258,7 @@ window.__ModuleLoader__.load({
2092
2258
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2093
2259
  className: TaskCommandNode_module_css_default.row,
2094
2260
  "data-variant": "ok",
2095
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconFolderOpenOutline16, { className: TaskCommandNode_module_css_default.icon }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: [
2261
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconFolderOpenOutline16, { className: TaskCommandNode_module_css_default.icon }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: [
2096
2262
  "记忆 ",
2097
2263
  scope,
2098
2264
  " · ",
@@ -2132,8 +2298,130 @@ window.__ModuleLoader__.load({
2132
2298
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2133
2299
  className: TaskCommandNode_module_css_default.row,
2134
2300
  "data-variant": "ok",
2135
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconFolderOpenOutline16, { className: TaskCommandNode_module_css_default.icon }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: label })]
2301
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconFolderOpenOutline16, { className: TaskCommandNode_module_css_default.icon }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: label })]
2302
+ });
2303
+ }
2304
+ //#endregion
2305
+ //#region src/client/slash.ts
2306
+ /** 源的稳定标识:同一 trigger 内唯一,重复注册会抛错。 */
2307
+ const SLASH_SOURCE_NAME = "project-memory";
2308
+ /** 菜单三行。顺序即渲染顺序;标题复用面板已有的 view.* 文案,保证与面板视图名一致。 */
2309
+ const ROWS = [
2310
+ {
2311
+ labelKey: "view.task",
2312
+ descriptionKey: "slash.tasks-desc",
2313
+ icon: IconChecklistOutline16,
2314
+ match: ["tasks", "task"],
2315
+ line: "/tasks"
2316
+ },
2317
+ {
2318
+ labelKey: "view.project",
2319
+ descriptionKey: "slash.project-desc",
2320
+ icon: IconLightOutline16,
2321
+ match: [
2322
+ "memory",
2323
+ "project",
2324
+ "insight"
2325
+ ],
2326
+ line: "/tasks insight list project"
2327
+ },
2328
+ {
2329
+ labelKey: "view.global",
2330
+ descriptionKey: "slash.global-desc",
2331
+ icon: IconGlobeOutline16,
2332
+ match: [
2333
+ "memory",
2334
+ "global",
2335
+ "insight"
2336
+ ],
2337
+ line: "/tasks insight list global"
2338
+ }
2339
+ ];
2340
+ /** 按当前语言取翻译函数。 */
2341
+ function translator(ctx) {
2342
+ return createTranslate(ctx?.locale?.getSnapshot?.()?.active === "zh" ? zh : en);
2343
+ }
2344
+ /**
2345
+ * 一轮候选:把三行装配成菜单行,再按查询与位置过滤。
2346
+ *
2347
+ * 三行都不接参数(动作全在面板卡片里),所以位置过滤只做一件事:行内出现的 `/` 多半是路径,
2348
+ * 不弹这一整组(宿主对无 input 命令在前导/行内都放行,这里更保守)。
2349
+ * @param t - 翻译函数。
2350
+ * @param req - 宿主给的候选请求(query / position)。
2351
+ * @returns 菜单行;永不抛出。
2352
+ */
2353
+ function buildSlashCandidates(t, req) {
2354
+ if (req?.position !== void 0 && req.position !== "leading") return [];
2355
+ const query = typeof req?.query === "string" ? req.query.trim().toLowerCase() : "";
2356
+ return ROWS.map((row) => {
2357
+ const title = t(row.labelKey);
2358
+ return {
2359
+ name: title,
2360
+ label: title,
2361
+ description: t(row.descriptionKey),
2362
+ icon: row.icon,
2363
+ section: t("slash.group"),
2364
+ line: row.line,
2365
+ terms: [title, ...row.match].map((term) => term.toLowerCase())
2366
+ };
2367
+ }).filter((row) => query === "" || row.terms.some((term) => term.startsWith(query))).map(({ terms: _terms, ...candidate }) => candidate);
2368
+ }
2369
+ /**
2370
+ * 把触发 token 从草稿里删掉(span 做 CAS:草稿被改过就拒绝,什么都不动)。
2371
+ * 这是宿主 command 源消费菜单点击的同一条契约事件。
2372
+ * @returns true 表示确实消费掉了。
2373
+ */
2374
+ function consumeSpan(deps, pick) {
2375
+ try {
2376
+ const scope = deps.sessions?.scope?.(pick?.session?.sessionId);
2377
+ if (!scope || typeof scope.bail !== "function") return false;
2378
+ return scope.bail(scope, "slash/input-consume-token", { guard: {
2379
+ kind: "span",
2380
+ span: pick.span
2381
+ } }) === true;
2382
+ } catch (err) {
2383
+ console.warn("[dsh-project-memory] slash token consume failed:", err);
2384
+ return false;
2385
+ }
2386
+ }
2387
+ /**
2388
+ * 一次菜单点击:消费掉触发 token 后立刻执行该行对应的命令行。
2389
+ *
2390
+ * 不返回 claim(回填 `/xxx ` 再等回车):这三行都是「打开某个视图」,claim 会多要一次回车,
2391
+ * 而且子动词(`insight list project`)也没法由一个 claim token 表达。消费失败时仍然执行,
2392
+ * 只是草稿里残留的触发文本要用户自己清掉 —— 比回填一个会执行错命令的 claim 安全。
2393
+ * @param t - 翻译函数(按当前语言把候选 name 映射回 ROWS)。
2394
+ * @param deps - 会话服务与命令执行通道。
2395
+ * @param pick - 宿主给的点击载荷。
2396
+ * @returns PickOutcome;拿不到可用形状时返回 undefined(菜单照常关闭,草稿不动)。
2397
+ */
2398
+ function dispatchSlashPick(t, deps, pick) {
2399
+ const title = pick?.candidate?.name;
2400
+ if (typeof title !== "string" || title === "") return void 0;
2401
+ const row = ROWS.find((candidate) => t(candidate.labelKey) === title);
2402
+ if (row === void 0) return void 0;
2403
+ consumeSpan(deps, pick);
2404
+ deps.run(pick.session, row.line, []).catch((err) => {
2405
+ console.warn(`[dsh-project-memory] ${row.line} failed:`, err);
2136
2406
  });
2407
+ return "handled";
2408
+ }
2409
+ /**
2410
+ * 造出注册给 `ctx.inputTriggers.registerSource` 的源对象。
2411
+ * @param ctx - client 根上下文(只读 locale)。
2412
+ * @param deps - 会话服务与命令执行通道。
2413
+ * @returns 触发器源;只依赖宿主的公开契约字段。
2414
+ */
2415
+ function createSlashSource(ctx, deps) {
2416
+ const t = translator(ctx);
2417
+ return {
2418
+ trigger: "/",
2419
+ name: SLASH_SOURCE_NAME,
2420
+ order: 1,
2421
+ showGroupTitle: false,
2422
+ candidates: async (_session, req) => buildSlashCandidates(t, req),
2423
+ onPick: (pick) => dispatchSlashPick(t, deps, pick)
2424
+ };
2137
2425
  }
2138
2426
  //#endregion
2139
2427
  //#region src/client/client.ts
@@ -2149,6 +2437,9 @@ window.__ModuleLoader__.load({
2149
2437
  *
2150
2438
  * 只依赖宿主 seed 提供的模块(react / dsh-client-ui-primitives),
2151
2439
  * 数据经 remote.commands.execute 执行 /tasks 命令获取 JSON 快照。
2440
+ *
2441
+ * 另有一个可选增强:自建的 `/` 菜单源(slash.ts),把三条命令收进一个带图标和
2442
+ * 小节标题的「工作流」组。它**整体是可选的**——宿主没有 slash 触发器服务时静默跳过。
2152
2443
  */
2153
2444
  const NS = "dsh-project-memory";
2154
2445
  const name = NS;
@@ -2160,29 +2451,89 @@ window.__ModuleLoader__.load({
2160
2451
  "remote.commands",
2161
2452
  "locale"
2162
2453
  ];
2163
- function apply(ctx) {
2164
- const slots = ctx?.slots;
2165
- if (!slots || typeof slots.inject !== "function") {
2166
- console.warn(`[${NS}] host has no slots service — task panel disabled`);
2454
+ /**
2455
+ * 执行一条命令行,映射成 composer 的 SubmitOutcome。
2456
+ * 与 TaskPanel 走同一个 remote.commands.execute 通道(同样的返回信封)。
2457
+ * @param commands - ctx.remote.commands
2458
+ * @param session - 会话投影(只读 sessionId)
2459
+ * @param line - 完整命令行(含前导斜杠)
2460
+ * @param attachments - 提交附件(菜单路径恒为空)
2461
+ * @returns {kind:'success'|'error', text?}
2462
+ */
2463
+ async function runCommand(commands, session, line, attachments = []) {
2464
+ const sessionId = session?.sessionId;
2465
+ if (typeof sessionId !== "string" || !commands || typeof commands.execute !== "function") return {
2466
+ kind: "error",
2467
+ text: `no session / commands service for ${line}`
2468
+ };
2469
+ const envelope = await commands.execute(sessionId, line, [...attachments]);
2470
+ if (envelope && typeof envelope === "object" && envelope.ok === false) return {
2471
+ kind: "error",
2472
+ text: `command.execute failed: ${envelope.error?.code ?? "unknown"}`
2473
+ };
2474
+ const execution = envelope && typeof envelope === "object" && "value" in envelope ? envelope.value : envelope;
2475
+ const result = execution?.result ?? execution;
2476
+ if (result?.kind === "error") return {
2477
+ kind: "error",
2478
+ text: result.text ?? `${line} failed`
2479
+ };
2480
+ return { kind: "success" };
2481
+ }
2482
+ /**
2483
+ * 注册自建的 `/` 菜单源(见 slash.ts)。
2484
+ *
2485
+ * 整段都是**可选增强**:宿主没有 `inputTriggers` 服务、或该服务换了契约时,绝不能因此
2486
+ * 让整个 client 插件挂掉——那会连任务面板一起消失。`inputTriggers` 因此不进顶层 `inject`
2487
+ * (顶层 inject 未满足时 cordis 根本不会调用 apply),而是走嵌套 inject + 两层 try/catch。
2488
+ * @param ctx - client 根上下文
2489
+ */
2490
+ function registerSlashSource(ctx) {
2491
+ if (typeof ctx?.inject !== "function") {
2492
+ console.warn(`[${NS}] host has no ctx.inject — slash menu group disabled`);
2167
2493
  return;
2168
2494
  }
2169
2495
  try {
2496
+ ctx.inject([
2497
+ "inputTriggers",
2498
+ "sessions",
2499
+ "remote.commands"
2500
+ ], (scope) => {
2501
+ try {
2502
+ const inputTriggers = scope?.inputTriggers;
2503
+ if (!inputTriggers || typeof inputTriggers.registerSource !== "function") {
2504
+ console.warn(`[${NS}] host has no inputTriggers.registerSource — slash menu group disabled`);
2505
+ return;
2506
+ }
2507
+ const source = createSlashSource(scope, {
2508
+ sessions: scope.sessions,
2509
+ run: (session, line, attachments) => runCommand(scope?.remote?.commands, session, line, attachments)
2510
+ });
2511
+ scope.effect(() => inputTriggers.registerSource(source), "dsh-project-memory: slash source");
2512
+ } catch (err) {
2513
+ console.warn(`[${NS}] slash source registration failed:`, err);
2514
+ }
2515
+ });
2516
+ } catch (err) {
2517
+ console.warn(`[${NS}] slash source injection failed:`, err);
2518
+ }
2519
+ }
2520
+ function apply(ctx) {
2521
+ const slots = ctx?.slots;
2522
+ if (!slots || typeof slots.inject !== "function") console.warn(`[${NS}] host has no slots service — task panel disabled`);
2523
+ else try {
2170
2524
  slots.inject("shell.overlay", () => slots.register({
2171
2525
  name: "shell.overlay",
2172
2526
  id: "dsh-project-memory-task-panel",
2173
2527
  order: 100
2174
2528
  }, () => (0, react.createElement)(TaskPanelEntry, { ctx })));
2175
- for (const key of [
2176
- "tasks",
2177
- "task",
2178
- "insight"
2179
- ]) slots.inject("conversation.chat.commandview", () => slots.register({
2529
+ for (const key of ["tasks"]) slots.inject("conversation.chat.commandview", () => slots.register({
2180
2530
  name: "conversation.chat.commandview",
2181
2531
  key
2182
2532
  }, TaskCommandNode));
2183
2533
  } catch (err) {
2184
2534
  console.warn(`[${NS}] task panel registration failed:`, err);
2185
2535
  }
2536
+ registerSlashSource(ctx);
2186
2537
  }
2187
2538
  //#endregion
2188
2539
  exports.apply = apply;