@upgraide/ui-notes-cli 0.2.3 → 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.3",
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"],
@@ -13,7 +13,7 @@
13
13
  "formatters/*.ts",
14
14
  "resolvers/*.ts",
15
15
  "schemas/*.json",
16
- "skills/*.md"
16
+ "skills/**/*.md"
17
17
  ],
18
18
  "engines": {
19
19
  "bun": ">=1.0.0"
@@ -0,0 +1,160 @@
1
+ ---
2
+ name: ui-notes
3
+ description: CLI commands for managing UI feedback notes — pull context, resolve issues, add comments, generate changelogs, and batch triage.
4
+ ---
5
+
6
+ # UI Notes CLI
7
+
8
+ `uinotes` is the CLI for UI Notes. Use these commands to triage, resolve, and manage UI feedback notes from your terminal.
9
+
10
+ ## Configuration
11
+
12
+ ```bash
13
+ uinotes config # Show current config
14
+ uinotes config set apiUrl <url> # Set API URL
15
+ uinotes config set apiKey <key> # Set API key
16
+ uinotes set project <slug> # Bind project to current directory
17
+ ```
18
+
19
+ ## Context
20
+
21
+ Fetch an AI-optimized digest of open UI notes.
22
+
23
+ ### When to use
24
+ - At the start of a task to understand what UI issues exist
25
+ - Before making UI changes to check for related notes
26
+ - When the user asks about UI bugs, feedback, or design issues
27
+
28
+ ```bash
29
+ uinotes context --format markdown
30
+ ```
31
+
32
+ Add `--resolve-files --root-dir .` to map notes to source files.
33
+ Add `--type bug` to filter by type (bug, ux, feature, question).
34
+ Add `--project <slug>` to target a specific project.
35
+
36
+ ## Explain
37
+
38
+ Get full context for a single note including comments, history, and visual diffs.
39
+
40
+ ### When to use
41
+ - When investigating a specific note before fixing it
42
+ - When you need comments, history, or visual diff context
43
+ - When triaging a single note
44
+
45
+ ```bash
46
+ uinotes explain <note-id>
47
+ uinotes explain <note-id> --format json
48
+ ```
49
+
50
+ Returns: note body, type, status, component, selector, URL, screenshot URL, all comments, status history, and visual diffs.
51
+
52
+ ## Resolve
53
+
54
+ Mark notes as resolved after fixing the issue they describe.
55
+
56
+ ### When to use
57
+ - After fixing a bug, UX issue, or implementing a feature request described in a note
58
+ - After verifying a fix addresses the feedback
59
+
60
+ ```bash
61
+ # Resolve with a message explaining the fix
62
+ uinotes resolve <note-id> "Fixed by updating the component"
63
+
64
+ # Mark as won't fix with explanation
65
+ uinotes wontfix <note-id> "Working as intended"
66
+
67
+ # Bulk resolve all notes for a component
68
+ uinotes resolve-by-component <ComponentName> --message "Refactored component"
69
+ ```
70
+
71
+ Always include a resolution message describing what was done.
72
+
73
+ ## Comment
74
+
75
+ Add comments to notes to document progress or communicate.
76
+
77
+ ### When to use
78
+ - To document what was tried or what's blocking a fix
79
+ - To ask clarifying questions about a note
80
+ - To provide a status update before resolving
81
+
82
+ ```bash
83
+ uinotes comment <note-id> "Comment text here"
84
+ uinotes comments <note-id> # List existing comments
85
+ ```
86
+
87
+ ## Changelog
88
+
89
+ Generate a changelog from notes resolved since a git ref or date.
90
+
91
+ ### When to use
92
+ - After resolving notes, to generate a summary for PR descriptions
93
+ - For release notes listing UI fixes
94
+ - To audit what was fixed in a time period
95
+
96
+ ```bash
97
+ uinotes changelog --since HEAD~5
98
+ uinotes changelog --since v1.2.0 --format conventional
99
+ uinotes changelog --since 2026-02-01 --format json
100
+ ```
101
+
102
+ Formats: `markdown` (default), `conventional` (conventional commits), `json`.
103
+
104
+ ## Batch
105
+
106
+ Execute multiple operations (resolve, wontfix, comment, reopen) in one invocation.
107
+
108
+ ### When to use
109
+ - After triaging multiple notes and deciding actions for each
110
+ - When resolving several notes as part of a single fix
111
+ - For bulk operations that span multiple notes
112
+
113
+ ```bash
114
+ echo '{"op":"resolve","id":"abc123","message":"Fixed overflow"}
115
+ {"op":"comment","id":"def456","text":"Needs design input"}
116
+ {"op":"wontfix","id":"ghi789","message":"Deprecated component"}' | uinotes batch -
117
+ ```
118
+
119
+ Operations: `resolve`, `wontfix`, `comment` (requires `text`), `reopen`.
120
+ Add `--dry-run` to preview without executing.
121
+
122
+ ## Pull
123
+
124
+ Fetch raw note data from the API.
125
+
126
+ ```bash
127
+ uinotes pull
128
+ uinotes pull --project my-app --status open --type bug --limit 50
129
+ ```
130
+
131
+ | Option | Description |
132
+ |--------|-------------|
133
+ | `--project <name>` | Filter by project |
134
+ | `--status <status>` | Filter by status (`open`, `resolved`, `wontfix`) |
135
+ | `--type <type>` | Filter by type (`bug`, `ux`, `feature`, `question`) |
136
+ | `--limit <n>` | Max number of results |
137
+ | `--resolve-files` | Map notes to source files |
138
+
139
+ ## Projects
140
+
141
+ Manage projects.
142
+
143
+ ```bash
144
+ uinotes projects # List all projects
145
+ uinotes projects create <slug> <name> # Create a project
146
+ uinotes projects get <slug> # Get project details
147
+ uinotes projects rename <slug> <name> # Rename a project
148
+ uinotes projects add-url <slug> <pattern> # Add URL pattern
149
+ uinotes projects remove-url <slug> <pattern> # Remove URL pattern
150
+ uinotes projects delete <slug> # Archive a project
151
+ ```
152
+
153
+ ## Visual Diff
154
+
155
+ Capture and compare page screenshots.
156
+
157
+ ```bash
158
+ uinotes visual-diff --url https://myapp.com --project my-app
159
+ uinotes visual-diff --compare # Compare against previous screenshots
160
+ ```