agent-devkit 0.2.0 → 0.3.1

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 (177) hide show
  1. package/README.md +66 -13
  2. package/bin/agent.mjs +133 -7
  3. package/package.json +1 -1
  4. package/runtime/README.md +205 -13
  5. package/runtime/agent +31 -5
  6. package/runtime/agents/README.md +18 -0
  7. package/runtime/agents/contribution-reviewer/AGENTS.md +8 -0
  8. package/runtime/agents/contribution-reviewer/README.md +8 -0
  9. package/runtime/agents/contribution-reviewer/agent.yaml +40 -0
  10. package/runtime/agents/contribution-reviewer/capabilities/plan-contribution-pr/capability.yaml +27 -0
  11. package/runtime/agents/contribution-reviewer/capabilities/plan-contribution-pr/decision-rules.md +5 -0
  12. package/runtime/agents/contribution-reviewer/capabilities/plan-contribution-pr/workflow.md +6 -0
  13. package/runtime/agents/contribution-reviewer/capabilities/review-contribution/capability.yaml +25 -0
  14. package/runtime/agents/contribution-reviewer/capabilities/review-contribution/decision-rules.md +5 -0
  15. package/runtime/agents/contribution-reviewer/capabilities/review-contribution/workflow.md +5 -0
  16. package/runtime/agents/contribution-reviewer/capabilities/validate-local-contribution/capability.yaml +26 -0
  17. package/runtime/agents/contribution-reviewer/capabilities/validate-local-contribution/decision-rules.md +5 -0
  18. package/runtime/agents/contribution-reviewer/capabilities/validate-local-contribution/workflow.md +6 -0
  19. package/runtime/agents/contribution-reviewer/infra/README.md +6 -0
  20. package/runtime/agents/contribution-reviewer/knowledge/context.md +8 -0
  21. package/runtime/agents/contribution-reviewer/knowledge/system.md +8 -0
  22. package/runtime/agents/contribution-reviewer/templates/README.md +3 -0
  23. package/runtime/agents/knowledge-author/AGENTS.md +7 -0
  24. package/runtime/agents/knowledge-author/README.md +7 -0
  25. package/runtime/agents/knowledge-author/agent.yaml +37 -0
  26. package/runtime/agents/knowledge-author/capabilities/create-knowledge-snapshot/capability.yaml +30 -0
  27. package/runtime/agents/knowledge-author/capabilities/create-knowledge-snapshot/decision-rules.md +6 -0
  28. package/runtime/agents/knowledge-author/capabilities/create-knowledge-snapshot/workflow.md +7 -0
  29. package/runtime/agents/knowledge-author/infra/.gitkeep +1 -0
  30. package/runtime/agents/knowledge-author/knowledge/context.md +4 -0
  31. package/runtime/agents/knowledge-author/knowledge/system.md +4 -0
  32. package/runtime/agents/knowledge-author/templates/.gitkeep +1 -0
  33. package/runtime/agents/knowledge-curator/AGENTS.md +7 -0
  34. package/runtime/agents/knowledge-curator/README.md +6 -0
  35. package/runtime/agents/knowledge-curator/agent.yaml +37 -0
  36. package/runtime/agents/knowledge-curator/capabilities/curate-knowledge-base/capability.yaml +29 -0
  37. package/runtime/agents/knowledge-curator/capabilities/curate-knowledge-base/decision-rules.md +6 -0
  38. package/runtime/agents/knowledge-curator/capabilities/curate-knowledge-base/workflow.md +7 -0
  39. package/runtime/agents/knowledge-curator/infra/.gitkeep +1 -0
  40. package/runtime/agents/knowledge-curator/knowledge/context.md +4 -0
  41. package/runtime/agents/knowledge-curator/knowledge/system.md +4 -0
  42. package/runtime/agents/knowledge-curator/templates/.gitkeep +1 -0
  43. package/runtime/agents/knowledge-infra-builder/AGENTS.md +8 -0
  44. package/runtime/agents/knowledge-infra-builder/README.md +8 -0
  45. package/runtime/agents/knowledge-infra-builder/agent.yaml +38 -0
  46. package/runtime/agents/knowledge-infra-builder/capabilities/create-knowledge-base/capability.yaml +30 -0
  47. package/runtime/agents/knowledge-infra-builder/capabilities/create-knowledge-base/decision-rules.md +6 -0
  48. package/runtime/agents/knowledge-infra-builder/capabilities/create-knowledge-base/workflow.md +7 -0
  49. package/runtime/agents/knowledge-infra-builder/infra/.gitkeep +1 -0
  50. package/runtime/agents/knowledge-infra-builder/knowledge/context.md +4 -0
  51. package/runtime/agents/knowledge-infra-builder/knowledge/system.md +4 -0
  52. package/runtime/agents/knowledge-infra-builder/templates/.gitkeep +1 -0
  53. package/runtime/agents/knowledge-owner/AGENTS.md +7 -0
  54. package/runtime/agents/knowledge-owner/README.md +6 -0
  55. package/runtime/agents/knowledge-owner/agent.yaml +37 -0
  56. package/runtime/agents/knowledge-owner/capabilities/publish-knowledge-snapshot/capability.yaml +28 -0
  57. package/runtime/agents/knowledge-owner/capabilities/publish-knowledge-snapshot/decision-rules.md +6 -0
  58. package/runtime/agents/knowledge-owner/capabilities/publish-knowledge-snapshot/workflow.md +7 -0
  59. package/runtime/agents/knowledge-owner/infra/.gitkeep +1 -0
  60. package/runtime/agents/knowledge-owner/knowledge/context.md +4 -0
  61. package/runtime/agents/knowledge-owner/knowledge/system.md +4 -0
  62. package/runtime/agents/knowledge-owner/templates/.gitkeep +1 -0
  63. package/runtime/agents/knowledge-reviewer/AGENTS.md +7 -0
  64. package/runtime/agents/knowledge-reviewer/README.md +7 -0
  65. package/runtime/agents/knowledge-reviewer/agent.yaml +36 -0
  66. package/runtime/agents/knowledge-reviewer/capabilities/review-knowledge-snapshot/capability.yaml +26 -0
  67. package/runtime/agents/knowledge-reviewer/capabilities/review-knowledge-snapshot/decision-rules.md +6 -0
  68. package/runtime/agents/knowledge-reviewer/capabilities/review-knowledge-snapshot/workflow.md +7 -0
  69. package/runtime/agents/knowledge-reviewer/infra/.gitkeep +1 -0
  70. package/runtime/agents/knowledge-reviewer/knowledge/context.md +4 -0
  71. package/runtime/agents/knowledge-reviewer/knowledge/system.md +4 -0
  72. package/runtime/agents/knowledge-reviewer/templates/.gitkeep +1 -0
  73. package/runtime/agents/local-memory-manager/AGENTS.md +5 -0
  74. package/runtime/agents/local-memory-manager/README.md +7 -0
  75. package/runtime/agents/local-memory-manager/agent.yaml +38 -0
  76. package/runtime/agents/local-memory-manager/capabilities/curate-local-memory/capability.yaml +19 -0
  77. package/runtime/agents/local-memory-manager/capabilities/curate-local-memory/decision-rules.md +5 -0
  78. package/runtime/agents/local-memory-manager/capabilities/curate-local-memory/workflow.md +6 -0
  79. package/runtime/agents/local-memory-manager/capabilities/inspect-local-memory/capability.yaml +19 -0
  80. package/runtime/agents/local-memory-manager/capabilities/inspect-local-memory/decision-rules.md +5 -0
  81. package/runtime/agents/local-memory-manager/capabilities/inspect-local-memory/workflow.md +5 -0
  82. package/runtime/agents/local-memory-manager/infra/.gitkeep +1 -0
  83. package/runtime/agents/local-memory-manager/knowledge/context.md +4 -0
  84. package/runtime/agents/local-memory-manager/knowledge/system.md +4 -0
  85. package/runtime/agents/local-memory-manager/templates/.gitkeep +1 -0
  86. package/runtime/agents/memory-sync-manager/AGENTS.md +7 -0
  87. package/runtime/agents/memory-sync-manager/README.md +7 -0
  88. package/runtime/agents/memory-sync-manager/agent.yaml +37 -0
  89. package/runtime/agents/memory-sync-manager/capabilities/plan-memory-backup/capability.yaml +29 -0
  90. package/runtime/agents/memory-sync-manager/capabilities/plan-memory-backup/decision-rules.md +6 -0
  91. package/runtime/agents/memory-sync-manager/capabilities/plan-memory-backup/workflow.md +7 -0
  92. package/runtime/agents/memory-sync-manager/infra/.gitkeep +1 -0
  93. package/runtime/agents/memory-sync-manager/knowledge/context.md +4 -0
  94. package/runtime/agents/memory-sync-manager/knowledge/system.md +4 -0
  95. package/runtime/agents/memory-sync-manager/templates/.gitkeep +1 -0
  96. package/runtime/agents/shared-memory-curator/AGENTS.md +5 -0
  97. package/runtime/agents/shared-memory-curator/README.md +6 -0
  98. package/runtime/agents/shared-memory-curator/agent.yaml +38 -0
  99. package/runtime/agents/shared-memory-curator/capabilities/create-shared-memory/capability.yaml +19 -0
  100. package/runtime/agents/shared-memory-curator/capabilities/create-shared-memory/decision-rules.md +5 -0
  101. package/runtime/agents/shared-memory-curator/capabilities/create-shared-memory/workflow.md +5 -0
  102. package/runtime/agents/shared-memory-curator/capabilities/publish-shared-submission/capability.yaml +19 -0
  103. package/runtime/agents/shared-memory-curator/capabilities/publish-shared-submission/decision-rules.md +5 -0
  104. package/runtime/agents/shared-memory-curator/capabilities/publish-shared-submission/workflow.md +5 -0
  105. package/runtime/agents/shared-memory-curator/capabilities/review-shared-submission/capability.yaml +19 -0
  106. package/runtime/agents/shared-memory-curator/capabilities/review-shared-submission/decision-rules.md +5 -0
  107. package/runtime/agents/shared-memory-curator/capabilities/review-shared-submission/workflow.md +5 -0
  108. package/runtime/agents/shared-memory-curator/infra/.gitkeep +1 -0
  109. package/runtime/agents/shared-memory-curator/knowledge/context.md +5 -0
  110. package/runtime/agents/shared-memory-curator/knowledge/system.md +4 -0
  111. package/runtime/agents/shared-memory-curator/templates/.gitkeep +1 -0
  112. package/runtime/cli/README.md +47 -8
  113. package/runtime/cli/aikit/__init__.py +1 -1
  114. package/runtime/cli/aikit/agent_registry.py +4 -2
  115. package/runtime/cli/aikit/agentic_commands.py +158 -0
  116. package/runtime/cli/aikit/app_home.py +2 -0
  117. package/runtime/cli/aikit/audit.py +16 -6
  118. package/runtime/cli/aikit/catalog.py +278 -8
  119. package/runtime/cli/aikit/cli_dispatch.py +489 -13
  120. package/runtime/cli/aikit/cli_parser.py +146 -8
  121. package/runtime/cli/aikit/contribution.py +132 -2
  122. package/runtime/cli/aikit/doctor_runtime.py +85 -0
  123. package/runtime/cli/aikit/embedded_mini_brain.py +351 -0
  124. package/runtime/cli/aikit/eval.py +356 -10
  125. package/runtime/cli/aikit/human_output.py +310 -4
  126. package/runtime/cli/aikit/interactive_wizard.py +146 -0
  127. package/runtime/cli/aikit/knowledge_base.py +1067 -0
  128. package/runtime/cli/aikit/llm.py +40 -6
  129. package/runtime/cli/aikit/local_artifacts.py +444 -0
  130. package/runtime/cli/aikit/local_llm.py +176 -0
  131. package/runtime/cli/aikit/local_llm_operator.py +15 -5
  132. package/runtime/cli/aikit/main.py +15 -0
  133. package/runtime/cli/aikit/mcp_manifest.py +798 -0
  134. package/runtime/cli/aikit/mcp_tools.py +643 -5
  135. package/runtime/cli/aikit/memory.py +405 -0
  136. package/runtime/cli/aikit/mini_brain.py +56 -25
  137. package/runtime/cli/aikit/model_router.py +42 -9
  138. package/runtime/cli/aikit/natural_prompt_runtime.py +194 -2
  139. package/runtime/cli/aikit/ollama.py +64 -15
  140. package/runtime/cli/aikit/onboarding.py +551 -0
  141. package/runtime/cli/aikit/output.py +67 -0
  142. package/runtime/cli/aikit/prompt_injection.py +12 -1
  143. package/runtime/cli/aikit/review_gate.py +14 -2
  144. package/runtime/cli/aikit/roadmap_cli.py +1 -1
  145. package/runtime/cli/aikit/secrets.py +3 -2
  146. package/runtime/cli/aikit/setup_wizard_payload.py +3 -0
  147. package/runtime/cli/aikit/shared_memory.py +415 -0
  148. package/runtime/cli/aikit/specialist_readiness.py +152 -0
  149. package/runtime/cli/aikit/tasks.py +104 -1
  150. package/runtime/cli/aikit/team.py +380 -0
  151. package/runtime/cli/aikit/toolchain.py +7 -2
  152. package/runtime/cli/aikit/workflows.py +115 -14
  153. package/runtime/models/qwen2.5-0.5b-instruct/manifest.json +30 -0
  154. package/runtime/providers/knowledge-github.yaml +40 -0
  155. package/runtime/providers/knowledge-google-drive.yaml +32 -0
  156. package/runtime/providers/knowledge-local.yaml +26 -0
  157. package/runtime/providers/knowledge-notion.yaml +32 -0
  158. package/runtime/providers/knowledge-obsidian.yaml +24 -0
  159. package/runtime/providers/knowledge-onedrive.yaml +36 -0
  160. package/runtime/providers/knowledge-s3.yaml +45 -0
  161. package/runtime/providers/knowledge-sharepoint.yaml +39 -0
  162. package/runtime/providers/knowledge-supabase.yaml +43 -0
  163. package/runtime/providers/knowledge-vector.yaml +39 -0
  164. package/runtime/requirements.txt +6 -0
  165. package/runtime/scripts/docker-cli-qa.sh +453 -0
  166. package/runtime/scripts/release-catalog-snapshot.json +55 -4
  167. package/runtime/scripts/release-gate.py +54 -13
  168. package/runtime/tooling/toolchain.yaml +92 -0
  169. package/runtime/vendor/skills/napkin/napkin.md +21 -7
  170. package/runtime/workflows/azure-card-analysis/README.md +3 -0
  171. package/runtime/workflows/azure-card-analysis/workflow.yaml +30 -0
  172. package/runtime/workflows/daily-pr-review/README.md +3 -0
  173. package/runtime/workflows/daily-pr-review/workflow.yaml +31 -0
  174. package/runtime/workflows/incident-analysis/README.md +3 -0
  175. package/runtime/workflows/incident-analysis/workflow.yaml +33 -0
  176. package/runtime/workflows/release-prep/README.md +3 -0
  177. package/runtime/workflows/release-prep/workflow.yaml +30 -0
