@velvetmonkey/flywheel-crank 0.1.0 → 0.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 +57 -16
- package/dist/index.js +46 -47
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -14,7 +14,7 @@ While **Flywheel** provides read-only graph intelligence (backlinks, queries, st
|
|
|
14
14
|
- Toggle and create tasks
|
|
15
15
|
- Update frontmatter
|
|
16
16
|
- Create and delete notes
|
|
17
|
-
-
|
|
17
|
+
- Optional git commits with undo support
|
|
18
18
|
|
|
19
19
|
## Installation
|
|
20
20
|
|
|
@@ -27,10 +27,7 @@ Add to your project's `.mcp.json` (in your vault root). **Zero-config** if `.mcp
|
|
|
27
27
|
"mcpServers": {
|
|
28
28
|
"flywheel-crank": {
|
|
29
29
|
"command": "npx",
|
|
30
|
-
"args": ["-y", "@velvetmonkey/flywheel-crank"]
|
|
31
|
-
"env": {
|
|
32
|
-
"AUTO_COMMIT": "true"
|
|
33
|
-
}
|
|
30
|
+
"args": ["-y", "@velvetmonkey/flywheel-crank"]
|
|
34
31
|
}
|
|
35
32
|
}
|
|
36
33
|
}
|
|
@@ -52,8 +49,7 @@ If `.mcp.json` is NOT in your vault, set `PROJECT_PATH`:
|
|
|
52
49
|
"command": "npx",
|
|
53
50
|
"args": ["-y", "@velvetmonkey/flywheel-crank"],
|
|
54
51
|
"env": {
|
|
55
|
-
"PROJECT_PATH": "/path/to/your/vault"
|
|
56
|
-
"AUTO_COMMIT": "true"
|
|
52
|
+
"PROJECT_PATH": "/path/to/your/vault"
|
|
57
53
|
}
|
|
58
54
|
}
|
|
59
55
|
}
|
|
@@ -69,8 +65,7 @@ If `.mcp.json` is NOT in your vault, set `PROJECT_PATH`:
|
|
|
69
65
|
"command": "cmd",
|
|
70
66
|
"args": ["/c", "npx", "-y", "@velvetmonkey/flywheel-crank"],
|
|
71
67
|
"env": {
|
|
72
|
-
"PROJECT_PATH": "C:/Users/yourname/path/to/vault"
|
|
73
|
-
"AUTO_COMMIT": "true"
|
|
68
|
+
"PROJECT_PATH": "C:/Users/yourname/path/to/vault"
|
|
74
69
|
}
|
|
75
70
|
}
|
|
76
71
|
}
|
|
@@ -83,10 +78,10 @@ If `.mcp.json` is NOT in your vault, set `PROJECT_PATH`:
|
|
|
83
78
|
|
|
84
79
|
```bash
|
|
85
80
|
# Zero-config (run from vault directory)
|
|
86
|
-
claude mcp add flywheel-crank
|
|
81
|
+
claude mcp add flywheel-crank -- npx -y @velvetmonkey/flywheel-crank
|
|
87
82
|
|
|
88
83
|
# With explicit vault path
|
|
89
|
-
claude mcp add flywheel-crank -e PROJECT_PATH=/path/to/vault
|
|
84
|
+
claude mcp add flywheel-crank -e PROJECT_PATH=/path/to/vault -- npx -y @velvetmonkey/flywheel-crank
|
|
90
85
|
```
|
|
91
86
|
|
|
92
87
|
### Verify
|
|
@@ -105,18 +100,65 @@ claude mcp list # Should show: flywheel-crank
|
|
|
105
100
|
| Notes | `vault_create_note`, `vault_delete_note` |
|
|
106
101
|
| System | `vault_list_sections`, `vault_undo_last_mutation` |
|
|
107
102
|
|
|
103
|
+
## The `commit` Parameter (Undo Support)
|
|
104
|
+
|
|
105
|
+
Every mutation tool has an optional `commit` parameter. Here's what it does and why you'd use it:
|
|
106
|
+
|
|
107
|
+
### What is a commit?
|
|
108
|
+
|
|
109
|
+
If your vault is tracked with **git** (a version control system), setting `commit: true` creates a **save point** after each change. Think of it like a checkpoint in a video game - you can always go back to it.
|
|
110
|
+
|
|
111
|
+
### Why use it?
|
|
112
|
+
|
|
113
|
+
| `commit: false` (default) | `commit: true` |
|
|
114
|
+
|---------------------------|----------------|
|
|
115
|
+
| Changes saved to file immediately | Changes saved AND recorded in git history |
|
|
116
|
+
| No undo available | Can undo with `vault_undo_last_mutation` |
|
|
117
|
+
| Faster (no git overhead) | Slightly slower |
|
|
118
|
+
| Good for: bulk edits, testing | Good for: important changes you might want to reverse |
|
|
119
|
+
|
|
120
|
+
### Example
|
|
121
|
+
|
|
122
|
+
```javascript
|
|
123
|
+
// Without commit - change is made, no undo available
|
|
124
|
+
vault_add_to_section({
|
|
125
|
+
path: "daily/2026-01-28.md",
|
|
126
|
+
section: "Log",
|
|
127
|
+
content: "Meeting with team"
|
|
128
|
+
})
|
|
129
|
+
|
|
130
|
+
// With commit - change is made AND you can undo it later
|
|
131
|
+
vault_add_to_section({
|
|
132
|
+
path: "daily/2026-01-28.md",
|
|
133
|
+
section: "Log",
|
|
134
|
+
content: "Meeting with team",
|
|
135
|
+
commit: true // Creates undo point
|
|
136
|
+
})
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
### Undoing a change
|
|
140
|
+
|
|
141
|
+
If you used `commit: true`, you can undo with:
|
|
142
|
+
|
|
143
|
+
```javascript
|
|
144
|
+
vault_undo_last_mutation({ confirm: true })
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
This reverts your vault to the state before the last committed change.
|
|
148
|
+
|
|
149
|
+
> **Note**: Undo only works if your vault is a git repository. Most Obsidian users who sync via git already have this set up. If you're unsure, the tool will tell you if undo isn't available.
|
|
150
|
+
|
|
108
151
|
## Configuration
|
|
109
152
|
|
|
110
153
|
| Environment Variable | Required | Default | Description |
|
|
111
154
|
|---------------------|:--------:|---------|-------------|
|
|
112
155
|
| `PROJECT_PATH` | No | `cwd()` | Path to markdown vault directory |
|
|
113
|
-
| `AUTO_COMMIT` | No | `false` | Auto-commit mutations to git |
|
|
114
156
|
|
|
115
157
|
## Design Principles
|
|
116
158
|
|
|
117
159
|
- **Deterministic**: No AI-driven edits, predictable output
|
|
118
160
|
- **Atomic**: Each mutation is a single, reversible operation
|
|
119
|
-
- **Safe**: Path sandboxing,
|
|
161
|
+
- **Safe**: Path sandboxing, explicit commit opt-in, undo support
|
|
120
162
|
- **Obsidian-compatible**: Follows Obsidian conventions (task format, wikilinks, etc.)
|
|
121
163
|
|
|
122
164
|
## Flywheel + Crank
|
|
@@ -132,8 +174,7 @@ Use both together for full vault intelligence:
|
|
|
132
174
|
},
|
|
133
175
|
"flywheel-crank": {
|
|
134
176
|
"command": "npx",
|
|
135
|
-
"args": ["-y", "@velvetmonkey/flywheel-crank"]
|
|
136
|
-
"env": { "AUTO_COMMIT": "true" }
|
|
177
|
+
"args": ["-y", "@velvetmonkey/flywheel-crank"]
|
|
137
178
|
}
|
|
138
179
|
}
|
|
139
180
|
}
|
|
@@ -144,7 +185,7 @@ Use both together for full vault intelligence:
|
|
|
144
185
|
|
|
145
186
|
## License
|
|
146
187
|
|
|
147
|
-
AGPL-3.0 — [
|
|
188
|
+
AGPL-3.0 — [velvetmonkey](https://github.com/velvetmonkey)
|
|
148
189
|
|
|
149
190
|
## Links
|
|
150
191
|
|
package/dist/index.js
CHANGED
|
@@ -308,7 +308,7 @@ async function undoLastCommit(vaultPath2) {
|
|
|
308
308
|
// src/tools/mutations.ts
|
|
309
309
|
import fs2 from "fs/promises";
|
|
310
310
|
import path3 from "path";
|
|
311
|
-
function registerMutationTools(server2, vaultPath2
|
|
311
|
+
function registerMutationTools(server2, vaultPath2) {
|
|
312
312
|
server2.tool(
|
|
313
313
|
"vault_add_to_section",
|
|
314
314
|
"Add content to a specific section in a markdown note",
|
|
@@ -317,9 +317,10 @@ function registerMutationTools(server2, vaultPath2, autoCommit2) {
|
|
|
317
317
|
section: z.string().describe('Heading text to add to (e.g., "Log" or "## Log")'),
|
|
318
318
|
content: z.string().describe("Content to add to the section"),
|
|
319
319
|
position: z.enum(["append", "prepend"]).default("append").describe("Where to insert content"),
|
|
320
|
-
format: z.enum(["plain", "bullet", "task", "numbered", "timestamp-bullet"]).default("plain").describe("How to format the content")
|
|
320
|
+
format: z.enum(["plain", "bullet", "task", "numbered", "timestamp-bullet"]).default("plain").describe("How to format the content"),
|
|
321
|
+
commit: z.boolean().default(false).describe("If true, commit this change to git (creates undo point)")
|
|
321
322
|
},
|
|
322
|
-
async ({ path: notePath, section, content, position, format }) => {
|
|
323
|
+
async ({ path: notePath, section, content, position, format, commit }) => {
|
|
323
324
|
try {
|
|
324
325
|
const fullPath = path3.join(vaultPath2, notePath);
|
|
325
326
|
try {
|
|
@@ -352,7 +353,7 @@ function registerMutationTools(server2, vaultPath2, autoCommit2) {
|
|
|
352
353
|
await writeVaultFile(vaultPath2, notePath, updatedContent, frontmatter);
|
|
353
354
|
let gitCommit;
|
|
354
355
|
let gitError;
|
|
355
|
-
if (
|
|
356
|
+
if (commit) {
|
|
356
357
|
const gitResult = await commitChange(vaultPath2, notePath, "[Crank:Add]");
|
|
357
358
|
if (gitResult.success) {
|
|
358
359
|
gitCommit = gitResult.hash;
|
|
@@ -388,9 +389,10 @@ function registerMutationTools(server2, vaultPath2, autoCommit2) {
|
|
|
388
389
|
section: z.string().describe('Heading text to remove from (e.g., "Log" or "## Log")'),
|
|
389
390
|
pattern: z.string().describe("Text or pattern to match for removal"),
|
|
390
391
|
mode: z.enum(["first", "last", "all"]).default("first").describe("Which matches to remove"),
|
|
391
|
-
useRegex: z.boolean().default(false).describe("Treat pattern as regex")
|
|
392
|
+
useRegex: z.boolean().default(false).describe("Treat pattern as regex"),
|
|
393
|
+
commit: z.boolean().default(false).describe("If true, commit this change to git (creates undo point)")
|
|
392
394
|
},
|
|
393
|
-
async ({ path: notePath, section, pattern, mode, useRegex }) => {
|
|
395
|
+
async ({ path: notePath, section, pattern, mode, useRegex, commit }) => {
|
|
394
396
|
try {
|
|
395
397
|
const fullPath = path3.join(vaultPath2, notePath);
|
|
396
398
|
try {
|
|
@@ -431,7 +433,7 @@ function registerMutationTools(server2, vaultPath2, autoCommit2) {
|
|
|
431
433
|
await writeVaultFile(vaultPath2, notePath, removeResult.content, frontmatter);
|
|
432
434
|
let gitCommit;
|
|
433
435
|
let gitError;
|
|
434
|
-
if (
|
|
436
|
+
if (commit) {
|
|
435
437
|
const gitResult = await commitChange(vaultPath2, notePath, "[Crank:Remove]");
|
|
436
438
|
if (gitResult.success) {
|
|
437
439
|
gitCommit = gitResult.hash;
|
|
@@ -467,9 +469,10 @@ function registerMutationTools(server2, vaultPath2, autoCommit2) {
|
|
|
467
469
|
search: z.string().describe("Text or pattern to search for"),
|
|
468
470
|
replacement: z.string().describe("Text to replace with"),
|
|
469
471
|
mode: z.enum(["first", "last", "all"]).default("first").describe("Which matches to replace"),
|
|
470
|
-
useRegex: z.boolean().default(false).describe("Treat search as regex")
|
|
472
|
+
useRegex: z.boolean().default(false).describe("Treat search as regex"),
|
|
473
|
+
commit: z.boolean().default(false).describe("If true, commit this change to git (creates undo point)")
|
|
471
474
|
},
|
|
472
|
-
async ({ path: notePath, section, search, replacement, mode, useRegex }) => {
|
|
475
|
+
async ({ path: notePath, section, search, replacement, mode, useRegex, commit }) => {
|
|
473
476
|
try {
|
|
474
477
|
const fullPath = path3.join(vaultPath2, notePath);
|
|
475
478
|
try {
|
|
@@ -511,7 +514,7 @@ function registerMutationTools(server2, vaultPath2, autoCommit2) {
|
|
|
511
514
|
await writeVaultFile(vaultPath2, notePath, replaceResult.content, frontmatter);
|
|
512
515
|
let gitCommit;
|
|
513
516
|
let gitError;
|
|
514
|
-
if (
|
|
517
|
+
if (commit) {
|
|
515
518
|
const gitResult = await commitChange(vaultPath2, notePath, "[Crank:Replace]");
|
|
516
519
|
if (gitResult.success) {
|
|
517
520
|
gitCommit = gitResult.hash;
|
|
@@ -592,16 +595,17 @@ function toggleTask(content, lineNumber) {
|
|
|
592
595
|
newState
|
|
593
596
|
};
|
|
594
597
|
}
|
|
595
|
-
function registerTaskTools(server2, vaultPath2
|
|
598
|
+
function registerTaskTools(server2, vaultPath2) {
|
|
596
599
|
server2.tool(
|
|
597
600
|
"vault_toggle_task",
|
|
598
601
|
"Toggle a task checkbox between checked and unchecked",
|
|
599
602
|
{
|
|
600
603
|
path: z2.string().describe("Vault-relative path to the note"),
|
|
601
604
|
task: z2.string().describe("Task text to find (partial match supported)"),
|
|
602
|
-
section: z2.string().optional().describe("Optional: limit search to this section")
|
|
605
|
+
section: z2.string().optional().describe("Optional: limit search to this section"),
|
|
606
|
+
commit: z2.boolean().default(false).describe("If true, commit this change to git (creates undo point)")
|
|
603
607
|
},
|
|
604
|
-
async ({ path: notePath, task, section }) => {
|
|
608
|
+
async ({ path: notePath, task, section, commit }) => {
|
|
605
609
|
try {
|
|
606
610
|
const fullPath = path4.join(vaultPath2, notePath);
|
|
607
611
|
try {
|
|
@@ -653,7 +657,7 @@ function registerTaskTools(server2, vaultPath2, autoCommit2) {
|
|
|
653
657
|
await writeVaultFile(vaultPath2, notePath, toggleResult.content, frontmatter);
|
|
654
658
|
let gitCommit;
|
|
655
659
|
let gitError;
|
|
656
|
-
if (
|
|
660
|
+
if (commit) {
|
|
657
661
|
const gitResult = await commitChange(vaultPath2, notePath, "[Crank:Task]");
|
|
658
662
|
if (gitResult.success) {
|
|
659
663
|
gitCommit = gitResult.hash;
|
|
@@ -690,9 +694,10 @@ function registerTaskTools(server2, vaultPath2, autoCommit2) {
|
|
|
690
694
|
section: z2.string().describe("Section to add the task to"),
|
|
691
695
|
task: z2.string().describe("Task text (without checkbox)"),
|
|
692
696
|
position: z2.enum(["append", "prepend"]).default("append").describe("Where to add the task"),
|
|
693
|
-
completed: z2.boolean().default(false).describe("Whether the task should start as completed")
|
|
697
|
+
completed: z2.boolean().default(false).describe("Whether the task should start as completed"),
|
|
698
|
+
commit: z2.boolean().default(false).describe("If true, commit this change to git (creates undo point)")
|
|
694
699
|
},
|
|
695
|
-
async ({ path: notePath, section, task, position, completed }) => {
|
|
700
|
+
async ({ path: notePath, section, task, position, completed, commit }) => {
|
|
696
701
|
try {
|
|
697
702
|
const fullPath = path4.join(vaultPath2, notePath);
|
|
698
703
|
try {
|
|
@@ -726,7 +731,7 @@ function registerTaskTools(server2, vaultPath2, autoCommit2) {
|
|
|
726
731
|
await writeVaultFile(vaultPath2, notePath, updatedContent, frontmatter);
|
|
727
732
|
let gitCommit;
|
|
728
733
|
let gitError;
|
|
729
|
-
if (
|
|
734
|
+
if (commit) {
|
|
730
735
|
const gitResult = await commitChange(vaultPath2, notePath, "[Crank:Task]");
|
|
731
736
|
if (gitResult.success) {
|
|
732
737
|
gitCommit = gitResult.hash;
|
|
@@ -760,15 +765,16 @@ function registerTaskTools(server2, vaultPath2, autoCommit2) {
|
|
|
760
765
|
import { z as z3 } from "zod";
|
|
761
766
|
import fs4 from "fs/promises";
|
|
762
767
|
import path5 from "path";
|
|
763
|
-
function registerFrontmatterTools(server2, vaultPath2
|
|
768
|
+
function registerFrontmatterTools(server2, vaultPath2) {
|
|
764
769
|
server2.tool(
|
|
765
770
|
"vault_update_frontmatter",
|
|
766
771
|
"Update frontmatter fields in a note (merge with existing frontmatter)",
|
|
767
772
|
{
|
|
768
773
|
path: z3.string().describe("Vault-relative path to the note"),
|
|
769
|
-
frontmatter: z3.record(z3.any()).describe("Frontmatter fields to update (JSON object)")
|
|
774
|
+
frontmatter: z3.record(z3.any()).describe("Frontmatter fields to update (JSON object)"),
|
|
775
|
+
commit: z3.boolean().default(false).describe("If true, commit this change to git (creates undo point)")
|
|
770
776
|
},
|
|
771
|
-
async ({ path: notePath, frontmatter: updates }) => {
|
|
777
|
+
async ({ path: notePath, frontmatter: updates, commit }) => {
|
|
772
778
|
try {
|
|
773
779
|
const fullPath = path5.join(vaultPath2, notePath);
|
|
774
780
|
try {
|
|
@@ -786,7 +792,7 @@ function registerFrontmatterTools(server2, vaultPath2, autoCommit2) {
|
|
|
786
792
|
await writeVaultFile(vaultPath2, notePath, content, updatedFrontmatter);
|
|
787
793
|
let gitCommit;
|
|
788
794
|
let gitError;
|
|
789
|
-
if (
|
|
795
|
+
if (commit) {
|
|
790
796
|
const gitResult = await commitChange(vaultPath2, notePath, "[Crank:FM]");
|
|
791
797
|
if (gitResult.success) {
|
|
792
798
|
gitCommit = gitResult.hash;
|
|
@@ -821,9 +827,10 @@ function registerFrontmatterTools(server2, vaultPath2, autoCommit2) {
|
|
|
821
827
|
{
|
|
822
828
|
path: z3.string().describe("Vault-relative path to the note"),
|
|
823
829
|
key: z3.string().describe("Field name to add"),
|
|
824
|
-
value: z3.any().describe("Field value (string, number, boolean, array, object)")
|
|
830
|
+
value: z3.any().describe("Field value (string, number, boolean, array, object)"),
|
|
831
|
+
commit: z3.boolean().default(false).describe("If true, commit this change to git (creates undo point)")
|
|
825
832
|
},
|
|
826
|
-
async ({ path: notePath, key, value }) => {
|
|
833
|
+
async ({ path: notePath, key, value, commit }) => {
|
|
827
834
|
try {
|
|
828
835
|
const fullPath = path5.join(vaultPath2, notePath);
|
|
829
836
|
try {
|
|
@@ -849,7 +856,7 @@ function registerFrontmatterTools(server2, vaultPath2, autoCommit2) {
|
|
|
849
856
|
await writeVaultFile(vaultPath2, notePath, content, updatedFrontmatter);
|
|
850
857
|
let gitCommit;
|
|
851
858
|
let gitError;
|
|
852
|
-
if (
|
|
859
|
+
if (commit) {
|
|
853
860
|
const gitResult = await commitChange(vaultPath2, notePath, "[Crank:FM]");
|
|
854
861
|
if (gitResult.success) {
|
|
855
862
|
gitCommit = gitResult.hash;
|
|
@@ -883,7 +890,7 @@ function registerFrontmatterTools(server2, vaultPath2, autoCommit2) {
|
|
|
883
890
|
import { z as z4 } from "zod";
|
|
884
891
|
import fs5 from "fs/promises";
|
|
885
892
|
import path6 from "path";
|
|
886
|
-
function registerNoteTools(server2, vaultPath2
|
|
893
|
+
function registerNoteTools(server2, vaultPath2) {
|
|
887
894
|
server2.tool(
|
|
888
895
|
"vault_create_note",
|
|
889
896
|
"Create a new note in the vault with optional frontmatter and content",
|
|
@@ -891,9 +898,10 @@ function registerNoteTools(server2, vaultPath2, autoCommit2) {
|
|
|
891
898
|
path: z4.string().describe('Vault-relative path for the new note (e.g., "daily-notes/2026-01-28.md")'),
|
|
892
899
|
content: z4.string().default("").describe("Initial content for the note"),
|
|
893
900
|
frontmatter: z4.record(z4.any()).default({}).describe("Frontmatter fields (JSON object)"),
|
|
894
|
-
overwrite: z4.boolean().default(false).describe("If true, overwrite existing file")
|
|
901
|
+
overwrite: z4.boolean().default(false).describe("If true, overwrite existing file"),
|
|
902
|
+
commit: z4.boolean().default(false).describe("If true, commit this change to git (creates undo point)")
|
|
895
903
|
},
|
|
896
|
-
async ({ path: notePath, content, frontmatter, overwrite }) => {
|
|
904
|
+
async ({ path: notePath, content, frontmatter, overwrite, commit }) => {
|
|
897
905
|
try {
|
|
898
906
|
if (!validatePath(vaultPath2, notePath)) {
|
|
899
907
|
const result2 = {
|
|
@@ -921,7 +929,7 @@ function registerNoteTools(server2, vaultPath2, autoCommit2) {
|
|
|
921
929
|
await writeVaultFile(vaultPath2, notePath, content, frontmatter);
|
|
922
930
|
let gitCommit;
|
|
923
931
|
let gitError;
|
|
924
|
-
if (
|
|
932
|
+
if (commit) {
|
|
925
933
|
const gitResult = await commitChange(vaultPath2, notePath, "[Crank:Create]");
|
|
926
934
|
if (gitResult.success) {
|
|
927
935
|
gitCommit = gitResult.hash;
|
|
@@ -954,9 +962,10 @@ Content length: ${content.length} chars`,
|
|
|
954
962
|
"Delete a note from the vault",
|
|
955
963
|
{
|
|
956
964
|
path: z4.string().describe("Vault-relative path to the note to delete"),
|
|
957
|
-
confirm: z4.boolean().default(false).describe("Must be true to confirm deletion")
|
|
965
|
+
confirm: z4.boolean().default(false).describe("Must be true to confirm deletion"),
|
|
966
|
+
commit: z4.boolean().default(false).describe("If true, commit this change to git (creates undo point)")
|
|
958
967
|
},
|
|
959
|
-
async ({ path: notePath, confirm }) => {
|
|
968
|
+
async ({ path: notePath, confirm, commit }) => {
|
|
960
969
|
try {
|
|
961
970
|
if (!confirm) {
|
|
962
971
|
const result2 = {
|
|
@@ -988,7 +997,7 @@ Content length: ${content.length} chars`,
|
|
|
988
997
|
await fs5.unlink(fullPath);
|
|
989
998
|
let gitCommit;
|
|
990
999
|
let gitError;
|
|
991
|
-
if (
|
|
1000
|
+
if (commit) {
|
|
992
1001
|
const gitResult = await commitChange(vaultPath2, notePath, "[Crank:Delete]");
|
|
993
1002
|
if (gitResult.success) {
|
|
994
1003
|
gitCommit = gitResult.hash;
|
|
@@ -1021,7 +1030,7 @@ Content length: ${content.length} chars`,
|
|
|
1021
1030
|
import { z as z5 } from "zod";
|
|
1022
1031
|
import fs6 from "fs/promises";
|
|
1023
1032
|
import path7 from "path";
|
|
1024
|
-
function registerSystemTools(server2, vaultPath2
|
|
1033
|
+
function registerSystemTools(server2, vaultPath2) {
|
|
1025
1034
|
server2.tool(
|
|
1026
1035
|
"vault_list_sections",
|
|
1027
1036
|
"List all sections (headings) in a markdown note",
|
|
@@ -1107,14 +1116,6 @@ Date: ${lastCommit.date}`
|
|
|
1107
1116
|
};
|
|
1108
1117
|
return { content: [{ type: "text", text: JSON.stringify(result2, null, 2) }] };
|
|
1109
1118
|
}
|
|
1110
|
-
if (!autoCommit2) {
|
|
1111
|
-
const result2 = {
|
|
1112
|
-
success: false,
|
|
1113
|
-
message: 'Undo is only available when AUTO_COMMIT is enabled. Set AUTO_COMMIT="true" in .mcp.json to enable automatic git commits for each mutation.',
|
|
1114
|
-
path: ""
|
|
1115
|
-
};
|
|
1116
|
-
return { content: [{ type: "text", text: JSON.stringify(result2, null, 2) }] };
|
|
1117
|
-
}
|
|
1118
1119
|
const isRepo = await isGitRepo(vaultPath2);
|
|
1119
1120
|
if (!isRepo) {
|
|
1120
1121
|
const result2 = {
|
|
@@ -1181,15 +1182,13 @@ var server = new McpServer({
|
|
|
1181
1182
|
version: "0.1.0"
|
|
1182
1183
|
});
|
|
1183
1184
|
var vaultPath = process.env.PROJECT_PATH || findVaultRoot();
|
|
1184
|
-
var autoCommit = process.env.AUTO_COMMIT === "true";
|
|
1185
1185
|
console.error(`[Crank] Starting Flywheel Crank MCP server`);
|
|
1186
1186
|
console.error(`[Crank] Vault path: ${vaultPath}`);
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
registerSystemTools(server, vaultPath, autoCommit);
|
|
1187
|
+
registerMutationTools(server, vaultPath);
|
|
1188
|
+
registerTaskTools(server, vaultPath);
|
|
1189
|
+
registerFrontmatterTools(server, vaultPath);
|
|
1190
|
+
registerNoteTools(server, vaultPath);
|
|
1191
|
+
registerSystemTools(server, vaultPath);
|
|
1193
1192
|
var transport = new StdioServerTransport();
|
|
1194
1193
|
await server.connect(transport);
|
|
1195
1194
|
console.error(`[Crank] Flywheel Crank MCP server started successfully`);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@velvetmonkey/flywheel-crank",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Deterministic vault mutations for Obsidian via MCP",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
"url": "https://github.com/velvetmonkey/flywheel-crank/issues"
|
|
16
16
|
},
|
|
17
17
|
"homepage": "https://github.com/velvetmonkey/flywheel-crank#readme",
|
|
18
|
-
"author": "
|
|
18
|
+
"author": "velvetmonkey",
|
|
19
19
|
"keywords": ["obsidian", "mcp", "model-context-protocol", "claude", "mutations", "vault-writes", "markdown"],
|
|
20
20
|
"scripts": {
|
|
21
21
|
"build": "npx esbuild src/index.ts --bundle --platform=node --format=esm --outfile=dist/index.js --packages=external && chmod +x dist/index.js",
|