engineering-memory 1.11.16 → 1.11.17

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "engineering-memory",
3
- "version": "1.11.16",
3
+ "version": "1.11.17",
4
4
  "description": "Installs the Engineering Memory skill and its local MCP bridge. Sign in after installing; your organization and project are resolved from your account.",
5
5
  "license": "UNLICENSED",
6
6
  "type": "module",
@@ -1,3 +1,3 @@
1
1
  {
2
- "gitHead": "9623849ae66a8cc72716533cb131790a4c517ac7"
2
+ "gitHead": "42ef1e9f90ef7b9d729457c45be97b755c737d1c"
3
3
  }
@@ -77,6 +77,7 @@ export const endpoints = {
77
77
  projectRestore: (projectId) => `/projects/${projectId}/restore`,
78
78
  projectMemberAdd: (projectId) => `/projects/${projectId}/members`,
79
79
  projectMemberList: (projectId) => `/projects/${projectId}/members`,
80
+ workItemStatuses: (projectId) => `/projects/${projectId}/work-items/statuses`,
80
81
  workItemList: (projectId) => `/projects/${projectId}/work-items`,
81
82
  workItemGet: (projectId, workItemId) => `/projects/${projectId}/work-items/${workItemId}`,
82
83
  projectLink: '/projects/link',
@@ -100,6 +100,7 @@ export const toolAnnotations = {
100
100
  'work_item.confirm_plan': write,
101
101
  'project.update': write,
102
102
  'work_item.list': read,
103
+ 'work_item.statuses': read,
103
104
  'work_item.get': read,
104
105
  'project.clone': outward,
105
106
  'project.link': write,
@@ -130,6 +130,7 @@ export const engineeringMemoryToolNames = [
130
130
  'work_item.plan',
131
131
  'work_item.confirm_plan',
132
132
  'project.update',
133
+ 'work_item.statuses',
133
134
  'work_item.list',
134
135
  'work_item.get',
135
136
  'project.clone',
@@ -932,7 +933,7 @@ export function registerEngineeringMemoryTools(server, service) {
932
933
  }),
933
934
  }, async (input) => toolResult(await service.workItemCreate(input)));
