domain-driver 0.2.0 → 0.3.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 CHANGED
@@ -209,6 +209,28 @@ Server-side repositories throw a clear not-implemented error until you wire your
209
209
 
210
210
  ---
211
211
 
212
+ ## Updating
213
+
214
+ Every command checks the registry at most once a day and, when a newer release exists, prints one line after its output:
215
+
216
+ ```
217
+ ℹ️ domain-driver 0.3.0 is available (you have 0.2.0). Run: domain-driver update
218
+ ```
219
+
220
+ ```bash
221
+ domain-driver update # detects how it was installed, runs your package manager, refreshes the guidance
222
+ domain-driver update --dry-run # show the command it would run
223
+ domain-driver update --check # only report whether a newer version exists
224
+ ```
225
+
226
+ Local installs use the package manager the project uses (npm, pnpm, yarn, or bun, from the `packageManager` field or the lockfile). Global installs use `npm install -g`. Running through `npx` needs no update: `npx domain-driver@latest` always fetches the newest.
227
+
228
+ The check is skipped in CI (any `CI` value other than empty, `0`, or `false`), when output is not a terminal, when `DOMAIN_DRIVER_NO_UPDATE_CHECK` is set (same value rule), or when `NO_UPDATE_NOTIFIER` is set to anything.
229
+
230
+ The cache lives in `~/.cache/domain-driver` (or `$XDG_CACHE_HOME/domain-driver`); `DOMAIN_DRIVER_CACHE_DIR` overrides it.
231
+
232
+ ---
233
+
212
234
  ## Upgrading from 0.1.0
213
235
 
214
236
  Every layer command now takes a single `<feature>/<Name>` target instead of separate feature and name arguments, for example `make:schema users User` becomes `make:schema users/User`. `make:feature users -a` still works and names the entity `Users`; write `users/User` if you want a different entity name.
@@ -217,7 +239,7 @@ Every layer command now takes a single `<feature>/<Name>` target instead of sepa
217
239
 
218
240
  ## Requirements
219
241
 
220
- - Node.js 18+
242
+ - Node.js 20+
221
243
 
222
244
  ---
223
245
 
