forgestack-os-cli 0.3.3 → 0.3.5

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
@@ -4,69 +4,476 @@ Generate production-ready full-stack SaaS projects with one command.
4
4
 
5
5
  ## Installation
6
6
 
7
+ ### Option 1: Using npx (Recommended - No Installation Needed)
8
+
7
9
  ```bash
8
- # Recommended: npx (no install)
9
10
  npx forgestack-os-cli create my-app
11
+ ```
12
+
13
+ No setup required! The fastest way to get started. The CLI is downloaded and run on the fly.
10
14
 
11
- # Or install globally
15
+ ### Option 2: Install Globally
16
+
17
+ ```bash
12
18
  npm install -g forgestack-os-cli
13
- # then
14
- forgestack create my-app
19
+ ```
20
+
21
+ Then create projects using:
22
+
23
+ ```bash
24
+ forgestack-os-cli create my-app
25
+ ```
26
+
27
+ **Note on Global Install:** After installing globally, the command `forgestack-os-cli` should be in your PATH. If you get `command not found` errors:
28
+
29
+ 1. Verify your npm bin directory is in PATH:
30
+ ```bash
31
+ npm config get prefix
32
+ ```
33
+ 2. Add it to PATH if needed:
34
+ - **Windows (PowerShell):** `[System.Environment]::SetEnvironmentVariable("Path", "$env:Path;C:\Users\YourUsername\AppData\Roaming\npm", [System.EnvironmentVariableTarget]::User)`
35
+ - **macOS/Linux:** Add `export PATH="~/.npm-global/bin:$PATH"` to `~/.bash_profile` or `~/.zshrc`
36
+
37
+ 3. Verify installation:
38
+ ```bash
39
+ forgestack-os-cli --version
40
+ ```
41
+
42
+ ## Quick Start
43
+
44
+ **Fastest way to create a project:**
45
+
46
+ ```bash
47
+ # Interactive mode - we'll ask you questions
48
+ npx forgestack-os-cli create my-app
49
+
50
+ # With a preset - production-ready stack
51
+ npx forgestack-os-cli create my-enterprise --preset next-nest-clerk-pg
52
+
53
+ # With specific flags
54
+ npx forgestack-os-cli create my-api --frontend nextjs --backend nestjs --auth supabase --database postgresql --docker
15
55
  ```
16
56
 
17
57
  ## Usage
18
58
 
