memory-journal-mcp 7.1.0 → 7.2.0

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 (124) hide show
  1. package/README.md +59 -51
  2. package/dist/{chunk-GW5DYUQJ.js → chunk-GR4T3SRW.js} +139 -100
  3. package/dist/{chunk-37BQOJDZ.js → chunk-IWKLHSPU.js} +81 -2
  4. package/dist/{chunk-JEGRDY6W.js → chunk-ORV7ZZOE.js} +357 -51
  5. package/dist/cli.js +30 -4
  6. package/dist/github-integration-2TFMXHIJ.js +1 -0
  7. package/dist/index.d.ts +5 -1
  8. package/dist/index.js +3 -3
  9. package/dist/{tools-O44Q52RD.js → tools-CXR2FEB2.js} +2 -2
  10. package/package.json +1 -1
  11. package/skills/README.md +77 -0
  12. package/skills/autonomous-dev/SKILL.md +56 -0
  13. package/skills/bin/sync.js +50 -0
  14. package/skills/bun/SKILL.md +156 -0
  15. package/skills/github-commander/SKILL.md +1 -1
  16. package/skills/github-commander/workflows/code-quality-audit.md +7 -5
  17. package/skills/github-commander/workflows/issue-triage.md +13 -4
  18. package/skills/github-commander/workflows/milestone-sprint.md +9 -1
  19. package/skills/github-commander/workflows/perf-audit.md +2 -0
  20. package/skills/github-commander/workflows/pr-review.md +9 -3
  21. package/skills/github-commander/workflows/roadmap-kickoff.md +79 -0
  22. package/skills/github-commander/workflows/security-audit.md +3 -3
  23. package/skills/github-commander/workflows/update-deps.md +2 -2
  24. package/skills/gitlab/SKILL.md +115 -0
  25. package/skills/gitlab/package-lock.json +392 -0
  26. package/skills/gitlab/package.json +14 -0
  27. package/skills/gitlab/scripts/gitlab-client.ts +125 -0
  28. package/skills/gitlab/scripts/gitlab-helper.ts +80 -0
  29. package/skills/golang/SKILL.md +54 -0
  30. package/skills/mysql/SKILL.md +30 -0
  31. package/skills/package.json +48 -0
  32. package/skills/playwright-standard/SKILL.md +58 -0
  33. package/skills/playwright-standard/examples/fixtures.ts +66 -0
  34. package/skills/playwright-standard/examples/type-stubs.d.ts +10 -0
  35. package/skills/playwright-standard/references/advanced-scenarios.md +59 -0
  36. package/skills/playwright-standard/references/infrastructure.md +43 -0
  37. package/skills/postgres/SKILL.md +33 -0
  38. package/skills/react-best-practices/AGENTS.md +2883 -0
  39. package/skills/react-best-practices/README.md +127 -0
  40. package/skills/react-best-practices/SKILL.md +138 -0
  41. package/skills/react-best-practices/metadata.json +17 -0
  42. package/skills/react-best-practices/rules/_sections.md +46 -0
  43. package/skills/react-best-practices/rules/_template.md +28 -0
  44. package/skills/react-best-practices/rules/advanced-event-handler-refs.md +55 -0
  45. package/skills/react-best-practices/rules/advanced-init-once.md +42 -0
  46. package/skills/react-best-practices/rules/advanced-use-latest.md +39 -0
  47. package/skills/react-best-practices/rules/async-api-routes.md +35 -0
  48. package/skills/react-best-practices/rules/async-defer-await.md +80 -0
  49. package/skills/react-best-practices/rules/async-dependencies.md +48 -0
  50. package/skills/react-best-practices/rules/async-parallel.md +24 -0
  51. package/skills/react-best-practices/rules/async-suspense-boundaries.md +99 -0
  52. package/skills/react-best-practices/rules/bundle-barrel-imports.md +59 -0
  53. package/skills/react-best-practices/rules/bundle-conditional.md +37 -0
  54. package/skills/react-best-practices/rules/bundle-defer-third-party.md +48 -0
  55. package/skills/react-best-practices/rules/bundle-dynamic-imports.md +34 -0
  56. package/skills/react-best-practices/rules/bundle-preload.md +44 -0
  57. package/skills/react-best-practices/rules/client-event-listeners.md +78 -0
  58. package/skills/react-best-practices/rules/client-localstorage-schema.md +74 -0
  59. package/skills/react-best-practices/rules/client-passive-event-listeners.md +48 -0
  60. package/skills/react-best-practices/rules/client-swr-dedup.md +56 -0
  61. package/skills/react-best-practices/rules/js-batch-dom-css.md +110 -0
  62. package/skills/react-best-practices/rules/js-cache-function-results.md +80 -0
  63. package/skills/react-best-practices/rules/js-cache-property-access.md +28 -0
  64. package/skills/react-best-practices/rules/js-cache-storage.md +68 -0
  65. package/skills/react-best-practices/rules/js-combine-iterations.md +32 -0
  66. package/skills/react-best-practices/rules/js-early-exit.md +50 -0
  67. package/skills/react-best-practices/rules/js-hoist-regexp.md +45 -0
  68. package/skills/react-best-practices/rules/js-index-maps.md +37 -0
  69. package/skills/react-best-practices/rules/js-length-check-first.md +50 -0
  70. package/skills/react-best-practices/rules/js-min-max-loop.md +82 -0
  71. package/skills/react-best-practices/rules/js-set-map-lookups.md +24 -0
  72. package/skills/react-best-practices/rules/js-tosorted-immutable.md +57 -0
  73. package/skills/react-best-practices/rules/rendering-activity.md +24 -0
  74. package/skills/react-best-practices/rules/rendering-animate-svg-wrapper.md +38 -0
  75. package/skills/react-best-practices/rules/rendering-conditional-render.md +32 -0
  76. package/skills/react-best-practices/rules/rendering-content-visibility.md +38 -0
  77. package/skills/react-best-practices/rules/rendering-hoist-jsx.md +36 -0
  78. package/skills/react-best-practices/rules/rendering-hydration-no-flicker.md +72 -0
  79. package/skills/react-best-practices/rules/rendering-hydration-suppress-warning.md +26 -0
  80. package/skills/react-best-practices/rules/rendering-svg-precision.md +28 -0
  81. package/skills/react-best-practices/rules/rendering-usetransition-loading.md +75 -0
  82. package/skills/react-best-practices/rules/rerender-defer-reads.md +39 -0
  83. package/skills/react-best-practices/rules/rerender-dependencies.md +45 -0
  84. package/skills/react-best-practices/rules/rerender-derived-state-no-effect.md +40 -0
  85. package/skills/react-best-practices/rules/rerender-derived-state.md +29 -0
  86. package/skills/react-best-practices/rules/rerender-functional-setstate.md +77 -0
  87. package/skills/react-best-practices/rules/rerender-lazy-state-init.md +56 -0
  88. package/skills/react-best-practices/rules/rerender-memo-with-default-value.md +36 -0
  89. package/skills/react-best-practices/rules/rerender-memo.md +44 -0
  90. package/skills/react-best-practices/rules/rerender-move-effect-to-event.md +45 -0
  91. package/skills/react-best-practices/rules/rerender-simple-expression-in-memo.md +35 -0
  92. package/skills/react-best-practices/rules/rerender-transitions.md +40 -0
  93. package/skills/react-best-practices/rules/rerender-use-ref-transient-values.md +73 -0
  94. package/skills/react-best-practices/rules/server-after-nonblocking.md +73 -0
  95. package/skills/react-best-practices/rules/server-auth-actions.md +96 -0
  96. package/skills/react-best-practices/rules/server-cache-lru.md +41 -0
  97. package/skills/react-best-practices/rules/server-cache-react.md +76 -0
  98. package/skills/react-best-practices/rules/server-dedup-props.md +65 -0
  99. package/skills/react-best-practices/rules/server-parallel-fetching.md +83 -0
  100. package/skills/react-best-practices/rules/server-serialization.md +38 -0
  101. package/skills/rust/SKILL.md +86 -0
  102. package/skills/shadcn-ui/SKILL.md +72 -0
  103. package/skills/skill-builder/SKILL.md +457 -0
  104. package/skills/skill-builder/checklist.md +65 -0
  105. package/skills/sqlite/SKILL.md +38 -0
  106. package/skills/typescript/SKILL.md +453 -0
  107. package/skills/typescript/assets/eslint-template.js +102 -0
  108. package/skills/typescript/assets/tsconfig-template.json +45 -0
  109. package/skills/typescript/references/enterprise-patterns.md +531 -0
  110. package/skills/typescript/references/generics.md +493 -0
  111. package/skills/typescript/references/nestjs-integration.md +579 -0
  112. package/skills/typescript/references/react-integration.md +616 -0
  113. package/skills/typescript/references/toolchain.md +547 -0
  114. package/skills/typescript/references/type-system.md +481 -0
  115. package/skills/vitest-standard/SKILL.md +82 -0
  116. package/skills/vitest-standard/examples/service-mock.ts +60 -0
  117. package/skills/vitest-standard/examples/tdd-calculator.ts +41 -0
  118. package/skills/vitest-standard/examples/type-stubs.d.ts +18 -0
  119. package/skills/vitest-standard/references/async-and-errors.md +58 -0
  120. package/skills/vitest-standard/references/coverage-and-config.md +53 -0
  121. package/skills/vitest-standard/references/mocking.md +61 -0
  122. package/skills/vitest-standard/references/tdd-patterns.md +60 -0
  123. package/dist/github-integration-FOJ4U6I5.js +0 -1
  124. package/skills/github-commander/workflows/full-audit.md +0 -134
