@dinoxx/dinox-cli 1.0.18 → 1.0.20
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 +807 -418
- package/dist/auth/userInfo.d.ts +2 -0
- package/dist/auth/userInfo.js +28 -11
- package/dist/cli.js +15 -1
- package/dist/commands/auth/index.d.ts +2 -0
- package/dist/commands/auth/index.js +217 -163
- package/dist/commands/boxes/index.d.ts +2 -0
- package/dist/commands/boxes/index.js +119 -75
- package/dist/commands/boxes/repo.d.ts +8 -0
- package/dist/commands/boxes/repo.js +41 -0
- package/dist/commands/config/index.d.ts +2 -0
- package/dist/commands/config/index.js +85 -36
- package/dist/commands/descriptor/register.d.ts +8 -0
- package/dist/commands/descriptor/register.js +113 -0
- package/dist/commands/descriptor/types.d.ts +37 -0
- package/dist/commands/descriptor/types.js +17 -0
- package/dist/commands/graph/index.d.ts +2 -0
- package/dist/commands/graph/index.js +143 -73
- package/dist/commands/info/index.d.ts +2 -0
- package/dist/commands/info/index.js +41 -14
- package/dist/commands/mcp/index.js +11 -10
- package/dist/commands/mcp/server.js +8 -4
- package/dist/commands/mcp/tools.js +30 -104
- package/dist/commands/mcp/types.d.ts +8 -1
- package/dist/commands/meta/index.d.ts +2 -0
- package/dist/commands/meta/index.js +64 -33
- package/dist/commands/meta/repo.d.ts +6 -0
- package/dist/commands/meta/repo.js +6 -0
- package/dist/commands/notes/actions.d.ts +10 -0
- package/dist/commands/notes/actions.js +403 -0
- package/dist/commands/notes/descriptors.d.ts +4 -0
- package/dist/commands/notes/descriptors.js +250 -0
- package/dist/commands/notes/dryRun.d.ts +31 -0
- package/dist/commands/notes/dryRun.js +130 -0
- package/dist/commands/notes/index.d.ts +2 -51
- package/dist/commands/notes/index.js +2 -581
- package/dist/commands/notes/repo.d.ts +9 -0
- package/dist/commands/notes/repo.js +56 -10
- package/dist/commands/notes/runtime.d.ts +28 -0
- package/dist/commands/notes/runtime.js +185 -0
- package/dist/commands/notes/searchOutput.d.ts +10 -2
- package/dist/commands/notes/searchOutput.js +14 -2
- package/dist/commands/notes/searchResponse.d.ts +51 -0
- package/dist/commands/notes/searchResponse.js +56 -0
- package/dist/commands/notes/searchSql.js +2 -0
- package/dist/commands/prompt/index.d.ts +2 -0
- package/dist/commands/prompt/index.js +122 -72
- package/dist/commands/prompt/repo.d.ts +7 -0
- package/dist/commands/prompt/repo.js +46 -0
- package/dist/commands/schema/commandMetadata.d.ts +2 -0
- package/dist/commands/schema/commandMetadata.js +18 -0
- package/dist/commands/schema/globalOptions.d.ts +3 -0
- package/dist/commands/schema/globalOptions.js +3 -0
- package/dist/commands/schema/index.d.ts +4 -0
- package/dist/commands/schema/index.js +109 -0
- package/dist/commands/schema/renderSkills.d.ts +6 -0
- package/dist/commands/schema/renderSkills.js +216 -0
- package/dist/commands/schema/spec.d.ts +6 -0
- package/dist/commands/schema/spec.js +40 -0
- package/dist/commands/schema/types.d.ts +47 -0
- package/dist/commands/schema/types.js +1 -0
- package/dist/commands/storage/index.d.ts +4 -0
- package/dist/commands/storage/index.js +292 -0
- package/dist/commands/storage/repo.d.ts +162 -0
- package/dist/commands/storage/repo.js +770 -0
- package/dist/commands/sync.d.ts +2 -0
- package/dist/commands/sync.js +41 -27
- package/dist/commands/tags/index.d.ts +2 -0
- package/dist/commands/tags/index.js +121 -79
- package/dist/commands/tags/repo.d.ts +7 -0
- package/dist/commands/tags/repo.js +52 -0
- package/dist/commands/todo/dryRun.d.ts +23 -0
- package/dist/commands/todo/dryRun.js +133 -0
- package/dist/commands/todo/index.d.ts +2 -0
- package/dist/commands/todo/index.js +285 -135
- package/dist/commands/todo/repo.d.ts +11 -0
- package/dist/commands/todo/repo.js +37 -9
- package/dist/commands/update.d.ts +3 -0
- package/dist/commands/update.js +35 -13
- package/dist/commands/writeDryRun.d.ts +62 -0
- package/dist/commands/writeDryRun.js +131 -0
- package/dist/config/paths.d.ts +2 -1
- package/dist/config/paths.js +11 -1
- package/dist/config/resolve.js +7 -5
- package/dist/constants/links.d.ts +1 -1
- package/dist/constants/links.js +1 -1
- package/dist/daemon/ensure.js +3 -2
- package/dist/daemon/index.js +17 -15
- package/dist/dinox.js +9 -13
- package/dist/powersync/runtime.d.ts +1 -1
- package/dist/powersync/runtime.js +5 -3
- package/dist/powersync/schema/index.d.ts +27 -0
- package/dist/powersync/schema/index.js +2 -0
- package/dist/powersync/schema/note.d.ts +1 -0
- package/dist/powersync/schema/note.js +1 -0
- package/dist/powersync/schema/projects.d.ts +3 -0
- package/dist/powersync/schema/projects.js +3 -0
- package/dist/powersync/schema/storage.d.ts +24 -0
- package/dist/powersync/schema/storage.js +24 -0
- package/dist/skills/check.d.ts +6 -0
- package/dist/skills/check.js +254 -0
- package/dist/utils/bidlinkCanonicalization.d.ts +2 -0
- package/dist/utils/bidlinkCanonicalization.js +161 -0
- package/dist/utils/dryRun.d.ts +13 -0
- package/dist/utils/dryRun.js +69 -0
- package/dist/utils/output.d.ts +19 -0
- package/dist/utils/output.js +155 -1
- package/dist/utils/terminalSanitization.d.ts +2 -0
- package/dist/utils/terminalSanitization.js +33 -0
- package/dist/utils/tiptapMarkdown.d.ts +4 -1
- package/dist/utils/tiptapMarkdown.js +285 -113
- package/dist/utils/tiptapMarkdownSchema.d.ts +37 -0
- package/dist/utils/tiptapMarkdownSchema.js +577 -0
- package/dist/utils/tiptapMarkdownSchemaUtils.d.ts +74 -0
- package/dist/utils/tiptapMarkdownSchemaUtils.js +394 -0
- package/dist/utils/updateNotice.d.ts +7 -0
- package/dist/utils/updateNotice.js +64 -0
- package/package.json +25 -3
|
@@ -1,12 +1,15 @@
|
|
|
1
|
-
import { printYaml } from '../../utils/output.js';
|
|
2
|
-
import { sendDaemonRequest } from '../../daemon/client.js';
|
|
3
|
-
import { ensureDaemonRunning } from '../../daemon/ensure.js';
|
|
4
|
-
import { DinoxError } from '../../utils/errors.js';
|
|
5
1
|
import { loadConfig } from '../../config/store.js';
|
|
6
2
|
import { resolveConfig } from '../../config/resolve.js';
|
|
7
3
|
import { connectPowerSync } from '../../powersync/runtime.js';
|
|
4
|
+
import { sendDaemonRequest } from '../../daemon/client.js';
|
|
5
|
+
import { ensureDaemonRunning } from '../../daemon/ensure.js';
|
|
6
|
+
import { registerCommandGroup } from '../descriptor/register.js';
|
|
7
|
+
import { FORMAT_OPTIONS, OFFLINE_OPTIONS, SYNC_TIMEOUT_OPTIONS } from '../schema/globalOptions.js';
|
|
8
|
+
import { DinoxError } from '../../utils/errors.js';
|
|
9
|
+
import { printSuccess } from '../../utils/output.js';
|
|
8
10
|
import { parseSyncTimeoutMs } from '../../utils/syncTimeout.js';
|
|
9
11
|
import { getBacklinks, getGraphStats, getOutlinks, getRelatedNotes } from './repo.js';
|
|
12
|
+
const GRAPH_GLOBAL_OPTIONS = [...FORMAT_OPTIONS, ...OFFLINE_OPTIONS, ...SYNC_TIMEOUT_OPTIONS];
|
|
10
13
|
function parseDepth(value) {
|
|
11
14
|
if (value === undefined || value === null || value === '') {
|
|
12
15
|
return 2;
|
|
@@ -26,8 +29,7 @@ function parseDepth(value) {
|
|
|
26
29
|
}
|
|
27
30
|
async function withOfflineDb(command, fn) {
|
|
28
31
|
const globals = command.optsWithGlobals?.() ?? {};
|
|
29
|
-
|
|
30
|
-
if (!offline) {
|
|
32
|
+
if (!Boolean(globals.offline)) {
|
|
31
33
|
throw new DinoxError('withOfflineDb called without --offline');
|
|
32
34
|
}
|
|
33
35
|
const config = resolveConfig(await loadConfig());
|
|
@@ -40,74 +42,142 @@ async function withOfflineDb(command, fn) {
|
|
|
40
42
|
await db.close().catch(() => undefined);
|
|
41
43
|
}
|
|
42
44
|
}
|
|
43
|
-
|
|
44
|
-
const
|
|
45
|
-
|
|
46
|
-
.command
|
|
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
|
-
|
|
45
|
+
async function runGraphCommand(context, onlineAction, offlineAction) {
|
|
46
|
+
const globals = context.globals;
|
|
47
|
+
if (Boolean(globals.offline)) {
|
|
48
|
+
printSuccess(await withOfflineDb(context.command, offlineAction));
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
await ensureDaemonRunning();
|
|
52
|
+
printSuccess(await onlineAction());
|
|
53
|
+
}
|
|
54
|
+
const graphBacklinksCommand = {
|
|
55
|
+
name: 'backlinks',
|
|
56
|
+
description: 'Get notes that link to a target note',
|
|
57
|
+
arguments: [{ syntax: '<id>', description: 'Note id' }],
|
|
58
|
+
schema: {
|
|
59
|
+
path: 'graph.backlinks',
|
|
60
|
+
risk: 'read',
|
|
61
|
+
auth: 'required',
|
|
62
|
+
globalOptions: GRAPH_GLOBAL_OPTIONS,
|
|
63
|
+
output: {
|
|
64
|
+
type: 'structured',
|
|
65
|
+
summary: 'Backlink graph payload',
|
|
66
|
+
fields: ['note_id', 'backlinks'],
|
|
67
|
+
},
|
|
68
|
+
mcp: {
|
|
69
|
+
toolName: 'dinox_graph_backlinks',
|
|
70
|
+
description: 'Get notes that link to this note',
|
|
71
|
+
properties: [{ name: 'id', type: 'string', description: 'Note ID', required: true }],
|
|
72
|
+
},
|
|
73
|
+
examples: ['dino graph backlinks <id> --format json'],
|
|
74
|
+
},
|
|
75
|
+
action: async (context) => {
|
|
76
|
+
const id = context.args.id;
|
|
77
|
+
if (!id) {
|
|
78
|
+
throw new DinoxError('Note id is required');
|
|
72
79
|
}
|
|
73
|
-
await
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
80
|
+
await runGraphCommand(context, () => sendDaemonRequest('graph.backlinks', { id }), (db) => getBacklinks(db, id));
|
|
81
|
+
},
|
|
82
|
+
};
|
|
83
|
+
const graphOutlinksCommand = {
|
|
84
|
+
name: 'outlinks',
|
|
85
|
+
description: 'Get notes that this note links to',
|
|
86
|
+
arguments: [{ syntax: '<id>', description: 'Note id' }],
|
|
87
|
+
schema: {
|
|
88
|
+
path: 'graph.outlinks',
|
|
89
|
+
risk: 'read',
|
|
90
|
+
auth: 'required',
|
|
91
|
+
globalOptions: GRAPH_GLOBAL_OPTIONS,
|
|
92
|
+
output: {
|
|
93
|
+
type: 'structured',
|
|
94
|
+
summary: 'Outlink graph payload',
|
|
95
|
+
fields: ['note_id', 'outlinks'],
|
|
96
|
+
},
|
|
97
|
+
mcp: {
|
|
98
|
+
toolName: 'dinox_graph_outlinks',
|
|
99
|
+
description: 'Get notes that this note links to',
|
|
100
|
+
properties: [{ name: 'id', type: 'string', description: 'Note ID', required: true }],
|
|
101
|
+
},
|
|
102
|
+
examples: ['dino graph outlinks <id> --format json'],
|
|
103
|
+
},
|
|
104
|
+
action: async (context) => {
|
|
105
|
+
const id = context.args.id;
|
|
106
|
+
if (!id) {
|
|
107
|
+
throw new DinoxError('Note id is required');
|
|
87
108
|
}
|
|
88
|
-
await
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
.
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
109
|
+
await runGraphCommand(context, () => sendDaemonRequest('graph.outlinks', { id }), (db) => getOutlinks(db, id));
|
|
110
|
+
},
|
|
111
|
+
};
|
|
112
|
+
const graphRelatedCommand = {
|
|
113
|
+
name: 'related',
|
|
114
|
+
description: 'Get notes related within N degrees of separation',
|
|
115
|
+
arguments: [{ syntax: '<id>', description: 'Note id' }],
|
|
116
|
+
options: [{ flags: '--depth <n>', description: 'Degrees of separation (1-5)', defaultValue: '2' }],
|
|
117
|
+
schema: {
|
|
118
|
+
path: 'graph.related',
|
|
119
|
+
risk: 'read',
|
|
120
|
+
auth: 'required',
|
|
121
|
+
globalOptions: GRAPH_GLOBAL_OPTIONS,
|
|
122
|
+
output: {
|
|
123
|
+
type: 'structured',
|
|
124
|
+
summary: 'Related-notes graph payload',
|
|
125
|
+
fields: ['note_id', 'depth', 'related_notes'],
|
|
126
|
+
},
|
|
127
|
+
mcp: {
|
|
128
|
+
toolName: 'dinox_graph_related',
|
|
129
|
+
description: 'Get related notes within N degrees',
|
|
130
|
+
properties: [
|
|
131
|
+
{ name: 'id', type: 'string', description: 'Note ID', required: true },
|
|
132
|
+
{ name: 'depth', type: 'number', description: 'Degrees (1-5, default: 2)' },
|
|
133
|
+
],
|
|
134
|
+
},
|
|
135
|
+
examples: ['dino graph related <id> --depth 2 --format json'],
|
|
136
|
+
},
|
|
137
|
+
action: async (context) => {
|
|
138
|
+
const id = context.args.id;
|
|
139
|
+
if (!id) {
|
|
140
|
+
throw new DinoxError('Note id is required');
|
|
105
141
|
}
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
142
|
+
const depth = parseDepth(context.options.depth);
|
|
143
|
+
await runGraphCommand(context, () => sendDaemonRequest('graph.related', { id, depth }), (db) => getRelatedNotes(db, id, depth));
|
|
144
|
+
},
|
|
145
|
+
};
|
|
146
|
+
const graphStatsCommand = {
|
|
147
|
+
name: 'stats',
|
|
148
|
+
description: 'Get graph statistics for notes and links',
|
|
149
|
+
schema: {
|
|
150
|
+
path: 'graph.stats',
|
|
151
|
+
risk: 'read',
|
|
152
|
+
auth: 'required',
|
|
153
|
+
globalOptions: GRAPH_GLOBAL_OPTIONS,
|
|
154
|
+
output: {
|
|
155
|
+
type: 'structured',
|
|
156
|
+
summary: 'Graph statistics payload',
|
|
157
|
+
entity: 'graph_stats',
|
|
158
|
+
fields: ['notes_count', 'links_count', 'connected_notes_count', 'isolated_notes_count'],
|
|
159
|
+
},
|
|
160
|
+
mcp: {
|
|
161
|
+
toolName: 'dinox_graph_stats',
|
|
162
|
+
description: 'Get graph statistics for notes and links',
|
|
163
|
+
properties: [],
|
|
164
|
+
},
|
|
165
|
+
examples: ['dino graph stats --format json'],
|
|
166
|
+
},
|
|
167
|
+
action: async (context) => {
|
|
168
|
+
await runGraphCommand(context, () => sendDaemonRequest('graph.stats'), (db) => getGraphStats(db));
|
|
169
|
+
},
|
|
170
|
+
};
|
|
171
|
+
export const graphCommandGroup = {
|
|
172
|
+
name: 'graph',
|
|
173
|
+
description: 'Query knowledge graph',
|
|
174
|
+
commands: [
|
|
175
|
+
graphBacklinksCommand,
|
|
176
|
+
graphOutlinksCommand,
|
|
177
|
+
graphRelatedCommand,
|
|
178
|
+
graphStatsCommand,
|
|
179
|
+
],
|
|
180
|
+
};
|
|
181
|
+
export function registerGraphCommands(program) {
|
|
182
|
+
registerCommandGroup(program, graphCommandGroup);
|
|
113
183
|
}
|
|
@@ -1,23 +1,50 @@
|
|
|
1
|
+
import { registerStandaloneCommand } from '../descriptor/register.js';
|
|
2
|
+
import { FORMAT_OPTIONS } from '../schema/globalOptions.js';
|
|
1
3
|
import { SKILLS_REPO_URL } from '../../constants/links.js';
|
|
2
|
-
import {
|
|
4
|
+
import { clearPendingStructuredNoticeProvider, getRequestedStructuredOutputFormat, printHumanReadableLine, printSuccess, setPendingStructuredNoticeProvider, } from '../../utils/output.js';
|
|
5
|
+
import { getUpdateNotice } from '../../utils/updateNotice.js';
|
|
3
6
|
import { getPackageVersion } from '../../utils/version.js';
|
|
4
|
-
export
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
7
|
+
export const infoCommandDescriptor = {
|
|
8
|
+
name: 'info',
|
|
9
|
+
description: 'Show CLI version and bundled skills location',
|
|
10
|
+
schema: {
|
|
11
|
+
path: 'info',
|
|
12
|
+
risk: 'read',
|
|
13
|
+
auth: 'none',
|
|
14
|
+
globalOptions: FORMAT_OPTIONS,
|
|
15
|
+
output: {
|
|
16
|
+
type: 'structured',
|
|
17
|
+
summary: 'CLI version metadata',
|
|
18
|
+
fields: ['name', 'version', 'skills_repo'],
|
|
19
|
+
},
|
|
20
|
+
examples: ['dino info', 'dino info --format json'],
|
|
21
|
+
},
|
|
22
|
+
action: async (_context) => {
|
|
23
|
+
const version = getPackageVersion();
|
|
11
24
|
const payload = {
|
|
12
25
|
name: 'dino',
|
|
13
|
-
version
|
|
26
|
+
version,
|
|
14
27
|
skills_repo: SKILLS_REPO_URL,
|
|
15
28
|
};
|
|
16
|
-
if (
|
|
17
|
-
|
|
29
|
+
if (getRequestedStructuredOutputFormat()) {
|
|
30
|
+
const updateNotice = await getUpdateNotice(version);
|
|
31
|
+
setPendingStructuredNoticeProvider(() => updateNotice
|
|
32
|
+
? {
|
|
33
|
+
update: updateNotice,
|
|
34
|
+
}
|
|
35
|
+
: null);
|
|
36
|
+
try {
|
|
37
|
+
printSuccess(payload);
|
|
38
|
+
}
|
|
39
|
+
finally {
|
|
40
|
+
clearPendingStructuredNoticeProvider();
|
|
41
|
+
}
|
|
18
42
|
return;
|
|
19
43
|
}
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
}
|
|
44
|
+
printHumanReadableLine(payload.version);
|
|
45
|
+
printHumanReadableLine(`skills repo: ${payload.skills_repo}`);
|
|
46
|
+
},
|
|
47
|
+
};
|
|
48
|
+
export function registerInfoCommand(program) {
|
|
49
|
+
registerStandaloneCommand(program, infoCommandDescriptor);
|
|
23
50
|
}
|
|
@@ -3,6 +3,7 @@ import { mcpTools } from './tools.js';
|
|
|
3
3
|
import { isLoopbackHost, resolveMCPAuthToken } from './auth.js';
|
|
4
4
|
import { DEFAULT_MCP_PORT } from './types.js';
|
|
5
5
|
import { DinoxError } from '../../utils/errors.js';
|
|
6
|
+
import { printHumanReadableLine } from '../../utils/output.js';
|
|
6
7
|
function parsePort(value) {
|
|
7
8
|
if (value === undefined || value === null || value === '') {
|
|
8
9
|
return DEFAULT_MCP_PORT;
|
|
@@ -49,20 +50,20 @@ export function registerMCPCommands(program) {
|
|
|
49
50
|
if (!loopback && !token) {
|
|
50
51
|
throw new DinoxError('--token (or DINOX_MCP_TOKEN) is required when --host is non-loopback');
|
|
51
52
|
}
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
53
|
+
printHumanReadableLine('Starting Dinox MCP server...');
|
|
54
|
+
printHumanReadableLine(`Host: ${host}`);
|
|
55
|
+
printHumanReadableLine(`Port: ${port}`);
|
|
56
|
+
printHumanReadableLine(`Auth: ${token ? 'enabled' : 'disabled (loopback only)'}`);
|
|
57
|
+
printHumanReadableLine('Protocol: Model Context Protocol (MCP)');
|
|
57
58
|
if (!loopback) {
|
|
58
|
-
|
|
59
|
+
printHumanReadableLine('warning: binding to a non-loopback host may expose your local data; use firewall/auth as needed');
|
|
59
60
|
}
|
|
60
|
-
|
|
61
|
-
|
|
61
|
+
printHumanReadableLine('');
|
|
62
|
+
printHumanReadableLine('Available tools:');
|
|
62
63
|
for (const tool of mcpTools) {
|
|
63
|
-
|
|
64
|
+
printHumanReadableLine(` - ${tool.name}`);
|
|
64
65
|
}
|
|
65
|
-
|
|
66
|
+
printHumanReadableLine('');
|
|
66
67
|
const { server, mcpServer } = await startMCPServer(port, host, token);
|
|
67
68
|
let shuttingDown = false;
|
|
68
69
|
const shutdown = async () => {
|
|
@@ -6,6 +6,7 @@ import { resolveAuthIdentity } from '../../auth/userInfo.js';
|
|
|
6
6
|
import { loadConfig } from '../../config/store.js';
|
|
7
7
|
import { resolveConfig } from '../../config/resolve.js';
|
|
8
8
|
import { connectPowerSync } from '../../powersync/runtime.js';
|
|
9
|
+
import { canonicalizeBidlinksInMarkdown } from '../../utils/bidlinkCanonicalization.js';
|
|
9
10
|
import { parseCreatedAtRange } from '../notes/searchTime.js';
|
|
10
11
|
import { parseNoteSearchFields, projectNoteSearchFields } from '../notes/searchOutput.js';
|
|
11
12
|
import { createNote, getNoteDetails, resolveZettelBoxIdsForCreate, resolveZettelBoxIdsForSearch, searchNotes, validateTagsForCreate, } from '../notes/repo.js';
|
|
@@ -13,6 +14,7 @@ import { getBacklinks, getGraphStats, getOutlinks, getRelatedNotes } from '../gr
|
|
|
13
14
|
import { searchTodoTasks } from '../todo/repo.js';
|
|
14
15
|
import { convertMarkdownToNoteContent } from '../../utils/tiptapMarkdown.js';
|
|
15
16
|
import { generateId } from '../../utils/id.js';
|
|
17
|
+
import { printHumanReadableLine } from '../../utils/output.js';
|
|
16
18
|
import { getPackageVersion } from '../../utils/version.js';
|
|
17
19
|
const MAX_MCP_REQUEST_BYTES = 1 * 1024 * 1024;
|
|
18
20
|
const DEFAULT_SEARCH_LIMIT = 50;
|
|
@@ -214,7 +216,8 @@ export class MCPServer {
|
|
|
214
216
|
case 'dinox_search_notes': {
|
|
215
217
|
const keyword = typeof args.query === 'string' ? args.query : '';
|
|
216
218
|
const tagsExpression = typeof args.tags === 'string' ? args.tags.trim() : '';
|
|
217
|
-
const
|
|
219
|
+
const rawBoxes = args.boxes ?? args.box;
|
|
220
|
+
const { names: boxNames, matchEmpty: matchEmptyBoxes } = parseBoxFilter(rawBoxes);
|
|
218
221
|
const zettelBoxIds = await resolveZettelBoxIdsForSearch(this.db, boxNames);
|
|
219
222
|
const daysInput = typeof args.days === 'number' ? String(args.days) : args.days;
|
|
220
223
|
const { createdAtFrom, createdAtTo } = parseCreatedAtRange({
|
|
@@ -269,7 +272,8 @@ export class MCPServer {
|
|
|
269
272
|
const rawConfig = await loadConfig();
|
|
270
273
|
const config = resolveConfig(rawConfig);
|
|
271
274
|
const identity = await resolveAuthIdentity(rawConfig, config);
|
|
272
|
-
const
|
|
275
|
+
const canonicalContentMd = await canonicalizeBidlinksInMarkdown(this.db, contentMd, identity.userId);
|
|
276
|
+
const { contentJson, contentText } = convertMarkdownToNoteContent(canonicalContentMd);
|
|
273
277
|
const tags = await validateTagsForCreate(this.db, normalizeStringArray(args.tags));
|
|
274
278
|
const zettelBoxIds = await resolveZettelBoxIdsForCreate(this.db, normalizeStringArray(args.boxes));
|
|
275
279
|
const id = generateId();
|
|
@@ -277,7 +281,7 @@ export class MCPServer {
|
|
|
277
281
|
id,
|
|
278
282
|
title,
|
|
279
283
|
noteType: 'crawl',
|
|
280
|
-
contentMd,
|
|
284
|
+
contentMd: canonicalContentMd,
|
|
281
285
|
contentText,
|
|
282
286
|
contentJson,
|
|
283
287
|
tags,
|
|
@@ -386,7 +390,7 @@ export async function startMCPServer(port = DEFAULT_MCP_PORT, host = '127.0.0.1'
|
|
|
386
390
|
server.headersTimeout = 5_000;
|
|
387
391
|
server.requestTimeout = 10_000;
|
|
388
392
|
server.listen(port, host, () => {
|
|
389
|
-
|
|
393
|
+
printHumanReadableLine(`MCP server listening on http://${host}:${port}`);
|
|
390
394
|
});
|
|
391
395
|
return { server, mcpServer };
|
|
392
396
|
}
|
|
@@ -1,104 +1,30 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
type: '
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
},
|
|
32
|
-
required: ['id'],
|
|
33
|
-
},
|
|
34
|
-
},
|
|
35
|
-
{
|
|
36
|
-
name: 'dinox_create_note',
|
|
37
|
-
description: 'Create a new note',
|
|
38
|
-
inputSchema: {
|
|
39
|
-
type: 'object',
|
|
40
|
-
properties: {
|
|
41
|
-
title: { type: 'string', description: 'Note title' },
|
|
42
|
-
content: { type: 'string', description: 'Markdown content' },
|
|
43
|
-
tags: { type: 'array', items: { type: 'string' }, description: 'Tags' },
|
|
44
|
-
boxes: { type: 'array', items: { type: 'string' }, description: 'Zettel boxes' },
|
|
45
|
-
},
|
|
46
|
-
required: ['title', 'content'],
|
|
47
|
-
},
|
|
48
|
-
},
|
|
49
|
-
{
|
|
50
|
-
name: 'dinox_graph_backlinks',
|
|
51
|
-
description: 'Get notes that link to this note',
|
|
52
|
-
inputSchema: {
|
|
53
|
-
type: 'object',
|
|
54
|
-
properties: {
|
|
55
|
-
id: { type: 'string', description: 'Note ID' },
|
|
56
|
-
},
|
|
57
|
-
required: ['id'],
|
|
58
|
-
},
|
|
59
|
-
},
|
|
60
|
-
{
|
|
61
|
-
name: 'dinox_graph_outlinks',
|
|
62
|
-
description: 'Get notes that this note links to',
|
|
63
|
-
inputSchema: {
|
|
64
|
-
type: 'object',
|
|
65
|
-
properties: {
|
|
66
|
-
id: { type: 'string', description: 'Note ID' },
|
|
67
|
-
},
|
|
68
|
-
required: ['id'],
|
|
69
|
-
},
|
|
70
|
-
},
|
|
71
|
-
{
|
|
72
|
-
name: 'dinox_graph_related',
|
|
73
|
-
description: 'Get related notes within N degrees',
|
|
74
|
-
inputSchema: {
|
|
75
|
-
type: 'object',
|
|
76
|
-
properties: {
|
|
77
|
-
id: { type: 'string', description: 'Note ID' },
|
|
78
|
-
depth: { type: 'number', description: 'Degrees (1-5, default: 2)' },
|
|
79
|
-
},
|
|
80
|
-
required: ['id'],
|
|
81
|
-
},
|
|
82
|
-
},
|
|
83
|
-
{
|
|
84
|
-
name: 'dinox_graph_stats',
|
|
85
|
-
description: 'Get graph statistics for notes and links',
|
|
86
|
-
inputSchema: {
|
|
87
|
-
type: 'object',
|
|
88
|
-
properties: {},
|
|
89
|
-
},
|
|
90
|
-
},
|
|
91
|
-
{
|
|
92
|
-
name: 'dinox_search_todos',
|
|
93
|
-
description: 'Search todo tasks',
|
|
94
|
-
inputSchema: {
|
|
95
|
-
type: 'object',
|
|
96
|
-
properties: {
|
|
97
|
-
query: { type: 'string', description: 'Task text query' },
|
|
98
|
-
status: { type: 'string', enum: ['all', 'completed', 'uncompleted'], description: 'Task status' },
|
|
99
|
-
tags: { type: 'array', items: { type: 'string' }, description: 'Task tags' },
|
|
100
|
-
limit: { type: 'number', description: 'Max results (default: 50)' },
|
|
101
|
-
},
|
|
102
|
-
},
|
|
103
|
-
},
|
|
104
|
-
];
|
|
1
|
+
import { listMcpCommandSchemas } from '../schema/spec.js';
|
|
2
|
+
function toMcpProperty(property) {
|
|
3
|
+
if (property.type === 'string[]') {
|
|
4
|
+
return {
|
|
5
|
+
type: 'array',
|
|
6
|
+
items: { type: 'string' },
|
|
7
|
+
...(property.description ? { description: property.description } : {}),
|
|
8
|
+
...(property.enum ? { enum: property.enum } : {}),
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
return {
|
|
12
|
+
type: property.type,
|
|
13
|
+
...(property.description ? { description: property.description } : {}),
|
|
14
|
+
...(property.enum ? { enum: property.enum } : {}),
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
export const mcpTools = listMcpCommandSchemas().map((schema) => {
|
|
18
|
+
const mcp = schema.mcp;
|
|
19
|
+
const properties = Object.fromEntries(mcp.properties.map((property) => [property.name, toMcpProperty(property)]));
|
|
20
|
+
const required = mcp.properties.filter((property) => property.required).map((property) => property.name);
|
|
21
|
+
return {
|
|
22
|
+
name: mcp.toolName,
|
|
23
|
+
description: mcp.description,
|
|
24
|
+
inputSchema: {
|
|
25
|
+
type: 'object',
|
|
26
|
+
properties,
|
|
27
|
+
...(required.length > 0 ? { required } : {}),
|
|
28
|
+
},
|
|
29
|
+
};
|
|
30
|
+
});
|
|
@@ -3,7 +3,14 @@ export type MCPTool = {
|
|
|
3
3
|
description: string;
|
|
4
4
|
inputSchema: {
|
|
5
5
|
type: 'object';
|
|
6
|
-
properties: Record<string,
|
|
6
|
+
properties: Record<string, {
|
|
7
|
+
type: 'string' | 'number' | 'boolean' | 'array';
|
|
8
|
+
description?: string;
|
|
9
|
+
enum?: string[];
|
|
10
|
+
items?: {
|
|
11
|
+
type: 'string';
|
|
12
|
+
};
|
|
13
|
+
}>;
|
|
7
14
|
required?: string[];
|
|
8
15
|
};
|
|
9
16
|
};
|