velloo 0.2.0 → 0.3.1

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.
Files changed (88) hide show
  1. package/README.md +1 -1
  2. package/canvas/assets/index-DZFrkC4P.js +29 -0
  3. package/canvas/assets/index-gB0StCrx.css +2 -0
  4. package/canvas/assets/lucide-all-Bu8-9UDF.js +1 -0
  5. package/canvas/assets/{radix-C3HaOB-d.js → radix-gmpYzWRH.js} +1 -1
  6. package/canvas/index.html +3 -3
  7. package/chunk-0s6drs3n.js +2 -0
  8. package/chunk-0zen7bzb.js +3 -0
  9. package/{chunk-er1fxb7d.js → chunk-1xb88e07.js} +1 -1
  10. package/{chunk-jv0xggh0.js → chunk-2e3nd4tz.js} +1 -1
  11. package/{chunk-08sv3k41.js → chunk-2fgfex9b.js} +1 -1
  12. package/{chunk-a84n72m2.js → chunk-2p37rp3j.js} +1 -1
  13. package/{chunk-ydhwv29b.js → chunk-2zbn6641.js} +2 -2
  14. package/{chunk-6w6ch4ce.js → chunk-3px2d1j1.js} +1 -1
  15. package/{chunk-2c7jbx46.js → chunk-4e9jh482.js} +1 -1
  16. package/{chunk-dgwjpbp9.js → chunk-4g250157.js} +1 -1
  17. package/{chunk-sjgqnykd.js → chunk-4ma2wnqw.js} +1 -1
  18. package/{chunk-x4f4k9pp.js → chunk-59zas5y2.js} +7 -7
  19. package/{chunk-0fe83yzt.js → chunk-5wv5a52y.js} +1 -1
  20. package/{chunk-bwnvcncx.js → chunk-6zz9kfbd.js} +1 -1
  21. package/{chunk-mh2pr7cq.js → chunk-7cvhnp9h.js} +1 -1
  22. package/{chunk-1h55s5he.js → chunk-7s8frnag.js} +1 -1
  23. package/chunk-7t13w87m.js +2 -0
  24. package/{chunk-zb6hc1y0.js → chunk-7yaafw73.js} +1 -1
  25. package/chunk-88eqqdk1.js +688 -0
  26. package/{chunk-ampmdzz4.js → chunk-8z3ykvcs.js} +1 -1
  27. package/{chunk-r22epxz2.js → chunk-98vvrj0c.js} +1 -1
  28. package/{chunk-vdkrbzh1.js → chunk-9cdwhrkh.js} +1 -1
  29. package/{chunk-xt5ayg7g.js → chunk-9p1dk5cb.js} +1 -1
  30. package/{chunk-dg6z4vdd.js → chunk-9psr8atb.js} +1 -1
  31. package/{chunk-4fj5tahv.js → chunk-9ydc34wc.js} +1 -1
  32. package/{chunk-pp86v6m6.js → chunk-as08hzp3.js} +1 -1
  33. package/chunk-asbajsmd.js +5 -0
  34. package/{chunk-vgmr98cv.js → chunk-avkfsc3p.js} +1 -1
  35. package/{chunk-ngnymg2q.js → chunk-bhx8k09w.js} +1 -1
  36. package/{chunk-3e8vez4d.js → chunk-c4fym47a.js} +63 -19
  37. package/{chunk-jb21jkp1.js → chunk-c6hywdvq.js} +1 -1
  38. package/{chunk-rpwpeph5.js → chunk-f0vsjfpc.js} +1 -1
  39. package/{chunk-ek71z2fv.js → chunk-g71v35gd.js} +1 -1
  40. package/{chunk-80r93t2e.js → chunk-hfzg98c8.js} +1 -1
  41. package/{chunk-jfx9xjmr.js → chunk-js6mz7h1.js} +1 -1
  42. package/{chunk-4dn75m6t.js → chunk-mfbxfb9b.js} +1 -1
  43. package/{chunk-mnapdp1y.js → chunk-mt8ds8zj.js} +1 -1
  44. package/chunk-n9t36h0q.js +2 -0
  45. package/{chunk-fsbsksga.js → chunk-nev5mv85.js} +1 -1
  46. package/{chunk-40xgcth8.js → chunk-nte0t7v3.js} +2 -2
  47. package/{chunk-hcb68zt7.js → chunk-p62fygqz.js} +1 -1
  48. package/{chunk-ayrb3qnm.js → chunk-p6e1eg76.js} +1 -1
  49. package/{chunk-7eq7nx37.js → chunk-ph2jfg1x.js} +1 -1
  50. package/{chunk-n9gs6yx9.js → chunk-pve2mmnr.js} +1 -1
  51. package/{chunk-avns4dbf.js → chunk-sn9vg42q.js} +1 -1
  52. package/{chunk-dy41n60j.js → chunk-t2w8a591.js} +5 -5
  53. package/{chunk-735ezz23.js → chunk-t5m5mznb.js} +2 -2
  54. package/{chunk-6nqghjda.js → chunk-t947mxr1.js} +1 -1
  55. package/chunk-tt7fys2w.js +2 -0
  56. package/chunk-wafb2xm9.js +6 -0
  57. package/{chunk-sn3x2m0m.js → chunk-whw3epmr.js} +1 -1
  58. package/{chunk-j11fyeqh.js → chunk-wndntr7s.js} +1 -1
  59. package/{chunk-7eyb8yrf.js → chunk-wnwgqvtd.js} +1 -1
  60. package/{chunk-v40fn08f.js → chunk-wp123630.js} +2 -2
  61. package/{chunk-ccszg02q.js → chunk-wvs939t5.js} +1 -1
  62. package/{chunk-jr0ss0mm.js → chunk-z21hg6j3.js} +2 -2
  63. package/cli.js +1 -1
  64. package/package.json +1 -1
  65. package/pkgs/helpers/src/descriptors.ts +7 -1
  66. package/pkgs/provider-antd/src/index.ts +1 -0
  67. package/pkgs/provider-chakra/src/index.ts +1 -0
  68. package/pkgs/provider-mui/src/index.ts +1 -0
  69. package/pkgs/provider-none/src/index.ts +42 -1
  70. package/pkgs/provider-none/src/intro.ts +2 -2
  71. package/pkgs/schema/src/config.ts +19 -0
  72. package/pkgs/schema/src/extension.ts +26 -0
  73. package/pkgs/schema/src/index.ts +10 -0
  74. package/pkgs/schema/src/node.ts +99 -0
  75. package/pkgs/schema/src/screen.ts +7 -0
  76. package/pkgs/shadcn-snapshot/dist/manifest.json +9 -1
  77. package/skills/velloo-setup/SKILL.md +70 -48
  78. package/canvas/assets/index-BCvDZiBo.css +0 -2
  79. package/canvas/assets/index-ClaBqcaS.js +0 -28
  80. package/canvas/assets/lucide-all-iIF-pmzN.js +0 -1
  81. package/chunk-308ynsxd.js +0 -2
  82. package/chunk-9vg3yafz.js +0 -3
  83. package/chunk-eyr8hrzf.js +0 -2
  84. package/chunk-k17gfwq3.js +0 -2
  85. package/chunk-rgasy862.js +0 -5
  86. package/chunk-t448rrfr.js +0 -2
  87. package/chunk-tdsk5wxa.js +0 -451
  88. package/chunk-vsv8z729.js +0 -6
@@ -1,9 +1,9 @@
1
1
  // @bun
