@codyswann/lisa 4.80.2 → 4.81.0
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/all/copy-contents/gitignore +5 -0
- package/dist/cli/doctor-session-manager-trust.d.ts +62 -0
- package/dist/cli/doctor-session-manager-trust.d.ts.map +1 -0
- package/dist/cli/doctor-session-manager-trust.js +174 -0
- package/dist/cli/doctor-session-manager-trust.js.map +1 -0
- package/dist/cli/doctor-session-manager.d.ts +35 -0
- package/dist/cli/doctor-session-manager.d.ts.map +1 -0
- package/dist/cli/doctor-session-manager.js +209 -0
- package/dist/cli/doctor-session-manager.js.map +1 -0
- package/dist/cli/doctor.d.ts.map +1 -1
- package/dist/cli/doctor.js +3 -0
- package/dist/cli/doctor.js.map +1 -1
- package/dist/cli/lisa-test-run-bootstrap.js +28 -0
- package/dist/cli/lisa-test-run-bootstrap.js.map +1 -1
- package/dist/cli/session-manager-config-merge.d.ts +25 -0
- package/dist/cli/session-manager-config-merge.d.ts.map +1 -0
- package/dist/cli/session-manager-config-merge.js +54 -0
- package/dist/cli/session-manager-config-merge.js.map +1 -0
- package/dist/core/nightly-e2e-guard-behavior-certificate.js +2 -2
- package/dist/core/upstream-evidence-manifest.d.ts.map +1 -1
- package/dist/core/upstream-evidence-manifest.js +257 -12
- package/dist/core/upstream-evidence-manifest.js.map +1 -1
- package/package.json +4 -4
- package/plugins/lisa/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa/.codex-plugin/skills/lisa-automation-status/SKILL.md +10 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-implement/SKILL.md +17 -2
- package/plugins/lisa/.codex-plugin/skills/lisa-secrets-access/scripts/validate-config.mjs +377 -5
- package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/SKILL.md +287 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/agents/openai.yaml +4 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/references/runbook-template.md +66 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/capacity.mjs +186 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/cli-cycle.mjs +262 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/cli-manager.mjs +308 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/cli-queue.mjs +148 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/cli-runtime.mjs +326 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/cli-usage.mjs +114 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/cli-workers.mjs +295 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/config.mjs +265 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/isolation.mjs +130 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/launch.mjs +234 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/ledger.mjs +445 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/reconcile.mjs +134 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/session-manager.mjs +274 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/state-dir.mjs +112 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/telegram-route.mjs +210 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/token.mjs +191 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/worker-launch.mjs +92 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/workers.mjs +406 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-setup-session-manager/SKILL.md +158 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-setup-session-manager/agents/openai.yaml +4 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-tear-down-automations/SKILL.md +10 -0
- package/plugins/lisa/commands/implement.md +2 -2
- package/plugins/lisa/commands/session-manager.md +7 -0
- package/plugins/lisa/commands/setup/session-manager.md +6 -0
- package/plugins/lisa/rules/reference/automation-runbook-contract.md +14 -0
- package/plugins/lisa/rules/reference/config-resolution.md +58 -0
- package/plugins/lisa/scripts/automation-status-claude-adapter.mjs +4 -1
- package/plugins/lisa/scripts/automation-status-codex-adapter.mjs +4 -1
- package/plugins/lisa/scripts/automation-status-expected-fleet.mjs +98 -0
- package/plugins/lisa/scripts/automation-status-run-history.mjs +29 -7
- package/plugins/lisa/scripts/ready-queue-order.mjs +352 -0
- package/plugins/lisa/skills/lisa-automation-status/SKILL.md +10 -0
- package/plugins/lisa/skills/lisa-implement/SKILL.md +17 -2
- package/plugins/lisa/skills/lisa-secrets-access/scripts/validate-config.mjs +377 -5
- package/plugins/lisa/skills/lisa-session-manager/SKILL.md +285 -0
- package/plugins/lisa/skills/lisa-session-manager/agents/openai.yaml +4 -0
- package/plugins/lisa/skills/lisa-session-manager/references/runbook-template.md +66 -0
- package/plugins/lisa/skills/lisa-session-manager/scripts/capacity.mjs +186 -0
- package/plugins/lisa/skills/lisa-session-manager/scripts/cli-cycle.mjs +262 -0
- package/plugins/lisa/skills/lisa-session-manager/scripts/cli-manager.mjs +308 -0
- package/plugins/lisa/skills/lisa-session-manager/scripts/cli-queue.mjs +148 -0
- package/plugins/lisa/skills/lisa-session-manager/scripts/cli-runtime.mjs +326 -0
- package/plugins/lisa/skills/lisa-session-manager/scripts/cli-usage.mjs +114 -0
- package/plugins/lisa/skills/lisa-session-manager/scripts/cli-workers.mjs +295 -0
- package/plugins/lisa/skills/lisa-session-manager/scripts/config.mjs +265 -0
- package/plugins/lisa/skills/lisa-session-manager/scripts/isolation.mjs +130 -0
- package/plugins/lisa/skills/lisa-session-manager/scripts/launch.mjs +234 -0
- package/plugins/lisa/skills/lisa-session-manager/scripts/ledger.mjs +445 -0
- package/plugins/lisa/skills/lisa-session-manager/scripts/reconcile.mjs +134 -0
- package/plugins/lisa/skills/lisa-session-manager/scripts/session-manager.mjs +274 -0
- package/plugins/lisa/skills/lisa-session-manager/scripts/state-dir.mjs +112 -0
- package/plugins/lisa/skills/lisa-session-manager/scripts/telegram-route.mjs +210 -0
- package/plugins/lisa/skills/lisa-session-manager/scripts/token.mjs +191 -0
- package/plugins/lisa/skills/lisa-session-manager/scripts/worker-launch.mjs +92 -0
- package/plugins/lisa/skills/lisa-session-manager/scripts/workers.mjs +406 -0
- package/plugins/lisa/skills/lisa-setup-session-manager/SKILL.md +156 -0
- package/plugins/lisa/skills/lisa-setup-session-manager/agents/openai.yaml +4 -0
- package/plugins/lisa/skills/lisa-tear-down-automations/SKILL.md +10 -0
- package/plugins/lisa-agy/commands/lisa/implement.md +2 -2
- package/plugins/lisa-agy/commands/lisa/session-manager.md +7 -0
- package/plugins/lisa-agy/commands/lisa/setup/session-manager.md +6 -0
- package/plugins/lisa-agy/plugin.json +1 -1
- package/plugins/lisa-agy/scripts/automation-status-claude-adapter.mjs +4 -1
- package/plugins/lisa-agy/scripts/automation-status-codex-adapter.mjs +4 -1
- package/plugins/lisa-agy/scripts/automation-status-expected-fleet.mjs +98 -0
- package/plugins/lisa-agy/scripts/automation-status-run-history.mjs +29 -7
- package/plugins/lisa-agy/scripts/ready-queue-order.mjs +352 -0
- package/plugins/lisa-agy/skills/lisa-automation-status/SKILL.md +10 -0
- package/plugins/lisa-agy/skills/lisa-implement/SKILL.md +17 -2
- package/plugins/lisa-agy/skills/lisa-secrets-access/scripts/validate-config.mjs +377 -5
- package/plugins/lisa-agy/skills/lisa-session-manager/SKILL.md +285 -0
- package/plugins/lisa-agy/skills/lisa-session-manager/references/runbook-template.md +66 -0
- package/plugins/lisa-agy/skills/lisa-session-manager/scripts/capacity.mjs +186 -0
- package/plugins/lisa-agy/skills/lisa-session-manager/scripts/cli-cycle.mjs +262 -0
- package/plugins/lisa-agy/skills/lisa-session-manager/scripts/cli-manager.mjs +308 -0
- package/plugins/lisa-agy/skills/lisa-session-manager/scripts/cli-queue.mjs +148 -0
- package/plugins/lisa-agy/skills/lisa-session-manager/scripts/cli-runtime.mjs +326 -0
- package/plugins/lisa-agy/skills/lisa-session-manager/scripts/cli-usage.mjs +114 -0
- package/plugins/lisa-agy/skills/lisa-session-manager/scripts/cli-workers.mjs +295 -0
- package/plugins/lisa-agy/skills/lisa-session-manager/scripts/config.mjs +265 -0
- package/plugins/lisa-agy/skills/lisa-session-manager/scripts/isolation.mjs +130 -0
- package/plugins/lisa-agy/skills/lisa-session-manager/scripts/launch.mjs +234 -0
- package/plugins/lisa-agy/skills/lisa-session-manager/scripts/ledger.mjs +445 -0
- package/plugins/lisa-agy/skills/lisa-session-manager/scripts/reconcile.mjs +134 -0
- package/plugins/lisa-agy/skills/lisa-session-manager/scripts/session-manager.mjs +274 -0
- package/plugins/lisa-agy/skills/lisa-session-manager/scripts/state-dir.mjs +112 -0
- package/plugins/lisa-agy/skills/lisa-session-manager/scripts/telegram-route.mjs +210 -0
- package/plugins/lisa-agy/skills/lisa-session-manager/scripts/token.mjs +191 -0
- package/plugins/lisa-agy/skills/lisa-session-manager/scripts/worker-launch.mjs +92 -0
- package/plugins/lisa-agy/skills/lisa-session-manager/scripts/workers.mjs +406 -0
- package/plugins/lisa-agy/skills/lisa-setup-session-manager/SKILL.md +156 -0
- package/plugins/lisa-agy/skills/lisa-tear-down-automations/SKILL.md +10 -0
- package/plugins/lisa-cdk/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cdk/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-cdk-agy/plugin.json +1 -1
- package/plugins/lisa-cdk-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cdk-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cdk-devin/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cdk-devin/.devin-plugin/plugin.json +1 -1
- package/plugins/lisa-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-copilot/commands/lisa/implement.md +2 -2
- package/plugins/lisa-copilot/commands/lisa/session-manager.md +7 -0
- package/plugins/lisa-copilot/commands/lisa/setup/session-manager.md +6 -0
- package/plugins/lisa-copilot/rules/reference/automation-runbook-contract.md +14 -0
- package/plugins/lisa-copilot/rules/reference/config-resolution.md +58 -0
- package/plugins/lisa-copilot/scripts/automation-status-claude-adapter.mjs +4 -1
- package/plugins/lisa-copilot/scripts/automation-status-codex-adapter.mjs +4 -1
- package/plugins/lisa-copilot/scripts/automation-status-expected-fleet.mjs +98 -0
- package/plugins/lisa-copilot/scripts/automation-status-run-history.mjs +29 -7
- package/plugins/lisa-copilot/scripts/ready-queue-order.mjs +352 -0
- package/plugins/lisa-copilot/skills/lisa-automation-status/SKILL.md +10 -0
- package/plugins/lisa-copilot/skills/lisa-implement/SKILL.md +17 -2
- package/plugins/lisa-copilot/skills/lisa-secrets-access/scripts/validate-config.mjs +377 -5
- package/plugins/lisa-copilot/skills/lisa-session-manager/SKILL.md +285 -0
- package/plugins/lisa-copilot/skills/lisa-session-manager/references/runbook-template.md +66 -0
- package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/capacity.mjs +186 -0
- package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/cli-cycle.mjs +262 -0
- package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/cli-manager.mjs +308 -0
- package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/cli-queue.mjs +148 -0
- package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/cli-runtime.mjs +326 -0
- package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/cli-usage.mjs +114 -0
- package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/cli-workers.mjs +295 -0
- package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/config.mjs +265 -0
- package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/isolation.mjs +130 -0
- package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/launch.mjs +234 -0
- package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/ledger.mjs +445 -0
- package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/reconcile.mjs +134 -0
- package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/session-manager.mjs +274 -0
- package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/state-dir.mjs +112 -0
- package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/telegram-route.mjs +210 -0
- package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/token.mjs +191 -0
- package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/worker-launch.mjs +92 -0
- package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/workers.mjs +406 -0
- package/plugins/lisa-copilot/skills/lisa-setup-session-manager/SKILL.md +156 -0
- package/plugins/lisa-copilot/skills/lisa-tear-down-automations/SKILL.md +10 -0
- package/plugins/lisa-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cursor/commands/lisa/implement.md +2 -2
- package/plugins/lisa-cursor/commands/lisa/session-manager.md +7 -0
- package/plugins/lisa-cursor/commands/lisa/setup/session-manager.md +6 -0
- package/plugins/lisa-cursor/rules/automation-runbook-contract-reference.mdc +14 -0
- package/plugins/lisa-cursor/rules/config-resolution-reference.mdc +58 -0
- package/plugins/lisa-cursor/scripts/automation-status-claude-adapter.mjs +4 -1
- package/plugins/lisa-cursor/scripts/automation-status-codex-adapter.mjs +4 -1
- package/plugins/lisa-cursor/scripts/automation-status-expected-fleet.mjs +98 -0
- package/plugins/lisa-cursor/scripts/automation-status-run-history.mjs +29 -7
- package/plugins/lisa-cursor/scripts/ready-queue-order.mjs +352 -0
- package/plugins/lisa-cursor/skills/lisa-automation-status/SKILL.md +10 -0
- package/plugins/lisa-cursor/skills/lisa-implement/SKILL.md +17 -2
- package/plugins/lisa-cursor/skills/lisa-secrets-access/scripts/validate-config.mjs +377 -5
- package/plugins/lisa-cursor/skills/lisa-session-manager/SKILL.md +285 -0
- package/plugins/lisa-cursor/skills/lisa-session-manager/references/runbook-template.md +66 -0
- package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/capacity.mjs +186 -0
- package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/cli-cycle.mjs +262 -0
- package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/cli-manager.mjs +308 -0
- package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/cli-queue.mjs +148 -0
- package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/cli-runtime.mjs +326 -0
- package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/cli-usage.mjs +114 -0
- package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/cli-workers.mjs +295 -0
- package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/config.mjs +265 -0
- package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/isolation.mjs +130 -0
- package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/launch.mjs +234 -0
- package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/ledger.mjs +445 -0
- package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/reconcile.mjs +134 -0
- package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/session-manager.mjs +274 -0
- package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/state-dir.mjs +112 -0
- package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/telegram-route.mjs +210 -0
- package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/token.mjs +191 -0
- package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/worker-launch.mjs +92 -0
- package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/workers.mjs +406 -0
- package/plugins/lisa-cursor/skills/lisa-setup-session-manager/SKILL.md +156 -0
- package/plugins/lisa-cursor/skills/lisa-tear-down-automations/SKILL.md +10 -0
- package/plugins/lisa-devin/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-devin/.devin-plugin/plugin.json +1 -1
- package/plugins/lisa-devin/rules/reference/automation-runbook-contract.md +14 -0
- package/plugins/lisa-devin/rules/reference/config-resolution.md +58 -0
- package/plugins/lisa-devin/scripts/automation-status-claude-adapter.mjs +4 -1
- package/plugins/lisa-devin/scripts/automation-status-codex-adapter.mjs +4 -1
- package/plugins/lisa-devin/scripts/automation-status-expected-fleet.mjs +98 -0
- package/plugins/lisa-devin/scripts/automation-status-run-history.mjs +29 -7
- package/plugins/lisa-devin/scripts/ready-queue-order.mjs +352 -0
- package/plugins/lisa-devin/skills/lisa-automation-status/SKILL.md +10 -0
- package/plugins/lisa-devin/skills/lisa-implement/SKILL.md +17 -2
- package/plugins/lisa-devin/skills/lisa-secrets-access/scripts/validate-config.mjs +377 -5
- package/plugins/lisa-devin/skills/lisa-session-manager/SKILL.md +285 -0
- package/plugins/lisa-devin/skills/lisa-session-manager/references/runbook-template.md +66 -0
- package/plugins/lisa-devin/skills/lisa-session-manager/scripts/capacity.mjs +186 -0
- package/plugins/lisa-devin/skills/lisa-session-manager/scripts/cli-cycle.mjs +262 -0
- package/plugins/lisa-devin/skills/lisa-session-manager/scripts/cli-manager.mjs +308 -0
- package/plugins/lisa-devin/skills/lisa-session-manager/scripts/cli-queue.mjs +148 -0
- package/plugins/lisa-devin/skills/lisa-session-manager/scripts/cli-runtime.mjs +326 -0
- package/plugins/lisa-devin/skills/lisa-session-manager/scripts/cli-usage.mjs +114 -0
- package/plugins/lisa-devin/skills/lisa-session-manager/scripts/cli-workers.mjs +295 -0
- package/plugins/lisa-devin/skills/lisa-session-manager/scripts/config.mjs +265 -0
- package/plugins/lisa-devin/skills/lisa-session-manager/scripts/isolation.mjs +130 -0
- package/plugins/lisa-devin/skills/lisa-session-manager/scripts/launch.mjs +234 -0
- package/plugins/lisa-devin/skills/lisa-session-manager/scripts/ledger.mjs +445 -0
- package/plugins/lisa-devin/skills/lisa-session-manager/scripts/reconcile.mjs +134 -0
- package/plugins/lisa-devin/skills/lisa-session-manager/scripts/session-manager.mjs +274 -0
- package/plugins/lisa-devin/skills/lisa-session-manager/scripts/state-dir.mjs +112 -0
- package/plugins/lisa-devin/skills/lisa-session-manager/scripts/telegram-route.mjs +210 -0
- package/plugins/lisa-devin/skills/lisa-session-manager/scripts/token.mjs +191 -0
- package/plugins/lisa-devin/skills/lisa-session-manager/scripts/worker-launch.mjs +92 -0
- package/plugins/lisa-devin/skills/lisa-session-manager/scripts/workers.mjs +406 -0
- package/plugins/lisa-devin/skills/lisa-setup-session-manager/SKILL.md +156 -0
- package/plugins/lisa-devin/skills/lisa-tear-down-automations/SKILL.md +10 -0
- package/plugins/lisa-expo/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-expo/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-expo-agy/plugin.json +1 -1
- package/plugins/lisa-expo-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-expo-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-expo-devin/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-expo-devin/.devin-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric-agy/plugin.json +1 -1
- package/plugins/lisa-harper-fabric-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric-devin/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric-devin/.devin-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs-agy/plugin.json +1 -1
- package/plugins/lisa-nestjs-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs-devin/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs-devin/.devin-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw-agy/plugin.json +1 -1
- package/plugins/lisa-openclaw-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw-devin/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw-devin/.devin-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser-agy/plugin.json +1 -1
- package/plugins/lisa-phaser-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser-devin/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser-devin/.devin-plugin/plugin.json +1 -1
- package/plugins/lisa-rails/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-rails/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-rails-agy/plugin.json +1 -1
- package/plugins/lisa-rails-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-rails-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-rails-devin/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-rails-devin/.devin-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript-agy/plugin.json +1 -1
- package/plugins/lisa-typescript-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript-devin/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript-devin/.devin-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki-agy/plugin.json +1 -1
- package/plugins/lisa-wiki-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki-devin/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki-devin/.devin-plugin/plugin.json +1 -1
- package/plugins/src/base/commands/implement.md +2 -2
- package/plugins/src/base/commands/session-manager.md +7 -0
- package/plugins/src/base/commands/setup/session-manager.md +6 -0
- package/plugins/src/base/rules/reference/automation-runbook-contract.md +14 -0
- package/plugins/src/base/rules/reference/config-resolution.md +58 -0
- package/plugins/src/base/scripts/automation-status-claude-adapter.mjs +4 -1
- package/plugins/src/base/scripts/automation-status-codex-adapter.mjs +4 -1
- package/plugins/src/base/scripts/automation-status-expected-fleet.mjs +98 -0
- package/plugins/src/base/scripts/automation-status-run-history.mjs +29 -7
- package/plugins/src/base/scripts/ready-queue-order.mjs +352 -0
- package/plugins/src/base/skills/lisa-automation-status/SKILL.md +10 -0
- package/plugins/src/base/skills/lisa-implement/SKILL.md +17 -2
- package/plugins/src/base/skills/lisa-secrets-access/scripts/validate-config.mjs +377 -5
- package/plugins/src/base/skills/lisa-session-manager/SKILL.md +285 -0
- package/plugins/src/base/skills/lisa-session-manager/references/runbook-template.md +66 -0
- package/plugins/src/base/skills/lisa-session-manager/scripts/capacity.mjs +186 -0
- package/plugins/src/base/skills/lisa-session-manager/scripts/cli-cycle.mjs +262 -0
- package/plugins/src/base/skills/lisa-session-manager/scripts/cli-manager.mjs +308 -0
- package/plugins/src/base/skills/lisa-session-manager/scripts/cli-queue.mjs +148 -0
- package/plugins/src/base/skills/lisa-session-manager/scripts/cli-runtime.mjs +326 -0
- package/plugins/src/base/skills/lisa-session-manager/scripts/cli-usage.mjs +114 -0
- package/plugins/src/base/skills/lisa-session-manager/scripts/cli-workers.mjs +295 -0
- package/plugins/src/base/skills/lisa-session-manager/scripts/config.mjs +265 -0
- package/plugins/src/base/skills/lisa-session-manager/scripts/isolation.mjs +130 -0
- package/plugins/src/base/skills/lisa-session-manager/scripts/launch.mjs +234 -0
- package/plugins/src/base/skills/lisa-session-manager/scripts/ledger.mjs +445 -0
- package/plugins/src/base/skills/lisa-session-manager/scripts/reconcile.mjs +134 -0
- package/plugins/src/base/skills/lisa-session-manager/scripts/session-manager.mjs +274 -0
- package/plugins/src/base/skills/lisa-session-manager/scripts/state-dir.mjs +112 -0
- package/plugins/src/base/skills/lisa-session-manager/scripts/telegram-route.mjs +210 -0
- package/plugins/src/base/skills/lisa-session-manager/scripts/token.mjs +191 -0
- package/plugins/src/base/skills/lisa-session-manager/scripts/worker-launch.mjs +92 -0
- package/plugins/src/base/skills/lisa-session-manager/scripts/workers.mjs +406 -0
- package/plugins/src/base/skills/lisa-setup-session-manager/SKILL.md +156 -0
- package/plugins/src/base/skills/lisa-tear-down-automations/SKILL.md +10 -0
|
@@ -280,6 +280,7 @@ fi
|
|
|
280
280
|
| `health.schedule` | no | `off` | Health cadence contract: `off`, `daily`, or `weekly`. `lisa sync` populates the default with `_lisaSync.populated` provenance and rejects values outside this closed vocabulary. Runtime results live only at the gitignored `.lisa/health/latest.json`, never in either config file. |
|
|
281
281
|
| `usage` | no | — | Optional token/cost pricing metadata consumed by the `usage-accounting` rule. Missing pricing never blocks a lifecycle flow; Lisa records token counts with `estimated_cost: null` when no trustworthy price source is configured. |
|
|
282
282
|
| `wiki` | no | — | Wiki location for the `wiki-knowledge-source` rule. Omit for a local in-repo wiki (`wiki/`). See **Wiki source** below. |
|
|
283
|
+
| `sessionManager` | no | — (opt-in) | Local session manager: polls the Ready queue, runs each item in a fresh background Claude Code session, and relays questions over a per-project Telegram bot. Absent means the manager is not used; `lisa sync` never populates it. Written by `/lisa:setup:session-manager`. See **Session manager (`sessionManager`)** below. |
|
|
283
284
|
|
|
284
285
|
### Host rules (`.agents/rules/`)
|
|
285
286
|
|
|
@@ -688,6 +689,63 @@ Resolution order matches every other key: `$ARGUMENTS` override → `.lisa.confi
|
|
|
688
689
|
roles count as "stuck", what each repair does) are fixed like every other lifecycle transition;
|
|
689
690
|
only these thresholds are tunable.
|
|
690
691
|
|
|
692
|
+
### Session manager (`sessionManager`)
|
|
693
|
+
|
|
694
|
+
The optional `sessionManager` block configures `lisa-session-manager`, a locally hosted loop that
|
|
695
|
+
claims Ready leaf items and runs each one as `/lisa:implement` in a fresh background Claude Code
|
|
696
|
+
session. It is **opt-in**: an absent block means no manager, and `lisa sync` never populates it
|
|
697
|
+
(it is deliberately not in the sync registry, because the manager starts sessions with permissions
|
|
698
|
+
bypassed). `/lisa:setup:session-manager` writes it, detecting a multi-repo layout from
|
|
699
|
+
`projects/*/.lisa.config.json`.
|
|
700
|
+
|
|
701
|
+
```json
|
|
702
|
+
"sessionManager": {
|
|
703
|
+
"name": "<project>-manager",
|
|
704
|
+
"interval": "5m",
|
|
705
|
+
"repos": {
|
|
706
|
+
"root": "projects",
|
|
707
|
+
"entries": {
|
|
708
|
+
"api": { "hints": ["NestJS", "API", "migrations"] },
|
|
709
|
+
"web": { "hints": ["Expo", "React", "screens"] }
|
|
710
|
+
}
|
|
711
|
+
},
|
|
712
|
+
"capacity": { "maxWorkers": 6, "gbPerWorker": 3, "reserveGb": 8, "cpuFraction": 0.75, "maxLoadPerCore": 1.5 },
|
|
713
|
+
"stall": { "noOutputMinutes": 60, "maxAttempts": 3, "requestionHours": 4 },
|
|
714
|
+
"notify": { "provider": "telegram", "stateDir": "~/.claude/channels/telegram-<project>" }
|
|
715
|
+
}
|
|
716
|
+
```
|
|
717
|
+
|
|
718
|
+
| Key | Required | Default | Notes |
|
|
719
|
+
|-----|----------|---------|-------|
|
|
720
|
+
| `sessionManager.name` | **yes** | — | Manager session name: lowercase letters, digits, `-` or `_`, at most 64 characters. It names the tmux session and the session workers `SendMessage` to. Setup writes `<project>-manager`. |
|
|
721
|
+
| `sessionManager.interval` | no | `5m` | `/loop` cadence: a positive integer plus `s`, `m`, `h` or `d`. |
|
|
722
|
+
| `sessionManager.repos` | no | — | Omit for a single-repo project: every item routes to the project root. When present, `entries` must name at least one repo. |
|
|
723
|
+
| `sessionManager.repos.root` | no | `projects` | Relative directory holding the child repos. It must stay inside the project. |
|
|
724
|
+
| `sessionManager.repos.entries.<key>.hints` | **yes** (per entry) | — | Non-empty list of words the manager matches a work item against to choose the repo. `<key>` is the child directory name (one safe path segment). |
|
|
725
|
+
| `sessionManager.capacity.maxWorkers` | no | `6` | Hard cap on concurrent workers (a positive integer). Telegram `cap N` overrides it at runtime through the manager's ledger, not config. |
|
|
726
|
+
| `sessionManager.capacity.gbPerWorker` | no | `3` | Memory budgeted per worker, in GB (positive). |
|
|
727
|
+
| `sessionManager.capacity.reserveGb` | no | `8` | Memory kept free for everything else, in GB (zero or positive). |
|
|
728
|
+
| `sessionManager.capacity.cpuFraction` | no | `0.75` | Share of CPU cores workers may occupy, greater than 0 and at most 1. |
|
|
729
|
+
| `sessionManager.capacity.maxLoadPerCore` | no | `1.5` | No new worker starts while the 1-minute load average exceeds cores × this (positive). |
|
|
730
|
+
| `sessionManager.stall.noOutputMinutes` | no | `60` | A live worker with no new output for this long counts as stalled (zero or positive). |
|
|
731
|
+
| `sessionManager.stall.maxAttempts` | no | `3` | Fresh sessions per item before it moves to the configured `blocked` role (a positive integer). |
|
|
732
|
+
| `sessionManager.stall.requestionHours` | no | `4` | How long an unanswered worker question waits before the manager asks again (zero or positive). |
|
|
733
|
+
| `sessionManager.notify.provider` | no | `telegram` | Only `telegram` is implemented. |
|
|
734
|
+
| `sessionManager.notify.stateDir` | **yes** | — | This project's own Telegram state dir. Never the shared default `~/.claude/channels/telegram`, because one bot per state dir is how projects stay isolated. Setup writes `~/.claude/channels/telegram-<project>`. |
|
|
735
|
+
| `sessionManager.trustedAuthorIds` | no | — | Optional list of **stable tracker user ids**, as strings: the GitHub numeric user id (`gh api users/<login> --jq .id`), the Jira `accountId`, or the Linear user id. Not logins or display names: matching compares ids, so a login silently matches no one and excludes every item. When set, the manager takes only items authored by these people. |
|
|
736
|
+
|
|
737
|
+
- **Non-secret only.** The bot token lives in `<stateDir>/.env` (directory 0700, file 0600),
|
|
738
|
+
written by setup. A credential-named field or a token-shaped value anywhere in this block is a
|
|
739
|
+
validation error.
|
|
740
|
+
- **Local over global, per key.** `.lisa.config.local.json` overrides `.lisa.config.json` key by key
|
|
741
|
+
(for example, a smaller `capacity.maxWorkers` on one machine); arrays such as `hints` are
|
|
742
|
+
replaced, not merged. Built-in defaults fill whatever neither file sets.
|
|
743
|
+
- **Workflow roles are not here.** Ready, claimed and blocked come from the existing tracker
|
|
744
|
+
workflow config, through the single role resolver, like every other lifecycle flow.
|
|
745
|
+
- **Shape check.** `validateSessionManager` in
|
|
746
|
+
`lisa-secrets-access/scripts/validate-config.mjs` runs as part of `validateConfig`, so
|
|
747
|
+
`/lisa:doctor` reports a malformed block.
|
|
748
|
+
|
|
691
749
|
### Env-keyed `done`
|
|
692
750
|
|
|
693
751
|
The `done` role is special: the terminal status/label depends on which environment a PR was merged into. A hotfix to staging ends at `On Stg`; a production hotfix ends at `Done`. So `done` is a **map** keyed by env name (`dev`, `staging`, `production`).
|
|
@@ -123,7 +123,9 @@ export async function inspectClaudeAutomationFleet(input) {
|
|
|
123
123
|
status: "UNSUPPORTED",
|
|
124
124
|
summary: unsupported.reason,
|
|
125
125
|
expectedCadence: unsupported.expectedCadence,
|
|
126
|
-
observed:
|
|
126
|
+
observed:
|
|
127
|
+
unsupported.observed ??
|
|
128
|
+
"No automation is expected for this repo/runtime combination.",
|
|
127
129
|
runbook: runDisplay?.runbook,
|
|
128
130
|
lastOutcome: runDisplay?.lastOutcome,
|
|
129
131
|
outcomeHistory: runDisplay?.outcomeHistory,
|
|
@@ -233,6 +235,7 @@ async function resolveFleetRunDisplays(projectRoot, expectedFleet) {
|
|
|
233
235
|
projectRoot,
|
|
234
236
|
loopId: entry.id,
|
|
235
237
|
runbookPath: entry.runbookPath,
|
|
238
|
+
runbookSetupCommand: entry.runbookSetupCommand,
|
|
236
239
|
})
|
|
237
240
|
)
|
|
238
241
|
);
|
|
@@ -214,7 +214,9 @@ export async function inspectCodexAutomationFleet(input) {
|
|
|
214
214
|
status: "UNSUPPORTED",
|
|
215
215
|
summary: unsupported.reason,
|
|
216
216
|
expectedCadence: unsupported.expectedCadence,
|
|
217
|
-
observed:
|
|
217
|
+
observed:
|
|
218
|
+
unsupported.observed ??
|
|
219
|
+
"No automation is expected for this repo/runtime combination.",
|
|
218
220
|
runbook: runDisplay?.runbook,
|
|
219
221
|
lastOutcome: runDisplay?.lastOutcome,
|
|
220
222
|
outcomeHistory: runDisplay?.outcomeHistory,
|
|
@@ -656,6 +658,7 @@ async function resolveFleetRunDisplays(projectRoot, expectedFleet) {
|
|
|
656
658
|
projectRoot,
|
|
657
659
|
loopId: entry.id,
|
|
658
660
|
runbookPath: entry.runbookPath,
|
|
661
|
+
runbookSetupCommand: entry.runbookSetupCommand,
|
|
659
662
|
})
|
|
660
663
|
)
|
|
661
664
|
);
|
|
@@ -140,7 +140,16 @@ export function renderAutomationGroups(bins) {
|
|
|
140
140
|
* readonly expectedCadence: string
|
|
141
141
|
* readonly expectedRRule: string
|
|
142
142
|
* readonly runbookPath: string
|
|
143
|
+
* readonly observed?: string
|
|
144
|
+
* readonly runbookSetupCommand?: string
|
|
143
145
|
* }} UnsupportedAutomationEntry
|
|
146
|
+
*
|
|
147
|
+
* `observed` overrides the adapters' default "No automation is expected" text
|
|
148
|
+
* for an entry that does run, just not where the scheduler can see it.
|
|
149
|
+
* `runbookSetupCommand` names the command that scaffolds this loop's runbook
|
|
150
|
+
* when it is not `/lisa:setup-automations` (the session manager's runbook is
|
|
151
|
+
* seeded by `/lisa:setup:session-manager`), so the "not scaffolded" fix-it
|
|
152
|
+
* text sends the operator to the command that can actually fix it.
|
|
144
153
|
*/
|
|
145
154
|
|
|
146
155
|
/**
|
|
@@ -351,6 +360,12 @@ export function resolveExpectedAutomationFleet(input = {}) {
|
|
|
351
360
|
);
|
|
352
361
|
}
|
|
353
362
|
|
|
363
|
+
if (isPlainObject(config.sessionManager)) {
|
|
364
|
+
unsupported.push(
|
|
365
|
+
createSessionManagerEntry(identity, config.sessionManager)
|
|
366
|
+
);
|
|
367
|
+
}
|
|
368
|
+
|
|
354
369
|
return {
|
|
355
370
|
...identity,
|
|
356
371
|
expected,
|
|
@@ -413,6 +428,89 @@ function createUnsupportedEntry(identity, id, reason, group) {
|
|
|
413
428
|
};
|
|
414
429
|
}
|
|
415
430
|
|
|
431
|
+
/** Loop id the session manager records its cycles under. */
|
|
432
|
+
export const SESSION_MANAGER_LOOP_ID = "session-manager";
|
|
433
|
+
|
|
434
|
+
/** Interval used when `sessionManager.interval` is absent or unparseable. */
|
|
435
|
+
const SESSION_MANAGER_DEFAULT_INTERVAL = "5m";
|
|
436
|
+
|
|
437
|
+
/**
|
|
438
|
+
* RRULE units, largest first, so `120m` reads as `HOURLY;INTERVAL=2`.
|
|
439
|
+
* @type {readonly (readonly [string, number])[]}
|
|
440
|
+
*/
|
|
441
|
+
const RRULE_UNITS = [
|
|
442
|
+
["DAILY", 86400],
|
|
443
|
+
["HOURLY", 3600],
|
|
444
|
+
["MINUTELY", 60],
|
|
445
|
+
["SECONDLY", 1],
|
|
446
|
+
];
|
|
447
|
+
|
|
448
|
+
/** Seconds per `sessionManager.interval` unit suffix. */
|
|
449
|
+
const LOOP_INTERVAL_UNIT_SECONDS = { s: 1, m: 60, h: 3600, d: 86400 };
|
|
450
|
+
|
|
451
|
+
/**
|
|
452
|
+
* Build the UNSUPPORTED entry for the locally hosted session manager.
|
|
453
|
+
*
|
|
454
|
+
* The manager is a `/loop` in a tmux session that setup starts, so no runtime
|
|
455
|
+
* scheduler can see it: MISSING would be a lie and HEALTHY unprovable. It is
|
|
456
|
+
* listed as UNSUPPORTED with a reason that says where it runs and how to check
|
|
457
|
+
* it, and the adapters still attach its runbook and recorded cycles (it records
|
|
458
|
+
* through `automation-run-record.mjs --loop-id session-manager`). Opt-in: only
|
|
459
|
+
* called when `config.sessionManager` exists, so other fleets are unchanged.
|
|
460
|
+
*
|
|
461
|
+
* The interval parser mirrors `parseLoopInterval` in the config validator; it
|
|
462
|
+
* is inlined because plugin-level scripts do not import from skill folders.
|
|
463
|
+
*
|
|
464
|
+
* @param {{ readonly automationPrefix: string }} identity
|
|
465
|
+
* @param {Record<string, any>} block The `sessionManager` config block.
|
|
466
|
+
* @returns {UnsupportedAutomationEntry}
|
|
467
|
+
*/
|
|
468
|
+
function createSessionManagerEntry(identity, block) {
|
|
469
|
+
const parsed = parseIntervalSeconds(block.interval);
|
|
470
|
+
const interval =
|
|
471
|
+
parsed === null ? SESSION_MANAGER_DEFAULT_INTERVAL : block.interval;
|
|
472
|
+
const seconds = parsed ?? parseIntervalSeconds(interval);
|
|
473
|
+
const [freq, unit] = RRULE_UNITS.find(([, size]) => seconds % size === 0);
|
|
474
|
+
const session =
|
|
475
|
+
typeof block.name === "string" && block.name !== ""
|
|
476
|
+
? `tmux session \`${block.name}\``
|
|
477
|
+
: "the manager's tmux session";
|
|
478
|
+
return {
|
|
479
|
+
id: SESSION_MANAGER_LOOP_ID,
|
|
480
|
+
automationId: `${identity.automationPrefix}${SESSION_MANAGER_LOOP_ID}`,
|
|
481
|
+
expectedCadence: `every ${interval} (tmux-hosted /loop)`,
|
|
482
|
+
expectedRRule: `FREQ=${freq};INTERVAL=${seconds / unit}`,
|
|
483
|
+
group: "opt-in",
|
|
484
|
+
reason: `Hosted locally in ${session} by /lisa:setup:session-manager, not by this runtime's scheduler, so the scheduler cannot see it. Its recorded cycles are below; check it is alive with \`/lisa:session-manager status\`.`,
|
|
485
|
+
observed:
|
|
486
|
+
"Hosted locally in tmux; the scheduler cannot observe it — check `/lisa:session-manager status`",
|
|
487
|
+
runbookPath: resolveAutomationRunbookPath(SESSION_MANAGER_LOOP_ID),
|
|
488
|
+
runbookSetupCommand: "/lisa:setup:session-manager",
|
|
489
|
+
};
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
/**
|
|
493
|
+
* Parse a `/loop` interval such as `5m` into seconds.
|
|
494
|
+
*
|
|
495
|
+
* @param {unknown} text
|
|
496
|
+
* @returns {number | null} Seconds, or null when unparseable or zero.
|
|
497
|
+
*/
|
|
498
|
+
function parseIntervalSeconds(text) {
|
|
499
|
+
if (typeof text !== "string") return null;
|
|
500
|
+
const match = /^(\d+)([smhd])$/.exec(text);
|
|
501
|
+
if (!match) return null;
|
|
502
|
+
const seconds = Number(match[1]) * LOOP_INTERVAL_UNIT_SECONDS[match[2]];
|
|
503
|
+
return seconds > 0 ? seconds : null;
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
/**
|
|
507
|
+
* @param {unknown} value
|
|
508
|
+
* @returns {boolean} True for `{}`-shaped values.
|
|
509
|
+
*/
|
|
510
|
+
function isPlainObject(value) {
|
|
511
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
512
|
+
}
|
|
513
|
+
|
|
416
514
|
/**
|
|
417
515
|
* @param {Record<string, any>} config
|
|
418
516
|
* @param {string | undefined} source
|
|
@@ -41,8 +41,23 @@ export const RECOVERY_REQUIRED_OUTCOME = "recovery-required";
|
|
|
41
41
|
*/
|
|
42
42
|
export const RECOVERY_REQUIRED_ESCALATION_THRESHOLD = 3;
|
|
43
43
|
|
|
44
|
-
|
|
45
|
-
|
|
44
|
+
/** The command that scaffolds a scheduled loop's runbook. */
|
|
45
|
+
const DEFAULT_RUNBOOK_SETUP_COMMAND = "/lisa:setup-automations";
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* The "not scaffolded" fix-it line, naming the command that scaffolds this
|
|
49
|
+
* loop's runbook.
|
|
50
|
+
*
|
|
51
|
+
* @param {string} [setupCommand]
|
|
52
|
+
* @returns {string}
|
|
53
|
+
*/
|
|
54
|
+
function runbookNotScaffoldedLine(
|
|
55
|
+
setupCommand = DEFAULT_RUNBOOK_SETUP_COMMAND
|
|
56
|
+
) {
|
|
57
|
+
return `not scaffolded — run ${setupCommand}`;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const RUNBOOK_NOT_SCAFFOLDED_LINE = runbookNotScaffoldedLine();
|
|
46
61
|
const NO_RECORDED_RUNS_LINE = "no recorded runs yet";
|
|
47
62
|
|
|
48
63
|
/**
|
|
@@ -72,12 +87,18 @@ const NO_RECORDED_RUNS_LINE = "no recorded runs yet";
|
|
|
72
87
|
* readonly projectRoot?: string
|
|
73
88
|
* readonly loopId: string
|
|
74
89
|
* readonly runbookPath?: string
|
|
75
|
-
*
|
|
90
|
+
* readonly runbookSetupCommand?: string
|
|
91
|
+
* }} input `runbookSetupCommand` overrides `/lisa:setup-automations` in the
|
|
92
|
+
* "not scaffolded" line for a loop another setup command scaffolds.
|
|
76
93
|
* @returns {Promise<AutomationRunDisplay>}
|
|
77
94
|
*/
|
|
78
95
|
export async function resolveAutomationRunDisplay(input) {
|
|
79
96
|
const projectRoot = path.resolve(input.projectRoot ?? process.cwd());
|
|
80
|
-
const runbook = await resolveRunbookLine(
|
|
97
|
+
const runbook = await resolveRunbookLine(
|
|
98
|
+
projectRoot,
|
|
99
|
+
input.runbookPath,
|
|
100
|
+
runbookNotScaffoldedLine(input.runbookSetupCommand)
|
|
101
|
+
);
|
|
81
102
|
const recordsPath = automationRunRecordPath(projectRoot, input.loopId);
|
|
82
103
|
const { records, skippedCorruptLines } =
|
|
83
104
|
await readAutomationRunRecords(recordsPath);
|
|
@@ -168,16 +189,17 @@ export function resolveRecoveryEscalation(runDisplay) {
|
|
|
168
189
|
/**
|
|
169
190
|
* @param {string} projectRoot
|
|
170
191
|
* @param {string | undefined} runbookPath
|
|
192
|
+
* @param {string} missingLine The "not scaffolded" line for this loop
|
|
171
193
|
* @returns {Promise<string>}
|
|
172
194
|
*/
|
|
173
|
-
async function resolveRunbookLine(projectRoot, runbookPath) {
|
|
195
|
+
async function resolveRunbookLine(projectRoot, runbookPath, missingLine) {
|
|
174
196
|
if (!runbookPath) {
|
|
175
|
-
return
|
|
197
|
+
return missingLine;
|
|
176
198
|
}
|
|
177
199
|
const scaffolded = await runbookIsScaffolded(
|
|
178
200
|
path.join(projectRoot, runbookPath)
|
|
179
201
|
);
|
|
180
|
-
return scaffolded ? runbookPath :
|
|
202
|
+
return scaffolded ? runbookPath : missingLine;
|
|
181
203
|
}
|
|
182
204
|
|
|
183
205
|
/**
|
|
@@ -0,0 +1,352 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Vendor-neutral Ready-queue normalization and mine-first dispatch order.
|
|
4
|
+
*
|
|
5
|
+
* ## Why this exists
|
|
6
|
+
*
|
|
7
|
+
* Intake has always taken Ready work in whatever order the tracker returned
|
|
8
|
+
* it, and `intake.assignee` only NARROWS a selection — it never ranks one. A
|
|
9
|
+
* dispatcher that runs several workers at once needs a stable answer to "what
|
|
10
|
+
* next?": the work already assigned to me first (somebody chose me for it),
|
|
11
|
+
* then unassigned work, most urgent and oldest first. Work assigned to someone
|
|
12
|
+
* else is theirs, so it is excluded, never re-ordered.
|
|
13
|
+
*
|
|
14
|
+
* The three trackers disagree on every field this needs — GitHub has priority
|
|
15
|
+
* LABELS, Linear a native 0..4 number where 0 means "none", Jira a priority
|
|
16
|
+
* NAME from a scheme that varies by site — so each vendor gets one normalizer
|
|
17
|
+
* onto a single item shape, and ordering only ever sees that shape.
|
|
18
|
+
*
|
|
19
|
+
* `queue-status-build-readers.mjs` reads raw GitHub only; Jira and Linear
|
|
20
|
+
* arrive here already fetched by their vendor access skills. This module never
|
|
21
|
+
* fetches: the caller pages the tracker to completion, and
|
|
22
|
+
* {@link assertPagedToCompletion} refuses to order a half-read queue, because
|
|
23
|
+
* a truncated sweep silently reorders by omission (#3529, #4105).
|
|
24
|
+
*
|
|
25
|
+
* Held items are judged by {@link isHumanGated}, the one shared answer to "is
|
|
26
|
+
* a person holding this?", rather than by a local label check (#3805).
|
|
27
|
+
* @module ready-queue-order
|
|
28
|
+
*/
|
|
29
|
+
|
|
30
|
+
import {
|
|
31
|
+
DEFAULT_HUMAN_NEEDED_LABEL,
|
|
32
|
+
isHumanGated,
|
|
33
|
+
} from "./intake-blocker-reprobe.mjs";
|
|
34
|
+
|
|
35
|
+
/** Rank for GitHub `priority:<value>` labels; no label ranks 4. */
|
|
36
|
+
const GITHUB_PRIORITY_RANKS = Object.freeze({
|
|
37
|
+
critical: 0,
|
|
38
|
+
high: 1,
|
|
39
|
+
medium: 2,
|
|
40
|
+
low: 3,
|
|
41
|
+
});
|
|
42
|
+
const GITHUB_NO_PRIORITY = 4;
|
|
43
|
+
|
|
44
|
+
/** Rank for Jira priority names, across the default and legacy schemes. */
|
|
45
|
+
const JIRA_PRIORITY_RANKS = Object.freeze({
|
|
46
|
+
highest: 0,
|
|
47
|
+
blocker: 0,
|
|
48
|
+
high: 1,
|
|
49
|
+
critical: 1,
|
|
50
|
+
medium: 2,
|
|
51
|
+
major: 2,
|
|
52
|
+
low: 3,
|
|
53
|
+
minor: 3,
|
|
54
|
+
lowest: 4,
|
|
55
|
+
trivial: 4,
|
|
56
|
+
});
|
|
57
|
+
const JIRA_UNKNOWN_PRIORITY = 5;
|
|
58
|
+
|
|
59
|
+
/** Linear native priority: 1 urgent .. 4 low, and 0 means no priority. */
|
|
60
|
+
const LINEAR_NO_PRIORITY = 4;
|
|
61
|
+
|
|
62
|
+
/** Linear workflow state types that leave a child finished. */
|
|
63
|
+
const LINEAR_FINISHED_STATE_TYPES = new Set(["completed", "canceled"]);
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* @param {unknown} value
|
|
67
|
+
* @returns {string}
|
|
68
|
+
*/
|
|
69
|
+
function text(value) {
|
|
70
|
+
return typeof value === "string" ? value : "";
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* A stable id as a string, or null. Numeric GitHub ids become strings so every
|
|
75
|
+
* comparison in this module is string-to-string.
|
|
76
|
+
* @param {unknown} value
|
|
77
|
+
* @returns {string | null}
|
|
78
|
+
*/
|
|
79
|
+
function idOrNull(value) {
|
|
80
|
+
if (typeof value === "string" && value.trim().length > 0) {
|
|
81
|
+
return value.trim();
|
|
82
|
+
}
|
|
83
|
+
return Number.isSafeInteger(value) ? String(value) : null;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Label names from strings, `{name}` objects or a GraphQL `{nodes}` connection.
|
|
88
|
+
* @param {unknown} labels
|
|
89
|
+
* @returns {string[]}
|
|
90
|
+
*/
|
|
91
|
+
function labelNames(labels) {
|
|
92
|
+
const list = Array.isArray(labels)
|
|
93
|
+
? labels
|
|
94
|
+
: Array.isArray(labels?.nodes)
|
|
95
|
+
? labels.nodes
|
|
96
|
+
: [];
|
|
97
|
+
return list
|
|
98
|
+
.map(label => (typeof label === "string" ? label : text(label?.name)))
|
|
99
|
+
.map(label => label.trim())
|
|
100
|
+
.filter(label => label.length > 0);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* The `repo:<name>` labels, lowercased, as `repo-scope-split` reads them.
|
|
105
|
+
* @param {readonly string[]} labels
|
|
106
|
+
* @returns {string[]}
|
|
107
|
+
*/
|
|
108
|
+
function repoLabelsOf(labels) {
|
|
109
|
+
return labels
|
|
110
|
+
.map(label => label.toLowerCase())
|
|
111
|
+
.filter(label => label.startsWith("repo:") && label.length > 5);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Flatten an Atlassian Document Format node to plain text, one line per block.
|
|
116
|
+
* A string description (the Markdown some access paths return) passes through.
|
|
117
|
+
* @param {unknown} node
|
|
118
|
+
* @returns {string}
|
|
119
|
+
*/
|
|
120
|
+
function adfText(node) {
|
|
121
|
+
if (typeof node === "string") return node;
|
|
122
|
+
if (!node || typeof node !== "object") return "";
|
|
123
|
+
if (node.type === "text") return text(node.text);
|
|
124
|
+
if (node.type === "hardBreak") return "\n";
|
|
125
|
+
const children = Array.isArray(node.content) ? node.content : [];
|
|
126
|
+
const joiner = node.type === "doc" ? "\n" : "";
|
|
127
|
+
return children.map(adfText).join(joiner);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Normalize one GitHub REST issue row. Returns null for a pull-request row,
|
|
132
|
+
* which the issues endpoint returns alongside issues and which is never a
|
|
133
|
+
* dispatchable leaf.
|
|
134
|
+
* @param {Record<string, any>} issue - REST `issues` list row
|
|
135
|
+
* @param {{ repo?: string }} [options] - `owner/repo`, else read from repository_url
|
|
136
|
+
* @returns {ReturnType<typeof shape> | null}
|
|
137
|
+
*/
|
|
138
|
+
export function normalizeGithubIssue(issue, options = {}) {
|
|
139
|
+
if (!issue || issue.pull_request) return null;
|
|
140
|
+
const labels = labelNames(issue.labels);
|
|
141
|
+
const ranks = labels
|
|
142
|
+
.map(label => label.toLowerCase())
|
|
143
|
+
.filter(label => label.startsWith("priority:"))
|
|
144
|
+
.map(label => GITHUB_PRIORITY_RANKS[label.slice("priority:".length)])
|
|
145
|
+
.filter(rank => rank !== undefined);
|
|
146
|
+
const repo =
|
|
147
|
+
text(options.repo) || text(issue.repository_url).split("/repos/")[1] || "";
|
|
148
|
+
const summary = issue.sub_issues_summary;
|
|
149
|
+
const assignee = issue.assignee ?? issue.assignees?.[0] ?? null;
|
|
150
|
+
|
|
151
|
+
return shape({
|
|
152
|
+
ref: `${repo}#${issue.number}`,
|
|
153
|
+
title: text(issue.title),
|
|
154
|
+
// GitHub "me" is the login (`gh api user --jq .login`), so match on it.
|
|
155
|
+
assigneeId: idOrNull(assignee?.login),
|
|
156
|
+
priorityRank: ranks.length > 0 ? Math.min(...ranks) : GITHUB_NO_PRIORITY,
|
|
157
|
+
createdAt: text(issue.created_at),
|
|
158
|
+
labels,
|
|
159
|
+
body: text(issue.body),
|
|
160
|
+
// Authors are trusted by stable id, never by login (ready-role-filing).
|
|
161
|
+
authorId: idOrNull(issue.user?.id),
|
|
162
|
+
isContainer: Number(summary?.total ?? 0) > Number(summary?.completed ?? 0),
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Normalize one Jira REST/MCP issue. An epic (hierarchy level above 0) or an
|
|
168
|
+
* issue with any subtask not yet in the done category is a container.
|
|
169
|
+
* @param {Record<string, any>} issue - Issue with `key` and `fields`
|
|
170
|
+
* @returns {ReturnType<typeof shape>}
|
|
171
|
+
*/
|
|
172
|
+
export function normalizeJiraIssue(issue) {
|
|
173
|
+
const fields = issue?.fields ?? {};
|
|
174
|
+
const labels = labelNames(fields.labels);
|
|
175
|
+
const priority = text(fields.priority?.name).trim().toLowerCase();
|
|
176
|
+
const type = fields.issuetype ?? {};
|
|
177
|
+
const openSubtasks = (Array.isArray(fields.subtasks) ? fields.subtasks : [])
|
|
178
|
+
.map(sub => sub?.fields?.status?.statusCategory?.key)
|
|
179
|
+
.filter(category => category !== "done");
|
|
180
|
+
|
|
181
|
+
return shape({
|
|
182
|
+
ref: text(issue?.key),
|
|
183
|
+
title: text(fields.summary),
|
|
184
|
+
assigneeId: idOrNull(fields.assignee?.accountId),
|
|
185
|
+
priorityRank: JIRA_PRIORITY_RANKS[priority] ?? JIRA_UNKNOWN_PRIORITY,
|
|
186
|
+
createdAt: text(fields.created),
|
|
187
|
+
labels,
|
|
188
|
+
body: adfText(fields.description),
|
|
189
|
+
authorId: idOrNull(fields.reporter?.accountId ?? fields.creator?.accountId),
|
|
190
|
+
isContainer:
|
|
191
|
+
Number(type.hierarchyLevel ?? 0) > 0 ||
|
|
192
|
+
text(type.name).toLowerCase() === "epic" ||
|
|
193
|
+
openSubtasks.length > 0,
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Normalize one Linear GraphQL issue. An issue with any child not completed
|
|
199
|
+
* or canceled is a container.
|
|
200
|
+
* @param {Record<string, any>} issue - Issue node
|
|
201
|
+
* @returns {ReturnType<typeof shape>}
|
|
202
|
+
*/
|
|
203
|
+
export function normalizeLinearIssue(issue) {
|
|
204
|
+
const native = Number(issue?.priority ?? 0);
|
|
205
|
+
const children = Array.isArray(issue?.children?.nodes)
|
|
206
|
+
? issue.children.nodes
|
|
207
|
+
: Array.isArray(issue?.children)
|
|
208
|
+
? issue.children
|
|
209
|
+
: [];
|
|
210
|
+
const labels = labelNames(issue?.labels);
|
|
211
|
+
|
|
212
|
+
return shape({
|
|
213
|
+
ref: text(issue?.identifier),
|
|
214
|
+
title: text(issue?.title),
|
|
215
|
+
assigneeId: idOrNull(issue?.assignee?.id),
|
|
216
|
+
priorityRank:
|
|
217
|
+
Number.isInteger(native) && native >= 1 && native <= 4
|
|
218
|
+
? native - 1
|
|
219
|
+
: LINEAR_NO_PRIORITY,
|
|
220
|
+
createdAt: text(issue?.createdAt),
|
|
221
|
+
labels,
|
|
222
|
+
body: text(issue?.description),
|
|
223
|
+
authorId: idOrNull(issue?.creator?.id),
|
|
224
|
+
isContainer: children.some(
|
|
225
|
+
child => !LINEAR_FINISHED_STATE_TYPES.has(child?.state?.type)
|
|
226
|
+
),
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* The one item shape every normalizer returns, in a fixed key order.
|
|
232
|
+
* @param {{ ref: string, title: string, assigneeId: string | null, priorityRank: number, createdAt: string, labels: string[], body: string, authorId: string | null, isContainer: boolean }} input
|
|
233
|
+
*/
|
|
234
|
+
function shape(input) {
|
|
235
|
+
return {
|
|
236
|
+
ref: input.ref,
|
|
237
|
+
title: input.title,
|
|
238
|
+
assigneeId: input.assigneeId,
|
|
239
|
+
priorityRank: input.priorityRank,
|
|
240
|
+
createdAt: input.createdAt,
|
|
241
|
+
labels: input.labels,
|
|
242
|
+
body: input.body,
|
|
243
|
+
authorId: input.authorId,
|
|
244
|
+
isContainer: input.isContainer,
|
|
245
|
+
repoLabels: repoLabelsOf(input.labels),
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
/**
|
|
250
|
+
* Why an item may not be dispatched, or null when it may.
|
|
251
|
+
*
|
|
252
|
+
* Order matters only for the reason reported: an item can fail several rules,
|
|
253
|
+
* and "someone else owns it" is the most useful thing to say about it.
|
|
254
|
+
* @param {Record<string, any>} item
|
|
255
|
+
* @param {{ me: string | null, trusted: ReadonlySet<string>, humanNeededLabel: string }} context
|
|
256
|
+
* @returns {string | null}
|
|
257
|
+
*/
|
|
258
|
+
function exclusionReason(item, context) {
|
|
259
|
+
const assignee = idOrNull(item.assigneeId);
|
|
260
|
+
if (assignee !== null && assignee !== context.me) return "assigned-to-other";
|
|
261
|
+
if (item.isContainer === true) return "container";
|
|
262
|
+
if (
|
|
263
|
+
isHumanGated({
|
|
264
|
+
labels: item.labels,
|
|
265
|
+
body: item.body,
|
|
266
|
+
comments: item.comments,
|
|
267
|
+
trustedHumanActorIds: item.trustedHumanActorIds,
|
|
268
|
+
humanNeededLabel: context.humanNeededLabel,
|
|
269
|
+
})
|
|
270
|
+
) {
|
|
271
|
+
return "human-gated";
|
|
272
|
+
}
|
|
273
|
+
if (
|
|
274
|
+
context.trusted.size > 0 &&
|
|
275
|
+
!context.trusted.has(idOrNull(item.authorId))
|
|
276
|
+
) {
|
|
277
|
+
return "untrusted-author";
|
|
278
|
+
}
|
|
279
|
+
return null;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
/**
|
|
283
|
+
* @param {Record<string, any>} left
|
|
284
|
+
* @param {Record<string, any>} right
|
|
285
|
+
* @returns {number}
|
|
286
|
+
*/
|
|
287
|
+
function compareWithinGroup(left, right) {
|
|
288
|
+
return (
|
|
289
|
+
Number(left.priorityRank) - Number(right.priorityRank) ||
|
|
290
|
+
text(left.createdAt).localeCompare(text(right.createdAt)) ||
|
|
291
|
+
text(left.ref).localeCompare(text(right.ref))
|
|
292
|
+
);
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
/**
|
|
296
|
+
* Order normalized Ready items for dispatch, mine first.
|
|
297
|
+
*
|
|
298
|
+
* Group 0 is assigned to `me`, group 1 unassigned; within a group, priority
|
|
299
|
+
* rank ascending, then creation time ascending, then ref as a stable
|
|
300
|
+
* tiebreak. Excluded items keep their input order. With no `me`, every
|
|
301
|
+
* assigned item is someone else's — the safe direction, since claiming
|
|
302
|
+
* assigned work steals it.
|
|
303
|
+
* @param {readonly Record<string, any>[]} items - Normalized items
|
|
304
|
+
* @param {{ me?: string | null, trustedAuthorIds?: readonly unknown[], humanNeededLabel?: string }} [options]
|
|
305
|
+
* @returns {{ ordered: Record<string, any>[], excluded: { ref: string, reason: string }[] }}
|
|
306
|
+
*/
|
|
307
|
+
export function orderReadyQueue(items, options = {}) {
|
|
308
|
+
const context = {
|
|
309
|
+
me: idOrNull(options.me),
|
|
310
|
+
trusted: new Set(
|
|
311
|
+
(options.trustedAuthorIds ?? []).map(idOrNull).filter(Boolean)
|
|
312
|
+
),
|
|
313
|
+
humanNeededLabel: options.humanNeededLabel ?? DEFAULT_HUMAN_NEEDED_LABEL,
|
|
314
|
+
};
|
|
315
|
+
const judged = (items ?? []).map(item => ({
|
|
316
|
+
item,
|
|
317
|
+
reason: exclusionReason(item, context),
|
|
318
|
+
}));
|
|
319
|
+
const eligible = judged
|
|
320
|
+
.filter(one => one.reason === null)
|
|
321
|
+
.map(one => one.item);
|
|
322
|
+
const group = item => (idOrNull(item.assigneeId) === null ? 1 : 0);
|
|
323
|
+
|
|
324
|
+
return {
|
|
325
|
+
ordered: [...eligible].sort(
|
|
326
|
+
(left, right) =>
|
|
327
|
+
group(left) - group(right) || compareWithinGroup(left, right)
|
|
328
|
+
),
|
|
329
|
+
excluded: judged
|
|
330
|
+
.filter(one => one.reason !== null)
|
|
331
|
+
.map(one => ({ ref: one.item.ref, reason: one.reason })),
|
|
332
|
+
};
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
/**
|
|
336
|
+
* Refuse a queue envelope that was not paged to completion.
|
|
337
|
+
*
|
|
338
|
+
* The vendor fetch writes `{complete, pages, items}`; anything but a literal
|
|
339
|
+
* `complete: true` means some pages may be missing, and ordering a partial
|
|
340
|
+
* queue would dispatch the wrong item with full confidence.
|
|
341
|
+
* @template T
|
|
342
|
+
* @param {T} envelope - `{ complete: true, pages, items }`
|
|
343
|
+
* @returns {T} The same envelope
|
|
344
|
+
*/
|
|
345
|
+
export function assertPagedToCompletion(envelope) {
|
|
346
|
+
if (envelope?.complete !== true) {
|
|
347
|
+
throw new Error(
|
|
348
|
+
"Ready queue was not paged to completion (envelope.complete !== true); refusing to order a partial queue."
|
|
349
|
+
);
|
|
350
|
+
}
|
|
351
|
+
return envelope;
|
|
352
|
+
}
|
|
@@ -31,6 +31,16 @@ scheduler means the project opted in, so the gardener is compared like any other
|
|
|
31
31
|
means it is reported `UNSUPPORTED` (opted out), never `MISSING`. This skill stays read-only — it
|
|
32
32
|
infers, it never writes the flag anywhere.
|
|
33
33
|
|
|
34
|
+
**Locally hosted loops.** When `.lisa.config.json` has a `sessionManager` block, the resolved fleet
|
|
35
|
+
carries one extra opt-in entry, `session-manager`, reported `UNSUPPORTED`. That status is honest:
|
|
36
|
+
`/lisa:setup:session-manager` hosts the manager as a `/loop` in a local tmux session, not on this
|
|
37
|
+
runtime's scheduler, so `/schedule` or Codex automations will never list it, and it is neither
|
|
38
|
+
`MISSING` nor provably `HEALTHY` from here. The manager records every cycle through
|
|
39
|
+
`automation-run-record.mjs` under loop id `session-manager`, so the row still shows its runbook
|
|
40
|
+
(`.lisa/automations/session-manager.runbook.md`), last outcome and history. Pass the entry's reason
|
|
41
|
+
through to the operator: it names the tmux session and says to check liveness with
|
|
42
|
+
`/lisa:session-manager status`. With no `sessionManager` block the entry does not exist.
|
|
43
|
+
|
|
34
44
|
Resolve the expected project identifier, fleet naming prefix, queue arguments, cadence, and stack-support rules from the same contract used by `setup-automations` and `tear-down-automations`. Do **not** invent a second source of truth for fleet naming or queue resolution.
|
|
35
45
|
|
|
36
46
|
## Runtime inspection
|