jules-agent 0.1.2__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.
@@ -0,0 +1,4 @@
1
+ """jules-agent package."""
2
+
3
+ __all__ = ["__version__"]
4
+ __version__ = "0.1.2"
@@ -0,0 +1,5 @@
1
+ from .cli import main
2
+
3
+
4
+ if __name__ == "__main__":
5
+ raise SystemExit(main())
@@ -0,0 +1,379 @@
1
+ from __future__ import annotations
2
+
3
+ import argparse
4
+ import os
5
+ from pathlib import Path
6
+
7
+ from .commands import (
8
+ handle_advance,
9
+ handle_approve,
10
+ handle_delete,
11
+ handle_feedback,
12
+ handle_import,
13
+ handle_merge,
14
+ handle_next,
15
+ handle_review,
16
+ handle_run,
17
+ handle_send,
18
+ handle_status,
19
+ handle_sync,
20
+ run_clarification_loop,
21
+ run_confirmation_loop,
22
+ run_feedback_loop,
23
+ )
24
+ from .io import (
25
+ build_review_prompt,
26
+ prompt_for_review,
27
+ render_plan,
28
+ select_task_interactively,
29
+ )
30
+ from .state import (
31
+ extract_pull_request_number,
32
+ get_candidates,
33
+ get_jules_state_mapping,
34
+ get_run_sync_status,
35
+ resolve_task,
36
+ sync_pr_created_task,
37
+ sync_task,
38
+ )
39
+ from ..client import JulesClient
40
+ from ..config import load_config
41
+ from ..github import GitHubClient
42
+ from ..models import (
43
+ ProjectState,
44
+ State,
45
+ )
46
+ from ..pipeline import (
47
+ suggest_reply,
48
+ )
49
+ from ..codex import PipelineError, SelectionCancelled
50
+ from ..git import get_git_remote_repo, get_git_root
51
+ from ..persistence import load_state
52
+
53
+ # Re-exporting for backward compatibility and tests
54
+ __all__ = [
55
+ "build_review_prompt",
56
+ "extract_pull_request_number",
57
+ "get_jules_state_mapping",
58
+ "get_run_sync_status",
59
+ "prompt_for_review",
60
+ "get_candidates",
61
+ "render_plan",
62
+ "resolve_task",
63
+ "run_clarification_loop",
64
+ "run_confirmation_loop",
65
+ "run_feedback_loop",
66
+ "select_task_interactively",
67
+ "sync_pr_created_task",
68
+ "sync_task",
69
+ "suggest_reply",
70
+ ]
71
+
72
+
73
+ def build_parser() -> argparse.ArgumentParser:
74
+ parser = argparse.ArgumentParser(
75
+ prog="jules-agent",
76
+ description="Analyze a task and dispatch it to Jules.",
77
+ )
78
+ parser.add_argument(
79
+ "--repo",
80
+ help="Optional Jules repo override, for example owner/name.",
81
+ )
82
+ parser.add_argument(
83
+ "--tool-bin",
84
+ help="Path to the backend tool executable.",
85
+ )
86
+ parser.add_argument(
87
+ "--tool",
88
+ help="Backend tool to use (codex, claude, gemini, opencode, copilot, cline).",
89
+ )
90
+ parser.add_argument(
91
+ "--gemini-skip-trust",
92
+ action="store_true",
93
+ default=None,
94
+ help="Pass --skip-trust to the Gemini CLI adapter.",
95
+ )
96
+ parser.add_argument(
97
+ "--plan-tool",
98
+ help="Tool override for the planning phase.",
99
+ )
100
+ parser.add_argument(
101
+ "--approve-tool",
102
+ help="Tool override for the approval phase.",
103
+ )
104
+ parser.add_argument(
105
+ "--feedback-tool",
106
+ help="Tool override for the feedback phase.",
107
+ )
108
+ parser.add_argument(
109
+ "--review-tool",
110
+ help="Tool override for the review phase.",
111
+ )
112
+ parser.add_argument(
113
+ "--config",
114
+ type=Path,
115
+ help="Path to a custom configuration file.",
116
+ )
117
+
118
+ subparsers = parser.add_subparsers(dest="command", help="Subcommands")
119
+
120
+ import_parser = subparsers.add_parser("import", help="Import an existing Jules session")
121
+ import_parser.add_argument("session_id", help="Jules session ID or name")
122
+
123
+ run_parser = subparsers.add_parser("run", help="Run a new task")
124
+ run_parser.add_argument("task", help="Task description")
125
+ run_parser.add_argument(
126
+ "--no-confirm",
127
+ action="store_true",
128
+ help="Skip the confirmation loop and dispatch immediately.",
129
+ )
130
+ run_parser.add_argument(
131
+ "--auto-plan-approval",
132
+ action="store_true",
133
+ help="Automatically approve the task plan (forces requirePlanApproval=false).",
134
+ )
135
+ run_parser.add_argument(
136
+ "--automation-mode",
137
+ help="Automation mode for the Jules session (e.g., AUTO_CREATE_PR).",
138
+ )
139
+
140
+ status_parser = subparsers.add_parser("status", help="Show local state")
141
+ status_parser.add_argument(
142
+ "-a",
143
+ "--all",
144
+ action="store_true",
145
+ help="Show all runs (default shows only planned and running)",
146
+ )
147
+ status_parser.add_argument(
148
+ "--show-activities",
149
+ action="store_true",
150
+ help="Show session activities",
151
+ )
152
+
153
+ subparsers.add_parser("sync", help="Sync with Jules API")
154
+
155
+ approve_parser = subparsers.add_parser("approve", help="Approve a task plan")
156
+ approve_parser.add_argument(
157
+ "task_id", nargs="?", help="Task ID (RUN_ID:TASK_ID or TASK_ID)"
158
+ )
159
+
160
+ send_parser = subparsers.add_parser("send", help="Send a message to a task")
161
+ send_parser.add_argument(
162
+ "args",
163
+ nargs="+",
164
+ help="[TASK_ID] MESSAGE (if TASK_ID is omitted, message must be quoted if it contains spaces)",
165
+ )
166
+
167
+ feedback_parser = subparsers.add_parser(
168
+ "feedback", help="Interactive feedback loop for a task"
169
+ )
170
+ feedback_parser.add_argument(
171
+ "task_id", nargs="?", help="Task ID (RUN_ID:TASK_ID or TASK_ID)"
172
+ )
173
+
174
+ review_parser = subparsers.add_parser("review", help="Manually run a review for a task")
175
+ review_parser.add_argument(
176
+ "task_id", nargs="?", help="Task ID (RUN_ID:TASK_ID or TASK_ID)"
177
+ )
178
+
179
+ merge_parser = subparsers.add_parser("merge", help="Merge pull request for a task")
180
+ merge_parser.add_argument(
181
+ "task_id", nargs="?", help="Task ID (RUN_ID:TASK_ID or TASK_ID)"
182
+ )
183
+ merge_parser.add_argument(
184
+ "--delete-branch",
185
+ action=argparse.BooleanOptionalAction,
186
+ default=None,
187
+ help="Delete the local and remote source branch after a successful merge.",
188
+ )
189
+ merge_parser.add_argument(
190
+ "--pull",
191
+ action=argparse.BooleanOptionalAction,
192
+ default=None,
193
+ help="Run git pull after switching to the target branch after merge.",
194
+ )
195
+ merge_group = merge_parser.add_mutually_exclusive_group()
196
+ merge_group.add_argument(
197
+ "--merge",
198
+ action="store_const",
199
+ dest="merge_method",
200
+ const="merge",
201
+ help="Use merge commit (default)",
202
+ )
203
+ merge_group.add_argument(
204
+ "--squash",
205
+ action="store_const",
206
+ dest="merge_method",
207
+ const="squash",
208
+ help="Squash and merge",
209
+ )
210
+ merge_group.add_argument(
211
+ "--rebase",
212
+ action="store_const",
213
+ dest="merge_method",
214
+ const="rebase",
215
+ help="Rebase and merge",
216
+ )
217
+
218
+ next_parser = subparsers.add_parser("next", help="Dispatch next task in sequential run")
219
+ next_parser.add_argument("run_id", nargs="?", help="Run ID")
220
+ next_parser.add_argument(
221
+ "--automation-mode",
222
+ help="Automation mode for the Jules session (e.g., AUTO_CREATE_PR).",
223
+ )
224
+
225
+ delete_parser = subparsers.add_parser("delete", aliases=["rm"], help="Delete a run or task from local state")
226
+ delete_parser.add_argument("--dry-run", action="store_true", help="Show what would be deleted without actually deleting")
227
+ delete_parser.add_argument("--yes", "-y", action="store_true", help="Skip confirmation prompt")
228
+
229
+ delete_subparsers = delete_parser.add_subparsers(dest="subcommand", required=True)
230
+
231
+ delete_run_parser = delete_subparsers.add_parser("run", help="Delete a run and all its tasks")
232
+ delete_run_parser.add_argument("run_id", nargs="?", help="Run ID to delete")
233
+
234
+ delete_task_parser = delete_subparsers.add_parser("task", help="Delete a specific task")
235
+ delete_task_parser.add_argument("task_id", nargs="?", help="Task ID to delete (RUN_ID:TASK_ID or TASK_ID)")
236
+
237
+ advance_parser = subparsers.add_parser(
238
+ "advance", help="Automatically or interactively advance work"
239
+ )
240
+ advance_parser.add_argument(
241
+ "--auto-plan-approval",
242
+ action="store_true",
243
+ default=None,
244
+ help="Automatically approve plans when recommended.",
245
+ )
246
+ advance_parser.add_argument(
247
+ "--auto-feedback",
248
+ action="store_true",
249
+ default=None,
250
+ help="Automatically send suggested feedback.",
251
+ )
252
+ advance_parser.add_argument(
253
+ "--auto-merge",
254
+ action="store_true",
255
+ default=None,
256
+ help="Automatically merge PRs.",
257
+ )
258
+ advance_parser.add_argument(
259
+ "--auto",
260
+ action="store_true",
261
+ help="Enable all automatic behaviors (plan approval and feedback).",
262
+ )
263
+ advance_parser.add_argument(
264
+ "--json",
265
+ action="store_true",
266
+ help="Emit result as JSON.",
267
+ )
268
+
269
+ cron_parser = subparsers.add_parser(
270
+ "cron", help="Non-interactive background execution"
271
+ )
272
+ cron_parser.add_argument(
273
+ "--auto-plan-approval",
274
+ action="store_true",
275
+ default=None,
276
+ help="Automatically approve plans when recommended.",
277
+ )
278
+ cron_parser.add_argument(
279
+ "--auto-feedback",
280
+ action="store_true",
281
+ default=None,
282
+ help="Automatically send suggested feedback.",
283
+ )
284
+ cron_parser.add_argument(
285
+ "--auto-merge",
286
+ action="store_true",
287
+ default=None,
288
+ help="Automatically merge PRs.",
289
+ )
290
+ cron_parser.add_argument(
291
+ "--auto",
292
+ action="store_true",
293
+ help="Enable all automatic behaviors (plan approval and feedback).",
294
+ )
295
+ cron_parser.add_argument(
296
+ "--json",
297
+ action="store_true",
298
+ help="Emit result as JSON.",
299
+ )
300
+
301
+ return parser
302
+
303
+
304
+ def main(argv: list[str] | None = None) -> int:
305
+ parser = build_parser()
306
+ args = parser.parse_args(argv)
307
+
308
+ config = load_config(args.config)
309
+
310
+ api_key = os.environ.get("JULES_API_KEY") or config.api_key
311
+ if not api_key:
312
+ parser.exit(
313
+ 1,
314
+ "Error: JULES_API_KEY is not set in environment or configuration.\n",
315
+ )
316
+
317
+ github_token = os.environ.get("GITHUB_TOKEN") or config.github_token
318
+
319
+ repo = args.repo or config.repo
320
+ base_url = config.base_url
321
+
322
+
323
+ client = JulesClient(api_key=api_key, base_url=base_url)
324
+ github_client = GitHubClient(token=github_token) if github_token else None
325
+ cwd = Path.cwd()
326
+
327
+ if args.command is None:
328
+ parser.print_help()
329
+ return 0
330
+
331
+ try:
332
+ state = load_state(cwd)
333
+ if state is None:
334
+ git_root = get_git_root(cwd)
335
+ if repo is None:
336
+ repo_info = get_git_remote_repo(cwd)
337
+ if repo_info:
338
+ repo = f"{repo_info[0]}/{repo_info[1]}"
339
+ if repo is None:
340
+ parser.exit(
341
+ 1, "Error: Could not determine repository. Use --repo owner/name.\n"
342
+ )
343
+
344
+ state = State(project=ProjectState(root=str(git_root), repo=repo))
345
+
346
+ if args.command == "run":
347
+ return handle_run(args, state, client, cwd, config)
348
+ elif args.command == "import":
349
+ return handle_import(args, state, client, cwd)
350
+ elif args.command == "advance":
351
+ return handle_advance(args, state, client, github_client, cwd, config)
352
+ elif args.command == "cron":
353
+ from .commands.advance import handle_cron
354
+ return handle_cron(args, state, client, github_client, cwd, config)
355
+ elif args.command == "status":
356
+ return handle_status(args, state)
357
+ elif args.command == "sync":
358
+ return handle_sync(args, state, client, github_client, cwd)
359
+ elif args.command == "approve":
360
+ return handle_approve(args, state, client, cwd, parser, config=config)
361
+ elif args.command == "feedback":
362
+ return handle_feedback(args, state, client, cwd, parser, config=config)
363
+ elif args.command == "review":
364
+ return handle_review(args, state, client, github_client, cwd, parser, config=config)
365
+ elif args.command == "send":
366
+ return handle_send(args, state, client, github_client, cwd, parser)
367
+ elif args.command == "merge":
368
+ return handle_merge(args, state, client, github_client, cwd, config, parser)
369
+ elif args.command == "next":
370
+ return handle_next(args, state, client, cwd, config)
371
+ elif args.command in ("delete", "rm"):
372
+ return handle_delete(args, state, cwd)
373
+
374
+ except SelectionCancelled:
375
+ return 0
376
+ except PipelineError as exc:
377
+ parser.exit(1, f"{exc}\n")
378
+
379
+ return 0
@@ -0,0 +1,5 @@
1
+ from . import main
2
+
3
+
4
+ if __name__ == "__main__":
5
+ raise SystemExit(main())