Local-first agent skill for turning a saved job post and candidate notes into a reviewable application brief.
npm install
npm test
npm run smoke
node bin/jobsearch-skill.js fixtures/job-post.md --candidate fixtures/candidate-notes.md --format jsonFor a human-readable brief:
node bin/jobsearch-skill.js fixtures/job-post.md --candidate fixtures/candidate-notes.md --format markdown
The package root exports createApplicationBrief, parseCandidateNotes,
parseJobPost, and renderMarkdown for ECMAScript module consumers:
import { createApplicationBrief, renderMarkdown } from 'jobsearch-skill'; const brief = createApplicationBrief(jobPostMarkdown, candidateNotesMarkdown); console.log(renderMarkdown(brief));
- Role metadata and seniority signals
- Requirement-to-evidence map
- Fit score based on local candidate notes
- Missing evidence and risk flags
- Next actions with approval boundaries
The job post should be a local Markdown or text file containing the role title,
responsibilities, requirements, and any metadata you want reflected in the
brief. Location is read only from an explicit Location: field; unlabelled
location prose is not promoted into the brief. Candidate notes should be local
evidence supplied by the user, not scraped profile data.
Requirement bullets are read from Requirements, Minimum Requirements,
Qualifications, Minimum Qualifications, Required Qualifications, or
Requirements & Qualifications Markdown sections using ATX heading levels H2
through H6 (for example, ## Requirements:, ## Requirements & Qualifications,
or #### Qualifications). Trailing heading punctuation is ignored. The
level-one heading remains the role title and is not treated as a section. When a post contains multiple
recognized sections, their bullets are combined in document order and
duplicates are omitted. A
recognized section may be empty; if every recognized section is empty, the
parsed requirements are empty. For compatibility with unstructured saved posts,
when none of those headings is present, all bullets in the document are treated
as requirements. Candidate notes accept Skills, Projects,
Constraints, and Proof, Evidence, or Supporting Evidence as either
Markdown headings (for example, ## Skills) or colon-terminated headings (for
example, Skills:). List items may use - or *, with zero to three leading
spaces as allowed for standard Markdown list markers. Lines indented four or
more spaces are not parsed as list items, so indented code or unrelated content
is not absorbed into the brief. Standard ATX closing hash sequences are also
supported for role, job-section, and candidate-note headings (for example,
# Platform Engineer at Example Robotics ## and ### Requirements ###).
Role classification is deliberately contextual. Seniority is inferred from the
role title, remote-friendly status from the title or explicit Location field,
contract status from the title or an Employment type, Job type, or
Engagement field, and startup status from a Company stage or Funding stage
field. Explicit remote
negation is respected. A title can therefore classify a role as remote-friendly
without becoming its parsed location; only Location: supplies that displayed
value. This avoids treating phrases such as "lead incident reviews", "remote
systems", "remote-friendly culture", "contract testing", and "application
startup time" as job metadata. Unusual titles or unlabelled prose may therefore
remain unclassified and should be reviewed rather than assumed.
Company metadata is accepted either as a labelled Company: Example Robotics
line or in a level-one role heading such as # Platform Engineer at Example Robotics. An unlabelled at Capitalized Phrase elsewhere in the post is
treated as ordinary prose, not an employer name.
Application instructions are taken from How to Apply, Application,
Application Instructions, Application Process, or Apply sections. In
unstructured text, only lines that begin with a direct application phrase such
as "To apply", "Please send", or "Email your" are included. Keyword mentions in
requirements and responsibilities are not treated as application guidance.
Evidence matching is deterministic token matching, not semantic inference. A
requirement matches a candidate skill, project, or proof item when they share
at least one distinctive, case-insensitive token. Common qualifiers and broad
terms such as experience, skills, and development are ignored, so Java development does not satisfy Python development; an item that mentions
Python does. Exact, distinctive short technology names (C++, C#, Go,
SQL, and AWS) are also recognized, while arbitrary short words remain
ignored. This intentionally conservative heuristic can miss synonyms,
abbreviations, and related technologies outside that set, so review both
matches and missing evidence rather than treating the fit score as a hiring
decision.
The CLI accepts exactly one job-post path plus optional --candidate and
--format flags. Flags may appear before or after the job-post path, and each
flag may be supplied at most once. --format accepts only markdown (the
default) or json. Missing values, unknown flags, duplicate flags, and extra
positional arguments produce a concise usage error and exit status 2.
Standalone --help prints usage and exits successfully; when combined with a
job-post path or any other argument, it is rejected as an unsupported option
with the same status 2 usage error.
Both inputs must name readable, regular local files; directories and other
non-file paths are rejected. Missing, unreadable, and non-file inputs also exit
with status 2 and print a path-specific diagnostic plus the CLI usage, without
a Node.js stack trace.
Run the release-readiness gate before publishing or sharing an updated skill:
npm run check
npm run lint
npm test
npm run smoke
npm run package:smoke
npm run release:checknpm run lint is the contributor-facing static check alias used by the release
gate, and currently delegates to the same local package validation as
npm run check. The package smoke creates the tarball, installs it in a
disposable consumer, imports the package root, exercises the library API, and
runs the installed CLI, including a packaged input-error case.
The parser is deterministic and intentionally conservative. It does not scrape live job boards, submit applications, send messages, or invent credentials.
Treat the output as a planning brief. A human should approve any downstream external action such as submitting an application, contacting a recruiter, or saving data to an external CRM.