ux-toolkit 0.5.0 → 0.5.3

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.
package/README.md CHANGED
@@ -130,9 +130,9 @@ This installs to `.opencode/` in your current directory.
130
130
  | Command | Description |
131
131
  |---------|-------------|
132
132
  | `/ux-audit` | Comprehensive UX audit |
133
- | `/a11y-check` | Quick accessibility scan |
134
- | `/design-review` | Visual consistency check |
135
- | `/screenshot-review` | Visual review from screenshot |
133
+ | `/ux-a11y-check` | Quick accessibility scan |
134
+ | `/ux-design-review` | Visual consistency check |
135
+ | `/ux-screenshot-review` | Visual review from screenshot |
136
136
 
137
137
  ## Usage Examples
138
138
 
@@ -143,7 +143,7 @@ This installs to `.opencode/` in your current directory.
143
143
  /ux-audit src/components/Button.tsx
144
144
 
145
145
  # Check accessibility
146
- /a11y-check src/pages/index.tsx
146
+ /ux-a11y-check src/pages/index.tsx
147
147
 
148
148
  # Invoke agent directly
149
149
  @ux-auditor Review the login flow for usability issues
package/dist/cli.js CHANGED
@@ -12,13 +12,40 @@ import { createInterface } from "readline";
12
12
 
13
13
  // src/installer.ts
14
14
  import { existsSync as existsSync2, mkdirSync, cpSync, readdirSync, rmSync, statSync } from "fs";
15
- import { join as join2, dirname as dirname2 } from "path";
15
+ import { dirname as dirname2 } from "path";
16
16
 
17
17
  // src/paths.ts
18
18
  import { homedir, platform } from "os";
19
- import { join, dirname, resolve } from "path";
19
+ import { dirname, resolve } from "path";
20
20
  import { fileURLToPath } from "url";
21
21
  import { existsSync } from "fs";
