jobhunt-kit 0.4.0 → 0.5.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.
Files changed (37) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/README.md +19 -2
  3. package/bin/jobhunt-kit.mjs +7 -3
  4. package/installer-assets/gitignore.txt +1 -0
  5. package/installer-assets/runtime-lock.json +56 -2
  6. package/installer-assets/workspace-lock.json +56 -2
  7. package/package.json +3 -1
  8. package/plugins/jobhunt-kit/.claude-plugin/plugin.json +1 -1
  9. package/plugins/jobhunt-kit/.codex-plugin/plugin.json +1 -1
  10. package/plugins/jobhunt-kit/package-lock.json +56 -2
  11. package/plugins/jobhunt-kit/package.json +2 -1
  12. package/plugins/jobhunt-kit/references/cli.md +27 -1
  13. package/plugins/jobhunt-kit/references/privacy.md +24 -0
  14. package/plugins/jobhunt-kit/references/storage.md +11 -5
  15. package/plugins/jobhunt-kit/schemas/draft.schema.json +13 -0
  16. package/plugins/jobhunt-kit/schemas/finish.schema.json +11 -0
  17. package/plugins/jobhunt-kit/schemas/policy.schema.json +36 -0
  18. package/plugins/jobhunt-kit/schemas/profile.schema.json +80 -0
  19. package/plugins/jobhunt-kit/schemas/status.schema.json +12 -0
  20. package/plugins/jobhunt-kit/schemas/vacancy.schema.json +29 -0
  21. package/plugins/jobhunt-kit/scripts/commands.mjs +57 -11
  22. package/plugins/jobhunt-kit/scripts/extract-resume.mjs +5 -3
  23. package/plugins/jobhunt-kit/scripts/files.mjs +69 -0
  24. package/plugins/jobhunt-kit/scripts/maintenance.mjs +292 -0
  25. package/plugins/jobhunt-kit/scripts/profile.mjs +23 -20
  26. package/plugins/jobhunt-kit/scripts/resume.mjs +42 -38
  27. package/plugins/jobhunt-kit/scripts/setup.mjs +8 -10
  28. package/plugins/jobhunt-kit/scripts/tracker.mjs +188 -33
  29. package/plugins/jobhunt-kit/scripts/validation.mjs +30 -0
  30. package/plugins/jobhunt-kit/skills/job-apply/SKILL.md +4 -2
  31. package/plugins/jobhunt-kit/skills/job-resume/SKILL.md +3 -2
  32. package/plugins/jobhunt-kit/skills/job-track/SKILL.md +8 -0
  33. package/scripts/check-package.mjs +7 -0
  34. package/scripts/pack-smoke.mjs +53 -0
  35. package/tests/hardening.test.mjs +337 -0
  36. package/tests/installer.test.mjs +9 -2
  37. package/tests/tracker.test.mjs +3 -0
@@ -8,7 +8,7 @@
8
8
  "name": "jobhunt-kit",
9
9
  "source": "./plugins/jobhunt-kit",
10
10
  "description": "Candidate intake, resume review, Hirify search and local tracking",
11
- "version": "0.4.0"
11
+ "version": "0.5.0"
12
12
  }
13
13
  ]
14
14
  }
package/README.md CHANGED
@@ -55,6 +55,11 @@ npx jobhunt-kit profile check
55
55
  npx jobhunt-kit search
56
56
  npx jobhunt-kit track
57
57
  npx jobhunt-kit report
