pinky-tools 0.1.0.dev1__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 (35) hide show
  1. pinky_tools-0.1.0.dev1/.github/workflows/ci.yml +36 -0
  2. pinky_tools-0.1.0.dev1/.github/workflows/publish.yml +32 -0
  3. pinky_tools-0.1.0.dev1/.github/workflows/release.yml +37 -0
  4. pinky_tools-0.1.0.dev1/.gitignore +19 -0
  5. pinky_tools-0.1.0.dev1/.pre-commit-config.yaml +116 -0
  6. pinky_tools-0.1.0.dev1/.secrets.baseline +127 -0
  7. pinky_tools-0.1.0.dev1/AGENT.md +80 -0
  8. pinky_tools-0.1.0.dev1/CONTEXT.md +18 -0
  9. pinky_tools-0.1.0.dev1/LICENSE +21 -0
  10. pinky_tools-0.1.0.dev1/PKG-INFO +84 -0
  11. pinky_tools-0.1.0.dev1/README.md +31 -0
  12. pinky_tools-0.1.0.dev1/docs/adr/0001-click-over-argparse.md +18 -0
  13. pinky_tools-0.1.0.dev1/docs/adr/0002-i18n-env-var-from-day-one.md +25 -0
  14. pinky_tools-0.1.0.dev1/docs/adr/0003-export-last-select-wins.md +21 -0
  15. pinky_tools-0.1.0.dev1/docs/adr/0004-export-col-transform-not-in-yaml.md +21 -0
  16. pinky_tools-0.1.0.dev1/docs/adr/0005-export-queries-path-convention.md +26 -0
  17. pinky_tools-0.1.0.dev1/docs/explanation/design.md +59 -0
  18. pinky_tools-0.1.0.dev1/docs/explanation/features.md +115 -0
  19. pinky_tools-0.1.0.dev1/docs/index.md +20 -0
  20. pinky_tools-0.1.0.dev1/docs/reference/generated/api/readme.md +5 -0
  21. pinky_tools-0.1.0.dev1/docs/reference/generated/api/regression.md +5 -0
  22. pinky_tools-0.1.0.dev1/docs/reference/generated/api/run.md +5 -0
  23. pinky_tools-0.1.0.dev1/docs/reference/generated/api/validate_name.md +5 -0
  24. pinky_tools-0.1.0.dev1/mkdocs.dev.yml +3 -0
  25. pinky_tools-0.1.0.dev1/mkdocs.yml +36 -0
  26. pinky_tools-0.1.0.dev1/pyproject.toml +97 -0
  27. pinky_tools-0.1.0.dev1/scripts/hooks/check_forbidden_keywords.py +76 -0
  28. pinky_tools-0.1.0.dev1/scripts/hooks/check_snowpark_api.py +160 -0
  29. pinky_tools-0.1.0.dev1/scripts/hooks/check_typed_api.py +140 -0
  30. pinky_tools-0.1.0.dev1/scripts/hooks/check_zws.py +39 -0
  31. pinky_tools-0.1.0.dev1/src/pinky_tools/__init__.py +1 -0
  32. pinky_tools-0.1.0.dev1/src/pinky_tools/readme.py +1 -0
  33. pinky_tools-0.1.0.dev1/src/pinky_tools/regression.py +1 -0
  34. pinky_tools-0.1.0.dev1/src/pinky_tools/run.py +1 -0
  35. pinky_tools-0.1.0.dev1/src/pinky_tools/validate_name.py +1 -0
