agent-devkit 0.1.0 → 0.1.6

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.
Files changed (93) hide show
  1. package/README.md +42 -2
  2. package/package.json +1 -1
  3. package/runtime/README.md +119 -5
  4. package/runtime/agents/README.md +8 -0
  5. package/runtime/agents/execution-reviewer/AGENTS.md +8 -0
  6. package/runtime/agents/execution-reviewer/README.md +13 -0
  7. package/runtime/agents/execution-reviewer/agent.yaml +25 -0
  8. package/runtime/agents/execution-reviewer/capabilities/review-agent-result/capability.yaml +15 -0
  9. package/runtime/agents/execution-reviewer/capabilities/review-agent-result/decision-rules.md +6 -0
  10. package/runtime/agents/execution-reviewer/capabilities/review-agent-result/workflow.md +7 -0
  11. package/runtime/agents/execution-reviewer/capabilities/review-final-output/capability.yaml +15 -0
  12. package/runtime/agents/execution-reviewer/capabilities/review-final-output/decision-rules.md +6 -0
  13. package/runtime/agents/execution-reviewer/capabilities/review-final-output/workflow.md +7 -0
  14. package/runtime/agents/execution-reviewer/capabilities/review-plan/capability.yaml +15 -0
  15. package/runtime/agents/execution-reviewer/capabilities/review-plan/decision-rules.md +6 -0
  16. package/runtime/agents/execution-reviewer/capabilities/review-plan/workflow.md +7 -0
  17. package/runtime/agents/execution-reviewer/infra/README.md +4 -0
  18. package/runtime/agents/execution-reviewer/knowledge/context.md +6 -0
  19. package/runtime/agents/execution-reviewer/knowledge/system.md +18 -0
  20. package/runtime/agents/execution-reviewer/templates/README.md +4 -0
  21. package/runtime/agents/local-llm-operator/AGENTS.md +8 -0
  22. package/runtime/agents/local-llm-operator/README.md +12 -0
  23. package/runtime/agents/local-llm-operator/agent.yaml +26 -0
  24. package/runtime/agents/local-llm-operator/capabilities/delegate-operational-task/capability.yaml +15 -0
  25. package/runtime/agents/local-llm-operator/capabilities/delegate-operational-task/decision-rules.md +6 -0
  26. package/runtime/agents/local-llm-operator/capabilities/delegate-operational-task/workflow.md +7 -0
  27. package/runtime/agents/local-llm-operator/capabilities/inspect-local-models/capability.yaml +15 -0
  28. package/runtime/agents/local-llm-operator/capabilities/inspect-local-models/decision-rules.md +5 -0
  29. package/runtime/agents/local-llm-operator/capabilities/inspect-local-models/workflow.md +6 -0
  30. package/runtime/agents/local-llm-operator/capabilities/select-local-worker/capability.yaml +15 -0
  31. package/runtime/agents/local-llm-operator/capabilities/select-local-worker/decision-rules.md +6 -0
  32. package/runtime/agents/local-llm-operator/capabilities/select-local-worker/workflow.md +7 -0
  33. package/runtime/agents/local-llm-operator/infra/README.md +4 -0
  34. package/runtime/agents/local-llm-operator/knowledge/context.md +6 -0
  35. package/runtime/agents/local-llm-operator/knowledge/system.md +18 -0
  36. package/runtime/agents/local-llm-operator/templates/README.md +4 -0
  37. package/runtime/agents/provider-configurator/AGENTS.md +8 -0
  38. package/runtime/agents/provider-configurator/README.md +5 -0
  39. package/runtime/agents/provider-configurator/agent.yaml +25 -0
  40. package/runtime/agents/provider-configurator/capabilities/collect-provider-credentials/capability.yaml +15 -0
  41. package/runtime/agents/provider-configurator/capabilities/collect-provider-credentials/decision-rules.md +6 -0
  42. package/runtime/agents/provider-configurator/capabilities/collect-provider-credentials/workflow.md +7 -0
  43. package/runtime/agents/provider-configurator/capabilities/configure-provider-source/capability.yaml +15 -0
  44. package/runtime/agents/provider-configurator/capabilities/configure-provider-source/decision-rules.md +6 -0
  45. package/runtime/agents/provider-configurator/capabilities/configure-provider-source/workflow.md +8 -0
  46. package/runtime/agents/provider-configurator/capabilities/validate-provider-readiness/capability.yaml +15 -0
  47. package/runtime/agents/provider-configurator/capabilities/validate-provider-readiness/decision-rules.md +6 -0
  48. package/runtime/agents/provider-configurator/capabilities/validate-provider-readiness/workflow.md +7 -0
  49. package/runtime/agents/provider-configurator/infra/README.md +5 -0
  50. package/runtime/agents/provider-configurator/knowledge/context.md +8 -0
  51. package/runtime/agents/provider-configurator/knowledge/system.md +19 -0
  52. package/runtime/agents/provider-configurator/templates/README.md +4 -0
  53. package/runtime/agents/task-orchestrator/AGENTS.md +8 -0
  54. package/runtime/agents/task-orchestrator/README.md +7 -0
  55. package/runtime/agents/task-orchestrator/agent.yaml +31 -0
  56. package/runtime/agents/task-orchestrator/capabilities/coordinate-execution/capability.yaml +15 -0
  57. package/runtime/agents/task-orchestrator/capabilities/coordinate-execution/decision-rules.md +6 -0
  58. package/runtime/agents/task-orchestrator/capabilities/coordinate-execution/workflow.md +7 -0
  59. package/runtime/agents/task-orchestrator/capabilities/plan-task/capability.yaml +15 -0
  60. package/runtime/agents/task-orchestrator/capabilities/plan-task/decision-rules.md +6 -0
  61. package/runtime/agents/task-orchestrator/capabilities/plan-task/workflow.md +8 -0
  62. package/runtime/agents/task-orchestrator/capabilities/select-specialists/capability.yaml +15 -0
  63. package/runtime/agents/task-orchestrator/capabilities/select-specialists/decision-rules.md +6 -0
  64. package/runtime/agents/task-orchestrator/capabilities/select-specialists/workflow.md +7 -0
  65. package/runtime/agents/task-orchestrator/infra/README.md +5 -0
  66. package/runtime/agents/task-orchestrator/knowledge/context.md +9 -0
  67. package/runtime/agents/task-orchestrator/knowledge/system.md +20 -0
  68. package/runtime/agents/task-orchestrator/templates/README.md +4 -0
  69. package/runtime/cli/README.md +111 -10
  70. package/runtime/cli/aikit/__init__.py +1 -1
  71. package/runtime/cli/aikit/agent_executor.py +66 -0
  72. package/runtime/cli/aikit/agent_registry.py +80 -0
  73. package/runtime/cli/aikit/app_home.py +87 -2
  74. package/runtime/cli/aikit/audit.py +44 -0
  75. package/runtime/cli/aikit/configuration_orchestrator.py +291 -0
  76. package/runtime/cli/aikit/control_router.py +298 -0
  77. package/runtime/cli/aikit/decision_store.py +175 -0
  78. package/runtime/cli/aikit/diagnostics.py +2 -1
  79. package/runtime/cli/aikit/execution_reviewer.py +174 -0
  80. package/runtime/cli/aikit/fallback.py +48 -2
  81. package/runtime/cli/aikit/install.py +4 -3
  82. package/runtime/cli/aikit/llm.py +9 -0
  83. package/runtime/cli/aikit/local_llm_operator.py +112 -0
  84. package/runtime/cli/aikit/lock.py +4 -1
  85. package/runtime/cli/aikit/main.py +604 -21
  86. package/runtime/cli/aikit/model_router.py +79 -0
  87. package/runtime/cli/aikit/ollama.py +237 -0
  88. package/runtime/cli/aikit/orchestrator.py +535 -0
  89. package/runtime/cli/aikit/provider_wizard.py +19 -0
  90. package/runtime/cli/aikit/review_gate.py +40 -0
  91. package/runtime/cli/aikit/sessions.py +37 -0
  92. package/runtime/cli/aikit/sources.py +6 -2
  93. package/runtime/cli/aikit/wizard_state.py +503 -0
