mnotes-cli 1.5.1 → 1.7.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/dist/commands/login.d.ts +2 -0
- package/dist/commands/login.js +5 -0
- package/dist/commands/workspace.js +146 -34
- package/dist/config.js +29 -1
- package/dist/index.js +1 -1
- package/dist/templates/claude-code.js +55 -2
- package/package.json +1 -1
package/dist/commands/login.d.ts
CHANGED
package/dist/commands/login.js
CHANGED
|
@@ -62,10 +62,15 @@ function readConfig() {
|
|
|
62
62
|
const raw = fs.readFileSync(configPath(), "utf-8");
|
|
63
63
|
const parsed = JSON.parse(raw);
|
|
64
64
|
if (typeof parsed.apiKey === "string" && typeof parsed.serverUrl === "string") {
|
|
65
|
+
let workspaces;
|
|
66
|
+
if (typeof parsed.workspaces === "object" && parsed.workspaces !== null && !Array.isArray(parsed.workspaces)) {
|
|
67
|
+
workspaces = parsed.workspaces;
|
|
68
|
+
}
|
|
65
69
|
return {
|
|
66
70
|
apiKey: parsed.apiKey,
|
|
67
71
|
serverUrl: parsed.serverUrl,
|
|
68
72
|
workspaceId: typeof parsed.workspaceId === "string" ? parsed.workspaceId : undefined,
|
|
73
|
+
workspaces,
|
|
69
74
|
};
|
|
70
75
|
}
|
|
71
76
|
return null;
|
|
@@ -45,6 +45,26 @@ function ask(rl, question) {
|
|
|
45
45
|
});
|
|
46
46
|
});
|
|
47
47
|
}
|
|
48
|
+
/** Save a directory → workspace mapping in config. */
|
|
49
|
+
function saveDirectoryMapping(dir, workspaceId) {
|
|
50
|
+
const stored = (0, login_1.readConfig)();
|
|
51
|
+
if (!stored) {
|
|
52
|
+
process.stderr.write("Error: not logged in. Run `mnotes login` first.\n");
|
|
53
|
+
process.exit(1);
|
|
54
|
+
}
|
|
55
|
+
const workspaces = stored.workspaces ?? {};
|
|
56
|
+
workspaces[dir] = workspaceId;
|
|
57
|
+
(0, login_1.writeConfig)({ ...stored, workspaces });
|
|
58
|
+
}
|
|
59
|
+
/** Remove a directory → workspace mapping from config. */
|
|
60
|
+
function removeDirectoryMapping(dir) {
|
|
61
|
+
const stored = (0, login_1.readConfig)();
|
|
62
|
+
if (!stored?.workspaces?.[dir])
|
|
63
|
+
return false;
|
|
64
|
+
delete stored.workspaces[dir];
|
|
65
|
+
(0, login_1.writeConfig)(stored);
|
|
66
|
+
return true;
|
|
67
|
+
}
|
|
48
68
|
function registerWorkspaceCommand(program) {
|
|
49
69
|
const ws = program
|
|
50
70
|
.command("workspace")
|
|
@@ -62,20 +82,25 @@ function registerWorkspaceCommand(program) {
|
|
|
62
82
|
return;
|
|
63
83
|
}
|
|
64
84
|
const stored = (0, login_1.readConfig)();
|
|
65
|
-
const
|
|
85
|
+
const cwd = process.cwd();
|
|
86
|
+
const dirMapped = stored?.workspaces?.[cwd];
|
|
87
|
+
const globalDefault = stored?.workspaceId;
|
|
66
88
|
for (const w of workspaces) {
|
|
67
89
|
const markers = [];
|
|
68
90
|
if (w.isDefault)
|
|
69
91
|
markers.push("default");
|
|
70
|
-
if (w.id ===
|
|
71
|
-
markers.push("
|
|
92
|
+
if (w.id === dirMapped)
|
|
93
|
+
markers.push("linked");
|
|
94
|
+
else if (w.id === globalDefault)
|
|
95
|
+
markers.push("global");
|
|
72
96
|
const suffix = markers.length > 0 ? ` (${markers.join(", ")})` : "";
|
|
73
97
|
console.log(` ${w.name} [${w.slug}]${suffix}`);
|
|
74
98
|
}
|
|
75
99
|
});
|
|
76
100
|
ws.command("select")
|
|
77
|
-
.description("Select
|
|
78
|
-
.
|
|
101
|
+
.description("Select workspace for current directory (saved to config)")
|
|
102
|
+
.option("--global", "Set as global default instead of per-directory")
|
|
103
|
+
.action(async (opts) => {
|
|
79
104
|
const globalOpts = program.opts();
|
|
80
105
|
const config = (0, config_1.resolveConfig)(globalOpts);
|
|
81
106
|
const client = (0, client_1.createClient)(config.baseUrl, config.apiKey);
|
|
@@ -86,7 +111,8 @@ function registerWorkspaceCommand(program) {
|
|
|
86
111
|
return;
|
|
87
112
|
}
|
|
88
113
|
const stored = (0, login_1.readConfig)();
|
|
89
|
-
const
|
|
114
|
+
const cwd = process.cwd();
|
|
115
|
+
const currentId = stored?.workspaces?.[cwd] || stored?.workspaceId;
|
|
90
116
|
const rl = readline.createInterface({
|
|
91
117
|
input: process.stdin,
|
|
92
118
|
output: process.stderr,
|
|
@@ -95,7 +121,7 @@ function registerWorkspaceCommand(program) {
|
|
|
95
121
|
process.stderr.write("\nWorkspaces:\n");
|
|
96
122
|
for (let i = 0; i < workspaces.length; i++) {
|
|
97
123
|
const w = workspaces[i];
|
|
98
|
-
const marker = w.id ===
|
|
124
|
+
const marker = w.id === currentId ? " *" : "";
|
|
99
125
|
process.stderr.write(` ${i + 1}. ${w.name} [${w.slug}]${marker}\n`);
|
|
100
126
|
}
|
|
101
127
|
const answer = await ask(rl, `\nSelect workspace [1-${workspaces.length}]: `);
|
|
@@ -105,52 +131,138 @@ function registerWorkspaceCommand(program) {
|
|
|
105
131
|
process.exit(1);
|
|
106
132
|
}
|
|
107
133
|
const selected = workspaces[choice - 1];
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
134
|
+
if (opts.global) {
|
|
135
|
+
const existing = stored ?? { apiKey: config.apiKey, serverUrl: config.baseUrl };
|
|
136
|
+
(0, login_1.writeConfig)({ ...existing, workspaceId: selected.id });
|
|
137
|
+
console.log(`Global default workspace: ${selected.name} [${selected.slug}]`);
|
|
138
|
+
}
|
|
139
|
+
else {
|
|
140
|
+
saveDirectoryMapping(cwd, selected.id);
|
|
141
|
+
console.log(`Linked ${cwd} → ${selected.name} [${selected.slug}]`);
|
|
142
|
+
}
|
|
111
143
|
}
|
|
112
144
|
finally {
|
|
113
145
|
rl.close();
|
|
114
146
|
}
|
|
115
147
|
});
|
|
116
|
-
ws.command("
|
|
148
|
+
ws.command("link")
|
|
149
|
+
.description("Link current directory to a workspace")
|
|
150
|
+
.argument("[workspace-id]", "Workspace ID or slug (interactive if omitted)")
|
|
151
|
+
.action(async (workspaceIdOrSlug) => {
|
|
152
|
+
const globalOpts = program.opts();
|
|
153
|
+
const config = (0, config_1.resolveConfig)(globalOpts);
|
|
154
|
+
const client = (0, client_1.createClient)(config.baseUrl, config.apiKey);
|
|
155
|
+
const res = await client.listWorkspaces();
|
|
156
|
+
const workspaces = res.data;
|
|
157
|
+
if (workspaces.length === 0) {
|
|
158
|
+
console.log("No workspaces found. Create one with: mnotes workspace create <name>");
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
const cwd = process.cwd();
|
|
162
|
+
let selected;
|
|
163
|
+
if (workspaceIdOrSlug) {
|
|
164
|
+
selected = workspaces.find((w) => w.id === workspaceIdOrSlug || w.slug === workspaceIdOrSlug);
|
|
165
|
+
if (!selected) {
|
|
166
|
+
process.stderr.write(`Error: workspace "${workspaceIdOrSlug}" not found.\n`);
|
|
167
|
+
process.exit(1);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
else {
|
|
171
|
+
const rl = readline.createInterface({
|
|
172
|
+
input: process.stdin,
|
|
173
|
+
output: process.stderr,
|
|
174
|
+
});
|
|
175
|
+
try {
|
|
176
|
+
process.stderr.write("\nWorkspaces:\n");
|
|
177
|
+
for (let i = 0; i < workspaces.length; i++) {
|
|
178
|
+
const w = workspaces[i];
|
|
179
|
+
process.stderr.write(` ${i + 1}. ${w.name} [${w.slug}]\n`);
|
|
180
|
+
}
|
|
181
|
+
const answer = await ask(rl, `\nLink to workspace [1-${workspaces.length}]: `);
|
|
182
|
+
const choice = parseInt(answer, 10);
|
|
183
|
+
if (choice < 1 || choice > workspaces.length || isNaN(choice)) {
|
|
184
|
+
process.stderr.write("Invalid selection.\n");
|
|
185
|
+
process.exit(1);
|
|
186
|
+
}
|
|
187
|
+
selected = workspaces[choice - 1];
|
|
188
|
+
}
|
|
189
|
+
finally {
|
|
190
|
+
rl.close();
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
saveDirectoryMapping(cwd, selected.id);
|
|
194
|
+
console.log(`Linked ${cwd} → ${selected.name} [${selected.slug}]`);
|
|
195
|
+
});
|
|
196
|
+
ws.command("unlink")
|
|
197
|
+
.description("Remove workspace mapping for current directory")
|
|
198
|
+
.action(async () => {
|
|
199
|
+
const cwd = process.cwd();
|
|
200
|
+
const removed = removeDirectoryMapping(cwd);
|
|
201
|
+
if (removed) {
|
|
202
|
+
console.log(`Unlinked ${cwd}`);
|
|
203
|
+
}
|
|
204
|
+
else {
|
|
205
|
+
console.log(`No workspace linked to ${cwd}`);
|
|
206
|
+
}
|
|
207
|
+
});
|
|
208
|
+
ws.command("create")
|
|
117
209
|
.description("Create a new workspace")
|
|
210
|
+
.argument("<name>", "Workspace name")
|
|
118
211
|
.action(async (name) => {
|
|
119
212
|
const globalOpts = program.opts();
|
|
120
213
|
const config = (0, config_1.resolveConfig)(globalOpts);
|
|
121
214
|
const client = (0, client_1.createClient)(config.baseUrl, config.apiKey);
|
|
122
215
|
const res = await client.createWorkspace(name);
|
|
123
216
|
console.log(`Created workspace: ${res.data.name} [${res.data.slug}]`);
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
console.log("Auto-selected as default workspace.");
|
|
129
|
-
}
|
|
217
|
+
// Auto-link to current directory
|
|
218
|
+
const cwd = process.cwd();
|
|
219
|
+
saveDirectoryMapping(cwd, res.data.id);
|
|
220
|
+
console.log(`Linked ${cwd} → ${res.data.name}`);
|
|
130
221
|
});
|
|
131
222
|
ws.command("current")
|
|
132
|
-
.description("Show
|
|
223
|
+
.description("Show workspace for current directory")
|
|
133
224
|
.action(async () => {
|
|
134
225
|
const stored = (0, login_1.readConfig)();
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
226
|
+
const cwd = process.cwd();
|
|
227
|
+
// Check directory mapping first
|
|
228
|
+
const dirMapped = stored?.workspaces?.[cwd];
|
|
229
|
+
const effectiveId = dirMapped || stored?.workspaceId;
|
|
230
|
+
const source = dirMapped ? "linked" : stored?.workspaceId ? "global" : null;
|
|
231
|
+
if (!effectiveId) {
|
|
232
|
+
console.log(`No workspace for ${cwd}`);
|
|
233
|
+
console.log("Run: mnotes workspace select");
|
|
234
|
+
return;
|
|
235
|
+
}
|
|
236
|
+
const config = (0, config_1.resolveConfig)(program.opts());
|
|
237
|
+
const client = (0, client_1.createClient)(config.baseUrl, config.apiKey);
|
|
238
|
+
try {
|
|
239
|
+
const res = await client.listWorkspaces();
|
|
240
|
+
const current = res.data.find((w) => w.id === effectiveId);
|
|
241
|
+
if (current) {
|
|
242
|
+
console.log(`${current.name} [${current.slug}] (${source})`);
|
|
147
243
|
}
|
|
148
|
-
|
|
149
|
-
console.log(
|
|
244
|
+
else {
|
|
245
|
+
console.log(`${effectiveId} (${source}, not found on server)`);
|
|
150
246
|
}
|
|
151
247
|
}
|
|
152
|
-
|
|
153
|
-
console.log(
|
|
248
|
+
catch {
|
|
249
|
+
console.log(`${effectiveId} (${source})`);
|
|
250
|
+
}
|
|
251
|
+
});
|
|
252
|
+
ws.command("mappings")
|
|
253
|
+
.description("Show all directory → workspace mappings")
|
|
254
|
+
.action(async () => {
|
|
255
|
+
const stored = (0, login_1.readConfig)();
|
|
256
|
+
const workspaces = stored?.workspaces;
|
|
257
|
+
if (!workspaces || Object.keys(workspaces).length === 0) {
|
|
258
|
+
console.log("No directory mappings. Use: mnotes workspace link");
|
|
259
|
+
return;
|
|
260
|
+
}
|
|
261
|
+
for (const [dir, wsId] of Object.entries(workspaces)) {
|
|
262
|
+
console.log(` ${dir} → ${wsId}`);
|
|
263
|
+
}
|
|
264
|
+
if (stored?.workspaceId) {
|
|
265
|
+
console.log(`\n Global default: ${stored.workspaceId}`);
|
|
154
266
|
}
|
|
155
267
|
});
|
|
156
268
|
}
|
package/dist/config.js
CHANGED
|
@@ -2,6 +2,34 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.resolveConfig = resolveConfig;
|
|
4
4
|
const login_1 = require("./commands/login");
|
|
5
|
+
/**
|
|
6
|
+
* Resolve workspace ID from (in priority order):
|
|
7
|
+
* 1. Explicit flag/option
|
|
8
|
+
* 2. MNOTES_WORKSPACE_ID env var
|
|
9
|
+
* 3. Per-directory mapping in config (cwd → workspaceId)
|
|
10
|
+
* 4. Global default workspaceId in config
|
|
11
|
+
*/
|
|
12
|
+
function resolveWorkspaceId(explicit, stored) {
|
|
13
|
+
if (explicit)
|
|
14
|
+
return explicit;
|
|
15
|
+
if (process.env.MNOTES_WORKSPACE_ID)
|
|
16
|
+
return process.env.MNOTES_WORKSPACE_ID;
|
|
17
|
+
// Check per-directory mapping
|
|
18
|
+
if (stored?.workspaces) {
|
|
19
|
+
const cwd = process.cwd();
|
|
20
|
+
// Exact match first, then walk up parent directories
|
|
21
|
+
let dir = cwd;
|
|
22
|
+
while (true) {
|
|
23
|
+
if (stored.workspaces[dir])
|
|
24
|
+
return stored.workspaces[dir];
|
|
25
|
+
const parent = require("path").dirname(dir);
|
|
26
|
+
if (parent === dir)
|
|
27
|
+
break; // reached root
|
|
28
|
+
dir = parent;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
return stored?.workspaceId;
|
|
32
|
+
}
|
|
5
33
|
function resolveConfig(opts) {
|
|
6
34
|
const stored = (0, login_1.readConfig)();
|
|
7
35
|
const apiKey = opts.apiKey || process.env.MNOTES_API_KEY || stored?.apiKey;
|
|
@@ -10,6 +38,6 @@ function resolveConfig(opts) {
|
|
|
10
38
|
process.exit(1);
|
|
11
39
|
}
|
|
12
40
|
const baseUrl = opts.url || process.env.MNOTES_URL || stored?.serverUrl || "https://mnotes.framework.by";
|
|
13
|
-
const workspaceId = opts.workspaceId
|
|
41
|
+
const workspaceId = resolveWorkspaceId(opts.workspaceId, stored ?? undefined);
|
|
14
42
|
return { apiKey, baseUrl, workspaceId };
|
|
15
43
|
}
|
package/dist/index.js
CHANGED
|
@@ -15,7 +15,7 @@ const program = new commander_1.Command();
|
|
|
15
15
|
program
|
|
16
16
|
.name("mnotes")
|
|
17
17
|
.description("CLI for m-notes AI knowledge base")
|
|
18
|
-
.version(
|
|
18
|
+
.version(require("../package.json").version)
|
|
19
19
|
.option("--api-key <key>", "API key (or set MNOTES_API_KEY)")
|
|
20
20
|
.option("--url <url>", "Base URL (or set MNOTES_URL)")
|
|
21
21
|
.option("--json", "Output as JSON");
|
|
@@ -38,17 +38,70 @@ Recall knowledge **before acting**. Specifically:
|
|
|
38
38
|
- At session start → \`project_context_load\` loads everything relevant
|
|
39
39
|
- When the user asks about past work → \`session_context_resume\`
|
|
40
40
|
|
|
41
|
+
## Notes — Your Working Documents
|
|
42
|
+
|
|
43
|
+
m-notes is not just a knowledge base — it's a full note-taking system. **Use notes actively** to create and maintain living documents:
|
|
44
|
+
|
|
45
|
+
### When to Create Notes
|
|
46
|
+
- **Meeting notes** → \`create_note\` after any planning discussion or decision
|
|
47
|
+
- **Investigation logs** → create a note when debugging, append findings as you go with \`append_to_note\`
|
|
48
|
+
- **Design docs** → write architecture or design decisions as full notes, not just knowledge entries
|
|
49
|
+
- **Task summaries** → after completing a story/task, create a note summarizing what was done
|
|
50
|
+
- **Checklists and plans** → create notes with markdown checklists for multi-step work
|
|
51
|
+
- **Daily notes** → use \`daily_note\` to create/get today's note for quick captures
|
|
52
|
+
|
|
53
|
+
### When to Edit Notes
|
|
54
|
+
- **Append progress** → use \`append_to_note\` to add to existing notes as work progresses
|
|
55
|
+
- **Update docs** → when code changes invalidate existing notes, update them with \`update_note\`
|
|
56
|
+
- **Tag and organize** → use \`manage_tags\` and folder tools to keep notes findable
|
|
57
|
+
|
|
58
|
+
### Note vs Knowledge Entry
|
|
59
|
+
| Use a **note** when... | Use **knowledge_store** when... |
|
|
60
|
+
|---|---|
|
|
61
|
+
| Content is long-form (paragraphs, lists, docs) | Content is a single fact or decision |
|
|
62
|
+
| Document will be updated over time | Entry is a permanent record |
|
|
63
|
+
| Needs folder organization | Needs key/tag retrieval |
|
|
64
|
+
| Meeting notes, plans, investigations | Architecture decisions, gotchas, patterns |
|
|
65
|
+
|
|
66
|
+
**When in doubt, create a note.** Notes are searchable, linkable, and visible in the UI.
|
|
67
|
+
|
|
41
68
|
## MCP Tools Reference
|
|
42
69
|
|
|
70
|
+
### Session & Context
|
|
43
71
|
| Tool | When to use |
|
|
44
72
|
|------|------------|
|
|
45
73
|
| \`project_context_load\` | Session start — loads project context |
|
|
46
74
|
| \`session_context_resume\` | Resume from previous session |
|
|
47
|
-
| \`
|
|
75
|
+
| \`session_log\` | Log session summary at end |
|
|
76
|
+
|
|
77
|
+
### Knowledge (quick structured entries)
|
|
78
|
+
| Tool | When to use |
|
|
79
|
+
|------|------------|
|
|
80
|
+
| \`knowledge_store\` | Store a knowledge entry (key, content, tags) |
|
|
48
81
|
| \`recall_knowledge\` | Semantic search across stored knowledge |
|
|
49
82
|
| \`bulk_knowledge_recall\` | Recall by tag patterns (e.g., all \`arch/*\`) |
|
|
50
83
|
| \`knowledge_snapshot\` | Export all knowledge at once |
|
|
51
|
-
|
|
84
|
+
|
|
85
|
+
### Notes (full documents)
|
|
86
|
+
| Tool | When to use |
|
|
87
|
+
|------|------------|
|
|
88
|
+
| \`create_note\` | Create a new note (title, content, folderId) |
|
|
89
|
+
| \`update_note\` | Replace note content |
|
|
90
|
+
| \`append_to_note\` | Add content to an existing note |
|
|
91
|
+
| \`get_note\` | Read a note by ID |
|
|
92
|
+
| \`get_note_by_title\` | Find a note by title |
|
|
93
|
+
| \`search_notes\` | Full-text or semantic search |
|
|
94
|
+
| \`list_notes\` | List notes in a folder |
|
|
95
|
+
| \`daily_note\` | Create or get today's daily note |
|
|
96
|
+
| \`manage_tags\` | Add/remove tags on notes |
|
|
97
|
+
| \`pin_note\` / \`toggle_star\` | Pin or star important notes |
|
|
98
|
+
|
|
99
|
+
### Organization
|
|
100
|
+
| Tool | When to use |
|
|
101
|
+
|------|------------|
|
|
102
|
+
| \`list_folders\` | List folders in workspace |
|
|
103
|
+
| \`create_folder\` | Create a new folder |
|
|
104
|
+
| \`move_note\` | Move note to a different folder |
|
|
52
105
|
| \`context_fetch\` | Search notes by query |
|
|
53
106
|
|
|
54
107
|
All tools require \`workspaceId: "${opts.workspaceId}"\`.`;
|