db-model-router 1.0.0 → 1.0.3

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/docs/SKILL.md CHANGED
@@ -1,15 +1,45 @@
1
+ ---
2
+ name: db-model-router
3
+ description: Database-agnostic REST API generator for Node.js/Express. Define model → get CRUD API + Express routes. 9 adapters, identical API. Works with express or ultimate-express.
4
+ ---
5
+
1
6
  # db-model-router — LLM Skill Reference
2
7
 
3
- Database-agnostic REST API generator for Node.js/Express. Define model → get CRUD API + Express routes. 9 adapters, identical API.
8
+ Database-agnostic REST API generator for Node.js/Express. Define model → get CRUD API + Express routes. 9 adapters, identical API. Works with `express` or `ultimate-express`.
9
+
10
+ ## LLM Workflow (follow this order)
11
+
12
+ 1. **Scaffold**: `db-model-router-init --framework express --database postgres --session redis --rateLimiting --helmet --logger` (all flags → zero prompts)
13
+ 2. **Migrations**: Write SQL/JS migration files into `migrations/` for the user's schema, then `npm run migrate`
14
+ 3. **Generate models**: `db-model-router-generate-model --type postgres --env .env --output ./models`
15
+ 4. **Generate routes + tests**: `db-model-router-generate-route --models ./models --output ./routes --tables users,posts,posts.comments`
16
+ 5. **Run**: `npm run dev`
17
+
18
+ Step 1 creates the project. Step 2 defines the schema. Steps 3-4 introspect the DB and generate models, routes, tests, and OpenAPI spec. Step 5 starts the server.
4
19
 
5
20
  ## Install
6
21
 
7
22
  ```bash
8
- npm install db-model-router <driver>
23
+ npm install db-model-router <framework> <driver>
9
24
  ```
10
25
 
26
+ Frameworks: `express` or `ultimate-express` (auto-detected, prefers ultimate-express)
11
27
  Drivers: `mysql2`, `pg`, `better-sqlite3`, `mongodb`, `mssql`, `oracledb`, `ioredis`, `@aws-sdk/client-dynamodb @aws-sdk/lib-dynamodb`
12
28
 
29
+ ## Adapters
30
+
31
+ | Module Key | Driver | Default Port |
32
+ | ------------- | ------------------------------------------------ | ------------ |
33
+ | `mysql` | mysql2 | 3306 |
34
+ | `postgres` | pg | 5432 |
35
+ | `sqlite3` | better-sqlite3 | — |
36
+ | `mongodb` | mongodb | 27017 |
37
+ | `mssql` | mssql | 1433 |
38
+ | `cockroachdb` | pg | 26257 |
39
+ | `oracle` | oracledb | 1521 |
40
+ | `redis` | ioredis | 6379 |
41
+ | `dynamodb` | @aws-sdk/client-dynamodb + @aws-sdk/lib-dynamodb | — |
42
+
13
43
  ## Init → Connect → Model → Route
14
44
 
15
45
  ```js
@@ -42,12 +72,12 @@ app.use("/users", route(users));
42
72
 
43
73
  ## model(db, table, structure, pk, unique, option)
44
74
 
45
- | Param | Type | Description |
46
- | --------- | --------------- | --------------------------------------------------------------------------------------------------------------------- |
47
- | structure | `{col: "rule"}` | Types: `string\|integer\|numeric\|object`. Prefix `required\|` for NOT NULL. Exclude PK, timestamps, soft-delete cols |
48
- | pk | string | Primary key column. Default `"id"` |
49
- | unique | string[] | Columns for upsert conflict resolution |
50
- | option | object | `{ safeDelete, created_at, modified_at }` — column names or null |
75
+ | Param | Type | Description |
76
+ | --------- | --------------- | ------------------------------------------------------------------------------------------------------------------------------ |
77
+ | structure | `{col: "rule"}` | Types: `string\|integer\|numeric\|boolean\|object`. Prefix `required\|` for NOT NULL. Exclude PK, timestamps, soft-delete cols |
78
+ | pk | string | Primary key column. Default `"id"` |
79
+ | unique | string[] | Columns for upsert conflict resolution |
80
+ | option | object | `{ safeDelete, created_at, modified_at }` — column names or null |
51
81
 
52
82
  ## Model Methods (all async)
53
83
 
@@ -83,14 +113,22 @@ Structure: `[OR_groups[AND_conditions[col, op, val]]]`
83
113
  Ops: `= != < > <= >= like not like in not in`
84
114
 
85
115
  ```js