@@ -5,6 +5,7 @@ from __future__ import annotations
5
5
  import re
6
6
  from typing import Any
7
7
 
8
+ from cli.aikit.embedded_mini_brain import EMBEDDED_BACKEND_ID
8
9
  from cli.aikit.llm import BACKENDS, doctor_backend, llm_preference, load_config
9
10
  from cli.aikit.mini_brain import mini_brain_contract
10
11
  from cli.aikit.ollama import ollama_status
@@ -14,6 +15,9 @@ from cli.aikit.write_policy import normalize_write_policy, write_policy_public_f
14
15
  OPERATIONAL_PATTERN = re.compile(
15
16
  r"(?i)\b(resum\w*|sumari\w*|classifi\w*|extra(?:i|ir|ia|cao|ção)\w*|normaliz\w*|compar\w*|logs?|rascunho|agrupe|agrupar)\b"
16
17
  )
18
+ SIMPLE_CHAT_SETUP_PATTERN = re.compile(
19
+ r"(?i)\b(ol[aá]|oi|bom dia|boa tarde|boa noite|ajuda|help|comec(?:ar|o)|começ(?:ar|o)|setup|onboard|configur|instal|usar)\b"
20
+ )
17
21
  HIGH_LEVEL_PATTERN = re.compile(
18
22
  r"(?i)\b(arquitet|decid|aprovar|reprovar|especifica|requisit|implemente|codigo|c[oó]digo|documento|automac|deploy|seguran)\b"
19
23
  )