@@ -1,5 +1,5 @@
1
1
  import { transformAutoReturn } from './chunk-OKOVZ5QE.js';
2
- import { GitHubIntegration, resolveAuthor, logger, assertSafeDirectoryPath, MemoryJournalMcpError, matchSuggestion, ConfigurationError } from './chunk-37BQOJDZ.js';
2
+ import { GitHubIntegration, resolveAuthor, logger, ValidationError, ResourceNotFoundError, assertSafeDirectoryPath, MemoryJournalMcpError, matchSuggestion, ConfigurationError } from './chunk-IWKLHSPU.js';
3
3
  import { z, ZodError } from 'zod';
4
4
  import { open, stat, mkdir, rename, appendFile, readdir, readFile } from 'fs/promises';
5
5
  import { tmpdir } from 'os';
@@ -59,7 +59,7 @@ async function resolveIssueUrl(context, projectNumber, issueNumber, existingUrl)
59
59
  ([_, v]) => v.project_number === projectNumber
60
60
  );
61
61
  if (entry) {
62
- const { GitHubIntegration: GitHubIntegration2 } = await import('./github-integration-FOJ4U6I5.js');
62
+ const { GitHubIntegration: GitHubIntegration2 } = await import('./github-integration-2TFMXHIJ.js');
63
63
  const targetGithub = new GitHubIntegration2(entry[1].path);
64
64
  const repoInfo = await targetGithub.getRepoInfo();
65
65
  if (repoInfo.owner && repoInfo.repo) {
@@ -114,6 +114,7 @@ var SIGNIFICANCE_TYPES = [
114
114
  "release"
115
115
  ];
116
116
  var MAX_CONTENT_LENGTH = 5e4;
117
+ var MAX_QUERY_LIMIT = 500;
117
118
  var DATE_MIN_SENTINEL = "1970-01-01";
118
119
  var DATE_MAX_SENTINEL = "2999-12-31";
119
120
  var DATE_FORMAT_REGEX = /^\d{4}-\d{2}-\d{2}$/;
@@ -215,7 +216,7 @@ var GetEntryByIdSchemaMcp = z.object({
215
216
  include_relationships: z.boolean().optional().default(true)
216
217
  });
217
218
  var GetRecentEntriesSchema = z.object({
218
- limit: z.number().max(500).optional().default(5),
219
+ limit: z.number().min(1).max(MAX_QUERY_LIMIT).optional().default(5),
219
220
  is_personal: z.boolean().optional()
220
221
  });
221
222
  var GetRecentEntriesSchemaMcp = z.object({
@@ -452,7 +453,6 @@ function getCoreTools(context) {
452
453
  }
453
454
 
454
455
  // src/handlers/tools/search/helpers.ts
455
- var MAX_QUERY_LIMIT = 500;
456
456
  var DEDUP_KEY_LENGTH = 200;
457
457
  function calcPerDbLimit(limit, hasTeamDb) {
458
458
  return hasTeamDb ? Math.min(limit * 2, MAX_QUERY_LIMIT) : limit;
@@ -730,11 +730,23 @@ function getSearchTools(context) {
730
730
  handler: async (params) => {
731
731
  try {
732
732
  const input = SearchEntriesSchema.parse(params);
733
- const query = input.query || "";
733
+ const query = input.query?.trim() || "";
734
734
  const mode = input.mode;
735
- const { resolvedMode, isAuto } = resolveSearchMode(mode, query);
736
735
  const hasFilters = input.project_number !== void 0 || input.issue_number !== void 0 || input.pr_number !== void 0 || input.pr_status !== void 0 || input.workflow_run_id !== void 0 || input.is_personal !== void 0 || input.tags !== void 0 || input.entry_type !== void 0 || input.start_date !== void 0 || input.end_date !== void 0;
737
- const effectiveMode = !query && !hasFilters ? "fts" : resolvedMode;
736
+ if (!query && !hasFilters) {
737
+ return {
738
+ ...formatHandlerError(
739
+ new ValidationError(
740
+ "Search requires either a query string or at least one filter",
741
+ { suggestion: "Provide a search query or use get_recent_entries instead" }
742
+ )
743
+ ),
744
+ entries: [],
745
+ count: 0
746
+ };
747
+ }
748
+ const { resolvedMode, isAuto } = resolveSearchMode(mode, query);
749
+ const effectiveMode = resolvedMode;
738
750
  const searchOptions = {
739
751
  limit: input.limit,
740
752
  isPersonal: input.is_personal,
@@ -1320,10 +1332,10 @@ function getRelationshipTools(context) {
1320
1332
  const input = LinkEntriesSchema.parse(params);
1321
1333
  if (input.from_entry_id === input.to_entry_id) {
1322
1334
  return {
1323
- success: false,
1324
- error: "Cannot link an entry to itself",
1325
- code: "VALIDATION_ERROR",
1326
- category: "validation"
1335
+ ...formatHandlerError(
1336
+ new ValidationError("Cannot link an entry to itself")
1337
+ ),
1338
+ success: false
1327
1339
  };
1328
1340
  }
1329
1341
  const existingRelationships = db.getRelationships(input.from_entry_id);
@@ -1358,12 +1370,13 @@ function getRelationshipTools(context) {
1358
1370
  const isFkError = errMsg.includes("FOREIGN KEY constraint failed");
1359
1371
  if (isFkError) {
1360
1372
  return {
1361
- success: false,
1362
- error: `One or both entries not found (from: ${String(input.from_entry_id)}, to: ${String(input.to_entry_id)})`,
1363
- code: "RESOURCE_NOT_FOUND",
1364
- category: "resource",
1365
- suggestion: "Verify both entry IDs exist before linking",
1366
- recoverable: true
1373
+ ...formatHandlerError(
1374
+ new ResourceNotFoundError(
1375
+ "Entry",
1376
+ `from: ${String(input.from_entry_id)}, to: ${String(input.to_entry_id)}`
1377
+ )
1378
+ ),
1379
+ success: false
1367
1380
  };
1368
1381
  }
1369
1382
  }
@@ -2098,11 +2111,7 @@ ${e.content}
2098
2111
  tags: e.tags,
2099
2112
  significance: e.significanceType ?? void 0
2100
2113
  }));
2101
- const result = await exportEntriesToMarkdown(
2102
- exportable,
2103
- input.output_dir,
2104
- db
2105
- );
2114
+ const result = await exportEntriesToMarkdown(exportable, input.output_dir, db);
2106
2115
  await sendProgress(progress, 3, 3, "Export complete");
2107
2116
  return result;
2108
2117
  } catch (err) {
@@ -2439,6 +2448,8 @@ var GitHubIssueOutputSchema = z.object({
2439
2448
  }).extend(ErrorFieldsMixin.shape);
2440
2449
  var GitHubIssueDetailsOutputSchema = GitHubIssueOutputSchema.extend({
2441
2450
  body: z.string().nullable(),
2451
+ bodyTruncated: z.boolean().optional(),
2452
+ bodyFullLength: z.number().optional(),
2442
2453
  labels: z.array(z.string()),
2443
2454
  assignees: z.array(z.string()),
2444
2455
  createdAt: z.string(),
@@ -2459,6 +2470,14 @@ var GitHubIssuesListOutputSchema = z.object({
2459
2470
  }).extend(ErrorFieldsMixin.shape);
2460
2471
  var GitHubIssueResultOutputSchema = z.object({
2461
2472
  issue: GitHubIssueDetailsOutputSchema.optional(),
2473
+ comments: z.array(
2474
+ z.object({
2475
+ author: z.string(),
2476
+ body: z.string(),
2477
+ createdAt: z.string()
2478
+ })
2479
+ ).optional(),
2480
+ commentCount: z.number().optional(),
2462
2481
  owner: z.string().optional(),
2463
2482
  repo: z.string().optional(),
2464
2483
  detectedOwner: z.string().nullable().optional(),
@@ -2475,6 +2494,8 @@ var GitHubPullRequestOutputSchema = z.object({
2475
2494
  }).extend(ErrorFieldsMixin.shape);
2476
2495
  var GitHubPRDetailsOutputSchema = GitHubPullRequestOutputSchema.extend({
2477
2496
  body: z.string().nullable(),
2497
+ bodyTruncated: z.boolean().optional(),
2498
+ bodyFullLength: z.number().optional(),
2478
2499
  draft: z.boolean(),
2479
2500
  headBranch: z.string(),
2480
2501
  baseBranch: z.string(),
@@ -2539,7 +2560,9 @@ var StatusOptionOutputSchema = z.object({
2539
2560
  var KanbanColumnOutputSchema = z.object({
2540
2561
  status: z.string(),
2541
2562
  statusOptionId: z.string(),
2542
- items: z.array(KanbanItemOutputSchema)
2563
+ items: z.array(KanbanItemOutputSchema).optional(),
2564
+ itemCount: z.number().optional(),
2565
+ truncated: z.boolean().optional()
2543
2566
  });
2544
2567
  var KanbanBoardOutputSchema = z.object({
2545
2568
  projectId: z.string().optional(),
@@ -2549,6 +2572,14 @@ var KanbanBoardOutputSchema = z.object({
2549
2572
  statusOptions: z.array(StatusOptionOutputSchema).optional(),
2550
2573
  columns: z.array(KanbanColumnOutputSchema).optional(),
2551
2574
  totalItems: z.number().optional(),
2575
+ itemDirectory: z.array(
2576
+ z.object({
2577
+ id: z.string(),
2578
+ title: z.string(),
2579
+ status: z.string().nullable()
2580
+ })
2581
+ ).optional(),
2582
+ summaryOnly: z.boolean().optional(),
2552
2583
  owner: z.string().optional(),
2553
2584
  detectedOwner: z.string().nullable().optional(),
2554
2585
  detectedRepo: z.string().nullable().optional(),
@@ -2558,16 +2589,19 @@ var KanbanBoardOutputSchema = z.object({
2558
2589
  instruction: z.string().optional()
2559
2590
  }).extend(ErrorFieldsMixin.shape);
2560
2591
  var MoveKanbanItemOutputSchema = z.object({
2561
- success: z.boolean().optional(),
2562
2592
  itemId: z.string().optional(),
2563
2593
  newStatus: z.string().optional(),
2564
2594
  projectNumber: z.number().optional(),
2565
- message: z.string().optional(),
2566
- error: z.string().optional(),
2567
- requiresUserInput: z.boolean().optional(),
2568
- hint: z.string().optional(),
2569
2595
  availableStatuses: z.array(z.string()).optional()
2570
2596
  }).extend(ErrorFieldsMixin.shape);
2597
+ var AddKanbanItemOutputSchema = z.object({
2598
+ itemId: z.string().optional(),
2599
+ projectNumber: z.number().optional()
2600
+ }).extend(ErrorFieldsMixin.shape);
2601
+ var DeleteKanbanItemOutputSchema = z.object({
2602
+ itemId: z.string().optional(),
2603
+ projectNumber: z.number().optional()
2604
+ }).extend(ErrorFieldsMixin.shape);
2571
2605
  var CreateGitHubIssueWithEntryOutputSchema = z.object({
2572
2606
  success: z.boolean().optional(),
2573
2607
  issue: z.object({
@@ -2858,7 +2892,7 @@ function getGitHubReadTools(context) {
2858
2892
  owner: z.string().optional(),
2859
2893
  repo: z.string().optional(),
2860
2894
  state: z.enum(["open", "closed", "all"]).optional().default("open"),
2861
- limit: z.number().optional().default(20)
2895
+ limit: z.number().max(MAX_QUERY_LIMIT).optional().default(20)
2862
2896
  }).parse(params);
2863
2897
  const resolved = await resolveOwnerRepo(
2864
2898
  context,
@@ -2908,7 +2942,7 @@ function getGitHubReadTools(context) {
2908
2942
  owner: z.string().optional(),
2909
2943
  repo: z.string().optional(),
2910
2944
  state: z.enum(["open", "closed", "all"]).optional().default("open"),
2911
- limit: z.number().optional().default(20)
2945
+ limit: z.number().max(MAX_QUERY_LIMIT).optional().default(20)
2912
2946
  }).parse(params);
2913
2947
  const resolved = await resolveOwnerRepo(
2914
2948
  context,
@@ -2943,7 +2977,11 @@ function getGitHubReadTools(context) {
2943
2977
  inputSchema: z.object({
2944
2978
  issue_number: z.number(),
2945
2979
  owner: z.string().optional().describe("LEAVE EMPTY to auto-detect from git"),
2946
- repo: z.string().optional().describe("LEAVE EMPTY to auto-detect from git")
2980
+ repo: z.string().optional().describe("LEAVE EMPTY to auto-detect from git"),
2981
+ truncate_body: relaxedNumber().optional().default(800).describe(
2982
+ "Max characters for body text (0 = full body, default 800). Reduces token usage."
2983
+ ),
2984
+ include_comments: z.boolean().optional().default(false).describe("Include issue comments (default false). Each comment adds tokens.")
2947
2985
  }),
2948
2986
  outputSchema: GitHubIssueResultOutputSchema,
2949
2987
  annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: true },
@@ -2952,7 +2990,9 @@ function getGitHubReadTools(context) {
2952
2990
  const input = z.object({
2953
2991
  issue_number: z.number(),
2954
2992
  owner: z.string().optional(),
2955
- repo: z.string().optional()
2993
+ repo: z.string().optional(),
2994
+ truncate_body: z.number().optional().default(800),
2995
+ include_comments: z.boolean().optional().default(false)
2956
2996
  }).parse(params);
2957
2997
  const resolved = await resolveOwnerRepo(context, input, "is this issue from");
2958
2998
  if ("error" in resolved) return resolved.response;
@@ -2975,8 +3015,30 @@ function getGitHubReadTools(context) {
2975
3015
  detectedRepo: resolved.detectedRepo
2976
3016
  };
2977
3017
  }
3018
+ const truncateBody = input.truncate_body;
3019
+ let bodyTruncated = false;
3020
+ let bodyFullLength;
3021
+ if (truncateBody > 0 && issue.body && issue.body.length > truncateBody) {
3022
+ bodyFullLength = issue.body.length;
3023
+ const remaining = issue.body.length - truncateBody;
3024
+ issue.body = issue.body.slice(0, truncateBody) + `
3025
+ [Truncated. Re-run with truncate_body: 0 to view remaining ${String(remaining)} chars]`;
3026
+ bodyTruncated = true;
3027
+ }
3028
+ let comments;
3029
+ if (input.include_comments) {
3030
+ comments = await resolved.github.getIssueComments(
3031
+ resolved.owner,
3032
+ resolved.repo,
3033
+ input.issue_number
3034
+ );
3035
+ }
2978
3036
  return {
2979
- issue,
3037
+ issue: {
3038
+ ...issue,
3039
+ ...bodyTruncated ? { bodyTruncated: true, bodyFullLength } : {}
3040
+ },
3041
+ ...comments ? { comments, commentCount: comments.length } : {},
2980
3042
  owner: resolved.owner,
2981
3043
  repo: resolved.repo,
2982
3044
  detectedOwner: resolved.detectedOwner,
@@ -2995,7 +3057,10 @@ function getGitHubReadTools(context) {
2995
3057
  inputSchema: z.object({
2996
3058
  pr_number: z.number(),
2997
3059
  owner: z.string().optional().describe("LEAVE EMPTY to auto-detect from git"),
2998
- repo: z.string().optional().describe("LEAVE EMPTY to auto-detect from git")
3060
+ repo: z.string().optional().describe("LEAVE EMPTY to auto-detect from git"),
3061
+ truncate_body: relaxedNumber().optional().default(800).describe(
3062
+ "Max characters for body text (0 = full body, default 800). Reduces token usage."
3063
+ )
2999
3064
  }),
3000
3065
  outputSchema: GitHubPRResultOutputSchema,
3001
3066
  annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: true },
@@ -3004,7 +3069,8 @@ function getGitHubReadTools(context) {
3004
3069
  const input = z.object({
3005
3070
  pr_number: z.number(),
3006
3071
  owner: z.string().optional(),
3007
- repo: z.string().optional()
3072
+ repo: z.string().optional(),
3073
+ truncate_body: z.number().optional().default(800)
3008
3074
  }).parse(params);
3009
3075
  const resolved = await resolveOwnerRepo(context, input, "is this PR from");
3010
3076
  if ("error" in resolved) return resolved.response;
@@ -3027,8 +3093,21 @@ function getGitHubReadTools(context) {
3027
3093
  detectedRepo: resolved.detectedRepo
3028
3094
  };
3029
3095
  }
3096
+ const truncateBody = input.truncate_body;
3097
+ let bodyTruncated = false;
3098
+ let bodyFullLength;
3099
+ if (truncateBody > 0 && pullRequest.body && pullRequest.body.length > truncateBody) {
3100
+ bodyFullLength = pullRequest.body.length;
3101
+ const remaining = pullRequest.body.length - truncateBody;
3102
+ pullRequest.body = pullRequest.body.slice(0, truncateBody) + `
3103
+ [Truncated. Re-run with truncate_body: 0 to view remaining ${String(remaining)} chars]`;
3104
+ bodyTruncated = true;
3105
+ }
3030
3106
  return {
3031
- pullRequest,
3107
+ pullRequest: {
3108
+ ...pullRequest,
3109
+ ...bodyTruncated ? { bodyTruncated: true, bodyFullLength } : {}
3110
+ },
3032
3111
  owner: resolved.owner,
3033
3112
  repo: resolved.repo,
3034
3113
  detectedOwner: resolved.detectedOwner,
@@ -3096,7 +3175,13 @@ function getKanbanTools(context) {
3096
3175
  inputSchema: z.object({
3097
3176
  project_number: z.number().optional().describe("GitHub Project number (optional if repo is registered)"),
3098
3177
  owner: z.string().optional().describe("Project owner - LEAVE EMPTY to auto-detect"),
3099
- repo: z.string().optional().describe("Repository name - LEAVE EMPTY to auto-detect")
3178
+ repo: z.string().optional().describe("Repository name - LEAVE EMPTY to auto-detect"),
3179
+ summary_only: z.boolean().optional().default(false).describe(
3180
+ "Return column summaries only (name + itemCount), omitting individual items. Saves ~80% tokens."
3181
+ ),
3182
+ item_limit: relaxedNumber().optional().default(25).describe(
3183
+ "Maximum items per column (default 25, max 100). Set to 0 for summary_only behavior."
3184
+ )
3100
3185
  }),
3101
3186
  outputSchema: KanbanBoardOutputSchema,
3102
3187
  annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: true },
@@ -3105,7 +3190,9 @@ function getKanbanTools(context) {
3105
3190
  const input = z.object({
3106
3191
  project_number: z.number().optional(),
3107
3192
  owner: z.string().optional(),
3108
- repo: z.string().optional()
3193
+ repo: z.string().optional(),
3194
+ summary_only: z.boolean().optional().default(false),
3195
+ item_limit: z.number().max(100).optional().default(25)
3109
3196
  }).parse(params);
3110
3197
  const resolved = await resolveOwner(context, input.owner);
3111
3198
  if ("error" in resolved) return resolved.response;
@@ -3144,7 +3231,44 @@ function getKanbanTools(context) {
3144
3231
  recoverable: true
3145
3232
  };
3146
3233
  }
3147
- return board;
3234
+ const summaryOnly = input.summary_only || input.item_limit === 0;
3235
+ if (summaryOnly) {
3236
+ const summaryColumns = board.columns.map((col) => ({
3237
+ status: col.status,
3238
+ statusOptionId: col.statusOptionId,
3239
+ items: [],
3240
+ itemCount: col.items.length
3241
+ }));
3242
+ const itemDirectory = board.columns.flatMap(
3243
+ (col) => col.items.map((item) => ({
3244
+ id: item.id,
3245
+ title: item.title,
3246
+ status: item.status
3247
+ }))
3248
+ );
3249
+ return {
3250
+ ...board,
3251
+ columns: summaryColumns,
3252
+ summaryOnly: true,
3253
+ itemDirectory
3254
+ };
3255
+ }
3256
+ const itemLimit = input.item_limit;
3257
+ const truncatedColumns = board.columns.map((col) => {
3258
+ if (col.items.length <= itemLimit) {
3259
+ return { ...col, itemCount: col.items.length };
3260
+ }
3261
+ return {
3262
+ ...col,
3263
+ items: col.items.slice(0, itemLimit),
3264
+ itemCount: col.items.length,
3265
+ truncated: true
3266
+ };
3267
+ });
3268
+ return {
3269
+ ...board,
3270
+ columns: truncatedColumns
3271
+ };
3148
3272
  } catch (err) {
3149
3273
  return formatHandlerError(err);
3150
3274
  }
@@ -3237,6 +3361,155 @@ function getKanbanTools(context) {
3237
3361
  return formatHandlerError(err);
3238
3362
  }
3239
3363
  }
3364
+ },
3365
+ {
3366
+ name: "add_kanban_item",
3367
+ title: "Add Kanban Item",
3368
+ description: "Add an existing GitHub Issue or Pull Request to a Kanban board (GitHub Project v2). Returns the project item ID on success.",
3369
+ group: "github",
3370
+ inputSchema: z.object({
3371
+ project_number: z.number().optional().describe("GitHub Project number (optional if repo is registered)"),
3372
+ issue_number: z.number().describe("The number of the issue or PR to add"),
3373
+ owner: z.string().optional().describe("Repository owner - LEAVE EMPTY to auto-detect"),
3374
+ repo: z.string().optional().describe("Repository name - LEAVE EMPTY to auto-detect")
3375
+ }),
3376
+ outputSchema: AddKanbanItemOutputSchema,
3377
+ annotations: { readOnlyHint: false, idempotentHint: true, openWorldHint: true },
3378
+ handler: async (params) => {
3379
+ try {
3380
+ const input = z.object({
3381
+ project_number: z.number().optional(),
3382
+ issue_number: z.number(),
3383
+ owner: z.string().optional(),
3384
+ repo: z.string().optional()
3385
+ }).parse(params);
3386
+ const resolved = await resolveOwnerRepo(context, input);
3387
+ if ("error" in resolved) return resolved.response;
3388
+ const projectNum = resolveProjectNumber(
3389
+ context,
3390
+ resolved.repo,
3391
+ input.project_number
3392
+ );
3393
+ if (projectNum === void 0) {
3394
+ return {
3395
+ success: false,
3396
+ error: "project_number is required and could not be resolved from registry. Please supply it explicitly.",
3397
+ code: "VALIDATION_ERROR",
3398
+ category: "validation",
3399
+ recoverable: true
3400
+ };
3401
+ }
3402
+ const issueDetails = await resolved.github.getIssue(
3403
+ resolved.owner,
3404
+ resolved.repo,
3405
+ input.issue_number
3406
+ );
3407
+ if (!issueDetails?.nodeId) {
3408
+ return {
3409
+ success: false,
3410
+ error: `Issue or PR #${String(input.issue_number)} not found or lacks a nodeId`,
3411
+ code: "RESOURCE_NOT_FOUND",
3412
+ category: "resource",
3413
+ recoverable: true
3414
+ };
3415
+ }
3416
+ const board = await resolved.github.getProjectKanban(
3417
+ resolved.owner,
3418
+ projectNum,
3419
+ resolved.repo
3420
+ );
3421
+ if (!board) {
3422
+ return {
3423
+ success: false,
3424
+ error: `Project #${String(projectNum)} not found`,
3425
+ code: "RESOURCE_NOT_FOUND",
3426
+ category: "resource",
3427
+ recoverable: true
3428
+ };
3429
+ }
3430
+ const result = await resolved.github.addProjectItem(
3431
+ board.projectId,
3432
+ issueDetails.nodeId
3433
+ );
3434
+ return {
3435
+ success: result.success,
3436
+ itemId: result.itemId,
3437
+ projectNumber: projectNum,
3438
+ message: result.success ? `Added Issue/PR #${String(input.issue_number)} to Project #${projectNum}` : void 0,
3439
+ error: result.error
3440
+ };
3441
+ } catch (err) {
3442
+ return formatHandlerError(err);
3443
+ }
3444
+ }
3445
+ },
3446
+ {
3447
+ name: "delete_kanban_item",
3448
+ title: "Delete Kanban Item",
3449
+ description: "Remove an item from a GitHub Kanban board cleanly. This untethers it from the project but does NOT close or delete the underlying Issue/PR.",
3450
+ group: "github",
3451
+ inputSchema: z.object({
3452
+ project_number: z.number().optional().describe("GitHub Project number (optional if repo is registered)"),
3453
+ item_id: z.string().describe("The project item ID to remove (from get_kanban_board)"),
3454
+ owner: z.string().optional().describe("Project owner - LEAVE EMPTY to auto-detect"),
3455
+ repo: z.string().optional().describe("Repository name - LEAVE EMPTY to auto-detect")
3456
+ }),
3457
+ outputSchema: DeleteKanbanItemOutputSchema,
3458
+ annotations: { readOnlyHint: false, idempotentHint: false, openWorldHint: true },
3459
+ handler: async (params) => {
3460
+ try {
3461
+ const input = z.object({
3462
+ project_number: z.number().optional(),
3463
+ item_id: z.string(),
3464
+ owner: z.string().optional(),
3465
+ repo: z.string().optional()
3466
+ }).parse(params);
3467
+ const resolved = await resolveOwner(context, input.owner);
3468
+ if ("error" in resolved) return resolved.response;
3469
+ const effectiveRepo = input.repo ?? resolved.repo;
3470
+ const projectNum = resolveProjectNumber(
3471
+ context,
3472
+ effectiveRepo,
3473
+ input.project_number
3474
+ );
3475
+ if (projectNum === void 0) {
3476
+ return {
3477
+ success: false,
3478
+ error: "project_number is required and could not be resolved from registry. Please supply it explicitly.",
3479
+ code: "VALIDATION_ERROR",
3480
+ category: "validation",
3481
+ recoverable: true
3482
+ };
3483
+ }
3484
+ const board = await resolved.github.getProjectKanban(
3485
+ resolved.owner,
3486
+ projectNum,
3487
+ effectiveRepo
3488
+ );
3489
+ if (!board) {
3490
+ return {
3491
+ success: false,
3492
+ error: `Project #${String(projectNum)} not found`,
3493
+ code: "RESOURCE_NOT_FOUND",
3494
+ category: "resource",
3495
+ recoverable: true
3496
+ };
3497
+ }
3498
+ const result = await resolved.github.deleteProjectItem(
3499
+ board.projectId,
3500
+ input.item_id
3501
+ );
3502
+ return {
3503
+ success: result.success,
3504
+ itemId: input.item_id,
3505
+ projectNumber: projectNum,
3506
+ message: result.success ? `Deleted item ${input.item_id} from Project #${projectNum}` : void 0,
3507
+ error: result.error
3508
+ };
3509
+ } catch (err) {
3510
+ return formatHandlerError(err);
3511
+ }
3512
+ }
3240
3513
  }
3241
3514
  ];
3242
3515
  }
@@ -3294,7 +3567,11 @@ function getGitHubIssueTools(context) {
3294
3567
  if (!issue) {
3295
3568
  return {
3296
3569
  success: false,
3297
- error: "Failed to create GitHub issue. Check GITHUB_TOKEN permissions."
3570
+ error: "Failed to create GitHub issue. Check GITHUB_TOKEN permissions.",
3571
+ code: "API_ERROR",
3572
+ category: "api",
3573
+ suggestion: "Verify GitHub token has write access to issues.",
3574
+ recoverable: true
3298
3575
  };
3299
3576
  }
3300
3577
  const projectNumber = resolveProjectNumber(
@@ -3453,13 +3730,21 @@ Description: ${input.body.slice(0, 200)}${input.body.length > 200 ? "..." : ""}`
3453
3730
  if (!issueDetails) {
3454
3731
  return {
3455
3732
  success: false,
3456
- error: `Issue #${String(input.issue_number)} not found`
3733
+ error: `Issue #${String(input.issue_number)} not found`,
3734
+ code: "RESOURCE_NOT_FOUND",
3735
+ category: "resource",
3736
+ suggestion: "Verify the issue number exists in this repository.",
3737
+ recoverable: true
3457
3738
  };
3458
3739
  }
3459
3740
  if (issueDetails.state === "CLOSED") {
3460
3741
  return {
3461
- success: false,
3462
- error: `Issue #${String(input.issue_number)} is already closed`
3742
+ ...formatHandlerError(
3743
+ new ValidationError(
3744
+ `Issue #${String(input.issue_number)} is already closed`
3745
+ )
3746
+ ),
3747
+ success: false
3463
3748
  };
3464
3749
  }
3465
3750
  const result = await resolved.github.closeIssue(
@@ -3471,7 +3756,11 @@ Description: ${input.body.slice(0, 200)}${input.body.length > 200 ? "..." : ""}`
3471
3756
  if (!result) {
3472
3757
  return {
3473
3758
  success: false,
3474
- error: "Failed to close GitHub issue. Check GITHUB_TOKEN permissions."
3759
+ error: "Failed to close GitHub issue. Check GITHUB_TOKEN permissions.",
3760
+ code: "API_ERROR",
3761
+ category: "api",
3762
+ suggestion: "Verify GitHub token has write access to issues.",
3763
+ recoverable: true
3475
3764
  };
3476
3765
  }
3477
3766
  let kanbanResult;
@@ -3630,10 +3919,12 @@ function isResourceError(result) {
3630
3919
  }
3631
3920
  var DEFAULT_BRIEFING_CONFIG = {
3632
3921
  entryCount: 3,
3922
+ summaryCount: 1,
3633
3923
  includeTeam: false,
3634
3924
  issueCount: 0,
3635
3925
  prCount: 0,
3636
3926
  prStatusBreakdown: false,
3927
+ milestoneCount: 3,
3637
3928
  workflowCount: 0,
3638
3929
  workflowStatusBreakdown: false,
3639
3930
  copilotReviews: false
@@ -4373,7 +4664,7 @@ var TeamCreateEntrySchemaMcp = z.object({
4373
4664
  author: z.string().optional()
4374
4665
  });
4375
4666
  var TeamGetRecentSchema = z.object({
4376
- limit: z.number().max(500).optional().default(10)
4667
+ limit: z.number().min(1).max(500).optional().default(10)
4377
4668
  });
4378
4669
  var TeamGetRecentSchemaMcp = z.object({
4379
4670
  limit: relaxedNumber().optional().default(10)
@@ -6021,6 +6312,8 @@ var METHOD_ALIASES = {
6021
6312
  context: "getGithubContext",
6022
6313
  kanban: "getKanbanBoard",
6023
6314
  moveKanban: "moveKanbanItem",
6315
+ addKanban: "addKanbanItem",
6316
+ deleteKanban: "deleteKanbanItem",
6024
6317
  milestones: "getGithubMilestones",
6025
6318
  milestone: "getGithubMilestone",
6026
6319
  createMilestone: "createGithubMilestone",
@@ -6095,6 +6388,7 @@ var GROUP_EXAMPLES = {
6095
6388
  'mj.github.getGithubIssues({ state: "open" })',
6096
6389
  'mj.github.getGithubPrs({ state: "open" })',
6097
6390
  "mj.github.getKanbanBoard({ project_number: 1 })",
6391
+ "mj.github.addKanbanItem({ project_number: 1, issue_number: 123 })",
6098
6392
  "mj.github.getGithubMilestones()",
6099
6393
  "mj.github.getRepoInsights()"
6100
6394
  ],
@@ -6143,7 +6437,9 @@ var POSITIONAL_PARAM_MAP = {
6143
6437
  getGithubIssue: "issue_number",
6144
6438
  getGithubPr: "pr_number",
6145
6439
  getKanbanBoard: "project_number",
6146
- moveKanbanItem: ["item_id", "status"],
6440
+ moveKanbanItem: ["item_id", "target_status"],
6441
+ addKanbanItem: "issue_number",
6442
+ deleteKanbanItem: "item_id",
6147
6443
  getGithubMilestone: "milestone_number",
6148
6444
  createGithubMilestone: "title",
6149
6445
  updateGithubMilestone: "milestone_number",
@@ -6377,8 +6673,8 @@ var DEFAULT_SECURITY_CONFIG = {
6377
6673
  maxCodeLength: 50 * 1024,
6378
6674
  // 50KB
6379
6675
  maxExecutionsPerMinute: 60,
6380
- maxResultSize: 10 * 1024 * 1024,
6381
- // 10MB
6676
+ maxResultSize: 100 * 1024,
6677
+ // 100KB — context window protection (configurable via CODE_MODE_MAX_RESULT_SIZE)
6382
6678
  blockedPatterns: [
6383
6679
  /\brequire\s*\(/,
6384
6680
  // No require()
@@ -6493,9 +6789,12 @@ var CodeModeSecurityManager = class {
6493
6789
  const errors = [];
6494
6790
  try {
6495
6791
  const serialized = JSON.stringify(result);
6496
- if (Buffer.byteLength(serialized, "utf-8") > this.config.maxResultSize) {
6792
+ const actualBytes = Buffer.byteLength(serialized, "utf-8");
6793
+ if (actualBytes > this.config.maxResultSize) {
6794
+ const actualKb = Math.ceil(actualBytes / 1024);
6795
+ const limitKb = Math.ceil(this.config.maxResultSize / 1024);
6497
6796
  errors.push(
6498
- `Result exceeds maximum size of ${String(this.config.maxResultSize)} bytes`
6797
+ `Result exceeds maximum size of ${String(limitKb)} KB (${String(actualKb)} KB returned). Extract specific fields or aggregate data before returning. Example: instead of \`return await mj.github.getKanbanBoard(5)\`, use \`const b = await mj.github.getKanbanBoard(5); return { columns: b.columns?.length ?? 0, totalItems: b.totalItems }\``
6499
6798
  );
6500
6799
  }
6501
6800
  } catch (error) {
@@ -6871,7 +7170,12 @@ var ExecuteCodeSchemaMcp = z.object({
6871
7170
  var securityManager = null;
6872
7171
  var sandboxPool = null;
6873
7172
  function getSecurityManager() {
6874
- securityManager ??= new CodeModeSecurityManager();
7173
+ if (!securityManager) {
7174
+ const envMaxSize = process.env["CODE_MODE_MAX_RESULT_SIZE"];
7175
+ const parsedMaxSize = envMaxSize && /^\d+$/.test(envMaxSize) ? parseInt(envMaxSize, 10) : void 0;
7176
+ const overrides = parsedMaxSize !== void 0 && Number.isFinite(parsedMaxSize) && Number.isInteger(parsedMaxSize) && parsedMaxSize > 0 ? { maxResultSize: parsedMaxSize } : void 0;
7177
+ securityManager = new CodeModeSecurityManager(overrides);
7178
+ }
6875
7179
  return securityManager;
6876
7180
  }
6877
7181
  function getSandboxPool() {
@@ -7343,6 +7647,8 @@ var TOOL_GROUPS = {
7343
7647
  "get_github_context",
7344
7648
  "get_kanban_board",
7345
7649
  "move_kanban_item",
7650
+ "add_kanban_item",
7651
+ "delete_kanban_item",
7346
7652
  "create_github_issue_with_entry",
7347
7653
  "close_github_issue_with_entry",
7348
7654
  "get_github_milestones",