@ekanos/cli 0.1.5 → 0.1.7

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 (83) hide show
  1. package/README.md +81 -2
  2. package/dist/bin.js +14 -1
  3. package/dist/bin.js.map +1 -1
  4. package/dist/commands/dev.d.ts +4 -0
  5. package/dist/commands/dev.js +6 -20
  6. package/dist/commands/dev.js.map +1 -1
  7. package/dist/commands/init.d.ts +61 -0
  8. package/dist/commands/init.js +145 -18
  9. package/dist/commands/init.js.map +1 -1
  10. package/dist/commands/publish.d.ts +13 -0
  11. package/dist/commands/publish.js +38 -15
  12. package/dist/commands/publish.js.map +1 -1
  13. package/dist/commands/status.js +9 -2
  14. package/dist/commands/status.js.map +1 -1
  15. package/dist/commands/upgrade.d.ts +47 -0
  16. package/dist/commands/upgrade.js +546 -0
  17. package/dist/commands/upgrade.js.map +1 -0
  18. package/dist/commands/validate.d.ts +5 -0
  19. package/dist/commands/validate.js +20 -6
  20. package/dist/commands/validate.js.map +1 -1
  21. package/dist/context.d.ts +9 -0
  22. package/dist/context.js +9 -0
  23. package/dist/context.js.map +1 -1
  24. package/dist/copy-tree.d.ts +48 -0
  25. package/dist/copy-tree.js +55 -0
  26. package/dist/copy-tree.js.map +1 -0
  27. package/dist/delegate.d.ts +28 -0
  28. package/dist/delegate.js +136 -0
  29. package/dist/delegate.js.map +1 -0
  30. package/dist/harness-scaffold.d.ts +21 -6
  31. package/dist/harness-scaffold.js +8 -5
  32. package/dist/harness-scaffold.js.map +1 -1
  33. package/dist/index.js +40 -3
  34. package/dist/index.js.map +1 -1
  35. package/dist/package-manager.d.ts +10 -0
  36. package/dist/package-manager.js +32 -0
  37. package/dist/package-manager.js.map +1 -1
  38. package/dist/parse-argv.d.ts +7 -1
  39. package/dist/parse-argv.js +10 -2
  40. package/dist/parse-argv.js.map +1 -1
  41. package/dist/project-checks.d.ts +9 -0
  42. package/dist/project-checks.js +29 -0
  43. package/dist/project-checks.js.map +1 -1
  44. package/dist/project.d.ts +23 -0
  45. package/dist/project.js +66 -1
  46. package/dist/project.js.map +1 -1
  47. package/dist/publish-api.d.ts +1 -1
  48. package/dist/publish-api.js +2 -2
  49. package/dist/publish-api.js.map +1 -1
  50. package/dist/safe-fs.d.ts +1 -1
  51. package/dist/safe-fs.js.map +1 -1
  52. package/dist/schema-skew.d.ts +77 -0
  53. package/dist/schema-skew.js +163 -0
  54. package/dist/schema-skew.js.map +1 -0
  55. package/dist/sources-api.d.ts +1 -1
  56. package/dist/sources-api.js.map +1 -1
  57. package/dist/toolchain-api.d.ts +54 -0
  58. package/dist/toolchain-api.js +58 -0
  59. package/dist/toolchain-api.js.map +1 -0
  60. package/dist/toolchain-resolve.d.ts +26 -0
  61. package/dist/toolchain-resolve.js +100 -0
  62. package/dist/toolchain-resolve.js.map +1 -0
  63. package/dist/toolchain.d.ts +21 -0
  64. package/dist/toolchain.js +16 -0
  65. package/dist/toolchain.js.map +1 -0
  66. package/dist/update-notice.d.ts +32 -0
  67. package/dist/update-notice.js +180 -0
  68. package/dist/update-notice.js.map +1 -0
  69. package/dist/validate-findings.d.ts +12 -0
  70. package/dist/validate-findings.js +14 -1
  71. package/dist/validate-findings.js.map +1 -1
  72. package/package.json +1 -1
  73. package/templates/AGENTS.md.tmpl +81 -0
  74. package/templates/CLAUDE.md.tmpl +2 -1
  75. package/templates/claude-skill.md.tmpl +20 -2
  76. package/templates/docs/api-notes.md.tmpl +21 -0
  77. package/templates/mcp.json.tmpl +8 -0
  78. package/templates/skills/build-integration/SKILL.md +178 -0
  79. package/templates/skills/build-integration/references/api-docs-research.md +93 -0
  80. package/templates/skills/build-integration/references/api-wiring.md +94 -0
  81. package/templates/skills/build-integration/references/widget-design.md +132 -0
  82. package/templates/skills/polish-widgets/SKILL.md +139 -0
  83. package/templates/skills/polish-widgets/references/patterns.md +269 -0
