skilldrop-cli 0.16.6 → 0.16.7

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 (104) hide show
  1. package/README.md +2 -1
  2. package/catalogue.json +10 -1
  3. package/model-routing.json +12 -0
  4. package/package.json +1 -1
  5. package/packs/ai-engineering/skills/ai-usage-policy/evals/evals.json +30 -0
  6. package/packs/ai-engineering/skills/ai-usage-policy/evals/files/2/data-handling-sheet.md +18 -0
  7. package/packs/ai-engineering/skills/ai-usage-policy/evals/files/2/it-notes.md +31 -0
  8. package/packs/ai-engineering/skills/ai-usage-policy/evals/files/2/md-email.md +29 -0
  9. package/packs/api-builder/skills/tool-use-schema-writer/evals/evals.json +29 -0
  10. package/packs/api-builder/skills/tool-use-schema-writer/evals/files/2/book_appointment.ts +64 -0
  11. package/packs/career/loops/apply/LOOP.md +88 -0
  12. package/packs/career/loops/apply/evals/eval_queries.json +9 -0
  13. package/packs/career/loops/apply/evals/evals.json +27 -0
  14. package/packs/career/loops/apply/loop.json +46 -0
  15. package/packs/career/pack.json +57 -0
  16. package/packs/career/skills/cover-letter/SKILL.md +132 -0
  17. package/packs/career/skills/cover-letter/evals/eval_queries.json +34 -0
  18. package/packs/career/skills/cover-letter/evals/evals.json +100 -0
  19. package/packs/career/skills/cover-letter/evals/files/1/posting.md +28 -0
  20. package/packs/career/skills/cover-letter/evals/files/1/resume.md +35 -0
  21. package/packs/career/skills/cover-letter/evals/files/2/posting.md +20 -0
  22. package/packs/career/skills/cover-letter/evals/files/2/resume.md +32 -0
  23. package/packs/career/skills/cover-letter/evals/files/4/posting.md +19 -0
  24. package/packs/career/skills/cover-letter/evals/files/4/resume.md +26 -0
  25. package/packs/career/skills/cover-letter/evals/files/5/posting.md +21 -0
  26. package/packs/career/skills/cover-letter/evals/files/5/resume.md +32 -0
  27. package/packs/career/skills/cover-letter/evals/files/6/posting.md +25 -0
  28. package/packs/career/skills/cover-letter/evals/files/6/resume.md +29 -0
  29. package/packs/career/skills/cover-letter/evals/files/7/posting.md +18 -0
  30. package/packs/career/skills/cover-letter/evals/files/7/resume.md +23 -0
  31. package/packs/career/skills/cover-letter/manifest.json +37 -0
  32. package/packs/career/skills/cover-letter/scripts/claim_check.py +247 -0
  33. package/packs/career/skills/job-fit-analysis/SKILL.md +136 -0
  34. package/packs/career/skills/job-fit-analysis/evals/eval_queries.json +34 -0
  35. package/packs/career/skills/job-fit-analysis/evals/evals.json +76 -0
  36. package/packs/career/skills/job-fit-analysis/evals/files/1/posting.md +28 -0
  37. package/packs/career/skills/job-fit-analysis/evals/files/1/resume.md +35 -0
  38. package/packs/career/skills/job-fit-analysis/evals/files/2/posting.md +20 -0
  39. package/packs/career/skills/job-fit-analysis/evals/files/2/resume.md +32 -0
  40. package/packs/career/skills/job-fit-analysis/evals/files/4/posting.md +25 -0
  41. package/packs/career/skills/job-fit-analysis/evals/files/4/resume.md +29 -0
  42. package/packs/career/skills/job-fit-analysis/evals/files/5/posting.md +18 -0
  43. package/packs/career/skills/job-fit-analysis/evals/files/5/resume.md +23 -0
  44. package/packs/career/skills/job-fit-analysis/manifest.json +31 -0
  45. package/packs/career/skills/resume-tailor/SKILL.md +116 -0
  46. package/packs/career/skills/resume-tailor/evals/eval_queries.json +34 -0
  47. package/packs/career/skills/resume-tailor/evals/evals.json +105 -0
  48. package/packs/career/skills/resume-tailor/evals/files/1/posting.md +26 -0
  49. package/packs/career/skills/resume-tailor/evals/files/1/resume.md +32 -0
  50. package/packs/career/skills/resume-tailor/evals/files/2/posting.md +19 -0
  51. package/packs/career/skills/resume-tailor/evals/files/2/resume.md +26 -0
  52. package/packs/career/skills/resume-tailor/evals/files/3/posting.md +21 -0
  53. package/packs/career/skills/resume-tailor/evals/files/3/resume.md +32 -0
  54. package/packs/career/skills/resume-tailor/evals/files/4/posting.md +28 -0
  55. package/packs/career/skills/resume-tailor/evals/files/4/resume.md +35 -0
  56. package/packs/career/skills/resume-tailor/evals/files/5/posting.md +20 -0
  57. package/packs/career/skills/resume-tailor/evals/files/5/resume.md +32 -0
  58. package/packs/career/skills/resume-tailor/evals/files/6/posting.md +25 -0
  59. package/packs/career/skills/resume-tailor/evals/files/6/resume.md +29 -0
  60. package/packs/career/skills/resume-tailor/evals/files/7/posting.md +18 -0
  61. package/packs/career/skills/resume-tailor/evals/files/7/resume.md +23 -0
  62. package/packs/career/skills/resume-tailor/manifest.json +37 -0
  63. package/packs/career/skills/resume-tailor/scripts/claim_check.py +247 -0
  64. package/packs/data-analytics/skills/metric-definition/evals/evals.json +30 -0
  65. package/packs/data-analytics/skills/metric-definition/evals/files/2/tables.md +42 -0
  66. package/packs/data-analytics/skills/metric-definition/evals/files/2/thread.md +40 -0
  67. package/packs/dev-team/skills/contribution-wizard/evals/evals.json +29 -0
  68. package/packs/dev-team/skills/contribution-wizard/evals/files/2/AGENTS.md +9 -0
  69. package/packs/dev-team/skills/contribution-wizard/evals/files/2/CATALOG.md +18 -0
  70. package/packs/dev-team/skills/contribution-wizard/evals/files/2/CONTRIBUTING.md +45 -0
  71. package/packs/dev-team/skills/contribution-wizard/evals/files/2/OWNERS.md +7 -0
  72. package/packs/dev-team/skills/contribution-wizard/evals/files/2/skills/detention-claim-letter/INSTRUCTIONS.md +20 -0
  73. package/packs/dev-team/skills/contribution-wizard/evals/files/2/skills/detention-claim-letter/skill.yaml +11 -0
  74. package/packs/dev-team/skills/contribution-wizard/evals/files/2/skills/detention-claim-letter/tests/cases.yaml +25 -0
  75. package/packs/dev-team/skills/contribution-wizard/evals/files/2/skills/rate-con-extractor/INSTRUCTIONS.md +20 -0
  76. package/packs/dev-team/skills/contribution-wizard/evals/files/2/skills/rate-con-extractor/skill.yaml +8 -0
  77. package/packs/dev-team/skills/contribution-wizard/evals/files/2/skills/rate-con-extractor/tests/cases.yaml +32 -0
  78. package/packs/dev-team/skills/devils-advocate/evals/evals.json +29 -0
  79. package/packs/dev-team/skills/devils-advocate/evals/files/2/signups.py +116 -0
  80. package/packs/dev-team/skills/devils-advocate/evals/files/2/test_signups.py +69 -0
  81. package/packs/dev-team/skills/release-notes/evals/evals.json +29 -0
  82. package/packs/dev-team/skills/release-notes/evals/files/2/commits.txt +35 -0
  83. package/packs/research/skills/source-synthesis/evals/evals.json +30 -0
  84. package/packs/research/skills/source-synthesis/evals/files/2/badge-data.md +27 -0
  85. package/packs/research/skills/source-synthesis/evals/files/2/director-email.md +16 -0
  86. package/packs/research/skills/source-synthesis/evals/files/2/exit-interviews.md +17 -0
  87. package/packs/research/skills/source-synthesis/evals/files/2/practice-weekly-article.md +21 -0
  88. package/packs/research/skills/source-synthesis/evals/files/2/staff-survey-summary.md +41 -0
  89. package/packs/solution-architect/skills/db-schema-design/evals/evals.json +29 -0
  90. package/packs/solution-architect/skills/db-schema-design/evals/files/2/current_tables.sql +29 -0
  91. package/packs/solution-architect/skills/db-schema-design/evals/files/2/notes.md +53 -0
  92. package/packs/solution-architect/skills/design-doc/evals/evals.json +29 -0
  93. package/packs/solution-architect/skills/design-doc/evals/files/2/brief.md +18 -0
  94. package/packs/solution-architect/skills/design-doc/evals/files/2/eng-notes.md +30 -0
  95. package/packs/solution-architect/skills/tech-comparison-matrix/evals/evals.json +29 -0
  96. package/packs/solution-architect/skills/tech-comparison-matrix/evals/files/2/requirements.md +20 -0
  97. package/packs/solution-architect/skills/tech-comparison-matrix/evals/files/2/vendor-notes.md +29 -0
  98. package/packs/sre-oncall/skills/postmortem-generator/evals/evals.json +30 -0
  99. package/packs/sre-oncall/skills/postmortem-generator/evals/files/2/pager-export.txt +22 -0
  100. package/packs/sre-oncall/skills/postmortem-generator/evals/files/2/slack-thread.txt +28 -0
  101. package/packs/stakeholder-comms/skills/guide-builder/evals/evals.json +29 -0
  102. package/packs/stakeholder-comms/skills/guide-builder/evals/files/2/api-notes.md +44 -0
  103. package/packs/stakeholder-comms/skills/guide-builder/evals/files/2/sample-booking.json +19 -0
  104. package/profiles.json +2 -2
