mnotes-cli 1.9.2 → 1.10.1
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/connect/__tests__/connect.test.js +37 -5
- package/dist/commands/connect/__tests__/wizard.test.js +41 -10
- package/dist/commands/connect/index.js +37 -3
- package/dist/commands/connect/wizard.js +14 -3
- package/dist/templates/claude-code/hooks.d.ts +10 -4
- package/dist/templates/claude-code/hooks.js +50 -20
- package/dist/templates/claude-code/index.d.ts +2 -2
- package/dist/templates/claude-code/index.js +2 -1
- package/dist/templates/claude-code.js +125 -106
- package/dist/templates/codex.js +36 -32
- package/dist/templates/openclaw.js +21 -12
- package/package.json +1 -1
|
@@ -43,6 +43,21 @@ const config_utils_1 = require("../config-utils");
|
|
|
43
43
|
const claude_code_1 = require("../../../templates/claude-code");
|
|
44
44
|
const codex_1 = require("../../../templates/codex");
|
|
45
45
|
const openclaw_1 = require("../../../templates/openclaw");
|
|
46
|
+
// Mock createClient so resolveWorkspace can validate workspaces without a real server
|
|
47
|
+
vitest_1.vi.mock("../../../client", () => ({
|
|
48
|
+
createClient: () => ({
|
|
49
|
+
listWorkspaces: async () => ({
|
|
50
|
+
data: [
|
|
51
|
+
{ id: "ws-123", name: "Test", slug: "test-123", isDefault: true },
|
|
52
|
+
{ id: "ws-explicit-id", name: "Explicit", slug: "explicit-id", isDefault: false },
|
|
53
|
+
{ id: "ws-new", name: "New", slug: "new", isDefault: false },
|
|
54
|
+
],
|
|
55
|
+
}),
|
|
56
|
+
createWorkspace: async (name) => ({
|
|
57
|
+
data: { id: name, name, slug: name, isDefault: false },
|
|
58
|
+
}),
|
|
59
|
+
}),
|
|
60
|
+
}));
|
|
46
61
|
// -- Helper: capture stdout --
|
|
47
62
|
function captureStdout(fn) {
|
|
48
63
|
const chunks = [];
|
|
@@ -380,14 +395,14 @@ function cleanTmpDir(dir) {
|
|
|
380
395
|
(0, vitest_1.expect)(result).toContain("https://notes.example.com");
|
|
381
396
|
(0, vitest_1.expect)(result).toContain("ws-abc-123");
|
|
382
397
|
});
|
|
383
|
-
(0, vitest_1.it)("includes session lifecycle
|
|
398
|
+
(0, vitest_1.it)("includes session lifecycle tools and wiki framing", () => {
|
|
384
399
|
const result = (0, claude_code_1.generateClaudeCodeTemplate)({
|
|
385
400
|
url: "http://localhost:3000",
|
|
386
401
|
workspaceId: "ws-test",
|
|
387
402
|
});
|
|
388
|
-
(0, vitest_1.expect)(result).toContain("
|
|
389
|
-
(0, vitest_1.expect)(result).toContain("
|
|
390
|
-
(0, vitest_1.expect)(result).toContain("
|
|
403
|
+
(0, vitest_1.expect)(result).toContain("living wiki");
|
|
404
|
+
(0, vitest_1.expect)(result).toContain("Ingest Loop");
|
|
405
|
+
(0, vitest_1.expect)(result).toContain("Lint Loop");
|
|
391
406
|
(0, vitest_1.expect)(result).toContain("project_context_load");
|
|
392
407
|
(0, vitest_1.expect)(result).toContain("session_context_resume");
|
|
393
408
|
(0, vitest_1.expect)(result).toContain("knowledge_store");
|
|
@@ -417,13 +432,30 @@ function cleanTmpDir(dir) {
|
|
|
417
432
|
"recall_knowledge",
|
|
418
433
|
"bulk_knowledge_recall",
|
|
419
434
|
"knowledge_snapshot",
|
|
435
|
+
"scan_knowledge_conflicts",
|
|
420
436
|
"session_log",
|
|
421
437
|
"context_fetch",
|
|
438
|
+
"create_note",
|
|
439
|
+
"update_note",
|
|
440
|
+
"append_to_note",
|
|
441
|
+
"search_notes",
|
|
442
|
+
"daily_note",
|
|
443
|
+
"populate_graph",
|
|
444
|
+
"query_note_graph",
|
|
422
445
|
];
|
|
423
446
|
for (const tool of expectedTools) {
|
|
424
447
|
(0, vitest_1.expect)(result).toContain(tool);
|
|
425
448
|
}
|
|
426
449
|
});
|
|
450
|
+
(0, vitest_1.it)("does not reference phantom MCP tools", () => {
|
|
451
|
+
const result = (0, claude_code_1.generateClaudeCodeTemplate)({
|
|
452
|
+
url: "http://localhost:3000",
|
|
453
|
+
workspaceId: "ws-test",
|
|
454
|
+
});
|
|
455
|
+
// These tool names don't exist in the MCP server — guard against regressions.
|
|
456
|
+
(0, vitest_1.expect)(result).not.toMatch(/\bcreate_folder\b/);
|
|
457
|
+
(0, vitest_1.expect)(result).not.toMatch(/\bmove_note\b/);
|
|
458
|
+
});
|
|
427
459
|
});
|
|
428
460
|
// =============================================================
|
|
429
461
|
// Template generation — generateCodexTemplate (AC-6.3)
|
|
@@ -615,7 +647,7 @@ function cleanTmpDir(dir) {
|
|
|
615
647
|
const claudeMd = fs.readFileSync(path.join(tmpDir, "CLAUDE.md"), "utf-8");
|
|
616
648
|
(0, vitest_1.expect)(claudeMd).toContain("<!-- m-notes:start -->");
|
|
617
649
|
(0, vitest_1.expect)(claudeMd).toContain("<!-- m-notes:end -->");
|
|
618
|
-
(0, vitest_1.expect)(claudeMd).toContain("m-notes
|
|
650
|
+
(0, vitest_1.expect)(claudeMd).toContain("m-notes — Your Wiki");
|
|
619
651
|
(0, vitest_1.expect)(claudeMd).toContain("ws-123");
|
|
620
652
|
});
|
|
621
653
|
(0, vitest_1.it)("replaces existing m-notes block on re-run", async () => {
|
|
@@ -41,6 +41,19 @@ const wizard_1 = require("../wizard");
|
|
|
41
41
|
const hooks_1 = require("../../../templates/claude-code/hooks");
|
|
42
42
|
const skills_1 = require("../../../templates/claude-code/skills");
|
|
43
43
|
const agents_1 = require("../../../templates/claude-code/agents");
|
|
44
|
+
// Mock createClient so resolveWorkspace can validate workspaces without a real server
|
|
45
|
+
vitest_1.vi.mock("../../../client", () => ({
|
|
46
|
+
createClient: () => ({
|
|
47
|
+
listWorkspaces: async () => ({
|
|
48
|
+
data: [
|
|
49
|
+
{ id: "ws-123", name: "Test", slug: "test-123", isDefault: true },
|
|
50
|
+
],
|
|
51
|
+
}),
|
|
52
|
+
createWorkspace: async (name) => ({
|
|
53
|
+
data: { id: name, name, slug: name, isDefault: false },
|
|
54
|
+
}),
|
|
55
|
+
}),
|
|
56
|
+
}));
|
|
44
57
|
function makeTmpDir() {
|
|
45
58
|
return fs.mkdtempSync(path.join(os.tmpdir(), "mnotes-wizard-test-"));
|
|
46
59
|
}
|
|
@@ -55,20 +68,29 @@ const DEFAULT_OPTS = {
|
|
|
55
68
|
// T-1: Template generation — hooks
|
|
56
69
|
// =============================================================
|
|
57
70
|
(0, vitest_1.describe)("hooks template", () => {
|
|
58
|
-
(0, vitest_1.it)("generates SessionStart hook
|
|
71
|
+
(0, vitest_1.it)("generates SessionStart hook referencing bash script", () => {
|
|
59
72
|
const hooks = (0, hooks_1.generateHooksTemplate)(DEFAULT_OPTS);
|
|
60
73
|
(0, vitest_1.expect)(hooks.SessionStart).toBeDefined();
|
|
61
74
|
(0, vitest_1.expect)(hooks.SessionStart).toHaveLength(1);
|
|
62
75
|
(0, vitest_1.expect)(hooks.SessionStart[0].matcher).toBe("");
|
|
63
76
|
(0, vitest_1.expect)(hooks.SessionStart[0].hooks).toHaveLength(1);
|
|
64
77
|
(0, vitest_1.expect)(hooks.SessionStart[0].hooks[0].type).toBe("command");
|
|
65
|
-
(0, vitest_1.expect)(hooks.SessionStart[0].hooks[0].command).
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
(0, vitest_1.expect)(
|
|
78
|
+
(0, vitest_1.expect)(hooks.SessionStart[0].hooks[0].command).toBe(".claude/hooks/mnotes-session-start.sh");
|
|
79
|
+
});
|
|
80
|
+
(0, vitest_1.it)("generates hook scripts with correct URL, workspaceId, and Accept headers", () => {
|
|
81
|
+
const scripts = (0, hooks_1.generateHookScripts)(DEFAULT_OPTS);
|
|
82
|
+
(0, vitest_1.expect)(scripts).toHaveLength(2);
|
|
83
|
+
const startScript = scripts.find((s) => s.filename === "mnotes-session-start.sh");
|
|
84
|
+
(0, vitest_1.expect)(startScript.content).toContain("localhost:3000/api/mcp");
|
|
85
|
+
(0, vitest_1.expect)(startScript.content).toContain("ws-test-123");
|
|
86
|
+
(0, vitest_1.expect)(startScript.content).toContain('Accept: text/event-stream');
|
|
87
|
+
(0, vitest_1.expect)(startScript.content).toContain('Accept: application/json');
|
|
88
|
+
});
|
|
89
|
+
(0, vitest_1.it)("strips trailing slashes from URL in hook scripts", () => {
|
|
90
|
+
const scripts = (0, hooks_1.generateHookScripts)({ url: "http://example.com///", workspaceId: "ws-1" });
|
|
91
|
+
const startScript = scripts.find((s) => s.filename === "mnotes-session-start.sh");
|
|
92
|
+
(0, vitest_1.expect)(startScript.content).toContain("http://example.com/api/mcp");
|
|
93
|
+
(0, vitest_1.expect)(startScript.content).not.toContain("///");
|
|
72
94
|
});
|
|
73
95
|
});
|
|
74
96
|
// =============================================================
|
|
@@ -162,17 +184,26 @@ const DEFAULT_OPTS = {
|
|
|
162
184
|
(0, vitest_1.afterEach)(() => {
|
|
163
185
|
cleanTmpDir(tmpDir);
|
|
164
186
|
});
|
|
165
|
-
(0, vitest_1.it)("creates .claude/settings.json with hooks (AC-4)", () => {
|
|
187
|
+
(0, vitest_1.it)("creates .claude/settings.json with hooks and bash scripts (AC-4)", () => {
|
|
166
188
|
const results = (0, wizard_1.scaffoldItems)(tmpDir, ["hooks"], DEFAULT_OPTS);
|
|
167
189
|
(0, vitest_1.expect)(results).toHaveLength(1);
|
|
168
190
|
(0, vitest_1.expect)(results[0].item).toBe("hooks");
|
|
169
|
-
|
|
191
|
+
// 2 bash scripts + settings.json
|
|
192
|
+
(0, vitest_1.expect)(results[0].filesWritten).toHaveLength(3);
|
|
170
193
|
const settingsPath = path.join(tmpDir, ".claude", "settings.json");
|
|
171
194
|
(0, vitest_1.expect)(fs.existsSync(settingsPath)).toBe(true);
|
|
172
195
|
const settings = JSON.parse(fs.readFileSync(settingsPath, "utf-8"));
|
|
173
196
|
(0, vitest_1.expect)(settings.hooks).toBeDefined();
|
|
174
197
|
(0, vitest_1.expect)(settings.hooks.SessionStart).toBeDefined();
|
|
175
198
|
(0, vitest_1.expect)(settings.hooks.SessionStart).toHaveLength(1);
|
|
199
|
+
// Bash scripts exist and are executable
|
|
200
|
+
const startScript = path.join(tmpDir, ".claude", "hooks", "mnotes-session-start.sh");
|
|
201
|
+
const stopScript = path.join(tmpDir, ".claude", "hooks", "mnotes-session-stop.sh");
|
|
202
|
+
(0, vitest_1.expect)(fs.existsSync(startScript)).toBe(true);
|
|
203
|
+
(0, vitest_1.expect)(fs.existsSync(stopScript)).toBe(true);
|
|
204
|
+
const startContent = fs.readFileSync(startScript, "utf-8");
|
|
205
|
+
(0, vitest_1.expect)(startContent).toContain("Accept: text/event-stream");
|
|
206
|
+
(0, vitest_1.expect)(startContent).toContain("Accept: application/json");
|
|
176
207
|
});
|
|
177
208
|
(0, vitest_1.it)("merges hooks into existing settings.json (AC-5)", () => {
|
|
178
209
|
const claudeDir = path.join(tmpDir, ".claude");
|
|
@@ -38,9 +38,11 @@ exports.handleClaudeCode = handleClaudeCode;
|
|
|
38
38
|
exports.registerConnectCommand = registerConnectCommand;
|
|
39
39
|
const fs = __importStar(require("fs"));
|
|
40
40
|
const path = __importStar(require("path"));
|
|
41
|
+
const readline = __importStar(require("readline"));
|
|
41
42
|
const config_utils_1 = require("./config-utils");
|
|
42
43
|
const workspace_prompt_1 = require("./workspace-prompt");
|
|
43
44
|
const config_1 = require("../../config");
|
|
45
|
+
const client_1 = require("../../client");
|
|
44
46
|
const claude_code_1 = require("../../templates/claude-code");
|
|
45
47
|
const codex_1 = require("../../templates/codex");
|
|
46
48
|
const openclaw_1 = require("../../templates/openclaw");
|
|
@@ -90,13 +92,45 @@ function printConnectionStatus() {
|
|
|
90
92
|
/**
|
|
91
93
|
* Resolves the workspace ID — uses --workspace flag if provided, otherwise
|
|
92
94
|
* prompts interactively after validating the connection.
|
|
95
|
+
*
|
|
96
|
+
* When a workspace value is provided (flag, env, or config), validates it
|
|
97
|
+
* against the API by matching on ID or slug. If not found, prompts to create.
|
|
93
98
|
*/
|
|
94
99
|
async function resolveWorkspace(opts) {
|
|
95
100
|
// Check flag, env, dir map, global config
|
|
96
101
|
const fromConfig = (0, config_1.resolveConfig)({ workspaceId: opts.workspace });
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
102
|
+
const candidate = fromConfig.workspaceId;
|
|
103
|
+
if (candidate) {
|
|
104
|
+
// Validate the candidate against the API
|
|
105
|
+
const client = (0, client_1.createClient)(opts.url, opts.apiKey);
|
|
106
|
+
let workspaces;
|
|
107
|
+
try {
|
|
108
|
+
const res = await client.listWorkspaces();
|
|
109
|
+
workspaces = res.data;
|
|
110
|
+
}
|
|
111
|
+
catch (err) {
|
|
112
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
113
|
+
throw new Error(`Failed to fetch workspaces: ${message}`);
|
|
114
|
+
}
|
|
115
|
+
// Match by ID or slug
|
|
116
|
+
const match = workspaces.find((ws) => ws.id === candidate || ws.slug === candidate);
|
|
117
|
+
if (match) {
|
|
118
|
+
return match.id;
|
|
119
|
+
}
|
|
120
|
+
// Not found — ask user to create
|
|
121
|
+
process.stderr.write(`\nWorkspace "${candidate}" not found.\n`);
|
|
122
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stderr });
|
|
123
|
+
const answer = await new Promise((resolve) => rl.question(`Create workspace "${candidate}"? [Y/n] `, resolve));
|
|
124
|
+
rl.close();
|
|
125
|
+
if (answer.trim() === "" || answer.trim().toLowerCase() === "y") {
|
|
126
|
+
const created = await client.createWorkspace(candidate);
|
|
127
|
+
process.stderr.write(`Created workspace "${created.data.name}" (${created.data.id})\n`);
|
|
128
|
+
return created.data.id;
|
|
129
|
+
}
|
|
130
|
+
// User declined — fall through to interactive
|
|
131
|
+
process.stderr.write("Falling back to interactive workspace selection.\n");
|
|
132
|
+
}
|
|
133
|
+
// Nothing stored or user declined — interactive selection/creation
|
|
100
134
|
const resolved = await (0, workspace_prompt_1.resolveWorkspaceInteractively)(opts.url, opts.apiKey);
|
|
101
135
|
return resolved.id;
|
|
102
136
|
}
|
|
@@ -102,13 +102,23 @@ function scaffoldItems(dir, items, opts) {
|
|
|
102
102
|
return results;
|
|
103
103
|
}
|
|
104
104
|
/**
|
|
105
|
-
* Merges hooks into `.claude/settings.json`.
|
|
105
|
+
* Merges hooks into `.claude/settings.json` and writes bash scripts to `.claude/hooks/`.
|
|
106
106
|
* Preserves all existing settings and hooks.
|
|
107
107
|
*/
|
|
108
108
|
function scaffoldHooks(dir, opts) {
|
|
109
109
|
const settingsPath = path.join(dir, ".claude", "settings.json");
|
|
110
110
|
const claudeDir = path.join(dir, ".claude");
|
|
111
|
-
|
|
111
|
+
const hooksDir = path.join(claudeDir, "hooks");
|
|
112
|
+
fs.mkdirSync(hooksDir, { recursive: true });
|
|
113
|
+
const filesWritten = [];
|
|
114
|
+
// 1. Write bash scripts to .claude/hooks/
|
|
115
|
+
const scripts = (0, index_1.generateHookScripts)(opts);
|
|
116
|
+
for (const script of scripts) {
|
|
117
|
+
const scriptPath = path.join(hooksDir, script.filename);
|
|
118
|
+
fs.writeFileSync(scriptPath, script.content, { mode: 0o755 });
|
|
119
|
+
filesWritten.push(scriptPath);
|
|
120
|
+
}
|
|
121
|
+
// 2. Merge hook entries into settings.json
|
|
112
122
|
let existing = {};
|
|
113
123
|
try {
|
|
114
124
|
const raw = fs.readFileSync(settingsPath, "utf-8");
|
|
@@ -158,7 +168,8 @@ function scaffoldHooks(dir, opts) {
|
|
|
158
168
|
mnotesMetadata.items.push("hooks");
|
|
159
169
|
}
|
|
160
170
|
fs.writeFileSync(settingsPath, JSON.stringify(existing, null, 2) + "\n", "utf-8");
|
|
161
|
-
|
|
171
|
+
filesWritten.push(settingsPath);
|
|
172
|
+
return { item: "hooks", filesWritten };
|
|
162
173
|
}
|
|
163
174
|
/**
|
|
164
175
|
* Writes skill files to `.claude/skills/`.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Template for Claude Code session hooks.
|
|
3
|
-
*
|
|
3
|
+
* Generates bash scripts in `.claude/hooks/` and hook entries for `.claude/settings.json`.
|
|
4
4
|
* Generated by m-notes CLI — do not edit manually.
|
|
5
5
|
*/
|
|
6
6
|
export interface HooksTemplateOpts {
|
|
@@ -19,11 +19,17 @@ export interface ClaudeCodeHooks {
|
|
|
19
19
|
SessionStart?: ClaudeCodeHookEntry[];
|
|
20
20
|
[key: string]: ClaudeCodeHookEntry[] | undefined;
|
|
21
21
|
}
|
|
22
|
+
export interface HookScript {
|
|
23
|
+
filename: string;
|
|
24
|
+
content: string;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Generates bash scripts to be written to `.claude/hooks/`.
|
|
28
|
+
*/
|
|
29
|
+
export declare function generateHookScripts(opts: HooksTemplateOpts): HookScript[];
|
|
22
30
|
/**
|
|
23
31
|
* Generates the hooks object to merge into `.claude/settings.json`.
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
* Claude Code hooks format requires: { matcher, hooks[] } wrapper.
|
|
32
|
+
* References bash scripts in `.claude/hooks/` instead of inline commands.
|
|
27
33
|
*/
|
|
28
34
|
export declare function generateHooksTemplate(opts: HooksTemplateOpts): ClaudeCodeHooks;
|
|
29
35
|
/** Header comment for the hooks section, used for identification. */
|
|
@@ -1,31 +1,69 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
/**
|
|
3
3
|
* Template for Claude Code session hooks.
|
|
4
|
-
*
|
|
4
|
+
* Generates bash scripts in `.claude/hooks/` and hook entries for `.claude/settings.json`.
|
|
5
5
|
* Generated by m-notes CLI — do not edit manually.
|
|
6
6
|
*/
|
|
7
7
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
8
|
exports.HOOKS_HEADER = void 0;
|
|
9
|
+
exports.generateHookScripts = generateHookScripts;
|
|
9
10
|
exports.generateHooksTemplate = generateHooksTemplate;
|
|
10
11
|
/**
|
|
11
|
-
* Generates
|
|
12
|
-
* Currently provides a SessionStart hook that auto-loads project context.
|
|
13
|
-
*
|
|
14
|
-
* Claude Code hooks format requires: { matcher, hooks[] } wrapper.
|
|
12
|
+
* Generates bash scripts to be written to `.claude/hooks/`.
|
|
15
13
|
*/
|
|
16
|
-
function
|
|
14
|
+
function generateHookScripts(opts) {
|
|
17
15
|
const baseUrl = opts.url.replace(/\/+$/, "");
|
|
18
16
|
const mcpUrl = `${baseUrl}/api/mcp`;
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
const payload = JSON.stringify({
|
|
17
|
+
function mcpPayload(method, args) {
|
|
18
|
+
return JSON.stringify({
|
|
22
19
|
jsonrpc: "2.0",
|
|
23
20
|
method: "tools/call",
|
|
24
21
|
params: { name: method, arguments: args },
|
|
25
22
|
id: 1,
|
|
26
23
|
});
|
|
27
|
-
return `curl -s -H "${authHeader}" "${mcpUrl}" -d '${payload}' -H "Content-Type: application/json" > /dev/null 2>&1 || true`;
|
|
28
24
|
}
|
|
25
|
+
const sessionStartScript = `#!/usr/bin/env bash
|
|
26
|
+
# Generated by m-notes CLI — do not edit manually.
|
|
27
|
+
# Loads project context on Claude Code session start.
|
|
28
|
+
set -euo pipefail
|
|
29
|
+
|
|
30
|
+
MNOTES_URL="${mcpUrl}"
|
|
31
|
+
PAYLOAD='${mcpPayload("project_context_load", { workspaceId: opts.workspaceId, query: "session start" })}'
|
|
32
|
+
|
|
33
|
+
curl -s \\
|
|
34
|
+
-H "Authorization: Bearer $MNOTES_API_KEY" \\
|
|
35
|
+
-H "Content-Type: application/json" \\
|
|
36
|
+
-H "Accept: text/event-stream" \\
|
|
37
|
+
-H "Accept: application/json" \\
|
|
38
|
+
"$MNOTES_URL" \\
|
|
39
|
+
-d "$PAYLOAD" > /dev/null 2>&1 || true
|
|
40
|
+
`;
|
|
41
|
+
const sessionStopScript = `#!/usr/bin/env bash
|
|
42
|
+
# Generated by m-notes CLI — do not edit manually.
|
|
43
|
+
# Logs session end to m-notes.
|
|
44
|
+
set -euo pipefail
|
|
45
|
+
|
|
46
|
+
MNOTES_URL="${mcpUrl}"
|
|
47
|
+
PAYLOAD='${mcpPayload("session_log", { workspaceId: opts.workspaceId, summary: "Session ended", decisions: [], actions: [] })}'
|
|
48
|
+
|
|
49
|
+
curl -s \\
|
|
50
|
+
-H "Authorization: Bearer $MNOTES_API_KEY" \\
|
|
51
|
+
-H "Content-Type: application/json" \\
|
|
52
|
+
-H "Accept: text/event-stream" \\
|
|
53
|
+
-H "Accept: application/json" \\
|
|
54
|
+
"$MNOTES_URL" \\
|
|
55
|
+
-d "$PAYLOAD" > /dev/null 2>&1 || true
|
|
56
|
+
`;
|
|
57
|
+
return [
|
|
58
|
+
{ filename: "mnotes-session-start.sh", content: sessionStartScript },
|
|
59
|
+
{ filename: "mnotes-session-stop.sh", content: sessionStopScript },
|
|
60
|
+
];
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Generates the hooks object to merge into `.claude/settings.json`.
|
|
64
|
+
* References bash scripts in `.claude/hooks/` instead of inline commands.
|
|
65
|
+
*/
|
|
66
|
+
function generateHooksTemplate(opts) {
|
|
29
67
|
return {
|
|
30
68
|
SessionStart: [
|
|
31
69
|
{
|
|
@@ -33,10 +71,7 @@ function generateHooksTemplate(opts) {
|
|
|
33
71
|
hooks: [
|
|
34
72
|
{
|
|
35
73
|
type: "command",
|
|
36
|
-
command:
|
|
37
|
-
workspaceId: opts.workspaceId,
|
|
38
|
-
query: "session start",
|
|
39
|
-
}),
|
|
74
|
+
command: ".claude/hooks/mnotes-session-start.sh",
|
|
40
75
|
},
|
|
41
76
|
],
|
|
42
77
|
},
|
|
@@ -47,12 +82,7 @@ function generateHooksTemplate(opts) {
|
|
|
47
82
|
hooks: [
|
|
48
83
|
{
|
|
49
84
|
type: "command",
|
|
50
|
-
command:
|
|
51
|
-
workspaceId: opts.workspaceId,
|
|
52
|
-
summary: "Session ended",
|
|
53
|
-
decisions: [],
|
|
54
|
-
actions: [],
|
|
55
|
-
}),
|
|
85
|
+
command: ".claude/hooks/mnotes-session-stop.sh",
|
|
56
86
|
},
|
|
57
87
|
],
|
|
58
88
|
},
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
export { generateHooksTemplate, HOOKS_HEADER } from "./hooks";
|
|
2
|
-
export type { HooksTemplateOpts, ClaudeCodeHooks, ClaudeCodeHook } from "./hooks";
|
|
1
|
+
export { generateHooksTemplate, generateHookScripts, HOOKS_HEADER } from "./hooks";
|
|
2
|
+
export type { HooksTemplateOpts, ClaudeCodeHooks, ClaudeCodeHook, HookScript } from "./hooks";
|
|
3
3
|
export { generateSkillTemplates } from "./skills";
|
|
4
4
|
export type { SkillTemplateOpts, SkillFile } from "./skills";
|
|
5
5
|
export { generateAgentTemplates } from "./agents";
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.generateAgentTemplates = exports.generateSkillTemplates = exports.HOOKS_HEADER = exports.generateHooksTemplate = void 0;
|
|
3
|
+
exports.generateAgentTemplates = exports.generateSkillTemplates = exports.HOOKS_HEADER = exports.generateHookScripts = exports.generateHooksTemplate = void 0;
|
|
4
4
|
var hooks_1 = require("./hooks");
|
|
5
5
|
Object.defineProperty(exports, "generateHooksTemplate", { enumerable: true, get: function () { return hooks_1.generateHooksTemplate; } });
|
|
6
|
+
Object.defineProperty(exports, "generateHookScripts", { enumerable: true, get: function () { return hooks_1.generateHookScripts; } });
|
|
6
7
|
Object.defineProperty(exports, "HOOKS_HEADER", { enumerable: true, get: function () { return hooks_1.HOOKS_HEADER; } });
|
|
7
8
|
var skills_1 = require("./skills");
|
|
8
9
|
Object.defineProperty(exports, "generateSkillTemplates", { enumerable: true, get: function () { return skills_1.generateSkillTemplates; } });
|
|
@@ -2,107 +2,131 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.generateClaudeCodeTemplate = generateClaudeCodeTemplate;
|
|
4
4
|
function generateClaudeCodeTemplate(opts) {
|
|
5
|
-
return
|
|
5
|
+
return `<!-- m-notes instructions v3 -->
|
|
6
|
+
# m-notes — Your Wiki (MANDATORY)
|
|
6
7
|
|
|
7
8
|
**Server**: ${opts.url}
|
|
8
9
|
**Workspace**: ${opts.workspaceId}
|
|
9
10
|
|
|
10
|
-
>
|
|
11
|
+
> You are not a reader of a scratchpad. You are the **author and maintainer of a living wiki**. Every session, three things compound: the wiki grows, links densify, contradictions get resolved. This is your persistent memory — without it you start from zero every conversation.
|
|
12
|
+
|
|
13
|
+
## The Three Layers
|
|
14
|
+
|
|
15
|
+
1. **Raw sources** — user messages, pasted docs, URLs, files. Immutable. You never edit these.
|
|
16
|
+
2. **The wiki** — your notes. You *write* these. They are interlinked with \`[[wikilinks]]\`, tagged for retrieval, and updated whenever sources change.
|
|
17
|
+
3. **Schema pages** — notes tagged \`type:config\` (or in a \`_schema/\` folder). The user's rules for how the wiki is organized. **Read these before editing the wiki.**
|
|
11
18
|
|
|
12
19
|
## Rules (follow strictly)
|
|
13
20
|
|
|
14
|
-
1. **ALWAYS recall before researching.** Before exploring the codebase
|
|
15
|
-
2. **ALWAYS store what you learn.**
|
|
16
|
-
3. **ALWAYS log sessions.**
|
|
17
|
-
4. **ALWAYS load context at session start.** Call \`project_context_load
|
|
21
|
+
1. **ALWAYS recall before researching.** Before exploring the codebase or making assumptions — call \`recall_knowledge\` and \`search_notes\`. Past you already figured things out.
|
|
22
|
+
2. **ALWAYS store what you learn.** Non-obvious discoveries → \`knowledge_store\` immediately. Anything worth re-finding belongs in the wiki.
|
|
23
|
+
3. **ALWAYS log sessions.** Call \`session_log\` before the session ends.
|
|
24
|
+
4. **ALWAYS load context at session start.** Call \`project_context_load\`. Use \`session_context_resume\` if mid-conversation.
|
|
25
|
+
5. **INGEST sources coherently.** When the user drops a URL, long paste, or file: don't just dump it into one note. Run the **ingest loop** (below).
|
|
26
|
+
6. **LINT the wiki periodically.** On session start for large workspaces, and after any ingest, run the **lint loop** (below).
|
|
27
|
+
7. **READ schema before editing.** Before creating or updating notes, call \`search_notes\` for \`type:config\` notes. Follow their conventions. If none exist, offer to create a starter schema note.
|
|
28
|
+
8. **EVERY note must link.** Outbound \`[[wikilinks]]\` are mandatory — an orphan note is invisible.
|
|
29
|
+
|
|
30
|
+
## The Ingest Loop
|
|
31
|
+
|
|
32
|
+
When the user supplies a source (URL, paste, file):
|
|
33
|
+
|
|
34
|
+
1. **Find related pages**: \`search_notes\` + \`query_graph\` to identify 3–15 existing notes affected.
|
|
35
|
+
2. **Plan the edits**: which existing notes need \`append_to_note\` / \`update_note\`, which new notes need \`create_note\`, and what \`[[wikilinks]]\` connect them.
|
|
36
|
+
3. **Apply**: execute the plan. Every touched note gets:
|
|
37
|
+
- A \`source/<slug>\` tag or a "Sources" section listing provenance
|
|
38
|
+
- At least one \`[[wikilink]]\` to another touched note
|
|
39
|
+
4. **Summarize**: tell the user which notes were created vs. updated.
|
|
40
|
+
|
|
41
|
+
A single source should rarely touch fewer than 3 notes — that's a sign you're treating the wiki as a dumping ground.
|
|
42
|
+
|
|
43
|
+
## The Lint Loop
|
|
44
|
+
|
|
45
|
+
Periodically (session start on large workspaces; after ingests):
|
|
46
|
+
|
|
47
|
+
- **Contradictions** → call \`scan_knowledge_conflicts\`. Resolve, don't stack.
|
|
48
|
+
- **Orphans** → \`query_note_graph\` for notes with zero inbound or outbound links. Link them or delete.
|
|
49
|
+
- **Broken wikilinks** → \`[[X]]\` targets that don't exist. Create a stub note or fix the link.
|
|
50
|
+
- **Stale** → notes untouched >90 days referenced by new sources. Update them.
|
|
51
|
+
|
|
52
|
+
Report findings as a short list; ask before bulk-deleting.
|
|
18
53
|
|
|
19
54
|
## When to Store Knowledge
|
|
20
55
|
|
|
21
|
-
Store
|
|
22
|
-
-
|
|
23
|
-
-
|
|
24
|
-
-
|
|
25
|
-
-
|
|
26
|
-
-
|
|
27
|
-
-
|
|
28
|
-
-
|
|
29
|
-
-
|
|
56
|
+
Store **proactively** — don't wait to be asked. Categories:
|
|
57
|
+
- Architecture decision → \`arch/{component}\`
|
|
58
|
+
- Code pattern or convention → \`pattern/{name}\`
|
|
59
|
+
- Debugged bug → \`bug/{id}\`
|
|
60
|
+
- Dependency quirk → \`dep/{package}\`
|
|
61
|
+
- Product/tech decision → \`decision/{topic}\`
|
|
62
|
+
- Domain understanding → \`context/{area}\`
|
|
63
|
+
- Gotcha or footgun → \`gotcha/{description}\`
|
|
64
|
+
- Completed task → \`task/{id}\`
|
|
65
|
+
|
|
66
|
+
**Promotion rule**: if a knowledge entry is recalled 3+ times, promote it to a full note with \`create_note\` — it has earned a wiki page.
|
|
30
67
|
|
|
31
68
|
## When to Recall Knowledge
|
|
32
69
|
|
|
33
|
-
Recall
|
|
34
|
-
-
|
|
35
|
-
-
|
|
36
|
-
-
|
|
37
|
-
-
|
|
38
|
-
-
|
|
39
|
-
-
|
|
40
|
-
|
|
41
|
-
## Notes
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
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... |
|
|
70
|
+
Recall **before acting**:
|
|
71
|
+
- Tech decision → \`arch/*\` + \`decision/*\`
|
|
72
|
+
- Touching a module → relevant \`pattern/*\` + \`context/*\`
|
|
73
|
+
- Debugging → \`bug/*\` for similar past issues
|
|
74
|
+
- Adding a dependency → \`dep/*\`
|
|
75
|
+
- Session start → \`project_context_load\` (automatic via SessionStart hook)
|
|
76
|
+
- User asks about past work → \`session_context_resume\`
|
|
77
|
+
|
|
78
|
+
## Notes vs. Knowledge Entries
|
|
79
|
+
|
|
80
|
+
In the wiki model, **notes are primary**. Knowledge entries are fast-capture for things that haven't earned a page yet.
|
|
81
|
+
|
|
82
|
+
| Use a **note** when... | Use \`knowledge_store\` when... |
|
|
60
83
|
|---|---|
|
|
61
|
-
| Content is long-form
|
|
62
|
-
|
|
|
63
|
-
|
|
|
64
|
-
| Meeting notes,
|
|
84
|
+
| Content is long-form or evolving | Content is a single fact |
|
|
85
|
+
| Needs \`[[wikilinks]]\` and backlinks | Key/tag retrieval is enough |
|
|
86
|
+
| Subject will be referenced repeatedly | One-shot capture |
|
|
87
|
+
| Meeting notes, investigations, design docs | Architecture decisions, gotchas |
|
|
88
|
+
|
|
89
|
+
**When in doubt, create a note.**
|
|
65
90
|
|
|
66
|
-
|
|
91
|
+
### Note Creation
|
|
92
|
+
- **Meetings / planning** → \`create_note\`
|
|
93
|
+
- **Investigations** → \`create_note\` then \`append_to_note\` as you go
|
|
94
|
+
- **Design / architecture** → full notes, not just knowledge entries
|
|
95
|
+
- **Task summaries** → after completing a story
|
|
96
|
+
- **Daily captures** → \`daily_note\`
|
|
67
97
|
|
|
68
|
-
|
|
98
|
+
### Note Maintenance
|
|
99
|
+
- \`append_to_note\` — add progress
|
|
100
|
+
- \`update_note\` — supersede stale content
|
|
101
|
+
- \`manage_tags\` / folder tools — keep notes findable
|
|
69
102
|
|
|
70
|
-
|
|
103
|
+
## Knowledge Graph — Non-Optional
|
|
71
104
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
- **
|
|
76
|
-
- **
|
|
77
|
-
- **
|
|
105
|
+
The graph is how the wiki stays navigable. **Every note you write or edit must have at least one outbound wikilink.**
|
|
106
|
+
|
|
107
|
+
### When to Build
|
|
108
|
+
- **Session start**: call \`populate_graph\` if the graph is empty (idempotent).
|
|
109
|
+
- **Architecture decisions**: create concept nodes, link with \`related\` / \`parent\`.
|
|
110
|
+
- **Dependency discovery**: create nodes for packages, link to components using them.
|
|
111
|
+
- **Bug investigations**: link bug nodes to affected components/patterns.
|
|
112
|
+
- **Any A↔B relationship**: create the edge. The graph compounds.
|
|
78
113
|
|
|
79
114
|
### Graph Tools
|
|
80
115
|
| Tool | When to use |
|
|
81
116
|
|------|------------|
|
|
82
|
-
| \`populate_graph\` | Initialize
|
|
83
|
-
| \`create_node\` |
|
|
84
|
-
| \`create_edge\` | Link two nodes (
|
|
85
|
-
| \`query_graph\` | Search
|
|
86
|
-
| \`get_neighbors\` | Explore
|
|
87
|
-
| \`query_note_graph\` | Get
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
- **tag** — represents a tag or category
|
|
92
|
-
- **concept** — free-form concept (architecture component, pattern, decision)
|
|
93
|
-
|
|
94
|
-
### Edge Types
|
|
95
|
-
- **wikilink** — note links to another note
|
|
96
|
-
- **related** — general relationship
|
|
97
|
-
- **parent** — hierarchical (component contains sub-component)
|
|
98
|
-
- **tagged** — node is tagged with a category
|
|
99
|
-
- **custom** — any other relationship (describe in metadata)
|
|
100
|
-
|
|
101
|
-
### Example: Mapping Architecture
|
|
117
|
+
| \`populate_graph\` | Initialize from existing notes (idempotent) |
|
|
118
|
+
| \`create_node\` | Add a concept/tag/note-linked node |
|
|
119
|
+
| \`create_edge\` | Link two nodes (wikilink, related, parent, tagged, custom) |
|
|
120
|
+
| \`query_graph\` | Search by type, label, or connectivity |
|
|
121
|
+
| \`get_neighbors\` | Explore connections from a node |
|
|
122
|
+
| \`query_note_graph\` | Get subgraph around a note |
|
|
123
|
+
|
|
124
|
+
Node types: **note**, **tag**, **concept**. Edge types: **wikilink**, **related**, **parent**, **tagged**, **custom**.
|
|
125
|
+
|
|
102
126
|
\`\`\`
|
|
103
|
-
create_node({ label: "Auth Module", nodeType: "concept", workspaceId: "
|
|
104
|
-
create_node({ label: "PostgreSQL", nodeType: "concept", workspaceId: "
|
|
105
|
-
create_edge({ sourceId: authNodeId, targetId: pgNodeId, edgeType: "related", workspaceId: "
|
|
127
|
+
create_node({ label: "Auth Module", nodeType: "concept", workspaceId: "${opts.workspaceId}" })
|
|
128
|
+
create_node({ label: "PostgreSQL", nodeType: "concept", workspaceId: "${opts.workspaceId}" })
|
|
129
|
+
create_edge({ sourceId: authNodeId, targetId: pgNodeId, edgeType: "related", workspaceId: "${opts.workspaceId}" })
|
|
106
130
|
\`\`\`
|
|
107
131
|
|
|
108
132
|
## MCP Tools Reference
|
|
@@ -110,49 +134,44 @@ create_edge({ sourceId: authNodeId, targetId: pgNodeId, edgeType: "related", wor
|
|
|
110
134
|
### Session & Context
|
|
111
135
|
| Tool | When to use |
|
|
112
136
|
|------|------------|
|
|
113
|
-
| \`project_context_load\` | Session start
|
|
114
|
-
| \`session_context_resume\` | Resume
|
|
115
|
-
| \`session_log\` | Log
|
|
137
|
+
| \`project_context_load\` | Session start |
|
|
138
|
+
| \`session_context_resume\` | Resume mid-conversation |
|
|
139
|
+
| \`session_log\` | Log summary at end |
|
|
116
140
|
|
|
117
|
-
### Knowledge (
|
|
141
|
+
### Knowledge (fast capture)
|
|
118
142
|
| Tool | When to use |
|
|
119
143
|
|------|------------|
|
|
120
|
-
| \`knowledge_store\` | Store a
|
|
121
|
-
| \`recall_knowledge\` | Semantic search
|
|
122
|
-
| \`bulk_knowledge_recall\` | Recall by tag
|
|
123
|
-
| \`knowledge_snapshot\` | Export all knowledge
|
|
144
|
+
| \`knowledge_store\` | Store a single fact |
|
|
145
|
+
| \`recall_knowledge\` | Semantic search |
|
|
146
|
+
| \`bulk_knowledge_recall\` | Recall by tag pattern (e.g., \`arch/*\`) |
|
|
147
|
+
| \`knowledge_snapshot\` | Export all knowledge |
|
|
148
|
+
| \`scan_knowledge_conflicts\` | Lint: find contradictions |
|
|
124
149
|
|
|
125
|
-
### Notes (
|
|
150
|
+
### Notes (wiki pages)
|
|
126
151
|
| Tool | When to use |
|
|
127
152
|
|------|------------|
|
|
128
|
-
| \`create_note\` |
|
|
129
|
-
| \`update_note\` | Replace
|
|
130
|
-
| \`append_to_note\` | Add
|
|
131
|
-
| \`get_note\` | Read
|
|
132
|
-
| \`
|
|
133
|
-
| \`
|
|
134
|
-
| \`
|
|
135
|
-
| \`
|
|
136
|
-
| \`
|
|
137
|
-
| \`pin_note\` / \`toggle_star\` | Pin or star important notes |
|
|
153
|
+
| \`create_note\` | New page |
|
|
154
|
+
| \`update_note\` | Replace content |
|
|
155
|
+
| \`append_to_note\` | Add to existing page |
|
|
156
|
+
| \`get_note\` / \`get_note_by_title\` | Read |
|
|
157
|
+
| \`search_notes\` | FTS + semantic search |
|
|
158
|
+
| \`list_notes\` | List by folder |
|
|
159
|
+
| \`daily_note\` | Today's capture note |
|
|
160
|
+
| \`manage_tags\` | Add/remove tags |
|
|
161
|
+
| \`pin_note\` / \`toggle_star\` | Elevate importance |
|
|
138
162
|
|
|
139
163
|
### Organization
|
|
140
164
|
| Tool | When to use |
|
|
141
165
|
|------|------------|
|
|
142
|
-
| \`list_folders\` |
|
|
143
|
-
| \`create_folder\` | Create a new folder |
|
|
144
|
-
| \`move_note\` | Move note to a different folder |
|
|
166
|
+
| \`list_folders\` / \`manage_folders\` / \`move_folder\` / \`bulk_move\` | Folder + note-move ops |
|
|
145
167
|
| \`context_fetch\` | Search notes by query |
|
|
146
168
|
|
|
147
169
|
### Knowledge Graph
|
|
148
170
|
| Tool | When to use |
|
|
149
171
|
|------|------------|
|
|
150
|
-
| \`populate_graph\` | Initialize graph
|
|
151
|
-
| \`create_node\`
|
|
152
|
-
| \`
|
|
153
|
-
| \`query_graph\` | Search graph by type, label, or connectivity |
|
|
154
|
-
| \`get_neighbors\` | Explore connections from a node |
|
|
155
|
-
| \`query_note_graph\` | Get subgraph around a specific note |
|
|
172
|
+
| \`populate_graph\` | Initialize graph |
|
|
173
|
+
| \`create_node\` / \`create_edge\` | Build structure |
|
|
174
|
+
| \`query_graph\` / \`get_neighbors\` / \`query_note_graph\` | Explore |
|
|
156
175
|
|
|
157
176
|
All tools require \`workspaceId: "${opts.workspaceId}"\`.`;
|
|
158
177
|
}
|
package/dist/templates/codex.js
CHANGED
|
@@ -2,63 +2,67 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.generateCodexTemplate = generateCodexTemplate;
|
|
4
4
|
function generateCodexTemplate(opts) {
|
|
5
|
-
return
|
|
5
|
+
return `<!-- m-notes instructions v3 -->
|
|
6
|
+
# m-notes — Your Wiki
|
|
6
7
|
|
|
7
8
|
Server: ${opts.url}
|
|
8
9
|
Workspace: ${opts.workspaceId}
|
|
9
10
|
|
|
11
|
+
You are the author and maintainer of a living wiki. Raw sources are immutable inputs; your notes are the wiki; notes tagged \`type:config\` are the rules you follow when editing.
|
|
12
|
+
|
|
10
13
|
## Session Lifecycle
|
|
11
14
|
|
|
12
15
|
### Session Start
|
|
13
|
-
Call \`project_context_load\` with workspaceId "${opts.workspaceId}"
|
|
14
|
-
To resume
|
|
16
|
+
- Call \`project_context_load\` with workspaceId "${opts.workspaceId}".
|
|
17
|
+
- To resume, call \`session_context_resume\`.
|
|
18
|
+
- If the graph is empty, call \`populate_graph\` (idempotent).
|
|
19
|
+
- Read any notes tagged \`type:config\` before editing.
|
|
15
20
|
|
|
16
21
|
### During Work
|
|
17
|
-
|
|
18
|
-
-
|
|
19
|
-
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
+
- Recall before researching: \`recall_knowledge\`, \`search_notes\`.
|
|
23
|
+
- Store discoveries via \`knowledge_store\` (key: \`<category>/<name>\`, tags: [category]).
|
|
24
|
+
- When the user supplies a source (URL/paste/file), run the **ingest loop**:
|
|
25
|
+
1. \`search_notes\` + \`query_graph\` to find 3–15 related notes
|
|
26
|
+
2. Plan creates/updates with \`[[wikilinks]]\` connecting touched notes
|
|
27
|
+
3. Apply — each touched note gets a \`source/<slug>\` tag
|
|
28
|
+
- Periodically run the **lint loop**: \`scan_knowledge_conflicts\`, find orphans via \`query_note_graph\`, fix broken \`[[wikilinks]]\`, update stale notes.
|
|
29
|
+
- Every new or edited note must have at least one outbound \`[[wikilink]]\`.
|
|
22
30
|
|
|
23
31
|
### Key Naming Conventions
|
|
24
|
-
- arch/{component} -- architecture decisions
|
|
32
|
+
- arch/{component} -- architecture decisions
|
|
25
33
|
- pattern/{name} -- code patterns and idioms
|
|
26
|
-
- bug/{id} -- bug investigations
|
|
27
|
-
- dep/{package} -- dependency notes
|
|
28
|
-
- decision/{topic} -- product/tech decisions
|
|
29
|
-
- context/{area} --
|
|
34
|
+
- bug/{id} -- bug investigations
|
|
35
|
+
- dep/{package} -- dependency notes
|
|
36
|
+
- decision/{topic} -- product/tech decisions
|
|
37
|
+
- context/{area} -- domain knowledge
|
|
30
38
|
|
|
31
39
|
### Session End
|
|
32
|
-
Call \`session_log\` with workspaceId "${opts.workspaceId}",
|
|
40
|
+
- Call \`session_log\` with workspaceId "${opts.workspaceId}", summary, decisions, actions.
|
|
33
41
|
|
|
34
42
|
## Knowledge Graph
|
|
35
43
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
### When to Build
|
|
39
|
-
- Session start: call \`populate_graph\` if graph is empty (idempotent)
|
|
40
|
-
- When you discover relationships between components, patterns, or decisions: create nodes and edges
|
|
41
|
-
- Architecture decisions: create concept nodes, link with "related" or "parent" edges
|
|
44
|
+
Every note belongs to the graph. Build relationships proactively.
|
|
42
45
|
|
|
43
|
-
|
|
44
|
-
|
|
46
|
+
- Node types: note, tag, concept
|
|
47
|
+
- Edge types: wikilink, related, parent, tagged, custom
|
|
45
48
|
|
|
46
49
|
Example:
|
|
47
50
|
create_node({ label: "Auth Module", nodeType: "concept", workspaceId: "${opts.workspaceId}" })
|
|
48
51
|
create_edge({ sourceId: "...", targetId: "...", edgeType: "related", workspaceId: "${opts.workspaceId}" })
|
|
49
52
|
|
|
50
53
|
## Available MCP Tools
|
|
51
|
-
- project_context_load -- load
|
|
52
|
-
- session_context_resume -- resume
|
|
53
|
-
- knowledge_store -- store knowledge
|
|
54
|
-
- recall_knowledge -- semantic search
|
|
55
|
-
- bulk_knowledge_recall -- recall by tag
|
|
54
|
+
- project_context_load -- load context at session start
|
|
55
|
+
- session_context_resume -- resume previous session
|
|
56
|
+
- knowledge_store -- store knowledge entry
|
|
57
|
+
- recall_knowledge -- semantic search
|
|
58
|
+
- bulk_knowledge_recall -- recall by tag pattern
|
|
56
59
|
- knowledge_snapshot -- export all knowledge
|
|
60
|
+
- scan_knowledge_conflicts -- lint for contradictions
|
|
57
61
|
- session_log -- log session summary
|
|
62
|
+
- create_note / update_note / append_to_note -- note authoring
|
|
63
|
+
- search_notes / get_note / list_notes -- note retrieval
|
|
58
64
|
- context_fetch -- search notes by query
|
|
59
|
-
- populate_graph -- initialize
|
|
60
|
-
- create_node
|
|
61
|
-
-
|
|
62
|
-
- query_graph -- search graph by type, label, or connectivity
|
|
63
|
-
- get_neighbors -- explore connections from a node`;
|
|
65
|
+
- populate_graph -- initialize graph (idempotent)
|
|
66
|
+
- create_node / create_edge -- build graph structure
|
|
67
|
+
- query_graph / get_neighbors / query_note_graph -- explore graph`;
|
|
64
68
|
}
|
|
@@ -2,11 +2,20 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.generateOpenClawTemplate = generateOpenClawTemplate;
|
|
4
4
|
function generateOpenClawTemplate(opts) {
|
|
5
|
-
return
|
|
5
|
+
return `<!-- m-notes instructions v3 -->
|
|
6
|
+
# m-notes — Your Wiki
|
|
6
7
|
|
|
7
8
|
**Server**: ${opts.url}
|
|
8
9
|
**Workspace**: ${opts.workspaceId}
|
|
9
10
|
|
|
11
|
+
You are the author of a living wiki. Notes are interlinked with \`[[wikilinks]]\`. Sources are immutable; your writing is the wiki.
|
|
12
|
+
|
|
13
|
+
## Core Loops
|
|
14
|
+
|
|
15
|
+
**Ingest** — when given a source (URL/paste): find 3+ related notes via \`recall_knowledge\`, update or create, link them with \`[[wikilinks]]\`, tag each with \`source/<slug>\`.
|
|
16
|
+
|
|
17
|
+
**Lint** — periodically check for contradictions, orphan notes, broken wikilinks, stale entries.
|
|
18
|
+
|
|
10
19
|
## Quick Reference
|
|
11
20
|
|
|
12
21
|
Store knowledge:
|
|
@@ -33,11 +42,11 @@ Call recall_knowledge with:
|
|
|
33
42
|
|
|
34
43
|
## Knowledge Graph
|
|
35
44
|
|
|
36
|
-
|
|
45
|
+
Every note should link to at least one other. Build the graph proactively:
|
|
37
46
|
\`\`\`
|
|
38
47
|
Call populate_graph with:
|
|
39
48
|
- workspaceId: "${opts.workspaceId}"
|
|
40
|
-
(initializes
|
|
49
|
+
(initializes from existing notes — idempotent)
|
|
41
50
|
|
|
42
51
|
Call create_node with:
|
|
43
52
|
- label: "<concept name>"
|
|
@@ -52,14 +61,14 @@ Call create_edge with:
|
|
|
52
61
|
\`\`\`
|
|
53
62
|
|
|
54
63
|
## Available MCP Tools
|
|
55
|
-
- \`knowledge_store\` -- Store knowledge
|
|
56
|
-
- \`recall_knowledge\` -- Semantic search
|
|
57
|
-
- \`bulk_knowledge_recall\` -- Recall by tag
|
|
64
|
+
- \`knowledge_store\` -- Store knowledge
|
|
65
|
+
- \`recall_knowledge\` -- Semantic search
|
|
66
|
+
- \`bulk_knowledge_recall\` -- Recall by tag pattern
|
|
58
67
|
- \`knowledge_snapshot\` -- Export all knowledge
|
|
59
|
-
- \`
|
|
60
|
-
- \`
|
|
61
|
-
- \`
|
|
62
|
-
- \`
|
|
63
|
-
- \`
|
|
64
|
-
- \`get_neighbors\` -- Explore
|
|
68
|
+
- \`scan_knowledge_conflicts\` -- Lint: find contradictions
|
|
69
|
+
- \`context_fetch\` / \`search_notes\` -- Search notes
|
|
70
|
+
- \`create_note\` / \`update_note\` / \`append_to_note\` -- Note authoring
|
|
71
|
+
- \`populate_graph\` -- Initialize graph
|
|
72
|
+
- \`create_node\` / \`create_edge\` -- Build graph structure
|
|
73
|
+
- \`query_graph\` / \`get_neighbors\` / \`query_note_graph\` -- Explore graph`;
|
|
65
74
|
}
|