package/dist/cli.js ADDED
@@ -0,0 +1,208 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.defaultCliDeps = defaultCliDeps;
37
+ exports.createProgram = createProgram;
38
+ // src/cli.ts
39
+ const commander_1 = require("commander");
40
+ const os = __importStar(require("os"));
41
+ const action_1 = require("./commands/action");
42
+ const component_1 = require("./commands/component");
43
+ const container_1 = require("./commands/container");
44
+ const controller_1 = require("./commands/controller");
45
+ const feature_1 = require("./commands/feature");
46
+ const hints_1 = require("./commands/hints");
47
+ const hook_1 = require("./commands/hook");
48
+ const repository_1 = require("./commands/repository");
49
+ const schema_1 = require("./commands/schema");
50
+ const service_1 = require("./commands/service");
51
+ const sides_1 = require("./commands/sides");
52
+ const target_1 = require("./commands/target");
53
+ const types_1 = require("./commands/types");
54
+ const update_1 = require("./commands/update");
55
+ const init_1 = require("./init/init");
56
+ const detect_1 = require("./stack/detect");
57
+ const types_2 = require("./stack/types");
58
+ const actions_1 = require("./templates/actions");
59
+ const check_1 = require("./update/check");
60
+ const registry_1 = require("./update/registry");
61
+ const version_1 = require("./update/version");
62
+ const SKIP_DETECTION = new Set(['init', 'update']);
63
+ function defaultCliDeps() {
64
+ return {
65
+ env: process.env,
66
+ isTTY: process.stdout.isTTY === true,
67
+ homedir: os.homedir(),
68
+ now: Date.now,
69
+ fetchImpl: registry_1.nodeFetch,
70
+ current: (0, version_1.currentVersion)(),
71
+ updateDeps: update_1.defaultUpdateDeps,
72
+ log: (line) => console.log(line),
73
+ };
74
+ }
75
+ function createProgram(deps) {
76
+ const program = new commander_1.Command();
77
+ program
78
+ .name('domain-driver')
79
+ .description('CLI scaffolding tool for domain-driven feature folders in Next.js, React, Node, and NestJS projects')
80
+ .version(deps.current)
81
+ .option('--stack <name>', `Override stack detection (${types_2.STACK_NAMES.join(', ')})`);
82
+ let pendingNotice = Promise.resolve(null);
83
+ program.hook('preAction', (_thisCommand, actionCommand) => {
84
+ const name = actionCommand.name();
85
+ if ((0, check_1.shouldCheck)(deps.env, deps.isTTY, name)) {
86
+ pendingNotice = (0, check_1.checkForUpdate)({
87
+ env: deps.env,
88
+ homedir: deps.homedir,
89
+ now: deps.now,
90
+ fetchImpl: deps.fetchImpl,
91
+ current: deps.current,
92
+ });
93
+ }
94
+ if (SKIP_DETECTION.has(name))
95
+ return;
96
+ const { stack } = program.opts();
97
+ deps.log((0, detect_1.describeStack)((0, detect_1.detectStack)(stack)));
98
+ });
99
+ program.hook('postAction', async () => {
100
+ const notice = await pendingNotice;
101
+ if (notice !== null)
102
+ deps.log(notice);
103
+ });
104
+ program
105
+ .command('make:feature <target>')
106
+ .description('Scaffold a feature folder for the detected stack (<feature> or <feature>/<Entity>)')
107
+ .option('-a, --all', 'Scaffold all files inside each folder')
108
+ .action(async (target, options) => {
109
+ const { feature, entity } = (0, target_1.parseFeatureTarget)(target);
110
+ await (0, feature_1.makeFeature)(feature, options.all ?? false, entity ?? undefined);
111
+ });
112
+ program
113
+ .command('make:component <target>')
114
+ .description('Scaffold a component inside an existing feature (<feature>/<Name>)')
115
+ .argument('[type]', 'Component type: client or server', 'client')
116
+ .action((target, type) => {
117
+ const { feature, name } = (0, target_1.parseTarget)(target);
118
+ (0, component_1.makeComponent)(feature, name, (0, component_1.parseComponentType)(type));
119
+ });
120
+ program
121
+ .command('make:container <target>')
122
+ .description('Scaffold a smart container component inside an existing feature (<feature>/<Name>)')
123
+ .action((target) => {
124
+ const { feature, name } = (0, target_1.parseTarget)(target);
125
+ (0, container_1.makeContainer)(feature, name);
126
+ });
127
+ program
128
+ .command('make:hook <target>')
129
+ .description('Scaffold a custom hook inside an existing feature (<feature>/<useName>)')
130
+ .action((target) => {
131
+ const { feature, name } = (0, target_1.parseTarget)(target);
132
+ (0, hook_1.makeHook)(feature, name);
133
+ });
134
+ program
135
+ .command('make:service <target>')
136
+ .description('Scaffold single-responsibility service files inside an existing feature (<feature>/<Entity>)')
137
+ .option('--side <side>', 'client, server, or both', 'both')
138
+ .action((target, options) => {
139
+ const { feature, name } = (0, target_1.parseTarget)(target);
140
+ const wrote = (0, service_1.makeService)(feature, name, (0, sides_1.parseSide)(options.side));
141
+ if (wrote)
142
+ (0, hints_1.hintRegisterInModule)(feature, (0, hints_1.standardClassNames)(name, 'Service'));
143
+ });
144
+ program
145
+ .command('make:repository <target>')
146
+ .description('Scaffold single-responsibility repository files inside an existing feature (<feature>/<Entity>)')
147
+ .option('--side <side>', 'client, server, or both', 'both')
148
+ .action((target, options) => {
149
+ const { feature, name } = (0, target_1.parseTarget)(target);
150
+ const wrote = (0, repository_1.makeRepository)(feature, name, (0, sides_1.parseSide)(options.side));
151
+ if (wrote)
152
+ (0, hints_1.hintRegisterInModule)(feature, (0, hints_1.standardClassNames)(name, 'Repository'));
153
+ });
154
+ program
155
+ .command('make:controller <target>')
156
+ .description('Scaffold single-responsibility controllers or route handlers inside an existing feature (<feature>/<Entity>)')
157
+ .action((target) => {
158
+ const { feature, name } = (0, target_1.parseTarget)(target);
159
+ const wrote = (0, controller_1.makeController)(feature, name);
160
+ if (wrote)
161
+ (0, hints_1.hintRegisterInModule)(feature, (0, hints_1.standardClassNames)(name, 'Controller'));
162
+ });
163
+ program
164
+ .command('make:action <target> <action>')
165
+ .description('Scaffold a bespoke action as its own service, repository, and controller (<feature>/<Entity> <actionName>)')
166
+ .option('--with-input', 'The action takes a request body validated by a Zod schema', false)
167
+ .option('--returns <kind>', 'list, one, or void', 'list')
168
+ .action((target, action, options) => {
169
+ const { feature, name } = (0, target_1.parseTarget)(target);
170
+ const returns = (0, action_1.parseReturns)(options.returns);
171
+ const wrote = (0, action_1.makeAction)(feature, name, action, { withInput: options.withInput, returns });
172
+ if (wrote) {
173
+ const { pascal } = (0, actions_1.actionCase)(action);
174
+ (0, hints_1.hintRegisterInModule)(feature, [`${pascal}Controller`, `${pascal}Service`, `${pascal}Repository`], `list ${pascal}Controller before Show${name}Controller in controllers`);
175
+ }
176
+ });
177
+ program
178
+ .command('make:schema <target>')
179
+ .description('Scaffold Zod schemas (and Nest DTOs) for create and update operations (<feature>/<Entity>)')
180
+ .action((target) => {
181
+ const { feature, name } = (0, target_1.parseTarget)(target);
182
+ (0, schema_1.makeSchema)(feature, name);
183
+ });
184
+ program
185
+ .command('make:types <target>')
186
+ .description('Scaffold a types file inside an existing feature (<feature>/<Entity>)')
187
+ .action((target) => {
188
+ const { feature, name } = (0, target_1.parseTarget)(target);
189
+ (0, types_1.makeTypes)(feature, name);
190
+ });
191
+ program
192
+ .command('init')
193
+ .description('Write agent guidance into this project: AGENTS.md, CLAUDE.md, and .claude/skills/domain-driver/SKILL.md')
194
+ .action(() => {
195
+ for (const result of (0, init_1.runInit)(process.cwd())) {
196
+ console.log(`${init_1.INIT_ICONS[result.status]} ${result.file} ${result.status}`);
197
+ }
198
+ });
199
+ program
200
+ .command('update')
201
+ .description('Update domain-driver with your package manager, then refresh the agent guidance')
202
+ .option('--dry-run', 'Print the command without running it', false)
203
+ .option('--check', 'Only report whether a newer version exists', false)
204
+ .action(async (options) => {
205
+ await (0, update_1.runUpdate)(options, deps.updateDeps());
206
+ });
207
+ return program;
208
+ }
@@ -0,0 +1,148 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.defaultUpdateDeps = defaultUpdateDeps;
37
+ exports.runUpdate = runUpdate;
38
+ const child_process_1 = require("child_process");
39
+ const fs = __importStar(require("fs"));
40
+ const os = __importStar(require("os"));
41
+ const path = __importStar(require("path"));
42
+ const check_1 = require("../update/check");
43
+ const install_mode_1 = require("../update/install-mode");
44
+ const package_manager_1 = require("../update/package-manager");
45
+ const registry_1 = require("../update/registry");
46
+ const version_1 = require("../update/version");
47
+ const NPX_MESSAGE = 'Nothing to update: you are running domain-driver through npx, which fetches the requested version each time. Use: npx domain-driver@latest <command>';
48
+ const REFRESH_FAILED = 'ℹ️ Guidance refresh failed; run: domain-driver init';
49
+ function unknownInstallMessage(root) {
50
+ return (`Could not tell how domain-driver was installed: ${path.join(root, 'package.json')} exists but does not depend on domain-driver ` +
51
+ '(this happens in hoisted workspaces). Run the update yourself in the package that depends on it, ' +
52
+ 'for example: npm install domain-driver@latest -w <workspace>');
53
+ }
54
+ function safeRealpath(target) {
55
+ try {
56
+ return fs.realpathSync(target);
57
+ }
58
+ catch {
59
+ return target;
60
+ }
61
+ }
62
+ function defaultUpdateDeps() {
63
+ return {
64
+ env: process.env,
65
+ homedir: os.homedir(),
66
+ now: Date.now,
67
+ fetchImpl: registry_1.nodeFetch,
68
+ current: (0, version_1.currentVersion)(),
69
+ binPath: safeRealpath(process.argv[1] ?? ''),
70
+ cwd: process.cwd(),
71
+ readFile: (filePath) => fs.readFileSync(filePath, 'utf-8'),
72
+ exists: (filePath) => fs.existsSync(filePath),
73
+ spawn: (command, args, cwd) => {
74
+ const result = (0, child_process_1.spawnSync)(command, [...args], { stdio: 'inherit', cwd, shell: process.platform === 'win32' });
75
+ return { status: result.status, error: result.error };
76
+ },
77
+ execPath: process.execPath,
78
+ log: (line) => console.log(line),
79
+ };
80
+ }
81
+ async function runUpdate(options, deps) {
82
+ const install = (0, install_mode_1.detectInstallMode)(deps.binPath, deps.readFile);
83
+ if (install.mode === 'npx') {
84
+ deps.log(NPX_MESSAGE);
85
+ return;
86
+ }
87
+ if (install.mode === 'unknown') {
88
+ const message = unknownInstallMessage(install.root ?? deps.cwd);
89
+ deps.log(message);
90
+ throw new Error(message);
91
+ }
92
+ const command = commandFor(install, deps);
93
+ if (options.dryRun) {
94
+ deps.log(install.root === null ? `Would run: ${command.display}` : `Would run: ${command.display} in ${install.root}`);
95
+ return;
96
+ }
97
+ const latest = await (0, check_1.latestVersion)({
98
+ env: deps.env,
99
+ homedir: deps.homedir,
100
+ now: deps.now,
101
+ fetchImpl: deps.fetchImpl,
102
+ current: deps.current,
103
+ force: true,
104
+ });
105
+ if (latest !== null && !(0, version_1.isNewer)(latest, deps.current)) {
106
+ deps.log(`domain-driver ${deps.current} is already the latest version.`);
107
+ return;
108
+ }
109
+ if (options.check) {
110
+ deps.log(latest === null ? 'Could not reach the registry to check for updates.' : (0, check_1.formatNotice)(latest, deps.current));
111
+ return;
112
+ }
113
+ if (latest === null)
114
+ deps.log('Could not reach the registry; updating to @latest anyway.');
115
+ runInstall(command, install, deps);
116
+ deps.log(`✅ domain-driver updated to ${latest ?? '@latest'}`);
117
+ refreshGuidance(install, deps);
118
+ }
119
+ function commandFor(install, deps) {
120
+ if (install.mode === 'local' && install.root !== null) {
121
+ return (0, package_manager_1.updateCommand)('local', (0, package_manager_1.detectPackageManager)(install.root, { readFile: deps.readFile, exists: deps.exists }));
122
+ }
123
+ return (0, package_manager_1.updateCommand)('global', 'npm');
124
+ }
125
+ function runInstall(command, install, deps) {
126
+ const cwd = install.root ?? deps.cwd;
127
+ const result = deps.spawn(command.command, command.args, cwd);
128
+ if (result.error === undefined && result.status === 0)
129
+ return;
130
+ const code = result.status ?? 'error';
131
+ const sudo = install.mode === 'global' ? ' (you may need sudo)' : '';
132
+ throw new Error(`Update failed (exit ${code}). Run it yourself: ${command.display}${sudo}`);
133
+ }
134
+ // The freshly installed package is run in a new process: this one already has the old modules loaded.
135
+ function refreshGuidance(install, deps) {
136
+ const root = install.mode === 'local' ? install.root : deps.exists(path.join(deps.cwd, 'package.json')) ? deps.cwd : null;
137
+ if (root === null)
138
+ return;
139
+ const result = deps.spawn(deps.execPath, [installedEntry(install, root, deps), 'init'], root);
140
+ if (result.error !== undefined || result.status !== 0)
141
+ deps.log(REFRESH_FAILED);
142
+ }
143
+ function installedEntry(install, root, deps) {
144
+ if (install.mode !== 'local')
145
+ return deps.binPath;
146
+ const entry = path.join(root, 'node_modules', 'domain-driver', 'dist', 'index.js');
147
+ return deps.exists(entry) ? entry : deps.binPath;
148
+ }
package/dist/index.js CHANGED
@@ -2,139 +2,10 @@
2
2
  "use strict";
