hirify-cli 0.5.2 → 0.5.4

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/README.md CHANGED
@@ -70,6 +70,7 @@ hirify vacancy read <slug>
70
70
  hirify vacancy reveal <slug>
71
71
  hirify vacancy hide <slug>...
72
72
  hirify company hide "<name>"
73
+ hirify hidden list
73
74
  hirify profile list
74
75
  hirify vacancy apply <slug> --profile <id> --cover-file cover.txt
75
76
  ```
@@ -79,7 +80,7 @@ have a separate allowance. `hirify account show` supplies the current values. Sh
79
80
  read to assess fit, then reveal what fits. Applying sends a real application and cannot be recalled:
80
81
  get the person's approval first. External vacancies are applied to through the destination from `reveal`.
81
82
  Hiding a vacancy or a company is free and has no limit: it stops coming back in search and feeds,
82
- here and on the website. `--undo` brings it back.
83
+ here and on the website. `--undo` brings it back; `hirify hidden list` shows what is hidden.
83
84
 
84
85
  The server defines search filters. Read `hirify filter guide`, preview with `filters.preview`, refine,
85
86
  and then run the final search. Do not infer filter names from an old example.
package/bin/lib/api.js CHANGED
@@ -3,9 +3,9 @@ import { CliError } from './errors.js'
3
3
  import { trustedURL } from './config.js'
4
4
  import { requireJSON, retryDelay } from './http.js'
5
5
  import { accessRestrictedMessage, actionRequiredMessage } from './messages.js'
6
- const READS = new Set(['account.status', 'vacancies.search', 'feeds.list', 'feeds.vacancies', 'profiles.list', 'webhooks.list', 'filters.guide'])
6
+ const READS = new Set(['account.status', 'vacancies.search', 'feeds.list', 'feeds.vacancies', 'profiles.list', 'webhooks.list', 'filters.guide', 'hidden.list'])
7
7
  const LISTS = new Set(['vacancies.search', 'feeds.list', 'feeds.vacancies', 'profiles.list', 'webhooks.list'])
8
- const OBJECTS = new Set(['account.status', 'vacancies.read', 'vacancies.reveal', 'applications.apply', 'feeds.create', 'feeds.set_delivery', 'webhooks.create', 'feedback.send', 'filters.guide'])
8
+ const OBJECTS = new Set(['account.status', 'vacancies.read', 'vacancies.reveal', 'applications.apply', 'feeds.create', 'feeds.set_delivery', 'webhooks.create', 'feedback.send', 'filters.guide', 'hidden.list'])
9
9
  const isObject = value => value && typeof value === 'object' && !Array.isArray(value)
10
10
  const immutable = value => { if (value && typeof value === 'object') { for (const child of Object.values(value)) immutable(child); Object.freeze(value) }; return value }
11
11
  export function validateManifest(doc, origin) {
package/bin/lib/cli.js CHANGED
@@ -19,6 +19,7 @@ export const COMMANDS = {
19
19
  'vacancy apply': { args: [1, 1], flags: ['profile', 'cover', 'cover-file'], description: 'send a real application; ask the person first' },
20
20
  'vacancy hide': { args: [1, 100], flags: ['undo'], description: 'hide vacancies from your search and feeds; free, no limit' },
21
21
  'company hide': { args: [0, 100], flags: ['vacancy', 'undo'], description: 'hide a company by name, or by --vacancy <slug>; free, no limit' },
22
+ 'hidden list': { args: [0, 0], flags: ['page', 'limit'], description: 'what you hid: vacancies and companies' },
22
23
  'feed list': { args: [0, 0], description: 'your saved feeds' },
23
24
  'feed show': { args: [1, 1], flags: ['limit', 'page'], description: 'vacancies in a feed' },
24
25
  'feed create': { args: [1, 1], flags: ['filters', 'telegram', 'no-telegram', 'webhook'], description: 'save a search and its delivery settings' },
@@ -238,6 +238,11 @@ async function cmdVacancyRead(args, words) {
238
238
  ['page', d.url],
239
239
  ])
240
240
  if (rows.length) console.log('\n' + rows.join('\n'))
241
+ if (d.hidden) {
242
+ console.log(d.hidden_reason === 'company'
243
+ ? '\nHidden: its company is hidden, so it stays out of search and feeds.'
244
+ : '\nHidden: you hid this vacancy, so it stays out of search and feeds.')
245
+ }
241
246
 
242
247
  const text = d.description ? asText(d.description) : ''
243
248
  console.log(text ? `\n${text}\n` : '\nThis vacancy has no text on it.\n')
@@ -295,20 +300,60 @@ async function cmdVacancyReveal(args, words) {
295
300
  * list the website's "Hide" button writes, so it works in both directions. Free and not
296
301
  * limited; --undo brings them back. Up to 100 per call: the server takes them in one request.
297
302
  */
298
- function printHidden(d, one, many, undo) {
299
- const n = count(d.updated) ?? 0
300
- const noun = n === 1 ? one : many
301
- console.log(undo
302
- ? `${n} ${noun} unhidden.`
303
- : `${n} ${noun} hidden. ${n === 1 ? 'It no longer appears' : 'They no longer appear'} in your search and feeds.`)
304
- if (Array.isArray(d.not_found) && d.not_found.length) console.log(`not found: ${d.not_found.join(', ')}`)
303
+ // What each result status means, in the words a person reads. The server names the outcome of
304
+ // every item, so nobody has to search again to find out whether a hide took.
305
+ const HIDE_STATUS = {
306
+ hidden: 'hidden', already_hidden: 'already hidden', unhidden: 'unhidden',
307
+ not_hidden: 'was not hidden', not_found: 'not found', no_company: 'no company name',
308
+ }
309
+
310
+ function printHidden(d, many, undo) {
311
+ const results = Array.isArray(d.results) ? d.results : []
312
+ const label = (r) => r.company ? `${r.company} (${r.slug})` : (r.name ?? r.slug ?? '-')
313
+ const width = Math.max(0, ...results.map((r) => (HIDE_STATUS[r.status] ?? String(r.status)).length))
314
+ for (const r of results) {
315
+ const status = HIDE_STATUS[r.status] ?? String(r.status)
316
+ console.log(`${status.padEnd(width)} ${label(r)}${r.still_hidden_by_company ? ' (still hidden: its company is hidden)' : ''}`)
317
+ }
318
+ const summary = Object.entries(d.summary ?? {}).map(([k, n]) => `${n} ${HIDE_STATUS[k] ?? k}`).join(', ')
319
+ if (summary) console.log(`\n${summary}.`)
320
+ if (!undo && (d.summary?.hidden || d.summary?.already_hidden)) console.log(`Hidden ${many} do not appear in your search and feeds. See them: hirify hidden list`)
321
+ }
322
+
323
+ /** Everything the person hid, here or on the website: vacancies page by page, all companies. */
324
+ async function cmdHiddenList(args) {
325
+ const query = new URLSearchParams()
326
+ const page = flag(args, '--page')
327
+ const limit = flag(args, '--limit')
328
+ if (page) query.set('page', page)
329
+ if (limit) query.set('per_page', limit)
330
+ const body = await callCapability('hidden.list', { query })
331
+ const d = body?.data ?? {}
332
+ const m = body?.meta ?? {}
333
+ out(body, () => {
334
+ const vacancies = d.vacancies ?? []
335
+ const companies = d.companies ?? []
336
+ console.log(`Hidden vacancies: ${d.totals?.vacancies ?? vacancies.length}`)
337
+ for (const v of vacancies) {
338
+ console.log(` ${v.slug}\n ${[v.title, v.company].filter(Boolean).join(' · ')}${v.archived ? ' [archived]' : ''} hidden ${dateOnly(v.hidden_at) ?? '-'}`)
339
+ }
340
+ if (m.last_page > 1) console.log(` page ${m.page} of ${m.last_page}: --page <n>`)
341
+ console.log(`\nHidden companies: ${d.totals?.companies ?? companies.length}`)
342
+ for (const c of companies) {
343
+ const n = count(c.live_vacancies) ?? 0
344
+ console.log(` ${c.name} (${n} live ${n === 1 ? 'vacancy' : 'vacancies'}) hidden ${dateOnly(c.hidden_at) ?? '-'}`)
345
+ }
346
+ if (vacancies.length || companies.length) {
347
+ console.log('\nUnhide: hirify vacancy hide <slug> --undo | hirify company hide "<name>" --undo')
348
+ }
349
+ })
305
350
  }
306
351
 
307
352
  async function cmdVacancyHide(args, words) {
308
353
  if (!words.length) die('at least one vacancy slug is required: hirify vacancy hide <slug>...')
309
354
  const undo = args.includes('--undo')
310
355
  const body = await callCapability('vacancies.hide', { payload: { slugs: words, hidden: !undo } })
311
- out(body, () => printHidden(body?.data ?? {}, 'vacancy', 'vacancies', undo))
356
+ out(body, () => printHidden(body?.data ?? {}, 'vacancies', undo))
312
357
  }
313
358
 
314
359
  async function cmdCompanyHide(args, words) {
@@ -317,7 +362,7 @@ async function cmdCompanyHide(args, words) {
317
362
  const undo = args.includes('--undo')
318
363
  const payload = { hidden: !undo, ...(words.length ? { names: words } : {}), ...(vacancy ? { slugs: [vacancy] } : {}) }
319
364
  const body = await callCapability('companies.hide', { payload })
320
- out(body, () => printHidden(body?.data ?? {}, 'company', 'companies', undo))
365
+ out(body, () => printHidden(body?.data ?? {}, 'companies', undo))
321
366
  }
322
367
 
323
368
  /**
@@ -848,7 +893,7 @@ return {
848
893
  intro: cmdIntro, skill: cmdSkill,
849
894
  'account show': cmdAccountShow,
850
895
  'vacancy search': cmdVacancySearch, 'vacancy read': cmdVacancyRead, 'vacancy reveal': cmdVacancyReveal, 'vacancy apply': cmdVacancyApply,
851
- 'vacancy hide': cmdVacancyHide, 'company hide': cmdCompanyHide,
896
+ 'vacancy hide': cmdVacancyHide, 'company hide': cmdCompanyHide, 'hidden list': cmdHiddenList,
852
897
  'feed list': cmdFeedList, 'feed show': cmdFeedShow, 'feed create': cmdFeedCreate, 'feed deliver': cmdFeedDeliver,
853
898
  'profile list': cmdProfileList, 'webhook list': cmdWebhookList, 'webhook create': cmdWebhookCreate,
854
899
  'feedback send': cmdFeedbackSend, 'filter guide': cmdFilterGuide, 'api call': cmdApiCall,
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "hirify-cli",
3
- "version": "0.5.2",
3
+ "version": "0.5.4",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "hirify-cli",
9
- "version": "0.5.2",
9
+ "version": "0.5.4",
10
10
  "license": "Apache-2.0",
11
11
  "dependencies": {
12
12
  "cross-spawn": "7.0.6",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hirify-cli",
3
- "version": "0.5.2",
3
+ "version": "0.5.4",
4
4
  "description": "Hirify CLI for AI agents: browser sign-in, job feeds, search, reading a vacancy in full, contacts to apply to, and applying on Hirify.",
5
5
  "bin": {
6
6
  "hirify": "bin/hirify.js"
@@ -49,7 +49,7 @@ no permission. Sending, saving and configuring do.
49
49
  3. `vacancy read <slug>` to assess fit from the full text rather than a headline.
50
50
  4. `vacancy reveal <slug>` only for vacancies that still fit after reading.
51
51
  5. Apply only after the person's approval and the rules below.
52
- 6. `vacancy hide` / `company hide` what the user rejected; free, unlimited.
52
+ 6. `vacancy hide`/`company hide` what the user rejected; `hidden list` shows it.
53
53
 
54
54
  ## Searching
55
55