@uzukko/agentpm 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/bin/taskapp.js +2 -0
- package/dist/api-client.d.ts +2 -0
- package/dist/api-client.js +27 -0
- package/dist/api-client.js.map +1 -0
- package/dist/commands/activity.d.ts +2 -0
- package/dist/commands/activity.js +88 -0
- package/dist/commands/activity.js.map +1 -0
- package/dist/commands/ball.d.ts +2 -0
- package/dist/commands/ball.js +69 -0
- package/dist/commands/ball.js.map +1 -0
- package/dist/commands/client.d.ts +2 -0
- package/dist/commands/client.js +149 -0
- package/dist/commands/client.js.map +1 -0
- package/dist/commands/config-cmd.d.ts +2 -0
- package/dist/commands/config-cmd.js +95 -0
- package/dist/commands/config-cmd.js.map +1 -0
- package/dist/commands/meeting.d.ts +2 -0
- package/dist/commands/meeting.js +100 -0
- package/dist/commands/meeting.js.map +1 -0
- package/dist/commands/milestone.d.ts +2 -0
- package/dist/commands/milestone.js +98 -0
- package/dist/commands/milestone.js.map +1 -0
- package/dist/commands/minutes.d.ts +2 -0
- package/dist/commands/minutes.js +62 -0
- package/dist/commands/minutes.js.map +1 -0
- package/dist/commands/review.d.ts +2 -0
- package/dist/commands/review.js +98 -0
- package/dist/commands/review.js.map +1 -0
- package/dist/commands/scheduling.d.ts +2 -0
- package/dist/commands/scheduling.js +184 -0
- package/dist/commands/scheduling.js.map +1 -0
- package/dist/commands/space.d.ts +2 -0
- package/dist/commands/space.js +69 -0
- package/dist/commands/space.js.map +1 -0
- package/dist/commands/task.d.ts +2 -0
- package/dist/commands/task.js +192 -0
- package/dist/commands/task.js.map +1 -0
- package/dist/commands/wiki.d.ts +2 -0
- package/dist/commands/wiki.js +121 -0
- package/dist/commands/wiki.js.map +1 -0
- package/dist/config.d.ts +8 -0
- package/dist/config.js +41 -0
- package/dist/config.js.map +1 -0
- package/dist/defaults.d.ts +4 -0
- package/dist/defaults.js +8 -0
- package/dist/defaults.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +48 -0
- package/dist/index.js.map +1 -0
- package/dist/output.d.ts +2 -0
- package/dist/output.js +140 -0
- package/dist/output.js.map +1 -0
- package/package.json +31 -0
- package/src/api-client.ts +32 -0
- package/src/commands/activity.ts +83 -0
- package/src/commands/ball.ts +64 -0
- package/src/commands/client.ts +135 -0
- package/src/commands/config-cmd.ts +106 -0
- package/src/commands/meeting.ts +91 -0
- package/src/commands/milestone.ts +89 -0
- package/src/commands/minutes.ts +57 -0
- package/src/commands/review.ts +89 -0
- package/src/commands/scheduling.ts +171 -0
- package/src/commands/space.ts +62 -0
- package/src/commands/task.ts +179 -0
- package/src/commands/wiki.ts +110 -0
- package/src/config.ts +56 -0
- package/src/index.ts +51 -0
- package/src/output.ts +139 -0
- package/tsconfig.json +19 -0
package/package.json
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@uzukko/agentpm",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "CLI for TaskApp - AI-first task management",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"bin": {
|
|
8
|
+
"agentpm": "./bin/taskapp.js"
|
|
9
|
+
},
|
|
10
|
+
"scripts": {
|
|
11
|
+
"build": "tsc",
|
|
12
|
+
"dev": "tsc --watch",
|
|
13
|
+
"prepublishOnly": "npm run build"
|
|
14
|
+
},
|
|
15
|
+
"dependencies": {
|
|
16
|
+
"commander": "^13.0.0",
|
|
17
|
+
"chalk": "^5.3.0",
|
|
18
|
+
"cli-table3": "^0.6.5"
|
|
19
|
+
},
|
|
20
|
+
"devDependencies": {
|
|
21
|
+
"@types/node": "^20.10.0",
|
|
22
|
+
"typescript": "^5.3.0"
|
|
23
|
+
},
|
|
24
|
+
"engines": {
|
|
25
|
+
"node": ">=18.0.0"
|
|
26
|
+
},
|
|
27
|
+
"publishConfig": {
|
|
28
|
+
"access": "public"
|
|
29
|
+
},
|
|
30
|
+
"license": "MIT"
|
|
31
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
let _apiUrl: string | undefined
|
|
2
|
+
let _apiKey: string | undefined
|
|
3
|
+
|
|
4
|
+
export function setApiConfig(apiUrl: string, apiKey: string): void {
|
|
5
|
+
_apiUrl = apiUrl
|
|
6
|
+
_apiKey = apiKey
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export async function callTool(toolName: string, params: Record<string, unknown>): Promise<unknown> {
|
|
10
|
+
if (!_apiUrl || !_apiKey) {
|
|
11
|
+
console.error('Error: Not configured. Run: agentpm login')
|
|
12
|
+
process.exit(1)
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const url = `${_apiUrl.replace(/\/$/, '')}/api/tools`
|
|
16
|
+
|
|
17
|
+
const response = await fetch(url, {
|
|
18
|
+
method: 'POST',
|
|
19
|
+
headers: {
|
|
20
|
+
'Content-Type': 'application/json',
|
|
21
|
+
'Authorization': `Bearer ${_apiKey}`,
|
|
22
|
+
},
|
|
23
|
+
body: JSON.stringify({ tool: toolName, params }),
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
if (!response.ok) {
|
|
27
|
+
const body = await response.json().catch(() => ({ error: `HTTP ${response.status}` })) as { error?: string; details?: string }
|
|
28
|
+
throw new Error(body.error || body.details || `HTTP ${response.status}`)
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return response.json()
|
|
32
|
+
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { Command } from 'commander'
|
|
2
|
+
import { resolveSpaceId } from '../config.js'
|
|
3
|
+
import { callTool } from '../api-client.js'
|
|
4
|
+
import { output, outputError } from '../output.js'
|
|
5
|
+
|
|
6
|
+
export function registerActivityCommands(program: Command): void {
|
|
7
|
+
const activity = program.command('activity').description('Activity log')
|
|
8
|
+
|
|
9
|
+
activity
|
|
10
|
+
.command('search')
|
|
11
|
+
.description('Search activity logs')
|
|
12
|
+
.option('-s, --space-id <uuid>', 'Space UUID')
|
|
13
|
+
.option('--entity-table <table>', 'Filter by table name')
|
|
14
|
+
.option('--entity-id <uuid>', 'Filter by entity ID')
|
|
15
|
+
.option('--actor-id <uuid>', 'Filter by actor ID')
|
|
16
|
+
.option('--action <action>', 'Filter by action')
|
|
17
|
+
.option('--from <datetime>', 'Start datetime (ISO8601)')
|
|
18
|
+
.option('--to <datetime>', 'End datetime (ISO8601)')
|
|
19
|
+
.option('--session-id <uuid>', 'Filter by session ID')
|
|
20
|
+
.option('--limit <n>', 'Max results', '100')
|
|
21
|
+
.action(async (opts) => {
|
|
22
|
+
try {
|
|
23
|
+
const result = await callTool('activity_search', {
|
|
24
|
+
spaceId: resolveSpaceId(opts),
|
|
25
|
+
entityTable: opts.entityTable,
|
|
26
|
+
entityId: opts.entityId,
|
|
27
|
+
actorId: opts.actorId,
|
|
28
|
+
action: opts.action,
|
|
29
|
+
from: opts.from,
|
|
30
|
+
to: opts.to,
|
|
31
|
+
sessionId: opts.sessionId,
|
|
32
|
+
limit: parseInt(opts.limit),
|
|
33
|
+
})
|
|
34
|
+
output(result, program.opts().json)
|
|
35
|
+
} catch (e) { outputError(e, program.opts().json) }
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
activity
|
|
39
|
+
.command('log')
|
|
40
|
+
.description('Create an activity log entry')
|
|
41
|
+
.option('-s, --space-id <uuid>', 'Space UUID')
|
|
42
|
+
.requiredOption('--entity-table <table>', 'Table name')
|
|
43
|
+
.requiredOption('--entity-id <uuid>', 'Entity ID')
|
|
44
|
+
.requiredOption('--action <action>', 'Action')
|
|
45
|
+
.option('--actor-type <type>', 'user|system|ai|service', 'ai')
|
|
46
|
+
.option('--actor-service <service>', 'Service name')
|
|
47
|
+
.option('--entity-display <name>', 'Display name')
|
|
48
|
+
.option('--reason <reason>', 'Reason')
|
|
49
|
+
.option('--status <status>', 'ok|error|warning', 'ok')
|
|
50
|
+
.action(async (opts) => {
|
|
51
|
+
try {
|
|
52
|
+
const result = await callTool('activity_log', {
|
|
53
|
+
spaceId: resolveSpaceId(opts),
|
|
54
|
+
entityTable: opts.entityTable,
|
|
55
|
+
entityId: opts.entityId,
|
|
56
|
+
action: opts.action,
|
|
57
|
+
actorType: opts.actorType,
|
|
58
|
+
actorService: opts.actorService,
|
|
59
|
+
entityDisplay: opts.entityDisplay,
|
|
60
|
+
reason: opts.reason,
|
|
61
|
+
status: opts.status,
|
|
62
|
+
})
|
|
63
|
+
output(result, program.opts().json)
|
|
64
|
+
} catch (e) { outputError(e, program.opts().json) }
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
activity
|
|
68
|
+
.command('history')
|
|
69
|
+
.description('Get entity change history')
|
|
70
|
+
.requiredOption('--entity-table <table>', 'Table name')
|
|
71
|
+
.requiredOption('--entity-id <uuid>', 'Entity ID')
|
|
72
|
+
.option('--limit <n>', 'Max results', '50')
|
|
73
|
+
.action(async (opts) => {
|
|
74
|
+
try {
|
|
75
|
+
const result = await callTool('activity_entity_history', {
|
|
76
|
+
entityTable: opts.entityTable,
|
|
77
|
+
entityId: opts.entityId,
|
|
78
|
+
limit: parseInt(opts.limit),
|
|
79
|
+
})
|
|
80
|
+
output(result, program.opts().json)
|
|
81
|
+
} catch (e) { outputError(e, program.opts().json) }
|
|
82
|
+
})
|
|
83
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { Command } from 'commander'
|
|
2
|
+
import { resolveSpaceId } from '../config.js'
|
|
3
|
+
import { callTool } from '../api-client.js'
|
|
4
|
+
import { output, outputError } from '../output.js'
|
|
5
|
+
|
|
6
|
+
export function registerBallCommands(program: Command): void {
|
|
7
|
+
const ball = program.command('ball').description('Ball (ownership) management')
|
|
8
|
+
|
|
9
|
+
ball
|
|
10
|
+
.command('pass')
|
|
11
|
+
.description('Pass ball ownership')
|
|
12
|
+
.option('-s, --space-id <uuid>', 'Space UUID')
|
|
13
|
+
.requiredOption('--task-id <uuid>', 'Task UUID')
|
|
14
|
+
.requiredOption('--ball <side>', 'New ball owner: client|internal')
|
|
15
|
+
.option('--client-owner-ids <ids...>', 'Client owner UUIDs')
|
|
16
|
+
.option('--internal-owner-ids <ids...>', 'Internal owner UUIDs')
|
|
17
|
+
.option('--reason <reason>', 'Reason for passing')
|
|
18
|
+
.action(async (opts) => {
|
|
19
|
+
try {
|
|
20
|
+
const result = await callTool('ball_pass', {
|
|
21
|
+
spaceId: resolveSpaceId(opts),
|
|
22
|
+
taskId: opts.taskId,
|
|
23
|
+
ball: opts.ball,
|
|
24
|
+
clientOwnerIds: opts.clientOwnerIds || [],
|
|
25
|
+
internalOwnerIds: opts.internalOwnerIds || [],
|
|
26
|
+
reason: opts.reason,
|
|
27
|
+
})
|
|
28
|
+
output(result, program.opts().json)
|
|
29
|
+
} catch (e) { outputError(e, program.opts().json) }
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
ball
|
|
33
|
+
.command('query')
|
|
34
|
+
.description('Query tasks by ball side')
|
|
35
|
+
.option('-s, --space-id <uuid>', 'Space UUID')
|
|
36
|
+
.requiredOption('--ball <side>', 'Ball side: client|internal')
|
|
37
|
+
.option('--include-owners', 'Include owner info')
|
|
38
|
+
.option('--limit <n>', 'Max results', '50')
|
|
39
|
+
.action(async (opts) => {
|
|
40
|
+
try {
|
|
41
|
+
const result = await callTool('ball_query', {
|
|
42
|
+
spaceId: resolveSpaceId(opts),
|
|
43
|
+
ball: opts.ball,
|
|
44
|
+
includeOwners: opts.includeOwners || false,
|
|
45
|
+
limit: parseInt(opts.limit),
|
|
46
|
+
})
|
|
47
|
+
output(result, program.opts().json)
|
|
48
|
+
} catch (e) { outputError(e, program.opts().json) }
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
// Dashboard as top-level shortcut
|
|
52
|
+
program
|
|
53
|
+
.command('dashboard')
|
|
54
|
+
.description('Get project dashboard')
|
|
55
|
+
.option('-s, --space-id <uuid>', 'Space UUID')
|
|
56
|
+
.action(async (opts) => {
|
|
57
|
+
try {
|
|
58
|
+
const result = await callTool('dashboard_get', {
|
|
59
|
+
spaceId: resolveSpaceId(opts),
|
|
60
|
+
})
|
|
61
|
+
output(result, program.opts().json)
|
|
62
|
+
} catch (e) { outputError(e, program.opts().json) }
|
|
63
|
+
})
|
|
64
|
+
}
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import { Command } from 'commander'
|
|
2
|
+
import { resolveSpaceId } from '../config.js'
|
|
3
|
+
import { callTool } from '../api-client.js'
|
|
4
|
+
import { output, outputError } from '../output.js'
|
|
5
|
+
|
|
6
|
+
export function registerClientCommands(program: Command): void {
|
|
7
|
+
const client = program.command('client').description('Client management')
|
|
8
|
+
|
|
9
|
+
client
|
|
10
|
+
.command('list')
|
|
11
|
+
.description('List clients')
|
|
12
|
+
.option('-s, --space-id <uuid>', 'Filter by space UUID')
|
|
13
|
+
.option('--no-include-invites', 'Exclude pending invites')
|
|
14
|
+
.action(async (opts) => {
|
|
15
|
+
try {
|
|
16
|
+
const result = await callTool('client_list', {
|
|
17
|
+
spaceId: opts.spaceId,
|
|
18
|
+
includeInvites: opts.includeInvites !== false,
|
|
19
|
+
})
|
|
20
|
+
output(result, program.opts().json)
|
|
21
|
+
} catch (e) { outputError(e, program.opts().json) }
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
client
|
|
25
|
+
.command('get')
|
|
26
|
+
.description('Get client details')
|
|
27
|
+
.requiredOption('--user-id <uuid>', 'Client user UUID')
|
|
28
|
+
.action(async (opts) => {
|
|
29
|
+
try {
|
|
30
|
+
const result = await callTool('client_get', { userId: opts.userId })
|
|
31
|
+
output(result, program.opts().json)
|
|
32
|
+
} catch (e) { outputError(e, program.opts().json) }
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
client
|
|
36
|
+
.command('update')
|
|
37
|
+
.description('Update client role in a space')
|
|
38
|
+
.requiredOption('--user-id <uuid>', 'Client user UUID')
|
|
39
|
+
.option('-s, --space-id <uuid>', 'Space UUID')
|
|
40
|
+
.requiredOption('--role <role>', 'New role: client|viewer')
|
|
41
|
+
.action(async (opts) => {
|
|
42
|
+
try {
|
|
43
|
+
const result = await callTool('client_update', {
|
|
44
|
+
userId: opts.userId,
|
|
45
|
+
spaceId: resolveSpaceId(opts),
|
|
46
|
+
role: opts.role,
|
|
47
|
+
})
|
|
48
|
+
output(result, program.opts().json)
|
|
49
|
+
} catch (e) { outputError(e, program.opts().json) }
|
|
50
|
+
})
|
|
51
|
+
|
|
52
|
+
client
|
|
53
|
+
.command('add-to-space')
|
|
54
|
+
.description('Add client to a space')
|
|
55
|
+
.requiredOption('--user-id <uuid>', 'Client user UUID')
|
|
56
|
+
.option('-s, --space-id <uuid>', 'Space UUID')
|
|
57
|
+
.option('--role <role>', 'Role: client|viewer', 'client')
|
|
58
|
+
.action(async (opts) => {
|
|
59
|
+
try {
|
|
60
|
+
const result = await callTool('client_add_to_space', {
|
|
61
|
+
userId: opts.userId,
|
|
62
|
+
spaceId: resolveSpaceId(opts),
|
|
63
|
+
role: opts.role,
|
|
64
|
+
})
|
|
65
|
+
output(result, program.opts().json)
|
|
66
|
+
} catch (e) { outputError(e, program.opts().json) }
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
// Invite subcommands
|
|
70
|
+
const invite = client.command('invite').description('Client invitations')
|
|
71
|
+
|
|
72
|
+
invite
|
|
73
|
+
.command('create')
|
|
74
|
+
.description('Create a client invite')
|
|
75
|
+
.option('-s, --space-id <uuid>', 'Space UUID')
|
|
76
|
+
.requiredOption('--email <email>', 'Client email')
|
|
77
|
+
.option('--expires-in-days <n>', 'Expiry days', '7')
|
|
78
|
+
.action(async (opts) => {
|
|
79
|
+
try {
|
|
80
|
+
const result = await callTool('client_invite_create', {
|
|
81
|
+
email: opts.email,
|
|
82
|
+
spaceId: resolveSpaceId(opts),
|
|
83
|
+
expiresInDays: parseInt(opts.expiresInDays),
|
|
84
|
+
})
|
|
85
|
+
output(result, program.opts().json)
|
|
86
|
+
} catch (e) { outputError(e, program.opts().json) }
|
|
87
|
+
})
|
|
88
|
+
|
|
89
|
+
invite
|
|
90
|
+
.command('bulk-create')
|
|
91
|
+
.description('Bulk create client invites')
|
|
92
|
+
.option('-s, --space-id <uuid>', 'Space UUID')
|
|
93
|
+
.requiredOption('--emails <emails...>', 'Client emails (max 50)')
|
|
94
|
+
.option('--expires-in-days <n>', 'Expiry days', '7')
|
|
95
|
+
.action(async (opts) => {
|
|
96
|
+
try {
|
|
97
|
+
const result = await callTool('client_invite_bulk_create', {
|
|
98
|
+
emails: opts.emails,
|
|
99
|
+
spaceId: resolveSpaceId(opts),
|
|
100
|
+
expiresInDays: parseInt(opts.expiresInDays),
|
|
101
|
+
})
|
|
102
|
+
output(result, program.opts().json)
|
|
103
|
+
} catch (e) { outputError(e, program.opts().json) }
|
|
104
|
+
})
|
|
105
|
+
|
|
106
|
+
invite
|
|
107
|
+
.command('list')
|
|
108
|
+
.description('List client invites')
|
|
109
|
+
.option('-s, --space-id <uuid>', 'Filter by space')
|
|
110
|
+
.option('--status <status>', 'pending|accepted|expired|all', 'pending')
|
|
111
|
+
.action(async (opts) => {
|
|
112
|
+
try {
|
|
113
|
+
const result = await callTool('client_invite_list', {
|
|
114
|
+
spaceId: opts.spaceId,
|
|
115
|
+
status: opts.status,
|
|
116
|
+
})
|
|
117
|
+
output(result, program.opts().json)
|
|
118
|
+
} catch (e) { outputError(e, program.opts().json) }
|
|
119
|
+
})
|
|
120
|
+
|
|
121
|
+
invite
|
|
122
|
+
.command('resend')
|
|
123
|
+
.description('Resend a client invite')
|
|
124
|
+
.requiredOption('--invite-id <uuid>', 'Invite UUID')
|
|
125
|
+
.option('--expires-in-days <n>', 'New expiry days', '7')
|
|
126
|
+
.action(async (opts) => {
|
|
127
|
+
try {
|
|
128
|
+
const result = await callTool('client_invite_resend', {
|
|
129
|
+
inviteId: opts.inviteId,
|
|
130
|
+
expiresInDays: parseInt(opts.expiresInDays),
|
|
131
|
+
})
|
|
132
|
+
output(result, program.opts().json)
|
|
133
|
+
} catch (e) { outputError(e, program.opts().json) }
|
|
134
|
+
})
|
|
135
|
+
}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { Command } from 'commander'
|
|
2
|
+
import { writeFileSync, existsSync, readFileSync, unlinkSync } from 'node:fs'
|
|
3
|
+
import { createInterface } from 'node:readline'
|
|
4
|
+
import { getConfigPath } from '../config.js'
|
|
5
|
+
|
|
6
|
+
async function prompt(question: string, defaultValue?: string): Promise<string> {
|
|
7
|
+
const rl = createInterface({ input: process.stdin, output: process.stderr })
|
|
8
|
+
const suffix = defaultValue ? ` [${defaultValue}]` : ''
|
|
9
|
+
return new Promise((resolve) => {
|
|
10
|
+
rl.question(`${question}${suffix}: `, (answer) => {
|
|
11
|
+
rl.close()
|
|
12
|
+
resolve(answer.trim() || defaultValue || '')
|
|
13
|
+
})
|
|
14
|
+
})
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function loadExisting(): Record<string, string> {
|
|
18
|
+
const configPath = getConfigPath()
|
|
19
|
+
if (existsSync(configPath)) {
|
|
20
|
+
try {
|
|
21
|
+
return JSON.parse(readFileSync(configPath, 'utf-8'))
|
|
22
|
+
} catch { /* ignore */ }
|
|
23
|
+
}
|
|
24
|
+
return {}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function maskSecret(value: string | undefined): string | undefined {
|
|
28
|
+
if (!value) return undefined
|
|
29
|
+
return value.slice(0, 8) + '...' + value.slice(-4)
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function registerConfigCommand(program: Command): void {
|
|
33
|
+
// agentpm login
|
|
34
|
+
program
|
|
35
|
+
.command('login')
|
|
36
|
+
.description('Login with your API Key (get it from Web UI → Settings → API Keys)')
|
|
37
|
+
.action(async () => {
|
|
38
|
+
const configPath = getConfigPath()
|
|
39
|
+
const existing = loadExisting()
|
|
40
|
+
|
|
41
|
+
console.error('AgentPM CLI Login')
|
|
42
|
+
console.error('─'.repeat(40))
|
|
43
|
+
|
|
44
|
+
const config: Record<string, string> = { ...existing }
|
|
45
|
+
|
|
46
|
+
const apiUrl = await prompt('API URL', existing.apiUrl || undefined)
|
|
47
|
+
if (apiUrl) config.apiUrl = apiUrl
|
|
48
|
+
|
|
49
|
+
console.error('\nPaste your API Key from Web UI → Settings → API Keys:')
|
|
50
|
+
const apiKey = await prompt('API Key', existing.apiKey ? maskSecret(existing.apiKey) : undefined)
|
|
51
|
+
if (apiKey && !apiKey.includes('...')) {
|
|
52
|
+
config.apiKey = apiKey
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const defaultSpaceId = await prompt('Default Space ID (optional, press Enter to skip)', existing.defaultSpaceId)
|
|
56
|
+
if (defaultSpaceId) config.defaultSpaceId = defaultSpaceId
|
|
57
|
+
|
|
58
|
+
writeFileSync(configPath, JSON.stringify(config, null, 2) + '\n')
|
|
59
|
+
console.error(`\n✓ Config saved to ${configPath}`)
|
|
60
|
+
|
|
61
|
+
if (config.apiKey || existing.apiKey) {
|
|
62
|
+
console.error('✓ Run `agentpm space list` to verify your setup')
|
|
63
|
+
}
|
|
64
|
+
})
|
|
65
|
+
|
|
66
|
+
// agentpm config — advanced config management
|
|
67
|
+
const cfg = program.command('config').description('CLI configuration')
|
|
68
|
+
|
|
69
|
+
cfg
|
|
70
|
+
.command('show')
|
|
71
|
+
.description('Show current config (secrets masked)')
|
|
72
|
+
.action(() => {
|
|
73
|
+
const configPath = getConfigPath()
|
|
74
|
+
|
|
75
|
+
if (!existsSync(configPath)) {
|
|
76
|
+
console.error(`No config file at ${configPath}`)
|
|
77
|
+
console.error('Run: agentpm login')
|
|
78
|
+
process.exit(1)
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const content = JSON.parse(readFileSync(configPath, 'utf-8'))
|
|
82
|
+
const masked = { ...content }
|
|
83
|
+
if (masked.apiKey) masked.apiKey = maskSecret(masked.apiKey)
|
|
84
|
+
console.log(JSON.stringify(masked, null, 2))
|
|
85
|
+
})
|
|
86
|
+
|
|
87
|
+
cfg
|
|
88
|
+
.command('path')
|
|
89
|
+
.description('Print config file path')
|
|
90
|
+
.action(() => {
|
|
91
|
+
console.log(getConfigPath())
|
|
92
|
+
})
|
|
93
|
+
|
|
94
|
+
cfg
|
|
95
|
+
.command('reset')
|
|
96
|
+
.description('Delete config file')
|
|
97
|
+
.action(() => {
|
|
98
|
+
const configPath = getConfigPath()
|
|
99
|
+
if (existsSync(configPath)) {
|
|
100
|
+
unlinkSync(configPath)
|
|
101
|
+
console.error(`Deleted ${configPath}`)
|
|
102
|
+
} else {
|
|
103
|
+
console.error('No config file to delete')
|
|
104
|
+
}
|
|
105
|
+
})
|
|
106
|
+
}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { Command } from 'commander'
|
|
2
|
+
import { resolveSpaceId } from '../config.js'
|
|
3
|
+
import { callTool } from '../api-client.js'
|
|
4
|
+
import { output, outputError } from '../output.js'
|
|
5
|
+
|
|
6
|
+
export function registerMeetingCommands(program: Command): void {
|
|
7
|
+
const meeting = program.command('meeting').description('Meeting management')
|
|
8
|
+
|
|
9
|
+
meeting
|
|
10
|
+
.command('list')
|
|
11
|
+
.description('List meetings')
|
|
12
|
+
.option('-s, --space-id <uuid>', 'Space UUID')
|
|
13
|
+
.option('--status <status>', 'Filter: planned|in_progress|ended')
|
|
14
|
+
.option('--limit <n>', 'Max results', '20')
|
|
15
|
+
.action(async (opts) => {
|
|
16
|
+
try {
|
|
17
|
+
const result = await callTool('meeting_list', {
|
|
18
|
+
spaceId: resolveSpaceId(opts),
|
|
19
|
+
status: opts.status,
|
|
20
|
+
limit: parseInt(opts.limit),
|
|
21
|
+
})
|
|
22
|
+
output(result, program.opts().json)
|
|
23
|
+
} catch (e) { outputError(e, program.opts().json) }
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
meeting
|
|
27
|
+
.command('create')
|
|
28
|
+
.description('Create a meeting')
|
|
29
|
+
.option('-s, --space-id <uuid>', 'Space UUID')
|
|
30
|
+
.requiredOption('--title <title>', 'Meeting title')
|
|
31
|
+
.option('--held-at <datetime>', 'Date/time (ISO8601)')
|
|
32
|
+
.option('--notes <notes>', 'Pre-meeting notes')
|
|
33
|
+
.option('--participant-ids <ids...>', 'Participant UUIDs')
|
|
34
|
+
.action(async (opts) => {
|
|
35
|
+
try {
|
|
36
|
+
const result = await callTool('meeting_create', {
|
|
37
|
+
spaceId: resolveSpaceId(opts),
|
|
38
|
+
title: opts.title,
|
|
39
|
+
heldAt: opts.heldAt,
|
|
40
|
+
notes: opts.notes,
|
|
41
|
+
participantIds: opts.participantIds || [],
|
|
42
|
+
})
|
|
43
|
+
output(result, program.opts().json)
|
|
44
|
+
} catch (e) { outputError(e, program.opts().json) }
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
meeting
|
|
48
|
+
.command('start')
|
|
49
|
+
.description('Start a meeting')
|
|
50
|
+
.option('-s, --space-id <uuid>', 'Space UUID')
|
|
51
|
+
.requiredOption('--meeting-id <uuid>', 'Meeting UUID')
|
|
52
|
+
.action(async (opts) => {
|
|
53
|
+
try {
|
|
54
|
+
const result = await callTool('meeting_start', {
|
|
55
|
+
spaceId: resolveSpaceId(opts),
|
|
56
|
+
meetingId: opts.meetingId,
|
|
57
|
+
})
|
|
58
|
+
output(result, program.opts().json)
|
|
59
|
+
} catch (e) { outputError(e, program.opts().json) }
|
|
60
|
+
})
|
|
61
|
+
|
|
62
|
+
meeting
|
|
63
|
+
.command('end')
|
|
64
|
+
.description('End a meeting')
|
|
65
|
+
.option('-s, --space-id <uuid>', 'Space UUID')
|
|
66
|
+
.requiredOption('--meeting-id <uuid>', 'Meeting UUID')
|
|
67
|
+
.action(async (opts) => {
|
|
68
|
+
try {
|
|
69
|
+
const result = await callTool('meeting_end', {
|
|
70
|
+
spaceId: resolveSpaceId(opts),
|
|
71
|
+
meetingId: opts.meetingId,
|
|
72
|
+
})
|
|
73
|
+
output(result, program.opts().json)
|
|
74
|
+
} catch (e) { outputError(e, program.opts().json) }
|
|
75
|
+
})
|
|
76
|
+
|
|
77
|
+
meeting
|
|
78
|
+
.command('get')
|
|
79
|
+
.description('Get meeting details')
|
|
80
|
+
.option('-s, --space-id <uuid>', 'Space UUID')
|
|
81
|
+
.requiredOption('--meeting-id <uuid>', 'Meeting UUID')
|
|
82
|
+
.action(async (opts) => {
|
|
83
|
+
try {
|
|
84
|
+
const result = await callTool('meeting_get', {
|
|
85
|
+
spaceId: resolveSpaceId(opts),
|
|
86
|
+
meetingId: opts.meetingId,
|
|
87
|
+
})
|
|
88
|
+
output(result, program.opts().json)
|
|
89
|
+
} catch (e) { outputError(e, program.opts().json) }
|
|
90
|
+
})
|
|
91
|
+
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { Command } from 'commander'
|
|
2
|
+
import { resolveSpaceId } from '../config.js'
|
|
3
|
+
import { callTool } from '../api-client.js'
|
|
4
|
+
import { output, outputError } from '../output.js'
|
|
5
|
+
|
|
6
|
+
export function registerMilestoneCommands(program: Command): void {
|
|
7
|
+
const ms = program.command('milestone').description('Milestone management')
|
|
8
|
+
|
|
9
|
+
ms
|
|
10
|
+
.command('list')
|
|
11
|
+
.description('List milestones')
|
|
12
|
+
.option('-s, --space-id <uuid>', 'Space UUID')
|
|
13
|
+
.action(async (opts) => {
|
|
14
|
+
try {
|
|
15
|
+
const result = await callTool('milestone_list', {
|
|
16
|
+
spaceId: resolveSpaceId(opts),
|
|
17
|
+
})
|
|
18
|
+
output(result, program.opts().json)
|
|
19
|
+
} catch (e) { outputError(e, program.opts().json) }
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
ms
|
|
23
|
+
.command('create')
|
|
24
|
+
.description('Create a milestone')
|
|
25
|
+
.option('-s, --space-id <uuid>', 'Space UUID')
|
|
26
|
+
.requiredOption('--name <name>', 'Milestone name')
|
|
27
|
+
.option('--due-date <date>', 'Due date (YYYY-MM-DD)')
|
|
28
|
+
.action(async (opts) => {
|
|
29
|
+
try {
|
|
30
|
+
const result = await callTool('milestone_create', {
|
|
31
|
+
spaceId: resolveSpaceId(opts),
|
|
32
|
+
name: opts.name,
|
|
33
|
+
dueDate: opts.dueDate,
|
|
34
|
+
})
|
|
35
|
+
output(result, program.opts().json)
|
|
36
|
+
} catch (e) { outputError(e, program.opts().json) }
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
ms
|
|
40
|
+
.command('update')
|
|
41
|
+
.description('Update a milestone')
|
|
42
|
+
.option('-s, --space-id <uuid>', 'Space UUID')
|
|
43
|
+
.requiredOption('--milestone-id <uuid>', 'Milestone UUID')
|
|
44
|
+
.option('--name <name>', 'New name')
|
|
45
|
+
.option('--due-date <date>', 'New due date')
|
|
46
|
+
.option('--order-key <n>', 'Display order key')
|
|
47
|
+
.action(async (opts) => {
|
|
48
|
+
try {
|
|
49
|
+
const result = await callTool('milestone_update', {
|
|
50
|
+
spaceId: resolveSpaceId(opts),
|
|
51
|
+
milestoneId: opts.milestoneId,
|
|
52
|
+
name: opts.name,
|
|
53
|
+
dueDate: opts.dueDate,
|
|
54
|
+
orderKey: opts.orderKey !== undefined ? parseInt(opts.orderKey) : undefined,
|
|
55
|
+
})
|
|
56
|
+
output(result, program.opts().json)
|
|
57
|
+
} catch (e) { outputError(e, program.opts().json) }
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
ms
|
|
61
|
+
.command('get')
|
|
62
|
+
.description('Get milestone details')
|
|
63
|
+
.option('-s, --space-id <uuid>', 'Space UUID')
|
|
64
|
+
.requiredOption('--milestone-id <uuid>', 'Milestone UUID')
|
|
65
|
+
.action(async (opts) => {
|
|
66
|
+
try {
|
|
67
|
+
const result = await callTool('milestone_get', {
|
|
68
|
+
spaceId: resolveSpaceId(opts),
|
|
69
|
+
milestoneId: opts.milestoneId,
|
|
70
|
+
})
|
|
71
|
+
output(result, program.opts().json)
|
|
72
|
+
} catch (e) { outputError(e, program.opts().json) }
|
|
73
|
+
})
|
|
74
|
+
|
|
75
|
+
ms
|
|
76
|
+
.command('delete')
|
|
77
|
+
.description('Delete a milestone')
|
|
78
|
+
.option('-s, --space-id <uuid>', 'Space UUID')
|
|
79
|
+
.requiredOption('--milestone-id <uuid>', 'Milestone UUID')
|
|
80
|
+
.action(async (opts) => {
|
|
81
|
+
try {
|
|
82
|
+
const result = await callTool('milestone_delete', {
|
|
83
|
+
spaceId: resolveSpaceId(opts),
|
|
84
|
+
milestoneId: opts.milestoneId,
|
|
85
|
+
})
|
|
86
|
+
output(result, program.opts().json)
|
|
87
|
+
} catch (e) { outputError(e, program.opts().json) }
|
|
88
|
+
})
|
|
89
|
+
}
|