Skip to content

Commit a096aaa

Browse files
committed
feat: improve sync UX and bundle schema
1 parent 0514a9b commit a096aaa

File tree

19 files changed

+461
-122
lines changed

19 files changed

+461
-122
lines changed

packages/clawdhub/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
},
2121
"dependencies": {
2222
"@clack/prompts": "^0.11.0",
23-
"clawdhub-schema": "^0.0.2",
23+
"arktype": "^2.1.29",
2424
"commander": "^14.0.2",
2525
"fflate": "^0.8.2",
2626
"ignore": "^7.0.5",

packages/clawdhub/src/cli.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,10 +191,14 @@ program
191191
.option('--bump <type>', 'Version bump for updates (patch|minor|major)', 'patch')
192192
.option('--changelog <text>', 'Changelog to use for updates (non-interactive)')
193193
.option('--tags <tags>', 'Comma-separated tags', 'latest')
194+
.option('--concurrency <n>', 'Concurrent registry checks (default: 8)', '8')
194195
.action(async (options) => {
195196
const opts = resolveGlobalOpts()
196197
const bump = String(options.bump ?? 'patch') as 'patch' | 'minor' | 'major'
197198
if (!['patch', 'minor', 'major'].includes(bump)) fail('--bump must be patch|minor|major')
199+
const concurrencyRaw = Number(options.concurrency ?? 8)
200+
const concurrency = Number.isFinite(concurrencyRaw) ? Math.round(concurrencyRaw) : 8
201+
if (concurrency < 1 || concurrency > 32) fail('--concurrency must be between 1 and 32')
198202
await cmdSync(
199203
opts,
200204
{
@@ -204,6 +208,7 @@ program
204208
bump,
205209
changelog: options.changelog,
206210
tags: options.tags,
211+
concurrency,
207212
},
208213
isInputAllowed(),
209214
)

packages/clawdhub/src/cli/commands/auth.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
import { ApiCliWhoamiResponseSchema, ApiRoutes } from 'clawdhub-schema'
21
import { buildCliAuthUrl, startLoopbackAuthServer } from '../../browserAuth.js'
32
import { readGlobalConfig, writeGlobalConfig } from '../../config.js'
43
import { discoverRegistryFromSite } from '../../discovery.js'
54
import { apiRequest } from '../../http.js'
5+
import { ApiCliWhoamiResponseSchema, ApiRoutes } from '../../schema/index.js'
66
import { getRegistry } from '../registry.js'
77
import type { GlobalOpts } from '../types.js'
88
import { createSpinner, fail, formatError, openInBrowser, promptHidden } from '../ui.js'

packages/clawdhub/src/cli/commands/delete.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
import { ApiCliSkillDeleteResponseSchema, ApiRoutes, parseArk } from 'clawdhub-schema'
21
import { readGlobalConfig } from '../../config.js'
32
import { apiRequest } from '../../http.js'
3+
import { ApiCliSkillDeleteResponseSchema, ApiRoutes, parseArk } from '../../schema/index.js'
44
import { getRegistry } from '../registry.js'
55
import type { GlobalOpts } from '../types.js'
66
import { createSpinner, fail, formatError, isInteractive, promptConfirm } from '../ui.js'

packages/clawdhub/src/cli/commands/publish.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
11
import { stat } from 'node:fs/promises'
22
import { basename, resolve } from 'node:path'
3+
import semver from 'semver'
4+
import { readGlobalConfig } from '../../config.js'
5+
import { apiRequest } from '../../http.js'
36
import {
47
ApiCliPublishResponseSchema,
58
ApiCliUploadUrlResponseSchema,
69
ApiRoutes,
710
ApiUploadFileResponseSchema,
811
CliPublishRequestSchema,
912
parseArk,
10-
} from 'clawdhub-schema'
11-
import semver from 'semver'
12-
import { readGlobalConfig } from '../../config.js'
13-
import { apiRequest } from '../../http.js'
13+
} from '../../schema/index.js'
1414
import { listTextFiles, sha256Hex } from '../../skills.js'
1515
import { getRegistry } from '../registry.js'
1616
import { sanitizeSlug, titleCase } from '../slug.js'

packages/clawdhub/src/cli/commands/skills.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
import { mkdir, rm, stat } from 'node:fs/promises'
22
import { join } from 'node:path'
3+
import semver from 'semver'
4+
import { apiRequest, downloadZip } from '../../http.js'
35
import {
46
ApiRoutes,
57
ApiSearchResponseSchema,
68
ApiSkillMetaResponseSchema,
79
ApiSkillResolveResponseSchema,
8-
} from 'clawdhub-schema'
9-
import semver from 'semver'
10-
import { apiRequest, downloadZip } from '../../http.js'
10+
} from '../../schema/index.js'
1111
import {
1212
extractZipToDir,
1313
hashSkillFiles,

packages/clawdhub/src/cli/commands/sync.test.ts

Lines changed: 9 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -95,15 +95,12 @@ describe('cmdSync', () => {
9595
interactive = false
9696
mockApiRequest.mockImplementation(async (_registry: string, args: { path: string }) => {
9797
if (args.path === '/api/cli/whoami') return { user: { handle: 'steipete' } }
98-
if (args.path.startsWith('/api/skill?slug=')) {
99-
const slug = new URL(`https://x.test${args.path}`).searchParams.get('slug')
100-
if (slug === 'new-skill') return { latestVersion: undefined, skill: null }
101-
if (slug === 'synced-skill') return { latestVersion: { version: '1.2.3' }, skill: {} }
102-
if (slug === 'update-skill') return { latestVersion: { version: '1.0.0' }, skill: {} }
103-
}
10498
if (args.path.startsWith('/api/skill/resolve?')) {
10599
const u = new URL(`https://x.test${args.path}`)
106100
const slug = u.searchParams.get('slug')
101+
if (slug === 'new-skill') {
102+
throw new Error('Skill not found')
103+
}
107104
if (slug === 'synced-skill') {
108105
return { match: { version: '1.2.3' }, latestVersion: { version: '1.2.3' } }
109106
}
@@ -133,15 +130,12 @@ describe('cmdSync', () => {
133130
})
134131
mockApiRequest.mockImplementation(async (_registry: string, args: { path: string }) => {
135132
if (args.path === '/api/cli/whoami') return { user: { handle: 'steipete' } }
136-
if (args.path.startsWith('/api/skill?slug=')) {
137-
const slug = new URL(`https://x.test${args.path}`).searchParams.get('slug')
138-
if (slug === 'new-skill') return { latestVersion: undefined, skill: null }
139-
if (slug === 'synced-skill') return { latestVersion: { version: '1.2.3' }, skill: {} }
140-
if (slug === 'update-skill') return { latestVersion: { version: '1.0.0' }, skill: {} }
141-
}
142133
if (args.path.startsWith('/api/skill/resolve?')) {
143134
const u = new URL(`https://x.test${args.path}`)
144135
const slug = u.searchParams.get('slug')
136+
if (slug === 'new-skill') {
137+
throw new Error('Skill not found')
138+
}
145139
if (slug === 'synced-skill') {
146140
return { match: { version: '1.2.3' }, latestVersion: { version: '1.2.3' } }
147141
}
@@ -171,9 +165,6 @@ describe('cmdSync', () => {
171165
interactive = false
172166
mockApiRequest.mockImplementation(async (_registry: string, args: { path: string }) => {
173167
if (args.path === '/api/cli/whoami') return { user: { handle: 'steipete' } }
174-
if (args.path.startsWith('/api/skill?slug=')) {
175-
return { latestVersion: { version: '1.0.0' }, skill: {} }
176-
}
177168
if (args.path.startsWith('/api/skill/resolve?')) {
178169
return { match: { version: '1.0.0' }, latestVersion: { version: '1.0.0' } }
179170
}
@@ -204,9 +195,6 @@ describe('cmdSync', () => {
204195

205196
mockApiRequest.mockImplementation(async (_registry: string, args: { path: string }) => {
206197
if (args.path === '/api/cli/whoami') return { user: { handle: 'steipete' } }
207-
if (args.path.startsWith('/api/skill?slug=')) {
208-
return { latestVersion: undefined, skill: null }
209-
}
210198
if (args.path.startsWith('/api/skill/resolve?')) {
211199
return { match: null, latestVersion: null }
212200
}
@@ -224,15 +212,12 @@ describe('cmdSync', () => {
224212
interactive = true
225213
mockApiRequest.mockImplementation(async (_registry: string, args: { path: string }) => {
226214
if (args.path === '/api/cli/whoami') return { user: { handle: 'steipete' } }
227-
if (args.path.startsWith('/api/skill?slug=')) {
228-
const slug = new URL(`https://x.test${args.path}`).searchParams.get('slug')
229-
if (slug === 'new-skill') return { latestVersion: undefined, skill: null }
230-
if (slug === 'synced-skill') return { latestVersion: { version: '1.2.3' }, skill: {} }
231-
if (slug === 'update-skill') return { latestVersion: { version: '1.0.0' }, skill: {} }
232-
}
233215
if (args.path.startsWith('/api/skill/resolve?')) {
234216
const u = new URL(`https://x.test${args.path}`)
235217
const slug = u.searchParams.get('slug')
218+
if (slug === 'new-skill') {
219+
throw new Error('Skill not found')
220+
}
236221
if (slug === 'synced-skill') {
237222
return { match: { version: '1.2.3' }, latestVersion: { version: '1.2.3' } }
238223
}

0 commit comments

Comments
 (0)