@openlife/cli 1.7.3

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 (394) hide show
  1. package/INSTALL.md +266 -0
  2. package/LICENSE +21 -0
  3. package/README.md +142 -0
  4. package/bin/openlife.js +3 -0
  5. package/dist/admin_panel_server.js +66 -0
  6. package/dist/cli/AgentManager.js +109 -0
  7. package/dist/cli/AutonomousInstaller.js +134 -0
  8. package/dist/cli/DreamOrganizer.js +88 -0
  9. package/dist/cli/HostInstaller.js +426 -0
  10. package/dist/cli/InstallBanner.js +16 -0
  11. package/dist/cli/InstallFlow.js +256 -0
  12. package/dist/cli/InstallHeadless.js +47 -0
  13. package/dist/cli/InstallModules.js +148 -0
  14. package/dist/cli/InstallStateStore.js +75 -0
  15. package/dist/cli/InstallWizard.js +364 -0
  16. package/dist/cli/ProfileManager.js +163 -0
  17. package/dist/cli/SystemInstaller.js +89 -0
  18. package/dist/cli/WorldClassCommands.js +208 -0
  19. package/dist/design/DesignMdImporter.js +82 -0
  20. package/dist/design/DesignMdMode.js +93 -0
  21. package/dist/design/DesignMdRegistry.js +67 -0
  22. package/dist/index.js +2575 -0
  23. package/dist/memory/ConversationMemory.js +33 -0
  24. package/dist/memory/LocalMemoryProvider.js +86 -0
  25. package/dist/memory/Mem0Provider.js +16 -0
  26. package/dist/memory/MemoryNamespacePolicy.js +27 -0
  27. package/dist/memory/MemoryOrchestrator.js +65 -0
  28. package/dist/memory/MemoryPromotionFlow.js +32 -0
  29. package/dist/memory/MemoryProvider.js +2 -0
  30. package/dist/memory/MemoryProviderRegistry.js +27 -0
  31. package/dist/memory/MemoryRetentionPolicy.js +60 -0
  32. package/dist/memory/MempalaceProvider.js +72 -0
  33. package/dist/memory/OmniMemory.js +106 -0
  34. package/dist/memory/RedisAgentMemoryProvider.js +16 -0
  35. package/dist/memory/SessionManager.js +86 -0
  36. package/dist/memory/ZepGraphitiProvider.js +16 -0
  37. package/dist/orchestrator/AgentRegistry.js +56 -0
  38. package/dist/orchestrator/AgentScoring.js +82 -0
  39. package/dist/orchestrator/AgentTeam.js +22 -0
  40. package/dist/orchestrator/ArbitrationAgent.js +43 -0
  41. package/dist/orchestrator/ArbitrationScorecard.js +17 -0
  42. package/dist/orchestrator/AssetPromotionEngine.js +65 -0
  43. package/dist/orchestrator/AssetReuseRouter.js +63 -0
  44. package/dist/orchestrator/BenchmarkEngine.js +75 -0
  45. package/dist/orchestrator/Brain.js +298 -0
  46. package/dist/orchestrator/CadenceEngine.js +76 -0
  47. package/dist/orchestrator/CapabilityRouter.js +36 -0
  48. package/dist/orchestrator/CommandLanguage.js +27 -0
  49. package/dist/orchestrator/CommandRouter.js +70 -0
  50. package/dist/orchestrator/ConsequenceForecaster.js +286 -0
  51. package/dist/orchestrator/CronManager.js +286 -0
  52. package/dist/orchestrator/DynamicAgentBuilder.js +48 -0
  53. package/dist/orchestrator/DynamicAgentExecutor.js +15 -0
  54. package/dist/orchestrator/EnterpriseAgenticCore.js +276 -0
  55. package/dist/orchestrator/ExecutionBoard.js +86 -0
  56. package/dist/orchestrator/ExecutionIntent.js +13 -0
  57. package/dist/orchestrator/ExecutionModePolicy.js +48 -0
  58. package/dist/orchestrator/ExecutionRouter.js +9 -0
  59. package/dist/orchestrator/ExecutionState.js +20 -0
  60. package/dist/orchestrator/ExecutorHealth.js +86 -0
  61. package/dist/orchestrator/ExternalCatalogRegistry.js +83 -0
  62. package/dist/orchestrator/Gatekeeper.js +414 -0
  63. package/dist/orchestrator/Gateway.js +508 -0
  64. package/dist/orchestrator/GovernanceConsentStore.js +66 -0
  65. package/dist/orchestrator/GovernanceLayer.js +179 -0
  66. package/dist/orchestrator/GovernancePolicyStore.js +53 -0
  67. package/dist/orchestrator/GovernanceScopeLedger.js +134 -0
  68. package/dist/orchestrator/GovernanceScopePolicy.js +67 -0
  69. package/dist/orchestrator/IntentClassifier.js +45 -0
  70. package/dist/orchestrator/JobLifecycle.js +91 -0
  71. package/dist/orchestrator/LearningRouter.js +24 -0
  72. package/dist/orchestrator/MediaManager.js +92 -0
  73. package/dist/orchestrator/MemoryCuratorAgent.js +41 -0
  74. package/dist/orchestrator/MissionState.js +155 -0
  75. package/dist/orchestrator/ModelManager.js +84 -0
  76. package/dist/orchestrator/OperatingSystem.js +71 -0
  77. package/dist/orchestrator/OperationalMemoryStore.js +94 -0
  78. package/dist/orchestrator/OptimizationLoop.js +72 -0
  79. package/dist/orchestrator/OrchestrationLoop.js +905 -0
  80. package/dist/orchestrator/OrgStructure.js +88 -0
  81. package/dist/orchestrator/OutcomeSimulator.js +46 -0
  82. package/dist/orchestrator/ParallelOrchestrationLoop.js +36 -0
  83. package/dist/orchestrator/PerformanceScorecard.js +105 -0
  84. package/dist/orchestrator/PlannerAgent.js +46 -0
  85. package/dist/orchestrator/ProcessSandbox.js +129 -0
  86. package/dist/orchestrator/PromotionPipeline.js +74 -0
  87. package/dist/orchestrator/PromotionReviewGate.js +11 -0
  88. package/dist/orchestrator/QueueScheduler.js +260 -0
  89. package/dist/orchestrator/ReleaseGate.js +36 -0
  90. package/dist/orchestrator/ReleaseWorkflow.js +68 -0
  91. package/dist/orchestrator/RemotePublisher.js +139 -0
  92. package/dist/orchestrator/ReuseEngine.js +89 -0
  93. package/dist/orchestrator/ReviewerAgent.js +49 -0
  94. package/dist/orchestrator/RoleHandoff.js +65 -0
  95. package/dist/orchestrator/RuntimeHealthMonitor.js +143 -0
  96. package/dist/orchestrator/RuntimePolicy.js +105 -0
  97. package/dist/orchestrator/RuntimeProbe.js +97 -0
  98. package/dist/orchestrator/RuntimeRegistry.js +73 -0
  99. package/dist/orchestrator/SandboxPolicy.js +22 -0
  100. package/dist/orchestrator/SecurityDownloadGuard.js +169 -0
  101. package/dist/orchestrator/SecurityEventStore.js +58 -0
  102. package/dist/orchestrator/ServiceCompletionPolicy.js +36 -0
  103. package/dist/orchestrator/ServiceState.js +195 -0
  104. package/dist/orchestrator/SkillCreator.js +404 -0
  105. package/dist/orchestrator/SkillLearningLoop.js +57 -0
  106. package/dist/orchestrator/SkillManager.js +75 -0
  107. package/dist/orchestrator/SkillNetwork.js +29 -0
  108. package/dist/orchestrator/SkillRegistryV2.js +28 -0
  109. package/dist/orchestrator/SkillScoring.js +70 -0
  110. package/dist/orchestrator/SquadAutoCreator.js +64 -0
  111. package/dist/orchestrator/SquadCreator.js +727 -0
  112. package/dist/orchestrator/SquadRegistry.js +28 -0
  113. package/dist/orchestrator/SquadRouter.js +33 -0
  114. package/dist/orchestrator/SquadScoring.js +70 -0
  115. package/dist/orchestrator/SubagentLifecycle.js +90 -0
  116. package/dist/orchestrator/SynthesizerAgent.js +48 -0
  117. package/dist/orchestrator/SystemDoctor.js +224 -0
  118. package/dist/orchestrator/TaskExecutor.js +422 -0
  119. package/dist/orchestrator/TeammateBoard.js +61 -0
  120. package/dist/orchestrator/TestHarness.js +184 -0
  121. package/dist/orchestrator/VoiceManager.js +203 -0
  122. package/dist/orchestrator/VoiceRouter.js +89 -0
  123. package/dist/orchestrator/capability/CapabilityGenesisEngine.js +278 -0
  124. package/dist/orchestrator/capability/CapabilityPackParser.js +223 -0
  125. package/dist/orchestrator/capability/CapabilityPackSchema.js +62 -0
  126. package/dist/orchestrator/capability/CapabilityPackState.js +163 -0
  127. package/dist/orchestrator/providers/AgentProvider.js +2 -0
  128. package/dist/orchestrator/providers/CapabilityProvider.js +12 -0
  129. package/dist/orchestrator/providers/CloudAgentProvider.js +55 -0
  130. package/dist/orchestrator/providers/CloudSkillProvider.js +55 -0
  131. package/dist/orchestrator/providers/CloudSquadProvider.js +55 -0
  132. package/dist/orchestrator/providers/CompositeAgentProvider.js +16 -0
  133. package/dist/orchestrator/providers/CompositeCapabilityProvider.js +25 -0
  134. package/dist/orchestrator/providers/CompositeSkillProvider.js +16 -0
  135. package/dist/orchestrator/providers/CompositeSquadProvider.js +16 -0
  136. package/dist/orchestrator/providers/CompositeWorkflowProvider.js +46 -0
  137. package/dist/orchestrator/providers/FileAgentProvider.js +105 -0
  138. package/dist/orchestrator/providers/FileCapabilityProvider.js +106 -0
  139. package/dist/orchestrator/providers/FileSkillProvider.js +65 -0
  140. package/dist/orchestrator/providers/FileSquadProvider.js +69 -0
  141. package/dist/orchestrator/providers/FileWorkflowProvider.js +103 -0
  142. package/dist/orchestrator/providers/SkillProvider.js +2 -0
  143. package/dist/orchestrator/providers/SquadProvider.js +2 -0
  144. package/dist/orchestrator/toolset/ToolsetGuard.js +69 -0
  145. package/dist/orchestrator/toolset/ToolsetRegistry.js +65 -0
  146. package/dist/orchestrator/toolset/ToolsetSchema.js +21 -0
  147. package/dist/orchestrator/util/AtomicWriter.js +204 -0
  148. package/dist/orchestrator/util/DistributedLock.js +232 -0
  149. package/dist/orchestrator/util/TemplateRenderer.js +87 -0
  150. package/dist/orchestrator/util/WatchdogHeartbeat.js +116 -0
  151. package/dist/orchestrator/workflow/ConditionParser.js +232 -0
  152. package/dist/orchestrator/workflow/WorkflowEngine.js +379 -0
  153. package/dist/orchestrator/workflow/WorkflowParser.js +368 -0
  154. package/dist/orchestrator/workflow/WorkflowSchema.js +65 -0
  155. package/dist/orchestrator/workflow/WorkflowState.js +11 -0
  156. package/dist/reversa/ReversaAgent.js +134 -0
  157. package/dist/reversa/ReversaContracts.js +62 -0
  158. package/dist/reversa/ReversaExecutors.js +65 -0
  159. package/dist/skills/SkillRegistry.js +71 -0
  160. package/dist/squads/SquadManager.js +87 -0
  161. package/dist/test_admin_teams_networks.js +54 -0
  162. package/dist/test_agent_team_skill_network.js +15 -0
  163. package/dist/test_aiobuilder_cli_parity.js +169 -0
  164. package/dist/test_ask_exit.js +73 -0
  165. package/dist/test_atomic_writer.js +209 -0
  166. package/dist/test_autonomous_soak.js +141 -0
  167. package/dist/test_benchmark_engine.js +41 -0
  168. package/dist/test_brain_error_diagnostics.js +51 -0
  169. package/dist/test_brain_fallback_chain.js +93 -0
  170. package/dist/test_capability_genesis_engine.js +225 -0
  171. package/dist/test_capability_pack_schema.js +214 -0
  172. package/dist/test_catalog_quality.js +150 -0
  173. package/dist/test_cli_crud_roundtrip.js +154 -0
  174. package/dist/test_cli_diagnostics.js +131 -0
  175. package/dist/test_cli_doc_parity.js +126 -0
  176. package/dist/test_cli_help_surface.js +106 -0
  177. package/dist/test_cli_service_commands.js +83 -0
  178. package/dist/test_consequence_forecast_brain.js +165 -0
  179. package/dist/test_consequence_forecaster.js +24 -0
  180. package/dist/test_conversation_memory.js +36 -0
  181. package/dist/test_create_entities.js +54 -0
  182. package/dist/test_creator_placeholders_completed.js +177 -0
  183. package/dist/test_cron_manager.js +123 -0
  184. package/dist/test_daemon_sigterm.js +72 -0
  185. package/dist/test_deep_research_capability.js +87 -0
  186. package/dist/test_designmd_import_registry.js +16 -0
  187. package/dist/test_designmd_mode.js +50 -0
  188. package/dist/test_designmd_mode_workspace.js +13 -0
  189. package/dist/test_dist_templates_layout.js +135 -0
  190. package/dist/test_distributed_lock.js +201 -0
  191. package/dist/test_distribution_installability.js +67 -0
  192. package/dist/test_doctor_sandbox_check.js +44 -0
  193. package/dist/test_dream_organizer.js +25 -0
  194. package/dist/test_dual_mode.js +15 -0
  195. package/dist/test_enterprise_agentic_core.js +128 -0
  196. package/dist/test_forecast_brain_wiring.js +87 -0
  197. package/dist/test_gateway_telegram_guardrails.js +52 -0
  198. package/dist/test_governance.js +34 -0
  199. package/dist/test_governance_advanced.js +75 -0
  200. package/dist/test_governance_scope_ledger.js +147 -0
  201. package/dist/test_governance_v13_policies.js +44 -0
  202. package/dist/test_guided_creator_cli.js +100 -0
  203. package/dist/test_host_install_e2e.js +324 -0
  204. package/dist/test_host_installer.js +259 -0
  205. package/dist/test_host_installers_gemini_codex.js +95 -0
  206. package/dist/test_host_uninstaller.js +295 -0
  207. package/dist/test_install_flow.js +70 -0
  208. package/dist/test_install_flow_host_validation.js +143 -0
  209. package/dist/test_install_wizard.js +272 -0
  210. package/dist/test_integration_gemini_live.js +95 -0
  211. package/dist/test_integration_http_trigger_live.js +154 -0
  212. package/dist/test_integration_telegram_live.js +102 -0
  213. package/dist/test_job_lifecycle.js +16 -0
  214. package/dist/test_memory_orchestrator.js +33 -0
  215. package/dist/test_memory_promotion.js +36 -0
  216. package/dist/test_memory_retention.js +37 -0
  217. package/dist/test_mission_checkpoint.js +204 -0
  218. package/dist/test_multi_host_docs_parity.js +125 -0
  219. package/dist/test_openlife_auto_creator_routing.js +69 -0
  220. package/dist/test_openlife_evolution_surface.js +77 -0
  221. package/dist/test_openlife_gatekeeper_routing.js +15 -0
  222. package/dist/test_openlife_routing_surface.js +27 -0
  223. package/dist/test_openlife_runtime_source_truth.js +25 -0
  224. package/dist/test_operating_system.js +45 -0
  225. package/dist/test_optimization_loop.js +38 -0
  226. package/dist/test_orchestration_assets_lifecycle.js +78 -0
  227. package/dist/test_outcome_simulator.js +38 -0
  228. package/dist/test_performance_latency.js +215 -0
  229. package/dist/test_performance_scorecard.js +38 -0
  230. package/dist/test_phase1_check_exit.js +103 -0
  231. package/dist/test_phase6_board.js +31 -0
  232. package/dist/test_phase6_cadence.js +29 -0
  233. package/dist/test_phase6_ops.js +37 -0
  234. package/dist/test_post_mission_evaluation.js +190 -0
  235. package/dist/test_process_sandbox.js +88 -0
  236. package/dist/test_profile_toolset_mcp.js +125 -0
  237. package/dist/test_queue_scheduler.js +239 -0
  238. package/dist/test_release_gate.js +23 -0
  239. package/dist/test_remote_publish.js +193 -0
  240. package/dist/test_reversa_contracts_e2e.js +48 -0
  241. package/dist/test_reversa_export_and_strict.js +51 -0
  242. package/dist/test_reversa_full_execution.js +12 -0
  243. package/dist/test_reversa_lite.js +9 -0
  244. package/dist/test_royal_stack_golden.js +179 -0
  245. package/dist/test_runtime_health_backoff.js +154 -0
  246. package/dist/test_runtime_policy.js +26 -0
  247. package/dist/test_runtime_probe.js +19 -0
  248. package/dist/test_runtime_profile_oauth_only.js +262 -0
  249. package/dist/test_runtime_registry.js +11 -0
  250. package/dist/test_security_download_and_scan.js +103 -0
  251. package/dist/test_security_download_guard.js +14 -0
  252. package/dist/test_service_command_surface.js +12 -0
  253. package/dist/test_service_completion_policy.js +32 -0
  254. package/dist/test_service_guardrails_delete.js +12 -0
  255. package/dist/test_service_mode_explicit_only.js +174 -0
  256. package/dist/test_sources_import_ref.js +46 -0
  257. package/dist/test_sources_scaffold.js +43 -0
  258. package/dist/test_squad_skill_creator.js +305 -0
  259. package/dist/test_squad_skill_design_llm.js +176 -0
  260. package/dist/test_subsystems_org_state.js +271 -0
  261. package/dist/test_subsystems_promotion_memory_assets.js +343 -0
  262. package/dist/test_subsystems_routing_governance.js +234 -0
  263. package/dist/test_task_executor_sandbox_optin.js +127 -0
  264. package/dist/test_teammate_learning.js +15 -0
  265. package/dist/test_telegram_delete_guardrail.js +21 -0
  266. package/dist/test_toolset_enforcement.js +188 -0
  267. package/dist/test_trigger_basic_auth.js +112 -0
  268. package/dist/test_util/doc_parity.js +120 -0
  269. package/dist/test_v15_e2e_integration.js +207 -0
  270. package/dist/test_watchdog_heartbeat.js +152 -0
  271. package/dist/test_workflow_condition_parser.js +63 -0
  272. package/dist/test_workflow_e2e.js +240 -0
  273. package/dist/test_workflow_engine.js +330 -0
  274. package/dist/test_workflow_parser.js +245 -0
  275. package/dist/test_workflow_schema_backward_compat.js +197 -0
  276. package/dist-templates/README.md +91 -0
  277. package/dist-templates/claude-code/agents/openlife-atlas.md +52 -0
  278. package/dist-templates/claude-code/agents/openlife-forge.md +42 -0
  279. package/dist-templates/claude-code/agents/openlife-genesis.md +59 -0
  280. package/dist-templates/claude-code/agents/openlife-lyra.md +40 -0
  281. package/dist-templates/claude-code/agents/openlife-maestro.md +45 -0
  282. package/dist-templates/claude-code/commands/openlife/ask.md +14 -0
  283. package/dist-templates/claude-code/commands/openlife/doctor.md +19 -0
  284. package/dist-templates/claude-code/commands/openlife/dream.md +20 -0
  285. package/dist-templates/claude-code/commands/openlife/status.md +14 -0
  286. package/dist-templates/claude-code/mcp/openlife-orchestrator.json +46 -0
  287. package/dist-templates/codex/README.md +7 -0
  288. package/dist-templates/codex/agents/openlife-atlas.md +52 -0
  289. package/dist-templates/codex/agents/openlife-forge.md +42 -0
  290. package/dist-templates/codex/agents/openlife-genesis.md +59 -0
  291. package/dist-templates/codex/agents/openlife-lyra.md +40 -0
  292. package/dist-templates/codex/agents/openlife-maestro.md +45 -0
  293. package/dist-templates/codex/commands/openlife/ask.md +14 -0
  294. package/dist-templates/codex/commands/openlife/doctor.md +19 -0
  295. package/dist-templates/codex/commands/openlife/dream.md +20 -0
  296. package/dist-templates/codex/commands/openlife/status.md +14 -0
  297. package/dist-templates/codex/mcp/openlife-orchestrator.json +46 -0
  298. package/dist-templates/gemini-cli/README.md +8 -0
  299. package/dist-templates/gemini-cli/agents/openlife-atlas.md +52 -0
  300. package/dist-templates/gemini-cli/agents/openlife-forge.md +42 -0
  301. package/dist-templates/gemini-cli/agents/openlife-genesis.md +59 -0
  302. package/dist-templates/gemini-cli/agents/openlife-lyra.md +40 -0
  303. package/dist-templates/gemini-cli/agents/openlife-maestro.md +45 -0
  304. package/dist-templates/gemini-cli/commands/openlife/ask.md +14 -0
  305. package/dist-templates/gemini-cli/commands/openlife/doctor.md +19 -0
  306. package/dist-templates/gemini-cli/commands/openlife/dream.md +20 -0
  307. package/dist-templates/gemini-cli/commands/openlife/status.md +14 -0
  308. package/dist-templates/gemini-cli/mcp/openlife-orchestrator.json +46 -0
  309. package/dist-templates/skill-template/README.md +34 -0
  310. package/dist-templates/skill-template/SKILL.md.template +59 -0
  311. package/dist-templates/squad-template/README.md +82 -0
  312. package/dist-templates/squad-template/SQUAD.md.template +51 -0
  313. package/dist-templates/squad-template/agent-template.md +51 -0
  314. package/dist-templates/squad-template/checklist-template.md +25 -0
  315. package/dist-templates/squad-template/task-template.md +36 -0
  316. package/dist-templates/workflows/PORTED_WORKFLOWS.md +60 -0
  317. package/dist-templates/workflows/brownfield-discovery.yaml +137 -0
  318. package/dist-templates/workflows/greenfield-fullstack.yaml +132 -0
  319. package/dist-templates/workflows/qa-loop.yaml +125 -0
  320. package/dist-templates/workflows/story-development-cycle.yaml +80 -0
  321. package/docs/CHANGELOG_FEATURE_ROLLOUT_DESIGNMD.md +43 -0
  322. package/docs/EXTERNAL_SOURCES_AND_SECURITY_GUARD.md +33 -0
  323. package/docs/OPENLIFE_AUDIT_2026-05-06.md +170 -0
  324. package/docs/OPENLIFE_CONSOLIDATED_PLAN_2026-05-06.md +299 -0
  325. package/docs/OPENLIFE_DUAL_MODE_IMPLEMENTATION_PLAN.md +205 -0
  326. package/docs/OPENLIFE_EVOLUTION_SURFACE_2026-05-07.md +53 -0
  327. package/docs/OPENLIFE_SKILLS_IMPORT_2026-05-07.json +223 -0
  328. package/docs/OPENLIFE_SQUADS_IMPORT_2026-05-07.json +184 -0
  329. package/docs/PAPERCLIP_OPENLIFE_INVESTIGATION.md +85 -0
  330. package/docs/README.md +28 -0
  331. package/docs/RELEASE_ORGANIZATION_PLAN.md +164 -0
  332. package/docs/audit/CLI-EXECUTION-RESULTS.md +113 -0
  333. package/docs/audit/CLI-MATRIX.md +556 -0
  334. package/docs/audit/DOC-PARITY-GAPS.md +351 -0
  335. package/docs/audit/ORCHESTRATOR-MATRIX.md +136 -0
  336. package/docs/audit/TEST-COVERAGE-GAPS.md +334 -0
  337. package/docs/audit/integrations/SKIPPED.md +101 -0
  338. package/docs/autonomous-install.md +79 -0
  339. package/docs/capability-genesis.md +137 -0
  340. package/docs/capability-pack-schema.md +157 -0
  341. package/docs/commands.md +82 -0
  342. package/docs/deep-research-capability.md +114 -0
  343. package/docs/development/typescript-conventions.md +95 -0
  344. package/docs/host-installers.md +68 -0
  345. package/docs/install/aiobuilder.md +70 -0
  346. package/docs/install/claude-code.md +83 -0
  347. package/docs/install/codex.md +64 -0
  348. package/docs/install/gemini-cli.md +64 -0
  349. package/docs/install/runtime-profiles.md +83 -0
  350. package/docs/openlife-agent-os-blueprint.md +114 -0
  351. package/docs/openlife-install-backlog.md +115 -0
  352. package/docs/openlife-install-spec.md +306 -0
  353. package/docs/operations/CLOUD_CUTOVER_AUDIT.md +37 -0
  354. package/docs/operations/PHASE_PROGRESS_CONTINUATION.md +24 -0
  355. package/docs/performance-benchmarks.md +83 -0
  356. package/docs/planning/v1.3-capability-genesis.md +157 -0
  357. package/docs/plans/2026-05-05-admin-interface-professional-dark-premium-plan.md +84 -0
  358. package/docs/plans/2026-05-05-openlife-autonomous-domain-marketplace-masterplan.md +122 -0
  359. package/docs/quickstart.md +60 -0
  360. package/docs/release-process.md +236 -0
  361. package/docs/roadmap/OPENLIFE_MASTER_PLAN_CLOUD_V3.md +97 -0
  362. package/docs/sandboxing-research.md +117 -0
  363. package/docs/stories/epic-feature-audit/1.1.story.md +84 -0
  364. package/docs/stories/epic-feature-audit/1.2.story.md +102 -0
  365. package/docs/stories/epic-feature-audit/1.3.story.md +93 -0
  366. package/docs/stories/epic-feature-audit/1.5.story.md +121 -0
  367. package/docs/stories/epic-feature-audit/1.6.story.md +80 -0
  368. package/docs/stories/epic-feature-completeness/2.1.story.md +70 -0
  369. package/docs/stories/epic-feature-completeness/2.2.story.md +49 -0
  370. package/docs/stories/epic-feature-completeness/2.3.story.md +74 -0
  371. package/docs/stories/epic-feature-completeness/2.4.story.md +71 -0
  372. package/docs/stories/epic-feature-completeness/3.1.story.md +56 -0
  373. package/docs/stories/epic-feature-completeness/3.2.story.md +80 -0
  374. package/docs/stories/epic-feature-completeness/3.3.story.md +68 -0
  375. package/docs/stories/epic-feature-completeness/3.4.story.md +71 -0
  376. package/docs/stories/epic-feature-completeness/3.5.story.md +72 -0
  377. package/docs/stories/epic-feature-completeness/3.6.story.md +69 -0
  378. package/docs/stories/epic-feature-completeness/3.7.story.md +68 -0
  379. package/docs/stories/epic-feature-completeness/3.8.story.md +57 -0
  380. package/docs/toolset-enforcement.md +122 -0
  381. package/docs/v1.4-changelog.md +159 -0
  382. package/docs/v1.5-changelog.md +106 -0
  383. package/docs/v1.5-roadmap.md +121 -0
  384. package/docs/v1.6-changelog.md +67 -0
  385. package/docs/v1.6-roadmap.md +89 -0
  386. package/docs/v1.7-changelog.md +98 -0
  387. package/docs/workflow-schema.md +177 -0
  388. package/package.json +177 -0
  389. package/scripts/clean-test-pollution.js +61 -0
  390. package/scripts/openlife-agent-start.sh +6 -0
  391. package/scripts/openlife-agent.service.example +13 -0
  392. package/scripts/openlife-agent.supervisord.conf.example +8 -0
  393. package/scripts/openlife-autonomous-install.sh +29 -0
  394. package/scripts/postinstall-check.sh +37 -0
