job_ops-mcp 0.3.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 (116) hide show
  1. package/.env.example +33 -0
  2. package/LICENSE +21 -0
  3. package/README.md +400 -0
  4. package/config/profile.example.yml +67 -0
  5. package/cv.example.md +53 -0
  6. package/dist/cli.js +385 -0
  7. package/dist/cli.js.map +1 -0
  8. package/dist/config.js +63 -0
  9. package/dist/config.js.map +1 -0
  10. package/dist/core/browser.js +27 -0
  11. package/dist/core/browser.js.map +1 -0
  12. package/dist/core/content_hash.js +11 -0
  13. package/dist/core/content_hash.js.map +1 -0
  14. package/dist/core/csv.js +107 -0
  15. package/dist/core/csv.js.map +1 -0
  16. package/dist/core/cv_parse.js +201 -0
  17. package/dist/core/cv_parse.js.map +1 -0
  18. package/dist/core/html.js +10 -0
  19. package/dist/core/html.js.map +1 -0
  20. package/dist/core/jd_normalize.js +99 -0
  21. package/dist/core/jd_normalize.js.map +1 -0
  22. package/dist/core/jobs.js +106 -0
  23. package/dist/core/jobs.js.map +1 -0
  24. package/dist/core/llm.js +227 -0
  25. package/dist/core/llm.js.map +1 -0
  26. package/dist/core/modes.js +55 -0
  27. package/dist/core/modes.js.map +1 -0
  28. package/dist/core/outreach_safety.js +77 -0
  29. package/dist/core/outreach_safety.js.map +1 -0
  30. package/dist/core/profile.js +88 -0
  31. package/dist/core/profile.js.map +1 -0
  32. package/dist/core/providers/amazon.js +36 -0
  33. package/dist/core/providers/amazon.js.map +1 -0
  34. package/dist/core/providers/ashby.js +31 -0
  35. package/dist/core/providers/ashby.js.map +1 -0
  36. package/dist/core/providers/google.js +46 -0
  37. package/dist/core/providers/google.js.map +1 -0
  38. package/dist/core/providers/greenhouse.js +55 -0
  39. package/dist/core/providers/greenhouse.js.map +1 -0
  40. package/dist/core/providers/http.js +36 -0
  41. package/dist/core/providers/http.js.map +1 -0
  42. package/dist/core/providers/index.js +53 -0
  43. package/dist/core/providers/index.js.map +1 -0
  44. package/dist/core/providers/lever.js +32 -0
  45. package/dist/core/providers/lever.js.map +1 -0
  46. package/dist/core/providers/playwright_generic.js +53 -0
  47. package/dist/core/providers/playwright_generic.js.map +1 -0
  48. package/dist/core/providers/types.js +2 -0
  49. package/dist/core/providers/types.js.map +1 -0
  50. package/dist/core/providers/workday.js +44 -0
  51. package/dist/core/providers/workday.js.map +1 -0
  52. package/dist/core/render.js +253 -0
  53. package/dist/core/render.js.map +1 -0
  54. package/dist/core/reports.js +257 -0
  55. package/dist/core/reports.js.map +1 -0
  56. package/dist/core/resources.js +40 -0
  57. package/dist/core/resources.js.map +1 -0
  58. package/dist/core/scan_engine.js +164 -0
  59. package/dist/core/scan_engine.js.map +1 -0
  60. package/dist/core/scheduler.js +117 -0
  61. package/dist/core/scheduler.js.map +1 -0
  62. package/dist/db.js +60 -0
  63. package/dist/db.js.map +1 -0
  64. package/dist/http/app.js +35 -0
  65. package/dist/http/app.js.map +1 -0
  66. package/dist/http/dashboard.js +131 -0
  67. package/dist/http/dashboard.js.map +1 -0
  68. package/dist/mcp/define.js +35 -0
  69. package/dist/mcp/define.js.map +1 -0
  70. package/dist/mcp/server.js +103 -0
  71. package/dist/mcp/server.js.map +1 -0
  72. package/dist/mcp/tools/apply_prefill.js +167 -0
  73. package/dist/mcp/tools/apply_prefill.js.map +1 -0
  74. package/dist/mcp/tools/batch_evaluate.js +143 -0
  75. package/dist/mcp/tools/batch_evaluate.js.map +1 -0
  76. package/dist/mcp/tools/evaluate_job.js +181 -0
  77. package/dist/mcp/tools/evaluate_job.js.map +1 -0
  78. package/dist/mcp/tools/generate_materials.js +126 -0
  79. package/dist/mcp/tools/generate_materials.js.map +1 -0
  80. package/dist/mcp/tools/get_report.js +24 -0
  81. package/dist/mcp/tools/get_report.js.map +1 -0
  82. package/dist/mcp/tools/ops.js +321 -0
  83. package/dist/mcp/tools/ops.js.map +1 -0
  84. package/dist/mcp/tools/outreach.js +481 -0
  85. package/dist/mcp/tools/outreach.js.map +1 -0
  86. package/dist/mcp/tools/render_pdf.js +27 -0
  87. package/dist/mcp/tools/render_pdf.js.map +1 -0
  88. package/dist/mcp/tools/scan_portals.js +35 -0
  89. package/dist/mcp/tools/scan_portals.js.map +1 -0
  90. package/dist/mcp/tools/scheduler.js +32 -0
  91. package/dist/mcp/tools/scheduler.js.map +1 -0
  92. package/dist/mcp/tools/stories.js +172 -0
  93. package/dist/mcp/tools/stories.js.map +1 -0
  94. package/dist/mcp/tools/tracker.js +183 -0
  95. package/dist/mcp/tools/tracker.js.map +1 -0
  96. package/dist/mcp/tools/visa.js +219 -0
  97. package/dist/mcp/tools/visa.js.map +1 -0
  98. package/dist/migrations/001_initial.sql +505 -0
  99. package/dist/migrations/002_llm_and_digest.sql +42 -0
  100. package/dist/server.js +55 -0
  101. package/dist/server.js.map +1 -0
  102. package/fonts/dm-sans-latin-ext.woff2 +0 -0
  103. package/fonts/dm-sans-latin.woff2 +0 -0
  104. package/fonts/space-grotesk-latin-ext.woff2 +0 -0
  105. package/fonts/space-grotesk-latin.woff2 +0 -0
  106. package/modes/career_packet.md +91 -0
  107. package/modes/negotiation_playbook.md +64 -0
  108. package/modes/outreach_tone.md +80 -0
  109. package/modes/report_format.md +83 -0
  110. package/modes/rubric.md +119 -0
  111. package/modes/tailoring_rules.md +102 -0
  112. package/package.json +67 -0
  113. package/portals.example.yml +95 -0
  114. package/templates/cover-template.html +64 -0
  115. package/templates/cv-template.html +421 -0
  116. package/templates/cv-template.tex +123 -0