@@ -48,7 +52,9 @@ def build_model_plan(
48
52
  mini_brain = mini_brain_contract(config=config, ollama_payload=ollama, ollama_backend=ollama_backend)
49
53
  local_available = mini_brain.get("available") is True
50
54
  operational = bool(OPERATIONAL_PATTERN.search(prompt))
55
+ simple_chat_setup = bool(SIMPLE_CHAT_SETUP_PATTERN.search(prompt))
51
56
  high_level = bool(HIGH_LEVEL_PATTERN.search(prompt))
57
+ local_provider = select_local_provider(ollama_payload=ollama, ollama_backend=ollama_backend, mini_brain=mini_brain)
52
58
  policy = choose_model_strategy(
53
59
  prompt,
54
60
  route=route,
@@ -56,10 +62,12 @@ def build_model_plan(
56
62
  specialist_tasks=specialist_tasks or [],
57
63
  configuration_tasks=configuration_tasks or [],
58
64
  operational=operational,
65
+ simple_chat_setup=simple_chat_setup,
59
66
  high_level=high_level,
60
67
  local_available=local_available,
61
68
  )
62
69
  use_local = policy["strategy"] == "mini-brain" and local_available
70
+ delegate_local = use_local and operational
63
71
  return {
64
72
  "kind": "model-plan",
65
73
  "status": "planned",
@@ -73,22 +81,30 @@ def build_model_plan(
73
81
  "max_llm_calls": policy["max_llm_calls"],
74
82
  "intent": route.get("intent") if route else "llm",
75
83
  "primary_coordinators": coordinator_order(preference),
76
- "local_llm_role": "operational-worker",
84
+ "local_llm_role": "operational-worker" if operational else "bootstrap-coordinator",
77
85
  "local_llm_available": local_available,
78
- "local_llm_provider": mini_brain.get("provider") or "ollama",
79
- "local_llm_backend_configured": ollama_backend.get("status") == "ok",
86
+ "local_llm_provider": local_provider,
87
+ "local_llm_backend_configured": ollama_backend.get("configured") is True if local_provider == "ollama" else True,
80
88
  "local_llm_runtime": {
81
- "binary_status": ollama.get("status"),
89
+ "provider": local_provider,
90
+ "binary_status": ollama.get("status") if local_provider == "ollama" else "embedded",
82
91
  "backend_status": ollama_backend.get("status"),
83
- "model": mini_brain.get("ollama_model") or ollama_backend.get("model"),
84
- "base_url": ollama_backend.get("base_url"),
92
+ "model": (mini_brain.get("ollama_model") or ollama_backend.get("model")) if local_provider == "ollama" else mini_brain.get("hf_model"),
93
+ "base_url": ollama_backend.get("base_url") if local_provider == "ollama" else None,
94
+ },
95
+ "optional_local_providers": {
96
+ "ollama": {
97
+ "status": ollama.get("status"),
98
+ "backend_status": ollama_backend.get("status"),
99
+ "model_count": ollama.get("model_count"),
100
+ }
85
101
  },
86
102
  "mini_brain": mini_brain,
87
103
  "local_llm_recommended": operational,
88
104
  "local_llm_selected": use_local,
89
105
  "delegation": {
90
- "allowed": policy["strategy"] == "mini-brain",
91
- "selected": use_local,
106
+ "allowed": policy["strategy"] == "mini-brain" and operational,
107
+ "selected": delegate_local,
92
108
  "reason": local_reason(
93
109
  operational=operational,
94
110
  local_available=local_available,
@@ -110,6 +126,7 @@ def choose_model_strategy(
110
126
  specialist_tasks: list[dict[str, Any]],
111
127
  configuration_tasks: list[dict[str, Any]],
112
128
  operational: bool,
129
+ simple_chat_setup: bool,
113
130
  high_level: bool,
114
131
  local_available: bool,
115
132
  ) -> dict[str, Any]:
@@ -154,7 +171,7 @@ def choose_model_strategy(
154
171
  max_llm_calls=0,
155
172
  matrix="Conhecida + estruturada + baixo risco -> automacao",
156
173
  )
157
- if operational and not high_level:
174
+ if (operational or simple_chat_setup) and not high_level:
158
175
  return policy(
159
176
  "mini-brain" if local_available else "external-llm",
160
177
  "The prompt is operational and low-risk; local mini-brain is preferred when available.",
@@ -241,3 +258,19 @@ def local_reason(*, operational: bool, local_available: bool, high_level: bool,
241
258
  if operational and not local_available:
242
259
  return "Task is operational, but the local mini-brain is not enabled or available; coordinator/API fallback should execute."
243
260
  return "Task requires coordinator-level reasoning or review."
261
+
262
+
263
+ def select_local_provider(
264
+ *,
265
+ ollama_payload: dict[str, Any],
266
+ ollama_backend: dict[str, Any],
267
+ mini_brain: dict[str, Any],
268
+ ) -> str:
269
+ ollama_ready = (
270
+ ollama_payload.get("status") == "ok"
271
+ and ollama_backend.get("status") == "ok"
272
+ and (ollama_backend.get("configured") is True or int(ollama_payload.get("model_count") or 0) > 0)
273
+ )
274
+ if ollama_ready:
275
+ return "ollama"
276
+ return str(mini_brain.get("provider") or EMBEDDED_BACKEND_ID)
@@ -3,9 +3,11 @@
3
3
  from __future__ import annotations
4
4
 
5
5
  import argparse
6
+ import re
6
7
  from pathlib import Path
7
8
  from typing import Any
8
9
 
10
+ from cli.aikit.agent_registry import load_agent_registry
9
11
  from cli.aikit.agent_executor import execute_plan_tasks
10
12
  from cli.aikit.capability_runtime import load_agent, run_capability
11
13
  from cli.aikit.calendar import calendar_summary, calendar_today, calendar_tomorrow
@@ -22,7 +24,7 @@ from cli.aikit.memory import napkin_context, record_usage
22
24
  from cli.aikit.model_router import build_model_plan
23
25
  from cli.aikit.module_controller import run_module_controller
24
26
  from cli.aikit.orchestrator import attach_source_to_primary_task, build_execution_plan, mark_plan_after_execution, mark_review_task
25
- from cli.aikit.personality import load_personality
27
+ from cli.aikit.personality import load_personality, update_personality
26
28
  from cli.aikit.provider_wizard import missing_source_wizard
27
29
  from cli.aikit.review_gate import build_review_gate
28
30
  from cli.aikit.router import route_prompt
@@ -36,6 +38,142 @@ def effective_dry_run(args: argparse.Namespace) -> bool:
36
38
  return bool(getattr(args, "dry_run", False) or getattr(args, "global_dry_run", False))
37
39
 
38
40
 
41
+ def parse_rename_prompt(prompt: str) -> str | None:
42
+ text = " ".join(prompt.strip().split())
43
+ patterns = [
44
+ r"(?i)\b(?:mude|troque|altere|renomeie)\s+(?:o\s+)?seu\s+nome\s+para\s+(.+)$",
45
+ r"(?i)\b(?:seu\s+nome\s+agora\s+e|seu\s+nome\s+agora\s+é)\s+(.+)$",
46
+ r"(?i)\b(?:teu\s+nome\s+agora\s+e|teu\s+nome\s+agora\s+é)\s+(.+)$",
47
+ ]
48
+ for pattern in patterns:
49
+ match = re.search(pattern, text)
50
+ if not match:
51
+ continue
52
+ value = clean_requested_name(match.group(1))
53
+ if value:
54
+ return value
55
+ return None
56
+
57
+
58
+ def clean_requested_name(value: str) -> str | None:
59
+ cleaned = " ".join(value.strip().strip("\"'`.,;:!").split())
60
+ if not cleaned:
61
+ return None
62
+ return cleaned[:80]
63
+
64
+
65
+ def is_capabilities_help_prompt(prompt: str) -> bool:
66
+ normalized = normalize_text(prompt)
67
+ if normalized in {"ajuda", "help", "o que voce faz", "o que voce consegue fazer"}:
68
+ return True
69
+ capability_markers = (
70
+ "o que voce consegue fazer",
71
+ "o que voce pode fazer",
72
+ "como voce pode ajudar",
73
+ "como usar o agent",
74
+ "como usar voce",
75
+ "quais suas capacidades",
76
+ "quais sao suas capacidades",
77
+ "quais agentes voce tem",
78
+ "que agentes voce tem",
79
+ )
80
+ return any(marker in normalized for marker in capability_markers)
81
+
82
+
83
+ def normalize_text(value: str) -> str:
84
+ replacements = str.maketrans(
85
+ {
86
+ "á": "a",
87
+ "à": "a",
88
+ "ã": "a",
89
+ "â": "a",
90
+ "é": "e",
91
+ "ê": "e",
92
+ "í": "i",
93
+ "ó": "o",
94
+ "õ": "o",
95
+ "ô": "o",
96
+ "ú": "u",
97
+ "ç": "c",
98
+ }
99
+ )
100
+ text = value.lower().translate(replacements)
101
+ return " ".join(re.sub(r"[^a-z0-9._:-]+", " ", text).split())
102
+
103
+
104
+ def local_capabilities_help_response(prompt: str, *, name: str) -> dict[str, Any]:
105
+ registry = load_agent_registry(ROOT)
106
+ agents = registry.get("agents") if isinstance(registry.get("agents"), dict) else {}
107
+ capabilities = registry.get("capabilities") if isinstance(registry.get("capabilities"), dict) else {}
108
+ examples = [
109
+ 'agent "analise o card 7914 do projeto sustentacao no azure"',
110
+ 'agent "mude seu nome para Ianota"',
111
+ "agent onboard minimal",
112
+ "agent catalog search pr",
113
+ "agent mcp tools",
114
+ "agent memory show",
115
+ ]
116
+ response = (
117
+ f"Eu sou {name}. Posso operar como harness/agente local do Agent DevKit: "
118
+ "validar onboarding, lembrar preferencias locais, rotear pedidos para agentes especialistas, "
119
+ "criar wizards de configuracao quando faltar provider/source, executar capabilities deterministicas, "
120
+ "expor ferramentas via MCP, preparar automacoes locais e usar LLMs configuradas quando a tarefa exigir raciocinio aberto. "
121
+ f"Neste runtime encontrei {len(agents)} agentes e {len(capabilities)} capabilities."
122
+ )
123
+ return {
124
+ "kind": "agent",
125
+ "status": "ok",
126
+ "ok": True,
127
+ "requires_llm": False,
128
+ "prompt_received": True,
129
+ "prompt_length": len(prompt),
130
+ "mode": "local-capabilities-help",
131
+ "identity": {"name": name, "source": "local"},
132
+ "response": response,
133
+ "catalog": {
134
+ "agents": len(agents),
135
+ "capabilities": len(capabilities),
136
+ },
137
+ "next_steps": examples,
138
+ }
139
+
140
+
141
+ def embedded_mini_brain_install_response(prompt: str, *, name: str, model_plan: dict[str, Any]) -> dict[str, Any]:
142
+ embedded = (
143
+ ((model_plan.get("mini_brain") or {}).get("embedded") or {})
144
+ if isinstance(model_plan.get("mini_brain"), dict)
145
+ else {}
146
+ )
147
+ status = embedded.get("status") or "not-installed"
148
+ response = (
149
+ f"Eu sou {name}. Consigo orientar o setup inicial localmente, mas o mini-cerebro local ainda nao esta instalado "
150
+ f"(status: {status}). Para habilitar conversa local sem Claude, Codex, Ollama ou API externa, execute "
151
+ "`agent setup mini-brain --yes`. Sem esse download, posso continuar com onboarding, memoria, wizards e "
152
+ "capabilities deterministicas."
153
+ )
154
+ return {
155
+ "kind": "agent",
156
+ "status": "needs-setup",
157
+ "ok": False,
158
+ "requires_llm": False,
159
+ "prompt_received": True,
160
+ "prompt_length": len(prompt),
161
+ "mode": "embedded-mini-brain-not-installed",
162
+ "identity": {"name": name, "source": "local"},
163
+ "llm_backend": "embedded-mini-brain",
164
+ "mini_brain": model_plan.get("mini_brain"),
165
+ "response": response,
166
+ "message": "Embedded mini-brain is not installed yet.",
167
+ "next_steps": [
168
+ "agent setup mini-brain --dry-run",
169
+ "agent setup mini-brain --yes",
170
+ "agent llm configure claude-code --set-default",
171
+ "agent llm configure codex-cli --set-default",
172
+ ],
173
+ "exit_code": 2,
174
+ }
175
+
176
+
39
177
  def agent_requires_llm(args: argparse.Namespace) -> dict[str, Any]:
40
178
  prompt = " ".join(args.prompt).strip()
41
179
  return run_agent_prompt_request(
@@ -70,6 +208,21 @@ def run_agent_prompt_request(request: AgentPromptRequest) -> dict[str, Any]:
70
208
  raise DevKitError(str(exc)) from exc
71
209
  personality = load_personality()
72
210
  name = public_name(personality=personality, invoked_as=request.prog_name)
211
+ rename = parse_rename_prompt(prompt)
212
+ if rename:
213
+ updated = update_personality(agent_name=rename)
214
+ result = {
215
+ "kind": "agent",
216
+ "status": "ok",
217
+ "ok": True,
218
+ "requires_llm": False,
219
+ "prompt_received": True,
220
+ "prompt_length": len(prompt),
221
+ "identity": {"name": updated.get("agent_name"), "source": "local"},
222
+ "action": "rename",
223
+ "response": f"Pronto. Meu nome local agora e {updated.get('agent_name')}.",
224
+ }
225
+ return finalize_agent_session(result, session, prompt, backend=request.llm)
73
226
  if is_identity_question(prompt):
74
227
  result = {
75
228
  "kind": "agent",
@@ -82,6 +235,13 @@ def run_agent_prompt_request(request: AgentPromptRequest) -> dict[str, Any]:
82
235
  "response": local_identity_response(prompt, name=name),
83
236
  }
84
237
  return finalize_agent_session(result, session, prompt, backend=request.llm)
238
+ if is_capabilities_help_prompt(prompt):
239
+ return finalize_agent_session(
240
+ local_capabilities_help_response(prompt, name=name),
241
+ session,
242
+ prompt,
243
+ backend=request.llm,
244
+ )
85
245
  natural_result = dispatch_natural_operational_prompt(prompt)
86
246
  if natural_result:
87
247
  return finalize_agent_session(natural_result, session, prompt, backend=request.llm)
@@ -113,9 +273,15 @@ def run_agent_prompt_request(request: AgentPromptRequest) -> dict[str, Any]:
113
273
  )
114
274
  local_llm_execution = maybe_delegate_local_llm(prompt, model_plan)
115
275
  coordinator_prompt = enrich_prompt_with_local_result(contextual_prompt, local_llm_execution)
276
+ requested_backend = request.llm
277
+ if should_prompt_for_embedded_install(model_plan, requested_backend=request.llm):
278
+ result = embedded_mini_brain_install_response(prompt, name=name, model_plan=model_plan)
279
+ return finalize_agent_session(result, session, prompt, backend="embedded-mini-brain")
280
+ if should_use_embedded_coordinator(model_plan, requested_backend=request.llm):
281
+ requested_backend = "embedded-mini-brain"
116
282
  result = invoke_agent_prompt(
117
283
  coordinator_prompt,
118
- request.llm,
284
+ requested_backend,
119
285
  public_name=name,
120
286
  allow_fallback=not request.no_llm_fallback,
121
287
  )
@@ -154,6 +320,32 @@ def run_agent_prompt_request(request: AgentPromptRequest) -> dict[str, Any]:
154
320
  return finalize_agent_session(result, session, prompt, backend=result.get("llm_backend") or request.llm)
155
321
 
156
322
 
323
+ def should_use_embedded_coordinator(model_plan: dict[str, Any], *, requested_backend: str | None) -> bool:
324
+ if requested_backend:
325
+ return False
326
+ return (
327
+ model_plan.get("strategy") == "mini-brain"
328
+ and model_plan.get("local_llm_provider") == "embedded-mini-brain"
329
+ and model_plan.get("risk") == "low"
330
+ )
331
+
332
+
333
+ def should_prompt_for_embedded_install(model_plan: dict[str, Any], *, requested_backend: str | None) -> bool:
334
+ if requested_backend:
335
+ return False
336
+ embedded = (
337
+ ((model_plan.get("mini_brain") or {}).get("embedded") or {})
338
+ if isinstance(model_plan.get("mini_brain"), dict)
339
+ else {}
340
+ )
341
+ return (
342
+ model_plan.get("strategy") in {"mini-brain", "external-llm"}
343
+ and model_plan.get("local_llm_provider") == "embedded-mini-brain"
344
+ and embedded.get("available") is not True
345
+ and model_plan.get("fallback") == "configure-local-mini-brain-or-use-external-llm"
346
+ )
347
+
348
+
157
349
  def mark_review_task_needs_review(execution_plan: dict[str, Any], review_result: dict[str, Any]) -> dict[str, Any]:
158
350
  task = dict(execution_plan.get("review_task") or {})
159
351
  if task:
@@ -3,6 +3,7 @@
3
3
  from __future__ import annotations
4
4
 
5
5
  import json
6
+ import os
6
7
  import platform
7
8
  import shutil
8
9
  import subprocess
@@ -10,17 +11,31 @@ import urllib.error
10
11
  import urllib.request
11
12
  from typing import Any
12
13
 
14
+ from cli.aikit.app_home import config_path
15
+
13
16
 
14
17
  OLLAMA_TIMEOUT_SECONDS = 120
15
18
  DEFAULT_BASE_URL = "http://localhost:11434"
19
+ DEFAULT_RECOMMENDED_MODELS = [
20
+ {"name": "qwen3:0.6b", "family": "mini-brain", "recommended_for": "setup help, short summaries and lightweight intent classification"},
21
+ {"name": "qwen2.5-coder", "family": "coding", "recommended_for": "operational code reading and generation"},
22
+ {"name": "deepseek-coder", "family": "coding", "recommended_for": "code analysis and mechanical refactors"},
23
+ {"name": "deepseek-r1", "family": "reasoning", "recommended_for": "local reasoning drafts with mandatory review"},
24
+ {"name": "llama3.2", "family": "general", "recommended_for": "general local summaries and classification"},
25
+ {"name": "mistral", "family": "general", "recommended_for": "lightweight operational summaries"},
26
+ {"name": "gemma", "family": "classification", "recommended_for": "small extraction and classification tasks"},
27
+ ]
16
28
 
17
29
 
18
30
  def ollama_status(*, base_url: str = DEFAULT_BASE_URL) -> dict[str, Any]:
31
+ base_url = os.environ.get("OLLAMA_BASE_URL") or base_url
19
32
  binary = shutil.which("ollama")
20
33
  version = command_output(["ollama", "--version"]) if binary else None
21
34
  daemon = daemon_status(base_url) if binary else {"status": "unknown", "message": "Ollama binary is not installed."}
22
35
  models = list_local_models(binary_available=bool(binary))
23
- status = "ok" if binary else "missing"
36
+ status = "missing"
37
+ if binary:
38
+ status = "ok" if daemon.get("status") == "ok" else "partial"
24
39
  return {
25
40
  "kind": "ollama-status",
26
41
  "status": status,
@@ -52,10 +67,12 @@ def ollama_pull(model: str | None, *, yes: bool = False, dry_run: bool = False)
52
67
  binary = shutil.which("ollama")
53
68
  command = ["ollama", "pull", model]
54
69
  if dry_run or not yes:
70
+ needs_confirmation = not dry_run and not yes
55
71
  return {
56
72
  "kind": "ollama-pull",
57
73
  "status": "planned" if dry_run else "needs-confirmation",
58
74
  "ok": bool(dry_run),
75
+ "exit_code": 2 if needs_confirmation else 0,
59
76
  "model": model,
60
77
  "binary": binary,
61
78
  "command": command,
@@ -91,10 +108,12 @@ def ollama_pull(model: str | None, *, yes: bool = False, dry_run: bool = False)
91
108
  def ollama_update(*, yes: bool = False, dry_run: bool = False) -> dict[str, Any]:
92
109
  command = update_command()
93
110
  if dry_run or not yes:
111
+ needs_confirmation = not dry_run and not yes
94
112
  return {
95
113
  "kind": "ollama-update",
96
114
  "status": "planned" if dry_run else "needs-confirmation",
97
115
  "ok": bool(dry_run),
116
+ "exit_code": 2 if needs_confirmation else 0,
98
117
  "command": command,
99
118
  "dry_run": dry_run,
100
119
  "yes": yes,
@@ -142,26 +161,56 @@ def parse_ollama_list(output: str) -> list[dict[str, Any]]:
142
161
 
143
162
 
144
163
  def recommended_models(*, installed: set[str]) -> list[dict[str, Any]]:
145
- catalog = [
146
- ("qwen3:0.6b", "mini-brain", "setup help, short summaries and lightweight intent classification"),
147
- ("qwen2.5-coder", "coding", "operational code reading and generation"),
148
- ("deepseek-coder", "coding", "code analysis and mechanical refactors"),
149
- ("deepseek-r1", "reasoning", "local reasoning drafts with mandatory review"),
150
- ("llama3.2", "general", "general local summaries and classification"),
151
- ("mistral", "general", "lightweight operational summaries"),
152
- ("gemma", "classification", "small extraction and classification tasks"),
153
- ]
164
+ catalog = model_catalog()
154
165
  return [
155
166
  {
156
- "name": name,
157
- "family": family,
158
- "recommended_for": recommended_for,
159
- "installed": any(item == name or item.startswith(f"{name}:") for item in installed),
167
+ "name": item["name"],
168
+ "family": item["family"],
169
+ "recommended_for": item["recommended_for"],
170
+ "installed": any(model == item["name"] or model.startswith(f"{item['name']}:") for model in installed),
171
+ "source": item["source"],
160
172
  }
161
- for name, family, recommended_for in catalog
173
+ for item in catalog
162
174
  ]
163
175
 
164
176
 
177
+ def model_catalog() -> list[dict[str, str]]:
178
+ items = {item["name"]: {**item, "source": "default"} for item in DEFAULT_RECOMMENDED_MODELS}
179
+ for item in configured_model_catalog():
180
+ name = str(item.get("name") or "").strip()
181
+ if not name:
182
+ continue
183
+ items[name] = {
184
+ "name": name,
185
+ "family": str(item.get("family") or "custom").strip() or "custom",
186
+ "recommended_for": str(item.get("recommended_for") or item.get("purpose") or "configured local model").strip(),
187
+ "source": "config",
188
+ }
189
+ return list(items.values())
190
+
191
+
192
+ def configured_model_catalog() -> list[dict[str, Any]]:
193
+ path = config_path()
194
+ if not path.exists():
195
+ return []
196
+ try:
197
+ payload = json.loads(path.read_text(encoding="utf-8"))
198
+ except (OSError, json.JSONDecodeError):
199
+ return []
200
+ if not isinstance(payload, dict):
201
+ return []
202
+ local_llm = payload.get("local_llm") if isinstance(payload.get("local_llm"), dict) else {}
203
+ mini_brain = payload.get("mini_brain") if isinstance(payload.get("mini_brain"), dict) else {}
204
+ for value in (
205
+ local_llm.get("recommended_models"),
206
+ local_llm.get("models"),
207
+ mini_brain.get("recommended_models"),
208
+ ):
209
+ if isinstance(value, list):
210
+ return [item for item in value if isinstance(item, dict)]
211
+ return []
212
+
213
+
165
214
  def daemon_status(base_url: str) -> dict[str, Any]:
166
215
  url = base_url.rstrip("/") + "/api/tags"
167
216
  try: