modwire-cli 1.0.1__tar.gz → 1.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.
Files changed (79) hide show
  1. modwire_cli-1.2.0/.github/workflows/ci.yml +17 -0
  2. modwire_cli-1.2.0/.github/workflows/release.yml +40 -0
  3. {modwire_cli-1.0.1 → modwire_cli-1.2.0}/.gitignore +1 -0
  4. modwire_cli-1.2.0/.modwire/INDEX.md +10 -0
  5. modwire_cli-1.2.0/.modwire/architecture.yaml +38 -0
  6. modwire_cli-1.2.0/.modwire/docs/ARCHITECTURE.md +34 -0
  7. modwire_cli-1.2.0/.modwire/docs/MODWIRE.md +5 -0
  8. modwire_cli-1.2.0/.modwire/docs/PROJECT.md +16 -0
  9. modwire_cli-1.2.0/.modwire/docs/RULES.md +6 -0
  10. modwire_cli-1.2.0/.modwire/docs/TESTING.md +7 -0
  11. modwire_cli-1.2.0/.modwire/docs/WORKFLOW.md +8 -0
  12. modwire_cli-1.2.0/AGENTS.md +1 -0
  13. modwire_cli-1.2.0/Makefile +14 -0
  14. {modwire_cli-1.0.1 → modwire_cli-1.2.0}/PKG-INFO +49 -13
  15. modwire_cli-1.2.0/README.md +73 -0
  16. {modwire_cli-1.0.1 → modwire_cli-1.2.0}/pyproject.toml +15 -14
  17. modwire_cli-1.2.0/scripts/generate_docs.py +4 -0
  18. modwire_cli-1.2.0/setup.cfg +4 -0
  19. modwire_cli-1.2.0/src/modwire_cli/__main__.py +4 -0
  20. {modwire_cli-1.0.1 → modwire_cli-1.2.0}/src/modwire_cli/_version.py +3 -3
  21. modwire_cli-1.2.0/src/modwire_cli/cli.py +63 -0
  22. {modwire_cli-1.0.1 → modwire_cli-1.2.0}/src/modwire_cli/config.py +2 -0
  23. modwire_cli-1.2.0/src/modwire_cli/documentation/__init__.py +4 -0
  24. modwire_cli-1.2.0/src/modwire_cli/documentation/command.py +23 -0
  25. modwire_cli-1.2.0/src/modwire_cli/documentation/generator.py +47 -0
  26. {modwire_cli-1.0.1 → modwire_cli-1.2.0}/src/modwire_cli/extraction.py +2 -0
  27. {modwire_cli-1.0.1 → modwire_cli-1.2.0}/src/modwire_cli/facade.py +4 -1
  28. modwire_cli-1.2.0/src/modwire_cli/initialization/__init__.py +12 -0
  29. modwire_cli-1.2.0/src/modwire_cli/initialization/asset.py +26 -0
  30. modwire_cli-1.2.0/src/modwire_cli/initialization/facade.py +34 -0
  31. modwire_cli-1.2.0/src/modwire_cli/initialization/result.py +13 -0
  32. modwire_cli-1.2.0/src/modwire_cli/initialization/service.py +47 -0
  33. {modwire_cli-1.0.1 → modwire_cli-1.2.0}/src/modwire_cli/pipeline/context.py +3 -0
  34. {modwire_cli-1.0.1 → modwire_cli-1.2.0}/src/modwire_cli/pipeline/runner.py +4 -2
  35. {modwire_cli-1.0.1 → modwire_cli-1.2.0}/src/modwire_cli/pipeline/step.py +2 -0
  36. {modwire_cli-1.0.1 → modwire_cli-1.2.0}/src/modwire_cli/pipeline/steps/callables_insight.py +2 -0
  37. {modwire_cli-1.0.1 → modwire_cli-1.2.0}/src/modwire_cli/pipeline/steps/clusters_insight.py +2 -0
  38. {modwire_cli-1.0.1 → modwire_cli-1.2.0}/src/modwire_cli/pipeline/steps/coherence_insight.py +2 -0
  39. {modwire_cli-1.0.1 → modwire_cli-1.2.0}/src/modwire_cli/pipeline/steps/flow_violations.py +2 -0
  40. {modwire_cli-1.0.1 → modwire_cli-1.2.0}/src/modwire_cli/pipeline/steps/hotspots_insight.py +2 -0
  41. {modwire_cli-1.0.1 → modwire_cli-1.2.0}/src/modwire_cli/pipeline/steps/map_tree.py +20 -2
  42. {modwire_cli-1.0.1 → modwire_cli-1.2.0}/src/modwire_cli/pipeline/steps/shape_violations.py +2 -0
  43. {modwire_cli-1.0.1 → modwire_cli-1.2.0}/src/modwire_cli/pipeline/steps/success.py +2 -0
  44. {modwire_cli-1.0.1 → modwire_cli-1.2.0}/src/modwire_cli/pipeline/steps/unknown_files_warning.py +2 -0
  45. {modwire_cli-1.0.1 → modwire_cli-1.2.0}/src/modwire_cli/pipeline/steps/unused_exports_insight.py +2 -0
  46. modwire_cli-1.2.0/src/modwire_cli/py.typed +1 -0
  47. modwire_cli-1.2.0/src/modwire_cli/resources/init/AGENTS.md +1 -0
  48. modwire_cli-1.2.0/src/modwire_cli/resources/init/ARCHITECTURE.md +34 -0
  49. modwire_cli-1.2.0/src/modwire_cli/resources/init/INDEX.md +9 -0
  50. modwire_cli-1.2.0/src/modwire_cli/resources/init/MODWIRE.md +5 -0
  51. modwire_cli-1.2.0/src/modwire_cli/resources/init/RULES.md +6 -0
  52. modwire_cli-1.2.0/src/modwire_cli/resources/init/TESTING.md +7 -0
  53. modwire_cli-1.2.0/src/modwire_cli/resources/init/WORKFLOW.md +8 -0
  54. modwire_cli-1.2.0/src/modwire_cli/resources/init/architecture.yaml +56 -0
  55. modwire_cli-1.2.0/src/modwire_cli.egg-info/PKG-INFO +108 -0
  56. modwire_cli-1.2.0/src/modwire_cli.egg-info/SOURCES.txt +74 -0
  57. modwire_cli-1.2.0/src/modwire_cli.egg-info/entry_points.txt +2 -0
  58. modwire_cli-1.2.0/src/modwire_cli.egg-info/requires.txt +11 -0
  59. modwire_cli-1.2.0/src/modwire_cli.egg-info/scm_file_list.json +69 -0
  60. modwire_cli-1.2.0/src/modwire_cli.egg-info/scm_version.json +8 -0
  61. modwire_cli-1.2.0/src/modwire_cli.egg-info/top_level.txt +1 -0
  62. modwire_cli-1.2.0/tests/__init__.py +0 -0
  63. modwire_cli-1.2.0/tests/functional/__init__.py +0 -0
  64. modwire_cli-1.2.0/tests/functional/support.py +44 -0
  65. modwire_cli-1.2.0/tests/functional/test_01_attacks.py +13 -0
  66. modwire_cli-1.2.0/tests/functional/test_02_invariants.py +37 -0
  67. modwire_cli-1.2.0/tests/functional/test_03_interruption.py +18 -0
  68. modwire_cli-1.2.0/tests/functional/test_04_cleanup.py +18 -0
  69. modwire_cli-1.2.0/tests/functional/test_05_recovery.py +16 -0
  70. modwire_cli-1.2.0/tests/functional/test_06_happy_path.py +31 -0
  71. modwire_cli-1.2.0/uv.lock +769 -0
  72. modwire_cli-1.0.1/README.md +0 -37
  73. modwire_cli-1.0.1/src/modwire_cli/__main__.py +0 -38
  74. modwire_cli-1.0.1/tests/test_cli.py +0 -60
  75. {modwire_cli-1.0.1 → modwire_cli-1.2.0}/LICENSE +0 -0
  76. {modwire_cli-1.0.1 → modwire_cli-1.2.0}/src/modwire_cli/__init__.py +0 -0
  77. {modwire_cli-1.0.1 → modwire_cli-1.2.0}/src/modwire_cli/pipeline/__init__.py +0 -0
  78. {modwire_cli-1.0.1 → modwire_cli-1.2.0}/src/modwire_cli/pipeline/steps/__init__.py +0 -0
  79. /modwire_cli-1.0.1/src/modwire_cli/py.typed → /modwire_cli-1.2.0/src/modwire_cli.egg-info/dependency_links.txt +0 -0
