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 +444 -37
- package/dist/commands/organize.d.ts +2 -0
- package/dist/commands/organize.js +126 -0
- package/dist/commands/organize.js.map +1 -0
- package/dist/commands/run-tasks.d.ts +12 -0
- package/dist/commands/run-tasks.js +125 -0
- package/dist/commands/run-tasks.js.map +1 -0
- package/dist/index.js +14 -0
- package/dist/index.js.map +1 -1
- package/dist/utils/file-organizer.d.ts +17 -0
- package/dist/utils/file-organizer.js +170 -0
- package/dist/utils/file-organizer.js.map +1 -0
- package/dist/utils/task-runner.d.ts +14 -0
- package/dist/utils/task-runner.js +79 -0
- package/dist/utils/task-runner.js.map +1 -0
- package/package.json +11 -4
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
|
-
|
|
15
|
+
### Option 2: Install Globally
|
|
16
|
+
|
|
17
|
+
```bash
|
|
12
18
|
npm install -g forgestack-os-cli
|
|
13
|
-
|
|
14
|
-
|
|
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
|
-
**
|
|
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
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
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
|
-
|
|
299
|
+
**Examples:**
|
|
38
300
|
|
|
39
301
|
```bash
|
|
40
|
-
#
|
|
41
|
-
forgestack
|
|
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
|
-
#
|
|
44
|
-
forgestack
|
|
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
|
-
|
|
47
|
-
|
|
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
|
-
|
|
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
|
-
|
|
56
|
-
-
|
|
57
|
-
-
|
|
58
|
-
-
|
|
59
|
-
-
|
|
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
|
-
|
|
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
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
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,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;
|
|
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.
|
|
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": ">=
|
|
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",
|