@mmerterden/multi-agent-pipeline 20.2.1 → 20.3.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/CHANGELOG.md +912 -1
- package/README.md +104 -81
- package/README.tr.md +103 -62
- package/docs/FIGMA_PIPELINE.md +35 -35
- package/docs/adr/0006-skills-core-external-split.md +1 -1
- package/docs/adr/0007-multi-tool-adapter-framework.md +6 -0
- package/docs/adr/0008-installer-modularization-and-secret-leak-defense.md +7 -0
- package/docs/architecture.md +50 -14
- package/docs/best-practices.md +1 -1
- package/docs/ecosystem.md +56 -32
- package/docs/facts.json +10 -10
- package/docs/features.md +97 -5
- package/docs/recovery-guide.md +7 -14
- package/docs/server-readiness.md +31 -24
- package/index.js +1 -1
- package/install/_common.mjs +3 -5
- package/install/_platform-filter.mjs +23 -1
- package/install/_unattended-profile.mjs +321 -75
- package/install/claude.mjs +51 -10
- package/install/codex.mjs +2 -0
- package/install/copilot.mjs +2 -0
- package/install/index.mjs +30 -17
- package/install/templates/claude-hooks.json +16 -5
- package/install/templates/copilot-instructions.md +1 -1
- package/install/templates/multi-agent-autopilot-awake.plist.template +48 -0
- package/install/templates/multi-agent-autopilot.plist.template +12 -5
- package/install/unattended-profile-legacy.json +80 -0
- package/manifest.json +616 -483
- package/package.json +8 -3
- package/pipeline/agents/code-reviewer.md +10 -0
- package/pipeline/agents/plan-critic.md +98 -0
- package/pipeline/agents/security-auditor.md +10 -0
- package/pipeline/agents/task-clarifier.md +10 -0
- package/pipeline/commands/multi-agent/SKILL.md +2 -2
- package/pipeline/commands/multi-agent/analysis/SKILL.md +6 -2
- package/pipeline/commands/multi-agent/analysis-jira/SKILL.md +12 -1
- package/pipeline/commands/multi-agent/analysis-resolve/SKILL.md +4 -0
- package/pipeline/commands/multi-agent/autopilot/SKILL.md +6 -2
- package/pipeline/commands/multi-agent/autopilot-off/SKILL.md +36 -6
- package/pipeline/commands/multi-agent/autopilot-on/SKILL.md +77 -12
- package/pipeline/commands/multi-agent/autopilot-status/SKILL.md +29 -8
- package/pipeline/commands/multi-agent/build-optimize/SKILL.md +4 -0
- package/pipeline/commands/multi-agent/channels/SKILL.md +9 -9
- package/pipeline/commands/multi-agent/complaint-analysis/SKILL.md +5 -1
- package/pipeline/commands/multi-agent/create-jira/SKILL.md +4 -0
- package/pipeline/commands/multi-agent/design-check/SKILL.md +16 -11
- package/pipeline/commands/multi-agent/diff-explain/SKILL.md +4 -0
- package/pipeline/commands/multi-agent/doctor/SKILL.md +4 -0
- package/pipeline/commands/multi-agent/feedback/SKILL.md +5 -1
- package/pipeline/commands/multi-agent/forget/SKILL.md +4 -0
- package/pipeline/commands/multi-agent/garbage-collect/SKILL.md +4 -0
- package/pipeline/commands/multi-agent/graph/SKILL.md +4 -0
- package/pipeline/commands/multi-agent/help/SKILL.md +37 -39
- package/pipeline/commands/multi-agent/ios-coding-standard/SKILL.md +4 -0
- package/pipeline/commands/multi-agent/issue/SKILL.md +7 -1
- package/pipeline/commands/multi-agent/jira/SKILL.md +7 -1
- package/pipeline/commands/multi-agent/kill/SKILL.md +9 -3
- package/pipeline/commands/multi-agent/language/SKILL.md +4 -0
- package/pipeline/commands/multi-agent/log/SKILL.md +4 -0
- package/pipeline/commands/multi-agent/manual-test/SKILL.md +5 -0
- package/pipeline/commands/multi-agent/model/SKILL.md +4 -0
- package/pipeline/commands/multi-agent/prune-logs/SKILL.md +4 -0
- package/pipeline/commands/multi-agent/prune-prompts/SKILL.md +4 -0
- package/pipeline/commands/multi-agent/purge/SKILL.md +4 -0
- package/pipeline/commands/multi-agent/refactor/SKILL.md +5 -0
- package/pipeline/commands/multi-agent/research/SKILL.md +49 -0
- package/pipeline/commands/multi-agent/resume/SKILL.md +29 -6
- package/pipeline/commands/multi-agent/review/SKILL.md +28 -9
- package/pipeline/commands/multi-agent/review-analysis/SKILL.md +5 -1
- package/pipeline/commands/multi-agent/review-issue/SKILL.md +4 -0
- package/pipeline/commands/multi-agent/review-jira/SKILL.md +4 -0
- package/pipeline/commands/multi-agent/route-off/SKILL.md +4 -0
- package/pipeline/commands/multi-agent/route-on/SKILL.md +4 -0
- package/pipeline/commands/multi-agent/route-status/SKILL.md +4 -0
- package/pipeline/commands/multi-agent/routines/SKILL.md +4 -0
- package/pipeline/commands/multi-agent/save/SKILL.md +6 -2
- package/pipeline/commands/multi-agent/scaffold/SKILL.md +47 -0
- package/pipeline/commands/multi-agent/scan/SKILL.md +4 -0
- package/pipeline/commands/multi-agent/search/SKILL.md +12 -8
- package/pipeline/commands/multi-agent/security-review/SKILL.md +4 -0
- package/pipeline/commands/multi-agent/serve/SKILL.md +62 -0
- package/pipeline/commands/multi-agent/setup/SKILL.md +27 -36
- package/pipeline/commands/multi-agent/stack/SKILL.md +4 -0
- package/pipeline/commands/multi-agent/status/SKILL.md +10 -9
- package/pipeline/commands/multi-agent/steer/SKILL.md +5 -1
- package/pipeline/commands/multi-agent/store-ready/SKILL.md +4 -0
- package/pipeline/commands/multi-agent/sync/SKILL.md +13 -13
- package/pipeline/commands/multi-agent/test/SKILL.md +4 -0
- package/pipeline/commands/multi-agent/test-accessibility/SKILL.md +4 -0
- package/pipeline/commands/multi-agent/test-dark-mode/SKILL.md +4 -0
- package/pipeline/commands/multi-agent/test-dynamic-type/SKILL.md +4 -0
- package/pipeline/commands/multi-agent/test-screenshots/SKILL.md +5 -1
- package/pipeline/commands/multi-agent/testflight-validation/SKILL.md +4 -0
- package/pipeline/commands/multi-agent/uninstall/SKILL.md +5 -1
- package/pipeline/commands/multi-agent/update/SKILL.md +4 -0
- package/pipeline/contract/CHANGELOG.md +74 -0
- package/pipeline/contract/README.md +126 -0
- package/pipeline/contract/build.mjs +427 -0
- package/pipeline/contract/fixtures/answer-result.json +11 -0
- package/pipeline/contract/fixtures/error-invalid-request.json +8 -0
- package/pipeline/contract/fixtures/error-unauthorized.json +5 -0
- package/pipeline/contract/fixtures/error-unsigned.json +5 -0
- package/pipeline/contract/fixtures/issues-empty.json +18 -0
- package/pipeline/contract/fixtures/launch-plan.json +31 -0
- package/pipeline/contract/fixtures/runs-awaiting-question.json +70 -0
- package/pipeline/contract/fixtures/runs-empty.json +6 -0
- package/pipeline/contract/fixtures/runs-failed.json +84 -0
- package/pipeline/contract/fixtures/runs-old-schema.json +70 -0
- package/pipeline/contract/fixtures/runs-pr-opened-redacted.json +101 -0
- package/pipeline/contract/fixtures/runs-pr-opened.json +106 -0
- package/pipeline/contract/fixtures/runs-running.json +84 -0
- package/pipeline/contract/fixtures/worktrees-empty.json +4 -0
- package/pipeline/contract/frozen/toolbox.json +107 -0
- package/pipeline/contract/manifest.json +263 -0
- package/pipeline/contract/types/index.d.ts +343 -0
- package/pipeline/lib/_jira-auth.sh +6 -2
- package/pipeline/lib/account-resolver.sh +1 -1
- package/pipeline/lib/autopilot-state.sh +19 -0
- package/pipeline/lib/context-link-extractor.sh +12 -5
- package/pipeline/lib/credential-inventory.sh +12 -5
- package/pipeline/lib/credential-store.sh +116 -185
- package/pipeline/lib/fetch-confluence.sh +44 -3
- package/pipeline/lib/fetch-document.sh +3 -4
- package/pipeline/lib/fetch-fortify.sh +1 -1
- package/pipeline/lib/figma-mcp-refresh.sh +2 -2
- package/pipeline/lib/figma-token.sh +5 -1
- package/pipeline/lib/issue-fetcher.sh +233 -16
- package/pipeline/lib/json-file-lock.mjs +172 -0
- package/pipeline/lib/model-dispatch.sh +21 -12
- package/pipeline/lib/model-rung.sh +6 -1
- package/pipeline/lib/multi-repo-pipeline.sh +1 -1
- package/pipeline/lib/outbound-gate.mjs +46 -16
- package/pipeline/lib/parse-complaints.sh +14 -7
- package/pipeline/lib/plan-todos.sh +3 -3
- package/pipeline/lib/post-pr-review.sh +9 -9
- package/pipeline/lib/pr-request-location.mjs +85 -0
- package/pipeline/lib/regular-file.mjs +153 -0
- package/pipeline/lib/repo-hygiene.sh +17 -0
- package/pipeline/lib/route-state.sh +5 -1
- package/pipeline/lib/run-paths.sh +3 -2
- package/pipeline/lib/stack-detect.sh +19 -1
- package/pipeline/lib/unattended-profile-check.mjs +178 -0
- package/pipeline/lib/unattended-settings-location.mjs +28 -0
- package/pipeline/lib/unattended.mjs +76 -0
- package/pipeline/lib/unattended.sh +32 -0
- package/pipeline/lib/untrusted.mjs +76 -0
- package/pipeline/lib/user-facing.mjs +82 -0
- package/pipeline/lib/user-facing.sh +58 -0
- package/pipeline/multi-agent-refs/_dev-context.md +1 -1
- package/pipeline/multi-agent-refs/analysis/evidence.md +4 -0
- package/pipeline/multi-agent-refs/analysis/locked.md +2 -2
- package/pipeline/multi-agent-refs/analysis/render.md +4 -3
- package/pipeline/multi-agent-refs/analysis/resolve.md +3 -1
- package/pipeline/multi-agent-refs/analysis/synthesis.md +4 -5
- package/pipeline/multi-agent-refs/analysis-template.md +10 -17
- package/pipeline/multi-agent-refs/channels/jira.md +1 -1
- package/pipeline/multi-agent-refs/component-dispatch.md +3 -3
- package/pipeline/multi-agent-refs/conventions-defaults.md +32 -32
- package/pipeline/multi-agent-refs/cross-cli-contract.md +10 -17
- package/pipeline/multi-agent-refs/features/autopilot-circuit-breaker.md +37 -9
- package/pipeline/multi-agent-refs/features/autopilot-operations.md +277 -0
- package/pipeline/multi-agent-refs/features/constitution.md +196 -0
- package/pipeline/multi-agent-refs/features/doctor.md +6 -3
- package/pipeline/multi-agent-refs/features/external-context-injection.md +6 -1
- package/pipeline/multi-agent-refs/features/jira-context.md +1 -1
- package/pipeline/multi-agent-refs/features/maturity-followup.md +43 -23
- package/pipeline/multi-agent-refs/features/model-fallback.md +9 -0
- package/pipeline/multi-agent-refs/features/phone-api.md +306 -0
- package/pipeline/multi-agent-refs/features/plan-critic.md +159 -0
- package/pipeline/multi-agent-refs/features/research.md +150 -0
- package/pipeline/multi-agent-refs/features/review-decision.md +185 -0
- package/pipeline/multi-agent-refs/features/scaffold.md +160 -0
- package/pipeline/multi-agent-refs/features/security-audit.md +6 -2
- package/pipeline/multi-agent-refs/features/skill-conformance.md +4 -1
- package/pipeline/multi-agent-refs/features/stack-adapters.md +116 -0
- package/pipeline/multi-agent-refs/features/unattended-gates.md +316 -0
- package/pipeline/multi-agent-refs/features/unattended-security.md +731 -0
- package/pipeline/multi-agent-refs/features/url-enrichment.md +2 -2
- package/pipeline/multi-agent-refs/features/usage-reporting.md +127 -35
- package/pipeline/multi-agent-refs/features/verify-by-test.md +1 -1
- package/pipeline/multi-agent-refs/features/visual-evidence.md +7 -7
- package/pipeline/multi-agent-refs/keychain.md +6 -11
- package/pipeline/multi-agent-refs/payload-contracts.md +2 -2
- package/pipeline/multi-agent-refs/phases/log-format.md +2 -3
- package/pipeline/multi-agent-refs/phases/modes.md +10 -12
- package/pipeline/multi-agent-refs/phases/operations.md +11 -5
- package/pipeline/multi-agent-refs/phases/phase-0-init.md +73 -81
- package/pipeline/multi-agent-refs/phases/phase-1-plan.md +22 -22
- package/pipeline/multi-agent-refs/phases/phase-2-dev.md +42 -66
- package/pipeline/multi-agent-refs/phases/phase-3-review.md +45 -79
- package/pipeline/multi-agent-refs/phases/phase-4-commit.md +28 -38
- package/pipeline/multi-agent-refs/phases/phase-5-report.md +27 -25
- package/pipeline/multi-agent-refs/phases.md +1 -1
- package/pipeline/multi-agent-refs/picker-contract.md +1 -1
- package/pipeline/multi-agent-refs/progress-contract.md +13 -16
- package/pipeline/multi-agent-refs/readiness-review.md +1 -1
- package/pipeline/multi-agent-refs/research/engine.md +91 -0
- package/pipeline/multi-agent-refs/rules.md +6 -4
- package/pipeline/multi-agent-refs/setup/repo-discovery.md +2 -2
- package/pipeline/multi-agent-refs/tracker-contract.md +2 -2
- package/pipeline/multi-agent-refs/unattended-contract.md +68 -16
- package/pipeline/rules/figma-pipeline.md +12 -12
- package/pipeline/schemas/agent-state.schema.json +480 -18
- package/pipeline/schemas/analysis-spec.schema.json +4 -4
- package/pipeline/schemas/answer-request.schema.json +24 -0
- package/pipeline/schemas/answer-result.schema.json +28 -0
- package/pipeline/schemas/autopilot-config.schema.json +111 -13
- package/pipeline/schemas/command-parameters.schema.json +99 -0
- package/pipeline/schemas/constitution.schema.json +56 -0
- package/pipeline/schemas/contract-error.schema.json +52 -0
- package/pipeline/schemas/design-check-config.schema.json +5 -1
- package/pipeline/schemas/issues.schema.json +61 -0
- package/pipeline/schemas/launch-plan.schema.json +46 -0
- package/pipeline/schemas/launch-request.schema.json +45 -0
- package/pipeline/schemas/launch.json +61 -0
- package/pipeline/schemas/launch.schema.json +84 -0
- package/pipeline/schemas/phases.json +2 -2
- package/pipeline/schemas/phases.schema.json +68 -0
- package/pipeline/schemas/phone-devices.schema.json +61 -0
- package/pipeline/schemas/phone-signed-request.schema.json +67 -0
- package/pipeline/schemas/plan-critique.schema.json +99 -0
- package/pipeline/schemas/plan-todos.schema.json +7 -7
- package/pipeline/schemas/planning-output.schema.json +5 -0
- package/pipeline/schemas/pr-request.schema.json +46 -0
- package/pipeline/schemas/prefs.schema.json +79 -8
- package/pipeline/schemas/research-output.schema.json +118 -0
- package/pipeline/schemas/review-file-exclusions.schema.json +25 -0
- package/pipeline/schemas/reviewer-output.schema.json +40 -4
- package/pipeline/schemas/run-questions.json +392 -0
- package/pipeline/schemas/run-questions.schema.json +118 -0
- package/pipeline/schemas/runs-index.schema.json +189 -0
- package/pipeline/schemas/scaffold-manifest.schema.json +43 -0
- package/pipeline/schemas/secret-patterns.schema.json +28 -0
- package/pipeline/schemas/stack-adapters.json +527 -0
- package/pipeline/schemas/stack-adapters.schema.json +184 -0
- package/pipeline/schemas/token-budget.json +1 -1
- package/pipeline/schemas/token-budget.schema.json +26 -0
- package/pipeline/schemas/triage-output.schema.json +64 -3
- package/pipeline/schemas/unattended-policy.json +139 -0
- package/pipeline/schemas/unattended-policy.schema.json +73 -0
- package/pipeline/schemas/unattended-profile.json +248 -0
- package/pipeline/schemas/unattended-profile.schema.json +198 -0
- package/pipeline/schemas/worktrees.schema.json +51 -0
- package/pipeline/scripts/README.md +1 -0
- package/pipeline/scripts/_autopilot-config.mjs +130 -0
- package/pipeline/scripts/_autopilot-ops.mjs +567 -0
- package/pipeline/scripts/_autopilot-outcomes.mjs +174 -0
- package/pipeline/scripts/_command-contract.mjs +384 -0
- package/pipeline/scripts/_cost.mjs +40 -0
- package/pipeline/scripts/_notices.mjs +160 -0
- package/pipeline/scripts/_phone-auth.mjs +485 -0
- package/pipeline/scripts/_pre-existing.mjs +294 -0
- package/pipeline/scripts/_redact.mjs +77 -0
- package/pipeline/scripts/_run-paths.mjs +4 -2
- package/pipeline/scripts/_stack-adapter.mjs +678 -0
- package/pipeline/scripts/_stack-routing.mjs +1 -1
- package/pipeline/scripts/agent-guard.py +348 -37
- package/pipeline/scripts/agent-guard.sh +41 -13
- package/pipeline/scripts/analysis-story-tree.mjs +79 -3
- package/pipeline/scripts/answer-question.mjs +181 -0
- package/pipeline/scripts/audit-log-rotate.sh +1 -4
- package/pipeline/scripts/audit-log.sh +4 -4
- package/pipeline/scripts/autopilot-arming.mjs +389 -21
- package/pipeline/scripts/autopilot-awake.mjs +255 -0
- package/pipeline/scripts/autopilot-intake.mjs +137 -36
- package/pipeline/scripts/autopilot-menubar.swift +156 -44
- package/pipeline/scripts/autopilot-publish.mjs +1625 -0
- package/pipeline/scripts/autopilot-runner.mjs +1678 -222
- package/pipeline/scripts/autopilot-status.sh +198 -33
- package/pipeline/scripts/build-lock.sh +120 -0
- package/pipeline/scripts/build-references.mjs +4 -1
- package/pipeline/scripts/build-stack-plugins.mjs +59 -22
- package/pipeline/scripts/capture-flush.sh +1 -1
- package/pipeline/scripts/capture-resume.sh +13 -9
- package/pipeline/scripts/check-derived-drift.mjs +52 -11
- package/pipeline/scripts/commands.mjs +88 -0
- package/pipeline/scripts/constitution.mjs +362 -0
- package/pipeline/scripts/contract-server.mjs +776 -0
- package/pipeline/scripts/cost-analyze.mjs +89 -39
- package/pipeline/scripts/diff-explain.mjs +12 -1
- package/pipeline/scripts/doctor.mjs +77 -28
- package/pipeline/scripts/evidence-gate.mjs +192 -12
- package/pipeline/scripts/feedback-send.mjs +4 -2
- package/pipeline/scripts/gate-ledger.mjs +449 -0
- package/pipeline/scripts/gc-abandoned.sh +132 -13
- package/pipeline/scripts/gen-facts.mjs +31 -15
- package/pipeline/scripts/gen-mode-dispatch.mjs +3 -3
- package/pipeline/scripts/github-ssh-setup.sh +140 -29
- package/pipeline/scripts/graph-mermaid.mjs +4 -1
- package/pipeline/scripts/issues.mjs +236 -0
- package/pipeline/scripts/jira-attach.sh +6 -2
- package/pipeline/scripts/jira-search.sh +4 -3
- package/pipeline/scripts/keychain-save.sh +125 -24
- package/pipeline/scripts/keychain.py +63 -93
- package/pipeline/scripts/launch-request.mjs +747 -0
- package/pipeline/scripts/localize-commands.mjs +4 -10
- package/pipeline/scripts/log-metric.sh +6 -5
- package/pipeline/scripts/maturity-followup.mjs +13 -4
- package/pipeline/scripts/memory-save.sh +25 -0
- package/pipeline/scripts/migrate-prefs.mjs +4 -3
- package/pipeline/scripts/open-questions-gate.mjs +276 -0
- package/pipeline/scripts/phase-tracker.sh +41 -27
- package/pipeline/scripts/phase0-exit-gate.mjs +22 -4
- package/pipeline/scripts/phone-devices.mjs +224 -0
- package/pipeline/scripts/plan-coverage-gate.mjs +200 -66
- package/pipeline/scripts/plan-critique-gate.mjs +591 -0
- package/pipeline/scripts/pr-request.mjs +188 -0
- package/pipeline/scripts/pre-commit-check.sh +115 -4
- package/pipeline/scripts/probe-evidence-capability.sh +44 -5
- package/pipeline/scripts/record-phase.mjs +71 -0
- package/pipeline/scripts/render-agent-log-cost.sh +17 -2
- package/pipeline/scripts/render-cost-summary.sh +1 -1
- package/pipeline/scripts/render-work-summary.sh +1 -1
- package/pipeline/scripts/require-supported-version.sh +4 -1
- package/pipeline/scripts/research-gate.mjs +704 -0
- package/pipeline/scripts/review-decision-gate.mjs +403 -0
- package/pipeline/scripts/routine-registry.mjs +5 -2
- package/pipeline/scripts/runs-index.mjs +135 -27
- package/pipeline/scripts/scaffold-gate.mjs +393 -0
- package/pipeline/scripts/skill-conformance.mjs +25 -8
- package/pipeline/scripts/skill-siblings.mjs +2 -1
- package/pipeline/scripts/smoke-cross-cli-behavior.sh +32 -6
- package/pipeline/scripts/smoke-schema-validation.sh +6 -2
- package/pipeline/scripts/spec-consistency-gate.mjs +469 -0
- package/pipeline/scripts/symbol-existence-gate.mjs +450 -0
- package/pipeline/scripts/test-gap-scan.mjs +40 -2
- package/pipeline/scripts/test-integrity-gate.mjs +20 -4
- package/pipeline/scripts/test-strength.mjs +484 -0
- package/pipeline/scripts/test-summary.mjs +651 -0
- package/pipeline/scripts/triage-memory.mjs +49 -9
- package/pipeline/scripts/unattended_policy.py +2786 -0
- package/pipeline/scripts/uninstall.mjs +10 -10
- package/pipeline/scripts/update-issue-progress.sh +1 -1
- package/pipeline/scripts/usage-identity.mjs +288 -0
- package/pipeline/scripts/usage-register.mjs +185 -65
- package/pipeline/scripts/usage-report.mjs +230 -66
- package/pipeline/scripts/validate-complaint-doc.mjs +28 -10
- package/pipeline/scripts/validate-planning.mjs +6 -0
- package/pipeline/scripts/verify-citations.mjs +151 -38
- package/pipeline/scripts/verify.mjs +58 -18
- package/pipeline/scripts/worktree-prepare.sh +126 -0
- package/pipeline/scripts/worktrees.mjs +124 -0
- package/pipeline/scripts/write-state.mjs +48 -17
- package/pipeline/skills/.skill-manifest.json +222 -226
- package/pipeline/skills/.skills-index.json +77 -88
- package/pipeline/skills/shared/README.md +44 -45
- package/pipeline/skills/shared/core/apple-archive-compliance/SKILL.md +3 -2
- package/pipeline/skills/shared/core/google-play-compliance/SKILL.md +3 -2
- package/pipeline/skills/shared/core/multi-agent/SKILL.md +10 -9
- package/pipeline/skills/shared/core/multi-agent-analysis/SKILL.md +6 -5
- package/pipeline/skills/shared/core/multi-agent-analysis-jira/SKILL.md +11 -3
- package/pipeline/skills/shared/core/multi-agent-analysis-resolve/SKILL.md +2 -1
- package/pipeline/skills/shared/core/multi-agent-autopilot/SKILL.md +4 -3
- package/pipeline/skills/shared/core/multi-agent-autopilot-off/SKILL.md +32 -6
- package/pipeline/skills/shared/core/multi-agent-autopilot-on/SKILL.md +66 -8
- package/pipeline/skills/shared/core/multi-agent-autopilot-status/SKILL.md +27 -9
- package/pipeline/skills/shared/core/multi-agent-build-optimize/SKILL.md +2 -1
- package/pipeline/skills/shared/core/multi-agent-channels/SKILL.md +7 -4
- package/pipeline/skills/shared/core/multi-agent-complaint-analysis/SKILL.md +3 -2
- package/pipeline/skills/shared/core/multi-agent-create-jira/SKILL.md +2 -1
- package/pipeline/skills/shared/core/multi-agent-design-check/SKILL.md +7 -5
- package/pipeline/skills/shared/core/multi-agent-diff-explain/SKILL.md +2 -1
- package/pipeline/skills/shared/core/multi-agent-doctor/SKILL.md +3 -2
- package/pipeline/skills/shared/core/multi-agent-feedback/SKILL.md +2 -1
- package/pipeline/skills/shared/core/multi-agent-forget/SKILL.md +2 -1
- package/pipeline/skills/shared/core/multi-agent-garbage-collect/SKILL.md +2 -1
- package/pipeline/skills/shared/core/multi-agent-graph/SKILL.md +2 -1
- package/pipeline/skills/shared/core/multi-agent-help/SKILL.md +2 -1
- package/pipeline/skills/shared/core/multi-agent-ios-coding-standard/SKILL.md +2 -1
- package/pipeline/skills/shared/core/multi-agent-issue/SKILL.md +3 -2
- package/pipeline/skills/shared/core/multi-agent-jira/SKILL.md +2 -1
- package/pipeline/skills/shared/core/multi-agent-kill/SKILL.md +7 -4
- package/pipeline/skills/shared/core/multi-agent-language/SKILL.md +2 -1
- package/pipeline/skills/shared/core/multi-agent-log/SKILL.md +2 -1
- package/pipeline/skills/shared/core/multi-agent-manual-test/SKILL.md +2 -1
- package/pipeline/skills/shared/core/multi-agent-model/SKILL.md +2 -1
- package/pipeline/skills/shared/core/multi-agent-prune-logs/SKILL.md +2 -1
- package/pipeline/skills/shared/core/multi-agent-prune-prompts/SKILL.md +2 -1
- package/pipeline/skills/shared/core/multi-agent-purge/SKILL.md +2 -1
- package/pipeline/skills/shared/core/multi-agent-refactor/SKILL.md +2 -1
- package/pipeline/skills/shared/core/multi-agent-research/SKILL.md +35 -0
- package/pipeline/skills/shared/core/multi-agent-resume/SKILL.md +7 -3
- package/pipeline/skills/shared/core/multi-agent-review/SKILL.md +7 -5
- package/pipeline/skills/shared/core/multi-agent-review-analysis/SKILL.md +2 -1
- package/pipeline/skills/shared/core/multi-agent-review-issue/SKILL.md +3 -2
- package/pipeline/skills/shared/core/multi-agent-review-jira/SKILL.md +2 -1
- package/pipeline/skills/shared/core/multi-agent-route-off/SKILL.md +2 -1
- package/pipeline/skills/shared/core/multi-agent-route-on/SKILL.md +2 -1
- package/pipeline/skills/shared/core/multi-agent-route-status/SKILL.md +2 -1
- package/pipeline/skills/shared/core/multi-agent-routines/SKILL.md +2 -1
- package/pipeline/skills/shared/core/multi-agent-save/SKILL.md +3 -2
- package/pipeline/skills/shared/core/multi-agent-scaffold/SKILL.md +30 -0
- package/pipeline/skills/shared/core/multi-agent-scan/SKILL.md +2 -1
- package/pipeline/skills/shared/core/multi-agent-search/SKILL.md +4 -3
- package/pipeline/skills/shared/core/multi-agent-security-review/SKILL.md +2 -1
- package/pipeline/skills/shared/core/multi-agent-serve/SKILL.md +60 -0
- package/pipeline/skills/shared/core/multi-agent-setup/SKILL.md +8 -8
- package/pipeline/skills/shared/core/multi-agent-stack/SKILL.md +2 -1
- package/pipeline/skills/shared/core/multi-agent-status/SKILL.md +7 -8
- package/pipeline/skills/shared/core/multi-agent-steer/SKILL.md +2 -1
- package/pipeline/skills/shared/core/multi-agent-store-ready/SKILL.md +2 -1
- package/pipeline/skills/shared/core/multi-agent-sync/SKILL.md +10 -10
- package/pipeline/skills/shared/core/multi-agent-test/SKILL.md +2 -1
- package/pipeline/skills/shared/core/multi-agent-test-accessibility/SKILL.md +2 -1
- package/pipeline/skills/shared/core/multi-agent-test-dark-mode/SKILL.md +2 -1
- package/pipeline/skills/shared/core/multi-agent-test-dynamic-type/SKILL.md +2 -1
- package/pipeline/skills/shared/core/multi-agent-test-screenshots/SKILL.md +2 -1
- package/pipeline/skills/shared/core/multi-agent-testflight-validation/SKILL.md +2 -1
- package/pipeline/skills/shared/core/multi-agent-uninstall/SKILL.md +3 -2
- package/pipeline/skills/shared/core/multi-agent-update/SKILL.md +2 -1
- package/pipeline/skills/shared/external/NOTICE-avdlee-swiftui-agent-skill.md +46 -0
- package/pipeline/skills/shared/external/NOTICE-dimillian-skills.md +9 -3
- package/pipeline/skills/shared/external/NOTICE-paul-hudson-skills.md +54 -0
- package/pipeline/skills/shared/external/NOTICE-swift-ios-skills.md +9 -11
- package/pipeline/skills/shared/external/NOTICE-vibeship-spawner-skills.md +203 -0
- package/pipeline/skills/shared/external/NOTICE-xcode-build-skills.md +10 -3
- package/pipeline/skills/shared/external/accessibility-compliance-accessibility-audit/SKILL.md +131 -25
- package/pipeline/skills/shared/external/agent-introspection-debugging/SKILL.md +4 -3
- package/pipeline/skills/shared/external/alarmkit/SKILL.md +2 -1
- package/pipeline/skills/shared/external/android-architecture/SKILL.md +85 -76
- package/pipeline/skills/shared/external/android-build-quality-gates/SKILL.md +51 -3
- package/pipeline/skills/shared/external/android-build-quality-gates/references/patterns.md +9 -0
- package/pipeline/skills/shared/external/android-datastore/SKILL.md +4 -3
- package/pipeline/skills/shared/external/android-design-tokens-codegen/SKILL.md +4 -3
- package/pipeline/skills/shared/external/android-jetpack-compose-expert/SKILL.md +106 -181
- package/pipeline/skills/shared/external/android-jetpack-compose-expert/references/patterns.md +147 -0
- package/pipeline/skills/shared/external/android-mvi-viewmodel/SKILL.md +45 -4
- package/pipeline/skills/shared/external/android-performance/SKILL.md +27 -2
- package/pipeline/skills/shared/external/android-performance/references/patterns.md +37 -37
- package/pipeline/skills/shared/external/api-patterns/SKILL.md +111 -67
- package/pipeline/skills/shared/external/api-patterns/references/contract-details.md +128 -0
- package/pipeline/skills/shared/external/api-security-best-practices/SKILL.md +136 -186
- package/pipeline/skills/shared/external/api-security-best-practices/references/abuse-controls.md +140 -0
- package/pipeline/skills/shared/external/api-security-best-practices/references/identity-and-access.md +207 -0
- package/pipeline/skills/shared/external/api-security-best-practices/references/operations.md +66 -0
- package/pipeline/skills/shared/external/api-security-best-practices/references/request-handling.md +175 -0
- package/pipeline/skills/shared/external/app-clips/SKILL.md +2 -0
- package/pipeline/skills/shared/external/app-intents/SKILL.md +2 -0
- package/pipeline/skills/shared/external/app-store-changelog/SKILL.md +4 -3
- package/pipeline/skills/shared/external/app-store-optimization/SKILL.md +2 -0
- package/pipeline/skills/shared/external/app-store-review/SKILL.md +2 -0
- package/pipeline/skills/shared/external/apple-on-device-ai/SKILL.md +2 -0
- package/pipeline/skills/shared/external/architecture/SKILL.md +106 -39
- package/pipeline/skills/shared/external/architecture/references/adr-and-review.md +76 -0
- package/pipeline/skills/shared/external/authentication/SKILL.md +2 -0
- package/pipeline/skills/shared/external/avkit/SKILL.md +2 -0
- package/pipeline/skills/shared/external/background-processing/SKILL.md +2 -1
- package/pipeline/skills/shared/external/backlog/SKILL.md +3 -2
- package/pipeline/skills/shared/external/callkit-voip/SKILL.md +2 -0
- package/pipeline/skills/shared/external/callkit-voip/evals/evals.json +1 -1
- package/pipeline/skills/shared/external/ci-cd-pipelines/SKILL.md +3 -2
- package/pipeline/skills/shared/external/clean-code/SKILL.md +192 -90
- package/pipeline/skills/shared/external/cloudkit-sync/SKILL.md +2 -1
- package/pipeline/skills/shared/external/cloudkit-sync/evals/evals.json +1 -1
- package/pipeline/skills/shared/external/compose-components/SKILL.md +4 -4
- package/pipeline/skills/shared/external/compose-navigation/SKILL.md +22 -22
- package/pipeline/skills/shared/external/compose-navigation/references/patterns.md +10 -10
- package/pipeline/skills/shared/external/compose-testing/SKILL.md +13 -6
- package/pipeline/skills/shared/external/compose-testing/references/patterns.md +59 -59
- package/pipeline/skills/shared/external/contacts-framework/SKILL.md +2 -0
- package/pipeline/skills/shared/external/context-compression/SKILL.md +118 -250
- package/pipeline/skills/shared/external/core-bluetooth/SKILL.md +2 -0
- package/pipeline/skills/shared/external/core-data/SKILL.md +2 -0
- package/pipeline/skills/shared/external/core-motion/SKILL.md +2 -0
- package/pipeline/skills/shared/external/core-nfc/SKILL.md +2 -0
- package/pipeline/skills/shared/external/coreml/SKILL.md +2 -1
- package/pipeline/skills/shared/external/council/SKILL.md +2 -1
- package/pipeline/skills/shared/external/cryptokit/SKILL.md +2 -0
- package/pipeline/skills/shared/external/css-modern/SKILL.md +3 -2
- package/pipeline/skills/shared/external/database-patterns/SKILL.md +3 -2
- package/pipeline/skills/shared/external/debugging-instruments/SKILL.md +2 -0
- package/pipeline/skills/shared/external/debugging-strategies/SKILL.md +130 -20
- package/pipeline/skills/shared/external/debugging-strategies/references/hard-cases.md +54 -0
- package/pipeline/skills/shared/external/device-integrity/SKILL.md +2 -0
- package/pipeline/skills/shared/external/docker-expert/SKILL.md +137 -381
- package/pipeline/skills/shared/external/docker-expert/references/patterns.md +98 -0
- package/pipeline/skills/shared/external/energykit/SKILL.md +2 -1
- package/pipeline/skills/shared/external/eventkit-calendar/SKILL.md +3 -1
- package/pipeline/skills/shared/external/eventkit-calendar/evals/evals.json +2 -2
- package/pipeline/skills/shared/external/fastapi-pro/SKILL.md +116 -185
- package/pipeline/skills/shared/external/fastapi-pro/references/app-structure.md +235 -0
- package/pipeline/skills/shared/external/fastapi-pro/references/testing-and-deployment.md +69 -0
- package/pipeline/skills/shared/external/firebase/SKILL.md +4 -3
- package/pipeline/skills/shared/external/github-actions-templates/SKILL.md +151 -293
- package/pipeline/skills/shared/external/github-actions-templates/references/patterns.md +96 -0
- package/pipeline/skills/shared/external/healthkit/SKILL.md +2 -0
- package/pipeline/skills/shared/external/hig-components-content/SKILL.md +69 -72
- package/pipeline/skills/shared/external/hig-components-content/references/content-views.md +111 -0
- package/pipeline/skills/shared/external/hig-components-layout/SKILL.md +77 -86
- package/pipeline/skills/shared/external/hig-components-layout/references/containers.md +98 -0
- package/pipeline/skills/shared/external/hig-components-status/SKILL.md +150 -78
- package/pipeline/skills/shared/external/hig-components-system/SKILL.md +76 -97
- package/pipeline/skills/shared/external/hig-components-system/references/surfaces.md +98 -0
- package/pipeline/skills/shared/external/hig-foundations/SKILL.md +48 -76
- package/pipeline/skills/shared/external/hig-foundations/references/foundations-detail.md +132 -0
- package/pipeline/skills/shared/external/hig-inputs/SKILL.md +147 -106
- package/pipeline/skills/shared/external/hig-patterns/SKILL.md +61 -77
- package/pipeline/skills/shared/external/hig-patterns/references/patterns.md +92 -0
- package/pipeline/skills/shared/external/hig-platforms/SKILL.md +147 -77
- package/pipeline/skills/shared/external/hig-technologies/SKILL.md +61 -121
- package/pipeline/skills/shared/external/hig-technologies/references/technologies.md +108 -0
- package/pipeline/skills/shared/external/homekit-matter/SKILL.md +2 -0
- package/pipeline/skills/shared/external/homekit-matter/evals/evals.json +1 -1
- package/pipeline/skills/shared/external/html-semantic/SKILL.md +3 -2
- package/pipeline/skills/shared/external/humanizer/SKILL.md +2 -1
- package/pipeline/skills/shared/external/ios-accessibility/SKILL.md +2 -0
- package/pipeline/skills/shared/external/ios-coding-standard/SKILL.md +2 -1
- package/pipeline/skills/shared/external/ios-coding-standard/references/STANDARD.md +14 -2
- package/pipeline/skills/shared/external/ios-coding-standard/references/rules.yml +2 -2
- package/pipeline/skills/shared/external/ios-debugger-agent/SKILL.md +4 -3
- package/pipeline/skills/shared/external/ios-localization/SKILL.md +8 -6
- package/pipeline/skills/shared/external/ios-localization/evals/evals.json +2 -2
- package/pipeline/skills/shared/external/ios-localization/references/string-catalogs.md +11 -11
- package/pipeline/skills/shared/external/ios-module-structure/SKILL.md +2 -1
- package/pipeline/skills/shared/external/ios-networking/SKILL.md +2 -0
- package/pipeline/skills/shared/external/ios-simulator/SKILL.md +2 -0
- package/pipeline/skills/shared/external/kotlin-coroutines-expert/SKILL.md +102 -193
- package/pipeline/skills/shared/external/kotlin-coroutines-expert/references/patterns.md +103 -0
- package/pipeline/skills/shared/external/live-activities/SKILL.md +4 -2
- package/pipeline/skills/shared/external/live-activities/evals/evals.json +2 -2
- package/pipeline/skills/shared/external/localization-reuse-map/SKILL.md +7 -9
- package/pipeline/skills/shared/external/localization-reuse-map/reference/sources-and-recipes.md +3 -3
- package/pipeline/skills/shared/external/localization-reuse-map/scripts/resolve-new-values.py +2 -2
- package/pipeline/skills/shared/external/macos-menubar-tuist-app/SKILL.md +4 -3
- package/pipeline/skills/shared/external/macos-spm-app-packaging/SKILL.md +5 -3
- package/pipeline/skills/shared/external/mapkit-location/SKILL.md +2 -0
- package/pipeline/skills/shared/external/mapkit-location/evals/evals.json +1 -1
- package/pipeline/skills/shared/external/metrickit-diagnostics/SKILL.md +2 -0
- package/pipeline/skills/shared/external/metrickit-diagnostics/evals/evals.json +1 -1
- package/pipeline/skills/shared/external/monorepo-architect/SKILL.md +149 -46
- package/pipeline/skills/shared/external/monorepo-architect/references/patterns.md +70 -0
- package/pipeline/skills/shared/external/musickit-audio/SKILL.md +2 -0
- package/pipeline/skills/shared/external/musickit-audio/evals/evals.json +1 -1
- package/pipeline/skills/shared/external/natural-language/SKILL.md +2 -0
- package/pipeline/skills/shared/external/nextjs-app-router/SKILL.md +3 -2
- package/pipeline/skills/shared/external/nodejs-backend-patterns/SKILL.md +91 -21
- package/pipeline/skills/shared/external/nodejs-backend-patterns/references/runtime-patterns.md +247 -0
- package/pipeline/skills/shared/external/nodejs-backend-patterns/references/testing-and-frameworks.md +57 -0
- package/pipeline/skills/shared/external/observability-engineer/SKILL.md +155 -232
- package/pipeline/skills/shared/external/observability-engineer/references/patterns.md +75 -0
- package/pipeline/skills/shared/external/passkit-wallet/SKILL.md +2 -0
- package/pipeline/skills/shared/external/passkit-wallet/evals/evals.json +2 -2
- package/pipeline/skills/shared/external/passkit-wallet/references/wallet-passes.md +18 -18
- package/pipeline/skills/shared/external/pdfkit/SKILL.md +2 -1
- package/pipeline/skills/shared/external/pencilkit-drawing/SKILL.md +2 -0
- package/pipeline/skills/shared/external/pencilkit-drawing/evals/evals.json +1 -1
- package/pipeline/skills/shared/external/permissionkit/SKILL.md +2 -0
- package/pipeline/skills/shared/external/photos-camera-media/SKILL.md +2 -1
- package/pipeline/skills/shared/external/push-notifications/SKILL.md +2 -0
- package/pipeline/skills/shared/external/python-patterns/SKILL.md +3 -2
- package/pipeline/skills/shared/external/react-best-practices/SKILL.md +3 -2
- package/pipeline/skills/shared/external/realitykit-ar/SKILL.md +2 -2
- package/pipeline/skills/shared/external/realitykit-ar/evals/evals.json +1 -1
- package/pipeline/skills/shared/external/rest-api-design/SKILL.md +3 -2
- package/pipeline/skills/shared/external/retrofit-networking/SKILL.md +7 -7
- package/pipeline/skills/shared/external/retrofit-networking/references/patterns.md +69 -69
- package/pipeline/skills/shared/external/room-database/references/patterns.md +126 -126
- package/pipeline/skills/shared/external/search-first/SKILL.md +2 -1
- package/pipeline/skills/shared/external/security-review/SKILL.md +4 -3
- package/pipeline/skills/shared/external/shareplay-activities/SKILL.md +2 -2
- package/pipeline/skills/shared/external/signal-community/SKILL.md +1 -1
- package/pipeline/skills/shared/external/skill-creator/SKILL.md +2 -1
- package/pipeline/skills/shared/external/speech-recognition/SKILL.md +2 -1
- package/pipeline/skills/shared/external/spm-build-analysis/SKILL.md +2 -0
- package/pipeline/skills/shared/external/storekit/SKILL.md +2 -0
- package/pipeline/skills/shared/external/swift-api-design-guidelines/SKILL.md +2 -0
- package/pipeline/skills/shared/external/swift-architecture/SKILL.md +2 -1
- package/pipeline/skills/shared/external/swift-charts/SKILL.md +2 -1
- package/pipeline/skills/shared/external/swift-codable/SKILL.md +2 -0
- package/pipeline/skills/shared/external/swift-concurrency/SKILL.md +3 -1
- package/pipeline/skills/shared/external/swift-concurrency-expert/SKILL.md +5 -4
- package/pipeline/skills/shared/external/swift-concurrency-pro/SKILL.md +2 -1
- package/pipeline/skills/shared/external/swift-formatstyle/SKILL.md +2 -0
- package/pipeline/skills/shared/external/swift-language/SKILL.md +3 -1
- package/pipeline/skills/shared/external/swift-security/SKILL.md +2 -1
- package/pipeline/skills/shared/external/swift-testing/SKILL.md +2 -0
- package/pipeline/skills/shared/external/swift-testing-pro/SKILL.md +2 -1
- package/pipeline/skills/shared/external/swift-testing-pro/references/new-features.md +10 -0
- package/pipeline/skills/shared/external/swiftdata/SKILL.md +2 -0
- package/pipeline/skills/shared/external/swiftdata-pro/SKILL.md +2 -1
- package/pipeline/skills/shared/external/swiftlint/SKILL.md +2 -0
- package/pipeline/skills/shared/external/swiftui-animation/SKILL.md +2 -0
- package/pipeline/skills/shared/external/swiftui-expert-skill/SKILL.md +3 -1
- package/pipeline/skills/shared/external/swiftui-gestures/SKILL.md +2 -0
- package/pipeline/skills/shared/external/swiftui-layout-components/SKILL.md +2 -0
- package/pipeline/skills/shared/external/swiftui-liquid-glass/SKILL.md +2 -0
- package/pipeline/skills/shared/external/swiftui-navigation/SKILL.md +2 -0
- package/pipeline/skills/shared/external/swiftui-patterns/SKILL.md +3 -1
- package/pipeline/skills/shared/external/swiftui-performance/SKILL.md +3 -1
- package/pipeline/skills/shared/external/swiftui-performance-audit/SKILL.md +5 -4
- package/pipeline/skills/shared/external/swiftui-pro/SKILL.md +2 -1
- package/pipeline/skills/shared/external/swiftui-ui-patterns/SKILL.md +5 -4
- package/pipeline/skills/shared/external/swiftui-uikit-interop/SKILL.md +2 -0
- package/pipeline/skills/shared/external/swiftui-view-refactor/SKILL.md +4 -3
- package/pipeline/skills/shared/external/swiftui-webkit/SKILL.md +2 -0
- package/pipeline/skills/shared/external/tailwind-css/SKILL.md +3 -2
- package/pipeline/skills/shared/external/testing-backend/SKILL.md +3 -2
- package/pipeline/skills/shared/external/tipkit/SKILL.md +2 -0
- package/pipeline/skills/shared/external/typescript-patterns/SKILL.md +3 -2
- package/pipeline/skills/shared/external/vision-framework/SKILL.md +2 -0
- package/pipeline/skills/shared/external/vue-composition/SKILL.md +3 -2
- package/pipeline/skills/shared/external/weatherkit/SKILL.md +2 -0
- package/pipeline/skills/shared/external/web-accessibility/SKILL.md +3 -2
- package/pipeline/skills/shared/external/web-performance/SKILL.md +3 -2
- package/pipeline/skills/shared/external/web-testing/SKILL.md +3 -2
- package/pipeline/skills/shared/external/widgetkit/SKILL.md +2 -0
- package/pipeline/skills/shared/external/widgetkit/references/widgetkit-advanced.md +1 -1
- package/pipeline/skills/shared/external/xcode-build-benchmark/SKILL.md +2 -0
- package/pipeline/skills/shared/external/xcode-build-fixer/SKILL.md +2 -0
- package/pipeline/skills/shared/external/xcode-build-orchestrator/SKILL.md +2 -0
- package/pipeline/skills/shared/external/xcode-compilation-analyzer/SKILL.md +2 -0
- package/pipeline/skills/shared/external/xcode-project-analyzer/SKILL.md +2 -0
- package/pipeline/skills/skills-index.md +40 -41
- package/docs/token-budget-history.md +0 -24
- package/pipeline/skills/shared/external/agentflow/SKILL.md +0 -199
- package/pipeline/skills/shared/external/android-ui-verification/SKILL.md +0 -66
- package/pipeline/skills/shared/external/api-security-best-practices/references/auth.md +0 -299
- package/pipeline/skills/shared/external/api-security-best-practices/references/input-validation.md +0 -255
- package/pipeline/skills/shared/external/api-security-best-practices/references/rate-limiting.md +0 -167
- package/pipeline/skills/shared/external/closed-loop-delivery/SKILL.md +0 -116
- package/pipeline/skills/shared/external/ios-developer/SKILL.md +0 -216
|
@@ -0,0 +1,567 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* _autopilot-ops.mjs - the operations a runner tick owns besides the run.
|
|
3
|
+
*
|
|
4
|
+
* SLEEP LOCK a sleep inhibitor held while a session is in flight, spawned
|
|
5
|
+
* by the tick and killed by the pid it spawned.
|
|
6
|
+
* PARALLEL CAP how many runner-launched sessions may be working at once.
|
|
7
|
+
* DAILY JOBS the cleanup report and the opt-in digest, each at most once
|
|
8
|
+
* per local date, stamped in daily.json.
|
|
9
|
+
*
|
|
10
|
+
* Each one is a pure function or a small effect with an injectable binary, so
|
|
11
|
+
* test/autopilot-operations.test.mjs drives them without a real `caffeinate`,
|
|
12
|
+
* `claude` or tracker.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import { spawn, spawnSync } from "node:child_process";
|
|
16
|
+
import {
|
|
17
|
+
closeSync,
|
|
18
|
+
existsSync,
|
|
19
|
+
linkSync,
|
|
20
|
+
openSync,
|
|
21
|
+
readFileSync,
|
|
22
|
+
readdirSync,
|
|
23
|
+
realpathSync,
|
|
24
|
+
rmSync,
|
|
25
|
+
statSync,
|
|
26
|
+
writeFileSync,
|
|
27
|
+
writeSync,
|
|
28
|
+
} from "node:fs";
|
|
29
|
+
import { basename, join } from "node:path";
|
|
30
|
+
import { classifyOutcome, isBlocked, isParked } from "./_autopilot-outcomes.mjs";
|
|
31
|
+
import { attemptSpend } from "./_cost.mjs";
|
|
32
|
+
import { logsRoot as defaultLogsRoot } from "./_run-paths.mjs";
|
|
33
|
+
import { readRegularJson } from "../lib/regular-file.mjs";
|
|
34
|
+
|
|
35
|
+
// Reports and digests are small, but one a day for a year is a directory
|
|
36
|
+
// nobody reads. The newest ones answer every question anyone asks of them.
|
|
37
|
+
const KEEP_DAILY_FILES = 14;
|
|
38
|
+
|
|
39
|
+
function readJson(p, fallback) {
|
|
40
|
+
try {
|
|
41
|
+
return JSON.parse(readFileSync(p, "utf-8"));
|
|
42
|
+
} catch {
|
|
43
|
+
return fallback;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function writePrivate(p, text) {
|
|
48
|
+
writeFileSync(p, text, { mode: 0o600 });
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function pidAlive(pid) {
|
|
52
|
+
if (!Number.isInteger(pid) || pid <= 0) return false;
|
|
53
|
+
try {
|
|
54
|
+
process.kill(pid, 0);
|
|
55
|
+
return true;
|
|
56
|
+
} catch (e) {
|
|
57
|
+
return e.code === "EPERM";
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// ---- SLEEP LOCK ------------------------------------------------------------
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* The inhibitor, tied to the runner's pid so it cannot outlive the runner even
|
|
65
|
+
* when the runner is killed before it can release it.
|
|
66
|
+
*
|
|
67
|
+
* `caffeinate -s -w <pid>`. `-s` is the assertion that prevents SYSTEM sleep
|
|
68
|
+
* and is honoured only on AC power. `-w` makes caffeinate exit by itself when
|
|
69
|
+
* the runner exits. Holding sleep off between ticks, and on battery, is the
|
|
70
|
+
* awake agent's job (autopilot-awake.mjs), not this lock's: this one covers the
|
|
71
|
+
* run in flight whether or not that agent is installed.
|
|
72
|
+
*
|
|
73
|
+
* macOS only (ADR-0012): any other platform holds nothing.
|
|
74
|
+
*
|
|
75
|
+
* @returns {{bin:string, args:string[]}|null}
|
|
76
|
+
*/
|
|
77
|
+
export function inhibitorCommand(platform, runnerPid, env = process.env) {
|
|
78
|
+
if (platform !== "darwin") return null;
|
|
79
|
+
return { bin: env.MA_AP_INHIBIT_BIN || "caffeinate", args: ["-s", "-w", String(runnerPid)] };
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Hold the sleep inhibitor for the rest of this session. `release()` is
|
|
84
|
+
* idempotent, runs on process exit as well, and kills only the process group
|
|
85
|
+
* this call spawned. inhibitor.json records it for autopilot-status and for the
|
|
86
|
+
* next tick's sweep.
|
|
87
|
+
*
|
|
88
|
+
* @returns {{held:boolean, pid:number|null, release:() => void}}
|
|
89
|
+
*/
|
|
90
|
+
export function holdSleep({
|
|
91
|
+
root,
|
|
92
|
+
env = process.env,
|
|
93
|
+
platform = env.MA_AP_PLATFORM || process.platform,
|
|
94
|
+
runnerPid = process.pid,
|
|
95
|
+
log = () => {},
|
|
96
|
+
} = {}) {
|
|
97
|
+
const none = { held: false, pid: null, release() {} };
|
|
98
|
+
const cmd = inhibitorCommand(platform, runnerPid, env);
|
|
99
|
+
if (!cmd) {
|
|
100
|
+
log(`sleep lock: not held on ${platform} - macOS only, see ADR-0012`);
|
|
101
|
+
return none;
|
|
102
|
+
}
|
|
103
|
+
let child;
|
|
104
|
+
try {
|
|
105
|
+
// Its own process group, so release() takes the waiter with it.
|
|
106
|
+
child = spawn(cmd.bin, cmd.args, { stdio: "ignore", detached: true });
|
|
107
|
+
} catch (e) {
|
|
108
|
+
log(`sleep lock: ${cmd.bin} could not start (${e?.message || e}) - running without it`);
|
|
109
|
+
return none;
|
|
110
|
+
}
|
|
111
|
+
child.on("error", () => {});
|
|
112
|
+
if (!child.pid) {
|
|
113
|
+
log(`sleep lock: ${cmd.bin} is not available - running without it`);
|
|
114
|
+
return none;
|
|
115
|
+
}
|
|
116
|
+
child.unref();
|
|
117
|
+
const record = join(root, "inhibitor.json");
|
|
118
|
+
try {
|
|
119
|
+
writePrivate(
|
|
120
|
+
record,
|
|
121
|
+
JSON.stringify({
|
|
122
|
+
pid: child.pid,
|
|
123
|
+
runnerPid,
|
|
124
|
+
bin: cmd.bin,
|
|
125
|
+
platform,
|
|
126
|
+
since: Math.floor(Date.now() / 1000),
|
|
127
|
+
}),
|
|
128
|
+
);
|
|
129
|
+
} catch {
|
|
130
|
+
// The inhibitor works without its record; only the status line loses it.
|
|
131
|
+
}
|
|
132
|
+
let released = false;
|
|
133
|
+
const release = () => {
|
|
134
|
+
if (released) return;
|
|
135
|
+
released = true;
|
|
136
|
+
process.removeListener("exit", release);
|
|
137
|
+
for (const target of [-child.pid, child.pid]) {
|
|
138
|
+
try {
|
|
139
|
+
process.kill(target, "SIGTERM");
|
|
140
|
+
break;
|
|
141
|
+
} catch {
|
|
142
|
+
// Already gone, or the group signal is not permitted here: try the pid.
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
rmSync(record, { force: true });
|
|
146
|
+
};
|
|
147
|
+
process.on("exit", release);
|
|
148
|
+
return { held: true, pid: child.pid, release };
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* A record left by a runner that is gone. Its inhibitor exits by itself when
|
|
153
|
+
* that runner's pid does, so this only matters when it did not; it is killed
|
|
154
|
+
* only when the recorded pid is still running the recorded binary.
|
|
155
|
+
*/
|
|
156
|
+
export function sweepInhibitor(root, log = () => {}) {
|
|
157
|
+
const record = join(root, "inhibitor.json");
|
|
158
|
+
const rec = readJson(record, null);
|
|
159
|
+
if (!rec) return;
|
|
160
|
+
if (rec.runnerPid !== process.pid && pidAlive(rec.runnerPid)) return;
|
|
161
|
+
if (pidAlive(rec.pid)) {
|
|
162
|
+
const r = spawnSync("ps", ["-p", String(rec.pid), "-o", "command="], { encoding: "utf-8" });
|
|
163
|
+
if (rec.bin && String(r.stdout || "").includes(basename(rec.bin))) {
|
|
164
|
+
try {
|
|
165
|
+
process.kill(rec.pid, "SIGTERM");
|
|
166
|
+
log(`sleep lock: stopped inhibitor pid ${rec.pid} left by runner ${rec.runnerPid}`);
|
|
167
|
+
} catch {
|
|
168
|
+
// It exited between the check and the signal.
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
rmSync(record, { force: true });
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// ---- PARALLEL CAP ----------------------------------------------------------
|
|
176
|
+
|
|
177
|
+
/** The configured cap, or null for unlimited (absent, null, or not a positive integer). */
|
|
178
|
+
export function parallelCap(config) {
|
|
179
|
+
const n = config?.maxParallelAgents;
|
|
180
|
+
return Number.isInteger(n) && n > 0 ? n : null;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Runner-launched sessions working right now: every entry the runner still
|
|
185
|
+
* supervises, plus each parked entry whose session is busy again (a person
|
|
186
|
+
* answered and it resumed). A parked session that is waiting is idle and does
|
|
187
|
+
* not count; one that has left the list does not either.
|
|
188
|
+
*
|
|
189
|
+
* @returns {number|undefined} undefined when the agent list cannot be read
|
|
190
|
+
*/
|
|
191
|
+
export function agentsInFlight(running, agentsJson) {
|
|
192
|
+
const entries = Array.isArray(running) ? running : [];
|
|
193
|
+
const active = entries.filter((r) => r?.state !== "parked").length;
|
|
194
|
+
const parked = entries.filter((r) => r?.state === "parked" && r.sessionId);
|
|
195
|
+
if (!parked.length) return active;
|
|
196
|
+
let rows;
|
|
197
|
+
try {
|
|
198
|
+
const doc = JSON.parse(String(agentsJson ?? ""));
|
|
199
|
+
rows = Array.isArray(doc) ? doc : doc?.agents || [];
|
|
200
|
+
} catch {
|
|
201
|
+
return undefined;
|
|
202
|
+
}
|
|
203
|
+
const working = new Set(["busy", "running"]);
|
|
204
|
+
const busy = parked.filter((p) =>
|
|
205
|
+
rows.some((a) => a?.sessionId === p.sessionId && working.has(a?.status)),
|
|
206
|
+
).length;
|
|
207
|
+
return active + busy;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* Create the claim file only if it does not exist. Two ticks that both passed
|
|
212
|
+
* the recovery check race here, and exactly one of them wins; the other sees
|
|
213
|
+
* false and leaves without launching.
|
|
214
|
+
*
|
|
215
|
+
* The claim is written whole to a private temp file first and then hard-linked
|
|
216
|
+
* into place: link() fails with EEXIST when the name is taken, so the exclusive
|
|
217
|
+
* create and the content arrive together, and no reader ever sees an empty or
|
|
218
|
+
* half-written claim.
|
|
219
|
+
*/
|
|
220
|
+
export function claimExclusive(path, obj) {
|
|
221
|
+
const tmp = `${path}.${process.pid}.${Date.now()}.claim`;
|
|
222
|
+
const fd = openSync(tmp, "wx", 0o600);
|
|
223
|
+
try {
|
|
224
|
+
writeSync(fd, JSON.stringify(obj, null, 2));
|
|
225
|
+
} finally {
|
|
226
|
+
closeSync(fd);
|
|
227
|
+
}
|
|
228
|
+
try {
|
|
229
|
+
linkSync(tmp, path);
|
|
230
|
+
return true;
|
|
231
|
+
} catch (e) {
|
|
232
|
+
if (e.code === "EEXIST") return false;
|
|
233
|
+
throw e;
|
|
234
|
+
} finally {
|
|
235
|
+
rmSync(tmp, { force: true });
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
// ---- DAILY JOBS ------------------------------------------------------------
|
|
240
|
+
|
|
241
|
+
/** YYYY-MM-DD in the machine's own time zone: a day is the operator's day. */
|
|
242
|
+
export function localDate(ms = Date.now()) {
|
|
243
|
+
const d = new Date(ms);
|
|
244
|
+
const pad = (n) => String(n).padStart(2, "0");
|
|
245
|
+
return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}`;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
export function dailyDue(root, job, date) {
|
|
249
|
+
return readJson(join(root, "daily.json"), {})?.[job] !== date;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
export function markDaily(root, job, date) {
|
|
253
|
+
const p = join(root, "daily.json");
|
|
254
|
+
const doc = readJson(p, {}) || {};
|
|
255
|
+
writePrivate(p, JSON.stringify({ ...doc, [job]: date }, null, 2));
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
/** Keep the newest KEEP_DAILY_FILES of `<prefix><date>.json|.md`. */
|
|
259
|
+
export function pruneDaily(root, prefix, keep = KEEP_DAILY_FILES) {
|
|
260
|
+
let names;
|
|
261
|
+
try {
|
|
262
|
+
names = readdirSync(root);
|
|
263
|
+
} catch {
|
|
264
|
+
return;
|
|
265
|
+
}
|
|
266
|
+
const dates = [
|
|
267
|
+
...new Set(
|
|
268
|
+
names
|
|
269
|
+
.map((n) => new RegExp(`^${prefix}(\\d{4}-\\d{2}-\\d{2})\\.(json|md)$`).exec(n)?.[1])
|
|
270
|
+
.filter(Boolean),
|
|
271
|
+
),
|
|
272
|
+
].sort();
|
|
273
|
+
for (const d of dates.slice(0, Math.max(0, dates.length - keep))) {
|
|
274
|
+
for (const ext of ["json", "md"]) rmSync(join(root, `${prefix}${d}.${ext}`), { force: true });
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
/** The newest `<prefix><date>.json` in root, or "" when there is none. */
|
|
279
|
+
export function latestDaily(root, prefix) {
|
|
280
|
+
try {
|
|
281
|
+
const hit = readdirSync(root)
|
|
282
|
+
.filter((n) => n.startsWith(prefix) && n.endsWith(".json"))
|
|
283
|
+
.sort()
|
|
284
|
+
.at(-1);
|
|
285
|
+
return hit ? join(root, hit) : "";
|
|
286
|
+
} catch {
|
|
287
|
+
return "";
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
function realOr(p) {
|
|
292
|
+
try {
|
|
293
|
+
return realpathSync(p);
|
|
294
|
+
} catch {
|
|
295
|
+
return p;
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
function readJsonl(p) {
|
|
300
|
+
if (!existsSync(p)) return [];
|
|
301
|
+
try {
|
|
302
|
+
return readFileSync(p, "utf-8")
|
|
303
|
+
.split("\n")
|
|
304
|
+
.filter(Boolean)
|
|
305
|
+
.map((l) => {
|
|
306
|
+
try {
|
|
307
|
+
return JSON.parse(l);
|
|
308
|
+
} catch {
|
|
309
|
+
return null;
|
|
310
|
+
}
|
|
311
|
+
})
|
|
312
|
+
.filter(Boolean);
|
|
313
|
+
} catch {
|
|
314
|
+
return [];
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
/**
|
|
319
|
+
* The runner task ids whose item is parked now: the item's newest attempt that
|
|
320
|
+
* ran (blocked rows are refusals, not attempts) is a PARKED outcome. A parked
|
|
321
|
+
* run leaves the queue once its session ends, so the queue alone does not hold
|
|
322
|
+
* its worktree; the attempt log does.
|
|
323
|
+
*/
|
|
324
|
+
function parkedTaskIds(root) {
|
|
325
|
+
const latest = new Map();
|
|
326
|
+
const itemOf = new Map();
|
|
327
|
+
for (const r of readJsonl(join(root, "attempted.jsonl"))) {
|
|
328
|
+
if (!r || typeof r.outcome !== "string" || isBlocked(r.outcome)) continue;
|
|
329
|
+
const item = `${r.source}:${r.id}`;
|
|
330
|
+
latest.set(item, r.outcome);
|
|
331
|
+
if (typeof r.taskId === "string" && r.taskId) itemOf.set(r.taskId, item);
|
|
332
|
+
}
|
|
333
|
+
const parked = new Set();
|
|
334
|
+
for (const [taskId, item] of itemOf) if (isParked(latest.get(item))) parked.add(taskId);
|
|
335
|
+
return parked;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
/**
|
|
339
|
+
* For every worktree a run state names, the session of the newest such state.
|
|
340
|
+
* Run directories are nested under their project or flat (_run-paths.mjs).
|
|
341
|
+
*/
|
|
342
|
+
function worktreeSessions(logs) {
|
|
343
|
+
const newest = new Map();
|
|
344
|
+
const offer = (statePath) => {
|
|
345
|
+
const doc = readRegularJson(statePath, null);
|
|
346
|
+
if (!doc || typeof doc.worktreePath !== "string" || !doc.worktreePath) return;
|
|
347
|
+
let mtimeMs;
|
|
348
|
+
try {
|
|
349
|
+
mtimeMs = statSync(statePath).mtimeMs;
|
|
350
|
+
} catch {
|
|
351
|
+
return;
|
|
352
|
+
}
|
|
353
|
+
const wt = realOr(doc.worktreePath);
|
|
354
|
+
const cur = newest.get(wt);
|
|
355
|
+
if (!cur || mtimeMs > cur.mtimeMs) {
|
|
356
|
+
newest.set(wt, {
|
|
357
|
+
sessionId: typeof doc.sessionId === "string" ? doc.sessionId : null,
|
|
358
|
+
mtimeMs,
|
|
359
|
+
});
|
|
360
|
+
}
|
|
361
|
+
};
|
|
362
|
+
const dirs = (p) => {
|
|
363
|
+
try {
|
|
364
|
+
return readdirSync(p, { withFileTypes: true }).filter((e) => e.isDirectory());
|
|
365
|
+
} catch {
|
|
366
|
+
return [];
|
|
367
|
+
}
|
|
368
|
+
};
|
|
369
|
+
for (const e of dirs(logs)) {
|
|
370
|
+
const top = join(logs, e.name);
|
|
371
|
+
if (existsSync(join(top, "agent-state.json"))) offer(join(top, "agent-state.json"));
|
|
372
|
+
for (const k of dirs(top)) {
|
|
373
|
+
const p = join(top, k.name, "agent-state.json");
|
|
374
|
+
if (existsSync(p)) offer(p);
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
return newest;
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
/**
|
|
381
|
+
* Worktrees this runner created, from worktrees.jsonl, minus any the queue
|
|
382
|
+
* still holds and any whose item is parked for a person. The ledger is written
|
|
383
|
+
* only for a run whose state carries the session id the runner launched it
|
|
384
|
+
* with - the same proof `createdWorktree` rests on - and that proof is checked
|
|
385
|
+
* again here: a path is listed only while the newest run state naming it
|
|
386
|
+
* carries the ledger row's session. A worktree an attended run later opened at
|
|
387
|
+
* the same path, a hand-made one, or one no state names never appears here.
|
|
388
|
+
*/
|
|
389
|
+
export function runnerWorktrees(root, queue, { logsRoot = defaultLogsRoot() } = {}) {
|
|
390
|
+
const held = new Set(
|
|
391
|
+
(queue?.running || []).map((r) => (r?.worktree ? realOr(r.worktree) : null)).filter(Boolean),
|
|
392
|
+
);
|
|
393
|
+
const parked = parkedTaskIds(root);
|
|
394
|
+
const rows = readJsonl(join(root, "worktrees.jsonl")).filter(
|
|
395
|
+
(row) => typeof row?.worktree === "string" && row.worktree,
|
|
396
|
+
);
|
|
397
|
+
for (const row of rows) {
|
|
398
|
+
if (typeof row.taskId === "string" && parked.has(row.taskId)) held.add(realOr(row.worktree));
|
|
399
|
+
}
|
|
400
|
+
const owners = worktreeSessions(logsRoot);
|
|
401
|
+
const seen = new Set();
|
|
402
|
+
for (const row of rows) {
|
|
403
|
+
const p = realOr(row.worktree);
|
|
404
|
+
if (held.has(p) || !existsSync(p)) continue;
|
|
405
|
+
const owner = owners.get(p);
|
|
406
|
+
if (!owner || !row.sessionId || owner.sessionId !== row.sessionId) continue;
|
|
407
|
+
seen.add(p);
|
|
408
|
+
}
|
|
409
|
+
return [...seen].sort();
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
const GC_ACTIONS = {
|
|
413
|
+
"would-remove": "would be removed",
|
|
414
|
+
"would-stash": "would be stashed (uncommitted work), worktree kept",
|
|
415
|
+
"would-mark": "would be marked abandoned (no removable worktree)",
|
|
416
|
+
removed: "removed",
|
|
417
|
+
"remove-failed": "could not be removed (git worktree remove refused), directory kept",
|
|
418
|
+
stashed: "stashed (uncommitted work), worktree kept",
|
|
419
|
+
marked: "marked abandoned (no removable worktree)",
|
|
420
|
+
unattributed: "no run state on record, left alone",
|
|
421
|
+
};
|
|
422
|
+
|
|
423
|
+
/**
|
|
424
|
+
* The daily cleanup report. gc-abandoned.sh decides, restricted with `--only`
|
|
425
|
+
* to the worktrees this runner created; its dry run is the default, and
|
|
426
|
+
* `gc.autoDelete: true` is the only thing that adds `--yes`.
|
|
427
|
+
*
|
|
428
|
+
* @returns {{json:string, md:string, candidates:number, mode:string}}
|
|
429
|
+
*/
|
|
430
|
+
export function gcReport({ root, gcScript, logsRoot, config, queue, date, log = () => {} }) {
|
|
431
|
+
const mode = config?.gc?.autoDelete === true ? "delete" : "dry-run";
|
|
432
|
+
const worktrees = runnerWorktrees(root, queue, { logsRoot });
|
|
433
|
+
let candidates = [];
|
|
434
|
+
let summary = "no runner-created worktree on record";
|
|
435
|
+
if (worktrees.length && !existsSync(gcScript)) {
|
|
436
|
+
summary = `${basename(gcScript)} is not installed beside the runner`;
|
|
437
|
+
} else if (worktrees.length) {
|
|
438
|
+
const only = join(root, "gc-only.txt");
|
|
439
|
+
const out = join(root, "gc-report.jsonl");
|
|
440
|
+
writePrivate(only, worktrees.join("\n") + "\n");
|
|
441
|
+
rmSync(out, { force: true });
|
|
442
|
+
const r = spawnSync(
|
|
443
|
+
"bash",
|
|
444
|
+
[
|
|
445
|
+
gcScript,
|
|
446
|
+
"--logs",
|
|
447
|
+
logsRoot,
|
|
448
|
+
"--only",
|
|
449
|
+
only,
|
|
450
|
+
"--report",
|
|
451
|
+
out,
|
|
452
|
+
...(mode === "delete" ? ["--yes"] : []),
|
|
453
|
+
],
|
|
454
|
+
{ encoding: "utf-8", stdio: ["ignore", "pipe", "pipe"], timeout: 10 * 60 * 1000 },
|
|
455
|
+
);
|
|
456
|
+
candidates = readJsonl(out);
|
|
457
|
+
summary =
|
|
458
|
+
String(r.stdout || "")
|
|
459
|
+
.trim()
|
|
460
|
+
.split("\n")
|
|
461
|
+
.at(-1) || `gc-abandoned exited ${r.status}`;
|
|
462
|
+
rmSync(out, { force: true });
|
|
463
|
+
rmSync(only, { force: true });
|
|
464
|
+
}
|
|
465
|
+
const doc = {
|
|
466
|
+
date,
|
|
467
|
+
generatedAt: new Date().toISOString(),
|
|
468
|
+
mode,
|
|
469
|
+
autoDelete: mode === "delete",
|
|
470
|
+
considered: worktrees.length,
|
|
471
|
+
candidates,
|
|
472
|
+
summary,
|
|
473
|
+
};
|
|
474
|
+
const json = join(root, `gc-report-${date}.json`);
|
|
475
|
+
const md = join(root, `gc-report-${date}.md`);
|
|
476
|
+
writePrivate(json, JSON.stringify(doc, null, 2) + "\n");
|
|
477
|
+
const lines = [
|
|
478
|
+
`# Cleanup report ${date}`,
|
|
479
|
+
"",
|
|
480
|
+
`Mode: ${mode === "delete" ? "delete (gc.autoDelete is on)" : "dry run - nothing was removed"}.`,
|
|
481
|
+
`Runner-created worktrees considered: ${worktrees.length}.`,
|
|
482
|
+
"",
|
|
483
|
+
...(candidates.length
|
|
484
|
+
? candidates.map(
|
|
485
|
+
(c) =>
|
|
486
|
+
`- ${c.path || c.task}: ${GC_ACTIONS[c.action] || c.action} - ${c.reason}` +
|
|
487
|
+
(Number.isFinite(c.idleDays) ? `, idle ${c.idleDays}d` : ""),
|
|
488
|
+
)
|
|
489
|
+
: ["Nothing to clean up."]),
|
|
490
|
+
"",
|
|
491
|
+
summary,
|
|
492
|
+
"",
|
|
493
|
+
];
|
|
494
|
+
writePrivate(md, lines.join("\n"));
|
|
495
|
+
pruneDaily(root, "gc-report-");
|
|
496
|
+
log(`cleanup report: ${candidates.length} candidate(s), ${mode} - ${json}`);
|
|
497
|
+
return { json, md, candidates: candidates.length, mode };
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
/**
|
|
501
|
+
* One day of the queue, from the attempt log and the parked entries.
|
|
502
|
+
*
|
|
503
|
+
* `items` counts distinct items that RAN: a blocked row is an arming refusal,
|
|
504
|
+
* written on every tick it holds, and counting it would make a quiet day look
|
|
505
|
+
* busy. Blocked rows are still counted in `byClass`, since a day of refusals is
|
|
506
|
+
* the thing the digest exists to surface. Cost follows the spend ceiling's
|
|
507
|
+
* rule (_cost.mjs attemptSpend): each run once, at its latest cumulative
|
|
508
|
+
* figure, and an unmeasured run as unknown, never as zero.
|
|
509
|
+
*/
|
|
510
|
+
export function buildDigest({ lines, queue, now = Date.now() / 1000, ceilingUsd = null }) {
|
|
511
|
+
const cutoff = now - 86400;
|
|
512
|
+
const rows = (lines || []).filter((r) => r && Number(r.at || 0) >= cutoff);
|
|
513
|
+
const byClass = {};
|
|
514
|
+
const byOutcome = {};
|
|
515
|
+
const items = new Set();
|
|
516
|
+
const prsOpened = [];
|
|
517
|
+
for (const r of rows) {
|
|
518
|
+
const cls = classifyOutcome(r.outcome) || "unclassified";
|
|
519
|
+
byClass[cls] = (byClass[cls] || 0) + 1;
|
|
520
|
+
byOutcome[r.outcome] = (byOutcome[r.outcome] || 0) + 1;
|
|
521
|
+
if (!isBlocked(r.outcome)) items.add(`${r.source}:${r.id}`);
|
|
522
|
+
if (typeof r.prUrl === "string" && r.prUrl && !prsOpened.includes(r.prUrl)) {
|
|
523
|
+
prsOpened.push(r.prUrl);
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
const { usd, unknown: unknownRuns } = attemptSpend(rows);
|
|
527
|
+
const awaitingAnswer = (queue?.running || [])
|
|
528
|
+
.filter((e) => e?.state === "parked")
|
|
529
|
+
.map((e) => ({ id: e.id, outcome: e.outcome ?? null, parkedAt: e.parkedAt ?? null }));
|
|
530
|
+
return {
|
|
531
|
+
windowStart: new Date(cutoff * 1000).toISOString(),
|
|
532
|
+
windowEnd: new Date(now * 1000).toISOString(),
|
|
533
|
+
items: items.size,
|
|
534
|
+
attempts: rows.filter((r) => !isBlocked(r.outcome)).length,
|
|
535
|
+
byClass,
|
|
536
|
+
byOutcome,
|
|
537
|
+
prsOpened,
|
|
538
|
+
awaitingAnswer,
|
|
539
|
+
cost: { usd: Math.round(usd * 100) / 100, unknownRuns, ceilingUsd },
|
|
540
|
+
};
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
/** The digest as a comment body: plain text, every number named. */
|
|
544
|
+
export function digestText(d) {
|
|
545
|
+
const classes = Object.entries(d.byClass)
|
|
546
|
+
.map(([k, v]) => `${k} ${v}`)
|
|
547
|
+
.join(", ");
|
|
548
|
+
const out = [
|
|
549
|
+
`Autopilot daily digest (${d.windowStart} to ${d.windowEnd})`,
|
|
550
|
+
"",
|
|
551
|
+
`${d.items} item(s) attempted in ${d.attempts} run(s). Outcomes: ${classes || "none"}.`,
|
|
552
|
+
`Cost: $${d.cost.usd.toFixed(2)}` +
|
|
553
|
+
(d.cost.ceilingUsd !== null ? ` of a $${Number(d.cost.ceilingUsd).toFixed(2)} ceiling` : "") +
|
|
554
|
+
(d.cost.unknownRuns ? `, ${d.cost.unknownRuns} run(s) not measured` : "") +
|
|
555
|
+
".",
|
|
556
|
+
"",
|
|
557
|
+
`PRs opened (${d.prsOpened.length}):`,
|
|
558
|
+
...(d.prsOpened.length ? d.prsOpened.map((u) => `- ${u}`) : ["- none"]),
|
|
559
|
+
"",
|
|
560
|
+
`Waiting for an answer (${d.awaitingAnswer.length}):`,
|
|
561
|
+
...(d.awaitingAnswer.length
|
|
562
|
+
? d.awaitingAnswer.map((p) => `- ${p.id}${p.outcome ? ` (${p.outcome})` : ""}`)
|
|
563
|
+
: ["- none"]),
|
|
564
|
+
"",
|
|
565
|
+
];
|
|
566
|
+
return out.join("\n");
|
|
567
|
+
}
|