116
+ // AND: age > 25 AND type = 1
86
117
  [
87
118
  [
88
119
  ["age", ">", 25],
89
120
  ["type", "=", 1],
90
121
  ],
91
- ][([["name", "=", "A"]], [["name", "=", "B"]])][[["type", "in", [1, 2, 3]]]][ // AND // OR // IN
122
+ ][
123
+ // OR: name = "A" OR name = "B"
124
+ ([["name", "=", "A"]], [["name", "=", "B"]])
125
+ ][
126
+ // IN
127
+ [["type", "in", [1, 2, 3]]]
128
+ ][
129
+ // LIKE (auto-wraps with %)
92
130
  [["name", "like", "Ali"]]
93
- ]; // LIKE %Ali%
131
+ ];
94
132
  ```
95
133
 
96
134
  ## route(model, override?)
@@ -100,14 +138,14 @@ Generates Express Router with 9 endpoints:
100
138
  | Method | Path | Action |
101
139
  | ------ | ------ | -------------------------------- |
102
140
  | GET | `/:pk` | Get by PK |
103
- | POST | `/:id` | Insert single |
104
- | PUT | `/:id` | Update single (PK from URL) |
105
- | PATCH | `/:id` | Partial update |
106
- | DELETE | `/:id` | Delete single |
141
+ | POST | `/add` | Insert single |
142
+ | PUT | `/:pk` | Update single (PK from URL) |
143
+ | PATCH | `/:pk` | Partial update (PK from URL) |
144
+ | DELETE | `/:pk` | Delete single |
107
145
  | GET | `/` | List (page, size, sort, filters) |
108
146
  | POST | `/` | Bulk insert `{data:[...]}` |
109
147
  | PUT | `/` | Bulk update `{data:[...]}` |
110
- | DELETE | `/` | Bulk delete `{data:[...]}` |
148
+ | DELETE | `/` | Bulk delete |
111
149
 
112
150
  **Payload override** (multi-tenancy): `route(m, { tenant_id: "user.tenant_id" })` — maps cols to `req` paths via lodash.get.
113
151
 
@@ -115,29 +153,94 @@ Generates Express Router with 9 endpoints:
115
153
 
116
154
  ## CLI Tools
117
155
 
118
- ### generate-app (full scaffold)
156
+ ### db-model-router-init (interactive project scaffold)
157
+
158
+ Scaffolds a complete Express-based REST API project from scratch. Supports both interactive prompts and fully non-interactive CLI flags.
119
159
 
120
160
  ```bash
