@firfi/huly-mcp 0.17.1 → 0.17.2

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 (2) hide show
  1. package/dist/index.cjs +117 -70
  2. package/package.json +1 -1
package/dist/index.cjs CHANGED
@@ -167891,7 +167891,7 @@ var parseDeleteTestResultParams = Schema_exports.decodeUnknown(DeleteTestResultP
167891
167891
  var parseRunTestPlanParams = Schema_exports.decodeUnknown(RunTestPlanParamsSchema);
167892
167892
 
167893
167893
  // src/version.ts
167894
- var VERSION = true ? "0.17.1" : "0.0.0-dev";
167894
+ var VERSION = true ? "0.17.2" : "0.0.0-dev";
167895
167895
 
167896
167896
  // src/mcp/error-mapping.ts
167897
167897
  var McpErrorCode = {
@@ -168904,6 +168904,36 @@ var findProject = (projectIdentifier) => Effect_exports.gen(function* () {
168904
168904
  );
168905
168905
  return { client, project: project3 };
168906
168906
  });
168907
+ var statusInfoFromDoc = (doc) => {
168908
+ const categoryStr = doc.category ? doc.category : "";
168909
+ return {
168910
+ _id: doc._id,
168911
+ name: doc.name,
168912
+ isDone: categoryStr === task.statusCategory.Won,
168913
+ isCanceled: categoryStr === task.statusCategory.Lost
168914
+ };
168915
+ };
168916
+ var fallbackStatusInfoFromRef = (statusRef) => {
168917
+ const name = statusRef.includes(":") ? statusRef.slice(statusRef.lastIndexOf(":") + 1) : statusRef;
168918
+ const nameLower = name.toLowerCase();
168919
+ const isDone5 = nameLower.includes("done") || nameLower.includes("complete") || nameLower.includes("finished") || nameLower.includes("resolved") || nameLower.includes("closed");
168920
+ const isCanceled = nameLower.includes("cancel") || nameLower.includes("reject") || nameLower.includes("abort") || nameLower.includes("wontfix") || nameLower.includes("invalid");
168921
+ return {
168922
+ _id: statusRef,
168923
+ name,
168924
+ isDone: isDone5,
168925
+ isCanceled
168926
+ };
168927
+ };
168928
+ var uniqueStatusRefs = (refs) => refs.reduce(
168929
+ (unique, ref) => unique.includes(ref) ? unique : [...unique, ref],
168930
+ []
168931
+ );
168932
+ var uniqueStatusDocs = (statuses) => Array.from(statuses).reduce(
168933
+ (unique, status) => unique.some((existing) => existing._id === status._id) ? unique : [...unique, status],
168934
+ []
168935
+ );
168936
+ var uniqueProjectTypeStatusRefs = (statuses) => uniqueStatusRefs(statuses.map((status) => status._id));
168907
168937
  var findProjectWithStatuses = (projectIdentifier) => Effect_exports.gen(function* () {
168908
168938
  const client = yield* HulyClient;
168909
168939
  const project3 = yield* findOneOrFail(
@@ -168914,45 +168944,25 @@ var findProjectWithStatuses = (projectIdentifier) => Effect_exports.gen(function
168914
168944
  { lookup: { type: task.class.ProjectType } }
168915
168945
  );
168916
168946
  const projectType = project3.$lookup?.type;
168917
- const statuses = [];
168918
- if (projectType?.statuses) {
168919
- const statusRefs = projectType.statuses.map((s) => s._id);
168920
- if (statusRefs.length > 0) {
168921
- const statusDocsResult = yield* Effect_exports.either(
168922
- client.findAll(
168923
- core.class.Status,
168924
- hulyQuery({ _id: { $in: statusRefs } })
168925
- )
168926
- );
168927
- if (statusDocsResult._tag === "Right") {
168928
- for (const doc of statusDocsResult.right) {
168929
- const categoryStr = doc.category ? doc.category : "";
168930
- statuses.push({
168931
- _id: doc._id,
168932
- name: doc.name,
168933
- isDone: categoryStr === task.statusCategory.Won,
168934
- isCanceled: categoryStr === task.statusCategory.Lost
168935
- });
168936
- }
168937
- } else {
168938
- yield* Effect_exports.logWarning(
168939
- `Status query failed for project ${projectIdentifier}, using fallback. Category-based filtering (open/done/canceled) will use name heuristics. Error: ${statusDocsResult.left.message}`
168940
- );
168941
- for (const ps of projectType.statuses) {
168942
- const name = ps._id.split(":").pop() ?? "Unknown";
168943
- const nameLower = name.toLowerCase();
168944
- const isDone5 = nameLower.includes("done") || nameLower.includes("complete") || nameLower.includes("finished") || nameLower.includes("resolved") || nameLower.includes("closed");
168945
- const isCanceled = nameLower.includes("cancel") || nameLower.includes("reject") || nameLower.includes("abort") || nameLower.includes("wontfix") || nameLower.includes("invalid");
168946
- statuses.push({
168947
- _id: ps._id,
168948
- name,
168949
- isDone: isDone5,
168950
- isCanceled
168951
- });
168952
- }
168953
- }
168947
+ const statuses = projectType?.statuses ? yield* Effect_exports.gen(function* () {
168948
+ const statusRefs = uniqueProjectTypeStatusRefs(projectType.statuses);
168949
+ if (statusRefs.length === 0) {
168950
+ return [];
168954
168951
  }
168955
- }
168952
+ const statusDocsResult = yield* Effect_exports.either(
168953
+ client.findAll(
168954
+ core.class.Status,
168955
+ hulyQuery({ _id: { $in: statusRefs } })
168956
+ )
168957
+ );
168958
+ if (statusDocsResult._tag === "Right") {
168959
+ return uniqueStatusDocs(statusDocsResult.right).map(statusInfoFromDoc);
168960
+ }
168961
+ yield* Effect_exports.logWarning(
168962
+ `Status query failed for project ${projectIdentifier}, using fallback. Category-based filtering (open/done/canceled) will use name heuristics. Error: ${statusDocsResult.left.message}`
168963
+ );
168964
+ return statusRefs.map(fallbackStatusInfoFromRef);
168965
+ }) : [];
168956
168966
  const defaultStatusId = project3.defaultIssueStatus || statuses[0]?._id;
168957
168967
  return { client, defaultStatusId, project: project3, projectType, statuses };
168958
168968
  });
@@ -177709,9 +177719,15 @@ var encodeOrConnectionError2 = (schema, value3, operation) => Schema_exports.enc
177709
177719
  })
177710
177720
  )
177711
177721
  );
177712
- var uniqueStatusIds = (projectType) => Array.from(new Set(projectType.statuses.map((status) => status._id)));
177722
+ var uniqueTaskTypeRefs = (refs) => refs.reduce((unique, ref) => unique.includes(ref) ? unique : [...unique, ref], []);
177723
+ var uniqueStatusIds = (projectType) => uniqueStatusRefs(projectType.statuses.map((status) => status._id));
177724
+ var sameProjectStatus = (left3, right3) => left3._id === right3._id && left3.taskType === right3.taskType;
177725
+ var uniqueProjectStatuses = (statuses) => statuses.reduce(
177726
+ (unique, status) => unique.some((existing) => sameProjectStatus(existing, status)) ? unique : [...unique, status],
177727
+ []
177728
+ );
177713
177729
  var getStatusDocs = (client, statusIds) => statusIds.length === 0 ? Effect_exports.succeed([]) : client.findAll(core.class.Status, hulyQuery({ _id: { $in: [...statusIds] } })).pipe(
177714
- Effect_exports.map((result) => [...result])
177730
+ Effect_exports.map(uniqueStatusDocs)
177715
177731
  );
177716
177732
  var getTaskTypes = (client, taskTypeIds) => taskTypeIds.length === 0 ? Effect_exports.succeed([]) : client.findAll(task.class.TaskType, hulyQuery({ _id: { $in: [...taskTypeIds] } })).pipe(
177717
177733
  Effect_exports.map((result) => [...result])
@@ -177743,7 +177759,11 @@ var projectTypeSummary = (data) => ({
177743
177759
  statusCount: uniqueStatusIds(data.projectType).length,
177744
177760
  isDefaultClassic: isDefaultClassicProjectType(data.projectType)
177745
177761
  });
177746
- var statusTaskTypeIds = (projectType, statusId) => projectType.statuses.filter((status) => status._id === statusId).map((status) => status.taskType);
177762
+ var statusTaskTypeIds = (projectType, statusId) => uniqueTaskTypeRefs(
177763
+ uniqueProjectStatuses(projectType.statuses).filter((status) => status._id === statusId).map(
177764
+ (status) => status.taskType
177765
+ )
177766
+ );
177747
177767
  var statusSummary = (projectType, status) => ({
177748
177768
  id: IssueStatusId.make(status._id),
177749
177769
  name: status.name,
@@ -177757,7 +177777,7 @@ var taskTypeSummary = (projectType, taskType) => ({
177757
177777
  projectTypeName: projectType.name,
177758
177778
  kind: taskType.kind,
177759
177779
  issueClass: taskType.ofClass,
177760
- statusCount: taskType.statuses.length
177780
+ statusCount: uniqueStatusRefs(taskType.statuses).length
177761
177781
  });
177762
177782
  var projectTypeDetail = (data) => ({
177763
177783
  ...projectTypeSummary(data),
@@ -177769,7 +177789,7 @@ var projectTypeDetail = (data) => ({
177769
177789
  taskTypeStatuses: data.taskTypes.map((taskType) => ({
177770
177790
  taskTypeId: TaskTypeId.make(taskType._id),
177771
177791
  taskTypeName: taskType.name,
177772
- statusIds: taskType.statuses.map((statusId) => IssueStatusId.make(statusId))
177792
+ statusIds: uniqueStatusRefs(taskType.statuses).map((statusId) => IssueStatusId.make(statusId))
177773
177793
  }))
177774
177794
  });
177775
177795
  var listAllProjectTypes = (client) => client.findAll(
@@ -177809,7 +177829,12 @@ var resolveStatusClass = (taskTypes) => {
177809
177829
  const statusClass = statusClasses.at(0);
177810
177830
  return statusClasses.length === 1 && statusClass !== void 0 ? Effect_exports.succeed(statusClass) : Effect_exports.fail(new HulyError({ message: "Target task types do not share one issue status class." }));
177811
177831
  };
177812
- var replaceOrAppendProjectStatus = (statuses, statusId, taskTypeId) => statuses.some((status) => status._id === statusId && status.taskType === taskTypeId) ? statuses : [...statuses, { _id: statusId, taskType: taskTypeId }];
177832
+ var replaceOrAppendProjectStatus = (statuses, statusId, taskTypeId) => statuses.some((status) => status._id === statusId && status.taskType === taskTypeId) ? [...statuses] : [...statuses, { _id: statusId, taskType: taskTypeId }];
177833
+ var sameStatusRefList = (left3, right3) => left3.length === right3.length && left3.every((value3, index) => value3 === right3[index]);
177834
+ var sameProjectStatusList = (left3, right3) => left3.length === right3.length && left3.every((value3, index) => {
177835
+ const rightValue = right3[index];
177836
+ return sameProjectStatus(value3, rightValue);
177837
+ });
177813
177838
  var listProjectTypes = (_params) => Effect_exports.gen(function* () {
177814
177839
  const client = yield* HulyClient;
177815
177840
  const projectTypes = yield* listAllProjectTypes(client);
@@ -177842,29 +177867,42 @@ var createTaskType = (params) => Effect_exports.gen(function* () {
177842
177867
  const workflowData = yield* loadWorkflowData(client, projectType);
177843
177868
  const allProjectTaskTypes = yield* getTaskTypesByProjectType(client, projectType._id);
177844
177869
  const existing = existingTaskTypeByName(allProjectTaskTypes, params.name);
177870
+ const normalizedProjectStatuses = uniqueProjectStatuses(projectType.statuses);
177845
177871
  if (existing !== void 0) {
177872
+ const existingTaskTypeStatuses = uniqueStatusRefs(existing.statuses);
177873
+ const taskTypeChanged = !sameStatusRefList(existingTaskTypeStatuses, existing.statuses);
177846
177874
  const projectTypeTasks = projectType.tasks.includes(existing._id) ? projectType.tasks : [...projectType.tasks, existing._id];
177847
- const existingProjectStatuses = existing.statuses.reduce(
177875
+ const existingProjectStatuses = existingTaskTypeStatuses.reduce(
177848
177876
  (statuses, statusId) => replaceOrAppendProjectStatus(statuses, statusId, existing._id),
177849
- [...projectType.statuses]
177877
+ normalizedProjectStatuses
177850
177878
  );
177851
- const projectTypeChanged = projectTypeTasks.length !== projectType.tasks.length || existingProjectStatuses.length !== projectType.statuses.length;
177879
+ const projectTypeChanged = projectTypeTasks.length !== projectType.tasks.length || !sameProjectStatusList(existingProjectStatuses, projectType.statuses);
177880
+ if (taskTypeChanged) {
177881
+ yield* client.updateDoc(
177882
+ task.class.TaskType,
177883
+ core.space.Model,
177884
+ existing._id,
177885
+ { statuses: [...existingTaskTypeStatuses] }
177886
+ );
177887
+ }
177852
177888
  if (projectTypeChanged) {
177853
177889
  yield* client.updateDoc(
177854
177890
  task.class.ProjectType,
177855
177891
  core.space.Model,
177856
177892
  projectType._id,
177857
- { tasks: projectTypeTasks, statuses: existingProjectStatuses }
177893
+ { tasks: projectTypeTasks, statuses: [...existingProjectStatuses] }
177858
177894
  );
177859
177895
  }
177860
177896
  const result2 = {
177861
- created: projectTypeChanged,
177897
+ created: taskTypeChanged || projectTypeChanged,
177862
177898
  projectType: projectTypeSummary({
177863
177899
  projectType: { ...projectType, tasks: projectTypeTasks, statuses: existingProjectStatuses },
177864
- taskTypes: workflowData.taskTypes.some((taskType) => taskType._id === existing._id) ? workflowData.taskTypes : [...workflowData.taskTypes, existing],
177900
+ taskTypes: workflowData.taskTypes.some((taskType) => taskType._id === existing._id) ? workflowData.taskTypes.map(
177901
+ (taskType) => taskType._id === existing._id ? { ...taskType, statuses: existingTaskTypeStatuses } : taskType
177902
+ ) : [...workflowData.taskTypes, { ...existing, statuses: existingTaskTypeStatuses }],
177865
177903
  statuses: workflowData.statuses
177866
177904
  }),
177867
- taskType: taskTypeSummary(projectType, existing),
177905
+ taskType: taskTypeSummary(projectType, { ...existing, statuses: existingTaskTypeStatuses }),
177868
177906
  affectedTaskTypeIds: [TaskTypeId.make(existing._id)],
177869
177907
  warning: WORKFLOW_WARNING
177870
177908
  };
@@ -177879,6 +177917,7 @@ var createTaskType = (params) => Effect_exports.gen(function* () {
177879
177917
  const taskTypeId = (0, import_core38.generateId)();
177880
177918
  const targetClassId = `${taskTypeId}:type:mixin`;
177881
177919
  const targetClassRef = toRef(targetClassId);
177920
+ const templateStatusIds = uniqueStatusRefs(template.statuses);
177882
177921
  yield* client.createDoc(
177883
177922
  core.class.Mixin,
177884
177923
  core.space.Model,
@@ -177907,7 +177946,7 @@ var createTaskType = (params) => Effect_exports.gen(function* () {
177907
177946
  kind: template.kind,
177908
177947
  ofClass: template.ofClass,
177909
177948
  targetClass: targetClassRef,
177910
- statuses: [...template.statuses],
177949
+ statuses: templateStatusIds,
177911
177950
  statusClass: template.statusClass,
177912
177951
  statusCategories: [...template.statusCategories],
177913
177952
  ...template.allowedAsChildOf === void 0 ? {} : { allowedAsChildOf: template.allowedAsChildOf },
@@ -177922,18 +177961,23 @@ var createTaskType = (params) => Effect_exports.gen(function* () {
177922
177961
  {
177923
177962
  tasks: [...projectType.tasks, taskTypeId],
177924
177963
  statuses: [
177925
- ...projectType.statuses,
177926
- ...template.statuses.map((statusId) => ({ _id: statusId, taskType: taskTypeId }))
177964
+ ...normalizedProjectStatuses,
177965
+ ...templateStatusIds.map((statusId) => ({ _id: statusId, taskType: taskTypeId }))
177927
177966
  ]
177928
177967
  }
177929
177968
  );
177969
+ const createdProjectStatuses = [
177970
+ ...normalizedProjectStatuses,
177971
+ ...templateStatusIds.map((statusId) => ({ _id: statusId, taskType: taskTypeId }))
177972
+ ];
177930
177973
  const createdTaskType = {
177931
177974
  ...template,
177932
177975
  _id: taskTypeId,
177933
177976
  parent: projectType._id,
177934
177977
  name: params.name,
177935
177978
  kind: template.kind,
177936
- targetClass: targetClassRef
177979
+ targetClass: targetClassRef,
177980
+ statuses: templateStatusIds
177937
177981
  };
177938
177982
  const result = {
177939
177983
  created: true,
@@ -177941,10 +177985,7 @@ var createTaskType = (params) => Effect_exports.gen(function* () {
177941
177985
  projectType: {
177942
177986
  ...projectType,
177943
177987
  tasks: [...projectType.tasks, taskTypeId],
177944
- statuses: [
177945
- ...projectType.statuses,
177946
- ...template.statuses.map((statusId) => ({ _id: statusId, taskType: taskTypeId }))
177947
- ]
177988
+ statuses: createdProjectStatuses
177948
177989
  },
177949
177990
  taskTypes: [...workflowData.taskTypes, createdTaskType],
177950
177991
  statuses: workflowData.statuses
@@ -177959,6 +178000,7 @@ var createIssueStatus = (params) => Effect_exports.gen(function* () {
177959
178000
  const client = yield* HulyClient;
177960
178001
  const projectType = yield* resolveProjectType(client, params.projectType);
177961
178002
  const workflowData = yield* loadWorkflowData(client, projectType);
178003
+ const normalizedProjectStatuses = uniqueProjectStatuses(projectType.statuses);
177962
178004
  const targetTaskTypes = params.taskType === void 0 ? workflowData.taskTypes : [yield* resolveTaskType(workflowData.taskTypes, params.taskType)];
177963
178005
  const statusClass = yield* resolveStatusClass(targetTaskTypes);
177964
178006
  const statusesByName = yield* getRecoverableStatusesByName(client, params.name);
@@ -177982,28 +178024,33 @@ var createIssueStatus = (params) => Effect_exports.gen(function* () {
177982
178024
  statusId
177983
178025
  );
177984
178026
  }
177985
- const taskTypesMissingStatus = targetTaskTypes.filter((taskType) => !taskType.statuses.includes(statusId));
178027
+ const taskTypesNeedingStatusUpdate = targetTaskTypes.filter((taskType) => {
178028
+ const normalizedStatuses = uniqueStatusRefs(taskType.statuses);
178029
+ return !normalizedStatuses.includes(statusId) || !sameStatusRefList(normalizedStatuses, taskType.statuses);
178030
+ });
177986
178031
  yield* Effect_exports.all(
177987
- taskTypesMissingStatus.map(
177988
- (taskType) => client.updateDoc(
178032
+ taskTypesNeedingStatusUpdate.map((taskType) => {
178033
+ const normalizedStatuses = uniqueStatusRefs(taskType.statuses);
178034
+ const updatedStatuses = normalizedStatuses.includes(statusId) ? normalizedStatuses : [...normalizedStatuses, statusId];
178035
+ return client.updateDoc(
177989
178036
  task.class.TaskType,
177990
178037
  core.space.Model,
177991
178038
  taskType._id,
177992
- { statuses: [...taskType.statuses, statusId] }
177993
- )
177994
- )
178039
+ { statuses: [...updatedStatuses] }
178040
+ );
178041
+ })
177995
178042
  );
177996
178043
  const updatedProjectStatuses = targetTaskTypes.reduce(
177997
178044
  (statuses, taskType) => replaceOrAppendProjectStatus(statuses, statusId, taskType._id),
177998
- [...projectType.statuses]
178045
+ normalizedProjectStatuses
177999
178046
  );
178000
- const projectTypeChanged = updatedProjectStatuses.length !== projectType.statuses.length;
178047
+ const projectTypeChanged = !sameProjectStatusList(updatedProjectStatuses, projectType.statuses);
178001
178048
  if (projectTypeChanged) {
178002
178049
  yield* client.updateDoc(
178003
178050
  task.class.ProjectType,
178004
178051
  core.space.Model,
178005
178052
  projectType._id,
178006
- { statuses: updatedProjectStatuses }
178053
+ { statuses: [...updatedProjectStatuses] }
178007
178054
  );
178008
178055
  }
178009
178056
  const statusDoc = existingStatus ?? {
@@ -178017,7 +178064,7 @@ var createIssueStatus = (params) => Effect_exports.gen(function* () {
178017
178064
  category: CATEGORY_TO_REF[params.category]
178018
178065
  };
178019
178066
  const result = {
178020
- created: existingStatus === void 0 || taskTypesMissingStatus.length > 0 || projectTypeChanged,
178067
+ created: existingStatus === void 0 || taskTypesNeedingStatusUpdate.length > 0 || projectTypeChanged,
178021
178068
  projectType: projectTypeSummary({
178022
178069
  projectType: { ...projectType, statuses: updatedProjectStatuses },
178023
178070
  taskTypes: workflowData.taskTypes,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@firfi/huly-mcp",
3
- "version": "0.17.1",
3
+ "version": "0.17.2",
4
4
  "description": "MCP server for Huly integration",
5
5
  "mcpName": "io.github.dearlordylord/huly-mcp",
6
6
  "type": "module",