@wavyx/pdcli 0.10.0 → 0.11.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/CHANGELOG.md +34 -0
- package/README.md +7 -1
- package/oclif.manifest.json +6369 -1598
- package/package.json +29 -2
- package/src/commands/activity/type/list.js +36 -0
- package/src/commands/deal/convert.js +112 -0
- package/src/commands/deal/follower/add.js +31 -0
- package/src/commands/deal/follower/list.js +38 -0
- package/src/commands/deal/follower/remove.js +44 -0
- package/src/commands/deal/list.js +7 -4
- package/src/commands/deal/participant/add.js +31 -0
- package/src/commands/deal/participant/list.js +46 -0
- package/src/commands/deal/participant/remove.js +53 -0
- package/src/commands/deal/summary.js +69 -0
- package/src/commands/field/create.js +72 -0
- package/src/commands/field/delete.js +56 -0
- package/src/commands/field/get.js +10 -1
- package/src/commands/field/list.js +10 -1
- package/src/commands/field/option/add.js +49 -0
- package/src/commands/field/option/remove.js +72 -0
- package/src/commands/field/update.js +53 -0
- package/src/commands/file/delete.js +41 -0
- package/src/commands/file/update.js +39 -0
- package/src/commands/filter/delete.js +41 -0
- package/src/commands/lead/convert.js +103 -0
- package/src/commands/note/comment/add.js +31 -0
- package/src/commands/note/comment/delete.js +51 -0
- package/src/commands/note/comment/list.js +43 -0
- package/src/commands/note/comment/update.js +37 -0
- package/src/commands/org/follower/add.js +31 -0
- package/src/commands/org/follower/list.js +41 -0
- package/src/commands/org/follower/remove.js +50 -0
- package/src/commands/org/relationship/add.js +44 -0
- package/src/commands/org/relationship/list.js +52 -0
- package/src/commands/org/relationship/remove.js +46 -0
- package/src/commands/person/follower/add.js +31 -0
- package/src/commands/person/follower/list.js +38 -0
- package/src/commands/person/follower/remove.js +48 -0
- package/src/commands/project/list.js +10 -4
- package/src/commands/search.js +66 -9
- package/src/commands/task/create.js +44 -0
- package/src/commands/task/delete.js +41 -0
- package/src/commands/task/get.js +26 -0
- package/src/commands/task/list.js +60 -0
- package/src/commands/task/update.js +72 -0
- package/src/lib/client.js +19 -0
- package/src/lib/fields.js +43 -12
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { Args, Flags } from '@oclif/core'
|
|
2
|
+
import chalk from 'chalk'
|
|
3
|
+
import BaseCommand from '../../../base-command.js'
|
|
4
|
+
import { confirmAction } from '../../../lib/confirm.js'
|
|
5
|
+
import { CliError } from '../../../lib/errors.js'
|
|
6
|
+
|
|
7
|
+
export default class OrgFollowerRemoveCommand extends BaseCommand {
|
|
8
|
+
static description = 'Remove a follower from an organization'
|
|
9
|
+
|
|
10
|
+
static examples = [
|
|
11
|
+
'<%= config.bin %> org follower remove 42 --user 5',
|
|
12
|
+
'<%= config.bin %> org follower remove 42 --user 5 --yes',
|
|
13
|
+
]
|
|
14
|
+
|
|
15
|
+
static args = {
|
|
16
|
+
id: Args.integer({ required: true, description: 'Organization ID' }),
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
static flags = {
|
|
20
|
+
...BaseCommand.baseFlags,
|
|
21
|
+
user: Flags.integer({ required: true, description: 'User ID' }),
|
|
22
|
+
yes: Flags.boolean({
|
|
23
|
+
char: 'y',
|
|
24
|
+
description: 'Skip the confirmation prompt',
|
|
25
|
+
default: false,
|
|
26
|
+
}),
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
async run() {
|
|
30
|
+
const { args, flags } = await this.parse(OrgFollowerRemoveCommand)
|
|
31
|
+
|
|
32
|
+
const ok = await confirmAction(
|
|
33
|
+
`Remove follower ${flags.user} from organization ${args.id}?`,
|
|
34
|
+
flags.yes,
|
|
35
|
+
{ default: false },
|
|
36
|
+
)
|
|
37
|
+
if (!ok) {
|
|
38
|
+
throw new CliError('Aborted', { exitCode: 1 })
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
await this.apiClient.del(
|
|
42
|
+
`/api/v2/organizations/${args.id}/followers/${flags.user}`,
|
|
43
|
+
)
|
|
44
|
+
this.log(
|
|
45
|
+
chalk.green(
|
|
46
|
+
`Removed follower ${flags.user} from organization ${args.id}`,
|
|
47
|
+
),
|
|
48
|
+
)
|
|
49
|
+
}
|
|
50
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { Flags } from '@oclif/core'
|
|
2
|
+
import BaseCommand from '../../../base-command.js'
|
|
3
|
+
import { outputRecord } from '../../../lib/entity-view.js'
|
|
4
|
+
|
|
5
|
+
export default class OrgRelationshipAddCommand extends BaseCommand {
|
|
6
|
+
static description =
|
|
7
|
+
'Create an organization relationship. For a parent relationship the ' +
|
|
8
|
+
'--owner organization is the parent and --linked is the daughter.'
|
|
9
|
+
|
|
10
|
+
static examples = [
|
|
11
|
+
'<%= config.bin %> org relationship add --type parent --owner 1481 --linked 1480',
|
|
12
|
+
'<%= config.bin %> org relationship add --type related --owner 1 --linked 2',
|
|
13
|
+
]
|
|
14
|
+
|
|
15
|
+
static flags = {
|
|
16
|
+
...BaseCommand.baseFlags,
|
|
17
|
+
type: Flags.string({
|
|
18
|
+
required: true,
|
|
19
|
+
description: 'Relationship type',
|
|
20
|
+
options: ['parent', 'related'],
|
|
21
|
+
}),
|
|
22
|
+
owner: Flags.integer({
|
|
23
|
+
required: true,
|
|
24
|
+
description: 'Owner organization ID (the parent for type parent)',
|
|
25
|
+
}),
|
|
26
|
+
linked: Flags.integer({
|
|
27
|
+
required: true,
|
|
28
|
+
description: 'Linked organization ID (the daughter for type parent)',
|
|
29
|
+
}),
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
async run() {
|
|
33
|
+
const { flags } = await this.parse(OrgRelationshipAddCommand)
|
|
34
|
+
|
|
35
|
+
const res = await this.apiClient.post('/api/v1/organizationRelationships', {
|
|
36
|
+
body: {
|
|
37
|
+
type: flags.type,
|
|
38
|
+
rel_owner_org_id: flags.owner,
|
|
39
|
+
rel_linked_org_id: flags.linked,
|
|
40
|
+
},
|
|
41
|
+
})
|
|
42
|
+
await outputRecord(this, res.data)
|
|
43
|
+
}
|
|
44
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { Flags } from '@oclif/core'
|
|
2
|
+
import BaseCommand from '../../../base-command.js'
|
|
3
|
+
import { collectPages } from '../../../lib/pagination.js'
|
|
4
|
+
|
|
5
|
+
const columns = {
|
|
6
|
+
id: { header: 'ID' },
|
|
7
|
+
type: { header: 'Type' },
|
|
8
|
+
owner: {
|
|
9
|
+
header: 'Owner Org',
|
|
10
|
+
get: (row) => row.rel_owner_org_id?.name ?? '',
|
|
11
|
+
},
|
|
12
|
+
linked: {
|
|
13
|
+
header: 'Linked Org',
|
|
14
|
+
get: (row) => row.rel_linked_org_id?.name ?? '',
|
|
15
|
+
},
|
|
16
|
+
related: {
|
|
17
|
+
header: 'Related',
|
|
18
|
+
get: (row) => row.related_organization_name ?? '',
|
|
19
|
+
},
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export default class OrgRelationshipListCommand extends BaseCommand {
|
|
23
|
+
static description = 'List relationships for an organization'
|
|
24
|
+
|
|
25
|
+
static examples = [
|
|
26
|
+
'<%= config.bin %> org relationship list --org 1481',
|
|
27
|
+
'<%= config.bin %> org relationship list --org 1481 --output json',
|
|
28
|
+
]
|
|
29
|
+
|
|
30
|
+
static flags = {
|
|
31
|
+
...BaseCommand.baseFlags,
|
|
32
|
+
org: Flags.integer({
|
|
33
|
+
required: true,
|
|
34
|
+
description: 'Organization ID to list relationships for',
|
|
35
|
+
}),
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
async run() {
|
|
39
|
+
const { flags } = await this.parse(OrgRelationshipListCommand)
|
|
40
|
+
const limit = flags.limit ?? 500
|
|
41
|
+
|
|
42
|
+
// The endpoint takes only org_id (no server-side pagination) — the
|
|
43
|
+
// client-side cap below is what --limit actually controls.
|
|
44
|
+
const query = { org_id: flags.org }
|
|
45
|
+
|
|
46
|
+
const items = await collectPages(
|
|
47
|
+
this.apiClient.pageV1('/api/v1/organizationRelationships', query),
|
|
48
|
+
limit,
|
|
49
|
+
)
|
|
50
|
+
await this.outputResults(items, columns)
|
|
51
|
+
}
|
|
52
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { Args, Flags } from '@oclif/core'
|
|
2
|
+
import chalk from 'chalk'
|
|
3
|
+
import BaseCommand from '../../../base-command.js'
|
|
4
|
+
import { confirmAction } from '../../../lib/confirm.js'
|
|
5
|
+
import { CliError } from '../../../lib/errors.js'
|
|
6
|
+
|
|
7
|
+
export default class OrgRelationshipRemoveCommand extends BaseCommand {
|
|
8
|
+
static description = 'Delete an organization relationship'
|
|
9
|
+
|
|
10
|
+
static examples = [
|
|
11
|
+
'<%= config.bin %> org relationship remove 7',
|
|
12
|
+
'<%= config.bin %> org relationship remove 7 --yes',
|
|
13
|
+
]
|
|
14
|
+
|
|
15
|
+
static args = {
|
|
16
|
+
id: Args.integer({
|
|
17
|
+
required: true,
|
|
18
|
+
description: 'Organization relationship ID',
|
|
19
|
+
}),
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
static flags = {
|
|
23
|
+
...BaseCommand.baseFlags,
|
|
24
|
+
yes: Flags.boolean({
|
|
25
|
+
char: 'y',
|
|
26
|
+
description: 'Skip the confirmation prompt',
|
|
27
|
+
default: false,
|
|
28
|
+
}),
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
async run() {
|
|
32
|
+
const { args, flags } = await this.parse(OrgRelationshipRemoveCommand)
|
|
33
|
+
|
|
34
|
+
const ok = await confirmAction(
|
|
35
|
+
`Delete organization relationship ${args.id}?`,
|
|
36
|
+
flags.yes,
|
|
37
|
+
{ default: false },
|
|
38
|
+
)
|
|
39
|
+
if (!ok) {
|
|
40
|
+
throw new CliError('Aborted', { exitCode: 1 })
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
await this.apiClient.del(`/api/v1/organizationRelationships/${args.id}`)
|
|
44
|
+
this.log(chalk.green(`Deleted organization relationship ${args.id}`))
|
|
45
|
+
}
|
|
46
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { Args, Flags } from '@oclif/core'
|
|
2
|
+
import BaseCommand from '../../../base-command.js'
|
|
3
|
+
import { outputRecord } from '../../../lib/entity-view.js'
|
|
4
|
+
|
|
5
|
+
export default class PersonFollowerAddCommand extends BaseCommand {
|
|
6
|
+
static description = 'Add a follower (user) to a person'
|
|
7
|
+
|
|
8
|
+
static examples = [
|
|
9
|
+
'<%= config.bin %> person follower add 42 --user 5',
|
|
10
|
+
'<%= config.bin %> person follower add 42 --user 5 --output json',
|
|
11
|
+
]
|
|
12
|
+
|
|
13
|
+
static args = {
|
|
14
|
+
id: Args.integer({ required: true, description: 'Person ID' }),
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
static flags = {
|
|
18
|
+
...BaseCommand.baseFlags,
|
|
19
|
+
user: Flags.integer({ required: true, description: 'User ID' }),
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
async run() {
|
|
23
|
+
const { args, flags } = await this.parse(PersonFollowerAddCommand)
|
|
24
|
+
|
|
25
|
+
const res = await this.apiClient.post(
|
|
26
|
+
`/api/v2/persons/${args.id}/followers`,
|
|
27
|
+
{ body: { user_id: flags.user } },
|
|
28
|
+
)
|
|
29
|
+
await outputRecord(this, res.data)
|
|
30
|
+
}
|
|
31
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { Args } from '@oclif/core'
|
|
2
|
+
import BaseCommand from '../../../base-command.js'
|
|
3
|
+
import { collectPages } from '../../../lib/pagination.js'
|
|
4
|
+
|
|
5
|
+
const columns = {
|
|
6
|
+
user_id: { header: 'User' },
|
|
7
|
+
add_time: { header: 'Added' },
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export default class PersonFollowerListCommand extends BaseCommand {
|
|
11
|
+
static description = 'List followers of a person'
|
|
12
|
+
|
|
13
|
+
static examples = [
|
|
14
|
+
'<%= config.bin %> person follower list 42',
|
|
15
|
+
'<%= config.bin %> person follower list 42 --output json',
|
|
16
|
+
]
|
|
17
|
+
|
|
18
|
+
static args = {
|
|
19
|
+
id: Args.integer({ required: true, description: 'Person ID' }),
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
static flags = {
|
|
23
|
+
...BaseCommand.baseFlags,
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
async run() {
|
|
27
|
+
const { args, flags } = await this.parse(PersonFollowerListCommand)
|
|
28
|
+
const limit = flags.limit ?? 500
|
|
29
|
+
|
|
30
|
+
const query = { limit: Math.min(limit, 500) }
|
|
31
|
+
|
|
32
|
+
const items = await collectPages(
|
|
33
|
+
this.apiClient.pageV2(`/api/v2/persons/${args.id}/followers`, query),
|
|
34
|
+
limit,
|
|
35
|
+
)
|
|
36
|
+
await this.outputResults(items, columns)
|
|
37
|
+
}
|
|
38
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { Args, Flags } from '@oclif/core'
|
|
2
|
+
import chalk from 'chalk'
|
|
3
|
+
import BaseCommand from '../../../base-command.js'
|
|
4
|
+
import { confirmAction } from '../../../lib/confirm.js'
|
|
5
|
+
import { CliError } from '../../../lib/errors.js'
|
|
6
|
+
|
|
7
|
+
export default class PersonFollowerRemoveCommand extends BaseCommand {
|
|
8
|
+
static description = 'Remove a follower from a person'
|
|
9
|
+
|
|
10
|
+
static examples = [
|
|
11
|
+
'<%= config.bin %> person follower remove 42 --user 5',
|
|
12
|
+
'<%= config.bin %> person follower remove 42 --user 5 --yes',
|
|
13
|
+
]
|
|
14
|
+
|
|
15
|
+
static args = {
|
|
16
|
+
id: Args.integer({ required: true, description: 'Person ID' }),
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
static flags = {
|
|
20
|
+
...BaseCommand.baseFlags,
|
|
21
|
+
user: Flags.integer({ required: true, description: 'User ID' }),
|
|
22
|
+
yes: Flags.boolean({
|
|
23
|
+
char: 'y',
|
|
24
|
+
description: 'Skip the confirmation prompt',
|
|
25
|
+
default: false,
|
|
26
|
+
}),
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
async run() {
|
|
30
|
+
const { args, flags } = await this.parse(PersonFollowerRemoveCommand)
|
|
31
|
+
|
|
32
|
+
const ok = await confirmAction(
|
|
33
|
+
`Remove follower ${flags.user} from person ${args.id}?`,
|
|
34
|
+
flags.yes,
|
|
35
|
+
{ default: false },
|
|
36
|
+
)
|
|
37
|
+
if (!ok) {
|
|
38
|
+
throw new CliError('Aborted', { exitCode: 1 })
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
await this.apiClient.del(
|
|
42
|
+
`/api/v2/persons/${args.id}/followers/${flags.user}`,
|
|
43
|
+
)
|
|
44
|
+
this.log(
|
|
45
|
+
chalk.green(`Removed follower ${flags.user} from person ${args.id}`),
|
|
46
|
+
)
|
|
47
|
+
}
|
|
48
|
+
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { Flags } from '@oclif/core'
|
|
1
2
|
import BaseCommand from '../../base-command.js'
|
|
2
3
|
import { collectPages } from '../../lib/pagination.js'
|
|
3
4
|
|
|
@@ -20,6 +21,10 @@ export default class ProjectListCommand extends BaseCommand {
|
|
|
20
21
|
|
|
21
22
|
static flags = {
|
|
22
23
|
...BaseCommand.baseFlags,
|
|
24
|
+
archived: Flags.boolean({
|
|
25
|
+
description: 'List archived projects instead of active ones',
|
|
26
|
+
default: false,
|
|
27
|
+
}),
|
|
23
28
|
}
|
|
24
29
|
|
|
25
30
|
async run() {
|
|
@@ -30,10 +35,11 @@ export default class ProjectListCommand extends BaseCommand {
|
|
|
30
35
|
limit: Math.min(limit, 100),
|
|
31
36
|
}
|
|
32
37
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
38
|
+
// Archived projects share the same cursor pager as active projects.
|
|
39
|
+
const path = flags.archived
|
|
40
|
+
? '/api/v2/projects/archived'
|
|
41
|
+
: '/api/v2/projects'
|
|
42
|
+
const items = await collectPages(this.apiClient.pageV2(path, query), limit)
|
|
37
43
|
await this.outputResults(items, columns)
|
|
38
44
|
}
|
|
39
45
|
}
|
package/src/commands/search.js
CHANGED
|
@@ -11,6 +11,15 @@ const columns = {
|
|
|
11
11
|
},
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
+
// Item types that have a dedicated v2 scoped-search endpoint (narrower OAuth
|
|
15
|
+
// scope, narrower scope + 500-row pages vs itemSearch). Singular type → scoped endpoint path.
|
|
16
|
+
const SCOPED_PATHS = {
|
|
17
|
+
deal: '/api/v2/deals/search',
|
|
18
|
+
person: '/api/v2/persons/search',
|
|
19
|
+
organization: '/api/v2/organizations/search',
|
|
20
|
+
product: '/api/v2/products/search',
|
|
21
|
+
}
|
|
22
|
+
|
|
14
23
|
export default class SearchCommand extends BaseCommand {
|
|
15
24
|
static description =
|
|
16
25
|
'Search across deals, persons, organizations, products, leads, files, and projects'
|
|
@@ -18,6 +27,7 @@ export default class SearchCommand extends BaseCommand {
|
|
|
18
27
|
static examples = [
|
|
19
28
|
'<%= config.bin %> search "acme"',
|
|
20
29
|
'<%= config.bin %> search "acme" --item-types deal,person --output json',
|
|
30
|
+
'<%= config.bin %> search "acme" --item-types deal --status open',
|
|
21
31
|
]
|
|
22
32
|
|
|
23
33
|
static args = {
|
|
@@ -34,6 +44,16 @@ export default class SearchCommand extends BaseCommand {
|
|
|
34
44
|
description: 'Exact match (allows 1-character terms)',
|
|
35
45
|
default: false,
|
|
36
46
|
}),
|
|
47
|
+
status: Flags.string({
|
|
48
|
+
description: 'Filter by deal status (only with --item-types deal)',
|
|
49
|
+
options: ['open', 'won', 'lost'],
|
|
50
|
+
}),
|
|
51
|
+
person: Flags.integer({
|
|
52
|
+
description: 'Filter by person ID (only with --item-types deal)',
|
|
53
|
+
}),
|
|
54
|
+
org: Flags.integer({
|
|
55
|
+
description: 'Filter by organization ID (only with --item-types deal)',
|
|
56
|
+
}),
|
|
37
57
|
}
|
|
38
58
|
|
|
39
59
|
async run() {
|
|
@@ -47,15 +67,52 @@ export default class SearchCommand extends BaseCommand {
|
|
|
47
67
|
)
|
|
48
68
|
}
|
|
49
69
|
|
|
50
|
-
//
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
70
|
+
// A single routable item type uses the scoped endpoint; everything else
|
|
71
|
+
// (multi-type, no type, or a non-routable type like lead/file) stays on
|
|
72
|
+
// the generic itemSearch wrapper.
|
|
73
|
+
const types = flags['item-types']
|
|
74
|
+
?.split(',')
|
|
75
|
+
.map((t) => t.trim())
|
|
76
|
+
.filter(Boolean)
|
|
77
|
+
const scopedType =
|
|
78
|
+
types?.length === 1 && SCOPED_PATHS[types[0]] ? types[0] : undefined
|
|
79
|
+
|
|
80
|
+
// --status/--person/--org narrow deal searches only and are accepted only
|
|
81
|
+
// when routing to the deals/search endpoint.
|
|
82
|
+
if (
|
|
83
|
+
(flags.status != null || flags.person != null || flags.org != null) &&
|
|
84
|
+
scopedType !== 'deal'
|
|
85
|
+
) {
|
|
86
|
+
throw new CliError(
|
|
87
|
+
'--status, --person, and --org are valid only with --item-types deal',
|
|
88
|
+
{ exitCode: 64 },
|
|
89
|
+
)
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
let body
|
|
93
|
+
if (scopedType) {
|
|
94
|
+
// Search costs 20 rate-limit tokens — single request, no auto-paging.
|
|
95
|
+
body = await this.apiClient.get(SCOPED_PATHS[scopedType], {
|
|
96
|
+
query: {
|
|
97
|
+
term: args.term,
|
|
98
|
+
exact_match: flags.exact ? true : undefined,
|
|
99
|
+
status: scopedType === 'deal' ? flags.status : undefined,
|
|
100
|
+
person_id: scopedType === 'deal' ? flags.person : undefined,
|
|
101
|
+
organization_id: scopedType === 'deal' ? flags.org : undefined,
|
|
102
|
+
limit: flags.limit,
|
|
103
|
+
},
|
|
104
|
+
})
|
|
105
|
+
} else {
|
|
106
|
+
// Search costs 20 rate-limit tokens — single request, no auto-paging.
|
|
107
|
+
body = await this.apiClient.get('/api/v2/itemSearch', {
|
|
108
|
+
query: {
|
|
109
|
+
term: args.term,
|
|
110
|
+
item_types: flags['item-types'],
|
|
111
|
+
exact_match: flags.exact ? true : undefined,
|
|
112
|
+
limit: flags.limit,
|
|
113
|
+
},
|
|
114
|
+
})
|
|
115
|
+
}
|
|
59
116
|
|
|
60
117
|
const items = (body.data?.items ?? []).map((entry) => ({
|
|
61
118
|
...entry.item,
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { Flags } from '@oclif/core'
|
|
2
|
+
import BaseCommand from '../../base-command.js'
|
|
3
|
+
import { buildWriteBody } from '../../lib/input.js'
|
|
4
|
+
import { outputRecord } from '../../lib/entity-view.js'
|
|
5
|
+
|
|
6
|
+
export default class TaskCreateCommand extends BaseCommand {
|
|
7
|
+
static description = 'Create a task'
|
|
8
|
+
|
|
9
|
+
static examples = [
|
|
10
|
+
'<%= config.bin %> task create --title "Write spec" --project 3',
|
|
11
|
+
'<%= config.bin %> task create --title "Subtask" --project 3 --parent 5 --assignee 7',
|
|
12
|
+
'<%= config.bin %> task create --title "Raw" --project 3 --body \'{"priority":5}\'',
|
|
13
|
+
]
|
|
14
|
+
|
|
15
|
+
static flags = {
|
|
16
|
+
...BaseCommand.baseFlags,
|
|
17
|
+
title: Flags.string({ required: true, description: 'Task title' }),
|
|
18
|
+
project: Flags.integer({ required: true, description: 'Project ID' }),
|
|
19
|
+
description: Flags.string({ description: 'Task description' }),
|
|
20
|
+
assignee: Flags.integer({ description: 'Assignee (user) ID' }),
|
|
21
|
+
'due-date': Flags.string({ description: 'Due date (YYYY-MM-DD)' }),
|
|
22
|
+
parent: Flags.integer({ description: 'Parent task ID' }),
|
|
23
|
+
body: Flags.string({ description: 'Raw JSON body to merge (flags win)' }),
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
async run() {
|
|
27
|
+
const { flags } = await this.parse(TaskCreateCommand)
|
|
28
|
+
|
|
29
|
+
const body = buildWriteBody({
|
|
30
|
+
typed: {
|
|
31
|
+
title: flags.title,
|
|
32
|
+
project_id: flags.project,
|
|
33
|
+
description: flags.description,
|
|
34
|
+
assignee_id: flags.assignee,
|
|
35
|
+
due_date: flags['due-date'],
|
|
36
|
+
parent_task_id: flags.parent,
|
|
37
|
+
},
|
|
38
|
+
rawBody: flags.body,
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
const res = await this.apiClient.post('/api/v2/tasks', { body })
|
|
42
|
+
await outputRecord(this, res.data)
|
|
43
|
+
}
|
|
44
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { Args, Flags } from '@oclif/core'
|
|
2
|
+
import chalk from 'chalk'
|
|
3
|
+
import BaseCommand from '../../base-command.js'
|
|
4
|
+
import { confirmAction } from '../../lib/confirm.js'
|
|
5
|
+
import { CliError } from '../../lib/errors.js'
|
|
6
|
+
|
|
7
|
+
export default class TaskDeleteCommand extends BaseCommand {
|
|
8
|
+
static description = 'Delete a task'
|
|
9
|
+
|
|
10
|
+
static examples = [
|
|
11
|
+
'<%= config.bin %> task delete 7',
|
|
12
|
+
'<%= config.bin %> task delete 7 --yes',
|
|
13
|
+
]
|
|
14
|
+
|
|
15
|
+
static args = {
|
|
16
|
+
id: Args.integer({ required: true, description: 'Task ID' }),
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
static flags = {
|
|
20
|
+
...BaseCommand.baseFlags,
|
|
21
|
+
yes: Flags.boolean({
|
|
22
|
+
char: 'y',
|
|
23
|
+
description: 'Skip the confirmation prompt',
|
|
24
|
+
default: false,
|
|
25
|
+
}),
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
async run() {
|
|
29
|
+
const { args, flags } = await this.parse(TaskDeleteCommand)
|
|
30
|
+
|
|
31
|
+
const ok = await confirmAction(`Delete task ${args.id}?`, flags.yes, {
|
|
32
|
+
default: false,
|
|
33
|
+
})
|
|
34
|
+
if (!ok) {
|
|
35
|
+
throw new CliError('Aborted', { exitCode: 1 })
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
await this.apiClient.del(`/api/v2/tasks/${args.id}`)
|
|
39
|
+
this.log(chalk.green(`Deleted task ${args.id}`))
|
|
40
|
+
}
|
|
41
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { Args } from '@oclif/core'
|
|
2
|
+
import BaseCommand from '../../base-command.js'
|
|
3
|
+
import { outputRecord } from '../../lib/entity-view.js'
|
|
4
|
+
|
|
5
|
+
export default class TaskGetCommand extends BaseCommand {
|
|
6
|
+
static description = 'Get a task by ID'
|
|
7
|
+
|
|
8
|
+
static examples = [
|
|
9
|
+
'<%= config.bin %> task get 9',
|
|
10
|
+
'<%= config.bin %> task get 9 --output json',
|
|
11
|
+
]
|
|
12
|
+
|
|
13
|
+
static flags = {
|
|
14
|
+
...BaseCommand.baseFlags,
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
static args = {
|
|
18
|
+
id: Args.integer({ required: true, description: 'Task ID' }),
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
async run() {
|
|
22
|
+
const { args } = await this.parse(TaskGetCommand)
|
|
23
|
+
const body = await this.apiClient.get(`/api/v2/tasks/${args.id}`)
|
|
24
|
+
await outputRecord(this, body.data)
|
|
25
|
+
}
|
|
26
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { Flags } from '@oclif/core'
|
|
2
|
+
import BaseCommand from '../../base-command.js'
|
|
3
|
+
import { collectPages } from '../../lib/pagination.js'
|
|
4
|
+
|
|
5
|
+
const columns = {
|
|
6
|
+
id: { header: 'ID' },
|
|
7
|
+
title: { header: 'Title' },
|
|
8
|
+
project_id: { header: 'Project' },
|
|
9
|
+
assignee_id: {
|
|
10
|
+
header: 'Assignee',
|
|
11
|
+
// v2 returns assignee_ids (array); surface the first for the column.
|
|
12
|
+
get: (row) => row.assignee_ids?.[0] ?? '',
|
|
13
|
+
},
|
|
14
|
+
due_date: { header: 'Due' },
|
|
15
|
+
done: { header: 'Done', get: (row) => (row.is_done ? 'yes' : 'no') },
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export default class TaskListCommand extends BaseCommand {
|
|
19
|
+
static description = 'List tasks'
|
|
20
|
+
|
|
21
|
+
static examples = [
|
|
22
|
+
'<%= config.bin %> task list',
|
|
23
|
+
'<%= config.bin %> task list --project 3 --todo',
|
|
24
|
+
'<%= config.bin %> task list --assignee 7 --output json',
|
|
25
|
+
]
|
|
26
|
+
|
|
27
|
+
static flags = {
|
|
28
|
+
...BaseCommand.baseFlags,
|
|
29
|
+
project: Flags.integer({ description: 'Filter by project ID' }),
|
|
30
|
+
assignee: Flags.integer({ description: 'Filter by assignee (user) ID' }),
|
|
31
|
+
parent: Flags.integer({ description: 'Filter by parent task ID' }),
|
|
32
|
+
done: Flags.boolean({
|
|
33
|
+
description: 'Only completed tasks',
|
|
34
|
+
exclusive: ['todo'],
|
|
35
|
+
}),
|
|
36
|
+
todo: Flags.boolean({
|
|
37
|
+
description: 'Only open (not done) tasks',
|
|
38
|
+
exclusive: ['done'],
|
|
39
|
+
}),
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
async run() {
|
|
43
|
+
const { flags } = await this.parse(TaskListCommand)
|
|
44
|
+
const limit = flags.limit ?? 500
|
|
45
|
+
|
|
46
|
+
const query = {
|
|
47
|
+
project_id: flags.project,
|
|
48
|
+
assignee_id: flags.assignee,
|
|
49
|
+
parent_task_id: flags.parent,
|
|
50
|
+
is_done: flags.done ? true : flags.todo ? false : undefined,
|
|
51
|
+
limit: Math.min(limit, 500),
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const items = await collectPages(
|
|
55
|
+
this.apiClient.pageV2('/api/v2/tasks', query),
|
|
56
|
+
limit,
|
|
57
|
+
)
|
|
58
|
+
await this.outputResults(items, columns)
|
|
59
|
+
}
|
|
60
|
+
}
|