pilothub 0.0.1 → 0.0.2
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/LICENSE +1 -0
- package/README.md +36 -129
- package/dist/browserAuth.d.ts +20 -0
- package/dist/browserAuth.js +156 -0
- package/dist/browserAuth.js.map +1 -0
- package/dist/browserAuth.test.d.ts +1 -0
- package/dist/browserAuth.test.js +83 -0
- package/dist/browserAuth.test.js.map +1 -0
- package/dist/cli/buildInfo.d.ts +3 -0
- package/dist/cli/buildInfo.js +103 -0
- package/dist/cli/buildInfo.js.map +1 -0
- package/dist/cli/commands/auth.d.ts +9 -0
- package/dist/cli/commands/auth.js +75 -0
- package/dist/cli/commands/auth.js.map +1 -0
- package/dist/cli/commands/delete.d.ts +11 -0
- package/dist/cli/commands/delete.js +67 -0
- package/dist/cli/commands/delete.js.map +1 -0
- package/dist/cli/commands/delete.test.d.ts +1 -0
- package/dist/cli/commands/delete.test.js +52 -0
- package/dist/cli/commands/delete.test.js.map +1 -0
- package/dist/cli/commands/publish.d.ts +9 -0
- package/dist/cli/commands/publish.js +87 -0
- package/dist/cli/commands/publish.js.map +1 -0
- package/dist/cli/commands/publish.test.d.ts +1 -0
- package/dist/cli/commands/publish.test.js +104 -0
- package/dist/cli/commands/publish.test.js.map +1 -0
- package/dist/cli/commands/skills.d.ts +23 -0
- package/dist/cli/commands/skills.js +298 -0
- package/dist/cli/commands/skills.js.map +1 -0
- package/dist/cli/commands/skills.test.d.ts +1 -0
- package/dist/cli/commands/skills.test.js +156 -0
- package/dist/cli/commands/skills.test.js.map +1 -0
- package/dist/cli/commands/star.d.ts +8 -0
- package/dist/cli/commands/star.js +38 -0
- package/dist/cli/commands/star.js.map +1 -0
- package/dist/cli/commands/sync.d.ts +3 -0
- package/dist/cli/commands/sync.js +160 -0
- package/dist/cli/commands/sync.js.map +1 -0
- package/dist/cli/commands/sync.test.d.ts +1 -0
- package/dist/cli/commands/sync.test.js +277 -0
- package/dist/cli/commands/sync.test.js.map +1 -0
- package/dist/cli/commands/syncHelpers.d.ts +76 -0
- package/dist/cli/commands/syncHelpers.js +349 -0
- package/dist/cli/commands/syncHelpers.js.map +1 -0
- package/dist/cli/commands/syncHelpers.test.d.ts +1 -0
- package/dist/cli/commands/syncHelpers.test.js +22 -0
- package/dist/cli/commands/syncHelpers.test.js.map +1 -0
- package/dist/cli/commands/syncTypes.d.ts +24 -0
- package/dist/cli/commands/syncTypes.js +2 -0
- package/dist/cli/commands/syncTypes.js.map +1 -0
- package/dist/cli/commands/unstar.d.ts +8 -0
- package/dist/cli/commands/unstar.js +38 -0
- package/dist/cli/commands/unstar.js.map +1 -0
- package/dist/cli/helpStyle.d.ts +13 -0
- package/dist/cli/helpStyle.js +38 -0
- package/dist/cli/helpStyle.js.map +1 -0
- package/dist/cli/pilotbotConfig.d.ts +6 -0
- package/dist/cli/pilotbotConfig.js +110 -0
- package/dist/cli/pilotbotConfig.js.map +1 -0
- package/dist/cli/pilotbotConfig.test.d.ts +1 -0
- package/dist/cli/pilotbotConfig.test.js +133 -0
- package/dist/cli/pilotbotConfig.test.js.map +1 -0
- package/dist/cli/registry.d.ts +7 -0
- package/dist/cli/registry.js +42 -0
- package/dist/cli/registry.js.map +1 -0
- package/dist/cli/registry.test.d.ts +1 -0
- package/dist/cli/registry.test.js +48 -0
- package/dist/cli/registry.test.js.map +1 -0
- package/dist/cli/scanSkills.d.ts +7 -0
- package/dist/cli/scanSkills.js +75 -0
- package/dist/cli/scanSkills.js.map +1 -0
- package/dist/cli/scanSkills.test.d.ts +1 -0
- package/dist/cli/scanSkills.test.js +60 -0
- package/dist/cli/scanSkills.test.js.map +1 -0
- package/dist/cli/slug.d.ts +2 -0
- package/dist/cli/slug.js +16 -0
- package/dist/cli/slug.js.map +1 -0
- package/dist/cli/types.d.ts +15 -0
- package/dist/cli/types.js +2 -0
- package/dist/cli/types.js.map +1 -0
- package/dist/cli/ui.d.ts +7 -0
- package/dist/cli/ui.js +72 -0
- package/dist/cli/ui.js.map +1 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +268 -0
- package/dist/cli.js.map +1 -0
- package/dist/config.d.ts +4 -0
- package/dist/config.js +38 -0
- package/dist/config.js.map +1 -0
- package/dist/discovery.d.ts +5 -0
- package/dist/discovery.js +21 -0
- package/dist/discovery.js.map +1 -0
- package/dist/discovery.test.d.ts +1 -0
- package/dist/discovery.test.js +46 -0
- package/dist/discovery.test.js.map +1 -0
- package/dist/http.d.ts +32 -0
- package/dist/http.js +261 -0
- package/dist/http.js.map +1 -0
- package/dist/http.test.d.ts +1 -0
- package/dist/http.test.js +135 -0
- package/dist/http.test.js.map +1 -0
- package/dist/schema/ark.js.map +1 -0
- package/dist/schema/index.js.map +1 -0
- package/dist/schema/routes.js.map +1 -0
- package/{packages/schema/dist → dist/schema}/schemas.d.ts +0 -39
- package/{packages/schema/dist → dist/schema}/schemas.js +0 -22
- package/dist/schema/schemas.js.map +1 -0
- package/dist/schema/textFiles.js.map +1 -0
- package/dist/schema/textFiles.test.d.ts +1 -0
- package/dist/schema/textFiles.test.js +20 -0
- package/dist/schema/textFiles.test.js.map +1 -0
- package/dist/skills.d.ts +43 -0
- package/dist/skills.js +163 -0
- package/dist/skills.js.map +1 -0
- package/dist/skills.test.d.ts +1 -0
- package/dist/skills.test.js +144 -0
- package/dist/skills.test.js.map +1 -0
- package/dist/types.d.ts +7 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/package.json +27 -70
- package/.env.local.example +0 -19
- package/.github/workflows/ci.yml +0 -40
- package/.oxlintrc.json +0 -3
- package/AGENTS.md +0 -45
- package/CHANGELOG.md +0 -138
- package/DEPRECATIONS.md +0 -7
- package/biome.json +0 -41
- package/convex/_generated/api.d.ts +0 -153
- package/convex/_generated/api.js +0 -23
- package/convex/_generated/dataModel.d.ts +0 -60
- package/convex/_generated/server.d.ts +0 -143
- package/convex/_generated/server.js +0 -93
- package/convex/auth.config.ts +0 -8
- package/convex/auth.ts +0 -19
- package/convex/comments.ts +0 -88
- package/convex/crons.ts +0 -34
- package/convex/devSeed.ts +0 -459
- package/convex/devSeedExtra.ts +0 -541
- package/convex/downloads.ts +0 -78
- package/convex/githubBackups.ts +0 -170
- package/convex/githubBackupsNode.ts +0 -183
- package/convex/githubImport.ts +0 -317
- package/convex/githubSoulBackups.ts +0 -170
- package/convex/githubSoulBackupsNode.ts +0 -186
- package/convex/http.ts +0 -194
- package/convex/httpApi.handlers.test.ts +0 -488
- package/convex/httpApi.test.ts +0 -70
- package/convex/httpApi.ts +0 -305
- package/convex/httpApiV1.handlers.test.ts +0 -584
- package/convex/httpApiV1.ts +0 -1172
- package/convex/leaderboards.ts +0 -39
- package/convex/lib/access.ts +0 -36
- package/convex/lib/apiTokenAuth.ts +0 -36
- package/convex/lib/badges.ts +0 -50
- package/convex/lib/changelog.test.ts +0 -34
- package/convex/lib/changelog.ts +0 -278
- package/convex/lib/embeddings.ts +0 -38
- package/convex/lib/githubBackup.ts +0 -443
- package/convex/lib/githubImport.test.ts +0 -247
- package/convex/lib/githubImport.ts +0 -425
- package/convex/lib/githubSoulBackup.ts +0 -443
- package/convex/lib/leaderboards.ts +0 -103
- package/convex/lib/moderation.ts +0 -42
- package/convex/lib/public.ts +0 -89
- package/convex/lib/searchText.test.ts +0 -46
- package/convex/lib/searchText.ts +0 -27
- package/convex/lib/skillBackfill.test.ts +0 -34
- package/convex/lib/skillBackfill.ts +0 -67
- package/convex/lib/skillPublish.test.ts +0 -28
- package/convex/lib/skillPublish.ts +0 -284
- package/convex/lib/skillStats.ts +0 -80
- package/convex/lib/skills.test.ts +0 -197
- package/convex/lib/skills.ts +0 -273
- package/convex/lib/soulChangelog.ts +0 -273
- package/convex/lib/soulPublish.ts +0 -236
- package/convex/lib/tokens.test.ts +0 -33
- package/convex/lib/tokens.ts +0 -51
- package/convex/lib/webhooks.test.ts +0 -91
- package/convex/lib/webhooks.ts +0 -112
- package/convex/maintenance.test.ts +0 -270
- package/convex/maintenance.ts +0 -840
- package/convex/rateLimits.ts +0 -50
- package/convex/schema.ts +0 -472
- package/convex/search.test.ts +0 -12
- package/convex/search.ts +0 -254
- package/convex/seed.test.ts +0 -37
- package/convex/seed.ts +0 -254
- package/convex/seedSouls.ts +0 -111
- package/convex/skillStatEvents.ts +0 -568
- package/convex/skills.ts +0 -1606
- package/convex/soulComments.ts +0 -88
- package/convex/soulDownloads.ts +0 -14
- package/convex/soulStars.ts +0 -71
- package/convex/souls.ts +0 -570
- package/convex/stars.ts +0 -108
- package/convex/statsMaintenance.ts +0 -205
- package/convex/telemetry.ts +0 -434
- package/convex/tokens.ts +0 -88
- package/convex/tsconfig.json +0 -7
- package/convex/uploads.ts +0 -20
- package/convex/users.ts +0 -122
- package/convex/webhooks.ts +0 -50
- package/convex.json +0 -3
- package/docs/README.md +0 -32
- package/docs/api.md +0 -51
- package/docs/architecture.md +0 -61
- package/docs/auth.md +0 -54
- package/docs/cli.md +0 -117
- package/docs/deploy.md +0 -78
- package/docs/diffing.md +0 -84
- package/docs/github-import.md +0 -171
- package/docs/http-api.md +0 -187
- package/docs/manual-testing.md +0 -64
- package/docs/mintlify.md +0 -43
- package/docs/quickstart.md +0 -120
- package/docs/skill-format.md +0 -58
- package/docs/soul-format.md +0 -37
- package/docs/spec.md +0 -177
- package/docs/telemetry.md +0 -91
- package/docs/troubleshooting.md +0 -49
- package/docs/webhook.md +0 -51
- package/e2e/menu-smoke.pw.test.ts +0 -49
- package/e2e/pilothub.e2e.test.ts +0 -494
- package/e2e/search-exact.pw.test.ts +0 -97
- package/packages/pilothub/LICENSE +0 -22
- package/packages/pilothub/README.md +0 -57
- package/packages/pilothub/package.json +0 -41
- package/packages/pilothub/src/browserAuth.test.ts +0 -96
- package/packages/pilothub/src/browserAuth.ts +0 -174
- package/packages/pilothub/src/cli/buildInfo.ts +0 -94
- package/packages/pilothub/src/cli/commands/auth.ts +0 -97
- package/packages/pilothub/src/cli/commands/delete.test.ts +0 -73
- package/packages/pilothub/src/cli/commands/delete.ts +0 -83
- package/packages/pilothub/src/cli/commands/publish.test.ts +0 -122
- package/packages/pilothub/src/cli/commands/publish.ts +0 -108
- package/packages/pilothub/src/cli/commands/skills.test.ts +0 -191
- package/packages/pilothub/src/cli/commands/skills.ts +0 -380
- package/packages/pilothub/src/cli/commands/star.ts +0 -46
- package/packages/pilothub/src/cli/commands/sync.test.ts +0 -310
- package/packages/pilothub/src/cli/commands/sync.ts +0 -200
- package/packages/pilothub/src/cli/commands/syncHelpers.test.ts +0 -26
- package/packages/pilothub/src/cli/commands/syncHelpers.ts +0 -427
- package/packages/pilothub/src/cli/commands/syncTypes.ts +0 -27
- package/packages/pilothub/src/cli/commands/unstar.ts +0 -48
- package/packages/pilothub/src/cli/helpStyle.ts +0 -45
- package/packages/pilothub/src/cli/pilotbotConfig.test.ts +0 -159
- package/packages/pilothub/src/cli/pilotbotConfig.ts +0 -147
- package/packages/pilothub/src/cli/registry.test.ts +0 -63
- package/packages/pilothub/src/cli/registry.ts +0 -43
- package/packages/pilothub/src/cli/scanSkills.test.ts +0 -64
- package/packages/pilothub/src/cli/scanSkills.ts +0 -84
- package/packages/pilothub/src/cli/slug.ts +0 -16
- package/packages/pilothub/src/cli/types.ts +0 -12
- package/packages/pilothub/src/cli/ui.ts +0 -75
- package/packages/pilothub/src/cli.ts +0 -311
- package/packages/pilothub/src/config.ts +0 -36
- package/packages/pilothub/src/discovery.test.ts +0 -75
- package/packages/pilothub/src/discovery.ts +0 -19
- package/packages/pilothub/src/http.test.ts +0 -156
- package/packages/pilothub/src/http.ts +0 -301
- package/packages/pilothub/src/schema/ark.ts +0 -29
- package/packages/pilothub/src/schema/index.ts +0 -5
- package/packages/pilothub/src/schema/routes.ts +0 -22
- package/packages/pilothub/src/schema/schemas.ts +0 -260
- package/packages/pilothub/src/schema/textFiles.test.ts +0 -23
- package/packages/pilothub/src/schema/textFiles.ts +0 -66
- package/packages/pilothub/src/skills.test.ts +0 -191
- package/packages/pilothub/src/skills.ts +0 -172
- package/packages/pilothub/src/types.ts +0 -10
- package/packages/pilothub/tsconfig.json +0 -14
- package/packages/schema/README.md +0 -3
- package/packages/schema/dist/ark.js.map +0 -1
- package/packages/schema/dist/index.js.map +0 -1
- package/packages/schema/dist/routes.js.map +0 -1
- package/packages/schema/dist/schemas.js.map +0 -1
- package/packages/schema/dist/textFiles.js.map +0 -1
- package/packages/schema/package.json +0 -26
- package/packages/schema/src/ark.ts +0 -29
- package/packages/schema/src/index.ts +0 -5
- package/packages/schema/src/routes.ts +0 -22
- package/packages/schema/src/schemas.test.ts +0 -123
- package/packages/schema/src/schemas.ts +0 -287
- package/packages/schema/src/textFiles.test.ts +0 -23
- package/packages/schema/src/textFiles.ts +0 -66
- package/packages/schema/tsconfig.json +0 -15
- package/pilothub +0 -46
- package/playwright.config.ts +0 -33
- package/public/.well-known/pilothub.json +0 -6
- package/public/api/v1/openapi.json +0 -379
- package/public/favicon.ico +0 -0
- package/public/logo192.png +0 -0
- package/public/logo512.png +0 -0
- package/public/manifest.json +0 -25
- package/public/og.png +0 -0
- package/public/og.svg +0 -98
- package/public/pilot-logo.png +0 -0
- package/public/pilot-mark.png +0 -0
- package/public/robots.txt +0 -3
- package/public/tanstack-circle-logo.png +0 -0
- package/public/tanstack-word-logo-white.svg +0 -1
- package/scripts/check-peer-deps.ts +0 -56
- package/scripts/docs-list.ts +0 -148
- package/scripts/run-playwright-local.sh +0 -14
- package/server/og/fetchSkillOgMeta.ts +0 -27
- package/server/og/fetchSoulOgMeta.ts +0 -27
- package/server/og/ogAssets.ts +0 -80
- package/server/og/skillOgSvg.test.ts +0 -59
- package/server/og/skillOgSvg.ts +0 -258
- package/server/og/soulOgSvg.ts +0 -209
- package/server/routes/og/skill.png.ts +0 -103
- package/server/routes/og/soul.png.ts +0 -111
- package/src/__tests__/skill-detail-page.test.tsx +0 -86
- package/src/__tests__/skills-index.test.tsx +0 -145
- package/src/__tests__/upload.route.test.tsx +0 -228
- package/src/components/AppProviders.tsx +0 -19
- package/src/components/ClientOnly.tsx +0 -18
- package/src/components/Footer.tsx +0 -29
- package/src/components/Header.tsx +0 -295
- package/src/components/InstallSwitcher.tsx +0 -53
- package/src/components/SkillCard.tsx +0 -36
- package/src/components/SkillDetailPage.tsx +0 -817
- package/src/components/SkillDiffCard.tsx +0 -485
- package/src/components/SoulCard.tsx +0 -19
- package/src/components/SoulDetailPage.tsx +0 -263
- package/src/components/UserBootstrap.tsx +0 -18
- package/src/components/ui/dropdown-menu.tsx +0 -67
- package/src/components/ui/toggle-group.tsx +0 -35
- package/src/convex/client.ts +0 -3
- package/src/lib/badges.ts +0 -29
- package/src/lib/diffing.test.ts +0 -163
- package/src/lib/diffing.ts +0 -106
- package/src/lib/gravatar.test.ts +0 -9
- package/src/lib/gravatar.ts +0 -158
- package/src/lib/og.test.ts +0 -142
- package/src/lib/og.ts +0 -156
- package/src/lib/publicUser.ts +0 -39
- package/src/lib/roles.ts +0 -19
- package/src/lib/site.test.ts +0 -130
- package/src/lib/site.ts +0 -84
- package/src/lib/theme-transition.test.ts +0 -134
- package/src/lib/theme-transition.ts +0 -134
- package/src/lib/theme.test.tsx +0 -88
- package/src/lib/theme.ts +0 -43
- package/src/lib/uploadFiles.jsdom.test.ts +0 -33
- package/src/lib/uploadFiles.test.ts +0 -123
- package/src/lib/uploadFiles.ts +0 -245
- package/src/lib/uploadUtils.test.ts +0 -78
- package/src/lib/uploadUtils.ts +0 -93
- package/src/lib/useAuthStatus.ts +0 -12
- package/src/lib/utils.test.ts +0 -9
- package/src/lib/utils.ts +0 -6
- package/src/logo.svg +0 -12
- package/src/routeTree.gen.ts +0 -345
- package/src/router.tsx +0 -17
- package/src/routes/$owner/$slug.tsx +0 -55
- package/src/routes/__root.tsx +0 -136
- package/src/routes/admin.tsx +0 -11
- package/src/routes/cli/auth.tsx +0 -168
- package/src/routes/dashboard.tsx +0 -97
- package/src/routes/import.tsx +0 -415
- package/src/routes/index.tsx +0 -252
- package/src/routes/management.tsx +0 -529
- package/src/routes/settings.tsx +0 -203
- package/src/routes/skills/index.tsx +0 -422
- package/src/routes/souls/$slug.tsx +0 -55
- package/src/routes/souls/index.tsx +0 -243
- package/src/routes/stars.tsx +0 -68
- package/src/routes/u/$handle.tsx +0 -307
- package/src/routes/upload/utils.ts +0 -81
- package/src/routes/upload.tsx +0 -499
- package/src/styles.css +0 -2718
- package/tsconfig.json +0 -24
- package/tsconfig.oxlint.json +0 -16
- package/vercel.json +0 -8
- package/vite.config.ts +0 -48
- package/vitest.config.ts +0 -47
- package/vitest.e2e.config.ts +0 -11
- package/vitest.setup.ts +0 -1
- /package/{packages/pilothub/bin → bin}/pilothub.js +0 -0
- /package/{packages/schema/dist → dist/schema}/ark.d.ts +0 -0
- /package/{packages/schema/dist → dist/schema}/ark.js +0 -0
- /package/{packages/schema/dist → dist/schema}/index.d.ts +0 -0
- /package/{packages/schema/dist → dist/schema}/index.js +0 -0
- /package/{packages/schema/dist → dist/schema}/routes.d.ts +0 -0
- /package/{packages/schema/dist → dist/schema}/routes.js +0 -0
- /package/{packages/schema/dist → dist/schema}/textFiles.d.ts +0 -0
- /package/{packages/schema/dist → dist/schema}/textFiles.js +0 -0
|
@@ -1,427 +0,0 @@
|
|
|
1
|
-
import { createHash } from 'node:crypto'
|
|
2
|
-
import { realpath } from 'node:fs/promises'
|
|
3
|
-
import { homedir } from 'node:os'
|
|
4
|
-
import { resolve } from 'node:path'
|
|
5
|
-
import { isCancel, multiselect } from '@clack/prompts'
|
|
6
|
-
import semver from 'semver'
|
|
7
|
-
import { apiRequest, downloadZip } from '../../http.js'
|
|
8
|
-
import {
|
|
9
|
-
ApiCliTelemetrySyncResponseSchema,
|
|
10
|
-
ApiRoutes,
|
|
11
|
-
ApiV1SkillResolveResponseSchema,
|
|
12
|
-
ApiV1SkillResponseSchema,
|
|
13
|
-
ApiV1WhoamiResponseSchema,
|
|
14
|
-
LegacyApiRoutes,
|
|
15
|
-
} from '../../schema/index.js'
|
|
16
|
-
import { hashSkillZip } from '../../skills.js'
|
|
17
|
-
import { getRegistry } from '../registry.js'
|
|
18
|
-
import { findSkillFolders, type SkillFolder } from '../scanSkills.js'
|
|
19
|
-
import type { GlobalOpts } from '../types.js'
|
|
20
|
-
import { fail, formatError } from '../ui.js'
|
|
21
|
-
import type { Candidate, LocalSkill } from './syncTypes.js'
|
|
22
|
-
|
|
23
|
-
export async function reportTelemetryIfEnabled(params: {
|
|
24
|
-
token: string
|
|
25
|
-
registry: string
|
|
26
|
-
scan: { roots: string[]; skillsByRoot: Record<string, SkillFolder[]> }
|
|
27
|
-
candidates: Candidate[]
|
|
28
|
-
}) {
|
|
29
|
-
if (isTelemetryDisabled()) return
|
|
30
|
-
const versionBySlug = new Map<string, string | null>()
|
|
31
|
-
for (const candidate of params.candidates) {
|
|
32
|
-
versionBySlug.set(candidate.slug, candidate.matchVersion ?? null)
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
const roots = params.scan.roots.map((root) => ({
|
|
36
|
-
rootId: rootTelemetryId(root),
|
|
37
|
-
label: formatRootLabel(root),
|
|
38
|
-
skills: (params.scan.skillsByRoot[root] ?? []).map((skill) => ({
|
|
39
|
-
slug: skill.slug,
|
|
40
|
-
version: versionBySlug.get(skill.slug) ?? null,
|
|
41
|
-
})),
|
|
42
|
-
}))
|
|
43
|
-
|
|
44
|
-
try {
|
|
45
|
-
await apiRequest(
|
|
46
|
-
params.registry,
|
|
47
|
-
{
|
|
48
|
-
method: 'POST',
|
|
49
|
-
path: LegacyApiRoutes.cliTelemetrySync,
|
|
50
|
-
token: params.token,
|
|
51
|
-
body: { roots },
|
|
52
|
-
},
|
|
53
|
-
ApiCliTelemetrySyncResponseSchema,
|
|
54
|
-
)
|
|
55
|
-
} catch {
|
|
56
|
-
// ignore telemetry failures
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
function isTelemetryDisabled() {
|
|
61
|
-
const raw = process.env.PILOTHUB_DISABLE_TELEMETRY
|
|
62
|
-
if (!raw) return false
|
|
63
|
-
return ['1', 'true', 'yes', 'on'].includes(raw.trim().toLowerCase())
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
export function buildScanRoots(opts: GlobalOpts, extraRoots: string[] | undefined) {
|
|
67
|
-
const roots = [opts.workdir, opts.dir, ...(extraRoots ?? [])]
|
|
68
|
-
return Array.from(new Set(roots.map((root) => resolve(root))))
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
export function normalizeConcurrency(value: number | undefined) {
|
|
72
|
-
const raw = typeof value === 'number' ? value : 4
|
|
73
|
-
const rounded = Number.isFinite(raw) ? Math.round(raw) : 4
|
|
74
|
-
return Math.min(32, Math.max(1, rounded))
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
export async function mapWithConcurrency<T, R>(
|
|
78
|
-
items: T[],
|
|
79
|
-
limit: number,
|
|
80
|
-
fn: (item: T) => Promise<R>,
|
|
81
|
-
) {
|
|
82
|
-
const results = Array.from({ length: items.length }) as R[]
|
|
83
|
-
let nextIndex = 0
|
|
84
|
-
const workerCount = Math.min(Math.max(1, limit), items.length || 1)
|
|
85
|
-
|
|
86
|
-
async function worker() {
|
|
87
|
-
while (true) {
|
|
88
|
-
const index = nextIndex
|
|
89
|
-
nextIndex += 1
|
|
90
|
-
if (index >= items.length) return
|
|
91
|
-
results[index] = await fn(items[index] as T)
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
await Promise.all(Array.from({ length: workerCount }, () => worker()))
|
|
96
|
-
return results
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
export async function checkRegistrySyncState(
|
|
100
|
-
registry: string,
|
|
101
|
-
skill: LocalSkill,
|
|
102
|
-
resolveSupport: { value: boolean | null },
|
|
103
|
-
): Promise<Candidate> {
|
|
104
|
-
if (resolveSupport.value !== false) {
|
|
105
|
-
try {
|
|
106
|
-
const resolved = await apiRequest(
|
|
107
|
-
registry,
|
|
108
|
-
{
|
|
109
|
-
method: 'GET',
|
|
110
|
-
path: `${ApiRoutes.resolve}?slug=${encodeURIComponent(skill.slug)}&hash=${encodeURIComponent(skill.fingerprint)}`,
|
|
111
|
-
},
|
|
112
|
-
ApiV1SkillResolveResponseSchema,
|
|
113
|
-
)
|
|
114
|
-
resolveSupport.value = true
|
|
115
|
-
const latestVersion = resolved.latestVersion?.version ?? null
|
|
116
|
-
const matchVersion = resolved.match?.version ?? null
|
|
117
|
-
if (!latestVersion) {
|
|
118
|
-
return {
|
|
119
|
-
...skill,
|
|
120
|
-
status: 'new',
|
|
121
|
-
matchVersion: null,
|
|
122
|
-
latestVersion: null,
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
return {
|
|
126
|
-
...skill,
|
|
127
|
-
status: matchVersion ? 'synced' : 'update',
|
|
128
|
-
matchVersion,
|
|
129
|
-
latestVersion,
|
|
130
|
-
}
|
|
131
|
-
} catch (error) {
|
|
132
|
-
const message = formatError(error)
|
|
133
|
-
if (/skill not found/i.test(message) || /HTTP 404/i.test(message)) {
|
|
134
|
-
resolveSupport.value = true
|
|
135
|
-
return {
|
|
136
|
-
...skill,
|
|
137
|
-
status: 'new',
|
|
138
|
-
matchVersion: null,
|
|
139
|
-
latestVersion: null,
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
if (/no matching routes found/i.test(message)) {
|
|
143
|
-
resolveSupport.value = false
|
|
144
|
-
} else {
|
|
145
|
-
throw error
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
const meta = await apiRequest(
|
|
151
|
-
registry,
|
|
152
|
-
{ method: 'GET', path: `${ApiRoutes.skills}/${encodeURIComponent(skill.slug)}` },
|
|
153
|
-
ApiV1SkillResponseSchema,
|
|
154
|
-
).catch(() => null)
|
|
155
|
-
|
|
156
|
-
const latestVersion = meta?.latestVersion?.version ?? null
|
|
157
|
-
if (!latestVersion) {
|
|
158
|
-
return {
|
|
159
|
-
...skill,
|
|
160
|
-
status: 'new',
|
|
161
|
-
matchVersion: null,
|
|
162
|
-
latestVersion: null,
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
const zip = await downloadZip(registry, { slug: skill.slug, version: latestVersion })
|
|
167
|
-
const remote = hashSkillZip(zip).fingerprint
|
|
168
|
-
const matchVersion = remote === skill.fingerprint ? latestVersion : null
|
|
169
|
-
|
|
170
|
-
return {
|
|
171
|
-
...skill,
|
|
172
|
-
status: matchVersion ? 'synced' : 'update',
|
|
173
|
-
matchVersion,
|
|
174
|
-
latestVersion,
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
export async function scanRoots(roots: string[]) {
|
|
179
|
-
const result = await scanRootsWithLabels(roots)
|
|
180
|
-
return {
|
|
181
|
-
roots: result.roots,
|
|
182
|
-
skillsByRoot: result.skillsByRoot,
|
|
183
|
-
skills: result.skills,
|
|
184
|
-
rootsWithSkills: result.rootsWithSkills,
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
export async function scanRootsWithLabels(roots: string[], labels?: Record<string, string>) {
|
|
189
|
-
const all: SkillFolder[] = []
|
|
190
|
-
const rootsWithSkills: string[] = []
|
|
191
|
-
const uniqueRoots = await dedupeRoots(roots)
|
|
192
|
-
const skillsByRoot: Record<string, SkillFolder[]> = {}
|
|
193
|
-
const rootLabels: Record<string, string> = {}
|
|
194
|
-
for (const root of uniqueRoots) {
|
|
195
|
-
const found = await findSkillFolders(root)
|
|
196
|
-
skillsByRoot[root] = found
|
|
197
|
-
if (found.length > 0) rootsWithSkills.push(root)
|
|
198
|
-
all.push(...found)
|
|
199
|
-
if (labels?.[root]) rootLabels[root] = labels[root] as string
|
|
200
|
-
}
|
|
201
|
-
const byFolder = new Map<string, SkillFolder>()
|
|
202
|
-
for (const folder of all) {
|
|
203
|
-
byFolder.set(folder.folder, folder)
|
|
204
|
-
}
|
|
205
|
-
return {
|
|
206
|
-
roots: uniqueRoots,
|
|
207
|
-
skillsByRoot,
|
|
208
|
-
skills: Array.from(byFolder.values()),
|
|
209
|
-
rootsWithSkills,
|
|
210
|
-
rootLabels,
|
|
211
|
-
}
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
export function mergeScan(
|
|
215
|
-
left: {
|
|
216
|
-
roots: string[]
|
|
217
|
-
skillsByRoot: Record<string, SkillFolder[]>
|
|
218
|
-
skills: SkillFolder[]
|
|
219
|
-
rootsWithSkills: string[]
|
|
220
|
-
rootLabels: Record<string, string>
|
|
221
|
-
},
|
|
222
|
-
right: {
|
|
223
|
-
roots: string[]
|
|
224
|
-
skillsByRoot: Record<string, SkillFolder[]>
|
|
225
|
-
skills: SkillFolder[]
|
|
226
|
-
rootsWithSkills: string[]
|
|
227
|
-
rootLabels: Record<string, string>
|
|
228
|
-
},
|
|
229
|
-
) {
|
|
230
|
-
const mergedRoots = Array.from(new Set([...left.roots, ...right.roots]))
|
|
231
|
-
const skillsByRoot: Record<string, SkillFolder[]> = {}
|
|
232
|
-
for (const root of mergedRoots) {
|
|
233
|
-
skillsByRoot[root] = right.skillsByRoot[root] ?? left.skillsByRoot[root] ?? []
|
|
234
|
-
}
|
|
235
|
-
const rootLabels: Record<string, string> = { ...left.rootLabels, ...right.rootLabels }
|
|
236
|
-
const byFolder = new Map<string, SkillFolder>()
|
|
237
|
-
for (const entry of [...left.skills, ...right.skills]) {
|
|
238
|
-
byFolder.set(entry.folder, entry)
|
|
239
|
-
}
|
|
240
|
-
const skills = Array.from(byFolder.values())
|
|
241
|
-
const rootsWithSkills = mergedRoots.filter((root) => (skillsByRoot[root]?.length ?? 0) > 0)
|
|
242
|
-
return { roots: mergedRoots, skillsByRoot, skills, rootsWithSkills, rootLabels }
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
async function dedupeRoots(roots: string[]) {
|
|
246
|
-
const seen = new Set<string>()
|
|
247
|
-
const unique: string[] = []
|
|
248
|
-
for (const root of roots) {
|
|
249
|
-
const resolved = resolve(root)
|
|
250
|
-
const canonical = await realpath(resolved).catch(() => null)
|
|
251
|
-
const key = canonical ?? resolved
|
|
252
|
-
if (seen.has(key)) continue
|
|
253
|
-
seen.add(key)
|
|
254
|
-
unique.push(key)
|
|
255
|
-
}
|
|
256
|
-
return unique
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
export async function selectToUpload(
|
|
260
|
-
candidates: Candidate[],
|
|
261
|
-
params: { allowPrompt: boolean; all: boolean; bump: 'patch' | 'minor' | 'major' },
|
|
262
|
-
): Promise<Candidate[]> {
|
|
263
|
-
if (params.all || !params.allowPrompt) return candidates
|
|
264
|
-
|
|
265
|
-
const valueByKey = new Map<string, Candidate>()
|
|
266
|
-
const choices = candidates.map((candidate) => {
|
|
267
|
-
const key = candidate.folder
|
|
268
|
-
valueByKey.set(key, candidate)
|
|
269
|
-
return {
|
|
270
|
-
value: key,
|
|
271
|
-
label: `${candidate.slug} ${formatActionableStatus(candidate, params.bump)}`,
|
|
272
|
-
hint: `${abbreviatePath(candidate.folder)} | ${candidate.fileCount} files`,
|
|
273
|
-
}
|
|
274
|
-
})
|
|
275
|
-
|
|
276
|
-
const picked = await multiselect({
|
|
277
|
-
message: 'Select skills to upload',
|
|
278
|
-
options: choices,
|
|
279
|
-
initialValues: choices.map((choice) => choice.value),
|
|
280
|
-
required: false,
|
|
281
|
-
})
|
|
282
|
-
if (isCancel(picked)) fail('Canceled')
|
|
283
|
-
const selected = picked.map((key) => valueByKey.get(String(key))).filter(Boolean) as Candidate[]
|
|
284
|
-
return selected
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
export async function resolvePublishMeta(
|
|
288
|
-
skill: Candidate,
|
|
289
|
-
params: { bump: 'patch' | 'minor' | 'major'; allowPrompt: boolean; changelogFlag?: string },
|
|
290
|
-
) {
|
|
291
|
-
if (skill.status === 'new') {
|
|
292
|
-
return { publishVersion: '1.0.0', changelog: '' }
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
const latest = skill.latestVersion
|
|
296
|
-
if (!latest) fail(`Could not resolve latest version for ${skill.slug}`)
|
|
297
|
-
const publishVersion = semver.inc(latest, params.bump)
|
|
298
|
-
if (!publishVersion) fail(`Could not bump version for ${skill.slug}`)
|
|
299
|
-
|
|
300
|
-
const fromFlag = params.changelogFlag?.trim()
|
|
301
|
-
if (fromFlag) return { publishVersion, changelog: fromFlag }
|
|
302
|
-
|
|
303
|
-
return { publishVersion, changelog: '' }
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
export async function getRegistryWithAuth(opts: GlobalOpts, token: string) {
|
|
307
|
-
const registry = await getRegistry(opts, { cache: true })
|
|
308
|
-
await apiRequest(
|
|
309
|
-
registry,
|
|
310
|
-
{ method: 'GET', path: ApiRoutes.whoami, token },
|
|
311
|
-
ApiV1WhoamiResponseSchema,
|
|
312
|
-
)
|
|
313
|
-
return registry
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
export function formatList(values: string[], max: number) {
|
|
317
|
-
if (values.length === 0) return ''
|
|
318
|
-
const shown = values.map(abbreviatePath)
|
|
319
|
-
if (shown.length <= max) return shown.join('\n')
|
|
320
|
-
const head = shown.slice(0, Math.max(1, max - 1))
|
|
321
|
-
const rest = values.length - head.length
|
|
322
|
-
return [...head, `… +${rest} more`].join('\n')
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
export function printSection(title: string, body?: string) {
|
|
326
|
-
const trimmed = body?.trim()
|
|
327
|
-
if (!trimmed) {
|
|
328
|
-
console.log(title)
|
|
329
|
-
return
|
|
330
|
-
}
|
|
331
|
-
if (trimmed.includes('\n')) {
|
|
332
|
-
console.log(`\n${title}\n${trimmed}`)
|
|
333
|
-
return
|
|
334
|
-
}
|
|
335
|
-
console.log(`${title}: ${trimmed}`)
|
|
336
|
-
}
|
|
337
|
-
|
|
338
|
-
function abbreviatePath(value: string) {
|
|
339
|
-
const home = homedir()
|
|
340
|
-
if (value.startsWith(home)) return `~${value.slice(home.length)}`
|
|
341
|
-
return value
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
function rootTelemetryId(value: string) {
|
|
345
|
-
return createHash('sha256').update(value).digest('hex')
|
|
346
|
-
}
|
|
347
|
-
|
|
348
|
-
function formatRootLabel(value: string) {
|
|
349
|
-
const home = homedir()
|
|
350
|
-
if (value === home) return '~'
|
|
351
|
-
|
|
352
|
-
const normalized = value.replaceAll('\\', '/')
|
|
353
|
-
const normalizedHome = home.replaceAll('\\', '/')
|
|
354
|
-
const isHome = normalized === normalizedHome || normalized.startsWith(`${normalizedHome}/`)
|
|
355
|
-
|
|
356
|
-
const stripped = isHome ? normalized.slice(normalizedHome.length).replace(/^\//, '') : normalized
|
|
357
|
-
const parts = stripped.split('/').filter(Boolean)
|
|
358
|
-
const tail = parts.slice(-2).join('/')
|
|
359
|
-
|
|
360
|
-
if (!tail) return isHome ? '~' : '…'
|
|
361
|
-
return isHome ? `~/${tail}` : `…/${tail}`
|
|
362
|
-
}
|
|
363
|
-
|
|
364
|
-
export function dedupeSkillsBySlug(skills: SkillFolder[]) {
|
|
365
|
-
const bySlug = new Map<string, SkillFolder[]>()
|
|
366
|
-
for (const skill of skills) {
|
|
367
|
-
const existing = bySlug.get(skill.slug)
|
|
368
|
-
if (existing) existing.push(skill)
|
|
369
|
-
else bySlug.set(skill.slug, [skill])
|
|
370
|
-
}
|
|
371
|
-
const unique: SkillFolder[] = []
|
|
372
|
-
const duplicates: string[] = []
|
|
373
|
-
for (const [slug, entries] of bySlug.entries()) {
|
|
374
|
-
unique.push(entries[0] as SkillFolder)
|
|
375
|
-
if (entries.length > 1) duplicates.push(`${slug} (${entries.length})`)
|
|
376
|
-
}
|
|
377
|
-
return { skills: unique, duplicates }
|
|
378
|
-
}
|
|
379
|
-
|
|
380
|
-
export function formatActionableStatus(
|
|
381
|
-
candidate: Candidate,
|
|
382
|
-
bump: 'patch' | 'minor' | 'major',
|
|
383
|
-
): string {
|
|
384
|
-
if (candidate.status === 'new') return 'NEW'
|
|
385
|
-
const latest = candidate.latestVersion
|
|
386
|
-
const next = latest ? semver.inc(latest, bump) : null
|
|
387
|
-
if (latest && next) return `UPDATE ${latest} → ${next}`
|
|
388
|
-
return 'UPDATE'
|
|
389
|
-
}
|
|
390
|
-
|
|
391
|
-
export function formatActionableLine(
|
|
392
|
-
candidate: Candidate,
|
|
393
|
-
bump: 'patch' | 'minor' | 'major',
|
|
394
|
-
): string {
|
|
395
|
-
return `${candidate.slug} ${formatActionableStatus(candidate, bump)} (${candidate.fileCount} files)`
|
|
396
|
-
}
|
|
397
|
-
|
|
398
|
-
function formatSyncedLine(candidate: Candidate): string {
|
|
399
|
-
const version = candidate.matchVersion ?? candidate.latestVersion ?? 'unknown'
|
|
400
|
-
return `${candidate.slug} synced (${version})`
|
|
401
|
-
}
|
|
402
|
-
|
|
403
|
-
export function formatSyncedSummary(candidate: Candidate): string {
|
|
404
|
-
const version = candidate.matchVersion ?? candidate.latestVersion
|
|
405
|
-
return version ? `${candidate.slug}@${version}` : candidate.slug
|
|
406
|
-
}
|
|
407
|
-
|
|
408
|
-
export function formatBulletList(lines: string[], max: number): string {
|
|
409
|
-
if (lines.length <= max) return lines.map((line) => `- ${line}`).join('\n')
|
|
410
|
-
const head = lines.slice(0, max)
|
|
411
|
-
const rest = lines.length - head.length
|
|
412
|
-
return [...head, `... +${rest} more`].map((line) => `- ${line}`).join('\n')
|
|
413
|
-
}
|
|
414
|
-
|
|
415
|
-
export function formatSyncedDisplay(synced: Candidate[]) {
|
|
416
|
-
const lines = synced.map(formatSyncedLine)
|
|
417
|
-
if (lines.length <= 12) return formatBulletList(lines, 12)
|
|
418
|
-
return formatCommaList(synced.map(formatSyncedSummary), 24)
|
|
419
|
-
}
|
|
420
|
-
|
|
421
|
-
export function formatCommaList(values: string[], max: number) {
|
|
422
|
-
if (values.length === 0) return ''
|
|
423
|
-
if (values.length <= max) return values.join(', ')
|
|
424
|
-
const head = values.slice(0, Math.max(1, max - 1))
|
|
425
|
-
const rest = values.length - head.length
|
|
426
|
-
return `${head.join(', ')}, ... +${rest} more`
|
|
427
|
-
}
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import type { SkillOrigin } from '../../skills.js'
|
|
2
|
-
import type { SkillFolder } from '../scanSkills.js'
|
|
3
|
-
|
|
4
|
-
export type SyncOptions = {
|
|
5
|
-
root?: string[]
|
|
6
|
-
all?: boolean
|
|
7
|
-
dryRun?: boolean
|
|
8
|
-
bump?: 'patch' | 'minor' | 'major'
|
|
9
|
-
changelog?: string
|
|
10
|
-
tags?: string
|
|
11
|
-
concurrency?: number
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
export type Candidate = SkillFolder & {
|
|
15
|
-
fingerprint: string
|
|
16
|
-
fileCount: number
|
|
17
|
-
origin: SkillOrigin | null
|
|
18
|
-
status: 'synced' | 'new' | 'update'
|
|
19
|
-
matchVersion: string | null
|
|
20
|
-
latestVersion: string | null
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
export type LocalSkill = SkillFolder & {
|
|
24
|
-
fingerprint: string
|
|
25
|
-
fileCount: number
|
|
26
|
-
origin: SkillOrigin | null
|
|
27
|
-
}
|
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
import { readGlobalConfig } from '../../config.js'
|
|
2
|
-
import { apiRequest } from '../../http.js'
|
|
3
|
-
import { ApiRoutes, ApiV1UnstarResponseSchema } from '../../schema/index.js'
|
|
4
|
-
import { getRegistry } from '../registry.js'
|
|
5
|
-
import type { GlobalOpts } from '../types.js'
|
|
6
|
-
import { createSpinner, fail, formatError, isInteractive, promptConfirm } from '../ui.js'
|
|
7
|
-
|
|
8
|
-
async function requireToken() {
|
|
9
|
-
const cfg = await readGlobalConfig()
|
|
10
|
-
const token = cfg?.token
|
|
11
|
-
if (!token) fail('Not logged in. Run: pilothub login')
|
|
12
|
-
return token
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export async function cmdUnstarSkill(
|
|
16
|
-
opts: GlobalOpts,
|
|
17
|
-
slugArg: string,
|
|
18
|
-
options: { yes?: boolean },
|
|
19
|
-
inputAllowed: boolean,
|
|
20
|
-
) {
|
|
21
|
-
const slug = slugArg.trim().toLowerCase()
|
|
22
|
-
if (!slug) fail('Slug required')
|
|
23
|
-
const allowPrompt = isInteractive() && inputAllowed !== false
|
|
24
|
-
|
|
25
|
-
if (!options.yes) {
|
|
26
|
-
if (!allowPrompt) fail('Pass --yes (no input)')
|
|
27
|
-
const ok = await promptConfirm(`Unstar ${slug}?`)
|
|
28
|
-
if (!ok) return
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
const token = await requireToken()
|
|
32
|
-
const registry = await getRegistry(opts, { cache: true })
|
|
33
|
-
const spinner = createSpinner(`Unstarring ${slug}`)
|
|
34
|
-
try {
|
|
35
|
-
const result = await apiRequest(
|
|
36
|
-
registry,
|
|
37
|
-
{ method: 'DELETE', path: `${ApiRoutes.stars}/${encodeURIComponent(slug)}`, token },
|
|
38
|
-
ApiV1UnstarResponseSchema,
|
|
39
|
-
)
|
|
40
|
-
spinner.succeed(
|
|
41
|
-
result.alreadyUnstarred ? `OK. ${slug} already unstarred.` : `OK. Unstarred ${slug}`,
|
|
42
|
-
)
|
|
43
|
-
return result
|
|
44
|
-
} catch (error) {
|
|
45
|
-
spinner.fail(formatError(error))
|
|
46
|
-
throw error
|
|
47
|
-
}
|
|
48
|
-
}
|
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
type Color = (value: string) => string
|
|
2
|
-
|
|
3
|
-
function wrap(start: string, end = '\x1b[0m'): Color {
|
|
4
|
-
return (value) => `${start}${value}${end}`
|
|
5
|
-
}
|
|
6
|
-
|
|
7
|
-
const ansi = {
|
|
8
|
-
reset: '\x1b[0m',
|
|
9
|
-
bold: wrap('\x1b[1m'),
|
|
10
|
-
dim: wrap('\x1b[2m'),
|
|
11
|
-
cyan: wrap('\x1b[36m'),
|
|
12
|
-
green: wrap('\x1b[32m'),
|
|
13
|
-
yellow: wrap('\x1b[33m'),
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
function isColorEnabled() {
|
|
17
|
-
if (!process.stdout.isTTY) return false
|
|
18
|
-
if (process.env.NO_COLOR) return false
|
|
19
|
-
return true
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
export function styleTitle(value: string) {
|
|
23
|
-
if (!isColorEnabled()) return value
|
|
24
|
-
return `${ansi.bold(ansi.cyan(value))}${ansi.reset}`
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
export function configureCommanderHelp(program: {
|
|
28
|
-
configureHelp: (config: {
|
|
29
|
-
sectionTitle?: (title: string) => string
|
|
30
|
-
optionTerm?: (option: { flags: string }) => string
|
|
31
|
-
commandTerm?: (cmd: { name: () => string }) => string
|
|
32
|
-
}) => unknown
|
|
33
|
-
}) {
|
|
34
|
-
if (!isColorEnabled()) return
|
|
35
|
-
program.configureHelp({
|
|
36
|
-
sectionTitle: (title) => ansi.bold(ansi.cyan(title)),
|
|
37
|
-
optionTerm: (option) => ansi.yellow(option.flags),
|
|
38
|
-
commandTerm: (cmd) => ansi.green(cmd.name()),
|
|
39
|
-
})
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
export function styleEnvBlock(value: string) {
|
|
43
|
-
if (!isColorEnabled()) return value
|
|
44
|
-
return `${ansi.dim(value)}${ansi.reset}`
|
|
45
|
-
}
|
|
@@ -1,159 +0,0 @@
|
|
|
1
|
-
/* @vitest-environment node */
|
|
2
|
-
import { mkdir, mkdtemp, writeFile } from 'node:fs/promises'
|
|
3
|
-
import { tmpdir } from 'node:os'
|
|
4
|
-
import { join, resolve } from 'node:path'
|
|
5
|
-
import { afterEach, describe, expect, it } from 'vitest'
|
|
6
|
-
import { resolvePilotbotDefaultWorkspace, resolvePilotbotSkillRoots } from './pilotbotConfig.js'
|
|
7
|
-
|
|
8
|
-
const originalEnv = { ...process.env }
|
|
9
|
-
|
|
10
|
-
afterEach(() => {
|
|
11
|
-
process.env = { ...originalEnv }
|
|
12
|
-
})
|
|
13
|
-
|
|
14
|
-
describe('resolvePilotbotSkillRoots', () => {
|
|
15
|
-
it('reads JSON5 config and resolves per-agent + shared skill roots', async () => {
|
|
16
|
-
const base = await mkdtemp(join(tmpdir(), 'pilothub-pilotbot-'))
|
|
17
|
-
const home = join(base, 'home')
|
|
18
|
-
const stateDir = join(base, 'state')
|
|
19
|
-
const configPath = join(base, 'pilotbot.json')
|
|
20
|
-
|
|
21
|
-
process.env.HOME = home
|
|
22
|
-
process.env.PILOTBOT_STATE_DIR = stateDir
|
|
23
|
-
process.env.PILOTBOT_CONFIG_PATH = configPath
|
|
24
|
-
|
|
25
|
-
const config = `{
|
|
26
|
-
// JSON5 comments + trailing commas supported
|
|
27
|
-
agents: {
|
|
28
|
-
defaults: { workspace: '~/pilot-main', },
|
|
29
|
-
list: [
|
|
30
|
-
{ id: 'work', name: 'Work Bot', workspace: '~/pilot-work', },
|
|
31
|
-
{ id: 'family', workspace: '~/pilot-family', },
|
|
32
|
-
],
|
|
33
|
-
},
|
|
34
|
-
// legacy entries still supported
|
|
35
|
-
agent: { workspace: '~/pilot-legacy', },
|
|
36
|
-
routing: {
|
|
37
|
-
agents: {
|
|
38
|
-
work: { name: 'Work Bot', workspace: '~/pilot-work', },
|
|
39
|
-
family: { workspace: '~/pilot-family' },
|
|
40
|
-
},
|
|
41
|
-
},
|
|
42
|
-
skills: {
|
|
43
|
-
load: { extraDirs: ['~/shared/skills', '/opt/skills',], },
|
|
44
|
-
},
|
|
45
|
-
}`
|
|
46
|
-
await writeFile(configPath, config, 'utf8')
|
|
47
|
-
|
|
48
|
-
const { roots, labels } = await resolvePilotbotSkillRoots()
|
|
49
|
-
|
|
50
|
-
const expectedRoots = [
|
|
51
|
-
resolve(stateDir, 'skills'),
|
|
52
|
-
resolve(home, 'pilot-main', 'skills'),
|
|
53
|
-
resolve(home, 'pilot-work', 'skills'),
|
|
54
|
-
resolve(home, 'pilot-family', 'skills'),
|
|
55
|
-
resolve(home, 'shared', 'skills'),
|
|
56
|
-
resolve('/opt/skills'),
|
|
57
|
-
]
|
|
58
|
-
|
|
59
|
-
expect(roots).toEqual(expect.arrayContaining(expectedRoots))
|
|
60
|
-
expect(labels[resolve(stateDir, 'skills')]).toBe('Shared skills')
|
|
61
|
-
expect(labels[resolve(home, 'pilot-main', 'skills')]).toBe('Agent: main')
|
|
62
|
-
expect(labels[resolve(home, 'pilot-work', 'skills')]).toBe('Agent: Work Bot')
|
|
63
|
-
expect(labels[resolve(home, 'pilot-family', 'skills')]).toBe('Agent: family')
|
|
64
|
-
expect(labels[resolve(home, 'shared', 'skills')]).toBe('Extra: skills')
|
|
65
|
-
expect(labels[resolve('/opt/skills')]).toBe('Extra: skills')
|
|
66
|
-
})
|
|
67
|
-
|
|
68
|
-
it('resolves default workspace from agents.defaults and agents.list', async () => {
|
|
69
|
-
const base = await mkdtemp(join(tmpdir(), 'pilothub-pilotbot-default-'))
|
|
70
|
-
const home = join(base, 'home')
|
|
71
|
-
const stateDir = join(base, 'state')
|
|
72
|
-
const configPath = join(base, 'pilotbot.json')
|
|
73
|
-
const workspaceMain = join(base, 'workspace-main')
|
|
74
|
-
const workspaceList = join(base, 'workspace-list')
|
|
75
|
-
|
|
76
|
-
process.env.HOME = home
|
|
77
|
-
process.env.PILOTBOT_STATE_DIR = stateDir
|
|
78
|
-
process.env.PILOTBOT_CONFIG_PATH = configPath
|
|
79
|
-
|
|
80
|
-
const config = `{
|
|
81
|
-
agents: {
|
|
82
|
-
defaults: { workspace: "${workspaceMain}", },
|
|
83
|
-
list: [
|
|
84
|
-
{ id: 'main', workspace: "${workspaceList}", default: true },
|
|
85
|
-
],
|
|
86
|
-
},
|
|
87
|
-
}`
|
|
88
|
-
await writeFile(configPath, config, 'utf8')
|
|
89
|
-
|
|
90
|
-
const workspace = await resolvePilotbotDefaultWorkspace()
|
|
91
|
-
expect(workspace).toBe(resolve(workspaceMain))
|
|
92
|
-
})
|
|
93
|
-
|
|
94
|
-
it('falls back to default agent in agents.list when defaults missing', async () => {
|
|
95
|
-
const base = await mkdtemp(join(tmpdir(), 'pilothub-pilotbot-list-'))
|
|
96
|
-
const home = join(base, 'home')
|
|
97
|
-
const configPath = join(base, 'pilotbot.json')
|
|
98
|
-
const workspaceMain = join(base, 'workspace-main')
|
|
99
|
-
const workspaceWork = join(base, 'workspace-work')
|
|
100
|
-
|
|
101
|
-
process.env.HOME = home
|
|
102
|
-
process.env.PILOTBOT_CONFIG_PATH = configPath
|
|
103
|
-
|
|
104
|
-
const config = `{
|
|
105
|
-
agents: {
|
|
106
|
-
list: [
|
|
107
|
-
{ id: 'main', workspace: "${workspaceMain}", default: true },
|
|
108
|
-
{ id: 'work', workspace: "${workspaceWork}" },
|
|
109
|
-
],
|
|
110
|
-
},
|
|
111
|
-
}`
|
|
112
|
-
await writeFile(configPath, config, 'utf8')
|
|
113
|
-
|
|
114
|
-
const workspace = await resolvePilotbotDefaultWorkspace()
|
|
115
|
-
expect(workspace).toBe(resolve(workspaceMain))
|
|
116
|
-
})
|
|
117
|
-
|
|
118
|
-
it('respects PILOTBOT_STATE_DIR and PILOTBOT_CONFIG_PATH overrides', async () => {
|
|
119
|
-
const base = await mkdtemp(join(tmpdir(), 'pilothub-pilotbot-override-'))
|
|
120
|
-
const home = join(base, 'home')
|
|
121
|
-
const stateDir = join(base, 'custom-state')
|
|
122
|
-
const configPath = join(base, 'config', 'pilotbot.json')
|
|
123
|
-
|
|
124
|
-
process.env.HOME = home
|
|
125
|
-
process.env.PILOTBOT_STATE_DIR = stateDir
|
|
126
|
-
process.env.PILOTBOT_CONFIG_PATH = configPath
|
|
127
|
-
|
|
128
|
-
const config = `{
|
|
129
|
-
agent: { workspace: "${join(base, 'workspace-main')}" },
|
|
130
|
-
}`
|
|
131
|
-
await mkdir(join(base, 'config'), { recursive: true })
|
|
132
|
-
await writeFile(configPath, config, 'utf8')
|
|
133
|
-
|
|
134
|
-
const { roots, labels } = await resolvePilotbotSkillRoots()
|
|
135
|
-
|
|
136
|
-
expect(roots).toEqual(
|
|
137
|
-
expect.arrayContaining([
|
|
138
|
-
resolve(stateDir, 'skills'),
|
|
139
|
-
resolve(join(base, 'workspace-main'), 'skills'),
|
|
140
|
-
]),
|
|
141
|
-
)
|
|
142
|
-
expect(labels[resolve(stateDir, 'skills')]).toBe('Shared skills')
|
|
143
|
-
expect(labels[resolve(join(base, 'workspace-main'), 'skills')]).toBe('Agent: main')
|
|
144
|
-
})
|
|
145
|
-
|
|
146
|
-
it('returns shared skills root when config is missing', async () => {
|
|
147
|
-
const base = await mkdtemp(join(tmpdir(), 'pilothub-pilotbot-missing-'))
|
|
148
|
-
const stateDir = join(base, 'state')
|
|
149
|
-
const configPath = join(base, 'missing', 'pilotbot.json')
|
|
150
|
-
|
|
151
|
-
process.env.PILOTBOT_STATE_DIR = stateDir
|
|
152
|
-
process.env.PILOTBOT_CONFIG_PATH = configPath
|
|
153
|
-
|
|
154
|
-
const { roots, labels } = await resolvePilotbotSkillRoots()
|
|
155
|
-
|
|
156
|
-
expect(roots).toEqual([resolve(stateDir, 'skills')])
|
|
157
|
-
expect(labels[resolve(stateDir, 'skills')]).toBe('Shared skills')
|
|
158
|
-
})
|
|
159
|
-
})
|