fec-lint 0.1.0__tar.gz

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.
@@ -0,0 +1,8 @@
1
+ __pycache__/
2
+ *.py[cod]
3
+ .venv/
4
+ venv/
5
+ dist/
6
+ build/
7
+ *.egg-info/
8
+ .pytest_cache/
fec_lint-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Vincent
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,147 @@
1
+ Metadata-Version: 2.5
2
+ Name: fec-lint
3
+ Version: 0.1.0
4
+ Summary: Free structural linter for French FEC files (Fichier des Écritures Comptables), CLI + MCP server
5
+ Project-URL: Homepage, https://github.com/vincenz-cdf/fec-lint
6
+ Project-URL: Full pre-audit, https://apify.com/highfiddle/fec-checker
7
+ Project-URL: Issues, https://github.com/vincenz-cdf/fec-lint/issues
8
+ Author: Vincent
9
+ License-Expression: MIT
10
+ License-File: LICENSE
11
+ Keywords: accounting,comptabilite,dgfip,fec,france,mcp
12
+ Classifier: Natural Language :: English
13
+ Classifier: Natural Language :: French
14
+ Classifier: Operating System :: OS Independent
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Topic :: Office/Business :: Financial :: Accounting
17
+ Requires-Python: >=3.10
18
+ Requires-Dist: mcp<3,>=2
19
+ Provides-Extra: test
20
+ Requires-Dist: pytest>=8; extra == 'test'
21
+ Description-Content-Type: text/markdown
22
+
23
+ # fec-lint
24
+
25
+ **[English](#english) · [Français](#français)**
26
+
27
+ <!-- mcp-name: io.github.vincenz-cdf/fec-lint -->
28
+
29
+ > **Technical check, not tax advice.** fec-lint checks the structure of a file against the published FEC format. It does not certify a file or replace an accountant's review.
30
+ >
31
+ > **Contrôle technique, pas un conseil fiscal.** fec-lint vérifie la structure d'un fichier au regard du format FEC publié. Il ne certifie pas un fichier et ne remplace pas la revue d'un expert-comptable.
32
+
33
+ ---
34
+
35
+ ## English
36
+
37
+ **Free structural linter for French FEC files (*Fichier des Écritures Comptables*, art. A47 A-1 LPF), as a command line tool and an MCP server.**
38
+
39
+ ### Install
40
+
41
+ ```bash
42
+ pipx install fec-lint
43
+ ```
44
+
45
+ ### Command line
46
+
47
+ ```bash
48
+ fec-lint 123456782FEC20251231.txt
49
+ ```
50
+
51
+ ```text
52
+ 123456782FEC20251231.txt: FAILED (encoding utf-8, separator pipe, lines 214)
53
+ error date_invalid x3: Non-existent date in EcritureDate. - line(s) 21, 22, 23
54
+ error date_format x3: Wrong date format in EcritureDate (expected AAAAMMJJ). - line(s) 64, 65, 66
55
+ Structure has errors. Full pre-audit (balance, numbering, risk signals): https://apify.com/highfiddle/fec-checker
56
+ ```
57
+
58
+ - `--json` prints the full result: `{ok, file, errors[], warnings[], stats}`.
59
+ - `--name 123456782FEC20251231.txt` sets the name checked against `SIRENFECAAAAMMJJ.txt`.
60
+ - `--quiet` hides the last line.
61
+ - Exit code: `0` when the structure is OK, `1` on errors, `2` when a file cannot be read. Several files can be given at once.
62
+ - A `.zip` holding exactly one `.txt` FEC is unpacked first.
63
+
64
+ ### MCP server
65
+
66
+ Add it to your MCP client (Claude Desktop, Claude Code, Cursor…):
67
+
68
+ ```json
69
+ {
70
+ "mcpServers": {
71
+ "fec-lint": {
72
+ "command": "uvx",
73
+ "args": ["--from", "fec-lint", "fec-lint-mcp"]
74
+ }
75
+ }
76
+ }
77
+ ```
78
+
79
+ Without `uv`, install with `pipx install fec-lint` and use `"command": "fec-lint-mcp"`. The server exposes one tool, `lint_fec(path)`, which takes a local file path and returns the same JSON as `--json`.
80
+
81
+ ### Free vs full pre-audit
82
+
83
+ | Check | fec-lint (free) | [FEC Checker on Apify](https://apify.com/highfiddle/fec-checker) |
84
+ |---|---|---|
85
+ | File name `SIRENFECAAAAMMJJ.txt`, SIREN key | ✅ | ✅ |
86
+ | Encoding, separator, 18 headers | ✅ | ✅ |
87
+ | Field formats: dates, amounts, mandatory fields, pairs | ✅ | ✅ |
88
+ | Balance per entry and for the whole file | — | ✅ |
89
+ | EcritureNum gaps and duplicates, ValidDate chronology | — | ✅ |
90
+ | Risk signals: weekends, holidays, round amounts, Benford, cash 53x, suspense 471, after year end | — | ✅ |
91
+ | Verdict, 0-100 score, AI-written fix list (FR/EN) | — | ✅ |
92
+ | Runs | locally, nothing leaves your machine | on Apify, pay per file |
93
+
94
+ ---
95
+
96
+ ## Français
97
+
98
+ **Linter structurel gratuit pour les FEC (*Fichier des Écritures Comptables*, art. A47 A-1 LPF), en ligne de commande et en serveur MCP.**
99
+
100
+ ### Installation
101
+
102
+ ```bash
103
+ pipx install fec-lint
104
+ ```
105
+
106
+ ### Ligne de commande
107
+
108
+ ```bash
109
+ fec-lint 123456782FEC20251231.txt
110
+ ```
111
+
112
+ - `--json` affiche le résultat complet : `{ok, file, errors[], warnings[], stats}`.
113
+ - `--name 123456782FEC20251231.txt` indique le nom à contrôler selon `SIRENFECAAAAMMJJ.txt`.
114
+ - `--quiet` masque la dernière ligne.
115
+ - Code de sortie : `0` si la structure est correcte, `1` en cas d'erreur, `2` si un fichier est illisible. Plusieurs fichiers peuvent être passés à la fois.
116
+ - Un `.zip` contenant un seul FEC `.txt` est décompressé d'abord.
117
+
118
+ Les messages sont en anglais ; les codes de règle (`date_invalid`, `header_missing`…) sont stables.
119
+
120
+ ### Serveur MCP
121
+
122
+ Ajoutez-le à votre client MCP (Claude Desktop, Claude Code, Cursor…) avec la configuration ci-dessus. L'outil `lint_fec(path)` prend le chemin d'un fichier local et renvoie le même JSON que `--json`.
123
+
124
+ ### Gratuit ou pré-audit complet
125
+
126
+ | Contrôle | fec-lint (gratuit) | [FEC Checker sur Apify](https://apify.com/highfiddle/fec-checker) |
127
+ |---|---|---|
128
+ | Nom `SIRENFECAAAAMMJJ.txt`, clé du SIREN | ✅ | ✅ |
129
+ | Encodage, séparateur, 18 colonnes | ✅ | ✅ |
130
+ | Formats : dates, montants, champs obligatoires, couples de champs | ✅ | ✅ |
131
+ | Équilibre par écriture et du fichier | — | ✅ |
132
+ | Ruptures et doublons d'EcritureNum, chronologie de ValidDate | — | ✅ |
133
+ | Signaux de risque : week-ends, jours fériés, montants ronds, Benford, caisse 53x, attente 471, après clôture | — | ✅ |
134
+ | Verdict, score 0-100, plan de correction rédigé par IA (FR/EN) | — | ✅ |
135
+ | Exécution | en local, rien ne quitte votre machine | sur Apify, paiement par fichier |
136
+
137
+ ---
138
+
139
+ ## Development
140
+
141
+ ```bash
142
+ python -m venv .venv
143
+ .venv/bin/pip install -e ".[test]" # Windows: .venv\Scripts\pip
144
+ pytest
145
+ ```
146
+
147
+ The fixtures in `tests/fixtures/` are synthetic FEC files (fictitious SIREN 123456782). MIT license.
@@ -0,0 +1,125 @@
1
+ # fec-lint
2
+
3
+ **[English](#english) · [Français](#français)**
4
+
5
+ <!-- mcp-name: io.github.vincenz-cdf/fec-lint -->
6
+
7
+ > **Technical check, not tax advice.** fec-lint checks the structure of a file against the published FEC format. It does not certify a file or replace an accountant's review.
8
+ >
9
+ > **Contrôle technique, pas un conseil fiscal.** fec-lint vérifie la structure d'un fichier au regard du format FEC publié. Il ne certifie pas un fichier et ne remplace pas la revue d'un expert-comptable.
10
+
11
+ ---
12
+
13
+ ## English
14
+
15
+ **Free structural linter for French FEC files (*Fichier des Écritures Comptables*, art. A47 A-1 LPF), as a command line tool and an MCP server.**
16
+
17
+ ### Install
18
+
19
+ ```bash
20
+ pipx install fec-lint
21
+ ```
22
+
23
+ ### Command line
24
+
25
+ ```bash
26
+ fec-lint 123456782FEC20251231.txt
27
+ ```
28
+
29
+ ```text
30
+ 123456782FEC20251231.txt: FAILED (encoding utf-8, separator pipe, lines 214)
31
+ error date_invalid x3: Non-existent date in EcritureDate. - line(s) 21, 22, 23
32
+ error date_format x3: Wrong date format in EcritureDate (expected AAAAMMJJ). - line(s) 64, 65, 66
33
+ Structure has errors. Full pre-audit (balance, numbering, risk signals): https://apify.com/highfiddle/fec-checker
34
+ ```
35
+
36
+ - `--json` prints the full result: `{ok, file, errors[], warnings[], stats}`.
37
+ - `--name 123456782FEC20251231.txt` sets the name checked against `SIRENFECAAAAMMJJ.txt`.
38
+ - `--quiet` hides the last line.
39
+ - Exit code: `0` when the structure is OK, `1` on errors, `2` when a file cannot be read. Several files can be given at once.
40
+ - A `.zip` holding exactly one `.txt` FEC is unpacked first.
41
+
42
+ ### MCP server
43
+
44
+ Add it to your MCP client (Claude Desktop, Claude Code, Cursor…):
45
+
46
+ ```json
47
+ {
48
+ "mcpServers": {
49
+ "fec-lint": {
50
+ "command": "uvx",
51
+ "args": ["--from", "fec-lint", "fec-lint-mcp"]
52
+ }
53
+ }
54
+ }
55
+ ```
56
+
57
+ Without `uv`, install with `pipx install fec-lint` and use `"command": "fec-lint-mcp"`. The server exposes one tool, `lint_fec(path)`, which takes a local file path and returns the same JSON as `--json`.
58
+
59
+ ### Free vs full pre-audit
60
+
61
+ | Check | fec-lint (free) | [FEC Checker on Apify](https://apify.com/highfiddle/fec-checker) |
62
+ |---|---|---|
63
+ | File name `SIRENFECAAAAMMJJ.txt`, SIREN key | ✅ | ✅ |
64
+ | Encoding, separator, 18 headers | ✅ | ✅ |
65
+ | Field formats: dates, amounts, mandatory fields, pairs | ✅ | ✅ |
66
+ | Balance per entry and for the whole file | — | ✅ |
67
+ | EcritureNum gaps and duplicates, ValidDate chronology | — | ✅ |
68
+ | Risk signals: weekends, holidays, round amounts, Benford, cash 53x, suspense 471, after year end | — | ✅ |
69
+ | Verdict, 0-100 score, AI-written fix list (FR/EN) | — | ✅ |
70
+ | Runs | locally, nothing leaves your machine | on Apify, pay per file |
71
+
72
+ ---
73
+
74
+ ## Français
75
+
76
+ **Linter structurel gratuit pour les FEC (*Fichier des Écritures Comptables*, art. A47 A-1 LPF), en ligne de commande et en serveur MCP.**
77
+
78
+ ### Installation
79
+
80
+ ```bash
81
+ pipx install fec-lint
82
+ ```
83
+
84
+ ### Ligne de commande
85
+
86
+ ```bash
87
+ fec-lint 123456782FEC20251231.txt
88
+ ```
89
+
90
+ - `--json` affiche le résultat complet : `{ok, file, errors[], warnings[], stats}`.
91
+ - `--name 123456782FEC20251231.txt` indique le nom à contrôler selon `SIRENFECAAAAMMJJ.txt`.
92
+ - `--quiet` masque la dernière ligne.
93
+ - Code de sortie : `0` si la structure est correcte, `1` en cas d'erreur, `2` si un fichier est illisible. Plusieurs fichiers peuvent être passés à la fois.
94
+ - Un `.zip` contenant un seul FEC `.txt` est décompressé d'abord.
95
+
96
+ Les messages sont en anglais ; les codes de règle (`date_invalid`, `header_missing`…) sont stables.
97
+
98
+ ### Serveur MCP
99
+
100
+ Ajoutez-le à votre client MCP (Claude Desktop, Claude Code, Cursor…) avec la configuration ci-dessus. L'outil `lint_fec(path)` prend le chemin d'un fichier local et renvoie le même JSON que `--json`.
101
+
102
+ ### Gratuit ou pré-audit complet
103
+
104
+ | Contrôle | fec-lint (gratuit) | [FEC Checker sur Apify](https://apify.com/highfiddle/fec-checker) |
105
+ |---|---|---|
106
+ | Nom `SIRENFECAAAAMMJJ.txt`, clé du SIREN | ✅ | ✅ |
107
+ | Encodage, séparateur, 18 colonnes | ✅ | ✅ |
108
+ | Formats : dates, montants, champs obligatoires, couples de champs | ✅ | ✅ |
109
+ | Équilibre par écriture et du fichier | — | ✅ |
110
+ | Ruptures et doublons d'EcritureNum, chronologie de ValidDate | — | ✅ |
111
+ | Signaux de risque : week-ends, jours fériés, montants ronds, Benford, caisse 53x, attente 471, après clôture | — | ✅ |
112
+ | Verdict, score 0-100, plan de correction rédigé par IA (FR/EN) | — | ✅ |
113
+ | Exécution | en local, rien ne quitte votre machine | sur Apify, paiement par fichier |
114
+
115
+ ---
116
+
117
+ ## Development
118
+
119
+ ```bash
120
+ python -m venv .venv
121
+ .venv/bin/pip install -e ".[test]" # Windows: .venv\Scripts\pip
122
+ pytest
123
+ ```
124
+
125
+ The fixtures in `tests/fixtures/` are synthetic FEC files (fictitious SIREN 123456782). MIT license.
@@ -0,0 +1,43 @@
1
+ [build-system]
2
+ requires = ["hatchling"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "fec-lint"
7
+ version = "0.1.0"
8
+ description = "Free structural linter for French FEC files (Fichier des Écritures Comptables), CLI + MCP server"
9
+ readme = "README.md"
10
+ license = "MIT"
11
+ license-files = ["LICENSE"]
12
+ authors = [{ name = "Vincent" }]
13
+ requires-python = ">=3.10"
14
+ dependencies = ["mcp>=2,<3"]
15
+ keywords = ["fec", "comptabilite", "dgfip", "accounting", "france", "mcp"]
16
+ classifiers = [
17
+ "Programming Language :: Python :: 3",
18
+ "Operating System :: OS Independent",
19
+ "Topic :: Office/Business :: Financial :: Accounting",
20
+ "Natural Language :: French",
21
+ "Natural Language :: English",
22
+ ]
23
+
24
+ [project.optional-dependencies]
25
+ test = ["pytest>=8"]
26
+
27
+ [project.scripts]
28
+ fec-lint = "fec_lint.cli:main"
29
+ fec-lint-mcp = "fec_lint.mcp_server:main"
30
+
31
+ [project.urls]
32
+ Homepage = "https://github.com/vincenz-cdf/fec-lint"
33
+ "Full pre-audit" = "https://apify.com/highfiddle/fec-checker"
34
+ Issues = "https://github.com/vincenz-cdf/fec-lint/issues"
35
+
36
+ [tool.hatch.build.targets.wheel]
37
+ packages = ["src/fec_lint"]
38
+
39
+ [tool.hatch.build.targets.sdist]
40
+ include = ["src/fec_lint", "tests", "README.md", "LICENSE"]
41
+
42
+ [tool.pytest.ini_options]
43
+ testpaths = ["tests"]
@@ -0,0 +1,8 @@
1
+ """Free structural linter for French FEC files (Fichier des Écritures Comptables)."""
2
+
3
+ from fec_lint.core import lint_file
4
+
5
+ __all__ = ['lint_file']
6
+ __version__ = '0.1.0'
7
+
8
+ FULL_PRE_AUDIT_URL = 'https://apify.com/highfiddle/fec-checker'
@@ -0,0 +1,63 @@
1
+ """fec-lint command line: lint one or more FEC files, print a summary or JSON."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import argparse
6
+ import json
7
+ import sys
8
+
9
+ from fec_lint import FULL_PRE_AUDIT_URL, __version__
10
+ from fec_lint.core import lint_file
11
+
12
+
13
+ def _print_result(result: dict) -> None:
14
+ status = 'OK' if result['ok'] else 'FAILED'
15
+ stats = result['stats']
16
+ details = ', '.join(f'{k} {v}' for k, v in (('encoding', stats['encoding']), ('separator', stats['separator']),
17
+ ('lines', stats['lineCount'])) if v)
18
+ print(f'{result["file"]}: {status}' + (f' ({details})' if details else ''))
19
+ for label, groups in (('error', result['errors']), ('warning', result['warnings'])):
20
+ for group in groups:
21
+ first = group['occurrences'][0] if group['occurrences'] else {}
22
+ lines = [str(o['line']) for o in group['occurrences'] if o.get('line')]
23
+ where = f' - line(s) {", ".join(lines[:10])}{"..." if group["truncated"] or len(lines) > 10 else ""}' if lines else ''
24
+ print(f' {label} {group["rule"]} x{group["count"]}: {first.get("message", "")}{where}')
25
+
26
+
27
+ def main(argv: list[str] | None = None) -> int:
28
+ parser = argparse.ArgumentParser(
29
+ prog='fec-lint',
30
+ description='Structural check of French FEC files: name, encoding, separator, 18 headers, field formats.',
31
+ )
32
+ parser.add_argument('files', nargs='+', help='FEC files (.txt, or a .zip holding one)')
33
+ parser.add_argument('--json', action='store_true', help='print the full result as JSON')
34
+ parser.add_argument('--name', help='file name to check against SIRENFECAAAAMMJJ.txt (one file only)')
35
+ parser.add_argument('--quiet', action='store_true', help='hide the closing line about the full pre-audit')
36
+ parser.add_argument('--version', action='version', version=f'fec-lint {__version__}')
37
+ args = parser.parse_args(argv)
38
+ if args.name and len(args.files) > 1:
39
+ parser.error('--name works with a single file')
40
+
41
+ results = []
42
+ for path in args.files:
43
+ try:
44
+ results.append(lint_file(path, args.name))
45
+ except OSError as e:
46
+ print(f'{path}: cannot read ({e.strerror or e})', file=sys.stderr)
47
+ return 2
48
+
49
+ if args.json:
50
+ print(json.dumps(results[0] if len(results) == 1 else results, ensure_ascii=False, indent=2))
51
+ else:
52
+ for result in results:
53
+ _print_result(result)
54
+
55
+ all_ok = all(r['ok'] for r in results)
56
+ if not args.quiet and not args.json:
57
+ verdict = 'Structure OK.' if all_ok else 'Structure has errors.'
58
+ print(f'{verdict} Full pre-audit (balance, numbering, risk signals): {FULL_PRE_AUDIT_URL}')
59
+ return 0 if all_ok else 1
60
+
61
+
62
+ if __name__ == '__main__':
63
+ sys.exit(main())