19
59
  ```bash
20
- forgestack create <project-name> [options]
60
+ forgestack-os-cli create <project-name> [options]
61
+ ```
62
+
63
+ Or with npx:
64
+
65
+ ```bash
66
+ npx forgestack-os-cli create <project-name> [options]
67
+ ```
68
+
69
+ ### Available Options
70
+
71
+ | Option | Values | Description |
72
+ | ---------------- | --------------------------------------------------------------------------------- | --------------------------------------- |
73
+ | `--frontend` | `react-vite` \| `nextjs` \| `vue-vite` \| `sveltekit` | Frontend framework |
74
+ | `--backend` | `express` \| `fastify` \| `nestjs` \| `bun-elysia` \| `go-fiber` | Backend framework |
75
+ | `--auth` | `jwt` \| `clerk` \| `supabase` \| `authjs` \| `firebase` | Authentication provider |
76
+ | `--database` | `postgresql` \| `mongodb` \| `mysql` \| `sqlite` \| `supabase-db` | Database |
77
+ | `--api` | `rest` \| `graphql` \| `trpc` | API style |
78
+ | `--preset` | `next-nest-clerk-pg` \| `react-express-jwt-mongo` \| `next-fastify-supabase-trpc` | Predefined stack |
79
+ | `--stack` | JSON string | Full stack config as JSON |
80
+ | `--docker` | - | Include Docker configuration |
81
+ | `--no-docker` | - | Skip Docker configuration (default) |
82
+ | `--multi-tenant` | - | Enable multi-tenancy scaffolding |
83
+ | `--skip-install` | - | Skip npm install after project creation |
84
+ | `--skip-git` | - | Skip git initialization |
85
+
86
+ ## Examples
87
+
88
+ ### Interactive Mode (Recommended)
89
+
90
+ ```bash
91
+ npx forgestack-os-cli create my-saas
92
+ ```
93
+
94
+ You'll be guided through selecting your stack options with interactive prompts.
95
+
96
+ ### Using Presets
97
+
98
+ **Next.js + NestJS + Clerk + PostgreSQL (Full-featured):**
99
+
100
+ ```bash
101
+ npx forgestack-os-cli create my-enterprise --preset next-nest-clerk-pg
102
+ ```
103
+
104
+ **React + Express + JWT + MongoDB (Simple SPA):**
105
+
106
+ ```bash
107
+ npx forgestack-os-cli create my-app --preset react-express-jwt-mongo
108
+ ```
109
+
110
+ **Next.js + Fastify + Supabase + tRPC (Modern fullstack):**
111
+
112
+ ```bash
113
+ npx forgestack-os-cli create my-trpc-app --preset next-fastify-supabase-trpc
114
+ ```
115
+
116
+ ### Using Flags
117
+
118
+ **RESTful API with React + Express:**
119
+
120
+ ```bash
121
+ npx forgestack-os-cli create my-rest-api \
122
+ --frontend react-vite \
123
+ --backend express \
124
+ --auth jwt \
125
+ --database postgresql \
126
+ --api rest
127
+ ```
128
+
129
+ **GraphQL Backend with Vue:**
130
+
131
+ ```bash
132
+ npx forgestack-os-cli create my-graphql-app \
133
+ --frontend vue-vite \
134
+ --backend nestjs \
135
+ --auth firebase \
136
+ --database mongodb \
137
+ --api graphql \
138
+ --docker
139
+ ```
140
+
141
+ **Minimal Setup (SQLite + No Docker):**
142
+
143
+ ```bash
144
+ npx forgestack-os-cli create my-minimal-app \
145
+ --frontend react-vite \
146
+ --backend express \
147
+ --auth jwt \
148
+ --database sqlite \
149
+ --no-docker
150
+ ```
151
+
152
+ ### Using JSON Stack Config
153
+
154
+ Provide complete configuration as a JSON string:
155
+
156
+ ```bash
157
+ npx forgestack-os-cli create my-custom-stack --stack '{
158
+ "frontend": "nextjs",
159
+ "backend": "fastify",
160
+ "auth": "supabase",
161
+ "database": "supabase-db",
162
+ "apiStyle": "trpc",
163
+ "docker": true,
164
+ "multiTenant": true
165
+ }'
166
+ ```
167
+
168
+ **Multi-tenancy with Docker:**
169
+
170
+ ```bash
171
+ npx forgestack-os-cli create my-multitenant-app \
172
+ --preset next-nest-clerk-pg \
173
+ --multi-tenant \
174
+ --docker
175
+ ```
176
+
177
+ **Skip Dependency Installation:**
178
+
179
+ ```bash
180
+ npx forgestack-os-cli create my-app --preset next-nest-clerk-pg --skip-install
181
+ cd my-app
182
+ npm install # Install later when you're ready
183
+ ```
184
+
185
+ ## Additional Commands
186
+
187
+ ### `organize` - File Organization Utility
188
+
189
+ Organize files in a folder by type or date, with optional duplicate detection. Supports MD5-based duplicate identification and automatic system folder exclusion for performance.
190
+
191
+ ```bash
192
+ npx forgestack-os-cli organize <folder-path> [options]
193
+ ```
194
+
195
+ **Options:**
196
+ - `--strategy <type>` - Organization strategy: `type` (default) or `date`
197
+ - `--duplicates` - Move duplicate files to a `Duplicates` folder
198
+
199
+ **File Categories (when using `--strategy type`):**
200
+ - **Images**: jpg, png, gif, svg, webp, bmp, ico
201
+ - **Documents**: pdf, doc, docx, txt, xlsx, csv, md
202
+ - **Videos**: mp4, mkv, avi, mov, wmv, flv
203
+ - **Audio**: mp3, wav, flac, aac, m4a, ogg
204
+ - **Code**: js, ts, py, java, cpp, go, rs, rb
205
+ - **Archives**: zip, rar, 7z, tar, gz, bz2
206
+ - **Data**: json, xml, yaml, sql, db, sqlite
207
+ - **Executables**: exe, msi, app, deb, rpm
208
+ - **Others**: All other file types
209
+
210
+ **Date Format (when using `--strategy date`):**
211
+ Files organized into folders using format: `YYYY-MM` (e.g., `2026-01`, `2025-12`)
212
+
213
+ **Features:**
214
+ - ✅ Automatically skips system folders (node_modules, .git, dist, build, .next, .env, .DS_Store, .vscode)
215
+ - ✅ MD5-based duplicate detection (prevents moving the same file twice)
216
+ - ✅ Graceful error handling for permission denied or inaccessible files
217
+ - ✅ Summary report showing total files organized and categories
218
+ - ✅ Interactive prompts if options not provided
219
+
220
+ **Examples:**
221
+
222
+ ```bash
223
+ # Organize by file type with duplicate detection
224
+ npx forgestack-os-cli organize ~/Downloads --strategy type --duplicates
225
+
226
+ # Organize photos by month
227
+ npx forgestack-os-cli organize ~/Pictures --strategy date
228
+
229
+ # Interactive mode (prompts for folder and options)
230
+ npx forgestack-os-cli organize
231
+
232
+ # Organize current directory
233
+ npx forgestack-os-cli organize .
234
+ ```
235
+
236
+ **Output Example:**
237
+ ```
238
+ ✓ Found and organized 1,250 files
239
+ ✓ Categories:
240
+ - Images: 450 files
241
+ - Documents: 280 files
242
+ - Videos: 320 files
243
+ - Code: 145 files
244
+ - Others: 55 files
245
+ ✓ Found 12 set(s) of duplicate files moved to Duplicates folder
246
+ ```
247
+
248
+ **Edge Cases Handled:**
249
+ - Empty folders: Displays "No files to organize" message
250
+ - Invalid paths: Shows clear error message with validation
251
+ - Permission errors: Skips files with access denied, continues processing
252
+ - Unreadable files: Skips during hash calculation, no crash
253
+ - Large monorepos: Excludes node_modules, .git, and build folders automatically
254
+
255
+ ---
256
+
257
+ ### `run-tasks` - Batch Task Runner
258
+
259
+ Execute shell commands from a JSON configuration file, sequentially or in parallel. Supports task-specific working directories and comprehensive error handling.
260
+
261
+ ```bash
262
+ npx forgestack-os-cli run-tasks <config-path> [options]
21
263
  ```
22
264
 
23
- **Common flags**
265
+ **Options:**
266
+ - `--parallel` - Run tasks concurrently instead of sequentially (default: false)
267
+ - `--stop-on-error` - Stop execution on first task failure (default: true)
268
+
269
+ **Config File Format (tasks.json):**
24
270
 
25
- - `--frontend <framework>`: react-vite | nextjs
26
- - `--backend <framework>`: express | fastify | nestjs | bun-elysia | go-fiber
27
- - `--auth <provider>`: jwt | clerk | supabase | authjs | firebase
28
- - `--database <db>`: postgresql | mongodb | mysql | sqlite | supabase-db
29
- - `--api <style>`: rest | graphql | trpc
30
- - `--preset <name>`: use a predefined stack (e.g., next-nest-clerk-pg)
31
- - `--stack <json>`: provide full stack config as JSON
32
- - `--docker` / `--no-docker`: include or skip Docker config
33
- - `--multi-tenant`: enable multi-tenancy scaffolding
34
- - `--skip-install`: skip dependency installation
35
- - `--skip-git`: skip git init
271
+ ```json
272
+ {
273
+ "tasks": [
274
+ {
275
+ "name": "Build",
276
+ "command": "npm run build",
277
+ "cwd": "./"
278
+ },
279
+ {
280
+ "name": "Test",
281
+ "command": "npm test",
282
+ "cwd": "./"
283
+ }
284
+ ],
285
+ "parallel": false,
286
+ "stopOnError": true
287
+ }
288
+ ```
289
+
290
+ **Features:**
291
+ - ✅ Cross-platform shell execution (Windows CMD, Unix bash)
292
+ - ✅ Task-specific working directory support
293
+ - ✅ Sequential or parallel execution modes
294
+ - ✅ Configurable failure handling
295
+ - ✅ Comprehensive error reporting
296
+ - ✅ Interactive mode with default config file detection
297
+ - ✅ Proper exit codes for CI/CD integration
36
298
 
37
- ### Examples
299
+ **Examples:**
38
300
 
