Skip to content

Commit ed76052

Browse files
committed
mettre à jour la version à 1.1.1 et ajouter le changelog
1 parent 25d9be4 commit ed76052

File tree

3 files changed

+86
-114
lines changed

3 files changed

+86
-114
lines changed

CHANGELOG.md

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
# Changelog
2+
3+
## [1.1.1] - 2024-01-20
4+
5+
### Added
6+
- Support for node relationships
7+
- Enhanced search with task status filtering
8+
- Improved callouts support
9+
10+
### Changed
11+
- Removed get_todo_tasks in favor of search_nodes with status filter
12+
- Optimized file searching
13+
- Improved tool descriptions
14+
15+
### Fixed
16+
- Fixed relationship parsing in content.json
17+
- Better file path handling
18+
19+
## [1.0.0] - 2024-01-19
20+
21+
### Added
22+
- Initial release
23+
- Basic XMind file support
24+
- Node and task extraction
25+
- File searching capabilities

index.ts

Lines changed: 60 additions & 113 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ interface XMindNode {
6060
callouts?: {
6161
title: string;
6262
}[];
63+
relationships?: XMindRelationship[];
6364
}
6465

6566
interface XMindTopic {
@@ -87,6 +88,13 @@ interface XMindTopic {
8788
labels?: string[];
8889
}
8990

91+
interface XMindRelationship {
92+
id: string;
93+
end1Id: string;
94+
end2Id: string;
95+
title?: string;
96+
}
97+
9098
// Class XMindParser
9199
class XMindParser {
92100
private filePath: string;
@@ -120,8 +128,17 @@ class XMindParser {
120128
private parseContentJson(jsonContent: string): Promise<XMindNode[]> {
121129
try {
122130
const content = JSON.parse(jsonContent);
123-
const allNodes = content.map((sheet: { rootTopic: XMindTopic; title?: string }) => {
124-
return this.processNode(sheet.rootTopic, sheet.title || "Untitled Map");
131+
const allNodes = content.map((sheet: {
132+
rootTopic: XMindTopic;
133+
title?: string;
134+
relationships?: XMindRelationship[];
135+
}) => {
136+
const rootNode = this.processNode(sheet.rootTopic, sheet.title || "Untitled Map");
137+
// Ajouter les relations au nœud racine
138+
if (sheet.relationships) {
139+
rootNode.relationships = sheet.relationships;
140+
}
141+
return rootNode;
125142
});
126143
return Promise.resolve(allNodes);
127144
} catch (error) {
@@ -180,71 +197,11 @@ function getNodePath(node: XMindNode, parents: string[] = []): string {
180197
return parents.length > 0 ? `${parents.join(' > ')} > ${node.title}` : node.title;
181198
}
182199

183-
interface TodoTask {
184-
path: string;
185-
sheet: string;
186-
title: string;
187-
status: 'todo' | 'done';
188-
context?: {
189-
children: {
190-
title: string;
191-
subChildren?: string[];
192-
}[];
193-
};
194-
notes?: {
195-
content?: string;
196-
};
197-
labels?: string[];
198-
}
199-
200-
function findTodoTasks(node: XMindNode, parents: string[] = []): TodoTask[] {
201-
const todos: TodoTask[] = [];
202-
203-
if (node.taskStatus) {
204-
const task: TodoTask = {
205-
path: getNodePath(node, parents),
206-
sheet: node.sheetTitle || 'Untitled Map',
207-
title: node.title,
208-
status: node.taskStatus
209-
};
210-
211-
// Add notes, callouts and labels
212-
if (node.notes) task.notes = node.notes;
213-
if (node.labels) task.labels = node.labels;
214-
215-
// Add child nodes as context
216-
if (node.children && node.children.length > 0) {
217-
task.context = {
218-
children: node.children.map(child => ({
219-
title: child.title,
220-
subChildren: child.children?.map(sc => sc.title)
221-
}))
222-
};
223-
}
224-
225-
todos.push(task);
226-
}
227-
228-
// Recursive search in children only (callouts are now part of notes)
229-
if (node.children) {
230-
const currentPath = [...parents, node.title];
231-
node.children.forEach(child => {
232-
todos.push(...findTodoTasks(child, currentPath));
233-
});
234-
}
235-
236-
return todos;
237-
}
238-
239200
// Schema definitions
240201
const ReadXMindArgsSchema = z.object({
241202
path: z.string(),
242203
});
243204

244-
const GetTodoTasksArgsSchema = z.object({
245-
path: z.string(),
246-
});
247-
248205
const ListXMindDirectoryArgsSchema = z.object({
249206
directory: z.string().optional(),
250207
});
@@ -272,8 +229,9 @@ const ExtractNodeByIdArgsSchema = z.object({
272229
const SearchNodesArgsSchema = z.object({
273230
path: z.string(),
274231
query: z.string(),
275-
searchIn: z.array(z.enum(['title', 'notes', 'labels', 'callouts'])).optional(),
232+
searchIn: z.array(z.enum(['title', 'notes', 'labels', 'callouts', 'tasks'])).optional(),
276233
caseSensitive: z.boolean().optional(),
234+
taskStatus: z.enum(['todo', 'done']).optional(), // Ajout du filtre de statut de tâche
277235
});
278236

279237
interface MultipleXMindResult {
@@ -458,6 +416,7 @@ interface NodeMatch {
458416
callouts?: {
459417
title: string;
460418
}[];
419+
taskStatus?: 'todo' | 'done';
461420
}
462421

463422
interface SearchResult {
@@ -473,13 +432,14 @@ function searchNodes(
473432
query: string,
474433
options: {
475434
searchIn?: string[],
476-
caseSensitive?: boolean
435+
caseSensitive?: boolean,
436+
taskStatus?: 'todo' | 'done'
477437
} = {},
478438
parents: string[] = []
479439
): NodeMatch[] {
480440
const matches: NodeMatch[] = [];
481441
const searchQuery = options.caseSensitive ? query : query.toLowerCase();
482-
const searchFields = options.searchIn || ['title', 'notes', 'labels', 'callouts'];
442+
const searchFields = options.searchIn || ['title', 'notes', 'labels', 'callouts', 'tasks'];
483443

484444
const matchedIn: string[] = [];
485445

@@ -490,6 +450,14 @@ function searchNodes(
490450
return searchIn.includes(searchQuery);
491451
};
492452

453+
// Vérification du statut de tâche si spécifié
454+
if (options.taskStatus && node.taskStatus) {
455+
if (node.taskStatus !== options.taskStatus) {
456+
// Si le statut ne correspond pas, ignorer ce nœud
457+
return [];
458+
}
459+
}
460+
493461
// Vérifier chaque champ configuré
494462
if (searchFields.includes('title') && matchesText(node.title)) {
495463
matchedIn.push('title');
@@ -503,9 +471,15 @@ function searchNodes(
503471
if (searchFields.includes('callouts') && node.callouts?.some(callout => matchesText(callout.title))) {
504472
matchedIn.push('callouts');
505473
}
474+
if (searchFields.includes('tasks') && node.taskStatus) {
475+
matchedIn.push('tasks');
476+
}
477+
478+
// Si on a trouvé des correspondances ou si c'est une tâche correspondante, ajouter ce nœud
479+
const shouldIncludeNode = matchedIn.length > 0 ||
480+
(options.taskStatus && node.taskStatus === options.taskStatus);
506481

507-
// Si on a trouvé des correspondances, ajouter ce nœud
508-
if (matchedIn.length > 0 && node.id) { // Vérifier que l'ID existe
482+
if (shouldIncludeNode && node.id) {
509483
matches.push({
510484
id: node.id,
511485
title: node.title,
@@ -514,7 +488,8 @@ function searchNodes(
514488
matchedIn,
515489
notes: node.notes?.content,
516490
labels: node.labels,
517-
callouts: node.callouts
491+
callouts: node.callouts,
492+
taskStatus: node.taskStatus // Ajout du statut de tâche dans les résultats
518493
});
519494
}
520495

@@ -633,33 +608,18 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
633608
name: "read_xmind",
634609
description: `Parse and analyze XMind files with multiple capabilities:
635610
- Extract complete mind map structure in JSON format
636-
- Generate text or markdown summaries of the entire map or specific nodes
637-
- Search for specific content using keywords or regular expressions
638-
- Extract relationships between nodes
611+
- Include all relationships between nodes with their IDs and titles
612+
- Extract callouts attached to topics
613+
- Generate text or markdown summaries
614+
- Search for specific content
639615
- Get hierarchical path to any node
640616
- Filter content by labels, task status, or node depth
641617
- Extract all URLs and external references
642-
- Generate outline view of the mind map
643-
- Count nodes, tasks, and get map statistics
618+
- Analyze relationships and connections between topics
644619
Input: File path to .xmind file
645-
Output: JSON structure or formatted text based on query parameters`,
620+
Output: JSON structure containing nodes, relationships, and callouts`,
646621
inputSchema: zodToJsonSchema(ReadXMindArgsSchema),
647622
},
648-
{
649-
name: "get_todo_tasks",
650-
description: `Advanced task management and analysis tool for XMind files:
651-
- Extract all tasks marked as TODO with their full context path
652-
- Group tasks by priority, labels, or categories
653-
- Find dependencies between tasks
654-
- Calculate task completion statistics
655-
- Identify task bottlenecks in projects
656-
- Extract deadlines and timeline information
657-
- Generate task reports in various formats
658-
- Track task status changes
659-
Input: File path to .xmind file
660-
Output: Structured list of tasks with contextual information`,
661-
inputSchema: zodToJsonSchema(GetTodoTasksArgsSchema),
662-
},
663623
{
664624
name: "list_xmind_directory",
665625
description: `Comprehensive XMind file discovery and analysis tool:
@@ -747,20 +707,22 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
747707
{
748708
name: "search_nodes",
749709
description: `Advanced node search with multiple criteria:
750-
- Search through titles, notes, and labels
710+
- Search through titles, notes, labels, callouts and tasks
711+
- Filter by task status (todo/done)
712+
- Find nodes by their relationships
751713
- Configure which fields to search in
752714
- Case-sensitive or insensitive search
753-
- Get full context including path and sheet
715+
- Get full context including task status
754716
- Returns all matching nodes with their IDs
755-
- Includes match location information
756-
Note: Use "extract_node" with the found path to get detailed node content and full context
717+
- Includes relationship information and task status
757718
Input: {
758719
path: Path to .xmind file,
759720
query: Search text,
760-
searchIn: Array of fields to search in ['title', 'notes', 'labels', 'callouts'],
721+
searchIn: Array of fields to search in ['title', 'notes', 'labels', 'callouts', 'tasks'],
722+
taskStatus: 'todo' | 'done' (optional),
761723
caseSensitive: Boolean (optional)
762724
}
763-
Output: Detailed search results with context`,
725+
Output: Detailed search results with task status and context`,
764726
inputSchema: zodToJsonSchema(SearchNodesArgsSchema),
765727
},
766728
],
@@ -787,22 +749,6 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
787749
};
788750
}
789751

790-
case "get_todo_tasks": {
791-
const parsed = GetTodoTasksArgsSchema.safeParse(args);
792-
if (!parsed.success) {
793-
throw new Error(`Invalid arguments for get_todo_tasks: ${parsed.error}`);
794-
}
795-
if (!isPathAllowed(parsed.data.path)) {
796-
throw new Error(`Access denied: ${parsed.data.path} is not in an allowed directory`);
797-
}
798-
const parser = new XMindParser(parsed.data.path);
799-
const mindmap = await parser.parse();
800-
const todos = mindmap.flatMap(node => findTodoTasks(node, []));
801-
return {
802-
content: [{ type: "text", text: JSON.stringify(todos, null, 2) }],
803-
};
804-
}
805-
806752
case "list_xmind_directory": {
807753
const parsed = ListXMindDirectoryArgsSchema.safeParse(args);
808754
if (!parsed.success) {
@@ -906,15 +852,16 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
906852
const matches: NodeMatch[] = mindmap.flatMap(sheet =>
907853
searchNodes(sheet, parsed.data.query, {
908854
searchIn: parsed.data.searchIn,
909-
caseSensitive: parsed.data.caseSensitive
855+
caseSensitive: parsed.data.caseSensitive,
856+
taskStatus: parsed.data.taskStatus
910857
})
911858
);
912859

913860
const result: SearchResult = {
914861
query: parsed.data.query,
915862
matches,
916863
totalMatches: matches.length,
917-
searchedIn: parsed.data.searchIn || ['title', 'notes', 'labels']
864+
searchedIn: parsed.data.searchIn || ['title', 'notes', 'labels', 'callouts', 'tasks']
918865
};
919866

920867
return {

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@41px/mcp-xmind",
3-
"version": "1.1.0",
3+
"version": "1.1.1",
44
"description": "MCP server for XMind",
55
"license": "MIT",
66
"author": "Alexandre Peyroux <[email protected]>",

0 commit comments

Comments
 (0)