58
+ npx jobhunt-kit recover inspect
59
+ npx jobhunt-kit recover packet <attempt_id>
60
+ npx jobhunt-kit backup ../jobhunt-backup
61
+ npx jobhunt-kit backup-verify ../jobhunt-backup
62
+ npx jobhunt-kit privacy map
58
63
  ```
59
64
 
60
65
  `resume` извлекает текст PDF, DOCX, TXT или Markdown и готовит материалы для проверки
@@ -77,7 +82,17 @@ npx jobhunt-kit report
77
82
  | `reports/latest.md` | Сводный отчёт |
78
83
 
79
84
  Папка `local/` исключена из Git. Храните личные файлы в ней; для резервной копии
80
- сохраняйте всю папку при остановленных процессах. Данные на диске не зашифрованы.
85
+ используйте `backup` и затем `backup-verify`. Restore принимает только проверяемую
86
+ копию, требует точное подтверждение и предварительно делает сырой quarantine-снимок,
87
+ который сохраняется даже тогда, когда текущие JSON или SQLite уже повреждены.
88
+ Restore и очистка history всегда отзывают auto-разрешение и подтверждение профиля.
89
+ Данные на диске не зашифрованы.
90
+
91
+ `doctor` проверяет JSON Schema профиля/policy, SQLite integrity и foreign keys,
92
+ незавершённые поиски/отправки и журнал атомарных файловых операций. `privacy map`
93
+ показывает размещение PII; `privacy export`, `redact` и `purge` дают контролируемый
94
+ экспорт и удаление. Redact/purge требуют точных подтверждений, а purge не запускается
95
+ без отдельной резервной копии.
81
96
 
82
97
  ## Выбор агентов
83
98
 
@@ -142,12 +157,14 @@ OpenCode учитывает `OPENCODE_CONFIG_DIR` и `XDG_CONFIG_HOME`; Hermes
142
157
  npm ci --ignore-scripts
143
158
  npm test
144
159
  npm run check
160
+ npm run pack:smoke
145
161
  ```
146
162
 
147
163
  Тесты работают с вымышленными данными и не отправляют реальные отклики.
148
164
 
149
165
  Руководства: [формат данных](plugins/jobhunt-kit/references/storage.md),
150
166
  [проверка резюме](plugins/jobhunt-kit/references/resume-guidance.md),
151
- [сопроводительные письма](plugins/jobhunt-kit/references/cover-guidance.md).
167
+ [сопроводительные письма](plugins/jobhunt-kit/references/cover-guidance.md),
168
+ [карта приватных данных](plugins/jobhunt-kit/references/privacy.md).
152
169
 
153
170
  Автор этого шаблона никак не аффилирован с Hirify.
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  import { copyFileSync, existsSync, lstatSync, mkdirSync, readFileSync, readdirSync, writeFileSync, realpathSync } from 'node:fs';
3
- import { dirname, join, relative, resolve } from 'node:path';
3
+ import { dirname, isAbsolute, join, relative, resolve, sep } from 'node:path';
4
4
  import { fileURLToPath, pathToFileURL } from 'node:url';
5
5
  import { spawnSync } from 'node:child_process';
6
6
  import { homedir } from 'node:os';
@@ -11,6 +11,10 @@ const SOURCE = resolve(dirname(fileURLToPath(import.meta.url)), '..');
11
11
  const roots = ['bin', 'installer-assets', 'plugins/jobhunt-kit', 'scripts', 'tests',
12
12
  'AGENTS.md', 'CLAUDE.md', 'README.md', 'package.json',
13
13
  '.agents/plugins/marketplace.json', '.claude-plugin/marketplace.json'];