39
301
  ```bash
40
- # Interactive
41
- forgestack create my-saas
302
+ # Run tasks sequentially (stops on first error)
303
+ npx forgestack-os-cli run-tasks ./tasks.json
304
+
305
+ # Run tasks in parallel
306
+ npx forgestack-os-cli run-tasks ./tasks.json --parallel
42
307
 
43
- # Preset
44
- forgestack create my-enterprise --preset next-nest-clerk-pg
308
+ # Continue on errors
309
+ npx forgestack-os-cli run-tasks ./tasks.json --stop-on-error false
310
+
311
+ # Interactive mode (looks for ./tasks.json automatically)
312
+ npx forgestack-os-cli run-tasks
313
+ ```
314
+
315
+ **Task Properties:**
316
+ - `name` (required) - Display name for the task (for logging)
317
+ - `command` (required) - Shell command to execute
318
+ - `cwd` (optional) - Working directory for command execution (must exist)
319
+
320
+ **Example: Monorepo Build Pipeline**
321
+
322
+ ```json
323
+ {
324
+ "tasks": [
325
+ {
326
+ "name": "Clean Build Artifacts",
327
+ "command": "rm -rf dist"
328
+ },
329
+ {
330
+ "name": "Build Frontend",
331
+ "command": "npm run build",
332
+ "cwd": "./packages/frontend"
333
+ },
334
+ {
335
+ "name": "Build Backend",
336
+ "command": "npm run build",
337
+ "cwd": "./packages/backend"
338
+ },
339
+ {
340
+ "name": "Run Tests",
341
+ "command": "npm test",
342
+ "cwd": "./packages/backend"
343
+ },
344
+ {
345
+ "name": "Generate Docs",
346
+ "command": "npm run docs"
347
+ }
348
+ ],
349
+ "parallel": false,
350
+ "stopOnError": true
351
+ }
352
+ ```
45
353
 
46
- # Full JSON config
47
- forgestack create my-app --stack '{"frontend":"nextjs","backend":"fastify","auth":"supabase","database":"supabase-db","apiStyle":"trpc","docker":true,"multiTenant":true}'
354
+ **Output Example:**
355
+ ```
356
+ ⏳ Running tasks sequentially...
357
+ ✓ Task 1/5: Clean Build Artifacts completed
358
+ ✓ Task 2/5: Build Frontend completed
359
+ ✓ Task 3/5: Build Backend completed
360
+ ✓ Task 4/5: Run Tests completed
361
+ ✓ Task 5/5: Generate Docs completed
48
362
 
49
- # Minimal flags
50
- forgestack create api-only --frontend=react-vite --backend=express --auth=jwt --database=postgresql --api=rest --no-docker
363
+ All 5 tasks completed successfully
51
364
  ```
52
365
 
366
+ **Error Handling:**
367
+ - **Invalid config path**: Shows clear error, no crash
368
+ - **Malformed JSON**: Displays JSON parsing error
369
+ - **Missing command**: Validates all tasks have required fields
370
+ - **Invalid working directory**: Shows warning, continues with project root
371
+ - **Task execution fails**: Respects `stopOnError` flag
372
+ - **Cross-platform compatibility**: Automatically handles Windows vs Unix paths
373
+
374
+ ---
375
+
53
376
  ## Features
54
377
 
55
- - 150+ stack combinations across frontend, backend, auth, database, and API styles
56
- - Multi-tenancy ready scaffolds
57
- - Docker Compose and env templates
58
- - API docs (Swagger) for REST backends
59
- - TypeScript-first across generated code
378
+ ### Project Generation
379
+ - **150+ Stack Combinations** - Frontend (React, Next.js, Vue, Svelte) × Backend (Express, Fastify, NestJS, Bun, Go) × Auth × Database × API Style
380
+ - **Multi-tenancy Ready** - Scaffolding support for SaaS applications
381
+ - **Docker Compose** - Complete Docker setup with frontend and backend services
382
+ - **Environment Templates** - Pre-configured `.env.example` files
383
+ - **API Documentation** - Swagger/OpenAPI docs for REST APIs
384
+ - **TypeScript First** - Full TypeScript support across all generated code
385
+ - **Production Ready** - Best practices, security headers, error handling
60
386
 
61
- ## Generated Project
387
+ ### Utility Commands
388
+ - **File Organization** (`organize`) - Sort files by type or date, with MD5-based duplicate detection and automatic system folder exclusion
389
+ - **Batch Task Runner** (`run-tasks`) - Execute complex workflows with sequential or parallel task execution, cross-platform compatibility, and comprehensive error handling
390
+
391
+ ## Generated Project Structure
62
392
 
63
393
  ```
64
394
  my-app/
65
- ├─ frontend/ # Next.js or React+Vite
66
- ├─ backend/ # Express, Fastify, NestJS, Bun+Elysia, or Go+Fiber
67
- ├─ .env.example
68
- ├─ package.json (workspaces)
69
- └─ README.md (per-project runbook)
395
+ ├── frontend/ # React+Vite, Next.js, Vue, or SvelteKit
396
+ │ ├── src/
397
+ │ ├── public/
398
+ │ ├── package.json
399
+ │ ├── tsconfig.json
400
+ │ └── vite.config.ts (or next.config.js)
401
+
402
+ ├── backend/ # Express, Fastify, NestJS, Bun+Elysia, or Go
403
+ │ ├── src/
404
+ │ ├── dist/
405
+ │ ├── package.json
406
+ │ ├── tsconfig.json
407
+ │ └── .env.example
408
+
409
+ ├── docker/ # Optional - if --docker enabled
410
+ │ ├── docker-compose.yml
411
+ │ ├── frontend.Dockerfile
412
+ │ └── backend.Dockerfile
413
+
414
+ ├── .env.example # Environment variables template
415
+ ├── package.json # Workspace configuration (monorepo)
416
+ ├── README.md # Per-project setup guide
417
+ └── .gitignore
418
+ ```
419
+
420
+ ## Troubleshooting
421
+
422
+ ### "command not found: forgestack-os-cli"
423
+
424
+ **Using npx?** Make sure you use the full package name:
425
+
426
+ ```bash
427
+ npx forgestack-os-cli create my-app
428
+ ```
429
+
430
+ **Installed globally?** Try:
431
+
432
+ 1. Verify npm global bin is in PATH: `echo $PATH` (Unix) or `$env:Path` (Windows)
433
+ 2. Reinstall: `npm install -g forgestack-os-cli@latest`
434
+ 3. Check Node.js version: `node --version` (requires 18+)
435
+
436
+ ### "404 Not Found - forgestack"
437
+
438
+ The package name is **`forgestack-os-cli`**, not `forgestack`:
439
+
440
+ ```bash
441
+ # ✅ Correct
442
+ npx forgestack-os-cli create my-app
443
+
444
+ # ❌ Wrong
445
+ npx forgestack create my-app
446
+ ```
447
+
448
+ ### "Unknown command"
449
+
450
+ Ensure you're using `create`, not `init`:
451
+
452
+ ```bash
453
+ # ✅ Correct
454
+ npx forgestack-os-cli create my-app
455
+
456
+ # ❌ Wrong
457
+ npx forgestack-os-cli init my-app
458
+ ```
459
+
460
+ ### Preset not found
461
+
462
+ Available presets:
463
+
464
+ - `next-nest-clerk-pg` - Next.js, NestJS, Clerk, PostgreSQL
465
+ - `react-express-jwt-mongo` - React+Vite, Express, JWT, MongoDB
466
+ - `next-fastify-supabase-trpc` - Next.js, Fastify, Supabase, tRPC
467
+
468
+ Use exact preset names (case-sensitive).
469
+
470
+ ### Node.js version error
471
+
472
+ ForgeStack requires Node.js 18 or higher:
473
+
474
+ ```bash
475
+ node --version # Check your version
476
+ nvm install 18 # Or use nvm/fnm to upgrade
70
477
  ```
