gitflic-cli-mcp 0.4.0 → 0.4.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/package.json +2 -2
  2. package/slim.mjs +24 -4
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gitflic-cli-mcp",
3
- "version": "0.4.0",
3
+ "version": "0.4.2",
4
4
  "description": "MCP server wrapping the gitflic CLI — exposes the GitFlic REST API as typed tools for AI agents (Claude Code, Cursor, etc.)",
5
5
  "type": "module",
6
6
  "bin": {
@@ -46,7 +46,7 @@
46
46
  },
47
47
  "dependencies": {
48
48
  "@modelcontextprotocol/sdk": "^1.0.0",
49
- "gitflic-cli": "^0.3.3",
49
+ "gitflic-cli": "0.3.5",
50
50
  "zod": "^3.25.76"
51
51
  }
52
52
  }
package/slim.mjs CHANGED
@@ -79,7 +79,12 @@ const P = {
79
79
  status: statusId(i.status),
80
80
  labels: labelTitles(i.labels) ?? [],
81
81
  assignedUsers: (i.assignedUsers || []).map(userName).filter(Boolean),
82
- author: i.userAlias ?? userName(i.createdBy),
82
+ // NOT i.userAlias: on an issue that is the *project owner's* alias (the
83
+ // `<userAlias>/<projectAlias>` path), identical on every issue in the repo —
84
+ // using it would report the owner as the author of everyone's issues.
85
+ // GitFlic's issue API exposes no author at all, so this stays undefined
86
+ // (key dropped on serialize) unless the API ever starts sending createdBy.
87
+ author: userName(i.createdBy),
83
88
  ...(i.updatedBy ? { updatedBy: userName(i.updatedBy) } : {}),
84
89
  createdAt: i.createdAt, updatedAt: i.updatedAt,
85
90
  ...(i.description != null ? { description: i.description } : {}),
@@ -209,7 +214,9 @@ export function applyListFilters(items, args = {}) {
209
214
  let out = items;
210
215
  if (args.label) out = out.filter((i) => (labelTitles(i.labels) || []).some((t) => ci(t).includes(ci(args.label))));
211
216
  if (args.assignee) out = out.filter((i) => (i.assignedUsers || []).some((u) => ci(userName(u)) === ci(args.assignee)));
212
- if (args.author) out = out.filter((i) => ci(userName(i.createdBy) || userName(i.user) || i.userAlias || i.authorIdent?.name).includes(ci(args.author)));
217
+ // Mirrors the author resolution in P.mr / slimCommit. i.userAlias is NOT in
218
+ // the chain on purpose — it is the project owner's alias, not an author.
219
+ if (args.author) out = out.filter((i) => ci(userName(i.createdBy) || userName(i.author) || userName(i.user) || i.authorIdent?.name).includes(ci(args.author)));
213
220
  if (args.status) out = out.filter((i) => ci(statusId(i.status)) === ci(args.status));
214
221
  if (args.targetBranch) out = out.filter((i) => ci(branchName(i.targetBranch)).includes(ci(args.targetBranch)));
215
222
  if (args.sourceBranch) out = out.filter((i) => ci(branchName(i.sourceBranch)).includes(ci(args.sourceBranch)));
@@ -223,7 +230,15 @@ export function applyListFilters(items, args = {}) {
223
230
  if (args.name) { const re = globToRe(args.name); out = out.filter((i) => re.test(i.name ?? "") || ci(i.name).includes(ci(args.name))); }
224
231
  if (args.q) {
225
232
  const q = ci(args.q);
226
- out = out.filter((i) => `${i.title ?? ""} ${i.description ?? ""} ${i.shortMessage ?? ""} ${i.message ?? ""}`.toLowerCase().includes(q));
233
+ // Haystack spans every entity's free-text/identity fields so `q` works for
234
+ // issues/MRs (title+description), commits (messages), users (username/
235
+ // fullName/name), projects (title/alias) and releases (tagName). Missing
236
+ // fields collapse to "" — harmless. (Bug fix: user_search used to drop all
237
+ // server results because the haystack lacked username/fullName.)
238
+ out = out.filter((i) =>
239
+ `${i.title ?? ""} ${i.description ?? ""} ${i.shortMessage ?? ""} ${i.message ?? ""} ${i.username ?? ""} ${i.fullName ?? ""} ${i.name ?? ""} ${i.alias ?? ""} ${i.tagName ?? ""}`
240
+ .toLowerCase()
241
+ .includes(q));
227
242
  }
228
243
  if (args.sort) {
229
244
  const desc = String(args.sort)[0] === "-";
@@ -279,7 +294,12 @@ export function slimResult(toolName, result, args = {}, env = process.env) {
279
294
  // Treat known list tools as lists even when GitFlic omits `_embedded` on a
280
295
  // zero-result response, so the {items,total,returned} contract is consistent.
281
296
  let lst = listOf(result);
282
- if (!lst && shape.list && result.page) lst = { items: [], total: result.page.totalElements };
297
+ // Zero-result lists omit `_embedded` entirely. Treat any list tool (mapped
298
+ // OR an unmapped *_list/*_search) carrying a `page` envelope as an empty list
299
+ // so the {items,total,returned} contract holds even when there are no items.
300
+ if (!lst && result.page && (shape.list || /_(list|search)$/.test(toolName))) {
301
+ lst = { items: [], total: result.page.totalElements };
302
+ }
283
303
 
284
304
  if (lst) {
285
305
  let items = Array.isArray(lst.items) ? lst.items : [];