jobless 1.5.0 → 1.6.0

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,248 @@
1
+ import { copyToClipboard } from './clip.js';
2
+ import { printFit } from './lint-screens.js';
3
+ import {
4
+ about,
5
+ cases,
6
+ claims,
7
+ experience,
8
+ hireMailto,
9
+ lookingFor,
10
+ mailtoFor,
11
+ mentorship,
12
+ profile,
13
+ quotes,
14
+ signature,
15
+ skills,
16
+ stats,
17
+ vcard,
18
+ } from './data.js';
19
+ import {
20
+ banner,
21
+ blank,
22
+ bold,
23
+ bullet,
24
+ cyan,
25
+ dim,
26
+ green,
27
+ kv,
28
+ link,
29
+ magenta,
30
+ print,
31
+ section,
32
+ wrap,
33
+ yellow,
34
+ } from './style.js';
35
+
36
+ export const printHeader = () => {
37
+ print(banner());
38
+ blank();
39
+ print(` ${bold(cyan(profile.name))}`);
40
+ print(` ${profile.title}`);
41
+ print(` ${dim(`${profile.location} · ${profile.availability}`)}`);
42
+ blank();
43
+ const statLine = (items) =>
44
+ items.map((item) => `${bold(green(item.value))} ${dim(item.label)}`).join(dim(' '));
45
+ print(` ${statLine(stats.slice(0, 2))}`);
46
+ print(` ${statLine(stats.slice(2))}`);
47
+ blank();
48
+ print(
49
+ ` ${link(profile.email, hireMailto)}${dim(' · ')}${link(profile.website, profile.website)}${dim(' · ')}${link(
50
+ profile.github.replace('https://', ''),
51
+ profile.github,
52
+ )}`,
53
+ );
54
+ };
55
+
56
+ export const printSignature = () => {
57
+ blank();
58
+ section('PROOF');
59
+ blank();
60
+ print(` ${bold(signature.title)}`);
61
+ blank();
62
+ for (const line of signature.lines) bullet(line);
63
+ };
64
+
65
+ export const printWho = () => {
66
+ blank();
67
+ section('WHO');
68
+ blank();
69
+ for (const paragraph of about) {
70
+ bullet(paragraph);
71
+ blank();
72
+ }
73
+ kv('Now', profile.currently);
74
+ kv('Study', profile.study);
75
+ kv('Site', link(profile.website, profile.website));
76
+ blank();
77
+ print(` ${dim('numbers, with owners')}`);
78
+ blank();
79
+ for (const claim of claims) {
80
+ print(` ${bold(claim.value)} ${dim(claim.label)}`);
81
+ print(` ${dim(claim.who)}`);
82
+ for (const line of wrap(claim.method)) print(` ${line}`);
83
+ blank();
84
+ }
85
+ };
86
+
87
+ export const printLooking = () => {
88
+ blank();
89
+ section('WHAT I WANT');
90
+ blank();
91
+ print(` ${dim('roles')}`);
92
+ for (const role of lookingFor.roles) bullet(role, green);
93
+ blank();
94
+ print(` ${dim('setup')}`);
95
+ for (const item of lookingFor.setup) bullet(item);
96
+ blank();
97
+ print(` ${dim('yes')}`);
98
+ for (const item of lookingFor.wants) bullet(item);
99
+ blank();
100
+ print(` ${dim('hard no')}`);
101
+ for (const item of lookingFor.no) bullet(item, yellow);
102
+ };
103
+
104
+ export const printCard = () => {
105
+ blank();
106
+ print(` ${bold(signature.title)}`);
107
+ print(` ${dim(signature.lines.at(-1))}`);
108
+ blank();
109
+ kv('Email', link(profile.email, hireMailto));
110
+ kv('Site', link(profile.website, profile.website));
111
+ kv('GitHub', link(profile.github, profile.github));
112
+ blank();
113
+ print(` ${dim('Remote senior / full-stack. Press')} ${bold(green('h'))} ${dim('or email from here.')}`);
114
+ };
115
+
116
+ const printCase = (item, { deep = false } = {}) => {
117
+ print(` ${bold(item.title)}`);
118
+ print(` ${cyan(item.company)} ${dim(`${item.role} · ${item.kind}`)}`);
119
+ blank();
120
+ print(` ${dim('problem')}`);
121
+ for (const line of wrap(item.problem)) print(` ${line}`);
122
+ if (deep && item.trace) {
123
+ blank();
124
+ print(` ${dim('trace')}`);
125
+ for (const step of item.trace) bullet(step);
126
+ }
127
+ blank();
128
+ print(` ${dim('change')}`);
129
+ for (const line of wrap(item.change)) print(` ${line}`);
130
+ blank();
131
+ print(` ${dim('result')}`);
132
+ for (const line of wrap(item.result)) print(` ${line}`);
133
+ if (deep && item.still) {
134
+ blank();
135
+ print(` ${dim('still open')}`);
136
+ for (const line of wrap(item.still)) print(` ${line}`);
137
+ }
138
+ if (deep && item.undo) {
139
+ blank();
140
+ print(` ${dim('what I would undo')}`);
141
+ for (const line of wrap(item.undo)) print(` ${line}`);
142
+ }
143
+ if (deep && item.ask) {
144
+ blank();
145
+ print(` ${dim('ask me on the call')}`);
146
+ for (const question of item.ask) bullet(question);
147
+ }
148
+ if (item.url) {
149
+ blank();
150
+ print(` ${link(item.url.replace('https://', ''), item.url)}`);
151
+ }
152
+ print(` ${dim(item.stack.join(' · '))}`);
153
+ };
154
+
155
+ export const printProof = () => {
156
+ blank();
157
+ section('PROOF');
158
+ blank();
159
+ printCase(cases[0], { deep: true });
160
+ blank();
161
+ print(` ${dim('The other two case studies are')} ${bold('1')}${dim('. Hire is')} ${bold(green('h'))}${dim('.')}`);
162
+ };
163
+
164
+ export const printVcf = ({ copy = false } = {}) => {
165
+ const card = vcard();
166
+ blank();
167
+ section('VCARD');
168
+ blank();
169
+ for (const line of card.trim().split('\n')) print(` ${line}`);
170
+ blank();
171
+ if (copy && process.stdout.isTTY && copyToClipboard(card)) {
172
+ print(` ${green('copied')} vCard`);
173
+ }
174
+ };
175
+
176
+ export const printWork = () => {
177
+ blank();
178
+ section('CASE STUDIES');
179
+ blank();
180
+ print(` ${dim('Private production systems. I will walk through them on a call.')}`);
181
+ for (const item of cases) {
182
+ blank();
183
+ printCase(item, { deep: true });
184
+ }
185
+ blank();
186
+ print(` ${dim('mentorship')}`);
187
+ blank();
188
+ print(` ${bold(mentorship.who)} ${dim(mentorship.where)}`);
189
+ print(` ${mentorship.on.join(dim(' · '))}`);
190
+ for (const line of wrap(mentorship.still)) print(` ${line}`);
191
+ blank();
192
+ print(` ${dim('experience')}`);
193
+ blank();
194
+ for (const job of experience) {
195
+ print(` ${bold(job.role)}`);
196
+ print(` ${cyan(job.company)} ${dim(`${job.dates} · ${job.place}`)}`);
197
+ blank();
198
+ for (const point of job.points) bullet(point);
199
+ print(` ${dim(job.stack.join(' · '))}`);
200
+ blank();
201
+ }
202
+ print(` ${dim('people who were not bots')}`);
203
+ blank();
204
+ for (const quote of quotes) {
205
+ print(` ${magenta(`"${quote.text}"`)}`);
206
+ print(` ${dim(`— ${quote.name}${quote.role ? `, ${quote.role}` : ''}`)}`);
207
+ blank();
208
+ }
209
+ };
210
+
211
+ export const printSkills = () => {
212
+ blank();
213
+ section('STACK');
214
+ blank();
215
+ for (const group of skills) {
216
+ print(` ${bold(group.name)} ${dim(group.note)}`);
217
+ print(` ${group.items.join(dim(' · '))}`);
218
+ blank();
219
+ }
220
+ };
221
+
222
+ export const printHire = ({ copy = false, report, to } = {}) => {
223
+ const mailto = mailtoFor({ report, to });
224
+ blank();
225
+ section('HIRE ME');
226
+ blank();
227
+ print(` ${dim('Skip the 47-field form. This is the whole application.')}`);
228
+ blank();
229
+ kv('Email', link(profile.email, mailto));
230
+ kv('Phone', link(profile.phone, `tel:${profile.phone.replace(/\s+/g, '')}`));
231
+ kv('Site', link(profile.website, profile.website));
232
+ kv('GitHub', link(profile.github, profile.github));
233
+ kv('LinkedIn', link(profile.linkedin, profile.linkedin));
234
+ kv('Resume', link(profile.resume, profile.resume));
235
+ blank();
236
+ if (report?.letter) {
237
+ print(` ${dim('last lint')} ${bold(report.letter.result)} ${dim(`${report.score}/100`)}`);
238
+ blank();
239
+ }
240
+ if (report?.fit) printFit(report.fit, { failed: !report.ok });
241
+ if (copy && process.stdout.isTTY && copyToClipboard(profile.email)) {
242
+ print(` ${green('copied')} ${profile.email} ${dim('to clipboard')}`);
243
+ }
244
+ print(` ${dim('or')} ${link('open a prefilled email', mailto)}`);
245
+ blank();
246
+ print(` ${dim('Remote senior / full-stack. I can start a conversation this week.')}`);
247
+ };
248
+
package/src/screens.js CHANGED
@@ -1,26 +1,21 @@
1
1
  import { copyToClipboard } from './clip.js';
