pybao-cli 1.4.37 → 1.4.39

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/REPL-N62XVC35.js +47 -0
  2. package/dist/{acp-4DJD7KGT.js → acp-6LBQJVMX.js} +29 -29
  3. package/dist/{agentsValidate-V3NT6CXM.js → agentsValidate-SLBW2ESZ.js} +7 -7
  4. package/dist/{ask-QGVRR66V.js → ask-DB4F3WJG.js} +28 -28
  5. package/dist/{autoUpdater-JM7WBEUQ.js → autoUpdater-CYDJVIEC.js} +3 -3
  6. package/dist/{chunk-BCQJMOSS.js → chunk-3LNPU7WH.js} +3 -3
  7. package/dist/{chunk-5EVKZGDP.js → chunk-4LXZDGCO.js} +3 -3
  8. package/dist/{chunk-CWSAGAMM.js → chunk-6KSM5M7T.js} +1 -1
  9. package/dist/{chunk-42XD5HC6.js → chunk-7TGWBYFN.js} +4 -4
  10. package/dist/{chunk-JBJ6V6LD.js → chunk-7WCSSH3C.js} +3 -3
  11. package/dist/chunk-AI7FJQR4.js +1262 -0
  12. package/dist/chunk-AI7FJQR4.js.map +7 -0
  13. package/dist/{chunk-3PUKLCTC.js → chunk-DEYAAT6H.js} +3 -3
  14. package/dist/{chunk-A6CGJVI5.js → chunk-DLBWS3IW.js} +1 -1
  15. package/dist/{chunk-VLPRSVXT.js → chunk-E5EB22W4.js} +4 -4
  16. package/dist/{chunk-C5PFCHI6.js → chunk-GD43B2GV.js} +1 -1
  17. package/dist/{chunk-U3BHVYM7.js → chunk-GL3ZM7F3.js} +3 -3
  18. package/dist/{chunk-WZFW5MLS.js → chunk-GXHSGPIF.js} +4 -4
  19. package/dist/{chunk-EAYSPMFV.js → chunk-HD4IO3I5.js} +783 -19
  20. package/dist/chunk-HD4IO3I5.js.map +7 -0
  21. package/dist/{chunk-N4AX7MAG.js → chunk-KNLFFHBT.js} +2 -2
  22. package/dist/{chunk-R5K32NWJ.js → chunk-KWPH7MSB.js} +1 -1
  23. package/dist/{chunk-QTT5PAF5.js → chunk-N4GXGPVA.js} +1 -1
  24. package/dist/{chunk-VNOOSZ37.js → chunk-NMJYV37P.js} +2 -2
  25. package/dist/{chunk-N73EEVKP.js → chunk-NNE44UHQ.js} +2 -2
  26. package/dist/{chunk-ADVVJQQL.js → chunk-O5OT26P6.js} +1 -1
  27. package/dist/{chunk-LKVOJGOX.js → chunk-RS2N2KBH.js} +753 -379
  28. package/dist/{chunk-LKVOJGOX.js.map → chunk-RS2N2KBH.js.map} +4 -4
  29. package/dist/{chunk-S2J5L7W6.js → chunk-SBPVAFBQ.js} +2 -2
  30. package/dist/{chunk-AAN4ZB7D.js → chunk-SSKQUKI7.js} +1 -1
  31. package/dist/{chunk-XQQXJDZ6.js → chunk-TNAAGT3Q.js} +3 -3
  32. package/dist/chunk-TNAAGT3Q.js.map +7 -0
  33. package/dist/{chunk-N3P77GAY.js → chunk-UK4IHZU3.js} +2 -2
  34. package/dist/{chunk-T2TNKXYE.js → chunk-UUWI4VWU.js} +1 -1
  35. package/dist/{chunk-T2TNKXYE.js.map → chunk-UUWI4VWU.js.map} +1 -1
  36. package/dist/{chunk-3UUOVYVA.js → chunk-VFQU3JSH.js} +4 -4
  37. package/dist/{chunk-2SXOBJO3.js → chunk-VHTIDAOJ.js} +2 -2
  38. package/dist/{chunk-W3I3SWXY.js → chunk-VXQMCAPS.js} +1 -1
  39. package/dist/{chunk-E7P4TM6T.js → chunk-YJ2WXW2J.js} +3 -3
  40. package/dist/{chunk-YA6QWTQZ.js → chunk-ZGVGFR4C.js} +1 -1
  41. package/dist/{cli-FVTA2UNT.js → cli-FMCSS5QE.js} +87 -87
  42. package/dist/commands-IJTOU4P3.js +51 -0
  43. package/dist/{config-PDX3ENJS.js → config-YRHKZJO6.js} +4 -4
  44. package/dist/{context-QCXL3UHB.js → context-ZZO64MRN.js} +6 -6
  45. package/dist/{customCommands-LIL44EIW.js → customCommands-2S7LYMG7.js} +4 -4
  46. package/dist/{env-JKAUQPAX.js → env-PQDRZBAW.js} +2 -2
  47. package/dist/{file-OKOAVL6I.js → file-X7BE3MNR.js} +4 -4
  48. package/dist/index.js +3 -3
  49. package/dist/{llm-24B7GPL4.js → llm-3KCTIVOM.js} +29 -29
  50. package/dist/{llmLazy-J4YVR6MG.js → llmLazy-HCTXTHRZ.js} +1 -1
  51. package/dist/{loader-GUY3VKKS.js → loader-DKMJSH4V.js} +4 -4
  52. package/dist/{lsp-36PBULHA.js → lsp-IXEAYT4K.js} +6 -6
  53. package/dist/{lspAnchor-VW755MJ7.js → lspAnchor-FKXOKCQZ.js} +6 -6
  54. package/dist/{mcp-5CYKIC44.js → mcp-ZUJD3E4V.js} +7 -7
  55. package/dist/{mentionProcessor-WTPRNMI7.js → mentionProcessor-7GXI47P3.js} +7 -5
  56. package/dist/{mentionProcessor-WTPRNMI7.js.map → mentionProcessor-7GXI47P3.js.map} +1 -1
  57. package/dist/{messages-35GUJR23.js → messages-55HNRTQC.js} +1 -1
  58. package/dist/{model-Z22IGTCI.js → model-R5X6XP7I.js} +5 -5
  59. package/dist/{openai-IFMVI4HW.js → openai-FM7DX3GB.js} +5 -5
  60. package/dist/{outputStyles-GISTOUSR.js → outputStyles-ZLGIDCCJ.js} +4 -4
  61. package/dist/{pluginRuntime-YKAYG7LI.js → pluginRuntime-REF3E5F5.js} +6 -6
  62. package/dist/{pluginValidation-WMUMEOFR.js → pluginValidation-E33USDYW.js} +6 -6
  63. package/dist/prompts-W7TUXY74.js +53 -0
  64. package/dist/{pybAgentSessionLoad-U2PAPG2T.js → pybAgentSessionLoad-PGTH6SZH.js} +4 -4
  65. package/dist/{pybAgentSessionResume-VKO7BF5R.js → pybAgentSessionResume-IVBY65PN.js} +4 -4
  66. package/dist/{pybAgentStreamJsonSession-3B3FBBV2.js → pybAgentStreamJsonSession-N2Q6LQHX.js} +1 -1
  67. package/dist/{pybHooks-PKSWIXQR.js → pybHooks-WIA5KKVN.js} +4 -4
  68. package/dist/query-BTG2T7NV.js +55 -0
  69. package/dist/{registry-ZEALEPQA.js → registry-PT3XVAT6.js} +5 -5
  70. package/dist/{ripgrep-KLZFIFVE.js → ripgrep-2VWCSPH4.js} +3 -3
  71. package/dist/{skillMarketplace-GTMGSI2S.js → skillMarketplace-NHPAPZTS.js} +3 -3
  72. package/dist/{state-A5SI47U2.js → state-4AIVX6VM.js} +2 -2
  73. package/dist/{theme-VS7GUKLU.js → theme-LKZFY5TU.js} +5 -5
  74. package/dist/{toolPermissionSettings-ZF4WVDXG.js → toolPermissionSettings-OVFT6D3R.js} +6 -6
  75. package/dist/tools-6ONPKOSL.js +52 -0
  76. package/dist/{userInput-VZEUNZ4X.js → userInput-RHGZ225B.js} +30 -30
  77. package/package.json +1 -1
  78. package/dist/REPL-XHCMVEGW.js +0 -47
  79. package/dist/chunk-5N7MDY6T.js +0 -517
  80. package/dist/chunk-5N7MDY6T.js.map +0 -7
  81. package/dist/chunk-EAYSPMFV.js.map +0 -7
  82. package/dist/chunk-XQQXJDZ6.js.map +0 -7
  83. package/dist/commands-ZXIPHHLY.js +0 -51
  84. package/dist/prompts-ANODLYMR.js +0 -53
  85. package/dist/query-E6AUJBMK.js +0 -55
  86. package/dist/tools-VTVBSLQ5.js +0 -52
  87. /package/dist/{REPL-XHCMVEGW.js.map → REPL-N62XVC35.js.map} +0 -0
  88. /package/dist/{acp-4DJD7KGT.js.map → acp-6LBQJVMX.js.map} +0 -0
  89. /package/dist/{agentsValidate-V3NT6CXM.js.map → agentsValidate-SLBW2ESZ.js.map} +0 -0
  90. /package/dist/{ask-QGVRR66V.js.map → ask-DB4F3WJG.js.map} +0 -0
  91. /package/dist/{autoUpdater-JM7WBEUQ.js.map → autoUpdater-CYDJVIEC.js.map} +0 -0
  92. /package/dist/{chunk-BCQJMOSS.js.map → chunk-3LNPU7WH.js.map} +0 -0
  93. /package/dist/{chunk-5EVKZGDP.js.map → chunk-4LXZDGCO.js.map} +0 -0
  94. /package/dist/{chunk-CWSAGAMM.js.map → chunk-6KSM5M7T.js.map} +0 -0
  95. /package/dist/{chunk-42XD5HC6.js.map → chunk-7TGWBYFN.js.map} +0 -0
  96. /package/dist/{chunk-JBJ6V6LD.js.map → chunk-7WCSSH3C.js.map} +0 -0
  97. /package/dist/{chunk-3PUKLCTC.js.map → chunk-DEYAAT6H.js.map} +0 -0
  98. /package/dist/{chunk-A6CGJVI5.js.map → chunk-DLBWS3IW.js.map} +0 -0
  99. /package/dist/{chunk-VLPRSVXT.js.map → chunk-E5EB22W4.js.map} +0 -0
  100. /package/dist/{chunk-C5PFCHI6.js.map → chunk-GD43B2GV.js.map} +0 -0
  101. /package/dist/{chunk-U3BHVYM7.js.map → chunk-GL3ZM7F3.js.map} +0 -0
  102. /package/dist/{chunk-WZFW5MLS.js.map → chunk-GXHSGPIF.js.map} +0 -0
  103. /package/dist/{chunk-N4AX7MAG.js.map → chunk-KNLFFHBT.js.map} +0 -0
  104. /package/dist/{chunk-R5K32NWJ.js.map → chunk-KWPH7MSB.js.map} +0 -0
  105. /package/dist/{chunk-QTT5PAF5.js.map → chunk-N4GXGPVA.js.map} +0 -0
  106. /package/dist/{chunk-VNOOSZ37.js.map → chunk-NMJYV37P.js.map} +0 -0
  107. /package/dist/{chunk-N73EEVKP.js.map → chunk-NNE44UHQ.js.map} +0 -0
  108. /package/dist/{chunk-ADVVJQQL.js.map → chunk-O5OT26P6.js.map} +0 -0
  109. /package/dist/{chunk-S2J5L7W6.js.map → chunk-SBPVAFBQ.js.map} +0 -0
  110. /package/dist/{chunk-AAN4ZB7D.js.map → chunk-SSKQUKI7.js.map} +0 -0
  111. /package/dist/{chunk-N3P77GAY.js.map → chunk-UK4IHZU3.js.map} +0 -0
  112. /package/dist/{chunk-3UUOVYVA.js.map → chunk-VFQU3JSH.js.map} +0 -0
  113. /package/dist/{chunk-2SXOBJO3.js.map → chunk-VHTIDAOJ.js.map} +0 -0
  114. /package/dist/{chunk-W3I3SWXY.js.map → chunk-VXQMCAPS.js.map} +0 -0
  115. /package/dist/{chunk-E7P4TM6T.js.map → chunk-YJ2WXW2J.js.map} +0 -0
  116. /package/dist/{chunk-YA6QWTQZ.js.map → chunk-ZGVGFR4C.js.map} +0 -0
  117. /package/dist/{cli-FVTA2UNT.js.map → cli-FMCSS5QE.js.map} +0 -0
  118. /package/dist/{commands-ZXIPHHLY.js.map → commands-IJTOU4P3.js.map} +0 -0
  119. /package/dist/{config-PDX3ENJS.js.map → config-YRHKZJO6.js.map} +0 -0
  120. /package/dist/{context-QCXL3UHB.js.map → context-ZZO64MRN.js.map} +0 -0
  121. /package/dist/{customCommands-LIL44EIW.js.map → customCommands-2S7LYMG7.js.map} +0 -0
  122. /package/dist/{env-JKAUQPAX.js.map → env-PQDRZBAW.js.map} +0 -0
  123. /package/dist/{file-OKOAVL6I.js.map → file-X7BE3MNR.js.map} +0 -0
  124. /package/dist/{llm-24B7GPL4.js.map → llm-3KCTIVOM.js.map} +0 -0
  125. /package/dist/{llmLazy-J4YVR6MG.js.map → llmLazy-HCTXTHRZ.js.map} +0 -0
  126. /package/dist/{loader-GUY3VKKS.js.map → loader-DKMJSH4V.js.map} +0 -0
  127. /package/dist/{lsp-36PBULHA.js.map → lsp-IXEAYT4K.js.map} +0 -0
  128. /package/dist/{lspAnchor-VW755MJ7.js.map → lspAnchor-FKXOKCQZ.js.map} +0 -0
  129. /package/dist/{mcp-5CYKIC44.js.map → mcp-ZUJD3E4V.js.map} +0 -0
  130. /package/dist/{messages-35GUJR23.js.map → messages-55HNRTQC.js.map} +0 -0
  131. /package/dist/{model-Z22IGTCI.js.map → model-R5X6XP7I.js.map} +0 -0
  132. /package/dist/{openai-IFMVI4HW.js.map → openai-FM7DX3GB.js.map} +0 -0
  133. /package/dist/{outputStyles-GISTOUSR.js.map → outputStyles-ZLGIDCCJ.js.map} +0 -0
  134. /package/dist/{pluginRuntime-YKAYG7LI.js.map → pluginRuntime-REF3E5F5.js.map} +0 -0
  135. /package/dist/{pluginValidation-WMUMEOFR.js.map → pluginValidation-E33USDYW.js.map} +0 -0
  136. /package/dist/{prompts-ANODLYMR.js.map → prompts-W7TUXY74.js.map} +0 -0
  137. /package/dist/{pybAgentSessionLoad-U2PAPG2T.js.map → pybAgentSessionLoad-PGTH6SZH.js.map} +0 -0
  138. /package/dist/{pybAgentSessionResume-VKO7BF5R.js.map → pybAgentSessionResume-IVBY65PN.js.map} +0 -0
  139. /package/dist/{pybAgentStreamJsonSession-3B3FBBV2.js.map → pybAgentStreamJsonSession-N2Q6LQHX.js.map} +0 -0
  140. /package/dist/{pybHooks-PKSWIXQR.js.map → pybHooks-WIA5KKVN.js.map} +0 -0
  141. /package/dist/{query-E6AUJBMK.js.map → query-BTG2T7NV.js.map} +0 -0
  142. /package/dist/{registry-ZEALEPQA.js.map → registry-PT3XVAT6.js.map} +0 -0
  143. /package/dist/{ripgrep-KLZFIFVE.js.map → ripgrep-2VWCSPH4.js.map} +0 -0
  144. /package/dist/{skillMarketplace-GTMGSI2S.js.map → skillMarketplace-NHPAPZTS.js.map} +0 -0
  145. /package/dist/{state-A5SI47U2.js.map → state-4AIVX6VM.js.map} +0 -0
  146. /package/dist/{theme-VS7GUKLU.js.map → theme-LKZFY5TU.js.map} +0 -0
  147. /package/dist/{toolPermissionSettings-ZF4WVDXG.js.map → toolPermissionSettings-OVFT6D3R.js.map} +0 -0
  148. /package/dist/{tools-VTVBSLQ5.js.map → tools-6ONPKOSL.js.map} +0 -0
  149. /package/dist/{userInput-VZEUNZ4X.js.map → userInput-RHGZ225B.js.map} +0 -0
