fixmind 1.0.2 → 1.0.4
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 +87 -87
- package/dist/dashboard/assets/index-Bl0HIoQR.js +14 -0
- package/dist/dashboard/assets/{index-DJIWVHe0.css → index-WDmgeIrO.css} +14 -0
- package/dist/dashboard/index.html +16 -16
- package/dist/src/cli.js +7 -1
- package/dist/src/cli.js.map +1 -1
- package/dist/src/launcher.d.ts +2 -0
- package/dist/src/launcher.js +16 -0
- package/dist/src/launcher.js.map +1 -0
- package/dist/src/mcp.js +96 -96
- package/dist/src/setup.js +15 -15
- package/dist/src/storage.js +72 -72
- package/dist/src/sync.js +61 -61
- package/docs/mcp-integration.md +112 -104
- package/package.json +45 -45
- package/dist/dashboard/assets/index-BMTB62U_.js +0 -14
- package/docs/sync-setup.md +0 -70
package/dist/src/storage.js
CHANGED
|
@@ -16,42 +16,42 @@ export function initializeDataDirectory() {
|
|
|
16
16
|
export function createLessonStore(filePath = databasePath()) {
|
|
17
17
|
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
18
18
|
const db = new DatabaseSync(filePath);
|
|
19
|
-
db.exec(`
|
|
20
|
-
CREATE TABLE IF NOT EXISTS lessons (
|
|
21
|
-
id TEXT PRIMARY KEY,
|
|
22
|
-
created_at TEXT NOT NULL,
|
|
23
|
-
updated_at TEXT NOT NULL,
|
|
24
|
-
tool TEXT NOT NULL,
|
|
25
|
-
project_path TEXT NOT NULL,
|
|
26
|
-
title TEXT NOT NULL,
|
|
27
|
-
original_prompt TEXT NOT NULL,
|
|
28
|
-
problem TEXT NOT NULL,
|
|
29
|
-
mistake TEXT NOT NULL,
|
|
30
|
-
root_cause TEXT NOT NULL,
|
|
31
|
-
fix_summary TEXT NOT NULL,
|
|
32
|
-
takeaway TEXT,
|
|
33
|
-
mistake_pattern TEXT,
|
|
34
|
-
when_not_applicable TEXT,
|
|
35
|
-
concepts TEXT NOT NULL,
|
|
36
|
-
files_changed TEXT NOT NULL,
|
|
37
|
-
code_example TEXT,
|
|
38
|
-
bad_code_example TEXT,
|
|
39
|
-
good_code_example TEXT,
|
|
40
|
-
code_explanation TEXT,
|
|
41
|
-
practice_task TEXT,
|
|
42
|
-
review_questions TEXT NOT NULL,
|
|
43
|
-
understanding TEXT NOT NULL,
|
|
44
|
-
next_review_at TEXT NOT NULL,
|
|
45
|
-
review_count INTEGER NOT NULL DEFAULT 0,
|
|
46
|
-
source_diff TEXT,
|
|
47
|
-
tags TEXT NOT NULL,
|
|
48
|
-
status TEXT NOT NULL DEFAULT 'active',
|
|
49
|
-
superseded_by TEXT,
|
|
50
|
-
supersedes TEXT,
|
|
51
|
-
supersede_reason TEXT
|
|
52
|
-
);
|
|
53
|
-
CREATE INDEX IF NOT EXISTS idx_lessons_created_at ON lessons(created_at DESC);
|
|
54
|
-
CREATE INDEX IF NOT EXISTS idx_lessons_next_review_at ON lessons(next_review_at);
|
|
19
|
+
db.exec(`
|
|
20
|
+
CREATE TABLE IF NOT EXISTS lessons (
|
|
21
|
+
id TEXT PRIMARY KEY,
|
|
22
|
+
created_at TEXT NOT NULL,
|
|
23
|
+
updated_at TEXT NOT NULL,
|
|
24
|
+
tool TEXT NOT NULL,
|
|
25
|
+
project_path TEXT NOT NULL,
|
|
26
|
+
title TEXT NOT NULL,
|
|
27
|
+
original_prompt TEXT NOT NULL,
|
|
28
|
+
problem TEXT NOT NULL,
|
|
29
|
+
mistake TEXT NOT NULL,
|
|
30
|
+
root_cause TEXT NOT NULL,
|
|
31
|
+
fix_summary TEXT NOT NULL,
|
|
32
|
+
takeaway TEXT,
|
|
33
|
+
mistake_pattern TEXT,
|
|
34
|
+
when_not_applicable TEXT,
|
|
35
|
+
concepts TEXT NOT NULL,
|
|
36
|
+
files_changed TEXT NOT NULL,
|
|
37
|
+
code_example TEXT,
|
|
38
|
+
bad_code_example TEXT,
|
|
39
|
+
good_code_example TEXT,
|
|
40
|
+
code_explanation TEXT,
|
|
41
|
+
practice_task TEXT,
|
|
42
|
+
review_questions TEXT NOT NULL,
|
|
43
|
+
understanding TEXT NOT NULL,
|
|
44
|
+
next_review_at TEXT NOT NULL,
|
|
45
|
+
review_count INTEGER NOT NULL DEFAULT 0,
|
|
46
|
+
source_diff TEXT,
|
|
47
|
+
tags TEXT NOT NULL,
|
|
48
|
+
status TEXT NOT NULL DEFAULT 'active',
|
|
49
|
+
superseded_by TEXT,
|
|
50
|
+
supersedes TEXT,
|
|
51
|
+
supersede_reason TEXT
|
|
52
|
+
);
|
|
53
|
+
CREATE INDEX IF NOT EXISTS idx_lessons_created_at ON lessons(created_at DESC);
|
|
54
|
+
CREATE INDEX IF NOT EXISTS idx_lessons_next_review_at ON lessons(next_review_at);
|
|
55
55
|
`);
|
|
56
56
|
return {
|
|
57
57
|
save(input) {
|
|
@@ -90,17 +90,17 @@ export function createLessonStore(filePath = databasePath()) {
|
|
|
90
90
|
tags: input.tags ?? [],
|
|
91
91
|
status: "active",
|
|
92
92
|
};
|
|
93
|
-
db.prepare(`
|
|
94
|
-
INSERT INTO lessons (
|
|
95
|
-
id, created_at, updated_at, tool, project_path, title, original_prompt,
|
|
96
|
-
problem, mistake, root_cause, fix_summary, takeaway, mistake_pattern,
|
|
97
|
-
when_not_applicable, concepts, files_changed, code_example, bad_code_example,
|
|
98
|
-
good_code_example, code_explanation, practice_task, review_questions,
|
|
99
|
-
understanding, next_review_at, review_count, source_diff, tags,
|
|
100
|
-
status, superseded_by, supersedes, supersede_reason
|
|
101
|
-
) VALUES (
|
|
102
|
-
?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, NULL, NULL, NULL
|
|
103
|
-
)
|
|
93
|
+
db.prepare(`
|
|
94
|
+
INSERT INTO lessons (
|
|
95
|
+
id, created_at, updated_at, tool, project_path, title, original_prompt,
|
|
96
|
+
problem, mistake, root_cause, fix_summary, takeaway, mistake_pattern,
|
|
97
|
+
when_not_applicable, concepts, files_changed, code_example, bad_code_example,
|
|
98
|
+
good_code_example, code_explanation, practice_task, review_questions,
|
|
99
|
+
understanding, next_review_at, review_count, source_diff, tags,
|
|
100
|
+
status, superseded_by, supersedes, supersede_reason
|
|
101
|
+
) VALUES (
|
|
102
|
+
?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, NULL, NULL, NULL
|
|
103
|
+
)
|
|
104
104
|
`).run(lesson.id, lesson.createdAt, lesson.updatedAt, lesson.tool, lesson.projectPath, lesson.title, lesson.originalPrompt, lesson.problem, lesson.mistake, lesson.rootCause, lesson.fixSummary, lesson.takeaway ?? null, lesson.mistakePattern ?? null, lesson.whenNotApplicable ?? null, JSON.stringify(lesson.concepts), JSON.stringify(lesson.filesChanged), lesson.codeExample ?? null, lesson.badCodeExample ?? null, lesson.goodCodeExample ?? null, lesson.codeExplanation ?? null, lesson.practiceTask ?? null, JSON.stringify(lesson.reviewQuestions), lesson.understanding, lesson.nextReviewAt, lesson.reviewCount, lesson.sourceDiff ?? null, JSON.stringify(lesson.tags), lesson.status);
|
|
105
105
|
if (input.supersedesLessonId && this.get(input.supersedesLessonId)) {
|
|
106
106
|
this.supersede(input.supersedesLessonId, lesson.id, input.supersedeReason);
|
|
@@ -120,31 +120,31 @@ export function createLessonStore(filePath = databasePath()) {
|
|
|
120
120
|
return db.prepare("SELECT * FROM lessons WHERE updated_at > ? ORDER BY updated_at ASC").all(timestamp).map(fromDb);
|
|
121
121
|
},
|
|
122
122
|
upsertFromRemote(lesson) {
|
|
123
|
-
db.prepare(`
|
|
124
|
-
INSERT INTO lessons (
|
|
125
|
-
id, created_at, updated_at, tool, project_path, title, original_prompt,
|
|
126
|
-
problem, mistake, root_cause, fix_summary, takeaway, mistake_pattern,
|
|
127
|
-
when_not_applicable, concepts, files_changed, code_example, bad_code_example,
|
|
128
|
-
good_code_example, code_explanation, practice_task, review_questions,
|
|
129
|
-
understanding, next_review_at, review_count, source_diff, tags,
|
|
130
|
-
status, superseded_by, supersedes, supersede_reason
|
|
131
|
-
) VALUES (
|
|
132
|
-
?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?
|
|
133
|
-
)
|
|
134
|
-
ON CONFLICT(id) DO UPDATE SET
|
|
135
|
-
created_at=excluded.created_at, updated_at=excluded.updated_at, tool=excluded.tool,
|
|
136
|
-
project_path=excluded.project_path, title=excluded.title, original_prompt=excluded.original_prompt,
|
|
137
|
-
problem=excluded.problem, mistake=excluded.mistake, root_cause=excluded.root_cause,
|
|
138
|
-
fix_summary=excluded.fix_summary, takeaway=excluded.takeaway, mistake_pattern=excluded.mistake_pattern,
|
|
139
|
-
when_not_applicable=excluded.when_not_applicable, concepts=excluded.concepts,
|
|
140
|
-
files_changed=excluded.files_changed, code_example=excluded.code_example,
|
|
141
|
-
bad_code_example=excluded.bad_code_example, good_code_example=excluded.good_code_example,
|
|
142
|
-
code_explanation=excluded.code_explanation, practice_task=excluded.practice_task,
|
|
143
|
-
review_questions=excluded.review_questions, understanding=excluded.understanding,
|
|
144
|
-
next_review_at=excluded.next_review_at, review_count=excluded.review_count,
|
|
145
|
-
source_diff=excluded.source_diff, tags=excluded.tags, status=excluded.status,
|
|
146
|
-
superseded_by=excluded.superseded_by, supersedes=excluded.supersedes,
|
|
147
|
-
supersede_reason=excluded.supersede_reason
|
|
123
|
+
db.prepare(`
|
|
124
|
+
INSERT INTO lessons (
|
|
125
|
+
id, created_at, updated_at, tool, project_path, title, original_prompt,
|
|
126
|
+
problem, mistake, root_cause, fix_summary, takeaway, mistake_pattern,
|
|
127
|
+
when_not_applicable, concepts, files_changed, code_example, bad_code_example,
|
|
128
|
+
good_code_example, code_explanation, practice_task, review_questions,
|
|
129
|
+
understanding, next_review_at, review_count, source_diff, tags,
|
|
130
|
+
status, superseded_by, supersedes, supersede_reason
|
|
131
|
+
) VALUES (
|
|
132
|
+
?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?
|
|
133
|
+
)
|
|
134
|
+
ON CONFLICT(id) DO UPDATE SET
|
|
135
|
+
created_at=excluded.created_at, updated_at=excluded.updated_at, tool=excluded.tool,
|
|
136
|
+
project_path=excluded.project_path, title=excluded.title, original_prompt=excluded.original_prompt,
|
|
137
|
+
problem=excluded.problem, mistake=excluded.mistake, root_cause=excluded.root_cause,
|
|
138
|
+
fix_summary=excluded.fix_summary, takeaway=excluded.takeaway, mistake_pattern=excluded.mistake_pattern,
|
|
139
|
+
when_not_applicable=excluded.when_not_applicable, concepts=excluded.concepts,
|
|
140
|
+
files_changed=excluded.files_changed, code_example=excluded.code_example,
|
|
141
|
+
bad_code_example=excluded.bad_code_example, good_code_example=excluded.good_code_example,
|
|
142
|
+
code_explanation=excluded.code_explanation, practice_task=excluded.practice_task,
|
|
143
|
+
review_questions=excluded.review_questions, understanding=excluded.understanding,
|
|
144
|
+
next_review_at=excluded.next_review_at, review_count=excluded.review_count,
|
|
145
|
+
source_diff=excluded.source_diff, tags=excluded.tags, status=excluded.status,
|
|
146
|
+
superseded_by=excluded.superseded_by, supersedes=excluded.supersedes,
|
|
147
|
+
supersede_reason=excluded.supersede_reason
|
|
148
148
|
`).run(lesson.id, lesson.createdAt, lesson.updatedAt, lesson.tool, lesson.projectPath, lesson.title, lesson.originalPrompt, lesson.problem, lesson.mistake, lesson.rootCause, lesson.fixSummary, lesson.takeaway ?? null, lesson.mistakePattern ?? null, lesson.whenNotApplicable ?? null, JSON.stringify(lesson.concepts), JSON.stringify(lesson.filesChanged), lesson.codeExample ?? null, lesson.badCodeExample ?? null, lesson.goodCodeExample ?? null, lesson.codeExplanation ?? null, lesson.practiceTask ?? null, JSON.stringify(lesson.reviewQuestions), lesson.understanding, lesson.nextReviewAt, lesson.reviewCount, lesson.sourceDiff ?? null, JSON.stringify(lesson.tags), lesson.status, lesson.supersededBy ?? null, lesson.supersedes ?? null, lesson.supersedeReason ?? null);
|
|
149
149
|
},
|
|
150
150
|
search(query, options = {}) {
|
package/dist/src/sync.js
CHANGED
|
@@ -46,67 +46,67 @@ function escapeHtml(value) {
|
|
|
46
46
|
}
|
|
47
47
|
function oauthCallbackPage(options) {
|
|
48
48
|
const tint = options.ok ? "124,92,255" : "255,92,114";
|
|
49
|
-
return `<!doctype html>
|
|
50
|
-
<html lang="en">
|
|
51
|
-
<head>
|
|
52
|
-
<meta charset="utf-8">
|
|
53
|
-
<title>Fixmind</title>
|
|
54
|
-
<style>
|
|
55
|
-
:root { color-scheme: dark; }
|
|
56
|
-
body {
|
|
57
|
-
margin: 0; min-height: 100vh; display: flex; align-items: center; justify-content: center;
|
|
58
|
-
position: relative; overflow: hidden;
|
|
59
|
-
background-color: #08090d; color: #e9ecf4;
|
|
60
|
-
font-family: "Inter", -apple-system, BlinkMacSystemFont, sans-serif;
|
|
61
|
-
}
|
|
62
|
-
body::before {
|
|
63
|
-
content: ""; position: absolute; inset: 0; pointer-events: none;
|
|
64
|
-
background-image:
|
|
65
|
-
linear-gradient(to right, rgba(35,40,56,0.6) 1px, transparent 1px),
|
|
66
|
-
linear-gradient(to bottom, rgba(35,40,56,0.6) 1px, transparent 1px);
|
|
67
|
-
background-size: 48px 48px;
|
|
68
|
-
mask-image: radial-gradient(circle at 50% 35%, black, transparent 75%);
|
|
69
|
-
}
|
|
70
|
-
body::after {
|
|
71
|
-
content: ""; position: absolute; left: 50%; top: 0; width: 760px; height: 420px;
|
|
72
|
-
transform: translateX(-50%); pointer-events: none; border-radius: 9999px;
|
|
73
|
-
background: rgba(${tint},0.15); filter: blur(130px);
|
|
74
|
-
}
|
|
75
|
-
.card {
|
|
76
|
-
position: relative; z-index: 1; text-align: center; padding: 2.5rem 3rem; border-radius: 16px;
|
|
77
|
-
border: 1px solid #232838; background: rgba(17,20,27,0.92);
|
|
78
|
-
}
|
|
79
|
-
.icon {
|
|
80
|
-
width: 48px; height: 48px; margin: 0 auto 1.25rem; border-radius: 50%;
|
|
81
|
-
display: flex; align-items: center; justify-content: center; font-size: 22px;
|
|
82
|
-
background: rgba(${tint},0.15);
|
|
83
|
-
color: ${options.ok ? "#7c5cff" : "#ff5c72"};
|
|
84
|
-
}
|
|
85
|
-
h1 {
|
|
86
|
-
font-family: "Space Grotesk", "Inter", sans-serif; font-size: 1.25rem; font-weight: 600;
|
|
87
|
-
margin: 0 0 0.5rem;
|
|
88
|
-
}
|
|
89
|
-
p { margin: 0; color: #828a9c; font-size: 0.95rem; line-height: 1.5; }
|
|
90
|
-
.redirect {
|
|
91
|
-
display: inline-block; margin-top: 1.5rem; padding: 0.6rem 1.5rem; border-radius: 8px;
|
|
92
|
-
background: #7c5cff; color: #08090d; font-weight: 600; font-size: 0.9rem;
|
|
93
|
-
text-decoration: none;
|
|
94
|
-
}
|
|
95
|
-
.brand {
|
|
96
|
-
margin-top: 2rem; font-size: 0.75rem; letter-spacing: 0.1em; text-transform: uppercase;
|
|
97
|
-
color: #4b3aae;
|
|
98
|
-
}
|
|
99
|
-
</style>
|
|
100
|
-
</head>
|
|
101
|
-
<body>
|
|
102
|
-
<div class="card">
|
|
103
|
-
<div class="icon">${options.ok ? "✓" : "!"}</div>
|
|
104
|
-
<h1>${options.ok ? "You're signed in" : "Sign in failed"}</h1>
|
|
105
|
-
<p>${escapeHtml(options.message)}</p>
|
|
106
|
-
${options.redirectUrl ? `<a class="redirect" href="${escapeHtml(options.redirectUrl)}">Try again</a>` : ""}
|
|
107
|
-
<div class="brand">Fixmind</div>
|
|
108
|
-
</div>
|
|
109
|
-
</body>
|
|
49
|
+
return `<!doctype html>
|
|
50
|
+
<html lang="en">
|
|
51
|
+
<head>
|
|
52
|
+
<meta charset="utf-8">
|
|
53
|
+
<title>Fixmind</title>
|
|
54
|
+
<style>
|
|
55
|
+
:root { color-scheme: dark; }
|
|
56
|
+
body {
|
|
57
|
+
margin: 0; min-height: 100vh; display: flex; align-items: center; justify-content: center;
|
|
58
|
+
position: relative; overflow: hidden;
|
|
59
|
+
background-color: #08090d; color: #e9ecf4;
|
|
60
|
+
font-family: "Inter", -apple-system, BlinkMacSystemFont, sans-serif;
|
|
61
|
+
}
|
|
62
|
+
body::before {
|
|
63
|
+
content: ""; position: absolute; inset: 0; pointer-events: none;
|
|
64
|
+
background-image:
|
|
65
|
+
linear-gradient(to right, rgba(35,40,56,0.6) 1px, transparent 1px),
|
|
66
|
+
linear-gradient(to bottom, rgba(35,40,56,0.6) 1px, transparent 1px);
|
|
67
|
+
background-size: 48px 48px;
|
|
68
|
+
mask-image: radial-gradient(circle at 50% 35%, black, transparent 75%);
|
|
69
|
+
}
|
|
70
|
+
body::after {
|
|
71
|
+
content: ""; position: absolute; left: 50%; top: 0; width: 760px; height: 420px;
|
|
72
|
+
transform: translateX(-50%); pointer-events: none; border-radius: 9999px;
|
|
73
|
+
background: rgba(${tint},0.15); filter: blur(130px);
|
|
74
|
+
}
|
|
75
|
+
.card {
|
|
76
|
+
position: relative; z-index: 1; text-align: center; padding: 2.5rem 3rem; border-radius: 16px;
|
|
77
|
+
border: 1px solid #232838; background: rgba(17,20,27,0.92);
|
|
78
|
+
}
|
|
79
|
+
.icon {
|
|
80
|
+
width: 48px; height: 48px; margin: 0 auto 1.25rem; border-radius: 50%;
|
|
81
|
+
display: flex; align-items: center; justify-content: center; font-size: 22px;
|
|
82
|
+
background: rgba(${tint},0.15);
|
|
83
|
+
color: ${options.ok ? "#7c5cff" : "#ff5c72"};
|
|
84
|
+
}
|
|
85
|
+
h1 {
|
|
86
|
+
font-family: "Space Grotesk", "Inter", sans-serif; font-size: 1.25rem; font-weight: 600;
|
|
87
|
+
margin: 0 0 0.5rem;
|
|
88
|
+
}
|
|
89
|
+
p { margin: 0; color: #828a9c; font-size: 0.95rem; line-height: 1.5; }
|
|
90
|
+
.redirect {
|
|
91
|
+
display: inline-block; margin-top: 1.5rem; padding: 0.6rem 1.5rem; border-radius: 8px;
|
|
92
|
+
background: #7c5cff; color: #08090d; font-weight: 600; font-size: 0.9rem;
|
|
93
|
+
text-decoration: none;
|
|
94
|
+
}
|
|
95
|
+
.brand {
|
|
96
|
+
margin-top: 2rem; font-size: 0.75rem; letter-spacing: 0.1em; text-transform: uppercase;
|
|
97
|
+
color: #4b3aae;
|
|
98
|
+
}
|
|
99
|
+
</style>
|
|
100
|
+
</head>
|
|
101
|
+
<body>
|
|
102
|
+
<div class="card">
|
|
103
|
+
<div class="icon">${options.ok ? "✓" : "!"}</div>
|
|
104
|
+
<h1>${options.ok ? "You're signed in" : "Sign in failed"}</h1>
|
|
105
|
+
<p>${escapeHtml(options.message)}</p>
|
|
106
|
+
${options.redirectUrl ? `<a class="redirect" href="${escapeHtml(options.redirectUrl)}">Try again</a>` : ""}
|
|
107
|
+
<div class="brand">Fixmind</div>
|
|
108
|
+
</div>
|
|
109
|
+
</body>
|
|
110
110
|
</html>`;
|
|
111
111
|
}
|
|
112
112
|
function waitForOAuthCode(port, authUrl) {
|
package/docs/mcp-integration.md
CHANGED
|
@@ -1,104 +1,112 @@
|
|
|
1
|
-
# MCP Integration
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
```powershell
|
|
20
|
-
|
|
21
|
-
```
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
```
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
##
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
"
|
|
82
|
-
"
|
|
83
|
-
"
|
|
84
|
-
"
|
|
85
|
-
"
|
|
86
|
-
"
|
|
87
|
-
"
|
|
88
|
-
"
|
|
89
|
-
"
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
"
|
|
96
|
-
"
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
1
|
+
# MCP Integration
|
|
2
|
+
|
|
3
|
+
## Supported clients
|
|
4
|
+
|
|
5
|
+
  
|
|
6
|
+
|
|
7
|
+
`fixmind setup` detects and configures all three. Any other MCP-compatible client works too — see [Other MCP clients](#other-mcp-clients).
|
|
8
|
+
|
|
9
|
+
Fixmind exposes a local stdio MCP server. Any MCP-compatible AI coding client can start it and call one tool:
|
|
10
|
+
|
|
11
|
+
`save_lesson`
|
|
12
|
+
|
|
13
|
+
The tool stores a lesson locally. It does not call an AI API, upload code, or expose lesson history to the agent. For what gets stored and how it's reviewed afterward, see [Lesson Schema](../../../docs/lesson-schema.md). For the `fixmind setup` flags used below, see [Commands](../../../docs/cli-reference.md#fixmind-setup).
|
|
14
|
+
|
|
15
|
+
## Guided setup
|
|
16
|
+
|
|
17
|
+
After installing the package globally, run:
|
|
18
|
+
|
|
19
|
+
```powershell
|
|
20
|
+
fixmind setup
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
The setup command detects Codex, Claude Code, and Cursor, asks which clients to configure, initializes local storage, and registers this stdio command:
|
|
24
|
+
|
|
25
|
+
```powershell
|
|
26
|
+
npx -y fixmind mcp
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
On Windows, clients receive the compatible equivalent using `cmd /c`.
|
|
30
|
+
|
|
31
|
+
Non-interactive examples:
|
|
32
|
+
|
|
33
|
+
```powershell
|
|
34
|
+
fixmind setup --client codex,claude,cursor
|
|
35
|
+
fixmind setup --client cursor --dry-run
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Setup is idempotent. Existing MCP entries are preserved. Before changing Cursor's JSON configuration, the previous file is copied to `mcp.json.backup`.
|
|
39
|
+
|
|
40
|
+
For Claude Code, setup additionally pre-approves the `mcp__fixmind__save_lesson` tool by adding it to `permissions.allow` in `~/.claude/settings.json`, so the agent isn't blocked by a runtime permission prompt when saving a lesson. Existing settings and other permission entries are preserved, and a `.backup` copy of `settings.json` is made before the first change.
|
|
41
|
+
|
|
42
|
+
## Other MCP clients
|
|
43
|
+
|
|
44
|
+
Run `fixmind setup` on a machine where no supported client is detected to print a generic MCP configuration. The equivalent configuration is:
|
|
45
|
+
|
|
46
|
+
```json
|
|
47
|
+
{
|
|
48
|
+
"mcpServers": {
|
|
49
|
+
"fixmind": {
|
|
50
|
+
"command": "npx",
|
|
51
|
+
"args": ["-y", "fixmind", "mcp"]
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## Agent behavior
|
|
58
|
+
|
|
59
|
+
The MCP server sends instructions asking the agent to create a lesson after a meaningful coding fix and skip formatting-only, rename-only, generated-file, and mechanical changes.
|
|
60
|
+
|
|
61
|
+
Automatic use is best-effort because each MCP client decides when to call available tools. The human can explicitly say "save a learning lesson after the fix" if a client does not consistently follow server instructions.
|
|
62
|
+
|
|
63
|
+
## Token and context overhead
|
|
64
|
+
|
|
65
|
+
Connecting the fixmind MCP server adds a small, mostly one-time cost to an agent's context:
|
|
66
|
+
|
|
67
|
+
- The server's instructions and the `save_lesson` tool schema are sent once when the client connects. Clients that support prompt caching reuse this across subsequent turns in the same session.
|
|
68
|
+
- `save_lesson` is called only when the agent determines a meaningful fix occurred, per the checklist in its instructions — not on every turn. Sessions with no qualifying fixes add nothing beyond the initial connection cost.
|
|
69
|
+
- When a lesson is saved, the generated payload (problem, root cause, fix summary, takeaway, code examples, review questions) is comparable in size to a short commit message or code review comment.
|
|
70
|
+
|
|
71
|
+
In practice, this overhead is negligible relative to the tokens used by the coding work itself.
|
|
72
|
+
|
|
73
|
+
If a client isn't consistently calling `save_lesson`, or you want to know why a save was rejected, see [FAQ & Troubleshooting](../../../docs/faq.md).
|
|
74
|
+
|
|
75
|
+
## Tool input
|
|
76
|
+
|
|
77
|
+
The MCP tool accepts:
|
|
78
|
+
|
|
79
|
+
```json
|
|
80
|
+
{
|
|
81
|
+
"projectPath": "/path/to/project",
|
|
82
|
+
"title": "Short learning-oriented title",
|
|
83
|
+
"originalPrompt": "The user's original request",
|
|
84
|
+
"problem": "What behavior was broken",
|
|
85
|
+
"mistake": "The specific mistake",
|
|
86
|
+
"rootCause": "Why the mistake caused the behavior",
|
|
87
|
+
"fixSummary": "What changed and why it works",
|
|
88
|
+
"takeaway": "One plain sentence the developer should remember",
|
|
89
|
+
"mistakePattern": "A short reusable category",
|
|
90
|
+
"concepts": ["Reusable concept"],
|
|
91
|
+
"filesChanged": ["src/example.ts"],
|
|
92
|
+
"codeExample": "A small focused example",
|
|
93
|
+
"badCodeExample": "A minimal example showing the mistake",
|
|
94
|
+
"goodCodeExample": "A minimal corrected example",
|
|
95
|
+
"codeExplanation": "The important difference between the two examples",
|
|
96
|
+
"practiceTask": "A small exercise to apply the concept",
|
|
97
|
+
"reviewQuestions": [
|
|
98
|
+
{
|
|
99
|
+
"question": "A recall question",
|
|
100
|
+
"expectedAnswer": "A concise expected answer"
|
|
101
|
+
}
|
|
102
|
+
],
|
|
103
|
+
"understanding": "unknown",
|
|
104
|
+
"tags": [
|
|
105
|
+
{ "name": "MDN: fetch()", "url": "https://developer.mozilla.org/en-US/docs/Web/API/Window/fetch" }
|
|
106
|
+
]
|
|
107
|
+
}
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
`tool` is optional and normally omitted: fixmind reads it from the connected MCP client's `clientInfo.name` during the initialize handshake. Each `tags` entry is `{ name, url? }`; only set `url` when it points to real, official documentation (MDN, the framework's docs, etc.) — otherwise omit it and the tag is shown as a plain label.
|
|
111
|
+
|
|
112
|
+
If Git is available at `projectPath`, omitted changed files and source diff are collected from the current working tree.
|
package/package.json
CHANGED
|
@@ -1,45 +1,45 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "fixmind",
|
|
3
|
-
"version": "1.0.
|
|
4
|
-
"description": "Local-first learning lessons for developers working with AI coding agents.",
|
|
5
|
-
"type": "module",
|
|
6
|
-
"repository": {
|
|
7
|
-
"type": "git",
|
|
8
|
-
"url": "git+https://github.com/F-47/fixmind.git",
|
|
9
|
-
"directory": "packages/core"
|
|
10
|
-
},
|
|
11
|
-
"homepage": "https://github.com/F-47/fixmind#readme",
|
|
12
|
-
"bin": {
|
|
13
|
-
"fixmind": "dist/src/
|
|
14
|
-
},
|
|
15
|
-
"files": [
|
|
16
|
-
"dist/src",
|
|
17
|
-
"dist/dashboard",
|
|
18
|
-
"README.md",
|
|
19
|
-
"docs"
|
|
20
|
-
],
|
|
21
|
-
"scripts": {
|
|
22
|
-
"clean": "node -e \"require('fs').rmSync('dist',{recursive:true,force:true})\"",
|
|
23
|
-
"build": "npm run clean && npm run build:server && npm run copy:dashboard && npm run chmod:bin",
|
|
24
|
-
"build:server": "tsc -p tsconfig.json",
|
|
25
|
-
"chmod:bin": "node -e \"require('fs').chmodSync('dist/src/cli.js', 0o755)\"",
|
|
26
|
-
"copy:dashboard": "node -e \"require('fs').cpSync('../dashboard/dist','dist/dashboard',{recursive:true})\"",
|
|
27
|
-
"test": "npm run build && node --no-warnings --test dist/test/*.test.js",
|
|
28
|
-
"prepack": "npm run build",
|
|
29
|
-
"fixmind": "
|
|
30
|
-
},
|
|
31
|
-
"dependencies": {
|
|
32
|
-
"@clack/prompts": "^1.5.1",
|
|
33
|
-
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
34
|
-
"@supabase/supabase-js": "^2.45.4",
|
|
35
|
-
"zod": "^4.4.3"
|
|
36
|
-
},
|
|
37
|
-
"engines": {
|
|
38
|
-
"node": ">=22.5.0"
|
|
39
|
-
},
|
|
40
|
-
"devDependencies": {
|
|
41
|
-
"@types/node": "^22.15.0",
|
|
42
|
-
"typescript": "^5.8.3"
|
|
43
|
-
},
|
|
44
|
-
"license": "MIT"
|
|
45
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "fixmind",
|
|
3
|
+
"version": "1.0.4",
|
|
4
|
+
"description": "Local-first learning lessons for developers working with AI coding agents.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "git+https://github.com/F-47/fixmind.git",
|
|
9
|
+
"directory": "packages/core"
|
|
10
|
+
},
|
|
11
|
+
"homepage": "https://github.com/F-47/fixmind#readme",
|
|
12
|
+
"bin": {
|
|
13
|
+
"fixmind": "dist/src/launcher.js"
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"dist/src",
|
|
17
|
+
"dist/dashboard",
|
|
18
|
+
"README.md",
|
|
19
|
+
"docs"
|
|
20
|
+
],
|
|
21
|
+
"scripts": {
|
|
22
|
+
"clean": "node -e \"require('fs').rmSync('dist',{recursive:true,force:true})\"",
|
|
23
|
+
"build": "npm run clean && npm run build:server && npm run copy:dashboard && npm run chmod:bin",
|
|
24
|
+
"build:server": "tsc -p tsconfig.json",
|
|
25
|
+
"chmod:bin": "node -e \"require('fs').chmodSync('dist/src/cli.js', 0o755); require('fs').chmodSync('dist/src/launcher.js', 0o755)\"",
|
|
26
|
+
"copy:dashboard": "node -e \"require('fs').cpSync('../dashboard/dist','dist/dashboard',{recursive:true})\"",
|
|
27
|
+
"test": "npm run build && node --no-warnings --test dist/test/*.test.js",
|
|
28
|
+
"prepack": "npm run build",
|
|
29
|
+
"fixmind": "node dist/src/launcher.js"
|
|
30
|
+
},
|
|
31
|
+
"dependencies": {
|
|
32
|
+
"@clack/prompts": "^1.5.1",
|
|
33
|
+
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
34
|
+
"@supabase/supabase-js": "^2.45.4",
|
|
35
|
+
"zod": "^4.4.3"
|
|
36
|
+
},
|
|
37
|
+
"engines": {
|
|
38
|
+
"node": ">=22.5.0"
|
|
39
|
+
},
|
|
40
|
+
"devDependencies": {
|
|
41
|
+
"@types/node": "^22.15.0",
|
|
42
|
+
"typescript": "^5.8.3"
|
|
43
|
+
},
|
|
44
|
+
"license": "MIT"
|
|
45
|
+
}
|