@@ -0,0 +1,17 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+ workflow_dispatch:
8
+
9
+ permissions:
10
+ contents: read
11
+
12
+ jobs:
13
+ verify:
14
+ uses: modwire/modwire-architecture/.github/workflows/python-package.yml@workflows-v1
15
+ with:
16
+ python-versions: '["3.12", "3.13", "3.14"]'
17
+ build-python-version: "3.12"
@@ -0,0 +1,40 @@
1
+ name: Release
2
+
3
+ on:
4
+ release:
5
+ types: [published]
6
+
7
+ permissions:
8
+ contents: read
9
+
10
+ jobs:
11
+ build:
12
+ uses: modwire/modwire-architecture/.github/workflows/python-release-build.yml@workflows-v1
13
+ with:
14
+ release-tag: ${{ github.event.release.tag_name }}
15
+ python-version: "3.12"
16
+
17
+ publish-github-assets:
18
+ needs: build
19
+ uses: modwire/modwire-architecture/.github/workflows/python-release-assets.yml@workflows-v1
20
+ with:
21
+ release-tag: ${{ github.event.release.tag_name }}
22
+ permissions:
23
+ contents: write
24
+
25
+ publish-pypi:
26
+ needs: [build, publish-github-assets]
27
+ runs-on: ubuntu-latest
28
+ environment:
29
+ name: pypi
30
+ url: https://pypi.org/project/modwire-cli/
31
+ permissions:
32
+ id-token: write
33
+ steps:
34
+ - uses: actions/download-artifact@v8
35
+ with:
36
+ name: python-package-distributions
37
+ path: dist/
38
+ - uses: pypa/gh-action-pypi-publish@release/v1
39
+ with:
40
+ skip-existing: true
@@ -5,3 +5,4 @@ __pycache__/
5
5
  *.py[cod]