71
478
 
72
479
  ## Author
@@ -0,0 +1,2 @@
1
+ import { Command } from 'commander';
2
+ export declare function organizeCommand(folderPath: string | undefined, options: Record<string, unknown>, _command?: Command): Promise<void>;
@@ -0,0 +1,126 @@
1
+ import path from 'path';
2
+ import fs from 'fs-extra';
3
+ import chalk from 'chalk';
4
+ import inquirer from 'inquirer';
5
+ import { logger } from '../utils/logger.js';
6
+ import { organizeFilesByType, organizeFilesByDate, detectDuplicates } from '../utils/file-organizer.js';
7
+ export async function organizeCommand(folderPath, options, _command) {
8
+ try {
9
+ let targetPath = folderPath || '';
10
+ // If no folder path provided, use interactive prompt
11
+ if (!targetPath) {
12
+ const answers = await inquirer.prompt([
13
+ {
14
+ type: 'input',
15
+ name: 'path',
16
+ message: 'Enter the folder path to organize:',
17
+ default: process.cwd(),
18
+ validate: (input) => {
19
+ if (!input.trim())
20
+ return 'Path cannot be empty';
21
+ return true;
22
+ },
23
+ },
24
+ ]);
25
+ targetPath = answers.path;
26
+ }
27
+ // Resolve to absolute path
28
+ const absolutePath = path.resolve(targetPath);
29
+ // Validate folder exists
30
+ if (!(await fs.pathExists(absolutePath))) {
31
+ logger.error(`Folder does not exist: ${absolutePath}`);
32
+ process.exit(1);
33
+ }
34
+ const stats = await fs.stat(absolutePath);
35
+ if (!stats.isDirectory()) {
36
+ logger.error(`Path is not a directory: ${absolutePath}`);
37
+ process.exit(1);
38
+ }
39
+ // Get organization strategy
40
+ let strategy = options.strategy || '';
41
+ if (!['type', 'date'].includes(strategy)) {
42
+ const answers = await inquirer.prompt([
43
+ {
44
+ type: 'list',
45
+ name: 'strategy',
46
+ message: 'How would you like to organize files?',
47
+ choices: [
48
+ { name: 'By File Type (images, documents, etc.)', value: 'type' },
49
+ { name: 'By Date (YYYY-MM)', value: 'date' },
50
+ ],
51
+ },
52
+ ]);
53
+ strategy = answers.strategy;
54
+ }
55
+ // Check if user wants to handle duplicates
56
+ let handleDuplicates = !!options.duplicates;
57
+ if (!options.duplicates) {
58
+ const answers = await inquirer.prompt([
59
+ {
60
+ type: 'confirm',
61
+ name: 'duplicates',
62
+ message: 'Move duplicate files to a "Duplicates" folder?',
63
+ default: false,
64
+ },
65
+ ]);
66
+ handleDuplicates = answers.duplicates;
67
+ }
68
+ console.log('');
69
+ logger.title('📁 Organizing files...');
70
+ // Detect duplicates if needed
71
+ let duplicates = new Map();
72
+ if (handleDuplicates) {
73
+ logger.info('Detecting duplicate files...');
74
+ duplicates = await detectDuplicates(absolutePath);
75
+ if (duplicates.size === 0) {
76
+ console.log(chalk.gray('No duplicates found.'));
77
+ }
78
+ else {
79
+ console.log(chalk.gray(`Found ${duplicates.size} set(s) of duplicate files.`));
80
+ }
81
+ }
82
+ // Organize files
83
+ let result;
84
+ if (strategy === 'type') {
85
+ result = await organizeFilesByType(absolutePath, duplicates);
86
+ }
87
+ else {
88
+ result = await organizeFilesByDate(absolutePath, duplicates);
89
+ }
90
+ // Display results
91
+ console.log('');
92
+ logger.success('Organization complete!');
93
+ console.log('');
94
+ console.log(chalk.bold('Summary:'));
95
+ console.log(chalk.gray('─'.repeat(50)));
96
+ let totalFilesMoved = 0;
97
+ const categorizedEntries = Object.entries(result.categorized);
98
+ if (categorizedEntries.length === 0) {
99
+ console.log(chalk.gray('No files to organize.'));
100
+ }
101
+ else {
102
+ for (const [folder, count] of categorizedEntries) {
103
+ console.log(`${chalk.cyan('→')} ${folder}: ${chalk.bold(count)} files`);
104
+ totalFilesMoved += count;
105
+ }
106
+ }
107
+ if (result.duplicates > 0) {
108
+ console.log(`${chalk.cyan('→')} Duplicates: ${chalk.bold(result.duplicates)} files`);
109
+ totalFilesMoved += result.duplicates;
110
+ }
111
+ console.log(chalk.gray('─'.repeat(50)));
112
+ console.log(`${chalk.bold('Total files moved:')} ${chalk.green(totalFilesMoved)}`);
113
+ console.log('');
114
+ }
115
+ catch (error) {
116
+ logger.error('Failed to organize files');
117
+ if (error instanceof Error) {
118
+ console.error(chalk.red(error.message));
119
+ }
120
+ else {
121
+ console.error(error);
122
+ }
123
+ process.exit(1);
124
+ }
125
+ }
126
+ //# sourceMappingURL=organize.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"organize.js","sourceRoot":"","sources":["../../src/commands/organize.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ import { Command } from 'commander';
2
+ export interface Task {
3
+ name: string;
4
+ command: string;
5
+ cwd?: string;
6
+ }
7
+ export interface TasksConfig {
8
+ tasks: Task[];
9
+ parallel?: boolean;
10
+ stopOnError?: boolean;
11
+ }
12
+ export declare function runTasksCommand(configPath: string | undefined, options: Record<string, unknown>, _command?: Command): Promise<void>;
@@ -0,0 +1,125 @@
1
+ import path from 'path';
2
+ import fs from 'fs-extra';
3
+ import chalk from 'chalk';
4
+ import inquirer from 'inquirer';
5
+ import { logger } from '../utils/logger.js';
6
+ import { executeTasksSequentially, executeTasksInParallel } from '../utils/task-runner.js';
7
+ export async function runTasksCommand(configPath, options, _command) {
8
+ try {
9
+ let tasksFilePath = configPath || '';
10
+ // If no config path provided, use interactive prompt
11
+ if (!tasksFilePath) {
12
+ const answers = await inquirer.prompt([
13
+ {
14
+ type: 'input',
15
+ name: 'path',
16
+ message: 'Enter the path to tasks.json config file:',
17
+ default: './tasks.json',
18
+ validate: (input) => {
19
+ if (!input.trim())
20
+ return 'Path cannot be empty';
21
+ return true;
22
+ },
23
+ },
24
+ ]);
25
+ tasksFilePath = answers.path;
26
+ }
27
+ // Resolve to absolute path
28
+ const absolutePath = path.resolve(tasksFilePath);
29
+ // Validate file exists
30
+ if (!(await fs.pathExists(absolutePath))) {
31
+ logger.error(`Config file not found: ${absolutePath}`);
32
+ process.exit(1);
33
+ }
34
+ // Validate it's a file
35
+ const stats = await fs.stat(absolutePath);
36
+ if (!stats.isFile()) {
37
+ logger.error(`Path is not a file: ${absolutePath}`);
38
+ process.exit(1);
39
+ }
40
+ // Parse config file
41
+ let config;
42
+ try {
43
+ const fileContent = await fs.readFile(absolutePath, 'utf-8');
44
+ config = JSON.parse(fileContent);
45
+ }
46
+ catch (error) {
47
+ logger.error(`Failed to parse config file: ${absolutePath}`);
48
+ if (error instanceof SyntaxError) {
49
+ console.error(chalk.red(`JSON Error: ${error.message}`));
50
+ }
51
+ process.exit(1);
52
+ }
53
+ // Validate config
54
+ if (!config.tasks || !Array.isArray(config.tasks) || config.tasks.length === 0) {
55
+ logger.error('Config file must contain a "tasks" array with at least one task');
56
+ process.exit(1);
57
+ }
58
+ // Validate each task
59
+ for (const task of config.tasks) {
60
+ if (!task.name || !task.command) {
61
+ logger.error('Each task must have a "name" and "command" field');
62
+ process.exit(1);
63
+ }
64
+ if (task.cwd && !(await fs.pathExists(task.cwd))) {
65
+ logger.warning(`Working directory does not exist for task "${task.name}": ${task.cwd}`);
66
+ }
67
+ }
68
+ // Determine execution mode (CLI options override config file)
69
+ const parallel = options.parallel === true || (config.parallel === true && options.parallel !== false);
70
+ const stopOnError = options.stopOnError !== false && (config.stopOnError !== false || options.stopOnError === true);
71
+ console.log('');
72
+ logger.title('🚀 Running Tasks');
73
+ console.log(chalk.gray(`Total tasks: ${config.tasks.length}`));
74
+ console.log(chalk.gray(`Mode: ${parallel ? 'Parallel' : 'Sequential'}`));
75
+ console.log(chalk.gray(`Stop on error: ${stopOnError ? 'Yes' : 'No'}`));
76
+ console.log('');
77
+ // Execute tasks
78
+ let results;
79
+ if (parallel) {
80
+ results = await executeTasksInParallel(config.tasks, stopOnError);
81
+ }
82
+ else {
83
+ results = await executeTasksSequentially(config.tasks, stopOnError);
84
+ }
85
+ // Display results
86
+ console.log('');
87
+ logger.title('📊 Task Results');
88
+ console.log(chalk.gray('─'.repeat(60)));
89
+ let successCount = 0;
90
+ let failureCount = 0;
91
+ for (const result of results) {
92
+ const statusIcon = result.success ? chalk.green('✔') : chalk.red('✖');
93
+ const statusText = result.success ? chalk.green('SUCCESS') : chalk.red('FAILED');
94
+ console.log(`${statusIcon} ${chalk.bold(result.name)}: ${statusText}`);
95
+ if (!result.success) {
96
+ failureCount++;
97
+ if (result.error) {
98
+ console.log(chalk.red(` Error: ${result.error}`));
99
+ }
100
+ }
101
+ else {
102
+ successCount++;
103
+ }
104
+ }
105
+ console.log(chalk.gray('─'.repeat(60)));
106
+ console.log('');
107
+ console.log(chalk.bold(`Results: ${chalk.green(`${successCount} succeeded`)} / ${chalk.red(`${failureCount} failed`)}`));
108
+ console.log('');
109
+ // Exit with appropriate code
110
+ if (failureCount > 0 && stopOnError) {
111
+ process.exit(1);
112
+ }
113
+ }
114
+ catch (error) {
115
+ logger.error('Failed to run tasks');
116
+ if (error instanceof Error) {
117
+ console.error(chalk.red(error.message));
118
+ }
119
+ else {
120
+ console.error(error);
121
+ }
122
+ process.exit(1);
123
+ }
124
+ }
125
+ //# sourceMappingURL=run-tasks.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"run-tasks.js","sourceRoot":"","sources":["../../src/commands/run-tasks.ts"],"names":[],"mappings":"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"}
package/dist/index.js CHANGED
@@ -2,6 +2,8 @@
2
2
  import { createRequire } from 'module';
3
3
  import { Command } from 'commander';
4
4
  import { createCommand } from './commands/create.js';
5
+ import { organizeCommand } from './commands/organize.js';
6
+ import { runTasksCommand } from './commands/run-tasks.js';
5
7
  const pkgRequire = createRequire(import.meta.url);
6
8
  const { version: CLI_VERSION } = pkgRequire('../package.json');
7
9
  const program = new Command();
@@ -25,5 +27,17 @@ program
25
27
  .option('--multi-tenant', 'Enable multi-tenancy')
26
28
  .option('--skip-install', 'Skip dependency installation')
27
29
  .option('--skip-git', 'Skip Git initialization');
30
+ program
31
+ .command('organize [folder-path]')
32
+ .description('Organize files by type or date')
33
+ .action(organizeCommand)
34
+ .option('--strategy <type>', 'Organization strategy: type or date')
35
+ .option('--duplicates', 'Move duplicate files to a Duplicates folder');
36
+ program
37
+ .command('run-tasks [config-path]')
38
+ .description('Run batch tasks from a JSON config file')
39
+ .action(runTasksCommand)
40
+ .option('--parallel', 'Run tasks in parallel instead of sequentially')
41
+ .option('--stop-on-error', 'Stop execution if any task fails');
28
42
  program.parse();
