@askrjs/cli 0.0.2 → 0.0.4

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 (310) hide show
  1. package/README.md +46 -7
  2. package/dist/add.d.ts +16 -0
  3. package/dist/add.js +558 -1
  4. package/dist/cli.d.ts +5 -0
  5. package/dist/cli.js +43 -16
  6. package/dist/create.d.ts +5 -0
  7. package/dist/create.js +104 -26
  8. package/dist/directory-swap-DWoHtx7C.js +35 -0
  9. package/dist/discovery-Difb7Y4G.js +323 -0
  10. package/dist/generate.d.ts +5 -0
  11. package/dist/generate.js +461 -0
  12. package/dist/is-direct-execution-Cdlr-ZUl.js +0 -2
  13. package/dist/openapi.d.ts +25 -0
  14. package/dist/openapi.js +49 -16
  15. package/dist/planner-VAj7qlxr.js +285 -0
  16. package/dist/range-YUs9eimn.js +125 -0
  17. package/dist/registry-CyrRHRG5.js +209 -0
  18. package/dist/skills/askr-dashboard-charts/SKILL.md +61 -44
  19. package/dist/skills/askr-dashboard-charts/agents/openai.yaml +2 -2
  20. package/dist/skills/askr-ssr-ssg/SKILL.md +3 -1
  21. package/dist/{skills-Bh0mEhIx.js → skills-B7CbWur9.js} +89 -26
  22. package/dist/skills.d.ts +18 -0
  23. package/dist/skills.js +1 -1
  24. package/dist/specification-DXnDOC-0.js +49 -0
  25. package/dist/ssg-config.d.ts +41 -0
  26. package/dist/ssg-config.js +1 -0
  27. package/dist/ssg.d.ts +61 -0
  28. package/dist/ssg.js +360 -24
  29. package/dist/templates/full-stack/AGENTS.md +0 -1
  30. package/dist/templates/full-stack/README.md +0 -1
  31. package/dist/templates/full-stack/index.html +0 -1
  32. package/dist/templates/full-stack/package.json +5 -3
  33. package/dist/templates/full-stack/server.ts +5 -9
  34. package/dist/templates/full-stack/src/action-authorizations.ts +2 -2
  35. package/dist/templates/full-stack/src/actions/create-message.ts +5 -5
  36. package/dist/templates/full-stack/src/i18n.ts +4 -4
  37. package/dist/templates/full-stack/src/main.tsx +4 -5
  38. package/dist/templates/full-stack/src/pages/home.tsx +5 -5
  39. package/dist/templates/full-stack/src/pages/layout.tsx +15 -5
  40. package/dist/templates/full-stack/src/pages/not-found.tsx +5 -2
  41. package/dist/templates/full-stack/src/routes.tsx +11 -12
  42. package/dist/templates/full-stack/src/schemas.ts +1 -2
  43. package/dist/templates/full-stack/src/server/action-registry.ts +5 -10
  44. package/dist/templates/full-stack/src/server/actions/create-message.ts +4 -4
  45. package/dist/templates/full-stack/src/server/app.ts +67 -57
  46. package/dist/templates/full-stack/src/server/dependencies.ts +10 -10
  47. package/dist/templates/full-stack/src/server/entry-server.ts +3 -3
  48. package/dist/templates/full-stack/src/telemetry.ts +1 -2
  49. package/dist/templates/full-stack/tests/actions/create-message.test.ts +7 -8
  50. package/dist/templates/full-stack/tsconfig.json +1 -2
  51. package/dist/templates/full-stack/vite.config.ts +5 -6
  52. package/dist/templates/spa/AGENTS.md +1 -1
  53. package/dist/templates/spa/package.json +3 -4
  54. package/dist/templates/spa/src/adapters/operations-client.ts +7 -2
  55. package/dist/templates/spa/src/pages/app/_routes.tsx +2 -0
  56. package/dist/templates/spa/src/pages/app/admin-home.tsx +21 -3
  57. package/dist/templates/spa/src/pages/public/_routes.tsx +2 -0
  58. package/dist/templates/spa/src/styles.css +1 -1
  59. package/dist/templates/spa/tsconfig.json +2 -1
  60. package/dist/templates/spa/vitest.config.ts +0 -4
  61. package/dist/templates/ssg/README.md +1 -1
  62. package/dist/templates/ssg/package.json +1 -1
  63. package/dist/templates/ssg/ssg.config.ts +7 -0
  64. package/dist/templates/ssg/tests/ssg-config.test.ts +5 -3
  65. package/dist/templates/ssg/tsconfig.json +2 -1
  66. package/dist/templates/ssg/vitest.config.ts +0 -4
  67. package/dist/templates/ssr/package.json +2 -2
  68. package/dist/templates/ssr/server.ts +4 -10
  69. package/dist/templates/ssr/src/entry-server.tsx +6 -4
  70. package/dist/templates/startkit/package.json +2 -2
  71. package/dist/templates/startkit/src/lib/mock-data.ts +17 -39
  72. package/dist/templates/startkit/tsconfig.json +2 -1
  73. package/dist/update.d.ts +41 -0
  74. package/dist/update.js +282 -2
  75. package/dist/writer-D8qe_7ud.js +226 -0
  76. package/package.json +23 -22
  77. package/dist/add-DqhqRzBQ.js +0 -555
  78. package/dist/add-DqhqRzBQ.js.map +0 -1
  79. package/dist/cli.js.map +0 -1
  80. package/dist/create.js.map +0 -1
  81. package/dist/is-direct-execution-Cdlr-ZUl.js.map +0 -1
  82. package/dist/openapi.js.map +0 -1
  83. package/dist/skills-Bh0mEhIx.js.map +0 -1
  84. package/dist/ssg.js.map +0 -1
  85. package/dist/templates/full-stack/src/vite-server.d.ts +0 -6
  86. package/dist/templates/spa/src/styles.d.ts +0 -1
  87. package/dist/templates/ssg/src/vite-env.d.ts +0 -5
  88. package/dist/templates/ssr/src/vite-server.d.ts +0 -7
  89. package/dist/templates/startkit/src/vite-env.d.ts +0 -5
  90. package/dist/update-DyW1na5V.js +0 -1140
  91. package/dist/update-DyW1na5V.js.map +0 -1
  92. package/skills/askr-accessibility/SKILL.md +0 -68
  93. package/skills/askr-accessibility/agents/openai.yaml +0 -4
  94. package/skills/askr-agent-execution/SKILL.md +0 -85
  95. package/skills/askr-agent-execution/agents/openai.yaml +0 -4
  96. package/skills/askr-agent-workflows/SKILL.md +0 -57
  97. package/skills/askr-agent-workflows/agents/openai.yaml +0 -4
  98. package/skills/askr-api-integration/SKILL.md +0 -79
  99. package/skills/askr-api-integration/agents/openai.yaml +0 -4
  100. package/skills/askr-app-builder/SKILL.md +0 -99
  101. package/skills/askr-app-builder/agents/openai.yaml +0 -4
  102. package/skills/askr-auth-access/SKILL.md +0 -74
  103. package/skills/askr-auth-access/agents/openai.yaml +0 -4
  104. package/skills/askr-cli-vite/SKILL.md +0 -76
  105. package/skills/askr-cli-vite/agents/openai.yaml +0 -4
  106. package/skills/askr-dashboard-charts/SKILL.md +0 -82
  107. package/skills/askr-dashboard-charts/agents/openai.yaml +0 -4
  108. package/skills/askr-design-system/SKILL.md +0 -86
  109. package/skills/askr-design-system/agents/openai.yaml +0 -4
  110. package/skills/askr-env-config/SKILL.md +0 -74
  111. package/skills/askr-env-config/agents/openai.yaml +0 -4
  112. package/skills/askr-error-loading-empty/SKILL.md +0 -89
  113. package/skills/askr-error-loading-empty/agents/openai.yaml +0 -4
  114. package/skills/askr-file-upload-artifacts/SKILL.md +0 -77
  115. package/skills/askr-file-upload-artifacts/agents/openai.yaml +0 -4
  116. package/skills/askr-forms-tables-crud/SKILL.md +0 -98
  117. package/skills/askr-forms-tables-crud/agents/openai.yaml +0 -4
  118. package/skills/askr-mental-model/SKILL.md +0 -109
  119. package/skills/askr-mental-model/agents/openai.yaml +0 -4
  120. package/skills/askr-migration-react/SKILL.md +0 -81
  121. package/skills/askr-migration-react/agents/openai.yaml +0 -4
  122. package/skills/askr-observability-debugging/SKILL.md +0 -83
  123. package/skills/askr-observability-debugging/agents/openai.yaml +0 -4
  124. package/skills/askr-project-structure/SKILL.md +0 -79
  125. package/skills/askr-project-structure/agents/openai.yaml +0 -4
  126. package/skills/askr-query-mutation/SKILL.md +0 -115
  127. package/skills/askr-query-mutation/agents/openai.yaml +0 -4
  128. package/skills/askr-realtime-streaming/SKILL.md +0 -76
  129. package/skills/askr-realtime-streaming/agents/openai.yaml +0 -4
  130. package/skills/askr-resources-data/SKILL.md +0 -100
  131. package/skills/askr-resources-data/agents/openai.yaml +0 -4
  132. package/skills/askr-routing-layouts/SKILL.md +0 -107
  133. package/skills/askr-routing-layouts/agents/openai.yaml +0 -4
  134. package/skills/askr-runtime-reactivity/SKILL.md +0 -92
  135. package/skills/askr-runtime-reactivity/agents/openai.yaml +0 -4
  136. package/skills/askr-ssr-ssg/SKILL.md +0 -76
  137. package/skills/askr-ssr-ssg/agents/openai.yaml +0 -4
  138. package/skills/askr-testing-determinism/SKILL.md +0 -75
  139. package/skills/askr-testing-determinism/agents/openai.yaml +0 -4
  140. package/skills/askr-theming/SKILL.md +0 -86
  141. package/skills/askr-theming/agents/openai.yaml +0 -4
  142. package/skills/askr-ui-composition/SKILL.md +0 -92
  143. package/skills/askr-ui-composition/agents/openai.yaml +0 -4
  144. package/templates/full-stack/AGENTS.md +0 -12
  145. package/templates/full-stack/README.md +0 -18
  146. package/templates/full-stack/gitignore.template +0 -4
  147. package/templates/full-stack/index.html +0 -14
  148. package/templates/full-stack/package.json +0 -37
  149. package/templates/full-stack/server.ts +0 -13
  150. package/templates/full-stack/src/action-authorizations.ts +0 -10
  151. package/templates/full-stack/src/actions/create-message.ts +0 -10
  152. package/templates/full-stack/src/i18n.ts +0 -8
  153. package/templates/full-stack/src/main.tsx +0 -12
  154. package/templates/full-stack/src/pages/home.tsx +0 -25
  155. package/templates/full-stack/src/pages/layout.tsx +0 -33
  156. package/templates/full-stack/src/pages/not-found.tsx +0 -4
  157. package/templates/full-stack/src/routes.tsx +0 -20
  158. package/templates/full-stack/src/schemas.ts +0 -11
  159. package/templates/full-stack/src/server/action-registry.ts +0 -12
  160. package/templates/full-stack/src/server/actions/create-message.ts +0 -14
  161. package/templates/full-stack/src/server/app.ts +0 -66
  162. package/templates/full-stack/src/server/dependencies.ts +0 -55
  163. package/templates/full-stack/src/server/entry-server.ts +0 -8
  164. package/templates/full-stack/src/telemetry.ts +0 -4
  165. package/templates/full-stack/src/vite-server.d.ts +0 -6
  166. package/templates/full-stack/tests/actions/create-message.test.ts +0 -13
  167. package/templates/full-stack/tsconfig.json +0 -16
  168. package/templates/full-stack/vite.config.ts +0 -11
  169. package/templates/spa/AGENTS.md +0 -60
  170. package/templates/spa/README.md +0 -96
  171. package/templates/spa/gitignore.template +0 -16
  172. package/templates/spa/index.html +0 -13
  173. package/templates/spa/package.json +0 -35
  174. package/templates/spa/src/adapters/operations-client.ts +0 -90
  175. package/templates/spa/src/components/shared/metric-card.tsx +0 -27
  176. package/templates/spa/src/components/shared/status-badge.tsx +0 -24
  177. package/templates/spa/src/features/operations/operations.query.ts +0 -9
  178. package/templates/spa/src/main.tsx +0 -10
  179. package/templates/spa/src/pages/_layout.tsx +0 -9
  180. package/templates/spa/src/pages/_routes.tsx +0 -27
  181. package/templates/spa/src/pages/app/_layout.tsx +0 -94
  182. package/templates/spa/src/pages/app/_routes.tsx +0 -10
  183. package/templates/spa/src/pages/app/admin-home.tsx +0 -152
  184. package/templates/spa/src/pages/app/agent-runs.tsx +0 -93
  185. package/templates/spa/src/pages/app/settings.tsx +0 -91
  186. package/templates/spa/src/pages/auth/_layout.tsx +0 -38
  187. package/templates/spa/src/pages/auth/_routes.tsx +0 -6
  188. package/templates/spa/src/pages/auth/login.tsx +0 -63
  189. package/templates/spa/src/pages/not-found.tsx +0 -22
  190. package/templates/spa/src/pages/public/_layout.tsx +0 -47
  191. package/templates/spa/src/pages/public/_routes.tsx +0 -6
  192. package/templates/spa/src/pages/public/home.tsx +0 -143
  193. package/templates/spa/src/shared/format.ts +0 -12
  194. package/templates/spa/src/shared/navigation.ts +0 -15
  195. package/templates/spa/src/styles/components.css +0 -49
  196. package/templates/spa/src/styles/layout.css +0 -192
  197. package/templates/spa/src/styles/reset.css +0 -44
  198. package/templates/spa/src/styles/theme.css +0 -57
  199. package/templates/spa/src/styles/tokens.css +0 -20
  200. package/templates/spa/src/styles.css +0 -7
  201. package/templates/spa/src/styles.d.ts +0 -1
  202. package/templates/spa/tests/app.test.tsx +0 -37
  203. package/templates/spa/tests/components/shared.test.tsx +0 -12
  204. package/templates/spa/tests/resources.test.ts +0 -22
  205. package/templates/spa/tsconfig.json +0 -20
  206. package/templates/spa/tsconfig.node.json +0 -10
  207. package/templates/spa/vite.config.ts +0 -24
  208. package/templates/spa/vitest.config.ts +0 -17
  209. package/templates/ssg/AGENTS.md +0 -55
  210. package/templates/ssg/README.md +0 -39
  211. package/templates/ssg/gitignore.template +0 -17
  212. package/templates/ssg/index.html +0 -13
  213. package/templates/ssg/package.json +0 -35
  214. package/templates/ssg/src/app.tsx +0 -14
  215. package/templates/ssg/src/components/badge.tsx +0 -3
  216. package/templates/ssg/src/components/counter.tsx +0 -30
  217. package/templates/ssg/src/components/site-shell.tsx +0 -111
  218. package/templates/ssg/src/jsx.d.ts +0 -23
  219. package/templates/ssg/src/main.tsx +0 -8
  220. package/templates/ssg/src/pages/about.tsx +0 -82
  221. package/templates/ssg/src/pages/content.tsx +0 -63
  222. package/templates/ssg/src/pages/example.tsx +0 -97
  223. package/templates/ssg/src/pages/home.tsx +0 -51
  224. package/templates/ssg/src/resources/user.ts +0 -15
  225. package/templates/ssg/src/routes.tsx +0 -15
  226. package/templates/ssg/src/styles.css +0 -89
  227. package/templates/ssg/src/vite-env.d.ts +0 -5
  228. package/templates/ssg/ssg.config.ts +0 -34
  229. package/templates/ssg/tests/app.test.tsx +0 -19
  230. package/templates/ssg/tests/components/counter.test.tsx +0 -18
  231. package/templates/ssg/tests/resources.test.ts +0 -11
  232. package/templates/ssg/tests/ssg-config.test.ts +0 -11
  233. package/templates/ssg/tsconfig.json +0 -20
  234. package/templates/ssg/tsconfig.node.json +0 -10
  235. package/templates/ssg/vite.config.ts +0 -24
  236. package/templates/ssg/vitest.config.ts +0 -17
  237. package/templates/ssr/AGENTS.md +0 -28
  238. package/templates/ssr/README.md +0 -38
  239. package/templates/ssr/gitignore.template +0 -16
  240. package/templates/ssr/index.html +0 -13
  241. package/templates/ssr/package.json +0 -34
  242. package/templates/ssr/server.ts +0 -16
  243. package/templates/ssr/src/app.tsx +0 -21
  244. package/templates/ssr/src/components/counter.tsx +0 -26
  245. package/templates/ssr/src/entry-server.tsx +0 -10
  246. package/templates/ssr/src/main.tsx +0 -11
  247. package/templates/ssr/src/pages/about.tsx +0 -75
  248. package/templates/ssr/src/pages/example.tsx +0 -113
  249. package/templates/ssr/src/pages/home.tsx +0 -56
  250. package/templates/ssr/src/resources/user.ts +0 -15
  251. package/templates/ssr/src/routes.tsx +0 -13
  252. package/templates/ssr/src/styles.css +0 -148
  253. package/templates/ssr/src/vite-server.d.ts +0 -7
  254. package/templates/ssr/tests/app.test.tsx +0 -24
  255. package/templates/ssr/tests/components/counter.test.tsx +0 -13
  256. package/templates/ssr/tests/resources.test.ts +0 -11
  257. package/templates/ssr/tsconfig.json +0 -22
  258. package/templates/ssr/tsconfig.node.json +0 -10
  259. package/templates/ssr/tsconfig.server.json +0 -15
  260. package/templates/ssr/vite.config.ts +0 -25
  261. package/templates/ssr/vitest.config.ts +0 -13
  262. package/templates/startkit/AGENTS.md +0 -77
  263. package/templates/startkit/README.md +0 -192
  264. package/templates/startkit/gitignore.template +0 -16
  265. package/templates/startkit/index.html +0 -13
  266. package/templates/startkit/package.json +0 -35
  267. package/templates/startkit/src/components/app-header.tsx +0 -93
  268. package/templates/startkit/src/components/app-sidebar.tsx +0 -95
  269. package/templates/startkit/src/components/data-table.tsx +0 -80
  270. package/templates/startkit/src/components/empty-state.tsx +0 -13
  271. package/templates/startkit/src/components/page-header.tsx +0 -23
  272. package/templates/startkit/src/components/stat-card.tsx +0 -24
  273. package/templates/startkit/src/features/accounts/account-filters.tsx +0 -61
  274. package/templates/startkit/src/features/accounts/account-table.tsx +0 -79
  275. package/templates/startkit/src/lib/format.ts +0 -31
  276. package/templates/startkit/src/lib/mock-data.ts +0 -441
  277. package/templates/startkit/src/lib/routes.ts +0 -116
  278. package/templates/startkit/src/main.tsx +0 -12
  279. package/templates/startkit/src/pages/_layout.tsx +0 -56
  280. package/templates/startkit/src/pages/auth/_layout.tsx +0 -7
  281. package/templates/startkit/src/pages/auth/login.tsx +0 -129
  282. package/templates/startkit/src/pages/home.tsx +0 -83
  283. package/templates/startkit/src/pages/not-found.tsx +0 -24
  284. package/templates/startkit/src/pages/workspace/_layout.tsx +0 -17
  285. package/templates/startkit/src/pages/workspace/accounts/index.tsx +0 -217
  286. package/templates/startkit/src/pages/workspace/dashboard.tsx +0 -118
  287. package/templates/startkit/src/pages/workspace/settings.tsx +0 -218
  288. package/templates/startkit/src/router.tsx +0 -7
  289. package/templates/startkit/src/routes/auth-config.ts +0 -38
  290. package/templates/startkit/src/routes/auth.ts +0 -8
  291. package/templates/startkit/src/routes/index.ts +0 -28
  292. package/templates/startkit/src/routes/public.ts +0 -8
  293. package/templates/startkit/src/routes/workspace/accounts.ts +0 -8
  294. package/templates/startkit/src/routes/workspace/index.ts +0 -14
  295. package/templates/startkit/src/styles/components.css +0 -396
  296. package/templates/startkit/src/styles/layout.css +0 -192
  297. package/templates/startkit/src/styles/reset.css +0 -38
  298. package/templates/startkit/src/styles/theme.css +0 -76
  299. package/templates/startkit/src/styles/tokens.css +0 -67
  300. package/templates/startkit/src/styles.css +0 -7
  301. package/templates/startkit/src/toast.ts +0 -55
  302. package/templates/startkit/src/utils/join-classes.ts +0 -4
  303. package/templates/startkit/src/vite-env.d.ts +0 -5
  304. package/templates/startkit/tests/app.test.tsx +0 -32
  305. package/templates/startkit/tests/preferences.test.ts +0 -43
  306. package/templates/startkit/tests/resources.test.ts +0 -82
  307. package/templates/startkit/tsconfig.json +0 -20
  308. package/templates/startkit/tsconfig.node.json +0 -10
  309. package/templates/startkit/vite.config.ts +0 -32
  310. package/templates/startkit/vitest.config.ts +0 -13