@@ -14,9 +14,16 @@ from typing import Any
14
14
 
15
15
  from cli.aikit.aliases import add_alias, list_aliases, remove_alias, sync_aliases
16
16
  from cli.aikit import __version__
17
+ from cli.aikit.agent_executor import execute_plan_tasks
18
+ from cli.aikit.app_home import app_home_status, migrate_default_home
17
19
  from cli.aikit.audit import export_audit, list_audits, record_audit, show_audit
18
20
  from cli.aikit.calendar import calendar_list, calendar_summary, calendar_today, calendar_tomorrow, configure_calendar
21
+ from cli.aikit.configuration_orchestrator import provider_wizard_from_requirement
22
+ from cli.aikit.control_router import dispatch_natural_control_prompt as route_natural_control_prompt
23
+ from cli.aikit.control_router import plan_natural_control_prompt
24
+ from cli.aikit.decision_store import forget_decision, list_decisions, reset_decisions, set_decision
19
25
  from cli.aikit.diagnostics import build_diagnostics
26
+ from cli.aikit.execution_reviewer import enforce_execution_review
20
27
  from cli.aikit.fallback import evaluate_provider_requirements
21
28
  from cli.aikit.github_pr import pr_create_automation, pr_inspect, pr_list_review_requests, pr_review
22
29
  from cli.aikit.guardrails import evaluate_execution_guardrails
@@ -31,8 +38,19 @@ from cli.aikit.llm import (
31
38
  set_llm_preference,
32
39
  )
33
40
  from cli.aikit.memory import memory_path_payload, napkin_context, record_usage, reset_memory, show_memory
41
+ from cli.aikit.model_router import build_model_plan
42
+ from cli.aikit.ollama import ollama_models, ollama_pull, ollama_status, ollama_update
43
+ from cli.aikit.local_llm_operator import enrich_prompt_with_local_result, maybe_delegate_local_llm
44
+ from cli.aikit.orchestrator import (
45
+ attach_source_to_primary_task,
46
+ build_execution_plan,
47
+ mark_plan_after_execution,
48
+ mark_review_task,
49
+ )
34
50
  from cli.aikit.personality import load_personality, reset_personality, setup_personality, update_personality
35
51
  from cli.aikit.permissions import grant_permission, revoke_permission, show_permissions