29
43
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,aAAa,EAAE,MAAM,QAAQ,CAAC;AACvC,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAErD,MAAM,UAAU,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAClD,MAAM,EAAE,OAAO,EAAE,WAAW,EAAE,GAAG,UAAU,CAAC,iBAAiB,CAAwB,CAAC;AAEtF,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE,CAAC;AAE9B,OAAO;KACF,IAAI,CAAC,YAAY,CAAC;KAClB,WAAW,CAAC,4DAA4D,CAAC;KACzE,OAAO,CAAC,WAAW,CAAC,CAAC;AAE1B,OAAO;KACF,OAAO,CAAC,uBAAuB,CAAC;KAChC,WAAW,CAAC,0CAA0C,CAAC;KACvD,MAAM,CAAC,aAAa,CAAC;KACrB,MAAM,CAAC,wBAAwB,EAAE,oBAAoB,CAAC;KACtD,MAAM,CAAC,uBAAuB,EAAE,mBAAmB,CAAC;KACpD,MAAM,CAAC,mBAAmB,EAAE,eAAe,CAAC;KAC5C,MAAM,CAAC,iBAAiB,EAAE,UAAU,CAAC;KACrC,MAAM,CAAC,eAAe,EAAE,WAAW,CAAC;KACpC,MAAM,CAAC,iBAAiB,EAAE,+BAA+B,CAAC;KAC1D,MAAM,CAAC,gBAAgB,EAAE,mCAAmC,CAAC;KAC7D,MAAM,CAAC,UAAU,EAAE,8BAA8B,CAAC;KAClD,MAAM,CAAC,aAAa,EAAE,2BAA2B,CAAC;KAClD,MAAM,CAAC,gBAAgB,EAAE,sBAAsB,CAAC;KAChD,MAAM,CAAC,gBAAgB,EAAE,8BAA8B,CAAC;KACxD,MAAM,CAAC,YAAY,EAAE,yBAAyB,CAAC,CAAC;AAErD,OAAO,CAAC,KAAK,EAAE,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,aAAa,EAAE,MAAM,QAAQ,CAAC;AACvC,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AACzD,OAAO,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAE1D,MAAM,UAAU,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAClD,MAAM,EAAE,OAAO,EAAE,WAAW,EAAE,GAAG,UAAU,CAAC,iBAAiB,CAAwB,CAAC;AAEtF,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE,CAAC;AAE9B,OAAO;KACF,IAAI,CAAC,YAAY,CAAC;KAClB,WAAW,CAAC,4DAA4D,CAAC;KACzE,OAAO,CAAC,WAAW,CAAC,CAAC;AAE1B,OAAO;KACF,OAAO,CAAC,uBAAuB,CAAC;KAChC,WAAW,CAAC,0CAA0C,CAAC;KACvD,MAAM,CAAC,aAAa,CAAC;KACrB,MAAM,CAAC,wBAAwB,EAAE,oBAAoB,CAAC;KACtD,MAAM,CAAC,uBAAuB,EAAE,mBAAmB,CAAC;KACpD,MAAM,CAAC,mBAAmB,EAAE,eAAe,CAAC;KAC5C,MAAM,CAAC,iBAAiB,EAAE,UAAU,CAAC;KACrC,MAAM,CAAC,eAAe,EAAE,WAAW,CAAC;KACpC,MAAM,CAAC,iBAAiB,EAAE,+BAA+B,CAAC;KAC1D,MAAM,CAAC,gBAAgB,EAAE,mCAAmC,CAAC;KAC7D,MAAM,CAAC,UAAU,EAAE,8BAA8B,CAAC;KAClD,MAAM,CAAC,aAAa,EAAE,2BAA2B,CAAC;KAClD,MAAM,CAAC,gBAAgB,EAAE,sBAAsB,CAAC;KAChD,MAAM,CAAC,gBAAgB,EAAE,8BAA8B,CAAC;KACxD,MAAM,CAAC,YAAY,EAAE,yBAAyB,CAAC,CAAC;AAErD,OAAO;KACF,OAAO,CAAC,wBAAwB,CAAC;KACjC,WAAW,CAAC,gCAAgC,CAAC;KAC7C,MAAM,CAAC,eAAe,CAAC;KACvB,MAAM,CAAC,mBAAmB,EAAE,qCAAqC,CAAC;KAClE,MAAM,CAAC,cAAc,EAAE,6CAA6C,CAAC,CAAC;AAE3E,OAAO;KACF,OAAO,CAAC,yBAAyB,CAAC;KAClC,WAAW,CAAC,yCAAyC,CAAC;KACtD,MAAM,CAAC,eAAe,CAAC;KACvB,MAAM,CAAC,YAAY,EAAE,+CAA+C,CAAC;KACrE,MAAM,CAAC,iBAAiB,EAAE,kCAAkC,CAAC,CAAC;AAEnE,OAAO,CAAC,KAAK,EAAE,CAAC"}
@@ -0,0 +1,17 @@
1
+ interface OrganizeResult {
2
+ categorized: Record<string, number>;
3
+ duplicates: number;
4
+ }
5
+ /**
6
+ * Detect duplicate files by hash
7
+ */
8
+ export declare function detectDuplicates(folderPath: string): Promise<Map<string, string[]>>;
9
+ /**
10
+ * Organize files by type into categorized folders
11
+ */
12
+ export declare function organizeFilesByType(folderPath: string, duplicates?: Map<string, string[]>): Promise<OrganizeResult>;
13
+ /**
14
+ * Organize files by date (YYYY-MM format)
15
+ */
16
+ export declare function organizeFilesByDate(folderPath: string, duplicates?: Map<string, string[]>): Promise<OrganizeResult>;
17
+ export {};
@@ -0,0 +1,170 @@
1
+ import path from 'path';
2
+ import fs from 'fs-extra';
3
+ import crypto from 'crypto';
4
+ // File type categories
5
+ const FILE_CATEGORIES = {
6
+ Images: ['.jpg', '.jpeg', '.png', '.gif', '.bmp', '.svg', '.webp', '.ico', '.tiff'],
7
+ Documents: ['.pdf', '.doc', '.docx', '.txt', '.xls', '.xlsx', '.ppt', '.pptx', '.odt'],
8
+ Archives: ['.zip', '.rar', '.7z', '.tar', '.gz', '.bz2', '.iso'],
9
+ Videos: ['.mp4', '.avi', '.mkv', '.mov', '.wmv', '.flv', '.webm', '.m4v'],
10
+ Audio: ['.mp3', '.wav', '.flac', '.aac', '.ogg', '.wma', '.m4a'],
11
+ Code: ['.js', '.ts', '.py', '.java', '.cpp', '.c', '.go', '.rs', '.rb', '.php', '.html', '.css', '.json', '.xml', '.yaml', '.yml'],
12
+ Data: ['.csv', '.sql', '.db', '.sqlite', '.json', '.yaml'],
13
+ Executables: ['.exe', '.msi', '.app', '.bin', '.sh', '.bat'],
14
+ Others: [],
15
+ };
16
+ /**
17
+ * Calculate MD5 hash of a file
18
+ */
19
+ async function getFileHash(filePath) {
20
+ try {
21
+ const content = await fs.readFile(filePath);
22
+ return crypto.createHash('md5').update(content).digest('hex');
23
+ }
24
+ catch {
25
+ // Return empty string for unreadable files
26
+ return '';
27
+ }
28
+ }
29
+ /**
30
+ * Detect duplicate files by hash
31
+ */
32
+ export async function detectDuplicates(folderPath) {
33
+ const fileHashes = new Map();
34
+ const duplicates = new Map();
35
+ // System folders to skip
36
+ const skipFolders = new Set(['.git', '.env', 'node_modules', '.next', 'dist', 'build', '.DS_Store', '.vscode']);
37
+ async function scanDirectory(dir) {
38
+ try {
39
+ const files = await fs.readdir(dir);
40
+ for (const file of files) {
41
+ if (skipFolders.has(file) || file.startsWith('.')) {
42
+ continue;
43
+ }
44
+ const filePath = path.join(dir, file);
45
+ try {
46
+ const stats = await fs.stat(filePath);
47
+ if (stats.isDirectory()) {
48
+ await scanDirectory(filePath);
49
+ }
50
+ else if (stats.isFile() && stats.size > 0) {
51
+ try {
52
+ const hash = await getFileHash(filePath);
53
+ if (hash) {
54
+ if (!fileHashes.has(hash)) {
55
+ fileHashes.set(hash, []);
56
+ }
57
+ fileHashes.get(hash).push(filePath);
58
+ }
59
+ }
60
+ catch {
61
+ // Skip files that can't be hashed
62
+ }
63
+ }
64
+ }
65
+ catch {
66
+ // Skip files/folders that can't be accessed
67
+ }
68
+ }
69
+ }
70
+ catch {
71
+ // Skip directories that can't be read
72
+ }
73
+ }
74
+ await scanDirectory(folderPath);
75
+ // Identify actual duplicates
76
+ for (const [hash, files] of fileHashes) {
77
+ if (files.length > 1) {
78
+ duplicates.set(hash, files);
79
+ }
80
+ }
81
+ return duplicates;
82
+ }
83
+ /**
84
+ * Organize files by type into categorized folders
85
+ */
86
+ export async function organizeFilesByType(folderPath, duplicates = new Map()) {
87
+ const result = { categorized: {}, duplicates: 0 };
88
+ const duplicateSet = new Set();
89
+ // Build a set of duplicate files for quick lookup
90
+ for (const files of duplicates.values()) {
91
+ files.forEach(f => duplicateSet.add(f));
92
+ }
93
+ async function scanAndOrganize(dir) {
94
+ const files = await fs.readdir(dir);
95
+ for (const file of files) {
96
+ const filePath = path.join(dir, file);
97
+ const stats = await fs.stat(filePath);
98
+ if (stats.isDirectory()) {
99
+ // Skip special folders
100
+ if (file === 'Duplicates' || file.startsWith('.')) {
101
+ continue;
102
+ }
103
+ await scanAndOrganize(filePath);
104
+ }
105
+ else {
106
+ const ext = path.extname(file).toLowerCase();
107
+ // Find category
108
+ let category = 'Others';
109
+ for (const [cat, extensions] of Object.entries(FILE_CATEGORIES)) {
110
+ if (extensions.includes(ext)) {
111
+ category = cat;
112
+ break;
113
+ }
114
+ }
115
+ const destFolder = path.join(folderPath, duplicateSet.has(filePath) ? 'Duplicates' : category);
116
+ await fs.ensureDir(destFolder);
117
+ await fs.move(filePath, path.join(destFolder, file), { overwrite: true });
118
+ if (duplicateSet.has(filePath)) {
119
+ result.duplicates++;
120
+ }
121
+ else {
122
+ result.categorized[category] = (result.categorized[category] || 0) + 1;
123
+ }
124
+ }
125
+ }
126
+ }
127
+ await scanAndOrganize(folderPath);
128
+ return result;
129
+ }
130
+ /**
131
+ * Organize files by date (YYYY-MM format)
132
+ */
133
+ export async function organizeFilesByDate(folderPath, duplicates = new Map()) {
134
+ const result = { categorized: {}, duplicates: 0 };
135
+ const duplicateSet = new Set();
136
+ // Build a set of duplicate files
137
+ for (const files of duplicates.values()) {
138
+ files.forEach(f => duplicateSet.add(f));
139
+ }
140
+ async function scanAndOrganize(dir) {
141
+ const files = await fs.readdir(dir);
142
+ for (const file of files) {
143
+ const filePath = path.join(dir, file);
144
+ const stats = await fs.stat(filePath);
145
+ if (stats.isDirectory()) {
146
+ if (file.startsWith('.') || /^\d{4}-\d{2}$/.test(file)) {
147
+ continue;
148
+ }
149
+ await scanAndOrganize(filePath);
150
+ }
151
+ else {
152
+ // Get file modification date
153
+ const mtime = new Date(stats.mtime);
154
+ const dateFolder = mtime.toISOString().slice(0, 7); // YYYY-MM
155
+ const destFolder = path.join(folderPath, duplicateSet.has(filePath) ? 'Duplicates' : dateFolder);
156
+ await fs.ensureDir(destFolder);
157
+ await fs.move(filePath, path.join(destFolder, file), { overwrite: true });
158
+ if (duplicateSet.has(filePath)) {
159
+ result.duplicates++;
160
+ }
161
+ else {
162
+ result.categorized[dateFolder] = (result.categorized[dateFolder] || 0) + 1;
163
+ }
164
+ }
165
+ }
166
+ }
167
+ await scanAndOrganize(folderPath);
168
+ return result;
169
+ }
170
+ //# sourceMappingURL=file-organizer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-organizer.js","sourceRoot":"","sources":["../../src/utils/file-organizer.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ import { Task } from '../commands/run-tasks.js';
2
+ export interface TaskResult {
3
+ name: string;
4
+ success: boolean;
5
+ error?: string;
6
+ }
7
+ /**
8
+ * Execute tasks sequentially (one after another)
9
+ */
10
+ export declare function executeTasksSequentially(tasks: Task[], stopOnError?: boolean): Promise<TaskResult[]>;
11
+ /**
12
+ * Execute tasks in parallel
13
+ */
14
+ export declare function executeTasksInParallel(tasks: Task[], stopOnError?: boolean): Promise<TaskResult[]>;
@@ -0,0 +1,79 @@
1
+ import { execSync } from 'child_process';
2
+ import chalk from 'chalk';
3
+ import os from 'os';
4
+ /**
5
+ * Execute a single task
6
+ */
7
+ async function executeTask(task) {
8
+ try {
9
+ console.log(`${chalk.cyan('→')} Running: ${chalk.bold(task.name)}`);
10
+ // Use shell: true for cross-platform command execution
11
+ const options = {
12
+ stdio: 'pipe',
13
+ encoding: 'utf-8',
14
+ shell: true,
15
+ };
16
+ if (task.cwd) {
17
+ options.cwd = task.cwd;
18
+ }
19
+ // Add shell option for cross-platform compatibility
20
+ if (os.platform() === 'win32') {
21
+ options.shell = 'cmd.exe';
22
+ }
23
+ try {
24
+ execSync(task.command, options);
25
+ }
26
+ catch (error) {
27
+ // execSync throws on non-zero exit code
28
+ // Extract stderr if available
29
+ if (error instanceof Error) {
30
+ throw error;
31
+ }
32
+ throw new Error(`Command failed: ${task.command}`);
33
+ }
34
+ console.log(`${chalk.green('✔')} ${task.name} completed`);
35
+ return { name: task.name, success: true };
36
+ }
37
+ catch (error) {
38
+ const errorMessage = error instanceof Error ? error.message.split('\n')[0] : String(error);
39
+ console.log(`${chalk.red('✖')} ${task.name} failed`);
40
+ return { name: task.name, success: false, error: errorMessage };
41
+ }
42
+ }
43
+ /**
44
+ * Execute tasks sequentially (one after another)
45
+ */
46
+ export async function executeTasksSequentially(tasks, stopOnError = true) {
47
+ const results = [];
48
+ for (const task of tasks) {
49
+ console.log('');
50
+ const result = await executeTask(task);
51
+ results.push(result);
52
+ if (!result.success && stopOnError) {
53
+ console.log('');
54
+ console.log(chalk.yellow('⚠ Stopping execution due to task failure'));
55
+ break;
56
+ }
57
+ }
58
+ return results;
59
+ }
60
+ /**
61
+ * Execute tasks in parallel
62
+ */
63
+ export async function executeTasksInParallel(tasks, stopOnError = true) {
64
+ console.log('');
65
+ const promises = tasks.map(task => executeTask(task)
66
+ .catch(error => ({
67
+ name: task.name,
68
+ success: false,
69
+ error: error instanceof Error ? error.message : String(error),
70
+ })));
71
+ const results = await Promise.all(promises);
72
+ // If stopOnError is true and any task failed, we should note this
73
+ if (stopOnError && results.some(r => !r.success)) {
74
+ console.log('');
75
+ console.log(chalk.yellow('⚠ Some tasks failed during parallel execution'));
76
+ }
77
+ return results;
78
+ }
79
+ //# sourceMappingURL=task-runner.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"task-runner.js","sourceRoot":"","sources":["../../src/utils/task-runner.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "forgestack-os-cli",
3
- "version": "0.3.3",
4
- "description": "ForgeStack OS CLI - Generate production-ready full-stack SaaS applications",
3
+ "version": "0.3.5",
4
+ "description": "ForgeStack OS CLI - Generate production-ready full-stack SaaS applications with file organization and batch task execution utilities",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
7
7
  "exports": {
@@ -32,7 +32,13 @@
32
32
  "cli",
33
33
  "generator",
34
34
  "saas",
35
- "full-stack"
35
+ "full-stack",
36
+ "organize-files",
37
+ "file-organization",
38
+ "task-runner",
39
+ "batch-execution",
40
+ "duplicate-detection",
41
+ "workflow"
36
42
  ],
37
43
  "author": {
38
44
  "name": "Sumit Chauhan",
@@ -48,11 +54,12 @@
48
54
  },
49
55
  "homepage": "https://github.com/halloffame12/forgestack-os#readme",
50
56
  "engines": {
51
- "node": ">=20"
57
+ "node": ">=18"
52
58
  },
53
59
  "dependencies": {
54
60
  "chalk": "^4.1.2",
55
61
  "commander": "^13.0.0",
62
+ "crypto": "^1.0.1",
56
63
  "ejs": "^3.1.10",
57
64
  "execa": "^5.1.1",
58
65
  "fs-extra": "^11.2.0",