@milesroxas/design-hub 0.0.0-stage → 0.1.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 (217) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +80 -2
  3. package/client.d.ts +6 -0
  4. package/configs/biome/biome.json +63 -0
  5. package/configs/tsconfig.project.json +17 -0
  6. package/core/bytes.test.ts +14 -0
  7. package/core/bytes.ts +14 -0
  8. package/core/catalog.test.ts +44 -0
  9. package/core/catalog.ts +43 -0
  10. package/core/checks.test.ts +23 -0
  11. package/core/checks.ts +72 -0
  12. package/core/contacts.test.ts +31 -0
  13. package/core/contacts.ts +38 -0
  14. package/core/doc.ts +4 -0
  15. package/core/feature.ts +6 -0
  16. package/core/fixtures.test.ts +33 -0
  17. package/core/fixtures.ts +69 -0
  18. package/core/frontmatter.ts +15 -0
  19. package/core/html.ts +3 -0
  20. package/core/image.test.ts +22 -0
  21. package/core/image.ts +37 -0
  22. package/core/jev.test.ts +66 -0
  23. package/core/jev.ts +77 -0
  24. package/core/review/model.ts +189 -0
  25. package/core/review/policy.ts +26 -0
  26. package/core/review/service.test.ts +163 -0
  27. package/core/review/service.ts +309 -0
  28. package/core/review/store.ts +51 -0
  29. package/core/review/triage.test.ts +41 -0
  30. package/core/review/triage.ts +58 -0
  31. package/core/states.test.ts +36 -0
  32. package/core/states.ts +34 -0
  33. package/dist/core/bytes.d.ts +5 -0
  34. package/dist/core/bytes.js +9 -0
  35. package/dist/core/catalog.d.ts +17 -0
  36. package/dist/core/catalog.js +24 -0
  37. package/dist/core/checks.d.ts +41 -0
  38. package/dist/core/checks.js +53 -0
  39. package/dist/core/contacts.d.ts +9 -0
  40. package/dist/core/contacts.js +34 -0
  41. package/dist/core/doc.d.ts +11 -0
  42. package/dist/core/doc.js +1 -0
  43. package/dist/core/feature.d.ts +3 -0
  44. package/dist/core/feature.js +4 -0
  45. package/dist/core/fixtures.d.ts +19 -0
  46. package/dist/core/fixtures.js +52 -0
  47. package/dist/core/frontmatter.d.ts +4 -0
  48. package/dist/core/frontmatter.js +15 -0
  49. package/dist/core/html.d.ts +2 -0
  50. package/dist/core/html.js +2 -0
  51. package/dist/core/image.d.ts +6 -0
  52. package/dist/core/image.js +34 -0
  53. package/dist/core/jev.d.ts +54 -0
  54. package/dist/core/jev.js +52 -0
  55. package/dist/core/review/model.d.ts +85 -0
  56. package/dist/core/review/model.js +112 -0
  57. package/dist/core/review/policy.d.ts +3 -0
  58. package/dist/core/review/policy.js +19 -0
  59. package/dist/core/review/service.d.ts +79 -0
  60. package/dist/core/review/service.js +245 -0
  61. package/dist/core/review/store.d.ts +20 -0
  62. package/dist/core/review/store.js +37 -0
  63. package/dist/core/review/triage.d.ts +6 -0
  64. package/dist/core/review/triage.js +45 -0
  65. package/dist/core/states.d.ts +6 -0
  66. package/dist/core/states.js +29 -0
  67. package/dist/scripts/bootstrap.d.ts +51 -0
  68. package/dist/scripts/bootstrap.js +278 -0
  69. package/dist/scripts/calibrate.d.ts +127 -0
  70. package/dist/scripts/calibrate.js +164 -0
  71. package/dist/scripts/check.d.ts +25 -0
  72. package/dist/scripts/check.js +30 -0
  73. package/dist/scripts/cli.d.ts +2 -0
  74. package/dist/scripts/cli.js +73 -0
  75. package/dist/scripts/commands.d.ts +25 -0
  76. package/dist/scripts/commands.js +49 -0
  77. package/dist/scripts/comments.d.ts +63 -0
  78. package/dist/scripts/comments.js +152 -0
  79. package/dist/scripts/dev.d.ts +46 -0
  80. package/dist/scripts/dev.js +84 -0
  81. package/dist/scripts/elyx.d.ts +61 -0
  82. package/dist/scripts/elyx.js +195 -0
  83. package/dist/scripts/fixtures-pull.d.ts +60 -0
  84. package/dist/scripts/fixtures-pull.js +121 -0
  85. package/dist/scripts/guard.d.ts +58 -0
  86. package/dist/scripts/guard.js +201 -0
  87. package/dist/scripts/hook.d.ts +18 -0
  88. package/dist/scripts/hook.js +46 -0
  89. package/dist/scripts/lib/bin.d.ts +4 -0
  90. package/dist/scripts/lib/bin.js +20 -0
  91. package/dist/scripts/lib/catalog.d.ts +7 -0
  92. package/dist/scripts/lib/catalog.js +18 -0
  93. package/dist/scripts/lib/cli.d.ts +75 -0
  94. package/dist/scripts/lib/cli.js +88 -0
  95. package/dist/scripts/lib/css-tokens.d.ts +13 -0
  96. package/dist/scripts/lib/css-tokens.js +67 -0
  97. package/dist/scripts/lib/judge.d.ts +24 -0
  98. package/dist/scripts/lib/judge.js +60 -0
  99. package/dist/scripts/lib/ports.d.ts +35 -0
  100. package/dist/scripts/lib/ports.js +113 -0
  101. package/dist/scripts/lib/review.d.ts +7 -0
  102. package/dist/scripts/lib/review.js +22 -0
  103. package/dist/scripts/lib/rules.d.ts +12 -0
  104. package/dist/scripts/lib/rules.js +20 -0
  105. package/dist/scripts/lib/vercel.d.ts +17 -0
  106. package/dist/scripts/lib/vercel.js +53 -0
  107. package/dist/scripts/new.d.ts +47 -0
  108. package/dist/scripts/new.js +113 -0
  109. package/dist/scripts/people.d.ts +59 -0
  110. package/dist/scripts/people.js +73 -0
  111. package/dist/scripts/preflight.d.ts +35 -0
  112. package/dist/scripts/preflight.js +251 -0
  113. package/dist/scripts/release-check.d.ts +19 -0
  114. package/dist/scripts/release-check.js +39 -0
  115. package/dist/scripts/skill-hint.d.ts +32 -0
  116. package/dist/scripts/skill-hint.js +103 -0
  117. package/dist/scripts/skills.d.ts +13 -0
  118. package/dist/scripts/skills.js +34 -0
  119. package/dist/scripts/visual-match.d.ts +46 -0
  120. package/dist/scripts/visual-match.js +93 -0
  121. package/dist/server/api.d.ts +22 -0
  122. package/dist/server/api.js +143 -0
  123. package/dist/server/checks.d.ts +4 -0
  124. package/dist/server/checks.js +22 -0
  125. package/dist/server/dev.d.ts +2 -0
  126. package/dist/server/dev.js +54 -0
  127. package/dist/server/env.d.ts +8 -0
  128. package/dist/server/env.js +57 -0
  129. package/dist/server/fake-upstash.d.ts +1 -0
  130. package/dist/server/fake-upstash.js +37 -0
  131. package/dist/server/file-kv.d.ts +2 -0
  132. package/dist/server/file-kv.js +24 -0
  133. package/dist/server/local.d.ts +4 -0
  134. package/dist/server/local.js +25 -0
  135. package/dist/server/paths.d.ts +22 -0
  136. package/dist/server/paths.js +81 -0
  137. package/dist/server/session.d.ts +22 -0
  138. package/dist/server/session.js +110 -0
  139. package/dist/server/upstash.d.ts +7 -0
  140. package/dist/server/upstash.js +41 -0
  141. package/dist/vercel/index.d.ts +5 -0
  142. package/dist/vercel/index.js +12 -0
  143. package/dist/vercel/proxy.d.ts +11 -0
  144. package/dist/vercel/proxy.js +238 -0
  145. package/dist/vite/kit.d.ts +4 -0
  146. package/dist/vite/kit.js +35 -0
  147. package/dist/vite/markdown.d.ts +2 -0
  148. package/dist/vite/markdown.js +212 -0
  149. package/dist/vite/plugin.d.ts +15 -0
  150. package/dist/vite/plugin.js +84 -0
  151. package/dist/vite/registry.d.ts +7 -0
  152. package/dist/vite/registry.js +70 -0
  153. package/kit/Icon.tsx +39 -0
  154. package/kit/Space.tsx +25 -0
  155. package/kit/Variants.tsx +134 -0
  156. package/kit/ViewTools.tsx +34 -0
  157. package/kit/base.css +27 -0
  158. package/kit/dials.css +98 -0
  159. package/kit/dials.tsx +322 -0
  160. package/kit/fixtures.ts +3 -0
  161. package/kit/format.ts +13 -0
  162. package/kit/harness.css +99 -0
  163. package/kit/hooks.ts +35 -0
  164. package/kit/icons.ts +329 -0
  165. package/kit/motion.ts +18 -0
  166. package/kit/prefs.ts +111 -0
  167. package/kit/space.css +12 -0
  168. package/kit/ui/Button.tsx +135 -0
  169. package/kit/ui/Menu.tsx +386 -0
  170. package/kit/ui/Overlay.tsx +300 -0
  171. package/kit/ui/Toast.tsx +187 -0
  172. package/kit/ui/index.ts +13 -0
  173. package/kit/ui/layer.ts +51 -0
  174. package/kit/ui/ui.css +238 -0
  175. package/package.json +87 -3
  176. package/shell/Guide.tsx +160 -0
  177. package/shell/Index.tsx +301 -0
  178. package/shell/Notice.tsx +27 -0
  179. package/shell/Prototype.tsx +31 -0
  180. package/shell/a11y.ts +50 -0
  181. package/shell/doc.d.ts +7 -0
  182. package/shell/doc.ts +3 -0
  183. package/shell/guide.css +133 -0
  184. package/shell/index.html +17 -0
  185. package/shell/main.tsx +53 -0
  186. package/shell/public/favicon.svg +1 -0
  187. package/shell/registry.ts +21 -0
  188. package/shell/registry.virtual.d.ts +10 -0
  189. package/shell/review/Review.tsx +989 -0
  190. package/shell/review/anchor.ts +132 -0
  191. package/shell/review/client.ts +165 -0
  192. package/shell/review/mount.tsx +12 -0
  193. package/shell/review/review.css +296 -0
  194. package/shell/shell.css +160 -0
  195. package/shell/system.css +238 -0
  196. package/skills/miles-conductor/SKILL.md +37 -0
  197. package/skills/miles-deploy/SKILL.md +67 -0
  198. package/skills/miles-design-hub/SKILL.md +62 -0
  199. package/skills/miles-get-hostile/SKILL.md +61 -0
  200. package/skills/miles-new-feature/SKILL.md +40 -0
  201. package/skills/miles-paper-to-prototype/SKILL.md +46 -0
  202. package/skills/miles-port/SKILL.md +59 -0
  203. package/skills/miles-port/references/astro.md +35 -0
  204. package/skills/miles-port/references/next.md +36 -0
  205. package/skills/miles-prototype/SKILL.md +83 -0
  206. package/skills/miles-prototype/references/accessibility.md +48 -0
  207. package/skills/miles-prototype/references/motion.md +59 -0
  208. package/skills/miles-prototype/references/performance.md +45 -0
  209. package/skills/miles-review/SKILL.md +39 -0
  210. package/templates/app/README.md +41 -0
  211. package/templates/app/index.tsx +32 -0
  212. package/templates/app/styles.css +1 -0
  213. package/templates/conductor/settings.toml +29 -0
  214. package/templates/guide.md +38 -0
  215. package/templates/space/README.md +37 -0
  216. package/templates/space/index.tsx +43 -0
  217. package/templates/space/styles.css +13 -0