3
3
  Object.defineProperty(exports, "__esModule", { value: true });
4
4
  // src/index.ts
5
- const commander_1 = require("commander");
6
- const action_1 = require("./commands/action");
7
- const component_1 = require("./commands/component");
8
- const container_1 = require("./commands/container");
9
- const controller_1 = require("./commands/controller");
10
- const feature_1 = require("./commands/feature");
11
- const hints_1 = require("./commands/hints");
12
- const hook_1 = require("./commands/hook");
13
- const repository_1 = require("./commands/repository");
14
- const schema_1 = require("./commands/schema");
15
- const service_1 = require("./commands/service");
16
- const sides_1 = require("./commands/sides");
17
- const target_1 = require("./commands/target");
18
- const types_1 = require("./commands/types");
19
- const init_1 = require("./init/init");
20
- const detect_1 = require("./stack/detect");
21
- const types_2 = require("./stack/types");
22
- const actions_1 = require("./templates/actions");
23
- const program = new commander_1.Command();
24
- program
25
- .name('domain-driver')
26
- .description('CLI scaffolding tool for domain-driven feature folders in Next.js, React, Node, and NestJS projects')
27
- .version('0.2.0')
28
- .option('--stack <name>', `Override stack detection (${types_2.STACK_NAMES.join(', ')})`);
29
- program.hook('preAction', (_thisCommand, actionCommand) => {
30
- if (actionCommand.name() === 'init')
31
- return;
32
- const { stack } = program.opts();
33
- console.log((0, detect_1.describeStack)((0, detect_1.detectStack)(stack)));
34
- });
35
- program
36
- .command('make:feature <target>')
37
- .description('Scaffold a feature folder for the detected stack (<feature> or <feature>/<Entity>)')
38
- .option('-a, --all', 'Scaffold all files inside each folder')
39
- .action(async (target, options) => {
40
- const { feature, entity } = (0, target_1.parseFeatureTarget)(target);
41
- await (0, feature_1.makeFeature)(feature, options.all ?? false, entity ?? undefined);
42
- });
43
- program
44
- .command('make:component <target>')
45
- .description('Scaffold a component inside an existing feature (<feature>/<Name>)')
46
- .argument('[type]', 'Component type: client or server', 'client')
47
- .action((target, type) => {
48
- const { feature, name } = (0, target_1.parseTarget)(target);
49
- (0, component_1.makeComponent)(feature, name, (0, component_1.parseComponentType)(type));
50
- });
51
- program
52
- .command('make:container <target>')
53
- .description('Scaffold a smart container component inside an existing feature (<feature>/<Name>)')
54
- .action((target) => {
55
- const { feature, name } = (0, target_1.parseTarget)(target);
56
- (0, container_1.makeContainer)(feature, name);
57
- });
58
- program
59
- .command('make:hook <target>')
60
- .description('Scaffold a custom hook inside an existing feature (<feature>/<useName>)')
61
- .action((target) => {
62
- const { feature, name } = (0, target_1.parseTarget)(target);
63
- (0, hook_1.makeHook)(feature, name);
64
- });
65
- program
66
- .command('make:service <target>')
67
- .description('Scaffold single-responsibility service files inside an existing feature (<feature>/<Entity>)')
68
- .option('--side <side>', 'client, server, or both', 'both')
69
- .action((target, options) => {
70
- const { feature, name } = (0, target_1.parseTarget)(target);
71
- const wrote = (0, service_1.makeService)(feature, name, (0, sides_1.parseSide)(options.side));
72
- if (wrote)
73
- (0, hints_1.hintRegisterInModule)(feature, (0, hints_1.standardClassNames)(name, 'Service'));
74
- });
75
- program
76
- .command('make:repository <target>')
77
- .description('Scaffold single-responsibility repository files inside an existing feature (<feature>/<Entity>)')
78
- .option('--side <side>', 'client, server, or both', 'both')
79
- .action((target, options) => {
80
- const { feature, name } = (0, target_1.parseTarget)(target);
81
- const wrote = (0, repository_1.makeRepository)(feature, name, (0, sides_1.parseSide)(options.side));
82
- if (wrote)
83
- (0, hints_1.hintRegisterInModule)(feature, (0, hints_1.standardClassNames)(name, 'Repository'));
84
- });
85
- program
86
- .command('make:controller <target>')
87
- .description('Scaffold single-responsibility controllers or route handlers inside an existing feature (<feature>/<Entity>)')
88
- .action((target) => {
89
- const { feature, name } = (0, target_1.parseTarget)(target);
90
- const wrote = (0, controller_1.makeController)(feature, name);
91
- if (wrote)
92
- (0, hints_1.hintRegisterInModule)(feature, (0, hints_1.standardClassNames)(name, 'Controller'));
93
- });
94
- program
95
- .command('make:action <target> <action>')
96
- .description('Scaffold a bespoke action as its own service, repository, and controller (<feature>/<Entity> <actionName>)')
97
- .option('--with-input', 'The action takes a request body validated by a Zod schema', false)
98
- .option('--returns <kind>', 'list, one, or void', 'list')
99
- .action((target, action, options) => {
100
- const { feature, name } = (0, target_1.parseTarget)(target);
101
- const returns = (0, action_1.parseReturns)(options.returns);
102
- const wrote = (0, action_1.makeAction)(feature, name, action, { withInput: options.withInput, returns });
103
- if (wrote) {
104
- const { pascal } = (0, actions_1.actionCase)(action);
105
- (0, hints_1.hintRegisterInModule)(feature, [`${pascal}Controller`, `${pascal}Service`, `${pascal}Repository`], `list ${pascal}Controller before Show${name}Controller in controllers`);
106
- }
107
- });
108
- program
109
- .command('make:schema <target>')
110
- .description('Scaffold Zod schemas (and Nest DTOs) for create and update operations (<feature>/<Entity>)')
111
- .action((target) => {
112
- const { feature, name } = (0, target_1.parseTarget)(target);
113
- (0, schema_1.makeSchema)(feature, name);
114
- });
115
- program
116
- .command('make:types <target>')
117
- .description('Scaffold a types file inside an existing feature (<feature>/<Entity>)')
118
- .action((target) => {
119
- const { feature, name } = (0, target_1.parseTarget)(target);
120
- (0, types_1.makeTypes)(feature, name);
121
- });
122
- const INIT_ICONS = Object.freeze({
123
- created: '✅',
124
- updated: '✅',
125
- unchanged: 'ℹ️ ',
126
- });
127
- program
128
- .command('init')
129
- .description('Write agent guidance into this project: AGENTS.md, CLAUDE.md, and .claude/skills/domain-driver/SKILL.md')
130
- .action(() => {
131
- for (const result of (0, init_1.runInit)(process.cwd())) {
132
- console.log(`${INIT_ICONS[result.status]} ${result.file} ${result.status}`);
133
- }
134
- });
5
+ const cli_1 = require("./cli");
135
6
  function fail(error) {
136
7
  const message = error instanceof Error ? error.message : 'Unknown error';
137
8
  console.error(`❌ ${message}`);
138
9
  process.exit(1);
139
10
  }