@@ -1,79 +1,96 @@
1
1
  ---
2
2
  name: askr-dashboard-charts
3
- description: Use when building askr dashboards, stat cards, activity feeds, product metrics, tables, async feedback, and @askrjs/charts visualizations such as area, bar, line, donut, heatmap, timeline, gauges, and chart chrome.
3
+ description: Build product dashboards and metric-heavy Askr routes with route-owned async state, shared formatting, and typed @askrjs/charts plots. Use for stat cards, mixed charts, live or follow-latest data, zoom, brush, selection, accessible summaries, and chart exports.
4
4
  ---
5
5
 
6
6
  # Askr Dashboard Charts
7
7
 
8
- Use this for product dashboards and metric-heavy screens. The goal is route-owned data, deterministic metric formatting, and charts that answer product questions without inventing parallel UI systems.
9
-
10
- ## Use This When
11
-
12
- - You are building a dashboard, metrics screen, stat-card surface, or chart-heavy route.
13
- - The page needs resource-owned async loading plus chart composition.
14
- - Charts and tables must share the same formatter logic.
15
- - You need loading, empty, and error truth on a dense metrics screen.
8
+ Build dashboards that answer product questions without inventing parallel data, UI, or formatting systems.
16
9
 
17
10
  ## Inspect First
18
11
 