6
6
  dist/
7
7
  src/modwire_cli/_version.py
8
+ *egg*
@@ -0,0 +1,10 @@
1
+ # Modwire
2
+
3
+ Modwire keeps software changes aligned with a project's architecture, working rules, testing expectations, and delivery workflow.
4
+
5
+ - [Architecture configuration](docs/ARCHITECTURE.md)
6
+ - [Quality loop](docs/MODWIRE.md)
7
+ - [Project](docs/PROJECT.md)
8
+ - [Working rules](docs/RULES.md)
9
+ - [Testing](docs/TESTING.md)
10
+ - [GitHub workflow](docs/WORKFLOW.md)
@@ -0,0 +1,38 @@
1
+ boundaries:
2
+ tags:
3
+ - name: package
4
+ match: src/modwire_cli
5
+ - name: tests
6
+ match: tests
7
+ - name: scripts
8
+ match: scripts
9
+ flow:
10
+ module_tag: package
11
+ realms:
12
+ - name: tests
13
+ module_tag: tests
14
+ - name: scripts
15
+ module_tag: scripts
16
+ analyzers:
17
+ - module-boundaries
18
+ - no-cycles
19
+ - no-reentry
20
+ shape:
21
+ max_classes_per_file: 1
22
+ max_interfaces_per_file: 0
23
+ max_types_per_file: 0
24
+ max_abstract_classes_per_file: -1
25
+ max_functions_per_file: 0
26
+ max_methods_per_class: -1
27
+ max_declared_args: -1
28
+ max_function_lines: -1
29
+ max_method_lines: -1
30
+ max_class_lines: -1
31
+ allow_optional_function_args: false
32
+ allow_optional_method_args: false
33
+ allow_optional_class_properties: false
34
+ allow_import_aliases: false
35
+ require_joined_imports: true
36
+ allowed_import_crossing_types:
37
+ - module
38
+ - symbol
@@ -0,0 +1,34 @@
1
+ # Architecture configuration
2
+
3
+ `architecture.yaml` describes the source-tree boundaries and structural limits that Modwire checks. Adapt its paths and names before enforcing it in a project.
4
+
5
+ ## Tags
6
+
7
+ Each tag gives a source-tree pattern a meaningful name. The example uses compact YAML mappings to keep the relationship visible:
8
+
9
+ ```yaml
10
+ tags:
11
+ - {name: &module_tag module, match: src/*}
12
+ - {name: &shared_tag shared, match: src/shared}
13
+ ```
14
+
15
+ `&module_tag` anchors the scalar value `module`; `*module_tag` inserts that same string elsewhere in the file. This avoids repeating tag names:
16
+
17
+ ```yaml
18
+ rules:
19
+ - {source: *module_tag, disallow: [*module_tag], allow: [*shared_tag], allow_same_match: true}
20
+ ```
21
+
22
+ Anchor the `name` value, not the whole tag object. Rule `source` and `allow` fields, and flow `module_tag` and `layers` fields, accept tag-name strings rather than `{name, match}` objects.
23
+
24
+ ## Rules and flow
25
+
26
+ Rules describe which tagged modules may depend on one another. Flow groups tags into a module realm and, when needed, orders its layers:
27
+
28
+ ```yaml
29
+ flow:
30
+ realms:
31
+ - {name: backend, module_tag: *backend_module_tag, layers: [*api_tag, *application_tag, *infrastructure_tag, *domain_tag]}
32
+ ```
33
+
34
+ Use aliases consistently when a tag name appears in more than one place. The supplied `architecture.yaml` is a complete, adaptable example; keep its shape limits only when they suit the project.
@@ -0,0 +1,5 @@
1
+ # Quality loop
2
+
3
+ 1. Run `modwire report --language <language>` before changing code.
4
+ 2. Make the smallest coherent change.
5
+ 3. Run the report again and resolve violations before review.
@@ -0,0 +1,16 @@
1
+ # modwire-cli
2
+
3
+ Python 3.12+ CLI for Modwire architecture reports.
4
+
5
+ ## Structure
6
+
7
+ - `__main__.py`: argument parsing and dependency-container lifecycle.
8
+ - `facade.py`: load config, extract code, request reports, run the output pipeline.
9
+ - `pipeline/steps/`: independently rendered report sections, ordered by Wireup qualifiers.
10
+ - `.modwire/architecture.yaml`: strict `ArchitectureConfig` input; unknown fields must remain invalid.
11
+
12
+ ## Working rules
13
+
14
+ - Preserve `modwire --language <language>` report behavior unless a change explicitly replaces it.
15
+ - Keep CLI options at the boundary; put behavior in injected services/facades.
16
+ - Add subprocess-level coverage for every user-facing command in `tests/test_cli.py`.
@@ -0,0 +1,6 @@
1
+ # Working rules
2
+
3
+ - Use comments only as docstrings that document public API classes or functions.
4
+ - Keep documentation focused on its readers' tasks; do not generate internal implementation inventories for end users.
5
+ - When running local CLI commands, use host mode or an execution mode with privileges equivalent to the user's.
6
+ - Read the local guidance before changing code.
@@ -0,0 +1,7 @@
1
+ # Testing
2
+
3
+ If a local Modwire MCP tool is available, consult it first for project-specific testing rules.
4
+
5
+ Test only public interfaces and assert observable outcomes such as returned values, exit codes, output, or files.
6
+
7
+ Write scenarios in Auntie order: attacks, invariants, interruption, cleanup, recovery, then happy paths.
@@ -0,0 +1,8 @@
1
+ # GitHub workflow
2
+
3
+ 1. Start with both the local and remote workspaces clean and with only the `main` branch.
4
+ 2. Make changes only in the local workspace, under user supervision.
5
+ 3. The user may create multiple local checkpoint commits while the work is in progress.
6
+ 4. Before pushing the feature branch, squash all of its current commits into one commit with a clear, appropriate message.
7
+ 5. Submit a pull request. It becomes ready to merge only after its QA checks succeed.
8
+ 6. After the user merges the pull request, clean up both workspaces so that only the `main` branch remains.
@@ -0,0 +1 @@
1
+ Check `.modwire/INDEX.md` before making changes.
@@ -0,0 +1,14 @@
1
+ .PHONY: docs docs-check verify
2
+
3
+ check:
4
+ uv run modwire --language python --summary
5
+
6
+ docs:
7
+ uv run python scripts/generate_docs.py
8
+
9
+ docs-check:
10
+ uv run python scripts/generate_docs.py --check
11
+
12
+ verify: docs-check
13
+ uv run ruff check .
14
+ uv run pytest
@@ -1,17 +1,15 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: modwire-cli
3
- Version: 1.0.1
3
+ Version: 1.2.0
4
4
  Summary: Command-line interface for the Modwire ecosystem.