@@ -0,0 +1,172 @@
1
+ // G5 — extract_stories, get_story_bank, negotiation_brief.
2
+ import { z } from 'zod';
3
+ import { randomUUID } from 'node:crypto';
4
+ import { config } from '../../config.js';
5
+ import { getDb, runInWriteLock } from '../../db.js';
6
+ import { defineTool, okResult, errResult } from '../define.js';
7
+ import { getLatestReport } from '../../core/reports.js';
8
+ import { getMode } from '../../core/modes.js';
9
+ // ── extract_stories ──────────────────────────────────────────────────────────
10
+ //
11
+ // Reads block_interview from the latest eval_report for the job, parses STAR + Reflection
12
+ // rows out of the markdown table, and appends them to story_bank. The chat client can
13
+ // optionally pre-extract by passing `stories` directly (skips parsing).
14
+ const storyShape = z.object({
15
+ story_text: z.string().min(1),
16
+ reflection: z.string().nullish(),
17
+ competency_tags: z.array(z.string()).nullish(),
18
+ });
19
+ export const extractStoriesTool = defineTool({
20
+ name: 'extract_stories',
21
+ title: 'Derive STAR+R stories from a job evaluation',
22
+ description: 'Pulls STAR + Reflection rows out of the latest eval_report.block_interview for the job and inserts them into story_bank. ' +
23
+ 'Pass `stories` to skip parsing and append a hand-curated list instead.',
24
+ inputSchema: {
25
+ job_id: z.string().min(1),
26
+ stories: z.array(storyShape).optional(),
27
+ },
28
+ handler: async (args) => {
29
+ let stories = args.stories ?? null;
30
+ if (!stories) {
31
+ const report = getLatestReport(args.job_id);
32
+ if (!report)
33
+ return errResult(`No eval report for ${args.job_id} — run evaluate_job first.`);
34
+ stories = parseStoriesFromBlockF(report.block_interview ?? '');
35
+ if (!stories.length) {
36
+ return okResult({
37
+ inserted: 0,
38
+ note: 'No STAR+R rows found in the markdown table. Re-run with `stories` to supply them manually, or finalize a richer block_interview.',
39
+ block_interview_chars: (report.block_interview ?? '').length,
40
+ });
41
+ }
42
+ }
43
+ const insertedIds = await runInWriteLock(() => {
44
+ const db = getDb();
45
+ const stmt = db.prepare(`
46
+ INSERT INTO story_bank (id, job_id, story_text, reflection, competency_tags)
47
+ VALUES (?, ?, ?, ?, ?)
48
+ `);
49
+ const ids = [];
50
+ for (const s of stories) {
51
+ const id = randomUUID();
52
+ stmt.run(id, args.job_id, s.story_text, s.reflection ?? null, s.competency_tags ? JSON.stringify(s.competency_tags) : null);
53
+ ids.push(id);
54
+ }
55
+ return ids;
56
+ });
57
+ return okResult({ inserted: insertedIds.length, ids: insertedIds });
58
+ },
59
+ });
60
+ // Heuristic parser — looks for | … | … | … | rows after a header containing 'Story' / 'STAR' / 'Reflection'.
61
+ function parseStoriesFromBlockF(md) {
62
+ const lines = md.split('\n');
63
+ const out = [];
64
+ let pastHeader = false;
65
+ for (const line of lines) {
66
+ if (!pastHeader) {
67
+ if (/^\s*\|\s*-+\s*\|/.test(line)) {
68
+ pastHeader = true;
69
+ continue;
70
+ }
71
+ continue;
72
+ }
73
+ if (!line.includes('|'))
74
+ continue;
75
+ const cells = line.split('|').slice(1, -1).map(c => c.trim()).filter(Boolean);
76
+ if (cells.length < 2)
77
+ continue;
78
+ // The shape is roughly: [#, Requirement, Story, S, T, A, R, Reflection] OR [#, Requirement, Story, Reflection]
79
+ const story = cells.slice(0, -1).join(' — ');
80
+ const reflection = cells[cells.length - 1];
81
+ const tags = extractCompetencyTags(story + ' ' + reflection);
82
+ out.push({ story_text: story, reflection: reflection || null, competency_tags: tags.length ? tags : null });
83
+ }
84
+ return out;
85
+ }
86
+ const COMPETENCY_VOCAB = [
87
+ 'leadership', 'ownership', 'ambiguity', 'prioritization', 'stakeholder', 'cross-functional',
88
+ 'systems design', 'agentic', 'llm', 'evals', 'observability', 'data engineering', 'sql', 'product discovery',
89
+ 'roadmap', 'metrics', 'a/b testing', 'negotiation', 'customer-facing', 'delivery speed',
90
+ ];
91
+ function extractCompetencyTags(s) {
92
+ const lower = s.toLowerCase();
93
+ return [...new Set(COMPETENCY_VOCAB.filter(v => lower.includes(v)))];
94
+ }
95
+ // ── get_story_bank ───────────────────────────────────────────────────────────
96
+ export const getStoryBankTool = defineTool({
97
+ name: 'get_story_bank',
98
+ title: 'Story bank',
99
+ description: 'Returns accumulated STAR+R stories. Optional competency filter (substring match on tags).',
100
+ inputSchema: {
101
+ competency: z.string().optional(),
102
+ limit: z.number().int().min(1).max(500).default(100),
103
+ },
104
+ handler: async (args) => {
105
+ const rows = getDb().prepare(`
106
+ SELECT s.id, s.story_text, s.reflection, s.competency_tags, s.created_at,
107
+ s.job_id, j.title AS job_title, COALESCE(c.name, j.company_name_raw) AS company_name
108
+ FROM story_bank s
109
+ LEFT JOIN jobs j ON j.id = s.job_id
110
+ LEFT JOIN companies c ON c.id = j.company_id
111
+ ORDER BY datetime(s.created_at) DESC
112
+ LIMIT ?
113
+ `).all(args.limit);
114
+ const filtered = args.competency
115
+ ? rows.filter(r => (r.competency_tags ?? '').toLowerCase().includes(args.competency.toLowerCase()))
116
+ : rows;
117
+ return okResult({
118
+ count: filtered.length,
119
+ items: filtered.map(r => ({
120
+ ...r,
121
+ competency_tags: r.competency_tags ? JSON.parse(r.competency_tags) : null,
122
+ })),
123
+ });
124
+ },
125
+ });
126
+ // ── negotiation_brief ────────────────────────────────────────────────────────
127
+ export const negotiationBriefTool = defineTool({
128
+ name: 'negotiation_brief',
129
+ title: 'Negotiation brief for a job',
130
+ description: 'Returns the negotiation_playbook + the most recent comp enrichment for the company + a draft framework. ' +
131
+ 'Chat-mode default; api-mode tailors via LLM.',
132
+ inputSchema: {
133
+ job_id: z.string().min(1),
134
+ mode: z.enum(['chat', 'api']).default('chat'),
135
+ },
136
+ handler: async (args) => {
137
+ const db = getDb();
138
+ const job = db.prepare(`
139
+ SELECT j.*, COALESCE(c.name, j.company_name_raw) AS company_name, c.id AS resolved_company_id
140
+ FROM jobs j LEFT JOIN companies c ON c.id = j.company_id WHERE j.id = ?
141
+ `).get(args.job_id);
142
+ if (!job)
143
+ return errResult(`No job ${args.job_id}`);
144
+ const enrichment = db.prepare(`
145
+ SELECT kind, summary, confidence_score, signal_quality, source_urls
146
+ FROM enrichment WHERE company_id = ? AND kind = 'comp' AND datetime(expires_at) > datetime('now')
147
+ `).get(job.resolved_company_id);
148
+ const playbook = getMode('negotiation_playbook.md');
149
+ return okResult({
150
+ job: { id: job.id, title: job.title, company: job.company_name,
151
+ location: job.location_raw, comp_min_usd: job.comp_min_usd, comp_max_usd: job.comp_max_usd },
152
+ comp_enrichment: enrichment ?? null,
153
+ playbook,
154
+ framework: {
155
+ anchor: 'Total comp (base + sign-on + equity + bonus + benefits), never base alone',
156
+ pillars: [
157
+ 'Salary framework anchored on market band',
158
+ 'Geographic-discount pushback (challenge silent geo adjustments)',
159
+ 'Competing-offer leverage only when REAL (named company, named stage)',
160
+ ],
161
+ knobs: ['sign-on', 'equity refresh', 'title', 'remote stipend', 'start date', '6-month review'],
162
+ hard_rules: [
163
+ 'Never accept verbally same-day',
164
+ 'Visa / OPT stays out of negotiation until comp is in writing',
165
+ 'Do not negotiate against yourself',
166
+ ],
167
+ },
168
+ tracker_url: `${config.baseUrl}/`,
169
+ });
170
+ },
171
+ });
172
+ //# sourceMappingURL=stories.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stories.js","sourceRoot":"","sources":["../../../src/mcp/tools/stories.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,183 @@
1
+ // G1 — read tools + state transitions on jobs.
2
+ //
3
+ // All writes serialize through runInWriteLock (db.ts).
4
+ import { z } from 'zod';
5
+ import { config } from '../../config.js';
6
+ import { getDb, runInWriteLock } from '../../db.js';
7
+ import { defineTool, okResult, errResult } from '../define.js';
8
+ import { safeJson } from '../../core/llm.js';
9
+ // Allowed transitions — denylist of obvious impossibilities; we don't enforce a strict
10
+ // state machine because the brief allows manual overrides. The CHECK on jobs.status is
11
+ // the hard guard.
12
+ const STATUSES = [
13
+ 'sourced', 'ready_to_apply', 'materials_drafted', 'ready_to_review',
14
+ 'applied', 'screen', 'onsite', 'offer', 'rejected', 'discarded', 'skip',
15
+ ];
16
+ const ROLE_CATEGORIES = ['pm', 'ml_eng', 'data_eng', 'analytics_eng', 'swe', 'forward_deployed', 'other'];
17
+ // ── get_top_jobs ─────────────────────────────────────────────────────────────
18
+ export const getTopJobsTool = defineTool({
19
+ name: 'get_top_jobs',
20
+ title: 'Top scored jobs (triage)',
21
+ description: 'Triage view of rated jobs, filterable by min score, role_category, and status. Default min_score=75, limit=20. ' +
22
+ 'Use a non-default `role_category` to get a per-role lane (e.g. PM-only).',
23
+ inputSchema: {
24
+ min_score: z.number().int().min(0).max(100).default(75),
25
+ role_category: z.enum(ROLE_CATEGORIES).optional(),
26
+ status: z.enum(STATUSES).optional(),
27
+ limit: z.number().int().min(1).max(200).default(20),
28
+ },
29
+ handler: async (args) => {
30
+ const where = ['j.score_total IS NOT NULL', 'j.score_total >= ?'];
31
+ const params = [args.min_score];
32
+ if (args.role_category) {
33
+ where.push('j.role_category = ?');
34
+ params.push(args.role_category);
35
+ }
36
+ if (args.status) {
37
+ where.push('j.status = ?');
38
+ params.push(args.status);
39
+ }
40
+ const rows = getDb().prepare(`
41
+ SELECT
42
+ j.id AS job_id,
43
+ j.title,
44
+ COALESCE(c.name, j.company_name_raw) AS company_name,
45
+ j.score_total, j.score_resume_fit, j.score_taste_fit, j.score_visa_fit,
46
+ j.role_category, j.seniority, j.location_raw AS location,
47
+ j.status, j.source_url, j.discovered_at, j.scored_at,
48
+ (SELECT er.html_path FROM eval_reports er WHERE er.job_id = j.id ORDER BY er.created_at DESC LIMIT 1) AS report_html
49
+ FROM jobs j
50
+ LEFT JOIN companies c ON c.id = j.company_id
51
+ WHERE ${where.join(' AND ')}
52
+ ORDER BY j.score_total DESC, datetime(j.discovered_at) DESC
53
+ LIMIT ?
54
+ `).all(...params, args.limit);
55
+ const items = rows.map(r => {
56
+ const out = {
57
+ ...r,
58
+ report_url: r.report_html ? `${config.baseUrl}/files/${r.report_html}` : null,
59
+ report_html: undefined,
60
+ };
61
+ if (!config.visaScoringEnabled)
62
+ delete out.score_visa_fit;
63
+ return out;
64
+ });
65
+ return okResult({ count: items.length, min_score: args.min_score, items });
66
+ },
67
+ });
68
+ // ── get_tracker ──────────────────────────────────────────────────────────────
69
+ export const getTrackerTool = defineTool({
70
+ name: 'get_tracker',
71
+ title: 'Tracker view (JSON)',
72
+ description: 'Full pipeline snapshot as JSON. Optional `status` filter. Mirrors what the dashboard at / shows.',
73
+ inputSchema: {
74
+ status: z.enum(STATUSES).optional(),
75
+ limit: z.number().int().min(1).max(500).default(100),
76
+ },
77
+ handler: async (args) => {
78
+ const where = [];
79
+ const params = [];
80
+ if (args.status) {
81
+ where.push('j.status = ?');
82
+ params.push(args.status);
83
+ }
84
+ const sql = `
85
+ SELECT
86
+ j.id AS job_id, j.title,
87
+ COALESCE(c.name, j.company_name_raw) AS company_name,
88
+ j.score_total, j.role_category, j.seniority,
89
+ j.location_raw AS location, j.status, j.source_url,
90
+ j.discovered_at, j.scored_at, j.applied_at,
91
+ (SELECT er.html_path FROM eval_reports er WHERE er.job_id = j.id ORDER BY er.created_at DESC LIMIT 1) AS report_html,
92
+ (SELECT a.resume_path FROM applications a WHERE a.job_id = j.id LIMIT 1) AS resume_path,
93
+ (SELECT a.cover_path FROM applications a WHERE a.job_id = j.id LIMIT 1) AS cover_path
94
+ FROM jobs j LEFT JOIN companies c ON c.id = j.company_id
95
+ ${where.length ? 'WHERE ' + where.join(' AND ') : ''}
96
+ ORDER BY datetime(j.discovered_at) DESC LIMIT ?
97
+ `;
98
+ const rows = getDb().prepare(sql).all(...params, args.limit);
99
+ const counts = getDb().prepare(`SELECT status, COUNT(*) AS n FROM jobs GROUP BY status`).all();
100
+ return okResult({
101
+ counts_by_status: Object.fromEntries(counts.map(c => [c.status, c.n])),
102
+ filtered_count: rows.length,
103
+ tracker_url: `${config.baseUrl}/`,
104
+ items: rows.map(r => ({
105
+ ...r,
106
+ report_url: r.report_html ? `${config.baseUrl}/files/${r.report_html}` : null,
107
+ resume_url: r.resume_path ? `${config.baseUrl}/files/${r.resume_path}` : null,
108
+ cover_url: r.cover_path ? `${config.baseUrl}/files/${r.cover_path}` : null,
109
+ report_html: undefined, resume_path: undefined, cover_path: undefined,
110
+ })),
111
+ });
112
+ },
113
+ });
114
+ // ── update_status ────────────────────────────────────────────────────────────
115
+ export const updateStatusTool = defineTool({
116
+ name: 'update_status',
117
+ title: 'Update job status',
118
+ description: 'Move a job to a new canonical status. Stamps applied_at when transitioning to "applied".',
119
+ inputSchema: {
120
+ job_id: z.string().min(1),
121
+ status: z.enum(STATUSES),
122
+ note: z.string().optional(),
123
+ },
124
+ handler: async (args) => {
125
+ const result = await runInWriteLock(() => {
126
+ const db = getDb();
127
+ const existing = db.prepare('SELECT status FROM jobs WHERE id = ?').get(args.job_id);
128
+ if (!existing)
129
+ return { ok: false, message: `no job ${args.job_id}` };
130
+ // applied_at gets stamped only on the applied transition; updated_at always.
131
+ const appliedClause = args.status === 'applied' ? ', applied_at = CURRENT_TIMESTAMP' : '';
132
+ db.prepare(`UPDATE jobs SET status = ?, updated_at = CURRENT_TIMESTAMP${appliedClause} WHERE id = ?`).run(args.status, args.job_id);
133
+ if (args.note) {
134
+ const cur = db.prepare('SELECT score_detail FROM jobs WHERE id = ?').get(args.job_id);
135
+ const det = safeJson(cur?.score_detail, {});
136
+ det.status_history = (Array.isArray(det.status_history) ? det.status_history : []);
137
+ det.status_history.push({
138
+ at: new Date().toISOString(), from: existing.status, to: args.status, note: args.note,
139
+ });
140
+ db.prepare('UPDATE jobs SET score_detail = ? WHERE id = ?').run(JSON.stringify(det), args.job_id);
141
+ }
142
+ return { ok: true, from: existing.status, to: args.status };
143
+ });
144
+ if (!result.ok)
145
+ return errResult(result.message ?? 'failed');
146
+ return okResult({ job_id: args.job_id, from: result.from, to: result.to });
147
+ },
148
+ });
149
+ // ── mark_ready_to_apply ──────────────────────────────────────────────────────
150
+ export const markReadyToApplyTool = defineTool({
151
+ name: 'mark_ready_to_apply',
152
+ title: 'Bulk-mark jobs ready to apply',
153
+ description: 'Bulk-sets job status to ready_to_apply. Skips jobs in terminal states (applied/screen/onsite/offer/rejected/discarded).',
154
+ inputSchema: { job_ids: z.array(z.string().min(1)).min(1).max(200) },
155
+ handler: async (args) => {
156
+ const TERMINAL = new Set(['applied', 'screen', 'onsite', 'offer', 'rejected', 'discarded']);
157
+ const summary = await runInWriteLock(() => {
158
+ const db = getDb();
159
+ const tx = db.transaction((ids) => {
160
+ const out = { updated: [], skipped: [] };
161
+ const sel = db.prepare('SELECT status FROM jobs WHERE id = ?');
162
+ const upd = db.prepare(`UPDATE jobs SET status = 'ready_to_apply', updated_at = CURRENT_TIMESTAMP WHERE id = ?`);
163
+ for (const id of ids) {
164
+ const row = sel.get(id);
165
+ if (!row) {
166
+ out.skipped.push({ job_id: id, reason: 'not found' });
167
+ continue;
168
+ }
169
+ if (TERMINAL.has(row.status)) {
170
+ out.skipped.push({ job_id: id, reason: `terminal state ${row.status}` });
171
+ continue;
172
+ }
173
+ upd.run(id);
174
+ out.updated.push(id);
175
+ }
176
+ return out;
177
+ });
178
+ return tx(args.job_ids);
179
+ });
180
+ return okResult({ updated_count: summary.updated.length, ...summary });
181
+ },
182
+ });
183
+ //# sourceMappingURL=tracker.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tracker.js","sourceRoot":"","sources":["../../../src/mcp/tools/tracker.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,219 @@
1
+ // G4 — visa: import_h1b, import_linkedin, visa_signal.
2
+ //
3
+ // Importers accept CSV files only (avoid xlsx dep). LinkedIn Connections.csv is the
4
+ // official export shape. DOL OFLC H1B quarterly CSV column names are mapped via header
5
+ // lookup with fallbacks — DOL renames columns across fiscal years, so we accept aliases.
6
+ import { z } from 'zod';
7
+ import { randomUUID } from 'node:crypto';
8
+ import { readFileSync, existsSync, statSync } from 'node:fs';
9
+ import { getDb, runInWriteLock } from '../../db.js';
10
+ import { defineTool, okResult, errResult } from '../define.js';
11
+ import { parseCsv, findLinkedinHeader, pickHeader, parseMaybeNumber, expandUserPath } from '../../core/csv.js';
12
+ import { upsertCompany, findCompanyByName } from '../../core/jobs.js';
13
+ const MAX_FILE_BYTES = 200 * 1024 * 1024; // 200 MB — full DOL quarterly is < this.
14
+ // ── visa_signal ──────────────────────────────────────────────────────────────
15
+ export const visaSignalTool = defineTool({
16
+ name: 'visa_signal',
17
+ title: 'Visa-friendliness signal for a company',
18
+ description: 'Aggregates h1b_filings via v_company_h1b_signal. Returns a band: strong / mixed / weak / none. ' +
19
+ 'INTERNAL ONLY — do NOT echo this into a resume, cover letter, or outreach.',
20
+ inputSchema: { company: z.string().min(1) },
21
+ handler: async (args) => {
22
+ const c = findCompanyByName(args.company);
23
+ const row = c
24
+ ? getDb().prepare(`SELECT * FROM v_company_h1b_signal WHERE company_id = ?`).get(c.id)
25
+ : null;
26
+ if (!row || !row.total_filings) {
27
+ return okResult({
28
+ company: args.company,
29
+ band: 'none',
30
+ note: 'No matching H1B filings on file. Either the company has no LCAs in our imported dataset, or the name does not match our companies row.',
31
+ total_filings: 0, certified: 0,
32
+ });
33
+ }
34
+ const certified = Number(row.certified_count ?? 0);
35
+ const total = Number(row.total_filings ?? 0);
36
+ const recent = Number(row.most_recent_fy ?? 0);
37
+ const thisFy = new Date().getFullYear();
38
+ const recentlyActive = recent >= thisFy - 1;
39
+ let band = 'weak';
40
+ if (certified >= 25 && recentlyActive)
41
+ band = 'strong';
42
+ else if (certified >= 5)
43
+ band = 'mixed';
44
+ return okResult({
45
+ company: row.company_name,
46
+ band,
47
+ total_filings: total,
48
+ certified,
49
+ most_recent_fy: recent,
50
+ last_decision_date: row.last_decision_date,
51
+ note: 'INTERNAL ONLY — never surface in a resume, cover letter, or outreach.',
52
+ });
53
+ },
54
+ });
55
+ // ── import_linkedin ──────────────────────────────────────────────────────────
56
+ export const importLinkedinTool = defineTool({
57
+ name: 'import_linkedin',
58
+ title: 'Import a LinkedIn Connections.csv export',
59
+ description: 'Bulk-loads linkedin_connections from the standard LinkedIn export (Settings → Data Privacy → Export your data → Connections).',
60
+ inputSchema: {
61
+ path: z.string().min(1).describe('Absolute path to Connections.csv. ~ is expanded.'),
62
+ dry_run: z.boolean().default(false),
63
+ },
64
+ handler: async (args) => {
65
+ const file = expandUserPath(args.path);
66
+ if (!existsSync(file))
67
+ return errResult(`file not found: ${file}`);
68
+ if (statSync(file).size > MAX_FILE_BYTES)
69
+ return errResult(`file too large (> ${MAX_FILE_BYTES} bytes)`);
70
+ const allRows = parseCsv(readFileSync(file, 'utf-8'));
71
+ const headerIdx = findLinkedinHeader(allRows);
72
+ const dataRows = allRows.slice(headerIdx);
73
+ if (dataRows.length < 2)
74
+ return errResult('no data rows after header detection');
75
+ const get = pickHeader(dataRows[0]);
76
+ const rows = dataRows.slice(1);
77
+ if (args.dry_run)
78
+ return okResult({ dry_run: true, total_rows: rows.length, file });
79
+ const summary = await runInWriteLock(() => {
80
+ const db = getDb();
81
+ const tx = db.transaction(() => {
82
+ let inserted = 0, updated = 0, skipped = 0;
83
+ const insertStmt = db.prepare(`
84
+ INSERT INTO linkedin_connections (
85
+ id, first_name, last_name, full_name, email, linkedin_url,
86
+ company_id, company_raw, position, connected_on,
87
+ is_recruiter, is_engineering, is_leadership
88
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
89
+ `);
90
+ const updateStmt = db.prepare(`
91
+ UPDATE linkedin_connections SET
92
+ first_name = ?, last_name = ?, full_name = ?,
93
+ email = COALESCE(NULLIF(?, ''), email),
94
+ company_id = ?, company_raw = ?, position = ?, connected_on = ?,
95
+ is_recruiter = ?, is_engineering = ?, is_leadership = ?,
96
+ updated_at = CURRENT_TIMESTAMP
97
+ WHERE linkedin_url = ?
98
+ `);
99
+ const existsStmt = db.prepare(`SELECT id FROM linkedin_connections WHERE linkedin_url = ?`);
100
+ for (const r of rows) {
101
+ const first = get(r, 'First Name');
102
+ const last = get(r, 'Last Name');
103
+ const url = get(r, 'URL', 'Profile URL');
104
+ const email = get(r, 'Email Address', 'Email');
105
+ const company = get(r, 'Company');
106
+ const position = get(r, 'Position');
107
+ const connected = get(r, 'Connected On');
108
+ if (!url || (!first && !last)) {
109
+ skipped++;
110
+ continue;
111
+ }
112
+ const full = `${first} ${last}`.trim();
113
+ const lower = position.toLowerCase();
114
+ const is_recruiter = /(recruit|talent|sourcer|head of talent)/i.test(lower) ? 1 : 0;
115
+ const is_engineering = /(engineer|developer|swe|sre|ml|data|backend|frontend|fullstack|platform)/i.test(lower) ? 1 : 0;
116
+ const is_leadership = /(chief|founder|ceo|cto|cpo|cmo|vp|director|head of|principal)/i.test(lower) ? 1 : 0;
117
+ let company_id = null;
118
+ if (company) {
119
+ try {
120
+ company_id = upsertCompany(company);
121
+ }
122
+ catch { /* ignore */ }
123
+ }
124
+ const existing = existsStmt.get(url);
125
+ if (existing) {
126
+ updateStmt.run(first, last, full, email, company_id, company, position, connected, is_recruiter, is_engineering, is_leadership, url);
127
+ updated++;
128
+ }
129
+ else {
130
+ insertStmt.run(randomUUID(), first, last, full, email, url, company_id, company, position, connected, is_recruiter, is_engineering, is_leadership);
131
+ inserted++;
132
+ }
133
+ }
134
+ return { inserted, updated, skipped };
135
+ });
136
+ return tx();
137
+ });
138
+ return okResult({ file, rows_in_csv: rows.length, ...summary });
139
+ },
140
+ });
141
+ // ── import_h1b ───────────────────────────────────────────────────────────────
142
+ //
143
+ // DOL OFLC quarterly LCA disclosure CSV. Recent files use these columns (subset of the
144
+ // 95 columns DOL ships). We pass-through unknowns into raw_json.
145
+ export const importH1bTool = defineTool({
146
+ name: 'import_h1b',
147
+ title: 'Import a DOL OFLC H1B LCA quarterly CSV',
148
+ description: 'Bulk-loads h1b_filings from a DOL OFLC LCA disclosure CSV (download the quarterly file from foreignlaborcert.doleta.gov/performancedata.cfm). ' +
149
+ 'Maps employer_name to companies via name_normalized; unmatched names are kept as employer_name_raw only.',
150
+ inputSchema: {
151
+ path: z.string().min(1),
152
+ fiscal_year: z.number().int().min(2010).max(2099).optional()
153
+ .describe('Override the fiscal_year column when missing; defaults to current FY.'),
154
+ dry_run: z.boolean().default(false),
155
+ batch_size: z.number().int().min(100).max(50_000).default(5_000),
156
+ },
157
+ handler: async (args) => {
158
+ const file = expandUserPath(args.path);
159
+ if (!existsSync(file))
160
+ return errResult(`file not found: ${file}`);
161
+ if (statSync(file).size > MAX_FILE_BYTES)
162
+ return errResult(`file too large (> ${MAX_FILE_BYTES} bytes)`);
163
+ const allRows = parseCsv(readFileSync(file, 'utf-8'));
164
+ if (allRows.length < 2)
165
+ return errResult('empty CSV');
166
+ const header = allRows[0];
167
+ const get = pickHeader(header);
168
+ const dataRows = allRows.slice(1);
169
+ if (args.dry_run)
170
+ return okResult({ dry_run: true, total_rows: dataRows.length, file });
171
+ const fy = args.fiscal_year ?? new Date().getFullYear();
172
+ const summary = await runInWriteLock(() => {
173
+ const db = getDb();
174
+ const insertStmt = db.prepare(`
175
+ INSERT OR IGNORE INTO h1b_filings (
176
+ case_number, case_status, visa_class, employer_id, employer_name_raw,
177
+ job_title, soc_code, soc_title, work_city, work_state, work_postal_code,
178
+ wage_rate_from, wage_rate_to, wage_unit, prevailing_wage,
179
+ received_date, decision_date, employment_start, employment_end,
180
+ full_time, new_employment, fiscal_year, raw_json
181
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
182
+ `);
183
+ const tx = db.transaction((batch) => {
184
+ let inserted = 0, skipped = 0, matched_company = 0;
185
+ for (const r of batch) {
186
+ const caseNum = get(r, 'CASE_NUMBER', 'Case Number');
187
+ if (!caseNum) {
188
+ skipped++;
189
+ continue;
190
+ }
191
+ const employerRaw = get(r, 'EMPLOYER_NAME', 'Employer Name');
192
+ let employerId = null;
193
+ if (employerRaw) {
194
+ try {
195
+ employerId = upsertCompany(employerRaw);
196
+ matched_company++;
197
+ }
198
+ catch { }
199
+ }
200
+ insertStmt.run(caseNum, get(r, 'CASE_STATUS', 'Case Status') || 'Unknown', get(r, 'VISA_CLASS', 'Visa Class') || null, employerId, employerRaw, get(r, 'JOB_TITLE', 'Job Title') || null, get(r, 'SOC_CODE') || null, get(r, 'SOC_TITLE') || null, get(r, 'WORKSITE_CITY', 'Worksite City') || null, get(r, 'WORKSITE_STATE', 'Worksite State') || null, get(r, 'WORKSITE_POSTAL_CODE', 'Worksite Postal Code') || null, parseMaybeNumber(get(r, 'WAGE_RATE_OF_PAY_FROM', 'Wage Rate of Pay From')), parseMaybeNumber(get(r, 'WAGE_RATE_OF_PAY_TO', 'Wage Rate of Pay To')), get(r, 'WAGE_UNIT_OF_PAY', 'Wage Unit Of Pay') || null, parseMaybeNumber(get(r, 'PREVAILING_WAGE')), get(r, 'RECEIVED_DATE') || null, get(r, 'DECISION_DATE') || null, get(r, 'BEGIN_DATE', 'Period of Employment Begin Date') || null, get(r, 'END_DATE', 'Period of Employment End Date') || null, get(r, 'FULL_TIME_POSITION') === 'Y' ? 1 : (get(r, 'FULL_TIME_POSITION') === 'N' ? 0 : null), get(r, 'NEW_EMPLOYMENT') === '1' ? 1 : (get(r, 'NEW_EMPLOYMENT') === '0' ? 0 : null), Number(get(r, 'FISCAL_YEAR') || fy),
201
+ // raw_json — we materialize a slim row object once per record (still bounded).
202
+ JSON.stringify(Object.fromEntries(header.map((h, i) => [h, r[i] ?? '']))).slice(0, 8000));
203
+ inserted++;
204
+ }
205
+ return { inserted, skipped, matched_company };
206
+ });
207
+ let inserted = 0, skipped = 0, matched_company = 0;
208
+ for (let i = 0; i < dataRows.length; i += args.batch_size) {
209
+ const r = tx(dataRows.slice(i, i + args.batch_size));
210
+ inserted += r.inserted;
211
+ skipped += r.skipped;
212
+ matched_company += r.matched_company;
213
+ }
214
+ return { inserted, skipped, matched_company };
215
+ });
216
+ return okResult({ file, rows_in_csv: dataRows.length, fiscal_year: fy, ...summary });
217
+ },
218
+ });
219
+ //# sourceMappingURL=visa.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"visa.js","sourceRoot":"","sources":["../../../src/mcp/tools/visa.ts"],"names":[],"mappings":"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"}