19
- - `templates/startkit/src/pages/workspace/dashboard.tsx`
20
- - Existing stat card, table, empty state, and chart styles
21
- - Existing format helpers in `src/shared`
22
- - Existing route or feature owners for the metrics you need
12
+ - Inspect the route or feature that owns the data.
13
+ - Inspect existing cards, tables, empty states, chart tokens, and format helpers.
14
+ - Inspect the nearest existing typed plot. In a generated SPA, start with `src/pages/app/admin-home.tsx`; in the CLI source, use `templates/spa/src/pages/app/admin-home.tsx`.
23
15
 
24
- ## Choose The Owner
16
+ ## Keep Ownership Clear
25
17
 
26
- - The route or page owns high-level loading and composition.
18
+ - Keep loading, empty, error, refresh, and composition state in the route or feature container.
27
19
  - `src/components/shared` owns reusable stat cards, page headers, tables, and empty states.
28
- - `src/features/<domain>` owns domain-specific dashboard panels and metric workflows.
20
+ - `src/features/<domain>` owns metric workflows and transforms.
29
21
  - `src/shared` owns formatting helpers so cards, tables, and charts stay consistent.
22
+ - Keep row types app-owned; do not import package-owned datum types.
30
23
 
31
- ## Do This In Order
24
+ ## Work In Order
32
25
 