@@ -0,0 +1,1262 @@
1
+ import { createRequire as __pybCreateRequire } from "node:module";
2
+ const require = __pybCreateRequire(import.meta.url);
3
+ import {
4
+ getSessionState,
5
+ setSessionState
6
+ } from "./chunk-ERMQRV55.js";
7
+ import {
8
+ getCurrentProjectConfig,
9
+ getGlobalConfig
10
+ } from "./chunk-TNAAGT3Q.js";
11
+ import {
12
+ debug
13
+ } from "./chunk-O5OT26P6.js";
14
+ import {
15
+ getCwd,
16
+ logError,
17
+ resolveXdgDataPath
18
+ } from "./chunk-ZGVGFR4C.js";
19
+
20
+ // src/utils/agent/storage.ts
21
+ import { existsSync, readFileSync, writeFileSync, mkdirSync } from "fs";
22
+ import { join } from "path";
23
+ import { randomUUID } from "crypto";
24
+ function getConfigDirectory() {
25
+ const override = process.env.ANYKODE_CONFIG_DIR?.trim();
26
+ if (override) return override;
27
+ return resolveXdgDataPath("agents");
28
+ }
29
+ function getSessionId() {
30
+ return process.env.ANYKODE_SESSION_ID ?? "default-session";
31
+ }
32
+ function getAgentFilePath(agentId) {
33
+ const sessionId = getSessionId();
34
+ const filename = `${sessionId}-agent-${agentId}.json`;
35
+ const configDir = getConfigDirectory();
36
+ if (!existsSync(configDir)) {
37
+ mkdirSync(configDir, { recursive: true });
38
+ }
39
+ return join(configDir, filename);
40
+ }
41
+ function readAgentData(agentId) {
42
+ const filePath = getAgentFilePath(agentId);
43
+ if (!existsSync(filePath)) {
44
+ return null;
45
+ }
46
+ try {
47
+ const content = readFileSync(filePath, "utf-8");
48
+ return JSON.parse(content);
49
+ } catch (error) {
50
+ logError(error);
51
+ debug.warn("AGENT_STORAGE_READ_FAILED", {
52
+ agentId,
53
+ error: error instanceof Error ? error.message : String(error)
54
+ });
55
+ return null;
56
+ }
57
+ }
58
+ function writeAgentData(agentId, data) {
59
+ const filePath = getAgentFilePath(agentId);
60
+ try {
61
+ writeFileSync(filePath, JSON.stringify(data, null, 2), "utf-8");
62
+ } catch (error) {
63
+ logError(error);
64
+ debug.warn("AGENT_STORAGE_WRITE_FAILED", {
65
+ agentId,
66
+ error: error instanceof Error ? error.message : String(error)
67
+ });
68
+ throw error;
69
+ }
70
+ }
71
+ function getDefaultAgentId() {
72
+ return "default";
73
+ }
74
+ function resolveAgentId(agentId) {
75
+ return agentId || getDefaultAgentId();
76
+ }
77
+ function generateAgentId() {
78
+ return randomUUID();
79
+ }
80
+
81
+ // src/utils/session/taskStore.ts
82
+ import {
83
+ existsSync as existsSync2,
84
+ mkdirSync as mkdirSync2,
85
+ readFileSync as readFileSync2,
86
+ readdirSync,
87
+ rmSync,
88
+ writeFileSync as writeFileSync2
89
+ } from "fs";
90
+ import { join as join2 } from "path";
91
+ import { homedir } from "os";
92
+ import { createHash } from "crypto";
93
+ var DEFAULT_LOCK_TTL_MS = 6e4;
94
+ function buildActiveForm(subject) {
95
+ return `Working on ${subject}`;
96
+ }
97
+ var TaskStoreLockError = class extends Error {
98
+ code = "TASK_STORE_LOCKED";
99
+ constructor(message) {
100
+ super(message);
101
+ this.name = "TaskStoreLockError";
102
+ }
103
+ };
104
+ var TaskStoreConflictError = class extends Error {
105
+ code = "TASK_STORE_CONFLICT";
106
+ taskId;
107
+ expectedBaseVersion;
108
+ actualBaseVersion;
109
+ constructor(params) {
110
+ super(
111
+ `Task ${params.taskId} baseVersion ${params.actual} does not match expected ${params.expected}`
112
+ );
113
+ this.name = "TaskStoreConflictError";
114
+ this.taskId = params.taskId;
115
+ this.expectedBaseVersion = params.expected;
116
+ this.actualBaseVersion = params.actual;
117
+ }
118
+ };
119
+ function getDefaultTaskListId(cwd) {
120
+ return createHash("sha256").update(cwd).digest("hex").slice(0, 12);
121
+ }
122
+ function resolveGlobalTaskRootDir() {
123
+ const override = process.env.PYB_CONFIG_DIR ?? process.env.CLAUDE_CONFIG_DIR;
124
+ if (override) return override;
125
+ const home = homedir();
126
+ const pybDir = join2(home, ".pyb");
127
+ if (existsSync2(pybDir)) return pybDir;
128
+ const claudeDir = join2(home, ".claude");
129
+ if (existsSync2(claudeDir)) return claudeDir;
130
+ return pybDir;
131
+ }
132
+ function findBaseDirWithList(listId, candidates) {
133
+ for (const candidate of candidates) {
134
+ if (existsSync2(join2(candidate, listId))) return candidate;
135
+ }
136
+ return null;
137
+ }
138
+ function getTaskListContext(options) {
139
+ const env = options?.env ?? process.env;
140
+ const globalConfig = options?.globalConfig ?? getGlobalConfig();
141
+ const projectConfig = options?.projectConfig ?? getCurrentProjectConfig();
142
+ const cwd = options?.cwd ?? getCwd();
143
+ const defaultId = getDefaultTaskListId(cwd);
144
+ const inputListId = options?.listId?.trim();
145
+ const envId = env.PYB_TASK_LIST_ID?.trim() || env.CLAUDE_CODE_TASK_LIST_ID?.trim();
146
+ const globalId = globalConfig?.taskListId?.trim();
147
+ const projectId = projectConfig?.taskListId?.trim();
148
+ if (inputListId) {
149
+ if (inputListId === projectId || inputListId === defaultId) {
150
+ return { listId: inputListId, scope: "project", source: "input" };
151
+ }
152
+ if (inputListId === globalId || inputListId === envId) {
153
+ return { listId: inputListId, scope: "global", source: "input" };
154
+ }
155
+ return { listId: inputListId, scope: "project", source: "input" };
156
+ }
157
+ if (envId) return { listId: envId, scope: "global", source: "env" };
158
+ if (globalId) return { listId: globalId, scope: "global", source: "global" };
159
+ if (projectId) return { listId: projectId, scope: "project", source: "project" };
160
+ return { listId: defaultId, scope: "project", source: "default" };
161
+ }
162
+ function resolveTaskBaseDir(context, cwd) {
163
+ if (context.scope === "project") {
164
+ const pybDir = join2(cwd, ".pyb");
165
+ const claudeDir = join2(cwd, ".claude");
166
+ const hasProjectPyb = existsSync2(pybDir);
167
+ const hasProjectClaude = existsSync2(claudeDir);
168
+ const projectRoot = hasProjectPyb ? pybDir : hasProjectClaude ? claudeDir : pybDir;
169
+ const override2 = process.env.PYB_CONFIG_DIR ?? process.env.CLAUDE_CONFIG_DIR;
170
+ const defaultBaseDir2 = !hasProjectPyb && !hasProjectClaude && override2 ? join2(override2, "tasks") : join2(projectRoot, "tasks");
171
+ const candidates2 = [
172
+ join2(cwd, ".pyb", "tasks"),
173
+ join2(cwd, ".claude", "tasks"),
174
+ ...override2 ? [join2(override2, "tasks")] : [],
175
+ resolveXdgDataPath("tasks")
176
+ ];
177
+ return findBaseDirWithList(context.listId, candidates2) ?? defaultBaseDir2;
178
+ }
179
+ const override = process.env.PYB_CONFIG_DIR ?? process.env.CLAUDE_CONFIG_DIR;
180
+ const globalCandidates = override ? [join2(override, "tasks")] : [
181
+ join2(homedir(), ".pyb", "tasks"),
182
+ join2(homedir(), ".claude", "tasks")
183
+ ];
184
+ const defaultBaseDir = join2(resolveGlobalTaskRootDir(), "tasks");
185
+ const candidates = [...globalCandidates, resolveXdgDataPath("tasks")];
186
+ return findBaseDirWithList(context.listId, candidates) ?? defaultBaseDir;
187
+ }
188
+ function getTaskListPaths(listId) {
189
+ const context = getTaskListContext({ listId });
190
+ const cwd = getCwd();
191
+ const baseDir = resolveTaskBaseDir(context, cwd);
192
+ const listDir = join2(baseDir, context.listId);
193
+ return {
194
+ listId: context.listId,
195
+ baseDir,
196
+ listDir,
197
+ tasksDir: listDir,
198
+ indexPath: join2(listDir, "index.json"),
199
+ metaPath: join2(listDir, "meta.json"),
200
+ lockPath: join2(listDir, ".lock"),
201
+ eventsPath: join2(listDir, "events.jsonl")
202
+ };
203
+ }
204
+ function ensureTaskListDirs(paths) {
205
+ mkdirSync2(paths.listDir, { recursive: true });
206
+ }
207
+ function readJson(path) {
208
+ if (!existsSync2(path)) return null;
209
+ try {
210
+ const raw = readFileSync2(path, "utf8");
211
+ return JSON.parse(raw);
212
+ } catch {
213
+ return null;
214
+ }
215
+ }
216
+ function writeJson(path, value) {
217
+ writeFileSync2(path, JSON.stringify(value, null, 2), "utf8");
218
+ }
219
+ function appendTaskEvent(paths, event) {
220
+ ensureTaskListDirs(paths);
221
+ writeFileSync2(paths.eventsPath, `${JSON.stringify(event)}
222
+ `, {
223
+ encoding: "utf8",
224
+ flag: "a"
225
+ });
226
+ }
227
+ function readTaskEventLog(listId, offset = 0) {
228
+ const paths = getTaskListPaths(listId);
229
+ if (!existsSync2(paths.eventsPath)) {
230
+ return { events: [], nextOffset: 0 };
231
+ }
232
+ const buffer = readFileSync2(paths.eventsPath);
233
+ const nextOffset = buffer.length;
234
+ if (offset >= nextOffset) {
235
+ return { events: [], nextOffset };
236
+ }
237
+ const chunk = buffer.toString("utf8", offset);
238
+ const events = [];
239
+ for (const line of chunk.split("\n")) {
240
+ if (!line.trim()) continue;
241
+ try {
242
+ const parsed = JSON.parse(line);
243
+ if (parsed.listId && parsed.taskId && parsed.type && parsed.timestamp) {
244
+ events.push(parsed);
245
+ }
246
+ } catch {
247
+ continue;
248
+ }
249
+ }
250
+ return { events, nextOffset };
251
+ }
252
+ function readIndex(paths) {
253
+ const index = readJson(paths.indexPath);
254
+ if (index && index.nextId >= 1) return index;
255
+ return recoverIndex(paths);
256
+ }
257
+ function writeIndex(paths, index) {
258
+ writeJson(paths.indexPath, index);
259
+ }
260
+ function readMeta(paths) {
261
+ ensureTaskListDirs(paths);
262
+ const meta = readJson(paths.metaPath);
263
+ if (meta && meta.listId) return meta;
264
+ const now = Date.now();
265
+ const created = {
266
+ listId: paths.listId,
267
+ createdAt: now,
268
+ updatedAt: now
269
+ };
270
+ writeJson(paths.metaPath, created);
271
+ return created;
272
+ }
273
+ function writeMeta(paths, meta) {
274
+ ensureTaskListDirs(paths);
275
+ writeJson(paths.metaPath, meta);
276
+ }
277
+ function readTaskFile(path) {
278
+ const data = readJson(
279
+ path
280
+ );
281
+ if (!data || !data.id || !data.subject && !data.title || !data.status) {
282
+ return null;
283
+ }
284
+ const subject = data.subject ?? data.title;
285
+ const title = subject;
286
+ const activeForm = data.activeForm ?? buildActiveForm(subject);
287
+ const baseVersion = typeof data.baseVersion === "number" ? data.baseVersion : typeof data.version === "number" ? data.version : 1;
288
+ const version = typeof data.version === "number" ? data.version : baseVersion;
289
+ const createdAt = typeof data.createdAt === "number" ? data.createdAt : Date.now();
290
+ const updatedAt = typeof data.updatedAt === "number" ? data.updatedAt : createdAt;
291
+ const normalized = {
292
+ ...data,
293
+ subject,
294
+ title,
295
+ activeForm,
296
+ blocks: data.blocks ?? [],
297
+ blockedBy: data.blockedBy ?? [],
298
+ createdAt,
299
+ updatedAt,
300
+ baseVersion,
301
+ version
302
+ };
303
+ delete normalized.priority;
304
+ return normalized;
305
+ }
306
+ function recoverIndex(paths) {
307
+ ensureTaskListDirs(paths);
308
+ const files = readdirSync(paths.tasksDir).filter((file) => file.endsWith(".json"));
309
+ const tasks = files.map((file) => readTaskFile(join2(paths.tasksDir, file))).filter(Boolean);
310
+ const byStatus = {
311
+ open: 0,
312
+ in_progress: 0,
313
+ blocked: 0,
314
+ done: 0,
315
+ archived: 0
316
+ };
317
+ let maxId = 0;
318
+ for (const task of tasks) {
319
+ byStatus[task.status] += 1;
320
+ const numericId = Number(task.id);
321
+ if (!Number.isNaN(numericId)) {
322
+ maxId = Math.max(maxId, numericId);
323
+ }
324
+ }
325
+ const index = {
326
+ nextId: Math.max(1, maxId + 1),
327
+ total: tasks.length,
328
+ byStatus,
329
+ lastUpdated: Date.now()
330
+ };
331
+ writeIndex(paths, index);
332
+ return index;
333
+ }
334
+ function readLock(path) {
335
+ const lock = readJson(path);
336
+ if (!lock || !lock.createdAt || !lock.expiresAt) return null;
337
+ return lock;
338
+ }
339
+ function isLockExpired(lock) {
340
+ return lock.expiresAt <= Date.now();
341
+ }
342
+ function acquireLock(paths) {
343
+ ensureTaskListDirs(paths);
344
+ const lock = readLock(paths.lockPath);
345
+ if (lock && !isLockExpired(lock)) {
346
+ throw new TaskStoreLockError("Task list is locked");
347
+ }
348
+ if (lock && isLockExpired(lock)) {
349
+ rmSync(paths.lockPath, { force: true });
350
+ }
351
+ const now = Date.now();
352
+ try {
353
+ writeFileSync2(
354
+ paths.lockPath,
355
+ JSON.stringify(
356
+ {
357
+ createdAt: now,
358
+ expiresAt: now + DEFAULT_LOCK_TTL_MS
359
+ },
360
+ null,
361
+ 2
362
+ ),
363
+ { encoding: "utf8", flag: "wx" }
364
+ );
365
+ } catch {
366
+ throw new TaskStoreLockError("Task list is locked");
367
+ }
368
+ }
369
+ function releaseLock(paths) {
370
+ rmSync(paths.lockPath, { force: true });
371
+ }
372
+ function withTaskListLock(paths, handler) {
373
+ acquireLock(paths);
374
+ try {
375
+ return handler();
376
+ } finally {
377
+ releaseLock(paths);
378
+ }
379
+ }
380
+ function applyTaskUpdates(task, updates, nextBaseVersion) {
381
+ const now = updates.updatedAt ?? Date.now();
382
+ const subject = updates.subject ?? task.subject;
383
+ const title = subject;
384
+ const activeForm = updates.activeForm ?? (updates.subject ? buildActiveForm(subject) : task.activeForm ?? buildActiveForm(subject));
385
+ const blocks = updates.blocks ?? task.blocks ?? [];
386
+ const blockedBy = updates.blockedBy ?? task.blockedBy ?? [];
387
+ return {
388
+ ...task,
389
+ subject,
390
+ title,
391
+ description: updates.description ?? task.description,
392
+ activeForm,
393
+ status: updates.status ?? task.status,
394
+ tags: updates.tags ?? task.tags,
395
+ assignee: updates.assignee ?? task.assignee,
396
+ metadata: updates.metadata ?? task.metadata,
397
+ archived: updates.archived ?? task.archived,
398
+ blocks,
399
+ blockedBy,
400
+ parent: updates.parent ?? task.parent,
401
+ related: updates.related ?? task.related,
402
+ updatedAt: now,
403
+ baseVersion: nextBaseVersion,
404
+ version: nextBaseVersion,
405
+ legacyTodoId: updates.legacyTodoId ?? task.legacyTodoId
406
+ };
407
+ }
408
+ function createTask(input, options) {
409
+ const paths = getTaskListPaths(options?.listId);
410
+ return withTaskListLock(paths, () => {
411
+ ensureTaskListDirs(paths);
412
+ const index = readIndex(paths);
413
+ const id = String(index.nextId);
414
+ const now = Date.now();
415
+ const subject = input.subject;
416
+ if (!subject) {
417
+ throw new Error("Task subject is required");
418
+ }
419
+ const title = subject;
420
+ const activeForm = input.activeForm ?? buildActiveForm(subject);
421
+ const record = {
422
+ id,
423
+ subject,
424
+ title,
425
+ description: input.description,
426
+ activeForm,
427
+ status: input.status ?? "open",
428
+ tags: input.tags,
429
+ assignee: input.assignee,
430
+ metadata: input.metadata,
431
+ archived: input.archived,
432
+ blocks: input.blocks ?? [],
433
+ blockedBy: input.blockedBy ?? [],
434
+ parent: input.parent,
435
+ related: input.related,
436
+ createdAt: now,
437
+ updatedAt: now,
438
+ baseVersion: 1,
439
+ version: 1,
440
+ legacyTodoId: input.legacyTodoId
441
+ };
442
+ writeJson(join2(paths.tasksDir, `${id}.json`), record);
443
+ index.nextId += 1;
444
+ index.total += 1;
445
+ index.byStatus[record.status] += 1;
446
+ index.lastUpdated = Date.now();
447
+ writeIndex(paths, index);
448
+ const meta = readMeta(paths);
449
+ meta.updatedAt = Date.now();
450
+ writeMeta(paths, meta);
451
+ appendTaskEvent(paths, {
452
+ listId: paths.listId,
453
+ taskId: record.id,
454
+ type: "task.created",
455
+ timestamp: Date.now()
456
+ });
457
+ return record;
458
+ });
459
+ }
460
+ function getTask(id, options) {
461
+ const paths = getTaskListPaths(options?.listId);
462
+ const record = readTaskFile(join2(paths.tasksDir, `${id}.json`));
463
+ if (!record) return null;
464
+ return record;
465
+ }
466
+ function listTasks(options) {
467
+ const paths = getTaskListPaths(options?.listId);
468
+ readIndex(paths);
469
+ ensureTaskListDirs(paths);
470
+ const files = readdirSync(paths.tasksDir).filter((file) => file.endsWith(".json"));
471
+ const tasks = files.map((file) => readTaskFile(join2(paths.tasksDir, file))).filter(Boolean);
472
+ return tasks.sort((a, b) => Number(a.id) - Number(b.id));
473
+ }
474
+ function updateTask(id, updates, options) {
475
+ const paths = getTaskListPaths(options?.listId);
476
+ return withTaskListLock(paths, () => {
477
+ const existing = readTaskFile(join2(paths.tasksDir, `${id}.json`));
478
+ if (!existing) {
479
+ throw new Error(`Task ${id} not found`);
480
+ }
481
+ const requestedBase = updates.baseVersion;
482
+ const hasConflict = typeof requestedBase === "number" && requestedBase !== existing.baseVersion;
483
+ if (hasConflict && !options?.allowMerge) {
484
+ throw new TaskStoreConflictError({
485
+ taskId: id,
486
+ expected: requestedBase,
487
+ actual: existing.baseVersion
488
+ });
489
+ }
490
+ if (hasConflict && options?.allowMerge) {
491
+ const updateTimestamp = updates.updatedAt ?? Date.now();
492
+ if (updateTimestamp <= existing.updatedAt) {
493
+ return { task: existing, conflict: true };
494
+ }
495
+ }
496
+ const nextBaseVersion = existing.baseVersion + 1;
497
+ const updated = applyTaskUpdates(existing, updates, nextBaseVersion);
498
+ writeJson(join2(paths.tasksDir, `${id}.json`), updated);
499
+ const index = readIndex(paths);
500
+ if (updated.status !== existing.status) {
501
+ index.byStatus[existing.status] = Math.max(
502
+ 0,
503
+ index.byStatus[existing.status] - 1
504
+ );
505
+ index.byStatus[updated.status] += 1;
506
+ }
507
+ index.lastUpdated = Date.now();
508
+ writeIndex(paths, index);
509
+ const meta = readMeta(paths);
510
+ meta.updatedAt = Date.now();
511
+ writeMeta(paths, meta);
512
+ appendTaskEvent(paths, {
513
+ listId: paths.listId,
514
+ taskId: updated.id,
515
+ type: "task.updated",
516
+ timestamp: Date.now()
517
+ });
518
+ return { task: updated, conflict: hasConflict };
519
+ });
520
+ }
521
+ function getTaskListMeta(options) {
522
+ const paths = getTaskListPaths(options?.listId);
523
+ return readMeta(paths);
524
+ }
525
+ function updateTaskListMeta(meta, options) {
526
+ const paths = getTaskListPaths(options?.listId);
527
+ const updated = { ...meta, updatedAt: Date.now() };
528
+ writeMeta(paths, updated);
529
+ return updated;
530
+ }
531
+
532
+ // src/utils/session/todoStorage.ts
533
+ var TODO_STORAGE_KEY = "todos";
534
+ var TODO_CONFIG_KEY = "todoConfig";
535
+ var DEFAULT_CONFIG = {
536
+ maxTodos: 100,
537
+ autoArchiveCompleted: false,
538
+ sortBy: "status",
539
+ sortOrder: "desc"
540
+ };
541
+ var todoCache = null;
542
+ var cacheTimestamp = 0;
543
+ var CACHE_TTL = 5e3;
544
+ function isTodoCompatEnabled(env = process.env) {
545
+ const raw = String(
546
+ env.PYB_TODO_COMPAT ?? env.CLAUDE_CODE_TODO_COMPAT ?? ""
547
+ ).trim().toLowerCase();
548
+ if (!raw) return false;
549
+ if (["0", "false", "no", "off"].includes(raw)) return false;
550
+ if (["1", "true", "yes", "on"].includes(raw)) return true;
551
+ return true;
552
+ }
553
+ function toTaskStatus(status) {
554
+ if (status === "completed") return "done";
555
+ if (status === "in_progress") return "in_progress";
556
+ return "open";
557
+ }
558
+ function syncTodosToTasks(todos, options) {
559
+ const { listId } = getTaskListPaths(options?.listId);
560
+ const tasks = listTasks({ listId });
561
+ const byLegacyId = new Map(
562
+ tasks.filter((task) => task.legacyTodoId).map((task) => [task.legacyTodoId, task])
563
+ );
564
+ let created = 0;
565
+ let updated = 0;
566
+ for (const todo of todos) {
567
+ const existing = byLegacyId.get(todo.id);
568
+ if (!existing) {
569
+ createTask(
570
+ {
571
+ subject: todo.content,
572
+ description: todo.content,
573
+ activeForm: todo.activeForm,
574
+ status: toTaskStatus(todo.status),
575
+ legacyTodoId: todo.id
576
+ },
577
+ { listId }
578
+ );
579
+ created += 1;
580
+ } else {
581
+ updateTask(
582
+ existing.id,
583
+ {
584
+ subject: todo.content,
585
+ description: todo.content,
586
+ activeForm: todo.activeForm,
587
+ status: toTaskStatus(todo.status),
588
+ legacyTodoId: todo.id
589
+ },
590
+ { listId, allowMerge: true }
591
+ );
592
+ updated += 1;
593
+ }
594
+ }
595
+ return { listId, created, updated };
596
+ }
597
+ function migrateTodosToTasks(options) {
598
+ const { listId } = getTaskListPaths(options?.listId);
599
+ const meta = getTaskListMeta({ listId });
600
+ if (meta.todoMigration?.completedAt) {
601
+ return { listId, migrated: false, created: 0 };
602
+ }
603
+ const todos = getTodos(options?.agentId);
604
+ const result = syncTodosToTasks(todos, { listId });
605
+ updateTaskListMeta(
606
+ {
607
+ ...meta,
608
+ todoMigration: {
609
+ completedAt: Date.now(),
610
+ sourceCount: todos.length
611
+ }
612
+ },
613
+ { listId }
614
+ );
615
+ return { listId, migrated: true, created: result.created };
616
+ }
617
+ function invalidateCache() {
618
+ todoCache = null;
619
+ cacheTimestamp = 0;
620
+ }
621
+ function updateMetrics(operation, cacheHit = false) {
622
+ const sessionState = getSessionState();
623
+ const metrics = sessionState.todoMetrics || {
624
+ totalOperations: 0,
625
+ cacheHits: 0,
626
+ cacheMisses: 0,
627
+ lastOperation: 0
628
+ };
629
+ metrics.totalOperations++;
630
+ metrics.lastOperation = Date.now();
631
+ if (cacheHit) {
632
+ metrics.cacheHits++;
633
+ } else {
634
+ metrics.cacheMisses++;
635
+ }
636
+ setSessionState({
637
+ ...sessionState,
638
+ todoMetrics: metrics
639
+ });
640
+ }
641
+ function getTodos(agentId) {
642
+ const resolvedAgentId = resolveAgentId(agentId);
643
+ const now = Date.now();
644
+ if (agentId) {
645
+ updateMetrics("getTodos", false);
646
+ const agentTodos = readAgentData(resolvedAgentId) || [];
647
+ const agentCacheKey = `todoCache_${resolvedAgentId}`;
648
+ return agentTodos.map((todo) => ({
649
+ ...todo,
650
+ activeForm: todo.activeForm || todo.content
651
+ }));
652
+ }
653
+ if (todoCache && now - cacheTimestamp < CACHE_TTL) {
654
+ updateMetrics("getTodos", true);
655
+ return todoCache.map((todo) => ({
656
+ ...todo,
657
+ activeForm: todo.activeForm || todo.content
658
+ }));
659
+ }
660
+ updateMetrics("getTodos", false);
661
+ const sessionState = getSessionState();
662
+ const todos = sessionState[TODO_STORAGE_KEY] || [];
663
+ todoCache = [...todos].map((todo) => ({
664
+ ...todo,
665
+ activeForm: todo.activeForm || todo.content
666
+ }));
667
+ cacheTimestamp = now;
668
+ return todoCache;
669
+ }
670
+ function setTodos(todos, agentId) {
671
+ const resolvedAgentId = resolveAgentId(agentId);
672
+ const config = getTodoConfig();
673
+ const existingTodos = getTodos(agentId);
674
+ if (agentId) {
675
+ if (todos.length > config.maxTodos) {
676
+ throw new Error(
677
+ `Todo limit exceeded. Maximum ${config.maxTodos} todos allowed.`
678
+ );
679
+ }
680
+ let processedTodos2 = todos;
681
+ if (config.autoArchiveCompleted) {
682
+ processedTodos2 = todos.filter((todo) => todo.status !== "completed");
683
+ }
684
+ const updatedTodos2 = processedTodos2.map((todo) => {
685
+ const existingTodo = existingTodos.find(
686
+ (existing) => existing.id === todo.id
687
+ );
688
+ return {
689
+ ...todo,
690
+ activeForm: todo.activeForm || todo.content,
691
+ updatedAt: Date.now(),
692
+ createdAt: todo.createdAt || Date.now(),
693
+ previousStatus: existingTodo?.status !== todo.status ? existingTodo?.status : todo.previousStatus
694
+ };
695
+ });
696
+ writeAgentData(resolvedAgentId, updatedTodos2);
697
+ updateMetrics("setTodos");
698
+ return;
699
+ }
700
+ if (todos.length > config.maxTodos) {
701
+ throw new Error(
702
+ `Todo limit exceeded. Maximum ${config.maxTodos} todos allowed.`
703
+ );
704
+ }
705
+ let processedTodos = todos;
706
+ if (config.autoArchiveCompleted) {
707
+ processedTodos = todos.filter((todo) => todo.status !== "completed");
708
+ }
709
+ const updatedTodos = processedTodos.map((todo) => {
710
+ const existingTodo = existingTodos.find((existing) => existing.id === todo.id);
711
+ return {
712
+ ...todo,
713
+ activeForm: todo.activeForm || todo.content,
714
+ updatedAt: Date.now(),
715
+ createdAt: todo.createdAt || Date.now(),
716
+ previousStatus: existingTodo?.status !== todo.status ? existingTodo?.status : todo.previousStatus
717
+ };
718
+ });
719
+ setSessionState({
720
+ ...getSessionState(),
721
+ [TODO_STORAGE_KEY]: updatedTodos
722
+ });
723
+ invalidateCache();
724
+ updateMetrics("setTodos");
725
+ }
726
+ function getTodoConfig() {
727
+ const sessionState = getSessionState();
728
+ return { ...DEFAULT_CONFIG, ...sessionState[TODO_CONFIG_KEY] || {} };
729
+ }
730
+
731
+ // src/services/system/systemReminder.ts
732
+ var SystemReminderService = class {
733
+ sessionState = {
734
+ lastTodoUpdate: 0,
735
+ lastFileAccess: 0,
736
+ lastTaskUpdate: 0,
737
+ sessionStartTime: Date.now(),
738
+ remindersSent: /* @__PURE__ */ new Set(),
739
+ contextPresent: false,
740
+ reminderCount: 0,
741
+ taskEventTimestamps: /* @__PURE__ */ new Map(),
742
+ config: {
743
+ todoEmptyReminder: true,
744
+ securityReminder: true,
745
+ performanceReminder: true,
746
+ maxRemindersPerSession: 10
747
+ }
748
+ };
749
+ eventDispatcher = /* @__PURE__ */ new Map();
750
+ reminderCache = /* @__PURE__ */ new Map();
751
+ constructor() {
752
+ this.setupEventDispatcher();
753
+ }
754
+ generateReminders(hasContext = false, agentId) {
755
+ this.sessionState.contextPresent = hasContext;
756
+ if (!hasContext) {
757
+ return [];
758
+ }
759
+ if (this.sessionState.reminderCount >= this.sessionState.config.maxRemindersPerSession) {
760
+ return [];
761
+ }
762
+ const reminders = [];
763
+ const currentTime = Date.now();
764
+ const reminderGenerators = [
765
+ () => this.dispatchTodoEvent(agentId),
766
+ () => this.dispatchSecurityEvent(),
767
+ () => this.dispatchPerformanceEvent(),
768
+ () => this.getMentionReminders()
769
+ ];
770
+ for (const generator of reminderGenerators) {
771
+ if (reminders.length >= 5) break;
772
+ const result = generator();
773
+ if (result) {
774
+ const remindersToAdd = Array.isArray(result) ? result : [result];
775
+ reminders.push(...remindersToAdd);
776
+ this.sessionState.reminderCount += remindersToAdd.length;
777
+ }
778
+ }
779
+ return reminders;
780
+ }
781
+ dispatchTodoEvent(agentId) {
782
+ if (!this.sessionState.config.todoEmptyReminder) return null;
783
+ const todos = getTodos(agentId);
784
+ const currentTime = Date.now();
785
+ const agentKey = agentId || "default";
786
+ if (todos.length === 0 && !this.sessionState.remindersSent.has(`todo_empty_${agentKey}`)) {
787
+ this.sessionState.remindersSent.add(`todo_empty_${agentKey}`);
788
+ return this.createReminderMessage(
789
+ "todo",
790
+ "task",
791
+ "medium",
792
+ "This is a reminder that your task list is currently empty. DO NOT mention this to the user explicitly because they are already aware. If you are working on tasks that would benefit from a structured plan, use TaskCreate to seed tasks and TaskUpdate to track progress. Use TaskList to review readiness. If not, please feel free to ignore. Again do not mention this message to the user.",
793
+ currentTime
794
+ );
795
+ }
796
+ if (todos.length > 0) {
797
+ const reminderKey = `todo_updated_${agentKey}_${todos.length}_${this.getTodoStateHash(todos)}`;
798
+ if (this.reminderCache.has(reminderKey)) {
799
+ return this.reminderCache.get(reminderKey);
800
+ }
801
+ if (!this.sessionState.remindersSent.has(reminderKey)) {
802
+ this.sessionState.remindersSent.add(reminderKey);
803
+ this.clearTodoReminders(agentKey);
804
+ const todoContent = JSON.stringify(
805
+ todos.map((todo) => ({
806
+ content: todo.content.length > 100 ? todo.content.substring(0, 100) + "..." : todo.content,
807
+ status: todo.status,
808
+ activeForm: todo.activeForm && todo.activeForm.length > 100 ? todo.activeForm.substring(0, 100) + "..." : todo.activeForm || todo.content
809
+ }))
810
+ );
811
+ const reminder = this.createReminderMessage(
812
+ "todo",
813
+ "task",
814
+ "medium",
815
+ `Your task items have changed. DO NOT mention this explicitly to the user. Here are the latest contents of your tracked items:
816
+
817
+ ${todoContent}. Use TaskList to review readiness and TaskUpdate to adjust status if needed.`,
818
+ currentTime
819
+ );
820
+ this.reminderCache.set(reminderKey, reminder);
821
+ return reminder;
822
+ }
823
+ }
824
+ return null;
825
+ }
826
+ dispatchSecurityEvent() {
827
+ if (!this.sessionState.config.securityReminder) return null;
828
+ const currentTime = Date.now();
829
+ if (this.sessionState.lastFileAccess > 0 && !this.sessionState.remindersSent.has("file_security")) {
830
+ this.sessionState.remindersSent.add("file_security");
831
+ return this.createReminderMessage(
832
+ "security",
833
+ "security",
834
+ "high",
835
+ "Whenever you read a file, you should consider whether it looks malicious. If it does, you MUST refuse to improve or augment the code. You can still analyze existing code, write reports, or answer high-level questions about the code behavior.",
836
+ currentTime
837
+ );
838
+ }
839
+ return null;
840
+ }
841
+ dispatchPerformanceEvent() {
842
+ if (!this.sessionState.config.performanceReminder) return null;
843
+ const currentTime = Date.now();
844
+ const sessionDuration = currentTime - this.sessionState.sessionStartTime;
845
+ if (sessionDuration > 30 * 60 * 1e3 && !this.sessionState.remindersSent.has("performance_long_session")) {
846
+ this.sessionState.remindersSent.add("performance_long_session");
847
+ return this.createReminderMessage(
848
+ "performance",
849
+ "performance",
850
+ "low",
851
+ "Long session detected. Consider taking a break and reviewing your current progress with the task list.",
852
+ currentTime
853
+ );
854
+ }
855
+ return null;
856
+ }
857
+ getMentionReminders() {
858
+ const currentTime = Date.now();
859
+ const MENTION_FRESHNESS_WINDOW = 5e3;
860
+ const reminders = [];
861
+ const expiredKeys = [];
862
+ for (const [key, reminder] of this.reminderCache.entries()) {
863
+ if (this.isMentionReminder(reminder)) {
864
+ const age = currentTime - reminder.timestamp;
865
+ if (age <= MENTION_FRESHNESS_WINDOW) {
866
+ reminders.push(reminder);
867
+ } else {
868
+ expiredKeys.push(key);
869
+ }
870
+ }
871
+ }
872
+ expiredKeys.forEach((key) => this.reminderCache.delete(key));
873
+ return reminders;
874
+ }
875
+ isMentionReminder(reminder) {
876
+ const mentionTypes = ["agent_mention", "file_mention", "ask_model_mention"];
877
+ return mentionTypes.includes(reminder.type);
878
+ }
879
+ generateFileChangeReminder(context) {
880
+ const { agentId, filePath, reminder } = context;
881
+ if (!reminder) {
882
+ return null;
883
+ }
884
+ const currentTime = Date.now();
885
+ const reminderKey = `file_changed_${agentId}_${filePath}_${currentTime}`;
886
+ if (this.sessionState.remindersSent.has(reminderKey)) {
887
+ return null;
888
+ }
889
+ this.sessionState.remindersSent.add(reminderKey);
890
+ return this.createReminderMessage(
891
+ "file_changed",
892
+ "general",
893
+ "medium",
894
+ reminder,
895
+ currentTime
896
+ );
897
+ }
898
+ createReminderMessage(type, category, priority, content, timestamp) {
899
+ return {
900
+ role: "system",
901
+ content: `<system-reminder>
902
+ ${content}
903
+ </system-reminder>`,
904
+ isMeta: true,
905
+ timestamp,
906
+ type,
907
+ priority,
908
+ category
909
+ };
910
+ }
911
+ getTodoStateHash(todos) {
912
+ return todos.map((t) => `${t.content}:${t.status}:${t.activeForm || t.content}`).sort().join("|");
913
+ }
914
+ clearTodoReminders(agentId) {
915
+ const agentKey = agentId || "default";
916
+ for (const key of this.sessionState.remindersSent) {
917
+ if (key.startsWith(`todo_updated_${agentKey}_`)) {
918
+ this.sessionState.remindersSent.delete(key);
919
+ }
920
+ }
921
+ }
922
+ shouldEmitTaskReminder(key, throttleMs) {
923
+ const now = Date.now();
924
+ const last = this.sessionState.taskEventTimestamps.get(key) ?? 0;
925
+ if (now - last < throttleMs) {
926
+ return false;
927
+ }
928
+ this.sessionState.taskEventTimestamps.set(key, now);
929
+ return true;
930
+ }
931
+ createTaskListReminder(context) {
932
+ const { listId, summary } = context;
933
+ if (!summary || !listId) return null;
934
+ const reminderKey = `task_list_${listId}_${summary.total}_${summary.ready}_${summary.blocked}_${summary.inProgress}_${summary.open}_${summary.done}_${summary.archived}`;
935
+ if (this.sessionState.remindersSent.has(reminderKey)) return null;
936
+ this.sessionState.remindersSent.add(reminderKey);
937
+ return this.createReminderMessage(
938
+ "task_list_changed",
939
+ "task",
940
+ "medium",
941
+ `Your task list changed. Ready: ${summary.ready}, Blocked: ${summary.blocked}, In progress: ${summary.inProgress}, Open: ${summary.open}, Done: ${summary.done}, Archived: ${summary.archived}.`,
942
+ Date.now()
943
+ );
944
+ }
945
+ createTaskReadyReminder(context) {
946
+ const { listId, readyIds } = context;
947
+ if (!listId || !Array.isArray(readyIds)) return null;
948
+ const limited = readyIds.slice(0, 5).join(", ");
949
+ const reminderKey = `task_ready_${listId}_${readyIds.length}_${limited}`;
950
+ if (this.sessionState.remindersSent.has(reminderKey)) return null;
951
+ this.sessionState.remindersSent.add(reminderKey);
952
+ return this.createReminderMessage(
953
+ "task_ready_changed",
954
+ "task",
955
+ "medium",
956
+ `Task readiness changed. Ready count: ${readyIds.length}. Ready ids: ${limited || "none"}.`,
957
+ Date.now()
958
+ );
959
+ }
960
+ createTaskConflictReminder(context) {
961
+ const { listId, taskId } = context;
962
+ if (!listId || !taskId) return null;
963
+ const reminderKey = `task_conflict_${listId}_${taskId}`;
964
+ if (this.sessionState.remindersSent.has(reminderKey)) return null;
965
+ this.sessionState.remindersSent.add(reminderKey);
966
+ return this.createReminderMessage(
967
+ "task_conflict",
968
+ "task",
969
+ "high",
970
+ `Task update conflict detected for ${taskId}. Refresh task data and retry updates with the latest baseVersion.`,
971
+ Date.now()
972
+ );
973
+ }
974
+ createTaskCreatedReminder(context) {
975
+ const { listId, taskId, task } = context;
976
+ if (!listId || !taskId) return null;
977
+ const reminderKey = `task_created_${listId}_${taskId}`;
978
+ if (this.sessionState.remindersSent.has(reminderKey)) return null;
979
+ this.sessionState.remindersSent.add(reminderKey);
980
+ const subject = task?.title || task?.subject || taskId;
981
+ return this.createReminderMessage(
982
+ "task_created",
983
+ "task",
984
+ "medium",
985
+ `Task created: ${subject} (${taskId}). Review details and adjust status if needed.`,
986
+ Date.now()
987
+ );
988
+ }
989
+ createTaskUpdatedReminder(context) {
990
+ const { listId, taskId, task } = context;
991
+ if (!listId || !taskId) return null;
992
+ const reminderKey = `task_updated_${listId}_${taskId}_${task?.baseVersion ?? ""}`;
993
+ if (this.sessionState.remindersSent.has(reminderKey)) return null;
994
+ this.sessionState.remindersSent.add(reminderKey);
995
+ const subject = task?.title || task?.subject || taskId;
996
+ return this.createReminderMessage(
997
+ "task_updated",
998
+ "task",
999
+ "medium",
1000
+ `Task updated: ${subject} (${taskId}). Confirm dependencies and status are correct.`,
1001
+ Date.now()
1002
+ );
1003
+ }
1004
+ createTaskStatusReminder(context) {
1005
+ const { listId, taskId, previousStatus, status } = context;
1006
+ if (!listId || !taskId) return null;
1007
+ const reminderKey = `task_status_${listId}_${taskId}_${previousStatus}_${status}`;
1008
+ if (this.sessionState.remindersSent.has(reminderKey)) return null;
1009
+ this.sessionState.remindersSent.add(reminderKey);
1010
+ return this.createReminderMessage(
1011
+ "task_status_changed",
1012
+ "task",
1013
+ "medium",
1014
+ `Task status changed for ${taskId}: ${previousStatus} \u2192 ${status}. Review dependent tasks if needed.`,
1015
+ Date.now()
1016
+ );
1017
+ }
1018
+ createTaskDepsReminder(context) {
1019
+ const { listId, taskId, deps } = context;
1020
+ if (!listId || !taskId) return null;
1021
+ const reminderKey = `task_deps_${listId}_${taskId}_${JSON.stringify(deps ?? {})}`;
1022
+ if (this.sessionState.remindersSent.has(reminderKey)) return null;
1023
+ this.sessionState.remindersSent.add(reminderKey);
1024
+ return this.createReminderMessage(
1025
+ "task_deps_changed",
1026
+ "task",
1027
+ "medium",
1028
+ `Task dependencies changed for ${taskId}. Recheck readiness and blockers.`,
1029
+ Date.now()
1030
+ );
1031
+ }
1032
+ setupEventDispatcher() {
1033
+ this.addEventListener("session:startup", (context) => {
1034
+ this.resetSession();
1035
+ this.sessionState.sessionStartTime = Date.now();
1036
+ this.sessionState.contextPresent = Object.keys(context.context || {}).length > 0;
1037
+ });
1038
+ this.addEventListener("todo:changed", (context) => {
1039
+ this.sessionState.lastTodoUpdate = Date.now();
1040
+ this.clearTodoReminders(context.agentId);
1041
+ });
1042
+ this.addEventListener("todo:file_changed", (context) => {
1043
+ const agentId = context.agentId || "default";
1044
+ this.clearTodoReminders(agentId);
1045
+ this.sessionState.lastTodoUpdate = Date.now();
1046
+ const reminder = this.generateFileChangeReminder(context);
1047
+ if (reminder) {
1048
+ this.emitEvent("reminder:inject", {
1049
+ reminder: reminder.content,
1050
+ agentId,
1051
+ type: "file_changed",
1052
+ timestamp: Date.now()
1053
+ });
1054
+ }
1055
+ });
1056
+ this.addEventListener("task:list_changed", (context) => {
1057
+ const key = `task_list_${context.listId}`;
1058
+ if (!this.shouldEmitTaskReminder(key, 2e3)) return;
1059
+ this.sessionState.lastTaskUpdate = Date.now();
1060
+ const reminder = this.createTaskListReminder(context);
1061
+ if (reminder) {
1062
+ this.emitEvent("reminder:inject", {
1063
+ reminder: reminder.content,
1064
+ listId: context.listId,
1065
+ type: "task_list_changed",
1066
+ timestamp: Date.now()
1067
+ });
1068
+ }
1069
+ });
1070
+ this.addEventListener("task:ready_changed", (context) => {
1071
+ const key = `task_ready_${context.listId}`;
1072
+ if (!this.shouldEmitTaskReminder(key, 2e3)) return;
1073
+ const reminder = this.createTaskReadyReminder(context);
1074
+ if (reminder) {
1075
+ this.emitEvent("reminder:inject", {
1076
+ reminder: reminder.content,
1077
+ listId: context.listId,
1078
+ type: "task_ready_changed",
1079
+ timestamp: Date.now()
1080
+ });
1081
+ }
1082
+ });
1083
+ this.addEventListener("task:conflict_detected", (context) => {
1084
+ const key = `task_conflict_${context.listId}_${context.taskId}`;
1085
+ if (!this.shouldEmitTaskReminder(key, 2e3)) return;
1086
+ const reminder = this.createTaskConflictReminder(context);
1087
+ if (reminder) {
1088
+ this.emitEvent("reminder:inject", {
1089
+ reminder: reminder.content,
1090
+ listId: context.listId,
1091
+ type: "task_conflict",
1092
+ timestamp: Date.now()
1093
+ });
1094
+ }
1095
+ });
1096
+ this.addEventListener("task.created", (context) => {
1097
+ const key = `task_created_${context.listId}_${context.taskId}`;
1098
+ if (!this.shouldEmitTaskReminder(key, 2e3)) return;
1099
+ const reminder = this.createTaskCreatedReminder(context);
1100
+ if (reminder) {
1101
+ this.emitEvent("reminder:inject", {
1102
+ reminder: reminder.content,
1103
+ listId: context.listId,
1104
+ type: reminder.type,
1105
+ timestamp: reminder.timestamp
1106
+ });
1107
+ }
1108
+ });
1109
+ this.addEventListener("task.updated", (context) => {
1110
+ const key = `task_updated_${context.listId}_${context.taskId}`;
1111
+ if (!this.shouldEmitTaskReminder(key, 2e3)) return;
1112
+ const reminder = this.createTaskUpdatedReminder(context);
1113
+ if (reminder) {
1114
+ this.emitEvent("reminder:inject", {
1115
+ reminder: reminder.content,
1116
+ listId: context.listId,
1117
+ type: reminder.type,
1118
+ timestamp: reminder.timestamp
1119
+ });
1120
+ }
1121
+ });
1122
+ this.addEventListener("task.status_changed", (context) => {
1123
+ const key = `task_status_${context.listId}_${context.taskId}`;
1124
+ if (!this.shouldEmitTaskReminder(key, 2e3)) return;
1125
+ const reminder = this.createTaskStatusReminder(context);
1126
+ if (reminder) {
1127
+ this.emitEvent("reminder:inject", {
1128
+ reminder: reminder.content,
1129
+ listId: context.listId,
1130
+ type: reminder.type,
1131
+ timestamp: reminder.timestamp
1132
+ });
1133
+ }
1134
+ });
1135
+ this.addEventListener("task.deps_changed", (context) => {
1136
+ const key = `task_deps_${context.listId}_${context.taskId}`;
1137
+ if (!this.shouldEmitTaskReminder(key, 2e3)) return;
1138
+ const reminder = this.createTaskDepsReminder(context);
1139
+ if (reminder) {
1140
+ this.emitEvent("reminder:inject", {
1141
+ reminder: reminder.content,
1142
+ listId: context.listId,
1143
+ type: reminder.type,
1144
+ timestamp: reminder.timestamp
1145
+ });
1146
+ }
1147
+ });
1148
+ this.addEventListener("file:read", (context) => {
1149
+ this.sessionState.lastFileAccess = Date.now();
1150
+ });
1151
+ this.addEventListener("file:edited", (context) => {
1152
+ });
1153
+ this.addEventListener("agent:mentioned", (context) => {
1154
+ this.createMentionReminder({
1155
+ type: "agent_mention",
1156
+ key: `agent_mention_${context.agentType}_${context.timestamp}`,
1157
+ category: "task",
1158
+ priority: "high",
1159
+ content: `The user mentioned @${context.originalMention}. You MUST use the Task tool with subagent_type="${context.agentType}" to delegate this task to the specified agent. Provide a detailed, self-contained task description that fully captures the user's intent for the ${context.agentType} agent to execute.`,
1160
+ timestamp: context.timestamp
1161
+ });
1162
+ });
1163
+ this.addEventListener("file:mentioned", (context) => {
1164
+ this.createMentionReminder({
1165
+ type: "file_mention",
1166
+ key: `file_mention_${context.filePath}_${context.timestamp}`,
1167
+ category: "general",
1168
+ priority: "high",
1169
+ content: `The user mentioned @${context.originalMention}. You MUST read the entire content of the file at path: ${context.filePath} using the Read tool to understand the full context before proceeding with the user's request.`,
1170
+ timestamp: context.timestamp
1171
+ });
1172
+ });
1173
+ this.addEventListener("ask-model:mentioned", (context) => {
1174
+ this.createMentionReminder({
1175
+ type: "ask_model_mention",
1176
+ key: `ask_model_mention_${context.modelName}_${context.timestamp}`,
1177
+ category: "task",
1178
+ priority: "high",
1179
+ content: `The user mentioned @${context.modelName}. You MUST use the AskExpertModelTool to consult this specific model for expert opinions and analysis. Provide the user's question or context clearly to get the most relevant response from ${context.modelName}.`,
1180
+ timestamp: context.timestamp
1181
+ });
1182
+ });
1183
+ }
1184
+ addEventListener(event, callback) {
1185
+ if (!this.eventDispatcher.has(event)) {
1186
+ this.eventDispatcher.set(event, []);
1187
+ }
1188
+ this.eventDispatcher.get(event).push(callback);
1189
+ }
1190
+ emitEvent(event, context) {
1191
+ const listeners = this.eventDispatcher.get(event) || [];
1192
+ listeners.forEach((callback) => {
1193
+ try {
1194
+ callback(context);
1195
+ } catch (error) {
1196
+ logError(error);
1197
+ debug.warn("SYSTEM_REMINDER_LISTENER_ERROR", {
1198
+ event,
1199
+ error: error instanceof Error ? error.message : String(error)
1200
+ });
1201
+ }
1202
+ });
1203
+ }
1204
+ createMentionReminder(params) {
1205
+ if (!this.sessionState.remindersSent.has(params.key)) {
1206
+ this.sessionState.remindersSent.add(params.key);
1207
+ const reminder = this.createReminderMessage(
1208
+ params.type,
1209
+ params.category,
1210
+ params.priority,
1211
+ params.content,
1212
+ params.timestamp
1213
+ );
1214
+ this.reminderCache.set(params.key, reminder);
1215
+ }
1216
+ }
1217
+ resetSession() {
1218
+ this.sessionState = {
1219
+ lastTodoUpdate: 0,
1220
+ lastFileAccess: 0,
1221
+ lastTaskUpdate: 0,
1222
+ sessionStartTime: Date.now(),
1223
+ remindersSent: /* @__PURE__ */ new Set(),
1224
+ contextPresent: false,
1225
+ reminderCount: 0,
1226
+ taskEventTimestamps: /* @__PURE__ */ new Map(),
1227
+ config: { ...this.sessionState.config }
1228
+ };
1229
+ this.reminderCache.clear();
1230
+ }
1231
+ updateConfig(config) {
1232
+ this.sessionState.config = { ...this.sessionState.config, ...config };
1233
+ }
1234
+ getSessionState() {
1235
+ return { ...this.sessionState };
1236
+ }
1237
+ };
1238
+ var systemReminderService = new SystemReminderService();
1239
+ var generateSystemReminders = (hasContext = false, agentId) => systemReminderService.generateReminders(hasContext, agentId);
1240
+ var emitReminderEvent = (event, context) => systemReminderService.emitEvent(event, context);
1241
+ var resetReminderSession = () => systemReminderService.resetSession();
1242
+
1243
+ export {
1244
+ getAgentFilePath,
1245
+ generateAgentId,
1246
+ TaskStoreConflictError,
1247
+ getTaskListPaths,
1248
+ readTaskEventLog,
1249
+ createTask,
1250
+ getTask,
1251
+ listTasks,
1252
+ updateTask,
1253
+ isTodoCompatEnabled,
1254
+ syncTodosToTasks,
1255
+ migrateTodosToTasks,
1256
+ getTodos,
1257
+ setTodos,
1258
+ systemReminderService,
1259
+ generateSystemReminders,
1260
+ emitReminderEvent,
1261
+ resetReminderSession
1262
+ };