verbolab 0.1.0-alpha.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +71 -0
- package/dist/bin/verbo.d.ts +3 -0
- package/dist/bin/verbo.d.ts.map +1 -0
- package/dist/bin/verbo.js +1156 -0
- package/dist/bin/verbo.js.map +1 -0
- package/dist/relay/index.d.ts +2 -0
- package/dist/relay/index.d.ts.map +1 -0
- package/dist/relay/index.js +15 -0
- package/dist/relay/index.js.map +1 -0
- package/dist/src/agent/agent-launcher.d.ts +64 -0
- package/dist/src/agent/agent-launcher.d.ts.map +1 -0
- package/dist/src/agent/agent-launcher.js +326 -0
- package/dist/src/agent/agent-launcher.js.map +1 -0
- package/dist/src/agent/agent-monitor.d.ts +16 -0
- package/dist/src/agent/agent-monitor.d.ts.map +1 -0
- package/dist/src/agent/agent-monitor.js +41 -0
- package/dist/src/agent/agent-monitor.js.map +1 -0
- package/dist/src/agent/fun-names.d.ts +9 -0
- package/dist/src/agent/fun-names.d.ts.map +1 -0
- package/dist/src/agent/fun-names.js +118 -0
- package/dist/src/agent/fun-names.js.map +1 -0
- package/dist/src/agent/mcp-shim.d.ts +45 -0
- package/dist/src/agent/mcp-shim.d.ts.map +1 -0
- package/dist/src/agent/mcp-shim.js +192 -0
- package/dist/src/agent/mcp-shim.js.map +1 -0
- package/dist/src/agent/personas.d.ts +47 -0
- package/dist/src/agent/personas.d.ts.map +1 -0
- package/dist/src/agent/personas.js +86 -0
- package/dist/src/agent/personas.js.map +1 -0
- package/dist/src/agent/provider-detection.d.ts +21 -0
- package/dist/src/agent/provider-detection.d.ts.map +1 -0
- package/dist/src/agent/provider-detection.js +47 -0
- package/dist/src/agent/provider-detection.js.map +1 -0
- package/dist/src/agent/providers/claude-provider.d.ts +3 -0
- package/dist/src/agent/providers/claude-provider.d.ts.map +1 -0
- package/dist/src/agent/providers/claude-provider.js +119 -0
- package/dist/src/agent/providers/claude-provider.js.map +1 -0
- package/dist/src/agent/providers/gemini-provider.d.ts +13 -0
- package/dist/src/agent/providers/gemini-provider.d.ts.map +1 -0
- package/dist/src/agent/providers/gemini-provider.js +143 -0
- package/dist/src/agent/providers/gemini-provider.js.map +1 -0
- package/dist/src/agent/providers/openai-provider.d.ts +3 -0
- package/dist/src/agent/providers/openai-provider.d.ts.map +1 -0
- package/dist/src/agent/providers/openai-provider.js +127 -0
- package/dist/src/agent/providers/openai-provider.js.map +1 -0
- package/dist/src/agent/providers/registry.d.ts +19 -0
- package/dist/src/agent/providers/registry.d.ts.map +1 -0
- package/dist/src/agent/providers/registry.js +30 -0
- package/dist/src/agent/providers/registry.js.map +1 -0
- package/dist/src/agent/providers/types.d.ts +118 -0
- package/dist/src/agent/providers/types.d.ts.map +1 -0
- package/dist/src/agent/providers/types.js +2 -0
- package/dist/src/agent/providers/types.js.map +1 -0
- package/dist/src/approval/approval-server.d.ts +17 -0
- package/dist/src/approval/approval-server.d.ts.map +1 -0
- package/dist/src/approval/approval-server.js +90 -0
- package/dist/src/approval/approval-server.js.map +1 -0
- package/dist/src/approval/approval-store.d.ts +29 -0
- package/dist/src/approval/approval-store.d.ts.map +1 -0
- package/dist/src/approval/approval-store.js +94 -0
- package/dist/src/approval/approval-store.js.map +1 -0
- package/dist/src/auth/auth-store.d.ts +18 -0
- package/dist/src/auth/auth-store.d.ts.map +1 -0
- package/dist/src/auth/auth-store.js +34 -0
- package/dist/src/auth/auth-store.js.map +1 -0
- package/dist/src/auth/device-code-client.d.ts +32 -0
- package/dist/src/auth/device-code-client.d.ts.map +1 -0
- package/dist/src/auth/device-code-client.js +41 -0
- package/dist/src/auth/device-code-client.js.map +1 -0
- package/dist/src/auth/plan-enforcer.d.ts +8 -0
- package/dist/src/auth/plan-enforcer.d.ts.map +1 -0
- package/dist/src/auth/plan-enforcer.js +14 -0
- package/dist/src/auth/plan-enforcer.js.map +1 -0
- package/dist/src/commands/audit.d.ts +7 -0
- package/dist/src/commands/audit.d.ts.map +1 -0
- package/dist/src/commands/audit.js +92 -0
- package/dist/src/commands/audit.js.map +1 -0
- package/dist/src/commands/team.d.ts +48 -0
- package/dist/src/commands/team.d.ts.map +1 -0
- package/dist/src/commands/team.js +175 -0
- package/dist/src/commands/team.js.map +1 -0
- package/dist/src/config/verbo-config.d.ts +43 -0
- package/dist/src/config/verbo-config.d.ts.map +1 -0
- package/dist/src/config/verbo-config.js +111 -0
- package/dist/src/config/verbo-config.js.map +1 -0
- package/dist/src/core/agent-session-store.d.ts +69 -0
- package/dist/src/core/agent-session-store.d.ts.map +1 -0
- package/dist/src/core/agent-session-store.js +168 -0
- package/dist/src/core/agent-session-store.js.map +1 -0
- package/dist/src/core/audit-log-store.d.ts +33 -0
- package/dist/src/core/audit-log-store.d.ts.map +1 -0
- package/dist/src/core/audit-log-store.js +104 -0
- package/dist/src/core/audit-log-store.js.map +1 -0
- package/dist/src/core/compliance.d.ts +50 -0
- package/dist/src/core/compliance.d.ts.map +1 -0
- package/dist/src/core/compliance.js +59 -0
- package/dist/src/core/compliance.js.map +1 -0
- package/dist/src/core/conflict-detector.d.ts +19 -0
- package/dist/src/core/conflict-detector.d.ts.map +1 -0
- package/dist/src/core/conflict-detector.js +87 -0
- package/dist/src/core/conflict-detector.js.map +1 -0
- package/dist/src/core/conflict-enforcer.d.ts +37 -0
- package/dist/src/core/conflict-enforcer.d.ts.map +1 -0
- package/dist/src/core/conflict-enforcer.js +139 -0
- package/dist/src/core/conflict-enforcer.js.map +1 -0
- package/dist/src/core/cost-store.d.ts +55 -0
- package/dist/src/core/cost-store.d.ts.map +1 -0
- package/dist/src/core/cost-store.js +140 -0
- package/dist/src/core/cost-store.js.map +1 -0
- package/dist/src/core/hot-files.d.ts +19 -0
- package/dist/src/core/hot-files.d.ts.map +1 -0
- package/dist/src/core/hot-files.js +64 -0
- package/dist/src/core/hot-files.js.map +1 -0
- package/dist/src/core/human-action-store.d.ts +33 -0
- package/dist/src/core/human-action-store.d.ts.map +1 -0
- package/dist/src/core/human-action-store.js +92 -0
- package/dist/src/core/human-action-store.js.map +1 -0
- package/dist/src/core/learning-store.d.ts +32 -0
- package/dist/src/core/learning-store.d.ts.map +1 -0
- package/dist/src/core/learning-store.js +95 -0
- package/dist/src/core/learning-store.js.map +1 -0
- package/dist/src/core/merge-queue.d.ts +28 -0
- package/dist/src/core/merge-queue.d.ts.map +1 -0
- package/dist/src/core/merge-queue.js +92 -0
- package/dist/src/core/merge-queue.js.map +1 -0
- package/dist/src/core/notification-service.d.ts +13 -0
- package/dist/src/core/notification-service.d.ts.map +1 -0
- package/dist/src/core/notification-service.js +126 -0
- package/dist/src/core/notification-service.js.map +1 -0
- package/dist/src/core/notifications.d.ts +10 -0
- package/dist/src/core/notifications.d.ts.map +1 -0
- package/dist/src/core/notifications.js +33 -0
- package/dist/src/core/notifications.js.map +1 -0
- package/dist/src/core/orchestrator-store.d.ts +44 -0
- package/dist/src/core/orchestrator-store.d.ts.map +1 -0
- package/dist/src/core/orchestrator-store.js +69 -0
- package/dist/src/core/orchestrator-store.js.map +1 -0
- package/dist/src/core/parallelizer.d.ts +47 -0
- package/dist/src/core/parallelizer.d.ts.map +1 -0
- package/dist/src/core/parallelizer.js +224 -0
- package/dist/src/core/parallelizer.js.map +1 -0
- package/dist/src/core/pipeline-rollback.d.ts +29 -0
- package/dist/src/core/pipeline-rollback.d.ts.map +1 -0
- package/dist/src/core/pipeline-rollback.js +84 -0
- package/dist/src/core/pipeline-rollback.js.map +1 -0
- package/dist/src/core/pipeline-runner.d.ts +73 -0
- package/dist/src/core/pipeline-runner.d.ts.map +1 -0
- package/dist/src/core/pipeline-runner.js +165 -0
- package/dist/src/core/pipeline-runner.js.map +1 -0
- package/dist/src/core/pr-creator.d.ts +22 -0
- package/dist/src/core/pr-creator.d.ts.map +1 -0
- package/dist/src/core/pr-creator.js +55 -0
- package/dist/src/core/pr-creator.js.map +1 -0
- package/dist/src/core/rbac.d.ts +27 -0
- package/dist/src/core/rbac.d.ts.map +1 -0
- package/dist/src/core/rbac.js +76 -0
- package/dist/src/core/rbac.js.map +1 -0
- package/dist/src/core/startup-cleanup.d.ts +47 -0
- package/dist/src/core/startup-cleanup.d.ts.map +1 -0
- package/dist/src/core/startup-cleanup.js +150 -0
- package/dist/src/core/startup-cleanup.js.map +1 -0
- package/dist/src/core/task-store.d.ts +96 -0
- package/dist/src/core/task-store.d.ts.map +1 -0
- package/dist/src/core/task-store.js +309 -0
- package/dist/src/core/task-store.js.map +1 -0
- package/dist/src/core/verbo-config-editor.d.ts +44 -0
- package/dist/src/core/verbo-config-editor.d.ts.map +1 -0
- package/dist/src/core/verbo-config-editor.js +204 -0
- package/dist/src/core/verbo-config-editor.js.map +1 -0
- package/dist/src/core/verbo-config.d.ts +35 -0
- package/dist/src/core/verbo-config.d.ts.map +1 -0
- package/dist/src/core/verbo-config.js +55 -0
- package/dist/src/core/verbo-config.js.map +1 -0
- package/dist/src/core/verbo-md.d.ts +96 -0
- package/dist/src/core/verbo-md.d.ts.map +1 -0
- package/dist/src/core/verbo-md.js +410 -0
- package/dist/src/core/verbo-md.js.map +1 -0
- package/dist/src/db/database.d.ts +9 -0
- package/dist/src/db/database.d.ts.map +1 -0
- package/dist/src/db/database.js +37 -0
- package/dist/src/db/database.js.map +1 -0
- package/dist/src/db/migrations/001-personas-subtasks.d.ts +10 -0
- package/dist/src/db/migrations/001-personas-subtasks.d.ts.map +1 -0
- package/dist/src/db/migrations/001-personas-subtasks.js +32 -0
- package/dist/src/db/migrations/001-personas-subtasks.js.map +1 -0
- package/dist/src/db/migrations/002-rbac.d.ts +9 -0
- package/dist/src/db/migrations/002-rbac.d.ts.map +1 -0
- package/dist/src/db/migrations/002-rbac.js +31 -0
- package/dist/src/db/migrations/002-rbac.js.map +1 -0
- package/dist/src/db/migrations/003-provider-column.d.ts +8 -0
- package/dist/src/db/migrations/003-provider-column.d.ts.map +1 -0
- package/dist/src/db/migrations/003-provider-column.js +12 -0
- package/dist/src/db/migrations/003-provider-column.js.map +1 -0
- package/dist/src/db/migrations/004-pipeline-cost.d.ts +8 -0
- package/dist/src/db/migrations/004-pipeline-cost.d.ts.map +1 -0
- package/dist/src/db/migrations/004-pipeline-cost.js +23 -0
- package/dist/src/db/migrations/004-pipeline-cost.js.map +1 -0
- package/dist/src/db/migrations/005-audit-log-timestamp.d.ts +10 -0
- package/dist/src/db/migrations/005-audit-log-timestamp.d.ts.map +1 -0
- package/dist/src/db/migrations/005-audit-log-timestamp.js +39 -0
- package/dist/src/db/migrations/005-audit-log-timestamp.js.map +1 -0
- package/dist/src/db/migrations/006-human-action-type.d.ts +9 -0
- package/dist/src/db/migrations/006-human-action-type.d.ts.map +1 -0
- package/dist/src/db/migrations/006-human-action-type.js +16 -0
- package/dist/src/db/migrations/006-human-action-type.js.map +1 -0
- package/dist/src/db/schema.d.ts +6 -0
- package/dist/src/db/schema.d.ts.map +1 -0
- package/dist/src/db/schema.js +255 -0
- package/dist/src/db/schema.js.map +1 -0
- package/dist/src/deps/dependabot-generator.d.ts +22 -0
- package/dist/src/deps/dependabot-generator.d.ts.map +1 -0
- package/dist/src/deps/dependabot-generator.js +83 -0
- package/dist/src/deps/dependabot-generator.js.map +1 -0
- package/dist/src/deps/dependabot-monitor.d.ts +22 -0
- package/dist/src/deps/dependabot-monitor.d.ts.map +1 -0
- package/dist/src/deps/dependabot-monitor.js +28 -0
- package/dist/src/deps/dependabot-monitor.js.map +1 -0
- package/dist/src/deps/package-auditor.d.ts +24 -0
- package/dist/src/deps/package-auditor.d.ts.map +1 -0
- package/dist/src/deps/package-auditor.js +118 -0
- package/dist/src/deps/package-auditor.js.map +1 -0
- package/dist/src/init/ensure-init.d.ts +6 -0
- package/dist/src/init/ensure-init.d.ts.map +1 -0
- package/dist/src/init/ensure-init.js +27 -0
- package/dist/src/init/ensure-init.js.map +1 -0
- package/dist/src/init/project-detector.d.ts +11 -0
- package/dist/src/init/project-detector.d.ts.map +1 -0
- package/dist/src/init/project-detector.js +117 -0
- package/dist/src/init/project-detector.js.map +1 -0
- package/dist/src/init/questionnaire.d.ts +5 -0
- package/dist/src/init/questionnaire.d.ts.map +1 -0
- package/dist/src/init/questionnaire.js +112 -0
- package/dist/src/init/questionnaire.js.map +1 -0
- package/dist/src/init/template.d.ts +17 -0
- package/dist/src/init/template.d.ts.map +1 -0
- package/dist/src/init/template.js +46 -0
- package/dist/src/init/template.js.map +1 -0
- package/dist/src/intelligence/claude-client.d.ts +3 -0
- package/dist/src/intelligence/claude-client.d.ts.map +1 -0
- package/dist/src/intelligence/claude-client.js +12 -0
- package/dist/src/intelligence/claude-client.js.map +1 -0
- package/dist/src/intelligence/conflict-mediator.d.ts +17 -0
- package/dist/src/intelligence/conflict-mediator.d.ts.map +1 -0
- package/dist/src/intelligence/conflict-mediator.js +83 -0
- package/dist/src/intelligence/conflict-mediator.js.map +1 -0
- package/dist/src/intelligence/github-orchestrator.d.ts +30 -0
- package/dist/src/intelligence/github-orchestrator.d.ts.map +1 -0
- package/dist/src/intelligence/github-orchestrator.js +270 -0
- package/dist/src/intelligence/github-orchestrator.js.map +1 -0
- package/dist/src/intelligence/kill-switch.d.ts +10 -0
- package/dist/src/intelligence/kill-switch.d.ts.map +1 -0
- package/dist/src/intelligence/kill-switch.js +12 -0
- package/dist/src/intelligence/kill-switch.js.map +1 -0
- package/dist/src/intelligence/learnings-extractor.d.ts +13 -0
- package/dist/src/intelligence/learnings-extractor.d.ts.map +1 -0
- package/dist/src/intelligence/learnings-extractor.js +74 -0
- package/dist/src/intelligence/learnings-extractor.js.map +1 -0
- package/dist/src/intelligence/project-scanner.d.ts +2 -0
- package/dist/src/intelligence/project-scanner.d.ts.map +1 -0
- package/dist/src/intelligence/project-scanner.js +28 -0
- package/dist/src/intelligence/project-scanner.js.map +1 -0
- package/dist/src/intelligence/task-decomposer.d.ts +14 -0
- package/dist/src/intelligence/task-decomposer.d.ts.map +1 -0
- package/dist/src/intelligence/task-decomposer.js +97 -0
- package/dist/src/intelligence/task-decomposer.js.map +1 -0
- package/dist/src/intelligence/types.d.ts +14 -0
- package/dist/src/intelligence/types.d.ts.map +1 -0
- package/dist/src/intelligence/types.js +2 -0
- package/dist/src/intelligence/types.js.map +1 -0
- package/dist/src/mcp/server.d.ts +63 -0
- package/dist/src/mcp/server.d.ts.map +1 -0
- package/dist/src/mcp/server.js +537 -0
- package/dist/src/mcp/server.js.map +1 -0
- package/dist/src/mcp/stdio-entry.d.ts +2 -0
- package/dist/src/mcp/stdio-entry.d.ts.map +1 -0
- package/dist/src/mcp/stdio-entry.js +124 -0
- package/dist/src/mcp/stdio-entry.js.map +1 -0
- package/dist/src/mcp/subtask-handlers.d.ts +7 -0
- package/dist/src/mcp/subtask-handlers.d.ts.map +1 -0
- package/dist/src/mcp/subtask-handlers.js +50 -0
- package/dist/src/mcp/subtask-handlers.js.map +1 -0
- package/dist/src/security/network-proxy.d.ts +20 -0
- package/dist/src/security/network-proxy.d.ts.map +1 -0
- package/dist/src/security/network-proxy.js +125 -0
- package/dist/src/security/network-proxy.js.map +1 -0
- package/dist/src/security/network-sandbox.d.ts +19 -0
- package/dist/src/security/network-sandbox.d.ts.map +1 -0
- package/dist/src/security/network-sandbox.js +100 -0
- package/dist/src/security/network-sandbox.js.map +1 -0
- package/dist/src/security/sanitize.d.ts +13 -0
- package/dist/src/security/sanitize.d.ts.map +1 -0
- package/dist/src/security/sanitize.js +19 -0
- package/dist/src/security/sanitize.js.map +1 -0
- package/dist/src/security/secrets-patterns.d.ts +29 -0
- package/dist/src/security/secrets-patterns.d.ts.map +1 -0
- package/dist/src/security/secrets-patterns.js +430 -0
- package/dist/src/security/secrets-patterns.js.map +1 -0
- package/dist/src/security/secrets-scanner.d.ts +26 -0
- package/dist/src/security/secrets-scanner.d.ts.map +1 -0
- package/dist/src/security/secrets-scanner.js +62 -0
- package/dist/src/security/secrets-scanner.js.map +1 -0
- package/dist/src/skills/classifier.d.ts +9 -0
- package/dist/src/skills/classifier.d.ts.map +1 -0
- package/dist/src/skills/classifier.js +41 -0
- package/dist/src/skills/classifier.js.map +1 -0
- package/dist/src/skills/registry.d.ts +16 -0
- package/dist/src/skills/registry.d.ts.map +1 -0
- package/dist/src/skills/registry.js +61 -0
- package/dist/src/skills/registry.js.map +1 -0
- package/dist/src/sync/events.d.ts +9 -0
- package/dist/src/sync/events.d.ts.map +1 -0
- package/dist/src/sync/events.js +2 -0
- package/dist/src/sync/events.js.map +1 -0
- package/dist/src/sync/relay-server.d.ts +18 -0
- package/dist/src/sync/relay-server.d.ts.map +1 -0
- package/dist/src/sync/relay-server.js +131 -0
- package/dist/src/sync/relay-server.js.map +1 -0
- package/dist/src/sync/sync-client.d.ts +31 -0
- package/dist/src/sync/sync-client.d.ts.map +1 -0
- package/dist/src/sync/sync-client.js +314 -0
- package/dist/src/sync/sync-client.js.map +1 -0
- package/dist/src/tui/app.d.ts +35 -0
- package/dist/src/tui/app.d.ts.map +1 -0
- package/dist/src/tui/app.js +676 -0
- package/dist/src/tui/app.js.map +1 -0
- package/dist/src/tui/components/activity-feed.d.ts +12 -0
- package/dist/src/tui/components/activity-feed.d.ts.map +1 -0
- package/dist/src/tui/components/activity-feed.js +82 -0
- package/dist/src/tui/components/activity-feed.js.map +1 -0
- package/dist/src/tui/components/agent-list.d.ts +19 -0
- package/dist/src/tui/components/agent-list.d.ts.map +1 -0
- package/dist/src/tui/components/agent-list.js +33 -0
- package/dist/src/tui/components/agent-list.js.map +1 -0
- package/dist/src/tui/components/agent-row.d.ts +12 -0
- package/dist/src/tui/components/agent-row.d.ts.map +1 -0
- package/dist/src/tui/components/agent-row.js +37 -0
- package/dist/src/tui/components/agent-row.js.map +1 -0
- package/dist/src/tui/components/approval-terminal.d.ts +13 -0
- package/dist/src/tui/components/approval-terminal.d.ts.map +1 -0
- package/dist/src/tui/components/approval-terminal.js +34 -0
- package/dist/src/tui/components/approval-terminal.js.map +1 -0
- package/dist/src/tui/components/audit-viewer.d.ts +16 -0
- package/dist/src/tui/components/audit-viewer.d.ts.map +1 -0
- package/dist/src/tui/components/audit-viewer.js +46 -0
- package/dist/src/tui/components/audit-viewer.js.map +1 -0
- package/dist/src/tui/components/auth-gate.d.ts +9 -0
- package/dist/src/tui/components/auth-gate.d.ts.map +1 -0
- package/dist/src/tui/components/auth-gate.js +112 -0
- package/dist/src/tui/components/auth-gate.js.map +1 -0
- package/dist/src/tui/components/command-palette.d.ts +12 -0
- package/dist/src/tui/components/command-palette.d.ts.map +1 -0
- package/dist/src/tui/components/command-palette.js +51 -0
- package/dist/src/tui/components/command-palette.js.map +1 -0
- package/dist/src/tui/components/compliance-badge.d.ts +11 -0
- package/dist/src/tui/components/compliance-badge.d.ts.map +1 -0
- package/dist/src/tui/components/compliance-badge.js +12 -0
- package/dist/src/tui/components/compliance-badge.js.map +1 -0
- package/dist/src/tui/components/decomposition-review.d.ts +10 -0
- package/dist/src/tui/components/decomposition-review.d.ts.map +1 -0
- package/dist/src/tui/components/decomposition-review.js +19 -0
- package/dist/src/tui/components/decomposition-review.js.map +1 -0
- package/dist/src/tui/components/dependency-graph.d.ts +13 -0
- package/dist/src/tui/components/dependency-graph.d.ts.map +1 -0
- package/dist/src/tui/components/dependency-graph.js +143 -0
- package/dist/src/tui/components/dependency-graph.js.map +1 -0
- package/dist/src/tui/components/diff-viewer.d.ts +11 -0
- package/dist/src/tui/components/diff-viewer.d.ts.map +1 -0
- package/dist/src/tui/components/diff-viewer.js +82 -0
- package/dist/src/tui/components/diff-viewer.js.map +1 -0
- package/dist/src/tui/components/edit-task-input.d.ts +10 -0
- package/dist/src/tui/components/edit-task-input.d.ts.map +1 -0
- package/dist/src/tui/components/edit-task-input.js +20 -0
- package/dist/src/tui/components/edit-task-input.js.map +1 -0
- package/dist/src/tui/components/footer.d.ts +12 -0
- package/dist/src/tui/components/footer.d.ts.map +1 -0
- package/dist/src/tui/components/footer.js +41 -0
- package/dist/src/tui/components/footer.js.map +1 -0
- package/dist/src/tui/components/header.d.ts +8 -0
- package/dist/src/tui/components/header.d.ts.map +1 -0
- package/dist/src/tui/components/header.js +20 -0
- package/dist/src/tui/components/header.js.map +1 -0
- package/dist/src/tui/components/human-actions.d.ts +14 -0
- package/dist/src/tui/components/human-actions.d.ts.map +1 -0
- package/dist/src/tui/components/human-actions.js +43 -0
- package/dist/src/tui/components/human-actions.js.map +1 -0
- package/dist/src/tui/components/log-panel.d.ts +10 -0
- package/dist/src/tui/components/log-panel.d.ts.map +1 -0
- package/dist/src/tui/components/log-panel.js +38 -0
- package/dist/src/tui/components/log-panel.js.map +1 -0
- package/dist/src/tui/components/memory-viewer.d.ts +10 -0
- package/dist/src/tui/components/memory-viewer.d.ts.map +1 -0
- package/dist/src/tui/components/memory-viewer.js +44 -0
- package/dist/src/tui/components/memory-viewer.js.map +1 -0
- package/dist/src/tui/components/new-task-input.d.ts +9 -0
- package/dist/src/tui/components/new-task-input.d.ts.map +1 -0
- package/dist/src/tui/components/new-task-input.js +21 -0
- package/dist/src/tui/components/new-task-input.js.map +1 -0
- package/dist/src/tui/components/orchestrator-status.d.ts +9 -0
- package/dist/src/tui/components/orchestrator-status.d.ts.map +1 -0
- package/dist/src/tui/components/orchestrator-status.js +15 -0
- package/dist/src/tui/components/orchestrator-status.js.map +1 -0
- package/dist/src/tui/components/parallelize-banner.d.ts +8 -0
- package/dist/src/tui/components/parallelize-banner.d.ts.map +1 -0
- package/dist/src/tui/components/parallelize-banner.js +9 -0
- package/dist/src/tui/components/parallelize-banner.js.map +1 -0
- package/dist/src/tui/components/progress-bar.d.ts +9 -0
- package/dist/src/tui/components/progress-bar.d.ts.map +1 -0
- package/dist/src/tui/components/progress-bar.js +15 -0
- package/dist/src/tui/components/progress-bar.js.map +1 -0
- package/dist/src/tui/components/review-queue.d.ts +13 -0
- package/dist/src/tui/components/review-queue.d.ts.map +1 -0
- package/dist/src/tui/components/review-queue.js +78 -0
- package/dist/src/tui/components/review-queue.js.map +1 -0
- package/dist/src/tui/components/rich-header.d.ts +11 -0
- package/dist/src/tui/components/rich-header.d.ts.map +1 -0
- package/dist/src/tui/components/rich-header.js +25 -0
- package/dist/src/tui/components/rich-header.js.map +1 -0
- package/dist/src/tui/components/spinner.d.ts +7 -0
- package/dist/src/tui/components/spinner.d.ts.map +1 -0
- package/dist/src/tui/components/spinner.js +8 -0
- package/dist/src/tui/components/spinner.js.map +1 -0
- package/dist/src/tui/components/status-bar.d.ts +23 -0
- package/dist/src/tui/components/status-bar.d.ts.map +1 -0
- package/dist/src/tui/components/status-bar.js +28 -0
- package/dist/src/tui/components/status-bar.js.map +1 -0
- package/dist/src/tui/components/task-queue.d.ts +11 -0
- package/dist/src/tui/components/task-queue.d.ts.map +1 -0
- package/dist/src/tui/components/task-queue.js +30 -0
- package/dist/src/tui/components/task-queue.js.map +1 -0
- package/dist/src/tui/components/team-view.d.ts +13 -0
- package/dist/src/tui/components/team-view.d.ts.map +1 -0
- package/dist/src/tui/components/team-view.js +12 -0
- package/dist/src/tui/components/team-view.js.map +1 -0
- package/dist/src/tui/graph-renderer.d.ts +11 -0
- package/dist/src/tui/graph-renderer.d.ts.map +1 -0
- package/dist/src/tui/graph-renderer.js +296 -0
- package/dist/src/tui/graph-renderer.js.map +1 -0
- package/dist/src/tui/hooks/use-bell.d.ts +21 -0
- package/dist/src/tui/hooks/use-bell.d.ts.map +1 -0
- package/dist/src/tui/hooks/use-bell.js +38 -0
- package/dist/src/tui/hooks/use-bell.js.map +1 -0
- package/dist/src/tui/hooks/use-ci-status.d.ts +16 -0
- package/dist/src/tui/hooks/use-ci-status.d.ts.map +1 -0
- package/dist/src/tui/hooks/use-ci-status.js +97 -0
- package/dist/src/tui/hooks/use-ci-status.js.map +1 -0
- package/dist/src/tui/hooks/use-command-palette.d.ts +37 -0
- package/dist/src/tui/hooks/use-command-palette.d.ts.map +1 -0
- package/dist/src/tui/hooks/use-command-palette.js +108 -0
- package/dist/src/tui/hooks/use-command-palette.js.map +1 -0
- package/dist/src/tui/hooks/use-diff-view.d.ts +16 -0
- package/dist/src/tui/hooks/use-diff-view.d.ts.map +1 -0
- package/dist/src/tui/hooks/use-diff-view.js +75 -0
- package/dist/src/tui/hooks/use-diff-view.js.map +1 -0
- package/dist/src/tui/hooks/use-keyboard.d.ts +3 -0
- package/dist/src/tui/hooks/use-keyboard.d.ts.map +1 -0
- package/dist/src/tui/hooks/use-keyboard.js +70 -0
- package/dist/src/tui/hooks/use-keyboard.js.map +1 -0
- package/dist/src/tui/hooks/use-log-stream.d.ts +25 -0
- package/dist/src/tui/hooks/use-log-stream.d.ts.map +1 -0
- package/dist/src/tui/hooks/use-log-stream.js +83 -0
- package/dist/src/tui/hooks/use-log-stream.js.map +1 -0
- package/dist/src/tui/hooks/use-spinner.d.ts +3 -0
- package/dist/src/tui/hooks/use-spinner.d.ts.map +1 -0
- package/dist/src/tui/hooks/use-spinner.js +13 -0
- package/dist/src/tui/hooks/use-spinner.js.map +1 -0
- package/dist/src/tui/hooks/use-store.d.ts +33 -0
- package/dist/src/tui/hooks/use-store.d.ts.map +1 -0
- package/dist/src/tui/hooks/use-store.js +21 -0
- package/dist/src/tui/hooks/use-store.js.map +1 -0
- package/dist/src/tui/lib/diff-parser.d.ts +24 -0
- package/dist/src/tui/lib/diff-parser.d.ts.map +1 -0
- package/dist/src/tui/lib/diff-parser.js +115 -0
- package/dist/src/tui/lib/diff-parser.js.map +1 -0
- package/dist/src/tui/lib/palette-commands.d.ts +14 -0
- package/dist/src/tui/lib/palette-commands.d.ts.map +1 -0
- package/dist/src/tui/lib/palette-commands.js +101 -0
- package/dist/src/tui/lib/palette-commands.js.map +1 -0
- package/dist/src/tui/tui.d.ts +8 -0
- package/dist/src/tui/tui.d.ts.map +1 -0
- package/dist/src/tui/tui.js +8 -0
- package/dist/src/tui/tui.js.map +1 -0
- package/package.json +66 -0
- package/skills/api-design.md +84 -0
- package/skills/backend-typescript.md +76 -0
- package/skills/data-modeling.md +73 -0
- package/skills/devops-ci.md +82 -0
- package/skills/frontend-design.md +69 -0
- package/skills/observability.md +73 -0
- package/skills/react-nextjs.md +76 -0
- package/skills/refactoring.md +77 -0
- package/skills/security.md +75 -0
- package/skills/testing.md +69 -0
|
@@ -0,0 +1,1156 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { Command } from 'commander';
|
|
3
|
+
import { execSync } from 'child_process';
|
|
4
|
+
import fs from 'fs';
|
|
5
|
+
import path from 'path';
|
|
6
|
+
import { fileURLToPath } from 'url';
|
|
7
|
+
import { createDatabase } from '../src/db/database.js';
|
|
8
|
+
import { createTaskStore } from '../src/core/task-store.js';
|
|
9
|
+
import { createAgentSessionStore } from '../src/core/agent-session-store.js';
|
|
10
|
+
import { createConflictDetector } from '../src/core/conflict-detector.js';
|
|
11
|
+
import { createConflictEnforcer } from '../src/core/conflict-enforcer.js';
|
|
12
|
+
import { createParallelizer, hasFileOverlap } from '../src/core/parallelizer.js';
|
|
13
|
+
import { createAgentLauncher } from '../src/agent/agent-launcher.js';
|
|
14
|
+
import { formatFunIdentity } from '../src/agent/fun-names.js';
|
|
15
|
+
import { createApprovalStore } from '../src/approval/approval-store.js';
|
|
16
|
+
import { createApprovalServer } from '../src/approval/approval-server.js';
|
|
17
|
+
import { createHumanActionStore } from '../src/core/human-action-store.js';
|
|
18
|
+
import { createLearningStore } from '../src/core/learning-store.js';
|
|
19
|
+
import { createOrchestratorStore } from '../src/core/orchestrator-store.js';
|
|
20
|
+
import { createGitHubOrchestrator } from '../src/intelligence/github-orchestrator.js';
|
|
21
|
+
import { readBaseBranch, readHotFiles } from '../src/core/verbo-md.js';
|
|
22
|
+
import { createMergeQueue } from '../src/core/merge-queue.js';
|
|
23
|
+
import { createHotFiles } from '../src/core/hot-files.js';
|
|
24
|
+
import { setVerboMdField, getVerboMdFields, validateFieldValue, requiresConfirmation, FIELD_REGISTRY, } from '../src/core/verbo-config-editor.js';
|
|
25
|
+
import { cleanStaleState, pruneAuditLog } from '../src/core/startup-cleanup.js';
|
|
26
|
+
import { registerTeamCommand, bootstrapTeamFromVerboMd } from '../src/commands/team.js';
|
|
27
|
+
import { registerAuditCommand } from '../src/commands/audit.js';
|
|
28
|
+
import { createRbacStore } from '../src/core/rbac.js';
|
|
29
|
+
import { createAuditLogStore } from '../src/core/audit-log-store.js';
|
|
30
|
+
import { createCostStore } from '../src/core/cost-store.js';
|
|
31
|
+
import { createPipelineRunner } from '../src/core/pipeline-runner.js';
|
|
32
|
+
import { createNotifications } from '../src/core/notifications.js';
|
|
33
|
+
import { readCostConfig, readPipelineConfigs, readNotificationConfig } from '../src/core/verbo-md.js';
|
|
34
|
+
import { readSession, clearSession } from '../src/auth/auth-store.js';
|
|
35
|
+
import { checkLimit } from '../src/auth/plan-enforcer.js';
|
|
36
|
+
import { AuthGate } from '../src/tui/components/auth-gate.js';
|
|
37
|
+
import { startTui } from '../src/tui/tui.js';
|
|
38
|
+
import { detectProject } from '../src/init/project-detector.js';
|
|
39
|
+
import { ensureInit } from '../src/init/ensure-init.js';
|
|
40
|
+
import { buildDefaults, runQuestionnaire } from '../src/init/questionnaire.js';
|
|
41
|
+
import { renderVerboMd } from '../src/init/template.js';
|
|
42
|
+
import { createTaskDecomposer } from '../src/intelligence/task-decomposer.js';
|
|
43
|
+
import { isIntelligenceEnabled } from '../src/intelligence/kill-switch.js';
|
|
44
|
+
import { render } from 'ink';
|
|
45
|
+
import React from 'react';
|
|
46
|
+
import { DecompositionReview } from '../src/tui/components/decomposition-review.js';
|
|
47
|
+
// ---- Helpers ----
|
|
48
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
49
|
+
const __dirname = path.dirname(__filename);
|
|
50
|
+
// When installed via npm, points to dist/src/mcp/stdio-entry.js inside the package
|
|
51
|
+
const MCP_SERVER_COMMAND = `node "${path.join(__dirname, '..', 'src', 'mcp', 'stdio-entry.js')}"`;
|
|
52
|
+
async function ensureAuth() {
|
|
53
|
+
const session = readSession();
|
|
54
|
+
if (session)
|
|
55
|
+
return; // cached (may be stale — refresh is background-only for now)
|
|
56
|
+
await new Promise((resolve) => {
|
|
57
|
+
const { unmount } = render(React.createElement(AuthGate, {
|
|
58
|
+
onSuccess: () => {
|
|
59
|
+
unmount();
|
|
60
|
+
resolve();
|
|
61
|
+
},
|
|
62
|
+
}));
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
function resolveProjectDir() {
|
|
66
|
+
const opts = program.opts();
|
|
67
|
+
if (opts.project) {
|
|
68
|
+
return path.resolve(opts.project);
|
|
69
|
+
}
|
|
70
|
+
return process.cwd();
|
|
71
|
+
}
|
|
72
|
+
let _allCache = null;
|
|
73
|
+
function _initAll() {
|
|
74
|
+
const projDir = resolveProjectDir();
|
|
75
|
+
const verboDir = path.join(projDir, '.verbo');
|
|
76
|
+
if (!fs.existsSync(verboDir))
|
|
77
|
+
fs.mkdirSync(verboDir, { recursive: true });
|
|
78
|
+
const dbFile = path.join(verboDir, 'verbo.db');
|
|
79
|
+
const db = createDatabase(dbFile);
|
|
80
|
+
const tasks = createTaskStore(db);
|
|
81
|
+
const sessions = createAgentSessionStore(db);
|
|
82
|
+
const detector = createConflictDetector(tasks);
|
|
83
|
+
const hotFileConfig = readHotFiles(projDir);
|
|
84
|
+
const hotFiles = createHotFiles({
|
|
85
|
+
projectDir: projDir,
|
|
86
|
+
configOverrides: hotFileConfig,
|
|
87
|
+
});
|
|
88
|
+
const parallelizer = createParallelizer(tasks, { maxAgents: 5 }, hotFiles);
|
|
89
|
+
const approvals = createApprovalStore(db);
|
|
90
|
+
const humanActions = createHumanActionStore(db);
|
|
91
|
+
const learnings = createLearningStore(db);
|
|
92
|
+
const orchestratorStore = createOrchestratorStore();
|
|
93
|
+
// Initialize cost store and pipeline runner
|
|
94
|
+
const costStore = createCostStore(db);
|
|
95
|
+
const pipelineRunner = createPipelineRunner(tasks);
|
|
96
|
+
// Read budget from VERBO.md cost section (seed pricing on startup)
|
|
97
|
+
const costConfig = readCostConfig(projDir);
|
|
98
|
+
const budgetPerTaskUsd = costConfig.budgetPerTaskUsd;
|
|
99
|
+
const launcher = createAgentLauncher({
|
|
100
|
+
projectDir: projDir,
|
|
101
|
+
mcpServerCommand: MCP_SERVER_COMMAND,
|
|
102
|
+
approvalPort: 5050,
|
|
103
|
+
budgetPerTaskUsd,
|
|
104
|
+
learningStore: learnings,
|
|
105
|
+
});
|
|
106
|
+
return {
|
|
107
|
+
projectDir: projDir, db, tasks, sessions, detector, parallelizer, hotFiles,
|
|
108
|
+
approvals, humanActions, learnings, orchestratorStore, launcher,
|
|
109
|
+
costStore, pipelineRunner, budgetPerTaskUsd,
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
function initAll() {
|
|
113
|
+
if (!_allCache)
|
|
114
|
+
_allCache = _initAll();
|
|
115
|
+
return _allCache;
|
|
116
|
+
}
|
|
117
|
+
function taskIcon(status) {
|
|
118
|
+
switch (status) {
|
|
119
|
+
case 'done': return '✓';
|
|
120
|
+
case 'in_progress': return '▸';
|
|
121
|
+
case 'blocked': return '✗';
|
|
122
|
+
default: return '○';
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
async function launchAgentForTask(taskId, tasks, sessions, launcher, approvals, projDir, trustLevel = 'medium', enforcer, orchestrator, extras) {
|
|
126
|
+
const task = tasks.get(taskId);
|
|
127
|
+
if (!task) {
|
|
128
|
+
console.error(`Task not found: ${taskId}`);
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
// Guard: don't launch if already in progress — mark immediately to prevent race
|
|
132
|
+
if (task.status === 'in_progress' || task.status === 'done') {
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
tasks.update(taskId, { status: 'in_progress' });
|
|
136
|
+
// Create a session record
|
|
137
|
+
const session = sessions.create({
|
|
138
|
+
taskId: task.id,
|
|
139
|
+
humanId: 'local',
|
|
140
|
+
});
|
|
141
|
+
// Set up log file for capturing agent output
|
|
142
|
+
const logDir = path.join(projDir, '.verbo', 'logs');
|
|
143
|
+
if (!fs.existsSync(logDir))
|
|
144
|
+
fs.mkdirSync(logDir, { recursive: true });
|
|
145
|
+
const logPath = path.join(logDir, `${task.id}.log`);
|
|
146
|
+
const logStream = fs.createWriteStream(logPath, { flags: 'a' });
|
|
147
|
+
logStream.write(`--- Agent launched at ${new Date().toISOString()} ---\n`);
|
|
148
|
+
try {
|
|
149
|
+
const launched = await launcher.launch({
|
|
150
|
+
taskId: task.id,
|
|
151
|
+
title: task.title,
|
|
152
|
+
description: task.description,
|
|
153
|
+
filePaths: task.filePaths,
|
|
154
|
+
trustLevel,
|
|
155
|
+
}, {
|
|
156
|
+
onOutput: (data) => {
|
|
157
|
+
logStream.write(data);
|
|
158
|
+
sessions.heartbeat(session.id);
|
|
159
|
+
},
|
|
160
|
+
onError: (data) => {
|
|
161
|
+
logStream.write(`[stderr] ${data}`);
|
|
162
|
+
},
|
|
163
|
+
onExit: (code) => {
|
|
164
|
+
logStream.write(`\n--- Agent exited with code ${code} at ${new Date().toISOString()} ---\n`);
|
|
165
|
+
logStream.end();
|
|
166
|
+
approvals.deleteBySession(session.id);
|
|
167
|
+
if (code === 0) {
|
|
168
|
+
const completedSession = sessions.get(session.id);
|
|
169
|
+
sessions.complete(session.id, 'Agent exited successfully');
|
|
170
|
+
tasks.update(task.id, { status: 'done' });
|
|
171
|
+
// Notify on task completion
|
|
172
|
+
extras?.notifications?.send('task_complete', task.title).catch(() => { });
|
|
173
|
+
// Advance any active pipeline runs that have this task as their current step
|
|
174
|
+
if (extras?.pipelineRunner && extras.pipelineRunIds) {
|
|
175
|
+
for (const runId of extras.pipelineRunIds) {
|
|
176
|
+
try {
|
|
177
|
+
extras.pipelineRunner.advance(runId);
|
|
178
|
+
}
|
|
179
|
+
catch {
|
|
180
|
+
// Non-fatal: pipeline advance failures shouldn't block task completion
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
// Review diff, push branch, and open PR via the GitHub orchestrator
|
|
185
|
+
if (orchestrator) {
|
|
186
|
+
const baseBranch = readBaseBranch(projDir);
|
|
187
|
+
const worktreeBranch = completedSession?.worktreeBranch ?? `verbo/${task.id}`;
|
|
188
|
+
const worktreePath = completedSession?.worktreePath ?? launcher.getWorktreePath(task.id);
|
|
189
|
+
orchestrator
|
|
190
|
+
.reviewAndPush({
|
|
191
|
+
taskId: task.id,
|
|
192
|
+
taskTitle: task.title,
|
|
193
|
+
summary: completedSession?.completionSummary ?? 'Task completed by Verbo agent',
|
|
194
|
+
branch: worktreeBranch,
|
|
195
|
+
baseBranch,
|
|
196
|
+
worktreePath,
|
|
197
|
+
})
|
|
198
|
+
.then((result) => {
|
|
199
|
+
if (result.prUrl) {
|
|
200
|
+
tasks.update(task.id, { prUrl: result.prUrl });
|
|
201
|
+
}
|
|
202
|
+
})
|
|
203
|
+
.catch(() => {
|
|
204
|
+
// Failures surface via human_actions (blocking review) or are non-critical
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
// Cleanup worktree after successful completion
|
|
208
|
+
launcher.cleanupWorktree(task.id).catch(() => { });
|
|
209
|
+
// Auto-relaunch tasks that were paused due to conflict with this task
|
|
210
|
+
if (enforcer) {
|
|
211
|
+
const toRelaunch = enforcer.checkRelaunch(task.id);
|
|
212
|
+
for (const relaunchTaskId of toRelaunch) {
|
|
213
|
+
launchAgentForTask(relaunchTaskId, tasks, sessions, launcher, approvals, projDir, trustLevel, enforcer, orchestrator, extras).catch((err) => {
|
|
214
|
+
console.error(`Failed to auto-relaunch task ${relaunchTaskId}:`, err.message);
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
else {
|
|
220
|
+
sessions.error(session.id);
|
|
221
|
+
tasks.unclaim(task.id);
|
|
222
|
+
// Cleanup worktree on failure too
|
|
223
|
+
launcher.cleanupWorktree(task.id).catch(() => { });
|
|
224
|
+
// Advance pipeline runs on failure too (step will be marked failed, on_failure applied)
|
|
225
|
+
if (extras?.pipelineRunner && extras.pipelineRunIds) {
|
|
226
|
+
for (const runId of extras.pipelineRunIds) {
|
|
227
|
+
try {
|
|
228
|
+
extras.pipelineRunner.advance(runId);
|
|
229
|
+
}
|
|
230
|
+
catch {
|
|
231
|
+
// Non-fatal
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
// Emit bell / notifications — notifications.send() includes the bell
|
|
237
|
+
if (!extras?.notifications) {
|
|
238
|
+
process.stderr.write('\x07');
|
|
239
|
+
}
|
|
240
|
+
},
|
|
241
|
+
});
|
|
242
|
+
// Store pid, worktree info, and fun name on the session
|
|
243
|
+
sessions.update(session.id, {
|
|
244
|
+
pid: launched.pid,
|
|
245
|
+
worktreePath: launched.worktreePath,
|
|
246
|
+
worktreeBranch: launched.worktreeBranch,
|
|
247
|
+
status: 'active',
|
|
248
|
+
funName: formatFunIdentity(launched.funIdentity),
|
|
249
|
+
});
|
|
250
|
+
// Set agentSessionId on the task so MCP claim_task can reuse it
|
|
251
|
+
tasks.update(task.id, {
|
|
252
|
+
agentSessionId: session.id,
|
|
253
|
+
});
|
|
254
|
+
}
|
|
255
|
+
catch (err) {
|
|
256
|
+
sessions.error(session.id);
|
|
257
|
+
throw err;
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
// ---- Program ----
|
|
261
|
+
const program = new Command();
|
|
262
|
+
program
|
|
263
|
+
.name('verbo')
|
|
264
|
+
.description('Orchestrate multiple Claude Code agents across your team')
|
|
265
|
+
.version('0.1.0')
|
|
266
|
+
.option('--project <path>', 'Target project directory (defaults to cwd)');
|
|
267
|
+
// ---- Auth gate: enforce authentication on all commands except login/logout/init ----
|
|
268
|
+
const AUTH_EXEMPT_COMMANDS = new Set(['login', 'logout', 'init']);
|
|
269
|
+
program.hook('preAction', async (_thisCommand, actionCommand) => {
|
|
270
|
+
if (!AUTH_EXEMPT_COMMANDS.has(actionCommand.name())) {
|
|
271
|
+
await ensureAuth();
|
|
272
|
+
}
|
|
273
|
+
});
|
|
274
|
+
// ---- verbo add <title> ----
|
|
275
|
+
program
|
|
276
|
+
.command('add <title>')
|
|
277
|
+
.description('Add a new task')
|
|
278
|
+
.option('-p, --priority <level>', 'Task priority (p0, p1, p2, p3)', 'p2')
|
|
279
|
+
.option('-d, --description <text>', 'Task description')
|
|
280
|
+
.option('-f, --files <paths...>', 'Relevant file paths')
|
|
281
|
+
.action(async (title, opts) => {
|
|
282
|
+
const { tasks } = initAll();
|
|
283
|
+
const task = tasks.create({
|
|
284
|
+
title,
|
|
285
|
+
priority: opts.priority,
|
|
286
|
+
description: opts.description,
|
|
287
|
+
filePaths: opts.files ?? [],
|
|
288
|
+
});
|
|
289
|
+
console.log(`Created task ${task.id}: ${task.title} [${task.priority}]`);
|
|
290
|
+
// Offer decomposition if description has >= 50 words
|
|
291
|
+
const descriptionText = opts.description ?? '';
|
|
292
|
+
const wordCount = descriptionText.trim().split(/\s+/).filter(Boolean).length;
|
|
293
|
+
if (wordCount >= 50) {
|
|
294
|
+
const decomposer = createTaskDecomposer(tasks);
|
|
295
|
+
console.log('Long description detected — generating decomposition…');
|
|
296
|
+
const subtasks = await decomposer.decompose(descriptionText);
|
|
297
|
+
if (subtasks && subtasks.length > 0) {
|
|
298
|
+
await runDecomposeReview(subtasks, decomposer, task.id);
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
});
|
|
302
|
+
// ---- Decompose review helper (used by CLI commands) ----
|
|
303
|
+
async function runDecomposeReview(subtasks, decomposer, parentTaskId) {
|
|
304
|
+
return new Promise((resolve) => {
|
|
305
|
+
const { unmount } = render(React.createElement(DecompositionReview, {
|
|
306
|
+
subtasks,
|
|
307
|
+
onConfirm: async () => {
|
|
308
|
+
unmount();
|
|
309
|
+
const created = await decomposer.confirm(subtasks, parentTaskId);
|
|
310
|
+
console.log(`\nCreated ${created.length} subtask${created.length !== 1 ? 's' : ''}:`);
|
|
311
|
+
for (const t of created) {
|
|
312
|
+
console.log(` ○ [${t.priority}] ${t.title} (${t.id.slice(0, 8)})`);
|
|
313
|
+
}
|
|
314
|
+
resolve();
|
|
315
|
+
},
|
|
316
|
+
onCancel: () => {
|
|
317
|
+
unmount();
|
|
318
|
+
console.log('Decomposition cancelled.');
|
|
319
|
+
resolve();
|
|
320
|
+
},
|
|
321
|
+
}));
|
|
322
|
+
});
|
|
323
|
+
}
|
|
324
|
+
// ---- verbo decompose <taskId> ----
|
|
325
|
+
program
|
|
326
|
+
.command('decompose <taskId>')
|
|
327
|
+
.description('Decompose a task into subtasks using AI')
|
|
328
|
+
.action(async (taskId) => {
|
|
329
|
+
const { tasks } = initAll();
|
|
330
|
+
const task = tasks.get(taskId);
|
|
331
|
+
if (!task) {
|
|
332
|
+
console.error(`Task not found: ${taskId}`);
|
|
333
|
+
process.exit(1);
|
|
334
|
+
}
|
|
335
|
+
const input = [task.title, task.description].filter(Boolean).join('\n\n');
|
|
336
|
+
const decomposer = createTaskDecomposer(tasks);
|
|
337
|
+
console.log(`Decomposing task: ${task.title}`);
|
|
338
|
+
const subtasks = await decomposer.decompose(input);
|
|
339
|
+
if (!subtasks || subtasks.length === 0) {
|
|
340
|
+
const wordCount = input.trim().split(/\s+/).filter(Boolean).length;
|
|
341
|
+
if (wordCount < 50) {
|
|
342
|
+
console.log('Task description too short for decomposition (need ≥50 words).');
|
|
343
|
+
}
|
|
344
|
+
else if (!process.env.ANTHROPIC_API_KEY) {
|
|
345
|
+
console.log('No ANTHROPIC_API_KEY set — cannot decompose.');
|
|
346
|
+
}
|
|
347
|
+
else {
|
|
348
|
+
console.log('Could not decompose task.');
|
|
349
|
+
}
|
|
350
|
+
return;
|
|
351
|
+
}
|
|
352
|
+
await runDecomposeReview(subtasks, decomposer, task.id);
|
|
353
|
+
});
|
|
354
|
+
// ---- verbo list ----
|
|
355
|
+
program
|
|
356
|
+
.command('list')
|
|
357
|
+
.description('List tasks')
|
|
358
|
+
.option('-s, --status <status>', 'Filter by status (todo, in_progress, done, blocked)')
|
|
359
|
+
.action((opts) => {
|
|
360
|
+
const { tasks } = initAll();
|
|
361
|
+
const filter = opts.status
|
|
362
|
+
? { status: opts.status }
|
|
363
|
+
: undefined;
|
|
364
|
+
const list = tasks.list(filter);
|
|
365
|
+
if (list.length === 0) {
|
|
366
|
+
console.log('No tasks found.');
|
|
367
|
+
return;
|
|
368
|
+
}
|
|
369
|
+
for (const task of list) {
|
|
370
|
+
const icon = taskIcon(task.status);
|
|
371
|
+
const shortId = task.id.slice(0, 8);
|
|
372
|
+
console.log(`${icon} [${task.priority}] ${task.title} (${shortId})`);
|
|
373
|
+
}
|
|
374
|
+
});
|
|
375
|
+
// ---- verbo launch <taskId> ----
|
|
376
|
+
program
|
|
377
|
+
.command('launch <taskId>')
|
|
378
|
+
.description('Launch agent on a specific task')
|
|
379
|
+
.option('--trust <level>', 'Trust level: low, medium, high (default: medium)', 'medium')
|
|
380
|
+
.action(async (taskId, opts) => {
|
|
381
|
+
const trust = opts.trust;
|
|
382
|
+
if (!['low', 'medium', 'high'].includes(trust)) {
|
|
383
|
+
console.error(`Invalid trust level: ${opts.trust}. Use low, medium, or high.`);
|
|
384
|
+
process.exit(1);
|
|
385
|
+
}
|
|
386
|
+
const { tasks, sessions, launcher, approvals, projectDir } = initAll();
|
|
387
|
+
try {
|
|
388
|
+
await launchAgentForTask(taskId, tasks, sessions, launcher, approvals, projectDir, trust);
|
|
389
|
+
}
|
|
390
|
+
catch (err) {
|
|
391
|
+
console.error('Failed to launch agent:', err.message);
|
|
392
|
+
process.exit(1);
|
|
393
|
+
}
|
|
394
|
+
});
|
|
395
|
+
// ---- verbo parallel ----
|
|
396
|
+
program
|
|
397
|
+
.command('parallel')
|
|
398
|
+
.description('Show and launch safe parallel set of tasks')
|
|
399
|
+
.action(async () => {
|
|
400
|
+
const { tasks, sessions, parallelizer, launcher, approvals, projectDir } = initAll();
|
|
401
|
+
const batch = parallelizer.findSafeBatch();
|
|
402
|
+
if (batch.length === 0) {
|
|
403
|
+
console.log('No parallelizable tasks found.');
|
|
404
|
+
return;
|
|
405
|
+
}
|
|
406
|
+
console.log(`Found ${batch.length} tasks to run in parallel:`);
|
|
407
|
+
for (const task of batch) {
|
|
408
|
+
console.log(` ${taskIcon(task.status)} [${task.priority}] ${task.title} (${task.id.slice(0, 8)})`);
|
|
409
|
+
}
|
|
410
|
+
console.log('');
|
|
411
|
+
for (const task of batch) {
|
|
412
|
+
try {
|
|
413
|
+
await launchAgentForTask(task.id, tasks, sessions, launcher, approvals, projectDir);
|
|
414
|
+
}
|
|
415
|
+
catch (err) {
|
|
416
|
+
console.error(`Failed to launch agent for task ${task.id}:`, err.message);
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
});
|
|
420
|
+
// ---- verbo status ----
|
|
421
|
+
program
|
|
422
|
+
.command('status')
|
|
423
|
+
.description('One-line summary of current state')
|
|
424
|
+
.action(() => {
|
|
425
|
+
const { tasks, sessions } = initAll();
|
|
426
|
+
const activeSessions = sessions.listActive();
|
|
427
|
+
const allTasks = tasks.list();
|
|
428
|
+
const readyTasks = allTasks.filter((t) => t.status === 'todo');
|
|
429
|
+
console.log(`${activeSessions.length} agents running, ${readyTasks.length} tasks ready, ${allTasks.length} total`);
|
|
430
|
+
});
|
|
431
|
+
// ---- verbo team (list, invite, role, log) ----
|
|
432
|
+
registerTeamCommand(program, () => {
|
|
433
|
+
const { db } = initAll();
|
|
434
|
+
const raw = db.raw();
|
|
435
|
+
const rbacStore = createRbacStore(raw);
|
|
436
|
+
const auditLog = createAuditLogStore(db);
|
|
437
|
+
return { db: raw, rbacStore, auditLog };
|
|
438
|
+
});
|
|
439
|
+
// ---- verbo audit (list, export) ----
|
|
440
|
+
registerAuditCommand(program, () => {
|
|
441
|
+
const { db } = initAll();
|
|
442
|
+
const auditLog = createAuditLogStore(db);
|
|
443
|
+
return { auditLog };
|
|
444
|
+
});
|
|
445
|
+
// ---- verbo kill <agentNumber> ----
|
|
446
|
+
program
|
|
447
|
+
.command('kill <agentNumber>')
|
|
448
|
+
.description('Kill agent by number (1-indexed)')
|
|
449
|
+
.action((agentNumberStr) => {
|
|
450
|
+
const agentNumber = parseInt(agentNumberStr, 10);
|
|
451
|
+
if (isNaN(agentNumber) || agentNumber < 1) {
|
|
452
|
+
console.error('Agent number must be a positive integer.');
|
|
453
|
+
process.exit(1);
|
|
454
|
+
}
|
|
455
|
+
const { sessions, launcher, approvals } = initAll();
|
|
456
|
+
const activeSessions = sessions.listActive();
|
|
457
|
+
if (activeSessions.length === 0) {
|
|
458
|
+
console.log('No active sessions.');
|
|
459
|
+
return;
|
|
460
|
+
}
|
|
461
|
+
const session = activeSessions[agentNumber - 1];
|
|
462
|
+
if (!session) {
|
|
463
|
+
console.error(`No agent #${agentNumber}. There are ${activeSessions.length} active agents.`);
|
|
464
|
+
process.exit(1);
|
|
465
|
+
}
|
|
466
|
+
if (session.pid !== null) {
|
|
467
|
+
launcher.kill(session.pid);
|
|
468
|
+
console.log(`Killed agent #${agentNumber} (PID ${session.pid})`);
|
|
469
|
+
}
|
|
470
|
+
else {
|
|
471
|
+
console.log(`Agent #${agentNumber} has no PID recorded — marking as errored.`);
|
|
472
|
+
}
|
|
473
|
+
approvals.deleteBySession(session.id);
|
|
474
|
+
sessions.error(session.id);
|
|
475
|
+
});
|
|
476
|
+
// ---- verbo sync ----
|
|
477
|
+
program
|
|
478
|
+
.command('sync')
|
|
479
|
+
.description('Force re-sync with relay server')
|
|
480
|
+
.action(() => {
|
|
481
|
+
console.log('Sync requested. (Relay integration pending team setup)');
|
|
482
|
+
});
|
|
483
|
+
// ---- verbo stop ----
|
|
484
|
+
program
|
|
485
|
+
.command('stop')
|
|
486
|
+
.description('Stop the daemon')
|
|
487
|
+
.action(() => {
|
|
488
|
+
const { projectDir } = initAll();
|
|
489
|
+
const pidFile = path.join(projectDir, '.verbo', 'daemon.pid');
|
|
490
|
+
if (!fs.existsSync(pidFile)) {
|
|
491
|
+
console.log('No daemon running (no PID file found).');
|
|
492
|
+
return;
|
|
493
|
+
}
|
|
494
|
+
const pid = parseInt(fs.readFileSync(pidFile, 'utf-8').trim(), 10);
|
|
495
|
+
if (isNaN(pid)) {
|
|
496
|
+
console.error('Invalid PID in daemon.pid file.');
|
|
497
|
+
fs.unlinkSync(pidFile);
|
|
498
|
+
return;
|
|
499
|
+
}
|
|
500
|
+
try {
|
|
501
|
+
process.kill(pid);
|
|
502
|
+
console.log(`Stopped daemon (PID ${pid}).`);
|
|
503
|
+
}
|
|
504
|
+
catch {
|
|
505
|
+
console.log(`Daemon (PID ${pid}) was not running.`);
|
|
506
|
+
}
|
|
507
|
+
fs.unlinkSync(pidFile);
|
|
508
|
+
});
|
|
509
|
+
// ---- verbo yolo ----
|
|
510
|
+
program
|
|
511
|
+
.command('yolo')
|
|
512
|
+
.description('Toggle YOLO mode — auto-approve all agent permissions')
|
|
513
|
+
.action(() => {
|
|
514
|
+
console.log('YOLO mode toggled. Use the TUI (!) to toggle in real-time.');
|
|
515
|
+
});
|
|
516
|
+
// ---- verbo memory ----
|
|
517
|
+
const memoryCmd = program
|
|
518
|
+
.command('memory')
|
|
519
|
+
.description('View or manage project memory (learnings from agent tasks)');
|
|
520
|
+
memoryCmd
|
|
521
|
+
.action(() => {
|
|
522
|
+
const { learnings, tasks } = initAll();
|
|
523
|
+
const all = learnings.list();
|
|
524
|
+
if (all.length === 0) {
|
|
525
|
+
console.log('No learnings stored yet.');
|
|
526
|
+
return;
|
|
527
|
+
}
|
|
528
|
+
// Group by category
|
|
529
|
+
const groups = new Map();
|
|
530
|
+
for (const l of all) {
|
|
531
|
+
if (!groups.has(l.category))
|
|
532
|
+
groups.set(l.category, []);
|
|
533
|
+
groups.get(l.category).push(l);
|
|
534
|
+
}
|
|
535
|
+
for (const [cat, items] of groups) {
|
|
536
|
+
console.log(`\n${cat.toUpperCase()}S (${items.length})`);
|
|
537
|
+
for (const l of items) {
|
|
538
|
+
const taskTitle = l.taskId ? tasks.get(l.taskId)?.title ?? l.taskId.slice(0, 8) : null;
|
|
539
|
+
const taskSuffix = taskTitle ? ` (${taskTitle})` : '';
|
|
540
|
+
const sourceSuffix = l.source ? ` [${l.source}]` : '';
|
|
541
|
+
console.log(` · ${l.content}${sourceSuffix}${taskSuffix}`);
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
console.log(`\nTotal: ${all.length} learnings`);
|
|
545
|
+
});
|
|
546
|
+
memoryCmd
|
|
547
|
+
.command('clear')
|
|
548
|
+
.description('Clear all stored project memory')
|
|
549
|
+
.action(async () => {
|
|
550
|
+
const { learnings } = initAll();
|
|
551
|
+
const count = learnings.list().length;
|
|
552
|
+
if (count === 0) {
|
|
553
|
+
console.log('No learnings to clear.');
|
|
554
|
+
return;
|
|
555
|
+
}
|
|
556
|
+
const readline = await import('readline');
|
|
557
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
558
|
+
await new Promise((resolve) => {
|
|
559
|
+
rl.question(`Clear all ${count} learnings? (y/N) `, (answer) => {
|
|
560
|
+
rl.close();
|
|
561
|
+
if (answer.trim().toLowerCase() !== 'y') {
|
|
562
|
+
console.log('Aborted.');
|
|
563
|
+
process.exit(0);
|
|
564
|
+
}
|
|
565
|
+
resolve();
|
|
566
|
+
});
|
|
567
|
+
});
|
|
568
|
+
learnings.clear();
|
|
569
|
+
console.log(`Cleared ${count} learnings.`);
|
|
570
|
+
});
|
|
571
|
+
// ---- verbo config ----
|
|
572
|
+
const configCmd = program
|
|
573
|
+
.command('config')
|
|
574
|
+
.description('Read or write VERBO.md configuration fields');
|
|
575
|
+
configCmd
|
|
576
|
+
.command('set <field> <value>')
|
|
577
|
+
.description('Set a field in VERBO.md (e.g. verbo config set merge_mode auto)')
|
|
578
|
+
.action(async (field, value) => {
|
|
579
|
+
const projDir = resolveProjectDir();
|
|
580
|
+
const verboMdPath = path.join(projDir, 'VERBO.md');
|
|
581
|
+
const validationErr = validateFieldValue(field, value);
|
|
582
|
+
if (validationErr) {
|
|
583
|
+
console.error(validationErr);
|
|
584
|
+
process.exit(1);
|
|
585
|
+
}
|
|
586
|
+
const warning = requiresConfirmation(field, value);
|
|
587
|
+
if (warning) {
|
|
588
|
+
console.log(warning);
|
|
589
|
+
const readline = await import('readline');
|
|
590
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
591
|
+
await new Promise((resolve) => {
|
|
592
|
+
rl.question('Confirm? (y/N) ', (answer) => {
|
|
593
|
+
rl.close();
|
|
594
|
+
if (answer.trim().toLowerCase() !== 'y') {
|
|
595
|
+
console.log('Aborted.');
|
|
596
|
+
process.exit(0);
|
|
597
|
+
}
|
|
598
|
+
resolve();
|
|
599
|
+
});
|
|
600
|
+
});
|
|
601
|
+
}
|
|
602
|
+
setVerboMdField(verboMdPath, field, value);
|
|
603
|
+
console.log(`✓ VERBO.md updated: ${field} → ${value}`);
|
|
604
|
+
});
|
|
605
|
+
configCmd
|
|
606
|
+
.command('get [field]')
|
|
607
|
+
.description('Show current VERBO.md configuration (or a specific field)')
|
|
608
|
+
.action((field) => {
|
|
609
|
+
const projDir = resolveProjectDir();
|
|
610
|
+
const verboMdPath = path.join(projDir, 'VERBO.md');
|
|
611
|
+
if (field) {
|
|
612
|
+
if (!FIELD_REGISTRY[field]) {
|
|
613
|
+
const known = Object.keys(FIELD_REGISTRY).join(', ');
|
|
614
|
+
console.error(`Unknown field "${field}". Known fields: ${known}`);
|
|
615
|
+
process.exit(1);
|
|
616
|
+
}
|
|
617
|
+
const fields = getVerboMdFields(verboMdPath);
|
|
618
|
+
const val = fields[field];
|
|
619
|
+
if (val === undefined) {
|
|
620
|
+
console.log(`${field}: (not set)`);
|
|
621
|
+
}
|
|
622
|
+
else {
|
|
623
|
+
console.log(`${field}: ${val}`);
|
|
624
|
+
}
|
|
625
|
+
return;
|
|
626
|
+
}
|
|
627
|
+
const fields = getVerboMdFields(verboMdPath);
|
|
628
|
+
if (Object.keys(fields).length === 0) {
|
|
629
|
+
console.log('No configuration set. Run `verbo init` to create VERBO.md.');
|
|
630
|
+
return;
|
|
631
|
+
}
|
|
632
|
+
for (const [k, v] of Object.entries(fields)) {
|
|
633
|
+
console.log(`${k}: ${v}`);
|
|
634
|
+
}
|
|
635
|
+
});
|
|
636
|
+
// ---- verbo init ----
|
|
637
|
+
program
|
|
638
|
+
.command('init')
|
|
639
|
+
.description('Initialize VERBO.md for your project')
|
|
640
|
+
.action(async () => {
|
|
641
|
+
const projDir = resolveProjectDir();
|
|
642
|
+
const verboMdPath = path.join(projDir, 'VERBO.md');
|
|
643
|
+
if (fs.existsSync(verboMdPath)) {
|
|
644
|
+
console.log('VERBO.md already exists. Delete it first to re-initialize.');
|
|
645
|
+
return;
|
|
646
|
+
}
|
|
647
|
+
console.log('Verbo Init — Setting up your project\n');
|
|
648
|
+
// Detect project
|
|
649
|
+
const info = detectProject(projDir);
|
|
650
|
+
const projectName = path.basename(projDir);
|
|
651
|
+
const defaults = buildDefaults(projectName, info);
|
|
652
|
+
// Check for gh CLI
|
|
653
|
+
try {
|
|
654
|
+
execSync('gh --version', { stdio: 'ignore' });
|
|
655
|
+
}
|
|
656
|
+
catch {
|
|
657
|
+
console.log(' Warning: gh CLI not found. Auto-PR creation requires it.');
|
|
658
|
+
console.log(' Install: https://cli.github.com/\n');
|
|
659
|
+
}
|
|
660
|
+
// Run questionnaire
|
|
661
|
+
const answers = await runQuestionnaire(defaults);
|
|
662
|
+
// Render and write
|
|
663
|
+
const md = renderVerboMd(answers);
|
|
664
|
+
fs.writeFileSync(verboMdPath, md, 'utf-8');
|
|
665
|
+
console.log(`\n VERBO.md created at ${verboMdPath}`);
|
|
666
|
+
console.log(' Agents will follow these rules when launched with verbo start.\n');
|
|
667
|
+
});
|
|
668
|
+
// ---- verbo login ----
|
|
669
|
+
program
|
|
670
|
+
.command('login')
|
|
671
|
+
.description('Log in to Verbo (opens browser)')
|
|
672
|
+
.action(async () => {
|
|
673
|
+
clearSession();
|
|
674
|
+
await ensureAuth();
|
|
675
|
+
const session = readSession();
|
|
676
|
+
if (session) {
|
|
677
|
+
console.log(`Logged in as ${session.email} (${session.plan} plan)`);
|
|
678
|
+
}
|
|
679
|
+
});
|
|
680
|
+
// ---- verbo logout ----
|
|
681
|
+
program
|
|
682
|
+
.command('logout')
|
|
683
|
+
.description('Log out and clear local credentials')
|
|
684
|
+
.action(() => {
|
|
685
|
+
clearSession();
|
|
686
|
+
console.log('Logged out. Run verbo login to authenticate again.');
|
|
687
|
+
});
|
|
688
|
+
// ---- verbo cost ----
|
|
689
|
+
function fmtUsd(n) {
|
|
690
|
+
return '$' + n.toFixed(4);
|
|
691
|
+
}
|
|
692
|
+
function fmtTokens(n) {
|
|
693
|
+
if (n >= 1_000_000)
|
|
694
|
+
return (n / 1_000_000).toFixed(2) + 'M';
|
|
695
|
+
if (n >= 1_000)
|
|
696
|
+
return (n / 1_000).toFixed(1) + 'k';
|
|
697
|
+
return String(n);
|
|
698
|
+
}
|
|
699
|
+
const costCmd = program
|
|
700
|
+
.command('cost')
|
|
701
|
+
.description('Show cost tracking summary and breakdowns');
|
|
702
|
+
costCmd
|
|
703
|
+
.command('summary')
|
|
704
|
+
.description('Overall cost summary')
|
|
705
|
+
.action(() => {
|
|
706
|
+
const { costStore } = initAll();
|
|
707
|
+
const s = costStore.getSummary();
|
|
708
|
+
const today = costStore.getCostToday();
|
|
709
|
+
console.log('=== Cost Summary ===');
|
|
710
|
+
console.log(`Total spent: ${fmtUsd(s.totalCostUsd)}`);
|
|
711
|
+
console.log(`Spent today: ${fmtUsd(today)}`);
|
|
712
|
+
console.log(`Entries: ${s.entryCount}`);
|
|
713
|
+
console.log(`Input tokens: ${fmtTokens(s.totalInputTokens)}`);
|
|
714
|
+
console.log(`Output tokens: ${fmtTokens(s.totalOutputTokens)}`);
|
|
715
|
+
console.log(`Cache read: ${fmtTokens(s.totalCacheReadTokens)}`);
|
|
716
|
+
console.log(`Cache write: ${fmtTokens(s.totalCacheWriteTokens)}`);
|
|
717
|
+
});
|
|
718
|
+
costCmd
|
|
719
|
+
.command('by-task')
|
|
720
|
+
.description('Cost breakdown by task')
|
|
721
|
+
.action(() => {
|
|
722
|
+
const { costStore, tasks } = initAll();
|
|
723
|
+
const groups = costStore.getCostByTask();
|
|
724
|
+
if (groups.length === 0) {
|
|
725
|
+
console.log('No cost data recorded yet.');
|
|
726
|
+
return;
|
|
727
|
+
}
|
|
728
|
+
console.log('=== Cost by Task ===');
|
|
729
|
+
for (const g of groups) {
|
|
730
|
+
const task = g.group !== '(no task)' ? tasks.get(g.group) : null;
|
|
731
|
+
const label = task ? `${g.group.slice(0, 8)}… ${task.title}` : g.group;
|
|
732
|
+
console.log(` ${fmtUsd(g.costUsd).padStart(10)} ${String(g.entryCount).padStart(4)} entries ${label}`);
|
|
733
|
+
}
|
|
734
|
+
});
|
|
735
|
+
costCmd
|
|
736
|
+
.command('by-day')
|
|
737
|
+
.description('Cost breakdown by day')
|
|
738
|
+
.action(() => {
|
|
739
|
+
const { costStore } = initAll();
|
|
740
|
+
const groups = costStore.getCostByDay();
|
|
741
|
+
if (groups.length === 0) {
|
|
742
|
+
console.log('No cost data recorded yet.');
|
|
743
|
+
return;
|
|
744
|
+
}
|
|
745
|
+
console.log('=== Cost by Day ===');
|
|
746
|
+
for (const g of groups) {
|
|
747
|
+
console.log(` ${g.group} ${fmtUsd(g.costUsd).padStart(10)} ${String(g.entryCount).padStart(4)} entries`);
|
|
748
|
+
}
|
|
749
|
+
});
|
|
750
|
+
costCmd
|
|
751
|
+
.command('by-model')
|
|
752
|
+
.description('Cost breakdown by model (requires cost_events data)')
|
|
753
|
+
.action(() => {
|
|
754
|
+
const { db } = initAll();
|
|
755
|
+
const raw = db.raw();
|
|
756
|
+
const rows = raw.prepare(`SELECT model, SUM(cost_usd) AS cost_usd, COUNT(*) AS entry_count
|
|
757
|
+
FROM cost_events GROUP BY model ORDER BY cost_usd DESC`).all();
|
|
758
|
+
if (rows.length === 0) {
|
|
759
|
+
console.log('No model-level cost data recorded yet.');
|
|
760
|
+
console.log('(Model tracking uses the cost_events table, populated by providers that report model info.)');
|
|
761
|
+
return;
|
|
762
|
+
}
|
|
763
|
+
console.log('=== Cost by Model ===');
|
|
764
|
+
for (const r of rows) {
|
|
765
|
+
console.log(` ${fmtUsd(r.cost_usd).padStart(10)} ${String(r.entry_count).padStart(4)} entries ${r.model}`);
|
|
766
|
+
}
|
|
767
|
+
});
|
|
768
|
+
costCmd
|
|
769
|
+
.command('by-pipeline')
|
|
770
|
+
.description('Cost breakdown by pipeline run')
|
|
771
|
+
.action(() => {
|
|
772
|
+
const { costStore } = initAll();
|
|
773
|
+
const groups = costStore.getCostByPipeline();
|
|
774
|
+
if (groups.length === 0) {
|
|
775
|
+
console.log('No cost data recorded yet.');
|
|
776
|
+
return;
|
|
777
|
+
}
|
|
778
|
+
console.log('=== Cost by Pipeline Run ===');
|
|
779
|
+
for (const g of groups) {
|
|
780
|
+
console.log(` ${fmtUsd(g.costUsd).padStart(10)} ${String(g.entryCount).padStart(4)} entries ${g.group}`);
|
|
781
|
+
}
|
|
782
|
+
});
|
|
783
|
+
costCmd
|
|
784
|
+
.command('update-prices <model>')
|
|
785
|
+
.description('Set or update pricing for a model (costs per 1M tokens)')
|
|
786
|
+
.option('--input <cost>', 'Input cost per 1M tokens', parseFloat)
|
|
787
|
+
.option('--output <cost>', 'Output cost per 1M tokens', parseFloat)
|
|
788
|
+
.option('--cache-read <cost>', 'Cache read cost per 1M tokens', parseFloat)
|
|
789
|
+
.option('--cache-write <cost>', 'Cache write cost per 1M tokens', parseFloat)
|
|
790
|
+
.action((model, opts) => {
|
|
791
|
+
if (opts.input == null || opts.output == null) {
|
|
792
|
+
console.error('--input and --output are required.');
|
|
793
|
+
process.exit(1);
|
|
794
|
+
}
|
|
795
|
+
const { costStore } = initAll();
|
|
796
|
+
costStore.updateModelPricing({
|
|
797
|
+
model,
|
|
798
|
+
inputCostPer1mTokens: opts.input,
|
|
799
|
+
outputCostPer1mTokens: opts.output,
|
|
800
|
+
cacheReadCostPer1mTokens: opts.cacheRead ?? null,
|
|
801
|
+
cacheWriteCostPer1mTokens: opts.cacheWrite ?? null,
|
|
802
|
+
});
|
|
803
|
+
console.log(`Pricing updated for model: ${model}`);
|
|
804
|
+
console.log(` Input: $${opts.input}/1M tokens`);
|
|
805
|
+
console.log(` Output: $${opts.output}/1M tokens`);
|
|
806
|
+
if (opts.cacheRead != null)
|
|
807
|
+
console.log(` Cache read: $${opts.cacheRead}/1M tokens`);
|
|
808
|
+
if (opts.cacheWrite != null)
|
|
809
|
+
console.log(` Cache write: $${opts.cacheWrite}/1M tokens`);
|
|
810
|
+
});
|
|
811
|
+
costCmd
|
|
812
|
+
.command('prices')
|
|
813
|
+
.description('List all configured model prices')
|
|
814
|
+
.action(() => {
|
|
815
|
+
const { costStore } = initAll();
|
|
816
|
+
const prices = costStore.listModelPricing();
|
|
817
|
+
if (prices.length === 0) {
|
|
818
|
+
console.log('No custom model pricing configured.');
|
|
819
|
+
console.log('Using defaults: $3/1M input, $15/1M output, $0.30/1M cache-read, $3.75/1M cache-write');
|
|
820
|
+
return;
|
|
821
|
+
}
|
|
822
|
+
console.log('=== Model Pricing ===');
|
|
823
|
+
for (const p of prices) {
|
|
824
|
+
console.log(` ${p.model}`);
|
|
825
|
+
console.log(` Input: $${p.inputCostPer1mTokens}/1M Output: $${p.outputCostPer1mTokens}/1M`);
|
|
826
|
+
if (p.cacheReadCostPer1mTokens != null)
|
|
827
|
+
console.log(` Cache read: $${p.cacheReadCostPer1mTokens}/1M Cache write: $${p.cacheWriteCostPer1mTokens}/1M`);
|
|
828
|
+
}
|
|
829
|
+
});
|
|
830
|
+
// Default: show summary when just `verbo cost` is run
|
|
831
|
+
costCmd.action(() => {
|
|
832
|
+
const { costStore } = initAll();
|
|
833
|
+
const s = costStore.getSummary();
|
|
834
|
+
const today = costStore.getCostToday();
|
|
835
|
+
console.log('=== Cost Summary ===');
|
|
836
|
+
console.log(`Total spent: ${fmtUsd(s.totalCostUsd)}`);
|
|
837
|
+
console.log(`Spent today: ${fmtUsd(today)}`);
|
|
838
|
+
console.log(`Entries: ${s.entryCount}`);
|
|
839
|
+
console.log(`Input tokens: ${fmtTokens(s.totalInputTokens)}`);
|
|
840
|
+
console.log(`Output tokens: ${fmtTokens(s.totalOutputTokens)}`);
|
|
841
|
+
console.log(`Cache read: ${fmtTokens(s.totalCacheReadTokens)}`);
|
|
842
|
+
console.log(`Cache write: ${fmtTokens(s.totalCacheWriteTokens)}`);
|
|
843
|
+
console.log('');
|
|
844
|
+
console.log('Subcommands: summary, by-task, by-day, by-model, by-pipeline, update-prices, prices');
|
|
845
|
+
});
|
|
846
|
+
// ---- verbo whoami ----
|
|
847
|
+
program
|
|
848
|
+
.command('whoami')
|
|
849
|
+
.description('Show current authenticated user and plan')
|
|
850
|
+
.action(() => {
|
|
851
|
+
const session = readSession();
|
|
852
|
+
if (!session) {
|
|
853
|
+
console.log('Not logged in. Run verbo login.');
|
|
854
|
+
return;
|
|
855
|
+
}
|
|
856
|
+
console.log(`Email: ${session.email}`);
|
|
857
|
+
console.log(`Plan: ${session.plan}`);
|
|
858
|
+
console.log(`Agents: ${session.limits.maxAgents === -1 ? 'unlimited' : session.limits.maxAgents}`);
|
|
859
|
+
console.log(`Tasks: ${session.limits.maxTasks === -1 ? 'unlimited' : session.limits.maxTasks}`);
|
|
860
|
+
});
|
|
861
|
+
// ---- Default action: TUI ----
|
|
862
|
+
program
|
|
863
|
+
.action(async () => {
|
|
864
|
+
// ensureAuth() is handled by the preAction hook
|
|
865
|
+
await ensureInit(resolveProjectDir());
|
|
866
|
+
const session = readSession();
|
|
867
|
+
let flashMessage = null;
|
|
868
|
+
const all = initAll();
|
|
869
|
+
const { projectDir, tasks, sessions, detector, parallelizer, hotFiles, approvals, humanActions, orchestratorStore, pipelineRunner, budgetPerTaskUsd, } = all;
|
|
870
|
+
const auditLogForTui = createAuditLogStore(all.db);
|
|
871
|
+
const pidFile = path.join(projectDir, '.verbo', 'daemon.pid');
|
|
872
|
+
fs.writeFileSync(pidFile, String(process.pid), 'utf-8');
|
|
873
|
+
const approvalServer = await createApprovalServer({
|
|
874
|
+
port: 5050,
|
|
875
|
+
approvals,
|
|
876
|
+
sessions,
|
|
877
|
+
pollIntervalMs: 500,
|
|
878
|
+
timeoutMs: 300000,
|
|
879
|
+
});
|
|
880
|
+
console.log(`Approval server running on http://localhost:${approvalServer.port()}`);
|
|
881
|
+
// Re-create launcher with the actual approval server port (and budget)
|
|
882
|
+
const launcher = createAgentLauncher({
|
|
883
|
+
projectDir,
|
|
884
|
+
mcpServerCommand: MCP_SERVER_COMMAND,
|
|
885
|
+
approvalPort: approvalServer.port(),
|
|
886
|
+
budgetPerTaskUsd,
|
|
887
|
+
learningStore: all.learnings,
|
|
888
|
+
});
|
|
889
|
+
// Initialize notifications from VERBO.md
|
|
890
|
+
const notificationConfig = readNotificationConfig(projectDir);
|
|
891
|
+
const notifications = createNotifications(notificationConfig);
|
|
892
|
+
// Sync pipelines from VERBO.md [pipelines] section: create runs for configured pipelines
|
|
893
|
+
const pipelineRunIds = new Set();
|
|
894
|
+
const pipelineConfigs = readPipelineConfigs(projectDir);
|
|
895
|
+
for (const pipelineConfig of pipelineConfigs) {
|
|
896
|
+
if (pipelineConfig.steps.length === 0)
|
|
897
|
+
continue;
|
|
898
|
+
// Map each step title to a matching task (first task whose title matches)
|
|
899
|
+
const allTasks = tasks.list();
|
|
900
|
+
const resolvedSteps = pipelineConfig.steps.map((step, i) => {
|
|
901
|
+
const match = allTasks.find((t) => t.title.toLowerCase() === step.title.toLowerCase());
|
|
902
|
+
return match ? {
|
|
903
|
+
id: `${pipelineConfig.name}-step-${i}`,
|
|
904
|
+
name: step.title,
|
|
905
|
+
taskId: match.id,
|
|
906
|
+
on_failure: (step.onFailure === 'notify' ? 'stop' : step.onFailure),
|
|
907
|
+
requires_approval: step.requiresApproval,
|
|
908
|
+
} : null;
|
|
909
|
+
});
|
|
910
|
+
// Only start a run if all steps have matching tasks
|
|
911
|
+
if (resolvedSteps.some((s) => s === null))
|
|
912
|
+
continue;
|
|
913
|
+
const pipeline = {
|
|
914
|
+
id: pipelineConfig.name,
|
|
915
|
+
name: pipelineConfig.title,
|
|
916
|
+
steps: resolvedSteps,
|
|
917
|
+
};
|
|
918
|
+
try {
|
|
919
|
+
const run = pipelineRunner.start(pipeline);
|
|
920
|
+
pipelineRunIds.add(run.id);
|
|
921
|
+
}
|
|
922
|
+
catch {
|
|
923
|
+
// Non-fatal: pipeline start failure doesn't block the TUI
|
|
924
|
+
}
|
|
925
|
+
}
|
|
926
|
+
// Clean up stale state from previous runs (orphaned worktrees, sessions, approvals)
|
|
927
|
+
const cleanupResult = await cleanStaleState({
|
|
928
|
+
tasks, sessions, approvals, launcher, projectDir,
|
|
929
|
+
pipelineRunner, pipelineRunIds,
|
|
930
|
+
});
|
|
931
|
+
if (cleanupResult.worktreesCleaned > 0) {
|
|
932
|
+
console.log(`Cleaned up ${cleanupResult.worktreesCleaned} orphaned worktree(s) from previous run`);
|
|
933
|
+
}
|
|
934
|
+
// Prune old audit log entries (retain 90 days by default)
|
|
935
|
+
const auditPruned = pruneAuditLog(all.db.raw(), 90);
|
|
936
|
+
if (auditPruned > 0) {
|
|
937
|
+
console.log(`Pruned ${auditPruned} audit log entries older than 90 days`);
|
|
938
|
+
}
|
|
939
|
+
// Bootstrap team members from VERBO.md [team] section
|
|
940
|
+
const verboMdPath = path.join(projectDir, 'VERBO.md');
|
|
941
|
+
if (fs.existsSync(verboMdPath)) {
|
|
942
|
+
const verboMdContent = fs.readFileSync(verboMdPath, 'utf-8');
|
|
943
|
+
const teamSeeded = bootstrapTeamFromVerboMd(all.db.raw(), verboMdContent);
|
|
944
|
+
if (teamSeeded > 0) {
|
|
945
|
+
console.log(`Bootstrapped ${teamSeeded} team member(s) from VERBO.md`);
|
|
946
|
+
}
|
|
947
|
+
}
|
|
948
|
+
// Create merge queue for sequential PR processing
|
|
949
|
+
const mergeQueue = createMergeQueue({
|
|
950
|
+
baseBranch: readBaseBranch(projectDir),
|
|
951
|
+
humanActions,
|
|
952
|
+
onMerged: () => {
|
|
953
|
+
hotFiles.refresh();
|
|
954
|
+
},
|
|
955
|
+
});
|
|
956
|
+
// Create orchestrator for review-and-push workflow
|
|
957
|
+
const orchestrator = createGitHubOrchestrator({
|
|
958
|
+
projectDir,
|
|
959
|
+
humanActions,
|
|
960
|
+
store: orchestratorStore,
|
|
961
|
+
mergeQueue,
|
|
962
|
+
});
|
|
963
|
+
// Kill session helper: kill PID, clean up approvals, mark errored
|
|
964
|
+
function killSessionById(sessionId) {
|
|
965
|
+
const session = sessions.get(sessionId);
|
|
966
|
+
if (session?.pid !== null && session?.pid !== undefined) {
|
|
967
|
+
launcher.kill(session.pid);
|
|
968
|
+
}
|
|
969
|
+
approvals.deleteBySession(sessionId);
|
|
970
|
+
sessions.error(sessionId);
|
|
971
|
+
}
|
|
972
|
+
// Create conflict enforcer for automatic conflict resolution
|
|
973
|
+
const enforcer = createConflictEnforcer({
|
|
974
|
+
tasks,
|
|
975
|
+
sessions,
|
|
976
|
+
detector,
|
|
977
|
+
killSession: killSessionById,
|
|
978
|
+
});
|
|
979
|
+
// Periodic conflict enforcement — check every 2 seconds
|
|
980
|
+
const enforcementInterval = setInterval(() => {
|
|
981
|
+
enforcer.enforce().then((actions) => {
|
|
982
|
+
for (const action of actions) {
|
|
983
|
+
// Terminal bell: conflict enforcement happened
|
|
984
|
+
process.stderr.write('\x07');
|
|
985
|
+
}
|
|
986
|
+
}).catch(() => { });
|
|
987
|
+
}, 2000);
|
|
988
|
+
// Factory mode — automatic task dispatch pipeline
|
|
989
|
+
const FACTORY_MAX_AGENTS = 4;
|
|
990
|
+
let factoryMode = false;
|
|
991
|
+
const factoryInterval = setInterval(() => {
|
|
992
|
+
const activeSessions = sessions.listActive();
|
|
993
|
+
if (activeSessions.length >= FACTORY_MAX_AGENTS)
|
|
994
|
+
return;
|
|
995
|
+
const allTasks = tasks.list();
|
|
996
|
+
const todoTasks = allTasks.filter((t) => t.status === 'todo' && !t.title.startsWith('HUMANO:'));
|
|
997
|
+
if (todoTasks.length === 0)
|
|
998
|
+
return;
|
|
999
|
+
const activeFiles = allTasks
|
|
1000
|
+
.filter((t) => t.status === 'in_progress')
|
|
1001
|
+
.flatMap((t) => t.filePaths);
|
|
1002
|
+
// Find the first non-conflicting task to launch.
|
|
1003
|
+
// Subtasks (parentTaskId set) are always launched regardless of factoryMode.
|
|
1004
|
+
// Regular top-level tasks are only launched when factoryMode is on.
|
|
1005
|
+
for (const candidate of todoTasks) {
|
|
1006
|
+
const isSubtask = !!candidate.parentTaskId;
|
|
1007
|
+
if (!isSubtask && !factoryMode)
|
|
1008
|
+
continue;
|
|
1009
|
+
if (activeFiles.length > 0 && candidate.filePaths.length > 0) {
|
|
1010
|
+
if (hasFileOverlap(candidate.filePaths, activeFiles))
|
|
1011
|
+
continue;
|
|
1012
|
+
}
|
|
1013
|
+
launchAgentForTask(candidate.id, tasks, sessions, launcher, approvals, projectDir, 'medium', enforcer, orchestrator, { notifications, pipelineRunner, pipelineRunIds }).catch((err) => {
|
|
1014
|
+
console.error(`Factory: failed to launch ${candidate.id}:`, err.message);
|
|
1015
|
+
});
|
|
1016
|
+
break;
|
|
1017
|
+
}
|
|
1018
|
+
}, 3000);
|
|
1019
|
+
function getState() {
|
|
1020
|
+
const allTasks = tasks.list();
|
|
1021
|
+
const activeSessions = sessions.listActive();
|
|
1022
|
+
const conflicts = detector.findOverlaps();
|
|
1023
|
+
const parallelBatch = parallelizer.findSafeBatch();
|
|
1024
|
+
// Group active sessions by humanId for teamMembers
|
|
1025
|
+
const byHuman = new Map();
|
|
1026
|
+
for (const session of activeSessions) {
|
|
1027
|
+
if (!byHuman.has(session.humanId)) {
|
|
1028
|
+
byHuman.set(session.humanId, []);
|
|
1029
|
+
}
|
|
1030
|
+
byHuman.get(session.humanId).push(session);
|
|
1031
|
+
}
|
|
1032
|
+
const teamMembers = Array.from(byHuman.entries()).map(([humanId, sArr]) => ({
|
|
1033
|
+
humanId,
|
|
1034
|
+
sessions: sArr,
|
|
1035
|
+
}));
|
|
1036
|
+
return {
|
|
1037
|
+
tasks: allTasks,
|
|
1038
|
+
activeSessions,
|
|
1039
|
+
conflicts,
|
|
1040
|
+
parallelBatch,
|
|
1041
|
+
teamMembers,
|
|
1042
|
+
syncStatus: 'disconnected',
|
|
1043
|
+
pendingApprovals: approvals.listPending(),
|
|
1044
|
+
resolvedApprovals: approvals.listResolved(),
|
|
1045
|
+
humanActions: humanActions.listPending(),
|
|
1046
|
+
orchestratorStates: orchestratorStore.list(),
|
|
1047
|
+
yoloMode: approvalServer.isYoloMode(),
|
|
1048
|
+
factoryMode,
|
|
1049
|
+
flashMessage,
|
|
1050
|
+
intelligenceEnabled: isIntelligenceEnabled(),
|
|
1051
|
+
isCompliance: false,
|
|
1052
|
+
sandboxEnabled: false,
|
|
1053
|
+
mergeQueueItems: mergeQueue.getQueue(),
|
|
1054
|
+
mergeQueueProcessing: mergeQueue.isProcessing(),
|
|
1055
|
+
};
|
|
1056
|
+
}
|
|
1057
|
+
const tui = startTui({
|
|
1058
|
+
getState,
|
|
1059
|
+
teamName: 'local',
|
|
1060
|
+
onCreateTask: (title) => {
|
|
1061
|
+
const allTasks = tasks.list();
|
|
1062
|
+
const check = checkLimit('maxTasks', allTasks.length, session.limits);
|
|
1063
|
+
if (!check.allowed) {
|
|
1064
|
+
flashMessage = check.message;
|
|
1065
|
+
setTimeout(() => { flashMessage = null; }, 4000);
|
|
1066
|
+
return;
|
|
1067
|
+
}
|
|
1068
|
+
tasks.create({ title });
|
|
1069
|
+
},
|
|
1070
|
+
onDeleteTask: (taskId) => {
|
|
1071
|
+
tasks.delete(taskId);
|
|
1072
|
+
},
|
|
1073
|
+
onLaunchAgent: (taskId, trustLevel) => {
|
|
1074
|
+
const activeSessions = sessions.listActive();
|
|
1075
|
+
const check = checkLimit('maxAgents', activeSessions.length, session.limits);
|
|
1076
|
+
if (!check.allowed) {
|
|
1077
|
+
flashMessage = check.message;
|
|
1078
|
+
setTimeout(() => { flashMessage = null; }, 4000);
|
|
1079
|
+
return;
|
|
1080
|
+
}
|
|
1081
|
+
launchAgentForTask(taskId, tasks, sessions, launcher, approvals, projectDir, trustLevel ?? 'medium', enforcer, orchestrator, { notifications, pipelineRunner, pipelineRunIds }).catch((err) => {
|
|
1082
|
+
const e = err;
|
|
1083
|
+
const debugLine = `[${new Date().toISOString()}] LAUNCH FAILED: ${e.message}\n${e.stack}\n`;
|
|
1084
|
+
fs.appendFileSync(path.join(projectDir, '.verbo', 'launch-debug.log'), debugLine);
|
|
1085
|
+
});
|
|
1086
|
+
},
|
|
1087
|
+
onLaunchBatch: (taskIds, trustLevel) => {
|
|
1088
|
+
const activeSessions = sessions.listActive();
|
|
1089
|
+
const check = checkLimit('maxAgents', activeSessions.length, session.limits);
|
|
1090
|
+
if (!check.allowed) {
|
|
1091
|
+
flashMessage = check.message;
|
|
1092
|
+
setTimeout(() => { flashMessage = null; }, 4000);
|
|
1093
|
+
return;
|
|
1094
|
+
}
|
|
1095
|
+
for (const taskId of taskIds) {
|
|
1096
|
+
launchAgentForTask(taskId, tasks, sessions, launcher, approvals, projectDir, trustLevel ?? 'medium', enforcer, orchestrator, { notifications, pipelineRunner, pipelineRunIds }).catch((err) => {
|
|
1097
|
+
console.error(`Failed to launch agent for ${taskId}:`, err.message);
|
|
1098
|
+
});
|
|
1099
|
+
}
|
|
1100
|
+
},
|
|
1101
|
+
onKillAgent: (sessionId) => {
|
|
1102
|
+
killSessionById(sessionId);
|
|
1103
|
+
},
|
|
1104
|
+
onDismiss: () => {
|
|
1105
|
+
clearInterval(enforcementInterval);
|
|
1106
|
+
clearInterval(factoryInterval);
|
|
1107
|
+
approvalServer.stop();
|
|
1108
|
+
tui.unmount();
|
|
1109
|
+
console.log('TUI dismissed. Daemon still running.');
|
|
1110
|
+
},
|
|
1111
|
+
onApproveRequest: (id) => {
|
|
1112
|
+
approvals.approve(id);
|
|
1113
|
+
},
|
|
1114
|
+
onDenyRequest: (id) => {
|
|
1115
|
+
approvals.deny(id);
|
|
1116
|
+
},
|
|
1117
|
+
onToggleYolo: () => {
|
|
1118
|
+
approvalServer.setYoloMode(!approvalServer.isYoloMode());
|
|
1119
|
+
},
|
|
1120
|
+
onToggleFactory: () => {
|
|
1121
|
+
factoryMode = !factoryMode;
|
|
1122
|
+
},
|
|
1123
|
+
onResolveHumanAction: (id) => {
|
|
1124
|
+
humanActions.resolve(id);
|
|
1125
|
+
},
|
|
1126
|
+
onDecomposeTask: async (taskId) => {
|
|
1127
|
+
const task = tasks.get(taskId);
|
|
1128
|
+
if (!task)
|
|
1129
|
+
return null;
|
|
1130
|
+
const input = [task.title, task.description].filter(Boolean).join('\n\n');
|
|
1131
|
+
const decomposer = createTaskDecomposer(tasks);
|
|
1132
|
+
return decomposer.decompose(input);
|
|
1133
|
+
},
|
|
1134
|
+
onConfirmDecompose: (subtasks, parentTaskId) => {
|
|
1135
|
+
const decomposer = createTaskDecomposer(tasks);
|
|
1136
|
+
decomposer.confirm(subtasks, parentTaskId).catch((err) => {
|
|
1137
|
+
console.error('Failed to create subtasks:', err.message);
|
|
1138
|
+
});
|
|
1139
|
+
},
|
|
1140
|
+
onForcePush: (taskId) => {
|
|
1141
|
+
orchestrator.forcePush(taskId).then((result) => {
|
|
1142
|
+
if (result.prUrl) {
|
|
1143
|
+
tasks.update(taskId, { prUrl: result.prUrl });
|
|
1144
|
+
}
|
|
1145
|
+
}).catch((err) => {
|
|
1146
|
+
console.error(`Force push failed for task ${taskId}:`, err.message);
|
|
1147
|
+
});
|
|
1148
|
+
},
|
|
1149
|
+
getLearnings: () => all.learnings.list(),
|
|
1150
|
+
getAuditEntries: () => auditLogForTui.query(),
|
|
1151
|
+
getTaskTitleById: (taskId) => tasks.get(taskId)?.title ?? null,
|
|
1152
|
+
});
|
|
1153
|
+
await tui.waitUntilExit();
|
|
1154
|
+
});
|
|
1155
|
+
program.parse();
|
|
1156
|
+
//# sourceMappingURL=verbo.js.map
|