33
- 1. Load dashboard data in the route or feature container with `resource()` unless the data must be shared across screens.
34
- 2. Keep metric formatting in shared helpers.
35
- 3. Use `@askrjs/charts` components for chart visuals and keep CSS imported at the app boundary.
36
- 4. Pair charts with labels, summaries, or tables when precision matters.
37
- 5. Keep loading, empty, error, and refresh states explicit.
26
+ 1. Load dashboard data with `resource()` unless a wider owner already exists.
27
+ 2. Model stable row keys and missing values before choosing marks.
28
+ 3. Create each typed plot namespace once at module scope.
29
+ 4. Compose the smallest plot that answers the product question.
30
+ 5. Import `@askrjs/charts/styles` once at the app boundary.
31
+ 6. Pair plots with summaries or on-demand data tables when precision matters.
38
32
 
39
- ## Copy This Shape
33
+ ## Compose A Typed Plot
40
34
 
41
35
  ```tsx
42
- import { resource } from "@askrjs/askr/resources";
43
- import { AreaChart, ChartPanel, ChartShell } from "@askrjs/charts/components";
44
-
45
- const dashboard = resource(({ signal }) => loadDashboard({ signal }), []);
36
+ import { createPlot } from "@askrjs/charts";
37
+
38
+ type ThroughputRow = {
39
+ id: string;
40
+ timestamp: Date;
41
+ completed: number;
42
+ movingAverage: number;
43
+ };
44
+
45
+ const ThroughputPlot = createPlot<ThroughputRow>();
46
+
47
+ <ThroughputPlot.Root
48
+ data={rows}
49
+ rowKey="id"
50
+ label="Completed runs over time"
51
+ title="Run throughput"
52
+ description="Completed runs with a moving average."
53
+ >
54
+ <ThroughputPlot.Bar x="timestamp" y="completed" />
55
+ <ThroughputPlot.Line x="timestamp" y="movingAverage" />
56
+ <ThroughputPlot.Point x="timestamp" y="movingAverage" />
57
+ <ThroughputPlot.Zoom axes="x" />
58
+ <ThroughputPlot.Brush axis="x" modifier="shift" />
59
+ </ThroughputPlot.Root>;
60
+ ```
46
61
 