121
- rest-router-generate-app --type mysql --env .env [--output ./dir] [--tables users,posts,posts.comments]
161
+ # Interactive (prompts for everything)
162
+ npx db-model-router-init
163
+
164
+ # Fully non-interactive (LLM-friendly — no prompts)
165
+ db-model-router-init --framework express --database postgres --session redis --rateLimiting --helmet --logger
166
+
167
+ # Partial flags — only prompts for missing values
168
+ db-model-router-init --database mysql --session memory
122
169
  ```
123
170
 
124
- Creates: `app.js`, `models/`, `routes/`, `middleware/logger.js`, `.env.example`, `.gitignore`, `migrations/`, `sessions/`, `openapi.json`
171
+ Flags: `--framework <name>`, `--database <name>` (or `--db`), `--session <type>`, `--rateLimiting`, `--helmet`, `--logger`, `--help`
172
+
173
+ When all 6 options are provided, runs with zero prompts.
174
+
175
+ Prompts (for missing values): framework (`ultimate-express`|`express`), database (9 options), session (`memory`|`redis`|`database`), rate limiting (y/n), helmet (y/n), logger (y/n).
176
+
177
+ If no `package.json` exists, runs `npm init` first. After prompts: generates files, updates `package.json`, runs `npm install`.
178
+
179
+ Generated structure:
180
+
181
+ ```
182
+ app.js, .env, .env.example, .gitignore, migrate.js, add_migration.js,
183
+ middleware/logger.js, migrations/{timestamp}_create_migrations_table.sql|.js
184
+ ```
185
+
186
+ Session migration (`{timestamp}_create_sessions_table.sql`) generated only for SQL databases with `session=database`.
187
+
188
+ Scripts added: `start` (node app.js), `dev` (nodemon app.js), `test`, `migrate` (node migrate.js), `add_migration` (node add_migration.js).
189
+
190
+ Dependencies always included: `db-model-router`, `dotenv`, selected framework, database driver(s), `express-session`. DevDeps: `nodemon`.
191
+ Conditional: `connect-redis` + `ioredis` (session=redis), `express-rate-limit`, `helmet`, `express-mung` (logger).
192
+
193
+ #### Environment Variables by Database
194
+
195
+ | Database | Variables |
196
+ | ----------- | --------------------------------------------------------------------------- |
197
+ | mysql | `PORT=3000 DB_HOST DB_PORT=3306 DB_NAME DB_USER DB_PASS` |
198
+ | postgres | `PORT=3000 DB_HOST DB_PORT=5432 DB_NAME DB_USER DB_PASS` |
199
+ | cockroachdb | `PORT=3000 DB_HOST DB_PORT=26257 DB_NAME DB_USER DB_PASS` |
200
+ | sqlite3 | `PORT=3000 DB_NAME=./data.db` |
201
+ | mongodb | `PORT=3000 DB_HOST DB_PORT=27017 DB_NAME DB_USER DB_PASS` |
202
+ | mssql | `PORT=3000 DB_HOST DB_PORT=1433 DB_NAME DB_USER DB_PASS` |
203
+ | oracle | `PORT=3000 DB_HOST DB_PORT=1521 DB_NAME DB_USER DB_PASS` |
204
+ | redis | `PORT=3000 DB_HOST DB_PORT=6379 DB_PASS` |
205
+ | dynamodb | `PORT=3000 AWS_REGION AWS_ENDPOINT AWS_ACCESS_KEY_ID AWS_SECRET_ACCESS_KEY` |
206
+
207
+ When `session=redis` and database ≠ redis: adds `REDIS_HOST=localhost REDIS_PORT=6379 REDIS_PASS`.
208
+
209
+ #### Migration Infrastructure
210
+
211
+ - `migrate.js` — reads `migrations/` dir, diffs against `_migrations` tracking table, executes pending in order
212
+ - `add_migration.js` — creates timestamped empty migration (`.sql` for SQL, `.js` for NoSQL)
213
+ - Tracking table `_migrations`: `{id, filename, executed_at, checksum}` (SQL) or equivalent collection/hash/table (NoSQL)
214
+ - Timestamp format: `YYYYMMDDHHMMSS`
125
215
 
126
216
  ### generate-model (DB introspection → model files)
127
217
 
128
218
  ```bash
129
- rest-router-generate-model --type <db> --env .env [--output ./models] [--tables t1,t2] [--schema public]
219
+ db-model-router-generate-model --type <db> --env .env [--output ./models] [--tables t1,t2] [--schema public]
130
220
  ```
131
221
 
132
- Auto-detects: PK, unique indexes, DEFAULT→optional, timestamp cols, soft-delete cols.
222
+ Options: `--type`, `--host`, `--port`, `--database`, `--user`, `--password`, `--schema`, `--output`, `--tables`, `--env`
223
+
224
+ Auto-detects: PK, unique indexes, DEFAULT→optional, timestamp cols (`created_at`, `updated_at`, `modified_at`, `createdAt`, etc.), soft-delete cols (`is_deleted`, `deleted`, `is_active`, `archived`, etc.). Multi-column unique indexes correctly grouped.
133
225
 
134
- ### generate-route (model files → route files + OpenAPI)
226
+ ### generate-route (model files → route files + tests + OpenAPI)
135
227
 
136
228
  ```bash
