hugoblox 0.4.1 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -1,3 +1,217 @@
1
+ interface TemplateInfo {
2
+ readonly id: string;
3
+ readonly name: string;
4
+ readonly description?: string;
5
+ readonly repoPath?: string;
6
+ readonly slug: string;
7
+ readonly tags?: string[];
8
+ readonly source?: "oss" | "pro" | "custom";
9
+ readonly version?: string;
10
+ readonly downloadEndpoint?: string;
11
+ readonly sizeBytes?: number | null;
12
+ readonly updatedAt?: string;
13
+ readonly commitHash?: string | null;
14
+ }
15
+ interface HbxConfigData {
16
+ readonly token?: string;
17
+ readonly telemetry?: {
18
+ enabled?: boolean;
19
+ lastPrompted?: number;
20
+ id?: string;
21
+ };
22
+ readonly device?: DeviceConfigData;
23
+ readonly updates?: {
24
+ lastChecked?: number;
25
+ latestVersion?: string;
26
+ lastNotifiedAt?: number;
27
+ };
28
+ readonly defaults?: {
29
+ template?: string;
30
+ };
31
+ }
32
+ interface DeviceConfigData {
33
+ readonly id?: string;
34
+ readonly label?: string;
35
+ readonly secretAlias?: string;
36
+ readonly secretFallback?: string;
37
+ readonly secretFallbackSalt?: string;
38
+ }
39
+
40
+ interface WelcomeScreenAPI {
41
+ templates: {
42
+ fetchAll(options?: TemplateQueryOptions): Promise<TemplateWithPreview[]>;
43
+ getById(id: string): Promise<TemplateWithPreview | null>;
44
+ getPreview(id: string): Promise<TemplatePreview | null>;
45
+ };
46
+ creation: {
47
+ create(config: SiteCreationConfig): AsyncIterable<CreationStep>;
48
+ validate(config: SiteCreationConfig): Promise<ValidationResult>;
49
+ cancel(): Promise<void>;
50
+ };
51
+ system: {
52
+ detectPackageManager(path?: string): Promise<PackageManager>;
53
+ validatePath(path: string): Promise<PathValidation>;
54
+ getDefaultConfig(): WelcomeScreenConfig;
55
+ };
56
+ }
57
+ interface SiteCreationConfig {
58
+ templateId: string;
59
+ siteName: string;
60
+ targetPath: string;
61
+ packageManager?: PackageManager;
62
+ options?: {
63
+ includeSampleContent?: boolean;
64
+ initGit?: boolean;
65
+ autoInstall?: boolean;
66
+ autoPreview?: boolean;
67
+ };
68
+ }
69
+ interface TemplateWithPreview extends TemplateInfo {
70
+ preview: {
71
+ thumbnailUrl: string;
72
+ category: TemplateCategory;
73
+ difficulty: TemplateDifficulty;
74
+ estimatedTime: number;
75
+ features: string[];
76
+ useCases: string[];
77
+ };
78
+ stats?: {
79
+ popularity: number;
80
+ lastUpdated: string;
81
+ downloads?: number;
82
+ };
83
+ }
84
+ interface CreationStep {
85
+ id: string;
86
+ type: "info" | "progress" | "success" | "error" | "warning";
87
+ title: string;
88
+ message?: string;
89
+ progress?: number;
90
+ timestamp: Date;
91
+ data?: {
92
+ templateId?: string;
93
+ path?: string;
94
+ url?: string;
95
+ error?: Error;
96
+ [key: string]: unknown;
97
+ };
98
+ }
99
+ interface TemplatePreview {
100
+ templateId: string;
101
+ thumbnailUrl: string;
102
+ screenshots: string[];
103
+ demoUrl?: string;
104
+ features: string[];
105
+ useCases: string[];
106
+ }
107
+ interface ValidationResult {
108
+ valid: boolean;
109
+ errors: ValidationError[];
110
+ warnings: ValidationWarning[];
111
+ }
112
+ interface ValidationError {
113
+ field: keyof SiteCreationConfig;
114
+ message: string;
115
+ code: string;
116
+ }
117
+ interface ValidationWarning {
118
+ field: keyof SiteCreationConfig;
119
+ message: string;
120
+ suggestion?: string;
121
+ }
122
+ type PackageManager = "pnpm" | "npm" | "yarn";
123
+ interface PathValidation {
124
+ valid: boolean;
125
+ exists: boolean;
126
+ isEmpty: boolean;
127
+ writable: boolean;
128
+ error?: string;
129
+ }
130
+ interface WelcomeScreenConfig extends SiteCreationConfig {
131
+ showAdvanced: boolean;
132
+ rememberedChoices: {
133
+ lastPackageManager?: PackageManager;
134
+ lastTargetPath?: string;
135
+ };
136
+ }
137
+ type TemplateCategory = "academic" | "business" | "portfolio" | "blog" | "documentation" | "other";
138
+ type TemplateDifficulty = "beginner" | "intermediate" | "advanced";
139
+ interface TemplateQueryOptions {
140
+ category?: TemplateCategory;
141
+ difficulty?: TemplateDifficulty;
142
+ source?: "oss" | "pro" | "custom";
143
+ sortBy?: "popularity" | "name" | "difficulty" | "recent";
144
+ limit?: number;
145
+ }
146
+ interface WelcomeAPIOptions {
147
+ debug?: boolean;
148
+ telemetryEnabled?: boolean;
149
+ cacheDir?: string;
150
+ progressCallback?: (step: CreationStep) => void;
151
+ }
152
+
153
+ /**
154
+ * Creates a new instance of the Welcome Screen API
155
+ * This is the main entry point for integrating hugoblox CLI functionality into welcome screens
156
+ */
157
+ declare function createWelcomeScreenAPI(options?: WelcomeAPIOptions): Promise<WelcomeScreenAPI>;
158
+
159
+ declare function getCliVersion(): string;
160
+
161
+ interface ConfigStoreApi {
162
+ toJSON(): HbxConfigData;
163
+ getLicense(): string | undefined;
164
+ setLicense(license: string): void;
165
+ clearLicense(): void;
166
+ isTelemetryEnabled(): boolean;
167
+ setTelemetryEnabled(value: boolean): void;
168
+ getTelemetryId(): string | undefined;
169
+ ensureTelemetryId(): Promise<string>;
170
+ getUpdateMetadata(): HbxConfigData["updates"] | undefined;
171
+ setUpdateMetadata(partial: Partial<NonNullable<HbxConfigData["updates"]>>): void;
172
+ getDeviceInfo(): HbxConfigData["device"] | undefined;
173
+ setDeviceInfo(info: HbxConfigData["device"] | undefined): void;
174
+ getDefaultTemplate(): string | undefined;
175
+ save(): Promise<void>;
176
+ }
177
+
178
+ interface LoggerOptions {
179
+ readonly json?: boolean;
180
+ readonly debug?: boolean;
181
+ }
182
+ declare class Logger {
183
+ #private;
184
+ constructor(options?: LoggerOptions);
185
+ isJsonMode(): boolean;
186
+ setJsonMode(value: boolean): void;
187
+ setDebug(value: boolean): void;
188
+ info(message: string): void;
189
+ success(message: string): void;
190
+ warn(message: string): void;
191
+ error(message: string): void;
192
+ debug(message: string): void;
193
+ json(payload: unknown): void;
194
+ }
195
+
196
+ interface TelemetryEvent {
197
+ readonly name: string;
198
+ readonly payload?: Record<string, unknown>;
199
+ }
200
+ declare class TelemetryClient {
201
+ #private;
202
+ constructor(config: ConfigStoreApi, logger: Logger, cliVersion: string);
203
+ isEnabled(): boolean;
204
+ setCommand(command?: string): void;
205
+ track(event: TelemetryEvent): Promise<void>;
206
+ flush(): Promise<void>;
207
+ }
208
+
209
+ interface HbxContext {
210
+ readonly config: ConfigStoreApi;
211
+ readonly logger: Logger;
212
+ readonly telemetry: TelemetryClient;
213
+ }
214
+
1
215
  declare function bootstrap(argv?: string[]): Promise<void>;
2
216
 
3
- export { bootstrap };
217
+ export { type CreationStep, type HbxContext, type PackageManager, type SiteCreationConfig, type TemplateQueryOptions, type TemplateWithPreview, type ValidationResult, type WelcomeAPIOptions, type WelcomeScreenAPI, type WelcomeScreenConfig, bootstrap, createWelcomeScreenAPI, getCliVersion };
package/dist/index.js CHANGED
@@ -1,28 +1,28 @@
1
1
  #!/usr/bin/env node
2
2
  import { Command } from 'commander';
3
- import P from 'node:fs/promises';
3
+ import T from 'node:fs/promises';
4
4
  import E from 'node:path';
5
- import Te from 'ora';
6
- import Ze from 'yaml';
5
+ import Ue from 'ora';
6
+ import He from 'yaml';
7
7
  import { execa } from 'execa';
8
- import ve from 'node:os';
9
- import Ke from 'node:dns/promises';
10
- import Pe from 'prompts';
11
- import oe, { randomUUID } from 'node:crypto';
8
+ import De from 'node:os';
9
+ import ht from 'node:dns/promises';
10
+ import Fe from 'prompts';
11
+ import pe, { randomUUID } from 'node:crypto';
12
12
  import { createRequire } from 'node:module';
13
13
  import { promisify } from 'node:util';
14
+ import F from 'node:fs';
14
15
  import { fileURLToPath } from 'node:url';
15
- import L from 'semver';
16
- import Wt from 'node:fs';
17
- import ne from 'chalk';
16
+ import N from 'semver';
17
+ import ge from 'chalk';
18
18
  import { Buffer as Buffer$1 } from 'node:buffer';
19
19
 