52
+ from cli.aikit.provider_wizard import missing_source_wizard
53
+ from cli.aikit.review_gate import build_review_gate
36
54
  from cli.aikit.sessions import (
37
55
  build_contextual_prompt,
38
56
  get_or_create_session,
@@ -79,6 +97,14 @@ from cli.aikit.sources import (
79
97
  source_env,
80
98
  source_status,
81
99
  )
100
+ from cli.aikit.wizard_state import (
101
+ WizardStateError,
102
+ answer_wizard,
103
+ cancel_wizard,
104
+ create_provider_wizard,
105
+ list_wizards,
106
+ show_wizard,
107
+ )
82
108
 
83
109
 
84
110
  DEFAULT_ROOT = Path(__file__).resolve().parents[2]
@@ -109,7 +135,14 @@ DETERMINISTIC_COMMANDS = (
109
135
  "pr",
110
136
  "permissions",
111
137
  "audit",
138
+ "config",
139
+ "tools",
140
+ "integrations",
141
+ "skills",
142
+ "decisions",
143
+ "ollama",
112
144
  "install",
145
+ "wizard",
113
146
  )
114
147
  LLM_COMMANDS = ("agent",)
115
148
 
@@ -132,6 +165,8 @@ def main(argv: list[str] | None = None, *, prog: str | None = None) -> int:
132
165
 
133
166
  if result is None:
134
167
  return 0
168
+ if not getattr(args, "json", False):
169
+ result = maybe_run_interactive_wizard(result)
135
170
  maybe_record_cli_audit(args, result=result, error=None)
136
171
 
137
172
  if getattr(args, "json", False):
@@ -211,6 +246,14 @@ def build_parser(prog: str | None = None) -> argparse.ArgumentParser:
211
246
  source_parser.add_argument("--default-for-agent", action="append", default=[], help="agent id that should use this source by default")
212
247
  source_parser.add_argument("--set-default", action="store_true", help="set as default source for its provider")
213
248
 
249
+ wizard_parser = subparsers.add_parser("wizard", help="continue agentic setup wizards")
250
+ wizard_parser.add_argument("--json", action="store_true", default=argparse.SUPPRESS, help=argparse.SUPPRESS)
251
+ wizard_parser.add_argument("action", nargs="?", default="list", choices=["list", "show", "answer", "cancel"])
252
+ wizard_parser.add_argument("wizard_id", nargs="?")
253
+ wizard_parser.add_argument("answer", nargs="*")
254
+ wizard_parser.add_argument("--status", help="filter wizard list by status")
255
+ wizard_parser.add_argument("--no-run", action="store_true", help="do not resume the original prompt after completing a wizard")
256
+
214
257
  memory_parser = subparsers.add_parser("memory", help="inspect or reset local AI DevKit memory")
215
258
  memory_parser.add_argument("--json", action="store_true", default=argparse.SUPPRESS, help=argparse.SUPPRESS)
216
259
  memory_parser.add_argument("action", nargs="?", default="show", choices=["show", "path", "reset"])
@@ -306,13 +349,41 @@ def build_parser(prog: str | None = None) -> argparse.ArgumentParser:
306
349
  audit_parser.add_argument("--limit", type=int, default=20)
307
350
  audit_parser.add_argument("--format", default="md", choices=["md", "json"])
308
351
 
352
+ config_parser = subparsers.add_parser("config", help="inspect local Agent DevKit configuration")
353
+ config_parser.add_argument("--json", action="store_true", default=argparse.SUPPRESS, help=argparse.SUPPRESS)
354
+ config_parser.add_argument("action", nargs="?", default="show", choices=["show", "path", "migrate-home"])
355
+ config_parser.add_argument("--dry-run", action="store_true", help="plan home migration without moving files")
356
+
357
+ for command_name, help_text in (
358
+ ("tools", "manage enabled local tools"),
359
+ ("integrations", "manage provider integration decisions"),
360
+ ("skills", "manage local skill decisions"),
361
+ ):
362
+ control_parser = subparsers.add_parser(command_name, help=help_text)
363
+ control_parser.add_argument("--json", action="store_true", default=argparse.SUPPRESS, help=argparse.SUPPRESS)
364
+ control_parser.add_argument("action", nargs="?", default="list", choices=["list", "enable", "disable"])
365
+ control_parser.add_argument("item_id", nargs="?")
366
+
367
+ decisions_parser = subparsers.add_parser("decisions", help="inspect or reset local opt-in and opt-out decisions")
368
+ decisions_parser.add_argument("--json", action="store_true", default=argparse.SUPPRESS, help=argparse.SUPPRESS)
369
+ decisions_parser.add_argument("action", nargs="?", default="list", choices=["list", "forget", "reset"])
370
+ decisions_parser.add_argument("item_id", nargs="?")
371
+ decisions_parser.add_argument("--category", choices=["tools", "integrations", "skills", "llms"])
372
+
373
+ ollama_parser = subparsers.add_parser("ollama", help="inspect and manage local Ollama models")
374
+ ollama_parser.add_argument("--json", action="store_true", default=argparse.SUPPRESS, help=argparse.SUPPRESS)
375
+ ollama_parser.add_argument("action", nargs="?", default="status", choices=["status", "models", "pull", "update"])
376
+ ollama_parser.add_argument("model", nargs="?")
377
+ ollama_parser.add_argument("--yes", action="store_true", help="confirm Ollama model or update operation")
378
+ ollama_parser.add_argument("--dry-run", action="store_true", help="show Ollama operation without executing it")
379
+
309
380
  llm_parser = subparsers.add_parser("llm", help="manage LLM backends")
310
381
  llm_parser.add_argument("--json", action="store_true", default=argparse.SUPPRESS, help=argparse.SUPPRESS)
311
382
  llm_parser.add_argument(
312
383
  "action",
313
384
  nargs="?",
314
385
  default="list",
315
- choices=["list", "doctor", "configure", "set-default", "preference"],
386
+ choices=["list", "doctor", "configure", "set-default", "default", "enable", "disable", "preference"],
316
387
  )
317
388
  llm_parser.add_argument("backend", nargs="?")
318
389
  llm_parser.add_argument("preference_value", nargs="?")
@@ -401,6 +472,8 @@ def dispatch(args: argparse.Namespace) -> dict[str, Any] | None:
401
472
  return dispatch_credential(args)
402
473
  if command == "source":
403
474
  return dispatch_source(args)
475
+ if command == "wizard":
476
+ return dispatch_wizard(args)
404
477
  if command == "memory":
405
478
  return dispatch_memory(args)
406
479
  if command == "personality":
@@ -425,6 +498,14 @@ def dispatch(args: argparse.Namespace) -> dict[str, Any] | None:
425
498
  return dispatch_permissions(args)
426
499
  if command == "audit":
427
500
  return dispatch_audit(args)
501
+ if command == "config":
502
+ return dispatch_config(args)
503
+ if command in {"tools", "integrations", "skills"}:
504
+ return dispatch_control_category(command, args)
505
+ if command == "decisions":
506
+ return dispatch_decisions(args)
507
+ if command == "ollama":
508
+ return dispatch_ollama(args)
428
509
  if command == "llm":
429
510
  return dispatch_llm(args)
430
511
  if command == "install":
@@ -529,10 +610,15 @@ def dispatch_llm(args: argparse.Namespace) -> dict[str, Any]:
529
610
  command=args.host_command,
530
611
  set_default=args.set_default,
531
612
  )
532
- if args.action == "set-default":
613
+ if args.action in {"set-default", "default"}:
533
614
  if not args.backend:
534
- raise DevKitError("llm set-default requires a backend")
615
+ raise DevKitError(f"llm {args.action} requires a backend")
535
616
  return set_default_backend(args.backend)
617
+ if args.action in {"enable", "disable"}:
618
+ if not args.backend:
619
+ raise DevKitError(f"llm {args.action} requires a backend")
620
+ state = "enabled" if args.action == "enable" else "disabled_by_user"
621
+ return set_decision("llms", args.backend, state, reason=f"llm {args.action} command")
536
622
  if args.action == "preference":
537
623
  if args.backend in {None, "show"}:
538
624
  return llm_preference()
@@ -551,6 +637,88 @@ def dispatch_llm(args: argparse.Namespace) -> dict[str, Any]:
551
637
  raise DevKitError(f"unsupported llm action: {args.action}")
552
638
 
553
639
 