5
+ Author: Tomasz Szpak
6
+ License-Expression: MIT
5
7
  Project-URL: Homepage, https://github.com/modwire/modwire-cli
6
8
  Project-URL: Repository, https://github.com/modwire/modwire-cli
7
9
  Project-URL: Issues, https://github.com/modwire/modwire-cli/issues
8
- Author: Tomasz Szpak
9
- License-Expression: MIT
10
- License-File: LICENSE
11
10
  Keywords: cli,modwire
12
11
  Classifier: Development Status :: 3 - Alpha
13
12
  Classifier: Intended Audience :: Developers
14
- Classifier: License :: OSI Approved :: MIT License
15
13
  Classifier: Operating System :: OS Independent
16
14
  Classifier: Programming Language :: Python
17
15
  Classifier: Programming Language :: Python :: 3
@@ -21,17 +19,19 @@ Classifier: Programming Language :: Python :: 3.13
21
19
  Classifier: Programming Language :: Python :: 3.14
22
20
  Classifier: Topic :: Software Development
23
21
  Requires-Python: >=3.12
22
+ Description-Content-Type: text/markdown
23
+ License-File: LICENSE
24
24
  Requires-Dist: modwire-architecture<7,>=6
25
25
  Requires-Dist: modwire-extraction<3,>=2
