pybao-cli 1.4.49 → 1.4.51

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 (148) hide show
  1. package/dist/REPL-6YEOSMEP.js +47 -0
  2. package/dist/{acp-FGRNDOS5.js → acp-2C774HUW.js} +31 -31
  3. package/dist/{agentsValidate-7CRDPNQ6.js → agentsValidate-RCXZBOU3.js} +7 -7
  4. package/dist/{ask-IR5W5M66.js → ask-EIO3Q6ED.js} +30 -30
  5. package/dist/{autoUpdater-MAEQZPST.js → autoUpdater-IIJMDPJE.js} +3 -3
  6. package/dist/{chunk-SHIPHOMT.js → chunk-5RX62PWR.js} +1 -1
  7. package/dist/{chunk-XHGUMYRL.js → chunk-6MFZG46W.js} +1 -1
  8. package/dist/{chunk-IQ3KORSP.js → chunk-DCSAQNHW.js} +4 -4
  9. package/dist/{chunk-YFBIVUDT.js → chunk-ESKTQXW6.js} +27 -3
  10. package/dist/chunk-ESKTQXW6.js.map +7 -0
  11. package/dist/{chunk-QW4VX3FW.js → chunk-G2FI43IB.js} +2 -2
  12. package/dist/{chunk-4Z6CCSED.js → chunk-GLS3ZXCS.js} +3 -3
  13. package/dist/{chunk-DEQQW4H6.js → chunk-GTP62T63.js} +3 -3
  14. package/dist/{chunk-UUGWQSET.js → chunk-HJDETBB6.js} +1 -1
  15. package/dist/{chunk-55QJI7OQ.js → chunk-HY24VLOS.js} +1 -1
  16. package/dist/{chunk-LZT5LZKG.js → chunk-KDKXQODT.js} +2 -2
  17. package/dist/{chunk-HFL2SOFQ.js → chunk-KNOCJVE6.js} +1 -1
  18. package/dist/{chunk-MCP52ZSD.js → chunk-L2FQX7IL.js} +419 -244
  19. package/dist/chunk-L2FQX7IL.js.map +7 -0
  20. package/dist/{chunk-6Y4OSP3J.js → chunk-LAFEZLTD.js} +307 -218
  21. package/dist/chunk-LAFEZLTD.js.map +7 -0
  22. package/dist/{chunk-EXEPX5H2.js → chunk-LAQQRRQR.js} +4 -4
  23. package/dist/{chunk-2U747SA5.js → chunk-LEQNJ73H.js} +3 -3
  24. package/dist/{chunk-NZTVG7YS.js → chunk-MZ6UJROW.js} +4 -4
  25. package/dist/{chunk-MLCISJKM.js → chunk-N6DXTYLR.js} +3 -3
  26. package/dist/{chunk-JF4AMPKU.js → chunk-OBFMPMGW.js} +2 -2
  27. package/dist/{chunk-A7VN6YXZ.js → chunk-OYCMMRZV.js} +1 -1
  28. package/dist/{chunk-XZNXY65J.js → chunk-PKAVBIO7.js} +1 -1
  29. package/dist/{chunk-S2AJAM6R.js → chunk-SNKLRBWT.js} +2 -2
  30. package/dist/{chunk-6GOKZQDV.js → chunk-SQJJRPIY.js} +5 -5
  31. package/dist/{chunk-72SGZIMD.js → chunk-TMFNSMVV.js} +6 -6
  32. package/dist/{chunk-D3Q32IUW.js → chunk-U7MPXQO4.js} +3 -3
  33. package/dist/{chunk-3P2EGWMK.js → chunk-VUILI5ED.js} +2 -2
  34. package/dist/{chunk-7FNDFUQH.js → chunk-WCKPMAYX.js} +1 -1
  35. package/dist/{chunk-PSI4PJ2H.js → chunk-WSN5UCPE.js} +1 -1
  36. package/dist/{chunk-AWML3QPK.js → chunk-YNVGW5VZ.js} +7 -7
  37. package/dist/{chunk-I5H5PGD5.js → chunk-YTUV273Y.js} +50 -122
  38. package/dist/{chunk-I5H5PGD5.js.map → chunk-YTUV273Y.js.map} +2 -2
  39. package/dist/{chunk-GFDV6FUG.js → chunk-ZQAKMVXR.js} +3 -3
  40. package/dist/{cli-PCLH664O.js → cli-JPS6PPHH.js} +91 -89
  41. package/dist/{cli-PCLH664O.js.map → cli-JPS6PPHH.js.map} +2 -2
  42. package/dist/commands-3IJYY2KF.js +51 -0
  43. package/dist/{config-EBW7GI65.js → config-7RFJBYCF.js} +4 -4
  44. package/dist/{context-4BILE3LL.js → context-L6IDU4ZF.js} +6 -6
  45. package/dist/{customCommands-GDDHYRJR.js → customCommands-2ZLDKEPS.js} +4 -4
  46. package/dist/{env-7V57CD26.js → env-KKHHZ47D.js} +2 -2
  47. package/dist/{file-3W4M7RXM.js → file-QGJRLWJ7.js} +4 -4
  48. package/dist/index.js +3 -3
  49. package/dist/{llm-PIRKDSMO.js → llm-HHSKS5JE.js} +31 -31
  50. package/dist/{llmLazy-HWKMKZ5M.js → llmLazy-YV33JO4W.js} +1 -1
  51. package/dist/{loader-GYFX3MOO.js → loader-YCEP5CYE.js} +4 -4
  52. package/dist/{lsp-BDQVEVFK.js → lsp-TG3EZZOU.js} +6 -6
  53. package/dist/{lspAnchor-S45W3FAI.js → lspAnchor-T3544E7J.js} +6 -6
  54. package/dist/{mcp-ZHLSRCM5.js → mcp-SWYFI5FF.js} +7 -7
  55. package/dist/{mentionProcessor-GOS6BB64.js → mentionProcessor-IP6FAEXL.js} +7 -6
  56. package/dist/{mentionProcessor-GOS6BB64.js.map → mentionProcessor-IP6FAEXL.js.map} +1 -1
  57. package/dist/{messages-AX4MWPK5.js → messages-3B2ZJJKW.js} +1 -1
  58. package/dist/{model-QDQAUOQ6.js → model-HDJXCBKD.js} +5 -5
  59. package/dist/{openai-IBJPYBTX.js → openai-DF2BQT7X.js} +5 -5
  60. package/dist/{outputStyles-UNAE3KVB.js → outputStyles-GWUBSKDY.js} +4 -4
  61. package/dist/{pluginRuntime-QP6HTJ6Y.js → pluginRuntime-LKAAMJH2.js} +6 -6
  62. package/dist/{pluginValidation-C66MGV4E.js → pluginValidation-7GJA32YV.js} +6 -6
  63. package/dist/prompts-DB772NLS.js +53 -0
  64. package/dist/{pybAgentSessionLoad-GJQQFHVS.js → pybAgentSessionLoad-BGIX7L4O.js} +4 -4
  65. package/dist/{pybAgentSessionResume-2ZNOOEVX.js → pybAgentSessionResume-VKK6VZSA.js} +4 -4
  66. package/dist/{pybAgentStreamJsonSession-PE2K75QJ.js → pybAgentStreamJsonSession-HDXJ4F5W.js} +1 -1
  67. package/dist/{pybHooks-22R4DOTT.js → pybHooks-LEIAZ7D6.js} +5 -5
  68. package/dist/query-LVPGQYDL.js +55 -0
  69. package/dist/{registry-ES4KTNF7.js → registry-C4V5AZ6N.js} +5 -5
  70. package/dist/{ripgrep-7BPG53S4.js → ripgrep-PWIRRS2Q.js} +3 -3
  71. package/dist/{skillMarketplace-O5TAM4G6.js → skillMarketplace-GZN6RWUY.js} +3 -3
  72. package/dist/{state-3OM3WWZ2.js → state-T7WQKFBG.js} +2 -2
  73. package/dist/{theme-7MFSQQ5Z.js → theme-CLYLDSCI.js} +5 -5
  74. package/dist/{toolPermissionSettings-KKMWAVTJ.js → toolPermissionSettings-UYHSUTC5.js} +6 -6
  75. package/dist/tools-SEWCFM5P.js +52 -0
  76. package/dist/{userInput-3BYMTC52.js → userInput-KZXH5O7V.js} +32 -32
  77. package/package.json +27 -3
  78. package/dist/REPL-PRQAXE32.js +0 -47
  79. package/dist/chunk-6Y4OSP3J.js.map +0 -7
  80. package/dist/chunk-MCP52ZSD.js.map +0 -7
  81. package/dist/chunk-YFBIVUDT.js.map +0 -7
  82. package/dist/commands-Y7GVRV66.js +0 -51
  83. package/dist/prompts-LNJ2SJQ7.js +0 -53
  84. package/dist/query-2ZFLZTV6.js +0 -55
  85. package/dist/tools-ME4EX5CM.js +0 -52
  86. /package/dist/{REPL-PRQAXE32.js.map → REPL-6YEOSMEP.js.map} +0 -0
  87. /package/dist/{acp-FGRNDOS5.js.map → acp-2C774HUW.js.map} +0 -0
  88. /package/dist/{agentsValidate-7CRDPNQ6.js.map → agentsValidate-RCXZBOU3.js.map} +0 -0
  89. /package/dist/{ask-IR5W5M66.js.map → ask-EIO3Q6ED.js.map} +0 -0
  90. /package/dist/{autoUpdater-MAEQZPST.js.map → autoUpdater-IIJMDPJE.js.map} +0 -0
  91. /package/dist/{chunk-SHIPHOMT.js.map → chunk-5RX62PWR.js.map} +0 -0
  92. /package/dist/{chunk-XHGUMYRL.js.map → chunk-6MFZG46W.js.map} +0 -0
  93. /package/dist/{chunk-IQ3KORSP.js.map → chunk-DCSAQNHW.js.map} +0 -0
  94. /package/dist/{chunk-QW4VX3FW.js.map → chunk-G2FI43IB.js.map} +0 -0
  95. /package/dist/{chunk-4Z6CCSED.js.map → chunk-GLS3ZXCS.js.map} +0 -0
  96. /package/dist/{chunk-DEQQW4H6.js.map → chunk-GTP62T63.js.map} +0 -0
  97. /package/dist/{chunk-UUGWQSET.js.map → chunk-HJDETBB6.js.map} +0 -0
  98. /package/dist/{chunk-55QJI7OQ.js.map → chunk-HY24VLOS.js.map} +0 -0
  99. /package/dist/{chunk-LZT5LZKG.js.map → chunk-KDKXQODT.js.map} +0 -0
  100. /package/dist/{chunk-HFL2SOFQ.js.map → chunk-KNOCJVE6.js.map} +0 -0
  101. /package/dist/{chunk-EXEPX5H2.js.map → chunk-LAQQRRQR.js.map} +0 -0
  102. /package/dist/{chunk-2U747SA5.js.map → chunk-LEQNJ73H.js.map} +0 -0
  103. /package/dist/{chunk-NZTVG7YS.js.map → chunk-MZ6UJROW.js.map} +0 -0
  104. /package/dist/{chunk-MLCISJKM.js.map → chunk-N6DXTYLR.js.map} +0 -0
  105. /package/dist/{chunk-JF4AMPKU.js.map → chunk-OBFMPMGW.js.map} +0 -0
  106. /package/dist/{chunk-A7VN6YXZ.js.map → chunk-OYCMMRZV.js.map} +0 -0
  107. /package/dist/{chunk-XZNXY65J.js.map → chunk-PKAVBIO7.js.map} +0 -0
  108. /package/dist/{chunk-S2AJAM6R.js.map → chunk-SNKLRBWT.js.map} +0 -0
  109. /package/dist/{chunk-6GOKZQDV.js.map → chunk-SQJJRPIY.js.map} +0 -0
  110. /package/dist/{chunk-72SGZIMD.js.map → chunk-TMFNSMVV.js.map} +0 -0
  111. /package/dist/{chunk-D3Q32IUW.js.map → chunk-U7MPXQO4.js.map} +0 -0
  112. /package/dist/{chunk-3P2EGWMK.js.map → chunk-VUILI5ED.js.map} +0 -0
  113. /package/dist/{chunk-7FNDFUQH.js.map → chunk-WCKPMAYX.js.map} +0 -0
  114. /package/dist/{chunk-PSI4PJ2H.js.map → chunk-WSN5UCPE.js.map} +0 -0
  115. /package/dist/{chunk-AWML3QPK.js.map → chunk-YNVGW5VZ.js.map} +0 -0
  116. /package/dist/{chunk-GFDV6FUG.js.map → chunk-ZQAKMVXR.js.map} +0 -0
  117. /package/dist/{commands-Y7GVRV66.js.map → commands-3IJYY2KF.js.map} +0 -0
  118. /package/dist/{config-EBW7GI65.js.map → config-7RFJBYCF.js.map} +0 -0
  119. /package/dist/{context-4BILE3LL.js.map → context-L6IDU4ZF.js.map} +0 -0
  120. /package/dist/{customCommands-GDDHYRJR.js.map → customCommands-2ZLDKEPS.js.map} +0 -0
  121. /package/dist/{env-7V57CD26.js.map → env-KKHHZ47D.js.map} +0 -0
  122. /package/dist/{file-3W4M7RXM.js.map → file-QGJRLWJ7.js.map} +0 -0
  123. /package/dist/{llm-PIRKDSMO.js.map → llm-HHSKS5JE.js.map} +0 -0
  124. /package/dist/{llmLazy-HWKMKZ5M.js.map → llmLazy-YV33JO4W.js.map} +0 -0
  125. /package/dist/{loader-GYFX3MOO.js.map → loader-YCEP5CYE.js.map} +0 -0
  126. /package/dist/{lsp-BDQVEVFK.js.map → lsp-TG3EZZOU.js.map} +0 -0
  127. /package/dist/{lspAnchor-S45W3FAI.js.map → lspAnchor-T3544E7J.js.map} +0 -0
  128. /package/dist/{mcp-ZHLSRCM5.js.map → mcp-SWYFI5FF.js.map} +0 -0
  129. /package/dist/{messages-AX4MWPK5.js.map → messages-3B2ZJJKW.js.map} +0 -0
  130. /package/dist/{model-QDQAUOQ6.js.map → model-HDJXCBKD.js.map} +0 -0
  131. /package/dist/{openai-IBJPYBTX.js.map → openai-DF2BQT7X.js.map} +0 -0
  132. /package/dist/{outputStyles-UNAE3KVB.js.map → outputStyles-GWUBSKDY.js.map} +0 -0
  133. /package/dist/{pluginRuntime-QP6HTJ6Y.js.map → pluginRuntime-LKAAMJH2.js.map} +0 -0
  134. /package/dist/{pluginValidation-C66MGV4E.js.map → pluginValidation-7GJA32YV.js.map} +0 -0
  135. /package/dist/{prompts-LNJ2SJQ7.js.map → prompts-DB772NLS.js.map} +0 -0
  136. /package/dist/{pybAgentSessionLoad-GJQQFHVS.js.map → pybAgentSessionLoad-BGIX7L4O.js.map} +0 -0
  137. /package/dist/{pybAgentSessionResume-2ZNOOEVX.js.map → pybAgentSessionResume-VKK6VZSA.js.map} +0 -0
  138. /package/dist/{pybAgentStreamJsonSession-PE2K75QJ.js.map → pybAgentStreamJsonSession-HDXJ4F5W.js.map} +0 -0
  139. /package/dist/{pybHooks-22R4DOTT.js.map → pybHooks-LEIAZ7D6.js.map} +0 -0
  140. /package/dist/{query-2ZFLZTV6.js.map → query-LVPGQYDL.js.map} +0 -0
  141. /package/dist/{registry-ES4KTNF7.js.map → registry-C4V5AZ6N.js.map} +0 -0
  142. /package/dist/{ripgrep-7BPG53S4.js.map → ripgrep-PWIRRS2Q.js.map} +0 -0
  143. /package/dist/{skillMarketplace-O5TAM4G6.js.map → skillMarketplace-GZN6RWUY.js.map} +0 -0
  144. /package/dist/{state-3OM3WWZ2.js.map → state-T7WQKFBG.js.map} +0 -0
  145. /package/dist/{theme-7MFSQQ5Z.js.map → theme-CLYLDSCI.js.map} +0 -0
  146. /package/dist/{toolPermissionSettings-KKMWAVTJ.js.map → toolPermissionSettings-UYHSUTC5.js.map} +0 -0
  147. /package/dist/{tools-ME4EX5CM.js.map → tools-SEWCFM5P.js.map} +0 -0
  148. /package/dist/{userInput-3BYMTC52.js.map → userInput-KZXH5O7V.js.map} +0 -0
