@lema-ufpb/ds-sync 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +194 -0
  2. package/dist/cli.js +13 -0
  3. package/package.json +48 -0
package/README.md ADDED
@@ -0,0 +1,194 @@
1
+ # @lema-ufpb/ds-sync
2
+
3
+ CLI para gerenciar componentes do [LEMA Design System](https://ds.lema.ufpb.br) em projetos consumidores.
4
+
5
+ Substitui `npx shadcn@latest add` em projetos UFPB com auth, lockfile, drift detection e CI.
6
+
7
+ ## Instalação
8
+
9
+ ```bash
10
+ npm install -D @lema-ufpb/ds-sync
11
+ ```
12
+
13
+ Configure no `.env.local`:
14
+
15
+ ```env
16
+ LEMA_DS_TOKEN=<seu-token>
17
+ LEMA_DS_REGISTRY=https://ds.lema.ufpb.br
18
+ ```
19
+
20
+ > Token fornecido pelo NOC do LEMA. Registry default aponta para produção.
21
+
22
+ ## Comandos
23
+
24
+ ```bash
25
+ npx lema-ds list # Listar disponíveis
26
+ npx lema-ds list --installed # Apenas instalados
27
+ npx lema-ds add dashbox bar-chart # Instalar (resolve registryDeps)
28
+ npx lema-ds add --all # Instalar tudo
29
+ npx lema-ds update dashbox # Atualizar componente(s)
30
+ npx lema-ds update --all # Atualizar tudo
31
+ npx lema-ds verify # Checar drift local vs lock
32
+ npx lema-ds info dashbox # Detalhes do componente
33
+ npx lema-ds whoami # Validar token
34
+ npx lema-ds diff dashbox # Diff local vs remoto
35
+ npx lema-ds diff --all # Diff de todos
36
+ npx lema-ds sync # Sincronizar (interativo)
37
+ npx lema-ds sync --all --yes # CI mode
38
+ npx lema-ds sync-tokens # Forçar refetch de tokens CSS
39
+ ```
40
+
41
+ ### Flags globais
42
+
43
+ | Flag | Descrição |
44
+ | ----------------------------- | ------------------------------ |
45
+ | `--registry <url>` | URL do registry |
46
+ | `--token <token>` | Token de autenticação |
47
+ | `--cwd <path>` | Diretório do projeto |
48
+ | `--lock <path>` | Path do lockfile |
49
+ | `--pm <npm\|pnpm\|yarn\|bun>` | Forçar package manager |
50
+ | `--no-install` | Pular instalação npm |
51
+ | `-y, --yes` | Pular confirmações |
52
+ | `--silent` | Apenas erros |
53
+ | `--verbose` | Log detalhado |
54
+ | `--json` | Output JSON (machine-readable) |
55
+
56
+ ## JSON output
57
+
58
+ ```bash
59
+ npx lema-ds add dashbox --json
60
+ # {"command":"add","success":true,"components":["dashbox"],...}
61
+
62
+ npx lema-ds verify --json
63
+ # {"command":"verify","ok":true,"drifted":[]}
64
+
65
+ npx lema-ds whoami --json
66
+ # {"command":"whoami","authenticated":true,"registry":"LEMA-UFPB Design System",...}
67
+ ```
68
+
69
+ ## Exit codes
70
+
71
+ | Code | Significado |
72
+ | ---- | ----------------------------- |
73
+ | 0 | Sucesso |
74
+ | 1 | Erro genérico |
75
+ | 2 | Auth (token inválido/ausente) |
76
+ | 3 | Config (`components.json`) |
77
+ | 4 | Drift detectado |
78
+ | 5 | Registry/network |
79
+
80
+ ## Lockfile
81
+
82
+ `lema-ds.lock.json` é gerado automaticamente e **deve ser commitado** — fonte de verdade para reproduzibilidade e drift detection.
83
+
84
+ ## Desenvolvimento
85
+
86
+ ```bash
87
+ make install # 📦 Instalar deps
88
+ make dev # 🔥 Watch mode
89
+ make build # 🏗️ Build produção
90
+ make test # 🧪 Rodar testes
91
+ make lint # 🧹 ESLint
92
+ make format # 💅 Prettier
93
+ make typecheck # 🔍 TypeScript
94
+ make pack # 📦 Gerar tarball para instalação local
95
+ make clean # 🧽 Limpar artefatos
96
+ make fresh # 🌱 Clean + install + build
97
+ make help # 📖 Listar todos os targets
98
+ ```
99
+
100
+ ## Arquitetura
101
+
102
+ ```
103
+ src/
104
+ ├── cli.ts # Entrypoint (commander)
105
+ ├── commands/ # add, list, update, verify, info, whoami, diff, sync, sync-tokens
106
+ ├── core/ # registry, resolver, writer, lockfile, hasher, pm, tokens, config, differ
107
+ └── lib/ # errors, ui
108
+ ```
109
+
110
+ Dependências: `commander`, `picocolors`, `@clack/prompts`. Build < 35KB.
111
+
112
+ ## Release & Deploy
113
+
114
+ O projeto usa um fluxo **GitFlow** com versionamento automático via [release-please](https://github.com/googleapis/release-please) e deploy contínuo para o GitHub Packages.
115
+
116
+ ### Branches
117
+
118
+ | Branch | Propósito |
119
+ | :-------- | :------------------------------------------------------------------ |
120
+ | `develop` | Branch de integração — todo PR de feature/fix entra aqui |
121
+ | `main` | Branch de release — recebe merge de `develop` quando pronto p/ ship |
122
+
123
+ ### Fluxo completo (do commit ao deploy)
124
+
125
+ ```text
126
+ feature/* ──PR──▶ develop ──PR──▶ main ──┐
127
+ │ (release-please observa)
128
+
129
+ chore(release): vX.Y.Z (PR aberto pelo bot)
130
+
131
+ ▼ merge
132
+ tag vX.Y.Z + GitHub Release criados
133
+
134
+ ▼ (release.published)
135
+ CD: npm build → npm publish (GitHub Packages)
136
+ ```
137
+
138
+ ### CI (`.github/workflows/ci.yml`)
139
+
140
+ Dispara **apenas em PR contra `develop`** — única etapa do fluxo onde código novo é introduzido. PRs `develop → main` e PRs do release-please **não rodam CI**. Pipeline **sequencial** com fail-fast (3 jobs encadeados) no self-hosted runner:
141
+
142
+ `lint → test → build`
143
+
144
+ 1. **🕵️‍♂️ Lint**: `npm run format:check`, `npm run lint` e `npm run typecheck`
145
+ 2. **🧪 Test**: `npm test` usando Vitest
146
+ 3. **📦 Build**: `npm run build` (tsup) e verificação rígida de tamanho do bundle (< 200KB)
147
+
148
+ > ⚠️ **Branch protection é obrigatório** em `develop` e `main`. O CI só atesta a qualidade se merges diretos (push) forem bloqueados.
149
+
150
+ ### Release automatizada (`.github/workflows/release-please.yml`)
151
+
152
+ Dispara em **push para `main`**. O bot do release-please:
153
+ 1. Lê os commits desde a última tag e calcula a próxima versão (Conventional Commits)
154
+ 2. Abre/atualiza um PR `chore(release): vX.Y.Z` contendo atualizações no `package.json`, `.release-please-manifest.json` e `CHANGELOG.md`
155
+ 3. Quando o PR é mergeado → cria a tag `vX.Y.Z` + GitHub Release automaticamente
156
+
157
+ ### CD (`.github/workflows/cd.yml`)
158
+
159
+ Dispara em **`release: published`**.
160
+ O fluxo realiza o checkout da tag da release, compila o CLI (`npm run build`) e faz a publicação no **GitHub Packages** (`npm publish`) sob o escopo `@lema-ufpb/ds-sync`.
161
+
162
+ > 🔒 **Garantia de qualidade**: a tag só nasce de um PR do release-please mergeado em `main`. O CI já validou todo o código em `develop`. O CD confia nessa validação upstream.
163
+
164
+ ### Recovery quando algo falha no CD
165
+
166
+ Caminho default (90% dos casos): **roll-forward** (novo fix em develop → merge para main → nova tag e release).
167
+
168
+ > ⚠️ **Atenção**: o `npm publish` é mais permanente que imagens Docker. Pacotes publicados não podem ser deletados facilmente após 72h, exigindo o uso de `npm deprecate`.
169
+
170
+ Para cenários complexos (tag criada mas publish falhou, pacote publicado com erro crítico, release-please calculando versão errada, block no CI do release-please), consulte o **[`.github/RELEASE_RUNBOOK.md`](.github/RELEASE_RUNBOOK.md)**. O runbook detalha todas as estratégias e comandos exatos para cada cenário.
171
+
172
+ ## Contribuindo
173
+
174
+ ### Conventional Commits (obrigatório)
175
+
176
+ Para que o release-please calcule corretamente a próxima versão e gere o `CHANGELOG.md`, **todos os commits devem seguir o padrão [Conventional Commits](https://www.conventionalcommits.org)**:
177
+
178
+ | Prefixo | Bump (pre-1.0) | Aparece no CHANGELOG |
179
+ | :--------------------------------------- | :------------------------------ | :------------------- |
180
+ | `feat:` | minor (`0.X.0`) | ✨ Features |
181
+ | `fix:` | patch (`0.0.X`) | 🐛 Bug Fixes |
182
+ | `perf:` | patch | ⚡ Performance |
183
+ | `refactor:` | patch | ♻️ Refactor |
184
+ | `docs:` | patch | 📚 Documentation |
185
+ | `revert:` | patch | ⏪ Reverts |
186
+ | `feat!:` / `BREAKING CHANGE:` | minor (pre-1.0 não pula p/ 1.0) | 💥 Breaking |
187
+ | `chore:` `style:` `test:` `build:` `ci:` | — (sem bump) | oculto |
188
+
189
+ ### Pull Request
190
+
191
+ 1. Crie uma branch a partir de `develop`: `git checkout -b feature/minha-feature`
192
+ 2. Push e abra PR contra `develop` — CI disparará automaticamente
193
+ 3. Após merge em `develop`, promova para `main` abrindo outro PR
194
+ 4. O bot do release-please fará a gestão da release a partir da `main`
package/dist/cli.js ADDED
@@ -0,0 +1,13 @@
1
+ #!/usr/bin/env node
2
+ var te=Object.defineProperty;var Et=(t,n)=>()=>(t&&(n=t(t=0)),n);var Ot=(t,n)=>{for(var e in n)te(t,e,{get:n[e],enumerable:!0})};var y,X,D,Y,dt,ct,h,v=Et(()=>{"use strict";y=class extends Error{constructor(e,o,s){super(o);this.code=e;this.hint=s;this.name="LemaError"}code;hint},X=class extends y{constructor(n,e,o){super(n,e,o),this.name="AuthError"}},D=class extends y{constructor(n,e,o){super(n,e,o),this.name="ConfigError"}},Y=class extends y{constructor(n,e,o){super(n,e,o),this.name="RegistryError"}},dt=class extends y{constructor(n,e,o){super(n,e,o),this.name="WriteError"}},ct=class extends y{constructor(n,e,o){super(n,e,o),this.name="ResolveError"}},h={SUCCESS:0,ERROR:1,AUTH:2,CONFIG:3,DRIFT:4,REGISTRY:5}});var Lt={};Ot(Lt,{loadConfig:()=>F,loadEnv:()=>$,parseEnvLine:()=>_t,resolveTargetPath:()=>lt});import{readFileSync as ee,existsSync as ne}from"fs";import{readFile as oe}from"fs/promises";import{resolve as ht}from"path";function _t(t){let n=t.trim();if(!n||n.startsWith("#"))return null;let e=n.indexOf("=");if(e===-1)return null;let o=n.slice(0,e).trim(),s=n.slice(e+1).trim();return(s.startsWith('"')&&s.endsWith('"')||s.startsWith("'")&&s.endsWith("'"))&&(s=s.slice(1,-1)),!o||!s?null:[o,s]}function $(t){let n=process.env.LEMA_DS_TOKEN??"",e=process.env.LEMA_DS_REGISTRY??se,o=[".env.local",".env"].map(s=>ht(t,s));for(let s of o){if(!ne(s))continue;let i=ee(s,"utf-8");for(let a of i.split(`
3
+ `)){let r=_t(a);if(!r)continue;let[l,d]=r;l==="LEMA_DS_TOKEN"&&!n&&(n=d),l==="LEMA_DS_REGISTRY"&&!e&&(e=d)}}return{token:n,registry:e}}function ut(t,n){return t?t.replace(/^\.\//,"").replace(/^@\//,""):n}async function F(t){let n=ht(t,"components.json"),e;try{let i=await oe(n,"utf-8");e=JSON.parse(i)}catch{throw new D("E_CONFIG_NO_COMPONENTS_JSON",`components.json not found or invalid at ${n}`,"Run npx shadcn@latest init to generate components.json in your project.")}let o={ui:ut(e.aliases?.ui,"components/ui"),components:ut(e.aliases?.components,"components"),lib:ut(e.aliases?.lib,"lib"),hooks:ut(e.aliases?.hooks,"hooks")},s=e.tailwind?.css?ht(t,e.tailwind.css):null;return{componentsJson:e,aliases:o,tailwindCssPath:s,cwd:t}}function lt(t,n,e){if(e.startsWith("/"))throw new D("E_CONFIG_INVALID_TARGET",`Absolute target path not allowed: ${e}`,"Use relative paths starting with components/, lib/ or hooks/.");let o;if(e.startsWith("components/ui/")){let s=e.slice(14);o=`${t.ui}/${s}`}else if(e.startsWith("components/")){let s=e.slice(11);o=`${t.components}/${s}`}else if(e.startsWith("lib/")){let s=e.slice(4);o=`${t.lib}/${s}`}else if(e.startsWith("hooks/")){let s=e.slice(6);o=`${t.hooks}/${s}`}else o=e;return ht(n,o)}var se,P=Et(()=>{"use strict";v();se="https://ds.lema.ufpb.br"});var Ut={};Ot(Ut,{detectPackageManager:()=>Tt,getInstallArgs:()=>jt,installPackages:()=>B});import{existsSync as mt}from"fs";import{resolve as pt}from"path";import{execSync as ke}from"child_process";function Tt(t){if(mt(pt(t,"pnpm-lock.yaml")))return"pnpm";if(mt(pt(t,"yarn.lock")))return"yarn";if(mt(pt(t,"bun.lockb"))||mt(pt(t,"bun.lock")))return"bun";if(mt(pt(t,"package-lock.json")))return"npm";let n=process.env.npm_config_user_agent??"";return n.includes("pnpm")?"pnpm":n.includes("yarn")?"yarn":n.includes("bun")?"bun":"npm"}function jt(t,n){switch(t){case"npm":return["install",...n,"--save-dev"];case"pnpm":return["add",...n,"--save-dev"];case"yarn":return["add",...n,"--dev"];case"bun":return["add",...n,"--dev"]}}async function B(t,n,e){let o=e??Tt(t),s=jt(o,n);try{ke(`${o} ${s.join(" ")}`,{cwd:t,stdio:"ignore",timeout:12e4})}catch{throw new y("E_PM_INSTALL",`Failed to install packages with ${o}`,`Run manually: ${o} ${s.join(" ")}`)}}var ft=Et(()=>{"use strict";v()});import{Command as He}from"commander";import vt from"picocolors";import*as H from"@clack/prompts";var ot="info",bt=!1;function $t(t){ot=t}function xt(t){bt=t}function C(){return bt}function x(){return ot==="silent"}function S(t){bt&&console.log(JSON.stringify(t,null,2))}function c(t){ot!=="silent"&&console.log(t)}function N(t){ot!=="silent"&&console.error(vt.yellow(`\u26A0 ${t}`))}function R(t){ot!=="silent"&&console.error(vt.red(`\u2716 ${t}`))}function b(t){ot!=="silent"&&console.log(vt.green(`\u2714 ${t}`))}function E(t,n){let e=H.spinner();return e.start(t),n().finally(()=>e.stop(""))}function K(t){return H.confirm({message:t})}function It(t,n){return H.select({message:t,options:n})}function I(t){ot!=="silent"&&H.outro(t)}P();import{existsSync as Nt}from"fs";import{resolve as ie}from"path";import{execSync as re}from"child_process";v();async function Z(t,n){let e=ie(t,"components.json");if(Nt(e))return;if(n){if(Dt(t),!Nt(e))throw new D("E_CONFIG_NO_COMPONENTS_JSON","components.json still not found after npx shadcn@latest init","Run npx shadcn@latest init manually in your project.");return}if(C()||x())throw new D("E_CONFIG_NO_COMPONENTS_JSON","components.json not found","Run npx shadcn@latest init first, then try again.");if(!await K("components.json not found. Run npx shadcn@latest init to create it?"))throw new D("E_CONFIG_NO_COMPONENTS_JSON","components.json is required to run this command","Run npx shadcn@latest init first, then try again.");Dt(t)}function Dt(t){re("npx shadcn@latest init --defaults",{cwd:t,stdio:"inherit",timeout:12e4})}var ae={ui:"components/ui",components:"components",lib:"lib",hooks:"hooks"};function z(t,n){let e=[];for(let[s,i]of Object.entries(ae)){let a=n[s];a!==i&&e.push({from:`@/${i}/`,to:`@/${a}/`})}if(e.length===0)return t;e.sort((s,i)=>i.from.length-s.from.length);let o=t;for(let{from:s,to:i}of e)o=o.replaceAll(s,i);return o}v();var Ft=3,Pt=250,ce=3e4;async function yt(t,n,e=1){let o=new AbortController,s=setTimeout(()=>o.abort(),ce);try{let i={"User-Agent":"@lema-ufpb/ds-sync/0.3.0"};n&&(i.Authorization=`Bearer ${n}`);let a=await fetch(t,{headers:i,signal:o.signal});if(a.status===401)throw new X("E_AUTH_401",`Token rejected by registry at ${t}`,"Check LEMA_DS_TOKEN in your .env.local file. Test with: lema-ds whoami");if(a.status===404)throw new Y("E_REGISTRY_NOT_FOUND",`Resource not found: ${t}`,"Use lema-ds list to see available components.");if(a.status>=500&&e<Ft){let r=Pt*Math.pow(2,e-1);return await new Promise(l=>setTimeout(l,r)),yt(t,n,e+1)}if(!a.ok)throw new Y("E_REGISTRY_NETWORK",`Registry returned ${a.status} for ${t}`,"Check connectivity and proxy settings.");return a}catch(i){if(i instanceof X||i instanceof Y)throw i;if(e<Ft){let a=Pt*Math.pow(2,e-1);return await new Promise(r=>setTimeout(r,a)),yt(t,n,e+1)}throw new Y("E_REGISTRY_NETWORK",`Network error accessing ${t}: ${i instanceof Error?i.message:String(i)}`,"Check connectivity, proxy, and registry availability.")}finally{clearTimeout(s)}}async function A(t,n){let e=`${t}/r/registry.json`,s=await(await yt(e,n)).json();if(!s||typeof s!="object"||!("name"in s)||!("items"in s))throw new Y("E_REGISTRY_INVALID",`Malformed registry manifest at ${e}`,"Report to the registry maintainer.");let i=s;return{name:String(i.name),version:String(i.version??"0.0.0"),homepage:i.homepage?String(i.homepage):void 0,items:i.items??[]}}async function _(t,n,e){let o=`${t}/r/${e}.json`,i=await(await yt(o,n)).json();if(!i||typeof i!="object"||!("name"in i))throw new Y("E_REGISTRY_INVALID",`Malformed registry item for "${e}" at ${o}`,"Report to the registry maintainer.");return i}v();async function kt(t,n){let e=new Set,o=new Set,s=[],i=new Set;async function a(r,l){if(e.has(r))return;if(o.has(r)){let m=[...l,r].join(" \u2192 ");throw new ct("E_RESOLVE_CYCLE",`Cycle detected in registryDependencies: ${m}`,"Report to the registry maintainer.")}o.add(r);let d=[...l,r],u;try{u=await n(r)}catch(m){throw o.delete(r),m instanceof y?m:new ct("E_RESOLVE_UNKNOWN",`Item "${r}" not found in registry`,m instanceof Error?m.message:String(m))}if(u.dependencies)for(let m of u.dependencies)i.add(m);if(u.registryDependencies)for(let m of u.registryDependencies)await a(m,d);o.delete(r),e.add(r),s.push(u)}for(let r of t)await a(r,[]);return{items:s,allNpmDeps:i}}v();P();import{writeFile as le,rename as me,unlink as pe}from"fs/promises";import{existsSync as dn}from"fs";import{dirname as fe}from"path";import{randomUUID as ge}from"crypto";async function tt(t,n){let e=[],o=[];try{for(let s of n){let i=lt(t.aliases,t.cwd,s.target),a=fe(i),{mkdir:r}=await import("fs/promises");await r(a,{recursive:!0});let l=`${i}.lema-tmp-${ge().slice(0,8)}`;await le(l,s.content,"utf-8"),e.push({tmp:l,real:i}),o.push({target:s.target,resolvedPath:i})}for(let{tmp:s,real:i}of e)await me(s,i);return o}catch(s){for(let{tmp:i}of e)try{await pe(i)}catch{}throw new dt("E_WRITE_FILE",`Error writing files: ${s instanceof Error?s.message:String(s)}`,"Check write permissions in the project directory.")}}v();import{readFile as de,writeFile as ue}from"fs/promises";import{existsSync as he}from"fs";import{resolve as wt}from"path";function At(t){return{$schema:"https://ds.lema.ufpb.br/schemas/lock-v1.json",lockfileVersion:1,registry:{url:t,name:"",version:"0.0.0",fetchedAt:new Date().toISOString()},components:{},tokens:null}}async function O(t,n){let e=n?wt(t,n):wt(t,"lema-ds.lock.json");if(!he(e))return null;try{let o=await de(e,"utf-8"),s=JSON.parse(o);return s.lockfileVersion?s:null}catch{throw new D("E_LOCK_INVALID",`Corrupted lockfile at ${e}`,"Backup the file and run lema-ds add --all to recreate it.")}}async function et(t,n,e){let o=e?wt(t,e):wt(t,"lema-ds.lock.json");await ue(o,JSON.stringify(n,null,2)+`
4
+ `,"utf-8")}function it(t,n,e,o,s,i){t.components[e]={registryVersion:n,installedAt:new Date().toISOString(),files:o,dependencies:s,registryDependencies:i}}function rt(t,n,e,o){t.registry.url=n,t.registry.name=e,t.registry.version=o,t.registry.fetchedAt=new Date().toISOString()}import{createHash as ye}from"crypto";function j(t){let n=t.replace(/\r\n/g,`
5
+ `);return ye("sha256").update(n,"utf-8").digest("hex")}async function St(t){let e=await(await import("fs/promises")).readFile(t,"utf-8");return j(e)}ft();v();import{readFile as we,writeFile as Se}from"fs/promises";var Jt=/\/\*\s*lema-ds:tokens:start[^*]*\*\/[\s\S]*?\/\*\s*lema-ds:tokens:end\s*\*\//;function Ce(t,n,e){let o=`/* lema-ds:tokens:start v${e} */
6
+ /* AUTO-GENERATED by @lema-ufpb/ds-sync. Do not edit manually. */
7
+ ${n.trim()}
8
+ /* lema-ds:tokens:end */`;return Jt.test(t)?t.replace(Jt,o):t.trimEnd()+`
9
+
10
+ `+o+`
11
+ `}async function G(t,n,e){let o;try{o=await we(t,"utf-8")}catch{throw new D("E_CONFIG_NO_TAILWIND_CSS",`CSS file declared in tailwind.css not found: ${t}`,"Verify the path in components.json \u2192 tailwind.css is correct.")}let s=Ce(o,n,e);await Se(t,s,"utf-8")}v();function Gt(t){t.command("add").description("Install one or more components from the LEMA Design System").argument("[components...]","Component name(s) to install").option("--all","Install all registry components").option("-y, --yes","Skip confirmation prompts").option("--no-install","Skip npm dependency installation").option("--registry <url>","Registry URL").option("--token <token>","Auth token").option("--cwd <path>","Project directory").option("--lock <path>","Lockfile path").option("--pm <name>","Package manager").option("--verbose","Verbose output").option("--silent","Errors only").option("--json","JSON output").action(async(n,e)=>{try{await Re(n,e),process.exit(h.SUCCESS)}catch(o){if(C()&&S({command:"add",success:!1,error:o instanceof y?{code:o.code,message:o.message,hint:o.hint}:String(o)}),o instanceof y){R(`[${o.code}] ${o.message}`),o.hint&&c(` \u2192 ${o.hint}`);let s={E_AUTH_MISSING:h.AUTH,E_AUTH_401:h.AUTH,E_CONFIG_NO_COMPONENTS_JSON:h.CONFIG,E_CONFIG_NO_ALIAS:h.CONFIG,E_CONFIG_NO_TAILWIND_CSS:h.CONFIG};process.exit(s[o.code]??h.ERROR)}R(String(o)),process.exit(h.ERROR)}})}async function Re(t,n){let e=n.cwd??process.cwd(),o=$(e),s=n.token??o.token,i=n.registry??o.registry,a=n.lock,r=C();!x()&&!r&&console.log(),await Z(e,n.yes);let l=await F(e),d=await(r?A(i,s):E("Fetching registry...",()=>A(i,s))),u=d.items.map(p=>p.name),m;if(n.all){let p=await O(e,a),g=new Set(Object.keys(p?.components??{}));if(m=u.filter(w=>!g.has(w)),m.length===0){if(r){S({command:"add",success:!0,components:[],files:[],npmDependencies:[],registryVersion:d.version,tokensUpdated:!1,message:"all_already_installed"});return}b("All components already installed.");return}}else{if(t.length===0)throw new y("E_USAGE","Specify one or more component names or use --all","Example: lema-ds add dashbox bar-chart");{let p=t.filter(g=>!u.includes(g));if(p.length>0)throw new y("E_RESOLVE_UNKNOWN",`Component(s) not found: ${p.join(", ")}`,"Use lema-ds list to see available components.");m=t}}r||c(`Resolving dependencies for ${m.length} component(s)...`);let k=await kt(m,async p=>_(i,s,p)),f=[...k.allNpmDeps];if(!n.yes&&!r){c(`
12
+ Components to install:`);for(let g of k.items)c(` \u2022 ${g.name}${g.title?` (${g.title})`:""}`);if(f.length>0&&c(`
13
+ npm dependencies: ${f.join(", ")}`),!await K("Proceed?")){c("Operation cancelled.");return}}let L=k.items.flatMap(p=>(p.files??[]).filter(g=>g.content).map(g=>({target:g.target??g.path,content:z(g.content,l.aliases)}))),T=await(r?tt(l,L):E("Writing files...",()=>tt(l,L)));if(!r)for(let p of T)b(`Written: ${p.resolvedPath}`);let q=await O(e,a)??At(i);rt(q,i,d.name,d.version);for(let p of k.items){let g=(p.files??[]).map(w=>({target:w.target??w.path,sha256:j(w.content?z(w.content,l.aliases):"")}));it(q,d.version,p.name,g,p.dependencies,p.registryDependencies)}await et(e,q,a),!n.noInstall&&f.length>0&&(r?await B(e,f):await E(`Installing dependencies (${f.length} packages)...`,async()=>{let{detectPackageManager:p}=await Promise.resolve().then(()=>(ft(),Ut)),g=n.pm??p(e);await B(e,f,g)}));let Q=!1;if(!k.items.some(p=>p.type==="registry:style")){let p=await _(i,s,"tokens").catch(()=>null),g=l.tailwindCssPath,w=p?.files?.[0]?.content;g&&w&&(r?await G(g,w,d.version):(await E("Patching CSS tokens...",()=>G(g,w,d.version)),b("CSS tokens updated.")),Q=!0)}if(r){S({command:"add",success:!0,components:k.items.map(p=>p.name),files:T.map(p=>({target:p.target,path:p.resolvedPath})),npmDependencies:f,registryVersion:d.version,tokensUpdated:Q});return}I(`Added ${k.items.length} component(s).`)}P();v();function Wt(t){t.command("list").description("List available components in the registry").option("--installed","Show only installed components").option("--registry <url>","Registry URL").option("--token <token>","Auth token").option("--cwd <path>","Project directory").option("--lock <path>","Lockfile path").option("--verbose","Verbose output").option("--silent","Errors only").option("--json","JSON output").action(async n=>{try{await Ee(n),process.exit(h.SUCCESS)}catch(e){C()&&S({command:"list",success:!1,error:e instanceof y?{code:e.code,message:e.message,hint:e.hint}:String(e)}),e instanceof y&&(N(`[${e.code}] ${e.message}`),process.exit(h.REGISTRY)),N(String(e)),process.exit(h.ERROR)}})}async function Ee(t){let n=t.cwd??process.cwd(),e=$(n),o=t.token??e.token,s=t.registry??e.registry,i=t.lock;x()||console.log();let a=await E("Fetching registry...",()=>A(s,o));if(C()){if(t.installed){let l=await O(n,i);S({command:"list",mode:"installed",components:l?.components??{}})}else S({command:"list",mode:"available",...a});return}let r=a.version&&a.version!=="0.0.0"?` ${a.version}`:"";if(c(`lema-ds v1.0.0 \u2014 Registry: ${a.name}${r}`),a.homepage&&c(`Homepage: ${a.homepage}`),c(""),t.installed){let l=await O(n,i);if(!l||Object.keys(l.components).length===0){N("No components installed.");return}c("Installed components:");for(let[d,u]of Object.entries(l.components)){let m=u.files.map(k=>k.target).join(", ");c(` \u2022 ${d}`),c(` Version: ${u.registryVersion}`),c(` Files: ${m}`)}}else{c(`Available components (${a.items.length}):`);for(let l of a.items)c(` \u2022 ${l.name}`)}I(`${t.installed?"Installed":"Available"}: ${t.installed?Object.keys((await O(n,i))?.components??{}).length:a.items.length}`)}P();ft();v();function Vt(t){t.command("update").description("Update installed components").argument("[components...]","Component name(s)").option("--all","Update all components").option("--force","Force update even if same version").option("-y, --yes","Skip confirmation prompts").option("--no-install","Skip npm dependency installation").option("--registry <url>","Registry URL").option("--token <token>","Auth token").option("--cwd <path>","Project directory").option("--lock <path>","Lockfile path").option("--pm <name>","Package manager").option("--verbose","Verbose output").option("--silent","Errors only").option("--json","JSON output").action(async(n,e)=>{try{await ve(n,e),process.exit(h.SUCCESS)}catch(o){C()&&S({command:"update",success:!1,error:o instanceof y?{code:o.code,message:o.message,hint:o.hint}:String(o)}),o instanceof y&&(R(`[${o.code}] ${o.message}`),o.hint&&c(` \u2192 ${o.hint}`),process.exit(h.ERROR)),R(String(o)),process.exit(h.ERROR)}})}async function ve(t,n){let e=n.cwd??process.cwd(),o=$(e),s=n.token??o.token,i=n.registry??o.registry,a=n.lock,r=C();!x()&&!r&&console.log(),await Z(e,n.yes);let l=await O(e,a);if(!l||Object.keys(l.components).length===0){if(r){S({command:"update",success:!1,error:"no_components_installed"});return}N("No components installed. Run lema-ds add first.");return}let d=await F(e),u=await(r?A(i,s):E("Fetching registry...",()=>A(i,s)));if(u.version===l.registry.version&&!n.force){if(r){S({command:"update",success:!0,components:[],files:[],npmDependencies:[],registryVersion:u.version,tokensUpdated:!1,message:"already_up_to_date"});return}b("Registry is already up to date (no changes).");return}let m;if(n.all)m=Object.keys(l.components);else if(t.length>0){let p=t.filter(g=>!l.components[g]);if(p.length>0)throw new y("E_RESOLVE_UNKNOWN",`Component(s) not installed: ${p.join(", ")}`,"Use lema-ds list --installed to see installed components.");m=t}else m=Object.keys(l.components);let k=await kt(m,async p=>_(i,s,p));if(!n.yes&&!r&&!await K(`Update ${k.items.length} component(s) to version ${u.version}?`)){c("Operation cancelled.");return}let f=k.items.flatMap(p=>(p.files??[]).filter(g=>g.content).map(g=>({target:g.target??g.path,content:z(g.content,d.aliases)}))),L=await(r?tt(d,f):E("Updating files...",()=>tt(d,f)));rt(l,i,u.name,u.version);for(let p of k.items){let g=(p.files??[]).map(w=>({target:w.target??w.path,sha256:j(w.content?z(w.content,d.aliases):"")}));it(l,u.version,p.name,g,p.dependencies,p.registryDependencies)}await et(e,l,a),r||b(`Updated ${L.length} file(s).`);let T=[...k.allNpmDeps];!n.noInstall&&T.length>0&&(r?await B(e,T):await E("Installing dependencies...",()=>B(e,T)));let st=!1,q=await _(i,s,"tokens").catch(()=>null),Q=d.tailwindCssPath,nt=q?.files?.[0]?.content;if(Q&&nt&&(r?await G(Q,nt,u.version):await E("Patching CSS tokens...",()=>G(Q,nt,u.version)),st=!0),r){S({command:"update",success:!0,components:k.items.map(p=>p.name),files:L.map(p=>({target:p.target,path:p.resolvedPath})),npmDependencies:T,registryVersion:u.version,tokensUpdated:st});return}I(`Updated ${k.items.length} component(s).`)}P();v();import be from"picocolors";function Mt(t){t.command("verify").description("Verify installed component integrity (local vs lock drift check)").option("--cwd <path>","Project directory").option("--lock <path>","Lockfile path").option("--verbose","Verbose output").option("--silent","Errors only").option("--json","JSON output").action(async n=>{try{let e=await $e(n);process.exit(e?h.SUCCESS:h.DRIFT)}catch(e){e instanceof y&&(R(`[${e.code}] ${e.message}`),process.exit(h.ERROR)),R(String(e)),process.exit(h.ERROR)}})}async function $e(t){let n=t.cwd??process.cwd(),e=t.lock;x()||console.log();let o=await O(n,e);if(!o)return N("No lockfile found. Run lema-ds add first."),!0;let s=await F(n),i=[];for(let[a,r]of Object.entries(o.components))for(let l of r.files){let d=(await Promise.resolve().then(()=>(P(),Lt))).resolveTargetPath(s.aliases,n,l.target),u=await St(d).catch(()=>null);u?u!==l.sha256&&i.push({name:a,target:l.target,expected:l.sha256,actual:u}):i.push({name:a,target:l.target,expected:l.sha256,actual:"FILE_MISSING"})}if(C())return S({command:"verify",ok:i.length===0,drifted:i}),i.length===0;if(i.length===0)return b("All components are intact. No drift detected."),!0;R(`Drift detected in ${i.length} file(s):`);for(let a of i)c(` ${be.red("!")} ${a.name}: ${a.target}`),c(` Expected: ${a.expected.slice(0,12)}...`),c(` Actual: ${a.actual==="FILE_MISSING"?"FILE MISSING":`${a.actual.slice(0,12)}...`}`);return c(""),N("Run lema-ds diff to see detailed changes."),!1}P();v();import at from"picocolors";function Ht(t){t.command("info").description("Show detailed information about a component").argument("<component>","Component name").option("--registry <url>","Registry URL").option("--token <token>","Auth token").option("--cwd <path>","Project directory").option("--verbose","Verbose output").option("--silent","Errors only").option("--json","JSON output").action(async(n,e)=>{try{await Oe(n,e),process.exit(h.SUCCESS)}catch(o){C()&&S({command:"info",success:!1,error:o instanceof y?{code:o.code,message:o.message,hint:o.hint}:String(o)}),o instanceof y&&(R(`[${o.code}] ${o.message}`),o.hint&&c(` \u2192 ${o.hint}`),process.exit(h.REGISTRY)),R(String(o)),process.exit(h.ERROR)}})}async function Oe(t,n){let e=n.cwd??process.cwd(),o=$(e),s=n.token??o.token,i=n.registry??o.registry,a=await E(`Fetching ${t}...`,()=>_(i,s,t));if(C()){S({command:"info",...a});return}if(c(""),c(`${at.bold(a.name)}${a.title?` \u2014 ${a.title}`:""}`),a.description&&c(` ${at.dim(a.description)}`),c(""),c(`${at.dim("Type:")} ${a.type}`),c(""),a.dependencies&&a.dependencies.length>0){c(`${at.dim("npm dependencies:")}`);for(let r of a.dependencies)c(` \u2022 ${r}`);c("")}if(a.registryDependencies&&a.registryDependencies.length>0){c(`${at.dim("Registry dependencies:")}`);for(let r of a.registryDependencies)c(` \u2022 ${r}`);c("")}c(`${at.dim("Files:")}`);for(let r of a.files??[])c(` \u2022 ${r.path} (${r.type})`);I(`info: ${a.name}`)}P();v();import Kt from"picocolors";function Yt(t){t.command("whoami").description("Validate auth token against the registry").option("--registry <url>","Registry URL").option("--token <token>","Auth token").option("--cwd <path>","Project directory").option("--verbose","Verbose output").option("--silent","Errors only").option("--json","JSON output").action(async n=>{try{await xe(n),process.exit(h.SUCCESS)}catch(e){C()&&S({command:"whoami",authenticated:!1,error:e instanceof y?{code:e.code,message:e.message,hint:e.hint}:String(e)}),e instanceof X&&(R(`[${e.code}] ${e.message}`),e.hint&&c(` \u2192 ${e.hint}`),process.exit(h.AUTH)),e instanceof y&&(R(`[${e.code}] ${e.message}`),process.exit(h.ERROR)),R(String(e)),process.exit(h.ERROR)}})}async function xe(t){let n=t.cwd??process.cwd(),e=$(n),o=t.token??e.token,s=t.registry??e.registry;if(!o)throw new X("E_AUTH_MISSING","Auth token not found","Set LEMA_DS_TOKEN in your .env.local file.");let i=await E("Validating token...",()=>A(s,o));if(C()){S({command:"whoami",authenticated:!0,registry:i.name,version:i.version});return}c(""),c(`${Kt.green("\u2714")} Token is valid!`),c(` Registry: ${i.name} ${i.version}`),c(` URL: ${s}`);let a=o.length>8?`${o.slice(0,4)}${"\u2022".repeat(o.length-8)}${o.slice(-4)}`:"\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022";c(` Token: ${Kt.dim(a)}`),I("Authenticated")}P();import J from"picocolors";import{execSync as Ie}from"child_process";import{existsSync as _e,writeFileSync as Le,unlinkSync as Ne}from"fs";import{readFile as De}from"fs/promises";import{tmpdir as Fe}from"os";import{randomUUID as Pe}from"crypto";import{resolve as Ae}from"path";P();async function Ct(t,n,e,o){let s=[];for(let i of e.files){let a=lt(t.aliases,t.cwd,i.target),r=_e(a)?await St(a).catch(()=>null):null,d=o?.files?.find(f=>(f.target??f.path)===i.target)?.content??null,u=d?j(d):null,m;r?u===null?m=r===i.sha256?"up-to-date":"drift-only":r===i.sha256&&u===i.sha256?m="up-to-date":r===i.sha256&&u!==i.sha256?m="clean-update":r!==i.sha256&&u===i.sha256?m="drift-only":m="conflict":m="missing";let k=null;if(m==="conflict"||m==="drift-only")try{k=await De(a,"utf-8")}catch{k=null}s.push({target:i.target,resolvedPath:a,status:m,localHash:r,lockHash:i.sha256,remoteHash:u,localContent:k,remoteContent:d})}return{name:e.registryVersion,files:s}}function Rt(t){let n={components:t,upToDate:0,cleanUpdates:0,drifts:0,conflicts:0,missing:0};for(let e of t)for(let o of e.files)o.status==="up-to-date"?n.upToDate++:o.status==="clean-update"?n.cleanUpdates++:o.status==="drift-only"?n.drifts++:o.status==="conflict"?n.conflicts++:o.status==="missing"&&n.missing++;return n}function zt(t,n,e){let o=Ae(Fe(),`lema-ds-${e}-${Pe().slice(0,8)}`);Le(o,n,"utf-8");try{return Ie(`git diff --no-index -- "${t}" "${o}"`,{encoding:"utf-8",stdio:["ignore","pipe","pipe"],timeout:15e3})}catch(s){return s.stdout?s.stdout:s.stderr?s.stderr:`[diff failed: ${s.message}]`}finally{try{Ne(o)}catch{}}}function gt(t,n){return zt(t,n,"remote")}function Bt(t,n,e){return zt(t,n,e)}v();function qt(t){t.command("diff").description("Show differences between local files and the registry").argument("[components...]","Component name(s)").option("--all","Show diff for all installed components").option("--registry <url>","Registry URL").option("--token <token>","Auth token").option("--cwd <path>","Project directory").option("--lock <path>","Lockfile path").option("--verbose","Verbose output").option("--silent","Errors only").option("--json","JSON output").action(async(n,e)=>{try{await Te(n,e),process.exit(h.SUCCESS)}catch(o){o instanceof y&&(R(`[${o.code}] ${o.message}`),o.hint&&c(` \u2192 ${o.hint}`),process.exit(h.ERROR)),R(String(o)),process.exit(h.ERROR)}})}async function Te(t,n){let e=n.cwd??process.cwd(),o=$(e),s=n.token??o.token,i=n.registry??o.registry,a=n.lock;x()||console.log();let r=await O(e,a);if(!r||Object.keys(r.components).length===0){N("No components installed. Run lema-ds add first.");return}let l=await F(e),d;if(n.all)d=Object.keys(r.components);else if(t.length>0){let k=t.filter(f=>!r.components[f]);if(k.length>0)throw new y("E_RESOLVE_UNKNOWN",`Component(s) not installed: ${k.join(", ")}`,"Use lema-ds list --installed to see installed components.");d=t}else d=Object.keys(r.components);let u=[];for(let k of d){let f=r.components[k],L=await _(i,s,k).catch(()=>null),T=await Ct(l,r,f,L);u.push({...T,name:k})}let m=Rt(u);if(C()){S({summary:m,components:u});return}if(m.upToDate===m.upToDate+m.cleanUpdates+m.drifts+m.conflicts+m.missing){b("All components are up to date. No differences found.");return}for(let k of u)for(let f of k.files)f.status!=="up-to-date"&&(c(""),c(J.bold(`\u{1F4C4} ${f.target}`)),c(` Status: ${je(f.status)}`),f.status==="drift-only"&&f.localContent&&(c(" Local changes (not reflected in lock):"),c(Bt(f.resolvedPath,f.localContent,"local"))),f.status==="conflict"&&f.remoteContent&&(c(" Conflict between local and remote versions:"),c(gt(f.resolvedPath,f.remoteContent))),f.status==="clean-update"&&f.remoteContent&&(c(" Remote version available (safe to apply):"),c(gt(f.resolvedPath,f.remoteContent))),f.status==="missing"&&c(` Expected file not found at: ${f.resolvedPath}`));c(""),c(Ue(m)),I("diff complete")}function je(t){return{"up-to-date":J.green("\u2713 up to date"),"clean-update":J.blue("\u21BB safe update"),"drift-only":J.yellow("\u26A1 locally edited"),conflict:J.red("\u2717 conflict"),missing:J.red("\u2717 file missing")}[t]??t}function Ue(t){let n=[];return t.upToDate>0&&n.push(J.green(`${t.upToDate} up to date`)),t.cleanUpdates>0&&n.push(J.blue(`${t.cleanUpdates} safe update(s)`)),t.drifts>0&&n.push(J.yellow(`${t.drifts} drift(s)`)),t.conflicts>0&&n.push(J.red(`${t.conflicts} conflict(s)`)),t.missing>0&&n.push(J.red(`${t.missing} missing`)),n.join(" | ")}P();import W from"picocolors";ft();v();function Qt(t){t.command("sync").description("Synchronize installed components with the registry (interactive)").argument("[components...]","Component name(s)").option("--all","Synchronize all components").option("-y, --yes","Auto-apply safe changes (no prompts)").option("--no-install","Skip npm dependency installation").option("--registry <url>","Registry URL").option("--token <token>","Auth token").option("--cwd <path>","Project directory").option("--lock <path>","Lockfile path").option("--pm <name>","Package manager").option("--verbose","Verbose output").option("--silent","Errors only").option("--json","JSON output").action(async(n,e)=>{try{await Je(n,e),process.exit(h.SUCCESS)}catch(o){o instanceof y&&(R(`[${o.code}] ${o.message}`),o.hint&&c(` \u2192 ${o.hint}`),process.exit(h.ERROR)),R(String(o)),process.exit(h.ERROR)}})}async function Je(t,n){let e=n.cwd??process.cwd(),o=$(e),s=n.token??o.token,i=n.registry??o.registry,a=n.lock,r=C();!x()&&!r&&console.log(),await Z(e,n.yes);let l=await O(e,a);if(!l||Object.keys(l.components).length===0){if(r){S({command:"sync",success:!1,message:"no_components_installed"});return}N("No components installed. Run lema-ds add first.");return}let d=await F(e),u=await(r?A(i,s):E("Fetching registry...",()=>A(i,s)));u.version===l.registry.version&&!r&&b("Registry is up to date. Checking for local drift...");let m;if(n.all)m=Object.keys(l.components);else if(t.length>0){let g=t.filter(w=>!l.components[w]);if(g.length>0)throw new y("E_RESOLVE_UNKNOWN",`Component(s) not installed: ${g.join(", ")}`,"Use lema-ds list --installed to see installed components.");m=t}else m=Object.keys(l.components);let k=await Ge(m,d,l,i,s),f=Rt(k);if(r){S({command:"sync",summary:f,components:k});return}if(f.upToDate===f.upToDate+f.cleanUpdates+f.drifts+f.conflicts+f.missing&&f.cleanUpdates+f.drifts+f.conflicts===0){b("Everything is in sync. No changes detected.");return}c(""),c(W.bold("Change summary:")),c(Ve(f)),c("");let L=[],T=[],st=new Set;for(let g of k)for(let w of g.files){if(w.status==="up-to-date")continue;let V;if(n.yes?V=w.status==="conflict"?"skip":"take":V=await We(g.name,w),V==="abort"){c("Operation aborted by user.");return}if(V==="skip"){c(` ${W.dim("\u2192 skipping")} ${w.target}`);continue}if(V==="keep"){c(` ${W.dim("\u2192 keeping local")} ${w.target}`);continue}V==="take"&&w.remoteContent&&(L.push({target:w.target,content:z(w.remoteContent,d.aliases)}),b(`${W.green("\u2192")} ${w.target}`))}if(L.length===0){c("No changes to apply.");return}if(!n.yes&&!await K(`Apply ${L.length} change(s)?`)){c("Operation cancelled.");return}await E("Applying changes...",()=>tt(d,L));for(let g of k){let w=await _(i,s,g.name).catch(()=>null);if(!w)continue;let V=(w.files??[]).filter(M=>L.some(Zt=>Zt.target===(M.target??M.path))).map(M=>({target:M.target??M.path,sha256:j(M.content?z(M.content,d.aliases):"")}));if(V.length>0&&(T.push({name:g.name,files:V,dependencies:w.dependencies,registryDependencies:w.registryDependencies}),w.dependencies))for(let M of w.dependencies)st.add(M)}rt(l,i,u.name,u.version);for(let g of T)it(l,u.version,g.name,g.files,g.dependencies,g.registryDependencies);await et(e,l,a);let q=[...st];!n.noInstall&&q.length>0&&await E("Installing dependencies...",()=>B(e,q));let Q=await _(i,s,"tokens").catch(()=>null),nt=d.tailwindCssPath,p=Q?.files?.[0]?.content;nt&&p&&await E("Patching CSS tokens...",()=>G(nt,p,u.version)),I(`${L.length} file(s) synchronized.`)}async function Ge(t,n,e,o,s){let i=[];for(let a of t){let r=e.components[a],l=await _(o,s,a).catch(()=>null),d=await Ct(n,e,r,l);i.push({...d,name:a})}return i}async function We(t,n){c(""),c(W.bold(`Conflict in ${t} \u2014 ${n.target}`));let e=[{label:"Keep local",value:"keep"},{label:"Overwrite with remote",value:"take"},{label:"Skip this file",value:"skip"},{label:"Abort everything",value:"abort"}];n.remoteContent&&await K("View diff before deciding?")&&(c(""),c(gt(n.resolvedPath,n.remoteContent)));let o=await It(`What to do with ${n.target}?`,e);return typeof o!="string"?"skip":o}function Ve(t){let n=[];return t.upToDate>0&&n.push(W.green(`${t.upToDate} up to date`)),t.cleanUpdates>0&&n.push(W.blue(`${t.cleanUpdates} safe update(s)`)),t.drifts>0&&n.push(W.yellow(`${t.drifts} drift(s)`)),t.conflicts>0&&n.push(W.red(`${t.conflicts} conflict(s)`)),t.missing>0&&n.push(W.red(`${t.missing} missing`)),n.join(" | ")}P();v();function Xt(t){t.command("sync-tokens").description("Force refetch and patch CSS tokens").option("--registry <url>","Registry URL").option("--token <token>","Auth token").option("--cwd <path>","Project directory").option("--lock <path>","Lockfile path").option("--verbose","Verbose output").option("--silent","Errors only").option("--json","JSON output").action(async n=>{try{await Me(n),process.exit(h.SUCCESS)}catch(e){C()&&S({command:"sync-tokens",success:!1,error:e instanceof y?{code:e.code,message:e.message,hint:e.hint}:String(e)}),e instanceof y&&(R(`[${e.code}] ${e.message}`),e.hint&&c(` \u2192 ${e.hint}`),process.exit(h.ERROR)),R(String(e)),process.exit(h.ERROR)}})}async function Me(t){let n=t.cwd??process.cwd(),e=$(n),o=t.token??e.token,s=t.registry??e.registry,i=t.lock,a=C();!x()&&!a&&console.log(),await Z(n);let r=await F(n);if(!r.tailwindCssPath)throw new y("E_CONFIG_NO_TAILWIND_CSS","tailwind.css not declared in components.json","Add tailwind.css to your components.json.");let d=(await E("Fetching tokens from registry...",()=>_(s,o,"tokens"))).files?.[0]?.content;if(!d)throw new y("E_REGISTRY_INVALID","Tokens item does not contain CSS content","Report to the registry maintainer.");let u=r.tailwindCssPath;await E("Patching globals.css...",()=>G(u,d,"0.0.0"));let m=await O(n,i);if(m&&(m.tokens={registryVersion:m.registry.version,installedAt:new Date().toISOString(),sha256:j(d)},await et(n,m,i)),a){S({command:"sync-tokens",success:!0,sha256:j(d),path:r.tailwindCssPath});return}b(`CSS tokens updated at ${r.tailwindCssPath}`),I("sync-tokens complete")}var U=new He;U.name("lema-ds").description("CLI to manage LEMA Design System components").version("1.0.0").hook("preAction",t=>{let n=t.optsWithGlobals();n.silent?$t("silent"):n.verbose&&$t("verbose"),n.json&&xt(!0)});U.option("--verbose","Verbose output").option("--silent","Errors only").option("--json","JSON output");Gt(U);Wt(U);Vt(U);Mt(U);Ht(U);Yt(U);qt(U);Qt(U);Xt(U);U.parse(process.argv);
package/package.json ADDED
@@ -0,0 +1,48 @@
1
+ {
2
+ "name": "@lema-ufpb/ds-sync",
3
+ "version": "1.0.0",
4
+ "description": "CLI to manage LEMA Design System components in consuming projects",
5
+ "type": "module",
6
+ "bin": {
7
+ "lema-ds": "./dist/cli.js"
8
+ },
9
+ "files": [
10
+ "dist"
11
+ ],
12
+ "scripts": {
13
+ "build": "tsup",
14
+ "dev": "tsup --watch",
15
+ "typecheck": "tsc --noEmit",
16
+ "test": "vitest run",
17
+ "test:watch": "vitest",
18
+ "lint": "eslint src/ test/",
19
+ "format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"",
20
+ "format:check": "prettier --check \"src/**/*.ts\" \"test/**/*.ts\"",
21
+ "prepublishOnly": "npm run build"
22
+ },
23
+ "dependencies": {
24
+ "@clack/prompts": "^0.9.1",
25
+ "commander": "^13.1.0",
26
+ "picocolors": "^1.1.1"
27
+ },
28
+ "peerDependencies": {
29
+ "shadcn": "^4.8.0"
30
+ },
31
+ "devDependencies": {
32
+ "@eslint/js": "^10.0.1",
33
+ "@types/node": "^22.13.0",
34
+ "eslint": "^9.39.4",
35
+ "prettier": "^3.8.3",
36
+ "tsup": "^8.3.6",
37
+ "typescript": "^5.7.3",
38
+ "typescript-eslint": "^8.59.4",
39
+ "vitest": "^3.0.4"
40
+ },
41
+ "engines": {
42
+ "node": ">=20.0.0"
43
+ },
44
+ "publishConfig": {
45
+ "access": "public",
46
+ "registry": "https://registry.npmjs.org"
47
+ }
48
+ }