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.
- logwise/ai.py +190 -0
- logwise/analyze.py +122 -0
- logwise/capture.py +64 -0
- logwise/display.py +221 -0
- logwise/env.py +98 -0
- logwise/main.py +206 -0
- logwise/paths.py +34 -0
- logwise/providers.py +83 -0
- logwise/rules.py +42 -0
- logwise_cli-0.2.0.dist-info/METADATA +438 -0
- logwise_cli-0.2.0.dist-info/RECORD +15 -0
- logwise_cli-0.2.0.dist-info/WHEEL +5 -0
- logwise_cli-0.2.0.dist-info/entry_points.txt +2 -0
- logwise_cli-0.2.0.dist-info/licenses/LICENSE +21 -0
- logwise_cli-0.2.0.dist-info/top_level.txt +1 -0
|
@@ -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,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
|