@@ -0,0 +1,73 @@
1
+ #!/usr/bin/env node
2
+ /// <reference types="node" />
3
+ // The design-hub command line: one binary, one subcommand per file in this
4
+ // folder. Every subcommand keeps the contract in lib/cli.ts: flags only, no
5
+ // prompts, one JSON object on stdout, `--help` for its schema. `dev`,
6
+ // `preview` and `open` are the exceptions: they run a server and print
7
+ // Vite's log. `hook <name>` is for Claude Code's settings.json and prints
8
+ // hook JSON or nothing.
9
+ import * as bootstrap from './bootstrap.js';
10
+ import * as calibrate from './calibrate.js';
11
+ import * as check from './check.js';
12
+ import * as commands from './commands.js';
13
+ import * as comments from './comments.js';
14
+ import * as dev from './dev.js';
15
+ import * as elyx from './elyx.js';
16
+ import * as fixturesPull from './fixtures-pull.js';
17
+ import * as guard from './guard.js';
18
+ import { HOOKS, runHook } from './hook.js';
19
+ import { runCommand } from './lib/cli.js';
20
+ import * as create from './new.js';
21
+ import * as people from './people.js';
22
+ import * as preflight from './preflight.js';
23
+ import * as releaseCheck from './release-check.js';
24
+ import * as skillHint from './skill-hint.js';
25
+ import * as skills from './skills.js';
26
+ import * as visualMatch from './visual-match.js';
27
+ // `guide` is `new --guide`; `open` is `dev` with the browser.
28
+ const COMMANDS = {
29
+ bootstrap,
30
+ new: create,
31
+ guide: {
32
+ spec: create.spec,
33
+ main: (args) => create.main({ ...args, flags: { ...args.flags, guide: true } }),
34
+ },
35
+ dev,
36
+ open: { spec: dev.openSpec, main: dev.openMain },
37
+ build: { spec: dev.buildSpec, main: dev.build },
38
+ preview: { spec: dev.previewSpec, main: dev.previewBuilt },
39
+ check,
40
+ guard,
41
+ preflight,
42
+ people,
43
+ comments,
44
+ calibrate,
45
+ 'fixtures-pull': fixturesPull,
46
+ 'visual-match': visualMatch,
47
+ elyx,
48
+ skills,
49
+ 'skill-hint': skillHint,
50
+ 'release-check': releaseCheck,
51
+ commands,
52
+ };
53
+ function usage() {
54
+ console.log(JSON.stringify({
55
+ ok: false,
56
+ error: 'Name a command.',
57
+ commands: [...Object.keys(COMMANDS), 'hook'],
58
+ fix: 'design-hub commands lists every schema; design-hub <command> --help prints one.',
59
+ }, null, 2));
60
+ }
61
+ export async function cli([name, ...rest]) {
62
+ if (name === 'hook')
63
+ return HOOKS.includes(rest[0]) ? runHook(rest[0]) : usage();
64
+ const command = name ? COMMANDS[name] : undefined;
65
+ if (!command) {
66
+ usage();
67
+ if (name && name !== '--help' && name !== '-h')
68
+ process.exitCode = 1;
69
+ return;
70
+ }
71
+ return runCommand(command.spec, command.main, rest);
72
+ }
73
+ await cli(process.argv.slice(2));
@@ -0,0 +1,25 @@
1
+ #!/usr/bin/env node
2
+ import { type CommandSpec } from './lib/cli.ts';
3
+ export declare const spec: {
4
+ readonly name: 'commands';
5
+ readonly summary: "List every command's schema: its flags, arguments and output.";
6
+ readonly flags: {};
7
+ readonly output: '{ ok, commands: [schema] }';
8
+ };
9
+ export declare const COMMANDS: CommandSpec[];
10
+ export declare const main: () => {
11
+ commands: {
12
+ command: string;
13
+ summary: string;
14
+ positionals: {
15
+ name: string;
16
+ description: string;
17
+ required?: boolean;
18
+ variadic?: boolean;
19
+ }[];
20
+ flags: {
21
+ [k: string]: import("./lib/cli.ts").Flag;
22
+ };
23
+ output: string;
24
+ }[];
25
+ };
@@ -0,0 +1,49 @@
1
+ #!/usr/bin/env node
2
+ /// <reference types="node" />
3
+ // `design-hub commands`: every command's schema in one JSON list, so an agent
4
+ // finds what it can run, and with which flags, without reading any source.
5
+ // Each schema is the command's own `spec`, the same one its --help prints.
6
+ import * as bootstrap from './bootstrap.js';
7
+ import * as calibrate from './calibrate.js';
8
+ import * as check from './check.js';
9
+ import * as comments from './comments.js';
10
+ import * as dev from './dev.js';
11
+ import * as elyx from './elyx.js';
12
+ import * as fixturesPull from './fixtures-pull.js';
13
+ import * as guard from './guard.js';
14
+ import { schema } from './lib/cli.js';
15
+ import * as create from './new.js';
16
+ import * as people from './people.js';
17
+ import * as preflight from './preflight.js';
18
+ import * as releaseCheck from './release-check.js';
19
+ import * as skillHint from './skill-hint.js';
20
+ import * as skills from './skills.js';
21
+ import * as visualMatch from './visual-match.js';
22
+ export const spec = {
23
+ name: 'commands',
24
+ summary: "List every command's schema: its flags, arguments and output.",
25
+ flags: {},
26
+ output: '{ ok, commands: [schema] }',
27
+ };
28
+ export const COMMANDS = [
29
+ bootstrap.spec,
30
+ dev.spec,
31
+ dev.buildSpec,
32
+ dev.previewSpec,
33
+ dev.openSpec,
34
+ create.spec,
35
+ check.spec,
36
+ guard.spec,
37
+ preflight.spec,
38
+ people.spec,
39
+ comments.spec,
40
+ calibrate.spec,
41
+ fixturesPull.spec,
42
+ visualMatch.spec,
43
+ elyx.spec,
44
+ skills.spec,
45
+ skillHint.spec,
46
+ releaseCheck.spec,
47
+ spec,
48
+ ];
49
+ export const main = () => ({ commands: COMMANDS.map(schema) });
@@ -0,0 +1,63 @@
1
+ #!/usr/bin/env node
2
+ import { type Comment } from '../core/review/model.ts';
3
+ import type { Review } from '../core/review/service.ts';
4
+ import { type Args } from './lib/cli.ts';
5
+ import { type CliReview } from './lib/review.ts';
6
+ export declare const spec: {
7
+ readonly name: 'comments';
8
+ readonly summary: 'Read review comments into the repo, resolve them, or sort the unsorted ones.';
9
+ readonly positionals: [{
10
+ readonly name: 'action';
11
+ readonly description: 'summary (default): open and resolved counts per prototype. pull: docs/sprints/<feature>/comments.md, by state then kind. resolve or reopen <id>. triage: sort what Jev left unsorted, against the README states on disk.';
12
+ }, {
13
+ readonly name: 'id';
14
+ readonly description: 'The comment, for resolve and reopen';
15
+ }];
16
+ readonly flags: {
17
+ readonly all: {
18
+ readonly type: 'boolean';
19
+ readonly description: 'For pull: include resolved threads.';
20
+ };
21
+ readonly local: {
22
+ readonly type: 'boolean';
23
+ readonly description: 'Use the local dev store even when the deployed one is configured.';
24
+ };
25
+ };
26
+ readonly output: '{ ok, store, ... } per action';
27
+ };
28
+ /** One feature's comments as Markdown: by README state in table order, then kind; replies under their thread. */
29
+ export declare function renderComments(feature: string, comments: Comment[], stateOrder: string[]): string;
30
+ type Context = {
31
+ review: Review;
32
+ store: CliReview['store'];
33
+ all: Comment[];
34
+ roots: Comment[];
35
+ };
36
+ export declare function summary({ store, roots }: Pick<Context, 'store' | 'roots'>): {
37
+ store: "deployed" | "local";
38
+ prototypes: Record<string, {
39
+ open: number;
40
+ resolved: number;
41
+ }>;
42
+ };
43
+ export declare function main({ positionals: [action, id], flags }: Args<typeof spec>): Promise<{
44
+ store: "deployed" | "local";
45
+ prototypes: Record<string, {
46
+ open: number;
47
+ resolved: number;
48
+ }>;
49
+ } | {
50
+ store: "deployed" | "local";
51
+ threads: number;
52
+ written: string[];
53
+ } | {
54
+ store: "deployed" | "local";
55
+ comment: {
56
+ id: string;
57
+ status: "open" | "resolved";
58
+ };
59
+ } | {
60
+ store: "deployed" | "local";
61
+ sorted: string[];
62
+ }>;
63
+ export {};
@@ -0,0 +1,152 @@
1
+ #!/usr/bin/env node
2
+ /// <reference types="node" />
3
+ // Review comments from the deployed prototypes, brought into the repo where
4
+ // Claude works from them. `pull` writes a generated file per feature; the
5
+ // panel or `resolve` is where a comment is closed, never the file.
6
+ import { mkdirSync, writeFileSync } from 'node:fs';
7
+ import { join, relative } from 'node:path';
8
+ import { featureTitle } from '../core/feature.js';
9
+ import { KINDS } from '../core/review/model.js';
10
+ import { jevTriager } from '../core/review/triage.js';
11
+ import { checks } from '../server/checks.js';
12
+ import { readEnv } from '../server/env.js';
13
+ import { DOCS, ROOT } from '../server/paths.js';
14
+ import { prototypes } from './lib/catalog.js';
15
+ import { CliError } from './lib/cli.js';
16
+ import { cliActor, cliReview } from './lib/review.js';
17
+ export const spec = {
18
+ name: 'comments',
19
+ summary: 'Read review comments into the repo, resolve them, or sort the unsorted ones.',
20
+ positionals: [
21
+ {
22
+ name: 'action',
23
+ description: 'summary (default): open and resolved counts per prototype. pull: docs/sprints/<feature>/comments.md, by state then kind. resolve or reopen <id>. triage: sort what Jev left unsorted, against the README states on disk.',
24
+ },
25
+ { name: 'id', description: 'The comment, for resolve and reopen' },
26
+ ],
27
+ flags: {
28
+ all: { type: 'boolean', description: 'For pull: include resolved threads.' },
29
+ local: {
30
+ type: 'boolean',
31
+ description: 'Use the local dev store even when the deployed one is configured.',
32
+ },
33
+ },
34
+ output: '{ ok, store, ... } per action',
35
+ };
36
+ const NO_STATE = 'No state';
37
+ const KIND_TITLE = { bug: 'Bugs', taste: 'Taste', copy: 'Copy' };
38
+ const stateOf = (c) => c.triage?.state ?? NO_STATE;
39
+ const isRoot = (c) => c.thread === c.id;
40
+ // A body without closing punctuation gets a full stop, so the location after it reads apart from it.
41
+ const sentence = (text) => (/[.!?…]$/.test(text) ? text : `${text}.`);
42
+ const line = (c) => `${c.author.name} (${c.author.team}): ${sentence(c.body.replace(/\s*\n\s*/g, ' '))}`;
43
+ function threadLines(root, comments) {
44
+ const where = root.anchor
45
+ ? ` On \`${root.anchor.url}\`${root.anchor.target ? `, element \`${root.anchor.target}\`` : ''}.`
46
+ : '';
47
+ const resolved = root.status === 'resolved' ? ' Resolved.' : '';
48
+ const replies = comments.filter((c) => c.thread === root.id && !isRoot(c));
49
+ return [`- ${line(root)}${where}${resolved} id \`${root.id}\``, ...replies.map((r) => ` - ${line(r)}`)];
50
+ }
51
+ function stateSection(state, roots, comments) {
52
+ const out = ['', `## ${state}`];
53
+ for (const kind of [...KINDS, null]) {
54
+ const group = roots.filter((c) => stateOf(c) === state && (c.triage?.kind ?? null) === kind);
55
+ if (group.length)
56
+ out.push('', `### ${kind ? KIND_TITLE[kind] : 'Unsorted'}`, '', ...group.flatMap((c) => threadLines(c, comments)));
57
+ }
58
+ return out;
59
+ }
60
+ /** One feature's comments as Markdown: by README state in table order, then kind; replies under their thread. */
61
+ export function renderComments(feature, comments, stateOrder) {
62
+ const roots = comments.filter(isRoot);
63
+ const states = [...new Set([...stateOrder, ...roots.map(stateOf)])].filter((s) => roots.some((c) => stateOf(c) === s));
64
+ const out = [
65
+ `# Comments: ${featureTitle(feature)}`,
66
+ '',
67
+ 'Generated by `pnpm comments pull`; a second pull replaces it. Resolve a comment in the review panel or with `pnpm comments resolve <id>`, never here.',
68
+ ...states.flatMap((state) => stateSection(state, roots, comments)),
69
+ ];
70
+ return `${out.join('\n')}\n`;
71
+ }
72
+ export function summary({ store, roots }) {
73
+ const by = {};
74
+ for (const c of roots) {
75
+ const key = c.anchor?.prototype ?? 'unknown';
76
+ by[key] ??= { open: 0, resolved: 0 };
77
+ by[key][c.status]++;
78
+ }
79
+ return { store, prototypes: by };
80
+ }
81
+ function pull({ store, all, roots }, includeResolved) {
82
+ const keep = new Map(roots.filter((c) => includeResolved || c.status === 'open').map((c) => [c.id, c]));
83
+ const byFeature = new Map();
84
+ for (const c of all) {
85
+ const root = keep.get(c.thread);
86
+ if (!root)
87
+ continue;
88
+ const feature = root.anchor?.feature || root.anchor?.prototype || 'unsorted';
89
+ byFeature.set(feature, [...(byFeature.get(feature) ?? []), c]);
90
+ }
91
+ const disk = prototypes();
92
+ const written = [...byFeature].map(([feature, comments]) => {
93
+ const order = disk
94
+ .filter((p) => p.feature === feature)
95
+ .flatMap((p) => (p.states ?? []).map((s) => s.name));
96
+ const dir = join(DOCS, 'sprints', feature);
97
+ mkdirSync(dir, { recursive: true });
98
+ writeFileSync(join(dir, 'comments.md'), renderComments(feature, comments, order));
99
+ return relative(ROOT, join(dir, 'comments.md'));
100
+ });
101
+ return { store, threads: keep.size, written };
102
+ }
103
+ async function setStatus({ review, store }, id, resolved) {
104
+ const action = resolved ? 'resolve' : 'reopen';
105
+ if (!id)
106
+ throw new CliError(`Name the comment: pnpm comments ${action} <id>.`, {
107
+ fix: 'pnpm comments pull lists ids.',
108
+ });
109
+ const found = await review.locate(id);
110
+ if (!found)
111
+ throw new CliError('No comment has that id.', { id });
112
+ const comment = await review.resolve(cliActor(), { prototype: found.prototype, id, resolved });
113
+ return { store, comment: { id: comment.id, status: comment.status } };
114
+ }
115
+ async function triage({ review, store, roots }) {
116
+ const triager = jevTriager(readEnv(ROOT).TYPESAFE_API_KEY ?? '', checks);
117
+ if (!triager)
118
+ throw new CliError('TypeSafe is off.', {
119
+ fix: 'pnpm bootstrap --typesafe-key <key>, or sort them in the panel.',
120
+ });
121
+ const statesOf = new Map(prototypes().map((p) => [p.slug, (p.states ?? []).map((s) => s.name)]));
122
+ const sorted = [];
123
+ for (const c of roots) {
124
+ if (c.triage || !c.anchor)
125
+ continue;
126
+ const states = statesOf.get(c.anchor.prototype) ?? [];
127
+ const t = await triager({ body: c.body, prototype: c.anchor.prototype, url: c.anchor.url, states });
128
+ if (!t)
129
+ continue;
130
+ await review.retriage(cliActor(), { prototype: c.anchor.prototype, id: c.id, triage: t, states });
131
+ sorted.push(c.id);
132
+ }
133
+ return { store, sorted };
134
+ }
135
+ export async function main({ positionals: [action = 'summary', id], flags }) {
136
+ const { review, store } = cliReview(flags.local);
137
+ const all = await review.allComments();
138
+ const ctx = { review, store, all, roots: all.filter(isRoot) };
139
+ switch (action) {
140
+ case 'summary':
141
+ return summary(ctx);
142
+ case 'pull':
143
+ return pull(ctx, flags.all);
144
+ case 'resolve':
145
+ case 'reopen':
146
+ return setStatus(ctx, id, action === 'resolve');
147
+ case 'triage':
148
+ return triage(ctx);
149
+ default:
150
+ throw new CliError(`Unknown action ${action}.`, { fix: 'summary, pull, resolve, reopen or triage' });
151
+ }
152
+ }
@@ -0,0 +1,46 @@
1
+ #!/usr/bin/env node
2
+ import type { Args } from './lib/cli.ts';
3
+ export declare const spec: {
4
+ readonly name: 'dev';
5
+ readonly summary: 'Serve the hub, every prototype and the review panel on the port in .env.';
6
+ readonly flags: {
7
+ readonly open: {
8
+ readonly type: 'boolean';
9
+ readonly description: 'Open the browser once the server is up.';
10
+ };
11
+ };
12
+ readonly output: "Vite's own log; the command runs until stopped.";
13
+ };
14
+ export declare const buildSpec: {
15
+ readonly name: 'build';
16
+ readonly summary: 'Build the deployable site into dist/.';
17
+ readonly flags: {};
18
+ readonly output: '{ ok, outDir }';
19
+ };
20
+ export declare const previewSpec: {
21
+ readonly name: 'preview';
22
+ readonly summary: 'Serve dist/ as built, on the port in .env.';
23
+ readonly flags: {};
24
+ readonly output: "Vite's own log; the command runs until stopped.";
25
+ };
26
+ export declare const openSpec: {
27
+ readonly name: 'open';
28
+ readonly summary: 'Start the hub and open it in the browser; what the project launcher double-click runs.';
29
+ readonly flags: {};
30
+ readonly output: "Vite's own log; the command runs until stopped.";
31
+ };
32
+ /** Builds dist/; `ok: false` carries the error, so preflight can report it. */
33
+ export declare function build(): Promise<{
34
+ ok: boolean;
35
+ outDir: string;
36
+ error?: string;
37
+ }>;
38
+ export declare function previewBuilt(): Promise<{
39
+ stopped: boolean;
40
+ }>;
41
+ export declare function main({ flags }: Args<typeof spec>): Promise<{
42
+ stopped: boolean;
43
+ }>;
44
+ export declare function openMain(): Promise<{
45
+ stopped: boolean;
46
+ }>;
@@ -0,0 +1,84 @@
1
+ #!/usr/bin/env node
2
+ /// <reference types="node" />
3
+ // `design-hub dev`, `build`, `preview` and `open`: Vite on the hub's config
4
+ // (vite/plugin.ts), with the project as the content. `dev` streams Vite's own
5
+ // output rather than one JSON object, because a server never finishes.
6
+ import { spawn } from 'node:child_process';
7
+ import { createServer, build as viteBuild, preview as vitePreview } from 'vite';
8
+ import { devPort, readEnv } from '../server/env.js';
9
+ import { ROOT } from '../server/paths.js';
10
+ import { hubConfig } from '../vite/plugin.js';
11
+ export const spec = {
12
+ name: 'dev',
13
+ summary: 'Serve the hub, every prototype and the review panel on the port in .env.',
14
+ flags: { open: { type: 'boolean', description: 'Open the browser once the server is up.' } },
15
+ output: "Vite's own log; the command runs until stopped.",
16
+ };
17
+ export const buildSpec = {
18
+ name: 'build',
19
+ summary: 'Build the deployable site into dist/.',
20
+ flags: {},
21
+ output: '{ ok, outDir }',
22
+ };
23
+ export const previewSpec = {
24
+ name: 'preview',
25
+ summary: 'Serve dist/ as built, on the port in .env.',
26
+ flags: {},
27
+ output: "Vite's own log; the command runs until stopped.",
28
+ };
29
+ export const openSpec = {
30
+ name: 'open',
31
+ summary: 'Start the hub and open it in the browser; what the project launcher double-click runs.',
32
+ flags: {},
33
+ output: "Vite's own log; the command runs until stopped.",
34
+ };
35
+ function openBrowser(url) {
36
+ const [cmd, args] = process.platform === 'darwin'
37
+ ? ['open', [url]]
38
+ : process.platform === 'win32'
39
+ ? ['cmd', ['/c', 'start', '', url]]
40
+ : ['xdg-open', [url]];
41
+ spawn(cmd, args, { stdio: 'ignore', detached: true })
42
+ .on('error', () => { })
43
+ .unref();
44
+ }
45
+ async function serve(open) {
46
+ const server = await createServer(hubConfig({ root: ROOT }, 'serve'));
47
+ await server.listen();
48
+ server.printUrls();
49
+ if (open)
50
+ openBrowser(`http://localhost:${devPort(readEnv(ROOT))}/`);
51
+ await new Promise((resolve) => {
52
+ server.httpServer?.on('close', resolve);
53
+ for (const signal of ['SIGINT', 'SIGTERM'])
54
+ process.once(signal, () => {
55
+ void server.close().then(resolve);
56
+ });
57
+ });
58
+ }
59
+ /** Builds dist/; `ok: false` carries the error, so preflight can report it. */
60
+ export async function build() {
61
+ const config = hubConfig({ root: ROOT }, 'build');
62
+ const outDir = config.build?.outDir ?? 'dist';
63
+ try {
64
+ await viteBuild({ ...config, logLevel: 'warn' });
65
+ return { ok: true, outDir };
66
+ }
67
+ catch (error) {
68
+ return { ok: false, outDir, error: error instanceof Error ? error.message : String(error) };
69
+ }
70
+ }
71
+ export async function previewBuilt() {
72
+ const server = await vitePreview(hubConfig({ root: ROOT }, 'build'));
73
+ server.printUrls();
74
+ await new Promise(() => { });
75
+ return { stopped: true };
76
+ }
77
+ export async function main({ flags }) {
78
+ await serve(flags.open);
79
+ return { stopped: true };
80
+ }
81
+ export async function openMain() {
82
+ await serve(true);
83
+ return { stopped: true };
84
+ }
@@ -0,0 +1,61 @@
1
+ #!/usr/bin/env node
2
+ import { type Args } from './lib/cli.ts';
3
+ export declare const spec: {
4
+ readonly name: 'elyx';
5
+ readonly summary: 'Generate the Elyx design-system project from kit/tokens.css, or check it has not drifted.';
6
+ readonly flags: {
7
+ readonly check: {
8
+ readonly type: 'boolean';
9
+ readonly description: 'Fail when design-system/ differs from a fresh generation.';
10
+ };
11
+ };
12
+ readonly output: '{ ok, out, tokens, skipped, diagnostics } or { ok, drift }';
13
+ };
14
+ export declare function plan(css: string): {
15
+ files: Record<string, string>;
16
+ light: Map<string, string>;
17
+ dark: Map<string, string>;
18
+ counts: {
19
+ colors: number;
20
+ dark: number;
21
+ radius: number;
22
+ lengths: number;
23
+ fonts: number;
24
+ };
25
+ skipped: string[];
26
+ };
27
+ type ExportedToken = {
28
+ name: string;
29
+ value: string;
30
+ context: {
31
+ theme?: string;
32
+ };
33
+ };
34
+ /** A token whose bytes differ from tokens.css, or null when they match or cannot be compared. */
35
+ export declare function mismatch(t: ExportedToken, source: string | undefined): string | null;
36
+ export declare function main({ flags }: Args<typeof spec>): {
37
+ out: string;
38
+ tokens: {
39
+ colors: number;
40
+ dark: number;
41
+ radius: number;
42
+ lengths: number;
43
+ fonts: number;
44
+ };
45
+ skipped: string[];
46
+ diagnostics: {
47
+ errors?: number;
48
+ };
49
+ ok?: undefined;
50
+ drift?: undefined;
51
+ fix?: undefined;
52
+ } | {
53
+ out?: undefined;
54
+ tokens?: undefined;
55
+ skipped?: undefined;
56
+ diagnostics?: undefined;
57
+ ok: boolean;
58
+ drift: string[];
59
+ fix: string | undefined;
60
+ };
61
+ export {};