applika-cli 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.
Files changed (34) hide show
  1. applika_cli-0.1.0/.gitignore +170 -0
  2. applika_cli-0.1.0/AGENTS.md +1 -0
  3. applika_cli-0.1.0/CLAUDE.md +123 -0
  4. applika_cli-0.1.0/Makefile +29 -0
  5. applika_cli-0.1.0/PKG-INFO +340 -0
  6. applika_cli-0.1.0/README.md +320 -0
  7. applika_cli-0.1.0/pyproject.toml +54 -0
  8. applika_cli-0.1.0/src/applika/__init__.py +0 -0
  9. applika_cli-0.1.0/src/applika/app.py +57 -0
  10. applika_cli-0.1.0/src/applika/commands/__init__.py +0 -0
  11. applika_cli-0.1.0/src/applika/commands/applications/__init__.py +25 -0
  12. applika_cli-0.1.0/src/applika/commands/applications/commands.py +405 -0
  13. applika_cli-0.1.0/src/applika/commands/applications/filter.py +73 -0
  14. applika_cli-0.1.0/src/applika/commands/applications/payloads.py +213 -0
  15. applika_cli-0.1.0/src/applika/commands/auth.py +93 -0
  16. applika_cli-0.1.0/src/applika/commands/skill.py +149 -0
  17. applika_cli-0.1.0/src/applika/config.py +27 -0
  18. applika_cli-0.1.0/src/applika/lib/__init__.py +0 -0
  19. applika_cli-0.1.0/src/applika/lib/api.py +168 -0
  20. applika_cli-0.1.0/src/applika/lib/loopback.py +85 -0
  21. applika_cli-0.1.0/src/applika/lib/session.py +59 -0
  22. applika_cli-0.1.0/src/applika/main.py +5 -0
  23. applika_cli-0.1.0/src/applika/schemas/__init__.py +0 -0
  24. applika_cli-0.1.0/src/applika/schemas/application.py +74 -0
  25. applika_cli-0.1.0/src/applika/schemas/enums.py +58 -0
  26. applika_cli-0.1.0/src/applika/skills/applika-cli/SKILL.md +243 -0
  27. applika_cli-0.1.0/src/applika/utils/__init__.py +0 -0
  28. applika_cli-0.1.0/src/applika/utils/dates.py +10 -0
  29. applika_cli-0.1.0/src/applika/utils/output.py +54 -0
  30. applika_cli-0.1.0/tests/conftest.py +89 -0
  31. applika_cli-0.1.0/tests/test_api.py +69 -0
  32. applika_cli-0.1.0/tests/test_commands.py +454 -0
  33. applika_cli-0.1.0/tests/test_session.py +28 -0
  34. applika_cli-0.1.0/uv.lock +385 -0
@@ -0,0 +1,170 @@
1
+ # Byte-compiled / optimized / DLL files
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+
6
+ # C extensions
7
+ *.so
8
+
9
+ # Distribution / packaging
10
+ .Python
11
+ build/
12
+ develop-eggs/
13
+ dist/
14
+ downloads/
15
+ eggs/
16
+ .eggs/
17
+ lib64/
18
+ parts/
19
+ sdist/
20
+ var/
21
+ wheels/
22
+ share/python-wheels/
23
+ *.egg-info/
24
+ .installed.cfg
25
+ *.egg
26
+ MANIFEST
27
+
28
+ # PyInstaller
29
+ # Usually these files are written by a python script from a template
30
+ # before PyInstaller builds the exe, so as to inject date/other infos into it.
31
+ *.manifest
32
+ *.spec
33
+
34
+ # Installer logs
35
+ pip-log.txt
36
+ pip-delete-this-directory.txt
37
+
38
+ # Unit test / coverage reports
39
+ htmlcov/
40
+ .tox/
41
+ .nox/
42
+ .coverage
43
+ .coverage.*
44
+ .cache
45
+ nosetests.xml
46
+ coverage.xml
47
+ *.cover
48
+ *.py,cover
49
+ .hypothesis/
50
+ .pytest_cache/
51
+ cover/
52
+
53
+ # Translations
54
+ *.mo
55
+ *.pot
56
+
57
+ # Django stuff:
58
+ *.log
59
+ local_settings.py
60
+ db.sqlite3
61
+ db.sqlite3-journal
62
+
63
+ # Flask stuff:
64
+ instance/
65
+ .webassets-cache
66
+
67
+ # vscode
68
+ settings.json
69
+ .vscode/
70
+
71
+ # Scrapy stuff:
72
+ .scrapy
73
+
74
+ # Sphinx documentation
75
+ docs/_build/
76
+
77
+ # PyBuilder
78
+ .pybuilder/
79
+ target/
80
+
81
+ # Jupyter Notebook
82
+ .ipynb_checkpoints
83
+
84
+ # IPython
85
+ profile_default/
86
+ ipython_config.py
87
+ /documents
88
+
89
+ # pyenv
90
+ # For a library or package, you might want to ignore these files since the code is
91
+ # intended to run in multiple environments; otherwise, check them in:
92
+ # .python-version
93
+
94
+ # pipenv
95
+ # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
96
+ # However, in case of collaboration, if having platform-specific dependencies or dependencies
97
+ # having no cross-platform support, pipenv may install dependencies that don't work, or not
98
+ # install all needed dependencies.
99
+ #Pipfile.lock
100
+
101
+ # poetry
102
+ # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
103
+ # This is especially recommended for binary packages to ensure reproducibility, and is more
104
+ # commonly ignored for libraries.
105
+ # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
106
+ #poetry.lock
107
+
108
+ # pdm
109
+ # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
110
+ #pdm.lock
111
+ # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
112
+ # in version control.
113
+ # https://pdm.fming.dev/#use-with-ide
114
+ .pdm.toml
115
+
116
+ # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
117
+ __pypackages__/
118
+
119
+ # Celery stuff
120
+ celerybeat-schedule
121
+ celerybeat.pid
122
+
123
+ # SageMath parsed files
124
+ *.sage.py
125
+
126
+ # Environments
127
+ .env
128
+ .venv
129
+ env/
130
+ venv/
131
+ ENV/
132
+ env.bak/
133
+ venv.bak/
134
+ .claude/settings.local.json
135
+
136
+ # Spyder project settings
137
+ .spyderproject
138
+ .spyproject
139
+
140
+ # Rope project settings
141
+ .ropeproject
142
+
143
+ # mkdocs documentation
144
+ /site
145
+
146
+ # mypy
147
+ .mypy_cache/
148
+ .dmypy.json
149
+ dmypy.json
150
+
151
+ # Pyre type checker
152
+ .pyre/
153
+
154
+ # pytype static type analyzer
155
+ .pytype/
156
+
157
+ # Cython debug symbols
158
+ cython_debug/
159
+
160
+ # PyCharm
161
+ # JetBrains specific template is maintained in a separate JetBrains.gitignore that can
162
+ # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
163
+ # and can be added to the global gitignore or merged into this file. For a more nuclear
164
+ # option (not recommended) you can uncomment the following to ignore the entire idea folder.
165
+ #.idea/
166
+ *.db
167
+
168
+ fake_ledger_db.json
169
+ *.pem
170
+ logs/
@@ -0,0 +1 @@
1
+ CLAUDE.md
@@ -0,0 +1,123 @@
1
+ # CLAUDE.md
2
+
3
+ This file provides guidance to Claude Code (claude.ai/code) when working with code in this CLI.
4
+
5
+ ## Keeping This File Up to Date
6
+
7
+ **CLAUDE.md is a living document. Update it when the CLI changes in ways that affect how you work here.**
8
+
9
+ Update when:
10
+ - A new command or sub-app is added
11
+ - The package structure changes
12
+ - A new development command is added or removed
13
+ - Packaging or install behavior changes
14
+ - A new required environment variable is introduced
15
+ - Validation, formatting, or test workflow changes
16
+
17
+ Do NOT add:
18
+ - Implementation details already visible in the code
19
+ - Ephemeral task notes or in-progress work
20
+ - Anything already obvious from the command help or tests
21
+
22
+ ---
23
+
24
+ ## Commands
25
+
26
+ ```bash
27
+ # Install the CLI globally from the local checkout
28
+ uv tool install --force .
29
+
30
+ # Install dev dependencies
31
+ uv sync
32
+
33
+ # Show CLI help
34
+ uv run python -m applika.main --help
35
+
36
+ # Run linter with auto-fix
37
+ make lint
38
+
39
+ # Run formatter
40
+ make format
41
+
42
+ # Run tests
43
+ make test
44
+
45
+ # Build wheel for PyPI
46
+ uv build
47
+ ```
48
+
49
+ ---
50
+
51
+ ## End-of-Task Validation
52
+
53
+ **Always finish a CLI task by running formatting, linting, and tests.**
54
+
55
+ Run these at the end of any CLI code change:
56
+
57
+ ```bash
58
+ make format
59
+ make lint
60
+ make test
61
+ ```
62
+
63
+ Do not skip this unless the user explicitly asks you not to run validation or the environment prevents it.
64
+
65
+ ---
66
+
67
+ ## Package Structure
68
+
69
+ All source code lives under `src/applika/` (importable as `applika`).
70
+
71
+ ```
72
+ src/applika/
73
+ ├── main.py # Entry point: def main() -> None
74
+ ├── app.py # Root Typer app + --api-base-url callback → AppConfig
75
+ ├── config.py # AppConfig dataclass + resolve_api_base_url
76
+ ├── skills/
77
+ │ └── applika-cli/ # Bundled SKILL.md (included in wheel, symlinked/copied by skill install)
78
+ ├── schemas/
79
+ │ ├── enums.py # StrEnum types: Currency, SalaryPeriod, ExperienceLevel,
80
+ │ │ # WorkMode, ApplicationMode, ModeFilter, StatusFilter, OutputFormat
81
+ │ └── application.py # Pydantic models: ApplicationCreate, ApplicationUpdate,
82
+ │ # ApplicationCompanyInput (vendored from backend DTOs)
83
+ ├── lib/
84
+ │ ├── api.py # ApiClient (httpx + cookie auth), ApiError, AuthError,
85
+ │ │ # require_session, create_session_from_exchange
86
+ │ ├── session.py # SessionData, SessionStore (~/.config/applika/session.json)
87
+ │ └── loopback.py # LoopbackLoginServer for OAuth callback
88
+ ├── utils/
89
+ │ ├── dates.py # parse_date, ensure_date_string
90
+ │ └── output.py # render_application_table, print_application_summary
91
+ └── commands/
92
+ ├── auth.py # login + logout + whoami Typer commands
93
+ ├── skill.py # skill install Typer sub-app
94
+ └── applications/
95
+ ├── __init__.py # applications_app Typer sub-app with default-to-list callback
96
+ ├── commands.py # list_applications, new_application, edit_application
97
+ ├── filter.py # filter_applications, resolve_platform_id
98
+ └── payloads.py # ApplicationArgs dataclass + build_application_payload
99
+ ```
100
+
101
+ ## Key Patterns
102
+
103
+ - **Global state**: `AppConfig` (dataclass with `api_base_url` + `store`) lives on `ctx.obj`, set by the root `@app.callback()` in `app.py`.
104
+ - **Auth required**: Commands call `require_session(config.store)` → raises `AuthError` if no session.
105
+ - **Payload validation**: `ApplicationCreate`/`ApplicationUpdate` Pydantic models validate inputs before API calls in `new_application` and `edit_application`.
106
+ - **ApplicationArgs**: Thin dataclass passed to `build_application_payload()` — decouples Typer command signatures from payload construction logic.
107
+ - **Schemas are vendored**: `schemas/enums.py` and `schemas/application.py` are standalone copies (no backend import). Keep in sync with `backend/app/core/enums.py` and `backend/app/application/dto/application.py` when the backend changes.
108
+
109
+ ## CLI Commands
110
+
111
+ | Command | Description |
112
+ |---|---|
113
+ | `applika login` | GitHub OAuth login (opens browser) |
114
+ | `applika logout` | Log out and clear session |
115
+ | `applika whoami` | Show the currently authenticated user |
116
+ | `applika applications list` | List applications (filterable) |
117
+ | `applika applications new` | Create a new application |
118
+ | `applika applications edit <id>` | Edit an existing application |
119
+ | `applika skill install` | Install the AI skill (symlink/copy) for Claude, Gemini, or Codex |
120
+
121
+ ## Environment Variables
122
+
123
+ - `APPLIKA_API_BASE_URL`: Override the default API URL (`https://applika.dev/api`)
@@ -0,0 +1,29 @@
1
+ install:
2
+ uv tool install --force .
3
+
4
+ install-dev:
5
+ uv sync
6
+
7
+ install-linux:
8
+ $(MAKE) install
9
+
10
+ install-macos:
11
+ $(MAKE) install
12
+
13
+ install-windows:
14
+ $(MAKE) install
15
+
16
+ test:
17
+ uv run --no-sync pytest tests -q
18
+
19
+ lint:
20
+ uv run --no-sync ruff check . --fix
21
+
22
+ format:
23
+ uv run --no-sync ruff format .
24
+
25
+ help:
26
+ uv run --no-sync applika --help
27
+
28
+ uninstall:
29
+ uv tool uninstall applika-cli
@@ -0,0 +1,340 @@
1
+ Metadata-Version: 2.4
2
+ Name: applika-cli
3
+ Version: 0.1.0
4
+ Summary: Job application tracker CLI for Applika.dev
5
+ Project-URL: Homepage, https://applika.dev
6
+ Project-URL: Repository, https://github.com/ApplikaDev/applika
7
+ Author-email: Luis Eduardo Soares <luisedu.soares@outlook.com.br>, David Alecrim <dsalecrim@outlook.com>
8
+ License: MIT
9
+ Keywords: applika,cli,job-tracker
10
+ Classifier: Environment :: Console
11
+ Classifier: License :: OSI Approved :: MIT License
12
+ Classifier: Programming Language :: Python :: 3
13
+ Classifier: Programming Language :: Python :: 3.12
14
+ Classifier: Topic :: Utilities
15
+ Requires-Python: >=3.12
16
+ Requires-Dist: httpx>=0.28.1
17
+ Requires-Dist: pydantic>=2.0
18
+ Requires-Dist: typer>=0.15.0
19
+ Description-Content-Type: text/markdown
20
+
21
+ # applika-cli
22
+
23
+ Command-line interface for [Applika.dev](https://applika.dev) — a structured job application tracker designed for active job seekers who want full control over their data without relying on a browser.
24
+
25
+ Track every application you send, filter and review your pipeline from the terminal, and keep your AI coding assistant (Claude Code, Gemini, Codex) aware of the CLI through a bundled skill file.
26
+
27
+ ## Requirements
28
+
29
+ - Python 3.12+
30
+ - [uv](https://docs.astral.sh/uv/) (recommended) or pip
31
+
32
+ ## Installation
33
+
34
+ ```bash
35
+ uv tool install applika-cli
36
+ ```
37
+
38
+ This installs the `applika` binary globally via uv's tool environment. Verify:
39
+
40
+ ```bash
41
+ applika --help
42
+ ```
43
+
44
+ To install from a local source checkout (useful during development):
45
+
46
+ ```bash
47
+ uv tool install --force .
48
+ ```
49
+
50
+ ---
51
+
52
+ ## Authentication
53
+
54
+ The CLI authenticates via GitHub OAuth. On `applika login`, your browser opens to GitHub's authorization page. Once you authorize, the callback is captured locally on a loopback port and the session (access + refresh tokens) is saved to `~/.config/applika/session.json`. The session refreshes automatically on expiry — you only need to log in once per device.
55
+
56
+ ```bash
57
+ # Open browser and complete GitHub OAuth
58
+ applika login
59
+
60
+ # Verify the active session without making any other API call
61
+ applika whoami
62
+ # → Logged in as: username=luissoares name=Luis Soares email=luis@example.com
63
+
64
+ # Revoke the session server-side and clear local storage
65
+ applika logout
66
+ ```
67
+
68
+ > `applika whoami` is the recommended pre-flight check. Run it before any other command, especially in scripts or AI-assisted workflows, to confirm a valid session exists before hitting the API.
69
+
70
+ ---
71
+
72
+ ## Commands
73
+
74
+ ### `applika applications list`
75
+
76
+ Lists all job applications in the current cycle, sorted by date descending. Supports rich filtering and both human-readable table output and machine-readable JSON.
77
+
78
+ ```bash
79
+ # Default: all applications as a table
80
+ applika applications list
81
+
82
+ # Narrow down by any combination of filters
83
+ applika applications list \
84
+ --search "stripe" \
85
+ --mode active \
86
+ --status active \
87
+ --platform LinkedIn \
88
+ --from 2026-01-01 \
89
+ --to 2026-06-30
90
+
91
+ # JSON output — useful for piping into jq or scripts
92
+ applika applications list --output-format json
93
+
94
+ # Scope to a specific job-search cycle by UUID
95
+ applika applications list --cycle-id <uuid>
96
+ ```
97
+
98
+ **Filter reference:**
99
+
100
+ | Flag | Values | Default | Description |
101
+ |---|---|---|---|
102
+ | `--search TEXT` | any string | — | Case-insensitive substring match on company name or role title |
103
+ | `--mode` | `active` · `passive` · `all` | `all` | `active` = you applied; `passive` = recruiter reached out |
104
+ | `--status` | `active` · `finalized` · `all` | `all` | `finalized` applications have a recorded outcome |
105
+ | `--platform TEXT` | e.g. `LinkedIn` | — | Exact match on platform name |
106
+ | `--from YYYY-MM-DD` | date | — | Include applications from this date (inclusive) |
107
+ | `--to YYYY-MM-DD` | date | — | Include applications up to this date (inclusive) |
108
+ | `--output-format` | `table` · `json` | `table` | `json` returns the raw API response as a formatted array |
109
+ | `--cycle-id TEXT` | UUID | — | Filter to a specific job-search cycle |
110
+
111
+ ---
112
+
113
+ ### `applika applications new`
114
+
115
+ Records a new job application. The CLI validates the payload locally with Pydantic before calling the API, so you get clear field-level error messages without a round-trip.
116
+
117
+ ```bash
118
+ # Minimal — required fields only
119
+ applika applications new \
120
+ --company "Stripe" \
121
+ --role "Backend Engineer" \
122
+ --platform "LinkedIn" \
123
+ --mode active \
124
+ --date 2026-05-10
125
+
126
+ # With salary info (currency and period are always required together with any salary field)
127
+ applika applications new \
128
+ --company "Cloudflare" \
129
+ --role "Systems Engineer" \
130
+ --platform "Email" \
131
+ --mode passive \
132
+ --date 2026-05-10 \
133
+ --observation "Recruiter cold-messaged. Interesting stack." \
134
+ --country "Brazil" \
135
+ --work-mode remote \
136
+ --salary-min 18000 \
137
+ --salary-max 24000 \
138
+ --currency BRL \
139
+ --salary-period monthly
140
+ ```
141
+
142
+ **Required flags:**
143
+
144
+ | Flag | Description |
145
+ |---|---|
146
+ | `--company TEXT` | Company name. Matched against known companies; a new record is created if not found. |
147
+ | `--role TEXT` | Job title or role description |
148
+ | `--platform TEXT` | Platform where you found or were contacted about the role (e.g. `LinkedIn`, `Indeed`, `Email`) |
149
+ | `--mode` | `active` — you applied proactively · `passive` — inbound from a recruiter |
150
+ | `--date YYYY-MM-DD` | Date the application was submitted or the first contact occurred |
151
+
152
+ **Optional flags:**
153
+
154
+ | Flag | Description |
155
+ |---|---|
156
+ | `--company-url URL` | Company website |
157
+ | `--job-url URL` | Direct link to the job posting |
158
+ | `--observation TEXT` | Free-form notes about the role, process, or company |
159
+ | `--country TEXT` | Country where the role is based |
160
+ | `--work-mode` | `remote` · `hybrid` · `on_site` |
161
+ | `--experience-level` | `intern` · `junior` · `mid_level` · `senior` · `staff` · `lead` · `principal` · `specialist` |
162
+ | `--expected-salary FLOAT` | The salary you expect or asked for |
163
+ | `--salary-min FLOAT` | Lower bound of a posted salary range |
164
+ | `--salary-max FLOAT` | Upper bound of a posted salary range |
165
+ | `--currency` | `USD` · `BRL` · `EUR` · `GBP` · `CAD` · `AUD` · `JPY` · `CHF` · `INR` |
166
+ | `--salary-period` | `hourly` · `monthly` · `annual` |
167
+
168
+ > **Salary rule:** if any salary amount is provided (`--expected-salary`, `--salary-min`, or `--salary-max`), both `--currency` and `--salary-period` become required. The CLI enforces this before making any API call.
169
+
170
+ ---
171
+
172
+ ### `applika applications edit <id>`
173
+
174
+ Updates an existing application. Only the flags you pass are changed — everything else keeps its current value. This makes partial updates safe: you can update just the role name or add salary info without touching anything else.
175
+
176
+ ```bash
177
+ # Update the role title
178
+ applika applications edit 42 --role "Staff Engineer"
179
+
180
+ # Add salary info that wasn't captured at application time
181
+ applika applications edit 42 \
182
+ --expected-salary 180000 \
183
+ --currency USD \
184
+ --salary-period annual
185
+
186
+ # Clear fields you no longer want to track
187
+ applika applications edit 42 \
188
+ --clear-job-url \
189
+ --clear-observation \
190
+ --clear-country \
191
+ --clear-salary
192
+ ```
193
+
194
+ Find the application `id` with:
195
+
196
+ ```bash
197
+ applika applications list --search "company name" --output-format json
198
+ ```
199
+
200
+ **Clear flags** — set a field back to null without affecting others:
201
+
202
+ | Flag | Clears |
203
+ |---|---|
204
+ | `--clear-job-url` | Job posting URL |
205
+ | `--clear-observation` | Notes |
206
+ | `--clear-country` | Country |
207
+ | `--clear-salary` | All salary fields (`expected_salary`, `salary_min`, `salary_max`, `currency`, `salary_period`) |
208
+
209
+ > Finalized applications (those with a recorded outcome) are read-only and cannot be edited. The CLI checks this before sending the request.
210
+
211
+ ---
212
+
213
+ ### `applika skill`
214
+
215
+ Installs the bundled AI skill into your assistant's skills directory. The skill teaches Claude Code, Gemini, or Codex how to use this CLI — what commands exist, how authentication works, required vs optional flags, and common workflows.
216
+
217
+ The skill file is shipped inside the installed package (`applika/skills/applika-cli/SKILL.md`) so it stays in sync with the CLI version you have installed. By default the command creates a symlink so updates are reflected automatically; it falls back to a file copy if symlink creation fails (e.g. Windows without Developer Mode enabled).
218
+
219
+ ```bash
220
+ # Interactive — choose which tool(s) to install for
221
+ applika skill
222
+ # → 1. Claude (~/.claude/skills/applika-cli)
223
+ # → 2. Gemini (~/.gemini/skills/applika-cli)
224
+ # → 3. Codex (~/.codex/skills/applika-cli)
225
+ # → 4. All of the above
226
+
227
+ # Install to the current project's .claude/skills/ (file copy, no prompt)
228
+ # Useful when you want the skill scoped to a single repo
229
+ applika skill --local
230
+
231
+ # Install to any arbitrary directory (file copy, no prompt)
232
+ applika skill --dir /path/to/skills
233
+
234
+ # Preview what would be installed without touching the filesystem
235
+ applika skill --dry-run
236
+
237
+ # Overwrite an existing installation
238
+ applika skill --force
239
+ ```
240
+
241
+ Once installed, the AI assistant automatically loads the skill context in every session and knows how to:
242
+ - check authentication with `applika whoami` before running commands
243
+ - construct valid `new` and `edit` payloads
244
+ - apply the correct filters on `list`
245
+ - recover from auth errors by prompting for `applika login`
246
+
247
+ ---
248
+
249
+ ## Global options
250
+
251
+ These flags apply to every command and are passed before the subcommand name:
252
+
253
+ ```bash
254
+ applika --api-base-url https://staging.applika.dev/api applications list
255
+ ```
256
+
257
+ | Flag | Default | Env variable |
258
+ |---|---|---|
259
+ | `--api-base-url TEXT` | `https://applika.dev/api` | `APPLIKA_API_BASE_URL` |
260
+
261
+ ---
262
+
263
+ ## Package structure
264
+
265
+ ```
266
+ cli/
267
+ ├── pyproject.toml # Build config, dependencies, entry point
268
+ ├── Makefile # Shortcuts: test, lint, format
269
+ ├── README.md
270
+ ├── CLAUDE.md # Guidance for AI assistants working in this repo
271
+ └── src/
272
+ └── applika/ # Importable package (entry: applika.main:main)
273
+ ├── main.py # Entry point — calls app()
274
+ ├── app.py # Root Typer app, --api-base-url callback, AppConfig wiring
275
+ ├── config.py # AppConfig dataclass + resolve_api_base_url()
276
+
277
+ ├── skills/
278
+ │ └── applika-cli/
279
+ │ └── SKILL.md # Bundled AI skill — installed via `applika skill`
280
+
281
+ ├── schemas/ # Vendored Pydantic models (no backend import)
282
+ │ ├── enums.py # StrEnum types: Currency, SalaryPeriod, WorkMode, etc.
283
+ │ └── application.py # ApplicationCreate, ApplicationUpdate with validators
284
+
285
+ ├── lib/ # Infrastructure — no Typer dependency
286
+ │ ├── api.py # ApiClient (httpx + cookie auth), ApiError, AuthError
287
+ │ ├── session.py # SessionData, SessionStore (~/.config/applika/session.json)
288
+ │ └── loopback.py # LoopbackLoginServer for OAuth browser callback
289
+
290
+ ├── utils/ # Pure helpers — no httpx, no Typer
291
+ │ ├── dates.py # parse_date, ensure_date_string
292
+ │ └── output.py # render_application_table, print_application_summary
293
+
294
+ └── commands/
295
+ ├── auth.py # login, logout, whoami commands
296
+ ├── skill.py # skill command — installs the AI skill
297
+ └── applications/
298
+ ├── __init__.py # applications_app Typer sub-app, default-to-list callback
299
+ ├── commands.py # list_applications, new_application, edit_application
300
+ ├── filter.py # filter_applications, resolve_platform_id
301
+ └── payloads.py # ApplicationArgs dataclass, build_application_payload()
302
+ ```
303
+
304
+ **Layer rules:**
305
+ - `lib/` — HTTP and session logic only. No Typer, no output formatting.
306
+ - `utils/` — Pure functions. No side effects, no I/O beyond what the function signature implies.
307
+ - `schemas/` — Vendored copies of backend DTOs. Keep in sync manually with `backend/app/application/dto/application.py` and `backend/app/core/enums.py` when the backend changes.
308
+ - `commands/` — Typer command functions only. Delegates to `lib/` and `utils/`.
309
+
310
+ ---
311
+
312
+ ## Development
313
+
314
+ ```bash
315
+ # Install project + dev dependencies into a local virtualenv
316
+ uv sync
317
+
318
+ # Run the test suite
319
+ make test
320
+
321
+ # Auto-fix lint issues
322
+ make lint
323
+
324
+ # Apply code formatter
325
+ make format
326
+
327
+ # Build a wheel for distribution
328
+ uv build
329
+
330
+ # Install the locally built CLI globally for manual testing
331
+ uv tool install --force .
332
+ ```
333
+
334
+ Tests live in `tests/` and use `typer.testing.CliRunner` with fake `ApiClient` and `SessionStore` implementations defined in `tests/conftest.py`. No real network calls are made.
335
+
336
+ ---
337
+
338
+ ## License
339
+
340
+ MIT