quickcall-integrations 0.2.0__tar.gz → 0.3.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {quickcall_integrations-0.2.0 → quickcall_integrations-0.3.0}/.gitignore +1 -0
- {quickcall_integrations-0.2.0 → quickcall_integrations-0.3.0}/PKG-INFO +1 -1
- {quickcall_integrations-0.2.0 → quickcall_integrations-0.3.0}/mcp_server/api_clients/github_client.py +162 -31
- {quickcall_integrations-0.2.0 → quickcall_integrations-0.3.0}/mcp_server/tools/github_tools.py +210 -6
- {quickcall_integrations-0.2.0 → quickcall_integrations-0.3.0}/plugins/quickcall/.claude-plugin/plugin.json +1 -1
- {quickcall_integrations-0.2.0 → quickcall_integrations-0.3.0}/pyproject.toml +3 -1
- {quickcall_integrations-0.2.0 → quickcall_integrations-0.3.0}/uv.lock +131 -1
- {quickcall_integrations-0.2.0 → quickcall_integrations-0.3.0}/.claude-plugin/marketplace.json +0 -0
- {quickcall_integrations-0.2.0 → quickcall_integrations-0.3.0}/.github/workflows/publish-pypi.yml +0 -0
- {quickcall_integrations-0.2.0 → quickcall_integrations-0.3.0}/.pre-commit-config.yaml +0 -0
- {quickcall_integrations-0.2.0 → quickcall_integrations-0.3.0}/Dockerfile +0 -0
- {quickcall_integrations-0.2.0 → quickcall_integrations-0.3.0}/README.md +0 -0
- {quickcall_integrations-0.2.0 → quickcall_integrations-0.3.0}/assets/logo.png +0 -0
- {quickcall_integrations-0.2.0 → quickcall_integrations-0.3.0}/mcp_server/__init__.py +0 -0
- {quickcall_integrations-0.2.0 → quickcall_integrations-0.3.0}/mcp_server/api_clients/__init__.py +0 -0
- {quickcall_integrations-0.2.0 → quickcall_integrations-0.3.0}/mcp_server/api_clients/slack_client.py +0 -0
- {quickcall_integrations-0.2.0 → quickcall_integrations-0.3.0}/mcp_server/auth/__init__.py +0 -0
- {quickcall_integrations-0.2.0 → quickcall_integrations-0.3.0}/mcp_server/auth/credentials.py +0 -0
- {quickcall_integrations-0.2.0 → quickcall_integrations-0.3.0}/mcp_server/auth/device_flow.py +0 -0
- {quickcall_integrations-0.2.0 → quickcall_integrations-0.3.0}/mcp_server/resources/__init__.py +0 -0
- {quickcall_integrations-0.2.0 → quickcall_integrations-0.3.0}/mcp_server/resources/slack_resources.py +0 -0
- {quickcall_integrations-0.2.0 → quickcall_integrations-0.3.0}/mcp_server/server.py +0 -0
- {quickcall_integrations-0.2.0 → quickcall_integrations-0.3.0}/mcp_server/tools/__init__.py +0 -0
- {quickcall_integrations-0.2.0 → quickcall_integrations-0.3.0}/mcp_server/tools/auth_tools.py +0 -0
- {quickcall_integrations-0.2.0 → quickcall_integrations-0.3.0}/mcp_server/tools/git_tools.py +0 -0
- {quickcall_integrations-0.2.0 → quickcall_integrations-0.3.0}/mcp_server/tools/slack_tools.py +0 -0
- {quickcall_integrations-0.2.0 → quickcall_integrations-0.3.0}/mcp_server/tools/utility_tools.py +0 -0
- {quickcall_integrations-0.2.0 → quickcall_integrations-0.3.0}/plugins/quickcall/commands/appraisal.md +0 -0
- {quickcall_integrations-0.2.0 → quickcall_integrations-0.3.0}/plugins/quickcall/commands/connect-github-pat.md +0 -0
- {quickcall_integrations-0.2.0 → quickcall_integrations-0.3.0}/plugins/quickcall/commands/connect.md +0 -0
- {quickcall_integrations-0.2.0 → quickcall_integrations-0.3.0}/plugins/quickcall/commands/slack-summary.md +0 -0
- {quickcall_integrations-0.2.0 → quickcall_integrations-0.3.0}/plugins/quickcall/commands/status.md +0 -0
- {quickcall_integrations-0.2.0 → quickcall_integrations-0.3.0}/plugins/quickcall/commands/updates.md +0 -0
- {quickcall_integrations-0.2.0 → quickcall_integrations-0.3.0}/requirements.txt +0 -0
- {quickcall_integrations-0.2.0 → quickcall_integrations-0.3.0}/tests/README.md +0 -0
- {quickcall_integrations-0.2.0 → quickcall_integrations-0.3.0}/tests/appraisal/__init__.py +0 -0
- {quickcall_integrations-0.2.0 → quickcall_integrations-0.3.0}/tests/appraisal/setup_test_data.py +0 -0
- {quickcall_integrations-0.2.0 → quickcall_integrations-0.3.0}/tests/test_integrations.py +0 -0
- {quickcall_integrations-0.2.0 → quickcall_integrations-0.3.0}/tests/test_tools.py +0 -0
|
@@ -6,6 +6,7 @@ Focuses on PRs and commits for minimal implementation.
|
|
|
6
6
|
"""
|
|
7
7
|
|
|
8
8
|
import logging
|
|
9
|
+
from concurrent.futures import ThreadPoolExecutor, as_completed
|
|
9
10
|
from typing import List, Optional, Dict, Any
|
|
10
11
|
from datetime import datetime
|
|
11
12
|
|
|
@@ -22,7 +23,7 @@ logger = logging.getLogger(__name__)
|
|
|
22
23
|
|
|
23
24
|
|
|
24
25
|
class Commit(BaseModel):
|
|
25
|
-
"""Represents a GitHub commit."""
|
|
26
|
+
"""Represents a GitHub commit (full details)."""
|
|
26
27
|
|
|
27
28
|
sha: str
|
|
28
29
|
message: str
|
|
@@ -31,8 +32,18 @@ class Commit(BaseModel):
|
|
|
31
32
|
html_url: str
|
|
32
33
|
|
|
33
34
|
|
|
35
|
+
class CommitSummary(BaseModel):
|
|
36
|
+
"""Represents a GitHub commit (summary - minimal fields for list operations)."""
|
|
37
|
+
|
|
38
|
+
sha: str
|
|
39
|
+
message_title: str # First line only
|
|
40
|
+
author: str
|
|
41
|
+
date: datetime
|
|
42
|
+
html_url: str
|
|
43
|
+
|
|
44
|
+
|
|
34
45
|
class PullRequest(BaseModel):
|
|
35
|
-
"""Represents a GitHub pull request."""
|
|
46
|
+
"""Represents a GitHub pull request (full details)."""
|
|
36
47
|
|
|
37
48
|
number: int
|
|
38
49
|
title: str
|
|
@@ -55,6 +66,18 @@ class PullRequest(BaseModel):
|
|
|
55
66
|
reviewers: List[str] = []
|
|
56
67
|
|
|
57
68
|
|
|
69
|
+
class PullRequestSummary(BaseModel):
|
|
70
|
+
"""Represents a GitHub pull request (summary - minimal fields for list operations)."""
|
|
71
|
+
|
|
72
|
+
number: int
|
|
73
|
+
title: str
|
|
74
|
+
state: str
|
|
75
|
+
author: str
|
|
76
|
+
created_at: datetime
|
|
77
|
+
merged_at: Optional[datetime] = None
|
|
78
|
+
html_url: str
|
|
79
|
+
|
|
80
|
+
|
|
58
81
|
class Repository(BaseModel):
|
|
59
82
|
"""Represents a GitHub repository."""
|
|
60
83
|
|
|
@@ -298,7 +321,8 @@ class GitHubClient:
|
|
|
298
321
|
repo: Optional[str] = None,
|
|
299
322
|
state: str = "open",
|
|
300
323
|
limit: int = 20,
|
|
301
|
-
|
|
324
|
+
detail_level: str = "summary",
|
|
325
|
+
) -> List[PullRequest] | List[PullRequestSummary]:
|
|
302
326
|
"""
|
|
303
327
|
List pull requests.
|
|
304
328
|
|
|
@@ -307,9 +331,11 @@ class GitHubClient:
|
|
|
307
331
|
repo: Repository name
|
|
308
332
|
state: PR state: 'open', 'closed', or 'all'
|
|
309
333
|
limit: Maximum PRs to return
|
|
334
|
+
detail_level: 'summary' for minimal fields (~200 bytes/PR),
|
|
335
|
+
'full' for all fields (~2KB/PR)
|
|
310
336
|
|
|
311
337
|
Returns:
|
|
312
|
-
List of pull requests
|
|
338
|
+
List of pull requests (summary or full based on detail_level)
|
|
313
339
|
"""
|
|
314
340
|
gh_repo = self._get_repo(owner, repo)
|
|
315
341
|
prs = []
|
|
@@ -319,7 +345,10 @@ class GitHubClient:
|
|
|
319
345
|
for i, pr in enumerate(pulls):
|
|
320
346
|
if i >= limit:
|
|
321
347
|
break
|
|
322
|
-
|
|
348
|
+
if detail_level == "full":
|
|
349
|
+
prs.append(self._convert_pr(pr))
|
|
350
|
+
else:
|
|
351
|
+
prs.append(self._convert_pr_summary(pr))
|
|
323
352
|
except IndexError:
|
|
324
353
|
# Empty repo or no PRs - return empty list
|
|
325
354
|
pass
|
|
@@ -353,7 +382,7 @@ class GitHubClient:
|
|
|
353
382
|
raise
|
|
354
383
|
|
|
355
384
|
def _convert_pr(self, pr) -> PullRequest:
|
|
356
|
-
"""Convert PyGithub PullRequest to Pydantic model."""
|
|
385
|
+
"""Convert PyGithub PullRequest to Pydantic model (full details)."""
|
|
357
386
|
return PullRequest(
|
|
358
387
|
number=pr.number,
|
|
359
388
|
title=pr.title,
|
|
@@ -376,6 +405,18 @@ class GitHubClient:
|
|
|
376
405
|
reviewers=[r.login for r in pr.requested_reviewers],
|
|
377
406
|
)
|
|
378
407
|
|
|
408
|
+
def _convert_pr_summary(self, pr) -> PullRequestSummary:
|
|
409
|
+
"""Convert PyGithub PullRequest to summary model (minimal fields)."""
|
|
410
|
+
return PullRequestSummary(
|
|
411
|
+
number=pr.number,
|
|
412
|
+
title=pr.title,
|
|
413
|
+
state=pr.state,
|
|
414
|
+
author=pr.user.login if pr.user else "unknown",
|
|
415
|
+
created_at=pr.created_at,
|
|
416
|
+
merged_at=pr.merged_at,
|
|
417
|
+
html_url=pr.html_url,
|
|
418
|
+
)
|
|
419
|
+
|
|
379
420
|
# ========================================================================
|
|
380
421
|
# Commit Operations
|
|
381
422
|
# ========================================================================
|
|
@@ -388,7 +429,8 @@ class GitHubClient:
|
|
|
388
429
|
author: Optional[str] = None,
|
|
389
430
|
since: Optional[str] = None,
|
|
390
431
|
limit: int = 20,
|
|
391
|
-
|
|
432
|
+
detail_level: str = "summary",
|
|
433
|
+
) -> List[Commit] | List[CommitSummary]:
|
|
392
434
|
"""
|
|
393
435
|
List commits.
|
|
394
436
|
|
|
@@ -399,9 +441,10 @@ class GitHubClient:
|
|
|
399
441
|
author: Filter by author username
|
|
400
442
|
since: ISO datetime - only commits after this date
|
|
401
443
|
limit: Maximum commits to return
|
|
444
|
+
detail_level: 'summary' for minimal fields, 'full' for all fields
|
|
402
445
|
|
|
403
446
|
Returns:
|
|
404
|
-
List of commits
|
|
447
|
+
List of commits (summary or full based on detail_level)
|
|
405
448
|
"""
|
|
406
449
|
gh_repo = self._get_repo(owner, repo)
|
|
407
450
|
|
|
@@ -427,15 +470,28 @@ class GitHubClient:
|
|
|
427
470
|
if author and author.lower() != commit_author.lower():
|
|
428
471
|
continue
|
|
429
472
|
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
473
|
+
if detail_level == "full":
|
|
474
|
+
commits.append(
|
|
475
|
+
Commit(
|
|
476
|
+
sha=commit.sha,
|
|
477
|
+
message=commit.commit.message,
|
|
478
|
+
author=commit_author,
|
|
479
|
+
date=commit.commit.author.date,
|
|
480
|
+
html_url=commit.html_url,
|
|
481
|
+
)
|
|
482
|
+
)
|
|
483
|
+
else:
|
|
484
|
+
# Summary: just first line of message
|
|
485
|
+
message_title = commit.commit.message.split("\n")[0][:100]
|
|
486
|
+
commits.append(
|
|
487
|
+
CommitSummary(
|
|
488
|
+
sha=commit.sha[:7], # Short SHA for summary
|
|
489
|
+
message_title=message_title,
|
|
490
|
+
author=commit_author,
|
|
491
|
+
date=commit.commit.author.date,
|
|
492
|
+
html_url=commit.html_url,
|
|
493
|
+
)
|
|
437
494
|
)
|
|
438
|
-
)
|
|
439
495
|
|
|
440
496
|
return commits
|
|
441
497
|
|
|
@@ -533,6 +589,7 @@ class GitHubClient:
|
|
|
533
589
|
org: Optional[str] = None,
|
|
534
590
|
repo: Optional[str] = None,
|
|
535
591
|
limit: int = 100,
|
|
592
|
+
detail_level: str = "summary",
|
|
536
593
|
) -> List[Dict[str, Any]]:
|
|
537
594
|
"""
|
|
538
595
|
Search for merged pull requests using GitHub Search API.
|
|
@@ -545,10 +602,11 @@ class GitHubClient:
|
|
|
545
602
|
org: GitHub org to search within
|
|
546
603
|
repo: Specific repo in "owner/repo" format (overrides org if specified)
|
|
547
604
|
limit: Maximum PRs to return (max 100 per page)
|
|
605
|
+
detail_level: 'summary' for minimal fields, 'full' for all fields
|
|
548
606
|
|
|
549
607
|
Returns:
|
|
550
|
-
List of merged PR dicts
|
|
551
|
-
|
|
608
|
+
List of merged PR dicts. Summary includes: number, title, merged_at,
|
|
609
|
+
repo, owner, html_url, author. Full adds: body, labels.
|
|
552
610
|
"""
|
|
553
611
|
# Build search query
|
|
554
612
|
query_parts = ["is:pr", "is:merged"]
|
|
@@ -596,19 +654,35 @@ class GitHubClient:
|
|
|
596
654
|
repo_owner = repo_url_parts[-2] if len(repo_url_parts) >= 2 else ""
|
|
597
655
|
repo_name = repo_url_parts[-1] if len(repo_url_parts) >= 1 else ""
|
|
598
656
|
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
657
|
+
if detail_level == "full":
|
|
658
|
+
prs.append(
|
|
659
|
+
{
|
|
660
|
+
"number": item["number"],
|
|
661
|
+
"title": item["title"],
|
|
662
|
+
"body": item.get("body") or "",
|
|
663
|
+
"merged_at": item.get("pull_request", {}).get("merged_at"),
|
|
664
|
+
"html_url": item["html_url"],
|
|
665
|
+
"labels": [
|
|
666
|
+
label["name"] for label in item.get("labels", [])
|
|
667
|
+
],
|
|
668
|
+
"repo": repo_name,
|
|
669
|
+
"owner": repo_owner,
|
|
670
|
+
"author": item.get("user", {}).get("login", "unknown"),
|
|
671
|
+
}
|
|
672
|
+
)
|
|
673
|
+
else:
|
|
674
|
+
# Summary: skip body and labels
|
|
675
|
+
prs.append(
|
|
676
|
+
{
|
|
677
|
+
"number": item["number"],
|
|
678
|
+
"title": item["title"],
|
|
679
|
+
"merged_at": item.get("pull_request", {}).get("merged_at"),
|
|
680
|
+
"html_url": item["html_url"],
|
|
681
|
+
"repo": repo_name,
|
|
682
|
+
"owner": repo_owner,
|
|
683
|
+
"author": item.get("user", {}).get("login", "unknown"),
|
|
684
|
+
}
|
|
685
|
+
)
|
|
612
686
|
|
|
613
687
|
return prs
|
|
614
688
|
|
|
@@ -618,3 +692,60 @@ class GitHubClient:
|
|
|
618
692
|
except Exception as e:
|
|
619
693
|
logger.error(f"Failed to search PRs: {e}")
|
|
620
694
|
raise
|
|
695
|
+
|
|
696
|
+
def fetch_prs_parallel(
|
|
697
|
+
self,
|
|
698
|
+
pr_refs: List[Dict[str, Any]],
|
|
699
|
+
max_workers: int = 10,
|
|
700
|
+
) -> List[Dict[str, Any]]:
|
|
701
|
+
"""
|
|
702
|
+
Fetch full PR details for multiple PRs in parallel.
|
|
703
|
+
|
|
704
|
+
Args:
|
|
705
|
+
pr_refs: List of dicts with 'owner', 'repo', 'number' keys
|
|
706
|
+
max_workers: Max concurrent requests (default: 10)
|
|
707
|
+
|
|
708
|
+
Returns:
|
|
709
|
+
List of full PR details with stats (additions, deletions, files)
|
|
710
|
+
"""
|
|
711
|
+
results = []
|
|
712
|
+
errors = []
|
|
713
|
+
|
|
714
|
+
def fetch_single_pr(pr_ref: Dict[str, Any]) -> Dict[str, Any] | None:
|
|
715
|
+
try:
|
|
716
|
+
owner = pr_ref["owner"]
|
|
717
|
+
repo = pr_ref["repo"]
|
|
718
|
+
number = pr_ref["number"]
|
|
719
|
+
pr = self.get_pr(number, owner=owner, repo=repo)
|
|
720
|
+
if pr:
|
|
721
|
+
pr_dict = pr.model_dump()
|
|
722
|
+
# Add owner/repo for context
|
|
723
|
+
pr_dict["owner"] = owner
|
|
724
|
+
pr_dict["repo"] = repo
|
|
725
|
+
return pr_dict
|
|
726
|
+
return None
|
|
727
|
+
except Exception as e:
|
|
728
|
+
logger.warning(f"Failed to fetch PR {pr_ref}: {e}")
|
|
729
|
+
return None
|
|
730
|
+
|
|
731
|
+
with ThreadPoolExecutor(max_workers=max_workers) as executor:
|
|
732
|
+
futures = {
|
|
733
|
+
executor.submit(fetch_single_pr, pr_ref): pr_ref for pr_ref in pr_refs
|
|
734
|
+
}
|
|
735
|
+
|
|
736
|
+
for future in as_completed(futures):
|
|
737
|
+
pr_ref = futures[future]
|
|
738
|
+
try:
|
|
739
|
+
result = future.result()
|
|
740
|
+
if result:
|
|
741
|
+
results.append(result)
|
|
742
|
+
else:
|
|
743
|
+
errors.append(pr_ref)
|
|
744
|
+
except Exception as e:
|
|
745
|
+
logger.error(f"Error fetching {pr_ref}: {e}")
|
|
746
|
+
errors.append(pr_ref)
|
|
747
|
+
|
|
748
|
+
if errors:
|
|
749
|
+
logger.warning(f"Failed to fetch {len(errors)} PRs: {errors[:5]}...")
|
|
750
|
+
|
|
751
|
+
return results
|
{quickcall_integrations-0.2.0 → quickcall_integrations-0.3.0}/mcp_server/tools/github_tools.py
RENAMED
|
@@ -11,7 +11,7 @@ PAT fallback is useful for:
|
|
|
11
11
|
- Testing and development
|
|
12
12
|
"""
|
|
13
13
|
|
|
14
|
-
from typing import Optional, Tuple
|
|
14
|
+
from typing import List, Optional, Tuple
|
|
15
15
|
import logging
|
|
16
16
|
|
|
17
17
|
from fastmcp import FastMCP
|
|
@@ -154,19 +154,34 @@ def create_github_tools(mcp: FastMCP) -> None:
|
|
|
154
154
|
default=20,
|
|
155
155
|
description="Maximum number of PRs to return (default: 20)",
|
|
156
156
|
),
|
|
157
|
+
detail_level: str = Field(
|
|
158
|
+
default="summary",
|
|
159
|
+
description="'summary' for minimal fields (~200 bytes/PR: number, title, state, author, merged_at, html_url), "
|
|
160
|
+
"'full' for all fields (~2KB/PR). Use 'summary' for large result sets, 'full' for detailed analysis.",
|
|
161
|
+
),
|
|
157
162
|
) -> dict:
|
|
158
163
|
"""
|
|
159
164
|
List pull requests for a GitHub repository.
|
|
160
165
|
|
|
161
166
|
Returns PRs sorted by last updated.
|
|
162
167
|
Requires QuickCall authentication with GitHub connected.
|
|
168
|
+
|
|
169
|
+
Use detail_level='summary' (default) to avoid context overflow with large result sets.
|
|
170
|
+
Use get_pr(number) to get full details for specific PRs when needed.
|
|
163
171
|
"""
|
|
164
172
|
try:
|
|
165
173
|
client = _get_client()
|
|
166
|
-
prs = client.list_prs(
|
|
174
|
+
prs = client.list_prs(
|
|
175
|
+
owner=owner,
|
|
176
|
+
repo=repo,
|
|
177
|
+
state=state,
|
|
178
|
+
limit=limit,
|
|
179
|
+
detail_level=detail_level,
|
|
180
|
+
)
|
|
167
181
|
|
|
168
182
|
return {
|
|
169
183
|
"count": len(prs),
|
|
184
|
+
"detail_level": detail_level,
|
|
170
185
|
"prs": [pr.model_dump() for pr in prs],
|
|
171
186
|
}
|
|
172
187
|
except ToolError:
|
|
@@ -241,12 +256,20 @@ def create_github_tools(mcp: FastMCP) -> None:
|
|
|
241
256
|
default=20,
|
|
242
257
|
description="Maximum number of commits to return (default: 20)",
|
|
243
258
|
),
|
|
259
|
+
detail_level: str = Field(
|
|
260
|
+
default="summary",
|
|
261
|
+
description="'summary' for minimal fields (short sha, message title, author, date, url), "
|
|
262
|
+
"'full' for all fields including full commit message. Use 'summary' for large result sets.",
|
|
263
|
+
),
|
|
244
264
|
) -> dict:
|
|
245
265
|
"""
|
|
246
266
|
List commits for a GitHub repository.
|
|
247
267
|
|
|
248
268
|
Returns commits sorted by date (newest first).
|
|
249
269
|
Requires QuickCall authentication with GitHub connected.
|
|
270
|
+
|
|
271
|
+
Use detail_level='summary' (default) to avoid context overflow with large result sets.
|
|
272
|
+
Use get_commit(sha) to get full details for specific commits when needed.
|
|
250
273
|
"""
|
|
251
274
|
try:
|
|
252
275
|
client = _get_client()
|
|
@@ -257,10 +280,12 @@ def create_github_tools(mcp: FastMCP) -> None:
|
|
|
257
280
|
author=author,
|
|
258
281
|
since=since,
|
|
259
282
|
limit=limit,
|
|
283
|
+
detail_level=detail_level,
|
|
260
284
|
)
|
|
261
285
|
|
|
262
286
|
return {
|
|
263
287
|
"count": len(commits),
|
|
288
|
+
"detail_level": detail_level,
|
|
264
289
|
"commits": [commit.model_dump() for commit in commits],
|
|
265
290
|
}
|
|
266
291
|
except ToolError:
|
|
@@ -370,6 +395,11 @@ def create_github_tools(mcp: FastMCP) -> None:
|
|
|
370
395
|
default=100,
|
|
371
396
|
description="Maximum PRs to return (default: 100)",
|
|
372
397
|
),
|
|
398
|
+
detail_level: str = Field(
|
|
399
|
+
default="summary",
|
|
400
|
+
description="'summary' for minimal fields (number, title, merged_at, repo, owner, html_url, author), "
|
|
401
|
+
"'full' adds body and labels. Use 'summary' for large result sets.",
|
|
402
|
+
),
|
|
373
403
|
) -> dict:
|
|
374
404
|
"""
|
|
375
405
|
Search for merged pull requests by author within a time period.
|
|
@@ -390,8 +420,8 @@ def create_github_tools(mcp: FastMCP) -> None:
|
|
|
390
420
|
|
|
391
421
|
3. SUMMARIZE for appraisal with accomplishments grouped by category
|
|
392
422
|
|
|
393
|
-
|
|
394
|
-
|
|
423
|
+
Use detail_level='summary' (default) to avoid context overflow with large result sets.
|
|
424
|
+
Use get_pr(number) to get full details for specific PRs when needed.
|
|
395
425
|
|
|
396
426
|
Requires QuickCall authentication with GitHub connected.
|
|
397
427
|
"""
|
|
@@ -399,9 +429,11 @@ def create_github_tools(mcp: FastMCP) -> None:
|
|
|
399
429
|
client = _get_client()
|
|
400
430
|
|
|
401
431
|
# Calculate since_date from days
|
|
402
|
-
from datetime import datetime, timedelta
|
|
432
|
+
from datetime import datetime, timedelta, timezone
|
|
403
433
|
|
|
404
|
-
since_date = (datetime.
|
|
434
|
+
since_date = (datetime.now(timezone.utc) - timedelta(days=days)).strftime(
|
|
435
|
+
"%Y-%m-%d"
|
|
436
|
+
)
|
|
405
437
|
|
|
406
438
|
# Use authenticated user if author not specified
|
|
407
439
|
if not author:
|
|
@@ -415,10 +447,12 @@ def create_github_tools(mcp: FastMCP) -> None:
|
|
|
415
447
|
org=org,
|
|
416
448
|
repo=repo,
|
|
417
449
|
limit=limit,
|
|
450
|
+
detail_level=detail_level,
|
|
418
451
|
)
|
|
419
452
|
|
|
420
453
|
return {
|
|
421
454
|
"count": len(prs),
|
|
455
|
+
"detail_level": detail_level,
|
|
422
456
|
"period": f"Last {days} days",
|
|
423
457
|
"author": author,
|
|
424
458
|
"org": org,
|
|
@@ -430,6 +464,176 @@ def create_github_tools(mcp: FastMCP) -> None:
|
|
|
430
464
|
except Exception as e:
|
|
431
465
|
raise ToolError(f"Failed to search merged PRs: {str(e)}")
|
|
432
466
|
|
|
467
|
+
@mcp.tool(tags={"github", "prs", "appraisal"})
|
|
468
|
+
def prepare_appraisal_data(
|
|
469
|
+
author: Optional[str] = Field(
|
|
470
|
+
default=None,
|
|
471
|
+
description="GitHub username. Defaults to authenticated user.",
|
|
472
|
+
),
|
|
473
|
+
days: int = Field(
|
|
474
|
+
default=180,
|
|
475
|
+
description="Number of days to look back (default: 180 for ~6 months)",
|
|
476
|
+
),
|
|
477
|
+
org: Optional[str] = Field(
|
|
478
|
+
default=None,
|
|
479
|
+
description="GitHub org to search within.",
|
|
480
|
+
),
|
|
481
|
+
repo: Optional[str] = Field(
|
|
482
|
+
default=None,
|
|
483
|
+
description="Specific repo in 'owner/repo' format.",
|
|
484
|
+
),
|
|
485
|
+
) -> dict:
|
|
486
|
+
"""
|
|
487
|
+
Prepare appraisal data by fetching ALL merged PRs with full details.
|
|
488
|
+
|
|
489
|
+
This tool:
|
|
490
|
+
1. Searches for all merged PRs by the author
|
|
491
|
+
2. Fetches FULL details (additions, deletions, files) for each PR IN PARALLEL
|
|
492
|
+
3. Dumps everything to a JSON file for later queries
|
|
493
|
+
4. Returns the file path + list of PR titles for Claude to review
|
|
494
|
+
|
|
495
|
+
USE THIS for appraisals instead of search_merged_prs + multiple get_pr calls.
|
|
496
|
+
After calling this, use get_appraisal_pr_details to get full info for specific PRs.
|
|
497
|
+
"""
|
|
498
|
+
import json
|
|
499
|
+
import tempfile
|
|
500
|
+
from datetime import datetime, timedelta, timezone
|
|
501
|
+
|
|
502
|
+
try:
|
|
503
|
+
client = _get_client()
|
|
504
|
+
|
|
505
|
+
since_date = (datetime.now(timezone.utc) - timedelta(days=days)).strftime(
|
|
506
|
+
"%Y-%m-%d"
|
|
507
|
+
)
|
|
508
|
+
|
|
509
|
+
# Use authenticated user if author not specified
|
|
510
|
+
if not author:
|
|
511
|
+
creds = get_credential_store().get_api_credentials()
|
|
512
|
+
if creds and creds.github_username:
|
|
513
|
+
author = creds.github_username
|
|
514
|
+
|
|
515
|
+
# Step 1: Get list of merged PRs
|
|
516
|
+
pr_list = client.search_merged_prs(
|
|
517
|
+
author=author,
|
|
518
|
+
since_date=since_date,
|
|
519
|
+
org=org,
|
|
520
|
+
repo=repo,
|
|
521
|
+
limit=100,
|
|
522
|
+
detail_level="full", # Get body/labels from search
|
|
523
|
+
)
|
|
524
|
+
|
|
525
|
+
if not pr_list:
|
|
526
|
+
return {
|
|
527
|
+
"count": 0,
|
|
528
|
+
"message": "No merged PRs found for the specified criteria",
|
|
529
|
+
"author": author,
|
|
530
|
+
"period": f"Last {days} days",
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
# Step 2: Prepare refs for parallel fetch
|
|
534
|
+
pr_refs = [
|
|
535
|
+
{"owner": pr["owner"], "repo": pr["repo"], "number": pr["number"]}
|
|
536
|
+
for pr in pr_list
|
|
537
|
+
]
|
|
538
|
+
|
|
539
|
+
# Step 3: Fetch full details in parallel
|
|
540
|
+
full_prs = client.fetch_prs_parallel(pr_refs, max_workers=10)
|
|
541
|
+
|
|
542
|
+
# Step 4: Merge search data with full PR data
|
|
543
|
+
# (search has body/labels, full PR has additions/deletions/files)
|
|
544
|
+
pr_lookup = {(pr["owner"], pr["repo"], pr["number"]): pr for pr in pr_list}
|
|
545
|
+
for pr in full_prs:
|
|
546
|
+
key = (pr["owner"], pr["repo"], pr["number"])
|
|
547
|
+
if key in pr_lookup:
|
|
548
|
+
# Add labels from search (not in PyGithub response for some reason)
|
|
549
|
+
search_pr = pr_lookup[key]
|
|
550
|
+
if "labels" in search_pr:
|
|
551
|
+
pr["labels"] = search_pr["labels"]
|
|
552
|
+
|
|
553
|
+
# Step 5: Dump to file
|
|
554
|
+
dump_data = {
|
|
555
|
+
"author": author,
|
|
556
|
+
"period": f"Last {days} days",
|
|
557
|
+
"org": org,
|
|
558
|
+
"repo": repo,
|
|
559
|
+
"fetched_at": datetime.now(timezone.utc).isoformat(),
|
|
560
|
+
"count": len(full_prs),
|
|
561
|
+
"prs": full_prs,
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
# Create temp file that persists
|
|
565
|
+
fd, file_path = tempfile.mkstemp(suffix=".json", prefix="appraisal_")
|
|
566
|
+
with open(file_path, "w") as f:
|
|
567
|
+
json.dump(dump_data, f, indent=2, default=str)
|
|
568
|
+
|
|
569
|
+
# Step 6: Return file path + just titles for Claude to scan
|
|
570
|
+
pr_titles = [
|
|
571
|
+
{
|
|
572
|
+
"number": pr["number"],
|
|
573
|
+
"title": pr["title"],
|
|
574
|
+
"repo": f"{pr['owner']}/{pr['repo']}",
|
|
575
|
+
}
|
|
576
|
+
for pr in full_prs
|
|
577
|
+
]
|
|
578
|
+
|
|
579
|
+
return {
|
|
580
|
+
"file_path": file_path,
|
|
581
|
+
"count": len(full_prs),
|
|
582
|
+
"author": author,
|
|
583
|
+
"period": f"Last {days} days",
|
|
584
|
+
"pr_titles": pr_titles,
|
|
585
|
+
"next_step": "Review titles above, then call "
|
|
586
|
+
"get_appraisal_pr_details(file_path, pr_numbers) for full details on selected PRs.",
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
except ToolError:
|
|
590
|
+
raise
|
|
591
|
+
except Exception as e:
|
|
592
|
+
raise ToolError(f"Failed to prepare appraisal data: {str(e)}")
|
|
593
|
+
|
|
594
|
+
@mcp.tool(tags={"github", "prs", "appraisal"})
|
|
595
|
+
def get_appraisal_pr_details(
|
|
596
|
+
file_path: str = Field(
|
|
597
|
+
...,
|
|
598
|
+
description="Path to the appraisal data file from prepare_appraisal_data",
|
|
599
|
+
),
|
|
600
|
+
pr_numbers: List[int] = Field(
|
|
601
|
+
..., description="List of PR numbers to get full details for"
|
|
602
|
+
),
|
|
603
|
+
) -> dict:
|
|
604
|
+
"""
|
|
605
|
+
Get full PR details from the appraisal data dump.
|
|
606
|
+
|
|
607
|
+
This reads from the local file created by prepare_appraisal_data.
|
|
608
|
+
NO API CALLS are made - all data comes from the cached dump.
|
|
609
|
+
|
|
610
|
+
Use this after prepare_appraisal_data to get full details for specific PRs
|
|
611
|
+
that Claude has identified as important for the appraisal.
|
|
612
|
+
"""
|
|
613
|
+
import json
|
|
614
|
+
|
|
615
|
+
try:
|
|
616
|
+
with open(file_path) as f:
|
|
617
|
+
data = json.load(f)
|
|
618
|
+
|
|
619
|
+
pr_numbers_set = set(pr_numbers)
|
|
620
|
+
selected_prs = [
|
|
621
|
+
pr for pr in data.get("prs", []) if pr["number"] in pr_numbers_set
|
|
622
|
+
]
|
|
623
|
+
|
|
624
|
+
return {
|
|
625
|
+
"count": len(selected_prs),
|
|
626
|
+
"requested": len(pr_numbers),
|
|
627
|
+
"prs": selected_prs,
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
except FileNotFoundError:
|
|
631
|
+
raise ToolError(f"Appraisal data file not found: {file_path}")
|
|
632
|
+
except json.JSONDecodeError:
|
|
633
|
+
raise ToolError(f"Invalid JSON in appraisal data file: {file_path}")
|
|
634
|
+
except Exception as e:
|
|
635
|
+
raise ToolError(f"Failed to read appraisal data: {str(e)}")
|
|
636
|
+
|
|
433
637
|
@mcp.tool(tags={"github", "status"})
|
|
434
638
|
def check_github_connection() -> dict:
|
|
435
639
|
"""
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "quickcall",
|
|
3
3
|
"description": "Integrate quickcall into dev workflows - eliminate interruptions for developers. Ask about your work, get instant answers. No more context switching.",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.6.0",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Sagar Sarkale"
|
|
7
7
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "quickcall-integrations"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.3.0"
|
|
4
4
|
description = "MCP server with developer integrations for Claude Code and Cursor"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
requires-python = ">=3.10"
|
|
@@ -27,4 +27,6 @@ dev = [
|
|
|
27
27
|
"black>=25.12.0",
|
|
28
28
|
"mcp>=1.9.0",
|
|
29
29
|
"rich>=13.0.0",
|
|
30
|
+
"ruff>=0.14.11",
|
|
31
|
+
"twine>=6.2.0",
|
|
30
32
|
]
|
|
@@ -571,6 +571,18 @@ wheels = [
|
|
|
571
571
|
{ url = "https://files.pythonhosted.org/packages/d2/fd/6668e5aec43ab844de6fc74927e155a3b37bf40d7c3790e49fc0406b6578/httpx_sse-0.4.3-py3-none-any.whl", hash = "sha256:0ac1c9fe3c0afad2e0ebb25a934a59f4c7823b60792691f779fad2c5568830fc", size = 8960, upload-time = "2025-10-10T21:48:21.158Z" },
|
|
572
572
|
]
|
|
573
573
|
|
|
574
|
+
[[package]]
|
|
575
|
+
name = "id"
|
|
576
|
+
version = "1.5.0"
|
|
577
|
+
source = { registry = "https://pypi.org/simple" }
|
|
578
|
+
dependencies = [
|
|
579
|
+
{ name = "requests" },
|
|
580
|
+
]
|
|
581
|
+
sdist = { url = "https://files.pythonhosted.org/packages/22/11/102da08f88412d875fa2f1a9a469ff7ad4c874b0ca6fed0048fe385bdb3d/id-1.5.0.tar.gz", hash = "sha256:292cb8a49eacbbdbce97244f47a97b4c62540169c976552e497fd57df0734c1d", size = 15237, upload-time = "2024-12-04T19:53:05.575Z" }
|
|
582
|
+
wheels = [
|
|
583
|
+
{ url = "https://files.pythonhosted.org/packages/9f/cb/18326d2d89ad3b0dd143da971e77afd1e6ca6674f1b1c3df4b6bec6279fc/id-1.5.0-py3-none-any.whl", hash = "sha256:f1434e1cef91f2cbb8a4ec64663d5a23b9ed43ef44c4c957d02583d61714c658", size = 13611, upload-time = "2024-12-04T19:53:03.02Z" },
|
|
584
|
+
]
|
|
585
|
+
|
|
574
586
|
[[package]]
|
|
575
587
|
name = "idna"
|
|
576
588
|
version = "3.11"
|
|
@@ -835,6 +847,39 @@ wheels = [
|
|
|
835
847
|
{ url = "https://files.pythonhosted.org/packages/79/7b/2c79738432f5c924bef5071f933bcc9efd0473bac3b4aa584a6f7c1c8df8/mypy_extensions-1.1.0-py3-none-any.whl", hash = "sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505", size = 4963, upload-time = "2025-04-22T14:54:22.983Z" },
|
|
836
848
|
]
|
|
837
849
|
|
|
850
|
+
[[package]]
|
|
851
|
+
name = "nh3"
|
|
852
|
+
version = "0.3.2"
|
|
853
|
+
source = { registry = "https://pypi.org/simple" }
|
|
854
|
+
sdist = { url = "https://files.pythonhosted.org/packages/ca/a5/34c26015d3a434409f4d2a1cd8821a06c05238703f49283ffeb937bef093/nh3-0.3.2.tar.gz", hash = "sha256:f394759a06df8b685a4ebfb1874fb67a9cbfd58c64fc5ed587a663c0e63ec376", size = 19288, upload-time = "2025-10-30T11:17:45.948Z" }
|
|
855
|
+
wheels = [
|
|
856
|
+
{ url = "https://files.pythonhosted.org/packages/5b/01/a1eda067c0ba823e5e2bb033864ae4854549e49fb6f3407d2da949106bfb/nh3-0.3.2-cp314-cp314t-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:d18957a90806d943d141cc5e4a0fefa1d77cf0d7a156878bf9a66eed52c9cc7d", size = 1419839, upload-time = "2025-10-30T11:17:09.956Z" },
|
|
857
|
+
{ url = "https://files.pythonhosted.org/packages/30/57/07826ff65d59e7e9cc789ef1dc405f660cabd7458a1864ab58aefa17411b/nh3-0.3.2-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:45c953e57028c31d473d6b648552d9cab1efe20a42ad139d78e11d8f42a36130", size = 791183, upload-time = "2025-10-30T11:17:11.99Z" },
|
|
858
|
+
{ url = "https://files.pythonhosted.org/packages/af/2f/e8a86f861ad83f3bb5455f596d5c802e34fcdb8c53a489083a70fd301333/nh3-0.3.2-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2c9850041b77a9147d6bbd6dbbf13eeec7009eb60b44e83f07fcb2910075bf9b", size = 829127, upload-time = "2025-10-30T11:17:13.192Z" },
|
|
859
|
+
{ url = "https://files.pythonhosted.org/packages/d8/97/77aef4daf0479754e8e90c7f8f48f3b7b8725a3b8c0df45f2258017a6895/nh3-0.3.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:403c11563e50b915d0efdb622866d1d9e4506bce590ef7da57789bf71dd148b5", size = 997131, upload-time = "2025-10-30T11:17:14.677Z" },
|
|
860
|
+
{ url = "https://files.pythonhosted.org/packages/41/ee/fd8140e4df9d52143e89951dd0d797f5546004c6043285289fbbe3112293/nh3-0.3.2-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:0dca4365db62b2d71ff1620ee4f800c4729849906c5dd504ee1a7b2389558e31", size = 1068783, upload-time = "2025-10-30T11:17:15.861Z" },
|
|
861
|
+
{ url = "https://files.pythonhosted.org/packages/87/64/bdd9631779e2d588b08391f7555828f352e7f6427889daf2fa424bfc90c9/nh3-0.3.2-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:0fe7ee035dd7b2290715baf29cb27167dddd2ff70ea7d052c958dbd80d323c99", size = 994732, upload-time = "2025-10-30T11:17:17.155Z" },
|
|
862
|
+
{ url = "https://files.pythonhosted.org/packages/79/66/90190033654f1f28ca98e3d76b8be1194505583f9426b0dcde782a3970a2/nh3-0.3.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:a40202fd58e49129764f025bbaae77028e420f1d5b3c8e6f6fd3a6490d513868", size = 975997, upload-time = "2025-10-30T11:17:18.77Z" },
|
|
863
|
+
{ url = "https://files.pythonhosted.org/packages/34/30/ebf8e2e8d71fdb5a5d5d8836207177aed1682df819cbde7f42f16898946c/nh3-0.3.2-cp314-cp314t-win32.whl", hash = "sha256:1f9ba555a797dbdcd844b89523f29cdc90973d8bd2e836ea6b962cf567cadd93", size = 583364, upload-time = "2025-10-30T11:17:20.286Z" },
|
|
864
|
+
{ url = "https://files.pythonhosted.org/packages/94/ae/95c52b5a75da429f11ca8902c2128f64daafdc77758d370e4cc310ecda55/nh3-0.3.2-cp314-cp314t-win_amd64.whl", hash = "sha256:dce4248edc427c9b79261f3e6e2b3ecbdd9b88c267012168b4a7b3fc6fd41d13", size = 589982, upload-time = "2025-10-30T11:17:21.384Z" },
|
|
865
|
+
{ url = "https://files.pythonhosted.org/packages/b4/bd/c7d862a4381b95f2469704de32c0ad419def0f4a84b7a138a79532238114/nh3-0.3.2-cp314-cp314t-win_arm64.whl", hash = "sha256:019ecbd007536b67fdf76fab411b648fb64e2257ca3262ec80c3425c24028c80", size = 577126, upload-time = "2025-10-30T11:17:22.755Z" },
|
|
866
|
+
{ url = "https://files.pythonhosted.org/packages/b6/3e/f5a5cc2885c24be13e9b937441bd16a012ac34a657fe05e58927e8af8b7a/nh3-0.3.2-cp38-abi3-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:7064ccf5ace75825bd7bf57859daaaf16ed28660c1c6b306b649a9eda4b54b1e", size = 1431980, upload-time = "2025-10-30T11:17:25.457Z" },
|
|
867
|
+
{ url = "https://files.pythonhosted.org/packages/7f/f7/529a99324d7ef055de88b690858f4189379708abae92ace799365a797b7f/nh3-0.3.2-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c8745454cdd28bbbc90861b80a0111a195b0e3961b9fa2e672be89eb199fa5d8", size = 820805, upload-time = "2025-10-30T11:17:26.98Z" },
|
|
868
|
+
{ url = "https://files.pythonhosted.org/packages/3d/62/19b7c50ccd1fa7d0764822d2cea8f2a320f2fd77474c7a1805cb22cf69b0/nh3-0.3.2-cp38-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:72d67c25a84579f4a432c065e8b4274e53b7cf1df8f792cf846abfe2c3090866", size = 803527, upload-time = "2025-10-30T11:17:28.284Z" },
|
|
869
|
+
{ url = "https://files.pythonhosted.org/packages/4a/ca/f022273bab5440abff6302731a49410c5ef66b1a9502ba3fbb2df998d9ff/nh3-0.3.2-cp38-abi3-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:13398e676a14d6233f372c75f52d5ae74f98210172991f7a3142a736bd92b131", size = 1051674, upload-time = "2025-10-30T11:17:29.909Z" },
|
|
870
|
+
{ url = "https://files.pythonhosted.org/packages/fa/f7/5728e3b32a11daf5bd21cf71d91c463f74305938bc3eb9e0ac1ce141646e/nh3-0.3.2-cp38-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:03d617e5c8aa7331bd2659c654e021caf9bba704b109e7b2b28b039a00949fe5", size = 1004737, upload-time = "2025-10-30T11:17:31.205Z" },
|
|
871
|
+
{ url = "https://files.pythonhosted.org/packages/53/7f/f17e0dba0a99cee29e6cee6d4d52340ef9cb1f8a06946d3a01eb7ec2fb01/nh3-0.3.2-cp38-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f2f55c4d2d5a207e74eefe4d828067bbb01300e06e2a7436142f915c5928de07", size = 911745, upload-time = "2025-10-30T11:17:32.945Z" },
|
|
872
|
+
{ url = "https://files.pythonhosted.org/packages/42/0f/c76bf3dba22c73c38e9b1113b017cf163f7696f50e003404ec5ecdb1e8a6/nh3-0.3.2-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7bb18403f02b655a1bbe4e3a4696c2ae1d6ae8f5991f7cacb684b1ae27e6c9f7", size = 797184, upload-time = "2025-10-30T11:17:34.226Z" },
|
|
873
|
+
{ url = "https://files.pythonhosted.org/packages/08/a1/73d8250f888fb0ddf1b119b139c382f8903d8bb0c5bd1f64afc7e38dad1d/nh3-0.3.2-cp38-abi3-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6d66f41672eb4060cf87c037f760bdbc6847852ca9ef8e9c5a5da18f090abf87", size = 838556, upload-time = "2025-10-30T11:17:35.875Z" },
|
|
874
|
+
{ url = "https://files.pythonhosted.org/packages/d1/09/deb57f1fb656a7a5192497f4a287b0ade5a2ff6b5d5de4736d13ef6d2c1f/nh3-0.3.2-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:f97f8b25cb2681d25e2338148159447e4d689aafdccfcf19e61ff7db3905768a", size = 1006695, upload-time = "2025-10-30T11:17:37.071Z" },
|
|
875
|
+
{ url = "https://files.pythonhosted.org/packages/b6/61/8f4d41c4ccdac30e4b1a4fa7be4b0f9914d8314a5058472f84c8e101a418/nh3-0.3.2-cp38-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:2ab70e8c6c7d2ce953d2a58102eefa90c2d0a5ed7aa40c7e29a487bc5e613131", size = 1075471, upload-time = "2025-10-30T11:17:38.225Z" },
|
|
876
|
+
{ url = "https://files.pythonhosted.org/packages/b0/c6/966aec0cb4705e69f6c3580422c239205d5d4d0e50fac380b21e87b6cf1b/nh3-0.3.2-cp38-abi3-musllinux_1_2_i686.whl", hash = "sha256:1710f3901cd6440ca92494ba2eb6dc260f829fa8d9196b659fa10de825610ce0", size = 1002439, upload-time = "2025-10-30T11:17:39.553Z" },
|
|
877
|
+
{ url = "https://files.pythonhosted.org/packages/e2/c8/97a2d5f7a314cce2c5c49f30c6f161b7f3617960ade4bfc2fd1ee092cb20/nh3-0.3.2-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:91e9b001101fb4500a2aafe3e7c92928d85242d38bf5ac0aba0b7480da0a4cd6", size = 987439, upload-time = "2025-10-30T11:17:40.81Z" },
|
|
878
|
+
{ url = "https://files.pythonhosted.org/packages/0d/95/2d6fc6461687d7a171f087995247dec33e8749a562bfadd85fb5dbf37a11/nh3-0.3.2-cp38-abi3-win32.whl", hash = "sha256:169db03df90da63286e0560ea0efa9b6f3b59844a9735514a1d47e6bb2c8c61b", size = 589826, upload-time = "2025-10-30T11:17:42.239Z" },
|
|
879
|
+
{ url = "https://files.pythonhosted.org/packages/64/9a/1a1c154f10a575d20dd634e5697805e589bbdb7673a0ad00e8da90044ba7/nh3-0.3.2-cp38-abi3-win_amd64.whl", hash = "sha256:562da3dca7a17f9077593214a9781a94b8d76de4f158f8c895e62f09573945fe", size = 596406, upload-time = "2025-10-30T11:17:43.773Z" },
|
|
880
|
+
{ url = "https://files.pythonhosted.org/packages/9e/7e/a96255f63b7aef032cbee8fc4d6e37def72e3aaedc1f72759235e8f13cb1/nh3-0.3.2-cp38-abi3-win_arm64.whl", hash = "sha256:cf5964d54edd405e68583114a7cba929468bcd7db5e676ae38ee954de1cfc104", size = 584162, upload-time = "2025-10-30T11:17:44.96Z" },
|
|
881
|
+
]
|
|
882
|
+
|
|
838
883
|
[[package]]
|
|
839
884
|
name = "openapi-pydantic"
|
|
840
885
|
version = "0.5.1"
|
|
@@ -1414,7 +1459,7 @@ wheels = [
|
|
|
1414
1459
|
|
|
1415
1460
|
[[package]]
|
|
1416
1461
|
name = "quickcall-integrations"
|
|
1417
|
-
version = "0.
|
|
1462
|
+
version = "0.3.0"
|
|
1418
1463
|
source = { editable = "." }
|
|
1419
1464
|
dependencies = [
|
|
1420
1465
|
{ name = "fastmcp" },
|
|
@@ -1429,6 +1474,8 @@ dev = [
|
|
|
1429
1474
|
{ name = "black" },
|
|
1430
1475
|
{ name = "mcp" },
|
|
1431
1476
|
{ name = "rich" },
|
|
1477
|
+
{ name = "ruff" },
|
|
1478
|
+
{ name = "twine" },
|
|
1432
1479
|
]
|
|
1433
1480
|
|
|
1434
1481
|
[package.metadata]
|
|
@@ -1445,6 +1492,8 @@ dev = [
|
|
|
1445
1492
|
{ name = "black", specifier = ">=25.12.0" },
|
|
1446
1493
|
{ name = "mcp", specifier = ">=1.9.0" },
|
|
1447
1494
|
{ name = "rich", specifier = ">=13.0.0" },
|
|
1495
|
+
{ name = "ruff", specifier = ">=0.14.11" },
|
|
1496
|
+
{ name = "twine", specifier = ">=6.2.0" },
|
|
1448
1497
|
]
|
|
1449
1498
|
|
|
1450
1499
|
[[package]]
|
|
@@ -1537,6 +1586,20 @@ wheels = [
|
|
|
1537
1586
|
{ url = "https://files.pythonhosted.org/packages/b1/ad/fa2d3e5c29a04ead7eaa731c7cd1f30f9ec3c77b3a578fdf90280797cbcb/rapidfuzz-3.14.3-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:56fefb4382bb12250f164250240b9dd7772e41c5c8ae976fd598a32292449cc5", size = 1511361, upload-time = "2025-11-01T11:54:49.057Z" },
|
|
1538
1587
|
]
|
|
1539
1588
|
|
|
1589
|
+
[[package]]
|
|
1590
|
+
name = "readme-renderer"
|
|
1591
|
+
version = "44.0"
|
|
1592
|
+
source = { registry = "https://pypi.org/simple" }
|
|
1593
|
+
dependencies = [
|
|
1594
|
+
{ name = "docutils" },
|
|
1595
|
+
{ name = "nh3" },
|
|
1596
|
+
{ name = "pygments" },
|
|
1597
|
+
]
|
|
1598
|
+
sdist = { url = "https://files.pythonhosted.org/packages/5a/a9/104ec9234c8448c4379768221ea6df01260cd6c2ce13182d4eac531c8342/readme_renderer-44.0.tar.gz", hash = "sha256:8712034eabbfa6805cacf1402b4eeb2a73028f72d1166d6f5cb7f9c047c5d1e1", size = 32056, upload-time = "2024-07-08T15:00:57.805Z" }
|
|
1599
|
+
wheels = [
|
|
1600
|
+
{ url = "https://files.pythonhosted.org/packages/e1/67/921ec3024056483db83953ae8e48079ad62b92db7880013ca77632921dd0/readme_renderer-44.0-py3-none-any.whl", hash = "sha256:2fbca89b81a08526aadf1357a8c2ae889ec05fb03f5da67f9769c9a592166151", size = 13310, upload-time = "2024-07-08T15:00:56.577Z" },
|
|
1601
|
+
]
|
|
1602
|
+
|
|
1540
1603
|
[[package]]
|
|
1541
1604
|
name = "redis"
|
|
1542
1605
|
version = "7.1.0"
|
|
@@ -1578,6 +1641,27 @@ wheels = [
|
|
|
1578
1641
|
{ url = "https://files.pythonhosted.org/packages/1e/db/4254e3eabe8020b458f1a747140d32277ec7a271daf1d235b70dc0b4e6e3/requests-2.32.5-py3-none-any.whl", hash = "sha256:2462f94637a34fd532264295e186976db0f5d453d1cdd31473c85a6a161affb6", size = 64738, upload-time = "2025-08-18T20:46:00.542Z" },
|
|
1579
1642
|
]
|
|
1580
1643
|
|
|
1644
|
+
[[package]]
|
|
1645
|
+
name = "requests-toolbelt"
|
|
1646
|
+
version = "1.0.0"
|
|
1647
|
+
source = { registry = "https://pypi.org/simple" }
|
|
1648
|
+
dependencies = [
|
|
1649
|
+
{ name = "requests" },
|
|
1650
|
+
]
|
|
1651
|
+
sdist = { url = "https://files.pythonhosted.org/packages/f3/61/d7545dafb7ac2230c70d38d31cbfe4cc64f7144dc41f6e4e4b78ecd9f5bb/requests-toolbelt-1.0.0.tar.gz", hash = "sha256:7681a0a3d047012b5bdc0ee37d7f8f07ebe76ab08caeccfc3921ce23c88d5bc6", size = 206888, upload-time = "2023-05-01T04:11:33.229Z" }
|
|
1652
|
+
wheels = [
|
|
1653
|
+
{ url = "https://files.pythonhosted.org/packages/3f/51/d4db610ef29373b879047326cbf6fa98b6c1969d6f6dc423279de2b1be2c/requests_toolbelt-1.0.0-py2.py3-none-any.whl", hash = "sha256:cccfdd665f0a24fcf4726e690f65639d272bb0637b9b92dfd91a5568ccf6bd06", size = 54481, upload-time = "2023-05-01T04:11:28.427Z" },
|
|
1654
|
+
]
|
|
1655
|
+
|
|
1656
|
+
[[package]]
|
|
1657
|
+
name = "rfc3986"
|
|
1658
|
+
version = "2.0.0"
|
|
1659
|
+
source = { registry = "https://pypi.org/simple" }
|
|
1660
|
+
sdist = { url = "https://files.pythonhosted.org/packages/85/40/1520d68bfa07ab5a6f065a186815fb6610c86fe957bc065754e47f7b0840/rfc3986-2.0.0.tar.gz", hash = "sha256:97aacf9dbd4bfd829baad6e6309fa6573aaf1be3f6fa735c8ab05e46cecb261c", size = 49026, upload-time = "2022-01-10T00:52:30.832Z" }
|
|
1661
|
+
wheels = [
|
|
1662
|
+
{ url = "https://files.pythonhosted.org/packages/ff/9a/9afaade874b2fa6c752c36f1548f718b5b83af81ed9b76628329dab81c1b/rfc3986-2.0.0-py2.py3-none-any.whl", hash = "sha256:50b1502b60e289cb37883f3dfd34532b8873c7de9f49bb546641ce9cbd256ebd", size = 31326, upload-time = "2022-01-10T00:52:29.594Z" },
|
|
1663
|
+
]
|
|
1664
|
+
|
|
1581
1665
|
[[package]]
|
|
1582
1666
|
name = "rich"
|
|
1583
1667
|
version = "14.2.0"
|
|
@@ -1726,6 +1810,32 @@ wheels = [
|
|
|
1726
1810
|
{ url = "https://files.pythonhosted.org/packages/d1/b7/b95708304cd49b7b6f82fdd039f1748b66ec2b21d6a45180910802f1abf1/rpds_py-0.30.0-pp311-pypy311_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:ac37f9f516c51e5753f27dfdef11a88330f04de2d564be3991384b2f3535d02e", size = 562191, upload-time = "2025-11-30T20:24:36.853Z" },
|
|
1727
1811
|
]
|
|
1728
1812
|
|
|
1813
|
+
[[package]]
|
|
1814
|
+
name = "ruff"
|
|
1815
|
+
version = "0.14.11"
|
|
1816
|
+
source = { registry = "https://pypi.org/simple" }
|
|
1817
|
+
sdist = { url = "https://files.pythonhosted.org/packages/d4/77/9a7fe084d268f8855d493e5031ea03fa0af8cc05887f638bf1c4e3363eb8/ruff-0.14.11.tar.gz", hash = "sha256:f6dc463bfa5c07a59b1ff2c3b9767373e541346ea105503b4c0369c520a66958", size = 5993417, upload-time = "2026-01-08T19:11:58.322Z" }
|
|
1818
|
+
wheels = [
|
|
1819
|
+
{ url = "https://files.pythonhosted.org/packages/f0/a6/a4c40a5aaa7e331f245d2dc1ac8ece306681f52b636b40ef87c88b9f7afd/ruff-0.14.11-py3-none-linux_armv6l.whl", hash = "sha256:f6ff2d95cbd335841a7217bdfd9c1d2e44eac2c584197ab1385579d55ff8830e", size = 12951208, upload-time = "2026-01-08T19:12:09.218Z" },
|
|
1820
|
+
{ url = "https://files.pythonhosted.org/packages/5c/5c/360a35cb7204b328b685d3129c08aca24765ff92b5a7efedbdd6c150d555/ruff-0.14.11-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:6f6eb5c1c8033680f4172ea9c8d3706c156223010b8b97b05e82c59bdc774ee6", size = 13330075, upload-time = "2026-01-08T19:12:02.549Z" },
|
|
1821
|
+
{ url = "https://files.pythonhosted.org/packages/1b/9e/0cc2f1be7a7d33cae541824cf3f95b4ff40d03557b575912b5b70273c9ec/ruff-0.14.11-py3-none-macosx_11_0_arm64.whl", hash = "sha256:f2fc34cc896f90080fca01259f96c566f74069a04b25b6205d55379d12a6855e", size = 12257809, upload-time = "2026-01-08T19:12:00.366Z" },
|
|
1822
|
+
{ url = "https://files.pythonhosted.org/packages/a7/e5/5faab97c15bb75228d9f74637e775d26ac703cc2b4898564c01ab3637c02/ruff-0.14.11-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:53386375001773ae812b43205d6064dae49ff0968774e6befe16a994fc233caa", size = 12678447, upload-time = "2026-01-08T19:12:13.899Z" },
|
|
1823
|
+
{ url = "https://files.pythonhosted.org/packages/1b/33/e9767f60a2bef779fb5855cab0af76c488e0ce90f7bb7b8a45c8a2ba4178/ruff-0.14.11-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a697737dce1ca97a0a55b5ff0434ee7205943d4874d638fe3ae66166ff46edbe", size = 12758560, upload-time = "2026-01-08T19:11:42.55Z" },
|
|
1824
|
+
{ url = "https://files.pythonhosted.org/packages/eb/84/4c6cf627a21462bb5102f7be2a320b084228ff26e105510cd2255ea868e5/ruff-0.14.11-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6845ca1da8ab81ab1dce755a32ad13f1db72e7fba27c486d5d90d65e04d17b8f", size = 13599296, upload-time = "2026-01-08T19:11:30.371Z" },
|
|
1825
|
+
{ url = "https://files.pythonhosted.org/packages/88/e1/92b5ed7ea66d849f6157e695dc23d5d6d982bd6aa8d077895652c38a7cae/ruff-0.14.11-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:e36ce2fd31b54065ec6f76cb08d60159e1b32bdf08507862e32f47e6dde8bcbf", size = 15048981, upload-time = "2026-01-08T19:12:04.742Z" },
|
|
1826
|
+
{ url = "https://files.pythonhosted.org/packages/61/df/c1bd30992615ac17c2fb64b8a7376ca22c04a70555b5d05b8f717163cf9f/ruff-0.14.11-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:590bcc0e2097ecf74e62a5c10a6b71f008ad82eb97b0a0079e85defe19fe74d9", size = 14633183, upload-time = "2026-01-08T19:11:40.069Z" },
|
|
1827
|
+
{ url = "https://files.pythonhosted.org/packages/04/e9/fe552902f25013dd28a5428a42347d9ad20c4b534834a325a28305747d64/ruff-0.14.11-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:53fe71125fc158210d57fe4da26e622c9c294022988d08d9347ec1cf782adafe", size = 14050453, upload-time = "2026-01-08T19:11:37.555Z" },
|
|
1828
|
+
{ url = "https://files.pythonhosted.org/packages/ae/93/f36d89fa021543187f98991609ce6e47e24f35f008dfe1af01379d248a41/ruff-0.14.11-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a35c9da08562f1598ded8470fcfef2afb5cf881996e6c0a502ceb61f4bc9c8a3", size = 13757889, upload-time = "2026-01-08T19:12:07.094Z" },
|
|
1829
|
+
{ url = "https://files.pythonhosted.org/packages/b7/9f/c7fb6ecf554f28709a6a1f2a7f74750d400979e8cd47ed29feeaa1bd4db8/ruff-0.14.11-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:0f3727189a52179393ecf92ec7057c2210203e6af2676f08d92140d3e1ee72c1", size = 13955832, upload-time = "2026-01-08T19:11:55.064Z" },
|
|
1830
|
+
{ url = "https://files.pythonhosted.org/packages/db/a0/153315310f250f76900a98278cf878c64dfb6d044e184491dd3289796734/ruff-0.14.11-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:eb09f849bd37147a789b85995ff734a6c4a095bed5fd1608c4f56afc3634cde2", size = 12586522, upload-time = "2026-01-08T19:11:35.356Z" },
|
|
1831
|
+
{ url = "https://files.pythonhosted.org/packages/2f/2b/a73a2b6e6d2df1d74bf2b78098be1572191e54bec0e59e29382d13c3adc5/ruff-0.14.11-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:c61782543c1231bf71041461c1f28c64b961d457d0f238ac388e2ab173d7ecb7", size = 12724637, upload-time = "2026-01-08T19:11:47.796Z" },
|
|
1832
|
+
{ url = "https://files.pythonhosted.org/packages/f0/41/09100590320394401cd3c48fc718a8ba71c7ddb1ffd07e0ad6576b3a3df2/ruff-0.14.11-py3-none-musllinux_1_2_i686.whl", hash = "sha256:82ff352ea68fb6766140381748e1f67f83c39860b6446966cff48a315c3e2491", size = 13145837, upload-time = "2026-01-08T19:11:32.87Z" },
|
|
1833
|
+
{ url = "https://files.pythonhosted.org/packages/3b/d8/e035db859d1d3edf909381eb8ff3e89a672d6572e9454093538fe6f164b0/ruff-0.14.11-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:728e56879df4ca5b62a9dde2dd0eb0edda2a55160c0ea28c4025f18c03f86984", size = 13850469, upload-time = "2026-01-08T19:12:11.694Z" },
|
|
1834
|
+
{ url = "https://files.pythonhosted.org/packages/4e/02/bb3ff8b6e6d02ce9e3740f4c17dfbbfb55f34c789c139e9cd91985f356c7/ruff-0.14.11-py3-none-win32.whl", hash = "sha256:337c5dd11f16ee52ae217757d9b82a26400be7efac883e9e852646f1557ed841", size = 12851094, upload-time = "2026-01-08T19:11:45.163Z" },
|
|
1835
|
+
{ url = "https://files.pythonhosted.org/packages/58/f1/90ddc533918d3a2ad628bc3044cdfc094949e6d4b929220c3f0eb8a1c998/ruff-0.14.11-py3-none-win_amd64.whl", hash = "sha256:f981cea63d08456b2c070e64b79cb62f951aa1305282974d4d5216e6e0178ae6", size = 14001379, upload-time = "2026-01-08T19:11:52.591Z" },
|
|
1836
|
+
{ url = "https://files.pythonhosted.org/packages/c4/1c/1dbe51782c0e1e9cfce1d1004752672d2d4629ea46945d19d731ad772b3b/ruff-0.14.11-py3-none-win_arm64.whl", hash = "sha256:649fb6c9edd7f751db276ef42df1f3df41c38d67d199570ae2a7bd6cbc3590f0", size = 12938644, upload-time = "2026-01-08T19:11:50.027Z" },
|
|
1837
|
+
]
|
|
1838
|
+
|
|
1729
1839
|
[[package]]
|
|
1730
1840
|
name = "secretstorage"
|
|
1731
1841
|
version = "3.5.0"
|
|
@@ -1832,6 +1942,26 @@ wheels = [
|
|
|
1832
1942
|
{ url = "https://files.pythonhosted.org/packages/77/b8/0135fadc89e73be292b473cb820b4f5a08197779206b33191e801feeae40/tomli-2.3.0-py3-none-any.whl", hash = "sha256:e95b1af3c5b07d9e643909b5abbec77cd9f1217e6d0bca72b0234736b9fb1f1b", size = 14408, upload-time = "2025-10-08T22:01:46.04Z" },
|
|
1833
1943
|
]
|
|
1834
1944
|
|
|
1945
|
+
[[package]]
|
|
1946
|
+
name = "twine"
|
|
1947
|
+
version = "6.2.0"
|
|
1948
|
+
source = { registry = "https://pypi.org/simple" }
|
|
1949
|
+
dependencies = [
|
|
1950
|
+
{ name = "id" },
|
|
1951
|
+
{ name = "keyring", marker = "platform_machine != 'ppc64le' and platform_machine != 's390x'" },
|
|
1952
|
+
{ name = "packaging" },
|
|
1953
|
+
{ name = "readme-renderer" },
|
|
1954
|
+
{ name = "requests" },
|
|
1955
|
+
{ name = "requests-toolbelt" },
|
|
1956
|
+
{ name = "rfc3986" },
|
|
1957
|
+
{ name = "rich" },
|
|
1958
|
+
{ name = "urllib3" },
|
|
1959
|
+
]
|
|
1960
|
+
sdist = { url = "https://files.pythonhosted.org/packages/e0/a8/949edebe3a82774c1ec34f637f5dd82d1cf22c25e963b7d63771083bbee5/twine-6.2.0.tar.gz", hash = "sha256:e5ed0d2fd70c9959770dce51c8f39c8945c574e18173a7b81802dab51b4b75cf", size = 172262, upload-time = "2025-09-04T15:43:17.255Z" }
|
|
1961
|
+
wheels = [
|
|
1962
|
+
{ url = "https://files.pythonhosted.org/packages/3a/7a/882d99539b19b1490cac5d77c67338d126e4122c8276bf640e411650c830/twine-6.2.0-py3-none-any.whl", hash = "sha256:418ebf08ccda9a8caaebe414433b0ba5e25eb5e4a927667122fbe8f829f985d8", size = 42727, upload-time = "2025-09-04T15:43:15.994Z" },
|
|
1963
|
+
]
|
|
1964
|
+
|
|
1835
1965
|
[[package]]
|
|
1836
1966
|
name = "typer"
|
|
1837
1967
|
version = "0.21.0"
|
{quickcall_integrations-0.2.0 → quickcall_integrations-0.3.0}/.claude-plugin/marketplace.json
RENAMED
|
File without changes
|
{quickcall_integrations-0.2.0 → quickcall_integrations-0.3.0}/.github/workflows/publish-pypi.yml
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{quickcall_integrations-0.2.0 → quickcall_integrations-0.3.0}/mcp_server/api_clients/__init__.py
RENAMED
|
File without changes
|
{quickcall_integrations-0.2.0 → quickcall_integrations-0.3.0}/mcp_server/api_clients/slack_client.py
RENAMED
|
File without changes
|
|
File without changes
|
{quickcall_integrations-0.2.0 → quickcall_integrations-0.3.0}/mcp_server/auth/credentials.py
RENAMED
|
File without changes
|
{quickcall_integrations-0.2.0 → quickcall_integrations-0.3.0}/mcp_server/auth/device_flow.py
RENAMED
|
File without changes
|
{quickcall_integrations-0.2.0 → quickcall_integrations-0.3.0}/mcp_server/resources/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{quickcall_integrations-0.2.0 → quickcall_integrations-0.3.0}/mcp_server/tools/auth_tools.py
RENAMED
|
File without changes
|
|
File without changes
|
{quickcall_integrations-0.2.0 → quickcall_integrations-0.3.0}/mcp_server/tools/slack_tools.py
RENAMED
|
File without changes
|
{quickcall_integrations-0.2.0 → quickcall_integrations-0.3.0}/mcp_server/tools/utility_tools.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{quickcall_integrations-0.2.0 → quickcall_integrations-0.3.0}/plugins/quickcall/commands/connect.md
RENAMED
|
File without changes
|
|
File without changes
|
{quickcall_integrations-0.2.0 → quickcall_integrations-0.3.0}/plugins/quickcall/commands/status.md
RENAMED
|
File without changes
|
{quickcall_integrations-0.2.0 → quickcall_integrations-0.3.0}/plugins/quickcall/commands/updates.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{quickcall_integrations-0.2.0 → quickcall_integrations-0.3.0}/tests/appraisal/setup_test_data.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|