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
|
@@ -30,7 +30,7 @@ import { EVOLUTION_TARGET_KINDS, } from "../../services/evolution/evolution-type
|
|
|
30
30
|
import { findProjectRoot } from "../../services/config/config-safety.js";
|
|
31
31
|
import { runIndependentEvaluator, } from "../../services/evolution/independent-evaluator-runner.js";
|
|
32
32
|
import { addJsonOption, getErrorMessage, printResult } from "../cli-helpers.js";
|
|
33
|
-
import { fail, ok } from
|
|
33
|
+
import { fail, ok } from 'peaks-loop-shared/result';
|
|
34
34
|
/**
|
|
35
35
|
* Parse `--target <kind:id>`. Returns the kind + id, or `null` on
|
|
36
36
|
* failure. Validates `kind` against the EVOLUTION_TARGET_KINDS
|
|
@@ -17,7 +17,7 @@ import { resolve as resolvePath } from 'node:path';
|
|
|
17
17
|
import { findProjectRoot } from '../../services/config/config-safety.js';
|
|
18
18
|
import { getCurrentSessionId } from '../../services/skills/skill-presence-service.js';
|
|
19
19
|
import { generatePromotionStub, isPromotionLayer, listUnpromotedFeedback, parseFeedbackMemory, PROMOTION_LAYER_DETAILS, PROMOTION_LAYERS, promoteFeedback } from '../../services/feedback/feedback-promotion-service.js';
|
|
20
|
-
import { fail, ok } from '
|
|
20
|
+
import { fail, ok } from 'peaks-loop-shared/result';
|
|
21
21
|
import { addJsonOption, getErrorMessage, printResult } from '../cli-helpers.js';
|
|
22
22
|
export function registerFeedbackCommands(program, io) {
|
|
23
23
|
const feedback = program
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
import { existsSync, statSync } from 'node:fs';
|
|
21
21
|
import { join, resolve } from 'node:path';
|
|
22
22
|
import { addJsonOption, getErrorMessage, printResult } from '../cli-helpers.js';
|
|
23
|
-
import { fail, ok } from '
|
|
23
|
+
import { fail, ok } from 'peaks-loop-shared/result';
|
|
24
24
|
/** Whitelist of supported `--llm-provider` values for `peaks prepare-final-review`. */
|
|
25
25
|
const SUPPORTED_LLM_PROVIDERS = ['stub'];
|
|
26
26
|
function isSupportedLlmProvider(value) {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { resolve } from 'node:path';
|
|
2
2
|
import { captureDerivedVariant, captureHistoricalFixture, EDGE_CASE_VARIANTS, ENVELOPE_KINDS } from '../../services/fixture/index.js';
|
|
3
3
|
import { addJsonOption, printResult } from '../cli-helpers.js';
|
|
4
|
-
import { fail, ok } from '
|
|
4
|
+
import { fail, ok } from 'peaks-loop-shared/result';
|
|
5
5
|
function isEnvelopeKind(v) {
|
|
6
6
|
return ENVELOPE_KINDS.includes(v);
|
|
7
7
|
}
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
*/
|
|
14
14
|
import { findProjectRoot } from '../../services/config/config-safety.js';
|
|
15
15
|
import { appendSyncRecord, buildForkStatusReport, makeSyncId, readForkState, recommendStableTags, updateSyncRecordStatus, writeForkState } from '../../services/fork/fork-sync-state.js';
|
|
16
|
-
import { fail, ok } from '
|
|
16
|
+
import { fail, ok } from 'peaks-loop-shared/result';
|
|
17
17
|
import { addJsonOption, printResult } from '../cli-helpers.js';
|
|
18
18
|
export function registerForkCommands(program, io) {
|
|
19
19
|
const fork = program
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import { enforceBashCommand, recordGateBypass, GateBypassError } from '../../services/sop/gate-enforce-service.js';
|
|
2
|
-
import {
|
|
2
|
+
import { evaluateWorktreeAuth } from '../../services/hooks/worktree-authorization-gate.js';
|
|
3
|
+
import { getCurrentSessionId } from '../../services/skills/skill-presence-service.js';
|
|
4
|
+
import { fail, ok } from 'peaks-loop-shared/result';
|
|
3
5
|
import { addJsonOption, getErrorMessage, printResult } from '../cli-helpers.js';
|
|
4
|
-
import { detectIdeFromContext, parseClaudeShapeStdin } from '../../services/ide/hook-translator.js';
|
|
6
|
+
import { detectIdeFromContext, parseClaudeShapeStdin, pluckObject, pluckString } from '../../services/ide/hook-translator.js';
|
|
5
7
|
import { getAdapter } from '../../services/ide/ide-registry.js';
|
|
6
|
-
import { emitBlock, emitHint } from '../../services/hooks/output.js';
|
|
8
|
+
import { emitBlock, emitDecision, emitHint } from '../../services/hooks/output.js';
|
|
7
9
|
/**
|
|
8
10
|
* Read the PreToolUse hook payload. `PEAKS_HOOK_STDIN` is a test seam; production
|
|
9
11
|
* reads stdin. The CLI-side stdin reader is intentionally kept here (not in
|
|
@@ -28,6 +30,35 @@ async function readHookPayload() {
|
|
|
28
30
|
process.stdin.on('error', () => resolveStdin(data));
|
|
29
31
|
});
|
|
30
32
|
}
|
|
33
|
+
/**
|
|
34
|
+
* Map Claude Code tool name to the gate's internal `ToolCallKind`. Anything we don't recognize
|
|
35
|
+
* returns `'Other'` — the worktree gate is opt-in by tool, so unknown tools short-circuit to allow.
|
|
36
|
+
*/
|
|
37
|
+
function classifyTool(toolName) {
|
|
38
|
+
if (toolName === 'Bash')
|
|
39
|
+
return 'Bash';
|
|
40
|
+
if (toolName === 'Agent' || toolName === 'Task')
|
|
41
|
+
return 'Agent';
|
|
42
|
+
if (toolName === 'EnterWorktree')
|
|
43
|
+
return 'EnterWorktree';
|
|
44
|
+
if (toolName === 'Workflow')
|
|
45
|
+
return 'Workflow';
|
|
46
|
+
return 'Other';
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Best-effort extraction of the `isolation` field for `Agent` / `Task` tool calls. The Claude
|
|
50
|
+
* hook payload puts args under `tool_input`, the Cursor/Trae sibling puts them under `toolInput`.
|
|
51
|
+
* Both shapes are accepted; everything else returns null.
|
|
52
|
+
*/
|
|
53
|
+
function extractIsolation(parsed) {
|
|
54
|
+
if (parsed === null || typeof parsed !== 'object')
|
|
55
|
+
return null;
|
|
56
|
+
const input = pluckObject(parsed, ['tool_input']) ?? pluckObject(parsed, ['toolInput']);
|
|
57
|
+
if (input === undefined)
|
|
58
|
+
return null;
|
|
59
|
+
const value = pluckString(input, ['isolation']);
|
|
60
|
+
return value === undefined ? null : value;
|
|
61
|
+
}
|
|
31
62
|
export function registerGateCommands(program, io) {
|
|
32
63
|
const gate = program.command('gate').description('SOP gate enforcement (PreToolUse hook handler and bypass)');
|
|
33
64
|
addJsonOption(gate
|
|
@@ -57,12 +88,69 @@ export function registerGateCommands(program, io) {
|
|
|
57
88
|
// change required.
|
|
58
89
|
const { toolName, command } = parseClaudeShapeStdin(parsedStdin);
|
|
59
90
|
if (toolName !== adapter.toolMatcher || typeof command !== 'string' || command.trim().length === 0) {
|
|
60
|
-
// Not a guarded surface — allow
|
|
91
|
+
// Not a guarded surface — allow. Emit minimal JSON on stdout so Claude
|
|
92
|
+
// Code's PreToolUse hook validator accepts the response. Empty stdout
|
|
93
|
+
// is rejected with "Hook JSON output validation failed — Invalid input"
|
|
94
|
+
// in Claude Code 2.x; `{}` is the canonical no-op marker.
|
|
61
95
|
if (options.json === true) {
|
|
62
96
|
printResult(io, ok('gate.enforce', { decision: 'allow', skipped: true }), true);
|
|
63
97
|
}
|
|
98
|
+
else {
|
|
99
|
+
emitDecision(io, {});
|
|
100
|
+
}
|
|
64
101
|
return;
|
|
65
102
|
}
|
|
103
|
+
// slice 2026-07-27-worktree-user-auth: BEFORE the SOP gate runs, check the worktree
|
|
104
|
+
// authorization gate. The worktree gate is narrower than the SOP gate (it only inspects
|
|
105
|
+
// a small set of worktree-mutating operations) and is fail-CLOSED. The two layers are
|
|
106
|
+
// complementary: SOP gates decide "may this command run under this SOP's state", the
|
|
107
|
+
// worktree gate decides "did the user explicitly authorize this worktree-mutating operation
|
|
108
|
+
// in the current task". The worktree gate is cheap and self-contained, so it runs first
|
|
109
|
+
// to give the LLM a clear error reason before the SOP gate would otherwise allow.
|
|
110
|
+
//
|
|
111
|
+
// We extract the tool kind from the hook payload (Bash/Agent/EnterWorktree/Workflow).
|
|
112
|
+
// For Agent/Task we also pull `isolation` from the tool input — only "worktree" isolation
|
|
113
|
+
// is gated. For all other tool kinds the worktree gate is a no-op.
|
|
114
|
+
const toolKind = classifyTool(toolName);
|
|
115
|
+
if (toolKind !== 'Other') {
|
|
116
|
+
const sessionId = getCurrentSessionId(options.project) ?? 'unknown-sid';
|
|
117
|
+
// slice 2026-07-29-worktree-l2-extended Part 2.B: the gate consults
|
|
118
|
+
// the lease file referenced by `PEAKS_WORKTREE_LEASE_ID` as a
|
|
119
|
+
// second authorization path when no `peaks worktree auth grant`
|
|
120
|
+
// is on file. Dispatch (Part 2.C) injects the env var on every
|
|
121
|
+
// sub-agent spawn so worktree-mutating tool calls from inside
|
|
122
|
+
// the sub-agent process auto-authorize via the lease instead
|
|
123
|
+
// of requiring a separate grant.
|
|
124
|
+
const leaseId = process.env.PEAKS_WORKTREE_LEASE_ID ?? null;
|
|
125
|
+
// Part 19: container lease (L4) is the parallel path for
|
|
126
|
+
// `--isolation container` sub-agents. The env is set by the
|
|
127
|
+
// dispatch command (Part 8 + Part 12) at spawn time.
|
|
128
|
+
const containerLeaseId = process.env.PEAKS_CONTAINER_LEASE_ID ?? null;
|
|
129
|
+
const wtDecision = evaluateWorktreeAuth({
|
|
130
|
+
projectRoot: options.project,
|
|
131
|
+
sessionId,
|
|
132
|
+
toolName: toolKind,
|
|
133
|
+
command: toolKind === 'Bash' ? command : null,
|
|
134
|
+
isolation: toolKind === 'Agent' || toolKind === 'EnterWorktree' ? extractIsolation(parsedStdin) : null,
|
|
135
|
+
requestId: null,
|
|
136
|
+
leaseId: leaseId !== null && /^[a-f0-9]{16}$/.test(leaseId) ? leaseId : null,
|
|
137
|
+
containerLeaseId: containerLeaseId !== null && /^[a-f0-9]{16}$/.test(containerLeaseId) ? containerLeaseId : null
|
|
138
|
+
});
|
|
139
|
+
if (!wtDecision.allow) {
|
|
140
|
+
// Hard block: a worktree-mutating tool call without a current-task user grant.
|
|
141
|
+
// emitBlock writes the Claude Code permissionDecision:"deny" envelope and exits 2.
|
|
142
|
+
// The reason includes the remediation hint so the LLM can run `peaks worktree auth grant`
|
|
143
|
+
// and retry, and the user can read the deny text in the next-turn stderr.
|
|
144
|
+
emitBlock(io, `[worktree-gate:${wtDecision.code}] ${wtDecision.reason} — ${wtDecision.remediation}`);
|
|
145
|
+
if (options.json === true) {
|
|
146
|
+
emitHint(io, JSON.stringify(ok('gate.enforce', { decision: 'deny', layer: 'worktree-auth', ...wtDecision })));
|
|
147
|
+
}
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
// allow: continue to the SOP gate (the regular enforcement path).
|
|
151
|
+
// wtDecision may have arrived via lease (viaLease != null); the SOP gate does not
|
|
152
|
+
// care which path granted, only that the worktree gate said allow.
|
|
153
|
+
}
|
|
66
154
|
const decision = await enforceBashCommand(options.project, command);
|
|
67
155
|
if (decision.decision === 'deny') {
|
|
68
156
|
// PRD#2 (2026-06-16-fact-forcing-gate-format): a true SOP gate failure is
|
|
@@ -83,14 +171,48 @@ export function registerGateCommands(program, io) {
|
|
|
83
171
|
}
|
|
84
172
|
}
|
|
85
173
|
if (options.json === true) {
|
|
86
|
-
|
|
174
|
+
// --json mode: emit the canonical envelope on stdout (the
|
|
175
|
+
// documented contract for downstream tooling). The hook
|
|
176
|
+
// decision (allow/deny) is still signalled via process.exitCode
|
|
177
|
+
// + the minimal `{}` on stdout in non-`--json` mode.
|
|
178
|
+
// Slice 2026-07-30-nightshift: emitHint here was wrong —
|
|
179
|
+
// emitHint writes to stderr, but the AC5 contract expects
|
|
180
|
+
// the JSON envelope on stdout. Switch to printResult(asJson=true).
|
|
181
|
+
printResult(io, ok('gate.enforce', decision), true);
|
|
182
|
+
}
|
|
183
|
+
else {
|
|
184
|
+
// allow: emit minimal JSON on stdout so Claude Code's PreToolUse hook
|
|
185
|
+
// validator accepts the response (see comment above).
|
|
186
|
+
emitDecision(io, {});
|
|
87
187
|
}
|
|
88
|
-
// allow: emit nothing on stdout → normal permission flow.
|
|
89
188
|
}
|
|
90
189
|
catch (error) {
|
|
91
190
|
// Fail-open: a bug in enforcement must not brick Claude Code.
|
|
92
191
|
emitHint(io, `gate enforce: internal error, allowing command (${getErrorMessage(error)})`);
|
|
93
192
|
}
|
|
193
|
+
// Slice 2026-07-29-windows-console-flash Part 46: actively
|
|
194
|
+
// exit the process when the gate-enforce handler returns.
|
|
195
|
+
// The default behavior is to wait for stdin EOF before
|
|
196
|
+
// exiting, but the hook's stdin pipe may not close promptly
|
|
197
|
+
// (the parent can keep the handle open even after it has
|
|
198
|
+
// finished reading the JSON decision), which causes 4
|
|
199
|
+
// Node.js processes to accumulate in the user's task
|
|
200
|
+
// manager. An explicit process.exit here forces the gate
|
|
201
|
+
// process to release all handles and exit immediately.
|
|
202
|
+
//
|
|
203
|
+
// Slice 2026-07-30-nightshift: gate the `process.exit` on
|
|
204
|
+
// being invoked as a real CLI hook (no PEAKS_HOOK_STDIN test
|
|
205
|
+
// seam, no PEAKS_TEST_SEAM env var). The test harness calls
|
|
206
|
+
// `runCommand` which invokes the same action handler in
|
|
207
|
+
// process — calling `process.exit` inside the test would
|
|
208
|
+
// kill the vitest runner before assertions complete. The
|
|
209
|
+
// `process.exitCode` set above is sufficient for the test
|
|
210
|
+
// path: Commander's `parseAsync` resolves the action, then
|
|
211
|
+
// the harness reads the captured stdout/stderr before the
|
|
212
|
+
// runner exits normally with the recorded exit code.
|
|
213
|
+
if (process.env.PEAKS_HOOK_STDIN === undefined && process.env.PEAKS_TEST_SEAM === undefined) {
|
|
214
|
+
process.exit(process.exitCode || 0);
|
|
215
|
+
}
|
|
94
216
|
});
|
|
95
217
|
addJsonOption(gate
|
|
96
218
|
.command('bypass')
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* rid-007 Family 1 merge: contract-commands + classify-classify-commands.
|
|
3
|
+
*
|
|
4
|
+
* Pre-merge originals (now deleted):
|
|
5
|
+
* - src/cli/commands/contract-commands.ts (167 lines)
|
|
6
|
+
* - src/cli/commands/classify-classify-commands.ts (190 lines)
|
|
7
|
+
*
|
|
8
|
+
* Both exported `register*Commands` functions are preserved **verbatim**
|
|
9
|
+
* (function name, signature, body). The merge is a structural refactor
|
|
10
|
+
* only — no behavior change. `autoRegisterAllCommands` discovers both
|
|
11
|
+
* exports from this single file.
|
|
12
|
+
*
|
|
13
|
+
* Why one file: the RD slice rid-007 user-confirmed one-family merge
|
|
14
|
+
* minimum; Family 1 is the cleanest demonstration because both inputs
|
|
15
|
+
* sit under 200 lines and the merged file lands at ~360 lines — well
|
|
16
|
+
* below the 400-line AC-9 sanity guard and the 800-line scan gate.
|
|
17
|
+
*/
|
|
18
|
+
import { Command } from 'commander';
|
|
19
|
+
import { type ProgramIO } from '../cli-helpers.js';
|
|
20
|
+
export declare function registerContractCommands(program: Command, io: ProgramIO): void;
|
|
21
|
+
export declare function registerClassifyCommands(program: Command, io: ProgramIO): void;
|
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* rid-007 Family 1 merge: contract-commands + classify-classify-commands.
|
|
3
|
+
*
|
|
4
|
+
* Pre-merge originals (now deleted):
|
|
5
|
+
* - src/cli/commands/contract-commands.ts (167 lines)
|
|
6
|
+
* - src/cli/commands/classify-classify-commands.ts (190 lines)
|
|
7
|
+
*
|
|
8
|
+
* Both exported `register*Commands` functions are preserved **verbatim**
|
|
9
|
+
* (function name, signature, body). The merge is a structural refactor
|
|
10
|
+
* only — no behavior change. `autoRegisterAllCommands` discovers both
|
|
11
|
+
* exports from this single file.
|
|
12
|
+
*
|
|
13
|
+
* Why one file: the RD slice rid-007 user-confirmed one-family merge
|
|
14
|
+
* minimum; Family 1 is the cleanest demonstration because both inputs
|
|
15
|
+
* sit under 200 lines and the merged file lands at ~360 lines — well
|
|
16
|
+
* below the 400-line AC-9 sanity guard and the 800-line scan gate.
|
|
17
|
+
*/
|
|
18
|
+
// ===========================================================================
|
|
19
|
+
// Shared imports (consolidated; both originals pulled names from these
|
|
20
|
+
// same modules — `cli-helpers.ts` re-exports `getErrorMessage` from
|
|
21
|
+
// `peaks-loop-shared/result`, so the single canonical import is
|
|
22
|
+
// functionally identical to either original line.)
|
|
23
|
+
// ===========================================================================
|
|
24
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
25
|
+
import { join } from 'node:path';
|
|
26
|
+
import { fail, ok, getErrorMessage } from 'peaks-loop-shared/result';
|
|
27
|
+
import { addJsonOption, printResult } from '../cli-helpers.js';
|
|
28
|
+
import { writeContract } from '../../services/dispatch/contract-store.js';
|
|
29
|
+
import { getCurrentSessionId } from '../../services/skills/skill-presence-service.js';
|
|
30
|
+
import { classifyTask } from '../../services/classify/classify-service.js';
|
|
31
|
+
import { TASK_LEVELS } from '../../services/classify/classify-types.js';
|
|
32
|
+
import { loadPreferences } from '../../services/preferences/preferences-service.js';
|
|
33
|
+
// ===========================================================================
|
|
34
|
+
// Section 1: `peaks contract` CLI commands (was contract-commands.ts).
|
|
35
|
+
// ===========================================================================
|
|
36
|
+
const INPUT_LIMIT_BYTES = 256 * 1024;
|
|
37
|
+
/** Split a comma-separated flag value into a trimmed string array. */
|
|
38
|
+
function splitCsv(value) {
|
|
39
|
+
if (value === undefined)
|
|
40
|
+
return [];
|
|
41
|
+
return value
|
|
42
|
+
.split(',')
|
|
43
|
+
.map((s) => s.trim())
|
|
44
|
+
.filter((s) => s.length > 0);
|
|
45
|
+
}
|
|
46
|
+
export function registerContractCommands(program, io) {
|
|
47
|
+
const contract = program
|
|
48
|
+
.command('contract')
|
|
49
|
+
.description('Slice contract store (skill-first / CLI-auxiliary). These commands ' +
|
|
50
|
+
'are primitives that peaks-code / peaks-rd SKILL.md compose. The LLM-side ' +
|
|
51
|
+
'runner (the IDE-resident sub-agent that finished a slice) calls ' +
|
|
52
|
+
'`peaks contract write` to persist the slice\'s public surface; the ' +
|
|
53
|
+
'orchestrator picks it up on the next dispatch run via listContracts() ' +
|
|
54
|
+
'and splices it into downstream prompts via formatContractInjection().');
|
|
55
|
+
// ─────────────────────────────────────────────────────────────────
|
|
56
|
+
// peaks contract write --project <root> --session-id <sid>
|
|
57
|
+
// --slice-id <id> --exports <a,b> --types <x,y> --signatures <s1,s2>
|
|
58
|
+
// [--broadcast-to <b1,b2>] [--completed-at <iso>]
|
|
59
|
+
// ─────────────────────────────────────────────────────────────────
|
|
60
|
+
addJsonOption(contract
|
|
61
|
+
.command('write')
|
|
62
|
+
.description('2.7.0 slice-dag-dispatcher MVP: persist a finished slice\'s public ' +
|
|
63
|
+
'surface (exports / types / publicSignatures) to disk at ' +
|
|
64
|
+
'.peaks/_runtime/<sessionId>/dispatch/contracts/<slice-id>.json. ' +
|
|
65
|
+
'The orchestrator picks it up on the next dispatch run. Idempotent: ' +
|
|
66
|
+
're-running with the same inputs overwrites in place; the SHA-256 ' +
|
|
67
|
+
'contractHash is content-derived so a contract write from a different ' +
|
|
68
|
+
'runner (re-execution) is detected as a content change.')
|
|
69
|
+
.option('--project <path>', 'target project root (defaults to cwd)')
|
|
70
|
+
.option('--session-id <sid>', 'session id (default: resolve from .peaks/_runtime/session.json; falls back to PEAKS_SESSION_ID env var; final fallback "unknown-sid")')
|
|
71
|
+
.requiredOption('--slice-id <id>', 'slice id; must be non-empty; used as the contract filename basename')
|
|
72
|
+
.option('--exports <list>', 'comma-separated public export names (e.g. "validateDag,topologicalLevels")')
|
|
73
|
+
.option('--types <list>', 'comma-separated public type names (e.g. "SliceDag,SliceNode")')
|
|
74
|
+
.option('--signatures <list>', 'comma-separated public function/method signatures (e.g. "validateDag(dag: SliceDag): void")')
|
|
75
|
+
.option('--broadcast-to <list>', 'comma-separated downstream slice ids that should auto-inherit this contract (e.g. "B,C")')
|
|
76
|
+
.option('--completed-at <iso>', 'ISO 8601 timestamp; defaults to now()')).action((options) => {
|
|
77
|
+
const asJson = options.json === true;
|
|
78
|
+
const projectRoot = options.project ?? process.cwd();
|
|
79
|
+
// Slice 2026-06-26-unknown-sid-fallback-fix: see dispatch-commands.ts.
|
|
80
|
+
const sid = options.sessionId
|
|
81
|
+
?? process.env.PEAKS_SESSION_ID
|
|
82
|
+
?? getCurrentSessionId(projectRoot)
|
|
83
|
+
?? 'unknown-sid';
|
|
84
|
+
const sliceId = options.sliceId;
|
|
85
|
+
if (sliceId === undefined || sliceId.length === 0) {
|
|
86
|
+
printResult(io, fail('contract.write', 'MISSING_SLICE_ID', '--slice-id is required', { path: null, contract: null }, [
|
|
87
|
+
'Re-run with --slice-id <id> (must be non-empty; used as the contract filename basename).'
|
|
88
|
+
]), asJson);
|
|
89
|
+
process.exitCode = 1;
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
const exports = splitCsv(options.exports);
|
|
93
|
+
const types = splitCsv(options.types);
|
|
94
|
+
const signatures = splitCsv(options.signatures);
|
|
95
|
+
const broadcastTo = splitCsv(options.broadcastTo);
|
|
96
|
+
// Cap the combined input to protect the file IO from runaway values
|
|
97
|
+
// (e.g. a 10MB comma-separated --signatures flag).
|
|
98
|
+
const inputSize = sliceId.length +
|
|
99
|
+
sid.length +
|
|
100
|
+
exports.join(',').length +
|
|
101
|
+
types.join(',').length +
|
|
102
|
+
signatures.join(',').length +
|
|
103
|
+
broadcastTo.join(',').length +
|
|
104
|
+
(options.completedAt?.length ?? 0);
|
|
105
|
+
if (inputSize > INPUT_LIMIT_BYTES) {
|
|
106
|
+
printResult(io, fail('contract.write', 'INPUT_TOO_LARGE', `combined input size ${inputSize} bytes exceeds ${INPUT_LIMIT_BYTES} (likely oversized --exports/--types/--signatures lists)`, { path: null, contract: null }, [
|
|
107
|
+
'Split the slice into smaller surfaces or omit optional fields.'
|
|
108
|
+
]), asJson);
|
|
109
|
+
process.exitCode = 1;
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
try {
|
|
113
|
+
const input = {
|
|
114
|
+
sliceId,
|
|
115
|
+
sessionId: sid,
|
|
116
|
+
exports,
|
|
117
|
+
types,
|
|
118
|
+
publicSignatures: signatures,
|
|
119
|
+
...(broadcastTo.length > 0 ? { broadcastTo } : {}),
|
|
120
|
+
...(options.completedAt !== undefined ? { completedAt: options.completedAt } : {})
|
|
121
|
+
};
|
|
122
|
+
const result = writeContract(projectRoot, sid, input);
|
|
123
|
+
printResult(io, ok('contract.write', {
|
|
124
|
+
path: result.path,
|
|
125
|
+
contractHash: result.contract.contractHash,
|
|
126
|
+
sliceId: result.contract.sliceId,
|
|
127
|
+
sessionId: result.contract.sessionId,
|
|
128
|
+
completedAt: result.contract.completedAt,
|
|
129
|
+
exportCount: result.contract.exports.length,
|
|
130
|
+
typeCount: result.contract.types.length,
|
|
131
|
+
signatureCount: result.contract.publicSignatures.length,
|
|
132
|
+
broadcastTo: result.contract.broadcastTo ?? []
|
|
133
|
+
}, [], [
|
|
134
|
+
`Contract written; orchestrator will pick it up on the next \`peaks sub-agent dispatch --from-dag\` run.`,
|
|
135
|
+
`Re-running with the same inputs is idempotent (overwrites in place).`
|
|
136
|
+
]), asJson);
|
|
137
|
+
}
|
|
138
|
+
catch (err) {
|
|
139
|
+
printResult(io, fail('contract.write', 'WRITE_ERROR', getErrorMessage(err), { path: null, contract: null }, [
|
|
140
|
+
'See error message; check that --project is a writable directory and --slice-id is a valid filename basename (no path separators).'
|
|
141
|
+
]), asJson);
|
|
142
|
+
process.exitCode = 1;
|
|
143
|
+
}
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
const CLASSIFY_AUDIT_FILE = 'classify-audit.jsonl';
|
|
147
|
+
function getSignalsFromGitDiff(projectRoot) {
|
|
148
|
+
// Use git diff --stat to extract file count + line count. Fall back to
|
|
149
|
+
// zeros if git is unavailable (e.g. fresh repo with no commits).
|
|
150
|
+
let stdout;
|
|
151
|
+
try {
|
|
152
|
+
const { execFileSync } = require('node:child_process');
|
|
153
|
+
stdout = execFileSync('git', ['diff', '--shortstat', 'HEAD'], {
|
|
154
|
+
cwd: projectRoot,
|
|
155
|
+
stdio: ['ignore', 'pipe', 'ignore'],
|
|
156
|
+
maxBuffer: 32 * 1024 * 1024,
|
|
157
|
+
}).toString('utf8');
|
|
158
|
+
}
|
|
159
|
+
catch {
|
|
160
|
+
return { filesChanged: 0, linesChanged: 0, touchesDependencies: false, touchesMigrationScripts: false, isPureRefactor: true, keywords: [] };
|
|
161
|
+
}
|
|
162
|
+
const lines = stdout.split('\n').filter((l) => l.trim().length > 0);
|
|
163
|
+
const filesChanged = lines.length;
|
|
164
|
+
let added = 0;
|
|
165
|
+
let removed = 0;
|
|
166
|
+
let touchesDependencies = false;
|
|
167
|
+
let touchesMigrationScripts = false;
|
|
168
|
+
for (const line of lines) {
|
|
169
|
+
const match = /(\d+)\s+insertion.*?(\d+)\s+deletion/.exec(line);
|
|
170
|
+
if (match) {
|
|
171
|
+
added += Number(match[1]);
|
|
172
|
+
removed += Number(match[2]);
|
|
173
|
+
}
|
|
174
|
+
if (/(package\.json|pnpm-lock\.yaml|yarn\.lock|requirements\.txt|go\.mod)/.test(line)) {
|
|
175
|
+
touchesDependencies = true;
|
|
176
|
+
}
|
|
177
|
+
if (/(migrate|codemod|backfill|schema)/.test(line)) {
|
|
178
|
+
touchesMigrationScripts = true;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
// isPureRefactor: heuristic — if added lines / removed lines < 0.1 OR
|
|
182
|
+
// no new exports were added, treat as refactor. For L2.2 the signal is
|
|
183
|
+
// binary (true/false). Default: true (no behavior change is the safe
|
|
184
|
+
// assumption; flip to false when keyword 'add' / 'new' / 'feature' present).
|
|
185
|
+
const isPureRefactor = true;
|
|
186
|
+
return {
|
|
187
|
+
filesChanged,
|
|
188
|
+
linesChanged: added + removed,
|
|
189
|
+
touchesDependencies,
|
|
190
|
+
touchesMigrationScripts,
|
|
191
|
+
isPureRefactor,
|
|
192
|
+
keywords: [],
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
function appendAuditEntry(projectRoot, entry) {
|
|
196
|
+
const auditDir = join(projectRoot, '.peaks/_runtime');
|
|
197
|
+
if (!existsSync(auditDir)) {
|
|
198
|
+
try {
|
|
199
|
+
mkdirSync(auditDir, { recursive: true });
|
|
200
|
+
}
|
|
201
|
+
catch { /* ignore */ } // TODO(g2): legacy silent catch — grace: 1 minor release (v2.14.0)
|
|
202
|
+
}
|
|
203
|
+
const auditPath = join(auditDir, CLASSIFY_AUDIT_FILE);
|
|
204
|
+
let body = '';
|
|
205
|
+
try {
|
|
206
|
+
if (existsSync(auditPath)) {
|
|
207
|
+
body = readFileSync(auditPath, 'utf8');
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
catch { /* ignore */ } // TODO(g2): legacy silent catch — grace: 1 minor release (v2.14.0)
|
|
211
|
+
body += JSON.stringify(entry) + '\n';
|
|
212
|
+
try {
|
|
213
|
+
writeFileSync(auditPath, body);
|
|
214
|
+
}
|
|
215
|
+
catch { /* best-effort */ } // TODO(g2): legacy silent catch — grace: 1 minor release (v2.14.0)
|
|
216
|
+
}
|
|
217
|
+
function isTaskLevel(value) {
|
|
218
|
+
return TASK_LEVELS.includes(value);
|
|
219
|
+
}
|
|
220
|
+
export function registerClassifyCommands(program, io) {
|
|
221
|
+
const classify = program
|
|
222
|
+
.command('classify')
|
|
223
|
+
.description('L1a task classification: 5-level heuristic (typo/bug/feature/refactor/migration) + override/upgrade + audit log');
|
|
224
|
+
addJsonOption(classify
|
|
225
|
+
.command('run')
|
|
226
|
+
.description('Classify the current diff (git diff HEAD) into one of 5 task levels')
|
|
227
|
+
.requiredOption('--project <path>', 'target project root')
|
|
228
|
+
.option('--override <level>', 'force a level (one of typo|bug|feature|refactor|migration); requires --reason')
|
|
229
|
+
.option('--reason <text>', 'reason for the override (mandatory when --override is set)')).action(async (options) => {
|
|
230
|
+
try {
|
|
231
|
+
const prefs = await loadPreferences(options.project);
|
|
232
|
+
const signals = getSignalsFromGitDiff(options.project);
|
|
233
|
+
let override;
|
|
234
|
+
if (options.override !== undefined) {
|
|
235
|
+
if (!isTaskLevel(options.override)) {
|
|
236
|
+
printResult(io, fail('classify.run', 'INVALID_LEVEL', `level must be one of: ${TASK_LEVELS.join(', ')}`, { provided: options.override }, ['Pass one of typo, bug, feature, refactor, migration']), options.json);
|
|
237
|
+
process.exitCode = 1;
|
|
238
|
+
return;
|
|
239
|
+
}
|
|
240
|
+
if (options.reason === undefined || options.reason.length === 0) {
|
|
241
|
+
printResult(io, fail('classify.run', 'REASON_REQUIRED', '--reason is required when --override is set', {}, ['Provide a non-empty reason for the override']), options.json);
|
|
242
|
+
process.exitCode = 1;
|
|
243
|
+
return;
|
|
244
|
+
}
|
|
245
|
+
override = { level: options.override, reason: options.reason };
|
|
246
|
+
}
|
|
247
|
+
const result = classifyTask(override !== undefined
|
|
248
|
+
? { signals, conservatism: prefs.classifyConservatism, override }
|
|
249
|
+
: { signals, conservatism: prefs.classifyConservatism }, prefs.classifyRules.feature_threshold_files, prefs.classifyRules.feature_threshold_lines);
|
|
250
|
+
appendAuditEntry(options.project, result.audit);
|
|
251
|
+
printResult(io, ok('classify.run', result, [], [
|
|
252
|
+
`gate set for level "${result.level}": ${result.gateSet.stages.join(', ')}`,
|
|
253
|
+
`audit log: .peaks/_runtime/${CLASSIFY_AUDIT_FILE}`,
|
|
254
|
+
]), options.json);
|
|
255
|
+
}
|
|
256
|
+
catch (error) {
|
|
257
|
+
printResult(io, fail('classify.run', 'CLASSIFY_RUN_FAILED', getErrorMessage(error), { projectRoot: options.project }, ['Run peaks classify --help for usage']), options.json);
|
|
258
|
+
process.exitCode = 1;
|
|
259
|
+
}
|
|
260
|
+
});
|
|
261
|
+
// Downgrade is REFUSED per spec §4. Surface this as a hard fail.
|
|
262
|
+
classify
|
|
263
|
+
.command('downgrade')
|
|
264
|
+
.description('REFUSED per spec §4 — peaks-loop never downgrades a classification; ask the user to override explicitly')
|
|
265
|
+
.requiredOption('--level <level>', 'attempted level')
|
|
266
|
+
.requiredOption('--reason <text>', 'reason for the attempt (always rejected)')
|
|
267
|
+
.requiredOption('--project <path>', 'target project root')
|
|
268
|
+
.option('--json', 'print machine-readable JSON envelope')
|
|
269
|
+
.action(async (options) => {
|
|
270
|
+
printResult(io, fail('classify.downgrade', 'DOWNGRADE_REFUSED', 'peaks classify downgrade is refused per spec §4. Use --override (with reason) on `classify run` to force a level; the CLI never downgrades a classification unilaterally.', { attemptedLevel: options.level, reason: options.reason }, ['Use `peaks classify run --override <level> --reason "<text>"` instead']), options.json);
|
|
271
|
+
process.exitCode = 2;
|
|
272
|
+
});
|
|
273
|
+
}
|
|
@@ -12,9 +12,10 @@
|
|
|
12
12
|
* point at a sensitive file outside the runtime tree.
|
|
13
13
|
*/
|
|
14
14
|
import { existsSync } from 'node:fs';
|
|
15
|
-
import { fail, getErrorMessage, ok } from '
|
|
15
|
+
import { fail, getErrorMessage, ok } from 'peaks-loop-shared/result';
|
|
16
16
|
import { addJsonOption, printResult } from '../cli-helpers.js';
|
|
17
|
-
import { appendHeartbeat } from '../../services/dispatch/dispatch-record-writer.js';
|
|
17
|
+
import { appendHeartbeat, setStage, tryAutoReleaseLease } from '../../services/dispatch/dispatch-record-writer.js';
|
|
18
|
+
import { isStageLabel } from '../../services/dispatch/stage-enum.js';
|
|
18
19
|
import { assertSafeDispatchRecordPath } from '../../services/security/safe-settings-path.js';
|
|
19
20
|
import { writeLogEntry } from '../../services/log/logger.js';
|
|
20
21
|
import { HEARTBEAT_STATUSES } from './sub-agent-shared.js';
|
|
@@ -26,9 +27,15 @@ export function registerHeartbeatCommand(parent, io) {
|
|
|
26
27
|
'wait and renders a status line. Sub-agents should call this at ' +
|
|
27
28
|
'least every 30s (configurable via SKILL.md heartbeatIntervalSec).')
|
|
28
29
|
.requiredOption('--record <path>', 'absolute path to a dispatch record JSON')
|
|
29
|
-
.requiredOption('--status <state>', 'queued | running | finalizing | done | failed | stale')
|
|
30
|
+
.requiredOption('--status <state>', 'queued | running | finalizing | done | failed | stale | cancelled | no-execution | never-started | unreadable')
|
|
30
31
|
.requiredOption('--progress <pct>', 'integer 0-100')
|
|
31
32
|
.option('--note <text>', 'free-form progress note (≤ 200 chars)')
|
|
33
|
+
// Slice 2026-07-29-dispatch-stall-governance / S5 (AC-5.2) —
|
|
34
|
+
// optional --stage flag. The label is bounded (see
|
|
35
|
+
// src/services/dispatch/stage-enum.ts); an unknown value is
|
|
36
|
+
// rejected with INVALID_STAGE so the watch surface never
|
|
37
|
+
// accumulates typo'd labels.
|
|
38
|
+
.option('--stage <label>', 'bounded stage label (intake | planning | gathering | analyzing | writing | testing | reviewing | finalizing)')
|
|
32
39
|
.option('--project <path>', 'trusted project root (defaults to cwd); used for the R-2 path guard so a malicious --record cannot point at another project\'s dispatch record')).action((options) => {
|
|
33
40
|
const asJson = options.json === true;
|
|
34
41
|
if (!options.record || !existsSync(options.record)) {
|
|
@@ -69,21 +76,85 @@ export function registerHeartbeatCommand(parent, io) {
|
|
|
69
76
|
// root's .peaks/_sub_agents/ subdir.
|
|
70
77
|
const trustedRoot = options.project ?? process.cwd();
|
|
71
78
|
assertSafeDispatchRecordPath(options.record, trustedRoot);
|
|
79
|
+
// Slice 2026-07-29-dispatch-stall-governance / S5 (AC-5.2) —
|
|
80
|
+
// validate the optional --stage against the bounded enum
|
|
81
|
+
// BEFORE the heartbeat write so the caller gets a specific
|
|
82
|
+
// INVALID_STAGE error rather than a generic HEARTBEAT_ERROR.
|
|
83
|
+
let stageLabel = null;
|
|
84
|
+
if (options.stage !== undefined && options.stage.length > 0) {
|
|
85
|
+
if (!isStageLabel(options.stage)) {
|
|
86
|
+
printResult(io, fail('sub-agent.heartbeat', 'INVALID_STAGE', `--stage must be one of intake | planning | gathering | analyzing | writing | testing | reviewing | finalizing (got ${options.stage})`, { recordPath: options.record, truncated: false }, [
|
|
87
|
+
'Use one of the bounded stage labels; free-form text is not accepted.'
|
|
88
|
+
]), asJson);
|
|
89
|
+
process.exitCode = 1;
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
stageLabel = options.stage;
|
|
93
|
+
}
|
|
72
94
|
const result = appendHeartbeat({
|
|
73
95
|
recordPath: options.record,
|
|
74
96
|
status: options.status,
|
|
75
97
|
progress,
|
|
76
98
|
...(options.note !== undefined ? { note: options.note } : {})
|
|
77
99
|
});
|
|
100
|
+
// S5 — promote the stage if --stage was supplied. setStage
|
|
101
|
+
// re-validates and is a no-op when stage is null. The
|
|
102
|
+
// appendHeartbeat call above already wrote the heartbeat;
|
|
103
|
+
// setStage is a separate file-locked read-modify-write on the
|
|
104
|
+
// same record.
|
|
105
|
+
if (stageLabel !== null) {
|
|
106
|
+
setStage({ recordPath: options.record, stage: stageLabel });
|
|
107
|
+
}
|
|
108
|
+
// Slice 2026-07-29-worktree-l2-extended Part 24: the
|
|
109
|
+
// heartbeat envelope surfaces a `leaseHint` field when the
|
|
110
|
+
// dispatch owns a lease. The hint is a one-line reminder
|
|
111
|
+
// for the sub-agent that the lease is the L2 surface it
|
|
112
|
+
// should clean up before exiting (or, per Part 3.A.2, the
|
|
113
|
+
// auto-release hook will fire on the next terminal status).
|
|
114
|
+
// The hint is opt-in via a flag so non-lease dispatches
|
|
115
|
+
// do not pollute the envelope.
|
|
116
|
+
const leaseId = result.record.leaseId ?? null;
|
|
117
|
+
const leaseHint = leaseId !== null
|
|
118
|
+
? `You own lease \`${leaseId}\`. Run \`peaks worktree release --lease-id ${leaseId}\` before exit, or report a terminal status (done/failed/cancelled) to let peaks-loop auto-release.`
|
|
119
|
+
: null;
|
|
78
120
|
printResult(io, ok('sub-agent.heartbeat', {
|
|
79
121
|
// Slice 2026-06-23-audit-4th #E1: envelopeVersion marker
|
|
80
|
-
|
|
122
|
+
// Part 24: bumped to 2.2.0 to advertise the new `leaseHint`
|
|
123
|
+
// field; readers from 2.1.0 ignore the unknown field.
|
|
124
|
+
envelopeVersion: '2.2.0',
|
|
81
125
|
recordPath: options.record,
|
|
82
126
|
heartbeatCount: result.record.heartbeats.length,
|
|
83
127
|
lastBeatAt: result.record.lastBeatAt,
|
|
84
128
|
status: result.record.status,
|
|
85
|
-
truncated: result.truncated
|
|
129
|
+
truncated: result.truncated,
|
|
130
|
+
leaseHint
|
|
86
131
|
}, [], ['Continue business logic; heartbeat is fire-and-forget.']), asJson);
|
|
132
|
+
// Slice 2026-07-29-worktree-l2-extended Part 3.A.2: when the
|
|
133
|
+
// heartbeat reports a TERMINAL status (done / failed / cancelled
|
|
134
|
+
// / no-execution) AND the dispatch owns a lease, fire the
|
|
135
|
+
// detached release. This is the common path: most sub-agents
|
|
136
|
+
// finalize via heartbeat before the share-reducer calls
|
|
137
|
+
// markCompleted, so the heartbeat path is the more frequent
|
|
138
|
+
// release trigger in practice. markCompleted's hook (Part 3.A.1)
|
|
139
|
+
// remains as the safety net for sub-agents that finalize via
|
|
140
|
+
// share without a terminal heartbeat. The release is detached
|
|
141
|
+
// + best-effort; a failure here cannot roll back the heartbeat
|
|
142
|
+
// write (the printResult above already shipped the response).
|
|
143
|
+
const terminalHeartbeatStatuses = new Set([
|
|
144
|
+
'done', 'failed', 'cancelled', 'no-execution'
|
|
145
|
+
]);
|
|
146
|
+
if (terminalHeartbeatStatuses.has(options.status) && result.record.leaseId !== null) {
|
|
147
|
+
try {
|
|
148
|
+
tryAutoReleaseLease({
|
|
149
|
+
projectRoot: trustedRoot,
|
|
150
|
+
sessionId: result.record.sessionId,
|
|
151
|
+
leaseId: result.record.leaseId
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
catch { // best-effort
|
|
155
|
+
/* swallow */
|
|
156
|
+
}
|
|
157
|
+
}
|
|
87
158
|
// Slice 2026-06-23-audit-4th #B1: structured log on success.
|
|
88
159
|
try {
|
|
89
160
|
writeLogEntry({
|