2
- import{isCancel,confirm,log,cancel,note,spinner,select,text}from"./chunk-80fjfyha.js";import{designNameIssue,CURRENT_SCHEMA_VERSION,schemaVersionOf,APP_PATH_PREFIX,planMigration,ConfigSchema}from"./chunk-40xgcth8.js";import{writeJsonAtomic,managedDesignPath,managedDesignId,localDesignOf,writeLocalDesign,removeLocalDesign,resolveAppPath,recordedDesignName,pickDesign,readRepoFeedback,writeRepoFeedback,writeFeedbackContactOk,loadDesignFolder,hostAppRootFrom,topUpTokens}from"./chunk-tdsk5wxa.js";import{CHROMIUM_INSTALL_CMD,chromiumExecutable}from"./chunk-3e8vez4d.js";import{PROJECT_AGENT_IDS,resolveProjectRoot,manualSetupText,askAgentWiring,globallyWiredAgents,connect}from"./chunk-vgmr98cv.js";import{TOOL_VERSION}from"./chunk-dg6z4vdd.js";import{fail}from"./chunk-s23gtejf.js";import{daemonRoot,ensureDaemon,stopDaemon}from"./chunk-sjgqnykd.js";import{installedVellooBin,updateStatus,upgradeInstalledVelloo}from"./chunk-xt5ayg7g.js";import{dirExists,discoverScanRoots,scanApps,primaryApp,buildScreensFromScan,buildBoardsFromScan,DEFAULT_THEME_PRESET,isValidPreset,WIZARD_PROVIDERS,LIBRARY_IDS,DEFAULT_LIBRARY_ID,interactiveLibraryChoices,planInstall,scanAdoption,DEFAULT_STACK_ID,stackForFramework,isValidStack,importThemeFromMui,findMuiTheme,importThemeFromGlobals,isEmptyOrMissing,resolveTheme,buildScaffold,writeScaffold,displayPath,printSummary,printExitInstructions,printScreenshotReadiness,promptShellCompletions}from"./chunk-dy41n60j.js";import{loadCredential,saveCredential}from"./chunk-80r93t2e.js";import{installChromiumInteractive}from"./chunk-08sv3k41.js";import{detectHost,findComponentsDir}from"./chunk-fsbsksga.js";import{copyToClipboard}from"./chunk-vbk1qhdz.js";import{upgradeFolder}from"./chunk-7eq7nx37.js";import{findManifest,manifestListing,findDesigns,withoutDesign,isWithin,findGitRoot,chooseDesignName,registerDesign,registerLocalDesign,existingDesignFolder,isDesignFolderSync,isEmptyOrMissingSync,hasDesignConfig,resolveDesign}from"./chunk-vsv8z729.js";import{verifyCredential,performDeviceLogin}from"./chunk-bwnvcncx.js";import{openUrl}from"./chunk-hggap8fq.js";import{defaultCloudUrl}from"./chunk-pp86v6m6.js";import{require_picocolors}from"./chunk-jrsknzjt.js";import{__toESM,defineCommand}from"./chunk-2smsg2ct.js";import{randomUUID as randomUUID2}from"crypto";import{dirname as dirname2,relative as relative5,resolve as resolve7}from"path";var import_picocolors11=__toESM(require_picocolors(),1);import{existsSync}from"fs";import{readFile}from"fs/promises";import{join,relative,resolve,sep}from"path";function isSentinel(path){return path==="binary"||path.startsWith("~/")}function toPosix(path){return path.split(sep).join("/")||"."}function isUnder(parent,child){let rel=relative(parent,child);return rel===""||!rel.startsWith("..")&&!/^(?:[\\/]|[A-Za-z]:)/.test(rel)}async function readConfig(folder){return ConfigSchema.parse(JSON.parse(await readFile(join(folder,".design","config.json"),"utf8")))}async function recordedAppRoot(designFolder){let folder=resolve(designFolder),local=localDesignOf(folder);if(local){let path=resolve(local.appRoot);return{path,kind:"local",localId:local.id,checkout:local.root,...appShape(path)}}let recorded=(await readConfig(folder)).hostApp?.root,path=recorded&&!isSentinel(recorded)?resolveAppPath(folder,recorded):resolve(folder,".."),found=await manifestListing(folder).catch(()=>null),designName=recordedDesignName(folder);return{path,kind:"in-repo",...designName?{designName}:{},...found?{manifestPath:found.path}:{},...appShape(path)}}function appShape(path){let exists=existsSync(path);return{exists,looksLikeApp:exists&&existsSync(join(path,"package.json"))}}async function planAppRootChange(designFolder,newRoot){let folder=resolve(designFolder),current=await recordedAppRoot(folder),to=resolve(newRoot),change={from:current.path,to,rewritten:[]};if(current.kind==="local"&&current.localId&&current.checkout){if(!isUnder(current.checkout,to))throw Error(`An application root must live inside the design's checkout (${current.checkout}). ${to} is outside it.`);return change.local={id:current.localId,root:current.checkout},change}let config=await readConfig(folder),reanchor=(field,stored)=>{if(isSentinel(stored))return;let abs=resolveAppPath(folder,stored);if(!isUnder(current.path,abs))return;let next=toPosix(relative(folder,join(to,relative(current.path,abs))));if(next===stored)return;return change.rewritten.push({field,from:stored,to:next}),next};if(config.hostApp)reanchor("hostApp.root",config.hostApp.root);else change.rewritten.push({field:"hostApp.root",from:"(unset)",to:toPosix(relative(folder,to))});for(let[name,app]of Object.entries(config.hostApps??{}))reanchor(`hostApps.${name}.root`,app.root);for(let[name,library]of Object.entries(config.libraries))if(library.source==="in-repo")reanchor(`libraries.${name}.componentsPath`,library.componentsPath);return change}async function applyAppRootChange(designFolder,change){let folder=resolve(designFolder);if(change.local){await writeLocalDesign(change.local.id,{root:change.local.root,appRoot:change.to,...managedDesignId(folder)?{}:{designPath:folder}});return}let config=await readConfig(folder),patch=new Map(change.rewritten.map((r)=>[r.field,r.to])),hostRoot=patch.get("hostApp.root");if(hostRoot!==void 0)config.hostApp=config.hostApp?{...config.hostApp,root:hostRoot}:{root:hostRoot};for(let[name,app]of Object.entries(config.hostApps??{})){let next=patch.get(`hostApps.${name}.root`);if(next!==void 0)app.root=next}for(let[name,library]of Object.entries(config.libraries)){let next=patch.get(`libraries.${name}.componentsPath`);if(next!==void 0)library.componentsPath=next}await writeJsonAtomic(join(folder,".design","config.json"),ConfigSchema.parse(config))}async function appRootCandidates(near){let start=resolve(near),from=(await findManifest(start).catch(()=>null))?.dir??await findGitRoot(start)??start;return(await discoverScanRoots(from)).map((app)=>({dir:app.dir,rel:relative(from,app.dir)||"."}))}async function appRootIsNotAnApp(appRoot){let abs=resolve(appRoot);if(existsSync(join(abs,"package.json"))&&(await discoverScanRoots(abs))[0]?.dir===abs)return null;let candidates=(await appRootCandidates(abs)).filter((c)=>c.dir!==abs);return candidates.length>0?candidates:null}async function promptAppRootChoice(current,opts={}){let message=opts.message??"Which application is this design for?",currentHint=opts.currentHint??"recorded now",abs=resolve(current),candidates=await appRootCandidates(abs),options=candidates.map((c)=>({value:c.dir,label:c.rel,...c.dir===abs?{hint:currentHint}:{}}));if(!candidates.some((c)=>c.dir===abs))options.push({value:abs,label:current,hint:currentHint});if(options.length<=1)return null;let chosen=await select({message,options});if(isCancel(chosen))return null;return chosen}import{createHash,randomUUID}from"crypto";import{existsSync as existsSync2,lstatSync,realpathSync}from"fs";import{cp,lstat,mkdir,readdir,readFile as readFile2,rm}from"fs/promises";import{basename,dirname,join as join2,relative as relative2,resolve as resolve2,sep as sep2}from"path";function physicalDestination(path){if(lstatSync(path,{throwIfNoEntry:!1}))return realpathSync(path);let parent=dirname(path);return join2(physicalDestination(parent),relative2(parent,path))}var RUNTIME=[".design/cache",".velloo","node_modules"];async function rebaseDesignConfig(source,destination,appRoot,external){let config=ConfigSchema.parse(JSON.parse(await readFile2(join2(destination,".design/config.json"),"utf8"))),rebase=(path)=>{if(path==="binary"||path.startsWith("~/"))return path;let abs=resolveAppPath(source,path),withinDesign=relative2(source,abs);if(withinDesign===""||withinDesign!==".."&&!withinDesign.startsWith(`..${sep2}`)&&!/^(?:[\\/]|[A-Za-z]:)/.test(withinDesign))return withinDesign.split(sep2).join("/")||".";return external?`${APP_PATH_PREFIX}${relative2(appRoot,abs).split(sep2).join("/")||"."}`:relative2(destination,abs).split(sep2).join("/")||"."};config.hostApp=config.hostApp?{...config.hostApp,root:rebase(config.hostApp.root)}:{root:rebase("..")};for(let app of Object.values(config.hostApps??{}))app.root=rebase(app.root);for(let library of Object.values(config.libraries))if(library.componentsPath)library.componentsPath=rebase(library.componentsPath);await writeJsonAtomic(join2(destination,".design/config.json"),config)}async function fingerprint(folder){let hash=createHash("sha256");async function walk(rel){if(rel===".git"||RUNTIME.some((p)=>rel===p||rel.startsWith(`${p}/`)))return;let path=join2(folder,rel),stat=await lstat(path);if(stat.isSymbolicLink())throw Error(`Cannot relocate symbolic link ${path}. Replace it with its actual design content first.`);if(stat.isDirectory())for(let name of(await readdir(path)).sort())await walk(rel?`${rel}/${name}`:name);else if(stat.isFile())hash.update(rel).update("\x00").update(await readFile2(path));else throw Error(`Unsupported filesystem entry: ${path}`)}return await walk(""),hash.digest("hex")}async function planRelocation(source,cwd,to,external){if(Boolean(to)===external)throw Error("Choose exactly one destination: --external or --to <path>.");let designs=await findDesigns(cwd),physicalSource=realpathSync(source),local=designs?.local.find((design)=>realOr(design.designRoot)===physicalSource),repo=designs?.repo??null,index=repo?.folders.findIndex((path)=>existsSync2(path)&&realpathSync(path)===physicalSource)??-1;if(!local&&index===-1)throw Error("Run relocation from the checkout the design belongs to; the source must be one of its designs.");let root=local?.root??repo?.dir,name=recordedDesignName(source);if(!name)throw Error(`${source} has no design name \u2014 run \`velloo upgrade\` first.`);let physicalRoot=realpathSync(root);if(physicalSource===physicalRoot)throw Error("Cannot relocate the application repository itself. Use a separate design directory.");if(external&&managedDesignId(source))throw Error("This design already lives in managed storage.");let destination=external?managedDesignPath(local?.id??randomUUID()):resolve2(cwd,to??""),physicalTarget=physicalDestination(destination),inRepo=!external&&isWithin(physicalRoot,physicalTarget);if(physicalTarget===physicalSource||physicalTarget.startsWith(physicalSource+sep2)||physicalSource.startsWith(physicalTarget+sep2))throw Error("Source and destination cannot contain each other.");if(existsSync2(destination))throw Error(`Destination is occupied: ${destination}. Choose a new path; nothing was changed.`);let manifestPath=repo?.path??join2(root,"velloo.json"),before=repo?await readFile2(repo.path,"utf8"):null,manifest,destinationManifestPath;if(repo?.legacy)throw Error(`${repo.path} is in the pre-designs format \u2014 run \`velloo upgrade\` first.`);if(inRepo){destinationManifestPath=relative2(physicalRoot,physicalTarget).split(sep2).join("/")||".";let designs=[...repo?.manifest.designs??[]];if(index===-1)designs.push(destinationManifestPath);else designs[index]=destinationManifestPath;manifest={...repo?.manifest??{},designs}}else if(repo&&index!==-1)manifest=withoutDesign(repo.manifest,index,name);let config=ConfigSchema.parse(JSON.parse(await readFile2(join2(source,".design/config.json"),"utf8")));return{source,destination,root,appRoot:local?.appRoot??hostAppRootFrom(source,config.hostApp),designName:name,sourceLocalId:local?.id,sourceManifestPath:index===-1?void 0:repo?.manifest.designs[index],destinationManifestPath,destinationLocal:inRepo?void 0:{id:local?.id??(external?basename(destination):randomUUID()),...external?{}:{designPath:destination}},manifestPath,before,manifest}}function realOr(path){return existsSync2(path)?realpathSync(path):resolve2(path)}async function relocateDesign(plan){await stopDaemon(daemonRoot(plan.source));let original=await fingerprint(plan.source);await mkdir(dirname(plan.destination),{recursive:!0}),await mkdir(plan.destination);let committed=!1;try{for(let entry of await readdir(plan.source))await cp(join2(plan.source,entry),join2(plan.destination,entry),{recursive:!0,errorOnExist:!0,force:!1,filter:(path)=>!RUNTIME.some((p)=>{let rel=relative2(plan.source,path).split(sep2).join("/");return rel===p||rel.startsWith(`${p}/`)})});if(await fingerprint(plan.destination)!==original)throw Error("Copied content did not match the source.");if(await rebaseDesignConfig(plan.source,plan.destination,plan.appRoot,plan.destinationLocal!==void 0),await loadDesignFolder(plan.destination,{preferences:!1}),await fingerprint(plan.source)!==original)throw Error("The source changed during relocation. Close editors and retry.");if(await readFile2(plan.manifestPath,"utf8").catch(()=>null)!==plan.before)throw Error("The manifest changed during relocation. Review it and retry.");if(plan.destinationLocal){if(plan.sourceLocalId&&plan.sourceLocalId!==plan.destinationLocal.id)await removeLocalDesign(plan.sourceLocalId);await writeLocalDesign(plan.destinationLocal.id,{root:plan.root,appRoot:plan.appRoot,...plan.destinationLocal.designPath?{designPath:plan.destinationLocal.designPath}:{}})}else if(plan.sourceLocalId)await removeLocalDesign(plan.sourceLocalId);if(plan.manifest===null)await rm(plan.manifestPath,{force:!0});else if(plan.manifest)await writeJsonAtomic(plan.manifestPath,plan.manifest);committed=!0}finally{if(!committed)await rm(plan.destination,{recursive:!0,force:!0})}try{await rm(plan.source,{recursive:!0})}catch(error){throw Error(`Relocation completed to ${plan.destination}, but the old copy remains at ${plan.source}. Remove it after checking the destination. ${error}`)}}var import_picocolors=__toESM(require_picocolors(),1);async function offerUpgradeBeforeInit(argv){if(process.env.VELLOO_DISABLE_UPDATE_CHECK==="1"||!process.stdin.isTTY)return!1;let status;try{status=await updateStatus({refresh:!0})}catch{return!1}if(!status.available||!status.upgradable||!status.latest)return!1;console.log(""),console.log(`${import_picocolors.default.yellow("A newer velloo is available:")} ${status.current} \u2192 ${import_picocolors.default.bold(status.latest)}`);let yes=await confirm({message:"Upgrade before setting up this design folder?",initialValue:!0});if(isCancel(yes)||!yes)return console.log(import_picocolors.default.dim(" Continuing on the current version.")),!1;try{if(!(await upgradeInstalledVelloo()).upgraded)return!1;let bin=installedVellooBin();if(!bin)return console.log(import_picocolors.default.dim(" Upgraded. Run `velloo init` again to continue on the new version.")),!0;let code=await Bun.spawn([bin,...argv],{stdin:"inherit",stdout:"inherit",stderr:"inherit",env:{...process.env,VELLOO_DISABLE_UPDATE_CHECK:"1"}}).exited;process.exit(code)}catch(error){return console.log(import_picocolors.default.yellow(` Could not upgrade (${error instanceof Error?error.message:String(error)}) \u2014 continuing on the current version.`)),!1}}import{resolve as resolve3}from"path";var START_TO_CONTENT={"redesign-screen":"redesign-screen","redesign-component":"component",custom:"custom",sample:"sample",blank:"blank",scratch:"sample",scan:"scan"};function isValidLibraryId(v){return LIBRARY_IDS.includes(v)}function isValidContent(v){return v==="sample"||v==="blank"||v==="scan"||v==="redesign-screen"||v==="component"||v==="custom"}function isValidStart(v){return v in START_TO_CONTENT}function shouldRunWizard(args,stdinIsTTY){if(args.nonInteractive)return!1;return stdinIsTTY}function answersFromArgs(args){if(args.library&&!isValidLibraryId(args.library))throw Error(`unknown --library ${JSON.stringify(args.library)}. Valid: ${LIBRARY_IDS.join(" | ")}.`);if(args.start&&!isValidStart(args.start))throw Error(`unknown --start ${JSON.stringify(args.start)}. Valid: redesign-screen | redesign-component | custom | sample | blank | scan.`);if(args.initialContent&&!isValidContent(args.initialContent))throw Error(`unknown --initial-content ${JSON.stringify(args.initialContent)}. Valid: sample | blank | scan | redesign-screen | component | custom.`);let themePreset=args.themePreset?.trim()||void 0;if(themePreset&&!isValidPreset(themePreset))throw Error(`unknown --theme-preset ${JSON.stringify(args.themePreset)}.`);let stack=args.stack?.trim()||void 0;if(stack&&!isValidStack(stack))throw Error(`unknown --stack ${JSON.stringify(args.stack)}. Valid: nextjs | vite | astro | remix.`);let appRoot=resolve3(args.folder??"."),folder=resolve3(appRoot,args.designFolder??"velloo"),library=args.library??DEFAULT_LIBRARY_ID,initialContent=args.initialContent??"sample";if(args.start)initialContent=START_TO_CONTENT[args.start]??initialContent;let screenName=args.screenName?.trim()||void 0,componentDescription=args.component?.trim()||void 0,customRequest=args.request?.trim()||void 0;if(initialContent==="component"&&!componentDescription)throw Error("--component is required for --start=redesign-component.");if(initialContent==="custom"&&!customRequest)throw Error("--request is required for --start=custom.");if(initialContent==="redesign-screen"&&!screenName&&args.start==="redesign-screen");let source=WIZARD_PROVIDERS[library].defaultSource;return{appRoot,scanRoot:appRoot,folder,library,source,componentsRelative:args.componentsDir??"src/components/ui",initialContent,themePreset,...stack?{stack}:{},...screenName?{screenName}:{},...componentDescription?{componentDescription}:{},...customRequest?{customRequest}:{}}}import{readFile as readFile3,rm as rm2,writeFile}from"fs/promises";import{tmpdir}from"os";import{join as join3}from"path";var import_picocolors2=__toESM(require_picocolors(),1);import{relative as relative3}from"path";var SCREENS_PLACEHOLDER="{{screens}}";function libraryLabel(library){return WIZARD_PROVIDERS[library].handoffComponentsLabel}function appContextLines(answers){let lines=[],appRels=[...new Set((answers.selectedRoutes??[]).map((r)=>r.appRel).filter(Boolean))];if(appRels.length>1)lines.push(`This is a monorepo with ${appRels.length} apps (${appRels.map((r)=>`\`${r}\``).join(", ")}); each app's screens sit on their own board. Run the matching app's dev server when comparing screens.`);else{let uiRel=relative3(answers.appRoot,answers.scanRoot);if(uiRel&&!uiRel.startsWith(".."))lines.push(`This app's UI lives in \`${uiRel}/\` \u2014 run its dev server from there.`)}return lines}function setupFirstGuidance(){return"**Calibrate before you design** \u2014 run the **velloo-setup** skill first. The canvas draws with Velloo's own components themed by this folder's tokens, not this repo's component files, so an uncalibrated board won't look like my app. Import my stylesheet, set the real fonts, match the custom components that appear in what you're designing, and get the app running so there's something real to check against. Tell me in a line what you matched and what stayed unverified."}function compareGuidance(){return"Compare against the real app, not memory: iterate with `screenshot` + `compare_to_url` at the same viewport and fix the `topMismatches` in the order they're ranked. If a capture comes back `unverified`, the similarity number means nothing \u2014 and if the reason is a login wall, call `start_capture_session` so I can sign in and capture the page for you, then verify against it with `compare_to_url { captureId }`."}function captureSiteGuidance(url){return[`**Start by capturing the site.** Call \`start_capture_session { url: "${url}" }\` \u2014 it opens a browser window I drive. I'll log in if needed and hit "Capture page" on each page worth designing from; the call returns straight away, so poll \`list_captures\` until my captures appear.`,"Then read each one with `get_capture`: it gives you a structural outline (repeated blocks are marked \u2014 those are your component candidates), the site's CSS custom properties as `import_theme`-ready CSS, and its images. Run `import_theme` with that CSS before composing so the real tokens resolve.","Re-express the pages with real components against the theme \u2014 do not transcribe the DOM node-for-node. Verify each screen with `compare_to_url { captureId }`."].join(`
3
- `)}function exploreAlternativesGuidance(){return"After the faithful recreation, explore alternatives on the same board \u2014 side-by-side A/B/C (or more) frames or grouped explorations of different directions. Velloo's strength is comparing options visually; do not stop at a single redesign."}function wantsHandoff(answers){switch(answers.initialContent){case"scan":case"redesign-screen":case"component":case"custom":return!0;default:return!1}}function buildHandoffPrompt(answers,screens,boards){switch(answers.initialContent){case"redesign-screen":return buildRedesignScreenHandoff(answers,screens);case"component":return buildComponentHandoff(answers);case"custom":return buildCustomHandoff(answers);case"scan":return buildLegacyScanHandoff(answers,screens,boards);default:return buildLegacyScanHandoff(answers,screens,boards)}}function buildRedesignScreenHandoff(answers,screens){let name=answers.screenName?.trim()||screens[0]?.name||screens[0]?.id||"the chosen screen",lines=[`Recreate and then explore alternatives for **${name}** as a Velloo design. Work entirely through the velloo MCP tools.`,...appContextLines(answers)];if(screens.length>0)lines.push(`A placeholder screen is already scaffolded (desktop + mobile frames \u2014 edits sync). Design it with the project's ${libraryLabel(answers.library)} components:`,SCREENS_PLACEHOLDER);else lines.push(`- Create a Velloo screen for **${name}** using the project's ${libraryLabel(answers.library)} components.`);return lines.push(`1. ${setupFirstGuidance()}`,`2. **Recreate** the real page faithfully. ${compareGuidance()}`,`3. **Then explore alternatives** \u2014 ${exploreAlternativesGuidance()}`),lines.join(`
4
- `)}function buildComponentHandoff(answers){let target=answers.componentDescription?.trim()||"the component";return[`Redesign **${target}** in Velloo \u2014 recreate it, then explore alternatives. Work entirely through the velloo MCP tools.`,...appContextLines(answers),`A component-focused board is scaffolded. Target: **${target}**. Prefer a snippet if the piece should be reused.`,`1. ${setupFirstGuidance()}`,`2. **Recreate** the current component faithfully with the project's ${libraryLabel(answers.library)} components. ${compareGuidance()}`,`3. **Then explore alternatives** \u2014 ${exploreAlternativesGuidance()}`].join(`
5
- `)}function buildCustomHandoff(answers){return["Design this in Velloo. Work entirely through the velloo MCP tools.","","**Request:**",answers.customRequest?.trim()||"the design I described","",...answers.captureUrl?[captureSiteGuidance(answers.captureUrl),""]:[],setupFirstGuidance(),`Then design it with the project's ${libraryLabel(answers.library)} components. When comparing options, put alternatives side-by-side on the board (explorations) instead of overwriting a single direction.`].join(`
6
- `)}function buildLegacyScanHandoff(answers,screens,boards){let lines=["Recreate this app's pages as a Velloo design \u2014 one Velloo screen per page, faithful to the real UI. Work entirely through the velloo MCP tools (wired during setup)."];if(lines.push(...appContextLines(answers)),lines.push(setupFirstGuidance()),screens.length>0){if(lines.push(`These screens are already scaffolded from the app's routes \u2014 design these, and only these, with the project's ${libraryLabel(answers.library)} components:`,SCREENS_PLACEHOLDER),answers.agentPicksFirst)lines.push("Start with the highest-impact screen (usually the landing page or the main dashboard) and design it fully \u2014 it sets the quality bar. Then work through the rest.")}else lines.push(`- For each route/page in the app, create a Velloo screen that reproduces that page's UI from the project's ${libraryLabel(answers.library)} components.`);if(lines.push("- Each screen already has a desktop and a mobile frame on its board (same screen \u2014 edits sync); make sure layouts hold at both widths."),boards.some((b)=>b.groups.length>0))lines.push("- Frames are pre-grouped by route section \u2014 keep the groups tidy as flows evolve.");return lines.push(`- ${compareGuidance()}`),lines.join(`
2
+ import{isCancel,confirm,log,cancel,note,spinner,select,text}from"./chunk-80fjfyha.js";import{designNameIssue,CURRENT_SCHEMA_VERSION,schemaVersionOf,APP_PATH_PREFIX,planMigration,ConfigSchema}from"./chunk-nte0t7v3.js";import{writeJsonAtomic,managedDesignPath,managedDesignId,localDesignOf,writeLocalDesign,removeLocalDesign,resolveAppPath,recordedDesignName,pickDesign,readRepoFeedback,writeRepoFeedback,writeFeedbackContactOk,loadDesignFolder,hostAppRootFrom,topUpTokens}from"./chunk-88eqqdk1.js";import{CHROMIUM_INSTALL_CMD,chromiumExecutable}from"./chunk-c4fym47a.js";import{PROJECT_AGENT_IDS,resolveProjectRoot,manualSetupText,askAgentWiring,globallyWiredAgents,connect}from"./chunk-avkfsc3p.js";import{TOOL_VERSION}from"./chunk-9psr8atb.js";import{fail}from"./chunk-s23gtejf.js";import{daemonRoot,ensureDaemon,stopDaemon}from"./chunk-4ma2wnqw.js";import{installedVellooBin,updateStatus,upgradeInstalledVelloo}from"./chunk-9p1dk5cb.js";import{dirExists,discoverScanRoots,scanApps,primaryApp,buildScreensFromScan,buildBoardsFromScan,DEFAULT_THEME_PRESET,isValidPreset,WIZARD_PROVIDERS,LIBRARY_IDS,DEFAULT_LIBRARY_ID,interactiveLibraryChoices,planInstall,scanAdoption,DEFAULT_STACK_ID,stackForFramework,isValidStack,importThemeFromMui,findMuiTheme,importThemeFromGlobals,isEmptyOrMissing,resolveTheme,buildScaffold,writeScaffold,displayPath,printSummary,printExitInstructions,printScreenshotReadiness,promptShellCompletions}from"./chunk-t2w8a591.js";import{loadCredential,saveCredential}from"./chunk-hfzg98c8.js";import{installChromiumInteractive}from"./chunk-2fgfex9b.js";import{detectHost,findComponentsDir}from"./chunk-nev5mv85.js";import{copyToClipboard}from"./chunk-vbk1qhdz.js";import{upgradeFolder}from"./chunk-ph2jfg1x.js";import{findManifest,manifestListing,findDesigns,withoutDesign,isWithin,findGitRoot,chooseDesignName,registerDesign,registerLocalDesign,existingDesignFolder,isDesignFolderSync,isEmptyOrMissingSync,hasDesignConfig,resolveDesign}from"./chunk-wafb2xm9.js";import{verifyCredential,performDeviceLogin}from"./chunk-6zz9kfbd.js";import{openUrl}from"./chunk-hggap8fq.js";import{defaultCloudUrl}from"./chunk-as08hzp3.js";import{require_picocolors}from"./chunk-jrsknzjt.js";import{__toESM,defineCommand}from"./chunk-2smsg2ct.js";import{randomUUID as randomUUID2}from"crypto";import{dirname as dirname2,relative as relative5,resolve as resolve7}from"path";var import_picocolors11=__toESM(require_picocolors(),1);import{existsSync}from"fs";import{readFile}from"fs/promises";import{join,relative,resolve,sep}from"path";function isSentinel(path){return path==="binary"||path.startsWith("~/")}function toPosix(path){return path.split(sep).join("/")||"."}function isUnder(parent,child){let rel=relative(parent,child);return rel===""||!rel.startsWith("..")&&!/^(?:[\\/]|[A-Za-z]:)/.test(rel)}async function readConfig(folder){return ConfigSchema.parse(JSON.parse(await readFile(join(folder,".design","config.json"),"utf8")))}async function recordedAppRoot(designFolder){let folder=resolve(designFolder),local=localDesignOf(folder);if(local){let path=resolve(local.appRoot);return{path,kind:"local",localId:local.id,checkout:local.root,...appShape(path)}}let recorded=(await readConfig(folder)).hostApp?.root,path=recorded&&!isSentinel(recorded)?resolveAppPath(folder,recorded):resolve(folder,".."),found=await manifestListing(folder).catch(()=>null),designName=recordedDesignName(folder);return{path,kind:"in-repo",...designName?{designName}:{},...found?{manifestPath:found.path}:{},...appShape(path)}}function appShape(path){let exists=existsSync(path);return{exists,looksLikeApp:exists&&existsSync(join(path,"package.json"))}}async function planAppRootChange(designFolder,newRoot){let folder=resolve(designFolder),current=await recordedAppRoot(folder),to=resolve(newRoot),change={from:current.path,to,rewritten:[]};if(current.kind==="local"&&current.localId&&current.checkout){if(!isUnder(current.checkout,to))throw Error(`An application root must live inside the design's checkout (${current.checkout}). ${to} is outside it.`);return change.local={id:current.localId,root:current.checkout},change}let config=await readConfig(folder),reanchor=(field,stored)=>{if(isSentinel(stored))return;let abs=resolveAppPath(folder,stored);if(!isUnder(current.path,abs))return;let next=toPosix(relative(folder,join(to,relative(current.path,abs))));if(next===stored)return;return change.rewritten.push({field,from:stored,to:next}),next};if(config.hostApp)reanchor("hostApp.root",config.hostApp.root);else change.rewritten.push({field:"hostApp.root",from:"(unset)",to:toPosix(relative(folder,to))});for(let[name,app]of Object.entries(config.hostApps??{}))reanchor(`hostApps.${name}.root`,app.root);for(let[name,library]of Object.entries(config.libraries))if(library.source==="in-repo")reanchor(`libraries.${name}.componentsPath`,library.componentsPath);return change}async function applyAppRootChange(designFolder,change){let folder=resolve(designFolder);if(change.local){await writeLocalDesign(change.local.id,{root:change.local.root,appRoot:change.to,...managedDesignId(folder)?{}:{designPath:folder}});return}let config=await readConfig(folder),patch=new Map(change.rewritten.map((r)=>[r.field,r.to])),hostRoot=patch.get("hostApp.root");if(hostRoot!==void 0)config.hostApp=config.hostApp?{...config.hostApp,root:hostRoot}:{root:hostRoot};for(let[name,app]of Object.entries(config.hostApps??{})){let next=patch.get(`hostApps.${name}.root`);if(next!==void 0)app.root=next}for(let[name,library]of Object.entries(config.libraries)){let next=patch.get(`libraries.${name}.componentsPath`);if(next!==void 0)library.componentsPath=next}await writeJsonAtomic(join(folder,".design","config.json"),ConfigSchema.parse(config))}async function appRootCandidates(near){let start=resolve(near),from=(await findManifest(start).catch(()=>null))?.dir??await findGitRoot(start)??start;return(await discoverScanRoots(from)).map((app)=>({dir:app.dir,rel:relative(from,app.dir)||"."}))}async function appRootIsNotAnApp(appRoot){let abs=resolve(appRoot);if(existsSync(join(abs,"package.json"))&&(await discoverScanRoots(abs))[0]?.dir===abs)return null;let candidates=(await appRootCandidates(abs)).filter((c)=>c.dir!==abs);return candidates.length>0?candidates:null}async function promptAppRootChoice(current,opts={}){let message=opts.message??"Which application is this design for?",currentHint=opts.currentHint??"recorded now",abs=resolve(current),candidates=await appRootCandidates(abs),options=candidates.map((c)=>({value:c.dir,label:c.rel,...c.dir===abs?{hint:currentHint}:{}}));if(!candidates.some((c)=>c.dir===abs))options.push({value:abs,label:current,hint:currentHint});if(options.length<=1)return null;let chosen=await select({message,options});if(isCancel(chosen))return null;return chosen}import{createHash,randomUUID}from"crypto";import{existsSync as existsSync2,lstatSync,realpathSync}from"fs";import{cp,lstat,mkdir,readdir,readFile as readFile2,rm}from"fs/promises";import{basename,dirname,join as join2,relative as relative2,resolve as resolve2,sep as sep2}from"path";function physicalDestination(path){if(lstatSync(path,{throwIfNoEntry:!1}))return realpathSync(path);let parent=dirname(path);return join2(physicalDestination(parent),relative2(parent,path))}var RUNTIME=[".design/cache",".velloo","node_modules"];async function rebaseDesignConfig(source,destination,appRoot,external){let config=ConfigSchema.parse(JSON.parse(await readFile2(join2(destination,".design/config.json"),"utf8"))),rebase=(path)=>{if(path==="binary"||path.startsWith("~/"))return path;let abs=resolveAppPath(source,path),withinDesign=relative2(source,abs);if(withinDesign===""||withinDesign!==".."&&!withinDesign.startsWith(`..${sep2}`)&&!/^(?:[\\/]|[A-Za-z]:)/.test(withinDesign))return withinDesign.split(sep2).join("/")||".";return external?`${APP_PATH_PREFIX}${relative2(appRoot,abs).split(sep2).join("/")||"."}`:relative2(destination,abs).split(sep2).join("/")||"."};config.hostApp=config.hostApp?{...config.hostApp,root:rebase(config.hostApp.root)}:{root:rebase("..")};for(let app of Object.values(config.hostApps??{}))app.root=rebase(app.root);for(let library of Object.values(config.libraries))if(library.componentsPath)library.componentsPath=rebase(library.componentsPath);await writeJsonAtomic(join2(destination,".design/config.json"),config)}async function fingerprint(folder){let hash=createHash("sha256");async function walk(rel){if(rel===".git"||RUNTIME.some((p)=>rel===p||rel.startsWith(`${p}/`)))return;let path=join2(folder,rel),stat=await lstat(path);if(stat.isSymbolicLink())throw Error(`Cannot relocate symbolic link ${path}. Replace it with its actual design content first.`);if(stat.isDirectory())for(let name of(await readdir(path)).sort())await walk(rel?`${rel}/${name}`:name);else if(stat.isFile())hash.update(rel).update("\x00").update(await readFile2(path));else throw Error(`Unsupported filesystem entry: ${path}`)}return await walk(""),hash.digest("hex")}async function planRelocation(source,cwd,to,external){if(Boolean(to)===external)throw Error("Choose exactly one destination: --external or --to <path>.");let designs=await findDesigns(cwd),physicalSource=realpathSync(source),local=designs?.local.find((design)=>realOr(design.designRoot)===physicalSource),repo=designs?.repo??null,index=repo?.folders.findIndex((path)=>existsSync2(path)&&realpathSync(path)===physicalSource)??-1;if(!local&&index===-1)throw Error("Run relocation from the checkout the design belongs to; the source must be one of its designs.");let root=local?.root??repo?.dir,name=recordedDesignName(source);if(!name)throw Error(`${source} has no design name \u2014 run \`velloo upgrade\` first.`);let physicalRoot=realpathSync(root);if(physicalSource===physicalRoot)throw Error("Cannot relocate the application repository itself. Use a separate design directory.");if(external&&managedDesignId(source))throw Error("This design already lives in managed storage.");let destination=external?managedDesignPath(local?.id??randomUUID()):resolve2(cwd,to??""),physicalTarget=physicalDestination(destination),inRepo=!external&&isWithin(physicalRoot,physicalTarget);if(physicalTarget===physicalSource||physicalTarget.startsWith(physicalSource+sep2)||physicalSource.startsWith(physicalTarget+sep2))throw Error("Source and destination cannot contain each other.");if(existsSync2(destination))throw Error(`Destination is occupied: ${destination}. Choose a new path; nothing was changed.`);let manifestPath=repo?.path??join2(root,"velloo.json"),before=repo?await readFile2(repo.path,"utf8"):null,manifest,destinationManifestPath;if(repo?.legacy)throw Error(`${repo.path} is in the pre-designs format \u2014 run \`velloo upgrade\` first.`);if(inRepo){destinationManifestPath=relative2(physicalRoot,physicalTarget).split(sep2).join("/")||".";let designs=[...repo?.manifest.designs??[]];if(index===-1)designs.push(destinationManifestPath);else designs[index]=destinationManifestPath;manifest={...repo?.manifest??{},designs}}else if(repo&&index!==-1)manifest=withoutDesign(repo.manifest,index,name);let config=ConfigSchema.parse(JSON.parse(await readFile2(join2(source,".design/config.json"),"utf8")));return{source,destination,root,appRoot:local?.appRoot??hostAppRootFrom(source,config.hostApp),designName:name,sourceLocalId:local?.id,sourceManifestPath:index===-1?void 0:repo?.manifest.designs[index],destinationManifestPath,destinationLocal:inRepo?void 0:{id:local?.id??(external?basename(destination):randomUUID()),...external?{}:{designPath:destination}},manifestPath,before,manifest}}function realOr(path){return existsSync2(path)?realpathSync(path):resolve2(path)}async function relocateDesign(plan){await stopDaemon(daemonRoot(plan.source));let original=await fingerprint(plan.source);await mkdir(dirname(plan.destination),{recursive:!0}),await mkdir(plan.destination);let committed=!1;try{for(let entry of await readdir(plan.source))await cp(join2(plan.source,entry),join2(plan.destination,entry),{recursive:!0,errorOnExist:!0,force:!1,filter:(path)=>!RUNTIME.some((p)=>{let rel=relative2(plan.source,path).split(sep2).join("/");return rel===p||rel.startsWith(`${p}/`)})});if(await fingerprint(plan.destination)!==original)throw Error("Copied content did not match the source.");if(await rebaseDesignConfig(plan.source,plan.destination,plan.appRoot,plan.destinationLocal!==void 0),await loadDesignFolder(plan.destination,{preferences:!1}),await fingerprint(plan.source)!==original)throw Error("The source changed during relocation. Close editors and retry.");if(await readFile2(plan.manifestPath,"utf8").catch(()=>null)!==plan.before)throw Error("The manifest changed during relocation. Review it and retry.");if(plan.destinationLocal){if(plan.sourceLocalId&&plan.sourceLocalId!==plan.destinationLocal.id)await removeLocalDesign(plan.sourceLocalId);await writeLocalDesign(plan.destinationLocal.id,{root:plan.root,appRoot:plan.appRoot,...plan.destinationLocal.designPath?{designPath:plan.destinationLocal.designPath}:{}})}else if(plan.sourceLocalId)await removeLocalDesign(plan.sourceLocalId);if(plan.manifest===null)await rm(plan.manifestPath,{force:!0});else if(plan.manifest)await writeJsonAtomic(plan.manifestPath,plan.manifest);committed=!0}finally{if(!committed)await rm(plan.destination,{recursive:!0,force:!0})}try{await rm(plan.source,{recursive:!0})}catch(error){throw Error(`Relocation completed to ${plan.destination}, but the old copy remains at ${plan.source}. Remove it after checking the destination. ${error}`)}}var import_picocolors=__toESM(require_picocolors(),1);async function offerUpgradeBeforeInit(argv){if(process.env.VELLOO_DISABLE_UPDATE_CHECK==="1"||!process.stdin.isTTY)return!1;let status;try{status=await updateStatus({refresh:!0})}catch{return!1}if(!status.available||!status.upgradable||!status.latest)return!1;console.log(""),console.log(`${import_picocolors.default.yellow("A newer velloo is available:")} ${status.current} \u2192 ${import_picocolors.default.bold(status.latest)}`);let yes=await confirm({message:"Upgrade before setting up this design folder?",initialValue:!0});if(isCancel(yes)||!yes)return console.log(import_picocolors.default.dim(" Continuing on the current version.")),!1;try{if(!(await upgradeInstalledVelloo()).upgraded)return!1;let bin=installedVellooBin();if(!bin)return console.log(import_picocolors.default.dim(" Upgraded. Run `velloo init` again to continue on the new version.")),!0;let code=await Bun.spawn([bin,...argv],{stdin:"inherit",stdout:"inherit",stderr:"inherit",env:{...process.env,VELLOO_DISABLE_UPDATE_CHECK:"1"}}).exited;process.exit(code)}catch(error){return console.log(import_picocolors.default.yellow(` Could not upgrade (${error instanceof Error?error.message:String(error)}) \u2014 continuing on the current version.`)),!1}}import{resolve as resolve3}from"path";var START_TO_CONTENT={"redesign-screen":"redesign-screen","redesign-component":"component",custom:"custom",sample:"sample",blank:"blank",scratch:"sample",scan:"scan"};function isValidLibraryId(v){return LIBRARY_IDS.includes(v)}function isValidContent(v){return v==="sample"||v==="blank"||v==="scan"||v==="redesign-screen"||v==="component"||v==="custom"}function isValidStart(v){return v in START_TO_CONTENT}function shouldRunWizard(args,stdinIsTTY){if(args.nonInteractive)return!1;return stdinIsTTY}function answersFromArgs(args){if(args.library&&!isValidLibraryId(args.library))throw Error(`unknown --library ${JSON.stringify(args.library)}. Valid: ${LIBRARY_IDS.join(" | ")}.`);if(args.start&&!isValidStart(args.start))throw Error(`unknown --start ${JSON.stringify(args.start)}. Valid: redesign-screen | redesign-component | custom | sample | blank | scan.`);if(args.initialContent&&!isValidContent(args.initialContent))throw Error(`unknown --initial-content ${JSON.stringify(args.initialContent)}. Valid: sample | blank | scan | redesign-screen | component | custom.`);let themePreset=args.themePreset?.trim()||void 0;if(themePreset&&!isValidPreset(themePreset))throw Error(`unknown --theme-preset ${JSON.stringify(args.themePreset)}.`);let stack=args.stack?.trim()||void 0;if(stack&&!isValidStack(stack))throw Error(`unknown --stack ${JSON.stringify(args.stack)}. Valid: nextjs | vite | astro | remix.`);let appRoot=resolve3(args.folder??"."),folder=resolve3(appRoot,args.designFolder??"velloo"),library=args.library??DEFAULT_LIBRARY_ID,initialContent=args.initialContent??"sample";if(args.start)initialContent=START_TO_CONTENT[args.start]??initialContent;let screenName=args.screenName?.trim()||void 0,componentDescription=args.component?.trim()||void 0,customRequest=args.request?.trim()||void 0;if(initialContent==="component"&&!componentDescription)throw Error("--component is required for --start=redesign-component.");if(initialContent==="custom"&&!customRequest)throw Error("--request is required for --start=custom.");if(initialContent==="redesign-screen"&&!screenName&&args.start==="redesign-screen");let source=WIZARD_PROVIDERS[library].defaultSource;return{appRoot,scanRoot:appRoot,folder,library,source,componentsRelative:args.componentsDir??"src/components/ui",initialContent,themePreset,...stack?{stack}:{},...screenName?{screenName}:{},...componentDescription?{componentDescription}:{},...customRequest?{customRequest}:{}}}import{readFile as readFile3,rm as rm2,writeFile}from"fs/promises";import{tmpdir}from"os";import{join as join3}from"path";var import_picocolors2=__toESM(require_picocolors(),1);import{relative as relative3,sep as sep3}from"path";var SCREENS_PLACEHOLDER="{{screens}}";function libraryLabel(answers){let unsupported=answers.detected?.unsupportedUi;if(answers.library==="none"&&unsupported)return`own ${unsupported}`;return WIZARD_PROVIDERS[answers.library].handoffComponentsLabel}function appContextLines(answers){let lines=[],appRels=[...new Set((answers.selectedRoutes??[]).map((r)=>r.appRel).filter(Boolean))];if(appRels.length>1)lines.push(`This is a monorepo with ${appRels.length} apps (${appRels.map((r)=>`\`${r}\``).join(", ")}); each app's screens sit on their own board. Run the matching app's dev server when comparing screens.`);else{let uiRel=relative3(answers.appRoot,answers.scanRoot).split(sep3).join("/");if(uiRel&&!uiRel.startsWith(".."))lines.push(`This app's UI lives in \`${uiRel}/\` \u2014 run its dev server from there.`)}return lines}function setupFirstGuidance(){return"**Calibrate before you design** \u2014 run the **velloo-setup** skill first. My app's own components render on the canvas once their preview entry is set up (`preview_status`, then `set_preview_entry`); everything else is drawn with Velloo's components themed by this folder's tokens, so an uncalibrated board won't look like my app. Set up the preview entry, import my stylesheet, set the real fonts, and get the app running so there's something real to check against. Use my components from list_components' Repo shelves rather than rebuilding them from primitives. Tell me in a line what you matched and what stayed unverified."}function compareGuidance(){return"Compare against the real app, not memory: iterate with `screenshot` + `compare_to_url` at the same viewport and fix the `topMismatches` in the order they're ranked. If a capture comes back `unverified`, the similarity number means nothing \u2014 and if the reason is a login wall, call `start_capture_session` so I can sign in and capture the page for you, then verify against it with `compare_to_url { captureId }`."}function captureSiteGuidance(url){return[`**Start by capturing the site.** Call \`start_capture_session { url: "${url}" }\` \u2014 it opens a browser window I drive. I'll log in if needed and hit "Capture page" on each page worth designing from; the call returns straight away, so poll \`list_captures\` until my captures appear.`,"Then read each one with `get_capture`: it gives you a structural outline (repeated blocks are marked \u2014 those are your component candidates), the site's CSS custom properties as `import_theme`-ready CSS, and its images. Run `import_theme` with that CSS before composing so the real tokens resolve.","Re-express the pages with real components against the theme \u2014 do not transcribe the DOM node-for-node. Verify each screen with `compare_to_url { captureId }`."].join(`
3
+ `)}function exploreAlternativesGuidance(){return"After the faithful recreation, explore alternatives on the same board \u2014 side-by-side A/B/C (or more) frames or grouped explorations of different directions. Velloo's strength is comparing options visually; do not stop at a single redesign."}function wantsHandoff(answers){switch(answers.initialContent){case"scan":case"redesign-screen":case"component":case"custom":return!0;default:return!1}}function buildHandoffPrompt(answers,screens,boards){switch(answers.initialContent){case"redesign-screen":return buildRedesignScreenHandoff(answers,screens);case"component":return buildComponentHandoff(answers);case"custom":return buildCustomHandoff(answers);case"scan":return buildLegacyScanHandoff(answers,screens,boards);default:return buildLegacyScanHandoff(answers,screens,boards)}}function buildRedesignScreenHandoff(answers,screens){let name=answers.screenName?.trim()||screens[0]?.name||screens[0]?.id||"the chosen screen",lines=[`Recreate and then explore alternatives for **${name}** as a Velloo design. Work entirely through the velloo MCP tools.`,...appContextLines(answers)];if(screens.length>0)lines.push(`A placeholder screen is already scaffolded (desktop + mobile frames \u2014 edits sync). Design it with the project's ${libraryLabel(answers)} components:`,SCREENS_PLACEHOLDER);else lines.push(`- Create a Velloo screen for **${name}** using the project's ${libraryLabel(answers)} components.`);return lines.push(`1. ${setupFirstGuidance()}`,`2. **Recreate** the real page faithfully. ${compareGuidance()}`,`3. **Then explore alternatives** \u2014 ${exploreAlternativesGuidance()}`),lines.join(`
4
+ `)}function buildComponentHandoff(answers){let target=answers.componentDescription?.trim()||"the component";return[`Redesign **${target}** in Velloo \u2014 recreate it, then explore alternatives. Work entirely through the velloo MCP tools.`,...appContextLines(answers),`A component-focused board is scaffolded. Target: **${target}**. Prefer a snippet if the piece should be reused.`,`1. ${setupFirstGuidance()}`,`2. **Recreate** the current component faithfully with the project's ${libraryLabel(answers)} components. ${compareGuidance()}`,`3. **Then explore alternatives** \u2014 ${exploreAlternativesGuidance()}`].join(`
5
+ `)}function buildCustomHandoff(answers){return["Design this in Velloo. Work entirely through the velloo MCP tools.","","**Request:**",answers.customRequest?.trim()||"the design I described","",...answers.captureUrl?[captureSiteGuidance(answers.captureUrl),""]:[],setupFirstGuidance(),`Then design it with the project's ${libraryLabel(answers)} components. When comparing options, put alternatives side-by-side on the board (explorations) instead of overwriting a single direction.`].join(`
6
+ `)}function buildLegacyScanHandoff(answers,screens,boards){let lines=["Recreate this app's pages as a Velloo design \u2014 one Velloo screen per page, faithful to the real UI. Work entirely through the velloo MCP tools (wired during setup)."];if(lines.push(...appContextLines(answers)),lines.push(setupFirstGuidance()),screens.length>0){if(lines.push(`These screens are already scaffolded from the app's routes \u2014 design these, and only these, with the project's ${libraryLabel(answers)} components:`,SCREENS_PLACEHOLDER),answers.agentPicksFirst)lines.push("Start with the highest-impact screen (usually the landing page or the main dashboard) and design it fully \u2014 it sets the quality bar. Then work through the rest.")}else lines.push(`- For each route/page in the app, create a Velloo screen that reproduces that page's UI from the project's ${libraryLabel(answers)} components.`);if(lines.push("- Each screen already has a desktop and a mobile frame on its board (same screen \u2014 edits sync); make sure layouts hold at both widths."),boards.some((b)=>b.groups.length>0))lines.push("- Frames are pre-grouped by route section \u2014 keep the groups tidy as flows evolve.");return lines.push(`- ${compareGuidance()}`),lines.join(`
7
7
  `)}function expandHandoffPrompt(prompt,screens){if(!prompt.includes(SCREENS_PLACEHOLDER))return prompt;let list=screens.map((s)=>` - ${s.name||s.id}`).join(`
