@forgehive/forge-cli 0.3.9 → 0.3.11

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.
@@ -35,11 +35,10 @@ export const newTask = createTask({
35
35
  boundaries,
36
36
  fn: async function (argv, boundaries) {
37
37
  console.log('input:', argv)
38
- console.log('boundaries:', boundaries)
38
+ console.log('boundaries:', Object.keys(boundaries))
39
39
  // Your task implementation goes here
40
- const status = { status: 'Ok' }
41
40
 
42
- return status
41
+ return {}
43
42
  }
44
43
  })
45
44
 
@@ -94,5 +93,7 @@ describe('Create task', () => {
94
93
  const forgeContent = await fs.promises.readFile(path_1.default.join(rootDir, 'forge.json'), 'utf-8');
95
94
  const forgeConf = JSON.parse(forgeContent);
96
95
  expect(forgeConf.tasks['sample:newTask']).toBeDefined();
96
+ expect(forgeConf.tasks['sample:newTask'].uuid).toBeDefined();
97
+ expect(typeof forgeConf.tasks['sample:newTask'].uuid).toBe('string');
97
98
  });
98
99
  });
package/forge.json CHANGED
@@ -118,6 +118,20 @@
118
118
  "docs:download": {
119
119
  "path": "src/tasks/docs/download.ts",
120
120
  "handler": "download"
121
+ },
122
+ "project:create": {
123
+ "path": "src/tasks/project/create.ts",
124
+ "handler": "create"
125
+ },
126
+ "project:link": {
127
+ "path": "src/tasks/project/link.ts",
128
+ "handler": "link",
129
+ "uuid": "cb9f82e1-d397-46d9-9f0d-2b0e3becbfa1"
130
+ },
131
+ "project:unlink": {
132
+ "path": "src/tasks/project/unlink.ts",
133
+ "handler": "unlink",
134
+ "uuid": "414d37de-793c-4d01-899d-69515f5e0948"
121
135
  }
122
136
  },
123
137
  "runners": {}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@forgehive/forge-cli",
3
- "version": "0.3.9",
3
+ "version": "0.3.11",
4
4
  "description": "TypeScript CLI application",
5
5
  "license": "MIT",
6
6
  "main": "dist/index.js",
@@ -17,7 +17,8 @@
17
17
  "@forgehive/task": "^0.2.5",
18
18
  "esbuild": "^0.25.0",
19
19
  "handlebars": "^4.7.8",
20
- "minimist": "^1.2.8"
20
+ "minimist": "^1.2.8",
21
+ "typescript": "^5.3.3"
21
22
  }
22
23
  },
23
24
  "dependencies": {
@@ -27,11 +28,13 @@
27
28
  "esbuild": "^0.25.0",
28
29
  "handlebars": "^4.7.8",
29
30
  "minimist": "^1.2.8",
31
+ "typescript": "^5.3.3",
32
+ "uuid": "^11.1.0",
30
33
  "@forgehive/hive-sdk": "0.1.2",
31
34
  "@forgehive/record-tape": "0.2.5",
32
- "@forgehive/task": "0.2.5",
33
35
  "@forgehive/runner": "0.2.5",
34
- "@forgehive/schema": "0.1.4"
36
+ "@forgehive/schema": "0.1.4",
37
+ "@forgehive/task": "0.2.5"
35
38
  },