26
26
  Requires-Dist: pydantic-yaml>=1.7
27
27
  Requires-Dist: rich>=13
28
28
  Requires-Dist: wireup>=2.12.0
29
29
  Provides-Extra: dev
30
- Requires-Dist: build>=1.3; extra == 'dev'
31
- Requires-Dist: pytest>=9; extra == 'dev'
32
- Requires-Dist: ruff>=0.14; extra == 'dev'
33
- Requires-Dist: twine>=6; extra == 'dev'
34
- Description-Content-Type: text/markdown
30
+ Requires-Dist: build>=1.3; extra == "dev"
31
+ Requires-Dist: pytest>=9; extra == "dev"
32
+ Requires-Dist: ruff>=0.14; extra == "dev"
33
+ Requires-Dist: twine>=6; extra == "dev"
34
+ Dynamic: license-file
35
35
 
36
36
  # modwire-cli
37
37
 
@@ -46,22 +46,51 @@ pip install modwire-cli
46
46
  ## Usage
47
47
 
48
48
  ```sh
49
- modwire --language python
49
+ modwire init
50
+ modwire report --language python
50
51
  ```
51
52
 
52
53
  By default, Modwire reads its configuration from `.modwire/architecture.yaml`
53
54
  and analyses the current directory. Override either path when needed:
54
55
 
55
56
  ```sh
56
- modwire --language typescript --dot-dir path/to/.modwire --architecture-root path/to/source
57
+ modwire report --language typescript --dot-dir path/to/.modwire --architecture-root path/to/source
58
+ ```
59
+
60
+ Use `--summary` for a compact module → layer map that omits individual source files:
61
+
62
+ ```sh
63
+ modwire report --language python --summary
64
+ ```
65
+
66
+ The original option-only command remains supported for existing automation:
67
+
68
+ ```sh
69
+ modwire --language python
57
70
  ```
58
71
 
59
72
  To check this repository's architecture:
60
73
 
61
74
  ```sh
62
- uv run modwire --language python
75
+ uv run modwire report --language python
63
76
  ```
64
77
 
78
+ <!-- generated:public-api:start -->
79
+ ## Command reference
80
+
81
+ Provide `modwire init` for setup and `modwire report` for architecture feedback.
82
+
83
+ Existing `modwire --language <language>` automation continues to run reports.
84
+
85
+ | Command | Purpose |
86
+ | --- | --- |
87
+ | `modwire init` | Create `.modwire/` guidance and a strict architecture template. |
88
+ | `modwire report --language <language>` | Analyse the configured project and render violations. |
89
+ | `modwire --language <language>` | Backwards-compatible form of `report`. |
90
+
91
+ Use `--summary` with `report` to render module-to-layer membership without files.
92
+ <!-- generated:public-api:end -->
93
+
65
94
  ## Development
