@agentuity/cli 0.0.6
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/AGENTS.md +139 -0
- package/README.md +239 -0
- package/bin/cli.ts +71 -0
- package/dist/api.d.ts +25 -0
- package/dist/api.d.ts.map +1 -0
- package/dist/auth.d.ts +7 -0
- package/dist/auth.d.ts.map +1 -0
- package/dist/banner.d.ts +2 -0
- package/dist/banner.d.ts.map +1 -0
- package/dist/cli.d.ts +5 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cmd/auth/api.d.ts +9 -0
- package/dist/cmd/auth/api.d.ts.map +1 -0
- package/dist/cmd/auth/index.d.ts +2 -0
- package/dist/cmd/auth/index.d.ts.map +1 -0
- package/dist/cmd/auth/login.d.ts +3 -0
- package/dist/cmd/auth/login.d.ts.map +1 -0
- package/dist/cmd/auth/logout.d.ts +3 -0
- package/dist/cmd/auth/logout.d.ts.map +1 -0
- package/dist/cmd/bundle/ast.d.ts +2 -0
- package/dist/cmd/bundle/ast.d.ts.map +1 -0
- package/dist/cmd/bundle/bundler.d.ts +6 -0
- package/dist/cmd/bundle/bundler.d.ts.map +1 -0
- package/dist/cmd/bundle/file.d.ts +2 -0
- package/dist/cmd/bundle/file.d.ts.map +1 -0
- package/dist/cmd/bundle/index.d.ts +2 -0
- package/dist/cmd/bundle/index.d.ts.map +1 -0
- package/dist/cmd/bundle/plugin.d.ts +4 -0
- package/dist/cmd/bundle/plugin.d.ts.map +1 -0
- package/dist/cmd/dev/index.d.ts +2 -0
- package/dist/cmd/dev/index.d.ts.map +1 -0
- package/dist/cmd/example/create-user.d.ts +2 -0
- package/dist/cmd/example/create-user.d.ts.map +1 -0
- package/dist/cmd/example/create.d.ts +2 -0
- package/dist/cmd/example/create.d.ts.map +1 -0
- package/dist/cmd/example/deploy.d.ts +2 -0
- package/dist/cmd/example/deploy.d.ts.map +1 -0
- package/dist/cmd/example/index.d.ts +2 -0
- package/dist/cmd/example/index.d.ts.map +1 -0
- package/dist/cmd/example/list.d.ts +2 -0
- package/dist/cmd/example/list.d.ts.map +1 -0
- package/dist/cmd/example/run-command.d.ts +2 -0
- package/dist/cmd/example/run-command.d.ts.map +1 -0
- package/dist/cmd/example/sound.d.ts +3 -0
- package/dist/cmd/example/sound.d.ts.map +1 -0
- package/dist/cmd/example/spinner.d.ts +2 -0
- package/dist/cmd/example/spinner.d.ts.map +1 -0
- package/dist/cmd/example/steps.d.ts +2 -0
- package/dist/cmd/example/steps.d.ts.map +1 -0
- package/dist/cmd/example/version.d.ts +2 -0
- package/dist/cmd/example/version.d.ts.map +1 -0
- package/dist/cmd/index.d.ts +3 -0
- package/dist/cmd/index.d.ts.map +1 -0
- package/dist/cmd/profile/create.d.ts +2 -0
- package/dist/cmd/profile/create.d.ts.map +1 -0
- package/dist/cmd/profile/delete.d.ts +2 -0
- package/dist/cmd/profile/delete.d.ts.map +1 -0
- package/dist/cmd/profile/index.d.ts +2 -0
- package/dist/cmd/profile/index.d.ts.map +1 -0
- package/dist/cmd/profile/list.d.ts +3 -0
- package/dist/cmd/profile/list.d.ts.map +1 -0
- package/dist/cmd/profile/show.d.ts +2 -0
- package/dist/cmd/profile/show.d.ts.map +1 -0
- package/dist/cmd/profile/use.d.ts +2 -0
- package/dist/cmd/profile/use.d.ts.map +1 -0
- package/dist/cmd/project/create.d.ts +2 -0
- package/dist/cmd/project/create.d.ts.map +1 -0
- package/dist/cmd/project/delete.d.ts +2 -0
- package/dist/cmd/project/delete.d.ts.map +1 -0
- package/dist/cmd/project/index.d.ts +2 -0
- package/dist/cmd/project/index.d.ts.map +1 -0
- package/dist/cmd/project/list.d.ts +2 -0
- package/dist/cmd/project/list.d.ts.map +1 -0
- package/dist/cmd/project/show.d.ts +2 -0
- package/dist/cmd/project/show.d.ts.map +1 -0
- package/dist/cmd/version/index.d.ts +2 -0
- package/dist/cmd/version/index.d.ts.map +1 -0
- package/dist/command-prefix.d.ts +11 -0
- package/dist/command-prefix.d.ts.map +1 -0
- package/dist/config.d.ts +16 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/index.d.ts +18 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/legacy-check.d.ts +6 -0
- package/dist/legacy-check.d.ts.map +1 -0
- package/dist/logger.d.ts +24 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/runtime.d.ts +3 -0
- package/dist/runtime.d.ts.map +1 -0
- package/dist/schema-parser.d.ts +24 -0
- package/dist/schema-parser.d.ts.map +1 -0
- package/dist/sound.d.ts +2 -0
- package/dist/sound.d.ts.map +1 -0
- package/dist/steps.d.ts +59 -0
- package/dist/steps.d.ts.map +1 -0
- package/dist/terminal.d.ts +3 -0
- package/dist/terminal.d.ts.map +1 -0
- package/dist/tui.d.ts +156 -0
- package/dist/tui.d.ts.map +1 -0
- package/dist/types.d.ts +164 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/version.d.ts +10 -0
- package/dist/version.d.ts.map +1 -0
- package/package.json +46 -0
- package/src/api-errors.md +115 -0
- package/src/api.ts +186 -0
- package/src/auth.ts +91 -0
- package/src/banner.ts +23 -0
- package/src/cli.ts +198 -0
- package/src/cmd/auth/README.md +95 -0
- package/src/cmd/auth/api.ts +71 -0
- package/src/cmd/auth/index.ts +9 -0
- package/src/cmd/auth/login.ts +76 -0
- package/src/cmd/auth/logout.ts +14 -0
- package/src/cmd/bundle/ast.ts +228 -0
- package/src/cmd/bundle/bundler.ts +88 -0
- package/src/cmd/bundle/file.ts +16 -0
- package/src/cmd/bundle/index.ts +38 -0
- package/src/cmd/bundle/plugin.ts +259 -0
- package/src/cmd/dev/index.ts +83 -0
- package/src/cmd/example/create-user.ts +38 -0
- package/src/cmd/example/create.ts +31 -0
- package/src/cmd/example/deploy.ts +36 -0
- package/src/cmd/example/index.ts +27 -0
- package/src/cmd/example/list.ts +32 -0
- package/src/cmd/example/run-command.ts +45 -0
- package/src/cmd/example/sound.ts +14 -0
- package/src/cmd/example/spinner.ts +44 -0
- package/src/cmd/example/steps.ts +66 -0
- package/src/cmd/example/version.ts +13 -0
- package/src/cmd/index.ts +46 -0
- package/src/cmd/profile/README.md +80 -0
- package/src/cmd/profile/create.ts +57 -0
- package/src/cmd/profile/delete.ts +52 -0
- package/src/cmd/profile/index.ts +12 -0
- package/src/cmd/profile/list.ts +27 -0
- package/src/cmd/profile/show.ts +54 -0
- package/src/cmd/profile/use.ts +30 -0
- package/src/cmd/project/create.ts +247 -0
- package/src/cmd/project/delete.ts +13 -0
- package/src/cmd/project/index.ts +11 -0
- package/src/cmd/project/list.ts +13 -0
- package/src/cmd/project/show.ts +12 -0
- package/src/cmd/version/index.ts +16 -0
- package/src/command-prefix.ts +43 -0
- package/src/config.ts +304 -0
- package/src/index.ts +40 -0
- package/src/legacy-check.ts +127 -0
- package/src/logger.ts +235 -0
- package/src/runtime.ts +22 -0
- package/src/schema-parser.ts +213 -0
- package/src/sound.ts +25 -0
- package/src/steps.ts +245 -0
- package/src/terminal.ts +151 -0
- package/src/tui.md +254 -0
- package/src/tui.ts +838 -0
- package/src/types.ts +243 -0
- package/src/version.ts +29 -0
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { SubcommandDefinition } from '../../types';
|
|
2
|
+
import * as tui from '@/tui';
|
|
3
|
+
import { playSound } from '@/sound';
|
|
4
|
+
|
|
5
|
+
export const soundSubcommand: SubcommandDefinition = {
|
|
6
|
+
name: 'sound',
|
|
7
|
+
description: 'Test completion sound',
|
|
8
|
+
|
|
9
|
+
async handler() {
|
|
10
|
+
tui.info('Playing completion sound...');
|
|
11
|
+
await playSound();
|
|
12
|
+
tui.success('Sound played!');
|
|
13
|
+
},
|
|
14
|
+
};
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { createSubcommand } from '@/types';
|
|
2
|
+
import * as tui from '@/tui';
|
|
3
|
+
|
|
4
|
+
export const spinnerSubcommand = createSubcommand({
|
|
5
|
+
name: 'spinner',
|
|
6
|
+
description: 'Demo of spinner TUI component',
|
|
7
|
+
|
|
8
|
+
async handler() {
|
|
9
|
+
await tui.spinner('Loading configuration...', async () => {
|
|
10
|
+
await Bun.sleep(2000);
|
|
11
|
+
return { loaded: true };
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
await tui.spinner('Fetching data...', Bun.sleep(1500));
|
|
15
|
+
|
|
16
|
+
const result = await tui.spinner('Processing...', async () => {
|
|
17
|
+
await Bun.sleep(1000);
|
|
18
|
+
return 42;
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
tui.info(`Result: ${result}`);
|
|
22
|
+
|
|
23
|
+
// Spinner with progress tracking
|
|
24
|
+
await tui.spinner({
|
|
25
|
+
type: 'progress',
|
|
26
|
+
message: 'Downloading file...',
|
|
27
|
+
callback: async (progress) => {
|
|
28
|
+
for (let i = 0; i <= 100; i += 5) {
|
|
29
|
+
progress(i);
|
|
30
|
+
await Bun.sleep(100);
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
try {
|
|
36
|
+
await tui.spinner('This will fail...', async () => {
|
|
37
|
+
await Bun.sleep(1000);
|
|
38
|
+
throw new Error('Something went wrong!');
|
|
39
|
+
});
|
|
40
|
+
} catch (err) {
|
|
41
|
+
tui.error(`Caught error: ${err instanceof Error ? err.message : String(err)}`);
|
|
42
|
+
}
|
|
43
|
+
},
|
|
44
|
+
});
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { createSubcommand } from '@/types';
|
|
2
|
+
import { runSteps, stepSuccess, stepSkipped, stepError } from '@/steps';
|
|
3
|
+
|
|
4
|
+
export const stepsSubcommand = createSubcommand({
|
|
5
|
+
name: 'steps',
|
|
6
|
+
description: 'Demo of step progress UI component',
|
|
7
|
+
|
|
8
|
+
async handler() {
|
|
9
|
+
await runSteps([
|
|
10
|
+
{
|
|
11
|
+
label: 'Launching application...',
|
|
12
|
+
run: async () => {
|
|
13
|
+
await Bun.sleep(1500);
|
|
14
|
+
return stepSuccess();
|
|
15
|
+
},
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
label: 'Installing dependencies...',
|
|
19
|
+
run: async () => {
|
|
20
|
+
await Bun.sleep(2000);
|
|
21
|
+
return stepSuccess();
|
|
22
|
+
},
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
label: 'Checking for updates...',
|
|
26
|
+
run: async () => {
|
|
27
|
+
await Bun.sleep(800);
|
|
28
|
+
return stepSkipped('already up to date');
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
label: 'Registering service...',
|
|
33
|
+
run: async () => {
|
|
34
|
+
await Bun.sleep(1200);
|
|
35
|
+
return stepSuccess();
|
|
36
|
+
},
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
label: 'Building project...',
|
|
40
|
+
run: async () => {
|
|
41
|
+
await Bun.sleep(1800);
|
|
42
|
+
return stepSuccess();
|
|
43
|
+
},
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
type: 'progress',
|
|
47
|
+
label: 'Downloading packages...',
|
|
48
|
+
run: async (progress) => {
|
|
49
|
+
// Simulate download with progress updates
|
|
50
|
+
for (let i = 0; i <= 100; i += 10) {
|
|
51
|
+
progress(i);
|
|
52
|
+
await Bun.sleep(200);
|
|
53
|
+
}
|
|
54
|
+
return stepSuccess();
|
|
55
|
+
},
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
label: 'Deploying the app ...',
|
|
59
|
+
run: async () => {
|
|
60
|
+
await Bun.sleep(1200);
|
|
61
|
+
return stepError('something bad happened');
|
|
62
|
+
},
|
|
63
|
+
},
|
|
64
|
+
]);
|
|
65
|
+
},
|
|
66
|
+
});
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { createSubcommand } from '@/types';
|
|
2
|
+
|
|
3
|
+
export const versionSubcommand = createSubcommand({
|
|
4
|
+
name: 'version',
|
|
5
|
+
description: 'Example: Display version information (no auth required)',
|
|
6
|
+
|
|
7
|
+
async handler(ctx) {
|
|
8
|
+
const { logger } = ctx;
|
|
9
|
+
|
|
10
|
+
logger.info('Version 1.0.0');
|
|
11
|
+
logger.info('This command does not require authentication');
|
|
12
|
+
},
|
|
13
|
+
});
|
package/src/cmd/index.ts
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { readdir } from 'node:fs/promises';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
import type { CommandDefinition } from '../types';
|
|
4
|
+
|
|
5
|
+
export async function discoverCommands(): Promise<CommandDefinition[]> {
|
|
6
|
+
const cmdDir = join(import.meta.dir);
|
|
7
|
+
const entries = await readdir(cmdDir, { withFileTypes: true });
|
|
8
|
+
|
|
9
|
+
const commands: CommandDefinition[] = [];
|
|
10
|
+
|
|
11
|
+
for (const entry of entries) {
|
|
12
|
+
if (entry.isDirectory()) {
|
|
13
|
+
try {
|
|
14
|
+
const modulePath = join(cmdDir, entry.name, 'index.ts');
|
|
15
|
+
const module = await import(modulePath);
|
|
16
|
+
|
|
17
|
+
if (module.default || module.command) {
|
|
18
|
+
const cmd = module.default || module.command;
|
|
19
|
+
commands.push(cmd);
|
|
20
|
+
|
|
21
|
+
// Auto-create hidden top-level aliases for subcommands with toplevel: true
|
|
22
|
+
if (cmd.subcommands) {
|
|
23
|
+
for (const subcommand of cmd.subcommands) {
|
|
24
|
+
if (subcommand.toplevel) {
|
|
25
|
+
const alias: CommandDefinition = {
|
|
26
|
+
name: subcommand.name,
|
|
27
|
+
description: subcommand.description,
|
|
28
|
+
aliases: subcommand.aliases,
|
|
29
|
+
hidden: true,
|
|
30
|
+
requiresAuth: subcommand.requiresAuth,
|
|
31
|
+
schema: subcommand.schema,
|
|
32
|
+
handler: subcommand.handler,
|
|
33
|
+
};
|
|
34
|
+
commands.push(alias);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
} catch (error) {
|
|
40
|
+
console.warn(`Warning: Failed to load command from ${entry.name}:`, error);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return commands;
|
|
46
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# Profile Management
|
|
2
|
+
|
|
3
|
+
The profile command allows you to manage multiple configuration profiles for different environments (production, staging, local development, etc.).
|
|
4
|
+
|
|
5
|
+
## How It Works
|
|
6
|
+
|
|
7
|
+
Profiles are YAML files stored in `~/.config/agentuity/` that contain a `name` field. The CLI maintains a `profile` file in the same directory that stores the path to the currently selected profile.
|
|
8
|
+
|
|
9
|
+
When you run the CLI, it automatically loads the active profile's configuration.
|
|
10
|
+
|
|
11
|
+
## Commands
|
|
12
|
+
|
|
13
|
+
### `profile show` (alias: `current`)
|
|
14
|
+
|
|
15
|
+
Show the configuration of the currently active profile.
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
agentuity profile show
|
|
19
|
+
agentuity profile current
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Example output:
|
|
23
|
+
|
|
24
|
+
```
|
|
25
|
+
[INFO] Profile: /Users/username/.config/agentuity/production.yaml
|
|
26
|
+
|
|
27
|
+
name: production
|
|
28
|
+
auth:
|
|
29
|
+
api_key: ck_...
|
|
30
|
+
user_id: user_...
|
|
31
|
+
overrides:
|
|
32
|
+
api_url: "https://api.agentuity.com"
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
### `profile list` (alias: `ls`)
|
|
36
|
+
|
|
37
|
+
Lists all available profiles. The currently active profile is marked with a bullet (`•`).
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
agentuity profile list
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Example output:
|
|
44
|
+
|
|
45
|
+
```
|
|
46
|
+
[INFO] Available profiles:
|
|
47
|
+
[INFO] • production agentuity/production.yaml
|
|
48
|
+
[INFO] local agentuity/local.yaml
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### `profile use [name]` (alias: `select`)
|
|
52
|
+
|
|
53
|
+
Switch to a different profile by name. If no name is provided, shows the current profile and available options.
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
# Switch to a specific profile
|
|
57
|
+
agentuity profile use local
|
|
58
|
+
|
|
59
|
+
# Show current profile
|
|
60
|
+
agentuity profile use
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## Profile File Format
|
|
64
|
+
|
|
65
|
+
Each profile is a YAML file with at minimum a `name` field:
|
|
66
|
+
|
|
67
|
+
```yaml
|
|
68
|
+
name: 'production'
|
|
69
|
+
# other configuration...
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
The `name` field is extracted using the regex: `/\bname:\s+["']?([\w-_]+)["']?/`
|
|
73
|
+
|
|
74
|
+
## Implementation Details
|
|
75
|
+
|
|
76
|
+
- Profile selection is stored in `~/.config/agentuity/profile`
|
|
77
|
+
- Profile files must have `.yaml` extension
|
|
78
|
+
- Profile names must match: `^[\w-_]{3,}$` (3+ chars, alphanumeric, dashes, underscores)
|
|
79
|
+
- The config loader (`loadConfig()`) automatically uses the active profile
|
|
80
|
+
- If no profile is selected or the file doesn't exist, falls back to `config.yaml`
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { createSubcommand } from '@/types';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import {
|
|
4
|
+
fetchProfiles,
|
|
5
|
+
getDefaultConfigDir,
|
|
6
|
+
ensureConfigDir,
|
|
7
|
+
generateYAMLTemplate,
|
|
8
|
+
} from '@/config';
|
|
9
|
+
import { join } from 'node:path';
|
|
10
|
+
import { writeFile } from 'node:fs/promises';
|
|
11
|
+
import * as tui from '@/tui';
|
|
12
|
+
|
|
13
|
+
const PROFILE_NAME_REGEX = /^[\w_-]{3,}$/;
|
|
14
|
+
|
|
15
|
+
export const createCommand = createSubcommand({
|
|
16
|
+
name: 'create',
|
|
17
|
+
description: 'Create a new configuration profile',
|
|
18
|
+
aliases: ['new'],
|
|
19
|
+
schema: {
|
|
20
|
+
args: z
|
|
21
|
+
.object({
|
|
22
|
+
name: z
|
|
23
|
+
.string()
|
|
24
|
+
.min(3)
|
|
25
|
+
.regex(PROFILE_NAME_REGEX)
|
|
26
|
+
.describe('The name of the profile to create'),
|
|
27
|
+
})
|
|
28
|
+
.describe('Profile creation arguments'),
|
|
29
|
+
},
|
|
30
|
+
|
|
31
|
+
async handler(ctx) {
|
|
32
|
+
const { logger, args } = ctx;
|
|
33
|
+
const { name } = args;
|
|
34
|
+
|
|
35
|
+
const profiles = await fetchProfiles();
|
|
36
|
+
const existing = profiles.find((p) => p.name === name);
|
|
37
|
+
|
|
38
|
+
if (existing) {
|
|
39
|
+
return logger.fatal(`Profile "${name}" already exists at ${existing.filename}`);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
await ensureConfigDir();
|
|
43
|
+
const configDir = getDefaultConfigDir();
|
|
44
|
+
const filename = join(configDir, `${name}.yaml`);
|
|
45
|
+
|
|
46
|
+
const template = generateYAMLTemplate(name);
|
|
47
|
+
|
|
48
|
+
try {
|
|
49
|
+
await writeFile(filename, template, { flag: 'wx', mode: 0o600 });
|
|
50
|
+
tui.success(`Created profile "${name}" at ${filename}`);
|
|
51
|
+
} catch (error) {
|
|
52
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
53
|
+
const stack = error instanceof Error ? error.stack : undefined;
|
|
54
|
+
logger.fatal(`Failed to create profile: ${message}${stack ? `\n${stack}` : ''}`);
|
|
55
|
+
}
|
|
56
|
+
},
|
|
57
|
+
});
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { createSubcommand } from '@/types';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import { fetchProfiles } from '@/config';
|
|
4
|
+
import { unlink } from 'node:fs/promises';
|
|
5
|
+
import * as tui from '@/tui';
|
|
6
|
+
|
|
7
|
+
export const deleteCommand = createSubcommand({
|
|
8
|
+
name: 'delete',
|
|
9
|
+
description: 'Delete a configuration profile',
|
|
10
|
+
aliases: ['remove', 'rm'],
|
|
11
|
+
schema: {
|
|
12
|
+
args: z.object({
|
|
13
|
+
name: z.string().describe('The name of the profile to delete'),
|
|
14
|
+
}),
|
|
15
|
+
options: z.object({
|
|
16
|
+
confirm: z.boolean().optional().describe('Skip confirmation prompt'),
|
|
17
|
+
}),
|
|
18
|
+
},
|
|
19
|
+
|
|
20
|
+
async handler(ctx) {
|
|
21
|
+
const { logger, args, opts } = ctx;
|
|
22
|
+
const { name } = args;
|
|
23
|
+
|
|
24
|
+
const profiles = await fetchProfiles();
|
|
25
|
+
const profile = profiles.find((p) => p.name === name);
|
|
26
|
+
|
|
27
|
+
if (!profile) {
|
|
28
|
+
return logger.fatal(`Profile "${name}" not found`);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Ask for confirmation unless --confirm flag is passed
|
|
32
|
+
if (!opts?.confirm) {
|
|
33
|
+
const confirmed = await tui.confirm(`Delete profile "${name}"?`, false);
|
|
34
|
+
if (!confirmed) {
|
|
35
|
+
logger.info('Cancelled');
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
try {
|
|
41
|
+
await unlink(profile.filename);
|
|
42
|
+
tui.success(`Deleted profile "${name}"`);
|
|
43
|
+
if (profile.selected) {
|
|
44
|
+
tui.warning(
|
|
45
|
+
'The active profile was deleted. Use "profile use <name>" to select a new default.'
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
} catch (error) {
|
|
49
|
+
logger.fatal(`Failed to delete profile: ${error}`);
|
|
50
|
+
}
|
|
51
|
+
},
|
|
52
|
+
});
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { createCommand } from '@/types';
|
|
2
|
+
import { createCommand as createProfileCmd } from './create';
|
|
3
|
+
import { useCommand } from './use';
|
|
4
|
+
import { listCommand } from './list';
|
|
5
|
+
import { showCommand } from './show';
|
|
6
|
+
import { deleteCommand } from './delete';
|
|
7
|
+
|
|
8
|
+
export const command = createCommand({
|
|
9
|
+
name: 'profile',
|
|
10
|
+
description: 'Manage configuration profiles',
|
|
11
|
+
subcommands: [createProfileCmd, useCommand, listCommand, showCommand, deleteCommand],
|
|
12
|
+
});
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { SubcommandDefinition } from '@/types';
|
|
2
|
+
import { fetchProfiles } from '@/config';
|
|
3
|
+
import { basename, dirname } from 'node:path';
|
|
4
|
+
import * as tui from '@/tui';
|
|
5
|
+
|
|
6
|
+
export const listCommand: SubcommandDefinition = {
|
|
7
|
+
name: 'list',
|
|
8
|
+
description: 'List all available profiles',
|
|
9
|
+
aliases: ['ls'],
|
|
10
|
+
|
|
11
|
+
async handler() {
|
|
12
|
+
const profiles = await fetchProfiles();
|
|
13
|
+
|
|
14
|
+
if (profiles.length === 0) {
|
|
15
|
+
tui.info('No profiles found');
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
console.log('Available profiles:');
|
|
20
|
+
for (const profile of profiles) {
|
|
21
|
+
const marker = profile.selected ? '•' : ' ';
|
|
22
|
+
const name = tui.padRight(profile.name, 15, ' ');
|
|
23
|
+
const path = `${basename(dirname(profile.filename))}/${basename(profile.filename)}`;
|
|
24
|
+
console.log(`${marker} ${name} ${tui.muted(path)}`);
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
};
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { createSubcommand } from '@/types';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import { getProfile, fetchProfiles } from '@/config';
|
|
4
|
+
import { readFile } from 'node:fs/promises';
|
|
5
|
+
import * as tui from '@/tui';
|
|
6
|
+
|
|
7
|
+
export const showCommand = createSubcommand({
|
|
8
|
+
name: 'show',
|
|
9
|
+
description: 'Show the configuration of a profile (defaults to current)',
|
|
10
|
+
aliases: ['current'],
|
|
11
|
+
schema: {
|
|
12
|
+
args: z
|
|
13
|
+
.object({
|
|
14
|
+
name: z.string().optional().describe('Profile name to show (optional)'),
|
|
15
|
+
})
|
|
16
|
+
.describe('Profile show arguments'),
|
|
17
|
+
},
|
|
18
|
+
|
|
19
|
+
async handler(ctx) {
|
|
20
|
+
const { logger, args } = ctx;
|
|
21
|
+
|
|
22
|
+
try {
|
|
23
|
+
let profilePath: string;
|
|
24
|
+
|
|
25
|
+
if (args.name) {
|
|
26
|
+
// Find profile by name
|
|
27
|
+
const profiles = await fetchProfiles();
|
|
28
|
+
const profile = profiles.find((p) => p.name === args.name);
|
|
29
|
+
|
|
30
|
+
if (!profile) {
|
|
31
|
+
return logger.fatal(`Profile "${args.name}" not found`);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
profilePath = profile.filename;
|
|
35
|
+
} else {
|
|
36
|
+
// Use current profile
|
|
37
|
+
profilePath = await getProfile();
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const content = await readFile(profilePath, 'utf-8');
|
|
41
|
+
|
|
42
|
+
tui.info(`Profile: ${profilePath}`);
|
|
43
|
+
tui.newline();
|
|
44
|
+
|
|
45
|
+
console.log(content);
|
|
46
|
+
} catch (error) {
|
|
47
|
+
if (error instanceof Error) {
|
|
48
|
+
logger.fatal(`Failed to show profile: ${error.message}`);
|
|
49
|
+
} else {
|
|
50
|
+
logger.fatal('Failed to show profile');
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
},
|
|
54
|
+
});
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { createSubcommand } from '@/types';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import { fetchProfiles, saveProfile } from '@/config';
|
|
4
|
+
import * as tui from '@/tui';
|
|
5
|
+
|
|
6
|
+
export const useCommand = createSubcommand({
|
|
7
|
+
name: 'use',
|
|
8
|
+
description: 'Switch to a different configuration profile',
|
|
9
|
+
aliases: ['switch'],
|
|
10
|
+
schema: {
|
|
11
|
+
args: z.object({
|
|
12
|
+
name: z.string().describe('The name of the profile to use'),
|
|
13
|
+
}),
|
|
14
|
+
},
|
|
15
|
+
|
|
16
|
+
async handler(ctx) {
|
|
17
|
+
const { logger, args } = ctx;
|
|
18
|
+
const { name } = args;
|
|
19
|
+
|
|
20
|
+
const profiles = await fetchProfiles();
|
|
21
|
+
const profile = profiles.find((p) => p.name === name);
|
|
22
|
+
|
|
23
|
+
if (!profile) {
|
|
24
|
+
logger.fatal(`Profile "${name}" not found`);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
await saveProfile(profile!.filename);
|
|
28
|
+
tui.success(`Switched to profile "${name}"`);
|
|
29
|
+
},
|
|
30
|
+
});
|