137
- rest-router-generate-route --models ./models --output ./routes [--tables posts,posts.comments]
229
+ db-model-router-generate-route --models ./models --output ./routes [--tables posts,posts.comments]
138
230
  ```
139
231
 
140
- Dot notation `parent.child` creates nested routes: `parent/:parent_id/child` with FK scoping via `<parent_singular>_id`.
232
+ Options: `--models`, `--output`, `--type`, `--host`, `--port`, `--database`, `--user`, `--password`, `--schema`, `--tables`, `--env`
233
+
234
+ Generates:
235
+
236
+ - Route files for each model
237
+ - `index.js` mounting all routes on an Express Router
238
+ - `openapi.json` (OpenAPI 3.0 spec)
239
+ - Test files in `tests/` directory for all routes and methods (uses `supertest` + `assert`)
240
+
241
+ Dot notation `parent.child` creates nested routes: `parent/:parent_id/child` with FK scoping via `<parent_singular>_id`. Also generates child route test files.
242
+
243
+ Generated test files cover all 8 CRUD endpoints per table: GET by ID, POST add, PUT update, DELETE, list, bulk insert, bulk update, bulk delete.
141
244
 
142
245
  ## Connection Configs
143
246
 
@@ -192,7 +295,7 @@ db.connect({ region, endpoint, accessKeyId, secretAccessKey });
192
295
  ## Rules
193
296
 
194
297
  1. `init()` before `db.connect()`. Don't destructure `db` before `init()`.
195
- 2. `modelStructure` excludes: PK col, timestamp cols, soft-delete cols.
298
+ 2. `model structure` excludes: PK col, timestamp cols, soft-delete cols.
196
299
  3. `update()`/`patch()` require PK in payload. `upsert()` PK is optional.
197
300
  4. `findOne()` returns `false` on no match. `byId()` returns `null`.
198
301
  5. Bulk ops wrap in `{ data: [...] }`. Single ops use flat object.
@@ -200,3 +303,72 @@ db.connect({ region, endpoint, accessKeyId, secretAccessKey });
200
303
  7. `safeDelete` makes `remove()` soft-delete; all reads auto-filter deleted rows.
201
304
  8. `list()` defaults: page=0, size=30. `sort` array: `["-col"]` for DESC.
202
305
  9. CommonJS only (`require`). Use dynamic `import()` for ESM.
306
+ 10. `db-model-router-init` for new projects (interactive). `generate-model` + `generate-route` for existing databases. Or use the unified `db-model-router` CLI with a `dbmr.schema.json` file.
307
+ 11. `generate-route` auto-generates test files alongside routes. Tests use `supertest`.
308
+
309
+ ## Schema-Driven Workflow (Unified CLI)
310
+
311
+ The `db-model-router` command is the unified CLI entry point. It uses a single `dbmr.schema.json` file as the source of truth for all code generation.
312
+
313
+ ### LLM Workflow (schema-driven)
314
+
315
+ 1. **Inspect** (existing DB): `db-model-router inspect --type postgres --env .env` → writes `dbmr.schema.json`
316
+ 2. **Edit** `dbmr.schema.json` — add relationships, tweak columns, set options
317
+ 3. **Generate**: `db-model-router generate --from dbmr.schema.json` → models, routes, tests, OpenAPI
318
+ 4. **Doctor**: `db-model-router doctor --from dbmr.schema.json` → validate schema + check sync
319
+ 5. **Run**: `npm run dev`
320
+
321
+ Or for new projects: `db-model-router init --from dbmr.schema.json --yes`
322
+
323
+ ### Subcommands
324
+
325
+ | Subcommand | Description |
326
+ | ---------- | ----------------------------------------------------------- |
327
+ | `init` | Scaffold project (optionally from schema file via `--from`) |
328
+ | `inspect` | Introspect live DB → produce `dbmr.schema.json` |
329
+ | `generate` | Generate models/routes/tests/OpenAPI from schema |
330
+ | `doctor` | Validate schema, check deps, verify files in sync |
331
+ | `diff` | Preview changes regeneration would make (read-only) |
332
+
333
+ ### Universal Flags
334
+
335
+ All subcommands accept: `--yes`, `--json`, `--dry-run`, `--no-install`, `--help`.
336
+
337
+ - `--yes`: suppress prompts, accept defaults
338
+ - `--json`: machine-readable JSON output only
339
+ - `--dry-run`: preview without writing files
340
+ - `--no-install`: skip `npm install`
341
+
342
+ ### dbmr.schema.json Format
343
+
344
+ ```json
345
+ {
346
+ "adapter": "postgres",
347
+ "framework": "express",
348
+ "options": {
349
+ "session": "redis",
350
+ "rateLimiting": true,
351
+ "helmet": true,
352
+ "logger": true
353
+ },
354
+ "tables": {
355
+ "users": {
356
+ "columns": {
357
+ "name": "required|string",
358
+ "email": "required|string",
359
+ "age": "integer"
360
+ },
361
+ "pk": "id",
362
+ "unique": ["email"],
363
+ "softDelete": "is_deleted",
364
+ "timestamps": { "created_at": "created_at", "modified_at": "updated_at" }
365
+ }
366
+ },
367
+ "relationships": [
368
+ { "parent": "users", "child": "posts", "foreignKey": "user_id" }
369
+ ]
370
+ }
371
+ ```
372
+
373
+ Fields per table: `columns` (required), `pk` (default `"id"`), `unique` (default `[pk]`), `softDelete`, `timestamps`.
374
+ Column rules: `(required|)?(string|integer|numeric|boolean|object)`.
@@ -46,4 +46,4 @@ CREATE TABLE users (
46
46
  );
47
47
  ```
