forked from santifer/career-ops
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathdoctor.mjs
More file actions
197 lines (179 loc) · 4.98 KB
/
doctor.mjs
File metadata and controls
197 lines (179 loc) · 4.98 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
#!/usr/bin/env node
/**
* doctor.mjs — Setup validation for career-ops
* Checks all prerequisites and prints a pass/fail checklist.
*/
import { existsSync, mkdirSync, readdirSync } from 'fs';
import { join, dirname } from 'path';
import { fileURLToPath } from 'url';
const __dirname = dirname(fileURLToPath(import.meta.url));
const projectRoot = __dirname;
// ANSI colors (only on TTY)
const isTTY = process.stdout.isTTY;
const green = (s) => isTTY ? `\x1b[32m${s}\x1b[0m` : s;
const red = (s) => isTTY ? `\x1b[31m${s}\x1b[0m` : s;
const dim = (s) => isTTY ? `\x1b[2m${s}\x1b[0m` : s;
function checkNodeVersion() {
const major = parseInt(process.versions.node.split('.')[0]);
if (major >= 18) {
return { pass: true, label: `Node.js >= 18 (v${process.versions.node})` };
}
return {
pass: false,
label: `Node.js >= 18 (found v${process.versions.node})`,
fix: 'Install Node.js 18 or later from https://nodejs.org',
};
}
function checkDependencies() {
if (existsSync(join(projectRoot, 'node_modules'))) {
return { pass: true, label: 'Dependencies installed' };
}
return {
pass: false,
label: 'Dependencies not installed',
fix: 'Run: npm install',
};
}
async function checkPlaywright() {
try {
const { chromium } = await import('playwright');
const execPath = chromium.executablePath();
if (existsSync(execPath)) {
return { pass: true, label: 'Playwright chromium installed' };
}
return {
pass: false,
label: 'Playwright chromium not installed',
fix: 'Run: npx playwright install chromium',
};
} catch {
return {
pass: false,
label: 'Playwright chromium not installed',
fix: 'Run: npx playwright install chromium',
};
}
}
function checkCv() {
if (existsSync(join(projectRoot, 'cv.md'))) {
return { pass: true, label: 'cv.md found' };
}
return {
pass: false,
label: 'cv.md not found',
fix: [
'Create cv.md in the project root with your CV in markdown',
'See examples/ for reference CVs',
],
};
}
function checkProfile() {
if (existsSync(join(projectRoot, 'config', 'profile.yml'))) {
return { pass: true, label: 'config/profile.yml found' };
}
return {
pass: false,
label: 'config/profile.yml not found',
fix: [
'Run: cp config/profile.example.yml config/profile.yml',
'Then edit it with your details',
],
};
}
function checkPortals() {
if (existsSync(join(projectRoot, 'portals.yml'))) {
return { pass: true, label: 'portals.yml found' };
}
return {
pass: false,
label: 'portals.yml not found',
fix: [
'Run: cp templates/portals.example.yml portals.yml',
'Then customize with your target companies',
],
};
}
function checkFonts() {
const fontsDir = join(projectRoot, 'fonts');
if (!existsSync(fontsDir)) {
return {
pass: false,
label: 'fonts/ directory not found',
fix: 'The fonts/ directory is required for PDF generation',
};
}
try {
const files = readdirSync(fontsDir);
if (files.length === 0) {
return {
pass: false,
label: 'fonts/ directory is empty',
fix: 'The fonts/ directory must contain font files for PDF generation',
};
}
} catch {
return {
pass: false,
label: 'fonts/ directory not readable',
fix: 'Check permissions on the fonts/ directory',
};
}
return { pass: true, label: 'Fonts directory ready' };
}
function checkAutoDir(name) {
const dirPath = join(projectRoot, name);
if (existsSync(dirPath)) {
return { pass: true, label: `${name}/ directory ready` };
}
try {
mkdirSync(dirPath, { recursive: true });
return { pass: true, label: `${name}/ directory ready (auto-created)` };
} catch {
return {
pass: false,
label: `${name}/ directory could not be created`,
fix: `Run: mkdir ${name}`,
};
}
}
async function main() {
console.log('\ncareer-ops doctor');
console.log('================\n');
const checks = [
checkNodeVersion(),
checkDependencies(),
await checkPlaywright(),
checkCv(),
checkProfile(),
checkPortals(),
checkFonts(),
checkAutoDir('data'),
checkAutoDir('output'),
checkAutoDir('reports'),
];
let failures = 0;
for (const result of checks) {
if (result.pass) {
console.log(`${green('✓')} ${result.label}`);
} else {
failures++;
console.log(`${red('✗')} ${result.label}`);
const fixes = Array.isArray(result.fix) ? result.fix : [result.fix];
for (const hint of fixes) {
console.log(` ${dim('→ ' + hint)}`);
}
}
}
console.log('');
if (failures > 0) {
console.log(`Result: ${failures} issue${failures === 1 ? '' : 's'} found. Fix them and run \`npm run doctor\` again.`);
process.exit(1);
} else {
console.log('Result: All checks passed. You\'re ready to go! Run `claude` to start.');
process.exit(0);
}
}
main().catch((err) => {
console.error('doctor.mjs failed:', err.message);
process.exit(1);
});