naider 1.24.5 → 2.0.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/mcp/server.js CHANGED
@@ -1,16 +1,37 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- import { createInterface } from 'readline';
4
- import { resolve, dirname } from 'path';
3
+ import { resolve, dirname, isAbsolute } from 'path';
5
4
  import { readFileSync } from 'fs';
6
- import { fileURLToPath } from 'url';
5
+ import { fileURLToPath, pathToFileURL } from 'url';
6
+ import { createRequire } from 'module';
7
+ import { checkNaideSource, getDoctorReport, inspectProject, listPackageAdapters, runProjectTests } from '../src/project-tools.js';
7
8
 
8
9
  const __dirname = dirname(fileURLToPath(import.meta.url));
10
+ const requireFromServer = createRequire(import.meta.url);
11
+
12
+ function resolveRunImport(specifier) {
13
+ if (specifier.startsWith('.') || isAbsolute(specifier)) {
14
+ const path = isAbsolute(specifier) ? specifier : resolve(process.cwd(), specifier);
15
+ return pathToFileURL(path).href;
16
+ }
17
+ const requireFromProject = createRequire(resolve(process.cwd(), '__naide_mcp_run__.cjs'));
18
+ try {
19
+ const path = requireFromProject.resolve(specifier);
20
+ return isAbsolute(path) ? pathToFileURL(path).href : path;
21
+ } catch (projectError) {
22
+ try {
23
+ const path = requireFromServer.resolve(specifier);
24
+ return isAbsolute(path) ? pathToFileURL(path).href : path;
25
+ } catch {
26
+ throw projectError;
27
+ }
28
+ }
29
+ }
9
30
 
10
31
  let compileModule = null;
11
32
  async function getCompiler() {
12
33
  if (!compileModule) {
13
- compileModule = await import(resolve(__dirname, '..', 'src', 'index.js'));
34
+ compileModule = await import(pathToFileURL(resolve(__dirname, '..', 'src', 'index.js')).href);
14
35
  }
15
36
  return compileModule;
16
37
  }
