fixos 2.2.26__tar.gz → 2.2.28__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.
- {fixos-2.2.26 → fixos-2.2.28}/CHANGELOG.md +41 -0
- {fixos-2.2.26 → fixos-2.2.28}/PKG-INFO +6 -6
- {fixos-2.2.26 → fixos-2.2.28}/README.md +5 -5
- {fixos-2.2.26 → fixos-2.2.28}/docker/docker-compose.yml +25 -10
- fixos-2.2.28/docker/test-scenarios.sh +147 -0
- fixos-2.2.28/docker/validate-scenario.py +158 -0
- {fixos-2.2.26 → fixos-2.2.28}/fixos/__init__.py +1 -1
- {fixos-2.2.26 → fixos-2.2.28}/fixos/agent/session_core.py +34 -0
- {fixos-2.2.26 → fixos-2.2.28}/fixos/cli/fix_cmd.py +33 -16
- fixos-2.2.28/fixos/cli/output_formatter.py +158 -0
- {fixos-2.2.26 → fixos-2.2.28}/fixos/cli/report_cmd.py +13 -1
- {fixos-2.2.26 → fixos-2.2.28}/fixos/cli/scan_cmd.py +92 -38
- {fixos-2.2.26 → fixos-2.2.28}/fixos/cli/shared.py +2 -0
- {fixos-2.2.26 → fixos-2.2.28}/fixos.egg-info/PKG-INFO +6 -6
- {fixos-2.2.26 → fixos-2.2.28}/fixos.egg-info/SOURCES.txt +3 -0
- {fixos-2.2.26 → fixos-2.2.28}/fixos.egg-info/top_level.txt +1 -0
- {fixos-2.2.26 → fixos-2.2.28}/pyproject.toml +1 -1
- {fixos-2.2.26 → fixos-2.2.28}/setup.py +1 -1
- {fixos-2.2.26 → fixos-2.2.28}/tests/unit/test_core.py +87 -0
- {fixos-2.2.26 → fixos-2.2.28}/.env.example +0 -0
- {fixos-2.2.26 → fixos-2.2.28}/LICENSE +0 -0
- {fixos-2.2.26 → fixos-2.2.28}/MANIFEST.in +0 -0
- {fixos-2.2.26 → fixos-2.2.28}/docker/README.md +0 -0
- {fixos-2.2.26 → fixos-2.2.28}/docker/TEST_RESULTS.md +0 -0
- {fixos-2.2.26 → fixos-2.2.28}/docker/TEST_RESULTS_V2.md +0 -0
- {fixos-2.2.26 → fixos-2.2.28}/docker/alpine/Dockerfile +0 -0
- {fixos-2.2.26 → fixos-2.2.28}/docker/arch/Dockerfile +0 -0
- {fixos-2.2.26 → fixos-2.2.28}/docker/base/Dockerfile +0 -0
- {fixos-2.2.26 → fixos-2.2.28}/docker/broken-audio/Dockerfile +0 -0
- {fixos-2.2.26 → fixos-2.2.28}/docker/broken-full/Dockerfile +0 -0
- {fixos-2.2.26 → fixos-2.2.28}/docker/broken-network/Dockerfile +0 -0
- {fixos-2.2.26 → fixos-2.2.28}/docker/broken-thumbnails/Dockerfile +0 -0
- {fixos-2.2.26 → fixos-2.2.28}/docker/debian/Dockerfile +0 -0
- {fixos-2.2.26 → fixos-2.2.28}/docker/docker-compose.multi-system.yml +0 -0
- {fixos-2.2.26 → fixos-2.2.28}/docker/fedora/Dockerfile +0 -0
- {fixos-2.2.26 → fixos-2.2.28}/docker/test-multi-system.sh +0 -0
- {fixos-2.2.26 → fixos-2.2.28}/docker/ubuntu/Dockerfile +0 -0
- {fixos-2.2.26 → fixos-2.2.28}/docs/examples/advanced_usage.py +0 -0
- {fixos-2.2.26 → fixos-2.2.28}/docs/examples/quickstart.py +0 -0
- {fixos-2.2.26 → fixos-2.2.28}/fixos/agent/__init__.py +0 -0
- {fixos-2.2.26 → fixos-2.2.28}/fixos/agent/autonomous.py +0 -0
- {fixos-2.2.26 → fixos-2.2.28}/fixos/agent/autonomous_session.py +0 -0
- {fixos-2.2.26 → fixos-2.2.28}/fixos/agent/hitl.py +0 -0
- {fixos-2.2.26 → fixos-2.2.28}/fixos/agent/hitl_session.py +0 -0
- {fixos-2.2.26 → fixos-2.2.28}/fixos/agent/session_handlers.py +0 -0
- {fixos-2.2.26 → fixos-2.2.28}/fixos/agent/session_io.py +0 -0
- {fixos-2.2.26 → fixos-2.2.28}/fixos/anonymizer.py +0 -0
- {fixos-2.2.26 → fixos-2.2.28}/fixos/cli/__init__.py +0 -0
- {fixos-2.2.26 → fixos-2.2.28}/fixos/cli/ask_cmd.py +0 -0
- {fixos-2.2.26 → fixos-2.2.28}/fixos/cli/cleanup_cmd.py +0 -0
- {fixos-2.2.26 → fixos-2.2.28}/fixos/cli/config_cmd.py +0 -0
- {fixos-2.2.26 → fixos-2.2.28}/fixos/cli/features_cmd.py +0 -0
- {fixos-2.2.26 → fixos-2.2.28}/fixos/cli/history_cmd.py +0 -0
- {fixos-2.2.26 → fixos-2.2.28}/fixos/cli/main.py +0 -0
- {fixos-2.2.26 → fixos-2.2.28}/fixos/cli/orchestrate_cmd.py +0 -0
- {fixos-2.2.26 → fixos-2.2.28}/fixos/cli/profile_cmd.py +0 -0
- {fixos-2.2.26 → fixos-2.2.28}/fixos/cli/provider_cmd.py +0 -0
- {fixos-2.2.26 → fixos-2.2.28}/fixos/cli/quickfix_cmd.py +0 -0
- {fixos-2.2.26 → fixos-2.2.28}/fixos/cli/rollback_cmd.py +0 -0
- {fixos-2.2.26 → fixos-2.2.28}/fixos/cli/token_cmd.py +0 -0
- {fixos-2.2.26 → fixos-2.2.28}/fixos/cli/watch_cmd.py +0 -0
- {fixos-2.2.26 → fixos-2.2.28}/fixos/cli.py +0 -0
- {fixos-2.2.26 → fixos-2.2.28}/fixos/config.py +0 -0
- {fixos-2.2.26 → fixos-2.2.28}/fixos/config_interactive.py +0 -0
- {fixos-2.2.26 → fixos-2.2.28}/fixos/constants.py +0 -0
- {fixos-2.2.26 → fixos-2.2.28}/fixos/diagnostics/__init__.py +0 -0
- {fixos-2.2.26 → fixos-2.2.28}/fixos/diagnostics/checks/__init__.py +0 -0
- {fixos-2.2.26 → fixos-2.2.28}/fixos/diagnostics/checks/_shared.py +0 -0
- {fixos-2.2.26 → fixos-2.2.28}/fixos/diagnostics/checks/audio.py +0 -0
- {fixos-2.2.26 → fixos-2.2.28}/fixos/diagnostics/checks/hardware.py +0 -0
- {fixos-2.2.26 → fixos-2.2.28}/fixos/diagnostics/checks/resources.py +0 -0
- {fixos-2.2.26 → fixos-2.2.28}/fixos/diagnostics/checks/security.py +0 -0
- {fixos-2.2.26 → fixos-2.2.28}/fixos/diagnostics/checks/system_core.py +0 -0
- {fixos-2.2.26 → fixos-2.2.28}/fixos/diagnostics/checks/thumbnails.py +0 -0
- {fixos-2.2.26 → fixos-2.2.28}/fixos/diagnostics/dev_project_analyzer.py +0 -0
- {fixos-2.2.26 → fixos-2.2.28}/fixos/diagnostics/disk_analyzer.py +0 -0
- {fixos-2.2.26 → fixos-2.2.28}/fixos/diagnostics/flatpak_analyzer.py +0 -0
- {fixos-2.2.26 → fixos-2.2.28}/fixos/diagnostics/service_cleanup.py +0 -0
- {fixos-2.2.26 → fixos-2.2.28}/fixos/diagnostics/service_details.py +0 -0
- {fixos-2.2.26 → fixos-2.2.28}/fixos/diagnostics/service_scanner.py +0 -0
- {fixos-2.2.26 → fixos-2.2.28}/fixos/diagnostics/storage_analyzer.py +0 -0
- {fixos-2.2.26 → fixos-2.2.28}/fixos/diagnostics/system_checks.py +0 -0
- {fixos-2.2.26 → fixos-2.2.28}/fixos/features/__init__.py +0 -0
- {fixos-2.2.26 → fixos-2.2.28}/fixos/features/auditor.py +0 -0
- {fixos-2.2.26 → fixos-2.2.28}/fixos/features/catalog.py +0 -0
- {fixos-2.2.26 → fixos-2.2.28}/fixos/features/installer.py +0 -0
- {fixos-2.2.26 → fixos-2.2.28}/fixos/features/profiles.py +0 -0
- {fixos-2.2.26 → fixos-2.2.28}/fixos/features/renderer.py +0 -0
- {fixos-2.2.26 → fixos-2.2.28}/fixos/fixes/__init__.py +0 -0
- {fixos-2.2.26 → fixos-2.2.28}/fixos/interactive/__init__.py +0 -0
- {fixos-2.2.26 → fixos-2.2.28}/fixos/interactive/cleanup_planner.py +0 -0
- {fixos-2.2.26 → fixos-2.2.28}/fixos/llm_shell.py +0 -0
- {fixos-2.2.26 → fixos-2.2.28}/fixos/orchestrator/__init__.py +0 -0
- {fixos-2.2.26 → fixos-2.2.28}/fixos/orchestrator/executor.py +0 -0
- {fixos-2.2.26 → fixos-2.2.28}/fixos/orchestrator/graph.py +0 -0
- {fixos-2.2.26 → fixos-2.2.28}/fixos/orchestrator/orchestrator.py +0 -0
- {fixos-2.2.26 → fixos-2.2.28}/fixos/orchestrator/rollback.py +0 -0
- {fixos-2.2.26 → fixos-2.2.28}/fixos/platform_utils.py +0 -0
- {fixos-2.2.26 → fixos-2.2.28}/fixos/plugins/__init__.py +0 -0
- {fixos-2.2.26 → fixos-2.2.28}/fixos/plugins/base.py +0 -0
- {fixos-2.2.26 → fixos-2.2.28}/fixos/plugins/builtin/__init__.py +0 -0
- {fixos-2.2.26 → fixos-2.2.28}/fixos/plugins/builtin/audio.py +0 -0
- {fixos-2.2.26 → fixos-2.2.28}/fixos/plugins/builtin/disk.py +0 -0
- {fixos-2.2.26 → fixos-2.2.28}/fixos/plugins/builtin/hardware.py +0 -0
- {fixos-2.2.26 → fixos-2.2.28}/fixos/plugins/builtin/resources.py +0 -0
- {fixos-2.2.26 → fixos-2.2.28}/fixos/plugins/builtin/security.py +0 -0
- {fixos-2.2.26 → fixos-2.2.28}/fixos/plugins/builtin/thumbnails.py +0 -0
- {fixos-2.2.26 → fixos-2.2.28}/fixos/plugins/registry.py +0 -0
- {fixos-2.2.26 → fixos-2.2.28}/fixos/profiles/__init__.py +0 -0
- {fixos-2.2.26 → fixos-2.2.28}/fixos/providers/__init__.py +0 -0
- {fixos-2.2.26 → fixos-2.2.28}/fixos/providers/llm.py +0 -0
- {fixos-2.2.26 → fixos-2.2.28}/fixos/providers/llm_analyzer.py +0 -0
- {fixos-2.2.26 → fixos-2.2.28}/fixos/providers/schemas.py +0 -0
- {fixos-2.2.26 → fixos-2.2.28}/fixos/system_checks.py +0 -0
- {fixos-2.2.26 → fixos-2.2.28}/fixos/utils/__init__.py +0 -0
- {fixos-2.2.26 → fixos-2.2.28}/fixos/utils/anonymizer.py +0 -0
- {fixos-2.2.26 → fixos-2.2.28}/fixos/utils/terminal.py +0 -0
- {fixos-2.2.26 → fixos-2.2.28}/fixos/utils/timeout.py +0 -0
- {fixos-2.2.26 → fixos-2.2.28}/fixos/utils/web_search.py +0 -0
- {fixos-2.2.26 → fixos-2.2.28}/fixos/watch.py +0 -0
- {fixos-2.2.26 → fixos-2.2.28}/fixos.egg-info/dependency_links.txt +0 -0
- {fixos-2.2.26 → fixos-2.2.28}/fixos.egg-info/entry_points.txt +0 -0
- {fixos-2.2.26 → fixos-2.2.28}/fixos.egg-info/requires.txt +0 -0
- {fixos-2.2.26 → fixos-2.2.28}/pytest.ini +0 -0
- {fixos-2.2.26 → fixos-2.2.28}/requirements-dev.txt +0 -0
- {fixos-2.2.26 → fixos-2.2.28}/requirements.txt +0 -0
- {fixos-2.2.26 → fixos-2.2.28}/scripts/pyqual-calibrate.py +0 -0
- {fixos-2.2.26 → fixos-2.2.28}/setup.cfg +0 -0
- {fixos-2.2.26 → fixos-2.2.28}/tests/__init__.py +0 -0
- {fixos-2.2.26 → fixos-2.2.28}/tests/conftest.py +0 -0
- {fixos-2.2.26 → fixos-2.2.28}/tests/e2e/__init__.py +0 -0
- {fixos-2.2.26 → fixos-2.2.28}/tests/e2e/test_anonymization_layers.py +0 -0
- {fixos-2.2.26 → fixos-2.2.28}/tests/e2e/test_audio_broken.py +0 -0
- {fixos-2.2.26 → fixos-2.2.28}/tests/e2e/test_cli.py +0 -0
- {fixos-2.2.26 → fixos-2.2.28}/tests/e2e/test_executor.py +0 -0
- {fixos-2.2.26 → fixos-2.2.28}/tests/e2e/test_multi_system.py +0 -0
- {fixos-2.2.26 → fixos-2.2.28}/tests/e2e/test_network_broken.py +0 -0
- {fixos-2.2.26 → fixos-2.2.28}/tests/e2e/test_thumbnails_broken.py +0 -0
- {fixos-2.2.26 → fixos-2.2.28}/tests/test_fixos.py +0 -0
- {fixos-2.2.26 → fixos-2.2.28}/tests/unit/__init__.py +0 -0
- {fixos-2.2.26 → fixos-2.2.28}/tests/unit/test_anonymizer.py +0 -0
- {fixos-2.2.26 → fixos-2.2.28}/tests/unit/test_executor.py +0 -0
- {fixos-2.2.26 → fixos-2.2.28}/tests/unit/test_orchestrator.py +0 -0
- {fixos-2.2.26 → fixos-2.2.28}/tests/unit/test_service_cleanup.py +0 -0
- {fixos-2.2.26 → fixos-2.2.28}/tests/unit/test_service_scanner.py +0 -0
|
@@ -150,6 +150,47 @@ fix(goal): code analysis engine
|
|
|
150
150
|
- **refactor(cli):** Usunięto zduplikowany kod ujednolicając funkcje analizy dysku do wspólnego helpera `_run_disk_analysis`.
|
|
151
151
|
- **refactor(ui):** Usunięto ikony Unicode z CLI i sformatowano wyjście `stderr` oraz standardowego logowania na czysty kod Markdown dla poprawy czytelności w oknach terminalowych.
|
|
152
152
|
|
|
153
|
+
## [2.2.28] - 2026-05-06
|
|
154
|
+
|
|
155
|
+
### Docs
|
|
156
|
+
- Update README.md
|
|
157
|
+
- Update SUMD.md
|
|
158
|
+
- Update SUMR.md
|
|
159
|
+
- Update project/README.md
|
|
160
|
+
- Update project/context.md
|
|
161
|
+
|
|
162
|
+
### Test
|
|
163
|
+
- Update tests/unit/test_core.py
|
|
164
|
+
|
|
165
|
+
### Other
|
|
166
|
+
- Update app.doql.less
|
|
167
|
+
- Update docker/docker-compose.yml
|
|
168
|
+
- Update fixos/agent/session_core.py
|
|
169
|
+
- Update project/analysis.toon.yaml
|
|
170
|
+
- Update project/calls.mmd
|
|
171
|
+
- Update project/calls.png
|
|
172
|
+
- Update project/calls.toon.yaml
|
|
173
|
+
- Update project/calls.yaml
|
|
174
|
+
- Update project/compact_flow.mmd
|
|
175
|
+
- Update project/compact_flow.png
|
|
176
|
+
- ... and 10 more files
|
|
177
|
+
|
|
178
|
+
## [2.2.27] - 2026-05-04
|
|
179
|
+
|
|
180
|
+
### Docs
|
|
181
|
+
- Update README.md
|
|
182
|
+
|
|
183
|
+
### Other
|
|
184
|
+
- Update docker/docker-compose.yml
|
|
185
|
+
- Update docker/test-scenarios.sh
|
|
186
|
+
- Update docker/validate-scenario.py
|
|
187
|
+
- Update fixos/cli/fix_cmd.py
|
|
188
|
+
- Update fixos/cli/output_formatter.py
|
|
189
|
+
- Update fixos/cli/report_cmd.py
|
|
190
|
+
- Update fixos/cli/scan_cmd.py
|
|
191
|
+
- Update fixos/cli/shared.py
|
|
192
|
+
- Update uv.lock
|
|
193
|
+
|
|
153
194
|
## [2.2.26] - 2026-05-04
|
|
154
195
|
|
|
155
196
|
### Docs
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: fixos
|
|
3
|
-
Version: 2.2.
|
|
3
|
+
Version: 2.2.28
|
|
4
4
|
Summary: AI-powered Linux/Windows diagnostics and repair – audio, hardware, system issues
|
|
5
5
|
Home-page: https://github.com/wronai/fixos
|
|
6
6
|
Author: fixos contributors
|
|
@@ -63,13 +63,13 @@ AI-powered OS Diagnostics
|
|
|
63
63
|
|
|
64
64
|
## AI Cost Tracking
|
|
65
65
|
|
|
66
|
-
     