20
- var R=class extends Error{constructor(e,o="HBX_ERROR",r){super(e),this.name="HbxError",this.code=o,this.details=r;}},d=class extends R{constructor(e,o){super(e,"HBX_INPUT_ERROR",o);}},G=class extends R{constructor(e="Operation cancelled by user"){super(e,"HBX_ABORTED");}},ie=class extends R{constructor(e,o){super(e,"HBX_CONFIG_ERROR",o);}};function De(t){return !!(t&&typeof t=="object"&&"code"in t)}async function v(t){try{return await P.access(t),!0}catch{return false}}async function H(t){await P.mkdir(t,{recursive:true});}async function Ne(t){if(!await v(t)){await H(t);return}if((await P.readdir(t)).length>0)throw new Error(`Directory ${t} is not empty`)}async function $(t,e){await H(E.dirname(t)),await P.writeFile(t,e);}var ce="hbx",ee="HBX_DEBUG",X="HBX_LICENSE_TOKEN",Me="HBX_TELEMETRY_DISABLED",Ce=E.join(ve.homedir(),".hbx"),le=E.join(Ce,"config.json"),Qt=E.join(ve.homedir(),".cache","hbx"),W=E.join(Qt,"templates"),Fe="HBX_UPDATE_CHECK_DISABLED",z="https://api.github.com",Ue=`${z}/repos/HugoBlox/hugo-blox-builder/contents/templates`,Ve="https://github.com/HugoBlox/hugo-blox-builder.git",Ge="HBX_TEMPLATES_DIR",M="https://cli-api.hugoblox.com",Xe=`${M}/api/usage-insights`,me="hugoblox",We=1e3*60*60*24,ze=1e3*60*60*12;var Ee=[{id:"academic-cv",name:"Academic CV",description:"Publish your academic CV and showcase your research.",repoPath:"templates/academic-cv",slug:"academic-cv",tags:["academic","cv"],source:"oss"},{id:"startup",name:"Startup",description:"Marketing site for SaaS and startups.",repoPath:"templates/startup",slug:"startup",tags:["business"],source:"oss"},{id:"folio",name:"Portfolio",description:"Minimal portfolio for creatives.",repoPath:"templates/folio",slug:"folio",tags:["portfolio"],source:"oss"}];var de="hbx.blocks.json";async function A(t=process.cwd()){let e=t,{root:o}=E.parse(t);for(;;){if((await Promise.all([v(E.join(e,"config","_default")),v(E.join(e,"hugoblox.yaml"))])).some(Boolean))return {root:e,manifestPath:E.join(e,de)};if(e===o)return;e=E.dirname(e);}}async function Je(t){let e=E.join(t,"hugoblox.yaml");if(await v(e))try{let o=await P.readFile(e,"utf8"),r=Ze.parse(o);if(!r)return;let i=r.template??{},n=r.build??{};return {templateId:i.id,templateName:i.name,requiredHugoVersion:n.hugo_version}}catch(o){throw new d("Failed to parse hugoblox.yaml",{error:o})}}async function qe(){try{let{stdout:t}=await execa("hugo",["version"]);return oo(t)}catch{return}}function oo(t){let e=t.match(/v(\d+\.\d+\.\d+)/i);return e?e[1]:t.match(/(\d+\.\d+\.\d+)/)?.[1]}var F=class extends Error{constructor(o,r){super(o);this.cause=r;this.name="NetworkError";}};async function q(t,e={}){let{timeout:o=15e3,retries:r=3,backoff:i=500,...n}=e,s=0;for(;s<=r;){let a=new AbortController,c=setTimeout(()=>a.abort(),o);try{let l=await fetch(t,{...n,signal:a.signal});if(clearTimeout(c),l.status===429||l.status>=500&&l.status<600)throw new F(`Server error: ${l.status}`);return l}catch(l){clearTimeout(c),s++;let u=l.name==="AbortError";if(s>r)throw u?new F(`Request timed out after ${o}ms`,l):l instanceof F?l:new F("Network request failed",l);let T=i*2**(s-1);await new Promise(m=>setTimeout(m,T));}}throw new F("Unreachable")}async function Ye(){try{return await Ke.lookup("google.com"),!0}catch{try{return await Ke.lookup("1.1.1.1"),!0}catch{return false}}}async function j(t,e={}){if(e.ci){if(typeof e.initial=="string")return e.initial;throw new d(`Option required in CI mode: ${t}`)}return (await Pe({type:"text",name:"value",message:t,initial:e.initial},{onCancel:()=>{throw new G}})).value}async function te(t,e,o={}){if(o.ci){if(typeof o.initial=="string")return o.initial;throw new d(`Option required in CI mode: ${t}`)}return (await Pe({type:"select",name:"value",message:t,choices:e,initial:0},{onCancel:()=>{throw new G}})).value}async function _(t,e={}){if(e.ci){if(typeof e.initial=="boolean")return e.initial;throw new d(`Confirmation required in CI mode: ${t}`)}return (await Pe({type:"confirm",name:"value",message:t,initial:e.initial??true},{onCancel:()=>{throw new G}})).value}function et(t){let e=t.match(/github\.com[/:]([\w-]+)\/([\w-]+?)(?:\.git)?$/i);return e?{owner:e[1],repo:e[2]}:null}function ro(t){return et(t)!==null}async function no(t,e){let o=`https://api.github.com/repos/${t}/${e}/contents/hugoblox/themes`;try{let r=await q(o,{headers:{"User-Agent":"hbx-cli",Accept:"application/vnd.github.v3+json"}});if(!r.ok)throw r.status===404?new d(`Repository does not contain a 'hugoblox/themes' directory. Please check the repository structure at https://github.com/${t}/${e}`):new d(`Failed to fetch themes from GitHub (status ${r.status})`);let n=(await r.json()).filter(s=>s.type==="file"&&s.name.toLowerCase().endsWith(".yaml"));if(n.length===0)throw new d(`No theme files found in hugoblox/themes directory at https://github.com/${t}/${e}`);return n.map(s=>({name:s.name,downloadUrl:s.download_url}))}catch(r){throw r instanceof d?r:new d(`Failed to access GitHub repository: ${r instanceof Error?r.message:String(r)}`)}}async function io(t,e){let o=await q(t,{headers:{"User-Agent":"hbx-cli"}});if(!o.ok)throw new d(`Failed to download theme file (status ${o.status})`);let r=await o.text();await $(e,r);}async function so(t,e){let o=E.join(t,"config","_default","params.yaml");if(!await v(o))throw new d(`params.yaml not found at ${o}. Please ensure you're in a HugoBlox site.`);let r=await P.readFile(o,"utf8"),i=Ze.parse(r),n=typeof i=="object"&&i!==null?i:{};(typeof n.hugoblox!="object"||n.hugoblox===null)&&(n.hugoblox={});let s=n.hugoblox;(typeof s.theme!="object"||s.theme===null)&&(s.theme={}),s.theme.pack=e;let a=Ze.stringify(n);await P.writeFile(o,a,"utf8");}function N(t){return t.toLowerCase().replace(/\.yaml$/i,"")}function tt(t,e){t.command("theme").description("Add theme pack(s) from a GitHub repository").option("--repo <url>","GitHub repository URL containing themes").option("--theme <name>","Specific theme filename to download (without .yaml), or 'all' for all themes").option("--set-active","Set the downloaded theme as active in params.yaml").option("--no-set-active","Skip setting theme as active").option("--ci","Run in non-interactive mode").action(async(o,r)=>{let{logger:i,telemetry:n}=e,s=!!r.optsWithGlobals().json;try{let a=await A();if(!a)throw new d("Not in a HugoBlox site. Please run this command from within a HugoBlox site directory (must contain hugoblox.yaml)");let c=a.root,l=o.repo;if(!l){if(o.ci)throw new d("--repo is required when running with --ci");l=await j("Enter the GitHub repository URL containing themes (e.g., https://github.com/username/themes-repo):");}if(l=l.trim(),!ro(l))throw new d("Invalid GitHub repository URL. Please provide a valid GitHub URL (e.g., https://github.com/owner/repo)");let u=et(l);if(!u)throw new d("Failed to parse GitHub URL");let{owner:g,repo:T}=u,m=g,f=Te("Fetching available themes...").start(),y;try{y=await no(g,T),f.succeed(`Found ${y.length} theme(s)`);}catch(w){throw f.fail("Failed to fetch themes"),w}let p,h=o.theme;if(!h)if(o.ci)h="all";else {let w=[{title:"All themes",value:"all"},...y.map(C=>({title:N(C.name),value:C.name}))];h=await te("Which theme(s) do you want to download?",w);}if(h.toLowerCase()==="all")p=y;else {let w=N(h),b=y.find(C=>N(C.name)===w);if(!b)throw new d(`Theme '${h}' not found. Available themes: ${y.map(C=>C.name).join(", ")}`);p=[b];}let I=E.join(c,"data","themes",m);await H(I);let x=Te(`Downloading ${p.length} theme(s)...`).start();try{for(let w of p){let b=E.join(I,w.name);await io(w.downloadUrl,b);}x.succeed(`\u2705 Downloaded ${p.length} theme(s) to data/themes/${m}/`);}catch(w){throw x.fail("Failed to download themes"),w}let k=!1,S=null;if((o.setActive||o.setActive!==!1&&!o.noSetActive)&&(o.ci?p.length===1&&(k=!0,S=N(p[0].name)):(k=await _("Do you want to set one of these themes as active?",{initial:p.length===1}),k&&(p.length===1?S=N(p[0].name):S=await te("Which theme do you want to activate?",p.map(b=>({title:N(b.name),value:N(b.name)})))))),k&&S){let w=`${m}/${S}`,b=Te("Updating params.yaml...").start();try{await so(c,w),b.succeed(`\u2705 Set active theme to: ${w}`);}catch(C){throw b.fail("Failed to update params.yaml"),C}}if(await n.track({name:"add-theme",payload:{vendor:m,themesCount:p.length,activated:k,ci:!!o.ci}}),s){i.json({status:"success",vendor:m,themes:p.map(w=>w.name),activated:S?`${m}/${S}`:null});return}i.success(`
21
- \u{1F3A8} Themes installed successfully!`),(!k||!S)&&(i.info(`
22
- \u{1F4DD} To use a theme, update your config/_default/params.yaml:`),i.info(`
23
- hugoblox:`),i.info(" theme:"),i.info(` pack: "${m}/${N(p[0].name)}"`));}catch(a){if(s){i.json({status:"error",message:a instanceof Error?a.message:String(a)});return}throw a}});}function ot(t,e){let o=t.command("add").description("Add resources (themes)");tt(o,e);}function rt(t,e){t.command("block").argument("<id>","Block identifier").description("Create a new HugoBlox block").option("--example <slug>","Include sample content for the block").option("--dry-run","Print the actions without writing files").option("--yes","Overwrite files when they already exist").action((o,r,i)=>{let{logger:n}=e,s=!!i.optsWithGlobals().json,a="Block creation is coming soon. Follow Discord for updates or to preview it early.";if(s){n.json({status:"pending",message:a});return}n.info(a);});}var He="com.hugoblox.cli",lo=1,mo=createRequire(import.meta.url),uo=promisify(oe.scrypt),D=null;try{D=mo("keytar");}catch{D=null;}async function nt(t,e,o,r,i){let n=`device:${o}`,s,a;if(D)await D.setPassword(He,n,r);else {let c=await go(r,e);s=c.payload,a=c.salt;}t.setDeviceInfo({id:o,label:i,secretAlias:D?n:void 0,secretFallback:s,secretFallbackSalt:a,secretFallbackVersion:s?lo:void 0}),await t.save();}async function it(t,e){let o=t.getDeviceInfo();if(!o?.id)return;let r;if(o.secretAlias&&D&&(r=await D.getPassword(He,o.secretAlias)),!r&&o.secretFallback&&o.secretFallbackSalt)try{let{key:i}=await st(e,o.secretFallbackSalt);r=fo(o.secretFallback,i);}catch{r=void 0;}if(r)return {id:o.id,secret:r,label:o.label}}async function Se(t){let e=t.getDeviceInfo();e?.secretAlias&&D&&await D.deletePassword(He,e.secretAlias),t.setDeviceInfo(void 0),await t.save();}function po(t,e){let o=oe.randomBytes(12),r=oe.createCipheriv("aes-256-gcm",e.subarray(0,32),o),i=Buffer.concat([r.update(t,"utf8"),r.final()]),n=r.getAuthTag();return Buffer.concat([o,n,i]).toString("base64")}function fo(t,e){let o=Buffer.from(t,"base64"),r=o.subarray(0,12),i=o.subarray(12,28),n=o.subarray(28),s=oe.createDecipheriv("aes-256-gcm",e.subarray(0,32),r);return s.setAuthTag(i),Buffer.concat([s.update(n),s.final()]).toString("utf8")}async function st(t,e){let o=Buffer.from(e,"base64");return {key:await uo(t,o,32,{N:32768,r:8,p:1,maxmem:64*1024*1024}),salt:e}}async function go(t,e){let o=oe.randomBytes(16).toString("base64"),{key:r}=await st(e,o);return {payload:po(t,r),salt:o}}async function B(t,e={}){let o=t.config.getLicense();if(!o)throw new d("Set HBX_LICENSE_TOKEN or run 'hbx login' to access Pro content.");let r=await t.config.ensureTelemetryId();if(e.force)await Se(t.config);else {let s=await it(t.config,r);if(s)return {id:s.id,secret:s.secret}}let i=e.label??ve.hostname(),n=await yo(t.config,o,r,i);return await nt(t.config,r,n.deviceId,n.deviceSecret,i),t.logger.debug(`Registered device ${n.deviceId}`),{id:n.deviceId,secret:n.deviceSecret}}async function at(t){await Se(t);}async function yo(t,e,o,r){let i=await fetch(`${M}/api/license/devices/register`,{method:"POST",headers:{Authorization:`Bearer ${e}`,"Content-Type":"application/json","x-telemetry-id":o},body:JSON.stringify({deviceLabel:r})}),n=await wo(i);if(!i.ok)throw n?.code==="license_device_limit"?new d("This license has reached the maximum number of devices. Revoke an existing device and try again."):i.status===401||i.status===403?new d(n?.error??"Unable to register device"):new d(n?.error??`Device registration failed (status ${i.status})`);if(!n?.deviceId||!n?.deviceSecret)throw new d("Device registration response was missing credentials");return n}async function wo(t){try{return await t.json()}catch{return}}function bo(t){return t.split("-").map(e=>e.charAt(0).toUpperCase()+e.slice(1)).join(" ")}async function ct(t){let{logger:e}=t,o=await vo(e),r=await xo(t);return o.length||r.length?[...r,...o]:Ee}async function vo(t){try{let e=await fetch(Ue,{headers:{"User-Agent":"hbx-cli"}});if(!e.ok)throw new Error(`GitHub responded with status ${e.status}`);return (await e.json()).filter(i=>i.type==="dir").map(i=>({id:i.name,name:bo(i.name),description:`Starter template ${i.name}`,repoPath:i.path,slug:i.name,source:"oss"}))}catch(e){return t?.debug?.(`Falling back to offline starter metadata: ${String(e)}`),Ee}}async function xo(t){let{logger:e,config:o}=t,r=new Headers({"User-Agent":"hbx-cli"});try{let n=await o.ensureTelemetryId();r.set("x-telemetry-id",n);}catch{}let i=o.getLicense();if(i)try{let n=await B(t);r.set("Authorization",`Bearer ${i}`),r.set("X-HBX-Device-Id",n.id),r.set("X-HBX-Device-Secret",n.secret);}catch(n){e?.debug?.(`Skipping device-auth headers for Pro templates: ${String(n)}`);}try{let n=await fetch(`${M}/api/pro-templates`,{headers:r});if(!n.ok)throw new Error(`catalog responded with status ${n.status}`);return (await n.json()).templates.map(a=>({id:a.id,name:`${a.name} (Pro)`,description:a.description??void 0,repoPath:void 0,slug:a.slug??a.id,source:"pro",version:a.version,sizeBytes:a.sizeBytes,updatedAt:a.updatedAt,commitHash:a.commitHash,downloadEndpoint:a.downloadUrl}))}catch(n){return e.debug(`Unable to fetch Pro template catalog: ${String(n)}`),[]}}async function lt(t,e){try{await execa("git",["init"],{cwd:t}),await execa("git",["add","."],{cwd:t}),await execa("git",["commit","-m","chore: initialize site"],{cwd:t,reject:!1}),e.success(`Initialized git repository in ${E.relative(process.cwd(),t)||"."}`);}catch(o){e.warn(`Git initialization skipped: ${String(o)}`);}}async function Ie(t,e,o={},r=0){let i=t.config.getLicense();if(!i)throw new d("A HugoBlox Pro license key is required before accessing Pro templates.");let n=await t.config.ensureTelemetryId(),s=await B(t,{force:r>0}),a=new Headers(o.headers??{});a.set("Authorization",`Bearer ${i}`),a.set("X-HBX-Device-Id",s.id),a.set("X-HBX-Device-Secret",s.secret),a.set("X-Telemetry-Id",n);let c=await fetch(`${M}${e}`,{...o,headers:a});if((c.status===401||c.status===403)&&r===0)return await B(t,{force:true}),Ie(t,e,o,r+1);if(!c.ok){let l=await Eo(c);throw l?.code==="license_device_limit"?new d("This license has reached the maximum number of devices. Revoke an existing device and try again."):c.status===401||c.status===403?new d(l?.error??"Device authentication failed."):new d(l?.error??`Request failed (${c.status})`)}return c}async function Eo(t){try{return await t.json()}catch{return}}var Po=3;async function ut(t,e,o,r){if(e.source==="pro"){await So(t,e,o,r);return}let i=process.env[Ge];if(i){await To(e,i,o),r.debug(`Hydrated template ${e.id} from local override`);return}let n=await Ho(e,r),s=n!==void 0?E.join(W,e.id,n):void 0;if(s&&await v(s)){await U(s,o),r.debug(`Hydrated template ${e.id} from cache (${n})`);return}let a=await P.mkdtemp(E.join(ve.tmpdir(),"hbx-template-")),c=E.join(a,"repo");try{await execa("git",["clone","--depth","1",Ve,c],{stdio:"ignore"});let l=await ko(c,e);s?(await H(E.dirname(s)),await P.rm(s,{recursive:!0,force:!0}),await P.cp(l,s,{recursive:!0}),await pt(E.join(W,e.id)),await U(s,o),r.debug(`Hydrated template ${e.id} from fresh cache (${n})`)):(await U(l,o),r.debug(`Hydrated template ${e.id} from repository`));}catch(l){throw new d("Failed to clone HugoBlox templates. Ensure git is installed and accessible.",{error:l})}finally{await P.rm(a,{recursive:true,force:true});}}async function To(t,e,o){let r=E.join(e,t.id);try{await P.access(r);}catch(i){throw new d(`Local template not found at ${r}`,{error:i})}await U(r,o);}async function ko(t,e){let o=e.repoPath??"",r=E.join(t,o);return await P.access(r),r}async function U(t,e){await P.rm(e,{recursive:true,force:true}),await H(E.dirname(e)),await P.cp(t,e,{recursive:true}),await P.rm(E.join(e,".git"),{recursive:true,force:true});}async function pt(t){if(!await v(t))return;let e=await P.readdir(t,{withFileTypes:true}),o=await Promise.all(e.filter(r=>r.isDirectory()).map(async r=>{let i=E.join(t,r.name),n=await P.stat(i);return {path:i,mtimeMs:n.mtimeMs}}));o.sort((r,i)=>i.mtimeMs-r.mtimeMs);for(let r of o.slice(Po))await P.rm(r.path,{recursive:true,force:true});}async function Ho(t,e){let o=encodeURIComponent(t.repoPath??""),r=`${z}/repos/HugoBlox/hugo-blox-builder/commits?path=${o}&per_page=1`;try{let i=await fetch(r,{headers:{"User-Agent":"hbx-cli"}});if(!i.ok)throw new Error(`GitHub responded with status ${i.status}`);return (await i.json())[0]?.sha}catch(i){e.debug(`Unable to determine latest template commit for ${t.id}: ${String(i)}`);return}}async function So(t,e,o,r){let i=e.version?E.join(W,e.id,e.version):void 0;if(i&&await v(i)){await U(i,o),r.debug(`Hydrated Pro template ${e.id} from cache (${e.version})`);return}let n=await P.mkdtemp(E.join(ve.tmpdir(),"hbx-pro-template-")),s=E.join(n,"template.tgz"),a=E.join(n,"extract");await H(a);try{let c=await $o(t,e,s);await execa("tar",["-xzf",s,"-C",a]);let l=await Io(a,e.slug),u=e.version??c.version??void 0,g=u!==void 0?E.join(W,e.id,u):void 0;g?(await H(E.dirname(g)),await P.rm(g,{recursive:!0,force:!0}),await P.cp(l,g,{recursive:!0}),await pt(E.join(W,e.id)),await U(g,o),r.debug(`Hydrated Pro template ${e.id} from fresh cache (${u})`)):(await U(l,o),r.debug(`Hydrated Pro template ${e.id} from archive`));}catch(c){throw new d(`Failed to download Pro template ${e.name}. Ensure your license is valid and try again.`,{error:c})}finally{await P.rm(n,{recursive:true,force:true});}}async function $o(t,e,o){let r=e.downloadEndpoint??`/api/pro-templates/${encodeURIComponent(e.id)}/download`,i=await Ie(t,r),n=await _o(i);if(!n?.url)throw new d("Pro template download URL was missing");let s=await fetch(n.url);if(!s.ok)throw new d(`Pro template archive download failed (${s.status})`);let a=Buffer.from(await s.arrayBuffer());return await P.writeFile(o,a),{version:n.version}}async function Io(t,e){let o=await P.readdir(t,{withFileTypes:true}),r=o.find(n=>n.isDirectory()&&n.name===e);if(r)return E.join(t,r.name);let i=o.filter(n=>n.isDirectory());return i.length===1?E.join(t,i[0].name):t}async function _o(t){try{return await t.json()}catch{return}}var ft="https://hugoblox.com/discord",gt="https://docs.hugoblox.com",ht="https://hugoblox.com/pro";function re(t){return t.trim().toLowerCase().replace(/[^a-z0-9-]/g,"-").replace(/-{2,}/g,"-").replace(/^-+|-+$/g,"").slice(0,64)}async function bt(t,e,o,r){let{logger:i}=t;await ut(t,o,e,i),await Bo(e,o),r.sampleContent||await Lo(e,o);}async function vt(t,e,o){o.info(`\u{1F4E6} Installing dependencies with ${e}...`);let r={...process.env},i=["install"],n=!o.isJsonMode();e==="pnpm"&&(r.PNPM_WORKSPACE_DIR=t,r.PNPM_IGNORE_WORKSPACE_ROOT_CHECK="1",await jo(t),i.push("--no-link-workspace-packages"));try{return await execa(e,i,{cwd:t,stdio:n?"ignore":"inherit",env:r}),o.success("\u2705 Dependencies installed"),!0}catch(s){return n?o.warn(`\u26A0\uFE0F Failed to install dependencies automatically. Run ${e} install inside ${t}`):o.warn(`\u26A0\uFE0F Failed to install dependencies automatically: ${String(s)}`),false}}async function jo(t){let e=E.join(t,"pnpm-workspace.yaml");if(await v(e))return;await $(e,`packages:
20
+ var O=class extends Error{constructor(e,t="HBX_ERROR",n){super(e),this.name="HbxError",this.code=t,this.details=n;}},d=class extends O{constructor(e,t){super(e,"HBX_INPUT_ERROR",t);}},Z=class extends O{constructor(e="Operation cancelled by user"){super(e,"HBX_ABORTED");}},he=class extends O{constructor(e,t){super(e,"HBX_CONFIG_ERROR",t);}};function ot(r){return !!(r&&typeof r=="object"&&"code"in r)}async function v(r){try{return await T.access(r),!0}catch{return false}}async function $(r){await T.mkdir(r,{recursive:true});}async function we(r){if(!await v(r)){await $(r);return}if((await T.readdir(r)).length>0)throw new Error(`Directory ${r} is not empty`)}async function j(r,e){await $(E.dirname(r)),await T.writeFile(r,e);}var ve="hbx",G="HBX_DEBUG",ee="HBX_LICENSE_TOKEN",st="HBX_TELEMETRY_DISABLED",Oe=E.join(De.homedir(),".hbx"),xe=E.join(Oe,"config.json"),br=E.join(De.homedir(),".cache","hbx"),te=E.join(br,"templates"),at="HBX_UPDATE_CHECK_DISABLED",re="https://api.github.com",ct=`${re}/repos/HugoBlox/hugo-blox-builder/contents/templates`,lt="https://github.com/HugoBlox/hugo-blox-builder.git",mt="HBX_TEMPLATES_DIR",X="https://cli-api.hugoblox.com",ut=`${X}/api/usage-insights`,Ce="hugoblox",dt=1e3*60*60*24,pt=1e3*60*60*12;var Me=[{id:"academic-cv",name:"Academic CV",description:"Publish your academic CV and showcase your research.",repoPath:"templates/academic-cv",slug:"academic-cv",tags:["academic","cv"],source:"oss"},{id:"startup",name:"Startup",description:"Marketing site for SaaS and startups.",repoPath:"templates/startup",slug:"startup",tags:["business"],source:"oss"},{id:"folio",name:"Portfolio",description:"Minimal portfolio for creatives.",repoPath:"templates/folio",slug:"folio",tags:["portfolio"],source:"oss"}];var Pe="hbx.blocks.json";async function D(r=process.cwd()){let e=r,{root:t}=E.parse(r);for(;;){if((await Promise.all([v(E.join(e,"config","_default")),v(E.join(e,"hugoblox.yaml"))])).some(Boolean))return {root:e,manifestPath:E.join(e,Pe)};if(e===t)return;e=E.dirname(e);}}async function ft(r){let e=E.join(r,"hugoblox.yaml");if(await v(e))try{let t=await T.readFile(e,"utf8"),n=He.parse(t);if(!n)return;let o=n.template??{},i=n.build??{};return {templateId:o.id,templateName:o.name,requiredHugoVersion:i.hugo_version}}catch(t){throw new d("Failed to parse hugoblox.yaml",{error:t})}}async function gt(){try{let{stdout:r}=await execa("hugo",["version"]);return Pr(r)}catch{return}}function Pr(r){let e=r.match(/v(\d+\.\d+\.\d+)/i);return e?e[1]:r.match(/(\d+\.\d+\.\d+)/)?.[1]}var J=class extends Error{constructor(t,n){super(t);this.cause=n;this.name="NetworkError";}};async function oe(r,e={}){let{timeout:t=15e3,retries:n=3,backoff:o=500,...i}=e,s=0;for(;s<=n;){let c=new AbortController,a=setTimeout(()=>c.abort(),t);try{let l=await fetch(r,{...i,signal:c.signal});if(clearTimeout(a),l.status===429||l.status>=500&&l.status<600)throw new J(`Server error: ${l.status}`);return l}catch(l){clearTimeout(a),s++;let m=l.name==="AbortError";if(s>n)throw m?new J(`Request timed out after ${t}ms`,l):l instanceof J?l:new J("Network request failed",l);let b=o*2**(s-1);await new Promise(u=>setTimeout(u,b));}}throw new J("Unreachable")}async function yt(){try{return await ht.lookup("google.com"),!0}catch{try{return await ht.lookup("1.1.1.1"),!0}catch{return false}}}async function B(r,e={}){if(e.ci){if(typeof e.initial=="string")return e.initial;throw new d(`Option required in CI mode: ${r}`)}return (await Fe({type:"text",name:"value",message:r,initial:e.initial},{onCancel:()=>{throw new Z}})).value}async function de(r,e,t={}){if(t.ci){if(typeof t.initial=="string")return t.initial;throw new d(`Option required in CI mode: ${r}`)}return (await Fe({type:"select",name:"value",message:r,choices:e,initial:0},{onCancel:()=>{throw new Z}})).value}async function R(r,e={}){if(e.ci){if(typeof e.initial=="boolean")return e.initial;throw new d(`Confirmation required in CI mode: ${r}`)}return (await Fe({type:"confirm",name:"value",message:r,initial:e.initial??true},{onCancel:()=>{throw new Z}})).value}function vt(r){let e=r.match(/github\.com[/:]([\w-]+)\/([\w-]+?)(?:\.git)?$/i);return e?{owner:e[1],repo:e[2]}:null}function Sr(r){return vt(r)!==null}async function kr(r,e){let t=`https://api.github.com/repos/${r}/${e}/contents/hugoblox/themes`;try{let n=await oe(t,{headers:{"User-Agent":"hbx-cli",Accept:"application/vnd.github.v3+json"}});if(!n.ok)throw n.status===404?new d(`Repository does not contain a 'hugoblox/themes' directory. Please check the repository structure at https://github.com/${r}/${e}`):new d(`Failed to fetch themes from GitHub (status ${n.status})`);let i=(await n.json()).filter(s=>s.type==="file"&&s.name.toLowerCase().endsWith(".yaml"));if(i.length===0)throw new d(`No theme files found in hugoblox/themes directory at https://github.com/${r}/${e}`);return i.map(s=>({name:s.name,downloadUrl:s.download_url}))}catch(n){throw n instanceof d?n:new d(`Failed to access GitHub repository: ${n instanceof Error?n.message:String(n)}`)}}async function Er(r,e){let t=await oe(r,{headers:{"User-Agent":"hbx-cli"}});if(!t.ok)throw new d(`Failed to download theme file (status ${t.status})`);let n=await t.text();await j(e,n);}async function Tr(r,e){let t=E.join(r,"config","_default","params.yaml");if(!await v(t))throw new d(`params.yaml not found at ${t}. Please ensure you're in a HugoBlox site.`);let n=await T.readFile(t,"utf8"),o=He.parse(n),i=typeof o=="object"&&o!==null?o:{};(typeof i.hugoblox!="object"||i.hugoblox===null)&&(i.hugoblox={});let s=i.hugoblox;(typeof s.theme!="object"||s.theme===null)&&(s.theme={}),s.theme.pack=e;let c=He.stringify(i);await T.writeFile(t,c,"utf8");}function U(r){return r.toLowerCase().replace(/\.yaml$/i,"")}function xt(r,e){r.command("theme").description("Add theme pack(s) from a GitHub repository").option("--repo <url>","GitHub repository URL containing themes").option("--theme <name>","Specific theme filename to download (without .yaml), or 'all' for all themes").option("--set-active","Set the downloaded theme as active in params.yaml").option("--no-set-active","Skip setting theme as active").option("--ci","Run in non-interactive mode").action(async(t,n)=>{let{logger:o,telemetry:i}=e,s=!!n.optsWithGlobals().json;try{let c=await D();if(!c)throw new d("Not in a HugoBlox site. Please run this command from within a HugoBlox site directory (must contain hugoblox.yaml)");let a=c.root,l=t.repo;if(!l){if(t.ci)throw new d("--repo is required when running with --ci");l=await B("Enter the GitHub repository URL containing themes (e.g., https://github.com/username/themes-repo):");}if(l=l.trim(),!Sr(l))throw new d("Invalid GitHub repository URL. Please provide a valid GitHub URL (e.g., https://github.com/owner/repo)");let m=vt(l);if(!m)throw new d("Failed to parse GitHub URL");let{owner:f,repo:b}=m,u=f,p=Ue("Fetching available themes...").start(),h;try{h=await kr(f,b),p.succeed(`Found ${h.length} theme(s)`);}catch(S){throw p.fail("Failed to fetch themes"),S}let g,w=t.theme;if(!w)if(t.ci)w="all";else {let S=[{title:"All themes",value:"all"},...h.map(A=>({title:U(A.name),value:A.name}))];w=await de("Which theme(s) do you want to download?",S);}if(w.toLowerCase()==="all")g=h;else {let S=U(w),k=h.find(A=>U(A.name)===S);if(!k)throw new d(`Theme '${w}' not found. Available themes: ${h.map(A=>A.name).join(", ")}`);g=[k];}let P=E.join(a,"data","themes",u);await $(P);let x=Ue(`Downloading ${g.length} theme(s)...`).start();try{for(let S of g){let k=E.join(P,S.name);await Er(S.downloadUrl,k);}x.succeed(`\u2705 Downloaded ${g.length} theme(s) to data/themes/${u}/`);}catch(S){throw x.fail("Failed to download themes"),S}let C=!1,H=null;if((t.setActive||t.setActive!==!1&&!t.noSetActive)&&(t.ci?g.length===1&&(C=!0,H=U(g[0].name)):(C=await R("Do you want to set one of these themes as active?",{initial:g.length===1}),C&&(g.length===1?H=U(g[0].name):H=await de("Which theme do you want to activate?",g.map(k=>({title:U(k.name),value:U(k.name)})))))),C&&H){let S=`${u}/${H}`,k=Ue("Updating params.yaml...").start();try{await Tr(a,S),k.succeed(`\u2705 Set active theme to: ${S}`);}catch(A){throw k.fail("Failed to update params.yaml"),A}}if(await i.track({name:"add-theme",payload:{vendor:u,themesCount:g.length,activated:C,ci:!!t.ci}}),s){o.json({status:"success",vendor:u,themes:g.map(S=>S.name),activated:H?`${u}/${H}`:null});return}o.success(`
21
+ \u{1F3A8} Themes installed successfully!`),(!C||!H)&&(o.info(`
22
+ \u{1F4DD} To use a theme, update your config/_default/params.yaml:`),o.info(`
23
+ hugoblox:`),o.info(" theme:"),o.info(` pack: "${u}/${U(g[0].name)}"`));}catch(c){if(s){o.json({status:"error",message:c instanceof Error?c.message:String(c)});return}throw c}});}function Ct(r,e){let t=r.command("add").description("Add resources (themes)");xt(t,e);}function Pt(r,e){r.command("block").argument("<id>","Block identifier").description("Create a new HugoBlox block").option("--example <slug>","Include sample content for the block").option("--dry-run","Print the actions without writing files").option("--yes","Overwrite files when they already exist").action((t,n,o)=>{let{logger:i}=e,s=!!o.optsWithGlobals().json,c="Block creation is coming soon. Follow Discord for updates or to preview it early.";if(s){i.json({status:"pending",message:c});return}i.info(c);});}var We="com.hugoblox.cli",$r=1,_r=createRequire(import.meta.url),Hr=promisify(pe.scrypt),M=null;try{M=_r("keytar");}catch{M=null;}async function St(r,e,t,n,o){let i=`device:${t}`,s,c;if(M)await M.setPassword(We,i,n);else {let a=await Dr(n,e);s=a.payload,c=a.salt;}r.setDeviceInfo({id:t,label:o,secretAlias:M?i:void 0,secretFallback:s,secretFallbackSalt:c,secretFallbackVersion:s?$r:void 0}),await r.save();}async function kt(r,e){let t=r.getDeviceInfo();if(!t?.id)return;let n;if(t.secretAlias&&M&&(n=await M.getPassword(We,t.secretAlias)),!n&&t.secretFallback&&t.secretFallbackSalt)try{let{key:o}=await Et(e,t.secretFallbackSalt);n=Rr(t.secretFallback,o);}catch{n=void 0;}if(n)return {id:t.id,secret:n,label:t.label}}async function ze(r){let e=r.getDeviceInfo();e?.secretAlias&&M&&await M.deletePassword(We,e.secretAlias),r.setDeviceInfo(void 0),await r.save();}function jr(r,e){let t=pe.randomBytes(12),n=pe.createCipheriv("aes-256-gcm",e.subarray(0,32),t),o=Buffer.concat([n.update(r,"utf8"),n.final()]),i=n.getAuthTag();return Buffer.concat([t,i,o]).toString("base64")}function Rr(r,e){let t=Buffer.from(r,"base64"),n=t.subarray(0,12),o=t.subarray(12,28),i=t.subarray(28),s=pe.createDecipheriv("aes-256-gcm",e.subarray(0,32),n);return s.setAuthTag(o),Buffer.concat([s.update(i),s.final()]).toString("utf8")}async function Et(r,e){let t=Buffer.from(e,"base64");return {key:await Hr(r,t,32,{N:32768,r:8,p:1,maxmem:64*1024*1024}),salt:e}}async function Dr(r,e){let t=pe.randomBytes(16).toString("base64"),{key:n}=await Et(e,t);return {payload:jr(r,n),salt:t}}async function L(r,e={}){let t=r.config.getLicense();if(!t)throw new d("Set HBX_LICENSE_TOKEN or run 'hbx login' to access Pro content.");let n=await r.config.ensureTelemetryId();if(e.force)await ze(r.config);else {let s=await kt(r.config,n);if(s)return {id:s.id,secret:s.secret}}let o=e.label??De.hostname(),i=await Lr(r.config,t,n,o);return await St(r.config,n,i.deviceId,i.deviceSecret,o),r.logger.debug(`Registered device ${i.deviceId}`),{id:i.deviceId,secret:i.deviceSecret}}async function Tt(r){await ze(r);}async function Lr(r,e,t,n){let o=await fetch(`${X}/api/license/devices/register`,{method:"POST",headers:{Authorization:`Bearer ${e}`,"Content-Type":"application/json","x-telemetry-id":t},body:JSON.stringify({deviceLabel:n})}),i=await Nr(o);if(!o.ok)throw i?.code==="license_device_limit"?new d("This license has reached the maximum number of devices. Revoke an existing device and try again."):o.status===401||o.status===403?new d(i?.error??"Unable to register device"):new d(i?.error??`Device registration failed (status ${o.status})`);if(!i?.deviceId||!i?.deviceSecret)throw new d("Device registration response was missing credentials");return i}async function Nr(r){try{return await r.json()}catch{return}}function Or(r){return r.split("-").map(e=>e.charAt(0).toUpperCase()+e.slice(1)).join(" ")}async function V(r){let{logger:e}=r,t=await Mr(e),n=await Fr(r);return t.length||n.length?[...n,...t]:Me}async function Mr(r){try{let e=await fetch(ct,{headers:{"User-Agent":"hbx-cli"}});if(!e.ok)throw new Error(`GitHub responded with status ${e.status}`);return (await e.json()).filter(o=>o.type==="dir").map(o=>({id:o.name,name:Or(o.name),description:`Starter template ${o.name}`,repoPath:o.path,slug:o.name,source:"oss"}))}catch(e){return r?.debug?.(`Falling back to offline starter metadata: ${String(e)}`),Me}}async function Fr(r){let{logger:e,config:t}=r,n=new Headers({"User-Agent":"hbx-cli"});try{let c=await t.ensureTelemetryId();n.set("x-telemetry-id",c);}catch{}let o,i=t.getLicense();if(i)try{let c=await L(r);o=new Headers(n),o.set("Authorization",`Bearer ${i}`),o.set("X-HBX-Device-Id",c.id),o.set("X-HBX-Device-Secret",c.secret);}catch(c){e?.debug?.(`Skipping device-auth headers for Pro templates: ${String(c)}`);}let s=[];o&&s.push({label:"with auth",headers:o}),s.push({label:o?"without auth":"anonymous",headers:n});for(let c of s)try{let a=await fetch(`${X}/api/pro-templates`,{headers:c.headers});if(!a.ok)throw new Error(`catalog responded with status ${a.status}`);return (await a.json()).templates.map(m=>({id:m.id,name:`${m.name} (Pro)`,description:m.description??void 0,repoPath:void 0,slug:m.slug??m.id,source:"pro",version:m.version,sizeBytes:m.sizeBytes,updatedAt:m.updatedAt,commitHash:m.commitHash,downloadEndpoint:m.downloadUrl}))}catch(a){e.debug(`Unable to fetch Pro template catalog (${c.label}): ${String(a)}`);}return []}async function Se(r,e){try{await execa("git",["init"],{cwd:r}),await execa("git",["add","."],{cwd:r}),await execa("git",["commit","-m","chore: initialize site"],{cwd:r,reject:!1}),e.success(`Initialized git repository in ${E.relative(process.cwd(),r)||"."}`);}catch(t){e.warn(`Git initialization skipped: ${String(t)}`);}}async function Xe(r,e,t={},n=0){let o=r.config.getLicense();if(!o)throw new d("A HugoBlox Pro license key is required before accessing Pro templates.");let i=await r.config.ensureTelemetryId(),s=await L(r,{force:n>0}),c=new Headers(t.headers??{});c.set("Authorization",`Bearer ${o}`),c.set("X-HBX-Device-Id",s.id),c.set("X-HBX-Device-Secret",s.secret),c.set("X-Telemetry-Id",i);let a=await fetch(`${X}${e}`,{...t,headers:c});if((a.status===401||a.status===403)&&n===0)return await L(r,{force:true}),Xe(r,e,t,n+1);if(!a.ok){let l=await Vr(a);throw l?.code==="license_device_limit"?new d("This license has reached the maximum number of devices. Revoke an existing device and try again."):a.status===401||a.status===403?new d(l?.error??"Device authentication failed."):new d(l?.error??`Request failed (${a.status})`)}return a}async function Vr(r){try{return await r.json()}catch{return}}var Wr=3;async function $t(r,e,t,n){if(e.source==="pro"){await Jr(r,e,t,n);return}let o=process.env[mt];if(o){await zr(e,o,t),n.debug(`Hydrated template ${e.id} from local override`);return}let i=await Xr(e,n),s=i!==void 0?E.join(te,e.id,i):void 0;if(s&&await v(s)){await q(s,t),n.debug(`Hydrated template ${e.id} from cache (${i})`);return}let c=await T.mkdtemp(E.join(De.tmpdir(),"hbx-template-")),a=E.join(c,"repo");try{await execa("git",["clone","--depth","1",lt,a],{stdio:"ignore"});let l=await Gr(a,e);s?(await $(E.dirname(s)),await T.rm(s,{recursive:!0,force:!0}),await T.cp(l,s,{recursive:!0}),await _t(E.join(te,e.id)),await q(s,t),n.debug(`Hydrated template ${e.id} from fresh cache (${i})`)):(await q(l,t),n.debug(`Hydrated template ${e.id} from repository`));}catch(l){throw new d("Failed to clone HugoBlox templates. Ensure git is installed and accessible.",{error:l})}finally{await T.rm(c,{recursive:true,force:true});}}async function zr(r,e,t){let n=E.join(e,r.id);try{await T.access(n);}catch(o){throw new d(`Local template not found at ${n}`,{error:o})}await q(n,t);}async function Gr(r,e){let t=e.repoPath??"",n=E.join(r,t);return await T.access(n),n}async function q(r,e){await T.rm(e,{recursive:true,force:true}),await $(E.dirname(e)),await T.cp(r,e,{recursive:true}),await T.rm(E.join(e,".git"),{recursive:true,force:true});}async function _t(r){if(!await v(r))return;let e=await T.readdir(r,{withFileTypes:true}),t=await Promise.all(e.filter(n=>n.isDirectory()).map(async n=>{let o=E.join(r,n.name),i=await T.stat(o);return {path:o,mtimeMs:i.mtimeMs}}));t.sort((n,o)=>o.mtimeMs-n.mtimeMs);for(let n of t.slice(Wr))await T.rm(n.path,{recursive:true,force:true});}async function Xr(r,e){let t=encodeURIComponent(r.repoPath??""),n=`${re}/repos/HugoBlox/hugo-blox-builder/commits?path=${t}&per_page=1`;try{let o=await fetch(n,{headers:{"User-Agent":"hbx-cli"}});if(!o.ok)throw new Error(`GitHub responded with status ${o.status}`);return (await o.json())[0]?.sha}catch(o){e.debug(`Unable to determine latest template commit for ${r.id}: ${String(o)}`);return}}async function Jr(r,e,t,n){let o=e.version?E.join(te,e.id,e.version):void 0;if(o&&await v(o)){await q(o,t),n.debug(`Hydrated Pro template ${e.id} from cache (${e.version})`);return}let i=await T.mkdtemp(E.join(De.tmpdir(),"hbx-pro-template-")),s=E.join(i,"template.tgz"),c=E.join(i,"extract");await $(c);try{let a=await qr(r,e,s);await execa("tar",["-xzf",s,"-C",c]);let l=await Kr(c,e.slug),m=e.version??a.version??void 0,f=m!==void 0?E.join(te,e.id,m):void 0;f?(await $(E.dirname(f)),await T.rm(f,{recursive:!0,force:!0}),await T.cp(l,f,{recursive:!0}),await _t(E.join(te,e.id)),await q(f,t),n.debug(`Hydrated Pro template ${e.id} from fresh cache (${m})`)):(await q(l,t),n.debug(`Hydrated Pro template ${e.id} from archive`));}catch(a){throw new d(`Failed to download Pro template ${e.name}. Ensure your license is valid and try again.`,{error:a})}finally{await T.rm(i,{recursive:true,force:true});}}async function qr(r,e,t){let n=e.downloadEndpoint??`/api/pro-templates/${encodeURIComponent(e.id)}/download`,o=await Xe(r,n),i=await Yr(o);if(!i?.url)throw new d("Pro template download URL was missing");let s=await fetch(i.url);if(!s.ok)throw new d(`Pro template archive download failed (${s.status})`);let c=Buffer.from(await s.arrayBuffer());return await T.writeFile(t,c),{version:i.version}}async function Kr(r,e){let t=await T.readdir(r,{withFileTypes:true}),n=t.find(i=>i.isDirectory()&&i.name===e);if(n)return E.join(r,n.name);let o=t.filter(i=>i.isDirectory());return o.length===1?E.join(r,o[0].name):r}async function Yr(r){try{return await r.json()}catch{return}}var Ht="https://hugoblox.com/discord",jt="https://docs.hugoblox.com",Rt="https://hugoblox.com/pro";function z(r){return r.trim().toLowerCase().replace(/[^a-z0-9-]/g,"-").replace(/-{2,}/g,"-").replace(/^-+|-+$/g,"").slice(0,64)}async function ke(r,e,t,n){let{logger:o}=r;await $t(r,t,e,o),await Zr(e,t,n),n.sampleContent||await tn(e,t);}async function Ee(r,e,t){t.info(`\u{1F4E6} Installing dependencies with ${e}...`);let n={...process.env},o=["install"],i=!t.isJsonMode();e==="pnpm"&&(n.PNPM_WORKSPACE_DIR=r,n.PNPM_IGNORE_WORKSPACE_ROOT_CHECK="1",await Qr(r),o.push("--no-link-workspace-packages"));try{return await execa(e,o,{cwd:r,stdio:i?"ignore":"inherit",env:n}),t.success("\u2705 Dependencies installed"),!0}catch(s){return i?t.warn(`\u26A0\uFE0F Failed to install dependencies automatically. Run ${e} install inside ${r}`):t.warn(`\u26A0\uFE0F Failed to install dependencies automatically: ${String(s)}`),false}}async function Qr(r){let e=E.join(r,"pnpm-workspace.yaml");if(await v(e))return;await j(e,`packages:
24
24
  - './*'
25
- `);}async function xt(t,e,o){o.info("\u{1F525} Starting dev server with Hugo (press Ctrl+C to exit)...");let r={...process.env};e==="pnpm"&&(r.PNPM_WORKSPACE_DIR=t);try{await execa(e,["dev"],{cwd:t,stdio:"inherit",env:r}),o.success("\u2705 Dev server stopped");}catch(i){let n=i;if(n.signal==="SIGINT"||n.signal==="SIGTERM"){o.info("\u2705 Dev server stopped");return}if(i?.code==="ENOENT"||/[hH]ugo/.test(String(n.stderr))){o.error("Hugo executable not found. Install Hugo (https://gohugo.io/getting-started/installing/) or deploy with the Copilot at https://hugoblox.com/templates/");return}throw o.error(`Dev server exited with an error: ${String(i)}`),i}}async function Bo(t,e,o){let r=re(E.basename(t))||e.slug;await Ao(t,r);let i={"README.md":`# ${e.name}
25
+ `);}async function Lt(r,e,t){t.info("\u{1F525} Starting dev server with Hugo (press Ctrl+C to exit)...");let n={...process.env};e==="pnpm"&&(n.PNPM_WORKSPACE_DIR=r);try{await execa(e,["dev"],{cwd:r,stdio:"inherit",env:n}),t.success("\u2705 Dev server stopped");}catch(o){let i=o;if(i.signal==="SIGINT"||i.signal==="SIGTERM"){t.info("\u2705 Dev server stopped");return}if(o?.code==="ENOENT"||/[hH]ugo/.test(String(i.stderr))){t.error("Hugo executable not found. Install Hugo (https://docs.hugoblox.com/start/cli) or deploy with the Copilot at https://hugoblox.com/templates/");return}throw t.error(`Dev server exited with an error: ${String(o)}`),o}}async function Zr(r,e,t){let n=z(E.basename(r))||e.slug;await en(r,n);let o={"README.md":`# ${e.name}
26
26
 
27
27
  This site was created with the HugoBlox CLI.
28
28
  `,".gitignore":`node_modules
@@ -31,10 +31,10 @@ This site was created with the HugoBlox CLI.
31
31
  public
32
32
  .DS_Store
33
33
  modules/_vendor
34
- `};(i[".env"]=`# HBX environment variables
34
+ `};t.includeEnv&&(o[".env"]=`# HBX environment variables
35
35
  HBX_LICENSE_TOKEN=
36
- `);for(let[n,s]of Object.entries(i)){let a=E.join(t,n);await v(a)||await $(a,s);}}async function Ao(t,e){let o=E.join(t,"package.json"),r={name:e,private:true,scripts:{dev:"hugo server -D",build:"hugo --gc --minify",lint:"echo 'TODO: add lint script'"},dependencies:{}};if(!await v(o)){await $(o,JSON.stringify(r,null,2));return}try{let i=await P.readFile(o,"utf8"),n=JSON.parse(i),s={...r,...n,name:e,scripts:{...r.scripts,...n.scripts},dependencies:{...r.dependencies,...n.dependencies}};await $(o,JSON.stringify(s,null,2));}catch{await $(o,JSON.stringify(r,null,2));}}async function Lo(t,e){let o=E.join(t,"content");await P.rm(o,{recursive:true,force:true}),await H(E.dirname(E.join(o,"_index.md"))),await $(E.join(o,"_index.md"),Ro(e));}function Ro(t){return `---
37
- title: "${t.name}"
36
+ `);for(let[i,s]of Object.entries(o)){let c=E.join(r,i);await v(c)||await j(c,s);}}async function en(r,e){let t=E.join(r,"package.json"),n={name:e,private:true,scripts:{dev:"hugo server -D",build:"hugo --gc --minify",lint:"echo 'TODO: add lint script'"},dependencies:{}};if(!await v(t)){await j(t,JSON.stringify(n,null,2));return}try{let o=await T.readFile(t,"utf8"),i=JSON.parse(o),s={...n,...i,name:e,scripts:{...n.scripts,...i.scripts},dependencies:{...n.dependencies,...i.dependencies}};await j(t,JSON.stringify(s,null,2));}catch{await j(t,JSON.stringify(n,null,2));}}async function tn(r,e){let t=E.join(r,"content");await T.rm(t,{recursive:true,force:true}),await $(E.dirname(E.join(t,"_index.md"))),await j(E.join(t,"_index.md"),rn(e));}function rn(r){return `---
37
+ title: "${r.name}"
38
38
  type: landing
39
39
  design:
40
40
  spacing: "5rem"
@@ -43,18 +43,18 @@ sections:
43
43
  - block: hero
44
44
  content:
45
45
  eyebrow: "You're live on HugoBlox"
46
- title: "Great job launching ${t.name}"
46
+ title: "Great job launching ${r.name}"
47
47
  text: "Start customizing blocks to launch your next experience."
48
48
  primary_action:
49
49
  text: "Join our Discord"
50
- url: "${ft}"
50
+ url: "${Ht}"
51
51
  icon: "chat-bubble-left-right"
52
52
  secondary_action:
53
53
  text: "Upgrade to Pro"
54
- url: "${ht}"
54
+ url: "${Rt}"
55
55
  tertiary_action:
56
56
  text: "View Docs"
57
- url: "${gt}"
57
+ url: "${jt}"
58
58
  design:
59
59
  css_class: "bg-gradient-to-r from-blue-600 to-indigo-600 text-white"
60
60
  spacing:
@@ -63,34 +63,47 @@ sections:
63
63
  content:
64
64
  title: "Your next steps"
65
65
  text: |
66
- 1. Join the community on [Discord](${ft}) for support and inspiration.
67
- 2. Explore the [documentation](${gt}) to learn how to add blocks, deploy, and automate.
68
- 3. [Upgrade to Pro](${ht}) to unlock premium blocks, design systems, and automation-ready packages.
66
+ 1. Join the community on [Discord](${Ht}) for support and inspiration.
67
+ 2. Explore the [documentation](${jt}) to learn how to add blocks, deploy, and automate.
68
+ 3. [Upgrade to Pro](${Rt}) to unlock premium blocks, design systems, and automation-ready packages.
69
69
  4. Share feedback and showcase your build to help the ecosystem grow.
70
- `}function _e(t){return t?t.length<=6?"***":`${t.slice(0,3)}***${t.slice(-2)}`:"(none)"}function Ct(t,e){let o=t??e;return E.resolve(process.cwd(),o)}function No(t,e){if(!e)return;let o=e.toLowerCase(),r=t.find(i=>{let n=i.id.toLowerCase(),s=i.slug?.toLowerCase();return n===o||s===o});if(r)return r;if(e.startsWith("http")||e.startsWith("git@"))return {id:e,name:"Custom template",description:`External template: ${e}`,repoPath:e,slug:"custom",source:"custom"}}function Tt(t,e){t.command("site").description("Create a new HugoBlox site").argument("[dir]","Target directory (defaults to template slug, e.g., new-site)").option("--template <id>","Starter template ID").option("--dir <path>","Target directory").option("--package-manager <name>","Package manager","pnpm").option("--no-install","Skip dependency installation").option("--git","Initialize a git repository").option("--ci","Run in non-interactive mode").option("--sample-content","Force include sample content").option("--no-sample-content","Skip sample content").option("--pro","Mark the project as Pro ready").option("--preview","Automatically start the dev server after setup").option("--no-preview","Skip the dev server prompt").action(async(o,r,i)=>{let{logger:n,config:s,telemetry:a}=e,l=!!i.optsWithGlobals().json,u=Te({text:"Fetching templates...",spinner:"dots"}).start(),g=await ct(e).finally(()=>{u.succeed("\u2728 Templates ready");}),m=No(g,r.template);if(!m){if(r.ci)throw new d("Template must be specified when running with --ci");let b=await te("Select a starter",g.map(C=>({title:C.name,description:C.description,value:C.id})));m=g.find(C=>C.id===b);}if(!m)throw new d("Unable to determine template");let f=m.source==="pro",y=await Mo(r,o,m),p=Ct(y,m.slug);await Ne(p);let h=typeof r.sampleContent=="boolean"?r.sampleContent:typeof r.noSampleContent=="boolean"?!r.noSampleContent:r.ci?false:await _("Include sample content? (recommended)",{initial:true}),I=f;if(!f)I=typeof r.pro=="boolean"?r.pro:r.ci?false:await _("Do you have a HugoBlox Pro license?",{initial:!!s.getLicense()});else if(!s.getLicense()){if(r.ci)throw new d(`"${m.name}" is a HugoBlox Pro template. Set HBX_LICENSE_TOKEN with your license key before running in CI.`);if(n.info(`"${m.name}" is part of HugoBlox Pro. Purchase a license at https://hugoblox.com/pro to receive your key via email (powered by Lemon Squeezy).`),!await _("Do you have a HugoBlox Pro license key to enter now?",{initial:true}))throw new d("Pro templates require an active license. Purchase at https://hugoblox.com/pro and rerun this command after running 'hbx login' or setting HBX_LICENSE_TOKEN.");let C=await Pt(s.getLicense());s.setLicense(C),await s.save();}if(!f&&I&&!s.getLicense()){if(r.ci)throw new d("Set HBX_LICENSE_TOKEN before running --ci when enabling Pro features.");let b=await Pt(s.getLicense());s.setLicense(b),await s.save();}I&&await B(e);let x=Te(`Creating site in ${E.relative(process.cwd(),p)||"."}...`).start();try{await bt(e,p,m,{sampleContent:h,includeEnv:!0,packageManager:r.packageManager??"pnpm"}),x.succeed("Files generated");}catch(b){throw x.fail("Failed to scaffold site"),b}let k=false;r.install!==false?k=await vt(p,r.packageManager??"pnpm",n):n.info("Skipping dependency installation"),r.git&&await lt(p,n);let S={status:"success",template:m.id,path:p,pro:I,manifest:E.join(p,de)};!k&&r.preview?n.warn("\u26A0\uFE0F Cannot start the dev server because dependencies failed to install."):r.install===false&&r.preview&&n.warn("\u26A0\uFE0F Install dependencies first (skip --no-install) to start the dev server automatically.");let w=false;if(k&&(w=await Oo(r,l)),await a.track({name:"new-site",payload:{template:m.id,sample:h,pro:I,ci:!!r.ci,packageManager:r.packageManager??"pnpm",install:r.install!==false,dependenciesReady:k,preview:w}}),w){await xt(p,r.packageManager??"pnpm",n);return}if(l){n.json(S);return}n.success(`\u2705 Site created at ${p}`),n.info("\u{1F680} Next steps:"),n.info(` \u{1F449} cd ${E.relative(process.cwd(),p)||p}`),r.install===false&&n.info(` \u{1F449} ${r.packageManager??"pnpm"} install`),n.info(" \u{1F449} pnpm dev");});}async function Oo(t,e){return e||t.ci?false:typeof t.preview=="boolean"?t.preview:t.noPreview?false:_("Start the dev server now?",{initial:true})}async function Mo(t,e,o){if(t.dir)return t.dir;if(e)return e;if(t.ci)return o.slug;let i=(await j("Project folder name",{initial:o.slug})).trim();return i?Fo(i)?i:re(i)||o.slug:o.slug}function Fo(t){return /[\\/]/.test(t)||t.includes(":")}async function Pt(t){let o=(await j("Enter your HugoBlox Pro license key (from your purchase confirmation email)",{initial:t??""})).trim();if(!o)throw new d("A HugoBlox Pro license key is required to continue. Check your purchase email or visit https://hugoblox.com/pro.");return o}var Xo=fileURLToPath(import.meta.url),Be=E.dirname(Xo);function Wo(){return Be.endsWith("dist")?E.join(Be,"lib","templates","minimal-theme.yaml"):E.resolve(Be,"..","lib","templates","minimal-theme.yaml")}var kt=Wo();function zo(t){return /^[a-zA-Z0-9_-]+$/.test(t)}async function Jo(){try{return await P.readFile(kt,"utf8")}catch(t){throw new d(`Failed to load theme template: ${kt}`,{error:t})}}function qo(t,e,o,r){let i=t.split(`
71
- `),n=[],s=false,a=0;for(let c of i){if(c.trim()==="meta:"){s=true,a=c.indexOf("meta:"),n.push(c),n.push(' format: "hugoblox-theme@1"'),n.push(` name: "${o}"`),n.push(` vendor: "${e}"`),n.push(' version: "0.0.1"'),n.push(' license: "MIT"'),n.push(' description: "Created via HugoBlox CLI"');continue}if(s){c.trim()&&!c.startsWith(" ".repeat(a+2))&&c.indexOf(":")>0&&(s=false,n.push(c));continue}n.push(c);}return n.join(`
72
- `)}function Ht(t,e){t.command("theme").description("Scaffold a new theme pack").argument("[name]","Theme slug/filename (will be converted to kebab-case)").option("--vendor <namespace>","Vendor namespace (e.g., GitHub username)").option("--theme-name <name>","Human-readable theme name (defaults to slug)").option("--git","Initialize a git repository").option("--ci","Run in non-interactive mode").action(async(o,r,i)=>{let{logger:n,telemetry:s}=e,a=!!i.optsWithGlobals().json;try{let c=r.vendor;if(!c){if(r.ci)throw new d("--vendor is required when running with --ci");c=await j("What is your GitHub username? (This will be your vendor namespace)");}if(c=c.trim(),!c)throw new d("Vendor namespace is required");if(!zo(c))throw new d("Vendor namespace must contain only alphanumeric characters, hyphens, and underscores");let l=o;if(!l){if(r.ci)throw new d("Theme slug is required when running with --ci");l=await j("What do you want to name your theme? (e.g., cyberpunk-neon)",{initial:"my-theme"});}if(l=l.trim(),!l)throw new d("Theme slug is required");let u=re(l),g=r.themeName;g||(r.ci?g=l:g=await j("Human-readable theme name?",{initial:l})),g=g.trim()||l;let T=E.join(process.cwd(),"data","themes",c),m=E.join(T,`${u}.yaml`);if(await v(m)){if(r.ci)throw new d(`Theme file already exists: ${m}`);if(!await _(`Theme file ${u}.yaml already exists. Overwrite?`,{initial:!1})){n.info("Theme creation cancelled");return}}let f=Te("Creating theme...").start();try{await H(T);let p=await Jo(),h=qo(p,c,g,u);await $(m,h),f.succeed("\u2705 Theme created successfully!");}catch(p){throw f.fail("Failed to create theme"),p}await s.track({name:"create-theme",payload:{vendor:c,themeFilename:u,ci:!!r.ci}});let y=E.relative(process.cwd(),m);if(a){n.json({status:"success",path:m,vendor:c,themeFilename:u,themeName:g});return}n.success(`
73
- Theme created at: ${y}`),n.info(`
74
- \u{1F4DD} To use this theme, update your params.yaml:`),n.info(`
75
- hugoblox:`),n.info(" theme:"),n.info(` pack: "${c}/${u}"`),r.ci||await _(`
76
- \u{1F30D} Would you like to share this theme with the community?`,{initial:!1})&&(n.info(`
77
- \u2728 Great! Here's how to publish your theme:`),n.info(`
78
- 1\uFE0F\u20E3 Create a new GitHub repository`),n.info("2\uFE0F\u20E3 In your repo, create a folder structure: hugoblox/themes/"),n.info(`3\uFE0F\u20E3 Copy your theme file to: hugoblox/themes/${u}.yaml`),n.info("4\uFE0F\u20E3 Commit and push to GitHub"),n.info(`
70
+ `}function Je(r){return r?r.length<=6?"***":`${r.slice(0,3)}***${r.slice(-2)}`:"(none)"}function Te(r,e){let t=r??e;return E.resolve(process.cwd(),t)}async function Nt(r=process.cwd()){let e=E.join(r,"pnpm-lock.yaml"),t=E.join(r,"yarn.lock"),n=E.join(r,"package-lock.json");return F.existsSync(e)?"pnpm":F.existsSync(t)?"yarn":F.existsSync(n)?"npm":"pnpm"}async function Ae(r){try{let e=E.resolve(r);if(r.includes("\0")||/[<>:"|?*]/.test(r))return {valid:!1,exists:!1,isEmpty:!1,writable:!1,error:"Path contains invalid characters"};let t=!1,n=!1,o=!1;try{let i=F.statSync(e);if(t=!0,i.isDirectory()){n=F.readdirSync(e).length===0;try{F.accessSync(e,F.constants.W_OK),o=!0;}catch{o=!1;}}else return {valid:!1,exists:!0,isEmpty:!1,writable:!1,error:"Path exists but is not a directory"}}catch{let s=E.dirname(e);try{F.accessSync(s,F.constants.W_OK),o=!0;}catch{return {valid:!1,exists:!1,isEmpty:!1,writable:!1,error:"Parent directory is not writable"}}}return {valid:!0,exists:t,isEmpty:n,writable:o}}catch(e){return {valid:false,exists:false,isEmpty:false,writable:false,error:e instanceof Error?e.message:"Unknown error"}}}function nn(r,e){if(!e)return;let t=e.toLowerCase(),n=r.find(o=>{let i=o.id.toLowerCase(),s=o.slug?.toLowerCase();return i===t||s===t});if(n)return n;if(e.startsWith("http")||e.startsWith("git@"))return {id:e,name:"Custom template",description:`External template: ${e}`,repoPath:e,slug:"custom",source:"custom"}}function Ft(r,e){r.command("site").description("Create a new HugoBlox site").argument("[dir]","Target directory (defaults to template slug, e.g., new-site)").option("--template <id>","Starter template ID").option("--dir <path>","Target directory").option("--package-manager <name>","Package manager","pnpm").option("--no-install","Skip dependency installation").option("--git","Initialize a git repository").option("--ci","Run in non-interactive mode").option("--sample-content","Force include sample content").option("--no-sample-content","Skip sample content").option("--pro","Mark the project as Pro ready").option("--preview","Automatically start the dev server after setup").option("--no-preview","Skip the dev server prompt").action(async(t,n,o)=>{let{logger:i,config:s,telemetry:c}=e,l=!!o.optsWithGlobals().json,m=Ue({text:"Fetching templates...",spinner:"dots"}).start(),f=await V(e).finally(()=>{m.succeed("\u2728 Templates ready");}),u=nn(f,n.template);if(!u){if(n.ci)throw new d("Template must be specified when running with --ci");let k=await de("Select a starter",f.map(A=>({title:A.name,description:A.description,value:A.id})));u=f.find(A=>A.id===k);}if(!u)throw new d("Unable to determine template");let p=u.source==="pro",h=await sn(n,t,u),g=Te(h,u.slug);await we(g);let w=typeof n.sampleContent=="boolean"?n.sampleContent:typeof n.noSampleContent=="boolean"?!n.noSampleContent:n.ci?false:await R("Include sample content? (recommended)",{initial:true}),P=p;if(!p)P=typeof n.pro=="boolean"?n.pro:n.ci?false:await R("Do you have a HugoBlox Pro license?",{initial:!!s.getLicense()});else if(!s.getLicense()){if(n.ci)throw new d(`"${u.name}" is a HugoBlox Pro template. Set HBX_LICENSE_TOKEN with your license key before running in CI.`);if(i.info(`"${u.name}" is part of HugoBlox Pro. Purchase a license at https://hugoblox.com/pro to receive your key via email (powered by Lemon Squeezy).`),!await R("Do you have a HugoBlox Pro license key to enter now?",{initial:true}))throw new d("Pro templates require an active license. Purchase at https://hugoblox.com/pro and rerun this command after running 'hbx login' or setting HBX_LICENSE_TOKEN.");let A=await Mt(s.getLicense());s.setLicense(A),await s.save();}if(!p&&P&&!s.getLicense()){if(n.ci)throw new d("Set HBX_LICENSE_TOKEN before running --ci when enabling Pro features.");let k=await Mt(s.getLicense());s.setLicense(k),await s.save();}P&&await L(e);let x=Ue(`Creating site in ${E.relative(process.cwd(),g)||"."}...`).start();try{await ke(e,g,u,{sampleContent:w,includeEnv:!0,packageManager:n.packageManager??"pnpm"}),x.succeed("Files generated");}catch(k){throw x.fail("Failed to scaffold site"),k}let C=false;n.install!==false?C=await Ee(g,n.packageManager??"pnpm",i):i.info("Skipping dependency installation"),n.git&&await Se(g,i);let H={status:"success",template:u.id,path:g,pro:P,manifest:E.join(g,Pe)};!C&&n.preview?i.warn("\u26A0\uFE0F Cannot start the dev server because dependencies failed to install."):n.install===false&&n.preview&&i.warn("\u26A0\uFE0F Install dependencies first (skip --no-install) to start the dev server automatically.");let S=false;if(C&&(S=await on(n,l)),await c.track({name:"new-site",payload:{template:u.id,sample:w,pro:P,ci:!!n.ci,packageManager:n.packageManager??"pnpm",install:n.install!==false,dependenciesReady:C,preview:S}}),S){await Lt(g,n.packageManager??"pnpm",i);return}if(l){i.json(H);return}i.success(`\u2705 Site created at ${g}`),i.info("\u{1F680} Next steps:"),i.info(` \u{1F449} cd ${E.relative(process.cwd(),g)||g}`),n.install===false&&i.info(` \u{1F449} ${n.packageManager??"pnpm"} install`),i.info(" \u{1F449} pnpm dev");});}async function on(r,e){return e||r.ci?false:typeof r.preview=="boolean"?r.preview:r.noPreview?false:R("Start the dev server now?",{initial:true})}async function sn(r,e,t){if(r.dir)return r.dir;if(e)return e;if(r.ci)return t.slug;let o=(await B("Project folder name",{initial:t.slug})).trim();return o?an(o)?o:z(o)||t.slug:t.slug}function an(r){return /[\\/]/.test(r)||r.includes(":")}async function Mt(r){let t=(await B("Enter your HugoBlox Pro license key (from your purchase confirmation email)",{initial:r??""})).trim();if(!t)throw new d("A HugoBlox Pro license key is required to continue. Check your purchase email or visit https://hugoblox.com/pro.");return t}var un=fileURLToPath(import.meta.url),Ke=E.dirname(un);function dn(){return Ke.endsWith("dist")?E.join(Ke,"lib","templates","minimal-theme.yaml"):E.resolve(Ke,"..","lib","templates","minimal-theme.yaml")}var Ut=dn();function pn(r){return /^[a-zA-Z0-9_-]+$/.test(r)}async function fn(){try{return await T.readFile(Ut,"utf8")}catch(r){throw new d(`Failed to load theme template: ${Ut}`,{error:r})}}function gn(r,e,t,n){let o=r.split(`
71
+ `),i=[],s=false,c=0;for(let a of o){if(a.trim()==="meta:"){s=true,c=a.indexOf("meta:"),i.push(a),i.push(' format: "hugoblox-theme@1"'),i.push(` name: "${t}"`),i.push(` vendor: "${e}"`),i.push(' version: "0.0.1"'),i.push(' license: "MIT"'),i.push(' description: "Created via HugoBlox CLI"');continue}if(s){a.trim()&&!a.startsWith(" ".repeat(c+2))&&a.indexOf(":")>0&&(s=false,i.push(a));continue}i.push(a);}return i.join(`
72
+ `)}function Vt(r,e){r.command("theme").description("Scaffold a new theme pack").argument("[name]","Theme slug/filename (will be converted to kebab-case)").option("--vendor <namespace>","Vendor namespace (e.g., GitHub username)").option("--theme-name <name>","Human-readable theme name (defaults to slug)").option("--git","Initialize a git repository").option("--ci","Run in non-interactive mode").action(async(t,n,o)=>{let{logger:i,telemetry:s}=e,c=!!o.optsWithGlobals().json;try{let a=n.vendor;if(!a){if(n.ci)throw new d("--vendor is required when running with --ci");a=await B("What is your GitHub username? (This will be your vendor namespace)");}if(a=a.trim(),!a)throw new d("Vendor namespace is required");if(!pn(a))throw new d("Vendor namespace must contain only alphanumeric characters, hyphens, and underscores");let l=t;if(!l){if(n.ci)throw new d("Theme slug is required when running with --ci");l=await B("What do you want to name your theme? (e.g., cyberpunk-neon)",{initial:"my-theme"});}if(l=l.trim(),!l)throw new d("Theme slug is required");let m=z(l),f=n.themeName;f||(n.ci?f=l:f=await B("Human-readable theme name?",{initial:l})),f=f.trim()||l;let b=E.join(process.cwd(),"data","themes",a),u=E.join(b,`${m}.yaml`);if(await v(u)){if(n.ci)throw new d(`Theme file already exists: ${u}`);if(!await R(`Theme file ${m}.yaml already exists. Overwrite?`,{initial:!1})){i.info("Theme creation cancelled");return}}let p=Ue("Creating theme...").start();try{await $(b);let g=await fn(),w=gn(g,a,f,m);await j(u,w),p.succeed("\u2705 Theme created successfully!");}catch(g){throw p.fail("Failed to create theme"),g}await s.track({name:"create-theme",payload:{vendor:a,themeFilename:m,ci:!!n.ci}});let h=E.relative(process.cwd(),u);if(c){i.json({status:"success",path:u,vendor:a,themeFilename:m,themeName:f});return}i.success(`
73
+ Theme created at: ${h}`),i.info(`
74
+ \u{1F4DD} To use this theme, update your params.yaml:`),i.info(`
75
+ hugoblox:`),i.info(" theme:"),i.info(` pack: "${a}/${m}"`),n.ci||await R(`
76
+ \u{1F30D} Would you like to share this theme with the community?`,{initial:!1})&&(i.info(`
77
+ \u2728 Great! Here's how to publish your theme:`),i.info(`
78
+ 1\uFE0F\u20E3 Create a new GitHub repository`),i.info("2\uFE0F\u20E3 In your repo, create a folder structure: hugoblox/themes/"),i.info(`3\uFE0F\u20E3 Copy your theme file to: hugoblox/themes/${m}.yaml`),i.info("4\uFE0F\u20E3 Commit and push to GitHub"),i.info(`
79
79
  \u{1F4E2} Once published, others can install it with:
80
- hbx add theme --repo https://github.com/${c}/your-repo-name
81
- `),n.info("\u{1F4A1} Tip: You can add multiple themes to the same repository!"));}catch(c){if(a){n.json({status:"error",message:c instanceof Error?c.message:String(c)});return}throw c}});}function St(t,e){let o=t.command("create").description("Create resources (site, theme, block)");Tt(o,e),Ht(o,e),rt(o,e);}function It(t,e){Yo(t,e),Qo(t,e);}function Yo(t,e){t.command("dev [hugoArgs...]").description("Start the Hugo development server (hugo server)").option("--path <dir>","Project directory (defaults to cwd)").option("--no-default-flags","Disable automatic --disableFastRender flag").action(async(o,r,i)=>{let{logger:n,telemetry:s}=e,c=!!i.optsWithGlobals().json,l=await _t(r.path),u=["server"];r.defaultFlags!==false&&u.push("--disableFastRender"),u.push(...o),await jt(u,l,n,{allowSignalExit:true,label:"dev server"}),await s.track({name:"dev",payload:{customPath:!!r.path,defaultFlags:r.defaultFlags!==false,args:o}}),c&&n.json({status:"success"});});}function Qo(t,e){t.command("build [hugoArgs...]").description("Run a production Hugo build (hugo --gc --minify)").option("--path <dir>","Project directory (defaults to cwd)").option("--no-default-flags","Disable automatic --gc/--minify flags").action(async(o,r,i)=>{let{logger:n,telemetry:s}=e,c=!!i.optsWithGlobals().json,l=await _t(r.path),u=[];r.defaultFlags!==false&&u.push("--gc","--minify"),u.push(...o),await jt(u,l,n,{allowSignalExit:false,label:"build"}),await s.track({name:"build",payload:{customPath:!!r.path,defaultFlags:r.defaultFlags!==false,args:o}}),c&&n.json({status:"success"});});}async function _t(t){let e=t?E.resolve(t):E.resolve(process.cwd()),o=await A(e);if(!o)throw new d("Unable to locate a HugoBlox project. Run this inside a project or pass --path.");return o.root}async function jt(t,e,o,r){o.info(`Running: hugo ${t.join(" ")}`);try{await execa("hugo",t,{cwd:e,stdio:"inherit"}),o.success(`Hugo ${r.label} finished`);}catch(i){let n=i;if(r.allowSignalExit&&(n.signal==="SIGINT"||n.signal==="SIGTERM")){o.info("Hugo process stopped");return}throw new d(`Hugo ${r.label} failed: ${n.shortMessage??n.message}`,{error:n})}}var or=["github.com/HugoBlox/hugo-blox-builder","github.com/wowchemy/wowchemy-hugo-themes"];async function pe(t){let e=E.join(t,"go.mod"),o;try{o=await P.readFile(e,"utf8");}catch{return []}let i=ir(o).filter(s=>or.some(a=>s.path.startsWith(a)));return await Promise.all(i.map(async s=>{try{let a=await rr(s.path);return {path:s.path,current:s.version,latest:a}}catch(a){return {path:s.path,current:s.version,latest:void 0,error:a.message}}}))}async function rr(t){if(t.includes("github.com/HugoBlox/hugo-blox-builder"))return nr(t)}async function nr(t){let e=await q(`${z}/repos/HugoBlox/hugo-blox-builder/tags?per_page=100`);if(!e.ok)throw new Error(`GitHub API error: ${e.status}`);let o=await e.json(),r=t.split("github.com/HugoBlox/hugo-blox-builder/");if(r.length<2)return;let i=r[1],n=o.filter(s=>s.name.startsWith(`${i}/`)&&/v\d+\.\d+\.\d+/.test(s.name)).map(s=>s.name);if(n.length!==0)return n.sort((s,a)=>{let c=s.split("/").pop()||"0.0.0",l=a.split("/").pop()||"0.0.0";return L.rcompare(c,l)}),n[0].split("/").pop()}async function Bt(t,e){if(!t.includes("github.com/HugoBlox/hugo-blox-builder"))return;let o=t.split("github.com/HugoBlox/hugo-blox-builder/");if(o.length<2)return;let r=o[1],i=e==="main"||e.startsWith("v")?e:`v${e}`,n=e==="main"?"main":`${r}/${i.replace(/^v/,"v")}`,s=`${r}/hugo.yaml`,a=await q(`${z}/repos/HugoBlox/hugo-blox-builder/contents/${s}?ref=${n}`,{headers:{Accept:"application/vnd.github.v3.raw"}});if(!a.ok)return;let c=await a.text();return Ze.parse(c)?.module?.hugoVersion?.min}function ir(t){let e=[],o=t.split(/\r?\n/),r=false;for(let i of o){let n=i.trim();if(n.startsWith("require (")){r=true;continue}if(r&&n===")"){r=false;continue}let s="";if(!r&&n.startsWith("require ")?s=n.replace("require","").trim():r&&(s=n),s){s=s.split("//")[0].trim();let a=s.split(/\s+/);a.length>=2&&e.push({path:a[0],version:a[1]});}}return e}function fe(t,e){if(!t||!e)return {updateAvailable:false,diff:""};let o=L.coerce(t),r=L.coerce(e);return !o||!r?{updateAvailable:false,diff:""}:L.lt(o,r)?{updateAvailable:true,diff:`${Y(t)} -> ${Y(e)}`}:{updateAvailable:false,diff:""}}function Y(t){let e=/v\d+\.\d+\.\d+-(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})-([a-f0-9]{12})/,o=t.match(e);if(o){let[r,i,n,s,a,c,l,u]=o;return `commit ${u.substring(0,7)} (released ${i}-${n}-${s})`}return t}function Lt(t){let e=process.env.HOME||process.env.USERPROFILE;if(!e)return t;let o=E.resolve(e),r=E.resolve(t);return r.startsWith(o)?r.replace(o,"~"):t}async function Ae(t,e){try{let o=await execa(t,e,{reject:!1});return o.exitCode===0?o.stdout.trim():`Failed (${o.stderr.trim()})`}catch(o){return `Not available (${o.message})`}}function Rt(t,e){t.command("doctor").description("Check local environment for common issues").action(async(o,r)=>{let{logger:i,config:n}=e,a=!!r.optsWithGlobals().json,c=[];c.push({name:"Node.js",ok:true,details:process.version});let l=await Ae("git",["--version"]);c.push({name:"Git",ok:!l.startsWith("Not"),details:l});let u=await Ae("hugo",["version"]);c.push({name:"Hugo",ok:!u.startsWith("Not"),details:u});let g=ar(u),T=await Ae("pnpm",["--version"]);c.push({name:"pnpm",ok:!T.startsWith("Not"),details:T});let m=n.getLicense();c.push({name:"Pro license",ok:!!m,details:m?"Configured":"Missing"});let f=await A();if(f){let h=await Je(f.root);if(h){let x=h.templateName??h.templateId??"Unknown";c.push({name:"Template",ok:true,details:`${x} (${Lt(f.root)})`});let k=cr(h.requiredHugoVersion,g);k&&c.push(k);}let I=await pe(f.root);for(let x of I){let k=x.path.split("/").pop()||x.path,S=x.current;if(x.latest){let b=L.coerce(x.current),C=L.coerce(x.latest);b&&C&&L.eq(b,C)&&(S=`${S} (latest)`);}c.push({name:`Module: ${k}`,ok:true,details:S});let{updateAvailable:w}=fe(x.current,x.latest);w&&x.latest&&c.push({name:`${k} update`,ok:false,details:`Current ${x.current}, latest ${x.latest}. Run 'hbx upgrade' to update.`,level:"warn"});}}let p={status:c.some(h=>(h.level??(h.ok?"info":"error"))==="error")?"error":"success",checks:c};if(a){i.json(p);return}for(let h of p.checks){let I=h.level??(h.ok?"info":"error");I==="info"?i.success(`${h.name}: ${h.details}`):I==="warn"?i.warn(`${h.name}: ${h.details}`):i.error(`${h.name}: ${h.details}`);}});}function ar(t){let e=t.match(/v(\d+\.\d+\.\d+)/i);return e?e[1]:t.match(/(\d+\.\d+\.\d+)/)?.[1]}function cr(t,e){if(!t||!e)return;let o=L.coerce(t),r=L.coerce(e);if(!(!o||!r))return L.lt(r,o)?{name:"Hugo version compatibility",ok:false,details:`Template requires Hugo ${o.version}, but ${r.version} is installed`,level:"error"}:L.major(r)>L.major(o)||L.minor(r)>L.minor(o)?{name:"Hugo version compatibility",ok:true,details:`Using Hugo ${r.version}; template tested with ${o.version}`,level:"warn"}:{name:"Hugo version compatibility",ok:true,details:`Template requires Hugo ${o.version} and you're running ${r.version}`,level:"info"}}function Dt(t,e){t.command("install").description("Hydrate HugoBlox Pro assets").option("--ci","Fail when prompts would appear").option("--force","Force re-download of assets").option("--path <dir>","Project directory (defaults to cwd)").action(async(o,r)=>{let{logger:i,config:n,telemetry:s}=e,c=!!r.optsWithGlobals().json,l=o.path?{root:E.resolve(o.path),manifestPath:E.join(E.resolve(o.path),"hbx.blocks.json")}:await A();if(!l)throw new d("Unable to locate a HugoBlox project - pass --path if needed");let u=n.getLicense();if(!u){if(o.ci)throw new d(`Missing license key. Set ${X} or run hbx login before using --ci.`);i.warn("No Pro license detected, proceeding with OSS assets only");}let g=Te("Hydrating Pro assets...").start();try{let m=E.join(l.root,"config","_default","module.hbx.toml"),f=E.join(l.root,"node_modules","@hugoblox-pro","sample");await H(f),await $(E.join(f,"README.md"),`# HugoBlox Pro assets placeholder
82
- `),await $(m,`# Generated by hbx install
80
+ hbx add theme --repo https://github.com/${a}/your-repo-name
81
+ `),i.info("\u{1F4A1} Tip: You can add multiple themes to the same repository!"));}catch(a){if(c){i.json({status:"error",message:a instanceof Error?a.message:String(a)});return}throw a}});}function Wt(r,e){let t=r.command("create").description("Create resources (site, theme, block)");Ft(t,e),Vt(t,e),Pt(t,e);}function Gt(r,e){yn(r,e),wn(r,e);}function yn(r,e){r.command("dev [hugoArgs...]").description("Start the Hugo development server (hugo server)").option("--path <dir>","Project directory (defaults to cwd)").option("--no-default-flags","Disable automatic --disableFastRender flag").action(async(t,n,o)=>{let{logger:i,telemetry:s}=e,a=!!o.optsWithGlobals().json,l=await Xt(n.path),m=["server"];n.defaultFlags!==false&&m.push("--disableFastRender"),m.push(...t),await Jt(m,l,i,{allowSignalExit:true,label:"dev server"}),await s.track({name:"dev",payload:{customPath:!!n.path,defaultFlags:n.defaultFlags!==false,args:t}}),a&&i.json({status:"success"});});}function wn(r,e){r.command("build [hugoArgs...]").description("Run a production Hugo build (hugo --gc --minify)").option("--path <dir>","Project directory (defaults to cwd)").option("--no-default-flags","Disable automatic --gc/--minify flags").action(async(t,n,o)=>{let{logger:i,telemetry:s}=e,a=!!o.optsWithGlobals().json,l=await Xt(n.path),m=[];n.defaultFlags!==false&&m.push("--gc","--minify"),m.push(...t),await Jt(m,l,i,{allowSignalExit:false,label:"build"}),await s.track({name:"build",payload:{customPath:!!n.path,defaultFlags:n.defaultFlags!==false,args:t}}),a&&i.json({status:"success"});});}async function Xt(r){let e=r?E.resolve(r):E.resolve(process.cwd()),t=await D(e);if(!t)throw new d("Unable to locate a HugoBlox project. Run this inside a project or pass --path.");return t.root}async function Jt(r,e,t,n){t.info(`Running: hugo ${r.join(" ")}`);try{await execa("hugo",r,{cwd:e,stdio:"inherit"}),t.success(`Hugo ${n.label} finished`);}catch(o){let i=o;if(n.allowSignalExit&&(i.signal==="SIGINT"||i.signal==="SIGTERM")){t.info("Hugo process stopped");return}throw new d(`Hugo ${n.label} failed: ${i.shortMessage??i.message}`,{error:i})}}var Cn=["github.com/HugoBlox/hugo-blox-builder","github.com/wowchemy/wowchemy-hugo-themes"];async function $e(r){let e=E.join(r,"go.mod"),t;try{t=await T.readFile(e,"utf8");}catch{return []}let o=kn(t).filter(s=>Cn.some(c=>s.path.startsWith(c)));return await Promise.all(o.map(async s=>{try{let c=await Pn(s.path);return {path:s.path,current:s.version,latest:c}}catch(c){return {path:s.path,current:s.version,latest:void 0,error:c.message}}}))}async function Pn(r){if(r.includes("github.com/HugoBlox/hugo-blox-builder"))return Sn(r)}async function Sn(r){let e=await oe(`${re}/repos/HugoBlox/hugo-blox-builder/tags?per_page=100`);if(!e.ok)throw new Error(`GitHub API error: ${e.status}`);let t=await e.json(),n=r.split("github.com/HugoBlox/hugo-blox-builder/");if(n.length<2)return;let o=n[1],i=t.filter(s=>s.name.startsWith(`${o}/`)&&/v\d+\.\d+\.\d+/.test(s.name)).map(s=>s.name);if(i.length!==0)return i.sort((s,c)=>{let a=s.split("/").pop()||"0.0.0",l=c.split("/").pop()||"0.0.0";return N.rcompare(a,l)}),i[0].split("/").pop()}async function qt(r,e){if(!r.includes("github.com/HugoBlox/hugo-blox-builder"))return;let t=r.split("github.com/HugoBlox/hugo-blox-builder/");if(t.length<2)return;let n=t[1],o=e==="main"||e.startsWith("v")?e:`v${e}`,i=e==="main"?"main":`${n}/${o.replace(/^v/,"v")}`,s=`${n}/hugo.yaml`,c=await oe(`${re}/repos/HugoBlox/hugo-blox-builder/contents/${s}?ref=${i}`,{headers:{Accept:"application/vnd.github.v3.raw"}});if(!c.ok)return;let a=await c.text();return He.parse(a)?.module?.hugoVersion?.min}function kn(r){let e=[],t=r.split(/\r?\n/),n=false;for(let o of t){let i=o.trim();if(i.startsWith("require (")){n=true;continue}if(n&&i===")"){n=false;continue}let s="";if(!n&&i.startsWith("require ")?s=i.replace("require","").trim():n&&(s=i),s){s=s.split("//")[0].trim();let c=s.split(/\s+/);c.length>=2&&e.push({path:c[0],version:c[1]});}}return e}function _e(r,e){if(!r||!e)return {updateAvailable:false,diff:""};let t=N.coerce(r),n=N.coerce(e);return !t||!n?{updateAvailable:false,diff:""}:N.lt(t,n)?{updateAvailable:true,diff:`${ae(r)} -> ${ae(e)}`}:{updateAvailable:false,diff:""}}function ae(r){let e=/v\d+\.\d+\.\d+-(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})-([a-f0-9]{12})/,t=r.match(e);if(t){let[n,o,i,s,c,a,l,m]=t;return `commit ${m.substring(0,7)} (released ${o}-${i}-${s})`}return r}function Yt(r){let e=process.env.HOME||process.env.USERPROFILE;if(!e)return r;let t=E.resolve(e),n=E.resolve(r);return n.startsWith(t)?n.replace(t,"~"):r}async function Ye(r,e){try{let t=await execa(r,e,{reject:!1});return t.exitCode===0?t.stdout.trim():`Failed (${t.stderr.trim()})`}catch(t){return `Not available (${t.message})`}}function Qt(r,e){r.command("doctor").description("Check local environment for common issues").action(async(t,n)=>{let{logger:o,config:i}=e,c=!!n.optsWithGlobals().json,a=[];a.push({name:"Node.js",ok:true,details:process.version});let l=await Ye("git",["--version"]);a.push({name:"Git",ok:!l.startsWith("Not"),details:l});let m=await Ye("hugo",["version"]);a.push({name:"Hugo",ok:!m.startsWith("Not"),details:m});let f=Tn(m),b=await Ye("pnpm",["--version"]);a.push({name:"pnpm",ok:!b.startsWith("Not"),details:b});let u=i.getLicense();a.push({name:"Pro license",ok:!!u,details:u?"Configured":"Missing"});let p=await D();if(p){let w=await ft(p.root);if(w){let x=w.templateName??w.templateId??"Unknown";a.push({name:"Template",ok:true,details:`${x} (${Yt(p.root)})`});let C=An(w.requiredHugoVersion,f);C&&a.push(C);}let P=await $e(p.root);for(let x of P){let C=x.path.split("/").pop()||x.path,H=x.current;if(x.latest){let k=N.coerce(x.current),A=N.coerce(x.latest);k&&A&&N.eq(k,A)&&(H=`${H} (latest)`);}a.push({name:`Module: ${C}`,ok:true,details:H});let{updateAvailable:S}=_e(x.current,x.latest);S&&x.latest&&a.push({name:`${C} update`,ok:false,details:`Current ${x.current}, latest ${x.latest}. Run 'hbx upgrade' to update.`,level:"warn"});}}let g={status:a.some(w=>(w.level??(w.ok?"info":"error"))==="error")?"error":"success",checks:a};if(c){o.json(g);return}for(let w of g.checks){let P=w.level??(w.ok?"info":"error");P==="info"?o.success(`${w.name}: ${w.details}`):P==="warn"?o.warn(`${w.name}: ${w.details}`):o.error(`${w.name}: ${w.details}`);}});}function Tn(r){let e=r.match(/v(\d+\.\d+\.\d+)/i);return e?e[1]:r.match(/(\d+\.\d+\.\d+)/)?.[1]}function An(r,e){if(!r||!e)return;let t=N.coerce(r),n=N.coerce(e);if(!(!t||!n))return N.lt(n,t)?{name:"Hugo version compatibility",ok:false,details:`Template requires Hugo ${t.version}, but ${n.version} is installed`,level:"error"}:N.major(n)>N.major(t)||N.minor(n)>N.minor(t)?{name:"Hugo version compatibility",ok:true,details:`Using Hugo ${n.version}; template tested with ${t.version}`,level:"warn"}:{name:"Hugo version compatibility",ok:true,details:`Template requires Hugo ${t.version} and you're running ${n.version}`,level:"info"}}function Zt(r,e){r.command("install").description("Hydrate HugoBlox Pro assets").option("--ci","Fail when prompts would appear").option("--force","Force re-download of assets").option("--path <dir>","Project directory (defaults to cwd)").action(async(t,n)=>{let{logger:o,config:i,telemetry:s}=e,a=!!n.optsWithGlobals().json,l=t.path?{root:E.resolve(t.path),manifestPath:E.join(E.resolve(t.path),"hbx.blocks.json")}:await D();if(!l)throw new d("Unable to locate a HugoBlox project - pass --path if needed");let m=i.getLicense();if(!m){if(t.ci)throw new d(`Missing license key. Set ${ee} or run hbx login before using --ci.`);o.warn("No Pro license detected, proceeding with OSS assets only");}let f=Ue("Hydrating Pro assets...").start();try{let u=E.join(l.root,"config","_default","module.hbx.toml"),p=E.join(l.root,"node_modules","@hugoblox-pro","sample");await $(p),await j(E.join(p,"README.md"),`# HugoBlox Pro assets placeholder
82
+ `),await j(u,`# Generated by hbx install
83
83
  [[module.imports]]
84
84
  path = "github.com/HugoBlox/hugo-blox-builder"
85
85
  [[module.imports]]
86
86
  path = "node_modules/@hugoblox-pro/sample"
87
- `),g.succeed("Assets hydrated");}catch(m){throw g.fail("Failed to hydrate assets"),m}await s.track({name:"install",payload:{ci:!!o.ci,force:!!o.force,tokenPresent:!!u,customPath:!!o.path}});let T={status:"success",path:l.root,forced:!!o.force,token:!!u};c?i.json(T):(i.success("Install complete"),u||i.info("Tip: run hbx login to unlock Pro downloads"));});}function Nt(t,e){t.command("login").description("Authenticate with HugoBlox Pro and register this device for template access").option("--token <value>","License key").option("--ci","Fail when license key is missing").action(async(o,r)=>{let{config:i,logger:n}=e,a=!!r.optsWithGlobals().json,c=o.token;if(!c){if(o.ci)throw new d("License key is required when using --ci");c=await j("Enter your HugoBlox Pro license key");}let l=false;try{i.setLicense(c),await i.save(),l=!0,await B(e,{force:!0});let u={status:"success",token:_e(c)};a?n.json(u):n.success(`License key saved (${_e(c)})`);}catch(u){throw l&&(i.clearLicense(),await i.save()),mr(u)}});}function Ot(t,e){t.command("logout").description("Remove stored HugoBlox credentials and device secrets").action(async(o,r)=>{let{config:i,logger:n}=e,s=!!r.optsWithGlobals().json;await at(i),i.clearLicense(),await i.save();let a={status:"success"};s?n.json(a):n.success("License key removed");});}function mr(t){let e=t instanceof Error?t.message:"Device registration failed",o="HBX must register this machine before downloading Pro templates. Check your internet connection, ensure your license has a free device slot, or revoke an old device and rerun 'hbx login'.";return t instanceof R?new d(`${e}. ${o}`,t.details):new d(`${e}. ${o}`)}function Mt(t,e){let o=t.command("telemetry").description("Manage HugoBlox telemetry preferences");o.command("enable").description("Enable anonymous telemetry").action(async()=>{e.config.setTelemetryEnabled(true),await e.config.save(),e.logger.success("Telemetry enabled. Thanks for helping improve HugoBlox!");}),o.command("disable").description("Disable telemetry collection").action(async()=>{e.config.setTelemetryEnabled(false),await e.config.save(),e.logger.success("Telemetry disabled. You can re-enable it anytime.");}),o.command("status").description("Show whether telemetry is enabled").action(()=>{let r=e.config.isTelemetryEnabled();e.logger.info(`Telemetry is ${r?"enabled":"disabled"}.`);}),o.action(()=>{let r=e.config.isTelemetryEnabled();e.logger.info(`Telemetry is ${r?"enabled":"disabled"}.`),e.logger.info("Use 'hbx telemetry enable|disable' to change the setting.");});}async function Ut(t,e,o){let r=L.coerce(e)?.version;if(!r)return;let i=[{path:"hugoblox.yaml",regex:/(hugo_version:\s*['"]?)([\d.]+)(['"]?)/,name:"hugoblox.yaml"},{path:"netlify.toml",regex:/(HUGO_VERSION\s*=\s*['"])([\d.]+)(['"])/,name:"netlify.toml"},{path:".github/workflows/deploy.yml",regex:/(WC_HUGO_VERSION:\s*['"]?)([\d.]+)(['"]?)/,name:"GitHub Actions"}];for(let n of i){let s=E.join(t,n.path);if(await v(s))try{let a=await P.readFile(s,"utf8"),c=a.match(n.regex);if(c){let l=c[2],u=L.coerce(l),g=L.coerce(r);if(u&&g&&L.lt(u,g)){let T=a.replace(n.regex,`$1${r}$3`);await P.writeFile(s,T,"utf8"),o.success(`Updated ${n.name} Hugo version to ${r}`);}}}catch{}}}function Xt(t,e){t.command("upgrade").alias("update").description("Upgrade HugoBlox modules to the latest version").option("-y, --yes","Skip confirmation").option("--ci","Run in non-interactive mode").option("--force","Force upgrade even if Hugo version is incompatible").option("--canary","Upgrade to the latest development commit (main branch) instead of stable release").action(async(o,r)=>{let{logger:i}=e;await Ye()||(i.error("\u274C You appear to be offline. Please check your internet connection."),process.exit(1));let n=await A();if(!n)throw new d("No Hugo project found in the current directory.");let s=Te("Checking for updates...").start(),a=await pe(n.root);if(a.length===0){s.stop(),i.info("\u2139\uFE0F No official HugoBlox modules found in go.mod");return}let c=a.filter(m=>m.error);if(c.length>0){s.stop(),i.error("\u274C Failed to check for updates for some modules:");for(let m of c)i.error(` ${m.path}: ${m.error}`);process.exit(1);}let l=[];if(o.canary)s.text="Checking canary versions...",l.push(...a.map(m=>({path:m.path,version:"main"})));else {let m=a.map(f=>{let{updateAvailable:y,diff:p}=fe(f.current,f.latest);return {...f,updateAvailable:y,diff:p}}).filter(f=>f.updateAvailable&&f.latest);if(m.length===0){s.stop(),i.success("\u2705 All HugoBlox modules are up to date");return}l.push(...m.map(f=>({path:f.path,version:f.latest||""})));}s.text="Checking version requirements...";let u=await Promise.all(l.map(async m=>{try{let f=await Bt(m.path,m.version);return {...m,min:f}}catch{return {...m,min:void 0}}}));if(s.stop(),!o.force){let m=await qe();if(m){let f=u.filter(y=>{if(!y.min)return false;let p=L.coerce(y.min),h=L.coerce(m);return p&&h&&L.lt(h,p)});if(f.length>0){i.error("\u274C Hugo version incompatible with upgrade targets:");for(let y of f)i.error(` ${y.path.split("/").pop()} requires Hugo >= ${y.min} (you have ${m})`);i.info("\u{1F449} Please upgrade Hugo first: https://gohugo.io/installation/"),i.info(" Or run with --force to ignore this check."),process.exit(1);}}else i.warn("\u26A0\uFE0F Could not detect local Hugo version. Skipping compatibility check.");}if(o.canary){i.warn("\u26A0\uFE0F Upgrading modules to latest canary (development) version.");for(let m of a)i.info(` ${m.path.split("/").pop()}: ${Y(m.current)} -> @main`);}else {i.info(`\u{1F4E6} Updates available for ${l.length} module(s):`);for(let m of l){let f=a.find(y=>y.path===m.path)?.current||"?";i.info(` ${m.path.split("/").pop()}`),i.info(` Current: ${Y(f)}`),i.info(` Latest: ${Y(m.version)}`);}}if(!o.yes&&!o.ci&&!await _(o.canary?"Are you sure you want to upgrade to the bleeding edge?":"Do you want to upgrade now?",{initial:true})){i.info("Upgrade cancelled");return}let g=l.map(m=>`${m.path}@${m.version}`);await ur(n.root,g,i);let T=u.reduce((m,f)=>{if(!f.min)return m;let y=L.coerce(f.min),p=L.coerce(m);return y&&(!p||L.gt(y,p))?f.min:m},void 0);T&&await Ut(n.root,T,i),i.info(""),i.warn("\u26A0\uFE0F Important: Please review the release notes for any breaking changes."),i.info("\u{1F449} https://github.com/HugoBlox/hugo-blox-builder/releases");});}async function ur(t,e,o){let r=Te("Upgrading modules...").start();try{await execa("hugo",["mod","get",...e],{cwd:t}),r.text="Tidying go.mod...",await execa("hugo",["mod","tidy"],{cwd:t}),r.succeed(`\u2705 Upgraded ${e.length} module(s)`);}catch(i){r.fail("Failed to upgrade modules"),o.error(String(i)),process.exit(1);}}var V;function he(){if(V)return V;let t=process.env.HBX_CLI_VERSION??process.env.npm_package_version;if(t)return V=t,V;let e=fr(import.meta.url);try{let o=Wt.readFileSync(e,"utf8");V=JSON.parse(o).version??"0.0.0";}catch{V="0.0.0";}return V}function fr(t){let e=E.dirname(fileURLToPath(t)),o=E.resolve(e,"../package.json");return Wt.existsSync(o)?o:E.resolve(e,"../../package.json")}function zt(t,e){t.command("version").description("Print CLI version").action((o,r)=>{let{logger:i}=e,n=!!r.optsWithGlobals().json,s=he(),a={status:"success",version:s};n?i.json(a):i.info(`hbx ${s}`);});}var we=class t{#e;constructor(e){this.#e=e;}static async load(){try{let e=await P.readFile(le,"utf8"),o=JSON.parse(e);return new t(o)}catch(e){if(e.code==="ENOENT")return new t({});throw new ie("Failed to read configuration",{error:e})}}toJSON(){return this.#e}getLicense(){return process.env[X]??this.#e.token}setLicense(e){this.#e={...this.#e,token:e};}clearLicense(){let e={...this.#e};e.token=void 0,e.device=void 0,this.#e=e;}isTelemetryEnabled(){return process.env[Me]==="1"?false:typeof this.#e.telemetry?.enabled=="boolean"?this.#e.telemetry.enabled:true}setTelemetryEnabled(e){this.#e={...this.#e,telemetry:{...this.#e.telemetry,enabled:e,lastPrompted:Date.now()}};}getTelemetryId(){return this.#e.telemetry?.id}async ensureTelemetryId(){let e=this.getTelemetryId();if(e)return e;let o=oe.randomUUID();return this.#e={...this.#e,telemetry:{...this.#e.telemetry,id:o}},await this.save(),o}getDefaultTemplate(){return this.#e.defaults?.template}getUpdateMetadata(){return this.#e.updates}setUpdateMetadata(e){this.#e={...this.#e,updates:{...this.#e.updates,...e}};}getDeviceInfo(){return this.#e.device}setDeviceInfo(e){this.#e={...this.#e,device:e};}async save(){await P.mkdir(Ce,{recursive:true});let e=JSON.stringify(this.#e,null,2);await P.writeFile(le,e,{mode:384}),await P.chmod(le,384);}};var be=class{#e;#t;constructor(e={}){this.#e=e.json??false,this.#t=e.debug??process.env[ee]==="1";}isJsonMode(){return this.#e}setJsonMode(e){this.#e=e;}setDebug(e){this.#t=e;}info(e){this.#e||process.stdout.write(`${ne.blue("i")} ${e}
88
- `);}success(e){this.#e||process.stdout.write(`${ne.green("\u2714")} ${e}
89
- `);}warn(e){this.#e||process.stdout.write(`${ne.yellow("!")} ${e}
90
- `);}error(e){this.#e||process.stderr.write(`${ne.red("\u2716")} ${e}
91
- `);}debug(e){!this.#t||this.#e||process.stdout.write(`${ne.gray("debug")} ${e}
92
- `);}json(e){let o=JSON.stringify(e,null,2);process.stdout.write(`${o}
93
- `);}};var yr=new Set(["command","project","duration","status","errorCode","errorMessage","metadata"]),wr=50,br=6144,Jt=25e3,xe=class{#e;#t;#r;#n=[];#o;#i=false;#s;#a;#c;constructor(e,o,r){this.#e=e,this.#t=o,this.#r=r,this.#a=process.env.HBX_RELEASE_CHANNEL??process.env.RELEASE_CHANNEL??"stable",this.#c=randomUUID(),this.#l();}isEnabled(){return this.#e.isTelemetryEnabled()}setCommand(e){this.#s=e;}async track(e){this.isEnabled()&&(this.#n.push({...e,timestamp:new Date().toISOString(),payload:e.payload??{}}),await this.flush());}async flush(){if(!this.#n.length)return;if(this.#o){await this.#o;return}let e=this.#n.splice(0);this.#o=this.#m(e).finally(()=>{this.#o=void 0;}),await this.#o;}#l(){if(this.#i)return;let e=()=>{this.flush();};process.once("beforeExit",e),process.once("exit",e),process.once("SIGINT",e),process.once("SIGTERM",e),this.#i=true;}async#m(e){if(!e.length)return;let o=this.#e.getTelemetryId(),i=[...e.map(n=>Cr(n))];for(;i.length;){let n=[],s;for(;i.length&&n.length<wr;){let a=i[0];if(!a)break;let c=[...n,a],l=this.#d(c,o),u=JSON.stringify(l);if(Buffer$1.byteLength(u,"utf8")>Jt){if(!n.length){i.shift(),this.#t.warn("Dropped telemetry event because payload exceeded max size");continue}break}let g=i.shift();if(!g)break;n.push(g),s=u;}!n.length||!s||await this.#u(s,o);}}#d(e,o){return {clientId:o&&o.length>=8?o:"anonymous",os:ve.platform(),osVersion:xr(),arch:process.arch,nodeVersion:process.version,ci:vr(),command:this.#s,releaseChannel:this.#a,sessionId:this.#c,cliVersion:this.#r,version:this.#r,hasLicense:!!this.#e.getLicense(),events:e}}async#u(e,o){let r=Buffer$1.byteLength(e,"utf8");if(r>Jt){this.#t.warn("Telemetry payload skipped because it exceeded the maximum request size");return}try{let i=new AbortController,n=setTimeout(()=>i.abort(),5e3),s=await fetch(Xe,{method:"POST",headers:{"Content-Type":"application/json","Content-Length":String(r),"User-Agent":`hbx-cli/${this.#r}`,...o?{"x-telemetry-id":o}:void 0},body:e,signal:i.signal});if(clearTimeout(n),s.status===413){this.#t.warn("Telemetry payload rejected (too large)");return}if(!s.ok)throw new Error(`telemetry responded with status ${s.status}`)}catch(i){this.#t.debug(`telemetry send failed: ${String(i)}`);}}};function vr(){return process.env.CI==="1"||process.env.CONTINUOUS_INTEGRATION==="1"||!!process.env.BUILD_NUMBER||!!process.env.RUN_ID}function xr(){let t=ve.version;if(typeof t=="function")try{return t()}catch{return ve.release()}return ve.release()}function Cr(t){let e=Er(t.payload);if(!e){let{payload:o,...r}=t;return r}try{let o=JSON.stringify(e);return Buffer$1.byteLength(o,"utf8")>br?{...t,payload:{truncated:!0}}:{...t,payload:e}}catch{return {...t,payload:{truncated:true}}}}function Er(t){if(!t||typeof t!="object")return;let e={};for(let o of yr)if(Object.hasOwn(t,o)){let r=t[o];if(o==="metadata"&&(r==null||typeof r!="object"))continue;e[o]=r;}return Object.keys(e).length?e:void 0}async function Kt(t,e,o){if(process.env[Fe]==="1")return;let r=e.getUpdateMetadata()??{},i=Date.now(),n=r.latestVersion,s=r.lastChecked??0;if(!n||i-s>We){let l=await Tr(t,o);l.latestVersion&&(n=l.latestVersion),l.checkedAt>0&&(s=l.checkedAt,e.setUpdateMetadata({lastChecked:s,latestVersion:n}),await e.save());}if(!n||!L.valid(n)||!L.gt(n,t)||!Pr(r,i,n))return;let c=`npm install -g ${me}`;o.warn(`A new version of hbx is available: ${n} (current ${t}).
94
- Update with: ${c} (or pnpm add -g ${me}).`),e.setUpdateMetadata({lastNotifiedAt:i,latestVersion:n}),await e.save();}function Pr(t,e,o){return !t||t.lastNotifiedAt===void 0||t.latestVersion!==o?true:e-t.lastNotifiedAt>ze}async function Tr(t,e){try{let o=await fetch(`https://registry.npmjs.org/${encodeURIComponent(me)}/latest`,{headers:{"User-Agent":`hbx-cli/${t}`}});if(!o.ok)throw new Error(`npm registry responded with status ${o.status}`);return {latestVersion:(await o.json()).version,checkedAt:Date.now()}}catch(o){return e.debug(`update check failed: ${String(o)}`),{checkedAt:Date.now()}}}async function Hr(t=process.argv){let e=he(),o=await we.load(),r=new be({debug:process.env[ee]==="1"});await o.ensureTelemetryId();let i=new xe(o,r,e),n={config:o,logger:r,telemetry:i};await Kt(e,o,r),await _r(n);let s=new Command;s.name("hbx").description("HugoBlox Experience CLI").version(e,"-v, --version","Show CLI version").option("--json","Output machine-readable JSON").option("--debug","Enable verbose logging"),s.hook("preAction",(a,c)=>{let l=c?.optsWithGlobals?.()??a.optsWithGlobals();r.setJsonMode(!!l.json),r.setDebug(!!l.debug||process.env[ee]==="1"),i.setCommand(Ir(c));}),St(s,n),ot(s,n),Dt(s,n),It(s,n),Rt(s,n),Nt(s,n),Ot(s,n),Xt(s,n),Mt(s,n),zt(s,n);try{await s.parseAsync(t);}catch(a){Sr(a,r);}}function Sr(t,e){if(De(t)){let o={status:"error",code:t.code,message:t.message,details:t.details};e.isJsonMode()?e.json(o):e.error(`${t.code}: ${t.message}`);}else t instanceof R?e.error(`${t.code}: ${t.message}`):t instanceof Error?e.error(t.message):e.error("Unknown error");process.exit(1);}(process.argv[1]===fileURLToPath(import.meta.url)||process.argv[1].endsWith("/bin/hbx")||process.argv[1].endsWith("/bin/hugoblox"))&&Hr();function Ir(t){if(!t)return ce;let e=[],o=t;for(;o?.parent;){let r=o.name?.();r&&e.unshift(r),o=o.parent;}return e.length?`${ce} ${e.join(" ")}`.trim():ce}async function _r(t){if(process.env[X]&&t.config.getLicense()&&!t.config.getDeviceInfo())try{await B(t);}catch(e){t.logger.debug(`Skipping device registration warm-up: ${String(e)}`);}}
87
+ `),f.succeed("Assets hydrated");}catch(u){throw f.fail("Failed to hydrate assets"),u}await s.track({name:"install",payload:{ci:!!t.ci,force:!!t.force,tokenPresent:!!m,customPath:!!t.path}});let b={status:"success",path:l.root,forced:!!t.force,token:!!m};a?o.json(b):(o.success("Install complete"),m||o.info("Tip: run hbx login to unlock Pro downloads"));});}function er(r,e){r.command("login").description("Authenticate with HugoBlox Pro and register this device for template access").option("--token <value>","License key").option("--ci","Fail when license key is missing").action(async(t,n)=>{let{config:o,logger:i}=e,c=!!n.optsWithGlobals().json,a=t.token;if(!a){if(t.ci)throw new d("License key is required when using --ci");a=await B("Enter your HugoBlox Pro license key");}let l=false;try{o.setLicense(a),await o.save(),l=!0,await L(e,{force:!0});let m={status:"success",token:Je(a)};c?i.json(m):i.success(`License key saved (${Je(a)})`);}catch(m){throw l&&(o.clearLicense(),await o.save()),$n(m)}});}function tr(r,e){r.command("logout").description("Remove stored HugoBlox credentials and device secrets").action(async(t,n)=>{let{config:o,logger:i}=e,s=!!n.optsWithGlobals().json;await Tt(o),o.clearLicense(),await o.save();let c={status:"success"};s?i.json(c):i.success("License key removed");});}function $n(r){let e=r instanceof Error?r.message:"Device registration failed",t="HBX must register this machine before downloading Pro templates. Check your internet connection, ensure your license has a free device slot, or revoke an old device and rerun 'hbx login'.";return r instanceof O?new d(`${e}. ${t}`,r.details):new d(`${e}. ${t}`)}function nr(r,e){r.command("migrate").description("Migrate HugoBlox project to newer versions").command("authors").description("Migrate author profiles from content/authors/ to data/authors/ (v0.11.0+)").option("-y, --yes","Skip confirmation prompts").option("--dry-run","Preview changes without modifying files").action(async n=>{let{logger:o}=e,i=await D();if(!i)throw new d("No Hugo project found in the current directory.");let s=Ue("Analyzing author profiles...").start();try{let c=E.join(i.root,"content","authors");if(!await v(c)){s.stop(),o.info("\u2139\uFE0F No authors directory found. Nothing to migrate.");return}let a=await _n(c);if(a.length===0){s.stop(),o.info("\u2139\uFE0F No author profiles found to migrate.");return}s.stop(),o.info(`
88
+ \u{1F4CB} Migration Plan (v0.11.0 Author System):
89
+ `),o.info(` Found ${a.length} author profile(s):`);for(let m of a){let f=m.slug==="admin"?"me":m.slug;o.info(` \u2022 ${m.slug} \u2192 data/authors/${f}.yaml`),m.avatarPath&&o.info(` Avatar: ${E.basename(m.avatarPath)} \u2192 assets/media/authors/${f}${E.extname(m.avatarPath)}`);}if(o.info(`
90
+ Changes:`),o.info(" \u2713 Create data/authors/ directory"),o.info(" \u2713 Generate YAML files with new schema (hugoblox/author/v1)"),o.info(" \u2713 Migrate avatar images to assets/media/authors/"),o.info(" \u2713 Update author references in content files"),o.info(" \u2713 Archive original content/authors/ directory"),n.dryRun){o.info(`
91
+ \u{1F50D} Dry run mode - no files will be modified.`);return}if(!n.yes&&!await R(`
92
+ This will restructure your author data. We recommend backing up first. Continue?`,{initial:!1})){o.info("Migration cancelled.");return}let l=await qn(i.root,a,o);if(o.info(""),l.success)o.success("\u2705 Migration complete!"),o.info(` \u2022 Migrated ${l.authorsMigrated} author profile(s)`),o.info(` \u2022 Updated ${l.contentFilesMigrated} content file(s)`),o.info(`
93
+ \u{1F4DA} Next steps:`),o.info(" 1. Review generated files in data/authors/"),o.info(" 2. Test your site: pnpm dev"),o.info(" 3. Remove backup: rm -rf content/authors.backup");else {o.error("\u274C Migration completed with errors:");for(let m of l.errors)o.error(` \u2022 ${m}`);o.info(`
94
+ Original files preserved in content/authors/`);}}catch(c){throw s.stop(),c}});}async function _n(r){let e=[],t=await T.readdir(r,{withFileTypes:true});for(let o of t){if(!o.isDirectory()||o.name.startsWith("_"))continue;let i=E.join(r,o.name),s=E.join(i,"_index.md");if(!await v(s))continue;let c=Ze(o.name);if(c)try{let a=await T.readFile(s,"utf8"),{frontMatter:l,body:m}=Qe(a),f,u=(await T.readdir(i)).find(p=>/^avatar\.(png|jpg|jpeg|webp)$/i.test(p));u&&(f=E.join(i,u)),e.push({slug:o.name,targetSlug:c,originalPath:i,frontMatter:l,content:m.trim(),avatarPath:f});}catch{}}let n=E.join(r,"_index.md");if(await v(n))try{let o=await T.readFile(n,"utf8"),{frontMatter:i,body:s}=Qe(o);if(Hn(i,s)){let c=y(i.slug)??"admin",a=c?Ze(c):void 0;if(a&&!e.some(l=>l.targetSlug===a)){let l,f=(await T.readdir(r)).find(b=>/^avatar\.(png|jpg|jpeg|webp)$/i.test(b));f&&(l=E.join(r,f)),e.push({slug:c,targetSlug:a,originalPath:r,frontMatter:i,content:s.trim(),avatarPath:l});}}}catch{}return e}function Qe(r){let e=r.match(/^---\r?\n([\s\S]*?)\r?\n---\r?\n([\s\S]*)$/);if(!e)return {frontMatter:{},body:r};try{let t=He.parse(e[1]);return typeof t!="object"||t===null||Array.isArray(t)?{frontMatter:{},body:r}:{frontMatter:t,body:e[2]??""}}catch{return {frontMatter:{},body:r}}}function Hn(r,e){return e.trim()?true:["title","name","first_name","last_name","bio","profiles","organizations","affiliations","interests","education","work","skills","languages","role","status","pronouns","name_pronunciation"].some(n=>r[n]!==void 0)}function Ze(r){let e=z(r);if(e)return e==="admin"?"me":e}function y(r){if(typeof r!="string")return;let e=r.trim();return e===""?void 0:e}function fe(r){return typeof r=="number"&&Number.isFinite(r)?r:y(r)}function jn(r){return Array.isArray(r)?r.map(e=>y(e)).filter(e=>!!e):[]}function Rn(r){let e={},t=y(r.title??r.name);t&&(e.display=t);let n=y(r.first_name??r.given_name??r.given);n&&(e.given=n);let o=y(r.last_name??r.family_name??r.family);o&&(e.family=o);let i=y(r.middle_name??r.middle);i&&(e.middle=i);let s=y(r.nickname??r.alternate_name??r.alt_name);s&&(e.alternate=s);let c=y(r.name_pronunciation??r.pronunciation);c&&(e.pronunciation=c);let a=y(r.pronouns);return a&&(e.pronouns=a),e}function Dn(r){if(r.status&&typeof r.status=="object"&&!Array.isArray(r.status)){let t=y(r.status.icon);if(t)return {icon:t}}let e=y(r.status_icon??r.status_emoji??r.status);if(e)return {icon:e}}function Bn(r){return Array.isArray(r)?r.map(e=>{if(typeof e!="object"||e===null)return;let t=e,n=y(t.name);if(!n)return;let o=y(t.url);return o?{name:n,url:o}:{name:n}}).filter(e=>!!e):[]}function Ln(r){let e={},t=["orcid","google_scholar","openalex","semantic_scholar","research_gate","scopus","arxiv"];for(let i of t){let s=y(r[i]);s&&(e[i]=s);}let n=[];Array.isArray(r.profiles)&&n.push(...r.profiles);let o=r.links;Array.isArray(o)&&n.push(...o);for(let i of n){if(typeof i!="object"||i===null)continue;let s=y(i.url);s&&Nn(s,e);}return Object.keys(e).length===0?void 0:e}function Nn(r,e){if(/^https?:\/\//i.test(r))try{let t=new URL(r),n=t.hostname.toLowerCase(),o=t.pathname.split("/").filter(Boolean);if(n.includes("scholar.google.")){let i=t.searchParams.get("user");i&&!e.google_scholar&&(e.google_scholar=i);return}if(n.includes("orcid.org")){let i=t.pathname.match(/(\d{4}-\d{4}-\d{4}-\d{3}[0-9X])/i);i&&!e.orcid&&(e.orcid=i[1]);return}if(n.includes("openalex.org")){let i=o[o.length-1];i&&!e.openalex&&(e.openalex=i);return}if(n.includes("semanticscholar.org")){let i=o[o.length-1];i&&!e.semantic_scholar&&(e.semantic_scholar=i);return}if(n.includes("researchgate.net")){let i=o[o.length-1];i&&!e.research_gate&&(e.research_gate=i);return}if(n.includes("scopus.com")){let i=t.searchParams.get("authorId")??t.searchParams.get("scopusId")??o[o.length-1];i&&!e.scopus&&(e.scopus=i);return}if(n.includes("arxiv.org")){let i=o[o.length-1];i&&!e.arxiv&&(e.arxiv=i);}}catch{}}function On(r,e,t){let n=[];if(Array.isArray(r))for(let s of r){if(typeof s!="object"||s===null)continue;let c=s,a=y(c.icon),l=y(c.url),m=y(c.label);if(a&&l){let f={icon:a,url:l};m&&(f.label=m),n.push(f);}}let o=y(t);if(o){let s=o.startsWith("mailto:")?o:`mailto:${o}`;n.some(c=>c.url===s)||n.push({icon:"at-symbol",url:s,label:"Email"});}let i=y(e);return i&&!n.some(s=>s.url===i)&&n.push({icon:"link",url:i,label:"Website"}),n}function Mn(r){if(typeof r=="string"){let e=r.trim();return e||void 0}if(Array.isArray(r)){let e=r.map(t=>y(t)).filter(t=>!!t);if(e.length>0)return e}}function Fn(r){return Array.isArray(r)?r.map(e=>{if(typeof e!="object"||e===null)return;let t=e,n=y(t.institution??t.organization??t.school);if(!n)return;let o={institution:n},i=y(t.degree??t.area??t.qualification);i&&(o.degree=i);let s=fe(t.year);s!==void 0&&(o.year=s);let c=fe(t.start??t.date_start);c!==void 0&&(o.start=c);let a=fe(t.end??t.date_end);a!==void 0&&(o.end=a);let l=y(t.summary);l&&(o.summary=l);let m=or(t.button);return m&&(o.button=m),o}).filter(e=>!!e):[]}function Un(r){return Array.isArray(r)?r.map(e=>{if(typeof e!="object"||e===null)return;let t=e,n=y(t.role??t.position),o=y(t.org??t.company_name??t.company);if(!n||!o)return;let i={role:n,org:o},s=fe(t.start??t.date_start);s!==void 0&&(i.start=s);let c=fe(t.end??t.date_end);c!==void 0&&(i.end=c);let a=y(t.summary);a&&(i.summary=a);let l=or(t.button);return l&&(i.button=l),i}).filter(e=>!!e):[]}function or(r){if(typeof r!="object"||r===null)return;let e=r,t=y(e.url);if(!t)return;let n={url:t},o=y(e.text);o&&(n.text=o);let i=y(e.icon);return i&&(n.icon=i),n}function Vn(r){return Array.isArray(r)?r.map(e=>{if(typeof e!="object"||e===null)return;let t=e,o=(Array.isArray(t.items)?t.items:[]).map(c=>{if(typeof c!="object"||c===null)return;let a=c,l=y(a.label??a.name);if(!l)return;let m={label:l},f=ir(a.level)??et(typeof a.percent=="number"?a.percent:void 0);f!==void 0&&(m.level=f);let b=y(a.icon);return b&&(m.icon=b),m}).filter(c=>!!c);if(o.length===0)return;let i={items:o},s=y(t.name);return s&&(i.name=s),i}).filter(e=>!!e):[]}function et(r){if(typeof r!="number"||Number.isNaN(r)||r<=0)return;let e=Math.min(100,r);return Math.max(1,Math.min(5,Math.ceil(e/20)))}function ir(r){if(!(typeof r!="number"||Number.isNaN(r)))return r>=1&&r<=5?Math.round(r):et(r)}function Wn(r){return Array.isArray(r)?r.map(e=>{if(typeof e=="string"){let a=y(e);return a?{name:a}:void 0}if(typeof e!="object"||e===null)return;let t=e,n=y(t.name??t.label);if(!n)return;let o=typeof t.percent=="number"?Math.max(0,Math.min(100,t.percent)):void 0,i=ir(t.level)??et(o),s={name:n};i!==void 0&&(s.level=i),o!==void 0&&(s.percent=o);let c=y(t.label)??zn(o,i);return c&&(s.label=c),s}).filter(e=>!!e):[]}function zn(r,e){let t=r??(typeof e=="number"?e*20:void 0);if(t!==void 0){if(t>=99)return "Native";if(t>=90)return "C2";if(t>=75)return "C1";if(t>=60)return "B2";if(t>=45)return "B1";if(t>=30)return "A2"}}function Gn(r){return Array.isArray(r)?r.map(e=>{if(typeof e=="string"){let l=y(e);return l?{title:l}:void 0}if(typeof e!="object"||e===null)return;let t=e,n=y(t.title);if(!n)return;let o={title:n},i=y(t.awarder);i&&(o.awarder=i);let s=y(t.date);s&&(o.date=s);let c=y(t.summary);c&&(o.summary=c);let a=y(t.icon);return a&&(o.icon=a),o}).filter(e=>!!e):[]}function Xn(r,e){let{frontMatter:t,content:n}=r,o={schema:"hugoblox/author/v1",slug:e},i=Rn(t);Object.keys(i).length>0&&(o.name=i),t.superuser===true&&(o.is_owner=true);let s=Dn(t);s&&(o.status=s);let c=y(t.role);c&&(o.role=c);let a=y(t.bio)??n.trim();a&&(o.bio=a);let l=Bn(t.organizations??t.affiliations);l.length>0&&(o.affiliations=l);let m=Ln(t);m&&(o.ids=m);let f=[];Array.isArray(t.profiles)&&f.push(...t.profiles);let b=t.links;Array.isArray(b)&&f.push(...b);let u=On(f,t.website,t.email);u.length>0&&(o.links=u);let p=jn(t.interests);p.length>0&&(o.interests=p);let h=Mn(t.postnominals??t.credentials);h&&(o.postnominals=h);let g=Fn(t.education);g.length>0&&(o.education=g);let w=Un(t.work??t.experience);w.length>0&&(o.experience=w);let P=Vn(t.skills);P.length>0&&(o.skills=P);let x=Wn(t.languages);x.length>0&&(o.languages=x);let C=Gn(t.awards);return C.length>0&&(o.awards=C),typeof t.weight=="number"&&Number.isFinite(t.weight)&&(o.weight=Math.trunc(t.weight)),o}async function Jn(r,e,t){let n=E.extname(r)||".png",o=E.join(e,`${t}${n}`);return await T.copyFile(r,o),o}async function qn(r,e,t){let n={success:true,authorsMigrated:0,contentFilesMigrated:0,errors:[]},o=Ue("Creating backup...").start(),i=E.join(r,"content","authors"),s=E.join(r,"content","authors.backup");try{if(await v(s)){let p=new Date().toISOString().replace(/[:.]/g,"-").split(".")[0],h=`${s}.${p}`;o.text=`Existing backup found, creating timestamped backup: ${E.basename(h)}`,await T.cp(i,h,{recursive:!0}),t.info(` Previous backup preserved as: ${E.basename(h)}`);}else await T.cp(i,s,{recursive:!0});o.text="Backup created, migrating author profiles...";let c=E.join(r,"data","authors"),a=E.join(r,"assets","media","authors");if(await v(c)){let h=(await T.readdir(c)).filter(g=>g.endsWith(".yaml")||g.endsWith(".yml"));if(h.length>0)throw o.stop(),new d(`data/authors/ already contains ${h.length} YAML file(s).
95
+ Please remove or back them up manually before running migration.`)}await $(c),await $(a);let l=new Map,m=new Set,f=[],b=[];for(let p of e){let h=p.targetSlug??Ze(p.slug);if(!h){b.push(p.slug);continue}m.has(h)&&f.push(h),m.add(h);let g=new Set([p.slug]),w=p.slug.toLowerCase();w!==p.slug&&g.add(w);let P=z(p.slug);P&&P!==p.slug&&g.add(P);for(let x of g)l.has(x)||l.set(x,h);}if(b.length>0)throw o.stop(),new d(`Unable to normalize the following author slug(s): ${b.join(", ")}
96
+ Please rename them to use lowercase letters, numbers, or hyphens.`);if(f.length>0)throw o.stop(),new d(`Slug collision detected: Multiple profiles would map to: ${f.join(", ")}
97
+ Please manually rename one of the conflicting profiles before migration.`);for(let p=0;p<e.length;p++){let h=e[p];o.text=`Migrating profiles... (${p+1}/${e.length})`;try{let g=l.get(h.slug);if(!g)throw new d(`Unable to determine target slug for '${h.slug}'`);let w=Xn(h,g),P=E.join(c,`${g}.yaml`),x=He.stringify(w,{defaultStringType:"QUOTE_DOUBLE",lineWidth:0});try{let C=He.parse(x);if(!C.schema||!C.slug)throw new Error("Generated YAML missing required fields")}catch(C){throw new Error(`Invalid YAML generated: ${String(C)}`)}if(await T.writeFile(P,x,"utf8"),h.avatarPath)try{await Jn(h.avatarPath,a,g);}catch(C){n.errors.push(`\u2713 Profile '${h.slug}' migrated, but avatar copy failed: ${String(C)}`);}n.authorsMigrated++;}catch(g){n.errors.push(`Failed to migrate ${h.slug}: ${String(g)}`),n.success=!1;}}o.text="Updating content references...";let u=E.join(r,"content");if(await v(u)){let p=await Kn(u,l);n.contentFilesMigrated=p;}o.text="Finalizing migration...",await T.rm(i,{recursive:!0,force:!0}),o.succeed("Migration complete");}catch(c){o.fail("Migration failed"),n.success=false,n.errors.push(String(c));try{t.warn("\u26A0\uFE0F Attempting to rollback changes..."),!await v(i)&&await v(s)&&(await T.cp(s,i,{recursive:!0}),t.info(" \u2713 Original authors directory restored from backup"));let a=E.join(r,"data","authors");await v(a)&&(await T.readdir(a)).filter(b=>b.endsWith(".yaml")).length===n.authorsMigrated&&(await T.rm(a,{recursive:!0,force:!0}),t.info(" \u2713 Partial data/authors/ files removed"));let l=E.join(r,"assets","media","authors");await v(l)&&(await T.readdir(l)).length<=n.authorsMigrated&&(await T.rm(l,{recursive:!0,force:!0}),t.info(" \u2713 Partial avatar files removed")),t.info("\u2713 Rollback complete. Your original files are safe.");}catch(a){n.errors.push(`Rollback failed: ${String(a)}`),t.error("\u274C Rollback failed. Please manually check:"),t.error(" - content/authors.backup/ contains your original files"),t.error(" - You may need to manually restore them");}}return n}async function Kn(r,e){let t=0;for await(let n of sr(r))try{let o=await T.readFile(n,"utf8"),{frontMatter:i,body:s}=Qe(o),c=!1;if(Array.isArray(i.authors)){let a=i.authors.map(l=>{if(typeof l=="string"){let m=l.trim(),f=m.toLowerCase(),b=e.get(m)??e.get(l)??e.get(f);if(b)return c=!0,b}return l});i.authors=a;}if(c){let a=`---
98
+ ${He.stringify(i)}---
95
99
 
96
- export { Hr as bootstrap };
100
+ ${s}`;await T.writeFile(n,a,"utf8"),t++;}}catch{}return t}async function*sr(r){let e;try{e=await T.readdir(r,{withFileTypes:!0});}catch{return}for(let t of e){let n=E.join(r,t.name);if(t.isDirectory()){if(t.name==="authors"||t.name==="authors.backup")continue;try{if((await T.lstat(n)).isSymbolicLink())continue}catch{continue}yield*sr(n);}else t.name.endsWith(".md")&&(yield n);}}function ar(r,e){let t=r.command("telemetry").description("Manage HugoBlox telemetry preferences");t.command("enable").description("Enable anonymous telemetry").action(async()=>{e.config.setTelemetryEnabled(true),await e.config.save(),e.logger.success("Telemetry enabled. Thanks for helping improve HugoBlox!");}),t.command("disable").description("Disable telemetry collection").action(async()=>{e.config.setTelemetryEnabled(false),await e.config.save(),e.logger.success("Telemetry disabled. You can re-enable it anytime.");}),t.command("status").description("Show whether telemetry is enabled").action(()=>{let n=e.config.isTelemetryEnabled();e.logger.info(`Telemetry is ${n?"enabled":"disabled"}.`);}),t.action(()=>{let n=e.config.isTelemetryEnabled();e.logger.info(`Telemetry is ${n?"enabled":"disabled"}.`),e.logger.info("Use 'hbx telemetry enable|disable' to change the setting.");});}async function lr(r,e,t){let n=N.coerce(e)?.version;if(!n)return;let o=[{path:"hugoblox.yaml",regex:/(hugo_version:\s*['"]?)([\d.]+)(['"]?)/,name:"hugoblox.yaml"},{path:"netlify.toml",regex:/(HUGO_VERSION\s*=\s*['"])([\d.]+)(['"])/,name:"netlify.toml"},{path:".github/workflows/deploy.yml",regex:/(WC_HUGO_VERSION:\s*['"]?)([\d.]+)(['"]?)/,name:"GitHub Actions"}];for(let i of o){let s=E.join(r,i.path);if(await v(s))try{let c=await T.readFile(s,"utf8"),a=c.match(i.regex);if(a){let l=a[2],m=N.coerce(l),f=N.coerce(n);if(m&&f&&N.lt(m,f)){let b=c.replace(i.regex,`$1${n}$3`);await T.writeFile(s,b,"utf8"),t.success(`Updated ${i.name} Hugo version to ${n}`);}}}catch{}}}function dr(r,e){r.command("upgrade").alias("update").description("Upgrade HugoBlox modules to the latest version").option("-y, --yes","Skip confirmation").option("--ci","Run in non-interactive mode").option("--force","Force upgrade even if Hugo version is incompatible").option("--canary","Upgrade to the latest development commit (main branch) instead of stable release").action(async(t,n)=>{let{logger:o}=e;await yt()||(o.error("\u274C You appear to be offline. Please check your internet connection."),process.exit(1));let i=await D();if(!i)throw new d("No Hugo project found in the current directory.");let s=Ue("Checking for updates...").start(),c=await $e(i.root);if(c.length===0){s.stop(),o.info("\u2139\uFE0F No official HugoBlox modules found in go.mod");return}let a=c.filter(u=>u.error);if(a.length>0){s.stop(),o.error("\u274C Failed to check for updates for some modules:");for(let u of a)o.error(` ${u.path}: ${u.error}`);process.exit(1);}let l=[];if(t.canary)s.text="Checking canary versions...",l.push(...c.map(u=>({path:u.path,version:"main"})));else {let u=c.map(p=>{let{updateAvailable:h,diff:g}=_e(p.current,p.latest);return {...p,updateAvailable:h,diff:g}}).filter(p=>p.updateAvailable&&p.latest);if(u.length===0){s.stop(),o.success("\u2705 All HugoBlox modules are up to date");return}l.push(...u.map(p=>({path:p.path,version:p.latest||""})));}s.text="Checking version requirements...";let m=await Promise.all(l.map(async u=>{try{let p=await qt(u.path,u.version);return {...u,min:p}}catch{return {...u,min:void 0}}}));if(s.stop(),!t.force){let u=await gt();if(u){let p=m.filter(h=>{if(!h.min)return false;let g=N.coerce(h.min),w=N.coerce(u);return g&&w&&N.lt(w,g)});if(p.length>0){o.error("\u274C Hugo version incompatible with upgrade targets:");for(let h of p)o.error(` ${h.path.split("/").pop()} requires Hugo >= ${h.min} (you have ${u})`);o.info("\u{1F449} Please upgrade Hugo first: https://docs.hugoblox.com/start/cli"),o.info(" Or run with --force to ignore this check."),process.exit(1);}}else o.warn("\u26A0\uFE0F Could not detect local Hugo version. Skipping compatibility check.");}if(t.canary){o.warn("\u26A0\uFE0F Upgrading modules to latest canary (development) version.");for(let u of c)o.info(` ${u.path.split("/").pop()}: ${ae(u.current)} -> @main`);}else {o.info(`\u{1F4E6} Updates available for ${l.length} module(s):`);for(let u of l){let p=c.find(h=>h.path===u.path)?.current||"?";o.info(` ${u.path.split("/").pop()}`),o.info(` Current: ${ae(p)}`),o.info(` Latest: ${ae(u.version)}`);}}if(!t.yes&&!t.ci&&!await R(t.canary?"Are you sure you want to upgrade to the bleeding edge?":"Do you want to upgrade now?",{initial:true})){o.info("Upgrade cancelled");return}let f=l.map(u=>`${u.path}@${u.version}`);await Qn(i.root,f,o);let b=m.reduce((u,p)=>{if(!p.min)return u;let h=N.coerce(p.min),g=N.coerce(u);return h&&(!g||N.gt(h,g))?p.min:u},void 0);b&&await lr(i.root,b,o),o.info(""),o.warn("\u26A0\uFE0F Important: Please review the release notes for any breaking changes."),o.info("\u{1F449} https://github.com/HugoBlox/hugo-blox-builder/releases");});}async function Qn(r,e,t){let n=Ue("Upgrading modules...").start();try{await execa("hugo",["mod","get",...e],{cwd:r}),n.text="Tidying go.mod...",await execa("hugo",["mod","tidy"],{cwd:r}),n.succeed(`\u2705 Upgraded ${e.length} module(s)`);}catch(o){n.fail("Failed to upgrade modules"),t.error(String(o)),process.exit(1);}}var K;function Y(){if(K)return K;let r=process.env.HBX_CLI_VERSION??process.env.npm_package_version;if(r)return K=r,K;let e=eo(import.meta.url);try{let t=F.readFileSync(e,"utf8");K=JSON.parse(t).version??"0.0.0";}catch{K="0.0.0";}return K}function eo(r){let e=E.dirname(fileURLToPath(r)),t=E.resolve(e,"../package.json");return F.existsSync(t)?t:E.resolve(e,"../../package.json")}function fr(r,e){r.command("version").description("Print CLI version").action((t,n)=>{let{logger:o}=e,i=!!n.optsWithGlobals().json,s=Y(),c={status:"success",version:s};i?o.json(c):o.info(`hbx ${s}`);});}var me=class r{#e;constructor(e){this.#e=e;}static async load(){try{let e=await T.readFile(xe,"utf8"),t=JSON.parse(e);return new r(t)}catch(e){if(e.code==="ENOENT")return new r({});throw new he("Failed to read configuration",{error:e})}}toJSON(){return this.#e}getLicense(){return process.env[ee]??this.#e.token}setLicense(e){this.#e={...this.#e,token:e};}clearLicense(){let e={...this.#e};e.token=void 0,e.device=void 0,this.#e=e;}isTelemetryEnabled(){return process.env[st]==="1"?false:typeof this.#e.telemetry?.enabled=="boolean"?this.#e.telemetry.enabled:true}setTelemetryEnabled(e){this.#e={...this.#e,telemetry:{...this.#e.telemetry,enabled:e,lastPrompted:Date.now()}};}getTelemetryId(){return this.#e.telemetry?.id}async ensureTelemetryId(){let e=this.getTelemetryId();if(e)return e;let t=pe.randomUUID();return this.#e={...this.#e,telemetry:{...this.#e.telemetry,id:t}},await this.save(),t}getDefaultTemplate(){return this.#e.defaults?.template}getUpdateMetadata(){return this.#e.updates}setUpdateMetadata(e){this.#e={...this.#e,updates:{...this.#e.updates,...e}};}getDeviceInfo(){return this.#e.device}setDeviceInfo(e){this.#e={...this.#e,device:e};}async save(){await T.mkdir(Oe,{recursive:true});let e=JSON.stringify(this.#e,null,2);await T.writeFile(xe,e,{mode:384}),await T.chmod(xe,384);}};var ue=class{#e;#t;constructor(e={}){this.#e=e.json??false,this.#t=e.debug??process.env[G]==="1";}isJsonMode(){return this.#e}setJsonMode(e){this.#e=e;}setDebug(e){this.#t=e;}info(e){this.#e||process.stdout.write(`${ge.blue("i")} ${e}
101
+ `);}success(e){this.#e||process.stdout.write(`${ge.green("\u2714")} ${e}
102
+ `);}warn(e){this.#e||process.stdout.write(`${ge.yellow("!")} ${e}
103
+ `);}error(e){this.#e||process.stderr.write(`${ge.red("\u2716")} ${e}
104
+ `);}debug(e){!this.#t||this.#e||process.stdout.write(`${ge.gray("debug")} ${e}
105
+ `);}json(e){let t=JSON.stringify(e,null,2);process.stdout.write(`${t}
106
+ `);}};var no=new Set(["command","project","duration","status","errorCode","errorMessage","metadata"]),oo=50,io=6144,gr=25e3,Q=class{#e;#t;#n;#o=[];#r;#i=false;#s;#a;#c;constructor(e,t,n){this.#e=e,this.#t=t,this.#n=n,this.#a=process.env.HBX_RELEASE_CHANNEL??process.env.RELEASE_CHANNEL??"stable",this.#c=randomUUID(),this.#l();}isEnabled(){return this.#e.isTelemetryEnabled()}setCommand(e){this.#s=e;}async track(e){this.isEnabled()&&(this.#o.push({...e,timestamp:new Date().toISOString(),payload:e.payload??{}}),await this.flush());}async flush(){if(!this.#o.length)return;if(this.#r){await this.#r;return}let e=this.#o.splice(0);this.#r=this.#m(e).finally(()=>{this.#r=void 0;}),await this.#r;}#l(){if(this.#i)return;let e=()=>{this.flush();};process.once("beforeExit",e),process.once("exit",e),process.once("SIGINT",e),process.once("SIGTERM",e),this.#i=true;}async#m(e){if(!e.length)return;let t=this.#e.getTelemetryId(),o=[...e.map(i=>co(i))];for(;o.length;){let i=[],s;for(;o.length&&i.length<oo;){let c=o[0];if(!c)break;let a=[...i,c],l=this.#u(a,t),m=JSON.stringify(l);if(Buffer$1.byteLength(m,"utf8")>gr){if(!i.length){o.shift(),this.#t.warn("Dropped telemetry event because payload exceeded max size");continue}break}let f=o.shift();if(!f)break;i.push(f),s=m;}!i.length||!s||await this.#d(s,t);}}#u(e,t){return {clientId:t&&t.length>=8?t:"anonymous",os:De.platform(),osVersion:ao(),arch:process.arch,nodeVersion:process.version,ci:so(),command:this.#s,releaseChannel:this.#a,sessionId:this.#c,cliVersion:this.#n,version:this.#n,hasLicense:!!this.#e.getLicense(),events:e}}async#d(e,t){let n=Buffer$1.byteLength(e,"utf8");if(n>gr){this.#t.warn("Telemetry payload skipped because it exceeded the maximum request size");return}try{let o=new AbortController,i=setTimeout(()=>o.abort(),5e3),s=await fetch(ut,{method:"POST",headers:{"Content-Type":"application/json","Content-Length":String(n),"User-Agent":`hbx-cli/${this.#n}`,...t?{"x-telemetry-id":t}:void 0},body:e,signal:o.signal});if(clearTimeout(i),s.status===413){this.#t.warn("Telemetry payload rejected (too large)");return}if(!s.ok)throw new Error(`telemetry responded with status ${s.status}`)}catch(o){this.#t.debug(`telemetry send failed: ${String(o)}`);}}};function so(){return process.env.CI==="1"||process.env.CONTINUOUS_INTEGRATION==="1"||!!process.env.BUILD_NUMBER||!!process.env.RUN_ID}function ao(){let r=De.version;if(typeof r=="function")try{return r()}catch{return De.release()}return De.release()}function co(r){let e=lo(r.payload);if(!e){let{payload:t,...n}=r;return n}try{let t=JSON.stringify(e);return Buffer$1.byteLength(t,"utf8")>io?{...r,payload:{truncated:!0}}:{...r,payload:e}}catch{return {...r,payload:{truncated:true}}}}function lo(r){if(!r||typeof r!="object")return;let e={};for(let t of no)if(Object.hasOwn(r,t)){let n=r[t];if(t==="metadata"&&(n==null||typeof n!="object"))continue;e[t]=n;}return Object.keys(e).length?e:void 0}async function yr(r,e,t){if(process.env[at]==="1")return;let n=e.getUpdateMetadata()??{},o=Date.now(),i=n.latestVersion,s=n.lastChecked??0;if(!i||o-s>dt){let l=await uo(r,t);l.latestVersion&&(i=l.latestVersion),l.checkedAt>0&&(s=l.checkedAt,e.setUpdateMetadata({lastChecked:s,latestVersion:i}),await e.save());}if(!i||!N.valid(i)||!N.gt(i,r)||!mo(n,o,i))return;let a=`npm install -g ${Ce}`;t.warn(`A new version of hbx is available: ${i} (current ${r}).
107
+ Update with: ${a} (or pnpm add -g ${Ce}).`),e.setUpdateMetadata({lastNotifiedAt:o,latestVersion:i}),await e.save();}function mo(r,e,t){return !r||r.lastNotifiedAt===void 0||r.latestVersion!==t?true:e-r.lastNotifiedAt>pt}async function uo(r,e){try{let t=await fetch(`https://registry.npmjs.org/${encodeURIComponent(Ce)}/latest`,{headers:{"User-Agent":`hbx-cli/${r}`}});if(!t.ok)throw new Error(`npm registry responded with status ${t.status}`);return {latestVersion:(await t.json()).version,checkedAt:Date.now()}}catch(t){return e.debug(`update check failed: ${String(t)}`),{checkedAt:Date.now()}}}var Be=class{constructor(e){this.context=e;this.cancelled=false;}async*create(e){this.cancelled=false;try{if(yield*this.validateConfiguration(e),this.cancelled||(yield*this.fetchTemplate(e.templateId),this.cancelled)||(yield*this.scaffoldSite(e),this.cancelled)||e.options?.autoInstall!==!1&&(yield*this.installDependencies(e),this.cancelled)||e.options?.initGit!==!1&&(yield*this.initializeGit(e),this.cancelled))return;yield*this.completeCreation(e);}catch(t){yield {id:"error",type:"error",title:"Creation Failed",message:t instanceof Error?t.message:String(t),timestamp:new Date,data:{error:t,config:e}};}}async*validateConfiguration(e){yield this.createProgressStep("validate",0,"Starting validation..."),yield this.createProgressStep("validate",25,"Checking template availability...");let t=await this.validate(e);if(yield this.createProgressStep("validate",75,"Validating target path..."),!t.valid)throw new Error(`Validation failed: ${t.errors[0]?.message}`);for(let n of t.warnings)yield {id:"validate",type:"warning",title:"Configuration Warning",message:n.message,timestamp:new Date,data:{field:n.field,suggestion:n.suggestion}};yield this.createSuccessStep("validate","Configuration validated");}async*fetchTemplate(e){yield this.createProgressStep("fetch_template",0,"Fetching available templates...");let t=await V(this.context);yield this.createProgressStep("fetch_template",50,"Locating template...");let n=t.find(o=>o.id===e);if(!n)throw new Error(`Template "${e}" not found`);yield this.createProgressStep("fetch_template",75,`Found template: ${n.name}`),await this.delay(500),yield this.createSuccessStep("fetch_template",`Template "${n.name}" ready`,{template:n});}async*scaffoldSite(e){let n=(await V(this.context)).find(s=>s.id===e.templateId);if(!n)throw new Error(`Template "${e.templateId}" not found`);yield this.createProgressStep("scaffold_site",0,"Preparing target directory...");let o=Te(e.targetPath,n.slug);await we(o),yield this.createProgressStep("scaffold_site",25,"Copying template files...");let i={sampleContent:e.options?.includeSampleContent??true,includeEnv:true,packageManager:e.packageManager||"pnpm"};await ke(this.context,o,n,i),yield this.createProgressStep("scaffold_site",75,"Customizing site configuration..."),await this.customizeSiteConfig(o,e),yield this.createSuccessStep("scaffold_site","Site structure created",{targetDir:o,templateId:e.templateId});}async*installDependencies(e){let t=e.packageManager||"pnpm",n=e.targetPath;yield this.createProgressStep("install_deps",0,`Starting ${t} install...`);try{await Ee(n,t,this.context.logger)?yield this.createSuccessStep("install_deps","Dependencies installed successfully"):yield {id:"install_deps",type:"warning",title:"Dependencies installation skipped",message:"Some dependencies failed to install, but site creation can continue",timestamp:new Date};}catch(o){throw new Error(`Failed to install dependencies: ${String(o)}`)}}async*initializeGit(e){yield this.createProgressStep("init_git",0,"Initializing git repository..."),await Se(e.targetPath,this.context.logger),yield this.createProgressStep("init_git",75,"Creating initial commit..."),await this.delay(1e3),yield this.createSuccessStep("init_git","Git repository initialized");}async*completeCreation(e){yield this.createProgressStep("complete",50,"Finalizing setup..."),await this.generateSummaryReport(e),yield {id:"complete",type:"success",title:`Site "${e.siteName}" created successfully! \u{1F389}`,progress:100,timestamp:new Date,data:{siteName:e.siteName,targetPath:e.targetPath,templateId:e.templateId,packageManager:e.packageManager,nextSteps:this.generateNextSteps(e)}};}async validate(e){let t=[],n=[];(await V(this.context)).find(s=>s.id===e.templateId)||t.push({field:"templateId",message:`Template "${e.templateId}" not found`,code:"TEMPLATE_NOT_FOUND"}),(!e.siteName||e.siteName.trim().length===0)&&t.push({field:"siteName",message:"Site name is required",code:"SITE_NAME_REQUIRED"});let i=await Ae(e.targetPath);return i.valid||t.push({field:"targetPath",message:i.error||"Invalid target path",code:"INVALID_PATH"}),i.exists&&!i.isEmpty&&n.push({field:"targetPath",message:"Directory is not empty",suggestion:"Files may be overwritten during site creation"}),{valid:t.length===0,errors:t,warnings:n}}async cancel(){this.cancelled=true;}createProgressStep(e,t,n,o){return {id:e,type:"progress",title:this.getStepTitle(e),message:n,progress:t,timestamp:new Date,data:o}}createSuccessStep(e,t,n){return {id:e,type:"success",title:this.getStepTitle(e),message:t,progress:100,timestamp:new Date,data:n}}getStepTitle(e){return {validate:"Validating configuration",fetch_template:"Fetching template",scaffold_site:"Creating site structure",install_deps:"Installing dependencies",init_git:"Initializing git repository",start_server:"Starting development server",complete:"Finalizing setup"}[e]||e}async delay(e){return new Promise(t=>setTimeout(t,e))}async customizeSiteConfig(e,t){this.context.logger.debug(`Customizing site config for ${t.siteName}`);}async generateSummaryReport(e){this.context.logger.debug(`Site creation completed: ${JSON.stringify({template:e.templateId,path:e.targetPath,options:e.options})}`);}generateNextSteps(e){let t=[`cd ${e.targetPath}`];return e.options?.autoInstall===false&&t.push(`${e.packageManager||"pnpm"} install`),e.options?.autoPreview!==false&&t.push(`${e.packageManager||"pnpm"} dev`),t}};var Le=class r{constructor(e){this.context=e;}static{this.PREVIEW_DATA={"academic-cv":{thumbnailUrl:"https://hugoblox.com/previews/academic-cv-thumb.jpg",category:"academic",difficulty:"beginner",estimatedTime:3,features:["Publication lists","Research sections","Academic timeline","Contact forms"],useCases:["Professors","Researchers","PhD students","Academic professionals"]},startup:{thumbnailUrl:"https://hugoblox.com/previews/startup-thumb.jpg",category:"business",difficulty:"intermediate",estimatedTime:5,features:["Landing pages","Pricing tables","Team sections","Newsletter signup"],useCases:["SaaS companies","Startups","Product launches","Marketing sites"]},folio:{thumbnailUrl:"https://hugoblox.com/previews/folio-thumb.jpg",category:"portfolio",difficulty:"beginner",estimatedTime:2,features:["Project galleries","About sections","Contact forms","Responsive design"],useCases:["Designers","Developers","Freelancers","Creative professionals"]}};}async enrichWithPreviews(e,t){let n=e.map(o=>this.enrichTemplate(o));return t?this.applyQueryOptions(n,t):n}enrichTemplate(e){let t=r.PREVIEW_DATA[e.id];return t?{...e,preview:t,stats:this.calculateStats(e)}:{...e,preview:{thumbnailUrl:this.generateFallbackThumbnail(e),category:this.inferCategory(e),difficulty:"intermediate",estimatedTime:4,features:this.inferFeatures(e),useCases:["General purpose"]},stats:this.calculateStats(e)}}generateFallbackThumbnail(e){return `https://hugoblox.com/api/placeholder-thumb/${encodeURIComponent(e.id)}`}inferCategory(e){let t=e.name.toLowerCase(),n=(e.description||"").toLowerCase();return t.includes("academic")||t.includes("cv")||t.includes("research")?"academic":t.includes("startup")||t.includes("business")||n.includes("saas")?"business":t.includes("portfolio")||t.includes("folio")?"portfolio":t.includes("blog")||n.includes("blog")?"blog":t.includes("docs")||t.includes("documentation")?"documentation":"other"}inferFeatures(e){let t=[],n=(e.description||"").toLowerCase();return n.includes("responsive")&&t.push("Mobile responsive"),n.includes("dark")&&t.push("Dark mode support"),n.includes("seo")&&t.push("SEO optimized"),n.includes("multilingual")&&t.push("Multi-language support"),t.length>0?t:["Modern design","Responsive layout"]}calculateStats(e){let t=e.source==="pro"?80:60;return {popularity:["academic-cv","startup","folio"].includes(e.id)?t+20:t,lastUpdated:new Date().toISOString().split("T")[0]}}applyQueryOptions(e,t){let n=[...e];switch(t.category&&(n=n.filter(o=>o.preview.category===t.category)),t.difficulty&&(n=n.filter(o=>o.preview.difficulty===t.difficulty)),t.source&&(n=n.filter(o=>o.source===t.source)),t.sortBy){case "popularity":n.sort((o,i)=>(i.stats?.popularity||0)-(o.stats?.popularity||0));break;case "name":n.sort((o,i)=>o.name.localeCompare(i.name));break;case "difficulty":{let o={beginner:1,intermediate:2,advanced:3};n.sort((i,s)=>o[i.preview.difficulty]-o[s.preview.difficulty]);break}default:n.sort((o,i)=>o.source==="pro"&&i.source!=="pro"?-1:i.source==="pro"&&o.source!=="pro"?1:o.name.localeCompare(i.name));}return t.limit&&t.limit>0&&(n=n.slice(0,t.limit)),n}async getPreview(e){let t=r.PREVIEW_DATA[e];return t?{templateId:e,thumbnailUrl:t.thumbnailUrl,screenshots:await this.getScreenshots(e),demoUrl:await this.getDemoUrl(e),features:t.features,useCases:t.useCases}:null}async getScreenshots(e){let t="https://hugoblox.com/previews/";return [`${t}${e}-1.jpg`,`${t}${e}-2.jpg`,`${t}${e}-3.jpg`]}async getDemoUrl(e){return {"academic-cv":"https://hugoblox.com/templates/academic-cv/",startup:"https://hugoblox.com/templates/startup/",folio:"https://hugoblox.com/templates/portfolio/"}[e]}};var Ne=class{constructor(e){this.context=e;this.templates={fetchAll:async e=>{let t=await V(this.context);return this.previewService.enrichWithPreviews(t,e)},getById:async e=>(await this.templates.fetchAll()).find(n=>n.id===e)||null,getPreview:async e=>this.previewService.getPreview(e)};this.creation={create:e=>this.creationService.create(e),validate:async e=>this.creationService.validate(e),cancel:async()=>this.creationService.cancel()};this.system={detectPackageManager:async e=>Nt(e||process.cwd()),validatePath:async e=>Ae(e),getDefaultConfig:()=>({templateId:"startup",siteName:"my-hugoblox-site",targetPath:"./my-hugoblox-site",packageManager:"pnpm",options:{includeSampleContent:true,initGit:true,autoInstall:true,autoPreview:true},showAdvanced:false,rememberedChoices:{}})};this.previewService=new Le(e),this.creationService=new Be(e);}};async function po(r={}){let e=await fo(r);return new Ne(e)}async function fo(r){let e=Y(),t=await me.load(),n=new ue({debug:r.debug??process.env[G]==="1",json:true});await t.ensureTelemetryId();let o=r.telemetryEnabled!==false?new Q(t,n,e):new nt(t,n,e);return r.progressCallback&&(o.progressCallback=r.progressCallback),{config:t,logger:n,telemetry:o}}var nt=class extends Q{async track(){}setCommand(){}};async function ho(r=process.argv){let e=Y(),t=await me.load(),n=new ue({debug:process.env[G]==="1"});await t.ensureTelemetryId();let o=new Q(t,n,e),i={config:t,logger:n,telemetry:o};await yr(e,t,n),await vo(i);let s=new Command;s.name("hbx").description("HugoBlox Experience CLI").version(e,"-v, --version","Show CLI version").option("--json","Output machine-readable JSON").option("--debug","Enable verbose logging"),s.hook("preAction",(c,a)=>{let l=a?.optsWithGlobals?.()??c.optsWithGlobals();n.setJsonMode(!!l.json),n.setDebug(!!l.debug||process.env[G]==="1"),o.setCommand(bo(a));}),Wt(s,i),Ct(s,i),Zt(s,i),Gt(s,i),Qt(s,i),er(s,i),tr(s,i),nr(s,i),dr(s,i),ar(s,i),fr(s,i);try{await s.parseAsync(r);}catch(c){yo(c,n);}}function yo(r,e){if(ot(r)){let t={status:"error",code:r.code,message:r.message,details:r.details};e.isJsonMode()?e.json(t):e.error(`${r.code}: ${r.message}`);}else r instanceof O?e.error(`${r.code}: ${r.message}`):r instanceof Error?e.error(r.message):e.error("Unknown error");process.exit(1);}(process.argv[1]===fileURLToPath(import.meta.url)||process.argv[1].endsWith("/bin/hbx")||process.argv[1].endsWith("/bin/hugoblox"))&&ho();function bo(r){if(!r)return ve;let e=[],t=r;for(;t?.parent;){let n=t.name?.();n&&e.unshift(n),t=t.parent;}return e.length?`${ve} ${e.join(" ")}`.trim():ve}async function vo(r){if(process.env[ee]&&r.config.getLicense()&&!r.config.getDeviceInfo())try{await L(r);}catch(e){r.logger.debug(`Skipping device registration warm-up: ${String(e)}`);}}
108
+
109
+ export { ho as bootstrap, po as createWelcomeScreenAPI, Y as getCliVersion };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hugoblox",
3
- "version": "0.4.1",
3
+ "version": "0.6.0",
4
4
  "description": "The official CLI for creating and managing HugoBlox sites.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -12,6 +12,10 @@
12
12
  ".": {
13
13
  "types": "./dist/index.d.ts",
14
14
  "import": "./dist/index.js"
15
+ },
16
+ "./welcome": {
17
+ "types": "./dist/lib/welcome-api.d.ts",
18
+ "import": "./dist/lib/welcome-api.js"
15
19
  }
16
20
  },
17
21
  "files": [
@@ -42,6 +46,7 @@
42
46
  "@biomejs/biome": "1.6.4",
43
47
  "@types/node": "20.11.20",
44
48
  "@types/keytar": "4.4.2",
49
+ "@types/prompts": "2.4.9",
45
50
  "@vitest/coverage-v8": "1.4.0",
46
51
  "cross-env": "7.0.3",
47
52
  "tsup": "8.0.1",
@@ -54,6 +59,7 @@
54
59
  "build:publish": "cross-env HBX_PUBLISH_BUILD=1 tsup",
55
60
  "dev": "tsx watch src/index.ts",
56
61
  "lint": "biome check .",
62
+ "lint:fix": "biome check --apply .",
57
63
  "format": "biome format --write .",
58
64
  "test": "vitest run",
59
65
  "test:watch": "vitest",