napkinstack 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.
- napkinstack-0.1.0/LICENSE +21 -0
- napkinstack-0.1.0/PKG-INFO +139 -0
- napkinstack-0.1.0/README.md +124 -0
- napkinstack-0.1.0/pyproject.toml +31 -0
- napkinstack-0.1.0/pyproject.toml.orig +27 -0
- napkinstack-0.1.0/src/napkinstack/__init__.py +5 -0
- napkinstack-0.1.0/src/napkinstack/cli.py +103 -0
- napkinstack-0.1.0/src/napkinstack/doctor.py +278 -0
- napkinstack-0.1.0/src/napkinstack/fitness/__init__.py +0 -0
- napkinstack-0.1.0/src/napkinstack/fitness/boundaries.py +222 -0
- napkinstack-0.1.0/src/napkinstack/fitness/manifests.py +198 -0
- napkinstack-0.1.0/src/napkinstack/fitness/pr_scope.sh +79 -0
- napkinstack-0.1.0/src/napkinstack/modules.py +132 -0
- napkinstack-0.1.0/src/napkinstack/project.py +170 -0
- napkinstack-0.1.0/src/napkinstack/skills.py +159 -0
- napkinstack-0.1.0/src/napkinstack/templates/module/AGENTS.md +36 -0
- napkinstack-0.1.0/src/napkinstack/templates/module/MANIFEST.yaml +77 -0
- napkinstack-0.1.0/src/napkinstack/templates/module/README.md +27 -0
- napkinstack-0.1.0/src/napkinstack/templates/module/docs/adr/.gitkeep +0 -0
- napkinstack-0.1.0/src/napkinstack/templates/module/src/.gitkeep +0 -0
- napkinstack-0.1.0/src/napkinstack/templates/module/tests/.gitkeep +0 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 NapkinStack
|
|
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,139 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: napkinstack
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Framework de travail pour faire travailler plusieurs équipes et leurs agents sur un même dépôt : modules, contrats, garde-fous en CI.
|
|
5
|
+
License-Expression: MIT
|
|
6
|
+
License-File: LICENSE
|
|
7
|
+
Requires-Dist: pyyaml>=6.0
|
|
8
|
+
Requires-Dist: copier==9.18.2
|
|
9
|
+
Requires-Dist: pre-commit==4.6.2
|
|
10
|
+
Requires-Python: >=3.12
|
|
11
|
+
Project-URL: Homepage, https://github.com/NapkinStack/engineering-os
|
|
12
|
+
Project-URL: Source, https://github.com/NapkinStack/engineering-os
|
|
13
|
+
Project-URL: Issues, https://github.com/NapkinStack/engineering-os/issues
|
|
14
|
+
Description-Content-Type: text/markdown
|
|
15
|
+
|
|
16
|
+
# NapkinStack
|
|
17
|
+
|
|
18
|
+
Framework de travail pour faire travailler plusieurs équipes et leurs agents sur un même
|
|
19
|
+
dépôt : modules, contrats, garde-fous en CI. Sur le modèle de Django ou Rails, une commande
|
|
20
|
+
crée le projet, qui reçoit ensuite les nouvelles versions à sa demande ; aucune stack
|
|
21
|
+
applicative n'est imposée. Positionnement et vocabulaire : [`PRODUCT.md`](PRODUCT.md) §1.
|
|
22
|
+
|
|
23
|
+
> **État : en construction (v0.1.0).** Toutes les commandes fonctionnent depuis ce dépôt ;
|
|
24
|
+
> la première version publiée sur PyPI arrive au chantier M5, avant un premier projet
|
|
25
|
+
> pilote. Suivi : [feuille de route](docs/governance/plans/2026-09-15-moteur-v0.1.0.md),
|
|
26
|
+
> [`docs/governance/chantiers.md`](docs/governance/chantiers.md).
|
|
27
|
+
|
|
28
|
+
## Le parcours d'un projet
|
|
29
|
+
|
|
30
|
+
```mermaid
|
|
31
|
+
flowchart LR
|
|
32
|
+
I["Installer<br/>uv tool install"]:::cmd --> N["nstack init"]:::cmd
|
|
33
|
+
N --> G["Publier sur GitHub<br/>appliquer la checklist"]:::humain
|
|
34
|
+
G --> D["nstack doctor<br/>lecture seule"]:::cmd
|
|
35
|
+
D --> M["nstack new-module"]:::cmd
|
|
36
|
+
M --> W["Travail en PR<br/>l'équipe et son agent"]:::humain
|
|
37
|
+
W --> U["nstack update<br/>branche fusionnée"]:::cmd
|
|
38
|
+
U --> P["PR relue<br/>validée par la CI"]:::humain
|
|
39
|
+
P -->|"version suivante"| U
|
|
40
|
+
|
|
41
|
+
classDef cmd fill:#1f2937,color:#fff
|
|
42
|
+
classDef humain fill:#065f46,color:#fff
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
**Légende** — gris : commande NapkinStack · vert : action de l'équipe. Décision :
|
|
46
|
+
[PDR-0001](docs/pdr/0001-creer-un-projet-et-recevoir-les-evolutions.md).
|
|
47
|
+
|
|
48
|
+
Le projet possède son squelette et l'adapte librement. Chaque nouvelle version lui arrive
|
|
49
|
+
à sa demande, fusionnée avec ses adaptations ; les conflits restent à l'équipe.
|
|
50
|
+
|
|
51
|
+
```mermaid
|
|
52
|
+
flowchart LR
|
|
53
|
+
V1["Squelette v0.1<br/>base commune"]:::ref --> F{"Fusion<br/>à 3 voies"}
|
|
54
|
+
V2["Squelette v0.2<br/>correctifs NapkinStack"]:::ns --> F
|
|
55
|
+
PR["Projet<br/>adaptations de l'équipe"]:::equipe --> F
|
|
56
|
+
F -->|"lignes différentes"| B["Branche de mise à jour<br/>correctifs + adaptations"]:::ok
|
|
57
|
+
F -->|"même ligne modifiée"| X["Conflit marqué<br/>commit refusé"]:::ko
|
|
58
|
+
|
|
59
|
+
classDef ref fill:#374151,color:#fff
|
|
60
|
+
classDef ns fill:#1e3a8a,color:#fff
|
|
61
|
+
classDef equipe fill:#065f46,color:#fff
|
|
62
|
+
classDef ok fill:#065f46,color:#fff
|
|
63
|
+
classDef ko fill:#7c2d12,color:#fff
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
**Légende** — gris : version dont le projet est issu · bleu : nouvelle version · vert :
|
|
67
|
+
travail de l'équipe et résultat accepté · rouge : conflit laissé à l'équipe.
|
|
68
|
+
|
|
69
|
+
## Installer
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
uv tool install napkinstack --with-executables-from pre-commit # prérequis : uv et git
|
|
73
|
+
nstack init mon-projet
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
Disponible dès la publication de la v0.1.0 sur PyPI ; chaque projet épingle ensuite sa
|
|
77
|
+
version et la change par `nstack update`.
|
|
78
|
+
|
|
79
|
+
## Les commandes
|
|
80
|
+
|
|
81
|
+
| Commande | Rôle |
|
|
82
|
+
|---|---|
|
|
83
|
+
| `nstack init <dossier>` | Crée le projet : squelette, dépôt git, commit initial, checklist GitHub |
|
|
84
|
+
| `nstack doctor` | Vérifie le poste et les réglages GitHub, en lecture seule |
|
|
85
|
+
| `nstack new-module <nom> <organisation>/<équipe> <criticité>` | Crée un module, sans stack imposée |
|
|
86
|
+
| `nstack check`, `test`, `bootstrap` `[module]` ; `nstack run <module>` | Exécutent les commandes déclarées dans le manifest du module |
|
|
87
|
+
| `nstack fitness` | Manifests, frontières entre modules, skills |
|
|
88
|
+
| `nstack pr-scope` | Une PR = un module, budget de revue |
|
|
89
|
+
| `nstack skills` | Expose les playbooks en skills pour l'agent |
|
|
90
|
+
| `nstack update` | Pose la nouvelle version sur une branche à relire |
|
|
91
|
+
|
|
92
|
+
**Prérequis** : uv et git. Les garde-fous bloquent vraiment sur un dépôt GitHub public, ou
|
|
93
|
+
privé sous l'offre Team ou Pro ; sur un dépôt privé de l'offre Free, la CI informe sans
|
|
94
|
+
bloquer ([précision de PDR-0001](docs/pdr/0001-creer-un-projet-et-recevoir-les-evolutions.md)).
|
|
95
|
+
|
|
96
|
+
**IA** : NapkinStack n'en embarque aucune. L'agent de l'équipe (Claude Code, Codex,
|
|
97
|
+
Copilot…) lit le kernel et les playbooks, lance les commandes, et la CI accepte ou refuse
|
|
98
|
+
ses propositions comme celles de n'importe quel contributeur.
|
|
99
|
+
|
|
100
|
+
## Ce dépôt
|
|
101
|
+
|
|
102
|
+
```mermaid
|
|
103
|
+
flowchart LR
|
|
104
|
+
S["skeleton/<br/>squelette de projet"]:::livre -->|"copier.yml"| P["Projet d'une équipe"]:::projet
|
|
105
|
+
E["src/napkinstack/<br/>moteur nstack"]:::livre -.->|"version épinglée"| P
|
|
106
|
+
A["PRODUCT.md · docs/governance/<br/>platform/ · CI du dépôt"]:::interne
|
|
107
|
+
|
|
108
|
+
classDef livre fill:#1e3a8a,color:#fff
|
|
109
|
+
classDef projet fill:#065f46,color:#fff
|
|
110
|
+
classDef interne fill:#374151,color:#fff
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
**Légende** — bleu : livré aux projets · vert : projet généré, qui possède son squelette ·
|
|
114
|
+
gris : développement de NapkinStack, jamais copié (PDR-0001 R6). Trait plein : génération ;
|
|
115
|
+
pointillé : dépendance versionnée.
|
|
116
|
+
|
|
117
|
+
| Chemin | Rôle |
|
|
118
|
+
|---|---|
|
|
119
|
+
| `skeleton/` | Ce que reçoit chaque projet : kernel, playbooks, manuel, CI, hooks |
|
|
120
|
+
| `copier.yml` | Questions posées à la création (gabarit Copier, ADR-0001) |
|
|
121
|
+
| `src/napkinstack/` | Le moteur, commande `nstack` |
|
|
122
|
+
| `platform/` | Enveloppe du module moteur : manifest, runbook, tests |
|
|
123
|
+
| `PRODUCT.md`, `docs/governance/` | Contexte de travail sur NapkinStack |
|
|
124
|
+
| `docs/adr/`, `docs/pdr/` | Décisions de NapkinStack |
|
|
125
|
+
|
|
126
|
+
## Développer NapkinStack
|
|
127
|
+
|
|
128
|
+
```bash
|
|
129
|
+
uv sync # prérequis : uv
|
|
130
|
+
uv run pre-commit install
|
|
131
|
+
uv run nstack fitness # garde-fous du dépôt
|
|
132
|
+
uv run bash platform/tests/run.sh # oracle : chaque garde-fou prouve qu'il sait échouer
|
|
133
|
+
uv run nstack init /tmp/essai --source . --ref HEAD # projet d'essai depuis l'arbre de travail
|
|
134
|
+
uv run nstack doctor --root /tmp/essai # poste et réglages GitHub, en lecture seule
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
Contribuer : [`CONTRIBUTING.md`](CONTRIBUTING.md), après [`PRODUCT.md`](PRODUCT.md).
|
|
138
|
+
|
|
139
|
+
Licence : [MIT](LICENSE).
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
# NapkinStack
|
|
2
|
+
|
|
3
|
+
Framework de travail pour faire travailler plusieurs équipes et leurs agents sur un même
|
|
4
|
+
dépôt : modules, contrats, garde-fous en CI. Sur le modèle de Django ou Rails, une commande
|
|
5
|
+
crée le projet, qui reçoit ensuite les nouvelles versions à sa demande ; aucune stack
|
|
6
|
+
applicative n'est imposée. Positionnement et vocabulaire : [`PRODUCT.md`](PRODUCT.md) §1.
|
|
7
|
+
|
|
8
|
+
> **État : en construction (v0.1.0).** Toutes les commandes fonctionnent depuis ce dépôt ;
|
|
9
|
+
> la première version publiée sur PyPI arrive au chantier M5, avant un premier projet
|
|
10
|
+
> pilote. Suivi : [feuille de route](docs/governance/plans/2026-09-15-moteur-v0.1.0.md),
|
|
11
|
+
> [`docs/governance/chantiers.md`](docs/governance/chantiers.md).
|
|
12
|
+
|
|
13
|
+
## Le parcours d'un projet
|
|
14
|
+
|
|
15
|
+
```mermaid
|
|
16
|
+
flowchart LR
|
|
17
|
+
I["Installer<br/>uv tool install"]:::cmd --> N["nstack init"]:::cmd
|
|
18
|
+
N --> G["Publier sur GitHub<br/>appliquer la checklist"]:::humain
|
|
19
|
+
G --> D["nstack doctor<br/>lecture seule"]:::cmd
|
|
20
|
+
D --> M["nstack new-module"]:::cmd
|
|
21
|
+
M --> W["Travail en PR<br/>l'équipe et son agent"]:::humain
|
|
22
|
+
W --> U["nstack update<br/>branche fusionnée"]:::cmd
|
|
23
|
+
U --> P["PR relue<br/>validée par la CI"]:::humain
|
|
24
|
+
P -->|"version suivante"| U
|
|
25
|
+
|
|
26
|
+
classDef cmd fill:#1f2937,color:#fff
|
|
27
|
+
classDef humain fill:#065f46,color:#fff
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
**Légende** — gris : commande NapkinStack · vert : action de l'équipe. Décision :
|
|
31
|
+
[PDR-0001](docs/pdr/0001-creer-un-projet-et-recevoir-les-evolutions.md).
|
|
32
|
+
|
|
33
|
+
Le projet possède son squelette et l'adapte librement. Chaque nouvelle version lui arrive
|
|
34
|
+
à sa demande, fusionnée avec ses adaptations ; les conflits restent à l'équipe.
|
|
35
|
+
|
|
36
|
+
```mermaid
|
|
37
|
+
flowchart LR
|
|
38
|
+
V1["Squelette v0.1<br/>base commune"]:::ref --> F{"Fusion<br/>à 3 voies"}
|
|
39
|
+
V2["Squelette v0.2<br/>correctifs NapkinStack"]:::ns --> F
|
|
40
|
+
PR["Projet<br/>adaptations de l'équipe"]:::equipe --> F
|
|
41
|
+
F -->|"lignes différentes"| B["Branche de mise à jour<br/>correctifs + adaptations"]:::ok
|
|
42
|
+
F -->|"même ligne modifiée"| X["Conflit marqué<br/>commit refusé"]:::ko
|
|
43
|
+
|
|
44
|
+
classDef ref fill:#374151,color:#fff
|
|
45
|
+
classDef ns fill:#1e3a8a,color:#fff
|
|
46
|
+
classDef equipe fill:#065f46,color:#fff
|
|
47
|
+
classDef ok fill:#065f46,color:#fff
|
|
48
|
+
classDef ko fill:#7c2d12,color:#fff
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
**Légende** — gris : version dont le projet est issu · bleu : nouvelle version · vert :
|
|
52
|
+
travail de l'équipe et résultat accepté · rouge : conflit laissé à l'équipe.
|
|
53
|
+
|
|
54
|
+
## Installer
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
uv tool install napkinstack --with-executables-from pre-commit # prérequis : uv et git
|
|
58
|
+
nstack init mon-projet
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Disponible dès la publication de la v0.1.0 sur PyPI ; chaque projet épingle ensuite sa
|
|
62
|
+
version et la change par `nstack update`.
|
|
63
|
+
|
|
64
|
+
## Les commandes
|
|
65
|
+
|
|
66
|
+
| Commande | Rôle |
|
|
67
|
+
|---|---|
|
|
68
|
+
| `nstack init <dossier>` | Crée le projet : squelette, dépôt git, commit initial, checklist GitHub |
|
|
69
|
+
| `nstack doctor` | Vérifie le poste et les réglages GitHub, en lecture seule |
|
|
70
|
+
| `nstack new-module <nom> <organisation>/<équipe> <criticité>` | Crée un module, sans stack imposée |
|
|
71
|
+
| `nstack check`, `test`, `bootstrap` `[module]` ; `nstack run <module>` | Exécutent les commandes déclarées dans le manifest du module |
|
|
72
|
+
| `nstack fitness` | Manifests, frontières entre modules, skills |
|
|
73
|
+
| `nstack pr-scope` | Une PR = un module, budget de revue |
|
|
74
|
+
| `nstack skills` | Expose les playbooks en skills pour l'agent |
|
|
75
|
+
| `nstack update` | Pose la nouvelle version sur une branche à relire |
|
|
76
|
+
|
|
77
|
+
**Prérequis** : uv et git. Les garde-fous bloquent vraiment sur un dépôt GitHub public, ou
|
|
78
|
+
privé sous l'offre Team ou Pro ; sur un dépôt privé de l'offre Free, la CI informe sans
|
|
79
|
+
bloquer ([précision de PDR-0001](docs/pdr/0001-creer-un-projet-et-recevoir-les-evolutions.md)).
|
|
80
|
+
|
|
81
|
+
**IA** : NapkinStack n'en embarque aucune. L'agent de l'équipe (Claude Code, Codex,
|
|
82
|
+
Copilot…) lit le kernel et les playbooks, lance les commandes, et la CI accepte ou refuse
|
|
83
|
+
ses propositions comme celles de n'importe quel contributeur.
|
|
84
|
+
|
|
85
|
+
## Ce dépôt
|
|
86
|
+
|
|
87
|
+
```mermaid
|
|
88
|
+
flowchart LR
|
|
89
|
+
S["skeleton/<br/>squelette de projet"]:::livre -->|"copier.yml"| P["Projet d'une équipe"]:::projet
|
|
90
|
+
E["src/napkinstack/<br/>moteur nstack"]:::livre -.->|"version épinglée"| P
|
|
91
|
+
A["PRODUCT.md · docs/governance/<br/>platform/ · CI du dépôt"]:::interne
|
|
92
|
+
|
|
93
|
+
classDef livre fill:#1e3a8a,color:#fff
|
|
94
|
+
classDef projet fill:#065f46,color:#fff
|
|
95
|
+
classDef interne fill:#374151,color:#fff
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
**Légende** — bleu : livré aux projets · vert : projet généré, qui possède son squelette ·
|
|
99
|
+
gris : développement de NapkinStack, jamais copié (PDR-0001 R6). Trait plein : génération ;
|
|
100
|
+
pointillé : dépendance versionnée.
|
|
101
|
+
|
|
102
|
+
| Chemin | Rôle |
|
|
103
|
+
|---|---|
|
|
104
|
+
| `skeleton/` | Ce que reçoit chaque projet : kernel, playbooks, manuel, CI, hooks |
|
|
105
|
+
| `copier.yml` | Questions posées à la création (gabarit Copier, ADR-0001) |
|
|
106
|
+
| `src/napkinstack/` | Le moteur, commande `nstack` |
|
|
107
|
+
| `platform/` | Enveloppe du module moteur : manifest, runbook, tests |
|
|
108
|
+
| `PRODUCT.md`, `docs/governance/` | Contexte de travail sur NapkinStack |
|
|
109
|
+
| `docs/adr/`, `docs/pdr/` | Décisions de NapkinStack |
|
|
110
|
+
|
|
111
|
+
## Développer NapkinStack
|
|
112
|
+
|
|
113
|
+
```bash
|
|
114
|
+
uv sync # prérequis : uv
|
|
115
|
+
uv run pre-commit install
|
|
116
|
+
uv run nstack fitness # garde-fous du dépôt
|
|
117
|
+
uv run bash platform/tests/run.sh # oracle : chaque garde-fou prouve qu'il sait échouer
|
|
118
|
+
uv run nstack init /tmp/essai --source . --ref HEAD # projet d'essai depuis l'arbre de travail
|
|
119
|
+
uv run nstack doctor --root /tmp/essai # poste et réglages GitHub, en lecture seule
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
Contribuer : [`CONTRIBUTING.md`](CONTRIBUTING.md), après [`PRODUCT.md`](PRODUCT.md).
|
|
123
|
+
|
|
124
|
+
Licence : [MIT](LICENSE).
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "napkinstack"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "Framework de travail pour faire travailler plusieurs équipes et leurs agents sur un même dépôt : modules, contrats, garde-fous en CI."
|
|
5
|
+
requires-python = ">=3.12"
|
|
6
|
+
license = "MIT"
|
|
7
|
+
license-files = ["LICENSE"]
|
|
8
|
+
readme = "README.md"
|
|
9
|
+
dependencies = [
|
|
10
|
+
"PyYAML>=6.0",
|
|
11
|
+
"copier==9.18.2",
|
|
12
|
+
"pre-commit==4.6.2",
|
|
13
|
+
]
|
|
14
|
+
|
|
15
|
+
[project.urls]
|
|
16
|
+
Homepage = "https://github.com/NapkinStack/engineering-os"
|
|
17
|
+
Source = "https://github.com/NapkinStack/engineering-os"
|
|
18
|
+
Issues = "https://github.com/NapkinStack/engineering-os/issues"
|
|
19
|
+
|
|
20
|
+
[project.scripts]
|
|
21
|
+
nstack = "napkinstack.cli:main"
|
|
22
|
+
|
|
23
|
+
[dependency-groups]
|
|
24
|
+
dev = ["pytest==9.1.1"]
|
|
25
|
+
|
|
26
|
+
[tool.uv]
|
|
27
|
+
required-version = ">=0.12.14,<0.13"
|
|
28
|
+
|
|
29
|
+
[build-system]
|
|
30
|
+
requires = ["uv_build>=0.12.14,<0.13"]
|
|
31
|
+
build-backend = "uv_build"
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "napkinstack"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "Framework de travail pour faire travailler plusieurs équipes et leurs agents sur un même dépôt : modules, contrats, garde-fous en CI."
|
|
5
|
+
requires-python = ">=3.12"
|
|
6
|
+
license = "MIT"
|
|
7
|
+
license-files = ["LICENSE"]
|
|
8
|
+
readme = "README.md"
|
|
9
|
+
dependencies = ["PyYAML>=6.0", "copier==9.18.2", "pre-commit==4.6.2"]
|
|
10
|
+
|
|
11
|
+
[project.urls]
|
|
12
|
+
Homepage = "https://github.com/NapkinStack/engineering-os"
|
|
13
|
+
Source = "https://github.com/NapkinStack/engineering-os"
|
|
14
|
+
Issues = "https://github.com/NapkinStack/engineering-os/issues"
|
|
15
|
+
|
|
16
|
+
[project.scripts]
|
|
17
|
+
nstack = "napkinstack.cli:main"
|
|
18
|
+
|
|
19
|
+
[dependency-groups]
|
|
20
|
+
dev = ["pytest==9.1.1"]
|
|
21
|
+
|
|
22
|
+
[tool.uv]
|
|
23
|
+
required-version = ">=0.12.14,<0.13"
|
|
24
|
+
|
|
25
|
+
[build-system]
|
|
26
|
+
requires = ["uv_build>=0.12.14,<0.13"]
|
|
27
|
+
build-backend = "uv_build"
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
"""Point d'entrée unique `nstack` (chantier C1, PDR-0001)."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import argparse
|
|
6
|
+
import os
|
|
7
|
+
import subprocess
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
|
|
10
|
+
from napkinstack import __version__, doctor, modules, skills
|
|
11
|
+
from napkinstack.fitness import boundaries, manifests
|
|
12
|
+
|
|
13
|
+
PACKAGE = Path(__file__).resolve().parent
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def _root(value: str) -> Path:
|
|
17
|
+
root = Path(value).resolve()
|
|
18
|
+
if not root.is_dir():
|
|
19
|
+
raise argparse.ArgumentTypeError(f"racine introuvable : {value}")
|
|
20
|
+
return root
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def _script(relative: str, *args: str, root: Path) -> int:
|
|
24
|
+
env = {**os.environ, "NSTACK_ROOT": str(root)}
|
|
25
|
+
return subprocess.run(["bash", str(PACKAGE / relative), *args], cwd=root, env=env).returncode
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def _fitness(root: Path) -> int:
|
|
29
|
+
results = [manifests.run(root), boundaries.run(root), skills.run(root, check_only=True)]
|
|
30
|
+
return 1 if any(results) else 0
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def _add(sub, name: str, help_: str, func) -> argparse.ArgumentParser:
|
|
34
|
+
parser = sub.add_parser(name, help=help_)
|
|
35
|
+
parser.add_argument("--root", type=_root, default=Path.cwd(),
|
|
36
|
+
help="racine du projet (défaut : dossier courant)")
|
|
37
|
+
parser.set_defaults(func=func)
|
|
38
|
+
return parser
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def _init(args: argparse.Namespace) -> int:
|
|
42
|
+
from napkinstack import project # Copier ne se charge que pour init et update
|
|
43
|
+
|
|
44
|
+
answers = {"project_name": args.project_name, "github_repo": args.github_repo,
|
|
45
|
+
"owner_team": args.owner_team}
|
|
46
|
+
return project.init(args.destination, answers, args.source or project.SOURCE,
|
|
47
|
+
args.ref or project.default_ref())
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def _update(args: argparse.Namespace) -> int:
|
|
51
|
+
from napkinstack import project
|
|
52
|
+
|
|
53
|
+
return project.update(args.root, args.ref or project.default_ref())
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def build_parser() -> argparse.ArgumentParser:
|
|
57
|
+
parser = argparse.ArgumentParser(prog="nstack", description="Moteur NapkinStack.")
|
|
58
|
+
parser.add_argument("--version", action="version", version=f"nstack {__version__}")
|
|
59
|
+
sub = parser.add_subparsers(dest="command", required=True, metavar="commande")
|
|
60
|
+
_add(sub, "manifests", "manifests, cycles de vie, dépréciations (M1–M9)",
|
|
61
|
+
lambda a: manifests.run(a.root))
|
|
62
|
+
_add(sub, "boundaries", "graphe déclaré contre graphe réel (B1–B5)",
|
|
63
|
+
lambda a: boundaries.run(a.root))
|
|
64
|
+
sk = _add(sub, "skills", "génère ou vérifie les skills (S1–S4)",
|
|
65
|
+
lambda a: skills.run(a.root, check_only=a.check))
|
|
66
|
+
sk.add_argument("--check", action="store_true", help="vérifier sans écrire")
|
|
67
|
+
_add(sub, "fitness", "manifests + frontières + skills",
|
|
68
|
+
lambda a: _fitness(a.root))
|
|
69
|
+
_add(sub, "doctor", "diagnostique le poste et les réglages GitHub, en lecture seule (PDR-0001)",
|
|
70
|
+
lambda a: doctor.run(a.root))
|
|
71
|
+
nm = _add(sub, "new-module", "crée un module et ses garde-fous, sans stack imposée",
|
|
72
|
+
lambda a: modules.nouveau(a.root, a.name, a.owner, a.criticality))
|
|
73
|
+
nm.add_argument("name", help="nom du module, kebab-case")
|
|
74
|
+
nm.add_argument("owner", help="équipe GitHub, organisation/équipe")
|
|
75
|
+
nm.add_argument("criticality", choices=["prototype", "standard", "eleve", "critique"])
|
|
76
|
+
for nom_verbe, aide in (("bootstrap", "prépare un module, ou tous (commands.bootstrap)"),
|
|
77
|
+
("check", "format, lint, types d'un module, ou de tous (commands.check)"),
|
|
78
|
+
("test", "tests d'un module, ou de tous (commands.test)")):
|
|
79
|
+
vb = _add(sub, nom_verbe, aide, lambda a, v=nom_verbe: modules.verbe(a.root, v, a.module))
|
|
80
|
+
vb.add_argument("module", nargs="?", help="nom du module (défaut : tous)")
|
|
81
|
+
rn = _add(sub, "run", "démarre un module en local (commands.run)",
|
|
82
|
+
lambda a: modules.verbe(a.root, "run", a.module))
|
|
83
|
+
rn.add_argument("module")
|
|
84
|
+
ps = _add(sub, "pr-scope", "une PR = un module, budget de revue (P1–P2)",
|
|
85
|
+
lambda a: _script("fitness/pr_scope.sh", a.base, root=a.root))
|
|
86
|
+
ps.add_argument("--base", default="origin/main")
|
|
87
|
+
ini = sub.add_parser("init", help="crée un projet à partir du squelette (PDR-0001)")
|
|
88
|
+
ini.add_argument("destination", type=Path, help="dossier du projet, absent ou vide")
|
|
89
|
+
ini.add_argument("--project-name", help="nom du projet (demandé si absent)")
|
|
90
|
+
ini.add_argument("--github-repo", help="dépôt GitHub, organisation/nom (demandé si absent)")
|
|
91
|
+
ini.add_argument("--owner-team", help="équipe GitHub du socle, organisation/équipe (demandé si absent)")
|
|
92
|
+
ini.add_argument("--source", help="gabarit : URL ou chemin (défaut : dépôt NapkinStack)")
|
|
93
|
+
ini.add_argument("--ref", help="version du squelette, tag vX.Y.Z (défaut : celle de nstack)")
|
|
94
|
+
ini.set_defaults(func=_init)
|
|
95
|
+
up = _add(sub, "update", "fusionne une version de NapkinStack sur une branche à relire (PDR-0001)",
|
|
96
|
+
_update)
|
|
97
|
+
up.add_argument("--ref", help="version cible, tag vX.Y.Z (défaut : celle de nstack)")
|
|
98
|
+
return parser
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def main(argv: list[str] | None = None) -> int:
|
|
102
|
+
args = build_parser().parse_args(argv)
|
|
103
|
+
return args.func(args)
|