140
- program.parseAsync().catch(fail);
11
+ (0, cli_1.createProgram)((0, cli_1.defaultCliDeps)()).parseAsync().catch(fail);
package/dist/init/init.js CHANGED
@@ -33,6 +33,7 @@ var __importStar = (this && this.__importStar) || (function () {
33
33
  };
34
34
  })();
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.INIT_ICONS = void 0;
36
37
  exports.runInit = runInit;
37
38
  const path = __importStar(require("path"));
38
39
  const fs_1 = require("../utils/fs");
@@ -58,3 +59,8 @@ function writeSkill(root) {
58
59
  (0, fs_1.writeFileSafe)(filePath, content_1.SKILL_CONTENT);
59
60
  return Object.freeze({ file: SKILL_FILE, status: existing === null ? 'created' : 'updated' });
60
61
  }
62
+ exports.INIT_ICONS = Object.freeze({
63
+ created: '✅',
64
+ updated: '✅',
65
+ unchanged: 'ℹ️ ',
66
+ });
@@ -0,0 +1,80 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.CACHE_TTL_MS = exports.CACHE_FILE = void 0;
37
+ exports.cacheDir = cacheDir;
38
+ exports.readCache = readCache;
39
+ exports.writeCache = writeCache;
40
+ exports.isFresh = isFresh;
41
+ const fs = __importStar(require("fs"));
42
+ const path = __importStar(require("path"));
43
+ exports.CACHE_FILE = 'update-check.json';
44
+ exports.CACHE_TTL_MS = 24 * 60 * 60 * 1000;
45
+ function cacheDir(env, homedir) {
46
+ if (env.DOMAIN_DRIVER_CACHE_DIR)
47
+ return env.DOMAIN_DRIVER_CACHE_DIR;
48
+ if (env.XDG_CACHE_HOME)
49
+ return path.join(env.XDG_CACHE_HOME, 'domain-driver');
50
+ return path.join(homedir, '.cache', 'domain-driver');
51
+ }
52
+ function isCache(value) {
53
+ if (typeof value !== 'object' || value === null)
54
+ return false;
55
+ const { latest, checkedAt } = value;
56
+ const latestOk = latest === null || typeof latest === 'string';
57
+ const checkedOk = typeof checkedAt === 'string' && !Number.isNaN(Date.parse(checkedAt));
58
+ return latestOk && checkedOk;
59
+ }
60
+ function readCache(dir) {
61
+ try {
62
+ const parsed = JSON.parse(fs.readFileSync(path.join(dir, exports.CACHE_FILE), 'utf-8'));
63
+ return isCache(parsed) ? Object.freeze({ latest: parsed.latest, checkedAt: parsed.checkedAt }) : null;
64
+ }
65
+ catch {
66
+ return null;
67
+ }
68
+ }
69
+ function writeCache(dir, cache) {
70
+ try {
71
+ fs.mkdirSync(dir, { recursive: true });
72
+ fs.writeFileSync(path.join(dir, exports.CACHE_FILE), JSON.stringify(cache));
73
+ }
74
+ catch {
75
+ // The cache is advisory; never fail a command over it.
76
+ }
77
+ }
78
+ function isFresh(cache, now, ttlMs = exports.CACHE_TTL_MS) {
79
+ return now - Date.parse(cache.checkedAt) < ttlMs;
80
+ }
@@ -0,0 +1,47 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.shouldCheck = shouldCheck;
4
+ exports.formatNotice = formatNotice;
5
+ exports.latestVersion = latestVersion;
6
+ exports.checkForUpdate = checkForUpdate;
7
+ const cache_1 = require("./cache");
8
+ const registry_1 = require("./registry");
9
+ const version_1 = require("./version");
10
+ const UNSET_VALUES = new Set(['', '0', 'false']);
11
+ function isSet(value) {
12
+ return value !== undefined && !UNSET_VALUES.has(value);
13
+ }
14
+ function shouldCheck(env, isTTY, commandName) {
15
+ if (isSet(env.CI))
16
+ return false;
17
+ if (isSet(env.DOMAIN_DRIVER_NO_UPDATE_CHECK))
18
+ return false;
19
+ if (env.NO_UPDATE_NOTIFIER !== undefined)
20
+ return false;
21
+ if (!isTTY)
22
+ return false;
23
+ return commandName !== 'update';
24
+ }
25
+ function formatNotice(latest, current) {
26
+ return `ℹ️ domain-driver ${latest} is available (you have ${current}). Run: domain-driver update`;
27
+ }
28
+ async function latestVersion(deps) {
29
+ const dir = (0, cache_1.cacheDir)(deps.env, deps.homedir);
30
+ const cached = (0, cache_1.readCache)(dir);
31
+ if (!deps.force && cached !== null && (0, cache_1.isFresh)(cached, deps.now()))
32
+ return cached.latest;
33
+ const latest = await (0, registry_1.fetchLatestVersion)(deps.fetchImpl);
34
+ (0, cache_1.writeCache)(dir, { latest, checkedAt: new Date(deps.now()).toISOString() });
35
+ return latest;
36
+ }
37
+ async function checkForUpdate(deps) {
38
+ try {
39
+ const latest = await latestVersion(deps);
40
+ if (latest !== null && (0, version_1.isNewer)(latest, deps.current))
41
+ return formatNotice(latest, deps.current);
42
+ return null;
43
+ }
44
+ catch {
45
+ return null;
46
+ }
47
+ }
@@ -0,0 +1,96 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.detectInstallMode = detectInstallMode;
37
+ const path = __importStar(require("path"));
38
+ const MAX_ANCESTORS = 6;
39
+ function detectInstallMode(binPath, readFile) {
40
+ const segments = binPath.split(path.sep);
41
+ const index = findInstallIndex(segments);
42
+ if (index === null)
43
+ return Object.freeze({ mode: 'global', root: null });
44
+ // npx unpacks into <cache>/_npx/<hash>/node_modules/domain-driver, so _npx sits three segments before the package.
45
+ if (segments[index - 3] === '_npx')
46
+ return Object.freeze({ mode: 'npx', root: null });
47
+ const installRoot = rootFrom(segments, index);
48
+ const projectRoot = findProjectRoot(installRoot, readFile);
49
+ if (projectRoot !== null)
50
+ return Object.freeze({ mode: 'local', root: projectRoot });
51
+ return hasPackageJson(installRoot, readFile)
52
+ ? Object.freeze({ mode: 'unknown', root: installRoot })
53
+ : Object.freeze({ mode: 'global', root: null });
54
+ }
55
+ function findInstallIndex(segments) {
56
+ for (let index = segments.length - 1; index > 0; index -= 1) {
57
+ if (segments[index - 1] === 'node_modules' && segments[index] === 'domain-driver')
58
+ return index;
59
+ }
60
+ return null;
61
+ }
62
+ function rootFrom(segments, index) {
63
+ const root = segments.slice(0, index - 1).join(path.sep);
64
+ return root === '' ? path.sep : root;
65
+ }
66
+ function findProjectRoot(start, readFile) {
67
+ let current = start;
68
+ for (let level = 0; level <= MAX_ANCESTORS; level += 1) {
69
+ if (dependsOnDomainDriver(current, readFile))
70
+ return current;
71
+ const parent = path.dirname(current);
72
+ if (parent === current)
73
+ return null;
74
+ current = parent;
75
+ }
76
+ return null;
77
+ }
78
+ function dependsOnDomainDriver(root, readFile) {
79
+ try {
80
+ const parsed = JSON.parse(readFile(path.join(root, 'package.json')));
81
+ const pkg = (parsed ?? {});
82
+ return 'domain-driver' in (pkg.dependencies ?? {}) || 'domain-driver' in (pkg.devDependencies ?? {});
83
+ }
84
+ catch {
85
+ return false;
86
+ }
87
+ }
88
+ function hasPackageJson(root, readFile) {
89
+ try {
90
+ readFile(path.join(root, 'package.json'));
91
+ return true;
92
+ }
93
+ catch {
94
+ return false;
95
+ }
96
+ }
@@ -0,0 +1,99 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.detectPackageManager = detectPackageManager;
37
+ exports.updateCommand = updateCommand;
38
+ const path = __importStar(require("path"));
39
+ const PACKAGE = 'domain-driver@latest';
40
+ function detectPackageManager(root, fsLike) {
41
+ const declared = declaredManager(root, fsLike);
42
+ if (declared !== null)
43
+ return declared;
44
+ const has = (name) => fsLike.exists(path.join(root, name));
45
+ if (has('bun.lock') || has('bun.lockb'))
46
+ return 'bun';
47
+ if (has('pnpm-lock.yaml'))
48
+ return 'pnpm';
49
+ if (has('yarn.lock'))
50
+ return yarnFlavourFromLock(root, fsLike);
51
+ return 'npm';
52
+ }
53
+ function declaredManager(root, fsLike) {
54
+ try {
55
+ const parsed = JSON.parse(fsLike.readFile(path.join(root, 'package.json')));
56
+ const declared = parsed?.packageManager;
57
+ if (typeof declared !== 'string')
58
+ return null;
59
+ const [name, version = ''] = declared.split('@');
60
+ if (name === 'npm' || name === 'pnpm' || name === 'bun')
61
+ return name;
62
+ if (name !== 'yarn')
63
+ return null;
64
+ if (version === '')
65
+ return yarnFlavourFromLock(root, fsLike);
66
+ return version.startsWith('1.') || version === '1' ? 'yarn-classic' : 'yarn-berry';
67
+ }
68
+ catch {
69
+ return null;
70
+ }
71
+ }
72
+ function yarnFlavourFromLock(root, fsLike) {
73
+ try {
74
+ const header = fsLike.readFile(path.join(root, 'yarn.lock')).split('\n').slice(0, 3).join('\n');
75
+ return header.includes('yarn lockfile v1') ? 'yarn-classic' : 'yarn-berry';
76
+ }
77
+ catch {
78
+ return 'yarn-berry';
79
+ }
80
+ }
81
+ function build(command, ...args) {
82
+ return Object.freeze({ command, args: Object.freeze(args), display: [command, ...args].join(' ') });
83
+ }
84
+ function updateCommand(mode, manager) {
85
+ if (mode === 'global')
86
+ return build('npm', 'install', '-g', PACKAGE);
87
+ switch (manager) {
88
+ case 'npm':
89
+ return build('npm', 'install', PACKAGE);
90
+ case 'pnpm':
91
+ return build('pnpm', 'update', PACKAGE);
92
+ case 'yarn-classic':
93
+ return build('yarn', 'upgrade', PACKAGE);
94
+ case 'yarn-berry':
95
+ return build('yarn', 'up', PACKAGE);
96
+ case 'bun':
97
+ return build('bun', 'update', PACKAGE);
98
+ }
99
+ }
@@ -0,0 +1,77 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.nodeFetch = exports.FETCH_TIMEOUT_MS = exports.DIST_TAGS_URL = void 0;
37
+ exports.fetchLatestVersion = fetchLatestVersion;
38
+ const http = __importStar(require("http"));
39
+ const https = __importStar(require("https"));
40
+ exports.DIST_TAGS_URL = 'https://registry.npmjs.org/-/package/domain-driver/dist-tags';
41
+ exports.FETCH_TIMEOUT_MS = 1500;
42
+ const nodeFetch = (url, init) => new Promise((resolve, reject) => {
43
+ const client = url.startsWith('https:') ? https : http;
44
+ const request = client.get(url, { signal: init?.signal, headers: { accept: 'application/json' } }, (response) => {
45
+ let body = '';
46
+ response.setEncoding('utf-8');
47
+ response.on('data', (chunk) => {
48
+ body += chunk;
49
+ });
50
+ response.on('end', () => {
51
+ const status = response.statusCode ?? 0;
52
+ resolve({ ok: status >= 200 && status < 300, status, json: async () => JSON.parse(body) });
53
+ });
54
+ response.on('error', reject);
55
+ });
56
+ request.on('socket', (socket) => socket.unref());
57
+ request.on('error', reject);
58
+ });
59
+ exports.nodeFetch = nodeFetch;
60
+ async function fetchLatestVersion(fetchImpl = exports.nodeFetch, timeoutMs = exports.FETCH_TIMEOUT_MS) {
61
+ const controller = new AbortController();
62
+ const timer = setTimeout(() => controller.abort(), timeoutMs);
63
+ try {
64
+ const response = await fetchImpl(exports.DIST_TAGS_URL, { signal: controller.signal });
65
+ if (!response.ok)
66
+ return null;
67
+ const body = await response.json();
68
+ const latest = body?.latest;
69
+ return typeof latest === 'string' ? latest : null;
70
+ }
71
+ catch {
72
+ return null;
73
+ }
74
+ finally {
75
+ clearTimeout(timer);
76
+ }
77
+ }
@@ -0,0 +1,70 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.currentVersion = currentVersion;
37
+ exports.parseVersion = parseVersion;
38
+ exports.isNewer = isNewer;
39
+ const fs = __importStar(require("fs"));
40
+ const path = __importStar(require("path"));
41
+ const FALLBACK_VERSION = '0.0.0';
42
+ const VERSION_PATTERN = /^(\d+)\.(\d+)\.(\d+)$/;
43
+ const readUtf8 = (filePath) => fs.readFileSync(filePath, 'utf-8');
44
+ function currentVersion(readFile = readUtf8) {
45
+ try {
46
+ const parsed = JSON.parse(readFile(path.join(__dirname, '..', '..', 'package.json')));
47
+ const version = parsed.version;
48
+ return typeof version === 'string' ? version : FALLBACK_VERSION;
49
+ }
50
+ catch {
51
+ return FALLBACK_VERSION;
52
+ }
53
+ }
54
+ function parseVersion(value) {
55
+ const match = VERSION_PATTERN.exec(value.trim());
56
+ if (!match)
57
+ return null;
58
+ return [Number(match[1]), Number(match[2]), Number(match[3])];
59
+ }
60
+ function isNewer(latest, current) {
61
+ const next = parseVersion(latest);
62
+ const now = parseVersion(current);
63
+ if (next === null || now === null)
64
+ return false;
65
+ for (let index = 0; index < 3; index += 1) {
66
+ if (next[index] !== now[index])
67
+ return next[index] > now[index];
68
+ }
69
+ return false;
70
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "domain-driver",
3
- "version": "0.2.0",
3
+ "version": "0.3.1",
4
4
  "description": "CLI scaffolding tool for domain-driven feature folders in Next.js, React, Node, and NestJS projects, with per-action files, bespoke actions, and agent guidance",
5
5
  "main": "dist/index.js",
6
6
  "bin": {
@@ -35,7 +35,7 @@
35
35
  "author": "Isaac Hatilima",
36
36
  "license": "MIT",
37
37
  "engines": {
38
- "node": ">=18"
38
+ "node": ">=20"
39
39
  },
40
40
  "devDependencies": {
41
41
  "@types/node": "^25.4.0",