@upgraide/ui-notes-cli 0.2.4 → 0.2.5

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.
@@ -0,0 +1,106 @@
1
+ import { existsSync, readFileSync } from 'fs';
2
+ import { join } from 'path';
3
+ import { homedir } from 'os';
4
+ import { getConfig, getConfigPath } from '../config.js';
5
+
6
+ const PACKAGE_NAME = '@upgraide/ui-notes-cli';
7
+
8
+ const PASS = '\x1b[32m✓\x1b[0m';
9
+ const FAIL = '\x1b[31m✗\x1b[0m';
10
+ const WARN = '\x1b[33m!\x1b[0m';
11
+
12
+ function check(ok: boolean, label: string, detail?: string): boolean {
13
+ const icon = ok ? PASS : FAIL;
14
+ console.log(` ${icon} ${label}${detail ? ` ${detail}` : ''}`);
15
+ return ok;
16
+ }
17
+
18
+ function warn(label: string, detail?: string): void {
19
+ console.log(` ${WARN} ${label}${detail ? ` ${detail}` : ''}`);
20
+ }
21
+
22
+ export async function doctor(): Promise<void> {
23
+ console.log('uinotes doctor\n');
24
+ let issues = 0;
25
+
26
+ // 1. Config file
27
+ const globalPath = join(homedir(), '.uinotes.json');
28
+ const localPath = join(process.cwd(), '.uinotes.json');
29
+ const hasGlobal = existsSync(globalPath);
30
+ const hasLocal = existsSync(localPath);
31
+ const configExists = hasGlobal || hasLocal;
32
+ if (!check(configExists, 'Config file', configExists ? `(${getConfigPath()})` : 'not found')) {
33
+ issues++;
34
+ }
35
+
36
+ // 2. API URL
37
+ const config = getConfig();
38
+ const hasUrl = !!config.apiUrl;
39
+ if (!check(hasUrl, 'API URL', hasUrl ? config.apiUrl : 'not set')) {
40
+ issues++;
41
+ }
42
+
43
+ // 3. API key
44
+ const hasKey = !!config.apiKey;
45
+ if (!check(hasKey, 'API key', hasKey ? 'configured' : 'not set — run: uinotes config set apiKey <key>')) {
46
+ issues++;
47
+ }
48
+
49
+ // 4. API reachability
50
+ if (hasUrl && hasKey) {
51
+ try {
52
+ const res = await fetch(`${config.apiUrl}/api/projects`, {
53
+ headers: { 'x-api-key': config.apiKey },
54
+ });
55
+ if (!check(res.ok, 'API reachable', `HTTP ${res.status}`)) {
56
+ issues++;
57
+ }
58
+ } catch (err: any) {
59
+ check(false, 'API reachable', err.message);
60
+ issues++;
61
+ }
62
+ } else {
63
+ warn('API reachable', 'skipped (no API key)');
64
+ }
65
+
66
+ // 5. Project binding
67
+ const hasProject = !!config.project;
68
+ if (!check(hasProject, 'Project bound', hasProject ? config.project : 'none — run: uinotes set project <slug>')) {
69
+ issues++;
70
+ }
71
+
72
+ // 6. Skills installed
73
+ const skillPath = join(process.cwd(), '.claude', 'skills', 'ui-notes', 'SKILL.md');
74
+ const skillsInstalled = existsSync(skillPath);
75
+ if (!check(skillsInstalled, 'Skills installed', skillsInstalled ? skillPath : 'not found — run: uinotes install-skills')) {
76
+ issues++;
77
+ }
78
+
79
+ // 7. CLI version
80
+ const pkgPath = join(import.meta.dir, '..', 'package.json');
81
+ const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'));
82
+ const currentVersion = pkg.version as string;
83
+ try {
84
+ const res = await fetch(`https://registry.npmjs.org/${PACKAGE_NAME}/latest`);
85
+ if (res.ok) {
86
+ const data = await res.json() as { version: string };
87
+ const latest = data.version;
88
+ const upToDate = currentVersion === latest;
89
+ if (!check(upToDate, 'CLI version', upToDate ? `${currentVersion} (latest)` : `${currentVersion} → ${latest} available — run: uinotes update`)) {
90
+ issues++;
91
+ }
92
+ } else {
93
+ warn('CLI version', `${currentVersion} (could not check registry)`);
94
+ }
95
+ } catch {
96
+ warn('CLI version', `${currentVersion} (could not reach registry)`);
97
+ }
98
+
99
+ // Summary
100
+ console.log('');
101
+ if (issues === 0) {
102
+ console.log('All checks passed.');
103
+ } else {
104
+ console.log(`${issues} issue${issues > 1 ? 's' : ''} found.`);
105
+ }
106
+ }
@@ -0,0 +1,81 @@
1
+ import { readFileSync } from 'fs';
2
+ import { join } from 'path';
3
+
4
+ const PACKAGE_NAME = '@upgraide/ui-notes-cli';
5
+
6
+ function parseVersion(v: string): number[] {
7
+ return v.replace(/^v/, '').split('.').map(Number);
8
+ }
9
+
10
+ function isNewer(latest: number[], current: number[]): boolean {
11
+ for (let i = 0; i < Math.max(latest.length, current.length); i++) {
12
+ const a = latest[i] ?? 0;
13
+ const b = current[i] ?? 0;
14
+ if (a > b) return true;
15
+ if (a < b) return false;
16
+ }
17
+ return false;
18
+ }
19
+
20
+ async function detectPackageManager(): Promise<'bun' | 'npm'> {
21
+ try {
22
+ const proc = Bun.spawn(['bun', '--version'], { stdout: 'pipe', stderr: 'pipe' });
23
+ await proc.exited;
24
+ if (proc.exitCode === 0) return 'bun';
25
+ } catch {}
26
+ return 'npm';
27
+ }
28
+
29
+ export async function update(): Promise<void> {
30
+ // Read current version
31
+ const pkgPath = join(import.meta.dir, '..', 'package.json');
32
+ const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'));
33
+ const currentVersion = pkg.version as string;
34
+
35
+ console.log(`Current version: ${currentVersion}`);
36
+ console.log('Checking for updates...');
37
+
38
+ // Fetch latest from npm registry
39
+ let latestVersion: string;
40
+ try {
41
+ const res = await fetch(`https://registry.npmjs.org/${PACKAGE_NAME}/latest`);
42
+ if (!res.ok) {
43
+ console.error(`Failed to check registry (HTTP ${res.status})`);
44
+ process.exit(1);
45
+ }
46
+ const data = await res.json() as { version: string };
47
+ latestVersion = data.version;
48
+ } catch (err: any) {
49
+ console.error(`Failed to reach npm registry: ${err.message}`);
50
+ process.exit(1);
51
+ }
52
+
53
+ const current = parseVersion(currentVersion);
54
+ const latest = parseVersion(latestVersion);
55
+
56
+ if (!isNewer(latest, current)) {
57
+ console.log(`Already up to date (${currentVersion})`);
58
+ return;
59
+ }
60
+
61
+ console.log(`New version available: ${latestVersion}`);
62
+
63
+ const pm = await detectPackageManager();
64
+ const cmd = pm === 'bun'
65
+ ? ['bun', 'add', '-g', PACKAGE_NAME]
66
+ : ['npm', 'install', '-g', PACKAGE_NAME];
67
+
68
+ console.log(`Installing with ${pm}...`);
69
+ const proc = Bun.spawn(cmd, {
70
+ stdout: 'inherit',
71
+ stderr: 'inherit',
72
+ });
73
+
74
+ const exitCode = await proc.exited;
75
+ if (exitCode !== 0) {
76
+ console.error('Update failed.');
77
+ process.exit(1);
78
+ }
79
+
80
+ console.log(`Updated to ${latestVersion}`);
81
+ }
package/index.ts CHANGED
@@ -36,6 +36,8 @@ Commands:
36
36
  batch <file|-> Execute NDJSON batch operations
37
37
  schema List or print API resource schemas
38
38
  install-skills Install Claude Code skill files
39
+ doctor Check CLI config, connectivity & version
40
+ update Check for and install CLI updates
39
41
  config View/edit configuration
40
42
 
41
43
  Options:
@@ -143,6 +145,20 @@ async function main() {
143
145
  return;
144
146
  }
145
147
 
148
+ // doctor is local-only, no API key needed
149
+ if (command === 'doctor') {
150
+ const { doctor } = await import('./commands/doctor.js');
151
+ await doctor();
152
+ return;
153
+ }
154
+
155
+ // update is local-only, no API key needed
156
+ if (command === 'update') {
157
+ const { update } = await import('./commands/update.js');
158
+ await update();
159
+ return;
160
+ }
161
+
146
162
  // install-skills is local-only, no API key needed
147
163
  if (command === 'install-skills') {
148
164
  const { installSkills } = await import('./commands/install-skills.js');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@upgraide/ui-notes-cli",
3
- "version": "0.2.4",
3
+ "version": "0.2.5",
4
4
  "description": "CLI for UI Notes - pull and manage UI feedback notes",
5
5
  "license": "MIT",
6
6
  "keywords": ["cli", "ui", "notes", "feedback", "ui-notes"],