8
8
  `);return prompt.split(SCREENS_PLACEHOLDER).join(list)}function detectEditor(){let env=(process.env.VISUAL||process.env.EDITOR||"").trim();if(env){let parts=env.split(/\s+/);if(parts[0]&&Bun.which(parts[0]))return parts}for(let cand of["nano","vim","vi"])if(Bun.which(cand))return[cand];return["vi"]}async function editPrompt(prompt){let file=join3(tmpdir(),`velloo-handoff-${process.pid}-${Date.now()}.md`);await writeFile(file,prompt,"utf8");let editor=detectEditor();try{return await Bun.spawn([...editor,file],{stdin:"inherit",stdout:"inherit",stderr:"inherit"}).exited,(await readFile3(file,"utf8")).trim()}catch{return null}finally{await rm2(file,{force:!0})}}var AGENT_LAUNCHERS=[{bin:"claude",label:"Claude Code",agentIds:["claude-code","claude-code-global"],argv:(p)=>["claude",p]},{bin:"cursor-agent",label:"Cursor CLI",agentIds:["cursor","cursor-global"],argv:(p)=>["cursor-agent",p]},{bin:"codex",label:"Codex CLI",agentIds:["codex","codex-global"],argv:(p)=>["codex",p]},{bin:"opencode",label:"opencode",agentIds:["opencode","opencode-global"],argv:(p)=>["opencode","--prompt",p]},{bin:"droid",label:"Droid",agentIds:["droid-global"],argv:(p)=>["droid",p]},{bin:"cline",label:"Cline",agentIds:["cline-global"],argv:(p)=>["cline",p]},{bin:"gemini",label:"Gemini CLI",agentIds:["gemini","gemini-global"],argv:(p)=>["gemini","-i",p]}];async function printAgentHandoff(answers,scaffold,interactive,wiredIds){if(!wantsHandoff(answers))return;let{screens}=scaffold,prompt=buildHandoffPrompt(answers,screens,scaffold.boards);console.log(import_picocolors2.default.bold(" Finish setup with your agent")),console.log(import_picocolors2.default.dim(" Paste this to your AI agent:")),console.log("");for(let line of prompt.split(`
9
9
  `))console.log(import_picocolors2.default.cyan(` ${line}`));if(prompt.includes(SCREENS_PLACEHOLDER))console.log(""),console.log(import_picocolors2.default.dim(` ${SCREENS_PLACEHOLDER} is replaced with your selected screens when the prompt is sent (an agent can also discover them with list_screens).`));if(console.log(""),!interactive)return;let launchers=AGENT_LAUNCHERS.filter((l)=>Bun.which(l.bin)&&l.agentIds.some((id)=>wiredIds.includes(id))),first=launchers[0],action=await select({message:first?"Start velloo and launch your agent to do this now?":"No wired agent CLI to launch \u2014 what next?",options:[...launchers.map((l)=>({value:`launch:${l.bin}`,label:`Yes \u2014 launch ${l.label} with this prompt`})),...first?[{value:"edit",label:"Edit the prompt first",hint:`opens ${detectEditor()[0]??"your editor"}`}]:[],{value:"copy",label:"Copy the prompt to my clipboard"},{value:"skip",label:first?"No \u2014 I'll run it later":"Skip"}],initialValue:first?`launch:${first.bin}`:"copy"});if(isCancel(action)||action==="skip")return;if(action==="copy"){let copied=await copyToClipboard(expandHandoffPrompt(prompt,screens));console.log(copied?` ${import_picocolors2.default.green("\u2713")} Prompt copied \u2014 paste it into your agent.`:import_picocolors2.default.dim(" Couldn't reach a clipboard tool \u2014 copy the prompt printed above."));return}let finalPrompt=prompt,launcher=first;if(action.startsWith("launch:"))launcher=launchers.find((l)=>`launch:${l.bin}`===action);else{let edited=await editPrompt(prompt);if(edited===null){console.error(" Couldn't open an editor. Set $EDITOR and retry, or copy the prompt above.");return}if(edited===""){console.log(import_picocolors2.default.dim(" Prompt was emptied \u2014 nothing launched."));return}if(finalPrompt=edited,launchers.length>1){let pick=await select({message:"Launch which agent?",options:launchers.map((l)=>({value:l.bin,label:l.label})),...launchers[0]?{initialValue:launchers[0].bin}:{}});if(isCancel(pick))return;launcher=launchers.find((l)=>l.bin===pick)}}if(!launcher)return;finalPrompt=expandHandoffPrompt(finalPrompt,screens);let canvasUrl;try{canvasUrl=(await ensureDaemon(answers.folder)).canvasUrl}catch(err){console.error(` Couldn't start the canvas (${err.message}). Run \`velloo run ${answers.folder}\` yourself, then paste the prompt above.`);return}let openCanvas=await confirm({message:"Open the board in your browser to watch? (your agent runs here in this terminal and may ask for MCP approval first)",initialValue:!0});if(!isCancel(openCanvas)&&openCanvas)await openUrl(canvasUrl);console.log(""),console.log(` Canvas running at ${import_picocolors2.default.cyan(canvasUrl)}.`),console.log(import_picocolors2.default.dim(` Launching ${launcher.label} here \u2014 it may ask you to approve the velloo MCP server first.`)),await Bun.spawn(launcher.argv(finalPrompt),{stdout:"inherit",stderr:"inherit",stdin:"inherit"}).exited}var import_picocolors3=__toESM(require_picocolors(),1),RGB={amber:[255,171,31],coral:[255,111,77]},ANSI256={amber:214,coral:209},TAGLINE="Design like a developer. Build like a designer.",V=[" "," ","oooo ooo"," `88. .8' "," `88..8' "," `888' "," `8' "],E=[" "," "," .ooooo. ","d88' `88b","888ooo888","888 .o","`Y8bod8P'"],L=["oooo ","`888 "," 888 "," 888 "," 888 "," 888 ","o888o"],O=[" "," "," .ooooo. ","d88' `88b","888 888","888 888","`Y8bod8P'"],MARK=[" .o8888888o. ","d88 88b ","88 .o8888888o. ","88 d88 88 88b","88 88 88 88","Y88 88 88P 88"," `o8888888P' 88"," Y88 88P"," `o8888888P' "],MARK_OWN=[" ccccccccccc ","ccc ccc ","cc aaaaaccaaaa ","cc aaa cc aaa","cc aa cc aa","ccc aa ccc aa"," ccccaaccccc aa"," aaa aaa"," aaaaaaaaaaa "];function supportsTruecolor(){let colorterm=process.env.COLORTERM??"";if(/truecolor|24bit/i.test(colorterm))return!0;if(process.env.TERM_PROGRAM==="Apple_Terminal")return!1;let program=process.env.TERM_PROGRAM??"";if(["iTerm.app","vscode","ghostty","WarpTerminal","WezTerm"].includes(program))return!0;return/truecolor|-direct$/i.test(process.env.TERM??"")}function paint(frame,text){if(!text||!import_picocolors3.default.isColorSupported)return text;if(supportsTruecolor()){let[r,g,b]=RGB[frame];return`\x1B[38;2;${r};${g};${b}m${text}\x1B[39m`}return`\x1B[38;5;${ANSI256[frame]}m${text}\x1B[39m`}function paintMarkRow(chars,owners){let out="",i=0;while(i<chars.length){let who=owners[i],j=i+1;while(j<owners.length&&owners[j]===who)j++;let slice=chars.slice(i,j);out+=who==="c"?paint("coral",slice):who==="a"?paint("amber",slice):slice,i=j}return out}function renderLogo(){return[...MARK.map((chars,i)=>{let mark=paintMarkRow(chars,MARK_OWN[i]??""),k=i-1,v=V[k],o=O[k],right=v===void 0||o===void 0?"":` ${import_picocolors3.default.bold(`${v}${E[k]}${L[k]}${L[k]}`)}${paint("amber",o)} ${paint("coral",o)}`;return` ${mark}${right}`}),""," "+import_picocolors3.default.dim(TAGLINE)].join(`
@@ -19,8 +19,8 @@ and enter the code: ${import_picocolors5.default.bold(userCode)}
19
19
  ${import_picocolors5.default.dim("Press Esc to skip and continue without signing in.")}`,"Sign up"),spin.start("Waiting for sign-in (Esc to skip)")},controller.signal);await saveCredential(cloudUrl,cred),spin.stop(`Signed in as ${cred.email}`),signedIn=!0}catch(err){if(controller.signal.aborted)spin.stop("Sign-in skipped \u2014 continuing without it.");else spin.stop("Sign-in failed"),note(`Couldn't sign in (${err instanceof Error?err.message:String(err)}).
20
20
  You can run ${import_picocolors5.default.cyan("velloo login")} anytime later.`,"Heads up")}finally{if(process.stdin.off("data",onKey),controller.signal.aborted&&process.stdin.isTTY){let drain=()=>{};process.stdin.on("data",drain),await new Promise((r)=>setTimeout(r,150)),process.stdin.off("data",drain)}if(process.stdin.isTTY&&!hadRaw)process.stdin.setRawMode(!1),process.stdin.pause()}return signedIn}var UI_LIBRARY_NAMES={shadcn:"shadcn",mui:"Material UI",antd:"Ant Design",chakra:"Chakra UI"};function describeDetected(d){let ui,canvas;if(d.uiLibrary==="shadcn")ui=`shadcn${d.shadcnStyle?` (${d.shadcnStyle})`:""}`,canvas="Velloo's own bundled shadcn components \u2014 your app's files are not imported, so designs stay in sync by matching the same upstream shadcn";else if(d.uiLibrary){let name=UI_LIBRARY_NAMES[d.uiLibrary];ui=name,canvas=`real ${name} components bundled with Velloo`}else if(d.unsupportedUi)ui=`${d.unsupportedUi} (no Velloo adapter yet)`,canvas="Velloo's no-library primitives stand in";else ui="none detected",canvas="Velloo's own bundled shadcn components";return[`UI library: ${ui}`,`Canvas uses: ${canvas}`,`Tailwind: ${d.tailwindMajor?`v${d.tailwindMajor}`:"not detected"}`,`theme css: ${d.globalsCssPath??"not found \u2014 will use a preset"}`].join(`
21
21
  `)}async function resolveComponentsDir(appRoot,hasHostApp,inherited){if(inherited)return{value:inherited};if(!hasHostApp)return{value:DEFAULT_COMPONENTS_DIR};let found=findComponentsDir(appRoot);if(found)return{value:found};let typed=await text({message:subtitledText("Components subfolder inside your app","Where your UI components live \u2014 emitted code imports from this path."),placeholder:DEFAULT_COMPONENTS_DIR,defaultValue:DEFAULT_COMPONENTS_DIR});if(isAborted(typed))return null;return{value:typed||DEFAULT_COMPONENTS_DIR}}async function resolveLibrary(ctx,initial=DEFAULT_LIBRARY_ID){if(ctx.pinnedLibrary){if(ctx.pinnedLibraryReason)log.info(`Component library: ${WIZARD_PROVIDERS[ctx.pinnedLibrary].label} ${import_picocolors6.default.dim(`(${ctx.pinnedLibraryReason})`)}`);return ctx.pinnedLibrary}let picked=await select({message:subtitled("Component library","What the canvas draws with, and what your emitted code imports."),options:interactiveLibraryChoices(),initialValue:initial});return isAborted(picked)?null:picked}async function tryAdoptLibraryFromApp(appRoot,scanDir){let spin=spinner();spin.start("Detecting your app's UI library");try{let primary=(await scanApps(appRoot,scanDir)).apps[0],scanRoot=primary?.dir??appRoot,detected=detectHost(scanRoot),adopted=scanAdoption(detected);if(spin.stop(adopted?`Detected ${WIZARD_PROVIDERS[adopted.library].label}`:"No UI library detected \u2014 you'll pick one next."),!adopted)return null;return{library:adopted.library,detected,scanRoot,stack:stackForFramework(primary?.framework)}}catch{return spin.stop("Couldn't scan the app \u2014 you'll pick a library next."),null}}async function promptLibraryThemePath(ctx,folder,agentWiring,initialContent,extra={},opts={}){let library=ctx.pinnedLibrary??DEFAULT_LIBRARY_ID;if(!opts.skipLibrary){let picked=await resolveLibrary(ctx);if(picked===null)return null;library=picked}let provider=WIZARD_PROVIDERS[library],source=provider.defaultSource,componentsRelative=DEFAULT_COMPONENTS_DIR;if(!opts.libraryOnly&&provider.asksComponentsSubfolder){let dir=await resolveComponentsDir(ctx.appRoot,ctx.hasHostApp,ctx.inheritComponentsDir);if(!dir)return null;componentsRelative=dir.value}let themePreset=initialContent==="blank"?"zinc":DEFAULT_THEME_PRESET,share=await promptShareAndFeedback(ctx.appRoot);if(share===null)return null;return noteSetup(ctx.appRoot,folder),{appRoot:ctx.appRoot,scanRoot:extra.scanRoot??ctx.appRoot,folder,library,source,componentsRelative,initialContent,themePreset,stack:extra.stack??DEFAULT_STACK_ID,...agentWiring?{agentWiring}:{},...share,...extra}}async function promptRedesignScreen(ctx,folder,agentWiring){let how=await select({message:subtitled("Which screen should your agent redesign?","Scanning finds your real routes; typing a name works for a screen that doesn't exist yet."),options:[{value:"scan",label:"Pick from my app's routes",hint:"Scan routes and choose one"},{value:"type",label:"Type a screen name",hint:"e.g. Pricing, Settings, Checkout"}],initialValue:"scan"});if(isAborted(how))return null;let selectedRoutes,screenName,scanRoot=ctx.appRoot,detected,library=ctx.pinnedLibrary??DEFAULT_LIBRARY_ID,stack=DEFAULT_STACK_ID;if(how==="scan"){let spin=spinner();spin.start("Scanning your app's routes");let scanned=await scanApps(ctx.appRoot,ctx.scanDir),appsSuffix=scanned.apps.length>1?` across ${scanned.apps.length} apps`:"";if(spin.stop(scanned.routes.length>0?`Found ${scanned.routes.length} screen${scanned.routes.length===1?"":"s"}${appsSuffix}`:"No routes detected \u2014 type a screen name instead."),scanned.routes.length===0){let typed=await promptScreenName();if(typed===null)return null;screenName=typed}else{let rankPrimary=scanned.apps[0];if(scanned.apps.length>1){let lines=scanned.apps.map((a)=>`${import_picocolors7.default.cyan(a.rel||".")} \u2014 ${a.routes.length} screen${a.routes.length===1?"":"s"}`);note(lines.join(`
22
- `),`Found ${scanned.apps.length} apps`)}let host=detectHost(rankPrimary?.dir??ctx.appRoot);note(describeDetected(host),"Detected in your app");let picked=await pickOneScreen(scanned);if(picked===null)return null;selectedRoutes=[picked],screenName=picked.name;let primary=primaryApp(scanned.apps,[picked])??rankPrimary;if(scanRoot=primary?.dir??ctx.appRoot,primary&&primary!==rankPrimary)host=detectHost(primary.dir);if(detected=host,stack=stackForFramework(primary?.framework),!ctx.pinnedLibrary){let adopted=scanAdoption(host);if(adopted)library=adopted.library}}}else{let typed=await promptScreenName();if(typed===null)return null;if(screenName=typed,!ctx.pinnedLibrary){let adopted=await tryAdoptLibraryFromApp(ctx.appRoot,ctx.scanDir);if(adopted)note(describeDetected(adopted.detected),"Detected in your app"),detected=adopted.detected,scanRoot=adopted.scanRoot,library=adopted.library,stack=adopted.stack}}let adoptedLibrary=detected?scanAdoption(detected)?.library:void 0,rest=await promptLibraryThemePath({...ctx,pinnedLibrary:ctx.pinnedLibrary??adoptedLibrary??library},folder,agentWiring,"redesign-screen",{screenName,selectedRoutes,detected,scanRoot,stack},{skipLibrary:Boolean(ctx.pinnedLibrary||adoptedLibrary),libraryOnly:Boolean(adoptedLibrary)});if(!rest)return null;let finalLibrary=ctx.pinnedLibrary??adoptedLibrary??rest.library;return{...rest,library:finalLibrary,source:WIZARD_PROVIDERS[finalLibrary].defaultSource,scanRoot,...detected?{detected}:{},...selectedRoutes?{selectedRoutes}:{},...screenName?{screenName}:{}}}async function promptScreenName(){let typed=await text({message:"Screen name",placeholder:"e.g. Pricing",validate(value){if(!value?.trim())return"Enter a screen name.";return}});return isAborted(typed)?null:String(typed).trim()}async function pickOneScreen(scanned){let{routes}=scanned,choice=await select({message:"Which screen?",options:routes.map((r)=>({value:r.id,label:r.name,hint:r.routePath})),...routes[0]?{initialValue:routes[0].id}:{}});if(isAborted(choice))return null;return routes.find((r)=>r.id===choice)??null}async function promptSample(ctx,folder,agentWiring){let library=await resolveLibrary(ctx);if(library===null)return null;let provider=WIZARD_PROVIDERS[library],source=provider.defaultSource,componentsRelative=DEFAULT_COMPONENTS_DIR;if(provider.asksComponentsSubfolder){let dir=await resolveComponentsDir(ctx.appRoot,ctx.hasHostApp,ctx.inheritComponentsDir);if(!dir)return null;componentsRelative=dir.value}let share=await promptShareAndFeedback(ctx.appRoot);if(share===null)return null;return noteSetup(ctx.appRoot,folder),{appRoot:ctx.appRoot,scanRoot:ctx.appRoot,folder,library,source,componentsRelative,initialContent:"sample",themePreset:DEFAULT_THEME_PRESET,stack:DEFAULT_STACK_ID,...agentWiring?{agentWiring}:{},...share}}async function buildBlankAnswers(ctx,folder,agentWiring,defaultLibrary="none"){let library=await resolveLibrary(ctx,defaultLibrary);if(library===null)return null;let share=await promptShareAndFeedback(ctx.appRoot);if(share===null)return null;return noteSetup(ctx.appRoot,folder),{appRoot:ctx.appRoot,scanRoot:ctx.appRoot,folder,library,source:WIZARD_PROVIDERS[library].defaultSource,componentsRelative:ctx.inheritComponentsDir??DEFAULT_COMPONENTS_DIR,initialContent:"blank",themePreset:"zinc",stack:DEFAULT_STACK_ID,...agentWiring?{agentWiring}:{},...share}}async function promptGoalWithAdoptedLibrary(ctx,folder,agentWiring,initialContent,extra={}){let adopted=ctx.pinnedLibrary?null:await tryAdoptLibraryFromApp(ctx.appRoot,ctx.scanDir);if(adopted)note(describeDetected(adopted.detected),"Detected in your app"),note(`Using ${WIZARD_PROVIDERS[adopted.library].label}.`,"Library");return promptLibraryThemePath({...ctx,pinnedLibrary:ctx.pinnedLibrary??adopted?.library},folder,agentWiring,initialContent,{...extra,...adopted?{detected:adopted.detected,scanRoot:adopted.scanRoot,stack:adopted.stack}:{}},{skipLibrary:Boolean(ctx.pinnedLibrary||adopted),libraryOnly:Boolean(adopted)})}function folderPathProblem(abs,shown){if(isDesignFolderSync(abs))return`${shown} is already a Velloo design folder \u2014 pick another path.`;if(!isEmptyOrMissingSync(abs))return`${shown} isn't empty \u2014 pick an empty path (or delete it first).`;return}function cancelled(){return cancel("Cancelled \u2014 no files were written."),{status:"abort"}}function outcome(answers){return answers?{status:"ok",answers}:cancelled()}function hostGoalOptions(hasHostApp){let selfContained=[{value:"sample",label:"Elsewhere welcome sample",hint:"Travel app with imagery, dark mode and three agentic trip explorations"},{value:"blank",label:"Blank",hint:"Empty canvas \u2014 you and your agent build it from nothing"}];if(!hasHostApp)return[{value:"capture-site",label:"Design from a live site",hint:"Open a browser, log in if needed, capture pages \u2014 then design from them"},...selfContained];return[{value:"redesign-screen",label:"Redesign a screen",hint:"Recreate one of your pages, then explore alternatives"},{value:"redesign-component",label:"Redesign a component",hint:"One region or widget \u2014 recreate it, then try variations"},{value:"custom",label:"Custom request",hint:"Describe any design you need and start there"},...selfContained]}async function promptBrief(opts){let answer=await text({message:subtitledText(opts.message,opts.subtitle),placeholder:opts.placeholder,validate:(value)=>opts.validate(value?.trim()??"")});return isAborted(answer)?null:String(answer).trim()}async function runInteractive(ctx){let folderDefault=ctx.secondFolder?"velloo-brand":"velloo",folderBase=ctx.folderBase??ctx.appRoot;if(ctx.presetFolder){let problem=folderPathProblem(resolve4(folderBase,ctx.presetFolder),ctx.presetFolder);if(problem)return cancel(problem),cancelled()}let folderInput=ctx.presetFolder??await text({message:subtitledText("Design folder name or path",ctx.secondFolder?"A second canvas in this repo \u2014 its own boards, theme, daemon and MCP endpoint.":"Your designs are plain files that live in this repo \u2014 commit them alongside your code."),placeholder:folderDefault,defaultValue:folderDefault,validate(value){let raw=(value||folderDefault).trim();if(raw==="")return"Path can't be empty.";return folderPathProblem(resolve4(folderBase,raw),raw)}});if(isAborted(folderInput))return cancelled();let folder=resolve4(folderBase,folderInput||folderDefault),root=folderBase,local=!isWithin(root,folder);if(local&&!ctx.presetFolder)log.info(`${folder} is outside ${root}, so it will be a local design: recorded only on this machine, not in velloo.json, and not version-controlled by this repo.`);let agentWiring;if(ctx.connectEnabled){let wiring=await askAgentWiring({skipWhenCovered:!0,projectRoot:root,globalOnly:local});if(wiring===null)return cancelled();agentWiring=wiring}let hasHostApp=(await discoverScanRoots(ctx.appRoot)).length>0,hostCtx={...ctx,hasHostApp},goal=await select({message:subtitled("How do you want to start your board?","Pick a real design task \u2014 your agent starts on it as soon as init finishes."),options:hostGoalOptions(hasHostApp),initialValue:hasHostApp?"redesign-screen":"sample"});if(isAborted(goal))return cancelled();if(goal==="redesign-screen")return outcome(await promptRedesignScreen(hostCtx,folder,agentWiring));if(goal==="redesign-component"){let desc=await promptBrief({message:"Component name or description",subtitle:"Whatever you'd call it in a code review \u2014 the agent finds it from this.",placeholder:"e.g. sidebar, pricing card, filter chip row",validate:(v)=>v?void 0:"Describe the component so the agent knows what to redesign."});if(desc===null)return cancelled();return outcome(await promptGoalWithAdoptedLibrary(hostCtx,folder,agentWiring,"component",{componentDescription:desc}))}if(goal==="custom"){let request=await promptBrief({message:"What design do you need?",subtitle:"A sentence is enough \u2014 it becomes the brief your agent works from.",placeholder:"e.g. a pricing page with three tiers and a FAQ",validate:(v)=>v?void 0:"Describe what you want designed."});if(request===null)return cancelled();return outcome(await promptGoalWithAdoptedLibrary(hostCtx,folder,agentWiring,"custom",{customRequest:request}))}if(goal==="capture-site"){let url=await promptBrief({message:"Site to design from",subtitle:"Init won't open it \u2014 your agent starts a capture session so you can log in first.",placeholder:"e.g. https://app.example.com/dashboard",validate(v){if(!v)return"Enter the URL of the site you want to design from.";try{let scheme=new URL(v).protocol;if(scheme!=="http:"&&scheme!=="https:")return"Use an http(s) URL."}catch{return"That doesn't look like a URL."}return}});if(url===null)return cancelled();return outcome(await promptGoalWithAdoptedLibrary(hostCtx,folder,agentWiring,"custom",{captureUrl:url,customRequest:`Recreate the pages I capture from ${url}, then explore alternatives.`}))}if(goal==="blank")return outcome(await buildBlankAnswers(ctx,folder,agentWiring));return outcome(await promptSample(hostCtx,folder,agentWiring))}var import_picocolors8=__toESM(require_picocolors(),1);var NOT_WIRED={wiredIds:[]};async function applyAgentWiring(folder,wiring){let result;if(wiring.agents.length>0)try{result=await connect({designFolder:folder,agents:wiring.agents,installSkill:!0})}catch{result=void 0}if(wiring.manual){console.log(""),console.log(import_picocolors8.default.bold(" Manual MCP setup"));for(let line of manualSetupText().split(`
22
+ `),`Found ${scanned.apps.length} apps`)}let host=detectHost(rankPrimary?.dir??ctx.appRoot);note(describeDetected(host),"Detected in your app");let picked=await pickOneScreen(scanned);if(picked===null)return null;selectedRoutes=[picked],screenName=picked.name;let primary=primaryApp(scanned.apps,[picked])??rankPrimary;if(scanRoot=primary?.dir??ctx.appRoot,primary&&primary!==rankPrimary)host=detectHost(primary.dir);if(detected=host,stack=stackForFramework(primary?.framework),!ctx.pinnedLibrary){let adopted=scanAdoption(host);if(adopted)library=adopted.library}}}else{let typed=await promptScreenName();if(typed===null)return null;if(screenName=typed,!ctx.pinnedLibrary){let adopted=await tryAdoptLibraryFromApp(ctx.appRoot,ctx.scanDir);if(adopted)note(describeDetected(adopted.detected),"Detected in your app"),detected=adopted.detected,scanRoot=adopted.scanRoot,library=adopted.library,stack=adopted.stack}}let adoptedLibrary=detected?scanAdoption(detected)?.library:void 0,rest=await promptLibraryThemePath({...ctx,pinnedLibrary:ctx.pinnedLibrary??adoptedLibrary??library},folder,agentWiring,"redesign-screen",{screenName,selectedRoutes,detected,scanRoot,stack},{skipLibrary:Boolean(ctx.pinnedLibrary||adoptedLibrary),libraryOnly:Boolean(adoptedLibrary)});if(!rest)return null;let finalLibrary=ctx.pinnedLibrary??adoptedLibrary??rest.library;return{...rest,library:finalLibrary,source:WIZARD_PROVIDERS[finalLibrary].defaultSource,scanRoot,...detected?{detected}:{},...selectedRoutes?{selectedRoutes}:{},...screenName?{screenName}:{}}}async function promptScreenName(){let typed=await text({message:"Screen name",placeholder:"e.g. Pricing",validate(value){if(!value?.trim())return"Enter a screen name.";return}});return isAborted(typed)?null:String(typed).trim()}async function pickOneScreen(scanned){let{routes}=scanned,choice=await select({message:"Which screen?",options:routes.map((r)=>({value:r.id,label:r.name,hint:r.routePath})),...routes[0]?{initialValue:routes[0].id}:{}});if(isAborted(choice))return null;return routes.find((r)=>r.id===choice)??null}async function promptSample(ctx,folder,agentWiring){let library=await resolveLibrary(ctx);if(library===null)return null;let provider=WIZARD_PROVIDERS[library],source=provider.defaultSource,componentsRelative=DEFAULT_COMPONENTS_DIR;if(provider.asksComponentsSubfolder){let dir=await resolveComponentsDir(ctx.appRoot,ctx.hasHostApp,ctx.inheritComponentsDir);if(!dir)return null;componentsRelative=dir.value}let share=await promptShareAndFeedback(ctx.appRoot);if(share===null)return null;return noteSetup(ctx.appRoot,folder),{appRoot:ctx.appRoot,scanRoot:ctx.appRoot,folder,library,source,componentsRelative,initialContent:"sample",themePreset:DEFAULT_THEME_PRESET,stack:DEFAULT_STACK_ID,...agentWiring?{agentWiring}:{},...share}}async function buildBlankAnswers(ctx,folder,agentWiring,defaultLibrary="none"){let adopted=ctx.pinnedLibrary?null:await tryAdoptLibraryFromApp(ctx.appRoot,ctx.scanDir);if(adopted)note(describeDetected(adopted.detected),"Detected in your app"),note(`Using ${WIZARD_PROVIDERS[adopted.library].label}.`,"Library");let library=adopted?.library??await resolveLibrary(ctx,defaultLibrary);if(library===null)return null;let share=await promptShareAndFeedback(ctx.appRoot);if(share===null)return null;return noteSetup(ctx.appRoot,folder),{appRoot:ctx.appRoot,scanRoot:adopted?.scanRoot??ctx.appRoot,folder,library,source:WIZARD_PROVIDERS[library].defaultSource,componentsRelative:ctx.inheritComponentsDir??DEFAULT_COMPONENTS_DIR,initialContent:"blank",themePreset:"zinc",stack:adopted?.stack??DEFAULT_STACK_ID,...adopted?{detected:adopted.detected}:{},...agentWiring?{agentWiring}:{},...share}}async function promptGoalWithAdoptedLibrary(ctx,folder,agentWiring,initialContent,extra={}){let adopted=ctx.pinnedLibrary?null:await tryAdoptLibraryFromApp(ctx.appRoot,ctx.scanDir);if(adopted)note(describeDetected(adopted.detected),"Detected in your app"),note(`Using ${WIZARD_PROVIDERS[adopted.library].label}.`,"Library");return promptLibraryThemePath({...ctx,pinnedLibrary:ctx.pinnedLibrary??adopted?.library},folder,agentWiring,initialContent,{...extra,...adopted?{detected:adopted.detected,scanRoot:adopted.scanRoot,stack:adopted.stack}:{}},{skipLibrary:Boolean(ctx.pinnedLibrary||adopted),libraryOnly:Boolean(adopted)})}function folderPathProblem(abs,shown){if(isDesignFolderSync(abs))return`${shown} is already a Velloo design folder \u2014 pick another path.`;if(!isEmptyOrMissingSync(abs))return`${shown} isn't empty \u2014 pick an empty path (or delete it first).`;return}function cancelled(){return cancel("Cancelled \u2014 no files were written."),{status:"abort"}}function outcome(answers){return answers?{status:"ok",answers}:cancelled()}function hostGoalOptions(hasHostApp){let selfContained=[{value:"sample",label:"Elsewhere welcome sample",hint:"Travel app with imagery, dark mode and three agentic trip explorations"},{value:"blank",label:"Blank",hint:"Empty canvas \u2014 you and your agent build it from nothing"}];if(!hasHostApp)return[{value:"capture-site",label:"Design from a live site",hint:"Open a browser, log in if needed, capture pages \u2014 then design from them"},...selfContained];return[{value:"redesign-screen",label:"Redesign a screen",hint:"Recreate one of your pages, then explore alternatives"},{value:"redesign-component",label:"Redesign a component",hint:"One region or widget \u2014 recreate it, then try variations"},{value:"custom",label:"Custom request",hint:"Describe any design you need and start there"},...selfContained]}async function promptBrief(opts){let answer=await text({message:subtitledText(opts.message,opts.subtitle),placeholder:opts.placeholder,validate:(value)=>opts.validate(value?.trim()??"")});return isAborted(answer)?null:String(answer).trim()}async function runInteractive(ctx){let folderDefault=ctx.secondFolder?"velloo-brand":"velloo",folderBase=ctx.folderBase??ctx.appRoot;if(ctx.presetFolder){let problem=folderPathProblem(resolve4(folderBase,ctx.presetFolder),ctx.presetFolder);if(problem)return cancel(problem),cancelled()}let folderInput=ctx.presetFolder??await text({message:subtitledText("Design folder name or path",ctx.secondFolder?"A second canvas in this repo \u2014 its own boards, theme, daemon and MCP endpoint.":"Your designs are plain files that live in this repo \u2014 commit them alongside your code."),placeholder:folderDefault,defaultValue:folderDefault,validate(value){let raw=(value||folderDefault).trim();if(raw==="")return"Path can't be empty.";return folderPathProblem(resolve4(folderBase,raw),raw)}});if(isAborted(folderInput))return cancelled();let folder=resolve4(folderBase,folderInput||folderDefault),root=folderBase,local=!isWithin(root,folder);if(local&&!ctx.presetFolder)log.info(`${folder} is outside ${root}, so it will be a local design: recorded only on this machine, not in velloo.json, and not version-controlled by this repo.`);let agentWiring;if(ctx.connectEnabled){let wiring=await askAgentWiring({skipWhenCovered:!0,projectRoot:root,globalOnly:local});if(wiring===null)return cancelled();agentWiring=wiring}let hasHostApp=(await discoverScanRoots(ctx.appRoot)).length>0,hostCtx={...ctx,hasHostApp},goal=await select({message:subtitled("How do you want to start your board?","Pick a real design task \u2014 your agent starts on it as soon as init finishes."),options:hostGoalOptions(hasHostApp),initialValue:hasHostApp?"redesign-screen":"sample"});if(isAborted(goal))return cancelled();if(goal==="redesign-screen")return outcome(await promptRedesignScreen(hostCtx,folder,agentWiring));if(goal==="redesign-component"){let desc=await promptBrief({message:"Component name or description",subtitle:"Whatever you'd call it in a code review \u2014 the agent finds it from this.",placeholder:"e.g. sidebar, pricing card, filter chip row",validate:(v)=>v?void 0:"Describe the component so the agent knows what to redesign."});if(desc===null)return cancelled();return outcome(await promptGoalWithAdoptedLibrary(hostCtx,folder,agentWiring,"component",{componentDescription:desc}))}if(goal==="custom"){let request=await promptBrief({message:"What design do you need?",subtitle:"A sentence is enough \u2014 it becomes the brief your agent works from.",placeholder:"e.g. a pricing page with three tiers and a FAQ",validate:(v)=>v?void 0:"Describe what you want designed."});if(request===null)return cancelled();return outcome(await promptGoalWithAdoptedLibrary(hostCtx,folder,agentWiring,"custom",{customRequest:request}))}if(goal==="capture-site"){let url=await promptBrief({message:"Site to design from",subtitle:"Init won't open it \u2014 your agent starts a capture session so you can log in first.",placeholder:"e.g. https://app.example.com/dashboard",validate(v){if(!v)return"Enter the URL of the site you want to design from.";try{let scheme=new URL(v).protocol;if(scheme!=="http:"&&scheme!=="https:")return"Use an http(s) URL."}catch{return"That doesn't look like a URL."}return}});if(url===null)return cancelled();return outcome(await promptGoalWithAdoptedLibrary(hostCtx,folder,agentWiring,"custom",{captureUrl:url,customRequest:`Recreate the pages I capture from ${url}, then explore alternatives.`}))}if(goal==="blank")return outcome(await buildBlankAnswers(ctx,folder,agentWiring));return outcome(await promptSample(hostCtx,folder,agentWiring))}var import_picocolors8=__toESM(require_picocolors(),1);var NOT_WIRED={wiredIds:[]};async function applyAgentWiring(folder,wiring){let result;if(wiring.agents.length>0)try{result=await connect({designFolder:folder,agents:wiring.agents,installSkill:!0})}catch{result=void 0}if(wiring.manual){console.log(""),console.log(import_picocolors8.default.bold(" Manual MCP setup"));for(let line of manualSetupText().split(`
23
23
  `))console.log(` ${line}`)}return{connected:result,wiredIds:[...wiring.preWired,...result?.configs.map((c)=>c.agent)??[]]}}function printWired(outcome){let connected=outcome.connected;if(!connected||connected.configs.length===0)return;let wired=connected.configs.map((c)=>c.agent).join(" + ");if(console.log(""),console.log(import_picocolors8.default.bold(" Agents wired")),console.log(` ${import_picocolors8.default.green("\u2713")} ${wired} ${import_picocolors8.default.dim(`(MCP config under ${connected.projectRoot})`)}`),connected.plugin)console.log(import_picocolors8.default.dim(" + Claude Code plugin (skills \xB7 commands \xB7 subagents)"));if(connected.skills?.length)console.log(import_picocolors8.default.dim(` + ${connected.skills.length} agent skill${connected.skills.length===1?"":"s"} (.agents/skills)`));if(connected.geminiExtension)console.log(import_picocolors8.default.dim(` + Gemini extension${connected.geminiExtension.linked?"":` (link it: gemini extensions link ${connected.geminiExtension.dir})`}`));if(connected.cursorRules?.installed)console.log(import_picocolors8.default.dim(" + Cursor rule"))}import{resolve as resolve6}from"path";var import_picocolors10=__toESM(require_picocolors(),1);import{mkdir as mkdir2,readdir as readdir2,readFile as readFile4,writeFile as writeFile2}from"fs/promises";import{join as join4,relative as relative4,resolve as resolve5}from"path";var import_picocolors9=__toESM(require_picocolors(),1);async function readConfig2(folder){return JSON.parse(await readFile4(join4(folder,".design","config.json"),"utf8"))??{}}async function readFolderFacts(folder,appRoot){let config=await readConfig2(folder),libraries=config.libraries??{},defaultLibrary=typeof config.defaultLibrary==="string"?config.defaultLibrary:"",library=libraries[defaultLibrary]?.id??Object.values(libraries)[0]?.id??"unknown",design;try{design=(await findDesigns(appRoot))?.designs.find((d)=>resolve5(d.root)===resolve5(folder))?.name}catch{}return{folder,library,schemaVersion:schemaVersionOf(config),pendingMigrations:planMigration(config).applied,toolVersionStale:config.toolVersion!==TOOL_VERSION,design}}function displayPath2(path){let rel=relative4(process.cwd(),path);return rel&&rel.length<path.length&&!rel.startsWith("../..")?rel:path}async function inheritedFromFolder(folder){try{let config=await readConfig2(folder),libraries=config.libraries??{},defaultLibrary=typeof config.defaultLibrary==="string"?config.defaultLibrary:"",codegen=config.codegen??{};return{library:libraries[defaultLibrary]?.id??Object.values(libraries)[0]?.id,componentsDir:codegen.componentsDir}}catch{return{library:void 0,componentsDir:void 0}}}function factsLine(facts,folder){let parts=[`${displayPath2(folder)}/`,facts.library,facts.pendingMigrations.length>0?`format v${facts.schemaVersion} \u2192 v${CURRENT_SCHEMA_VERSION}`:`format v${facts.schemaVersion} (current)`];if(facts.design)parts.push(`design "${facts.design}"`);return parts.join(" \xB7 ")}async function promptExistingFolderAction(facts,folder){let options=[];if(facts.pendingMigrations.length>0)options.push({value:"upgrade",label:"Upgrade folder format",hint:`v${facts.schemaVersion} \u2192 v${CURRENT_SCHEMA_VERSION}`});options.push({value:"another",label:"Create another design folder",hint:"a second canvas in this repo"},{value:"connect",label:"Connect agents",hint:"wire the MCP into Claude Code / Cursor"},{value:"scan",label:"Scan for new routes",hint:"add screens for routes you've since added"},{value:"theme",label:"Re-import theme from the app",hint:"pull the app's tokens again"},{value:"check",label:"Check setup",hint:"browser, agent wiring, velloo.json, host app"},{value:"open",label:"Open the canvas",hint:"velloo run"},{value:"cancel",label:"Cancel"}),log.info(import_picocolors9.default.dim(factsLine(facts,folder)));let action=await select({message:"Velloo is already set up here. What would you like to do?",options,initialValue:options[0]?.value??"cancel"});if(isCancel(action))return null;return action}async function runUpgrade(folder){let result=await upgradeFolder(folder);if(result.applied.length===0){console.log(import_picocolors9.default.dim(" Already on the current format \u2014 nothing to do."));return}console.log(`velloo: upgraded ${displayPath2(folder)} (v${result.from} \u2192 v${result.to})`);for(let step of result.applied)console.log(import_picocolors9.default.dim(` ${step}`));console.log(import_picocolors9.default.dim(` Refresh agent skills too with \`velloo upgrade ${displayPath2(folder)}\`.`))}async function runScan(folder,appRoot,scanDir){let scanned=await scanApps(appRoot,scanDir);if(scanned.routes.length===0){log.warn("No routes found \u2014 nothing to add.");return}let existing=[];try{existing=(await readdir2(join4(folder,"screens"))).filter((f)=>f.endsWith(".json")&&!f.endsWith(".annotations.json")).map((f)=>f.slice(0,-5))}catch{}let seen=new Set(existing),fresh=scanned.routes.filter((r)=>!seen.has(r.id));if(fresh.length===0){log.success(`All ${scanned.routes.length} routes already have screens \u2014 nothing to add.`);return}log.info(`${fresh.length} new route${fresh.length===1?"":"s"}: ${fresh.slice(0,8).map((r)=>r.routePath).join(", ")}${fresh.length>8?", \u2026":""}`);let go=await confirm({message:`Add ${fresh.length} placeholder screen(s) on a new board?`});if(isCancel(go)||!go){console.log(import_picocolors9.default.dim(" Nothing changed."));return}let libraries=(await readConfig2(folder)).libraries??{},libraryId=Object.values(libraries)[0]?.id??"shadcn-upstream",scanOpts=WIZARD_PROVIDERS[libraryId]?.scanScreenOpts??{hasBadge:!0},screens=buildScreensFromScan({routes:fresh,...scanOpts}),boards=buildBoardsFromScan({routes:fresh});await mkdir2(join4(folder,"screens"),{recursive:!0}),await mkdir2(join4(folder,"boards"),{recursive:!0});for(let screen of screens)await writeJson(join4(folder,"screens",`${screen.id}.json`),screen);let stamp=new Date().toISOString().slice(0,10),existingBoards=new Set((await readdir2(join4(folder,"boards")).catch(()=>[])).filter((f)=>f.endsWith(".json")&&!f.endsWith(".notes.json")).map((f)=>f.slice(0,-5)));for(let board of boards){let id=`${board.id}-${stamp}`,attempt=2;while(existingBoards.has(id))id=`${board.id}-${stamp}-${attempt++}`;existingBoards.add(id),await writeJson(join4(folder,"boards",`${id}.json`),{...board,id,name:`${board.name} \u2014 new routes ${stamp}`})}log.success(`Added ${screens.length} screen${screens.length===1?"":"s"} on ${boards.length} board${boards.length===1?"":"s"}.`)}async function runThemeReimport(folder,appRoot){let scanRoot=(await scanApps(appRoot)).apps[0]?.dir??appRoot,detected=detectHost(scanRoot),imported=null;if(detected.uiLibrary==="mui"){let themeFile=findMuiTheme(scanRoot);if(themeFile)imported=importThemeFromMui(themeFile)}if(!imported&&detected.globalsCssPath)imported=importThemeFromGlobals(detected.globalsCssPath);if(!imported){log.warn("Couldn't find a theme to import \u2014 no globals.css tokens and no MUI createTheme() in the app.");return}let from=displayPath2(imported.importedFrom);log.info(`Found ${imported.tokenCount} color token(s) in ${from}.`);let go=await confirm({message:"Replace theme/default.json with the app's tokens? Custom edits to it are lost.",initialValue:!1});if(isCancel(go)||!go){console.log(import_picocolors9.default.dim(" Nothing changed."));return}await writeJson(join4(folder,"theme","default.json"),imported.theme),log.success(`Theme re-imported from ${from}.`)}async function runCheckSetup(folder,appRoot){let facts=await readFolderFacts(folder,appRoot),config=await readConfig2(folder),ok=(s)=>console.log(` ${import_picocolors9.default.green("\u2713")} ${s}`),bad=(s)=>console.log(` ${import_picocolors9.default.yellow("!")} ${s}`);if(facts.pendingMigrations.length>0)bad(`folder format v${facts.schemaVersion} \u2014 v${CURRENT_SCHEMA_VERSION} available (\`velloo upgrade <design>\`)`);else ok(`folder format v${facts.schemaVersion} (current)`);if(facts.toolVersionStale)bad(`agent skills recorded for velloo ${String(config.toolVersion)} \u2014 refresh with \`velloo upgrade <design>\``);else ok(`agent skills current (velloo ${TOOL_VERSION})`);if(facts.design)ok(`listed as design "${facts.design}"`);else bad("not listed in velloo.json \u2014 commands must name the path");let app=await recordedAppRoot(folder).catch(()=>null);if(app)if(!app.exists)bad(`host app ${displayPath2(app.path)} no longer exists`);else if(!app.looksLikeApp)bad(`host app ${displayPath2(app.path)} holds no package.json`);else ok(`host app at ${displayPath2(app.path)}`);let wired=await globallyWiredAgents();if(wired.length>0)ok(`agents wired globally: ${wired.join(", ")}`);else bad('no globally wired agents \u2014 run init again and pick "Connect agents"');let browser=await chromiumExecutable();if(browser)ok("browser installed (screenshots + publish)");else bad(`no browser for screenshots \u2014 install with \`${CHROMIUM_INSTALL_CMD}\``);if(!facts.design){let go=await confirm({message:"List this design in velloo.json?"});if(!isCancel(go)&&go)try{let reg=await registerDesign(folder,appRoot);if(reg.created)log.success(`Listed design "${reg.name}" in velloo.json.`);else log.info("Already listed.")}catch(err){log.warn(err.message)}}if(app&&!app.looksLikeApp)await repairAppRoot(folder,app.path);if(!browser)await installChromiumInteractive()}async function repairAppRoot(folder,current){let go=await confirm({message:"Point this design at a different application?"});if(isCancel(go)||!go)return;let target=await promptAppRootChoice(current);if(!target){log.info("No other application found in this repo \u2014 `velloo design set-app-root --to <path>`.");return}let change=await planAppRootChange(folder,target);if(resolve5(change.to)===resolve5(change.from))return;await stopDaemon(daemonRoot(folder)),await applyAppRootChange(folder,change),log.success(`Application root is now ${displayPath2(change.to)}.`)}async function runOpenCanvas(folder){let rec=await ensureDaemon(folder);console.log(`velloo: canvas at ${rec.canvasUrl}`)}async function writeJson(path,value){await writeFile2(path,`${JSON.stringify(value,null,2)}
24
- `,"utf8")}async function offerExistingFolderActions(appRoot,cliArgs){let designs=await findDesigns(appRoot),picked=designs?pickDesign(designs,appRoot):null,existing=(picked&&picked.reason!=="arbitrary"&&!cliArgs.designFolder?await resolveDesign(picked.design.root,"init",{cwd:appRoot,interactive:!0}):null)??(cliArgs.addFolder?await existingDesignFolder(appRoot)??resolve6(appRoot,"velloo"):resolve6(appRoot,cliArgs.designFolder??"velloo"));if(!await hasDesignConfig(existing))return{kind:"none"};let contextRoot=localDesignOf(existing)?.appRoot??appRoot,facts=await readFolderFacts(existing,contextRoot),action=cliArgs.addFolder?"another":await promptExistingFolderAction(facts,existing);if(action===null||action==="cancel")return console.log(import_picocolors10.default.dim(" Nothing changed.")),{kind:"handled"};if(action==="connect"){let wiring=await askAgentWiring({skipWhenCovered:!0,projectRoot:await resolveProjectRoot(existing),globalOnly:localDesignOf(existing)!==null}),wireOutcome=wiring?await applyAgentWiring(existing,wiring):NOT_WIRED;return printWired(wireOutcome),printExitInstructions(existing,wireOutcome),{kind:"handled"}}if(action!=="another"){if(action==="upgrade")await runUpgrade(existing);if(action==="scan")await runScan(existing,contextRoot,cliArgs.scanDir);if(action==="theme")await runThemeReimport(existing,contextRoot);if(action==="check")await runCheckSetup(existing,contextRoot);if(action==="open")await runOpenCanvas(existing);return{kind:"handled"}}return{kind:"another",inherited:await inheritedFromFolder(existing)}}var init_default=defineCommand({meta:{name:"init",description:"Scaffold a new Velloo design folder"},args:{folder:{type:"positional",required:!1,description:"Your app root \u2014 where Velloo installs (default: current directory)"},designFolder:{type:"string",description:"Design folder, relative to the app root (default: velloo)"},external:{type:"boolean",description:"Keep the design in Velloo-managed storage outside the application repository"},force:{type:"boolean",default:!1,description:"Allow scaffolding into a non-empty design folder"},nonInteractive:{type:"boolean",default:!1,description:"Skip the wizard and use defaults / flags"},connect:{type:"boolean",default:!0,description:"Wire your AI agents' MCP config + guidance \u2014 Claude Code / Cursor / Codex / Continue; non-interactive default wires Claude Code + Cursor (use --no-connect to skip)"},start:{type:"string",description:"Goal: redesign-screen | redesign-component | custom | sample | blank | scan (legacy multi-route)"},scanDir:{type:"string",description:"Subfolder to scan when your UI isn't at the root (e.g. web/frontend). Auto-detected if omitted."},library:{type:"string",description:`Component library: ${LIBRARY_IDS.join(" | ")} (default ${DEFAULT_LIBRARY_ID})`},componentsDir:{type:"string",description:"Upstream components subfolder inside your app (default src/components/ui)"},initialContent:{type:"string",description:"Initial content: sample | blank | redesign-screen | component | custom | scan"},screenName:{type:"string",description:"Screen name for --start=redesign-screen (optional if routes are scanned)"},component:{type:"string",description:"Component name or description for --start=redesign-component"},request:{type:"string",description:"Free-text design request for --start=custom"},themePreset:{type:"string",description:"Theme preset: elsewhere | indigo | violet | blue | emerald | rose | orange | amber | zinc"},stack:{type:"string",description:"Your app's stack \u2014 sets the emitted import alias: nextjs | vite | astro (@/components/ui) | remix (~/components/ui)"},name:{type:"string",description:"The design's name (default: derived from its folder, or the app it designs)"}},run:({args})=>runInit2(args)});async function runInit2(cliArgs){let appRoot=resolve7(cliArgs.folder??"."),launchRoot=appRoot,interactive=shouldRunWizard(cliArgs,Boolean(process.stdin.isTTY)),allowNonEmpty=cliArgs.force,secondFolder=!1,inherited={library:void 0,componentsDir:void 0};if(cliArgs.scanDir&&!await dirExists(resolve7(appRoot,cliArgs.scanDir)))fail("init",`--scan-dir "${cliArgs.scanDir}" doesn't exist under ${appRoot}.`);if(interactive&&await offerUpgradeBeforeInit(process.argv.slice(2)))return;if(interactive)printLogo();if(interactive&&!cliArgs.force&&!cliArgs.external){let existing=await offerExistingFolderActions(appRoot,cliArgs);if(existing.kind==="handled")return;if(existing.kind==="another")secondFolder=!0,inherited=existing.inherited}if(interactive&&!cliArgs.folder){let candidates=await appRootIsNotAnApp(appRoot).catch(()=>null);if(candidates){console.log(import_picocolors11.default.dim(` ${appRoot} holds ${candidates.length} app${candidates.length===1?"":"s"} but isn't one itself.`));let chosen=await promptAppRootChoice(appRoot,{currentHint:"this directory"});if(chosen)appRoot=chosen}}let external=cliArgs.external===!0,presetFolder=cliArgs.designFolder;if(external&&cliArgs.designFolder)fail("init","Choose --external or --design-folder, not both.");let candidateId=randomUUID2(),candidateFolder=managedDesignPath(candidateId);if(interactive&&cliArgs.external===void 0&&!cliArgs.designFolder){let storage=await select({message:"Where should the design live?",options:[{value:"repository",label:"Default in repo",hint:secondFolder?"./velloo-brand/":"./velloo/"},{value:"external",label:"Default out of repo",hint:`${displayPath(dirname2(candidateFolder))}/ \u2014 not version-controlled`},{value:"custom",label:"Custom name",hint:"Choose a design folder name or path"}]});if(isCancel(storage))return;if(external=storage==="external",storage==="repository")presetFolder=secondFolder?"velloo-brand":"velloo"}let managedId=external?candidateId:void 0,managedFolder=managedId?candidateFolder:void 0,promptedName;if(interactive&&managedFolder&&!cliArgs.name){if(promptedName=await promptDesignName(managedFolder,appRoot,launchRoot),promptedName===void 0)return}let answers;if(interactive){if(console.log(import_picocolors11.default.dim(` App root: ${appRoot} (the app this design targets)`)),managedFolder)console.log(import_picocolors11.default.dim(` Design: ${displayPath(managedFolder)} (outside the repo, not version-controlled)`));else if(presetFolder)console.log(import_picocolors11.default.dim(` Design: ${displayPath(resolve7(launchRoot,presetFolder))}`));if(launchRoot!==appRoot)console.log(import_picocolors11.default.dim(` Design folder and agent config go under ${launchRoot}.`));console.log("");let inheritedLibrary=inherited.library&&isValidLibraryId(inherited.library)?inherited.library:void 0,result=await runInteractive({appRoot,folderBase:launchRoot,secondFolder,...managedFolder||presetFolder?{presetFolder:managedFolder??presetFolder}:{},scanDir:cliArgs.scanDir,connectEnabled:cliArgs.connect!==!1,pinnedLibrary:cliArgs.library&&isValidLibraryId(cliArgs.library)?cliArgs.library:inheritedLibrary,...inheritedLibrary&&!cliArgs.library?{pinnedLibraryReason:"same as the design folder already in this repo"}:{},...inherited.componentsDir?{inheritComponentsDir:inherited.componentsDir}:{}});if(result.status==="abort")printExitInstructions(void 0,NOT_WIRED),process.exit(1);answers=result.answers}else{try{answers=answersFromArgs(cliArgs)}catch(err){fail("init",err.message)}console.log(`velloo: app root ${answers.appRoot}`)}if((answers.initialContent==="scan"||answers.initialContent==="redesign-screen")&&!answers.detected){let scanned=await scanApps(answers.appRoot,cliArgs.scanDir),primary=scanned.apps[0];if(primary)answers.scanRoot=primary.dir;if(scanned.apps.length>1)console.log(import_picocolors11.default.dim(` Found ${scanned.apps.length} apps (${scanned.apps.map((a)=>a.rel||".").join(", ")}) \u2014 one board per app.`));else if(primary?.rel&&primary.rel!==".")console.log(import_picocolors11.default.dim(` Scanning UI in ${primary.rel} (app root has no package.json).`));if(answers.selectedRoutes=answers.initialContent==="redesign-screen"?scanned.routes.slice(0,1):scanned.routes,answers.agentPicksFirst=answers.initialContent==="scan",answers.initialContent==="redesign-screen"&&scanned.routes[0])answers.screenName=answers.screenName??scanned.routes[0].name;answers.detected=detectHost(answers.scanRoot);let adopted=cliArgs.library?void 0:scanAdoption(answers.detected);if(adopted)answers.library=adopted.library,answers.source="binary",console.log(import_picocolors11.default.dim(` ${adopted.note}`))}if(managedFolder)answers.folder=managedFolder;let folder=answers.folder;if(!allowNonEmpty&&!await isEmptyOrMissing(folder))fail("init",`design folder is not empty: ${folder}
24
+ `,"utf8")}async function offerExistingFolderActions(appRoot,cliArgs){let designs=await findDesigns(appRoot),picked=designs?pickDesign(designs,appRoot):null,existing=(picked&&picked.reason!=="arbitrary"&&!cliArgs.designFolder?await resolveDesign(picked.design.root,"init",{cwd:appRoot,interactive:!0}):null)??(cliArgs.addFolder?await existingDesignFolder(appRoot)??resolve6(appRoot,"velloo"):resolve6(appRoot,cliArgs.designFolder??"velloo"));if(!await hasDesignConfig(existing))return{kind:"none"};let contextRoot=localDesignOf(existing)?.appRoot??appRoot,facts=await readFolderFacts(existing,contextRoot),action=cliArgs.addFolder?"another":await promptExistingFolderAction(facts,existing);if(action===null||action==="cancel")return console.log(import_picocolors10.default.dim(" Nothing changed.")),{kind:"handled"};if(action==="connect"){let wiring=await askAgentWiring({skipWhenCovered:!0,projectRoot:await resolveProjectRoot(existing),globalOnly:localDesignOf(existing)!==null}),wireOutcome=wiring?await applyAgentWiring(existing,wiring):NOT_WIRED;return printWired(wireOutcome),printExitInstructions(existing,wireOutcome),{kind:"handled"}}if(action!=="another"){if(action==="upgrade")await runUpgrade(existing);if(action==="scan")await runScan(existing,contextRoot,cliArgs.scanDir);if(action==="theme")await runThemeReimport(existing,contextRoot);if(action==="check")await runCheckSetup(existing,contextRoot);if(action==="open")await runOpenCanvas(existing);return{kind:"handled"}}return{kind:"another",inherited:await inheritedFromFolder(existing)}}var init_default=defineCommand({meta:{name:"init",description:"Scaffold a new Velloo design folder"},args:{folder:{type:"positional",required:!1,description:"Your app root \u2014 where Velloo installs (default: current directory)"},designFolder:{type:"string",description:"Design folder, relative to the app root (default: velloo)"},external:{type:"boolean",description:"Keep the design in Velloo-managed storage outside the application repository"},force:{type:"boolean",default:!1,description:"Allow scaffolding into a non-empty design folder"},nonInteractive:{type:"boolean",default:!1,description:"Skip the wizard and use defaults / flags"},connect:{type:"boolean",default:!0,description:"Wire your AI agents' MCP config + guidance \u2014 Claude Code / Cursor / Codex / Continue; non-interactive default wires Claude Code + Cursor (use --no-connect to skip)"},start:{type:"string",description:"Goal: redesign-screen | redesign-component | custom | sample | blank | scan (legacy multi-route)"},scanDir:{type:"string",description:"Subfolder to scan when your UI isn't at the root (e.g. web/frontend). Auto-detected if omitted."},library:{type:"string",description:`Component library: ${LIBRARY_IDS.join(" | ")} (default ${DEFAULT_LIBRARY_ID})`},componentsDir:{type:"string",description:"Upstream components subfolder inside your app (default src/components/ui)"},initialContent:{type:"string",description:"Initial content: sample | blank | redesign-screen | component | custom | scan"},screenName:{type:"string",description:"Screen name for --start=redesign-screen (optional if routes are scanned)"},component:{type:"string",description:"Component name or description for --start=redesign-component"},request:{type:"string",description:"Free-text design request for --start=custom"},themePreset:{type:"string",description:"Theme preset: elsewhere | indigo | violet | blue | emerald | rose | orange | amber | zinc"},stack:{type:"string",description:"Your app's stack \u2014 sets the emitted import alias: nextjs | vite | astro (@/components/ui) | remix (~/components/ui)"},name:{type:"string",description:"The design's name (default: derived from its folder, or the app it designs)"}},run:({args})=>runInit2(args)});async function runInit2(cliArgs){let appRoot=resolve7(cliArgs.folder??"."),launchRoot=appRoot,interactive=shouldRunWizard(cliArgs,Boolean(process.stdin.isTTY)),allowNonEmpty=cliArgs.force,secondFolder=!1,inherited={library:void 0,componentsDir:void 0};if(cliArgs.scanDir&&!await dirExists(resolve7(appRoot,cliArgs.scanDir)))fail("init",`--scan-dir "${cliArgs.scanDir}" doesn't exist under ${appRoot}.`);if(interactive&&await offerUpgradeBeforeInit(process.argv.slice(2)))return;if(interactive)printLogo();if(interactive&&!cliArgs.force&&!cliArgs.external){let existing=await offerExistingFolderActions(appRoot,cliArgs);if(existing.kind==="handled")return;if(existing.kind==="another")secondFolder=!0,inherited=existing.inherited}if(interactive&&!cliArgs.folder){let candidates=await appRootIsNotAnApp(appRoot).catch(()=>null);if(candidates){console.log(import_picocolors11.default.dim(` ${appRoot} holds ${candidates.length} app${candidates.length===1?"":"s"} but isn't one itself.`));let chosen=await promptAppRootChoice(appRoot,{currentHint:"this directory"});if(chosen)appRoot=chosen}}let external=cliArgs.external===!0,presetFolder=cliArgs.designFolder;if(external&&cliArgs.designFolder)fail("init","Choose --external or --design-folder, not both.");let candidateId=randomUUID2(),candidateFolder=managedDesignPath(candidateId);if(interactive&&cliArgs.external===void 0&&!cliArgs.designFolder){let storage=await select({message:"Where should the design live?",options:[{value:"repository",label:"Default in repo",hint:secondFolder?"./velloo-brand/":"./velloo/"},{value:"external",label:"Default out of repo",hint:`${displayPath(dirname2(candidateFolder))}/ \u2014 not version-controlled`},{value:"custom",label:"Custom name",hint:"Choose a design folder name or path"}]});if(isCancel(storage))return;if(external=storage==="external",storage==="repository")presetFolder=secondFolder?"velloo-brand":"velloo"}let managedId=external?candidateId:void 0,managedFolder=managedId?candidateFolder:void 0,promptedName;if(interactive&&managedFolder&&!cliArgs.name){if(promptedName=await promptDesignName(managedFolder,appRoot,launchRoot),promptedName===void 0)return}let answers;if(interactive){if(console.log(import_picocolors11.default.dim(` App root: ${appRoot} (the app this design targets)`)),managedFolder)console.log(import_picocolors11.default.dim(` Design: ${displayPath(managedFolder)} (outside the repo, not version-controlled)`));else if(presetFolder)console.log(import_picocolors11.default.dim(` Design: ${displayPath(resolve7(launchRoot,presetFolder))}`));if(launchRoot!==appRoot)console.log(import_picocolors11.default.dim(` Design folder and agent config go under ${launchRoot}.`));console.log("");let inheritedLibrary=inherited.library&&isValidLibraryId(inherited.library)?inherited.library:void 0,result=await runInteractive({appRoot,folderBase:launchRoot,secondFolder,...managedFolder||presetFolder?{presetFolder:managedFolder??presetFolder}:{},scanDir:cliArgs.scanDir,connectEnabled:cliArgs.connect!==!1,pinnedLibrary:cliArgs.library&&isValidLibraryId(cliArgs.library)?cliArgs.library:inheritedLibrary,...inheritedLibrary&&!cliArgs.library?{pinnedLibraryReason:"same as the design folder already in this repo"}:{},...inherited.componentsDir?{inheritComponentsDir:inherited.componentsDir}:{}});if(result.status==="abort")printExitInstructions(void 0,NOT_WIRED),process.exit(1);answers=result.answers}else{try{answers=answersFromArgs(cliArgs)}catch(err){fail("init",err.message)}console.log(`velloo: app root ${answers.appRoot}`)}let scansRoutes=answers.initialContent==="scan"||answers.initialContent==="redesign-screen";if((scansRoutes||answers.initialContent==="blank")&&!answers.detected){let scanned=await scanApps(answers.appRoot,cliArgs.scanDir),primary=scanned.apps[0];if(primary)answers.scanRoot=primary.dir;if(scansRoutes&&scanned.apps.length>1)console.log(import_picocolors11.default.dim(` Found ${scanned.apps.length} apps (${scanned.apps.map((a)=>a.rel||".").join(", ")}) \u2014 one board per app.`));else if(scansRoutes&&primary?.rel&&primary.rel!==".")console.log(import_picocolors11.default.dim(` Scanning UI in ${primary.rel} (app root has no package.json).`));if(scansRoutes){if(answers.selectedRoutes=answers.initialContent==="redesign-screen"?scanned.routes.slice(0,1):scanned.routes,answers.agentPicksFirst=answers.initialContent==="scan",answers.initialContent==="redesign-screen"&&scanned.routes[0])answers.screenName=answers.screenName??scanned.routes[0].name}answers.detected=detectHost(answers.scanRoot);let adopted=cliArgs.library?void 0:scanAdoption(answers.detected);if(adopted)answers.library=adopted.library,answers.source="binary",console.log(import_picocolors11.default.dim(` ${adopted.note}`))}if(managedFolder)answers.folder=managedFolder;let folder=answers.folder;if(!allowNonEmpty&&!await isEmptyOrMissing(folder))fail("init",`design folder is not empty: ${folder}
25
25
  Pick an empty path, or pass --force to scaffold over it.`);let plan;try{plan=planInstall(answers)}catch(err){fail("init",err.message)}if(interactive)console.log(import_picocolors11.default.dim(" Scaffolding your design folder\u2026"));let{theme,importedFrom}=resolveTheme(answers),scaffold;try{scaffold=await buildScaffold(answers,theme)}catch(err){fail("init",err.message)}let root=launchRoot,localId=managedId??(isWithin(root,folder)?void 0:randomUUID2()),name;try{name=await chooseDesignName({folder,appRoot:answers.appRoot,storage:localId?managedId?"managed":"chosen":"repository",checkout:root,requested:cliArgs.name??promptedName})}catch(err){fail("init",err.message)}try{if(await writeScaffold(folder,scaffold,plan,answers,name,localId!==void 0),localId)await rebaseDesignConfig(folder,folder,answers.appRoot,!0)}catch(error){if(localId)throw Error(`Could not finish the design at ${folder}. Existing content was retained. Check write permissions and free disk space before retrying. ${error instanceof Error?error.message:error}`);throw error}console.log(`velloo: scaffolded ${folder} (${plan.library.id} ${plan.library.version})`);try{if(localId)await registerLocalDesign({id:localId,root,appRoot:answers.appRoot,...managedId?{}:{designPath:folder}}),console.log(import_picocolors11.default.dim(` Recorded as local design "${name}" for ${displayPath(root)} \u2014 on this machine only; nothing was added to the repository.`));else{let reg=await registerDesign(folder,root);if(reg.created)console.log(import_picocolors11.default.dim(` Listed design "${reg.name}" in ${relative5(process.cwd(),reg.path)||reg.path}.`))}if(answers.feedback)await writeRepoFeedback(folder,answers.feedback),await writeFeedbackContactOk(answers.feedback.contactOk===!0)}catch(err){if(localId)fail("init",`Design retained at ${folder}, but registration failed: ${err.message}`);console.log(import_picocolors11.default.yellow(` Couldn't update the repo manifest: ${err.message}`))}if(importedFrom)console.log(import_picocolors11.default.dim(` Imported your theme from ${relative5(answers.appRoot,importedFrom)}.`));if(answers.initialContent==="scan"&&scaffold.screens.length===0)console.log(import_picocolors11.default.dim(" No routes detected \u2014 started you with a blank folder instead of a scan."));printSummary(folder,scaffold,plan,answers,importedFrom);let wireOutcome=NOT_WIRED;if(cliArgs.connect!==!1){let wiring=interactive?answers.agentWiring:localId?void 0:{agents:PROJECT_AGENT_IDS,manual:!1,preWired:[]};if(wiring)wireOutcome=await applyAgentWiring(folder,wiring);else if(localId&&!interactive)console.log(import_picocolors11.default.dim(" Agents not wired: a local design uses global configs \u2014 run `velloo connect`."))}printWired(wireOutcome),await printScreenshotReadiness(interactive),await promptShellCompletions(interactive),await printAgentHandoff(answers,scaffold,interactive,wireOutcome.wiredIds),printExitInstructions(folder,wireOutcome)}async function promptDesignName(folder,appRoot,checkout){let request={folder,appRoot,storage:"managed",checkout},suggestion=await chooseDesignName(request).catch(()=>{return});for(;;){let answer=await text({message:"Name this design",...suggestion?{placeholder:suggestion,initialValue:suggestion}:{},validate:(value)=>designNameIssue((value??"").trim())??void 0});if(isCancel(answer))return;try{return await chooseDesignName({...request,requested:answer.trim()})}catch(err){log.warn(err.message)}}}
26
26
  export{recordedAppRoot,planAppRootChange,applyAppRootChange,promptAppRootChoice,planRelocation,relocateDesign,init_default,runInit2};
@@ -1,2 +1,2 @@
1
1
  // @bun
2
- import"./chunk-t448rrfr.js";import"./chunk-40xgcth8.js";import{normalizeCloudUrl,loadCredential,deleteCredential,clearCredentials}from"./chunk-80r93t2e.js";import{defaultCloudUrl}from"./chunk-pp86v6m6.js";import{defineCommand}from"./chunk-2smsg2ct.js";var logout_default=defineCommand({meta:{name:"logout",description:"Log out of velloo-cloud (removes the saved CLI token)"},args:{url:{type:"string",description:"velloo-cloud base URL (default: $VELLOO_CLOUD_URL or the built-in default)"},all:{type:"boolean",default:!1,description:"Remove saved credentials for every cloud"}},async run({args}){if(args.all){let count=await clearCredentials();console.log(count>0?`velloo logout: removed credentials for ${count} cloud${count===1?"":"s"}`:"velloo logout: no saved credentials");return}let cloudUrl=args.url?normalizeCloudUrl(args.url):defaultCloudUrl(),existing=await loadCredential(cloudUrl);if(await deleteCredential(cloudUrl))console.log(`velloo logout: logged out of ${cloudUrl}${existing?.email?` (${existing.email})`:""}`);else console.log(`velloo logout: not logged in to ${cloudUrl}`)}});export{logout_default as default};
2
+ import"./chunk-n9t36h0q.js";import"./chunk-nte0t7v3.js";import{normalizeCloudUrl,loadCredential,deleteCredential,clearCredentials}from"./chunk-hfzg98c8.js";import{defaultCloudUrl}from"./chunk-as08hzp3.js";import{defineCommand}from"./chunk-2smsg2ct.js";var logout_default=defineCommand({meta:{name:"logout",description:"Log out of velloo-cloud (removes the saved CLI token)"},args:{url:{type:"string",description:"velloo-cloud base URL (default: $VELLOO_CLOUD_URL or the built-in default)"},all:{type:"boolean",default:!1,description:"Remove saved credentials for every cloud"}},async run({args}){if(args.all){let count=await clearCredentials();console.log(count>0?`velloo logout: removed credentials for ${count} cloud${count===1?"":"s"}`:"velloo logout: no saved credentials");return}let cloudUrl=args.url?normalizeCloudUrl(args.url):defaultCloudUrl(),existing=await loadCredential(cloudUrl);if(await deleteCredential(cloudUrl))console.log(`velloo logout: logged out of ${cloudUrl}${existing?.email?` (${existing.email})`:""}`);else console.log(`velloo logout: not logged in to ${cloudUrl}`)}});export{logout_default as default};
@@ -1,3 +1,3 @@
1
1
  // @bun
