# How the AI Job Search Framework Maintains Language-Agnostic Core Components

> Discover how the AI Job Search framework uses a thin-pointer architecture for language-agnostic core components, processing only structured data and placeholder tokens for efficient, universal application.

- Repository: [Mads Lorentzen/ai-job-search](https://github.com/MadsLorentzen/ai-job-search)
- Tags: architecture
- Published: 2026-08-29

---

**The AI Job Search framework achieves language agnosticism through a thin-pointer architecture that isolates all natural-language specifics to profile data and pluggable skill modules, ensuring the core pipeline manipulates only structured data and placeholder tokens.**

The MadsLorentzen/ai-job-search repository demonstrates a deliberate architectural strategy to remain portable across any locale or natural language. By treating language and market specifics as external configuration rather than embedded logic, the framework ensures its core components never assume a particular region or tongue. This design allows the same codebase to serve job seekers in Copenhagen, Berlin, or Tokyo without modification to the engine itself.

## Thin-Pointer Architecture as the Foundation

The framework implements a **thin-pointer** pattern where high-level specifications live under `.claude/` and the core engine loads these at runtime. According to the source code in [`AGENTS.md`](https://github.com/MadsLorentzen/ai-job-search/blob/main/AGENTS.md), this approach means the engine never hard-codes language or market details. Instead, it discovers commands, skills, and settings dynamically.

This architecture creates a clean separation: the core pipeline (`/setup → /scrape → /apply`) described in [`README.md`](https://github.com/MadsLorentzen/ai-job-search/blob/main/README.md) manipulates only structured formats like JSON, CSV, and LaTeX fragments. Any language-specific handling remains delegated to isolated modules, making the workflow inherently **language- and country-agnostic**.

## Profile-Driven Language Handling

All language information resides in a single source of truth: the candidate profile stored in [`CLAUDE.md`](https://github.com/MadsLorentzen/ai-job-search/blob/main/CLAUDE.md). This file contains a dedicated *Languages* table that drives the entire system's linguistic behavior.

The **Language Gate**—implemented in [`.claude/skills/job-application-assistant/04-job-evaluation.md`](https://github.com/MadsLorentzen/ai-job-search/blob/main/.claude/skills/job-application-assistant/04-job-evaluation.md)—reads this table to apply veto or flag logic during job evaluation. However, the rest of the system treats the language field as opaque data. This isolation ensures that adding support for a new language requires only editing [`CLAUDE.md`](https://github.com/MadsLorentzen/ai-job-search/blob/main/CLAUDE.md), leaving the core codebase untouched.

### Runtime Query Generation

The scraper generates search queries dynamically by iterating over the languages listed in the profile:

```python

# pseudo-code used by the scraper

for lang in profile.languages:          # profile read from CLAUDE.md

    for category in SEARCH_CATEGORIES:
        query = f"{category} {lang.name}"
        run_query(query)

```

This loop operates regardless of how many languages are declared, with zero hard-coded language strings in the core logic.

## Template-Based Document Generation

The CV and cover-letter system uses **placeholder-only skeletons** stored in `template.<ext>` files. As documented in [`templates/README.md`](https://github.com/MadsLorentzen/ai-job-search/blob/main/templates/README.md), these templates contain only `[PLACEHOLDER]` tokens and no hard-coded prose.

Because the templates are language-agnostic structures, the same file works for English, Danish, Spanish, or any other language. The system simply injects appropriate language strings at runtime after determining the posting's language:

```python
template = read_file("template.tex")
for token, value in placeholders.items():
    template = template.replace(f"[{token}]", value)
write_file("cv.tex", template)

```

## Country-Agnostic Portal CLIs

Skills such as `linkedin-search` and `freehire-search` function as **country-agnostic CLIs** accepting a `--location` flag that accepts any city, region, or "Remote" value. According to [`.agents/skills/linkedin-search/SKILL.md`](https://github.com/MadsLorentzen/ai-job-search/blob/main/.agents/skills/linkedin-search/SKILL.md), these tools contain zero runtime dependencies and embed no country-specific URLs.

This design allows the same binary to operate across any market:

```bash

# In .agents/skills/example-search/cli

bun run example-search.ts --location "Berlin, Germany"

```

The CLI receives the target location at runtime rather than compiling it into the source, ensuring universal applicability.

## Contribution Policies Enforcing Neutrality

The framework codifies its agnostic stance in [`CONTRIBUTING.md`](https://github.com/MadsLorentzen/ai-job-search/blob/main/CONTRIBUTING.md), which explicitly states the repository serves as a **universal template** that remains "market-agnostic, person-agnostic." This policy mandates that upstream contributions must not embed language-specific logic into core components. Language-specific adaptations belong in forks or isolated skill modules, preventing bloat and maintaining the thin-pointer integrity.

## Summary

- The **thin-pointer architecture** loads specifications from `.claude/` at runtime, eliminating hard-coded locale dependencies.
- **Profile-driven design** centralizes all language data in [`CLAUDE.md`](https://github.com/MadsLorentzen/ai-job-search/blob/main/CLAUDE.md), treating language rules as external configuration rather than embedded logic.
- **Placeholder-based templates** generate documents in any language by replacing tokens at runtime rather than storing translated strings.
- **Country-agnostic CLIs** accept location parameters at runtime, allowing the same skills to operate across global markets without code changes.
- **Strict contribution policies** in [`CONTRIBUTING.md`](https://github.com/MadsLorentzen/ai-job-search/blob/main/CONTRIBUTING.md) enforce the universal template philosophy, ensuring the core remains portable.

## Frequently Asked Questions

### Can I use the AI Job Search framework for non-English job markets without modifying the core code?

Yes. The framework treats language specifications as data in [`CLAUDE.md`](https://github.com/MadsLorentzen/ai-job-search/blob/main/CLAUDE.md). You only need to populate the *Languages* table in your profile and ensure your templates contain the appropriate placeholder mappings. The core engine in `.claude/skills/job-application-assistant/` reads these values at runtime without requiring source modifications.

### How does the Language Gate prevent applying to jobs in unsupported languages?

The Language Gate logic in [`04-job-evaluation.md`](https://github.com/MadsLorentzen/ai-job-search/blob/main/04-job-evaluation.md) compares the job posting's detected language against the *Languages* table in [`CLAUDE.md`](https://github.com/MadsLorentzen/ai-job-search/blob/main/CLAUDE.md). If the language does not appear in your profile or falls below your specified proficiency threshold, the gate applies a veto flag. This check occurs early in the pipeline, ensuring downstream components never process incompatible listings.

### What prevents contributors from embedding Danish or English-specific URLs in new portal skills?

The [`CONTRIBUTING.md`](https://github.com/MadsLorentzen/ai-job-search/blob/main/CONTRIBUTING.md) file explicitly forbids embedding market-specific logic in upstream contributions. Additionally, the skill architecture requires CLIs to accept location via runtime flags like `--location` rather than hard-coding URLs. Reviewers enforce that new skills under `.agents/skills/` remain country-agnostic utilities that accept parameters rather than carrying embedded locale assumptions.

### Do I need separate template files for each language I want to support?

No. The template system uses language-agnostic placeholder skeletons. You maintain a single `template.tex` or similar file containing `[PLACEHOLDER]` tokens. The `apply` command replaces these tokens with language-appropriate content drawn from your profile at document generation time, allowing one template to serve multilingual outputs.