@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.
- package/README.md +37 -323
- package/dist/__tests__/e2e/autoPrune.e2e.test.d.ts +2 -0
- package/dist/__tests__/e2e/autoPrune.e2e.test.d.ts.map +1 -0
- package/dist/__tests__/e2e/autoPrune.e2e.test.js +533 -0
- package/dist/__tests__/e2e/autoPrune.e2e.test.js.map +1 -0
- package/dist/__tests__/e2e/configuration/activation.e2e.test.js +6 -6
- package/dist/__tests__/e2e/configuration/activation.e2e.test.js.map +1 -1
- package/dist/__tests__/e2e/configuration/commandLineArgs.e2e.test.js +55 -6
- package/dist/__tests__/e2e/configuration/commandLineArgs.e2e.test.js.map +1 -1
- package/dist/__tests__/e2e/configuration/directorySetup.e2e.test.js +19 -19
- package/dist/__tests__/e2e/configuration/directorySetup.e2e.test.js.map +1 -1
- package/dist/__tests__/e2e/configuration/invalidConfig.e2e.test.js +9 -9
- package/dist/__tests__/e2e/configuration/invalidConfig.e2e.test.js.map +1 -1
- package/dist/__tests__/e2e/configuration/preActivation.e2e.test.js +15 -15
- package/dist/__tests__/e2e/configuration/preActivation.e2e.test.js.map +1 -1
- package/dist/__tests__/e2e/crud/createObject.e2e.test.js +34 -34
- package/dist/__tests__/e2e/crud/createObject.e2e.test.js.map +1 -1
- package/dist/__tests__/e2e/crud/deleteObject.e2e.test.js +19 -19
- package/dist/__tests__/e2e/crud/deleteObject.e2e.test.js.map +1 -1
- package/dist/__tests__/e2e/crud/fileValidation.e2e.test.js +32 -32
- package/dist/__tests__/e2e/crud/fileValidation.e2e.test.js.map +1 -1
- package/dist/__tests__/e2e/crud/getObject.e2e.test.js +143 -22
- package/dist/__tests__/e2e/crud/getObject.e2e.test.js.map +1 -1
- package/dist/__tests__/e2e/crud/listObjects.e2e.test.js +561 -42
- package/dist/__tests__/e2e/crud/listObjects.e2e.test.js.map +1 -1
- package/dist/__tests__/e2e/crud/updateObject.e2e.test.js +497 -25
- package/dist/__tests__/e2e/crud/updateObject.e2e.test.js.map +1 -1
- package/dist/__tests__/e2e/hierarchicalPrerequisites.e2e.test.d.ts +2 -0
- package/dist/__tests__/e2e/hierarchicalPrerequisites.e2e.test.d.ts.map +1 -0
- package/dist/__tests__/e2e/hierarchicalPrerequisites.e2e.test.js +319 -0
- package/dist/__tests__/e2e/hierarchicalPrerequisites.e2e.test.js.map +1 -0
- package/dist/__tests__/e2e/infrastructure/client.e2e.test.js +4 -4
- package/dist/__tests__/e2e/infrastructure/client.e2e.test.js.map +1 -1
- package/dist/__tests__/e2e/infrastructure/server.e2e.test.js +6 -6
- package/dist/__tests__/e2e/infrastructure/server.e2e.test.js.map +1 -1
- package/dist/__tests__/e2e/utils/extractObjectIds.d.ts +1 -1
- package/dist/__tests__/e2e/utils/extractObjectIds.js +1 -1
- package/dist/__tests__/e2e/utils/mcpTestClient.d.ts.map +1 -1
- package/dist/__tests__/e2e/utils/mcpTestClient.js +3 -2
- package/dist/__tests__/e2e/utils/mcpTestClient.js.map +1 -1
- package/dist/__tests__/e2e/utils/parseListObjectsResponse.d.ts +1 -1
- package/dist/__tests__/e2e/utils/parseListObjectsResponse.js +1 -1
- package/dist/__tests__/e2e/utils/parseUpdateObjectResponse.d.ts +1 -1
- package/dist/__tests__/e2e/utils/parseUpdateObjectResponse.js +1 -1
- package/dist/__tests__/e2e/workflow/appendLog.e2e.test.js +4 -4
- package/dist/__tests__/e2e/workflow/appendLog.e2e.test.js.map +1 -1
- package/dist/__tests__/e2e/workflow/appendModifiedFiles.e2e.test.js +15 -15
- package/dist/__tests__/e2e/workflow/appendModifiedFiles.e2e.test.js.map +1 -1
- package/dist/__tests__/e2e/workflow/claimTask.e2e.test.js +44 -0
- package/dist/__tests__/e2e/workflow/claimTask.e2e.test.js.map +1 -1
- package/dist/__tests__/e2e/workflow/completeTask.e2e.test.js +4 -4
- package/dist/__tests__/e2e/workflow/completeTask.e2e.test.js.map +1 -1
- package/dist/__tests__/e2e/workflow/prerequisites.e2e.test.js +43 -43
- package/dist/__tests__/e2e/workflow/prerequisites.e2e.test.js.map +1 -1
- package/dist/__tests__/e2e/workflow/taskLifecycle.e2e.test.js +19 -19
- package/dist/__tests__/e2e/workflow/taskLifecycle.e2e.test.js.map +1 -1
- package/dist/__tests__/serverStartup.test.d.ts +2 -0
- package/dist/__tests__/serverStartup.test.d.ts.map +1 -0
- package/dist/__tests__/serverStartup.test.js +171 -0
- package/dist/__tests__/serverStartup.test.js.map +1 -0
- package/dist/configuration/ServerConfig.d.ts +2 -1
- package/dist/configuration/ServerConfig.d.ts.map +1 -1
- package/dist/repositories/Repository.d.ts +2 -1
- package/dist/repositories/Repository.d.ts.map +1 -1
- package/dist/repositories/local/LocalRepository.d.ts +2 -1
- package/dist/repositories/local/LocalRepository.d.ts.map +1 -1
- package/dist/repositories/local/LocalRepository.js +4 -0
- package/dist/repositories/local/LocalRepository.js.map +1 -1
- package/dist/repositories/local/__tests__/getChildrenOf.test.d.ts +2 -0
- package/dist/repositories/local/__tests__/getChildrenOf.test.d.ts.map +1 -0
- package/dist/repositories/local/__tests__/getChildrenOf.test.js +306 -0
- package/dist/repositories/local/__tests__/getChildrenOf.test.js.map +1 -0
- package/dist/repositories/local/__tests__/getObjects.test.js +309 -0
- package/dist/repositories/local/__tests__/getObjects.test.js.map +1 -1
- package/dist/repositories/local/deleteObjectById.d.ts.map +1 -1
- package/dist/repositories/local/deleteObjectById.js +2 -0
- package/dist/repositories/local/deleteObjectById.js.map +1 -1
- package/dist/repositories/local/getChildrenOf.d.ts +11 -0
- package/dist/repositories/local/getChildrenOf.d.ts.map +1 -0
- package/dist/repositories/local/getChildrenOf.js +73 -0
- package/dist/repositories/local/getChildrenOf.js.map +1 -0
- package/dist/repositories/local/getObjects.d.ts +1 -1
- package/dist/repositories/local/getObjects.d.ts.map +1 -1
- package/dist/repositories/local/getObjects.js +25 -6
- package/dist/repositories/local/getObjects.js.map +1 -1
- package/dist/server.js +39 -15
- package/dist/server.js.map +1 -1
- package/dist/services/TaskTrellisService.d.ts +4 -13
- package/dist/services/TaskTrellisService.d.ts.map +1 -1
- package/dist/services/local/LocalTaskTrellisService.d.ts +3 -9
- package/dist/services/local/LocalTaskTrellisService.d.ts.map +1 -1
- package/dist/services/local/LocalTaskTrellisService.js +4 -8
- package/dist/services/local/LocalTaskTrellisService.js.map +1 -1
- package/dist/services/local/__tests__/appendModifiedFiles.test.js +1 -0
- package/dist/services/local/__tests__/appendModifiedFiles.test.js.map +1 -1
- package/dist/services/local/__tests__/appendObjectLog.test.js +1 -0
- package/dist/services/local/__tests__/appendObjectLog.test.js.map +1 -1
- package/dist/services/local/__tests__/claimTask.test.js +127 -131
- package/dist/services/local/__tests__/claimTask.test.js.map +1 -1
- package/dist/services/local/__tests__/completeTask.test.js +30 -28
- package/dist/services/local/__tests__/completeTask.test.js.map +1 -1
- package/dist/services/local/__tests__/createObject.test.js +1 -0
- package/dist/services/local/__tests__/createObject.test.js.map +1 -1
- package/dist/services/local/__tests__/listObjects.test.js +135 -10
- package/dist/services/local/__tests__/listObjects.test.js.map +1 -1
- package/dist/services/local/__tests__/pruneClosed.test.js +446 -186
- package/dist/services/local/__tests__/pruneClosed.test.js.map +1 -1
- package/dist/services/local/__tests__/updateObject.test.js +234 -27
- package/dist/services/local/__tests__/updateObject.test.js.map +1 -1
- package/dist/services/local/claimTask.d.ts.map +1 -1
- package/dist/services/local/claimTask.js +25 -34
- package/dist/services/local/claimTask.js.map +1 -1
- package/dist/services/local/completeTask.d.ts +1 -1
- package/dist/services/local/completeTask.d.ts.map +1 -1
- package/dist/services/local/completeTask.js +4 -40
- package/dist/services/local/completeTask.js.map +1 -1
- package/dist/services/local/listObjects.d.ts +1 -1
- package/dist/services/local/listObjects.d.ts.map +1 -1
- package/dist/services/local/listObjects.js +10 -1
- package/dist/services/local/listObjects.js.map +1 -1
- package/dist/services/local/pruneClosed.d.ts.map +1 -1
- package/dist/services/local/pruneClosed.js +63 -6
- package/dist/services/local/pruneClosed.js.map +1 -1
- package/dist/services/local/updateObject.d.ts +2 -1
- package/dist/services/local/updateObject.d.ts.map +1 -1
- package/dist/services/local/updateObject.js +28 -1
- package/dist/services/local/updateObject.js.map +1 -1
- package/dist/tools/__tests__/appendModifiedFilesTool.test.js +1 -0
- package/dist/tools/__tests__/appendModifiedFilesTool.test.js.map +1 -1
- package/dist/tools/__tests__/appendObjectLogTool.test.js +1 -0
- package/dist/tools/__tests__/appendObjectLogTool.test.js.map +1 -1
- package/dist/tools/__tests__/claimTaskTool.test.js +1 -1
- package/dist/tools/__tests__/claimTaskTool.test.js.map +1 -1
- package/dist/tools/__tests__/completeTaskTool.test.js +23 -16
- package/dist/tools/__tests__/completeTaskTool.test.js.map +1 -1
- package/dist/tools/__tests__/createObjectTool.test.js +1 -0
- package/dist/tools/__tests__/createObjectTool.test.js.map +1 -1
- package/dist/tools/__tests__/deleteObjectTool.test.js +1 -0
- package/dist/tools/__tests__/deleteObjectTool.test.js.map +1 -1
- package/dist/tools/__tests__/getObjectTool.test.js +1 -0
- package/dist/tools/__tests__/getObjectTool.test.js.map +1 -1
- package/dist/tools/__tests__/listObjectsTool.test.js +160 -1
- package/dist/tools/__tests__/listObjectsTool.test.js.map +1 -1
- package/dist/tools/__tests__/updateObjectTool.test.js +39 -10
- package/dist/tools/__tests__/updateObjectTool.test.js.map +1 -1
- package/dist/tools/appendModifiedFilesTool.d.ts +2 -2
- package/dist/tools/appendModifiedFilesTool.js +4 -4
- package/dist/tools/appendModifiedFilesTool.js.map +1 -1
- package/dist/tools/appendObjectLogTool.d.ts +3 -3
- package/dist/tools/appendObjectLogTool.js +4 -4
- package/dist/tools/appendObjectLogTool.js.map +1 -1
- package/dist/tools/completeTaskTool.d.ts +1 -1
- package/dist/tools/completeTaskTool.d.ts.map +1 -1
- package/dist/tools/completeTaskTool.js +1 -1
- package/dist/tools/completeTaskTool.js.map +1 -1
- package/dist/tools/createObjectTool.d.ts +8 -8
- package/dist/tools/createObjectTool.js +13 -13
- package/dist/tools/createObjectTool.js.map +1 -1
- package/dist/tools/deleteObjectTool.d.ts +3 -3
- package/dist/tools/deleteObjectTool.js +12 -12
- package/dist/tools/deleteObjectTool.js.map +1 -1
- package/dist/tools/getObjectTool.d.ts +3 -3
- package/dist/tools/getObjectTool.d.ts.map +1 -1
- package/dist/tools/getObjectTool.js +12 -7
- package/dist/tools/getObjectTool.js.map +1 -1
- package/dist/tools/index.d.ts +7 -9
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +22 -28
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/listObjectsTool.d.ts +20 -11
- package/dist/tools/listObjectsTool.d.ts.map +1 -1
- package/dist/tools/listObjectsTool.js +112 -40
- package/dist/tools/listObjectsTool.js.map +1 -1
- package/dist/tools/updateObjectTool.d.ts +12 -7
- package/dist/tools/updateObjectTool.d.ts.map +1 -1
- package/dist/tools/updateObjectTool.js +20 -14
- package/dist/tools/updateObjectTool.js.map +1 -1
- package/dist/utils/__tests__/checkHierarchicalPrerequisitesComplete.test.d.ts +2 -0
- package/dist/utils/__tests__/checkHierarchicalPrerequisitesComplete.test.d.ts.map +1 -0
- package/dist/utils/__tests__/checkHierarchicalPrerequisitesComplete.test.js +206 -0
- package/dist/utils/__tests__/checkHierarchicalPrerequisitesComplete.test.js.map +1 -0
- package/dist/utils/__tests__/checkPrerequisitesComplete.test.js +5 -0
- package/dist/utils/__tests__/checkPrerequisitesComplete.test.js.map +1 -1
- package/dist/utils/__tests__/filterUnavailableObjects.test.js +51 -25
- package/dist/utils/__tests__/filterUnavailableObjects.test.js.map +1 -1
- package/dist/utils/__tests__/isRequiredForOtherObjects.test.js +5 -0
- package/dist/utils/__tests__/isRequiredForOtherObjects.test.js.map +1 -1
- package/dist/utils/__tests__/updateParentHierarchy.test.d.ts +2 -0
- package/dist/utils/__tests__/updateParentHierarchy.test.d.ts.map +1 -0
- package/dist/utils/__tests__/updateParentHierarchy.test.js +137 -0
- package/dist/utils/__tests__/updateParentHierarchy.test.js.map +1 -0
- package/dist/utils/autoCompleteParentHierarchy.d.ts +11 -0
- package/dist/utils/autoCompleteParentHierarchy.d.ts.map +1 -0
- package/dist/utils/autoCompleteParentHierarchy.js +49 -0
- package/dist/utils/autoCompleteParentHierarchy.js.map +1 -0
- package/dist/utils/checkHierarchicalPrerequisitesComplete.d.ts +14 -0
- package/dist/utils/checkHierarchicalPrerequisitesComplete.d.ts.map +1 -0
- package/dist/utils/checkHierarchicalPrerequisitesComplete.js +47 -0
- package/dist/utils/checkHierarchicalPrerequisitesComplete.js.map +1 -0
- package/dist/utils/filterUnavailableObjects.d.ts +6 -4
- package/dist/utils/filterUnavailableObjects.d.ts.map +1 -1
- package/dist/utils/filterUnavailableObjects.js +16 -22
- package/dist/utils/filterUnavailableObjects.js.map +1 -1
- package/dist/utils/index.d.ts +3 -2
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js +7 -3
- package/dist/utils/index.js.map +1 -1
- package/dist/utils/updateParentHierarchy.d.ts +11 -0
- package/dist/utils/updateParentHierarchy.d.ts.map +1 -0
- package/dist/utils/updateParentHierarchy.js +40 -0
- package/dist/utils/updateParentHierarchy.js.map +1 -0
- package/dist/validation/__tests__/validateObjectCreation.test.js +1 -0
- package/dist/validation/__tests__/validateObjectCreation.test.js.map +1 -1
- package/dist/validation/__tests__/validateParentExists.test.js +1 -0
- package/dist/validation/__tests__/validateParentExists.test.js.map +1 -1
- package/dist/validation/__tests__/validateStatusTransition.test.js +1 -0
- package/dist/validation/__tests__/validateStatusTransition.test.js.map +1 -1
- package/package.json +1 -1
- package/dist/__tests__/e2e/crud/replaceObjectBodyRegex.e2e.test.d.ts +0 -2
- package/dist/__tests__/e2e/crud/replaceObjectBodyRegex.e2e.test.d.ts.map +0 -1
- package/dist/__tests__/e2e/crud/replaceObjectBodyRegex.e2e.test.js +0 -693
- package/dist/__tests__/e2e/crud/replaceObjectBodyRegex.e2e.test.js.map +0 -1
- package/dist/__tests__/e2e/workflow/pruneClosed.e2e.test.d.ts +0 -2
- package/dist/__tests__/e2e/workflow/pruneClosed.e2e.test.d.ts.map +0 -1
- package/dist/__tests__/e2e/workflow/pruneClosed.e2e.test.js +0 -352
- package/dist/__tests__/e2e/workflow/pruneClosed.e2e.test.js.map +0 -1
- package/dist/services/local/__tests__/replaceObjectBodyRegex.test.d.ts +0 -2
- package/dist/services/local/__tests__/replaceObjectBodyRegex.test.d.ts.map +0 -1
- package/dist/services/local/__tests__/replaceObjectBodyRegex.test.js +0 -283
- package/dist/services/local/__tests__/replaceObjectBodyRegex.test.js.map +0 -1
- package/dist/services/local/replaceObjectBodyRegex.d.ts +0 -8
- package/dist/services/local/replaceObjectBodyRegex.d.ts.map +0 -1
- package/dist/services/local/replaceObjectBodyRegex.js +0 -85
- package/dist/services/local/replaceObjectBodyRegex.js.map +0 -1
- package/dist/tools/__tests__/pruneClosedTool.test.d.ts +0 -2
- package/dist/tools/__tests__/pruneClosedTool.test.d.ts.map +0 -1
- package/dist/tools/__tests__/pruneClosedTool.test.js +0 -112
- package/dist/tools/__tests__/pruneClosedTool.test.js.map +0 -1
- package/dist/tools/__tests__/replaceObjectBodyRegexTool.test.d.ts +0 -2
- package/dist/tools/__tests__/replaceObjectBodyRegexTool.test.d.ts.map +0 -1
- package/dist/tools/__tests__/replaceObjectBodyRegexTool.test.js +0 -89
- package/dist/tools/__tests__/replaceObjectBodyRegexTool.test.js.map +0 -1
- package/dist/tools/pruneClosedTool.d.ts +0 -27
- package/dist/tools/pruneClosedTool.d.ts.map +0 -1
- package/dist/tools/pruneClosedTool.js +0 -57
- package/dist/tools/pruneClosedTool.js.map +0 -1
- package/dist/tools/replaceObjectBodyRegexTool.d.ts +0 -36
- package/dist/tools/replaceObjectBodyRegexTool.d.ts.map +0 -1
- package/dist/tools/replaceObjectBodyRegexTool.js +0 -67
- package/dist/tools/replaceObjectBodyRegexTool.js.map +0 -1
- package/dist/utils/ReplaceStringOptions.d.ts +0 -12
- package/dist/utils/ReplaceStringOptions.d.ts.map +0 -1
- package/dist/utils/ReplaceStringOptions.js +0 -3
- package/dist/utils/ReplaceStringOptions.js.map +0 -1
- package/dist/utils/__tests__/replaceStringWithRegex.test.d.ts +0 -2
- package/dist/utils/__tests__/replaceStringWithRegex.test.d.ts.map +0 -1
- package/dist/utils/__tests__/replaceStringWithRegex.test.js +0 -281
- package/dist/utils/__tests__/replaceStringWithRegex.test.js.map +0 -1
- package/dist/utils/replaceStringWithRegex.d.ts +0 -45
- package/dist/utils/replaceStringWithRegex.d.ts.map +0 -1
- package/dist/utils/replaceStringWithRegex.js +0 -91
- 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("
|
|
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("
|
|
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("
|
|
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("
|
|
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("
|
|
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("
|
|
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("
|
|
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("
|
|
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("
|
|
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("
|
|
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("
|
|
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("
|
|
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("
|
|
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("
|
|
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("
|
|
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("
|
|
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("
|
|
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("
|
|
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("
|
|
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("
|
|
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("
|
|
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("
|
|
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("
|
|
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("
|
|
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("
|
|
1116
|
+
const result3 = await client.callTool("update_issue", {
|
|
645
1117
|
id: "T-sequential",
|
|
646
1118
|
body: "Final content after multiple updates",
|
|
647
1119
|
});
|