@cleocode/cleo 2026.4.161 → 2026.5.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/dist/backfill/audit-columns.d.ts +105 -0
- package/dist/backfill/audit-columns.d.ts.map +1 -0
- package/dist/backfill/audit-columns.js +258 -0
- package/dist/backfill/audit-columns.js.map +1 -0
- package/dist/cli/commands/adapter.d.ts +28 -0
- package/dist/cli/commands/adapter.d.ts.map +1 -0
- package/dist/cli/commands/adapter.js +119 -0
- package/dist/cli/commands/adapter.js.map +1 -0
- package/dist/cli/commands/add-batch.d.ts +33 -0
- package/dist/cli/commands/add-batch.d.ts.map +1 -0
- package/dist/cli/commands/add-batch.js +148 -0
- package/dist/cli/commands/add-batch.js.map +1 -0
- package/dist/cli/commands/add.d.ts +162 -0
- package/dist/cli/commands/add.d.ts.map +1 -0
- package/dist/cli/commands/add.js +279 -0
- package/dist/cli/commands/add.js.map +1 -0
- package/dist/cli/commands/admin.d.ts +24 -0
- package/dist/cli/commands/admin.d.ts.map +1 -0
- package/dist/cli/commands/admin.js +283 -0
- package/dist/cli/commands/admin.js.map +1 -0
- package/dist/cli/commands/adr.d.ts +33 -0
- package/dist/cli/commands/adr.d.ts.map +1 -0
- package/dist/cli/commands/adr.js +147 -0
- package/dist/cli/commands/adr.js.map +1 -0
- package/dist/cli/commands/agent-profile-status.d.ts +98 -0
- package/dist/cli/commands/agent-profile-status.d.ts.map +1 -0
- package/dist/cli/commands/agent-profile-status.js +71 -0
- package/dist/cli/commands/agent-profile-status.js.map +1 -0
- package/dist/cli/commands/agent.d.ts +47 -0
- package/dist/cli/commands/agent.d.ts.map +1 -0
- package/dist/cli/commands/agent.js +2976 -0
- package/dist/cli/commands/agent.js.map +1 -0
- package/dist/cli/commands/analyze.d.ts +21 -0
- package/dist/cli/commands/analyze.d.ts.map +1 -0
- package/dist/cli/commands/analyze.js +32 -0
- package/dist/cli/commands/analyze.js.map +1 -0
- package/dist/cli/commands/archive-stats.d.ts +66 -0
- package/dist/cli/commands/archive-stats.d.ts.map +1 -0
- package/dist/cli/commands/archive-stats.js +93 -0
- package/dist/cli/commands/archive-stats.js.map +1 -0
- package/dist/cli/commands/archive.d.ts +42 -0
- package/dist/cli/commands/archive.d.ts.map +1 -0
- package/dist/cli/commands/archive.js +59 -0
- package/dist/cli/commands/archive.js.map +1 -0
- package/dist/cli/commands/audit.d.ts +22 -0
- package/dist/cli/commands/audit.d.ts.map +1 -0
- package/dist/cli/commands/audit.js +137 -0
- package/dist/cli/commands/audit.js.map +1 -0
- package/dist/cli/commands/backfill.d.ts +56 -0
- package/dist/cli/commands/backfill.d.ts.map +1 -0
- package/dist/cli/commands/backfill.js +161 -0
- package/dist/cli/commands/backfill.js.map +1 -0
- package/dist/cli/commands/backup-inspect.d.ts +33 -0
- package/dist/cli/commands/backup-inspect.d.ts.map +1 -0
- package/dist/cli/commands/backup-inspect.js +430 -0
- package/dist/cli/commands/backup-inspect.js.map +1 -0
- package/dist/cli/commands/backup.d.ts +23 -0
- package/dist/cli/commands/backup.d.ts.map +1 -0
- package/dist/cli/commands/backup.js +564 -0
- package/dist/cli/commands/backup.js.map +1 -0
- package/dist/cli/commands/blockers.d.ts +20 -0
- package/dist/cli/commands/blockers.d.ts.map +1 -0
- package/dist/cli/commands/blockers.js +31 -0
- package/dist/cli/commands/blockers.js.map +1 -0
- package/dist/cli/commands/brain.d.ts +37 -0
- package/dist/cli/commands/brain.d.ts.map +1 -0
- package/dist/cli/commands/brain.js +445 -0
- package/dist/cli/commands/brain.js.map +1 -0
- package/dist/cli/commands/briefing.d.ts +52 -0
- package/dist/cli/commands/briefing.d.ts.map +1 -0
- package/dist/cli/commands/briefing.js +69 -0
- package/dist/cli/commands/briefing.js.map +1 -0
- package/dist/cli/commands/bug.d.ts +61 -0
- package/dist/cli/commands/bug.d.ts.map +1 -0
- package/dist/cli/commands/bug.js +198 -0
- package/dist/cli/commands/bug.js.map +1 -0
- package/dist/cli/commands/cancel.d.ts +26 -0
- package/dist/cli/commands/cancel.d.ts.map +1 -0
- package/dist/cli/commands/cancel.js +40 -0
- package/dist/cli/commands/cancel.js.map +1 -0
- package/dist/cli/commands/cant.d.ts +13 -0
- package/dist/cli/commands/cant.d.ts.map +1 -0
- package/dist/cli/commands/cant.js +245 -0
- package/dist/cli/commands/cant.js.map +1 -0
- package/dist/cli/commands/chain.d.ts +24 -0
- package/dist/cli/commands/chain.d.ts.map +1 -0
- package/dist/cli/commands/chain.js +116 -0
- package/dist/cli/commands/chain.js.map +1 -0
- package/dist/cli/commands/check.d.ts +18 -0
- package/dist/cli/commands/check.d.ts.map +1 -0
- package/dist/cli/commands/check.js +280 -0
- package/dist/cli/commands/check.js.map +1 -0
- package/dist/cli/commands/checkpoint.d.ts +27 -0
- package/dist/cli/commands/checkpoint.d.ts.map +1 -0
- package/dist/cli/commands/checkpoint.js +105 -0
- package/dist/cli/commands/checkpoint.js.map +1 -0
- package/dist/cli/commands/claim.d.ts +35 -0
- package/dist/cli/commands/claim.d.ts.map +1 -0
- package/dist/cli/commands/claim.js +35 -0
- package/dist/cli/commands/claim.js.map +1 -0
- package/dist/cli/commands/code.d.ts +22 -0
- package/dist/cli/commands/code.d.ts.map +1 -0
- package/dist/cli/commands/code.js +161 -0
- package/dist/cli/commands/code.js.map +1 -0
- package/dist/cli/commands/complete.d.ts +58 -0
- package/dist/cli/commands/complete.d.ts.map +1 -0
- package/dist/cli/commands/complete.js +83 -0
- package/dist/cli/commands/complete.js.map +1 -0
- package/dist/cli/commands/complexity.d.ts +13 -0
- package/dist/cli/commands/complexity.d.ts.map +1 -0
- package/dist/cli/commands/complexity.js +32 -0
- package/dist/cli/commands/complexity.js.map +1 -0
- package/dist/cli/commands/compliance.d.ts +27 -0
- package/dist/cli/commands/compliance.d.ts.map +1 -0
- package/dist/cli/commands/compliance.js +233 -0
- package/dist/cli/commands/compliance.js.map +1 -0
- package/dist/cli/commands/conduit.d.ts +28 -0
- package/dist/cli/commands/conduit.d.ts.map +1 -0
- package/dist/cli/commands/conduit.js +279 -0
- package/dist/cli/commands/conduit.js.map +1 -0
- package/dist/cli/commands/config.d.ts +25 -0
- package/dist/cli/commands/config.d.ts.map +1 -0
- package/dist/cli/commands/config.js +132 -0
- package/dist/cli/commands/config.js.map +1 -0
- package/dist/cli/commands/consensus.d.ts +21 -0
- package/dist/cli/commands/consensus.d.ts.map +1 -0
- package/dist/cli/commands/consensus.js +100 -0
- package/dist/cli/commands/consensus.js.map +1 -0
- package/dist/cli/commands/context.d.ts +19 -0
- package/dist/cli/commands/context.d.ts.map +1 -0
- package/dist/cli/commands/context.js +111 -0
- package/dist/cli/commands/context.js.map +1 -0
- package/dist/cli/commands/contribution.d.ts +21 -0
- package/dist/cli/commands/contribution.d.ts.map +1 -0
- package/dist/cli/commands/contribution.js +90 -0
- package/dist/cli/commands/contribution.js.map +1 -0
- package/dist/cli/commands/current.d.ts +18 -0
- package/dist/cli/commands/current.d.ts.map +1 -0
- package/dist/cli/commands/current.js +28 -0
- package/dist/cli/commands/current.js.map +1 -0
- package/dist/cli/commands/daemon.d.ts +36 -0
- package/dist/cli/commands/daemon.d.ts.map +1 -0
- package/dist/cli/commands/daemon.js +223 -0
- package/dist/cli/commands/daemon.js.map +1 -0
- package/dist/cli/commands/dash.d.ts +23 -0
- package/dist/cli/commands/dash.d.ts.map +1 -0
- package/dist/cli/commands/dash.js +38 -0
- package/dist/cli/commands/dash.js.map +1 -0
- package/dist/cli/commands/decomposition.d.ts +13 -0
- package/dist/cli/commands/decomposition.d.ts.map +1 -0
- package/dist/cli/commands/decomposition.js +92 -0
- package/dist/cli/commands/decomposition.js.map +1 -0
- package/dist/cli/commands/delete.d.ts +29 -0
- package/dist/cli/commands/delete.d.ts.map +1 -0
- package/dist/cli/commands/delete.js +55 -0
- package/dist/cli/commands/delete.js.map +1 -0
- package/dist/cli/commands/deps.d.ts +45 -0
- package/dist/cli/commands/deps.d.ts.map +1 -0
- package/dist/cli/commands/deps.js +170 -0
- package/dist/cli/commands/deps.js.map +1 -0
- package/dist/cli/commands/detect-drift.d.ts +23 -0
- package/dist/cli/commands/detect-drift.d.ts.map +1 -0
- package/dist/cli/commands/detect-drift.js +440 -0
- package/dist/cli/commands/detect-drift.js.map +1 -0
- package/dist/cli/commands/detect.d.ts +3 -0
- package/dist/cli/commands/detect.d.ts.map +1 -0
- package/dist/cli/commands/detect.js +14 -0
- package/dist/cli/commands/detect.js.map +1 -0
- package/dist/cli/commands/diagnostics.d.ts +19 -0
- package/dist/cli/commands/diagnostics.d.ts.map +1 -0
- package/dist/cli/commands/diagnostics.js +109 -0
- package/dist/cli/commands/diagnostics.js.map +1 -0
- package/dist/cli/commands/docs.d.ts +25 -0
- package/dist/cli/commands/docs.d.ts.map +1 -0
- package/dist/cli/commands/docs.js +798 -0
- package/dist/cli/commands/docs.js.map +1 -0
- package/dist/cli/commands/doctor-projects.d.ts +101 -0
- package/dist/cli/commands/doctor-projects.d.ts.map +1 -0
- package/dist/cli/commands/doctor-projects.js +188 -0
- package/dist/cli/commands/doctor-projects.js.map +1 -0
- package/dist/cli/commands/doctor.d.ts +66 -0
- package/dist/cli/commands/doctor.d.ts.map +1 -0
- package/dist/cli/commands/doctor.js +178 -0
- package/dist/cli/commands/doctor.js.map +1 -0
- package/dist/cli/commands/dynamic.d.ts +15 -0
- package/dist/cli/commands/dynamic.d.ts.map +1 -0
- package/dist/cli/commands/dynamic.js +21 -0
- package/dist/cli/commands/dynamic.js.map +1 -0
- package/dist/cli/commands/exists.d.ts +13 -0
- package/dist/cli/commands/exists.d.ts.map +1 -0
- package/dist/cli/commands/exists.js +40 -0
- package/dist/cli/commands/exists.js.map +1 -0
- package/dist/cli/commands/export-tasks.d.ts +46 -0
- package/dist/cli/commands/export-tasks.d.ts.map +1 -0
- package/dist/cli/commands/export-tasks.js +81 -0
- package/dist/cli/commands/export-tasks.js.map +1 -0
- package/dist/cli/commands/export.d.ts +35 -0
- package/dist/cli/commands/export.d.ts.map +1 -0
- package/dist/cli/commands/export.js +68 -0
- package/dist/cli/commands/export.js.map +1 -0
- package/dist/cli/commands/find.d.ts +54 -0
- package/dist/cli/commands/find.d.ts.map +1 -0
- package/dist/cli/commands/find.js +92 -0
- package/dist/cli/commands/find.js.map +1 -0
- package/dist/cli/commands/gc.d.ts +25 -0
- package/dist/cli/commands/gc.d.ts.map +1 -0
- package/dist/cli/commands/gc.js +165 -0
- package/dist/cli/commands/gc.js.map +1 -0
- package/dist/cli/commands/generate-changelog.d.ts +30 -0
- package/dist/cli/commands/generate-changelog.d.ts.map +1 -0
- package/dist/cli/commands/generate-changelog.js +270 -0
- package/dist/cli/commands/generate-changelog.js.map +1 -0
- package/dist/cli/commands/grade.d.ts +13 -0
- package/dist/cli/commands/grade.d.ts.map +1 -0
- package/dist/cli/commands/grade.js +27 -0
- package/dist/cli/commands/grade.js.map +1 -0
- package/dist/cli/commands/history.d.ts +13 -0
- package/dist/cli/commands/history.d.ts.map +1 -0
- package/dist/cli/commands/history.js +65 -0
- package/dist/cli/commands/history.js.map +1 -0
- package/dist/cli/commands/import-tasks.d.ts +60 -0
- package/dist/cli/commands/import-tasks.d.ts.map +1 -0
- package/dist/cli/commands/import-tasks.js +83 -0
- package/dist/cli/commands/import-tasks.js.map +1 -0
- package/dist/cli/commands/import.d.ts +42 -0
- package/dist/cli/commands/import.d.ts.map +1 -0
- package/dist/cli/commands/import.js +64 -0
- package/dist/cli/commands/import.js.map +1 -0
- package/dist/cli/commands/init.d.ts +65 -0
- package/dist/cli/commands/init.d.ts.map +1 -0
- package/dist/cli/commands/init.js +122 -0
- package/dist/cli/commands/init.js.map +1 -0
- package/dist/cli/commands/inject.d.ts +41 -0
- package/dist/cli/commands/inject.d.ts.map +1 -0
- package/dist/cli/commands/inject.js +56 -0
- package/dist/cli/commands/inject.js.map +1 -0
- package/dist/cli/commands/install-global.d.ts +48 -0
- package/dist/cli/commands/install-global.d.ts.map +1 -0
- package/dist/cli/commands/install-global.js +104 -0
- package/dist/cli/commands/install-global.js.map +1 -0
- package/dist/cli/commands/intelligence.d.ts +21 -0
- package/dist/cli/commands/intelligence.d.ts.map +1 -0
- package/dist/cli/commands/intelligence.js +145 -0
- package/dist/cli/commands/intelligence.js.map +1 -0
- package/dist/cli/commands/issue.d.ts +23 -0
- package/dist/cli/commands/issue.d.ts.map +1 -0
- package/dist/cli/commands/issue.js +152 -0
- package/dist/cli/commands/issue.js.map +1 -0
- package/dist/cli/commands/labels.d.ts +21 -0
- package/dist/cli/commands/labels.d.ts.map +1 -0
- package/dist/cli/commands/labels.js +65 -0
- package/dist/cli/commands/labels.js.map +1 -0
- package/dist/cli/commands/lifecycle.d.ts +25 -0
- package/dist/cli/commands/lifecycle.d.ts.map +1 -0
- package/dist/cli/commands/lifecycle.js +221 -0
- package/dist/cli/commands/lifecycle.js.map +1 -0
- package/dist/cli/commands/list.d.ts +28 -0
- package/dist/cli/commands/list.d.ts.map +1 -0
- package/dist/cli/commands/list.js +81 -0
- package/dist/cli/commands/list.js.map +1 -0
- package/dist/cli/commands/log.d.ts +36 -0
- package/dist/cli/commands/log.d.ts.map +1 -0
- package/dist/cli/commands/log.js +50 -0
- package/dist/cli/commands/log.js.map +1 -0
- package/dist/cli/commands/manifest.d.ts +15 -0
- package/dist/cli/commands/manifest.d.ts.map +1 -0
- package/dist/cli/commands/manifest.js +334 -0
- package/dist/cli/commands/manifest.js.map +1 -0
- package/dist/cli/commands/map.d.ts +25 -0
- package/dist/cli/commands/map.d.ts.map +1 -0
- package/dist/cli/commands/map.js +37 -0
- package/dist/cli/commands/map.js.map +1 -0
- package/dist/cli/commands/memory.d.ts +48 -0
- package/dist/cli/commands/memory.d.ts.map +1 -0
- package/dist/cli/commands/memory.js +2439 -0
- package/dist/cli/commands/memory.js.map +1 -0
- package/dist/cli/commands/migrate-claude-mem.d.ts +23 -0
- package/dist/cli/commands/migrate-claude-mem.d.ts.map +1 -0
- package/dist/cli/commands/migrate-claude-mem.js +181 -0
- package/dist/cli/commands/migrate-claude-mem.js.map +1 -0
- package/dist/cli/commands/next.d.ts +27 -0
- package/dist/cli/commands/next.d.ts.map +1 -0
- package/dist/cli/commands/next.js +40 -0
- package/dist/cli/commands/next.js.map +1 -0
- package/dist/cli/commands/nexus.d.ts +15 -0
- package/dist/cli/commands/nexus.d.ts.map +1 -0
- package/dist/cli/commands/nexus.js +3377 -0
- package/dist/cli/commands/nexus.js.map +1 -0
- package/dist/cli/commands/ops.d.ts +23 -0
- package/dist/cli/commands/ops.d.ts.map +1 -0
- package/dist/cli/commands/ops.js +35 -0
- package/dist/cli/commands/ops.js.map +1 -0
- package/dist/cli/commands/orchestrate.d.ts +48 -0
- package/dist/cli/commands/orchestrate.d.ts.map +1 -0
- package/dist/cli/commands/orchestrate.js +774 -0
- package/dist/cli/commands/orchestrate.js.map +1 -0
- package/dist/cli/commands/otel.d.ts +30 -0
- package/dist/cli/commands/otel.d.ts.map +1 -0
- package/dist/cli/commands/otel.js +193 -0
- package/dist/cli/commands/otel.js.map +1 -0
- package/dist/cli/commands/phase.d.ts +29 -0
- package/dist/cli/commands/phase.d.ts.map +1 -0
- package/dist/cli/commands/phase.js +189 -0
- package/dist/cli/commands/phase.js.map +1 -0
- package/dist/cli/commands/pivot.d.ts +34 -0
- package/dist/cli/commands/pivot.d.ts.map +1 -0
- package/dist/cli/commands/pivot.js +50 -0
- package/dist/cli/commands/pivot.js.map +1 -0
- package/dist/cli/commands/plan.d.ts +17 -0
- package/dist/cli/commands/plan.d.ts.map +1 -0
- package/dist/cli/commands/plan.js +27 -0
- package/dist/cli/commands/plan.js.map +1 -0
- package/dist/cli/commands/playbook.d.ts +26 -0
- package/dist/cli/commands/playbook.d.ts.map +1 -0
- package/dist/cli/commands/playbook.js +220 -0
- package/dist/cli/commands/playbook.js.map +1 -0
- package/dist/cli/commands/promote.d.ts +19 -0
- package/dist/cli/commands/promote.d.ts.map +1 -0
- package/dist/cli/commands/promote.js +27 -0
- package/dist/cli/commands/promote.js.map +1 -0
- package/dist/cli/commands/provider.d.ts +23 -0
- package/dist/cli/commands/provider.d.ts.map +1 -0
- package/dist/cli/commands/provider.js +168 -0
- package/dist/cli/commands/provider.js.map +1 -0
- package/dist/cli/commands/reason.d.ts +18 -0
- package/dist/cli/commands/reason.d.ts.map +1 -0
- package/dist/cli/commands/reason.js +102 -0
- package/dist/cli/commands/reason.js.map +1 -0
- package/dist/cli/commands/reconcile.d.ts +35 -0
- package/dist/cli/commands/reconcile.d.ts.map +1 -0
- package/dist/cli/commands/reconcile.js +102 -0
- package/dist/cli/commands/reconcile.js.map +1 -0
- package/dist/cli/commands/refresh-memory.d.ts +16 -0
- package/dist/cli/commands/refresh-memory.d.ts.map +1 -0
- package/dist/cli/commands/refresh-memory.js +34 -0
- package/dist/cli/commands/refresh-memory.js.map +1 -0
- package/dist/cli/commands/relates.d.ts +19 -0
- package/dist/cli/commands/relates.d.ts.map +1 -0
- package/dist/cli/commands/relates.js +129 -0
- package/dist/cli/commands/relates.js.map +1 -0
- package/dist/cli/commands/release.d.ts +27 -0
- package/dist/cli/commands/release.d.ts.map +1 -0
- package/dist/cli/commands/release.js +300 -0
- package/dist/cli/commands/release.js.map +1 -0
- package/dist/cli/commands/remote.d.ts +49 -0
- package/dist/cli/commands/remote.d.ts.map +1 -0
- package/dist/cli/commands/remote.js +265 -0
- package/dist/cli/commands/remote.js.map +1 -0
- package/dist/cli/commands/reorder.d.ts +31 -0
- package/dist/cli/commands/reorder.d.ts.map +1 -0
- package/dist/cli/commands/reorder.js +57 -0
- package/dist/cli/commands/reorder.js.map +1 -0
- package/dist/cli/commands/reparent.d.ts +27 -0
- package/dist/cli/commands/reparent.d.ts.map +1 -0
- package/dist/cli/commands/reparent.js +36 -0
- package/dist/cli/commands/reparent.js.map +1 -0
- package/dist/cli/commands/req.d.ts +37 -0
- package/dist/cli/commands/req.d.ts.map +1 -0
- package/dist/cli/commands/req.js +121 -0
- package/dist/cli/commands/req.js.map +1 -0
- package/dist/cli/commands/research.d.ts +25 -0
- package/dist/cli/commands/research.d.ts.map +1 -0
- package/dist/cli/commands/research.js +327 -0
- package/dist/cli/commands/research.js.map +1 -0
- package/dist/cli/commands/restore.d.ts +64 -0
- package/dist/cli/commands/restore.d.ts.map +1 -0
- package/dist/cli/commands/restore.js +539 -0
- package/dist/cli/commands/restore.js.map +1 -0
- package/dist/cli/commands/revert.d.ts +79 -0
- package/dist/cli/commands/revert.d.ts.map +1 -0
- package/dist/cli/commands/revert.js +300 -0
- package/dist/cli/commands/revert.js.map +1 -0
- package/dist/cli/commands/roadmap.d.ts +29 -0
- package/dist/cli/commands/roadmap.d.ts.map +1 -0
- package/dist/cli/commands/roadmap.js +43 -0
- package/dist/cli/commands/roadmap.js.map +1 -0
- package/dist/cli/commands/safestop.d.ts +41 -0
- package/dist/cli/commands/safestop.d.ts.map +1 -0
- package/dist/cli/commands/safestop.js +62 -0
- package/dist/cli/commands/safestop.js.map +1 -0
- package/dist/cli/commands/schema.d.ts +44 -0
- package/dist/cli/commands/schema.d.ts.map +1 -0
- package/dist/cli/commands/schema.js +177 -0
- package/dist/cli/commands/schema.js.map +1 -0
- package/dist/cli/commands/self-update.d.ts +81 -0
- package/dist/cli/commands/self-update.d.ts.map +1 -0
- package/dist/cli/commands/self-update.js +483 -0
- package/dist/cli/commands/self-update.js.map +1 -0
- package/dist/cli/commands/sentient.d.ts +44 -0
- package/dist/cli/commands/sentient.d.ts.map +1 -0
- package/dist/cli/commands/sentient.js +687 -0
- package/dist/cli/commands/sentient.js.map +1 -0
- package/dist/cli/commands/sequence.d.ts +15 -0
- package/dist/cli/commands/sequence.d.ts.map +1 -0
- package/dist/cli/commands/sequence.js +68 -0
- package/dist/cli/commands/sequence.js.map +1 -0
- package/dist/cli/commands/session.d.ts +32 -0
- package/dist/cli/commands/session.d.ts.map +1 -0
- package/dist/cli/commands/session.js +583 -0
- package/dist/cli/commands/session.js.map +1 -0
- package/dist/cli/commands/show.d.ts +21 -0
- package/dist/cli/commands/show.d.ts.map +1 -0
- package/dist/cli/commands/show.js +37 -0
- package/dist/cli/commands/show.js.map +1 -0
- package/dist/cli/commands/skills.d.ts +31 -0
- package/dist/cli/commands/skills.d.ts.map +1 -0
- package/dist/cli/commands/skills.js +303 -0
- package/dist/cli/commands/skills.js.map +1 -0
- package/dist/cli/commands/snapshot.d.ts +17 -0
- package/dist/cli/commands/snapshot.d.ts.map +1 -0
- package/dist/cli/commands/snapshot.js +95 -0
- package/dist/cli/commands/snapshot.js.map +1 -0
- package/dist/cli/commands/start.d.ts +21 -0
- package/dist/cli/commands/start.d.ts.map +1 -0
- package/dist/cli/commands/start.js +32 -0
- package/dist/cli/commands/start.js.map +1 -0
- package/dist/cli/commands/stats.d.ts +30 -0
- package/dist/cli/commands/stats.d.ts.map +1 -0
- package/dist/cli/commands/stats.js +71 -0
- package/dist/cli/commands/stats.js.map +1 -0
- package/dist/cli/commands/sticky.d.ts +23 -0
- package/dist/cli/commands/sticky.d.ts.map +1 -0
- package/dist/cli/commands/sticky.js +315 -0
- package/dist/cli/commands/sticky.js.map +1 -0
- package/dist/cli/commands/stop.d.ts +15 -0
- package/dist/cli/commands/stop.d.ts.map +1 -0
- package/dist/cli/commands/stop.js +25 -0
- package/dist/cli/commands/stop.js.map +1 -0
- package/dist/cli/commands/sync.d.ts +25 -0
- package/dist/cli/commands/sync.d.ts.map +1 -0
- package/dist/cli/commands/sync.js +125 -0
- package/dist/cli/commands/sync.js.map +1 -0
- package/dist/cli/commands/testing.d.ts +22 -0
- package/dist/cli/commands/testing.d.ts.map +1 -0
- package/dist/cli/commands/testing.js +111 -0
- package/dist/cli/commands/testing.js.map +1 -0
- package/dist/cli/commands/token.d.ts +22 -0
- package/dist/cli/commands/token.d.ts.map +1 -0
- package/dist/cli/commands/token.js +197 -0
- package/dist/cli/commands/token.js.map +1 -0
- package/dist/cli/commands/transcript.d.ts +32 -0
- package/dist/cli/commands/transcript.d.ts.map +1 -0
- package/dist/cli/commands/transcript.js +526 -0
- package/dist/cli/commands/transcript.js.map +1 -0
- package/dist/cli/commands/update.d.ts +164 -0
- package/dist/cli/commands/update.d.ts.map +1 -0
- package/dist/cli/commands/update.js +234 -0
- package/dist/cli/commands/update.js.map +1 -0
- package/dist/cli/commands/upgrade.d.ts +76 -0
- package/dist/cli/commands/upgrade.d.ts.map +1 -0
- package/dist/cli/commands/upgrade.js +154 -0
- package/dist/cli/commands/upgrade.js.map +1 -0
- package/dist/cli/commands/verify.d.ts +83 -0
- package/dist/cli/commands/verify.d.ts.map +1 -0
- package/dist/cli/commands/verify.js +108 -0
- package/dist/cli/commands/verify.js.map +1 -0
- package/dist/cli/commands/web.d.ts +27 -0
- package/dist/cli/commands/web.d.ts.map +1 -0
- package/dist/cli/commands/web.js +414 -0
- package/dist/cli/commands/web.js.map +1 -0
- package/dist/cli/field-context.d.ts +32 -0
- package/dist/cli/field-context.d.ts.map +1 -0
- package/dist/cli/field-context.js +47 -0
- package/dist/cli/field-context.js.map +1 -0
- package/dist/cli/format-context.d.ts +32 -0
- package/dist/cli/format-context.d.ts.map +1 -0
- package/dist/cli/format-context.js +50 -0
- package/dist/cli/format-context.js.map +1 -0
- package/dist/cli/help-renderer.d.ts +40 -0
- package/dist/cli/help-renderer.d.ts.map +1 -0
- package/dist/cli/help-renderer.js +325 -0
- package/dist/cli/help-renderer.js.map +1 -0
- package/dist/cli/index.d.ts +14 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +1712 -9777
- package/dist/cli/index.js.map +4 -4
- package/dist/cli/infer-files-via-gitnexus.d.ts +12 -0
- package/dist/cli/infer-files-via-gitnexus.d.ts.map +1 -0
- package/dist/cli/infer-files-via-gitnexus.js +12 -0
- package/dist/cli/infer-files-via-gitnexus.js.map +1 -0
- package/dist/cli/lib/did-you-mean.d.ts +30 -0
- package/dist/cli/lib/did-you-mean.d.ts.map +1 -0
- package/dist/cli/lib/did-you-mean.js +63 -0
- package/dist/cli/lib/did-you-mean.js.map +1 -0
- package/dist/cli/lib/registry-args.d.ts +36 -0
- package/dist/cli/lib/registry-args.d.ts.map +1 -0
- package/dist/cli/lib/registry-args.js +37 -0
- package/dist/cli/lib/registry-args.js.map +1 -0
- package/dist/cli/lib/subcommand-guard.d.ts +45 -0
- package/dist/cli/lib/subcommand-guard.d.ts.map +1 -0
- package/dist/cli/lib/subcommand-guard.js +55 -0
- package/dist/cli/lib/subcommand-guard.js.map +1 -0
- package/dist/cli/logger-bootstrap.d.ts +6 -0
- package/dist/cli/logger-bootstrap.d.ts.map +1 -0
- package/dist/cli/logger-bootstrap.js +10 -0
- package/dist/cli/logger-bootstrap.js.map +1 -0
- package/dist/cli/middleware/output-format.d.ts +30 -0
- package/dist/cli/middleware/output-format.d.ts.map +1 -0
- package/dist/cli/middleware/output-format.js +35 -0
- package/dist/cli/middleware/output-format.js.map +1 -0
- package/dist/cli/paths.d.ts +85 -0
- package/dist/cli/paths.d.ts.map +1 -0
- package/dist/cli/paths.js +108 -0
- package/dist/cli/paths.js.map +1 -0
- package/dist/cli/progress.d.ts +89 -0
- package/dist/cli/progress.d.ts.map +1 -0
- package/dist/cli/progress.js +185 -0
- package/dist/cli/progress.js.map +1 -0
- package/dist/cli/renderers/colors.d.ts +32 -0
- package/dist/cli/renderers/colors.d.ts.map +1 -0
- package/dist/cli/renderers/colors.js +141 -0
- package/dist/cli/renderers/colors.js.map +1 -0
- package/dist/cli/renderers/error.d.ts +13 -0
- package/dist/cli/renderers/error.d.ts.map +1 -0
- package/dist/cli/renderers/error.js +42 -0
- package/dist/cli/renderers/error.js.map +1 -0
- package/dist/cli/renderers/index.d.ts +90 -0
- package/dist/cli/renderers/index.d.ts.map +1 -0
- package/dist/cli/renderers/index.js +268 -0
- package/dist/cli/renderers/index.js.map +1 -0
- package/dist/cli/renderers/lafs-validator.d.ts +91 -0
- package/dist/cli/renderers/lafs-validator.d.ts.map +1 -0
- package/dist/cli/renderers/lafs-validator.js +176 -0
- package/dist/cli/renderers/lafs-validator.js.map +1 -0
- package/dist/cli/renderers/normalizer.d.ts +21 -0
- package/dist/cli/renderers/normalizer.d.ts.map +1 -0
- package/dist/cli/renderers/normalizer.js +106 -0
- package/dist/cli/renderers/normalizer.js.map +1 -0
- package/dist/cli/renderers/system.d.ts +110 -0
- package/dist/cli/renderers/system.d.ts.map +1 -0
- package/dist/cli/renderers/system.js +662 -0
- package/dist/cli/renderers/system.js.map +1 -0
- package/dist/cli/renderers/tasks.d.ts +28 -0
- package/dist/cli/renderers/tasks.d.ts.map +1 -0
- package/dist/cli/renderers/tasks.js +306 -0
- package/dist/cli/renderers/tasks.js.map +1 -0
- package/dist/cli/tree-context.d.ts +53 -0
- package/dist/cli/tree-context.d.ts.map +1 -0
- package/dist/cli/tree-context.js +43 -0
- package/dist/cli/tree-context.js.map +1 -0
- package/dist/dispatch/adapters/cli.d.ts +67 -0
- package/dist/dispatch/adapters/cli.d.ts.map +1 -0
- package/dist/dispatch/adapters/cli.js +331 -0
- package/dist/dispatch/adapters/cli.js.map +1 -0
- package/dist/dispatch/adapters/typed.d.ts +362 -0
- package/dist/dispatch/adapters/typed.d.ts.map +1 -0
- package/dist/dispatch/adapters/typed.js +278 -0
- package/dist/dispatch/adapters/typed.js.map +1 -0
- package/dist/dispatch/context/session-context.d.ts +108 -0
- package/dist/dispatch/context/session-context.d.ts.map +1 -0
- package/dist/dispatch/context/session-context.js +111 -0
- package/dist/dispatch/context/session-context.js.map +1 -0
- package/dist/dispatch/dispatcher.d.ts +37 -0
- package/dist/dispatch/dispatcher.d.ts.map +1 -0
- package/dist/dispatch/dispatcher.js +172 -0
- package/dist/dispatch/dispatcher.js.map +1 -0
- package/dist/dispatch/domains/_base.d.ts +104 -0
- package/dist/dispatch/domains/_base.d.ts.map +1 -0
- package/dist/dispatch/domains/_base.js +147 -0
- package/dist/dispatch/domains/_base.js.map +1 -0
- package/dist/dispatch/domains/_meta.d.ts +23 -0
- package/dist/dispatch/domains/_meta.d.ts.map +1 -0
- package/dist/dispatch/domains/_meta.js +25 -0
- package/dist/dispatch/domains/_meta.js.map +1 -0
- package/dist/dispatch/domains/_routing.d.ts +8 -0
- package/dist/dispatch/domains/_routing.d.ts.map +1 -0
- package/dist/dispatch/domains/_routing.js +20 -0
- package/dist/dispatch/domains/_routing.js.map +1 -0
- package/dist/dispatch/domains/admin/smoke-provider.d.ts +54 -0
- package/dist/dispatch/domains/admin/smoke-provider.d.ts.map +1 -0
- package/dist/dispatch/domains/admin/smoke-provider.js +309 -0
- package/dist/dispatch/domains/admin/smoke-provider.js.map +1 -0
- package/dist/dispatch/domains/admin.d.ts +51 -0
- package/dist/dispatch/domains/admin.d.ts.map +1 -0
- package/dist/dispatch/domains/admin.js +1163 -0
- package/dist/dispatch/domains/admin.js.map +1 -0
- package/dist/dispatch/domains/check/canon.d.ts +65 -0
- package/dist/dispatch/domains/check/canon.d.ts.map +1 -0
- package/dist/dispatch/domains/check/canon.js +193 -0
- package/dist/dispatch/domains/check/canon.js.map +1 -0
- package/dist/dispatch/domains/check.d.ts +37 -0
- package/dist/dispatch/domains/check.d.ts.map +1 -0
- package/dist/dispatch/domains/check.js +562 -0
- package/dist/dispatch/domains/check.js.map +1 -0
- package/dist/dispatch/domains/conduit.d.ts +61 -0
- package/dist/dispatch/domains/conduit.d.ts.map +1 -0
- package/dist/dispatch/domains/conduit.js +609 -0
- package/dist/dispatch/domains/conduit.js.map +1 -0
- package/dist/dispatch/domains/diagnostics.d.ts +25 -0
- package/dist/dispatch/domains/diagnostics.d.ts.map +1 -0
- package/dist/dispatch/domains/diagnostics.js +82 -0
- package/dist/dispatch/domains/diagnostics.js.map +1 -0
- package/dist/dispatch/domains/docs.d.ts +63 -0
- package/dist/dispatch/domains/docs.d.ts.map +1 -0
- package/dist/dispatch/domains/docs.js +539 -0
- package/dist/dispatch/domains/docs.js.map +1 -0
- package/dist/dispatch/domains/index.d.ts +33 -0
- package/dist/dispatch/domains/index.d.ts.map +1 -0
- package/dist/dispatch/domains/index.js +58 -0
- package/dist/dispatch/domains/index.js.map +1 -0
- package/dist/dispatch/domains/intelligence.d.ts +26 -0
- package/dist/dispatch/domains/intelligence.d.ts.map +1 -0
- package/dist/dispatch/domains/intelligence.js +154 -0
- package/dist/dispatch/domains/intelligence.js.map +1 -0
- package/dist/dispatch/domains/ivtr.d.ts +182 -0
- package/dist/dispatch/domains/ivtr.d.ts.map +1 -0
- package/dist/dispatch/domains/ivtr.js +430 -0
- package/dist/dispatch/domains/ivtr.js.map +1 -0
- package/dist/dispatch/domains/memory.d.ts +22 -0
- package/dist/dispatch/domains/memory.d.ts.map +1 -0
- package/dist/dispatch/domains/memory.js +1281 -0
- package/dist/dispatch/domains/memory.js.map +1 -0
- package/dist/dispatch/domains/nexus.d.ts +78 -0
- package/dist/dispatch/domains/nexus.d.ts.map +1 -0
- package/dist/dispatch/domains/nexus.js +938 -0
- package/dist/dispatch/domains/nexus.js.map +1 -0
- package/dist/dispatch/domains/orchestrate.d.ts +307 -0
- package/dist/dispatch/domains/orchestrate.d.ts.map +1 -0
- package/dist/dispatch/domains/orchestrate.js +986 -0
- package/dist/dispatch/domains/orchestrate.js.map +1 -0
- package/dist/dispatch/domains/pipeline.d.ts +276 -0
- package/dist/dispatch/domains/pipeline.d.ts.map +1 -0
- package/dist/dispatch/domains/pipeline.js +689 -0
- package/dist/dispatch/domains/pipeline.js.map +1 -0
- package/dist/dispatch/domains/playbook.d.ts +131 -0
- package/dist/dispatch/domains/playbook.d.ts.map +1 -0
- package/dist/dispatch/domains/playbook.js +633 -0
- package/dist/dispatch/domains/playbook.js.map +1 -0
- package/dist/dispatch/domains/release.d.ts +97 -0
- package/dist/dispatch/domains/release.d.ts.map +1 -0
- package/dist/dispatch/domains/release.js +177 -0
- package/dist/dispatch/domains/release.js.map +1 -0
- package/dist/dispatch/domains/sentient.d.ts +60 -0
- package/dist/dispatch/domains/sentient.d.ts.map +1 -0
- package/dist/dispatch/domains/sentient.js +270 -0
- package/dist/dispatch/domains/sentient.js.map +1 -0
- package/dist/dispatch/domains/session.d.ts +49 -0
- package/dist/dispatch/domains/session.d.ts.map +1 -0
- package/dist/dispatch/domains/session.js +459 -0
- package/dist/dispatch/domains/session.js.map +1 -0
- package/dist/dispatch/domains/sticky.d.ts +82 -0
- package/dist/dispatch/domains/sticky.d.ts.map +1 -0
- package/dist/dispatch/domains/sticky.js +287 -0
- package/dist/dispatch/domains/sticky.js.map +1 -0
- package/dist/dispatch/domains/tasks.d.ts +58 -0
- package/dist/dispatch/domains/tasks.d.ts.map +1 -0
- package/dist/dispatch/domains/tasks.js +497 -0
- package/dist/dispatch/domains/tasks.js.map +1 -0
- package/dist/dispatch/domains/tools.d.ts +37 -0
- package/dist/dispatch/domains/tools.d.ts.map +1 -0
- package/dist/dispatch/domains/tools.js +481 -0
- package/dist/dispatch/domains/tools.js.map +1 -0
- package/dist/dispatch/engines/_error.d.ts +114 -0
- package/dist/dispatch/engines/_error.d.ts.map +1 -0
- package/dist/dispatch/engines/_error.js +290 -0
- package/dist/dispatch/engines/_error.js.map +1 -0
- package/dist/dispatch/engines/admin-engine.d.ts +386 -0
- package/dist/dispatch/engines/admin-engine.d.ts.map +1 -0
- package/dist/dispatch/engines/admin-engine.js +270 -0
- package/dist/dispatch/engines/admin-engine.js.map +1 -0
- package/dist/dispatch/engines/code-engine.d.ts +14 -0
- package/dist/dispatch/engines/code-engine.d.ts.map +1 -0
- package/dist/dispatch/engines/code-engine.js +14 -0
- package/dist/dispatch/engines/code-engine.js.map +1 -0
- package/dist/dispatch/engines/codebase-map-engine.d.ts +31 -0
- package/dist/dispatch/engines/codebase-map-engine.d.ts.map +1 -0
- package/dist/dispatch/engines/codebase-map-engine.js +43 -0
- package/dist/dispatch/engines/codebase-map-engine.js.map +1 -0
- package/dist/dispatch/engines/config-engine.d.ts +14 -0
- package/dist/dispatch/engines/config-engine.d.ts.map +1 -0
- package/dist/dispatch/engines/config-engine.js +14 -0
- package/dist/dispatch/engines/config-engine.js.map +1 -0
- package/dist/dispatch/engines/diagnostics-engine.d.ts +13 -0
- package/dist/dispatch/engines/diagnostics-engine.d.ts.map +1 -0
- package/dist/dispatch/engines/diagnostics-engine.js +12 -0
- package/dist/dispatch/engines/diagnostics-engine.js.map +1 -0
- package/dist/dispatch/engines/hooks-engine.d.ts +13 -0
- package/dist/dispatch/engines/hooks-engine.d.ts.map +1 -0
- package/dist/dispatch/engines/hooks-engine.js +12 -0
- package/dist/dispatch/engines/hooks-engine.js.map +1 -0
- package/dist/dispatch/engines/init-engine.d.ts +14 -0
- package/dist/dispatch/engines/init-engine.d.ts.map +1 -0
- package/dist/dispatch/engines/init-engine.js +14 -0
- package/dist/dispatch/engines/init-engine.js.map +1 -0
- package/dist/dispatch/engines/lifecycle-engine.d.ts +13 -0
- package/dist/dispatch/engines/lifecycle-engine.d.ts.map +1 -0
- package/dist/dispatch/engines/lifecycle-engine.js +12 -0
- package/dist/dispatch/engines/lifecycle-engine.js.map +1 -0
- package/dist/dispatch/engines/memory-engine.d.ts +10 -0
- package/dist/dispatch/engines/memory-engine.d.ts.map +1 -0
- package/dist/dispatch/engines/memory-engine.js +10 -0
- package/dist/dispatch/engines/memory-engine.js.map +1 -0
- package/dist/dispatch/engines/nexus-engine.d.ts +603 -0
- package/dist/dispatch/engines/nexus-engine.d.ts.map +1 -0
- package/dist/dispatch/engines/nexus-engine.js +1438 -0
- package/dist/dispatch/engines/nexus-engine.js.map +1 -0
- package/dist/dispatch/engines/orchestrate-engine.d.ts +252 -0
- package/dist/dispatch/engines/orchestrate-engine.d.ts.map +1 -0
- package/dist/dispatch/engines/orchestrate-engine.js +1526 -0
- package/dist/dispatch/engines/orchestrate-engine.js.map +1 -0
- package/dist/dispatch/engines/pipeline-engine.d.ts +13 -0
- package/dist/dispatch/engines/pipeline-engine.d.ts.map +1 -0
- package/dist/dispatch/engines/pipeline-engine.js +12 -0
- package/dist/dispatch/engines/pipeline-engine.js.map +1 -0
- package/dist/dispatch/engines/release-engine.d.ts +13 -0
- package/dist/dispatch/engines/release-engine.d.ts.map +1 -0
- package/dist/dispatch/engines/release-engine.js +13 -0
- package/dist/dispatch/engines/release-engine.js.map +1 -0
- package/dist/dispatch/engines/session-engine.d.ts +15 -0
- package/dist/dispatch/engines/session-engine.d.ts.map +1 -0
- package/dist/dispatch/engines/session-engine.js +12 -0
- package/dist/dispatch/engines/session-engine.js.map +1 -0
- package/dist/dispatch/engines/sticky-engine.d.ts +13 -0
- package/dist/dispatch/engines/sticky-engine.d.ts.map +1 -0
- package/dist/dispatch/engines/sticky-engine.js +12 -0
- package/dist/dispatch/engines/sticky-engine.js.map +1 -0
- package/dist/dispatch/engines/system-engine.d.ts +543 -0
- package/dist/dispatch/engines/system-engine.d.ts.map +1 -0
- package/dist/dispatch/engines/system-engine.js +1278 -0
- package/dist/dispatch/engines/system-engine.js.map +1 -0
- package/dist/dispatch/engines/task-engine.d.ts +1161 -0
- package/dist/dispatch/engines/task-engine.d.ts.map +1 -0
- package/dist/dispatch/engines/task-engine.js +1599 -0
- package/dist/dispatch/engines/task-engine.js.map +1 -0
- package/dist/dispatch/engines/template-parser.d.ts +85 -0
- package/dist/dispatch/engines/template-parser.d.ts.map +1 -0
- package/dist/dispatch/engines/template-parser.js +114 -0
- package/dist/dispatch/engines/template-parser.js.map +1 -0
- package/dist/dispatch/engines/tools-engine.d.ts +13 -0
- package/dist/dispatch/engines/tools-engine.d.ts.map +1 -0
- package/dist/dispatch/engines/tools-engine.js +12 -0
- package/dist/dispatch/engines/tools-engine.js.map +1 -0
- package/dist/dispatch/engines/validate-engine.d.ts +13 -0
- package/dist/dispatch/engines/validate-engine.d.ts.map +1 -0
- package/dist/dispatch/engines/validate-engine.js +13 -0
- package/dist/dispatch/engines/validate-engine.js.map +1 -0
- package/dist/dispatch/index.d.ts +20 -0
- package/dist/dispatch/index.d.ts.map +1 -0
- package/dist/dispatch/index.js +19 -0
- package/dist/dispatch/index.js.map +1 -0
- package/dist/dispatch/lib/background-jobs.d.ts +162 -0
- package/dist/dispatch/lib/background-jobs.d.ts.map +1 -0
- package/dist/dispatch/lib/background-jobs.js +360 -0
- package/dist/dispatch/lib/background-jobs.js.map +1 -0
- package/dist/dispatch/lib/budget.d.ts +36 -0
- package/dist/dispatch/lib/budget.d.ts.map +1 -0
- package/dist/dispatch/lib/budget.js +109 -0
- package/dist/dispatch/lib/budget.js.map +1 -0
- package/dist/dispatch/lib/capability-matrix.d.ts +11 -0
- package/dist/dispatch/lib/capability-matrix.d.ts.map +1 -0
- package/dist/dispatch/lib/capability-matrix.js +10 -0
- package/dist/dispatch/lib/capability-matrix.js.map +1 -0
- package/dist/dispatch/lib/config-loader.d.ts +42 -0
- package/dist/dispatch/lib/config-loader.d.ts.map +1 -0
- package/dist/dispatch/lib/config-loader.js +218 -0
- package/dist/dispatch/lib/config-loader.js.map +1 -0
- package/dist/dispatch/lib/config.d.ts +11 -0
- package/dist/dispatch/lib/config.d.ts.map +1 -0
- package/dist/dispatch/lib/config.js +10 -0
- package/dist/dispatch/lib/config.js.map +1 -0
- package/dist/dispatch/lib/defaults.d.ts +115 -0
- package/dist/dispatch/lib/defaults.d.ts.map +1 -0
- package/dist/dispatch/lib/defaults.js +61 -0
- package/dist/dispatch/lib/defaults.js.map +1 -0
- package/dist/dispatch/lib/engine.d.ts +17 -0
- package/dist/dispatch/lib/engine.d.ts.map +1 -0
- package/dist/dispatch/lib/engine.js +36 -0
- package/dist/dispatch/lib/engine.js.map +1 -0
- package/dist/dispatch/lib/exit-codes.d.ts +35 -0
- package/dist/dispatch/lib/exit-codes.d.ts.map +1 -0
- package/dist/dispatch/lib/exit-codes.js +60 -0
- package/dist/dispatch/lib/exit-codes.js.map +1 -0
- package/dist/dispatch/lib/gateway-meta.d.ts +37 -0
- package/dist/dispatch/lib/gateway-meta.d.ts.map +1 -0
- package/dist/dispatch/lib/gateway-meta.js +50 -0
- package/dist/dispatch/lib/gateway-meta.js.map +1 -0
- package/dist/dispatch/lib/job-manager-accessor.d.ts +9 -0
- package/dist/dispatch/lib/job-manager-accessor.d.ts.map +1 -0
- package/dist/dispatch/lib/job-manager-accessor.js +13 -0
- package/dist/dispatch/lib/job-manager-accessor.js.map +1 -0
- package/dist/dispatch/lib/meta.d.ts +26 -0
- package/dist/dispatch/lib/meta.d.ts.map +1 -0
- package/dist/dispatch/lib/meta.js +37 -0
- package/dist/dispatch/lib/meta.js.map +1 -0
- package/dist/dispatch/lib/param-utils.d.ts +11 -0
- package/dist/dispatch/lib/param-utils.d.ts.map +1 -0
- package/dist/dispatch/lib/param-utils.js +10 -0
- package/dist/dispatch/lib/param-utils.js.map +1 -0
- package/dist/dispatch/lib/projections.d.ts +56 -0
- package/dist/dispatch/lib/projections.d.ts.map +1 -0
- package/dist/dispatch/lib/projections.js +65 -0
- package/dist/dispatch/lib/projections.js.map +1 -0
- package/dist/dispatch/lib/proto-envelope.d.ts +56 -0
- package/dist/dispatch/lib/proto-envelope.d.ts.map +1 -0
- package/dist/dispatch/lib/proto-envelope.js +17 -0
- package/dist/dispatch/lib/proto-envelope.js.map +1 -0
- package/dist/dispatch/lib/schema-utils.d.ts +39 -0
- package/dist/dispatch/lib/schema-utils.d.ts.map +1 -0
- package/dist/dispatch/lib/schema-utils.js +88 -0
- package/dist/dispatch/lib/schema-utils.js.map +1 -0
- package/dist/dispatch/lib/security.d.ts +11 -0
- package/dist/dispatch/lib/security.d.ts.map +1 -0
- package/dist/dispatch/lib/security.js +10 -0
- package/dist/dispatch/lib/security.js.map +1 -0
- package/dist/dispatch/middleware/audit.d.ts +23 -0
- package/dist/dispatch/middleware/audit.d.ts.map +1 -0
- package/dist/dispatch/middleware/audit.js +169 -0
- package/dist/dispatch/middleware/audit.js.map +1 -0
- package/dist/dispatch/middleware/field-filter.d.ts +25 -0
- package/dist/dispatch/middleware/field-filter.d.ts.map +1 -0
- package/dist/dispatch/middleware/field-filter.js +70 -0
- package/dist/dispatch/middleware/field-filter.js.map +1 -0
- package/dist/dispatch/middleware/pipeline.d.ts +33 -0
- package/dist/dispatch/middleware/pipeline.d.ts.map +1 -0
- package/dist/dispatch/middleware/pipeline.js +60 -0
- package/dist/dispatch/middleware/pipeline.js.map +1 -0
- package/dist/dispatch/middleware/projection.d.ts +35 -0
- package/dist/dispatch/middleware/projection.d.ts.map +1 -0
- package/dist/dispatch/middleware/projection.js +146 -0
- package/dist/dispatch/middleware/projection.js.map +1 -0
- package/dist/dispatch/middleware/protocol-enforcement.d.ts +30 -0
- package/dist/dispatch/middleware/protocol-enforcement.d.ts.map +1 -0
- package/dist/dispatch/middleware/protocol-enforcement.js +56 -0
- package/dist/dispatch/middleware/protocol-enforcement.js.map +1 -0
- package/dist/dispatch/middleware/rate-limiter.d.ts +72 -0
- package/dist/dispatch/middleware/rate-limiter.d.ts.map +1 -0
- package/dist/dispatch/middleware/rate-limiter.js +127 -0
- package/dist/dispatch/middleware/rate-limiter.js.map +1 -0
- package/dist/dispatch/middleware/sanitizer.d.ts +24 -0
- package/dist/dispatch/middleware/sanitizer.d.ts.map +1 -0
- package/dist/dispatch/middleware/sanitizer.js +56 -0
- package/dist/dispatch/middleware/sanitizer.js.map +1 -0
- package/dist/dispatch/middleware/session-resolver.d.ts +26 -0
- package/dist/dispatch/middleware/session-resolver.d.ts.map +1 -0
- package/dist/dispatch/middleware/session-resolver.js +65 -0
- package/dist/dispatch/middleware/session-resolver.js.map +1 -0
- package/dist/dispatch/middleware/telemetry.d.ts +21 -0
- package/dist/dispatch/middleware/telemetry.d.ts.map +1 -0
- package/dist/dispatch/middleware/telemetry.js +50 -0
- package/dist/dispatch/middleware/telemetry.js.map +1 -0
- package/dist/dispatch/middleware/verification-gates.d.ts +22 -0
- package/dist/dispatch/middleware/verification-gates.d.ts.map +1 -0
- package/dist/dispatch/middleware/verification-gates.js +59 -0
- package/dist/dispatch/middleware/verification-gates.js.map +1 -0
- package/dist/dispatch/registry.d.ts +91 -0
- package/dist/dispatch/registry.d.ts.map +1 -0
- package/dist/dispatch/registry.js +6430 -0
- package/dist/dispatch/registry.js.map +1 -0
- package/dist/dispatch/types.d.ts +150 -0
- package/dist/dispatch/types.d.ts.map +1 -0
- package/dist/dispatch/types.js +38 -0
- package/dist/dispatch/types.js.map +1 -0
- package/dist/migrations/2026-04-25-t991-parent-link-repair.d.ts +88 -0
- package/dist/migrations/2026-04-25-t991-parent-link-repair.d.ts.map +1 -0
- package/dist/migrations/2026-04-25-t991-parent-link-repair.js +76 -0
- package/dist/migrations/2026-04-25-t991-parent-link-repair.js.map +1 -0
- package/package.json +9 -9
|
@@ -0,0 +1,2976 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CLI agent command group — agent credential management (unified registry).
|
|
3
|
+
*
|
|
4
|
+
* Provides:
|
|
5
|
+
* cleo agent register — register a new agent credential
|
|
6
|
+
* cleo agent list — list all registered agent credentials
|
|
7
|
+
* cleo agent get <id> — get a specific agent credential
|
|
8
|
+
* cleo agent remove <id> — remove an agent credential
|
|
9
|
+
* cleo agent rotate-key <id> — rotate an agent's API key
|
|
10
|
+
* cleo agent poll — one-shot message check
|
|
11
|
+
* cleo agent send — send a message to an agent or conversation
|
|
12
|
+
* cleo agent start — start the daemon poller for an agent
|
|
13
|
+
* cleo agent install — install an agent from .cantz archive or directory
|
|
14
|
+
* cleo agent pack — package an agent directory as .cantz archive
|
|
15
|
+
* cleo agent create — scaffold a new agent package with persona.cant and manifest.json
|
|
16
|
+
*
|
|
17
|
+
* **Daemon vs. Pi session — important distinction.** The daemon spawned
|
|
18
|
+
* by `cleo agent start` ONLY polls SignalDock for inbound messages and
|
|
19
|
+
* keeps the cloud status indicator green. It does NOT execute CANT
|
|
20
|
+
* workflow profiles inside the daemon process. CANT workflow execution
|
|
21
|
+
* (sessions, parallel arms, conditionals, approval gates, discretion
|
|
22
|
+
* evaluation, etc.) lives entirely inside the
|
|
23
|
+
* `cant-bridge.ts` Pi extension at
|
|
24
|
+
* `packages/cleo/templates/cleoos-hub/pi-extensions/cant-bridge.ts`,
|
|
25
|
+
* which interprets `.cant` files via shell-out to the `cleo cant`
|
|
26
|
+
* command family. Operators who want profile-driven behaviour should
|
|
27
|
+
* start a Pi session and use `/cant:load <file>` followed by
|
|
28
|
+
* `/cant:run <file> <workflowName>`. The daemon and the Pi session are
|
|
29
|
+
* distinct runtimes with distinct purposes, by design (see ADR-035 §D5
|
|
30
|
+
* "Option Y" addendum).
|
|
31
|
+
*
|
|
32
|
+
* @see docs/specs/SIGNALDOCK-UNIFIED-AGENT-REGISTRY.md Section 3.4
|
|
33
|
+
* @see .cleo/adrs/ADR-035-pi-v2-v3-harness.md §D5 + Addendum
|
|
34
|
+
* @task T178
|
|
35
|
+
*/
|
|
36
|
+
import { checkAgentHealth, detectCrashedAgents, detectStaleAgents, getHealthReport, STALE_THRESHOLD_MS, } from '@cleocode/core/internal';
|
|
37
|
+
import { defineCommand, showUsage } from 'citty';
|
|
38
|
+
import { AGENTS_SUBDIR, CANT_AGENTS_SUBDIR, CLEO_DIR_NAME } from '../paths.js';
|
|
39
|
+
import { cliOutput } from '../renderers/index.js';
|
|
40
|
+
import { computeProfileStatus } from './agent-profile-status.js';
|
|
41
|
+
/** cleo agent register — register a new agent credential in the local registry */
|
|
42
|
+
const registerCommand = defineCommand({
|
|
43
|
+
meta: {
|
|
44
|
+
name: 'register',
|
|
45
|
+
description: 'Register a new agent credential in the local registry',
|
|
46
|
+
},
|
|
47
|
+
args: {
|
|
48
|
+
id: {
|
|
49
|
+
type: 'string',
|
|
50
|
+
description: 'Unique agent identifier',
|
|
51
|
+
required: true,
|
|
52
|
+
},
|
|
53
|
+
name: {
|
|
54
|
+
type: 'string',
|
|
55
|
+
description: 'Human-readable display name',
|
|
56
|
+
required: true,
|
|
57
|
+
},
|
|
58
|
+
'api-key': {
|
|
59
|
+
type: 'string',
|
|
60
|
+
description: 'API key (sk_live_...)',
|
|
61
|
+
required: true,
|
|
62
|
+
},
|
|
63
|
+
'api-url': {
|
|
64
|
+
type: 'string',
|
|
65
|
+
description: 'API base URL',
|
|
66
|
+
default: 'https://api.signaldock.io',
|
|
67
|
+
},
|
|
68
|
+
classification: {
|
|
69
|
+
type: 'string',
|
|
70
|
+
description: 'Agent classification (e.g. code_dev, orchestrator)',
|
|
71
|
+
},
|
|
72
|
+
privacy: {
|
|
73
|
+
type: 'string',
|
|
74
|
+
description: 'Privacy tier: public, discoverable, private',
|
|
75
|
+
default: 'public',
|
|
76
|
+
},
|
|
77
|
+
},
|
|
78
|
+
async run({ args }) {
|
|
79
|
+
try {
|
|
80
|
+
const { AgentRegistryAccessor, getDb } = await import('@cleocode/core/internal');
|
|
81
|
+
await getDb();
|
|
82
|
+
const registry = new AgentRegistryAccessor(process.cwd());
|
|
83
|
+
const agentId = args.id;
|
|
84
|
+
const displayName = args.name;
|
|
85
|
+
const classification = args.classification;
|
|
86
|
+
const credential = await registry.register({
|
|
87
|
+
agentId,
|
|
88
|
+
displayName,
|
|
89
|
+
apiKey: args['api-key'],
|
|
90
|
+
apiBaseUrl: args['api-url'] ?? 'https://api.signaldock.io',
|
|
91
|
+
classification,
|
|
92
|
+
privacyTier: args.privacy ?? 'public',
|
|
93
|
+
capabilities: [],
|
|
94
|
+
skills: [],
|
|
95
|
+
transportType: 'http',
|
|
96
|
+
transportConfig: {},
|
|
97
|
+
isActive: true,
|
|
98
|
+
});
|
|
99
|
+
// Scaffold .cant persona file if it doesn't exist
|
|
100
|
+
const { existsSync, mkdirSync, writeFileSync } = await import('node:fs');
|
|
101
|
+
const { join } = await import('node:path');
|
|
102
|
+
const cantDir = join(CLEO_DIR_NAME, AGENTS_SUBDIR);
|
|
103
|
+
const cantPath = join(cantDir, `${agentId}.cant`);
|
|
104
|
+
let cantScaffolded = false;
|
|
105
|
+
if (!existsSync(cantPath)) {
|
|
106
|
+
mkdirSync(cantDir, { recursive: true });
|
|
107
|
+
const role = classification ?? 'specialist';
|
|
108
|
+
const cantContent = `---
|
|
109
|
+
kind: agent
|
|
110
|
+
version: 2
|
|
111
|
+
---
|
|
112
|
+
|
|
113
|
+
agent ${agentId}:
|
|
114
|
+
house: none
|
|
115
|
+
allegiance: canon
|
|
116
|
+
role: ${role}
|
|
117
|
+
parent: project-orchestrator
|
|
118
|
+
description: "${displayName}"
|
|
119
|
+
|
|
120
|
+
tone:
|
|
121
|
+
|
|
|
122
|
+
TODO: Describe how this agent communicates.
|
|
123
|
+
|
|
124
|
+
prompt:
|
|
125
|
+
|
|
|
126
|
+
TODO: Write the core behavioral instruction.
|
|
127
|
+
|
|
128
|
+
skills: [ct-cleo]
|
|
129
|
+
|
|
130
|
+
permissions:
|
|
131
|
+
tasks: read
|
|
132
|
+
session: read
|
|
133
|
+
memory: read
|
|
134
|
+
|
|
135
|
+
transport:
|
|
136
|
+
primary: local
|
|
137
|
+
fallback: sse
|
|
138
|
+
cloud: http
|
|
139
|
+
apiBaseUrl: https://api.signaldock.io
|
|
140
|
+
|
|
141
|
+
lifecycle:
|
|
142
|
+
start: cleo agent start ${agentId}
|
|
143
|
+
stop: cleo agent stop ${agentId}
|
|
144
|
+
status: cleo agent status ${agentId}
|
|
145
|
+
|
|
146
|
+
context:
|
|
147
|
+
active-tasks
|
|
148
|
+
memory-bridge
|
|
149
|
+
|
|
150
|
+
on SessionStart:
|
|
151
|
+
/checkin @all #online
|
|
152
|
+
|
|
153
|
+
enforcement:
|
|
154
|
+
1: TODO — what does this agent push back on?
|
|
155
|
+
`;
|
|
156
|
+
writeFileSync(cantPath, cantContent, 'utf-8');
|
|
157
|
+
cantScaffolded = true;
|
|
158
|
+
}
|
|
159
|
+
cliOutput({
|
|
160
|
+
success: true,
|
|
161
|
+
data: {
|
|
162
|
+
agentId: credential.agentId,
|
|
163
|
+
displayName: credential.displayName,
|
|
164
|
+
cantFile: cantScaffolded ? cantPath : existsSync(cantPath) ? cantPath : null,
|
|
165
|
+
cantScaffolded,
|
|
166
|
+
},
|
|
167
|
+
}, { command: 'agent register' });
|
|
168
|
+
}
|
|
169
|
+
catch (err) {
|
|
170
|
+
cliOutput({ success: false, error: { code: 'E_REGISTER', message: String(err) } }, { command: 'agent register' });
|
|
171
|
+
process.exitCode = 1;
|
|
172
|
+
}
|
|
173
|
+
},
|
|
174
|
+
});
|
|
175
|
+
/** cleo agent signin <agentId> — sign in as an agent */
|
|
176
|
+
const signinCommand = defineCommand({
|
|
177
|
+
meta: {
|
|
178
|
+
name: 'signin',
|
|
179
|
+
description: 'Sign in as an agent — marks active, caches credentials for session',
|
|
180
|
+
},
|
|
181
|
+
args: {
|
|
182
|
+
agentId: {
|
|
183
|
+
type: 'positional',
|
|
184
|
+
description: 'Agent ID to sign in as',
|
|
185
|
+
required: true,
|
|
186
|
+
},
|
|
187
|
+
'api-url': {
|
|
188
|
+
type: 'string',
|
|
189
|
+
description: 'Override API base URL for cloud status update',
|
|
190
|
+
},
|
|
191
|
+
},
|
|
192
|
+
async run({ args }) {
|
|
193
|
+
try {
|
|
194
|
+
const { AgentRegistryAccessor, getDb } = await import('@cleocode/core/internal');
|
|
195
|
+
await getDb();
|
|
196
|
+
const registry = new AgentRegistryAccessor(process.cwd());
|
|
197
|
+
// Look up the credential
|
|
198
|
+
const credential = await registry.get(args.agentId);
|
|
199
|
+
if (!credential) {
|
|
200
|
+
cliOutput({
|
|
201
|
+
success: false,
|
|
202
|
+
error: {
|
|
203
|
+
code: 'E_NOT_FOUND',
|
|
204
|
+
message: `Agent '${args.agentId}' not registered. Run: cleo agent register --id ${args.agentId} --name "..." --api-key sk_live_...`,
|
|
205
|
+
},
|
|
206
|
+
}, { command: 'agent signin' });
|
|
207
|
+
process.exitCode = 1;
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
210
|
+
// Mark as active + update lastUsedAt
|
|
211
|
+
await registry.update(args.agentId, { isActive: true });
|
|
212
|
+
await registry.markUsed(args.agentId);
|
|
213
|
+
// Attempt to set online status on cloud (best-effort, don't fail if offline)
|
|
214
|
+
const apiUrl = args['api-url'] ?? credential.apiBaseUrl;
|
|
215
|
+
try {
|
|
216
|
+
await fetch(`${apiUrl}/agents/${args.agentId}/status`, {
|
|
217
|
+
method: 'PUT',
|
|
218
|
+
headers: {
|
|
219
|
+
'Content-Type': 'application/json',
|
|
220
|
+
Authorization: `Bearer ${credential.apiKey}`,
|
|
221
|
+
'X-Agent-Id': args.agentId,
|
|
222
|
+
},
|
|
223
|
+
body: JSON.stringify({ status: 'online' }),
|
|
224
|
+
signal: AbortSignal.timeout(5000),
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
catch {
|
|
228
|
+
// Offline is fine — LocalTransport works without cloud
|
|
229
|
+
}
|
|
230
|
+
cliOutput({
|
|
231
|
+
success: true,
|
|
232
|
+
data: {
|
|
233
|
+
agentId: credential.agentId,
|
|
234
|
+
displayName: credential.displayName,
|
|
235
|
+
apiBaseUrl: apiUrl,
|
|
236
|
+
status: 'online',
|
|
237
|
+
transport: 'local',
|
|
238
|
+
},
|
|
239
|
+
}, { command: 'agent signin' });
|
|
240
|
+
}
|
|
241
|
+
catch (err) {
|
|
242
|
+
cliOutput({ success: false, error: { code: 'E_SIGNIN', message: String(err) } }, { command: 'agent signin' });
|
|
243
|
+
process.exitCode = 1;
|
|
244
|
+
}
|
|
245
|
+
},
|
|
246
|
+
});
|
|
247
|
+
/**
|
|
248
|
+
* cleo agent start <agentId> — start the SignalDock-poller daemon.
|
|
249
|
+
*
|
|
250
|
+
* Profile handling:
|
|
251
|
+
* The `--cant <file>` option (or the default
|
|
252
|
+
* `.cleo/agents/<agentId>.cant` lookup) is read for two reasons:
|
|
253
|
+
*
|
|
254
|
+
* 1. **Fail-fast validation**: if the file exists and the optional
|
|
255
|
+
* `@cleocode/cant` validator is available in this build, the
|
|
256
|
+
* file is parsed so malformed profiles surface as a status
|
|
257
|
+
* string rather than blowing up later.
|
|
258
|
+
* 2. **Status surface**: the resulting status (`validated`,
|
|
259
|
+
* `invalid (N errors)`, `loaded (unvalidated)`, or `none`) is
|
|
260
|
+
* reported in the LAFS envelope so operators know the daemon
|
|
261
|
+
* saw the file they expected.
|
|
262
|
+
*
|
|
263
|
+
* The profile string is then DROPPED. The daemon does NOT execute
|
|
264
|
+
* workflow profiles. Profile-driven behaviour runs inside Pi sessions
|
|
265
|
+
* through the `cant-bridge.ts` Pi extension.
|
|
266
|
+
* See ADR-035 §D5 (Option Y addendum) for the architectural rationale.
|
|
267
|
+
*/
|
|
268
|
+
const startCommand = defineCommand({
|
|
269
|
+
meta: {
|
|
270
|
+
name: 'start',
|
|
271
|
+
description: 'Start an agent daemon — polls SignalDock for messages. Profile is validated for fail-fast feedback only; CANT execution lives in Pi via cant-bridge.ts.',
|
|
272
|
+
},
|
|
273
|
+
args: {
|
|
274
|
+
agentId: {
|
|
275
|
+
type: 'positional',
|
|
276
|
+
description: 'Agent ID to start',
|
|
277
|
+
required: true,
|
|
278
|
+
},
|
|
279
|
+
cant: {
|
|
280
|
+
type: 'string',
|
|
281
|
+
description: 'Path to .cant persona file (validated only, NOT executed by the daemon)',
|
|
282
|
+
},
|
|
283
|
+
'poll-interval': {
|
|
284
|
+
type: 'string',
|
|
285
|
+
description: 'Poll interval in milliseconds',
|
|
286
|
+
default: '5000',
|
|
287
|
+
},
|
|
288
|
+
heartbeat: {
|
|
289
|
+
type: 'boolean',
|
|
290
|
+
description: 'Enable heartbeat service (default: true)',
|
|
291
|
+
default: true,
|
|
292
|
+
},
|
|
293
|
+
},
|
|
294
|
+
async run({ args }) {
|
|
295
|
+
try {
|
|
296
|
+
const { AgentRegistryAccessor, getDb } = await import('@cleocode/core/internal');
|
|
297
|
+
const { createRuntime } = await import('@cleocode/runtime');
|
|
298
|
+
const { existsSync, readFileSync } = await import('node:fs');
|
|
299
|
+
const { join } = await import('node:path');
|
|
300
|
+
await getDb();
|
|
301
|
+
const registry = new AgentRegistryAccessor(process.cwd());
|
|
302
|
+
// 1. Look up credential
|
|
303
|
+
const credential = await registry.get(args.agentId);
|
|
304
|
+
if (!credential) {
|
|
305
|
+
cliOutput({
|
|
306
|
+
success: false,
|
|
307
|
+
error: {
|
|
308
|
+
code: 'E_NOT_FOUND',
|
|
309
|
+
message: `Agent '${args.agentId}' not registered. Run: cleo agent register --id ${args.agentId} --name "..." --api-key sk_live_...`,
|
|
310
|
+
},
|
|
311
|
+
}, { command: 'agent start' });
|
|
312
|
+
process.exitCode = 1;
|
|
313
|
+
return;
|
|
314
|
+
}
|
|
315
|
+
// 2. Read and (best-effort) validate the .cant profile.
|
|
316
|
+
// This is FAIL-FAST GUARDING ONLY. The profile string is
|
|
317
|
+
// used to compute a status field below, then dropped. The
|
|
318
|
+
// daemon does not interpret the workflow body.
|
|
319
|
+
let profile = null;
|
|
320
|
+
let cantValidation = null;
|
|
321
|
+
const cantPath = args.cant ?? join(CLEO_DIR_NAME, AGENTS_SUBDIR, `${args.agentId}.cant`);
|
|
322
|
+
if (existsSync(cantPath)) {
|
|
323
|
+
profile = readFileSync(cantPath, 'utf-8');
|
|
324
|
+
try {
|
|
325
|
+
const cantModule = await import('@cleocode/cant');
|
|
326
|
+
// validate() may not be available in all builds of @cleocode/cant
|
|
327
|
+
const validate = 'validate' in cantModule
|
|
328
|
+
? cantModule.validate
|
|
329
|
+
: null;
|
|
330
|
+
if (validate) {
|
|
331
|
+
const result = validate(profile);
|
|
332
|
+
cantValidation = {
|
|
333
|
+
valid: result.valid,
|
|
334
|
+
errors: result.diagnostics?.map((d) => d.message) ?? [],
|
|
335
|
+
};
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
catch {
|
|
339
|
+
// cant-napi not available — profile loaded but unvalidated
|
|
340
|
+
cantValidation = null;
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
// 3. Mark active + update lastUsedAt
|
|
344
|
+
await registry.update(args.agentId, { isActive: true });
|
|
345
|
+
await registry.markUsed(args.agentId);
|
|
346
|
+
// 4. Set cloud status (best-effort)
|
|
347
|
+
try {
|
|
348
|
+
await fetch(`${credential.apiBaseUrl}/agents/${args.agentId}/status`, {
|
|
349
|
+
method: 'PUT',
|
|
350
|
+
headers: {
|
|
351
|
+
'Content-Type': 'application/json',
|
|
352
|
+
Authorization: `Bearer ${credential.apiKey}`,
|
|
353
|
+
'X-Agent-Id': args.agentId,
|
|
354
|
+
},
|
|
355
|
+
body: JSON.stringify({ status: 'online' }),
|
|
356
|
+
signal: AbortSignal.timeout(5000),
|
|
357
|
+
});
|
|
358
|
+
}
|
|
359
|
+
catch {
|
|
360
|
+
// Offline is fine — LocalTransport works without cloud
|
|
361
|
+
}
|
|
362
|
+
// 5. Start runtime services (transport auto-resolved: Local > SSE > HTTP).
|
|
363
|
+
// Note: createRuntime() does NOT receive the profile. The
|
|
364
|
+
// daemon's job is purely SignalDock polling + cloud status.
|
|
365
|
+
const pollInterval = Number.parseInt(args['poll-interval'], 10);
|
|
366
|
+
const runtime = await createRuntime(registry, {
|
|
367
|
+
agentId: args.agentId,
|
|
368
|
+
pollIntervalMs: pollInterval,
|
|
369
|
+
heartbeatIntervalMs: args.heartbeat === false ? 0 : 30000,
|
|
370
|
+
groupConversationIds: [],
|
|
371
|
+
});
|
|
372
|
+
runtime.poller.start();
|
|
373
|
+
cliOutput({
|
|
374
|
+
success: true,
|
|
375
|
+
data: {
|
|
376
|
+
agentId: args.agentId,
|
|
377
|
+
displayName: credential.displayName,
|
|
378
|
+
status: 'online',
|
|
379
|
+
transport: runtime.transport.name,
|
|
380
|
+
// Surfaced for operator visibility only — see
|
|
381
|
+
// computeProfileStatus tsdoc for the four possible values.
|
|
382
|
+
profile: computeProfileStatus(profile, cantValidation),
|
|
383
|
+
services: {
|
|
384
|
+
poller: 'running',
|
|
385
|
+
heartbeat: runtime.heartbeat ? 'running' : 'disabled',
|
|
386
|
+
keyRotation: runtime.keyRotation ? 'running' : 'disabled',
|
|
387
|
+
},
|
|
388
|
+
},
|
|
389
|
+
}, { command: 'agent start' });
|
|
390
|
+
// 6. Keep process alive until shutdown signal (cross-platform)
|
|
391
|
+
const shutdown = () => {
|
|
392
|
+
runtime.stop();
|
|
393
|
+
void registry.update(args.agentId, { isActive: false }).catch(() => { });
|
|
394
|
+
process.exit(0);
|
|
395
|
+
};
|
|
396
|
+
process.on('SIGINT', shutdown);
|
|
397
|
+
process.on('SIGTERM', shutdown);
|
|
398
|
+
// Windows: listen for 'message' from parent process managers (PM2, etc.)
|
|
399
|
+
if (process.platform === 'win32') {
|
|
400
|
+
process.on('message', (msg) => {
|
|
401
|
+
if (msg === 'shutdown')
|
|
402
|
+
shutdown();
|
|
403
|
+
});
|
|
404
|
+
}
|
|
405
|
+
// Keep alive
|
|
406
|
+
await new Promise(() => { });
|
|
407
|
+
}
|
|
408
|
+
catch (err) {
|
|
409
|
+
cliOutput({ success: false, error: { code: 'E_START', message: String(err) } }, { command: 'agent start' });
|
|
410
|
+
process.exitCode = 1;
|
|
411
|
+
}
|
|
412
|
+
},
|
|
413
|
+
});
|
|
414
|
+
/** cleo agent stop <agentId> — stop an agent and mark it offline */
|
|
415
|
+
const stopCommand = defineCommand({
|
|
416
|
+
meta: {
|
|
417
|
+
name: 'stop',
|
|
418
|
+
description: 'Stop an agent — mark offline and deactivate',
|
|
419
|
+
},
|
|
420
|
+
args: {
|
|
421
|
+
agentId: {
|
|
422
|
+
type: 'positional',
|
|
423
|
+
description: 'Agent ID to stop',
|
|
424
|
+
required: true,
|
|
425
|
+
},
|
|
426
|
+
},
|
|
427
|
+
async run({ args }) {
|
|
428
|
+
try {
|
|
429
|
+
const { AgentRegistryAccessor, getDb } = await import('@cleocode/core/internal');
|
|
430
|
+
await getDb();
|
|
431
|
+
const registry = new AgentRegistryAccessor(process.cwd());
|
|
432
|
+
const credential = await registry.get(args.agentId);
|
|
433
|
+
if (!credential) {
|
|
434
|
+
cliOutput({
|
|
435
|
+
success: false,
|
|
436
|
+
error: { code: 'E_NOT_FOUND', message: `Agent '${args.agentId}' not registered.` },
|
|
437
|
+
}, { command: 'agent stop' });
|
|
438
|
+
process.exitCode = 1;
|
|
439
|
+
return;
|
|
440
|
+
}
|
|
441
|
+
// Mark inactive
|
|
442
|
+
await registry.update(args.agentId, { isActive: false });
|
|
443
|
+
// Set cloud status offline (best-effort)
|
|
444
|
+
try {
|
|
445
|
+
await fetch(`${credential.apiBaseUrl}/agents/${args.agentId}/status`, {
|
|
446
|
+
method: 'PUT',
|
|
447
|
+
headers: {
|
|
448
|
+
'Content-Type': 'application/json',
|
|
449
|
+
Authorization: `Bearer ${credential.apiKey}`,
|
|
450
|
+
'X-Agent-Id': args.agentId,
|
|
451
|
+
},
|
|
452
|
+
body: JSON.stringify({ status: 'offline' }),
|
|
453
|
+
signal: AbortSignal.timeout(5000),
|
|
454
|
+
});
|
|
455
|
+
}
|
|
456
|
+
catch {
|
|
457
|
+
// Offline is fine
|
|
458
|
+
}
|
|
459
|
+
cliOutput({ success: true, data: { agentId: args.agentId, status: 'offline' } }, { command: 'agent stop' });
|
|
460
|
+
}
|
|
461
|
+
catch (err) {
|
|
462
|
+
cliOutput({ success: false, error: { code: 'E_STOP', message: String(err) } }, { command: 'agent stop' });
|
|
463
|
+
process.exitCode = 1;
|
|
464
|
+
}
|
|
465
|
+
},
|
|
466
|
+
});
|
|
467
|
+
/** cleo agent status [agentId] — show agent status for all or a specific agent */
|
|
468
|
+
const statusCommand = defineCommand({
|
|
469
|
+
meta: {
|
|
470
|
+
name: 'status',
|
|
471
|
+
description: 'Show agent status — all agents or specific agent',
|
|
472
|
+
},
|
|
473
|
+
args: {
|
|
474
|
+
agentId: {
|
|
475
|
+
type: 'positional',
|
|
476
|
+
description: 'Agent ID to check (optional — omit for all)',
|
|
477
|
+
required: false,
|
|
478
|
+
},
|
|
479
|
+
},
|
|
480
|
+
async run({ args }) {
|
|
481
|
+
try {
|
|
482
|
+
const { AgentRegistryAccessor, getDb } = await import('@cleocode/core/internal');
|
|
483
|
+
await getDb();
|
|
484
|
+
const registry = new AgentRegistryAccessor(process.cwd());
|
|
485
|
+
if (args.agentId) {
|
|
486
|
+
const credential = await registry.get(args.agentId);
|
|
487
|
+
if (!credential) {
|
|
488
|
+
cliOutput({
|
|
489
|
+
success: false,
|
|
490
|
+
error: { code: 'E_NOT_FOUND', message: `Agent '${args.agentId}' not registered.` },
|
|
491
|
+
}, { command: 'agent status' });
|
|
492
|
+
process.exitCode = 1;
|
|
493
|
+
return;
|
|
494
|
+
}
|
|
495
|
+
cliOutput({
|
|
496
|
+
success: true,
|
|
497
|
+
data: {
|
|
498
|
+
agentId: credential.agentId,
|
|
499
|
+
displayName: credential.displayName,
|
|
500
|
+
active: credential.isActive,
|
|
501
|
+
lastUsedAt: credential.lastUsedAt,
|
|
502
|
+
transport: credential.transportType,
|
|
503
|
+
},
|
|
504
|
+
}, { command: 'agent status' });
|
|
505
|
+
}
|
|
506
|
+
else {
|
|
507
|
+
const agents = await registry.list();
|
|
508
|
+
cliOutput({
|
|
509
|
+
success: true,
|
|
510
|
+
data: {
|
|
511
|
+
agents: agents.map((a) => ({
|
|
512
|
+
agentId: a.agentId,
|
|
513
|
+
displayName: a.displayName,
|
|
514
|
+
active: a.isActive,
|
|
515
|
+
lastUsedAt: a.lastUsedAt,
|
|
516
|
+
})),
|
|
517
|
+
total: agents.length,
|
|
518
|
+
},
|
|
519
|
+
}, { command: 'agent status' });
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
catch (err) {
|
|
523
|
+
cliOutput({ success: false, error: { code: 'E_STATUS', message: String(err) } }, { command: 'agent status' });
|
|
524
|
+
process.exitCode = 1;
|
|
525
|
+
}
|
|
526
|
+
},
|
|
527
|
+
});
|
|
528
|
+
/** cleo agent assign <agentId> <taskId> — assign a task to an agent via messaging */
|
|
529
|
+
const assignCommand = defineCommand({
|
|
530
|
+
meta: {
|
|
531
|
+
name: 'assign',
|
|
532
|
+
description: 'Assign a task to an agent via messaging',
|
|
533
|
+
},
|
|
534
|
+
args: {
|
|
535
|
+
agentId: {
|
|
536
|
+
type: 'positional',
|
|
537
|
+
description: 'Target agent ID',
|
|
538
|
+
required: true,
|
|
539
|
+
},
|
|
540
|
+
taskId: {
|
|
541
|
+
type: 'positional',
|
|
542
|
+
description: 'Task ID to assign',
|
|
543
|
+
required: true,
|
|
544
|
+
},
|
|
545
|
+
},
|
|
546
|
+
async run({ args }) {
|
|
547
|
+
try {
|
|
548
|
+
const { AgentRegistryAccessor, getDb, createConduit } = await import('@cleocode/core/internal');
|
|
549
|
+
await getDb();
|
|
550
|
+
const registry = new AgentRegistryAccessor(process.cwd());
|
|
551
|
+
const active = await registry.getActive();
|
|
552
|
+
if (!active) {
|
|
553
|
+
cliOutput({
|
|
554
|
+
success: false,
|
|
555
|
+
error: {
|
|
556
|
+
code: 'E_NO_ACTIVE',
|
|
557
|
+
message: 'No active agent. Run: cleo agent signin <id>',
|
|
558
|
+
},
|
|
559
|
+
}, { command: 'agent assign' });
|
|
560
|
+
process.exitCode = 1;
|
|
561
|
+
return;
|
|
562
|
+
}
|
|
563
|
+
const conduit = await createConduit(registry);
|
|
564
|
+
await conduit.send(args.agentId, `/action @${args.agentId} #task-assignment\n\nAssigned task ${args.taskId}. Run: cleo show ${args.taskId} && cleo start ${args.taskId}`);
|
|
565
|
+
await conduit.disconnect();
|
|
566
|
+
cliOutput({
|
|
567
|
+
success: true,
|
|
568
|
+
data: { agentId: args.agentId, taskId: args.taskId, assignedBy: active.agentId },
|
|
569
|
+
}, { command: 'agent assign' });
|
|
570
|
+
}
|
|
571
|
+
catch (err) {
|
|
572
|
+
cliOutput({ success: false, error: { code: 'E_ASSIGN', message: String(err) } }, { command: 'agent assign' });
|
|
573
|
+
process.exitCode = 1;
|
|
574
|
+
}
|
|
575
|
+
},
|
|
576
|
+
});
|
|
577
|
+
/** cleo agent wake <agentId> — wake an idle agent by sending a prod message */
|
|
578
|
+
const wakeCommand = defineCommand({
|
|
579
|
+
meta: {
|
|
580
|
+
name: 'wake',
|
|
581
|
+
description: 'Wake an idle agent — send a prod message',
|
|
582
|
+
},
|
|
583
|
+
args: {
|
|
584
|
+
agentId: {
|
|
585
|
+
type: 'positional',
|
|
586
|
+
description: 'Agent ID to wake',
|
|
587
|
+
required: true,
|
|
588
|
+
},
|
|
589
|
+
},
|
|
590
|
+
async run({ args }) {
|
|
591
|
+
try {
|
|
592
|
+
const { AgentRegistryAccessor, getDb, createConduit } = await import('@cleocode/core/internal');
|
|
593
|
+
await getDb();
|
|
594
|
+
const registry = new AgentRegistryAccessor(process.cwd());
|
|
595
|
+
const active = await registry.getActive();
|
|
596
|
+
if (!active) {
|
|
597
|
+
cliOutput({
|
|
598
|
+
success: false,
|
|
599
|
+
error: {
|
|
600
|
+
code: 'E_NO_ACTIVE',
|
|
601
|
+
message: 'No active agent. Run: cleo agent signin <id>',
|
|
602
|
+
},
|
|
603
|
+
}, { command: 'agent wake' });
|
|
604
|
+
process.exitCode = 1;
|
|
605
|
+
return;
|
|
606
|
+
}
|
|
607
|
+
const conduit = await createConduit(registry);
|
|
608
|
+
await conduit.send(args.agentId, `/action @${args.agentId} #wake #prod\n\nYou are idle. Check your queue: cleo current || cleo next. Report status immediately.`);
|
|
609
|
+
await conduit.disconnect();
|
|
610
|
+
cliOutput({ success: true, data: { agentId: args.agentId, prodBy: active.agentId } }, { command: 'agent wake' });
|
|
611
|
+
}
|
|
612
|
+
catch (err) {
|
|
613
|
+
cliOutput({ success: false, error: { code: 'E_WAKE', message: String(err) } }, { command: 'agent wake' });
|
|
614
|
+
process.exitCode = 1;
|
|
615
|
+
}
|
|
616
|
+
},
|
|
617
|
+
});
|
|
618
|
+
/** cleo agent spawn — spawn a new ephemeral agent for a specific task */
|
|
619
|
+
const spawnCommand = defineCommand({
|
|
620
|
+
meta: {
|
|
621
|
+
name: 'spawn',
|
|
622
|
+
description: 'Spawn a new ephemeral agent for a specific task',
|
|
623
|
+
},
|
|
624
|
+
args: {
|
|
625
|
+
role: {
|
|
626
|
+
type: 'string',
|
|
627
|
+
description: 'Agent role (e.g. code_dev, research, security)',
|
|
628
|
+
required: true,
|
|
629
|
+
},
|
|
630
|
+
task: {
|
|
631
|
+
type: 'string',
|
|
632
|
+
description: 'Task to assign to the spawned agent',
|
|
633
|
+
},
|
|
634
|
+
model: {
|
|
635
|
+
type: 'string',
|
|
636
|
+
description: 'Model to use (e.g. opus, sonnet)',
|
|
637
|
+
},
|
|
638
|
+
name: {
|
|
639
|
+
type: 'string',
|
|
640
|
+
description: 'Display name for the agent',
|
|
641
|
+
},
|
|
642
|
+
},
|
|
643
|
+
async run({ args }) {
|
|
644
|
+
try {
|
|
645
|
+
const { AgentRegistryAccessor, getDb } = await import('@cleocode/core/internal');
|
|
646
|
+
await getDb();
|
|
647
|
+
const registry = new AgentRegistryAccessor(process.cwd());
|
|
648
|
+
const role = args.role;
|
|
649
|
+
const taskId = args.task;
|
|
650
|
+
const model = args.model ?? 'sonnet';
|
|
651
|
+
const displayName = args.name ?? `ephemeral-${role}-${Date.now().toString(36)}`;
|
|
652
|
+
const agentId = displayName;
|
|
653
|
+
// Register the ephemeral agent locally (no cloud registration)
|
|
654
|
+
await registry.register({
|
|
655
|
+
agentId,
|
|
656
|
+
displayName,
|
|
657
|
+
apiKey: 'ephemeral-local-only',
|
|
658
|
+
apiBaseUrl: 'local',
|
|
659
|
+
classification: role,
|
|
660
|
+
privacyTier: 'private',
|
|
661
|
+
capabilities: ['chat', 'tools'],
|
|
662
|
+
skills: [role],
|
|
663
|
+
transportType: 'http',
|
|
664
|
+
transportConfig: {},
|
|
665
|
+
isActive: true,
|
|
666
|
+
});
|
|
667
|
+
cliOutput({
|
|
668
|
+
success: true,
|
|
669
|
+
data: {
|
|
670
|
+
agentId,
|
|
671
|
+
displayName,
|
|
672
|
+
role,
|
|
673
|
+
model,
|
|
674
|
+
taskId: taskId ?? null,
|
|
675
|
+
transport: 'local',
|
|
676
|
+
lifecycle: 'ephemeral',
|
|
677
|
+
},
|
|
678
|
+
}, { command: 'agent spawn' });
|
|
679
|
+
}
|
|
680
|
+
catch (err) {
|
|
681
|
+
cliOutput({ success: false, error: { code: 'E_SPAWN', message: String(err) } }, { command: 'agent spawn' });
|
|
682
|
+
process.exitCode = 1;
|
|
683
|
+
}
|
|
684
|
+
},
|
|
685
|
+
});
|
|
686
|
+
/** cleo agent reassign <taskId> <agentId> — reassign a task to another agent via conduit */
|
|
687
|
+
const reassignCommand = defineCommand({
|
|
688
|
+
meta: {
|
|
689
|
+
name: 'reassign',
|
|
690
|
+
description: 'Reassign a task to another agent via conduit message',
|
|
691
|
+
},
|
|
692
|
+
args: {
|
|
693
|
+
taskId: {
|
|
694
|
+
type: 'positional',
|
|
695
|
+
description: 'Task ID to reassign',
|
|
696
|
+
required: true,
|
|
697
|
+
},
|
|
698
|
+
agentId: {
|
|
699
|
+
type: 'positional',
|
|
700
|
+
description: 'Target agent ID',
|
|
701
|
+
required: true,
|
|
702
|
+
},
|
|
703
|
+
},
|
|
704
|
+
async run({ args }) {
|
|
705
|
+
try {
|
|
706
|
+
const { AgentRegistryAccessor, getDb, createConduit } = await import('@cleocode/core/internal');
|
|
707
|
+
await getDb();
|
|
708
|
+
const registry = new AgentRegistryAccessor(process.cwd());
|
|
709
|
+
const active = await registry.getActive();
|
|
710
|
+
if (!active) {
|
|
711
|
+
cliOutput({ success: false, error: { code: 'E_NO_ACTIVE', message: 'No active agent.' } }, { command: 'agent reassign' });
|
|
712
|
+
process.exitCode = 1;
|
|
713
|
+
return;
|
|
714
|
+
}
|
|
715
|
+
const conduit = await createConduit(registry);
|
|
716
|
+
await conduit.send(args.agentId, `/action @${args.agentId} #task-reassignment\n\nTask ${args.taskId} reassigned to you by ${active.agentId}. Run: cleo show ${args.taskId} && cleo start ${args.taskId}`);
|
|
717
|
+
await conduit.disconnect();
|
|
718
|
+
cliOutput({
|
|
719
|
+
success: true,
|
|
720
|
+
data: { taskId: args.taskId, newOwner: args.agentId, reassignedBy: active.agentId },
|
|
721
|
+
}, { command: 'agent reassign' });
|
|
722
|
+
}
|
|
723
|
+
catch (err) {
|
|
724
|
+
cliOutput({ success: false, error: { code: 'E_REASSIGN', message: String(err) } }, { command: 'agent reassign' });
|
|
725
|
+
process.exitCode = 1;
|
|
726
|
+
}
|
|
727
|
+
},
|
|
728
|
+
});
|
|
729
|
+
/** cleo agent stop-all — stop all active agents and mark them offline */
|
|
730
|
+
const stopAllCommand = defineCommand({
|
|
731
|
+
meta: {
|
|
732
|
+
name: 'stop-all',
|
|
733
|
+
description: 'Stop all active agents — mark all offline',
|
|
734
|
+
},
|
|
735
|
+
async run() {
|
|
736
|
+
try {
|
|
737
|
+
const { AgentRegistryAccessor, getDb } = await import('@cleocode/core/internal');
|
|
738
|
+
await getDb();
|
|
739
|
+
const registry = new AgentRegistryAccessor(process.cwd());
|
|
740
|
+
const agents = await registry.list({ active: true });
|
|
741
|
+
let stopped = 0;
|
|
742
|
+
for (const a of agents) {
|
|
743
|
+
await registry.update(a.agentId, { isActive: false });
|
|
744
|
+
try {
|
|
745
|
+
await fetch(`${a.apiBaseUrl}/agents/${a.agentId}/status`, {
|
|
746
|
+
method: 'PUT',
|
|
747
|
+
headers: {
|
|
748
|
+
'Content-Type': 'application/json',
|
|
749
|
+
Authorization: `Bearer ${a.apiKey}`,
|
|
750
|
+
'X-Agent-Id': a.agentId,
|
|
751
|
+
},
|
|
752
|
+
body: JSON.stringify({ status: 'offline' }),
|
|
753
|
+
signal: AbortSignal.timeout(3000),
|
|
754
|
+
});
|
|
755
|
+
}
|
|
756
|
+
catch {
|
|
757
|
+
/* best-effort */
|
|
758
|
+
}
|
|
759
|
+
stopped++;
|
|
760
|
+
}
|
|
761
|
+
cliOutput({ success: true, data: { stopped, total: agents.length } }, { command: 'agent stop-all' });
|
|
762
|
+
}
|
|
763
|
+
catch (err) {
|
|
764
|
+
cliOutput({ success: false, error: { code: 'E_STOP_ALL', message: String(err) } }, { command: 'agent stop-all' });
|
|
765
|
+
process.exitCode = 1;
|
|
766
|
+
}
|
|
767
|
+
},
|
|
768
|
+
});
|
|
769
|
+
/**
|
|
770
|
+
* cleo agent work <agentId> — enter autonomous work loop.
|
|
771
|
+
*
|
|
772
|
+
* Phase 3: --execute enables the Conductor Loop, which autonomously
|
|
773
|
+
* dispatches ready tasks via orchestrate.spawn.execute.
|
|
774
|
+
*/
|
|
775
|
+
const workCommand = defineCommand({
|
|
776
|
+
meta: {
|
|
777
|
+
name: 'work',
|
|
778
|
+
description: 'Enter autonomous work loop — poll tasks, report, optionally execute. --execute enables the Conductor Loop.',
|
|
779
|
+
},
|
|
780
|
+
args: {
|
|
781
|
+
agentId: {
|
|
782
|
+
type: 'positional',
|
|
783
|
+
description: 'Agent ID to run the work loop as',
|
|
784
|
+
required: true,
|
|
785
|
+
},
|
|
786
|
+
'poll-interval': {
|
|
787
|
+
type: 'string',
|
|
788
|
+
description: 'Task check interval in milliseconds',
|
|
789
|
+
default: '30000',
|
|
790
|
+
},
|
|
791
|
+
execute: {
|
|
792
|
+
type: 'boolean',
|
|
793
|
+
description: 'Autonomously execute ready tasks via orchestrate.spawn.execute (Phase 3 Conductor Loop)',
|
|
794
|
+
},
|
|
795
|
+
adapter: {
|
|
796
|
+
type: 'string',
|
|
797
|
+
description: 'Adapter id to route spawns through (default: auto-detect from capabilities)',
|
|
798
|
+
},
|
|
799
|
+
epic: {
|
|
800
|
+
type: 'string',
|
|
801
|
+
description: 'Restrict autonomous execution to a specific epic (default: any ready task)',
|
|
802
|
+
},
|
|
803
|
+
},
|
|
804
|
+
async run({ args }) {
|
|
805
|
+
try {
|
|
806
|
+
const { AgentRegistryAccessor, getDb } = await import('@cleocode/core/internal');
|
|
807
|
+
const { createRuntime } = await import('@cleocode/runtime');
|
|
808
|
+
const { existsSync } = await import('node:fs');
|
|
809
|
+
const { join } = await import('node:path');
|
|
810
|
+
const { execFile } = await import('node:child_process');
|
|
811
|
+
const { promisify } = await import('node:util');
|
|
812
|
+
const execFileAsync = promisify(execFile);
|
|
813
|
+
await getDb();
|
|
814
|
+
const registry = new AgentRegistryAccessor(process.cwd());
|
|
815
|
+
const credential = await registry.get(args.agentId);
|
|
816
|
+
if (!credential) {
|
|
817
|
+
cliOutput({
|
|
818
|
+
success: false,
|
|
819
|
+
error: { code: 'E_NOT_FOUND', message: `Agent '${args.agentId}' not registered.` },
|
|
820
|
+
}, { command: 'agent work' });
|
|
821
|
+
process.exitCode = 1;
|
|
822
|
+
return;
|
|
823
|
+
}
|
|
824
|
+
await registry.update(args.agentId, { isActive: true });
|
|
825
|
+
await registry.markUsed(args.agentId);
|
|
826
|
+
const cantPath = join(CLEO_DIR_NAME, AGENTS_SUBDIR, `${args.agentId}.cant`);
|
|
827
|
+
const hasProfile = existsSync(cantPath);
|
|
828
|
+
const runtime = await createRuntime(registry, {
|
|
829
|
+
agentId: args.agentId,
|
|
830
|
+
pollIntervalMs: 5000,
|
|
831
|
+
heartbeatIntervalMs: 30000,
|
|
832
|
+
});
|
|
833
|
+
runtime.poller.start();
|
|
834
|
+
const executeMode = args.execute === true;
|
|
835
|
+
const epicRestrict = args.epic;
|
|
836
|
+
const adapterRestrict = args.adapter;
|
|
837
|
+
cliOutput({
|
|
838
|
+
success: true,
|
|
839
|
+
data: {
|
|
840
|
+
agentId: args.agentId,
|
|
841
|
+
mode: executeMode ? 'conductor-loop' : 'watch-only',
|
|
842
|
+
profile: hasProfile ? 'loaded' : 'none',
|
|
843
|
+
status: 'running',
|
|
844
|
+
epic: epicRestrict ?? 'any',
|
|
845
|
+
adapter: adapterRestrict ?? 'auto',
|
|
846
|
+
},
|
|
847
|
+
}, { command: 'agent work' });
|
|
848
|
+
// Parse LAFS envelope from CLI stdout (handles both minimal and full shapes)
|
|
849
|
+
const parseLafs = (raw) => {
|
|
850
|
+
const lines = raw.trim().split('\n');
|
|
851
|
+
const envLine = [...lines].reverse().find((l) => l.startsWith('{'));
|
|
852
|
+
if (!envLine)
|
|
853
|
+
return undefined;
|
|
854
|
+
try {
|
|
855
|
+
const env = JSON.parse(envLine);
|
|
856
|
+
if (env.ok === true)
|
|
857
|
+
return env.r;
|
|
858
|
+
if (env.success === true)
|
|
859
|
+
return (env.result ?? env.data);
|
|
860
|
+
return undefined;
|
|
861
|
+
}
|
|
862
|
+
catch {
|
|
863
|
+
return undefined;
|
|
864
|
+
}
|
|
865
|
+
};
|
|
866
|
+
const runCleo = async (cleoArgs, timeoutMs = 15000) => {
|
|
867
|
+
const { stdout } = await execFileAsync('cleo', cleoArgs, {
|
|
868
|
+
encoding: 'utf-8',
|
|
869
|
+
timeout: timeoutMs,
|
|
870
|
+
});
|
|
871
|
+
return stdout;
|
|
872
|
+
};
|
|
873
|
+
const taskInterval = Number.parseInt(args['poll-interval'], 10);
|
|
874
|
+
let inFlight = false;
|
|
875
|
+
let iterations = 0;
|
|
876
|
+
const workLoop = setInterval(async () => {
|
|
877
|
+
if (inFlight)
|
|
878
|
+
return;
|
|
879
|
+
inFlight = true;
|
|
880
|
+
iterations += 1;
|
|
881
|
+
try {
|
|
882
|
+
const currentRaw = await runCleo(['current']).catch(() => '');
|
|
883
|
+
if (currentRaw.trim()) {
|
|
884
|
+
// A task is already in progress — skip
|
|
885
|
+
return;
|
|
886
|
+
}
|
|
887
|
+
// Resolve next ready task (respect epic restriction when set)
|
|
888
|
+
const nextArgs = epicRestrict ? ['orchestrate', 'next', epicRestrict] : ['next'];
|
|
889
|
+
const nextRaw = await runCleo(nextArgs).catch(() => '');
|
|
890
|
+
if (!nextRaw.trim())
|
|
891
|
+
return;
|
|
892
|
+
const nextData = parseLafs(nextRaw);
|
|
893
|
+
const taskId = nextData?.nextTask?.id ?? (typeof nextData?.id === 'string' ? nextData.id : undefined);
|
|
894
|
+
if (!taskId)
|
|
895
|
+
return;
|
|
896
|
+
if (!executeMode) {
|
|
897
|
+
// Watch-only legacy behaviour: advertise availability
|
|
898
|
+
console.log(`[${args.agentId}] Task available: ${taskId}. Pass --execute to run autonomously.`);
|
|
899
|
+
return;
|
|
900
|
+
}
|
|
901
|
+
// Phase 3 Conductor Loop: actually execute via orchestrate.spawn.execute
|
|
902
|
+
const spawnArgs = ['orchestrate', 'spawn', taskId];
|
|
903
|
+
if (adapterRestrict) {
|
|
904
|
+
spawnArgs.push('--adapter', adapterRestrict);
|
|
905
|
+
}
|
|
906
|
+
const spawnRaw = await runCleo(spawnArgs, 60000).catch((e) => {
|
|
907
|
+
console.error(`[${args.agentId}] conductor-loop: spawn failed for ${taskId}: ${String(e)}`);
|
|
908
|
+
return '';
|
|
909
|
+
});
|
|
910
|
+
const spawnData = parseLafs(spawnRaw);
|
|
911
|
+
if (spawnData?.instanceId) {
|
|
912
|
+
console.log(`[${args.agentId}] conductor-loop spawned task=${taskId} instance=${spawnData.instanceId} status=${spawnData.status ?? 'unknown'}`);
|
|
913
|
+
}
|
|
914
|
+
}
|
|
915
|
+
catch {
|
|
916
|
+
/* non-fatal — loop continues */
|
|
917
|
+
}
|
|
918
|
+
finally {
|
|
919
|
+
inFlight = false;
|
|
920
|
+
}
|
|
921
|
+
}, taskInterval);
|
|
922
|
+
const shutdown = () => {
|
|
923
|
+
clearInterval(workLoop);
|
|
924
|
+
runtime.stop();
|
|
925
|
+
void registry.update(args.agentId, { isActive: false }).catch(() => { });
|
|
926
|
+
if (executeMode) {
|
|
927
|
+
console.log(`[${args.agentId}] conductor-loop shutdown after ${iterations} iterations.`);
|
|
928
|
+
}
|
|
929
|
+
process.exit(0);
|
|
930
|
+
};
|
|
931
|
+
process.on('SIGINT', shutdown);
|
|
932
|
+
process.on('SIGTERM', shutdown);
|
|
933
|
+
if (process.platform === 'win32') {
|
|
934
|
+
process.on('message', (msg) => {
|
|
935
|
+
if (msg === 'shutdown')
|
|
936
|
+
shutdown();
|
|
937
|
+
});
|
|
938
|
+
}
|
|
939
|
+
await new Promise(() => { });
|
|
940
|
+
}
|
|
941
|
+
catch (err) {
|
|
942
|
+
cliOutput({ success: false, error: { code: 'E_WORK', message: String(err) } }, { command: 'agent work' });
|
|
943
|
+
process.exitCode = 1;
|
|
944
|
+
}
|
|
945
|
+
},
|
|
946
|
+
});
|
|
947
|
+
/**
|
|
948
|
+
* cleo agent list — list registered agent credentials.
|
|
949
|
+
*
|
|
950
|
+
* Default: project-scoped INNER JOIN. Use --global for full cross-project scan
|
|
951
|
+
* (ADR-037 §4 Q1=B).
|
|
952
|
+
*
|
|
953
|
+
* @task T362 @epic T310
|
|
954
|
+
*/
|
|
955
|
+
const listCommand = defineCommand({
|
|
956
|
+
meta: {
|
|
957
|
+
name: 'list',
|
|
958
|
+
description: 'List registered agent credentials',
|
|
959
|
+
},
|
|
960
|
+
args: {
|
|
961
|
+
active: {
|
|
962
|
+
type: 'boolean',
|
|
963
|
+
description: 'Show only active agents (project-scoped mode only)',
|
|
964
|
+
},
|
|
965
|
+
global: {
|
|
966
|
+
type: 'boolean',
|
|
967
|
+
description: 'Show all global agents regardless of project attachment (ADR-037 §4 Q1=B)',
|
|
968
|
+
},
|
|
969
|
+
'include-disabled': {
|
|
970
|
+
type: 'boolean',
|
|
971
|
+
description: 'Include detached/disabled agents (enabled=0)',
|
|
972
|
+
},
|
|
973
|
+
},
|
|
974
|
+
async run({ args }) {
|
|
975
|
+
try {
|
|
976
|
+
const { listAgentsForProject, getDb } = await import('@cleocode/core/internal');
|
|
977
|
+
await getDb();
|
|
978
|
+
const includeGlobal = args.global === true;
|
|
979
|
+
const includeDisabled = args['include-disabled'] === true;
|
|
980
|
+
const agents = listAgentsForProject(process.cwd(), {
|
|
981
|
+
includeGlobal,
|
|
982
|
+
includeDisabled,
|
|
983
|
+
});
|
|
984
|
+
// Apply legacy --active filter only when NOT in global mode
|
|
985
|
+
const filtered = !includeGlobal && args.active ? agents.filter((a) => a.isActive) : agents;
|
|
986
|
+
cliOutput({
|
|
987
|
+
success: true,
|
|
988
|
+
data: filtered.map((a) => ({
|
|
989
|
+
agentId: a.agentId,
|
|
990
|
+
name: a.displayName,
|
|
991
|
+
classification: a.classification ?? null,
|
|
992
|
+
transportType: a.transportType,
|
|
993
|
+
isActive: a.isActive,
|
|
994
|
+
lastUsedAt: a.lastUsedAt ?? null,
|
|
995
|
+
attachment: a.projectRef
|
|
996
|
+
? a.projectRef.enabled === 1
|
|
997
|
+
? '[attached]'
|
|
998
|
+
: '[disabled]'
|
|
999
|
+
: '[global]',
|
|
1000
|
+
})),
|
|
1001
|
+
}, { command: 'agent list' });
|
|
1002
|
+
}
|
|
1003
|
+
catch (err) {
|
|
1004
|
+
cliOutput({ success: false, error: { code: 'E_LIST', message: String(err) } }, { command: 'agent list' });
|
|
1005
|
+
process.exitCode = 1;
|
|
1006
|
+
}
|
|
1007
|
+
},
|
|
1008
|
+
});
|
|
1009
|
+
/**
|
|
1010
|
+
* cleo agent get <agentId> — get details for a specific agent credential.
|
|
1011
|
+
*
|
|
1012
|
+
* Default: project-scoped lookup. With --global: cross-project identity lookup
|
|
1013
|
+
* (ADR-037 §4).
|
|
1014
|
+
*
|
|
1015
|
+
* @task T362 @epic T310
|
|
1016
|
+
*/
|
|
1017
|
+
const getCommand = defineCommand({
|
|
1018
|
+
meta: {
|
|
1019
|
+
name: 'get',
|
|
1020
|
+
description: 'Get details for a specific agent credential',
|
|
1021
|
+
},
|
|
1022
|
+
args: {
|
|
1023
|
+
agentId: {
|
|
1024
|
+
type: 'positional',
|
|
1025
|
+
description: 'Agent ID to look up',
|
|
1026
|
+
required: true,
|
|
1027
|
+
},
|
|
1028
|
+
global: {
|
|
1029
|
+
type: 'boolean',
|
|
1030
|
+
description: 'Perform global identity lookup — returns agent even if not attached to current project',
|
|
1031
|
+
},
|
|
1032
|
+
},
|
|
1033
|
+
async run({ args }) {
|
|
1034
|
+
try {
|
|
1035
|
+
const { lookupAgent, getDb } = await import('@cleocode/core/internal');
|
|
1036
|
+
await getDb();
|
|
1037
|
+
const includeGlobal = args.global === true;
|
|
1038
|
+
const agent = lookupAgent(process.cwd(), args.agentId, { includeGlobal });
|
|
1039
|
+
if (!agent) {
|
|
1040
|
+
cliOutput({
|
|
1041
|
+
success: false,
|
|
1042
|
+
error: { code: 'E_NOT_FOUND', message: `Agent not found: ${args.agentId}` },
|
|
1043
|
+
}, { command: 'agent get' });
|
|
1044
|
+
process.exitCode = 4;
|
|
1045
|
+
return;
|
|
1046
|
+
}
|
|
1047
|
+
// Redact API key in output
|
|
1048
|
+
const redactedKey = agent.apiKey.length > 16
|
|
1049
|
+
? `${agent.apiKey.substring(0, 12)}...${agent.apiKey.substring(agent.apiKey.length - 4)}`
|
|
1050
|
+
: '***redacted***';
|
|
1051
|
+
cliOutput({
|
|
1052
|
+
success: true,
|
|
1053
|
+
data: {
|
|
1054
|
+
agentId: agent.agentId,
|
|
1055
|
+
displayName: agent.displayName,
|
|
1056
|
+
apiKey: redactedKey,
|
|
1057
|
+
apiBaseUrl: agent.apiBaseUrl,
|
|
1058
|
+
classification: agent.classification ?? null,
|
|
1059
|
+
transportType: agent.transportType,
|
|
1060
|
+
isActive: agent.isActive,
|
|
1061
|
+
lastUsedAt: agent.lastUsedAt ?? null,
|
|
1062
|
+
createdAt: agent.createdAt,
|
|
1063
|
+
updatedAt: agent.updatedAt,
|
|
1064
|
+
projectRef: agent.projectRef ?? 'not attached to current project',
|
|
1065
|
+
},
|
|
1066
|
+
}, { command: 'agent get' });
|
|
1067
|
+
}
|
|
1068
|
+
catch (err) {
|
|
1069
|
+
cliOutput({ success: false, error: { code: 'E_GET', message: String(err) } }, { command: 'agent get' });
|
|
1070
|
+
process.exitCode = 1;
|
|
1071
|
+
}
|
|
1072
|
+
},
|
|
1073
|
+
});
|
|
1074
|
+
/**
|
|
1075
|
+
* cleo agent attach <agentId> — attach a global agent to the current project.
|
|
1076
|
+
*
|
|
1077
|
+
* @task T364 @epic T310
|
|
1078
|
+
* @why ADR-037 §3 — project_agent_refs override table allows per-project
|
|
1079
|
+
* agents to be attached/detached without touching global identity.
|
|
1080
|
+
*/
|
|
1081
|
+
const attachCommand = defineCommand({
|
|
1082
|
+
meta: {
|
|
1083
|
+
name: 'attach',
|
|
1084
|
+
description: 'Attach a global agent to the current project',
|
|
1085
|
+
},
|
|
1086
|
+
args: {
|
|
1087
|
+
agentId: {
|
|
1088
|
+
type: 'positional',
|
|
1089
|
+
description: 'Agent ID to attach',
|
|
1090
|
+
required: true,
|
|
1091
|
+
},
|
|
1092
|
+
role: {
|
|
1093
|
+
type: 'string',
|
|
1094
|
+
description: 'Per-project role override',
|
|
1095
|
+
},
|
|
1096
|
+
'capabilities-override': {
|
|
1097
|
+
type: 'string',
|
|
1098
|
+
description: 'JSON blob of capability overrides',
|
|
1099
|
+
},
|
|
1100
|
+
},
|
|
1101
|
+
async run({ args }) {
|
|
1102
|
+
try {
|
|
1103
|
+
const { AgentRegistryAccessor, attachAgentToProject, lookupAgent, getDb } = await import('@cleocode/core/internal');
|
|
1104
|
+
await getDb();
|
|
1105
|
+
const projectRoot = process.cwd();
|
|
1106
|
+
// Ensure both DBs initialised before any cross-DB operation
|
|
1107
|
+
const _registry = new AgentRegistryAccessor(projectRoot);
|
|
1108
|
+
void _registry;
|
|
1109
|
+
// Verify agent exists globally
|
|
1110
|
+
const globalAgent = lookupAgent(projectRoot, args.agentId, { includeGlobal: true });
|
|
1111
|
+
if (!globalAgent) {
|
|
1112
|
+
cliOutput({
|
|
1113
|
+
success: false,
|
|
1114
|
+
error: { code: 'E_NOT_FOUND', message: `Agent not found: ${args.agentId}` },
|
|
1115
|
+
}, { command: 'agent attach' });
|
|
1116
|
+
process.exitCode = 4;
|
|
1117
|
+
return;
|
|
1118
|
+
}
|
|
1119
|
+
attachAgentToProject(projectRoot, args.agentId, {
|
|
1120
|
+
role: typeof args.role === 'string' ? args.role : null,
|
|
1121
|
+
capabilitiesOverride: typeof args['capabilities-override'] === 'string' ? args['capabilities-override'] : null,
|
|
1122
|
+
});
|
|
1123
|
+
cliOutput({
|
|
1124
|
+
success: true,
|
|
1125
|
+
data: {
|
|
1126
|
+
attached: args.agentId,
|
|
1127
|
+
projectRoot,
|
|
1128
|
+
role: typeof args.role === 'string' ? args.role : null,
|
|
1129
|
+
},
|
|
1130
|
+
}, { command: 'agent attach' });
|
|
1131
|
+
}
|
|
1132
|
+
catch (err) {
|
|
1133
|
+
cliOutput({ success: false, error: { code: 'E_ATTACH', message: String(err) } }, { command: 'agent attach' });
|
|
1134
|
+
process.exitCode = 1;
|
|
1135
|
+
}
|
|
1136
|
+
},
|
|
1137
|
+
});
|
|
1138
|
+
/**
|
|
1139
|
+
* cleo agent detach <agentId> — detach an agent from the current project.
|
|
1140
|
+
*
|
|
1141
|
+
* @task T364 @epic T310
|
|
1142
|
+
* @why ADR-037 §3 — soft-delete via project_agent_refs.enabled=0 preserves
|
|
1143
|
+
* global agent identity while removing it from the project view.
|
|
1144
|
+
*/
|
|
1145
|
+
const detachCommand = defineCommand({
|
|
1146
|
+
meta: {
|
|
1147
|
+
name: 'detach',
|
|
1148
|
+
description: 'Detach an agent from the current project (preserves global identity)',
|
|
1149
|
+
},
|
|
1150
|
+
args: {
|
|
1151
|
+
agentId: {
|
|
1152
|
+
type: 'positional',
|
|
1153
|
+
description: 'Agent ID to detach',
|
|
1154
|
+
required: true,
|
|
1155
|
+
},
|
|
1156
|
+
},
|
|
1157
|
+
async run({ args }) {
|
|
1158
|
+
try {
|
|
1159
|
+
const { AgentRegistryAccessor, detachAgentFromProject, getProjectAgentRef, getDb } = await import('@cleocode/core/internal');
|
|
1160
|
+
await getDb();
|
|
1161
|
+
const projectRoot = process.cwd();
|
|
1162
|
+
// Ensure both DBs initialised
|
|
1163
|
+
const _registry = new AgentRegistryAccessor(projectRoot);
|
|
1164
|
+
void _registry;
|
|
1165
|
+
const ref = getProjectAgentRef(projectRoot, args.agentId);
|
|
1166
|
+
if (!ref) {
|
|
1167
|
+
cliOutput({
|
|
1168
|
+
success: false,
|
|
1169
|
+
error: {
|
|
1170
|
+
code: 'E_NOT_FOUND',
|
|
1171
|
+
message: `Agent ${args.agentId} not attached to current project`,
|
|
1172
|
+
},
|
|
1173
|
+
}, { command: 'agent detach' });
|
|
1174
|
+
process.exitCode = 4;
|
|
1175
|
+
return;
|
|
1176
|
+
}
|
|
1177
|
+
detachAgentFromProject(projectRoot, args.agentId);
|
|
1178
|
+
cliOutput({ success: true, data: { detached: args.agentId, projectRoot } }, { command: 'agent detach' });
|
|
1179
|
+
}
|
|
1180
|
+
catch (err) {
|
|
1181
|
+
cliOutput({ success: false, error: { code: 'E_DETACH', message: String(err) } }, { command: 'agent detach' });
|
|
1182
|
+
process.exitCode = 1;
|
|
1183
|
+
}
|
|
1184
|
+
},
|
|
1185
|
+
});
|
|
1186
|
+
/**
|
|
1187
|
+
* cleo agent remove <agentId> — detach agent from project or remove from global registry.
|
|
1188
|
+
*
|
|
1189
|
+
* Default (no --global): sets enabled=0 in conduit.db:project_agent_refs only.
|
|
1190
|
+
* --global: calls AgentRegistryAccessor.removeGlobal() (destructive). Pre-check
|
|
1191
|
+
* scans the current project's conduit.db for an active reference.
|
|
1192
|
+
*
|
|
1193
|
+
* @task T366 @epic T310
|
|
1194
|
+
* @why ADR-037 §6 — never-auto-delete semantics; --global flag required for
|
|
1195
|
+
* destructive removal; best-effort cross-project scan with documented
|
|
1196
|
+
* limitation (current project only for v1).
|
|
1197
|
+
*/
|
|
1198
|
+
const removeCommand = defineCommand({
|
|
1199
|
+
meta: {
|
|
1200
|
+
name: 'remove',
|
|
1201
|
+
description: 'Detach agent from current project (default) or remove global identity with --global',
|
|
1202
|
+
},
|
|
1203
|
+
args: {
|
|
1204
|
+
agentId: {
|
|
1205
|
+
type: 'positional',
|
|
1206
|
+
description: 'Agent ID to remove',
|
|
1207
|
+
required: true,
|
|
1208
|
+
},
|
|
1209
|
+
global: {
|
|
1210
|
+
type: 'boolean',
|
|
1211
|
+
description: 'Remove from global signaldock.db (destructive, irreversible)',
|
|
1212
|
+
},
|
|
1213
|
+
'force-global': {
|
|
1214
|
+
type: 'boolean',
|
|
1215
|
+
description: 'Force global removal even when the current project still has an active reference',
|
|
1216
|
+
},
|
|
1217
|
+
},
|
|
1218
|
+
async run({ args }) {
|
|
1219
|
+
try {
|
|
1220
|
+
const { AgentRegistryAccessor, detachAgentFromProject, getProjectAgentRef, getDb } = await import('@cleocode/core/internal');
|
|
1221
|
+
await getDb();
|
|
1222
|
+
const projectRoot = process.cwd();
|
|
1223
|
+
if (!args.global) {
|
|
1224
|
+
// Project-scoped detach (default post-T310) — mirrors `cleo agent detach`
|
|
1225
|
+
// Ensure both DBs initialised
|
|
1226
|
+
const _registry = new AgentRegistryAccessor(projectRoot);
|
|
1227
|
+
void _registry;
|
|
1228
|
+
const ref = getProjectAgentRef(projectRoot, args.agentId);
|
|
1229
|
+
if (!ref) {
|
|
1230
|
+
cliOutput({
|
|
1231
|
+
success: false,
|
|
1232
|
+
error: {
|
|
1233
|
+
code: 'E_NOT_FOUND',
|
|
1234
|
+
message: `Agent ${args.agentId} not attached to current project`,
|
|
1235
|
+
},
|
|
1236
|
+
}, { command: 'agent remove' });
|
|
1237
|
+
process.exitCode = 4;
|
|
1238
|
+
return;
|
|
1239
|
+
}
|
|
1240
|
+
detachAgentFromProject(projectRoot, args.agentId);
|
|
1241
|
+
cliOutput({ success: true, data: { removed: args.agentId, scope: 'project', projectRoot } }, { command: 'agent remove' });
|
|
1242
|
+
return;
|
|
1243
|
+
}
|
|
1244
|
+
// --global: destructive removal from signaldock.db
|
|
1245
|
+
// Safety scan — limited to current project (ADR-037 §6 known limitation).
|
|
1246
|
+
const _registry2 = new AgentRegistryAccessor(projectRoot);
|
|
1247
|
+
void _registry2;
|
|
1248
|
+
const activeRef = getProjectAgentRef(projectRoot, args.agentId);
|
|
1249
|
+
if (activeRef && !args['force-global']) {
|
|
1250
|
+
console.warn(`NOTE: Safety scan is limited to the current project. Other projects may have ` +
|
|
1251
|
+
`dangling references after global removal.`);
|
|
1252
|
+
cliOutput({
|
|
1253
|
+
success: false,
|
|
1254
|
+
error: {
|
|
1255
|
+
code: 'E_VALIDATION',
|
|
1256
|
+
message: `Agent "${args.agentId}" is still attached to current project. ` +
|
|
1257
|
+
`Detach first or pass --force-global to proceed anyway.`,
|
|
1258
|
+
fix: `cleo agent detach ${args.agentId} # detach first, then retry` +
|
|
1259
|
+
` OR cleo agent remove ${args.agentId} --global --force-global`,
|
|
1260
|
+
},
|
|
1261
|
+
}, { command: 'agent remove' });
|
|
1262
|
+
process.exitCode = 6;
|
|
1263
|
+
return;
|
|
1264
|
+
}
|
|
1265
|
+
console.warn(`NOTE: Safety scan is limited to the current project. Other projects may have ` +
|
|
1266
|
+
`dangling references after global removal.`);
|
|
1267
|
+
const registry = new AgentRegistryAccessor(projectRoot);
|
|
1268
|
+
await registry.removeGlobal(args.agentId, { force: args['force-global'] === true });
|
|
1269
|
+
cliOutput({ success: true, data: { removed: args.agentId, scope: 'global' } }, { command: 'agent remove' });
|
|
1270
|
+
}
|
|
1271
|
+
catch (err) {
|
|
1272
|
+
cliOutput({ success: false, error: { code: 'E_REMOVE', message: String(err) } }, { command: 'agent remove' });
|
|
1273
|
+
process.exitCode = 1;
|
|
1274
|
+
}
|
|
1275
|
+
},
|
|
1276
|
+
});
|
|
1277
|
+
/** cleo agent rotate-key <agentId> — rotate an agent API key */
|
|
1278
|
+
const rotateKeyCommand = defineCommand({
|
|
1279
|
+
meta: {
|
|
1280
|
+
name: 'rotate-key',
|
|
1281
|
+
description: 'Rotate an agent API key (generates new key on cloud, re-encrypts locally)',
|
|
1282
|
+
},
|
|
1283
|
+
args: {
|
|
1284
|
+
agentId: {
|
|
1285
|
+
type: 'positional',
|
|
1286
|
+
description: 'Agent ID whose key should be rotated',
|
|
1287
|
+
required: true,
|
|
1288
|
+
},
|
|
1289
|
+
},
|
|
1290
|
+
async run({ args }) {
|
|
1291
|
+
try {
|
|
1292
|
+
const { AgentRegistryAccessor, getDb } = await import('@cleocode/core/internal');
|
|
1293
|
+
await getDb();
|
|
1294
|
+
const registry = new AgentRegistryAccessor(process.cwd());
|
|
1295
|
+
const result = await registry.rotateKey(args.agentId);
|
|
1296
|
+
cliOutput({
|
|
1297
|
+
success: true,
|
|
1298
|
+
data: {
|
|
1299
|
+
agentId: result.agentId,
|
|
1300
|
+
newApiKey: `${result.newApiKey.substring(0, 12)}...`,
|
|
1301
|
+
message: 'API key rotated. Old key is invalidated.',
|
|
1302
|
+
},
|
|
1303
|
+
}, { command: 'agent rotate-key' });
|
|
1304
|
+
}
|
|
1305
|
+
catch (err) {
|
|
1306
|
+
cliOutput({ success: false, error: { code: 'E_ROTATE', message: String(err) } }, { command: 'agent rotate-key' });
|
|
1307
|
+
process.exitCode = 1;
|
|
1308
|
+
}
|
|
1309
|
+
},
|
|
1310
|
+
});
|
|
1311
|
+
/** cleo agent claim-code <agentId> — generate a claim code for human ownership verification */
|
|
1312
|
+
const claimCodeCommand = defineCommand({
|
|
1313
|
+
meta: {
|
|
1314
|
+
name: 'claim-code',
|
|
1315
|
+
description: 'Generate a claim code for human ownership of an agent',
|
|
1316
|
+
},
|
|
1317
|
+
args: {
|
|
1318
|
+
agentId: {
|
|
1319
|
+
type: 'positional',
|
|
1320
|
+
description: 'Agent ID to generate a claim code for',
|
|
1321
|
+
required: true,
|
|
1322
|
+
},
|
|
1323
|
+
},
|
|
1324
|
+
async run({ args }) {
|
|
1325
|
+
try {
|
|
1326
|
+
const { AgentRegistryAccessor, getDb } = await import('@cleocode/core/internal');
|
|
1327
|
+
await getDb();
|
|
1328
|
+
const registry = new AgentRegistryAccessor(process.cwd());
|
|
1329
|
+
const credential = await registry.get(args.agentId);
|
|
1330
|
+
if (!credential) {
|
|
1331
|
+
cliOutput({
|
|
1332
|
+
success: false,
|
|
1333
|
+
error: { code: 'E_NOT_FOUND', message: `Agent not found: ${args.agentId}` },
|
|
1334
|
+
}, { command: 'agent claim-code' });
|
|
1335
|
+
process.exitCode = 4;
|
|
1336
|
+
return;
|
|
1337
|
+
}
|
|
1338
|
+
const response = await fetch(`${credential.apiBaseUrl}/agents/${args.agentId}/claim-code`, {
|
|
1339
|
+
method: 'POST',
|
|
1340
|
+
headers: {
|
|
1341
|
+
'Content-Type': 'application/json',
|
|
1342
|
+
Authorization: `Bearer ${credential.apiKey}`,
|
|
1343
|
+
'X-Agent-Id': args.agentId,
|
|
1344
|
+
},
|
|
1345
|
+
});
|
|
1346
|
+
if (!response.ok) {
|
|
1347
|
+
const text = await response.text().catch(() => '');
|
|
1348
|
+
throw new Error(`Failed to generate claim code: ${response.status} ${text}`);
|
|
1349
|
+
}
|
|
1350
|
+
const data = (await response.json());
|
|
1351
|
+
cliOutput({
|
|
1352
|
+
success: true,
|
|
1353
|
+
data: {
|
|
1354
|
+
agentId: args.agentId,
|
|
1355
|
+
claimCode: data.data?.claimCode,
|
|
1356
|
+
claimUrl: data.data?.claimUrl ?? `https://signaldock.io/claim/${data.data?.claimCode}`,
|
|
1357
|
+
expiresAt: data.data?.expiresAt,
|
|
1358
|
+
message: 'Share this claim code with the human owner to verify agent ownership.',
|
|
1359
|
+
},
|
|
1360
|
+
}, { command: 'agent claim-code' });
|
|
1361
|
+
}
|
|
1362
|
+
catch (err) {
|
|
1363
|
+
cliOutput({ success: false, error: { code: 'E_CLAIM', message: String(err) } }, { command: 'agent claim-code' });
|
|
1364
|
+
process.exitCode = 1;
|
|
1365
|
+
}
|
|
1366
|
+
},
|
|
1367
|
+
});
|
|
1368
|
+
/** cleo agent watch — start continuous message polling for the active agent */
|
|
1369
|
+
const watchCommand = defineCommand({
|
|
1370
|
+
meta: {
|
|
1371
|
+
name: 'watch',
|
|
1372
|
+
description: 'Start continuous message polling for the active agent (long-running)',
|
|
1373
|
+
},
|
|
1374
|
+
args: {
|
|
1375
|
+
agent: {
|
|
1376
|
+
type: 'string',
|
|
1377
|
+
description: 'Agent ID to watch as (defaults to most recently used)',
|
|
1378
|
+
alias: 'a',
|
|
1379
|
+
},
|
|
1380
|
+
interval: {
|
|
1381
|
+
type: 'string',
|
|
1382
|
+
description: 'Poll interval in milliseconds',
|
|
1383
|
+
default: '5000',
|
|
1384
|
+
},
|
|
1385
|
+
group: {
|
|
1386
|
+
type: 'string',
|
|
1387
|
+
description: 'Comma-separated group conversation IDs to monitor',
|
|
1388
|
+
},
|
|
1389
|
+
},
|
|
1390
|
+
async run({ args }) {
|
|
1391
|
+
try {
|
|
1392
|
+
const { AgentRegistryAccessor, getDb } = await import('@cleocode/core/internal');
|
|
1393
|
+
const { createRuntime } = await import('@cleocode/runtime');
|
|
1394
|
+
await getDb();
|
|
1395
|
+
const registry = new AgentRegistryAccessor(process.cwd());
|
|
1396
|
+
const groupIds = args.group ? args.group.split(',').map((s) => s.trim()) : undefined;
|
|
1397
|
+
const handle = await createRuntime(registry, {
|
|
1398
|
+
agentId: args.agent,
|
|
1399
|
+
pollIntervalMs: Number(args.interval) || 5000,
|
|
1400
|
+
groupConversationIds: groupIds,
|
|
1401
|
+
});
|
|
1402
|
+
handle.poller.onMessage((msg) => {
|
|
1403
|
+
cliOutput({
|
|
1404
|
+
success: true,
|
|
1405
|
+
data: {
|
|
1406
|
+
event: 'message',
|
|
1407
|
+
id: msg.id,
|
|
1408
|
+
from: msg.from,
|
|
1409
|
+
content: msg.content,
|
|
1410
|
+
threadId: msg.threadId,
|
|
1411
|
+
timestamp: msg.timestamp,
|
|
1412
|
+
},
|
|
1413
|
+
}, { command: 'agent watch' });
|
|
1414
|
+
});
|
|
1415
|
+
handle.poller.start();
|
|
1416
|
+
cliOutput({
|
|
1417
|
+
success: true,
|
|
1418
|
+
data: {
|
|
1419
|
+
event: 'started',
|
|
1420
|
+
agentId: handle.agentId,
|
|
1421
|
+
pollIntervalMs: Number(args.interval) || 5000,
|
|
1422
|
+
groupConversationIds: groupIds ?? [],
|
|
1423
|
+
message: 'Watching for messages. Press Ctrl+C to stop.',
|
|
1424
|
+
},
|
|
1425
|
+
}, { command: 'agent watch' });
|
|
1426
|
+
// Keep alive until SIGINT/SIGTERM
|
|
1427
|
+
const shutdown = () => {
|
|
1428
|
+
handle.stop();
|
|
1429
|
+
cliOutput({ success: true, data: { event: 'stopped', agentId: handle.agentId } }, { command: 'agent watch' });
|
|
1430
|
+
process.exit(0);
|
|
1431
|
+
};
|
|
1432
|
+
process.on('SIGINT', shutdown);
|
|
1433
|
+
process.on('SIGTERM', shutdown);
|
|
1434
|
+
if (process.platform === 'win32') {
|
|
1435
|
+
process.on('message', (msg) => {
|
|
1436
|
+
if (msg === 'shutdown')
|
|
1437
|
+
shutdown();
|
|
1438
|
+
});
|
|
1439
|
+
}
|
|
1440
|
+
}
|
|
1441
|
+
catch (err) {
|
|
1442
|
+
cliOutput({ success: false, error: { code: 'E_WATCH', message: String(err) } }, { command: 'agent watch' });
|
|
1443
|
+
process.exitCode = 1;
|
|
1444
|
+
}
|
|
1445
|
+
},
|
|
1446
|
+
});
|
|
1447
|
+
/** cleo agent poll — one-shot message check for the active agent */
|
|
1448
|
+
const pollCommand = defineCommand({
|
|
1449
|
+
meta: {
|
|
1450
|
+
name: 'poll',
|
|
1451
|
+
description: 'One-shot message check for the active agent',
|
|
1452
|
+
},
|
|
1453
|
+
args: {
|
|
1454
|
+
agent: {
|
|
1455
|
+
type: 'string',
|
|
1456
|
+
description: 'Agent ID to poll as (defaults to most recently used)',
|
|
1457
|
+
alias: 'a',
|
|
1458
|
+
},
|
|
1459
|
+
limit: {
|
|
1460
|
+
type: 'string',
|
|
1461
|
+
description: 'Max messages to fetch',
|
|
1462
|
+
default: '20',
|
|
1463
|
+
},
|
|
1464
|
+
},
|
|
1465
|
+
async run({ args }) {
|
|
1466
|
+
try {
|
|
1467
|
+
const { AgentRegistryAccessor, createConduit, getDb } = await import('@cleocode/core/internal');
|
|
1468
|
+
await getDb();
|
|
1469
|
+
const registry = new AgentRegistryAccessor(process.cwd());
|
|
1470
|
+
const agentId = args.agent;
|
|
1471
|
+
const conduit = await createConduit(registry, agentId);
|
|
1472
|
+
const limit = Number(args.limit) || 20;
|
|
1473
|
+
const messages = await conduit.poll({ limit });
|
|
1474
|
+
cliOutput({
|
|
1475
|
+
success: true,
|
|
1476
|
+
data: { agentId: conduit.agentId, messages, count: messages.length, limit },
|
|
1477
|
+
}, { command: 'agent poll' });
|
|
1478
|
+
await conduit.disconnect();
|
|
1479
|
+
}
|
|
1480
|
+
catch (err) {
|
|
1481
|
+
cliOutput({ success: false, error: { code: 'E_POLL', message: String(err) } }, { command: 'agent poll' });
|
|
1482
|
+
process.exitCode = 1;
|
|
1483
|
+
}
|
|
1484
|
+
},
|
|
1485
|
+
});
|
|
1486
|
+
/** cleo agent send <message> — send a message to an agent or conversation */
|
|
1487
|
+
const sendCommand = defineCommand({
|
|
1488
|
+
meta: {
|
|
1489
|
+
name: 'send',
|
|
1490
|
+
description: 'Send a message to an agent or conversation',
|
|
1491
|
+
},
|
|
1492
|
+
args: {
|
|
1493
|
+
message: {
|
|
1494
|
+
type: 'positional',
|
|
1495
|
+
description: 'Message content to send',
|
|
1496
|
+
required: true,
|
|
1497
|
+
},
|
|
1498
|
+
to: {
|
|
1499
|
+
type: 'string',
|
|
1500
|
+
description: 'Target agent ID',
|
|
1501
|
+
},
|
|
1502
|
+
conv: {
|
|
1503
|
+
type: 'string',
|
|
1504
|
+
description: 'Target conversation ID',
|
|
1505
|
+
},
|
|
1506
|
+
agent: {
|
|
1507
|
+
type: 'string',
|
|
1508
|
+
description: 'Send as this agent (defaults to most recently used)',
|
|
1509
|
+
alias: 'a',
|
|
1510
|
+
},
|
|
1511
|
+
},
|
|
1512
|
+
async run({ args }) {
|
|
1513
|
+
try {
|
|
1514
|
+
const { AgentRegistryAccessor, createConduit, getDb } = await import('@cleocode/core/internal');
|
|
1515
|
+
await getDb();
|
|
1516
|
+
const registry = new AgentRegistryAccessor(process.cwd());
|
|
1517
|
+
const agentId = args.agent;
|
|
1518
|
+
const to = args.to;
|
|
1519
|
+
const conv = args.conv;
|
|
1520
|
+
if (!to && !conv) {
|
|
1521
|
+
cliOutput({ success: false, error: { code: 'E_ARGS', message: 'Must specify --to or --conv' } }, { command: 'agent send' });
|
|
1522
|
+
process.exitCode = 1;
|
|
1523
|
+
return;
|
|
1524
|
+
}
|
|
1525
|
+
const conduit = await createConduit(registry, agentId);
|
|
1526
|
+
const result = await conduit.send(to ?? conv ?? '', args.message, {
|
|
1527
|
+
threadId: conv,
|
|
1528
|
+
});
|
|
1529
|
+
cliOutput({ success: true, data: { messageId: result.messageId, deliveredAt: result.deliveredAt } }, { command: 'agent send' });
|
|
1530
|
+
await conduit.disconnect();
|
|
1531
|
+
}
|
|
1532
|
+
catch (err) {
|
|
1533
|
+
cliOutput({ success: false, error: { code: 'E_SEND', message: String(err) } }, { command: 'agent send' });
|
|
1534
|
+
process.exitCode = 1;
|
|
1535
|
+
}
|
|
1536
|
+
},
|
|
1537
|
+
});
|
|
1538
|
+
/** cleo agent health — check agent health and detect stale or crashed agents */
|
|
1539
|
+
const healthCommand = defineCommand({
|
|
1540
|
+
meta: {
|
|
1541
|
+
name: 'health',
|
|
1542
|
+
description: 'Check agent health and detect stale or crashed agents',
|
|
1543
|
+
},
|
|
1544
|
+
args: {
|
|
1545
|
+
id: {
|
|
1546
|
+
type: 'string',
|
|
1547
|
+
description: 'Check health for a specific agent ID',
|
|
1548
|
+
},
|
|
1549
|
+
threshold: {
|
|
1550
|
+
type: 'string',
|
|
1551
|
+
description: 'Staleness threshold in milliseconds (default: 180000 = 3 minutes)',
|
|
1552
|
+
default: String(STALE_THRESHOLD_MS),
|
|
1553
|
+
},
|
|
1554
|
+
'detect-crashed': {
|
|
1555
|
+
type: 'boolean',
|
|
1556
|
+
description: 'Detect and mark crashed agents (write operation)',
|
|
1557
|
+
},
|
|
1558
|
+
},
|
|
1559
|
+
async run({ args }) {
|
|
1560
|
+
const thresholdMs = Number(args.threshold);
|
|
1561
|
+
const agentId = args.id;
|
|
1562
|
+
const detectCrashed = args['detect-crashed'] === true;
|
|
1563
|
+
if (agentId) {
|
|
1564
|
+
const health = await checkAgentHealth(agentId, thresholdMs);
|
|
1565
|
+
if (!health) {
|
|
1566
|
+
cliOutput({
|
|
1567
|
+
success: false,
|
|
1568
|
+
error: { code: 'E_NOT_FOUND', message: `Agent not found: ${agentId}` },
|
|
1569
|
+
}, { command: 'agent health' });
|
|
1570
|
+
process.exitCode = 4;
|
|
1571
|
+
return;
|
|
1572
|
+
}
|
|
1573
|
+
cliOutput({ success: true, data: health }, { command: 'agent health' });
|
|
1574
|
+
return;
|
|
1575
|
+
}
|
|
1576
|
+
if (detectCrashed) {
|
|
1577
|
+
const crashed = await detectCrashedAgents(thresholdMs);
|
|
1578
|
+
cliOutput({
|
|
1579
|
+
success: true,
|
|
1580
|
+
data: {
|
|
1581
|
+
detectedCrashed: crashed.length,
|
|
1582
|
+
agents: crashed.map((a) => ({
|
|
1583
|
+
id: a.id,
|
|
1584
|
+
agentType: a.agentType,
|
|
1585
|
+
lastHeartbeat: a.lastHeartbeat,
|
|
1586
|
+
status: a.status,
|
|
1587
|
+
})),
|
|
1588
|
+
},
|
|
1589
|
+
}, { command: 'agent health' });
|
|
1590
|
+
return;
|
|
1591
|
+
}
|
|
1592
|
+
const [report, stale] = await Promise.all([
|
|
1593
|
+
getHealthReport(thresholdMs),
|
|
1594
|
+
detectStaleAgents(thresholdMs),
|
|
1595
|
+
]);
|
|
1596
|
+
cliOutput({
|
|
1597
|
+
success: true,
|
|
1598
|
+
data: {
|
|
1599
|
+
summary: {
|
|
1600
|
+
total: report.total,
|
|
1601
|
+
active: report.active,
|
|
1602
|
+
idle: report.idle,
|
|
1603
|
+
starting: report.starting,
|
|
1604
|
+
error: report.error,
|
|
1605
|
+
crashed: report.crashed,
|
|
1606
|
+
stopped: report.stopped,
|
|
1607
|
+
totalErrors: report.totalErrors,
|
|
1608
|
+
},
|
|
1609
|
+
staleAgents: stale.map((s) => ({
|
|
1610
|
+
id: s.agentId,
|
|
1611
|
+
status: s.status,
|
|
1612
|
+
heartbeatAgeMs: s.heartbeatAgeMs,
|
|
1613
|
+
lastHeartbeat: s.lastHeartbeat,
|
|
1614
|
+
thresholdMs: s.thresholdMs,
|
|
1615
|
+
})),
|
|
1616
|
+
thresholdMs,
|
|
1617
|
+
},
|
|
1618
|
+
}, { command: 'agent health' });
|
|
1619
|
+
},
|
|
1620
|
+
});
|
|
1621
|
+
/**
|
|
1622
|
+
* cleo agent install <path> — install an agent from a `.cant` manifest or
|
|
1623
|
+
* `.cantz` archive (ZIP) using the T889 / W2-3 {@link installAgentFromCant}
|
|
1624
|
+
* pipeline.
|
|
1625
|
+
*
|
|
1626
|
+
* - `.cant` file: fed directly to the pipeline; copied to the tier directory
|
|
1627
|
+
* and the `agents` row + `agent_skills` junctions are written atomically.
|
|
1628
|
+
* - `.cantz` archive: extracted to a temp dir, `persona.cant` is located and
|
|
1629
|
+
* renamed to `<agentId>.cant` before being passed to the pipeline.
|
|
1630
|
+
* - Agent-directory (legacy): same path as `.cantz` — looks for
|
|
1631
|
+
* `persona.cant` inside and feeds a renamed copy to the pipeline.
|
|
1632
|
+
*
|
|
1633
|
+
* Flags:
|
|
1634
|
+
* - `--global`: install to global tier ({@link getCleoGlobalAgentsDir}).
|
|
1635
|
+
* - `--strict`: fail with `E_VALIDATION` when the pipeline reports warnings
|
|
1636
|
+
* (e.g. unknown skill slugs).
|
|
1637
|
+
* - `--attach`: after install, also attach the agent to the current project
|
|
1638
|
+
* via {@link attachAgentToProject} (conduit.db:project_agent_refs).
|
|
1639
|
+
* - `--force`: overwrite an existing row / file instead of throwing
|
|
1640
|
+
* `E_AGENT_ALREADY_INSTALLED`.
|
|
1641
|
+
* - `--resync`: drop the existing `agents` row (keep the `.cant` on disk)
|
|
1642
|
+
* then re-install; combines `force: true` with a pre-flight row delete.
|
|
1643
|
+
*
|
|
1644
|
+
* On success, emits a LAFS envelope of shape `{ agentId, tier, cantPath,
|
|
1645
|
+
* cantSha256, inserted, skillsAttached, warnings, attached }`.
|
|
1646
|
+
*
|
|
1647
|
+
* @task T889 / W2-6
|
|
1648
|
+
* @epic T889
|
|
1649
|
+
* @see docs/specs/CANTZ-PACKAGE-STANDARD.md
|
|
1650
|
+
*/
|
|
1651
|
+
const installCommand = defineCommand({
|
|
1652
|
+
meta: {
|
|
1653
|
+
name: 'install',
|
|
1654
|
+
description: 'Install an agent from a .cant file or .cantz archive',
|
|
1655
|
+
},
|
|
1656
|
+
args: {
|
|
1657
|
+
path: {
|
|
1658
|
+
type: 'positional',
|
|
1659
|
+
description: 'Path to the .cant file, .cantz archive, or agent directory',
|
|
1660
|
+
required: true,
|
|
1661
|
+
},
|
|
1662
|
+
global: {
|
|
1663
|
+
type: 'boolean',
|
|
1664
|
+
description: 'Install to global tier (~/.local/share/cleo/cant/agents/)',
|
|
1665
|
+
},
|
|
1666
|
+
strict: {
|
|
1667
|
+
type: 'boolean',
|
|
1668
|
+
description: 'Fail on warnings (e.g. unknown skill slugs)',
|
|
1669
|
+
},
|
|
1670
|
+
attach: {
|
|
1671
|
+
type: 'boolean',
|
|
1672
|
+
description: 'Attach to the current project after install',
|
|
1673
|
+
},
|
|
1674
|
+
force: {
|
|
1675
|
+
type: 'boolean',
|
|
1676
|
+
description: 'Overwrite existing agent row / file',
|
|
1677
|
+
},
|
|
1678
|
+
resync: {
|
|
1679
|
+
type: 'boolean',
|
|
1680
|
+
description: 'Drop + reinstall the agents row, keeping the on-disk .cant',
|
|
1681
|
+
},
|
|
1682
|
+
},
|
|
1683
|
+
async run({ args }) {
|
|
1684
|
+
let tempDir = null;
|
|
1685
|
+
try {
|
|
1686
|
+
const { existsSync, mkdirSync, statSync, readdirSync, copyFileSync } = await import('node:fs');
|
|
1687
|
+
const { join, basename, resolve, extname } = await import('node:path');
|
|
1688
|
+
const { tmpdir } = await import('node:os');
|
|
1689
|
+
const resolvedPath = resolve(args.path);
|
|
1690
|
+
if (!existsSync(resolvedPath)) {
|
|
1691
|
+
cliOutput({
|
|
1692
|
+
success: false,
|
|
1693
|
+
error: {
|
|
1694
|
+
code: 'E_NOT_FOUND',
|
|
1695
|
+
message: `Path does not exist: ${resolvedPath}`,
|
|
1696
|
+
},
|
|
1697
|
+
}, { command: 'agent install' });
|
|
1698
|
+
process.exitCode = 4;
|
|
1699
|
+
return;
|
|
1700
|
+
}
|
|
1701
|
+
// Resolve the eventual `.cant` path that will be handed to the install
|
|
1702
|
+
// pipeline. Three input shapes are accepted:
|
|
1703
|
+
// 1. <id>.cant — used as-is.
|
|
1704
|
+
// 2. <pkg>.cantz — extracted; persona.cant inside is renamed
|
|
1705
|
+
// to `<agentId>.cant` in the temp tree.
|
|
1706
|
+
// 3. <dir>/persona.cant — same flow as .cantz; rename into tmp.
|
|
1707
|
+
let cantPath;
|
|
1708
|
+
const stat = statSync(resolvedPath);
|
|
1709
|
+
const ext = extname(resolvedPath);
|
|
1710
|
+
if (stat.isFile() && ext === '.cant') {
|
|
1711
|
+
cantPath = resolvedPath;
|
|
1712
|
+
}
|
|
1713
|
+
else if (stat.isFile() && ext === '.cantz') {
|
|
1714
|
+
const { execFileSync } = await import('node:child_process');
|
|
1715
|
+
tempDir = join(tmpdir(), `cleo-agent-install-${Date.now()}`);
|
|
1716
|
+
mkdirSync(tempDir, { recursive: true });
|
|
1717
|
+
try {
|
|
1718
|
+
execFileSync('unzip', ['-o', '-q', resolvedPath, '-d', tempDir], {
|
|
1719
|
+
encoding: 'utf-8',
|
|
1720
|
+
timeout: 30_000,
|
|
1721
|
+
});
|
|
1722
|
+
}
|
|
1723
|
+
catch (unzipErr) {
|
|
1724
|
+
cliOutput({
|
|
1725
|
+
success: false,
|
|
1726
|
+
error: {
|
|
1727
|
+
code: 'E_VALIDATION',
|
|
1728
|
+
message: `Failed to extract .cantz archive: ${String(unzipErr)}`,
|
|
1729
|
+
},
|
|
1730
|
+
}, { command: 'agent install' });
|
|
1731
|
+
process.exitCode = 6;
|
|
1732
|
+
return;
|
|
1733
|
+
}
|
|
1734
|
+
const topLevel = readdirSync(tempDir).filter((entry) => statSync(join(tempDir, entry)).isDirectory());
|
|
1735
|
+
if (topLevel.length !== 1) {
|
|
1736
|
+
cliOutput({
|
|
1737
|
+
success: false,
|
|
1738
|
+
error: {
|
|
1739
|
+
code: 'E_VALIDATION',
|
|
1740
|
+
message: `Archive must contain exactly one top-level directory, found ${topLevel.length}`,
|
|
1741
|
+
},
|
|
1742
|
+
}, { command: 'agent install' });
|
|
1743
|
+
process.exitCode = 6;
|
|
1744
|
+
return;
|
|
1745
|
+
}
|
|
1746
|
+
const agentName = topLevel[0];
|
|
1747
|
+
const personaPath = join(tempDir, agentName, 'persona.cant');
|
|
1748
|
+
if (!existsSync(personaPath)) {
|
|
1749
|
+
cliOutput({
|
|
1750
|
+
success: false,
|
|
1751
|
+
error: {
|
|
1752
|
+
code: 'E_VALIDATION',
|
|
1753
|
+
message: `Archive must contain persona.cant: ${personaPath}`,
|
|
1754
|
+
},
|
|
1755
|
+
}, { command: 'agent install' });
|
|
1756
|
+
process.exitCode = 6;
|
|
1757
|
+
return;
|
|
1758
|
+
}
|
|
1759
|
+
// The pipeline requires filename base === agent name; materialize a
|
|
1760
|
+
// renamed copy into the temp dir so the check passes.
|
|
1761
|
+
cantPath = join(tempDir, `${agentName}.cant`);
|
|
1762
|
+
copyFileSync(personaPath, cantPath);
|
|
1763
|
+
}
|
|
1764
|
+
else if (stat.isDirectory()) {
|
|
1765
|
+
const agentName = basename(resolvedPath);
|
|
1766
|
+
const personaPath = join(resolvedPath, 'persona.cant');
|
|
1767
|
+
if (!existsSync(personaPath)) {
|
|
1768
|
+
cliOutput({
|
|
1769
|
+
success: false,
|
|
1770
|
+
error: {
|
|
1771
|
+
code: 'E_VALIDATION',
|
|
1772
|
+
message: `Agent directory must contain persona.cant: ${personaPath}`,
|
|
1773
|
+
},
|
|
1774
|
+
}, { command: 'agent install' });
|
|
1775
|
+
process.exitCode = 6;
|
|
1776
|
+
return;
|
|
1777
|
+
}
|
|
1778
|
+
tempDir = join(tmpdir(), `cleo-agent-install-${Date.now()}`);
|
|
1779
|
+
mkdirSync(tempDir, { recursive: true });
|
|
1780
|
+
cantPath = join(tempDir, `${agentName}.cant`);
|
|
1781
|
+
copyFileSync(personaPath, cantPath);
|
|
1782
|
+
}
|
|
1783
|
+
else {
|
|
1784
|
+
cliOutput({
|
|
1785
|
+
success: false,
|
|
1786
|
+
error: {
|
|
1787
|
+
code: 'E_VALIDATION',
|
|
1788
|
+
message: `Path must be a .cant, .cantz, or agent directory: ${resolvedPath}`,
|
|
1789
|
+
},
|
|
1790
|
+
}, { command: 'agent install' });
|
|
1791
|
+
process.exitCode = 6;
|
|
1792
|
+
return;
|
|
1793
|
+
}
|
|
1794
|
+
// Lazy-import the core facade so test mocks can intercept these symbols.
|
|
1795
|
+
const { ensureGlobalSignaldockDb, getGlobalSignaldockDbPath, installAgentFromCant, attachAgentToProject, } = await import('@cleocode/core/internal');
|
|
1796
|
+
const { DatabaseSync } = (await import('node:sqlite'));
|
|
1797
|
+
ensureGlobalSignaldockDb();
|
|
1798
|
+
const dbPath = getGlobalSignaldockDbPath();
|
|
1799
|
+
const db = new DatabaseSync(dbPath);
|
|
1800
|
+
db.exec('PRAGMA foreign_keys = ON');
|
|
1801
|
+
db.exec('PRAGMA journal_mode = WAL');
|
|
1802
|
+
const isGlobal = args.global === true;
|
|
1803
|
+
const targetTier = isGlobal ? 'global' : 'project';
|
|
1804
|
+
const projectRoot = process.cwd();
|
|
1805
|
+
try {
|
|
1806
|
+
// `--resync`: drop+reinstall the registry row but preserve the
|
|
1807
|
+
// on-disk `.cant`. Implementation: delete the existing row BEFORE
|
|
1808
|
+
// calling the pipeline, then let the pipeline handle it as a fresh
|
|
1809
|
+
// insert. The file copy over itself is still atomic.
|
|
1810
|
+
if (args.resync) {
|
|
1811
|
+
const parsedName = basename(cantPath, '.cant');
|
|
1812
|
+
const row = db.prepare('SELECT id FROM agents WHERE agent_id = ?').get(parsedName);
|
|
1813
|
+
if (row) {
|
|
1814
|
+
db.exec('BEGIN IMMEDIATE TRANSACTION');
|
|
1815
|
+
try {
|
|
1816
|
+
db.prepare('DELETE FROM agent_skills WHERE agent_id = ?').run(row.id);
|
|
1817
|
+
db.prepare('DELETE FROM agents WHERE id = ?').run(row.id);
|
|
1818
|
+
db.exec('COMMIT');
|
|
1819
|
+
}
|
|
1820
|
+
catch (resyncErr) {
|
|
1821
|
+
db.exec('ROLLBACK');
|
|
1822
|
+
throw resyncErr;
|
|
1823
|
+
}
|
|
1824
|
+
}
|
|
1825
|
+
}
|
|
1826
|
+
const result = installAgentFromCant(db, {
|
|
1827
|
+
cantSource: cantPath,
|
|
1828
|
+
targetTier,
|
|
1829
|
+
installedFrom: 'user',
|
|
1830
|
+
projectRoot: targetTier === 'project' ? projectRoot : undefined,
|
|
1831
|
+
force: args.force === true || args.resync === true,
|
|
1832
|
+
});
|
|
1833
|
+
if (args.strict && result.warnings.length > 0) {
|
|
1834
|
+
cliOutput({
|
|
1835
|
+
success: false,
|
|
1836
|
+
error: {
|
|
1837
|
+
code: 'E_VALIDATION',
|
|
1838
|
+
message: `Install produced warnings in --strict mode: ${result.warnings.join('; ')}`,
|
|
1839
|
+
},
|
|
1840
|
+
data: {
|
|
1841
|
+
agentId: result.agentId,
|
|
1842
|
+
tier: result.tier,
|
|
1843
|
+
warnings: result.warnings,
|
|
1844
|
+
},
|
|
1845
|
+
}, { command: 'agent install' });
|
|
1846
|
+
process.exitCode = 6;
|
|
1847
|
+
return;
|
|
1848
|
+
}
|
|
1849
|
+
let attached = false;
|
|
1850
|
+
if (args.attach && targetTier === 'global') {
|
|
1851
|
+
attachAgentToProject(projectRoot, result.agentId);
|
|
1852
|
+
attached = true;
|
|
1853
|
+
}
|
|
1854
|
+
cliOutput({
|
|
1855
|
+
success: true,
|
|
1856
|
+
data: {
|
|
1857
|
+
agentId: result.agentId,
|
|
1858
|
+
tier: result.tier,
|
|
1859
|
+
cantPath: result.cantPath,
|
|
1860
|
+
cantSha256: result.cantSha256,
|
|
1861
|
+
inserted: result.inserted,
|
|
1862
|
+
skillsAttached: result.skillsAttached,
|
|
1863
|
+
warnings: result.warnings,
|
|
1864
|
+
attached,
|
|
1865
|
+
},
|
|
1866
|
+
}, { command: 'agent install' });
|
|
1867
|
+
}
|
|
1868
|
+
finally {
|
|
1869
|
+
db.close();
|
|
1870
|
+
}
|
|
1871
|
+
}
|
|
1872
|
+
catch (err) {
|
|
1873
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
1874
|
+
const code = /E_AGENT_ALREADY_INSTALLED/.test(message)
|
|
1875
|
+
? 'E_AGENT_ALREADY_INSTALLED'
|
|
1876
|
+
: 'E_INSTALL';
|
|
1877
|
+
cliOutput({ success: false, error: { code, message } }, { command: 'agent install' });
|
|
1878
|
+
process.exitCode = 1;
|
|
1879
|
+
}
|
|
1880
|
+
finally {
|
|
1881
|
+
if (tempDir) {
|
|
1882
|
+
try {
|
|
1883
|
+
const { rmSync } = await import('node:fs');
|
|
1884
|
+
rmSync(tempDir, { recursive: true, force: true });
|
|
1885
|
+
}
|
|
1886
|
+
catch {
|
|
1887
|
+
// best-effort cleanup — don't mask the primary error
|
|
1888
|
+
}
|
|
1889
|
+
}
|
|
1890
|
+
}
|
|
1891
|
+
},
|
|
1892
|
+
});
|
|
1893
|
+
/**
|
|
1894
|
+
* cleo agent pack <dir> — package an agent directory into a .cantz ZIP archive.
|
|
1895
|
+
*
|
|
1896
|
+
* Validates that the source directory contains `persona.cant`, then creates a
|
|
1897
|
+
* ZIP archive named `<dirname>.cantz` in the current working directory.
|
|
1898
|
+
*
|
|
1899
|
+
* @task T438 @epic T250
|
|
1900
|
+
* @see docs/specs/CANTZ-PACKAGE-STANDARD.md
|
|
1901
|
+
*/
|
|
1902
|
+
const packCommand = defineCommand({
|
|
1903
|
+
meta: {
|
|
1904
|
+
name: 'pack',
|
|
1905
|
+
description: 'Package an agent directory as a .cantz archive',
|
|
1906
|
+
},
|
|
1907
|
+
args: {
|
|
1908
|
+
dir: {
|
|
1909
|
+
type: 'positional',
|
|
1910
|
+
description: 'Agent directory to package',
|
|
1911
|
+
required: true,
|
|
1912
|
+
},
|
|
1913
|
+
},
|
|
1914
|
+
async run({ args }) {
|
|
1915
|
+
try {
|
|
1916
|
+
const { existsSync, statSync } = await import('node:fs');
|
|
1917
|
+
const { resolve, basename, dirname } = await import('node:path');
|
|
1918
|
+
const { execFileSync } = await import('node:child_process');
|
|
1919
|
+
const resolvedDir = resolve(args.dir);
|
|
1920
|
+
if (!existsSync(resolvedDir) || !statSync(resolvedDir).isDirectory()) {
|
|
1921
|
+
cliOutput({
|
|
1922
|
+
success: false,
|
|
1923
|
+
error: {
|
|
1924
|
+
code: 'E_NOT_FOUND',
|
|
1925
|
+
message: `Directory does not exist: ${resolvedDir}`,
|
|
1926
|
+
},
|
|
1927
|
+
}, { command: 'agent pack' });
|
|
1928
|
+
process.exitCode = 4;
|
|
1929
|
+
return;
|
|
1930
|
+
}
|
|
1931
|
+
// Validate persona.cant exists
|
|
1932
|
+
const { join } = await import('node:path');
|
|
1933
|
+
const personaPath = join(resolvedDir, 'persona.cant');
|
|
1934
|
+
if (!existsSync(personaPath)) {
|
|
1935
|
+
cliOutput({
|
|
1936
|
+
success: false,
|
|
1937
|
+
error: {
|
|
1938
|
+
code: 'E_VALIDATION',
|
|
1939
|
+
message: `Agent directory must contain persona.cant: ${personaPath}`,
|
|
1940
|
+
},
|
|
1941
|
+
}, { command: 'agent pack' });
|
|
1942
|
+
process.exitCode = 6;
|
|
1943
|
+
return;
|
|
1944
|
+
}
|
|
1945
|
+
const agentName = basename(resolvedDir);
|
|
1946
|
+
const archiveName = `${agentName}.cantz`;
|
|
1947
|
+
const archivePath = resolve(archiveName);
|
|
1948
|
+
const parentDir = dirname(resolvedDir);
|
|
1949
|
+
// Create ZIP archive — run zip from parent directory so the
|
|
1950
|
+
// archive contains agentName/ as the top-level directory
|
|
1951
|
+
try {
|
|
1952
|
+
execFileSync('zip', ['-r', archivePath, agentName], {
|
|
1953
|
+
cwd: parentDir,
|
|
1954
|
+
encoding: 'utf-8',
|
|
1955
|
+
timeout: 30000,
|
|
1956
|
+
});
|
|
1957
|
+
}
|
|
1958
|
+
catch (zipErr) {
|
|
1959
|
+
cliOutput({
|
|
1960
|
+
success: false,
|
|
1961
|
+
error: {
|
|
1962
|
+
code: 'E_PACK',
|
|
1963
|
+
message: `Failed to create archive: ${String(zipErr)}`,
|
|
1964
|
+
},
|
|
1965
|
+
}, { command: 'agent pack' });
|
|
1966
|
+
process.exitCode = 1;
|
|
1967
|
+
return;
|
|
1968
|
+
}
|
|
1969
|
+
// Get file count and archive size
|
|
1970
|
+
const archiveStats = statSync(archivePath);
|
|
1971
|
+
const { readdirSync } = await import('node:fs');
|
|
1972
|
+
let fileCount = 0;
|
|
1973
|
+
const countFiles = (dirPath) => {
|
|
1974
|
+
const entries = readdirSync(dirPath, { withFileTypes: true });
|
|
1975
|
+
for (const entry of entries) {
|
|
1976
|
+
if (entry.isFile()) {
|
|
1977
|
+
fileCount++;
|
|
1978
|
+
}
|
|
1979
|
+
else if (entry.isDirectory()) {
|
|
1980
|
+
countFiles(join(dirPath, entry.name));
|
|
1981
|
+
}
|
|
1982
|
+
}
|
|
1983
|
+
};
|
|
1984
|
+
countFiles(resolvedDir);
|
|
1985
|
+
cliOutput({
|
|
1986
|
+
success: true,
|
|
1987
|
+
data: {
|
|
1988
|
+
archive: archivePath,
|
|
1989
|
+
agent: agentName,
|
|
1990
|
+
files: fileCount,
|
|
1991
|
+
size: archiveStats.size,
|
|
1992
|
+
},
|
|
1993
|
+
}, { command: 'agent pack' });
|
|
1994
|
+
}
|
|
1995
|
+
catch (err) {
|
|
1996
|
+
cliOutput({ success: false, error: { code: 'E_PACK', message: String(err) } }, { command: 'agent pack' });
|
|
1997
|
+
process.exitCode = 1;
|
|
1998
|
+
}
|
|
1999
|
+
},
|
|
2000
|
+
});
|
|
2001
|
+
/**
|
|
2002
|
+
* cleo agent create — scaffold a complete agent package.
|
|
2003
|
+
*
|
|
2004
|
+
* Creates a directory structure conforming to the CANTZ package standard
|
|
2005
|
+
* (docs/specs/CANTZ-PACKAGE-STANDARD.md) with role-based persona templates
|
|
2006
|
+
* derived from the starter-bundle canonical agents.
|
|
2007
|
+
*
|
|
2008
|
+
* Template roles:
|
|
2009
|
+
* - **orchestrator**: read-only tools, high tier, dispatch-focused
|
|
2010
|
+
* - **lead**: read-only tools, mid tier, task decomposition
|
|
2011
|
+
* - **worker**: full tool access, mid tier, code execution
|
|
2012
|
+
* - **docs-worker**: documentation-focused worker variant
|
|
2013
|
+
*
|
|
2014
|
+
* @task T439 @epic T250
|
|
2015
|
+
* @see docs/specs/CANTZ-PACKAGE-STANDARD.md
|
|
2016
|
+
* @see packages/cleo-os/starter-bundle/agents/ — canonical format reference
|
|
2017
|
+
*/
|
|
2018
|
+
const createCommand = defineCommand({
|
|
2019
|
+
meta: {
|
|
2020
|
+
name: 'create',
|
|
2021
|
+
description: 'Scaffold a new agent package with persona.cant and manifest.json',
|
|
2022
|
+
},
|
|
2023
|
+
args: {
|
|
2024
|
+
name: {
|
|
2025
|
+
type: 'string',
|
|
2026
|
+
description: 'Agent name (kebab-case)',
|
|
2027
|
+
required: true,
|
|
2028
|
+
},
|
|
2029
|
+
role: {
|
|
2030
|
+
type: 'string',
|
|
2031
|
+
description: 'Agent role: orchestrator, lead, worker, or docs-worker',
|
|
2032
|
+
required: true,
|
|
2033
|
+
},
|
|
2034
|
+
tier: {
|
|
2035
|
+
type: 'string',
|
|
2036
|
+
description: 'Agent tier: low, mid, or high (defaults based on role)',
|
|
2037
|
+
},
|
|
2038
|
+
team: {
|
|
2039
|
+
type: 'string',
|
|
2040
|
+
description: 'Team this agent belongs to',
|
|
2041
|
+
},
|
|
2042
|
+
domain: {
|
|
2043
|
+
type: 'string',
|
|
2044
|
+
description: 'Domain description for file permissions and context',
|
|
2045
|
+
},
|
|
2046
|
+
global: {
|
|
2047
|
+
type: 'boolean',
|
|
2048
|
+
description: 'Create in global tier (~/.local/share/cleo/cant/agents/)',
|
|
2049
|
+
},
|
|
2050
|
+
'seed-brain': {
|
|
2051
|
+
type: 'boolean',
|
|
2052
|
+
description: 'Create expertise/mental-model-seed.md and seed a BRAIN observation',
|
|
2053
|
+
},
|
|
2054
|
+
parent: {
|
|
2055
|
+
type: 'string',
|
|
2056
|
+
description: 'Parent agent name in the hierarchy',
|
|
2057
|
+
},
|
|
2058
|
+
},
|
|
2059
|
+
async run({ args }) {
|
|
2060
|
+
try {
|
|
2061
|
+
const { existsSync, mkdirSync, writeFileSync } = await import('node:fs');
|
|
2062
|
+
const { join } = await import('node:path');
|
|
2063
|
+
const { homedir } = await import('node:os');
|
|
2064
|
+
const name = args.name;
|
|
2065
|
+
const role = args.role;
|
|
2066
|
+
const tier = args.tier ?? inferTierFromRole(role);
|
|
2067
|
+
const team = args.team;
|
|
2068
|
+
const domain = args.domain;
|
|
2069
|
+
const isGlobal = args.global === true;
|
|
2070
|
+
const seedBrain = args['seed-brain'] === true;
|
|
2071
|
+
const parent = args.parent;
|
|
2072
|
+
// Validate role
|
|
2073
|
+
const validRoles = ['orchestrator', 'lead', 'worker', 'docs-worker'];
|
|
2074
|
+
if (!validRoles.includes(role)) {
|
|
2075
|
+
cliOutput({
|
|
2076
|
+
success: false,
|
|
2077
|
+
error: {
|
|
2078
|
+
code: 'E_VALIDATION',
|
|
2079
|
+
message: `Invalid role "${role}". Must be one of: ${validRoles.join(', ')}`,
|
|
2080
|
+
fix: `cleo agent create --name ${name} --role worker`,
|
|
2081
|
+
},
|
|
2082
|
+
}, { command: 'agent create' });
|
|
2083
|
+
process.exitCode = 6;
|
|
2084
|
+
return;
|
|
2085
|
+
}
|
|
2086
|
+
// Validate tier
|
|
2087
|
+
const validTiers = ['low', 'mid', 'high'];
|
|
2088
|
+
if (!validTiers.includes(tier)) {
|
|
2089
|
+
cliOutput({
|
|
2090
|
+
success: false,
|
|
2091
|
+
error: {
|
|
2092
|
+
code: 'E_VALIDATION',
|
|
2093
|
+
message: `Invalid tier "${tier}". Must be one of: ${validTiers.join(', ')}`,
|
|
2094
|
+
fix: `cleo agent create --name ${name} --role ${role} --tier mid`,
|
|
2095
|
+
},
|
|
2096
|
+
}, { command: 'agent create' });
|
|
2097
|
+
process.exitCode = 6;
|
|
2098
|
+
return;
|
|
2099
|
+
}
|
|
2100
|
+
// Validate name is kebab-case
|
|
2101
|
+
if (!/^[a-z][a-z0-9]*(-[a-z0-9]+)*$/.test(name)) {
|
|
2102
|
+
cliOutput({
|
|
2103
|
+
success: false,
|
|
2104
|
+
error: {
|
|
2105
|
+
code: 'E_VALIDATION',
|
|
2106
|
+
message: `Agent name must be kebab-case: "${name}"`,
|
|
2107
|
+
fix: 'Use lowercase letters, numbers, and hyphens. Must start with a letter.',
|
|
2108
|
+
},
|
|
2109
|
+
}, { command: 'agent create' });
|
|
2110
|
+
process.exitCode = 6;
|
|
2111
|
+
return;
|
|
2112
|
+
}
|
|
2113
|
+
// Determine target directory
|
|
2114
|
+
let targetRoot;
|
|
2115
|
+
if (isGlobal) {
|
|
2116
|
+
const home = homedir();
|
|
2117
|
+
const xdgData = process.env['XDG_DATA_HOME'] ?? join(home, '.local', 'share');
|
|
2118
|
+
targetRoot = join(xdgData, 'cleo', 'cant', 'agents');
|
|
2119
|
+
}
|
|
2120
|
+
else {
|
|
2121
|
+
targetRoot = join(process.cwd(), CLEO_DIR_NAME, CANT_AGENTS_SUBDIR);
|
|
2122
|
+
}
|
|
2123
|
+
const agentDir = join(targetRoot, name);
|
|
2124
|
+
// Check if agent directory already exists
|
|
2125
|
+
if (existsSync(agentDir)) {
|
|
2126
|
+
cliOutput({
|
|
2127
|
+
success: false,
|
|
2128
|
+
error: {
|
|
2129
|
+
code: 'E_VALIDATION',
|
|
2130
|
+
message: `Agent directory already exists: ${agentDir}`,
|
|
2131
|
+
fix: 'Remove the existing directory or choose a different name.',
|
|
2132
|
+
},
|
|
2133
|
+
}, { command: 'agent create' });
|
|
2134
|
+
process.exitCode = 6;
|
|
2135
|
+
return;
|
|
2136
|
+
}
|
|
2137
|
+
// Create directory structure
|
|
2138
|
+
mkdirSync(agentDir, { recursive: true });
|
|
2139
|
+
// Generate persona.cant from role template
|
|
2140
|
+
const personaContent = generatePersonaCant({
|
|
2141
|
+
name,
|
|
2142
|
+
role,
|
|
2143
|
+
tier,
|
|
2144
|
+
team,
|
|
2145
|
+
domain,
|
|
2146
|
+
parent,
|
|
2147
|
+
});
|
|
2148
|
+
writeFileSync(join(agentDir, 'persona.cant'), personaContent, 'utf-8');
|
|
2149
|
+
// Generate manifest.json
|
|
2150
|
+
const manifest = generateManifest({ name, role, tier, domain });
|
|
2151
|
+
writeFileSync(join(agentDir, 'manifest.json'), `${JSON.stringify(manifest, null, 2)}\n`, 'utf-8');
|
|
2152
|
+
// Track created files for summary
|
|
2153
|
+
const createdFiles = [
|
|
2154
|
+
join(agentDir, 'persona.cant'),
|
|
2155
|
+
join(agentDir, 'manifest.json'),
|
|
2156
|
+
];
|
|
2157
|
+
// Generate team config if team specified
|
|
2158
|
+
if (team) {
|
|
2159
|
+
const teamConfigContent = generateTeamConfig(name, role, team);
|
|
2160
|
+
writeFileSync(join(agentDir, 'team-config.cant'), teamConfigContent, 'utf-8');
|
|
2161
|
+
createdFiles.push(join(agentDir, 'team-config.cant'));
|
|
2162
|
+
}
|
|
2163
|
+
// Seed brain expertise if requested
|
|
2164
|
+
if (seedBrain) {
|
|
2165
|
+
const expertiseDir = join(agentDir, 'expertise');
|
|
2166
|
+
mkdirSync(expertiseDir, { recursive: true });
|
|
2167
|
+
const seedContent = generateMentalModelSeed(name, role, domain);
|
|
2168
|
+
writeFileSync(join(expertiseDir, 'mental-model-seed.md'), seedContent, 'utf-8');
|
|
2169
|
+
createdFiles.push(join(expertiseDir, 'mental-model-seed.md'));
|
|
2170
|
+
// Best-effort BRAIN observation via CLI
|
|
2171
|
+
try {
|
|
2172
|
+
const { execFile } = await import('node:child_process');
|
|
2173
|
+
const { promisify } = await import('node:util');
|
|
2174
|
+
const execFileAsync = promisify(execFile);
|
|
2175
|
+
await execFileAsync('cleo', [
|
|
2176
|
+
'observe',
|
|
2177
|
+
`Agent ${name} created with role ${role}`,
|
|
2178
|
+
'--title',
|
|
2179
|
+
`Agent creation: ${name}`,
|
|
2180
|
+
], { encoding: 'utf-8', timeout: 10000 }).catch(() => {
|
|
2181
|
+
// Best-effort — do not fail create if observe fails
|
|
2182
|
+
});
|
|
2183
|
+
}
|
|
2184
|
+
catch {
|
|
2185
|
+
// Best-effort — do not fail create if observe fails
|
|
2186
|
+
}
|
|
2187
|
+
}
|
|
2188
|
+
// Best-effort agent registration in signaldock.db
|
|
2189
|
+
let registered = false;
|
|
2190
|
+
try {
|
|
2191
|
+
const { AgentRegistryAccessor, getDb } = await import('@cleocode/core/internal');
|
|
2192
|
+
await getDb();
|
|
2193
|
+
const registry = new AgentRegistryAccessor(process.cwd());
|
|
2194
|
+
const existing = await registry.get(name);
|
|
2195
|
+
if (!existing) {
|
|
2196
|
+
const descMatch = personaContent.match(/description:\s*"([^"]+)"/);
|
|
2197
|
+
const displayName = descMatch?.[1] ?? name;
|
|
2198
|
+
await registry.register({
|
|
2199
|
+
agentId: name,
|
|
2200
|
+
displayName,
|
|
2201
|
+
apiKey: 'local-created',
|
|
2202
|
+
apiBaseUrl: 'local',
|
|
2203
|
+
classification: role,
|
|
2204
|
+
privacyTier: 'private',
|
|
2205
|
+
capabilities: [],
|
|
2206
|
+
skills: [],
|
|
2207
|
+
transportType: 'http',
|
|
2208
|
+
transportConfig: {},
|
|
2209
|
+
isActive: false,
|
|
2210
|
+
});
|
|
2211
|
+
registered = true;
|
|
2212
|
+
}
|
|
2213
|
+
}
|
|
2214
|
+
catch {
|
|
2215
|
+
// Registration is best-effort — do not fail the create
|
|
2216
|
+
}
|
|
2217
|
+
cliOutput({
|
|
2218
|
+
success: true,
|
|
2219
|
+
data: {
|
|
2220
|
+
agent: name,
|
|
2221
|
+
role,
|
|
2222
|
+
tier,
|
|
2223
|
+
directory: agentDir,
|
|
2224
|
+
scope: isGlobal ? 'global' : 'project',
|
|
2225
|
+
files: createdFiles,
|
|
2226
|
+
registered,
|
|
2227
|
+
brainSeeded: seedBrain,
|
|
2228
|
+
},
|
|
2229
|
+
}, { command: 'agent create' });
|
|
2230
|
+
}
|
|
2231
|
+
catch (err) {
|
|
2232
|
+
cliOutput({ success: false, error: { code: 'E_CREATE', message: String(err) } }, { command: 'agent create' });
|
|
2233
|
+
process.exitCode = 1;
|
|
2234
|
+
}
|
|
2235
|
+
},
|
|
2236
|
+
});
|
|
2237
|
+
/**
|
|
2238
|
+
* cleo agent doctor — reconcile `.cant` files on disk vs the registry DB.
|
|
2239
|
+
*
|
|
2240
|
+
* Walks the tier filesystems (global + the current project, if any) against
|
|
2241
|
+
* `signaldock.db:agents` and emits typed D-code findings. With `--repair`
|
|
2242
|
+
* the doctor applies safe, idempotent remediations (delete orphan rows,
|
|
2243
|
+
* refresh drifting SHA-256 digests). Exits non-zero when any
|
|
2244
|
+
* error-severity finding is present so CI can gate on a clean report.
|
|
2245
|
+
*
|
|
2246
|
+
* @task T889 / T901 / W2-7
|
|
2247
|
+
* @epic T889
|
|
2248
|
+
*/
|
|
2249
|
+
/**
|
|
2250
|
+
* cleo agent mint — invoke agent-architect meta-agent to synthesize a project-specific
|
|
2251
|
+
* agent from a .cant spec file and project context.
|
|
2252
|
+
*
|
|
2253
|
+
* Semantic distinction from `cleo agent create`:
|
|
2254
|
+
* - `create` — static scaffold from role templates (no AI synthesis)
|
|
2255
|
+
* - `mint` — meta-agent-driven synthesis from a spec file + project context (AC8)
|
|
2256
|
+
*
|
|
2257
|
+
* @task T1276 v2026.4.127 T1259 E2 cleo agent mint CLI verb
|
|
2258
|
+
*/
|
|
2259
|
+
const mintCommand = defineCommand({
|
|
2260
|
+
meta: {
|
|
2261
|
+
name: 'mint',
|
|
2262
|
+
description: 'Synthesize a project-specific agent from a .cant spec using agent-architect meta-agent',
|
|
2263
|
+
},
|
|
2264
|
+
args: {
|
|
2265
|
+
spec: {
|
|
2266
|
+
type: 'positional',
|
|
2267
|
+
description: 'Path to the .cant spec file describing the agent to synthesize',
|
|
2268
|
+
required: true,
|
|
2269
|
+
},
|
|
2270
|
+
'output-dir': {
|
|
2271
|
+
type: 'string',
|
|
2272
|
+
description: 'Directory to write synthesized .cant files (defaults to .cleo/cant/agents/)',
|
|
2273
|
+
},
|
|
2274
|
+
'dry-run': {
|
|
2275
|
+
type: 'boolean',
|
|
2276
|
+
description: 'Preview invocation tokens without invoking agent-architect',
|
|
2277
|
+
default: false,
|
|
2278
|
+
},
|
|
2279
|
+
json: {
|
|
2280
|
+
type: 'boolean',
|
|
2281
|
+
description: 'Emit result as LAFS JSON envelope',
|
|
2282
|
+
default: false,
|
|
2283
|
+
},
|
|
2284
|
+
},
|
|
2285
|
+
async run({ args }) {
|
|
2286
|
+
try {
|
|
2287
|
+
const { existsSync, readFileSync, mkdirSync } = await import('node:fs');
|
|
2288
|
+
const { resolve, join } = await import('node:path');
|
|
2289
|
+
const specPath = resolve(args.spec);
|
|
2290
|
+
if (!existsSync(specPath)) {
|
|
2291
|
+
const errEnv = {
|
|
2292
|
+
success: false,
|
|
2293
|
+
error: { code: 'E_NOT_FOUND', message: `spec file not found: ${specPath}` },
|
|
2294
|
+
meta: { operation: 'agent.mint', timestamp: new Date().toISOString() },
|
|
2295
|
+
};
|
|
2296
|
+
if (args.json) {
|
|
2297
|
+
process.stdout.write(JSON.stringify(errEnv, null, 2) + '\n');
|
|
2298
|
+
}
|
|
2299
|
+
else {
|
|
2300
|
+
process.stderr.write(`error: spec file not found: ${specPath}\n`);
|
|
2301
|
+
}
|
|
2302
|
+
process.exitCode = 4;
|
|
2303
|
+
return;
|
|
2304
|
+
}
|
|
2305
|
+
const specContent = readFileSync(specPath, 'utf-8');
|
|
2306
|
+
const projectRoot = process.cwd();
|
|
2307
|
+
const outputDir = args['output-dir']
|
|
2308
|
+
? resolve(args['output-dir'])
|
|
2309
|
+
: join(projectRoot, '.cleo', 'cant', 'agents');
|
|
2310
|
+
mkdirSync(outputDir, { recursive: true });
|
|
2311
|
+
if (args['dry-run']) {
|
|
2312
|
+
const preview = {
|
|
2313
|
+
success: true,
|
|
2314
|
+
data: {
|
|
2315
|
+
dryRun: true,
|
|
2316
|
+
agentName: 'agent-architect',
|
|
2317
|
+
specPath,
|
|
2318
|
+
outputDir,
|
|
2319
|
+
projectRoot,
|
|
2320
|
+
message: 'Dry-run: would invoke agent-architect with the above tokens',
|
|
2321
|
+
},
|
|
2322
|
+
meta: { operation: 'agent.mint', timestamp: new Date().toISOString() },
|
|
2323
|
+
};
|
|
2324
|
+
process.stdout.write(JSON.stringify(preview, null, 2) + '\n');
|
|
2325
|
+
return;
|
|
2326
|
+
}
|
|
2327
|
+
const { invokeMetaAgent } = await import('@cleocode/core/agents/invoke-meta-agent');
|
|
2328
|
+
const result = await invokeMetaAgent({
|
|
2329
|
+
agentName: 'agent-architect',
|
|
2330
|
+
projectRoot,
|
|
2331
|
+
tokens: {
|
|
2332
|
+
CANT_AGENTS_DIR: outputDir,
|
|
2333
|
+
// Pass spec content as PROJECT_CONTEXT to let agent-architect read it
|
|
2334
|
+
PROJECT_CONTEXT: specContent,
|
|
2335
|
+
},
|
|
2336
|
+
});
|
|
2337
|
+
if (result.invoked) {
|
|
2338
|
+
const envelope = {
|
|
2339
|
+
success: true,
|
|
2340
|
+
data: {
|
|
2341
|
+
invoked: true,
|
|
2342
|
+
outputs: result.outputs ?? [],
|
|
2343
|
+
outputDir,
|
|
2344
|
+
message: `agent-architect synthesized ${result.outputs?.length ?? 0} agent(s)`,
|
|
2345
|
+
},
|
|
2346
|
+
meta: { operation: 'agent.mint', timestamp: new Date().toISOString() },
|
|
2347
|
+
};
|
|
2348
|
+
if (args.json) {
|
|
2349
|
+
process.stdout.write(JSON.stringify(envelope, null, 2) + '\n');
|
|
2350
|
+
}
|
|
2351
|
+
else {
|
|
2352
|
+
process.stdout.write(`minted ${result.outputs?.length ?? 0} agent(s) to ${outputDir}\n`);
|
|
2353
|
+
for (const out of result.outputs ?? []) {
|
|
2354
|
+
process.stdout.write(` + ${out}\n`);
|
|
2355
|
+
}
|
|
2356
|
+
}
|
|
2357
|
+
}
|
|
2358
|
+
else {
|
|
2359
|
+
const fallbackMsg = `agent-architect unavailable: ${result.reason ?? 'unknown'}. Run 'cleo agent create' for static scaffolding.`;
|
|
2360
|
+
const envelope = {
|
|
2361
|
+
success: false,
|
|
2362
|
+
error: { code: 'E_META_AGENT_UNAVAILABLE', message: fallbackMsg },
|
|
2363
|
+
meta: { operation: 'agent.mint', timestamp: new Date().toISOString() },
|
|
2364
|
+
};
|
|
2365
|
+
if (args.json) {
|
|
2366
|
+
process.stdout.write(JSON.stringify(envelope, null, 2) + '\n');
|
|
2367
|
+
}
|
|
2368
|
+
else {
|
|
2369
|
+
process.stderr.write(`warn: ${fallbackMsg}\n`);
|
|
2370
|
+
}
|
|
2371
|
+
process.exitCode = 1;
|
|
2372
|
+
}
|
|
2373
|
+
}
|
|
2374
|
+
catch (err) {
|
|
2375
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
2376
|
+
process.stderr.write(`error: agent mint failed: ${message}\n`);
|
|
2377
|
+
process.exitCode = 1;
|
|
2378
|
+
}
|
|
2379
|
+
},
|
|
2380
|
+
});
|
|
2381
|
+
const doctorCommand = defineCommand({
|
|
2382
|
+
meta: {
|
|
2383
|
+
name: 'doctor',
|
|
2384
|
+
description: 'Reconcile .cant files on disk against the registry and report drift',
|
|
2385
|
+
},
|
|
2386
|
+
args: {
|
|
2387
|
+
repair: {
|
|
2388
|
+
type: 'boolean',
|
|
2389
|
+
description: 'Apply safe repairs (D-002 orphan-row delete, D-003 hash refresh)',
|
|
2390
|
+
},
|
|
2391
|
+
'import-legacy-json': {
|
|
2392
|
+
type: 'boolean',
|
|
2393
|
+
description: 'When used with --repair, import a discovered ~/.cleo/agent-registry.json',
|
|
2394
|
+
},
|
|
2395
|
+
'migrate-path': {
|
|
2396
|
+
type: 'boolean',
|
|
2397
|
+
description: 'When used with --repair, migrate legacy .cleo/agents/ rows to .cleo/cant/agents/',
|
|
2398
|
+
},
|
|
2399
|
+
json: {
|
|
2400
|
+
type: 'boolean',
|
|
2401
|
+
description: 'Emit the raw DoctorReport envelope as JSON instead of a human table',
|
|
2402
|
+
},
|
|
2403
|
+
},
|
|
2404
|
+
async run({ args }) {
|
|
2405
|
+
try {
|
|
2406
|
+
const { buildDoctorReport, reconcileDoctor, ensureGlobalSignaldockDb, getGlobalSignaldockDbPath, } = await import('@cleocode/core/internal');
|
|
2407
|
+
const { createRequire } = await import('node:module');
|
|
2408
|
+
const nodeSqlite = createRequire(import.meta.url)('node:sqlite');
|
|
2409
|
+
const { DatabaseSync } = nodeSqlite;
|
|
2410
|
+
await ensureGlobalSignaldockDb();
|
|
2411
|
+
const dbPath = getGlobalSignaldockDbPath();
|
|
2412
|
+
const db = new DatabaseSync(dbPath);
|
|
2413
|
+
db.exec('PRAGMA foreign_keys = ON');
|
|
2414
|
+
try {
|
|
2415
|
+
const report = await buildDoctorReport(db, { projectRoot: process.cwd() });
|
|
2416
|
+
const repairFlag = args.repair === true;
|
|
2417
|
+
let reconciled;
|
|
2418
|
+
if (repairFlag) {
|
|
2419
|
+
reconciled = await reconcileDoctor(db, report.findings, {
|
|
2420
|
+
importLegacyJson: args['import-legacy-json'] === true,
|
|
2421
|
+
allowPathMigration: args['migrate-path'] === true,
|
|
2422
|
+
});
|
|
2423
|
+
}
|
|
2424
|
+
if (args.json === true) {
|
|
2425
|
+
cliOutput({ success: true, data: { report, reconciled: reconciled ?? null } }, { command: 'agent doctor' });
|
|
2426
|
+
}
|
|
2427
|
+
else {
|
|
2428
|
+
const lines = [];
|
|
2429
|
+
if (report.findings.length === 0) {
|
|
2430
|
+
lines.push('No drift detected — registry and filesystem are in sync.');
|
|
2431
|
+
}
|
|
2432
|
+
else {
|
|
2433
|
+
lines.push(`Findings: ${report.summary.error} error(s), ${report.summary.warn} warning(s), ${report.summary.info} info`);
|
|
2434
|
+
lines.push('');
|
|
2435
|
+
for (const f of report.findings) {
|
|
2436
|
+
lines.push(`[${f.code}] ${f.severity.toUpperCase()} ${f.subject} — ${f.message}`);
|
|
2437
|
+
if (f.fixCommand)
|
|
2438
|
+
lines.push(` fix: ${f.fixCommand}`);
|
|
2439
|
+
}
|
|
2440
|
+
if (reconciled) {
|
|
2441
|
+
lines.push('');
|
|
2442
|
+
lines.push(`Repaired: ${reconciled.repaired.length > 0 ? reconciled.repaired.join(', ') : '(none)'}`);
|
|
2443
|
+
lines.push(`Skipped: ${reconciled.skipped.length > 0 ? reconciled.skipped.join(', ') : '(none)'}`);
|
|
2444
|
+
}
|
|
2445
|
+
}
|
|
2446
|
+
cliOutput({
|
|
2447
|
+
success: true,
|
|
2448
|
+
data: {
|
|
2449
|
+
message: lines.join('\n'),
|
|
2450
|
+
summary: report.summary,
|
|
2451
|
+
generatedAt: report.generatedAt,
|
|
2452
|
+
findings: report.findings.length,
|
|
2453
|
+
repaired: reconciled?.repaired.length ?? 0,
|
|
2454
|
+
skipped: reconciled?.skipped.length ?? 0,
|
|
2455
|
+
},
|
|
2456
|
+
}, { command: 'agent doctor' });
|
|
2457
|
+
}
|
|
2458
|
+
if (report.summary.error > 0) {
|
|
2459
|
+
process.exitCode = 1;
|
|
2460
|
+
}
|
|
2461
|
+
}
|
|
2462
|
+
finally {
|
|
2463
|
+
db.close();
|
|
2464
|
+
}
|
|
2465
|
+
}
|
|
2466
|
+
catch (err) {
|
|
2467
|
+
cliOutput({ success: false, error: { code: 'E_DOCTOR', message: String(err) } }, { command: 'agent doctor' });
|
|
2468
|
+
process.exitCode = 1;
|
|
2469
|
+
}
|
|
2470
|
+
},
|
|
2471
|
+
});
|
|
2472
|
+
/**
|
|
2473
|
+
* Root agent command group — agent lifecycle, credentials, and messaging.
|
|
2474
|
+
*
|
|
2475
|
+
* Registers all agent subcommands. See file-level TSDoc for the full
|
|
2476
|
+
* command surface and daemon vs. Pi session architectural distinction.
|
|
2477
|
+
*
|
|
2478
|
+
* @see docs/specs/SIGNALDOCK-UNIFIED-AGENT-REGISTRY.md Section 3.4
|
|
2479
|
+
* @see .cleo/adrs/ADR-035-pi-v2-v3-harness.md §D5
|
|
2480
|
+
* @task T178
|
|
2481
|
+
*/
|
|
2482
|
+
export const agentCommand = defineCommand({
|
|
2483
|
+
meta: { name: 'agent', description: 'Agent lifecycle, credentials, and messaging' },
|
|
2484
|
+
subCommands: {
|
|
2485
|
+
doctor: doctorCommand,
|
|
2486
|
+
register: registerCommand,
|
|
2487
|
+
signin: signinCommand,
|
|
2488
|
+
start: startCommand,
|
|
2489
|
+
stop: stopCommand,
|
|
2490
|
+
status: statusCommand,
|
|
2491
|
+
assign: assignCommand,
|
|
2492
|
+
wake: wakeCommand,
|
|
2493
|
+
spawn: spawnCommand,
|
|
2494
|
+
reassign: reassignCommand,
|
|
2495
|
+
'stop-all': stopAllCommand,
|
|
2496
|
+
work: workCommand,
|
|
2497
|
+
list: listCommand,
|
|
2498
|
+
get: getCommand,
|
|
2499
|
+
attach: attachCommand,
|
|
2500
|
+
detach: detachCommand,
|
|
2501
|
+
remove: removeCommand,
|
|
2502
|
+
'rotate-key': rotateKeyCommand,
|
|
2503
|
+
'claim-code': claimCodeCommand,
|
|
2504
|
+
watch: watchCommand,
|
|
2505
|
+
poll: pollCommand,
|
|
2506
|
+
send: sendCommand,
|
|
2507
|
+
health: healthCommand,
|
|
2508
|
+
install: installCommand,
|
|
2509
|
+
pack: packCommand,
|
|
2510
|
+
create: createCommand,
|
|
2511
|
+
mint: mintCommand,
|
|
2512
|
+
},
|
|
2513
|
+
async run({ cmd, rawArgs }) {
|
|
2514
|
+
const firstArg = rawArgs?.find((a) => !a.startsWith('-'));
|
|
2515
|
+
if (firstArg && cmd.subCommands && firstArg in cmd.subCommands)
|
|
2516
|
+
return;
|
|
2517
|
+
await showUsage(cmd);
|
|
2518
|
+
},
|
|
2519
|
+
});
|
|
2520
|
+
/**
|
|
2521
|
+
* Infer the default tier from the agent role.
|
|
2522
|
+
*
|
|
2523
|
+
* - orchestrator -> high
|
|
2524
|
+
* - lead -> mid
|
|
2525
|
+
* - worker -> mid
|
|
2526
|
+
* - docs-worker -> mid
|
|
2527
|
+
*
|
|
2528
|
+
* @param role - The agent role string.
|
|
2529
|
+
* @returns The inferred tier string.
|
|
2530
|
+
*/
|
|
2531
|
+
function inferTierFromRole(role) {
|
|
2532
|
+
if (role === 'orchestrator')
|
|
2533
|
+
return 'high';
|
|
2534
|
+
return 'mid';
|
|
2535
|
+
}
|
|
2536
|
+
/**
|
|
2537
|
+
* Generate a `persona.cant` file from role-based templates.
|
|
2538
|
+
*
|
|
2539
|
+
* Templates are derived from the canonical starter-bundle agents at
|
|
2540
|
+
* `packages/cleo-os/starter-bundle/agents/`. Each role maps to a
|
|
2541
|
+
* specific set of tools, permissions, context sources, and behavioral
|
|
2542
|
+
* hooks.
|
|
2543
|
+
*
|
|
2544
|
+
* @param params - Agent persona parameters.
|
|
2545
|
+
* @returns The complete persona.cant file content.
|
|
2546
|
+
*
|
|
2547
|
+
* @see packages/cleo-os/starter-bundle/agents/cleo-orchestrator.cant
|
|
2548
|
+
* @see packages/cleo-os/starter-bundle/agents/dev-lead.cant
|
|
2549
|
+
* @see packages/cleo-os/starter-bundle/agents/code-worker.cant
|
|
2550
|
+
* @see packages/cleo-os/starter-bundle/agents/docs-worker.cant
|
|
2551
|
+
*/
|
|
2552
|
+
function generatePersonaCant(params) {
|
|
2553
|
+
const { name, role, tier, team, domain, parent } = params;
|
|
2554
|
+
switch (role) {
|
|
2555
|
+
case 'orchestrator':
|
|
2556
|
+
return generateOrchestratorPersona(name, tier, team, parent);
|
|
2557
|
+
case 'lead':
|
|
2558
|
+
return generateLeadPersona(name, tier, team, domain, parent);
|
|
2559
|
+
case 'worker':
|
|
2560
|
+
return generateWorkerPersona(name, tier, team, domain, parent);
|
|
2561
|
+
case 'docs-worker':
|
|
2562
|
+
return generateDocsWorkerPersona(name, tier, team, domain, parent);
|
|
2563
|
+
default:
|
|
2564
|
+
return generateWorkerPersona(name, tier, team, domain, parent);
|
|
2565
|
+
}
|
|
2566
|
+
}
|
|
2567
|
+
/**
|
|
2568
|
+
* Generate an orchestrator persona.cant.
|
|
2569
|
+
*
|
|
2570
|
+
* Orchestrators coordinate work but do not execute code. They hold
|
|
2571
|
+
* read-only core tools (Read, Grep, Glob) plus dispatch tools for
|
|
2572
|
+
* routing work to leads and workers.
|
|
2573
|
+
*
|
|
2574
|
+
* @param name - Agent name (kebab-case).
|
|
2575
|
+
* @param tier - Agent tier.
|
|
2576
|
+
* @param team - Optional team name.
|
|
2577
|
+
* @param parent - Optional parent agent.
|
|
2578
|
+
* @returns The persona.cant content string.
|
|
2579
|
+
*/
|
|
2580
|
+
function generateOrchestratorPersona(name, tier, team, parent) {
|
|
2581
|
+
const parentLine = parent ? `\n parent: ${parent}` : '';
|
|
2582
|
+
const teamComment = team ? `\n# Team: ${team}` : '';
|
|
2583
|
+
return `---
|
|
2584
|
+
kind: agent
|
|
2585
|
+
version: "1"
|
|
2586
|
+
---
|
|
2587
|
+
|
|
2588
|
+
# ${name} — orchestrator agent.${teamComment}
|
|
2589
|
+
# Coordinates the team, classifies work, dispatches to leads/workers.
|
|
2590
|
+
|
|
2591
|
+
agent ${name}:
|
|
2592
|
+
role: orchestrator${parentLine}
|
|
2593
|
+
tier: ${tier}
|
|
2594
|
+
description: "Orchestrator agent. Reads task context, classifies work, dispatches to leads, and synthesizes results. Does not execute code — coordinates."
|
|
2595
|
+
consult-when: "Cross-team decisions, scope changes, human-in-the-loop escalation, or when a lead reports a blocking ambiguity"
|
|
2596
|
+
|
|
2597
|
+
context_sources:
|
|
2598
|
+
- source: decisions
|
|
2599
|
+
query: "recent architectural and project decisions"
|
|
2600
|
+
max_entries: 5
|
|
2601
|
+
- source: patterns
|
|
2602
|
+
query: "project conventions and established patterns"
|
|
2603
|
+
max_entries: 3
|
|
2604
|
+
on_overflow: escalate_tier
|
|
2605
|
+
|
|
2606
|
+
mental_model:
|
|
2607
|
+
scope: project
|
|
2608
|
+
max_tokens: 2000
|
|
2609
|
+
on_load:
|
|
2610
|
+
validate: true
|
|
2611
|
+
|
|
2612
|
+
permissions:
|
|
2613
|
+
tasks: read, write
|
|
2614
|
+
session: read, write
|
|
2615
|
+
memory: read, write
|
|
2616
|
+
|
|
2617
|
+
skills:
|
|
2618
|
+
- ct-cleo
|
|
2619
|
+
- ct-task-executor
|
|
2620
|
+
|
|
2621
|
+
tools:
|
|
2622
|
+
core: [Read, Grep, Glob]
|
|
2623
|
+
dispatch: [dispatch_worker, report_to_user]
|
|
2624
|
+
|
|
2625
|
+
on SessionStart:
|
|
2626
|
+
session "Read active tasks and recent decisions to build situational awareness"
|
|
2627
|
+
context: [active-tasks, memory-bridge, recent-decisions]
|
|
2628
|
+
|
|
2629
|
+
on TaskCompleted:
|
|
2630
|
+
if **the completed task unblocks downstream work**:
|
|
2631
|
+
session "Reassess task queue and dispatch next work"
|
|
2632
|
+
`;
|
|
2633
|
+
}
|
|
2634
|
+
/**
|
|
2635
|
+
* Generate a lead persona.cant.
|
|
2636
|
+
*
|
|
2637
|
+
* Leads decide HOW to build and dispatch work to workers. They hold
|
|
2638
|
+
* read-only tools per TEAM-002 / ULTRAPLAN 10.3 — no Edit, Write, or
|
|
2639
|
+
* Bash access.
|
|
2640
|
+
*
|
|
2641
|
+
* @param name - Agent name (kebab-case).
|
|
2642
|
+
* @param tier - Agent tier.
|
|
2643
|
+
* @param team - Optional team name.
|
|
2644
|
+
* @param domain - Optional domain description.
|
|
2645
|
+
* @param parent - Optional parent agent.
|
|
2646
|
+
* @returns The persona.cant content string.
|
|
2647
|
+
*/
|
|
2648
|
+
function generateLeadPersona(name, tier, team, domain, parent) {
|
|
2649
|
+
const parentLine = parent ? `\n parent: ${parent}` : '\n parent: cleo-orchestrator';
|
|
2650
|
+
const teamComment = team ? `\n# Team: ${team}` : '';
|
|
2651
|
+
const domainDesc = domain ? ` Specializes in ${domain}.` : '';
|
|
2652
|
+
return `---
|
|
2653
|
+
kind: agent
|
|
2654
|
+
version: "1"
|
|
2655
|
+
---
|
|
2656
|
+
|
|
2657
|
+
# ${name} — lead agent.${teamComment}
|
|
2658
|
+
# Decomposes tasks, reviews worker output, decides technical approach.
|
|
2659
|
+
# MUST NOT hold Edit/Write/Bash tools (TEAM-002 / ULTRAPLAN 10.3).
|
|
2660
|
+
|
|
2661
|
+
agent ${name}:
|
|
2662
|
+
role: lead${parentLine}
|
|
2663
|
+
tier: ${tier}
|
|
2664
|
+
description: "Development lead.${domainDesc} Decomposes tasks into concrete implementation steps, reviews worker output, and decides technical approach. Does not write code directly."
|
|
2665
|
+
consult-when: "Implementation strategy, code architecture, refactoring direction, task decomposition, or when workers need clarification"
|
|
2666
|
+
|
|
2667
|
+
context_sources:
|
|
2668
|
+
- source: patterns
|
|
2669
|
+
query: "codebase conventions and architecture patterns"
|
|
2670
|
+
max_entries: 5
|
|
2671
|
+
- source: decisions
|
|
2672
|
+
query: "technical decisions affecting implementation"
|
|
2673
|
+
max_entries: 3
|
|
2674
|
+
on_overflow: escalate_tier
|
|
2675
|
+
|
|
2676
|
+
mental_model:
|
|
2677
|
+
scope: project
|
|
2678
|
+
max_tokens: 1000
|
|
2679
|
+
on_load:
|
|
2680
|
+
validate: true
|
|
2681
|
+
|
|
2682
|
+
permissions:
|
|
2683
|
+
files:
|
|
2684
|
+
read: ["**/*"]
|
|
2685
|
+
|
|
2686
|
+
skills:
|
|
2687
|
+
- ct-cleo
|
|
2688
|
+
- ct-dev-workflow
|
|
2689
|
+
- ct-task-executor
|
|
2690
|
+
|
|
2691
|
+
tools:
|
|
2692
|
+
core: [Read, Grep, Glob]
|
|
2693
|
+
dispatch: [dispatch_worker, report_to_orchestrator]
|
|
2694
|
+
|
|
2695
|
+
on SessionStart:
|
|
2696
|
+
session "Review current task assignments and worker availability"
|
|
2697
|
+
context: [active-tasks, memory-bridge]
|
|
2698
|
+
|
|
2699
|
+
on TaskCompleted:
|
|
2700
|
+
if **the completed task introduced new code**:
|
|
2701
|
+
session "Review worker output for quality and completeness before reporting to orchestrator"
|
|
2702
|
+
`;
|
|
2703
|
+
}
|
|
2704
|
+
/**
|
|
2705
|
+
* Generate a worker persona.cant.
|
|
2706
|
+
*
|
|
2707
|
+
* Workers execute code changes within declared file globs. They hold
|
|
2708
|
+
* the full tool set (Read, Edit, Write, Bash, Glob, Grep) and operate
|
|
2709
|
+
* within file permission boundaries derived from the `--domain` flag.
|
|
2710
|
+
*
|
|
2711
|
+
* @param name - Agent name (kebab-case).
|
|
2712
|
+
* @param tier - Agent tier.
|
|
2713
|
+
* @param team - Optional team name.
|
|
2714
|
+
* @param domain - Optional domain description for file permissions.
|
|
2715
|
+
* @param parent - Optional parent agent.
|
|
2716
|
+
* @returns The persona.cant content string.
|
|
2717
|
+
*/
|
|
2718
|
+
function generateWorkerPersona(name, tier, team, domain, parent) {
|
|
2719
|
+
const parentLine = parent ? `\n parent: ${parent}` : '\n parent: dev-lead';
|
|
2720
|
+
const teamComment = team ? `\n# Team: ${team}` : '';
|
|
2721
|
+
const domainDesc = domain ? ` Specializes in ${domain}.` : '';
|
|
2722
|
+
const writeGlobs = deriveWriteGlobs(domain);
|
|
2723
|
+
return `---
|
|
2724
|
+
kind: agent
|
|
2725
|
+
version: "1"
|
|
2726
|
+
---
|
|
2727
|
+
|
|
2728
|
+
# ${name} — worker agent.${teamComment}
|
|
2729
|
+
# Executes code changes within declared file globs.
|
|
2730
|
+
|
|
2731
|
+
agent ${name}:
|
|
2732
|
+
role: worker${parentLine}
|
|
2733
|
+
tier: ${tier}
|
|
2734
|
+
description: "Code worker.${domainDesc} Reads requirements, writes code, runs tests, and validates changes. Operates within declared file permission globs."
|
|
2735
|
+
consult-when: "Writing code, fixing bugs, running tests, formatting, or any file modification task"
|
|
2736
|
+
|
|
2737
|
+
context_sources:
|
|
2738
|
+
- source: patterns
|
|
2739
|
+
query: "coding conventions and testing patterns"
|
|
2740
|
+
max_entries: 5
|
|
2741
|
+
- source: learnings
|
|
2742
|
+
query: "past implementation mistakes and fixes"
|
|
2743
|
+
max_entries: 3
|
|
2744
|
+
on_overflow: escalate_tier
|
|
2745
|
+
|
|
2746
|
+
mental_model:
|
|
2747
|
+
scope: project
|
|
2748
|
+
max_tokens: 1000
|
|
2749
|
+
on_load:
|
|
2750
|
+
validate: true
|
|
2751
|
+
|
|
2752
|
+
permissions:
|
|
2753
|
+
files:
|
|
2754
|
+
write: ${JSON.stringify(writeGlobs)}
|
|
2755
|
+
read: ["**/*"]
|
|
2756
|
+
delete: ${JSON.stringify(writeGlobs)}
|
|
2757
|
+
|
|
2758
|
+
skills:
|
|
2759
|
+
- ct-cleo
|
|
2760
|
+
- ct-dev-workflow
|
|
2761
|
+
- ct-task-executor
|
|
2762
|
+
|
|
2763
|
+
tools:
|
|
2764
|
+
core: [Read, Edit, Write, Bash, Glob, Grep]
|
|
2765
|
+
|
|
2766
|
+
on SessionStart:
|
|
2767
|
+
session "Check assigned task and read relevant source files before starting work"
|
|
2768
|
+
context: [active-tasks, memory-bridge]
|
|
2769
|
+
|
|
2770
|
+
on PostToolUse:
|
|
2771
|
+
if tool.name == "Write" or tool.name == "Edit":
|
|
2772
|
+
session "Verify the change compiles and passes lint before proceeding"
|
|
2773
|
+
`;
|
|
2774
|
+
}
|
|
2775
|
+
/**
|
|
2776
|
+
* Generate a docs-worker persona.cant.
|
|
2777
|
+
*
|
|
2778
|
+
* Documentation workers write and maintain documentation within declared
|
|
2779
|
+
* documentation file globs. They carry documentation-specific skills and
|
|
2780
|
+
* context sources.
|
|
2781
|
+
*
|
|
2782
|
+
* @param name - Agent name (kebab-case).
|
|
2783
|
+
* @param tier - Agent tier.
|
|
2784
|
+
* @param team - Optional team name.
|
|
2785
|
+
* @param domain - Optional domain description.
|
|
2786
|
+
* @param parent - Optional parent agent.
|
|
2787
|
+
* @returns The persona.cant content string.
|
|
2788
|
+
*/
|
|
2789
|
+
function generateDocsWorkerPersona(name, tier, team, domain, parent) {
|
|
2790
|
+
const parentLine = parent ? `\n parent: ${parent}` : '\n parent: dev-lead';
|
|
2791
|
+
const teamComment = team ? `\n# Team: ${team}` : '';
|
|
2792
|
+
const domainDesc = domain ? ` Specializes in ${domain} documentation.` : '';
|
|
2793
|
+
return `---
|
|
2794
|
+
kind: agent
|
|
2795
|
+
version: "1"
|
|
2796
|
+
---
|
|
2797
|
+
|
|
2798
|
+
# ${name} — documentation worker agent.${teamComment}
|
|
2799
|
+
# Writes and maintains documentation within declared globs.
|
|
2800
|
+
|
|
2801
|
+
agent ${name}:
|
|
2802
|
+
role: worker${parentLine}
|
|
2803
|
+
tier: ${tier}
|
|
2804
|
+
description: "Documentation worker.${domainDesc} Writes READMEs, updates guides, adds TSDoc comments, and maintains project documentation. Operates within declared documentation file globs."
|
|
2805
|
+
consult-when: "Writing documentation, updating READMEs, adding TSDoc comments, or improving existing docs"
|
|
2806
|
+
|
|
2807
|
+
context_sources:
|
|
2808
|
+
- source: patterns
|
|
2809
|
+
query: "documentation conventions and style patterns"
|
|
2810
|
+
max_entries: 3
|
|
2811
|
+
- source: decisions
|
|
2812
|
+
query: "architectural decisions needing documentation"
|
|
2813
|
+
max_entries: 3
|
|
2814
|
+
on_overflow: escalate_tier
|
|
2815
|
+
|
|
2816
|
+
mental_model:
|
|
2817
|
+
scope: project
|
|
2818
|
+
max_tokens: 1000
|
|
2819
|
+
on_load:
|
|
2820
|
+
validate: true
|
|
2821
|
+
|
|
2822
|
+
permissions:
|
|
2823
|
+
files:
|
|
2824
|
+
write: ["docs/**", "**/*.md", "**/*.mdx"]
|
|
2825
|
+
read: ["**/*"]
|
|
2826
|
+
delete: ["docs/**"]
|
|
2827
|
+
|
|
2828
|
+
skills:
|
|
2829
|
+
- ct-cleo
|
|
2830
|
+
- ct-documentor
|
|
2831
|
+
- ct-docs-write
|
|
2832
|
+
|
|
2833
|
+
tools:
|
|
2834
|
+
core: [Read, Edit, Write, Bash, Glob, Grep]
|
|
2835
|
+
|
|
2836
|
+
on SessionStart:
|
|
2837
|
+
session "Check assigned documentation task and review existing docs for context"
|
|
2838
|
+
context: [active-tasks, memory-bridge]
|
|
2839
|
+
|
|
2840
|
+
on PostToolUse:
|
|
2841
|
+
if tool.name == "Write" or tool.name == "Edit":
|
|
2842
|
+
session "Verify markdown renders correctly and follows project style conventions"
|
|
2843
|
+
`;
|
|
2844
|
+
}
|
|
2845
|
+
/**
|
|
2846
|
+
* Derive file write globs from a domain description string.
|
|
2847
|
+
*
|
|
2848
|
+
* Maps common domain keywords to appropriate file glob patterns.
|
|
2849
|
+
* Falls back to the default `["src/**", "packages/**"]` when no
|
|
2850
|
+
* domain is specified or no keywords match.
|
|
2851
|
+
*
|
|
2852
|
+
* @param domain - Optional domain description string.
|
|
2853
|
+
* @returns Array of glob pattern strings for file write permissions.
|
|
2854
|
+
*/
|
|
2855
|
+
function deriveWriteGlobs(domain) {
|
|
2856
|
+
const defaults = ['src/**', 'packages/**', 'lib/**', 'test/**', 'tests/**'];
|
|
2857
|
+
if (!domain)
|
|
2858
|
+
return defaults;
|
|
2859
|
+
const lower = domain.toLowerCase();
|
|
2860
|
+
// Domain-specific glob mappings
|
|
2861
|
+
if (lower.includes('frontend') || lower.includes('ui') || lower.includes('component')) {
|
|
2862
|
+
return ['src/**', 'packages/**', 'components/**', 'styles/**', 'public/**', 'test/**'];
|
|
2863
|
+
}
|
|
2864
|
+
if (lower.includes('backend') || lower.includes('api') || lower.includes('server')) {
|
|
2865
|
+
return ['src/**', 'packages/**', 'lib/**', 'api/**', 'test/**', 'tests/**'];
|
|
2866
|
+
}
|
|
2867
|
+
if (lower.includes('infra') || lower.includes('deploy') || lower.includes('ci')) {
|
|
2868
|
+
return ['.github/**', 'infra/**', 'deploy/**', 'scripts/**', 'Dockerfile*'];
|
|
2869
|
+
}
|
|
2870
|
+
if (lower.includes('test') || lower.includes('qa') || lower.includes('quality')) {
|
|
2871
|
+
return ['test/**', 'tests/**', 'src/**/*.test.*', 'src/**/*.spec.*', 'packages/**/*.test.*'];
|
|
2872
|
+
}
|
|
2873
|
+
if (lower.includes('rust') || lower.includes('crate')) {
|
|
2874
|
+
return ['crates/**', 'src/**', 'Cargo.toml', 'test/**'];
|
|
2875
|
+
}
|
|
2876
|
+
if (lower.includes('doc')) {
|
|
2877
|
+
return ['docs/**', '**/*.md', '**/*.mdx'];
|
|
2878
|
+
}
|
|
2879
|
+
return defaults;
|
|
2880
|
+
}
|
|
2881
|
+
/**
|
|
2882
|
+
* Generate a `manifest.json` object for the agent package.
|
|
2883
|
+
*
|
|
2884
|
+
* Conforms to the CANTZ-PACKAGE-STANDARD.md Section 2.3 schema.
|
|
2885
|
+
*
|
|
2886
|
+
* @param params - Manifest generation parameters.
|
|
2887
|
+
* @returns A plain object ready for JSON serialization.
|
|
2888
|
+
*/
|
|
2889
|
+
function generateManifest(params) {
|
|
2890
|
+
return {
|
|
2891
|
+
name: params.name,
|
|
2892
|
+
version: '1.0.0',
|
|
2893
|
+
description: `${capitalizeFirst(params.role)} agent${params.domain ? ` for ${params.domain}` : ''}`,
|
|
2894
|
+
cant: {
|
|
2895
|
+
minVersion: '1',
|
|
2896
|
+
tier: params.tier,
|
|
2897
|
+
role: params.role === 'docs-worker' ? 'worker' : params.role,
|
|
2898
|
+
},
|
|
2899
|
+
createdAt: new Date().toISOString(),
|
|
2900
|
+
};
|
|
2901
|
+
}
|
|
2902
|
+
/**
|
|
2903
|
+
* Generate a team configuration CANT file fragment.
|
|
2904
|
+
*
|
|
2905
|
+
* Creates a minimal team-config.cant that declares the agent's
|
|
2906
|
+
* membership in a named team.
|
|
2907
|
+
*
|
|
2908
|
+
* @param name - Agent name.
|
|
2909
|
+
* @param role - Agent role.
|
|
2910
|
+
* @param team - Team name.
|
|
2911
|
+
* @returns The team-config.cant content string.
|
|
2912
|
+
*/
|
|
2913
|
+
function generateTeamConfig(name, role, team) {
|
|
2914
|
+
return `---
|
|
2915
|
+
kind: team-config
|
|
2916
|
+
version: "1"
|
|
2917
|
+
---
|
|
2918
|
+
|
|
2919
|
+
# Team membership for ${name}
|
|
2920
|
+
|
|
2921
|
+
team ${team}:
|
|
2922
|
+
member ${name}:
|
|
2923
|
+
role: ${role}
|
|
2924
|
+
status: active
|
|
2925
|
+
`;
|
|
2926
|
+
}
|
|
2927
|
+
/**
|
|
2928
|
+
* Generate a mental model seed markdown file.
|
|
2929
|
+
*
|
|
2930
|
+
* Creates an initial expertise document with placeholder sections
|
|
2931
|
+
* for the agent to populate as it learns about the project domain.
|
|
2932
|
+
*
|
|
2933
|
+
* @param name - Agent name.
|
|
2934
|
+
* @param role - Agent role.
|
|
2935
|
+
* @param domain - Optional domain description.
|
|
2936
|
+
* @returns The mental-model-seed.md content string.
|
|
2937
|
+
*/
|
|
2938
|
+
function generateMentalModelSeed(name, role, domain) {
|
|
2939
|
+
const domainSection = domain
|
|
2940
|
+
? `## Domain\n\n${domain}\n`
|
|
2941
|
+
: `## Domain\n\nTODO: Describe the domain this agent specializes in.\n`;
|
|
2942
|
+
return `# Mental Model Seed: ${name}
|
|
2943
|
+
|
|
2944
|
+
> Auto-generated at ${new Date().toISOString()}
|
|
2945
|
+
> Role: ${role}
|
|
2946
|
+
|
|
2947
|
+
${domainSection}
|
|
2948
|
+
## Key Patterns
|
|
2949
|
+
|
|
2950
|
+
TODO: Document recurring patterns this agent should recognize.
|
|
2951
|
+
|
|
2952
|
+
## Known Pitfalls
|
|
2953
|
+
|
|
2954
|
+
TODO: Document common mistakes or anti-patterns in this domain.
|
|
2955
|
+
|
|
2956
|
+
## Decision History
|
|
2957
|
+
|
|
2958
|
+
TODO: Track important decisions and their rationale.
|
|
2959
|
+
|
|
2960
|
+
## Learning Log
|
|
2961
|
+
|
|
2962
|
+
TODO: Record discoveries and insights as the agent operates.
|
|
2963
|
+
`;
|
|
2964
|
+
}
|
|
2965
|
+
/**
|
|
2966
|
+
* Capitalize the first letter of a string.
|
|
2967
|
+
*
|
|
2968
|
+
* @param str - Input string.
|
|
2969
|
+
* @returns String with the first character uppercased.
|
|
2970
|
+
*/
|
|
2971
|
+
function capitalizeFirst(str) {
|
|
2972
|
+
if (str.length === 0)
|
|
2973
|
+
return str;
|
|
2974
|
+
return str.charAt(0).toUpperCase() + str.slice(1);
|
|
2975
|
+
}
|
|
2976
|
+
//# sourceMappingURL=agent.js.map
|