47
- if (dashboard.pending && !dashboard.value) return <p>Loading dashboard...</p>;
48
- if (dashboard.error) return <p role="alert">Unable to load dashboard.</p>;
62
+ ## Handle Mixed, Live, And Interactive Data
49
63
 
50
- <ChartShell title="Revenue" description="Last 7 days">
51
- <ChartPanel title="Trend">
52
- <AreaChart label="Revenue trend" data={dashboard.value.revenue} />
53
- </ChartPanel>
54
- </ChartShell>;
55
- ```
64
+ - Use named scales and dual axes only when a mixed plot compares different units.
65
+ - Pass a reactive data getter for changing rows and update arrays with `appendPlotRows`, `upsertPlotRows`, `removePlotRows`, or `trimPlotRows`.
66
+ - Configure follow-latest by row count or time; let user pan or zoom pause following and provide an explicit `resumeLive()` action.
67
+ - Keep `view` and `selection` controlled when route or shared state owns them; otherwise use plot-local state.
68
+ - Add zoom, brush, legend filtering, or `onActivate` only when the workflow needs that interaction.
69
+ - Keep source and transformed exports distinguishable, and preserve access to visible or selected rows.
56
70
 
57
- ## Never Do These
71
+ ## Avoid
58
72
 
59
73
  - Inline mock metrics inside page JSX.
60
74
  - Hardcoded chart colors in runtime code.
61
75
  - Decorative charts that do not answer a product question.
76
+ - Calling `createPlot()` inside a component or using unstable row keys.
77
+ - Treating missing or non-finite values as zero.
62
78
  - Dense dashboards without loading, empty, and error states.
63
79
 
64
80
  ## Validate
65
81
 
66
- - Cards and charts share formatter logic.
67
- - The page scans cleanly at mobile, tablet, and desktop widths.
68
- - Chart data has stable labels and keys.
69
- - Loading, empty, error, and refresh states are visible.
82
+ - Verify cards, tables, tooltips, and exports share formatter logic; keep summaries and tooltip formatters valid for empty or missing rows.
83
+ - Verify mobile and desktop layouts in light and dark themes.
84
+ - Verify keyboard inspection, selection, zoom, brush, and the on-demand data table when used.
85
+ - Verify live updates retain stable selections and pause follow-latest after user navigation.
86
+ - Run the app's targeted tests and typecheck before its full acceptance command.
70
87
 
71
88
  ## Done When
72
89
 
73
- - Route-owned loading and chart composition are clear.
74
- - Metric formatting is shared and deterministic.
75
- - Charts support the page's product question instead of decorating it.
76
- - Async states remain explicit on the dashboard surface.
90
+ - Route-owned async states remain explicit.
91
+ - Typed rows and stable keys drive the plot.
92
+ - The plot supports the product question without becoming a component gallery.
93
+ - Accessible summaries and precise data remain available without relying on canvas pixels.
77
94
 
78
95
  ## Handoff
79
96
 
@@ -1,4 +1,4 @@
1
1
  interface:
2
2
  display_name: "Askr Dashboard Charts"
3
- short_description: "Build dashboards and charts"
4
- default_prompt: "Use $askr-dashboard-charts to build dashboard metrics and charts."
3
+ short_description: "Compose typed Askr dashboard plots"
4
+ default_prompt: "Use $askr-dashboard-charts to build an accessible typed dashboard plot."
@@ -28,7 +28,8 @@ Use this when the app renders outside the browser or produces static output. The
28
28
  2. Register routes at module load before boot or render.
29
29
  3. Keep render paths deterministic and environment-safe.
30
30
  4. Use route `entries` or the template's static config for parameterized SSG paths.
31
- 5. Build and preview after changes that cross the render boundary.
31
+ 5. Set the canonical `siteUrl` and explicitly exclude non-indexable routes in `sitemap.routes`.
32
+ 6. Build and preview after changes that cross the render boundary.
32
33
 
33
34
  ## Copy This Shape
34
35
 
@@ -61,6 +62,7 @@ npx @askrjs/cli ssg --config ./ssg.config.ts --output ./dist/static
61
62
  - SSR or SSG build output is deterministic for the same inputs.
62
63
  - Hydration has no structural mismatch warnings.
63
64
  - Generated static routes cover expected params.
65
+ - `sitemap.xml` contains every intended canonical route and excludes runtime-only or non-indexable routes.
64
66
  - Browser preview still navigates correctly after hydration.
65
67
 
66
68
  ## Done When
@@ -1,6 +1,8 @@
1
1
  import { t as isDirectExecution } from "./is-direct-execution-Cdlr-ZUl.js";
2
+ import { n as publishStagedDirectory, t as createSiblingStage } from "./directory-swap-DWoHtx7C.js";
2
3
  import fs from "node:fs/promises";
3
4
  import path from "node:path";
5
+ import { constants } from "node:fs";
4
6
  import { fileURLToPath } from "node:url";
5
7
  //#region src/bin/skill-review.ts
6
8
  const TEXT_FILE_EXTENSIONS = /* @__PURE__ */ new Set([
@@ -312,29 +314,41 @@ const REVIEW_PROMPTS = [
312
314
  async function pathExists$1(filePath) {
313
315
  return Boolean(await fs.stat(filePath).catch(() => null));
314
316
  }
317
+ const MAX_REVIEW_FILES = 1e4;
318
+ const MAX_REVIEW_FILE_BYTES = 2 * 1024 * 1024;
319
+ const MAX_REVIEW_TOTAL_BYTES = 20 * 1024 * 1024;
315
320
  function shouldReadFile(filePath) {
316
321
  return TEXT_FILE_EXTENSIONS.has(path.extname(filePath).toLowerCase());
317
322
  }
318
- async function collectReviewFiles(targetPath, files) {
323
+ async function collectReviewFiles(targetPath, state) {
319
324
  const stat = await fs.lstat(targetPath);
320
325
  if (stat.isSymbolicLink()) return;
321
326
  if (stat.isDirectory()) {
322
327
  const entries = await fs.readdir(targetPath, { withFileTypes: true });
323
328
  for (const entry of entries) {
324
329
  if (entry.isDirectory() && IGNORED_DIRECTORIES.has(entry.name)) continue;
325
- await collectReviewFiles(path.join(targetPath, entry.name), files);
330
+ await collectReviewFiles(path.join(targetPath, entry.name), state);
326
331
  }
327
332
  return;
328
333
  }
329
- if (stat.isFile() && shouldReadFile(targetPath)) files.push(targetPath);
334
+ if (stat.isFile() && shouldReadFile(targetPath)) {
335
+ if (stat.size > MAX_REVIEW_FILE_BYTES) throw new Error(`Review file exceeds ${MAX_REVIEW_FILE_BYTES} bytes: ${targetPath}`);
336
+ if (state.files.length >= MAX_REVIEW_FILES) throw new Error(`Review target exceeds ${MAX_REVIEW_FILES} text files`);
337
+ state.totalBytes += stat.size;
338
+ if (state.totalBytes > MAX_REVIEW_TOTAL_BYTES) throw new Error(`Review target exceeds ${MAX_REVIEW_TOTAL_BYTES} text bytes`);
339
+ state.files.push(targetPath);
340
+ }
330
341
  }
331
342
  async function loadReviewFiles(targetPath) {
332
343
  const resolvedTarget = path.resolve(targetPath);
333
344
  if (!await pathExists$1(resolvedTarget)) throw new Error(`Review target not found: ${resolvedTarget}`);
334
345
  const baseDirectory = (await fs.stat(resolvedTarget)).isDirectory() ? resolvedTarget : path.dirname(resolvedTarget);
335
- const absoluteFiles = [];
336
- await collectReviewFiles(resolvedTarget, absoluteFiles);
337
- const files = await Promise.all(absoluteFiles.map(async (filePath) => ({
346
+ const collection = {
347
+ files: [],
348
+ totalBytes: 0
349
+ };
350
+ await collectReviewFiles(resolvedTarget, collection);
351
+ const files = await Promise.all(collection.files.map(async (filePath) => ({
338
352
  path: path.relative(baseDirectory, filePath) || path.basename(filePath),
339
353
  content: await fs.readFile(filePath, "utf8")
340
354
  })));
@@ -496,17 +510,23 @@ function parseArgs(args) {
496
510
  cwd: process.cwd(),
497
511
  force: false,
498
512
  help: false,
499
- json: false
513
+ json: false,
514
+ errors: []
500
515
  };
501
516
  for (let i = 0; i < args.length; i += 1) {
502
517
  const arg = args[i];
503
518
  if (arg === "--help" || arg === "-h") parsed.help = true;
504
519
  else if (arg === "--json") parsed.json = true;
505
520
  else if (arg === "--force") parsed.force = true;
506
- else if (arg === "--cwd" && i + 1 < args.length) {
507
- parsed.cwd = args[i + 1];
508
- i += 1;
509
- } else positional.push(arg);
521
+ else if (arg === "--cwd") {
522
+ const value = args[i + 1];
523
+ if (!value || value.startsWith("-")) parsed.errors.push("Missing value for --cwd");
524
+ else {
525
+ parsed.cwd = value;
526
+ i += 1;
527
+ }
528
+ } else if (arg.startsWith("-")) parsed.errors.push(`Unknown option: ${arg}`);
529
+ else positional.push(arg);
510
530
  }
511
531
  return {
512
532
  ...parsed,
@@ -572,16 +592,36 @@ async function copyBundledSkills(targetSkillsDir, bundledNames) {
572
592
  await copyDir(src, dest);
573
593
  }
574
594
  }
595
+ async function stageSkillsDirectory(targetSkillsDir) {
596
+ const stage = await createSiblingStage(targetSkillsDir, "askr-skills");
597
+ if (await pathExists(targetSkillsDir)) await fs.cp(targetSkillsDir, stage, {
598
+ recursive: true,
599
+ mode: constants.COPYFILE_FICLONE
600
+ });
601
+ return stage;
602
+ }
603
+ async function replaceBundledSkills(targetSkillsDir, bundledNames) {
604
+ const stage = await stageSkillsDirectory(targetSkillsDir);
605
+ try {
606
+ await removeManagedSkillArtifacts(stage, bundledNames);
607
+ await copyBundledSkills(stage, bundledNames);
608
+ await publishStagedDirectory(stage, targetSkillsDir);
609
+ } catch (error) {
610
+ await fs.rm(stage, {
611
+ recursive: true,
612
+ force: true
613
+ });
614
+ throw error;
615
+ }
616
+ }
575
617
  async function installBundledSkills(options = {}) {
576
618
  const targetRoot = path.resolve(options.cwd ?? process.cwd());
577
619
  const targetSkillsDir = path.join(targetRoot, PROJECT_SKILLS_DIR);
578
620
  const bundledNames = await listBundledSkills();
621
+ const targetStat = await fs.stat(targetSkillsDir).catch(() => null);
622
+ if (targetStat && !targetStat.isDirectory()) throw new Error(`Skills target exists and is not a directory: ${targetSkillsDir}`);
579
623
  if ((await fs.readdir(targetSkillsDir).catch(() => [])).length > 0 && !options.force) throw new Error(`Refusing to install into non-empty ${targetSkillsDir}.`);
580
- if (options.force) {
581
- await fs.mkdir(targetSkillsDir, { recursive: true });
582
- await removeManagedSkillArtifacts(targetSkillsDir, bundledNames);
583
- }
584
- await copyBundledSkills(targetSkillsDir, bundledNames);
624
+ await replaceBundledSkills(targetSkillsDir, bundledNames);
585
625
  return {
586
626
  bundledNames,
587
627
  targetSkillsDir
@@ -591,9 +631,9 @@ async function syncBundledSkills(options = {}) {
591
631
  const targetRoot = path.resolve(options.cwd ?? process.cwd());
592
632
  const targetSkillsDir = path.join(targetRoot, PROJECT_SKILLS_DIR);
593
633
  const bundledNames = await listBundledSkills();
594
- await fs.mkdir(targetSkillsDir, { recursive: true });
595
- await removeManagedSkillArtifacts(targetSkillsDir, bundledNames);
596
- await copyBundledSkills(targetSkillsDir, bundledNames);
634
+ const targetStat = await fs.stat(targetSkillsDir).catch(() => null);
635
+ if (targetStat && !targetStat.isDirectory()) throw new Error(`Skills target exists and is not a directory: ${targetSkillsDir}`);
636
+ await replaceBundledSkills(targetSkillsDir, bundledNames);
597
637
  return {
598
638
  bundledNames,
599
639
  targetSkillsDir
@@ -615,9 +655,14 @@ async function installSkills(parsed, io) {
615
655
  }
616
656
  }
617
657
  async function syncSkills(parsed, io) {
618
- const { bundledNames, targetSkillsDir } = await syncBundledSkills({ cwd: parsed.cwd });
619
- io.log(`Synced ${bundledNames.length} Askr skills to ${targetSkillsDir}`);
620
- return 0;
658
+ try {
659
+ const { bundledNames, targetSkillsDir } = await syncBundledSkills({ cwd: parsed.cwd });
660
+ io.log(`Synced ${bundledNames.length} Askr skills to ${targetSkillsDir}`);
661
+ return 0;
662
+ } catch (error) {
663
+ io.error(error instanceof Error ? error.message : String(error));
664
+ return 1;
665
+ }
621
666
  }
622
667
  async function reviewSkills(parsed, io) {
623
668
  if (parsed.help && parsed.command === "review") {
@@ -646,6 +691,10 @@ async function reviewSkills(parsed, io) {
646
691
  }
647
692
  async function runSkillsCli(args = process.argv.slice(2), io = console) {
648
693
  const parsed = parseArgs(args);
694
+ if (parsed.errors.length > 0) {
695
+ for (const error of parsed.errors) io.error(error);
696
+ return 1;
697
+ }
649
698
  if (parsed.command === "review" && parsed.help) {
650
699
  io.log(reviewHelpText());
651
700
  return 0;
@@ -660,12 +709,28 @@ async function runSkillsCli(args = process.argv.slice(2), io = console) {
660
709
  return 1;
661
710
  }
662
711
  if (parsed.command === "list") {
712
+ if (parsed.positionals.length !== 1) {
713
+ io.error("Usage: askr skills list");
714
+ return 1;
715
+ }
663
716
  const names = await listBundledSkills();
664
717
  for (const name of names) io.log(name);
665
718
  return 0;
666
719
  }
667
- if (parsed.command === "install") return installSkills(parsed, io);
668
- if (parsed.command === "sync") return syncSkills(parsed, io);
720
+ if (parsed.command === "install") {
721
+ if (parsed.positionals.length !== 1) {
722
+ io.error("Usage: askr skills install [--cwd <dir>] [--force]");
723
+ return 1;
724
+ }
725
+ return installSkills(parsed, io);
726
+ }
727
+ if (parsed.command === "sync") {
728
+ if (parsed.positionals.length !== 1) {
729
+ io.error("Usage: askr skills sync [--cwd <dir>]");
730
+ return 1;
731
+ }
732
+ return syncSkills(parsed, io);
733
+ }
669
734
  if (parsed.command === "review") return reviewSkills(parsed, io);
670
735
  io.error(`Unknown skills command: ${parsed.command}`);
671
736
  io.error("Run `askr skills --help` to see available commands.");
@@ -678,5 +743,3 @@ async function main() {
678
743
  if (isDirectExecution(import.meta.url)) main();
679
744
  //#endregion
680
745
  export { runSkillsCli as n, syncBundledSkills as r, installBundledSkills as t };
681
-
682
- //# sourceMappingURL=skills-Bh0mEhIx.js.map
@@ -0,0 +1,18 @@
1
+ //#region src/bin/skills.d.ts
2
+ type CliIo = Pick<Console, "error" | "log">;
3
+ declare function installBundledSkills(options?: {
4
+ cwd?: string;
5
+ force?: boolean;
6
+ }): Promise<{
7
+ bundledNames: string[];
8
+ targetSkillsDir: string;
9
+ }>;
10
+ declare function syncBundledSkills(options?: {
11
+ cwd?: string;
12
+ }): Promise<{
13
+ bundledNames: string[];
14
+ targetSkillsDir: string;
15
+ }>;
16
+ declare function runSkillsCli(args?: string[], io?: CliIo): Promise<number>;
17
+ //#endregion
18
+ export { installBundledSkills, runSkillsCli, syncBundledSkills };
package/dist/skills.js CHANGED
@@ -1,3 +1,3 @@
1
1
  #!/usr/bin/env node
2
- import { n as runSkillsCli, r as syncBundledSkills, t as installBundledSkills } from "./skills-Bh0mEhIx.js";
2
+ import { n as runSkillsCli, r as syncBundledSkills, t as installBundledSkills } from "./skills-B7CbWur9.js";
3
3
  export { installBundledSkills, runSkillsCli, syncBundledSkills };
@@ -0,0 +1,49 @@
1
+ import semver from "semver";
2
+ //#region src/update/specification.ts
3
+ const WINDOWS_PATH = /^[A-Za-z]:[\\/]/;
4
+ const HOSTED_GIT = /^(?:bitbucket|gist|github|gitlab):/i;
5
+ const GIT_URL = /^(?:git(?:\+[^:]+)?:|git@|ssh:)/i;
6
+ const GIT_SHORTHAND = /^[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+(?:#.*)?$/;
7
+ const REGISTRY_TAG = /^[A-Za-z][A-Za-z0-9._-]*$/;
8
+ const SCOPED_PATH = /^@[^/]+\/[^/]+$/;
9
+ function parseDependencySpecification(specification) {
10
+ const rawSpec = specification.trim();
11
+ if (!rawSpec) return {
12
+ type: "unsupported",
13
+ rawSpec
14
+ };
15
+ if (/^npm:/i.test(rawSpec)) return {
16
+ type: "alias",
17
+ rawSpec
18
+ };
19
+ if (/^(?:file|link|workspace):/i.test(rawSpec) || rawSpec.startsWith("./") || rawSpec.startsWith("../") || rawSpec.startsWith("/") || rawSpec.startsWith("~/") || /\.(?:tar\.gz|tgz)$/i.test(rawSpec) || WINDOWS_PATH.test(rawSpec) || /^[A-Za-z]:/.test(rawSpec) || SCOPED_PATH.test(rawSpec)) return {
20
+ type: "file",
21
+ rawSpec
22
+ };
23
+ if (HOSTED_GIT.test(rawSpec) || GIT_URL.test(rawSpec) || GIT_SHORTHAND.test(rawSpec)) return {
24
+ type: "git",
25
+ rawSpec
26
+ };
27
+ if (/^https?:/i.test(rawSpec) || /^[a-z][a-z0-9+.-]*:\/\//i.test(rawSpec)) return {
28
+ type: "remote",
29
+ rawSpec
30
+ };
31
+ if (semver.valid(rawSpec)) return {
32
+ type: "version",
33
+ rawSpec
34
+ };
35
+ if (semver.validRange(rawSpec)) return {
36
+ type: "range",
37
+ rawSpec
38
+ };
39
+ if (REGISTRY_TAG.test(rawSpec)) return {
40
+ type: "tag",
41
+ rawSpec
42
+ };
43
+ return {
44
+ type: "unsupported",
45
+ rawSpec
46
+ };
47
+ }
48
+ //#endregion
49
+ export { parseDependencySpecification as t };
@@ -0,0 +1,41 @@
1
+ //#region src/ssg/sitemap.d.ts
2
+ type SitemapChangeFrequency = "always" | "hourly" | "daily" | "weekly" | "monthly" | "yearly" | "never";
3
+ interface SitemapRouteConfig {
4
+ /** Canonical URL. Relative values resolve against siteUrl. */
5
+ url?: string;
6
+ /** W3C date/datetime or Date describing the last meaningful page update. */
7
+ lastModified?: string | Date;
8
+ changeFrequency?: SitemapChangeFrequency;
9
+ /** Relative crawl priority from 0 through 1. */
10
+ priority?: number;
11
+ /** hreflang to canonical URL mapping, including optional x-default. */
12
+ alternates?: Readonly<Record<string, string>>;
13
+ }
14
+ interface SitemapRouteContext {
15
+ path: string;
16
+ filePath: string;
17
+ status: string;
18
+ }
19
+ interface SitemapConfig {
20
+ /** Site-wide values inherited by included routes. */
21
+ defaults?: Omit<SitemapRouteConfig, "url" | "alternates">;
22
+ /** Exact concrete route overrides. Set a route to false to exclude it. */
23
+ routes?: Readonly<Record<string, SitemapRouteConfig | false>>;
24
+ /** Compute or override sitemap data. Returning false excludes the route. */
25
+ resolve?: (route: SitemapRouteContext) => SitemapRouteConfig | false | null | undefined | Promise<SitemapRouteConfig | false | null | undefined>;
26
+ /** Output path relative to the SSG output directory. */
27
+ output?: string;
28
+ /** Maintain a Sitemap directive in robots.txt. Defaults to true. */
29
+ robots?: boolean | {
30
+ output?: string;
31
+ };
32
+ /** Optional lower limits for deterministic partitioning and tests. */
33
+ limits?: {
34
+ urlsPerFile?: number;
35
+ bytesPerFile?: number;
36
+ };
37
+ /** Maximum concurrent async route metadata resolvers. Defaults to 16. */
38
+ resolverConcurrency?: number;
39
+ }
40
+ //#endregion
41
+ export type { SitemapChangeFrequency, SitemapConfig, SitemapRouteConfig, SitemapRouteContext };
@@ -0,0 +1 @@
1
+ export {};
package/dist/ssg.d.ts ADDED
@@ -0,0 +1,61 @@
1
+ import { existsSync } from "node:fs";
2
+ //#region src/bin/ssg.d.ts
3
+ type CliIo = Pick<Console, "error" | "log">;
4
+ interface ParsedSsgArgs {
5
+ configPath: string;
6
+ outputDir: string;
7
+ workers: number | "auto";
8
+ incremental: boolean;
9
+ changedKeys: string[];
10
+ changedRoutes: string[];
11
+ forceFull: boolean;
12
+ help: boolean;
13
+ errors: string[];
14
+ }
15
+ interface RouteResult {
16
+ path: string;
17
+ filePath: string;
18
+ status: string;
19
+ error?: string;
20
+ }
21
+ interface GenerateResult {
22
+ mode: string;
23
+ successful: number;
24
+ totalRoutes: number;
25
+ failed: number;
26
+ rebuilt: number;
27
+ skipped: number;
28
+ removed: number;
29
+ cacheHits: number;
30
+ routes: RouteResult[];
31
+ }
32
+ interface StaticGen {
33
+ generate(options: ReturnType<typeof toGenerateOptions>): Promise<GenerateResult>;
34
+ }
35
+ interface SsgDeps {
36
+ cwd?: () => string;
37
+ now?: () => number;
38
+ existsSync?: typeof existsSync;
39
+ importConfig?: (filePath: string) => Promise<unknown>;
40
+ createStaticGen?: (options: {
41
+ routes?: unknown[];
42
+ registry?: unknown;
43
+ outputDir: string;
44
+ seed?: unknown;
45
+ dataOverrides?: unknown;
46
+ concurrency?: number;
47
+ parallelism: number | "auto";
48
+ document?: unknown;
49
+ assets?: unknown[];
50
+ }) => StaticGen;
51
+ }
52
+ declare function parseCliArgs(args: string[]): ParsedSsgArgs;
53
+ declare function toGenerateOptions(args: ParsedSsgArgs): {
54
+ mode: string;
55
+ changedKeys: string[];
56
+ changedRoutes: string[];
57
+ forceFull: boolean;
58
+ };
59
+ declare function runSsgCli(args?: string[], deps?: SsgDeps, io?: CliIo): Promise<number>;
60
+ //#endregion
61
+ export { parseCliArgs, runSsgCli };