naider 1.19.0 → 1.21.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +73 -1
- package/bin/naide.js +311 -6
- package/mcp/server.js +28 -1
- package/package.json +1 -1
- package/src/gen.js +1660 -0
- package/src/index.js +8 -1
- package/src/parser.js +1 -1
package/README.md
CHANGED
|
@@ -77,6 +77,9 @@ naide pkg install <name> # Install a NAIDE package
|
|
|
77
77
|
naide pkg publish # Publish package to npm
|
|
78
78
|
naide -w <file> # Watch mode (auto-restart on changes)
|
|
79
79
|
naide -d <file> # Debug mode (Node.js inspector)
|
|
80
|
+
naide ~~ "instruction" # Generate NAIDE code from natural language
|
|
81
|
+
naide gen "instruction" # Same as ~~ (alias)
|
|
82
|
+
naide --expand <file> # Show ~~ directive expansion results
|
|
80
83
|
naide --emit <file> # Print generated JavaScript
|
|
81
84
|
naide -o <out.js> <file> # Write JavaScript to file
|
|
82
85
|
naide --mid <file.nx> # Show intermediate NAIDE v1 (debug X mode)
|
|
@@ -84,11 +87,80 @@ naide --ast <file> # Print AST
|
|
|
84
87
|
naide --tokens <file> # Print token stream
|
|
85
88
|
```
|
|
86
89
|
|
|
90
|
+
### Code Generation (`~~`)
|
|
91
|
+
|
|
92
|
+
Generate valid NAIDE code from natural language instructions — no AI, no network, no dependencies. Pure pattern matching + template composition with self-healing parser validation. Multi-entity support with automatic relationship detection, paginated CRUD, soft delete, batch operations, nested routes, seed data, tests, and full project scaffolding.
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
# Single entity
|
|
96
|
+
naide ~~ "REST API for users with auth"
|
|
97
|
+
naide ~~ "todo app with database"
|
|
98
|
+
|
|
99
|
+
# Multi-entity (auto-detects relationships, injects foreign keys)
|
|
100
|
+
naide ~~ "users and products and orders with auth"
|
|
101
|
+
|
|
102
|
+
# Composite presets
|
|
103
|
+
naide ~~ "shop" # User + Product + Order
|
|
104
|
+
naide ~~ "crm" # User + Contact + Deal + Activity
|
|
105
|
+
naide ~~ "sns" # User + Post + Comment + Message + Notification
|
|
106
|
+
naide ~~ "booking" # User + Event + Reservation
|
|
107
|
+
|
|
108
|
+
# Project mode (generates full directory)
|
|
109
|
+
naide ~~ "e-commerce app with auth" --project
|
|
110
|
+
|
|
111
|
+
# File write mode (merge into existing file)
|
|
112
|
+
naide ~~ "REST API for users" --write app.naide
|
|
113
|
+
|
|
114
|
+
# Japanese
|
|
115
|
+
naide ~~ "ECサイト"
|
|
116
|
+
naide ~~ "ユーザー管理APIを認証付きで"
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
Generated routes per entity: paginated list, CRUD, soft delete, restore, batch create/delete, nested routes (from relationships), file upload, webhooks. With auth: register, login, profile, admin stats, audit log.
|
|
120
|
+
|
|
121
|
+
Project mode (`--project`) generates: `app.naide`, `package.json`, `.env`, `Dockerfile`, `README.md`, `openapi.json`, `admin.html`, `client.mjs`.
|
|
122
|
+
|
|
123
|
+
API usage:
|
|
124
|
+
|
|
125
|
+
```javascript
|
|
126
|
+
import { generate, generateProject } from 'naider';
|
|
127
|
+
|
|
128
|
+
const result = generate("users and products with auth");
|
|
129
|
+
console.log(result.entities); // ['User', 'Product']
|
|
130
|
+
console.log(result.relationships); // [{parent:'User', child:'Product', fk:'user_id'}]
|
|
131
|
+
console.log(result.valid); // true
|
|
132
|
+
console.log(result.analysis); // {entities, relationships, plan, ...}
|
|
133
|
+
|
|
134
|
+
const project = generateProject("e-commerce with auth");
|
|
135
|
+
project.files.forEach(f => console.log(f.path));
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
**Self-healing**: Generated code is validated against the actual NAIDE parser. Parse errors are auto-fixed (up to 5 retries) — missing colons, indent issues, reserved keyword conflicts.
|
|
139
|
+
|
|
140
|
+
**Field normalization**: Smart aliases — `e-mail` → `email`, `pwd` → `password`, `tel` → `phone`, `desc` → `description`. Field types are auto-inferred from names (`email` → `str + email`, `age` → `int`, `done` → `bool`).
|
|
141
|
+
|
|
142
|
+
<details>
|
|
143
|
+
<summary>Keyword Cheat Sheet</summary>
|
|
144
|
+
|
|
145
|
+
| Category | Keywords |
|
|
146
|
+
|----------|----------|
|
|
147
|
+
| Intent | `server` `api` `rest` `bot` `cli` `page` `test` `database` `ai` `crud` `auth` `websocket` `mail` `graphql` |
|
|
148
|
+
| Composite | `todo` `blog` `chat` `shop` `fullstack` `board` `crm` `inventory` `booking` `sns` |
|
|
149
|
+
| Platform | `discord` `slack` `telegram` `line` |
|
|
150
|
+
| DB Type | `sqlite` `postgres` |
|
|
151
|
+
| JP Intent | `サーバー` `認証` `ログイン` `会員` `CRUD` `管理` `データベース` `ボット` `テスト` `ページ` `メール` |
|
|
152
|
+
| JP Entity | `ユーザー` `商品` `記事` `タスク` `注文` `コメント` `イベント` `問い合わせ` `通知` `カテゴリ` `掲示板` `決済` `予約` `社員` |
|
|
153
|
+
| JP Composite | `管理システム` `やることリスト` `ブログ` `チャットアプリ` `ECサイト` `掲示板` |
|
|
154
|
+
| NL Phrases | `"users can comment"` `"admin manage"` `"upload"` `"search"` `"notification"` `"deploy"` `"seed"` |
|
|
155
|
+
| Fields | `"with name email age"` — auto-inferred types & validators |
|
|
156
|
+
|
|
157
|
+
</details>
|
|
158
|
+
|
|
87
159
|
### REPL
|
|
88
160
|
|
|
89
161
|
```
|
|
90
162
|
$ naide
|
|
91
|
-
NAIDE REPL v1.
|
|
163
|
+
NAIDE REPL v1.21.0 — type NAIDE code, see JavaScript output
|
|
92
164
|
Type .exit to quit, .eval to toggle eval mode
|
|
93
165
|
|
|
94
166
|
>>> str name = "hello"
|
package/bin/naide.js
CHANGED
|
@@ -8,6 +8,42 @@ import { spawn } from 'child_process';
|
|
|
8
8
|
import { createRequire } from 'module';
|
|
9
9
|
const _require = createRequire(import.meta.url);
|
|
10
10
|
|
|
11
|
+
const NAIDE_VERSION = '1.21.0';
|
|
12
|
+
|
|
13
|
+
function crashReport(err, context = {}) {
|
|
14
|
+
const info = [
|
|
15
|
+
`NAIDE v${NAIDE_VERSION}`,
|
|
16
|
+
`Node ${process.version}`,
|
|
17
|
+
`${process.platform} ${process.arch}`,
|
|
18
|
+
];
|
|
19
|
+
if (context.file) info.push(`File: ${context.file}`);
|
|
20
|
+
if (context.target) info.push(`Target: ${context.target}`);
|
|
21
|
+
|
|
22
|
+
console.error(`\n ┌─ NAIDE Internal Error ───────────────────────────`);
|
|
23
|
+
console.error(` │ ${err.message}`);
|
|
24
|
+
console.error(` │`);
|
|
25
|
+
console.error(` │ ${info.join(' | ')}`);
|
|
26
|
+
if (err.stack) {
|
|
27
|
+
const frames = err.stack.split('\n').slice(1, 4).map(l => l.trim());
|
|
28
|
+
for (const f of frames) console.error(` │ ${f}`);
|
|
29
|
+
}
|
|
30
|
+
console.error(` │`);
|
|
31
|
+
console.error(` │ This is a compiler bug, not a syntax error.`);
|
|
32
|
+
console.error(` │ Report: https://github.com/irxk/naide/issues/new`);
|
|
33
|
+
console.error(` │ Include the .naide file and the info above.`);
|
|
34
|
+
console.error(` └──────────────────────────────────────────────────\n`);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
process.on('uncaughtException', (err) => {
|
|
38
|
+
crashReport(err);
|
|
39
|
+
process.exit(99);
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
process.on('unhandledRejection', (reason) => {
|
|
43
|
+
crashReport(reason instanceof Error ? reason : new Error(String(reason)));
|
|
44
|
+
process.exit(99);
|
|
45
|
+
});
|
|
46
|
+
|
|
11
47
|
function checkDependencies(jsCode) {
|
|
12
48
|
const depMap = {
|
|
13
49
|
"from 'express'": { pkg: 'express', reason: 'server' },
|
|
@@ -77,6 +113,10 @@ const flags = {
|
|
|
77
113
|
debug: false,
|
|
78
114
|
check: false,
|
|
79
115
|
target: 'node',
|
|
116
|
+
expand: false,
|
|
117
|
+
project: false,
|
|
118
|
+
write: null,
|
|
119
|
+
replace: false,
|
|
80
120
|
};
|
|
81
121
|
|
|
82
122
|
const files = [];
|
|
@@ -95,10 +135,198 @@ for (let i = 0; i < args.length; i++) {
|
|
|
95
135
|
case '--debug': case '-d': flags.debug = true; break;
|
|
96
136
|
case '--check': flags.check = true; flags.run = false; break;
|
|
97
137
|
case '--target': case '-t': flags.target = args[++i]; break;
|
|
138
|
+
case '--expand': flags.expand = true; flags.run = false; break;
|
|
139
|
+
case '--project': case '-p': flags.project = true; break;
|
|
140
|
+
case '--write': flags.write = args[++i]; break;
|
|
141
|
+
case '--replace': flags.replace = true; break;
|
|
98
142
|
default: files.push(arg);
|
|
99
143
|
}
|
|
100
144
|
}
|
|
101
145
|
|
|
146
|
+
// ── Code Generator (~~) ──
|
|
147
|
+
if (files[0] === 'gen' || files[0] === '~~') {
|
|
148
|
+
const instruction = files.slice(1).join(' ');
|
|
149
|
+
if (!instruction) {
|
|
150
|
+
console.log(`
|
|
151
|
+
NAIDE Agent Coder (~~)
|
|
152
|
+
|
|
153
|
+
Usage:
|
|
154
|
+
naide ~~ "REST API for users with auth"
|
|
155
|
+
naide gen "todo app with database"
|
|
156
|
+
naide gen "users and products and orders with auth"
|
|
157
|
+
naide ~~ "ECサイト" --project
|
|
158
|
+
|
|
159
|
+
Autonomous code generator with analysis, multi-entity support,
|
|
160
|
+
relationship detection, and full auth routes. No AI, no network.
|
|
161
|
+
|
|
162
|
+
Examples:
|
|
163
|
+
naide ~~ "REST API for products with name price stock"
|
|
164
|
+
naide ~~ "users and products with auth" # multi-entity
|
|
165
|
+
naide ~~ "SNS app" # User+Post+Comment
|
|
166
|
+
naide ~~ "ECサイト" # User+Product+Order
|
|
167
|
+
naide ~~ "予約システム" # User+Reservation
|
|
168
|
+
naide ~~ "Discord bot with hello and help commands"
|
|
169
|
+
naide ~~ "ユーザーと商品と注文の管理APIを認証付きで" # JP multi-entity
|
|
170
|
+
|
|
171
|
+
Project Mode (--project):
|
|
172
|
+
naide ~~ "e-commerce app with auth" --project
|
|
173
|
+
# Creates: app.naide, package.json, .env, .gitignore,
|
|
174
|
+
# Dockerfile, .dockerignore, README.md
|
|
175
|
+
|
|
176
|
+
File Write Mode:
|
|
177
|
+
naide ~~ "add auth" app.naide # merge into existing file
|
|
178
|
+
naide ~~ "add websocket" --write app.naide # explicit --write
|
|
179
|
+
naide ~~ "REST API" --write app.naide --replace # overwrite file
|
|
180
|
+
|
|
181
|
+
In .naide files:
|
|
182
|
+
~~ "REST API for users with auth"
|
|
183
|
+
# Expands at compile time to full NAIDE code
|
|
184
|
+
|
|
185
|
+
Debug:
|
|
186
|
+
naide --expand app.naide Show ~~ expansion results
|
|
187
|
+
|
|
188
|
+
API:
|
|
189
|
+
import { generate, generateProject, writeToFile } from 'naider';
|
|
190
|
+
const result = generate("users and products with auth");
|
|
191
|
+
console.log(result.analysis); // agent analysis
|
|
192
|
+
console.log(result.entities); // ['User', 'Product']
|
|
193
|
+
writeToFile("app.naide", "add auth"); // merge into file
|
|
194
|
+
|
|
195
|
+
── Keyword Cheat Sheet ──────────────────────────────────────
|
|
196
|
+
|
|
197
|
+
Intent server, api, rest, bot, cli, page, test, database,
|
|
198
|
+
ai, crud, auth, websocket, mail, graphql
|
|
199
|
+
Composite todo, blog, chat, shop, fullstack, board,
|
|
200
|
+
crm, inventory, booking, sns
|
|
201
|
+
Platform discord, slack, telegram, line
|
|
202
|
+
DB Type sqlite, postgres
|
|
203
|
+
JP Intent サーバー, 認証, ログイン, 会員, CRUD, 管理, データベース,
|
|
204
|
+
ボット, テスト, ページ, メール, リアルタイム
|
|
205
|
+
JP Entity ユーザー, 商品, 記事, タスク, 注文, コメント, イベント,
|
|
206
|
+
問い合わせ, 通知, カテゴリ, プロジェクト, 掲示板, 決済
|
|
207
|
+
Fields "with name email age" — auto-inferred types & validators
|
|
208
|
+
Field Alias e-mail→email, pwd→password, tel→phone, desc→description
|
|
209
|
+
Relations Auto-detected: User→Post, User→Order, Product→Order, etc.
|
|
210
|
+
|
|
211
|
+
────────────────────────────────────────────────────────────
|
|
212
|
+
`);
|
|
213
|
+
process.exit(0);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
const { generate, generateProject, writeToFile } = await import('../src/gen.js');
|
|
217
|
+
|
|
218
|
+
// Write mode: inject into existing .naide/.nx file
|
|
219
|
+
const targetFile = instruction.match(/\S+\.(?:naide|nx)$/)?.[0];
|
|
220
|
+
if (targetFile || flags.write) {
|
|
221
|
+
const file = flags.write || targetFile;
|
|
222
|
+
const cleanInstruction = targetFile ? instruction.replace(/\s+\S+\.(?:naide|nx)$/, '') : instruction;
|
|
223
|
+
const mode = flags.replace ? 'replace' : 'append';
|
|
224
|
+
const result = writeToFile(resolve(file), cleanInstruction, { mode });
|
|
225
|
+
const a = result.analysis;
|
|
226
|
+
process.stderr.write('\n ── NAIDE Agent ──────────────────────────────────────────\n');
|
|
227
|
+
process.stderr.write(` Input: "${a.instruction}"\n`);
|
|
228
|
+
process.stderr.write(` Action: ${result.action} → ${file}\n`);
|
|
229
|
+
if (a.entities.length > 0) {
|
|
230
|
+
process.stderr.write(` Entities: ${a.entities.map(e => `${e.name} (${e.fieldCount} fields)`).join(', ')}\n`);
|
|
231
|
+
}
|
|
232
|
+
process.stderr.write(' ────────────────────────────────────────────────────────\n\n');
|
|
233
|
+
console.log(result.code);
|
|
234
|
+
process.stderr.write(` ── ${result.action}: ${file} (valid: ${result.valid}) ──\n`);
|
|
235
|
+
process.exit(0);
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
// Project mode: generate full project directory
|
|
239
|
+
if (flags.project) {
|
|
240
|
+
const result = generateProject(instruction);
|
|
241
|
+
const primaryName = (result.entities[0] || 'app').toLowerCase().replace(/[^a-z0-9]/g, '');
|
|
242
|
+
const dir = flags.output || (primaryName + '-app');
|
|
243
|
+
const outDir = resolve(dir);
|
|
244
|
+
if (!existsSync(outDir)) mkdirSync(outDir, { recursive: true });
|
|
245
|
+
for (const f of result.files) {
|
|
246
|
+
const fp = resolve(outDir, f.path);
|
|
247
|
+
writeFileSync(fp, f.content, 'utf-8');
|
|
248
|
+
}
|
|
249
|
+
const a = result.analysis;
|
|
250
|
+
process.stderr.write('\n ── NAIDE Agent ──────────────────────────────────────────\n');
|
|
251
|
+
process.stderr.write(` Input: "${a.instruction}"\n`);
|
|
252
|
+
if (a.entities.length > 0) {
|
|
253
|
+
process.stderr.write(` Entities: ${a.entities.map(e => `${e.name} (${e.fieldCount} fields)`).join(', ')}\n`);
|
|
254
|
+
}
|
|
255
|
+
if (a.relationships.length > 0) {
|
|
256
|
+
for (const r of a.relationships) process.stderr.write(` Relation: ${r.parent} 1→N ${r.child} (${r.fk})\n`);
|
|
257
|
+
}
|
|
258
|
+
process.stderr.write(` Plan:\n`);
|
|
259
|
+
for (const step of a.plan) process.stderr.write(` + ${step}\n`);
|
|
260
|
+
process.stderr.write(' ────────────────────────────────────────────────────────\n\n');
|
|
261
|
+
console.log(` Project created: ${dir}/`);
|
|
262
|
+
for (const f of result.files) console.log(` ${f.path}`);
|
|
263
|
+
console.log(`\n cd ${dir} && npm install && npm run dev\n`);
|
|
264
|
+
process.exit(0);
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
const result = generate(instruction);
|
|
268
|
+
|
|
269
|
+
if (flags.output) {
|
|
270
|
+
writeFileSync(flags.output, result.code, 'utf-8');
|
|
271
|
+
console.log(` Generated: ${flags.output} (${result.intents.join(' + ')})`);
|
|
272
|
+
} else {
|
|
273
|
+
// Agent-style analysis header
|
|
274
|
+
const a = result.analysis;
|
|
275
|
+
process.stderr.write('\n ── NAIDE Agent ──────────────────────────────────────────\n');
|
|
276
|
+
process.stderr.write(` Input: "${a.instruction}"\n`);
|
|
277
|
+
if (a.entities.length > 0) {
|
|
278
|
+
process.stderr.write(` Entities: ${a.entities.map(e => `${e.name} (${e.fieldCount} fields)`).join(', ')}\n`);
|
|
279
|
+
}
|
|
280
|
+
if (a.relationships.length > 0) {
|
|
281
|
+
for (const r of a.relationships) {
|
|
282
|
+
process.stderr.write(` Relation: ${r.parent} 1→N ${r.child} (${r.fk})\n`);
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
process.stderr.write(` Plan:\n`);
|
|
286
|
+
for (const step of a.plan) {
|
|
287
|
+
process.stderr.write(` + ${step}\n`);
|
|
288
|
+
}
|
|
289
|
+
process.stderr.write(' ────────────────────────────────────────────────────────\n\n');
|
|
290
|
+
|
|
291
|
+
console.log(result.code);
|
|
292
|
+
|
|
293
|
+
const meta = [`[${result.intents.join(' + ')}]`];
|
|
294
|
+
if (result.entities && result.entities.length > 1) {
|
|
295
|
+
meta.push(`→ ${result.entities.join(', ')}`);
|
|
296
|
+
} else if (result.schema) {
|
|
297
|
+
meta.push(`→ ${result.schema}`);
|
|
298
|
+
}
|
|
299
|
+
meta.push(`valid: ${result.valid}`);
|
|
300
|
+
if (result.fixed) meta.push('(auto-fixed)');
|
|
301
|
+
process.stderr.write(` ── gen ~~ ${meta.join(' ')} ──\n`);
|
|
302
|
+
if (result.repairs.length > 0) {
|
|
303
|
+
process.stderr.write(' Repairs:\n');
|
|
304
|
+
for (const r of result.repairs) process.stderr.write(` - ${r}\n`);
|
|
305
|
+
}
|
|
306
|
+
if (result.suggestion) {
|
|
307
|
+
process.stderr.write(`\n${result.suggestion}\n`);
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
process.exit(0);
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
// ── Expand Debug ──
|
|
314
|
+
if (flags.expand && files.length > 0) {
|
|
315
|
+
const { expandDirectives } = await import('../src/gen.js');
|
|
316
|
+
for (const file of files) {
|
|
317
|
+
const filePath = resolve(file);
|
|
318
|
+
const source = readFileSync(filePath, 'utf-8');
|
|
319
|
+
const { source: expanded, expanded: didExpand } = expandDirectives(source);
|
|
320
|
+
if (didExpand) {
|
|
321
|
+
console.log(` ── ${file} (expanded) ──\n`);
|
|
322
|
+
console.log(expanded);
|
|
323
|
+
} else {
|
|
324
|
+
console.log(` ${file}: no ~~ directives found`);
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
process.exit(0);
|
|
328
|
+
}
|
|
329
|
+
|
|
102
330
|
// ── VS Code Extension Install ──
|
|
103
331
|
if (files[0] === 'vscode') {
|
|
104
332
|
const os = await import('os');
|
|
@@ -357,24 +585,67 @@ if (files[0] === 'build') {
|
|
|
357
585
|
|
|
358
586
|
console.log(`\n NAIDE build — ${sourceFiles.length} file(s)\n`);
|
|
359
587
|
let errors = 0;
|
|
588
|
+
const schemaMap = {};
|
|
589
|
+
const routeMap = {};
|
|
590
|
+
|
|
360
591
|
for (const srcFile of sourceFiles) {
|
|
361
592
|
const rel = relative(dir, srcFile);
|
|
362
593
|
const mode = srcFile.endsWith('.nx') ? 'x' : 'naide';
|
|
363
594
|
try {
|
|
364
595
|
const source = readFileSync(srcFile, 'utf-8');
|
|
365
|
-
const { js } = compile(source, { mode });
|
|
596
|
+
const { js, ast } = compile(source, { mode });
|
|
366
597
|
const outName = rel.replace(/\.(naide|nx)$/, '.mjs');
|
|
367
598
|
const outPath = outDir ? join(outDir, outName) : join(dir, outName);
|
|
368
599
|
const outDirPath = resolve(outPath, '..');
|
|
369
600
|
if (!existsSync(outDirPath)) mkdirSync(outDirPath, { recursive: true });
|
|
370
601
|
writeFileSync(outPath, js);
|
|
371
602
|
console.log(` ${rel} → ${outDir ? join(relative('.', outDir), outName) : outName}`);
|
|
603
|
+
|
|
604
|
+
if (ast && ast.body) {
|
|
605
|
+
for (const node of ast.body) {
|
|
606
|
+
if (node.type === 'SchemaDecl' && node.name) {
|
|
607
|
+
(schemaMap[node.name] = schemaMap[node.name] || []).push(rel);
|
|
608
|
+
}
|
|
609
|
+
if (node.type === 'Server' && node.routes) {
|
|
610
|
+
for (const s of node.routes) {
|
|
611
|
+
if (s.type === 'CrudDecl' && s.path) {
|
|
612
|
+
const key = `CRUD ${s.path.raw || s.path}`;
|
|
613
|
+
(routeMap[key] = routeMap[key] || []).push(rel);
|
|
614
|
+
}
|
|
615
|
+
if (s.type === 'Route' && s.path) {
|
|
616
|
+
const key = `${(s.method || 'get').toUpperCase()} ${s.path.raw || s.path}`;
|
|
617
|
+
(routeMap[key] = routeMap[key] || []).push(rel);
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
}
|
|
622
|
+
}
|
|
372
623
|
} catch (e) {
|
|
373
|
-
|
|
624
|
+
const isSyntaxErr = e.message.includes('line ') || e.message.includes('Unexpected') || e.message.includes('Expected');
|
|
625
|
+
if (isSyntaxErr) {
|
|
626
|
+
console.error(` FAIL ${rel}: ${e.message.split('\n')[0]}`);
|
|
627
|
+
} else {
|
|
628
|
+
crashReport(e, { file: rel });
|
|
629
|
+
}
|
|
374
630
|
errors++;
|
|
375
631
|
}
|
|
376
632
|
}
|
|
377
|
-
|
|
633
|
+
|
|
634
|
+
let warnings = 0;
|
|
635
|
+
for (const [name, files] of Object.entries(schemaMap)) {
|
|
636
|
+
if (files.length > 1) {
|
|
637
|
+
console.error(` WARN: schema "${name}" defined in ${files.length} files: ${files.join(', ')}`);
|
|
638
|
+
warnings++;
|
|
639
|
+
}
|
|
640
|
+
}
|
|
641
|
+
for (const [route, files] of Object.entries(routeMap)) {
|
|
642
|
+
if (files.length > 1) {
|
|
643
|
+
console.error(` WARN: route ${route} defined in ${files.length} files: ${files.join(', ')}`);
|
|
644
|
+
warnings++;
|
|
645
|
+
}
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
console.log(`\n Done. ${sourceFiles.length - errors} compiled, ${errors} failed${warnings > 0 ? `, ${warnings} warning(s)` : ''}.`);
|
|
378
649
|
process.exit(errors > 0 ? 1 : 0);
|
|
379
650
|
}
|
|
380
651
|
|
|
@@ -664,6 +935,8 @@ if (flags.help) {
|
|
|
664
935
|
naide Start interactive REPL
|
|
665
936
|
naide <file.naide> Run a NAIDE file
|
|
666
937
|
naide <file.nx> Run a NAIDE-X file (auto-detected)
|
|
938
|
+
naide ~~ "instruction" Generate NAIDE code from instruction (no AI)
|
|
939
|
+
naide gen "instruction" Same as ~~ (alias)
|
|
667
940
|
naide init [dir] Create a new NAIDE project
|
|
668
941
|
naide build [dir] [outdir] Transpile all files to JavaScript
|
|
669
942
|
naide check <files...> Type-check without running
|
|
@@ -683,10 +956,28 @@ if (flags.help) {
|
|
|
683
956
|
naide -w <file.naide> Watch mode (auto-restart on changes)
|
|
684
957
|
naide -d <file> Debug mode (Node.js inspector)
|
|
685
958
|
|
|
686
|
-
|
|
959
|
+
Code Generation (~~):
|
|
960
|
+
naide ~~ "instruction" Generate code from instruction (no AI)
|
|
961
|
+
naide gen "instruction" Alias for ~~
|
|
962
|
+
naide --expand <file> Show ~~ directive expansion results
|
|
963
|
+
In .naide files: ~~ "instruction" expands at compile time
|
|
964
|
+
|
|
965
|
+
Targets (15):
|
|
687
966
|
node Node.js / JavaScript (default)
|
|
688
967
|
bun Bun-optimized JavaScript
|
|
689
|
-
|
|
968
|
+
ts TypeScript
|
|
969
|
+
python Python (Flask)
|
|
970
|
+
go Go (net/http)
|
|
971
|
+
java Java (HttpServer)
|
|
972
|
+
rust Rust (actix-web)
|
|
973
|
+
cpp C++ (cpp-httplib)
|
|
974
|
+
c C (libmicrohttpd)
|
|
975
|
+
csharp C# (ASP.NET)
|
|
976
|
+
kotlin Kotlin (Ktor)
|
|
977
|
+
swift Swift (Vapor)
|
|
978
|
+
dart Dart (shelf)
|
|
979
|
+
php PHP
|
|
980
|
+
ruby Ruby (Sinatra)
|
|
690
981
|
|
|
691
982
|
Modes:
|
|
692
983
|
.naide Standard NAIDE (~40% fewer tokens than JS)
|
|
@@ -701,9 +992,18 @@ if (flags.help) {
|
|
|
701
992
|
-t, --target Compile target: node (default), bun, python/py
|
|
702
993
|
--check Type-check files without running
|
|
703
994
|
--mid Show intermediate NAIDE v1 (X mode only)
|
|
995
|
+
--expand Show ~~ directive expansion (debug)
|
|
704
996
|
--ast Print AST
|
|
705
997
|
--tokens Print tokens
|
|
706
998
|
-h, --help Show this help
|
|
999
|
+
|
|
1000
|
+
~~ Keyword Cheat Sheet:
|
|
1001
|
+
Intent server api rest bot cli page test database ai crud auth ws mail graphql
|
|
1002
|
+
Composite todo blog chat shop fullstack board
|
|
1003
|
+
Platform discord slack telegram line
|
|
1004
|
+
JP Intent サーバー 認証 ログイン 会員 CRUD 管理 データベース ボット テスト ページ
|
|
1005
|
+
JP Entity ユーザー 商品 記事 タスク 注文 コメント イベント 問い合わせ 掲示板 決済
|
|
1006
|
+
Fields "with name email age done" → auto-inferred types
|
|
707
1007
|
`);
|
|
708
1008
|
process.exit(0);
|
|
709
1009
|
}
|
|
@@ -872,7 +1172,12 @@ for (const file of files) {
|
|
|
872
1172
|
} catch {}
|
|
873
1173
|
}
|
|
874
1174
|
} catch (err) {
|
|
875
|
-
|
|
1175
|
+
const isSyntaxErr = err.message.includes('line ') || err.message.includes('Unexpected') || err.message.includes('Expected');
|
|
1176
|
+
if (isSyntaxErr) {
|
|
1177
|
+
console.error(`\n${err.message}`);
|
|
1178
|
+
} else {
|
|
1179
|
+
crashReport(err, { file, target: flags.target });
|
|
1180
|
+
}
|
|
876
1181
|
if (process.env.NAIDE_DEBUG) console.error(err.stack);
|
|
877
1182
|
process.exit(1);
|
|
878
1183
|
}
|
package/mcp/server.js
CHANGED
|
@@ -63,6 +63,19 @@ const TOOLS = [
|
|
|
63
63
|
description: 'List all available NAIDE compilation targets with details.',
|
|
64
64
|
inputSchema: { type: 'object', properties: {} }
|
|
65
65
|
},
|
|
66
|
+
{
|
|
67
|
+
name: 'naide_generate',
|
|
68
|
+
description: 'Generate NAIDE code from a natural language instruction. No AI — uses pattern matching and template composition with self-healing parser validation. Extremely lightweight, runs in microseconds.',
|
|
69
|
+
inputSchema: {
|
|
70
|
+
type: 'object',
|
|
71
|
+
properties: {
|
|
72
|
+
instruction: { type: 'string', description: 'Natural language instruction (e.g. "REST API for users with auth", "Discord bot", "todo app")' },
|
|
73
|
+
port: { type: 'number', description: 'Server port (default: 3000)' },
|
|
74
|
+
schemaName: { type: 'string', description: 'Override schema/entity name' }
|
|
75
|
+
},
|
|
76
|
+
required: ['instruction']
|
|
77
|
+
}
|
|
78
|
+
},
|
|
66
79
|
{
|
|
67
80
|
name: 'naide_spec',
|
|
68
81
|
description: 'Get the NAIDE language specification. Use this to understand NAIDE syntax before writing code.',
|
|
@@ -133,6 +146,20 @@ async function handleToolCall(name, args) {
|
|
|
133
146
|
return { content: [{ type: 'text', text: `NAIDE Compilation Targets (15):\n\n${'Target'.padEnd(12)} ${'Language'.padEnd(25)} ${'Server'.padEnd(20)} Flag\n${'─'.repeat(75)}\n${text}` }] };
|
|
134
147
|
}
|
|
135
148
|
|
|
149
|
+
case 'naide_generate': {
|
|
150
|
+
try {
|
|
151
|
+
const { generate } = await import(resolve(__dirname, '..', 'src', 'gen.js'));
|
|
152
|
+
const opts = {};
|
|
153
|
+
if (args.port) opts.port = args.port;
|
|
154
|
+
if (args.schemaName) opts.schemaName = args.schemaName;
|
|
155
|
+
const result = generate(args.instruction, opts);
|
|
156
|
+
const meta = `[${result.intents.join(' + ')}]${result.schema ? ` → ${result.schema}` : ''} | valid: ${result.valid}${result.fixed ? ' (auto-fixed)' : ''}`;
|
|
157
|
+
return { content: [{ type: 'text', text: `${result.code}\n# ${meta}` }] };
|
|
158
|
+
} catch (e) {
|
|
159
|
+
return { content: [{ type: 'text', text: `Generation error: ${e.message}` }], isError: true };
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
136
163
|
case 'naide_spec': {
|
|
137
164
|
try {
|
|
138
165
|
let spec = readFileSync(resolve(__dirname, '..', 'SPEC.naide'), 'utf-8');
|
|
@@ -165,7 +192,7 @@ async function handleMessage(msg) {
|
|
|
165
192
|
respond(msg.id, {
|
|
166
193
|
protocolVersion: '2024-11-05',
|
|
167
194
|
capabilities: { tools: {} },
|
|
168
|
-
serverInfo: { name: 'naide-mcp', version: '1.
|
|
195
|
+
serverInfo: { name: 'naide-mcp', version: '1.21.0' }
|
|
169
196
|
});
|
|
170
197
|
break;
|
|
171
198
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "naider",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.21.0",
|
|
4
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.",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"exports": {
|