@fiftth/fiftth-cli 0.1.0
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 +111 -0
- package/dist/api/client.d.ts +5 -0
- package/dist/api/client.d.ts.map +1 -0
- package/dist/api/client.js +20 -0
- package/dist/api/client.js.map +1 -0
- package/dist/commands/checkout.d.ts +2 -0
- package/dist/commands/checkout.d.ts.map +1 -0
- package/dist/commands/checkout.js +50 -0
- package/dist/commands/checkout.js.map +1 -0
- package/dist/commands/login.d.ts +12 -0
- package/dist/commands/login.d.ts.map +1 -0
- package/dist/commands/login.js +114 -0
- package/dist/commands/login.js.map +1 -0
- package/dist/commands/repo.d.ts +3 -0
- package/dist/commands/repo.d.ts.map +1 -0
- package/dist/commands/repo.js +86 -0
- package/dist/commands/repo.js.map +1 -0
- package/dist/commands/tasks.d.ts +2 -0
- package/dist/commands/tasks.d.ts.map +1 -0
- package/dist/commands/tasks.js +66 -0
- package/dist/commands/tasks.js.map +1 -0
- package/dist/commands/use.d.ts +6 -0
- package/dist/commands/use.d.ts.map +1 -0
- package/dist/commands/use.js +109 -0
- package/dist/commands/use.js.map +1 -0
- package/dist/config/configService.d.ts +7 -0
- package/dist/config/configService.d.ts.map +1 -0
- package/dist/config/configService.js +28 -0
- package/dist/config/configService.js.map +1 -0
- package/dist/context/runtimeContext.d.ts +14 -0
- package/dist/context/runtimeContext.d.ts.map +1 -0
- package/dist/context/runtimeContext.js +21 -0
- package/dist/context/runtimeContext.js.map +1 -0
- package/dist/git/gitService.d.ts +6 -0
- package/dist/git/gitService.d.ts.map +1 -0
- package/dist/git/gitService.js +24 -0
- package/dist/git/gitService.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +110 -0
- package/dist/index.js.map +1 -0
- package/dist/services/taskContext.d.ts +14 -0
- package/dist/services/taskContext.d.ts.map +1 -0
- package/dist/services/taskContext.js +15 -0
- package/dist/services/taskContext.js.map +1 -0
- package/dist/services/taskService.d.ts +12 -0
- package/dist/services/taskService.d.ts.map +1 -0
- package/dist/services/taskService.js +19 -0
- package/dist/services/taskService.js.map +1 -0
- package/dist/utils/api.d.ts +10 -0
- package/dist/utils/api.d.ts.map +1 -0
- package/dist/utils/api.js +25 -0
- package/dist/utils/api.js.map +1 -0
- package/dist/utils/config.d.ts +11 -0
- package/dist/utils/config.d.ts.map +1 -0
- package/dist/utils/config.js +37 -0
- package/dist/utils/config.js.map +1 -0
- package/dist/utils/ui.d.ts +19 -0
- package/dist/utils/ui.d.ts.map +1 -0
- package/dist/utils/ui.js +36 -0
- package/dist/utils/ui.js.map +1 -0
- package/package.json +56 -0
- package/src/api/client.ts +26 -0
- package/src/commands/checkout.ts +68 -0
- package/src/commands/login.ts +145 -0
- package/src/commands/repo.ts +113 -0
- package/src/commands/tasks.ts +83 -0
- package/src/commands/use.ts +149 -0
- package/src/config/configService.ts +40 -0
- package/src/context/runtimeContext.ts +42 -0
- package/src/git/gitService.ts +29 -0
- package/src/index.ts +133 -0
- package/src/services/taskContext.ts +32 -0
- package/src/services/taskService.ts +45 -0
- package/src/utils/api.ts +41 -0
- package/src/utils/config.ts +48 -0
- package/src/utils/ui.ts +46 -0
- package/tsconfig.json +18 -0
- package/vitest.config.ts +8 -0
package/README.md
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
# Fiftth CLI
|
|
2
|
+
|
|
3
|
+
The "fiftth" symphony of Software Engineering. Start orchestrating your engineering workflows.
|
|
4
|
+
|
|
5
|
+
Fiftth CLI helps your team authenticate, select a workspace, sync tasks, and checkout the right branch for each task-linked repository.
|
|
6
|
+
|
|
7
|
+
## Features
|
|
8
|
+
|
|
9
|
+
- Command aliases: `fiftth` and `ftt`
|
|
10
|
+
|
|
11
|
+
## Install
|
|
12
|
+
|
|
13
|
+
### Global from npm (when published)
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npm install -g fiftth
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
### Local development link
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
npm install
|
|
23
|
+
npm run build
|
|
24
|
+
npm link
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
After linking, both commands should work:
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
fiftth --help
|
|
31
|
+
ftt --help
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Quick Start
|
|
35
|
+
|
|
36
|
+
1. Authenticate
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
fiftth login
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
2. Choose workspace
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
fiftth use
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
3. Add local repositories
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
fiftth repo add api ~/dev/api
|
|
52
|
+
fiftth repo add mobile C:\Users\you\Desktop\Owner\repo
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
4. Select task and list linked repositories
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
fiftth tasks
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
5. Checkout task branch in a configured repository
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
fiftth checkout owner/repo
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## Commands
|
|
68
|
+
|
|
69
|
+
### Authentication
|
|
70
|
+
|
|
71
|
+
- `fiftth login`
|
|
72
|
+
- `fiftth login --token <token>`
|
|
73
|
+
- `fiftth login --host <host>`
|
|
74
|
+
- `fiftth login status`
|
|
75
|
+
- `fiftth login logout`
|
|
76
|
+
|
|
77
|
+
### Workspace
|
|
78
|
+
|
|
79
|
+
- `fiftth use`
|
|
80
|
+
- `fiftth use <workspace>`
|
|
81
|
+
- `fiftth use --list`
|
|
82
|
+
|
|
83
|
+
### Repository Registry
|
|
84
|
+
|
|
85
|
+
- `fiftth repo add <repoName> <path...>`
|
|
86
|
+
- `fiftth repo list`
|
|
87
|
+
- `fiftth repo remove <repoName>`
|
|
88
|
+
|
|
89
|
+
### Tasks and Checkout
|
|
90
|
+
|
|
91
|
+
- `fiftth tasks`
|
|
92
|
+
- `fiftth checkout <repoName>`
|
|
93
|
+
|
|
94
|
+
## Configuration
|
|
95
|
+
|
|
96
|
+
Config is saved to:
|
|
97
|
+
|
|
98
|
+
- `~/.fiftth/config.json`
|
|
99
|
+
|
|
100
|
+
Current defaults:
|
|
101
|
+
|
|
102
|
+
## Notes
|
|
103
|
+
|
|
104
|
+
- `checkout` requires:
|
|
105
|
+
- a selected task (`fiftth tasks`)
|
|
106
|
+
- a local repo mapping (`fiftth repo add ...`)
|
|
107
|
+
- a clean git working tree
|
|
108
|
+
|
|
109
|
+
## License
|
|
110
|
+
|
|
111
|
+
MIT
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/api/client.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,SAAS;IACxB,GAAG,CAAC,CAAC,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC,CAAA;CACrC;AAED,wBAAgB,eAAe,IAAI,SAAS,CAkB3C"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import axios from 'axios';
|
|
2
|
+
import { loadConfig } from '../utils/config.js';
|
|
3
|
+
export function createApiClient() {
|
|
4
|
+
const config = loadConfig();
|
|
5
|
+
const httpClient = axios.create({
|
|
6
|
+
baseURL: config.host,
|
|
7
|
+
headers: {
|
|
8
|
+
Authorization: `Bearer ${config.token}`,
|
|
9
|
+
'Content-Type': 'application/json',
|
|
10
|
+
'User-Agent': 'fiftth-cli/0.1.0',
|
|
11
|
+
},
|
|
12
|
+
});
|
|
13
|
+
return {
|
|
14
|
+
async get(endpoint) {
|
|
15
|
+
const response = await httpClient.get(endpoint);
|
|
16
|
+
return response.data;
|
|
17
|
+
},
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/api/client.ts"],"names":[],"mappings":"AAAA,OAAO,KAA6B,MAAM,OAAO,CAAA;AACjD,OAAO,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAA;AAM/C,MAAM,UAAU,eAAe;IAC7B,MAAM,MAAM,GAAG,UAAU,EAAE,CAAA;IAE3B,MAAM,UAAU,GAAkB,KAAK,CAAC,MAAM,CAAC;QAC7C,OAAO,EAAE,MAAM,CAAC,IAAI;QACpB,OAAO,EAAE;YACP,aAAa,EAAE,UAAU,MAAM,CAAC,KAAK,EAAE;YACvC,cAAc,EAAE,kBAAkB;YAClC,YAAY,EAAE,kBAAkB;SACjC;KACF,CAAC,CAAA;IAEF,OAAO;QACL,KAAK,CAAC,GAAG,CAAI,QAAgB;YAC3B,MAAM,QAAQ,GAAG,MAAM,UAAU,CAAC,GAAG,CAAI,QAAQ,CAAC,CAAA;YAClD,OAAO,QAAQ,CAAC,IAAI,CAAA;QACtB,CAAC;KACF,CAAA;AACH,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"checkout.d.ts","sourceRoot":"","sources":["../../src/commands/checkout.ts"],"names":[],"mappings":"AAYA,wBAAsB,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAuDrE"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import { getRepoPath } from '../config/configService.js';
|
|
3
|
+
import { getRuntimeSelectedTask } from '../context/runtimeContext.js';
|
|
4
|
+
import { checkGitRepo, checkRepoExists, checkUncommittedChanges, checkoutBranch, fetch, } from '../git/gitService.js';
|
|
5
|
+
import { cmd, fail, info, muted, ok, section } from '../utils/ui.js';
|
|
6
|
+
export async function checkoutCommand(repoName) {
|
|
7
|
+
const selectedTask = getRuntimeSelectedTask();
|
|
8
|
+
if (!selectedTask) {
|
|
9
|
+
console.log(`\n${fail('No task selected.')}\n`);
|
|
10
|
+
console.log(`${muted(`Run ${cmd('fiftth tasks')} to select one.`)}\n`);
|
|
11
|
+
process.exit(1);
|
|
12
|
+
}
|
|
13
|
+
const taskRepository = selectedTask.repositories.find((repository) => repository.fullName === repoName);
|
|
14
|
+
if (!taskRepository) {
|
|
15
|
+
console.log(`\n${fail('Repository not linked to the selected task.')}\n`);
|
|
16
|
+
console.log(`${section('Available repositories')}\n`);
|
|
17
|
+
for (const repository of selectedTask.repositories) {
|
|
18
|
+
console.log(` ${muted('•')} ${repository.fullName}`);
|
|
19
|
+
}
|
|
20
|
+
console.log();
|
|
21
|
+
process.exit(1);
|
|
22
|
+
}
|
|
23
|
+
const repoPath = getRepoPath(repoName);
|
|
24
|
+
if (!repoPath) {
|
|
25
|
+
console.log(`\n${fail('Repository not configured locally.')}\n`);
|
|
26
|
+
console.log(`${muted(`Run ${cmd('fiftth repo add <repoName> <path>')}`)}\n`);
|
|
27
|
+
process.exit(1);
|
|
28
|
+
}
|
|
29
|
+
if (!checkRepoExists(repoPath)) {
|
|
30
|
+
console.log(`\n${fail('Repository path not found.')}\n`);
|
|
31
|
+
process.exit(1);
|
|
32
|
+
}
|
|
33
|
+
if (!checkGitRepo(repoPath)) {
|
|
34
|
+
console.log(`\n${fail('The configured path is not a git repository.')}\n`);
|
|
35
|
+
process.exit(1);
|
|
36
|
+
}
|
|
37
|
+
const hasUncommittedChanges = await checkUncommittedChanges(repoPath);
|
|
38
|
+
if (hasUncommittedChanges) {
|
|
39
|
+
console.log(`\n${fail('Uncommitted changes detected.')}\n`);
|
|
40
|
+
console.log(`${info('Please commit or stash changes before switching branches.')}\n`);
|
|
41
|
+
process.exit(1);
|
|
42
|
+
}
|
|
43
|
+
console.log(`\n${section('Syncing repository state')}\n`);
|
|
44
|
+
await fetch(repoPath);
|
|
45
|
+
await checkoutBranch(repoPath, taskRepository.branch);
|
|
46
|
+
console.log(ok(`Repository: ${chalk.bold(repoName)}`));
|
|
47
|
+
console.log(ok(`Branch: ${chalk.bold(taskRepository.branch)}`));
|
|
48
|
+
console.log(`${ok('Checkout complete')}\n`);
|
|
49
|
+
}
|
|
50
|
+
//# sourceMappingURL=checkout.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"checkout.js","sourceRoot":"","sources":["../../src/commands/checkout.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { input } from '@inquirer/prompts';
|
|
2
|
+
interface LoginOptions {
|
|
3
|
+
token?: string;
|
|
4
|
+
host?: string;
|
|
5
|
+
}
|
|
6
|
+
declare function normalizeToken(rawToken: string): string;
|
|
7
|
+
export declare function loginCommand(options: LoginOptions): Promise<void>;
|
|
8
|
+
export declare function logoutCommand(): Promise<void>;
|
|
9
|
+
export declare function loginStatusCommand(): Promise<void>;
|
|
10
|
+
export { input };
|
|
11
|
+
export { normalizeToken };
|
|
12
|
+
//# sourceMappingURL=login.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"login.d.ts","sourceRoot":"","sources":["../../src/commands/login.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,EAAqB,MAAM,mBAAmB,CAAA;AAK5D,UAAU,YAAY;IACpB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,IAAI,CAAC,EAAE,MAAM,CAAA;CACd;AAED,iBAAS,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAEhD;AAyBD,wBAAsB,YAAY,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAsDvE;AAED,wBAAsB,aAAa,IAAI,OAAO,CAAC,IAAI,CAAC,CAsBnD;AAED,wBAAsB,kBAAkB,IAAI,OAAO,CAAC,IAAI,CAAC,CAuBxD;AAED,OAAO,EAAE,KAAK,EAAE,CAAA;AAChB,OAAO,EAAE,cAAc,EAAE,CAAA"}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import { input, password, confirm } from '@inquirer/prompts';
|
|
3
|
+
import ora from 'ora';
|
|
4
|
+
import { loadConfig, saveConfig } from '../utils/config.js';
|
|
5
|
+
import { cmd, fail, info, kv, muted, ok, section, title } from '../utils/ui.js';
|
|
6
|
+
function normalizeToken(rawToken) {
|
|
7
|
+
return rawToken.trim().replace(/^Bearer\s+/i, '');
|
|
8
|
+
}
|
|
9
|
+
async function validateToken(host, token) {
|
|
10
|
+
try {
|
|
11
|
+
const response = await fetch(`${host}/cli-tokens`, {
|
|
12
|
+
headers: {
|
|
13
|
+
Authorization: `Bearer ${token}`,
|
|
14
|
+
'User-Agent': 'fiftth-cli/0.1.0',
|
|
15
|
+
},
|
|
16
|
+
});
|
|
17
|
+
if (!response.ok) {
|
|
18
|
+
return false;
|
|
19
|
+
}
|
|
20
|
+
const body = (await response.json().catch(() => null));
|
|
21
|
+
return body?.valid === true;
|
|
22
|
+
}
|
|
23
|
+
catch {
|
|
24
|
+
return false;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
export async function loginCommand(options) {
|
|
28
|
+
const config = loadConfig();
|
|
29
|
+
const host = options.host ?? config.host;
|
|
30
|
+
console.log(`\n${title('Welcome to Fiftth')}\n`);
|
|
31
|
+
console.log(muted('Orchestrate tasks, repos and agent workflows from your terminal.\n'));
|
|
32
|
+
if (config.token && !options.token) {
|
|
33
|
+
const alreadyLoggedIn = await confirm({
|
|
34
|
+
message: chalk.yellow('You are already logged in. Validate login again?'),
|
|
35
|
+
default: false,
|
|
36
|
+
});
|
|
37
|
+
if (!alreadyLoggedIn) {
|
|
38
|
+
console.log(`\n${muted('No changes made.')}\n`);
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
let token;
|
|
43
|
+
if (options.token) {
|
|
44
|
+
token = normalizeToken(options.token);
|
|
45
|
+
}
|
|
46
|
+
else {
|
|
47
|
+
console.log(muted(`Generate an access token at: ${cmd(`${host}/cli-tokens`)}\n`));
|
|
48
|
+
token = await password({
|
|
49
|
+
message: 'Paste your access token:',
|
|
50
|
+
mask: '*',
|
|
51
|
+
});
|
|
52
|
+
if (!token || token.trim() === '') {
|
|
53
|
+
console.error(`\n${fail('Token cannot be empty.')}\n`);
|
|
54
|
+
process.exit(1);
|
|
55
|
+
}
|
|
56
|
+
token = normalizeToken(token);
|
|
57
|
+
}
|
|
58
|
+
const spinner = ora('Validating credentials...').start();
|
|
59
|
+
const valid = await validateToken(host, token);
|
|
60
|
+
if (!valid) {
|
|
61
|
+
spinner.fail(fail('Authentication failed.'));
|
|
62
|
+
console.error(`\n${muted('Make sure your token is correct and has not expired.')}\n`);
|
|
63
|
+
process.exit(1);
|
|
64
|
+
}
|
|
65
|
+
spinner.succeed(ok('Authentication validated.'));
|
|
66
|
+
saveConfig({ ...config, token, host });
|
|
67
|
+
console.log(`\n${section('Session ready')}`);
|
|
68
|
+
console.log(kv('Host', cmd(host)));
|
|
69
|
+
console.log(kv('Next', `run ${cmd('fiftth use <workspace>')}`));
|
|
70
|
+
console.log();
|
|
71
|
+
}
|
|
72
|
+
export async function logoutCommand() {
|
|
73
|
+
const config = loadConfig();
|
|
74
|
+
if (!config.token) {
|
|
75
|
+
console.log(`\n${info('You are not currently logged in.')}\n`);
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
const confirmed = await confirm({
|
|
79
|
+
message: 'Are you sure you want to log out?',
|
|
80
|
+
default: false,
|
|
81
|
+
});
|
|
82
|
+
if (!confirmed) {
|
|
83
|
+
console.log(`\n${muted('Logout cancelled.')}\n`);
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
const { token: _token, ...rest } = config;
|
|
87
|
+
saveConfig(rest);
|
|
88
|
+
console.log(`\n${ok('Logged out successfully.')}\n`);
|
|
89
|
+
}
|
|
90
|
+
export async function loginStatusCommand() {
|
|
91
|
+
const config = loadConfig();
|
|
92
|
+
if (!config.token) {
|
|
93
|
+
console.log(`\n${info('Not logged in.')}\n`);
|
|
94
|
+
console.log(`${muted(`Run ${cmd('fiftth login')} to authenticate.`)}\n`);
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
const spinner = ora('Checking authentication status...').start();
|
|
98
|
+
const valid = await validateToken(config.host, config.token);
|
|
99
|
+
if (valid) {
|
|
100
|
+
spinner.succeed(ok('Authenticated'));
|
|
101
|
+
console.log(`\n${kv('Host', cmd(config.host))}`);
|
|
102
|
+
if (config.workspace) {
|
|
103
|
+
console.log(kv('Workspace', cmd(config.workspace)));
|
|
104
|
+
}
|
|
105
|
+
console.log();
|
|
106
|
+
}
|
|
107
|
+
else {
|
|
108
|
+
spinner.fail(fail('Token is invalid or expired.'));
|
|
109
|
+
console.log(`\n${muted(`Run ${cmd('fiftth login')} to authenticate again.`)}\n`);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
export { input };
|
|
113
|
+
export { normalizeToken };
|
|
114
|
+
//# sourceMappingURL=login.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"login.js","sourceRoot":"","sources":["../../src/commands/login.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"repo.d.ts","sourceRoot":"","sources":["../../src/commands/repo.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AA2FnC,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAiB3D"}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import os from 'os';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
import chalk from 'chalk';
|
|
5
|
+
import { addRepository, getRepositories, removeRepository, } from '../config/configService.js';
|
|
6
|
+
import { cmd, fail, info, kv, muted, ok, section } from '../utils/ui.js';
|
|
7
|
+
function expandHomeDirectory(inputPath) {
|
|
8
|
+
if (inputPath === '~') {
|
|
9
|
+
return os.homedir();
|
|
10
|
+
}
|
|
11
|
+
if (inputPath.startsWith('~/') || inputPath.startsWith('~\\')) {
|
|
12
|
+
return path.join(os.homedir(), inputPath.slice(2));
|
|
13
|
+
}
|
|
14
|
+
return inputPath;
|
|
15
|
+
}
|
|
16
|
+
function resolveRepoPath(inputPath) {
|
|
17
|
+
const expandedPath = expandHomeDirectory(inputPath);
|
|
18
|
+
return path.resolve(expandedPath);
|
|
19
|
+
}
|
|
20
|
+
function validateRepositoryPath(repoPath) {
|
|
21
|
+
if (!fs.existsSync(repoPath) || !fs.statSync(repoPath).isDirectory()) {
|
|
22
|
+
console.error(`\n${fail('Path not found.')}\n`);
|
|
23
|
+
process.exit(1);
|
|
24
|
+
}
|
|
25
|
+
const gitPath = path.join(repoPath, '.git');
|
|
26
|
+
if (!fs.existsSync(gitPath) || !fs.statSync(gitPath).isDirectory()) {
|
|
27
|
+
console.error(`\n${fail('The provided path is not a git repository.')}\n`);
|
|
28
|
+
process.exit(1);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
function addRepoCommand(repoName, repoPathInput) {
|
|
32
|
+
const normalizedPathInput = Array.isArray(repoPathInput)
|
|
33
|
+
? repoPathInput.join(' ')
|
|
34
|
+
: repoPathInput;
|
|
35
|
+
const absolutePath = resolveRepoPath(normalizedPathInput);
|
|
36
|
+
validateRepositoryPath(absolutePath);
|
|
37
|
+
const { overwritten } = addRepository(repoName, absolutePath);
|
|
38
|
+
if (overwritten) {
|
|
39
|
+
console.log(chalk.yellow(`${info('Repository already exists. Overwriting mapping.')}`));
|
|
40
|
+
}
|
|
41
|
+
console.log(`\n${ok('Repository registered')}\n`);
|
|
42
|
+
console.log(kv('Repo', repoName));
|
|
43
|
+
console.log(kv('Path', cmd(absolutePath)));
|
|
44
|
+
console.log();
|
|
45
|
+
}
|
|
46
|
+
function listRepoCommand() {
|
|
47
|
+
const repositories = getRepositories();
|
|
48
|
+
const entries = Object.entries(repositories);
|
|
49
|
+
if (entries.length === 0) {
|
|
50
|
+
console.log(`\n${info('No repositories configured yet.')}\n`);
|
|
51
|
+
console.log(`${muted(`Use ${cmd('fiftth repo add <repoName> <path>')}`)}\n`);
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
const longestNameLength = Math.max(...entries.map(([name]) => name.length));
|
|
55
|
+
console.log(`\n${section('Configured repositories')}\n`);
|
|
56
|
+
for (const [repoName, repoPath] of entries) {
|
|
57
|
+
console.log(`${repoName.padEnd(longestNameLength, ' ')} ${muted('->')} ${cmd(repoPath)}`);
|
|
58
|
+
}
|
|
59
|
+
console.log();
|
|
60
|
+
}
|
|
61
|
+
function removeRepoCommand(repoName) {
|
|
62
|
+
const removed = removeRepository(repoName);
|
|
63
|
+
if (!removed) {
|
|
64
|
+
console.error(`\n${fail(`Repository '${repoName}' is not configured.`)}\n`);
|
|
65
|
+
process.exit(1);
|
|
66
|
+
}
|
|
67
|
+
console.log(`\n${ok('Repository removed')}\n`);
|
|
68
|
+
console.log(kv('Repo', repoName));
|
|
69
|
+
console.log();
|
|
70
|
+
}
|
|
71
|
+
export function registerRepoCommands(program) {
|
|
72
|
+
const repoCmd = program.command('repo').description('Manage local repository mappings');
|
|
73
|
+
repoCmd
|
|
74
|
+
.command('add <repoName> <path...>')
|
|
75
|
+
.description('Register a local repository path')
|
|
76
|
+
.action(addRepoCommand);
|
|
77
|
+
repoCmd
|
|
78
|
+
.command('list')
|
|
79
|
+
.description('List all configured repositories')
|
|
80
|
+
.action(listRepoCommand);
|
|
81
|
+
repoCmd
|
|
82
|
+
.command('remove <repoName>')
|
|
83
|
+
.description('Remove a configured repository')
|
|
84
|
+
.action(removeRepoCommand);
|
|
85
|
+
}
|
|
86
|
+
//# sourceMappingURL=repo.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"repo.js","sourceRoot":"","sources":["../../src/commands/repo.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,IAAI,CAAA;AACnB,OAAO,EAAE,MAAM,IAAI,CAAA;AACnB,OAAO,IAAI,MAAM,MAAM,CAAA;AACvB,OAAO,KAAK,MAAM,OAAO,CAAA;AAEzB,OAAO,EACL,aAAa,EACb,eAAe,EACf,gBAAgB,GACjB,MAAM,4BAA4B,CAAA;AACnC,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAA;AAExE,SAAS,mBAAmB,CAAC,SAAiB;IAC5C,IAAI,SAAS,KAAK,GAAG,EAAE,CAAC;QACtB,OAAO,EAAE,CAAC,OAAO,EAAE,CAAA;IACrB,CAAC;IAED,IAAI,SAAS,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,SAAS,CAAC,UAAU,CAAC,KAAK,CAAC,EAAE,CAAC;QAC9D,OAAO,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAA;IACpD,CAAC;IAED,OAAO,SAAS,CAAA;AAClB,CAAC;AAED,SAAS,eAAe,CAAC,SAAiB;IACxC,MAAM,YAAY,GAAG,mBAAmB,CAAC,SAAS,CAAC,CAAA;IACnD,OAAO,IAAI,CAAC,OAAO,CAAC,YAAY,CAAC,CAAA;AACnC,CAAC;AAED,SAAS,sBAAsB,CAAC,QAAgB;IAC9C,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,WAAW,EAAE,EAAE,CAAC;QACrE,OAAO,CAAC,KAAK,CAAC,KAAK,IAAI,CAAC,iBAAiB,CAAC,IAAI,CAAC,CAAA;QAC/C,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;IACjB,CAAC;IAED,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAA;IAE3C,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,WAAW,EAAE,EAAE,CAAC;QACnE,OAAO,CAAC,KAAK,CAAC,KAAK,IAAI,CAAC,4CAA4C,CAAC,IAAI,CAAC,CAAA;QAC1E,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;IACjB,CAAC;AACH,CAAC;AAED,SAAS,cAAc,CAAC,QAAgB,EAAE,aAAgC;IACxE,MAAM,mBAAmB,GAAG,KAAK,CAAC,OAAO,CAAC,aAAa,CAAC;QACtD,CAAC,CAAC,aAAa,CAAC,IAAI,CAAC,GAAG,CAAC;QACzB,CAAC,CAAC,aAAa,CAAA;IAEjB,MAAM,YAAY,GAAG,eAAe,CAAC,mBAAmB,CAAC,CAAA;IACzD,sBAAsB,CAAC,YAAY,CAAC,CAAA;IAEpC,MAAM,EAAE,WAAW,EAAE,GAAG,aAAa,CAAC,QAAQ,EAAE,YAAY,CAAC,CAAA;IAE7D,IAAI,WAAW,EAAE,CAAC;QAChB,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,IAAI,CAAC,iDAAiD,CAAC,EAAE,CAAC,CAAC,CAAA;IACzF,CAAC;IAED,OAAO,CAAC,GAAG,CAAC,KAAK,EAAE,CAAC,uBAAuB,CAAC,IAAI,CAAC,CAAA;IACjD,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC,CAAA;IACjC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,MAAM,EAAE,GAAG,CAAC,YAAY,CAAC,CAAC,CAAC,CAAA;IAC1C,OAAO,CAAC,GAAG,EAAE,CAAA;AACf,CAAC;AAED,SAAS,eAAe;IACtB,MAAM,YAAY,GAAG,eAAe,EAAE,CAAA;IACtC,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC,YAAY,CAAC,CAAA;IAE5C,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACzB,OAAO,CAAC,GAAG,CAAC,KAAK,IAAI,CAAC,iCAAiC,CAAC,IAAI,CAAC,CAAA;QAC7D,OAAO,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC,OAAO,GAAG,CAAC,mCAAmC,CAAC,EAAE,CAAC,IAAI,CAAC,CAAA;QAC5E,OAAM;IACR,CAAC;IAED,MAAM,iBAAiB,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAA;IAE3E,OAAO,CAAC,GAAG,CAAC,KAAK,OAAO,CAAC,yBAAyB,CAAC,IAAI,CAAC,CAAA;IACxD,KAAK,MAAM,CAAC,QAAQ,EAAE,QAAQ,CAAC,IAAI,OAAO,EAAE,CAAC;QAC3C,OAAO,CAAC,GAAG,CAAC,GAAG,QAAQ,CAAC,MAAM,CAAC,iBAAiB,EAAE,GAAG,CAAC,IAAI,KAAK,CAAC,IAAI,CAAC,IAAI,GAAG,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAA;IAC3F,CAAC;IACD,OAAO,CAAC,GAAG,EAAE,CAAA;AACf,CAAC;AAED,SAAS,iBAAiB,CAAC,QAAgB;IACzC,MAAM,OAAO,GAAG,gBAAgB,CAAC,QAAQ,CAAC,CAAA;IAE1C,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,OAAO,CAAC,KAAK,CAAC,KAAK,IAAI,CAAC,eAAe,QAAQ,sBAAsB,CAAC,IAAI,CAAC,CAAA;QAC3E,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;IACjB,CAAC;IAED,OAAO,CAAC,GAAG,CAAC,KAAK,EAAE,CAAC,oBAAoB,CAAC,IAAI,CAAC,CAAA;IAC9C,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC,CAAA;IACjC,OAAO,CAAC,GAAG,EAAE,CAAA;AACf,CAAC;AAED,MAAM,UAAU,oBAAoB,CAAC,OAAgB;IACnD,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,WAAW,CAAC,kCAAkC,CAAC,CAAA;IAEvF,OAAO;SACJ,OAAO,CAAC,0BAA0B,CAAC;SACnC,WAAW,CAAC,kCAAkC,CAAC;SAC/C,MAAM,CAAC,cAAc,CAAC,CAAA;IAEzB,OAAO;SACJ,OAAO,CAAC,MAAM,CAAC;SACf,WAAW,CAAC,kCAAkC,CAAC;SAC/C,MAAM,CAAC,eAAe,CAAC,CAAA;IAE1B,OAAO;SACJ,OAAO,CAAC,mBAAmB,CAAC;SAC5B,WAAW,CAAC,gCAAgC,CAAC;SAC7C,MAAM,CAAC,iBAAiB,CAAC,CAAA;AAC9B,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tasks.d.ts","sourceRoot":"","sources":["../../src/commands/tasks.ts"],"names":[],"mappings":"AAiBA,wBAAsB,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC,CAiElD"}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import inquirer from 'inquirer';
|
|
3
|
+
import { loadConfig } from '../utils/config.js';
|
|
4
|
+
import { fetchActiveTasks } from '../services/taskService.js';
|
|
5
|
+
import { setRuntimeSelectedTask } from '../context/runtimeContext.js';
|
|
6
|
+
import { cmd, fail, info, kv, muted, ok, section } from '../utils/ui.js';
|
|
7
|
+
function printTaskRepositories(task) {
|
|
8
|
+
console.log(`\n${section(`Linked repositories for: ${task.title}`)}\n`);
|
|
9
|
+
for (const repository of task.repositories) {
|
|
10
|
+
console.log(kv('Repository', repository.fullName));
|
|
11
|
+
console.log(` ${muted('branch')} ${chalk.bold(repository.branch)}`);
|
|
12
|
+
console.log('');
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
export async function tasksCommand() {
|
|
16
|
+
const config = loadConfig();
|
|
17
|
+
if (!config.token) {
|
|
18
|
+
console.error(`\n${fail('Not authenticated.')} ${muted(`Run ${cmd('fiftth login')} first.`)}\n`);
|
|
19
|
+
process.exit(1);
|
|
20
|
+
}
|
|
21
|
+
if (!config.workspaceId) {
|
|
22
|
+
console.error(`\n${fail('Workspace not selected.')} ${muted(`Run ${cmd('fiftth use')} first.`)}\n`);
|
|
23
|
+
process.exit(1);
|
|
24
|
+
}
|
|
25
|
+
let tasks;
|
|
26
|
+
try {
|
|
27
|
+
tasks = await fetchActiveTasks(config.workspaceId);
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
console.error(`\n${fail('Failed to fetch tasks from API.')}\n`);
|
|
31
|
+
process.exit(1);
|
|
32
|
+
}
|
|
33
|
+
if (tasks.length === 0) {
|
|
34
|
+
console.log(`\n${info('No active tasks found.')}\n`);
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
console.log(`\n${section(`Active tasks in ${config.workspace ?? 'workspace'}`)}\n`);
|
|
38
|
+
const { selectedTaskId } = await inquirer.prompt([
|
|
39
|
+
{
|
|
40
|
+
type: 'list',
|
|
41
|
+
name: 'selectedTaskId',
|
|
42
|
+
message: 'Choose a task',
|
|
43
|
+
choices: tasks.map((task) => ({
|
|
44
|
+
name: task.title,
|
|
45
|
+
value: task.id,
|
|
46
|
+
})),
|
|
47
|
+
},
|
|
48
|
+
]);
|
|
49
|
+
const selectedTask = tasks.find((task) => task.id === selectedTaskId);
|
|
50
|
+
if (!selectedTask) {
|
|
51
|
+
console.error(`\n${fail('No active tasks found.')}\n`);
|
|
52
|
+
process.exit(1);
|
|
53
|
+
}
|
|
54
|
+
setRuntimeSelectedTask({
|
|
55
|
+
taskId: selectedTask.id,
|
|
56
|
+
title: selectedTask.title,
|
|
57
|
+
repositories: selectedTask.repositories,
|
|
58
|
+
});
|
|
59
|
+
console.log(`\n${ok(`Task selected: ${chalk.bold(selectedTask.title)}`)}`);
|
|
60
|
+
if (selectedTask.repositories.length === 0) {
|
|
61
|
+
console.log(`${info('This task has no linked repositories.')}\n`);
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
printTaskRepositories(selectedTask);
|
|
65
|
+
}
|
|
66
|
+
//# sourceMappingURL=tasks.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tasks.js","sourceRoot":"","sources":["../../src/commands/tasks.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use.d.ts","sourceRoot":"","sources":["../../src/commands/use.ts"],"names":[],"mappings":"AAOA,UAAU,UAAU;IAClB,IAAI,CAAC,EAAE,OAAO,CAAA;CACf;AAED,wBAAsB,UAAU,CAC9B,SAAS,EAAE,MAAM,GAAG,SAAS,EAC7B,OAAO,EAAE,UAAU,GAClB,OAAO,CAAC,IAAI,CAAC,CAsBf"}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import { select } from '@inquirer/prompts';
|
|
3
|
+
import ora from 'ora';
|
|
4
|
+
import { loadConfig, saveConfig } from '../utils/config.js';
|
|
5
|
+
import { createApiClient } from '../utils/api.js';
|
|
6
|
+
import { cmd, fail, info, kv, muted, ok, section } from '../utils/ui.js';
|
|
7
|
+
export async function useCommand(workspace, options) {
|
|
8
|
+
const config = loadConfig();
|
|
9
|
+
if (!config.token) {
|
|
10
|
+
console.error(`\n${fail('Not authenticated.')} ${muted(`Run ${cmd('fiftth login')} first.`)}\n`);
|
|
11
|
+
process.exit(1);
|
|
12
|
+
}
|
|
13
|
+
const client = createApiClient();
|
|
14
|
+
if (options.list) {
|
|
15
|
+
await listWorkspaces(client);
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
if (workspace) {
|
|
19
|
+
await switchWorkspace(workspace, config, client);
|
|
20
|
+
}
|
|
21
|
+
else {
|
|
22
|
+
await interactiveSelectWorkspace(config, client);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
async function listWorkspaces(client) {
|
|
26
|
+
const spinner = ora('Fetching workspaces…').start();
|
|
27
|
+
let workspaces;
|
|
28
|
+
try {
|
|
29
|
+
workspaces = await client.getWorkspaces();
|
|
30
|
+
}
|
|
31
|
+
catch (err) {
|
|
32
|
+
spinner.fail(fail('Failed to fetch workspaces.'));
|
|
33
|
+
console.error(`\n${muted(err instanceof Error ? err.message : String(err))}\n`);
|
|
34
|
+
process.exit(1);
|
|
35
|
+
}
|
|
36
|
+
spinner.stop();
|
|
37
|
+
if (workspaces.length === 0) {
|
|
38
|
+
console.log(`\n${info('No workspaces found.')}\n`);
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
const config = loadConfig();
|
|
42
|
+
console.log(`\n${section('Available workspaces')}\n`);
|
|
43
|
+
for (const ws of workspaces) {
|
|
44
|
+
const active = ws.slug === config.workspace;
|
|
45
|
+
const marker = active ? chalk.green('✓') : chalk.dim('·');
|
|
46
|
+
const name = active ? chalk.bold(ws.name) : ws.name;
|
|
47
|
+
console.log(` ${marker} ${name} ${muted(ws.slug)}`);
|
|
48
|
+
}
|
|
49
|
+
console.log();
|
|
50
|
+
}
|
|
51
|
+
async function switchWorkspace(slug, config, client) {
|
|
52
|
+
const spinner = ora('Fetching workspaces...').start();
|
|
53
|
+
let workspaces;
|
|
54
|
+
try {
|
|
55
|
+
workspaces = await client.getWorkspaces();
|
|
56
|
+
}
|
|
57
|
+
catch (err) {
|
|
58
|
+
spinner.fail(fail('Failed to fetch workspaces.'));
|
|
59
|
+
console.error(`\n${muted(err instanceof Error ? err.message : String(err))}\n`);
|
|
60
|
+
process.exit(1);
|
|
61
|
+
}
|
|
62
|
+
spinner.stop();
|
|
63
|
+
const match = workspaces.find((ws) => ws.slug === slug || ws.name === slug);
|
|
64
|
+
if (!match) {
|
|
65
|
+
console.error(`\n${fail(`Workspace ${chalk.bold(slug)} not found.`)}\n`);
|
|
66
|
+
console.log(`${muted(`Run ${cmd('fiftth use --list')} to see available workspaces.`)}\n`);
|
|
67
|
+
process.exit(1);
|
|
68
|
+
}
|
|
69
|
+
saveConfig({
|
|
70
|
+
...config,
|
|
71
|
+
workspace: match.slug,
|
|
72
|
+
workspaceId: match.id,
|
|
73
|
+
});
|
|
74
|
+
console.log(`\n${ok(`Now using workspace ${chalk.bold(match.name)} ${muted(`(${match.slug})`)}`)}`);
|
|
75
|
+
console.log(`${kv('Workspace ID', match.id)}\n`);
|
|
76
|
+
}
|
|
77
|
+
async function interactiveSelectWorkspace(config, client) {
|
|
78
|
+
const spinner = ora('Fetching workspaces...').start();
|
|
79
|
+
let workspaces;
|
|
80
|
+
try {
|
|
81
|
+
workspaces = await client.getWorkspaces();
|
|
82
|
+
}
|
|
83
|
+
catch (err) {
|
|
84
|
+
spinner.fail(fail('Failed to fetch workspaces.'));
|
|
85
|
+
console.error(`\n${muted(err instanceof Error ? err.message : String(err))}\n`);
|
|
86
|
+
process.exit(1);
|
|
87
|
+
}
|
|
88
|
+
spinner.stop();
|
|
89
|
+
if (workspaces.length === 0) {
|
|
90
|
+
console.log(`\n${info('No workspaces found.')}\n`);
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
const chosen = await select({
|
|
94
|
+
message: 'Select a workspace',
|
|
95
|
+
choices: workspaces.map((ws) => ({
|
|
96
|
+
value: ws.slug,
|
|
97
|
+
name: `${ws.name} ${chalk.dim(ws.slug)}`,
|
|
98
|
+
})),
|
|
99
|
+
default: config.workspace,
|
|
100
|
+
});
|
|
101
|
+
const match = workspaces.find((ws) => ws.slug === chosen);
|
|
102
|
+
saveConfig({
|
|
103
|
+
...config,
|
|
104
|
+
workspace: chosen,
|
|
105
|
+
workspaceId: match.id,
|
|
106
|
+
});
|
|
107
|
+
console.log(`\n${ok(`Now using workspace ${chalk.bold(match.name)} ${muted(`(${match.slug})`)}`)}\n`);
|
|
108
|
+
}
|
|
109
|
+
//# sourceMappingURL=use.js.map
|