package/dist/index.js ADDED
@@ -0,0 +1,2575 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
4
+ if (k2 === undefined) k2 = k;
5
+ var desc = Object.getOwnPropertyDescriptor(m, k);
6
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
7
+ desc = { enumerable: true, get: function() { return m[k]; } };
8
+ }
9
+ Object.defineProperty(o, k2, desc);
10
+ }) : (function(o, m, k, k2) {
11
+ if (k2 === undefined) k2 = k;
12
+ o[k2] = m[k];
13
+ }));
14
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
15
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
16
+ }) : function(o, v) {
17
+ o["default"] = v;
18
+ });
19
+ var __importStar = (this && this.__importStar) || (function () {
20
+ var ownKeys = function(o) {
21
+ ownKeys = Object.getOwnPropertyNames || function (o) {
22
+ var ar = [];
23
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
24
+ return ar;
25
+ };
26
+ return ownKeys(o);
27
+ };
28
+ return function (mod) {
29
+ if (mod && mod.__esModule) return mod;
30
+ var result = {};
31
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
32
+ __setModuleDefault(result, mod);
33
+ return result;
34
+ };
35
+ })();
36
+ Object.defineProperty(exports, "__esModule", { value: true });
37
+ const dotenv = __importStar(require("dotenv"));
38
+ const path = __importStar(require("path"));
39
+ const fs = __importStar(require("fs"));
40
+ dotenv.config({ path: path.join(__dirname, '..', '.env') });
41
+ const commander_1 = require("commander");
42
+ const IntentClassifier_1 = require("./orchestrator/IntentClassifier");
43
+ // Story 12.1 — small unknown→message helper used across CLI handlers.
44
+ function errMsg(e) {
45
+ if (e instanceof Error)
46
+ return e.message;
47
+ if (e && typeof e === 'object' && 'message' in e && typeof e.message === 'string') {
48
+ return e.message;
49
+ }
50
+ return String(e);
51
+ }
52
+ function errStdout(e) {
53
+ if (e && typeof e === 'object' && 'stdout' in e) {
54
+ const v = e.stdout;
55
+ if (typeof v === 'string')
56
+ return v;
57
+ }
58
+ return '';
59
+ }
60
+ function errStderr(e) {
61
+ if (e && typeof e === 'object' && 'stderr' in e) {
62
+ const v = e.stderr;
63
+ if (typeof v === 'string')
64
+ return v;
65
+ }
66
+ return '';
67
+ }
68
+ // Heavy runtime classes are intentionally loaded lazily by the commands that need them.
69
+ // This keeps deterministic CLI commands (`--help`, `plugin`, `context`, tests) from
70
+ // hanging on optional gateway/model dependencies during module import.
71
+ const ModelManager_1 = require("./orchestrator/ModelManager");
72
+ const child_process = __importStar(require("child_process"));
73
+ const util_1 = require("util");
74
+ const SystemInstaller_1 = require("./cli/SystemInstaller");
75
+ const SystemDoctor_1 = require("./orchestrator/SystemDoctor");
76
+ const WorldClassCommands_1 = require("./cli/WorldClassCommands");
77
+ const InstallBanner_1 = require("./cli/InstallBanner");
78
+ const DreamOrganizer_1 = require("./cli/DreamOrganizer");
79
+ const InstallFlow_1 = require("./cli/InstallFlow");
80
+ const InstallWizard_1 = require("./cli/InstallWizard");
81
+ const InstallHeadless_1 = require("./cli/InstallHeadless");
82
+ const ServiceState_1 = require("./orchestrator/ServiceState");
83
+ const MissionState_1 = require("./orchestrator/MissionState");
84
+ const JobLifecycle_1 = require("./orchestrator/JobLifecycle");
85
+ const RuntimeRegistry_1 = require("./orchestrator/RuntimeRegistry");
86
+ const ReversaAgent_1 = require("./reversa/ReversaAgent");
87
+ const TeammateBoard_1 = require("./orchestrator/TeammateBoard");
88
+ const SkillLearningLoop_1 = require("./orchestrator/SkillLearningLoop");
89
+ const DesignMdMode_1 = require("./design/DesignMdMode");
90
+ const ExternalCatalogRegistry_1 = require("./orchestrator/ExternalCatalogRegistry");
91
+ const SecurityDownloadGuard_1 = require("./orchestrator/SecurityDownloadGuard");
92
+ const DesignMdImporter_1 = require("./design/DesignMdImporter");
93
+ const DesignMdRegistry_1 = require("./design/DesignMdRegistry");
94
+ const EnterpriseAgenticCore_1 = require("./orchestrator/EnterpriseAgenticCore");
95
+ const exec = (0, util_1.promisify)(child_process.exec);
96
+ const program = new commander_1.Command();
97
+ function tryHandleDesignConversation(message) {
98
+ const text = message.trim();
99
+ const lower = text.toLowerCase();
100
+ const importer = new DesignMdImporter_1.DesignMdImporter();
101
+ const dm = new DesignMdMode_1.DesignMdMode();
102
+ const reversa = new ReversaAgent_1.ReversaAgent();
103
+ const wantsOpenDesignImport = /(import|carreg|baix|sync|sincroniz|traz|adicion)/i.test(text) && /(open\s*design|opendesign|getdesign)/i.test(text);
104
+ const wantsDesignMode = /(ativ|set|usar|trocar|mudar|habilit|enable)/i.test(text) && /(designmd|design\s*mode|modo\s*design)/i.test(text);
105
+ const wantsGenerateUi = /(gerar|gera|criar|cria|montar|fazer|faz|produce|build|generate)/i.test(text) && /(ui|interface|tela|layout|frontend|painel|dashboard)/i.test(text);
106
+ const wantsRefine = /^(refinar|refina|refine|iterar|itera|iterate|melhorar|melhora|polir)\b/i.test(text);
107
+ // Import Open Design por conversa
108
+ if (wantsOpenDesignImport) {
109
+ const pathMatch = text.match(/(?:em|from|de)\s+([^\n]+)$/i);
110
+ const vendorPath = (pathMatch?.[1] || 'vendor/open-design').trim();
111
+ if (!fs.existsSync(vendorPath)) {
112
+ return JSON.stringify({ error: 'vendor_not_found', vendorPath, hint: 'Clone open-design into this path or informe um caminho existente.' }, null, 2);
113
+ }
114
+ const guard = new SecurityDownloadGuard_1.SecurityDownloadGuard();
115
+ const scan = guard.scanExtractedDir(vendorPath);
116
+ if (!scan.ok) {
117
+ return JSON.stringify({ error: 'security_guard_blocked', scan }, null, 2);
118
+ }
119
+ const result = importer.importFromOpenDesign(vendorPath);
120
+ return JSON.stringify({ action: 'designmd.import', source: 'open-design', vendorPath, result }, null, 2);
121
+ }
122
+ // Definir modo designmd por conversa
123
+ if (wantsDesignMode) {
124
+ const profileMatch = text.match(/perfil\s+([a-zA-Z0-9._-]+)/i) || text.match(/profile\s+([a-zA-Z0-9._-]+)/i);
125
+ const profileId = profileMatch?.[1] || 'user-conversational-profile';
126
+ return JSON.stringify(reversa.setMode('designmd', profileId), null, 2);
127
+ }
128
+ // Refinar draft existente por conversa contínua (multi-turn)
129
+ if (wantsRefine) {
130
+ const featureRaw = text.replace(/^(refinar|refina|refine|iterar|itera|iterate|melhorar|melhora|polir)\s*/i, '').trim();
131
+ const statePath = require('path').join(process.cwd(), '.reversa', 'ui-drafts', '_conversation-state.json');
132
+ const remembered = require('fs').existsSync(statePath)
133
+ ? JSON.parse(require('fs').readFileSync(statePath, 'utf-8'))
134
+ : {};
135
+ const feature = featureRaw || remembered.lastFeature;
136
+ if (!feature) {
137
+ return 'Não identifiquei a feature para refinamento. Ex: "refinar dashboard financeiro"';
138
+ }
139
+ const featureSlug = feature.replace(/\s+/g, '-').toLowerCase();
140
+ const draftPath = require('path').join(process.cwd(), '.reversa', 'ui-drafts', `${featureSlug}.md`);
141
+ if (!require('fs').existsSync(draftPath)) {
142
+ return `Draft não encontrado para "${feature}". Gere primeiro com: openlife ask "gerar ui para ${feature}"`;
143
+ }
144
+ const current = require('fs').readFileSync(draftPath, 'utf-8');
145
+ const patchText = [
146
+ '',
147
+ '## Conversational Refinement',
148
+ `- User request: ${text}`,
149
+ '- Applied: stronger hierarchy, denser operator actions, clearer error recovery copy, stricter token invariants.',
150
+ '- Next: validate with stakeholders and promote to implementation tasks.'
151
+ ].join('\n');
152
+ require('fs').writeFileSync(draftPath, `${current}${patchText}\n`, 'utf-8');
153
+ return JSON.stringify({ action: 'aiobuilder.refine-ui', feature, output: { type: 'text-draft', path: draftPath } }, null, 2);
154
+ }
155
+ // Gerar UI por conversa com consistency-check implícito
156
+ if (wantsGenerateUi && !lower.includes('aiobuilder mode')) {
157
+ const featureMatch = text.match(/(?:para|for)\s+([a-zA-Z0-9._\-\s]+)/i);
158
+ const featureName = (featureMatch?.[1] || 'feature').trim();
159
+ const state = reversa.resume();
160
+ const mode = state.mode || 'default';
161
+ const profile = state.designProfileId || 'none';
162
+ const profiles = dm.listProfiles();
163
+ const activeProfile = profiles.find((p) => p.id === profile);
164
+ const effectiveSource = activeProfile?.source || 'user-conversation';
165
+ const effectiveChecksum = activeProfile?.checksum || `user-${Buffer.from(featureName).toString('hex').slice(0, 16)}`;
166
+ if (mode !== 'designmd') {
167
+ return 'Para engenharia de precisão visual, ative o modo designmd (ex.: "ative designmd").';
168
+ }
169
+ const outputDir = require('path').join(process.cwd(), '.reversa', 'ui-drafts');
170
+ require('fs').mkdirSync(outputDir, { recursive: true });
171
+ const featureSlug = featureName.replace(/\s+/g, '-').toLowerCase();
172
+ const outFile = require('path').join(outputDir, `${featureSlug}.md`);
173
+ const draft = [
174
+ `# UI Draft — ${featureName}`,
175
+ '',
176
+ `mode: ${mode}`,
177
+ `profile: ${profile}`,
178
+ `source: ${effectiveSource}`,
179
+ `checksum: ${effectiveChecksum}`,
180
+ '',
181
+ '## Generation Log',
182
+ '- Step 1/4: Loaded active DESIGN.md profile context',
183
+ '- Step 2/4: Applied precision and consistency rules',
184
+ '- Step 3/4: Incorporated final-contracts constraints',
185
+ '- Step 4/4: Produced textual UI draft',
186
+ '',
187
+ '## Initial Textual Design Output',
188
+ `- Feature: ${featureName}`,
189
+ '- Layout: premium enterprise shell with operational sidebar and command bar',
190
+ '- Core blocks: KPI row, status cards, teams matrix, skill networks table',
191
+ '- Interaction model: granular refresh, retry per panel, safe error boundaries',
192
+ '- Visual governance: no token drift from active design profile',
193
+ ].join('\n');
194
+ require('fs').writeFileSync(outFile, draft, 'utf-8');
195
+ const conversationStatePath = require('path').join(process.cwd(), '.reversa', 'ui-drafts', '_conversation-state.json');
196
+ const prevState = require('fs').existsSync(conversationStatePath)
197
+ ? JSON.parse(require('fs').readFileSync(conversationStatePath, 'utf-8'))
198
+ : {};
199
+ prevState.lastFeature = featureName;
200
+ prevState.lastFeatureSlug = featureSlug;
201
+ prevState.updatedAt = new Date().toISOString();
202
+ require('fs').writeFileSync(conversationStatePath, JSON.stringify(prevState, null, 2), 'utf-8');
203
+ return JSON.stringify({
204
+ action: 'aiobuilder.generate-ui',
205
+ featureName,
206
+ strictContracts: true,
207
+ consistencyCheck: true,
208
+ output: { type: 'text-draft', path: outFile },
209
+ context: {
210
+ designContext: {
211
+ mode,
212
+ profile,
213
+ source: effectiveSource,
214
+ checksum: effectiveChecksum,
215
+ },
216
+ precisionRules: [
217
+ 'Maintain token consistency across pages/components',
218
+ 'Preserve spacing/typography scale from DESIGN.md',
219
+ 'Reject visual drift that breaks profile identity',
220
+ ],
221
+ },
222
+ }, null, 2);
223
+ }
224
+ return null;
225
+ }
226
+ program
227
+ .name('openlife')
228
+ .description('OPEN-LIFE Córtex Orquestrador (Dual-Core)')
229
+ .version('1.0.0');
230
+ program
231
+ .command('install')
232
+ .description('Onboarding unificado: instala CLI ou Agente Autônomo')
233
+ .option('--mode <mode>', 'cli | autonomous')
234
+ .option('--resume', 'retoma estado salvo do setup')
235
+ .option('--host <host>', 'host principal de execução', 'claude-code')
236
+ .option('--skip-doctor', 'pula check automático do doctor')
237
+ .option('--models <chain>', 'ordem de modelos, separado por vírgula')
238
+ .option('--model-1 <provider/model>', 'modelo primário (opcional no setup guiado)')
239
+ .option('--model-2 <provider/model>', 'fallback 1 (opcional)')
240
+ .option('--model-3 <provider/model>', 'fallback 2 (opcional)')
241
+ .option('--telegram-token <token>', 'token do bot do Telegram (opcional)')
242
+ .option('--telegram-chat-id <chatId>', 'chat id padrão do Telegram (opcional)')
243
+ .option('--default-agent-name <name>', 'renomeia agente padrão (modo autônomo)')
244
+ .option('--additional-agent-name <name>', 'cria agente adicional (modo autônomo)')
245
+ .option('--headless', 'executa sem interação usando flags/arquivo')
246
+ .option('--from-file <path>', 'carrega configuração JSON de setup')
247
+ .action((options) => {
248
+ const fileCfg = options.fromFile ? (0, InstallHeadless_1.loadHeadlessConfig)(options.fromFile) : {};
249
+ // Explicit pick avoids smuggling models (string[] from file, string from CLI)
250
+ // through the same field — its only consumer reads options.models directly.
251
+ const effective = {
252
+ mode: options.mode || fileCfg.mode,
253
+ host: options.host || fileCfg.host,
254
+ telegramToken: options.telegramToken || fileCfg.telegramToken,
255
+ telegramChatId: options.telegramChatId || fileCfg.telegramChatId,
256
+ defaultAgentName: options.defaultAgentName || fileCfg.defaultAgentName,
257
+ additionalAgentName: options.additionalAgentName || fileCfg.additionalAgentName,
258
+ model1: options.model1,
259
+ model2: options.model2,
260
+ model3: options.model3,
261
+ };
262
+ const guidedModels = [effective.model1, effective.model2, effective.model3].filter((m) => !!m);
263
+ if (guidedModels.length > 0 && !options.model1) {
264
+ console.error('❌ Setup guiado inválido: --model-1 é obrigatório quando usar --model-2+');
265
+ process.exitCode = 1;
266
+ return;
267
+ }
268
+ const modelOrder = guidedModels.length > 0
269
+ ? guidedModels
270
+ : (typeof options.models === 'string' ? options.models.split(',').map((m) => m.trim()).filter(Boolean) : undefined);
271
+ const wizard = new InstallWizard_1.HeadlessInstallWizard();
272
+ const lines = wizard.run({
273
+ mode: effective.mode,
274
+ resume: !!options.resume,
275
+ host: effective.host || 'claude-code',
276
+ skipDoctor: !!options.skipDoctor,
277
+ telegramToken: effective.telegramToken,
278
+ telegramChatId: effective.telegramChatId,
279
+ defaultAgentName: effective.defaultAgentName,
280
+ additionalAgentName: effective.additionalAgentName,
281
+ modelOrder
282
+ });
283
+ for (const line of lines)
284
+ console.log(line);
285
+ });
286
+ // ============================================================================
287
+ // INIT — Interactive install wizard (Story 3.5)
288
+ // ============================================================================
289
+ program.command('init')
290
+ .description('Interactive install wizard — guided setup for OpenLife into the chosen host CLI')
291
+ .action(async () => {
292
+ // Lazy require preserves the lazy-import contract (`src/index.ts:11-13`).
293
+ const { InstallWizard, ReadlineAnswerProvider } = require('./cli/InstallWizard');
294
+ const { InstallFlow } = require('./cli/InstallFlow');
295
+ const provider = new ReadlineAnswerProvider();
296
+ const wizard = new InstallWizard(process.cwd(), provider);
297
+ try {
298
+ const result = await wizard.run();
299
+ if (!result.ok) {
300
+ console.log(JSON.stringify(result, null, 2));
301
+ process.exitCode = result.reason === 'user_aborted' ? 0 : 1;
302
+ return;
303
+ }
304
+ const flow = new InstallFlow();
305
+ const installResult = flow.run(result.options);
306
+ for (const line of flow.renderSummary(installResult))
307
+ console.log(line);
308
+ if (result.warnings && result.warnings.length) {
309
+ console.log('');
310
+ console.log('⚠️ Warnings:');
311
+ for (const w of result.warnings)
312
+ console.log(` - ${w}`);
313
+ }
314
+ }
315
+ finally {
316
+ // Release the readline interface so the CLI exits cleanly.
317
+ provider.close?.();
318
+ }
319
+ });
320
+ // ============================================================================
321
+ // AUTENTICAÇÃO (AUTH)
322
+ // ============================================================================
323
+ const authCmd = program.command('auth').description('Gerencia a autenticação nativa sem uso de API Keys');
324
+ authCmd.command('gemini')
325
+ .description('Realiza o login OAuth via Google para o Gemini CLI')
326
+ .action(async () => {
327
+ console.log("🔐 Iniciando autenticação nativa do Gemini (Google OAuth)...");
328
+ try {
329
+ await exec('gemini auth');
330
+ console.log("✅ Autenticação Gemini concluída com sucesso! Modelos 3.0+ liberados.");
331
+ }
332
+ catch {
333
+ console.log("ℹ️ Autenticação manual necessária. Rode no seu terminal: gemini auth");
334
+ }
335
+ });
336
+ authCmd.command('openai')
337
+ .alias('codex')
338
+ .description('Realiza o login OAuth para o OpenAI Codex CLI (GPT-5.4) e valida automaticamente')
339
+ .action(async () => {
340
+ console.log("🔐 Iniciando autenticação nativa da OpenAI (ChatGPT OAuth)...");
341
+ console.log("Se o navegador não abrir, copie o link exibido abaixo e conclua o login.");
342
+ try {
343
+ const { stdout } = await exec('codex login status');
344
+ if (stdout.toLowerCase().includes('logged in')) {
345
+ console.log(`✅ ${stdout.trim()}`);
346
+ return;
347
+ }
348
+ }
349
+ catch { }
350
+ try {
351
+ // codex login normalmente fica aguardando callback local; usamos timeout curto só para capturar URL
352
+ await exec('codex login', { timeout: 7000 });
353
+ }
354
+ catch (e) {
355
+ const out = `${errStdout(e)}\n${errStderr(e)}`;
356
+ const m = out.match(/https:\/\/auth\.openai\.com\/oauth\/authorize\S+/);
357
+ if (m?.[0]) {
358
+ console.log(`🌐 Link de autenticação: ${m[0]}`);
359
+ console.log('Após concluir no navegador, este comando pode ser rodado novamente para validar status.');
360
+ }
361
+ else {
362
+ console.log('ℹ️ Não consegui capturar o link automático. Rode: codex login --device-auth');
363
+ }
364
+ }
365
+ try {
366
+ const { stdout } = await exec('codex login status');
367
+ if (stdout.toLowerCase().includes('logged in')) {
368
+ console.log(`✅ ${stdout.trim()}`);
369
+ }
370
+ else {
371
+ console.log(`⚠️ Status atual: ${stdout.trim()}`);
372
+ }
373
+ }
374
+ catch {
375
+ console.log('⚠️ Não foi possível validar status agora. Rode: codex login status');
376
+ }
377
+ });
378
+ // ============================================================================
379
+ // GESTÃO DE MODELOS E FALLBACKS
380
+ // ============================================================================
381
+ const modelsCmd = program.command('models').description('Gestão do ciclo de vida e roteamento de Modelos Inteligentes');
382
+ modelsCmd.command('status')
383
+ .description('Exibe o modelo primário e a cadeia de fallbacks atual')
384
+ .action(() => {
385
+ const manager = new ModelManager_1.ModelManager();
386
+ const config = manager.getModelConfig();
387
+ console.log(`\n🧠 OPEN-LIFE: Status de Modelos Cognitivos`);
388
+ console.log(`=================================================`);
389
+ console.log(`⭐ Primário: ${config.primary.raw}`);
390
+ console.log(`\n🛡️ Cadeia de Fallbacks (Ordem de Execução de Failover):`);
391
+ if (config.fallbacks.length === 0) {
392
+ console.log(` [Nenhum fallback configurado. Risco de falha única!]`);
393
+ }
394
+ else {
395
+ config.fallbacks.forEach((f, i) => {
396
+ console.log(` ${i + 1}. ${f.raw}`);
397
+ });
398
+ }
399
+ console.log(`=================================================\n`);
400
+ });
401
+ modelsCmd.command('set <provider/model>')
402
+ .description('Define o modelo cognitivo padrão (ex: gemini-cli/gemini-3.1-pro-preview)')
403
+ .action((modelString) => {
404
+ try {
405
+ const manager = new ModelManager_1.ModelManager();
406
+ const config = manager.getModelConfig();
407
+ const newPrimary = manager.parseModelString(modelString);
408
+ config.primary = newPrimary;
409
+ manager.saveConfig(config);
410
+ console.log(`✅ Modelo primário atualizado com sucesso para: ${newPrimary.raw}`);
411
+ }
412
+ catch (e) {
413
+ console.error(`❌ Erro: ${errMsg(e)}`);
414
+ }
415
+ });
416
+ const fallbacksCmd = modelsCmd.command('fallbacks').description('Gerencia a cadeia de tolerância a falhas (Fallbacks)');
417
+ fallbacksCmd.command('list')
418
+ .action(() => {
419
+ const manager = new ModelManager_1.ModelManager();
420
+ const config = manager.getModelConfig();
421
+ console.log(`\n🛡️ Cadeia de Fallbacks Atual:`);
422
+ config.fallbacks.forEach((f, i) => console.log(` ${i + 1}. ${f.raw}`));
423
+ console.log();
424
+ });
425
+ fallbacksCmd.command('add <provider/model>')
426
+ .action((modelString) => {
427
+ try {
428
+ const manager = new ModelManager_1.ModelManager();
429
+ const config = manager.getModelConfig();
430
+ const newFallback = manager.parseModelString(modelString);
431
+ if (config.fallbacks.some(f => f.raw === newFallback.raw)) {
432
+ console.log(`⚠️ O modelo ${newFallback.raw} já está na lista de fallbacks.`);
433
+ return;
434
+ }
435
+ config.fallbacks.push(newFallback);
436
+ manager.saveConfig(config);
437
+ console.log(`✅ Fallback adicionado. Nova Ordem de Segurança:`);
438
+ config.fallbacks.forEach((f, i) => console.log(` ${i + 1}. ${f.raw}`));
439
+ }
440
+ catch (e) {
441
+ console.error(`❌ Erro: ${errMsg(e)}`);
442
+ }
443
+ });
444
+ fallbacksCmd.command('remove <provider/model>')
445
+ .action((modelString) => {
446
+ try {
447
+ const manager = new ModelManager_1.ModelManager();
448
+ const config = manager.getModelConfig();
449
+ const parsed = manager.parseModelString(modelString);
450
+ config.fallbacks = config.fallbacks.filter(f => f.raw !== parsed.raw);
451
+ manager.saveConfig(config);
452
+ console.log(`✅ Fallback ${parsed.raw} removido do sistema de segurança.`);
453
+ }
454
+ catch (e) {
455
+ console.error(`❌ Erro: ${errMsg(e)}`);
456
+ }
457
+ });
458
+ fallbacksCmd.command('clear')
459
+ .action(() => {
460
+ const manager = new ModelManager_1.ModelManager();
461
+ const config = manager.getModelConfig();
462
+ config.fallbacks = [];
463
+ manager.saveConfig(config);
464
+ console.log(`✅ Cadeia de fallbacks esvaziada.`);
465
+ });
466
+ // ============================================================================
467
+ // CORE ORQUESTRADOR
468
+ // ============================================================================
469
+ program
470
+ .command('ask <mensagem...>')
471
+ .description('Envia uma intenção direta ao Gatekeeper via Terminal')
472
+ .option('--mode <mode>', 'task | service')
473
+ .action(async (mensagemArgs, options) => {
474
+ const mensagem = mensagemArgs.join(' ');
475
+ console.log(`\n> Você: "${mensagem}"\n`);
476
+ const designHandled = tryHandleDesignConversation(mensagem);
477
+ if (designHandled) {
478
+ console.log(`\n> OPEN-LIFE: ${designHandled}\n`);
479
+ process.exit(0);
480
+ }
481
+ const timeoutMs = Number(process.env.OPENLIFE_ASK_TIMEOUT_MS || 90000);
482
+ const classifier = new IntentClassifier_1.IntentClassifier();
483
+ const { Gatekeeper } = require('./orchestrator/Gatekeeper');
484
+ const gatekeeper = new Gatekeeper();
485
+ const run = async () => {
486
+ const task = await classifier.classify(mensagem);
487
+ const modeRaw = (options?.mode || '').toString().toLowerCase();
488
+ const mode = modeRaw === 'service' ? 'service' : modeRaw === 'task' ? 'task' : undefined;
489
+ return gatekeeper.routeTask(task, mensagem, 'default', mode ? { mode } : undefined);
490
+ };
491
+ try {
492
+ const resposta = await Promise.race([
493
+ run(),
494
+ new Promise((_, reject) => setTimeout(() => reject(new Error(`Timeout após ${timeoutMs}ms no comando ask`)), timeoutMs))
495
+ ]);
496
+ console.log(`\n> OPEN-LIFE: ${resposta}\n`);
497
+ process.exit(0);
498
+ }
499
+ catch (err) {
500
+ console.error(`\n> OPEN-LIFE [erro]: ${errMsg(err)}\n`);
501
+ process.exit(1);
502
+ }
503
+ });
504
+ program
505
+ .command('phase1-check')
506
+ .description('Roda os checks mínimos da Fase 1 para validar o runtime principal')
507
+ .action(async () => {
508
+ let exitCode = 0;
509
+ const MASTER_TIMEOUT_MS = Number(process.env.OPENLIFE_PHASE1_TIMEOUT_MS || 30000);
510
+ const masterTimeout = new Promise((_, reject) => setTimeout(() => reject(new Error(`PHASE1_CHECK_TIMEOUT after ${MASTER_TIMEOUT_MS}ms — provavelmente Gatekeeper/Brain/Gateway constructor ou check travou esperando LLM/recurso externo`)), MASTER_TIMEOUT_MS));
511
+ try {
512
+ // Wrap both construction and run inside race — TestHarness constructor instantiates
513
+ // Brain/Gatekeeper/Gateway/etc which may block synchronously
514
+ const results = await Promise.race([
515
+ (async () => {
516
+ const { TestHarness } = require('./orchestrator/TestHarness');
517
+ const harness = new TestHarness();
518
+ return await harness.runPhase1Checks();
519
+ })(),
520
+ masterTimeout
521
+ ]);
522
+ console.log('\n🔎 OPEN-LIFE Phase 1 Check');
523
+ console.log('=================================================');
524
+ for (const result of results) {
525
+ console.log(`${result.ok ? '✅' : '❌'} ${result.name}: ${result.detail}`);
526
+ }
527
+ console.log('=================================================\n');
528
+ const failed = results.filter((r) => !r.ok);
529
+ if (failed.length > 0)
530
+ exitCode = 1;
531
+ }
532
+ catch (error) {
533
+ console.error('phase1-check failed:', errMsg(error));
534
+ exitCode = 1;
535
+ }
536
+ // Force deterministic exit — TestHarness instantiates Gateway → Telegraf, leaving
537
+ // event-loop handles open. Same root-cause pattern as Story 1.2 `ask` exit fix.
538
+ // Override timeout via OPENLIFE_PHASE1_TIMEOUT_MS (default 30s).
539
+ process.exit(exitCode);
540
+ });
541
+ const systemCmd = program.command('system').description('Instalação, bootstrap e status do OpenLife');
542
+ systemCmd.command('install')
543
+ .description('Fluxo guiado de instalação (rápido e intuitivo)')
544
+ .option('--profile <profile>', 'framework | autonomous', 'framework')
545
+ .option('--host <host>', 'host principal de execução', 'claude-code')
546
+ .option('--skip-doctor', 'pula check automático do doctor')
547
+ .option('--models <chain>', 'ordem de modelos, separado por vírgula. Ex: openai-cli/gpt-5.4,gemini-cli/gemini-3.1-pro-preview')
548
+ .option('--model-1 <provider/model>', 'modelo primário (obrigatório para setup guiado)')
549
+ .option('--model-2 <provider/model>', 'fallback 1 (opcional)')
550
+ .option('--model-3 <provider/model>', 'fallback 2 (opcional)')
551
+ .option('--model-4 <provider/model>', 'fallback 3 (opcional)')
552
+ .action((options) => {
553
+ console.log((0, InstallBanner_1.installationBanner)());
554
+ const installFlow = new InstallFlow_1.InstallFlow();
555
+ const profile = (options.profile || 'framework').toLowerCase();
556
+ const safeProfile = profile === 'autonomous' ? 'autonomous' : 'framework';
557
+ const guidedModels = [options.model1, options.model2, options.model3, options.model4].filter((m) => !!m);
558
+ if (guidedModels.length > 0 && !options.model1) {
559
+ console.error('❌ Setup guiado inválido: --model-1 é obrigatório quando usar --model-2+');
560
+ process.exitCode = 1;
561
+ return;
562
+ }
563
+ const modelOrder = guidedModels.length > 0
564
+ ? guidedModels
565
+ : (typeof options.models === 'string' ? options.models.split(',').map((m) => m.trim()).filter(Boolean) : undefined);
566
+ const result = installFlow.run({
567
+ profile: safeProfile,
568
+ host: options.host || 'claude-code',
569
+ skipDoctor: !!options.skipDoctor,
570
+ modelOrder
571
+ });
572
+ for (const line of installFlow.renderSummary(result)) {
573
+ console.log(line);
574
+ }
575
+ });
576
+ systemCmd.command('setup')
577
+ .description('Alias de system install para onboarding completo')
578
+ .option('--profile <profile>', 'framework | autonomous', 'framework')
579
+ .option('--host <host>', 'host principal de execução', 'claude-code')
580
+ .option('--skip-doctor', 'pula check automático do doctor')
581
+ .option('--models <chain>', 'ordem de modelos, separado por vírgula. Ex: openai-cli/gpt-5.4,gemini-cli/gemini-3.1-pro-preview')
582
+ .option('--model-1 <provider/model>', 'modelo primário (obrigatório para setup guiado)')
583
+ .option('--model-2 <provider/model>', 'fallback 1 (opcional)')
584
+ .option('--model-3 <provider/model>', 'fallback 2 (opcional)')
585
+ .option('--model-4 <provider/model>', 'fallback 3 (opcional)')
586
+ .action((options) => {
587
+ console.log((0, InstallBanner_1.installationBanner)());
588
+ const installFlow = new InstallFlow_1.InstallFlow();
589
+ const profile = (options.profile || 'framework').toLowerCase();
590
+ const safeProfile = profile === 'autonomous' ? 'autonomous' : 'framework';
591
+ const guidedModels = [options.model1, options.model2, options.model3, options.model4].filter((m) => !!m);
592
+ if (guidedModels.length > 0 && !options.model1) {
593
+ console.error('❌ Setup guiado inválido: --model-1 é obrigatório quando usar --model-2+');
594
+ process.exitCode = 1;
595
+ return;
596
+ }
597
+ const modelOrder = guidedModels.length > 0
598
+ ? guidedModels
599
+ : (typeof options.models === 'string' ? options.models.split(',').map((m) => m.trim()).filter(Boolean) : undefined);
600
+ const result = installFlow.run({
601
+ profile: safeProfile,
602
+ host: options.host || 'claude-code',
603
+ skipDoctor: !!options.skipDoctor,
604
+ modelOrder
605
+ });
606
+ for (const line of installFlow.renderSummary(result)) {
607
+ console.log(line);
608
+ }
609
+ });
610
+ systemCmd.command('uninstall')
611
+ .description('Remove OpenLife artifacts installed into the host CLI (reversible install). Does NOT touch .openlife/ state.')
612
+ .option('--host <host>', 'host to uninstall from (claude-code | gemini-cli | codex)')
613
+ .action((options) => {
614
+ const { HostInstaller, HostNotYetSupportedError } = require('./cli/HostInstaller');
615
+ const { validateHost, detectHostFromEnv, DEFAULT_HOST } = require('./cli/InstallFlow');
616
+ const host = options.host ? validateHost(options.host) : (detectHostFromEnv() || DEFAULT_HOST);
617
+ try {
618
+ const result = new HostInstaller().uninstall(host, { targetRoot: process.cwd() });
619
+ console.log(JSON.stringify({ ok: true, ...result }, null, 2));
620
+ }
621
+ catch (err) {
622
+ if (err instanceof HostNotYetSupportedError) {
623
+ const hostErr = err;
624
+ console.log(JSON.stringify({ ok: false, error: hostErr.code, host, message: hostErr.message }, null, 2));
625
+ process.exitCode = 1;
626
+ }
627
+ else {
628
+ console.error(JSON.stringify({ ok: false, error: 'uninstall_failed', message: errMsg(err) }, null, 2));
629
+ process.exitCode = 1;
630
+ }
631
+ }
632
+ });
633
+ systemCmd.command('status')
634
+ .description('Mostra status básico da instalação do sistema')
635
+ .action(() => {
636
+ const installer = new SystemInstaller_1.SystemInstaller();
637
+ console.log(JSON.stringify(installer.status(), null, 2));
638
+ });
639
+ systemCmd.command('doctor')
640
+ .description('Roda checkup básico do sistema e runtime local')
641
+ .action(() => {
642
+ const doctor = new SystemDoctor_1.SystemDoctor();
643
+ const results = doctor.run();
644
+ for (const result of results) {
645
+ console.log(`${result.ok ? '✅' : '❌'} ${result.name}: ${result.detail}`);
646
+ }
647
+ });
648
+ systemCmd.command('init-project')
649
+ .description('Cria o arquivo base de contexto do projeto OpenLife')
650
+ .action(() => {
651
+ const installer = new SystemInstaller_1.SystemInstaller();
652
+ const file = installer.initProject();
653
+ console.log(`✅ Projeto inicializado em: ${file}`);
654
+ });
655
+ systemCmd.command('doctor-world')
656
+ .description('Roda diagnóstico world-class do sistema')
657
+ .action(() => {
658
+ const world = new WorldClassCommands_1.WorldClassCommands();
659
+ for (const line of world.doctorWorld())
660
+ console.log(line);
661
+ });
662
+ systemCmd.command('bootstrap-world')
663
+ .description('Prepara artefatos de bootstrap world-class')
664
+ .action(() => {
665
+ const world = new WorldClassCommands_1.WorldClassCommands();
666
+ console.log(`✅ Bootstrap world-class: ${world.bootstrapWorld()}`);
667
+ });
668
+ const memoryCmd = program.command('memory').description('Operações de memória do OpenLife');
669
+ memoryCmd.command('status').action(() => {
670
+ const world = new WorldClassCommands_1.WorldClassCommands();
671
+ console.log(JSON.stringify(world.memoryStatus(), null, 2));
672
+ });
673
+ memoryCmd.command('provider-list').action(() => {
674
+ const world = new WorldClassCommands_1.WorldClassCommands();
675
+ console.log(world.memoryProviderList().join('\n'));
676
+ });
677
+ memoryCmd.command('provider-use <name>').action((name) => {
678
+ const world = new WorldClassCommands_1.WorldClassCommands();
679
+ console.log(`✅ Provider ativo salvo em: ${world.memoryProviderUse(name)}`);
680
+ });
681
+ memoryCmd.command('test <name>').action((name) => {
682
+ const world = new WorldClassCommands_1.WorldClassCommands();
683
+ console.log(world.memoryTest(name));
684
+ });
685
+ memoryCmd.command('search <query...>').description('Pesquisa memória por texto e retorna contexto').action(async (queryArgs) => {
686
+ const { OmniMemory } = await Promise.resolve().then(() => __importStar(require('./memory/OmniMemory')));
687
+ const memory = new OmniMemory();
688
+ const query = queryArgs.join(' ');
689
+ const result = await memory.search(query);
690
+ console.log(result);
691
+ });
692
+ const swarmCmd = program.command('swarm').description('Operações de swarm e consenso');
693
+ swarmCmd.command('run')
694
+ .argument('[goal...]', 'objetivo do swarm', [])
695
+ .action((goalArgs) => {
696
+ const goal = Array.isArray(goalArgs) ? goalArgs.join(' ') : String(goalArgs || '');
697
+ const now = new Date().toISOString();
698
+ const artifact = {
699
+ type: 'openlife.swarm.run',
700
+ status: 'completed',
701
+ goal: goal || 'diagnóstico geral',
702
+ pipeline: ['planner', 'executor', 'reviewer', 'synthesizer'],
703
+ stages: [
704
+ { role: 'planner', status: 'completed', summary: 'Plano determinístico criado para execução local.' },
705
+ { role: 'executor', status: 'completed', summary: 'Executor local registrou artefato de swarm.' },
706
+ { role: 'reviewer', status: 'completed', summary: 'Review mínimo aprovado; sem ações destrutivas.' },
707
+ { role: 'synthesizer', status: 'completed', summary: 'Síntese final pronta.' }
708
+ ],
709
+ createdAt: now
710
+ };
711
+ const out = path.join(process.cwd(), '.openlife', 'swarm-last-run.json');
712
+ fs.mkdirSync(path.dirname(out), { recursive: true });
713
+ fs.writeFileSync(out, JSON.stringify(artifact, null, 2), 'utf-8');
714
+ console.log(JSON.stringify({ ok: true, artifactPath: out, ...artifact }, null, 2));
715
+ });
716
+ swarmCmd.command('consensus-test').action(() => {
717
+ const world = new WorldClassCommands_1.WorldClassCommands();
718
+ console.log(world.swarmConsensusTest());
719
+ });
720
+ const governanceCmd = program.command('governance').description('Operações de governança e risco');
721
+ governanceCmd.command('status').action(() => {
722
+ const world = new WorldClassCommands_1.WorldClassCommands();
723
+ console.log(JSON.stringify(world.governanceStatus(), null, 2));
724
+ });
725
+ governanceCmd.command('audit').action(() => {
726
+ const world = new WorldClassCommands_1.WorldClassCommands();
727
+ console.log(world.governanceAudit());
728
+ });
729
+ governanceCmd.command('risk-check <goal>').action((goal) => {
730
+ const world = new WorldClassCommands_1.WorldClassCommands();
731
+ console.log(JSON.stringify(world.governanceRiskCheck(goal), null, 2));
732
+ });
733
+ governanceCmd.command('consent <scope> <userId>').action((scope, userId) => {
734
+ const world = new WorldClassCommands_1.WorldClassCommands();
735
+ console.log(world.governanceConsentApprove(userId, scope));
736
+ });
737
+ // Story 14.2 (v1.5) — Governance scope ledger (SHA-chained, tamper-evident).
738
+ const governanceLedgerCmd = governanceCmd.command('ledger').description('Inspeciona o ledger SHA-chained de decisões de governança (Story 14.2, v1.5)');
739
+ governanceLedgerCmd.command('show')
740
+ .description('Imprime todas as entradas do ledger em JSON')
741
+ .option('--tail <n>', 'imprime apenas as últimas N entradas')
742
+ .action((options) => {
743
+ const { GovernanceScopeLedger } = require('./orchestrator/GovernanceScopeLedger');
744
+ const entries = new GovernanceScopeLedger().read();
745
+ const tail = options.tail ? Number(options.tail) : undefined;
746
+ const out = tail && tail > 0 ? entries.slice(-tail) : entries;
747
+ console.log(JSON.stringify({ ok: true, count: out.length, entries: out }, null, 2));
748
+ });
749
+ governanceLedgerCmd.command('verify')
750
+ .description('Verifica a integridade da cadeia SHA do ledger')
751
+ .action(() => {
752
+ const { GovernanceScopeLedger } = require('./orchestrator/GovernanceScopeLedger');
753
+ const result = new GovernanceScopeLedger().verify();
754
+ console.log(JSON.stringify(result, null, 2));
755
+ if (!result.ok)
756
+ process.exitCode = 1;
757
+ });
758
+ const governancePolicyCmd = governanceCmd.command('policy').description('Inspeção da política de governança ativa');
759
+ governancePolicyCmd.command('show').description('Exibe o conteúdo de governance-policy.json').action(() => {
760
+ const fs = require('fs');
761
+ const path = require('path');
762
+ const candidates = [
763
+ path.join(process.cwd(), 'governance-policy.json'),
764
+ path.join(process.cwd(), '.catalog', 'governance-policy.json'),
765
+ path.join(process.cwd(), '.openlife', 'governance-policy.json')
766
+ ];
767
+ const found = candidates.find((p) => fs.existsSync(p));
768
+ if (!found) {
769
+ console.log(JSON.stringify({ ok: false, error: 'policy_not_found', searched: candidates }, null, 2));
770
+ process.exitCode = 1;
771
+ return;
772
+ }
773
+ try {
774
+ const raw = fs.readFileSync(found, 'utf-8');
775
+ const parsed = JSON.parse(raw);
776
+ console.log(JSON.stringify({ ok: true, source: found, policy: parsed }, null, 2));
777
+ }
778
+ catch (err) {
779
+ console.log(JSON.stringify({ ok: false, source: found, error: 'parse_error', message: errMsg(err) }, null, 2));
780
+ process.exitCode = 1;
781
+ }
782
+ });
783
+ const benchmarkCmd = program.command('benchmark').description('Operações de benchmark do OpenLife');
784
+ benchmarkCmd.command('run').action(() => {
785
+ const world = new WorldClassCommands_1.WorldClassCommands();
786
+ console.log(`✅ Benchmark run salvo em: ${world.benchmarkRun()}`);
787
+ });
788
+ benchmarkCmd.command('compare').action(() => {
789
+ const world = new WorldClassCommands_1.WorldClassCommands();
790
+ console.log(world.benchmarkCompare());
791
+ });
792
+ benchmarkCmd.command('report').action(() => {
793
+ const world = new WorldClassCommands_1.WorldClassCommands();
794
+ console.log(world.benchmarkReport());
795
+ });
796
+ const agentsCmd = program.command('agents').description('Operações de agentes');
797
+ agentsCmd.command('list')
798
+ .description('lista agentes reais carregados do registry')
799
+ .action(() => {
800
+ const world = new WorldClassCommands_1.WorldClassCommands();
801
+ console.log(JSON.stringify(world.agentsListReal(), null, 2));
802
+ });
803
+ agentsCmd.command('create <id>')
804
+ .option('--role <role>', 'papel do agente', 'specialist')
805
+ .option('--notes <notes>', 'notas adicionais', '')
806
+ .action((id, options) => {
807
+ const fs = require('fs');
808
+ const path = require('path');
809
+ const base = path.join(process.cwd(), '.catalog', 'agents', id);
810
+ fs.mkdirSync(base, { recursive: true });
811
+ const content = `# ${id}\n\nrole: ${options.role || 'specialist'}\nstatus: draft\ncreatedAt: ${new Date().toISOString()}\n\n## Notes\n${options.notes || 'n/a'}\n`;
812
+ fs.writeFileSync(path.join(base, 'AGENT.md'), content, 'utf-8');
813
+ console.log(JSON.stringify({ ok: true, path: path.join(base, 'AGENT.md') }, null, 2));
814
+ });
815
+ agentsCmd.command('show <id>')
816
+ .description('Exibe metadata de um agente do .catalog/agents/<id>')
817
+ .action((id) => {
818
+ const fs = require('fs');
819
+ const path = require('path');
820
+ const base = path.join(process.cwd(), '.catalog', 'agents', id);
821
+ const agentFile = path.join(base, 'AGENT.md');
822
+ if (!fs.existsSync(agentFile)) {
823
+ console.log(JSON.stringify({ ok: false, error: 'agent_not_found', id, expected: agentFile }, null, 2));
824
+ process.exitCode = 1;
825
+ return;
826
+ }
827
+ const raw = fs.readFileSync(agentFile, 'utf-8');
828
+ const meta = {};
829
+ const fmMatch = raw.match(/^---\n([\s\S]*?)\n---/);
830
+ if (fmMatch) {
831
+ for (const line of fmMatch[1].split('\n')) {
832
+ const m = line.match(/^([a-zA-Z0-9_-]+):\s*(.+)$/);
833
+ if (m)
834
+ meta[m[1]] = m[2].trim();
835
+ }
836
+ }
837
+ for (const key of ['role', 'status', 'createdAt']) {
838
+ const m = raw.match(new RegExp(`^${key}:\\s*(.+)$`, 'm'));
839
+ if (m && !meta[key])
840
+ meta[key] = m[1].trim();
841
+ }
842
+ const capabilities = [];
843
+ const capSection = raw.match(/##\s*Capabilities?\n([\s\S]*?)(?:\n##|$)/i);
844
+ if (capSection) {
845
+ for (const line of capSection[1].split('\n')) {
846
+ const m = line.match(/^[-*]\s+(.+)$/);
847
+ if (m)
848
+ capabilities.push(m[1].trim());
849
+ }
850
+ }
851
+ console.log(JSON.stringify({ ok: true, id, path: agentFile, metadata: meta, capabilities, sizeBytes: raw.length }, null, 2));
852
+ });
853
+ const squadsCmd = program.command('squads').description('Operações de squads');
854
+ squadsCmd.command('list')
855
+ .description('lista squads reais carregadas do registry')
856
+ .action(() => {
857
+ const world = new WorldClassCommands_1.WorldClassCommands();
858
+ console.log(JSON.stringify(world.squadsListReal(), null, 2));
859
+ });
860
+ squadsCmd.command('create <id>')
861
+ .option('--domain <domain>', 'domínio da squad', 'general')
862
+ .option('--notes <notes>', 'notas adicionais', '')
863
+ .action((id, options) => {
864
+ const fs = require('fs');
865
+ const path = require('path');
866
+ const base = path.join(process.cwd(), '.catalog', 'squads', id);
867
+ fs.mkdirSync(base, { recursive: true });
868
+ const content = `# ${id}\n\ndomain: ${options.domain || 'general'}\nstatus: draft\ncreatedAt: ${new Date().toISOString()}\n\n## Notes\n${options.notes || 'n/a'}\n`;
869
+ fs.writeFileSync(path.join(base, 'SQUAD.md'), content, 'utf-8');
870
+ console.log(JSON.stringify({ ok: true, path: path.join(base, 'SQUAD.md') }, null, 2));
871
+ });
872
+ const skillsCmd = program.command('skills').description('Operações de skills');
873
+ skillsCmd.command('list')
874
+ .description('lista skills reais carregadas do registry')
875
+ .action(() => {
876
+ const world = new WorldClassCommands_1.WorldClassCommands();
877
+ console.log(JSON.stringify(world.skillsListReal(), null, 2));
878
+ });
879
+ skillsCmd.command('create <id>')
880
+ .option('--notes <notes>', 'notas adicionais', '')
881
+ .action((id, options) => {
882
+ const fs = require('fs');
883
+ const path = require('path');
884
+ const base = path.join(process.cwd(), '.catalog', 'skills', id);
885
+ fs.mkdirSync(base, { recursive: true });
886
+ const content = `---\nname: ${id}\ndescription: draft skill\n---\n\n# ${id}\n\n## Notes\n${options.notes || 'n/a'}\n`;
887
+ fs.writeFileSync(path.join(base, 'SKILL.md'), content, 'utf-8');
888
+ console.log(JSON.stringify({ ok: true, path: path.join(base, 'SKILL.md') }, null, 2));
889
+ });
890
+ function readCatalogText(kind, canonicalFile) {
891
+ const root = path.join(process.cwd(), '.catalog', kind);
892
+ if (!fs.existsSync(root))
893
+ return [];
894
+ return fs.readdirSync(root)
895
+ .filter((id) => fs.existsSync(path.join(root, id, canonicalFile)))
896
+ .map((id) => {
897
+ const sourcePath = path.join(root, id, canonicalFile);
898
+ const raw = fs.readFileSync(sourcePath, 'utf-8');
899
+ return { id, score: 0, sourcePath, matched: [], raw };
900
+ });
901
+ }
902
+ function tokenizeForRoute(text) {
903
+ const normalized = text.toLowerCase().normalize('NFD').replace(/[\u0300-\u036f]/g, ' ');
904
+ const synonyms = {
905
+ landing: ['landing', 'page', 'pagina', 'frontend', 'design', 'copy'],
906
+ deploy: ['deploy', 'github', 'railway', 'vercel', 'cloudflare', 'hostinger'],
907
+ media: ['imagem', 'image', 'video', 'voz', 'voice', 'audio', 'tts', 'stt', 'gemini', 'openai'],
908
+ security: ['security', 'seguranca', 'cyber', 'audit', 'vulnerability', 'token', 'secret'],
909
+ data: ['data', 'dados', 'analytics', 'quality', 'sql', 'supabase'],
910
+ marketing: ['ads', 'copy', 'brand', 'sales', 'traffic', 'growth', 'marketing', 'hormozi'],
911
+ telegram: ['telegram', 'bot', 'gateway', 'webhook', 'polling'],
912
+ code: ['code', 'codigo', 'github', 'review', 'cli', 'typescript', 'node', 'codex']
913
+ };
914
+ const base = normalized.split(/[^a-z0-9]+/).filter((t) => t.length >= 3);
915
+ const out = new Set(base);
916
+ for (const token of base)
917
+ for (const extra of synonyms[token] || [])
918
+ out.add(extra);
919
+ return Array.from(out);
920
+ }
921
+ function rankCatalog(kind, canonicalFile, query, limit = 8) {
922
+ const terms = tokenizeForRoute(query);
923
+ return readCatalogText(kind, canonicalFile)
924
+ .map((item) => {
925
+ const raw = typeof item.raw === 'string' ? item.raw : '';
926
+ const hay = `${item.id}\\n${raw}`.toLowerCase().normalize('NFD').replace(/[\u0300-\u036f]/g, ' ');
927
+ const matched = terms.filter((t) => hay.includes(t));
928
+ const idBoost = terms.filter((t) => item.id.toLowerCase().includes(t)).length * 3;
929
+ const score = matched.length + idBoost;
930
+ return { id: item.id, score, sourcePath: item.sourcePath, matched: matched.slice(0, 12) };
931
+ })
932
+ .filter((x) => x.score > 0)
933
+ .sort((a, b) => b.score - a.score || a.id.localeCompare(b.id))
934
+ .slice(0, limit);
935
+ }
936
+ function slugifyCatalogId(input) {
937
+ return input.toLowerCase().normalize('NFD').replace(/[\u0300-\u036f]/g, '')
938
+ .replace(/[^a-z0-9]+/g, '-').replace(/^-+|-+$/g, '').slice(0, 72) || `auto-${Date.now()}`;
939
+ }
940
+ function writeAutoCatalog(kind, id, fileName, body) {
941
+ const dir = path.join(process.cwd(), '.catalog', kind, id);
942
+ fs.mkdirSync(dir, { recursive: true });
943
+ const out = path.join(dir, fileName);
944
+ if (!fs.existsSync(out))
945
+ fs.writeFileSync(out, body, 'utf-8');
946
+ return out;
947
+ }
948
+ function autoCreateForRoute(goal, intent, route, options) {
949
+ const prefix = slugifyCatalogId(options.createPrefix || intent || goal);
950
+ const created = [];
951
+ const topScore = (items) => items?.[0]?.score || 0;
952
+ const now = new Date().toISOString();
953
+ if (topScore(route.agents) < options.minScore) {
954
+ const id = slugifyCatalogId(`${prefix}-agent`);
955
+ const out = writeAutoCatalog('agents', id, 'AGENT.md', `---\nid: ${id}\nname: ${id}\nrole: specialist\nsource: aiobuilder-auto-route\nstatus: draft\ncreatedAt: ${now}\nintent: ${intent}\n---\n\n# ${id}\n\nAuto-criado pelo OpenLife porque o roteador não encontrou agente com cobertura suficiente.\n\n## Goal\n${goal}\n\n## Guardrails\n- Revisar antes de promoção para active.\n- Não usar segredos no arquivo.\n`);
956
+ created.push({ type: 'agent', id, path: out, reason: 'insufficient-agent-coverage' });
957
+ }
958
+ if (topScore(route.squads) < options.minScore) {
959
+ const id = slugifyCatalogId(`${prefix}-squad`);
960
+ const out = writeAutoCatalog('squads', id, 'SQUAD.md', `---\nid: ${id}\nname: ${id}\ndomain: ${intent}\nsource: aiobuilder-auto-route\nstatus: draft\ncreatedAt: ${now}\n---\n\n# ${id}\n\nAuto-criada pelo OpenLife porque o roteador não encontrou squad com cobertura suficiente.\n\n## Mission\n${goal}\n\n## Minimum artifacts\n- agente principal: ${prefix}-agent\n- índice de uso: este SQUAD.md\n- workflow inicial: revisar fluxo após primeira execução\n- espelho Obsidian: opcional/reference only, não runtime\n`);
961
+ created.push({ type: 'squad', id, path: out, reason: 'insufficient-squad-coverage' });
962
+ }
963
+ if (topScore(route.skills) < options.minScore) {
964
+ const id = slugifyCatalogId(`${prefix}-skill`);
965
+ const out = writeAutoCatalog('skills', id, 'SKILL.md', `---\nid: ${id}\nname: ${id}\nsource: aiobuilder-auto-route\nstatus: draft\ncreatedAt: ${now}\ndescription: Skill draft auto-criada para ${intent}\n---\n\n# ${id}\n\n<description>\nSkill draft auto-criada para cobrir lacuna operacional detectada no roteamento: ${goal}\n</description>\n\n## When to use\nUse quando a missão envolver: ${goal}\n\n## Procedure\n1. Validar contexto e riscos.\n2. Executar passos mínimos com ferramentas disponíveis.\n3. Registrar evidência e atualizar esta skill após uso real.\n\n## Guardrails\n- Não guardar segredos.\n- Promover de draft para active só após validação real.\n`);
966
+ created.push({ type: 'skill', id, path: out, reason: 'insufficient-skill-coverage' });
967
+ }
968
+ return created;
969
+ }
970
+ function classifyRouteIntent(goal) {
971
+ const t = goal.toLowerCase();
972
+ if (/telegram|bot|gateway|webhook/.test(t))
973
+ return 'telegram-runtime';
974
+ if (/landing|pagina|page|design|copy|ads|brand|sales|marketing/.test(t))
975
+ return 'product-growth';
976
+ if (/security|seguran|cyber|audit|token|secret/.test(t))
977
+ return 'security';
978
+ if (/data|dados|analytics|sql|supabase/.test(t))
979
+ return 'data';
980
+ if (/deploy|railway|github|vercel|cloudflare|code|codigo|review/.test(t))
981
+ return 'engineering';
982
+ return 'general';
983
+ }
984
+ const routeCmd = program.command('route').description('Roteamento inteligente por intenção para squads, agentes e skills');
985
+ routeCmd.command('intent <goal...>')
986
+ .description('recomenda squads/agentes/skills para uma missão')
987
+ .option('--limit <n>', 'limite por categoria', '8')
988
+ .option('--auto-create', 'cria drafts de agent/squad/skill quando houver lacuna de cobertura', false)
989
+ .option('--min-score <n>', 'score mínimo por categoria antes de auto-criar', '1')
990
+ .option('--create-prefix <prefix>', 'prefixo/id base para artefatos auto-criados')
991
+ .action((goalParts, options) => {
992
+ const goal = goalParts.join(' ');
993
+ const limit = Math.max(1, Number(options.limit || '8'));
994
+ const intent = classifyRouteIntent(goal);
995
+ const payload = {
996
+ type: 'openlife.route.intent',
997
+ ok: true,
998
+ goal,
999
+ intent,
1000
+ squads: rankCatalog('squads', 'SQUAD.md', goal, limit),
1001
+ agents: rankCatalog('agents', 'AGENT.md', goal, limit),
1002
+ skills: rankCatalog('skills', 'SKILL.md', goal, limit),
1003
+ generatedAt: new Date().toISOString()
1004
+ };
1005
+ if (options.autoCreate) {
1006
+ const minScore = Math.max(0, Number(options.minScore || '1'));
1007
+ payload.autoCreate = {
1008
+ enabled: true,
1009
+ minScore,
1010
+ created: autoCreateForRoute(goal, intent, payload, { minScore, createPrefix: options.createPrefix })
1011
+ };
1012
+ }
1013
+ else {
1014
+ payload.autoCreate = { enabled: false, created: [] };
1015
+ }
1016
+ console.log(JSON.stringify(payload, null, 2));
1017
+ });
1018
+ const mcpCmd = program.command('mcp').description('Operações de MCP');
1019
+ mcpCmd.command('create <id>')
1020
+ .option('--transport <transport>', 'stdio|http', 'stdio')
1021
+ .option('--entry <entry>', 'command/url de entrada', '')
1022
+ .action((id, options) => {
1023
+ const fs = require('fs');
1024
+ const path = require('path');
1025
+ const base = path.join(process.cwd(), '.catalog', 'mcps', id);
1026
+ fs.mkdirSync(base, { recursive: true });
1027
+ const content = {
1028
+ id,
1029
+ transport: options.transport || 'stdio',
1030
+ entry: options.entry || '',
1031
+ status: 'draft',
1032
+ createdAt: new Date().toISOString()
1033
+ };
1034
+ fs.writeFileSync(path.join(base, 'mcp.json'), JSON.stringify(content, null, 2), 'utf-8');
1035
+ console.log(JSON.stringify({ ok: true, path: path.join(base, 'mcp.json') }, null, 2));
1036
+ });
1037
+ const catalogCmd = program.command('catalog').description('Diagnóstico do catálogo runtime do OpenLife');
1038
+ function countCatalogFiles(kind, canonicalFile) {
1039
+ const root = path.join(process.cwd(), '.catalog', kind);
1040
+ if (!fs.existsSync(root))
1041
+ return { root, total: 0, demo: 0, test: 0, canonical: 0, items: [] };
1042
+ const items = fs.readdirSync(root).filter((name) => fs.statSync(path.join(root, name)).isDirectory()).sort();
1043
+ return {
1044
+ root,
1045
+ total: items.length,
1046
+ demo: items.filter((id) => /(^demo-|demo$)/i.test(id)).length,
1047
+ test: items.filter((id) => /(^test-|test$)/i.test(id)).length,
1048
+ canonical: items.filter((id) => fs.existsSync(path.join(root, id, canonicalFile))).length,
1049
+ items: items.slice(0, 25)
1050
+ };
1051
+ }
1052
+ catalogCmd.command('doctor')
1053
+ .option('--strict', 'modo produção: falha se houver demo/test no catálogo runtime', false)
1054
+ .action((options) => {
1055
+ const agents = countCatalogFiles('agents', 'AGENT.md');
1056
+ const squads = countCatalogFiles('squads', 'SQUAD.md');
1057
+ const skills = countCatalogFiles('skills', 'SKILL.md');
1058
+ const mcps = countCatalogFiles('mcps', 'mcp.json');
1059
+ const demoTestTotal = agents.demo + agents.test + squads.demo + squads.test + skills.demo + skills.test + mcps.demo + mcps.test;
1060
+ const checks = [
1061
+ { name: 'agents:min-300', ok: agents.total >= 300, detail: `${agents.total} agentes` },
1062
+ { name: 'agents:canonical-files', ok: agents.canonical === agents.total, detail: `${agents.canonical}/${agents.total} AGENT.md` },
1063
+ { name: 'squads:min-real', ok: squads.total >= 2, detail: `${squads.total} squads` },
1064
+ { name: 'skills:available', ok: skills.total >= 1, detail: `${skills.total} skills catalogadas` },
1065
+ { name: 'mcps:available', ok: mcps.total >= 1, detail: `${mcps.total} MCPs catalogados` },
1066
+ { name: 'catalog:no-demo-test-strict', ok: !options.strict || demoTestTotal === 0, detail: `${demoTestTotal} itens demo/test detectados` },
1067
+ ];
1068
+ const ok = checks.every((c) => c.ok);
1069
+ const warnings = demoTestTotal > 0 ? [{ name: 'catalog:demo-test-present', detail: `${demoTestTotal} itens demo/test ainda existem; use --strict para bloquear produção.` }] : [];
1070
+ console.log(JSON.stringify({ type: 'openlife.catalog.doctor', ok, strict: Boolean(options.strict), checks, warnings, summary: { agents, squads, skills, mcps } }, null, 2));
1071
+ if (!ok)
1072
+ process.exitCode = 1;
1073
+ });
1074
+ catalogCmd.command('list').action(() => {
1075
+ console.log(JSON.stringify({ type: 'openlife.catalog.list', agents: countCatalogFiles('agents', 'AGENT.md'), squads: countCatalogFiles('squads', 'SQUAD.md'), skills: countCatalogFiles('skills', 'SKILL.md'), mcps: countCatalogFiles('mcps', 'mcp.json') }, null, 2));
1076
+ });
1077
+ const teamsCmd = program.command('teams').description('Administração de times de agentes por domínio');
1078
+ teamsCmd.command('list')
1079
+ .action(() => {
1080
+ const fs = require('fs');
1081
+ const path = require('path');
1082
+ const file = path.join(process.cwd(), '.catalog', 'teams.json');
1083
+ const data = fs.existsSync(file) ? JSON.parse(fs.readFileSync(file, 'utf-8')) : [];
1084
+ console.log(JSON.stringify(data, null, 2));
1085
+ });
1086
+ teamsCmd.command('upsert <id>')
1087
+ .option('--domain <domain>', 'domínio: sales|social|support|general', 'general')
1088
+ .option('--description <description>', 'descrição do time', '')
1089
+ .option('--sla <sla>', 'perfil de SLA: fast|balanced|strict', 'balanced')
1090
+ .action((id, options) => {
1091
+ const fs = require('fs');
1092
+ const path = require('path');
1093
+ const dir = path.join(process.cwd(), '.catalog');
1094
+ const file = path.join(dir, 'teams.json');
1095
+ fs.mkdirSync(dir, { recursive: true });
1096
+ const arr = fs.existsSync(file) ? JSON.parse(fs.readFileSync(file, 'utf-8')) : [];
1097
+ const next = { id, domain: options.domain || 'general', description: options.description || '', slaProfile: options.sla || 'balanced', members: [] };
1098
+ const idx = arr.findIndex((x) => x.id === id);
1099
+ if (idx >= 0)
1100
+ arr[idx] = { ...arr[idx], ...next };
1101
+ else
1102
+ arr.push(next);
1103
+ fs.writeFileSync(file, JSON.stringify(arr, null, 2), 'utf-8');
1104
+ console.log(JSON.stringify({ ok: true, file, id }, null, 2));
1105
+ });
1106
+ const networksCmd = program.command('networks').description('Administração de redes de skills por domínio');
1107
+ networksCmd.command('list')
1108
+ .action(() => {
1109
+ const fs = require('fs');
1110
+ const path = require('path');
1111
+ const file = path.join(process.cwd(), '.catalog', 'skill-networks.json');
1112
+ const data = fs.existsSync(file) ? JSON.parse(fs.readFileSync(file, 'utf-8')) : [];
1113
+ console.log(JSON.stringify(data, null, 2));
1114
+ });
1115
+ networksCmd.command('upsert <id>')
1116
+ .option('--domain <domain>', 'domínio: sales|social|support|general', 'general')
1117
+ .option('--capabilities <capabilities>', 'csv de capabilities', '')
1118
+ .action((id, options) => {
1119
+ const fs = require('fs');
1120
+ const path = require('path');
1121
+ const dir = path.join(process.cwd(), '.catalog');
1122
+ const file = path.join(dir, 'skill-networks.json');
1123
+ fs.mkdirSync(dir, { recursive: true });
1124
+ const arr = fs.existsSync(file) ? JSON.parse(fs.readFileSync(file, 'utf-8')) : [];
1125
+ const capabilities = String(options.capabilities || '').split(',').map((s) => s.trim()).filter(Boolean);
1126
+ const next = { id, domain: options.domain || 'general', nodes: [{ id: `${id}-node-1`, capabilities }] };
1127
+ const idx = arr.findIndex((x) => x.id === id);
1128
+ if (idx >= 0)
1129
+ arr[idx] = { ...arr[idx], ...next };
1130
+ else
1131
+ arr.push(next);
1132
+ fs.writeFileSync(file, JSON.stringify(arr, null, 2), 'utf-8');
1133
+ console.log(JSON.stringify({ ok: true, file, id }, null, 2));
1134
+ });
1135
+ const mediaCmd = program.command('media').description('Roteamento e telemetria de mídia (vision/stt/tts)');
1136
+ mediaCmd.command('route').description('Configura preferência de Gemini para mídia').option('--prefer-gemini <value>', 'true|false', 'true').action((options) => {
1137
+ const value = String(options.preferGemini || 'true').toLowerCase() === 'true' ? 'true' : 'false';
1138
+ const envPath = path.join(process.cwd(), '.env');
1139
+ const current = fs.existsSync(envPath) ? fs.readFileSync(envPath, 'utf-8') : '';
1140
+ const without = current
1141
+ .split('\n')
1142
+ .filter(line => !line.startsWith('OPENLIFE_PREFER_GEMINI_FOR_MEDIA='))
1143
+ .join('\n')
1144
+ .replace(/\n+$/, '');
1145
+ const next = `${without}\nOPENLIFE_PREFER_GEMINI_FOR_MEDIA=${value}\n`;
1146
+ fs.writeFileSync(envPath, next, 'utf-8');
1147
+ console.log(`✅ OPENLIFE_PREFER_GEMINI_FOR_MEDIA=${value} salvo em ${envPath}`);
1148
+ });
1149
+ mediaCmd.command('route-status').description('Mostra status da preferência e resumo dos logs de roteamento').option('--tail <n>', 'quantidade de eventos recentes', '20').action((options) => {
1150
+ const envPath = path.join(process.cwd(), '.env');
1151
+ const envRaw = fs.existsSync(envPath) ? fs.readFileSync(envPath, 'utf-8') : '';
1152
+ const preferLine = envRaw.split('\n').find(line => line.startsWith('OPENLIFE_PREFER_GEMINI_FOR_MEDIA='));
1153
+ const prefer = preferLine ? preferLine.split('=')[1] : 'true';
1154
+ const logPath = path.join(process.cwd(), '.openlife', 'media-routing.log.jsonl');
1155
+ const tail = Math.max(1, Number(options.tail || '20'));
1156
+ const events = fs.existsSync(logPath)
1157
+ ? fs.readFileSync(logPath, 'utf-8').split('\n').filter(Boolean).map((line) => {
1158
+ try {
1159
+ return JSON.parse(line);
1160
+ }
1161
+ catch {
1162
+ return null;
1163
+ }
1164
+ }).filter(Boolean)
1165
+ : [];
1166
+ const counters = {};
1167
+ for (const ev of events) {
1168
+ const key = `${ev.kind}:${ev.provider}`;
1169
+ if (!counters[key])
1170
+ counters[key] = { success: 0, failed: 0 };
1171
+ if (ev.status === 'success')
1172
+ counters[key].success += 1;
1173
+ else
1174
+ counters[key].failed += 1;
1175
+ }
1176
+ console.log('🎛️ Media routing status');
1177
+ console.log(`- prefer_gemini_for_media: ${prefer}`);
1178
+ console.log(`- log_file: ${logPath}`);
1179
+ console.log(`- total_events: ${events.length}`);
1180
+ console.log('- counters:');
1181
+ Object.entries(counters).forEach(([k, v]) => console.log(` • ${k} -> success=${v.success}, failed=${v.failed}`));
1182
+ console.log(`- last_${tail}_events:`);
1183
+ events.slice(-tail).forEach((ev) => {
1184
+ console.log(` • ${ev.ts} | ${ev.kind} | ${ev.provider} | ${ev.status}${ev.detail ? ` | ${ev.detail}` : ''}`);
1185
+ });
1186
+ });
1187
+ mediaCmd.command('doctor').description('Valida routing Gemini-first e fallback de mídia sem executar chamadas externas').option('--require-fallback', 'exige evidência de fallback bem-sucedido nos logs', false).action((options) => {
1188
+ const envPath = path.join(process.cwd(), '.env');
1189
+ const envRaw = fs.existsSync(envPath) ? fs.readFileSync(envPath, 'utf-8') : '';
1190
+ const preferLine = envRaw.split('\n').find(line => line.startsWith('OPENLIFE_PREFER_GEMINI_FOR_MEDIA='));
1191
+ const prefer = preferLine ? preferLine.split('=')[1] : 'true';
1192
+ const logPath = path.join(process.cwd(), '.openlife', 'media-routing.log.jsonl');
1193
+ const events = fs.existsSync(logPath)
1194
+ ? fs.readFileSync(logPath, 'utf-8').split('\n').filter(Boolean).map((line) => { try {
1195
+ return JSON.parse(line);
1196
+ }
1197
+ catch {
1198
+ return null;
1199
+ } }).filter(Boolean)
1200
+ : [];
1201
+ const geminiFailures = events.filter((ev) => ev.kind === 'tts' && ev.provider === 'gemini' && ev.status === 'failed').length;
1202
+ const openaiSuccess = events.filter((ev) => ev.kind === 'tts' && ev.provider === 'openai' && ev.status === 'success').length;
1203
+ const fallbackObserved = geminiFailures > 0 && openaiSuccess > 0;
1204
+ const checks = [
1205
+ { name: 'media:prefer-gemini-default', ok: prefer === 'true', detail: `OPENLIFE_PREFER_GEMINI_FOR_MEDIA=${prefer}` },
1206
+ { name: 'media:log-readable', ok: fs.existsSync(logPath) || !options.requireFallback, detail: logPath },
1207
+ { name: 'media:fallback-observed', ok: !options.requireFallback || fallbackObserved, detail: `gemini_failed=${geminiFailures}, openai_success=${openaiSuccess}` },
1208
+ ];
1209
+ const ok = checks.every((c) => c.ok);
1210
+ console.log(JSON.stringify({ type: 'openlife.media.doctor', ok, checks, counters: { geminiTtsFailed: geminiFailures, openaiTtsSuccess: openaiSuccess }, logPath }, null, 2));
1211
+ if (!ok)
1212
+ process.exitCode = 1;
1213
+ });
1214
+ mediaCmd.command('read-image <imagePath>').description('Lê imagem via Gemini CLI com fallback de modelos e log detalhado').option('--prompt <text>', 'prompt de extração', 'Extraia texto e descreva a estrutura desta imagem.').action(async (imagePath, options) => {
1215
+ const absPath = path.isAbsolute(imagePath) ? imagePath : path.join(process.cwd(), imagePath);
1216
+ if (!fs.existsSync(absPath)) {
1217
+ console.error(`❌ Arquivo não encontrado: ${absPath}`);
1218
+ process.exitCode = 1;
1219
+ return;
1220
+ }
1221
+ const { promisify } = await Promise.resolve().then(() => __importStar(require('util')));
1222
+ const { execFile } = await Promise.resolve().then(() => __importStar(require('child_process')));
1223
+ const runExecFile = promisify(execFile);
1224
+ const prompt = String(options.prompt || 'Extraia texto e descreva a estrutura desta imagem.');
1225
+ const models = ['gemini-3.1-flash-lite-preview', 'gemini-2.5-flash', 'gemini-3.1-pro-preview'];
1226
+ const logPath = path.join(process.cwd(), '.openlife', 'media-routing.log.jsonl');
1227
+ fs.mkdirSync(path.dirname(logPath), { recursive: true });
1228
+ let lastErr = null;
1229
+ for (const model of models) {
1230
+ try {
1231
+ const args = ['-o', 'json', '-m', model, '-p', `${prompt} @${absPath}`, '-y'];
1232
+ const { stdout } = await runExecFile('gemini', args, { maxBuffer: 1024 * 1024 * 10, timeout: 180000 });
1233
+ const idx = stdout.indexOf('{');
1234
+ if (idx === -1)
1235
+ throw new Error('Resposta JSON inválida do Gemini CLI.');
1236
+ const parsed = JSON.parse(stdout.slice(idx));
1237
+ const text = String(parsed.response || parsed.text || '').trim();
1238
+ fs.appendFileSync(logPath, JSON.stringify({ ts: new Date().toISOString(), kind: 'vision', provider: `gemini:${model}`, status: 'success', detail: 'media read-image' }) + '\n');
1239
+ console.log(`✅ provider=gemini:${model}`);
1240
+ console.log(text || '[sem texto retornado]');
1241
+ return;
1242
+ }
1243
+ catch (error) {
1244
+ lastErr = error;
1245
+ const msg = errMsg(error);
1246
+ fs.appendFileSync(logPath, JSON.stringify({ ts: new Date().toISOString(), kind: 'vision', provider: `gemini:${model}`, status: 'failed', detail: msg }) + '\n');
1247
+ const retryable = /429|resource_exhausted|capacity|rate limit|quota/i.test(msg);
1248
+ if (!retryable)
1249
+ break;
1250
+ }
1251
+ }
1252
+ console.error(`❌ Falha leitura de imagem via Gemini fallback-chain: ${errMsg(lastErr)}`);
1253
+ process.exitCode = 1;
1254
+ });
1255
+ const orchestrationCmd = program.command('orchestration').description('Contrato e snapshot de orquestração');
1256
+ orchestrationCmd.command('contract').action(() => {
1257
+ const world = new WorldClassCommands_1.WorldClassCommands();
1258
+ console.log(JSON.stringify(world.orchestrationContract(), null, 2));
1259
+ });
1260
+ orchestrationCmd.command('snapshot').action(() => {
1261
+ const world = new WorldClassCommands_1.WorldClassCommands();
1262
+ const file = world.writeOrchestrationSnapshot({ contract: world.orchestrationContract() });
1263
+ console.log(`✅ Snapshot salvo em: ${file}`);
1264
+ });
1265
+ mcpCmd.command('status')
1266
+ .option('--real', 'flag legacy/deprecated — comportamento default agora é status real (mantido pra backwards compat)')
1267
+ .action((_options) => {
1268
+ // Pre-Story 2.2 (P2): required --real flag, errored out without it. Fixed: default
1269
+ // behavior now returns real runtime status. --real flag accepted but no-op for compat.
1270
+ // See docs/stories/epic-feature-completeness/2.2.story.md
1271
+ const world = new WorldClassCommands_1.WorldClassCommands();
1272
+ console.log(JSON.stringify(world.mcpStatusReal(), null, 2));
1273
+ });
1274
+ program
1275
+ .command('status')
1276
+ .description('Status universal do OpenLife (determinístico)')
1277
+ .action(() => {
1278
+ const world = new WorldClassCommands_1.WorldClassCommands();
1279
+ console.log(JSON.stringify({
1280
+ agents: world.agentsListReal().total,
1281
+ squads: world.squadsListReal().total,
1282
+ skills: world.skillsListReal().total,
1283
+ mcp: world.mcpStatusReal().checks
1284
+ }, null, 2));
1285
+ });
1286
+ program
1287
+ .command('doctor')
1288
+ .description('Doctor universal com severidade')
1289
+ .action(() => {
1290
+ const world = new WorldClassCommands_1.WorldClassCommands();
1291
+ console.log(JSON.stringify(world.doctorWorldDetailed(), null, 2));
1292
+ });
1293
+ program
1294
+ .command('restart')
1295
+ .description('Reinício controlado do daemon OpenLife')
1296
+ .action(async () => {
1297
+ try {
1298
+ const { exec } = await Promise.resolve().then(() => __importStar(require('child_process')));
1299
+ exec('pkill -f "openlife.js start --daemon|dist/index.js start --daemon" || true', () => {
1300
+ console.log('🔁 Processos anteriores encerrados. Inicie novamente com: node bin/openlife.js start --daemon');
1301
+ });
1302
+ }
1303
+ catch (e) {
1304
+ console.error(`❌ restart falhou: ${errMsg(e)}`);
1305
+ process.exitCode = 1;
1306
+ }
1307
+ });
1308
+ program
1309
+ .command('update')
1310
+ .description('Atualiza dependências, recompila o core e valida status do sistema (modo dev)')
1311
+ .action(async () => {
1312
+ console.log('🔄 OPEN-LIFE update: npm install + build + system status');
1313
+ try {
1314
+ const { stdout, stderr } = await exec('npm install && npm run build && node bin/openlife.js system status', { cwd: process.cwd(), maxBuffer: 1024 * 1024 * 10 });
1315
+ if (stdout?.trim())
1316
+ console.log(stdout.trim());
1317
+ if (stderr?.trim())
1318
+ console.error(stderr.trim());
1319
+ console.log('✅ openlife update concluído com sucesso.');
1320
+ }
1321
+ catch (e) {
1322
+ const so = errStdout(e);
1323
+ if (so)
1324
+ console.log(so);
1325
+ const se = errStderr(e);
1326
+ if (se)
1327
+ console.error(se);
1328
+ console.error(`❌ openlife update falhou: ${errMsg(e)}`);
1329
+ process.exitCode = 1;
1330
+ }
1331
+ });
1332
+ program
1333
+ .command('up')
1334
+ .description('Inicialização one-shot: doctor rápido + start do agente em daemon')
1335
+ .action(async () => {
1336
+ try {
1337
+ const world = new WorldClassCommands_1.WorldClassCommands();
1338
+ const checks = world.doctorWorldDetailed();
1339
+ const blockers = checks.filter(c => !c.ok && c.severity === 'blocker');
1340
+ if (blockers.length > 0) {
1341
+ console.error('❌ openlife up abortado: doctor encontrou blockers.');
1342
+ for (const b of blockers) {
1343
+ console.error(`- ${b.name}: ${b.detail}`);
1344
+ }
1345
+ process.exitCode = 1;
1346
+ return;
1347
+ }
1348
+ const token = (process.env.TELEGRAM_BOT_TOKEN || '').trim();
1349
+ if (!/^\d{6,}:[A-Za-z0-9_-]{20,}$/.test(token)) {
1350
+ console.error('❌ TELEGRAM_BOT_TOKEN inválido. Esperado formato <digits>:<secret>.');
1351
+ process.exitCode = 1;
1352
+ return;
1353
+ }
1354
+ const res = await fetch(`https://api.telegram.org/bot${token}/getMe`);
1355
+ const data = (await res.json());
1356
+ if (!data?.ok) {
1357
+ console.error(`❌ TELEGRAM_BOT_TOKEN rejeitado pelo Telegram: ${data?.description || 'erro desconhecido'}`);
1358
+ process.exitCode = 1;
1359
+ return;
1360
+ }
1361
+ await exec('pkill -f "openlife.js start --daemon|dist/index.js start --daemon" || true', { cwd: process.cwd() });
1362
+ console.log(`✅ Telegram autenticado: @${data?.result?.username || 'unknown'}`);
1363
+ console.log('🚀 openlife up: iniciando daemon...');
1364
+ const { Gateway } = require('./orchestrator/Gateway');
1365
+ const gateway = new Gateway();
1366
+ gateway.start();
1367
+ }
1368
+ catch (e) {
1369
+ console.error(`❌ openlife up falhou: ${errMsg(e)}`);
1370
+ process.exitCode = 1;
1371
+ }
1372
+ });
1373
+ program
1374
+ .command('agent <verb>')
1375
+ .description('Alias compatível com documentação: agent install|start|status → autonomous lifecycle')
1376
+ .action(async (verb) => {
1377
+ const childProc = require('child_process');
1378
+ const pathMod = require('path');
1379
+ const cliEntry = pathMod.join(__dirname, 'index.js');
1380
+ let argv;
1381
+ switch (verb) {
1382
+ case 'install':
1383
+ argv = [cliEntry, 'install', '--mode=autonomous'];
1384
+ break;
1385
+ case 'start':
1386
+ argv = [cliEntry, 'start', '--daemon'];
1387
+ break;
1388
+ case 'status':
1389
+ argv = [cliEntry, 'status'];
1390
+ break;
1391
+ default:
1392
+ console.error(`❌ verb desconhecido: "${verb}". Use: install | start | status`);
1393
+ console.error(` (Aliases para os verbs reais: install --mode=autonomous, start --daemon, status)`);
1394
+ process.exit(1);
1395
+ }
1396
+ const r = childProc.spawnSync(process.execPath, argv, { stdio: 'inherit' });
1397
+ process.exit(r.status ?? 0);
1398
+ });
1399
+ program
1400
+ .command('start')
1401
+ .option('-d, --daemon', 'Roda em background ouvindo Telegram e Cron Jobs')
1402
+ .description('Inicia o metabolismo do Organismo Vivo')
1403
+ .action(async (options) => {
1404
+ if (options.daemon) {
1405
+ const token = (process.env.TELEGRAM_BOT_TOKEN || '').trim();
1406
+ if (!/^\d{6,}:[A-Za-z0-9_-]{20,}$/.test(token)) {
1407
+ console.error("❌ TELEGRAM_BOT_TOKEN inválido. Esperado formato <digits>:<secret>.");
1408
+ process.exitCode = 1;
1409
+ return;
1410
+ }
1411
+ try {
1412
+ const res = await fetch(`https://api.telegram.org/bot${token}/getMe`);
1413
+ const data = (await res.json());
1414
+ if (!data?.ok) {
1415
+ console.error(`❌ TELEGRAM_BOT_TOKEN rejeitado pelo Telegram: ${data?.description || 'erro desconhecido'}`);
1416
+ process.exitCode = 1;
1417
+ return;
1418
+ }
1419
+ console.log(`✅ Telegram autenticado: @${data?.result?.username || 'unknown'}`);
1420
+ }
1421
+ catch (e) {
1422
+ console.error(`❌ Falha ao validar TELEGRAM_BOT_TOKEN no Telegram: ${errMsg(e)}`);
1423
+ process.exitCode = 1;
1424
+ return;
1425
+ }
1426
+ console.log("Iniciando OPEN-LIFE em Background...");
1427
+ const { Gateway } = require('./orchestrator/Gateway');
1428
+ const gateway = new Gateway();
1429
+ gateway.start();
1430
+ let shuttingDown = false;
1431
+ const shutdown = async (signal) => {
1432
+ if (shuttingDown)
1433
+ return;
1434
+ shuttingDown = true;
1435
+ console.log(`\n[DAEMON] Recebido ${signal}, iniciando shutdown gracioso...`);
1436
+ const force = setTimeout(() => {
1437
+ console.error('[DAEMON] Shutdown timeout (5s), forçando exit');
1438
+ process.exit(1);
1439
+ }, 5000);
1440
+ force.unref();
1441
+ try {
1442
+ await gateway.shutdown(signal);
1443
+ process.exit(0);
1444
+ }
1445
+ catch (e) {
1446
+ console.error('[DAEMON] Erro durante shutdown:', e);
1447
+ process.exit(1);
1448
+ }
1449
+ };
1450
+ process.on('SIGTERM', () => void shutdown('SIGTERM'));
1451
+ process.on('SIGINT', () => void shutdown('SIGINT'));
1452
+ }
1453
+ else {
1454
+ console.log("Use a flag --daemon para rodar de forma persistente.");
1455
+ }
1456
+ });
1457
+ const smokeCmd = program.command('smoke').description('Smokes formais de produção/local');
1458
+ smokeCmd.command('railway').action(() => {
1459
+ const url = process.env.OPENLIFE_RAILWAY_URL || 'https://openlife-01-production.up.railway.app/';
1460
+ const r = child_process.spawnSync('curl', ['-sS', '-o', '/tmp/openlife_smoke_body.txt', '-w', 'HTTP:%{http_code}', url], { encoding: 'utf-8' });
1461
+ const body = fs.existsSync('/tmp/openlife_smoke_body.txt') ? fs.readFileSync('/tmp/openlife_smoke_body.txt', 'utf-8').slice(0, 500) : '';
1462
+ const ok = r.status === 0 && /HTTP:200/.test(r.stdout || '');
1463
+ console.log(JSON.stringify({ type: 'openlife.smoke.railway', ok, url, status: r.stdout, body }, null, 2));
1464
+ if (!ok)
1465
+ process.exitCode = 1;
1466
+ });
1467
+ smokeCmd.command('telegram').action(() => {
1468
+ const token = process.env.TELEGRAM_BOT_TOKEN || '';
1469
+ if (!/^\d{6,}:[A-Za-z0-9_-]{20,}$/.test(token)) {
1470
+ console.log(JSON.stringify({ ok: false, error: 'telegram_token_missing_or_invalid' }, null, 2));
1471
+ process.exitCode = 1;
1472
+ return;
1473
+ }
1474
+ const r = child_process.spawnSync('curl', ['-sS', `https://api.telegram.org/bot${token}/getMe`], { encoding: 'utf-8' });
1475
+ let parsed = {};
1476
+ try {
1477
+ parsed = JSON.parse(r.stdout || '{}');
1478
+ }
1479
+ catch { }
1480
+ if (parsed.result?.token)
1481
+ parsed.result.token = '[REDACTED]';
1482
+ console.log(JSON.stringify({ type: 'openlife.smoke.telegram', ok: Boolean(parsed.ok), bot: parsed.result ? { id: parsed.result.id, username: parsed.result.username, first_name: parsed.result.first_name } : null }, null, 2));
1483
+ if (!parsed.ok)
1484
+ process.exitCode = 1;
1485
+ });
1486
+ program
1487
+ .command('dream <entry...>')
1488
+ .alias('/dream')
1489
+ .description('Organiza automaticamente diário/notas pessoais')
1490
+ .action((entryArgs) => {
1491
+ const organizer = new DreamOrganizer_1.DreamOrganizer();
1492
+ const raw = entryArgs.join(' ');
1493
+ const note = organizer.organize(raw);
1494
+ const file = organizer.save(note);
1495
+ console.log(`✅ /dream organizado em: ${file}`);
1496
+ console.log(JSON.stringify(note, null, 2));
1497
+ });
1498
+ const serviceCmd = program.command('service').description('Operações de serviço (mode service)');
1499
+ serviceCmd.command('list')
1500
+ .option('--limit <limit>', 'limite', '100')
1501
+ .action((options) => {
1502
+ const store = new ServiceState_1.ServiceStateStore();
1503
+ const limit = Number(options.limit || 100);
1504
+ console.log(JSON.stringify(store.list(Number.isFinite(limit) ? limit : 100), null, 2));
1505
+ });
1506
+ serviceCmd.command('create <serviceId>')
1507
+ .option('--objective <objective>', 'objetivo contínuo', 'Serviço operacional OpenLife')
1508
+ .option('--risk <risk>', 'low|medium|high', 'medium')
1509
+ .action((serviceId, options) => {
1510
+ const store = new ServiceState_1.ServiceStateStore();
1511
+ const now = new Date().toISOString();
1512
+ const state = { workspaceId: 'default', serviceId, objective: options.objective, status: 'active', riskProfile: (['low', 'medium', 'high'].includes(options.risk) ? options.risk : 'medium'), slo: {}, kpis: {}, budget: { hardStop: false }, missionIds: [], createdAt: now, updatedAt: now };
1513
+ const dir = path.join(process.cwd(), '.artifacts', 'workspaces', 'default', 'services');
1514
+ fs.mkdirSync(dir, { recursive: true });
1515
+ const out = path.join(dir, `${serviceId}.json`);
1516
+ fs.writeFileSync(out, JSON.stringify(state, null, 2), 'utf-8');
1517
+ console.log(JSON.stringify({ ok: true, path: out, service: state }, null, 2));
1518
+ });
1519
+ serviceCmd.command('status <serviceId>')
1520
+ .description('Mostra status do serviço')
1521
+ .action((serviceId) => {
1522
+ const store = new ServiceState_1.ServiceStateStore();
1523
+ const service = store.getById(serviceId);
1524
+ if (!service) {
1525
+ console.log(`❌ Serviço não encontrado: ${serviceId}`);
1526
+ return;
1527
+ }
1528
+ console.log(JSON.stringify(service, null, 2));
1529
+ });
1530
+ serviceCmd.command('pause <serviceId>')
1531
+ .description('Pausa serviço')
1532
+ .option('--reason <reason>', 'motivo', 'Pausado manualmente pelo operador')
1533
+ .action((serviceId, options) => {
1534
+ const store = new ServiceState_1.ServiceStateStore();
1535
+ const updated = store.setStatus(serviceId, 'paused', options.reason);
1536
+ if (!updated) {
1537
+ console.log(`❌ Serviço não encontrado: ${serviceId}`);
1538
+ return;
1539
+ }
1540
+ console.log(`⏸️ Serviço pausado: ${serviceId}`);
1541
+ });
1542
+ serviceCmd.command('resume <serviceId>')
1543
+ .description('Retoma serviço')
1544
+ .option('--reason <reason>', 'motivo', 'Retomado manualmente pelo operador')
1545
+ .action((serviceId, options) => {
1546
+ const store = new ServiceState_1.ServiceStateStore();
1547
+ const updated = store.setStatus(serviceId, 'active', options.reason);
1548
+ if (!updated) {
1549
+ console.log(`❌ Serviço não encontrado: ${serviceId}`);
1550
+ return;
1551
+ }
1552
+ console.log(`▶️ Serviço retomado: ${serviceId}`);
1553
+ });
1554
+ serviceCmd.command('events <serviceId>')
1555
+ .description('Lista eventos recentes do serviço')
1556
+ .option('--limit <limit>', 'quantidade de eventos', '50')
1557
+ .action((serviceId, options) => {
1558
+ const store = new ServiceState_1.ServiceStateStore();
1559
+ const limit = Number(options.limit || 50);
1560
+ const events = store.listEvents(serviceId, Number.isFinite(limit) ? limit : 50);
1561
+ if (!events.length) {
1562
+ console.log(`ℹ️ Sem eventos para serviço: ${serviceId}`);
1563
+ return;
1564
+ }
1565
+ console.log(JSON.stringify(events, null, 2));
1566
+ });
1567
+ const taskCmd = program.command('task').description('Operações de tarefa (mode task)');
1568
+ taskCmd.command('list')
1569
+ .option('--limit <limit>', 'limite', '100')
1570
+ .action((options) => {
1571
+ const dir = path.join(process.cwd(), '.artifacts', 'missions');
1572
+ const limit = Number(options.limit || 100);
1573
+ const files = fs.existsSync(dir) ? fs.readdirSync(dir).filter((f) => f.endsWith('.json') && !f.endsWith('.thought-trace.json')).slice(-(Number.isFinite(limit) ? limit : 100)) : [];
1574
+ const items = files.map((f) => JSON.parse(fs.readFileSync(path.join(dir, f), 'utf-8')));
1575
+ console.log(JSON.stringify(items, null, 2));
1576
+ });
1577
+ taskCmd.command('run <goal...>')
1578
+ .option('--service <serviceId>', 'serviço contínuo associado', '')
1579
+ .action((goalArgs, options) => {
1580
+ const goal = goalArgs.join(' ');
1581
+ const taskId = `task_${Date.now()}`;
1582
+ const now = new Date().toISOString();
1583
+ const state = { taskId, goal, mode: options.service ? 'service' : 'task', status: 'completed', createdAt: now, updatedAt: now, attempts: [], chosenAgents: [], chosenSquads: [], chosenSkills: [], routeMode: 'single', memoryCandidates: [], governanceEvents: [], userId: 'cli', plan: [], successCriteria: [], reviewFindings: [], artifacts: [] };
1584
+ const store = new MissionState_1.MissionStateStore();
1585
+ const out = store.persist(state);
1586
+ if (options.service) {
1587
+ // v1.3 — explicit-only contract. CLI passing --service constitutes
1588
+ // the operator's explicit opt-in, so we synthesize the human actor
1589
+ // from the OS user (or 'cli-operator' fallback) plus the canonical
1590
+ // consent scope.
1591
+ const actor = process.env.OPENLIFE_OPERATOR_ID || process.env.USER || 'cli-operator';
1592
+ new ServiceState_1.ServiceStateStore().promoteTaskToService(state, options.service, goal, {
1593
+ actor,
1594
+ consentScope: 'task_promotion',
1595
+ reason: 'cli --service flag',
1596
+ });
1597
+ }
1598
+ console.log(JSON.stringify({ ok: true, taskId, path: out, state }, null, 2));
1599
+ });
1600
+ taskCmd.command('status <taskId>')
1601
+ .description('Mostra status da tarefa')
1602
+ .action((taskId) => {
1603
+ const missionPath = path.join(process.cwd(), '.artifacts', 'missions', `${taskId}.json`);
1604
+ if (!fs.existsSync(missionPath)) {
1605
+ console.log(`❌ Task não encontrada: ${taskId}`);
1606
+ return;
1607
+ }
1608
+ const mission = JSON.parse(fs.readFileSync(missionPath, 'utf-8'));
1609
+ console.log(JSON.stringify(mission, null, 2));
1610
+ });
1611
+ const jobCmd = program.command('job').description('Lifecycle de jobs autônomos');
1612
+ jobCmd.command('list')
1613
+ .option('--limit <limit>', 'limite', '50')
1614
+ .action((options) => {
1615
+ const store = new JobLifecycle_1.JobLifecycleStore();
1616
+ const limit = Number(options.limit || 50);
1617
+ console.log(JSON.stringify(store.list(limit), null, 2));
1618
+ });
1619
+ jobCmd.command('events <jobId>')
1620
+ .action((jobId) => {
1621
+ const store = new JobLifecycle_1.JobLifecycleStore();
1622
+ console.log(JSON.stringify(store.events(jobId), null, 2));
1623
+ });
1624
+ const runtimeCmd = program.command('runtime').description('Runtime registry unificado');
1625
+ runtimeCmd.command('probe').action(() => {
1626
+ const rr = new RuntimeRegistry_1.RuntimeRegistry();
1627
+ console.log(JSON.stringify(rr.probe(), null, 2));
1628
+ });
1629
+ runtimeCmd.command('list').action(() => {
1630
+ const rr = new RuntimeRegistry_1.RuntimeRegistry();
1631
+ console.log(JSON.stringify(rr.list(), null, 2));
1632
+ });
1633
+ const reversaCmd = program.command('reversa').description('Reversa-lite reverse-spec pipeline');
1634
+ reversaCmd.command('status').action(() => {
1635
+ const r = new ReversaAgent_1.ReversaAgent();
1636
+ console.log(JSON.stringify(r.resume(), null, 2));
1637
+ });
1638
+ reversaCmd.command('next')
1639
+ .option('--note <note>', 'nota de avanço', 'checkpoint avançado')
1640
+ .action((options) => {
1641
+ const r = new ReversaAgent_1.ReversaAgent();
1642
+ console.log(JSON.stringify(r.advance(options.note), null, 2));
1643
+ });
1644
+ reversaCmd.command('mode')
1645
+ .option('--set <mode>', 'default | designmd')
1646
+ .option('--profile <profileId>', 'design profile id')
1647
+ .action((options) => {
1648
+ const r = new ReversaAgent_1.ReversaAgent();
1649
+ if (!options.set) {
1650
+ console.log(JSON.stringify(r.resume(), null, 2));
1651
+ return;
1652
+ }
1653
+ console.log(JSON.stringify(r.setMode(options.set, options.profile), null, 2));
1654
+ });
1655
+ reversaCmd.command('run-phase <phase>')
1656
+ .option('--note <note>', 'phase note', 'phase run')
1657
+ .action((phase, options) => {
1658
+ const r = new ReversaAgent_1.ReversaAgent();
1659
+ console.log(JSON.stringify(r.runPhase(phase, options.note), null, 2));
1660
+ });
1661
+ reversaCmd.command('run-all')
1662
+ .option('--note <note>', 'run note', 'run all')
1663
+ .action((options) => {
1664
+ const r = new ReversaAgent_1.ReversaAgent();
1665
+ console.log(JSON.stringify(r.runAll(options.note), null, 2));
1666
+ });
1667
+ reversaCmd.command('export-json').action(() => {
1668
+ const r = new ReversaAgent_1.ReversaAgent();
1669
+ console.log(JSON.stringify({ path: r.exportContractsJson() }, null, 2));
1670
+ });
1671
+ const sourcesCmd = program.command('sources').description('Catálogo de fontes externas (skills/squads/agents/mcps)');
1672
+ sourcesCmd.command('list').action(() => {
1673
+ const reg = new ExternalCatalogRegistry_1.ExternalCatalogRegistry();
1674
+ console.log(JSON.stringify(reg.list(), null, 2));
1675
+ });
1676
+ sourcesCmd.command('guard-check <url>').action((url) => {
1677
+ const guard = new SecurityDownloadGuard_1.SecurityDownloadGuard();
1678
+ console.log(JSON.stringify(guard.validateUrl(url), null, 2));
1679
+ });
1680
+ sourcesCmd.command('scaffold <type> <id>')
1681
+ .description('cria scaffold local de referência para skill|squad|agent|mcp')
1682
+ .option('--notes <notes>', 'notas adicionais', '')
1683
+ .action((type, id, options) => {
1684
+ const fs = require('fs');
1685
+ const path = require('path');
1686
+ const allowed = new Set(['skill', 'squad', 'agent', 'mcp']);
1687
+ if (!allowed.has(type)) {
1688
+ console.error('type inválido: use skill|squad|agent|mcp');
1689
+ process.exit(2);
1690
+ }
1691
+ const base = path.join(process.cwd(), '.catalog', `${type}s`, id);
1692
+ fs.mkdirSync(base, { recursive: true });
1693
+ const content = `# ${type}:${id}\n\nstatus: reference\ncreatedAt: ${new Date().toISOString()}\n\n## Notes\n${options.notes || 'n/a'}\n`;
1694
+ fs.writeFileSync(path.join(base, 'REFERENCE.md'), content, 'utf-8');
1695
+ console.log(JSON.stringify({ ok: true, path: path.join(base, 'REFERENCE.md') }, null, 2));
1696
+ });
1697
+ sourcesCmd.command('import-ref <type> <id> <url>')
1698
+ .description('importa referência remota com guardrail para .catalog')
1699
+ .action(async (type, id, url) => {
1700
+ const fs = require('fs');
1701
+ const path = require('path');
1702
+ const guard = new SecurityDownloadGuard_1.SecurityDownloadGuard();
1703
+ const urlCheck = guard.validateUrl(url);
1704
+ if (!urlCheck.ok) {
1705
+ console.error(JSON.stringify({ error: 'untrusted_source', urlCheck }, null, 2));
1706
+ process.exit(5);
1707
+ }
1708
+ const { spawnSync } = require('child_process');
1709
+ const r = spawnSync('bash', ['-lc', `curl -L --max-time 20 ${JSON.stringify(url)}`], { encoding: 'utf-8', maxBuffer: 1024 * 1024 * 3 });
1710
+ const body = r.status === 0 ? (r.stdout || '') : '';
1711
+ const base = path.join(process.cwd(), '.catalog', `${type}s`, id);
1712
+ fs.mkdirSync(base, { recursive: true });
1713
+ const out = path.join(base, 'IMPORTED_REFERENCE.md');
1714
+ fs.writeFileSync(out, `# Imported ${type}:${id}\n\nsource: ${url}\nimportedAt: ${new Date().toISOString()}\n\n---\n\n${body}`.slice(0, 120000), 'utf-8');
1715
+ console.log(JSON.stringify({ ok: true, path: out, source: url }, null, 2));
1716
+ });
1717
+ const teammateCmd = program.command('teammate').description('Agents as teammates board');
1718
+ teammateCmd.command('list').action(() => {
1719
+ const store = new TeammateBoard_1.TeammateBoardStore();
1720
+ console.log(JSON.stringify(store.list(), null, 2));
1721
+ });
1722
+ teammateCmd.command('upsert <id> <owner> <status> <title>')
1723
+ .option('--blocker <blocker>', 'blocker', '')
1724
+ .option('--comment <comment>', 'comment', '')
1725
+ .action((id, owner, status, title, options) => {
1726
+ const store = new TeammateBoard_1.TeammateBoardStore();
1727
+ store.upsert({ id, workspaceId: 'default', owner, status, title, blockers: options.blocker ? [options.blocker] : [], comments: options.comment ? [options.comment] : [], updatedAt: new Date().toISOString() });
1728
+ console.log('OK');
1729
+ });
1730
+ const learningCmd = program.command('learning').description('Governed skill learning loop');
1731
+ learningCmd.command('add <taskId> <summary>').action((taskId, summary) => {
1732
+ const store = new SkillLearningLoop_1.SkillLearningLoopStore();
1733
+ console.log(JSON.stringify(store.add(taskId, summary), null, 2));
1734
+ });
1735
+ learningCmd.command('list').action(() => {
1736
+ const store = new SkillLearningLoop_1.SkillLearningLoopStore();
1737
+ console.log(JSON.stringify(store.list(), null, 2));
1738
+ });
1739
+ const contextCmd = program.command('context').description('Contexto institucional do OpenLife');
1740
+ contextCmd.command('init')
1741
+ .option('--name <name>', 'nome institucional', 'OpenLife Institutional Context')
1742
+ .action((options) => {
1743
+ console.log(JSON.stringify(new EnterpriseAgenticCore_1.InstitutionalContextStore().init(options.name), null, 2));
1744
+ });
1745
+ contextCmd.command('doctor').action(() => {
1746
+ const result = new EnterpriseAgenticCore_1.InstitutionalContextStore().doctor();
1747
+ if (!result.ok)
1748
+ process.exitCode = 1;
1749
+ console.log(JSON.stringify(result, null, 2));
1750
+ });
1751
+ contextCmd.command('summary').action(() => {
1752
+ console.log(JSON.stringify(new EnterpriseAgenticCore_1.InstitutionalContextStore().summary(), null, 2));
1753
+ });
1754
+ function printCliResult(result) {
1755
+ if (!result?.ok)
1756
+ process.exitCode = 1;
1757
+ console.log(JSON.stringify(result, null, 2));
1758
+ }
1759
+ const pluginCmd = program.command('plugin').description('Marketplace interno de plugins/skills governados');
1760
+ pluginCmd.command('install <id>')
1761
+ .option('--risk <risk>', 'low|medium|high', 'low')
1762
+ .option('--capability <capability>', 'capacidade do plugin', (value, previous) => previous.concat([value]), [])
1763
+ .action((id, options) => {
1764
+ printCliResult(new EnterpriseAgenticCore_1.PluginManifestStore().install(id, options.capability || [], options.risk));
1765
+ });
1766
+ pluginCmd.command('list').action(() => {
1767
+ console.log(JSON.stringify(new EnterpriseAgenticCore_1.PluginManifestStore().list(), null, 2));
1768
+ });
1769
+ pluginCmd.command('inspect <id>').action((id) => {
1770
+ printCliResult(new EnterpriseAgenticCore_1.PluginManifestStore().inspect(id));
1771
+ });
1772
+ pluginCmd.command('disable <id>').action((id) => {
1773
+ printCliResult(new EnterpriseAgenticCore_1.PluginManifestStore().setStatus(id, 'disabled'));
1774
+ });
1775
+ pluginCmd.command('enable <id>').action((id) => {
1776
+ printCliResult(new EnterpriseAgenticCore_1.PluginManifestStore().setStatus(id, 'installed'));
1777
+ });
1778
+ const pilotCmd = program.command('pilot').description('Pilotos mensuráveis para serviços/capacidades OpenLife');
1779
+ pilotCmd.command('create <id>')
1780
+ .option('--metric <metric>', 'métrica de sucesso')
1781
+ .option('--service <service>', 'serviço associado', 'unassigned')
1782
+ .action((id, options) => {
1783
+ printCliResult(new EnterpriseAgenticCore_1.PilotStore().create(id, options.metric || '', options.service));
1784
+ });
1785
+ pilotCmd.command('list').action(() => {
1786
+ console.log(JSON.stringify(new EnterpriseAgenticCore_1.PilotStore().list(), null, 2));
1787
+ });
1788
+ pilotCmd.command('report <id>').action((id) => {
1789
+ printCliResult(new EnterpriseAgenticCore_1.PilotStore().report(id));
1790
+ });
1791
+ pilotCmd.command('complete <id>')
1792
+ .option('--outcome <outcome>', 'resultado do piloto', 'completed')
1793
+ .action((id, options) => {
1794
+ printCliResult(new EnterpriseAgenticCore_1.PilotStore().complete(id, options.outcome));
1795
+ });
1796
+ const evalCmd = program.command('eval').description('Avaliação/LLM-as-judge interno para missões');
1797
+ evalCmd.command('judge')
1798
+ .requiredOption('--mission <mission>', 'id da missão')
1799
+ .option('--criteria <criteria>', 'critérios separados por vírgula', 'quality,governance,actionability')
1800
+ .action((options) => {
1801
+ printCliResult(new EnterpriseAgenticCore_1.MissionEvaluationStore().judge(options.mission, options.criteria));
1802
+ });
1803
+ evalCmd.command('report <mission>').action((mission) => {
1804
+ printCliResult(new EnterpriseAgenticCore_1.MissionEvaluationStore().report(mission));
1805
+ });
1806
+ evalCmd.command('list')
1807
+ .description('Lista as avaliações persistidas em .openlife/evaluations/ (Story 13.2)')
1808
+ .option('--verdict <verdict>', 'filtra por ready_for_review | needs_iteration')
1809
+ .option('--min-score <score>', 'score mínimo (0-100)')
1810
+ .option('--source <source>', 'filtra por source (brain | heuristic)')
1811
+ .action((options) => {
1812
+ const all = new EnterpriseAgenticCore_1.MissionEvaluationStore().list();
1813
+ let filtered = Array.isArray(all) ? all : [];
1814
+ if (options.verdict) {
1815
+ filtered = filtered.filter((e) => e && e.verdict === options.verdict);
1816
+ }
1817
+ if (options.minScore !== undefined) {
1818
+ const min = Number(options.minScore);
1819
+ if (!Number.isNaN(min))
1820
+ filtered = filtered.filter((e) => typeof e?.score === 'number' && e.score >= min);
1821
+ }
1822
+ if (options.source) {
1823
+ // 'brain' = explicit source field; 'heuristic' = source missing or anything else.
1824
+ filtered = filtered.filter((e) => {
1825
+ const src = (e && e.source) || 'heuristic';
1826
+ return src === options.source;
1827
+ });
1828
+ }
1829
+ console.log(JSON.stringify({ ok: true, count: filtered.length, evaluations: filtered }, null, 2));
1830
+ });
1831
+ const learnCmd = program.command('learn').description('Feedback loop que transforma revisão humana em evolução');
1832
+ learnCmd.command('from-last-run')
1833
+ .requiredOption('--mission <mission>', 'id da missão')
1834
+ .option('--feedback <feedback>', 'feedback humano ou operacional', '')
1835
+ .action((options) => {
1836
+ printCliResult(new EnterpriseAgenticCore_1.LearningLoopStore().fromLastRun(options.mission, options.feedback));
1837
+ });
1838
+ learnCmd.command('list').action(() => {
1839
+ console.log(JSON.stringify(new EnterpriseAgenticCore_1.LearningLoopStore().list(), null, 2));
1840
+ });
1841
+ const enterpriseCmd = program.command('enterprise').description('Diagnóstico agregado do núcleo enterprise-agentic');
1842
+ enterpriseCmd.command('doctor').action(() => {
1843
+ const result = new EnterpriseAgenticCore_1.EnterpriseDoctor().run();
1844
+ if (!result.ok)
1845
+ process.exitCode = 1;
1846
+ console.log(JSON.stringify(result, null, 2));
1847
+ });
1848
+ const designmdCmd = program.command('designmd').description('DESIGN.md mode manager');
1849
+ designmdCmd.command('status').action(() => {
1850
+ const dm = new DesignMdMode_1.DesignMdMode();
1851
+ console.log(JSON.stringify(dm.status(), null, 2));
1852
+ });
1853
+ designmdCmd.command('import')
1854
+ .option('--vendor <vendorPath>', 'vendor path', 'vendor/awesome-design-md')
1855
+ .option('--source <source>', 'awesome-design-md | open-design', 'awesome-design-md')
1856
+ .action((options) => {
1857
+ const guard = new SecurityDownloadGuard_1.SecurityDownloadGuard();
1858
+ const scan = guard.scanExtractedDir(options.vendor);
1859
+ if (!scan.ok) {
1860
+ console.error(JSON.stringify({ error: 'security_guard_blocked', scan }, null, 2));
1861
+ process.exit(4);
1862
+ }
1863
+ const importer = new DesignMdImporter_1.DesignMdImporter();
1864
+ const result = options.source === 'open-design'
1865
+ ? importer.importFromOpenDesign(options.vendor)
1866
+ : importer.importFromVendor(options.vendor);
1867
+ console.log(JSON.stringify({ security: scan, source: options.source, result }, null, 2));
1868
+ });
1869
+ designmdCmd.command('list').action(() => {
1870
+ const registry = new DesignMdRegistry_1.DesignMdRegistry();
1871
+ console.log(JSON.stringify(registry.list(), null, 2));
1872
+ });
1873
+ designmdCmd.command('show <profileId>').action((profileId) => {
1874
+ const registry = new DesignMdRegistry_1.DesignMdRegistry();
1875
+ const p = registry.get(profileId);
1876
+ console.log(JSON.stringify(p.meta, null, 2));
1877
+ });
1878
+ designmdCmd.command('apply <profileId> <source> <title> <designPath>').action((profileId, source, title, designPath) => {
1879
+ const dm = new DesignMdMode_1.DesignMdMode();
1880
+ const content = require('fs').readFileSync(designPath, 'utf-8');
1881
+ console.log(JSON.stringify(dm.applyProfile({ id: profileId, source, title, appliedAt: new Date().toISOString() }, content), null, 2));
1882
+ });
1883
+ // Workflow execution surface (Story 4.3 — workflow engine CLI)
1884
+ const workflowCmd = program.command('workflow').description('OpenLife workflow engine — list, validate, run, status, resume, abort');
1885
+ workflowCmd.command('list')
1886
+ .description('List available workflows (composite: .catalog/workflows/ ∪ dist-templates/workflows/)')
1887
+ .action(() => {
1888
+ const { FileWorkflowProvider } = require('./orchestrator/providers/FileWorkflowProvider');
1889
+ const provider = new FileWorkflowProvider();
1890
+ const entries = provider.list();
1891
+ console.log(JSON.stringify({ ok: true, count: entries.length, workflows: entries }, null, 2));
1892
+ });
1893
+ workflowCmd.command('validate <workflowFile>')
1894
+ .description('Validate a workflow YAML file against the schema')
1895
+ .action((workflowFile) => {
1896
+ const { parseWorkflowFile } = require('./orchestrator/workflow/WorkflowParser');
1897
+ const resolved = path.isAbsolute(workflowFile) ? workflowFile : path.resolve(process.cwd(), workflowFile);
1898
+ const r = parseWorkflowFile(resolved);
1899
+ if (!r.ok) {
1900
+ console.log(JSON.stringify({ ok: false, errors: r.errors }, null, 2));
1901
+ process.exitCode = 1;
1902
+ return;
1903
+ }
1904
+ console.log(JSON.stringify({ ok: true, workflowId: r.workflow.id, steps: r.workflow.sequence.length }, null, 2));
1905
+ });
1906
+ workflowCmd.command('run <workflowFile>')
1907
+ .description('Execute a workflow (--dry-run uses the simulated executor; default)')
1908
+ .option('--dry-run', 'simulate steps without invoking real LLMs', true)
1909
+ .action(async (workflowFile, opts) => {
1910
+ const { parseWorkflowFile } = require('./orchestrator/workflow/WorkflowParser');
1911
+ const { WorkflowEngine } = require('./orchestrator/workflow/WorkflowEngine');
1912
+ const resolved = path.isAbsolute(workflowFile) ? workflowFile : path.resolve(process.cwd(), workflowFile);
1913
+ const r = parseWorkflowFile(resolved);
1914
+ if (!r.ok) {
1915
+ console.log(JSON.stringify({ ok: false, error: 'workflow_parse_failed', errors: r.errors }, null, 2));
1916
+ process.exitCode = 1;
1917
+ return;
1918
+ }
1919
+ const engine = new WorkflowEngine();
1920
+ const state = await engine.run(r.workflow);
1921
+ console.log(JSON.stringify({ ok: state.status === 'completed' || state.status === 'awaiting_input', state }, null, 2));
1922
+ if (state.status === 'failed' || state.status === 'aborted')
1923
+ process.exitCode = 1;
1924
+ });
1925
+ workflowCmd.command('status <runId>')
1926
+ .description('Show the persisted state of a workflow run')
1927
+ .action((runId) => {
1928
+ const { WorkflowEngine } = require('./orchestrator/workflow/WorkflowEngine');
1929
+ const engine = new WorkflowEngine();
1930
+ const state = engine.status(runId);
1931
+ if (!state) {
1932
+ console.log(JSON.stringify({ ok: false, error: 'run_not_found', runId }, null, 2));
1933
+ process.exitCode = 1;
1934
+ return;
1935
+ }
1936
+ console.log(JSON.stringify({ ok: true, state }, null, 2));
1937
+ });
1938
+ workflowCmd.command('resume <workflowFile> <runId>')
1939
+ .description('Resume a paused or crashed workflow run from the last checkpoint')
1940
+ .action(async (workflowFile, runId) => {
1941
+ const { parseWorkflowFile } = require('./orchestrator/workflow/WorkflowParser');
1942
+ const { WorkflowEngine } = require('./orchestrator/workflow/WorkflowEngine');
1943
+ const resolved = path.isAbsolute(workflowFile) ? workflowFile : path.resolve(process.cwd(), workflowFile);
1944
+ const r = parseWorkflowFile(resolved);
1945
+ if (!r.ok) {
1946
+ console.log(JSON.stringify({ ok: false, error: 'workflow_parse_failed', errors: r.errors }, null, 2));
1947
+ process.exitCode = 1;
1948
+ return;
1949
+ }
1950
+ const engine = new WorkflowEngine();
1951
+ try {
1952
+ const state = await engine.resume(r.workflow, runId);
1953
+ console.log(JSON.stringify({ ok: state.status === 'completed', state }, null, 2));
1954
+ if (state.status === 'failed' || state.status === 'aborted')
1955
+ process.exitCode = 1;
1956
+ }
1957
+ catch (err) {
1958
+ const msg = err instanceof Error ? err.message : String(err);
1959
+ console.log(JSON.stringify({ ok: false, error: 'resume_failed', message: msg }, null, 2));
1960
+ process.exitCode = 1;
1961
+ }
1962
+ });
1963
+ workflowCmd.command('abort <runId>')
1964
+ .description('Mark a workflow run as aborted and clear its checkpoint')
1965
+ .option('--reason <reason>', 'human-readable abort reason', 'manual')
1966
+ .action((runId, opts) => {
1967
+ const { WorkflowEngine } = require('./orchestrator/workflow/WorkflowEngine');
1968
+ const engine = new WorkflowEngine();
1969
+ const state = engine.abort(runId, opts.reason);
1970
+ if (!state) {
1971
+ console.log(JSON.stringify({ ok: false, error: 'run_not_found', runId }, null, 2));
1972
+ process.exitCode = 1;
1973
+ return;
1974
+ }
1975
+ console.log(JSON.stringify({ ok: true, state }, null, 2));
1976
+ });
1977
+ // Mission checkpoint inspection (Story 6.2 — autonomy layer)
1978
+ const missionCmd = program.command('mission').description('Inspect mission state and checkpoints');
1979
+ missionCmd.command('checkpoint <missionId>')
1980
+ .description('Show the latest persisted checkpoint for a mission (if any)')
1981
+ .action((missionId) => {
1982
+ const { MissionCheckpointStore } = require('./orchestrator/MissionState');
1983
+ const store = new MissionCheckpointStore();
1984
+ const ckpt = store.getLatestCheckpoint(missionId);
1985
+ if (!ckpt) {
1986
+ console.log(JSON.stringify({ ok: false, error: 'no_checkpoint', missionId }, null, 2));
1987
+ process.exitCode = 1;
1988
+ return;
1989
+ }
1990
+ console.log(JSON.stringify({ ok: true, ...ckpt }, null, 2));
1991
+ });
1992
+ missionCmd.command('checkpoints')
1993
+ .description('List all missionIds with persisted checkpoints')
1994
+ .action(() => {
1995
+ const { MissionCheckpointStore } = require('./orchestrator/MissionState');
1996
+ const store = new MissionCheckpointStore();
1997
+ const ids = store.list();
1998
+ console.log(JSON.stringify({ ok: true, count: ids.length, missionIds: ids }, null, 2));
1999
+ });
2000
+ missionCmd.command('clear-checkpoint <missionId>')
2001
+ .description('Remove the persisted checkpoint for a mission')
2002
+ .action((missionId) => {
2003
+ const { MissionCheckpointStore } = require('./orchestrator/MissionState');
2004
+ const store = new MissionCheckpointStore();
2005
+ const had = store.getLatestCheckpoint(missionId) !== null;
2006
+ store.clear(missionId);
2007
+ console.log(JSON.stringify({ ok: true, missionId, cleared: had }, null, 2));
2008
+ });
2009
+ const aiobuilderCmd = program.command('aiobuilder').description('AIOBUILDER operations');
2010
+ aiobuilderCmd.command('mode')
2011
+ .option('--set <mode>', 'default | designmd')
2012
+ .option('--profile <profileId>', 'design profile id')
2013
+ .action((options) => {
2014
+ const r = new ReversaAgent_1.ReversaAgent();
2015
+ if (!options.set) {
2016
+ console.log(JSON.stringify(r.resume(), null, 2));
2017
+ return;
2018
+ }
2019
+ console.log(JSON.stringify(r.setMode(options.set, options.profile), null, 2));
2020
+ });
2021
+ aiobuilderCmd.command('create-agent <id>')
2022
+ .option('--role <role>', 'papel', 'specialist')
2023
+ .option('--notes <notes>', 'notas', '')
2024
+ .action((id, options) => {
2025
+ const base = path.join(process.cwd(), '.catalog', 'agents', id);
2026
+ fs.mkdirSync(base, { recursive: true });
2027
+ const out = path.join(base, 'AGENT.md');
2028
+ fs.writeFileSync(out, `---\nid: ${id}\nname: ${id}\nrole: ${options.role}\nsource: aiobuilder\n---\n\n# ${id}\n\n${options.notes || 'Criado pelo AIOBUILDER.'}\n`, 'utf-8');
2029
+ console.log(JSON.stringify({ ok: true, type: 'agent', path: out }, null, 2));
2030
+ });
2031
+ aiobuilderCmd.command('create-squad <id>')
2032
+ .option('--domain <domain>', 'domínio', 'general')
2033
+ .option('--notes <notes>', 'notas', '')
2034
+ .action((id, options) => {
2035
+ const base = path.join(process.cwd(), '.catalog', 'squads', id);
2036
+ fs.mkdirSync(base, { recursive: true });
2037
+ const out = path.join(base, 'SQUAD.md');
2038
+ fs.writeFileSync(out, `---\nid: ${id}\ndomain: ${options.domain}\nsource: aiobuilder\n---\n\n# ${id}\n\n${options.notes || 'Criada pelo AIOBUILDER.'}\n`, 'utf-8');
2039
+ console.log(JSON.stringify({ ok: true, type: 'squad', path: out }, null, 2));
2040
+ });
2041
+ aiobuilderCmd.command('validate-catalog').action(() => {
2042
+ const agents = countCatalogFiles('agents', 'AGENT.md');
2043
+ const squads = countCatalogFiles('squads', 'SQUAD.md');
2044
+ const ok = agents.total >= 300 && squads.total >= 2;
2045
+ console.log(JSON.stringify({ ok, agents, squads }, null, 2));
2046
+ if (!ok)
2047
+ process.exitCode = 1;
2048
+ });
2049
+ aiobuilderCmd.command('generate-ui <featureName>')
2050
+ .option('--strict-contracts', 'require valid final contracts', false)
2051
+ .option('--consistency-check', 'require active design profile with checksum/source', false)
2052
+ .action((featureName, options) => {
2053
+ const r = new ReversaAgent_1.ReversaAgent().resume();
2054
+ const mode = r.mode || 'default';
2055
+ const profile = r.designProfileId || 'none';
2056
+ const contractsPath = require('path').join(process.cwd(), '.reversa', 'final-contracts.md');
2057
+ const fs = require('fs');
2058
+ const contractsSummary = fs.existsSync(contractsPath) ? fs.readFileSync(contractsPath, 'utf-8').slice(0, 1500) : 'No final contracts found';
2059
+ if (options?.strictContracts) {
2060
+ const jsonPath = require('path').join(process.cwd(), '.reversa', 'final-contracts.json');
2061
+ if (!fs.existsSync(jsonPath)) {
2062
+ console.error('strict-contracts enabled: final-contracts.json missing');
2063
+ process.exit(2);
2064
+ }
2065
+ const data = JSON.parse(fs.readFileSync(jsonPath, 'utf-8'));
2066
+ if (!data?.validation?.ok) {
2067
+ console.error('strict-contracts enabled: contracts validation failed');
2068
+ process.exit(3);
2069
+ }
2070
+ }
2071
+ const dm = new DesignMdMode_1.DesignMdMode();
2072
+ const profiles = dm.listProfiles();
2073
+ const activeProfile = profiles.find((p) => p.id === profile);
2074
+ if (options?.consistencyCheck) {
2075
+ if (mode !== 'designmd' || !activeProfile?.checksum || !activeProfile?.source) {
2076
+ console.error('consistency-check enabled: missing designmd mode/profile/checksum/source');
2077
+ process.exit(5);
2078
+ }
2079
+ }
2080
+ const context = {
2081
+ designContext: {
2082
+ mode,
2083
+ profile,
2084
+ source: activeProfile?.source || 'unknown',
2085
+ checksum: activeProfile?.checksum || 'unknown',
2086
+ },
2087
+ precisionRules: [
2088
+ 'Maintain token consistency across pages/components',
2089
+ 'Preserve spacing/typography scale from DESIGN.md',
2090
+ 'Reject visual drift that breaks profile identity',
2091
+ ],
2092
+ };
2093
+ const outputDir = require('path').join(process.cwd(), '.reversa', 'ui-drafts');
2094
+ fs.mkdirSync(outputDir, { recursive: true });
2095
+ const featureSlug = featureName.replace(/\s+/g, '-').toLowerCase();
2096
+ const outFile = require('path').join(outputDir, `${featureSlug}.md`);
2097
+ const draft = [
2098
+ `# UI Draft — ${featureName}`,
2099
+ '',
2100
+ `mode: ${mode}`,
2101
+ `profile: ${profile}`,
2102
+ `source: ${activeProfile?.source || 'unknown'}`,
2103
+ `checksum: ${activeProfile?.checksum || 'unknown'}`,
2104
+ '',
2105
+ '## Generation Log',
2106
+ '- Step 1/4: Loaded active DESIGN.md profile context',
2107
+ '- Step 2/4: Applied precision and consistency rules',
2108
+ '- Step 3/4: Merged final-contracts constraints',
2109
+ '- Step 4/4: Produced textual UI draft',
2110
+ '',
2111
+ '## Initial Textual Design Output',
2112
+ `- Feature: ${featureName}`,
2113
+ '- Layout: premium enterprise shell with operational sidebar and command bar',
2114
+ '- Core blocks: KPI row, status cards, teams matrix, skill networks table',
2115
+ '- Interaction model: granular refresh, retry per panel, safe error boundaries',
2116
+ '- Visual governance: no token drift from active design profile',
2117
+ '',
2118
+ '## Contracts Snapshot',
2119
+ contractsSummary
2120
+ ].join('\n');
2121
+ fs.writeFileSync(outFile, draft, 'utf-8');
2122
+ console.log('🧠 AIOBUILDER: iniciando ferramenta de design automática...');
2123
+ console.log('🎨 Step 1/4: contexto DESIGN.md carregado');
2124
+ console.log('📏 Step 2/4: regras de precisão/consistência aplicadas');
2125
+ console.log('🧩 Step 3/4: contratos finais incorporados');
2126
+ console.log('✍️ Step 4/4: draft textual produzido');
2127
+ console.log(JSON.stringify({ featureName, mode, profile, prompt: `Generate UI for ${featureName} following DESIGN.md profile ${profile}`, contractsSummary, context, output: { type: 'text-draft', path: outFile } }, null, 2));
2128
+ });
2129
+ aiobuilderCmd.command('chat <mensagem...>')
2130
+ .description('Executa intents de design por linguagem natural (import, mode, generate, refine)')
2131
+ .action((mensagemArgs) => {
2132
+ const mensagem = mensagemArgs.join(' ').trim();
2133
+ const result = tryHandleDesignConversation(mensagem);
2134
+ if (!result) {
2135
+ console.log('Nenhuma intenção de design identificada. Tente: importar open design, ativar designmd perfil X, gerar interface, refinar.');
2136
+ return;
2137
+ }
2138
+ console.log(result);
2139
+ });
2140
+ // AIOBuilder CLI completion (Story 5.4 — close 10 sub-command parity gap)
2141
+ // Each sub-command delegates to SquadCreator / SkillCreator / WorkflowEngine
2142
+ // with lazy require. Maintains aditividade: pre-existing aliases (create-agent,
2143
+ // create-squad, generate-ui) keep working.
2144
+ aiobuilderCmd.command('discover')
2145
+ .description('Brownfield discovery — maps codebase + impact of a planned change')
2146
+ .option('--dry-run', 'simulate without invoking real LLMs', true)
2147
+ .action(async () => {
2148
+ const { parseWorkflowFile } = require('./orchestrator/workflow/WorkflowParser');
2149
+ const { WorkflowEngine } = require('./orchestrator/workflow/WorkflowEngine');
2150
+ const wfFile = path.resolve(__dirname, '..', 'dist-templates', 'workflows', 'brownfield-discovery.yaml');
2151
+ const r = parseWorkflowFile(wfFile);
2152
+ if (!r.ok) {
2153
+ console.log(JSON.stringify({ ok: false, error: 'workflow_load_failed', errors: r.errors }, null, 2));
2154
+ process.exitCode = 1;
2155
+ return;
2156
+ }
2157
+ const engine = new WorkflowEngine();
2158
+ const state = await engine.run(r.workflow);
2159
+ console.log(JSON.stringify({ ok: state.status === 'completed', action: 'aiobuilder.discover', state }, null, 2));
2160
+ });
2161
+ aiobuilderCmd.command('plan')
2162
+ .description('Generate plan, risks, and squad/skill picks for a goal')
2163
+ .option('--goal <goal>', 'goal description', '')
2164
+ .action(async (opts) => {
2165
+ const { parseWorkflowFile } = require('./orchestrator/workflow/WorkflowParser');
2166
+ const { WorkflowEngine } = require('./orchestrator/workflow/WorkflowEngine');
2167
+ const wfFile = path.resolve(__dirname, '..', 'dist-templates', 'workflows', 'story-development-cycle.yaml');
2168
+ const r = parseWorkflowFile(wfFile);
2169
+ if (!r.ok) {
2170
+ console.log(JSON.stringify({ ok: false, error: 'workflow_load_failed', errors: r.errors }, null, 2));
2171
+ process.exitCode = 1;
2172
+ return;
2173
+ }
2174
+ const engine = new WorkflowEngine();
2175
+ // Plan = phase 1 only (Create) — operator runs full SDC separately
2176
+ const state = await engine.run(r.workflow, { goal: opts.goal });
2177
+ console.log(JSON.stringify({ ok: state.status === 'completed', action: 'aiobuilder.plan', state }, null, 2));
2178
+ });
2179
+ aiobuilderCmd.command('build')
2180
+ .description('Execute the development cycle for a story (delegates to greenfield-fullstack workflow)')
2181
+ .option('--story <storyId>', 'story id', '')
2182
+ .action(async (opts) => {
2183
+ const { parseWorkflowFile } = require('./orchestrator/workflow/WorkflowParser');
2184
+ const { WorkflowEngine } = require('./orchestrator/workflow/WorkflowEngine');
2185
+ const wfFile = path.resolve(__dirname, '..', 'dist-templates', 'workflows', 'greenfield-fullstack.yaml');
2186
+ const r = parseWorkflowFile(wfFile);
2187
+ if (!r.ok) {
2188
+ console.log(JSON.stringify({ ok: false, error: 'workflow_load_failed', errors: r.errors }, null, 2));
2189
+ process.exitCode = 1;
2190
+ return;
2191
+ }
2192
+ const engine = new WorkflowEngine();
2193
+ const state = await engine.run(r.workflow, { storyId: opts.story });
2194
+ console.log(JSON.stringify({ ok: state.status === 'completed', action: 'aiobuilder.build', state }, null, 2));
2195
+ });
2196
+ aiobuilderCmd.command('test')
2197
+ .description('Run the canonical test suite and report results')
2198
+ .action(() => {
2199
+ const { spawnSync } = require('child_process');
2200
+ const r = spawnSync('npm', ['run', 'test:all'], { encoding: 'utf-8', cwd: process.cwd() });
2201
+ console.log(JSON.stringify({
2202
+ ok: r.status === 0,
2203
+ action: 'aiobuilder.test',
2204
+ exitCode: r.status,
2205
+ summary: r.stdout?.split('\n').filter((l) => /TEST_.*_OK/.test(l)).length || 0,
2206
+ }, null, 2));
2207
+ if (r.status !== 0)
2208
+ process.exitCode = 1;
2209
+ });
2210
+ aiobuilderCmd.command('preview')
2211
+ .description('Prepare a preview URL for the current branch (v1.3 will wire to Vercel/Railway)')
2212
+ .action(() => {
2213
+ console.log(JSON.stringify({
2214
+ ok: true,
2215
+ action: 'aiobuilder.preview',
2216
+ note: 'preview deployment integration deferred to v1.3 capability genesis epic',
2217
+ next: 'manually push to feature branch + open PR for now',
2218
+ }, null, 2));
2219
+ });
2220
+ aiobuilderCmd.command('release')
2221
+ .description('Run QA loop + open PR (delegates to qa-loop workflow)')
2222
+ .action(async () => {
2223
+ const { parseWorkflowFile } = require('./orchestrator/workflow/WorkflowParser');
2224
+ const { WorkflowEngine } = require('./orchestrator/workflow/WorkflowEngine');
2225
+ const wfFile = path.resolve(__dirname, '..', 'dist-templates', 'workflows', 'qa-loop.yaml');
2226
+ const r = parseWorkflowFile(wfFile);
2227
+ if (!r.ok) {
2228
+ console.log(JSON.stringify({ ok: false, error: 'workflow_load_failed', errors: r.errors }, null, 2));
2229
+ process.exitCode = 1;
2230
+ return;
2231
+ }
2232
+ const engine = new WorkflowEngine();
2233
+ const state = await engine.run(r.workflow);
2234
+ console.log(JSON.stringify({ ok: state.status === 'completed', action: 'aiobuilder.release', state }, null, 2));
2235
+ });
2236
+ aiobuilderCmd.command('infra')
2237
+ .description('Infrastructure report (placeholder — real integration v1.3)')
2238
+ .action(() => {
2239
+ console.log(JSON.stringify({
2240
+ ok: true,
2241
+ action: 'aiobuilder.infra',
2242
+ note: 'infra integration deferred to v1.3',
2243
+ checks: ['supabase: TBD', 'railway: TBD', 'env: TBD'],
2244
+ }, null, 2));
2245
+ });
2246
+ aiobuilderCmd.command('score')
2247
+ .description('Show squad/skill scores from .artifacts/squad-scores.json')
2248
+ .action(() => {
2249
+ const scorePath = path.join(process.cwd(), '.artifacts', 'squad-scores.json');
2250
+ if (!fs.existsSync(scorePath)) {
2251
+ console.log(JSON.stringify({ ok: true, action: 'aiobuilder.score', scores: {}, note: 'no scores recorded yet' }, null, 2));
2252
+ return;
2253
+ }
2254
+ const scores = JSON.parse(fs.readFileSync(scorePath, 'utf-8'));
2255
+ console.log(JSON.stringify({ ok: true, action: 'aiobuilder.score', scores }, null, 2));
2256
+ });
2257
+ aiobuilderCmd.command('evolve')
2258
+ .description('Run analyze() on every squad and report improvement suggestions')
2259
+ .action(() => {
2260
+ const { SquadCreator } = require('./orchestrator/SquadCreator');
2261
+ const sc = new SquadCreator();
2262
+ const squads = sc.list();
2263
+ const reports = squads.map((s) => sc.analyze(s.id)).filter(Boolean);
2264
+ console.log(JSON.stringify({
2265
+ ok: true,
2266
+ action: 'aiobuilder.evolve',
2267
+ squadCount: squads.length,
2268
+ reports,
2269
+ }, null, 2));
2270
+ });
2271
+ aiobuilderCmd.command('ship')
2272
+ .description('Full pipeline: discover → plan → build → test → release')
2273
+ .option('--dry-run', 'do not invoke real LLMs', true)
2274
+ .action(async () => {
2275
+ const { parseWorkflowFile } = require('./orchestrator/workflow/WorkflowParser');
2276
+ const { WorkflowEngine } = require('./orchestrator/workflow/WorkflowEngine');
2277
+ const stages = ['brownfield-discovery', 'story-development-cycle', 'qa-loop'];
2278
+ const engine = new WorkflowEngine();
2279
+ const results = [];
2280
+ for (const stageId of stages) {
2281
+ const wfFile = path.resolve(__dirname, '..', 'dist-templates', 'workflows', `${stageId}.yaml`);
2282
+ const r = parseWorkflowFile(wfFile);
2283
+ if (!r.ok) {
2284
+ results.push({ stage: stageId, ok: false, error: 'workflow_load_failed', errors: r.errors });
2285
+ break;
2286
+ }
2287
+ const state = await engine.run(r.workflow);
2288
+ results.push({ stage: stageId, ok: state.status === 'completed', runId: state.runId, status: state.status });
2289
+ if (state.status !== 'completed')
2290
+ break;
2291
+ }
2292
+ const allOk = results.every((r) => r.ok);
2293
+ console.log(JSON.stringify({ ok: allOk, action: 'aiobuilder.ship', stages: results }, null, 2));
2294
+ if (!allOk)
2295
+ process.exitCode = 1;
2296
+ });
2297
+ aiobuilderCmd.command('build-skill <id>')
2298
+ .description('Create a new skill from dist-templates/skill-template/')
2299
+ .option('--name <name>', 'human-readable name', '')
2300
+ .option('--description <description>', 'one-paragraph description', '')
2301
+ .option('--when <text>', 'when to use (single bullet)', 'TBD')
2302
+ .option('--step <text>', 'first procedure step', 'TBD')
2303
+ .action((id, opts) => {
2304
+ const { SkillCreator } = require('./orchestrator/SkillCreator');
2305
+ const sc = new SkillCreator();
2306
+ const r = sc.create({
2307
+ id,
2308
+ name: opts.name || id,
2309
+ description: opts.description || '',
2310
+ whenToUse: [opts.when],
2311
+ procedure: [opts.step],
2312
+ });
2313
+ console.log(JSON.stringify({ action: 'aiobuilder.build-skill', ...r }, null, 2));
2314
+ if (!r.ok)
2315
+ process.exitCode = 1;
2316
+ });
2317
+ aiobuilderCmd.command('canonize <type> <id>')
2318
+ .description('Promote a squad or skill from draft → active and record canonization event')
2319
+ .action((type, id) => {
2320
+ if (type !== 'squad' && type !== 'skill') {
2321
+ console.log(JSON.stringify({ ok: false, action: 'aiobuilder.canonize', error: 'invalid_type', detail: 'must be `squad` or `skill`' }, null, 2));
2322
+ process.exitCode = 1;
2323
+ return;
2324
+ }
2325
+ const root = type === 'squad' ? '.catalog/squads' : '.catalog/skills';
2326
+ const file = path.join(process.cwd(), root, id, type === 'squad' ? 'SQUAD.md' : 'SKILL.md');
2327
+ if (!fs.existsSync(file)) {
2328
+ console.log(JSON.stringify({ ok: false, action: 'aiobuilder.canonize', error: 'not_found', detail: file }, null, 2));
2329
+ process.exitCode = 1;
2330
+ return;
2331
+ }
2332
+ const content = fs.readFileSync(file, 'utf-8');
2333
+ const updated = content.replace(/^status:\s*draft\s*$/m, 'status: active');
2334
+ fs.writeFileSync(file, updated, 'utf-8');
2335
+ // Record canonization event
2336
+ const logFile = path.join(process.cwd(), '.openlife', 'canonization-log.jsonl');
2337
+ fs.mkdirSync(path.dirname(logFile), { recursive: true });
2338
+ fs.appendFileSync(logFile, JSON.stringify({ ts: new Date().toISOString(), type, id, action: 'canonize' }) + '\n');
2339
+ console.log(JSON.stringify({ ok: true, action: 'aiobuilder.canonize', type, id }, null, 2));
2340
+ });
2341
+ // ─── Story 3.3: aiobuilder create-capability ─────────────────────────
2342
+ aiobuilderCmd.command('create-capability <description>')
2343
+ .description('Genesis Engine: produce a draft Capability Pack from a free-text brief')
2344
+ .option('--id <id>', 'explicit pack id (slug)')
2345
+ .option('--mode <mode>', 'quick | professional | elite', 'professional')
2346
+ .option('--actor <actor>', 'actor recorded in the lifecycle ledger', 'cli')
2347
+ .option('--skills <csv>', 'comma-separated skill ids to include')
2348
+ .option('--squads <csv>', 'comma-separated squad ids to include')
2349
+ .option('--workflows <csv>', 'comma-separated workflow ids to include (elite mode only)')
2350
+ .action((description, opts) => {
2351
+ const { CapabilityGenesisEngine } = require('./orchestrator/capability/CapabilityGenesisEngine');
2352
+ const engine = new CapabilityGenesisEngine();
2353
+ const csv = (s) => s ? s.split(',').map((x) => x.trim()).filter(Boolean) : [];
2354
+ const r = engine.generate({
2355
+ description,
2356
+ packId: opts.id,
2357
+ mode: opts.mode,
2358
+ actor: opts.actor || 'cli',
2359
+ hints: {
2360
+ skills: csv(opts.skills),
2361
+ squads: csv(opts.squads),
2362
+ workflows: csv(opts.workflows),
2363
+ },
2364
+ });
2365
+ console.log(JSON.stringify(r, null, 2));
2366
+ if (!r.ok)
2367
+ process.exitCode = 1;
2368
+ });
2369
+ // ─── Story 3.2: top-level `openlife create …` aliases ────────────────
2370
+ // Thin forwarders to aiobuilder verbs. AIOBUILDER remains canonical maestro
2371
+ // per locked decision 1 in plan; this is just a friendlier help surface.
2372
+ const createCmd = program.command('create').description('Guided creator surface (forwards to aiobuilder)');
2373
+ createCmd.command('capability <description>')
2374
+ .description('Forward to: openlife aiobuilder create-capability <description>')
2375
+ .option('--id <id>', 'explicit pack id')
2376
+ .option('--mode <mode>', 'quick | professional | elite', 'professional')
2377
+ .option('--actor <actor>', 'actor for lifecycle ledger', 'cli')
2378
+ .option('--skills <csv>', 'comma-separated skill ids')
2379
+ .option('--squads <csv>', 'comma-separated squad ids')
2380
+ .option('--workflows <csv>', 'comma-separated workflow ids (elite only)')
2381
+ .action((description, opts) => {
2382
+ const { CapabilityGenesisEngine } = require('./orchestrator/capability/CapabilityGenesisEngine');
2383
+ const engine = new CapabilityGenesisEngine();
2384
+ const csv = (s) => s ? s.split(',').map((x) => x.trim()).filter(Boolean) : [];
2385
+ const r = engine.generate({
2386
+ description,
2387
+ packId: opts.id,
2388
+ mode: opts.mode || 'professional',
2389
+ actor: opts.actor || 'cli',
2390
+ hints: { skills: csv(opts.skills), squads: csv(opts.squads), workflows: csv(opts.workflows) },
2391
+ });
2392
+ console.log(JSON.stringify(r, null, 2));
2393
+ if (!r.ok)
2394
+ process.exitCode = 1;
2395
+ });
2396
+ createCmd.command('squad <id>')
2397
+ .description('Forward to: openlife aiobuilder create-squad <id>')
2398
+ .option('--description <text>', 'short squad description', 'Created via openlife create squad')
2399
+ .option('--agents <csv>', 'comma-separated agent ids (defaults to <id>-lead,<id>-executor)')
2400
+ .action((id, opts) => {
2401
+ const { SquadCreator } = require('./orchestrator/SquadCreator');
2402
+ const sc = new SquadCreator();
2403
+ const agentIds = opts.agents ? opts.agents.split(',').map((s) => s.trim()).filter(Boolean) : [`${id}-lead`, `${id}-executor`];
2404
+ const r = sc.create({
2405
+ id,
2406
+ name: id,
2407
+ description: opts.description || `Squad ${id}`,
2408
+ agents: agentIds.map((aid) => ({ id: aid, name: aid, role: aid.endsWith('-lead') ? 'lead' : 'executor' })),
2409
+ });
2410
+ console.log(JSON.stringify(r, null, 2));
2411
+ if (!r.ok)
2412
+ process.exitCode = 1;
2413
+ });
2414
+ createCmd.command('skill <id>')
2415
+ .description('Forward to: openlife aiobuilder build-skill <id>')
2416
+ .option('--description <text>', 'short skill description', 'Created via openlife create skill')
2417
+ .action((id, opts) => {
2418
+ const { SkillCreator } = require('./orchestrator/SkillCreator');
2419
+ const sc = new SkillCreator();
2420
+ const r = sc.create({
2421
+ id, name: id, description: opts.description || `Skill ${id}`,
2422
+ whenToUse: [opts.description || `use ${id}`],
2423
+ procedure: ['TBD — refine after creation'],
2424
+ });
2425
+ console.log(JSON.stringify(r, null, 2));
2426
+ if (!r.ok)
2427
+ process.exitCode = 1;
2428
+ });
2429
+ createCmd.command('agent <id>')
2430
+ .description('Forward to: openlife aiobuilder create-agent <id>')
2431
+ .action((id) => {
2432
+ // Forward to the existing aiobuilder create-agent handler shape.
2433
+ // We reuse its implementation by calling commander programmatically.
2434
+ program.parseAsync(['node', 'openlife', 'aiobuilder', 'create-agent', id]).catch(() => { process.exitCode = 1; });
2435
+ });
2436
+ createCmd.command('workflow <id>')
2437
+ .description('Stub: workflow creation lands via capability pack genesis (use create capability)')
2438
+ .action((id) => {
2439
+ console.log(JSON.stringify({
2440
+ ok: false,
2441
+ error: 'use_create_capability',
2442
+ hint: `workflows are authored as part of a capability pack; run: openlife create capability "<brief>" --mode elite --workflows ${id}`,
2443
+ }, null, 2));
2444
+ process.exitCode = 1;
2445
+ });
2446
+ // ─── Story 5.1: openlife cron ────────────────────────────────────────
2447
+ const cronCmd = program.command('cron').description('CronManager — schedule recurring missions');
2448
+ cronCmd.command('create <id> <expression>')
2449
+ .description('Create a cron job (5-field syntax)')
2450
+ .option('--payload <json>', 'JSON payload to fire with', '{}')
2451
+ .action((id, expression, opts) => {
2452
+ try {
2453
+ const { CronManager } = require('./orchestrator/CronManager');
2454
+ const cm = new CronManager();
2455
+ let payload = {};
2456
+ try {
2457
+ payload = JSON.parse(opts.payload || '{}');
2458
+ }
2459
+ catch { /* keep empty */ }
2460
+ const job = cm.create(id, expression, payload);
2461
+ console.log(JSON.stringify({ ok: true, job }, null, 2));
2462
+ }
2463
+ catch (e) {
2464
+ const code = e.code || 'unknown';
2465
+ console.log(JSON.stringify({ ok: false, error: code, detail: e.message }, null, 2));
2466
+ process.exitCode = 1;
2467
+ }
2468
+ });
2469
+ cronCmd.command('list').description('List cron jobs').action(() => {
2470
+ const { CronManager } = require('./orchestrator/CronManager');
2471
+ const cm = new CronManager();
2472
+ console.log(JSON.stringify({ ok: true, jobs: cm.list() }, null, 2));
2473
+ });
2474
+ cronCmd.command('pause <id>').description('Pause a cron job').action((id) => {
2475
+ const { CronManager } = require('./orchestrator/CronManager');
2476
+ const cm = new CronManager();
2477
+ const ok = cm.pause(id);
2478
+ console.log(JSON.stringify({ ok, id }, null, 2));
2479
+ if (!ok)
2480
+ process.exitCode = 1;
2481
+ });
2482
+ cronCmd.command('resume <id>').description('Resume a paused cron job').action((id) => {
2483
+ const { CronManager } = require('./orchestrator/CronManager');
2484
+ const cm = new CronManager();
2485
+ const ok = cm.resume(id);
2486
+ console.log(JSON.stringify({ ok, id }, null, 2));
2487
+ if (!ok)
2488
+ process.exitCode = 1;
2489
+ });
2490
+ cronCmd.command('run <id>').description('Fire a cron job immediately').action(async (id) => {
2491
+ const { CronManager } = require('./orchestrator/CronManager');
2492
+ const cm = new CronManager();
2493
+ const r = await cm.runNow(id);
2494
+ console.log(JSON.stringify(r, null, 2));
2495
+ if (!r.ok)
2496
+ process.exitCode = 1;
2497
+ });
2498
+ cronCmd.command('remove <id>').description('Remove a cron job').action((id) => {
2499
+ const { CronManager } = require('./orchestrator/CronManager');
2500
+ const cm = new CronManager();
2501
+ const ok = cm.remove(id);
2502
+ console.log(JSON.stringify({ ok, id }, null, 2));
2503
+ if (!ok)
2504
+ process.exitCode = 1;
2505
+ });
2506
+ // ─── Story 5.2: openlife profile ────────────────────────────────────
2507
+ const profileCmd = program.command('profile').description('ProfileManager — per-profile catalog scope, memory namespace, toolsets');
2508
+ profileCmd.command('list').description('List profiles').action(() => {
2509
+ const { ProfileManager } = require('./cli/ProfileManager');
2510
+ const pm = new ProfileManager();
2511
+ const active = pm.active();
2512
+ console.log(JSON.stringify({ ok: true, activeProfileId: active.profileId, profiles: pm.list() }, null, 2));
2513
+ });
2514
+ profileCmd.command('create <id>').description('Create a profile').option('--name <name>', 'human-readable name').option('--description <desc>').option('--toolsets <csv>', 'allowed toolsets', '*')
2515
+ .action((id, opts) => {
2516
+ try {
2517
+ const { ProfileManager } = require('./cli/ProfileManager');
2518
+ const pm = new ProfileManager();
2519
+ const toolsetAllowed = (opts.toolsets || '*').split(',').map((s) => s.trim()).filter(Boolean);
2520
+ const p = pm.create({ id, name: opts.name || id, description: opts.description, toolsetAllowed });
2521
+ console.log(JSON.stringify({ ok: true, profile: p }, null, 2));
2522
+ }
2523
+ catch (e) {
2524
+ const code = e.code || 'unknown';
2525
+ console.log(JSON.stringify({ ok: false, error: code, detail: e.message }, null, 2));
2526
+ process.exitCode = 1;
2527
+ }
2528
+ });
2529
+ profileCmd.command('use <id>').description('Set the active profile').action((id) => {
2530
+ const { ProfileManager } = require('./cli/ProfileManager');
2531
+ const pm = new ProfileManager();
2532
+ const r = pm.use(id);
2533
+ console.log(JSON.stringify(r, null, 2));
2534
+ if (!r.ok)
2535
+ process.exitCode = 1;
2536
+ });
2537
+ profileCmd.command('show <id>').description('Show a profile').action((id) => {
2538
+ const { ProfileManager } = require('./cli/ProfileManager');
2539
+ const pm = new ProfileManager();
2540
+ const p = pm.show(id);
2541
+ if (!p) {
2542
+ console.log(JSON.stringify({ ok: false, error: 'not_found' }, null, 2));
2543
+ process.exitCode = 1;
2544
+ return;
2545
+ }
2546
+ console.log(JSON.stringify({ ok: true, profile: p }, null, 2));
2547
+ });
2548
+ profileCmd.command('export <id>').description('Export a profile as JSON to stdout').action((id) => {
2549
+ const { ProfileManager } = require('./cli/ProfileManager');
2550
+ const pm = new ProfileManager();
2551
+ const p = pm.export(id);
2552
+ if (!p) {
2553
+ console.log(JSON.stringify({ ok: false, error: 'not_found' }, null, 2));
2554
+ process.exitCode = 1;
2555
+ return;
2556
+ }
2557
+ console.log(JSON.stringify(p, null, 2));
2558
+ });
2559
+ profileCmd.command('import <file>').description('Import a profile from a JSON file').action((file) => {
2560
+ try {
2561
+ const { ProfileManager } = require('./cli/ProfileManager');
2562
+ const fsLocal = require('fs');
2563
+ const data = JSON.parse(fsLocal.readFileSync(file, 'utf-8'));
2564
+ const pm = new ProfileManager();
2565
+ const r = pm.import(data);
2566
+ console.log(JSON.stringify(r, null, 2));
2567
+ if (!r.ok)
2568
+ process.exitCode = 1;
2569
+ }
2570
+ catch (e) {
2571
+ console.log(JSON.stringify({ ok: false, error: 'import_failed', detail: e.message }, null, 2));
2572
+ process.exitCode = 1;
2573
+ }
2574
+ });
2575
+ program.parse(process.argv);