agent-devkit 0.0.4 → 0.1.0
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.
- package/README.md +1 -1
- package/package.json +1 -1
- package/runtime/README.md +3 -0
- package/runtime/agent +29 -13
- package/runtime/agents/README.md +3 -0
- package/runtime/agents/github-pr-reviewer/AGENTS.md +10 -0
- package/runtime/agents/github-pr-reviewer/README.md +7 -0
- package/runtime/agents/github-pr-reviewer/agent.yaml +33 -0
- package/runtime/agents/github-pr-reviewer/capabilities/create-review-automation/capability.yaml +20 -0
- package/runtime/agents/github-pr-reviewer/capabilities/create-review-automation/decision-rules.md +8 -0
- package/runtime/agents/github-pr-reviewer/capabilities/create-review-automation/workflow.md +9 -0
- package/runtime/agents/github-pr-reviewer/capabilities/inspect-pr/capability.yaml +20 -0
- package/runtime/agents/github-pr-reviewer/capabilities/inspect-pr/decision-rules.md +7 -0
- package/runtime/agents/github-pr-reviewer/capabilities/inspect-pr/workflow.md +8 -0
- package/runtime/agents/github-pr-reviewer/capabilities/list-review-requests/capability.yaml +20 -0
- package/runtime/agents/github-pr-reviewer/capabilities/list-review-requests/decision-rules.md +7 -0
- package/runtime/agents/github-pr-reviewer/capabilities/list-review-requests/workflow.md +8 -0
- package/runtime/agents/github-pr-reviewer/capabilities/review-pr-diff/capability.yaml +20 -0
- package/runtime/agents/github-pr-reviewer/capabilities/review-pr-diff/decision-rules.md +9 -0
- package/runtime/agents/github-pr-reviewer/capabilities/review-pr-diff/workflow.md +10 -0
- package/runtime/agents/github-pr-reviewer/infra/README.md +7 -0
- package/runtime/agents/github-pr-reviewer/knowledge/context.md +7 -0
- package/runtime/agents/github-pr-reviewer/knowledge/system.md +17 -0
- package/runtime/agents/github-pr-reviewer/templates/pr-automation-output.md +10 -0
- package/runtime/agents/github-pr-reviewer/templates/pr-inspection-output.md +9 -0
- package/runtime/agents/github-pr-reviewer/templates/pr-list-output.md +9 -0
- package/runtime/agents/github-pr-reviewer/templates/pr-review-output.md +17 -0
- package/runtime/cli/aikit/__init__.py +1 -1
- package/runtime/cli/aikit/aliases.py +196 -0
- package/runtime/cli/aikit/app_home.py +78 -0
- package/runtime/cli/aikit/audit.py +344 -0
- package/runtime/cli/aikit/calendar.py +163 -0
- package/runtime/cli/aikit/diagnostics.py +9 -0
- package/runtime/cli/aikit/github_pr.py +254 -0
- package/runtime/cli/aikit/identity.py +75 -0
- package/runtime/cli/aikit/llm.py +240 -48
- package/runtime/cli/aikit/main.py +924 -24
- package/runtime/cli/aikit/memory.py +148 -8
- package/runtime/cli/aikit/notifications.py +9 -0
- package/runtime/cli/aikit/permissions.py +345 -0
- package/runtime/cli/aikit/personality.py +123 -0
- package/runtime/cli/aikit/providers.py +4 -5
- package/runtime/cli/aikit/scheduler.py +18 -0
- package/runtime/cli/aikit/sessions.py +396 -0
- package/runtime/cli/aikit/setup_wizard.py +12 -0
- package/runtime/cli/aikit/tasks.py +351 -0
- package/runtime/cli/aikit/toolchain.py +274 -0
- package/runtime/providers/calendar.yaml +28 -0
- package/runtime/providers/github.yaml +42 -0
- package/runtime/providers/local-notification.yaml +19 -0
- package/runtime/providers/local-scheduler.yaml +24 -0
- package/runtime/vendor/skills/napkin/napkin.md +13 -3
|
@@ -12,19 +12,48 @@ import sys
|
|
|
12
12
|
from pathlib import Path
|
|
13
13
|
from typing import Any
|
|
14
14
|
|
|
15
|
+
from cli.aikit.aliases import add_alias, list_aliases, remove_alias, sync_aliases
|
|
15
16
|
from cli.aikit import __version__
|
|
17
|
+
from cli.aikit.audit import export_audit, list_audits, record_audit, show_audit
|
|
18
|
+
from cli.aikit.calendar import calendar_list, calendar_summary, calendar_today, calendar_tomorrow, configure_calendar
|
|
16
19
|
from cli.aikit.diagnostics import build_diagnostics
|
|
17
20
|
from cli.aikit.fallback import evaluate_provider_requirements
|
|
21
|
+
from cli.aikit.github_pr import pr_create_automation, pr_inspect, pr_list_review_requests, pr_review
|
|
18
22
|
from cli.aikit.guardrails import evaluate_execution_guardrails
|
|
23
|
+
from cli.aikit.identity import enforce_identity_response, is_identity_question, local_identity_response, public_name
|
|
19
24
|
from cli.aikit.llm import (
|
|
20
25
|
configure_backend,
|
|
21
26
|
doctor_backends,
|
|
22
27
|
invoke_agent_prompt,
|
|
23
28
|
list_backends,
|
|
24
|
-
|
|
29
|
+
llm_preference,
|
|
25
30
|
set_default_backend,
|
|
31
|
+
set_llm_preference,
|
|
26
32
|
)
|
|
27
|
-
from cli.aikit.memory import record_usage, reset_memory, show_memory
|
|
33
|
+
from cli.aikit.memory import memory_path_payload, napkin_context, record_usage, reset_memory, show_memory
|
|
34
|
+
from cli.aikit.personality import load_personality, reset_personality, setup_personality, update_personality
|
|
35
|
+
from cli.aikit.permissions import grant_permission, revoke_permission, show_permissions
|
|
36
|
+
from cli.aikit.sessions import (
|
|
37
|
+
build_contextual_prompt,
|
|
38
|
+
get_or_create_session,
|
|
39
|
+
list_sessions,
|
|
40
|
+
record_exchange,
|
|
41
|
+
resume_session,
|
|
42
|
+
show_session,
|
|
43
|
+
)
|
|
44
|
+
from cli.aikit.setup_wizard import setup_wizard
|
|
45
|
+
from cli.aikit.scheduler import run_scheduler_once, scheduler_daemon_plan
|
|
46
|
+
from cli.aikit.tasks import (
|
|
47
|
+
create_task,
|
|
48
|
+
delete_task,
|
|
49
|
+
list_tasks,
|
|
50
|
+
run_task,
|
|
51
|
+
show_task,
|
|
52
|
+
task_history,
|
|
53
|
+
update_task_schedule,
|
|
54
|
+
update_task_status,
|
|
55
|
+
)
|
|
56
|
+
from cli.aikit.toolchain import doctor_toolchain, install_toolchain, list_toolchain
|
|
28
57
|
from cli.aikit.install import InstallError, install_runtime
|
|
29
58
|
from cli.aikit.lock import lock_status, parse_profiles
|
|
30
59
|
from cli.aikit.output import run_payload
|
|
@@ -69,6 +98,17 @@ DETERMINISTIC_COMMANDS = (
|
|
|
69
98
|
"credential",
|
|
70
99
|
"source",
|
|
71
100
|
"memory",
|
|
101
|
+
"personality",
|
|
102
|
+
"setup",
|
|
103
|
+
"alias",
|
|
104
|
+
"session",
|
|
105
|
+
"toolchain",
|
|
106
|
+
"task",
|
|
107
|
+
"scheduler",
|
|
108
|
+
"calendar",
|
|
109
|
+
"pr",
|
|
110
|
+
"permissions",
|
|
111
|
+
"audit",
|
|
72
112
|
"install",
|
|
73
113
|
)
|
|
74
114
|
LLM_COMMANDS = ("agent",)
|
|
@@ -86,11 +126,13 @@ def main(argv: list[str] | None = None, *, prog: str | None = None) -> int:
|
|
|
86
126
|
try:
|
|
87
127
|
result = dispatch(args)
|
|
88
128
|
except DevKitError as exc:
|
|
129
|
+
maybe_record_cli_audit(args, result=None, error=str(exc))
|
|
89
130
|
print(f"error: {exc}", file=sys.stderr)
|
|
90
131
|
return 1
|
|
91
132
|
|
|
92
133
|
if result is None:
|
|
93
134
|
return 0
|
|
135
|
+
maybe_record_cli_audit(args, result=result, error=None)
|
|
94
136
|
|
|
95
137
|
if getattr(args, "json", False):
|
|
96
138
|
print(json.dumps(result, ensure_ascii=False, indent=2))
|
|
@@ -109,7 +151,15 @@ def build_parser(prog: str | None = None) -> argparse.ArgumentParser:
|
|
|
109
151
|
description="AI DevKit CLI",
|
|
110
152
|
)
|
|
111
153
|
parser.add_argument("--json", action="store_true", help="print machine-readable JSON")
|
|
154
|
+
parser.add_argument("--dry-run", dest="global_dry_run", action="store_true", help="show execution plan without external write effects")
|
|
112
155
|
parser.add_argument("-v", "--version", action="store_true", help="print CLI version and exit")
|
|
156
|
+
parser.add_argument(
|
|
157
|
+
"-s",
|
|
158
|
+
"--sessions",
|
|
159
|
+
dest="sessions_shortcut",
|
|
160
|
+
action="store_true",
|
|
161
|
+
help="list local conversation sessions and exit",
|
|
162
|
+
)
|
|
113
163
|
|
|
114
164
|
subparsers = parser.add_subparsers(dest="command")
|
|
115
165
|
|
|
@@ -119,6 +169,10 @@ def build_parser(prog: str | None = None) -> argparse.ArgumentParser:
|
|
|
119
169
|
)
|
|
120
170
|
agent_parser.add_argument("--json", action="store_true", default=argparse.SUPPRESS, help=argparse.SUPPRESS)
|
|
121
171
|
agent_parser.add_argument("--llm", help="LLM backend id to use")
|
|
172
|
+
agent_parser.add_argument("--dry-run", action="store_true", help="show execution plan without invoking LLM or external writes")
|
|
173
|
+
agent_parser.add_argument("--no-llm-fallback", action="store_true", help="disable automatic fallback to secondary LLM backends")
|
|
174
|
+
agent_parser.add_argument("--session", dest="session_id", help="resume a local conversation session")
|
|
175
|
+
agent_parser.add_argument("--new-session", action="store_true", help="start a new local conversation session")
|
|
122
176
|
agent_parser.add_argument("prompt", nargs=argparse.REMAINDER)
|
|
123
177
|
|
|
124
178
|
commands_parser = subparsers.add_parser("commands", help="list CLI command modes")
|
|
@@ -159,10 +213,98 @@ def build_parser(prog: str | None = None) -> argparse.ArgumentParser:
|
|
|
159
213
|
|
|
160
214
|
memory_parser = subparsers.add_parser("memory", help="inspect or reset local AI DevKit memory")
|
|
161
215
|
memory_parser.add_argument("--json", action="store_true", default=argparse.SUPPRESS, help=argparse.SUPPRESS)
|
|
162
|
-
memory_parser.add_argument("action", nargs="?", default="show", choices=["show", "reset"])
|
|
216
|
+
memory_parser.add_argument("action", nargs="?", default="show", choices=["show", "path", "reset"])
|
|
163
217
|
memory_parser.add_argument("--agent", dest="agent_id")
|
|
164
218
|
memory_parser.add_argument("--source", dest="source_id")
|
|
165
219
|
memory_parser.add_argument("--all", action="store_true", help="reset all local memory")
|
|
220
|
+
memory_parser.add_argument("--sessions", action="store_true", help="reset local conversation sessions")
|
|
221
|
+
memory_parser.add_argument("--tasks", action="store_true", help="reset local task schedules")
|
|
222
|
+
memory_parser.add_argument("--cache", action="store_true", help="reset local cache")
|
|
223
|
+
|
|
224
|
+
personality_parser = subparsers.add_parser("personality", help="inspect or update local Agent DevKit personality")
|
|
225
|
+
personality_parser.add_argument("--json", action="store_true", default=argparse.SUPPRESS, help=argparse.SUPPRESS)
|
|
226
|
+
personality_parser.add_argument("action", nargs="?", default="show", choices=["show", "edit", "reset"])
|
|
227
|
+
personality_parser.add_argument("--name", dest="agent_name", help="public agent name")
|
|
228
|
+
personality_parser.add_argument("--user-name", help="user name")
|
|
229
|
+
personality_parser.add_argument("--language", help="default response language")
|
|
230
|
+
personality_parser.add_argument("--tone", help="response tone")
|
|
231
|
+
personality_parser.add_argument("--detail-level", help="response detail level")
|
|
232
|
+
|
|
233
|
+
setup_parser = subparsers.add_parser("setup", help="run setup helpers")
|
|
234
|
+
setup_parser.add_argument("--json", action="store_true", default=argparse.SUPPRESS, help=argparse.SUPPRESS)
|
|
235
|
+
setup_parser.add_argument("--dry-run", action="store_true", help="show setup plan without installing external tools")
|
|
236
|
+
setup_parser.add_argument("--yes", action="store_true", help="confirm setup actions")
|
|
237
|
+
setup_parser.add_argument("action", nargs="?", default="plan", choices=["plan", "personality"])
|
|
238
|
+
|
|
239
|
+
alias_parser = subparsers.add_parser("alias", help="manage local command aliases for agent")
|
|
240
|
+
alias_parser.add_argument("--json", action="store_true", default=argparse.SUPPRESS, help=argparse.SUPPRESS)
|
|
241
|
+
alias_parser.add_argument("action", nargs="?", default="list", choices=["add", "list", "remove", "sync"])
|
|
242
|
+
alias_parser.add_argument("name", nargs="?")
|
|
243
|
+
alias_parser.add_argument("--force", action="store_true", help="allow replacing an existing local alias file")
|
|
244
|
+
|
|
245
|
+
session_parser = subparsers.add_parser("session", help="manage local conversation sessions")
|
|
246
|
+
session_parser.add_argument("--json", action="store_true", default=argparse.SUPPRESS, help=argparse.SUPPRESS)
|
|
247
|
+
session_parser.add_argument("action", nargs="?", default="list", choices=["list", "show", "resume"])
|
|
248
|
+
session_parser.add_argument("session_id", nargs="?")
|
|
249
|
+
|
|
250
|
+
toolchain_parser = subparsers.add_parser("toolchain", help="inspect or install external tools")
|
|
251
|
+
toolchain_parser.add_argument("--json", action="store_true", default=argparse.SUPPRESS, help=argparse.SUPPRESS)
|
|
252
|
+
toolchain_parser.add_argument("action", nargs="?", default="list", choices=["list", "doctor", "install"])
|
|
253
|
+
toolchain_parser.add_argument("tool", nargs="?", default="all")
|
|
254
|
+
toolchain_parser.add_argument("--dry-run", action="store_true", help="show install plan without executing it")
|
|
255
|
+
toolchain_parser.add_argument("--yes", action="store_true", help="confirm external tool installation")
|
|
256
|
+
|
|
257
|
+
task_parser = subparsers.add_parser("task", help="manage local scheduled tasks")
|
|
258
|
+
task_parser.add_argument("--json", action="store_true", default=argparse.SUPPRESS, help=argparse.SUPPRESS)
|
|
259
|
+
task_parser.add_argument("action", nargs="?", default="list", choices=["create", "list", "show", "history", "run", "pause", "resume", "update", "enable", "disable", "delete"])
|
|
260
|
+
task_parser.add_argument("task_id", nargs="?")
|
|
261
|
+
task_parser.add_argument("--title")
|
|
262
|
+
task_parser.add_argument("--prompt")
|
|
263
|
+
task_parser.add_argument("--every")
|
|
264
|
+
task_parser.add_argument("--cron")
|
|
265
|
+
task_parser.add_argument("--dry-run", action="store_true")
|
|
266
|
+
task_parser.add_argument("--yes", action="store_true")
|
|
267
|
+
|
|
268
|
+
scheduler_parser = subparsers.add_parser("scheduler", help="run local scheduler")
|
|
269
|
+
scheduler_parser.add_argument("--json", action="store_true", default=argparse.SUPPRESS, help=argparse.SUPPRESS)
|
|
270
|
+
scheduler_parser.add_argument("action", nargs="?", default="run-once", choices=["run-once", "daemon"])
|
|
271
|
+
scheduler_parser.add_argument("--dry-run", action="store_true")
|
|
272
|
+
|
|
273
|
+
calendar_parser = subparsers.add_parser("calendar", help="inspect configured calendar")
|
|
274
|
+
calendar_parser.add_argument("--json", action="store_true", default=argparse.SUPPRESS, help=argparse.SUPPRESS)
|
|
275
|
+
calendar_parser.add_argument("action", nargs="?", default="today", choices=["today", "tomorrow", "list", "configure"])
|
|
276
|
+
calendar_parser.add_argument("--from", dest="date_from")
|
|
277
|
+
calendar_parser.add_argument("--to", dest="date_to")
|
|
278
|
+
calendar_parser.add_argument("--ics")
|
|
279
|
+
calendar_parser.add_argument("--timezone")
|
|
280
|
+
|
|
281
|
+
pr_parser = subparsers.add_parser("pr", help="review GitHub pull requests")
|
|
282
|
+
pr_parser.add_argument("--json", action="store_true", default=argparse.SUPPRESS, help=argparse.SUPPRESS)
|
|
283
|
+
pr_parser.add_argument("action", nargs="?", default="list-review-requests", choices=["list-review-requests", "inspect", "review", "automation"])
|
|
284
|
+
pr_parser.add_argument("pr_ref", nargs="?")
|
|
285
|
+
pr_parser.add_argument("automation_action", nargs="?")
|
|
286
|
+
pr_parser.add_argument("--approve", action="store_true")
|
|
287
|
+
pr_parser.add_argument("--request-changes", action="store_true")
|
|
288
|
+
pr_parser.add_argument("--comment")
|
|
289
|
+
pr_parser.add_argument("--allow-write", action="store_true")
|
|
290
|
+
pr_parser.add_argument("--dry-run", action="store_true")
|
|
291
|
+
pr_parser.add_argument("--time", default="09:00")
|
|
292
|
+
|
|
293
|
+
permissions_parser = subparsers.add_parser("permissions", help="manage local permission policies")
|
|
294
|
+
permissions_parser.add_argument("--json", action="store_true", default=argparse.SUPPRESS, help=argparse.SUPPRESS)
|
|
295
|
+
permissions_parser.add_argument("action", nargs="?", default="show", choices=["show", "grant", "revoke"])
|
|
296
|
+
permissions_parser.add_argument("agent", nargs="?")
|
|
297
|
+
permissions_parser.add_argument("provider", nargs="?")
|
|
298
|
+
permissions_parser.add_argument("level", nargs="?")
|
|
299
|
+
permissions_parser.add_argument("--project")
|
|
300
|
+
permissions_parser.add_argument("--task", dest="task_id")
|
|
301
|
+
|
|
302
|
+
audit_parser = subparsers.add_parser("audit", help="inspect local execution audit trail")
|
|
303
|
+
audit_parser.add_argument("--json", action="store_true", default=argparse.SUPPRESS, help=argparse.SUPPRESS)
|
|
304
|
+
audit_parser.add_argument("action", nargs="?", default="list", choices=["list", "show", "export"])
|
|
305
|
+
audit_parser.add_argument("execution_id", nargs="?")
|
|
306
|
+
audit_parser.add_argument("--limit", type=int, default=20)
|
|
307
|
+
audit_parser.add_argument("--format", default="md", choices=["md", "json"])
|
|
166
308
|
|
|
167
309
|
llm_parser = subparsers.add_parser("llm", help="manage LLM backends")
|
|
168
310
|
llm_parser.add_argument("--json", action="store_true", default=argparse.SUPPRESS, help=argparse.SUPPRESS)
|
|
@@ -170,14 +312,17 @@ def build_parser(prog: str | None = None) -> argparse.ArgumentParser:
|
|
|
170
312
|
"action",
|
|
171
313
|
nargs="?",
|
|
172
314
|
default="list",
|
|
173
|
-
choices=["list", "doctor", "configure", "set-default"],
|
|
315
|
+
choices=["list", "doctor", "configure", "set-default", "preference"],
|
|
174
316
|
)
|
|
175
317
|
llm_parser.add_argument("backend", nargs="?")
|
|
318
|
+
llm_parser.add_argument("preference_value", nargs="?")
|
|
176
319
|
llm_parser.add_argument("--api-key-env", help="environment variable that stores the API key")
|
|
177
320
|
llm_parser.add_argument("--base-url", help="OpenAI-compatible base URL")
|
|
178
321
|
llm_parser.add_argument("--model", help="default model id")
|
|
179
322
|
llm_parser.add_argument("--command", dest="host_command", help="host CLI command name or path")
|
|
180
323
|
llm_parser.add_argument("--set-default", action="store_true", help="set backend as the default LLM")
|
|
324
|
+
llm_parser.add_argument("--primary", help="primary backend for LLM preference")
|
|
325
|
+
llm_parser.add_argument("--order", help="comma-separated fallback order")
|
|
181
326
|
|
|
182
327
|
install_parser = subparsers.add_parser("install", help="install AI DevKit host artifacts")
|
|
183
328
|
install_parser.add_argument("--json", action="store_true", default=argparse.SUPPRESS, help=argparse.SUPPRESS)
|
|
@@ -237,6 +382,8 @@ def build_parser(prog: str | None = None) -> argparse.ArgumentParser:
|
|
|
237
382
|
def dispatch(args: argparse.Namespace) -> dict[str, Any] | None:
|
|
238
383
|
if args.version:
|
|
239
384
|
return {"kind": "version", "program": getattr(args, "prog_name", "aikit"), "version": __version__}
|
|
385
|
+
if args.sessions_shortcut:
|
|
386
|
+
return list_sessions()
|
|
240
387
|
|
|
241
388
|
if not args.command:
|
|
242
389
|
raise DevKitError("missing command. Use --help for usage.")
|
|
@@ -256,6 +403,28 @@ def dispatch(args: argparse.Namespace) -> dict[str, Any] | None:
|
|
|
256
403
|
return dispatch_source(args)
|
|
257
404
|
if command == "memory":
|
|
258
405
|
return dispatch_memory(args)
|
|
406
|
+
if command == "personality":
|
|
407
|
+
return dispatch_personality(args)
|
|
408
|
+
if command == "setup":
|
|
409
|
+
return dispatch_setup(args)
|
|
410
|
+
if command == "alias":
|
|
411
|
+
return dispatch_alias(args)
|
|
412
|
+
if command == "session":
|
|
413
|
+
return dispatch_session(args)
|
|
414
|
+
if command == "toolchain":
|
|
415
|
+
return dispatch_toolchain(args)
|
|
416
|
+
if command == "task":
|
|
417
|
+
return dispatch_task(args)
|
|
418
|
+
if command == "scheduler":
|
|
419
|
+
return dispatch_scheduler(args)
|
|
420
|
+
if command == "calendar":
|
|
421
|
+
return dispatch_calendar(args)
|
|
422
|
+
if command == "pr":
|
|
423
|
+
return dispatch_pr(args)
|
|
424
|
+
if command == "permissions":
|
|
425
|
+
return dispatch_permissions(args)
|
|
426
|
+
if command == "audit":
|
|
427
|
+
return dispatch_audit(args)
|
|
259
428
|
if command == "llm":
|
|
260
429
|
return dispatch_llm(args)
|
|
261
430
|
if command == "install":
|
|
@@ -327,8 +496,22 @@ def list_command_modes() -> dict[str, Any]:
|
|
|
327
496
|
}
|
|
328
497
|
|
|
329
498
|
|
|
499
|
+
def maybe_record_cli_audit(args: argparse.Namespace, *, result: dict[str, Any] | None, error: str | None) -> None:
|
|
500
|
+
command = canonical_command(getattr(args, "command", None) or "")
|
|
501
|
+
if command in {"", "audit"} or getattr(args, "version", False):
|
|
502
|
+
return
|
|
503
|
+
try:
|
|
504
|
+
audit = record_audit(command=command, args=vars(args), result=result, error=error)
|
|
505
|
+
except Exception:
|
|
506
|
+
return
|
|
507
|
+
if result is not None:
|
|
508
|
+
result["audit"] = audit
|
|
509
|
+
|
|
510
|
+
|
|
330
511
|
def dispatch_llm(args: argparse.Namespace) -> dict[str, Any]:
|
|
331
512
|
try:
|
|
513
|
+
if args.action != "preference" and args.preference_value:
|
|
514
|
+
raise DevKitError(f"llm {args.action} received an unexpected argument: {args.preference_value}")
|
|
332
515
|
if args.action == "list":
|
|
333
516
|
if args.backend:
|
|
334
517
|
raise DevKitError("llm list does not accept a backend argument")
|
|
@@ -350,6 +533,19 @@ def dispatch_llm(args: argparse.Namespace) -> dict[str, Any]:
|
|
|
350
533
|
if not args.backend:
|
|
351
534
|
raise DevKitError("llm set-default requires a backend")
|
|
352
535
|
return set_default_backend(args.backend)
|
|
536
|
+
if args.action == "preference":
|
|
537
|
+
if args.backend in {None, "show"}:
|
|
538
|
+
return llm_preference()
|
|
539
|
+
if args.backend == "set":
|
|
540
|
+
if not args.primary and not args.order:
|
|
541
|
+
raise DevKitError("llm preference set requires --primary or --order")
|
|
542
|
+
return set_llm_preference(primary=args.primary, order=args.order)
|
|
543
|
+
if args.backend == "reorder":
|
|
544
|
+
order = args.preference_value or args.order
|
|
545
|
+
if not order:
|
|
546
|
+
raise DevKitError("llm preference reorder requires an order value or --order")
|
|
547
|
+
return set_llm_preference(order=order)
|
|
548
|
+
raise DevKitError("llm preference action must be show, set or reorder")
|
|
353
549
|
except ValueError as exc:
|
|
354
550
|
raise DevKitError(str(exc)) from exc
|
|
355
551
|
raise DevKitError(f"unsupported llm action: {args.action}")
|
|
@@ -363,7 +559,7 @@ def dispatch_install(args: argparse.Namespace) -> dict[str, Any]:
|
|
|
363
559
|
host=args.host,
|
|
364
560
|
target=Path(args.target) if args.target else None,
|
|
365
561
|
home=Path(args.home) if args.home else None,
|
|
366
|
-
dry_run=args
|
|
562
|
+
dry_run=effective_dry_run(args),
|
|
367
563
|
profiles=parse_profiles(args.profiles),
|
|
368
564
|
)
|
|
369
565
|
except InstallError as exc:
|
|
@@ -452,40 +648,476 @@ def dispatch_source(args: argparse.Namespace) -> dict[str, Any]:
|
|
|
452
648
|
def dispatch_memory(args: argparse.Namespace) -> dict[str, Any]:
|
|
453
649
|
if args.action == "show":
|
|
454
650
|
return show_memory(ROOT, agent_id=args.agent_id, source_id=args.source_id)
|
|
651
|
+
if args.action == "path":
|
|
652
|
+
return memory_path_payload()
|
|
455
653
|
if args.action == "reset":
|
|
456
|
-
return reset_memory(
|
|
654
|
+
return reset_memory(
|
|
655
|
+
all_memory=args.all,
|
|
656
|
+
agent_id=args.agent_id,
|
|
657
|
+
source_id=args.source_id,
|
|
658
|
+
reset_sessions=args.sessions,
|
|
659
|
+
reset_tasks=args.tasks,
|
|
660
|
+
reset_cache=args.cache,
|
|
661
|
+
)
|
|
457
662
|
raise DevKitError(f"unsupported memory action: {args.action}")
|
|
458
663
|
|
|
459
664
|
|
|
665
|
+
def dispatch_personality(args: argparse.Namespace) -> dict[str, Any]:
|
|
666
|
+
if args.action == "show":
|
|
667
|
+
return load_personality()
|
|
668
|
+
if args.action == "edit":
|
|
669
|
+
if not any([args.agent_name, args.user_name, args.language, args.tone, args.detail_level]):
|
|
670
|
+
payload = load_personality()
|
|
671
|
+
payload["status"] = "needs-input"
|
|
672
|
+
payload["message"] = "Use --name, --user-name, --language, --tone or --detail-level to edit non-interactively."
|
|
673
|
+
return payload
|
|
674
|
+
return update_personality(
|
|
675
|
+
agent_name=args.agent_name,
|
|
676
|
+
user_name=args.user_name,
|
|
677
|
+
language=args.language,
|
|
678
|
+
tone=args.tone,
|
|
679
|
+
detail_level=args.detail_level,
|
|
680
|
+
)
|
|
681
|
+
if args.action == "reset":
|
|
682
|
+
return reset_personality()
|
|
683
|
+
raise DevKitError(f"unsupported personality action: {args.action}")
|
|
684
|
+
|
|
685
|
+
|
|
686
|
+
def dispatch_setup(args: argparse.Namespace) -> dict[str, Any]:
|
|
687
|
+
if args.action == "personality":
|
|
688
|
+
return setup_personality()
|
|
689
|
+
if args.action == "plan":
|
|
690
|
+
return setup_wizard(ROOT, dry_run=effective_dry_run(args), yes=args.yes)
|
|
691
|
+
raise DevKitError(f"unsupported setup action: {args.action}")
|
|
692
|
+
|
|
693
|
+
|
|
694
|
+
def dispatch_toolchain(args: argparse.Namespace) -> dict[str, Any]:
|
|
695
|
+
try:
|
|
696
|
+
if args.action == "list":
|
|
697
|
+
if args.tool != "all":
|
|
698
|
+
raise DevKitError("toolchain list does not accept a tool argument")
|
|
699
|
+
return list_toolchain(ROOT)
|
|
700
|
+
if args.action == "doctor":
|
|
701
|
+
return doctor_toolchain(ROOT, None if args.tool == "all" else args.tool)
|
|
702
|
+
if args.action == "install":
|
|
703
|
+
return install_toolchain(ROOT, None if args.tool == "all" else args.tool, dry_run=effective_dry_run(args), yes=args.yes)
|
|
704
|
+
except ValueError as exc:
|
|
705
|
+
raise DevKitError(str(exc)) from exc
|
|
706
|
+
raise DevKitError(f"unsupported toolchain action: {args.action}")
|
|
707
|
+
|
|
708
|
+
|
|
709
|
+
def dispatch_task(args: argparse.Namespace) -> dict[str, Any]:
|
|
710
|
+
try:
|
|
711
|
+
if args.action == "list":
|
|
712
|
+
return list_tasks()
|
|
713
|
+
if args.action == "create":
|
|
714
|
+
schedule = {"type": "manual"}
|
|
715
|
+
if args.every:
|
|
716
|
+
schedule = {"type": "interval", "every": args.every}
|
|
717
|
+
if args.cron:
|
|
718
|
+
schedule = {"type": "cron", "cron": args.cron}
|
|
719
|
+
return create_task(task_id=args.task_id, title=args.title, prompt=args.prompt, schedule=schedule)
|
|
720
|
+
if args.action == "show":
|
|
721
|
+
require_id(args.task_id, "task show")
|
|
722
|
+
return show_task(args.task_id)
|
|
723
|
+
if args.action == "history":
|
|
724
|
+
require_id(args.task_id, "task history")
|
|
725
|
+
return task_history(args.task_id)
|
|
726
|
+
if args.action == "run":
|
|
727
|
+
require_id(args.task_id, "task run")
|
|
728
|
+
return run_task(args.task_id, dry_run=effective_dry_run(args))
|
|
729
|
+
if args.action in {"pause", "disable"}:
|
|
730
|
+
require_id(args.task_id, f"task {args.action}")
|
|
731
|
+
return update_task_status(args.task_id, "paused" if args.action == "pause" else "disabled")
|
|
732
|
+
if args.action in {"resume", "enable"}:
|
|
733
|
+
require_id(args.task_id, f"task {args.action}")
|
|
734
|
+
return update_task_status(args.task_id, "enabled")
|
|
735
|
+
if args.action == "update":
|
|
736
|
+
require_id(args.task_id, "task update")
|
|
737
|
+
return update_task_schedule(args.task_id, every=args.every, cron=args.cron)
|
|
738
|
+
if args.action == "delete":
|
|
739
|
+
require_id(args.task_id, "task delete")
|
|
740
|
+
return delete_task(args.task_id, yes=args.yes)
|
|
741
|
+
except ValueError as exc:
|
|
742
|
+
raise DevKitError(str(exc)) from exc
|
|
743
|
+
raise DevKitError(f"unsupported task action: {args.action}")
|
|
744
|
+
|
|
745
|
+
|
|
746
|
+
def dispatch_scheduler(args: argparse.Namespace) -> dict[str, Any]:
|
|
747
|
+
if args.action == "run-once":
|
|
748
|
+
return run_scheduler_once(dry_run=effective_dry_run(args))
|
|
749
|
+
if args.action == "daemon":
|
|
750
|
+
return scheduler_daemon_plan()
|
|
751
|
+
raise DevKitError(f"unsupported scheduler action: {args.action}")
|
|
752
|
+
|
|
753
|
+
|
|
754
|
+
def dispatch_calendar(args: argparse.Namespace) -> dict[str, Any]:
|
|
755
|
+
try:
|
|
756
|
+
if args.action == "configure":
|
|
757
|
+
return configure_calendar(ics_path=args.ics, timezone=args.timezone)
|
|
758
|
+
if args.action == "today":
|
|
759
|
+
return calendar_today()
|
|
760
|
+
if args.action == "tomorrow":
|
|
761
|
+
return calendar_tomorrow()
|
|
762
|
+
if args.action == "list":
|
|
763
|
+
return calendar_list(args.date_from, args.date_to)
|
|
764
|
+
except ValueError as exc:
|
|
765
|
+
raise DevKitError(str(exc)) from exc
|
|
766
|
+
raise DevKitError(f"unsupported calendar action: {args.action}")
|
|
767
|
+
|
|
768
|
+
|
|
769
|
+
def dispatch_pr(args: argparse.Namespace) -> dict[str, Any]:
|
|
770
|
+
if args.action == "list-review-requests":
|
|
771
|
+
if effective_dry_run(args):
|
|
772
|
+
return pr_read_dry_run("list-review-requests")
|
|
773
|
+
return pr_list_review_requests()
|
|
774
|
+
if args.action == "inspect":
|
|
775
|
+
require_id(args.pr_ref, "pr inspect")
|
|
776
|
+
if effective_dry_run(args):
|
|
777
|
+
return pr_read_dry_run("inspect", pr_ref=args.pr_ref)
|
|
778
|
+
return pr_inspect(args.pr_ref)
|
|
779
|
+
if args.action == "review":
|
|
780
|
+
require_id(args.pr_ref, "pr review")
|
|
781
|
+
return pr_review(
|
|
782
|
+
args.pr_ref,
|
|
783
|
+
approve=args.approve,
|
|
784
|
+
request_changes=args.request_changes,
|
|
785
|
+
comment=args.comment,
|
|
786
|
+
allow_write=args.allow_write,
|
|
787
|
+
dry_run=effective_dry_run(args),
|
|
788
|
+
)
|
|
789
|
+
if args.action == "automation":
|
|
790
|
+
if args.automation_action not in {None, "create"}:
|
|
791
|
+
raise DevKitError("pr automation action must be create")
|
|
792
|
+
if effective_dry_run(args):
|
|
793
|
+
return pr_automation_dry_run(time=args.time)
|
|
794
|
+
return pr_create_automation(time=args.time)
|
|
795
|
+
raise DevKitError(f"unsupported pr action: {args.action}")
|
|
796
|
+
|
|
797
|
+
|
|
798
|
+
def dispatch_permissions(args: argparse.Namespace) -> dict[str, Any]:
|
|
799
|
+
try:
|
|
800
|
+
if args.action == "show":
|
|
801
|
+
return show_permissions()
|
|
802
|
+
if args.action == "grant":
|
|
803
|
+
if not args.agent or not args.provider or not args.level:
|
|
804
|
+
raise DevKitError("permissions grant requires agent, provider and level")
|
|
805
|
+
return grant_permission(args.agent, args.provider, args.level, project=args.project, task_id=args.task_id)
|
|
806
|
+
if args.action == "revoke":
|
|
807
|
+
if not args.agent or not args.provider:
|
|
808
|
+
raise DevKitError("permissions revoke requires agent and provider")
|
|
809
|
+
return revoke_permission(args.agent, args.provider, args.level, project=args.project, task_id=args.task_id)
|
|
810
|
+
except ValueError as exc:
|
|
811
|
+
raise DevKitError(str(exc)) from exc
|
|
812
|
+
raise DevKitError(f"unsupported permissions action: {args.action}")
|
|
813
|
+
|
|
814
|
+
|
|
815
|
+
def dispatch_audit(args: argparse.Namespace) -> dict[str, Any]:
|
|
816
|
+
try:
|
|
817
|
+
if args.action == "list":
|
|
818
|
+
if args.execution_id:
|
|
819
|
+
raise DevKitError("audit list does not accept an execution id")
|
|
820
|
+
return list_audits(limit=max(1, int(args.limit or 20)))
|
|
821
|
+
if args.action == "show":
|
|
822
|
+
require_id(args.execution_id, "audit show")
|
|
823
|
+
return show_audit(args.execution_id)
|
|
824
|
+
if args.action == "export":
|
|
825
|
+
require_id(args.execution_id, "audit export")
|
|
826
|
+
return export_audit(args.execution_id, fmt=args.format)
|
|
827
|
+
except ValueError as exc:
|
|
828
|
+
raise DevKitError(str(exc)) from exc
|
|
829
|
+
raise DevKitError(f"unsupported audit action: {args.action}")
|
|
830
|
+
|
|
831
|
+
|
|
832
|
+
def pr_read_dry_run(action: str, *, pr_ref: str | None = None) -> dict[str, Any]:
|
|
833
|
+
return {
|
|
834
|
+
"kind": "pr",
|
|
835
|
+
"status": "planned",
|
|
836
|
+
"ok": True,
|
|
837
|
+
"dry_run": True,
|
|
838
|
+
"mode": "report-only",
|
|
839
|
+
"provider": "github",
|
|
840
|
+
"action": action,
|
|
841
|
+
"pr_ref": pr_ref,
|
|
842
|
+
"commands": planned_pr_commands(action, pr_ref=pr_ref),
|
|
843
|
+
"summary": "Dry-run only. GitHub would be read through gh; no PR write action would be submitted.",
|
|
844
|
+
}
|
|
845
|
+
|
|
846
|
+
|
|
847
|
+
def pr_automation_dry_run(*, time: str) -> dict[str, Any]:
|
|
848
|
+
return {
|
|
849
|
+
"kind": "pr-automation",
|
|
850
|
+
"status": "planned",
|
|
851
|
+
"ok": True,
|
|
852
|
+
"dry_run": True,
|
|
853
|
+
"mode": "report-only",
|
|
854
|
+
"provider": "github",
|
|
855
|
+
"task": {
|
|
856
|
+
"id": "daily-pr-review",
|
|
857
|
+
"title": "Revisar PRs pendentes diariamente",
|
|
858
|
+
"schedule": {"type": "daily", "time": time},
|
|
859
|
+
"action": {
|
|
860
|
+
"type": "capability",
|
|
861
|
+
"agent": "github-pr-reviewer",
|
|
862
|
+
"capability": "list-review-requests",
|
|
863
|
+
"external_writes": False,
|
|
864
|
+
},
|
|
865
|
+
"permissions": {"mode": "report-only", "comment": False, "approve": False, "request_changes": False},
|
|
866
|
+
},
|
|
867
|
+
"summary": "Dry-run only. A local report-only PR review task would be created.",
|
|
868
|
+
}
|
|
869
|
+
|
|
870
|
+
|
|
871
|
+
def planned_pr_commands(action: str, *, pr_ref: str | None = None) -> list[list[str]]:
|
|
872
|
+
if action == "list-review-requests":
|
|
873
|
+
return [["gh", "pr", "list", "--review-requested", "@me", "--json", "number,title,url,author,headRefName,baseRefName,isDraft"]]
|
|
874
|
+
if action == "inspect":
|
|
875
|
+
return [["gh", "pr", "view", str(pr_ref), "--json", "number,title,url,author,body,headRefName,baseRefName,state,isDraft,reviewDecision,mergeable"]]
|
|
876
|
+
return []
|
|
877
|
+
|
|
878
|
+
|
|
879
|
+
def require_id(value: str | None, command: str) -> None:
|
|
880
|
+
if not value:
|
|
881
|
+
raise DevKitError(f"{command} requires an id")
|
|
882
|
+
|
|
883
|
+
|
|
884
|
+
def effective_dry_run(args: argparse.Namespace) -> bool:
|
|
885
|
+
return bool(getattr(args, "dry_run", False) or getattr(args, "global_dry_run", False))
|
|
886
|
+
|
|
887
|
+
|
|
888
|
+
def dispatch_alias(args: argparse.Namespace) -> dict[str, Any]:
|
|
889
|
+
try:
|
|
890
|
+
if args.action == "list":
|
|
891
|
+
if args.name:
|
|
892
|
+
raise DevKitError("alias list does not accept a name")
|
|
893
|
+
return list_aliases()
|
|
894
|
+
if args.action == "add":
|
|
895
|
+
if not args.name:
|
|
896
|
+
raise DevKitError("alias add requires a name")
|
|
897
|
+
return add_alias(args.name, force=args.force)
|
|
898
|
+
if args.action == "remove":
|
|
899
|
+
if not args.name:
|
|
900
|
+
raise DevKitError("alias remove requires a name")
|
|
901
|
+
return remove_alias(args.name)
|
|
902
|
+
if args.action == "sync":
|
|
903
|
+
if args.name:
|
|
904
|
+
raise DevKitError("alias sync does not accept a name")
|
|
905
|
+
return sync_aliases()
|
|
906
|
+
except ValueError as exc:
|
|
907
|
+
raise DevKitError(str(exc)) from exc
|
|
908
|
+
raise DevKitError(f"unsupported alias action: {args.action}")
|
|
909
|
+
|
|
910
|
+
|
|
911
|
+
def dispatch_session(args: argparse.Namespace) -> dict[str, Any]:
|
|
912
|
+
try:
|
|
913
|
+
if args.action == "list":
|
|
914
|
+
if args.session_id:
|
|
915
|
+
raise DevKitError("session list does not accept a session id")
|
|
916
|
+
return list_sessions()
|
|
917
|
+
if args.action == "show":
|
|
918
|
+
if not args.session_id:
|
|
919
|
+
raise DevKitError("session show requires a session id")
|
|
920
|
+
return show_session(args.session_id)
|
|
921
|
+
if args.action == "resume":
|
|
922
|
+
if not args.session_id:
|
|
923
|
+
raise DevKitError("session resume requires a session id")
|
|
924
|
+
return resume_session(args.session_id)
|
|
925
|
+
except ValueError as exc:
|
|
926
|
+
raise DevKitError(str(exc)) from exc
|
|
927
|
+
raise DevKitError(f"unsupported session action: {args.action}")
|
|
928
|
+
|
|
929
|
+
|
|
460
930
|
def agent_requires_llm(args: argparse.Namespace) -> dict[str, Any]:
|
|
461
931
|
prompt = " ".join(args.prompt).strip()
|
|
462
932
|
if not prompt:
|
|
463
933
|
raise DevKitError("agent requires a natural-language prompt")
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
return invoke_deterministic_route(prompt, route)
|
|
934
|
+
if effective_dry_run(args):
|
|
935
|
+
return build_agent_dry_run_plan(prompt, args)
|
|
467
936
|
try:
|
|
468
|
-
|
|
937
|
+
session = get_or_create_session(
|
|
938
|
+
session_id=args.session_id,
|
|
939
|
+
force_new=args.new_session,
|
|
940
|
+
prompt=prompt,
|
|
941
|
+
project=str(Path.cwd()),
|
|
942
|
+
backend=args.llm,
|
|
943
|
+
)
|
|
469
944
|
except ValueError as exc:
|
|
470
945
|
raise DevKitError(str(exc)) from exc
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
946
|
+
personality = load_personality()
|
|
947
|
+
name = public_name(personality=personality, invoked_as=getattr(args, "prog_name", "agent"))
|
|
948
|
+
if is_identity_question(prompt):
|
|
949
|
+
result = {
|
|
950
|
+
"kind": "agent",
|
|
951
|
+
"status": "ok",
|
|
952
|
+
"ok": True,
|
|
953
|
+
"requires_llm": False,
|
|
954
|
+
"prompt_received": True,
|
|
955
|
+
"prompt_length": len(prompt),
|
|
956
|
+
"identity": {"name": name, "source": "local"},
|
|
957
|
+
"response": local_identity_response(prompt, name=name),
|
|
958
|
+
}
|
|
959
|
+
return finalize_agent_session(result, session, prompt, backend=args.llm)
|
|
960
|
+
natural_result = dispatch_natural_operational_prompt(prompt)
|
|
961
|
+
if natural_result:
|
|
962
|
+
return finalize_agent_session(natural_result, session, prompt, backend=args.llm)
|
|
963
|
+
route = route_prompt(prompt)
|
|
964
|
+
if route:
|
|
965
|
+
result = invoke_deterministic_route(prompt, route)
|
|
966
|
+
return finalize_agent_session(result, session, prompt, backend=args.llm)
|
|
967
|
+
contextual_prompt = build_contextual_prompt(str(session["id"]), prompt)
|
|
968
|
+
result = invoke_agent_prompt(
|
|
969
|
+
contextual_prompt,
|
|
970
|
+
args.llm,
|
|
971
|
+
public_name=name,
|
|
972
|
+
allow_fallback=not args.no_llm_fallback,
|
|
973
|
+
)
|
|
974
|
+
result["prompt_length"] = len(prompt)
|
|
975
|
+
result["session_context_applied"] = contextual_prompt != prompt
|
|
976
|
+
if result.get("response"):
|
|
977
|
+
result["response"] = enforce_identity_response(str(result["response"]), prompt, name=name)
|
|
978
|
+
result["identity"] = {"name": name, "source": "local"}
|
|
979
|
+
return finalize_agent_session(result, session, prompt, backend=result.get("llm_backend") or args.llm)
|
|
980
|
+
|
|
981
|
+
|
|
982
|
+
def dispatch_natural_operational_prompt(prompt: str) -> dict[str, Any] | None:
|
|
983
|
+
normalized = " ".join(prompt.lower().split())
|
|
984
|
+
if "agenda" in normalized:
|
|
985
|
+
if "amanha" in normalized or "amanhã" in normalized:
|
|
986
|
+
payload = calendar_tomorrow()
|
|
987
|
+
else:
|
|
988
|
+
payload = calendar_today()
|
|
989
|
+
payload = dict(payload)
|
|
990
|
+
payload["kind"] = "agent"
|
|
991
|
+
payload["mode"] = "calendar-route"
|
|
992
|
+
payload["requires_llm"] = False
|
|
993
|
+
payload["prompt_received"] = True
|
|
994
|
+
payload["prompt_length"] = len(prompt)
|
|
995
|
+
payload["response"] = calendar_summary(payload)
|
|
996
|
+
if payload.get("status") == "needs-input":
|
|
997
|
+
payload["ok"] = False
|
|
998
|
+
else:
|
|
999
|
+
payload["ok"] = True
|
|
1000
|
+
return payload
|
|
1001
|
+
if has_pr_intent(normalized):
|
|
1002
|
+
if any(marker in normalized for marker in ("diariamente", "todo dia", "diaria", "diária", "recorrente")):
|
|
1003
|
+
payload = pr_create_automation()
|
|
1004
|
+
return {
|
|
1005
|
+
"kind": "agent",
|
|
1006
|
+
"status": payload.get("status"),
|
|
1007
|
+
"ok": True,
|
|
1008
|
+
"mode": "pr-automation-route",
|
|
1009
|
+
"requires_llm": False,
|
|
1010
|
+
"prompt_received": True,
|
|
1011
|
+
"prompt_length": len(prompt),
|
|
1012
|
+
"response": "Automacao diaria de revisao de PRs criada em modo report-only.",
|
|
1013
|
+
"result": payload,
|
|
1014
|
+
}
|
|
1015
|
+
payload = pr_list_review_requests()
|
|
1016
|
+
return {
|
|
1017
|
+
"kind": "agent",
|
|
1018
|
+
"status": payload.get("status"),
|
|
1019
|
+
"ok": payload.get("status") == "ok",
|
|
1020
|
+
"mode": "pr-route",
|
|
1021
|
+
"requires_llm": False,
|
|
1022
|
+
"prompt_received": True,
|
|
1023
|
+
"prompt_length": len(prompt),
|
|
1024
|
+
"response": summarize_pr_list(payload),
|
|
1025
|
+
"result": payload,
|
|
1026
|
+
"exit_code": payload.get("exit_code", 0 if payload.get("status") == "ok" else 2),
|
|
1027
|
+
}
|
|
1028
|
+
return None
|
|
1029
|
+
|
|
1030
|
+
|
|
1031
|
+
def build_agent_dry_run_plan(prompt: str, args: argparse.Namespace) -> dict[str, Any]:
|
|
1032
|
+
normalized = " ".join(prompt.lower().split())
|
|
1033
|
+
route = route_prompt(prompt)
|
|
1034
|
+
plan: dict[str, Any] = {
|
|
474
1035
|
"kind": "agent",
|
|
475
|
-
"status": "
|
|
476
|
-
"ok":
|
|
477
|
-
"
|
|
478
|
-
"
|
|
1036
|
+
"status": "planned",
|
|
1037
|
+
"ok": True,
|
|
1038
|
+
"dry_run": True,
|
|
1039
|
+
"requires_llm": False,
|
|
479
1040
|
"prompt_received": True,
|
|
480
1041
|
"prompt_length": len(prompt),
|
|
481
|
-
"
|
|
482
|
-
"
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
],
|
|
487
|
-
"
|
|
1042
|
+
"mode": "dry-run",
|
|
1043
|
+
"intent": "llm" if not route else route.get("intent"),
|
|
1044
|
+
"route": route,
|
|
1045
|
+
"llm_backend": getattr(args, "llm", None),
|
|
1046
|
+
"external_writes": False,
|
|
1047
|
+
"providers": {"used": [], "missing": [], "skipped": []},
|
|
1048
|
+
"commands": [],
|
|
1049
|
+
"permissions": [],
|
|
1050
|
+
"response": "Dry-run: nenhuma chamada LLM ou escrita externa foi executada.",
|
|
488
1051
|
}
|
|
1052
|
+
if "agenda" in normalized:
|
|
1053
|
+
plan.update(
|
|
1054
|
+
{
|
|
1055
|
+
"intent": "calendar",
|
|
1056
|
+
"mode": "calendar-dry-run",
|
|
1057
|
+
"providers": {"used": ["calendar"], "missing": [], "skipped": []},
|
|
1058
|
+
"data_reads": ["configured calendar provider, if present"],
|
|
1059
|
+
"response": "Dry-run: o calendario seria consultado localmente se configurado.",
|
|
1060
|
+
}
|
|
1061
|
+
)
|
|
1062
|
+
return plan
|
|
1063
|
+
if has_pr_intent(normalized):
|
|
1064
|
+
recurring = any(marker in normalized for marker in ("diariamente", "todo dia", "diaria", "diária", "recorrente"))
|
|
1065
|
+
plan.update(
|
|
1066
|
+
{
|
|
1067
|
+
"intent": "github-pr-review",
|
|
1068
|
+
"mode": "pr-dry-run",
|
|
1069
|
+
"providers": {"used": ["github"], "missing": [], "skipped": []},
|
|
1070
|
+
"commands": planned_pr_commands("list-review-requests"),
|
|
1071
|
+
"external_writes": False,
|
|
1072
|
+
"permissions": [{"agent": "github-pr-reviewer", "provider": "github", "required_level": "read-only"}],
|
|
1073
|
+
"response": (
|
|
1074
|
+
"Dry-run: a automacao diaria de PR seria planejada em modo report-only."
|
|
1075
|
+
if recurring
|
|
1076
|
+
else "Dry-run: PRs aguardando revisao seriam listadas via gh em modo report-only."
|
|
1077
|
+
),
|
|
1078
|
+
}
|
|
1079
|
+
)
|
|
1080
|
+
return plan
|
|
1081
|
+
if route:
|
|
1082
|
+
plan["providers"] = {"used": [route.get("provider")], "missing": [], "skipped": []}
|
|
1083
|
+
plan["response"] = "Dry-run: a capability roteada seria executada somente apos validar source/provider."
|
|
1084
|
+
return plan
|
|
1085
|
+
plan["response"] = "Dry-run: o prompt exigiria LLM configurada; nenhuma chamada foi feita."
|
|
1086
|
+
return plan
|
|
1087
|
+
|
|
1088
|
+
|
|
1089
|
+
def has_pr_intent(normalized_prompt: str) -> bool:
|
|
1090
|
+
tokens = {token.strip(".,;:!?()[]{}\"'") for token in normalized_prompt.split()}
|
|
1091
|
+
return bool({"pr", "prs"} & tokens) or "pull request" in normalized_prompt or "pull requests" in normalized_prompt
|
|
1092
|
+
|
|
1093
|
+
|
|
1094
|
+
def summarize_pr_list(payload: dict[str, Any]) -> str:
|
|
1095
|
+
if payload.get("status") != "ok":
|
|
1096
|
+
return str(payload.get("message") or "Nao foi possivel listar PRs.")
|
|
1097
|
+
items = payload.get("items") or []
|
|
1098
|
+
if not items:
|
|
1099
|
+
return "Nenhuma PR aguardando sua revisao foi encontrada."
|
|
1100
|
+
lines = []
|
|
1101
|
+
for item in items:
|
|
1102
|
+
number = item.get("number")
|
|
1103
|
+
title = item.get("title") or "-"
|
|
1104
|
+
url = item.get("url") or ""
|
|
1105
|
+
lines.append(f"- #{number} {title} {url}".strip())
|
|
1106
|
+
return "\n".join(lines)
|
|
1107
|
+
|
|
1108
|
+
|
|
1109
|
+
def finalize_agent_session(
|
|
1110
|
+
result: dict[str, Any],
|
|
1111
|
+
session: dict[str, Any],
|
|
1112
|
+
prompt: str,
|
|
1113
|
+
*,
|
|
1114
|
+
backend: str | None = None,
|
|
1115
|
+
) -> dict[str, Any]:
|
|
1116
|
+
try:
|
|
1117
|
+
result["session"] = record_exchange(str(session["id"]), prompt=prompt, result=result, backend=backend)
|
|
1118
|
+
except ValueError as exc:
|
|
1119
|
+
raise DevKitError(str(exc)) from exc
|
|
1120
|
+
return result
|
|
489
1121
|
|
|
490
1122
|
|
|
491
1123
|
def invoke_deterministic_route(prompt: str, route: dict[str, Any]) -> dict[str, Any]:
|
|
@@ -1050,6 +1682,8 @@ def print_human(result: dict[str, Any]) -> None:
|
|
|
1050
1682
|
print_llm_configure(result)
|
|
1051
1683
|
elif kind == "llm-default":
|
|
1052
1684
|
print_llm_default(result)
|
|
1685
|
+
elif kind == "llm-preference":
|
|
1686
|
+
print_llm_preference(result)
|
|
1053
1687
|
elif kind == "providers":
|
|
1054
1688
|
print_providers(result)
|
|
1055
1689
|
elif kind == "provider-status":
|
|
@@ -1072,8 +1706,48 @@ def print_human(result: dict[str, Any]) -> None:
|
|
|
1072
1706
|
print_source_remove(result)
|
|
1073
1707
|
elif kind == "memory":
|
|
1074
1708
|
print_memory(result)
|
|
1709
|
+
elif kind == "memory-path":
|
|
1710
|
+
print_memory_path(result)
|
|
1075
1711
|
elif kind == "memory-reset":
|
|
1076
1712
|
print_memory_reset(result)
|
|
1713
|
+
elif kind == "personality":
|
|
1714
|
+
print_personality(result)
|
|
1715
|
+
elif kind == "aliases":
|
|
1716
|
+
print_aliases(result)
|
|
1717
|
+
elif kind == "alias":
|
|
1718
|
+
print_alias(result)
|
|
1719
|
+
elif kind == "sessions":
|
|
1720
|
+
print_sessions(result)
|
|
1721
|
+
elif kind == "session":
|
|
1722
|
+
print_session(result)
|
|
1723
|
+
elif kind == "setup":
|
|
1724
|
+
print_setup(result)
|
|
1725
|
+
elif kind == "toolchain":
|
|
1726
|
+
print_toolchain(result)
|
|
1727
|
+
elif kind == "toolchain-doctor":
|
|
1728
|
+
print_toolchain_doctor(result)
|
|
1729
|
+
elif kind == "toolchain-install":
|
|
1730
|
+
print_toolchain_install(result)
|
|
1731
|
+
elif kind == "tasks":
|
|
1732
|
+
print_tasks(result)
|
|
1733
|
+
elif kind == "task":
|
|
1734
|
+
print_task(result)
|
|
1735
|
+
elif kind == "task-history":
|
|
1736
|
+
print_task_history(result)
|
|
1737
|
+
elif kind == "task-run":
|
|
1738
|
+
print_task_run(result)
|
|
1739
|
+
elif kind == "scheduler":
|
|
1740
|
+
print_scheduler(result)
|
|
1741
|
+
elif kind == "calendar":
|
|
1742
|
+
print_calendar(result)
|
|
1743
|
+
elif kind == "calendar-configure":
|
|
1744
|
+
print_calendar_configure(result)
|
|
1745
|
+
elif kind in {"pr", "pr-review", "pr-automation"}:
|
|
1746
|
+
print_pr(result)
|
|
1747
|
+
elif kind == "permissions":
|
|
1748
|
+
print_permissions(result)
|
|
1749
|
+
elif kind in {"audit", "audit-entry", "audit-export"}:
|
|
1750
|
+
print_audit(result)
|
|
1077
1751
|
elif kind == "install":
|
|
1078
1752
|
print_install(result)
|
|
1079
1753
|
else:
|
|
@@ -1242,6 +1916,10 @@ def print_source_remove(result: dict[str, Any]) -> None:
|
|
|
1242
1916
|
|
|
1243
1917
|
def print_memory(result: dict[str, Any]) -> None:
|
|
1244
1918
|
print(f"Memory home: {result['memory_home']}")
|
|
1919
|
+
if result.get("files"):
|
|
1920
|
+
print("\nFiles:")
|
|
1921
|
+
for item in result["files"]:
|
|
1922
|
+
print(f"- {item['name']}: {item['path']}")
|
|
1245
1923
|
for bucket in ("prompts", "routes", "sources"):
|
|
1246
1924
|
print(f"\n{bucket.title()}:")
|
|
1247
1925
|
items = result["usage"].get(bucket) or []
|
|
@@ -1257,6 +1935,218 @@ def print_memory_reset(result: dict[str, Any]) -> None:
|
|
|
1257
1935
|
print(f"Config: {result['config_path']}")
|
|
1258
1936
|
|
|
1259
1937
|
|
|
1938
|
+
def print_memory_path(result: dict[str, Any]) -> None:
|
|
1939
|
+
print(f"Memory home: {result['home']}")
|
|
1940
|
+
if result.get("created"):
|
|
1941
|
+
print("Created:")
|
|
1942
|
+
for path in result["created"]:
|
|
1943
|
+
print(f"- {path}")
|
|
1944
|
+
print("Files:")
|
|
1945
|
+
for item in result["files"]:
|
|
1946
|
+
print(f"- {item['name']}: {item['path']}")
|
|
1947
|
+
|
|
1948
|
+
|
|
1949
|
+
def print_personality(result: dict[str, Any]) -> None:
|
|
1950
|
+
print(f"Personality: {result.get('status', 'ok')}")
|
|
1951
|
+
print(f"Path: {result['path']}")
|
|
1952
|
+
print(f"Agent name: {result.get('agent_name') or '-'}")
|
|
1953
|
+
print(f"User name: {result.get('user_name') or '-'}")
|
|
1954
|
+
print(f"Language: {result.get('language') or '-'}")
|
|
1955
|
+
print(f"Tone: {result.get('tone') or '-'}")
|
|
1956
|
+
print(f"Detail level: {result.get('detail_level') or '-'}")
|
|
1957
|
+
if result.get("message"):
|
|
1958
|
+
print(result["message"])
|
|
1959
|
+
if result.get("questions"):
|
|
1960
|
+
print("Setup questions:")
|
|
1961
|
+
for question in result["questions"]:
|
|
1962
|
+
print(f"- {question}")
|
|
1963
|
+
|
|
1964
|
+
|
|
1965
|
+
def print_aliases(result: dict[str, Any]) -> None:
|
|
1966
|
+
print(f"Aliases config: {result['config_path']}")
|
|
1967
|
+
if not result["items"]:
|
|
1968
|
+
print("No aliases configured.")
|
|
1969
|
+
return
|
|
1970
|
+
for item in result["items"]:
|
|
1971
|
+
print(f"- {item['name']}: {item['path']}")
|
|
1972
|
+
|
|
1973
|
+
|
|
1974
|
+
def print_alias(result: dict[str, Any]) -> None:
|
|
1975
|
+
print(f"Alias {result['status']}: {result['name']}")
|
|
1976
|
+
if result.get("path"):
|
|
1977
|
+
print(f"Path: {result['path']}")
|
|
1978
|
+
if result.get("removed_paths"):
|
|
1979
|
+
print("Removed:")
|
|
1980
|
+
for path in result["removed_paths"]:
|
|
1981
|
+
print(f"- {path}")
|
|
1982
|
+
print(f"Config: {result['config_path']}")
|
|
1983
|
+
|
|
1984
|
+
|
|
1985
|
+
def print_sessions(result: dict[str, Any]) -> None:
|
|
1986
|
+
print(f"Sessions home: {result['home']}")
|
|
1987
|
+
if result.get("active_session_id"):
|
|
1988
|
+
print(f"Active: {result['active_session_id']}")
|
|
1989
|
+
if not result["items"]:
|
|
1990
|
+
print("No sessions found.")
|
|
1991
|
+
return
|
|
1992
|
+
for item in result["items"]:
|
|
1993
|
+
marker = " active" if item.get("active") else ""
|
|
1994
|
+
print(
|
|
1995
|
+
f"- {item['id']}{marker} {item.get('title') or '-'} "
|
|
1996
|
+
f"{item.get('exchange_count', 0)} exchanges ~{item.get('token_estimate', 0)} tokens"
|
|
1997
|
+
)
|
|
1998
|
+
if item.get("project"):
|
|
1999
|
+
print(f" Project: {item['project']}")
|
|
2000
|
+
|
|
2001
|
+
|
|
2002
|
+
def print_session(result: dict[str, Any]) -> None:
|
|
2003
|
+
session = result["session"]
|
|
2004
|
+
print(f"Session {result['status']}: {session['id']}")
|
|
2005
|
+
print(f"Title: {session.get('title') or '-'}")
|
|
2006
|
+
print(f"Path: {session.get('path') or '-'}")
|
|
2007
|
+
print(f"Project: {session.get('project') or '-'}")
|
|
2008
|
+
print(f"Exchanges: {session.get('exchange_count', 0)}")
|
|
2009
|
+
print(f"Token estimate: {session.get('token_estimate', 0)}")
|
|
2010
|
+
|
|
2011
|
+
|
|
2012
|
+
def print_setup(result: dict[str, Any]) -> None:
|
|
2013
|
+
print(f"Setup: {result['status']}")
|
|
2014
|
+
print(f"Dry-run: {result.get('dry_run', False)}")
|
|
2015
|
+
toolchain = result.get("toolchain") or {}
|
|
2016
|
+
print(f"Toolchain: {toolchain.get('status', '-')}")
|
|
2017
|
+
missing = (toolchain.get("required_missing") or []) + (toolchain.get("optional_missing") or [])
|
|
2018
|
+
if missing:
|
|
2019
|
+
print(f"Missing: {', '.join(missing)}")
|
|
2020
|
+
if result.get("next_steps"):
|
|
2021
|
+
print("Next steps:")
|
|
2022
|
+
for step in result["next_steps"]:
|
|
2023
|
+
print(f"- {step}")
|
|
2024
|
+
|
|
2025
|
+
|
|
2026
|
+
def print_toolchain(result: dict[str, Any]) -> None:
|
|
2027
|
+
print(f"Toolchain: {result['status']}")
|
|
2028
|
+
print(f"Platform: {result['platform']}")
|
|
2029
|
+
for item in result["items"]:
|
|
2030
|
+
print(f"- {item['id']} {item['command']} required={item['required']}")
|
|
2031
|
+
|
|
2032
|
+
|
|
2033
|
+
def print_toolchain_doctor(result: dict[str, Any]) -> None:
|
|
2034
|
+
print(f"Toolchain doctor: {result['status']}")
|
|
2035
|
+
print(f"Platform: {result['platform']}")
|
|
2036
|
+
for item in result["items"]:
|
|
2037
|
+
print(f"- {item['id']}: {item['status']}")
|
|
2038
|
+
if item.get("binary"):
|
|
2039
|
+
print(f" {item['binary']}")
|
|
2040
|
+
if item.get("install"):
|
|
2041
|
+
print(f" Install: {item['install']}")
|
|
2042
|
+
|
|
2043
|
+
|
|
2044
|
+
def print_toolchain_install(result: dict[str, Any]) -> None:
|
|
2045
|
+
print(f"Toolchain install: {result['status']}")
|
|
2046
|
+
if result.get("message"):
|
|
2047
|
+
print(result["message"])
|
|
2048
|
+
for plan in result["plans"]:
|
|
2049
|
+
print(f"- {plan['id']}: {plan.get('command') or '-'}")
|
|
2050
|
+
|
|
2051
|
+
|
|
2052
|
+
def print_tasks(result: dict[str, Any]) -> None:
|
|
2053
|
+
print(f"Tasks: {len(result['items'])}")
|
|
2054
|
+
for item in result["items"]:
|
|
2055
|
+
print(f"- {item['id']} {item['status']} {item.get('title') or '-'}")
|
|
2056
|
+
|
|
2057
|
+
|
|
2058
|
+
def print_task(result: dict[str, Any]) -> None:
|
|
2059
|
+
if result.get("message"):
|
|
2060
|
+
print(result["message"])
|
|
2061
|
+
task = result.get("task") or {}
|
|
2062
|
+
if task:
|
|
2063
|
+
print(f"Task {result['status']}: {task.get('id')}")
|
|
2064
|
+
print(f"Title: {task.get('title') or '-'}")
|
|
2065
|
+
print(f"Status: {task.get('status') or '-'}")
|
|
2066
|
+
|
|
2067
|
+
|
|
2068
|
+
def print_task_history(result: dict[str, Any]) -> None:
|
|
2069
|
+
print(result.get("history") or "No history.")
|
|
2070
|
+
|
|
2071
|
+
|
|
2072
|
+
def print_task_run(result: dict[str, Any]) -> None:
|
|
2073
|
+
print(f"Task run: {result['status']}")
|
|
2074
|
+
if result.get("message"):
|
|
2075
|
+
print(result["message"])
|
|
2076
|
+
|
|
2077
|
+
|
|
2078
|
+
def print_scheduler(result: dict[str, Any]) -> None:
|
|
2079
|
+
print(f"Scheduler: {result['status']}")
|
|
2080
|
+
if result.get("message"):
|
|
2081
|
+
print(result["message"])
|
|
2082
|
+
if "due_count" in result:
|
|
2083
|
+
print(f"Due tasks: {result['due_count']}")
|
|
2084
|
+
|
|
2085
|
+
|
|
2086
|
+
def print_calendar(result: dict[str, Any]) -> None:
|
|
2087
|
+
if result.get("status") != "ok":
|
|
2088
|
+
print(result.get("message") or "Calendar is not available.")
|
|
2089
|
+
for step in result.get("next_steps") or []:
|
|
2090
|
+
print(f"- {step}")
|
|
2091
|
+
return
|
|
2092
|
+
print(calendar_summary(result))
|
|
2093
|
+
|
|
2094
|
+
|
|
2095
|
+
def print_calendar_configure(result: dict[str, Any]) -> None:
|
|
2096
|
+
print(f"Calendar configured: {result['provider']}")
|
|
2097
|
+
print(f"Source: {result['source_ref']}")
|
|
2098
|
+
print("Stored secret: no")
|
|
2099
|
+
|
|
2100
|
+
|
|
2101
|
+
def print_pr(result: dict[str, Any]) -> None:
|
|
2102
|
+
if result.get("message"):
|
|
2103
|
+
print(result["message"])
|
|
2104
|
+
if result.get("items") is not None:
|
|
2105
|
+
print(summarize_pr_list(result))
|
|
2106
|
+
elif result.get("summary"):
|
|
2107
|
+
print(result["summary"])
|
|
2108
|
+
elif result.get("task"):
|
|
2109
|
+
task = result["task"]
|
|
2110
|
+
print(f"PR automation {result['status']}: {task.get('id')}")
|
|
2111
|
+
|
|
2112
|
+
|
|
2113
|
+
def print_permissions(result: dict[str, Any]) -> None:
|
|
2114
|
+
print(f"Permissions: {result['status']}")
|
|
2115
|
+
if result.get("default_level"):
|
|
2116
|
+
print(f"Default: {result['default_level']}")
|
|
2117
|
+
if result.get("grant"):
|
|
2118
|
+
grant = result["grant"]
|
|
2119
|
+
print(f"Grant: {grant.get('agent')} / {grant.get('provider')} -> {grant.get('level')}")
|
|
2120
|
+
if result.get("removed") is not None:
|
|
2121
|
+
print(f"Removed: {len(result.get('removed') or [])}")
|
|
2122
|
+
grants = result.get("grants")
|
|
2123
|
+
if grants is not None:
|
|
2124
|
+
if not grants:
|
|
2125
|
+
print("No explicit grants.")
|
|
2126
|
+
for grant in grants:
|
|
2127
|
+
print(f"- {grant.get('agent')} / {grant.get('provider')} -> {grant.get('level')}")
|
|
2128
|
+
if result.get("json_path"):
|
|
2129
|
+
print(f"Policy: {result['json_path']}")
|
|
2130
|
+
|
|
2131
|
+
|
|
2132
|
+
def print_audit(result: dict[str, Any]) -> None:
|
|
2133
|
+
if result["kind"] == "audit":
|
|
2134
|
+
print(f"Audit home: {result['home']}")
|
|
2135
|
+
for item in result.get("items") or []:
|
|
2136
|
+
print(f"- {item.get('id')} {item.get('created_at')} {item.get('command')} {item.get('status')}")
|
|
2137
|
+
return
|
|
2138
|
+
if result["kind"] == "audit-entry":
|
|
2139
|
+
entry = result.get("entry") or {}
|
|
2140
|
+
print(f"Audit: {entry.get('id')}")
|
|
2141
|
+
print(f"Created: {entry.get('created_at')}")
|
|
2142
|
+
print(f"Command: {entry.get('command')}")
|
|
2143
|
+
print(f"Status: {(entry.get('result') or {}).get('status')}")
|
|
2144
|
+
print(f"JSON: {result.get('json_path')}")
|
|
2145
|
+
print(f"Markdown: {result.get('markdown_path')}")
|
|
2146
|
+
return
|
|
2147
|
+
print(result.get("content") or "")
|
|
2148
|
+
|
|
2149
|
+
|
|
1260
2150
|
def print_llm_backends(result: dict[str, Any]) -> None:
|
|
1261
2151
|
print(f"LLM config: {result['config_path']}")
|
|
1262
2152
|
print(f"Default: {result.get('default') or '-'}")
|
|
@@ -1294,6 +2184,16 @@ def print_llm_default(result: dict[str, Any]) -> None:
|
|
|
1294
2184
|
print(f"Config: {result['config_path']}")
|
|
1295
2185
|
|
|
1296
2186
|
|
|
2187
|
+
def print_llm_preference(result: dict[str, Any]) -> None:
|
|
2188
|
+
print(f"LLM preference: {result.get('status', 'ok')}")
|
|
2189
|
+
print(f"Primary: {result.get('primary') or '-'}")
|
|
2190
|
+
print(f"Fallback enabled: {result.get('fallback_enabled', True)}")
|
|
2191
|
+
print("Order:")
|
|
2192
|
+
for backend_id in result.get("order") or []:
|
|
2193
|
+
print(f"- {backend_id}")
|
|
2194
|
+
print(f"Config: {result['config_path']}")
|
|
2195
|
+
|
|
2196
|
+
|
|
1297
2197
|
def print_providers(result: dict[str, Any]) -> None:
|
|
1298
2198
|
if not result["items"]:
|
|
1299
2199
|
print("No providers found.")
|