2
- import{AuthConfigResponseSchema,AccountResponseSchema,DeviceCodeResponseSchema,DeviceTokenResponseSchema,CliTokenResponseSchema,readFailure}from"./chunk-t448rrfr.js";import{openUrl}from"./chunk-hggap8fq.js";import{isSecureCloudUrl,assertSecureCloudUrl}from"./chunk-pp86v6m6.js";async function fetchAccount(cloudUrl,token,timeoutMs=8000){if(!isSecureCloudUrl(cloudUrl))return{status:"unreachable"};let res=await fetch(`${cloudUrl}/v1/me`,{headers:{authorization:`Bearer ${token}`},signal:AbortSignal.timeout(timeoutMs)}).catch(()=>null);if(!res)return{status:"unreachable"};if(res.status===401||res.status===403)return{status:"rejected"};if(!res.ok)return{status:"unreachable"};let parsed=AccountResponseSchema.safeParse(await res.json().catch(()=>null));if(!parsed.success)return{status:"unreachable"};let body=parsed.data;return{status:"ok",account:{email:body.email,...body.name?{name:body.name}:{},...body.tier?{tier:body.tier}:{},...body.creditMicros!==void 0?{creditMicros:body.creditMicros}:{}}}}async function verifyCredential(cloudUrl,token,timeoutMs=8000){let found=await fetchAccount(cloudUrl,token,timeoutMs);return found.status==="ok"?found.account.email:null}var sleepCancelable=(ms,signal)=>new Promise((resolve,reject)=>{if(signal.aborted){reject(Error("cancelled"));return}let timer=setTimeout(resolve,ms);signal.addEventListener("abort",()=>{clearTimeout(timer),reject(Error("cancelled"))},{once:!0})});async function performDeviceLogin(cloudUrl,onPrompt,signal){assertSecureCloudUrl(cloudUrl);let configRes=await fetch(`${cloudUrl}/v1/auth/config`).catch(()=>null);if(!configRes?.ok)throw Error(`cannot reach ${cloudUrl} \u2014 is velloo-cloud up?`);let config=AuthConfigResponseSchema.safeParse(await configRes.json().catch(()=>null)),issuer=config.success?config.data.issuer:void 0,clientId=config.success?config.data.clientId:void 0;if(issuer===void 0||clientId===void 0)throw Error(`${cloudUrl} does not advertise a sign-in service \u2014 is velloo-cloud current?`);let codeRes=await fetch(`${issuer}/api/auth/device/code`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({client_id:clientId})}).catch(()=>null);if(!codeRes?.ok)throw Error(`the auth service at ${issuer} is not answering \u2014 is it up?`);let deviceBody=DeviceCodeResponseSchema.safeParse(await codeRes.json().catch(()=>null));if(!deviceBody.success)throw Error(`the auth service at ${issuer} answered something this velloo can't read`);let device=deviceBody.data;await onPrompt({verificationUrl:device.verification_uri_complete,userCode:device.user_code,expiresIn:device.expires_in}),openUrl(device.verification_uri_complete);let deadline=Date.now()+device.expires_in*1000,intervalMs=Math.max(1,device.interval)*1000,accessToken=null,abortSignal=signal??new AbortController().signal;while(Date.now()<deadline){await sleepCancelable(intervalMs,abortSignal);let res=await fetch(`${issuer}/api/auth/device/token`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({grant_type:"urn:ietf:params:oauth:grant-type:device_code",device_code:device.device_code,client_id:clientId}),signal:abortSignal}),polled=DeviceTokenResponseSchema.safeParse(await res.json().catch(()=>({}))),body=polled.success?polled.data:{};if(body.access_token){accessToken=body.access_token;break}if(body.error==="authorization_pending")continue;if(body.error==="slow_down"){intervalMs+=5000;continue}throw Error(body.error_description??body.error??"device authorization failed")}if(!accessToken)throw Error("the sign-in code expired \u2014 run velloo login again");let exchangeRes=await fetch(`${cloudUrl}/v1/auth/cli-token`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({accessToken})});if(exchangeRes.status!==201){let{detail}=await readFailure(exchangeRes);throw Error(`token exchange failed (${exchangeRes.status}): ${detail}`)}let exchanged=CliTokenResponseSchema.safeParse(await exchangeRes.json().catch(()=>null));if(!exchanged.success)throw Error(`${cloudUrl} returned a sign-in token this velloo can't read`);return exchanged.data}
2
+ import{AuthConfigResponseSchema,AccountResponseSchema,DeviceCodeResponseSchema,DeviceTokenResponseSchema,CliTokenResponseSchema,readFailure}from"./chunk-n9t36h0q.js";import{openUrl}from"./chunk-hggap8fq.js";import{isSecureCloudUrl,assertSecureCloudUrl}from"./chunk-as08hzp3.js";async function fetchAccount(cloudUrl,token,timeoutMs=8000){if(!isSecureCloudUrl(cloudUrl))return{status:"unreachable"};let res=await fetch(`${cloudUrl}/v1/me`,{headers:{authorization:`Bearer ${token}`},signal:AbortSignal.timeout(timeoutMs)}).catch(()=>null);if(!res)return{status:"unreachable"};if(res.status===401||res.status===403)return{status:"rejected"};if(!res.ok)return{status:"unreachable"};let parsed=AccountResponseSchema.safeParse(await res.json().catch(()=>null));if(!parsed.success)return{status:"unreachable"};let body=parsed.data;return{status:"ok",account:{email:body.email,...body.name?{name:body.name}:{},...body.tier?{tier:body.tier}:{},...body.creditMicros!==void 0?{creditMicros:body.creditMicros}:{}}}}async function verifyCredential(cloudUrl,token,timeoutMs=8000){let found=await fetchAccount(cloudUrl,token,timeoutMs);return found.status==="ok"?found.account.email:null}var sleepCancelable=(ms,signal)=>new Promise((resolve,reject)=>{if(signal.aborted){reject(Error("cancelled"));return}let timer=setTimeout(resolve,ms);signal.addEventListener("abort",()=>{clearTimeout(timer),reject(Error("cancelled"))},{once:!0})});async function performDeviceLogin(cloudUrl,onPrompt,signal){assertSecureCloudUrl(cloudUrl);let configRes=await fetch(`${cloudUrl}/v1/auth/config`).catch(()=>null);if(!configRes?.ok)throw Error(`cannot reach ${cloudUrl} \u2014 is velloo-cloud up?`);let config=AuthConfigResponseSchema.safeParse(await configRes.json().catch(()=>null)),issuer=config.success?config.data.issuer:void 0,clientId=config.success?config.data.clientId:void 0;if(issuer===void 0||clientId===void 0)throw Error(`${cloudUrl} does not advertise a sign-in service \u2014 is velloo-cloud current?`);let codeRes=await fetch(`${issuer}/api/auth/device/code`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({client_id:clientId})}).catch(()=>null);if(!codeRes?.ok)throw Error(`the auth service at ${issuer} is not answering \u2014 is it up?`);let deviceBody=DeviceCodeResponseSchema.safeParse(await codeRes.json().catch(()=>null));if(!deviceBody.success)throw Error(`the auth service at ${issuer} answered something this velloo can't read`);let device=deviceBody.data;await onPrompt({verificationUrl:device.verification_uri_complete,userCode:device.user_code,expiresIn:device.expires_in}),openUrl(device.verification_uri_complete);let deadline=Date.now()+device.expires_in*1000,intervalMs=Math.max(1,device.interval)*1000,accessToken=null,abortSignal=signal??new AbortController().signal;while(Date.now()<deadline){await sleepCancelable(intervalMs,abortSignal);let res=await fetch(`${issuer}/api/auth/device/token`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({grant_type:"urn:ietf:params:oauth:grant-type:device_code",device_code:device.device_code,client_id:clientId}),signal:abortSignal}),polled=DeviceTokenResponseSchema.safeParse(await res.json().catch(()=>({}))),body=polled.success?polled.data:{};if(body.access_token){accessToken=body.access_token;break}if(body.error==="authorization_pending")continue;if(body.error==="slow_down"){intervalMs+=5000;continue}throw Error(body.error_description??body.error??"device authorization failed")}if(!accessToken)throw Error("the sign-in code expired \u2014 run velloo login again");let exchangeRes=await fetch(`${cloudUrl}/v1/auth/cli-token`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({accessToken})});if(exchangeRes.status!==201){let{detail}=await readFailure(exchangeRes);throw Error(`token exchange failed (${exchangeRes.status}): ${detail}`)}let exchanged=CliTokenResponseSchema.safeParse(await exchangeRes.json().catch(()=>null));if(!exchanged.success)throw Error(`${cloudUrl} returned a sign-in token this velloo can't read`);return exchanged.data}
3
3
  export{fetchAccount,verifyCredential,performDeviceLogin};
@@ -1,5 +1,5 @@
1
1
  // @bun
