@langadventurellc/task-trellis-mcp 1.1.0 → 1.2.1

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 (262) hide show
  1. package/README.md +37 -323
  2. package/dist/__tests__/e2e/autoPrune.e2e.test.d.ts +2 -0
  3. package/dist/__tests__/e2e/autoPrune.e2e.test.d.ts.map +1 -0
  4. package/dist/__tests__/e2e/autoPrune.e2e.test.js +533 -0
  5. package/dist/__tests__/e2e/autoPrune.e2e.test.js.map +1 -0
  6. package/dist/__tests__/e2e/configuration/activation.e2e.test.js +6 -6
  7. package/dist/__tests__/e2e/configuration/activation.e2e.test.js.map +1 -1
  8. package/dist/__tests__/e2e/configuration/commandLineArgs.e2e.test.js +55 -6
  9. package/dist/__tests__/e2e/configuration/commandLineArgs.e2e.test.js.map +1 -1
  10. package/dist/__tests__/e2e/configuration/directorySetup.e2e.test.js +19 -19
  11. package/dist/__tests__/e2e/configuration/directorySetup.e2e.test.js.map +1 -1
  12. package/dist/__tests__/e2e/configuration/invalidConfig.e2e.test.js +9 -9
  13. package/dist/__tests__/e2e/configuration/invalidConfig.e2e.test.js.map +1 -1
  14. package/dist/__tests__/e2e/configuration/preActivation.e2e.test.js +15 -15
  15. package/dist/__tests__/e2e/configuration/preActivation.e2e.test.js.map +1 -1
  16. package/dist/__tests__/e2e/crud/createObject.e2e.test.js +34 -34
  17. package/dist/__tests__/e2e/crud/createObject.e2e.test.js.map +1 -1
  18. package/dist/__tests__/e2e/crud/deleteObject.e2e.test.js +19 -19
  19. package/dist/__tests__/e2e/crud/deleteObject.e2e.test.js.map +1 -1
  20. package/dist/__tests__/e2e/crud/fileValidation.e2e.test.js +32 -32
  21. package/dist/__tests__/e2e/crud/fileValidation.e2e.test.js.map +1 -1
  22. package/dist/__tests__/e2e/crud/getObject.e2e.test.js +143 -22
  23. package/dist/__tests__/e2e/crud/getObject.e2e.test.js.map +1 -1
  24. package/dist/__tests__/e2e/crud/listObjects.e2e.test.js +561 -42
  25. package/dist/__tests__/e2e/crud/listObjects.e2e.test.js.map +1 -1
  26. package/dist/__tests__/e2e/crud/updateObject.e2e.test.js +497 -25
  27. package/dist/__tests__/e2e/crud/updateObject.e2e.test.js.map +1 -1
  28. package/dist/__tests__/e2e/hierarchicalPrerequisites.e2e.test.d.ts +2 -0
  29. package/dist/__tests__/e2e/hierarchicalPrerequisites.e2e.test.d.ts.map +1 -0
  30. package/dist/__tests__/e2e/hierarchicalPrerequisites.e2e.test.js +319 -0
  31. package/dist/__tests__/e2e/hierarchicalPrerequisites.e2e.test.js.map +1 -0
  32. package/dist/__tests__/e2e/infrastructure/client.e2e.test.js +4 -4
  33. package/dist/__tests__/e2e/infrastructure/client.e2e.test.js.map +1 -1
  34. package/dist/__tests__/e2e/infrastructure/server.e2e.test.js +6 -6
  35. package/dist/__tests__/e2e/infrastructure/server.e2e.test.js.map +1 -1
  36. package/dist/__tests__/e2e/utils/extractObjectIds.d.ts +1 -1
  37. package/dist/__tests__/e2e/utils/extractObjectIds.js +1 -1
  38. package/dist/__tests__/e2e/utils/mcpTestClient.d.ts.map +1 -1
  39. package/dist/__tests__/e2e/utils/mcpTestClient.js +3 -2
  40. package/dist/__tests__/e2e/utils/mcpTestClient.js.map +1 -1
  41. package/dist/__tests__/e2e/utils/parseListObjectsResponse.d.ts +1 -1
  42. package/dist/__tests__/e2e/utils/parseListObjectsResponse.js +1 -1
  43. package/dist/__tests__/e2e/utils/parseUpdateObjectResponse.d.ts +1 -1
  44. package/dist/__tests__/e2e/utils/parseUpdateObjectResponse.js +1 -1
  45. package/dist/__tests__/e2e/workflow/appendLog.e2e.test.js +4 -4
  46. package/dist/__tests__/e2e/workflow/appendLog.e2e.test.js.map +1 -1
  47. package/dist/__tests__/e2e/workflow/appendModifiedFiles.e2e.test.js +15 -15
  48. package/dist/__tests__/e2e/workflow/appendModifiedFiles.e2e.test.js.map +1 -1
  49. package/dist/__tests__/e2e/workflow/claimTask.e2e.test.js +44 -0
  50. package/dist/__tests__/e2e/workflow/claimTask.e2e.test.js.map +1 -1
  51. package/dist/__tests__/e2e/workflow/completeTask.e2e.test.js +4 -4
  52. package/dist/__tests__/e2e/workflow/completeTask.e2e.test.js.map +1 -1
  53. package/dist/__tests__/e2e/workflow/prerequisites.e2e.test.js +43 -43
  54. package/dist/__tests__/e2e/workflow/prerequisites.e2e.test.js.map +1 -1
  55. package/dist/__tests__/e2e/workflow/taskLifecycle.e2e.test.js +19 -19
  56. package/dist/__tests__/e2e/workflow/taskLifecycle.e2e.test.js.map +1 -1
  57. package/dist/__tests__/serverStartup.test.d.ts +2 -0
  58. package/dist/__tests__/serverStartup.test.d.ts.map +1 -0
  59. package/dist/__tests__/serverStartup.test.js +171 -0
  60. package/dist/__tests__/serverStartup.test.js.map +1 -0
  61. package/dist/configuration/ServerConfig.d.ts +2 -1
  62. package/dist/configuration/ServerConfig.d.ts.map +1 -1
  63. package/dist/repositories/Repository.d.ts +2 -1
  64. package/dist/repositories/Repository.d.ts.map +1 -1
  65. package/dist/repositories/local/LocalRepository.d.ts +2 -1
  66. package/dist/repositories/local/LocalRepository.d.ts.map +1 -1
  67. package/dist/repositories/local/LocalRepository.js +4 -0
  68. package/dist/repositories/local/LocalRepository.js.map +1 -1
  69. package/dist/repositories/local/__tests__/getChildrenOf.test.d.ts +2 -0
  70. package/dist/repositories/local/__tests__/getChildrenOf.test.d.ts.map +1 -0
  71. package/dist/repositories/local/__tests__/getChildrenOf.test.js +306 -0
  72. package/dist/repositories/local/__tests__/getChildrenOf.test.js.map +1 -0
  73. package/dist/repositories/local/__tests__/getObjects.test.js +309 -0
  74. package/dist/repositories/local/__tests__/getObjects.test.js.map +1 -1
  75. package/dist/repositories/local/deleteObjectById.d.ts.map +1 -1
  76. package/dist/repositories/local/deleteObjectById.js +2 -0
  77. package/dist/repositories/local/deleteObjectById.js.map +1 -1
  78. package/dist/repositories/local/getChildrenOf.d.ts +11 -0
  79. package/dist/repositories/local/getChildrenOf.d.ts.map +1 -0
  80. package/dist/repositories/local/getChildrenOf.js +73 -0
  81. package/dist/repositories/local/getChildrenOf.js.map +1 -0
  82. package/dist/repositories/local/getObjects.d.ts +1 -1
  83. package/dist/repositories/local/getObjects.d.ts.map +1 -1
  84. package/dist/repositories/local/getObjects.js +25 -6
  85. package/dist/repositories/local/getObjects.js.map +1 -1
  86. package/dist/server.js +39 -15
  87. package/dist/server.js.map +1 -1
  88. package/dist/services/TaskTrellisService.d.ts +4 -13
  89. package/dist/services/TaskTrellisService.d.ts.map +1 -1
  90. package/dist/services/local/LocalTaskTrellisService.d.ts +3 -9
  91. package/dist/services/local/LocalTaskTrellisService.d.ts.map +1 -1
  92. package/dist/services/local/LocalTaskTrellisService.js +4 -8
  93. package/dist/services/local/LocalTaskTrellisService.js.map +1 -1
  94. package/dist/services/local/__tests__/appendModifiedFiles.test.js +1 -0
  95. package/dist/services/local/__tests__/appendModifiedFiles.test.js.map +1 -1
  96. package/dist/services/local/__tests__/appendObjectLog.test.js +1 -0
  97. package/dist/services/local/__tests__/appendObjectLog.test.js.map +1 -1
  98. package/dist/services/local/__tests__/claimTask.test.js +127 -131
  99. package/dist/services/local/__tests__/claimTask.test.js.map +1 -1
  100. package/dist/services/local/__tests__/completeTask.test.js +30 -28
  101. package/dist/services/local/__tests__/completeTask.test.js.map +1 -1
  102. package/dist/services/local/__tests__/createObject.test.js +1 -0
  103. package/dist/services/local/__tests__/createObject.test.js.map +1 -1
  104. package/dist/services/local/__tests__/listObjects.test.js +135 -10
  105. package/dist/services/local/__tests__/listObjects.test.js.map +1 -1
  106. package/dist/services/local/__tests__/pruneClosed.test.js +446 -186
  107. package/dist/services/local/__tests__/pruneClosed.test.js.map +1 -1
  108. package/dist/services/local/__tests__/updateObject.test.js +234 -27
  109. package/dist/services/local/__tests__/updateObject.test.js.map +1 -1
  110. package/dist/services/local/claimTask.d.ts.map +1 -1
  111. package/dist/services/local/claimTask.js +25 -34
  112. package/dist/services/local/claimTask.js.map +1 -1
  113. package/dist/services/local/completeTask.d.ts +1 -1
  114. package/dist/services/local/completeTask.d.ts.map +1 -1
  115. package/dist/services/local/completeTask.js +4 -40
  116. package/dist/services/local/completeTask.js.map +1 -1
  117. package/dist/services/local/listObjects.d.ts +1 -1
  118. package/dist/services/local/listObjects.d.ts.map +1 -1
  119. package/dist/services/local/listObjects.js +10 -1
  120. package/dist/services/local/listObjects.js.map +1 -1
  121. package/dist/services/local/pruneClosed.d.ts.map +1 -1
  122. package/dist/services/local/pruneClosed.js +63 -6
  123. package/dist/services/local/pruneClosed.js.map +1 -1
  124. package/dist/services/local/updateObject.d.ts +2 -1
  125. package/dist/services/local/updateObject.d.ts.map +1 -1
  126. package/dist/services/local/updateObject.js +28 -1
  127. package/dist/services/local/updateObject.js.map +1 -1
  128. package/dist/tools/__tests__/appendModifiedFilesTool.test.js +1 -0
  129. package/dist/tools/__tests__/appendModifiedFilesTool.test.js.map +1 -1
  130. package/dist/tools/__tests__/appendObjectLogTool.test.js +1 -0
  131. package/dist/tools/__tests__/appendObjectLogTool.test.js.map +1 -1
  132. package/dist/tools/__tests__/claimTaskTool.test.js +1 -1
  133. package/dist/tools/__tests__/claimTaskTool.test.js.map +1 -1
  134. package/dist/tools/__tests__/completeTaskTool.test.js +23 -16
  135. package/dist/tools/__tests__/completeTaskTool.test.js.map +1 -1
  136. package/dist/tools/__tests__/createObjectTool.test.js +1 -0
  137. package/dist/tools/__tests__/createObjectTool.test.js.map +1 -1
  138. package/dist/tools/__tests__/deleteObjectTool.test.js +1 -0
  139. package/dist/tools/__tests__/deleteObjectTool.test.js.map +1 -1
  140. package/dist/tools/__tests__/getObjectTool.test.js +1 -0
  141. package/dist/tools/__tests__/getObjectTool.test.js.map +1 -1
  142. package/dist/tools/__tests__/listObjectsTool.test.js +160 -1
  143. package/dist/tools/__tests__/listObjectsTool.test.js.map +1 -1
  144. package/dist/tools/__tests__/updateObjectTool.test.js +39 -10
  145. package/dist/tools/__tests__/updateObjectTool.test.js.map +1 -1
  146. package/dist/tools/appendModifiedFilesTool.d.ts +2 -2
  147. package/dist/tools/appendModifiedFilesTool.js +4 -4
  148. package/dist/tools/appendModifiedFilesTool.js.map +1 -1
  149. package/dist/tools/appendObjectLogTool.d.ts +3 -3
  150. package/dist/tools/appendObjectLogTool.js +4 -4
  151. package/dist/tools/appendObjectLogTool.js.map +1 -1
  152. package/dist/tools/completeTaskTool.d.ts +1 -1
  153. package/dist/tools/completeTaskTool.d.ts.map +1 -1
  154. package/dist/tools/completeTaskTool.js +1 -1
  155. package/dist/tools/completeTaskTool.js.map +1 -1
  156. package/dist/tools/createObjectTool.d.ts +8 -8
  157. package/dist/tools/createObjectTool.js +13 -13
  158. package/dist/tools/createObjectTool.js.map +1 -1
  159. package/dist/tools/deleteObjectTool.d.ts +3 -3
  160. package/dist/tools/deleteObjectTool.js +12 -12
  161. package/dist/tools/deleteObjectTool.js.map +1 -1
  162. package/dist/tools/getObjectTool.d.ts +3 -3
  163. package/dist/tools/getObjectTool.d.ts.map +1 -1
  164. package/dist/tools/getObjectTool.js +12 -7
  165. package/dist/tools/getObjectTool.js.map +1 -1
  166. package/dist/tools/index.d.ts +7 -9
  167. package/dist/tools/index.d.ts.map +1 -1
  168. package/dist/tools/index.js +22 -28
  169. package/dist/tools/index.js.map +1 -1
  170. package/dist/tools/listObjectsTool.d.ts +20 -11
  171. package/dist/tools/listObjectsTool.d.ts.map +1 -1
  172. package/dist/tools/listObjectsTool.js +112 -40
  173. package/dist/tools/listObjectsTool.js.map +1 -1
  174. package/dist/tools/updateObjectTool.d.ts +12 -7
  175. package/dist/tools/updateObjectTool.d.ts.map +1 -1
  176. package/dist/tools/updateObjectTool.js +20 -14
  177. package/dist/tools/updateObjectTool.js.map +1 -1
  178. package/dist/utils/__tests__/checkHierarchicalPrerequisitesComplete.test.d.ts +2 -0
  179. package/dist/utils/__tests__/checkHierarchicalPrerequisitesComplete.test.d.ts.map +1 -0
  180. package/dist/utils/__tests__/checkHierarchicalPrerequisitesComplete.test.js +206 -0
  181. package/dist/utils/__tests__/checkHierarchicalPrerequisitesComplete.test.js.map +1 -0
  182. package/dist/utils/__tests__/checkPrerequisitesComplete.test.js +5 -0
  183. package/dist/utils/__tests__/checkPrerequisitesComplete.test.js.map +1 -1
  184. package/dist/utils/__tests__/filterUnavailableObjects.test.js +51 -25
  185. package/dist/utils/__tests__/filterUnavailableObjects.test.js.map +1 -1
  186. package/dist/utils/__tests__/isRequiredForOtherObjects.test.js +5 -0
  187. package/dist/utils/__tests__/isRequiredForOtherObjects.test.js.map +1 -1
  188. package/dist/utils/__tests__/updateParentHierarchy.test.d.ts +2 -0
  189. package/dist/utils/__tests__/updateParentHierarchy.test.d.ts.map +1 -0
  190. package/dist/utils/__tests__/updateParentHierarchy.test.js +137 -0
  191. package/dist/utils/__tests__/updateParentHierarchy.test.js.map +1 -0
  192. package/dist/utils/autoCompleteParentHierarchy.d.ts +11 -0
  193. package/dist/utils/autoCompleteParentHierarchy.d.ts.map +1 -0
  194. package/dist/utils/autoCompleteParentHierarchy.js +49 -0
  195. package/dist/utils/autoCompleteParentHierarchy.js.map +1 -0
  196. package/dist/utils/checkHierarchicalPrerequisitesComplete.d.ts +14 -0
  197. package/dist/utils/checkHierarchicalPrerequisitesComplete.d.ts.map +1 -0
  198. package/dist/utils/checkHierarchicalPrerequisitesComplete.js +47 -0
  199. package/dist/utils/checkHierarchicalPrerequisitesComplete.js.map +1 -0
  200. package/dist/utils/filterUnavailableObjects.d.ts +6 -4
  201. package/dist/utils/filterUnavailableObjects.d.ts.map +1 -1
  202. package/dist/utils/filterUnavailableObjects.js +16 -22
  203. package/dist/utils/filterUnavailableObjects.js.map +1 -1
  204. package/dist/utils/index.d.ts +3 -2
  205. package/dist/utils/index.d.ts.map +1 -1
  206. package/dist/utils/index.js +7 -3
  207. package/dist/utils/index.js.map +1 -1
  208. package/dist/utils/updateParentHierarchy.d.ts +11 -0
  209. package/dist/utils/updateParentHierarchy.d.ts.map +1 -0
  210. package/dist/utils/updateParentHierarchy.js +40 -0
  211. package/dist/utils/updateParentHierarchy.js.map +1 -0
  212. package/dist/validation/__tests__/validateObjectCreation.test.js +1 -0
  213. package/dist/validation/__tests__/validateObjectCreation.test.js.map +1 -1
  214. package/dist/validation/__tests__/validateParentExists.test.js +1 -0
  215. package/dist/validation/__tests__/validateParentExists.test.js.map +1 -1
  216. package/dist/validation/__tests__/validateStatusTransition.test.js +1 -0
  217. package/dist/validation/__tests__/validateStatusTransition.test.js.map +1 -1
  218. package/package.json +1 -1
  219. package/dist/__tests__/e2e/crud/replaceObjectBodyRegex.e2e.test.d.ts +0 -2
  220. package/dist/__tests__/e2e/crud/replaceObjectBodyRegex.e2e.test.d.ts.map +0 -1
  221. package/dist/__tests__/e2e/crud/replaceObjectBodyRegex.e2e.test.js +0 -693
  222. package/dist/__tests__/e2e/crud/replaceObjectBodyRegex.e2e.test.js.map +0 -1
  223. package/dist/__tests__/e2e/workflow/pruneClosed.e2e.test.d.ts +0 -2
  224. package/dist/__tests__/e2e/workflow/pruneClosed.e2e.test.d.ts.map +0 -1
  225. package/dist/__tests__/e2e/workflow/pruneClosed.e2e.test.js +0 -352
  226. package/dist/__tests__/e2e/workflow/pruneClosed.e2e.test.js.map +0 -1
  227. package/dist/services/local/__tests__/replaceObjectBodyRegex.test.d.ts +0 -2
  228. package/dist/services/local/__tests__/replaceObjectBodyRegex.test.d.ts.map +0 -1
  229. package/dist/services/local/__tests__/replaceObjectBodyRegex.test.js +0 -283
  230. package/dist/services/local/__tests__/replaceObjectBodyRegex.test.js.map +0 -1
  231. package/dist/services/local/replaceObjectBodyRegex.d.ts +0 -8
  232. package/dist/services/local/replaceObjectBodyRegex.d.ts.map +0 -1
  233. package/dist/services/local/replaceObjectBodyRegex.js +0 -85
  234. package/dist/services/local/replaceObjectBodyRegex.js.map +0 -1
  235. package/dist/tools/__tests__/pruneClosedTool.test.d.ts +0 -2
  236. package/dist/tools/__tests__/pruneClosedTool.test.d.ts.map +0 -1
  237. package/dist/tools/__tests__/pruneClosedTool.test.js +0 -112
  238. package/dist/tools/__tests__/pruneClosedTool.test.js.map +0 -1
  239. package/dist/tools/__tests__/replaceObjectBodyRegexTool.test.d.ts +0 -2
  240. package/dist/tools/__tests__/replaceObjectBodyRegexTool.test.d.ts.map +0 -1
  241. package/dist/tools/__tests__/replaceObjectBodyRegexTool.test.js +0 -89
  242. package/dist/tools/__tests__/replaceObjectBodyRegexTool.test.js.map +0 -1
  243. package/dist/tools/pruneClosedTool.d.ts +0 -27
  244. package/dist/tools/pruneClosedTool.d.ts.map +0 -1
  245. package/dist/tools/pruneClosedTool.js +0 -57
  246. package/dist/tools/pruneClosedTool.js.map +0 -1
  247. package/dist/tools/replaceObjectBodyRegexTool.d.ts +0 -36
  248. package/dist/tools/replaceObjectBodyRegexTool.d.ts.map +0 -1
  249. package/dist/tools/replaceObjectBodyRegexTool.js +0 -67
  250. package/dist/tools/replaceObjectBodyRegexTool.js.map +0 -1
  251. package/dist/utils/ReplaceStringOptions.d.ts +0 -12
  252. package/dist/utils/ReplaceStringOptions.d.ts.map +0 -1
  253. package/dist/utils/ReplaceStringOptions.js +0 -3
  254. package/dist/utils/ReplaceStringOptions.js.map +0 -1
  255. package/dist/utils/__tests__/replaceStringWithRegex.test.d.ts +0 -2
  256. package/dist/utils/__tests__/replaceStringWithRegex.test.d.ts.map +0 -1
  257. package/dist/utils/__tests__/replaceStringWithRegex.test.js +0 -281
  258. package/dist/utils/__tests__/replaceStringWithRegex.test.js.map +0 -1
  259. package/dist/utils/replaceStringWithRegex.d.ts +0 -45
  260. package/dist/utils/replaceStringWithRegex.d.ts.map +0 -1
  261. package/dist/utils/replaceStringWithRegex.js +0 -91
  262. package/dist/utils/replaceStringWithRegex.js.map +0 -1
