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.
- jules_agent/__init__.py +4 -0
- jules_agent/__main__.py +5 -0
- jules_agent/cli/__init__.py +379 -0
- jules_agent/cli/__main__.py +5 -0
- jules_agent/cli/advance_core.py +388 -0
- jules_agent/cli/commands/__init__.py +29 -0
- jules_agent/cli/commands/advance.py +64 -0
- jules_agent/cli/commands/approve.py +48 -0
- jules_agent/cli/commands/delete.py +119 -0
- jules_agent/cli/commands/feedback.py +247 -0
- jules_agent/cli/commands/import_command.py +185 -0
- jules_agent/cli/commands/merge.py +139 -0
- jules_agent/cli/commands/next.py +51 -0
- jules_agent/cli/commands/review.py +48 -0
- jules_agent/cli/commands/run.py +237 -0
- jules_agent/cli/commands/send.py +50 -0
- jules_agent/cli/commands/status.py +44 -0
- jules_agent/cli/commands/sync.py +118 -0
- jules_agent/cli/io.py +193 -0
- jules_agent/cli/state.py +315 -0
- jules_agent/client.py +156 -0
- jules_agent/codex.py +408 -0
- jules_agent/config.py +80 -0
- jules_agent/git.py +98 -0
- jules_agent/github.py +120 -0
- jules_agent/models.py +330 -0
- jules_agent/persistence.py +55 -0
- jules_agent/pipeline.py +757 -0
- jules_agent/review.py +267 -0
- jules_agent-0.1.2.dist-info/METADATA +296 -0
- jules_agent-0.1.2.dist-info/RECORD +35 -0
- jules_agent-0.1.2.dist-info/WHEEL +5 -0
- jules_agent-0.1.2.dist-info/entry_points.txt +2 -0
- jules_agent-0.1.2.dist-info/licenses/LICENSE +21 -0
- jules_agent-0.1.2.dist-info/top_level.txt +1 -0
jules_agent/__init__.py
ADDED
jules_agent/__main__.py
ADDED
|
@@ -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
|