2
- import{isCancel,confirm,password,select}from"./chunk-80fjfyha.js";import{PRICING_URL,protectedSharesAllowed,PROTECTED_SHARES_UNAVAILABLE,describeCloudError}from"./chunk-t448rrfr.js";import"./chunk-40xgcth8.js";import{closePooledBrowser}from"./chunk-3e8vez4d.js";import{loadDesignFolder,screensForBoards,resolveProviders,findHostTailwindConfig,TailwindJit,extraThemeBlock}from"./chunk-tdsk5wxa.js";import{loadCredential}from"./chunk-80r93t2e.js";import{fail}from"./chunk-s23gtejf.js";import{confirmRenderFailures}from"./chunk-a84n72m2.js";import{publishedDesignSubtitle,listPublishedDesigns,unpublishDesign,listPublishDestinations,designGitEnv,describePublishError,recommendedPublishSlot,exactPublishSlots,gitContext,resolveTeam,publishDesign,createPublishBundler}from"./chunk-rgasy862.js";import{createProgress}from"./chunk-pjtg3wnm.js";import{DESIGN_ARG_DESCRIPTION,resolveDesign,pickBoards}from"./chunk-vsv8z729.js";import{fetchAccount}from"./chunk-bwnvcncx.js";import{defaultCloudUrl,publishedBoardsUrl,billingPageUrl,checkCloudHealth}from"./chunk-pp86v6m6.js";import{withSubcommands}from"./chunk-94vx1yb0.js";import{defineCommand}from"./chunk-2smsg2ct.js";import{join as join2}from"path";import{execFileSync}from"child_process";import{realpathSync}from"fs";import{join,relative,resolve}from"path";function plainJsonStem(path,dir){let prefix=`${dir}/`;if(!path.startsWith(prefix)||!path.endsWith(".json"))return null;let stem=path.slice(prefix.length,-5);if(stem.includes("/")||stem.includes("."))return null;return stem}function classifyChanges(changes){let set={screens:new Map,global:!1,snippets:new Set,assets:new Set};for(let{status,path}of changes){let screen=plainJsonStem(path,"screens");if(screen){set.screens.set(screen,status);continue}if(path.startsWith("theme/")||path===".design/config.json"){set.global=!0;continue}let snippet=plainJsonStem(path,"snippets");if(snippet){set.snippets.add(snippet);continue}if(path.startsWith("assets/"))set.assets.add(`/${path}`)}return set}function snippetIdsIn(value,acc){if(Array.isArray(value)){for(let v of value)snippetIdsIn(v,acc);return}if(value!==null&&typeof value==="object"){let id=value.$snippet;if(typeof id==="string")acc.add(id);for(let v of Object.values(value))snippetIdsIn(v,acc)}}function snippetsUsedByScreen(screen,snippets){let used=new Set;snippetIdsIn(screen.tree,used);let grew=!0;while(grew){grew=!1;for(let id of[...used]){let body=snippets.get(id);if(!body)continue;let inner=new Set;snippetIdsIn(body,inner);for(let innerId of inner)if(!used.has(innerId))used.add(innerId),grew=!0}}return used}function detectChangedScreens(changes,afterScreens,afterSnippets){let out=new Map;for(let[id,status]of changes.screens)out.set(id,{id,status:status==="A"?"added":status==="D"?"deleted":"modified",reasons:["screen"]});let markModified=(id,reason)=>{let existing=out.get(id);if(existing){if(!existing.reasons.includes(reason))existing.reasons.push(reason);return}out.set(id,{id,status:"modified",reasons:[reason]})};if(changes.global||changes.snippets.size>0||changes.assets.size>0)for(let[id,screen]of afterScreens){if(changes.global)markModified(id,"theme");if(changes.snippets.size===0&&changes.assets.size===0)continue;let used=snippetsUsedByScreen(screen,afterSnippets);for(let snippetId of changes.snippets)if(used.has(snippetId))markModified(id,`snippet:${snippetId}`);if(changes.assets.size>0){let haystack=JSON.stringify(screen);for(let snippetId of used){let body=afterSnippets.get(snippetId);if(body)haystack+=JSON.stringify(body)}for(let asset of changes.assets)if(haystack.includes(asset))markModified(id,`asset:${asset}`)}}return[...out.values()].sort((a,b)=>a.id.localeCompare(b.id))}function git(repo,args){return execFileSync("git",["-C",repo,...args],{encoding:"utf8",maxBuffer:67108864,stdio:["ignore","pipe","pipe"]})}function resolveGitContext(designFolder,base,head){let repoRoot;try{repoRoot=execFileSync("git",["-C",designFolder,"rev-parse","--show-toplevel"],{encoding:"utf8",stdio:["ignore","pipe","pipe"],env:designGitEnv(designFolder)}).trim()}catch{throw Error(`design folder ${designFolder} is not in a git repository, so there are no refs to compare`)}let designRel=relative(realpathSync(repoRoot),realpathSync(resolve(designFolder)));if(designRel.startsWith(".."))throw Error(`design folder ${designFolder} is outside the git repo at ${repoRoot}`);let rev=(ref)=>{try{return git(repoRoot,["rev-parse","--verify",`${ref}^{commit}`]).trim()}catch{throw Error(`cannot resolve git ref '${ref}'`)}},baseSha=rev(base),headSha=rev(head),currentSha=rev("HEAD");return{repoRoot,designRel,baseSha,headSha,headIsWorktree:headSha===currentSha}}function diffDesignFolder(ctx){let args=["diff","--name-status","--no-renames",ctx.baseSha];if(!ctx.headIsWorktree)args.push(ctx.headSha);if(ctx.designRel!=="")args.push("--",ctx.designRel);let output=git(ctx.repoRoot,args),prefix=ctx.designRel===""?"":`${ctx.designRel}/`,out=[];for(let line of output.split(`
2
+ import{isCancel,confirm,password,select}from"./chunk-80fjfyha.js";import{PRICING_URL,protectedSharesAllowed,PROTECTED_SHARES_UNAVAILABLE,describeCloudError}from"./chunk-n9t36h0q.js";import"./chunk-nte0t7v3.js";import{closePooledBrowser}from"./chunk-c4fym47a.js";import{loadDesignFolder,screensForBoards,resolveProviders,findHostTailwindConfig,TailwindJit,extraThemeBlock}from"./chunk-88eqqdk1.js";import{loadCredential}from"./chunk-hfzg98c8.js";import{fail}from"./chunk-s23gtejf.js";import{confirmRenderFailures}from"./chunk-2p37rp3j.js";import{publishedDesignSubtitle,listPublishedDesigns,unpublishDesign,listPublishDestinations,designGitEnv,describePublishError,recommendedPublishSlot,exactPublishSlots,gitContext,resolveTeam,publishDesign,createPublishBundler}from"./chunk-asbajsmd.js";import{createProgress}from"./chunk-pjtg3wnm.js";import{DESIGN_ARG_DESCRIPTION,resolveDesign,pickBoards}from"./chunk-wafb2xm9.js";import{fetchAccount}from"./chunk-6zz9kfbd.js";import{defaultCloudUrl,publishedBoardsUrl,billingPageUrl,checkCloudHealth}from"./chunk-as08hzp3.js";import{withSubcommands}from"./chunk-94vx1yb0.js";import{defineCommand}from"./chunk-2smsg2ct.js";import{join as join2}from"path";import{execFileSync}from"child_process";import{realpathSync}from"fs";import{join,relative,resolve,sep}from"path";function plainJsonStem(path,dir){let prefix=`${dir}/`;if(!path.startsWith(prefix)||!path.endsWith(".json"))return null;let stem=path.slice(prefix.length,-5);if(stem.includes("/")||stem.includes("."))return null;return stem}function classifyChanges(changes){let set={screens:new Map,global:!1,snippets:new Set,assets:new Set};for(let{status,path}of changes){let screen=plainJsonStem(path,"screens");if(screen){set.screens.set(screen,status);continue}if(path.startsWith("theme/")||path===".design/config.json"){set.global=!0;continue}let snippet=plainJsonStem(path,"snippets");if(snippet){set.snippets.add(snippet);continue}if(path.startsWith("assets/"))set.assets.add(`/${path}`)}return set}function snippetIdsIn(value,acc){if(Array.isArray(value)){for(let v of value)snippetIdsIn(v,acc);return}if(value!==null&&typeof value==="object"){let id=value.$snippet;if(typeof id==="string")acc.add(id);for(let v of Object.values(value))snippetIdsIn(v,acc)}}function snippetsUsedByScreen(screen,snippets){let used=new Set;snippetIdsIn(screen.tree,used);let grew=!0;while(grew){grew=!1;for(let id of[...used]){let body=snippets.get(id);if(!body)continue;let inner=new Set;snippetIdsIn(body,inner);for(let innerId of inner)if(!used.has(innerId))used.add(innerId),grew=!0}}return used}function detectChangedScreens(changes,afterScreens,afterSnippets){let out=new Map;for(let[id,status]of changes.screens)out.set(id,{id,status:status==="A"?"added":status==="D"?"deleted":"modified",reasons:["screen"]});let markModified=(id,reason)=>{let existing=out.get(id);if(existing){if(!existing.reasons.includes(reason))existing.reasons.push(reason);return}out.set(id,{id,status:"modified",reasons:[reason]})};if(changes.global||changes.snippets.size>0||changes.assets.size>0)for(let[id,screen]of afterScreens){if(changes.global)markModified(id,"theme");if(changes.snippets.size===0&&changes.assets.size===0)continue;let used=snippetsUsedByScreen(screen,afterSnippets);for(let snippetId of changes.snippets)if(used.has(snippetId))markModified(id,`snippet:${snippetId}`);if(changes.assets.size>0){let haystack=JSON.stringify(screen);for(let snippetId of used){let body=afterSnippets.get(snippetId);if(body)haystack+=JSON.stringify(body)}for(let asset of changes.assets)if(haystack.includes(asset))markModified(id,`asset:${asset}`)}}return[...out.values()].sort((a,b)=>a.id.localeCompare(b.id))}function git(repo,args){return execFileSync("git",["-C",repo,...args],{encoding:"utf8",maxBuffer:67108864,stdio:["ignore","pipe","pipe"]})}function resolveGitContext(designFolder,base,head){let repoRoot;try{repoRoot=execFileSync("git",["-C",designFolder,"rev-parse","--show-toplevel"],{encoding:"utf8",stdio:["ignore","pipe","pipe"],env:designGitEnv(designFolder)}).trim()}catch{throw Error(`design folder ${designFolder} is not in a git repository, so there are no refs to compare`)}let designRel=relative(realpathSync.native(repoRoot),realpathSync.native(resolve(designFolder))).split(sep).join("/");if(designRel.startsWith(".."))throw Error(`design folder ${designFolder} is outside the git repo at ${repoRoot}`);let rev=(ref)=>{try{return git(repoRoot,["rev-parse","--verify",`${ref}^{commit}`]).trim()}catch{throw Error(`cannot resolve git ref '${ref}'`)}},baseSha=rev(base),headSha=rev(head),currentSha=rev("HEAD");return{repoRoot,designRel,baseSha,headSha,headIsWorktree:headSha===currentSha}}function diffDesignFolder(ctx){let args=["diff","--name-status","--no-renames",ctx.baseSha];if(!ctx.headIsWorktree)args.push(ctx.headSha);if(ctx.designRel!=="")args.push("--",ctx.designRel);let output=git(ctx.repoRoot,args),prefix=ctx.designRel===""?"":`${ctx.designRel}/`,out=[];for(let line of output.split(`
3
3
  `)){if(line.trim()==="")continue;let tab=line.indexOf("\t");if(tab===-1)continue;let code=line.slice(0,tab).trim(),repoPath=line.slice(tab+1);if(!repoPath.startsWith(prefix))continue;let status=code==="A"?"A":code==="D"?"D":code==="M"||code==="T"?"M":null;if(!status)continue;out.push({status,path:repoPath.slice(prefix.length)})}return out}function changedBoardId(path){if(!path.startsWith("boards/")||!path.endsWith(".json"))return null;let id=path.slice(7,-5);return id.includes("/")||id.includes(".")?null:id}function selectChangedPreviews(changes,screens,snippets,boards){let availableScreens=new Map(screens.map((screen)=>[screen.id,screen])),screenIds=new Set(detectChangedScreens(classifyChanges(changes),availableScreens,snippets).filter((change)=>change.status!=="deleted"&&availableScreens.has(change.id)).map((change)=>change.id)),directlyChangedBoards=new Set(changes.map((change)=>changedBoardId(change.path)).filter((id)=>id!==null)),boardIds=new Set(boards.filter((board)=>directlyChangedBoards.has(board.id)||board.frames.some((frame)=>screenIds.has(frame.screen))).map((board)=>board.id));return{screenIds,boardIds}}function changedPreviewsSince(designFolder,baseRef,screens,snippets,boards){let git=resolveGitContext(designFolder,baseRef,"HEAD");return selectChangedPreviews(diffDesignFolder(git),screens,snippets,boards)}async function resolveCloud(command,args){let baseUrl=args.url?args.url.replace(/\/+$/,""):defaultCloudUrl(),token=args.token??process.env.VELLOO_CLOUD_TOKEN??(await loadCredential(baseUrl))?.token;if(!token)fail(command,"not logged in. Run `velloo login` first.");return{baseUrl,token}}function cloudFailure(command,error){fail(command,describeCloudError(error))}async function listPublished(args){let{baseUrl,token}=await resolveCloud("publish list",args),listed=await listPublishedDesigns({baseUrl,token});if(!listed.ok)cloudFailure("publish list",listed.error);let designs=listed.value;if(designs.length===0){console.log("velloo publish: no published designs."),console.log(` ${await publishedBoardsUrl(baseUrl)}`);return}console.log(`velloo publish: ${designs.length} design${designs.length===1?"":"s"}`);for(let design of designs){let access=design.passwordProtected?"password protected":design.visibility==="private"?"private":"public";console.log(`
4
4
  ${design.title?.trim()||"Untitled design"}`),console.log(` ${access}`),console.log(` ${publishedDesignSubtitle(design)}`),console.log(` ${design.url}`)}console.log(`
5
5
  Manage in velloo-cloud: ${await publishedBoardsUrl(baseUrl)}`)}async function removePublished(args){let{baseUrl,token}=await resolveCloud("publish remove",args),interactive=Boolean(process.stdin.isTTY);if(!interactive&&!args.shareUrl)fail("publish remove","pass the full share URL and --yes when there is no interactive terminal");if(!interactive&&args.yes!==!0)fail("publish remove","refusing to remove a published design without --yes");let listed=await listPublishedDesigns({baseUrl,token});if(!listed.ok)cloudFailure("publish remove",listed.error);let designs=listed.value,manageable=designs.filter((design)=>design.canManage);if(manageable.length===0)fail("publish remove",`there are no published designs you can remove. Manage boards at ${await publishedBoardsUrl(baseUrl)}`);let target;if(args.shareUrl){if(target=designFromReference(args.shareUrl,designs),!target)fail("publish remove",`that share URL is not in your published designs. Manage boards at ${await publishedBoardsUrl(baseUrl)}`);if(!target.canManage)fail("publish remove","you do not have permission to remove that design")}else{let value=await select({message:"Design to unpublish",options:manageable.map((design)=>({value:design.slug,label:design.title?.trim()||"Untitled design",hint:publishedDesignSubtitle(design)}))});if(target=resolveUnpublishSelection(isCancel(value)?null:String(value),manageable),!target)fail("publish remove","cancelled")}if(args.yes!==!0){let approved=await confirm({message:`Unpublish \u201C${target.title?.trim()||"Untitled design"}\u201D? Its share URL will stop working.`,initialValue:!1});if(isCancel(approved)||!approved)fail("publish remove","cancelled")}let removed=await unpublishDesign({baseUrl,token,slug:target.slug});if(!removed.ok)cloudFailure("publish remove",removed.error);console.log(`velloo publish: removed ${target.title?.trim()||"Untitled design"}`),console.log(` ${target.url}`)}function resolveUnpublishSelection(value,designs){return value===null?null:designs.find((design)=>design.slug===value)??null}function designFromReference(reference,designs){if(!/^https?:\/\//i.test(reference.trim()))return null;let normalize=(raw)=>{try{let url=new URL(raw.trim());return url.search="",url.hash="",url.toString().replace(/\/+$/,"")}catch{return null}},normalized=normalize(reference);if(normalized===null)return null;return designs.find((design)=>normalize(design.url)===normalized)??null}function privacyFlagsError(args,protectedShares=!0,upgradeUrl=PRICING_URL){if(args.visibility!==void 0&&args.visibility!=="public"&&args.visibility!=="private")return`--visibility must be 'public' or 'private', got '${args.visibility}'`;if(args.private&&args.public)return"--private and --public cannot be used together";if(args.private&&args.visibility==="public")return"--private conflicts with --visibility public";if(args.public&&args.visibility==="private")return"--public conflicts with --visibility private";if((args.private===!0||args.visibility==="private"||args.password===!0)&&!protectedShares)return`${PROTECTED_SHARES_UNAVAILABLE}. Publish with --public, or upgrade at ${upgradeUrl}`;return null}async function resolvePublishPrivacy(args,interactive,{protectedShares=!0,upgradeUrl=PRICING_URL,choose=promptForPrivacyMode}={}){let invalid=privacyFlagsError(args,protectedShares,upgradeUrl);if(invalid)throw Error(invalid);if(args.visibility!==void 0||args.private===!0||args.public===!0||args.password===!0)return{visibility:args.private||args.visibility==="private"?"private":"public",password:args.password===!0};if(!interactive)throw Error("privacy mode is required without a terminal; pass --public, --private, --password, or --visibility");let mode=await choose(protectedShares,upgradeUrl);if(typeof mode==="symbol")throw Error("cancelled");if(mode!=="public"&&!protectedShares)throw Error(PROTECTED_SHARES_UNAVAILABLE);return mode==="private"?{visibility:"private",password:!1}:{visibility:"public",password:mode==="password"}}async function promptForPrivacyMode(protectedShares,upgradeUrl){let locked=protectedShares?{}:{disabled:!0,hint:"Team & Business plans"};return select({message:protectedShares?"Who should be able to view this publish?":`Who should be able to view this publish? (free plan: public links \u2014 upgrade at ${upgradeUrl})`,initialValue:"public",options:[{value:"public",label:"Public",hint:"anyone with the link"},{value:"private",label:"Private",hint:"only your organization",...locked},{value:"password",label:"Password protected",hint:"anyone with the password",...locked}]})}var CLOUD_ARGS={url:{type:"string",description:"velloo-cloud base URL (default: $VELLOO_CLOUD_URL or the built-in default)"},token:{type:"string",description:"velloo-cloud access token (default: $VELLOO_CLOUD_TOKEN)"}},list=defineCommand({meta:{name:"list",description:"List the designs you've published"},args:CLOUD_ARGS,run:({args})=>listPublished(args)}),remove=defineCommand({meta:{name:"remove",description:"Take a published design down"},args:{"share-url":{type:"positional",required:!1,description:"The share URL to take down (default: pick one interactively)"},yes:{type:"boolean",description:"Skip the confirmation (required without an interactive terminal)"},...CLOUD_ARGS},run:({args})=>removePublished({...args,shareUrl:args["share-url"]})}),publish=defineCommand({meta:{name:"publish",description:"Publish a design as a velloo-cloud share link"},args:{design:{type:"positional",required:!1,description:DESIGN_ARG_DESCRIPTION},yes:{type:"boolean",description:"Publish even when a screen's component fails to render (required without an interactive terminal)"},...CLOUD_ARGS,slug:{type:"string",description:"Custom slug for --new (default: server-generated)"},new:{type:"boolean",description:"Create a separate publish link instead of updating an existing slot"},update:{type:"boolean",description:"Update the exact matching published design"},title:{type:"string",description:"Link title (default: the design's name)"},boards:{type:"string",description:"Comma-separated board ids to publish (default: pick interactively; all when non-interactive)"},visibility:{type:"string",description:"public | private \u2014 explicit alternative to the interactive privacy prompt"},public:{type:"boolean",description:"Publish for anyone with the link; skips the privacy prompt"},private:{type:"boolean",description:"Publish for your organization only; skips the privacy prompt"},password:{type:"boolean",description:"Password-protect the link; anyone with the password can view. Prompts for it, or reads $VELLOO_SHARE_PASSWORD"},"password-expires":{type:"string",description:"Stop accepting the password after this date (YYYY-MM-DD or ISO timestamp)"},team:{type:"string",description:"Publish into a team by name or UUID (default: personal workspace)"},w:{type:"string",description:"Viewport width in px (default: 1440)"},h:{type:"string",description:"Viewport height in px (default: 900)"},"changed-since":{type:"string",description:"Capture only screen/board previews affected since this git ref (the full design still publishes)"}},async run({args}){let folder=await resolveDesign(args.design,"publish"),baseUrl=args.url?args.url.replace(/\/+$/,""):defaultCloudUrl(),token=args.token??process.env.VELLOO_CLOUD_TOKEN??(await loadCredential(baseUrl))?.token;if(!token)fail("publish","not logged in. Run `velloo login` (or pass --token / VELLOO_CLOUD_TOKEN).");let interactive=Boolean(process.stdin.isTTY),team=await resolveTeam(baseUrl,token,args.team);if(!team.ok)fail("publish",describePublishError(team.error));let teamId=team.value,viewport={w:args.w?Number(args.w):1440,h:args.h?Number(args.h):900},design=await loadDesignFolder(folder),config=design.config,health=await checkCloudHealth(baseUrl);if(health.status==="unreachable")fail("publish",`cannot reach ${baseUrl} (${health.reason}). Is velloo-cloud up? (bun cloud:up)`);if(health.status==="unhealthy")fail("publish",health.detail);let account=await fetchAccount(baseUrl,token,5000),protectedShares=protectedSharesAllowed(account.status==="ok"?account.account.tier:void 0),upgradeUrl=protectedShares?void 0:await billingPageUrl(baseUrl),flagsError=privacyFlagsError(args,protectedShares,upgradeUrl);if(flagsError)fail("publish",flagsError);let selected=await pickBoards(folder,args.boards,interactive,"publish");if(selected===null){console.log("velloo publish: no boards selected \u2014 nothing was published.");return}let provenance=gitContext(folder),selectedBoardIds=new Set(selected.map((board)=>board.id)),selectedBoards=[...design.boards.values()].filter((board)=>selectedBoardIds.has(board.id)),screenshotSelection=args["changed-since"]?(()=>{try{return changedPreviewsSince(folder,args["changed-since"],[...design.screens.values()],design.snippets,selectedBoards)}catch(error){fail("publish",`cannot determine previews changed since '${args["changed-since"]}': ${error instanceof Error?error.message:String(error)}`)}})():void 0,listed=await(async()=>{if(!config.folderId||args.new===!0)return{effectiveTeamId:teamId??null,slots:[]};let destinations=await listPublishDestinations({baseUrl,token,folderId:config.folderId,...teamId?{teamId}:{}});if(!destinations.ok)fail("publish",describePublishError(destinations.error));return destinations.value})(),source={boardIds:selected.map((board)=>board.id),teamId:listed.effectiveTeamId,...provenance},destination=await choosePublishDestination({slots:listed.slots,source,interactive,createNew:args.new===!0,updateExisting:args.update===!0,customSlug:args.slug,manageUrl:await publishedBoardsUrl(baseUrl)}),privacy=await resolvePublishPrivacy(args,interactive,{protectedShares,...upgradeUrl?{upgradeUrl}:{}}).catch((error)=>fail("publish",error instanceof Error?error.message:String(error))),visibility=privacy.visibility,password=privacy.password?await readSharePassword():void 0,passwordExpiresAt=resolvePasswordExpiry(args["password-expires"],password!=null),{providers,defaultProvider}=await resolveProviders(config,folder);await confirmRenderFailures("publish",{folder:design,providers,defaultProvider},source.boardIds.length>0?screensForBoards(design,source.boardIds):[...design.screens.values()],args.yes===!0);let bundler=createPublishBundler(folder,config),jit=new TailwindJit(Object.values(providers),join2(folder,"screens"),void 0,()=>extraThemeBlock(design),()=>bundler.hostSourceDirs(),()=>findHostTailwindConfig(folder,config.hostApp),config.styling?.framework),progress=createProgress(),report=createPublishReporter(progress),outcome;try{let published=await publishDesign({baseUrl,token},{folder:design,providers,defaultProvider,snapshotCss:()=>jit.build(),liveBundler:bundler},{boardIds:selected.map((b)=>b.id),...args.title?{title:args.title}:{},visibility,...password?{password}:{},...passwordExpiresAt?{passwordExpiresAt}:{},destination,...teamId?{teamId}:{},provenance,viewport,...screenshotSelection?{screenshotSelection}:{}},report);if(!published.ok)progress.fail("publish failed"),fail("publish",describePublishError(published.error));outcome=published.value,progress.succeed("published design")}catch(error){progress.fail("publish failed"),fail("publish",error instanceof Error?error.message:String(error))}finally{await closePooledBrowser()}console.log(`velloo publish: ${outcome.files} files, ${Math.round(outcome.bytes/1024)} KB${outcome.screenshots>0?`, ${outcome.screenshots} screenshots`:""}${outcome.commitSha?`, commit ${outcome.commitSha.slice(0,7)}`:""}`),console.log(` ${outcome.shareUrl}`),console.log(` ${describeAccess(outcome)}`);let history=outcome.history;if(history&&!history.retained&&history.pruned>0)console.log(" replaced the previous version \u2014 the free plan keeps only the latest."),console.log(` Upgrade to Team to keep version history and revisit past publishes: ${PRICING_URL}`);else if(history?.retained&&history.versions>1)console.log(` history: ${history.versions} versions kept \u2014 pin any of them from ${outcome.shareUrl}v/<version>/`)}}),publish_default=withSubcommands(publish,{list,remove});function resolvePublishDestinationChoice(value,slots){if(value===null)return null;if(value==="new")return{mode:"new"};let slug=value.replace(/^update:/,""),slot=slots.find((candidate)=>candidate.slug===slug);if(!slot)return null;return{mode:"update",slug:slot.slug,expectedVersionId:slot.latestVersionId}}async function choosePublishDestination(opts){if(opts.createNew&&opts.updateExisting)fail("publish","choose either --new or --update, not both");if(opts.customSlug&&!opts.createNew)fail("publish","--slug names a new link; use it together with --new");let matching=exactPublishSlots(opts.slots,opts.source);if(opts.updateExisting){let slot=matching[0];if(!slot)fail("publish",`there is no exact published-design match to update. Manage existing boards at ${opts.manageUrl}`);return{mode:"update",slug:slot.slug,expectedVersionId:slot.latestVersionId}}if(opts.createNew)return{mode:"new",...opts.customSlug?{slug:opts.customSlug}:{}};if(matching.length===0)return(opts.log??console.log)("velloo publish: no matching published design \u2014 a new link will be created."),{mode:"new"};if(!opts.interactive)fail("publish","this folder already has published designs; pass --update or --new explicitly");let recommended=recommendedPublishSlot(opts.slots,opts.source),latestMatch=matching[0],value=await select({message:"Publish destination",initialValue:recommended?`update:${recommended.slug}`:"new",options:[...latestMatch?[{value:`update:${latestMatch.slug}`,label:`Update ${latestMatch.title||"Untitled design"}`,hint:"latest exact match for this team, board selection, and source"}]:[],{value:"new",label:"Create a new link",hint:recommended?"keep the matching design separate":"recommended for this source"}]}),destination=resolvePublishDestinationChoice(isCancel(value)?null:String(value),matching);if(!destination)fail("publish",isCancel(value)?"cancelled":"selected publish slot is no longer available");return destination}function createPublishReporter(progress){let latestCapture;return(event)=>{if(event.kind==="capture"){latestCapture=`capturing previews ${event.done}/${event.total}`,progress.step(latestCapture,{transient:!0});return}if(event.kind==="step"){if(latestCapture)progress.step(latestCapture);latestCapture=void 0,progress.step(event.message);return}progress.log(event.message)}}async function readSharePassword(){let fromEnv=process.env.VELLOO_SHARE_PASSWORD?.trim();if(fromEnv){if(fromEnv.length<3)fail("publish","VELLOO_SHARE_PASSWORD must be at least 3 characters");return fromEnv}if(!process.stdin.isTTY)fail("publish","--password needs a terminal to prompt \u2014 set $VELLOO_SHARE_PASSWORD instead");let entered=await password({message:"Password for this share link",validate:(value)=>(value??"").length<3?"At least 3 characters.":void 0});if(isCancel(entered))fail("publish","cancelled");return entered}function resolvePasswordExpiry(raw,hasPassword){if(!raw)return;if(!hasPassword)fail("publish","--password-expires needs --password");let value=/^\d{4}-\d{2}-\d{2}$/.test(raw)?`${raw}T23:59:59`:raw,when=new Date(value);if(Number.isNaN(when.getTime()))fail("publish",`--password-expires must be a date (YYYY-MM-DD) or ISO timestamp, got '${raw}'`);return when.toISOString()}function describeAccess(outcome){if(outcome.visibility==="private")return outcome.passwordProtected?"private \u2014 your organization, or anyone with the password":"private \u2014 anyone signed in at your organization";return outcome.passwordProtected?"public \u2014 anyone with the link and the password":"public \u2014 anyone with the link"}export{publish_default as default};
@@ -1,2 +1,2 @@
1
1
  // @bun
2
- import"./chunk-40xgcth8.js";import{signInRequired,boardLimitReached,topUpTokens,createServer}from"./chunk-tdsk5wxa.js";import{boardLimitFrom,describeCloudError}from"./chunk-t448rrfr.js";import"./chunk-3e8vez4d.js";import{loadCredential,saveCredential,deleteCredential}from"./chunk-80r93t2e.js";import{listPublishedDesigns,unpublishDesign,listPublishDestinations,describePublishError,PUBLISH_VIEWPORT,gitContext,listTeams,publishDesign}from"./chunk-rgasy862.js";import{TOOL_VERSION}from"./chunk-dg6z4vdd.js";import{updateStatus,upgradeInstalledVelloo}from"./chunk-xt5ayg7g.js";import{assertLoopbackHost,daemonRoot,writeLock,removeLock,readRememberedPort,rememberPort,portCandidates,registryUpsert,registryRemove}from"./chunk-sjgqnykd.js";import{fetchAccount,performDeviceLogin}from"./chunk-bwnvcncx.js";import{defaultCloudUrl,fetchCloudAppUrl}from"./chunk-pp86v6m6.js";import{defineCommand}from"./chunk-2smsg2ct.js";import{existsSync as existsSync2}from"fs";import{existsSync}from"fs";import{dirname,join}from"path";import{fileURLToPath}from"url";var here=dirname(fileURLToPath(import.meta.url));function resolveCanvasDist(){let devLayout=join(here,"..","dist");return[process.env.VELLOO_CANVAS_DIST,join(here,"canvas"),devLayout].filter((p)=>Boolean(p)).find((p)=>existsSync(join(p,"index.html")))??devLayout}var canvasDistPath=resolveCanvasDist();var ACCOUNT_TTL_MS=60000,ACCOUNT_TIMEOUT_MS=5000;function createCanvasAuth(cloudUrl,dependencies={}){let runDeviceLogin=dependencies.performDeviceLogin??performDeviceLogin,login={state:"idle"},pending=null,cached=null,cachedApp=null,describe=async(token,email)=>{if(cached&&cached.token===token&&Date.now()-cached.at<ACCOUNT_TTL_MS)return{account:cached.account,verified:cached.verified};let found=await fetchAccount(cloudUrl,token,ACCOUNT_TIMEOUT_MS),resolved=found.status==="ok"?{account:found.account,verified:!0}:{account:{email},verified:found.status==="rejected"?!1:null};return cached={token,at:Date.now(),...resolved},resolved},resolveAppUrl=async()=>{if(cachedApp&&Date.now()-cachedApp.at<ACCOUNT_TTL_MS)return cachedApp.url;let url=await fetchCloudAppUrl(cloudUrl,ACCOUNT_TIMEOUT_MS);return cachedApp={at:Date.now(),url},url},status=async()=>{let cred=await loadCredential(cloudUrl),appUrlP=resolveAppUrl();if(!cred?.token)return{loggedIn:!1,cloudUrl,appUrl:await appUrlP,verified:null,login};let[{account,verified},appUrl]=await Promise.all([describe(cred.token,cred.email),appUrlP]);return{loggedIn:!0,cloudUrl,appUrl,verified,login,...account?{account}:{}}};return{status,async beginLogin(){if(login.state==="pending")return status();let attempt=new AbortController;pending=attempt,login={state:"idle"};let settle=()=>{},ready=new Promise((resolve)=>{settle=resolve});return runDeviceLogin(cloudUrl,({verificationUrl,userCode,expiresIn})=>{login={state:"pending",userCode,verificationUrl,expiresAt:new Date(Date.now()+expiresIn*1000).toISOString()},settle()},attempt.signal).then(async(result)=>{await saveCredential(cloudUrl,result),cached=null,login={state:"idle"},await topUpTokens({url:cloudUrl,token:result.token},10).catch(()=>{return})}).catch((err)=>{login=attempt.signal.aborted?{state:"idle"}:{state:"error",message:err instanceof Error?err.message:String(err)}}).finally(()=>{if(pending===attempt)pending=null;settle()}),await ready,status()},async cancelLogin(){pending?.abort(),pending=null,login={state:"idle"}},async logout(){pending?.abort(),pending=null,login={state:"idle"},cached=null,await deleteCredential(cloudUrl)}}}function createCanvasPublish(cloudUrl,auth){let tokenFor=async()=>(await loadCredential(cloudUrl))?.token,access=async()=>{if(!await tokenFor())return{state:"signed-out"};return(await auth.status().catch(()=>null))?.verified===!1?{state:"expired"}:{state:"ready"}},rejection=(error)=>error.kind==="LoggedOut"?signInRequired("expired",describeCloudError(error)):Error(describeCloudError(error)),publishedAt=(value)=>{let parsed=value?Date.parse(value):Number.NaN;return Number.isFinite(parsed)?parsed:Number.NEGATIVE_INFINITY};return{access,async teams(){let token=await tokenFor();if(!token)return[];let listed=await listTeams(cloudUrl,token);return listed.ok?listed.value:[]},async published(){let token=await tokenFor();if(!token)throw signInRequired("signed-out","not signed in to velloo-cloud");let listed=await listPublishedDesigns({baseUrl:cloudUrl,token});if(!listed.ok)throw rejection(listed.error);return listed.value.map((design)=>({slug:design.slug,title:design.title??"",url:design.url,visibility:design.visibility,passwordProtected:design.passwordProtected,canManage:design.canManage,lastPublishedAt:design.lastPublishedAt})).sort((left,right)=>publishedAt(right.lastPublishedAt)-publishedAt(left.lastPublishedAt))},async unpublish(slug){let token=await tokenFor();if(!token)throw signInRequired("signed-out","not signed in to velloo-cloud");let removed=await unpublishDesign({baseUrl:cloudUrl,token,slug});if(!removed.ok)throw rejection(removed.error)},async destinations(host){let token=await tokenFor();if(!token)throw signInRequired("signed-out","not signed in to velloo-cloud");let provenance=gitContext(host.folder.root),folderId=host.folder.config.folderId;if(!folderId){let teams=await listTeams(cloudUrl,token);if(!teams.ok)throw rejection(teams.error);return{effectiveTeamId:teams.value.find((team)=>team.isDefault)?.id??null,provenance,slots:[]}}let listed=await listPublishDestinations({baseUrl:cloudUrl,token,folderId});if(!listed.ok)throw rejection(listed.error);return{...listed.value,provenance}},async run(host,request,onProgress,onWarning){let token=await tokenFor();if(!token)throw signInRequired("signed-out","not signed in to velloo-cloud \u2014 sign in from the account menu first.");let step="start",message="starting",published=await publishDesign({baseUrl:cloudUrl,token},{folder:host.folder,providers:host.providers,defaultProvider:host.defaultProvider,snapshotCss:host.snapshotCss},{boardIds:request.boardIds,...request.title?{title:request.title}:{},visibility:request.visibility,...request.password?{password:request.password}:{},destination:request.destination,...request.teamId?{teamId:request.teamId}:{},provenance:gitContext(host.folder.root),viewport:PUBLISH_VIEWPORT},(event)=>{if(event.kind==="warn"){onWarning(event.message);return}if(event.kind==="note"){onWarning(event.message);return}if(event.kind==="info")return;if(event.kind==="capture"){onProgress({step,message,capture:{done:event.done,total:event.total}});return}step=event.step,message=event.message,onProgress({step,message})});if(!published.ok){let reason=describePublishError(published.error);if(published.error.kind==="LoggedOut")throw signInRequired("expired",reason);let limit=published.error.kind==="HttpFailure"?boardLimitFrom(published.error):null;if(limit)throw boardLimitReached(limit,reason);throw Error(reason)}let outcome=published.value;return{shareUrl:outcome.shareUrl,visibility:outcome.visibility,passwordProtected:outcome.passwordProtected,files:outcome.files,bytes:outcome.bytes,screenshots:outcome.screenshots,boards:outcome.boards,screens:outcome.screens,created:outcome.created,...outcome.tier!==void 0?{tier:outcome.tier}:{},...outcome.history!==void 0?{history:outcome.history}:{}}}}}function createCanvasUpdates(){return{async status(opts={}){let status=await updateStatus(opts);return{current:status.current,latest:status.latest,available:status.available,method:status.method,channel:status.channel,upgradable:status.upgradable,...status.reason===void 0?{}:{reason:status.reason}}},async upgrade(){let outcome=await upgradeInstalledVelloo();return{upgraded:outcome.upgraded,from:outcome.from,to:outcome.to,restartRequired:outcome.upgraded}}}}var IDLE_MS=300000;function portFree(port,host){try{return Bun.serve({port,hostname:host,fetch:()=>new Response("")}).stop(!0),!0}catch{return!1}}var daemon_default=defineCommand({meta:{name:"__daemon",hidden:!0,description:"(internal) Run the persistent canvas daemon. Spawned by `velloo run` / `velloo mcp`."},args:{folder:{type:"positional",required:!0,description:"Design folder (absolute)"},port:{type:"string",description:"Preferred canvas port"},host:{type:"string",description:"Bind hostname (default 127.0.0.1)"}},async run({args}){let root=daemonRoot(args.folder),host=args.host??"127.0.0.1";assertLoopbackHost(host);let cloudUrl=defaultCloudUrl(),cred=await loadCredential(cloudUrl),cloud={url:cloudUrl,token:cred?.token,resolveToken:async()=>(await loadCredential(cloudUrl))?.token},auth=createCanvasAuth(cloudUrl),publish=createCanvasPublish(cloudUrl,auth),updates=createCanvasUpdates(),canvasPort=portCandidates({explicit:args.port?Number(args.port):void 0,remembered:await readRememberedPort(root),root}).find((port)=>portFree(port,host))??0,handle;try{handle=await createServer({folder:root,port:canvasPort,host,mcp:{transport:"http",port:0},canvasDist:canvasDistPath,cloud,auth,publish,updates})}catch(err){if(canvasPort===0)throw err;handle=await createServer({folder:root,port:0,host,mcp:{transport:"http",port:0},canvasDist:canvasDistPath,cloud,auth,publish,updates})}let rec={root,pid:process.pid,canvasUrl:handle.url,canvasPort:handle.port,mcpUrl:handle.mcpUrl??"",mcpPort:handle.mcpPort??0,cloudUrl,version:TOOL_VERSION,startedAt:new Date().toISOString()};await writeLock(rec),await registryUpsert(rec),await rememberPort(root,handle.port).catch(()=>{return}),console.error(`velloo: canvas daemon up at ${handle.url} (mcp ${handle.mcpUrl}), pid ${process.pid}`);let stopped=!1,shutdown=async()=>{if(stopped)return;stopped=!0,removeLock(root),await registryRemove(root).catch(()=>{return}),await handle.close(),process.exit(0)};process.on("SIGTERM",shutdown),process.on("SIGINT",shutdown);let rootMissing=0;setInterval(()=>{if(existsSync2(root)){rootMissing=0;return}if(rootMissing+=1,rootMissing>=2)console.error(`velloo: design folder ${root} is gone \u2014 shutting down.`),shutdown()},1e4);let idleSince=Date.now();setInterval(()=>{let{canvas,mcp}=handle.connections();if(canvas+mcp>0){idleSince=null;return}if(idleSince===null){idleSince=Date.now();return}if(Date.now()-idleSince>=IDLE_MS)console.error("velloo: canvas idle for 5 min \u2014 shutting down."),shutdown()},30000),await new Promise(()=>{})}});export{daemon_default as default};
2
+ import"./chunk-nte0t7v3.js";import{signInRequired,boardLimitReached,topUpTokens,createServer}from"./chunk-88eqqdk1.js";import{boardLimitFrom,describeCloudError}from"./chunk-n9t36h0q.js";import"./chunk-c4fym47a.js";import{loadCredential,saveCredential,deleteCredential}from"./chunk-hfzg98c8.js";import{listPublishedDesigns,unpublishDesign,listPublishDestinations,describePublishError,PUBLISH_VIEWPORT,gitContext,listTeams,publishDesign}from"./chunk-asbajsmd.js";import{TOOL_VERSION}from"./chunk-9psr8atb.js";import{updateStatus,upgradeInstalledVelloo}from"./chunk-9p1dk5cb.js";import{assertLoopbackHost,daemonRoot,writeLock,removeLock,readRememberedPort,rememberPort,portCandidates,registryUpsert,registryRemove}from"./chunk-4ma2wnqw.js";import{fetchAccount,performDeviceLogin}from"./chunk-6zz9kfbd.js";import{defaultCloudUrl,fetchCloudAppUrl}from"./chunk-as08hzp3.js";import{defineCommand}from"./chunk-2smsg2ct.js";import{existsSync as existsSync2}from"fs";import{existsSync}from"fs";import{dirname,join}from"path";import{fileURLToPath}from"url";var here=dirname(fileURLToPath(import.meta.url));function resolveCanvasDist(){let devLayout=join(here,"..","dist");return[process.env.VELLOO_CANVAS_DIST,join(here,"canvas"),devLayout].filter((p)=>Boolean(p)).find((p)=>existsSync(join(p,"index.html")))??devLayout}var canvasDistPath=resolveCanvasDist();var ACCOUNT_TTL_MS=60000,ACCOUNT_TIMEOUT_MS=5000;function createCanvasAuth(cloudUrl,dependencies={}){let runDeviceLogin=dependencies.performDeviceLogin??performDeviceLogin,login={state:"idle"},pending=null,cached=null,cachedApp=null,describe=async(token,email)=>{if(cached&&cached.token===token&&Date.now()-cached.at<ACCOUNT_TTL_MS)return{account:cached.account,verified:cached.verified};let found=await fetchAccount(cloudUrl,token,ACCOUNT_TIMEOUT_MS),resolved=found.status==="ok"?{account:found.account,verified:!0}:{account:{email},verified:found.status==="rejected"?!1:null};return cached={token,at:Date.now(),...resolved},resolved},resolveAppUrl=async()=>{if(cachedApp&&Date.now()-cachedApp.at<ACCOUNT_TTL_MS)return cachedApp.url;let url=await fetchCloudAppUrl(cloudUrl,ACCOUNT_TIMEOUT_MS);return cachedApp={at:Date.now(),url},url},status=async()=>{let cred=await loadCredential(cloudUrl),appUrlP=resolveAppUrl();if(!cred?.token)return{loggedIn:!1,cloudUrl,appUrl:await appUrlP,verified:null,login};let[{account,verified},appUrl]=await Promise.all([describe(cred.token,cred.email),appUrlP]);return{loggedIn:!0,cloudUrl,appUrl,verified,login,...account?{account}:{}}};return{status,async beginLogin(){if(login.state==="pending")return status();let attempt=new AbortController;pending=attempt,login={state:"idle"};let settle=()=>{},ready=new Promise((resolve)=>{settle=resolve});return runDeviceLogin(cloudUrl,({verificationUrl,userCode,expiresIn})=>{login={state:"pending",userCode,verificationUrl,expiresAt:new Date(Date.now()+expiresIn*1000).toISOString()},settle()},attempt.signal).then(async(result)=>{await saveCredential(cloudUrl,result),cached=null,login={state:"idle"},await topUpTokens({url:cloudUrl,token:result.token},10).catch(()=>{return})}).catch((err)=>{login=attempt.signal.aborted?{state:"idle"}:{state:"error",message:err instanceof Error?err.message:String(err)}}).finally(()=>{if(pending===attempt)pending=null;settle()}),await ready,status()},async cancelLogin(){pending?.abort(),pending=null,login={state:"idle"}},async logout(){pending?.abort(),pending=null,login={state:"idle"},cached=null,await deleteCredential(cloudUrl)}}}function createCanvasPublish(cloudUrl,auth){let tokenFor=async()=>(await loadCredential(cloudUrl))?.token,access=async()=>{if(!await tokenFor())return{state:"signed-out"};return(await auth.status().catch(()=>null))?.verified===!1?{state:"expired"}:{state:"ready"}},rejection=(error)=>error.kind==="LoggedOut"?signInRequired("expired",describeCloudError(error)):Error(describeCloudError(error)),publishedAt=(value)=>{let parsed=value?Date.parse(value):Number.NaN;return Number.isFinite(parsed)?parsed:Number.NEGATIVE_INFINITY};return{access,async teams(){let token=await tokenFor();if(!token)return[];let listed=await listTeams(cloudUrl,token);return listed.ok?listed.value:[]},async published(){let token=await tokenFor();if(!token)throw signInRequired("signed-out","not signed in to velloo-cloud");let listed=await listPublishedDesigns({baseUrl:cloudUrl,token});if(!listed.ok)throw rejection(listed.error);return listed.value.map((design)=>({slug:design.slug,title:design.title??"",url:design.url,visibility:design.visibility,passwordProtected:design.passwordProtected,canManage:design.canManage,lastPublishedAt:design.lastPublishedAt})).sort((left,right)=>publishedAt(right.lastPublishedAt)-publishedAt(left.lastPublishedAt))},async unpublish(slug){let token=await tokenFor();if(!token)throw signInRequired("signed-out","not signed in to velloo-cloud");let removed=await unpublishDesign({baseUrl:cloudUrl,token,slug});if(!removed.ok)throw rejection(removed.error)},async destinations(host){let token=await tokenFor();if(!token)throw signInRequired("signed-out","not signed in to velloo-cloud");let provenance=gitContext(host.folder.root),folderId=host.folder.config.folderId;if(!folderId){let teams=await listTeams(cloudUrl,token);if(!teams.ok)throw rejection(teams.error);return{effectiveTeamId:teams.value.find((team)=>team.isDefault)?.id??null,provenance,slots:[]}}let listed=await listPublishDestinations({baseUrl:cloudUrl,token,folderId});if(!listed.ok)throw rejection(listed.error);return{...listed.value,provenance}},async run(host,request,onProgress,onWarning){let token=await tokenFor();if(!token)throw signInRequired("signed-out","not signed in to velloo-cloud \u2014 sign in from the account menu first.");let step="start",message="starting",published=await publishDesign({baseUrl:cloudUrl,token},{folder:host.folder,providers:host.providers,defaultProvider:host.defaultProvider,snapshotCss:host.snapshotCss},{boardIds:request.boardIds,...request.title?{title:request.title}:{},visibility:request.visibility,...request.password?{password:request.password}:{},destination:request.destination,...request.teamId?{teamId:request.teamId}:{},provenance:gitContext(host.folder.root),viewport:PUBLISH_VIEWPORT},(event)=>{if(event.kind==="warn"){onWarning(event.message);return}if(event.kind==="note"){onWarning(event.message);return}if(event.kind==="info")return;if(event.kind==="capture"){onProgress({step,message,capture:{done:event.done,total:event.total}});return}step=event.step,message=event.message,onProgress({step,message})});if(!published.ok){let reason=describePublishError(published.error);if(published.error.kind==="LoggedOut")throw signInRequired("expired",reason);let limit=published.error.kind==="HttpFailure"?boardLimitFrom(published.error):null;if(limit)throw boardLimitReached(limit,reason);throw Error(reason)}let outcome=published.value;return{shareUrl:outcome.shareUrl,visibility:outcome.visibility,passwordProtected:outcome.passwordProtected,files:outcome.files,bytes:outcome.bytes,screenshots:outcome.screenshots,boards:outcome.boards,screens:outcome.screens,created:outcome.created,...outcome.tier!==void 0?{tier:outcome.tier}:{},...outcome.history!==void 0?{history:outcome.history}:{}}}}}function createCanvasUpdates(){return{async status(opts={}){let status=await updateStatus(opts);return{current:status.current,latest:status.latest,available:status.available,method:status.method,channel:status.channel,upgradable:status.upgradable,...status.reason===void 0?{}:{reason:status.reason}}},async upgrade(){let outcome=await upgradeInstalledVelloo();return{upgraded:outcome.upgraded,from:outcome.from,to:outcome.to,restartRequired:outcome.upgraded}}}}var IDLE_MS=300000;function portFree(port,host){try{return Bun.serve({port,hostname:host,fetch:()=>new Response("")}).stop(!0),!0}catch{return!1}}var daemon_default=defineCommand({meta:{name:"__daemon",hidden:!0,description:"(internal) Run the persistent canvas daemon. Spawned by `velloo run` / `velloo mcp`."},args:{folder:{type:"positional",required:!0,description:"Design folder (absolute)"},port:{type:"string",description:"Preferred canvas port"},host:{type:"string",description:"Bind hostname (default 127.0.0.1)"}},async run({args}){let root=daemonRoot(args.folder),host=args.host??"127.0.0.1";assertLoopbackHost(host);let cloudUrl=defaultCloudUrl(),cred=await loadCredential(cloudUrl),cloud={url:cloudUrl,token:cred?.token,resolveToken:async()=>(await loadCredential(cloudUrl))?.token},auth=createCanvasAuth(cloudUrl),publish=createCanvasPublish(cloudUrl,auth),updates=createCanvasUpdates(),canvasPort=portCandidates({explicit:args.port?Number(args.port):void 0,remembered:await readRememberedPort(root),root}).find((port)=>portFree(port,host))??0,handle;try{handle=await createServer({folder:root,port:canvasPort,host,mcp:{transport:"http",port:0},canvasDist:canvasDistPath,cloud,auth,publish,updates})}catch(err){if(canvasPort===0)throw err;handle=await createServer({folder:root,port:0,host,mcp:{transport:"http",port:0},canvasDist:canvasDistPath,cloud,auth,publish,updates})}let rec={root,pid:process.pid,canvasUrl:handle.url,canvasPort:handle.port,mcpUrl:handle.mcpUrl??"",mcpPort:handle.mcpPort??0,cloudUrl,version:TOOL_VERSION,startedAt:new Date().toISOString()};await writeLock(rec),await registryUpsert(rec),await rememberPort(root,handle.port).catch(()=>{return}),console.error(`velloo: canvas daemon up at ${handle.url} (mcp ${handle.mcpUrl}), pid ${process.pid}`);let stopped=!1,shutdown=async()=>{if(stopped)return;stopped=!0,removeLock(root),await registryRemove(root).catch(()=>{return}),await handle.close(),process.exit(0)};process.on("SIGTERM",shutdown),process.on("SIGINT",shutdown);let rootMissing=0;setInterval(()=>{if(existsSync2(root)){rootMissing=0;return}if(rootMissing+=1,rootMissing>=2)console.error(`velloo: design folder ${root} is gone \u2014 shutting down.`),shutdown()},1e4);let idleSince=Date.now();setInterval(()=>{let{canvas,mcp}=handle.connections();if(canvas+mcp>0){idleSince=null;return}if(idleSince===null){idleSince=Date.now();return}if(Date.now()-idleSince>=IDLE_MS)console.error("velloo: canvas idle for 5 min \u2014 shutting down."),shutdown()},30000),await new Promise(()=>{})}});export{daemon_default as default};
@@ -0,0 +1,2 @@
1
+ // @bun
2
+ import{upgrade_default,migrateFolder2}from"./chunk-mfbxfb9b.js";import"./chunk-pjtg3wnm.js";import"./chunk-avkfsc3p.js";import"./chunk-ph2jfg1x.js";import"./chunk-wafb2xm9.js";import"./chunk-4ma2wnqw.js";import"./chunk-80fjfyha.js";import"./chunk-88eqqdk1.js";import"./chunk-c4fym47a.js";import"./chunk-as08hzp3.js";import"./chunk-n9t36h0q.js";import"./chunk-9p1dk5cb.js";import"./chunk-9psr8atb.js";import"./chunk-s23gtejf.js";import"./chunk-nte0t7v3.js";export{upgrade_default as default,migrateFolder2 as migrateFolder};
@@ -1,2 +1,2 @@
1
1
  // @bun
2
- import{registry2,loadManifest2}from"./chunk-hcb68zt7.js";import"./chunk-jb21jkp1.js";import{snapshotVersion2,entryCssPath2,componentsDir2}from"./chunk-cvn689d5.js";export{componentsDir2 as componentsDir,entryCssPath2 as entryCssPath,loadManifest2 as loadManifest,registry2 as registry,snapshotVersion2 as snapshotVersion};
2
+ import{registry2,loadManifest2}from"./chunk-p62fygqz.js";import"./chunk-c6hywdvq.js";import{snapshotVersion2,entryCssPath2,componentsDir2}from"./chunk-cvn689d5.js";export{componentsDir2 as componentsDir,entryCssPath2 as entryCssPath,loadManifest2 as loadManifest,registry2 as registry,snapshotVersion2 as snapshotVersion};