@@ -31,7 +31,7 @@ describe("E2E CRUD - updateObject", () => {
31
31
  };
32
32
  await (0, utils_1.createObjectFile)(testEnv.projectRoot, "task", "T-priority-test", (0, utils_1.createObjectContent)(taskData));
33
33
  // Update priority
34
- const result = await client.callTool("update_object", {
34
+ const result = await client.callTool("update_issue", {
35
35
  id: "T-priority-test",
36
36
  priority: "high",
37
37
  });
@@ -55,7 +55,7 @@ describe("E2E CRUD - updateObject", () => {
55
55
  };
56
56
  await (0, utils_1.createObjectFile)(testEnv.projectRoot, "project", "P-status-test", (0, utils_1.createObjectContent)(projectData));
57
57
  // Update status
58
- const result = await client.callTool("update_object", {
58
+ const result = await client.callTool("update_issue", {
59
59
  id: "P-status-test",
60
60
  status: "open",
61
61
  });
@@ -82,7 +82,7 @@ This is the new body content with markdown formatting.
82
82
  ## Details
83
83
  - Point 1
84
84
  - Point 2`;
85
- const result = await client.callTool("update_object", {
85
+ const result = await client.callTool("update_issue", {
86
86
  id: "F-body-test",
87
87
  body: newBody,
88
88
  });
@@ -112,7 +112,7 @@ This is the new body content with markdown formatting.
112
112
  };
113
113
  await (0, utils_1.createObjectFile)(testEnv.projectRoot, "task", "T-deps-test", (0, utils_1.createObjectContent)(taskData));
114
114
  // Update prerequisites
115
- const result = await client.callTool("update_object", {
115
+ const result = await client.callTool("update_issue", {
116
116
  id: "T-deps-test",
117
117
  prerequisites: ["T-prereq-1", "T-prereq-2", "F-external-dep"],
118
118
  });
@@ -144,7 +144,7 @@ This is the new body content with markdown formatting.
144
144
  };
145
145
  await (0, utils_1.createObjectFile)(testEnv.projectRoot, "epic", "E-multi-update", (0, utils_1.createObjectContent)(epicData), { projectId: "P-parent-project" });
146
146
  // Update multiple fields
147
- const result = await client.callTool("update_object", {
147
+ const result = await client.callTool("update_issue", {
148
148
  id: "E-multi-update",
149
149
  priority: "high",
150
150
  status: "open",
@@ -213,7 +213,7 @@ This is the new body content with markdown formatting.
213
213
  status: "open",
214
214
  });
215
215
  // Update only priority
216
- const result = await client.callTool("update_object", {
216
+ const result = await client.callTool("update_issue", {
217
217
  id: "F-preserve-test",
218
218
  priority: "high",
219
219
  });
@@ -246,7 +246,7 @@ This is the new body content with markdown formatting.
246
246
  prerequisites: ["T-prereq-done"],
247
247
  }));
248
248
  // Update status to in-progress
249
- const result = await client.callTool("update_object", {
249
+ const result = await client.callTool("update_issue", {
250
250
  id: "T-can-progress",
251
251
  status: "in-progress",
252
252
  });
@@ -269,7 +269,7 @@ This is the new body content with markdown formatting.
269
269
  prerequisites: ["T-prereq-incomplete"],
270
270
  }));
271
271
  // Attempt to update status to in-progress
272
- const result = await client.callTool("update_object", {
272
+ const result = await client.callTool("update_issue", {
273
273
  id: "T-blocked",
274
274
  status: "in-progress",
275
275
  });
@@ -299,7 +299,7 @@ This is the new body content with markdown formatting.
299
299
  prerequisites: ["T-prereq-done-2", "T-prereq-open"],
300
300
  }));
301
301
  // Attempt to update status to done
302
- const result = await client.callTool("update_object", {
302
+ const result = await client.callTool("update_issue", {
303
303
  id: "F-blocked-done",
304
304
  status: "done",
305
305
  });
@@ -320,7 +320,7 @@ This is the new body content with markdown formatting.
320
320
  prerequisites: ["T-prereq-incomplete-2"],
321
321
  }));
322
322
  // Force update status to done
323
- const result = await client.callTool("update_object", {
323
+ const result = await client.callTool("update_issue", {
324
324
  id: "T-force-update",
325
325
  status: "done",
326
326
  force: true,
@@ -341,7 +341,7 @@ This is the new body content with markdown formatting.
341
341
  prerequisites: ["T-nonexistent"],
342
342
  }));
343
343
  // Update to draft (no validation required)
344
- const result = await client.callTool("update_object", {
344
+ const result = await client.callTool("update_issue", {
345
345
  id: "T-to-draft",
346
346
  status: "draft",
347
347
  });
@@ -358,7 +358,7 @@ This is the new body content with markdown formatting.
358
358
  prerequisites: ["T-incomplete"],
359
359
  }));
360
360
  // Update to wont-do (no validation required)
361
- const result = await client.callTool("update_object", {
361
+ const result = await client.callTool("update_issue", {
362
362
  id: "T-to-wontdo",
363
363
  status: "wont-do",
364
364
  });
@@ -381,7 +381,7 @@ This is the new body content with markdown formatting.
381
381
  prerequisites: ["T-wontdo-prereq"],
382
382
  }));
383
383
  // Should allow progression since wont-do is considered complete
384
- const result = await client.callTool("update_object", {
384
+ const result = await client.callTool("update_issue", {
385
385
  id: "T-with-wontdo",
386
386
  status: "done",
387
387
  });
@@ -392,7 +392,7 @@ This is the new body content with markdown formatting.
392
392
  });
393
393
  describe("Error Handling", () => {
394
394
  it("should handle non-existent object IDs", async () => {
395
- const result = await client.callTool("update_object", {
395
+ const result = await client.callTool("update_issue", {
396
396
  id: "T-nonexistent",
397
397
  priority: "high",
398
398
  });
@@ -406,7 +406,7 @@ This is the new body content with markdown formatting.
406
406
  title: "Invalid Priority Test",
407
407
  }));
408
408
  // Attempt update with invalid priority
409
- const result = await client.callTool("update_object", {
409
+ const result = await client.callTool("update_issue", {
410
410
  id: "T-invalid-priority",
411
411
  priority: "critical", // Invalid value
412
412
  });
@@ -430,7 +430,7 @@ This is the new body content with markdown formatting.
430
430
  title: "Invalid Status Test",
431
431
  }));
432
432
  // Attempt update with invalid status
433
- const result = await client.callTool("update_object", {
433
+ const result = await client.callTool("update_issue", {
434
434
  id: "P-invalid-status",
435
435
  status: "completed", // Invalid value (should be "done")
436
436
  });
@@ -454,7 +454,7 @@ This is the new body content with markdown formatting.
454
454
  "T-", // Missing slug
455
455
  ];
456
456
  for (const id of malformedIds) {
457
- const result = await client.callTool("update_object", {
457
+ const result = await client.callTool("update_issue", {
458
458
  id,
459
459
  priority: "high",
460
460
  });
@@ -470,7 +470,7 @@ This is the new body content with markdown formatting.
470
470
  prerequisites: ["T-dep-1", "T-dep-2"],
471
471
  }));
472
472
  // Clear prerequisites
473
- const result = await client.callTool("update_object", {
473
+ const result = await client.callTool("update_issue", {
474
474
  id: "T-clear-prereqs",
475
475
  prerequisites: [],
476
476
  });
@@ -489,7 +489,7 @@ This is the new body content with markdown formatting.
489
489
  }));
490
490
  // Create very long body content
491
491
  const longBody = "A".repeat(10000) + "\n\n" + "B".repeat(10000);
492
- const result = await client.callTool("update_object", {
492
+ const result = await client.callTool("update_issue", {
493
493
  id: "F-long-body",
494
494
  body: longBody,
495
495
  });
@@ -520,7 +520,7 @@ const test = "value with 'quotes' and 'double quotes'";
520
520
  - Math: α β γ δ ε
521
521
  - Arrows: → ← ↑ ↓
522
522
  - Other: & < > | \\ / * ? : " ' \` ~ ! @ # $ % ^ & * ( ) [ ] { }`;
523
- const result = await client.callTool("update_object", {
523
+ const result = await client.callTool("update_issue", {
524
524
  id: "E-special-chars",
525
525
  body: specialBody,
526
526
  });
@@ -567,7 +567,7 @@ const test = "value with 'quotes' and 'double quotes'";
567
567
  status: "open",
568
568
  }), { projectId, epicId, featureId, status: "open" });
569
569
  // Update task in deep hierarchy
570
- const result = await client.callTool("update_object", {
570
+ const result = await client.callTool("update_issue", {
571
571
  id: taskId,
572
572
  priority: "high",
573
573
  status: "done",
@@ -603,7 +603,7 @@ const test = "value with 'quotes' and 'double quotes'";
603
603
  prerequisites: [],
604
604
  }));
605
605
  // Update with cross-hierarchy prerequisites
606
- const result = await client.callTool("update_object", {
606
+ const result = await client.callTool("update_issue", {
607
607
  id: "T-cross-deps",
608
608
  prerequisites: ["P-proj-1", "F-feat-1", "E-external"],
609
609
  status: "in-progress",
@@ -618,6 +618,478 @@ const test = "value with 'quotes' and 'double quotes'";
618
618
  expect(updatedObject.status).toBe("in-progress");
619
619
  });
620
620
  });
621
+ describe("Hierarchical Status Updates", () => {
622
+ it("should update parent hierarchy to in-progress when task changes to in-progress", async () => {
623
+ // Create complete hierarchy: Project -> Epic -> Feature -> Task
624
+ const projectId = "P-hierarchy-status";
625
+ const epicId = "E-hierarchy-status";
626
+ const featureId = "F-hierarchy-status";
627
+ const taskId = "T-hierarchy-status";
628
+ // Create project (open status)
629
+ await (0, utils_1.createObjectFile)(testEnv.projectRoot, "project", projectId, (0, utils_1.createObjectContent)({
630
+ id: projectId,
631
+ title: "Hierarchy Status Project",
632
+ status: "open",
633
+ childrenIds: [epicId],
634
+ }));
635
+ // Create epic (open status)
636
+ await (0, utils_1.createObjectFile)(testEnv.projectRoot, "epic", epicId, (0, utils_1.createObjectContent)({
637
+ id: epicId,
638
+ title: "Hierarchy Status Epic",
639
+ parent: projectId,
640
+ status: "open",
641
+ childrenIds: [featureId],
642
+ }), { projectId });
643
+ // Create feature (open status)
644
+ await (0, utils_1.createObjectFile)(testEnv.projectRoot, "feature", featureId, (0, utils_1.createObjectContent)({
645
+ id: featureId,
646
+ title: "Hierarchy Status Feature",
647
+ parent: epicId,
648
+ status: "open",
649
+ childrenIds: [taskId],
650
+ }), { projectId, epicId });
651
+ // Create task (open status)
652
+ await (0, utils_1.createObjectFile)(testEnv.projectRoot, "task", taskId, (0, utils_1.createObjectContent)({
653
+ id: taskId,
654
+ title: "Hierarchy Status Task",
655
+ parent: featureId,
656
+ status: "open",
657
+ }), { projectId, epicId, featureId, status: "open" });
658
+ // Update task to in-progress
659
+ const result = await client.callTool("update_issue", {
660
+ id: taskId,
661
+ status: "in-progress",
662
+ });
663
+ expect(result.content[0].text).toContain("Successfully updated object");
664
+ const updatedTask = (0, utils_1.parseUpdateObjectResponse)(result.content[0].text);
665
+ expect(updatedTask.status).toBe("in-progress");
666
+ // Verify task file remains in open folder (in-progress is still "open")
667
+ const taskFile = await (0, utils_1.readObjectFile)(testEnv.projectRoot, `p/${projectId}/e/${epicId}/f/${featureId}/t/open/${taskId}.md`);
668
+ expect(taskFile.yaml.status).toBe("in-progress");
669
+ // Verify feature was updated to in-progress
670
+ const featureFile = await (0, utils_1.readObjectFile)(testEnv.projectRoot, `p/${projectId}/e/${epicId}/f/${featureId}/${featureId}.md`);
671
+ expect(featureFile.yaml.status).toBe("in-progress");
672
+ // Verify epic was updated to in-progress
673
+ const epicFile = await (0, utils_1.readObjectFile)(testEnv.projectRoot, `p/${projectId}/e/${epicId}/${epicId}.md`);
674
+ expect(epicFile.yaml.status).toBe("in-progress");
675
+ // Verify project was updated to in-progress
676
+ const projectFile = await (0, utils_1.readObjectFile)(testEnv.projectRoot, `p/${projectId}/${projectId}.md`);
677
+ expect(projectFile.yaml.status).toBe("in-progress");
678
+ });
679
+ it("should update partial hierarchy when intermediate parent is missing", async () => {
680
+ // Create partial hierarchy: Feature -> Task (no parent for feature)
681
+ const featureId = "F-partial-hierarchy";
682
+ const taskId = "T-partial-hierarchy";
683
+ // Create feature (open status)
684
+ await (0, utils_1.createObjectFile)(testEnv.projectRoot, "feature", featureId, (0, utils_1.createObjectContent)({
685
+ id: featureId,
686
+ title: "Partial Hierarchy Feature",
687
+ status: "open",
688
+ childrenIds: [taskId],
689
+ }));
690
+ // Create task (open status)
691
+ await (0, utils_1.createObjectFile)(testEnv.projectRoot, "task", taskId, (0, utils_1.createObjectContent)({
692
+ id: taskId,
693
+ title: "Partial Hierarchy Task",
694
+ parent: featureId,
695
+ status: "open",
696
+ }), { featureId, status: "open" });
697
+ // Update task to in-progress
698
+ const result = await client.callTool("update_issue", {
699
+ id: taskId,
700
+ status: "in-progress",
701
+ });
702
+ expect(result.content[0].text).toContain("Successfully updated object");
703
+ const updatedTask = (0, utils_1.parseUpdateObjectResponse)(result.content[0].text);
704
+ expect(updatedTask.status).toBe("in-progress");
705
+ // Verify task file remains in open folder (in-progress is still "open")
706
+ const taskFile = await (0, utils_1.readObjectFile)(testEnv.projectRoot, `f/${featureId}/t/open/${taskId}.md`);
707
+ expect(taskFile.yaml.status).toBe("in-progress");
708
+ // Verify feature was updated to in-progress
709
+ const featureFile = await (0, utils_1.readObjectFile)(testEnv.projectRoot, `f/${featureId}/${featureId}.md`);
710
+ expect(featureFile.yaml.status).toBe("in-progress");
711
+ });
712
+ it("should not update parent hierarchy if parent is already in-progress", async () => {
713
+ // Create hierarchy where parent is already in-progress
714
+ const projectId = "P-already-in-progress-parent";
715
+ const epicId = "E-already-in-progress";
716
+ const featureId = "F-child-of-in-progress";
717
+ const taskId = "T-child-of-in-progress";
718
+ // Create project first
719
+ await (0, utils_1.createObjectFile)(testEnv.projectRoot, "project", projectId, (0, utils_1.createObjectContent)({
720
+ id: projectId,
721
+ title: "Already In Progress Parent Project",
722
+ status: "open",
723
+ childrenIds: [epicId],
724
+ }));
725
+ // Create epic (already in-progress)
726
+ await (0, utils_1.createObjectFile)(testEnv.projectRoot, "epic", epicId, (0, utils_1.createObjectContent)({
727
+ id: epicId,
728
+ title: "Already In Progress Epic",
729
+ parent: projectId,
730
+ status: "in-progress",
731
+ childrenIds: [featureId],
732
+ }), { projectId });
733
+ // Create feature (open status)
734
+ await (0, utils_1.createObjectFile)(testEnv.projectRoot, "feature", featureId, (0, utils_1.createObjectContent)({
735
+ id: featureId,
736
+ title: "Child Of In Progress Feature",
737
+ parent: epicId,
738
+ status: "open",
739
+ childrenIds: [taskId],
740
+ }), { projectId, epicId });
741
+ // Create task (open status)
742
+ await (0, utils_1.createObjectFile)(testEnv.projectRoot, "task", taskId, (0, utils_1.createObjectContent)({
743
+ id: taskId,
744
+ title: "Child Of In Progress Task",
745
+ parent: featureId,
746
+ status: "open",
747
+ }), { projectId, epicId, featureId, status: "open" });
748
+ // Update task to in-progress
749
+ const result = await client.callTool("update_issue", {
750
+ id: taskId,
751
+ status: "in-progress",
752
+ });
753
+ expect(result.content[0].text).toContain("Successfully updated object");
754
+ // Verify task is in-progress (remains in open folder)
755
+ const taskFile = await (0, utils_1.readObjectFile)(testEnv.projectRoot, `p/${projectId}/e/${epicId}/f/${featureId}/t/open/${taskId}.md`);
756
+ expect(taskFile.yaml.status).toBe("in-progress");
757
+ // Verify feature was updated to in-progress
758
+ const featureFile = await (0, utils_1.readObjectFile)(testEnv.projectRoot, `p/${projectId}/e/${epicId}/f/${featureId}/${featureId}.md`);
759
+ expect(featureFile.yaml.status).toBe("in-progress");
760
+ // Verify epic remains in-progress (not changed)
761
+ const epicFile = await (0, utils_1.readObjectFile)(testEnv.projectRoot, `p/${projectId}/e/${epicId}/${epicId}.md`);
762
+ expect(epicFile.yaml.status).toBe("in-progress");
763
+ });
764
+ it("should only update hierarchy when status changes to in-progress from non-in-progress", async () => {
765
+ // Create simple hierarchy
766
+ const featureId = "F-status-change-test";
767
+ const taskId = "T-status-change-test";
768
+ // Create feature (open status)
769
+ await (0, utils_1.createObjectFile)(testEnv.projectRoot, "feature", featureId, (0, utils_1.createObjectContent)({
770
+ id: featureId,
771
+ title: "Status Change Test Feature",
772
+ status: "open",
773
+ childrenIds: [taskId],
774
+ }));
775
+ // Create task (already in-progress)
776
+ await (0, utils_1.createObjectFile)(testEnv.projectRoot, "task", taskId, (0, utils_1.createObjectContent)({
777
+ id: taskId,
778
+ title: "Status Change Test Task",
779
+ parent: featureId,
780
+ status: "in-progress",
781
+ }), { featureId, status: "open" });
782
+ // Update task priority but keep status as in-progress (no status change)
783
+ const result = await client.callTool("update_issue", {
784
+ id: taskId,
785
+ priority: "high",
786
+ status: "in-progress", // Same status
787
+ });
788
+ expect(result.content[0].text).toContain("Successfully updated object");
789
+ // Verify feature status is unchanged (should still be open)
790
+ const featureFile = await (0, utils_1.readObjectFile)(testEnv.projectRoot, `f/${featureId}/${featureId}.md`);
791
+ expect(featureFile.yaml.status).toBe("open");
792
+ });
793
+ });
794
+ describe("Auto-Complete Parent Hierarchy", () => {
795
+ it("should auto-complete feature when all tasks are done via update", async () => {
796
+ // Create complete hierarchy: Project -> Epic -> Feature -> Tasks
797
+ const projectId = "P-auto-complete-done";
798
+ const epicId = "E-auto-complete-done";
799
+ const featureId = "F-auto-complete-done";
800
+ const task1Id = "T-auto-complete-1";
801
+ const task2Id = "T-auto-complete-2";
802
+ // Create project (open status)
803
+ await (0, utils_1.createObjectFile)(testEnv.projectRoot, "project", projectId, (0, utils_1.createObjectContent)({
804
+ id: projectId,
805
+ title: "Auto Complete Done Project",
806
+ status: "open",
807
+ childrenIds: [epicId],
808
+ }));
809
+ // Create epic (open status)
810
+ await (0, utils_1.createObjectFile)(testEnv.projectRoot, "epic", epicId, (0, utils_1.createObjectContent)({
811
+ id: epicId,
812
+ title: "Auto Complete Done Epic",
813
+ parent: projectId,
814
+ status: "open",
815
+ childrenIds: [featureId],
816
+ }), { projectId });
817
+ // Create feature (open status)
818
+ await (0, utils_1.createObjectFile)(testEnv.projectRoot, "feature", featureId, (0, utils_1.createObjectContent)({
819
+ id: featureId,
820
+ title: "Auto Complete Done Feature",
821
+ parent: epicId,
822
+ status: "open",
823
+ childrenIds: [task1Id, task2Id],
824
+ }), { projectId, epicId });
825
+ // Create first task (already done)
826
+ await (0, utils_1.createObjectFile)(testEnv.projectRoot, "task", task1Id, (0, utils_1.createObjectContent)({
827
+ id: task1Id,
828
+ title: "Auto Complete Task 1",
829
+ parent: featureId,
830
+ status: "done",
831
+ }), { projectId, epicId, featureId, status: "closed" });
832
+ // Create second task (in-progress)
833
+ await (0, utils_1.createObjectFile)(testEnv.projectRoot, "task", task2Id, (0, utils_1.createObjectContent)({
834
+ id: task2Id,
835
+ title: "Auto Complete Task 2",
836
+ parent: featureId,
837
+ status: "in-progress",
838
+ }), { projectId, epicId, featureId, status: "open" });
839
+ // Update second task to done - this should trigger auto-complete
840
+ const result = await client.callTool("update_issue", {
841
+ id: task2Id,
842
+ status: "done",
843
+ });
844
+ expect(result.content[0].text).toContain("Successfully updated object");
845
+ const updatedTask = (0, utils_1.parseUpdateObjectResponse)(result.content[0].text);
846
+ expect(updatedTask.status).toBe("done");
847
+ // Verify task2 file moved to closed folder
848
+ const task2File = await (0, utils_1.readObjectFile)(testEnv.projectRoot, `p/${projectId}/e/${epicId}/f/${featureId}/t/closed/${task2Id}.md`);
849
+ expect(task2File.yaml.status).toBe("done");
850
+ // Verify feature was auto-completed
851
+ const featureFile = await (0, utils_1.readObjectFile)(testEnv.projectRoot, `p/${projectId}/e/${epicId}/f/${featureId}/${featureId}.md`);
852
+ expect(featureFile.yaml.status).toBe("done");
853
+ // Verify epic was auto-completed (only one feature)
854
+ const epicFile = await (0, utils_1.readObjectFile)(testEnv.projectRoot, `p/${projectId}/e/${epicId}/${epicId}.md`);
855
+ expect(epicFile.yaml.status).toBe("done");
856
+ // Verify project was auto-completed (only one epic)
857
+ const projectFile = await (0, utils_1.readObjectFile)(testEnv.projectRoot, `p/${projectId}/${projectId}.md`);
858
+ expect(projectFile.yaml.status).toBe("done");
859
+ });
860
+ it("should auto-complete feature when mixing done and wont-do tasks via update", async () => {
861
+ // Create hierarchy: Feature -> Tasks (mixed done/wont-do completion)
862
+ const featureId = "F-auto-complete-mixed";
863
+ const task1Id = "T-mixed-done";
864
+ const task2Id = "T-mixed-wontdo";
865
+ const task3Id = "T-mixed-pending";
866
+ // Create feature (open status)
867
+ await (0, utils_1.createObjectFile)(testEnv.projectRoot, "feature", featureId, (0, utils_1.createObjectContent)({
868
+ id: featureId,
869
+ title: "Auto Complete Mixed Feature",
870
+ status: "open",
871
+ childrenIds: [task1Id, task2Id, task3Id],
872
+ }));
873
+ // Create first task (done)
874
+ await (0, utils_1.createObjectFile)(testEnv.projectRoot, "task", task1Id, (0, utils_1.createObjectContent)({
875
+ id: task1Id,
876
+ title: "Mixed Done Task",
877
+ parent: featureId,
878
+ status: "done",
879
+ }), { featureId, status: "closed" });
880
+ // Create second task (wont-do)
881
+ await (0, utils_1.createObjectFile)(testEnv.projectRoot, "task", task2Id, (0, utils_1.createObjectContent)({
882
+ id: task2Id,
883
+ title: "Mixed Wont-Do Task",
884
+ parent: featureId,
885
+ status: "wont-do",
886
+ }), { featureId, status: "closed" });
887
+ // Create third task (in-progress)
888
+ await (0, utils_1.createObjectFile)(testEnv.projectRoot, "task", task3Id, (0, utils_1.createObjectContent)({
889
+ id: task3Id,
890
+ title: "Mixed Pending Task",
891
+ parent: featureId,
892
+ status: "in-progress",
893
+ }), { featureId, status: "open" });
894
+ // Update third task to wont-do - this should trigger auto-complete
895
+ const result = await client.callTool("update_issue", {
896
+ id: task3Id,
897
+ status: "wont-do",
898
+ });
899
+ expect(result.content[0].text).toContain("Successfully updated object");
900
+ const updatedTask = (0, utils_1.parseUpdateObjectResponse)(result.content[0].text);
901
+ expect(updatedTask.status).toBe("wont-do");
902
+ // Verify task3 file moved to closed folder
903
+ const task3File = await (0, utils_1.readObjectFile)(testEnv.projectRoot, `f/${featureId}/t/closed/${task3Id}.md`);
904
+ expect(task3File.yaml.status).toBe("wont-do");
905
+ // Verify feature was auto-completed
906
+ const featureFile = await (0, utils_1.readObjectFile)(testEnv.projectRoot, `f/${featureId}/${featureId}.md`);
907
+ expect(featureFile.yaml.status).toBe("done");
908
+ });
909
+ it("should not auto-complete when some children are still open", async () => {
910
+ // Create hierarchy with one complete feature and one incomplete feature
911
+ const projectId = "P-not-auto-complete";
912
+ const epicId = "E-not-auto-complete";
913
+ const feature1Id = "F-complete";
914
+ const feature2Id = "F-incomplete";
915
+ // Create project first
916
+ await (0, utils_1.createObjectFile)(testEnv.projectRoot, "project", projectId, (0, utils_1.createObjectContent)({
917
+ id: projectId,
918
+ title: "Not Auto Complete Project",
919
+ status: "open",
920
+ childrenIds: [epicId],
921
+ }));
922
+ // Create epic (open status)
923
+ await (0, utils_1.createObjectFile)(testEnv.projectRoot, "epic", epicId, (0, utils_1.createObjectContent)({
924
+ id: epicId,
925
+ title: "Not Auto Complete Epic",
926
+ parent: projectId,
927
+ status: "open",
928
+ childrenIds: [feature1Id, feature2Id],
929
+ }), { projectId });
930
+ // Create first feature (done)
931
+ await (0, utils_1.createObjectFile)(testEnv.projectRoot, "feature", feature1Id, (0, utils_1.createObjectContent)({
932
+ id: feature1Id,
933
+ title: "Complete Feature",
934
+ parent: epicId,
935
+ status: "done",
936
+ childrenIds: [],
937
+ }), { projectId, epicId });
938
+ // Create second feature (open - incomplete)
939
+ await (0, utils_1.createObjectFile)(testEnv.projectRoot, "feature", feature2Id, (0, utils_1.createObjectContent)({
940
+ id: feature2Id,
941
+ title: "Incomplete Feature",
942
+ parent: epicId,
943
+ status: "open",
944
+ childrenIds: [],
945
+ }), { projectId, epicId });
946
+ // Update first feature to done (already done, no status change)
947
+ // This should NOT auto-complete the epic because feature2 is still open
948
+ const result = await client.callTool("update_issue", {
949
+ id: feature1Id,
950
+ priority: "high", // Just update priority, keep status as done
951
+ status: "done",
952
+ });
953
+ expect(result.content[0].text).toContain("Successfully updated object");
954
+ // Verify epic status remains open (not auto-completed because feature2 is still open)
955
+ const epicFile = await (0, utils_1.readObjectFile)(testEnv.projectRoot, `p/${projectId}/e/${epicId}/${epicId}.md`);
956
+ expect(epicFile.yaml.status).toBe("open");
957
+ });
958
+ it("should not trigger auto-complete when status doesn't change", async () => {
959
+ // Create simple hierarchy
960
+ const featureId = "F-no-status-change";
961
+ const taskId = "T-already-done";
962
+ // Create feature (open)
963
+ await (0, utils_1.createObjectFile)(testEnv.projectRoot, "feature", featureId, (0, utils_1.createObjectContent)({
964
+ id: featureId,
965
+ title: "No Status Change Feature",
966
+ status: "open",
967
+ childrenIds: [taskId],
968
+ }));
969
+ // Create task (already done)
970
+ await (0, utils_1.createObjectFile)(testEnv.projectRoot, "task", taskId, (0, utils_1.createObjectContent)({
971
+ id: taskId,
972
+ title: "Already Done Task",
973
+ parent: featureId,
974
+ status: "done",
975
+ }), { featureId, status: "closed" });
976
+ // Update task priority but keep status as done (no status change)
977
+ const result = await client.callTool("update_issue", {
978
+ id: taskId,
979
+ priority: "high",
980
+ status: "done", // Same status
981
+ });
982
+ expect(result.content[0].text).toContain("Successfully updated object");
983
+ // Verify feature status remains open (auto-complete should not trigger)
984
+ const featureFile = await (0, utils_1.readObjectFile)(testEnv.projectRoot, `f/${featureId}/${featureId}.md`);
985
+ expect(featureFile.yaml.status).toBe("open");
986
+ });
987
+ it("should handle auto-complete when parent object is missing", async () => {
988
+ // Create task with parent that doesn't exist
989
+ const taskId = "T-orphaned-task";
990
+ const missingParentId = "F-missing-parent";
991
+ // Create task with non-existent parent
992
+ await (0, utils_1.createObjectFile)(testEnv.projectRoot, "task", taskId, (0, utils_1.createObjectContent)({
993
+ id: taskId,
994
+ title: "Orphaned Task",
995
+ parent: missingParentId,
996
+ status: "in-progress",
997
+ }));
998
+ // Update task to done (should fail due to missing parent validation)
999
+ const result = await client.callTool("update_issue", {
1000
+ id: taskId,
1001
+ status: "done",
1002
+ });
1003
+ // The system validates parent exists, so this should fail
1004
+ expect(result.content[0].text).toContain("Parent object with ID 'F-missing-parent' not found");
1005
+ });
1006
+ it("should auto-complete up the entire hierarchy", async () => {
1007
+ // Create deep hierarchy: Project -> Epic -> Feature -> Task
1008
+ const projectId = "P-deep-auto-complete";
1009
+ const epicId = "E-deep-auto-complete";
1010
+ const featureId = "F-deep-auto-complete";
1011
+ const taskId = "T-deep-auto-complete";
1012
+ // Create project
1013
+ await (0, utils_1.createObjectFile)(testEnv.projectRoot, "project", projectId, (0, utils_1.createObjectContent)({
1014
+ id: projectId,
1015
+ title: "Deep Auto Complete Project",
1016
+ status: "open",
1017
+ childrenIds: [epicId],
1018
+ }));
1019
+ // Create epic
1020
+ await (0, utils_1.createObjectFile)(testEnv.projectRoot, "epic", epicId, (0, utils_1.createObjectContent)({
1021
+ id: epicId,
1022
+ title: "Deep Auto Complete Epic",
1023
+ parent: projectId,
1024
+ status: "open",
1025
+ childrenIds: [featureId],
1026
+ }), { projectId });
1027
+ // Create feature
1028
+ await (0, utils_1.createObjectFile)(testEnv.projectRoot, "feature", featureId, (0, utils_1.createObjectContent)({
1029
+ id: featureId,
1030
+ title: "Deep Auto Complete Feature",
1031
+ parent: epicId,
1032
+ status: "open",
1033
+ childrenIds: [taskId],
1034
+ }), { projectId, epicId });
1035
+ // Create task
1036
+ await (0, utils_1.createObjectFile)(testEnv.projectRoot, "task", taskId, (0, utils_1.createObjectContent)({
1037
+ id: taskId,
1038
+ title: "Deep Auto Complete Task",
1039
+ parent: featureId,
1040
+ status: "open",
1041
+ }), { projectId, epicId, featureId, status: "open" });
1042
+ // Update task to done - should trigger auto-complete all the way up
1043
+ const result = await client.callTool("update_issue", {
1044
+ id: taskId,
1045
+ status: "done",
1046
+ });
1047
+ expect(result.content[0].text).toContain("Successfully updated object");
1048
+ // Verify task is done
1049
+ const taskFile = await (0, utils_1.readObjectFile)(testEnv.projectRoot, `p/${projectId}/e/${epicId}/f/${featureId}/t/closed/${taskId}.md`);
1050
+ expect(taskFile.yaml.status).toBe("done");
1051
+ // Verify feature was auto-completed
1052
+ const featureFile = await (0, utils_1.readObjectFile)(testEnv.projectRoot, `p/${projectId}/e/${epicId}/f/${featureId}/${featureId}.md`);
1053
+ expect(featureFile.yaml.status).toBe("done");
1054
+ // Verify epic was auto-completed
1055
+ const epicFile = await (0, utils_1.readObjectFile)(testEnv.projectRoot, `p/${projectId}/e/${epicId}/${epicId}.md`);
1056
+ expect(epicFile.yaml.status).toBe("done");
1057
+ // Verify project was auto-completed
1058
+ const projectFile = await (0, utils_1.readObjectFile)(testEnv.projectRoot, `p/${projectId}/${projectId}.md`);
1059
+ expect(projectFile.yaml.status).toBe("done");
1060
+ });
1061
+ it("should not auto-complete parent if it's already done", async () => {
1062
+ // Create hierarchy where parent is already done
1063
+ const featureId = "F-already-done-parent";
1064
+ const taskId = "T-child-of-done";
1065
+ // Create feature (already done)
1066
+ await (0, utils_1.createObjectFile)(testEnv.projectRoot, "feature", featureId, (0, utils_1.createObjectContent)({
1067
+ id: featureId,
1068
+ title: "Already Done Parent Feature",
1069
+ status: "done",
1070
+ childrenIds: [taskId],
1071
+ }));
1072
+ // Create task (open)
1073
+ await (0, utils_1.createObjectFile)(testEnv.projectRoot, "task", taskId, (0, utils_1.createObjectContent)({
1074
+ id: taskId,
1075
+ title: "Child of Done Task",
1076
+ parent: featureId,
1077
+ status: "open",
1078
+ }), { featureId, status: "open" });
1079
+ // Update task to done
1080
+ const result = await client.callTool("update_issue", {
1081
+ id: taskId,
1082
+ status: "done",
1083
+ });
1084
+ expect(result.content[0].text).toContain("Successfully updated object");
1085
+ // Verify feature status remains done (unchanged)
1086
+ const featureFile = await (0, utils_1.readObjectFile)(testEnv.projectRoot, `f/${featureId}/${featureId}.md`);
1087
+ expect(featureFile.yaml.status).toBe("done");
1088
+ // Verify task is done
1089
+ const taskFile = await (0, utils_1.readObjectFile)(testEnv.projectRoot, `f/${featureId}/t/closed/${taskId}.md`);
1090
+ expect(taskFile.yaml.status).toBe("done");
1091
+ });
1092
+ });
621
1093
  describe("Concurrent Updates", () => {
622
1094
  it("should handle sequential updates to same object", async () => {
623
1095
  // Create initial task
@@ -629,19 +1101,19 @@ const test = "value with 'quotes' and 'double quotes'";
629
1101
  body: "Initial",
630
1102
  }));
631
1103
  // First update
632
- const result1 = await client.callTool("update_object", {
1104
+ const result1 = await client.callTool("update_issue", {
633
1105
  id: "T-sequential",
634
1106
  priority: "medium",
635
1107
  });
636
1108
  expect(result1.content[0].text).toContain("Successfully updated object");
637
1109
  // Second update
638
- const result2 = await client.callTool("update_object", {
1110
+ const result2 = await client.callTool("update_issue", {
639
1111
  id: "T-sequential",
640
1112
  status: "open",
641
1113
  });
642
1114
  expect(result2.content[0].text).toContain("Successfully updated object");
643
1115
  // Third update
644
- const result3 = await client.callTool("update_object", {
1116
+ const result3 = await client.callTool("update_issue", {
645
1117
  id: "T-sequential",
646
1118
  body: "Final content after multiple updates",
647
1119
  });