@fiftth/fiftth-cli 0.1.0 → 1.0.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/.github/workflows/publish-npm.yml +62 -0
- package/README.md +123 -111
- package/dist/api/client.d.ts +1 -0
- package/dist/api/client.d.ts.map +1 -1
- package/dist/api/client.js +4 -0
- package/dist/api/client.js.map +1 -1
- package/dist/commands/checkout.d.ts +1 -1
- package/dist/commands/checkout.d.ts.map +1 -1
- package/dist/commands/checkout.js +63 -31
- package/dist/commands/checkout.js.map +1 -1
- package/dist/commands/tasks.d.ts.map +1 -1
- package/dist/commands/tasks.js +4 -1
- package/dist/commands/tasks.js.map +1 -1
- package/dist/config/configService.d.ts.map +1 -1
- package/dist/config/configService.js +17 -4
- package/dist/config/configService.js.map +1 -1
- package/dist/index.js +25 -25
- package/dist/index.js.map +1 -1
- package/dist/services/taskService.d.ts +1 -0
- package/dist/services/taskService.d.ts.map +1 -1
- package/dist/services/taskService.js +7 -0
- package/dist/services/taskService.js.map +1 -1
- package/package.json +56 -56
- package/src/api/client.ts +31 -26
- package/src/commands/checkout.ts +101 -68
- package/src/commands/login.ts +145 -145
- package/src/commands/repo.ts +113 -113
- package/src/commands/tasks.ts +86 -83
- package/src/commands/use.ts +149 -149
- package/src/config/configService.ts +56 -40
- package/src/context/runtimeContext.ts +42 -42
- package/src/git/gitService.ts +29 -29
- package/src/index.ts +133 -133
- package/src/services/taskContext.ts +32 -32
- package/src/services/taskService.ts +53 -45
- package/src/utils/api.ts +41 -41
- package/src/utils/config.ts +48 -48
- package/src/utils/ui.ts +46 -46
- package/tsconfig.json +18 -18
- package/vitest.config.ts +8 -8
package/src/git/gitService.ts
CHANGED
|
@@ -1,29 +1,29 @@
|
|
|
1
|
-
import fs from 'fs'
|
|
2
|
-
import path from 'path'
|
|
3
|
-
import { simpleGit } from 'simple-git'
|
|
4
|
-
|
|
5
|
-
export function checkRepoExists(repoPath: string): boolean {
|
|
6
|
-
return fs.existsSync(repoPath) && fs.statSync(repoPath).isDirectory()
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
export function checkGitRepo(repoPath: string): boolean {
|
|
10
|
-
const gitPath = path.join(repoPath, '.git')
|
|
11
|
-
return fs.existsSync(gitPath) && fs.statSync(gitPath).isDirectory()
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
export async function checkUncommittedChanges(repoPath: string): Promise<boolean> {
|
|
15
|
-
const git = simpleGit(repoPath)
|
|
16
|
-
const status = await git.status()
|
|
17
|
-
|
|
18
|
-
return !status.isClean()
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
export async function fetch(repoPath: string): Promise<void> {
|
|
22
|
-
const git = simpleGit(repoPath)
|
|
23
|
-
await git.fetch()
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
export async function checkoutBranch(repoPath: string, branch: string): Promise<void> {
|
|
27
|
-
const git = simpleGit(repoPath)
|
|
28
|
-
await git.checkout(branch)
|
|
29
|
-
}
|
|
1
|
+
import fs from 'fs'
|
|
2
|
+
import path from 'path'
|
|
3
|
+
import { simpleGit } from 'simple-git'
|
|
4
|
+
|
|
5
|
+
export function checkRepoExists(repoPath: string): boolean {
|
|
6
|
+
return fs.existsSync(repoPath) && fs.statSync(repoPath).isDirectory()
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function checkGitRepo(repoPath: string): boolean {
|
|
10
|
+
const gitPath = path.join(repoPath, '.git')
|
|
11
|
+
return fs.existsSync(gitPath) && fs.statSync(gitPath).isDirectory()
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export async function checkUncommittedChanges(repoPath: string): Promise<boolean> {
|
|
15
|
+
const git = simpleGit(repoPath)
|
|
16
|
+
const status = await git.status()
|
|
17
|
+
|
|
18
|
+
return !status.isClean()
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export async function fetch(repoPath: string): Promise<void> {
|
|
22
|
+
const git = simpleGit(repoPath)
|
|
23
|
+
await git.fetch()
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export async function checkoutBranch(repoPath: string, branch: string): Promise<void> {
|
|
27
|
+
const git = simpleGit(repoPath)
|
|
28
|
+
await git.checkout(branch)
|
|
29
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -1,133 +1,133 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import { Command } from 'commander'
|
|
3
|
-
import chalk from 'chalk'
|
|
4
|
-
import { loginCommand, logoutCommand, loginStatusCommand } from './commands/login.js'
|
|
5
|
-
import { useCommand } from './commands/use.js'
|
|
6
|
-
import { registerRepoCommands } from './commands/repo.js'
|
|
7
|
-
import { tasksCommand } from './commands/tasks.js'
|
|
8
|
-
import { checkoutCommand } from './commands/checkout.js'
|
|
9
|
-
import { loadConfig, getConfigPath } from './utils/config.js'
|
|
10
|
-
import { cmd, fail, kv, muted, ok, section, title } from './utils/ui.js'
|
|
11
|
-
|
|
12
|
-
const program = new Command()
|
|
13
|
-
|
|
14
|
-
program
|
|
15
|
-
.name('fiftth')
|
|
16
|
-
.description('Startup-grade CLI for autonomous software teams')
|
|
17
|
-
.version('0.1.0', '-v, --version', 'Output the current version')
|
|
18
|
-
.addHelpText(
|
|
19
|
-
'before',
|
|
20
|
-
`\n${title('Fiftth CLI')}\n${muted('start orchestrating your AI engineering workflows.')}\n`,
|
|
21
|
-
)
|
|
22
|
-
.addHelpText(
|
|
23
|
-
'after',
|
|
24
|
-
`
|
|
25
|
-
${chalk.dim('Examples:')}
|
|
26
|
-
${cmd('$ fiftth login')} Authenticate with Fiftth
|
|
27
|
-
${cmd('$ fiftth login --token <tok>')} Authenticate using a token directly
|
|
28
|
-
${cmd('$ fiftth use my-workspace')} Switch to a workspace
|
|
29
|
-
${cmd('$ fiftth use')} Interactively select a workspace
|
|
30
|
-
${cmd('$ fiftth use --list')} List all available workspaces
|
|
31
|
-
${cmd('$ fiftth repo add api ~/dev/api')} Register a local git repository
|
|
32
|
-
${cmd('$ fiftth repo list')} List configured repositories
|
|
33
|
-
${cmd('$ fiftth tasks')} Select an active task and inspect repos
|
|
34
|
-
${cmd('$ fiftth checkout
|
|
35
|
-
`,
|
|
36
|
-
)
|
|
37
|
-
|
|
38
|
-
// ─── fiftth login ──────────────────────────────────────────────────────────
|
|
39
|
-
const loginCmd = program
|
|
40
|
-
.command('login')
|
|
41
|
-
.description('Authenticate with the Fiftth platform')
|
|
42
|
-
.option('-t, --token <token>', 'Access token (for non-interactive / CI use)')
|
|
43
|
-
.option('--host <host>', 'API host (defaults to https://api.fiftth.io)')
|
|
44
|
-
.addHelpText(
|
|
45
|
-
'after',
|
|
46
|
-
`
|
|
47
|
-
${chalk.dim('Examples:')}
|
|
48
|
-
${chalk.cyan('$ fiftth login')}
|
|
49
|
-
${chalk.cyan('$ fiftth login --token ghp_xxxxxxxxxxxx')}
|
|
50
|
-
${chalk.cyan('$ fiftth login --host https://selfhosted.example.com')}
|
|
51
|
-
`,
|
|
52
|
-
)
|
|
53
|
-
|
|
54
|
-
loginCmd
|
|
55
|
-
.command('status')
|
|
56
|
-
.description('Show current authentication status')
|
|
57
|
-
.action(async () => {
|
|
58
|
-
await loginStatusCommand().catch(handleError)
|
|
59
|
-
})
|
|
60
|
-
|
|
61
|
-
loginCmd
|
|
62
|
-
.command('logout')
|
|
63
|
-
.description('Log out and remove stored credentials')
|
|
64
|
-
.action(async () => {
|
|
65
|
-
await logoutCommand().catch(handleError)
|
|
66
|
-
})
|
|
67
|
-
|
|
68
|
-
loginCmd.action(async (options: { token?: string; host?: string }) => {
|
|
69
|
-
await loginCommand(options).catch(handleError)
|
|
70
|
-
})
|
|
71
|
-
|
|
72
|
-
// ─── fiftth use ────────────────────────────────────────────────────────────
|
|
73
|
-
program
|
|
74
|
-
.command('use [workspace]')
|
|
75
|
-
.description('Select the active workspace')
|
|
76
|
-
.option('-l, --list', 'List all available workspaces')
|
|
77
|
-
.addHelpText(
|
|
78
|
-
'after',
|
|
79
|
-
`
|
|
80
|
-
${chalk.dim('Examples:')}
|
|
81
|
-
${chalk.cyan('$ fiftth use')} Interactively select a workspace
|
|
82
|
-
${chalk.cyan('$ fiftth use my-workspace')} Switch directly to a workspace by slug or name
|
|
83
|
-
${chalk.cyan('$ fiftth use --list')} List all workspaces
|
|
84
|
-
`,
|
|
85
|
-
)
|
|
86
|
-
.action(async (workspace: string | undefined, options: { list?: boolean }) => {
|
|
87
|
-
await useCommand(workspace, options).catch(handleError)
|
|
88
|
-
})
|
|
89
|
-
|
|
90
|
-
// ─── fiftth config ─────────────────────────────────────────────────────────
|
|
91
|
-
program
|
|
92
|
-
.command('config')
|
|
93
|
-
.description('Show current CLI configuration')
|
|
94
|
-
.action(() => {
|
|
95
|
-
const config = loadConfig()
|
|
96
|
-
console.log(`\n${section('CLI configuration')}\n`)
|
|
97
|
-
console.log(kv('Config file', cmd(getConfigPath())))
|
|
98
|
-
console.log(kv('Host', cmd(config.host)))
|
|
99
|
-
console.log(kv('Workspace', config.workspace ? cmd(config.workspace) : muted('(none)')))
|
|
100
|
-
console.log(kv('Logged in', config.token ? ok('yes') : fail('no')))
|
|
101
|
-
console.log()
|
|
102
|
-
})
|
|
103
|
-
|
|
104
|
-
// ─── fiftth repo ───────────────────────────────────────────────────────────
|
|
105
|
-
registerRepoCommands(program)
|
|
106
|
-
|
|
107
|
-
// ─── fiftth tasks ──────────────────────────────────────────────────────────
|
|
108
|
-
program
|
|
109
|
-
.command('tasks')
|
|
110
|
-
.description('List and select active tasks')
|
|
111
|
-
.action(async () => {
|
|
112
|
-
await tasksCommand().catch(handleError)
|
|
113
|
-
})
|
|
114
|
-
|
|
115
|
-
// ─── fiftth checkout ───────────────────────────────────────────────────────
|
|
116
|
-
program
|
|
117
|
-
.command('checkout <
|
|
118
|
-
.description('Checkout the selected task
|
|
119
|
-
.action(async (
|
|
120
|
-
await checkoutCommand(
|
|
121
|
-
})
|
|
122
|
-
|
|
123
|
-
// ─── Error handling ────────────────────────────────────────────────────────
|
|
124
|
-
function handleError(err: unknown): void {
|
|
125
|
-
if (err instanceof Error) {
|
|
126
|
-
console.error(`\n${fail(`Error: ${err.message}`)}\n`)
|
|
127
|
-
} else {
|
|
128
|
-
console.error(`\n${fail('An unexpected error occurred.')}\n`)
|
|
129
|
-
}
|
|
130
|
-
process.exit(1)
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
program.parse()
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { Command } from 'commander'
|
|
3
|
+
import chalk from 'chalk'
|
|
4
|
+
import { loginCommand, logoutCommand, loginStatusCommand } from './commands/login.js'
|
|
5
|
+
import { useCommand } from './commands/use.js'
|
|
6
|
+
import { registerRepoCommands } from './commands/repo.js'
|
|
7
|
+
import { tasksCommand } from './commands/tasks.js'
|
|
8
|
+
import { checkoutCommand } from './commands/checkout.js'
|
|
9
|
+
import { loadConfig, getConfigPath } from './utils/config.js'
|
|
10
|
+
import { cmd, fail, kv, muted, ok, section, title } from './utils/ui.js'
|
|
11
|
+
|
|
12
|
+
const program = new Command()
|
|
13
|
+
|
|
14
|
+
program
|
|
15
|
+
.name('fiftth')
|
|
16
|
+
.description('Startup-grade CLI for autonomous software teams')
|
|
17
|
+
.version('0.1.0', '-v, --version', 'Output the current version')
|
|
18
|
+
.addHelpText(
|
|
19
|
+
'before',
|
|
20
|
+
`\n${title('Fiftth CLI')}\n${muted('start orchestrating your AI engineering workflows.')}\n`,
|
|
21
|
+
)
|
|
22
|
+
.addHelpText(
|
|
23
|
+
'after',
|
|
24
|
+
`
|
|
25
|
+
${chalk.dim('Examples:')}
|
|
26
|
+
${cmd('$ fiftth login')} Authenticate with Fiftth
|
|
27
|
+
${cmd('$ fiftth login --token <tok>')} Authenticate using a token directly
|
|
28
|
+
${cmd('$ fiftth use my-workspace')} Switch to a workspace
|
|
29
|
+
${cmd('$ fiftth use')} Interactively select a workspace
|
|
30
|
+
${cmd('$ fiftth use --list')} List all available workspaces
|
|
31
|
+
${cmd('$ fiftth repo add api ~/dev/api')} Register a local git repository
|
|
32
|
+
${cmd('$ fiftth repo list')} List configured repositories
|
|
33
|
+
${cmd('$ fiftth tasks')} Select an active task and inspect repos
|
|
34
|
+
${cmd('$ fiftth checkout <taskId>')} Checkout task branches in all linked repositories
|
|
35
|
+
`,
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
// ─── fiftth login ──────────────────────────────────────────────────────────
|
|
39
|
+
const loginCmd = program
|
|
40
|
+
.command('login')
|
|
41
|
+
.description('Authenticate with the Fiftth platform')
|
|
42
|
+
.option('-t, --token <token>', 'Access token (for non-interactive / CI use)')
|
|
43
|
+
.option('--host <host>', 'API host (defaults to https://api.fiftth.io)')
|
|
44
|
+
.addHelpText(
|
|
45
|
+
'after',
|
|
46
|
+
`
|
|
47
|
+
${chalk.dim('Examples:')}
|
|
48
|
+
${chalk.cyan('$ fiftth login')}
|
|
49
|
+
${chalk.cyan('$ fiftth login --token ghp_xxxxxxxxxxxx')}
|
|
50
|
+
${chalk.cyan('$ fiftth login --host https://selfhosted.example.com')}
|
|
51
|
+
`,
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
loginCmd
|
|
55
|
+
.command('status')
|
|
56
|
+
.description('Show current authentication status')
|
|
57
|
+
.action(async () => {
|
|
58
|
+
await loginStatusCommand().catch(handleError)
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
loginCmd
|
|
62
|
+
.command('logout')
|
|
63
|
+
.description('Log out and remove stored credentials')
|
|
64
|
+
.action(async () => {
|
|
65
|
+
await logoutCommand().catch(handleError)
|
|
66
|
+
})
|
|
67
|
+
|
|
68
|
+
loginCmd.action(async (options: { token?: string; host?: string }) => {
|
|
69
|
+
await loginCommand(options).catch(handleError)
|
|
70
|
+
})
|
|
71
|
+
|
|
72
|
+
// ─── fiftth use ────────────────────────────────────────────────────────────
|
|
73
|
+
program
|
|
74
|
+
.command('use [workspace]')
|
|
75
|
+
.description('Select the active workspace')
|
|
76
|
+
.option('-l, --list', 'List all available workspaces')
|
|
77
|
+
.addHelpText(
|
|
78
|
+
'after',
|
|
79
|
+
`
|
|
80
|
+
${chalk.dim('Examples:')}
|
|
81
|
+
${chalk.cyan('$ fiftth use')} Interactively select a workspace
|
|
82
|
+
${chalk.cyan('$ fiftth use my-workspace')} Switch directly to a workspace by slug or name
|
|
83
|
+
${chalk.cyan('$ fiftth use --list')} List all workspaces
|
|
84
|
+
`,
|
|
85
|
+
)
|
|
86
|
+
.action(async (workspace: string | undefined, options: { list?: boolean }) => {
|
|
87
|
+
await useCommand(workspace, options).catch(handleError)
|
|
88
|
+
})
|
|
89
|
+
|
|
90
|
+
// ─── fiftth config ─────────────────────────────────────────────────────────
|
|
91
|
+
program
|
|
92
|
+
.command('config')
|
|
93
|
+
.description('Show current CLI configuration')
|
|
94
|
+
.action(() => {
|
|
95
|
+
const config = loadConfig()
|
|
96
|
+
console.log(`\n${section('CLI configuration')}\n`)
|
|
97
|
+
console.log(kv('Config file', cmd(getConfigPath())))
|
|
98
|
+
console.log(kv('Host', cmd(config.host)))
|
|
99
|
+
console.log(kv('Workspace', config.workspace ? cmd(config.workspace) : muted('(none)')))
|
|
100
|
+
console.log(kv('Logged in', config.token ? ok('yes') : fail('no')))
|
|
101
|
+
console.log()
|
|
102
|
+
})
|
|
103
|
+
|
|
104
|
+
// ─── fiftth repo ───────────────────────────────────────────────────────────
|
|
105
|
+
registerRepoCommands(program)
|
|
106
|
+
|
|
107
|
+
// ─── fiftth tasks ──────────────────────────────────────────────────────────
|
|
108
|
+
program
|
|
109
|
+
.command('tasks')
|
|
110
|
+
.description('List and select active tasks')
|
|
111
|
+
.action(async () => {
|
|
112
|
+
await tasksCommand().catch(handleError)
|
|
113
|
+
})
|
|
114
|
+
|
|
115
|
+
// ─── fiftth checkout ───────────────────────────────────────────────────────
|
|
116
|
+
program
|
|
117
|
+
.command('checkout <taskId>')
|
|
118
|
+
.description('Checkout the selected task branches for all linked repositories')
|
|
119
|
+
.action(async (taskId: string) => {
|
|
120
|
+
await checkoutCommand(taskId).catch(handleError)
|
|
121
|
+
})
|
|
122
|
+
|
|
123
|
+
// ─── Error handling ────────────────────────────────────────────────────────
|
|
124
|
+
function handleError(err: unknown): void {
|
|
125
|
+
if (err instanceof Error) {
|
|
126
|
+
console.error(`\n${fail(`Error: ${err.message}`)}\n`)
|
|
127
|
+
} else {
|
|
128
|
+
console.error(`\n${fail('An unexpected error occurred.')}\n`)
|
|
129
|
+
}
|
|
130
|
+
process.exit(1)
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
program.parse()
|
|
@@ -1,32 +1,32 @@
|
|
|
1
|
-
export interface SelectedTaskRepository {
|
|
2
|
-
repoName: string
|
|
3
|
-
branch: string
|
|
4
|
-
}
|
|
5
|
-
|
|
6
|
-
export interface SelectedTaskContext {
|
|
7
|
-
taskId: string
|
|
8
|
-
title: string
|
|
9
|
-
repositories: SelectedTaskRepository[]
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
let selectedTaskContext: SelectedTaskContext | null = null
|
|
13
|
-
|
|
14
|
-
export function setSelectedTaskContext(task: SelectedTaskContext): void {
|
|
15
|
-
selectedTaskContext = task
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
export function getSelectedTaskContext(): SelectedTaskContext | null {
|
|
19
|
-
return selectedTaskContext
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
export function getSelectedTaskBranch(repoName: string): string | undefined {
|
|
23
|
-
const repository = selectedTaskContext?.repositories.find(
|
|
24
|
-
(entry) => entry.repoName === repoName,
|
|
25
|
-
)
|
|
26
|
-
|
|
27
|
-
return repository?.branch
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
export function clearSelectedTaskContext(): void {
|
|
31
|
-
selectedTaskContext = null
|
|
32
|
-
}
|
|
1
|
+
export interface SelectedTaskRepository {
|
|
2
|
+
repoName: string
|
|
3
|
+
branch: string
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
export interface SelectedTaskContext {
|
|
7
|
+
taskId: string
|
|
8
|
+
title: string
|
|
9
|
+
repositories: SelectedTaskRepository[]
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
let selectedTaskContext: SelectedTaskContext | null = null
|
|
13
|
+
|
|
14
|
+
export function setSelectedTaskContext(task: SelectedTaskContext): void {
|
|
15
|
+
selectedTaskContext = task
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function getSelectedTaskContext(): SelectedTaskContext | null {
|
|
19
|
+
return selectedTaskContext
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function getSelectedTaskBranch(repoName: string): string | undefined {
|
|
23
|
+
const repository = selectedTaskContext?.repositories.find(
|
|
24
|
+
(entry) => entry.repoName === repoName,
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
return repository?.branch
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function clearSelectedTaskContext(): void {
|
|
31
|
+
selectedTaskContext = null
|
|
32
|
+
}
|
|
@@ -1,45 +1,53 @@
|
|
|
1
|
-
import { createApiClient } from '../api/client.js'
|
|
2
|
-
|
|
3
|
-
const ALLOWED_COLUMNS = new Set(['IN_PROGRESS', 'HUMAN_REVIEW'])
|
|
4
|
-
|
|
5
|
-
interface ApiTaskRepository {
|
|
6
|
-
fullName: string
|
|
7
|
-
githubPrBranch: string
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
interface ApiTask {
|
|
11
|
-
id: string
|
|
12
|
-
title: string
|
|
13
|
-
column: string
|
|
14
|
-
repositories?: ApiTaskRepository[]
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
export interface TaskRepository {
|
|
18
|
-
fullName: string
|
|
19
|
-
branch: string
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
export interface Task {
|
|
23
|
-
id: string
|
|
24
|
-
title: string
|
|
25
|
-
column: string
|
|
26
|
-
repositories: TaskRepository[]
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
export async function
|
|
30
|
-
const client = createApiClient()
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
1
|
+
import { createApiClient } from '../api/client.js'
|
|
2
|
+
|
|
3
|
+
const ALLOWED_COLUMNS = new Set(['IN_PROGRESS', 'HUMAN_REVIEW'])
|
|
4
|
+
|
|
5
|
+
interface ApiTaskRepository {
|
|
6
|
+
fullName: string
|
|
7
|
+
githubPrBranch: string
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
interface ApiTask {
|
|
11
|
+
id: string
|
|
12
|
+
title: string
|
|
13
|
+
column: string
|
|
14
|
+
repositories?: ApiTaskRepository[]
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface TaskRepository {
|
|
18
|
+
fullName: string
|
|
19
|
+
branch: string
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export interface Task {
|
|
23
|
+
id: string
|
|
24
|
+
title: string
|
|
25
|
+
column: string
|
|
26
|
+
repositories: TaskRepository[]
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export async function moveTaskToHumanReview(taskId: string): Promise<void> {
|
|
30
|
+
const client = createApiClient()
|
|
31
|
+
await client.post(`/tasks/${taskId}/move`, {
|
|
32
|
+
targetColumn: 'HUMAN_REVIEW',
|
|
33
|
+
targetOrder: 0,
|
|
34
|
+
})
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export async function fetchActiveTasks(workspaceId: string): Promise<Task[]> {
|
|
38
|
+
const client = createApiClient()
|
|
39
|
+
const query = new URLSearchParams({ workspaceId }).toString()
|
|
40
|
+
const tasks = await client.get<ApiTask[]>(`/tasks?${query}`)
|
|
41
|
+
|
|
42
|
+
return tasks
|
|
43
|
+
.filter((task) => ALLOWED_COLUMNS.has(task.column))
|
|
44
|
+
.map((task) => ({
|
|
45
|
+
id: task.id,
|
|
46
|
+
title: task.title,
|
|
47
|
+
column: task.column,
|
|
48
|
+
repositories: (task.repositories ?? []).map((repository) => ({
|
|
49
|
+
fullName: repository.fullName,
|
|
50
|
+
branch: repository.githubPrBranch,
|
|
51
|
+
})),
|
|
52
|
+
}))
|
|
53
|
+
}
|
package/src/utils/api.ts
CHANGED
|
@@ -1,41 +1,41 @@
|
|
|
1
|
-
import { loadConfig } from './config.js'
|
|
2
|
-
|
|
3
|
-
export interface Workspace {
|
|
4
|
-
id: string
|
|
5
|
-
name: string
|
|
6
|
-
slug: string
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
export interface ApiClient {
|
|
10
|
-
getWorkspaces(): Promise<Workspace[]>
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
export function createApiClient(): ApiClient {
|
|
14
|
-
const config = loadConfig()
|
|
15
|
-
|
|
16
|
-
async function request<T>(endpoint: string): Promise<T> {
|
|
17
|
-
const url = `${config.host}${endpoint}`
|
|
18
|
-
const response = await fetch(url, {
|
|
19
|
-
headers: {
|
|
20
|
-
Authorization: `Bearer ${config.token}`,
|
|
21
|
-
'Content-Type': 'application/json',
|
|
22
|
-
'User-Agent': 'fiftth-cli/0.1.0',
|
|
23
|
-
},
|
|
24
|
-
})
|
|
25
|
-
|
|
26
|
-
if (!response.ok) {
|
|
27
|
-
const body = await response.text().catch(() => '')
|
|
28
|
-
throw new Error(
|
|
29
|
-
`API request failed: ${response.status} ${response.statusText}${body ? ` — ${body}` : ''}`,
|
|
30
|
-
)
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
return response.json() as Promise<T>
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
return {
|
|
37
|
-
getWorkspaces(): Promise<Workspace[]> {
|
|
38
|
-
return request<Workspace[]>('/workspaces')
|
|
39
|
-
},
|
|
40
|
-
}
|
|
41
|
-
}
|
|
1
|
+
import { loadConfig } from './config.js'
|
|
2
|
+
|
|
3
|
+
export interface Workspace {
|
|
4
|
+
id: string
|
|
5
|
+
name: string
|
|
6
|
+
slug: string
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export interface ApiClient {
|
|
10
|
+
getWorkspaces(): Promise<Workspace[]>
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function createApiClient(): ApiClient {
|
|
14
|
+
const config = loadConfig()
|
|
15
|
+
|
|
16
|
+
async function request<T>(endpoint: string): Promise<T> {
|
|
17
|
+
const url = `${config.host}${endpoint}`
|
|
18
|
+
const response = await fetch(url, {
|
|
19
|
+
headers: {
|
|
20
|
+
Authorization: `Bearer ${config.token}`,
|
|
21
|
+
'Content-Type': 'application/json',
|
|
22
|
+
'User-Agent': 'fiftth-cli/0.1.0',
|
|
23
|
+
},
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
if (!response.ok) {
|
|
27
|
+
const body = await response.text().catch(() => '')
|
|
28
|
+
throw new Error(
|
|
29
|
+
`API request failed: ${response.status} ${response.statusText}${body ? ` — ${body}` : ''}`,
|
|
30
|
+
)
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
return response.json() as Promise<T>
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return {
|
|
37
|
+
getWorkspaces(): Promise<Workspace[]> {
|
|
38
|
+
return request<Workspace[]>('/workspaces')
|
|
39
|
+
},
|
|
40
|
+
}
|
|
41
|
+
}
|