@telemetryos/cli 1.7.5 → 1.8.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 (23) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/dist/commands/init.js +11 -0
  3. package/dist/services/generate-application.d.ts +1 -0
  4. package/dist/services/generate-application.js +127 -4
  5. package/dist/utils/validate-project-name.d.ts +19 -0
  6. package/dist/utils/validate-project-name.js +44 -0
  7. package/package.json +2 -2
  8. package/templates/vite-react-typescript/CLAUDE.md +68 -1244
  9. package/templates/vite-react-typescript/_claude/settings.local.json +17 -0
  10. package/templates/vite-react-typescript/_claude/skills/tos-architecture/SKILL.md +313 -0
  11. package/templates/vite-react-typescript/_claude/skills/tos-debugging/SKILL.md +299 -0
  12. package/templates/vite-react-typescript/_claude/skills/tos-media-api/SKILL.md +335 -0
  13. package/templates/vite-react-typescript/_claude/skills/tos-proxy-fetch/SKILL.md +319 -0
  14. package/templates/vite-react-typescript/_claude/skills/tos-render-design/SKILL.md +332 -0
  15. package/templates/vite-react-typescript/_claude/skills/tos-requirements/SKILL.md +252 -0
  16. package/templates/vite-react-typescript/_claude/skills/tos-settings-ui/SKILL.md +636 -0
  17. package/templates/vite-react-typescript/_claude/skills/tos-store-sync/SKILL.md +359 -0
  18. package/templates/vite-react-typescript/_claude/skills/tos-weather-api/SKILL.md +384 -0
  19. package/templates/vite-react-typescript/src/views/Render.css +1 -1
  20. package/templates/vite-react-typescript/src/views/Render.tsx +1 -1
  21. package/templates/vite-react-typescript/src/views/Settings.tsx +2 -2
  22. package/templates/vite-react-typescript/AGENTS.md +0 -7
  23. /package/templates/vite-react-typescript/{gitignore → _gitignore} +0 -0