2
- import { unifiedDiff } from './diff.js';
3
2
  import { tweetFromReport } from './fit.js';
4
- import { rules } from './lint.js';
5
- import { letterText, printCompanyReject } from './reject.js';
6
-
3
+ import { letterText } from './reject.js';
4
+ export * from './profile-screens.js';
5
+ export * from './lint-screens.js';
7
6
  import {
8
- about,
9
7
  cases,
10
8
  claims,
11
9
  experience,
12
10
  hireMailto,
13
11
  lookingFor,
14
- mailtoFor,
15
12
  menu,
16
13
  mentorship,
17
14
  profile,
18
- quotes,
19
15
  rants,
20
16
  signature,
21
17
  skills,
22
18
  stats,
23
- vcard,
24
19
  } from './data.js';
25
20
  import {
26
21
  banner,
@@ -32,46 +27,13 @@ import {
32
27
  dim,
33
28
  green,
34
29
  inner,
35
- kv,
36
- link,
37
- magenta,
38
30
  pad,
39
31
  print,
40
- red,
41
32
  section,
42
33
  wrap,
43
34
  yellow,
44
35
  } from './style.js';
45
36
 
46
- export const printHeader = () => {
47
- print(banner());
48
- blank();
49
- print(` ${bold(cyan(profile.name))}`);
50
- print(` ${profile.title}`);
51
- print(` ${dim(`${profile.location} · ${profile.availability}`)}`);
52
- blank();
53
- const statLine = (items) =>
54
- items.map((item) => `${bold(green(item.value))} ${dim(item.label)}`).join(dim(' '));
55
- print(` ${statLine(stats.slice(0, 2))}`);
56
- print(` ${statLine(stats.slice(2))}`);
57
- blank();
58
- print(
59
- ` ${link(profile.email, hireMailto)}${dim(' · ')}${link(profile.website, profile.website)}${dim(' · ')}${link(
60
- profile.github.replace('https://', ''),
61
- profile.github,
62
- )}`,
63
- );
64
- };
65
-
66
- export const printSignature = () => {
67
- blank();
68
- section('PROOF');
69
- blank();
70
- print(` ${bold(signature.title)}`);
71
- blank();
72
- for (const line of signature.lines) bullet(line);
73
- };
74
-
75
37
  export const printHints = () => {
76
38
  blank();
77
39
  print(
@@ -79,51 +41,12 @@ export const printHints = () => {
79
41
  );
80
42
  };
81
43
 
82
- export const printWho = () => {
83
- blank();
84
- section('WHO');
85
- blank();
86
- for (const paragraph of about) {
87
- bullet(paragraph);
88
- blank();
89
- }
90
- kv('Now', profile.currently);
91
- kv('Study', profile.study);
92
- kv('Site', link(profile.website, profile.website));
93
- blank();
94
- print(` ${dim('numbers, with owners')}`);
95
- blank();
96
- for (const claim of claims) {
97
- print(` ${bold(claim.value)} ${dim(claim.label)}`);
98
- print(` ${dim(claim.who)}`);
99
- for (const line of wrap(claim.method)) print(` ${line}`);
100
- blank();
101
- }
102
- };
103
-
104
- export const printLooking = () => {
105
- blank();
106
- section('WHAT I WANT');
107
- blank();
108
- print(` ${dim('roles')}`);
109
- for (const role of lookingFor.roles) bullet(role, green);
110
- blank();
111
- print(` ${dim('setup')}`);
112
- for (const item of lookingFor.setup) bullet(item);
113
- blank();
114
- print(` ${dim('yes')}`);
115
- for (const item of lookingFor.wants) bullet(item);
116
- blank();
117
- print(` ${dim('hard no')}`);
118
- for (const item of lookingFor.no) bullet(item, yellow);
119
- };
120
-
121
44
  export const printRant = () => {
122
45
  blank();
123
46
  section('THE MARKET');
124
47
  blank();
125
48
  for (const line of wrap(
126
- 'The feed is thriving. Hiring is a lore page. The recruiter\'s coffee order is a senior requirement.',
49
+ 'A hiring incident report, with coping mechanisms.',
127
50
  )) {
128
51
  print(` ${dim(line)}`);
129
52
  }
@@ -136,170 +59,11 @@ export const printRant = () => {
136
59
  print(` ${dim('If you want the work, press')} ${bold('1')}${dim('. If you want to hire, press')} ${bold(green('h'))}${dim('.')}`);
137
60
  };
138
61
 
139
- export const printLintReport = (report, { quiet = false, copied = false } = {}) => {
140
- const { findings, source, errors, warnings, letter, ok, score } = report;
141
- const visible = quiet ? findings.filter((item) => item.level === 'error') : findings;
142
-
143
- blank();
144
- section('JOBLESS LINT');
145
- blank();
146
- print(` ${dim(source)} ${dim('·')} ${bold(`${score}/100`)}`);
147
- blank();
148
-
149
- if (ok && !findings.length) {
150
- print(` ${green('clean')} this posting might be a real job.`);
151
- if (report.fit) printFit(report.fit, { failed: false });
152
- blank();
153
- print(` ${dim('If you still want the engineer who wrote the linter:')} ${bold(green('h'))}`);
154
- return report;
155
- }
156
-
157
- for (const finding of visible) {
158
- const tag = finding.level === 'error' ? red('error') : yellow('warn ');
159
- print(` ${tag} ${finding.message}`);
160
- print(` ${dim(finding.id)}`);
161
- if (finding.line && finding.snippet) {
162
- print(` ${dim(`L${finding.line}`)} ${finding.snippet}`);
163
- }
164
- blank();
165
- }
166
-
167
- printCompanyReject(letter);
168
- if (report.fit) printFit(report.fit, { failed: true });
169
- blank();
170
- print(
171
- ` ${bold(`${score}/100`)} ${errors} error${errors === 1 ? '' : 's'} ${warnings} warning${warnings === 1 ? '' : 's'} ${dim('·')} ${dim('hire:')} ${bold(green('h'))}${dim(' copy letter:')} ${bold('c')}`,
172
- );
173
- if (copied) {
174
- print(` ${green('copied')} rejection letter`);
175
- }
176
- return report;
177
- };
178
-
179
- export const printFit = (fit, { failed = false } = {}) => {
180
- if (!fit) return;
181
- blank();
182
- section('FIT');
183
- blank();
184
- if (fit.matched.length) print(` ${green('overlap')} ${fit.matched.join(dim(' · '))}`);
185
- if (fit.missed.length) print(` ${yellow('miss')} ${fit.missed.join(dim(' · '))}`);
186
- if (fit.extra.length) print(` ${dim('also')} ${fit.extra.join(' · ')}`);
187
- blank();
188
- if (fit.score == null) {
189
- print(` ${dim('The posting did not name a stack. The engineer still exists.')} ${bold(green('h'))}`);
190
- } else if (failed) {
191
- print(
192
- ` ${bold(`${fit.score}/100`)} overlap ${dim('·')} ${dim('the posting failed. the engineer still matches:')} ${bold(green('h'))}`,
193
- );
194
- } else {
195
- print(
196
- ` ${bold(`${fit.score}/100`)} overlap ${dim('·')} ${dim('hire the engineer who matches:')} ${bold(green('h'))}`,
197
- );
198
- }
199
- };
200
-
201
- export const printFix = (fixed) => {
202
- blank();
203
- section('DIFF');
204
- blank();
205
- if (!fixed.applied.length) {
206
- print(` ${dim('nothing to rewrite.')}`);
207
- return;
208
- }
209
- print(` ${dim('applied')} ${fixed.applied.join(' · ')}`);
210
- blank();
211
- const diff = fixed.diff || unifiedDiff(fixed.before || '', fixed.text, fixed.path || 'posting.txt');
212
- if (!diff) {
213
- print(` ${dim('rewrite produced the same lines.')}`);
214
- return;
215
- }
216
- for (const line of diff.split('\n')) {
217
- if (line.startsWith('+') && !line.startsWith('+++')) print(` ${green(line)}`);
218
- else if (line.startsWith('-') && !line.startsWith('---')) print(` ${red(line)}`);
219
- else print(` ${dim(line)}`);
220
- }
221
- };
222
-
223
- export const printRules = () => {
224
- blank();
225
- section('RULES');
226
- blank();
227
- for (const rule of rules) {
228
- const tag = rule.level === 'error' ? red('error') : yellow('warn ');
229
- print(` ${tag} ${rule.id}`);
230
- print(` ${dim(rule.message)}`);
231
- if (rule.why) print(` ${dim(rule.why)}`);
232
- blank();
233
- }
234
- print(` ${dim('npx jobless lint --off=rockstar-ninja posting.txt')}`);
235
- print(` ${dim('jobless-disable-next-line no-fake-salary')}`);
236
- };
237
-
238
- export const printCard = () => {
239
- blank();
240
- print(` ${bold(signature.title)}`);
241
- print(` ${dim(signature.lines.at(-1))}`);
242
- blank();
243
- kv('Email', link(profile.email, hireMailto));
244
- kv('Site', link(profile.website, profile.website));
245
- kv('GitHub', link(profile.github, profile.github));
246
- blank();
247
- print(` ${dim('Remote senior / full-stack. Press')} ${bold(green('h'))} ${dim('or email from here.')}`);
248
- };
249
-
250
- const printCase = (item, { deep = false } = {}) => {
251
- print(` ${bold(item.title)}`);
252
- print(` ${cyan(item.company)} ${dim(`${item.role} · ${item.kind}`)}`);
253
- blank();
254
- print(` ${dim('problem')}`);
255
- for (const line of wrap(item.problem)) print(` ${line}`);
256
- if (deep && item.trace) {
257
- blank();
258
- print(` ${dim('trace')}`);
259
- for (const step of item.trace) bullet(step);
260
- }
261
- blank();
262
- print(` ${dim('change')}`);
263
- for (const line of wrap(item.change)) print(` ${line}`);
264
- blank();
265
- print(` ${dim('result')}`);
266
- for (const line of wrap(item.result)) print(` ${line}`);
267
- if (deep && item.still) {
268
- blank();
269
- print(` ${dim('still open')}`);
270
- for (const line of wrap(item.still)) print(` ${line}`);
271
- }
272
- if (deep && item.undo) {
273
- blank();
274
- print(` ${dim('what I would undo')}`);
275
- for (const line of wrap(item.undo)) print(` ${line}`);
276
- }
277
- if (deep && item.ask) {
278
- blank();
279
- print(` ${dim('ask me on the call')}`);
280
- for (const question of item.ask) bullet(question);
281
- }
282
- if (item.url) {
283
- blank();
284
- print(` ${link(item.url.replace('https://', ''), item.url)}`);
285
- }
286
- print(` ${dim(item.stack.join(' · '))}`);
287
- };
288
-
289
- export const printProof = () => {
290
- blank();
291
- section('PROOF');
292
- blank();
293
- printCase(cases[0], { deep: true });
294
- blank();
295
- print(` ${dim('The other two case studies are')} ${bold('1')}${dim('. Hire is')} ${bold(green('h'))}${dim('.')}`);
296
- };
297
-
298
62
  export const printTweet = (report) => {
299
63
  blank();
300
64
  print(tweetFromReport(report));
301
65
  blank();
302
- print(` ${dim('Post the command, not the punchline.')}`);
66
+ print(` ${dim('Copy it, remix it, send it to someone waiting for a reply.')}`);
303
67
  };
304
68
 
305
69
  export const printWhy = () => {
@@ -307,12 +71,12 @@ export const printWhy = () => {
307
71
  section('WHY');
308
72
  blank();
309
73
  for (const line of [
310
- 'The market parses humans in 400ms and calls it hiring.',
311
- 'jobless parses you first, then parses the job post.',
312
- 'It is a reverse ATS: the company has to pass.',
313
- 'Then it shows the engineer who debugs silent production failures.',
314
- 'Local only. No apply. No email sent. No scrape.',
315
- 'If the joke lands, press h.',
74
+ 'I got invited to interview and rejected at the same time.',
75
+ 'So I turned the hiring process into an npm package.',
76
+ 'They asked how I handle rejection. Apparently: semantic versioning.',
77
+ 'The second act is a reverse ATS: lint the posting, then reject it yourself.',
78
+ 'The hiring simulator is satire. The engineer behind it is real.',
79
+ 'Try npx jobless lint --demo. Meet me with npx jobless hire.',
316
80
  ]) {
317
81
  bullet(line);
318
82
  }
@@ -320,91 +84,6 @@ export const printWhy = () => {
320
84
  print(` ${dim('Hire is')} ${bold(green('h'))}${dim('.')}`);
321
85
  };
322
86
 
323
- export const printVcf = ({ copy = true } = {}) => {
324
- const card = vcard();
325
- blank();
326
- section('VCARD');
327
- blank();
328
- for (const line of card.trim().split('\n')) print(` ${line}`);
329
- blank();
330
- if (copy && process.stdout.isTTY && copyToClipboard(card)) {
331
- print(` ${green('copied')} vCard`);
332
- }
333
- };
334
-
335
- export const printWork = () => {
336
- blank();
337
- section('CASE STUDIES');
338
- blank();
339
- print(` ${dim('Private production systems. I will walk through them on a call.')}`);
340
- for (const item of cases) {
341
- blank();
342
- printCase(item, { deep: true });
343
- }
344
- blank();
345
- print(` ${dim('mentorship')}`);
346
- blank();
347
- print(` ${bold(mentorship.who)} ${dim(mentorship.where)}`);
348
- print(` ${mentorship.on.join(dim(' · '))}`);
349
- for (const line of wrap(mentorship.still)) print(` ${line}`);
350
- blank();
351
- print(` ${dim('experience')}`);
352
- blank();
353
- for (const job of experience) {
354
- print(` ${bold(job.role)}`);
355
- print(` ${cyan(job.company)} ${dim(`${job.dates} · ${job.place}`)}`);
356
- blank();
357
- for (const point of job.points) bullet(point);
358
- print(` ${dim(job.stack.join(' · '))}`);
359
- blank();
360
- }
361
- print(` ${dim('people who were not bots')}`);
362
- blank();
363
- for (const quote of quotes) {
364
- print(` ${magenta(`"${quote.text}"`)}`);
365
- print(` ${dim(`— ${quote.name}${quote.role ? `, ${quote.role}` : ''}`)}`);
366
- blank();
367
- }
368
- };
369
-
370
- export const printSkills = () => {
371
- blank();
372
- section('STACK');
373
- blank();
374
- for (const group of skills) {
375
- print(` ${bold(group.name)} ${dim(group.note)}`);
376
- print(` ${group.items.join(dim(' · '))}`);
377
- blank();
378
- }
379
- };
380
-
381
- export const printHire = ({ copy = true, report, to } = {}) => {
382
- const mailto = mailtoFor({ report, to });
383
- blank();
384
- section('HIRE ME');
385
- blank();
386
- print(` ${dim('Skip the 47-field form. This is the whole application.')}`);
387
- blank();
388
- kv('Email', link(profile.email, mailto));
389
- kv('Phone', link(profile.phone, `tel:${profile.phone.replace(/\s+/g, '')}`));
390
- kv('Site', link(profile.website, profile.website));
391
- kv('GitHub', link(profile.github, profile.github));
392
- kv('LinkedIn', link(profile.linkedin, profile.linkedin));
393
- kv('Resume', link(profile.resume, profile.resume));
394
- blank();
395
- if (report?.letter) {
396
- print(` ${dim('last lint')} ${bold(report.letter.result)} ${dim(`${report.score}/100`)}`);
397
- blank();
398
- }
399
- if (report?.fit) printFit(report.fit, { failed: !report.ok });
400
- if (copy && process.stdout.isTTY && copyToClipboard(profile.email)) {
401
- print(` ${green('copied')} ${profile.email} ${dim('to clipboard')}`);
402
- }
403
- print(` ${dim('or')} ${link('open a prefilled email', mailto)}`);
404
- blank();
405
- print(` ${dim('Remote senior / full-stack. I can start a conversation this week.')}`);
406
- };
407
-
408
87
  export const printMenu = () => {
409
88
  blank();
410
89
  print(
@@ -420,7 +99,11 @@ export const printMenu = () => {
420
99
  export const printHelp = () => {
421
100
  print(banner());
422
101
  blank();
423
- print(` ${bold('npx jobless')} reject you, then the resume`);
102
+ print(` ${bold('npx jobless')} reproduce a hiring race condition`);
103
+ print(` ${bold('npx jobless share --copy')} copy a compact anonymous result`);
104
+ print(` ${bold('npx jobless --record')} repeatable, anonymous, no prompt`);
105
+ print(` ${bold('npx jobless --personalize')} opt in to your local Git name`);
106
+ print(` ${bold('npx jobless lint --demo')} reject a fictional posting`);
424
107
  print(` ${bold('npx jobless lint [file]')} lint a job posting or jobs/`);
425
108
  print(` ${bold('npx jobless lint --json')} findings + rejection letter`);
426
109
  print(` ${bold('npx jobless lint --fix')} unified diff (dry-run)`);
@@ -435,17 +118,17 @@ export const printHelp = () => {
435
118
  print(` ${bold('npx jobless hire --to=manager')} hiring-manager subject`);
436
119
  print(` ${bold('npx jobless vcf')} vCard`);
437
120
  print(` ${bold('npx jobless why')} why this exists`);
438
- print(` ${bold('npx jobless tweet')} 280 characters, no punchline`);
121
+ print(` ${bold('npx jobless tweet')} the origin story, ready to share`);
439
122
  print(` ${bold('npx jobless who')} bio`);
440
123
  print(` ${bold('npx jobless looking')} roles`);
441
124
  print(` ${bold('npx jobless skills')} stack`);
442
125
  print(` ${bold('npx jobless rant')} the market`);
443
126
  print(` ${bold('npx jobless --resume')} skip the rejection`);
444
- print(` ${bold('npx jobless --seed=42')} same rejection every time`);
127
+ print(` ${bold('npx jobless --seed=42')} choose a repeatable scenario`);
445
128
  print(` ${bold('npx jobless --json')} machine readable resume`);
446
- print(` ${bold('npx jobless --no-color')} recording-friendly`);
129
+ print(` ${bold('npx jobless --no-color')} plain text output`);
447
130
  blank();
448
- print(` ${dim('In the prompt:')} ${bold(green('h'))} ${dim('hires,')} ${bold('l')} ${dim('lints,')} ${bold('c')} ${dim('copies the last letter,')} ${bold('q')} ${dim('quits.')}`);
131
+ print(` ${dim('In the prompt:')} ${bold(green('h'))} ${dim('hires,')} ${bold('l')} ${dim('lints,')} ${bold('c')} ${dim('copies the last result,')} ${bold('q')} ${dim('quits.')}`);
449
132
  print(` ${dim('Post the command, not the punchline:')} ${bold('npx jobless')}`);
450
133
  print(` ${dim(profile.website)}`);
451
134
  };