naider 1.20.0 → 1.21.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/README.md +73 -1
- package/bin/naide.js +148 -18
- package/mcp/server.js +1 -1
- package/package.json +1 -1
- package/src/gen.js +1173 -50
- package/src/index.js +2 -2
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,7 +8,7 @@ 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.
|
|
11
|
+
const NAIDE_VERSION = '1.21.1';
|
|
12
12
|
|
|
13
13
|
function crashReport(err, context = {}) {
|
|
14
14
|
const info = [
|
|
@@ -114,6 +114,9 @@ const flags = {
|
|
|
114
114
|
check: false,
|
|
115
115
|
target: 'node',
|
|
116
116
|
expand: false,
|
|
117
|
+
project: false,
|
|
118
|
+
write: null,
|
|
119
|
+
replace: false,
|
|
117
120
|
};
|
|
118
121
|
|
|
119
122
|
const files = [];
|
|
@@ -133,6 +136,9 @@ for (let i = 0; i < args.length; i++) {
|
|
|
133
136
|
case '--check': flags.check = true; flags.run = false; break;
|
|
134
137
|
case '--target': case '-t': flags.target = args[++i]; break;
|
|
135
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;
|
|
136
142
|
default: files.push(arg);
|
|
137
143
|
}
|
|
138
144
|
}
|
|
@@ -142,25 +148,41 @@ if (files[0] === 'gen' || files[0] === '~~') {
|
|
|
142
148
|
const instruction = files.slice(1).join(' ');
|
|
143
149
|
if (!instruction) {
|
|
144
150
|
console.log(`
|
|
145
|
-
NAIDE
|
|
151
|
+
NAIDE Agent Coder (~~)
|
|
146
152
|
|
|
147
153
|
Usage:
|
|
148
154
|
naide ~~ "REST API for users with auth"
|
|
149
155
|
naide gen "todo app with database"
|
|
150
|
-
naide gen "
|
|
156
|
+
naide gen "users and products and orders with auth"
|
|
157
|
+
naide ~~ "ECサイト" --project
|
|
151
158
|
|
|
152
|
-
|
|
153
|
-
|
|
159
|
+
Autonomous code generator with analysis, multi-entity support,
|
|
160
|
+
relationship detection, and full auth routes. No AI, no network.
|
|
154
161
|
|
|
155
162
|
Examples:
|
|
156
163
|
naide ~~ "REST API for products with name price stock"
|
|
157
|
-
naide ~~ "
|
|
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
|
|
158
168
|
naide ~~ "Discord bot with hello and help commands"
|
|
159
|
-
naide ~~ "
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
naide ~~ "
|
|
163
|
-
naide
|
|
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, openapi.json,
|
|
175
|
+
# admin.html, client.mjs
|
|
176
|
+
|
|
177
|
+
Update existing project (re-run --project in same dir):
|
|
178
|
+
naide ~~ "add users with auth" --project -o my-app
|
|
179
|
+
naide ~~ "add products" --project -o my-app
|
|
180
|
+
# Merges new entities, regenerates all supporting files
|
|
181
|
+
|
|
182
|
+
File Write Mode:
|
|
183
|
+
naide ~~ "add auth" app.naide # merge into existing file
|
|
184
|
+
naide ~~ "add websocket" --write app.naide # explicit --write
|
|
185
|
+
naide ~~ "REST API" --write app.naide --replace # overwrite file
|
|
164
186
|
|
|
165
187
|
In .naide files:
|
|
166
188
|
~~ "REST API for users with auth"
|
|
@@ -170,15 +192,18 @@ if (files[0] === 'gen' || files[0] === '~~') {
|
|
|
170
192
|
naide --expand app.naide Show ~~ expansion results
|
|
171
193
|
|
|
172
194
|
API:
|
|
173
|
-
import { generate } from 'naider';
|
|
174
|
-
const result = generate("
|
|
175
|
-
console.log(result.
|
|
195
|
+
import { generate, generateProject, writeToFile } from 'naider';
|
|
196
|
+
const result = generate("users and products with auth");
|
|
197
|
+
console.log(result.analysis); // agent analysis
|
|
198
|
+
console.log(result.entities); // ['User', 'Product']
|
|
199
|
+
writeToFile("app.naide", "add auth"); // merge into file
|
|
176
200
|
|
|
177
201
|
── Keyword Cheat Sheet ──────────────────────────────────────
|
|
178
202
|
|
|
179
203
|
Intent server, api, rest, bot, cli, page, test, database,
|
|
180
204
|
ai, crud, auth, websocket, mail, graphql
|
|
181
|
-
Composite todo, blog, chat, shop, fullstack, board
|
|
205
|
+
Composite todo, blog, chat, shop, fullstack, board,
|
|
206
|
+
crm, inventory, booking, sns
|
|
182
207
|
Platform discord, slack, telegram, line
|
|
183
208
|
DB Type sqlite, postgres
|
|
184
209
|
JP Intent サーバー, 認証, ログイン, 会員, CRUD, 管理, データベース,
|
|
@@ -187,25 +212,130 @@ if (files[0] === 'gen' || files[0] === '~~') {
|
|
|
187
212
|
問い合わせ, 通知, カテゴリ, プロジェクト, 掲示板, 決済
|
|
188
213
|
Fields "with name email age" — auto-inferred types & validators
|
|
189
214
|
Field Alias e-mail→email, pwd→password, tel→phone, desc→description
|
|
215
|
+
Relations Auto-detected: User→Post, User→Order, Product→Order, etc.
|
|
190
216
|
|
|
191
217
|
────────────────────────────────────────────────────────────
|
|
192
218
|
`);
|
|
193
219
|
process.exit(0);
|
|
194
220
|
}
|
|
195
221
|
|
|
196
|
-
const { generate } = await import('../src/gen.js');
|
|
222
|
+
const { generate, generateProject, updateProject, writeToFile } = await import('../src/gen.js');
|
|
223
|
+
|
|
224
|
+
// Write mode: inject into existing .naide/.nx file
|
|
225
|
+
const targetFile = instruction.match(/\S+\.(?:naide|nx)$/)?.[0];
|
|
226
|
+
if (targetFile || flags.write) {
|
|
227
|
+
const file = flags.write || targetFile;
|
|
228
|
+
const cleanInstruction = targetFile ? instruction.replace(/\s+\S+\.(?:naide|nx)$/, '') : instruction;
|
|
229
|
+
const mode = flags.replace ? 'replace' : 'append';
|
|
230
|
+
const result = writeToFile(resolve(file), cleanInstruction, { mode });
|
|
231
|
+
const a = result.analysis;
|
|
232
|
+
process.stderr.write('\n ── NAIDE Agent ──────────────────────────────────────────\n');
|
|
233
|
+
process.stderr.write(` Input: "${a.instruction}"\n`);
|
|
234
|
+
process.stderr.write(` Action: ${result.action} → ${file}\n`);
|
|
235
|
+
if (a.entities.length > 0) {
|
|
236
|
+
process.stderr.write(` Entities: ${a.entities.map(e => `${e.name} (${e.fieldCount} fields)`).join(', ')}\n`);
|
|
237
|
+
}
|
|
238
|
+
process.stderr.write(' ────────────────────────────────────────────────────────\n\n');
|
|
239
|
+
console.log(result.code);
|
|
240
|
+
process.stderr.write(` ── ${result.action}: ${file} (valid: ${result.valid}) ──\n`);
|
|
241
|
+
process.exit(0);
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
// Project mode: generate or update project directory
|
|
245
|
+
if (flags.project) {
|
|
246
|
+
const isCurrentDir = instruction === '.' || instruction === './';
|
|
247
|
+
let dir, result;
|
|
248
|
+
|
|
249
|
+
if (isCurrentDir) {
|
|
250
|
+
// naide gen . --project → update current directory
|
|
251
|
+
dir = '.';
|
|
252
|
+
const outDir = resolve(dir);
|
|
253
|
+
if (!existsSync(resolve(outDir, 'app.naide'))) {
|
|
254
|
+
process.stderr.write(' Error: no app.naide found in current directory.\n');
|
|
255
|
+
process.stderr.write(' Use naide ~~ "instruction" --project to create a new project.\n');
|
|
256
|
+
process.exit(1);
|
|
257
|
+
}
|
|
258
|
+
process.stderr.write(' Error: use naide ~~ "instruction" --project in an existing project dir to update.\n');
|
|
259
|
+
process.exit(1);
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
const primaryGuess = instruction.split(/\s+/)[0].toLowerCase().replace(/[^a-z0-9]/g, '') || 'app';
|
|
263
|
+
dir = flags.output || (primaryGuess + '-app');
|
|
264
|
+
const outDir = resolve(dir);
|
|
265
|
+
const isUpdate = existsSync(resolve(outDir, 'app.naide'));
|
|
266
|
+
|
|
267
|
+
if (isUpdate) {
|
|
268
|
+
result = updateProject(outDir, instruction);
|
|
269
|
+
} else {
|
|
270
|
+
result = generateProject(instruction);
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
const primaryName = (result.entities[0] || 'app').toLowerCase().replace(/[^a-z0-9]/g, '');
|
|
274
|
+
if (!flags.output && !isUpdate) dir = primaryName + '-app';
|
|
275
|
+
const finalDir = resolve(dir);
|
|
276
|
+
if (!existsSync(finalDir)) mkdirSync(finalDir, { recursive: true });
|
|
277
|
+
for (const f of result.files) {
|
|
278
|
+
const fp = resolve(finalDir, f.path);
|
|
279
|
+
writeFileSync(fp, f.content, 'utf-8');
|
|
280
|
+
}
|
|
281
|
+
const a = result.analysis;
|
|
282
|
+
const mode = isUpdate ? 'Updated' : 'Created';
|
|
283
|
+
process.stderr.write(`\n ── NAIDE Agent (${mode}) ────────────────────────────────\n`);
|
|
284
|
+
process.stderr.write(` Input: "${a.instruction}"\n`);
|
|
285
|
+
if (a.entities.length > 0) {
|
|
286
|
+
process.stderr.write(` Entities: ${a.entities.map(e => `${e.name} (${e.fieldCount} fields)`).join(', ')}\n`);
|
|
287
|
+
}
|
|
288
|
+
if (a.relationships && a.relationships.length > 0) {
|
|
289
|
+
for (const r of a.relationships) process.stderr.write(` Relation: ${r.parent} 1→N ${r.child} (${r.fk})\n`);
|
|
290
|
+
}
|
|
291
|
+
if (a.added && a.added.length > 0) {
|
|
292
|
+
process.stderr.write(` Added: ${a.added.join(', ')}\n`);
|
|
293
|
+
}
|
|
294
|
+
process.stderr.write(` Plan:\n`);
|
|
295
|
+
for (const step of a.plan) process.stderr.write(` + ${step}\n`);
|
|
296
|
+
process.stderr.write(' ────────────────────────────────────────────────────────\n\n');
|
|
297
|
+
console.log(` Project ${isUpdate ? 'updated' : 'created'}: ${dir}/`);
|
|
298
|
+
for (const f of result.files) console.log(` ${f.path}`);
|
|
299
|
+
if (!isUpdate) console.log(`\n cd ${dir} && npm install && npm run dev\n`);
|
|
300
|
+
else console.log(`\n Files regenerated. Run: npm run dev\n`);
|
|
301
|
+
process.exit(0);
|
|
302
|
+
}
|
|
303
|
+
|
|
197
304
|
const result = generate(instruction);
|
|
198
305
|
|
|
199
306
|
if (flags.output) {
|
|
200
307
|
writeFileSync(flags.output, result.code, 'utf-8');
|
|
201
308
|
console.log(` Generated: ${flags.output} (${result.intents.join(' + ')})`);
|
|
202
309
|
} else {
|
|
310
|
+
// Agent-style analysis header
|
|
311
|
+
const a = result.analysis;
|
|
312
|
+
process.stderr.write('\n ── NAIDE Agent ──────────────────────────────────────────\n');
|
|
313
|
+
process.stderr.write(` Input: "${a.instruction}"\n`);
|
|
314
|
+
if (a.entities.length > 0) {
|
|
315
|
+
process.stderr.write(` Entities: ${a.entities.map(e => `${e.name} (${e.fieldCount} fields)`).join(', ')}\n`);
|
|
316
|
+
}
|
|
317
|
+
if (a.relationships.length > 0) {
|
|
318
|
+
for (const r of a.relationships) {
|
|
319
|
+
process.stderr.write(` Relation: ${r.parent} 1→N ${r.child} (${r.fk})\n`);
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
process.stderr.write(` Plan:\n`);
|
|
323
|
+
for (const step of a.plan) {
|
|
324
|
+
process.stderr.write(` + ${step}\n`);
|
|
325
|
+
}
|
|
326
|
+
process.stderr.write(' ────────────────────────────────────────────────────────\n\n');
|
|
327
|
+
|
|
203
328
|
console.log(result.code);
|
|
329
|
+
|
|
204
330
|
const meta = [`[${result.intents.join(' + ')}]`];
|
|
205
|
-
if (result.
|
|
331
|
+
if (result.entities && result.entities.length > 1) {
|
|
332
|
+
meta.push(`→ ${result.entities.join(', ')}`);
|
|
333
|
+
} else if (result.schema) {
|
|
334
|
+
meta.push(`→ ${result.schema}`);
|
|
335
|
+
}
|
|
206
336
|
meta.push(`valid: ${result.valid}`);
|
|
207
337
|
if (result.fixed) meta.push('(auto-fixed)');
|
|
208
|
-
process.stderr.write(
|
|
338
|
+
process.stderr.write(` ── gen ~~ ${meta.join(' ')} ──\n`);
|
|
209
339
|
if (result.repairs.length > 0) {
|
|
210
340
|
process.stderr.write(' Repairs:\n');
|
|
211
341
|
for (const r of result.repairs) process.stderr.write(` - ${r}\n`);
|
package/mcp/server.js
CHANGED
|
@@ -192,7 +192,7 @@ async function handleMessage(msg) {
|
|
|
192
192
|
respond(msg.id, {
|
|
193
193
|
protocolVersion: '2024-11-05',
|
|
194
194
|
capabilities: { tools: {} },
|
|
195
|
-
serverInfo: { name: 'naide-mcp', version: '1.
|
|
195
|
+
serverInfo: { name: 'naide-mcp', version: '1.21.1' }
|
|
196
196
|
});
|
|
197
197
|
break;
|
|
198
198
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "naider",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.21.1",
|
|
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": {
|