logwise-cli 0.2.0__py3-none-any.whl

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.
@@ -0,0 +1,438 @@
1
+ Metadata-Version: 2.4
2
+ Name: logwise-cli
3
+ Version: 0.2.0
4
+ Summary: Intelligent Log Analyzer CLI Tool
5
+ Author-email: Dhanush Ballanki <dhanushballanki14@gmail.com>
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/dhanush-ballanki/logwise
8
+ Project-URL: Repository, https://github.com/dhanush-ballanki/logwise
9
+ Project-URL: Issues, https://github.com/dhanush-ballanki/logwise/issues
10
+ Keywords: cli,logs,debugging,devtools,ai
11
+ Classifier: Development Status :: 3 - Alpha
12
+ Classifier: Environment :: Console
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: Operating System :: OS Independent
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.10
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Topic :: Software Development :: Debuggers
20
+ Classifier: Topic :: System :: Logging
21
+ Classifier: Typing :: Typed
22
+ Requires-Python: >=3.10
23
+ Description-Content-Type: text/markdown
24
+ License-File: LICENSE
25
+ Requires-Dist: typer==0.21.1
26
+ Requires-Dist: rich==15.0.0
27
+ Dynamic: license-file
28
+
29
+ # LogWise
30
+
31
+ LogWise is a lightweight Python CLI that runs shell commands, saves failures as JSON logs, detects common failure patterns with built-in rules, and optionally asks an AI provider to explain the error and suggest fixes.
32
+
33
+ ## Why LogWise?
34
+
35
+ When a command fails, the usual workflow is: rerun it, squint at the output, guess the root cause. LogWise shortens that loop by:
36
+
37
+ - running commands through a simple CLI,
38
+ - capturing stdout, stderr, exit codes, and timestamps,
39
+ - persisting every failure as a JSON log for later inspection,
40
+ - matching failures against built-in rules (missing files, permissions, …),
41
+ - suggesting practical fixes immediately,
42
+ - optionally consulting an AI provider — Gemini, OpenAI, DeepSeek, Groq, OpenRouter, or a local Ollama — through a single SDK-free HTTP layer.
43
+
44
+ ## Features
45
+
46
+ - `run` — execute a command; print stdout on success, log + analyze on failure
47
+ - `analyze` — re-analyze any saved log file
48
+ - `list` — list saved logs as a table (file, command, exit code)
49
+ - `prune` — delete old logs by count (`--keep`) and/or age (`--older-than`), asks first unless `--yes`
50
+ - Interactive retry (with command editing) / AI-offer / rerun prompts on terminals (off when piped; `--no-prompt` / `LOGWISE_NO_PROMPT`)
51
+ - Rule engine with extensible `ERROR_RULES`
52
+ - Multi-provider AI analysis over OpenAI-compatible APIs (stdlib `urllib` only, zero vendor SDKs), with spinner + per-provider accent colors
53
+ - Rich terminal output with byte-identical plain fallback for pipes (`--no-color`, `NO_COLOR`, `LOGWISE_NO_COLOR`)
54
+ - `uv`-managed project: locked deps, reproducible builds
55
+ - stdlib `unittest` suite (`tests/`), Ruff lint + format, CI workflow
56
+
57
+ ## Requirements
58
+
59
+ - Python 3.10+ (`.python-version` pins 3.12)
60
+ - [`uv`](https://docs.astral.sh/uv/) for install/build (recommended)
61
+ - A terminal environment
62
+ - Optional: an API key for your AI provider (see [AI analysis](#ai-analysis)); `ollama` needs no key
63
+
64
+ ## Installation
65
+
66
+ ```bash
67
+ git clone https://github.com/dhanush-ballanki/logwise.git
68
+ cd logwise
69
+ uv sync # create .venv, install locked deps + editable logwise
70
+ ```
71
+
72
+ Run via `uv run`, or activate the venv (`.venv\Scripts\activate` on Windows, `source .venv/bin/activate` on Unix) and use `logwise` directly:
73
+
74
+ ```bash
75
+ uv run logwise run "python script.py"
76
+ ```
77
+
78
+ Build distributables (wheel + sdist into `dist/`):
79
+
80
+ ```bash
81
+ uv build
82
+ ```
83
+
84
+ Prefer plain pip? `pyproject.toml` is standards-based, so this works too:
85
+
86
+ ```bash
87
+ python -m venv .venv
88
+ pip install -e .
89
+ ```
90
+
91
+ ## Usage
92
+
93
+ ### Run a command
94
+
95
+ ```bash
96
+ logwise run "python script.py"
97
+ ```
98
+
99
+ Success prints stdout only. Failure saves `logs/log_<timestamp>.json` and prints the analysis.
100
+
101
+ ```bash
102
+ logwise run "python script.py" --ai --provider openai --model gpt-4o-mini
103
+ ```
104
+
105
+ ### Analyze an existing log
106
+
107
+ ```bash
108
+ logwise analyze log_2025-04-06T10-30-00.json
109
+ logwise analyze log_2025-04-06T10-30-00.json --ai --provider groq
110
+ ```
111
+
112
+ ### List captured logs
113
+
114
+ ```bash
115
+ logwise list
116
+ ```
117
+
118
+ ### Prune old logs
119
+
120
+ ```bash
121
+ logwise prune --keep 50 # keep newest 50, preview + confirm
122
+ logwise prune --keep 20 --older-than 30 --yes # ...older than 30 days, no prompt
123
+ ```
124
+
125
+ ### Example
126
+
127
+ ```bash
128
+ logwise run "ls /missing/path"
129
+ ```
130
+
131
+ ```text
132
+ Error occurred (exit code: 2)
133
+ Stderr captured:
134
+ ls: cannot access '/missing/path': No such file or directory
135
+
136
+ Analysis:
137
+ Found 2 rule-based issue(s).
138
+ - Reason: Command failed with non-zero exit code. (Possible reasons: Invalid arguments, missing dependencies, or runtime errors. Check stderr for details.)
139
+ Steps to fix: Verify command syntax, install missing packages, or debug the script.
140
+ - Reason: File or directory not found. (Path issue.)
141
+ Steps to fix: Check if the file exists (ls), correct the path, or create the missing item.
142
+ ```
143
+
144
+ ## Output & colors
145
+
146
+ On a real terminal, failures render with Rich: a red `❌ Error` header, stderr in a red panel, yellow `⚠` issue rows with green `→` fixes, and AI advice as a Markdown panel captioned with `provider / model` in that provider's accent color (blue Gemini, green OpenAI, violet DeepSeek, orange Groq, cyan OpenRouter, grey Ollama). `list` renders a table (file, command, exit code).
147
+
148
+ Plain text is automatic when output is piped or redirected, and can be forced with `--no-color`, `NO_COLOR=1`, or `LOGWISE_NO_COLOR=1`. Successful command stdout is never styled — it stays byte-identical so pipes and scripts keep working.
149
+
150
+ On terminals, `run` offers to retry a failed command — with a chance to edit it first (typos welcome), looping until it succeeds or you decline — and then offers AI analysis if you didn't pass `--ai`. `analyze` offers to re-run (and edit) the logged command. Prompts never appear when piped; `--no-prompt` / `LOGWISE_NO_PROMPT=1` disables them.
151
+
152
+ ## Architecture
153
+
154
+ ### System overview
155
+
156
+ ```mermaid
157
+ flowchart LR
158
+ subgraph CLI["CLI (main.py)"]
159
+ RUN["run"]
160
+ ANALYZE["analyze"]
161
+ LIST["list"]
162
+ end
163
+
164
+ subgraph CORE["Core"]
165
+ CAP["capture.py\ncapture_and_run()"]
166
+ ANZ["analyze.py\nanalyze_log_in_memory()"]
167
+ RULES["rules.py\napply_rules()"]
168
+ end
169
+
170
+ subgraph AILAYER["AI layer (SDK-free)"]
171
+ AI["ai.py\nai_analyze_err()"]
172
+ PROV["providers.py\nPROVIDERS table"]
173
+ end
174
+
175
+ STORE[("logs/\nlog_<iso>.json")]
176
+
177
+ RUN --> CAP
178
+ ANALYZE --> ANZ
179
+ LIST --> STORE
180
+ CAP --> STORE
181
+ CAP --> ANZ
182
+ ANZ --> RULES
183
+ ANZ --> AI
184
+ AI --> PROV
185
+ AI --> EXT[("AI APIs\nOpenAI-compatible\n/chat/completions")]
186
+ ```
187
+
188
+ **Module responsibilities:**
189
+
190
+ | Module | Role |
191
+ | --- | --- |
192
+ | `main.py` | Typer `app` with `run` / `analyze` / `list` commands; owns `--ai`, `--provider`, `--model` flags |
193
+ | `capture.py` | Runs the command via `subprocess.run(shell=True)`; writes the JSON log on failure; prints the analysis |
194
+ | `analyze.py` | `analyze_log_in_memory()` — the analysis orchestrator; `analyze_log()` loads a file then delegates; `list_logs()` lists `logs/` |
195
+ | `rules.py` | `ERROR_RULES` list + `apply_rules()`; new rules are plain dicts |
196
+ | `ai.py` | One `POST {base_url}/chat/completions` call via stdlib `urllib`; tolerant reason/fixes parsing |
197
+ | `providers.py` | `PROVIDERS` table (`base_url`, `key_env`, `default_model`) + `resolve_*()` helpers; adding a vendor is data-only |
198
+
199
+ `src/logwise/` has no `__init__.py` (namespace package).
200
+
201
+ ### `logwise run` lifecycle
202
+
203
+ ```mermaid
204
+ sequenceDiagram
205
+ participant U as User
206
+ participant CLI as main.py run
207
+ participant CAP as capture_and_run
208
+ participant FS as logs/
209
+ participant ANZ as analyze_log_in_memory
210
+
211
+ U->>CLI: logwise run "cmd" [--ai]
212
+ CLI->>CAP: capture_and_run(cmd, use_ai, provider, model)
213
+ CAP->>CAP: subprocess.run(shell=True)
214
+ alt exit 0 and empty stderr
215
+ CAP->>U: print(stdout)
216
+ else failure
217
+ CAP->>FS: write log_<iso>.json
218
+ CAP->>ANZ: analyze_log_in_memory(entry, ...)
219
+ ANZ->>U: via CAP: summary + issues + ai_analysis
220
+ end
221
+ ```
222
+
223
+ > Quirk: `returncode == 0` **with non-empty stderr** counts as failure and writes a log.
224
+
225
+ ### Analysis decision flow
226
+
227
+ ```mermaid
228
+ flowchart TD
229
+ START(["analyze_log_in_memory(log, use_ai)"]) --> ISAI{use_ai?}
230
+ ISAI -- yes --> AIONLY["ai_analyze_err()\n(rules skipped)"]
231
+ AIONLY --> S1["summary = 'AI-based analysis requested.'\nor 'AI analysis failed: ...'"]
232
+ ISAI -- no --> RULES["apply_rules()"]
233
+ RULES --> HIT{issues?}
234
+ HIT -- yes --> S2["summary = 'Found N rule-based issue(s).'"]
235
+ HIT -- no --> FB["ai_analyze_err() fallback"]
236
+ FB --> S3["summary = 'No rule-based issues. AI fallback used.'\nor '... AI fallback failed: ...'"]
237
+ ```
238
+
239
+ `--ai` is **AI-only** (rules skipped). Default mode runs rules first and calls AI only when zero rules match. AI errors are surfaced in `summary` — never silent, never a traceback.
240
+
241
+ ### AI layer: one HTTP path, N providers
242
+
243
+ ```mermaid
244
+ flowchart LR
245
+ AI["ai_analyze_err()\nresolves provider/model/key"] --> HTTP["_post_chat_completions()\nurllib POST\n{base_url}/chat/completions"]
246
+ HTTP --> GEM["Gemini\n.../v1beta/openai"]
247
+ HTTP --> OAI["OpenAI"]
248
+ HTTP --> DS["DeepSeek"]
249
+ HTTP --> GR["Groq"]
250
+ HTTP --> OR["OpenRouter"]
251
+ HTTP --> OL["Ollama\nlocalhost:11434"]
252
+ ```
253
+
254
+ Request shape: `{model, messages: [{role: user, content: prompt}], temperature: 0.7, max_tokens: 700}`, with `Authorization: Bearer <key>` when the provider needs one. The response text is split into `reason` / `fixes` on the `step-by-step` marker (case-insensitive, tolerant of missing headers).
255
+
256
+ ### Resolution precedence
257
+
258
+ ```mermaid
259
+ flowchart LR
260
+ F["CLI flag\n--provider / --model"] --> E["Env\nLOGWISE_*"] --> D["Provider default"]
261
+ ```
262
+
263
+ | Setting | Flag | Env | Default |
264
+ | --- | --- | --- | --- |
265
+ | Provider | `--provider` | `LOGWISE_PROVIDER` | `gemini` |
266
+ | Model | `--model` | `LOGWISE_MODEL` | provider's `default_model` |
267
+ | Base URL | — | `LOGWISE_BASE_URL` | provider's `base_url` |
268
+ | API key | — | `LOGWISE_API_KEY` then provider key env | error unless keyless/localhost |
269
+
270
+ ## Log format
271
+
272
+ Each failure is stored as `logs/log_<ISO-timestamp>.json` (`LOG_DIR` is `src/logwise/../../logs`, i.e. repo-root `logs/`, not configurable):
273
+
274
+ ```json
275
+ {
276
+ "command": "ls /missing/path",
277
+ "start_time": "2026-09-20T06:49:37.422836",
278
+ "end_time": "2026-09-20T06:49:37.435101",
279
+ "stdout": "",
280
+ "stderr": "ls: cannot access '/missing/path': No such file or directory",
281
+ "exit_code": 2
282
+ }
283
+ ```
284
+
285
+ And `analyze_log_in_memory()` returns:
286
+
287
+ ```python
288
+ {
289
+ "log": {...}, # the entry above
290
+ "issues": [...], # rule hits (default mode only)
291
+ "summary": "Found 2 rule-based issue(s).",
292
+ "ai_analysis": { # only on AI success
293
+ "reason": "...", "fixes": "...",
294
+ "provider": "groq", "model": "llama-3.3-70b-versatile",
295
+ },
296
+ }
297
+ ```
298
+
299
+ ## Rule-based detection
300
+
301
+ `src/logwise/rules.py` currently ships three rules:
302
+
303
+ - `non_zero_exit` — exit code ≠ 0
304
+ - `permission_denied` — `permission denied` in stderr
305
+ - `file_not_found` — `no such file or directory` in stderr
306
+
307
+ Add a rule as a dict — no framework, no registration calls:
308
+
309
+ ```python
310
+ {
311
+ 'id': 'module_not_found',
312
+ 'condition': lambda log: 'modulenotfounderror' in log['stderr'].lower().replace(' ', ''),
313
+ 'description': 'Python module not found.',
314
+ 'root_cause': 'Missing dependency.',
315
+ 'fixes': 'Install it with pip (pip install <package>).'
316
+ }
317
+ ```
318
+
319
+ ## AI analysis
320
+
321
+ When `--ai` is used, LogWise sends the command, exit code, and stderr to the selected provider and returns a concise reason plus fix steps.
322
+
323
+ Available providers (`--provider`, or `LOGWISE_PROVIDER` env, default `gemini`):
324
+
325
+ | Provider | Key env | Default model |
326
+ | --- | --- | --- |
327
+ | `gemini` | `GEMINI_API_KEY` | `gemini-2.0-flash` |
328
+ | `openai` | `OPENAI_API_KEY` | `gpt-4o-mini` |
329
+ | `deepseek` | `DEEPSEEK_API_KEY` | `deepseek-chat` |
330
+ | `groq` | `GROQ_API_KEY` | `llama-3.3-70b-versatile` |
331
+ | `openrouter` | `OPENROUTER_API_KEY` | `openai/gpt-4o-mini` |
332
+ | `ollama` | none (local) | `llama3.1` |
333
+
334
+ ```bash
335
+ export GEMINI_API_KEY="your_key_here" # or OPENAI_API_KEY / GROQ_API_KEY / ...
336
+ logwise run "python script.py" --ai
337
+ logwise run "python script.py" --ai --provider openai --model gpt-4o-mini
338
+ logwise analyze log_....json --ai --provider groq
339
+ ollama serve & logwise run "python script.py" --ai --provider ollama
340
+ ```
341
+
342
+ Generic overrides for any OpenAI-compatible server: `LOGWISE_API_KEY`,
343
+ `LOGWISE_MODEL`, `LOGWISE_BASE_URL`. All of these — plus the provider keys —
344
+ can live in `.env` instead of exports (see [Installation](#installation)).
345
+
346
+ ## Configuration reference
347
+
348
+ | Env var | Purpose | Default |
349
+ | --- | --- | --- |
350
+ | `LOGWISE_PROVIDER` | AI provider name | `gemini` |
351
+ | `LOGWISE_MODEL` | Model override | provider default |
352
+ | `LOGWISE_BASE_URL` | Custom OpenAI-compatible endpoint | provider default |
353
+ | `LOGWISE_API_KEY` | Generic key (beats provider-specific env) | — |
354
+ | `GEMINI_API_KEY` / `OPENAI_API_KEY` / `DEEPSEEK_API_KEY` / `GROQ_API_KEY` / `OPENROUTER_API_KEY` | Provider keys | — |
355
+ | `LOGWISE_ENV_FILE` | Explicit `.env` path (skips upward search) | nearest `.env` from cwd upward |
356
+ | `LOGWISE_LOG_DIR` | Where failure logs are stored | `logs/` under cwd |
357
+ | `LOGWISE_NO_COLOR` / `NO_COLOR` | Force plain-text output | color when attached to a terminal |
358
+
359
+ Flags beat env vars: `--provider` > `LOGWISE_PROVIDER`, `--model` > `LOGWISE_MODEL`.
360
+ File beats nothing: exported variables always win over `.env` values.
361
+
362
+ ## Project structure
363
+
364
+ ```text
365
+ logwise/
366
+ ├── .github/workflows/ # CI: ruff + unittest + build
367
+ ├── AGENTS.md
368
+ ├── LICENSE
369
+ ├── README.md
370
+ ├── pyproject.toml # deps + build config (setuptools, src layout)
371
+ ├── uv.lock # locked deps (committed)
372
+ ├── .python-version # pins 3.12
373
+ ├── src/
374
+ │ └── logwise/
375
+ │ ├── ai.py # SDK-free chat-completions client
376
+ │ ├── analyze.py # analysis orchestrator + prune_logs()
377
+ │ ├── capture.py # subprocess runner + log writer
378
+ │ ├── display.py # Rich terminal rendering (only color-aware module)
379
+ │ ├── env.py # stdlib .env loader (no extra dependency)
380
+ │ ├── main.py # Typer CLI (run | analyze | list | prune)
381
+ │ ├── paths.py # runtime data dirs (cwd-based, install-safe)
382
+ │ ├── providers.py # provider registry
383
+ │ └── rules.py # ERROR_RULES
384
+ ├── tests/ # stdlib unittest suite (71 tests)
385
+ ├── logs/ # auto-created at startup (cwd-based; override with LOGWISE_LOG_DIR)
386
+ ├── dist/ # uv build output (git-ignored)
387
+ └── .venv/ # uv venv (git-ignored)
388
+ ```
389
+
390
+ ## Development
391
+
392
+ ```bash
393
+ uv sync --group dev # install dev tools (ruff)
394
+ uv run python -m unittest discover # 71 tests, stdlib only
395
+ uv run --group dev ruff check src tests
396
+ uv run --group dev ruff format --check src tests
397
+ uv run logwise run "ls /missing/path" # rules path
398
+ uv run logwise run "echo hi" # success path
399
+ uv run logwise list
400
+ uv run logwise prune --keep 50 --yes # rotate old logs
401
+ uv build # wheel + sdist into dist/
402
+ ```
403
+
404
+ CI (`.github/workflows/ci.yml`) runs ruff, the test suite, and `uv build` on every push to `main` and every PR. To exercise the AI layer without spending API calls, point `LOGWISE_BASE_URL` at any stub that answers `POST /chat/completions` with `{"choices": [{"message": {"content": "..."}}]}`.
405
+
406
+ ## Publishing to PyPI
407
+
408
+ The package is publish-ready: SPDX license, readme, classifiers, project URLs, `src` layout, and two runtime dependencies (`typer`, `rich`).
409
+
410
+ ```bash
411
+ uv build # wheel + sdist into dist/
412
+ uv publish # needs a PyPI token (or: twine upload dist/*)
413
+ ```
414
+
415
+ Checklist before the first upload:
416
+
417
+ 1. `uv build` succeeds with no warnings.
418
+ 2. Bump `version` in `pyproject.toml` for every subsequent release.
419
+ 3. Never commit `.env` (git-ignored) or `dist/` (git-ignored).
420
+ 4. `pip install logwise` in a fresh venv, then `logwise run "ls /missing"` from an unrelated directory — logs must land in that directory's `logs/`, never in `site-packages`.
421
+
422
+ ## Contributing
423
+
424
+ Contributions are welcome:
425
+
426
+ 1. Fork the repository
427
+ 2. Create a feature branch
428
+ 3. Make your changes (new rules go in `rules.py`, new vendors in `providers.py`)
429
+ 4. Sanity-check with `uv run logwise run` on a failing + passing command
430
+ 5. Submit a pull request
431
+
432
+ ## License
433
+
434
+ This project is licensed under the MIT License. See the `LICENSE` file for details.
435
+
436
+ ## Maintainer
437
+
438
+ Dhanush Ballanki
@@ -0,0 +1,15 @@
1
+ logwise/ai.py,sha256=QOlAzKdLrPIkdFxTHtFi_SOaQzLklLh7jMzviPcDi8U,6446
2
+ logwise/analyze.py,sha256=1QO8RwgvM4eEHc-x20nzCvrDxANuxL6U1Ka-SYEq-Hw,3985
3
+ logwise/capture.py,sha256=4UwV0MeHvGP9onyENgvp54zvo9tFyCrWJcesT2U1B6k,2319
4
+ logwise/display.py,sha256=PwqidJuIHobhdZO1UdmzA8Jt10rbbd1faapSrkP7G8E,7351
5
+ logwise/env.py,sha256=PJ7OWUVAmKcT2RbQ6fo3uEMcdjohgWMX3OBy4Hj9mQI,3383
6
+ logwise/main.py,sha256=dZ0l7aET5qQtbogS4vvfjnr4-YezGePlDLx8nf90RQY,6973
7
+ logwise/paths.py,sha256=LKM5zFPJ2arZZB6SG0-BOcbqE1eJ3ePuqOs5iTzI_to,1019
8
+ logwise/providers.py,sha256=2h4lUp8PxcpmqzwSIqzilXTSg4i2ApjiMNYLvVFPmwY,2936
9
+ logwise/rules.py,sha256=SzCCCXQLvj5V0Y5FqG304BKMuZqXquGcGiSnt4UN0Pk,1575
10
+ logwise_cli-0.2.0.dist-info/licenses/LICENSE,sha256=o6DqrM6VMkMLMsvQ47EdZ32_ztVRmMZszwCclIZ77Hc,1094
11
+ logwise_cli-0.2.0.dist-info/METADATA,sha256=EDDYghku_2FWKfN6jqRzYV5yggnC3WHd76ssyJp_H1w,17170
12
+ logwise_cli-0.2.0.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
13
+ logwise_cli-0.2.0.dist-info/entry_points.txt,sha256=RsKeLFVA5J2TgIEeFDObDJ-o-dbNOpfxKjyQnQ2CHZ0,45
14
+ logwise_cli-0.2.0.dist-info/top_level.txt,sha256=XlmjFkIO4qHQK7djRNT1cKmPRYmCd33u71-E2eT7VUw,8
15
+ logwise_cli-0.2.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (84.0.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ logwise = logwise.main:app
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 dhanush-ballanki
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 @@
1
+ logwise