@@ -0,0 +1,36 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main, develop]
6
+ pull_request:
7
+ branches: [main, develop]
8
+
9
+ jobs:
10
+ ci:
11
+ runs-on: ubuntu-latest
12
+ env:
13
+ FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
14
+
15
+ steps:
16
+ - uses: actions/checkout@v4
17
+
18
+ - name: Set up Python 3.11
19
+ uses: actions/setup-python@v5
20
+ with:
21
+ python-version: "3.11"
22
+
23
+ - name: Install dependencies
24
+ run: pip install -e ".[dev]"
25
+
26
+ - name: Lint
27
+ run: ruff check src/ scripts/
28
+
29
+ - name: Format check
30
+ run: ruff format --check src/ scripts/
31
+
32
+ - name: Type check
33
+ run: mypy src/pinky_tools/
34
+
35
+ - name: Security scan
36
+ run: bandit -r src/pinky_tools/ -ll
@@ -0,0 +1,32 @@
1
+ name: Publish to PyPI
2
+
3
+ on:
4
+ push:
5
+ tags:
6
+ - "v*.dev*"
7
+
8
+ jobs:
9
+ publish:
10
+ runs-on: ubuntu-latest
11
+ env:
12
+ FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
13
+ permissions:
14
+ contents: read
15
+ id-token: write # required for trusted publishing (OIDC)
16
+
17
+ steps:
18
+ - uses: actions/checkout@v4
19
+
20
+ - name: Set up Python 3.11
21
+ uses: actions/setup-python@v5
22
+ with:
23
+ python-version: "3.11"
24
+
25
+ - name: Install build
26
+ run: pip install build
27
+
28
+ - name: Build wheel and sdist
29
+ run: python -m build
30
+
31
+ - name: Publish to PyPI
32
+ uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,37 @@
1
+ name: Release
2
+ on:
3
+ push:
4
+ branches: [main]
5
+ jobs:
6
+ release:
7
+ runs-on: ubuntu-latest
8
+ env:
9
+ FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
10
+ permissions:
11
+ contents: write
12
+ id-token: write
13
+ steps:
14
+ - uses: actions/checkout@v4
15
+ - name: Read version
16
+ id: version
17
+ run: echo "version=$(grep '^version' pyproject.toml | cut -d'"' -f2)" >> $GITHUB_OUTPUT
18
+ - name: Guard — no .dev version on main
19
+ run: |
20
+ if [[ "${{ steps.version.outputs.version }}" == *".dev"* ]]; then
21
+ echo "❌ version .dev sur main — bumper la version avant de merger"
22
+ exit 1
23
+ fi
24
+ - name: Create tag
25
+ run: |
26
+ git tag "v${{ steps.version.outputs.version }}"
27
+ git push origin "v${{ steps.version.outputs.version }}"
28
+ - name: Set up Python 3.11
29
+ uses: actions/setup-python@v5
30
+ with:
31
+ python-version: "3.11"
32
+ - name: Install build
33
+ run: pip install build
34
+ - name: Build
35
+ run: python -m build
36
+ - name: Publish to PyPI
37
+ uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,19 @@
1
+ # MkDocs
2
+ docs/assets/
3
+ site/
4
+
5
+ # Python
6
+ __pycache__/
7
+ *.py[cod]
8
+ *.egg-info/
9
+ dist/
10
+ build/
11
+ .venv/
12
+ .mypy_cache/
13
+ .ruff_cache/
14
+ .pytest_cache/
15
+ .coverage
16
+ htmlcov/
17
+
18
+ # macOS
19
+ .DS_Store
@@ -0,0 +1,116 @@
1
+ ---
2
+ # =============================================================================
3
+ # .pre-commit-config.yaml — pinky-tools
4
+ # =============================================================================
5
+ # Install:
6
+ # pip install pre-commit
7
+ # pre-commit install
8
+ # pre-commit install --hook-type commit-msg
9
+ #
10
+ # Update standard hooks:
11
+ # pre-commit autoupdate
12
+ # =============================================================================
13
+ default_install_hook_types: [pre-commit, commit-msg]
14
+
15
+ repos:
16
+ # ── Formatting: trailing spaces + newline ─────────────────────────────────
17
+ - repo: https://github.com/pre-commit/pre-commit-hooks
18
+ rev: v6.0.0
19
+ hooks:
20
+ - id: trailing-whitespace
21
+ - id: end-of-file-fixer
22
+ - id: check-yaml
23
+ - id: check-toml
24
+ - id: check-merge-conflict
25
+ - id: debug-statements
26
+
27
+ # ── Formatting: Markdown ──────────────────────────────────────────────────
28
+ - repo: local
29
+ hooks:
30
+ - id: mdformat
31
+ name: mdformat
32
+ entry: mdformat
33
+ language: system
34
+ files: ^README\.md$
35
+
36
+ # ── Security: secrets & forbidden files ──────────────────────────────────
37
+ - repo: https://github.com/gitleaks/gitleaks
38
+ rev: v8.30.0
39
+ hooks:
40
+ - id: gitleaks
41
+
42
+ # ── Security: alternative secret detection ────────────────────────────────
43
+ - repo: https://github.com/Yelp/detect-secrets
44
+ rev: v1.5.0
45
+ hooks:
46
+ - id: detect-secrets
47
+ args: ["--baseline", ".secrets.baseline"]
48
+ exclude: ^docs/
49
+
50
+ # ── Python: ruff lint + format ────────────────────────────────────────────
51
+ - repo: https://github.com/astral-sh/ruff-pre-commit
52
+ rev: v0.15.9
53
+ hooks:
54
+ - id: ruff-check
55
+ args: [--no-fix]
56
+ - id: ruff-format
57
+ args: [--check]
58
+
59
+ # ── Python: type checking ─────────────────────────────────────────────────
60
+ - repo: local
61
+ hooks:
62
+ - id: mypy
63
+ name: mypy
64
+ entry: mypy
65
+ language: system
66
+ types: [python]
67
+ args: [--ignore-missing-imports]
68
+ files: ^src/
69
+
70
+ # ── Python: security ─────────────────────────────────────────────────────
71
+ - repo: local
72
+ hooks:
73
+ - id: bandit
74
+ name: bandit
75
+ entry: bandit
76
+ language: system
77
+ pass_filenames: false
78
+ args: [-r, src/, -ll]
79
+
80
+ # ── Zero-width spaces (copy-paste from Outlook/Teams) ────────────────────
81
+ - repo: local
82
+ hooks:
83
+ - id: check-zws
84
+ name: "[ZWS] Invisible characters (zero-width)"
85
+ entry: python scripts/hooks/check_zws.py
86
+ language: system
87
+ pass_filenames: true
88
+
89
+ # ── Typed API guard ──────────────────────────────────────────────────────
90
+ - repo: local
91
+ hooks:
92
+ - id: check-typed-api
93
+ name: "[TYPED-API] No raw dict/tuple/set in public annotations"
94
+ entry: python scripts/hooks/check_typed_api.py
95
+ language: system
96
+ types: [python]
97
+ files: ^src/
98
+
99
+ # ── Snowpark / Core API conventions ──────────────────────────────────────
100
+ - repo: local
101
+ hooks:
102
+ - id: check-snowpark-api
103
+ name: "[SNOWPARK-API] No session.sql(), canonical method names"
104
+ entry: python scripts/hooks/check_snowpark_api.py
105
+ language: system
106
+ types: [python]
107
+ files: ^src/
108
+
109
+ # ── Employer reference guard ──────────────────────────────────────────────
110
+ - repo: local
111
+ hooks:
112
+ - id: check-forbidden-keywords
113
+ name: "[FORBIDDEN] Employer references"
114
+ entry: python scripts/hooks/check_forbidden_keywords.py
115
+ language: system
116
+ pass_filenames: true
@@ -0,0 +1,127 @@
1
+ {
2
+ "version": "1.5.0",
3
+ "plugins_used": [
4
+ {
5
+ "name": "ArtifactoryDetector"
6
+ },
7
+ {
8
+ "name": "AWSKeyDetector"
9
+ },
10
+ {
11
+ "name": "AzureStorageKeyDetector"
12
+ },
13
+ {
14
+ "name": "Base64HighEntropyString",
15
+ "limit": 4.5
16
+ },
17
+ {
18
+ "name": "BasicAuthDetector"
19
+ },
20
+ {
21
+ "name": "CloudantDetector"
22
+ },
23
+ {
24
+ "name": "DiscordBotTokenDetector"
25
+ },
26
+ {
27
+ "name": "GitHubTokenDetector"
28
+ },
29
+ {
30
+ "name": "GitLabTokenDetector"
31
+ },
32
+ {
33
+ "name": "HexHighEntropyString",
34
+ "limit": 3.0
35
+ },
36
+ {
37
+ "name": "IbmCloudIamDetector"
38
+ },
39
+ {
40
+ "name": "IbmCosHmacDetector"
41
+ },
42
+ {
43
+ "name": "IPPublicDetector"
44
+ },
45
+ {
46
+ "name": "JwtTokenDetector"
47
+ },
48
+ {
49
+ "name": "KeywordDetector",
50
+ "keyword_exclude": ""
51
+ },
52
+ {
53
+ "name": "MailchimpDetector"
54
+ },
55
+ {
56
+ "name": "NpmDetector"
57
+ },
58
+ {
59
+ "name": "OpenAIDetector"
60
+ },
61
+ {
62
+ "name": "PrivateKeyDetector"
63
+ },
64
+ {
65
+ "name": "PypiTokenDetector"
66
+ },
67
+ {
68
+ "name": "SendGridDetector"
69
+ },
70
+ {
71
+ "name": "SlackDetector"
72
+ },
73
+ {
74
+ "name": "SoftlayerDetector"
75
+ },
76
+ {
77
+ "name": "SquareOAuthDetector"
78
+ },
79
+ {
80
+ "name": "StripeDetector"
81
+ },
82
+ {
83
+ "name": "TelegramBotTokenDetector"
84
+ },
85
+ {
86
+ "name": "TwilioKeyDetector"
87
+ }
88
+ ],
89
+ "filters_used": [
90
+ {
91
+ "path": "detect_secrets.filters.allowlist.is_line_allowlisted"
92
+ },
93
+ {
94
+ "path": "detect_secrets.filters.common.is_ignored_due_to_verification_policies",
95
+ "min_level": 2
96
+ },
97
+ {
98
+ "path": "detect_secrets.filters.heuristic.is_indirect_reference"
99
+ },
100
+ {
101
+ "path": "detect_secrets.filters.heuristic.is_likely_id_string"
102
+ },
103
+ {
104
+ "path": "detect_secrets.filters.heuristic.is_lock_file"
105
+ },
106
+ {
107
+ "path": "detect_secrets.filters.heuristic.is_not_alphanumeric_string"
108
+ },
109
+ {
110
+ "path": "detect_secrets.filters.heuristic.is_potential_uuid"
111
+ },
112
+ {
113
+ "path": "detect_secrets.filters.heuristic.is_prefixed_with_dollar_sign"
114
+ },
115
+ {
116
+ "path": "detect_secrets.filters.heuristic.is_sequential_string"
117
+ },
118
+ {
119
+ "path": "detect_secrets.filters.heuristic.is_swagger_file"
120
+ },
121
+ {
122
+ "path": "detect_secrets.filters.heuristic.is_templated_secret"
123
+ }
124
+ ],
125
+ "results": {},
126
+ "generated_at": "2026-06-04T22:13:47Z"
127
+ }
@@ -0,0 +1,80 @@
1
+ # AGENT.md — pinky-tools
2
+ *Update date : 2026-05-31 12:10*
3
+
4
+ Local dev CLI for Snowflake data engineers.
5
+ Part of the pinky suite.
6
+
7
+ ---
8
+
9
+ ## What this repo is
10
+
11
+ `pinky-tools` is the developer-side CLI — it covers the local dev loop that
12
+ `pinky-provider` does not: run a stored procedure locally, generate the README
13
+ from AST, compare outputs across versions, and validate naming conventions.
14
+
15
+ The CLI entry point is `pinky`. See [`CONTEXT.md`](CONTEXT.md) for naming.
16
+
17
+ > **Status: design phase.** See [`docs/INDEX.md`](docs/INDEX.md) for the full design.
18
+
19
+ ---
20
+
21
+ ## Repository structure (target)
22
+
23
+ ```
24
+ pinky-tools/
25
+ ├── src/
26
+ │ └── pinky_tools/
27
+ │ ├── cli.py ← click entry point
28
+ │ ├── run.py ← local SP runner
29
+ │ ├── readme.py ← README generator (AST + live enrichment)
30
+ │ ├── regression.py ← output comparator (CSV / XLSX)
31
+ │ └── validate_name.py ← naming convention validator
32
+ ├── docs/ ← see docs/INDEX.md for the full map
33
+ │ ├── explanation/ ← design.md · features.md
34
+ │ └── adr/
35
+ ├── tests/
36
+ ├── pyproject.toml
37
+ └── AGENT.md ← this file
38
+ ```
39
+
40
+ ---
41
+
42
+ ## Stack
43
+
44
+ - Python 3.11
45
+ - `click` + `rich-click` — CLI
46
+ - `rich` — terminal output
47
+ - `snowflake-snowpark-python` — session for `run` command
48
+ - `openpyxl` — XLSX support for `regression`
49
+
50
+ ---
51
+
52
+ ## CLI commands
53
+
54
+ | Command | What it does |
55
+ |---------|-------------|
56
+ | `pinky run <file>` | Execute a Snowpark `.py` file against a live Snowflake connection |
57
+ | `pinky readme` | Generate README from Python AST extraction |
58
+ | `pinky regression <f1> <f2>` | Compare two output files (CSV / XLSX) |
59
+ | `pinky validate-name <name>` | Validate a schema/repo name against the naming convention |
60
+
61
+ ---
62
+
63
+ ## Commit convention
64
+
65
+ Format: `<emoji> <type>(<scope>): <description>`
66
+
67
+ Scopes: `setup` | `cli` | `run` | `readme` | `regression` | `validate` | `docs` | `ci`
68
+
69
+ See workspace-level CLAUDE.md for the full emoji ↔ type table.
70
+
71
+ ---
72
+
73
+ ## Branch strategy
74
+
75
+ ```
76
+ main ← stable, PR required
77
+ develop ← integration, PR required from feature/*
78
+ feature/* ← one task = one branch
79
+ hotfix/* ← urgent fix from main
80
+ ```
@@ -0,0 +1,18 @@
1
+ # CONTEXT.md — pinky-tools
2
+ *Update date : 2026-05-31 12:10*
3
+
4
+ Single source of truth for naming in this repo.
5
+
6
+ | Term (preferred) | Avoid | Definition |
7
+ |---|---|---|
8
+ | `pinky` | `pythia`, `snowflake-tools` | CLI entry point — the `pinky <command>` invocation |
9
+ | `run` | `runner`, `local_runner` | Command that executes a Snowpark `.py` file against a live Snowflake connection |
10
+ | `readme` | `generate-readme`, `ast-readme` | Command that generates a README from Python AST extraction |
11
+ | `regression` | `compare`, `diff` | Command that compares two output files (CSV / XLSX) |
12
+ | `validate-name` | `check-name`, `name-check` | Command that validates a schema/repo name against the naming convention |
13
+ | SP | stored procedure | A Snowpark Python stored procedure |
14
+ | project | repo, pipeline | A data project following the 1-schema = 1-repo convention |
15
+
16
+ ## Flagged ambiguities
17
+
18
+ - **`pinky` vs `pythia`**: the CLI entry point is `pinky`. `pythia` appears in legacy docs and the rename is being applied progressively — always use `pinky` in new code and docs.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 pinky-and-co
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,84 @@
1
+ Metadata-Version: 2.4
2
+ Name: pinky-tools
3
+ Version: 0.1.0.dev1
4
+ Summary: Local dev CLI for Snowflake data engineers
5
+ Project-URL: Homepage, https://github.com/pinky-and-co/pinky-tools
6
+ Project-URL: Repository, https://github.com/pinky-and-co/pinky-tools
7
+ Project-URL: Issues, https://github.com/pinky-and-co/pinky-tools/issues
8
+ Author: pinky-and-co
9
+ License: MIT License
10
+
11
+ Copyright (c) 2026 pinky-and-co
12
+
13
+ Permission is hereby granted, free of charge, to any person obtaining a copy
14
+ of this software and associated documentation files (the "Software"), to deal
15
+ in the Software without restriction, including without limitation the rights
16
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
17
+ copies of the Software, and to permit persons to whom the Software is
18
+ furnished to do so, subject to the following conditions:
19
+
20
+ The above copyright notice and this permission notice shall be included in all
21
+ copies or substantial portions of the Software.
22
+
23
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
24
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
26
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
27
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
28
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
29
+ SOFTWARE.
30
+ License-File: LICENSE
31
+ Keywords: cli,data-engineering,snowflake,snowpark
32
+ Classifier: License :: OSI Approved :: MIT License
33
+ Classifier: Operating System :: OS Independent
34
+ Classifier: Programming Language :: Python :: 3.11
35
+ Classifier: Topic :: Database
36
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
37
+ Requires-Python: >=3.11
38
+ Requires-Dist: click
39
+ Requires-Dist: openpyxl
40
+ Requires-Dist: rich
41
+ Requires-Dist: rich-click
42
+ Requires-Dist: snowflake-snowpark-python
43
+ Provides-Extra: dev
44
+ Requires-Dist: bandit; extra == 'dev'
45
+ Requires-Dist: mkdocs-material>=9.0; extra == 'dev'
46
+ Requires-Dist: mkdocstrings[python]>=0.25; extra == 'dev'
47
+ Requires-Dist: mypy; extra == 'dev'
48
+ Requires-Dist: pre-commit; extra == 'dev'
49
+ Requires-Dist: pytest; extra == 'dev'
50
+ Requires-Dist: pytest-cov; extra == 'dev'
51
+ Requires-Dist: ruff; extra == 'dev'
52
+ Description-Content-Type: text/markdown
53
+
54
+ # pinky-tools
55
+
56
+ *Update date : 2026-05-31 12:10*
57
+
58
+ Local dev CLI for Snowflake data engineers — run stored procedures locally, generate README docs, and validate naming conventions.
59
+
60
+ Part of the [pinky-and-co](https://github.com/pinky-and-co) suite.
61
+
62
+ ## Install
63
+
64
+ ```bash
65
+ pip install pinky-tools
66
+ ```
67
+
68
+ ## Quick start
69
+
70
+ ```bash
71
+ pinky run resources/procedure/my_sp.py
72
+ pinky readme
73
+ pinky validate-name "financial__workday_to_altaix__compensation"
74
+ ```
75
+
76
+ ## Docs
77
+
78
+ - [Design](docs/explanation/design.md)
79
+ - [Features & roadmap](docs/explanation/features.md)
80
+ - [ADRs](docs/adr/)
81
+
82
+ ______________________________________________________________________
83
+
84
+ MIT License
@@ -0,0 +1,31 @@
1
+ # pinky-tools
2
+
3
+ *Update date : 2026-05-31 12:10*
4
+
5
+ Local dev CLI for Snowflake data engineers — run stored procedures locally, generate README docs, and validate naming conventions.
6
+
7
+ Part of the [pinky-and-co](https://github.com/pinky-and-co) suite.
8
+
9
+ ## Install
10
+
11
+ ```bash
12
+ pip install pinky-tools
13
+ ```
14
+
15
+ ## Quick start
16
+
17
+ ```bash
18
+ pinky run resources/procedure/my_sp.py
19
+ pinky readme
20
+ pinky validate-name "financial__workday_to_altaix__compensation"
21
+ ```
22
+
23
+ ## Docs
24
+
25
+ - [Design](docs/explanation/design.md)
26
+ - [Features & roadmap](docs/explanation/features.md)
27
+ - [ADRs](docs/adr/)
28
+
29
+ ______________________________________________________________________
30
+
31
+ MIT License
@@ -0,0 +1,18 @@
1
+ # ADR-0001 — click over argparse
2
+ Status: accepted
3
+ Date: 2026-05-31
4
+
5
+ ## Context
6
+
7
+ The CLI commands migrated from legacy use `argparse`. `pinky-provider` had the
8
+ same choice and already decided on `click` (see pinky-provider ADR-0002).
9
+
10
+ ## Decision
11
+
12
+ Use `click` + `rich-click` for all CLI commands — same decision as `pinky-provider`.
13
+
14
+ ## Consequences
15
+
16
+ Consistent CLI experience across the suite. `rich-click` adds automatic Rich
17
+ formatting to help text with zero extra effort. Aligns with `pinky-provider`
18
+ so conventions and patterns are shared.
@@ -0,0 +1,25 @@
1
+ # ADR-0002 — i18n via env var from day one
2
+ Status: accepted
3
+ Date: 2026-05-31
4
+
5
+ ## Context
6
+
7
+ CLI output strings need to be consistent and localizable. The suite is open-source
8
+ and may be used in both English and French contexts.
9
+
10
+ ## Decision
11
+
12
+ All CLI-facing strings are defined as keyed templates in `i18n/en.py` and `i18n/fr.py`.
13
+ Locale is resolved in order: `locale` argument → `SNOWFLAKE_TOOLS_LOCALE` env var → `en`.
14
+ Unsupported locales fall back silently to `en`. Missing keys return the key itself — no crash.
15
+
16
+ ```python
17
+ t = get_translator() # uses env var or "en"
18
+ t = get_translator("fr") # force French
19
+ print(t("export.done", path="/tmp/out.xlsx"))
20
+ ```
21
+
22
+ ## Consequences
23
+
24
+ Zero hardcoded output strings in command implementations. Adding a new locale = adding
25
+ one file in `i18n/`. Applies to all commands, not just `pinky export`.
@@ -0,0 +1,21 @@
1
+ # ADR-0003 — `pinky export`: last-SELECT-wins execution model
2
+ Status: accepted
3
+ Date: 2026-05-31
4
+
5
+ ## Context
6
+
7
+ A `.sql` file passed to `pinky export` may contain multiple statements: session
8
+ variable SETs, intermediate CTEs, DML warm-ups, and a final SELECT to export.
9
+ Only one result is exported to xlsx.
10
+
11
+ ## Decision
12
+
13
+ The last statement in the file is the one exported. All preceding statements are
14
+ executed for side effects (SET, DML, intermediate SELECTs). If the last statement
15
+ is not a SELECT, the command fails immediately with a clear error message.
16
+
17
+ ## Consequences
18
+
19
+ Forces good SQL hygiene — the export query is always the last and most visible
20
+ thing in the file. No silent partial exports. Callers know exactly which result
21
+ they will get without reading the implementation.