@@ -0,0 +1,247 @@
1
+ #!/usr/bin/env python3
2
+ """List what a tailored resume or cover letter says that the candidate's own material doesn't.
3
+
4
+ python3 claim_check.py DRAFT --source RESUME [--source NOTES ...] [--posting POSTING] [--json]
5
+
6
+ DRAFT is the text you are about to send. Each --source is something the candidate wrote or
7
+ told you: the original resume, notes from the conversation. --posting is the job posting.
8
+
9
+ It reports four kinds of finding, each with the line it is on:
10
+
11
+ number a figure in the draft that no source contains ("35%", "$2M", "team of 8")
12
+ name a named thing in the draft that no source contains: a tool, an employer, a
13
+ certification, a degree ("Kubernetes", "Epic", "Green Belt")
14
+ combination capitalised words that each appear in a source but never together
15
+ ("Senior Manager" when the source has "Senior Analyst" and "Office Manager")
16
+ borrowed wording the draft shares with the posting and no source uses. Fine when the
17
+ source shows the same thing in other words; a false claim when it doesn't.
18
+
19
+ A name or number that the posting contains is marked "in posting": right when the draft is
20
+ talking about the employer, wrong when it is claimed as the candidate's own.
21
+
22
+ It reads text, so it cannot tell a true claim from a false one. It tells you where to look.
23
+ Exit status: 0 nothing found, 1 findings, 2 a file could not be read. Standard library only.
24
+ """
25
+ import argparse
26
+ import json
27
+ import re
28
+ import sys
29
+
30
+ MULT = {"k": 1e3, "thousand": 1e3, "m": 1e6, "mm": 1e6, "million": 1e6,
31
+ "bn": 1e9, "b": 1e9, "billion": 1e9}
32
+ NUMBER = re.compile(r"(?<![\w.])(\d[\d,]*(?:\.\d+)?)(?:(k|mm|m|bn|b)\b|\s?(thousand|million|billion)\b)?"
33
+ r"(\s?%|\s?percent\b|\s?per cent\b)?", re.I)
34
+ WORD = re.compile(r"\.?[A-Za-z][A-Za-z0-9+#']*(?:\.[A-Za-z0-9]+)*")
35
+ LIST_MARK = re.compile(r"^\s*(?:[-*•–>]+|\d+[.)]|#+|\|)\s*")
36
+ LINK = re.compile(r"\[([^\]]*)\]\([^)]*\)")
37
+ SUFFIXES = ("ations", "ation", "ments", "ment", "ings", "ing", "ers", "ies", "ed", "er", "es", "s", "ly")
38
+
39
+ # Words that start a line or a sentence with a capital letter and name nothing.
40
+ PLAIN = set("""
41
+ a an and are as at be been both but by for from had has have he her his i if in into is it its
42
+ me my no not of on or our she so that the their them then there these they this to up us was
43
+ we were what when where which who why with you your dear sincerely regards kind best yours
44
+ faithfully hiring manager team committee recruiter sir madam re subject date enclosed
45
+ summary profile objective experience employment work history education skills tools
46
+ certifications certification qualifications training projects awards languages interests
47
+ references available request professional career key core technical additional other
48
+ volunteering volunteer contact details achievements highlights about present current
49
+ january february march april may june july august september october november december
50
+ jan feb mar apr jun jul aug sep sept oct nov dec monday tuesday wednesday thursday friday
51
+ saturday sunday full part time application applying while although though because since
52
+ during after before however also please thank thanks between over across within having most each every one two three
53
+ """.split())
54
+ DATE_LINE = re.compile(r"^\s*(?:\d{1,2}(?:st|nd|rd|th)?\s+)?[A-Z][a-z]{2,8}\.?\s+(?:\d{1,2}(?:st|nd|rd|th)?,?\s+)?\d{4}\s*$")
55
+ # The joining words allowed inside a capitalised phrase: "Head of Geography".
56
+ JOINERS = {"of", "and", "for", "the", "in", "at", "&", "de"}
57
+ STOP = PLAIN | set("""
58
+ able across after also been before being between can could did does doing each every experience
59
+ experienced including into just like more most much need needs over role roles some such than
60
+ them through under using very well will within without work worked working would years year
61
+ have with that this from your their about must should strong good great team teams including
62
+ ability skills skill knowledge new help helps support supports ensure make makes
63
+ """.split())
64
+
65
+
66
+ def read(path):
67
+ try:
68
+ with open(path, encoding="utf-8", errors="replace") as f:
69
+ return f.read()
70
+ except OSError as e:
71
+ print("claim_check: cannot read %s: %s" % (path, e.strerror), file=sys.stderr)
72
+ sys.exit(2)
73
+
74
+
75
+ def clean(line):
76
+ """A line with its markdown removed, so `**Go**` and `Go` read the same."""
77
+ line = LINK.sub(r"\1", line)
78
+ line = line.replace("’", "'").replace("‘", "'").replace("–", " - ").replace("—", " - ")
79
+ return re.sub(r"[*_`~]", "", line)
80
+
81
+
82
+ def numbers(text):
83
+ """{(value, is_percent)} for every figure in the text. "$2M" and "2 million" are one value."""
84
+ found = set()
85
+ for m in NUMBER.finditer(text):
86
+ try:
87
+ value = float(m.group(1).replace(",", ""))
88
+ except ValueError:
89
+ continue
90
+ unit = (m.group(2) or m.group(3) or "").lower()
91
+ found.add((value * MULT.get(unit, 1), bool(m.group(4))))
92
+ return found
93
+
94
+
95
+ def stem(word):
96
+ w = word.lower().strip("'.")
97
+ if w.endswith("'s"):
98
+ w = w[:-2]
99
+ for s in SUFFIXES:
100
+ if w.endswith(s) and len(w) - len(s) >= 4:
101
+ return w[: -len(s)]
102
+ return w
103
+
104
+
105
+ def key(word):
106
+ """How a name is compared: case, dots, a plural, a possessive or a contraction don't matter."""
107
+ w = word.lower().strip("'")
108
+ w = re.sub(r"'(?:s|m|d|ve|ll|re|t)$", "", w)
109
+ w = w.replace(".", "") or w
110
+ return w[:-1] if len(w) > 4 and w.endswith("s") and not w.endswith("ss") else w
111
+
112
+
113
+ def vocabulary(text):
114
+ names, stems = set(), set()
115
+ for line in text.splitlines():
116
+ for w in WORD.findall(clean(line)):
117
+ names.add(key(w))
118
+ stems.add(stem(w))
119
+ flat = " ".join(key(w) for line in text.splitlines() for w in WORD.findall(clean(line)))
120
+ return names, stems, " " + flat + " "
121
+
122
+
123
+ def looks_named(word):
124
+ """True for a word that is a name whatever its position: AWS, JavaScript, C++, k8s, .NET."""
125
+ body = word.lstrip(".")
126
+ return bool(re.search(r"[A-Z]", body[1:]) or re.search(r"[0-9+#]", body) or word.startswith("."))
127
+
128
+
129
+ def phrases(line):
130
+ """Runs of capitalised or name-shaped words in one line, as lists of words.
131
+
132
+ The first word of a line or sentence is capitalised by grammar, not because it is a name,
133
+ so it only counts when it is name-shaped itself or opens a longer capitalised run."""
134
+ text = LIST_MARK.sub("", clean(line))
135
+ out, run, start_of_sentence = [], [], True
136
+ tokens = re.findall(r"\.?[A-Za-z][A-Za-z0-9+#']*(?:\.[A-Za-z0-9]+)*|&|[.!?;:]\s|[,|·/()]|\s-\s", text + " ")
137
+ for i, tok in enumerate(tokens):
138
+ if not (tok[0].isalpha() or tok[0] in ".&") or tok.strip() in (".", "!", "?", ";", ":"):
139
+ if run:
140
+ out.append(run)
141
+ run = []
142
+ start_of_sentence = tok.strip() in (".", "!", "?")
143
+ continue
144
+ if "'" in tok and key(tok) in PLAIN: # I'm, I've, we'd
145
+ tok = key(tok)
146
+ capital = tok[0].isupper() or looks_named(tok)
147
+ if capital and not (start_of_sentence and not looks_named(tok) and tok.lower() in PLAIN):
148
+ run.append((tok, start_of_sentence))
149
+ elif run and tok.lower() in JOINERS and i + 1 < len(tokens) and tokens[i + 1][:1].isupper():
150
+ run.append((tok, False))
151
+ else:
152
+ if run:
153
+ out.append(run)
154
+ run = []
155
+ start_of_sentence = False
156
+ if run:
157
+ out.append(run)
158
+ cleaned = []
159
+ for r in out:
160
+ # A lone sentence-opening word ("Led", "Built") is grammar, not a name.
161
+ if len(r) == 1 and r[0][1] and not looks_named(r[0][0]):
162
+ continue
163
+ # A sentence-opening verb in front of a name ("Led AWS migration") is dropped from the run.
164
+ if len(r) > 1 and r[0][1] and not looks_named(r[0][0]) and r[0][0].lower() not in PLAIN:
165
+ r = r[1:] if r[0][0].lower().endswith(("ed", "ing")) else r
166
+ words = [w for w, _ in r]
167
+ while words and words[-1].lower() in JOINERS:
168
+ words.pop()
169
+ if words:
170
+ cleaned.append(words)
171
+ return cleaned
172
+
173
+
174
+ def check(draft, sources, posting):
175
+ src_names, src_stems, src_flat = vocabulary("\n".join(sources))
176
+ src_numbers = numbers("\n".join(clean(s) for s in sources))
177
+ post_names, post_stems, _ = vocabulary(posting or "")
178
+ post_numbers = numbers(clean(posting or ""))
179
+ findings, seen = [], set()
180
+
181
+ def add(kind, what, n, line, in_posting=False):
182
+ if (kind, what.lower()) in seen:
183
+ return
184
+ seen.add((kind, what.lower()))
185
+ findings.append({"kind": kind, "text": what, "line": n, "context": line.strip()[:160],
186
+ "in_posting": in_posting})
187
+
188
+ for n, raw in enumerate(draft.splitlines(), 1):
189
+ line = clean(raw)
190
+ if not line.strip() or DATE_LINE.match(LIST_MARK.sub("", line)):
191
+ continue # a letter's own date line claims nothing
192
+ body = LIST_MARK.sub("", line)
193
+ for m in NUMBER.finditer(body):
194
+ got = next(iter(numbers(m.group(0))), None)
195
+ if got and got not in src_numbers:
196
+ add("number", m.group(0).strip(), n, raw, got in post_numbers)
197
+ for words in phrases(raw):
198
+ content = [w for w in words if w.lower() not in JOINERS]
199
+ missing = [w for w in content if key(w) not in src_names and w.lower() not in PLAIN]
200
+ if missing:
201
+ add("name", " ".join(words), n, raw, all(key(w) in post_names for w in missing))
202
+ elif len(content) > 1 and all(w.lower() not in PLAIN for w in content):
203
+ flat = " ".join(key(w) for w in words)
204
+ if " " + flat + " " not in src_flat:
205
+ add("combination", " ".join(words), n, raw)
206
+ if posting:
207
+ for w in WORD.findall(body):
208
+ s = stem(w)
209
+ if (len(s) >= 4 and w.lower() not in STOP and s in post_stems and s not in src_stems
210
+ and key(w) not in src_names and not w[0].isupper() and not looks_named(w)):
211
+ add("borrowed", w.lower(), n, raw, True)
212
+ return findings
213
+
214
+
215
+ def main():
216
+ ap = argparse.ArgumentParser(description="List what a draft says that the candidate's own material doesn't.")
217
+ ap.add_argument("draft")
218
+ ap.add_argument("--source", action="append", required=True, metavar="FILE",
219
+ help="the candidate's own resume or notes; repeat for more than one")
220
+ ap.add_argument("--posting", metavar="FILE", help="the job posting")
221
+ ap.add_argument("--json", action="store_true")
222
+ a = ap.parse_args()
223
+
224
+ findings = check(read(a.draft), [read(p) for p in a.source], read(a.posting) if a.posting else None)
225
+ if a.json:
226
+ print(json.dumps({"draft": a.draft, "findings": findings}, indent=1))
227
+ return 1 if findings else 0
228
+ if not findings:
229
+ print("PASS %s: every figure and name is in the source material." % a.draft)
230
+ return 0
231
+ labels = {"number": "Figures not in the source", "name": "Names not in the source",
232
+ "combination": "Words the source has, but not together",
233
+ "borrowed": "Wording taken from the posting"}
234
+ print("REVIEW %s: %d item(s) to check against the source material." % (a.draft, len(findings)))
235
+ for kind in ("number", "name", "combination", "borrowed"):
236
+ rows = [f for f in findings if f["kind"] == kind]
237
+ if not rows:
238
+ continue
239
+ print("\n%s (%d)" % (labels[kind], len(rows)))
240
+ for f in rows:
241
+ note = " [in posting]" if f["in_posting"] and kind != "borrowed" else ""
242
+ print(" line %d: %s%s\n %s" % (f["line"], f["text"], note, f["context"]))
243
+ return 1
244
+
245
+
246
+ if __name__ == "__main__":
247
+ sys.exit(main())
@@ -0,0 +1,136 @@
1
+ ---
2
+ name: job-fit-analysis
3
+ description: Compare a job posting with a resume and say honestly whether to apply: each requirement marked met, partly met, not shown or not met, with the resume line it rests on; the requirements an employer screens on listed first; and the questions whose answers would change the picture. Works for any occupation and level. Use when the user asks "am I a good fit for this job", "should I apply for this", "how does my resume match this posting", or wants the gaps between a job description and their CV before applying.
4
+ ---
5
+
6
+ # job-fit-analysis
7
+
8
+ You compare one job posting with one person's resume and tell them where they stand before
9
+ they spend an evening on the application. The reader is the candidate. They need a straight
10
+ answer, the evidence behind it, and to know which gaps are real and which are only missing
11
+ from the page. It works the same for a nurse, a warehouse supervisor, a graduate or a staff
12
+ engineer: the posting sets the requirements, the resume is the evidence.
13
+
14
+ ## How to respond
15
+
16
+ These four rules outrank the steps below.
17
+
18
+ - **Answer first.** The first sentence answers what they asked: apply or don't, and why.
19
+ The whole analysis is in the reply. Do not put it in a file unless they ask for one.
20
+ - **Use only what you were given.** The resume and what the person told you are the only
21
+ evidence. Nothing is credited because people in that job usually have it.
22
+ - **Deliver from what you have.** With a posting and three lines about the person, compare
23
+ those. Do not stop to ask for a full resume.
24
+ - **Write to the candidate.** Address them as "you", never by name or as "he", "she" or
25
+ "the candidate". Plain words, no scoring jargon, no mention of this skill. When they gave
26
+ you a few lines and no resume, say "what you told me", never "your resume".
27
+
28
+ 1. **Read the posting and list its requirements.** Keep the employer's own wording. Sort
29
+ them into what the posting calls required and what it calls preferred. When the posting
30
+ doesn't say, treat what it lists first or repeats as required, and say you inferred it.
31
+
32
+ 2. **Find what could get the application rejected.** Two kinds of requirement do it: the
33
+ ones a recruiter or an application form checks before anyone reads the rest (a licence or
34
+ registration, a degree, a certification, a minimum number of years, the right to work,
35
+ location or shift, a clearance, a language), and any other required item the resume does
36
+ not meet, such as a named system the posting lists under "must have". These go first,
37
+ because one of them outweighs five met skills.
38
+
39
+ 3. **Mark each requirement against the resume** with one of four words:
40
+ - **Met**: the resume shows it. Quote the line.
41
+ - **Partly**: the resume shows something close, smaller or older. Say what it shows and
42
+ what is short: "team of 12; the posting asks for 20 or more".
43
+ - **Not shown**: the resume is silent. The person may well have it.
44
+ - **Not met**: the resume shows otherwise, such as a diploma where a degree is required
45
+ or two years where five are asked for.
46
+
47
+ A similar tool is not the named tool. Cerner is not Epic, an in-house warehouse system is
48
+ not SAP: when the posting requires the named one, mark it `Not met` and give the similar
49
+ one as the nearest thing you have. A related task is not the named skill: cashing up a
50
+ till is not bookkeeping in Xero, so that is `Not shown`.
51
+
52
+ Take lengths of time only from dates the resume gives for that exact thing. When the
53
+ posting asks for two years of something and the resume shows the activity with no start
54
+ date, mark it `Partly`, say the resume doesn't show how long, and ask. Never state a
55
+ duration you worked out from a neighbouring date.
56
+
57
+ 4. **Give a verdict the person can act on.** Open with a plain sentence that says whether
58
+ to apply, in the terms they asked ("Yes, apply", "Apply, but expect the degree to be a
59
+ problem", "Probably not worth your weekend unless..."), then one of four labels and the
60
+ reason:
61
+ - **Strong match**: every required item is met, or partly met with close evidence.
62
+ - **Worth applying**: nothing that would get it rejected is unmet, and most of the rest
63
+ is met.
64
+ - **Long shot**: something that could get it rejected is unmet. Say what would have to be
65
+ true for it to be worth the time, and whether you would still send it.
66
+ - **Not a match**: a requirement that cannot be worked around is unmet, such as a licence.
67
+
68
+ The opening sentence counts every required item that is `Not met` or `Partly`, not only
69
+ the worst one: "four of the six required items aren't met: the degree, the team size,
70
+ advanced Excel and SAP". It must agree with the table below it. Give the verdict on what
71
+ you know now; say what answer would raise it, but do not make the verdict wait on the
72
+ answer. Do not soften a long shot into encouragement, do not harden a workable gap into a
73
+ refusal, and do not give a percentage.
74
+
75
+ 5. **Say what to bring forward.** List the two to four things already in the resume that
76
+ answer the posting best and are buried, understated or worded differently from the
77
+ posting. Give the resume's wording and the posting's wording side by side.
78
+
79
+ 6. **Close with what to do next**, in two to four lines: what to lead the application with,
80
+ what to say about the main gap, and the one or two questions whose answers would change
81
+ the verdict ("Have you used Epic anywhere? If so, where and for how long?"). Never tell
82
+ them to add a keyword they have not said is true, and do not end on a list of questions
83
+ with no advice.
84
+
85
+ 7. **Lay the reply out in this order:** the answer and verdict; what could get it rejected;
86
+ one table covering every requirement (requirement, required or preferred, status,
87
+ evidence); what to bring forward; what to do next. Each requirement appears in the table
88
+ once and nothing is repeated in a second table or restated section by section.
89
+
90
+ Match the length to what you were given. A short posting and a few lines about the person
91
+ gets a short answer: the verdict, one line per requirement, and what to do, in under 250
92
+ words and with no table. A full resume against a long posting gets the table, and still
93
+ fits a page.
94
+
95
+ **With a posting and no resume**, compare against whatever the person said about themselves,
96
+ mark the rest `Not shown`, and say a resume would settle those. **With a resume and no
97
+ posting**, ask for the posting in one line: there is nothing to compare against.
98
+
99
+ ## Quality bar
100
+
101
+ - **Every Met and Partly quotes the resume.** A status with no line behind it is a guess.
102
+ - **What could get the application rejected is named first**, never left as one row among many.
103
+ - **No length of time appears that the resume's own dates don't give.**
104
+ - **`Not shown` and `Not met` are kept apart.** One is a question, the other is a gap.
105
+ - **Nothing is credited that the resume or the person doesn't state.**
106
+ - **The verdict matches the table.** An unmet required degree cannot sit under "strong
107
+ match", and a verdict that names one gap cannot sit over a table that shows four.
108
+
109
+ ## When to use this skill
110
+
111
+ - ✅ Deciding whether a posting is worth applying for
112
+ - ✅ Finding the gaps between a resume and a posting before tailoring the resume
113
+ - ✅ Checking several postings against one resume to choose where to spend effort
114
+
115
+ ## When NOT to use this skill
116
+
117
+ - ❌ Rewriting the resume for the posting. Use `resume-tailor`.
118
+ - ❌ Writing the cover letter. Use `cover-letter`.
119
+ - ❌ Critiquing the resume as a document, with no job in mind. Use `doc-critique`.
120
+
121
+ ## Anti-patterns to avoid
122
+
123
+ - ❌ **"87% match."** A percentage hides which requirement is the problem.
124
+ - ❌ **Cheerleading.** "You'd be a great fit!" over an unmet required degree wastes the
125
+ person's evening.
126
+ - ❌ **Crediting the usual.** "As a nurse you will have used Epic" is an invention.
127
+ - ❌ **Treating a similar tool as the named one.**
128
+ - ❌ **Advising them to add keywords** so the resume passes a filter. If it isn't true it
129
+ doesn't go in.
130
+ - ❌ **Judging the person.** No remarks on age, gaps, family, health or anything a recruiter
131
+ may not lawfully weigh.
132
+ - ❌ **Answering in a file.** They asked a question; the answer belongs in the reply.
133
+ - ❌ **A verdict with no advice.** "Long shot" followed by questions leaves them where they
134
+ started.
135
+ - ❌ **Showing the machinery.** No status legend longer than a line, no account of how the
136
+ comparison was produced, no remark about what you could or couldn't save.
@@ -0,0 +1,34 @@
1
+ [
2
+ {
3
+ "query": "Am I a good fit for this job? Here's the posting and my resume",
4
+ "should_trigger": true
5
+ },
6
+ {
7
+ "query": "Should I apply for this role or is it a stretch?",
8
+ "should_trigger": true
9
+ },
10
+ {
11
+ "query": "What's missing from my CV for this job description?",
12
+ "should_trigger": true
13
+ },
14
+ {
15
+ "query": "Compare my resume against these three postings and tell me which to go for",
16
+ "should_trigger": true
17
+ },
18
+ {
19
+ "query": "Tailor my resume to this job posting",
20
+ "should_trigger": false
21
+ },
22
+ {
23
+ "query": "Write a cover letter for this role",
24
+ "should_trigger": false
25
+ },
26
+ {
27
+ "query": "Critique my resume, it's not for any particular job",
28
+ "should_trigger": false
29
+ },
30
+ {
31
+ "query": "Compare these three applicant tracking systems for our HR team",
32
+ "should_trigger": false
33
+ }
34
+ ]
@@ -0,0 +1,76 @@
1
+ {
2
+ "skill_name": "job-fit-analysis",
3
+ "evals": [
4
+ {
5
+ "id": 1,
6
+ "prompt": "My CV is in resume.md and the job I'm looking at is in posting.md. Am I a good fit? Be honest, I don't want to waste a weekend on it.",
7
+ "assertions": [
8
+ "The Epic requirement is marked as not met, partly met or not shown, and the reply says her CV shows Cerner, not Epic, naming both systems",
9
+ "Basic Life Support instructor certification is shown as not held: the CV has BLS provider only",
10
+ "The teaching requirement is credited from precepting newly qualified nurses (9 preceptees) or the 2022 mentorship module, and the reply notes or asks how long she has been precepting, because the CV does not show two years of it",
11
+ "NMC registration, a nursing degree and at least 3 years of post-registration medical-surgical experience are each marked as met",
12
+ "A master's degree and simulation lab experience are shown as not held or not shown, and as preferred items, not required ones",
13
+ "The reply opens with an overall verdict on whether to apply, before the requirement-by-requirement detail",
14
+ "It points to the sepsis screening guide or the Cerner superuser role as existing evidence worth bringing forward",
15
+ "It does not credit her with any tool, qualification or experience that is not in the CV, and does not advise adding one"
16
+ ]
17
+ },
18
+ {
19
+ "id": 2,
20
+ "prompt": "Thinking of applying for this (posting.md). My resume's in resume.md. What are my chances and what's missing?",
21
+ "assertions": [
22
+ "The bachelor's degree is marked as required and not met: the resume shows a high school diploma and 18 uncompleted college credits",
23
+ "The degree gap is stated in the verdict or before the detailed list, as something likely to screen him out, not only as one row among many",
24
+ "The team-size requirement is marked short, with both numbers: he supervises 12 and the posting asks for 20 or more",
25
+ "SAP Extended Warehouse Management is marked not shown or partly met, and the reply says the resume shows an in-house warehouse system instead",
26
+ "Advanced Excel is not marked fully met: the resume shows rosters and attendance in Excel, and pivot tables and VLOOKUP are not shown",
27
+ "The safety record (zero lost-time incidents for 14 months) and the forklift certification are marked as met, quoting the resume",
28
+ "The overall verdict is not a strong match, and matches the unmet degree requirement",
29
+ "It does not advise him to claim a degree, SAP experience, a bigger team or any keyword he has not shown"
30
+ ]
31
+ },
32
+ {
33
+ "id": 3,
34
+ "prompt": "Is this worth applying for? I've been a barista for 4 years, shift lead for the last 2 - I do the rota for 9 people, the weekly stock order and cashing up. No degree.\n\nOffice Manager, Pell & Dray Architects (14 staff)\n- 2+ years in an office administration role\n- Managing supplier relationships and ordering\n- Organising staff schedules and meeting rooms\n- Bookkeeping experience with Xero\n- Confident with Microsoft 365\n- Friendly first point of contact for clients and visitors",
35
+ "assertions": [
36
+ "It delivers a requirement-by-requirement comparison from what was given, and does not stop to ask for a resume first",
37
+ "The 2+ years of office administration is marked not met or named as the main gap, since the experience is in a cafe and not an office",
38
+ "The rota for 9 people is credited against organising staff schedules, and the weekly stock order against ordering, each citing what the person said",
39
+ "Xero is marked not shown or not met, and cashing up is not presented as Xero or bookkeeping-software experience",
40
+ "Microsoft 365 is marked not shown or asked about, not assumed",
41
+ "The lack of a degree is not treated as a gap, because the posting does not ask for one",
42
+ "It gives an overall verdict that is honest about the office-experience gap",
43
+ "It does not invent an employer name, a qualification, a tool or any experience the person did not state"
44
+ ]
45
+ },
46
+ {
47
+ "id": 4,
48
+ "held_out": "2026-10-05",
49
+ "prompt": "Would I get anywhere with this? CV is resume.md, job is posting.md.",
50
+ "assertions": [
51
+ "The qualification requirement is marked not met: she has AAT Level 3 and two of five Level 4 units, not Level 4 complete or part-qualified ACCA or CIMA",
52
+ "The 2 years in a management accounts or analyst role is marked not met or short: her roles are accounts payable clerk and finance administrator",
53
+ "Power BI is shown as not held, and the Excel workbook is not presented as Power BI experience",
54
+ "Advanced Excel is credited from the workbook with pivot tables and lookups, quoting the CV",
55
+ "Variance analysis and budget commentary are marked not shown or not met",
56
+ "Sage 200 and the accruals for goods received not invoiced are credited as relevant evidence",
57
+ "The reply opens with a clear answer on whether to apply",
58
+ "It does not credit her with any qualification, tool or experience that is not in the CV"
59
+ ]
60
+ },
61
+ {
62
+ "id": 5,
63
+ "held_out": "2026-10-05",
64
+ "prompt": "Head chef job at a hotel, posting.md. My CV's resume.md. Realistic or not?",
65
+ "assertions": [
66
+ "Level 3 Food Safety is marked not met: he holds Level 2, and his Level 3 is in Professional Cookery, which is a different qualification",
67
+ "Team size is marked short with both numbers: a brigade of 6 against 12 or more",
68
+ "Running the kitchen on the head chef's two days off is credited toward the sous-chef-with-full-responsibility requirement, quoting the CV",
69
+ "Costing is credited from the weekly specials, and the 70% gross profit target is noted as not shown",
70
+ "Banqueting is credited from covering weddings at Harbour Rooms Hotel, and hotel experience is marked met",
71
+ "The reply gives a clear answer on whether it is realistic",
72
+ "It does not credit him with a qualification, a cover count, a team size or a system that is not in the CV"
73
+ ]
74
+ }
75
+ ]
76
+ }
@@ -0,0 +1,28 @@
1
+ # Clinical Nurse Educator — Surgical Division
2
+
3
+ **Northfield General Hospital** · Full time, permanent · Band 6
4
+
5
+ Northfield General is a 320-bed teaching hospital. The Surgical Division's education team supports 240 nursing staff across five wards.
6
+
7
+ ## The role
8
+
9
+ You will plan and deliver clinical education for surgical nursing staff: induction for new starters, annual skills updates, and ward-based teaching after incidents and audits.
10
+
11
+ ## You must have
12
+
13
+ - Current NMC registration (adult)
14
+ - A nursing degree
15
+ - At least 2 years' experience teaching, precepting or mentoring clinical staff
16
+ - At least 3 years' post-registration experience in an acute surgical or medical-surgical setting
17
+ - Experience using Epic, including supporting colleagues with it
18
+
19
+ ## It would help if you have
20
+
21
+ - A master's degree in nursing or education, or to be working towards one
22
+ - Basic Life Support instructor certification
23
+ - Experience running sessions in a simulation lab
24
+ - Experience writing teaching materials or guidelines
25
+
26
+ ## How to apply
27
+
28
+ Send a CV and a cover letter of no more than one page to the Surgical Education Lead. Closing date 24 October 2026.
@@ -0,0 +1,35 @@
1
+ # Maya Okafor, RN, BSN
2
+
3
+ Leeds, UK · maya.okafor@example.com · 07700 900142
4
+
5
+ ## Profile
6
+
7
+ Registered nurse with six years on an acute medical-surgical ward. Charge nurse on rotation since 2023.
8
+
9
+ ## Experience
10
+
11
+ **Staff Nurse, then Charge Nurse (rotating)** — Ward 14 Medical-Surgical, Aldermoor Hospital
12
+ August 2020 – present
13
+
14
+ - Care for up to 6 post-operative and acute medical patients per shift on a 28-bed ward.
15
+ - Take charge of the ward on rotation since March 2023: staffing, bed flow and escalation.
16
+ - Precept newly qualified nurses through their first 12 weeks; 9 preceptees so far.
17
+ - Wrote the ward's one-page guide to the sepsis screening tool and walked each shift through it at handover.
18
+ - Document in Cerner; ward superuser for the Cerner medication administration module since 2022.
19
+ - Member of the falls prevention working group.
20
+
21
+ **Healthcare Assistant** — Rowan Court Care Home
22
+ June 2017 – July 2020 (part time during degree)
23
+
24
+ - Personal care, observations and mealtime support for 30 residents.
25
+
26
+ ## Education and registration
27
+
28
+ - BSc (Hons) Adult Nursing, University of Bradford, 2020
29
+ - NMC registered nurse (adult), registered 2020
30
+ - Mentorship and Assessment in Practice module, 2022
31
+ - Basic Life Support provider, renewed January 2026
32
+
33
+ ## Other
34
+
35
+ - Volunteer first aider, Leeds 10K, 2023 and 2024
@@ -0,0 +1,20 @@
1
+ Operations Coordinator
2
+ Kestrel Freight Solutions - Columbus, OH - Full time
3
+
4
+ About the job
5
+ The Operations Coordinator keeps our cross-dock running to plan: scheduling inbound and outbound loads, coordinating with carriers, and reporting daily performance to the Operations Manager.
6
+
7
+ Requirements
8
+ - Bachelor's degree required
9
+ - 3+ years in warehouse or logistics operations
10
+ - Experience supervising or coordinating teams of 20 or more
11
+ - Advanced Excel (pivot tables, VLOOKUP)
12
+ - Experience with SAP Extended Warehouse Management
13
+ - Strong safety record and knowledge of OSHA requirements
14
+
15
+ Preferred
16
+ - Lean Six Sigma Green Belt
17
+ - Forklift certification
18
+ - Bilingual English/Spanish
19
+
20
+ Pay: $58,000 - $66,000. Day shift, Monday to Friday.
@@ -0,0 +1,32 @@
1
+ Daniel Reyes
2
+ Columbus, OH | daniel.reyes@example.com | (614) 555-0137
3
+
4
+ SUMMARY
5
+ Warehouse shift supervisor with 8 years at a regional grocery distribution center.
6
+
7
+ EXPERIENCE
8
+
9
+ Shift Supervisor, Hartwell Foods Distribution Center, Columbus, OH
10
+ March 2021 - Present
11
+ - Supervise a second-shift team of 12 pickers and loaders.
12
+ - Build the weekly shift roster in Excel and track attendance.
13
+ - Zero lost-time incidents on my shift for the last 14 months.
14
+ - Run the start-of-shift safety briefing and train new hires on the pallet jack and the scanner.
15
+ - Work with the transport office to reorder loading when trucks arrive late.
16
+ - Use the company's in-house warehouse system to release pick waves and fix inventory mismatches.
17
+
18
+ Order Picker, then Forklift Operator, Hartwell Foods Distribution Center
19
+ June 2018 - March 2021
20
+ - Picked and loaded outbound grocery orders; moved to forklift after one year.
21
+ - Picked as trainer for new forklift operators in 2020.
22
+
23
+ Stock Associate, Brightway Home Improvement, Dayton, OH
24
+ 2016 - 2018
25
+
26
+ EDUCATION
27
+ Westland High School, diploma, 2015
28
+ Columbus State Community College, 18 credits toward an associate degree in business (2019-2020, not completed)
29
+
30
+ CERTIFICATIONS
31
+ OSHA forklift operator certification, renewed 2025
32
+ First Aid/CPR, 2025
@@ -0,0 +1,25 @@
1
+ # Finance Analyst
2
+
3
+ **Orrell Leisure Group** · Birmingham · £36,000 – £40,000
4
+
5
+ Orrell runs 22 gyms across the Midlands. The Finance Analyst reports to the Financial Controller and produces the monthly management pack for site managers.
6
+
7
+ ## What you'll do
8
+
9
+ - Build the monthly management accounts pack: variance to budget by site, with commentary
10
+ - Own the month-end close for payroll and utilities accruals
11
+ - Maintain the site-level budget model and reforecast each quarter
12
+ - Build dashboards in Power BI for the operations team
13
+
14
+ ## What we need
15
+
16
+ - Part-qualified ACCA or CIMA, or AAT Level 4 complete
17
+ - At least 2 years in a management accounts or analyst role
18
+ - Advanced Excel (pivot tables, lookups, modelling)
19
+ - Experience preparing variance analysis and budget commentary
20
+ - Experience with Power BI or a similar reporting tool
21
+
22
+ ## Nice to have
23
+
24
+ - Experience in a multi-site business
25
+ - Experience with Sage or NetSuite