peaks-loop 4.0.0-beta.9 → 4.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +910 -141
- package/README-en.md +26 -7
- package/README.md +75 -6
- 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 +47 -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
|
@@ -1,929 +1,25 @@
|
|
|
1
|
-
import { join } from 'node:path';
|
|
2
|
-
import { homedir } from 'node:os';
|
|
3
|
-
import { existsSync, lstatSync, readFileSync, readdirSync } from 'node:fs';
|
|
4
|
-
import { createRequire } from 'node:module';
|
|
5
|
-
import { dirname, resolve as resolvePath } from 'node:path';
|
|
6
|
-
import { readText } from '../../shared/fs.js';
|
|
7
|
-
import { requiredSchemaFiles, requiredSkillNames, schemasDir } from '../../shared/paths.js';
|
|
8
|
-
import { getErrorMessage } from '../../shared/result.js';
|
|
9
|
-
import { loadSkillRegistry } from '../skills/skill-registry.js';
|
|
10
|
-
import { getSkillPresence } from '../skills/skill-presence-service.js';
|
|
11
|
-
import { planStatusLineInstall } from '../skills/statusline-settings-service.js';
|
|
12
|
-
import { findProjectRoot } from '../config/config-safety.js';
|
|
13
|
-
import { isValidSessionId } from '../workspace/sid-naming-guard.js';
|
|
14
|
-
import { CLI_VERSION } from '../../shared/version.js';
|
|
15
|
-
const CODEGRAPH_EXPECTED_VERSION = '0.7.10';
|
|
16
|
-
const SKILL_PRESENCE_FRESHNESS_THRESHOLD_MS = 24 * 60 * 60 * 1000;
|
|
17
|
-
function defaultCodegraphProbe() {
|
|
18
|
-
const require = createRequire(import.meta.url);
|
|
19
|
-
const packagePath = require.resolve('@colbymchenry/codegraph/package.json');
|
|
20
|
-
const pkg = require(packagePath);
|
|
21
|
-
const binaryPath = resolvePath(dirname(packagePath), 'dist', 'bin', 'codegraph.js');
|
|
22
|
-
return {
|
|
23
|
-
packagePath,
|
|
24
|
-
version: pkg.version ?? 'unknown',
|
|
25
|
-
binaryPath,
|
|
26
|
-
binaryExists: existsSync(binaryPath)
|
|
27
|
-
};
|
|
28
|
-
}
|
|
29
|
-
function defaultStatusLineInstalledProbe() {
|
|
30
|
-
const projectRoot = findProjectRoot(process.cwd());
|
|
31
|
-
// Check both scopes: a user may have installed the statusLine globally, which
|
|
32
|
-
// the project-only check would miss and falsely report as "not installed".
|
|
33
|
-
try {
|
|
34
|
-
if (projectRoot !== null && planStatusLineInstall('project', projectRoot).alreadyInstalled) {
|
|
35
|
-
return true;
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
catch { // TODO(g2): legacy silent catch — grace: 1 minor release (v2.14.0)
|
|
39
|
-
/* fall through to global */
|
|
40
|
-
}
|
|
41
|
-
try {
|
|
42
|
-
return planStatusLineInstall('global').alreadyInstalled;
|
|
43
|
-
}
|
|
44
|
-
catch {
|
|
45
|
-
return false;
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
function defaultWorkspaceInitializedProbe() {
|
|
49
|
-
const projectRoot = findProjectRoot(process.cwd());
|
|
50
|
-
if (projectRoot === null)
|
|
51
|
-
return false;
|
|
52
|
-
// Workspace is "initialized" when EITHER the canonical runtime-layer session
|
|
53
|
-
// binding (`.peaks/_runtime/session.json`, the home since slice
|
|
54
|
-
// 2026-06-05-peaks-runtime-layer) OR the legacy top-level binding
|
|
55
|
-
// (`.peaks/.session.json`, kept as read-only back-compat for one minor
|
|
56
|
-
// release) is present. The legacy check is what catches projects that ran
|
|
57
|
-
// `peaks workspace init` before the runtime-layer migration and have not yet
|
|
58
|
-
// been reconciled; both paths must continue to satisfy the doctor until the
|
|
59
|
-
// legacy location is removed.
|
|
60
|
-
return isWorkspaceInitializedAt(projectRoot);
|
|
61
|
-
}
|
|
62
1
|
/**
|
|
63
|
-
*
|
|
64
|
-
*
|
|
65
|
-
*
|
|
66
|
-
*
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
return (existsSync(join(projectRoot, '.peaks', '_runtime', 'session.json')) ||
|
|
70
|
-
existsSync(join(projectRoot, '.peaks', '.session.json')));
|
|
71
|
-
}
|
|
72
|
-
/**
|
|
73
|
-
* Pure helper that compares the published dist `CLI_VERSION` against the
|
|
74
|
-
* source-of-truth `package.json#version`. Default readers fail-soft to `null`
|
|
75
|
-
* on missing/unreadable/malformed input. Exported so tests can drive the
|
|
76
|
-
* filesystem reads without monkey-patching `process.cwd()`.
|
|
77
|
-
*/
|
|
78
|
-
export function compareDistVersion(opts) {
|
|
79
|
-
const distReader = opts.distVersionReader ?? defaultDistVersionReader;
|
|
80
|
-
const sourceReader = opts.sourceVersionReader ?? defaultSourceVersionReader;
|
|
81
|
-
const dist = safeRead(() => distReader(opts.projectRoot));
|
|
82
|
-
const source = safeRead(() => sourceReader(opts.projectRoot)) ?? 'unknown';
|
|
83
|
-
const distReadable = dist !== null;
|
|
84
|
-
return {
|
|
85
|
-
dist,
|
|
86
|
-
source,
|
|
87
|
-
match: distReadable && dist === source,
|
|
88
|
-
distReadable
|
|
89
|
-
};
|
|
90
|
-
}
|
|
91
|
-
function safeRead(reader) {
|
|
92
|
-
try {
|
|
93
|
-
return reader();
|
|
94
|
-
}
|
|
95
|
-
catch { // TODO(g2): legacy silent catch — grace: 1 minor release (v2.14.0)
|
|
96
|
-
return null;
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
function defaultDistVersionReader(projectRoot) {
|
|
100
|
-
// Synchronous read is fine: the dist version.js is small and on the
|
|
101
|
-
// local build pipeline's hot path. readFileSync + regex is cheaper
|
|
102
|
-
// than pulling in fs/promises for a single short file.
|
|
103
|
-
const distPath = join(projectRoot, 'dist', 'src', 'shared', 'version.js');
|
|
104
|
-
if (!existsSync(distPath)) {
|
|
105
|
-
return null;
|
|
106
|
-
}
|
|
107
|
-
const body = readFileSync(distPath, 'utf8');
|
|
108
|
-
const match = /export\s+const\s+CLI_VERSION\s*=\s*["']([^"']+)["']/.exec(body);
|
|
109
|
-
return match?.[1] ?? null;
|
|
110
|
-
}
|
|
111
|
-
function defaultSourceVersionReader(projectRoot) {
|
|
112
|
-
const pkgPath = join(projectRoot, 'package.json');
|
|
113
|
-
if (!existsSync(pkgPath)) {
|
|
114
|
-
return null;
|
|
115
|
-
}
|
|
116
|
-
const body = readFileSync(pkgPath, 'utf8');
|
|
117
|
-
const parsed = JSON.parse(body);
|
|
118
|
-
return typeof parsed.version === 'string' ? parsed.version : null;
|
|
119
|
-
}
|
|
120
|
-
function defaultDistVersionProbe() {
|
|
121
|
-
const projectRoot = findProjectRoot(process.cwd());
|
|
122
|
-
if (projectRoot === null) {
|
|
123
|
-
return { dist: null, source: 'unknown', match: false, distReadable: false };
|
|
124
|
-
}
|
|
125
|
-
return compareDistVersion({ projectRoot });
|
|
126
|
-
}
|
|
127
|
-
/**
|
|
128
|
-
* Pure helper that inspects the on-disk workspace layout for
|
|
129
|
-
* post-F3-canonical violations. The post-F3 canonical layout puts
|
|
130
|
-
* session dirs under `.peaks/_runtime/<sid>/` and the runtime
|
|
131
|
-
* binding at `.peaks/_runtime/session.json`; the legacy paths
|
|
132
|
-
* (top-level `<YYYY-MM-DD-session-<hex>>/` dirs and the legacy
|
|
133
|
-
* top-level `.peaks/.session.json` / `.peaks/.active-skill.json`
|
|
134
|
-
* dotfiles) must be absent. This helper is exported so tests can
|
|
135
|
-
* drive the filesystem walk without monkey-patching `process.cwd()`
|
|
136
|
-
* or `findProjectRoot`.
|
|
2
|
+
* Thin shim — `doctor-service.ts` (legacy 1309-line file)
|
|
3
|
+
* was split in slice rid-004 into a code-driven fixed-registry
|
|
4
|
+
* tree under `./doctor-service/`. This file remains so the legacy
|
|
5
|
+
* import paths (`from '../services/doctor/doctor-service.js'` and
|
|
6
|
+
* `peaks-loop-doctor/services/doctor/doctor-service` package
|
|
7
|
+
* export) keep working without modification.
|
|
137
8
|
*
|
|
138
|
-
*
|
|
139
|
-
*
|
|
140
|
-
* doctor
|
|
9
|
+
* Do NOT add logic here — the public surface lives in
|
|
10
|
+
* `./doctor-service/index.ts` and the check modules under
|
|
11
|
+
* `./doctor-service/checks/`.
|
|
141
12
|
*/
|
|
142
|
-
export
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
return Array.isArray(out) ? out : [];
|
|
156
|
-
}
|
|
157
|
-
catch {
|
|
158
|
-
return [];
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
const SESSION_DIR_PATTERN = /^\d{4}-\d{2}-\d{2}-session-[a-f0-9]+$/;
|
|
162
|
-
function defaultTopLevelSessionDirScanner(projectRoot) {
|
|
163
|
-
const peaksRoot = join(projectRoot, '.peaks');
|
|
164
|
-
if (!existsSync(peaksRoot))
|
|
165
|
-
return [];
|
|
166
|
-
let names;
|
|
167
|
-
try {
|
|
168
|
-
names = readdirSync(peaksRoot);
|
|
169
|
-
}
|
|
170
|
-
catch {
|
|
171
|
-
return [];
|
|
172
|
-
}
|
|
173
|
-
const offenders = [];
|
|
174
|
-
for (const name of names) {
|
|
175
|
-
if (!SESSION_DIR_PATTERN.test(name))
|
|
176
|
-
continue;
|
|
177
|
-
const full = join(peaksRoot, name);
|
|
178
|
-
try {
|
|
179
|
-
const stat = existsSync(full) ? lstatSync(full) : null;
|
|
180
|
-
if (stat === null)
|
|
181
|
-
continue;
|
|
182
|
-
// Directories only — the regex should never match a dotfile or
|
|
183
|
-
// regular file, but be defensive against weird filesystem state
|
|
184
|
-
// (e.g. someone manually created a file whose name happens to
|
|
185
|
-
// match the session-id pattern).
|
|
186
|
-
if (stat.isDirectory()) {
|
|
187
|
-
offenders.push(join('.peaks', name) + '/');
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
catch {
|
|
191
|
-
continue;
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
return offenders;
|
|
195
|
-
}
|
|
196
|
-
const LEGACY_DOTFILES = ['.session.json', '.active-skill.json'];
|
|
197
|
-
function defaultLegacyDotfileScanner(projectRoot) {
|
|
198
|
-
const peaksRoot = join(projectRoot, '.peaks');
|
|
199
|
-
if (!existsSync(peaksRoot))
|
|
200
|
-
return [];
|
|
201
|
-
const offenders = [];
|
|
202
|
-
for (const name of LEGACY_DOTFILES) {
|
|
203
|
-
if (existsSync(join(peaksRoot, name))) {
|
|
204
|
-
offenders.push(join('.peaks', name));
|
|
205
|
-
}
|
|
206
|
-
}
|
|
207
|
-
return offenders;
|
|
208
|
-
}
|
|
209
|
-
function defaultWorkspaceLayoutProbe() {
|
|
210
|
-
const projectRoot = findProjectRoot(process.cwd());
|
|
211
|
-
if (projectRoot === null) {
|
|
212
|
-
return { topLevelSessionDirs: [], legacyDotfiles: [], perChangeIdDirs: [] };
|
|
213
|
-
}
|
|
214
|
-
return inspectWorkspaceLayout({ projectRoot });
|
|
215
|
-
}
|
|
216
|
-
// Slice 007 — per-change-id top-level dir pattern. Matches the
|
|
217
|
-
// F3-canonical (pre-canonicalization) layout the 5 already-shipped
|
|
218
|
-
// slices left behind, e.g. `.peaks/001-2026-06-06-doctor-dist-version-check/`.
|
|
219
|
-
// The pattern is intentionally narrow so it does NOT match the
|
|
220
|
-
// post-F3 system dirs (`_runtime/`, `_dogfood/`, `retrospective/`,
|
|
221
|
-
// `memory/`, `perf-baseline/`, `project-scan/`, `sops/`,
|
|
222
|
-
// `0NN-session-...`, `YYYY-MM-DD-session-...`).
|
|
223
|
-
const PER_CHANGE_ID_PATTERN = /^\d{3}-\d{4}-\d{2}-\d{2}-[a-z][a-z0-9-]*[a-z0-9]$/;
|
|
224
|
-
function defaultPerChangeIdDirScanner(projectRoot) {
|
|
225
|
-
const peaksRoot = join(projectRoot, '.peaks');
|
|
226
|
-
if (!existsSync(peaksRoot))
|
|
227
|
-
return [];
|
|
228
|
-
let names;
|
|
229
|
-
try {
|
|
230
|
-
names = readdirSync(peaksRoot);
|
|
231
|
-
}
|
|
232
|
-
catch {
|
|
233
|
-
return [];
|
|
234
|
-
}
|
|
235
|
-
const offenders = [];
|
|
236
|
-
for (const name of names) {
|
|
237
|
-
if (!PER_CHANGE_ID_PATTERN.test(name))
|
|
238
|
-
continue;
|
|
239
|
-
const full = join(peaksRoot, name);
|
|
240
|
-
try {
|
|
241
|
-
const stat = existsSync(full) ? lstatSync(full) : null;
|
|
242
|
-
if (stat === null)
|
|
243
|
-
continue;
|
|
244
|
-
// Directories only — the regex should never match a dotfile
|
|
245
|
-
// or regular file, but be defensive against weird filesystem
|
|
246
|
-
// state (e.g. someone manually created a file whose name
|
|
247
|
-
// happens to match the per-change-id pattern).
|
|
248
|
-
if (stat.isDirectory()) {
|
|
249
|
-
offenders.push(join('.peaks', name) + '/');
|
|
250
|
-
}
|
|
251
|
-
}
|
|
252
|
-
catch {
|
|
253
|
-
continue;
|
|
254
|
-
}
|
|
255
|
-
}
|
|
256
|
-
return offenders;
|
|
257
|
-
}
|
|
258
|
-
const DESTRUCTIVE_APPLY_PATTERNS = [
|
|
259
|
-
/peaks\s+memory\s+sync[^\n]*--apply/,
|
|
260
|
-
/peaks\s+memory\s+extract[^\n]*--apply/,
|
|
261
|
-
/peaks\s+artifacts\s+sync[^\n]*--apply/,
|
|
262
|
-
/peaks\s+openspec\s+archive[^\n]*--apply/,
|
|
263
|
-
/peaks\s+standards\s+(?:init|update)[^\n]*--apply/
|
|
264
|
-
];
|
|
265
|
-
const AUTHORIZATION_KEYWORDS_PATTERN = /authoriz|explicit|--dry-run|approv|only after|only when/i;
|
|
266
|
-
function extractRunbookSection(body) {
|
|
267
|
-
const match = /## Default runbook\n+([\s\S]*?)(?=\n## |$)/.exec(body);
|
|
268
|
-
return match === null ? null : (match[1] ?? null);
|
|
269
|
-
}
|
|
270
|
-
function findDestructiveApplyLines(section) {
|
|
271
|
-
const lines = section.split(/\r?\n/);
|
|
272
|
-
return lines.filter((line) => DESTRUCTIVE_APPLY_PATTERNS.some((pattern) => pattern.test(line)));
|
|
273
|
-
}
|
|
274
|
-
// ---------------------------------------------------------------------------
|
|
275
|
-
// 2026-06-10 — gateguard-fact-force integration check (NOT a peaks-loop hook).
|
|
276
|
-
//
|
|
277
|
-
// The `gateguard-fact-force` hook is a third-party PreToolUse hook that
|
|
278
|
-
// fires on Edit / Write / MultiEdit and demands a 4-fact questionnaire
|
|
279
|
-
// before allowing the edit. It is unrelated to peaks-loop, but when the
|
|
280
|
-
// LLM is in a peaks-qa flow and edits `.peaks/_runtime/<sid>/qa/requests/
|
|
281
|
-
// *.md`, the questionnaire demands facts that do not apply (no
|
|
282
|
-
// importers, no public API, no data files, user instruction already
|
|
283
|
-
// in the conversation context). The check below detects the hook in
|
|
284
|
-
// `~/.claude/settings.json` and the project `.claude/settings.json`,
|
|
285
|
-
// and warns when no `.peaks/**` skip is configured.
|
|
286
|
-
//
|
|
287
|
-
// Probing is split out of the check so the check itself stays a pure
|
|
288
|
-
// mapping over `GateguardProbeResult`. Tests inject the probe to keep
|
|
289
|
-
// `~/.claude/settings.json` from leaking into test fixtures.
|
|
290
|
-
// ---------------------------------------------------------------------------
|
|
291
|
-
/** Hook command fragments that identify the gateguard-fact-force hook. */
|
|
292
|
-
const GATEGUARD_HOOK_NEEDLES = ['gateguard', 'fact-force', 'fact_force'];
|
|
293
|
-
/** Token the gateguard hook exposes for "skip these paths" — the check
|
|
294
|
-
* treats any match against `.peaks` (path or globs) as a routed
|
|
295
|
-
* configuration. We accept a few common spellings because the third-
|
|
296
|
-
* party hook's CLI surface is not part of peaks-loop's contract. */
|
|
297
|
-
const GATEGUARD_PEAKS_SKIP_NEEDLES = [
|
|
298
|
-
'.peaks',
|
|
299
|
-
'peaks-skip',
|
|
300
|
-
'skip-glob',
|
|
301
|
-
'--skip',
|
|
302
|
-
'skip_paths'
|
|
303
|
-
];
|
|
304
|
-
function commandMentionsGateguard(command) {
|
|
305
|
-
if (typeof command !== 'string' || command.length === 0)
|
|
306
|
-
return false;
|
|
307
|
-
const lower = command.toLowerCase();
|
|
308
|
-
return GATEGUARD_HOOK_NEEDLES.some((needle) => lower.includes(needle));
|
|
309
|
-
}
|
|
310
|
-
function entrySkipsPeaks(entry) {
|
|
311
|
-
const matcher = typeof entry.matcher === 'string' ? entry.matcher : '';
|
|
312
|
-
const matcherMentionsPeaks = matcher.toLowerCase().includes('.peaks');
|
|
313
|
-
if (matcherMentionsPeaks)
|
|
314
|
-
return true;
|
|
315
|
-
for (const hook of entry.hooks) {
|
|
316
|
-
const command = typeof hook.command === 'string' ? hook.command : '';
|
|
317
|
-
const lower = command.toLowerCase();
|
|
318
|
-
if (GATEGUARD_PEAKS_SKIP_NEEDLES.some((needle) => lower.includes(needle))) {
|
|
319
|
-
return true;
|
|
320
|
-
}
|
|
321
|
-
}
|
|
322
|
-
return false;
|
|
323
|
-
}
|
|
324
|
-
function extractGateguardEntries(source, sourcePath, settings) {
|
|
325
|
-
if (settings === null || typeof settings !== 'object')
|
|
326
|
-
return [];
|
|
327
|
-
const hooks = settings.hooks;
|
|
328
|
-
if (hooks === null || typeof hooks !== 'object')
|
|
329
|
-
return [];
|
|
330
|
-
const preToolUse = hooks.PreToolUse;
|
|
331
|
-
if (!Array.isArray(preToolUse))
|
|
332
|
-
return [];
|
|
333
|
-
const out = [];
|
|
334
|
-
for (const rawEntry of preToolUse) {
|
|
335
|
-
if (rawEntry === null || typeof rawEntry !== 'object')
|
|
336
|
-
continue;
|
|
337
|
-
const entry = rawEntry;
|
|
338
|
-
if (!Array.isArray(entry.hooks))
|
|
339
|
-
continue;
|
|
340
|
-
const hooks = [];
|
|
341
|
-
for (const rawHook of entry.hooks) {
|
|
342
|
-
if (rawHook === null || typeof rawHook !== 'object')
|
|
343
|
-
continue;
|
|
344
|
-
const h = rawHook;
|
|
345
|
-
const hookEntry = {};
|
|
346
|
-
if (typeof h.type === 'string')
|
|
347
|
-
hookEntry.type = h.type;
|
|
348
|
-
if (typeof h.command === 'string')
|
|
349
|
-
hookEntry.command = h.command;
|
|
350
|
-
hooks.push(hookEntry);
|
|
351
|
-
}
|
|
352
|
-
if (!hooks.some((h) => commandMentionsGateguard(h.command)))
|
|
353
|
-
continue;
|
|
354
|
-
const outEntry = { hooks };
|
|
355
|
-
if (typeof entry.matcher === 'string')
|
|
356
|
-
outEntry.matcher = entry.matcher;
|
|
357
|
-
out.push({ source, sourcePath, entry: outEntry });
|
|
358
|
-
}
|
|
359
|
-
return out;
|
|
360
|
-
}
|
|
361
|
-
function readSettingsJson(path) {
|
|
362
|
-
if (!existsSync(path))
|
|
363
|
-
return null;
|
|
364
|
-
try {
|
|
365
|
-
return JSON.parse(readFileSync(path, 'utf8'));
|
|
366
|
-
}
|
|
367
|
-
catch { // TODO(g2): legacy silent catch — grace: 1 minor release (v2.14.0)
|
|
368
|
-
return null;
|
|
369
|
-
}
|
|
370
|
-
}
|
|
371
|
-
function defaultGateguardProbe() {
|
|
372
|
-
const projectRoot = findProjectRoot(process.cwd());
|
|
373
|
-
const globalPath = join(homedir(), '.claude', 'settings.json');
|
|
374
|
-
const projectPath = projectRoot === null ? null : join(projectRoot, '.claude', 'settings.json');
|
|
375
|
-
return {
|
|
376
|
-
globalSettingsPath: globalPath,
|
|
377
|
-
globalSettings: readSettingsJson(globalPath),
|
|
378
|
-
projectSettingsPath: projectPath,
|
|
379
|
-
projectSettings: projectPath === null ? null : readSettingsJson(projectPath)
|
|
380
|
-
};
|
|
381
|
-
}
|
|
382
|
-
export function collectGateguardEntries(probe) {
|
|
383
|
-
const fromGlobal = extractGateguardEntries('global', probe.globalSettingsPath ?? '~/.claude/settings.json', probe.globalSettings);
|
|
384
|
-
const fromProject = probe.projectSettingsPath === null
|
|
385
|
-
? []
|
|
386
|
-
: extractGateguardEntries('project', probe.projectSettingsPath, probe.projectSettings);
|
|
387
|
-
return [...fromGlobal, ...fromProject];
|
|
388
|
-
}
|
|
389
|
-
export async function runDoctor(options = {}) {
|
|
390
|
-
const checks = [];
|
|
391
|
-
const registry = await loadSkillRegistry(options.skillsBaseDir);
|
|
392
|
-
const skills = registry.skills;
|
|
393
|
-
const skillNames = new Set(skills.map((skill) => skill.name));
|
|
394
|
-
for (const requiredSkill of requiredSkillNames) {
|
|
395
|
-
checks.push({
|
|
396
|
-
id: `skill:${requiredSkill}`,
|
|
397
|
-
ok: skillNames.has(requiredSkill),
|
|
398
|
-
message: skillNames.has(requiredSkill)
|
|
399
|
-
? `Required skill ${requiredSkill} exists`
|
|
400
|
-
: `Missing required skill ${requiredSkill}`
|
|
401
|
-
});
|
|
402
|
-
}
|
|
403
|
-
for (const skill of skills) {
|
|
404
|
-
checks.push({
|
|
405
|
-
id: `skill-name:${skill.directory}`,
|
|
406
|
-
ok: skill.name === skill.directory,
|
|
407
|
-
message: skill.name === skill.directory
|
|
408
|
-
? `Skill ${skill.name} matches its directory`
|
|
409
|
-
: `Skill ${skill.directory} declares mismatched name ${skill.name}`
|
|
410
|
-
});
|
|
411
|
-
}
|
|
412
|
-
for (const failure of registry.failures) {
|
|
413
|
-
checks.push({
|
|
414
|
-
id: `skill-parse:${failure.directory}`,
|
|
415
|
-
ok: false,
|
|
416
|
-
message: `Skill ${failure.directory} has invalid metadata: ${failure.message}`
|
|
417
|
-
});
|
|
418
|
-
}
|
|
419
|
-
const requiredSkillNameSet = new Set(requiredSkillNames);
|
|
420
|
-
for (const skill of skills) {
|
|
421
|
-
if (!requiredSkillNameSet.has(skill.name)) {
|
|
422
|
-
continue;
|
|
423
|
-
}
|
|
424
|
-
try {
|
|
425
|
-
const body = await readText(skill.skillPath);
|
|
426
|
-
const hasRunbook = /## Default runbook\s/.test(body);
|
|
427
|
-
checks.push({
|
|
428
|
-
id: `skill-runbook:${skill.name}`,
|
|
429
|
-
ok: hasRunbook,
|
|
430
|
-
message: hasRunbook
|
|
431
|
-
? `Skill ${skill.name} declares a Default runbook`
|
|
432
|
-
: `Skill ${skill.name} is missing a ## Default runbook section`
|
|
433
|
-
});
|
|
434
|
-
const runbookSection = extractRunbookSection(body);
|
|
435
|
-
if (runbookSection !== null) {
|
|
436
|
-
const destructiveLines = findDestructiveApplyLines(runbookSection);
|
|
437
|
-
if (destructiveLines.length === 0) {
|
|
438
|
-
checks.push({
|
|
439
|
-
id: `skill-apply-note:${skill.name}`,
|
|
440
|
-
ok: true,
|
|
441
|
-
message: `Skill ${skill.name} runbook has no destructive --apply commands to gate`
|
|
442
|
-
});
|
|
443
|
-
}
|
|
444
|
-
else {
|
|
445
|
-
const hasAuthorizationNote = AUTHORIZATION_KEYWORDS_PATTERN.test(runbookSection);
|
|
446
|
-
checks.push({
|
|
447
|
-
id: `skill-apply-note:${skill.name}`,
|
|
448
|
-
ok: hasAuthorizationNote,
|
|
449
|
-
message: hasAuthorizationNote
|
|
450
|
-
? `Skill ${skill.name} gates ${destructiveLines.length} destructive --apply command(s) with an authorization note`
|
|
451
|
-
: `Skill ${skill.name} has ${destructiveLines.length} destructive --apply command(s) without an authorization/dry-run note in the runbook section`
|
|
452
|
-
});
|
|
453
|
-
}
|
|
454
|
-
}
|
|
455
|
-
}
|
|
456
|
-
catch (error) {
|
|
457
|
-
checks.push({
|
|
458
|
-
id: `skill-runbook:${skill.name}`,
|
|
459
|
-
ok: false,
|
|
460
|
-
message: `Skill ${skill.name} runbook check failed: ${getErrorMessage(error)}`
|
|
461
|
-
});
|
|
462
|
-
}
|
|
463
|
-
}
|
|
464
|
-
const schemaRoot = options.schemasBaseDir ?? schemasDir;
|
|
465
|
-
for (const schemaFile of requiredSchemaFiles) {
|
|
466
|
-
try {
|
|
467
|
-
JSON.parse(await readText(join(schemaRoot, schemaFile)));
|
|
468
|
-
checks.push({ id: `schema:${schemaFile}`, ok: true, message: `Schema ${schemaFile} is valid JSON` });
|
|
469
|
-
}
|
|
470
|
-
catch (error) {
|
|
471
|
-
checks.push({
|
|
472
|
-
id: `schema:${schemaFile}`,
|
|
473
|
-
ok: false,
|
|
474
|
-
message: `Schema ${schemaFile} is missing or invalid: ${getErrorMessage(error)}`
|
|
475
|
-
});
|
|
476
|
-
}
|
|
477
|
-
}
|
|
478
|
-
const userConfigPath = join(homedir(), '.peaks', 'config.json');
|
|
479
|
-
const hasUserConfig = existsSync(userConfigPath);
|
|
480
|
-
checks.push({
|
|
481
|
-
id: 'config:user',
|
|
482
|
-
ok: true,
|
|
483
|
-
message: hasUserConfig ? 'User config exists at ~/.peaks/config.json' : 'Optional user config not found at ~/.peaks/config.json'
|
|
484
|
-
});
|
|
485
|
-
const presenceProbe = options.skillPresenceProbe ?? getSkillPresence;
|
|
486
|
-
const freshnessThresholdMs = options.skillPresenceFreshnessThresholdMs ?? SKILL_PRESENCE_FRESHNESS_THRESHOLD_MS;
|
|
487
|
-
let presence = null;
|
|
488
|
-
try {
|
|
489
|
-
presence = presenceProbe();
|
|
490
|
-
}
|
|
491
|
-
catch {
|
|
492
|
-
presence = null;
|
|
493
|
-
}
|
|
494
|
-
if (presence === null) {
|
|
495
|
-
checks.push({
|
|
496
|
-
id: 'skill-presence:current',
|
|
497
|
-
ok: true,
|
|
498
|
-
message: 'No active Peaks skill presence (.peaks/.active-skill.json absent or invalid)'
|
|
499
|
-
});
|
|
500
|
-
checks.push({
|
|
501
|
-
id: 'skill-presence:freshness',
|
|
502
|
-
ok: true,
|
|
503
|
-
message: 'No active Peaks skill presence to age-check'
|
|
504
|
-
});
|
|
505
|
-
}
|
|
506
|
-
else {
|
|
507
|
-
const modePart = presence.mode !== undefined ? `, mode ${presence.mode}` : '';
|
|
508
|
-
const gatePart = presence.gate !== undefined ? `, gate ${presence.gate}` : '';
|
|
509
|
-
checks.push({
|
|
510
|
-
id: 'skill-presence:current',
|
|
511
|
-
ok: true,
|
|
512
|
-
message: `Active Peaks skill presence: ${presence.skill}${modePart}${gatePart} (set ${presence.setAt})`
|
|
513
|
-
});
|
|
514
|
-
const setAtMs = Date.parse(presence.setAt);
|
|
515
|
-
if (Number.isNaN(setAtMs)) {
|
|
516
|
-
checks.push({
|
|
517
|
-
id: 'skill-presence:freshness',
|
|
518
|
-
ok: false,
|
|
519
|
-
message: `Skill presence ${presence.skill} has invalid setAt: ${presence.setAt}`
|
|
520
|
-
});
|
|
521
|
-
}
|
|
522
|
-
else {
|
|
523
|
-
const ageMs = Date.now() - setAtMs;
|
|
524
|
-
if (ageMs > freshnessThresholdMs) {
|
|
525
|
-
const ageHours = Math.round(ageMs / (60 * 60 * 1000));
|
|
526
|
-
checks.push({
|
|
527
|
-
id: 'skill-presence:freshness',
|
|
528
|
-
ok: false,
|
|
529
|
-
message: `Skill presence ${presence.skill} is stale (set ${presence.setAt}, ~${ageHours}h ago); run peaks skill presence:clear if the role has ended`
|
|
530
|
-
});
|
|
531
|
-
}
|
|
532
|
-
else {
|
|
533
|
-
checks.push({
|
|
534
|
-
id: 'skill-presence:freshness',
|
|
535
|
-
ok: true,
|
|
536
|
-
message: `Skill presence ${presence.skill} is fresh (set ${presence.setAt})`
|
|
537
|
-
});
|
|
538
|
-
}
|
|
539
|
-
}
|
|
540
|
-
}
|
|
541
|
-
// Workspace guard: an active workflow presence (peaks-code) with no workspace
|
|
542
|
-
// session means the skill was anchored but `peaks workspace init` never ran —
|
|
543
|
-
// the #1 reported failure where .peaks/ artifacts are never created. This
|
|
544
|
-
// turns the SKILL.md "MUST create the workspace" prose into an executable check.
|
|
545
|
-
const workspaceProbe = options.workspaceInitializedProbe ?? defaultWorkspaceInitializedProbe;
|
|
546
|
-
let workspaceInitialized = false;
|
|
547
|
-
try {
|
|
548
|
-
workspaceInitialized = workspaceProbe();
|
|
549
|
-
}
|
|
550
|
-
catch {
|
|
551
|
-
workspaceInitialized = false;
|
|
552
|
-
}
|
|
553
|
-
if (presence !== null && !workspaceInitialized) {
|
|
554
|
-
checks.push({
|
|
555
|
-
id: 'skill-presence:workspace',
|
|
556
|
-
ok: false,
|
|
557
|
-
message: `Skill ${presence.skill} is active but no workspace session exists (.peaks/_runtime/session.json missing); run \`peaks workspace init --project <repo>\` — peaks-code Step 0 must anchor the workspace before any work`
|
|
558
|
-
});
|
|
559
|
-
}
|
|
560
|
-
else {
|
|
561
|
-
checks.push({
|
|
562
|
-
id: 'skill-presence:workspace',
|
|
563
|
-
ok: true,
|
|
564
|
-
message: presence === null
|
|
565
|
-
? 'No active skill presence; workspace guard not applicable'
|
|
566
|
-
: `Workspace session present for active skill ${presence.skill}`
|
|
567
|
-
});
|
|
568
|
-
}
|
|
569
|
-
// Discoverability nudge: when a skill is actively orchestrating but the
|
|
570
|
-
// out-of-band statusLine isn't installed, the user has no terminal-level
|
|
571
|
-
// signal that Peaks is in control. Suggest installing it (non-failing).
|
|
572
|
-
const statusLineProbe = options.statusLineInstalledProbe ?? defaultStatusLineInstalledProbe;
|
|
573
|
-
let statusLineInstalled = false;
|
|
574
|
-
try {
|
|
575
|
-
statusLineInstalled = statusLineProbe();
|
|
576
|
-
}
|
|
577
|
-
catch {
|
|
578
|
-
statusLineInstalled = false;
|
|
579
|
-
}
|
|
580
|
-
if (presence !== null && !statusLineInstalled) {
|
|
581
|
-
checks.push({
|
|
582
|
-
id: 'statusline:install',
|
|
583
|
-
ok: true,
|
|
584
|
-
message: 'A Peaks skill is active but the statusLine is not installed; run `peaks statusline install` so the active skill shows in the terminal status bar'
|
|
585
|
-
});
|
|
586
|
-
}
|
|
587
|
-
else {
|
|
588
|
-
checks.push({
|
|
589
|
-
id: 'statusline:install',
|
|
590
|
-
ok: true,
|
|
591
|
-
message: statusLineInstalled
|
|
592
|
-
? 'Peaks statusLine is installed'
|
|
593
|
-
: 'Peaks statusLine not installed (no active skill; install optional)'
|
|
594
|
-
});
|
|
595
|
-
}
|
|
596
|
-
// Runtime/platform diagnostic for the "statusLine shows nothing" reports.
|
|
597
|
-
// Surfaces (a) the running peaks version — a stale global install predating
|
|
598
|
-
// the statusLine feature is a common cause — and (b) on Windows, the fact that
|
|
599
|
-
// the bare `peaks statusline` command must resolve in the shell Claude Code
|
|
600
|
-
// spawns, which fails when the npm global bin dir is not on that shell's PATH.
|
|
601
|
-
const platform = options.platform ?? process.platform;
|
|
602
|
-
if (platform === 'win32') {
|
|
603
|
-
checks.push({
|
|
604
|
-
id: 'statusline:runtime',
|
|
605
|
-
ok: true,
|
|
606
|
-
message: `peaks ${CLI_VERSION} (win32): if the statusLine shows nothing in git bash, verify \`peaks\` resolves on PATH in the shell Claude Code uses (run \`peaks -v\` there), reinstall globally with \`npm i -g peaks-loop@latest\` if the version is older than ${CLI_VERSION}, then re-run \`peaks statusline install\` and reload Claude Code`
|
|
607
|
-
});
|
|
608
|
-
}
|
|
609
|
-
else {
|
|
610
|
-
checks.push({
|
|
611
|
-
id: 'statusline:runtime',
|
|
612
|
-
ok: true,
|
|
613
|
-
message: `peaks ${CLI_VERSION} (${platform}): statusLine command is \`peaks statusline\``
|
|
614
|
-
});
|
|
615
|
-
}
|
|
616
|
-
const probe = options.codegraphProbe ?? defaultCodegraphProbe;
|
|
617
|
-
try {
|
|
618
|
-
const result = probe();
|
|
619
|
-
const versionOk = result.version === CODEGRAPH_EXPECTED_VERSION;
|
|
620
|
-
if (!versionOk) {
|
|
621
|
-
checks.push({
|
|
622
|
-
id: 'capability:codegraph',
|
|
623
|
-
ok: false,
|
|
624
|
-
message: `@colbymchenry/codegraph version mismatch: expected ${CODEGRAPH_EXPECTED_VERSION}, resolved ${result.version} at ${result.packagePath}`
|
|
625
|
-
});
|
|
626
|
-
}
|
|
627
|
-
else if (!result.binaryExists) {
|
|
628
|
-
checks.push({
|
|
629
|
-
id: 'capability:codegraph',
|
|
630
|
-
ok: false,
|
|
631
|
-
message: `@colbymchenry/codegraph@${result.version} resolved at ${result.packagePath} but binary is missing at ${result.binaryPath}`
|
|
632
|
-
});
|
|
633
|
-
}
|
|
634
|
-
else {
|
|
635
|
-
checks.push({
|
|
636
|
-
id: 'capability:codegraph',
|
|
637
|
-
ok: true,
|
|
638
|
-
message: `@colbymchenry/codegraph@${result.version} resolves with binary at ${result.binaryPath}`
|
|
639
|
-
});
|
|
640
|
-
}
|
|
641
|
-
}
|
|
642
|
-
catch (error) {
|
|
643
|
-
checks.push({
|
|
644
|
-
id: 'capability:codegraph',
|
|
645
|
-
ok: false,
|
|
646
|
-
message: `@colbymchenry/codegraph not resolvable: ${getErrorMessage(error)}`
|
|
647
|
-
});
|
|
648
|
-
}
|
|
649
|
-
// Build-hygiene check: the published `dist/` ships a different CLI_VERSION
|
|
650
|
-
// than the source-of-truth `src/shared/version.ts` / `package.json#version`
|
|
651
|
-
// whenever the user runs `npx peaks` or `node bin/peaks.js` after `pnpm
|
|
652
|
-
// install` but before `pnpm build`. This is the silent-stale-CLI failure
|
|
653
|
-
// mode reported in `.peaks/2026-06-05-session-fecddb/txt/dogfood-2026-06-04-05.md`
|
|
654
|
-
// (F1). A missing dist/ is treated as informational (fresh clone, not broken)
|
|
655
|
-
// so the check does not flip the summary to red on a clean checkout.
|
|
656
|
-
const distProbe = options.distVersionProbe ?? defaultDistVersionProbe;
|
|
657
|
-
try {
|
|
658
|
-
const result = distProbe();
|
|
659
|
-
if (!result.distReadable) {
|
|
660
|
-
checks.push({
|
|
661
|
-
id: 'build:dist-version-matches-source',
|
|
662
|
-
ok: true,
|
|
663
|
-
message: `dist/ is not present; run \`pnpm build\` to populate dist/src/shared/version.js (source version ${result.source})`
|
|
664
|
-
});
|
|
665
|
-
}
|
|
666
|
-
else if (result.match) {
|
|
667
|
-
checks.push({
|
|
668
|
-
id: 'build:dist-version-matches-source',
|
|
669
|
-
ok: true,
|
|
670
|
-
message: `dist/src/shared/version.js ships CLI_VERSION ${result.dist} matching source ${result.source}`
|
|
671
|
-
});
|
|
672
|
-
}
|
|
673
|
-
else {
|
|
674
|
-
checks.push({
|
|
675
|
-
id: 'build:dist-version-matches-source',
|
|
676
|
-
ok: false,
|
|
677
|
-
message: `dist/src/shared/version.js ships CLI_VERSION ${result.dist} but source ${result.source} is in src/shared/version.ts; run \`pnpm build\` to refresh dist/`
|
|
678
|
-
});
|
|
679
|
-
}
|
|
680
|
-
}
|
|
681
|
-
catch (error) {
|
|
682
|
-
checks.push({
|
|
683
|
-
id: 'build:dist-version-matches-source',
|
|
684
|
-
ok: false,
|
|
685
|
-
message: `dist version check failed: ${getErrorMessage(error)}`
|
|
686
|
-
});
|
|
687
|
-
}
|
|
688
|
-
// Build-hygiene check: a non-canonical post-F3 workspace layout is
|
|
689
|
-
// the silent-regression failure mode that slice 003 explicitly chose
|
|
690
|
-
// to allow (the current session binding was kept at top-level as a
|
|
691
|
-
// safety measure). This check surfaces any leftover top-level
|
|
692
|
-
// session dirs, the legacy runtime dotfiles (`.peaks/.session.json`,
|
|
693
|
-
// `.peaks/.active-skill.json`), OR per-change-id top-level dirs
|
|
694
|
-
// (`.peaks/NNN-YYYY-MM-DD-<slug>/`) so a future contributor who
|
|
695
|
-
// manually recreates one of them is warned. The check is read-only;
|
|
696
|
-
// the fix path is `peaks workspace migrate --to-runtime --project
|
|
697
|
-
// <repo> --apply`.
|
|
698
|
-
//
|
|
699
|
-
// Slice 007 broadened this check to flag per-change-id top-level
|
|
700
|
-
// dirs (the 5 already-shipped slices left them behind). Until
|
|
701
|
-
// slice 008's migration consolidates them, the check reports
|
|
702
|
-
// `ok: false` for any repo that still has the legacy per-change-id
|
|
703
|
-
// layout.
|
|
704
|
-
const layoutProbe = options.workspaceLayoutProbe ?? defaultWorkspaceLayoutProbe;
|
|
705
|
-
try {
|
|
706
|
-
const layout = layoutProbe();
|
|
707
|
-
// Back-compat: probes injected by older tests (pre-slice-007)
|
|
708
|
-
// return a 2-field shape (no perChangeIdDirs). Treat missing
|
|
709
|
-
// field as empty.
|
|
710
|
-
const perChangeIdDirs = layout.perChangeIdDirs ?? [];
|
|
711
|
-
if (layout.topLevelSessionDirs.length === 0 && layout.legacyDotfiles.length === 0 && perChangeIdDirs.length === 0) {
|
|
712
|
-
checks.push({
|
|
713
|
-
id: 'build:workspace-layout-canonical',
|
|
714
|
-
ok: true,
|
|
715
|
-
message: 'Workspace layout is canonical: no top-level session dirs, no legacy runtime dotfiles, no per-change-id top-level dirs'
|
|
716
|
-
});
|
|
717
|
-
}
|
|
718
|
-
else {
|
|
719
|
-
const offenders = [
|
|
720
|
-
...layout.topLevelSessionDirs.map((p) => `top-level session dir: ${p}`),
|
|
721
|
-
...layout.legacyDotfiles.map((p) => `legacy dotfile: ${p}`),
|
|
722
|
-
...perChangeIdDirs.map((p) => `per-change-id top-level dir: ${p}`)
|
|
723
|
-
];
|
|
724
|
-
checks.push({
|
|
725
|
-
id: 'build:workspace-layout-canonical',
|
|
726
|
-
ok: false,
|
|
727
|
-
message: `Workspace layout is not canonical. Offenders: ${offenders.join('; ')}. Run \`peaks workspace migrate --to-runtime --project <repo> --apply\` to consolidate.`
|
|
728
|
-
});
|
|
729
|
-
}
|
|
730
|
-
}
|
|
731
|
-
catch (error) {
|
|
732
|
-
checks.push({
|
|
733
|
-
id: 'build:workspace-layout-canonical',
|
|
734
|
-
ok: false,
|
|
735
|
-
message: `Workspace layout check failed: ${getErrorMessage(error)}`
|
|
736
|
-
});
|
|
737
|
-
}
|
|
738
|
-
// 2026-06-10 — gateguard-fact-force integration check. The hook is a
|
|
739
|
-
// third-party PreToolUse that fires on Edit / Write and demands a
|
|
740
|
-
// 4-fact questionnaire; when the LLM is in a peaks-qa flow updating
|
|
741
|
-
// `.peaks/_runtime/<sid>/qa/requests/*.md` the facts do not apply.
|
|
742
|
-
// We warn when the hook is installed and no `.peaks/**` skip is
|
|
743
|
-
// configured. The check stays a pure mapping over the probe result
|
|
744
|
-
// so tests can drive it without touching the real `~/.claude/`.
|
|
745
|
-
const gateguardProbe = options.gateguardProbe ?? defaultGateguardProbe;
|
|
746
|
-
try {
|
|
747
|
-
const probe = gateguardProbe();
|
|
748
|
-
const offending = collectGateguardEntries(probe);
|
|
749
|
-
if (offending.length === 0) {
|
|
750
|
-
checks.push({
|
|
751
|
-
id: 'integration:gateguard-peaks-conflict',
|
|
752
|
-
ok: true,
|
|
753
|
-
message: 'No gateguard-fact-force PreToolUse hook detected in ~/.claude/settings.json or project .claude/settings.json; the Edit/Write fact-forcing flow will not interfere with peaks-qa .peaks/ artifact writes'
|
|
754
|
-
});
|
|
755
|
-
}
|
|
756
|
-
else {
|
|
757
|
-
const unrouted = offending.filter((location) => !entrySkipsPeaks(location.entry));
|
|
758
|
-
if (unrouted.length === 0) {
|
|
759
|
-
checks.push({
|
|
760
|
-
id: 'integration:gateguard-peaks-conflict',
|
|
761
|
-
ok: true,
|
|
762
|
-
message: `gateguard-fact-force hook is installed in ${offending.map((l) => l.source).join(' + ')} but a .peaks/** skip pattern is configured; peaks-qa .peaks/ artifact writes are not blocked`
|
|
763
|
-
});
|
|
764
|
-
}
|
|
765
|
-
else {
|
|
766
|
-
const sources = Array.from(new Set(unrouted.map((u) => u.sourcePath))).join(' + ');
|
|
767
|
-
const matchers = unrouted.map((u) => u.entry.matcher ?? '*').join(', ');
|
|
768
|
-
checks.push({
|
|
769
|
-
id: 'integration:gateguard-peaks-conflict',
|
|
770
|
-
ok: false,
|
|
771
|
-
message: `gateguard-fact-force PreToolUse hook is installed (${sources}, matcher: ${matchers}) with no .peaks/** skip pattern; every Edit/Write of a peaks-qa envelope (.peaks/_runtime/<sid>/qa/requests/*.md) will be intercepted and demand a 4-fact questionnaire that does not apply to QA templates. Workaround: set \`ECC_DISABLED_HOOKS=pre:edit-write:gateguard-fact-force\` for the session, OR add a paired PreToolUse entry whose matcher restricts the hook to non-.peaks paths. peaks-loop is NOT the source of this hook.`
|
|
772
|
-
});
|
|
773
|
-
}
|
|
774
|
-
}
|
|
775
|
-
}
|
|
776
|
-
catch (error) {
|
|
777
|
-
checks.push({
|
|
778
|
-
id: 'integration:gateguard-peaks-conflict',
|
|
779
|
-
ok: true,
|
|
780
|
-
message: `gateguard probe failed (${getErrorMessage(error)}); skipping check`
|
|
781
|
-
});
|
|
782
|
-
}
|
|
783
|
-
try {
|
|
784
|
-
const schemaText = await readText(join(schemaRoot, 'doctor-report.schema.json'));
|
|
785
|
-
const schema = JSON.parse(schemaText);
|
|
786
|
-
const patternSource = schema.properties?.checks?.items?.properties?.id?.pattern;
|
|
787
|
-
if (typeof patternSource === 'string') {
|
|
788
|
-
const pattern = new RegExp(patternSource);
|
|
789
|
-
const mismatches = checks.filter((check) => !pattern.test(check.id)).map((check) => check.id);
|
|
790
|
-
checks.push({
|
|
791
|
-
id: 'doctor-self:check-id-pattern',
|
|
792
|
-
ok: mismatches.length === 0,
|
|
793
|
-
message: mismatches.length === 0
|
|
794
|
-
? 'All doctor check IDs match the doctor-report schema pattern'
|
|
795
|
-
: `Doctor check IDs missing from schema pattern: ${mismatches.join(', ')}`
|
|
796
|
-
});
|
|
797
|
-
}
|
|
798
|
-
else {
|
|
799
|
-
checks.push({
|
|
800
|
-
id: 'doctor-self:check-id-pattern',
|
|
801
|
-
ok: false,
|
|
802
|
-
message: 'doctor-report.schema.json does not declare a check.id pattern'
|
|
803
|
-
});
|
|
804
|
-
}
|
|
805
|
-
}
|
|
806
|
-
catch (error) {
|
|
807
|
-
checks.push({
|
|
808
|
-
id: 'doctor-self:check-id-pattern',
|
|
809
|
-
ok: false,
|
|
810
|
-
message: `Failed to load doctor-report.schema.json for self-validation: ${getErrorMessage(error)}`
|
|
811
|
-
});
|
|
812
|
-
}
|
|
813
|
-
// Slice L3.2: project doctor MVP — 2 new diagnostic checks.
|
|
814
|
-
// 1. L3:l3-orphan-sessions — flags bare sids in .peaks/_runtime/ that
|
|
815
|
-
// fail isValidSessionId. Reuses the Slice 0.5 sid-naming-guard.
|
|
816
|
-
// 2. L3:l3-memory-health — verifies .peaks/memory/index.json is
|
|
817
|
-
// well-formed JSON with the expected schema_version field and
|
|
818
|
-
// references real files on disk.
|
|
819
|
-
// Slice L3.2: project doctor MVP — 2 new diagnostic checks.
|
|
820
|
-
// 1. L3:l3-orphan-sessions — flags bare sids in .peaks/_runtime/ that
|
|
821
|
-
// fail isValidSessionId. Reuses the Slice 0.5 sid-naming-guard.
|
|
822
|
-
// 2. L3:l3-memory-health — verifies .peaks/memory/index.json is
|
|
823
|
-
// well-formed JSON with the expected schema_version field and
|
|
824
|
-
// references real files on disk.
|
|
825
|
-
//
|
|
826
|
-
// Round 2 (slice 2026-06-24-doctor-1xdetector-residual): the reducer
|
|
827
|
-
// must skip canonical system subdirs that intentionally live under
|
|
828
|
-
// `.peaks/_runtime/` — used by `request-artifact-service` /
|
|
829
|
-
// `workflow-autonomous-service` to route reviewable artifacts under
|
|
830
|
-
// session-axis dirs. Excluding these from the orphan scan is a
|
|
831
|
-
// reducer-level narrowing of which entries are subject to the check;
|
|
832
|
-
// it does NOT change the pass/fail rule for actual session ids.
|
|
833
|
-
const RUNTIME_SYSTEM_SUBDIRS = new Set(['change']);
|
|
834
|
-
const l3ProjectRoot = options.l3ProjectRoot ?? findProjectRoot(process.cwd()) ?? process.cwd();
|
|
835
|
-
try {
|
|
836
|
-
const runtimeDir = join(l3ProjectRoot, '.peaks/_runtime');
|
|
837
|
-
if (existsSync(runtimeDir)) {
|
|
838
|
-
const entries = readdirSync(runtimeDir, { withFileTypes: true })
|
|
839
|
-
.filter((e) => e.isDirectory())
|
|
840
|
-
.map((e) => e.name)
|
|
841
|
-
.filter((name) => !RUNTIME_SYSTEM_SUBDIRS.has(name));
|
|
842
|
-
const validSids = entries.filter((sid) => isValidSessionId(sid));
|
|
843
|
-
const invalidSids = entries.filter((sid) => !isValidSessionId(sid));
|
|
844
|
-
checks.push({
|
|
845
|
-
id: 'L3:l3-orphan-sessions',
|
|
846
|
-
ok: invalidSids.length === 0,
|
|
847
|
-
message: invalidSids.length === 0
|
|
848
|
-
? `All ${validSids.length} session(s) under .peaks/_runtime/ are valid (isValidSessionId)`
|
|
849
|
-
: `${invalidSids.length} orphan session(s) under .peaks/_runtime/ fail isValidSessionId: ${invalidSids.slice(0, 5).join(', ')}${invalidSids.length > 5 ? '...' : ''}. Run \`peaks workspace clean --project <repo>\` to archive.`
|
|
850
|
-
});
|
|
851
|
-
}
|
|
852
|
-
else {
|
|
853
|
-
checks.push({
|
|
854
|
-
id: 'L3:l3-orphan-sessions',
|
|
855
|
-
ok: true,
|
|
856
|
-
message: 'No .peaks/_runtime/ directory; nothing to check.'
|
|
857
|
-
});
|
|
858
|
-
}
|
|
859
|
-
}
|
|
860
|
-
catch (error) {
|
|
861
|
-
checks.push({
|
|
862
|
-
id: 'L3:l3-orphan-sessions',
|
|
863
|
-
ok: true,
|
|
864
|
-
message: `L3:l3-orphan-sessions probe failed (${getErrorMessage(error)}); skipping check`
|
|
865
|
-
});
|
|
866
|
-
}
|
|
867
|
-
try {
|
|
868
|
-
const memoryIndexPath = join(l3ProjectRoot, '.peaks/memory/index.json');
|
|
869
|
-
if (existsSync(memoryIndexPath)) {
|
|
870
|
-
const raw = readFileSync(memoryIndexPath, 'utf8');
|
|
871
|
-
try {
|
|
872
|
-
// Slice 2026-06-13-repair-pre-existing-test-failures: the
|
|
873
|
-
// production MemoryIndex schema (see
|
|
874
|
-
// src/services/memory/project-memory-service.ts) uses
|
|
875
|
-
// `version: 1` as the schema marker, NOT `schema_version`.
|
|
876
|
-
// Accept BOTH names for back-compat with any external index
|
|
877
|
-
// writers (e.g. a future `schema_version: '2.0.0'` form).
|
|
878
|
-
const parsed = JSON.parse(raw);
|
|
879
|
-
const schemaMarker = parsed.schema_version ?? parsed.version;
|
|
880
|
-
if (schemaMarker === undefined) {
|
|
881
|
-
checks.push({
|
|
882
|
-
id: 'L3:l3-memory-health',
|
|
883
|
-
ok: false,
|
|
884
|
-
message: '.peaks/memory/index.json missing schema_version / version field'
|
|
885
|
-
});
|
|
886
|
-
}
|
|
887
|
-
else {
|
|
888
|
-
const hotCount = Object.values(parsed.hot ?? {}).reduce((sum, arr) => sum + (Array.isArray(arr) ? arr.length : 0), 0);
|
|
889
|
-
const warmCount = Object.values(parsed.warm ?? {}).reduce((sum, arr) => sum + (Array.isArray(arr) ? arr.length : 0), 0);
|
|
890
|
-
checks.push({
|
|
891
|
-
id: 'L3:l3-memory-health',
|
|
892
|
-
ok: true,
|
|
893
|
-
message: `.peaks/memory/index.json is well-formed JSON; version=${schemaMarker}; ${hotCount} hot + ${warmCount} warm memory entries`
|
|
894
|
-
});
|
|
895
|
-
}
|
|
896
|
-
}
|
|
897
|
-
catch (parseError) {
|
|
898
|
-
checks.push({
|
|
899
|
-
id: 'L3:l3-memory-health',
|
|
900
|
-
ok: false,
|
|
901
|
-
message: `.peaks/memory/index.json is not valid JSON: ${getErrorMessage(parseError)}`
|
|
902
|
-
});
|
|
903
|
-
}
|
|
904
|
-
}
|
|
905
|
-
else {
|
|
906
|
-
checks.push({
|
|
907
|
-
id: 'L3:l3-memory-health',
|
|
908
|
-
ok: true,
|
|
909
|
-
message: 'No .peaks/memory/index.json yet (no memories extracted)'
|
|
910
|
-
});
|
|
911
|
-
}
|
|
912
|
-
}
|
|
913
|
-
catch (error) {
|
|
914
|
-
checks.push({
|
|
915
|
-
id: 'L3:l3-memory-health',
|
|
916
|
-
ok: true,
|
|
917
|
-
message: `L3:l3-memory-health probe failed (${getErrorMessage(error)}); skipping check`
|
|
918
|
-
});
|
|
919
|
-
}
|
|
920
|
-
const failed = checks.filter((check) => !check.ok).length;
|
|
921
|
-
return {
|
|
922
|
-
checks,
|
|
923
|
-
summary: {
|
|
924
|
-
ok: failed === 0,
|
|
925
|
-
passed: checks.length - failed,
|
|
926
|
-
failed
|
|
927
|
-
}
|
|
928
|
-
};
|
|
929
|
-
}
|
|
13
|
+
export * from './doctor-service/index.js';
|
|
14
|
+
// Re-export the helper symbols the legacy monolithic doctor-service.ts
|
|
15
|
+
// exposed at the package boundary. These were split into the check
|
|
16
|
+
// modules during rid-004; we re-export here so legacy callers
|
|
17
|
+
// (`import { compareDistVersion } from '../services/doctor/doctor-service.js'`)
|
|
18
|
+
// keep working without modification.
|
|
19
|
+
export { compareDistVersion } from './doctor-service/checks/dist-source-version.js';
|
|
20
|
+
export { inspectWorkspaceLayout } from './doctor-service/checks/workspace-layout.js';
|
|
21
|
+
export { collectGateguardEntries } from './doctor-service/checks/gateguard-conflict.js';
|
|
22
|
+
// Re-export the fixed plugin registry so characterization tests and
|
|
23
|
+
// future CLI surfaces (e.g. `peaks doctor --list-checks`) can enumerate
|
|
24
|
+
// the check pipeline without reaching into a private module path.
|
|
25
|
+
export { PLUGINS } from './doctor-service/plugin-registry.js';
|