934
935
  server.registerTool('work_item.update', {
935
- description: "Edit or assign a work item at its current version. Assignees must already be active project members; this does not grant project access. Status is a slug from the project's own status catalogue; a backend that still keeps the six legacy values (backlog, ready, in_progress, in_review, done, cancelled) refuses anything else and names what it accepts.",
936
+ description: 'Edit or assign a work item at its current version. Assignees must already be active project members; this does not grant project access. Read work_item.statuses before changing status. Send the actual project catalogue slug and follow its user-defined meaning, entryRule and flags; never infer workflow from a status name.',
936
937
  inputSchema: z.object({
937
938
  ...workItemLocator,
938
939
  data: z.object({
@@ -1015,11 +1016,22 @@ export function registerEngineeringMemoryTools(server, service) {
1015
1016
  }),
1016
1017
  }),
1017
1018
  }, async (input) => toolResult(await service.projectUpdate(input)));
1019
+ server.registerTool('work_item.statuses', {
1020
+ description: 'Read the project status catalogue before choosing a work item status. Follow the user-defined meaning, entryRule and flags; never infer workflow from status names. Page with offset and limit (defaults 0 and 50, maximum 100). Archived statuses are excluded unless includeArchived is true.',
1021
+ inputSchema: z.object({
1022
+ projectId: z.string().uuid(),
1023
+ offset: z.number().int().min(0).optional(),
1024
+ limit: z.number().int().min(1).max(100).optional(),
1025
+ includeArchived: z.boolean().optional(),
1026
+ }),
1027
+ }, async (input) => toolResult(await service.workItemStatuses(input)));
1018
1028
  server.registerTool('work_item.list', {
1019
- description: "List selectable work items for a project before opening an engineering run in this chat. The status filter is a slug from the project's own status catalogue; a backend that still keeps the six legacy values (backlog, ready, in_progress, in_review, done, cancelled) refuses anything else and names what it accepts.",
1029
+ description: 'List selectable work items for a project before opening an engineering run in this chat. Read work_item.statuses for the actual project catalogue slugs and user-defined meanings; never infer workflow from status names. The status filter matches an actual slug, including a retained archived status; an unknown slug returns an empty page.',
1020
1030
  inputSchema: z.object({
1021
1031
  projectId: z.string().uuid(),
1022
1032
  status: z.string().trim().min(1).max(64).optional(),
1033
+ offset: z.number().int().min(0).optional(),
1034
+ limit: z.number().int().min(1).max(100).optional(),
1023
1035
  priority: z.enum(['lowest', 'low', 'medium', 'high', 'highest']).optional(),
1024
1036
  assigneeUserId: z.string().uuid().optional(),
1025
1037
  includeArchived: z.boolean().optional(),
@@ -20,6 +20,7 @@ export const backendRecoveryOperationNames = [
20
20
  'organization.list',
21
21
  'project.member_add',
22
22
  'work_item.list',
23
+ 'work_item.statuses',
23
24
  'work_item.get',
24
25
  'session.bootstrap',
25
26
  'session.resume',
@@ -3077,6 +3077,19 @@ export class BridgeService {
3077
3077
  });
3078
3078
  });
3079
3079
  }
3080
+ async workItemStatuses(input) {
3081
+ return this.execute(async () => {
3082
+ const query = new URLSearchParams({
3083
+ offset: String(input.offset ?? 0),
3084
+ limit: String(input.limit ?? 50),
3085
+ });
3086
+ if (input.includeArchived !== undefined) {
3087
+ query.set('includeArchived', String(input.includeArchived));
3088
+ }
3089
+ const response = await this.dependencies.client.request(`${endpoints.workItemStatuses(input.projectId)}?${query.toString()}`);
3090
+ return asJsonValue(response.data);
3091
+ });
3092
+ }
3080
3093
  async workItemList(input) {
3081
3094
  return await this.execute(async () => {
3082
3095
  const query = new URLSearchParams();
@@ -3089,6 +3102,10 @@ export class BridgeService {
3089
3102
  }
3090
3103
  if (input.includeArchived)
3091
3104
  query.set('includeArchived', 'true');
3105
+ if (input.offset !== undefined)
3106
+ query.set('offset', String(input.offset));
3107
+ if (input.limit !== undefined)
3108
+ query.set('limit', String(input.limit));
3092
3109
  const suffix = query.size > 0 ? `?${query.toString()}` : '';
3093
3110
  const response = await this.dependencies.client.request(`${endpoints.workItemList(input.projectId)}${suffix}`);
3094
3111
  return asJsonValue({
@@ -3551,30 +3568,15 @@ export class BridgeService {
3551
3568
  }
3552
3569
  }
3553
3570
  async actionableWorkItems(projectId) {
3554
- const path = `${endpoints.workItemList(projectId)}?limit=100`;
3555
3571
  try {
3556
- const chosen = await this.workItemPage(`${path}&actionable=true`).catch((error) => {
3557
- if (error instanceof ApiResponseError && error.httpStatus === 400)
3558
- return null;
3559
- throw error;
3560
- });
3561
- if (chosen && chosen.length > 0)
3562
- return chosen;
3563
- const every = await this.workItemPage(path);
3564
- if (chosen && every.some((entry) => objectValue(objectValue(entry)?.projectStatus) !== null))
3565
- return [];
3566
- const actionable = new Set(['backlog', 'ready', 'in_progress', 'in_review']);
3567
- return every.filter((entry) => actionable.has(String(objectValue(entry)?.status)));
3572
+ const response = await this.dependencies.client.request(`${endpoints.workItemList(projectId)}?limit=100&actionable=true`);
3573
+ const items = objectValue(response.data)?.items;
3574
+ return Array.isArray(items) ? items : [];
3568
3575
  }
3569
3576
  catch {
3570
3577
  return [];
3571
3578
  }
3572
3579
  }
3573
- async workItemPage(path) {
3574
- const response = await this.dependencies.client.request(path);
3575
- const items = objectValue(response.data)?.items;
3576
- return Array.isArray(items) ? items : [];
3577
- }
3578
3580
  async clientUpdate(authenticated) {
3579
3581
  const installed = this.dependencies.clientVersion;
3580
3582
  if (!authenticated) {
@@ -73,6 +73,14 @@ One WorkItem UUID coordinates independent EngineeringTask runs, one per selected
73
73
 
74
74
  Open each side with the same workItemId and its own repository binding when work reaches that side. Each side has its own lease, reconciliation, verify, close and commit gate. Check `work_item.runs` and sibling checkpoints when handing off; one side completing does not prove all selected projects are complete. A plan's scope and branch cannot change while an implementation run is active. Read-only analysis does not acquire application write rights; its later write transition must satisfy discipline and the confirmed plan.
75
75
 
76
+ ### Work item statuses
77
+
78
+ Read `work_item.statuses` before choosing a status for `work_item.update` or filtering `work_item.list`. The catalogue belongs to the project: follow its user-defined `meaning`, `entryRule` and flags. Never infer workflow from a name or translate statuses into the old six-value set. Send the actual catalogue slug. A work item's `status` is that slug, or null when its status link is invalid.
79
+
80
+ The catalogue returns `items`, `total`, `offset` and `limit`. Page with `offset` (default 0) and `limit` (default 50, maximum 100); use `includeArchived: true` to inspect retained archived statuses. Each row includes `id`, `slug`, `name`, `category`, `isInitial`, `isActionable`, `isParked`, `isTerminal`, `archivedAt`, `meaning`, `entryRule`, `position` and `lockVersion`. The work item's nested `projectStatus` remains its compact nine-field summary. `work_item.list` also accepts `offset` and `limit`; a status filter matches the actual slug, including retained archived statuses, and an unknown slug returns an empty page. Its `includeArchived` option concerns work items.
81
+
82
+ Session entry uses the backend's actionable selection. An empty page remains empty, and a failed lookup offers no work items; never retry an unfiltered list to classify statuses locally. Reuse existing user choices. Any unresolved workflow choice must use the native questionnaire.
83
+
76
84
  ### Administration, product management and QA
77
85
 
78
86
  Administrative authority and work discipline are independent. Organization admins can manage projects, assignments, repository URLs and work items in their organization. A global admin has those rights only in organizations they actively belong to. Ordinary users need a live explicit project assignment. Active organization administrators, including a global admin with live membership in that organization, inherit contributor access to its projects without a separate project grant. Fullstack/backend/web/mobile/frontend disciplines still govern implementation, while project overrides do not grant administration.