@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.
- package/commands/doctor.ts +106 -0
- package/commands/update.ts +81 -0
- package/index.ts +16 -0
- package/package.json +1 -1
|
@@ -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');
|