@agentuity/cli 1.0.36 → 1.0.38
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/dist/cmd/cloud/sandbox/cp.d.ts.map +1 -1
- package/dist/cmd/cloud/sandbox/cp.js +31 -0
- package/dist/cmd/cloud/sandbox/cp.js.map +1 -1
- package/dist/cmd/cloud/sandbox/exec.d.ts.map +1 -1
- package/dist/cmd/cloud/sandbox/exec.js +44 -11
- package/dist/cmd/cloud/sandbox/exec.js.map +1 -1
- package/dist/cmd/cloud/task/create.js +1 -1
- package/dist/cmd/cloud/task/create.js.map +1 -1
- package/dist/cmd/cloud/task/delete.js +3 -3
- package/dist/cmd/cloud/task/delete.js.map +1 -1
- package/dist/cmd/cloud/task/get.d.ts.map +1 -1
- package/dist/cmd/cloud/task/get.js +58 -4
- package/dist/cmd/cloud/task/get.js.map +1 -1
- package/dist/cmd/cloud/task/index.d.ts.map +1 -1
- package/dist/cmd/cloud/task/index.js +14 -2
- package/dist/cmd/cloud/task/index.js.map +1 -1
- package/dist/cmd/cloud/task/list.d.ts.map +1 -1
- package/dist/cmd/cloud/task/list.js +1 -2
- package/dist/cmd/cloud/task/list.js.map +1 -1
- package/dist/cmd/cloud/task/project.d.ts +2 -0
- package/dist/cmd/cloud/task/project.d.ts.map +1 -0
- package/dist/cmd/cloud/task/project.js +206 -0
- package/dist/cmd/cloud/task/project.js.map +1 -0
- package/dist/cmd/cloud/task/update.d.ts.map +1 -1
- package/dist/cmd/cloud/task/update.js +4 -1
- package/dist/cmd/cloud/task/update.js.map +1 -1
- package/dist/cmd/cloud/task/user.d.ts +2 -0
- package/dist/cmd/cloud/task/user.d.ts.map +1 -0
- package/dist/cmd/cloud/task/user.js +179 -0
- package/dist/cmd/cloud/task/user.js.map +1 -0
- package/dist/cmd/cloud/task/util.d.ts +1 -0
- package/dist/cmd/cloud/task/util.d.ts.map +1 -1
- package/dist/cmd/cloud/task/util.js +13 -0
- package/dist/cmd/cloud/task/util.js.map +1 -1
- package/dist/cmd/coder/hub-url.d.ts +35 -0
- package/dist/cmd/coder/hub-url.d.ts.map +1 -0
- package/dist/cmd/coder/hub-url.js +101 -0
- package/dist/cmd/coder/hub-url.js.map +1 -0
- package/dist/cmd/coder/index.d.ts +2 -0
- package/dist/cmd/coder/index.d.ts.map +1 -0
- package/dist/cmd/coder/index.js +27 -0
- package/dist/cmd/coder/index.js.map +1 -0
- package/dist/cmd/coder/inspect.d.ts +2 -0
- package/dist/cmd/coder/inspect.d.ts.map +1 -0
- package/dist/cmd/coder/inspect.js +145 -0
- package/dist/cmd/coder/inspect.js.map +1 -0
- package/dist/cmd/coder/list.d.ts +2 -0
- package/dist/cmd/coder/list.d.ts.map +1 -0
- package/dist/cmd/coder/list.js +109 -0
- package/dist/cmd/coder/list.js.map +1 -0
- package/dist/cmd/coder/start.d.ts +2 -0
- package/dist/cmd/coder/start.d.ts.map +1 -0
- package/dist/cmd/coder/start.js +384 -0
- package/dist/cmd/coder/start.js.map +1 -0
- package/dist/cmd/dev/index.d.ts.map +1 -1
- package/dist/cmd/dev/index.js +4 -0
- package/dist/cmd/dev/index.js.map +1 -1
- package/dist/cmd/index.d.ts.map +1 -1
- package/dist/cmd/index.js +1 -0
- package/dist/cmd/index.js.map +1 -1
- package/package.json +6 -6
- package/src/cmd/cloud/sandbox/cp.ts +32 -0
- package/src/cmd/cloud/sandbox/exec.ts +62 -13
- package/src/cmd/cloud/task/create.ts +1 -1
- package/src/cmd/cloud/task/delete.ts +3 -3
- package/src/cmd/cloud/task/get.ts +68 -4
- package/src/cmd/cloud/task/index.ts +14 -2
- package/src/cmd/cloud/task/list.ts +1 -2
- package/src/cmd/cloud/task/project.ts +227 -0
- package/src/cmd/cloud/task/update.ts +4 -1
- package/src/cmd/cloud/task/user.ts +202 -0
- package/src/cmd/cloud/task/util.ts +18 -0
- package/src/cmd/coder/hub-url.ts +105 -0
- package/src/cmd/coder/index.ts +27 -0
- package/src/cmd/coder/inspect.ts +200 -0
- package/src/cmd/coder/list.ts +143 -0
- package/src/cmd/coder/start.ts +419 -0
- package/src/cmd/dev/index.ts +5 -0
- package/src/cmd/index.ts +1 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agentuity/cli",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.38",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"author": "Agentuity employees and contributors",
|
|
6
6
|
"type": "module",
|
|
@@ -41,9 +41,9 @@
|
|
|
41
41
|
"prepublishOnly": "bun run clean && bun run build"
|
|
42
42
|
},
|
|
43
43
|
"dependencies": {
|
|
44
|
-
"@agentuity/auth": "1.0.
|
|
45
|
-
"@agentuity/core": "1.0.
|
|
46
|
-
"@agentuity/server": "1.0.
|
|
44
|
+
"@agentuity/auth": "1.0.38",
|
|
45
|
+
"@agentuity/core": "1.0.38",
|
|
46
|
+
"@agentuity/server": "1.0.38",
|
|
47
47
|
"@datasert/cronjs-parser": "^1.4.0",
|
|
48
48
|
"@vitejs/plugin-react": "^5.1.2",
|
|
49
49
|
"acorn-loose": "^8.5.2",
|
|
@@ -60,10 +60,10 @@
|
|
|
60
60
|
"typescript": "^5.9.0",
|
|
61
61
|
"vite": "^7.2.7",
|
|
62
62
|
"zod": "^4.3.5",
|
|
63
|
-
"@agentuity/frontend": "1.0.
|
|
63
|
+
"@agentuity/frontend": "1.0.38"
|
|
64
64
|
},
|
|
65
65
|
"devDependencies": {
|
|
66
|
-
"@agentuity/test-utils": "1.0.
|
|
66
|
+
"@agentuity/test-utils": "1.0.38",
|
|
67
67
|
"@types/adm-zip": "^0.5.7",
|
|
68
68
|
"@types/bun": "latest",
|
|
69
69
|
"@types/tar-fs": "^2.0.4",
|
|
@@ -42,6 +42,10 @@ const SandboxCpResponseSchema = z.object({
|
|
|
42
42
|
destination: z.string().describe('Destination path'),
|
|
43
43
|
bytesTransferred: z.number().describe('Number of bytes transferred'),
|
|
44
44
|
filesTransferred: z.number().describe('Number of files transferred'),
|
|
45
|
+
directoriesCreated: z
|
|
46
|
+
.array(z.string())
|
|
47
|
+
.optional()
|
|
48
|
+
.describe('Parent directories auto-created on the destination'),
|
|
45
49
|
});
|
|
46
50
|
|
|
47
51
|
export const cpSubcommand = createCommand({
|
|
@@ -156,6 +160,30 @@ function getAllFiles(dirPath: string, basePath: string = dirPath): string[] {
|
|
|
156
160
|
return files;
|
|
157
161
|
}
|
|
158
162
|
|
|
163
|
+
/**
|
|
164
|
+
* Computes the parent directories that would be auto-created by the server
|
|
165
|
+
* when writing files to the given paths. Filters out directories that always
|
|
166
|
+
* exist in a sandbox (/, /home, /home/agentuity).
|
|
167
|
+
*/
|
|
168
|
+
function getImplicitDirectories(filePaths: string[]): string[] {
|
|
169
|
+
const dirs = new Set<string>();
|
|
170
|
+
// Directories that always exist in a sandbox
|
|
171
|
+
const knownDirs = new Set(['/', '/home', '/home/agentuity']);
|
|
172
|
+
|
|
173
|
+
for (const filePath of filePaths) {
|
|
174
|
+
let dir = dirname(filePath);
|
|
175
|
+
while (dir && dir !== '.' && dir !== '/') {
|
|
176
|
+
if (!knownDirs.has(dir)) {
|
|
177
|
+
dirs.add(dir);
|
|
178
|
+
}
|
|
179
|
+
const parent = dirname(dir);
|
|
180
|
+
if (parent === dir) break;
|
|
181
|
+
dir = parent;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
return Array.from(dirs).sort();
|
|
185
|
+
}
|
|
186
|
+
|
|
159
187
|
async function uploadToSandbox(
|
|
160
188
|
client: APIClient,
|
|
161
189
|
logger: Logger,
|
|
@@ -234,11 +262,13 @@ async function uploadSingleFile(
|
|
|
234
262
|
tui.success(`Copied ${displayPath} → ${sandboxId}:${targetPath} (${buffer.length} bytes)`);
|
|
235
263
|
}
|
|
236
264
|
|
|
265
|
+
const implicitDirs = getImplicitDirectories([targetPath]);
|
|
237
266
|
return {
|
|
238
267
|
source: displayPath,
|
|
239
268
|
destination: `${sandboxId}:${targetPath}`,
|
|
240
269
|
bytesTransferred: buffer.length,
|
|
241
270
|
filesTransferred: 1,
|
|
271
|
+
directoriesCreated: implicitDirs.length > 0 ? implicitDirs : undefined,
|
|
242
272
|
};
|
|
243
273
|
}
|
|
244
274
|
|
|
@@ -281,11 +311,13 @@ async function uploadDirectory(
|
|
|
281
311
|
);
|
|
282
312
|
}
|
|
283
313
|
|
|
314
|
+
const implicitDirs = getImplicitDirectories(files.map((f) => f.path));
|
|
284
315
|
return {
|
|
285
316
|
source: localDir,
|
|
286
317
|
destination: `${sandboxId}:${baseRemotePath}`,
|
|
287
318
|
bytesTransferred: totalBytes,
|
|
288
319
|
filesTransferred: allFiles.length,
|
|
320
|
+
directoriesCreated: implicitDirs.length > 0 ? implicitDirs : undefined,
|
|
289
321
|
};
|
|
290
322
|
}
|
|
291
323
|
|
|
@@ -15,6 +15,14 @@ const SandboxExecResponseSchema = z.object({
|
|
|
15
15
|
status: z.string().describe('Execution status'),
|
|
16
16
|
exitCode: z.number().optional().describe('Exit code (if completed)'),
|
|
17
17
|
durationMs: z.number().optional().describe('Duration in milliseconds (if completed)'),
|
|
18
|
+
stdout: z
|
|
19
|
+
.string()
|
|
20
|
+
.optional()
|
|
21
|
+
.describe('Standard output (only when separate streams are available)'),
|
|
22
|
+
stderr: z
|
|
23
|
+
.string()
|
|
24
|
+
.optional()
|
|
25
|
+
.describe('Standard error output (only when separate streams are available)'),
|
|
18
26
|
output: z.string().optional().describe('Combined stdout/stderr output'),
|
|
19
27
|
});
|
|
20
28
|
|
|
@@ -68,16 +76,6 @@ export const execSubcommand = createCommand({
|
|
|
68
76
|
process.on('SIGINT', handleSignal);
|
|
69
77
|
process.on('SIGTERM', handleSignal);
|
|
70
78
|
|
|
71
|
-
const outputChunks: string[] = [];
|
|
72
|
-
|
|
73
|
-
// For JSON output, capture to buffer; otherwise stream to process
|
|
74
|
-
const stdout = options.json
|
|
75
|
-
? createCaptureStream((chunk) => outputChunks.push(chunk))
|
|
76
|
-
: process.stdout;
|
|
77
|
-
const stderr = options.json
|
|
78
|
-
? createCaptureStream((chunk) => outputChunks.push(chunk))
|
|
79
|
-
: process.stderr;
|
|
80
|
-
|
|
81
79
|
try {
|
|
82
80
|
const execution = await sandboxExecute(client, {
|
|
83
81
|
sandboxId: args.sandboxId,
|
|
@@ -98,24 +96,69 @@ export const execSubcommand = createCommand({
|
|
|
98
96
|
const isCombinedOutput =
|
|
99
97
|
stdoutStreamUrl && stderrStreamUrl && stdoutStreamUrl === stderrStreamUrl;
|
|
100
98
|
|
|
99
|
+
// Set up stream capture — in JSON mode, capture to buffers;
|
|
100
|
+
// when streams are separate, capture stdout/stderr independently
|
|
101
|
+
const outputChunks: string[] = [];
|
|
102
|
+
const stdoutChunks: string[] = [];
|
|
103
|
+
const stderrChunks: string[] = [];
|
|
104
|
+
|
|
105
|
+
let stdoutWritable: NodeJS.WritableStream;
|
|
106
|
+
let stderrWritable: NodeJS.WritableStream;
|
|
107
|
+
|
|
108
|
+
if (options.json) {
|
|
109
|
+
if (isCombinedOutput) {
|
|
110
|
+
// Combined stream: can't distinguish stdout from stderr
|
|
111
|
+
stdoutWritable = createCaptureStream((chunk) => outputChunks.push(chunk));
|
|
112
|
+
stderrWritable = createCaptureStream((chunk) => outputChunks.push(chunk));
|
|
113
|
+
} else {
|
|
114
|
+
// Separate streams: capture each independently and also to combined output
|
|
115
|
+
stdoutWritable = createCaptureStream((chunk) => {
|
|
116
|
+
stdoutChunks.push(chunk);
|
|
117
|
+
outputChunks.push(chunk);
|
|
118
|
+
});
|
|
119
|
+
stderrWritable = createCaptureStream((chunk) => {
|
|
120
|
+
stderrChunks.push(chunk);
|
|
121
|
+
outputChunks.push(chunk);
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
} else {
|
|
125
|
+
stdoutWritable = process.stdout;
|
|
126
|
+
stderrWritable = process.stderr;
|
|
127
|
+
}
|
|
128
|
+
|
|
101
129
|
if (isCombinedOutput) {
|
|
102
130
|
// Stream combined output to stdout only to avoid duplicates
|
|
103
131
|
logger.debug('using combined output stream (stdout === stderr): %s', stdoutStreamUrl);
|
|
104
132
|
streamPromises.push(
|
|
105
|
-
streamUrlToWritable(
|
|
133
|
+
streamUrlToWritable(
|
|
134
|
+
stdoutStreamUrl,
|
|
135
|
+
stdoutWritable,
|
|
136
|
+
streamAbortController.signal,
|
|
137
|
+
logger
|
|
138
|
+
)
|
|
106
139
|
);
|
|
107
140
|
} else {
|
|
108
141
|
if (stdoutStreamUrl) {
|
|
109
142
|
logger.debug('starting stdout stream from: %s', stdoutStreamUrl);
|
|
110
143
|
streamPromises.push(
|
|
111
|
-
streamUrlToWritable(
|
|
144
|
+
streamUrlToWritable(
|
|
145
|
+
stdoutStreamUrl,
|
|
146
|
+
stdoutWritable,
|
|
147
|
+
streamAbortController.signal,
|
|
148
|
+
logger
|
|
149
|
+
)
|
|
112
150
|
);
|
|
113
151
|
}
|
|
114
152
|
|
|
115
153
|
if (stderrStreamUrl) {
|
|
116
154
|
logger.debug('starting stderr stream from: %s', stderrStreamUrl);
|
|
117
155
|
streamPromises.push(
|
|
118
|
-
streamUrlToWritable(
|
|
156
|
+
streamUrlToWritable(
|
|
157
|
+
stderrStreamUrl,
|
|
158
|
+
stderrWritable,
|
|
159
|
+
streamAbortController.signal,
|
|
160
|
+
logger
|
|
161
|
+
)
|
|
119
162
|
);
|
|
120
163
|
}
|
|
121
164
|
}
|
|
@@ -145,6 +188,10 @@ export const execSubcommand = createCommand({
|
|
|
145
188
|
|
|
146
189
|
const duration = Date.now() - started;
|
|
147
190
|
const output = outputChunks.join('');
|
|
191
|
+
const stdoutOutput =
|
|
192
|
+
!isCombinedOutput && stdoutStreamUrl ? stdoutChunks.join('') : undefined;
|
|
193
|
+
const stderrOutput =
|
|
194
|
+
!isCombinedOutput && stderrStreamUrl ? stderrChunks.join('') : undefined;
|
|
148
195
|
|
|
149
196
|
if (!options.json) {
|
|
150
197
|
if (finalExecution.exitCode === 0) {
|
|
@@ -163,6 +210,8 @@ export const execSubcommand = createCommand({
|
|
|
163
210
|
status: finalExecution.status,
|
|
164
211
|
exitCode: finalExecution.exitCode,
|
|
165
212
|
durationMs: finalExecution.durationMs,
|
|
213
|
+
stdout: options.json ? stdoutOutput : undefined,
|
|
214
|
+
stderr: options.json ? stderrOutput : undefined,
|
|
166
215
|
output: options.json ? output : undefined,
|
|
167
216
|
};
|
|
168
217
|
} finally {
|
|
@@ -95,7 +95,7 @@ export const createSubcommand = createCommand({
|
|
|
95
95
|
.optional()
|
|
96
96
|
.describe('task priority (default: none)'),
|
|
97
97
|
status: z
|
|
98
|
-
.enum(['open', 'in_progress', '
|
|
98
|
+
.enum(['open', 'in_progress', 'done', 'cancelled'])
|
|
99
99
|
.optional()
|
|
100
100
|
.describe('initial task status (default: open)'),
|
|
101
101
|
parentId: z.string().optional().describe('parent task ID for subtasks'),
|
|
@@ -70,8 +70,8 @@ export const deleteSubcommand = createCommand({
|
|
|
70
70
|
description: 'Delete a single task by ID',
|
|
71
71
|
},
|
|
72
72
|
{
|
|
73
|
-
command: getCommand('cloud task delete --status
|
|
74
|
-
description: 'Delete
|
|
73
|
+
command: getCommand('cloud task delete --status done --older-than 7d'),
|
|
74
|
+
description: 'Delete done tasks older than 7 days',
|
|
75
75
|
},
|
|
76
76
|
{
|
|
77
77
|
command: getCommand('cloud task delete --status done --limit 10 --dry-run'),
|
|
@@ -88,7 +88,7 @@ export const deleteSubcommand = createCommand({
|
|
|
88
88
|
}),
|
|
89
89
|
options: z.object({
|
|
90
90
|
status: z
|
|
91
|
-
.enum(['open', 'in_progress', 'done', '
|
|
91
|
+
.enum(['open', 'in_progress', 'done', 'cancelled'])
|
|
92
92
|
.optional()
|
|
93
93
|
.describe('filter batch delete by status'),
|
|
94
94
|
type: z
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
import { createCommand } from '../../../types';
|
|
3
3
|
import * as tui from '../../../tui';
|
|
4
|
-
import {
|
|
4
|
+
import { createStorageAdapterOptionalOrg, cacheTaskId } from './util';
|
|
5
5
|
import { getCommand } from '../../../command-prefix';
|
|
6
6
|
|
|
7
7
|
const EntityRefSchema = z
|
|
@@ -19,6 +19,15 @@ const UserEntityRefSchema = z
|
|
|
19
19
|
})
|
|
20
20
|
.optional();
|
|
21
21
|
|
|
22
|
+
const SubtaskSchema = z.object({
|
|
23
|
+
id: z.string().describe('Subtask ID'),
|
|
24
|
+
title: z.string().describe('Subtask title'),
|
|
25
|
+
type: z.string().describe('Subtask type'),
|
|
26
|
+
status: z.string().describe('Subtask status'),
|
|
27
|
+
priority: z.string().describe('Subtask priority'),
|
|
28
|
+
assignee: UserEntityRefSchema.describe('Subtask assignee'),
|
|
29
|
+
});
|
|
30
|
+
|
|
22
31
|
const TaskGetResponseSchema = z.object({
|
|
23
32
|
success: z.boolean().describe('Whether the operation succeeded'),
|
|
24
33
|
task: z.object({
|
|
@@ -41,6 +50,7 @@ const TaskGetResponseSchema = z.object({
|
|
|
41
50
|
closed_date: z.string().optional().describe('Date task was closed'),
|
|
42
51
|
cancelled_date: z.string().optional().describe('Date task was cancelled'),
|
|
43
52
|
}),
|
|
53
|
+
subtasks: z.array(SubtaskSchema).optional().describe('Subtasks of this task'),
|
|
44
54
|
durationMs: z.number().describe('Operation duration in milliseconds'),
|
|
45
55
|
});
|
|
46
56
|
|
|
@@ -60,21 +70,27 @@ export const getSubcommand = createCommand({
|
|
|
60
70
|
command: getCommand('cloud task get task_abc123 --json'),
|
|
61
71
|
description: 'Get task details as JSON',
|
|
62
72
|
},
|
|
73
|
+
{
|
|
74
|
+
command: getCommand('cloud task get task_abc123 --no-subtasks'),
|
|
75
|
+
description: 'Get task details without subtasks',
|
|
76
|
+
},
|
|
63
77
|
],
|
|
64
78
|
schema: {
|
|
65
79
|
args: z.object({
|
|
66
80
|
id: z.string().min(1).describe('the task ID to get'),
|
|
67
81
|
}),
|
|
82
|
+
options: z.object({
|
|
83
|
+
'no-subtasks': z.boolean().optional().describe('Do not show subtasks'),
|
|
84
|
+
}),
|
|
68
85
|
response: TaskGetResponseSchema,
|
|
69
86
|
},
|
|
70
87
|
|
|
71
88
|
async handler(ctx) {
|
|
72
|
-
const { args, options } = ctx;
|
|
89
|
+
const { args, opts, options } = ctx;
|
|
73
90
|
const started = Date.now();
|
|
74
|
-
const storage = await
|
|
91
|
+
const storage = await createStorageAdapterOptionalOrg(ctx);
|
|
75
92
|
|
|
76
93
|
const task = await storage.get(args.id);
|
|
77
|
-
const durationMs = Date.now() - started;
|
|
78
94
|
|
|
79
95
|
if (!task) {
|
|
80
96
|
tui.fatal(`Task not found: ${args.id}`);
|
|
@@ -82,6 +98,37 @@ export const getSubcommand = createCommand({
|
|
|
82
98
|
|
|
83
99
|
await cacheTaskId(ctx, task.id);
|
|
84
100
|
|
|
101
|
+
// Fetch subtasks unless disabled
|
|
102
|
+
let subtasksList: {
|
|
103
|
+
id: string;
|
|
104
|
+
title: string;
|
|
105
|
+
type: string;
|
|
106
|
+
status: string;
|
|
107
|
+
priority: string;
|
|
108
|
+
assignee?: { id: string; name: string; type?: 'human' | 'agent' };
|
|
109
|
+
}[] = [];
|
|
110
|
+
let subtasksError: string | undefined;
|
|
111
|
+
if (!opts['no-subtasks']) {
|
|
112
|
+
try {
|
|
113
|
+
const subtasksResult = await storage.list({ parent_id: task.id });
|
|
114
|
+
subtasksList = subtasksResult.tasks.map((st) => ({
|
|
115
|
+
id: st.id,
|
|
116
|
+
title: st.title,
|
|
117
|
+
type: st.type,
|
|
118
|
+
status: st.status,
|
|
119
|
+
priority: st.priority,
|
|
120
|
+
assignee: st.assignee,
|
|
121
|
+
}));
|
|
122
|
+
} catch (err) {
|
|
123
|
+
subtasksError = err instanceof Error ? err.message : 'Failed to fetch subtasks';
|
|
124
|
+
if (!options.json) {
|
|
125
|
+
tui.warn(`Could not load subtasks: ${subtasksError}`);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const durationMs = Date.now() - started;
|
|
131
|
+
|
|
85
132
|
if (!options.json) {
|
|
86
133
|
const tableData: Record<string, string> = {
|
|
87
134
|
ID: task.id,
|
|
@@ -133,6 +180,21 @@ export const getSubcommand = createCommand({
|
|
|
133
180
|
tui.header('Metadata');
|
|
134
181
|
tui.json(task.metadata);
|
|
135
182
|
}
|
|
183
|
+
|
|
184
|
+
// Show subtasks
|
|
185
|
+
if (subtasksList.length > 0) {
|
|
186
|
+
tui.newline();
|
|
187
|
+
tui.header('Subtasks');
|
|
188
|
+
const subtaskRows = subtasksList.map((st) => ({
|
|
189
|
+
ID: st.id,
|
|
190
|
+
Title: st.title,
|
|
191
|
+
Type: st.type,
|
|
192
|
+
Status: st.status,
|
|
193
|
+
Priority: st.priority,
|
|
194
|
+
Assignee: st.assignee?.name ?? 'Unassigned',
|
|
195
|
+
}));
|
|
196
|
+
tui.table(subtaskRows, ['ID', 'Title', 'Type', 'Status', 'Priority', 'Assignee']);
|
|
197
|
+
}
|
|
136
198
|
}
|
|
137
199
|
|
|
138
200
|
return {
|
|
@@ -157,6 +219,8 @@ export const getSubcommand = createCommand({
|
|
|
157
219
|
closed_date: task.closed_date,
|
|
158
220
|
cancelled_date: task.cancelled_date,
|
|
159
221
|
},
|
|
222
|
+
subtasks: subtasksList.length > 0 ? subtasksList : undefined,
|
|
223
|
+
subtasksError,
|
|
160
224
|
durationMs,
|
|
161
225
|
};
|
|
162
226
|
},
|
|
@@ -6,6 +6,8 @@ import { listSubcommand } from './list';
|
|
|
6
6
|
import { deleteSubcommand } from './delete';
|
|
7
7
|
import { statsSubcommand } from './stats';
|
|
8
8
|
import { attachmentSubcommand } from './attachment';
|
|
9
|
+
import { userSubcommand } from './user';
|
|
10
|
+
import { projectSubcommand } from './project';
|
|
9
11
|
import { getCommand } from '../../../command-prefix';
|
|
10
12
|
|
|
11
13
|
export const taskCommand = createCommand({
|
|
@@ -34,13 +36,21 @@ export const taskCommand = createCommand({
|
|
|
34
36
|
description: 'Delete a task by ID',
|
|
35
37
|
},
|
|
36
38
|
{
|
|
37
|
-
command: getCommand('cloud task delete --status
|
|
38
|
-
description: 'Batch delete
|
|
39
|
+
command: getCommand('cloud task delete --status done --older-than 7d'),
|
|
40
|
+
description: 'Batch delete done tasks older than 7 days',
|
|
39
41
|
},
|
|
40
42
|
{
|
|
41
43
|
command: getCommand('cloud task attachment upload task_abc123 ./report.pdf'),
|
|
42
44
|
description: 'Upload a file attachment to a task',
|
|
43
45
|
},
|
|
46
|
+
{
|
|
47
|
+
command: getCommand('cloud task user list'),
|
|
48
|
+
description: 'List task users',
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
command: getCommand('cloud task project list'),
|
|
52
|
+
description: 'List task projects',
|
|
53
|
+
},
|
|
44
54
|
],
|
|
45
55
|
subcommands: [
|
|
46
56
|
getSubcommand,
|
|
@@ -50,6 +60,8 @@ export const taskCommand = createCommand({
|
|
|
50
60
|
deleteSubcommand,
|
|
51
61
|
statsSubcommand,
|
|
52
62
|
attachmentSubcommand,
|
|
63
|
+
userSubcommand,
|
|
64
|
+
projectSubcommand,
|
|
53
65
|
],
|
|
54
66
|
requires: { auth: true },
|
|
55
67
|
});
|
|
@@ -55,7 +55,6 @@ const STATUS_COLORS: Record<string, (s: string) => string> = {
|
|
|
55
55
|
open: tui.colorSuccess,
|
|
56
56
|
in_progress: tui.colorWarning,
|
|
57
57
|
done: tui.colorInfo,
|
|
58
|
-
closed: tui.muted,
|
|
59
58
|
cancelled: tui.muted,
|
|
60
59
|
};
|
|
61
60
|
|
|
@@ -108,7 +107,7 @@ export const listSubcommand = createCommand({
|
|
|
108
107
|
schema: {
|
|
109
108
|
options: z.object({
|
|
110
109
|
status: z
|
|
111
|
-
.enum(['open', 'in_progress', 'done', '
|
|
110
|
+
.enum(['open', 'in_progress', 'done', 'cancelled'])
|
|
112
111
|
.optional()
|
|
113
112
|
.describe('filter by status'),
|
|
114
113
|
type: z
|
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { createCommand } from '../../../types';
|
|
3
|
+
import * as tui from '../../../tui';
|
|
4
|
+
import { createStorageAdapter } from './util';
|
|
5
|
+
import { getCommand } from '../../../command-prefix';
|
|
6
|
+
|
|
7
|
+
// ── List ────────────────────────────────────────────────────────────────
|
|
8
|
+
|
|
9
|
+
const listProjectsSubcommand = createCommand({
|
|
10
|
+
name: 'list',
|
|
11
|
+
aliases: ['ls'],
|
|
12
|
+
description: 'List all task projects',
|
|
13
|
+
tags: ['read-only', 'slow', 'requires-auth'],
|
|
14
|
+
requires: { auth: true },
|
|
15
|
+
idempotent: true,
|
|
16
|
+
examples: [
|
|
17
|
+
{
|
|
18
|
+
command: getCommand('cloud task project list'),
|
|
19
|
+
description: 'List all projects',
|
|
20
|
+
},
|
|
21
|
+
],
|
|
22
|
+
schema: {
|
|
23
|
+
response: z.object({
|
|
24
|
+
success: z.boolean(),
|
|
25
|
+
projects: z.array(z.object({ id: z.string(), name: z.string() })),
|
|
26
|
+
total: z.number(),
|
|
27
|
+
durationMs: z.number(),
|
|
28
|
+
}),
|
|
29
|
+
},
|
|
30
|
+
|
|
31
|
+
async handler(ctx) {
|
|
32
|
+
const { options } = ctx;
|
|
33
|
+
const started = Date.now();
|
|
34
|
+
const storage = await createStorageAdapter(ctx);
|
|
35
|
+
const result = await storage.listProjects();
|
|
36
|
+
const durationMs = Date.now() - started;
|
|
37
|
+
|
|
38
|
+
if (!options.json) {
|
|
39
|
+
if (result.projects.length === 0) {
|
|
40
|
+
tui.info('No projects found.');
|
|
41
|
+
} else {
|
|
42
|
+
tui.table(
|
|
43
|
+
result.projects.map((p) => ({ id: p.id, name: p.name })),
|
|
44
|
+
['id', 'name']
|
|
45
|
+
);
|
|
46
|
+
tui.info(`${result.projects.length} project(s) (${durationMs.toFixed(1)}ms)`);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return {
|
|
51
|
+
success: true,
|
|
52
|
+
projects: result.projects,
|
|
53
|
+
total: result.projects.length,
|
|
54
|
+
durationMs,
|
|
55
|
+
};
|
|
56
|
+
},
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
// ── Create ──────────────────────────────────────────────────────────────
|
|
60
|
+
|
|
61
|
+
const createProjectSubcommand = createCommand({
|
|
62
|
+
name: 'create',
|
|
63
|
+
description: 'Create a new task project',
|
|
64
|
+
tags: ['mutating', 'slow', 'requires-auth'],
|
|
65
|
+
requires: { auth: true },
|
|
66
|
+
examples: [
|
|
67
|
+
{
|
|
68
|
+
command: getCommand('cloud task project create "My Project"'),
|
|
69
|
+
description: 'Create a new project',
|
|
70
|
+
},
|
|
71
|
+
],
|
|
72
|
+
schema: {
|
|
73
|
+
args: z.object({
|
|
74
|
+
name: z.string().min(1).describe('the project name'),
|
|
75
|
+
}),
|
|
76
|
+
response: z.object({
|
|
77
|
+
success: z.boolean(),
|
|
78
|
+
project: z.object({ id: z.string(), name: z.string() }),
|
|
79
|
+
durationMs: z.number(),
|
|
80
|
+
}),
|
|
81
|
+
},
|
|
82
|
+
|
|
83
|
+
async handler(ctx) {
|
|
84
|
+
const { args, options } = ctx;
|
|
85
|
+
const started = Date.now();
|
|
86
|
+
const storage = await createStorageAdapter(ctx);
|
|
87
|
+
const project = await storage.createProject({ name: args.name });
|
|
88
|
+
const durationMs = Date.now() - started;
|
|
89
|
+
|
|
90
|
+
if (!options.json) {
|
|
91
|
+
tui.success(`Created project: ${project.name} (${project.id})`);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
return { success: true, project, durationMs };
|
|
95
|
+
},
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
// ── Get ─────────────────────────────────────────────────────────────────
|
|
99
|
+
|
|
100
|
+
const getProjectSubcommand = createCommand({
|
|
101
|
+
name: 'get',
|
|
102
|
+
description: 'Get a task project by ID',
|
|
103
|
+
tags: ['read-only', 'slow', 'requires-auth'],
|
|
104
|
+
requires: { auth: true },
|
|
105
|
+
idempotent: true,
|
|
106
|
+
examples: [
|
|
107
|
+
{
|
|
108
|
+
command: getCommand('cloud task project get prj_abc123'),
|
|
109
|
+
description: 'Get project details',
|
|
110
|
+
},
|
|
111
|
+
],
|
|
112
|
+
schema: {
|
|
113
|
+
args: z.object({
|
|
114
|
+
id: z.string().min(1).describe('the project ID'),
|
|
115
|
+
}),
|
|
116
|
+
response: z.object({
|
|
117
|
+
success: z.boolean(),
|
|
118
|
+
project: z.object({ id: z.string(), name: z.string() }),
|
|
119
|
+
durationMs: z.number(),
|
|
120
|
+
}),
|
|
121
|
+
},
|
|
122
|
+
|
|
123
|
+
async handler(ctx) {
|
|
124
|
+
const { args, options } = ctx;
|
|
125
|
+
const started = Date.now();
|
|
126
|
+
const storage = await createStorageAdapter(ctx);
|
|
127
|
+
try {
|
|
128
|
+
const project = await storage.getProject(args.id);
|
|
129
|
+
const durationMs = Date.now() - started;
|
|
130
|
+
|
|
131
|
+
if (!options.json) {
|
|
132
|
+
tui.table([{ id: project.id, name: project.name }], ['id', 'name'], {
|
|
133
|
+
layout: 'vertical',
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
return { success: true, project, durationMs };
|
|
138
|
+
} catch (_err) {
|
|
139
|
+
const durationMs = Date.now() - started;
|
|
140
|
+
if (!options.json) {
|
|
141
|
+
tui.error(`Project not found: ${args.id}`);
|
|
142
|
+
}
|
|
143
|
+
return { success: false, project: { id: args.id, name: '' }, durationMs };
|
|
144
|
+
}
|
|
145
|
+
},
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
// ── Delete ──────────────────────────────────────────────────────────────
|
|
149
|
+
|
|
150
|
+
const deleteProjectSubcommand = createCommand({
|
|
151
|
+
name: 'delete',
|
|
152
|
+
aliases: ['del', 'rm'],
|
|
153
|
+
description: 'Delete a task project',
|
|
154
|
+
tags: ['mutating', 'slow', 'requires-auth', 'destructive'],
|
|
155
|
+
requires: { auth: true },
|
|
156
|
+
examples: [
|
|
157
|
+
{
|
|
158
|
+
command: getCommand('cloud task project delete prj_abc123'),
|
|
159
|
+
description: 'Delete a project',
|
|
160
|
+
},
|
|
161
|
+
],
|
|
162
|
+
schema: {
|
|
163
|
+
args: z.object({
|
|
164
|
+
id: z.string().min(1).describe('the project ID'),
|
|
165
|
+
}),
|
|
166
|
+
response: z.object({
|
|
167
|
+
success: z.boolean(),
|
|
168
|
+
durationMs: z.number(),
|
|
169
|
+
}),
|
|
170
|
+
},
|
|
171
|
+
|
|
172
|
+
async handler(ctx) {
|
|
173
|
+
const { args, options } = ctx;
|
|
174
|
+
const started = Date.now();
|
|
175
|
+
const storage = await createStorageAdapter(ctx);
|
|
176
|
+
try {
|
|
177
|
+
await storage.deleteProject(args.id);
|
|
178
|
+
const durationMs = Date.now() - started;
|
|
179
|
+
|
|
180
|
+
if (!options.json) {
|
|
181
|
+
tui.success(`Deleted project: ${args.id}`);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
return { success: true, durationMs };
|
|
185
|
+
} catch (_err) {
|
|
186
|
+
const durationMs = Date.now() - started;
|
|
187
|
+
if (!options.json) {
|
|
188
|
+
tui.error(`Failed to delete project: ${args.id}`);
|
|
189
|
+
}
|
|
190
|
+
return { success: false, durationMs };
|
|
191
|
+
}
|
|
192
|
+
},
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
// ── Parent command ──────────────────────────────────────────────────────
|
|
196
|
+
|
|
197
|
+
export const projectSubcommand = createCommand({
|
|
198
|
+
name: 'project',
|
|
199
|
+
aliases: ['proj'],
|
|
200
|
+
description: 'Manage task projects',
|
|
201
|
+
tags: ['requires-auth'],
|
|
202
|
+
requires: { auth: true },
|
|
203
|
+
examples: [
|
|
204
|
+
{
|
|
205
|
+
command: getCommand('cloud task project list'),
|
|
206
|
+
description: 'List all projects',
|
|
207
|
+
},
|
|
208
|
+
{
|
|
209
|
+
command: getCommand('cloud task project create "My Project"'),
|
|
210
|
+
description: 'Create a new project',
|
|
211
|
+
},
|
|
212
|
+
{
|
|
213
|
+
command: getCommand('cloud task project get prj_abc123'),
|
|
214
|
+
description: 'Get project details',
|
|
215
|
+
},
|
|
216
|
+
{
|
|
217
|
+
command: getCommand('cloud task project delete prj_abc123'),
|
|
218
|
+
description: 'Delete a project',
|
|
219
|
+
},
|
|
220
|
+
],
|
|
221
|
+
subcommands: [
|
|
222
|
+
listProjectsSubcommand,
|
|
223
|
+
createProjectSubcommand,
|
|
224
|
+
getProjectSubcommand,
|
|
225
|
+
deleteProjectSubcommand,
|
|
226
|
+
],
|
|
227
|
+
});
|
|
@@ -55,7 +55,10 @@ export const updateSubcommand = createCommand({
|
|
|
55
55
|
.enum(['epic', 'feature', 'enhancement', 'bug', 'task'])
|
|
56
56
|
.optional()
|
|
57
57
|
.describe('new task type'),
|
|
58
|
-
status: z
|
|
58
|
+
status: z
|
|
59
|
+
.enum(['open', 'in_progress', 'done', 'cancelled'])
|
|
60
|
+
.optional()
|
|
61
|
+
.describe('new task status'),
|
|
59
62
|
assignedId: z.string().optional().describe('new assigned agent or user ID'),
|
|
60
63
|
parentId: z.string().optional().describe('new parent task ID'),
|
|
61
64
|
closedId: z.string().optional().describe('ID of the closer (agent or user)'),
|