48
48
 
49
- [← Back to main docs](../README.md)
49
+ [← Back to main docs](../../README.md)
@@ -50,4 +50,4 @@ aws dynamodb create-table \
50
50
  --endpoint-url http://localhost:8000
51
51
  ```
52
52
 
53
- [← Back to main docs](../README.md)
53
+ [← Back to main docs](../../README.md)
@@ -53,4 +53,4 @@ const users = model(
53
53
  );
54
54
  ```
55
55
 
56
- [← Back to main docs](../README.md)
56
+ [← Back to main docs](../../README.md)
@@ -52,4 +52,4 @@ CREATE TABLE users (
52
52
  );
53
53
  ```
54
54
 
55
- [← Back to main docs](../README.md)
55
+ [← Back to main docs](../../README.md)
@@ -49,4 +49,4 @@ CREATE TABLE users (
49
49
  );
50
50
  ```
51
51
 
52
- [← Back to main docs](../README.md)
52
+ [← Back to main docs](../../README.md)
@@ -47,4 +47,4 @@ CREATE TABLE users (
47
47
  );
48
48
  ```
49
49
 
50
- [← Back to main docs](../README.md)
50
+ [← Back to main docs](../../README.md)
@@ -50,4 +50,4 @@ const users = model(
50
50
  );
51
51
  ```
52
52
 
53
- [← Back to main docs](../README.md)
53
+ [← Back to main docs](../../README.md)
@@ -40,4 +40,4 @@ CREATE TABLE users (
40
40
  );
