@agentuity/cli 2.0.6 → 2.0.8
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/README.md +11 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +4 -2
- package/dist/cli.js.map +1 -1
- package/dist/cmd/build/vite/route-discovery.d.ts.map +1 -1
- package/dist/cmd/build/vite/route-discovery.js +6 -0
- package/dist/cmd/build/vite/route-discovery.js.map +1 -1
- package/dist/cmd/cloud/sandbox/fs/rm.d.ts.map +1 -1
- package/dist/cmd/cloud/sandbox/fs/rm.js +9 -3
- package/dist/cmd/cloud/sandbox/fs/rm.js.map +1 -1
- package/dist/cmd/cloud/sandbox/fs/rmdir.d.ts.map +1 -1
- package/dist/cmd/cloud/sandbox/fs/rmdir.js +9 -3
- package/dist/cmd/cloud/sandbox/fs/rmdir.js.map +1 -1
- package/dist/cmd/cloud/task/close.d.ts +3 -0
- package/dist/cmd/cloud/task/close.d.ts.map +1 -0
- package/dist/cmd/cloud/task/close.js +286 -0
- package/dist/cmd/cloud/task/close.js.map +1 -0
- package/dist/cmd/cloud/task/delete.d.ts +1 -5
- package/dist/cmd/cloud/task/delete.d.ts.map +1 -1
- package/dist/cmd/cloud/task/delete.js +15 -38
- package/dist/cmd/cloud/task/delete.js.map +1 -1
- package/dist/cmd/cloud/task/index.d.ts.map +1 -1
- package/dist/cmd/cloud/task/index.js +10 -0
- 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 +97 -3
- package/dist/cmd/cloud/task/list.js.map +1 -1
- package/dist/cmd/cloud/task/util.d.ts +10 -0
- package/dist/cmd/cloud/task/util.d.ts.map +1 -1
- package/dist/cmd/cloud/task/util.js +47 -3
- package/dist/cmd/cloud/task/util.js.map +1 -1
- package/dist/cmd/coder/archive.d.ts +2 -0
- package/dist/cmd/coder/archive.d.ts.map +1 -0
- package/dist/cmd/coder/archive.js +57 -0
- package/dist/cmd/coder/archive.js.map +1 -0
- package/dist/cmd/coder/create.d.ts +2 -0
- package/dist/cmd/coder/create.d.ts.map +1 -0
- package/dist/cmd/coder/create.js +245 -0
- package/dist/cmd/coder/create.js.map +1 -0
- package/dist/cmd/coder/delete.d.ts +2 -0
- package/dist/cmd/coder/delete.d.ts.map +1 -0
- package/dist/cmd/coder/delete.js +64 -0
- package/dist/cmd/coder/delete.js.map +1 -0
- package/dist/cmd/coder/events.d.ts +2 -0
- package/dist/cmd/coder/events.d.ts.map +1 -0
- package/dist/cmd/coder/events.js +99 -0
- package/dist/cmd/coder/events.js.map +1 -0
- package/dist/cmd/coder/extension-path.d.ts +8 -0
- package/dist/cmd/coder/extension-path.d.ts.map +1 -0
- package/dist/cmd/coder/extension-path.js +59 -0
- package/dist/cmd/coder/extension-path.js.map +1 -0
- package/dist/cmd/coder/get.d.ts +2 -0
- package/dist/cmd/coder/get.d.ts.map +1 -0
- package/dist/cmd/coder/{inspect.js → get.js} +37 -33
- package/dist/cmd/coder/get.js.map +1 -0
- package/dist/cmd/coder/index.d.ts.map +1 -1
- package/dist/cmd/coder/index.js +54 -4
- package/dist/cmd/coder/index.js.map +1 -1
- package/dist/cmd/coder/list.d.ts.map +1 -1
- package/dist/cmd/coder/list.js +25 -34
- package/dist/cmd/coder/list.js.map +1 -1
- package/dist/cmd/coder/loop.d.ts +2 -0
- package/dist/cmd/coder/loop.d.ts.map +1 -0
- package/dist/cmd/coder/loop.js +78 -0
- package/dist/cmd/coder/loop.js.map +1 -0
- package/dist/cmd/coder/participants.d.ts +2 -0
- package/dist/cmd/coder/participants.d.ts.map +1 -0
- package/dist/cmd/coder/participants.js +93 -0
- package/dist/cmd/coder/participants.js.map +1 -0
- package/dist/cmd/coder/replay.d.ts +2 -0
- package/dist/cmd/coder/replay.d.ts.map +1 -0
- package/dist/cmd/coder/replay.js +53 -0
- package/dist/cmd/coder/replay.js.map +1 -0
- package/dist/cmd/coder/resolve-repo.d.ts +27 -0
- package/dist/cmd/coder/resolve-repo.d.ts.map +1 -0
- package/dist/cmd/coder/resolve-repo.js +97 -0
- package/dist/cmd/coder/resolve-repo.js.map +1 -0
- package/dist/cmd/coder/skill/buckets.d.ts +2 -0
- package/dist/cmd/coder/skill/buckets.d.ts.map +1 -0
- package/dist/cmd/coder/skill/buckets.js +174 -0
- package/dist/cmd/coder/skill/buckets.js.map +1 -0
- package/dist/cmd/coder/skill/delete.d.ts +2 -0
- package/dist/cmd/coder/skill/delete.d.ts.map +1 -0
- package/dist/cmd/coder/skill/delete.js +64 -0
- package/dist/cmd/coder/skill/delete.js.map +1 -0
- package/dist/cmd/coder/skill/index.d.ts +2 -0
- package/dist/cmd/coder/skill/index.d.ts.map +1 -0
- package/dist/cmd/coder/skill/index.js +33 -0
- package/dist/cmd/coder/skill/index.js.map +1 -0
- package/dist/cmd/coder/skill/list.d.ts +2 -0
- package/dist/cmd/coder/skill/list.d.ts.map +1 -0
- package/dist/cmd/coder/skill/list.js +93 -0
- package/dist/cmd/coder/skill/list.js.map +1 -0
- package/dist/cmd/coder/skill/save.d.ts +2 -0
- package/dist/cmd/coder/skill/save.d.ts.map +1 -0
- package/dist/cmd/coder/skill/save.js +77 -0
- package/dist/cmd/coder/skill/save.js.map +1 -0
- package/dist/cmd/coder/start.d.ts.map +1 -1
- package/dist/cmd/coder/start.js +88 -117
- package/dist/cmd/coder/start.js.map +1 -1
- package/dist/cmd/coder/tui-init.d.ts +4 -1
- package/dist/cmd/coder/tui-init.d.ts.map +1 -1
- package/dist/cmd/coder/tui-init.js +9 -3
- package/dist/cmd/coder/tui-init.js.map +1 -1
- package/dist/cmd/coder/update.d.ts +2 -0
- package/dist/cmd/coder/update.d.ts.map +1 -0
- package/dist/cmd/coder/update.js +126 -0
- package/dist/cmd/coder/update.js.map +1 -0
- package/dist/cmd/coder/users.d.ts +2 -0
- package/dist/cmd/coder/users.d.ts.map +1 -0
- package/dist/cmd/coder/users.js +97 -0
- package/dist/cmd/coder/users.js.map +1 -0
- package/dist/cmd/coder/workspace/create.d.ts +2 -0
- package/dist/cmd/coder/workspace/create.d.ts.map +1 -0
- package/dist/cmd/coder/workspace/create.js +97 -0
- package/dist/cmd/coder/workspace/create.js.map +1 -0
- package/dist/cmd/coder/workspace/delete.d.ts +2 -0
- package/dist/cmd/coder/workspace/delete.d.ts.map +1 -0
- package/dist/cmd/coder/workspace/delete.js +64 -0
- package/dist/cmd/coder/workspace/delete.js.map +1 -0
- package/dist/cmd/coder/workspace/get.d.ts +2 -0
- package/dist/cmd/coder/workspace/get.d.ts.map +1 -0
- package/dist/cmd/coder/workspace/get.js +109 -0
- package/dist/cmd/coder/workspace/get.js.map +1 -0
- package/dist/cmd/coder/workspace/index.d.ts +2 -0
- package/dist/cmd/coder/workspace/index.d.ts.map +1 -0
- package/dist/cmd/coder/workspace/index.js +38 -0
- package/dist/cmd/coder/workspace/index.js.map +1 -0
- package/dist/cmd/coder/workspace/list.d.ts +2 -0
- package/dist/cmd/coder/workspace/list.d.ts.map +1 -0
- package/dist/cmd/coder/workspace/list.js +93 -0
- package/dist/cmd/coder/workspace/list.js.map +1 -0
- package/dist/cmd/dev/sync.js +5 -5
- package/dist/cmd/dev/sync.js.map +1 -1
- package/dist/coder-hub-url.d.ts +3 -0
- package/dist/coder-hub-url.d.ts.map +1 -0
- package/dist/coder-hub-url.js +32 -0
- package/dist/coder-hub-url.js.map +1 -0
- package/dist/config.d.ts +1 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +14 -3
- package/dist/config.js.map +1 -1
- package/dist/internal-logger.d.ts +4 -0
- package/dist/internal-logger.d.ts.map +1 -1
- package/dist/internal-logger.js +64 -2
- package/dist/internal-logger.js.map +1 -1
- package/dist/keychain.d.ts +3 -0
- package/dist/keychain.d.ts.map +1 -1
- package/dist/keychain.js +47 -28
- package/dist/keychain.js.map +1 -1
- package/dist/types.d.ts +1 -1
- package/package.json +7 -6
- package/src/cli.ts +4 -2
- package/src/cmd/ai/prompt/agent.md +6 -6
- package/src/cmd/build/vite/route-discovery.ts +8 -0
- package/src/cmd/cloud/sandbox/fs/rm.ts +8 -3
- package/src/cmd/cloud/sandbox/fs/rmdir.ts +8 -3
- package/src/cmd/cloud/task/close.ts +319 -0
- package/src/cmd/cloud/task/delete.ts +15 -43
- package/src/cmd/cloud/task/index.ts +10 -0
- package/src/cmd/cloud/task/list.ts +111 -4
- package/src/cmd/cloud/task/util.ts +59 -5
- package/src/cmd/coder/archive.ts +59 -0
- package/src/cmd/coder/create.ts +268 -0
- package/src/cmd/coder/delete.ts +67 -0
- package/src/cmd/coder/events.ts +106 -0
- package/src/cmd/coder/extension-path.ts +71 -0
- package/src/cmd/coder/{inspect.ts → get.ts} +44 -45
- package/src/cmd/coder/index.ts +54 -4
- package/src/cmd/coder/list.ts +28 -65
- package/src/cmd/coder/loop.ts +85 -0
- package/src/cmd/coder/participants.ts +100 -0
- package/src/cmd/coder/replay.ts +58 -0
- package/src/cmd/coder/resolve-repo.ts +119 -0
- package/src/cmd/coder/skill/buckets.ts +191 -0
- package/src/cmd/coder/skill/delete.ts +67 -0
- package/src/cmd/coder/skill/index.ts +35 -0
- package/src/cmd/coder/skill/list.ts +97 -0
- package/src/cmd/coder/skill/save.ts +84 -0
- package/src/cmd/coder/start.ts +104 -141
- package/src/cmd/coder/tui-init.ts +13 -4
- package/src/cmd/coder/update.ts +128 -0
- package/src/cmd/coder/users.ts +101 -0
- package/src/cmd/coder/workspace/create.ts +104 -0
- package/src/cmd/coder/workspace/delete.ts +70 -0
- package/src/cmd/coder/workspace/get.ts +112 -0
- package/src/cmd/coder/workspace/index.ts +38 -0
- package/src/cmd/coder/workspace/list.ts +101 -0
- package/src/cmd/dev/sync.ts +5 -5
- package/src/coder-hub-url.ts +32 -0
- package/src/config.ts +17 -3
- package/src/internal-logger.ts +83 -2
- package/src/keychain.ts +68 -39
- package/dist/cmd/coder/hub-url.d.ts +0 -36
- package/dist/cmd/coder/hub-url.d.ts.map +0 -1
- package/dist/cmd/coder/hub-url.js +0 -106
- package/dist/cmd/coder/hub-url.js.map +0 -1
- package/dist/cmd/coder/inspect.d.ts +0 -2
- package/dist/cmd/coder/inspect.d.ts.map +0 -1
- package/dist/cmd/coder/inspect.js.map +0 -1
- package/src/cmd/coder/hub-url.ts +0 -111
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import type { CoderClient, CoderSessionRepositoryRef } from '@agentuity/core/coder';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Parse a GitHub reference string into owner/repo/branch.
|
|
5
|
+
* Accepts:
|
|
6
|
+
* - github.com/owner/repo
|
|
7
|
+
* - https://github.com/owner/repo
|
|
8
|
+
* - https://github.com/owner/repo.git
|
|
9
|
+
* - owner/repo
|
|
10
|
+
* - owner/repo#branch
|
|
11
|
+
* - https://github.com/owner/repo/tree/branch
|
|
12
|
+
*/
|
|
13
|
+
export function parseGitHubRef(
|
|
14
|
+
input: string
|
|
15
|
+
): { owner: string; repo: string; branch?: string } | null {
|
|
16
|
+
let clean = input.trim();
|
|
17
|
+
|
|
18
|
+
// Handle owner/repo#branch
|
|
19
|
+
let branch: string | undefined;
|
|
20
|
+
const hashIdx = clean.indexOf('#');
|
|
21
|
+
if (hashIdx > 0) {
|
|
22
|
+
branch = clean.slice(hashIdx + 1);
|
|
23
|
+
clean = clean.slice(0, hashIdx);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// Handle URLs
|
|
27
|
+
if (clean.includes('github.com')) {
|
|
28
|
+
try {
|
|
29
|
+
const url = new URL(clean.startsWith('http') ? clean : `https://${clean}`);
|
|
30
|
+
const parts = url.pathname
|
|
31
|
+
.replace(/^\//, '')
|
|
32
|
+
.replace(/\.git$/, '')
|
|
33
|
+
.split('/');
|
|
34
|
+
const owner = parts[0];
|
|
35
|
+
const repo = parts[1];
|
|
36
|
+
if (parts.length >= 2 && owner && repo) {
|
|
37
|
+
// Handle /tree/branch paths
|
|
38
|
+
if (parts.length >= 4 && parts[2] === 'tree') {
|
|
39
|
+
branch = branch || parts.slice(3).join('/');
|
|
40
|
+
}
|
|
41
|
+
return { owner, repo, branch };
|
|
42
|
+
}
|
|
43
|
+
} catch {
|
|
44
|
+
// fall through
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Handle owner/repo
|
|
49
|
+
const slashParts = clean.replace(/\.git$/, '').split('/');
|
|
50
|
+
if (slashParts.length === 2 && slashParts[0] && slashParts[1]) {
|
|
51
|
+
return { owner: slashParts[0], repo: slashParts[1], branch };
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Resolve a GitHub reference to a full repository ref by calling the Hub's GitHub APIs.
|
|
59
|
+
*
|
|
60
|
+
* Flow:
|
|
61
|
+
* 1. Parse the input to get owner/repo
|
|
62
|
+
* 2. List GitHub accounts → find the one matching the owner
|
|
63
|
+
* 3. List repos for that account → find the matching repo
|
|
64
|
+
* 4. Build full CoderSessionRepositoryRef with all metadata
|
|
65
|
+
*/
|
|
66
|
+
export async function resolveGitHubRepo(
|
|
67
|
+
client: CoderClient,
|
|
68
|
+
input: string,
|
|
69
|
+
branchOverride?: string
|
|
70
|
+
): Promise<CoderSessionRepositoryRef> {
|
|
71
|
+
const parsed = parseGitHubRef(input);
|
|
72
|
+
if (!parsed) {
|
|
73
|
+
throw new Error(`Could not parse GitHub reference: ${input}`);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const { owner, repo, branch: parsedBranch } = parsed;
|
|
77
|
+
const branch = branchOverride || parsedBranch;
|
|
78
|
+
|
|
79
|
+
// Step 1: List GitHub accounts
|
|
80
|
+
const accountsResponse = await client.listGitHubAccounts();
|
|
81
|
+
if (!accountsResponse.connected) {
|
|
82
|
+
throw new Error('GitHub is not connected. Please connect GitHub via the Coder web UI first.');
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const account = accountsResponse.accounts.find(
|
|
86
|
+
(a) => a.accountName.toLowerCase() === owner.toLowerCase()
|
|
87
|
+
);
|
|
88
|
+
if (!account) {
|
|
89
|
+
const available = accountsResponse.accounts.map((a) => a.accountName).join(', ');
|
|
90
|
+
throw new Error(
|
|
91
|
+
`GitHub account "${owner}" not found. Available accounts: ${available || 'none'}`
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// Step 2: List repos for that account
|
|
96
|
+
const reposResponse = await client.listGitHubRepos(account.accountId);
|
|
97
|
+
const repoMatch = reposResponse.repositories.find(
|
|
98
|
+
(r) => r.name.toLowerCase() === repo.toLowerCase()
|
|
99
|
+
);
|
|
100
|
+
if (!repoMatch) {
|
|
101
|
+
throw new Error(`Repository "${repo}" not found in GitHub account "${owner}".`);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Step 3: Build full repo ref
|
|
105
|
+
return {
|
|
106
|
+
url: repoMatch.cloneUrl,
|
|
107
|
+
branch: branch || repoMatch.defaultBranch,
|
|
108
|
+
provider: 'github',
|
|
109
|
+
name: repoMatch.name,
|
|
110
|
+
fullName: repoMatch.fullName,
|
|
111
|
+
defaultBranch: repoMatch.defaultBranch,
|
|
112
|
+
private: repoMatch.private,
|
|
113
|
+
htmlUrl: repoMatch.htmlUrl,
|
|
114
|
+
accountId: account.accountId,
|
|
115
|
+
accountName: account.accountName,
|
|
116
|
+
accountType: account.accountType,
|
|
117
|
+
accountAvatarUrl: account.avatarUrl,
|
|
118
|
+
} as CoderSessionRepositoryRef;
|
|
119
|
+
}
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { CoderClient, type CoderSkillBucket } from '@agentuity/core/coder';
|
|
3
|
+
import { ValidationOutputError } from '@agentuity/core';
|
|
4
|
+
import { createSubcommand } from '../../../types';
|
|
5
|
+
import * as tui from '../../../tui';
|
|
6
|
+
import { getCommand } from '../../../command-prefix';
|
|
7
|
+
import { ErrorCode } from '../../../errors';
|
|
8
|
+
|
|
9
|
+
function formatRelativeTime(isoDate: string): string {
|
|
10
|
+
const parsed = new Date(isoDate).getTime();
|
|
11
|
+
if (Number.isNaN(parsed)) return 'unknown';
|
|
12
|
+
const diffMs = Math.max(0, Date.now() - parsed);
|
|
13
|
+
const seconds = Math.floor(diffMs / 1000);
|
|
14
|
+
if (seconds < 60) return `${seconds}s ago`;
|
|
15
|
+
const minutes = Math.floor(seconds / 60);
|
|
16
|
+
if (minutes < 60) return `${minutes}m ago`;
|
|
17
|
+
const hours = Math.floor(minutes / 60);
|
|
18
|
+
if (hours < 24) return `${hours}h ago`;
|
|
19
|
+
const days = Math.floor(hours / 24);
|
|
20
|
+
return `${days}d ago`;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export const bucketsSubcommand = createSubcommand({
|
|
24
|
+
name: 'buckets',
|
|
25
|
+
aliases: ['bucket'],
|
|
26
|
+
description: 'List, create, or delete skill buckets',
|
|
27
|
+
tags: ['requires-auth'],
|
|
28
|
+
requires: { auth: true, org: true },
|
|
29
|
+
examples: [
|
|
30
|
+
{
|
|
31
|
+
command: getCommand('coder skill buckets'),
|
|
32
|
+
description: 'List skill buckets',
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
command: getCommand('coder skill buckets --create "My Bucket"'),
|
|
36
|
+
description: 'Create a new skill bucket',
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
command: getCommand(
|
|
40
|
+
'coder skill buckets --create "My Bucket" --description "Frontend skills"'
|
|
41
|
+
),
|
|
42
|
+
description: 'Create a skill bucket with description',
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
command: getCommand('coder skill buckets --delete bucket_abc123'),
|
|
46
|
+
description: 'Delete a skill bucket',
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
command: getCommand('coder skill buckets --json'),
|
|
50
|
+
description: 'List skill buckets as JSON',
|
|
51
|
+
},
|
|
52
|
+
],
|
|
53
|
+
schema: {
|
|
54
|
+
options: z.object({
|
|
55
|
+
url: z.string().optional().describe('Coder API URL override'),
|
|
56
|
+
create: z.string().optional().describe('Create a new bucket with this name'),
|
|
57
|
+
delete: z.string().optional().describe('Delete bucket by ID'),
|
|
58
|
+
description: z
|
|
59
|
+
.string()
|
|
60
|
+
.optional()
|
|
61
|
+
.describe('Description for new bucket (used with --create)'),
|
|
62
|
+
}),
|
|
63
|
+
},
|
|
64
|
+
async handler(ctx) {
|
|
65
|
+
const { opts, options } = ctx;
|
|
66
|
+
|
|
67
|
+
// Validate mutually exclusive actions
|
|
68
|
+
if (opts?.create && opts?.delete) {
|
|
69
|
+
tui.fatal('Cannot use --create and --delete together.', ErrorCode.VALIDATION_FAILED);
|
|
70
|
+
}
|
|
71
|
+
if (opts?.create && !opts.create.trim()) {
|
|
72
|
+
tui.fatal('--create requires a non-empty bucket name.', ErrorCode.VALIDATION_FAILED);
|
|
73
|
+
}
|
|
74
|
+
if (opts?.delete && !opts.delete.trim()) {
|
|
75
|
+
tui.fatal('--delete requires a non-empty bucket ID.', ErrorCode.VALIDATION_FAILED);
|
|
76
|
+
}
|
|
77
|
+
if (opts?.description && !opts?.create) {
|
|
78
|
+
tui.fatal('--description can only be used with --create.', ErrorCode.VALIDATION_FAILED);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const client = new CoderClient({
|
|
82
|
+
apiKey: ctx.auth.apiKey,
|
|
83
|
+
url: opts?.url,
|
|
84
|
+
orgId: ctx.orgId,
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
// Delete a bucket
|
|
88
|
+
if (opts?.delete) {
|
|
89
|
+
if (!options.json) {
|
|
90
|
+
const confirmed = await tui.confirm(`Delete skill bucket ${opts.delete}?`, false);
|
|
91
|
+
if (!confirmed) {
|
|
92
|
+
tui.info('Cancelled.');
|
|
93
|
+
return { deleted: false, id: opts.delete };
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
try {
|
|
98
|
+
await client.deleteSkillBucket(opts.delete);
|
|
99
|
+
} catch (err) {
|
|
100
|
+
if (err instanceof ValidationOutputError) {
|
|
101
|
+
ctx.logger.trace('Validation response URL: %s', err.url ?? 'unknown');
|
|
102
|
+
ctx.logger.trace('Validation issues: %s', JSON.stringify(err.issues, null, 2));
|
|
103
|
+
}
|
|
104
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
105
|
+
tui.fatal(
|
|
106
|
+
`Failed to delete skill bucket ${opts.delete}: ${msg}`,
|
|
107
|
+
ErrorCode.NETWORK_ERROR
|
|
108
|
+
);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
if (options.json) {
|
|
112
|
+
return { deleted: true, id: opts.delete };
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
tui.success(`Skill bucket ${opts.delete} deleted.`);
|
|
116
|
+
return { deleted: true, id: opts.delete };
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// Create a bucket
|
|
120
|
+
if (opts?.create) {
|
|
121
|
+
try {
|
|
122
|
+
const created = await client.createSkillBucket({
|
|
123
|
+
name: opts.create,
|
|
124
|
+
...(opts?.description && { description: opts.description }),
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
if (options.json) {
|
|
128
|
+
return created;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
tui.success(`Skill bucket ${created.id} created.`);
|
|
132
|
+
tui.newline();
|
|
133
|
+
tui.output(` Name: ${tui.bold(created.name)}`);
|
|
134
|
+
if (created.description) {
|
|
135
|
+
tui.output(` Desc: ${created.description}`);
|
|
136
|
+
}
|
|
137
|
+
tui.output(` Skills: ${created.skillCount}`);
|
|
138
|
+
|
|
139
|
+
return created;
|
|
140
|
+
} catch (err) {
|
|
141
|
+
if (err instanceof ValidationOutputError) {
|
|
142
|
+
ctx.logger.trace('Validation response URL: %s', err.url ?? 'unknown');
|
|
143
|
+
ctx.logger.trace('Validation issues: %s', JSON.stringify(err.issues, null, 2));
|
|
144
|
+
}
|
|
145
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
146
|
+
tui.fatal(`Failed to create skill bucket: ${msg}`, ErrorCode.NETWORK_ERROR);
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// Default: list buckets
|
|
152
|
+
let buckets: CoderSkillBucket[] = [];
|
|
153
|
+
try {
|
|
154
|
+
const response = await client.listSkillBuckets();
|
|
155
|
+
buckets = response.buckets;
|
|
156
|
+
} catch (err) {
|
|
157
|
+
if (err instanceof ValidationOutputError) {
|
|
158
|
+
ctx.logger.trace('Validation response URL: %s', err.url ?? 'unknown');
|
|
159
|
+
ctx.logger.trace('Validation issues: %s', JSON.stringify(err.issues, null, 2));
|
|
160
|
+
}
|
|
161
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
162
|
+
tui.fatal(`Failed to list Coder skill buckets: ${msg}`, ErrorCode.NETWORK_ERROR);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
if (options.json) {
|
|
166
|
+
return buckets;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
if (buckets.length === 0) {
|
|
170
|
+
tui.info('No Coder skill buckets found.');
|
|
171
|
+
return [];
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
tui.table(
|
|
175
|
+
buckets.map((b) => ({
|
|
176
|
+
ID: b.id,
|
|
177
|
+
Name: b.name,
|
|
178
|
+
Skills: String(b.skillCount),
|
|
179
|
+
Created: formatRelativeTime(b.createdAt),
|
|
180
|
+
})),
|
|
181
|
+
[
|
|
182
|
+
{ name: 'ID', alignment: 'left' },
|
|
183
|
+
{ name: 'Name', alignment: 'left' },
|
|
184
|
+
{ name: 'Skills', alignment: 'right' },
|
|
185
|
+
{ name: 'Created', alignment: 'right' },
|
|
186
|
+
]
|
|
187
|
+
);
|
|
188
|
+
|
|
189
|
+
return buckets;
|
|
190
|
+
},
|
|
191
|
+
});
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { CoderClient } from '@agentuity/core/coder';
|
|
3
|
+
import { ValidationOutputError } from '@agentuity/core';
|
|
4
|
+
import { createSubcommand } from '../../../types';
|
|
5
|
+
import * as tui from '../../../tui';
|
|
6
|
+
import { getCommand } from '../../../command-prefix';
|
|
7
|
+
import { ErrorCode } from '../../../errors';
|
|
8
|
+
|
|
9
|
+
export const deleteSkillSubcommand = createSubcommand({
|
|
10
|
+
name: 'delete',
|
|
11
|
+
aliases: ['rm', 'del', 'remove'],
|
|
12
|
+
description: 'Delete a saved skill from your library',
|
|
13
|
+
tags: ['destructive', 'deletes-resource', 'requires-auth'],
|
|
14
|
+
requires: { auth: true, org: true },
|
|
15
|
+
examples: [
|
|
16
|
+
{
|
|
17
|
+
command: getCommand('coder skill delete sk_abc123'),
|
|
18
|
+
description: 'Delete a saved skill',
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
command: getCommand('coder skill delete sk_abc123 --json'),
|
|
22
|
+
description: 'Delete a saved skill and return JSON output',
|
|
23
|
+
},
|
|
24
|
+
],
|
|
25
|
+
schema: {
|
|
26
|
+
args: z.object({
|
|
27
|
+
id: z.string().describe('Saved skill ID to delete'),
|
|
28
|
+
}),
|
|
29
|
+
options: z.object({
|
|
30
|
+
url: z.string().optional().describe('Coder API URL override'),
|
|
31
|
+
}),
|
|
32
|
+
},
|
|
33
|
+
async handler(ctx) {
|
|
34
|
+
const { args, opts, options } = ctx;
|
|
35
|
+
const client = new CoderClient({
|
|
36
|
+
apiKey: ctx.auth.apiKey,
|
|
37
|
+
url: opts?.url,
|
|
38
|
+
orgId: ctx.orgId,
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
if (!options.json) {
|
|
42
|
+
const confirmed = await tui.confirm(`Delete saved skill ${args.id}?`, false);
|
|
43
|
+
if (!confirmed) {
|
|
44
|
+
tui.info('Cancelled.');
|
|
45
|
+
return { deleted: false, id: args.id };
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
try {
|
|
50
|
+
await client.deleteSavedSkill(args.id);
|
|
51
|
+
} catch (err) {
|
|
52
|
+
if (err instanceof ValidationOutputError) {
|
|
53
|
+
ctx.logger.trace('Validation response URL: %s', err.url ?? 'unknown');
|
|
54
|
+
ctx.logger.trace('Validation issues: %s', JSON.stringify(err.issues, null, 2));
|
|
55
|
+
}
|
|
56
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
57
|
+
tui.fatal(`Failed to delete saved skill ${args.id}: ${msg}`, ErrorCode.NETWORK_ERROR);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (options.json) {
|
|
61
|
+
return { deleted: true, id: args.id };
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
tui.success(`Saved skill ${args.id} deleted.`);
|
|
65
|
+
return { deleted: true, id: args.id };
|
|
66
|
+
},
|
|
67
|
+
});
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { createCommand } from '../../../types';
|
|
2
|
+
import { listSubcommand } from './list';
|
|
3
|
+
import { saveSkillSubcommand } from './save';
|
|
4
|
+
import { deleteSkillSubcommand } from './delete';
|
|
5
|
+
import { bucketsSubcommand } from './buckets';
|
|
6
|
+
import { getCommand } from '../../../command-prefix';
|
|
7
|
+
|
|
8
|
+
export const skillCommand = createCommand({
|
|
9
|
+
name: 'skill',
|
|
10
|
+
aliases: ['skills'],
|
|
11
|
+
description: 'Manage Coder saved skills and skill buckets',
|
|
12
|
+
tags: ['requires-auth'],
|
|
13
|
+
requires: { auth: true, org: true },
|
|
14
|
+
examples: [
|
|
15
|
+
{
|
|
16
|
+
command: getCommand('coder skill list'),
|
|
17
|
+
description: 'List saved skills',
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
command: getCommand(
|
|
21
|
+
'coder skill save --repo org/repo --skill-id sk_abc --name "My Skill"'
|
|
22
|
+
),
|
|
23
|
+
description: 'Save a skill to your library',
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
command: getCommand('coder skill delete sk_abc123'),
|
|
27
|
+
description: 'Delete a saved skill',
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
command: getCommand('coder skill buckets'),
|
|
31
|
+
description: 'List skill buckets',
|
|
32
|
+
},
|
|
33
|
+
],
|
|
34
|
+
subcommands: [listSubcommand, saveSkillSubcommand, deleteSkillSubcommand, bucketsSubcommand],
|
|
35
|
+
});
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { CoderClient, type CoderSavedSkill, CoderSavedSkillSchema } from '@agentuity/core/coder';
|
|
3
|
+
import { ValidationOutputError } from '@agentuity/core';
|
|
4
|
+
import { createSubcommand } from '../../../types';
|
|
5
|
+
import * as tui from '../../../tui';
|
|
6
|
+
import { getCommand } from '../../../command-prefix';
|
|
7
|
+
import { ErrorCode } from '../../../errors';
|
|
8
|
+
|
|
9
|
+
function formatRelativeTime(isoDate: string): string {
|
|
10
|
+
const parsed = new Date(isoDate).getTime();
|
|
11
|
+
if (Number.isNaN(parsed)) return 'unknown';
|
|
12
|
+
const diffMs = Math.max(0, Date.now() - parsed);
|
|
13
|
+
const seconds = Math.floor(diffMs / 1000);
|
|
14
|
+
if (seconds < 60) return `${seconds}s ago`;
|
|
15
|
+
const minutes = Math.floor(seconds / 60);
|
|
16
|
+
if (minutes < 60) return `${minutes}m ago`;
|
|
17
|
+
const hours = Math.floor(minutes / 60);
|
|
18
|
+
if (hours < 24) return `${hours}h ago`;
|
|
19
|
+
const days = Math.floor(hours / 24);
|
|
20
|
+
return `${days}d ago`;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export const listSubcommand = createSubcommand({
|
|
24
|
+
name: 'list',
|
|
25
|
+
aliases: ['ls'],
|
|
26
|
+
description: 'List saved skills',
|
|
27
|
+
tags: ['read-only', 'fast', 'requires-auth'],
|
|
28
|
+
idempotent: true,
|
|
29
|
+
requires: { auth: true, org: true },
|
|
30
|
+
examples: [
|
|
31
|
+
{
|
|
32
|
+
command: getCommand('coder skill list'),
|
|
33
|
+
description: 'List saved skills',
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
command: getCommand('coder skill list --json'),
|
|
37
|
+
description: 'Return saved skills as JSON',
|
|
38
|
+
},
|
|
39
|
+
],
|
|
40
|
+
schema: {
|
|
41
|
+
options: z.object({
|
|
42
|
+
url: z.string().optional().describe('Coder API URL override'),
|
|
43
|
+
}),
|
|
44
|
+
response: z.array(CoderSavedSkillSchema),
|
|
45
|
+
},
|
|
46
|
+
async handler(ctx) {
|
|
47
|
+
const { options, opts } = ctx;
|
|
48
|
+
const client = new CoderClient({
|
|
49
|
+
apiKey: ctx.auth.apiKey,
|
|
50
|
+
url: opts?.url,
|
|
51
|
+
orgId: ctx.orgId,
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
let skills: CoderSavedSkill[] = [];
|
|
55
|
+
try {
|
|
56
|
+
const response = await client.listSavedSkills();
|
|
57
|
+
skills = response.skills;
|
|
58
|
+
} catch (err) {
|
|
59
|
+
if (err instanceof ValidationOutputError) {
|
|
60
|
+
ctx.logger.trace('Validation response URL: %s', err.url ?? 'unknown');
|
|
61
|
+
ctx.logger.trace('Validation issues: %s', JSON.stringify(err.issues, null, 2));
|
|
62
|
+
}
|
|
63
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
64
|
+
tui.fatal(`Failed to list Coder saved skills: ${msg}`, ErrorCode.NETWORK_ERROR);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
if (options.json) {
|
|
68
|
+
return skills;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if (skills.length === 0) {
|
|
72
|
+
tui.info('No Coder saved skills found.');
|
|
73
|
+
return [];
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
tui.table(
|
|
77
|
+
skills.map((s) => ({
|
|
78
|
+
ID: s.id,
|
|
79
|
+
Name: s.name,
|
|
80
|
+
Repo: s.repo,
|
|
81
|
+
Source: s.source,
|
|
82
|
+
Installs: s.installs !== undefined ? String(s.installs) : '-',
|
|
83
|
+
Created: formatRelativeTime(s.createdAt),
|
|
84
|
+
})),
|
|
85
|
+
[
|
|
86
|
+
{ name: 'ID', alignment: 'left' },
|
|
87
|
+
{ name: 'Name', alignment: 'left' },
|
|
88
|
+
{ name: 'Repo', alignment: 'left' },
|
|
89
|
+
{ name: 'Source', alignment: 'left' },
|
|
90
|
+
{ name: 'Installs', alignment: 'right' },
|
|
91
|
+
{ name: 'Created', alignment: 'right' },
|
|
92
|
+
]
|
|
93
|
+
);
|
|
94
|
+
|
|
95
|
+
return skills;
|
|
96
|
+
},
|
|
97
|
+
});
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { CoderClient } from '@agentuity/core/coder';
|
|
3
|
+
import { ValidationOutputError } from '@agentuity/core';
|
|
4
|
+
import { createSubcommand } from '../../../types';
|
|
5
|
+
import * as tui from '../../../tui';
|
|
6
|
+
import { getCommand } from '../../../command-prefix';
|
|
7
|
+
import { ErrorCode } from '../../../errors';
|
|
8
|
+
|
|
9
|
+
export const saveSkillSubcommand = createSubcommand({
|
|
10
|
+
name: 'save',
|
|
11
|
+
aliases: ['add', 'upsert'],
|
|
12
|
+
description: 'Save a skill to your library',
|
|
13
|
+
tags: ['mutating', 'requires-auth'],
|
|
14
|
+
requires: { auth: true, org: true },
|
|
15
|
+
examples: [
|
|
16
|
+
{
|
|
17
|
+
command: getCommand(
|
|
18
|
+
'coder skill save --repo my-org/my-repo --skill-id sk_abc123 --name "My Skill"'
|
|
19
|
+
),
|
|
20
|
+
description: 'Save a skill to your library',
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
command: getCommand(
|
|
24
|
+
'coder skill save --repo my-org/my-repo --skill-id sk_abc123 --name "My Skill" --description "Useful skill" --skill-url https://example.com --json'
|
|
25
|
+
),
|
|
26
|
+
description: 'Save a skill with all options and return JSON',
|
|
27
|
+
},
|
|
28
|
+
],
|
|
29
|
+
schema: {
|
|
30
|
+
options: z.object({
|
|
31
|
+
url: z.string().optional().describe('Coder API URL override'),
|
|
32
|
+
repo: z.string().describe('Repository identifier for the skill'),
|
|
33
|
+
skillId: z.string().describe('Skill identifier'),
|
|
34
|
+
name: z.string().describe('Skill name'),
|
|
35
|
+
description: z.string().optional().describe('Skill description'),
|
|
36
|
+
skillUrl: z.string().optional().describe('Skill URL'),
|
|
37
|
+
source: z.string().optional().describe('Skill source (default: registry)'),
|
|
38
|
+
content: z.string().optional().describe('Skill content'),
|
|
39
|
+
}),
|
|
40
|
+
},
|
|
41
|
+
async handler(ctx) {
|
|
42
|
+
const { opts, options } = ctx;
|
|
43
|
+
const client = new CoderClient({
|
|
44
|
+
apiKey: ctx.auth.apiKey,
|
|
45
|
+
url: opts?.url,
|
|
46
|
+
orgId: ctx.orgId,
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
try {
|
|
50
|
+
const saved = await client.saveSkill({
|
|
51
|
+
repo: opts.repo,
|
|
52
|
+
skillId: opts.skillId,
|
|
53
|
+
name: opts.name,
|
|
54
|
+
...(opts?.description !== undefined && { description: opts.description }),
|
|
55
|
+
...(opts?.skillUrl !== undefined && { url: opts.skillUrl }),
|
|
56
|
+
...(opts?.source !== undefined && { source: opts.source }),
|
|
57
|
+
...(opts?.content !== undefined && { content: opts.content }),
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
if (options.json) {
|
|
61
|
+
return saved;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
tui.success(`Skill ${saved.id} saved.`);
|
|
65
|
+
tui.newline();
|
|
66
|
+
tui.output(` Name: ${tui.bold(saved.name)}`);
|
|
67
|
+
tui.output(` Repo: ${saved.repo}`);
|
|
68
|
+
tui.output(` Source: ${saved.source}`);
|
|
69
|
+
if (saved.description) {
|
|
70
|
+
tui.output(` Desc: ${saved.description}`);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return saved;
|
|
74
|
+
} catch (err) {
|
|
75
|
+
if (err instanceof ValidationOutputError) {
|
|
76
|
+
ctx.logger.trace('Validation response URL: %s', err.url ?? 'unknown');
|
|
77
|
+
ctx.logger.trace('Validation issues: %s', JSON.stringify(err.issues, null, 2));
|
|
78
|
+
tui.fatal(`Failed to save skill: ${err.message}`, ErrorCode.VALIDATION_FAILED);
|
|
79
|
+
}
|
|
80
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
81
|
+
tui.fatal(`Failed to save skill: ${msg}`, ErrorCode.NETWORK_ERROR);
|
|
82
|
+
}
|
|
83
|
+
},
|
|
84
|
+
});
|