boomtick-cli 0.2.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- boomtick_cli-0.2.1.dist-info/METADATA +20 -0
- boomtick_cli-0.2.1.dist-info/RECORD +103 -0
- boomtick_cli-0.2.1.dist-info/WHEEL +5 -0
- boomtick_cli-0.2.1.dist-info/entry_points.txt +3 -0
- boomtick_cli-0.2.1.dist-info/top_level.txt +1 -0
- dev_tools/__init__.py +1 -0
- dev_tools/cli-schema.json +2028 -0
- dev_tools/cli.py +1542 -0
- dev_tools/config.py +235 -0
- dev_tools/constants.py +14 -0
- dev_tools/daemon.py +146 -0
- dev_tools/dist/config.js +108 -0
- dev_tools/dist/index.js +10 -0
- dev_tools/dist/lib/error_utils.js +8 -0
- dev_tools/dist/lib/git.js +31 -0
- dev_tools/dist/lib/result.js +21 -0
- dev_tools/dist/lib/shell.js +91 -0
- dev_tools/dist/lib/shell.test.js +10 -0
- dev_tools/dist/lib/td-cli.js +27 -0
- dev_tools/dist/lib/test-utils.js +20 -0
- dev_tools/dist/mcp/definitions.js +242 -0
- dev_tools/dist/mcp/server.js +317 -0
- dev_tools/dist/mcp/tools.js +18 -0
- dev_tools/dist/tools/contract.js +2069 -0
- dev_tools/dist/tools/ddgs.search.js +50 -0
- dev_tools/dist/tools/ddgs.search.test.js +67 -0
- dev_tools/dist/tools/ddgs_search.py +23 -0
- dev_tools/dist/tools/github.checkout_branch.js +20 -0
- dev_tools/dist/tools/github.comment_triage_summary.js +25 -0
- dev_tools/dist/tools/github.create_issue.js +28 -0
- dev_tools/dist/tools/github.create_issue.test.js +54 -0
- dev_tools/dist/tools/github.create_pull_request.js +44 -0
- dev_tools/dist/tools/github.get_merge_conflict_files.js +26 -0
- dev_tools/dist/tools/github.get_pr.js +18 -0
- dev_tools/dist/tools/github.get_pr_diff.js +24 -0
- dev_tools/dist/tools/github.issue_comment.js +24 -0
- dev_tools/dist/tools/github.issue_update.js +29 -0
- dev_tools/dist/tools/github.issue_view.js +28 -0
- dev_tools/dist/tools/github.open_replacement_pr.js +41 -0
- dev_tools/dist/tools/github.search_open_prs.js +31 -0
- dev_tools/dist/tools/github.search_open_prs.test.js +51 -0
- dev_tools/dist/tools/jules/cancel-session.js +20 -0
- dev_tools/dist/tools/jules/create-session.js +20 -0
- dev_tools/dist/tools/jules/create-session.test.js +99 -0
- dev_tools/dist/tools/jules/get-messages.js +19 -0
- dev_tools/dist/tools/jules/get-messages.test.js +45 -0
- dev_tools/dist/tools/jules/get-pr.js +14 -0
- dev_tools/dist/tools/jules/get-pr.test.js +37 -0
- dev_tools/dist/tools/jules/get-session.js +10 -0
- dev_tools/dist/tools/jules/list-sessions.js +43 -0
- dev_tools/dist/tools/jules/send-message.js +22 -0
- dev_tools/dist/tools/jules/send-message.test.js +56 -0
- dev_tools/dist/tools/jules/shared.js +26 -0
- dev_tools/dist/tools/jules/trigger-feedback.js +16 -0
- dev_tools/dist/tools/jules/trigger-feedback.test.js +42 -0
- dev_tools/dist/tools/repo.commit_patch.js +33 -0
- dev_tools/dist/tools/repo.create_branch.js +21 -0
- dev_tools/dist/tools/repo.create_branch.test.js +42 -0
- dev_tools/dist/tools/repo.create_repair_branch.js +38 -0
- dev_tools/dist/tools/repo.get_changed_files.js +21 -0
- dev_tools/dist/tools/repo.get_command_schema.js +27 -0
- dev_tools/dist/tools/repo.get_package_scripts.js +35 -0
- dev_tools/dist/tools/repo.get_package_scripts.test.js +39 -0
- dev_tools/dist/tools/repo.get_route_map.js +37 -0
- dev_tools/dist/tools/repo.logs.js +16 -0
- dev_tools/dist/tools/repo.read_agent_context.js +20 -0
- dev_tools/dist/tools/repo.read_ci_logs.js +18 -0
- dev_tools/dist/tools/repo.run_lighthouse.js +37 -0
- dev_tools/dist/tools/repo.run_playwright.js +29 -0
- dev_tools/dist/tools/repo.run_tests.js +43 -0
- dev_tools/dist/tools/types.js +1 -0
- dev_tools/get_ai_context.py +63 -0
- dev_tools/handlers/__init__.py +1 -0
- dev_tools/handlers/command_handler.py +69 -0
- dev_tools/mcp_server.py +36 -0
- dev_tools/models.py +354 -0
- dev_tools/orchestrator.py +2841 -0
- dev_tools/pr_overlap.py +139 -0
- dev_tools/resources/__init__.py +1 -0
- dev_tools/resources/build-repo-context.py +182 -0
- dev_tools/resources/prompt_constants.json +5 -0
- dev_tools/resources/review_template.md +41 -0
- dev_tools/resources/ux-audit.config.json +15 -0
- dev_tools/resources/visual_guidelines.json +3 -0
- dev_tools/review_read_pass.py +232 -0
- dev_tools/schema_gen.py +55 -0
- dev_tools/schema_utils.py +125 -0
- dev_tools/scope_check.py +85 -0
- dev_tools/services/__init__.py +1 -0
- dev_tools/services/ai_service.py +816 -0
- dev_tools/services/dependency_graph.py +123 -0
- dev_tools/services/github.py +783 -0
- dev_tools/services/jules.py +181 -0
- dev_tools/services/repair_service.py +199 -0
- dev_tools/services/vector_store.py +82 -0
- dev_tools/services/vision_service.py +91 -0
- dev_tools/td_cli.py +28 -0
- dev_tools/utils/__init__.py +1035 -0
- dev_tools/utils/git.py +68 -0
- dev_tools/ux_report.py +213 -0
- dev_tools/verify_infra.py +91 -0
- dev_tools/verify_versions.py +191 -0
- dev_tools/version_utils.py +175 -0
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
# pylint: disable=import-outside-toplevel,missing-docstring,too-many-branches,too-many-locals,too-many-statements
|
|
2
|
+
import click
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
def get_type_name(param):
|
|
6
|
+
t = param.type
|
|
7
|
+
if isinstance(t, click.Choice):
|
|
8
|
+
return "choice"
|
|
9
|
+
|
|
10
|
+
t_str = str(t).lower()
|
|
11
|
+
if hasattr(t, "name"):
|
|
12
|
+
t_str = t.name.lower()
|
|
13
|
+
|
|
14
|
+
if "int" in t_str:
|
|
15
|
+
return "integer"
|
|
16
|
+
if "bool" in t_str:
|
|
17
|
+
return "boolean"
|
|
18
|
+
return "string"
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def collect_commands(root_cmd, prefix="", max_depth=10):
|
|
22
|
+
"""
|
|
23
|
+
Collects leaves of the command tree iteratively using breadth-first traversal.
|
|
24
|
+
Avoids recursion limits and ensures predictable depth enforcement.
|
|
25
|
+
"""
|
|
26
|
+
from collections import deque
|
|
27
|
+
|
|
28
|
+
subcmds = {}
|
|
29
|
+
# queue elements: (cmd_obj, current_prefix, depth)
|
|
30
|
+
queue = deque([(root_cmd, prefix, 0)])
|
|
31
|
+
|
|
32
|
+
while queue:
|
|
33
|
+
cmd, pfx, depth = queue.popleft()
|
|
34
|
+
|
|
35
|
+
if depth > max_depth:
|
|
36
|
+
continue
|
|
37
|
+
|
|
38
|
+
if isinstance(cmd, click.Group):
|
|
39
|
+
# Sort items once to maintain deterministic output
|
|
40
|
+
try:
|
|
41
|
+
# Using sorted() once per group is efficient enough for typical CLI trees
|
|
42
|
+
items = sorted(cmd.commands.items())
|
|
43
|
+
for sub_name, sub_cmd in items:
|
|
44
|
+
new_prefix = f"{pfx} {sub_name}".strip()
|
|
45
|
+
queue.append((sub_cmd, new_prefix, depth + 1))
|
|
46
|
+
except Exception:
|
|
47
|
+
pass
|
|
48
|
+
elif isinstance(cmd, click.Command):
|
|
49
|
+
cmd_name = pfx
|
|
50
|
+
cmd_help = cmd.help or ""
|
|
51
|
+
|
|
52
|
+
args_str = []
|
|
53
|
+
req_args = []
|
|
54
|
+
opt_flags = []
|
|
55
|
+
req_flags = []
|
|
56
|
+
|
|
57
|
+
for param in cmd.params:
|
|
58
|
+
param_type = get_type_name(param)
|
|
59
|
+
if isinstance(param, click.Argument):
|
|
60
|
+
arg_name = param.name.upper()
|
|
61
|
+
if param.nargs == -1:
|
|
62
|
+
args_str.append(f"<{arg_name}...>")
|
|
63
|
+
else:
|
|
64
|
+
args_str.append(f"<{arg_name}>")
|
|
65
|
+
req_args.append(
|
|
66
|
+
{
|
|
67
|
+
"name": param.name,
|
|
68
|
+
"type": param_type,
|
|
69
|
+
"description": getattr(param, "help", "") or "",
|
|
70
|
+
}
|
|
71
|
+
)
|
|
72
|
+
elif isinstance(param, click.Option):
|
|
73
|
+
flag_name = param.opts[0]
|
|
74
|
+
flag_desc = param.help or ""
|
|
75
|
+
option_dict = {"flag": flag_name, "type": param_type, "description": flag_desc}
|
|
76
|
+
if param.required:
|
|
77
|
+
req_flags.append(option_dict)
|
|
78
|
+
else:
|
|
79
|
+
opt_flags.append(option_dict)
|
|
80
|
+
|
|
81
|
+
# Build a cleaner exact_usage string
|
|
82
|
+
usage = f"td-cli {cmd_name}"
|
|
83
|
+
if req_flags:
|
|
84
|
+
usage += " " + " ".join([f"{f['flag']} <{f['flag'].lstrip('-').upper()}>" for f in req_flags])
|
|
85
|
+
|
|
86
|
+
# Show a compact view of optional flags
|
|
87
|
+
boolean_flags = [f for f in opt_flags if f["type"] == "boolean"]
|
|
88
|
+
other_opt_flags = [f for f in opt_flags if f["type"] != "boolean"]
|
|
89
|
+
|
|
90
|
+
if boolean_flags:
|
|
91
|
+
usage += " " + " ".join([f"[{f['flag']}]" for f in boolean_flags])
|
|
92
|
+
if other_opt_flags:
|
|
93
|
+
usage += " " + " ".join([f"[{f['flag']} <{f['flag'].lstrip('-').upper()}>]" for f in other_opt_flags])
|
|
94
|
+
|
|
95
|
+
if args_str:
|
|
96
|
+
usage += " " + " ".join(args_str)
|
|
97
|
+
|
|
98
|
+
cmd_info = {"description": cmd_help, "exact_usage": usage}
|
|
99
|
+
if req_args:
|
|
100
|
+
cmd_info["required_arguments"] = req_args
|
|
101
|
+
if req_flags:
|
|
102
|
+
cmd_info["required_flags"] = req_flags
|
|
103
|
+
if opt_flags:
|
|
104
|
+
cmd_info["optional_flags"] = opt_flags
|
|
105
|
+
|
|
106
|
+
subcmds[cmd_name] = cmd_info
|
|
107
|
+
|
|
108
|
+
return subcmds
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def get_command_by_path(cli_root, path_str):
|
|
112
|
+
"""
|
|
113
|
+
Finds a command object by its path (e.g. "gh audit-pr")
|
|
114
|
+
"""
|
|
115
|
+
if not path_str:
|
|
116
|
+
return cli_root
|
|
117
|
+
|
|
118
|
+
parts = path_str.split()
|
|
119
|
+
current = cli_root
|
|
120
|
+
for part in parts:
|
|
121
|
+
if isinstance(current, click.Group) and part in current.commands:
|
|
122
|
+
current = current.commands[part]
|
|
123
|
+
else:
|
|
124
|
+
return None
|
|
125
|
+
return current
|
dev_tools/scope_check.py
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
# pylint: disable=line-too-long,missing-docstring,no-value-for-parameter
|
|
2
|
+
import sys
|
|
3
|
+
from typing import Set
|
|
4
|
+
|
|
5
|
+
import click
|
|
6
|
+
from dev_tools.config import load_project_config
|
|
7
|
+
from dev_tools.utils import log_info, run_command
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def get_changed_files():
|
|
11
|
+
"""Returns the list of files changed in the current branch."""
|
|
12
|
+
config = load_project_config()
|
|
13
|
+
base = config.base_branch
|
|
14
|
+
# Use check=False to manually handle fallback
|
|
15
|
+
res = run_command(["git", "diff", "--name-only", base], check=False, log_on_error=False)
|
|
16
|
+
if res.returncode == 0:
|
|
17
|
+
return res.stdout.strip().splitlines()
|
|
18
|
+
|
|
19
|
+
res = run_command(["git", "diff", "--name-only", "HEAD"], check=False, log_on_error=False)
|
|
20
|
+
if res.returncode == 0:
|
|
21
|
+
return res.stdout.strip().splitlines()
|
|
22
|
+
|
|
23
|
+
return []
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def verify_pr_scope(file_list=None):
|
|
27
|
+
"""Checks if a PR touches too many core layout/component files or mixes content scopes."""
|
|
28
|
+
if file_list is None:
|
|
29
|
+
file_list = get_changed_files()
|
|
30
|
+
|
|
31
|
+
config = load_project_config()
|
|
32
|
+
core_dirs = config.core_dirs
|
|
33
|
+
threshold = config.monolithic_pr_threshold
|
|
34
|
+
|
|
35
|
+
core_files = [f for f in file_list if any(f.startswith(d) for d in core_dirs)]
|
|
36
|
+
if len(core_files) > threshold:
|
|
37
|
+
return f"PR scope warning: Touching {len(core_files)} core files in {core_dirs}. Consider splitting this monolithic PR to avoid merge conflicts (AGENTS.md §23)."
|
|
38
|
+
|
|
39
|
+
# Content Scope Check
|
|
40
|
+
content_scopes = config.content_scopes
|
|
41
|
+
|
|
42
|
+
active_scopes: Set[str] = set()
|
|
43
|
+
for f in file_list:
|
|
44
|
+
for scope_name, prefix in content_scopes.items():
|
|
45
|
+
if f.startswith(prefix):
|
|
46
|
+
active_scopes.add(scope_name)
|
|
47
|
+
|
|
48
|
+
if len(active_scopes) > 1:
|
|
49
|
+
scope_names = ", ".join(sorted(content_scopes.keys()))
|
|
50
|
+
return f"Content scope warning: Mixed content domains detected ({', '.join(active_scopes)}). PRs should be split by scope: {scope_names} (AGENTS.md §21)."
|
|
51
|
+
|
|
52
|
+
# Mixed Content and Code Check
|
|
53
|
+
has_content = len(active_scopes) > 0
|
|
54
|
+
code_files = [f for f in file_list if f.startswith("src/") and not any(f.startswith(d) for d in core_dirs)]
|
|
55
|
+
|
|
56
|
+
if has_content and len(code_files) > 2:
|
|
57
|
+
return "PR scope warning: Mixing significant code changes with content updates. Consider splitting content corrections from feature development."
|
|
58
|
+
|
|
59
|
+
return None
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
@click.command()
|
|
63
|
+
@click.argument("files", nargs=-1)
|
|
64
|
+
def main(files):
|
|
65
|
+
"""Checks if a PR touches too many core files or mixes content scopes."""
|
|
66
|
+
file_list = list(files)
|
|
67
|
+
|
|
68
|
+
# If no files provided as arguments, check stdin (piped input)
|
|
69
|
+
if not file_list and not sys.stdin.isatty():
|
|
70
|
+
file_list = sys.stdin.read().splitlines()
|
|
71
|
+
|
|
72
|
+
# If still no files, auto-detect from git
|
|
73
|
+
if not file_list:
|
|
74
|
+
warning = verify_pr_scope()
|
|
75
|
+
else:
|
|
76
|
+
warning = verify_pr_scope(file_list)
|
|
77
|
+
|
|
78
|
+
if warning:
|
|
79
|
+
log_info(warning)
|
|
80
|
+
sys.exit(1)
|
|
81
|
+
sys.exit(0)
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
if __name__ == "__main__":
|
|
85
|
+
main()
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# pylint: disable=missing-docstring
|