22
+
23
+ // src/utils.ts
24
+ import { join as pathJoin } from "path";
25
+ function safeJoin(...paths) {
26
+ const stringPaths = [];
27
+ for (const p of paths) {
28
+ if (typeof p === "string") {
29
+ stringPaths.push(p);
30
+ } else if (p != null) {
31
+ const str = String(p);
32
+ if (str && str !== "[object Object]" && str !== "undefined" && str !== "null") {
33
+ stringPaths.push(str);
34
+ }
35
+ }
36
+ }
37
+ if (stringPaths.length === 0) {
38
+ return ".";
39
+ }
40
+ try {
41
+ return pathJoin(...stringPaths);
42
+ } catch {
43
+ const sep = stringPaths[0].includes("\\") ? "\\" : "/";
44
+ return stringPaths.join(sep).replace(/[/\\]+/g, sep);
45
+ }
46
+ }
47
+
48
+ // src/paths.ts
22
49
  function getCurrentDirname() {
23
50
  try {
24
51
  if (import.meta?.url) {
@@ -28,7 +55,7 @@ function getCurrentDirname() {
28
55
  }
29
56
  try {
30
57
  const packageJsonPath = __require.resolve("ux-toolkit/package.json");
31
- return join(dirname(packageJsonPath), "dist");
58
+ return safeJoin(dirname(packageJsonPath), "dist");
32
59
  } catch {
33
60
  }
34
61
  const cwd = process.cwd();
@@ -47,7 +74,7 @@ function getCurrentDirname() {
47
74
  }
48
75
  var currentDirname = getCurrentDirname();
49
76
  function getPackageRoot() {
50
- return join(currentDirname, "..");
77
+ return safeJoin(currentDirname, "..");
51
78
  }
52
79
  function getGlobalConfigDir() {
53
80
  if (process.env.UX_TOOLKIT_CONFIG_DIR) {
@@ -59,9 +86,9 @@ function getGlobalConfigDir() {
59
86
  const home = homedir();
60
87
  const currentPlatform = platform();
61
88
  if (currentPlatform === "linux" && process.env.XDG_CONFIG_HOME) {
62
- return join(process.env.XDG_CONFIG_HOME, "opencode");
89
+ return safeJoin(process.env.XDG_CONFIG_HOME, "opencode");
63
90
  }
64
- return join(home, ".config", "opencode");
91
+ return safeJoin(home, ".config", "opencode");
65
92
  }
66
93
  function isOpenCodeInstalled() {
67
94
  return existsSync(getGlobalConfigDir());
@@ -70,7 +97,7 @@ function getClaudeConfigDir() {
70
97
  if (process.env.CLAUDE_CONFIG_DIR) {
71
98
  return resolve(process.env.CLAUDE_CONFIG_DIR);
72
99
  }
73
- return join(homedir(), ".claude");
100
+ return safeJoin(homedir(), ".claude");
74
101
  }
75
102
  function isClaudeInstalled() {
76
103
  return existsSync(getClaudeConfigDir());
@@ -92,7 +119,7 @@ function getPlatformInfo() {
92
119
  }
93
120
  function getProjectConfigDir(projectRoot = process.cwd(), target = "opencode") {
94
121
  const dirName = target === "claude" ? ".claude" : ".opencode";
95
- return join(projectRoot, dirName);
122
+ return safeJoin(projectRoot, dirName);
96
123
  }
97
124
  function getDestinationPaths(global, projectRoot, target = "opencode") {
98
125
  let baseDir;
@@ -102,9 +129,9 @@ function getDestinationPaths(global, projectRoot, target = "opencode") {
102
129
  baseDir = getProjectConfigDir(projectRoot, target);
103
130
  }
104
131
  return {
105
- skills: join(baseDir, "skills"),
106
- agents: join(baseDir, "agents"),
107
- commands: join(baseDir, "commands")
132
+ skills: safeJoin(baseDir, "skills"),
133
+ agents: safeJoin(baseDir, "agents"),
134
+ commands: safeJoin(baseDir, "commands")
108
135
  };
109
136
  }
110
137
 
@@ -346,15 +373,15 @@ var COMMANDS = [
346
373
  description: "Comprehensive UX audit"
347
374
  },
348
375
  {
349
- name: "a11y-check",
376
+ name: "ux-a11y-check",
350
377
  description: "Quick accessibility scan"
351
378
  },
352
379
  {
353
- name: "design-review",
380
+ name: "ux-design-review",
354
381
  description: "Visual consistency check"
355
382
  },
356
383
  {
357
- name: "screenshot-review",
384
+ name: "ux-screenshot-review",
358
385
  description: "Visual review from screenshot"
359
386
  }
360
387
  ];
@@ -390,7 +417,7 @@ async function install(options = {}) {
390
417
  if (verbose) console.log(msg);
391
418
  };
392
419
  if (effectiveCategories.includes("skills")) {
393
- const skillsDir = join2(packageRoot, "skills");
420
+ const skillsDir = safeJoin(packageRoot, "skills");
394
421
  if (existsSync2(skillsDir)) {
395
422
  let skills = readdirSync(skillsDir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name);
396
423
  if (specificSkills?.length) {
@@ -398,8 +425,8 @@ async function install(options = {}) {
398
425
  skills = skills.filter((s) => requested.has(s.toLowerCase()));
399
426
  }
400
427
  for (const skill of skills) {
401
- const src = join2(skillsDir, skill);
402
- const dest = join2(destinations.skills, skill);
428
+ const src = safeJoin(skillsDir, skill);
429
+ const dest = safeJoin(destinations.skills, skill);
403
430
  try {
404
431
  if (existsSync2(dest) && !force) {
405
432
  result.skipped.push(`skill:${skill}`);
@@ -417,7 +444,7 @@ async function install(options = {}) {
417
444
  }
418
445
  }
419
446
  if (effectiveCategories.includes("agents")) {
420
- const agentsDir = join2(packageRoot, "agents");
447
+ const agentsDir = safeJoin(packageRoot, "agents");
421
448
  if (existsSync2(agentsDir)) {
422
449
  let agents = readdirSync(agentsDir).filter((f) => f.endsWith(".md")).map((f) => f.replace(".md", ""));
423
450
  if (specificAgents?.length) {
@@ -425,8 +452,8 @@ async function install(options = {}) {
425
452
  agents = agents.filter((a) => requested.has(a.toLowerCase()));
426
453
  }
427
454
  for (const agent of agents) {
428
- const src = join2(agentsDir, `${agent}.md`);
429
- const dest = join2(destinations.agents, `${agent}.md`);
455
+ const src = safeJoin(agentsDir, `${agent}.md`);
456
+ const dest = safeJoin(destinations.agents, `${agent}.md`);
430
457
  try {
431
458
  if (existsSync2(dest) && !force) {
432
459
  result.skipped.push(`agent:${agent}`);
@@ -444,7 +471,7 @@ async function install(options = {}) {
444
471
  }
445
472
  }
446
473
  if (effectiveCategories.includes("commands")) {
447
- const commandsDir = join2(packageRoot, "commands");
474
+ const commandsDir = safeJoin(packageRoot, "commands");
448
475
  if (existsSync2(commandsDir)) {
449
476
  let commands = readdirSync(commandsDir).filter((f) => f.endsWith(".md")).map((f) => f.replace(".md", ""));
450
477
  if (specificCommands?.length) {
@@ -452,8 +479,8 @@ async function install(options = {}) {
452
479
  commands = commands.filter((c) => requested.has(c.toLowerCase()));
453
480
  }
454
481
  for (const command of commands) {
455
- const src = join2(commandsDir, `${command}.md`);
456
- const dest = join2(destinations.commands, `${command}.md`);
482
+ const src = safeJoin(commandsDir, `${command}.md`);
483
+ const dest = safeJoin(destinations.commands, `${command}.md`);
457
484
  try {
458
485
  if (existsSync2(dest) && !force) {
459
486
  result.skipped.push(`command:${command}`);
@@ -493,13 +520,13 @@ function getTargetStatus(target, isGlobal, projectRoot) {
493
520
  const available = target === "opencode" ? isOpenCodeInstalled() : isClaudeInstalled();
494
521
  const destinations = getDestinationPaths(isGlobal, projectRoot, target);
495
522
  const skillStatuses = SKILLS.map(
496
- (s) => checkComponentStatus(join2(destinations.skills, s.name), s.name)
523
+ (s) => checkComponentStatus(safeJoin(destinations.skills, s.name), s.name)
497
524
  );
498
525
  const agentStatuses = AGENTS.map(
499
- (a) => checkComponentStatus(join2(destinations.agents, `${a.name}.md`), a.name)
526
+ (a) => checkComponentStatus(safeJoin(destinations.agents, `${a.name}.md`), a.name)
500
527
  );
501
528
  const commandStatuses = COMMANDS.map(
502
- (c) => checkComponentStatus(join2(destinations.commands, `${c.name}.md`), c.name)
529
+ (c) => checkComponentStatus(safeJoin(destinations.commands, `${c.name}.md`), c.name)
503
530
  );
504
531
  return {
505
532
  target,
@@ -551,7 +578,7 @@ async function uninstall(options = {}) {
551
578
  const ourCommands = COMMANDS.map((c) => c.name);
552
579
  if (categories.includes("skills")) {
553
580
  for (const skill of ourSkills) {
554
- const dest = join2(destinations.skills, skill);
581
+ const dest = safeJoin(destinations.skills, skill);
555
582
  try {
556
583
  if (!existsSync2(dest)) {
557
584
  result.notFound.push(`skill:${skill}`);
@@ -568,7 +595,7 @@ async function uninstall(options = {}) {
568
595
  }
569
596
  if (categories.includes("agents")) {
570
597
  for (const agent of ourAgents) {
571
- const dest = join2(destinations.agents, `${agent}.md`);
598
+ const dest = safeJoin(destinations.agents, `${agent}.md`);
572
599
  try {
573
600
  if (!existsSync2(dest)) {
574
601
  result.notFound.push(`agent:${agent}`);
@@ -585,7 +612,7 @@ async function uninstall(options = {}) {
585
612
  }
586
613
  if (categories.includes("commands")) {
587
614
  for (const command of ourCommands) {
588
- const dest = join2(destinations.commands, `${command}.md`);
615
+ const dest = safeJoin(destinations.commands, `${command}.md`);
589
616
  try {
590
617
  if (!existsSync2(dest)) {
591
618
  result.notFound.push(`command:${command}`);
@@ -936,13 +963,12 @@ Errors:`);
936
963
  ok.push(`Node.js ${nodeVersion}`);
937
964
  }
938
965
  const platformInfo = getPlatformInfo();
966
+ const { existsSync: existsSync3 } = await import("fs");
939
967
  if (platformInfo.opencode.exists) {
940
968
  ok.push(`OpenCode detected at ${platformInfo.opencode.configDir}`);
941
- const { existsSync: existsSync3 } = await import("fs");
942
- const { join: join3 } = await import("path");
943
- const skillsDir = join3(platformInfo.opencode.configDir, "skills");
944
- const agentsDir = join3(platformInfo.opencode.configDir, "agents");
945
- const commandsDir = join3(platformInfo.opencode.configDir, "commands");
969
+ const skillsDir = safeJoin(platformInfo.opencode.configDir, "skills");
970
+ const agentsDir = safeJoin(platformInfo.opencode.configDir, "agents");
971
+ const commandsDir = safeJoin(platformInfo.opencode.configDir, "commands");
946
972
  if (!existsSync3(skillsDir)) {
947
973
  warnings.push("OpenCode skills directory missing");
948
974
  }
@@ -957,11 +983,9 @@ Errors:`);
957
983
  }
958
984
  if (platformInfo.claude.exists) {
959
985
  ok.push(`Claude Code detected at ${platformInfo.claude.configDir}`);
960
- const { existsSync: existsSync3 } = await import("fs");
961
- const { join: join3 } = await import("path");
962
- const skillsDir = join3(platformInfo.claude.configDir, "skills");
963
- const agentsDir = join3(platformInfo.claude.configDir, "agents");
964
- const commandsDir = join3(platformInfo.claude.configDir, "commands");
986
+ const skillsDir = safeJoin(platformInfo.claude.configDir, "skills");
987
+ const agentsDir = safeJoin(platformInfo.claude.configDir, "agents");
988
+ const commandsDir = safeJoin(platformInfo.claude.configDir, "commands");
965
989
  if (!existsSync3(skillsDir)) {
966
990
  warnings.push("Claude Code skills directory missing");
967
991
  }