66
95
 
67
96
  ```sh
@@ -70,3 +99,10 @@ uv run pytest
70
99
  uv run ruff check .
71
100
  uv build
72
101
  ```
102
+
103
+ The README command reference is generated from the public CLI docstring:
104
+
105
+ ```sh
106
+ make docs
107
+ make verify
108
+ ```
@@ -0,0 +1,73 @@
1
+ # modwire-cli
2
+
3
+ The command-line interface for the Modwire ecosystem.
4
+
5
+ ## Install
6
+
7
+ ```sh
8
+ pip install modwire-cli
9
+ ```
10
+
11
+ ## Usage
12
+
13
+ ```sh
14
+ modwire init
15
+ modwire report --language python
16
+ ```
17
+
18
+ By default, Modwire reads its configuration from `.modwire/architecture.yaml`
19
+ and analyses the current directory. Override either path when needed:
20
+
21
+ ```sh
22
+ modwire report --language typescript --dot-dir path/to/.modwire --architecture-root path/to/source
23
+ ```
24
+
25
+ Use `--summary` for a compact module → layer map that omits individual source files:
26
+
27
+ ```sh
28
+ modwire report --language python --summary
29
+ ```
30
+
31
+ The original option-only command remains supported for existing automation:
32
+
33
+ ```sh
34
+ modwire --language python
35
+ ```
36
+
37
+ To check this repository's architecture:
38
+
39
+ ```sh
40
+ uv run modwire report --language python
41
+ ```
42
+
43
+ <!-- generated:public-api:start -->
44
+ ## Command reference
45
+
46
+ Provide `modwire init` for setup and `modwire report` for architecture feedback.
47
+
48
+ Existing `modwire --language <language>` automation continues to run reports.
49
+
50
+ | Command | Purpose |
51
+ | --- | --- |
52
+ | `modwire init` | Create `.modwire/` guidance and a strict architecture template. |
53
+ | `modwire report --language <language>` | Analyse the configured project and render violations. |
54
+ | `modwire --language <language>` | Backwards-compatible form of `report`. |
55
+
56
+ Use `--summary` with `report` to render module-to-layer membership without files.
57
+ <!-- generated:public-api:end -->
58
+
59
+ ## Development
60
+
61
+ ```sh
62
+ uv sync --all-groups
63
+ uv run pytest
64
+ uv run ruff check .
65
+ uv build
66
+ ```
67
+
68
+ The README command reference is generated from the public CLI docstring:
69
+
70
+ ```sh
71
+ make docs
72
+ make verify
73
+ ```
@@ -1,6 +1,6 @@
1
1
  [build-system]