41
41
  ```
42
42
 
43
- [← Back to main docs](../README.md)
43
+ [← Back to main docs](../../README.md)
package/package.json CHANGED
@@ -1,12 +1,10 @@
1
1
  {
2
2
  "name": "db-model-router",
3
- "version": "1.0.0",
3
+ "version": "1.0.3",
4
4
  "description": "Generative API Creation using mysql2 and express libraries in node js",
5
5
  "main": "src/index.js",
6
6
  "bin": {
7
- "generate-model": "src/cli/generate-model.js",
8
- "generate-route": "src/cli/generate-route.js",
9
- "generate-app": "src/cli/generate-app.js"
7
+ "db-model-router": "src/cli/main.js"
10
8
  },
11
9
  "scripts": {
12
10
  "dev": "nodemon src/serve.js",
@@ -30,6 +28,7 @@
30
28
  "keywords": [
31
29
  "mysql2",
32
30
  "express",
31
+ "ultimate-express",
33
32
  "generative",
34
33
  "rest",
35
34
  "api"
@@ -42,22 +41,22 @@
42
41
  "homepage": "https://github.com/AvinashSKaranth/db-model-router#readme",
43
42
  "dependencies": {
44
43
  "dotenv": "^10.0.0",
44
+ "inquirer": "^8.2.6",
45
45
  "lodash": "^4.17.21",
46
46
  "node-input-validator": "^4.5.0"
47
47
  },
48
48
  "peerDependencies": {
49
- "express": "^4.17.2 || ^5.0.0",
50
- "ultimate-express": "^2.0.0",
51
- "body-parser": "^1.20.0",
52
49
  "@aws-sdk/client-dynamodb": "^3.1029.0",
53
50
  "@aws-sdk/lib-dynamodb": "^3.1029.0",
54
51
  "better-sqlite3": "^12.9.0",
52
+ "express": "^4.17.2 || ^5.0.0",
55
53
  "ioredis": "^5.10.1",
56
54
  "mongodb": "^7.1.1",
57
55
  "mssql": "^12.2.1",
58
56
  "mysql2": "^3.14.4",
59
57
  "oracledb": "^6.10.0",
60
- "pg": "^8.20.0"
58
+ "pg": "^8.20.0",
59
+ "ultimate-express": "^2.0.0"
61
60
  },
62
61
  "peerDependenciesMeta": {
63
62
  "express": {
@@ -66,9 +65,6 @@
66
65
  "ultimate-express": {
67
66
  "optional": true
68
67
  },
69
- "body-parser": {
70
- "optional": true
71
- },
72
68
  "@aws-sdk/client-dynamodb": {
73
69
  "optional": true
74
70
  },
@@ -98,7 +94,6 @@
98
94
  }
99
95
  },
100
96
  "devDependencies": {
101
- "body-parser": "^1.20.0",
102
97
  "dotenv-cli": "^11.0.0",
103
98
  "express": "^4.21.0",
104
99
  "faker": "^5.5.3",
@@ -0,0 +1,114 @@
1
+ "use strict";
2
+
3
+ const fs = require("fs");
4
+ const path = require("path");
5
+ const { parseSchema } = require("../../schema/schema-parser");
6
+ const { SchemaValidationError } = require("../../schema/schema-validator");
7
+ const { schemaToModelMeta } = require("../../schema/schema-to-meta");
8
+ const { computeDiff } = require("../diff-engine");
9
+
10
+ /**
11
+ * Diff command handler for the unified CLI.
12
+ *
13
+ * Compares the current generated files against what the schema would produce,
14
+ * reporting additions, modifications (with line diffs), and deletions.
15
+ * Does NOT modify any files on disk.
16
+ *
17
+ * Supported flags:
18
+ * --from Path to schema file (default: dbmr.schema.json)
19
+ * --json Output JSON result via ctx
20
+ *
21
+ * @param {object} args - Parsed key-value args
22
+ * @param {object} flags - Universal flags: { yes, json, dryRun, noInstall, help }
23
+ * @param {import('../flags').OutputContext} ctx - Output context
24
+ */
25
+ async function diff(args, flags, ctx) {
26
+ const schemaFile = args.from || "dbmr.schema.json";
27
+ const schemaPath = path.resolve(schemaFile);
28
+ const baseDir = process.cwd();
29
+
30
+ // --- 1. Read and parse schema ---
31
+ if (!fs.existsSync(schemaPath)) {
32
+ const msg = `Schema file not found: ${schemaFile}`;
33
+ if (flags.json) {
34
+ ctx.result({ error: true, code: "SCHEMA_NOT_FOUND", message: msg });
35
+ } else {
36
+ ctx.log(`Error: ${msg}`);
37
+ }
38
+ process.exitCode = 1;
39
+ return;
40
+ }
41
+
42
+ let schema;
43
+ try {
44
+ const raw = fs.readFileSync(schemaPath, "utf8");
45
+ schema = parseSchema(raw);
46
+ } catch (err) {
47
+ const msg = `Schema parse error: ${err.message}`;
48
+ if (flags.json) {
49
+ ctx.result({
50
+ error: true,
51
+ code: "SCHEMA_VALIDATION",
52
+ message: msg,
53
+ errors: err.errors || [],
54
+ });
55
+ } else {
56
+ ctx.log(`Error: ${msg}`);
57
+ }
58
+ process.exitCode = 1;
59
+ return;
60
+ }
61
+
62
+ // --- 2. Compute diff ---
63
+ const meta = schemaToModelMeta(schema);
64
+ const relationships = schema.relationships || [];
65
+ const result = computeDiff(baseDir, meta, relationships);
66
+
67
+ // --- 3. Output results ---
68
+ if (flags.json) {
69
+ ctx.result({
70
+ added: result.added,
71
+ modified: result.modified,
72
+ deleted: result.deleted,
73
+ });
74
+ } else {
75
+ const total =
76
+ result.added.length + result.modified.length + result.deleted.length;
77
+
78
+ if (total === 0) {
79
+ ctx.log("All generated files are up to date.");
80
+ return;
81
+ }
82
+
83
+ if (result.added.length > 0) {
84
+ ctx.log("Added (new files to create):");
85
+ for (const f of result.added) {
86
+ ctx.log(` + ${f}`);
87
+ }
88
+ }
89
+
90
+ if (result.modified.length > 0) {
91
+ ctx.log("Modified (files with changes):");
92
+ for (const m of result.modified) {
93
+ ctx.log(` ~ ${m.file}`);
94
+ // Display line diffs indented
95
+ for (const line of m.diff.split("\n")) {
96
+ if (line) {
97
+ ctx.log(` ${line}`);
98
+ }
99
+ }
100
+ }
101
+ }
102
+
103
+ if (result.deleted.length > 0) {
104
+ ctx.log("Deleted (extra files to remove):");
105
+ for (const f of result.deleted) {
106
+ ctx.log(` - ${f}`);
107
+ }
108
+ }
109
+
110
+ ctx.log(`\n${total} file(s) differ.`);
111
+ }
112
+ }
113
+
114
+ module.exports = diff;