dominds 1.2.5 → 1.2.7

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 (149) hide show
  1. package/dist/agent-priming.js +2051 -0
  2. package/dist/apps/app-lock-file.js +228 -0
  3. package/dist/apps/assigned-port.js +124 -0
  4. package/dist/apps/enabled-apps.js +472 -7
  5. package/dist/apps/manifest.js +37 -0
  6. package/dist/apps/override-paths.js +19 -6
  7. package/dist/apps/problems.js +43 -0
  8. package/dist/apps/resolution-file.js +370 -0
  9. package/dist/apps/runtime.js +5 -17
  10. package/dist/apps/teammates.js +102 -1
  11. package/dist/cli/disable.js +10 -6
  12. package/dist/cli/enable.js +21 -19
  13. package/dist/cli/install.js +40 -18
  14. package/dist/cli/uninstall.js +6 -6
  15. package/dist/cli/update.js +38 -13
  16. package/dist/dialog.js +5 -0
  17. package/dist/docs/app-constitution.md +85 -18
  18. package/dist/docs/app-constitution.zh.md +86 -21
  19. package/dist/docs/dialog-system.md +1 -1
  20. package/dist/docs/dialog-system.zh.md +1 -1
  21. package/dist/docs/dominds-agent-priming.md +218 -0
  22. package/dist/docs/dominds-agent-priming.zh.md +196 -0
  23. package/dist/docs/drive-logic-context-refactor-plan.zh.md +338 -0
  24. package/dist/docs/keep-going.md +176 -0
  25. package/dist/docs/keep-going.zh.md +162 -0
  26. package/dist/docs/showing-by-doing.md +208 -0
  27. package/dist/docs/showing-by-doing.zh.md +177 -0
  28. package/dist/docs/team-mgmt-toolset.md +482 -0
  29. package/dist/docs/team-mgmt-toolset.zh.md +426 -0
  30. package/dist/llm/defaults.yaml +1 -1
  31. package/dist/llm/driver.js +4093 -0
  32. package/dist/llm/kernel-driver/drive.js +5 -2
  33. package/dist/llm/kernel-driver/flow.js +3 -0
  34. package/dist/minds/promptdocs.js +263 -0
  35. package/dist/problems.js +67 -16
  36. package/dist/server/api-routes.js +333 -0
  37. package/dist/server/prompts-routes.js +545 -0
  38. package/dist/server/server-core.js +4 -0
  39. package/dist/server/websocket-handler.js +17 -0
  40. package/dist/shared/team-mgmt-manual.js +120 -0
  41. package/dist/shared/types/prompts.js +2 -0
  42. package/dist/shared/types/tellask.js +8 -0
  43. package/dist/showing-by-doing.js +1091 -0
  44. package/dist/snippets/README.en.md +3 -0
  45. package/dist/snippets/README.md +4 -0
  46. package/dist/static/assets/{_basePickBy-CF9r08iy.js → _basePickBy-BMCtwrV7.js} +3 -3
  47. package/dist/static/assets/{_basePickBy-CF9r08iy.js.map → _basePickBy-BMCtwrV7.js.map} +1 -1
  48. package/dist/static/assets/{_baseUniq-CxKv0cd4.js → _baseUniq-BuyCgJiA.js} +2 -2
  49. package/dist/static/assets/{_baseUniq-CxKv0cd4.js.map → _baseUniq-BuyCgJiA.js.map} +1 -1
  50. package/dist/static/assets/{arc-C9JyvnlB.js → arc-BDuN8lwA.js} +2 -2
  51. package/dist/static/assets/{arc-C9JyvnlB.js.map → arc-BDuN8lwA.js.map} +1 -1
  52. package/dist/static/assets/{architectureDiagram-VXUJARFQ-CpcUgjHf.js → architectureDiagram-VXUJARFQ-C-ekqGAD.js} +7 -7
  53. package/dist/static/assets/{architectureDiagram-VXUJARFQ-CpcUgjHf.js.map → architectureDiagram-VXUJARFQ-C-ekqGAD.js.map} +1 -1
  54. package/dist/static/assets/{blockDiagram-VD42YOAC-BA9vtmm7.js → blockDiagram-VD42YOAC-CgQiNuuQ.js} +7 -7
  55. package/dist/static/assets/{blockDiagram-VD42YOAC-BA9vtmm7.js.map → blockDiagram-VD42YOAC-CgQiNuuQ.js.map} +1 -1
  56. package/dist/static/assets/{c4Diagram-YG6GDRKO-D49MGNdF.js → c4Diagram-YG6GDRKO-DONC39q-.js} +3 -3
  57. package/dist/static/assets/{c4Diagram-YG6GDRKO-D49MGNdF.js.map → c4Diagram-YG6GDRKO-DONC39q-.js.map} +1 -1
  58. package/dist/static/assets/{channel-B4KzL0Kg.js → channel-CJTFwXIG.js} +2 -2
  59. package/dist/static/assets/{channel-B4KzL0Kg.js.map → channel-CJTFwXIG.js.map} +1 -1
  60. package/dist/static/assets/{chunk-4BX2VUAB-0F-1ayl0.js → chunk-4BX2VUAB-NaIy4uLJ.js} +2 -2
  61. package/dist/static/assets/{chunk-4BX2VUAB-0F-1ayl0.js.map → chunk-4BX2VUAB-NaIy4uLJ.js.map} +1 -1
  62. package/dist/static/assets/{chunk-55IACEB6-Dnl2HDTZ.js → chunk-55IACEB6-JUKI_Ayx.js} +2 -2
  63. package/dist/static/assets/{chunk-55IACEB6-Dnl2HDTZ.js.map → chunk-55IACEB6-JUKI_Ayx.js.map} +1 -1
  64. package/dist/static/assets/{chunk-B4BG7PRW-Bhx5RbkQ.js → chunk-B4BG7PRW-dIswFJDn.js} +5 -5
  65. package/dist/static/assets/{chunk-B4BG7PRW-Bhx5RbkQ.js.map → chunk-B4BG7PRW-dIswFJDn.js.map} +1 -1
  66. package/dist/static/assets/{chunk-DI55MBZ5-EYd1wL3E.js → chunk-DI55MBZ5-DU2b_N30.js} +4 -4
  67. package/dist/static/assets/{chunk-DI55MBZ5-EYd1wL3E.js.map → chunk-DI55MBZ5-DU2b_N30.js.map} +1 -1
  68. package/dist/static/assets/{chunk-FMBD7UC4-DAjkhhUU.js → chunk-FMBD7UC4-BgExcScw.js} +2 -2
  69. package/dist/static/assets/{chunk-FMBD7UC4-DAjkhhUU.js.map → chunk-FMBD7UC4-BgExcScw.js.map} +1 -1
  70. package/dist/static/assets/{chunk-QN33PNHL-CK6TY7IE.js → chunk-QN33PNHL-bitxyqh7.js} +2 -2
  71. package/dist/static/assets/{chunk-QN33PNHL-CK6TY7IE.js.map → chunk-QN33PNHL-bitxyqh7.js.map} +1 -1
  72. package/dist/static/assets/{chunk-QZHKN3VN-CketngiE.js → chunk-QZHKN3VN-Cor8u7DT.js} +2 -2
  73. package/dist/static/assets/{chunk-QZHKN3VN-CketngiE.js.map → chunk-QZHKN3VN-Cor8u7DT.js.map} +1 -1
  74. package/dist/static/assets/{chunk-TZMSLE5B-Bcuvqo45.js → chunk-TZMSLE5B-Aceoxav_.js} +2 -2
  75. package/dist/static/assets/{chunk-TZMSLE5B-Bcuvqo45.js.map → chunk-TZMSLE5B-Aceoxav_.js.map} +1 -1
  76. package/dist/static/assets/{classDiagram-2ON5EDUG-CaP4T3r4.js → classDiagram-2ON5EDUG-D1Q6a8Hg.js} +6 -6
  77. package/dist/static/assets/{classDiagram-2ON5EDUG-CaP4T3r4.js.map → classDiagram-2ON5EDUG-D1Q6a8Hg.js.map} +1 -1
  78. package/dist/static/assets/{classDiagram-v2-WZHVMYZB-CaP4T3r4.js → classDiagram-v2-WZHVMYZB-D1Q6a8Hg.js} +6 -6
  79. package/dist/static/assets/{classDiagram-v2-WZHVMYZB-CaP4T3r4.js.map → classDiagram-v2-WZHVMYZB-D1Q6a8Hg.js.map} +1 -1
  80. package/dist/static/assets/{clone-C-JULvnG.js → clone-MlWbv1V0.js} +2 -2
  81. package/dist/static/assets/{clone-C-JULvnG.js.map → clone-MlWbv1V0.js.map} +1 -1
  82. package/dist/static/assets/{cose-bilkent-S5V4N54A-vXCmi_eC.js → cose-bilkent-S5V4N54A-DWPCXSrn.js} +2 -2
  83. package/dist/static/assets/{cose-bilkent-S5V4N54A-vXCmi_eC.js.map → cose-bilkent-S5V4N54A-DWPCXSrn.js.map} +1 -1
  84. package/dist/static/assets/{dagre-6UL2VRFP-bhGzX6kO.js → dagre-6UL2VRFP-C8ptQ9V3.js} +7 -7
  85. package/dist/static/assets/{dagre-6UL2VRFP-bhGzX6kO.js.map → dagre-6UL2VRFP-C8ptQ9V3.js.map} +1 -1
  86. package/dist/static/assets/{diagram-PSM6KHXK-BUKfmfGk.js → diagram-PSM6KHXK-Bgf1FqkE.js} +8 -8
  87. package/dist/static/assets/{diagram-PSM6KHXK-BUKfmfGk.js.map → diagram-PSM6KHXK-Bgf1FqkE.js.map} +1 -1
  88. package/dist/static/assets/{diagram-QEK2KX5R-DYlq3uFq.js → diagram-QEK2KX5R-BZ5xzofU.js} +7 -7
  89. package/dist/static/assets/{diagram-QEK2KX5R-DYlq3uFq.js.map → diagram-QEK2KX5R-BZ5xzofU.js.map} +1 -1
  90. package/dist/static/assets/{diagram-S2PKOQOG-CjxkLHWG.js → diagram-S2PKOQOG-Dwp47T9I.js} +7 -7
  91. package/dist/static/assets/{diagram-S2PKOQOG-CjxkLHWG.js.map → diagram-S2PKOQOG-Dwp47T9I.js.map} +1 -1
  92. package/dist/static/assets/{erDiagram-Q2GNP2WA-S3hR85On.js → erDiagram-Q2GNP2WA-Cx4weIHl.js} +5 -5
  93. package/dist/static/assets/{erDiagram-Q2GNP2WA-S3hR85On.js.map → erDiagram-Q2GNP2WA-Cx4weIHl.js.map} +1 -1
  94. package/dist/static/assets/{flowDiagram-NV44I4VS-aBmNMuQ0.js → flowDiagram-NV44I4VS-vNUuIeRk.js} +6 -6
  95. package/dist/static/assets/{flowDiagram-NV44I4VS-aBmNMuQ0.js.map → flowDiagram-NV44I4VS-vNUuIeRk.js.map} +1 -1
  96. package/dist/static/assets/{ganttDiagram-JELNMOA3-DJxXaiW1.js → ganttDiagram-JELNMOA3-BEfozJAr.js} +3 -3
  97. package/dist/static/assets/{ganttDiagram-JELNMOA3-DJxXaiW1.js.map → ganttDiagram-JELNMOA3-BEfozJAr.js.map} +1 -1
  98. package/dist/static/assets/{gitGraphDiagram-V2S2FVAM-DEOBCM0G.js → gitGraphDiagram-V2S2FVAM-eHxwc3d9.js} +8 -8
  99. package/dist/static/assets/{gitGraphDiagram-V2S2FVAM-DEOBCM0G.js.map → gitGraphDiagram-V2S2FVAM-eHxwc3d9.js.map} +1 -1
  100. package/dist/static/assets/{graph-DwrKSIE7.js → graph-C6a6uAok.js} +3 -3
  101. package/dist/static/assets/{graph-DwrKSIE7.js.map → graph-C6a6uAok.js.map} +1 -1
  102. package/dist/static/assets/{index-HWTRvE2k.js → index-D3TQbAKh.js} +383 -59
  103. package/dist/static/assets/index-D3TQbAKh.js.map +1 -0
  104. package/dist/static/assets/{infoDiagram-HS3SLOUP-BH9kVuYd.js → infoDiagram-HS3SLOUP-CX0NiId3.js} +6 -6
  105. package/dist/static/assets/{infoDiagram-HS3SLOUP-BH9kVuYd.js.map → infoDiagram-HS3SLOUP-CX0NiId3.js.map} +1 -1
  106. package/dist/static/assets/{journeyDiagram-XKPGCS4Q-Dap7AcjR.js → journeyDiagram-XKPGCS4Q-C1IepPZ-.js} +5 -5
  107. package/dist/static/assets/{journeyDiagram-XKPGCS4Q-Dap7AcjR.js.map → journeyDiagram-XKPGCS4Q-C1IepPZ-.js.map} +1 -1
  108. package/dist/static/assets/{kanban-definition-3W4ZIXB7-4NOl8MEj.js → kanban-definition-3W4ZIXB7-uMNX4Z1W.js} +3 -3
  109. package/dist/static/assets/{kanban-definition-3W4ZIXB7-4NOl8MEj.js.map → kanban-definition-3W4ZIXB7-uMNX4Z1W.js.map} +1 -1
  110. package/dist/static/assets/{layout-D6uIxu1E.js → layout-CpE3kk5z.js} +5 -5
  111. package/dist/static/assets/{layout-D6uIxu1E.js.map → layout-CpE3kk5z.js.map} +1 -1
  112. package/dist/static/assets/{linear-CvBOGQA2.js → linear-DV8laXr9.js} +2 -2
  113. package/dist/static/assets/{linear-CvBOGQA2.js.map → linear-DV8laXr9.js.map} +1 -1
  114. package/dist/static/assets/{mindmap-definition-VGOIOE7T-ugsrLNY5.js → mindmap-definition-VGOIOE7T-CKjgVM9S.js} +4 -4
  115. package/dist/static/assets/{mindmap-definition-VGOIOE7T-ugsrLNY5.js.map → mindmap-definition-VGOIOE7T-CKjgVM9S.js.map} +1 -1
  116. package/dist/static/assets/{pieDiagram-ADFJNKIX-CdVZjM8g.js → pieDiagram-ADFJNKIX-BBonlNyT.js} +8 -8
  117. package/dist/static/assets/{pieDiagram-ADFJNKIX-CdVZjM8g.js.map → pieDiagram-ADFJNKIX-BBonlNyT.js.map} +1 -1
  118. package/dist/static/assets/{quadrantDiagram-AYHSOK5B-A6m5lZKd.js → quadrantDiagram-AYHSOK5B-BTI8HbBu.js} +3 -3
  119. package/dist/static/assets/{quadrantDiagram-AYHSOK5B-A6m5lZKd.js.map → quadrantDiagram-AYHSOK5B-BTI8HbBu.js.map} +1 -1
  120. package/dist/static/assets/{requirementDiagram-UZGBJVZJ-Cac3zSJH.js → requirementDiagram-UZGBJVZJ-ZtSr9Q5R.js} +4 -4
  121. package/dist/static/assets/{requirementDiagram-UZGBJVZJ-Cac3zSJH.js.map → requirementDiagram-UZGBJVZJ-ZtSr9Q5R.js.map} +1 -1
  122. package/dist/static/assets/{sankeyDiagram-TZEHDZUN-DXDdUUl1.js → sankeyDiagram-TZEHDZUN-DibLVGzg.js} +2 -2
  123. package/dist/static/assets/{sankeyDiagram-TZEHDZUN-DXDdUUl1.js.map → sankeyDiagram-TZEHDZUN-DibLVGzg.js.map} +1 -1
  124. package/dist/static/assets/{sequenceDiagram-WL72ISMW-Domsjl5Y.js → sequenceDiagram-WL72ISMW-qXatfzVt.js} +4 -4
  125. package/dist/static/assets/{sequenceDiagram-WL72ISMW-Domsjl5Y.js.map → sequenceDiagram-WL72ISMW-qXatfzVt.js.map} +1 -1
  126. package/dist/static/assets/{stateDiagram-FKZM4ZOC-Bu0lRQK1.js → stateDiagram-FKZM4ZOC-7fgxCQHo.js} +9 -9
  127. package/dist/static/assets/{stateDiagram-FKZM4ZOC-Bu0lRQK1.js.map → stateDiagram-FKZM4ZOC-7fgxCQHo.js.map} +1 -1
  128. package/dist/static/assets/{stateDiagram-v2-4FDKWEC3-D0K-n3ic.js → stateDiagram-v2-4FDKWEC3-DcWlOAnF.js} +5 -5
  129. package/dist/static/assets/{stateDiagram-v2-4FDKWEC3-D0K-n3ic.js.map → stateDiagram-v2-4FDKWEC3-DcWlOAnF.js.map} +1 -1
  130. package/dist/static/assets/{timeline-definition-IT6M3QCI-BGvpddwR.js → timeline-definition-IT6M3QCI-iX2MRdpY.js} +3 -3
  131. package/dist/static/assets/{timeline-definition-IT6M3QCI-BGvpddwR.js.map → timeline-definition-IT6M3QCI-iX2MRdpY.js.map} +1 -1
  132. package/dist/static/assets/{treemap-GDKQZRPO-BoOzOm2j.js → treemap-GDKQZRPO-AVRnyXu1.js} +5 -5
  133. package/dist/static/assets/{treemap-GDKQZRPO-BoOzOm2j.js.map → treemap-GDKQZRPO-AVRnyXu1.js.map} +1 -1
  134. package/dist/static/assets/{xychartDiagram-PRI3JC2R-C_h3_ICR.js → xychartDiagram-PRI3JC2R-DVYEo5aJ.js} +3 -3
  135. package/dist/static/assets/{xychartDiagram-PRI3JC2R-C_h3_ICR.js.map → xychartDiagram-PRI3JC2R-DVYEo5aJ.js.map} +1 -1
  136. package/dist/static/index.html +1 -1
  137. package/dist/team.js +52 -48
  138. package/dist/tellask.js +439 -0
  139. package/dist/tools/context-health.js +177 -0
  140. package/dist/tools/diag.js +583 -0
  141. package/dist/tools/fs.js +194 -68
  142. package/dist/tools/prompts/memory/en/principles.md +13 -5
  143. package/dist/tools/prompts/memory/en/tools.md +11 -36
  144. package/dist/tools/prompts/memory/zh/principles.md +18 -8
  145. package/dist/tools/prompts/memory/zh/tools.md +11 -36
  146. package/dist/tools/team-mgmt.js +3487 -0
  147. package/dist/utils/task-doc.js +236 -0
  148. package/package.json +1 -1
  149. package/dist/static/assets/index-HWTRvE2k.js.map +0 -1