36
39
  "devDependencies": {
37
40
  "@types/archiver": "^6.0.3",
package/src/runner.ts CHANGED
@@ -20,7 +20,6 @@ import { describe as describeTask } from './tasks/task/describe'
20
20
  import { fingerprint as fingerprintTask } from './tasks/task/fingerprint'
21
21
  import { invoke as invokeTask } from './tasks/task/invoke'
22
22
 
23
-
24
23
  import { create as createRunner } from './tasks/runner/create'
25
24
  import { remove as removeRunner } from './tasks/runner/remove'
26
25
  import { bundle as bundleRunner } from './tasks/runner/bundle'
@@ -34,6 +33,10 @@ import { switchProfile } from './tasks/auth/switch'
34
33
  import { list as listProfiles } from './tasks/auth/list'
35
34
  import { remove as removeProfile } from './tasks/auth/remove'
36
35
 
36
+ import { create as createProject } from './tasks/project/create'
37
+ import { link as linkProject } from './tasks/project/link'
38
+ import { unlink as unlinkProject } from './tasks/project/unlink'
39
+
37
40
  interface CliParsedArguments extends RunnerParsedArguments {
38
41
  action: string;
39
42
  }
@@ -81,6 +84,11 @@ runner.load('auth:switch', switchProfile)
81
84
  runner.load('auth:list', listProfiles)
82
85
  runner.load('auth:remove', removeProfile)
83
86
 
87
+ // Project commands
88
+ runner.load('project:create', createProject)
89
+ runner.load('project:link', linkProject)
90
+ runner.load('project:unlink', unlinkProject)
91
+
84
92
  // Set handler
85
93
  runner.setHandler(async (data: ParsedArgs): Promise<unknown> => {
86
94
  const parsedArgs = runner.parseArguments(data)
@@ -167,7 +175,7 @@ runner.setHandler(async (data: ParsedArgs): Promise<unknown> => {
167
175
  })
168
176
  } else if (taskName === 'auth:switch' || taskName === 'auth:remove') {
169
177
  result = await task.run({
170
- profileName: action
178
+ profileName: String(action)
171
179
  })
172
180
  } else if (taskName === 'docs:download') {
173
181
  const { path } = args as { path?: string }
@@ -175,6 +183,20 @@ runner.setHandler(async (data: ParsedArgs): Promise<unknown> => {
175
183
  result = await task.run({
176
184
  path
177
185
  })
186
+ } else if (taskName === 'project:create') {
187
+ const { projectName, description } = args as { projectName?: string, description?: string }
188
+
189
+ result = await task.run({
190
+ projectName,
191
+ description
192
+ })
193
+ } else if (taskName === 'project:link') {
194
+ const { uuid } = args as { uuid: string }
195
+ result = await task.run({
196
+ uuid
197
+ })
198
+ } else if (taskName === 'project:unlink') {
199
+ result = await task.run({})
178
200
  } else {
179
201
  result = await task.run(args)
180
202
 
@@ -183,7 +205,7 @@ runner.setHandler(async (data: ParsedArgs): Promise<unknown> => {
183
205
  }
184
206
  }
185
207
 
186
- if (taskName === 'task:describe' || taskName === 'task:list') {
208
+ if (taskName === 'task:describe' || taskName === 'task:list' || taskName === 'auth:list') {
187
209
  silent = true
188
210
  }
189
211
 
@@ -25,19 +25,33 @@ export const list = createTask({
25
25
  return { status: 'Ok', profiles: [] }
26
26
  }
27
27
 
28
- console.log('Available profiles:')
28
+ // Show current profile
29
+ const currentProfile = profiles.profiles.find(profile => profile.name === profiles.default)
30
+ if (currentProfile) {
31
+ console.log('Current Profile:')
32
+ console.log(` Name: ${currentProfile.name}`)
33
+ console.log(` API Key: ${currentProfile.apiKey}`)
34
+ console.log(` URL: ${currentProfile.url}`)
35
+ console.log('')
36
+ }
37
+
38
+ console.log('Available profiles:\n')
39
+
40
+ const tableData = profiles.profiles.map(profile => ({
41
+ Name: profile.name,
42
+ 'API Key': profile.apiKey,
43
+ URL: profile.url
44
+ }))
29
45
 
30
- profiles.profiles.forEach(profile => {
31
- const isDefault = profile.name === profiles.default
32
- const prefix = isDefault ? '* ' : ' '
33
- console.log(`${prefix}${profile.name} - API Key: ${profile.apiKey}`)
34
- })
46
+ console.table(tableData, ['Name', 'API Key', 'URL'])
35
47
 
36
48
  console.log('\nUse auth:add to create or update a profile')
37
- console.log('\nUse auth:switch to switch to a profile')
49
+ console.log('Use auth:switch [name] or auth:switch [index] to switch profiles')
50
+ console.log('========================================')
38
51
 
39
52
  return {
40
- default: profiles.default
53
+ default: profiles.default,
54
+ profiles: tableData
41
55
  }
42
56
  }
43
57
  })
@@ -43,6 +43,7 @@ export const load = createTask({
43
43
  profiles = JSON.parse(content) as Profiles
44
44
  } catch (_error) {
45
45
  console.log('Creating profiles.json')
46
+ console.log('===============================================')
46
47
  await fs.writeFile(profilesPath, '{"profiles": [], "default": ""}')
47
48
  }
48
49
 
@@ -1,6 +1,6 @@
1
1
  // TASK: switch
2
2
  // Run this task with:
3
- // forge task:run auth:switch --profileName [name]
3
+ // forge auth:switch [name] or forge auth:switch [index]
4
4
 
5
5
  import { createTask } from '@forgehive/task'
6
6
  import { Schema } from '@forgehive/schema'
@@ -31,23 +31,39 @@ export const switchProfile = createTask({
31
31
  // Load profiles
32
32
  const profiles = await loadProfiles({})
33
33
 
34
- // Check if profile exists
35
- const profileExists = profiles.profiles.some(profile => profile.name === profileName)
34
+ if (profiles.profiles.length === 0) {
35
+ throw new Error('No profiles found. Use auth:add to create one.')
36
+ }
37
+
38
+ let targetProfile: string
36
39
 
37
- if (!profileExists) {
38
- throw new Error(`Profile "${profileName}" not found. Use auth:list to see available profiles.`)
40
+ // Check if profileName is a number (index)
41
+ const indexInput = parseInt(profileName, 10)
42
+ if (!isNaN(indexInput)) {
43
+ // Using index
44
+ if (indexInput < 0 || indexInput >= profiles.profiles.length) {
45
+ throw new Error(`Profile index ${indexInput} is out of range. Use auth:list to see available profiles (0-${profiles.profiles.length - 1}).`)
46
+ }
47
+ targetProfile = profiles.profiles[indexInput].name
48
+ } else {
49
+ // Using profile name
50
+ const profileExists = profiles.profiles.some(profile => profile.name === profileName)
51
+ if (!profileExists) {
52
+ throw new Error(`Profile "${profileName}" not found. Use auth:list to see available profiles.`)
53
+ }
54
+ targetProfile = profileName
39
55
  }
40
56
 
41
57
  // Update default profile
42
- profiles.default = profileName
58
+ profiles.default = targetProfile
43
59
 
44
60
  // Save updated profiles
45
61
  await persistProfiles(profiles)
46
62
 
47
- console.log(`Switched to profile: ${profileName}`)
63
+ console.log(`Switched to profile: ${targetProfile}`)
48
64
 
49
65
  return {
50
- default: profileName
66
+ default: targetProfile
51
67
  }
52
68
  }
53
69
  })
@@ -27,7 +27,29 @@ export const info = createTask({
27
27
  const packageJsonContent = await readFile(packageJsonPath)
28
28
  const packageJson = JSON.parse(packageJsonContent)
29
29
 
30
- const config = await loadConfig({})
30
+ let config
31
+ try {
32
+ config = await loadConfig({})
33
+ } catch (error: unknown) {
34
+ if (error instanceof Error && error.message.includes('ENOENT')) {
35
+ console.log('============ Forge CLI Information ============')
36
+ console.log('===============================================')
37
+ console.log()
38
+ console.log(`Version: ${packageJson.version}`)
39
+ console.log()
40
+ console.log('āŒ No forge.json file found in current directory.')
41
+ console.log(' Run "forge init" to create a new Forge project.')
42
+ console.log()
43
+ console.log('===============================================')
44
+ return {
45
+ version: packageJson.version,
46
+ profile: null,
47
+ paths: null,
48
+ error: 'No forge.json found'
49
+ }
50
+ }
51
+ throw error
52
+ }
31
53
 
32
54
  // Display human-friendly information
33
55
  console.log('===============================================')
@@ -0,0 +1,268 @@
1
+ # Project Commands
2
+
3
+ This directory contains ForgeHive CLI commands for project management operations.
4
+
5
+ ## Commands
6
+
7
+ ### `project:create`
8
+
9
+ Creates a new project in ForgeHive with automatic UUID generation and API integration.
10
+
11
+ #### Usage
12
+
13
+ ```bash
14
+ forge project:create [--projectName="My Project"] [--description="Project description"]
15
+ ```
16
+
17
+ #### Parameters
18
+
19
+ | Parameter | Type | Required | Description |
20
+ |-----------|------|----------|-------------|
21
+ | `projectName` | string | No | Name of the project to create. If not provided, uses the project name from `forge.json` |
22
+ | `description` | string | No | Optional description for the project |
23
+
24
+ #### Behavior
25
+
26
+ 1. **UUID Management**: Checks if the local `forge.json` has a project UUID
27
+ - If no UUID exists, generates a new UUID v4 and saves it to `forge.json`
28
+ - If UUID already exists, uses the existing one
29
+
30
+ 2. **Authentication**: Uses the current profile from `auth:list` for API authentication
31
+ - Requires a valid profile set via `auth:add` and `auth:switch`
32
+ - Uses profile's `apiKey`, `apiSecret`, and `url` for the request
33
+
34
+ 3. **API Request**: Makes a POST request to `/api/projects` with:
35
+ - `projectName`: The provided project name
36
+ - `description`: The provided description (or empty string if not provided)
37
+ - `uuid`: The project UUID (generated or existing)
38
+
39
+ 4. **Response**: Returns the created project details including:
40
+ - Project UUID
41
+ - Project name
42
+ - Team information
43
+ - Creation timestamps
44
+
45
+ #### Examples
46
+
47
+ ```bash
48
+ # Create a project using the name from forge.json
49
+ forge project:create
50
+
51
+ # Create a project with custom name
52
+ forge project:create --projectName="My New Project"
53
+
54
+ # Create a project with custom name and description
55
+ forge project:create --projectName="Analytics Dashboard" --description="Customer analytics and reporting platform"
56
+
57
+ # Create a project using forge.json name but with description
58
+ forge project:create --description="Using the default project name from configuration"
59
+ ```
60
+
61
+ #### Success Output
62
+
63
+ ```
64
+ Generated and saved project UUID: 550e8400-e29b-41d4-a716-446655440000
65
+ Project created successfully!
66
+ Project UUID: 550e8400-e29b-41d4-a716-446655440000
67
+ Project Name: My New Project
68
+
69
+ 🌐 View your project on the dashboard: https://api.forgehive.com/dashboard/projects/550e8400-e29b-41d4-a716-446655440000
70
+ ```
71
+
72
+ #### Error Cases
73
+
74
+ - **No Project Name**: If no `--projectName` is provided and `forge.json` doesn't contain a project name
75
+ - **No Profile**: If no authentication profile is configured
76
+ - **Invalid Profile**: If the current profile is invalid or expired
77
+ - **API Errors**: Network issues, server errors, or validation failures
78
+ - **Duplicate Names**: If a project with the same name already exists in the team
79
+
80
+ #### File Changes
81
+
82
+ When run, this command may modify:
83
+ - `forge.json`: Adds or updates the project UUID if one doesn't exist
84
+
85
+ #### Dependencies
86
+
87
+ - Requires `@forgehive/task` framework
88
+ - Requires `@forgehive/schema` for validation
89
+ - Uses `uuid` package for UUID generation
90
+ - Integrates with `auth:loadCurrent` for profile management
91
+ - Integrates with `conf:load` for configuration management
92
+
93
+ #### API Integration
94
+
95
+ This command integrates with the ForgeHive Projects API. See the [Projects API Documentation](../../../../../../../hive/docs/projects-api.md) for detailed API specifications.
96
+
97
+ ---
98
+
99
+ ## Project Configuration
100
+
101
+ Projects are configured via the `forge.json` file in your project root:
102
+
103
+ ```json
104
+ {
105
+ "project": {
106
+ "name": "My Project",
107
+ "uuid": "550e8400-e29b-41d4-a716-446655440000"
108
+ }
109
+ }
110
+ ```
111
+
112
+ The UUID is automatically generated and managed by the CLI commands.
113
+
114
+ ---
115
+
116
+ ### `project:link`
117
+
118
+ Links an existing remote project to the local project by validating the UUID and updating forge.json.
119
+
120
+ #### Usage
121
+
122
+ ```bash
123
+ forge project:link [uuid]
124
+ ```
125
+
126
+ #### Parameters
127
+
128
+ | Parameter | Type | Required | Description |
129
+ |-----------|------|----------|-------------|
130
+ | `uuid` | string (UUID) | Yes | UUID of the existing remote project to link |
131
+
132
+ #### Behavior
133
+
134
+ 1. **UUID Validation**: Validates the provided UUID format using regex
135
+ 2. **Remote Verification**: Makes a GET request to `/api/projects/{uuid}` to verify the project exists
136
+ 3. **Authentication**: Uses the current profile for API authentication
137
+ 4. **Project Details**: Displays project information (name, description, task count) if found
138
+ 5. **Local Update**: Updates the local `forge.json` with the verified UUID
139
+
140
+ #### Examples
141
+
142
+ ```bash
143
+ # Link to an existing remote project
144
+ forge project:link 550e8400-e29b-41d4-a716-446655440000
145
+ ```
146
+
147
+ #### Success Output
148
+
149
+ ```
150
+ Checking if project 550e8400-e29b-41d4-a716-446655440000 exists on https://api.forgehive.com...
151
+ āœ“ Found project: Customer Analytics
152
+ Description: Analytics platform for customer behavior analysis
153
+ Tasks: 3 task(s)
154
+
155
+ āœ“ Successfully linked project 550e8400-e29b-41d4-a716-446655440000 to local forge.json
156
+ Local project name: My Local Project
157
+ Remote project name: Customer Analytics
158
+
159
+ 🌐 View your project on the dashboard: https://api.forgehive.com/dashboard/projects/550e8400-e29b-41d4-a716-446655440000
160
+ ```
161
+
162
+ #### Error Cases
163
+
164
+ - **Invalid UUID Format**: If the provided UUID doesn't match the expected format
165
+ - **Project Already Linked**: If the local project already has a UUID in forge.json
166
+ - **Project Not Found**: If the UUID doesn't exist on the remote server (404)
167
+ - **Authentication Failed**: If the current profile credentials are invalid (401)
168
+ - **No Profile**: If no authentication profile is configured
169
+ - **Network Issues**: Connection problems or server errors
170
+
171
+ #### Error Examples
172
+
173
+ ```bash
174
+ # Invalid UUID format
175
+ forge project:link invalid-uuid
176
+ # Error: Invalid UUID format: invalid-uuid. Please provide a valid UUID.
177
+
178
+ # Project already linked
179
+ forge project:link 550e8400-e29b-41d4-a716-446655440000
180
+ # Error: Project is already linked to UUID: 123e4567-e89b-12d3-a456-426614174000. Use a different project or remove the existing UUID from forge.json first.
181
+
182
+ # Project not found
183
+ forge project:link 00000000-0000-0000-0000-000000000000
184
+ # Error: Project with UUID 00000000-0000-0000-0000-000000000000 not found on https://api.forgehive.com. Please verify the UUID is correct.
185
+
186
+ # Authentication failed
187
+ forge project:link 550e8400-e29b-41d4-a716-446655440000
188
+ # Error: Authentication failed. Please check your profile credentials with 'forge auth:list'.
189
+ ```
190
+
191
+ #### File Changes
192
+
193
+ When run successfully, this command modifies:
194
+ - `forge.json`: Updates the `project.uuid` field with the verified remote project UUID
195
+
196
+ #### Dependencies
197
+
198
+ - Requires `@forgehive/task` framework
199
+ - Requires `@forgehive/schema` for validation
200
+ - Integrates with `auth:loadCurrent` for profile management
201
+ - Integrates with `conf:load` for configuration management
202
+ - Uses the ForgeHive Projects API `/api/projects/{uuid}` endpoint
203
+
204
+ ---
205
+
206
+ ### `project:unlink`
207
+
208
+ Removes the project UUID link from the local forge.json, allowing the project to be linked to a different remote project.
209
+
210
+ #### Usage
211
+
212
+ ```bash
213
+ forge project:unlink
214
+ ```
215
+
216
+ #### Parameters
217
+
218
+ This command takes no parameters.
219
+
220
+ #### Behavior
221
+
222
+ 1. **UUID Check**: Verifies that a project UUID exists in forge.json
223
+ 2. **Local Update**: Removes the UUID field from the project configuration
224
+ 3. **Confirmation**: Shows the UUID that was removed and next steps
225
+
226
+ #### Examples
227
+
228
+ ```bash
229
+ # Unlink the current project
230
+ forge project:unlink
231
+ ```
232
+
233
+ #### Success Output
234
+
235
+ ```
236
+ āœ“ Successfully unlinked project from UUID: 550e8400-e29b-41d4-a716-446655440000
237
+ The project is no longer linked to a remote project.
238
+ You can now link to a different project using 'forge project:link [uuid]'
239
+ ```
240
+
241
+ #### Error Cases
242
+
243
+ - **No UUID Found**: If the project is not currently linked (no UUID in forge.json)
244
+
245
+ #### Error Examples
246
+
247
+ ```bash
248
+ # No project linked
249
+ forge project:unlink
250
+ # Error: No project UUID found in forge.json. The project is not currently linked to a remote project.
251
+ ```
252
+
253
+ #### File Changes
254
+
255
+ When run successfully, this command modifies:
256
+ - `forge.json`: Removes the `project.uuid` field completely
257
+
258
+ #### Dependencies
259
+
260
+ - Requires `@forgehive/task` framework
261
+ - Requires `@forgehive/schema` for validation
262
+ - Integrates with `conf:load` for configuration management
263
+
264
+ **Note**: This command only affects the local forge.json file. It does not make any API calls or modify anything on the remote server.
265
+
266
+ ---
267
+
268
+ ## Project Configuration
@@ -0,0 +1,111 @@
1
+ // TASK: create
2
+ // Run this task with:
3
+ // forge task:run project:create
4
+
5
+ import { createTask } from '@forgehive/task'
6
+ import { Schema } from '@forgehive/schema'
7
+ import { v4 as uuidv4 } from 'uuid'
8
+ import fs from 'fs/promises'
9
+ import path from 'path'
10
+
11
+ import { load as loadConf } from '../conf/load'
12
+ import { loadCurrent as loadCurrentProfile } from '../auth/loadCurrent'
13
+ import { type ForgeConf, type Profile } from '../types'
14
+
15
+ const name = 'project:create'
16
+ const description = 'Create a new project in ForgeHive'
17
+
18
+ const schema = new Schema({
19
+ projectName: Schema.string().optional(),
20
+ description: Schema.string().optional()
21
+ })
22
+
23
+ const boundaries = {
24
+ loadConf: loadConf.asBoundary(),
25
+ loadCurrentProfile: loadCurrentProfile.asBoundary(),
26
+ writeFile: async (filePath: string, content: string): Promise<void> => {
27
+ await fs.writeFile(filePath, content, 'utf-8')
28
+ },
29
+ createProject: async (profile: Profile, payload: { projectName: string; description: string; uuid: string }): Promise<Response> => {
30
+ const authToken = `${profile.apiKey}:${profile.apiSecret}`
31
+ return await fetch(`${profile.url}/api/projects`, {
32
+ method: 'POST',
33
+ headers: {
34
+ 'Content-Type': 'application/json',
35
+ 'Authorization': `Bearer ${authToken}`
36
+ },
37
+ body: JSON.stringify(payload)
38
+ })
39
+ }
40
+ }
41
+
42
+ export const create = createTask({
43
+ name,
44
+ description,
45
+ schema,
46
+ boundaries,
47
+ fn: async function (argv, { loadConf, loadCurrentProfile, writeFile, createProject }) {
48
+ const { projectName: inputProjectName, description } = argv
49
+
50
+ // Load current configuration
51
+ const conf = await loadConf({})
52
+
53
+ // Use provided projectName or fall back to forge.json project name
54
+ const projectName = inputProjectName || conf.project.name
55
+
56
+ if (!projectName) {
57
+ throw new Error('Project name is required. Provide --projectName or ensure forge.json has a project name.')
58
+ }
59
+
60
+ // Check if project already has a UUID, generate one if not
61
+ let projectUuid = conf.project.uuid
62
+ if (!projectUuid) {
63
+ projectUuid = uuidv4()
64
+
65
+ // Update forge.json with the new UUID
66
+ const forgePath = path.join(process.cwd(), 'forge.json')
67
+ const updatedConf: ForgeConf = {
68
+ ...conf,
69
+ project: {
70
+ ...conf.project,
71
+ uuid: projectUuid
72
+ }
73
+ }
74
+
75
+ await writeFile(forgePath, JSON.stringify(updatedConf, null, 2))
76
+ console.log(`Generated and saved project UUID: ${projectUuid}`)
77
+ }
78
+
79
+ // Load current profile for API authentication
80
+ const profile = await loadCurrentProfile({})
81
+
82
+ // Prepare API request payload
83
+ const payload = {
84
+ projectName,
85
+ description: description || '',
86
+ uuid: projectUuid
87
+ }
88
+
89
+ // Make API request to create project
90
+ const response = await createProject(profile, payload)
91
+
92
+ if (!response.ok) {
93
+ const errorText = await response.text()
94
+ throw new Error(`Failed to create project: ${response.status} ${response.statusText} - ${errorText}`)
95
+ }
96
+
97
+ const result = await response.json()
98
+
99
+ console.log('Project created successfully!')
100
+ console.log(`Project UUID: ${result.project.uuid}`)
101
+ console.log(`Project Name: ${result.project.projectName}`)
102
+ console.log(`\n🌐 View your project on the dashboard: ${profile.url}/dashboard/projects/${result.project.uuid}`)
103
+
104
+ return {
105
+ success: true,
106
+ project: result.project,
107
+ localUuid: projectUuid
108
+ }
109
+ }
110
+ })
111
+