@@ -0,0 +1,269 @@
1
+ # Widget fix patterns — before → after
2
+
3
+ Each pattern names the failure, then shows the smallest honest fix. All
4
+ imports are `@ekanos/ui/*`; icons go through `@ekanos/ui/icon` (Font Awesome
5
+ names — verify against the Free set). Everything sits inside the standard
6
+ `Widget.Active` body from the SDK's compound API; these are the bodies, not
7
+ the wrapper.
8
+
9
+ ## 1. Flat list row → row with a first read
10
+
11
+ **Failure:** every field the same size, so nothing answers "what needs my
12
+ attention?". Values styled like labels, metadata styled like values.
13
+
14
+ ```tsx
15
+ // BEFORE — three spans, one weight
16
+ {
17
+ invoices.map((inv) => (
18
+ <div key={inv.id} className="flex justify-between p-2 text-sm">
19
+ <span>{inv.customer}</span>
20
+ <span className="text-xs">{inv.dueDate}</span>
21
+ <span>{inv.amount}</span>
22
+ </div>
23
+ ));
24
+ }
25
+ ```
26
+
27
+ ```tsx
28
+ // AFTER — name leads, value anchors right, metadata recedes
29
+ <div className="divide-border divide-y">
30
+ {invoices.map((inv) => (
31
+ <div key={inv.id} className="flex items-center gap-3 px-4 py-3">
32
+ <div className="min-w-0 flex-1">
33
+ <p className="truncate text-base font-medium">{inv.customer}</p>
34
+ <p className="text-muted-foreground text-sm">
35
+ Due {formatDate(inv.dueDate)}
36
+ </p>
37
+ </div>
38
+ <span className="shrink-0 text-right text-base font-semibold tabular-nums">
39
+ {formatCurrency(inv.amount)}
40
+ </span>
41
+ </div>
42
+ ))}
43
+ </div>
44
+ ```
45
+
46
+ Rules at work: `min-w-0 flex-1` + `truncate` so long names never push the
47
+ value out; `shrink-0` + `tabular-nums` so amounts hold a column; rows own
48
+ their padding (`px-4 py-3`), the container owns none.
49
+
50
+ ## 2. Bare stat grid → hero KPI with supporting stats
51
+
52
+ **Failure:** four equal chips. The widget's one question ("how are we
53
+ doing?") has no answer — every number claims the same importance.
54
+
55
+ ```tsx
56
+ // BEFORE — nothing is the headline
57
+ <div className="grid grid-cols-2 gap-2 p-4">
58
+ <div className="text-sm">Revenue: {revenue}</div>
59
+ <div className="text-sm">Orders: {orders}</div>
60
+ <div className="text-sm">Avg: {avg}</div>
61
+ <div className="text-sm">Refunds: {refunds}</div>
62
+ </div>
63
+ ```
64
+
65
+ ```tsx
66
+ // AFTER — one hero, a delta, the rest demoted to a footer strip
67
+ <div className="flex flex-1 flex-col items-center justify-center px-4 py-6">
68
+ <span className="text-muted-foreground text-sm">Revenue this month</span>
69
+ <span className="text-3xl font-semibold tracking-tight tabular-nums">
70
+ {formatCurrency(revenue)}
71
+ </span>
72
+ <span
73
+ className={cn(
74
+ 'mt-1 text-sm font-medium',
75
+ delta >= 0 ? 'text-success-strong' : 'text-destructive',
76
+ )}
77
+ >
78
+ {delta >= 0 ? '+' : ''}
79
+ {delta.toFixed(1)}% vs last month
80
+ </span>
81
+ </div>
82
+
83
+ <div className="flex shrink-0 items-center border-t">
84
+ {[
85
+ { label: 'Orders', value: orders },
86
+ { label: 'Avg order', value: formatCurrency(avg) },
87
+ { label: 'Refunds', value: refunds },
88
+ ].map((stat, i) => (
89
+ <Fragment key={stat.label}>
90
+ {i > 0 && <div className="bg-border h-8 w-px" />}
91
+ <div className="flex flex-1 flex-col items-center py-3">
92
+ <span className="text-lg font-semibold tabular-nums">{stat.value}</span>
93
+ <span className="text-muted-foreground text-xs">{stat.label}</span>
94
+ </div>
95
+ </Fragment>
96
+ ))}
97
+ </div>
98
+ ```
99
+
100
+ (`cn` from `@ekanos/ui/utils`.) Pick the hero by the interview's SEE-outcome,
101
+ not by which number is largest. One `text-3xl` element per widget — two heroes
102
+ is zero heroes.
103
+
104
+ ## 3. Unstyled status → semantic badge or dot
105
+
106
+ **Failure:** status as bare text, or hardcoded palette (`bg-green-500`) that
107
+ breaks in dark mode and off-brand tenants.
108
+
109
+ ```tsx
110
+ // BEFORE
111
+ <span className="text-xs">{sub.status}</span>
112
+ // or
113
+ <span className="rounded bg-green-500 px-1 text-white">{sub.status}</span>
114
+ ```
115
+
116
+ ```tsx
117
+ // AFTER — Badge variants carry the semantics; the map is the single source
118
+ import { Badge } from '@ekanos/ui/badge';
119
+
120
+ const STATUS_VARIANT = {
121
+ active: 'success',
122
+ trialing: 'info',
123
+ past_due: 'warning',
124
+ canceled: 'destructive',
125
+ } as const;
126
+
127
+ <Badge variant={STATUS_VARIANT[sub.status] ?? 'outline'}>
128
+ {STATUS_LABEL[sub.status] ?? sub.status}
129
+ </Badge>;
130
+ ```
131
+
132
+ When a badge is too loud (dense lists), use a dot plus muted text instead:
133
+
134
+ ```tsx
135
+ <span
136
+ className={cn(
137
+ 'h-2.5 w-2.5 shrink-0 rounded-full',
138
+ sub.status === 'active' ? 'bg-success' : 'bg-muted-foreground/40',
139
+ )}
140
+ />
141
+ <span className="text-muted-foreground text-sm">{STATUS_LABEL[sub.status]}</span>
142
+ ```
143
+
144
+ One status treatment per row. A row with a colored badge AND a colored icon
145
+ AND colored money is signalling nothing.
146
+
147
+ ## 4. Cramped card → sizing floors applied
148
+
149
+ **Failure:** `p-2`, `space-y-1`, `text-xs` everywhere, `h-3 w-3` icons. Dense
150
+ is not information-rich; it is unreadable.
151
+
152
+ ```tsx
153
+ // BEFORE
154
+ <div className="space-y-1 p-2">
155
+ {events.map((e) => (
156
+ <div key={e.id} className="flex gap-1 text-xs">
157
+ <Icon name="fa-light fa-bolt" className="h-3 w-3" />
158
+ <span>{e.title}</span>
159
+ </div>
160
+ ))}
161
+ </div>
162
+ ```
163
+
164
+ ```tsx
165
+ // AFTER — floors: rows px-4 py-3, icons h-5 w-5, body text-sm+
166
+ <div className="divide-border divide-y">
167
+ {events.slice(0, 6).map((e) => (
168
+ <div key={e.id} className="flex items-start gap-3 px-4 py-3">
169
+ <span className="bg-muted text-muted-foreground shrink-0 rounded-full p-2">
170
+ <Icon name={eventIcon(e.type)} className="h-5 w-5" />
171
+ </span>
172
+ <div className="min-w-0 flex-1">
173
+ <p className="truncate text-base font-medium">{e.title}</p>
174
+ <p className="text-muted-foreground text-sm">{e.summary}</p>
175
+ </div>
176
+ <span className="text-muted-foreground shrink-0 text-xs">
177
+ {formatTimeAgo(e.at)}
178
+ </span>
179
+ </div>
180
+ ))}
181
+ </div>
182
+ ```
183
+
184
+ If the content no longer fits after applying floors, show fewer items — the
185
+ floors are not negotiable, the item count is.
186
+
187
+ ## 5. Overflowing card → inner scroll + "View all"
188
+
189
+ **Failure:** the list grows the card past its grid slot, or a fixed
190
+ `max-h-[180px]` caps it arbitrarily and clips the last row.
191
+
192
+ ```tsx
193
+ // BEFORE — the card scrolls (or grows); a magic pixel height fights the grid
194
+ <div className="max-h-[180px] overflow-y-auto">{rows}</div>
195
+ ```
196
+
197
+ ```tsx
198
+ // AFTER — the LIST scrolls inside the flex column; the card holds its size
199
+ import { Button } from '@ekanos/ui/button';
200
+
201
+ <div className="flex h-full min-h-0 flex-col">
202
+ <div className="min-h-0 flex-1 overflow-y-auto">
203
+ <div className="divide-border divide-y">{rows}</div>
204
+ </div>
205
+ {items.length > 7 && (
206
+ <div className="shrink-0 border-t px-4 py-2">
207
+ <Button variant="ghost" size="sm" className="text-muted-foreground">
208
+ View all {items.length}
209
+ </Button>
210
+ </div>
211
+ )}
212
+ </div>;
213
+ ```
214
+
215
+ `min-h-0` is the load-bearing class — without it the flex child refuses to
216
+ shrink and nothing scrolls. Test at the sm viewport preset, where the failure
217
+ actually shows.
218
+
219
+ ## 6. Button pile-up → one action + menu
220
+
221
+ **Failure:** two or three buttons per row; the row becomes a toolbar.
222
+
223
+ ```tsx
224
+ // AFTER — one visible action, the rest behind a menu
225
+ import {
226
+ DropdownMenu,
227
+ DropdownMenuContent,
228
+ DropdownMenuItem,
229
+ DropdownMenuTrigger,
230
+ } from '@ekanos/ui/dropdown-menu';
231
+
232
+ <div className="flex shrink-0 items-center gap-2">
233
+ <Button size="sm">Pay now</Button>
234
+ <DropdownMenu>
235
+ <DropdownMenuTrigger
236
+ render={<Button variant="ghost" size="icon" className="h-8 w-8" />}
237
+ >
238
+ <Icon name="fa-light fa-ellipsis-vertical" className="h-4 w-4" />
239
+ </DropdownMenuTrigger>
240
+ <DropdownMenuContent align="end">
241
+ <DropdownMenuItem>View details</DropdownMenuItem>
242
+ <DropdownMenuItem>Payment history</DropdownMenuItem>
243
+ </DropdownMenuContent>
244
+ </DropdownMenu>
245
+ </div>;
246
+ ```
247
+
248
+ The one visible action should be the urgent one; if no action is urgent, all
249
+ of them belong in the menu.
250
+
251
+ ## 7. Blank empty state → designed empty state
252
+
253
+ **Failure:** an empty array renders an empty card, indistinguishable from a
254
+ bug. Empty is a state you design, distinct from error.
255
+
256
+ ```tsx
257
+ // AFTER — icon, one sentence of what appears here, what action produces it
258
+ <div className="flex flex-1 flex-col items-center justify-center gap-3 p-8 text-center">
259
+ <div className="bg-muted flex h-16 w-16 items-center justify-center rounded-full">
260
+ <Icon name="fa-light fa-inbox" className="text-muted-foreground h-8 w-8" />
261
+ </div>
262
+ <p className="text-base font-medium">No invoices yet</p>
263
+ <p className="text-muted-foreground max-w-xs text-sm">
264
+ Invoices appear here as soon as your first one is issued.
265
+ </p>
266
+ </div>
267
+ ```
268
+
269
+ No CTA unless there is a real action the user can take from the dashboard.