@@ -4,18 +4,21 @@ import {
4
4
  getSessionState,
5
5
  setSessionState
6
6
  } from "./chunk-ERMQRV55.js";
7
+ import {
8
+ getPybAgentSessionId
9
+ } from "./chunk-B6IMQJZM.js";
7
10
  import {
8
11
  getCurrentProjectConfig,
9
12
  getGlobalConfig
10
- } from "./chunk-2U747SA5.js";
13
+ } from "./chunk-LEQNJ73H.js";
11
14
  import {
12
15
  debug
13
- } from "./chunk-XHGUMYRL.js";
16
+ } from "./chunk-6MFZG46W.js";
14
17
  import {
15
18
  getCwd,
16
19
  logError,
17
20
  resolveXdgDataPath
18
- } from "./chunk-UUGWQSET.js";
21
+ } from "./chunk-HJDETBB6.js";
19
22
 
20
23
  // src/utils/agent/storage.ts
21
24
  import { existsSync, readFileSync, writeFileSync, mkdirSync } from "fs";
@@ -81,16 +84,153 @@ function generateAgentId() {
81
84
  // src/utils/session/taskStore.ts
82
85
  import {
83
86
  existsSync as existsSync2,
87
+ closeSync,
84
88
  mkdirSync as mkdirSync2,
89
+ openSync,
85
90
  readFileSync as readFileSync2,
86
91
  readdirSync,
92
+ renameSync,
87
93
  rmSync,
88
94
  writeFileSync as writeFileSync2
89
95
  } from "fs";
90
96
  import { join as join2 } from "path";
91
97
  import { homedir } from "os";
92
- import { createHash } from "crypto";
93
- var DEFAULT_LOCK_TTL_MS = 6e4;
98
+ import { randomUUID as randomUUID2 } from "crypto";
99
+ import { AsyncLocalStorage } from "async_hooks";
100
+
101
+ // src/utils/session/taskLockNative.ts
102
+ import { createRequire } from "module";
103
+ var require2 = createRequire(import.meta.url);
104
+ var getLinuxVariant = (report) => {
105
+ const glibc = report?.getReport?.()?.header?.glibcVersionRuntime;
106
+ return glibc ? "gnu" : "musl";
107
+ };
108
+ var resolveNativeLockPackage = (options) => {
109
+ const platform = options?.platform ?? process.platform;
110
+ const arch = options?.arch ?? process.arch;
111
+ const report = options?.report ?? process.report;
112
+ if (platform === "win32" && arch === "x64") {
113
+ return "@pyb/lock-win32-x64-msvc";
114
+ }
115
+ if (platform === "win32" && arch === "arm64") {
116
+ return "@pyb/lock-win32-arm64-msvc";
117
+ }
118
+ if (platform === "darwin" && arch === "x64") {
119
+ return "@pyb/lock-darwin-x64";
120
+ }
121
+ if (platform === "darwin" && arch === "arm64") {
122
+ return "@pyb/lock-darwin-arm64";
123
+ }
124
+ if (platform === "linux" && arch === "x64") {
125
+ const variant = getLinuxVariant(report);
126
+ return variant === "musl" ? "@pyb/lock-linux-x64-musl" : "@pyb/lock-linux-x64-gnu";
127
+ }
128
+ if (platform === "linux" && arch === "arm64") {
129
+ return "@pyb/lock-linux-arm64-gnu";
130
+ }
131
+ if (platform === "linux" && arch === "arm") {
132
+ return "@pyb/lock-linux-armv7-gnueabihf";
133
+ }
134
+ return null;
135
+ };
136
+ var cachedLock;
137
+ var loadNativeTaskLock = () => {
138
+ const globalLock = globalThis.__PYB_NATIVE_TASK_LOCK__;
139
+ if (globalLock) {
140
+ cachedLock = globalLock;
141
+ return cachedLock;
142
+ }
143
+ if (cachedLock !== void 0) return cachedLock;
144
+ const envPath = process.env.PYB_NATIVE_LOCK_NODE_PATH;
145
+ if (envPath) {
146
+ try {
147
+ const mod = require2(envPath);
148
+ const candidate = mod?.default ?? mod;
149
+ if (candidate && typeof candidate.lockExclusive === "function" && typeof candidate.tryLockExclusive === "function" && typeof candidate.unlock === "function") {
150
+ cachedLock = candidate;
151
+ return cachedLock;
152
+ }
153
+ } catch {
154
+ }
155
+ }
156
+ const packageName = resolveNativeLockPackage();
157
+ if (!packageName) {
158
+ cachedLock = null;
159
+ return null;
160
+ }
161
+ try {
162
+ const mod = require2(packageName);
163
+ const candidate = mod?.default ?? mod;
164
+ if (candidate && typeof candidate.lockExclusive === "function" && typeof candidate.tryLockExclusive === "function" && typeof candidate.unlock === "function") {
165
+ cachedLock = candidate;
166
+ return cachedLock;
167
+ }
168
+ } catch {
169
+ }
170
+ cachedLock = null;
171
+ return null;
172
+ };
173
+
174
+ // src/utils/session/taskStore.ts
175
+ var taskListIdStorage = new AsyncLocalStorage();
176
+ var taskListEnvStorage = new AsyncLocalStorage();
177
+ var nativeLock = null;
178
+ var nativeLockResolved = false;
179
+ function getNativeLock() {
180
+ const globalLock = globalThis.__PYB_NATIVE_TASK_LOCK__;
181
+ if (globalLock) {
182
+ nativeLock = globalLock;
183
+ nativeLockResolved = true;
184
+ return nativeLock;
185
+ }
186
+ if (nativeLockResolved) return nativeLock;
187
+ nativeLockResolved = true;
188
+ nativeLock = loadNativeTaskLock();
189
+ return nativeLock;
190
+ }
191
+ function generateTaskListId() {
192
+ return randomUUID2();
193
+ }
194
+ function runWithTaskListId(listId, handler) {
195
+ return taskListIdStorage.run(listId, () => {
196
+ const result = handler();
197
+ const asyncIterator = result && typeof result[Symbol.asyncIterator] === "function" ? result : null;
198
+ if (!asyncIterator) return result;
199
+ return {
200
+ [Symbol.asyncIterator]() {
201
+ const iterator = asyncIterator[Symbol.asyncIterator]();
202
+ return {
203
+ next: (value) => taskListIdStorage.run(listId, () => iterator.next(value)),
204
+ return: iterator.return ? (value) => taskListIdStorage.run(listId, () => iterator.return(value)) : void 0,
205
+ throw: iterator.throw ? (err) => taskListIdStorage.run(listId, () => iterator.throw(err)) : void 0
206
+ };
207
+ }
208
+ };
209
+ });
210
+ }
211
+ function runWithTaskListEnv(env, handler) {
212
+ return taskListEnvStorage.run(env, () => {
213
+ const result = handler();
214
+ const asyncIterator = result && typeof result[Symbol.asyncIterator] === "function" ? result : null;
215
+ if (!asyncIterator) return result;
216
+ return {
217
+ [Symbol.asyncIterator]() {
218
+ const iterator = asyncIterator[Symbol.asyncIterator]();
219
+ return {
220
+ next: (value) => taskListEnvStorage.run(env, () => iterator.next(value)),
221
+ return: iterator.return ? (value) => taskListEnvStorage.run(env, () => iterator.return(value)) : void 0,
222
+ throw: iterator.throw ? (err) => taskListEnvStorage.run(env, () => iterator.throw(err)) : void 0
223
+ };
224
+ }
225
+ };
226
+ });
227
+ }
228
+ var taskListLocks = /* @__PURE__ */ new Map();
229
+ var taskLockWaitBuffer = new Int32Array(new SharedArrayBuffer(4));
230
+ var DEFAULT_LOCK_TIMEOUT_MS = Number(
231
+ process.env.PYB_TASK_LOCK_TIMEOUT_MS ?? 5e3
232
+ );
233
+ var DEFAULT_LOCK_RETRY_MS = Number(process.env.PYB_TASK_LOCK_RETRY_MS ?? 50);
94
234
  var TaskStoreLockError = class extends Error {
95
235
  code = "TASK_STORE_LOCKED";
96
236
  constructor(message) {
@@ -114,7 +254,7 @@ var TaskStoreConflictError = class extends Error {
114
254
  }
115
255
  };
116
256
  function getDefaultTaskListId(cwd) {
117
- return createHash("sha256").update(cwd).digest("hex").slice(0, 12);
257
+ return getPybAgentSessionId();
118
258
  }
119
259
  function resolveGlobalTaskRootDir() {
120
260
  const override = process.env.PYB_CONFIG_DIR ?? process.env.CLAUDE_CONFIG_DIR;
@@ -133,12 +273,13 @@ function findBaseDirWithList(listId, candidates) {
133
273
  return null;
134
274
  }
135
275
  function getTaskListContext(options) {
136
- const env = options?.env ?? process.env;
276
+ const env = options?.env ?? taskListEnvStorage.getStore() ?? process.env;
137
277
  const globalConfig = options?.globalConfig ?? getGlobalConfig();
138
278
  const projectConfig = options?.projectConfig ?? getCurrentProjectConfig();
139
279
  const cwd = options?.cwd ?? getCwd();
140
280
  const defaultId = getDefaultTaskListId(cwd);
141
281
  const inputListId = options?.listId?.trim();
282
+ const contextListId = taskListIdStorage.getStore()?.trim();
142
283
  const envId = env.PYB_TASK_LIST_ID?.trim() || env.CLAUDE_CODE_TASK_LIST_ID?.trim();
143
284
  const globalId = globalConfig?.taskListId?.trim();
144
285
  const projectId = projectConfig?.taskListId?.trim();
@@ -151,6 +292,9 @@ function getTaskListContext(options) {
151
292
  }
152
293
  return { listId: inputListId, scope: "project", source: "input" };
153
294
  }
295
+ if (contextListId) {
296
+ return { listId: contextListId, scope: "project", source: "context" };
297
+ }
154
298
  if (envId) return { listId: envId, scope: "global", source: "env" };
155
299
  if (globalId) return { listId: globalId, scope: "global", source: "global" };
156
300
  if (projectId) return { listId: projectId, scope: "project", source: "project" };
@@ -182,24 +326,92 @@ function resolveTaskBaseDir(context, cwd) {
182
326
  const candidates = [...globalCandidates, resolveXdgDataPath("tasks")];
183
327
  return findBaseDirWithList(context.listId, candidates) ?? defaultBaseDir;
184
328
  }
185
- function getTaskListPaths(listId) {
186
- const context = getTaskListContext({ listId });
187
- const cwd = getCwd();
188
- const baseDir = resolveTaskBaseDir(context, cwd);
189
- const listDir = join2(baseDir, context.listId);
329
+ function buildTaskListPaths(baseDir, listId) {
330
+ const listDir = join2(baseDir, listId);
190
331
  return {
191
- listId: context.listId,
332
+ listId,
192
333
  baseDir,
193
334
  listDir,
194
335
  tasksDir: listDir,
195
- indexPath: join2(listDir, "index.json"),
196
- metaPath: join2(listDir, "meta.json"),
197
336
  lockPath: join2(listDir, ".lock"),
198
- eventsPath: join2(listDir, "events.jsonl")
337
+ highwatermarkPath: join2(listDir, ".highwatermark")
199
338
  };
200
339
  }
340
+ function getTaskListPaths(listId) {
341
+ const context = getTaskListContext({ listId });
342
+ const cwd = getCwd();
343
+ const baseDir = resolveTaskBaseDir(context, cwd);
344
+ return buildTaskListPaths(baseDir, context.listId);
345
+ }
346
+ function initTaskListLockFile(options) {
347
+ const paths = getTaskListPaths(options?.listId);
348
+ ensureTaskListDirs(paths);
349
+ }
350
+ function sortReadPaths(paths) {
351
+ const priority = { pyb: 0, claude: 1, other: 2 };
352
+ return [...paths].sort((a, b) => priority[a.source] - priority[b.source]);
353
+ }
354
+ function resolveSourceForBaseDir(baseDir) {
355
+ if (baseDir.includes(`${join2(".pyb", "tasks")}`)) return "pyb";
356
+ if (baseDir.includes(`${join2(".claude", "tasks")}`)) return "claude";
357
+ if (baseDir.includes(`${join2(".pyb")}`)) return "pyb";
358
+ if (baseDir.includes(`${join2(".claude")}`)) return "claude";
359
+ return "other";
360
+ }
361
+ function getTaskListReadPaths(listId) {
362
+ const context = getTaskListContext({ listId });
363
+ const cwd = getCwd();
364
+ const projectPyb = join2(cwd, ".pyb", "tasks");
365
+ const projectClaude = join2(cwd, ".claude", "tasks");
366
+ if (context.scope === "project") {
367
+ const existing2 = [];
368
+ if (existsSync2(join2(projectPyb, context.listId))) {
369
+ existing2.push({
370
+ ...buildTaskListPaths(projectPyb, context.listId),
371
+ source: "pyb"
372
+ });
373
+ }
374
+ if (existsSync2(join2(projectClaude, context.listId))) {
375
+ existing2.push({
376
+ ...buildTaskListPaths(projectClaude, context.listId),
377
+ source: "claude"
378
+ });
379
+ }
380
+ if (existing2.length > 0) return sortReadPaths(existing2);
381
+ }
382
+ const override = process.env.PYB_CONFIG_DIR ?? process.env.CLAUDE_CONFIG_DIR;
383
+ const globalCandidates = override ? [
384
+ {
385
+ baseDir: join2(override, "tasks"),
386
+ source: resolveSourceForBaseDir(join2(override, "tasks"))
387
+ }
388
+ ] : [
389
+ { baseDir: join2(homedir(), ".pyb", "tasks"), source: "pyb" },
390
+ { baseDir: join2(homedir(), ".claude", "tasks"), source: "claude" }
391
+ ];
392
+ const candidates = [
393
+ ...globalCandidates,
394
+ { baseDir: resolveXdgDataPath("tasks"), source: "other" }
395
+ ];
396
+ const existing = candidates.filter((candidate) => existsSync2(join2(candidate.baseDir, context.listId))).map((candidate) => ({
397
+ ...buildTaskListPaths(candidate.baseDir, context.listId),
398
+ source: candidate.source
399
+ }));
400
+ if (existing.length > 0) return sortReadPaths(existing);
401
+ const baseDir = resolveTaskBaseDir(context, cwd);
402
+ return [
403
+ {
404
+ ...buildTaskListPaths(baseDir, context.listId),
405
+ source: resolveSourceForBaseDir(baseDir)
406
+ }
407
+ ];
408
+ }
201
409
  function ensureTaskListDirs(paths) {
202
410
  mkdirSync2(paths.listDir, { recursive: true });
411
+ if (!existsSync2(paths.lockPath)) {
412
+ writeFileSync2(paths.lockPath, "");
413
+ }
414
+ cleanupLegacyFiles(paths);
203
415
  }
204
416
  function readJson(path) {
205
417
  if (!existsSync2(path)) return null;
@@ -213,78 +425,27 @@ function readJson(path) {
213
425
  function writeJson(path, value) {
214
426
  writeFileSync2(path, JSON.stringify(value, null, 2), "utf8");
215
427
  }
216
- function appendTaskEvent(paths, event) {
217
- ensureTaskListDirs(paths);
218
- writeFileSync2(paths.eventsPath, `${JSON.stringify(event)}
219
- `, {
220
- encoding: "utf8",
221
- flag: "a"
222
- });
223
- }
224
- function readTaskEventLog(listId, offset = 0) {
225
- const paths = getTaskListPaths(listId);
226
- if (!existsSync2(paths.eventsPath)) {
227
- return { events: [], nextOffset: 0 };
228
- }
229
- const buffer = readFileSync2(paths.eventsPath);
230
- const nextOffset = buffer.length;
231
- if (offset >= nextOffset) {
232
- return { events: [], nextOffset };
233
- }
234
- const chunk = buffer.toString("utf8", offset);
235
- const events = [];
236
- for (const line of chunk.split("\n")) {
237
- if (!line.trim()) continue;
238
- try {
239
- const parsed = JSON.parse(line);
240
- if (parsed.listId && parsed.taskId && parsed.type && parsed.timestamp) {
241
- events.push(parsed);
242
- }
243
- } catch {
244
- continue;
245
- }
246
- }
247
- return { events, nextOffset };
248
- }
249
- function readIndex(paths) {
250
- const index = readJson(paths.indexPath);
251
- if (index && typeof index.nextId === "number" && index.nextId >= 1) {
252
- const byStatus = index.byStatus ?? {};
253
- const pending = typeof byStatus.pending === "number" ? byStatus.pending : typeof byStatus.open === "number" ? byStatus.open : 0;
254
- return {
255
- nextId: index.nextId,
256
- total: typeof index.total === "number" ? index.total : 0,
257
- byStatus: {
258
- pending,
259
- in_progress: typeof byStatus.in_progress === "number" ? byStatus.in_progress : 0,
260
- blocked: typeof byStatus.blocked === "number" ? byStatus.blocked : 0,
261
- done: typeof byStatus.done === "number" ? byStatus.done : 0,
262
- archived: typeof byStatus.archived === "number" ? byStatus.archived : 0
263
- },
264
- lastUpdated: typeof index.lastUpdated === "number" ? index.lastUpdated : Date.now()
265
- };
266
- }
267
- return recoverIndex(paths);
428
+ function writeJsonAtomic(path, value) {
429
+ const tempPath = `${path}.${randomUUID2()}.tmp`;
430
+ writeFileSync2(tempPath, JSON.stringify(value, null, 2), "utf8");
431
+ renameSync(tempPath, path);
268
432
  }
269
- function writeIndex(paths, index) {
270
- writeJson(paths.indexPath, index);
433
+ function readHighwatermark(paths) {
434
+ if (!existsSync2(paths.highwatermarkPath)) return null;
435
+ const raw = readFileSync2(paths.highwatermarkPath, "utf8").trim();
436
+ const value = Number(raw);
437
+ if (!Number.isFinite(value) || value < 0) return null;
438
+ return value;
271
439
  }
272
- function readMeta(paths) {
440
+ function writeHighwatermark(paths, value) {
273
441
  ensureTaskListDirs(paths);
274
- const meta = readJson(paths.metaPath);
275
- if (meta && meta.listId) return meta;
276
- const now = Date.now();
277
- const created = {
278
- listId: paths.listId,
279
- createdAt: now,
280
- updatedAt: now
281
- };
282
- writeJson(paths.metaPath, created);
283
- return created;
442
+ writeFileSync2(paths.highwatermarkPath, String(value), "utf8");
284
443
  }
285
- function writeMeta(paths, meta) {
286
- ensureTaskListDirs(paths);
287
- writeJson(paths.metaPath, meta);
444
+ function updateHighwatermark(paths, value) {
445
+ if (!Number.isFinite(value) || value < 0) return;
446
+ const current = readHighwatermark(paths);
447
+ const next = current !== null ? Math.max(current, value) : value;
448
+ writeHighwatermark(paths, next);
288
449
  }
289
450
  function readTaskFile(path) {
290
451
  const data = readJson(path);
@@ -358,6 +519,7 @@ function areBlocksEqual(a, b) {
358
519
  return true;
359
520
  }
360
521
  function readTasksFromDisk(paths) {
522
+ if (!existsSync2(paths.tasksDir)) return [];
361
523
  const files = readdirSync(paths.tasksDir).filter((file) => file.endsWith(".json"));
362
524
  return files.map((file) => readTaskFile(join2(paths.tasksDir, file))).filter(Boolean);
363
525
  }
@@ -390,71 +552,123 @@ function rebuildBlocksForPaths(paths) {
390
552
  const updated = persistBlocksMap(paths, map);
391
553
  return { tasks: applyBlocksMap(tasks, map), updated };
392
554
  }
393
- function recoverIndex(paths) {
394
- ensureTaskListDirs(paths);
555
+ var legacyFiles = ["index.json", "meta.json", "events.jsonl"];
556
+ function cleanupLegacyFiles(paths) {
557
+ for (const file of legacyFiles) {
558
+ const path = join2(paths.listDir, file);
559
+ if (existsSync2(path)) {
560
+ rmSync(path, { force: true });
561
+ }
562
+ }
563
+ }
564
+ function getMaxIdFromTasksDir(paths) {
565
+ if (!existsSync2(paths.tasksDir)) return 0;
395
566
  const files = readdirSync(paths.tasksDir).filter((file) => file.endsWith(".json"));
396
- const tasks = files.map((file) => readTaskFile(join2(paths.tasksDir, file))).filter(Boolean);
397
- const byStatus = {
398
- pending: 0,
399
- in_progress: 0,
400
- blocked: 0,
401
- done: 0,
402
- archived: 0
403
- };
404
567
  let maxId = 0;
405
- for (const task of tasks) {
406
- byStatus[task.status] += 1;
407
- const numericId = Number(task.id);
408
- if (!Number.isNaN(numericId)) {
409
- maxId = Math.max(maxId, numericId);
568
+ for (const file of files) {
569
+ const numeric = Number(file.replace(/\.json$/, ""));
570
+ if (Number.isFinite(numeric)) {
571
+ maxId = Math.max(maxId, numeric);
410
572
  }
411
573
  }
412
- const index = {
413
- nextId: Math.max(1, maxId + 1),
414
- total: tasks.length,
415
- byStatus,
416
- lastUpdated: Date.now()
417
- };
418
- writeIndex(paths, index);
419
- return index;
420
- }
421
- function readLock(path) {
422
- const lock = readJson(path);
423
- if (!lock || !lock.createdAt || !lock.expiresAt) return null;
424
- return lock;
574
+ return maxId;
425
575
  }
426
- function isLockExpired(lock) {
427
- return lock.expiresAt <= Date.now();
576
+ function getNextTaskId(paths) {
577
+ const highwatermark = readHighwatermark(paths) ?? 0;
578
+ const maxId = getMaxIdFromTasksDir(paths);
579
+ return Math.max(highwatermark, maxId) + 1;
428
580
  }
429
581
  function acquireLock(paths) {
430
582
  ensureTaskListDirs(paths);
431
- const lock = readLock(paths.lockPath);
432
- if (lock && !isLockExpired(lock)) {
433
- throw new TaskStoreLockError("Task list is locked");
434
- }
435
- if (lock && isLockExpired(lock)) {
436
- rmSync(paths.lockPath, { force: true });
583
+ const start = Date.now();
584
+ while (taskListLocks.has(paths.lockPath)) {
585
+ const elapsed = Date.now() - start;
586
+ if (elapsed >= DEFAULT_LOCK_TIMEOUT_MS) {
587
+ throw new TaskStoreLockError("Task list is locked");
588
+ }
589
+ Atomics.wait(taskLockWaitBuffer, 0, 0, DEFAULT_LOCK_RETRY_MS);
437
590
  }
438
- const now = Date.now();
439
- try {
440
- writeFileSync2(
441
- paths.lockPath,
442
- JSON.stringify(
443
- {
444
- createdAt: now,
445
- expiresAt: now + DEFAULT_LOCK_TTL_MS
446
- },
447
- null,
448
- 2
449
- ),
450
- { encoding: "utf8", flag: "wx" }
591
+ const native = getNativeLock();
592
+ if (!native) {
593
+ const platformLabel = `${process.platform}-${process.arch}`;
594
+ const packageName = resolveNativeLockPackage();
595
+ if (packageName) {
596
+ throw new TaskStoreLockError(
597
+ `Native lock unavailable. Missing package ${packageName} for ${platformLabel}`
598
+ );
599
+ }
600
+ throw new TaskStoreLockError(
601
+ `Native lock unavailable. Unsupported platform ${platformLabel}`
451
602
  );
452
- } catch {
453
- throw new TaskStoreLockError("Task list is locked");
603
+ }
604
+ if (native.tryLockExclusivePath && native.unlockHandle) {
605
+ const lockKey = paths.lockPath;
606
+ while (true) {
607
+ try {
608
+ const handle = native.tryLockExclusivePath(lockKey);
609
+ if (handle > 0) {
610
+ taskListLocks.set(paths.lockPath, { handle, mode: "nativeHandle" });
611
+ return;
612
+ }
613
+ } catch {
614
+ }
615
+ const elapsed = Date.now() - start;
616
+ if (elapsed >= DEFAULT_LOCK_TIMEOUT_MS) {
617
+ throw new TaskStoreLockError("Task list is locked");
618
+ }
619
+ Atomics.wait(taskLockWaitBuffer, 0, 0, DEFAULT_LOCK_RETRY_MS);
620
+ }
621
+ }
622
+ while (true) {
623
+ const handle = openSync(paths.lockPath, "r+");
624
+ try {
625
+ const ok = native.tryLockExclusive(handle);
626
+ if (ok) {
627
+ taskListLocks.set(paths.lockPath, { handle, mode: "fd" });
628
+ return;
629
+ }
630
+ try {
631
+ closeSync(handle);
632
+ } catch {
633
+ }
634
+ const elapsed = Date.now() - start;
635
+ if (elapsed >= DEFAULT_LOCK_TIMEOUT_MS) {
636
+ throw new TaskStoreLockError("Task list is locked");
637
+ }
638
+ Atomics.wait(taskLockWaitBuffer, 0, 0, DEFAULT_LOCK_RETRY_MS);
639
+ } catch {
640
+ try {
641
+ closeSync(handle);
642
+ } catch {
643
+ }
644
+ const elapsed = Date.now() - start;
645
+ if (elapsed >= DEFAULT_LOCK_TIMEOUT_MS) {
646
+ throw new TaskStoreLockError("Task list is locked");
647
+ }
648
+ Atomics.wait(taskLockWaitBuffer, 0, 0, DEFAULT_LOCK_RETRY_MS);
649
+ }
454
650
  }
455
651
  }
456
652
  function releaseLock(paths) {
457
- rmSync(paths.lockPath, { force: true });
653
+ const lock = taskListLocks.get(paths.lockPath);
654
+ if (lock !== void 0) {
655
+ const native = getNativeLock();
656
+ if (native && lock.mode === "nativeHandle" && native.unlockHandle) {
657
+ try {
658
+ native.unlockHandle(lock.handle);
659
+ } catch {
660
+ }
661
+ } else {
662
+ if (native) {
663
+ try {
664
+ native.unlock(lock.handle);
665
+ } catch {
666
+ }
667
+ }
668
+ closeSync(lock.handle);
669
+ }
670
+ taskListLocks.delete(paths.lockPath);
671
+ }
458
672
  }
459
673
  function withTaskListLock(paths, handler) {
460
674
  acquireLock(paths);
@@ -464,6 +678,18 @@ function withTaskListLock(paths, handler) {
464
678
  releaseLock(paths);
465
679
  }
466
680
  }
681
+ function mergeMetadata(existing, updates) {
682
+ if (!updates) return existing;
683
+ const next = { ...existing ?? {} };
684
+ for (const [key, value] of Object.entries(updates)) {
685
+ if (value === null) {
686
+ delete next[key];
687
+ } else {
688
+ next[key] = value;
689
+ }
690
+ }
691
+ return Object.keys(next).length > 0 ? next : void 0;
692
+ }
467
693
  function applyTaskUpdates(task, updates, nextBaseVersion) {
468
694
  const now = updates.updatedAt ?? Date.now();
469
695
  const subject = updates.subject ?? task.subject;
@@ -479,7 +705,7 @@ function applyTaskUpdates(task, updates, nextBaseVersion) {
479
705
  status,
480
706
  tags: updates.tags ?? task.tags,
481
707
  assignee: updates.assignee ?? task.assignee,
482
- metadata: updates.metadata ?? task.metadata,
708
+ metadata: mergeMetadata(task.metadata, updates.metadata),
483
709
  archived: updates.archived ?? task.archived,
484
710
  blocks: task.blocks ?? [],
485
711
  blockedBy,
@@ -498,16 +724,12 @@ function createTask(input, options) {
498
724
  const paths = getTaskListPaths(options?.listId);
499
725
  return withTaskListLock(paths, () => {
500
726
  ensureTaskListDirs(paths);
501
- const index = readIndex(paths);
502
- const id = String(index.nextId);
727
+ const id = String(getNextTaskId(paths));
503
728
  const now = Date.now();
504
729
  const subject = input.subject;
505
730
  if (!subject) {
506
731
  throw new Error("Task subject is required");
507
732
  }
508
- if (!input.activeForm) {
509
- throw new Error("Task activeForm is required");
510
- }
511
733
  const activeForm = input.activeForm;
512
734
  const status = input.status ?? "pending";
513
735
  if (status === "open") {
@@ -534,50 +756,55 @@ function createTask(input, options) {
534
756
  legacyTodoId: input.legacyTodoId
535
757
  };
536
758
  writeJson(join2(paths.tasksDir, `${id}.json`), record);
537
- index.nextId += 1;
538
- index.total += 1;
539
- index.byStatus[record.status] += 1;
540
- index.lastUpdated = Date.now();
541
- writeIndex(paths, index);
542
- const meta = readMeta(paths);
543
- meta.updatedAt = Date.now();
544
- writeMeta(paths, meta);
545
- appendTaskEvent(paths, {
546
- listId: paths.listId,
547
- taskId: record.id,
548
- type: "task.created",
549
- timestamp: Date.now()
550
- });
759
+ updateHighwatermark(paths, Number(id));
551
760
  const { tasks } = rebuildBlocksForPaths(paths);
552
761
  const refreshed = tasks.find((task) => task.id === record.id);
553
762
  return refreshed ?? record;
554
763
  });
555
764
  }
556
765
  function getTask(id, options) {
557
- const tasks = listTasks(options);
558
- return tasks.find((task) => task.id === id) ?? null;
766
+ const pathsList = getTaskListReadPaths(options?.listId);
767
+ for (const paths of pathsList) {
768
+ const taskPath = join2(paths.tasksDir, `${id}.json`);
769
+ if (!existsSync2(taskPath)) continue;
770
+ const task = readTaskFile(taskPath);
771
+ if (task) return task;
772
+ }
773
+ return null;
559
774
  }
560
775
  function listTasks(options) {
561
- const paths = getTaskListPaths(options?.listId);
562
- readIndex(paths);
563
- ensureTaskListDirs(paths);
564
- const tasks = readTasksFromDisk(paths);
565
- const map = deriveBlocks(tasks);
566
- const derived = applyBlocksMap(tasks, map);
567
- const needsPersist = tasks.some((task) => {
568
- const nextBlocks = map.get(task.id) ?? [];
569
- return !areBlocksEqual(task.blocks, nextBlocks);
570
- });
571
- const ready = needsPersist ? withTaskListLock(paths, () => rebuildBlocksForPaths(paths).tasks) : derived;
572
- return ready.sort((a, b) => Number(a.id) - Number(b.id));
776
+ const pathsList = getTaskListReadPaths(options?.listId);
777
+ const merged = /* @__PURE__ */ new Map();
778
+ for (const paths of pathsList) {
779
+ const tasks = readTasksFromDisk(paths);
780
+ const map = deriveBlocks(tasks);
781
+ const derived = applyBlocksMap(tasks, map);
782
+ const needsPersist = tasks.some((task) => {
783
+ const nextBlocks = map.get(task.id) ?? [];
784
+ return !areBlocksEqual(task.blocks, nextBlocks);
785
+ });
786
+ const ready = needsPersist ? withTaskListLock(paths, () => rebuildBlocksForPaths(paths).tasks) : derived;
787
+ for (const task of ready) {
788
+ if (!merged.has(task.id)) {
789
+ merged.set(task.id, task);
790
+ }
791
+ }
792
+ }
793
+ return Array.from(merged.values()).sort((a, b) => Number(a.id) - Number(b.id));
573
794
  }
574
795
  function updateTask(id, updates, options) {
575
796
  if (updates.status === "open") {
576
797
  throw new Error("Task status open is not supported");
577
798
  }
578
- const paths = getTaskListPaths(options?.listId);
579
- return withTaskListLock(paths, () => {
580
- const existing = readTaskFile(join2(paths.tasksDir, `${id}.json`));
799
+ const pathsList = getTaskListReadPaths(options?.listId);
800
+ const target = pathsList.find(
801
+ (paths) => existsSync2(join2(paths.tasksDir, `${id}.json`))
802
+ );
803
+ if (!target) {
804
+ throw new Error(`Task ${id} not found`);
805
+ }
806
+ return withTaskListLock(target, () => {
807
+ const existing = readTaskFile(join2(target.tasksDir, `${id}.json`));
581
808
  if (!existing) {
582
809
  throw new Error(`Task ${id} not found`);
583
810
  }
@@ -598,27 +825,8 @@ function updateTask(id, updates, options) {
598
825
  }
599
826
  const nextBaseVersion = existing.baseVersion + 1;
600
827
  const updated = applyTaskUpdates(existing, updates, nextBaseVersion);
601
- writeJson(join2(paths.tasksDir, `${id}.json`), updated);
602
- const index = readIndex(paths);
603
- if (updated.status !== existing.status) {
604
- index.byStatus[existing.status] = Math.max(
605
- 0,
606
- index.byStatus[existing.status] - 1
607
- );
608
- index.byStatus[updated.status] += 1;
609
- }
610
- index.lastUpdated = Date.now();
611
- writeIndex(paths, index);
612
- const meta = readMeta(paths);
613
- meta.updatedAt = Date.now();
614
- writeMeta(paths, meta);
615
- appendTaskEvent(paths, {
616
- listId: paths.listId,
617
- taskId: updated.id,
618
- type: "task.updated",
619
- timestamp: Date.now()
620
- });
621
- const { tasks } = rebuildBlocksForPaths(paths);
828
+ writeJsonAtomic(join2(target.tasksDir, `${id}.json`), updated);
829
+ const { tasks } = rebuildBlocksForPaths(target);
622
830
  const refreshed = tasks.find((task) => task.id === updated.id);
623
831
  return { task: refreshed ?? updated, conflict: hasConflict };
624
832
  });
@@ -626,26 +834,16 @@ function updateTask(id, updates, options) {
626
834
  function rebuildTaskBlocks(options) {
627
835
  const paths = getTaskListPaths(options?.listId);
628
836
  return withTaskListLock(paths, () => {
629
- readIndex(paths);
630
837
  ensureTaskListDirs(paths);
631
838
  const { tasks, updated } = rebuildBlocksForPaths(paths);
632
839
  return { updated, total: tasks.length };
633
840
  });
634
841
  }
635
- function getTaskListMeta(options) {
636
- const paths = getTaskListPaths(options?.listId);
637
- return readMeta(paths);
638
- }
639
- function updateTaskListMeta(meta, options) {
640
- const paths = getTaskListPaths(options?.listId);
641
- const updated = { ...meta, updatedAt: Date.now() };
642
- writeMeta(paths, updated);
643
- return updated;
644
- }
645
842
 
646
843
  // src/utils/session/todoStorage.ts
647
844
  var TODO_STORAGE_KEY = "todos";
648
845
  var TODO_CONFIG_KEY = "todoConfig";
846
+ var TODO_MIGRATION_KEY = "todoMigration";
649
847
  var DEFAULT_CONFIG = {
650
848
  maxTodos: 100,
651
849
  autoArchiveCompleted: false,
@@ -710,22 +908,24 @@ function syncTodosToTasks(todos, options) {
710
908
  }
711
909
  function migrateTodosToTasks(options) {
712
910
  const { listId } = getTaskListPaths(options?.listId);
713
- const meta = getTaskListMeta({ listId });
714
- if (meta.todoMigration?.completedAt) {
911
+ const migrationKey = `${listId}:${options?.agentId ?? "default"}`;
912
+ const sessionState = getSessionState();
913
+ const migrationState = sessionState[TODO_MIGRATION_KEY] ?? {};
914
+ if (migrationState[migrationKey]?.completedAt) {
715
915
  return { listId, migrated: false, created: 0 };
716
916
  }
717
917
  const todos = getTodos(options?.agentId);
718
918
  const result = syncTodosToTasks(todos, { listId });
719
- updateTaskListMeta(
720
- {
721
- ...meta,
722
- todoMigration: {
919
+ setSessionState({
920
+ ...sessionState,
921
+ [TODO_MIGRATION_KEY]: {
922
+ ...migrationState,
923
+ [migrationKey]: {
723
924
  completedAt: Date.now(),
724
925
  sourceCount: todos.length
725
926
  }
726
- },
727
- { listId }
728
- );
927
+ }
928
+ });
729
929
  return { listId, migrated: true, created: result.created };
730
930
  }
731
931
  function invalidateCache() {
@@ -903,38 +1103,10 @@ var SystemReminderService = class {
903
1103
  "todo",
904
1104
  "task",
905
1105
  "medium",
906
- "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.",
1106
+ "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 once to provide a complete task list (no dependencies); the tool will finalize dependencies internally. Use TaskList to review readiness. If not, please feel free to ignore. Again do not mention this message to the user.",
907
1107
  currentTime
908
1108
  );
909
1109
  }
910
- if (todos.length > 0) {
911
- const reminderKey = `todo_updated_${agentKey}_${todos.length}_${this.getTodoStateHash(todos)}`;
912
- if (this.reminderCache.has(reminderKey)) {
913
- return this.reminderCache.get(reminderKey);
914
- }
915
- if (!this.sessionState.remindersSent.has(reminderKey)) {
916
- this.sessionState.remindersSent.add(reminderKey);
917
- this.clearTodoReminders(agentKey);
918
- const todoContent = JSON.stringify(
919
- todos.map((todo) => ({
920
- content: todo.content.length > 100 ? todo.content.substring(0, 100) + "..." : todo.content,
921
- status: todo.status,
922
- activeForm: todo.activeForm && todo.activeForm.length > 100 ? todo.activeForm.substring(0, 100) + "..." : todo.activeForm || todo.content
923
- }))
924
- );
925
- const reminder = this.createReminderMessage(
926
- "todo",
927
- "task",
928
- "medium",
929
- `Your task items have changed. DO NOT mention this explicitly to the user. Here are the latest contents of your tracked items:
930
-
931
- ${todoContent}. Use TaskList to review readiness and TaskUpdate to adjust status if needed.`,
932
- currentTime
933
- );
934
- this.reminderCache.set(reminderKey, reminder);
935
- return reminder;
936
- }
937
- }
938
1110
  return null;
939
1111
  }
940
1112
  dispatchSecurityEvent() {
@@ -1357,9 +1529,12 @@ var resetReminderSession = () => systemReminderService.resetSession();
1357
1529
  export {
1358
1530
  getAgentFilePath,
1359
1531
  generateAgentId,
1532
+ generateTaskListId,
1533
+ runWithTaskListId,
1534
+ runWithTaskListEnv,
1360
1535
  TaskStoreConflictError,
1361
1536
  getTaskListPaths,
1362
- readTaskEventLog,
1537
+ initTaskListLockFile,
1363
1538
  createTask,
1364
1539
  getTask,
1365
1540
  listTasks,