@@ -33,7 +54,7 @@ function respondError(id, code, message) {
33
54
  const TOOLS = [
34
55
  {
35
56
  name: 'naide_compile',
36
- description: 'Compile NAIDE code to a target language. NAIDE is an AI-optimized language that transpiles to 15 targets.',
57
+ description: 'Compile readable NAIDE source code to a target language.',
37
58
  inputSchema: {
38
59
  type: 'object',
39
60
  properties: {
@@ -47,6 +68,18 @@ const TOOLS = [
47
68
  required: ['code']
48
69
  }
49
70
  },
71
+ {
72
+ name: 'naide_check',
73
+ description: 'Compile and type-check NAIDE source without executing it. Returns structured diagnostics.',
74
+ inputSchema: {
75
+ type: 'object',
76
+ properties: {
77
+ code: { type: 'string', description: 'NAIDE source code to check' },
78
+ sourceFile: { type: 'string', description: 'Optional source filename used in diagnostics' },
79
+ },
80
+ required: ['code']
81
+ }
82
+ },
50
83
  {
51
84
  name: 'naide_run',
52
85
  description: 'Compile NAIDE code to JavaScript and execute it. Returns stdout output.',
@@ -88,6 +121,35 @@ const TOOLS = [
88
121
  }
89
122
  }
90
123
  }
124
+ },
125
+ {
126
+ name: 'naide_doctor',
127
+ description: 'Diagnose the local Node.js runtime, installed language tools, and project manifests.',
128
+ inputSchema: {
129
+ type: 'object',
130
+ properties: { directory: { type: 'string', description: 'Project directory (defaults to the current directory)' } }
131
+ }
132
+ },
133
+ {
134
+ name: 'naide_verify',
135
+ description: 'Validate project manifests and summarize NAIDE files and setup warnings without running project code.',
136
+ inputSchema: {
137
+ type: 'object',
138
+ properties: { directory: { type: 'string', description: 'Project directory (defaults to the current directory)' } }
139
+ }
140
+ },
141
+ {
142
+ name: 'naide_test',
143
+ description: 'Run detected native project test suites. This executes project-configured test code with the current user permissions.',
144
+ inputSchema: {
145
+ type: 'object',
146
+ properties: { directory: { type: 'string', description: 'Project directory (defaults to the current directory)' } }
147
+ }
148
+ },
149
+ {
150
+ name: 'naide_package_managers',
151
+ description: 'List supported native package ecosystems and explain the limits of cross-language package interoperability.',
152
+ inputSchema: { type: 'object', properties: {} }
91
153
  }
92
154
  ];
93
155
 
@@ -110,10 +172,23 @@ const TARGETS_INFO = [
110
172
  ];
111
173
 
112
174
  async function handleToolCall(name, args) {
113
- const { compile, compileAsync } = await getCompiler();
114
-
115
175
  switch (name) {
176
+ case 'naide_check': {
177
+ if (typeof args.code !== 'string') {
178
+ return {
179
+ content: [{ type: 'text', text: 'The code argument must be a string.' }],
180
+ isError: true,
181
+ };
182
+ }
183
+ const report = checkNaideSource(args.code, args.sourceFile);
184
+ return {
185
+ content: [{ type: 'text', text: JSON.stringify(report, null, 2) }],
186
+ ...(!report.ok ? { isError: true } : {}),
187
+ };
188
+ }
189
+
116
190
  case 'naide_compile': {
191
+ const { compile, compileAsync } = await getCompiler();
117
192
  const target = args.target || 'node';
118
193
  try {
119
194
  if (target === 'node') {
@@ -128,12 +203,35 @@ async function handleToolCall(name, args) {
128
203
  }
129
204
 
130
205
  case 'naide_run': {
206
+ const { compile } = await getCompiler();
131
207
  try {
132
208
  const result = compile(args.code);
133
209
  const AsyncFunction = Object.getPrototypeOf(async function(){}).constructor;
134
210
  let output = '';
135
211
  const fakeConsole = { log: (...a) => { output += a.map(String).join(' ') + '\n'; }, error: (...a) => { output += a.map(String).join(' ') + '\n'; } };
136
- const fn = new AsyncFunction('console', result.js);
212
+ const executable = result.js.replace(
213
+ /^\s*import\s+(.+?)\s+from\s+(['"])([^'"\r\n]+)\2;\s*$/gm,
214
+ (_statement, clause, _quote, specifier) => {
215
+ const resolvedImport = resolveRunImport(specifier);
216
+ const module = `await import(${JSON.stringify(resolvedImport)})`;
217
+ const bindings = clause.trim();
218
+ if (bindings.startsWith('{')) {
219
+ return `const ${bindings.replace(/\bas\b/g, ':')} = ${module};`;
220
+ }
221
+ if (bindings.startsWith('* as ')) {
222
+ return `const ${bindings.slice(5)} = ${module};`;
223
+ }
224
+ const defaultAndNamed = bindings.match(/^([A-Za-z_$][\w$]*)\s*,\s*(\{[^}]+\})$/);
225
+ if (defaultAndNamed) {
226
+ return `const { default: ${defaultAndNamed[1]}, ${defaultAndNamed[2].slice(1, -1).replace(/\bas\b/g, ':')} } = ${module};`;
227
+ }
228
+ if (/^[A-Za-z_$][\w$]*$/.test(bindings)) {
229
+ return `const { default: ${bindings} } = ${module};`;
230
+ }
231
+ return _statement;
232
+ },
233
+ );
234
+ const fn = new AsyncFunction('console', executable);
137
235
  await fn(fakeConsole);
138
236
  return { content: [{ type: 'text', text: output || '(no output)' }] };
139
237
  } catch (e) {
@@ -148,7 +246,7 @@ async function handleToolCall(name, args) {
148
246
 
149
247
  case 'naide_generate': {
150
248
  try {
151
- const { generate } = await import(resolve(__dirname, '..', 'src', 'gen.js'));
249
+ const { generate } = await import(pathToFileURL(resolve(__dirname, '..', 'src', 'gen.js')).href);
152
250
  const opts = {};
153
251
  if (args.port) opts.port = args.port;
154
252
  if (args.schemaName) opts.schemaName = args.schemaName;
@@ -181,6 +279,41 @@ async function handleToolCall(name, args) {
181
279
  }
182
280
  }
183
281
 
282
+ case 'naide_doctor': {
283
+ const directory = typeof args.directory === 'string' ? resolve(args.directory) : process.cwd();
284
+ return { content: [{ type: 'text', text: JSON.stringify(await getDoctorReport(directory), null, 2) }] };
285
+ }
286
+
287
+ case 'naide_verify': {
288
+ const directory = typeof args.directory === 'string' ? resolve(args.directory) : process.cwd();
289
+ const report = inspectProject(directory);
290
+ return {
291
+ content: [{ type: 'text', text: JSON.stringify(report, null, 2) }],
292
+ ...(report.ok ? {} : { isError: true }),
293
+ };
294
+ }
295
+
296
+ case 'naide_test': {
297
+ const directory = typeof args.directory === 'string' ? resolve(args.directory) : process.cwd();
298
+ const report = await runProjectTests(directory);
299
+ return {
300
+ content: [{ type: 'text', text: JSON.stringify(report, null, 2) }],
301
+ ...(report.ok ? {} : { isError: true }),
302
+ };
303
+ }
304
+
305
+ case 'naide_package_managers': {
306
+ return {
307
+ content: [{
308
+ type: 'text',
309
+ text: JSON.stringify({
310
+ adapters: listPackageAdapters(),
311
+ interoperability: 'Packages are installed with their ecosystem-native manager and run in their native runtime. Calling a package from NAIDE requires an explicit process, HTTP, FFI, or generated-language adapter; arbitrary libraries cannot be called directly across languages.',
312
+ }, null, 2),
313
+ }],
314
+ };
315
+ }
316
+
184
317
  default:
185
318
  return { content: [{ type: 'text', text: `Unknown tool: ${name}` }], isError: true };
186
319
  }
@@ -191,8 +324,8 @@ async function handleMessage(msg) {
191
324
  case 'initialize':
192
325
  respond(msg.id, {
193
326
  protocolVersion: '2024-11-05',
194
- capabilities: { tools: {} },
195
- serverInfo: { name: 'naide-mcp', version: '1.24.5' }
327
+ capabilities: { tools: {}, prompts: {}, resources: {} },
328
+ serverInfo: { name: 'naide-mcp', version: '2.0.1' }
196
329
  });
197
330
  break;
198
331
 
@@ -203,6 +336,74 @@ async function handleMessage(msg) {
203
336
  respond(msg.id, { tools: TOOLS });
204
337
  break;
205
338
 
339
+ case 'prompts/list':
340
+ respond(msg.id, {
341
+ prompts: [
342
+ { name: 'getting_started', description: 'Create and verify a small NAIDE project.' },
343
+ { name: 'verify_changes', description: 'Check AI-authored NAIDE changes without executing project code.' },
344
+ { name: 'secure_service', description: 'Design a NAIDE service with authentication, safe defaults, and a verification checklist.' },
345
+ { name: 'polyglot_dependency', description: 'Choose a native package manager and a safe bridge for using a foreign-language package.' },
346
+ { name: 'getting_started_ja', description: '日本語でNAIDEの小さなプロジェクトを作成して確認する。' },
347
+ { name: 'verify_changes_ja', description: 'AIが変更したNAIDEコードを実行せずに検査する。' },
348
+ { name: 'secure_service_ja', description: '認証と安全な初期設定を備えたサービスを設計する。' },
349
+ { name: 'polyglot_dependency_ja', description: '他言語パッケージの導入方法と明示的な連携方法を検討する。' },
350
+ ],
351
+ });
352
+ break;
353
+
354
+ case 'prompts/get': {
355
+ const prompts = {
356
+ getting_started: 'Help me create a small NAIDE project. Prefer readable .naide source, explain each step, and finish by running naide doctor and naide verify.',
357
+ verify_changes: 'Review the requested NAIDE changes, then use naide_check on each changed .naide source and naide_verify on the project. These checks do not execute project code. Explain every diagnostic and fix only issues in scope. Do not use naide_run or naide_test unless I explicitly ask you to execute code or tests.',
358
+ secure_service: 'Help me design a secure NAIDE service. Use authentication for sensitive routes, validate input, keep secrets in environment variables, bind development servers to localhost, and provide concrete verification steps. Do not claim security without evidence.',
359
+ polyglot_dependency: 'Help me use this dependency from NAIDE. First identify its native language and package manager, then explain installation with naide pkg add where supported. Do not imply direct cross-language calls: select and explain an explicit process, HTTP, FFI, or generated-language adapter.',
360
+ getting_started_ja: 'NAIDEの初心者向け小規模プロジェクトを作成してください。読みやすい.naideコードを使い、手順を日本語で説明し、最後にnaide doctorとnaide verifyで確認してください。',
361
+ verify_changes_ja: '依頼されたNAIDEの変更を確認し、変更した各.naideコードにはnaide_check、プロジェクトにはnaide_verifyを使ってください。これらはプロジェクトコードを実行しません。診断を説明し、依頼の範囲内だけ修正してください。明示的にコードやテストの実行を求められない限り、naide_runやnaide_testは使わないでください。',
362
+ secure_service_ja: '認証が必要なルート、入力検証、環境変数での秘密情報管理、localhostへの開発用バインドを含むNAIDEサービスを設計し、日本語で検証手順も示してください。根拠なく安全性を断言しないでください。',
363
+ polyglot_dependency_ja: 'この依存パッケージをNAIDEプロジェクトで使う方法を日本語で提案してください。言語と標準パッケージマネージャーを特定し、対応していればnaide pkg addを案内してください。異なる言語間の直接呼び出しを約束せず、プロセス・HTTP・FFI・生成コードなど明示的な境界を選んでください。',
364
+ };
365
+ const prompt = prompts[msg.params?.name];
366
+ if (!prompt) {
367
+ respondError(msg.id, -32602, `Unknown prompt: ${msg.params?.name}`);
368
+ } else {
369
+ respond(msg.id, { description: prompt, messages: [{ role: 'user', content: { type: 'text', text: prompt } }] });
370
+ }
371
+ break;
372
+ }
373
+
374
+ case 'resources/list':
375
+ respond(msg.id, {
376
+ resources: [
377
+ { uri: 'naide://spec', name: 'NAIDE language specification', mimeType: 'text/plain', description: 'Canonical NAIDE syntax and language reference.' },
378
+ { uri: 'naide://package-ecosystems', name: 'Native package ecosystems', mimeType: 'application/json', description: 'Supported NAIDE package manager adapters and interoperability guidance.' },
379
+ { uri: 'naide://project', name: 'Current project diagnostics', mimeType: 'application/json', description: 'A read-only summary of project setup in the server working directory.' },
380
+ ],
381
+ });
382
+ break;
383
+
384
+ case 'resources/read': {
385
+ const uri = msg.params?.uri;
386
+ let text;
387
+ let mimeType = 'text/plain';
388
+ if (uri === 'naide://spec') {
389
+ text = readFileSync(resolve(__dirname, '..', 'SPEC.naide'), 'utf-8');
390
+ } else if (uri === 'naide://package-ecosystems') {
391
+ mimeType = 'application/json';
392
+ text = JSON.stringify({
393
+ adapters: listPackageAdapters(),
394
+ interoperability: 'A native package is not automatically callable from a different language runtime. Use an explicit process, HTTP, FFI, or generated-language adapter.',
395
+ }, null, 2);
396
+ } else if (uri === 'naide://project') {
397
+ mimeType = 'application/json';
398
+ text = JSON.stringify(inspectProject(process.cwd()), null, 2);
399
+ } else {
400
+ respondError(msg.id, -32602, `Unknown resource: ${uri}`);
401
+ break;
402
+ }
403
+ respond(msg.id, { contents: [{ uri, mimeType, text }] });
404
+ break;
405
+ }
406
+
206
407
  case 'tools/call':
207
408
  try {
208
409
  const result = await handleToolCall(msg.params.name, msg.params.arguments || {});
@@ -223,25 +424,49 @@ async function handleMessage(msg) {
223
424
  }
224
425
  }
225
426
 
226
- let buffer = '';
227
- process.stdin.setEncoding('utf-8');
427
+ let buffer = Buffer.alloc(0);
228
428
  process.stdin.on('data', (chunk) => {
229
- buffer += chunk;
429
+ buffer = Buffer.concat([buffer, chunk]);
230
430
  while (true) {
231
431
  const headerEnd = buffer.indexOf('\r\n\r\n');
232
- if (headerEnd === -1) break;
233
- const header = buffer.slice(0, headerEnd);
234
- const match = header.match(/Content-Length:\s*(\d+)/i);
235
- if (!match) { buffer = buffer.slice(headerEnd + 4); continue; }
236
- const len = parseInt(match[1], 10);
432
+ if (headerEnd === -1) {
433
+ if (buffer.length > 8192) {
434
+ process.stderr.write('MCP error: rejected oversized message header\n');
435
+ process.exit(1);
436
+ }
437
+ break;
438
+ }
439
+ if (headerEnd > 8192) {
440
+ process.stderr.write('MCP error: rejected oversized message header\n');
441
+ process.exit(1);
442
+ }
443
+ const header = buffer.toString('ascii', 0, headerEnd);
444
+ const match = header.match(/(?:^|\r\n)Content-Length:\s*(\d+)(?:\r\n|$)/i);
445
+ if (!match) {
446
+ process.stderr.write('MCP error: missing Content-Length header\n');
447
+ process.exit(1);
448
+ }
449
+ const len = Number(match[1]);
450
+ if (!Number.isSafeInteger(len) || len > 32 * 1024 * 1024) {
451
+ process.stderr.write('MCP error: rejected oversized or invalid message body\n');
452
+ process.exit(1);
453
+ }
237
454
  const bodyStart = headerEnd + 4;
238
455
  if (buffer.length < bodyStart + len) break;
239
- const body = buffer.slice(bodyStart, bodyStart + len);
456
+ const body = buffer.toString('utf8', bodyStart, bodyStart + len);
240
457
  buffer = buffer.slice(bodyStart + len);
241
458
  try {
242
- handleMessage(JSON.parse(body));
459
+ const message = JSON.parse(body);
460
+ if (!message || typeof message !== 'object' || Array.isArray(message)) {
461
+ respondError(null, -32600, 'Invalid Request');
462
+ continue;
463
+ }
464
+ handleMessage(message).catch((error) => {
465
+ process.stderr.write(`MCP request error: ${error.message}\n`);
466
+ if (message.id !== undefined) respondError(message.id, -32603, 'Internal error');
467
+ });
243
468
  } catch (e) {
244
- process.stderr.write(`Parse error: ${e.message}\n`);
469
+ respondError(null, -32700, `Parse error: ${e.message}`);
245
470
  }
246
471
  }
247
472
  });
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "naider",
3
- "version": "1.24.5",
4
- "description": "NAIDE - Simpler than Python, compiles to 15 targets. AI-specialized language with 35+ built-in functions, syntax sugar (unless/until/repeat/swap/is/isnt), and 47 features. Targets: Node.js, Python, TypeScript, C, C++, Java, Go, Rust, PHP, Ruby, Kotlin, Swift, Dart, C#, Bun.",
3
+ "version": "2.0.1",
4
+ "description": "NAIDE is a readable, practical programming language with a single syntax, a compiler, type checking, and batteries-included development tools.",
5
5
  "main": "src/index.js",
6
6
  "exports": {
7
7
  ".": "./src/index.js",
@@ -21,14 +21,17 @@
21
21
  "examples/",
22
22
  "vscode-naide/",
23
23
  "SPEC.naide",
24
- "SPEC-X.nx",
25
24
  "README.md",
26
25
  "LICENSE"
27
26
  ],
28
27
  "scripts": {
29
- "test": "node --test test/test.js",
28
+ "test": "node --test",
29
+ "test:targets": "node --test test/targets.js",
30
+ "test:runtimes": "node --test test/target-runtimes.js",
31
+ "test:parity": "node --test test/compiler-parity.js",
32
+ "test:onboarding": "node --test test/onboarding.js",
33
+ "test:features": "node --test test/project-tools.js test/mcp.js test/lsp.js test/security.js test/onboarding.js",
30
34
  "example": "node bin/naide.js examples/hello.naide",
31
- "example:x": "node bin/naide.js examples/hello.nx",
32
35
  "postinstall": "node bin/postinstall.js"
33
36
  },
34
37
  "keywords": [