peaks-loop 4.0.0-beta.9 → 4.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +572 -234
- package/README-en.md +67 -30
- package/README.md +67 -30
- package/agents/karpathy-reviewer.md +33 -0
- package/bin/peaks.js +0 -0
- package/dist/cli/cli-helpers.d.ts +31 -6
- package/dist/cli/cli-helpers.js +48 -25
- package/dist/cli/commands/_register.d.ts +3 -0
- package/dist/cli/commands/_register.js +139 -0
- package/dist/cli/commands/_super.d.ts +10 -0
- package/dist/cli/commands/_super.js +97 -0
- package/dist/cli/commands/adapter-commands-s2a.js +1 -1
- package/dist/cli/commands/asset-commands.js +33 -16
- package/dist/cli/commands/audit-commands.js +2 -2
- package/dist/cli/commands/autonomous-swarm-commands.d.ts +12 -0
- package/dist/cli/commands/autonomous-swarm-commands.js +120 -0
- package/dist/cli/commands/bee-commands.js +1 -1
- package/dist/cli/commands/capability-commands.js +1 -1
- package/dist/cli/commands/capability-worker-config-sc-commands.js +0 -2
- package/dist/cli/commands/changeset-commands.d.ts +13 -0
- package/dist/cli/commands/changeset-commands.js +46 -0
- package/dist/cli/commands/code-commands.d.ts +11 -1
- package/dist/cli/commands/code-commands.js +19 -699
- package/dist/cli/commands/code-job-shape-commands.d.ts +9 -0
- package/dist/cli/commands/code-job-shape-commands.js +164 -0
- package/dist/cli/commands/code-mode-gate-commands.d.ts +9 -0
- package/dist/cli/commands/code-mode-gate-commands.js +223 -0
- package/dist/cli/commands/code-review-commands.js +2 -2
- package/dist/cli/commands/code-run-command.d.ts +12 -0
- package/dist/cli/commands/code-run-command.js +68 -0
- package/dist/cli/commands/code-runtime-commands.d.ts +11 -0
- package/dist/cli/commands/code-runtime-commands.js +391 -0
- package/dist/cli/commands/codegraph-commands.js +1 -1
- package/dist/cli/commands/compact-command.js +53 -1
- package/dist/cli/commands/complexity-commands.js +1 -1
- package/dist/cli/commands/config-commands.js +4 -75
- package/dist/cli/commands/container-commands.d.ts +28 -0
- package/dist/cli/commands/container-commands.js +221 -0
- package/dist/cli/commands/context-commands.js +3 -2
- package/dist/cli/commands/core/artifacts-command.js +1 -1
- package/dist/cli/commands/core/binding-commands.js +1 -1
- package/dist/cli/commands/core/doctor-command.js +63 -3
- package/dist/cli/commands/core/memory-command.js +1 -1
- package/dist/cli/commands/core/profile-command.js +1 -1
- package/dist/cli/commands/core/proxy-command.js +1 -1
- package/dist/cli/commands/core/session-command.js +9 -7
- package/dist/cli/commands/core/skill-command.js +7 -4
- package/dist/cli/commands/core/standards-command.js +1 -1
- package/dist/cli/commands/cron-commands.d.ts +57 -0
- package/dist/cli/commands/cron-commands.js +257 -0
- package/dist/cli/commands/cron-scheduler-commands.d.ts +47 -0
- package/dist/cli/commands/cron-scheduler-commands.js +284 -0
- package/dist/cli/commands/dashboard-commands.d.ts +4 -0
- package/dist/cli/commands/dashboard-commands.js +10 -0
- package/dist/cli/commands/dashboard-long-run.d.ts +25 -0
- package/dist/cli/commands/dashboard-long-run.js +122 -0
- package/dist/cli/commands/dashboard-summary.d.ts +12 -0
- package/dist/cli/commands/dashboard-summary.js +58 -0
- package/dist/cli/commands/dispatch-commands.d.ts +0 -7
- package/dist/cli/commands/dispatch-commands.js +372 -8
- package/dist/cli/commands/dispatch-from-dag.js +4 -1
- package/dist/cli/commands/doc-commands.js +1 -1
- package/dist/cli/commands/ecc-commands.d.ts +30 -0
- package/dist/cli/commands/ecc-commands.js +119 -0
- package/dist/cli/commands/evolution-commands.js +1 -1
- package/dist/cli/commands/feedback-commands.js +1 -1
- package/dist/cli/commands/final-review-commands.js +1 -1
- package/dist/cli/commands/fixture-commands.js +1 -1
- package/dist/cli/commands/fork-commands.js +1 -1
- package/dist/cli/commands/gate-commands.js +128 -6
- package/dist/cli/commands/governance-classify-contract-commands.d.ts +21 -0
- package/dist/cli/commands/governance-classify-contract-commands.js +273 -0
- package/dist/cli/commands/heartbeat-commands.js +76 -5
- package/dist/cli/commands/heartbeat-watch-command.d.ts +31 -0
- package/dist/cli/commands/heartbeat-watch-command.js +146 -0
- package/dist/cli/commands/hook-handle.js +1 -1
- package/dist/cli/commands/hooks-commands.js +126 -10
- package/dist/cli/commands/ide-commands.js +1 -1
- package/dist/cli/commands/impact-commands.js +1 -1
- package/dist/cli/commands/job-commands.js +34 -1
- package/dist/cli/commands/lease-metrics-commands.d.ts +67 -0
- package/dist/cli/commands/lease-metrics-commands.js +231 -0
- package/dist/cli/commands/lease-stats-commands.d.ts +65 -0
- package/dist/cli/commands/lease-stats-commands.js +107 -0
- package/dist/cli/commands/legacy-commands.js +1 -1
- package/dist/cli/commands/log-commands.js +1 -1
- package/dist/cli/commands/loop-commands.js +1 -1
- package/dist/cli/commands/loop-eval-commands.js +1 -1
- package/dist/cli/commands/memory-commands.js +1 -1
- package/dist/cli/commands/migrate-1-4-1-command.js +1 -1
- package/dist/cli/commands/migrate-v2-10-to-v2-11-command.js +1 -1
- package/dist/cli/commands/mut-commands.d.ts +1 -1
- package/dist/cli/commands/mut-commands.js +1 -5
- package/dist/cli/commands/observability-commands.js +1 -1
- package/dist/cli/commands/openspec-commands.js +65 -4
- package/dist/cli/commands/perf-audit-commands.js +3 -3
- package/dist/cli/commands/perf-commands.js +1 -1
- package/dist/cli/commands/polyrepo-commands.js +1 -1
- package/dist/cli/commands/prd-blocks-commands.js +1 -1
- package/dist/cli/commands/prd-commands.js +4 -2
- package/dist/cli/commands/preferences-commands.js +23 -3
- package/dist/cli/commands/project-commands.js +1 -1
- package/dist/cli/commands/qa-business-review-commands.js +1 -1
- package/dist/cli/commands/qa-commands.d.ts +1 -1
- package/dist/cli/commands/qa-commands.js +39 -3
- package/dist/cli/commands/release-commands.d.ts +15 -0
- package/dist/cli/commands/release-commands.js +113 -21
- package/dist/cli/commands/request-commands.js +1 -1
- package/dist/cli/commands/retrospective-commands.js +1 -1
- package/dist/cli/commands/reviewer-commands.js +1 -1
- package/dist/cli/commands/role-commands.js +1 -1
- package/dist/cli/commands/runtime-commands-helpers.d.ts +7 -0
- package/dist/cli/commands/runtime-commands-helpers.js +24 -0
- package/dist/cli/commands/runtime-commands.js +1 -2
- package/dist/cli/commands/sc-commands.js +4 -2
- package/dist/cli/commands/scan-commands.js +1 -1
- package/dist/cli/commands/security-audit-commands.js +3 -3
- package/dist/cli/commands/sediment-commands.js +10 -1
- package/dist/cli/commands/session-24h-mode.d.ts +26 -0
- package/dist/cli/commands/session-24h-mode.js +196 -0
- package/dist/cli/commands/share-commands.d.ts +19 -0
- package/dist/cli/commands/share-commands.js +121 -2
- package/dist/cli/commands/skill-conformance-commands.js +1 -1
- package/dist/cli/commands/skill-loop-engineering-readiness-commands.js +1 -1
- package/dist/cli/commands/skill-search-commands.js +4 -1
- package/dist/cli/commands/slice-commands.js +1 -1
- package/dist/cli/commands/slice-integrate-commands.js +1 -1
- package/dist/cli/commands/slice-review-commands.js +1 -1
- package/dist/cli/commands/smoke-commands.js +1 -1
- package/dist/cli/commands/sop-commands.js +1 -1
- package/dist/cli/commands/spill-demo-command.d.ts +7 -0
- package/dist/cli/commands/spill-demo-command.js +29 -0
- package/dist/cli/commands/statusline-commands.d.ts +12 -0
- package/dist/cli/commands/statusline-commands.js +79 -17
- package/dist/cli/commands/sub-agent-commands.js +2 -1
- package/dist/cli/commands/sub-agent-shared.d.ts +10 -0
- package/dist/cli/commands/sub-agent-shared.js +24 -1
- package/dist/cli/commands/swarm-commands.d.ts +13 -0
- package/dist/cli/commands/swarm-commands.js +77 -0
- package/dist/cli/commands/tech-commands.d.ts +64 -0
- package/dist/cli/commands/tech-commands.js +195 -0
- package/dist/cli/commands/understand-commands.js +1 -1
- package/dist/cli/commands/upgrade-commands.d.ts +2 -0
- package/dist/cli/commands/upgrade-commands.js +40 -5
- package/dist/cli/commands/user-touchpoint-commands.js +1 -1
- package/dist/cli/commands/verdict-aggregate-command.js +1 -1
- package/dist/cli/commands/vm-commands.d.ts +31 -0
- package/dist/cli/commands/vm-commands.js +274 -0
- package/dist/cli/commands/workflow-commands.js +24 -1
- package/dist/cli/commands/workflow-plan-commands.js +1 -1
- package/dist/cli/commands/workspace/init-command.js +1 -1
- package/dist/cli/commands/workspace/migrate-command.js +1 -1
- package/dist/cli/commands/workspace/reconcile-command.js +1 -1
- package/dist/cli/commands/worktree-auth-commands.d.ts +29 -0
- package/dist/cli/commands/worktree-auth-commands.js +652 -0
- package/dist/cli/index.js +58 -13
- package/dist/cli/program.d.ts +2 -2
- package/dist/cli/program.js +86 -281
- package/dist/services/24h-mode/decider.d.ts +103 -0
- package/dist/services/24h-mode/decider.js +144 -0
- package/dist/services/24h-mode/index.d.ts +12 -0
- package/dist/services/24h-mode/index.js +12 -0
- package/dist/services/24h-mode/state.d.ts +48 -0
- package/dist/services/24h-mode/state.js +51 -0
- package/dist/services/24h-mode/store.d.ts +27 -0
- package/dist/services/24h-mode/store.js +119 -0
- package/dist/services/artifacts/artifact-prerequisites.js +1 -1
- package/dist/services/artifacts/request-artifact-service.js +1 -1
- package/dist/services/artifacts/workspace-service.js +1 -1
- package/dist/services/audit/enforcers/lint-bee-runtime-contract.d.ts +25 -0
- package/dist/services/audit/enforcers/lint-bee-runtime-contract.js +91 -0
- package/dist/services/audit/enforcers/lint-peaks-code-runtime.d.ts +16 -0
- package/dist/services/audit/enforcers/lint-peaks-code-runtime.js +31 -0
- package/dist/services/audit/enforcers/lint-peaks-skill-runtime.d.ts +23 -0
- package/dist/services/audit/enforcers/lint-peaks-skill-runtime.js +59 -0
- package/dist/services/audit/enforcers/lint-peaks-ui-sc-txt-runtime.d.ts +28 -0
- package/dist/services/audit/enforcers/lint-peaks-ui-sc-txt-runtime.js +86 -0
- package/dist/services/audit/enforcers/lint-prd-artifact-handoff.d.ts +16 -0
- package/dist/services/audit/enforcers/lint-prd-artifact-handoff.js +41 -0
- package/dist/services/audit/enforcers/lint-prd-source-snapshot.d.ts +27 -0
- package/dist/services/audit/enforcers/lint-prd-source-snapshot.js +47 -0
- package/dist/services/audit/enforcers/lint-qa-gateguard-and-runtime.d.ts +19 -0
- package/dist/services/audit/enforcers/lint-qa-gateguard-and-runtime.js +47 -0
- package/dist/services/audit/enforcers/lint-rd-handoff-coverage.d.ts +18 -0
- package/dist/services/audit/enforcers/lint-rd-handoff-coverage.js +70 -0
- package/dist/services/audit/enforcers/lint-skill-presence-mandatory.d.ts +25 -0
- package/dist/services/audit/enforcers/lint-skill-presence-mandatory.js +53 -0
- package/dist/services/audit/red-line-catalog-p2-a.d.ts +0 -6
- package/dist/services/audit/red-line-catalog-p2-a.js +230 -1
- package/dist/services/audit/static-service.d.ts +32 -25
- package/dist/services/audit/static-service.js +30 -109
- package/dist/services/audit-independent/index.d.ts +2 -0
- package/dist/services/audit-independent/index.js +2 -0
- package/dist/services/autonomous-swarm/autonomous-swarm-service.d.ts +127 -0
- package/dist/services/autonomous-swarm/autonomous-swarm-service.js +446 -0
- package/dist/services/changeset/changeset-check-service.d.ts +22 -0
- package/dist/services/changeset/changeset-check-service.js +47 -0
- package/dist/services/code/auto-compact-modes.d.ts +30 -0
- package/dist/services/code/auto-compact-modes.js +38 -0
- package/dist/services/code/auto-compact-orchestrator.d.ts +43 -5
- package/dist/services/code/auto-compact-orchestrator.js +101 -21
- package/dist/services/code/dag-orchestrator.d.ts +30 -1
- package/dist/services/code/dag-orchestrator.js +106 -1
- package/dist/services/code/status-line-renderer.d.ts +2 -0
- package/dist/services/code/status-line-renderer.js +21 -3
- package/dist/services/code-review/ocr-service.d.ts +1 -1
- package/dist/services/code-review/ocr-service.js +6 -3
- package/dist/services/compact-history/compact-history-service.d.ts +41 -0
- package/dist/services/compact-history/compact-history-service.js +62 -0
- package/dist/services/compact-statusline/compact-statusline-service.d.ts +16 -0
- package/dist/services/compact-statusline/compact-statusline-service.js +83 -0
- package/dist/services/config/config-service.d.ts +1 -12
- package/dist/services/config/config-service.js +3 -72
- package/dist/services/config/config-types.d.ts +2 -4
- package/dist/services/config/config-types.js +1 -1
- package/dist/services/container/container-lease.d.ts +67 -0
- package/dist/services/container/container-lease.js +135 -0
- package/dist/services/context/auto-compact-dispatcher.d.ts +40 -5
- package/dist/services/context/auto-compact-dispatcher.js +32 -102
- package/dist/services/context/auto-compact-reader.js +1 -1
- package/dist/services/context/auto-compact-types.d.ts +27 -2
- package/dist/services/context/auto-compact-types.js +8 -1
- package/dist/services/context/build-dispatch-system-prompt.d.ts +75 -0
- package/dist/services/context/build-dispatch-system-prompt.js +117 -0
- package/dist/services/context/collector.d.ts +2 -2
- package/dist/services/context/context-builder.d.ts +4 -4
- package/dist/services/context/context-schema.d.ts +48 -48
- package/dist/services/context/ide-detect.d.ts +4 -0
- package/dist/services/context/ide-detect.js +24 -0
- package/dist/services/context/main-session-monitor.d.ts +1 -4
- package/dist/services/context/main-session-monitor.js +1 -24
- package/dist/services/context/memory-index-reader.d.ts +8 -0
- package/dist/services/context/memory-index-reader.js +57 -0
- package/dist/services/context/memory-lru-cache.d.ts +12 -0
- package/dist/services/context/memory-lru-cache.js +60 -0
- package/dist/services/context/memory-preflight-config.d.ts +19 -0
- package/dist/services/context/memory-preflight-config.js +25 -0
- package/dist/services/context/memory-preflight-service.d.ts +19 -0
- package/dist/services/context/memory-preflight-service.js +120 -0
- package/dist/services/context/spillover-store.d.ts +23 -0
- package/dist/services/context/spillover-store.js +155 -0
- package/dist/services/context/spillover-types.d.ts +18 -0
- package/dist/services/context/spillover-types.js +1 -0
- package/dist/services/crystallization/crystallization-service.d.ts +22 -5
- package/dist/services/crystallization/crystallization-service.js +8 -14
- package/dist/services/crystallization/crystallization-types.d.ts +6 -6
- package/dist/services/crystallization/evidence-brief-builder.d.ts +1 -1
- package/dist/services/crystallization/index.d.ts +13 -0
- package/dist/services/crystallization/index.js +13 -0
- package/dist/services/dashboard/project-dashboard-service.js +2 -2
- package/dist/services/dispatch/await-batch.d.ts +70 -0
- package/dist/services/dispatch/await-batch.js +272 -0
- package/dist/services/dispatch/batch-counter.js +1 -1
- package/dist/services/dispatch/dispatch-record-writer.d.ts +126 -4
- package/dist/services/dispatch/dispatch-record-writer.js +239 -9
- package/dist/services/dispatch/slice-dag.d.ts +46 -0
- package/dist/services/dispatch/slice-dag.js +6 -0
- package/dist/services/dispatch/stage-enum.d.ts +27 -0
- package/dist/services/dispatch/stage-enum.js +48 -0
- package/dist/services/dispatch/startup-timeout.d.ts +86 -0
- package/dist/services/dispatch/startup-timeout.js +97 -0
- package/dist/services/dispatch/sub-agent-dispatcher.js +40 -89
- package/dist/services/dispatch/test-tool-detection.d.ts +25 -1
- package/dist/services/dispatch/test-tool-detection.js +80 -0
- package/dist/services/doctor/doctor-service/checks/check-id-schema.d.ts +18 -0
- package/dist/services/doctor/doctor-service/checks/check-id-schema.js +64 -0
- package/dist/services/doctor/doctor-service/checks/codegraph-capability.d.ts +14 -0
- package/dist/services/doctor/doctor-service/checks/codegraph-capability.js +69 -0
- package/dist/services/doctor/doctor-service/checks/dist-source-version.d.ts +32 -0
- package/dist/services/doctor/doctor-service/checks/dist-source-version.js +110 -0
- package/dist/services/doctor/doctor-service/checks/gateguard-conflict.d.ts +24 -0
- package/dist/services/doctor/doctor-service/checks/gateguard-conflict.js +164 -0
- package/dist/services/doctor/doctor-service/checks/l3-memory-health.d.ts +16 -0
- package/dist/services/doctor/doctor-service/checks/l3-memory-health.js +57 -0
- package/dist/services/doctor/doctor-service/checks/l3-orphan-sessions.d.ts +19 -0
- package/dist/services/doctor/doctor-service/checks/l3-orphan-sessions.js +66 -0
- package/dist/services/doctor/doctor-service/checks/schema-validity.d.ts +10 -0
- package/dist/services/doctor/doctor-service/checks/schema-validity.js +33 -0
- package/dist/services/doctor/doctor-service/checks/skill-apply-note.d.ts +20 -0
- package/dist/services/doctor/doctor-service/checks/skill-apply-note.js +85 -0
- package/dist/services/doctor/doctor-service/checks/skill-existence.d.ts +13 -0
- package/dist/services/doctor/doctor-service/checks/skill-existence.js +30 -0
- package/dist/services/doctor/doctor-service/checks/skill-name-match.d.ts +10 -0
- package/dist/services/doctor/doctor-service/checks/skill-name-match.js +25 -0
- package/dist/services/doctor/doctor-service/checks/skill-parse.d.ts +10 -0
- package/dist/services/doctor/doctor-service/checks/skill-parse.js +19 -0
- package/dist/services/doctor/doctor-service/checks/skill-presence.d.ts +21 -0
- package/dist/services/doctor/doctor-service/checks/skill-presence.js +75 -0
- package/dist/services/doctor/doctor-service/checks/skill-runbook.d.ts +14 -0
- package/dist/services/doctor/doctor-service/checks/skill-runbook.js +47 -0
- package/dist/services/doctor/doctor-service/checks/statusline-install.d.ts +14 -0
- package/dist/services/doctor/doctor-service/checks/statusline-install.js +32 -0
- package/dist/services/doctor/doctor-service/checks/statusline-runtime.d.ts +13 -0
- package/dist/services/doctor/doctor-service/checks/statusline-runtime.js +30 -0
- package/dist/services/doctor/doctor-service/checks/user-config.d.ts +11 -0
- package/dist/services/doctor/doctor-service/checks/user-config.js +27 -0
- package/dist/services/doctor/doctor-service/checks/workspace-init.d.ts +16 -0
- package/dist/services/doctor/doctor-service/checks/workspace-init.js +34 -0
- package/dist/services/doctor/doctor-service/checks/workspace-layout.d.ts +33 -0
- package/dist/services/doctor/doctor-service/checks/workspace-layout.js +180 -0
- package/dist/services/doctor/doctor-service/index.d.ts +48 -0
- package/dist/services/doctor/doctor-service/index.js +254 -0
- package/dist/services/doctor/doctor-service/plugin-registry.d.ts +41 -0
- package/dist/services/doctor/doctor-service/plugin-registry.js +77 -0
- package/dist/services/doctor/doctor-service/report/final-summary.d.ts +20 -0
- package/dist/services/doctor/doctor-service/report/final-summary.js +29 -0
- package/dist/services/doctor/doctor-service/types.d.ts +245 -0
- package/dist/services/doctor/doctor-service/types.js +15 -0
- package/dist/services/doctor/doctor-service.d.ts +15 -146
- package/dist/services/doctor/doctor-service.js +22 -926
- package/dist/services/doctor/index.d.ts +22 -0
- package/dist/services/doctor/index.js +22 -0
- package/dist/services/env/shell-probe.d.ts +71 -0
- package/dist/services/env/shell-probe.js +161 -0
- package/dist/services/evolution/evolution-types.d.ts +39 -39
- package/dist/services/final-review/final-review-service.d.ts +11 -1
- package/dist/services/final-review/index.d.ts +2 -0
- package/dist/services/final-review/index.js +1 -0
- package/dist/services/hooks/auto-compact-hook-install.d.ts +4 -5
- package/dist/services/hooks/auto-compact-hook-install.js +4 -5
- package/dist/services/hooks/worktree-authorization-gate.d.ts +205 -0
- package/dist/services/hooks/worktree-authorization-gate.js +492 -0
- package/dist/services/ide/adapters/claude-code-adapter.js +2 -2
- package/dist/services/ide/adapters/qoder-adapter.d.ts +25 -0
- package/dist/services/ide/adapters/qoder-adapter.js +57 -0
- package/dist/services/ide/adapters/tongyi-lingma-adapter.d.ts +16 -0
- package/dist/services/ide/adapters/tongyi-lingma-adapter.js +45 -0
- package/dist/services/ide/ide-registry.js +6 -0
- package/dist/services/ide/ide-types.d.ts +1 -1
- package/dist/services/job/job-types.d.ts +56 -56
- package/dist/services/job-snapshot/index.d.ts +8 -0
- package/dist/services/job-snapshot/index.js +7 -0
- package/dist/services/{job/job-resource-snapshot.d.ts → job-snapshot/snapshot.d.ts} +1 -1
- package/dist/services/job-snapshot/types.d.ts +7 -0
- package/dist/services/job-snapshot/types.js +1 -0
- package/dist/services/karpathy-cost/karpathy-cost-check-service.d.ts +67 -0
- package/dist/services/karpathy-cost/karpathy-cost-check-service.js +108 -0
- package/dist/services/log/retention.d.ts +16 -0
- package/dist/services/log/retention.js +17 -0
- package/dist/services/loop/loop-bee-relation-types.d.ts +6 -6
- package/dist/services/loop/loop-release-types.d.ts +24 -24
- package/dist/services/loop/monotonic-runner.d.ts +17 -11
- package/dist/services/loop/monotonic-runner.js +118 -79
- package/dist/services/memory/project-memory-service/index/kind-dispatch.d.ts +8 -0
- package/dist/services/memory/project-memory-service/index/kind-dispatch.js +253 -0
- package/dist/services/memory/project-memory-service/index/ranking.d.ts +6 -0
- package/dist/services/memory/project-memory-service/index/ranking.js +171 -0
- package/dist/services/memory/project-memory-service/index/search.d.ts +34 -0
- package/dist/services/memory/project-memory-service/index/search.js +194 -0
- package/dist/services/memory/project-memory-service/index.d.ts +9 -0
- package/dist/services/memory/project-memory-service/index.js +24 -0
- package/dist/services/memory/project-memory-service/parsers/frontmatter.d.ts +9 -0
- package/dist/services/memory/project-memory-service/parsers/frontmatter.js +106 -0
- package/dist/services/memory/project-memory-service/parsers/markdown-pure.d.ts +7 -0
- package/dist/services/memory/project-memory-service/parsers/markdown-pure.js +96 -0
- package/dist/services/memory/project-memory-service/store/atomic-write.d.ts +5 -0
- package/dist/services/memory/project-memory-service/store/atomic-write.js +56 -0
- package/dist/services/memory/project-memory-service/store/paths.d.ts +8 -0
- package/dist/services/memory/project-memory-service/store/paths.js +116 -0
- package/dist/services/memory/project-memory-service/types.d.ts +128 -0
- package/dist/services/memory/project-memory-service/types.js +9 -0
- package/dist/services/memory/project-memory-service.d.ts +1 -167
- package/dist/services/memory/project-memory-service.js +19 -816
- package/dist/services/migrate-skill-name/schema.d.ts +2 -2
- package/dist/services/observability/aggregation.d.ts +27 -0
- package/dist/services/observability/aggregation.js +72 -0
- package/dist/services/observability/observability-service.d.ts +73 -7
- package/dist/services/observability/observability-service.js +87 -1
- package/dist/services/openspec/artifact-boundary.d.ts +138 -0
- package/dist/services/openspec/artifact-boundary.js +150 -0
- package/dist/services/openspec/coverage-evidence-reader.d.ts +191 -0
- package/dist/services/openspec/coverage-evidence-reader.js +386 -0
- package/dist/services/openspec/openspec-archive-service.d.ts +70 -0
- package/dist/services/openspec/openspec-archive-service.js +320 -8
- package/dist/services/openspec/openspec-bridge-service.js +1 -1
- package/dist/services/openspec/openspec-init-service.js +1 -1
- package/dist/services/openspec/openspec-propose-from-doctor-service.js +1 -1
- package/dist/services/openspec/openspec-render-service.js +7 -4
- package/dist/services/openspec/openspec-scan-service.js +1 -1
- package/dist/services/openspec/openspec-validate-service.js +5 -4
- package/dist/services/preferences/preferences-service.js +10 -0
- package/dist/services/preferences/preferences-types.d.ts +13 -0
- package/dist/services/preferences/preferences-types.js +11 -0
- package/dist/services/qa/screenshot-archive-service.d.ts +51 -0
- package/dist/services/qa/screenshot-archive-service.js +134 -0
- package/dist/services/rd/rd-service.js +10 -11
- package/dist/services/rd/types.d.ts +32 -32
- package/dist/services/rd-swarm/rd-swarm-service.d.ts +60 -0
- package/dist/services/rd-swarm/rd-swarm-service.js +93 -0
- package/dist/services/recommendations/capability-seed-items.js +0 -1
- package/dist/services/recommendations/capability-seed-mappings.js +0 -1
- package/dist/services/recommendations/capability-seed-sources.js +0 -1
- package/dist/services/release/version-precheck-service.d.ts +51 -0
- package/dist/services/release/version-precheck-service.js +257 -0
- package/dist/services/scan/acceptance-coverage-service.js +1 -1
- package/dist/services/scan/archetype-service.js +1 -1
- package/dist/services/scan/existing-system-service.js +1 -1
- package/dist/services/scan/libraries-service.js +1 -1
- package/dist/services/sediment/json-schema.d.ts +40 -40
- package/dist/services/share/bundle-types.d.ts +12 -12
- package/dist/services/share/run-state-contract.d.ts +2 -2
- package/dist/services/skill/skill-search-service.d.ts +12 -7
- package/dist/services/skill/skill-search-service.js +8 -4
- package/dist/services/skillhub/migrations/001-initial.sql +71 -0
- package/dist/services/skillhub/migrations/002-loop-release.sql +32 -0
- package/dist/services/skillhub/migrations/003-loop-bee-relation.sql +35 -0
- package/dist/services/skillhub/migrations/004-loop-bee-extension.sql +65 -0
- package/dist/services/skillhub/migrations/005-evolution-evaluation.sql +81 -0
- package/dist/services/skillhub/migrations/006-crystallization-event.sql +92 -0
- package/dist/services/skills/hooks-settings-service.d.ts +75 -0
- package/dist/services/skills/hooks-settings-service.js +230 -2
- package/dist/services/skills/skill-registry.d.ts +1 -0
- package/dist/services/skills/skill-registry.js +6 -4
- package/dist/services/skills/skill-runbook-service.js +1 -1
- package/dist/services/slice/slice-check-service.js +1 -1
- package/dist/services/standards/main-session-monitor-ban-hook.d.ts +14 -0
- package/dist/services/standards/main-session-monitor-ban-hook.js +19 -0
- package/dist/services/standards/missing-standards-detector.d.ts +25 -0
- package/dist/services/standards/missing-standards-detector.js +43 -12
- package/dist/services/tech/tech-change-id-service.d.ts +82 -0
- package/dist/services/tech/tech-change-id-service.js +117 -0
- package/dist/services/understand/understand-hybrid-service.js +1 -1
- package/dist/services/understand/understand-scan-service.js +2 -2
- package/dist/services/upgrade/upgrade-service.js +4 -3
- package/dist/services/verdict/envelopes.d.ts +2 -2
- package/dist/services/verdict/envelopes.js +2 -2
- package/dist/services/verdict/verdict-aggregator.d.ts +3 -3
- package/dist/services/vm/vm-lease.d.ts +40 -0
- package/dist/services/vm/vm-lease.js +137 -0
- package/dist/services/workflow/autonomous-resume-writer.js +1 -1
- package/dist/services/workflow/pipeline-verify-gate-support.d.ts +47 -0
- package/dist/services/workflow/pipeline-verify-gate-support.js +194 -0
- package/dist/services/workflow/pipeline-verify-service.d.ts +17 -81
- package/dist/services/workflow/pipeline-verify-service.js +42 -218
- package/dist/services/workflow/pipeline-verify-types.d.ts +90 -0
- package/dist/services/workflow/pipeline-verify-types.js +10 -0
- package/dist/services/workflow/plan-reader.d.ts +1 -1
- package/dist/services/workflow/plan-reader.js +1 -1
- package/dist/services/workflow/plan-refresher.d.ts +1 -1
- package/dist/services/workflow/plan-refresher.js +1 -1
- package/dist/services/workflow/plan-trigger-detector.d.ts +1 -1
- package/dist/services/workflow/plan-trigger-detector.js +1 -1
- package/dist/services/workflow/workflow-autonomous-resume-helpers.d.ts +1 -1
- package/dist/services/workflow/workflow-autonomous-service.d.ts +2 -98
- package/dist/services/workflow/workflow-autonomous-types.d.ts +114 -0
- package/dist/services/workflow/workflow-autonomous-types.js +21 -0
- package/dist/services/workflow/workflow-router-service.js +18 -16
- package/dist/services/workflow/workflow-spec-lint.d.ts +13 -0
- package/dist/services/workflow/workflow-spec-lint.js +75 -0
- package/dist/services/workflow/workflow-spec-types.d.ts +100 -0
- package/dist/services/workflow/workflow-spec-types.js +13 -0
- package/dist/services/workflow/workflow-spec-yaml.d.ts +17 -0
- package/dist/services/workflow/workflow-spec-yaml.js +88 -0
- package/dist/services/workflow/workflow-spec.d.ts +11 -91
- package/dist/services/workflow/workflow-spec.js +16 -145
- package/dist/services/workspace/migrate-service.js +1 -1
- package/dist/services/workspace/workspace-service.js +1 -1
- package/dist/services/worktree/worktree-lease.d.ts +170 -0
- package/dist/services/worktree/worktree-lease.js +242 -0
- package/package.json +91 -51
- package/scripts/clean-dist.mjs +31 -1
- package/scripts/copy-templates.mjs +16 -0
- package/scripts/sync-version.mjs +53 -2
- package/skills/bee/peaks-perf-audit/SKILL.md +17 -0
- package/skills/bee/peaks-prd/SKILL.md +16 -0
- package/skills/bee/peaks-prd/test-prompts.json +12 -0
- package/skills/bee/peaks-qa/SKILL.md +16 -1
- package/skills/bee/peaks-qa/references/qa-sub-agent-dispatch.md +22 -0
- package/skills/bee/peaks-qa/test-prompts.json +12 -0
- package/skills/bee/peaks-rd/SKILL.md +29 -20
- package/skills/bee/peaks-rd/references/rd-sub-agent-dispatch.md +39 -0
- package/skills/bee/peaks-rd/test-prompts.json +12 -0
- package/skills/bee/peaks-reviewer/SKILL.md +17 -0
- package/skills/bee/peaks-sc/SKILL.md +16 -0
- package/skills/bee/peaks-security-audit/SKILL.md +17 -0
- package/skills/bee/peaks-txt/SKILL.md +16 -0
- package/skills/bee/peaks-txt/test-prompts.json +12 -0
- package/skills/bee/peaks-ui/SKILL.md +49 -43
- package/skills/bee/peaks-ui/test-prompts.json +12 -0
- package/skills/peaks-code/SKILL.md +54 -109
- package/skills/peaks-code/references/boundaries.md +15 -1
- package/skills/peaks-code/references/cron-scheduler-deployment.md +313 -0
- package/skills/peaks-code/references/external-skill-invocation.md +27 -0
- package/skills/peaks-code/references/lease-dashboard.html +224 -0
- package/skills/peaks-code/references/lease-stats.sample.json +26 -0
- package/skills/peaks-code/references/peaks-worktree-l2-enforcers.md +208 -0
- package/skills/peaks-code/references/periodic-checkpoint.md +4 -4
- package/skills/peaks-code/references/runbook.md +63 -1
- package/skills/peaks-code/references/startup-sequence.md +110 -0
- package/skills/peaks-code/references/step-0-8-gate.md +1 -1
- package/skills/peaks-code/references/sub-agent-dispatch.md +92 -1
- package/skills/peaks-code/references/worktree-governance.md +123 -0
- package/skills/peaks-code/test-prompts.json +12 -0
- package/skills/peaks-doctor/SKILL.md +1 -1
- package/skills/peaks-ide/SKILL.md +1 -1
- package/skills/peaks-solo/SKILL.md +21 -0
- package/skills/peaks-solo/references/triage-decision-table.md +6 -0
- package/skills/peaks-sop/test-prompts.json +17 -0
- package/dist/cli/commands/agent-commands.d.ts +0 -20
- package/dist/cli/commands/agent-commands.js +0 -48
- package/dist/cli/commands/classify-classify-commands.d.ts +0 -19
- package/dist/cli/commands/classify-classify-commands.js +0 -151
- package/dist/cli/commands/contract-commands.d.ts +0 -26
- package/dist/cli/commands/contract-commands.js +0 -114
- package/dist/cli/commands/session-auto-compact-hook-command.d.ts +0 -37
- package/dist/cli/commands/session-auto-compact-hook-command.js +0 -127
- package/dist/cli/commands/worker-commands.d.ts +0 -3
- package/dist/cli/commands/worker-commands.js +0 -52
- package/dist/services/agent/ecc-agent-service.d.ts +0 -47
- package/dist/services/agent/ecc-agent-service.js +0 -143
- package/dist/services/config/provider-service.d.ts +0 -48
- package/dist/services/config/provider-service.js +0 -143
- package/dist/services/context/dispatch-context-guard.d.ts +0 -27
- package/dist/services/context/dispatch-context-guard.js +0 -192
- package/dist/services/context/shared-channel.d.ts +0 -93
- package/dist/services/context/shared-channel.js +0 -308
- package/dist/services/filesystem/file-lock.d.ts +0 -11
- package/dist/services/filesystem/file-lock.js +0 -152
- package/dist/services/mut/assert-scanner.d.ts +0 -6
- package/dist/services/mut/assert-scanner.js +0 -116
- package/dist/services/mut/index.d.ts +0 -8
- package/dist/services/mut/index.js +0 -7
- package/dist/services/mut/mut-runner.d.ts +0 -39
- package/dist/services/mut/mut-runner.js +0 -29
- package/dist/services/mut/production-stryker.d.ts +0 -2
- package/dist/services/mut/production-stryker.js +0 -75
- package/dist/services/mut/report-builder.d.ts +0 -11
- package/dist/services/mut/report-builder.js +0 -110
- package/dist/services/mut/report-loader.d.ts +0 -31
- package/dist/services/mut/report-loader.js +0 -78
- package/dist/services/mut/thresholds.d.ts +0 -21
- package/dist/services/mut/thresholds.js +0 -22
- package/dist/services/mut/types.d.ts +0 -336
- package/dist/services/mut/types.js +0 -59
- package/dist/services/providers/minimax-provider-service.d.ts +0 -24
- package/dist/services/providers/minimax-provider-service.js +0 -143
- package/dist/services/providers/minimax-worker-service.d.ts +0 -21
- package/dist/services/providers/minimax-worker-service.js +0 -83
- package/dist/shared/fs.d.ts +0 -4
- package/dist/shared/fs.js +0 -26
- package/dist/shared/paths.d.ts +0 -6
- package/dist/shared/paths.js +0 -50
- package/dist/shared/result.d.ts +0 -22
- package/dist/shared/result.js +0 -46
- package/dist/shared/version.d.ts +0 -1
- package/dist/shared/version.js +0 -1
- package/examples/video-demo/README.md +0 -142
- package/examples/video-demo/out/en-closing-960.png +0 -0
- package/examples/video-demo/out/zh-closing-960.png +0 -0
- package/examples/video-demo/preview/peaks-loop-demo-en.mp4 +0 -0
- package/examples/video-demo/preview/peaks-loop-demo.mp4 +0 -0
- /package/dist/services/{job/job-resource-snapshot.js → job-snapshot/snapshot.js} +0 -0
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `peaks cron` — slice 2026-07-29-worktree-l2-extended Part 14.
|
|
3
|
+
*
|
|
4
|
+
* Persistent scheduled-task system. Stores a JSON schedule at
|
|
5
|
+
* `.peaks/cron/schedule.json` and lets the LLM-side runner
|
|
6
|
+
* trigger tasks synchronously (`peaks cron run <id>`) or
|
|
7
|
+
* register a built-in periodic lease gc that the runbook
|
|
8
|
+
* (`skills/peaks-code/`) checks on every session start.
|
|
9
|
+
*
|
|
10
|
+
* Built-in tasks (the only ones auto-registered today):
|
|
11
|
+
* - `lease-gc-daily` — runs `peaks worktree gc --all-sessions
|
|
12
|
+
* --project .` once every 24h. Cleans up orphan leases that
|
|
13
|
+
* leaked past their auto-release hook (Part 3.A) — the
|
|
14
|
+
* safety net for the safety net.
|
|
15
|
+
*
|
|
16
|
+
* Why a JSON file (not a real cron daemon): peaks-loop is a CLI
|
|
17
|
+
* tool, not a service. The schedule is best-effort: the runbook
|
|
18
|
+
* checks it on session start and surfaces overdue tasks. A
|
|
19
|
+
* proper system cron / systemd timer is the production
|
|
20
|
+
* deployment story; this CLI is the portable fallback.
|
|
21
|
+
*
|
|
22
|
+
* File layout:
|
|
23
|
+
* .peaks/cron/schedule.json — registered tasks with their
|
|
24
|
+
* interval + lastRunAt
|
|
25
|
+
* .peaks/cron/history.jsonl — append-only run history
|
|
26
|
+
*/
|
|
27
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
28
|
+
import { join } from 'node:path';
|
|
29
|
+
import { randomUUID } from 'node:crypto';
|
|
30
|
+
import { execSync } from 'node:child_process';
|
|
31
|
+
import { fail, getErrorMessage, ok } from 'peaks-loop-shared/result';
|
|
32
|
+
import { addJsonOption, printResult } from '../cli-helpers.js';
|
|
33
|
+
import { findProjectRoot } from '../../services/config/config-safety.js';
|
|
34
|
+
const SCHEDULE_VERSION = 1;
|
|
35
|
+
const SCHEDULE_FILENAME = 'schedule.json';
|
|
36
|
+
const HISTORY_FILENAME = 'history.jsonl';
|
|
37
|
+
const DEFAULT_INTERVAL_MS = 24 * 60 * 60 * 1_000; // 24h
|
|
38
|
+
function cronDir(projectRoot) {
|
|
39
|
+
return join(projectRoot, '.peaks', 'cron');
|
|
40
|
+
}
|
|
41
|
+
function schedulePath(projectRoot) {
|
|
42
|
+
return join(cronDir(projectRoot), SCHEDULE_FILENAME);
|
|
43
|
+
}
|
|
44
|
+
function historyPath(projectRoot) {
|
|
45
|
+
return join(cronDir(projectRoot), HISTORY_FILENAME);
|
|
46
|
+
}
|
|
47
|
+
export function readSchedule(projectRoot) {
|
|
48
|
+
const path = schedulePath(projectRoot);
|
|
49
|
+
if (!existsSync(path))
|
|
50
|
+
return { version: SCHEDULE_VERSION, entries: [] };
|
|
51
|
+
const raw = readFileSync(path, 'utf8');
|
|
52
|
+
let parsed;
|
|
53
|
+
try {
|
|
54
|
+
parsed = JSON.parse(raw);
|
|
55
|
+
}
|
|
56
|
+
catch (err) {
|
|
57
|
+
throw new Error(`schedule.json malformed: ${err.message}`);
|
|
58
|
+
}
|
|
59
|
+
if (typeof parsed !== 'object' || parsed === null) {
|
|
60
|
+
throw new Error('schedule.json root must be an object');
|
|
61
|
+
}
|
|
62
|
+
const obj = parsed;
|
|
63
|
+
if (obj.version !== SCHEDULE_VERSION) {
|
|
64
|
+
throw new Error(`schedule.json version mismatch (got ${String(obj.version)}, expected ${SCHEDULE_VERSION})`);
|
|
65
|
+
}
|
|
66
|
+
if (!Array.isArray(obj.entries)) {
|
|
67
|
+
throw new Error('schedule.json entries must be an array');
|
|
68
|
+
}
|
|
69
|
+
const entries = [];
|
|
70
|
+
for (const e of obj.entries) {
|
|
71
|
+
if (typeof e !== 'object' || e === null)
|
|
72
|
+
continue;
|
|
73
|
+
const ent = e;
|
|
74
|
+
if (typeof ent.id !== 'string' || typeof ent.name !== 'string' || typeof ent.command !== 'string')
|
|
75
|
+
continue;
|
|
76
|
+
if (!Array.isArray(ent.args) || !ent.args.every((a) => typeof a === 'string'))
|
|
77
|
+
continue;
|
|
78
|
+
if (typeof ent.intervalMs !== 'number' || typeof ent.createdAt !== 'number')
|
|
79
|
+
continue;
|
|
80
|
+
entries.push({
|
|
81
|
+
id: ent.id,
|
|
82
|
+
name: ent.name,
|
|
83
|
+
command: ent.command,
|
|
84
|
+
args: ent.args,
|
|
85
|
+
intervalMs: ent.intervalMs,
|
|
86
|
+
lastRunAt: typeof ent.lastRunAt === 'number' ? ent.lastRunAt : null,
|
|
87
|
+
enabled: ent.enabled !== false,
|
|
88
|
+
createdAt: ent.createdAt
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
return { version: SCHEDULE_VERSION, entries };
|
|
92
|
+
}
|
|
93
|
+
function writeSchedule(projectRoot, file) {
|
|
94
|
+
const dir = cronDir(projectRoot);
|
|
95
|
+
if (!existsSync(dir))
|
|
96
|
+
mkdirSync(dir, { recursive: true });
|
|
97
|
+
writeFileSync(schedulePath(projectRoot), `${JSON.stringify(file, null, 2)}\n`, 'utf8');
|
|
98
|
+
}
|
|
99
|
+
export function appendHistory(projectRoot, record) {
|
|
100
|
+
const dir = cronDir(projectRoot);
|
|
101
|
+
if (!existsSync(dir))
|
|
102
|
+
mkdirSync(dir, { recursive: true });
|
|
103
|
+
const path = historyPath(projectRoot);
|
|
104
|
+
const line = `${JSON.stringify(record)}\n`;
|
|
105
|
+
if (!existsSync(path)) {
|
|
106
|
+
writeFileSync(path, line, 'utf8');
|
|
107
|
+
}
|
|
108
|
+
else {
|
|
109
|
+
// Append — small file, OK to read+write
|
|
110
|
+
const existing = readFileSync(path, 'utf8');
|
|
111
|
+
writeFileSync(path, existing + line, 'utf8');
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
function ensureLeaseGcEntry(file) {
|
|
115
|
+
if (file.entries.some((e) => e.id === 'lease-gc-daily'))
|
|
116
|
+
return file;
|
|
117
|
+
const entry = {
|
|
118
|
+
id: 'lease-gc-daily',
|
|
119
|
+
name: 'Daily lease listing — refresh the alive-lease set; operators run peaks worktree gc --lease-id <id> manually on stale entries',
|
|
120
|
+
command: 'worktree',
|
|
121
|
+
args: ['list'],
|
|
122
|
+
intervalMs: DEFAULT_INTERVAL_MS,
|
|
123
|
+
lastRunAt: null,
|
|
124
|
+
enabled: true,
|
|
125
|
+
createdAt: Date.now()
|
|
126
|
+
};
|
|
127
|
+
return { version: SCHEDULE_VERSION, entries: [...file.entries, entry] };
|
|
128
|
+
}
|
|
129
|
+
export function runTask(projectRoot, task) {
|
|
130
|
+
const id = randomUUID();
|
|
131
|
+
const startedAt = Date.now();
|
|
132
|
+
let exitCode = 0;
|
|
133
|
+
let stderr = '';
|
|
134
|
+
try {
|
|
135
|
+
execSync(`peaks ${task.command} ${task.args.map((a) => `"${a.replace(/"/g, '\\"')}"`).join(' ')}`, {
|
|
136
|
+
cwd: projectRoot,
|
|
137
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
138
|
+
encoding: 'utf8',
|
|
139
|
+
timeout: 5 * 60_000
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
catch (err) {
|
|
143
|
+
const e = err;
|
|
144
|
+
exitCode = typeof e.status === 'number' ? e.status : 1;
|
|
145
|
+
stderr = (e.stderr ?? getErrorMessage(err)).slice(0, 2000);
|
|
146
|
+
}
|
|
147
|
+
const record = {
|
|
148
|
+
id,
|
|
149
|
+
taskId: task.id,
|
|
150
|
+
startedAt,
|
|
151
|
+
finishedAt: Date.now(),
|
|
152
|
+
exitCode,
|
|
153
|
+
stderr
|
|
154
|
+
};
|
|
155
|
+
appendHistory(projectRoot, record);
|
|
156
|
+
return record;
|
|
157
|
+
}
|
|
158
|
+
export function listDueTasks(projectRoot, now = Date.now()) {
|
|
159
|
+
const file = readSchedule(projectRoot);
|
|
160
|
+
return file.entries.filter((e) => {
|
|
161
|
+
if (!e.enabled)
|
|
162
|
+
return false;
|
|
163
|
+
if (e.lastRunAt === null)
|
|
164
|
+
return true;
|
|
165
|
+
return now - e.lastRunAt >= e.intervalMs;
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
export function registerCronCommand(program, io) {
|
|
169
|
+
const cmd = program
|
|
170
|
+
.command('cron')
|
|
171
|
+
.description('Persistent scheduled tasks (Part 14; companion to peaks worktree / peaks container).');
|
|
172
|
+
addJsonOption(cmd.command('init')
|
|
173
|
+
.description('Create .peaks/cron/schedule.json with the built-in tasks (currently: lease-gc-daily). Idempotent.')
|
|
174
|
+
.option('--project <path>', 'project root (default: findProjectRoot(cwd))')).action((options) => {
|
|
175
|
+
try {
|
|
176
|
+
const projectRoot = options.project ?? findProjectRoot(process.cwd()) ?? process.cwd();
|
|
177
|
+
const file = readSchedule(projectRoot);
|
|
178
|
+
const updated = ensureLeaseGcEntry(file);
|
|
179
|
+
writeSchedule(projectRoot, updated);
|
|
180
|
+
const added = updated.entries.length - file.entries.length;
|
|
181
|
+
printResult(io, ok('cron.init', { projectRoot, schedulePath: schedulePath(projectRoot), totalEntries: updated.entries.length, added }, [], [
|
|
182
|
+
added > 0
|
|
183
|
+
? `Added ${added} built-in task(s). Use 'peaks cron list' to inspect.`
|
|
184
|
+
: 'Schedule already contains the built-in tasks; no change made.'
|
|
185
|
+
]), options.json);
|
|
186
|
+
}
|
|
187
|
+
catch (err) {
|
|
188
|
+
printResult(io, fail('cron.init', 'CRON_INIT_FAILED', getErrorMessage(err), { projectRoot: options.project }, [
|
|
189
|
+
'Verify the project root is a peaks-loop project (.peaks/ exists).',
|
|
190
|
+
'Check filesystem permissions.'
|
|
191
|
+
]), options.json);
|
|
192
|
+
process.exitCode = 1;
|
|
193
|
+
}
|
|
194
|
+
});
|
|
195
|
+
addJsonOption(cmd.command('list')
|
|
196
|
+
.description('List all registered cron tasks + their due status (relative to now).')
|
|
197
|
+
.option('--project <path>', 'project root (default: findProjectRoot(cwd))')).action((options) => {
|
|
198
|
+
try {
|
|
199
|
+
const projectRoot = options.project ?? findProjectRoot(process.cwd()) ?? process.cwd();
|
|
200
|
+
const file = readSchedule(projectRoot);
|
|
201
|
+
const now = Date.now();
|
|
202
|
+
const annotated = file.entries.map((e) => ({
|
|
203
|
+
...e,
|
|
204
|
+
due: e.enabled && (e.lastRunAt === null || now - e.lastRunAt >= e.intervalMs),
|
|
205
|
+
nextDueAt: e.lastRunAt === null ? now : e.lastRunAt + e.intervalMs
|
|
206
|
+
}));
|
|
207
|
+
annotated.sort((a, b) => a.nextDueAt - b.nextDueAt);
|
|
208
|
+
printResult(io, ok('cron.list', { projectRoot, schedulePath: schedulePath(projectRoot), entries: annotated }, [], [
|
|
209
|
+
`${annotated.length} task(s); ${annotated.filter((e) => e.due).length} due now.`
|
|
210
|
+
]), options.json);
|
|
211
|
+
}
|
|
212
|
+
catch (err) {
|
|
213
|
+
printResult(io, fail('cron.list', 'CRON_LIST_FAILED', getErrorMessage(err), { projectRoot: options.project }, [
|
|
214
|
+
"If schedule.json is missing, run 'peaks cron init' first."
|
|
215
|
+
]), options.json);
|
|
216
|
+
process.exitCode = 1;
|
|
217
|
+
}
|
|
218
|
+
});
|
|
219
|
+
addJsonOption(cmd.command('run')
|
|
220
|
+
.description('Run the specified task (by id) immediately, update lastRunAt, append a history record.')
|
|
221
|
+
.option('--id <taskId>', 'task id to run (default: all due tasks)')
|
|
222
|
+
.option('--project <path>', 'project root (default: findProjectRoot(cwd))')).action((options) => {
|
|
223
|
+
try {
|
|
224
|
+
const projectRoot = options.project ?? findProjectRoot(process.cwd()) ?? process.cwd();
|
|
225
|
+
const file = readSchedule(projectRoot);
|
|
226
|
+
const targets = options.id
|
|
227
|
+
? file.entries.filter((e) => e.id === options.id)
|
|
228
|
+
: listDueTasks(projectRoot);
|
|
229
|
+
if (targets.length === 0) {
|
|
230
|
+
printResult(io, ok('cron.run', { projectRoot, ran: 0, records: [] }, [], ['No due tasks; nothing to run.']), options.json);
|
|
231
|
+
return;
|
|
232
|
+
}
|
|
233
|
+
const records = [];
|
|
234
|
+
const updatedEntries = [];
|
|
235
|
+
const now = Date.now();
|
|
236
|
+
for (const task of file.entries) {
|
|
237
|
+
if (!targets.some((t) => t.id === task.id)) {
|
|
238
|
+
updatedEntries.push(task);
|
|
239
|
+
continue;
|
|
240
|
+
}
|
|
241
|
+
const record = runTask(projectRoot, task);
|
|
242
|
+
records.push(record);
|
|
243
|
+
updatedEntries.push({ ...task, lastRunAt: record.finishedAt });
|
|
244
|
+
}
|
|
245
|
+
writeSchedule(projectRoot, { version: SCHEDULE_VERSION, entries: updatedEntries });
|
|
246
|
+
printResult(io, ok('cron.run', { projectRoot, ran: records.length, records }, records.filter((r) => r.exitCode !== 0).map((r) => `Task ${r.taskId} failed (exit=${r.exitCode}): ${r.stderr.slice(0, 200)}`), [`Ran ${records.length} task(s); ${records.filter((r) => r.exitCode === 0).length} succeeded.`]), options.json);
|
|
247
|
+
if (records.some((r) => r.exitCode !== 0))
|
|
248
|
+
process.exitCode = 1;
|
|
249
|
+
}
|
|
250
|
+
catch (err) {
|
|
251
|
+
printResult(io, fail('cron.run', 'CRON_RUN_FAILED', getErrorMessage(err), { projectRoot: options.project }, [
|
|
252
|
+
"If schedule.json is missing, run 'peaks cron init' first."
|
|
253
|
+
]), options.json);
|
|
254
|
+
process.exitCode = 1;
|
|
255
|
+
}
|
|
256
|
+
});
|
|
257
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `peaks cron scheduler start|stop|status|run-once` — slice 2026-07-29 Part 15.
|
|
3
|
+
*
|
|
4
|
+
* Companion CLI to Part 14 (peaks cron init/list/run). The
|
|
5
|
+
* scheduler is a long-running background daemon that wakes up
|
|
6
|
+
* once per minute, reads `.peaks/cron/schedule.json`, and runs
|
|
7
|
+
* any due tasks via detached execSync (so a misbehaving task
|
|
8
|
+
* cannot block the scheduler). Best-effort persistence via
|
|
9
|
+
* `.peaks/cron/scheduler.pid` (advisory — operators can `kill
|
|
10
|
+
* $(cat scheduler.pid)` if a stale entry lingers).
|
|
11
|
+
*
|
|
12
|
+
* Why a separate `peaks-cron-scheduler` CLI (not a peaks
|
|
13
|
+
* subcommand) — long-running daemons that are spawned from a
|
|
14
|
+
* `peaks ...` invocation confuse a future "which peaks version
|
|
15
|
+
* is running" check. A dedicated binary name keeps the
|
|
16
|
+
* scheduler's startup visible to operators.
|
|
17
|
+
*
|
|
18
|
+
* Windows: the scheduler writes its pid to a file. To run as
|
|
19
|
+
* a real Windows service, the operator wires NSSM (or
|
|
20
|
+
* node-windows) to call `peaks-cron-scheduler start` on
|
|
21
|
+
* service start. Future slice: ship an NSSM install recipe
|
|
22
|
+
* in the operator docs.
|
|
23
|
+
*
|
|
24
|
+
* POSIX: same pid-file pattern; an init.d / systemd unit
|
|
25
|
+
* file would call `peaks-cron-scheduler start` and rely on
|
|
26
|
+
* the scheduler to write its own pid. Or just run from
|
|
27
|
+
* `nohup peaks-cron-scheduler start &` if you don't need
|
|
28
|
+
* restart-on-crash.
|
|
29
|
+
*/
|
|
30
|
+
import type { Command } from 'commander';
|
|
31
|
+
import { type ProgramIO } from '../cli-helpers.js';
|
|
32
|
+
export declare function registerCronSchedulerCommand(program: Command, io: ProgramIO): void;
|
|
33
|
+
/**
|
|
34
|
+
* Standalone scheduler entry point (the process the CLI spawns).
|
|
35
|
+
* Runs a setInterval tick + reads schedule + runs due tasks.
|
|
36
|
+
* Exits cleanly on SIGTERM.
|
|
37
|
+
*
|
|
38
|
+
* Exported as `runSchedulerLoop` so the dedicated
|
|
39
|
+
* `peaks-cron-scheduler.js` entry shim can call it; the
|
|
40
|
+
* command-side `registerCronSchedulerCommand` only uses
|
|
41
|
+
* start/stop/status, not the loop.
|
|
42
|
+
*/
|
|
43
|
+
export declare function runSchedulerLoop(args: {
|
|
44
|
+
projectRoot: string;
|
|
45
|
+
tickMs?: number;
|
|
46
|
+
signal?: AbortSignal;
|
|
47
|
+
}): Promise<void>;
|
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `peaks cron scheduler start|stop|status|run-once` — slice 2026-07-29 Part 15.
|
|
3
|
+
*
|
|
4
|
+
* Companion CLI to Part 14 (peaks cron init/list/run). The
|
|
5
|
+
* scheduler is a long-running background daemon that wakes up
|
|
6
|
+
* once per minute, reads `.peaks/cron/schedule.json`, and runs
|
|
7
|
+
* any due tasks via detached execSync (so a misbehaving task
|
|
8
|
+
* cannot block the scheduler). Best-effort persistence via
|
|
9
|
+
* `.peaks/cron/scheduler.pid` (advisory — operators can `kill
|
|
10
|
+
* $(cat scheduler.pid)` if a stale entry lingers).
|
|
11
|
+
*
|
|
12
|
+
* Why a separate `peaks-cron-scheduler` CLI (not a peaks
|
|
13
|
+
* subcommand) — long-running daemons that are spawned from a
|
|
14
|
+
* `peaks ...` invocation confuse a future "which peaks version
|
|
15
|
+
* is running" check. A dedicated binary name keeps the
|
|
16
|
+
* scheduler's startup visible to operators.
|
|
17
|
+
*
|
|
18
|
+
* Windows: the scheduler writes its pid to a file. To run as
|
|
19
|
+
* a real Windows service, the operator wires NSSM (or
|
|
20
|
+
* node-windows) to call `peaks-cron-scheduler start` on
|
|
21
|
+
* service start. Future slice: ship an NSSM install recipe
|
|
22
|
+
* in the operator docs.
|
|
23
|
+
*
|
|
24
|
+
* POSIX: same pid-file pattern; an init.d / systemd unit
|
|
25
|
+
* file would call `peaks-cron-scheduler start` and rely on
|
|
26
|
+
* the scheduler to write its own pid. Or just run from
|
|
27
|
+
* `nohup peaks-cron-scheduler start &` if you don't need
|
|
28
|
+
* restart-on-crash.
|
|
29
|
+
*/
|
|
30
|
+
import { spawn } from 'node:child_process';
|
|
31
|
+
import { existsSync, readFileSync, writeFileSync, unlinkSync } from 'node:fs';
|
|
32
|
+
import { dirname, join, resolve } from 'node:path';
|
|
33
|
+
import { fileURLToPath } from 'node:url';
|
|
34
|
+
import { fail, getErrorMessage, ok } from 'peaks-loop-shared/result';
|
|
35
|
+
import { addJsonOption, printResult } from '../cli-helpers.js';
|
|
36
|
+
import { findProjectRoot } from '../../services/config/config-safety.js';
|
|
37
|
+
import { readSchedule, listDueTasks, runTask } from './cron-commands.js';
|
|
38
|
+
const SCHEDULER_TICK_MS = 60_000; // 1 minute
|
|
39
|
+
const PID_FILENAME = 'scheduler.pid';
|
|
40
|
+
function schedulerPidPath(projectRoot) {
|
|
41
|
+
return join(projectRoot, '.peaks', 'cron', PID_FILENAME);
|
|
42
|
+
}
|
|
43
|
+
function readSchedulerPid(projectRoot) {
|
|
44
|
+
const p = schedulerPidPath(projectRoot);
|
|
45
|
+
if (!existsSync(p))
|
|
46
|
+
return null;
|
|
47
|
+
const raw = readFileSync(p, 'utf8').trim();
|
|
48
|
+
const pid = Number.parseInt(raw, 10);
|
|
49
|
+
if (!Number.isFinite(pid) || pid <= 0)
|
|
50
|
+
return null;
|
|
51
|
+
return pid;
|
|
52
|
+
}
|
|
53
|
+
function isPidAlive(pid) {
|
|
54
|
+
try {
|
|
55
|
+
// `process.kill(pid, 0)` does not actually kill; it just
|
|
56
|
+
// checks if the process exists. ESRCH means no such pid;
|
|
57
|
+
// EPERM means it exists but we don't have permission to
|
|
58
|
+
// signal it (still alive).
|
|
59
|
+
process.kill(pid, 0);
|
|
60
|
+
return true;
|
|
61
|
+
}
|
|
62
|
+
catch (err) {
|
|
63
|
+
const code = err.code;
|
|
64
|
+
return code === 'EPERM';
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
function findSchedulerEntryPoint() {
|
|
68
|
+
// The peaks-cron-scheduler.js entry shim lives in bin/ (not
|
|
69
|
+
// dist/, so the pnpm build does not overwrite it). It forwards
|
|
70
|
+
// to dist/cli/commands/cron-scheduler-commands.js with
|
|
71
|
+
// PEAKS_CRON_SCHEDULER_DAEMON=1 set; the CLI detects the env
|
|
72
|
+
// and runs runSchedulerLoop directly.
|
|
73
|
+
const here = dirname(fileURLToPath(import.meta.url));
|
|
74
|
+
// dist/cli/commands/cron-scheduler-commands.js → bin/peaks-cron-scheduler.js
|
|
75
|
+
return resolve(here, '..', '..', '..', 'bin', 'peaks-cron-scheduler.js');
|
|
76
|
+
}
|
|
77
|
+
export function registerCronSchedulerCommand(program, io) {
|
|
78
|
+
const cmd = program
|
|
79
|
+
.command('cron-scheduler')
|
|
80
|
+
.description('Long-running background scheduler for peaks cron tasks (Part 15).');
|
|
81
|
+
addJsonOption(cmd.command('start')
|
|
82
|
+
.description('Spawn the scheduler as a detached background process. Idempotent: refuses to start when an alive pid already exists.')
|
|
83
|
+
.option('--project <path>', 'project root (default: findProjectRoot(cwd))')).action((options) => {
|
|
84
|
+
try {
|
|
85
|
+
const projectRoot = options.project ?? findProjectRoot(process.cwd()) ?? process.cwd();
|
|
86
|
+
const pidFile = schedulerPidPath(projectRoot);
|
|
87
|
+
const existing = readSchedulerPid(projectRoot);
|
|
88
|
+
if (existing !== null && isPidAlive(existing)) {
|
|
89
|
+
printResult(io, ok('cron-scheduler.start', { projectRoot, started: false, alreadyRunning: true, existingPid: existing }, [], [
|
|
90
|
+
`Scheduler already running at pid ${existing}; not starting a second one.`,
|
|
91
|
+
'Use `peaks cron-scheduler stop` first if you need to restart.'
|
|
92
|
+
]), options.json);
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
// Spawn detached via `peaks-cron-scheduler.js` — the
|
|
96
|
+
// child runs `runSchedulerLoop` (60s setInterval + due-task
|
|
97
|
+
// runner). The detached flag + stdio:ignore puts the
|
|
98
|
+
// scheduler in its own process group (POSIX) / detaches
|
|
99
|
+
// from the console (Windows). PEAKS_CRON_SCHEDULER_DAEMON
|
|
100
|
+
// env is the canonical "I'm the long-running child" signal.
|
|
101
|
+
const entry = findSchedulerEntryPoint();
|
|
102
|
+
const child = spawn(process.execPath, [entry, '--project', projectRoot], {
|
|
103
|
+
cwd: projectRoot,
|
|
104
|
+
stdio: 'ignore',
|
|
105
|
+
detached: true,
|
|
106
|
+
windowsHide: true,
|
|
107
|
+
env: { ...process.env, PEAKS_CRON_SCHEDULER: '1', PEAKS_CRON_SCHEDULER_DAEMON: '1' }
|
|
108
|
+
});
|
|
109
|
+
child.unref();
|
|
110
|
+
if (typeof child.pid !== 'number') {
|
|
111
|
+
throw new Error('spawn returned no pid');
|
|
112
|
+
}
|
|
113
|
+
writeFileSync(pidFile, String(child.pid), 'utf8');
|
|
114
|
+
printResult(io, ok('cron-scheduler.start', { projectRoot, started: true, pid: child.pid, pidFile }, [], [
|
|
115
|
+
`Scheduler started at pid ${child.pid}; logs go to .peaks/cron/scheduler.log.`,
|
|
116
|
+
'Use `peaks cron-scheduler status` to confirm it is alive; `stop` to terminate.'
|
|
117
|
+
]), options.json);
|
|
118
|
+
}
|
|
119
|
+
catch (err) {
|
|
120
|
+
printResult(io, fail('cron-scheduler.start', 'SCHEDULER_START_FAILED', getErrorMessage(err), { projectRoot: options.project }, [
|
|
121
|
+
'Verify the peaks-cron-scheduler.js entry point is on disk.',
|
|
122
|
+
'Check that the .peaks/cron/ directory is writable.'
|
|
123
|
+
]), options.json);
|
|
124
|
+
process.exitCode = 1;
|
|
125
|
+
}
|
|
126
|
+
});
|
|
127
|
+
addJsonOption(cmd.command('stop')
|
|
128
|
+
.description('Send SIGTERM to the scheduler pid (best-effort; removes the pid file either way).')
|
|
129
|
+
.option('--project <path>', 'project root (default: findProjectRoot(cwd))')).action((options) => {
|
|
130
|
+
try {
|
|
131
|
+
const projectRoot = options.project ?? findProjectRoot(process.cwd()) ?? process.cwd();
|
|
132
|
+
const pid = readSchedulerPid(projectRoot);
|
|
133
|
+
if (pid === null) {
|
|
134
|
+
printResult(io, ok('cron-scheduler.stop', { projectRoot, stopped: false, reason: 'no-pid-file' }, [], ['No scheduler pid file found; nothing to stop.']), options.json);
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
let signalSent = false;
|
|
138
|
+
if (isPidAlive(pid)) {
|
|
139
|
+
try {
|
|
140
|
+
process.kill(pid, 'SIGTERM');
|
|
141
|
+
signalSent = true;
|
|
142
|
+
}
|
|
143
|
+
catch {
|
|
144
|
+
signalSent = false;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
const pidFile = schedulerPidPath(projectRoot);
|
|
148
|
+
try {
|
|
149
|
+
unlinkSync(pidFile);
|
|
150
|
+
}
|
|
151
|
+
catch { /* best-effort */ }
|
|
152
|
+
printResult(io, ok('cron-scheduler.stop', { projectRoot, stopped: signalSent, pid, pidFile }, [], [
|
|
153
|
+
signalSent
|
|
154
|
+
? `Sent SIGTERM to pid ${pid}; pid file removed.`
|
|
155
|
+
: `pid ${pid} was not alive; pid file removed.`
|
|
156
|
+
]), options.json);
|
|
157
|
+
}
|
|
158
|
+
catch (err) {
|
|
159
|
+
printResult(io, fail('cron-scheduler.stop', 'SCHEDULER_STOP_FAILED', getErrorMessage(err), { projectRoot: options.project }, [
|
|
160
|
+
'Verify the pid file is readable / the process is yours.'
|
|
161
|
+
]), options.json);
|
|
162
|
+
process.exitCode = 1;
|
|
163
|
+
}
|
|
164
|
+
});
|
|
165
|
+
addJsonOption(cmd.command('status')
|
|
166
|
+
.description('Report whether the scheduler is alive + when it last ran a task.')
|
|
167
|
+
.option('--project <path>', 'project root (default: findProjectRoot(cwd))')).action((options) => {
|
|
168
|
+
try {
|
|
169
|
+
const projectRoot = options.project ?? findProjectRoot(process.cwd()) ?? process.cwd();
|
|
170
|
+
const pid = readSchedulerPid(projectRoot);
|
|
171
|
+
const alive = pid !== null && isPidAlive(pid);
|
|
172
|
+
let schedule;
|
|
173
|
+
try {
|
|
174
|
+
schedule = readSchedule(projectRoot);
|
|
175
|
+
}
|
|
176
|
+
catch (err) {
|
|
177
|
+
schedule = { version: 1, entries: [] };
|
|
178
|
+
}
|
|
179
|
+
const due = alive ? listDueTasks(projectRoot) : [];
|
|
180
|
+
printResult(io, ok('cron-scheduler.status', { projectRoot, pid, alive, scheduleEntries: schedule.entries.length, dueTaskCount: due.length }, [], [alive
|
|
181
|
+
? `Scheduler alive at pid ${pid}; ${due.length} task(s) due now.`
|
|
182
|
+
: 'Scheduler is NOT running. Use `peaks cron-scheduler start` to spawn.']), options.json);
|
|
183
|
+
}
|
|
184
|
+
catch (err) {
|
|
185
|
+
printResult(io, fail('cron-scheduler.status', 'SCHEDULER_STATUS_FAILED', getErrorMessage(err), { projectRoot: options.project }, [
|
|
186
|
+
'Verify the .peaks/cron/ directory exists.'
|
|
187
|
+
]), options.json);
|
|
188
|
+
process.exitCode = 1;
|
|
189
|
+
}
|
|
190
|
+
});
|
|
191
|
+
addJsonOption(cmd.command('run-once')
|
|
192
|
+
.description('Synchronous foreground one-shot: run every currently-due task and exit. Useful for manual cron substitute.')
|
|
193
|
+
.option('--project <path>', 'project root (default: findProjectRoot(cwd))')).action((options) => {
|
|
194
|
+
try {
|
|
195
|
+
const projectRoot = options.project ?? findProjectRoot(process.cwd()) ?? process.cwd();
|
|
196
|
+
const due = listDueTasks(projectRoot);
|
|
197
|
+
const records = due.map((t) => runTask(projectRoot, t));
|
|
198
|
+
printResult(io, ok('cron-scheduler.run-once', { projectRoot, ran: records.length, records }, [], [
|
|
199
|
+
`${records.length} due task(s) ran; ${records.filter((r) => r.exitCode === 0).length} succeeded.`
|
|
200
|
+
]), options.json);
|
|
201
|
+
if (records.some((r) => r.exitCode !== 0))
|
|
202
|
+
process.exitCode = 1;
|
|
203
|
+
}
|
|
204
|
+
catch (err) {
|
|
205
|
+
printResult(io, fail('cron-scheduler.run-once', 'SCHEDULER_RUN_ONCE_FAILED', getErrorMessage(err), { projectRoot: options.project }, [
|
|
206
|
+
"If schedule.json is missing, run 'peaks cron init' first."
|
|
207
|
+
]), options.json);
|
|
208
|
+
process.exitCode = 1;
|
|
209
|
+
}
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
/**
|
|
213
|
+
* Standalone scheduler entry point (the process the CLI spawns).
|
|
214
|
+
* Runs a setInterval tick + reads schedule + runs due tasks.
|
|
215
|
+
* Exits cleanly on SIGTERM.
|
|
216
|
+
*
|
|
217
|
+
* Exported as `runSchedulerLoop` so the dedicated
|
|
218
|
+
* `peaks-cron-scheduler.js` entry shim can call it; the
|
|
219
|
+
* command-side `registerCronSchedulerCommand` only uses
|
|
220
|
+
* start/stop/status, not the loop.
|
|
221
|
+
*/
|
|
222
|
+
export async function runSchedulerLoop(args) {
|
|
223
|
+
const tickMs = args.tickMs ?? SCHEDULER_TICK_MS;
|
|
224
|
+
// Idempotency: refuse to start a second loop in the same
|
|
225
|
+
// process (the start CLI is expected to be a fresh spawn).
|
|
226
|
+
if (process.__peaksCronScheduler) {
|
|
227
|
+
throw new Error('scheduler loop already running in this process');
|
|
228
|
+
}
|
|
229
|
+
process.__peaksCronScheduler = true;
|
|
230
|
+
const tick = () => {
|
|
231
|
+
try {
|
|
232
|
+
const due = listDueTasks(args.projectRoot);
|
|
233
|
+
for (const task of due) {
|
|
234
|
+
runTask(args.projectRoot, task);
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
catch (err) {
|
|
238
|
+
process.stderr.write(`[cron-scheduler] tick error: ${getErrorMessage(err)}\n`);
|
|
239
|
+
}
|
|
240
|
+
};
|
|
241
|
+
// First tick on entry (don't wait the full interval
|
|
242
|
+
// for the first run after a start).
|
|
243
|
+
tick();
|
|
244
|
+
const handle = setInterval(tick, tickMs);
|
|
245
|
+
// Wait for the abort signal (POSIX) or the platform
|
|
246
|
+
// equivalent. We don't use Node's process.on('SIGTERM')
|
|
247
|
+
// for portability — the parent process kills us via
|
|
248
|
+
// `process.kill(pid, 'SIGTERM')` and the event loop
|
|
249
|
+
// exits naturally if the interval is the only thing
|
|
250
|
+
// keeping the loop alive. We clearInterval on exit.
|
|
251
|
+
await new Promise((resolve) => {
|
|
252
|
+
const onAbort = () => {
|
|
253
|
+
clearInterval(handle);
|
|
254
|
+
resolve();
|
|
255
|
+
};
|
|
256
|
+
if (args.signal) {
|
|
257
|
+
args.signal.addEventListener('abort', onAbort, { once: true });
|
|
258
|
+
}
|
|
259
|
+
else {
|
|
260
|
+
// POSIX: SIGTERM. Windows: kill the process directly.
|
|
261
|
+
process.on('SIGTERM', onAbort);
|
|
262
|
+
process.on('SIGINT', onAbort);
|
|
263
|
+
}
|
|
264
|
+
});
|
|
265
|
+
process.exit(0);
|
|
266
|
+
}
|
|
267
|
+
// Standalone entry shim. When this file is run directly via
|
|
268
|
+
// `node peaks-cron-scheduler.js`, the loop starts; when it is
|
|
269
|
+
// imported (e.g. by the CLI), the export is used but the
|
|
270
|
+
// loop is not started.
|
|
271
|
+
if (process.env.PEAKS_CRON_SCHEDULER_DAEMON === '1') {
|
|
272
|
+
// Spawned by `peaks cron-scheduler start` as the long-running
|
|
273
|
+
// detached child. Run the loop until SIGTERM. Gated on the
|
|
274
|
+
// env var (not argv[1]) because on Windows a forked node
|
|
275
|
+
// process inherits the parent's argv[1] (which is bin/peaks.js).
|
|
276
|
+
const projectRoot = (() => {
|
|
277
|
+
const idx = process.argv.indexOf('--project');
|
|
278
|
+
return idx >= 0 ? process.argv[idx + 1] : findProjectRoot(process.cwd()) ?? process.cwd();
|
|
279
|
+
})();
|
|
280
|
+
runSchedulerLoop({ projectRoot }).catch((err) => {
|
|
281
|
+
process.stderr.write(`[cron-scheduler] fatal: ${getErrorMessage(err)}\n`);
|
|
282
|
+
process.exit(1);
|
|
283
|
+
});
|
|
284
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { registerDashboardLongRunCommand } from './dashboard-long-run.js';
|
|
2
|
+
import { registerDashboardSummaryCommand } from './dashboard-summary.js';
|
|
3
|
+
/** Registers the top-level `peaks dashboard` command tree. */
|
|
4
|
+
export function registerDashboardCommands(program, io) {
|
|
5
|
+
const dashboard = program
|
|
6
|
+
.command('dashboard')
|
|
7
|
+
.description('peaks dashboard surface (24h long-run view etc.)');
|
|
8
|
+
registerDashboardLongRunCommand(dashboard, io);
|
|
9
|
+
registerDashboardSummaryCommand(dashboard, io);
|
|
10
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { Command } from 'commander';
|
|
2
|
+
import { type ProgramIO } from '../cli-helpers.js';
|
|
3
|
+
type ParseResult = {
|
|
4
|
+
ok: true;
|
|
5
|
+
ms: number;
|
|
6
|
+
} | {
|
|
7
|
+
ok: false;
|
|
8
|
+
error: string;
|
|
9
|
+
};
|
|
10
|
+
declare function parseSince(raw: string | undefined): ParseResult;
|
|
11
|
+
type Boundary = {
|
|
12
|
+
key: 'empty' | 'cap' | 'valid';
|
|
13
|
+
message: string;
|
|
14
|
+
};
|
|
15
|
+
declare function boundaryLabel(raw: string): Boundary;
|
|
16
|
+
export declare function registerDashboardLongRunCommand(dashboard: Command, io: ProgramIO): void;
|
|
17
|
+
export declare const DASHBOARD_LONG_RUN_CONSTANTS: {
|
|
18
|
+
readonly WINDOW_CAP_HOURS: 24;
|
|
19
|
+
readonly WINDOW_CAP_MS: number;
|
|
20
|
+
};
|
|
21
|
+
export declare const __test__: {
|
|
22
|
+
parseSince: typeof parseSince;
|
|
23
|
+
boundaryLabel: typeof boundaryLabel;
|
|
24
|
+
};
|
|
25
|
+
export {};
|