2
- requires = ["hatchling>=1.28", "hatch-vcs>=0.5"]
3
- build-backend = "hatchling.build"
2
+ requires = ["setuptools>=77", "setuptools-scm[toml]>=8", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "modwire-cli"
@@ -13,7 +13,6 @@ authors = [{ name = "Tomasz Szpak" }]
13
13
  classifiers = [
14
14
  "Development Status :: 3 - Alpha",
15
15
  "Intended Audience :: Developers",
16
- "License :: OSI Approved :: MIT License",
17
16
  "Operating System :: OS Independent",
18
17
  "Programming Language :: Python",
19
18
  "Programming Language :: Python :: 3",
@@ -33,7 +32,7 @@ dependencies = [
33
32
  ]
34
33
 
35
34
  [project.scripts]
36
- modwire = "modwire_cli.__main__:main"
35
+ modwire = "modwire_cli.cli:main"
37
36
 
38
37
  [project.optional-dependencies]
39
38
  dev = [
@@ -56,19 +55,21 @@ dev = [
56
55
  "twine>=6",
57
56
  ]
58
57
 
59
- [tool.hatch.version]
60
- source = "vcs"
61
- tag-pattern = '^v(?P<version>\d+\.\d+\.\d+)$'
62
- fallback-version = "0.0.0"
58
+ [tool.setuptools]
59
+ package-dir = { "" = "src" }
60
+ include-package-data = true
63
61
 
64
- [tool.hatch.build.hooks.vcs]
65
- version-file = "src/modwire_cli/_version.py"
62
+ [tool.setuptools.packages.find]
63
+ where = ["src"]
66
64
 
67
- [tool.hatch.build.targets.wheel]
68
- packages = ["src/modwire_cli"]
65
+ [tool.setuptools.package-data]
66
+ "modwire_cli" = ["py.typed", "resources/init/*"]
69
67
 
70
- [tool.hatch.build.targets.sdist]
71
- include = ["src", "tests", "README.md", "LICENSE", "pyproject.toml"]
68
+ [tool.setuptools_scm]
69
+ tag_regex = '^v(?P<version>\d+\.\d+\.\d+)$'
70
+ fallback_version = "0.0.0.dev0"
71
+ local_scheme = "no-local-version"
72
+ version_file = "src/modwire_cli/_version.py"
72
73
 
73
74
  [tool.pytest.ini_options]
74
75
  testpaths = ["tests"]
@@ -0,0 +1,4 @@
1
+ from modwire_cli.documentation import DocumentationCommand
2
+
3
+ if __name__ == "__main__":
4
+ raise SystemExit(DocumentationCommand().run())
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,4 @@
1
+ from .cli import main
2
+
3
+ if __name__ == "__main__":
4
+ raise SystemExit(main())
@@ -18,7 +18,7 @@ version_tuple: tuple[int | str, ...]
18
18
  commit_id: str | None
19
19
  __commit_id__: str | None
20
20
 
21
- __version__ = version = '1.0.1'
22
- __version_tuple__ = version_tuple = (1, 0, 1)
21
+ __version__ = version = '1.2.0'
22
+ __version_tuple__ = version_tuple = (1, 2, 0)
23
23
 
24
- __commit_id__ = commit_id = None
24
+ __commit_id__ = commit_id = 'g5c68c8d1a'
@@ -0,0 +1,63 @@
1
+ from __future__ import annotations
2
+
3
+ import argparse
4
+ import sys
5
+ from collections.abc import Sequence
6
+ from pathlib import Path
7
+
8
+ from rich.console import Console
9
+ from wireup import create_sync_container, instance
10
+
11
+ import modwire_cli
12
+
13
+ from .facade import FitnessFacade
14
+ from .initialization import InitializationFacade
15
+
16
+
17
+ class CommandLine:
18
+ """Provide `modwire init` for setup and `modwire report` for architecture feedback.
19
+
20
+ Existing `modwire --language <language>` automation continues to run reports.
21
+ """
22
+
23
+ def __call__(self) -> int:
24
+ """Run the command supplied by the process argument vector."""
25
+ return self.run(sys.argv[1:])
26
+
27
+ def run(self, argv: Sequence[str]) -> int:
28
+ """Run one command and return its shell-compatible status code."""
29
+ arguments = self._parser().parse_args(self._legacy_report_arguments(argv))
30
+ container = create_sync_container(injectables=[modwire_cli, instance(Console(), as_type=Console)])
31
+ try:
32
+ if arguments.command == "init":
33
+ return container.get(InitializationFacade).initialize(Path.cwd(), arguments.dot_dir, arguments.force)
34
+ return container.get(FitnessFacade).reports(
35
+ arguments.dot_dir,
36
+ arguments.architecture_root,
37
+ arguments.language,
38
+ arguments.summary,
39
+ )
40
+ finally:
41
+ container.close()
42
+
43
+ @staticmethod
44
+ def _parser() -> argparse.ArgumentParser:
45
+ parser = argparse.ArgumentParser()
46
+ commands = parser.add_subparsers(dest="command", required=True)
47
+ init = commands.add_parser("init", help="Create project-local Modwire configuration.")
48
+ init.add_argument("--dot-dir", type=Path, default=Path(".modwire"))
49
+ init.add_argument("--force", action="store_true", help="Replace existing generated assets.")
50
+ report = commands.add_parser("report", help="Run architecture reports.")
51
+ report.add_argument("--dot-dir", type=Path, default=Path(".modwire"))
52
+ report.add_argument("--architecture-root", type=Path, default=Path("."))
53
+ report.add_argument("--language", required=True)
54
+ report.add_argument("--summary", action="store_true", help="Render modules without individual files.")
55
+ return parser
56
+
57
+ @staticmethod
58
+ def _legacy_report_arguments(argv: Sequence[str]) -> Sequence[str]:
59
+ command_names = {"init", "report", "-h", "--help"}
60
+ return ("report", *argv) if argv and argv[0] not in command_names else argv
61
+
62
+
63
+ main = CommandLine()
@@ -11,5 +11,7 @@ from wireup import injectable
11
11
  @injectable
12
12
  @dataclass(frozen=True)
13
13
  class ConfigService:
14
+ """Load validated architecture configuration from the project dot directory."""
15
+
14
16
  def load(self, dot_dir: Path) -> ArchitectureConfig:
15
17
  return parse_yaml_file_as(ArchitectureConfig, dot_dir / "architecture.yaml")
@@ -0,0 +1,4 @@
1
+ from .command import DocumentationCommand
2
+ from .generator import DocumentationGenerator
3
+
4
+ __all__ = ["DocumentationCommand", "DocumentationGenerator"]
@@ -0,0 +1,23 @@
1
+ from __future__ import annotations
2
+
3
+ import argparse
4
+ from pathlib import Path
5
+
6
+ from .generator import DocumentationGenerator
7
+
8
+
9
+ class DocumentationCommand:
10
+ """Update or validate the generated README class reference."""
11
+
12
+ def run(self) -> int:
13
+ """Run the documentation command from the process argument vector."""
14
+ parser = argparse.ArgumentParser(description="Generate README class reference documentation.")
15
+ parser.add_argument("--check", action="store_true", help="Fail instead of updating stale documentation.")
16
+ arguments = parser.parse_args()
17
+ readme = Path(__file__).parents[3] / "README.md"
18
+ if DocumentationGenerator().update(readme, arguments.check):
19
+ return 0
20
+ if arguments.check:
21
+ print("Generated documentation is stale. Run `make docs` and commit the result.")
22
+ return 1
23
+ return 0
@@ -0,0 +1,47 @@
1
+ from __future__ import annotations
2
+
3
+ from inspect import cleandoc
4
+ from pathlib import Path
5
+
6
+ from ..cli import CommandLine
7
+
8
+ START = "<!-- generated:public-api:start -->"
9
+ END = "<!-- generated:public-api:end -->"
10
+
11
+
12
+ class DocumentationGenerator:
13
+ """Render a concise README command reference from the public CLI docstring."""
14
+
15
+ def render(self) -> str:
16
+ """Render the marked README section for people using the CLI."""
17
+ return "\n".join(
18
+ (
19
+ START,
20
+ "## Command reference",
21
+ "",
22
+ cleandoc(CommandLine.__doc__ or ""),
23
+ "",
24
+ "| Command | Purpose |",
25
+ "| --- | --- |",
26
+ "| `modwire init` | Create `.modwire/` guidance and a strict architecture template. |",
27
+ "| `modwire report --language <language>` | Analyse the configured project and render violations. |",
28
+ "| `modwire --language <language>` | Backwards-compatible form of `report`. |",
29
+ "",
30
+ "Use `--summary` with `report` to render module-to-layer membership without files.",
31
+ END,
32
+ )
33
+ )
34
+
35
+ def update(self, readme: Path, check: bool) -> bool:
36
+ """Update the README section, or report whether it is current."""
37
+ current = readme.read_text(encoding="utf-8")
38
+ if START not in current or END not in current:
39
+ raise ValueError(f"Missing generated documentation markers in {readme}")
40
+ prefix, remainder = current.split(START, 1)
41
+ _, suffix = remainder.split(END, 1)
42
+ expected = f"{prefix}{self.render()}{suffix}"
43
+ if current == expected:
44
+ return True
45
+ if not check:
46
+ readme.write_text(expected, encoding="utf-8")
47
+ return False
@@ -10,5 +10,7 @@ from wireup import injectable
10
10
  @injectable
11
11
  @dataclass(frozen=True)
12
12
  class ExtractionService:
13
+ """Extract a queryable code map for one source root and language."""
14
+
13
15
  def load(self, architecture_root: Path, language: str) -> QueryableCodeMap:
14
16
  return ModwireExtraction(architecture_root).generate_queryable_map(language)