package/dist/tools/fs.js CHANGED
@@ -17,6 +17,60 @@ const readline_1 = require("readline");
17
17
  const access_control_1 = require("../access-control");
18
18
  const log_1 = require("../log");
19
19
  const runtime_language_1 = require("../shared/runtime-language");
20
+ async function statWithSymlinkInfo(absPath) {
21
+ const lstat = await promises_1.default.lstat(absPath);
22
+ if (!lstat.isSymbolicLink()) {
23
+ return {
24
+ lstat,
25
+ followStat: lstat,
26
+ isSymlink: false,
27
+ };
28
+ }
29
+ let symlinkTarget;
30
+ try {
31
+ symlinkTarget = await promises_1.default.readlink(absPath);
32
+ }
33
+ catch (err) {
34
+ log_1.log.warn(`Failed to read symlink ${absPath}:`, err);
35
+ }
36
+ const followStat = await promises_1.default.stat(absPath);
37
+ return {
38
+ lstat,
39
+ followStat,
40
+ isSymlink: true,
41
+ symlinkTarget,
42
+ };
43
+ }
44
+ function symlinkTargetText(target) {
45
+ return target ?? '<unreadable>';
46
+ }
47
+ function symlinkFollowNotice(workLanguage, relPath, target, asKind) {
48
+ const kindLabel = asKind === 'dir'
49
+ ? workLanguage === 'zh'
50
+ ? '目录'
51
+ : 'directory'
52
+ : workLanguage === 'zh'
53
+ ? '文件'
54
+ : 'file';
55
+ const targetText = symlinkTargetText(target);
56
+ if (workLanguage === 'zh') {
57
+ return `🔗 说明:\`${relPath}\` 是符号链接(→ \`${targetText}\`),已按${kindLabel}跟随处理。`;
58
+ }
59
+ return `🔗 Note: \`${relPath}\` is a symlink (→ \`${targetText}\`) and was followed as a ${kindLabel}.`;
60
+ }
61
+ function symlinkRemovalNotice(workLanguage, relPath, target) {
62
+ const targetText = symlinkTargetText(target);
63
+ if (workLanguage === 'zh') {
64
+ return `🔗 说明:删除的是符号链接路径 \`${relPath}\` 本身(→ \`${targetText}\`),不会直接删除其目标。`;
65
+ }
66
+ return `🔗 Note: removed symlink path \`${relPath}\` itself (→ \`${targetText}\`), not the target directly.`;
67
+ }
68
+ function appendSymlinkYamlFields(lines, keyPrefix, info) {
69
+ if (!info || !info.isSymlink)
70
+ return;
71
+ lines.push(`${keyPrefix}_kind: symlink`);
72
+ lines.push(`${keyPrefix}_symlink_target: ${yamlQuote(symlinkTargetText(info.symlinkTarget))}`);
73
+ }
20
74
  function formatSize(bytes) {
21
75
  if (bytes === 0)
22
76
  return '0 B';
@@ -99,6 +153,9 @@ exports.listDirTool = {
99
153
  notDir: (p) => `❌ **错误**\n\n路径 \`${p}\` 不是目录。`,
100
154
  readDirFailed: (msg) => `❌ **错误**\n\n读取目录失败:${msg}`,
101
155
  dirHeader: '📁 **目录:**',
156
+ symlinkPathNotice: (p, target) => target
157
+ ? `🔗 **说明:** \`${p}\` 是符号链接(→ \`${target}\`),已按目录跟随读取。`
158
+ : `🔗 **说明:** \`${p}\` 是符号链接,已按目录跟随读取。`,
102
159
  emptyDir: '_此目录为空。_',
103
160
  table: {
104
161
  name: '名称',
@@ -114,6 +171,9 @@ exports.listDirTool = {
114
171
  notDir: (p) => `❌ **Error**\n\nPath \`${p}\` is not a directory.`,
115
172
  readDirFailed: (msg) => `❌ **Error**\n\nFailed to read directory: ${msg}`,
116
173
  dirHeader: '📁 **Directory:**',
174
+ symlinkPathNotice: (p, target) => target
175
+ ? `🔗 **Note:** \`${p}\` is a symlink (→ \`${target}\`), and was followed as a directory.`
176
+ : `🔗 **Note:** \`${p}\` is a symlink and was followed as a directory.`,
117
177
  emptyDir: '_This directory is empty._',
118
178
  table: {
119
179
  name: 'Name',
@@ -141,9 +201,13 @@ exports.listDirTool = {
141
201
  return content;
142
202
  }
143
203
  try {
204
+ let inputPathIsSymlink = false;
205
+ let inputPathSymlinkTarget;
144
206
  try {
145
- const stats = await promises_1.default.lstat(dir);
146
- if (!stats.isDirectory()) {
207
+ const statsInfo = await statWithSymlinkInfo(dir);
208
+ inputPathIsSymlink = statsInfo.isSymlink;
209
+ inputPathSymlinkTarget = statsInfo.symlinkTarget;
210
+ if (!statsInfo.followStat.isDirectory()) {
147
211
  const content = labels.notDir(rel);
148
212
  return content;
149
213
  }
@@ -188,21 +252,31 @@ exports.listDirTool = {
188
252
  try {
189
253
  const target = await promises_1.default.readlink(entryPath);
190
254
  dirEntry.target = target;
191
- // If symlink points to a text file, count lines from the target
192
255
  try {
193
256
  const targetStats = await promises_1.default.stat(entryPath); // Follow the symlink
194
- if (targetStats.isFile() && isTextFile(entry.name)) {
257
+ if (targetStats.isDirectory()) {
258
+ dirEntry.symlinkResolvedType = 'dir';
259
+ }
260
+ else if (targetStats.isFile()) {
261
+ dirEntry.symlinkResolvedType = 'file';
262
+ }
263
+ else {
264
+ dirEntry.symlinkResolvedType = 'other';
265
+ }
266
+ // If symlink points to a text file, count lines from the target
267
+ if (targetStats.isFile() && (isTextFile(entry.name) || isTextFile(target))) {
195
268
  dirEntry.lines = await countLines(entryPath);
196
269
  }
197
270
  }
198
271
  catch (err) {
199
272
  log_1.log.warn(`Failed to stat symlink target ${entryPath}:`, err);
200
- // Target doesn't exist or can't be accessed
273
+ dirEntry.symlinkResolvedType = 'broken';
201
274
  }
202
275
  }
203
276
  catch (err) {
204
277
  log_1.log.warn(`Failed to read symlink ${entryPath}:`, err);
205
278
  dirEntry.target = '<unreadable>';
279
+ dirEntry.symlinkResolvedType = 'other';
206
280
  }
207
281
  }
208
282
  else {
@@ -228,6 +302,9 @@ exports.listDirTool = {
228
302
  const relativeDir = path_1.default.relative(cwd, dir) || '.';
229
303
  // Create markdown table for directory entries
230
304
  let markdown = `${labels.dirHeader} \`${relativeDir}\`\n\n`;
305
+ if (inputPathIsSymlink) {
306
+ markdown += `${labels.symlinkPathNotice(rel, inputPathSymlinkTarget)}\n\n`;
307
+ }
231
308
  if (data.length === 0) {
232
309
  markdown += labels.emptyDir;
233
310
  }
@@ -244,7 +321,10 @@ exports.listDirTool = {
244
321
  : '❓';
245
322
  const sizeStr = entry.size ? formatSize(entry.size) : '-';
246
323
  const linesStr = entry.lines ? entry.lines.toString() : '-';
247
- const targetStr = entry.target ? `→ ${entry.target}` : '-';
324
+ const targetTypeStr = entry.type === 'symlink' && entry.symlinkResolvedType
325
+ ? ` (${entry.symlinkResolvedType})`
326
+ : '';
327
+ const targetStr = entry.target ? `→ ${entry.target}${targetTypeStr}` : '-';
248
328
  markdown += `| ${typeIcon} \`${entry.name}\` | ${entry.type} | ${sizeStr} | ${linesStr} | ${targetStr} |\n`;
249
329
  }
250
330
  }
@@ -335,20 +415,27 @@ exports.rmDirTool = {
335
415
  return (0, access_control_1.getAccessDeniedMessage)('write', rel, workLanguage);
336
416
  }
337
417
  try {
418
+ let pathInfo;
338
419
  // Check if path exists and is a directory
339
- const stats = await promises_1.default.lstat(targetPath);
340
- if (!stats.isDirectory()) {
341
- return labels.notDir(rel);
420
+ pathInfo = await statWithSymlinkInfo(targetPath);
421
+ const followNotice = pathInfo.isSymlink
422
+ ? `\n\n${symlinkFollowNotice(workLanguage, rel, pathInfo.symlinkTarget, 'dir')}`
423
+ : '';
424
+ if (!pathInfo.followStat.isDirectory()) {
425
+ return `${labels.notDir(rel)}${followNotice}`;
342
426
  }
343
427
  // Check if directory is empty when not using recursive
344
428
  if (!recursive) {
345
429
  const entries = await promises_1.default.readdir(targetPath);
346
430
  if (entries.length > 0) {
347
- return labels.notEmpty(rel);
431
+ return `${labels.notEmpty(rel)}${followNotice}`;
348
432
  }
349
433
  }
350
434
  // Node.js >=22+ types deprecate recursive rmdir in favor of rm.
351
435
  await promises_1.default.rm(targetPath, { recursive, force: false });
436
+ if (pathInfo.isSymlink) {
437
+ return `${labels.removed(rel)}\n\n${symlinkFollowNotice(workLanguage, rel, pathInfo.symlinkTarget, 'dir')}\n\n${symlinkRemovalNotice(workLanguage, rel, pathInfo.symlinkTarget)}`;
438
+ }
352
439
  return labels.removed(rel);
353
440
  }
354
441
  catch (error) {
@@ -416,13 +503,20 @@ exports.rmFileTool = {
416
503
  return (0, access_control_1.getAccessDeniedMessage)('write', rel, workLanguage);
417
504
  }
418
505
  try {
506
+ let pathInfo;
419
507
  // Check if path exists and is a file
420
- const stats = await promises_1.default.lstat(targetPath);
421
- if (!stats.isFile()) {
422
- return labels.notFile(rel);
508
+ pathInfo = await statWithSymlinkInfo(targetPath);
509
+ const followNotice = pathInfo.isSymlink
510
+ ? `\n\n${symlinkFollowNotice(workLanguage, rel, pathInfo.symlinkTarget, 'file')}`
511
+ : '';
512
+ if (!pathInfo.followStat.isFile()) {
513
+ return `${labels.notFile(rel)}${followNotice}`;
423
514
  }
424
515
  // Remove the file
425
516
  await promises_1.default.unlink(targetPath);
517
+ if (pathInfo.isSymlink) {
518
+ return `${labels.removed(rel)}\n\n${symlinkFollowNotice(workLanguage, rel, pathInfo.symlinkTarget, 'file')}\n\n${symlinkRemovalNotice(workLanguage, rel, pathInfo.symlinkTarget)}`;
519
+ }
426
520
  return labels.removed(rel);
427
521
  }
428
522
  catch (error) {
@@ -518,7 +612,7 @@ exports.mkDirTool = {
518
612
  return (0, access_control_1.getAccessDeniedMessage)('write', rel, workLanguage);
519
613
  }
520
614
  try {
521
- const st = await promises_1.default.lstat(targetPath).catch((err) => {
615
+ const pathInfo = await statWithSymlinkInfo(targetPath).catch((err) => {
522
616
  if (typeof err === 'object' &&
523
617
  err !== null &&
524
618
  'code' in err &&
@@ -527,25 +621,30 @@ exports.mkDirTool = {
527
621
  }
528
622
  throw err;
529
623
  });
530
- if (st) {
531
- if (!st.isDirectory()) {
532
- const yaml = [
624
+ if (pathInfo) {
625
+ const symlinkSummary = pathInfo.isSymlink
626
+ ? ` ${symlinkFollowNotice(workLanguage, rel, pathInfo.symlinkTarget, 'dir')}`
627
+ : '';
628
+ if (!pathInfo.followStat.isDirectory()) {
629
+ const yamlLines = [
533
630
  `status: error`,
534
631
  `path: ${yamlQuote(rel)}`,
535
632
  `error: PATH_EXISTS_NOT_DIR`,
536
633
  `summary: ${yamlQuote(workLanguage === 'zh'
537
- ? 'Mk-dir failed: path exists and is not a directory.'
538
- : 'Mk-dir failed: path exists and is not a directory.')}`,
539
- ].join('\n');
540
- return formatYamlCodeBlock(yaml);
634
+ ? `Mk-dir failed: path exists and is not a directory.${symlinkSummary}`
635
+ : `Mk-dir failed: path exists and is not a directory.${symlinkSummary}`)}`,
636
+ ];
637
+ appendSymlinkYamlFields(yamlLines, 'path', pathInfo);
638
+ return formatYamlCodeBlock(yamlLines.join('\n'));
541
639
  }
542
- const yaml = [
640
+ const yamlLines = [
543
641
  `status: ok`,
544
642
  `path: ${yamlQuote(rel)}`,
545
643
  `created: false`,
546
- `summary: ${yamlQuote(`Mk-dir: ${rel} (parents=${parents}).`)}`,
547
- ].join('\n');
548
- return formatYamlCodeBlock(yaml);
644
+ `summary: ${yamlQuote(`Mk-dir: ${rel} (parents=${parents}).${symlinkSummary}`)}`,
645
+ ];
646
+ appendSymlinkYamlFields(yamlLines, 'path', pathInfo);
647
+ return formatYamlCodeBlock(yamlLines.join('\n'));
549
648
  }
550
649
  await promises_1.default.mkdir(targetPath, { recursive: parents });
551
650
  const yaml = [
@@ -620,32 +719,40 @@ exports.moveFileTool = {
620
719
  return (0, access_control_1.getAccessDeniedMessage)('write', from, workLanguage);
621
720
  }
622
721
  try {
623
- const st = await promises_1.default.lstat(absFrom);
624
- if (!st.isFile()) {
625
- const yaml = [
722
+ const fromInfo = await statWithSymlinkInfo(absFrom);
723
+ const fromSymlinkSummary = fromInfo.isSymlink
724
+ ? ` ${symlinkFollowNotice(workLanguage, from, fromInfo.symlinkTarget, 'file')}`
725
+ : '';
726
+ if (!fromInfo.followStat.isFile()) {
727
+ const yamlLines = [
626
728
  `status: error`,
627
729
  `from: ${yamlQuote(from)}`,
628
730
  `to: ${yamlQuote(to)}`,
629
731
  `error: FROM_NOT_FILE`,
630
732
  `summary: ${yamlQuote(workLanguage === 'zh'
631
- ? 'Move-file failed: from is not a file.'
632
- : 'Move-file failed: from is not a file.')}`,
633
- ].join('\n');
634
- return formatYamlCodeBlock(yaml);
733
+ ? `Move-file failed: from is not a file.${fromSymlinkSummary}`
734
+ : `Move-file failed: from is not a file.${fromSymlinkSummary}`)}`,
735
+ ];
736
+ appendSymlinkYamlFields(yamlLines, 'from_path', fromInfo);
737
+ return formatYamlCodeBlock(yamlLines.join('\n'));
635
738
  }
636
739
  const toParent = path_1.default.dirname(absTo);
637
- const toParentSt = await promises_1.default.lstat(toParent).catch(() => undefined);
638
- if (!toParentSt || !toParentSt.isDirectory()) {
639
- const yaml = [
740
+ const toParentInfo = await statWithSymlinkInfo(toParent).catch(() => undefined);
741
+ const toParentSymlinkSummary = toParentInfo && toParentInfo.isSymlink
742
+ ? ` ${symlinkFollowNotice(workLanguage, path_1.default.relative(cwd, toParent) || '.', toParentInfo.symlinkTarget, 'dir')}`
743
+ : '';
744
+ if (!toParentInfo || !toParentInfo.followStat.isDirectory()) {
745
+ const yamlLines = [
640
746
  `status: error`,
641
747
  `from: ${yamlQuote(from)}`,
642
748
  `to: ${yamlQuote(to)}`,
643
749
  `error: TO_PARENT_NOT_DIR`,
644
750
  `summary: ${yamlQuote(workLanguage === 'zh'
645
- ? 'Move-file failed: destination parent directory does not exist. Use mk_dir first.'
646
- : 'Move-file failed: destination parent directory does not exist. Use mk_dir first.')}`,
647
- ].join('\n');
648
- return formatYamlCodeBlock(yaml);
751
+ ? `Move-file failed: destination parent directory does not exist. Use mk_dir first.${toParentSymlinkSummary}`
752
+ : `Move-file failed: destination parent directory does not exist. Use mk_dir first.${toParentSymlinkSummary}`)}`,
753
+ ];
754
+ appendSymlinkYamlFields(yamlLines, 'to_parent_path', toParentInfo);
755
+ return formatYamlCodeBlock(yamlLines.join('\n'));
649
756
  }
650
757
  const toExists = await promises_1.default
651
758
  .lstat(absTo)
@@ -660,7 +767,7 @@ exports.moveFileTool = {
660
767
  throw err;
661
768
  });
662
769
  if (toExists) {
663
- const yaml = [
770
+ const yamlLines = [
664
771
  `status: error`,
665
772
  `from: ${yamlQuote(from)}`,
666
773
  `to: ${yamlQuote(to)}`,
@@ -668,17 +775,21 @@ exports.moveFileTool = {
668
775
  `summary: ${yamlQuote(workLanguage === 'zh'
669
776
  ? 'Move-file failed: destination already exists.'
670
777
  : 'Move-file failed: destination already exists.')}`,
671
- ].join('\n');
672
- return formatYamlCodeBlock(yaml);
778
+ ];
779
+ appendSymlinkYamlFields(yamlLines, 'from_path', fromInfo);
780
+ appendSymlinkYamlFields(yamlLines, 'to_parent_path', toParentInfo);
781
+ return formatYamlCodeBlock(yamlLines.join('\n'));
673
782
  }
674
783
  await promises_1.default.rename(absFrom, absTo);
675
- const yaml = [
784
+ const yamlLines = [
676
785
  `status: ok`,
677
786
  `from: ${yamlQuote(from)}`,
678
787
  `to: ${yamlQuote(to)}`,
679
- `summary: ${yamlQuote(`Move-file: ${from} \u2192 ${to}.`)}`,
680
- ].join('\n');
681
- return formatYamlCodeBlock(yaml);
788
+ `summary: ${yamlQuote(`Move-file: ${from} \u2192 ${to}.${fromSymlinkSummary}${toParentSymlinkSummary}`)}`,
789
+ ];
790
+ appendSymlinkYamlFields(yamlLines, 'from_path', fromInfo);
791
+ appendSymlinkYamlFields(yamlLines, 'to_parent_path', toParentInfo);
792
+ return formatYamlCodeBlock(yamlLines.join('\n'));
682
793
  }
683
794
  catch (error) {
684
795
  const yaml = [
@@ -745,32 +856,40 @@ exports.moveDirTool = {
745
856
  return (0, access_control_1.getAccessDeniedMessage)('write', from, workLanguage);
746
857
  }
747
858
  try {
748
- const st = await promises_1.default.lstat(absFrom);
749
- if (!st.isDirectory()) {
750
- const yaml = [
859
+ const fromInfo = await statWithSymlinkInfo(absFrom);
860
+ const fromSymlinkSummary = fromInfo.isSymlink
861
+ ? ` ${symlinkFollowNotice(workLanguage, from, fromInfo.symlinkTarget, 'dir')}`
862
+ : '';
863
+ if (!fromInfo.followStat.isDirectory()) {
864
+ const yamlLines = [
751
865
  `status: error`,
752
866
  `from: ${yamlQuote(from)}`,
753
867
  `to: ${yamlQuote(to)}`,
754
868
  `error: FROM_NOT_DIR`,
755
869
  `summary: ${yamlQuote(workLanguage === 'zh'
756
- ? 'Move-dir failed: from is not a directory.'
757
- : 'Move-dir failed: from is not a directory.')}`,
758
- ].join('\n');
759
- return formatYamlCodeBlock(yaml);
870
+ ? `Move-dir failed: from is not a directory.${fromSymlinkSummary}`
871
+ : `Move-dir failed: from is not a directory.${fromSymlinkSummary}`)}`,
872
+ ];
873
+ appendSymlinkYamlFields(yamlLines, 'from_path', fromInfo);
874
+ return formatYamlCodeBlock(yamlLines.join('\n'));
760
875
  }
761
876
  const toParent = path_1.default.dirname(absTo);
762
- const toParentSt = await promises_1.default.lstat(toParent).catch(() => undefined);
763
- if (!toParentSt || !toParentSt.isDirectory()) {
764
- const yaml = [
877
+ const toParentInfo = await statWithSymlinkInfo(toParent).catch(() => undefined);
878
+ const toParentSymlinkSummary = toParentInfo && toParentInfo.isSymlink
879
+ ? ` ${symlinkFollowNotice(workLanguage, path_1.default.relative(cwd, toParent) || '.', toParentInfo.symlinkTarget, 'dir')}`
880
+ : '';
881
+ if (!toParentInfo || !toParentInfo.followStat.isDirectory()) {
882
+ const yamlLines = [
765
883
  `status: error`,
766
884
  `from: ${yamlQuote(from)}`,
767
885
  `to: ${yamlQuote(to)}`,
768
886
  `error: TO_PARENT_NOT_DIR`,
769
887
  `summary: ${yamlQuote(workLanguage === 'zh'
770
- ? 'Move-dir failed: destination parent directory does not exist. Use mk_dir first.'
771
- : 'Move-dir failed: destination parent directory does not exist. Use mk_dir first.')}`,
772
- ].join('\n');
773
- return formatYamlCodeBlock(yaml);
888
+ ? `Move-dir failed: destination parent directory does not exist. Use mk_dir first.${toParentSymlinkSummary}`
889
+ : `Move-dir failed: destination parent directory does not exist. Use mk_dir first.${toParentSymlinkSummary}`)}`,
890
+ ];
891
+ appendSymlinkYamlFields(yamlLines, 'to_parent_path', toParentInfo);
892
+ return formatYamlCodeBlock(yamlLines.join('\n'));
774
893
  }
775
894
  const toExists = await promises_1.default
776
895
  .lstat(absTo)
@@ -785,7 +904,7 @@ exports.moveDirTool = {
785
904
  throw err;
786
905
  });
787
906
  if (toExists) {
788
- const yaml = [
907
+ const yamlLines = [
789
908
  `status: error`,
790
909
  `from: ${yamlQuote(from)}`,
791
910
  `to: ${yamlQuote(to)}`,
@@ -793,19 +912,26 @@ exports.moveDirTool = {
793
912
  `summary: ${yamlQuote(workLanguage === 'zh'
794
913
  ? 'Move-dir failed: destination already exists.'
795
914
  : 'Move-dir failed: destination already exists.')}`,
796
- ].join('\n');
797
- return formatYamlCodeBlock(yaml);
915
+ ];
916
+ appendSymlinkYamlFields(yamlLines, 'from_path', fromInfo);
917
+ appendSymlinkYamlFields(yamlLines, 'to_parent_path', toParentInfo);
918
+ return formatYamlCodeBlock(yamlLines.join('\n'));
798
919
  }
799
- const movedEntryCount = await countDirEntries(absFrom);
920
+ const movedEntryCount = fromInfo.isSymlink ? 1 : await countDirEntries(absFrom);
800
921
  await promises_1.default.rename(absFrom, absTo);
801
- const yaml = [
922
+ const symlinkMoveNotice = fromInfo.isSymlink
923
+ ? ` ${symlinkRemovalNotice(workLanguage, from, fromInfo.symlinkTarget)}`
924
+ : '';
925
+ const yamlLines = [
802
926
  `status: ok`,
803
927
  `from: ${yamlQuote(from)}`,
804
928
  `to: ${yamlQuote(to)}`,
805
929
  `moved_entry_count: ${movedEntryCount}`,
806
- `summary: ${yamlQuote(`Move-dir: ${from} \u2192 ${to} (${movedEntryCount} entries).`)}`,
807
- ].join('\n');
808
- return formatYamlCodeBlock(yaml);
930
+ `summary: ${yamlQuote(`Move-dir: ${from} \u2192 ${to} (${movedEntryCount} entries).${fromSymlinkSummary}${toParentSymlinkSummary}${symlinkMoveNotice}`)}`,
931
+ ];
932
+ appendSymlinkYamlFields(yamlLines, 'from_path', fromInfo);
933
+ appendSymlinkYamlFields(yamlLines, 'to_parent_path', toParentInfo);
934
+ return formatYamlCodeBlock(yamlLines.join('\n'));
809
935
  }
810
936
  catch (error) {
811
937
  const yaml = [
@@ -51,14 +51,20 @@ The memory toolset uses a **path key-value storage** model:
51
51
  ### 1. Path Naming Conventions
52
52
 
53
53
  - Use descriptive paths: `project/architecture`, `user/preferences/language`
54
- - Avoid special characters: Do not include `/`, `\`, `*`, etc. in paths
55
- - Use hierarchical structure: Organize memory by topic, e.g., `project/todo`, `project/done`
54
+ - Use `/` to organize hierarchy (this is the recommended usage), with guardrails:
55
+ - Absolute paths are forbidden (must not start with `/`)
56
+ - Path traversal is forbidden (`..` is not allowed)
57
+ - Avoid `\\` (cross-platform readability; prefer `/`)
58
+ - Keep paths _flat_: prefer a small number of topic files rather than a deep directory tree.
56
59
 
57
60
  ### 2. Content Format
58
61
 
59
- - Keep content concise: Each memory should cover only one topic
60
- - Use structured format: Can use Markdown format to organize content
61
- - Regular cleanup: Periodically check and delete outdated memories
62
+ - Treat personal memory as a **carry-along stable-facts memo**: enable **0 ripgrep** startup within your scope.
63
+ - Store stable facts only: **anchor points (file/symbol) + 1-line meaning + key contracts/priorities (≤3)**.
64
+ - Fewer memory files is better: group facts that will be updated together into one file; avoid adding extra “directory-of-directory” layers.
65
+ - Do not store task progress or daily state here:
66
+ - Team-visible progress belongs in Taskdoc `progress`
67
+ - Short-term working set belongs in reminders
62
68
 
63
69
  ### 3. Usage Scenarios
64
70
 
@@ -66,6 +72,8 @@ The memory toolset uses a **path key-value storage** model:
66
72
  - **Context memory**: Save important information from conversation context
67
73
  - **Preference settings**: Save user preferences and configuration information
68
74
 
75
+ > Note: If you find yourself using personal memory to store “current progress of this run”, it likely belongs in Taskdoc `progress` or reminders instead.
76
+
69
77
  ## Relationship with Other Tools
70
78
 
71
79
  - **team_memory**: Team shared memory, visible to all members
@@ -30,12 +30,8 @@ Create new memory (when path does not exist).
30
30
 
31
31
  **Returns:**
32
32
 
33
- ```yaml
34
- status: ok|error
35
- path: <memory path>
36
- content_size: <content size in bytes>
37
- created_at: <creation timestamp>
38
- ```
33
+ - Success: a short plain-text message in the work language (e.g. `Added`).
34
+ - Failure: a plain-text error message (often starts with `Error:`) with an actionable next step (e.g. use `replace_memory`).
39
35
 
40
36
  **Errors:**
41
37
 
@@ -52,12 +48,8 @@ Update existing memory (when path exists).
52
48
 
53
49
  **Returns:**
54
50
 
55
- ```yaml
56
- status: ok|error
57
- path: <memory path>
58
- content_size: <content size in bytes>
59
- updated_at: <update timestamp>
60
- ```
51
+ - Success: a short plain-text message in the work language (e.g. `Updated`).
52
+ - Failure: a plain-text error message (often starts with `Error:`) with an actionable next step (e.g. use `add_memory`).
61
53
 
62
54
  **Errors:**
63
55
 
@@ -73,11 +65,8 @@ Delete specified memory.
73
65
 
74
66
  **Returns:**
75
67
 
76
- ```yaml
77
- status: ok|error
78
- path: <memory path>
79
- deleted_at: <deletion timestamp>
80
- ```
68
+ - Success: a short plain-text message in the work language (e.g. `Deleted`).
69
+ - Failure: a plain-text error message (often starts with `Error:`).
81
70
 
82
71
  **Errors:**
83
72
 
@@ -93,11 +82,8 @@ Clear all personal memory.
93
82
 
94
83
  **Returns:**
95
84
 
96
- ```yaml
97
- status: ok|error
98
- cleared_count: <number of memories deleted>
99
- cleared_at: <deletion timestamp>
100
- ```
85
+ - Success: a short plain-text message in the work language (e.g. `Cleared`). If there is nothing to clear, returns a message like `No personal memory to clear.`
86
+ - Failure: a plain-text error message.
101
87
 
102
88
  **Errors:**
103
89
 
@@ -137,18 +123,7 @@ drop_memory({
137
123
  clear_memory({});
138
124
  ```
139
125
 
140
- ## YAML Output Contract
126
+ ## Output and Language
141
127
 
142
- All tool outputs use YAML format for programmatic processing:
143
-
144
- - `status`: Operation status, `ok` for success, `error` for failure
145
- - `path`: Memory path
146
- - Other fields: Additional information for specific operations
147
-
148
- On error, returns:
149
-
150
- ```yaml
151
- status: error
152
- error_code: <error code>
153
- message: <error message>
154
- ```
128
+ - Output is **plain text**, not structured JSON/YAML.
129
+ - The message language follows the current **work language**.
@@ -51,14 +51,20 @@ memory 工具集采用**路径键值存储**模型:
51
51
  ### 1. 路径命名规范
52
52
 
53
53
  - 使用描述性路径:`project/architecture`、`user/preferences/language`
54
- - 避免使用特殊字符:路径中不要包含 `/`、`\`、`*`
55
- - 使用层级结构:按主题组织记忆,例如 `project/todo`、`project/done`
54
+ - 允许用 `/` 组织层级(这就是推荐用法),但必须遵守护栏:
55
+ - 不允许绝对路径(不能以 `/` 开头)
56
+ - 不允许路径穿越(`..` 禁止)
57
+ - 避免使用 `\\`(跨平台可读性差,统一用 `/`)
58
+ - 保持路径扁平:优先少量主题文件,而不是深层目录树(个人记忆的目标是“快速索引”,不是建文件系统)。
56
59
 
57
60
  ### 2. 内容格式
58
61
 
59
- - 保持内容简洁:每条记忆只包含一个主题
60
- - 使用结构化格式:可以使用 Markdown 格式组织内容
61
- - 定期清理:定期检查并删除过时的记忆
62
+ - 把个人记忆当作“长期随身携带的稳定事实备忘”:让你在职责范围内尽量做到 **0 次 ripgrep 就能开工**。
63
+ - 内容只写稳定事实:**落点(文件/符号)+ 一句话语义 + 关键契约/优先级(≤3 条)**。
64
+ - 记忆文件更少更好:按“未来会一起更新的内容”合并在同一文件里;不要为了“看起来整齐”引入额外的“目录套目录”。
65
+ - 不要把任务进度/当天状态写进个人记忆:
66
+ - 任务协作公告板用差遣牒(Taskdoc 的 `progress`)
67
+ - 短期工作集用提醒项(reminders)
62
68
 
63
69
  ### 3. 使用场景
64
70
 
@@ -66,6 +72,8 @@ memory 工具集采用**路径键值存储**模型:
66
72
  - **上下文记忆**:保存对话上下文中的重要信息
67
73
  - **偏好设置**:保存用户偏好和配置信息
68
74
 
75
+ > 注:如果你发现自己在用个人记忆保存“这次对话的当前进展”,大概率应该写到 Taskdoc `progress` 或 reminders。
76
+
69
77
  ## 与其他工具的关系
70
78
 
71
79
  - **team_memory**:团队共享记忆,所有成员可见
@@ -74,6 +82,8 @@ memory 工具集采用**路径键值存储**模型:
74
82
 
75
83
  ## 限制与注意事项
76
84
 
77
- 1. 记忆内容有大小限制(单条记忆最大 1MB)
78
- 2. 记忆路径不能超过 255 个字符
79
- 3. `clear_memory` 会删除所有记忆,**不可恢复**
85
+ 1. 路径护栏:不允许绝对路径;不允许包含 `..`。
86
+ 2. 内容护栏:`content` 不能为空字符串。
87
+ 3. `clear_memory` 会删除当前智能体的全部个人记忆,**不可恢复**。
88
+
89
+ > 建议:即使当前实现没有硬性 size 限制,也应保持记忆内容精炼,否则会显著增加上下文成本与过期风险。