|
|
67
|
+
  
|
|
68
68
|
|
|
69
|
-
- 🤖 **LLM usage:** $7.5000 (
|
|
70
|
-
- 👤 **Human dev:** ~$
|
|
69
|
+
- 🤖 **LLM usage:** $7.5000 (127 commits)
|
|
70
|
+
- 👤 **Human dev:** ~$2647 (26.5h @ $100/h, 30min dedup)
|
|
71
71
|
|
|
72
|
-
Generated on 2026-05-
|
|
72
|
+
Generated on 2026-05-06 using [openrouter/qwen/qwen3-coder-next](https://openrouter.ai/qwen/qwen3-coder-next)
|
|
73
73
|
|
|
74
74
|
---
|
|
75
75
|
|
|
@@ -19,13 +19,13 @@ AI-powered OS Diagnostics
|
|
|
19
19
|
|
|
20
20
|
## AI Cost Tracking
|
|
21
21
|
|
|
22
|
-
     
|
|
23
|
+
  
|
|
24
24
|
|
|
25
|
-
- 🤖 **LLM usage:** $7.5000 (
|
|
26
|
-
- 👤 **Human dev:** ~$
|
|
25
|
+
- 🤖 **LLM usage:** $7.5000 (127 commits)
|
|
26
|
+
- 👤 **Human dev:** ~$2647 (26.5h @ $100/h, 30min dedup)
|
|
27
27
|
|
|
28
|
-
Generated on 2026-05-
|
|
28
|
+
Generated on 2026-05-06 using [openrouter/qwen/qwen3-coder-next](https://openrouter.ai/qwen/qwen3-coder-next)
|
|
29
29
|
|
|
30
30
|
---
|
|
31
31
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
# docker-compose.yml – fixos test environments
|
|
2
2
|
|
|
3
3
|
# ═══════════════════════════════════════════════════════════
|
|
4
4
|
# fixos – środowiska testowe (Docker)
|
|
@@ -32,7 +32,6 @@ services:
|
|
|
32
32
|
context: ..
|
|
33
33
|
dockerfile: docker/base/Dockerfile
|
|
34
34
|
image: fixos-base:latest
|
|
35
|
-
profiles: ["build-only"]
|
|
36
35
|
command: echo "base image built"
|
|
37
36
|
|
|
38
37
|
# ── Uszkodzony dźwięk ──────────────────────────────────
|
|
@@ -49,9 +48,9 @@ services:
|
|
|
49
48
|
- FIXOS_TEST_MODE=1
|
|
50
49
|
command: >
|
|
51
50
|
sh -c "
|
|
52
|
-
echo '=== Scenariusz: broken-audio ===' &&
|
|
53
|
-
verify-scenario.sh &&
|
|
54
|
-
python3 -m fixos.
|
|
51
|
+
echo '=== Scenariusz: broken-audio ===' >&2 &&
|
|
52
|
+
verify-scenario.sh >&2 &&
|
|
53
|
+
python3 -m fixos.cli.main scan --yaml -M audio --no-banner
|
|
55
54
|
"
|
|
56
55
|
|
|
57
56
|
# ── Uszkodzone thumbnails ──────────────────────────────
|
|
@@ -83,9 +82,9 @@ services:
|
|
|
83
82
|
- FIXOS_TEST_MODE=1
|
|
84
83
|
command: >
|
|
85
84
|
sh -c "
|
|
86
|
-
echo '=== Scenariusz: broken-network ===' &&
|
|
87
|
-
verify-scenario.sh &&
|
|
88
|
-
python3
|
|
85
|
+
echo '=== Scenariusz: broken-network ===' >&2 &&
|
|
86
|
+
verify-scenario.sh >&2 &&
|
|
87
|
+
python3 -m fixos.cli.main scan --yaml -M system --no-banner
|
|
89
88
|
"
|
|
90
89
|
|
|
91
90
|
# ── Pełne uszkodzenie ─────────────────────────────────
|
|
@@ -123,8 +122,7 @@ services:
|
|
|
123
122
|
--cov-report=html:/reports/coverage
|
|
124
123
|
-m 'not real_api'
|
|
125
124
|
"
|
|
126
|
-
|
|
127
|
-
- base
|
|
125
|
+
|
|
128
126
|
|
|
129
127
|
# ── Unit testy (bez Dockera środowisk) ────────────────
|
|
130
128
|
unit-tests:
|
|
@@ -153,6 +151,23 @@ services:
|
|
|
153
151
|
python -m pytest tests/e2e/test_cli.py tests/e2e/test_executor.py -v --tb=short
|
|
154
152
|
"
|
|
155
153
|
|
|
154
|
+
# ── Testy scenariuszy (YAML validation) ────────────────
|
|
155
|
+
scenario-tests:
|
|
156
|
+
<<: *common
|
|
157
|
+
build:
|
|
158
|
+
context: ..
|
|
159
|
+
dockerfile: docker/base/Dockerfile
|
|
160
|
+
container_name: fixos-scenario-tests
|
|
161
|
+
environment:
|
|
162
|
+
- FIXOS_TEST_MODE=1
|
|
163
|
+
command: >
|
|
164
|
+
sh -c "
|
|
165
|
+
echo 'Running scenario validation...' &&
|
|
166
|
+
python3 -m fixos.cli.main scan --yaml -M audio --no-banner |
|
|
167
|
+
python3 docker/validate-scenario.py broken-audio &&
|
|
168
|
+
echo 'All scenario tests passed!'
|
|
169
|
+
"
|
|
170
|
+
|
|
156
171
|
networks:
|
|
157
172
|
fixos-test:
|
|
158
173
|
driver: bridge
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# ═══════════════════════════════════════════════════════════
|
|
3
|
+
# fixos – Docker Scenario Test Harness
|
|
4
|
+
#
|
|
5
|
+
# Builds and runs each broken-* scenario container,
|
|
6
|
+
# captures YAML output from `fixos scan --yaml`,
|
|
7
|
+
# and validates results with validate-scenario.py.
|
|
8
|
+
#
|
|
9
|
+
# Usage:
|
|
10
|
+
# ./test-scenarios.sh # all scenarios
|
|
11
|
+
# ./test-scenarios.sh broken-audio # single scenario
|
|
12
|
+
# ═══════════════════════════════════════════════════════════
|
|
13
|
+
|
|
14
|
+
set -euo pipefail
|
|
15
|
+
|
|
16
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
17
|
+
PROJECT_ROOT="$(dirname "$SCRIPT_DIR")"
|
|
18
|
+
VALIDATOR="$SCRIPT_DIR/validate-scenario.py"
|
|
19
|
+
REPORTS_DIR="${REPORTS_DIR:-/tmp/fixos-test-reports}"
|
|
20
|
+
|
|
21
|
+
# Colors
|
|
22
|
+
RED='\033[0;31m'
|
|
23
|
+
GREEN='\033[0;32m'
|
|
24
|
+
YELLOW='\033[1;33m'
|
|
25
|
+
CYAN='\033[0;36m'
|
|
26
|
+
NC='\033[0m'
|
|
27
|
+
|
|
28
|
+
# Scenarios: service name → modules to scan
|
|
29
|
+
declare -A SCENARIO_MODULES=(
|
|
30
|
+
["broken-audio"]="audio"
|
|
31
|
+
["broken-thumbnails"]="thumbnails"
|
|
32
|
+
["broken-network"]="system"
|
|
33
|
+
["broken-full"]="audio,thumbnails,system"
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
PASSED=0
|
|
37
|
+
FAILED=0
|
|
38
|
+
ERRORS=()
|
|
39
|
+
|
|
40
|
+
log() { echo -e "${CYAN}[fixos-test]${NC} $*"; }
|
|
41
|
+
ok() { echo -e " ${GREEN}✓${NC} $*"; }
|
|
42
|
+
err() { echo -e " ${RED}✗${NC} $*"; }
|
|
43
|
+
warn() { echo -e " ${YELLOW}⚠${NC} $*"; }
|
|
44
|
+
|
|
45
|
+
# ── Build base image ──────────────────────────────────────
|
|
46
|
+
build_base() {
|
|
47
|
+
log "Building base image..."
|
|
48
|
+
docker compose -f "$SCRIPT_DIR/docker-compose.yml" build base 2>&1 | tail -5
|
|
49
|
+
ok "fixos-base:latest built"
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
# ── Run scenario ──────────────────────────────────────────
|
|
53
|
+
run_scenario() {
|
|
54
|
+
local scenario="$1"
|
|
55
|
+
local modules="${SCENARIO_MODULES[$scenario]:-system}"
|
|
56
|
+
local output_file="$REPORTS_DIR/${scenario}.yml"
|
|
57
|
+
|
|
58
|
+
log "━━━ Scenario: ${YELLOW}${scenario}${NC} (modules: $modules) ━━━"
|
|
59
|
+
|
|
60
|
+
# Build scenario image
|
|
61
|
+
log " Building $scenario..."
|
|
62
|
+
if ! docker compose -f "$SCRIPT_DIR/docker-compose.yml" build "$scenario" 2>&1 | tail -3; then
|
|
63
|
+
err "Build failed for $scenario"
|
|
64
|
+
ERRORS+=("$scenario: build failed")
|
|
65
|
+
((FAILED++))
|
|
66
|
+
return 1
|
|
67
|
+
fi
|
|
68
|
+
|
|
69
|
+
# Run fixos scan --yaml inside container
|
|
70
|
+
log " Running fixos scan --yaml -M $modules..."
|
|
71
|
+
mkdir -p "$REPORTS_DIR"
|
|
72
|
+
|
|
73
|
+
local exit_code=0
|
|
74
|
+
docker compose -f "$SCRIPT_DIR/docker-compose.yml" run --rm \
|
|
75
|
+
--no-deps \
|
|
76
|
+
-e FIXOS_TEST_MODE=1 \
|
|
77
|
+
"$scenario" \
|
|
78
|
+
python3 -m fixos.cli.main scan --yaml -M "$modules" --no-banner \
|
|
79
|
+
> "$output_file" 2>/dev/null || exit_code=$?
|
|
80
|
+
|
|
81
|
+
# Check we got output
|
|
82
|
+
if [ ! -s "$output_file" ]; then
|
|
83
|
+
err "No YAML output from $scenario"
|
|
84
|
+
ERRORS+=("$scenario: no output")
|
|
85
|
+
((FAILED++))
|
|
86
|
+
return 1
|
|
87
|
+
fi
|
|
88
|
+
|
|
89
|
+
ok "YAML captured: $output_file ($(wc -l < "$output_file") lines)"
|
|
90
|
+
|
|
91
|
+
# Validate with validate-scenario.py
|
|
92
|
+
if python3 "$VALIDATOR" "$scenario" < "$output_file"; then
|
|
93
|
+
ok "$scenario PASSED"
|
|
94
|
+
((PASSED++))
|
|
95
|
+
else
|
|
96
|
+
err "$scenario FAILED validation"
|
|
97
|
+
ERRORS+=("$scenario: validation failed")
|
|
98
|
+
((FAILED++))
|
|
99
|
+
fi
|
|
100
|
+
|
|
101
|
+
echo ""
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
# ── Main ──────────────────────────────────────────────────
|
|
105
|
+
main() {
|
|
106
|
+
log "fixOS Docker Scenario Test Harness"
|
|
107
|
+
log "═══════════════════════════════════"
|
|
108
|
+
|
|
109
|
+
# Build base first
|
|
110
|
+
build_base
|
|
111
|
+
|
|
112
|
+
# Determine which scenarios to run
|
|
113
|
+
local scenarios=()
|
|
114
|
+
if [ $# -gt 0 ]; then
|
|
115
|
+
scenarios=("$@")
|
|
116
|
+
else
|
|
117
|
+
scenarios=("broken-audio" "broken-thumbnails" "broken-network" "broken-full")
|
|
118
|
+
fi
|
|
119
|
+
|
|
120
|
+
# Run each scenario
|
|
121
|
+
for scenario in "${scenarios[@]}"; do
|
|
122
|
+
if [ -z "${SCENARIO_MODULES[$scenario]+x}" ]; then
|
|
123
|
+
warn "Unknown scenario: $scenario (skipping)"
|
|
124
|
+
continue
|
|
125
|
+
fi
|
|
126
|
+
run_scenario "$scenario" || true
|
|
127
|
+
done
|
|
128
|
+
|
|
129
|
+
# Summary
|
|
130
|
+
echo ""
|
|
131
|
+
log "═══════════════════════════════════"
|
|
132
|
+
log " SUMMARY"
|
|
133
|
+
log "═══════════════════════════════════"
|
|
134
|
+
ok "Passed: $PASSED"
|
|
135
|
+
if [ $FAILED -gt 0 ]; then
|
|
136
|
+
err "Failed: $FAILED"
|
|
137
|
+
for e in "${ERRORS[@]}"; do
|
|
138
|
+
err " $e"
|
|
139
|
+
done
|
|
140
|
+
exit 1
|
|
141
|
+
else
|
|
142
|
+
ok "All scenarios passed!"
|
|
143
|
+
exit 0
|
|
144
|
+
fi
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
main "$@"
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Validates fixOS scan YAML output against expected scenario conditions.
|
|
4
|
+
|
|
5
|
+
Usage:
|
|
6
|
+
fixos scan --yaml --modules audio | python3 validate-scenario.py broken-audio
|
|
7
|
+
cat scan-output.yml | python3 validate-scenario.py broken-full
|
|
8
|
+
|
|
9
|
+
Exit codes:
|
|
10
|
+
0 – all expectations met
|
|
11
|
+
1 – validation failures
|
|
12
|
+
2 – invalid input / unknown scenario
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
from __future__ import annotations
|
|
16
|
+
|
|
17
|
+
import sys
|
|
18
|
+
from typing import Any
|
|
19
|
+
|
|
20
|
+
import yaml
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
# ── Scenario expectations ─────────────────────────────────
|
|
24
|
+
|
|
25
|
+
SCENARIOS: dict[str, list[dict[str, Any]]] = {
|
|
26
|
+
"broken-audio": [
|
|
27
|
+
{
|
|
28
|
+
"path": "diagnostics.audio.alsa_cards",
|
|
29
|
+
"contains": "no soundcards",
|
|
30
|
+
"desc": "ALSA powinno zgłaszać brak kart dźwiękowych",
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
"path": "diagnostics.audio.sof_firmware",
|
|
34
|
+
"contains_any": ["No such file", "not installed", "ERR"],
|
|
35
|
+
"desc": "SOF firmware powinno być niedostępne",
|
|
36
|
+
},
|
|
37
|
+
],
|
|
38
|
+
"broken-thumbnails": [
|
|
39
|
+
{
|
|
40
|
+
"path": "diagnostics.thumbnails.thumbnail_cache_count",
|
|
41
|
+
"equals": "0",
|
|
42
|
+
"desc": "Cache miniaturek powinien być pusty",
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
"path": "diagnostics.thumbnails.ffmpegthumbnailer",
|
|
46
|
+
"contains": "nie zainstalowany",
|
|
47
|
+
"desc": "ffmpegthumbnailer powinien być niezainstalowany",
|
|
48
|
+
},
|
|
49
|
+
],
|
|
50
|
+
"broken-network": [
|
|
51
|
+
{
|
|
52
|
+
"path": "diagnostics.system.systemctl_failed",
|
|
53
|
+
"contains": "NetworkManager",
|
|
54
|
+
"desc": "NetworkManager powinien być w stanie failed",
|
|
55
|
+
},
|
|
56
|
+
],
|
|
57
|
+
"broken-full": [
|
|
58
|
+
{
|
|
59
|
+
"path": "diagnostics.audio.alsa_cards",
|
|
60
|
+
"contains": "no soundcards",
|
|
61
|
+
"desc": "ALSA: brak kart dźwiękowych",
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
"path": "diagnostics.thumbnails.ffmpegthumbnailer",
|
|
65
|
+
"contains": "nie zainstalowany",
|
|
66
|
+
"desc": "ffmpegthumbnailer niezainstalowany",
|
|
67
|
+
},
|
|
68
|
+
],
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def _get_nested(data: dict, path: str) -> Any:
|
|
73
|
+
"""Get a nested value from a dict using dot notation."""
|
|
74
|
+
keys = path.split(".")
|
|
75
|
+
current = data
|
|
76
|
+
for key in keys:
|
|
77
|
+
if isinstance(current, dict) and key in current:
|
|
78
|
+
current = current[key]
|
|
79
|
+
else:
|
|
80
|
+
return None
|
|
81
|
+
return current
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def validate(data: dict, scenario: str) -> list[str]:
|
|
85
|
+
"""Validate data against scenario expectations. Returns list of failures."""
|
|
86
|
+
expectations = SCENARIOS.get(scenario)
|
|
87
|
+
if expectations is None:
|
|
88
|
+
return [f"Nieznany scenariusz: '{scenario}'. Dostępne: {', '.join(SCENARIOS.keys())}"]
|
|
89
|
+
|
|
90
|
+
failures = []
|
|
91
|
+
for exp in expectations:
|
|
92
|
+
path = exp["path"]
|
|
93
|
+
desc = exp.get("desc", path)
|
|
94
|
+
value = _get_nested(data, path)
|
|
95
|
+
|
|
96
|
+
if value is None:
|
|
97
|
+
failures.append(f" ✗ BRAK KLUCZA: {path} — {desc}")
|
|
98
|
+
continue
|
|
99
|
+
|
|
100
|
+
value_str = str(value)
|
|
101
|
+
|
|
102
|
+
if "equals" in exp:
|
|
103
|
+
if value_str.strip() != str(exp["equals"]).strip():
|
|
104
|
+
failures.append(f" ✗ {desc}: oczekiwano '{exp['equals']}', otrzymano '{value_str[:80]}'")
|
|
105
|
+
|
|
106
|
+
if "contains" in exp:
|
|
107
|
+
if exp["contains"].lower() not in value_str.lower():
|
|
108
|
+
failures.append(f" ✗ {desc}: brak '{exp['contains']}' w '{value_str[:80]}'")
|
|
109
|
+
|
|
110
|
+
if "contains_any" in exp:
|
|
111
|
+
if not any(term.lower() in value_str.lower() for term in exp["contains_any"]):
|
|
112
|
+
failures.append(
|
|
113
|
+
f" ✗ {desc}: brak żadnego z {exp['contains_any']} w '{value_str[:80]}'"
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
return failures
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
def main() -> None:
|
|
120
|
+
if len(sys.argv) < 2:
|
|
121
|
+
print(f"Użycie: {sys.argv[0]} <scenario>", file=sys.stderr)
|
|
122
|
+
print(f"Dostępne scenariusze: {', '.join(SCENARIOS.keys())}", file=sys.stderr)
|
|
123
|
+
sys.exit(2)
|
|
124
|
+
|
|
125
|
+
scenario = sys.argv[1]
|
|
126
|
+
if scenario not in SCENARIOS:
|
|
127
|
+
print(f"✗ Nieznany scenariusz: '{scenario}'", file=sys.stderr)
|
|
128
|
+
print(f" Dostępne: {', '.join(SCENARIOS.keys())}", file=sys.stderr)
|
|
129
|
+
sys.exit(2)
|
|
130
|
+
|
|
131
|
+
# Read YAML from stdin
|
|
132
|
+
try:
|
|
133
|
+
raw = sys.stdin.read()
|
|
134
|
+
data = yaml.safe_load(raw)
|
|
135
|
+
except yaml.YAMLError as e:
|
|
136
|
+
print(f"✗ Błąd parsowania YAML: {e}", file=sys.stderr)
|
|
137
|
+
sys.exit(2)
|
|
138
|
+
|
|
139
|
+
if not isinstance(data, dict):
|
|
140
|
+
print(f"✗ Oczekiwano dict, otrzymano: {type(data).__name__}", file=sys.stderr)
|
|
141
|
+
sys.exit(2)
|
|
142
|
+
|
|
143
|
+
# Validate
|
|
144
|
+
failures = validate(data, scenario)
|
|
145
|
+
|
|
146
|
+
if failures:
|
|
147
|
+
print(f"✗ Scenariusz '{scenario}' — {len(failures)} błędów:", file=sys.stderr)
|
|
148
|
+
for f in failures:
|
|
149
|
+
print(f, file=sys.stderr)
|
|
150
|
+
sys.exit(1)
|
|
151
|
+
else:
|
|
152
|
+
expectations_count = len(SCENARIOS[scenario])
|
|
153
|
+
print(f"✓ Scenariusz '{scenario}' — {expectations_count}/{expectations_count} OK", file=sys.stderr)
|
|
154
|
+
sys.exit(0)
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
if __name__ == "__main__":
|
|
158
|
+
main()
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
"""fixos – AI-powered Linux/Windows diagnostics and repair."""
|
|
2
|
-
__version__ = "2.2.
|
|
2
|
+
__version__ = "2.2.28"
|
|
@@ -111,9 +111,17 @@ def _is_part_diagnostic_only(part: str) -> bool:
|
|
|
111
111
|
return normalized.startswith(diagnostic_prefixes)
|
|
112
112
|
|
|
113
113
|
|
|
114
|
+
def _extract_co_robi(text: str) -> str:
|
|
115
|
+
"""Extract 'Co robi:' comment from text following a command match."""
|
|
116
|
+
m = re.search(r"\s*\*{0,2}Co robi:\*{0,2}\s*(.+?)(?:\n|$)", text, re.IGNORECASE)
|
|
117
|
+
return m.group(1).strip() if m else ""
|
|
118
|
+
|
|
119
|
+
|
|
114
120
|
def extract_fixes(reply: str) -> List[Tuple[str, str]]:
|
|
115
121
|
"""Extract (command, comment) pairs from LLM reply."""
|
|
116
122
|
fixes: List[Tuple[str, str]] = []
|
|
123
|
+
|
|
124
|
+
# Pattern 1: **Komenda:** `command` (strict: bold + backticks)
|
|
117
125
|
for m in re.finditer(
|
|
118
126
|
r"\*\*Komenda:\*\*\s*`([^`]+)`(?:[^\n]*?\*\*Co robi:\*\*\s*(.+?))?(?=\n|$)",
|
|
119
127
|
reply, re.IGNORECASE,
|
|
@@ -121,12 +129,38 @@ def extract_fixes(reply: str) -> List[Tuple[str, str]]:
|
|
|
121
129
|
cmd = m.group(1).strip()
|
|
122
130
|
if cmd:
|
|
123
131
|
fixes.append((cmd, (m.group(2) or "").strip()))
|
|
132
|
+
|
|
133
|
+
# Pattern 2: Komenda: `command` (backticks, optional bold)
|
|
134
|
+
if not fixes:
|
|
135
|
+
for m in re.finditer(
|
|
136
|
+
r"\*{0,2}Komenda:\*{0,2}\s*`([^`]+)`",
|
|
137
|
+
reply, re.IGNORECASE,
|
|
138
|
+
):
|
|
139
|
+
cmd = m.group(1).strip()
|
|
140
|
+
if cmd:
|
|
141
|
+
fixes.append((cmd, _extract_co_robi(reply[m.end():])))
|
|
142
|
+
|
|
143
|
+
# Pattern 3: Komenda: command (no backticks — command until Co robi:/next problem/section)
|
|
144
|
+
if not fixes:
|
|
145
|
+
for m in re.finditer(
|
|
146
|
+
r"\*{0,2}Komenda:\*{0,2}\s*"
|
|
147
|
+
r"(.+?)"
|
|
148
|
+
r"(?=\n\s*\*{0,2}Co robi:|\n[🔴🟡🟢]|\n━|\n─|\n\[[\dA-Z]|\Z)",
|
|
149
|
+
reply, re.IGNORECASE | re.DOTALL,
|
|
150
|
+
):
|
|
151
|
+
cmd = re.sub(r"\s*\n\s*", " ", m.group(1)).strip()
|
|
152
|
+
if cmd:
|
|
153
|
+
fixes.append((cmd, _extract_co_robi(reply[m.end():])))
|
|
154
|
+
|
|
155
|
+
# Fallback: → Fix: `command`
|
|
124
156
|
if not fixes:
|
|
125
157
|
for m in re.finditer(r"→\s*Fix:\s*`([^`]+)`", reply, re.IGNORECASE):
|
|
126
158
|
fixes.append((m.group(1).strip(), ""))
|
|
159
|
+
# Fallback: [N] ... `command`
|
|
127
160
|
if not fixes:
|
|
128
161
|
for m in re.finditer(r"\[(\d+)\][^`\n]+`([^`]+)`", reply):
|
|
129
162
|
fixes.append((m.group(2).strip(), f"Fix #{m.group(1)}"))
|
|
163
|
+
# Fallback: EXEC: `command`
|
|
130
164
|
if not fixes:
|
|
131
165
|
for m in re.finditer(r"EXEC:\s*`([^`]+)`", reply, re.IGNORECASE):
|
|
132
166
|
fixes.append((m.group(1).strip(), ""))
|
|
@@ -9,6 +9,7 @@ from typing import Dict, Any, List
|
|
|
9
9
|
import click
|
|
10
10
|
|
|
11
11
|
from fixos.cli.shared import add_common_options, add_shared_options, BANNER
|
|
12
|
+
from fixos.cli.output_formatter import OutputFormatter
|
|
12
13
|
from fixos.config import FixOsConfig, interactive_provider_setup
|
|
13
14
|
from fixos.agent.hitl import run_hitl_session
|
|
14
15
|
from fixos.agent.autonomous import run_autonomous_session
|
|
@@ -19,22 +20,20 @@ from fixos.constants import (
|
|
|
19
20
|
)
|
|
20
21
|
|
|
21
22
|
|
|
22
|
-
def _collect_diagnostics(modules: str, disc: bool,
|
|
23
|
+
def _collect_diagnostics(modules: str, disc: bool, fmt: OutputFormatter, output: str) -> dict:
|
|
23
24
|
"""Run diagnostics collection and optionally disk analysis. Returns data dict."""
|
|
24
25
|
selected_modules = modules.split(",") if modules else None
|
|
25
26
|
|
|
26
27
|
if disc and not modules:
|
|
27
28
|
data: dict = {}
|
|
28
29
|
else:
|
|
29
|
-
|
|
30
|
-
def progress(name, desc) -> None:
|
|
31
|
-
click.echo(f" → {desc}...")
|
|
30
|
+
fmt.status("\nZbieranie diagnostyki...", fg="yellow")
|
|
32
31
|
from fixos.diagnostics import get_full_diagnostics
|
|
33
|
-
data = get_full_diagnostics(selected_modules, progress_callback=progress)
|
|
32
|
+
data = get_full_diagnostics(selected_modules, progress_callback=fmt.progress)
|
|
34
33
|
|
|
35
34
|
if disc:
|
|
36
35
|
from fixos.cli.scan_cmd import _run_disk_analysis
|
|
37
|
-
_run_disk_analysis(data,
|
|
36
|
+
_run_disk_analysis(data, fmt=fmt, is_fix_mode=True)
|
|
38
37
|
|
|
39
38
|
if output:
|
|
40
39
|
from fixos.utils.anonymizer import anonymize
|
|
@@ -44,9 +43,9 @@ def _collect_diagnostics(modules: str, disc: bool, json_output: bool, output: st
|
|
|
44
43
|
json.dumps({"anonymized": anon_str, "raw": data}, ensure_ascii=False, indent=2, default=str),
|
|
45
44
|
encoding="utf-8"
|
|
46
45
|
)
|
|
47
|
-
|
|
46
|
+
fmt.status(f"Raport: {output}", fg="green")
|
|
48
47
|
except Exception as e:
|
|
49
|
-
|
|
48
|
+
fmt.status(f"Błąd zapisu: {e}")
|
|
50
49
|
|
|
51
50
|
return data
|
|
52
51
|
|
|
@@ -83,7 +82,7 @@ def _run_agent_session(cfg, data: dict, max_fixes: int) -> None:
|
|
|
83
82
|
help="Maksymalna liczba napraw w sesji")
|
|
84
83
|
@add_shared_options
|
|
85
84
|
def fix(provider, token, model, no_banner, mode, timeout, modules, no_show_data, output, max_fixes,
|
|
86
|
-
disc, dry_run, interactive, json_output, llm_fallback, show_raw) -> None:
|
|
85
|
+
disc, dry_run, interactive, json_output, yaml_output, llm_fallback, show_raw) -> None:
|
|
87
86
|
"""
|
|
88
87
|
Przeprowadza pełną diagnostykę i uruchamia sesję naprawczą z LLM.
|
|
89
88
|
|
|
@@ -92,12 +91,17 @@ def fix(provider, token, model, no_banner, mode, timeout, modules, no_show_data,
|
|
|
92
91
|
hitl – Human-in-the-Loop (pyta o każdą akcję) [domyślny]
|
|
93
92
|
autonomous – Agent sam wykonuje komendy (UWAGA: wymaga potwierdzenia)
|
|
94
93
|
|
|
94
|
+
\b
|
|
95
|
+
Pipeline (synchroniczny, bez interakcji):
|
|
96
|
+
fixos fix --yaml --no-interactive # diagnostyka → YAML
|
|
97
|
+
fixos fix --yaml --no-interactive --disc # diagnostyka + dysk → YAML
|
|
98
|
+
|
|
95
99
|
\b
|
|
96
100
|
Opcje dyskowe:
|
|
97
101
|
--disc – Analiza zajętości dysku + grupowanie przyczyn
|
|
98
102
|
--dry-run – Symulacja bez wykonywania akcji
|
|
99
|
-
--interactive – Tryb interaktywny (domyślnie włączony)
|
|
100
103
|
--json – Wyjście w formacie JSON
|
|
104
|
+
--yaml – Wyjście w formacie YAML (pipe-safe)
|
|
101
105
|
--llm-fallback – Użyj LLM gdy heurystyki nie wystarczą
|
|
102
106
|
|
|
103
107
|
\b
|
|
@@ -107,11 +111,24 @@ def fix(provider, token, model, no_banner, mode, timeout, modules, no_show_data,
|
|
|
107
111
|
fixos fix --disc --dry-run # analiza dysku bez wykonywania
|
|
108
112
|
fixos fix --mode autonomous # tryb autonomiczny
|
|
109
113
|
fixos fix --modules audio,thumbnails # tylko audio i thumbnails
|
|
114
|
+
fixos fix --yaml --no-interactive # pipeline mode → YAML
|
|
110
115
|
fixos fix --provider openai --token sk-...
|
|
111
116
|
"""
|
|
117
|
+
fmt = OutputFormatter.from_flags(yaml_output=yaml_output, json_output=json_output)
|
|
118
|
+
|
|
112
119
|
if not no_banner:
|
|
113
|
-
|
|
120
|
+
fmt.banner(BANNER)
|
|
121
|
+
|
|
122
|
+
# ── Pipeline mode: --yaml/--json + --no-interactive ──────
|
|
123
|
+
if fmt.is_machine and not interactive:
|
|
124
|
+
fmt.status("Pipeline mode: diagnostyka → structured output", fg="cyan")
|
|
125
|
+
data = _collect_diagnostics(modules, disc, fmt, output)
|
|
126
|
+
fmt.status("Diagnostyka gotowa.", fg="green")
|
|
127
|
+
content = fmt.format_diagnostics(data)
|
|
128
|
+
click.echo(content)
|
|
129
|
+
return
|
|
114
130
|
|
|
131
|
+
# ── Interactive / LLM mode ────────────────────────────────
|
|
115
132
|
cfg = FixOsConfig.load(
|
|
116
133
|
provider=provider,
|
|
117
134
|
api_key=token,
|
|
@@ -138,15 +155,15 @@ def fix(provider, token, model, no_banner, mode, timeout, modules, no_show_data,
|
|
|
138
155
|
click.echo(click.style(f"{err}", fg="red"))
|
|
139
156
|
sys.exit(1)
|
|
140
157
|
|
|
141
|
-
|
|
158
|
+
fmt.status("\nKonfiguracja:", fg="cyan")
|
|
142
159
|
click.echo(cfg.summary())
|
|
143
160
|
if dry_run:
|
|
144
|
-
|
|
161
|
+
fmt.status(" Tryb: DRY-RUN (komendy nie będą wykonywane)", fg="yellow")
|
|
145
162
|
if disc:
|
|
146
|
-
|
|
163
|
+
fmt.status(" Analiza dysku: Włączona", fg="blue")
|
|
147
164
|
|
|
148
|
-
data = _collect_diagnostics(modules, disc,
|
|
149
|
-
|
|
165
|
+
data = _collect_diagnostics(modules, disc, fmt, output)
|
|
166
|
+
fmt.status("Diagnostyka gotowa.\n", fg="green")
|
|
150
167
|
|
|
151
168
|
if disc and "disk_analysis" in data:
|
|
152
169
|
return handle_disk_cleanup_mode(data["disk_analysis"], cfg, dry_run, interactive, json_output, llm_fallback)
|