fixfleet 0.4.1__tar.gz → 0.4.2__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.
- {fixfleet-0.4.1/fixfleet.egg-info → fixfleet-0.4.2}/PKG-INFO +12 -1
- {fixfleet-0.4.1 → fixfleet-0.4.2}/README.md +11 -0
- {fixfleet-0.4.1 → fixfleet-0.4.2}/bugfixer/__init__.py +1 -1
- {fixfleet-0.4.1 → fixfleet-0.4.2}/bugfixer/cli.py +15 -1
- {fixfleet-0.4.1 → fixfleet-0.4.2}/bugfixer/config.py +3 -0
- {fixfleet-0.4.1 → fixfleet-0.4.2}/bugfixer/json_api.py +28 -1
- fixfleet-0.4.2/bugfixer/telemetry.py +152 -0
- {fixfleet-0.4.1 → fixfleet-0.4.2/fixfleet.egg-info}/PKG-INFO +12 -1
- {fixfleet-0.4.1 → fixfleet-0.4.2}/fixfleet.egg-info/SOURCES.txt +1 -0
- {fixfleet-0.4.1 → fixfleet-0.4.2}/pyproject.toml +1 -1
- {fixfleet-0.4.1 → fixfleet-0.4.2}/tests/test_all.py +3 -0
- {fixfleet-0.4.1 → fixfleet-0.4.2}/tests/test_providers_qa.py +5 -0
- {fixfleet-0.4.1 → fixfleet-0.4.2}/LICENSE +0 -0
- {fixfleet-0.4.1 → fixfleet-0.4.2}/bugfixer/backends/__init__.py +0 -0
- {fixfleet-0.4.1 → fixfleet-0.4.2}/bugfixer/backends/_subprocess.py +0 -0
- {fixfleet-0.4.1 → fixfleet-0.4.2}/bugfixer/backends/api/__init__.py +0 -0
- {fixfleet-0.4.1 → fixfleet-0.4.2}/bugfixer/backends/api/openai_compat.py +0 -0
- {fixfleet-0.4.1 → fixfleet-0.4.2}/bugfixer/backends/base.py +0 -0
- {fixfleet-0.4.1 → fixfleet-0.4.2}/bugfixer/backends/cli/__init__.py +0 -0
- {fixfleet-0.4.1 → fixfleet-0.4.2}/bugfixer/backends/cli/aider.py +0 -0
- {fixfleet-0.4.1 → fixfleet-0.4.2}/bugfixer/backends/cli/claude.py +0 -0
- {fixfleet-0.4.1 → fixfleet-0.4.2}/bugfixer/backends/cli/codex.py +0 -0
- {fixfleet-0.4.1 → fixfleet-0.4.2}/bugfixer/backends/cli/cursor.py +0 -0
- {fixfleet-0.4.1 → fixfleet-0.4.2}/bugfixer/backends/cli/gemini.py +0 -0
- {fixfleet-0.4.1 → fixfleet-0.4.2}/bugfixer/backends/cli/qwen.py +0 -0
- {fixfleet-0.4.1 → fixfleet-0.4.2}/bugfixer/backends/registry.py +0 -0
- {fixfleet-0.4.1 → fixfleet-0.4.2}/bugfixer/budget.py +0 -0
- {fixfleet-0.4.1 → fixfleet-0.4.2}/bugfixer/confidence.py +0 -0
- {fixfleet-0.4.1 → fixfleet-0.4.2}/bugfixer/gitlab.py +0 -0
- {fixfleet-0.4.1 → fixfleet-0.4.2}/bugfixer/locator.py +0 -0
- {fixfleet-0.4.1 → fixfleet-0.4.2}/bugfixer/parser.py +0 -0
- {fixfleet-0.4.1 → fixfleet-0.4.2}/bugfixer/prompt.py +0 -0
- {fixfleet-0.4.1 → fixfleet-0.4.2}/bugfixer/providers/__init__.py +0 -0
- {fixfleet-0.4.1 → fixfleet-0.4.2}/bugfixer/providers/azure.py +0 -0
- {fixfleet-0.4.1 → fixfleet-0.4.2}/bugfixer/providers/base.py +0 -0
- {fixfleet-0.4.1 → fixfleet-0.4.2}/bugfixer/providers/bitbucket.py +0 -0
- {fixfleet-0.4.1 → fixfleet-0.4.2}/bugfixer/providers/github.py +0 -0
- {fixfleet-0.4.1 → fixfleet-0.4.2}/bugfixer/providers/gitlab.py +0 -0
- {fixfleet-0.4.1 → fixfleet-0.4.2}/bugfixer/providers/jira.py +0 -0
- {fixfleet-0.4.1 → fixfleet-0.4.2}/bugfixer/providers/linear.py +0 -0
- {fixfleet-0.4.1 → fixfleet-0.4.2}/bugfixer/providers/registry.py +0 -0
- {fixfleet-0.4.1 → fixfleet-0.4.2}/bugfixer/state.py +0 -0
- {fixfleet-0.4.1 → fixfleet-0.4.2}/bugfixer/ui.py +0 -0
- {fixfleet-0.4.1 → fixfleet-0.4.2}/fixfleet.egg-info/dependency_links.txt +0 -0
- {fixfleet-0.4.1 → fixfleet-0.4.2}/fixfleet.egg-info/entry_points.txt +0 -0
- {fixfleet-0.4.1 → fixfleet-0.4.2}/fixfleet.egg-info/top_level.txt +0 -0
- {fixfleet-0.4.1 → fixfleet-0.4.2}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: fixfleet
|
|
3
|
-
Version: 0.4.
|
|
3
|
+
Version: 0.4.2
|
|
4
4
|
Summary: Auto-fix bugs from GitHub, GitLab, Jira, Linear, Bitbucket, or Azure DevOps with AI agents — bring your own CLI or free API.
|
|
5
5
|
Author: Yash Koladiya
|
|
6
6
|
License-Expression: GPL-3.0-or-later
|
|
@@ -405,6 +405,17 @@ No. FixFleet runs **100% locally** on your machine. No FixFleet servers, no Azur
|
|
|
405
405
|
|
|
406
406
|
</details>
|
|
407
407
|
|
|
408
|
+
<details>
|
|
409
|
+
<summary><b>Does FixFleet collect any data?</b></summary>
|
|
410
|
+
|
|
411
|
+
Only anonymous usage analytics (via Google Analytics): event names and coarse metadata like provider key, backend name, and success/failure — **never your code, tokens, project URLs, or issue content**. Used purely to find bugs and see which trackers/backends need attention.
|
|
412
|
+
|
|
413
|
+
Opt out anytime:
|
|
414
|
+
- CLI: `fixfleet --config-set telemetry.enabled=false` (or set `DO_NOT_TRACK=1`)
|
|
415
|
+
- VSCode: uncheck `fixfleet.telemetry` in settings (the global VS Code telemetry setting is respected too)
|
|
416
|
+
|
|
417
|
+
</details>
|
|
418
|
+
|
|
408
419
|
<details>
|
|
409
420
|
<summary><b>What's the difference between the VSCode extension and the CLI?</b></summary>
|
|
410
421
|
|
|
@@ -377,6 +377,17 @@ No. FixFleet runs **100% locally** on your machine. No FixFleet servers, no Azur
|
|
|
377
377
|
|
|
378
378
|
</details>
|
|
379
379
|
|
|
380
|
+
<details>
|
|
381
|
+
<summary><b>Does FixFleet collect any data?</b></summary>
|
|
382
|
+
|
|
383
|
+
Only anonymous usage analytics (via Google Analytics): event names and coarse metadata like provider key, backend name, and success/failure — **never your code, tokens, project URLs, or issue content**. Used purely to find bugs and see which trackers/backends need attention.
|
|
384
|
+
|
|
385
|
+
Opt out anytime:
|
|
386
|
+
- CLI: `fixfleet --config-set telemetry.enabled=false` (or set `DO_NOT_TRACK=1`)
|
|
387
|
+
- VSCode: uncheck `fixfleet.telemetry` in settings (the global VS Code telemetry setting is respected too)
|
|
388
|
+
|
|
389
|
+
</details>
|
|
390
|
+
|
|
380
391
|
<details>
|
|
381
392
|
<summary><b>What's the difference between the VSCode extension and the CLI?</b></summary>
|
|
382
393
|
|
|
@@ -5,7 +5,7 @@ import sys
|
|
|
5
5
|
from datetime import datetime
|
|
6
6
|
from pathlib import Path
|
|
7
7
|
|
|
8
|
-
from . import budget, config, state, ui
|
|
8
|
+
from . import budget, config, state, telemetry, ui
|
|
9
9
|
from .backends.base import Backend, RunResult
|
|
10
10
|
from .backends.registry import (
|
|
11
11
|
API_PRESETS,
|
|
@@ -569,6 +569,7 @@ def _fix_issues(selected: list, project_dir: str, project_id: str,
|
|
|
569
569
|
failed += 1
|
|
570
570
|
continue
|
|
571
571
|
|
|
572
|
+
telemetry.track("fix_started", {"provider": "gitlab", "backend": backend.name})
|
|
572
573
|
result: RunResult = backend.run(prompt, project_dir)
|
|
573
574
|
consumed = check.estimated # use estimate as proxy
|
|
574
575
|
|
|
@@ -590,6 +591,15 @@ def _fix_issues(selected: list, project_dir: str, project_id: str,
|
|
|
590
591
|
if conf.final_score < 0.20 and not conf.files_changed:
|
|
591
592
|
success = False
|
|
592
593
|
|
|
594
|
+
telemetry.track("fix_completed", {
|
|
595
|
+
"provider": "gitlab",
|
|
596
|
+
"backend": backend.name,
|
|
597
|
+
"success": success,
|
|
598
|
+
"timed_out": result.timed_out,
|
|
599
|
+
"confidence": conf.label(),
|
|
600
|
+
"files_changed": len(conf.files_changed),
|
|
601
|
+
})
|
|
602
|
+
|
|
593
603
|
session_tokens += consumed
|
|
594
604
|
state.record_usage(
|
|
595
605
|
backend_name=backend.name,
|
|
@@ -636,20 +646,24 @@ def main():
|
|
|
636
646
|
try:
|
|
637
647
|
issues = fetch_bug_issues(token, project_id, date_filter, host=host)
|
|
638
648
|
except GitLabAuthError as e:
|
|
649
|
+
telemetry.track("provider_error", {"provider": "gitlab", "code": e.code})
|
|
639
650
|
ui.print_error(e.message)
|
|
640
651
|
ui.print_info("Open https://gitlab.com/-/user_settings/personal_access_tokens to create a new token.")
|
|
641
652
|
ui.print_end()
|
|
642
653
|
sys.exit(1)
|
|
643
654
|
except GitLabNotFoundError as e:
|
|
655
|
+
telemetry.track("provider_error", {"provider": "gitlab", "code": e.code})
|
|
644
656
|
ui.print_error(e.message)
|
|
645
657
|
ui.print_end()
|
|
646
658
|
sys.exit(1)
|
|
647
659
|
except GitLabNetworkError as e:
|
|
660
|
+
telemetry.track("provider_error", {"provider": "gitlab", "code": e.code})
|
|
648
661
|
ui.print_error(e.message)
|
|
649
662
|
ui.print_info("Check your internet connection and try again.")
|
|
650
663
|
ui.print_end()
|
|
651
664
|
sys.exit(1)
|
|
652
665
|
except GitLabError as e:
|
|
666
|
+
telemetry.track("provider_error", {"provider": "gitlab", "code": e.code})
|
|
653
667
|
ui.print_error(e.message)
|
|
654
668
|
ui.print_end()
|
|
655
669
|
sys.exit(1)
|
|
@@ -11,7 +11,7 @@ import json
|
|
|
11
11
|
import sys
|
|
12
12
|
from pathlib import Path
|
|
13
13
|
|
|
14
|
-
from . import __version__, budget, config, state
|
|
14
|
+
from . import __version__, budget, config, state, telemetry
|
|
15
15
|
from .backends.base import RunResult
|
|
16
16
|
from .backends.registry import (
|
|
17
17
|
API_PRESETS,
|
|
@@ -139,12 +139,21 @@ def cmd_list_bugs_json(args):
|
|
|
139
139
|
date_to=getattr(args, "date_to", None),
|
|
140
140
|
)
|
|
141
141
|
except ProviderNotImplementedError as e:
|
|
142
|
+
telemetry.track("provider_error", {"provider": provider.key, "code": e.code})
|
|
142
143
|
_err_from_gitlab(e)
|
|
143
144
|
except ProviderError as e:
|
|
145
|
+
telemetry.track("provider_error", {"provider": provider.key, "code": e.code})
|
|
144
146
|
_err_from_gitlab(e)
|
|
145
147
|
except Exception as e:
|
|
148
|
+
telemetry.track("unexpected_error", {"where": "list_bugs", "type": type(e).__name__})
|
|
146
149
|
_err(f"unexpected error: {e}", error_code="unexpected")
|
|
147
150
|
|
|
151
|
+
telemetry.track("bugs_listed", {
|
|
152
|
+
"provider": provider.key,
|
|
153
|
+
"count": len(issues),
|
|
154
|
+
"date_filter": bool(args.date or getattr(args, "date_from", None) or getattr(args, "date_to", None)),
|
|
155
|
+
})
|
|
156
|
+
|
|
148
157
|
payload_issues = []
|
|
149
158
|
for i in issues:
|
|
150
159
|
parsed = parse_issue(i)
|
|
@@ -238,8 +247,10 @@ def cmd_fix_issue(args):
|
|
|
238
247
|
try:
|
|
239
248
|
issues = provider.fetch_bugs(token=token, project_id=project_id, host=host)
|
|
240
249
|
except ProviderNotImplementedError as e:
|
|
250
|
+
telemetry.track("provider_error", {"provider": provider.key, "code": e.code})
|
|
241
251
|
_err_from_gitlab(e)
|
|
242
252
|
except ProviderError as e:
|
|
253
|
+
telemetry.track("provider_error", {"provider": provider.key, "code": e.code})
|
|
243
254
|
_err_from_gitlab(e)
|
|
244
255
|
target = next(
|
|
245
256
|
(i for i in issues if str(i.get("iid")) == str(issue_iid)),
|
|
@@ -261,6 +272,7 @@ def cmd_fix_issue(args):
|
|
|
261
272
|
budgets=cfg_budgets,
|
|
262
273
|
)
|
|
263
274
|
if not check.allowed and not getattr(args, "force", False):
|
|
275
|
+
telemetry.track("budget_blocked", {"backend": backend.name})
|
|
264
276
|
_emit({
|
|
265
277
|
"ok": False,
|
|
266
278
|
"code": "budget_exceeded",
|
|
@@ -275,6 +287,7 @@ def cmd_fix_issue(args):
|
|
|
275
287
|
sys.exit(1)
|
|
276
288
|
|
|
277
289
|
# Run backend
|
|
290
|
+
telemetry.track("fix_started", {"provider": provider.key, "backend": backend.name})
|
|
278
291
|
result: RunResult = backend.run(prompt, project_dir)
|
|
279
292
|
conf = evaluate_confidence(
|
|
280
293
|
result.stdout, project_dir,
|
|
@@ -283,6 +296,14 @@ def cmd_fix_issue(args):
|
|
|
283
296
|
)
|
|
284
297
|
|
|
285
298
|
success = result.ok and (conf.final_score >= 0.20 or conf.files_changed)
|
|
299
|
+
telemetry.track("fix_completed", {
|
|
300
|
+
"provider": provider.key,
|
|
301
|
+
"backend": backend.name,
|
|
302
|
+
"success": success,
|
|
303
|
+
"timed_out": result.timed_out,
|
|
304
|
+
"confidence": conf.label(),
|
|
305
|
+
"files_changed": len(conf.files_changed),
|
|
306
|
+
})
|
|
286
307
|
state.record_usage(
|
|
287
308
|
backend_name=backend.name,
|
|
288
309
|
tokens=check.estimated,
|
|
@@ -422,25 +443,31 @@ def main():
|
|
|
422
443
|
args.backend = args.backend or os.environ.get("BUGFIXER_BACKEND", "")
|
|
423
444
|
|
|
424
445
|
if args.backends_json:
|
|
446
|
+
telemetry.track("cli_start", {"mode": "backends_json"})
|
|
425
447
|
cmd_backends_json()
|
|
426
448
|
return
|
|
427
449
|
if args.providers_json:
|
|
428
450
|
cmd_providers_json()
|
|
429
451
|
return
|
|
430
452
|
if args.list_bugs_json:
|
|
453
|
+
telemetry.track("cli_start", {"mode": "list_bugs_json"})
|
|
431
454
|
cmd_list_bugs_json(args)
|
|
432
455
|
return
|
|
433
456
|
if args.fix_issue is not None:
|
|
457
|
+
telemetry.track("cli_start", {"mode": "fix_issue"})
|
|
434
458
|
cmd_fix_issue(args)
|
|
435
459
|
return
|
|
436
460
|
if args.config_get:
|
|
437
461
|
cmd_config_get()
|
|
438
462
|
return
|
|
439
463
|
if args.config_set:
|
|
464
|
+
telemetry.track("config_set", {"keys": ",".join(p.split("=")[0] for p in args.config_set)})
|
|
440
465
|
cmd_config_set(args)
|
|
441
466
|
return
|
|
442
467
|
|
|
443
468
|
# No JSON flag → fall through to interactive CLI
|
|
469
|
+
telemetry.maybe_show_notice()
|
|
470
|
+
telemetry.track("cli_start", {"mode": "interactive"})
|
|
444
471
|
from .cli import main as interactive_main
|
|
445
472
|
interactive_main()
|
|
446
473
|
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
"""Anonymous usage analytics via GA4 Measurement Protocol (Firebase Analytics).
|
|
2
|
+
|
|
3
|
+
Privacy rules (do not weaken):
|
|
4
|
+
- Sends ONLY event names + coarse metadata (provider key, backend name,
|
|
5
|
+
success bool, error codes, version). NEVER tokens, URLs, project names,
|
|
6
|
+
issue titles, file paths, or code.
|
|
7
|
+
- Anonymous random client id stored locally in ~/.bugfixer-analytics.json.
|
|
8
|
+
- Opt-out: `fixfleet --config-set telemetry.enabled=false`, or env
|
|
9
|
+
FIXFLEET_TELEMETRY=0 / DO_NOT_TRACK=1.
|
|
10
|
+
- Fire-and-forget on a daemon thread with a short timeout; a telemetry
|
|
11
|
+
failure must never break or slow the tool.
|
|
12
|
+
|
|
13
|
+
Setup: create a Firebase project with Analytics enabled, add a Web app,
|
|
14
|
+
then in GA4 Admin → Data Streams → Measurement Protocol API secrets create
|
|
15
|
+
a secret, and fill the two constants below before release.
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
import json
|
|
19
|
+
import os
|
|
20
|
+
import threading
|
|
21
|
+
import urllib.request
|
|
22
|
+
import uuid
|
|
23
|
+
from pathlib import Path
|
|
24
|
+
|
|
25
|
+
from . import __version__
|
|
26
|
+
|
|
27
|
+
# Fill these from your Firebase/GA4 project (see module docstring).
|
|
28
|
+
# Not filled = telemetry silently disabled.
|
|
29
|
+
MEASUREMENT_ID = "G-RB1CHG2YLD"
|
|
30
|
+
API_SECRET = "DORXWPkpQ2uDnsz0PzzF8A"
|
|
31
|
+
|
|
32
|
+
ENDPOINT = "https://www.google-analytics.com/mp/collect"
|
|
33
|
+
DEBUG_ENDPOINT = "https://www.google-analytics.com/debug/mp/collect"
|
|
34
|
+
|
|
35
|
+
_ID_PATH = Path.home() / ".bugfixer-analytics.json"
|
|
36
|
+
|
|
37
|
+
_NOTICE = (
|
|
38
|
+
"FixFleet collects anonymous usage events (event names + coarse metadata "
|
|
39
|
+
"only — never your code, tokens, URLs, or issue content) to find bugs and "
|
|
40
|
+
"improve the tool. Disable anytime:\n"
|
|
41
|
+
" fixfleet --config-set telemetry.enabled=false\n"
|
|
42
|
+
"This notice is shown once."
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def _load_id_state() -> dict:
|
|
47
|
+
try:
|
|
48
|
+
return json.loads(_ID_PATH.read_text())
|
|
49
|
+
except (OSError, json.JSONDecodeError):
|
|
50
|
+
return {}
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def _save_id_state(state: dict):
|
|
54
|
+
try:
|
|
55
|
+
_ID_PATH.write_text(json.dumps(state))
|
|
56
|
+
except OSError:
|
|
57
|
+
pass
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def client_id() -> str:
|
|
61
|
+
state = _load_id_state()
|
|
62
|
+
cid = state.get("client_id")
|
|
63
|
+
if not cid:
|
|
64
|
+
cid = str(uuid.uuid4())
|
|
65
|
+
state["client_id"] = cid
|
|
66
|
+
_save_id_state(state)
|
|
67
|
+
return cid
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def enabled() -> bool:
|
|
71
|
+
if not MEASUREMENT_ID or not API_SECRET:
|
|
72
|
+
return False
|
|
73
|
+
if os.environ.get("DO_NOT_TRACK", "") not in ("", "0"):
|
|
74
|
+
return False
|
|
75
|
+
if os.environ.get("FIXFLEET_TELEMETRY", "") in ("0", "false", "off"):
|
|
76
|
+
return False
|
|
77
|
+
try:
|
|
78
|
+
from . import config
|
|
79
|
+
t = config.load().get("telemetry")
|
|
80
|
+
if isinstance(t, dict) and t.get("enabled") is False:
|
|
81
|
+
return False
|
|
82
|
+
except Exception:
|
|
83
|
+
pass
|
|
84
|
+
return True
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def maybe_show_notice():
|
|
88
|
+
"""Print the one-time telemetry disclosure to stderr (interactive CLI only)."""
|
|
89
|
+
if not enabled():
|
|
90
|
+
return
|
|
91
|
+
state = _load_id_state()
|
|
92
|
+
if state.get("notified"):
|
|
93
|
+
return
|
|
94
|
+
state["notified"] = True
|
|
95
|
+
_save_id_state(state)
|
|
96
|
+
import sys
|
|
97
|
+
print(f"\n{_NOTICE}\n", file=sys.stderr)
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def _sanitize(params: dict) -> dict:
|
|
101
|
+
"""Keep only short scalar values — defense against accidental PII."""
|
|
102
|
+
out = {}
|
|
103
|
+
for k, v in (params or {}).items():
|
|
104
|
+
if isinstance(v, bool):
|
|
105
|
+
out[k] = "true" if v else "false"
|
|
106
|
+
elif isinstance(v, (int, float)):
|
|
107
|
+
out[k] = v
|
|
108
|
+
elif isinstance(v, str):
|
|
109
|
+
out[k] = v[:80]
|
|
110
|
+
return out
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def track(event_name: str, params: dict = None):
|
|
114
|
+
"""Send one GA4 event. Non-blocking, never raises."""
|
|
115
|
+
if not enabled():
|
|
116
|
+
return
|
|
117
|
+
|
|
118
|
+
body = {
|
|
119
|
+
"client_id": client_id(),
|
|
120
|
+
"events": [{
|
|
121
|
+
"name": event_name,
|
|
122
|
+
"params": {
|
|
123
|
+
"app_version": __version__,
|
|
124
|
+
"platform": os.name,
|
|
125
|
+
"engagement_time_msec": 1,
|
|
126
|
+
**_sanitize(params),
|
|
127
|
+
},
|
|
128
|
+
}],
|
|
129
|
+
}
|
|
130
|
+
debug = os.environ.get("FIXFLEET_TELEMETRY_DEBUG", "") == "1"
|
|
131
|
+
if debug:
|
|
132
|
+
body["events"][0]["params"]["debug_mode"] = 1
|
|
133
|
+
endpoint = DEBUG_ENDPOINT if debug else ENDPOINT
|
|
134
|
+
url = f"{endpoint}?measurement_id={MEASUREMENT_ID}&api_secret={API_SECRET}"
|
|
135
|
+
|
|
136
|
+
def _send():
|
|
137
|
+
try:
|
|
138
|
+
req = urllib.request.Request(
|
|
139
|
+
url,
|
|
140
|
+
data=json.dumps(body).encode(),
|
|
141
|
+
headers={"Content-Type": "application/json"},
|
|
142
|
+
method="POST",
|
|
143
|
+
)
|
|
144
|
+
with urllib.request.urlopen(req, timeout=3) as resp:
|
|
145
|
+
if debug:
|
|
146
|
+
import sys
|
|
147
|
+
print(f"[telemetry] {event_name}: {resp.read().decode()[:300]}",
|
|
148
|
+
file=sys.stderr)
|
|
149
|
+
except Exception:
|
|
150
|
+
pass # telemetry must never break the tool
|
|
151
|
+
|
|
152
|
+
threading.Thread(target=_send, daemon=True).start()
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: fixfleet
|
|
3
|
-
Version: 0.4.
|
|
3
|
+
Version: 0.4.2
|
|
4
4
|
Summary: Auto-fix bugs from GitHub, GitLab, Jira, Linear, Bitbucket, or Azure DevOps with AI agents — bring your own CLI or free API.
|
|
5
5
|
Author: Yash Koladiya
|
|
6
6
|
License-Expression: GPL-3.0-or-later
|
|
@@ -405,6 +405,17 @@ No. FixFleet runs **100% locally** on your machine. No FixFleet servers, no Azur
|
|
|
405
405
|
|
|
406
406
|
</details>
|
|
407
407
|
|
|
408
|
+
<details>
|
|
409
|
+
<summary><b>Does FixFleet collect any data?</b></summary>
|
|
410
|
+
|
|
411
|
+
Only anonymous usage analytics (via Google Analytics): event names and coarse metadata like provider key, backend name, and success/failure — **never your code, tokens, project URLs, or issue content**. Used purely to find bugs and see which trackers/backends need attention.
|
|
412
|
+
|
|
413
|
+
Opt out anytime:
|
|
414
|
+
- CLI: `fixfleet --config-set telemetry.enabled=false` (or set `DO_NOT_TRACK=1`)
|
|
415
|
+
- VSCode: uncheck `fixfleet.telemetry` in settings (the global VS Code telemetry setting is respected too)
|
|
416
|
+
|
|
417
|
+
</details>
|
|
418
|
+
|
|
408
419
|
<details>
|
|
409
420
|
<summary><b>What's the difference between the VSCode extension and the CLI?</b></summary>
|
|
410
421
|
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "fixfleet"
|
|
7
|
-
version = "0.4.
|
|
7
|
+
version = "0.4.2"
|
|
8
8
|
description = "Auto-fix bugs from GitHub, GitLab, Jira, Linear, Bitbucket, or Azure DevOps with AI agents — bring your own CLI or free API."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.9"
|
|
@@ -11,6 +11,9 @@ from pathlib import Path
|
|
|
11
11
|
# Make sure the package is importable
|
|
12
12
|
sys.path.insert(0, str(Path(__file__).resolve().parent.parent))
|
|
13
13
|
|
|
14
|
+
# Telemetry must never fire during tests.
|
|
15
|
+
os.environ["FIXFLEET_TELEMETRY"] = "0"
|
|
16
|
+
|
|
14
17
|
from bugfixer import budget, confidence
|
|
15
18
|
from bugfixer.backends import registry
|
|
16
19
|
from bugfixer.cli import _sanitize_path
|
|
@@ -9,12 +9,17 @@ provider-side anyway.
|
|
|
9
9
|
|
|
10
10
|
import io
|
|
11
11
|
import json
|
|
12
|
+
import os
|
|
12
13
|
import sys
|
|
13
14
|
import unittest
|
|
14
15
|
import urllib.error
|
|
15
16
|
from pathlib import Path
|
|
16
17
|
from unittest.mock import patch, MagicMock
|
|
17
18
|
|
|
19
|
+
# Telemetry must never fire during tests — its background thread would consume
|
|
20
|
+
# the mocked urlopen side_effect responses and poison unrelated assertions.
|
|
21
|
+
os.environ["FIXFLEET_TELEMETRY"] = "0"
|
|
22
|
+
|
|
18
23
|
sys.path.insert(0, str(Path(__file__).resolve().parent.parent))
|
|
19
24
|
|
|
20
25
|
from bugfixer.providers import (
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|