memory-journal-mcp 7.0.1 → 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.
- package/README.md +75 -66
- package/dist/{chunk-6J4RPJ4I.js → chunk-GR4T3SRW.js} +146 -105
- package/dist/{chunk-ARLH46WS.js → chunk-IWKLHSPU.js} +89 -3
- package/dist/{chunk-2BJHLTYP.js → chunk-ORV7ZZOE.js} +1086 -86
- package/dist/cli.js +30 -4
- package/dist/github-integration-2TFMXHIJ.js +1 -0
- package/dist/index.d.ts +6 -2
- package/dist/index.js +3 -3
- package/dist/{tools-FFFGXIKN.js → tools-CXR2FEB2.js} +2 -2
- package/package.json +2 -2
- package/skills/README.md +77 -0
- package/skills/autonomous-dev/SKILL.md +56 -0
- package/skills/bin/sync.js +50 -0
- package/skills/bun/SKILL.md +156 -0
- package/skills/github-commander/SKILL.md +1 -1
- package/skills/github-commander/workflows/code-quality-audit.md +7 -5
- package/skills/github-commander/workflows/issue-triage.md +13 -4
- package/skills/github-commander/workflows/milestone-sprint.md +9 -1
- package/skills/github-commander/workflows/perf-audit.md +2 -0
- package/skills/github-commander/workflows/pr-review.md +9 -3
- package/skills/github-commander/workflows/roadmap-kickoff.md +79 -0
- package/skills/github-commander/workflows/security-audit.md +3 -3
- package/skills/github-commander/workflows/update-deps.md +2 -2
- package/skills/gitlab/SKILL.md +115 -0
- package/skills/gitlab/package-lock.json +392 -0
- package/skills/gitlab/package.json +14 -0
- package/skills/gitlab/scripts/gitlab-client.ts +125 -0
- package/skills/gitlab/scripts/gitlab-helper.ts +80 -0
- package/skills/golang/SKILL.md +54 -0
- package/skills/mysql/SKILL.md +30 -0
- package/skills/package.json +48 -0
- package/skills/playwright-standard/SKILL.md +58 -0
- package/skills/playwright-standard/examples/fixtures.ts +66 -0
- package/skills/playwright-standard/examples/type-stubs.d.ts +10 -0
- package/skills/playwright-standard/references/advanced-scenarios.md +59 -0
- package/skills/playwright-standard/references/infrastructure.md +43 -0
- package/skills/postgres/SKILL.md +33 -0
- package/skills/react-best-practices/AGENTS.md +2883 -0
- package/skills/react-best-practices/README.md +127 -0
- package/skills/react-best-practices/SKILL.md +138 -0
- package/skills/react-best-practices/metadata.json +17 -0
- package/skills/react-best-practices/rules/_sections.md +46 -0
- package/skills/react-best-practices/rules/_template.md +28 -0
- package/skills/react-best-practices/rules/advanced-event-handler-refs.md +55 -0
- package/skills/react-best-practices/rules/advanced-init-once.md +42 -0
- package/skills/react-best-practices/rules/advanced-use-latest.md +39 -0
- package/skills/react-best-practices/rules/async-api-routes.md +35 -0
- package/skills/react-best-practices/rules/async-defer-await.md +80 -0
- package/skills/react-best-practices/rules/async-dependencies.md +48 -0
- package/skills/react-best-practices/rules/async-parallel.md +24 -0
- package/skills/react-best-practices/rules/async-suspense-boundaries.md +99 -0
- package/skills/react-best-practices/rules/bundle-barrel-imports.md +59 -0
- package/skills/react-best-practices/rules/bundle-conditional.md +37 -0
- package/skills/react-best-practices/rules/bundle-defer-third-party.md +48 -0
- package/skills/react-best-practices/rules/bundle-dynamic-imports.md +34 -0
- package/skills/react-best-practices/rules/bundle-preload.md +44 -0
- package/skills/react-best-practices/rules/client-event-listeners.md +78 -0
- package/skills/react-best-practices/rules/client-localstorage-schema.md +74 -0
- package/skills/react-best-practices/rules/client-passive-event-listeners.md +48 -0
- package/skills/react-best-practices/rules/client-swr-dedup.md +56 -0
- package/skills/react-best-practices/rules/js-batch-dom-css.md +110 -0
- package/skills/react-best-practices/rules/js-cache-function-results.md +80 -0
- package/skills/react-best-practices/rules/js-cache-property-access.md +28 -0
- package/skills/react-best-practices/rules/js-cache-storage.md +68 -0
- package/skills/react-best-practices/rules/js-combine-iterations.md +32 -0
- package/skills/react-best-practices/rules/js-early-exit.md +50 -0
- package/skills/react-best-practices/rules/js-hoist-regexp.md +45 -0
- package/skills/react-best-practices/rules/js-index-maps.md +37 -0
- package/skills/react-best-practices/rules/js-length-check-first.md +50 -0
- package/skills/react-best-practices/rules/js-min-max-loop.md +82 -0
- package/skills/react-best-practices/rules/js-set-map-lookups.md +24 -0
- package/skills/react-best-practices/rules/js-tosorted-immutable.md +57 -0
- package/skills/react-best-practices/rules/rendering-activity.md +24 -0
- package/skills/react-best-practices/rules/rendering-animate-svg-wrapper.md +38 -0
- package/skills/react-best-practices/rules/rendering-conditional-render.md +32 -0
- package/skills/react-best-practices/rules/rendering-content-visibility.md +38 -0
- package/skills/react-best-practices/rules/rendering-hoist-jsx.md +36 -0
- package/skills/react-best-practices/rules/rendering-hydration-no-flicker.md +72 -0
- package/skills/react-best-practices/rules/rendering-hydration-suppress-warning.md +26 -0
- package/skills/react-best-practices/rules/rendering-svg-precision.md +28 -0
- package/skills/react-best-practices/rules/rendering-usetransition-loading.md +75 -0
- package/skills/react-best-practices/rules/rerender-defer-reads.md +39 -0
- package/skills/react-best-practices/rules/rerender-dependencies.md +45 -0
- package/skills/react-best-practices/rules/rerender-derived-state-no-effect.md +40 -0
- package/skills/react-best-practices/rules/rerender-derived-state.md +29 -0
- package/skills/react-best-practices/rules/rerender-functional-setstate.md +77 -0
- package/skills/react-best-practices/rules/rerender-lazy-state-init.md +56 -0
- package/skills/react-best-practices/rules/rerender-memo-with-default-value.md +36 -0
- package/skills/react-best-practices/rules/rerender-memo.md +44 -0
- package/skills/react-best-practices/rules/rerender-move-effect-to-event.md +45 -0
- package/skills/react-best-practices/rules/rerender-simple-expression-in-memo.md +35 -0
- package/skills/react-best-practices/rules/rerender-transitions.md +40 -0
- package/skills/react-best-practices/rules/rerender-use-ref-transient-values.md +73 -0
- package/skills/react-best-practices/rules/server-after-nonblocking.md +73 -0
- package/skills/react-best-practices/rules/server-auth-actions.md +96 -0
- package/skills/react-best-practices/rules/server-cache-lru.md +41 -0
- package/skills/react-best-practices/rules/server-cache-react.md +76 -0
- package/skills/react-best-practices/rules/server-dedup-props.md +65 -0
- package/skills/react-best-practices/rules/server-parallel-fetching.md +83 -0
- package/skills/react-best-practices/rules/server-serialization.md +38 -0
- package/skills/rust/SKILL.md +86 -0
- package/skills/shadcn-ui/SKILL.md +72 -0
- package/skills/skill-builder/SKILL.md +457 -0
- package/skills/skill-builder/checklist.md +65 -0
- package/skills/sqlite/SKILL.md +38 -0
- package/skills/typescript/SKILL.md +453 -0
- package/skills/typescript/assets/eslint-template.js +102 -0
- package/skills/typescript/assets/tsconfig-template.json +45 -0
- package/skills/typescript/references/enterprise-patterns.md +531 -0
- package/skills/typescript/references/generics.md +493 -0
- package/skills/typescript/references/nestjs-integration.md +579 -0
- package/skills/typescript/references/react-integration.md +616 -0
- package/skills/typescript/references/toolchain.md +547 -0
- package/skills/typescript/references/type-system.md +481 -0
- package/skills/vitest-standard/SKILL.md +82 -0
- package/skills/vitest-standard/examples/service-mock.ts +60 -0
- package/skills/vitest-standard/examples/tdd-calculator.ts +41 -0
- package/skills/vitest-standard/examples/type-stubs.d.ts +18 -0
- package/skills/vitest-standard/references/async-and-errors.md +58 -0
- package/skills/vitest-standard/references/coverage-and-config.md +53 -0
- package/skills/vitest-standard/references/mocking.md +61 -0
- package/skills/vitest-standard/references/tdd-patterns.md +60 -0
- package/dist/github-integration-PDRLXKGM.js +0 -1
- package/skills/github-commander/workflows/full-audit.md +0 -134
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
import { transformAutoReturn } from './chunk-OKOVZ5QE.js';
|
|
2
|
-
import { GitHubIntegration, resolveAuthor, logger, MemoryJournalMcpError, matchSuggestion, ConfigurationError } from './chunk-
|
|
2
|
+
import { GitHubIntegration, resolveAuthor, logger, ValidationError, ResourceNotFoundError, assertSafeDirectoryPath, MemoryJournalMcpError, matchSuggestion, ConfigurationError } from './chunk-IWKLHSPU.js';
|
|
3
3
|
import { z, ZodError } from 'zod';
|
|
4
|
+
import { open, stat, mkdir, rename, appendFile, readdir, readFile } from 'fs/promises';
|
|
5
|
+
import { tmpdir } from 'os';
|
|
6
|
+
import * as path from 'path';
|
|
7
|
+
import { dirname, resolve, sep, join } from 'path';
|
|
4
8
|
import * as vm from 'vm';
|
|
5
9
|
import { MessageChannel, Worker } from 'worker_threads';
|
|
6
10
|
import * as crypto2 from 'crypto';
|
|
7
11
|
import { fileURLToPath } from 'url';
|
|
8
|
-
import * as path from 'path';
|
|
9
|
-
import { dirname } from 'path';
|
|
10
12
|
import { performance as performance$1 } from 'perf_hooks';
|
|
11
|
-
import { open, stat, mkdir, rename, appendFile } from 'fs/promises';
|
|
12
13
|
|
|
13
14
|
function formatZodError(error) {
|
|
14
15
|
return error.issues.map((issue) => {
|
|
@@ -58,7 +59,7 @@ async function resolveIssueUrl(context, projectNumber, issueNumber, existingUrl)
|
|
|
58
59
|
([_, v]) => v.project_number === projectNumber
|
|
59
60
|
);
|
|
60
61
|
if (entry) {
|
|
61
|
-
const { GitHubIntegration: GitHubIntegration2 } = await import('./github-integration-
|
|
62
|
+
const { GitHubIntegration: GitHubIntegration2 } = await import('./github-integration-2TFMXHIJ.js');
|
|
62
63
|
const targetGithub = new GitHubIntegration2(entry[1].path);
|
|
63
64
|
const repoInfo = await targetGithub.getRepoInfo();
|
|
64
65
|
if (repoInfo.owner && repoInfo.repo) {
|
|
@@ -113,6 +114,7 @@ var SIGNIFICANCE_TYPES = [
|
|
|
113
114
|
"release"
|
|
114
115
|
];
|
|
115
116
|
var MAX_CONTENT_LENGTH = 5e4;
|
|
117
|
+
var MAX_QUERY_LIMIT = 500;
|
|
116
118
|
var DATE_MIN_SENTINEL = "1970-01-01";
|
|
117
119
|
var DATE_MAX_SENTINEL = "2999-12-31";
|
|
118
120
|
var DATE_FORMAT_REGEX = /^\d{4}-\d{2}-\d{2}$/;
|
|
@@ -214,7 +216,7 @@ var GetEntryByIdSchemaMcp = z.object({
|
|
|
214
216
|
include_relationships: z.boolean().optional().default(true)
|
|
215
217
|
});
|
|
216
218
|
var GetRecentEntriesSchema = z.object({
|
|
217
|
-
limit: z.number().max(
|
|
219
|
+
limit: z.number().min(1).max(MAX_QUERY_LIMIT).optional().default(5),
|
|
218
220
|
is_personal: z.boolean().optional()
|
|
219
221
|
});
|
|
220
222
|
var GetRecentEntriesSchemaMcp = z.object({
|
|
@@ -451,7 +453,6 @@ function getCoreTools(context) {
|
|
|
451
453
|
}
|
|
452
454
|
|
|
453
455
|
// src/handlers/tools/search/helpers.ts
|
|
454
|
-
var MAX_QUERY_LIMIT = 500;
|
|
455
456
|
var DEDUP_KEY_LENGTH = 200;
|
|
456
457
|
function calcPerDbLimit(limit, hasTeamDb) {
|
|
457
458
|
return hasTeamDb ? Math.min(limit * 2, MAX_QUERY_LIMIT) : limit;
|
|
@@ -729,11 +730,23 @@ function getSearchTools(context) {
|
|
|
729
730
|
handler: async (params) => {
|
|
730
731
|
try {
|
|
731
732
|
const input = SearchEntriesSchema.parse(params);
|
|
732
|
-
const query = input.query || "";
|
|
733
|
+
const query = input.query?.trim() || "";
|
|
733
734
|
const mode = input.mode;
|
|
734
|
-
const { resolvedMode, isAuto } = resolveSearchMode(mode, query);
|
|
735
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;
|
|
736
|
-
|
|
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;
|
|
737
750
|
const searchOptions = {
|
|
738
751
|
limit: input.limit,
|
|
739
752
|
isPersonal: input.is_personal,
|
|
@@ -1261,12 +1274,23 @@ var LinkEntriesSchemaMcp = z.object({
|
|
|
1261
1274
|
var VisualizeInputSchema = z.object({
|
|
1262
1275
|
entry_id: z.number().optional().describe("Specific entry ID to visualize (shows connected entries)"),
|
|
1263
1276
|
tags: z.array(z.string()).optional().describe("Filter entries by tags"),
|
|
1277
|
+
relationship_type: z.enum([
|
|
1278
|
+
"evolves_from",
|
|
1279
|
+
"references",
|
|
1280
|
+
"implements",
|
|
1281
|
+
"clarifies",
|
|
1282
|
+
"response_to",
|
|
1283
|
+
"blocked_by",
|
|
1284
|
+
"resolved",
|
|
1285
|
+
"caused"
|
|
1286
|
+
]).optional().describe("Filter to show only this relationship type"),
|
|
1264
1287
|
depth: z.number().min(1).max(3).optional().default(2).describe("Relationship traversal depth"),
|
|
1265
1288
|
limit: z.number().max(500).optional().default(20).describe("Maximum entries to include")
|
|
1266
1289
|
});
|
|
1267
1290
|
var VisualizeInputSchemaMcp = z.object({
|
|
1268
1291
|
entry_id: relaxedNumber().optional().describe("Specific entry ID to visualize (shows connected entries)"),
|
|
1269
1292
|
tags: z.array(z.string()).optional().describe("Filter entries by tags"),
|
|
1293
|
+
relationship_type: z.string().optional().describe("Filter to show only this relationship type (e.g., blocked_by, implements)"),
|
|
1270
1294
|
depth: relaxedNumber().optional().default(2).describe("Relationship traversal depth"),
|
|
1271
1295
|
limit: relaxedNumber().optional().default(20).describe("Maximum entries to include")
|
|
1272
1296
|
});
|
|
@@ -1308,10 +1332,10 @@ function getRelationshipTools(context) {
|
|
|
1308
1332
|
const input = LinkEntriesSchema.parse(params);
|
|
1309
1333
|
if (input.from_entry_id === input.to_entry_id) {
|
|
1310
1334
|
return {
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1335
|
+
...formatHandlerError(
|
|
1336
|
+
new ValidationError("Cannot link an entry to itself")
|
|
1337
|
+
),
|
|
1338
|
+
success: false
|
|
1315
1339
|
};
|
|
1316
1340
|
}
|
|
1317
1341
|
const existingRelationships = db.getRelationships(input.from_entry_id);
|
|
@@ -1346,12 +1370,13 @@ function getRelationshipTools(context) {
|
|
|
1346
1370
|
const isFkError = errMsg.includes("FOREIGN KEY constraint failed");
|
|
1347
1371
|
if (isFkError) {
|
|
1348
1372
|
return {
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
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
|
|
1355
1380
|
};
|
|
1356
1381
|
}
|
|
1357
1382
|
}
|
|
@@ -1463,15 +1488,18 @@ function getRelationshipTools(context) {
|
|
|
1463
1488
|
}
|
|
1464
1489
|
const entryIds = Object.keys(entries).map(Number);
|
|
1465
1490
|
const placeholders = entryIds.map(() => "?").join(",");
|
|
1466
|
-
|
|
1467
|
-
`
|
|
1491
|
+
let relsQuery = `
|
|
1468
1492
|
SELECT from_entry_id, to_entry_id, relationship_type
|
|
1469
1493
|
FROM relationships
|
|
1470
1494
|
WHERE from_entry_id IN (${placeholders})
|
|
1471
1495
|
AND to_entry_id IN (${placeholders})
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
)
|
|
1496
|
+
`;
|
|
1497
|
+
const relsParams = [...entryIds, ...entryIds];
|
|
1498
|
+
if (input.relationship_type) {
|
|
1499
|
+
relsQuery += " AND relationship_type = ?";
|
|
1500
|
+
relsParams.push(input.relationship_type);
|
|
1501
|
+
}
|
|
1502
|
+
const relsResult = db.executeRawQuery(relsQuery, relsParams);
|
|
1475
1503
|
const relationships = relsResult[0]?.values ?? [];
|
|
1476
1504
|
const MERMAID_CONTENT_PREVIEW_LENGTH = 40;
|
|
1477
1505
|
let mermaid = "```mermaid\\ngraph TD\\n";
|
|
@@ -1554,8 +1582,345 @@ async function sendProgress(ctx, progress, total, message) {
|
|
|
1554
1582
|
} catch {
|
|
1555
1583
|
}
|
|
1556
1584
|
}
|
|
1585
|
+
var FrontmatterSchema = z.object({
|
|
1586
|
+
mj_id: z.number().int().positive().optional(),
|
|
1587
|
+
entry_type: z.string().optional(),
|
|
1588
|
+
author: z.string().optional(),
|
|
1589
|
+
tags: z.array(z.string()).optional(),
|
|
1590
|
+
timestamp: z.string().optional(),
|
|
1591
|
+
significance: z.string().optional(),
|
|
1592
|
+
relationships: z.array(
|
|
1593
|
+
z.object({
|
|
1594
|
+
type: z.string(),
|
|
1595
|
+
target_id: z.number().int().positive()
|
|
1596
|
+
})
|
|
1597
|
+
).optional(),
|
|
1598
|
+
source: z.string().optional()
|
|
1599
|
+
});
|
|
1600
|
+
function serializeFrontmatter(data) {
|
|
1601
|
+
if (data === null || data === void 0) return "";
|
|
1602
|
+
const lines = ["---"];
|
|
1603
|
+
if (data.mj_id !== void 0) lines.push(`mj_id: ${String(data.mj_id)}`);
|
|
1604
|
+
if (data.entry_type !== void 0) lines.push(`entry_type: ${data.entry_type}`);
|
|
1605
|
+
if (data.author !== void 0) lines.push(`author: ${data.author}`);
|
|
1606
|
+
if (data.timestamp !== void 0) lines.push(`timestamp: ${data.timestamp}`);
|
|
1607
|
+
if (data.significance !== void 0) lines.push(`significance: ${data.significance}`);
|
|
1608
|
+
if (data.source !== void 0) lines.push(`source: ${data.source}`);
|
|
1609
|
+
if (data.tags && data.tags.length > 0) {
|
|
1610
|
+
lines.push("tags:");
|
|
1611
|
+
for (const tag of data.tags) {
|
|
1612
|
+
lines.push(` - ${tag}`);
|
|
1613
|
+
}
|
|
1614
|
+
}
|
|
1615
|
+
if (data.relationships !== void 0 && data.relationships.length > 0) {
|
|
1616
|
+
lines.push("relationships:");
|
|
1617
|
+
for (const rel of data.relationships) {
|
|
1618
|
+
lines.push(` - type: ${rel.type}`);
|
|
1619
|
+
lines.push(` target_id: ${String(rel.target_id)}`);
|
|
1620
|
+
}
|
|
1621
|
+
}
|
|
1622
|
+
if (lines.length === 1) {
|
|
1623
|
+
return "";
|
|
1624
|
+
}
|
|
1625
|
+
lines.push("---");
|
|
1626
|
+
return lines.join("\n") + "\n";
|
|
1627
|
+
}
|
|
1628
|
+
function parseFrontmatter(content) {
|
|
1629
|
+
const lines = content.split("\n");
|
|
1630
|
+
if (lines[0]?.trim() !== "---") {
|
|
1631
|
+
return { metadata: {}, body: content };
|
|
1632
|
+
}
|
|
1633
|
+
let closingIndex = -1;
|
|
1634
|
+
for (let i = 1; i < lines.length; i++) {
|
|
1635
|
+
if (lines[i]?.trim() === "---") {
|
|
1636
|
+
closingIndex = i;
|
|
1637
|
+
break;
|
|
1638
|
+
}
|
|
1639
|
+
}
|
|
1640
|
+
if (closingIndex === -1) {
|
|
1641
|
+
return { metadata: {}, body: content };
|
|
1642
|
+
}
|
|
1643
|
+
const fmLines = lines.slice(1, closingIndex);
|
|
1644
|
+
const raw = {};
|
|
1645
|
+
let currentKey = null;
|
|
1646
|
+
let currentArray = null;
|
|
1647
|
+
let isRelationshipArray = false;
|
|
1648
|
+
let currentRelObj = null;
|
|
1649
|
+
for (const line of fmLines) {
|
|
1650
|
+
if (line.trim() === "") continue;
|
|
1651
|
+
if (/^\s{2,}- /.test(line)) {
|
|
1652
|
+
const itemContent = line.replace(/^\s{2,}- /, "");
|
|
1653
|
+
if (isRelationshipArray && currentKey === "relationships") {
|
|
1654
|
+
const colonIdx2 = itemContent.indexOf(":");
|
|
1655
|
+
if (colonIdx2 !== -1) {
|
|
1656
|
+
if (currentRelObj !== null) {
|
|
1657
|
+
currentArray?.push(currentRelObj);
|
|
1658
|
+
}
|
|
1659
|
+
currentRelObj = {};
|
|
1660
|
+
const key = itemContent.slice(0, colonIdx2).trim();
|
|
1661
|
+
const value = itemContent.slice(colonIdx2 + 1).trim();
|
|
1662
|
+
currentRelObj[key] = parseScalar(value);
|
|
1663
|
+
}
|
|
1664
|
+
} else if (currentArray !== null) {
|
|
1665
|
+
currentArray.push(itemContent.trim());
|
|
1666
|
+
}
|
|
1667
|
+
continue;
|
|
1668
|
+
}
|
|
1669
|
+
if (/^\s{4,}\w/.test(line) && currentRelObj !== null) {
|
|
1670
|
+
const colonIdx2 = line.indexOf(":");
|
|
1671
|
+
if (colonIdx2 !== -1) {
|
|
1672
|
+
const key = line.slice(0, colonIdx2).trim();
|
|
1673
|
+
const value = line.slice(colonIdx2 + 1).trim();
|
|
1674
|
+
currentRelObj[key] = parseScalar(value);
|
|
1675
|
+
}
|
|
1676
|
+
continue;
|
|
1677
|
+
}
|
|
1678
|
+
if (currentKey !== null && currentArray !== null) {
|
|
1679
|
+
if (isRelationshipArray && currentRelObj !== null) {
|
|
1680
|
+
currentArray.push(currentRelObj);
|
|
1681
|
+
currentRelObj = null;
|
|
1682
|
+
}
|
|
1683
|
+
raw[currentKey] = currentArray;
|
|
1684
|
+
currentArray = null;
|
|
1685
|
+
currentKey = null;
|
|
1686
|
+
isRelationshipArray = false;
|
|
1687
|
+
}
|
|
1688
|
+
const colonIdx = line.indexOf(":");
|
|
1689
|
+
if (colonIdx !== -1) {
|
|
1690
|
+
const key = line.slice(0, colonIdx).trim();
|
|
1691
|
+
const value = line.slice(colonIdx + 1).trim();
|
|
1692
|
+
if (value === "") {
|
|
1693
|
+
currentKey = key;
|
|
1694
|
+
currentArray = [];
|
|
1695
|
+
isRelationshipArray = key === "relationships";
|
|
1696
|
+
} else {
|
|
1697
|
+
raw[key] = parseScalar(value);
|
|
1698
|
+
}
|
|
1699
|
+
}
|
|
1700
|
+
}
|
|
1701
|
+
if (currentKey !== null && currentArray !== null) {
|
|
1702
|
+
if (isRelationshipArray && currentRelObj !== null) {
|
|
1703
|
+
currentArray.push(currentRelObj);
|
|
1704
|
+
}
|
|
1705
|
+
raw[currentKey] = currentArray;
|
|
1706
|
+
}
|
|
1707
|
+
const parseResult = FrontmatterSchema.safeParse(raw);
|
|
1708
|
+
if (!parseResult.success) {
|
|
1709
|
+
const issues = parseResult.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`);
|
|
1710
|
+
throw new Error(`Invalid frontmatter: ${issues.join("; ")}`);
|
|
1711
|
+
}
|
|
1712
|
+
const body = lines.slice(closingIndex + 1).join("\n").replace(/^\n/, "");
|
|
1713
|
+
return { metadata: parseResult.data, body };
|
|
1714
|
+
}
|
|
1715
|
+
function parseScalar(value) {
|
|
1716
|
+
if (/^-?\d+$/.test(value)) return parseInt(value, 10);
|
|
1717
|
+
if (/^-?\d+\.\d+$/.test(value)) return parseFloat(value);
|
|
1718
|
+
if (value === "true") return true;
|
|
1719
|
+
if (value === "false") return false;
|
|
1720
|
+
if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
|
|
1721
|
+
return value.slice(1, -1);
|
|
1722
|
+
}
|
|
1723
|
+
return value;
|
|
1724
|
+
}
|
|
1725
|
+
function generateSlug(content) {
|
|
1726
|
+
const slug = content.slice(0, 50).toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").replace(/-{2,}/g, "-");
|
|
1727
|
+
return slug || "untitled";
|
|
1728
|
+
}
|
|
1729
|
+
function generateFilename(id, content) {
|
|
1730
|
+
return `${String(id)}-${generateSlug(content)}.md`;
|
|
1731
|
+
}
|
|
1732
|
+
async function exportEntriesToMarkdown(entries, outputDir, db) {
|
|
1733
|
+
const resolvedOutputDir = resolve(outputDir);
|
|
1734
|
+
const resolvedTmpDir = resolve(tmpdir());
|
|
1735
|
+
const tmpPrefix = resolvedTmpDir.endsWith(sep) ? resolvedTmpDir : resolvedTmpDir + sep;
|
|
1736
|
+
if (resolvedOutputDir === resolvedTmpDir || resolvedOutputDir.startsWith(tmpPrefix)) {
|
|
1737
|
+
throw new Error("Refusing to export markdown files into the OS temporary directory");
|
|
1738
|
+
}
|
|
1739
|
+
await mkdir(resolvedOutputDir, { recursive: true });
|
|
1740
|
+
const files = [];
|
|
1741
|
+
let skipped = 0;
|
|
1742
|
+
for (const entry of entries) {
|
|
1743
|
+
if (!entry.content.trim()) {
|
|
1744
|
+
skipped++;
|
|
1745
|
+
continue;
|
|
1746
|
+
}
|
|
1747
|
+
const fmData = {
|
|
1748
|
+
mj_id: entry.id,
|
|
1749
|
+
entry_type: entry.entryType,
|
|
1750
|
+
timestamp: entry.timestamp,
|
|
1751
|
+
source: "memory-journal-mcp"
|
|
1752
|
+
};
|
|
1753
|
+
if (entry.tags !== void 0 && entry.tags.length > 0) {
|
|
1754
|
+
fmData.tags = entry.tags;
|
|
1755
|
+
}
|
|
1756
|
+
if (entry.significance !== void 0) {
|
|
1757
|
+
fmData.significance = entry.significance;
|
|
1758
|
+
}
|
|
1759
|
+
if (entry.author !== void 0) {
|
|
1760
|
+
fmData.author = entry.author;
|
|
1761
|
+
}
|
|
1762
|
+
try {
|
|
1763
|
+
const relationships = db.getRelationships(entry.id);
|
|
1764
|
+
if (relationships.length > 0) {
|
|
1765
|
+
fmData.relationships = relationships.map((r) => ({
|
|
1766
|
+
type: r.relationshipType,
|
|
1767
|
+
target_id: r.fromEntryId === entry.id ? r.toEntryId : r.fromEntryId
|
|
1768
|
+
}));
|
|
1769
|
+
}
|
|
1770
|
+
} catch {
|
|
1771
|
+
}
|
|
1772
|
+
const frontmatter = serializeFrontmatter(fmData);
|
|
1773
|
+
const fileContent = frontmatter + "\n" + entry.content + "\n";
|
|
1774
|
+
const filename = generateFilename(entry.id, entry.content);
|
|
1775
|
+
const filepath = join(resolvedOutputDir, filename);
|
|
1776
|
+
const handle = await open(filepath, "w", 384);
|
|
1777
|
+
try {
|
|
1778
|
+
await handle.writeFile(fileContent, "utf-8");
|
|
1779
|
+
} finally {
|
|
1780
|
+
await handle.close();
|
|
1781
|
+
}
|
|
1782
|
+
files.push(filename);
|
|
1783
|
+
}
|
|
1784
|
+
return {
|
|
1785
|
+
success: true,
|
|
1786
|
+
exported_count: files.length,
|
|
1787
|
+
output_dir: resolvedOutputDir,
|
|
1788
|
+
files,
|
|
1789
|
+
skipped
|
|
1790
|
+
};
|
|
1791
|
+
}
|
|
1792
|
+
var VALID_RELATIONSHIP_TYPES = /* @__PURE__ */ new Set([
|
|
1793
|
+
"evolves_from",
|
|
1794
|
+
"references",
|
|
1795
|
+
"implements",
|
|
1796
|
+
"clarifies",
|
|
1797
|
+
"response_to",
|
|
1798
|
+
"blocked_by",
|
|
1799
|
+
"resolved",
|
|
1800
|
+
"caused"
|
|
1801
|
+
]);
|
|
1802
|
+
var VALID_ENTRY_TYPES = /* @__PURE__ */ new Set([
|
|
1803
|
+
"personal_reflection",
|
|
1804
|
+
"project_decision",
|
|
1805
|
+
"technical_achievement",
|
|
1806
|
+
"bug_fix",
|
|
1807
|
+
"feature_implementation",
|
|
1808
|
+
"code_review",
|
|
1809
|
+
"meeting_notes",
|
|
1810
|
+
"learning",
|
|
1811
|
+
"research",
|
|
1812
|
+
"planning",
|
|
1813
|
+
"retrospective",
|
|
1814
|
+
"standup",
|
|
1815
|
+
"technical_note",
|
|
1816
|
+
"development_note",
|
|
1817
|
+
"enhancement",
|
|
1818
|
+
"milestone",
|
|
1819
|
+
"system_integration_test",
|
|
1820
|
+
"test_entry",
|
|
1821
|
+
"other"
|
|
1822
|
+
]);
|
|
1823
|
+
function toEntryType(value) {
|
|
1824
|
+
if (value === void 0) return void 0;
|
|
1825
|
+
return VALID_ENTRY_TYPES.has(value) ? value : void 0;
|
|
1826
|
+
}
|
|
1827
|
+
async function importMarkdownEntries(sourceDir, db, options = {}, vectorManager) {
|
|
1828
|
+
const { dry_run = false, limit = 100 } = options;
|
|
1829
|
+
const allFiles = await readdir(sourceDir);
|
|
1830
|
+
const mdFiles = allFiles.filter((f) => f.endsWith(".md")).slice(0, limit);
|
|
1831
|
+
const result = {
|
|
1832
|
+
success: true,
|
|
1833
|
+
created: 0,
|
|
1834
|
+
updated: 0,
|
|
1835
|
+
skipped: 0,
|
|
1836
|
+
errors: [],
|
|
1837
|
+
dry_run
|
|
1838
|
+
};
|
|
1839
|
+
for (const filename of mdFiles) {
|
|
1840
|
+
try {
|
|
1841
|
+
const filepath = join(sourceDir, filename);
|
|
1842
|
+
const content = await readFile(filepath, "utf-8");
|
|
1843
|
+
const { metadata, body } = parseFrontmatter(content);
|
|
1844
|
+
if (!body.trim()) {
|
|
1845
|
+
result.skipped++;
|
|
1846
|
+
continue;
|
|
1847
|
+
}
|
|
1848
|
+
if (dry_run) {
|
|
1849
|
+
if (metadata.mj_id !== void 0) {
|
|
1850
|
+
const existing = db.getEntryById(metadata.mj_id);
|
|
1851
|
+
if (existing) {
|
|
1852
|
+
result.updated++;
|
|
1853
|
+
} else {
|
|
1854
|
+
result.created++;
|
|
1855
|
+
}
|
|
1856
|
+
} else {
|
|
1857
|
+
result.created++;
|
|
1858
|
+
}
|
|
1859
|
+
continue;
|
|
1860
|
+
}
|
|
1861
|
+
let entryId;
|
|
1862
|
+
const entryType = toEntryType(metadata.entry_type) ?? "personal_reflection";
|
|
1863
|
+
if (metadata.mj_id !== void 0) {
|
|
1864
|
+
const existing = db.getEntryById(metadata.mj_id);
|
|
1865
|
+
if (existing) {
|
|
1866
|
+
db.updateEntry(metadata.mj_id, {
|
|
1867
|
+
content: body,
|
|
1868
|
+
entryType: toEntryType(metadata.entry_type),
|
|
1869
|
+
tags: metadata.tags
|
|
1870
|
+
});
|
|
1871
|
+
entryId = metadata.mj_id;
|
|
1872
|
+
result.updated++;
|
|
1873
|
+
} else {
|
|
1874
|
+
const newEntry = db.createEntry({
|
|
1875
|
+
content: body,
|
|
1876
|
+
entryType,
|
|
1877
|
+
tags: metadata.tags,
|
|
1878
|
+
...metadata.significance !== void 0 && {
|
|
1879
|
+
significanceType: metadata.significance
|
|
1880
|
+
}
|
|
1881
|
+
});
|
|
1882
|
+
entryId = newEntry.id;
|
|
1883
|
+
result.created++;
|
|
1884
|
+
}
|
|
1885
|
+
} else {
|
|
1886
|
+
const newEntry = db.createEntry({
|
|
1887
|
+
content: body,
|
|
1888
|
+
entryType,
|
|
1889
|
+
tags: metadata.tags,
|
|
1890
|
+
...metadata.significance !== void 0 && {
|
|
1891
|
+
significanceType: metadata.significance
|
|
1892
|
+
}
|
|
1893
|
+
});
|
|
1894
|
+
entryId = newEntry.id;
|
|
1895
|
+
result.created++;
|
|
1896
|
+
}
|
|
1897
|
+
if (metadata.relationships !== void 0) {
|
|
1898
|
+
for (const rel of metadata.relationships) {
|
|
1899
|
+
if (!VALID_RELATIONSHIP_TYPES.has(rel.type)) continue;
|
|
1900
|
+
try {
|
|
1901
|
+
const targetExists = db.getEntryById(rel.target_id);
|
|
1902
|
+
if (targetExists) {
|
|
1903
|
+
db.linkEntries(entryId, rel.target_id, rel.type);
|
|
1904
|
+
}
|
|
1905
|
+
} catch {
|
|
1906
|
+
}
|
|
1907
|
+
}
|
|
1908
|
+
}
|
|
1909
|
+
if (vectorManager) {
|
|
1910
|
+
void vectorManager.addEntry(entryId, body).catch(() => {
|
|
1911
|
+
});
|
|
1912
|
+
}
|
|
1913
|
+
} catch (err) {
|
|
1914
|
+
result.errors.push({
|
|
1915
|
+
file: filename,
|
|
1916
|
+
error: err instanceof Error ? err.message : String(err)
|
|
1917
|
+
});
|
|
1918
|
+
}
|
|
1919
|
+
}
|
|
1920
|
+
return result;
|
|
1921
|
+
}
|
|
1557
1922
|
|
|
1558
|
-
// src/handlers/tools/
|
|
1923
|
+
// src/handlers/tools/io.ts
|
|
1559
1924
|
var ExportEntriesSchema = z.object({
|
|
1560
1925
|
format: z.enum(["json", "markdown"]).optional().default("json"),
|
|
1561
1926
|
start_date: z.string().regex(DATE_FORMAT_REGEX, DATE_FORMAT_MESSAGE).optional(),
|
|
@@ -1580,14 +1945,60 @@ var ExportEntriesOutputSchema = z.object({
|
|
|
1580
1945
|
success: z.boolean().optional(),
|
|
1581
1946
|
error: z.string().optional()
|
|
1582
1947
|
}).extend(ErrorFieldsMixin.shape);
|
|
1583
|
-
|
|
1948
|
+
var ExportMarkdownSchema = z.object({
|
|
1949
|
+
output_dir: z.string().min(1),
|
|
1950
|
+
start_date: z.string().regex(DATE_FORMAT_REGEX, DATE_FORMAT_MESSAGE).optional(),
|
|
1951
|
+
end_date: z.string().regex(DATE_FORMAT_REGEX, DATE_FORMAT_MESSAGE).optional(),
|
|
1952
|
+
entry_types: z.array(z.enum(ENTRY_TYPES)).optional(),
|
|
1953
|
+
tags: z.array(z.string()).optional(),
|
|
1954
|
+
limit: z.number().max(500).optional().default(100)
|
|
1955
|
+
});
|
|
1956
|
+
var ExportMarkdownSchemaMcp = z.object({
|
|
1957
|
+
output_dir: z.string().describe("Target directory for .md files"),
|
|
1958
|
+
start_date: z.string().optional().describe("Start date filter (YYYY-MM-DD)"),
|
|
1959
|
+
end_date: z.string().optional().describe("End date filter (YYYY-MM-DD)"),
|
|
1960
|
+
entry_types: z.array(z.string()).optional().describe("Filter by entry types"),
|
|
1961
|
+
tags: z.array(z.string()).optional().describe("Filter by tags"),
|
|
1962
|
+
limit: relaxedNumber().optional().default(100).describe("Max entries to export (default: 100, max: 500)")
|
|
1963
|
+
});
|
|
1964
|
+
var ExportMarkdownOutputSchema = z.object({
|
|
1965
|
+
success: z.boolean().optional(),
|
|
1966
|
+
exported_count: z.number().optional(),
|
|
1967
|
+
output_dir: z.string().optional(),
|
|
1968
|
+
files: z.array(z.string()).optional(),
|
|
1969
|
+
skipped: z.number().optional()
|
|
1970
|
+
}).extend(ErrorFieldsMixin.shape);
|
|
1971
|
+
var ImportMarkdownSchema = z.object({
|
|
1972
|
+
source_dir: z.string().min(1),
|
|
1973
|
+
dry_run: z.boolean().optional().default(false),
|
|
1974
|
+
limit: z.number().max(500).optional().default(100)
|
|
1975
|
+
});
|
|
1976
|
+
var ImportMarkdownSchemaMcp = z.object({
|
|
1977
|
+
source_dir: z.string().describe("Directory containing .md files to import"),
|
|
1978
|
+
dry_run: z.boolean().optional().default(false).describe("Parse and validate without writing to database"),
|
|
1979
|
+
limit: relaxedNumber().optional().default(100).describe("Max files to process (default: 100, max: 500)")
|
|
1980
|
+
});
|
|
1981
|
+
var ImportMarkdownOutputSchema = z.object({
|
|
1982
|
+
success: z.boolean().optional(),
|
|
1983
|
+
created: z.number().optional(),
|
|
1984
|
+
updated: z.number().optional(),
|
|
1985
|
+
skipped: z.number().optional(),
|
|
1986
|
+
errors: z.array(
|
|
1987
|
+
z.object({
|
|
1988
|
+
file: z.string(),
|
|
1989
|
+
error: z.string()
|
|
1990
|
+
})
|
|
1991
|
+
).optional(),
|
|
1992
|
+
dry_run: z.boolean().optional()
|
|
1993
|
+
}).extend(ErrorFieldsMixin.shape);
|
|
1994
|
+
function getIoTools(context) {
|
|
1584
1995
|
const { db, progress } = context;
|
|
1585
1996
|
return [
|
|
1586
1997
|
{
|
|
1587
1998
|
name: "export_entries",
|
|
1588
1999
|
title: "Export Entries",
|
|
1589
2000
|
description: "Export journal entries to JSON or Markdown format",
|
|
1590
|
-
group: "
|
|
2001
|
+
group: "io",
|
|
1591
2002
|
inputSchema: ExportEntriesSchemaMcp,
|
|
1592
2003
|
outputSchema: ExportEntriesOutputSchema,
|
|
1593
2004
|
annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: false },
|
|
@@ -1643,6 +2054,107 @@ ${e.content}
|
|
|
1643
2054
|
return formatHandlerError(err);
|
|
1644
2055
|
}
|
|
1645
2056
|
}
|
|
2057
|
+
},
|
|
2058
|
+
// ==================================================================
|
|
2059
|
+
// export_markdown
|
|
2060
|
+
// ==================================================================
|
|
2061
|
+
{
|
|
2062
|
+
name: "export_markdown",
|
|
2063
|
+
title: "Export to Markdown Files",
|
|
2064
|
+
description: "Export journal entries as individual frontmattered Markdown files (.md). Each file contains YAML frontmatter (mj_id, entry_type, tags, relationships) and the entry content. Files can be edited externally and re-imported via import_markdown.",
|
|
2065
|
+
group: "io",
|
|
2066
|
+
inputSchema: ExportMarkdownSchemaMcp,
|
|
2067
|
+
outputSchema: ExportMarkdownOutputSchema,
|
|
2068
|
+
annotations: {
|
|
2069
|
+
readOnlyHint: false,
|
|
2070
|
+
destructiveHint: false,
|
|
2071
|
+
idempotentHint: true,
|
|
2072
|
+
openWorldHint: true
|
|
2073
|
+
},
|
|
2074
|
+
handler: async (params) => {
|
|
2075
|
+
try {
|
|
2076
|
+
const input = ExportMarkdownSchema.parse(params);
|
|
2077
|
+
assertSafeDirectoryPath(input.output_dir);
|
|
2078
|
+
await sendProgress(progress, 0, 3, "Fetching entries...");
|
|
2079
|
+
const limit = input.limit ?? 100;
|
|
2080
|
+
let entries;
|
|
2081
|
+
if (input.start_date || input.end_date) {
|
|
2082
|
+
const startDate = input.start_date ?? DATE_MIN_SENTINEL;
|
|
2083
|
+
const endDate = input.end_date ?? DATE_MAX_SENTINEL;
|
|
2084
|
+
entries = db.searchByDateRange(startDate, endDate, {
|
|
2085
|
+
tags: input.tags,
|
|
2086
|
+
limit
|
|
2087
|
+
});
|
|
2088
|
+
} else if (input.tags && input.tags.length > 0) {
|
|
2089
|
+
entries = db.searchByDateRange(DATE_MIN_SENTINEL, DATE_MAX_SENTINEL, {
|
|
2090
|
+
tags: input.tags,
|
|
2091
|
+
limit
|
|
2092
|
+
});
|
|
2093
|
+
} else {
|
|
2094
|
+
entries = db.getRecentEntries(limit);
|
|
2095
|
+
}
|
|
2096
|
+
if (input.entry_types && input.entry_types.length > 0) {
|
|
2097
|
+
const allowedTypes = new Set(input.entry_types);
|
|
2098
|
+
entries = entries.filter((e) => allowedTypes.has(e.entryType)).slice(0, limit);
|
|
2099
|
+
}
|
|
2100
|
+
await sendProgress(
|
|
2101
|
+
progress,
|
|
2102
|
+
1,
|
|
2103
|
+
3,
|
|
2104
|
+
`Exporting ${String(entries.length)} entries...`
|
|
2105
|
+
);
|
|
2106
|
+
const exportable = entries.map((e) => ({
|
|
2107
|
+
id: e.id,
|
|
2108
|
+
content: e.content,
|
|
2109
|
+
entryType: e.entryType,
|
|
2110
|
+
timestamp: e.timestamp,
|
|
2111
|
+
tags: e.tags,
|
|
2112
|
+
significance: e.significanceType ?? void 0
|
|
2113
|
+
}));
|
|
2114
|
+
const result = await exportEntriesToMarkdown(exportable, input.output_dir, db);
|
|
2115
|
+
await sendProgress(progress, 3, 3, "Export complete");
|
|
2116
|
+
return result;
|
|
2117
|
+
} catch (err) {
|
|
2118
|
+
return formatHandlerError(err);
|
|
2119
|
+
}
|
|
2120
|
+
}
|
|
2121
|
+
},
|
|
2122
|
+
// ==================================================================
|
|
2123
|
+
// import_markdown
|
|
2124
|
+
// ==================================================================
|
|
2125
|
+
{
|
|
2126
|
+
name: "import_markdown",
|
|
2127
|
+
title: "Import from Markdown Files",
|
|
2128
|
+
description: "Import frontmattered Markdown files (.md) into the journal. If a file has an mj_id in its frontmatter and the entry exists, it is updated. Otherwise, a new entry is created. Tags and relationships are reconciled automatically. Use dry_run: true to preview what would happen without writing.",
|
|
2129
|
+
group: "io",
|
|
2130
|
+
inputSchema: ImportMarkdownSchemaMcp,
|
|
2131
|
+
outputSchema: ImportMarkdownOutputSchema,
|
|
2132
|
+
annotations: {
|
|
2133
|
+
readOnlyHint: false,
|
|
2134
|
+
destructiveHint: false,
|
|
2135
|
+
idempotentHint: false,
|
|
2136
|
+
openWorldHint: true
|
|
2137
|
+
},
|
|
2138
|
+
handler: async (params) => {
|
|
2139
|
+
try {
|
|
2140
|
+
const input = ImportMarkdownSchema.parse(params);
|
|
2141
|
+
assertSafeDirectoryPath(input.source_dir);
|
|
2142
|
+
await sendProgress(progress, 0, 2, "Reading markdown files...");
|
|
2143
|
+
const result = await importMarkdownEntries(
|
|
2144
|
+
input.source_dir,
|
|
2145
|
+
db,
|
|
2146
|
+
{
|
|
2147
|
+
dry_run: input.dry_run,
|
|
2148
|
+
limit: input.limit
|
|
2149
|
+
},
|
|
2150
|
+
context.vectorManager
|
|
2151
|
+
);
|
|
2152
|
+
await sendProgress(progress, 2, 2, "Import complete");
|
|
2153
|
+
return result;
|
|
2154
|
+
} catch (err) {
|
|
2155
|
+
return formatHandlerError(err);
|
|
2156
|
+
}
|
|
2157
|
+
}
|
|
1646
2158
|
}
|
|
1647
2159
|
];
|
|
1648
2160
|
}
|
|
@@ -1936,6 +2448,8 @@ var GitHubIssueOutputSchema = z.object({
|
|
|
1936
2448
|
}).extend(ErrorFieldsMixin.shape);
|
|
1937
2449
|
var GitHubIssueDetailsOutputSchema = GitHubIssueOutputSchema.extend({
|
|
1938
2450
|
body: z.string().nullable(),
|
|
2451
|
+
bodyTruncated: z.boolean().optional(),
|
|
2452
|
+
bodyFullLength: z.number().optional(),
|
|
1939
2453
|
labels: z.array(z.string()),
|
|
1940
2454
|
assignees: z.array(z.string()),
|
|
1941
2455
|
createdAt: z.string(),
|
|
@@ -1956,6 +2470,14 @@ var GitHubIssuesListOutputSchema = z.object({
|
|
|
1956
2470
|
}).extend(ErrorFieldsMixin.shape);
|
|
1957
2471
|
var GitHubIssueResultOutputSchema = z.object({
|
|
1958
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(),
|
|
1959
2481
|
owner: z.string().optional(),
|
|
1960
2482
|
repo: z.string().optional(),
|
|
1961
2483
|
detectedOwner: z.string().nullable().optional(),
|
|
@@ -1972,6 +2494,8 @@ var GitHubPullRequestOutputSchema = z.object({
|
|
|
1972
2494
|
}).extend(ErrorFieldsMixin.shape);
|
|
1973
2495
|
var GitHubPRDetailsOutputSchema = GitHubPullRequestOutputSchema.extend({
|
|
1974
2496
|
body: z.string().nullable(),
|
|
2497
|
+
bodyTruncated: z.boolean().optional(),
|
|
2498
|
+
bodyFullLength: z.number().optional(),
|
|
1975
2499
|
draft: z.boolean(),
|
|
1976
2500
|
headBranch: z.string(),
|
|
1977
2501
|
baseBranch: z.string(),
|
|
@@ -2036,7 +2560,9 @@ var StatusOptionOutputSchema = z.object({
|
|
|
2036
2560
|
var KanbanColumnOutputSchema = z.object({
|
|
2037
2561
|
status: z.string(),
|
|
2038
2562
|
statusOptionId: z.string(),
|
|
2039
|
-
items: z.array(KanbanItemOutputSchema)
|
|
2563
|
+
items: z.array(KanbanItemOutputSchema).optional(),
|
|
2564
|
+
itemCount: z.number().optional(),
|
|
2565
|
+
truncated: z.boolean().optional()
|
|
2040
2566
|
});
|
|
2041
2567
|
var KanbanBoardOutputSchema = z.object({
|
|
2042
2568
|
projectId: z.string().optional(),
|
|
@@ -2046,6 +2572,14 @@ var KanbanBoardOutputSchema = z.object({
|
|
|
2046
2572
|
statusOptions: z.array(StatusOptionOutputSchema).optional(),
|
|
2047
2573
|
columns: z.array(KanbanColumnOutputSchema).optional(),
|
|
2048
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(),
|
|
2049
2583
|
owner: z.string().optional(),
|
|
2050
2584
|
detectedOwner: z.string().nullable().optional(),
|
|
2051
2585
|
detectedRepo: z.string().nullable().optional(),
|
|
@@ -2055,16 +2589,19 @@ var KanbanBoardOutputSchema = z.object({
|
|
|
2055
2589
|
instruction: z.string().optional()
|
|
2056
2590
|
}).extend(ErrorFieldsMixin.shape);
|
|
2057
2591
|
var MoveKanbanItemOutputSchema = z.object({
|
|
2058
|
-
success: z.boolean().optional(),
|
|
2059
2592
|
itemId: z.string().optional(),
|
|
2060
2593
|
newStatus: z.string().optional(),
|
|
2061
2594
|
projectNumber: z.number().optional(),
|
|
2062
|
-
message: z.string().optional(),
|
|
2063
|
-
error: z.string().optional(),
|
|
2064
|
-
requiresUserInput: z.boolean().optional(),
|
|
2065
|
-
hint: z.string().optional(),
|
|
2066
2595
|
availableStatuses: z.array(z.string()).optional()
|
|
2067
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);
|
|
2068
2605
|
var CreateGitHubIssueWithEntryOutputSchema = z.object({
|
|
2069
2606
|
success: z.boolean().optional(),
|
|
2070
2607
|
issue: z.object({
|
|
@@ -2355,7 +2892,7 @@ function getGitHubReadTools(context) {
|
|
|
2355
2892
|
owner: z.string().optional(),
|
|
2356
2893
|
repo: z.string().optional(),
|
|
2357
2894
|
state: z.enum(["open", "closed", "all"]).optional().default("open"),
|
|
2358
|
-
limit: z.number().optional().default(20)
|
|
2895
|
+
limit: z.number().max(MAX_QUERY_LIMIT).optional().default(20)
|
|
2359
2896
|
}).parse(params);
|
|
2360
2897
|
const resolved = await resolveOwnerRepo(
|
|
2361
2898
|
context,
|
|
@@ -2405,7 +2942,7 @@ function getGitHubReadTools(context) {
|
|
|
2405
2942
|
owner: z.string().optional(),
|
|
2406
2943
|
repo: z.string().optional(),
|
|
2407
2944
|
state: z.enum(["open", "closed", "all"]).optional().default("open"),
|
|
2408
|
-
limit: z.number().optional().default(20)
|
|
2945
|
+
limit: z.number().max(MAX_QUERY_LIMIT).optional().default(20)
|
|
2409
2946
|
}).parse(params);
|
|
2410
2947
|
const resolved = await resolveOwnerRepo(
|
|
2411
2948
|
context,
|
|
@@ -2440,7 +2977,11 @@ function getGitHubReadTools(context) {
|
|
|
2440
2977
|
inputSchema: z.object({
|
|
2441
2978
|
issue_number: z.number(),
|
|
2442
2979
|
owner: z.string().optional().describe("LEAVE EMPTY to auto-detect from git"),
|
|
2443
|
-
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.")
|
|
2444
2985
|
}),
|
|
2445
2986
|
outputSchema: GitHubIssueResultOutputSchema,
|
|
2446
2987
|
annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: true },
|
|
@@ -2449,7 +2990,9 @@ function getGitHubReadTools(context) {
|
|
|
2449
2990
|
const input = z.object({
|
|
2450
2991
|
issue_number: z.number(),
|
|
2451
2992
|
owner: z.string().optional(),
|
|
2452
|
-
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)
|
|
2453
2996
|
}).parse(params);
|
|
2454
2997
|
const resolved = await resolveOwnerRepo(context, input, "is this issue from");
|
|
2455
2998
|
if ("error" in resolved) return resolved.response;
|
|
@@ -2472,8 +3015,30 @@ function getGitHubReadTools(context) {
|
|
|
2472
3015
|
detectedRepo: resolved.detectedRepo
|
|
2473
3016
|
};
|
|
2474
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
|
+
}
|
|
2475
3036
|
return {
|
|
2476
|
-
issue
|
|
3037
|
+
issue: {
|
|
3038
|
+
...issue,
|
|
3039
|
+
...bodyTruncated ? { bodyTruncated: true, bodyFullLength } : {}
|
|
3040
|
+
},
|
|
3041
|
+
...comments ? { comments, commentCount: comments.length } : {},
|
|
2477
3042
|
owner: resolved.owner,
|
|
2478
3043
|
repo: resolved.repo,
|
|
2479
3044
|
detectedOwner: resolved.detectedOwner,
|
|
@@ -2492,7 +3057,10 @@ function getGitHubReadTools(context) {
|
|
|
2492
3057
|
inputSchema: z.object({
|
|
2493
3058
|
pr_number: z.number(),
|
|
2494
3059
|
owner: z.string().optional().describe("LEAVE EMPTY to auto-detect from git"),
|
|
2495
|
-
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
|
+
)
|
|
2496
3064
|
}),
|
|
2497
3065
|
outputSchema: GitHubPRResultOutputSchema,
|
|
2498
3066
|
annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: true },
|
|
@@ -2501,7 +3069,8 @@ function getGitHubReadTools(context) {
|
|
|
2501
3069
|
const input = z.object({
|
|
2502
3070
|
pr_number: z.number(),
|
|
2503
3071
|
owner: z.string().optional(),
|
|
2504
|
-
repo: z.string().optional()
|
|
3072
|
+
repo: z.string().optional(),
|
|
3073
|
+
truncate_body: z.number().optional().default(800)
|
|
2505
3074
|
}).parse(params);
|
|
2506
3075
|
const resolved = await resolveOwnerRepo(context, input, "is this PR from");
|
|
2507
3076
|
if ("error" in resolved) return resolved.response;
|
|
@@ -2524,8 +3093,21 @@ function getGitHubReadTools(context) {
|
|
|
2524
3093
|
detectedRepo: resolved.detectedRepo
|
|
2525
3094
|
};
|
|
2526
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
|
+
}
|
|
2527
3106
|
return {
|
|
2528
|
-
pullRequest
|
|
3107
|
+
pullRequest: {
|
|
3108
|
+
...pullRequest,
|
|
3109
|
+
...bodyTruncated ? { bodyTruncated: true, bodyFullLength } : {}
|
|
3110
|
+
},
|
|
2529
3111
|
owner: resolved.owner,
|
|
2530
3112
|
repo: resolved.repo,
|
|
2531
3113
|
detectedOwner: resolved.detectedOwner,
|
|
@@ -2593,7 +3175,13 @@ function getKanbanTools(context) {
|
|
|
2593
3175
|
inputSchema: z.object({
|
|
2594
3176
|
project_number: z.number().optional().describe("GitHub Project number (optional if repo is registered)"),
|
|
2595
3177
|
owner: z.string().optional().describe("Project owner - LEAVE EMPTY to auto-detect"),
|
|
2596
|
-
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
|
+
)
|
|
2597
3185
|
}),
|
|
2598
3186
|
outputSchema: KanbanBoardOutputSchema,
|
|
2599
3187
|
annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: true },
|
|
@@ -2602,7 +3190,9 @@ function getKanbanTools(context) {
|
|
|
2602
3190
|
const input = z.object({
|
|
2603
3191
|
project_number: z.number().optional(),
|
|
2604
3192
|
owner: z.string().optional(),
|
|
2605
|
-
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)
|
|
2606
3196
|
}).parse(params);
|
|
2607
3197
|
const resolved = await resolveOwner(context, input.owner);
|
|
2608
3198
|
if ("error" in resolved) return resolved.response;
|
|
@@ -2641,7 +3231,44 @@ function getKanbanTools(context) {
|
|
|
2641
3231
|
recoverable: true
|
|
2642
3232
|
};
|
|
2643
3233
|
}
|
|
2644
|
-
|
|
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
|
+
};
|
|
2645
3272
|
} catch (err) {
|
|
2646
3273
|
return formatHandlerError(err);
|
|
2647
3274
|
}
|
|
@@ -2734,6 +3361,155 @@ function getKanbanTools(context) {
|
|
|
2734
3361
|
return formatHandlerError(err);
|
|
2735
3362
|
}
|
|
2736
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
|
+
}
|
|
2737
3513
|
}
|
|
2738
3514
|
];
|
|
2739
3515
|
}
|
|
@@ -2791,7 +3567,11 @@ function getGitHubIssueTools(context) {
|
|
|
2791
3567
|
if (!issue) {
|
|
2792
3568
|
return {
|
|
2793
3569
|
success: false,
|
|
2794
|
-
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
|
|
2795
3575
|
};
|
|
2796
3576
|
}
|
|
2797
3577
|
const projectNumber = resolveProjectNumber(
|
|
@@ -2950,13 +3730,21 @@ Description: ${input.body.slice(0, 200)}${input.body.length > 200 ? "..." : ""}`
|
|
|
2950
3730
|
if (!issueDetails) {
|
|
2951
3731
|
return {
|
|
2952
3732
|
success: false,
|
|
2953
|
-
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
|
|
2954
3738
|
};
|
|
2955
3739
|
}
|
|
2956
3740
|
if (issueDetails.state === "CLOSED") {
|
|
2957
3741
|
return {
|
|
2958
|
-
|
|
2959
|
-
|
|
3742
|
+
...formatHandlerError(
|
|
3743
|
+
new ValidationError(
|
|
3744
|
+
`Issue #${String(input.issue_number)} is already closed`
|
|
3745
|
+
)
|
|
3746
|
+
),
|
|
3747
|
+
success: false
|
|
2960
3748
|
};
|
|
2961
3749
|
}
|
|
2962
3750
|
const result = await resolved.github.closeIssue(
|
|
@@ -2968,7 +3756,11 @@ Description: ${input.body.slice(0, 200)}${input.body.length > 200 ? "..." : ""}`
|
|
|
2968
3756
|
if (!result) {
|
|
2969
3757
|
return {
|
|
2970
3758
|
success: false,
|
|
2971
|
-
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
|
|
2972
3764
|
};
|
|
2973
3765
|
}
|
|
2974
3766
|
let kanbanResult;
|
|
@@ -3127,10 +3919,12 @@ function isResourceError(result) {
|
|
|
3127
3919
|
}
|
|
3128
3920
|
var DEFAULT_BRIEFING_CONFIG = {
|
|
3129
3921
|
entryCount: 3,
|
|
3922
|
+
summaryCount: 1,
|
|
3130
3923
|
includeTeam: false,
|
|
3131
3924
|
issueCount: 0,
|
|
3132
3925
|
prCount: 0,
|
|
3133
3926
|
prStatusBreakdown: false,
|
|
3927
|
+
milestoneCount: 3,
|
|
3134
3928
|
workflowCount: 0,
|
|
3135
3929
|
workflowStatusBreakdown: false,
|
|
3136
3930
|
copilotReviews: false
|
|
@@ -3870,7 +4664,7 @@ var TeamCreateEntrySchemaMcp = z.object({
|
|
|
3870
4664
|
author: z.string().optional()
|
|
3871
4665
|
});
|
|
3872
4666
|
var TeamGetRecentSchema = z.object({
|
|
3873
|
-
limit: z.number().max(500).optional().default(10)
|
|
4667
|
+
limit: z.number().min(1).max(500).optional().default(10)
|
|
3874
4668
|
});
|
|
3875
4669
|
var TeamGetRecentSchemaMcp = z.object({
|
|
3876
4670
|
limit: relaxedNumber().optional().default(10)
|
|
@@ -5015,6 +5809,179 @@ function getTeamExportTools(context) {
|
|
|
5015
5809
|
}
|
|
5016
5810
|
];
|
|
5017
5811
|
}
|
|
5812
|
+
var TeamExportMarkdownSchema = z.object({
|
|
5813
|
+
output_dir: z.string().min(1),
|
|
5814
|
+
start_date: z.string().regex(DATE_FORMAT_REGEX, DATE_FORMAT_MESSAGE).optional(),
|
|
5815
|
+
end_date: z.string().regex(DATE_FORMAT_REGEX, DATE_FORMAT_MESSAGE).optional(),
|
|
5816
|
+
tags: z.array(z.string()).optional(),
|
|
5817
|
+
limit: z.number().max(500).optional().default(100)
|
|
5818
|
+
});
|
|
5819
|
+
var TeamExportMarkdownSchemaMcp = z.object({
|
|
5820
|
+
output_dir: z.string().describe("Target directory for .md files"),
|
|
5821
|
+
start_date: z.string().optional().describe("Start date filter (YYYY-MM-DD)"),
|
|
5822
|
+
end_date: z.string().optional().describe("End date filter (YYYY-MM-DD)"),
|
|
5823
|
+
tags: z.array(z.string()).optional().describe("Filter by tags"),
|
|
5824
|
+
limit: relaxedNumber().optional().default(100).describe("Max entries to export (default: 100, max: 500)")
|
|
5825
|
+
});
|
|
5826
|
+
var TeamExportMarkdownOutputSchema = z.object({
|
|
5827
|
+
success: z.boolean().optional(),
|
|
5828
|
+
exported_count: z.number().optional(),
|
|
5829
|
+
output_dir: z.string().optional(),
|
|
5830
|
+
files: z.array(z.string()).optional(),
|
|
5831
|
+
skipped: z.number().optional()
|
|
5832
|
+
}).extend(ErrorFieldsMixin.shape);
|
|
5833
|
+
var TeamImportMarkdownSchema = z.object({
|
|
5834
|
+
source_dir: z.string().min(1),
|
|
5835
|
+
dry_run: z.boolean().optional().default(false),
|
|
5836
|
+
limit: z.number().max(500).optional().default(100)
|
|
5837
|
+
});
|
|
5838
|
+
var TeamImportMarkdownSchemaMcp = z.object({
|
|
5839
|
+
source_dir: z.string().describe("Directory containing .md files to import"),
|
|
5840
|
+
dry_run: z.boolean().optional().default(false).describe("Parse and validate without writing to database"),
|
|
5841
|
+
limit: relaxedNumber().optional().default(100).describe("Max files to process (default: 100, max: 500)")
|
|
5842
|
+
});
|
|
5843
|
+
var TeamImportMarkdownOutputSchema = z.object({
|
|
5844
|
+
success: z.boolean().optional(),
|
|
5845
|
+
created: z.number().optional(),
|
|
5846
|
+
updated: z.number().optional(),
|
|
5847
|
+
skipped: z.number().optional(),
|
|
5848
|
+
errors: z.array(z.object({ file: z.string(), error: z.string() })).optional(),
|
|
5849
|
+
dry_run: z.boolean().optional()
|
|
5850
|
+
}).extend(ErrorFieldsMixin.shape);
|
|
5851
|
+
function getTeamIoTools(context) {
|
|
5852
|
+
const { teamDb, progress } = context;
|
|
5853
|
+
if (!teamDb) {
|
|
5854
|
+
return [
|
|
5855
|
+
{
|
|
5856
|
+
name: "team_export_markdown",
|
|
5857
|
+
title: "Team Export to Markdown",
|
|
5858
|
+
description: "Export team entries as frontmattered Markdown files",
|
|
5859
|
+
group: "team",
|
|
5860
|
+
inputSchema: TeamExportMarkdownSchemaMcp,
|
|
5861
|
+
outputSchema: TeamExportMarkdownOutputSchema,
|
|
5862
|
+
annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: true },
|
|
5863
|
+
handler: () => ({
|
|
5864
|
+
success: false,
|
|
5865
|
+
error: "Team collaboration is not configured. Set TEAM_DB_PATH to enable."
|
|
5866
|
+
})
|
|
5867
|
+
},
|
|
5868
|
+
{
|
|
5869
|
+
name: "team_import_markdown",
|
|
5870
|
+
title: "Team Import from Markdown",
|
|
5871
|
+
description: "Import frontmattered Markdown files into the team journal",
|
|
5872
|
+
group: "team",
|
|
5873
|
+
inputSchema: TeamImportMarkdownSchemaMcp,
|
|
5874
|
+
outputSchema: TeamImportMarkdownOutputSchema,
|
|
5875
|
+
annotations: {
|
|
5876
|
+
readOnlyHint: false,
|
|
5877
|
+
idempotentHint: false,
|
|
5878
|
+
openWorldHint: true
|
|
5879
|
+
},
|
|
5880
|
+
handler: () => ({
|
|
5881
|
+
success: false,
|
|
5882
|
+
error: "Team collaboration is not configured. Set TEAM_DB_PATH to enable."
|
|
5883
|
+
})
|
|
5884
|
+
}
|
|
5885
|
+
];
|
|
5886
|
+
}
|
|
5887
|
+
return [
|
|
5888
|
+
{
|
|
5889
|
+
name: "team_export_markdown",
|
|
5890
|
+
title: "Team Export to Markdown",
|
|
5891
|
+
description: "Export team journal entries as individual frontmattered Markdown files (.md). Includes author field in frontmatter. Files can be re-imported via team_import_markdown.",
|
|
5892
|
+
group: "team",
|
|
5893
|
+
inputSchema: TeamExportMarkdownSchemaMcp,
|
|
5894
|
+
outputSchema: TeamExportMarkdownOutputSchema,
|
|
5895
|
+
annotations: {
|
|
5896
|
+
readOnlyHint: false,
|
|
5897
|
+
destructiveHint: false,
|
|
5898
|
+
idempotentHint: true,
|
|
5899
|
+
openWorldHint: true
|
|
5900
|
+
},
|
|
5901
|
+
handler: async (params) => {
|
|
5902
|
+
try {
|
|
5903
|
+
const input = TeamExportMarkdownSchema.parse(params);
|
|
5904
|
+
assertSafeDirectoryPath(input.output_dir);
|
|
5905
|
+
await sendProgress(progress, 0, 3, "Fetching team entries...");
|
|
5906
|
+
const limit = input.limit ?? 100;
|
|
5907
|
+
let entries;
|
|
5908
|
+
if (input.start_date || input.end_date) {
|
|
5909
|
+
const startDate = input.start_date ?? DATE_MIN_SENTINEL;
|
|
5910
|
+
const endDate = input.end_date ?? DATE_MAX_SENTINEL;
|
|
5911
|
+
entries = teamDb.searchByDateRange(startDate, endDate, {
|
|
5912
|
+
tags: input.tags,
|
|
5913
|
+
limit
|
|
5914
|
+
});
|
|
5915
|
+
} else {
|
|
5916
|
+
entries = teamDb.getRecentEntries(limit);
|
|
5917
|
+
}
|
|
5918
|
+
await sendProgress(
|
|
5919
|
+
progress,
|
|
5920
|
+
1,
|
|
5921
|
+
3,
|
|
5922
|
+
`Exporting ${String(entries.length)} team entries...`
|
|
5923
|
+
);
|
|
5924
|
+
const entryIds = entries.map((e) => e.id);
|
|
5925
|
+
const authorMap = batchFetchAuthors(teamDb, entryIds);
|
|
5926
|
+
const exportable = entries.map((e) => ({
|
|
5927
|
+
id: e.id,
|
|
5928
|
+
content: e.content,
|
|
5929
|
+
entryType: e.entryType,
|
|
5930
|
+
timestamp: e.timestamp,
|
|
5931
|
+
tags: e.tags,
|
|
5932
|
+
significance: e.significanceType ?? void 0,
|
|
5933
|
+
author: authorMap.get(e.id) ?? void 0
|
|
5934
|
+
}));
|
|
5935
|
+
const result = await exportEntriesToMarkdown(
|
|
5936
|
+
exportable,
|
|
5937
|
+
input.output_dir,
|
|
5938
|
+
teamDb
|
|
5939
|
+
);
|
|
5940
|
+
await sendProgress(progress, 3, 3, "Team export complete");
|
|
5941
|
+
return result;
|
|
5942
|
+
} catch (err) {
|
|
5943
|
+
return formatHandlerError(err);
|
|
5944
|
+
}
|
|
5945
|
+
}
|
|
5946
|
+
},
|
|
5947
|
+
{
|
|
5948
|
+
name: "team_import_markdown",
|
|
5949
|
+
title: "Team Import from Markdown",
|
|
5950
|
+
description: "Import frontmattered Markdown files (.md) into the team journal. Author is set from TEAM_AUTHOR env or git config. Use dry_run: true to preview without writing.",
|
|
5951
|
+
group: "team",
|
|
5952
|
+
inputSchema: TeamImportMarkdownSchemaMcp,
|
|
5953
|
+
outputSchema: TeamImportMarkdownOutputSchema,
|
|
5954
|
+
annotations: {
|
|
5955
|
+
readOnlyHint: false,
|
|
5956
|
+
destructiveHint: false,
|
|
5957
|
+
idempotentHint: false,
|
|
5958
|
+
openWorldHint: true
|
|
5959
|
+
},
|
|
5960
|
+
handler: async (params) => {
|
|
5961
|
+
try {
|
|
5962
|
+
const input = TeamImportMarkdownSchema.parse(params);
|
|
5963
|
+
assertSafeDirectoryPath(input.source_dir);
|
|
5964
|
+
await sendProgress(progress, 0, 2, "Reading markdown files...");
|
|
5965
|
+
const author = resolveAuthor();
|
|
5966
|
+
const result = await importMarkdownEntries(
|
|
5967
|
+
input.source_dir,
|
|
5968
|
+
teamDb,
|
|
5969
|
+
{
|
|
5970
|
+
dry_run: input.dry_run,
|
|
5971
|
+
limit: input.limit,
|
|
5972
|
+
author
|
|
5973
|
+
},
|
|
5974
|
+
context.vectorManager
|
|
5975
|
+
);
|
|
5976
|
+
await sendProgress(progress, 2, 2, "Team import complete");
|
|
5977
|
+
return result;
|
|
5978
|
+
} catch (err) {
|
|
5979
|
+
return formatHandlerError(err);
|
|
5980
|
+
}
|
|
5981
|
+
}
|
|
5982
|
+
}
|
|
5983
|
+
];
|
|
5984
|
+
}
|
|
5018
5985
|
function getTeamBackupTools(context) {
|
|
5019
5986
|
const { teamDb } = context;
|
|
5020
5987
|
return [
|
|
@@ -5295,6 +6262,7 @@ function getTeamTools(context) {
|
|
|
5295
6262
|
...getTeamAnalyticsTools(context),
|
|
5296
6263
|
...getTeamRelationshipTools(context),
|
|
5297
6264
|
...getTeamExportTools(context),
|
|
6265
|
+
...getTeamIoTools(context),
|
|
5298
6266
|
...getTeamBackupTools(context),
|
|
5299
6267
|
...getTeamVectorTools(context)
|
|
5300
6268
|
];
|
|
@@ -5324,8 +6292,10 @@ var METHOD_ALIASES = {
|
|
|
5324
6292
|
link: "linkEntries",
|
|
5325
6293
|
graph: "visualizeRelationships"
|
|
5326
6294
|
},
|
|
5327
|
-
|
|
5328
|
-
dump: "exportEntries"
|
|
6295
|
+
io: {
|
|
6296
|
+
dump: "exportEntries",
|
|
6297
|
+
md: "exportMarkdown",
|
|
6298
|
+
importMd: "importMarkdown"
|
|
5329
6299
|
},
|
|
5330
6300
|
admin: {
|
|
5331
6301
|
edit: "updateEntry",
|
|
@@ -5342,6 +6312,8 @@ var METHOD_ALIASES = {
|
|
|
5342
6312
|
context: "getGithubContext",
|
|
5343
6313
|
kanban: "getKanbanBoard",
|
|
5344
6314
|
moveKanban: "moveKanbanItem",
|
|
6315
|
+
addKanban: "addKanbanItem",
|
|
6316
|
+
deleteKanban: "deleteKanbanItem",
|
|
5345
6317
|
milestones: "getGithubMilestones",
|
|
5346
6318
|
milestone: "getGithubMilestone",
|
|
5347
6319
|
createMilestone: "createGithubMilestone",
|
|
@@ -5400,9 +6372,10 @@ var GROUP_EXAMPLES = {
|
|
|
5400
6372
|
'mj.relationships.linkEntries({ from_entry_id: 1, to_entry_id: 2, relationship_type: "implements" })',
|
|
5401
6373
|
"mj.relationships.visualizeRelationships({ entry_id: 1 })"
|
|
5402
6374
|
],
|
|
5403
|
-
|
|
5404
|
-
'mj.
|
|
5405
|
-
'mj.
|
|
6375
|
+
io: [
|
|
6376
|
+
'mj.io.exportEntries({ format: "json" })',
|
|
6377
|
+
'mj.io.exportMarkdown({ output_dir: "./export" })',
|
|
6378
|
+
'mj.io.importMarkdown({ source_dir: "./import" })'
|
|
5406
6379
|
],
|
|
5407
6380
|
admin: [
|
|
5408
6381
|
'mj.admin.updateEntry({ entry_id: 1, content: "Updated content" })',
|
|
@@ -5415,6 +6388,7 @@ var GROUP_EXAMPLES = {
|
|
|
5415
6388
|
'mj.github.getGithubIssues({ state: "open" })',
|
|
5416
6389
|
'mj.github.getGithubPrs({ state: "open" })',
|
|
5417
6390
|
"mj.github.getKanbanBoard({ project_number: 1 })",
|
|
6391
|
+
"mj.github.addKanbanItem({ project_number: 1, issue_number: 123 })",
|
|
5418
6392
|
"mj.github.getGithubMilestones()",
|
|
5419
6393
|
"mj.github.getRepoInsights()"
|
|
5420
6394
|
],
|
|
@@ -5463,7 +6437,9 @@ var POSITIONAL_PARAM_MAP = {
|
|
|
5463
6437
|
getGithubIssue: "issue_number",
|
|
5464
6438
|
getGithubPr: "pr_number",
|
|
5465
6439
|
getKanbanBoard: "project_number",
|
|
5466
|
-
moveKanbanItem: ["item_id", "
|
|
6440
|
+
moveKanbanItem: ["item_id", "target_status"],
|
|
6441
|
+
addKanbanItem: "issue_number",
|
|
6442
|
+
deleteKanbanItem: "item_id",
|
|
5467
6443
|
getGithubMilestone: "milestone_number",
|
|
5468
6444
|
createGithubMilestone: "title",
|
|
5469
6445
|
updateGithubMilestone: "milestone_number",
|
|
@@ -5493,7 +6469,7 @@ var GROUP_PREFIX_MAP = {
|
|
|
5493
6469
|
search: "",
|
|
5494
6470
|
analytics: "",
|
|
5495
6471
|
relationships: "",
|
|
5496
|
-
|
|
6472
|
+
io: "",
|
|
5497
6473
|
admin: "",
|
|
5498
6474
|
github: "",
|
|
5499
6475
|
backup: "",
|
|
@@ -5596,7 +6572,7 @@ var JournalApi = class {
|
|
|
5596
6572
|
search;
|
|
5597
6573
|
analytics;
|
|
5598
6574
|
relationships;
|
|
5599
|
-
|
|
6575
|
+
io;
|
|
5600
6576
|
admin;
|
|
5601
6577
|
github;
|
|
5602
6578
|
backup;
|
|
@@ -5617,7 +6593,7 @@ var JournalApi = class {
|
|
|
5617
6593
|
"relationships",
|
|
5618
6594
|
this.toolsByGroup.get("relationships") ?? []
|
|
5619
6595
|
);
|
|
5620
|
-
this.
|
|
6596
|
+
this.io = createGroupApi("io", this.toolsByGroup.get("io") ?? []);
|
|
5621
6597
|
this.admin = createGroupApi("admin", this.toolsByGroup.get("admin") ?? []);
|
|
5622
6598
|
this.github = createGroupApi("github", this.toolsByGroup.get("github") ?? []);
|
|
5623
6599
|
this.backup = createGroupApi("backup", this.toolsByGroup.get("backup") ?? []);
|
|
@@ -5649,7 +6625,9 @@ var JournalApi = class {
|
|
|
5649
6625
|
search: this.search,
|
|
5650
6626
|
analytics: this.analytics,
|
|
5651
6627
|
relationships: this.relationships,
|
|
5652
|
-
|
|
6628
|
+
io: this.io,
|
|
6629
|
+
// Backward-compat alias — agents using mj.export.* still work
|
|
6630
|
+
export: this.io,
|
|
5653
6631
|
admin: this.admin,
|
|
5654
6632
|
github: this.github,
|
|
5655
6633
|
backup: this.backup,
|
|
@@ -5695,8 +6673,8 @@ var DEFAULT_SECURITY_CONFIG = {
|
|
|
5695
6673
|
maxCodeLength: 50 * 1024,
|
|
5696
6674
|
// 50KB
|
|
5697
6675
|
maxExecutionsPerMinute: 60,
|
|
5698
|
-
maxResultSize:
|
|
5699
|
-
//
|
|
6676
|
+
maxResultSize: 100 * 1024,
|
|
6677
|
+
// 100KB — context window protection (configurable via CODE_MODE_MAX_RESULT_SIZE)
|
|
5700
6678
|
blockedPatterns: [
|
|
5701
6679
|
/\brequire\s*\(/,
|
|
5702
6680
|
// No require()
|
|
@@ -5811,9 +6789,12 @@ var CodeModeSecurityManager = class {
|
|
|
5811
6789
|
const errors = [];
|
|
5812
6790
|
try {
|
|
5813
6791
|
const serialized = JSON.stringify(result);
|
|
5814
|
-
|
|
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);
|
|
5815
6796
|
errors.push(
|
|
5816
|
-
`Result exceeds maximum size of ${String(
|
|
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 }\``
|
|
5817
6798
|
);
|
|
5818
6799
|
}
|
|
5819
6800
|
} catch (error) {
|
|
@@ -5999,7 +6980,7 @@ var WorkerSandbox = class {
|
|
|
5999
6980
|
const effectiveTimeout = timeoutMs ?? this.options.timeoutMs;
|
|
6000
6981
|
const startTime = performance.now();
|
|
6001
6982
|
const startRss = process.memoryUsage.rss();
|
|
6002
|
-
return new Promise((
|
|
6983
|
+
return new Promise((resolve2) => {
|
|
6003
6984
|
const methodList = {};
|
|
6004
6985
|
const topLevel = [];
|
|
6005
6986
|
for (const [key, value] of Object.entries(apiBindings)) {
|
|
@@ -6055,7 +7036,7 @@ var WorkerSandbox = class {
|
|
|
6055
7036
|
cpuTimeMs: result.metrics.cpuTimeMs,
|
|
6056
7037
|
memoryUsedMb: Math.round((endRss - startRss) / 1024 / 1024)
|
|
6057
7038
|
};
|
|
6058
|
-
|
|
7039
|
+
resolve2(result);
|
|
6059
7040
|
});
|
|
6060
7041
|
worker.on("error", (err) => {
|
|
6061
7042
|
clearTimeout(timeoutHandle);
|
|
@@ -6064,7 +7045,7 @@ var WorkerSandbox = class {
|
|
|
6064
7045
|
const endRss = process.memoryUsage.rss();
|
|
6065
7046
|
const errorMessage = err.message;
|
|
6066
7047
|
const errorStack = err.stack;
|
|
6067
|
-
|
|
7048
|
+
resolve2({
|
|
6068
7049
|
success: false,
|
|
6069
7050
|
error: errorMessage,
|
|
6070
7051
|
stack: errorStack,
|
|
@@ -6081,7 +7062,7 @@ var WorkerSandbox = class {
|
|
|
6081
7062
|
if (exitCode !== 0) {
|
|
6082
7063
|
const endTime = performance.now();
|
|
6083
7064
|
const endRss = process.memoryUsage.rss();
|
|
6084
|
-
|
|
7065
|
+
resolve2({
|
|
6085
7066
|
success: false,
|
|
6086
7067
|
error: `Worker exited with code ${String(exitCode)} (likely timeout or OOM)`,
|
|
6087
7068
|
metrics: {
|
|
@@ -6189,7 +7170,12 @@ var ExecuteCodeSchemaMcp = z.object({
|
|
|
6189
7170
|
var securityManager = null;
|
|
6190
7171
|
var sandboxPool = null;
|
|
6191
7172
|
function getSecurityManager() {
|
|
6192
|
-
|
|
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
|
+
}
|
|
6193
7179
|
return securityManager;
|
|
6194
7180
|
}
|
|
6195
7181
|
function getSandboxPool() {
|
|
@@ -6207,7 +7193,7 @@ function collectNonCodeModeTools(context) {
|
|
|
6207
7193
|
...getSearchTools(context),
|
|
6208
7194
|
...getAnalyticsTools(context),
|
|
6209
7195
|
...getRelationshipTools(context),
|
|
6210
|
-
...
|
|
7196
|
+
...getIoTools(context),
|
|
6211
7197
|
...getAdminTools(context),
|
|
6212
7198
|
...getGitHubTools(context),
|
|
6213
7199
|
...getBackupTools(context),
|
|
@@ -6221,7 +7207,7 @@ function getCodeModeTools(context) {
|
|
|
6221
7207
|
{
|
|
6222
7208
|
name: "mj_execute_code",
|
|
6223
7209
|
title: "Execute Code (Code Mode)",
|
|
6224
|
-
description: "Execute JavaScript in a sandboxed environment with access to all journal tools via the `mj.*` API. Enables multi-step workflows in a single call, reducing token usage by 70-90%. API groups: mj.core.*, mj.search.*, mj.analytics.*, mj.relationships.*, mj.
|
|
7210
|
+
description: "Execute JavaScript in a sandboxed environment with access to all journal tools via the `mj.*` API. Enables multi-step workflows in a single call, reducing token usage by 70-90%. API groups: mj.core.*, mj.search.*, mj.analytics.*, mj.relationships.*, mj.io.*, mj.admin.*, mj.github.*, mj.backup.*, mj.team.*. Use mj.help() for method listing. Returns the last expression value.",
|
|
6225
7211
|
group: "codemode",
|
|
6226
7212
|
inputSchema: ExecuteCodeSchemaMcp,
|
|
6227
7213
|
annotations: {
|
|
@@ -6645,7 +7631,7 @@ var TOOL_GROUPS = {
|
|
|
6645
7631
|
search: ["search_entries", "search_by_date_range", "semantic_search", "get_vector_index_stats"],
|
|
6646
7632
|
analytics: ["get_statistics", "get_cross_project_insights"],
|
|
6647
7633
|
relationships: ["link_entries", "visualize_relationships"],
|
|
6648
|
-
|
|
7634
|
+
io: ["export_entries", "export_markdown", "import_markdown"],
|
|
6649
7635
|
admin: [
|
|
6650
7636
|
"update_entry",
|
|
6651
7637
|
"delete_entry",
|
|
@@ -6661,6 +7647,8 @@ var TOOL_GROUPS = {
|
|
|
6661
7647
|
"get_github_context",
|
|
6662
7648
|
"get_kanban_board",
|
|
6663
7649
|
"move_kanban_item",
|
|
7650
|
+
"add_kanban_item",
|
|
7651
|
+
"delete_kanban_item",
|
|
6664
7652
|
"create_github_issue_with_entry",
|
|
6665
7653
|
"close_github_issue_with_entry",
|
|
6666
7654
|
"get_github_milestones",
|
|
@@ -6686,6 +7674,8 @@ var TOOL_GROUPS = {
|
|
|
6686
7674
|
"team_link_entries",
|
|
6687
7675
|
"team_visualize_relationships",
|
|
6688
7676
|
"team_export_entries",
|
|
7677
|
+
"team_export_markdown",
|
|
7678
|
+
"team_import_markdown",
|
|
6689
7679
|
"team_backup",
|
|
6690
7680
|
"team_list_backups",
|
|
6691
7681
|
"team_semantic_search",
|
|
@@ -6704,14 +7694,14 @@ var META_GROUPS = {
|
|
|
6704
7694
|
"search",
|
|
6705
7695
|
"analytics",
|
|
6706
7696
|
"relationships",
|
|
6707
|
-
"
|
|
7697
|
+
"io",
|
|
6708
7698
|
"admin",
|
|
6709
7699
|
"github",
|
|
6710
7700
|
"backup",
|
|
6711
7701
|
"team",
|
|
6712
7702
|
"codemode"
|
|
6713
7703
|
],
|
|
6714
|
-
readonly: ["core", "search", "analytics", "relationships", "
|
|
7704
|
+
readonly: ["core", "search", "analytics", "relationships", "io"]
|
|
6715
7705
|
};
|
|
6716
7706
|
function getAllToolNames() {
|
|
6717
7707
|
const allTools = [];
|
|
@@ -6737,8 +7727,12 @@ function getEnabledGroups(enabledTools) {
|
|
|
6737
7727
|
}
|
|
6738
7728
|
return groups;
|
|
6739
7729
|
}
|
|
7730
|
+
var GROUP_ALIASES = { export: "io" };
|
|
7731
|
+
function resolveGroupAlias(name) {
|
|
7732
|
+
return GROUP_ALIASES[name] ?? name;
|
|
7733
|
+
}
|
|
6740
7734
|
function isGroup(name) {
|
|
6741
|
-
return name in TOOL_GROUPS;
|
|
7735
|
+
return resolveGroupAlias(name) in TOOL_GROUPS;
|
|
6742
7736
|
}
|
|
6743
7737
|
function isMetaGroup(name) {
|
|
6744
7738
|
return name in META_GROUPS;
|
|
@@ -6761,7 +7755,8 @@ function parseToolFilter(filterString) {
|
|
|
6761
7755
|
enabledTools = /* @__PURE__ */ new Set([...enabledTools, ...TOOL_GROUPS[group]]);
|
|
6762
7756
|
}
|
|
6763
7757
|
} else if (isGroup(name)) {
|
|
6764
|
-
|
|
7758
|
+
const resolved = resolveGroupAlias(name);
|
|
7759
|
+
enabledTools = /* @__PURE__ */ new Set([...enabledTools, ...TOOL_GROUPS[resolved]]);
|
|
6765
7760
|
} else {
|
|
6766
7761
|
enabledTools.add(name);
|
|
6767
7762
|
}
|
|
@@ -6772,7 +7767,8 @@ function parseToolFilter(filterString) {
|
|
|
6772
7767
|
});
|
|
6773
7768
|
} else if (isRemove) {
|
|
6774
7769
|
if (isGroup(name)) {
|
|
6775
|
-
|
|
7770
|
+
const resolved = resolveGroupAlias(name);
|
|
7771
|
+
for (const tool of TOOL_GROUPS[resolved]) {
|
|
6776
7772
|
enabledTools.delete(tool);
|
|
6777
7773
|
}
|
|
6778
7774
|
} else {
|
|
@@ -6789,7 +7785,8 @@ function parseToolFilter(filterString) {
|
|
|
6789
7785
|
enabledTools = /* @__PURE__ */ new Set([...enabledTools, ...TOOL_GROUPS[group]]);
|
|
6790
7786
|
}
|
|
6791
7787
|
} else if (isGroup(name)) {
|
|
6792
|
-
|
|
7788
|
+
const resolved = resolveGroupAlias(name);
|
|
7789
|
+
enabledTools = /* @__PURE__ */ new Set([...enabledTools, ...TOOL_GROUPS[resolved]]);
|
|
6793
7790
|
} else {
|
|
6794
7791
|
enabledTools.add(name);
|
|
6795
7792
|
}
|
|
@@ -6805,7 +7802,8 @@ function parseToolFilter(filterString) {
|
|
|
6805
7802
|
for (const rule of rules) {
|
|
6806
7803
|
if (rule.type === "exclude") {
|
|
6807
7804
|
if (isGroup(rule.target)) {
|
|
6808
|
-
|
|
7805
|
+
const resolved = resolveGroupAlias(rule.target);
|
|
7806
|
+
for (const tool of TOOL_GROUPS[resolved]) {
|
|
6809
7807
|
enabledTools.delete(tool);
|
|
6810
7808
|
}
|
|
6811
7809
|
} else {
|
|
@@ -6861,7 +7859,7 @@ var TOOL_GROUP_SCOPES = {
|
|
|
6861
7859
|
search: SCOPES.READ,
|
|
6862
7860
|
analytics: SCOPES.READ,
|
|
6863
7861
|
relationships: SCOPES.READ,
|
|
6864
|
-
|
|
7862
|
+
io: SCOPES.READ,
|
|
6865
7863
|
admin: SCOPES.ADMIN,
|
|
6866
7864
|
github: SCOPES.WRITE,
|
|
6867
7865
|
backup: SCOPES.ADMIN,
|
|
@@ -6914,6 +7912,8 @@ for (const [group, tools] of Object.entries(TOOL_GROUPS)) {
|
|
|
6914
7912
|
}
|
|
6915
7913
|
}
|
|
6916
7914
|
}
|
|
7915
|
+
toolScopeMap.set("import_markdown", SCOPES.WRITE);
|
|
7916
|
+
toolScopeMap.set("team_import_markdown", SCOPES.WRITE);
|
|
6917
7917
|
function getRequiredScope(toolName) {
|
|
6918
7918
|
return toolScopeMap.get(toolName) ?? SCOPES.READ;
|
|
6919
7919
|
}
|
|
@@ -7147,10 +8147,10 @@ function getToolIcon(group) {
|
|
|
7147
8147
|
title: "Relationships",
|
|
7148
8148
|
description: "Entry relationship management"
|
|
7149
8149
|
},
|
|
7150
|
-
|
|
7151
|
-
iconUrl: "https://cdn.jsdelivr.net/npm/@mdi/svg@7.4.47/svg/
|
|
7152
|
-
title: "
|
|
7153
|
-
description: "
|
|
8150
|
+
io: {
|
|
8151
|
+
iconUrl: "https://cdn.jsdelivr.net/npm/@mdi/svg@7.4.47/svg/swap-horizontal.svg",
|
|
8152
|
+
title: "IO",
|
|
8153
|
+
description: "Import/export operations"
|
|
7154
8154
|
},
|
|
7155
8155
|
admin: {
|
|
7156
8156
|
iconUrl: "https://cdn.jsdelivr.net/npm/@mdi/svg@7.4.47/svg/cog.svg",
|
|
@@ -7264,7 +8264,7 @@ function getAllToolDefinitions(context) {
|
|
|
7264
8264
|
...getSearchTools(context),
|
|
7265
8265
|
...getAnalyticsTools(context),
|
|
7266
8266
|
...getRelationshipTools(context),
|
|
7267
|
-
...
|
|
8267
|
+
...getIoTools(context),
|
|
7268
8268
|
...getAdminTools(context),
|
|
7269
8269
|
...getGitHubTools(context),
|
|
7270
8270
|
...getBackupTools(context),
|