napkinstack 0.1.0__tar.gz → 0.2.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.2.0/PKG-INFO +142 -0
- napkinstack-0.2.0/README.md +127 -0
- {napkinstack-0.1.0 → napkinstack-0.2.0}/pyproject.toml +2 -2
- {napkinstack-0.1.0 → napkinstack-0.2.0}/pyproject.toml.orig +2 -2
- napkinstack-0.2.0/src/napkinstack/__init__.py +5 -0
- napkinstack-0.2.0/src/napkinstack/cli.py +104 -0
- napkinstack-0.2.0/src/napkinstack/doctor.py +280 -0
- {napkinstack-0.1.0 → napkinstack-0.2.0}/src/napkinstack/fitness/boundaries.py +46 -46
- napkinstack-0.2.0/src/napkinstack/fitness/manifests.py +198 -0
- napkinstack-0.2.0/src/napkinstack/fitness/pr_scope.sh +79 -0
- napkinstack-0.2.0/src/napkinstack/modules.py +133 -0
- napkinstack-0.2.0/src/napkinstack/project.py +170 -0
- napkinstack-0.2.0/src/napkinstack/skills.py +159 -0
- napkinstack-0.2.0/src/napkinstack/templates/module/AGENTS.md +36 -0
- napkinstack-0.2.0/src/napkinstack/templates/module/MANIFEST.yaml +77 -0
- napkinstack-0.2.0/src/napkinstack/templates/module/README.md +27 -0
- napkinstack-0.1.0/PKG-INFO +0 -139
- napkinstack-0.1.0/README.md +0 -124
- napkinstack-0.1.0/src/napkinstack/__init__.py +0 -5
- napkinstack-0.1.0/src/napkinstack/cli.py +0 -103
- napkinstack-0.1.0/src/napkinstack/doctor.py +0 -278
- napkinstack-0.1.0/src/napkinstack/fitness/manifests.py +0 -198
- napkinstack-0.1.0/src/napkinstack/fitness/pr_scope.sh +0 -79
- napkinstack-0.1.0/src/napkinstack/modules.py +0 -132
- napkinstack-0.1.0/src/napkinstack/project.py +0 -170
- napkinstack-0.1.0/src/napkinstack/skills.py +0 -159
- napkinstack-0.1.0/src/napkinstack/templates/module/AGENTS.md +0 -36
- napkinstack-0.1.0/src/napkinstack/templates/module/MANIFEST.yaml +0 -77
- napkinstack-0.1.0/src/napkinstack/templates/module/README.md +0 -27
- {napkinstack-0.1.0 → napkinstack-0.2.0}/LICENSE +0 -0
- {napkinstack-0.1.0 → napkinstack-0.2.0}/src/napkinstack/fitness/__init__.py +0 -0
- {napkinstack-0.1.0 → napkinstack-0.2.0}/src/napkinstack/templates/module/docs/adr/.gitkeep +0 -0
- {napkinstack-0.1.0 → napkinstack-0.2.0}/src/napkinstack/templates/module/src/.gitkeep +0 -0
- {napkinstack-0.1.0 → napkinstack-0.2.0}/src/napkinstack/templates/module/tests/.gitkeep +0 -0
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: napkinstack
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: Engineering framework for several teams and their agents working in one repository: modules, contracts, guardrails in 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
|
+
An engineering framework for several teams and their agents working in one repository:
|
|
19
|
+
modules, contracts, guardrails in CI. On the Django or Rails model, one command creates
|
|
20
|
+
the project, which then receives new versions on demand; no application stack is imposed.
|
|
21
|
+
Positioning and vocabulary: [`PRODUCT.md`](PRODUCT.md) §1.
|
|
22
|
+
|
|
23
|
+
> **Status: v0.1.0, the first published version** ([PyPI](https://pypi.org/project/napkinstack/)).
|
|
24
|
+
> A first pilot project, private, puts it to the test before the rest. Tracking:
|
|
25
|
+
> [roadmap](docs/governance/plans/2026-09-15-engine-v0.1.0.md),
|
|
26
|
+
> [`docs/governance/workstreams.md`](docs/governance/workstreams.md).
|
|
27
|
+
|
|
28
|
+
## A project's journey
|
|
29
|
+
|
|
30
|
+
```mermaid
|
|
31
|
+
flowchart LR
|
|
32
|
+
I["Install<br/>uv tool install"]:::cmd --> N["nstack init"]:::cmd
|
|
33
|
+
N --> G["Publish on GitHub<br/>apply the checklist"]:::human
|
|
34
|
+
G --> D["nstack doctor<br/>read-only"]:::cmd
|
|
35
|
+
D --> M["nstack new-module"]:::cmd
|
|
36
|
+
M --> W["Work in pull requests<br/>the team and its agent"]:::human
|
|
37
|
+
W --> U["nstack update<br/>merged branch"]:::cmd
|
|
38
|
+
U --> P["PR reviewed<br/>validated by CI"]:::human
|
|
39
|
+
P -->|"next version"| U
|
|
40
|
+
|
|
41
|
+
classDef cmd fill:#1f2937,color:#fff
|
|
42
|
+
classDef human fill:#065f46,color:#fff
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
**Legend** — grey: a NapkinStack command · green: the team's action. Decision:
|
|
46
|
+
[PDR-0001](docs/pdr/0001-create-a-project-and-receive-updates.md).
|
|
47
|
+
|
|
48
|
+
The project owns its skeleton and adapts it freely. Every new version reaches it on
|
|
49
|
+
demand, merged with its adaptations; the conflicts are left to the team.
|
|
50
|
+
|
|
51
|
+
```mermaid
|
|
52
|
+
flowchart LR
|
|
53
|
+
V1["Skeleton v0.1<br/>common base"]:::ref --> F{"Three-way<br/>merge"}
|
|
54
|
+
V2["Skeleton v0.2<br/>NapkinStack fixes"]:::ns --> F
|
|
55
|
+
PR["Project<br/>the team's adaptations"]:::team --> F
|
|
56
|
+
F -->|"different lines"| B["Update branch<br/>fixes + adaptations"]:::ok
|
|
57
|
+
F -->|"same line changed"| X["Conflict marked<br/>commit refused"]:::ko
|
|
58
|
+
|
|
59
|
+
classDef ref fill:#374151,color:#fff
|
|
60
|
+
classDef ns fill:#1e3a8a,color:#fff
|
|
61
|
+
classDef team fill:#065f46,color:#fff
|
|
62
|
+
classDef ok fill:#065f46,color:#fff
|
|
63
|
+
classDef ko fill:#7c2d12,color:#fff
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
**Legend** — grey: the version the project came from · blue: the new version · green: the
|
|
67
|
+
team's work and the accepted result · red: a conflict left to the team.
|
|
68
|
+
|
|
69
|
+
## Install
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
uv tool install napkinstack --with-executables-from pre-commit # prerequisites: uv and git
|
|
73
|
+
nstack init my-project
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
Each project then pins its version and changes it through `nstack update`. Every published
|
|
77
|
+
version carries a provenance attestation, visible on PyPI, tying it to the workflow and
|
|
78
|
+
the commit of this repository
|
|
79
|
+
([ADR-0002](docs/adr/0002-distribute-napkinstack-on-pypi.md)).
|
|
80
|
+
|
|
81
|
+
## The commands
|
|
82
|
+
|
|
83
|
+
| Command | Role |
|
|
84
|
+
|---|---|
|
|
85
|
+
| `nstack init <folder>` | Creates the project: skeleton, git repository, initial commit, GitHub checklist |
|
|
86
|
+
| `nstack doctor` | Checks the workstation and the GitHub settings, read-only |
|
|
87
|
+
| `nstack new-module <name> <organisation>/<team> <criticality>` | Creates a module, with no imposed stack |
|
|
88
|
+
| `nstack check`, `test`, `bootstrap` `[module]`; `nstack run <module>` | Run the commands declared in the module's manifest |
|
|
89
|
+
| `nstack fitness` | Manifests, boundaries between modules, skills |
|
|
90
|
+
| `nstack pr-scope` | One PR = one module, review budget |
|
|
91
|
+
| `nstack skills` | Exposes the playbooks as skills for the agent |
|
|
92
|
+
| `nstack update` | Lays the new version on a branch to review |
|
|
93
|
+
|
|
94
|
+
**Prerequisites**: uv and git. The guardrails really block on a public GitHub repository,
|
|
95
|
+
or on a private one under the Team or Pro plan; on a private repository on the Free plan
|
|
96
|
+
CI informs without blocking
|
|
97
|
+
([a clarification of PDR-0001](docs/pdr/0001-create-a-project-and-receive-updates.md)).
|
|
98
|
+
|
|
99
|
+
**AI**: NapkinStack embeds none. The team's agent (Claude Code, Codex, Copilot…) reads the
|
|
100
|
+
kernel and the playbooks, runs the commands, and CI accepts or refuses its proposals
|
|
101
|
+
exactly as it would any other contributor's.
|
|
102
|
+
|
|
103
|
+
## This repository
|
|
104
|
+
|
|
105
|
+
```mermaid
|
|
106
|
+
flowchart LR
|
|
107
|
+
S["skeleton/<br/>the project skeleton"]:::shipped -->|"copier.yml"| P["A team's project"]:::project
|
|
108
|
+
E["src/napkinstack/<br/>the nstack engine"]:::shipped -.->|"pinned version"| P
|
|
109
|
+
A["PRODUCT.md · docs/governance/<br/>platform/ · this repository's CI"]:::internal
|
|
110
|
+
|
|
111
|
+
classDef shipped fill:#1e3a8a,color:#fff
|
|
112
|
+
classDef project fill:#065f46,color:#fff
|
|
113
|
+
classDef internal fill:#374151,color:#fff
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
**Legend** — blue: shipped to projects · green: a generated project, which owns its
|
|
117
|
+
skeleton · grey: developing NapkinStack itself, never copied (PDR-0001 R6). Solid line:
|
|
118
|
+
generation; dotted: a versioned dependency.
|
|
119
|
+
|
|
120
|
+
| Path | Role |
|
|
121
|
+
|---|---|
|
|
122
|
+
| `skeleton/` | What every project receives: kernel, playbooks, handbook, CI, hooks |
|
|
123
|
+
| `copier.yml` | The questions asked at creation (a Copier template, ADR-0001) |
|
|
124
|
+
| `src/napkinstack/` | The engine, the `nstack` command |
|
|
125
|
+
| `platform/` | The engine module's envelope: manifest, runbook, tests |
|
|
126
|
+
| `PRODUCT.md`, `docs/governance/` | The working context for NapkinStack itself |
|
|
127
|
+
| `docs/adr/`, `docs/pdr/` | NapkinStack's decisions |
|
|
128
|
+
|
|
129
|
+
## Developing NapkinStack
|
|
130
|
+
|
|
131
|
+
```bash
|
|
132
|
+
uv sync # prerequisite: uv
|
|
133
|
+
uv run pre-commit install
|
|
134
|
+
uv run nstack fitness # this repository's guardrails
|
|
135
|
+
uv run bash platform/tests/run.sh # the oracle: every guardrail proves it can fail
|
|
136
|
+
uv run nstack init /tmp/trial --source . --ref HEAD # a trial project from the working tree
|
|
137
|
+
uv run nstack doctor --root /tmp/trial # workstation and GitHub settings, read-only
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
Contributing: [`CONTRIBUTING.md`](CONTRIBUTING.md), after [`PRODUCT.md`](PRODUCT.md).
|
|
141
|
+
|
|
142
|
+
Licence: [MIT](LICENSE).
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
# NapkinStack
|
|
2
|
+
|
|
3
|
+
An engineering framework for several teams and their agents working in one repository:
|
|
4
|
+
modules, contracts, guardrails in CI. On the Django or Rails model, one command creates
|
|
5
|
+
the project, which then receives new versions on demand; no application stack is imposed.
|
|
6
|
+
Positioning and vocabulary: [`PRODUCT.md`](PRODUCT.md) §1.
|
|
7
|
+
|
|
8
|
+
> **Status: v0.1.0, the first published version** ([PyPI](https://pypi.org/project/napkinstack/)).
|
|
9
|
+
> A first pilot project, private, puts it to the test before the rest. Tracking:
|
|
10
|
+
> [roadmap](docs/governance/plans/2026-09-15-engine-v0.1.0.md),
|
|
11
|
+
> [`docs/governance/workstreams.md`](docs/governance/workstreams.md).
|
|
12
|
+
|
|
13
|
+
## A project's journey
|
|
14
|
+
|
|
15
|
+
```mermaid
|
|
16
|
+
flowchart LR
|
|
17
|
+
I["Install<br/>uv tool install"]:::cmd --> N["nstack init"]:::cmd
|
|
18
|
+
N --> G["Publish on GitHub<br/>apply the checklist"]:::human
|
|
19
|
+
G --> D["nstack doctor<br/>read-only"]:::cmd
|
|
20
|
+
D --> M["nstack new-module"]:::cmd
|
|
21
|
+
M --> W["Work in pull requests<br/>the team and its agent"]:::human
|
|
22
|
+
W --> U["nstack update<br/>merged branch"]:::cmd
|
|
23
|
+
U --> P["PR reviewed<br/>validated by CI"]:::human
|
|
24
|
+
P -->|"next version"| U
|
|
25
|
+
|
|
26
|
+
classDef cmd fill:#1f2937,color:#fff
|
|
27
|
+
classDef human fill:#065f46,color:#fff
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
**Legend** — grey: a NapkinStack command · green: the team's action. Decision:
|
|
31
|
+
[PDR-0001](docs/pdr/0001-create-a-project-and-receive-updates.md).
|
|
32
|
+
|
|
33
|
+
The project owns its skeleton and adapts it freely. Every new version reaches it on
|
|
34
|
+
demand, merged with its adaptations; the conflicts are left to the team.
|
|
35
|
+
|
|
36
|
+
```mermaid
|
|
37
|
+
flowchart LR
|
|
38
|
+
V1["Skeleton v0.1<br/>common base"]:::ref --> F{"Three-way<br/>merge"}
|
|
39
|
+
V2["Skeleton v0.2<br/>NapkinStack fixes"]:::ns --> F
|
|
40
|
+
PR["Project<br/>the team's adaptations"]:::team --> F
|
|
41
|
+
F -->|"different lines"| B["Update branch<br/>fixes + adaptations"]:::ok
|
|
42
|
+
F -->|"same line changed"| X["Conflict marked<br/>commit refused"]:::ko
|
|
43
|
+
|
|
44
|
+
classDef ref fill:#374151,color:#fff
|
|
45
|
+
classDef ns fill:#1e3a8a,color:#fff
|
|
46
|
+
classDef team fill:#065f46,color:#fff
|
|
47
|
+
classDef ok fill:#065f46,color:#fff
|
|
48
|
+
classDef ko fill:#7c2d12,color:#fff
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
**Legend** — grey: the version the project came from · blue: the new version · green: the
|
|
52
|
+
team's work and the accepted result · red: a conflict left to the team.
|
|
53
|
+
|
|
54
|
+
## Install
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
uv tool install napkinstack --with-executables-from pre-commit # prerequisites: uv and git
|
|
58
|
+
nstack init my-project
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Each project then pins its version and changes it through `nstack update`. Every published
|
|
62
|
+
version carries a provenance attestation, visible on PyPI, tying it to the workflow and
|
|
63
|
+
the commit of this repository
|
|
64
|
+
([ADR-0002](docs/adr/0002-distribute-napkinstack-on-pypi.md)).
|
|
65
|
+
|
|
66
|
+
## The commands
|
|
67
|
+
|
|
68
|
+
| Command | Role |
|
|
69
|
+
|---|---|
|
|
70
|
+
| `nstack init <folder>` | Creates the project: skeleton, git repository, initial commit, GitHub checklist |
|
|
71
|
+
| `nstack doctor` | Checks the workstation and the GitHub settings, read-only |
|
|
72
|
+
| `nstack new-module <name> <organisation>/<team> <criticality>` | Creates a module, with no imposed stack |
|
|
73
|
+
| `nstack check`, `test`, `bootstrap` `[module]`; `nstack run <module>` | Run the commands declared in the module's manifest |
|
|
74
|
+
| `nstack fitness` | Manifests, boundaries between modules, skills |
|
|
75
|
+
| `nstack pr-scope` | One PR = one module, review budget |
|
|
76
|
+
| `nstack skills` | Exposes the playbooks as skills for the agent |
|
|
77
|
+
| `nstack update` | Lays the new version on a branch to review |
|
|
78
|
+
|
|
79
|
+
**Prerequisites**: uv and git. The guardrails really block on a public GitHub repository,
|
|
80
|
+
or on a private one under the Team or Pro plan; on a private repository on the Free plan
|
|
81
|
+
CI informs without blocking
|
|
82
|
+
([a clarification of PDR-0001](docs/pdr/0001-create-a-project-and-receive-updates.md)).
|
|
83
|
+
|
|
84
|
+
**AI**: NapkinStack embeds none. The team's agent (Claude Code, Codex, Copilot…) reads the
|
|
85
|
+
kernel and the playbooks, runs the commands, and CI accepts or refuses its proposals
|
|
86
|
+
exactly as it would any other contributor's.
|
|
87
|
+
|
|
88
|
+
## This repository
|
|
89
|
+
|
|
90
|
+
```mermaid
|
|
91
|
+
flowchart LR
|
|
92
|
+
S["skeleton/<br/>the project skeleton"]:::shipped -->|"copier.yml"| P["A team's project"]:::project
|
|
93
|
+
E["src/napkinstack/<br/>the nstack engine"]:::shipped -.->|"pinned version"| P
|
|
94
|
+
A["PRODUCT.md · docs/governance/<br/>platform/ · this repository's CI"]:::internal
|
|
95
|
+
|
|
96
|
+
classDef shipped fill:#1e3a8a,color:#fff
|
|
97
|
+
classDef project fill:#065f46,color:#fff
|
|
98
|
+
classDef internal fill:#374151,color:#fff
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
**Legend** — blue: shipped to projects · green: a generated project, which owns its
|
|
102
|
+
skeleton · grey: developing NapkinStack itself, never copied (PDR-0001 R6). Solid line:
|
|
103
|
+
generation; dotted: a versioned dependency.
|
|
104
|
+
|
|
105
|
+
| Path | Role |
|
|
106
|
+
|---|---|
|
|
107
|
+
| `skeleton/` | What every project receives: kernel, playbooks, handbook, CI, hooks |
|
|
108
|
+
| `copier.yml` | The questions asked at creation (a Copier template, ADR-0001) |
|
|
109
|
+
| `src/napkinstack/` | The engine, the `nstack` command |
|
|
110
|
+
| `platform/` | The engine module's envelope: manifest, runbook, tests |
|
|
111
|
+
| `PRODUCT.md`, `docs/governance/` | The working context for NapkinStack itself |
|
|
112
|
+
| `docs/adr/`, `docs/pdr/` | NapkinStack's decisions |
|
|
113
|
+
|
|
114
|
+
## Developing NapkinStack
|
|
115
|
+
|
|
116
|
+
```bash
|
|
117
|
+
uv sync # prerequisite: uv
|
|
118
|
+
uv run pre-commit install
|
|
119
|
+
uv run nstack fitness # this repository's guardrails
|
|
120
|
+
uv run bash platform/tests/run.sh # the oracle: every guardrail proves it can fail
|
|
121
|
+
uv run nstack init /tmp/trial --source . --ref HEAD # a trial project from the working tree
|
|
122
|
+
uv run nstack doctor --root /tmp/trial # workstation and GitHub settings, read-only
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
Contributing: [`CONTRIBUTING.md`](CONTRIBUTING.md), after [`PRODUCT.md`](PRODUCT.md).
|
|
126
|
+
|
|
127
|
+
Licence: [MIT](LICENSE).
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "napkinstack"
|
|
3
|
-
version = "0.
|
|
4
|
-
description = "
|
|
3
|
+
version = "0.2.0"
|
|
4
|
+
description = "Engineering framework for several teams and their agents working in one repository: modules, contracts, guardrails in CI."
|
|
5
5
|
requires-python = ">=3.12"
|
|
6
6
|
license = "MIT"
|
|
7
7
|
license-files = ["LICENSE"]
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "napkinstack"
|
|
3
|
-
version = "0.
|
|
4
|
-
description = "
|
|
3
|
+
version = "0.2.0"
|
|
4
|
+
description = "Engineering framework for several teams and their agents working in one repository: modules, contracts, guardrails in CI."
|
|
5
5
|
requires-python = ">=3.12"
|
|
6
6
|
license = "MIT"
|
|
7
7
|
license-files = ["LICENSE"]
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
"""Single entry point `nstack` (workstream 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"root not found: {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="project root (default: current folder)")
|
|
37
|
+
parser.set_defaults(func=func)
|
|
38
|
+
return parser
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def _init(args: argparse.Namespace) -> int:
|
|
42
|
+
from napkinstack import project # Copier is only loaded for init and 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="NapkinStack engine.")
|
|
58
|
+
parser.add_argument("--version", action="version", version=f"nstack {__version__}")
|
|
59
|
+
sub = parser.add_subparsers(dest="command", required=True, metavar="command")
|
|
60
|
+
_add(sub, "manifests", "manifests, lifecycles, deprecations (M1-M9)",
|
|
61
|
+
lambda a: manifests.run(a.root))
|
|
62
|
+
_add(sub, "boundaries", "declared graph against real graph (B1-B5)",
|
|
63
|
+
lambda a: boundaries.run(a.root))
|
|
64
|
+
sk = _add(sub, "skills", "generates or checks the skills (S1-S4)",
|
|
65
|
+
lambda a: skills.run(a.root, check_only=a.check))
|
|
66
|
+
sk.add_argument("--check", action="store_true", help="check without writing")
|
|
67
|
+
_add(sub, "fitness", "manifests + boundaries + skills",
|
|
68
|
+
lambda a: _fitness(a.root))
|
|
69
|
+
_add(sub, "doctor", "diagnoses the workstation and the GitHub settings, read-only (PDR-0001)",
|
|
70
|
+
lambda a: doctor.run(a.root))
|
|
71
|
+
nm = _add(sub, "new-module", "creates a module and its guardrails, with no imposed stack",
|
|
72
|
+
lambda a: modules.create(a.root, a.name, a.owner, a.criticality))
|
|
73
|
+
nm.add_argument("name", help="module name, kebab-case")
|
|
74
|
+
nm.add_argument("owner", help="GitHub team, organisation/team")
|
|
75
|
+
nm.add_argument("criticality", choices=["prototype", "standard", "high", "critical"])
|
|
76
|
+
for verb, help_text in (("bootstrap", "prepares one module, or all of them (commands.bootstrap)"),
|
|
77
|
+
("check", "format, lint, types of one module, or all (commands.check)"),
|
|
78
|
+
("test", "tests of one module, or of all of them (commands.test)")):
|
|
79
|
+
vb = _add(sub, verb, help_text, lambda a, v=verb: modules.run_verb(a.root, v, a.module))
|
|
80
|
+
vb.add_argument("module", nargs="?", help="module name (default: all)")
|
|
81
|
+
rn = _add(sub, "run", "starts a module locally (commands.run)",
|
|
82
|
+
lambda a: modules.run_verb(a.root, "run", a.module))
|
|
83
|
+
rn.add_argument("module")
|
|
84
|
+
ps = _add(sub, "pr-scope", "one PR = one module, review budget (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="creates a project from the skeleton (PDR-0001)")
|
|
88
|
+
ini.add_argument("destination", type=Path, help="project folder, missing or empty")
|
|
89
|
+
ini.add_argument("--project-name", help="project name (asked when absent)")
|
|
90
|
+
ini.add_argument("--github-repo", help="GitHub repository, organisation/name (asked when absent)")
|
|
91
|
+
ini.add_argument("--owner-team", help="GitHub team owning the foundation, organisation/team "
|
|
92
|
+
"(asked when absent)")
|
|
93
|
+
ini.add_argument("--source", help="template: URL or path (default: the NapkinStack repository)")
|
|
94
|
+
ini.add_argument("--ref", help="skeleton version, tag vX.Y.Z (default: the one of nstack)")
|
|
95
|
+
ini.set_defaults(func=_init)
|
|
96
|
+
up = _add(sub, "update", "merges a NapkinStack version onto a branch to review (PDR-0001)",
|
|
97
|
+
_update)
|
|
98
|
+
up.add_argument("--ref", help="target version, tag vX.Y.Z (default: the one of nstack)")
|
|
99
|
+
return parser
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def main(argv: list[str] | None = None) -> int:
|
|
103
|
+
args = build_parser().parse_args(argv)
|
|
104
|
+
return args.func(args)
|