@fiftth/fiftth-cli 1.0.1 → 1.1.1
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/.fiftthnexus/.github/workflows/copilot-orchestrator.yml +78 -0
- package/.fiftthnexus/actions/Dockerfile +34 -0
- package/.fiftthnexus/actions/copilot-agent.mjs +269 -0
- package/.fiftthnexus/actions/package.json +8 -0
- package/.fiftthnexus/orchestrator.ts +2304 -0
- package/.fiftthnexus/skills/env-implement-prompt.md +65 -0
- package/.fiftthnexus/skills/env-plan-prompt.md +33 -0
- package/.fiftthnexus/skills/env-review-prompt.md +61 -0
- package/.fiftthnexus/skills/grill-me.md +9 -0
- package/.fiftthnexus/skills/prd-to-issues.md +150 -0
- package/.fiftthnexus/skills/write-prd.md +70 -0
- package/README.md +216 -25
- package/dist/api/client.d.ts +6 -0
- package/dist/api/client.d.ts.map +1 -1
- package/dist/api/client.js +13 -2
- package/dist/api/client.js.map +1 -1
- package/dist/commands/checkout.d.ts +6 -1
- package/dist/commands/checkout.d.ts.map +1 -1
- package/dist/commands/checkout.js +415 -44
- package/dist/commands/checkout.js.map +1 -1
- package/dist/commands/login.d.ts +0 -2
- package/dist/commands/login.d.ts.map +1 -1
- package/dist/commands/login.js +83 -32
- package/dist/commands/login.js.map +1 -1
- package/dist/commands/model.d.ts +2 -0
- package/dist/commands/model.d.ts.map +1 -0
- package/dist/commands/model.js +32 -0
- package/dist/commands/model.js.map +1 -0
- package/dist/commands/planningContext.d.ts +6 -0
- package/dist/commands/planningContext.d.ts.map +1 -0
- package/dist/commands/planningContext.js +91 -0
- package/dist/commands/planningContext.js.map +1 -0
- package/dist/commands/repo.d.ts.map +1 -1
- package/dist/commands/repo.js +38 -15
- package/dist/commands/repo.js.map +1 -1
- package/dist/commands/skills.d.ts +2 -0
- package/dist/commands/skills.d.ts.map +1 -0
- package/dist/commands/skills.js +123 -0
- package/dist/commands/skills.js.map +1 -0
- package/dist/commands/use.d.ts +1 -5
- package/dist/commands/use.d.ts.map +1 -1
- package/dist/commands/use.js +63 -48
- package/dist/commands/use.js.map +1 -1
- package/dist/index.js +86 -27
- package/dist/index.js.map +1 -1
- package/dist/services/nexusService.d.ts +30 -0
- package/dist/services/nexusService.d.ts.map +1 -0
- package/dist/services/nexusService.js +188 -0
- package/dist/services/nexusService.js.map +1 -0
- package/dist/services/prdService.d.ts +12 -0
- package/dist/services/prdService.d.ts.map +1 -0
- package/dist/services/prdService.js +103 -0
- package/dist/services/prdService.js.map +1 -0
- package/dist/services/taskSelection.d.ts +10 -0
- package/dist/services/taskSelection.d.ts.map +1 -0
- package/dist/services/taskSelection.js +112 -0
- package/dist/services/taskSelection.js.map +1 -0
- package/dist/services/taskService.d.ts +23 -1
- package/dist/services/taskService.d.ts.map +1 -1
- package/dist/services/taskService.js +118 -12
- package/dist/services/taskService.js.map +1 -1
- package/dist/utils/config.d.ts +2 -0
- package/dist/utils/config.d.ts.map +1 -1
- package/dist/utils/config.js +20 -3
- package/dist/utils/config.js.map +1 -1
- package/dist/utils/dashboard.d.ts +65 -0
- package/dist/utils/dashboard.d.ts.map +1 -0
- package/dist/utils/dashboard.js +205 -0
- package/dist/utils/dashboard.js.map +1 -0
- package/dist/utils/models.d.ts +14 -0
- package/dist/utils/models.d.ts.map +1 -0
- package/dist/utils/models.js +89 -0
- package/dist/utils/models.js.map +1 -0
- package/dist/utils/ui.d.ts +6 -0
- package/dist/utils/ui.d.ts.map +1 -1
- package/dist/utils/ui.js +22 -1
- package/dist/utils/ui.js.map +1 -1
- package/dist/utils/version.d.ts +4 -0
- package/dist/utils/version.d.ts.map +1 -0
- package/dist/utils/version.js +26 -0
- package/dist/utils/version.js.map +1 -0
- package/package.json +9 -4
- package/.github/workflows/publish-npm.yml +0 -62
- package/dist/commands/tasks.d.ts +0 -2
- package/dist/commands/tasks.d.ts.map +0 -1
- package/dist/commands/tasks.js +0 -69
- package/dist/commands/tasks.js.map +0 -1
- package/dist/context/runtimeContext.d.ts +0 -14
- package/dist/context/runtimeContext.d.ts.map +0 -1
- package/dist/context/runtimeContext.js +0 -21
- package/dist/context/runtimeContext.js.map +0 -1
- package/dist/services/taskContext.d.ts +0 -14
- package/dist/services/taskContext.d.ts.map +0 -1
- package/dist/services/taskContext.js +0 -15
- package/dist/services/taskContext.js.map +0 -1
- package/dist/utils/api.d.ts +0 -10
- package/dist/utils/api.d.ts.map +0 -1
- package/dist/utils/api.js +0 -25
- package/dist/utils/api.js.map +0 -1
- package/src/api/client.ts +0 -31
- package/src/commands/checkout.ts +0 -101
- package/src/commands/login.ts +0 -145
- package/src/commands/repo.ts +0 -113
- package/src/commands/tasks.ts +0 -86
- package/src/commands/use.ts +0 -149
- package/src/config/configService.ts +0 -56
- package/src/context/runtimeContext.ts +0 -42
- package/src/git/gitService.ts +0 -29
- package/src/index.ts +0 -133
- package/src/services/taskContext.ts +0 -32
- package/src/services/taskService.ts +0 -53
- package/src/utils/api.ts +0 -41
- package/src/utils/config.ts +0 -48
- package/src/utils/ui.ts +0 -46
- package/tsconfig.json +0 -18
- package/vitest.config.ts +0 -8
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
interface SelectedTaskRepository {
|
|
2
|
-
fullName: string;
|
|
3
|
-
branch: string;
|
|
4
|
-
}
|
|
5
|
-
export interface SelectedTaskContext {
|
|
6
|
-
taskId: string;
|
|
7
|
-
title: string;
|
|
8
|
-
repositories: SelectedTaskRepository[];
|
|
9
|
-
}
|
|
10
|
-
export declare function setRuntimeSelectedTask(task: SelectedTaskContext): void;
|
|
11
|
-
export declare function getRuntimeSelectedTask(): SelectedTaskContext | null;
|
|
12
|
-
export declare function clearRuntimeSelectedTask(): void;
|
|
13
|
-
export {};
|
|
14
|
-
//# sourceMappingURL=runtimeContext.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"runtimeContext.d.ts","sourceRoot":"","sources":["../../src/context/runtimeContext.ts"],"names":[],"mappings":"AAEA,UAAU,sBAAsB;IAC9B,QAAQ,EAAE,MAAM,CAAA;IAChB,MAAM,EAAE,MAAM,CAAA;CACf;AAED,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAE,MAAM,CAAA;IACd,KAAK,EAAE,MAAM,CAAA;IACb,YAAY,EAAE,sBAAsB,EAAE,CAAA;CACvC;AAaD,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,mBAAmB,GAAG,IAAI,CAGtE;AAED,wBAAgB,sBAAsB,IAAI,mBAAmB,GAAG,IAAI,CAMnE;AAED,wBAAgB,wBAAwB,IAAI,IAAI,CAG/C"}
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
import Conf from 'conf';
|
|
2
|
-
const contextStore = new Conf({
|
|
3
|
-
projectName: 'fiftth-cli',
|
|
4
|
-
defaults: {},
|
|
5
|
-
});
|
|
6
|
-
let selectedTaskInMemory = null;
|
|
7
|
-
export function setRuntimeSelectedTask(task) {
|
|
8
|
-
selectedTaskInMemory = task;
|
|
9
|
-
contextStore.set('selectedTask', task);
|
|
10
|
-
}
|
|
11
|
-
export function getRuntimeSelectedTask() {
|
|
12
|
-
if (selectedTaskInMemory) {
|
|
13
|
-
return selectedTaskInMemory;
|
|
14
|
-
}
|
|
15
|
-
return contextStore.get('selectedTask') ?? null;
|
|
16
|
-
}
|
|
17
|
-
export function clearRuntimeSelectedTask() {
|
|
18
|
-
selectedTaskInMemory = null;
|
|
19
|
-
contextStore.delete('selectedTask');
|
|
20
|
-
}
|
|
21
|
-
//# sourceMappingURL=runtimeContext.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"runtimeContext.js","sourceRoot":"","sources":["../../src/context/runtimeContext.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,MAAM,CAAA;AAiBvB,MAAM,YAAY,GAAG,IAAI,IAAI,CAAsB;IACjD,WAAW,EAAE,YAAY;IACzB,QAAQ,EAAE,EAAE;CACb,CAAC,CAAA;AAEF,IAAI,oBAAoB,GAA+B,IAAI,CAAA;AAE3D,MAAM,UAAU,sBAAsB,CAAC,IAAyB;IAC9D,oBAAoB,GAAG,IAAI,CAAA;IAC3B,YAAY,CAAC,GAAG,CAAC,cAAc,EAAE,IAAI,CAAC,CAAA;AACxC,CAAC;AAED,MAAM,UAAU,sBAAsB;IACpC,IAAI,oBAAoB,EAAE,CAAC;QACzB,OAAO,oBAAoB,CAAA;IAC7B,CAAC;IAED,OAAO,YAAY,CAAC,GAAG,CAAC,cAAc,CAAC,IAAI,IAAI,CAAA;AACjD,CAAC;AAED,MAAM,UAAU,wBAAwB;IACtC,oBAAoB,GAAG,IAAI,CAAA;IAC3B,YAAY,CAAC,MAAM,CAAC,cAAc,CAAC,CAAA;AACrC,CAAC"}
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
export interface SelectedTaskRepository {
|
|
2
|
-
repoName: string;
|
|
3
|
-
branch: string;
|
|
4
|
-
}
|
|
5
|
-
export interface SelectedTaskContext {
|
|
6
|
-
taskId: string;
|
|
7
|
-
title: string;
|
|
8
|
-
repositories: SelectedTaskRepository[];
|
|
9
|
-
}
|
|
10
|
-
export declare function setSelectedTaskContext(task: SelectedTaskContext): void;
|
|
11
|
-
export declare function getSelectedTaskContext(): SelectedTaskContext | null;
|
|
12
|
-
export declare function getSelectedTaskBranch(repoName: string): string | undefined;
|
|
13
|
-
export declare function clearSelectedTaskContext(): void;
|
|
14
|
-
//# sourceMappingURL=taskContext.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"taskContext.d.ts","sourceRoot":"","sources":["../../src/services/taskContext.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,sBAAsB;IACrC,QAAQ,EAAE,MAAM,CAAA;IAChB,MAAM,EAAE,MAAM,CAAA;CACf;AAED,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAE,MAAM,CAAA;IACd,KAAK,EAAE,MAAM,CAAA;IACb,YAAY,EAAE,sBAAsB,EAAE,CAAA;CACvC;AAID,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,mBAAmB,GAAG,IAAI,CAEtE;AAED,wBAAgB,sBAAsB,IAAI,mBAAmB,GAAG,IAAI,CAEnE;AAED,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAM1E;AAED,wBAAgB,wBAAwB,IAAI,IAAI,CAE/C"}
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
let selectedTaskContext = null;
|
|
2
|
-
export function setSelectedTaskContext(task) {
|
|
3
|
-
selectedTaskContext = task;
|
|
4
|
-
}
|
|
5
|
-
export function getSelectedTaskContext() {
|
|
6
|
-
return selectedTaskContext;
|
|
7
|
-
}
|
|
8
|
-
export function getSelectedTaskBranch(repoName) {
|
|
9
|
-
const repository = selectedTaskContext?.repositories.find((entry) => entry.repoName === repoName);
|
|
10
|
-
return repository?.branch;
|
|
11
|
-
}
|
|
12
|
-
export function clearSelectedTaskContext() {
|
|
13
|
-
selectedTaskContext = null;
|
|
14
|
-
}
|
|
15
|
-
//# sourceMappingURL=taskContext.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"taskContext.js","sourceRoot":"","sources":["../../src/services/taskContext.ts"],"names":[],"mappings":"AAWA,IAAI,mBAAmB,GAA+B,IAAI,CAAA;AAE1D,MAAM,UAAU,sBAAsB,CAAC,IAAyB;IAC9D,mBAAmB,GAAG,IAAI,CAAA;AAC5B,CAAC;AAED,MAAM,UAAU,sBAAsB;IACpC,OAAO,mBAAmB,CAAA;AAC5B,CAAC;AAED,MAAM,UAAU,qBAAqB,CAAC,QAAgB;IACpD,MAAM,UAAU,GAAG,mBAAmB,EAAE,YAAY,CAAC,IAAI,CACvD,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,QAAQ,KAAK,QAAQ,CACvC,CAAA;IAED,OAAO,UAAU,EAAE,MAAM,CAAA;AAC3B,CAAC;AAED,MAAM,UAAU,wBAAwB;IACtC,mBAAmB,GAAG,IAAI,CAAA;AAC5B,CAAC"}
|
package/dist/utils/api.d.ts
DELETED
package/dist/utils/api.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../../src/utils/api.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,SAAS;IACxB,EAAE,EAAE,MAAM,CAAA;IACV,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,CAAA;CACb;AAED,MAAM,WAAW,SAAS;IACxB,aAAa,IAAI,OAAO,CAAC,SAAS,EAAE,CAAC,CAAA;CACtC;AAED,wBAAgB,eAAe,IAAI,SAAS,CA4B3C"}
|
package/dist/utils/api.js
DELETED
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
import { loadConfig } from './config.js';
|
|
2
|
-
export function createApiClient() {
|
|
3
|
-
const config = loadConfig();
|
|
4
|
-
async function request(endpoint) {
|
|
5
|
-
const url = `${config.host}${endpoint}`;
|
|
6
|
-
const response = await fetch(url, {
|
|
7
|
-
headers: {
|
|
8
|
-
Authorization: `Bearer ${config.token}`,
|
|
9
|
-
'Content-Type': 'application/json',
|
|
10
|
-
'User-Agent': 'fiftth-cli/0.1.0',
|
|
11
|
-
},
|
|
12
|
-
});
|
|
13
|
-
if (!response.ok) {
|
|
14
|
-
const body = await response.text().catch(() => '');
|
|
15
|
-
throw new Error(`API request failed: ${response.status} ${response.statusText}${body ? ` — ${body}` : ''}`);
|
|
16
|
-
}
|
|
17
|
-
return response.json();
|
|
18
|
-
}
|
|
19
|
-
return {
|
|
20
|
-
getWorkspaces() {
|
|
21
|
-
return request('/workspaces');
|
|
22
|
-
},
|
|
23
|
-
};
|
|
24
|
-
}
|
|
25
|
-
//# sourceMappingURL=api.js.map
|
package/dist/utils/api.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"api.js","sourceRoot":"","sources":["../../src/utils/api.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AAYxC,MAAM,UAAU,eAAe;IAC7B,MAAM,MAAM,GAAG,UAAU,EAAE,CAAA;IAE3B,KAAK,UAAU,OAAO,CAAI,QAAgB;QACxC,MAAM,GAAG,GAAG,GAAG,MAAM,CAAC,IAAI,GAAG,QAAQ,EAAE,CAAA;QACvC,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE;YAChC,OAAO,EAAE;gBACP,aAAa,EAAE,UAAU,MAAM,CAAC,KAAK,EAAE;gBACvC,cAAc,EAAE,kBAAkB;gBAClC,YAAY,EAAE,kBAAkB;aACjC;SACF,CAAC,CAAA;QAEF,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;YACjB,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,CAAA;YAClD,MAAM,IAAI,KAAK,CACb,uBAAuB,QAAQ,CAAC,MAAM,IAAI,QAAQ,CAAC,UAAU,GAAG,IAAI,CAAC,CAAC,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,CAC3F,CAAA;QACH,CAAC;QAED,OAAO,QAAQ,CAAC,IAAI,EAAgB,CAAA;IACtC,CAAC;IAED,OAAO;QACL,aAAa;YACX,OAAO,OAAO,CAAc,aAAa,CAAC,CAAA;QAC5C,CAAC;KACF,CAAA;AACH,CAAC"}
|
package/src/api/client.ts
DELETED
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
import axios, { type AxiosInstance } from 'axios'
|
|
2
|
-
import { loadConfig } from '../utils/config.js'
|
|
3
|
-
|
|
4
|
-
export interface ApiClient {
|
|
5
|
-
get<T>(endpoint: string): Promise<T>
|
|
6
|
-
post<T>(endpoint: string, body: unknown): Promise<T>
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
export function createApiClient(): ApiClient {
|
|
10
|
-
const config = loadConfig()
|
|
11
|
-
|
|
12
|
-
const httpClient: AxiosInstance = axios.create({
|
|
13
|
-
baseURL: config.host,
|
|
14
|
-
headers: {
|
|
15
|
-
Authorization: `Bearer ${config.token}`,
|
|
16
|
-
'Content-Type': 'application/json',
|
|
17
|
-
'User-Agent': 'fiftth-cli/0.1.0',
|
|
18
|
-
},
|
|
19
|
-
})
|
|
20
|
-
|
|
21
|
-
return {
|
|
22
|
-
async get<T>(endpoint: string): Promise<T> {
|
|
23
|
-
const response = await httpClient.get<T>(endpoint)
|
|
24
|
-
return response.data
|
|
25
|
-
},
|
|
26
|
-
async post<T>(endpoint: string, body: unknown): Promise<T> {
|
|
27
|
-
const response = await httpClient.post<T>(endpoint, body)
|
|
28
|
-
return response.data
|
|
29
|
-
},
|
|
30
|
-
}
|
|
31
|
-
}
|
package/src/commands/checkout.ts
DELETED
|
@@ -1,101 +0,0 @@
|
|
|
1
|
-
import chalk from 'chalk'
|
|
2
|
-
import { getRepoPath } from '../config/configService.js'
|
|
3
|
-
import { getRuntimeSelectedTask } from '../context/runtimeContext.js'
|
|
4
|
-
import {
|
|
5
|
-
checkGitRepo,
|
|
6
|
-
checkRepoExists,
|
|
7
|
-
checkUncommittedChanges,
|
|
8
|
-
checkoutBranch,
|
|
9
|
-
fetch,
|
|
10
|
-
} from '../git/gitService.js'
|
|
11
|
-
import { moveTaskToHumanReview } from '../services/taskService.js'
|
|
12
|
-
import { cmd, fail, info, kv, muted, ok, section } from '../utils/ui.js'
|
|
13
|
-
|
|
14
|
-
export async function checkoutCommand(taskId: string): Promise<void> {
|
|
15
|
-
const selectedTask = getRuntimeSelectedTask()
|
|
16
|
-
|
|
17
|
-
if (!selectedTask) {
|
|
18
|
-
console.log(`\n${fail('No task selected.')}\n`)
|
|
19
|
-
console.log(`${muted(`Run ${cmd('fiftth tasks')} to select one.`)}\n`)
|
|
20
|
-
process.exit(1)
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
if (selectedTask.taskId !== taskId) {
|
|
24
|
-
console.log(`\n${fail('Task ID does not match the selected task.')}\n`)
|
|
25
|
-
console.log(kv('Selected task ID', selectedTask.taskId))
|
|
26
|
-
console.log(`${muted(`Run ${cmd('fiftth tasks')} and select task ${cmd(taskId)} first.`)}\n`)
|
|
27
|
-
console.log()
|
|
28
|
-
process.exit(1)
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
if (selectedTask.repositories.length === 0) {
|
|
32
|
-
console.log(`\n${info('This task has no linked repositories.')}\n`)
|
|
33
|
-
return
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
console.log(`\n${section(`Checking out task ${selectedTask.taskId} in linked repositories`)}\n`)
|
|
37
|
-
|
|
38
|
-
const successfulRepositories: string[] = []
|
|
39
|
-
const failedRepositories: string[] = []
|
|
40
|
-
|
|
41
|
-
for (const taskRepository of selectedTask.repositories) {
|
|
42
|
-
const repoName = taskRepository.fullName
|
|
43
|
-
const repoPath = getRepoPath(repoName)
|
|
44
|
-
|
|
45
|
-
console.log(kv('Repository', repoName))
|
|
46
|
-
|
|
47
|
-
if (!repoPath) {
|
|
48
|
-
console.log(` ${fail('Skipped: repository not configured locally.')}`)
|
|
49
|
-
failedRepositories.push(repoName)
|
|
50
|
-
continue
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
if (!checkRepoExists(repoPath)) {
|
|
54
|
-
console.log(` ${fail('Skipped: repository path not found.')}`)
|
|
55
|
-
failedRepositories.push(repoName)
|
|
56
|
-
continue
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
if (!checkGitRepo(repoPath)) {
|
|
60
|
-
console.log(` ${fail('Skipped: configured path is not a git repository.')}`)
|
|
61
|
-
failedRepositories.push(repoName)
|
|
62
|
-
continue
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
const hasUncommittedChanges = await checkUncommittedChanges(repoPath)
|
|
66
|
-
if (hasUncommittedChanges) {
|
|
67
|
-
console.log(` ${fail('Skipped: uncommitted changes detected.')}`)
|
|
68
|
-
failedRepositories.push(repoName)
|
|
69
|
-
continue
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
try {
|
|
73
|
-
await fetch(repoPath)
|
|
74
|
-
await checkoutBranch(repoPath, taskRepository.branch)
|
|
75
|
-
console.log(` ${ok(`Checked out branch ${chalk.bold(taskRepository.branch)}`)}`)
|
|
76
|
-
successfulRepositories.push(repoName)
|
|
77
|
-
} catch {
|
|
78
|
-
console.log(` ${fail('Failed: could not fetch/checkout branch.')}`)
|
|
79
|
-
failedRepositories.push(repoName)
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
console.log()
|
|
84
|
-
console.log(ok(`Repositories checked out: ${successfulRepositories.length}`))
|
|
85
|
-
if (failedRepositories.length > 0) {
|
|
86
|
-
console.log(fail(`Repositories failed/skipped: ${failedRepositories.length}`))
|
|
87
|
-
for (const repository of failedRepositories) {
|
|
88
|
-
console.log(` ${muted('•')} ${repository}`)
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
if (successfulRepositories.length > 0 && failedRepositories.length === 0) {
|
|
93
|
-
try {
|
|
94
|
-
await moveTaskToHumanReview(selectedTask.taskId)
|
|
95
|
-
} catch {
|
|
96
|
-
console.log(`\n${info('Could not move task to Human Review. Please update it manually.')}\n`)
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
console.log(`\n${ok('Checkout complete')}\n`)
|
|
101
|
-
}
|
package/src/commands/login.ts
DELETED
|
@@ -1,145 +0,0 @@
|
|
|
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
|
-
|
|
7
|
-
interface LoginOptions {
|
|
8
|
-
token?: string
|
|
9
|
-
host?: string
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
function normalizeToken(rawToken: string): string {
|
|
13
|
-
return rawToken.trim().replace(/^Bearer\s+/i, '')
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
async function validateToken(host: string, token: string): Promise<boolean> {
|
|
17
|
-
try {
|
|
18
|
-
const response = await fetch(`${host}/cli-tokens`, {
|
|
19
|
-
headers: {
|
|
20
|
-
Authorization: `Bearer ${token}`,
|
|
21
|
-
'User-Agent': 'fiftth-cli/0.1.0',
|
|
22
|
-
},
|
|
23
|
-
})
|
|
24
|
-
|
|
25
|
-
if (!response.ok) {
|
|
26
|
-
return false
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
const body = (await response.json().catch(() => null)) as
|
|
30
|
-
| { valid?: boolean }
|
|
31
|
-
| null
|
|
32
|
-
|
|
33
|
-
return body?.valid === true
|
|
34
|
-
} catch {
|
|
35
|
-
return false
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
export async function loginCommand(options: LoginOptions): Promise<void> {
|
|
40
|
-
const config = loadConfig()
|
|
41
|
-
const host = options.host ?? config.host
|
|
42
|
-
|
|
43
|
-
console.log(`\n${title('Welcome to Fiftth')}\n`)
|
|
44
|
-
console.log(muted('Orchestrate tasks, repos and agent workflows from your terminal.\n'))
|
|
45
|
-
|
|
46
|
-
if (config.token && !options.token) {
|
|
47
|
-
const alreadyLoggedIn = await confirm({
|
|
48
|
-
message: chalk.yellow('You are already logged in. Validate login again?'),
|
|
49
|
-
default: false,
|
|
50
|
-
})
|
|
51
|
-
if (!alreadyLoggedIn) {
|
|
52
|
-
console.log(`\n${muted('No changes made.')}\n`)
|
|
53
|
-
return
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
let token: string
|
|
58
|
-
|
|
59
|
-
if (options.token) {
|
|
60
|
-
token = normalizeToken(options.token)
|
|
61
|
-
} else {
|
|
62
|
-
console.log(muted(`Generate an access token at: ${cmd(`${host}/cli-tokens`)}\n`))
|
|
63
|
-
token = await password({
|
|
64
|
-
message: 'Paste your access token:',
|
|
65
|
-
mask: '*',
|
|
66
|
-
})
|
|
67
|
-
|
|
68
|
-
if (!token || token.trim() === '') {
|
|
69
|
-
console.error(`\n${fail('Token cannot be empty.')}\n`)
|
|
70
|
-
process.exit(1)
|
|
71
|
-
}
|
|
72
|
-
token = normalizeToken(token)
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
const spinner = ora('Validating credentials...').start()
|
|
76
|
-
|
|
77
|
-
const valid = await validateToken(host, token)
|
|
78
|
-
|
|
79
|
-
if (!valid) {
|
|
80
|
-
spinner.fail(fail('Authentication failed.'))
|
|
81
|
-
console.error(`\n${muted('Make sure your token is correct and has not expired.')}\n`)
|
|
82
|
-
process.exit(1)
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
spinner.succeed(ok('Authentication validated.'))
|
|
86
|
-
|
|
87
|
-
saveConfig({ ...config, token, host })
|
|
88
|
-
|
|
89
|
-
console.log(`\n${section('Session ready')}`)
|
|
90
|
-
console.log(kv('Host', cmd(host)))
|
|
91
|
-
console.log(kv('Next', `run ${cmd('fiftth use <workspace>')}`))
|
|
92
|
-
console.log()
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
export async function logoutCommand(): Promise<void> {
|
|
96
|
-
const config = loadConfig()
|
|
97
|
-
|
|
98
|
-
if (!config.token) {
|
|
99
|
-
console.log(`\n${info('You are not currently logged in.')}\n`)
|
|
100
|
-
return
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
const confirmed = await confirm({
|
|
104
|
-
message: 'Are you sure you want to log out?',
|
|
105
|
-
default: false,
|
|
106
|
-
})
|
|
107
|
-
|
|
108
|
-
if (!confirmed) {
|
|
109
|
-
console.log(`\n${muted('Logout cancelled.')}\n`)
|
|
110
|
-
return
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
const { token: _token, ...rest } = config
|
|
114
|
-
saveConfig(rest as ReturnType<typeof loadConfig>)
|
|
115
|
-
|
|
116
|
-
console.log(`\n${ok('Logged out successfully.')}\n`)
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
export async function loginStatusCommand(): Promise<void> {
|
|
120
|
-
const config = loadConfig()
|
|
121
|
-
|
|
122
|
-
if (!config.token) {
|
|
123
|
-
console.log(`\n${info('Not logged in.')}\n`)
|
|
124
|
-
console.log(`${muted(`Run ${cmd('fiftth login')} to authenticate.`)}\n`)
|
|
125
|
-
return
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
const spinner = ora('Checking authentication status...').start()
|
|
129
|
-
const valid = await validateToken(config.host, config.token)
|
|
130
|
-
|
|
131
|
-
if (valid) {
|
|
132
|
-
spinner.succeed(ok('Authenticated'))
|
|
133
|
-
console.log(`\n${kv('Host', cmd(config.host))}`)
|
|
134
|
-
if (config.workspace) {
|
|
135
|
-
console.log(kv('Workspace', cmd(config.workspace)))
|
|
136
|
-
}
|
|
137
|
-
console.log()
|
|
138
|
-
} else {
|
|
139
|
-
spinner.fail(fail('Token is invalid or expired.'))
|
|
140
|
-
console.log(`\n${muted(`Run ${cmd('fiftth login')} to authenticate again.`)}\n`)
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
export { input }
|
|
145
|
-
export { normalizeToken }
|
package/src/commands/repo.ts
DELETED
|
@@ -1,113 +0,0 @@
|
|
|
1
|
-
import fs from 'fs'
|
|
2
|
-
import os from 'os'
|
|
3
|
-
import path from 'path'
|
|
4
|
-
import chalk from 'chalk'
|
|
5
|
-
import { Command } from 'commander'
|
|
6
|
-
import {
|
|
7
|
-
addRepository,
|
|
8
|
-
getRepositories,
|
|
9
|
-
removeRepository,
|
|
10
|
-
} from '../config/configService.js'
|
|
11
|
-
import { cmd, fail, info, kv, muted, ok, section } from '../utils/ui.js'
|
|
12
|
-
|
|
13
|
-
function expandHomeDirectory(inputPath: string): string {
|
|
14
|
-
if (inputPath === '~') {
|
|
15
|
-
return os.homedir()
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
if (inputPath.startsWith('~/') || inputPath.startsWith('~\\')) {
|
|
19
|
-
return path.join(os.homedir(), inputPath.slice(2))
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
return inputPath
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
function resolveRepoPath(inputPath: string): string {
|
|
26
|
-
const expandedPath = expandHomeDirectory(inputPath)
|
|
27
|
-
return path.resolve(expandedPath)
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
function validateRepositoryPath(repoPath: string): void {
|
|
31
|
-
if (!fs.existsSync(repoPath) || !fs.statSync(repoPath).isDirectory()) {
|
|
32
|
-
console.error(`\n${fail('Path not found.')}\n`)
|
|
33
|
-
process.exit(1)
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
const gitPath = path.join(repoPath, '.git')
|
|
37
|
-
|
|
38
|
-
if (!fs.existsSync(gitPath) || !fs.statSync(gitPath).isDirectory()) {
|
|
39
|
-
console.error(`\n${fail('The provided path is not a git repository.')}\n`)
|
|
40
|
-
process.exit(1)
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
function addRepoCommand(repoName: string, repoPathInput: string | string[]): void {
|
|
45
|
-
const normalizedPathInput = Array.isArray(repoPathInput)
|
|
46
|
-
? repoPathInput.join(' ')
|
|
47
|
-
: repoPathInput
|
|
48
|
-
|
|
49
|
-
const absolutePath = resolveRepoPath(normalizedPathInput)
|
|
50
|
-
validateRepositoryPath(absolutePath)
|
|
51
|
-
|
|
52
|
-
const { overwritten } = addRepository(repoName, absolutePath)
|
|
53
|
-
|
|
54
|
-
if (overwritten) {
|
|
55
|
-
console.log(chalk.yellow(`${info('Repository already exists. Overwriting mapping.')}`))
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
console.log(`\n${ok('Repository registered')}\n`)
|
|
59
|
-
console.log(kv('Repo', repoName))
|
|
60
|
-
console.log(kv('Path', cmd(absolutePath)))
|
|
61
|
-
console.log()
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
function listRepoCommand(): void {
|
|
65
|
-
const repositories = getRepositories()
|
|
66
|
-
const entries = Object.entries(repositories)
|
|
67
|
-
|
|
68
|
-
if (entries.length === 0) {
|
|
69
|
-
console.log(`\n${info('No repositories configured yet.')}\n`)
|
|
70
|
-
console.log(`${muted(`Use ${cmd('fiftth repo add <repoName> <path>')}`)}\n`)
|
|
71
|
-
return
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
const longestNameLength = Math.max(...entries.map(([name]) => name.length))
|
|
75
|
-
|
|
76
|
-
console.log(`\n${section('Configured repositories')}\n`)
|
|
77
|
-
for (const [repoName, repoPath] of entries) {
|
|
78
|
-
console.log(`${repoName.padEnd(longestNameLength, ' ')} ${muted('->')} ${cmd(repoPath)}`)
|
|
79
|
-
}
|
|
80
|
-
console.log()
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
function removeRepoCommand(repoName: string): void {
|
|
84
|
-
const removed = removeRepository(repoName)
|
|
85
|
-
|
|
86
|
-
if (!removed) {
|
|
87
|
-
console.error(`\n${fail(`Repository '${repoName}' is not configured.`)}\n`)
|
|
88
|
-
process.exit(1)
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
console.log(`\n${ok('Repository removed')}\n`)
|
|
92
|
-
console.log(kv('Repo', repoName))
|
|
93
|
-
console.log()
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
export function registerRepoCommands(program: Command): void {
|
|
97
|
-
const repoCmd = program.command('repo').description('Manage local repository mappings')
|
|
98
|
-
|
|
99
|
-
repoCmd
|
|
100
|
-
.command('add <repoName> <path...>')
|
|
101
|
-
.description('Register a local repository path')
|
|
102
|
-
.action(addRepoCommand)
|
|
103
|
-
|
|
104
|
-
repoCmd
|
|
105
|
-
.command('list')
|
|
106
|
-
.description('List all configured repositories')
|
|
107
|
-
.action(listRepoCommand)
|
|
108
|
-
|
|
109
|
-
repoCmd
|
|
110
|
-
.command('remove <repoName>')
|
|
111
|
-
.description('Remove a configured repository')
|
|
112
|
-
.action(removeRepoCommand)
|
|
113
|
-
}
|
package/src/commands/tasks.ts
DELETED
|
@@ -1,86 +0,0 @@
|
|
|
1
|
-
import chalk from 'chalk'
|
|
2
|
-
import inquirer from 'inquirer'
|
|
3
|
-
import { loadConfig } from '../utils/config.js'
|
|
4
|
-
import { fetchActiveTasks, type Task } 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
|
-
|
|
8
|
-
function printTaskRepositories(task: Task): void {
|
|
9
|
-
console.log(`\n${section(`Linked repositories for: ${task.title}`)}\n`)
|
|
10
|
-
console.log(kv('Task ID', task.id))
|
|
11
|
-
console.log('')
|
|
12
|
-
|
|
13
|
-
for (const repository of task.repositories) {
|
|
14
|
-
console.log(kv('Repository', repository.fullName))
|
|
15
|
-
console.log(` ${muted('branch')} ${chalk.bold(repository.branch)}`)
|
|
16
|
-
console.log('')
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
export async function tasksCommand(): Promise<void> {
|
|
21
|
-
const config = loadConfig()
|
|
22
|
-
|
|
23
|
-
if (!config.token) {
|
|
24
|
-
console.error(
|
|
25
|
-
`\n${fail('Not authenticated.')} ${muted(`Run ${cmd('fiftth login')} first.`)}\n`,
|
|
26
|
-
)
|
|
27
|
-
process.exit(1)
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
if (!config.workspaceId) {
|
|
31
|
-
console.error(
|
|
32
|
-
`\n${fail('Workspace not selected.')} ${muted(`Run ${cmd('fiftth use')} first.`)}\n`,
|
|
33
|
-
)
|
|
34
|
-
process.exit(1)
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
let tasks: Task[]
|
|
38
|
-
try {
|
|
39
|
-
tasks = await fetchActiveTasks(config.workspaceId)
|
|
40
|
-
} catch {
|
|
41
|
-
console.error(`\n${fail('Failed to fetch tasks from API.')}\n`)
|
|
42
|
-
process.exit(1)
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
if (tasks.length === 0) {
|
|
46
|
-
console.log(`\n${info('No active tasks found.')}\n`)
|
|
47
|
-
return
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
console.log(`\n${section(`Active tasks in ${config.workspace ?? 'workspace'}`)}\n`)
|
|
51
|
-
|
|
52
|
-
const { selectedTaskId } = await inquirer.prompt<{ selectedTaskId: string }>([
|
|
53
|
-
{
|
|
54
|
-
type: 'list',
|
|
55
|
-
name: 'selectedTaskId',
|
|
56
|
-
message: 'Choose a task',
|
|
57
|
-
choices: tasks.map((task) => ({
|
|
58
|
-
name: `${task.title} (${task.id})`,
|
|
59
|
-
value: task.id,
|
|
60
|
-
})),
|
|
61
|
-
},
|
|
62
|
-
])
|
|
63
|
-
|
|
64
|
-
const selectedTask = tasks.find((task) => task.id === selectedTaskId)
|
|
65
|
-
|
|
66
|
-
if (!selectedTask) {
|
|
67
|
-
console.error(`\n${fail('No active tasks found.')}\n`)
|
|
68
|
-
process.exit(1)
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
setRuntimeSelectedTask({
|
|
72
|
-
taskId: selectedTask.id,
|
|
73
|
-
title: selectedTask.title,
|
|
74
|
-
repositories: selectedTask.repositories,
|
|
75
|
-
})
|
|
76
|
-
|
|
77
|
-
console.log(`\n${ok(`Task selected: ${chalk.bold(selectedTask.title)}`)}`)
|
|
78
|
-
console.log(kv('Task ID', selectedTask.id))
|
|
79
|
-
|
|
80
|
-
if (selectedTask.repositories.length === 0) {
|
|
81
|
-
console.log(`${info('This task has no linked repositories.')}\n`)
|
|
82
|
-
return
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
printTaskRepositories(selectedTask)
|
|
86
|
-
}
|