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,13 +1,175 @@
|
|
|
1
|
-
import { mkdir, rename } from 'node:fs/promises';
|
|
2
|
-
import { dirname, join } from 'node:path';
|
|
3
|
-
import { isDirectory } from '
|
|
4
|
-
|
|
1
|
+
import { mkdir, readFile, rename, readdir } from 'node:fs/promises';
|
|
2
|
+
import { dirname, join, resolve } from 'node:path';
|
|
3
|
+
import { isDirectory } from 'peaks-loop-shared/fs';
|
|
4
|
+
import { validateChangeId } from './artifact-boundary.js';
|
|
5
|
+
import { findStaleChangeFiles, parseCapabilityMapping, readC8Summary, resolveCoverageSummaryPath, validateCapabilityCoverage, } from './coverage-evidence-reader.js';
|
|
6
|
+
export class OpenSpecArchiveError extends Error {
|
|
7
|
+
code;
|
|
8
|
+
detail;
|
|
9
|
+
constructor(code, message, detail = {}) {
|
|
10
|
+
super(message);
|
|
11
|
+
this.code = code;
|
|
12
|
+
this.detail = detail;
|
|
13
|
+
this.name = 'OpenSpecArchiveError';
|
|
14
|
+
}
|
|
15
|
+
}
|
|
5
16
|
function defaultOpenSpecRoot() {
|
|
6
17
|
return join(process.cwd(), 'openspec');
|
|
7
18
|
}
|
|
19
|
+
/**
|
|
20
|
+
* Parse the `## Coverage Evidence` block out of a proposal.md.
|
|
21
|
+
*
|
|
22
|
+
* Accepted shape (markdown table):
|
|
23
|
+
*
|
|
24
|
+
* ## Coverage Evidence
|
|
25
|
+
*
|
|
26
|
+
* | capability | requirement | status | testAnchor |
|
|
27
|
+
* | --- | --- | --- | --- |
|
|
28
|
+
* | quality-gates | 100% coverage for included modules | covered | tests/unit/quality-gates.test.ts |
|
|
29
|
+
*
|
|
30
|
+
* Returns `{ present: false }` when the heading is missing. Throws
|
|
31
|
+
* `OpenSpecArchiveError` with `code: 'OPENSPEC_COVERAGE_EVIDENCE_MALFORMED'`
|
|
32
|
+
* when the heading is present but no parsable rows are extracted.
|
|
33
|
+
*/
|
|
34
|
+
export async function parseCoverageEvidence(proposalPath) {
|
|
35
|
+
let raw;
|
|
36
|
+
try {
|
|
37
|
+
raw = await readFile(proposalPath, 'utf8');
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
40
|
+
return {
|
|
41
|
+
rows: [],
|
|
42
|
+
present: false,
|
|
43
|
+
capabilityRows: [],
|
|
44
|
+
summaryStatus: 'unavailable',
|
|
45
|
+
capabilityValidation: 'not-enforced',
|
|
46
|
+
staleFiles: [],
|
|
47
|
+
mismatches: [],
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
const lines = raw.split(/\r?\n/);
|
|
51
|
+
const headingIdx = lines.findIndex((line) => /^##\s+Coverage\s+Evidence\s*$/.test(line));
|
|
52
|
+
if (headingIdx === -1) {
|
|
53
|
+
return {
|
|
54
|
+
rows: [],
|
|
55
|
+
present: false,
|
|
56
|
+
capabilityRows: [],
|
|
57
|
+
summaryStatus: 'unavailable',
|
|
58
|
+
capabilityValidation: 'not-enforced',
|
|
59
|
+
staleFiles: [],
|
|
60
|
+
mismatches: [],
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
const rows = [];
|
|
64
|
+
let i = headingIdx + 1;
|
|
65
|
+
let inFence = false;
|
|
66
|
+
while (i < lines.length) {
|
|
67
|
+
const line = lines[i];
|
|
68
|
+
if (line === undefined)
|
|
69
|
+
break;
|
|
70
|
+
if (!inFence && /^##\s+/.test(line))
|
|
71
|
+
break;
|
|
72
|
+
if (/^```/.test(line.trim())) {
|
|
73
|
+
inFence = !inFence;
|
|
74
|
+
i += 1;
|
|
75
|
+
continue;
|
|
76
|
+
}
|
|
77
|
+
const parsed = parseTableRow(line);
|
|
78
|
+
if (parsed !== null) {
|
|
79
|
+
rows.push({ ...parsed, line: i + 1 });
|
|
80
|
+
}
|
|
81
|
+
i += 1;
|
|
82
|
+
}
|
|
83
|
+
if (rows.length === 0) {
|
|
84
|
+
throw new OpenSpecArchiveError('OPENSPEC_COVERAGE_EVIDENCE_MALFORMED', `proposal.md contains a "## Coverage Evidence" heading but no parsable requirement rows`, { proposalPath });
|
|
85
|
+
}
|
|
86
|
+
return {
|
|
87
|
+
rows,
|
|
88
|
+
present: true,
|
|
89
|
+
capabilityRows: [],
|
|
90
|
+
summaryStatus: 'unavailable',
|
|
91
|
+
capabilityValidation: 'not-enforced',
|
|
92
|
+
staleFiles: [],
|
|
93
|
+
mismatches: [],
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
function parseTableRow(line) {
|
|
97
|
+
const trimmed = line.trim();
|
|
98
|
+
if (!trimmed.startsWith('|'))
|
|
99
|
+
return null;
|
|
100
|
+
const cells = trimmed.split('|').slice(1, -1).map((c) => c.trim());
|
|
101
|
+
if (cells.length < 3)
|
|
102
|
+
return null;
|
|
103
|
+
const capability = cells[0];
|
|
104
|
+
const requirement = cells[1];
|
|
105
|
+
const statusRaw = cells[2];
|
|
106
|
+
const testAnchor = cells[3];
|
|
107
|
+
if (capability === undefined || requirement === undefined || statusRaw === undefined)
|
|
108
|
+
return null;
|
|
109
|
+
if (capability.toLowerCase() === 'capability' && requirement.toLowerCase() === 'requirement')
|
|
110
|
+
return null;
|
|
111
|
+
if (/^-+$/.test(capability.replace(/\s+/g, '')))
|
|
112
|
+
return null;
|
|
113
|
+
const status = statusRaw.toLowerCase();
|
|
114
|
+
if (status !== 'covered' && status !== 'partial' && status !== 'uncovered')
|
|
115
|
+
return null;
|
|
116
|
+
const row = {
|
|
117
|
+
capability,
|
|
118
|
+
requirement,
|
|
119
|
+
status: status,
|
|
120
|
+
};
|
|
121
|
+
if (testAnchor !== undefined && testAnchor !== '') {
|
|
122
|
+
row.testAnchor = testAnchor;
|
|
123
|
+
}
|
|
124
|
+
return row;
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Determine whether the change has any spec files (specs/<capability>/spec.md).
|
|
128
|
+
* When the change has zero spec files, there are no requirements to enforce,
|
|
129
|
+
* and the Pre-cond 2 gate is skipped — backward-compat for legacy spec-less
|
|
130
|
+
* changes (e.g. design-only or doc-only changes).
|
|
131
|
+
*/
|
|
132
|
+
async function changeHasSpecs(changeRoot) {
|
|
133
|
+
const specsRoot = join(changeRoot, 'specs');
|
|
134
|
+
if (!(await isDirectory(specsRoot)))
|
|
135
|
+
return false;
|
|
136
|
+
const entries = await readdir(specsRoot);
|
|
137
|
+
for (const entry of entries) {
|
|
138
|
+
const specPath = join(specsRoot, entry, 'spec.md');
|
|
139
|
+
if (await isDirectory(join(specsRoot, entry))) {
|
|
140
|
+
try {
|
|
141
|
+
await readFile(specPath, 'utf8');
|
|
142
|
+
return true;
|
|
143
|
+
}
|
|
144
|
+
catch {
|
|
145
|
+
// missing spec.md; keep scanning siblings
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
return false;
|
|
150
|
+
}
|
|
151
|
+
function evaluateGate(evidence) {
|
|
152
|
+
const failing = evidence.rows.filter((r) => r.status !== 'covered');
|
|
153
|
+
if (failing.length > 0) {
|
|
154
|
+
return {
|
|
155
|
+
ok: false,
|
|
156
|
+
reason: 'requirement-not-fully-covered',
|
|
157
|
+
failing,
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
if (evidence.rows.length === 0) {
|
|
161
|
+
return {
|
|
162
|
+
ok: false,
|
|
163
|
+
reason: 'no-coverage-evidence-block',
|
|
164
|
+
failing: [],
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
return { ok: true };
|
|
168
|
+
}
|
|
8
169
|
export async function archiveOpenSpecChange(changeId, options = {}) {
|
|
9
|
-
|
|
10
|
-
|
|
170
|
+
const changeIdResult = validateChangeId(changeId);
|
|
171
|
+
if (!changeIdResult.ok) {
|
|
172
|
+
throw new Error(`changeId ${changeId} does not match [A-Za-z0-9][A-Za-z0-9._-]*`);
|
|
11
173
|
}
|
|
12
174
|
const openspecRoot = options.openspecRoot ?? defaultOpenSpecRoot();
|
|
13
175
|
const archiveDir = options.archiveDirName ?? 'archive';
|
|
@@ -16,13 +178,163 @@ export async function archiveOpenSpecChange(changeId, options = {}) {
|
|
|
16
178
|
if (!(await isDirectory(from))) {
|
|
17
179
|
return null;
|
|
18
180
|
}
|
|
181
|
+
// Pre-cond 2 (Fix-6A + Fix-6B): Coverage Evidence gate.
|
|
182
|
+
// Only enforce when the change declares at least one spec (otherwise there
|
|
183
|
+
// are no requirements to gate on — backward-compat for design-only changes).
|
|
184
|
+
let coverage;
|
|
185
|
+
let coverageGateBypassed;
|
|
186
|
+
let coverageMismatchBypassed;
|
|
187
|
+
if (options.apply === true && (await changeHasSpecs(from))) {
|
|
188
|
+
const proposalPath = join(from, 'proposal.md');
|
|
189
|
+
// --- Fix-6A: parse Coverage Evidence block (declarative half) ---
|
|
190
|
+
const evidence = await parseCoverageEvidence(proposalPath);
|
|
191
|
+
coverage = evidence;
|
|
192
|
+
if (!evidence.present) {
|
|
193
|
+
throw new OpenSpecArchiveError('OPENSPEC_COVERAGE_GATE_FAILED', `Refusing to archive "${changeId}": proposal.md is missing a "## Coverage Evidence" block. ` +
|
|
194
|
+
'Add a Coverage Evidence table listing every Requirement from specs/*/spec.md with status covered/partial/uncovered, ' +
|
|
195
|
+
'or re-run with --force to bypass the gate.', { changeId, reason: 'no-coverage-evidence-block', coverage });
|
|
196
|
+
}
|
|
197
|
+
const verdict = evaluateGate(evidence);
|
|
198
|
+
if (!verdict.ok) {
|
|
199
|
+
if (options.force === true) {
|
|
200
|
+
coverageGateBypassed = true;
|
|
201
|
+
}
|
|
202
|
+
else {
|
|
203
|
+
throw new OpenSpecArchiveError('OPENSPEC_COVERAGE_GATE_PARTIAL', `Refusing to archive "${changeId}": ${verdict.failing.length} requirement(s) not fully covered. ` +
|
|
204
|
+
'Re-run with --force to bypass the gate, or update the Coverage Evidence table to mark each requirement as covered.', {
|
|
205
|
+
changeId,
|
|
206
|
+
reason: verdict.reason,
|
|
207
|
+
failing: verdict.failing.map((r) => ({
|
|
208
|
+
capability: r.capability,
|
|
209
|
+
requirement: r.requirement,
|
|
210
|
+
status: r.status,
|
|
211
|
+
testAnchor: r.testAnchor,
|
|
212
|
+
line: r.line,
|
|
213
|
+
})),
|
|
214
|
+
coverage,
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
// --- Fix-6B: parse Capability Mapping block + validate against c8 summary ---
|
|
219
|
+
const mapping = await parseCapabilityMapping(proposalPath);
|
|
220
|
+
coverage = {
|
|
221
|
+
...coverage,
|
|
222
|
+
capabilityRows: mapping.rows,
|
|
223
|
+
};
|
|
224
|
+
if (mapping.rows.length === 0) {
|
|
225
|
+
// No mapping → cannot enforce per-capability. Refuse with the same
|
|
226
|
+
// gate-failed code but a distinct reason in detail (Fix-6B AC3).
|
|
227
|
+
throw new OpenSpecArchiveError('OPENSPEC_COVERAGE_GATE_FAILED', `Refusing to archive "${changeId}": proposal.md is missing a "## Capability Mapping" block. ` +
|
|
228
|
+
'Add a Capability Mapping table listing each declared capability alongside its source file or subtree, ' +
|
|
229
|
+
'or re-run with --force to bypass the gate.', { changeId, reason: 'no-capability-mapping-block', coverage });
|
|
230
|
+
}
|
|
231
|
+
// Discover coverage-summary.json
|
|
232
|
+
const projectRoot = resolve(openspecRoot, '..');
|
|
233
|
+
const summaryPathResult = await resolveCoverageSummaryPath({
|
|
234
|
+
projectRoot,
|
|
235
|
+
...(options.coverageSummaryPath !== undefined ? { explicitPath: options.coverageSummaryPath } : {}),
|
|
236
|
+
});
|
|
237
|
+
if (!summaryPathResult.ok) {
|
|
238
|
+
// AC1: missing or not-readable
|
|
239
|
+
const triedPaths = summaryPathResult.error.code === 'missing' ? summaryPathResult.error.triedPaths : [summaryPathResult.error.path];
|
|
240
|
+
throw new OpenSpecArchiveError('OPENSPEC_COVERAGE_EVIDENCE_MISSING', `Refusing to archive "${changeId}": no coverage-summary.json found at any of: ${triedPaths.join(', ')}. ` +
|
|
241
|
+
'Run `pnpm test:coverage` (which invokes scripts/coverage-c8.mjs) to generate one, ' +
|
|
242
|
+
'or pass --coverage-summary <path> to point at an existing summary, ' +
|
|
243
|
+
'or re-run with --force to bypass.', {
|
|
244
|
+
changeId,
|
|
245
|
+
triedPaths,
|
|
246
|
+
coverage,
|
|
247
|
+
});
|
|
248
|
+
}
|
|
249
|
+
// Parse summary + check freshness (AC2)
|
|
250
|
+
let summary;
|
|
251
|
+
try {
|
|
252
|
+
summary = await readC8Summary(summaryPathResult.value);
|
|
253
|
+
}
|
|
254
|
+
catch (error) {
|
|
255
|
+
throw new OpenSpecArchiveError('OPENSPEC_COVERAGE_EVIDENCE_MISSING', `Refusing to archive "${changeId}": coverage-summary.json at ${summaryPathResult.value} is malformed (${error.message}). ` +
|
|
256
|
+
'Re-run `pnpm test:coverage` to regenerate, or pass --coverage-summary <path> to point at a valid summary.', { changeId, path: summaryPathResult.value, coverage });
|
|
257
|
+
}
|
|
258
|
+
coverage = {
|
|
259
|
+
...coverage,
|
|
260
|
+
summaryPath: summary.path,
|
|
261
|
+
};
|
|
262
|
+
const staleFiles = await findStaleChangeFiles({
|
|
263
|
+
projectRoot,
|
|
264
|
+
openspecRoot,
|
|
265
|
+
changeId,
|
|
266
|
+
summary,
|
|
267
|
+
});
|
|
268
|
+
if (staleFiles.length > 0) {
|
|
269
|
+
coverage = {
|
|
270
|
+
...coverage,
|
|
271
|
+
summaryStatus: 'stale',
|
|
272
|
+
staleFiles,
|
|
273
|
+
};
|
|
274
|
+
throw new OpenSpecArchiveError('OPENSPEC_COVERAGE_EVIDENCE_STALE', `Refusing to archive "${changeId}": coverage-summary.json is older than ${staleFiles.length} change file(s). ` +
|
|
275
|
+
'Re-run `pnpm test:coverage` to refresh, or re-run with --force to bypass.', { changeId, staleFiles, summaryPath: summary.path, coverage });
|
|
276
|
+
}
|
|
277
|
+
coverage = {
|
|
278
|
+
...coverage,
|
|
279
|
+
summaryStatus: 'fresh',
|
|
280
|
+
};
|
|
281
|
+
// Per-capability coverage check (AC4)
|
|
282
|
+
const validation = await validateCapabilityCoverage({
|
|
283
|
+
projectRoot,
|
|
284
|
+
summary,
|
|
285
|
+
rows: mapping.rows,
|
|
286
|
+
});
|
|
287
|
+
if (!validation.ok) {
|
|
288
|
+
coverage = {
|
|
289
|
+
...coverage,
|
|
290
|
+
capabilityValidation: 'mismatch',
|
|
291
|
+
mismatches: validation.mismatches,
|
|
292
|
+
};
|
|
293
|
+
if (options.force === true) {
|
|
294
|
+
coverageMismatchBypassed = true;
|
|
295
|
+
}
|
|
296
|
+
else {
|
|
297
|
+
throw new OpenSpecArchiveError('OPENSPEC_COVERAGE_EVIDENCE_MISMATCH', `Refusing to archive "${changeId}": ${validation.mismatches.length} capability row(s) claim "covered" in proposal.md but c8 reports < 100% coverage. ` +
|
|
298
|
+
'Re-run `pnpm test:coverage` after closing the gap, or re-run with --force to bypass.', {
|
|
299
|
+
changeId,
|
|
300
|
+
mismatches: validation.mismatches,
|
|
301
|
+
coverage,
|
|
302
|
+
});
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
else {
|
|
306
|
+
coverage = {
|
|
307
|
+
...coverage,
|
|
308
|
+
capabilityValidation: 'ok',
|
|
309
|
+
};
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
else if (options.apply === true) {
|
|
313
|
+
// Spec-less change — surface coverage evidence for visibility, never fail.
|
|
314
|
+
const proposalPath = join(from, 'proposal.md');
|
|
315
|
+
coverage = await parseCoverageEvidence(proposalPath);
|
|
316
|
+
}
|
|
19
317
|
if (options.apply !== true) {
|
|
20
|
-
return {
|
|
318
|
+
return {
|
|
319
|
+
changeId,
|
|
320
|
+
from,
|
|
321
|
+
to,
|
|
322
|
+
applied: false,
|
|
323
|
+
...(coverage !== undefined ? { coverage } : {}),
|
|
324
|
+
};
|
|
21
325
|
}
|
|
22
326
|
if (await isDirectory(to)) {
|
|
23
327
|
throw new Error(`Refusing to archive: target already exists at ${to}`);
|
|
24
328
|
}
|
|
25
329
|
await mkdir(dirname(to), { recursive: true });
|
|
26
330
|
await rename(from, to);
|
|
27
|
-
return {
|
|
331
|
+
return {
|
|
332
|
+
changeId,
|
|
333
|
+
from,
|
|
334
|
+
to,
|
|
335
|
+
applied: true,
|
|
336
|
+
...(coverage !== undefined ? { coverage } : {}),
|
|
337
|
+
...(coverageGateBypassed === true ? { coverageGateBypassed: true } : {}),
|
|
338
|
+
...(coverageMismatchBypassed === true ? { coverageMismatchBypassed: true } : {}),
|
|
339
|
+
};
|
|
28
340
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { mkdir, writeFile } from 'node:fs/promises';
|
|
2
2
|
import { join } from 'node:path';
|
|
3
|
-
import { isDirectory, pathExists } from '
|
|
3
|
+
import { isDirectory, pathExists } from 'peaks-loop-shared/fs';
|
|
4
4
|
const README_BODY = `# OpenSpec — change proposals for this project
|
|
5
5
|
|
|
6
6
|
This directory hosts the \`peaks openspec\` change proposal lifecycle:
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
*/
|
|
13
13
|
import { existsSync, mkdirSync, writeFileSync } from 'node:fs';
|
|
14
14
|
import { join } from 'node:path';
|
|
15
|
-
import { getErrorMessage } from '
|
|
15
|
+
import { getErrorMessage } from 'peaks-loop-shared/result';
|
|
16
16
|
const SLUGIFY = /[^a-z0-9]+/g;
|
|
17
17
|
function slugify(text) {
|
|
18
18
|
return text
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { mkdir, readFile, writeFile } from 'node:fs/promises';
|
|
2
2
|
import { dirname, join, resolve } from 'node:path';
|
|
3
3
|
import { fileURLToPath } from 'node:url';
|
|
4
|
-
import { isDirectory, pathExists } from '
|
|
4
|
+
import { isDirectory, pathExists } from 'peaks-loop-shared/fs';
|
|
5
5
|
import { validateAgainstSchema } from '../../shared/json-schema-mini.js';
|
|
6
|
-
|
|
6
|
+
import { validateChangeId } from './artifact-boundary.js';
|
|
7
7
|
function defaultOpenSpecRoot() {
|
|
8
8
|
return join(process.cwd(), 'openspec');
|
|
9
9
|
}
|
|
@@ -113,8 +113,11 @@ export async function renderOpenSpecChange(request, options = {}) {
|
|
|
113
113
|
throw new OpenSpecRenderRequestInvalidError(result.errors);
|
|
114
114
|
}
|
|
115
115
|
}
|
|
116
|
-
else
|
|
117
|
-
|
|
116
|
+
else {
|
|
117
|
+
const changeIdResult = validateChangeId(request.changeId);
|
|
118
|
+
if (!changeIdResult.ok) {
|
|
119
|
+
throw new Error(`changeId ${request.changeId} does not match [A-Za-z0-9][A-Za-z0-9._-]*`);
|
|
120
|
+
}
|
|
118
121
|
}
|
|
119
122
|
const openspecRoot = options.openspecRoot ?? defaultOpenSpecRoot();
|
|
120
123
|
const changeRoot = join(openspecRoot, 'changes', request.changeId);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { join } from 'node:path';
|
|
2
|
-
import { isDirectory, listDirectories, pathExists, readText } from '
|
|
2
|
+
import { isDirectory, listDirectories, pathExists, readText } from 'peaks-loop-shared/fs';
|
|
3
3
|
function defaultOpenSpecRoot() {
|
|
4
4
|
return join(process.cwd(), 'openspec');
|
|
5
5
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { join } from 'node:path';
|
|
2
|
-
import { isDirectory } from '
|
|
2
|
+
import { isDirectory } from 'peaks-loop-shared/fs';
|
|
3
3
|
import { loadOpenSpecChange } from './openspec-scan-service.js';
|
|
4
|
-
|
|
4
|
+
import { validateChangeId } from './artifact-boundary.js';
|
|
5
5
|
function defaultOpenSpecRoot() {
|
|
6
6
|
return join(process.cwd(), 'openspec');
|
|
7
7
|
}
|
|
@@ -10,11 +10,12 @@ async function defaultExternalRunner(_command, _args) {
|
|
|
10
10
|
}
|
|
11
11
|
function buildInternalIssues(changeId, detail) {
|
|
12
12
|
const issues = [];
|
|
13
|
-
|
|
13
|
+
const changeIdResult = validateChangeId(changeId);
|
|
14
|
+
if (!changeIdResult.ok) {
|
|
14
15
|
issues.push({
|
|
15
16
|
level: 'error',
|
|
16
17
|
rule: 'change-id-format',
|
|
17
|
-
message:
|
|
18
|
+
message: changeIdResult.error.message
|
|
18
19
|
});
|
|
19
20
|
}
|
|
20
21
|
if (detail === null || detail.proposal === null) {
|
|
@@ -9,6 +9,16 @@ export function preferencesPath(projectRoot) {
|
|
|
9
9
|
export function loadPreferences(projectRoot) {
|
|
10
10
|
const filePath = preferencesPath(projectRoot);
|
|
11
11
|
if (!existsSync(filePath)) {
|
|
12
|
+
// Slice 2026-07-22-orchestrator-memory-preflight: when
|
|
13
|
+
// .peaks/preferences.json is missing, this returns a clone of
|
|
14
|
+
// DEFAULT_PREFERENCES, which now carries the `memoryPreflight`
|
|
15
|
+
// block (see preferences-types.ts). Partial overlays fall through
|
|
16
|
+
// to mergePreferences below, where the new `memoryPreflight` block
|
|
17
|
+
// shallow-replaces the default block — `mergePreferences` does not
|
|
18
|
+
// deep-merge `memoryPreflight` (only `fanout` is deep-merged), so a
|
|
19
|
+
// partial overlay will replace the whole block. Resolution still
|
|
20
|
+
// works because `resolveMemoryPreflightConfig` applies field-level
|
|
21
|
+
// defaults over the partial input (see memory-preflight-config.ts).
|
|
12
22
|
return structuredClone(DEFAULT_PREFERENCES);
|
|
13
23
|
}
|
|
14
24
|
let raw;
|
|
@@ -131,6 +131,19 @@ export interface ProjectPreferences {
|
|
|
131
131
|
* saved file is rejected at load time.
|
|
132
132
|
*/
|
|
133
133
|
readonly fanout: FanoutPreference;
|
|
134
|
+
/**
|
|
135
|
+
* Slice 2026-07-22-orchestrator-memory-preflight: orchestrator-side memory
|
|
136
|
+
* preflight knobs. Optional — `resolveMemoryPreflightConfig` (see
|
|
137
|
+
* src/services/context/memory-preflight-config.ts) merges over a hard-coded
|
|
138
|
+
* default block when this key is absent, so legacy preferences.json files
|
|
139
|
+
* keep working unchanged.
|
|
140
|
+
*/
|
|
141
|
+
readonly memoryPreflight?: {
|
|
142
|
+
readonly enabled?: boolean;
|
|
143
|
+
readonly maxTokens?: number;
|
|
144
|
+
readonly listCap?: number;
|
|
145
|
+
readonly contentCacheBytes?: number;
|
|
146
|
+
};
|
|
134
147
|
}
|
|
135
148
|
export type FanoutMode = 'fan-out';
|
|
136
149
|
export declare const FANOUT_MODES: readonly FanoutMode[];
|
|
@@ -50,4 +50,15 @@ export const DEFAULT_PREFERENCES = {
|
|
|
50
50
|
fanout: {
|
|
51
51
|
defaultMode: 'fan-out'
|
|
52
52
|
},
|
|
53
|
+
// Slice 2026-07-22-orchestrator-memory-preflight: defaults aligned with
|
|
54
|
+
// memory-preflight-config.ts::DEFAULTS. Kept in sync manually because
|
|
55
|
+
// loadPreferences() returns DEFAULT_PREFERENCES verbatim when the on-disk
|
|
56
|
+
// file is absent, and partial overlays rely on memoryPreflight being
|
|
57
|
+
// defined here.
|
|
58
|
+
memoryPreflight: {
|
|
59
|
+
enabled: true,
|
|
60
|
+
maxTokens: 1200,
|
|
61
|
+
listCap: 12,
|
|
62
|
+
contentCacheBytes: 6000,
|
|
63
|
+
},
|
|
53
64
|
};
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* rid-012 (2026-07-27) — screenshot archive service.
|
|
3
|
+
*
|
|
4
|
+
* Purpose: enforce the peaks-qa SKILL.md "Hard contracts for browser
|
|
5
|
+
* validation" Contract 1 — every Playwright screenshot MUST land under
|
|
6
|
+
* `.peaks/_runtime/<sessionId>/qa/screenshots/`, not at the project
|
|
7
|
+
* root. When the LLM forgets to pass `filename` to
|
|
8
|
+
* `browser_take_screenshot`, Playwright MCP writes to the current
|
|
9
|
+
* working directory (typically the project root), which scatters
|
|
10
|
+
* `.png` files at the repo top level. This service is the
|
|
11
|
+
* remediation: scan a directory, move stray screenshot files into
|
|
12
|
+
* the canonical screenshots/ subdir under the active QA scope.
|
|
13
|
+
*
|
|
14
|
+
* CLI surface (peaks qa archive-screenshots):
|
|
15
|
+
* peaks qa archive-screenshots [--source <dir>] [--project <root>]
|
|
16
|
+
*
|
|
17
|
+
* Behavior:
|
|
18
|
+
* - Scan `--source` for *.png / *.jpg / *.jpeg files (case-insensitive
|
|
19
|
+
* extension match). Recurse exactly 1 level deep — the project
|
|
20
|
+
* root plus one subdir (covers `./foo.png` + `./screenshots/*.png`).
|
|
21
|
+
* Avoid recursing into `.peaks/` or `node_modules/` (artifact dirs).
|
|
22
|
+
* - Auto-create `.peaks/_runtime/<session-id>/qa/screenshots/` if
|
|
23
|
+
* missing (mkdirSync recursive, mode 0o755).
|
|
24
|
+
* - For each stray file: `fs.renameSync(src, dst)`. If `dst` exists
|
|
25
|
+
* (filename collision across multiple invocations), suffix with
|
|
26
|
+
* `-<ISO-timestamp>` so the move is non-destructive.
|
|
27
|
+
* - Return JSON envelope with moved list + skipped list + final
|
|
28
|
+
* screenshots/ dir contents.
|
|
29
|
+
*/
|
|
30
|
+
export type ScreenshotExtension = '.png' | '.jpg' | '.jpeg';
|
|
31
|
+
export interface ArchiveScreenshotOptions {
|
|
32
|
+
readonly sourceDir: string;
|
|
33
|
+
readonly targetDir: string;
|
|
34
|
+
readonly now?: Date;
|
|
35
|
+
}
|
|
36
|
+
export interface ArchiveScreenshotEnvelope {
|
|
37
|
+
readonly scannedSource: string;
|
|
38
|
+
readonly targetDir: string;
|
|
39
|
+
readonly moved: readonly {
|
|
40
|
+
readonly from: string;
|
|
41
|
+
readonly to: string;
|
|
42
|
+
}[];
|
|
43
|
+
readonly skipped: readonly {
|
|
44
|
+
readonly path: string;
|
|
45
|
+
readonly reason: string;
|
|
46
|
+
}[];
|
|
47
|
+
readonly targetContentsBefore: readonly string[];
|
|
48
|
+
readonly targetContentsAfter: readonly string[];
|
|
49
|
+
readonly archivedAt: string;
|
|
50
|
+
}
|
|
51
|
+
export declare function archiveScreenshots(options: ArchiveScreenshotOptions): ArchiveScreenshotEnvelope;
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* rid-012 (2026-07-27) — screenshot archive service.
|
|
3
|
+
*
|
|
4
|
+
* Purpose: enforce the peaks-qa SKILL.md "Hard contracts for browser
|
|
5
|
+
* validation" Contract 1 — every Playwright screenshot MUST land under
|
|
6
|
+
* `.peaks/_runtime/<sessionId>/qa/screenshots/`, not at the project
|
|
7
|
+
* root. When the LLM forgets to pass `filename` to
|
|
8
|
+
* `browser_take_screenshot`, Playwright MCP writes to the current
|
|
9
|
+
* working directory (typically the project root), which scatters
|
|
10
|
+
* `.png` files at the repo top level. This service is the
|
|
11
|
+
* remediation: scan a directory, move stray screenshot files into
|
|
12
|
+
* the canonical screenshots/ subdir under the active QA scope.
|
|
13
|
+
*
|
|
14
|
+
* CLI surface (peaks qa archive-screenshots):
|
|
15
|
+
* peaks qa archive-screenshots [--source <dir>] [--project <root>]
|
|
16
|
+
*
|
|
17
|
+
* Behavior:
|
|
18
|
+
* - Scan `--source` for *.png / *.jpg / *.jpeg files (case-insensitive
|
|
19
|
+
* extension match). Recurse exactly 1 level deep — the project
|
|
20
|
+
* root plus one subdir (covers `./foo.png` + `./screenshots/*.png`).
|
|
21
|
+
* Avoid recursing into `.peaks/` or `node_modules/` (artifact dirs).
|
|
22
|
+
* - Auto-create `.peaks/_runtime/<session-id>/qa/screenshots/` if
|
|
23
|
+
* missing (mkdirSync recursive, mode 0o755).
|
|
24
|
+
* - For each stray file: `fs.renameSync(src, dst)`. If `dst` exists
|
|
25
|
+
* (filename collision across multiple invocations), suffix with
|
|
26
|
+
* `-<ISO-timestamp>` so the move is non-destructive.
|
|
27
|
+
* - Return JSON envelope with moved list + skipped list + final
|
|
28
|
+
* screenshots/ dir contents.
|
|
29
|
+
*/
|
|
30
|
+
import { existsSync, mkdirSync, readdirSync, renameSync } from 'node:fs';
|
|
31
|
+
import { join, resolve } from 'node:path';
|
|
32
|
+
const SCREENSHOT_EXTS = new Set(['.png', '.jpg', '.jpeg']);
|
|
33
|
+
function isScreenshotFile(name) {
|
|
34
|
+
const lower = name.toLowerCase();
|
|
35
|
+
for (const ext of SCREENSHOT_EXTS) {
|
|
36
|
+
if (lower.endsWith(ext)) {
|
|
37
|
+
return true;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
return false;
|
|
41
|
+
}
|
|
42
|
+
function listScreenshotFiles(sourceDir, targetDir) {
|
|
43
|
+
// Recurse 1 level deep. Skip .peaks/ + node_modules/ + hidden dirs.
|
|
44
|
+
// Also skip the targetDir itself to avoid re-scanning already-archived files
|
|
45
|
+
// (otherwise the same file would appear twice across calls, and the second
|
|
46
|
+
// call's collision check would treat it as a duplicate).
|
|
47
|
+
const targetBaseName = targetDir.split(/[\\/]/).pop();
|
|
48
|
+
const out = [];
|
|
49
|
+
const top = readdirSync(sourceDir, { withFileTypes: true });
|
|
50
|
+
for (const entry of top) {
|
|
51
|
+
if (entry.name === targetBaseName) {
|
|
52
|
+
continue;
|
|
53
|
+
}
|
|
54
|
+
const full = join(sourceDir, entry.name);
|
|
55
|
+
if (entry.isFile()) {
|
|
56
|
+
if (isScreenshotFile(entry.name)) {
|
|
57
|
+
out.push(full);
|
|
58
|
+
}
|
|
59
|
+
continue;
|
|
60
|
+
}
|
|
61
|
+
if (entry.isDirectory()) {
|
|
62
|
+
if (entry.name.startsWith('.') || entry.name === 'node_modules') {
|
|
63
|
+
continue;
|
|
64
|
+
}
|
|
65
|
+
try {
|
|
66
|
+
const sub = readdirSync(full, { withFileTypes: true });
|
|
67
|
+
for (const subEntry of sub) {
|
|
68
|
+
if (subEntry.isFile() && isScreenshotFile(subEntry.name)) {
|
|
69
|
+
out.push(join(full, subEntry.name));
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
catch {
|
|
74
|
+
// unreadable subdir — skip silently
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
return out;
|
|
79
|
+
}
|
|
80
|
+
export function archiveScreenshots(options) {
|
|
81
|
+
const sourceDir = resolve(options.sourceDir);
|
|
82
|
+
const targetDir = resolve(options.targetDir);
|
|
83
|
+
const now = options.now ?? new Date();
|
|
84
|
+
if (!existsSync(sourceDir)) {
|
|
85
|
+
return {
|
|
86
|
+
scannedSource: sourceDir,
|
|
87
|
+
targetDir,
|
|
88
|
+
moved: [],
|
|
89
|
+
skipped: [{ path: sourceDir, reason: 'source dir not found' }],
|
|
90
|
+
targetContentsBefore: [],
|
|
91
|
+
targetContentsAfter: [],
|
|
92
|
+
archivedAt: now.toISOString()
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
const targetContentsBefore = existsSync(targetDir)
|
|
96
|
+
? readdirSync(targetDir)
|
|
97
|
+
: [];
|
|
98
|
+
mkdirSync(targetDir, { recursive: true });
|
|
99
|
+
const candidates = listScreenshotFiles(sourceDir, targetDir);
|
|
100
|
+
const moved = [];
|
|
101
|
+
const skipped = [];
|
|
102
|
+
for (const src of candidates) {
|
|
103
|
+
const baseName = src.split(/[\\/]/).pop() ?? 'unknown.png';
|
|
104
|
+
let dst = join(targetDir, baseName);
|
|
105
|
+
if (existsSync(dst)) {
|
|
106
|
+
// collision: suffix with ISO timestamp
|
|
107
|
+
const dot = baseName.lastIndexOf('.');
|
|
108
|
+
const stem = dot > 0 ? baseName.slice(0, dot) : baseName;
|
|
109
|
+
const ext = dot > 0 ? baseName.slice(dot) : '';
|
|
110
|
+
const stamp = now.toISOString().replace(/[:.]/g, '-');
|
|
111
|
+
dst = join(targetDir, `${stem}-${stamp}${ext}`);
|
|
112
|
+
}
|
|
113
|
+
try {
|
|
114
|
+
renameSync(src, dst);
|
|
115
|
+
moved.push({ from: src, to: dst });
|
|
116
|
+
}
|
|
117
|
+
catch (err) {
|
|
118
|
+
skipped.push({
|
|
119
|
+
path: src,
|
|
120
|
+
reason: err instanceof Error ? err.message : 'rename failed'
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
const targetContentsAfter = readdirSync(targetDir);
|
|
125
|
+
return {
|
|
126
|
+
scannedSource: sourceDir,
|
|
127
|
+
targetDir,
|
|
128
|
+
moved,
|
|
129
|
+
skipped,
|
|
130
|
+
targetContentsBefore,
|
|
131
|
+
targetContentsAfter,
|
|
132
|
+
archivedAt: now.toISOString()
|
|
133
|
+
};
|
|
134
|
+
}
|