name: capacite-emprunt-fr description: "Generate French mortgage capacity per HCSF rules (35% DTI, 25-year cap) with live rates. Use when a buyer asks how much to borrow; outputs 3 scenarios (15/20/25 ans). Don't use for consumer credit, debt consolidation, non-France loans, tax advice." license: MIT metadata: version: 1.4.1 author: francais-skills effort: high
capacite-emprunt-fr — Capacité d'emprunt immobilier (France)
Estimate how much a buyer can borrow from a French bank, respecting current HCSF rules and today's Meilleurtaux/Empruntis barème.
When to Use
- "Combien je peux emprunter pour acheter ?" / "quelle est ma capacité d'emprunt ?"
- "Avec X € de revenus et Y € de crédit voiture, je peux acheter à combien ?"
- "Simule mon prêt immobilier sur 15 / 20 / 25 ans."
- "Quel est mon taux d'endettement maximal selon le HCSF ?"
- "Voilà mon bulletin de salaire / tableau d'amortissement / avis d'impôt — combien je peux emprunter ?"
- "Recalcule avec mon profil existant" / "Charge mon profil
" / "Sauve ce profil sous " - The user invokes
/capacite-emprunt-frwith or without arguments.
Do not trigger for: crédit à la consommation, rachat / regroupement de crédits, prêt professionnel, simulation immobilière hors France (Belgique, Suisse, Luxembourg), ou questions de fiscalité.
Output: a single FR markdown briefing
Write the result inline in the conversation as a single French markdown briefing — no HTML file, no separate report. Keep it under one screen so the user can act on it.
Sections, in order:
- Vos données — recap of inputs (revenus, charges, apport, profil).
- Règles HCSF appliquées — with "Source au [date]" line citing HCSF and Meilleurtaux.
- 3 scénarios — markdown table for 15/20/25 ans. Columns: durée, taux nominal, taux assurance, mensualité, capital empruntable, prix max bien (capital + apport − frais de notaire estimés), coût total des intérêts.
- Marge de manœuvre HCSF — one paragraph: banks may deviate up to 20% of new loans per quarter (≥70% reserved for résidence principale, 30% for primo-accédants), so an atypical profile can still pass.
- Avertissement — one short sentence: "Ce calcul est indicatif. Seule une banque ou un courtier valide votre dossier."
See references/output-template.md for the exact structure to render.
Prerequisites
python3available on PATH; the bundled scripts requiremarkitdownfor PDF parsing.- WebFetch access to
legifrance.gouv.frandempruntis.com. Cloudflare-protected sources (economie.gouv.fr, cafpi.fr) return 403 and are listed only as secondaries — do not rely on them. - Write access to
~/.francais-skills/profiles/(created at mode 0700 on first use). The profile store will fail with a descriptive error if the directory is read-only.
Error handling
- WebFetch timeout or 403 — retry once. If it still fails, fall back to
references/sources.md, mark the report line× fallback, and warn the user the rate is stale. compute_capacity.pynon-zero exit — surface stderr to the user with the offending input. Never patch the script's output silently; ask the user to validate the input that triggered the error.- PDF parser returns empty fields — do not guess; ask the user for the missing figure directly. Confirm any extracted number before using it.
- Profile save failure — the script rolls back to the pre-edit file and prints a caution line. Tell the user the slug was not written and ask whether to retry under a different name.
- Conflicting inputs (e.g. bulletin says 3 000 € but user types 4 000 €) — flag the discrepancy, ask which to use, never silently override.
Workflow
Always follow these phases in order. Each phase ends with a one-line status before moving on.
Phase 0 — Pick or create a profile
Profiles live at ~/.francais-skills/profiles/{slug}.json and persist across conversations so the user never re-enters the same numbers twice. Schema is documented in references/profile-schema.md. CRUD via scripts/profile_store.py. See references/inputs.md for the decision tree (recalcule, sauve sous, no-mention, PDF-attached).
Phase 1 — Collect/update inputs
Three input modes: A interactive (AskUserQuestion in one batch), B PDFs (run the matching parser script per attached file), or C load profile (already done in Phase 0). Full required-fields list, parser commands, and validation rules live in references/inputs.md. Always confirm extracted figures with the user before computing — a single misread comma can swing the answer 10×.
Phase 2 — Fetch fresh data (mandatory)
Use WebFetch on every invocation — never trust cached values from prior turns. Fetch in parallel:
- HCSF rules — primary URL, extraction prompt, fallbacks: see
references/sources.md. - Barème des taux — primary URL, secondary fallback, blocking notes: see
references/sources.md.
Record the date of fetch for each source. Both go into the output's "Source au [date]" line. If both primary and secondary fail, fall back to the reference values in references/sources.md and explicitly tell the user the source was unreachable so they know the numbers may be stale.
Phase 3 — Compute (deterministic)
Run scripts/compute_capacity.py with the collected inputs and fetched values. The agent must not do this math inline — always shell out to the script so the calculation is reproducible and testable.
python3 scripts/compute_capacity.py \
--revenus 4500 --revenus2 0 --charges 250 --apport 50000 \
--type-bien ancien --assurance 0.0034 \
--taux-15 0.0320 --taux-20 0.0333 --taux-25 0.0343 \
--dti-max 0.35
The script sums income, computes the 35% DTI cap assurance comprise, subtracts existing monthly debts, back-solves the amortization formula for each duration (15/20/25 ans), and adds apport minus frais de notaire to get the max property price. Output is JSON on stdout that the agent reads into the template.
Phase 4 — Render
Render the FR markdown briefing using references/output-template.md. Do not invent rules, dates, or rates that weren't fetched in Phase 2 — every regulatory claim must carry a "Source au [date]" line tied to the WebFetch result.
Phase 5 — Persist profile
After rendering, save the (possibly updated) profile back to ~/.francais-skills/profiles/{slug}.json via profile_store.py save. What to persist vs. what to skip is documented in references/inputs.md and references/profile-schema.md.
Phase 6 — Status report
End with the Step Completion Report below so the user sees what was loaded, fetched, computed, and saved.
Step Completion Report
After Phase 6, emit:
◆ Capacité d'emprunt FR (terminé)
··································································
Profil: √ chargé (<slug>) / √ nouveau / × pas de profil
Inputs collectés: √ pass (mode A interactif / B PDFs / C profil)
HCSF récupéré: √ pass (source du YYYY-MM-DD)
Taux récupérés: √ pass (source du YYYY-MM-DD)
Calcul script: √ pass
3 scénarios rendus: √ pass
Profil sauvé: √ pass (<slug>) / — non sauvé
____________________________
Result: PASS
If a fetch fell back to references/, mark that line with × fallback — using stale data from <date> instead of √ pass.
Acceptance Criteria
Verify each before declaring done:
- Vos données section lists revenus, charges, apport, type de bien — every supplied or extracted field appears with its numeric value.
- HCSF block names 35% DTI, 25-year max, 20% flexibility, each with a "Source au YYYY-MM-DD" line from the WebFetch run.
- 3-scenario table has exactly three rows (15/20/25 ans) and the columns listed in
references/output-template.md. - Mensualité on each row ≤ 35% of total monthly income (assurance comprise) minus existing debts.
- Closing "Ce calcul est indicatif" disclaimer appears verbatim.
scripts/compute_capacity.pywas invoked exactly once (no inline math). Its JSON matches the table numbers.- A profile was loaded or saved (never both skipped); the Step Completion Report shows the slug.
- Output is markdown, not HTML, and fits in one screen.
Expected output
A short FR markdown briefing with the five sections listed in Output above. Sample shape (numbers illustrative):
## Vos données
- Profil : couple co-emprunteurs · 4 800 € + 2 100 € = 6 900 € · charges 280 € · apport 60 000 € · ancien
## Règles HCSF appliquées
- DTI max **35%** · durée max **25 ans** · marge flexibilité **20%** — Source au 2026-05-14
## 3 scénarios
| Durée | Taux | Mensualité | Capital | Prix max | Intérêts |
|--------|------|------------|---------|----------|----------|
| 15 ans | 3.20 % | 2 135 € | 273 400 € | 309 800 € | 110 900 € |
| 20 ans | 3.33 % | 2 135 € | 339 100 € | 375 500 € | 178 200 € |
| 25 ans | 3.43 % | 2 135 € | 393 800 € | 430 200 € | 247 700 € |
## Marge de manœuvre HCSF
Les banques peuvent déroger jusqu'à 20% de leurs nouveaux crédits par trimestre…
## Avertissement
Ce calcul est indicatif. Seule une banque ou un courtier valide votre dossier.
See references/output-template.md for the full column set and formatting rules.
Edge cases
Critical cases the agent must detect:
- Mensualité > 35% DTI on every duration — emit scenarios with the max-allowed payment and flag over-budget. Never zero out a row.
- Apport ≥ price of target bien — surface as "pas besoin d'emprunter"; ask the user how to proceed.
- RFR ÷ 12 diverges from bulletin × 13 by >20% — flag the discrepancy; keep bulletin-derived income unless user overrides.
- Both Phase-2 sources fail — fall back to
references/sources.md, mark report line× fallback, warn the user the rate is stale. - User asks for advice ("should I take 20 or 25 ans?") — refuse to recommend a duration, bank, or whether to buy.
- Non-France purchase (Belgique, Suisse, Luxembourg) — do not trigger; HCSF rules don't apply.
Full list (joint borrowers, profile-slug collisions, foreign currency income, TNS / self-employed flow): see references/edge-cases.md.
Important rules (do not skip)
- No financial advice — estimate capacity, never recommend a duration, bank, or whether to buy.
- No hardcoded rates or rules — every regulatory number (35%, 25 ans, 20%) and every rate comes from Phase 2 WebFetch.
- No HTML output — markdown only.
- Round to the nearest euro in display; keep raw precision in the script JSON.
- Joint borrowers: sum incomes, sum debts at 100% for résidence principale.
- DTI includes insurance — the 35% cap is assurance comprise; the script handles this.
Reference files
references/inputs.md— Phase 1 input modes, parser commands, profile decisions, what to persist.references/sources.md— Authoritative URLs, fallback values, extraction prompts.references/output-template.md— Exact FR markdown structure for the briefing.references/profile-schema.md— JSON schema for~/.francais-skills/profiles/{slug}.jsonand privacy rules.scripts/compute_capacity.py— Deterministic capacity solver. Returns JSON.scripts/profile_store.py— Profile CRUD: list / show / save / delete / field.scripts/parse_bulletin.py,parse_amortissement.py,parse_avis_impot.py— PDF → JSON parsers (markitdown-based).
This indirection keeps the agent's context budget small — SKILL.md stays scannable, and references are pulled only on demand.
Privacy & PII
Profiles store personal data (employer, salary, capital, RFR) under ~/.francais-skills/ (dir 0700, files 0600). Never put profile contents in commit messages, git diffs, web-pasted output, or telemetry. When the user shares a profile with a courtier, redact employer name, numéro fiscal, and account numbers first. See references/profile-schema.md for the full privacy rules.
Examples (for triggering, not training)
- "Mon couple gagne 5500 net, on a 60k d'apport, on cherche un appart ancien — combien on peut emprunter ?" → triggers.
- "Capacité d'emprunt sur 25 ans avec 3800 € de revenus et un crédit voiture de 320 €/mois" → triggers.
- "Quel est le meilleur courtier ?" → does NOT trigger (recommendation, not estimation).
- "Combien je paie d'impôts si j'achète un appart en LMNP ?" → does NOT trigger (fiscalité).