14
+ export function pathInside(parent, child, pathApi = { relative, isAbsolute, sep }) {
15
+ const delta = pathApi.relative(parent, child);
16
+ return delta === '' || (!pathApi.isAbsolute(delta) && delta !== '..' && !delta.startsWith(`..${pathApi.sep}`));
17
+ }
14
18
  function collect(path) {
15
19
  const stat = lstatSync(path);
16
20
  if (stat.isSymbolicLink()) throw new Error(`Symlink in template: ${path}`);
@@ -33,7 +37,7 @@ export function install(destination, { source = SOURCE, installDependencies = tr
33
37
  if (scope === 'global' && !agents.length) throw new Error('Global installation requires at least one agent');
34
38
  const target = resolve(scope === 'global' ? home : destination);
35
39
  source = resolve(source);
36
- if (target === source || !relative(source, target).startsWith('..')) throw new Error('Choose a destination outside the installer package');
40
+ if (pathInside(source, target)) throw new Error('Choose a destination outside the installer package');
37
41
  const files = new Map();
38
42
  if (scope === 'project') {
39
43
  for (const root of roots) for (const path of collect(join(source, root))) files.set(relative(source, path), path);
@@ -94,7 +98,7 @@ export async function main(args) {
94
98
  return;
95
99
  }
96
100
  if (args.length === 0 || args.includes('--help') || args.includes('-h')) {
97
- console.log('Usage: jobhunt-kit <command> [arguments] [--workspace dir | --data dir]\n\ninstall [directory] Choose agents and install (default: ./my-jobhunt)\n --agents codex,cursor,... Select agents (or all); --providers is an alias\n agents List supported agents\n --scope project|global Workspace skills or user-wide skills\n --yes, -y Use detected agents and project scope\ninit <directory> Alias with an explicit destination\nprofile init|show|check Initialize/view/validate local profile\nprofile save --input file Save profile and clear confirmation\nprofile confirm --note text Record explicit candidate confirmation\nresume [file] Import file, fingerprint and mechanical checks\nresume check|reviewed Inspect file / record agent review (--input file)\nsearch [plan] Prepare context for agent; no live search\nsearch start|event|record|finish Persist search operations (--input file)\napply preview|export|begin slug Review/export/reserve application\napply prepare|approve|send|finish|resolve --input file\ntrack list|show slug|status slug --input file\nhistory | runs | report Inspect history or generate Markdown report\npolicy show|set --input file Inspect/set application policy\nschedule --input file Generate scheduler prompt; does not schedule\ndoctor Check local setup without network\n\nNode.js 24+ required. Default data: ./local/jobhunt-kit. Only apply send performs a live application call.');
101
+ console.log('Usage: jobhunt-kit <command> [arguments] [--workspace dir | --data dir]\n\ninstall [directory] Choose agents and install (default: ./my-jobhunt)\n --agents codex,cursor,... Select agents (or all); --providers is an alias\n agents List supported agents\n --scope project|global Workspace skills or user-wide skills\n --yes, -y Use detected agents and project scope\ninit <directory> Alias with an explicit destination\nprofile init|show|check Initialize/view/validate local profile\nprofile save --input file Save profile and clear confirmation\nprofile confirm --note text Record explicit candidate confirmation\nresume [file] Import file, fingerprint and mechanical checks\nresume check|reviewed Inspect file / record agent review (--input file)\nsearch [plan] Prepare context for agent; no live search\nsearch start|event|record|finish Persist search operations (--input file)\napply preview|export|begin slug Review/export/reserve application\napply prepare|approve|send|finish|resolve --input file\ntrack list|show slug|status slug --input file\nhistory | runs | report Inspect history or generate Markdown report\npolicy show|set --input file Inspect/set application policy\nschedule --input file Generate scheduler prompt; does not schedule\nbackup/backup-verify <directory> Create or verify a local backup\nrestore --input file Restore after exact typed confirmation\nrecover inspect|apply|packet Inspect, resolve, or recreate an attempt packet\nprivacy map|export|redact|purge Inspect, export or remove local PII\ndoctor Check schemas, database and recovery state without network\n\nNode.js 24+ required. Default data: ./local/jobhunt-kit. Only apply send performs a live application call.');
98
102
  return;
99
103
  }
100
104
  if (!['init', 'install'].includes(args[0])) {
@@ -20,3 +20,4 @@ local/
20
20
  __pycache__/
21
21
  .tmp/
22
22
  *.tgz
23
+ plan/
@@ -1,13 +1,14 @@
1
1
  {
2
2
  "name": "jobhunt-kit-plugin",
3
- "version": "0.4.0",
3
+ "version": "0.5.0",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "jobhunt-kit-plugin",
9
- "version": "0.4.0",
9
+ "version": "0.5.0",
10
10
  "dependencies": {
11
+ "ajv": "8.20.0",
11
12
  "hirify-cli": "0.4.5",
12
13
  "mammoth": "1.12.2",
13
14
  "pdf-parse": "2.4.5"
@@ -224,6 +225,22 @@
224
225
  "node": ">=10.0.0"
225
226
  }
226
227
  },
228
+ "node_modules/ajv": {
229
+ "version": "8.20.0",
230
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.20.0.tgz",
231
+ "integrity": "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==",
232
+ "license": "MIT",
233
+ "dependencies": {
234
+ "fast-deep-equal": "^3.1.3",
235
+ "fast-uri": "^3.0.1",
236
+ "json-schema-traverse": "^1.0.0",
237
+ "require-from-string": "^2.0.2"
238
+ },
239
+ "funding": {
240
+ "type": "github",
241
+ "url": "https://github.com/sponsors/epoberezkin"
242
+ }
243
+ },
227
244
  "node_modules/argparse": {
228
245
  "version": "1.0.10",
229
246
  "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
@@ -280,6 +297,28 @@
280
297
  "underscore": "^1.13.1"
281
298
  }
282
299
  },
300
+ "node_modules/fast-deep-equal": {
301
+ "version": "3.1.3",
302
+ "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
303
+ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
304
+ "license": "MIT"
305
+ },
306
+ "node_modules/fast-uri": {
307
+ "version": "3.1.7",
308
+ "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.7.tgz",
309
+ "integrity": "sha512-dOvZVzjdZdz7phd9v6jCbwxrBW3fK6n8Rc0CtdmM4bumzMnxywBYhuph6J819RRw/ku+rLbelwfMunktuzVVHg==",
310
+ "funding": [
311
+ {
312
+ "type": "github",
313
+ "url": "https://github.com/sponsors/fastify"
314
+ },
315
+ {
316
+ "type": "opencollective",
317
+ "url": "https://opencollective.com/fastify"
318
+ }
319
+ ],
320
+ "license": "BSD-3-Clause"
321
+ },
283
322
  "node_modules/hirify-cli": {
284
323
  "version": "0.4.5",
285
324
  "resolved": "https://registry.npmjs.org/hirify-cli/-/hirify-cli-0.4.5.tgz",
@@ -310,6 +349,12 @@
310
349
  "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==",
311
350
  "license": "MIT"
312
351
  },
352
+ "node_modules/json-schema-traverse": {
353
+ "version": "1.0.0",
354
+ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
355
+ "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
356
+ "license": "MIT"
357
+ },
313
358
  "node_modules/jszip": {
314
359
  "version": "3.10.1",
315
360
  "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz",
@@ -440,6 +485,15 @@
440
485
  "util-deprecate": "~1.0.1"
441
486
  }
442
487
  },
488
+ "node_modules/require-from-string": {
489
+ "version": "2.0.2",
490
+ "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz",
491
+ "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==",
492
+ "license": "MIT",
493
+ "engines": {
494
+ "node": ">=0.10.0"
495
+ }
496
+ },
443
497
  "node_modules/safe-buffer": {
444
498
  "version": "5.1.2",
445
499
  "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
@@ -1,13 +1,14 @@
1
1
  {
2
2
  "name": "jobhunt-kit",
3
- "version": "0.4.0",
3
+ "version": "0.5.0",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "jobhunt-kit",
9
- "version": "0.4.0",
9
+ "version": "0.5.0",
10
10
  "dependencies": {
11
+ "ajv": "8.20.0",
11
12
  "hirify-cli": "0.4.5",
12
13
  "mammoth": "1.12.2",
13
14
  "pdf-parse": "2.4.5"
@@ -227,6 +228,22 @@
227
228
  "node": ">=10.0.0"
228
229
  }
229
230
  },
231
+ "node_modules/ajv": {
232
+ "version": "8.20.0",
233
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.20.0.tgz",
234
+ "integrity": "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==",
235
+ "license": "MIT",
236
+ "dependencies": {
237
+ "fast-deep-equal": "^3.1.3",
238
+ "fast-uri": "^3.0.1",
239
+ "json-schema-traverse": "^1.0.0",
240
+ "require-from-string": "^2.0.2"
241
+ },
242
+ "funding": {
243
+ "type": "github",
244
+ "url": "https://github.com/sponsors/epoberezkin"
245
+ }
246
+ },
230
247
  "node_modules/argparse": {
231
248
  "version": "1.0.10",
232
249
  "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
@@ -283,6 +300,28 @@
283
300
  "underscore": "^1.13.1"
284
301
  }
285
302
  },
303
+ "node_modules/fast-deep-equal": {
304
+ "version": "3.1.3",
305
+ "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
306
+ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
307
+ "license": "MIT"
308
+ },
309
+ "node_modules/fast-uri": {
310
+ "version": "3.1.7",
311
+ "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.7.tgz",
312
+ "integrity": "sha512-dOvZVzjdZdz7phd9v6jCbwxrBW3fK6n8Rc0CtdmM4bumzMnxywBYhuph6J819RRw/ku+rLbelwfMunktuzVVHg==",
313
+ "funding": [
314
+ {
315
+ "type": "github",
316
+ "url": "https://github.com/sponsors/fastify"
317
+ },
318
+ {
319
+ "type": "opencollective",
320
+ "url": "https://opencollective.com/fastify"
321
+ }
322
+ ],
323
+ "license": "BSD-3-Clause"
324
+ },
286
325
  "node_modules/hirify-cli": {
287
326
  "version": "0.4.5",
288
327
  "resolved": "https://registry.npmjs.org/hirify-cli/-/hirify-cli-0.4.5.tgz",
@@ -313,6 +352,12 @@
313
352
  "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==",
314
353
  "license": "MIT"
315
354
  },
355
+ "node_modules/json-schema-traverse": {
356
+ "version": "1.0.0",
357
+ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
358
+ "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
359
+ "license": "MIT"
360
+ },
316
361
  "node_modules/jszip": {
317
362
  "version": "3.10.1",
318
363
  "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz",
@@ -443,6 +488,15 @@
443
488
  "util-deprecate": "~1.0.1"
444
489
  }
445
490
  },
491
+ "node_modules/require-from-string": {
492
+ "version": "2.0.2",
493
+ "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz",
494
+ "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==",
495
+ "license": "MIT",
496
+ "engines": {
497
+ "node": ">=0.10.0"
498
+ }
499
+ },
446
500
  "node_modules/safe-buffer": {
447
501
  "version": "5.1.2",
448
502
  "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jobhunt-kit",
3
- "version": "0.4.0",
3
+ "version": "0.5.0",
4
4
  "bin": {
5
5
  "jobhunt-kit": "bin/jobhunt-kit.mjs"
6
6
  },
@@ -19,12 +19,14 @@
19
19
  "hirify": "hirify",
20
20
  "test": "node --test tests/*.test.mjs",
21
21
  "check": "node scripts/check-package.mjs",
22
+ "pack:smoke": "node scripts/pack-smoke.mjs",
22
23
  "prepublishOnly": "npm test && npm run check"
23
24
  },
24
25
  "engines": {
25
26
  "node": ">=24.0.0"
26
27
  },
27
28
  "dependencies": {
29
+ "ajv": "8.20.0",
28
30
  "hirify-cli": "0.4.5",
29
31
  "mammoth": "1.12.2",
30
32
  "pdf-parse": "2.4.5"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jobhunt-kit",
3
- "version": "0.4.0",
3
+ "version": "0.5.0",
4
4
  "description": "Reusable candidate intake, resume review, Hirify search and local application tracking",
5
5
  "author": {
6
6
  "name": "Jobhunt Kit contributors"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jobhunt-kit",
3
- "version": "0.4.0",
3
+ "version": "0.5.0",
4
4
  "description": "Reusable candidate intake, resume review, Hirify search and local application tracking",
5
5
  "author": {
6
6
  "name": "Jobhunt Kit contributors"
@@ -1,13 +1,14 @@
1
1
  {
2
2
  "name": "jobhunt-kit-plugin",
3
- "version": "0.4.0",
3
+ "version": "0.5.0",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "jobhunt-kit-plugin",
9
- "version": "0.4.0",
9
+ "version": "0.5.0",
10
10
  "dependencies": {
11
+ "ajv": "8.20.0",
11
12
  "hirify-cli": "0.4.5",
12
13
  "mammoth": "1.12.2",
13
14
  "pdf-parse": "2.4.5"
@@ -224,6 +225,22 @@
224
225
  "node": ">=10.0.0"
225
226
  }
226
227
  },
228
+ "node_modules/ajv": {
229
+ "version": "8.20.0",
230
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.20.0.tgz",
231
+ "integrity": "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==",
232
+ "license": "MIT",
233
+ "dependencies": {
234
+ "fast-deep-equal": "^3.1.3",
235
+ "fast-uri": "^3.0.1",
236
+ "json-schema-traverse": "^1.0.0",
237
+ "require-from-string": "^2.0.2"
238
+ },
239
+ "funding": {
240
+ "type": "github",
241
+ "url": "https://github.com/sponsors/epoberezkin"
242
+ }
243
+ },
227
244
  "node_modules/argparse": {
228
245
  "version": "1.0.10",
229
246
  "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
@@ -280,6 +297,28 @@
280
297
  "underscore": "^1.13.1"
281
298
  }
282
299
  },
300
+ "node_modules/fast-deep-equal": {
301
+ "version": "3.1.3",
302
+ "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
303
+ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
304
+ "license": "MIT"
305
+ },
306
+ "node_modules/fast-uri": {
307
+ "version": "3.1.7",
308
+ "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.7.tgz",
309
+ "integrity": "sha512-dOvZVzjdZdz7phd9v6jCbwxrBW3fK6n8Rc0CtdmM4bumzMnxywBYhuph6J819RRw/ku+rLbelwfMunktuzVVHg==",
310
+ "funding": [
311
+ {
312
+ "type": "github",
313
+ "url": "https://github.com/sponsors/fastify"
314
+ },
315
+ {
316
+ "type": "opencollective",
317
+ "url": "https://opencollective.com/fastify"
318
+ }
319
+ ],
320
+ "license": "BSD-3-Clause"
321
+ },
283
322
  "node_modules/hirify-cli": {
284
323
  "version": "0.4.5",
285
324
  "resolved": "https://registry.npmjs.org/hirify-cli/-/hirify-cli-0.4.5.tgz",
@@ -310,6 +349,12 @@
310
349
  "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==",
311
350
  "license": "MIT"
312
351
  },
352
+ "node_modules/json-schema-traverse": {
353
+ "version": "1.0.0",
354
+ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
355
+ "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
356
+ "license": "MIT"
357
+ },
313
358
  "node_modules/jszip": {
314
359
  "version": "3.10.1",
315
360
  "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz",
@@ -440,6 +485,15 @@
440
485
  "util-deprecate": "~1.0.1"
441
486
  }
442
487
  },
488
+ "node_modules/require-from-string": {
489
+ "version": "2.0.2",
490
+ "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz",
491
+ "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==",
492
+ "license": "MIT",
493
+ "engines": {
494
+ "node": ">=0.10.0"
495
+ }
496
+ },
443
497
  "node_modules/safe-buffer": {
444
498
  "version": "5.1.2",
445
499
  "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
@@ -1,12 +1,13 @@
1
1
  {
2
2
  "name": "jobhunt-kit-plugin",
3
- "version": "0.4.0",
3
+ "version": "0.5.0",
4
4
  "private": true,
5
5
  "type": "module",
6
6
  "engines": {
7
7
  "node": ">=24.0.0"
8
8
  },
9
9
  "dependencies": {
10
+ "ajv": "8.20.0",
10
11
  "hirify-cli": "0.4.5",
11
12
  "mammoth": "1.12.2",
12
13
  "pdf-parse": "2.4.5"
@@ -25,7 +25,7 @@
25
25
  | job-resume | Копия по SHA-256, извлечение PDF/DOCX/TXT/MD, простые проверки текста, сохранение отчёта, запись завершённой проверки | Визуальная оценка, истинность фактов, соответствие роли и содержательные правки |
26
26
  | job-search | Сбор профиля, бюджетов и известных вакансий; журнал запусков/результатов, запрет второго активного запуска через CLI | Актуальные фильтры Hirify, preview, живой поиск, оценка требований и ранжирование |
27
27
  | job-apply | Снимок черновика, экспорт текста/ответов, согласование, резервирование и однократный вызов CLI, запись исхода | Написание письма из фактов, проверка живого профиля/квоты, получение разрешения, разбор ответа сервиса |
28
- | job-track | Списки/история/статусы/Markdown; заполнение prompt для расписания | Интерпретация сообщения кандидата, создание задачи в планировщике после запроса |
28
+ | job-track | Списки/история/статусы/Markdown, integrity/recovery, backup/restore, privacy-команды; заполнение prompt для расписания | Интерпретация сообщения кандидата, создание задачи в планировщике после запроса |
29
29
 
30
30
  Технические функции: `profile.mjs`, `resume.mjs`, `extract-resume.mjs`, `commands.mjs`;
31
31
  история и защита отправки переиспользуют `tracker.mjs` и `send-packet.mjs`.
@@ -129,3 +129,29 @@ schedule.json: `{"frequency":"По будням в 09:00","timezone":"Europe/Ber
129
129
  materials/scheduled-search.md, а не включённая задача; auto-разрешение не выдаётся.
130
130
  Нужен --workspace с установленным шаблоном, чтобы prompt не ссылался на временный
131
131
  кеш npx. Планировщик выбирает агент по явному запросу пользователя.
132
+
133
+ ## Целостность, резервирование и приватность
134
+
135
+ ```text
136
+ npx jobhunt-kit doctor
137
+ npx jobhunt-kit recover inspect
138
+ npx jobhunt-kit recover apply --input recovery.json
139
+ npx jobhunt-kit recover packet <attempt_id>
140
+ npx jobhunt-kit backup ../jobhunt-backup
141
+ npx jobhunt-kit backup-verify ../jobhunt-backup
142
+ npx jobhunt-kit restore --input restore.json
143
+ npx jobhunt-kit privacy map
144
+ npx jobhunt-kit privacy export ../jobhunt-private-export
145
+ npx jobhunt-kit privacy redact --input redact.json
146
+ npx jobhunt-kit privacy purge --input purge.json
147
+ ```
148
+
149
+ `doctor` проверяет JSON Schema, SQLite integrity/foreign keys, активный run,
150
+ неразрешённые attempts и незавершённые файловые операции. Recovery ничего не угадывает:
151
+ для run нужны id/status/reason, для attempt — slug/outcome/evidence.
152
+
153
+ Restore требует `{"source":"...","confirmation":"RESTORE VERIFIED JOBHUNT BACKUP"}`
154
+ и перед заменой создаёт автоматический сырой pre-restore quarantine-снимок. Redact требует
155
+ `REDACT LOCAL JOBHUNT PII` и массив dotted profile fields. Purge требует фразу
156
+ `PURGE LOCAL JOBHUNT DATA`, scopes и отдельный backup_directory; удаление без
157
+ проверяемой резервной копии не выполняется.
@@ -0,0 +1,24 @@
1
+ # Локальные данные и приватность
2
+
3
+ Jobhunt Kit не шифрует данные. Рабочая папка `D` должна быть доступна только
4
+ пользователю и не должна синхронизироваться или публиковаться без его решения.
5
+
6
+ | Путь | Возможные персональные данные | Назначение |
7
+ |---|---|---|
8
+ | `profile.json` | Имя, контакты, опыт, ответы, разрешения на работу | Авторитетный профиль кандидата |
9
+ | `policy.json` | Текст auto-разрешения, срок и лимит | Авторитетная политика отправки |
10
+ | `history.sqlite` | Вакансии, письма, ответы, approvals, application snapshots; legacy v1 мог хранить полные снимки профиля | Авторитетная история |
11
+ | `resumes/` | Неизменяемые копии резюме | Проверка и отправочный контекст |
12
+ | `materials/` | Извлечённый текст, письма, ответы, attempt packets | Рабочие материалы агента |
13
+ | `reports/` | Названия вакансий, ссылки и статусы | Производное представление истории |
14
+
15
+ `privacy map` возвращает абсолютные пути этой карты. `privacy export` создаёт ту же
16
+ проверяемую копию, что backup. `privacy redact` меняет перечисленные dotted-поля
17
+ профиля, сбрасывает подтверждение и удаляет полные profile snapshots/user authorization
18
+ из legacy events; для полного удаления истории использовать purge scope `history`.
19
+
20
+ `privacy purge` требует точную фразу подтверждения и отдельный `backup_directory`.
21
+ До удаления создаётся и проверяется полная резервная копия. Scopes: `profile`,
22
+ `history`, `resumes`, `materials`, `reports`. Очистка `history` всегда очищает и
23
+ производные `reports`, отзывает auto-разрешение и подтверждение профиля. Purge не удаляет внешний файл, из которого ранее
24
+ импортировали резюме, и не отзывает уже отправленные заявления на стороне Hirify.
@@ -30,7 +30,8 @@ P/D — абсолютные пути из workflow.md. JSON-ответы ком
30
30
  Дата confirmed_at — ISO 8601. Profile-команда отдаёт hash текущего JSON для разрешения
31
31
  auto. Хеш зависит от сериализации объекта: изменение/перестановка ключей может
32
32
  потребовать нового согласования (консервативное поведение).
33
- resume.path — путь относительно D или абсолютный локальный; sha256 — байтов файла.
33
+ resume.path — только относительный путь внутри D, штатно `resumes/<sha>.<ext>`;
34
+ внешний файл сначала импортируется командой resume. sha256 — хеш байтов файла.
34
35
  Пример вычисления в PowerShell: `(Get-FileHash <file> -Algorithm SHA256).Hash.ToLower()`.
35
36
  readiness и confirmed — утверждения агента по фактической проверке, не автоматическая
36
37
  проверка истинности биографии. Не отмечать их только ради прохождения команды.
@@ -109,12 +110,17 @@ External или недостающие ответы → needs_user. После
109
110
  можно dismissed. После отправки: interview / rejected / withdrawn / offer;
110
111
  из offer: accepted / declined / withdrawn. Status — локальная запись, не действие сервиса.
111
112
 
112
- Схема SQLite v1: runs, jobs, aliases, observations, attempts, dispatches, events.
113
+ Схема SQLite v2: runs, jobs, aliases, observations, attempts, dispatches, events,
114
+ file_revisions и pending_file_writes. Миграция v1→v2 сохраняет данные, закрывает
115
+ старые дублирующиеся running runs, записывается в schema_migrations и добавляет
116
+ immutable application snapshots.
113
117
  В attempts UNIQUE(job_id), в dispatches PRIMARY KEY(attempt_id): повтор не проходит
114
118
  после перезапуска процесса. Сбой после намерения требует ручного разбора.
115
119
  Нет универсальной транзакции между SQLite и Hirify: невозможно обещать exactly-once
116
120
  доставку через сеть. Здесь предотвращаются автоматические повторные отправки.
117
121
 
118
- Историю читать через команды. Для резервирования закрыть работающие процессы и
119
- сохранить весь D. Обновление схемы пока не требуется: это первая версия; будущий
120
- апгрейд обязан иметь явную миграцию и резервную копию.
122
+ Историю читать через команды. Для резервирования использовать backup и backup-verify.
123
+ Restore сначала создаёт сырой автоматический pre-restore quarantine-снимок, не требующий
124
+ валидности текущих данных. Перед активацией восстановленной истории restore отзывает
125
+ auto-разрешение и подтверждение профиля. Doctor проверяет JSON Schema,
126
+ SQLite, foreign keys, незавершённые runs/attempts и журнал атомарных файловых операций.
@@ -0,0 +1,13 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "jobhunt-kit/draft",
4
+ "type": "object",
5
+ "required": ["slug", "cover_letter", "hirify_profile_id", "claims", "answers", "unresolved"],
6
+ "properties": {
7
+ "slug": { "type": "string", "minLength": 1 }, "cover_letter": { "type": "string", "minLength": 1 },
8
+ "hirify_profile_id": { "type": ["integer", "null"], "minimum": 1 },
9
+ "claims": { "type": "array", "items": { "type": "object", "required": ["text", "evidence_id"], "properties": { "text": { "type": "string", "minLength": 1 }, "evidence_id": { "type": "string", "minLength": 1 } }, "additionalProperties": false } },
10
+ "answers": { "type": "array" }, "unresolved": { "type": "array", "items": { "type": "string", "minLength": 1 } }
11
+ },
12
+ "additionalProperties": true
13
+ }
@@ -0,0 +1,11 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "jobhunt-kit/finish",
4
+ "type": "object",
5
+ "required": ["attempt_id", "outcome", "evidence"],
6
+ "properties": {
7
+ "attempt_id": { "type": "string", "minLength": 1 }, "outcome": { "enum": ["submitted", "failed", "unknown"] },
8
+ "evidence": { "type": "string", "minLength": 1 }, "application_id": { "type": "string", "minLength": 1 }
9
+ },
10
+ "additionalProperties": true
11
+ }
@@ -0,0 +1,36 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "jobhunt-kit/policy",
4
+ "type": "object",
5
+ "required": ["schema_version", "mode", "search", "auto"],
6
+ "properties": {
7
+ "schema_version": { "const": 1 },
8
+ "mode": { "enum": ["review_each", "auto"] },
9
+ "search": {
10
+ "type": "object",
11
+ "required": ["target", "max_cards", "max_reads", "max_reveals", "max_minutes"],
12
+ "properties": {
13
+ "target": { "type": "integer", "minimum": 1 }, "max_cards": { "type": "integer", "minimum": 1 },
14
+ "max_reads": { "type": "integer", "minimum": 1 }, "max_reveals": { "type": "integer", "minimum": 0 },
15
+ "max_minutes": { "type": "integer", "minimum": 1 }
16
+ },
17
+ "additionalProperties": false
18
+ },
19
+ "auto": {
20
+ "anyOf": [
21
+ { "type": "null" },
22
+ {
23
+ "type": "object", "required": ["user_authorization", "profile_hash", "expires_at", "max_attempts_per_utc_day"],
24
+ "properties": {
25
+ "user_authorization": { "type": "string", "minLength": 1 },
26
+ "profile_hash": { "type": "string", "pattern": "^[a-f0-9]{64}$" },
27
+ "expires_at": { "type": "string", "format": "date-time" },
28
+ "max_attempts_per_utc_day": { "type": "integer", "minimum": 1 }
29
+ },
30
+ "additionalProperties": false
31
+ }
32
+ ]
33
+ }
34
+ },
35
+ "additionalProperties": false
36
+ }