@@ -0,0 +1,332 @@
1
+ ---
2
+ name: tos-render-design
3
+ description: Design patterns for TelemetryOS Render views. Use when building or reviewing Render view layouts, handling responsive scaling, or ensuring digital signage best practices.
4
+ ---
5
+
6
+ # Render View Design
7
+
8
+ TelemetryOS Render views display on digital signage—TVs, video walls, and displays viewed from a distance with no user interaction. This fundamentally shapes how to design them.
9
+
10
+ > **Note:** The init project already provides base styles in `index.css` (viewport scaling, box-sizing) and `Render.css` (`.render` class with padding, overflow, flexbox). Build on these—don't override them.
11
+
12
+ ## Digital Signage Constraints
13
+
14
+ ### No User Interaction
15
+
16
+ Unless building for kiosk/touchscreen scenarios, assume **no mouse, keyboard, or touch input**:
17
+
18
+ ```css
19
+ /* WRONG - No one will hover */
20
+ .button:hover {
21
+ background: blue;
22
+ }
23
+
24
+ /* WRONG - No one will focus */
25
+ .input:focus {
26
+ outline: 2px solid blue;
27
+ }
28
+ ```
29
+
30
+ Avoid `:hover`, `:focus`, `:active`, and similar interaction pseudo-classes.
31
+
32
+ ### No Scrolling
33
+
34
+ Content **must fit the viewport**. There's no user to scroll:
35
+
36
+ ```css
37
+ /* WRONG - Creates scrollbar no one can use */
38
+ .container {
39
+ overflow-y: scroll;
40
+ }
41
+
42
+ /* WRONG - Content disappears off-screen */
43
+ .content {
44
+ height: 150vh;
45
+ }
46
+ ```
47
+
48
+ ```css
49
+ /* CORRECT - Content contained */
50
+ .container {
51
+ height: 100vh;
52
+ overflow: hidden;
53
+ }
54
+ ```
55
+
56
+ If content might overflow, truncate it or conditionally hide elements—never show a scrollbar.
57
+
58
+ ## UI Scale Hooks
59
+
60
+ Displays range from tablets to 8K video walls. Standard CSS pixels create inconsistent sizing. The SDK provides hooks that redefine `rem` as viewport-relative:
61
+
62
+ ### useUiScaleToSetRem(uiScale)
63
+
64
+ Sets the document's root font-size based on viewport. **Call once in your Render view:**
65
+
66
+ ```typescript
67
+ import { useUiScaleToSetRem } from '@telemetryos/sdk/react'
68
+ import { useUiScaleStoreState } from '../hooks/store'
69
+
70
+ export function Render() {
71
+ const [_isLoading, uiScale] = useUiScaleStoreState()
72
+ useUiScaleToSetRem(uiScale)
73
+
74
+ return <div className="content">...</div>
75
+ }
76
+ ```
77
+
78
+ **How it works:**
79
+ - At scale 1: `1rem` = 1% of viewport's longest dimension
80
+ - At scale 2: `1rem` = 2% of viewport's longest dimension
81
+ - A 2rem font occupies identical screen percentage on Full HD and 4K
82
+
83
+ ### useUiAspectRatio()
84
+
85
+ Returns current aspect ratio, updating on resize:
86
+
87
+ ```typescript
88
+ import { useUiAspectRatio } from '@telemetryos/sdk/react'
89
+
90
+ export function Render() {
91
+ const aspectRatio = useUiAspectRatio()
92
+
93
+ // > 1 = landscape, < 1 = portrait, = 1 = square
94
+ const isPortrait = aspectRatio < 1
95
+
96
+ return (
97
+ <div className={isPortrait ? 'portrait-layout' : 'landscape-layout'}>
98
+ ...
99
+ </div>
100
+ )
101
+ }
102
+ ```
103
+
104
+ ## Best Practices
105
+
106
+ ### Use rem for Everything
107
+
108
+ All sizing should use `rem` to scale with the UI scale setting:
109
+
110
+ ```css
111
+ /* CORRECT - Scales with viewport */
112
+ .title {
113
+ font-size: 4rem;
114
+ margin-bottom: 1rem;
115
+ }
116
+
117
+ .card {
118
+ padding: 2rem;
119
+ border-radius: 0.5rem;
120
+ }
121
+ ```
122
+
123
+ ```css
124
+ /* WRONG - Fixed pixels don't scale */
125
+ .title {
126
+ font-size: 48px;
127
+ margin-bottom: 12px;
128
+ }
129
+ ```
130
+
131
+ ### Title Safe Zone
132
+
133
+ The init project's `.render` class already applies ~3rem padding from screen edges (SMPTE ST 2046-1 standard for avoiding bezel cutoff). Keep this padding when building your layout.
134
+
135
+ ### Minimum Text Size
136
+
137
+ Text should be no smaller than ~2rem for comfortable viewing at typical distances (approximately 4% of screen height):
138
+
139
+ ```css
140
+ .body-text {
141
+ font-size: 2rem; /* Minimum readable size */
142
+ }
143
+
144
+ .headline {
145
+ font-size: 4rem;
146
+ }
147
+
148
+ .small-label {
149
+ font-size: 1.5rem; /* Use sparingly */
150
+ }
151
+ ```
152
+
153
+ ### Constrain Layouts
154
+
155
+ The init project's `index.css` and `.render` class already set up the base layout with `overflow: hidden` and flexbox. When adding child elements, use `min-height: 0` or `min-width: 0` on flex children to allow them to shrink:
156
+
157
+ ```css
158
+ .my-content {
159
+ flex: 1;
160
+ min-height: 0; /* Allows flex children to shrink below content size */
161
+ }
162
+ ```
163
+
164
+ ### Text Truncation
165
+
166
+ When text might overflow, truncate gracefully:
167
+
168
+ ```css
169
+ .title {
170
+ white-space: nowrap;
171
+ overflow: hidden;
172
+ text-overflow: ellipsis;
173
+ }
174
+
175
+ /* Multi-line truncation */
176
+ .description {
177
+ display: -webkit-box;
178
+ -webkit-line-clamp: 3;
179
+ -webkit-box-orient: vertical;
180
+ overflow: hidden;
181
+ }
182
+ ```
183
+
184
+ ### Adaptive Content
185
+
186
+ Use `useUiAspectRatio()` to adapt layouts for portrait vs landscape:
187
+
188
+ ```typescript
189
+ function Dashboard() {
190
+ const aspectRatio = useUiAspectRatio()
191
+ const isPortrait = aspectRatio < 1
192
+
193
+ return (
194
+ <div className={`dashboard ${isPortrait ? 'dashboard--portrait' : ''}`}>
195
+ <PrimaryContent />
196
+ {/* Hide sidebar in portrait mode */}
197
+ {!isPortrait && <Sidebar />}
198
+ </div>
199
+ )
200
+ }
201
+ ```
202
+
203
+ ## Complete Example
204
+
205
+ ```typescript
206
+ // Render.tsx - Building on the init project's .render class
207
+ import { useUiScaleToSetRem, useUiAspectRatio } from '@telemetryos/sdk/react'
208
+ import { useUiScaleStoreState } from '../hooks/store'
209
+ import './Render.css'
210
+
211
+ export function Render() {
212
+ const [isLoading, uiScale] = useUiScaleStoreState()
213
+ const aspectRatio = useUiAspectRatio()
214
+
215
+ useUiScaleToSetRem(uiScale)
216
+
217
+ if (isLoading) return null
218
+
219
+ const isPortrait = aspectRatio < 1
220
+
221
+ return (
222
+ <div className="render">
223
+ <header className="render__header">
224
+ <h1 className="render__title">Dashboard</h1>
225
+ </header>
226
+
227
+ <main className={`render__content ${isPortrait ? 'render__content--portrait' : ''}`}>
228
+ <div className="render__primary">
229
+ <MainDisplay />
230
+ </div>
231
+
232
+ {!isPortrait && (
233
+ <aside className="render__sidebar">
234
+ <SecondaryInfo />
235
+ </aside>
236
+ )}
237
+ </main>
238
+ </div>
239
+ )
240
+ }
241
+ ```
242
+
243
+ ```css
244
+ /* Render.css - Add to existing styles, don't override .render base */
245
+ .render__header {
246
+ flex-shrink: 0;
247
+ margin-bottom: 2rem;
248
+ }
249
+
250
+ .render__title {
251
+ font-size: 4rem;
252
+ margin: 0;
253
+ white-space: nowrap;
254
+ overflow: hidden;
255
+ text-overflow: ellipsis;
256
+ }
257
+
258
+ .render__content {
259
+ flex: 1;
260
+ min-height: 0;
261
+ display: flex;
262
+ gap: 2rem;
263
+ }
264
+
265
+ .render__content--portrait {
266
+ flex-direction: column;
267
+ }
268
+
269
+ .render__primary {
270
+ flex: 1;
271
+ min-width: 0;
272
+ min-height: 0;
273
+ }
274
+
275
+ .render__sidebar {
276
+ width: 25rem;
277
+ flex-shrink: 0;
278
+ }
279
+ ```
280
+
281
+ ## Store Hook for UI Scale
282
+
283
+ Create a store hook to let admins adjust the UI scale:
284
+
285
+ ```typescript
286
+ // hooks/store.ts
287
+ import { createUseInstanceStoreState } from '@telemetryos/sdk/react'
288
+
289
+ export const useUiScaleStoreState = createUseInstanceStoreState<number>('ui-scale', 1)
290
+ ```
291
+
292
+ ```typescript
293
+ // Settings.tsx - Add slider control
294
+ import { SettingsSliderFrame, SettingsField, SettingsLabel } from '@telemetryos/sdk/react'
295
+ import { useUiScaleStoreState } from '../hooks/store'
296
+
297
+ export function Settings() {
298
+ // Pass 0 debounce for instant slider updates
299
+ const [isLoading, uiScale, setUiScale] = useUiScaleStoreState(0)
300
+
301
+ return (
302
+ <SettingsField>
303
+ <SettingsLabel>UI Scale</SettingsLabel>
304
+ <SettingsSliderFrame>
305
+ <input
306
+ type="range"
307
+ min={1}
308
+ max={3}
309
+ step={0.01}
310
+ disabled={isLoading}
311
+ value={uiScale}
312
+ onChange={(e) => setUiScale(parseFloat(e.target.value))}
313
+ />
314
+ <span>{uiScale}x</span>
315
+ </SettingsSliderFrame>
316
+ </SettingsField>
317
+ )
318
+ }
319
+ ```
320
+
321
+ ## Common Mistakes
322
+
323
+ | Mistake | Problem | Fix |
324
+ |---------|---------|-----|
325
+ | Using `px` units | Won't scale across resolutions | Use `rem` everywhere |
326
+ | Adding `:hover` styles | No mouse on digital signage | Remove interaction states |
327
+ | Using `overflow: scroll` | No user to scroll | Use `overflow: hidden`, truncate content |
328
+ | Fixed heights in `px` | Breaks on different aspect ratios | Use `vh`, `%`, or flex |
329
+ | Forgetting `useUiScaleToSetRem()` | `rem` units won't scale properly | Call it once in Render view with the uiScale from `useUiScaleStoreState()` |
330
+ | Text below 2rem | Unreadable from viewing distance | Minimum 2rem for body text |
331
+ | Removing `.render` padding | Content cut off by bezels | Keep the ~3rem padding from init project |
332
+ | Overriding `index.css` base styles | Breaks viewport scaling | Add new styles, don't modify base setup |
@@ -0,0 +1,252 @@
1
+ ---
2
+ name: tos-requirements
3
+ description: Gather requirements for a new TelemetryOS app. Use at the START of any new app project to understand the developer's vision, render layout design, configuration fields, data sources, and implementation plan before writing code.
4
+ ---
5
+
6
+ # TelemetryOS App Requirements Gathering
7
+
8
+ Use this skill at the START of any new TelemetryOS application project. Gather complete requirements before writing any code to ensure a successful "one-shot" implementation.
9
+
10
+ ## Requirements Interview
11
+
12
+ **IMPORTANT: This is a conversation, not a survey.** Ask questions one phase at a time. Wait for answers before proceeding. Use earlier answers to skip irrelevant questions.
13
+
14
+ ### Phase 1: Start with Vision
15
+
16
+ Ask ONE question to start:
17
+
18
+ > "What app do you want to build? Give me a quick description."
19
+
20
+ That's it. Wait for their answer. Their response will tell you:
21
+ - What data sources they need (weather? media? external API?)
22
+ - What the layout probably looks like
23
+ - What settings make sense
24
+
25
+ ### Phase 2: Clarify Based on Their Answer
26
+
27
+ Based on what they described, ask only the relevant follow-ups:
28
+
29
+ **If they mentioned specific data** (weather, stocks, social media, etc.):
30
+ - "Do you have an API in mind, or should I suggest one?"
31
+ - "How often should it refresh?"
32
+
33
+ **If they mentioned media/images/video**:
34
+ - "Will these come from the TelemetryOS media library, or external URLs?"
35
+
36
+ **If the layout isn't clear**:
37
+ - "Quick layout check: fullscreen content, or split/grid layout?"
38
+
39
+ **If they gave a detailed description**: Skip to Phase 3 - you probably have enough.
40
+
41
+ ### Phase 3: Fill Gaps
42
+
43
+ Only ask about things that aren't obvious from their description:
44
+
45
+ - Settings they'd want to configure (if not clear)
46
+ - Any specific constraints (refresh rates, data limits)
47
+ - Edge cases that matter for their use case
48
+
49
+ **Don't ask about:**
50
+ - Categories that don't apply to their app
51
+ - Settings with obvious defaults
52
+ - Technical details you can infer
53
+
54
+ ### Reference: Layout Types
55
+
56
+ If you need to clarify layout:
57
+ - **Single panel** - fullscreen content
58
+ - **Split layout** - sidebar + main area
59
+ - **Grid** - multiple items in rows/columns
60
+ - **Fullscreen media** - image/video player
61
+
62
+ ### Reference: Store Keys
63
+
64
+ Settings allow admins to configure the app. Use these categories to identify what settings make sense for their app (don't ask about every category).
65
+
66
+ #### Categories (consult as needed)
67
+
68
+ **Display Settings** - Visual appearance and layout
69
+ - Colors, fonts, background styles
70
+ - Layout options (columns, alignment, spacing)
71
+ - Show/hide toggles for UI elements
72
+ - Animation preferences
73
+
74
+ > Digital signage typically uses dark backgrounds (better contrast on TVs, reduces glare). Don't ask about light/dark "mode" unless the developer brings it up.
75
+
76
+ **Data Configuration** - How the app fetches and processes data
77
+ - API keys and credentials
78
+ - Endpoint URLs
79
+ - Refresh intervals
80
+ - Data limits (max items, page size)
81
+
82
+ **Content Selection** - What content to display
83
+ - Media folder IDs or tags
84
+ - Playlist/item selection
85
+ - Content filtering rules
86
+ - Sort order preferences
87
+
88
+ **Localization** - Regional and format preferences
89
+ - Timezone
90
+ - Units (imperial/metric, currency)
91
+ - Date/time formats
92
+ - Language/locale
93
+
94
+ #### Store Scope Rules
95
+
96
+ **Default to `instance`** - most settings are instance-scoped. Only use other scopes when the setting clearly fits the patterns below.
97
+
98
+ **Use `application` scope for:**
99
+ - API keys and credentials (shared cost, single billing)
100
+ - Account-wide branding (company logo URL, brand colors)
101
+ - License keys or subscription identifiers
102
+ - Shared service endpoints configured once per account
103
+
104
+ **Use `instance` scope for everything else:**
105
+ - Content selection (what to display)
106
+ - Layout options (how to display it)
107
+ - Refresh intervals and timing
108
+ - Localization (timezone, units, language)
109
+ - Visual preferences (colors, fonts, backgrounds)
110
+ - Filters, sort order, display limits
111
+
112
+ **Quick Reference:**
113
+ | Scope | Synced? | Shared Across | Common Use |
114
+ |-------|---------|---------------|------------|
115
+ | `instance` | Yes | Same instance on all devices | Per-widget config |
116
+ | `application` | Yes | All instances in account | API keys, credentials |
117
+
118
+ #### Capture Each Store Key
119
+
120
+ For each setting identified, record:
121
+
122
+ | Key | Category | Scope | Type | Default | Constraints | Required? |
123
+ |-----|----------|-------|------|---------|-------------|-----------|
124
+ | city | Localization | instance | string | '' | min 2 chars | Yes |
125
+ | apiKey | Data Config | application | string | '' | - | Yes |
126
+ | units | Localization | instance | 'imperial' \| 'metric' | 'imperial' | enum | Yes |
127
+ | refreshInterval | Data Config | instance | number | 30 | 10-300 | No |
128
+
129
+ #### Questions to Ask (spread across conversation)
130
+
131
+ After understanding the core app, circle back to fill in settings details:
132
+ - **What should admins be able to configure?** (prompt with relevant categories)
133
+ - **What are sensible defaults?** (app should work with minimal config)
134
+ - **Are there validation rules or constraints?** (min/max, patterns, enums)
135
+ - **Which settings are required vs optional?**
136
+
137
+ Ask these as follow-ups in later turns, not all upfront. Scope is typically inferred from the rules above—only ask about scope if a setting doesn't clearly fit.
138
+
139
+ ### Reference: Data Sources
140
+
141
+ Consult this when you need to understand their data needs (don't ask about all of these):
142
+
143
+ **TelemetryOS Platform APIs:**
144
+ - `media()` - User-uploaded images/videos from media library
145
+ - `weather()` - Weather data (current, hourly, daily forecasts)
146
+ - `applications()` - Embedding other TOS apps
147
+
148
+ **External APIs:**
149
+ - Use `proxy().fetch()` for external APIs (handles CORS)
150
+ - Note: authentication method (API key, OAuth, none)
151
+ - Note: rate limits if known
152
+
153
+ **Refresh patterns:**
154
+ - Timer-based (every N seconds/minutes)
155
+ - Event-based (on user action)
156
+ - Most apps use 30-60 second refresh for live data
157
+
158
+ ### Implementation Checklist
159
+
160
+ After gathering requirements, generate:
161
+
162
+ #### Store Hooks (hooks/store.ts)
163
+
164
+ ```typescript
165
+ // Instance-scoped keys (most common - per-widget config)
166
+ export const use[Key]State = createUseInstanceStoreState<Type>('key', default)
167
+ // Usage: const [loading, value, setValue] = use[Key]State()
168
+
169
+ // Application-scoped keys (shared across all instances)
170
+ export const use[Key]State = createUseApplicationStoreState<Type>('key', default)
171
+ // Usage: const [loading, value, setValue] = use[Key]State()
172
+
173
+ // Device-scoped keys (stays on device, Render only)
174
+ export const use[Key]State = createUseDeviceStoreState<Type>('key', default)
175
+ // Usage: const [loading, value, setValue] = use[Key]State()
176
+ ```
177
+
178
+ #### Settings UI Components
179
+
180
+ List each Settings control needed:
181
+ - [ ] Text input for X
182
+ - [ ] Dropdown for Y
183
+ - [ ] Slider for Z
184
+ - [ ] Toggle for W
185
+
186
+ #### Render View Structure
187
+
188
+ Describe the component hierarchy:
189
+ ```
190
+ Render
191
+ ├── Header (title, logo)
192
+ ├── MainContent
193
+ │ └── DataDisplay
194
+ └── Footer (timestamp, refresh indicator)
195
+ ```
196
+
197
+ #### SDK APIs Required
198
+
199
+ - [ ] createUseInstanceStoreState - Settings ↔ Render sync
200
+ - [ ] proxy().fetch() - External API calls
201
+ - [ ] weather() - Weather data
202
+ - [ ] media() - Media library
203
+
204
+ ## Output Format
205
+
206
+ After gathering requirements, provide a structured summary:
207
+
208
+ ```markdown
209
+ # [App Name] Requirements
210
+
211
+ ## Vision
212
+ [One sentence description]
213
+
214
+ ## Render View
215
+ - Layout: [single/split/grid/fullscreen]
216
+ - Content: [description]
217
+ - Refresh: [interval or trigger]
218
+
219
+ ## Store Keys
220
+ | Key | Category | Scope | Type | Default | UI Component |
221
+ |-----|----------|-------|------|---------|--------------|
222
+ | ... | ... | instance/application/device | ... | ... | ... |
223
+
224
+ ## Data Sources
225
+ - Internal: [list]
226
+ - External: [list with endpoints]
227
+
228
+ ## Implementation Plan
229
+ 1. Create store hooks
230
+ 2. Build Settings UI
231
+ 3. Build Render view
232
+ 4. Add data fetching
233
+ 5. Test and polish
234
+ ```
235
+
236
+ ## Tips for Success
237
+
238
+ 1. **Don't skip requirements** - Incomplete requirements lead to rework
239
+ 2. **Validate early** - Confirm understanding before coding
240
+ 3. **Start simple** - MVP first, then add features
241
+ 4. **Use SDK hooks** - `createUseInstanceStoreState` for all store keys
242
+ 5. **Follow patterns** - Match existing Settings UI components exactly
243
+
244
+ ## Next Steps
245
+
246
+ After gathering requirements, use these skills to implement:
247
+
248
+ - **`tos-store-sync`** - Create store hooks from the Store Keys table
249
+ - **`tos-settings-ui`** - Build the Settings UI components
250
+ - **`tos-proxy-fetch`** - Implement external API calls
251
+ - **`tos-weather-api`** - Integrate weather data (if needed)
252
+ - **`tos-media-api`** - Access media library (if needed)