640
+ def dispatch_config(args: argparse.Namespace) -> dict[str, Any]:
641
+ from cli.aikit.llm import config_path
642
+
643
+ if args.action == "path":
644
+ return {"kind": "config", "status": "ok", "path": str(config_path()), "home": app_home_status()}
645
+ if args.action == "migrate-home":
646
+ return migrate_default_home(dry_run=effective_dry_run(args))
647
+ if args.action == "show":
648
+ return {
649
+ "kind": "config",
650
+ "status": "ok",
651
+ "path": str(config_path()),
652
+ "home": app_home_status(),
653
+ "decisions": list_decisions(),
654
+ "llm": llm_preference(),
655
+ "ollama": ollama_status(),
656
+ }
657
+ raise DevKitError(f"unsupported config action: {args.action}")
658
+
659
+
660
+ def dispatch_control_category(command: str, args: argparse.Namespace) -> dict[str, Any]:
661
+ try:
662
+ category = command
663
+ if args.action == "list":
664
+ if args.item_id:
665
+ raise DevKitError(f"{command} list does not accept an item id")
666
+ payload = list_decisions(category)
667
+ payload["kind"] = command
668
+ return payload
669
+ if args.action in {"enable", "disable"}:
670
+ if not args.item_id:
671
+ raise DevKitError(f"{command} {args.action} requires an item id")
672
+ state = "enabled" if args.action == "enable" else "disabled_by_user"
673
+ payload = set_decision(category, args.item_id, state, reason=f"{command} {args.action} command")
674
+ payload["kind"] = command[:-1] if command.endswith("s") else command
675
+ return payload
676
+ except ValueError as exc:
677
+ raise DevKitError(str(exc)) from exc
678
+ raise DevKitError(f"unsupported {command} action: {args.action}")
679
+
680
+
681
+ def dispatch_decisions(args: argparse.Namespace) -> dict[str, Any]:
682
+ try:
683
+ if args.action == "list":
684
+ if args.item_id:
685
+ raise DevKitError("decisions list does not accept an item id")
686
+ return list_decisions(args.category)
687
+ if args.action == "reset":
688
+ if args.item_id:
689
+ raise DevKitError("decisions reset does not accept an item id")
690
+ return reset_decisions(args.category)
691
+ if args.action == "forget":
692
+ if not args.item_id:
693
+ raise DevKitError("decisions forget requires an item id")
694
+ category = args.category or "tools"
695
+ return forget_decision(category, args.item_id)
696
+ except ValueError as exc:
697
+ raise DevKitError(str(exc)) from exc
698
+ raise DevKitError(f"unsupported decisions action: {args.action}")
699
+
700
+
701
+ def dispatch_ollama(args: argparse.Namespace) -> dict[str, Any]:
702
+ try:
703
+ if args.action == "status":
704
+ if args.model:
705
+ raise DevKitError("ollama status does not accept a model")
706
+ return ollama_status()
707
+ if args.action == "models":
708
+ if args.model:
709
+ raise DevKitError("ollama models does not accept a model")
710
+ return ollama_models()
711
+ if args.action == "pull":
712
+ return ollama_pull(args.model, yes=args.yes, dry_run=effective_dry_run(args))
713
+ if args.action == "update":
714
+ if args.model:
715
+ raise DevKitError("ollama update does not accept a model")
716
+ return ollama_update(yes=args.yes, dry_run=effective_dry_run(args))
717
+ except ValueError as exc:
718
+ raise DevKitError(str(exc)) from exc
719
+ raise DevKitError(f"unsupported ollama action: {args.action}")
720
+
721
+
554
722
  def dispatch_install(args: argparse.Namespace) -> dict[str, Any]:
555
723
  try:
556
724
  return install_runtime(
@@ -645,6 +813,117 @@ def dispatch_source(args: argparse.Namespace) -> dict[str, Any]:
645
813
  raise DevKitError(f"unsupported source action: {args.action}")
646
814
 
647
815
 
816
+ def dispatch_wizard(args: argparse.Namespace) -> dict[str, Any]:
817
+ try:
818
+ if args.action == "list":
819
+ return list_wizards(status=args.status)
820
+ if args.action == "show":
821
+ require_id(args.wizard_id, "wizard show")
822
+ return show_wizard(args.wizard_id)
823
+ if args.action == "cancel":
824
+ require_id(args.wizard_id, "wizard cancel")
825
+ return cancel_wizard(args.wizard_id)
826
+ if args.action == "answer":
827
+ require_id(args.wizard_id, "wizard answer")
828
+ answer = " ".join(args.answer or []).strip()
829
+ if not answer:
830
+ raise DevKitError("wizard answer requires an answer")
831
+ payload = answer_wizard(args.wizard_id, answer)
832
+ if payload.get("status") == "completed" and not args.no_run:
833
+ resume_prompt = payload.get("resume_prompt") or (payload.get("wizard") or {}).get("resume_prompt")
834
+ if resume_prompt:
835
+ payload["resumed_prompt"] = True
836
+ payload["resume_result"] = resume_agent_prompt(str(resume_prompt))
837
+ else:
838
+ payload["resumed_prompt"] = False
839
+ else:
840
+ payload.setdefault("resumed_prompt", False)
841
+ return payload
842
+ except WizardStateError as exc:
843
+ raise DevKitError(str(exc)) from exc
844
+ except SourceRegistryError as exc:
845
+ raise DevKitError(str(exc)) from exc
846
+ raise DevKitError(f"unsupported wizard action: {args.action}")
847
+
848
+
849
+ def maybe_run_interactive_wizard(result: dict[str, Any]) -> dict[str, Any]:
850
+ if not sys.stdin.isatty() or not sys.stdout.isatty():
851
+ return result
852
+ wizard = result.get("setup_wizard") if isinstance(result.get("setup_wizard"), dict) else None
853
+ if not wizard or not wizard.get("wizard_id") or result.get("status") != "needs-input":
854
+ return result
855
+ print(result.get("message") or "O agente precisa de configuracao antes de continuar.")
856
+ return run_interactive_wizard(str(wizard["wizard_id"]))
857
+
858
+
859
+ def run_interactive_wizard(wizard_id: str) -> dict[str, Any]:
860
+ payload = show_wizard(wizard_id)
861
+ while True:
862
+ wizard = payload.get("wizard") if isinstance(payload.get("wizard"), dict) else {}
863
+ question = payload.get("next_question") or wizard.get("next_question")
864
+ if not isinstance(question, dict):
865
+ return payload
866
+ print_interactive_question(question)
867
+ try:
868
+ answer = input("> ")
869
+ except (EOFError, KeyboardInterrupt):
870
+ print()
871
+ return cancel_wizard(wizard_id, reason="interactive wizard interrupted")
872
+ if answer.strip().lower() in {"cancelar", "cancel", "sair", "exit"}:
873
+ return cancel_wizard(wizard_id, reason="interactive wizard cancelled by user")
874
+ try:
875
+ payload = answer_wizard(wizard_id, answer)
876
+ except WizardStateError as exc:
877
+ print(f"Erro: {exc}")
878
+ continue
879
+ if payload.get("status") == "completed":
880
+ resume_prompt = payload.get("resume_prompt") or (payload.get("wizard") or {}).get("resume_prompt")
881
+ if resume_prompt:
882
+ payload["resumed_prompt"] = True
883
+ payload["resume_result"] = resume_agent_prompt(str(resume_prompt))
884
+ else:
885
+ payload["resumed_prompt"] = False
886
+ return payload
887
+ if payload.get("status") in {"cancelled", "denied-by-user", "failed"}:
888
+ payload.setdefault("resumed_prompt", False)
889
+ return payload
890
+
891
+
892
+ def print_interactive_question(question: dict[str, Any]) -> None:
893
+ text = question.get("text") or "Informe a resposta."
894
+ print(f"\nPergunta: {text}")
895
+ if question.get("type") == "confirm":
896
+ print("[s/N]")
897
+ options = question.get("options")
898
+ if isinstance(options, list) and options:
899
+ print("Opcoes: " + ", ".join(str(item) for item in options))
900
+ if question.get("suggested_value"):
901
+ print(f"Sugestao: {question['suggested_value']}")
902
+ if question.get("env_ref_key"):
903
+ print("Informe o nome da variavel de ambiente, nao o valor da credencial.")
904
+ print("Digite 'cancelar' para interromper.")
905
+
906
+
907
+ def resume_agent_prompt(prompt: str) -> dict[str, Any]:
908
+ args = argparse.Namespace(
909
+ command="agent",
910
+ prompt=prompt.split(),
911
+ session_id=None,
912
+ new_session=False,
913
+ llm=None,
914
+ no_llm_fallback=False,
915
+ dry_run=False,
916
+ global_dry_run=False,
917
+ prog_name="agent",
918
+ json=True,
919
+ version=False,
920
+ sessions_shortcut=False,
921
+ )
922
+ result = agent_requires_llm(args)
923
+ result.pop("audit", None)
924
+ return result
925
+
926
+
648
927
  def dispatch_memory(args: argparse.Namespace) -> dict[str, Any]:
649
928
  if args.action == "show":
650
929
  return show_memory(ROOT, agent_id=args.agent_id, source_id=args.source_id)
@@ -962,25 +1241,131 @@ def agent_requires_llm(args: argparse.Namespace) -> dict[str, Any]:
962
1241
  return finalize_agent_session(natural_result, session, prompt, backend=args.llm)
963
1242
  route = route_prompt(prompt)
964
1243
  if route:
965
- result = invoke_deterministic_route(prompt, route)
1244
+ result = invoke_agentic_route(prompt, route)
966
1245
  return finalize_agent_session(result, session, prompt, backend=args.llm)
967
1246
  contextual_prompt = build_contextual_prompt(str(session["id"]), prompt)
1247
+ execution_plan = build_execution_plan(ROOT, prompt, dry_run=False)
1248
+ if execution_plan.get("configuration_tasks"):
1249
+ result = agentic_needs_input_from_plan(prompt, execution_plan)
1250
+ return finalize_agent_session(result, session, prompt, backend=args.llm)
1251
+ model_plan = execution_plan.get("model_plan") if isinstance(execution_plan.get("model_plan"), dict) else build_model_plan(prompt)
1252
+ review_gate = execution_plan.get("review_gate") if isinstance(execution_plan.get("review_gate"), dict) else build_review_gate(prompt, model_plan=model_plan)
1253
+ local_llm_execution = maybe_delegate_local_llm(prompt, model_plan)
1254
+ coordinator_prompt = enrich_prompt_with_local_result(contextual_prompt, local_llm_execution)
968
1255
  result = invoke_agent_prompt(
969
- contextual_prompt,
1256
+ coordinator_prompt,
970
1257
  args.llm,
971
1258
  public_name=name,
972
1259
  allow_fallback=not args.no_llm_fallback,
973
1260
  )
1261
+ review_result = {
1262
+ "kind": "execution-review",
1263
+ "agent_id": "execution-reviewer",
1264
+ "capability_id": "review-final-output",
1265
+ "status": "not-run",
1266
+ "ok": False,
1267
+ }
1268
+ if result.get("ok"):
1269
+ result, review_gate, review_result = enforce_execution_review(
1270
+ prompt=prompt,
1271
+ result=result,
1272
+ review_gate=review_gate,
1273
+ execution_plan=execution_plan,
1274
+ producer_backend=str(result.get("llm_backend") or args.llm or ""),
1275
+ )
1276
+ if review_result.get("ok"):
1277
+ execution_plan = mark_review_task(execution_plan, reviewer=str(review_result.get("llm_backend") or "execution-reviewer"))
1278
+ elif review_gate.get("status") == "needs-review":
1279
+ execution_plan = mark_review_task_needs_review(execution_plan, review_result)
1280
+ result["model_plan"] = model_plan
1281
+ result["local_llm_execution"] = local_llm_execution
1282
+ result["review_gate"] = review_gate
1283
+ result["review_result"] = review_result
1284
+ result["review_task"] = execution_plan.get("review_task")
1285
+ result["execution_plan"] = execution_plan
1286
+ result["orchestration_trace"] = execution_plan.get("trace", [])
974
1287
  result["prompt_length"] = len(prompt)
975
1288
  result["session_context_applied"] = contextual_prompt != prompt
1289
+ result["local_context_applied"] = coordinator_prompt != contextual_prompt
976
1290
  if result.get("response"):
977
1291
  result["response"] = enforce_identity_response(str(result["response"]), prompt, name=name)
978
1292
  result["identity"] = {"name": name, "source": "local"}
979
1293
  return finalize_agent_session(result, session, prompt, backend=result.get("llm_backend") or args.llm)
980
1294
 
981
1295
 
1296
+ def mark_review_task_needs_review(execution_plan: dict[str, Any], review_result: dict[str, Any]) -> dict[str, Any]:
1297
+ task = dict(execution_plan.get("review_task") or {})
1298
+ if task:
1299
+ task["status"] = "needs-review"
1300
+ task["reviewer"] = None
1301
+ task["message"] = review_result.get("message")
1302
+ execution_plan["review_task"] = task
1303
+ return execution_plan
1304
+
1305
+
1306
+ def agentic_needs_input_from_plan(prompt: str, execution_plan: dict[str, Any]) -> dict[str, Any]:
1307
+ configuration_task = next(
1308
+ (task for task in execution_plan.get("configuration_tasks") or [] if isinstance(task, dict)),
1309
+ {},
1310
+ )
1311
+ wizard = configuration_task.get("setup_wizard") if isinstance(configuration_task.get("setup_wizard"), dict) else {}
1312
+ provider = configuration_task.get("provider") or wizard.get("provider")
1313
+ payload = {
1314
+ "kind": "agent",
1315
+ "status": "needs-input",
1316
+ "ok": False,
1317
+ "mode": "agentic-plan",
1318
+ "prompt_received": True,
1319
+ "prompt_length": len(prompt),
1320
+ "provider": provider,
1321
+ "source_provider": provider,
1322
+ "requires_source": True,
1323
+ "execution_plan": execution_plan,
1324
+ "orchestration_trace": execution_plan.get("trace", []),
1325
+ "setup_wizard": wizard,
1326
+ "next_question": wizard.get("next_question"),
1327
+ "message": wizard.get("message") or "O plano multiagente precisa de configuracao antes de executar.",
1328
+ "next_steps": [
1329
+ "Responda a pergunta do wizard para autorizar ou negar a configuracao deste provider.",
1330
+ "Depois de configurar a fonte, reexecute ou retome o mesmo prompt.",
1331
+ ],
1332
+ "exit_code": 2,
1333
+ }
1334
+ return persist_setup_wizard_payload(payload, execution_plan=execution_plan)
1335
+
1336
+
1337
+ def persist_setup_wizard_payload(
1338
+ payload: dict[str, Any],
1339
+ *,
1340
+ execution_plan: dict[str, Any] | None = None,
1341
+ route: dict[str, Any] | None = None,
1342
+ ) -> dict[str, Any]:
1343
+ wizard = payload.get("setup_wizard")
1344
+ if not isinstance(wizard, dict) or wizard.get("wizard_id"):
1345
+ return payload
1346
+ persisted = create_provider_wizard(
1347
+ wizard,
1348
+ execution_plan=execution_plan or payload.get("execution_plan"),
1349
+ route=route or payload.get("route"),
1350
+ )
1351
+ payload["setup_wizard"] = persisted
1352
+ payload["next_question"] = persisted.get("next_question")
1353
+ if execution_plan and execution_plan.get("configuration_tasks"):
1354
+ for task in execution_plan.get("configuration_tasks") or []:
1355
+ if isinstance(task, dict) and task.get("provider") == persisted.get("provider"):
1356
+ task["setup_wizard"] = persisted
1357
+ break
1358
+ payload.setdefault("wizard_id", persisted.get("wizard_id"))
1359
+ return payload
1360
+
1361
+
982
1362
  def dispatch_natural_operational_prompt(prompt: str) -> dict[str, Any] | None:
983
1363
  normalized = " ".join(prompt.lower().split())
1364
+ control_result = dispatch_natural_control_prompt(normalized)
1365
+ if control_result:
1366
+ control_result["prompt_received"] = True
1367
+ control_result["prompt_length"] = len(prompt)
1368
+ return control_result
984
1369
  if "agenda" in normalized:
985
1370
  if "amanha" in normalized or "amanhã" in normalized:
986
1371
  payload = calendar_tomorrow()
@@ -1028,9 +1413,21 @@ def dispatch_natural_operational_prompt(prompt: str) -> dict[str, Any] | None:
1028
1413
  return None
1029
1414
 
1030
1415
 
1416
+ def dispatch_natural_control_prompt(normalized_prompt: str) -> dict[str, Any] | None:
1417
+ return route_natural_control_prompt(ROOT, normalized_prompt)
1418
+
1419
+
1031
1420
  def build_agent_dry_run_plan(prompt: str, args: argparse.Namespace) -> dict[str, Any]:
1032
1421
  normalized = " ".join(prompt.lower().split())
1422
+ control_plan = plan_natural_control_prompt(ROOT, normalized)
1423
+ if control_plan:
1424
+ control_plan["prompt_received"] = True
1425
+ control_plan["prompt_length"] = len(prompt)
1426
+ return control_plan
1033
1427
  route = route_prompt(prompt)
1428
+ execution_plan = build_execution_plan(ROOT, prompt, dry_run=True)
1429
+ model_plan = execution_plan.get("model_plan") if isinstance(execution_plan.get("model_plan"), dict) else build_model_plan(prompt, route=route)
1430
+ review_gate = execution_plan.get("review_gate") if isinstance(execution_plan.get("review_gate"), dict) else build_review_gate(prompt, route=route, model_plan=model_plan)
1034
1431
  plan: dict[str, Any] = {
1035
1432
  "kind": "agent",
1036
1433
  "status": "planned",
@@ -1047,6 +1444,10 @@ def build_agent_dry_run_plan(prompt: str, args: argparse.Namespace) -> dict[str,
1047
1444
  "providers": {"used": [], "missing": [], "skipped": []},
1048
1445
  "commands": [],
1049
1446
  "permissions": [],
1447
+ "model_plan": model_plan,
1448
+ "review_gate": review_gate,
1449
+ "execution_plan": execution_plan,
1450
+ "orchestration_trace": execution_plan.get("trace", []),
1050
1451
  "response": "Dry-run: nenhuma chamada LLM ou escrita externa foi executada.",
1051
1452
  }
1052
1453
  if "agenda" in normalized:
@@ -1120,7 +1521,8 @@ def finalize_agent_session(
1120
1521
  return result
1121
1522
 
1122
1523
 
1123
- def invoke_deterministic_route(prompt: str, route: dict[str, Any]) -> dict[str, Any]:
1524
+ def invoke_agentic_route(prompt: str, route: dict[str, Any]) -> dict[str, Any]:
1525
+ execution_plan = build_execution_plan(ROOT, prompt, dry_run=False)
1124
1526
  try:
1125
1527
  source = resolve_source(
1126
1528
  provider=route.get("provider"),
@@ -1131,43 +1533,96 @@ def invoke_deterministic_route(prompt: str, route: dict[str, Any]) -> dict[str,
1131
1533
  raise DevKitError(str(exc)) from exc
1132
1534
 
1133
1535
  if not source:
1134
- return {
1536
+ wizard = missing_source_wizard(prompt, route, root=ROOT)
1537
+ if execution_plan.get("configuration_tasks"):
1538
+ execution_plan["configuration_tasks"][0]["setup_wizard"] = wizard
1539
+ execution_plan["status"] = "needs-input"
1540
+ execution_plan["trace"] = [
1541
+ {"agent_id": "task-orchestrator", "action": "plan", "status": "needs-input"},
1542
+ {"agent_id": "provider-configurator", "action": "configure", "status": "waiting-for-user"},
1543
+ ]
1544
+ payload = {
1135
1545
  "kind": "agent",
1136
1546
  "status": "needs-input",
1137
1547
  "ok": False,
1138
1548
  "requires_source": True,
1549
+ "provider": route.get("provider"),
1139
1550
  "source_provider": route.get("provider"),
1140
1551
  "prompt_received": True,
1141
1552
  "prompt_length": len(prompt),
1142
1553
  "route": route,
1143
1554
  "napkin": napkin_context(ROOT, agent_id=route.get("agent_id")),
1144
- "message": "agent identified the task, but no reusable source is configured for this provider.",
1555
+ "execution_plan": execution_plan,
1556
+ "orchestration_trace": execution_plan.get("trace", []),
1557
+ "setup_wizard": wizard,
1558
+ "next_question": wizard.get("next_question"),
1559
+ "message": wizard.get("message"),
1145
1560
  "next_steps": [
1146
- "Configure a source with `agent source add azure-sustentacao --provider azure-devops --config project=<project> --env AZURE_DEVOPS_PAT=AZURE_DEVOPS_PAT --default-for card`.",
1147
- "Use `--config fixture=<path>` for local fixture-based tests or demos.",
1148
- "Rerun the same prompt after configuring the source.",
1561
+ "Responda a pergunta do wizard para autorizar ou negar a configuracao desta fonte.",
1562
+ "Se preferir teste local, configure uma source com fixture sem armazenar segredos.",
1563
+ "O prompt original sera retomado apos a fonte reutilizavel ser configurada.",
1149
1564
  ],
1150
1565
  "exit_code": 2,
1151
1566
  }
1567
+ return persist_setup_wizard_payload(payload, execution_plan=execution_plan, route=route)
1152
1568
 
1153
- agent = load_agent(str(route["agent_id"]))
1154
- capability_args = [*route.get("args", []), "--source", str(source["id"])]
1155
- result = run_capability(agent, str(route["capability_id"]), capability_args, capture_output=True)
1569
+ execution_plan = attach_source_to_primary_task(execution_plan)
1570
+ executed, blocked = execute_plan_tasks(
1571
+ execution_plan,
1572
+ load_agent=load_agent,
1573
+ run_capability=lambda agent, capability_id, capability_args: run_capability(agent, capability_id, capability_args, capture_output=True),
1574
+ )
1575
+ execution_plan = mark_plan_after_execution(execution_plan, executed, blocked)
1576
+ result = (executed[0].get("result") if executed else blocked[0].get("result") if blocked and isinstance(blocked[0].get("result"), dict) else {}) or {}
1156
1577
  response = result.get("stdout") or result.get("error") or ""
1157
1578
  record_usage(prompt, route=route, source_id=str(source["id"]))
1579
+ model_plan = execution_plan.get("model_plan") if isinstance(execution_plan.get("model_plan"), dict) else build_model_plan(prompt, route=route)
1580
+ review_gate = execution_plan.get("review_gate") if isinstance(execution_plan.get("review_gate"), dict) else build_review_gate(prompt, route=route, model_plan=model_plan)
1581
+ review_payload = {
1582
+ "kind": "agent",
1583
+ "status": execution_plan.get("status") if execution_plan.get("status") != "partial" else result.get("status"),
1584
+ "ok": bool(executed) and not blocked,
1585
+ "response": response,
1586
+ }
1587
+ review_result = {
1588
+ "kind": "execution-review",
1589
+ "agent_id": "execution-reviewer",
1590
+ "capability_id": "review-final-output",
1591
+ "status": "not-run",
1592
+ "ok": False,
1593
+ }
1594
+ if result.get("ok"):
1595
+ review_payload, review_gate, review_result = enforce_execution_review(
1596
+ prompt=prompt,
1597
+ result=review_payload,
1598
+ review_gate=review_gate,
1599
+ execution_plan=execution_plan,
1600
+ producer_backend="deterministic-capability",
1601
+ )
1602
+ if review_result.get("ok"):
1603
+ execution_plan = mark_review_task(execution_plan, reviewer=str(review_result.get("llm_backend") or "execution-reviewer"))
1604
+ elif review_gate.get("status") == "needs-review":
1605
+ execution_plan = mark_review_task_needs_review(execution_plan, review_result)
1158
1606
  return {
1159
1607
  "kind": "agent",
1160
- "status": result.get("status"),
1161
- "ok": result.get("ok", False),
1162
- "mode": "deterministic-route",
1608
+ "status": review_payload.get("status"),
1609
+ "ok": bool(review_payload.get("ok")),
1610
+ "mode": "agentic-route",
1611
+ "legacy_mode": "deterministic-route",
1163
1612
  "prompt_received": True,
1164
1613
  "prompt_length": len(prompt),
1165
1614
  "route": route,
1166
1615
  "source": public_source(source),
1167
1616
  "napkin": napkin_context(ROOT, agent_id=route.get("agent_id"), source_id=str(source["id"])),
1168
- "response": response,
1617
+ "model_plan": model_plan,
1618
+ "review_gate": review_gate,
1619
+ "review_result": review_result,
1620
+ "review_task": execution_plan.get("review_task"),
1621
+ "execution_plan": execution_plan,
1622
+ "orchestration_trace": execution_plan.get("trace", []),
1623
+ "response": review_payload.get("response") or response,
1169
1624
  "result": result,
1170
- "exit_code": result.get("exit_code", 0 if result.get("ok") else 1),
1625
+ "exit_code": review_payload.get("exit_code", result.get("exit_code", 0 if executed and not blocked else 1)),
1171
1626
  }
1172
1627
 
1173
1628
 
@@ -1361,9 +1816,9 @@ def run_capability(
1361
1816
  next_steps=guardrail["next_steps"],
1362
1817
  exit_code=2,
1363
1818
  )
1364
- readiness = evaluate_provider_requirements(ROOT, data)
1819
+ readiness = evaluate_provider_requirements(ROOT, data, capability_args)
1365
1820
  if not readiness["ready"]:
1366
- return run_payload(
1821
+ payload = run_payload(
1367
1822
  status=readiness["status"],
1368
1823
  agent=summarize_agent(agent),
1369
1824
  capability=data.get("id", capability_id),
@@ -1377,6 +1832,18 @@ def run_capability(
1377
1832
  artifacts=readiness["artifacts"],
1378
1833
  exit_code=readiness.get("exit_code"),
1379
1834
  )
1835
+ wizard = setup_wizard_from_readiness(readiness, agent=summarize_agent(agent), capability_id=str(data.get("id", capability_id)))
1836
+ if wizard:
1837
+ payload["setup_wizard"] = wizard
1838
+ payload["next_question"] = wizard.get("next_question")
1839
+ payload["configuration_agent"] = wizard.get("owner_agent")
1840
+ payload["next_steps"] = [
1841
+ "Responda a pergunta do wizard para autorizar ou negar a configuracao deste provider.",
1842
+ "Informe uma referencia segura de credencial por variavel de ambiente, arquivo ou cadeia nativa quando solicitado.",
1843
+ "Reexecute ou retome a mesma capability depois que a configuracao estiver salva.",
1844
+ ]
1845
+ payload = persist_setup_wizard_payload(payload)
1846
+ return payload
1380
1847
 
1381
1848
  runner_ref = (data.get("entrypoint", {}) or {}).get("runner")
1382
1849
  if not runner_ref:
@@ -1495,6 +1962,26 @@ def supports_runtime_source(agent_id: str, capability_id: str) -> bool:
1495
1962
  return agent_id == "azure-devops-orchestrator" and capability_id == "read-card"
1496
1963
 
1497
1964
 
1965
+ def setup_wizard_from_readiness(readiness: dict[str, Any], *, agent: dict[str, Any], capability_id: str) -> dict[str, Any] | None:
1966
+ providers = readiness.get("providers") if isinstance(readiness.get("providers"), dict) else {}
1967
+ missing = providers.get("missing") or []
1968
+ if not missing:
1969
+ return None
1970
+ provider_id = str(missing[0])
1971
+ details = providers.get("details") or []
1972
+ detail = next((item for item in details if isinstance(item, dict) and item.get("id") == provider_id), {})
1973
+ try:
1974
+ return provider_wizard_from_requirement(
1975
+ ROOT,
1976
+ provider_id,
1977
+ agent_id=str(agent.get("id") or ""),
1978
+ capability_id=capability_id,
1979
+ reason=str(detail.get("purpose") or "Provider is required but not configured."),
1980
+ )
1981
+ except Exception:
1982
+ return None
1983
+
1984
+
1498
1985
  def doctor(project: str | None = None, home: str | None = None, scope: str = "auto") -> dict[str, Any]:
1499
1986
  agents = list_agents()
1500
1987
  capabilities = list_all_capabilities()
@@ -1704,6 +2191,8 @@ def print_human(result: dict[str, Any]) -> None:
1704
2191
  print_source_configure(result)
1705
2192
  elif kind == "source-remove":
1706
2193
  print_source_remove(result)
2194
+ elif kind in {"wizards", "wizard"}:
2195
+ print_wizard(result)
1707
2196
  elif kind == "memory":
1708
2197
  print_memory(result)
1709
2198
  elif kind == "memory-path":
@@ -1748,6 +2237,12 @@ def print_human(result: dict[str, Any]) -> None:
1748
2237
  print_permissions(result)
1749
2238
  elif kind in {"audit", "audit-entry", "audit-export"}:
1750
2239
  print_audit(result)
2240
+ elif kind == "config":
2241
+ print_config(result)
2242
+ elif kind in {"tools", "tool", "integrations", "integration", "skills", "skill", "decisions", "decision", "decisions-reset"}:
2243
+ print_control(result)
2244
+ elif kind in {"ollama-status", "ollama-models", "ollama-pull", "ollama-update"}:
2245
+ print_ollama(result)
1751
2246
  elif kind == "install":
1752
2247
  print_install(result)
1753
2248
  else:
@@ -1875,6 +2370,15 @@ def print_agent_response(result: dict[str, Any]) -> None:
1875
2370
  print(result.get("response", ""))
1876
2371
  return
1877
2372
  print(result.get("message") or result.get("response") or "Agent execution did not complete.")
2373
+ question = result.get("next_question") or ((result.get("setup_wizard") or {}).get("next_question") if isinstance(result.get("setup_wizard"), dict) else None)
2374
+ if isinstance(question, dict) and question.get("text"):
2375
+ print(f"\nPergunta: {question['text']}")
2376
+ if question.get("type") == "confirm":
2377
+ print("[s/N]")
2378
+ wizard = result.get("setup_wizard") if isinstance(result.get("setup_wizard"), dict) else {}
2379
+ if wizard.get("wizard_id"):
2380
+ print(f"\nWizard: {wizard['wizard_id']}")
2381
+ print(f"Responder: agent wizard answer {wizard['wizard_id']} <resposta>")
1878
2382
  if result.get("llm_backend"):
1879
2383
  print(f"Requested backend: {result['llm_backend']}")
1880
2384
  if result.get("next_steps"):
@@ -1883,6 +2387,33 @@ def print_agent_response(result: dict[str, Any]) -> None:
1883
2387
  print(f"- {step}")
1884
2388
 
1885
2389
 
2390
+ def print_wizard(result: dict[str, Any]) -> None:
2391
+ if result.get("kind") == "wizards":
2392
+ items = result.get("items") or []
2393
+ print(f"Wizards: {len(items)}")
2394
+ for item in items:
2395
+ print(f"- {item.get('wizard_id')} {item.get('status')} {item.get('provider')}")
2396
+ return
2397
+ wizard = result.get("wizard") if isinstance(result.get("wizard"), dict) else result.get("setup_wizard")
2398
+ if not isinstance(wizard, dict):
2399
+ print(json.dumps(result, ensure_ascii=False, indent=2))
2400
+ return
2401
+ print(f"Wizard {wizard.get('wizard_id')}: {wizard.get('status')}")
2402
+ print(f"Provider: {wizard.get('provider')}")
2403
+ question = result.get("next_question") or wizard.get("next_question")
2404
+ if isinstance(question, dict) and question.get("text"):
2405
+ print(f"Pergunta: {question['text']}")
2406
+ if question.get("type") == "confirm":
2407
+ print("[s/N]")
2408
+ print(f"Responder: agent wizard answer {wizard.get('wizard_id')} <resposta>")
2409
+ if result.get("source_result"):
2410
+ source = result["source_result"].get("source") or {}
2411
+ print(f"Source configurada: {source.get('id')}")
2412
+ if result.get("resumed_prompt"):
2413
+ resume = result.get("resume_result") or {}
2414
+ print(f"Prompt retomado: {resume.get('status')}")
2415
+
2416
+
1886
2417
  def print_sources(result: dict[str, Any]) -> None:
1887
2418
  print(f"Sources config: {result['config_path']}")
1888
2419
  if not result["items"]:
@@ -2268,6 +2799,58 @@ def print_credential_backends(result: dict[str, Any]) -> None:
2268
2799
  print(f"- {item}")
2269
2800
 
2270
2801
 
2802
+ def print_config(result: dict[str, Any]) -> None:
2803
+ print(f"Config: {result.get('path')}")
2804
+ if result.get("llm"):
2805
+ print(f"Primary LLM: {(result['llm'] or {}).get('primary') or '-'}")
2806
+ if result.get("ollama"):
2807
+ print(f"Ollama: {(result['ollama'] or {}).get('status')}")
2808
+
2809
+
2810
+ def print_control(result: dict[str, Any]) -> None:
2811
+ kind = result.get("kind")
2812
+ if kind == "decisions-reset":
2813
+ print(f"Decisions reset: {result.get('category') or 'all'}")
2814
+ print(f"Path: {result.get('path')}")
2815
+ return
2816
+ if "items" in result:
2817
+ print(f"{kind}:")
2818
+ for item in result.get("items") or []:
2819
+ print(f"- {item.get('category')}:{item.get('id')} {item.get('state')}")
2820
+ if not result.get("items"):
2821
+ print("- none")
2822
+ return
2823
+ item = result.get("item") or result
2824
+ print(f"{item.get('category') or result.get('category')}:{item.get('id') or result.get('id')} {item.get('state') or result.get('state')}")
2825
+
2826
+
2827
+ def print_ollama(result: dict[str, Any]) -> None:
2828
+ kind = result.get("kind")
2829
+ if kind == "ollama-status":
2830
+ print(f"Ollama: {result.get('status')}")
2831
+ print(f"Binary: {result.get('binary') or '-'}")
2832
+ print(f"Version: {result.get('version') or '-'}")
2833
+ daemon = result.get("daemon") or {}
2834
+ print(f"Daemon: {daemon.get('status') or '-'}")
2835
+ print(f"Models: {result.get('model_count', 0)}")
2836
+ if result.get("install_plan"):
2837
+ print(f"Install: {(result['install_plan'] or {}).get('command')}")
2838
+ return
2839
+ if kind == "ollama-models":
2840
+ print(f"Ollama models: {result.get('status')}")
2841
+ for item in result.get("items") or []:
2842
+ print(f"- {item.get('name')} {item.get('size') or '-'}")
2843
+ if not result.get("items"):
2844
+ print("- none")
2845
+ return
2846
+ print(f"{kind}: {result.get('status')}")
2847
+ if result.get("command"):
2848
+ command = result["command"]
2849
+ print("Command: " + (" ".join(command) if isinstance(command, list) else str(command)))
2850
+ if result.get("message"):
2851
+ print(result["message"])
2852
+
2853
+
2271
2854
  def print_install(result: dict[str, Any]) -> None:
2272
2855
  print(f"AI DevKit install: {result['status']}")
2273
2856
  print(f"Scope: {result['scope']}")