peaks-loop 4.1.2 → 4.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +136 -47
- package/CHANGELOG.md +56 -0
- package/README-en.md +1 -1
- package/README.md +1 -1
- package/contracts/readonly-argv-whitelist.json +211 -0
- package/dist/cli/commands/audit-artifact-runners.d.ts +16 -0
- package/dist/cli/commands/audit-artifact-runners.js +170 -0
- package/dist/cli/commands/audit-command-shared.d.ts +97 -0
- package/dist/cli/commands/audit-command-shared.js +60 -0
- package/dist/cli/commands/audit-commands.d.ts +7 -31
- package/dist/cli/commands/audit-commands.js +64 -473
- package/dist/cli/commands/audit-goal-runners.d.ts +17 -0
- package/dist/cli/commands/audit-goal-runners.js +114 -0
- package/dist/cli/commands/audit-red-lines-runners.d.ts +11 -0
- package/dist/cli/commands/audit-red-lines-runners.js +43 -0
- package/dist/cli/commands/audit-refusal-data.d.ts +23 -0
- package/dist/cli/commands/audit-refusal-data.js +69 -0
- package/dist/cli/commands/audit-static-runners.d.ts +13 -0
- package/dist/cli/commands/audit-static-runners.js +139 -0
- package/dist/cli/commands/await-runners.d.ts +11 -0
- package/dist/cli/commands/await-runners.js +148 -0
- package/dist/cli/commands/code-auto-compact-command.d.ts +3 -0
- package/dist/cli/commands/code-auto-compact-command.js +124 -0
- package/dist/cli/commands/code-auto-compact-envelope.d.ts +22 -0
- package/dist/cli/commands/code-auto-compact-envelope.js +39 -0
- package/dist/cli/commands/code-context-audit-command.d.ts +3 -0
- package/dist/cli/commands/code-context-audit-command.js +68 -0
- package/dist/cli/commands/code-context-now-command.d.ts +3 -0
- package/dist/cli/commands/code-context-now-command.js +124 -0
- package/dist/cli/commands/code-context-now-envelope.d.ts +66 -0
- package/dist/cli/commands/code-context-now-envelope.js +136 -0
- package/dist/cli/commands/code-context-now-mapping.d.ts +27 -0
- package/dist/cli/commands/code-context-now-mapping.js +43 -0
- package/dist/cli/commands/code-emit-handoff-command.d.ts +3 -0
- package/dist/cli/commands/code-emit-handoff-command.js +99 -0
- package/dist/cli/commands/code-gate-step-08-command.d.ts +3 -0
- package/dist/cli/commands/code-gate-step-08-command.js +139 -0
- package/dist/cli/commands/code-post-compact-detect-command.d.ts +3 -0
- package/dist/cli/commands/code-post-compact-detect-command.js +50 -0
- package/dist/cli/commands/code-runtime-commands.d.ts +9 -43
- package/dist/cli/commands/code-runtime-commands.js +20 -672
- package/dist/cli/commands/code-runtime-session.d.ts +1 -0
- package/dist/cli/commands/code-runtime-session.js +15 -0
- package/dist/cli/commands/compact-command.d.ts +6 -2
- package/dist/cli/commands/compact-command.js +12 -559
- package/dist/cli/commands/compact-dry-run-command.d.ts +3 -0
- package/dist/cli/commands/compact-dry-run-command.js +87 -0
- package/dist/cli/commands/compact-force-command.d.ts +3 -0
- package/dist/cli/commands/compact-force-command.js +98 -0
- package/dist/cli/commands/compact-harness-window-command.d.ts +3 -0
- package/dist/cli/commands/compact-harness-window-command.js +174 -0
- package/dist/cli/commands/compact-history-command.d.ts +3 -0
- package/dist/cli/commands/compact-history-command.js +89 -0
- package/dist/cli/commands/compact-read-commands.d.ts +3 -0
- package/dist/cli/commands/compact-read-commands.js +123 -0
- package/dist/cli/commands/compact-settle-command.d.ts +3 -0
- package/dist/cli/commands/compact-settle-command.js +130 -0
- package/dist/cli/commands/compact-shared.d.ts +9 -0
- package/dist/cli/commands/compact-shared.js +26 -0
- package/dist/cli/commands/core/skill-command.d.ts +1 -44
- package/dist/cli/commands/core/skill-command.js +13 -541
- package/dist/cli/commands/core/skill-context-verdict.d.ts +38 -0
- package/dist/cli/commands/core/skill-context-verdict.js +63 -0
- package/dist/cli/commands/core/skill-generated-config-notice.d.ts +41 -0
- package/dist/cli/commands/core/skill-generated-config-notice.js +75 -0
- package/dist/cli/commands/core/skill-heartbeat-commands.d.ts +3 -0
- package/dist/cli/commands/core/skill-heartbeat-commands.js +50 -0
- package/dist/cli/commands/core/skill-presence-commands.d.ts +3 -0
- package/dist/cli/commands/core/skill-presence-commands.js +123 -0
- package/dist/cli/commands/core/skill-presence-housekeeping-commands.d.ts +3 -0
- package/dist/cli/commands/core/skill-presence-housekeeping-commands.js +165 -0
- package/dist/cli/commands/core/skill-registry-commands.d.ts +3 -0
- package/dist/cli/commands/core/skill-registry-commands.js +117 -0
- package/dist/cli/commands/core/skill-runbook-command.d.ts +3 -0
- package/dist/cli/commands/core/skill-runbook-command.js +29 -0
- package/dist/cli/commands/dispatch-command-spec.d.ts +2 -0
- package/dist/cli/commands/dispatch-command-spec.js +62 -0
- package/dist/cli/commands/dispatch-commands.js +159 -775
- package/dist/cli/commands/dispatch-detached-branch.d.ts +3 -0
- package/dist/cli/commands/dispatch-detached-branch.js +58 -0
- package/dist/cli/commands/dispatch-envelope.d.ts +36 -0
- package/dist/cli/commands/dispatch-envelope.js +104 -0
- package/dist/cli/commands/dispatch-isolation-lease.d.ts +17 -0
- package/dist/cli/commands/dispatch-isolation-lease.js +149 -0
- package/dist/cli/commands/dispatch-preflight.d.ts +7 -0
- package/dist/cli/commands/dispatch-preflight.js +105 -0
- package/dist/cli/commands/dispatch-prompt-blocks.d.ts +10 -0
- package/dist/cli/commands/dispatch-prompt-blocks.js +56 -0
- package/dist/cli/commands/dispatch-prompt-composition.d.ts +30 -0
- package/dist/cli/commands/dispatch-prompt-composition.js +171 -0
- package/dist/cli/commands/dispatch-record-write.d.ts +24 -0
- package/dist/cli/commands/dispatch-record-write.js +96 -0
- package/dist/cli/commands/dispatch-tool-call.d.ts +15 -0
- package/dist/cli/commands/dispatch-tool-call.js +128 -0
- package/dist/cli/commands/evolution-command-shared.d.ts +78 -0
- package/dist/cli/commands/evolution-command-shared.js +107 -0
- package/dist/cli/commands/evolution-commands.d.ts +11 -1
- package/dist/cli/commands/evolution-commands.js +19 -350
- package/dist/cli/commands/evolution-evaluate-command.d.ts +24 -0
- package/dist/cli/commands/evolution-evaluate-command.js +138 -0
- package/dist/cli/commands/evolution-propose-command.d.ts +23 -0
- package/dist/cli/commands/evolution-propose-command.js +145 -0
- package/dist/cli/commands/evolution-status-command.d.ts +17 -0
- package/dist/cli/commands/evolution-status-command.js +41 -0
- package/dist/cli/commands/evolution-verdict-commands.d.ts +30 -0
- package/dist/cli/commands/evolution-verdict-commands.js +100 -0
- package/dist/cli/commands/finalize-accumulator.d.ts +44 -0
- package/dist/cli/commands/finalize-accumulator.js +55 -0
- package/dist/cli/commands/finalize-branches.d.ts +34 -0
- package/dist/cli/commands/finalize-branches.js +119 -0
- package/dist/cli/commands/finalize-runners.d.ts +3 -0
- package/dist/cli/commands/finalize-runners.js +92 -0
- package/dist/cli/commands/finalize-selection.d.ts +65 -0
- package/dist/cli/commands/finalize-selection.js +35 -0
- package/dist/cli/commands/gate-commands.js +26 -8
- package/dist/cli/commands/job-checkpoint-commands.d.ts +3 -0
- package/dist/cli/commands/job-checkpoint-commands.js +157 -0
- package/dist/cli/commands/job-command-shared.d.ts +109 -0
- package/dist/cli/commands/job-command-shared.js +72 -0
- package/dist/cli/commands/job-commands.d.ts +4 -64
- package/dist/cli/commands/job-commands.js +42 -552
- package/dist/cli/commands/job-cost-check-command.d.ts +3 -0
- package/dist/cli/commands/job-cost-check-command.js +41 -0
- package/dist/cli/commands/job-init-status-commands.d.ts +3 -0
- package/dist/cli/commands/job-init-status-commands.js +149 -0
- package/dist/cli/commands/job-run-commands.d.ts +3 -0
- package/dist/cli/commands/job-run-commands.js +97 -0
- package/dist/cli/commands/job-state-root.d.ts +35 -0
- package/dist/cli/commands/job-state-root.js +76 -0
- package/dist/cli/commands/loop-eval-capture-index.d.ts +4 -0
- package/dist/cli/commands/loop-eval-capture-index.js +44 -0
- package/dist/cli/commands/loop-eval-commands.d.ts +8 -5
- package/dist/cli/commands/loop-eval-commands.js +12 -626
- package/dist/cli/commands/loop-eval-eval-command.d.ts +3 -0
- package/dist/cli/commands/loop-eval-eval-command.js +168 -0
- package/dist/cli/commands/loop-eval-monotonic-command.d.ts +3 -0
- package/dist/cli/commands/loop-eval-monotonic-command.js +90 -0
- package/dist/cli/commands/loop-eval-parents.d.ts +7 -0
- package/dist/cli/commands/loop-eval-parents.js +20 -0
- package/dist/cli/commands/loop-eval-run-code.d.ts +7 -0
- package/dist/cli/commands/loop-eval-run-code.js +40 -0
- package/dist/cli/commands/loop-eval-run-command.d.ts +3 -0
- package/dist/cli/commands/loop-eval-run-command.js +113 -0
- package/dist/cli/commands/loop-eval-spec-commands.d.ts +3 -0
- package/dist/cli/commands/loop-eval-spec-commands.js +165 -0
- package/dist/cli/commands/loop-eval-spec-lint-command.d.ts +3 -0
- package/dist/cli/commands/loop-eval-spec-lint-command.js +42 -0
- package/dist/cli/commands/loop-eval-workflow-commands.d.ts +3 -0
- package/dist/cli/commands/loop-eval-workflow-commands.js +141 -0
- package/dist/cli/commands/mcp-commands.d.ts +10 -0
- package/dist/cli/commands/mcp-commands.js +157 -0
- package/dist/cli/commands/request-command-options.d.ts +49 -0
- package/dist/cli/commands/request-command-options.js +1 -0
- package/dist/cli/commands/request-commands.d.ts +3 -9
- package/dist/cli/commands/request-commands.js +17 -522
- package/dist/cli/commands/request-init-command.d.ts +3 -0
- package/dist/cli/commands/request-init-command.js +100 -0
- package/dist/cli/commands/request-lint-command.d.ts +3 -0
- package/dist/cli/commands/request-lint-command.js +50 -0
- package/dist/cli/commands/request-list-command.d.ts +10 -0
- package/dist/cli/commands/request-list-command.js +53 -0
- package/dist/cli/commands/request-repair-status-command.d.ts +3 -0
- package/dist/cli/commands/request-repair-status-command.js +61 -0
- package/dist/cli/commands/request-show-command.d.ts +3 -0
- package/dist/cli/commands/request-show-command.js +56 -0
- package/dist/cli/commands/request-transition-command.d.ts +3 -0
- package/dist/cli/commands/request-transition-command.js +122 -0
- package/dist/cli/commands/request-transition-failures.d.ts +8 -0
- package/dist/cli/commands/request-transition-failures.js +90 -0
- package/dist/cli/commands/request-transition-hooks.d.ts +50 -0
- package/dist/cli/commands/request-transition-hooks.js +115 -0
- package/dist/cli/commands/request-transition-preflight.d.ts +10 -0
- package/dist/cli/commands/request-transition-preflight.js +107 -0
- package/dist/cli/commands/scan-command-shared.d.ts +96 -0
- package/dist/cli/commands/scan-command-shared.js +83 -0
- package/dist/cli/commands/scan-commands.d.ts +3 -2
- package/dist/cli/commands/scan-commands.js +23 -463
- package/dist/cli/commands/scan-inventory-commands.d.ts +3 -0
- package/dist/cli/commands/scan-inventory-commands.js +96 -0
- package/dist/cli/commands/scan-project-commands.d.ts +3 -0
- package/dist/cli/commands/scan-project-commands.js +65 -0
- package/dist/cli/commands/scan-scope-commands.d.ts +3 -0
- package/dist/cli/commands/scan-scope-commands.js +149 -0
- package/dist/cli/commands/scan-scope-helpers.d.ts +8 -0
- package/dist/cli/commands/scan-scope-helpers.js +54 -0
- package/dist/cli/commands/scan-structural-commands.d.ts +3 -0
- package/dist/cli/commands/scan-structural-commands.js +113 -0
- package/dist/cli/commands/share-commands.d.ts +12 -68
- package/dist/cli/commands/share-commands.js +47 -556
- package/dist/cli/commands/share-error-hints.d.ts +14 -0
- package/dist/cli/commands/share-error-hints.js +38 -0
- package/dist/cli/commands/share-identity.d.ts +13 -0
- package/dist/cli/commands/share-identity.js +22 -0
- package/dist/cli/commands/share-record-paths.d.ts +37 -0
- package/dist/cli/commands/share-record-paths.js +68 -0
- package/dist/cli/commands/share-runners.d.ts +3 -0
- package/dist/cli/commands/share-runners.js +158 -0
- package/dist/cli/commands/shared-read-runners.d.ts +3 -0
- package/dist/cli/commands/shared-read-runners.js +51 -0
- package/dist/cli/commands/slice-check-command.d.ts +3 -0
- package/dist/cli/commands/slice-check-command.js +54 -0
- package/dist/cli/commands/slice-commands.d.ts +2 -22
- package/dist/cli/commands/slice-commands.js +18 -517
- package/dist/cli/commands/slice-decompose-artifacts.d.ts +18 -0
- package/dist/cli/commands/slice-decompose-artifacts.js +79 -0
- package/dist/cli/commands/slice-decompose-command.d.ts +3 -0
- package/dist/cli/commands/slice-decompose-command.js +123 -0
- package/dist/cli/commands/slice-list-commands.d.ts +3 -0
- package/dist/cli/commands/slice-list-commands.js +182 -0
- package/dist/cli/commands/slice-pick-command.d.ts +3 -0
- package/dist/cli/commands/slice-pick-command.js +60 -0
- package/dist/cli/commands/slice-plan-command.d.ts +24 -0
- package/dist/cli/commands/slice-plan-command.js +111 -0
- package/dist/cli/commands/workflow-commands.d.ts +10 -1
- package/dist/cli/commands/workflow-commands.js +6 -594
- package/dist/cli/commands/workflow-gate-commands.d.ts +3 -0
- package/dist/cli/commands/workflow-gate-commands.js +116 -0
- package/dist/cli/commands/workflow-plan-helpers.d.ts +48 -0
- package/dist/cli/commands/workflow-plan-helpers.js +103 -0
- package/dist/cli/commands/workflow-planning-commands.d.ts +3 -0
- package/dist/cli/commands/workflow-planning-commands.js +56 -0
- package/dist/cli/commands/workflow-swarm-actions.d.ts +7 -0
- package/dist/cli/commands/workflow-swarm-actions.js +109 -0
- package/dist/cli/commands/workflow-swarm-commands.d.ts +3 -0
- package/dist/cli/commands/workflow-swarm-commands.js +138 -0
- package/dist/cli/commands/workflow-tech-actions.d.ts +10 -0
- package/dist/cli/commands/workflow-tech-actions.js +150 -0
- package/dist/cli/commands/workspace/init-actions.d.ts +10 -0
- package/dist/cli/commands/workspace/init-actions.js +199 -0
- package/dist/cli/commands/workspace/init-codegraph-stake.d.ts +10 -0
- package/dist/cli/commands/workspace/init-codegraph-stake.js +54 -0
- package/dist/cli/commands/workspace/init-command.d.ts +8 -124
- package/dist/cli/commands/workspace/init-command.js +59 -623
- package/dist/cli/commands/workspace/init-context.d.ts +58 -0
- package/dist/cli/commands/workspace/init-context.js +121 -0
- package/dist/cli/commands/workspace/init-envelope.d.ts +3 -0
- package/dist/cli/commands/workspace/init-envelope.js +34 -0
- package/dist/cli/commands/workspace/init-failures.d.ts +3 -0
- package/dist/cli/commands/workspace/init-failures.js +84 -0
- package/dist/cli/commands/workspace/init-hooks-decision.d.ts +24 -0
- package/dist/cli/commands/workspace/init-hooks-decision.js +189 -0
- package/dist/cli/commands/workspace/init-options.d.ts +102 -0
- package/dist/cli/commands/workspace/init-options.js +1 -0
- package/dist/cli/commands/worktree-lease-commands.d.ts +10 -6
- package/dist/cli/commands/worktree-lease-commands.js +42 -512
- package/dist/cli/commands/worktree-lease-gc-command.d.ts +12 -0
- package/dist/cli/commands/worktree-lease-gc-command.js +117 -0
- package/dist/cli/commands/worktree-lease-git.d.ts +22 -0
- package/dist/cli/commands/worktree-lease-git.js +48 -0
- package/dist/cli/commands/worktree-lease-list-command.d.ts +12 -0
- package/dist/cli/commands/worktree-lease-list-command.js +100 -0
- package/dist/cli/commands/worktree-lease-load.d.ts +37 -0
- package/dist/cli/commands/worktree-lease-load.js +28 -0
- package/dist/cli/commands/worktree-lease-release-command.d.ts +13 -0
- package/dist/cli/commands/worktree-lease-release-command.js +105 -0
- package/dist/cli/commands/worktree-lease-renew-command.d.ts +11 -0
- package/dist/cli/commands/worktree-lease-renew-command.js +99 -0
- package/dist/cli/commands/worktree-lease-session.d.ts +22 -0
- package/dist/cli/commands/worktree-lease-session.js +29 -0
- package/dist/cli/commands/worktree-lease-spawn-command.d.ts +11 -0
- package/dist/cli/commands/worktree-lease-spawn-command.js +119 -0
- package/dist/cli/commands/worktree-lease-status-command.d.ts +11 -0
- package/dist/cli/commands/worktree-lease-status-command.js +86 -0
- package/dist/cli/commands/worktree-lease-store.d.ts +19 -0
- package/dist/cli/commands/worktree-lease-store.js +24 -0
- package/dist/cli/program.js +5 -0
- package/dist/services/distribution/mcp-install-runner.d.ts +21 -0
- package/dist/services/distribution/mcp-install-runner.js +59 -0
- package/dist/services/distribution/mcp-install.d.ts +95 -0
- package/dist/services/distribution/mcp-install.js +179 -0
- package/dist/services/distribution/plugin-manifest.d.ts +97 -0
- package/dist/services/distribution/plugin-manifest.js +126 -0
- package/dist/services/hooks/mcp-surface-gate.d.ts +19 -0
- package/dist/services/hooks/mcp-surface-gate.js +87 -0
- package/dist/services/ide/adapters/claude-code-adapter.d.ts +4 -35
- package/dist/services/ide/adapters/claude-code-adapter.js +48 -171
- package/dist/services/ide/adapters/claude-code-local-state.d.ts +28 -0
- package/dist/services/ide/adapters/claude-code-local-state.js +112 -0
- package/dist/services/ide/adapters/claude-code-model-window.d.ts +37 -0
- package/dist/services/ide/adapters/claude-code-model-window.js +113 -0
- package/dist/services/ide/ide-mcp-install-types.d.ts +78 -0
- package/dist/services/ide/ide-mcp-install-types.js +104 -0
- package/dist/services/ide/ide-types.d.ts +45 -1
- package/dist/services/ide/mcp-tool-matcher.d.ts +34 -0
- package/dist/services/ide/mcp-tool-matcher.js +84 -0
- package/dist/services/lint/npx-resolver.js +7 -0
- package/dist/services/mcp/cli-executor.d.ts +36 -0
- package/dist/services/mcp/cli-executor.js +97 -0
- package/dist/services/mcp/json-rpc.d.ts +81 -0
- package/dist/services/mcp/json-rpc.js +116 -0
- package/dist/services/mcp/memory-tool.d.ts +3 -0
- package/dist/services/mcp/memory-tool.js +62 -0
- package/dist/services/mcp/server-main.d.ts +1 -0
- package/dist/services/mcp/server-main.js +66 -0
- package/dist/services/mcp/server.d.ts +22 -0
- package/dist/services/mcp/server.js +152 -0
- package/dist/services/mcp/surface.d.ts +62 -0
- package/dist/services/mcp/surface.js +198 -0
- package/dist/services/mcp/tool-core.d.ts +42 -0
- package/dist/services/mcp/tool-core.js +137 -0
- package/dist/services/mcp/tools.d.ts +13 -0
- package/dist/services/mcp/tools.js +58 -0
- package/dist/services/readonly-surface/argv-guard.d.ts +32 -0
- package/dist/services/readonly-surface/argv-guard.js +164 -0
- package/dist/services/readonly-surface/readonly-whitelist.d.ts +147 -0
- package/dist/services/readonly-surface/readonly-whitelist.js +107 -0
- package/dist/services/readonly-surface/whitelist-generator.d.ts +64 -0
- package/dist/services/readonly-surface/whitelist-generator.js +212 -0
- package/dist/services/skills/hooks-codegate-superpowers.d.ts +1 -0
- package/dist/services/skills/hooks-codegate-superpowers.js +26 -0
- package/dist/services/skills/hooks-settings-service.js +10 -2
- package/package.json +17 -6
- package/scripts/install-skills-bundles.mjs +233 -0
- package/scripts/install-skills-fs.mjs +148 -0
- package/scripts/install-skills-ide.mjs +279 -0
- package/scripts/install-skills-output-style-default.mjs +196 -0
- package/scripts/install-skills-output-styles.mjs +121 -0
- package/scripts/install-skills-report.mjs +42 -0
- package/scripts/install-skills-user-config.mjs +198 -0
- package/scripts/install-skills.mjs +36 -1066
- package/scripts/watch-tree.mjs +140 -0
- package/scripts/watch.mjs +4 -126
- package/skills/peaks-status/SKILL.md +31 -13
|
@@ -1,628 +1,14 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import { runMonotonicCheck, resolveMonotonicContext } from '../../services/loop/monotonic-runner.js';
|
|
8
|
-
import { resolveLoopSpec, persistSpec, buildSpec, lintLoopSpec, lintSpecFile, specPath, MONOTONIC_TERMINATION, DEFAULT_MAX_CYCLES } from '../../services/loop/spec-service.js';
|
|
9
|
-
import { runLoop, resolveRunContext } from '../../services/loop/run-driver.js';
|
|
10
|
-
import { addJsonOption, getErrorMessage, printResult } from '../cli-helpers.js';
|
|
11
|
-
import { fail, ok } from 'peaks-loop-shared/result';
|
|
12
|
-
import { findProjectRoot } from '../../services/config/config-safety.js';
|
|
13
|
-
const VALID_EVALUATORS = new Set([
|
|
14
|
-
'karpathy',
|
|
15
|
-
'code-review',
|
|
16
|
-
'security-review',
|
|
17
|
-
'perf-baseline',
|
|
18
|
-
'verdict-aggregate',
|
|
19
|
-
'monotonic-improvement',
|
|
20
|
-
'impact-scan',
|
|
21
|
-
'smoke-run',
|
|
22
|
-
'canary-watch'
|
|
23
|
-
]);
|
|
1
|
+
import { registerWorkflowEvalWorkflowCommands } from './loop-eval-workflow-commands.js';
|
|
2
|
+
import { registerLoopEvalCommand } from './loop-eval-eval-command.js';
|
|
3
|
+
import { registerLoopCheckMonotonicCommand } from './loop-eval-monotonic-command.js';
|
|
4
|
+
import { registerLoopSpecCommands } from './loop-eval-spec-commands.js';
|
|
5
|
+
import { registerLoopSpecLintCommand } from './loop-eval-spec-lint-command.js';
|
|
6
|
+
import { registerLoopRunCommand } from './loop-eval-run-command.js';
|
|
24
7
|
export function registerWorkflowEvalCommands(program, io) {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
const workflow = existingWorkflow ??
|
|
32
|
-
program.command('workflow').description('Workflow primitive (run / plan / lint)');
|
|
33
|
-
const loop = existingLoop ?? program.command('loop').description('Loop primitive (eval)');
|
|
34
|
-
// peaks workflow run <id>
|
|
35
|
-
addJsonOption(workflow
|
|
36
|
-
.command('run')
|
|
37
|
-
.description('Slice A.3: replay a captured workflow (.peaks/workflows/<id>.yaml) deterministically. Returns the run-plan order + per-phase status without re-deriving the phase plan.')
|
|
38
|
-
.argument('<id>', 'workflow id (matches .peaks/workflows/<id>.yaml)')
|
|
39
|
-
.requiredOption('--session <sid>', 'session id (from peaks workspace init)')
|
|
40
|
-
.option('--project <path>', 'project root (default: cwd)')).action((id, options) => {
|
|
41
|
-
try {
|
|
42
|
-
const projectRoot = options.project ?? findProjectRoot(process.cwd()) ?? process.cwd();
|
|
43
|
-
const resolved = resolveWorkflow(projectRoot, id);
|
|
44
|
-
if (resolved.source.kind === 'missing') {
|
|
45
|
-
printResult(io, fail('workflow.run', 'WORKFLOW_NOT_FOUND', `workflow "${id}" not found`, { sessionId: options.session }, [`peaks workflow lint ${id} --session ${options.session} --json`]), options.json);
|
|
46
|
-
process.exitCode = 1;
|
|
47
|
-
return;
|
|
48
|
-
}
|
|
49
|
-
if (!resolved.lint.ok) {
|
|
50
|
-
printResult(io, fail('workflow.run', 'WORKFLOW_LINT_FAILED', `workflow "${id}" has ${resolved.lint.errors.length} lint error(s): ${resolved.lint.errors.join('; ')}`, resolved.lint, [`peaks workflow lint ${id} --session ${options.session} --json`]), options.json);
|
|
51
|
-
process.exitCode = 1;
|
|
52
|
-
return;
|
|
53
|
-
}
|
|
54
|
-
const runPlan = planWorkflowRun(resolved.spec);
|
|
55
|
-
printResult(io, ok('workflow.run', {
|
|
56
|
-
sessionId: options.session,
|
|
57
|
-
workflow: { id: resolved.spec.id, label: resolved.spec.label, source: resolved.source },
|
|
58
|
-
runPlan,
|
|
59
|
-
lintWarnings: resolved.lint.warnings
|
|
60
|
-
}, [], [
|
|
61
|
-
`Run \`peaks workflow plan ${id} --session ${options.session} --json\` to preview the graph.`
|
|
62
|
-
]), options.json);
|
|
63
|
-
}
|
|
64
|
-
catch (error) {
|
|
65
|
-
printResult(io, fail('workflow.run', 'WORKFLOW_RUN_FAILED', getErrorMessage(error), { sessionId: options.session }, ['Verify the workflow id and session binding.']), options.json);
|
|
66
|
-
process.exitCode = 1;
|
|
67
|
-
}
|
|
68
|
-
});
|
|
69
|
-
// peaks workflow graph <id> (dry-run graph render)
|
|
70
|
-
// Renamed from `plan` to `graph` to avoid collision with the existing
|
|
71
|
-
// `peaks workflow plan <read|refresh|detect-trigger>` family registered
|
|
72
|
-
// Reuse the existing `graph` parent if `registerWorkflowLifecycleCommand`
|
|
73
|
-
// already created one (it owns `peaks workflow graph show|list`).
|
|
74
|
-
const existingGraph = workflow.commands.find((c) => c.name() === 'graph');
|
|
75
|
-
const graphParent = existingGraph ??
|
|
76
|
-
workflow
|
|
77
|
-
.command('graph')
|
|
78
|
-
.description('workflow graph read commands (slice A.3 dry-run + slice 4.0.8 lifecycle)');
|
|
79
|
-
addJsonOption(graphParent
|
|
80
|
-
.description('Slice A.3 dry-run: render the workflow graph (phases + parallel groups + evaluators + budget). No phase is executed.')
|
|
81
|
-
.argument('<id>', 'workflow id')
|
|
82
|
-
.requiredOption('--session <sid>', 'session id')
|
|
83
|
-
.option('--project <path>', 'project root (default: cwd)')).action((id, options) => {
|
|
84
|
-
try {
|
|
85
|
-
const projectRoot = options.project ?? findProjectRoot(process.cwd()) ?? process.cwd();
|
|
86
|
-
const resolved = resolveWorkflow(projectRoot, id);
|
|
87
|
-
if (resolved.source.kind === 'missing') {
|
|
88
|
-
printResult(io, fail('workflow.graph', 'WORKFLOW_NOT_FOUND', `workflow "${id}" not found`, { sessionId: options.session }, [`Create .peaks/workflows/${id}.yaml or use the bundled default-fullauto-md.`]), options.json);
|
|
89
|
-
process.exitCode = 1;
|
|
90
|
-
return;
|
|
91
|
-
}
|
|
92
|
-
if (!resolved.lint.ok) {
|
|
93
|
-
printResult(io, fail('workflow.graph', 'WORKFLOW_LINT_FAILED', `workflow "${id}" has ${resolved.lint.errors.length} lint error(s): ${resolved.lint.errors.join('; ')}`, resolved.lint, [`peaks workflow lint ${id} --session ${options.session} --json`]), options.json);
|
|
94
|
-
process.exitCode = 1;
|
|
95
|
-
return;
|
|
96
|
-
}
|
|
97
|
-
const graph = planWorkflow(resolved.spec, resolved.source);
|
|
98
|
-
printResult(io, ok('workflow.graph', {
|
|
99
|
-
sessionId: options.session,
|
|
100
|
-
graph,
|
|
101
|
-
lintWarnings: resolved.lint.warnings
|
|
102
|
-
}, [], [
|
|
103
|
-
`Run \`peaks workflow run ${id} --session ${options.session} --json\` to materialize the run-plan order.`
|
|
104
|
-
]), options.json);
|
|
105
|
-
}
|
|
106
|
-
catch (error) {
|
|
107
|
-
printResult(io, fail('workflow.graph', 'WORKFLOW_GRAPH_FAILED', getErrorMessage(error), { sessionId: options.session }, ['Verify the workflow id and session binding.']), options.json);
|
|
108
|
-
process.exitCode = 1;
|
|
109
|
-
}
|
|
110
|
-
});
|
|
111
|
-
// peaks workflow lint <id>
|
|
112
|
-
addJsonOption(workflow
|
|
113
|
-
.command('lint')
|
|
114
|
-
.description('Slice A.3: validate a workflow spec (phases / gates / evaluators / parallel groups / budget).')
|
|
115
|
-
.argument('<id>', 'workflow id')
|
|
116
|
-
.requiredOption('--session <sid>', 'session id')
|
|
117
|
-
.option('--project <path>', 'project root (default: cwd)')).action((id, options) => {
|
|
118
|
-
try {
|
|
119
|
-
const projectRoot = options.project ?? findProjectRoot(process.cwd()) ?? process.cwd();
|
|
120
|
-
const resolved = resolveWorkflow(projectRoot, id);
|
|
121
|
-
if (resolved.source.kind === 'missing') {
|
|
122
|
-
printResult(io, fail('workflow.lint', 'WORKFLOW_NOT_FOUND', `workflow "${id}" not found`, { sessionId: options.session }, [`Create .peaks/workflows/${id}.yaml`]), options.json);
|
|
123
|
-
process.exitCode = 1;
|
|
124
|
-
return;
|
|
125
|
-
}
|
|
126
|
-
printResult(io, ok('workflow.lint', {
|
|
127
|
-
sessionId: options.session,
|
|
128
|
-
source: resolved.source,
|
|
129
|
-
lint: resolved.lint
|
|
130
|
-
}, [], []), options.json);
|
|
131
|
-
if (!resolved.lint.ok)
|
|
132
|
-
process.exitCode = 1;
|
|
133
|
-
}
|
|
134
|
-
catch (error) {
|
|
135
|
-
printResult(io, fail('workflow.lint', 'WORKFLOW_LINT_FAILED', getErrorMessage(error), { sessionId: options.session }, ['Verify the workflow file syntax.']), options.json);
|
|
136
|
-
process.exitCode = 1;
|
|
137
|
-
}
|
|
138
|
-
});
|
|
139
|
-
// peaks loop eval <rid> --evaluator <name>
|
|
140
|
-
addJsonOption(loop
|
|
141
|
-
.command('eval')
|
|
142
|
-
.description('Slice B.2: invoke a native evaluator directly. The runtime calls the evaluator (no LLM scheduling) and returns a verdict envelope compatible with peaks verdict aggregate. Pass --capture-score to also persist the score row to .peaks/_runtime/<sid>/loop/<rid>/cycles/cycle-N.json (P0 closure: this is the score the run-driver reads back).')
|
|
143
|
-
.argument('<rid>', 'request id (e.g. 2026-06-30-...)')
|
|
144
|
-
.requiredOption('--evaluator <name>', `evaluator: ${[...VALID_EVALUATORS].join(', ')}`)
|
|
145
|
-
.option('--session <sid>', 'session id (required by --evaluator monotonic-improvement; also required by --capture-score)')
|
|
146
|
-
.option('--project <path>', 'project root (default: cwd)')
|
|
147
|
-
.option('--scope <scope>', 'optional scope expression (forwarded to the evaluator)')
|
|
148
|
-
.option('--threshold <threshold>', 'optional SLA threshold (evaluator-specific)')
|
|
149
|
-
.option('--capture-score', 'persist the verdict score to .peaks/_runtime/<sid>/loop/<rid>/cycles/cycle-N.json (requires --session); adds data.score', false)).action((rid, options) => {
|
|
150
|
-
try {
|
|
151
|
-
if (!VALID_EVALUATORS.has(options.evaluator)) {
|
|
152
|
-
printResult(io, fail('loop.eval', 'UNKNOWN_EVALUATOR', `evaluator "${options.evaluator}" is not a native evaluator (allowed: ${[...VALID_EVALUATORS].join(', ')})`, { rid }, [`Use one of: ${[...VALID_EVALUATORS].join(', ')}`]), options.json);
|
|
153
|
-
process.exitCode = 1;
|
|
154
|
-
return;
|
|
155
|
-
}
|
|
156
|
-
const projectRoot = options.project ?? findProjectRoot(process.cwd()) ?? process.cwd();
|
|
157
|
-
const envelope = dispatchEvaluator(options.evaluator, {
|
|
158
|
-
projectRoot,
|
|
159
|
-
rid,
|
|
160
|
-
...(options.session !== undefined ? { sessionId: options.session } : {}),
|
|
161
|
-
...(options.scope !== undefined ? { scope: options.scope } : {}),
|
|
162
|
-
...(options.threshold !== undefined ? { threshold: options.threshold } : {})
|
|
163
|
-
});
|
|
164
|
-
const verdict = envelope.gateAction;
|
|
165
|
-
const exitCode = envelope.gateAction === 'block' ? 1 : 0;
|
|
166
|
-
// --capture-score path: persist a single-row cycle-N.json to
|
|
167
|
-
// the run-driver's cycles dir. Mirrors the scoring convention
|
|
168
|
-
// of monotonic-guard (pass=1.0, warn=0.5, block=0.0; degraded=0.25).
|
|
169
|
-
let capture = null;
|
|
170
|
-
if (options.captureScore === true) {
|
|
171
|
-
if (options.session === undefined || options.session.length === 0) {
|
|
172
|
-
printResult(io, fail('loop.eval', 'CAPTURE_SCORE_NEEDS_SESSION', '--capture-score requires --session <sid>', { rid }, ['Pass --session <sid> alongside --capture-score.']), options.json);
|
|
173
|
-
process.exitCode = 1;
|
|
174
|
-
return;
|
|
175
|
-
}
|
|
176
|
-
// Sid axis AND rid axis — the join below takes TWO ids, not one.
|
|
177
|
-
// covers "the whole `--capture-score` write path". The security audit of
|
|
178
|
-
// the `--session` guard passed and the `rid` slot escaped:
|
|
179
|
-
// `peaks loop eval '../../../../…/EVILCYC' --capture-score --session
|
|
180
|
-
// <legal>` created `<projectRoot>/../EVILCYC/cycles/cycle-1.json` under
|
|
181
|
-
// an `ok: true` envelope. The rid is the CLI positional and has no pinned
|
|
182
|
-
// format, so the segment check is the control for that axis.
|
|
183
|
-
if (isUnsafePathInput(options.session)) {
|
|
184
|
-
printResult(io, fail('loop.eval', 'INVALID_SESSION_ID', `Invalid session id: ${options.session} (must be a single path segment)`, { provided: options.session }, ['Pass a session id that is a single path segment']), options.json);
|
|
185
|
-
process.exitCode = 1;
|
|
186
|
-
return;
|
|
187
|
-
}
|
|
188
|
-
if (isUnsafePathInput(rid)) {
|
|
189
|
-
printResult(io, fail('loop.eval', 'INVALID_REQUEST_ID', `Invalid request id: ${rid} (must be a single path segment)`, { provided: rid }, ['Pass a request id that is a single path segment']), options.json);
|
|
190
|
-
process.exitCode = 1;
|
|
191
|
-
return;
|
|
192
|
-
}
|
|
193
|
-
const score = envelope.degraded
|
|
194
|
-
? 0.25
|
|
195
|
-
: envelope.gateAction === 'pass'
|
|
196
|
-
? 1.0
|
|
197
|
-
: envelope.gateAction === 'warn'
|
|
198
|
-
? 0.5
|
|
199
|
-
: 0.0;
|
|
200
|
-
const dir = join(projectRoot, '.peaks', '_runtime', options.session, 'loop', rid, 'cycles');
|
|
201
|
-
const n = nextEvalCaptureIndex(projectRoot, options.session, rid);
|
|
202
|
-
const path = join(dir, `cycle-${n}.json`);
|
|
203
|
-
try {
|
|
204
|
-
mkdirSync(dir, { recursive: true });
|
|
205
|
-
writeFileSync(path, JSON.stringify({
|
|
206
|
-
cycle: n,
|
|
207
|
-
rid,
|
|
208
|
-
sid: options.session,
|
|
209
|
-
persistedAt: new Date().toISOString(),
|
|
210
|
-
scores: [
|
|
211
|
-
{
|
|
212
|
-
evaluator: envelope.kind,
|
|
213
|
-
score,
|
|
214
|
-
gateAction: envelope.gateAction,
|
|
215
|
-
degraded: envelope.degraded,
|
|
216
|
-
observedAt: new Date().toISOString()
|
|
217
|
-
}
|
|
218
|
-
]
|
|
219
|
-
}), 'utf8');
|
|
220
|
-
capture = { score, persistedAt: path };
|
|
221
|
-
}
|
|
222
|
-
catch {
|
|
223
|
-
capture = { score, persistedAt: null };
|
|
224
|
-
}
|
|
225
|
-
}
|
|
226
|
-
printResult(io, ok('loop.eval', {
|
|
227
|
-
rid,
|
|
228
|
-
evaluator: envelope.kind,
|
|
229
|
-
verdict,
|
|
230
|
-
passed: envelope.passed,
|
|
231
|
-
violations: envelope.violations,
|
|
232
|
-
summary: envelope.summary,
|
|
233
|
-
wallSeconds: envelope.wallSeconds,
|
|
234
|
-
degraded: envelope.degraded,
|
|
235
|
-
...(capture !== null ? { score: capture.score, capture } : {})
|
|
236
|
-
}, [], envelope.degraded
|
|
237
|
-
? [
|
|
238
|
-
'Evaluator ran in degraded mode (peaks CLI unavailable). Verify the verdict by running `peaks verdict aggregate --from-rid ' +
|
|
239
|
-
rid +
|
|
240
|
-
'`.',
|
|
241
|
-
'Re-run on a fully-installed peaks-loop environment for a real verdict.'
|
|
242
|
-
]
|
|
243
|
-
: [
|
|
244
|
-
`Verifier verdict-aggregate can consume this envelope via \`peaks verdict aggregate --from-rid ${rid}\`.`
|
|
245
|
-
]), options.json);
|
|
246
|
-
process.exitCode = exitCode;
|
|
247
|
-
}
|
|
248
|
-
catch (error) {
|
|
249
|
-
printResult(io, fail('loop.eval', 'LOOP_EVAL_FAILED', getErrorMessage(error), { rid, evaluator: options.evaluator }, ['Verify the rid and evaluator name.']), options.json);
|
|
250
|
-
process.exitCode = 1;
|
|
251
|
-
}
|
|
252
|
-
});
|
|
253
|
-
// peaks loop check-monotonic <rid>
|
|
254
|
-
// Slice C: compare adjacent cycles' per-evaluator scores; reject
|
|
255
|
-
// score regression > threshold with `MONOTONICITY_VIOLATION`.
|
|
256
|
-
addJsonOption(loop
|
|
257
|
-
.command('check-monotonic')
|
|
258
|
-
.description('Slice C.2: compare adjacent cycles of evaluator scores for a rid. Reject (exit 1) when an evaluator score regresses beyond the configured threshold.')
|
|
259
|
-
.argument('<rid>', 'request id (e.g. 2026-06-30-...)')
|
|
260
|
-
.requiredOption('--session <sid>', 'session id')
|
|
261
|
-
.option('--project <path>', 'project root (default: cwd)')
|
|
262
|
-
.option('--threshold <threshold>', `maximum allowed score regression on the 0..1 scale (default: ${DEFAULT_MONOTONIC_THRESHOLD} = 5%)`)
|
|
263
|
-
.option('--no-persist', 'skip persisting the current cycle score rows to disk')).action((rid, options) => {
|
|
264
|
-
try {
|
|
265
|
-
const { projectRoot, sid } = resolveMonotonicContext({
|
|
266
|
-
...(options.project !== undefined ? { project: options.project } : {}),
|
|
267
|
-
session: options.session,
|
|
268
|
-
rid
|
|
269
|
-
});
|
|
270
|
-
const persist = options.persist !== false;
|
|
271
|
-
const thresholdNum = options.threshold !== undefined ? Number(options.threshold) : DEFAULT_MONOTONIC_THRESHOLD;
|
|
272
|
-
if (options.threshold !== undefined &&
|
|
273
|
-
(!Number.isFinite(thresholdNum) || thresholdNum < 0 || thresholdNum > 1)) {
|
|
274
|
-
printResult(io, fail('loop.check-monotonic', 'INVALID_THRESHOLD', `threshold must be a finite number in [0,1] (got "${options.threshold}")`, { rid }, [`Pass --threshold ${DEFAULT_MONOTONIC_THRESHOLD} (default) or any number in [0,1].`]), options.json);
|
|
275
|
-
process.exitCode = 1;
|
|
276
|
-
return;
|
|
277
|
-
}
|
|
278
|
-
const result = runMonotonicCheck({
|
|
279
|
-
projectRoot,
|
|
280
|
-
sid,
|
|
281
|
-
rid,
|
|
282
|
-
threshold: thresholdNum,
|
|
283
|
-
persist
|
|
284
|
-
});
|
|
285
|
-
const exitCode = result.report.monotonicityViolation ? 1 : 0;
|
|
286
|
-
printResult(io, ok('loop.check-monotonic', {
|
|
287
|
-
rid,
|
|
288
|
-
sessionId: sid,
|
|
289
|
-
projectRoot,
|
|
290
|
-
currentCycle: result.currentCycle,
|
|
291
|
-
previousCycle: result.previousCycle,
|
|
292
|
-
persistedAt: result.persistedAt,
|
|
293
|
-
rows: result.rows,
|
|
294
|
-
threshold: result.report.threshold,
|
|
295
|
-
status: result.report.status,
|
|
296
|
-
code: result.report.code,
|
|
297
|
-
monotonicityViolation: result.report.monotonicityViolation,
|
|
298
|
-
regressions: result.report.regressions,
|
|
299
|
-
reason: result.report.reason
|
|
300
|
-
}, [], result.report.monotonicityViolation
|
|
301
|
-
? [
|
|
302
|
-
'Investigate which evaluator regressed most and the previous cycle threshold.',
|
|
303
|
-
`Inspect ${result.persistedAt ?? 'the persisted cycle row at .peaks/_runtime/<sid>/loop/<rid>/cycle-N.json'}.`
|
|
304
|
-
]
|
|
305
|
-
: result.report.status === 'skip'
|
|
306
|
-
? [
|
|
307
|
-
'Cycle is the first run or incomparable; monotonicity guard is a no-op.',
|
|
308
|
-
'A future cycle will surface a violation if any evaluator regresses.'
|
|
309
|
-
]
|
|
310
|
-
: [
|
|
311
|
-
'All evaluators held or improved.',
|
|
312
|
-
`Verifier verdict-aggregate can consume this envelope via \`peaks verdict aggregate --from-rid ${rid}\`.`
|
|
313
|
-
]), options.json);
|
|
314
|
-
process.exitCode = exitCode;
|
|
315
|
-
}
|
|
316
|
-
catch (error) {
|
|
317
|
-
printResult(io, fail('loop.check-monotonic', 'LOOP_CHECK_MONOTONIC_FAILED', getErrorMessage(error), { rid }, ['Verify the rid and session binding.']), options.json);
|
|
318
|
-
process.exitCode = 1;
|
|
319
|
-
}
|
|
320
|
-
});
|
|
321
|
-
// peaks loop spec <rid> — Slice E.2: read or bootstrap the
|
|
322
|
-
// project-level `.peaks/_runtime/<sid>/loop/<rid>/spec.yaml`. When
|
|
323
|
-
// `--bootstrap` is set, a default spec is written; otherwise the
|
|
324
|
-
// existing spec is read (or `{kind:'missing'}` is returned).
|
|
325
|
-
const spec = loop
|
|
326
|
-
.command('spec')
|
|
327
|
-
.description('Slice E.2: read or bootstrap the spec for a rid. Defaults to read; pass --bootstrap to write a default spec.');
|
|
328
|
-
addJsonOption(spec
|
|
329
|
-
.command('show')
|
|
330
|
-
.description('Slice E.2: print the resolved spec for a rid.')
|
|
331
|
-
.argument('<rid>', 'request id')
|
|
332
|
-
.requiredOption('--session <sid>', 'session id')
|
|
333
|
-
.option('--project <path>', 'project root (default: cwd)')).action((rid, options) => {
|
|
334
|
-
try {
|
|
335
|
-
const projectRoot = options.project ?? findProjectRoot(process.cwd()) ?? process.cwd();
|
|
336
|
-
const resolved = resolveLoopSpec(projectRoot, options.session, rid);
|
|
337
|
-
if (resolved.spec === null) {
|
|
338
|
-
printResult(io, fail('loop.spec.show', 'SPEC_NOT_FOUND', `no spec.yaml at ${resolved.origin && resolved.origin.kind === 'missing' ? `.peaks/_runtime/${options.session}/loop/${rid}/spec.yaml` : 'unknown'}`, { rid, sessionId: options.session }, [
|
|
339
|
-
`Create one via \`peaks loop spec bootstrap ${rid} --session ${options.session} --project ${projectRoot}\`.`
|
|
340
|
-
]), options.json);
|
|
341
|
-
process.exitCode = 1;
|
|
342
|
-
return;
|
|
343
|
-
}
|
|
344
|
-
const report = lintLoopSpec(resolved.spec);
|
|
345
|
-
printResult(io, ok('loop.spec.show', {
|
|
346
|
-
rid,
|
|
347
|
-
sessionId: options.session,
|
|
348
|
-
projectRoot,
|
|
349
|
-
origin: resolved.origin,
|
|
350
|
-
spec: resolved.spec,
|
|
351
|
-
lint: { ok: report.ok, errors: report.errors, warnings: report.warnings }
|
|
352
|
-
}, [], [
|
|
353
|
-
`Edit \`${typeof resolved.origin === 'object' && resolved.origin.kind === 'project' ? resolved.origin.path : '<spec>'}\` and re-run \`peaks loop spec lint <file>\`.`
|
|
354
|
-
]), options.json);
|
|
355
|
-
if (!report.ok)
|
|
356
|
-
process.exitCode = 1;
|
|
357
|
-
}
|
|
358
|
-
catch (error) {
|
|
359
|
-
printResult(io, fail('loop.spec.show', 'LOOP_SPEC_SHOW_FAILED', getErrorMessage(error), { rid }, [
|
|
360
|
-
'Verify the rid and session binding.'
|
|
361
|
-
]), options.json);
|
|
362
|
-
process.exitCode = 1;
|
|
363
|
-
}
|
|
364
|
-
});
|
|
365
|
-
addJsonOption(spec
|
|
366
|
-
.command('bootstrap')
|
|
367
|
-
.description('Slice E.2: write a default spec.yaml for a rid at `.peaks/_runtime/<sid>/loop/<rid>/spec.yaml`. Refuses to overwrite an existing spec without --force (P1 from dogfood audit: bootstrap is destructive on re-run).')
|
|
368
|
-
.argument('<rid>', 'request id')
|
|
369
|
-
.requiredOption('--session <sid>', 'session id')
|
|
370
|
-
.option('--project <path>', 'project root (default: cwd)')
|
|
371
|
-
.option('--strategy <strategy>', `termination strategy (manual|max-cycles|${MONOTONIC_TERMINATION})`, MONOTONIC_TERMINATION)
|
|
372
|
-
.option('--max-cycles <n>', `max-cycles (only when strategy=max-cycles; default ${DEFAULT_MAX_CYCLES})`, String(DEFAULT_MAX_CYCLES))
|
|
373
|
-
.option('--force', 'overwrite an existing spec.yaml (without --force, bootstrap refuses with SPEC_EXISTS_NEEDS_FORCE)', false)).action((rid, options) => {
|
|
374
|
-
try {
|
|
375
|
-
const projectRoot = options.project ?? findProjectRoot(process.cwd()) ?? process.cwd();
|
|
376
|
-
const path = specPath(projectRoot, options.session, rid);
|
|
377
|
-
// P1二次保护:spec.yaml 已存在且未带 --force → SPEC_EXISTS_NEEDS_FORCE
|
|
378
|
-
if (existsSync(path) && options.force !== true) {
|
|
379
|
-
printResult(io, fail('loop.spec.bootstrap', 'SPEC_EXISTS_NEEDS_FORCE', `spec.yaml already exists at ${path}; re-run with --force to overwrite`, { rid, sessionId: options.session, path }, [
|
|
380
|
-
`Re-run with \`peaks loop spec bootstrap ${rid} --session ${options.session} --force\` to overwrite.`,
|
|
381
|
-
`Or edit \`${path}\` directly.`
|
|
382
|
-
]), options.json);
|
|
383
|
-
process.exitCode = 1;
|
|
384
|
-
return;
|
|
385
|
-
}
|
|
386
|
-
const strategyRaw = options.strategy;
|
|
387
|
-
const strategy = strategyRaw === 'max-cycles' ||
|
|
388
|
-
strategyRaw === MONOTONIC_TERMINATION ||
|
|
389
|
-
strategyRaw === 'manual'
|
|
390
|
-
? strategyRaw
|
|
391
|
-
: MONOTONIC_TERMINATION;
|
|
392
|
-
const termination = strategy === 'max-cycles'
|
|
393
|
-
? {
|
|
394
|
-
strategy,
|
|
395
|
-
maxCycles: Math.max(1, Math.floor(Number(options.maxCycles) || DEFAULT_MAX_CYCLES))
|
|
396
|
-
}
|
|
397
|
-
: { strategy };
|
|
398
|
-
const evaluators = [
|
|
399
|
-
{ kind: 'karpathy', gate: 'Gate B3', scope: 'src/' },
|
|
400
|
-
{ kind: 'code-review', gate: 'Gate B3', scope: 'src/' },
|
|
401
|
-
{ kind: 'security-review', gate: 'Gate B4', scope: 'src/' },
|
|
402
|
-
{ kind: 'perf-baseline', gate: 'Gate B4', scope: 'src/' },
|
|
403
|
-
{ kind: 'monotonic-improvement', gate: 'Gate D1' }
|
|
404
|
-
];
|
|
405
|
-
const sla = [
|
|
406
|
-
{ evaluator: 'karpathy', maxScore: 0.7 },
|
|
407
|
-
{ evaluator: 'code-review', maxScore: 0.7 },
|
|
408
|
-
{ evaluator: 'security-review', maxScore: 0.7 },
|
|
409
|
-
{ evaluator: 'perf-baseline', maxScore: 0.7 },
|
|
410
|
-
{ evaluator: 'monotonic-improvement', maxScore: 0.5 }
|
|
411
|
-
];
|
|
412
|
-
const specObj = buildSpec({ rid, evaluators, sla, termination }, rid);
|
|
413
|
-
const report = lintLoopSpec(specObj);
|
|
414
|
-
if (!report.ok) {
|
|
415
|
-
printResult(io, fail('loop.spec.bootstrap', 'SPEC_LINT_FAILED', report.errors.join('; '), { rid, report }, ['Verify the strategy flag.']), options.json);
|
|
416
|
-
process.exitCode = 1;
|
|
417
|
-
return;
|
|
418
|
-
}
|
|
419
|
-
const writtenPath = persistSpec(projectRoot, options.session, specObj);
|
|
420
|
-
printResult(io, ok('loop.spec.bootstrap', {
|
|
421
|
-
rid,
|
|
422
|
-
sessionId: options.session,
|
|
423
|
-
projectRoot,
|
|
424
|
-
path: writtenPath,
|
|
425
|
-
spec: specObj,
|
|
426
|
-
lint: { ok: report.ok, errors: report.errors, warnings: report.warnings },
|
|
427
|
-
overwritten: options.force === true && existsSync(writtenPath)
|
|
428
|
-
}, [], [
|
|
429
|
-
`Run \`peaks loop spec lint ${writtenPath}\` to re-validate.`,
|
|
430
|
-
`Then run \`peaks loop run ${rid} --session ${options.session}\` to execute the loop.`
|
|
431
|
-
]), options.json);
|
|
432
|
-
}
|
|
433
|
-
catch (error) {
|
|
434
|
-
printResult(io, fail('loop.spec.bootstrap', 'LOOP_SPEC_BOOTSTRAP_FAILED', getErrorMessage(error), { rid }, ['Verify the rid, session, and strategy flag.']), options.json);
|
|
435
|
-
process.exitCode = 1;
|
|
436
|
-
}
|
|
437
|
-
});
|
|
438
|
-
addJsonOption(spec
|
|
439
|
-
.command('lint')
|
|
440
|
-
.description('Slice E.2: schema-validate a spec.yaml file (path required).')
|
|
441
|
-
.argument('<file>', 'path to spec.yaml')
|
|
442
|
-
.option('--rid <rid>', 'override the rid embedded in the spec (default: inferred from path)')).action((file, options) => {
|
|
443
|
-
try {
|
|
444
|
-
const result = lintSpecFile(file, options.rid);
|
|
445
|
-
if (result.spec === null) {
|
|
446
|
-
printResult(io, fail('loop.spec.lint', 'SPEC_LINT_FAILED', result.report.errors.join('; '), { file, raw: result.raw.slice(0, 200) }, ['Verify the file path and YAML syntax.']), options.json);
|
|
447
|
-
process.exitCode = 1;
|
|
448
|
-
return;
|
|
449
|
-
}
|
|
450
|
-
printResult(io, ok('loop.spec.lint', {
|
|
451
|
-
file,
|
|
452
|
-
spec: result.spec,
|
|
453
|
-
lint: {
|
|
454
|
-
ok: result.report.ok,
|
|
455
|
-
errors: result.report.errors,
|
|
456
|
-
warnings: result.report.warnings
|
|
457
|
-
}
|
|
458
|
-
}, [], []), options.json);
|
|
459
|
-
if (!result.report.ok)
|
|
460
|
-
process.exitCode = 1;
|
|
461
|
-
}
|
|
462
|
-
catch (error) {
|
|
463
|
-
printResult(io, fail('loop.spec.lint', 'LOOP_SPEC_LINT_FAILED', getErrorMessage(error), { file }, [
|
|
464
|
-
'Verify the file path and rid flag.'
|
|
465
|
-
]), options.json);
|
|
466
|
-
process.exitCode = 1;
|
|
467
|
-
}
|
|
468
|
-
});
|
|
469
|
-
// peaks loop run <rid> — Slice F.1 (P0 closure): the closed-loop
|
|
470
|
-
// driver that actually consumes `termination.strategy` (previously
|
|
471
|
-
// declared-and-validated but never consumed — dogfood audit #2).
|
|
472
|
-
addJsonOption(loop
|
|
473
|
-
.command('run')
|
|
474
|
-
.description(`Slice F.1: drive the closed loop for <rid> per .peaks/_runtime/<sid>/loop/<rid>/spec.yaml. Consumes termination.strategy (${MONOTONIC_TERMINATION} | max-cycles | manual); aborts on MONOTONICITY_VIOLATION.`)
|
|
475
|
-
.argument('<rid>', 'request id (e.g. 2026-06-30-...)')
|
|
476
|
-
.requiredOption('--session <sid>', 'session id')
|
|
477
|
-
.option('--project <path>', 'project root (default: cwd)')
|
|
478
|
-
.option('--strategy <strategy>', `override termination.strategy (allowed: ${MONOTONIC_TERMINATION}, max-cycles, manual)`)
|
|
479
|
-
.option('--max-cycles <n>', `override termination.maxCycles (default ${DEFAULT_MAX_CYCLES})`)
|
|
480
|
-
.option('--threshold <t>', 'monotonic threshold (0..1); default 0.05')
|
|
481
|
-
.option('--no-persist', 'skip writing cycle-N.json + summary to disk')).action((rid, options) => {
|
|
482
|
-
try {
|
|
483
|
-
const { projectRoot, sid, rid: r2 } = resolveRunContext({
|
|
484
|
-
...(options.project !== undefined ? { project: options.project } : {}),
|
|
485
|
-
session: options.session,
|
|
486
|
-
rid
|
|
487
|
-
});
|
|
488
|
-
const threshold = options.threshold !== undefined ? Number(options.threshold) : undefined;
|
|
489
|
-
if (threshold !== undefined &&
|
|
490
|
-
(!Number.isFinite(threshold) || threshold < 0 || threshold > 1)) {
|
|
491
|
-
printResult(io, fail('loop.run', 'INVALID_THRESHOLD', `threshold must be finite in [0,1] (got "${options.threshold}")`, { rid: r2 }, [`Pass --threshold 0.05 (default) or any number in [0,1].`]), options.json);
|
|
492
|
-
process.exitCode = 1;
|
|
493
|
-
return;
|
|
494
|
-
}
|
|
495
|
-
const maxCycles = options.maxCycles !== undefined
|
|
496
|
-
? Math.max(1, Math.floor(Number(options.maxCycles) || DEFAULT_MAX_CYCLES))
|
|
497
|
-
: undefined;
|
|
498
|
-
const strategyOverride = options.strategy;
|
|
499
|
-
const result = runLoop({
|
|
500
|
-
projectRoot,
|
|
501
|
-
sid,
|
|
502
|
-
rid: r2,
|
|
503
|
-
...(threshold !== undefined ? { threshold } : {}),
|
|
504
|
-
...(maxCycles !== undefined ? { maxCyclesOverride: maxCycles } : {}),
|
|
505
|
-
...(strategyOverride !== undefined ? { strategyOverride } : {}),
|
|
506
|
-
persist: options.persist !== false
|
|
507
|
-
});
|
|
508
|
-
const exitCode = mapRunDriverCodeToExit(result.code);
|
|
509
|
-
const finalReportCode = result.finalReport?.code ?? null;
|
|
510
|
-
printResult(io, result.ok
|
|
511
|
-
? ok('loop.run', {
|
|
512
|
-
rid: r2,
|
|
513
|
-
sessionId: sid,
|
|
514
|
-
projectRoot,
|
|
515
|
-
code: result.code,
|
|
516
|
-
strategy: result.strategy,
|
|
517
|
-
maxCycles: result.maxCycles,
|
|
518
|
-
cycles: result.cycles.map((c) => ({
|
|
519
|
-
cycle: c.cycle,
|
|
520
|
-
rows: c.rows,
|
|
521
|
-
persistedAt: c.persistedAt,
|
|
522
|
-
monotonicReport: c.monotonicReport
|
|
523
|
-
})),
|
|
524
|
-
finalReport: result.finalReport,
|
|
525
|
-
finalReportCode,
|
|
526
|
-
summary: result.summary
|
|
527
|
-
}, [], result.code === 'RUN_OK'
|
|
528
|
-
? [
|
|
529
|
-
`Loop completed ${result.summary.totalCycles} cycle(s) with ${result.summary.regressionCount} regression(s).`
|
|
530
|
-
]
|
|
531
|
-
: [`Run driver exited with ${result.code}: ${result.message}`])
|
|
532
|
-
: fail('loop.run', result.code, result.message, {
|
|
533
|
-
rid: r2,
|
|
534
|
-
sessionId: sid,
|
|
535
|
-
projectRoot,
|
|
536
|
-
code: result.code,
|
|
537
|
-
strategy: result.strategy,
|
|
538
|
-
maxCycles: result.maxCycles,
|
|
539
|
-
cycles: result.cycles,
|
|
540
|
-
finalReport: result.finalReport,
|
|
541
|
-
finalReportCode,
|
|
542
|
-
summary: result.summary
|
|
543
|
-
}, nextActionsForCode(result.code, r2, sid, result.summary)), options.json);
|
|
544
|
-
process.exitCode = exitCode;
|
|
545
|
-
}
|
|
546
|
-
catch (error) {
|
|
547
|
-
printResult(io, fail('loop.run', 'RUN_FAILED', getErrorMessage(error), { rid }, [
|
|
548
|
-
'Verify the rid and session binding.'
|
|
549
|
-
]), options.json);
|
|
550
|
-
process.exitCode = 1;
|
|
551
|
-
}
|
|
552
|
-
});
|
|
553
|
-
}
|
|
554
|
-
/** Test seam: pick the next cycle-N.json number for capture-score
|
|
555
|
-
* writes. The run-driver also writes to the same dir; the next
|
|
556
|
-
* index = max(prior)+1, or 1. */
|
|
557
|
-
function nextEvalCaptureIndex(projectRoot, sid, rid) {
|
|
558
|
-
// BOTH id axes: this function is the join for the capture-score cycle dir.
|
|
559
|
-
// The action guards `options.session` and `rid` before calling in, but the
|
|
560
|
-
// seam is callable on its own, so the join states its own contract. The rid
|
|
561
|
-
// half was missing until 2026-09-14 (repair R1) — the same second-slot hole
|
|
562
|
-
// the action had, in the same file, one function below it.
|
|
563
|
-
if (isUnsafePathInput(sid)) {
|
|
564
|
-
throw new Error(`Invalid session id: ${sid} (must be a single path segment)`);
|
|
565
|
-
}
|
|
566
|
-
if (isUnsafePathInput(rid)) {
|
|
567
|
-
throw new Error(`Invalid request id: ${rid} (must be a single path segment)`);
|
|
568
|
-
}
|
|
569
|
-
const dir = join(projectRoot, '.peaks', '_runtime', sid, 'loop', rid, 'cycles');
|
|
570
|
-
if (!existsSync(dir))
|
|
571
|
-
return 1;
|
|
572
|
-
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
573
|
-
const fs = require('node:fs');
|
|
574
|
-
let entries;
|
|
575
|
-
try {
|
|
576
|
-
entries = fs.readdirSync(dir);
|
|
577
|
-
}
|
|
578
|
-
catch {
|
|
579
|
-
return 1;
|
|
580
|
-
}
|
|
581
|
-
let best = null;
|
|
582
|
-
for (const entry of entries) {
|
|
583
|
-
const m = entry.match(/^cycle-(\d+)\.json$/);
|
|
584
|
-
if (m === null)
|
|
585
|
-
continue;
|
|
586
|
-
const n = parseInt(m[1] ?? '0', 10);
|
|
587
|
-
if (best === null || n > best)
|
|
588
|
-
best = n;
|
|
589
|
-
}
|
|
590
|
-
return (best ?? 0) + 1;
|
|
591
|
-
}
|
|
592
|
-
/** Map a run-driver code to a process exit code. */
|
|
593
|
-
function mapRunDriverCodeToExit(code) {
|
|
594
|
-
if (code === 'RUN_OK' || code === 'RUN_OK_REGRESSION')
|
|
595
|
-
return 0;
|
|
596
|
-
return 1;
|
|
597
|
-
}
|
|
598
|
-
function nextActionsForCode(code, rid, sid, summary) {
|
|
599
|
-
switch (code) {
|
|
600
|
-
case 'SPEC_NOT_FOUND':
|
|
601
|
-
return [`Create a spec with \`peaks loop spec bootstrap ${rid} --session ${sid}\`.`];
|
|
602
|
-
case 'SPEC_INVALID':
|
|
603
|
-
return [`Re-run \`peaks loop spec lint <path>\` to see the schema errors.`];
|
|
604
|
-
case 'UNKNOWN_TERMINATION_STRATEGY':
|
|
605
|
-
return [
|
|
606
|
-
`Edit the spec.yaml and set termination.strategy to one of: ${MONOTONIC_TERMINATION}, max-cycles, manual.`
|
|
607
|
-
];
|
|
608
|
-
case 'MONOTONICITY_VIOLATION':
|
|
609
|
-
return [
|
|
610
|
-
'Inspect the regression rows in the cycles output and the previous cycle row at .peaks/_runtime/' +
|
|
611
|
-
sid +
|
|
612
|
-
'/loop/' +
|
|
613
|
-
rid +
|
|
614
|
-
'/cycles/.',
|
|
615
|
-
'Fix the regression in the next iteration and re-run.'
|
|
616
|
-
];
|
|
617
|
-
case 'LOCKED':
|
|
618
|
-
return ['Wait for the in-flight loop run to complete, or pick a different rid.'];
|
|
619
|
-
case 'RUN_OK_REGRESSION':
|
|
620
|
-
return ['A regression was observed; the run captured it for operator review.'];
|
|
621
|
-
case 'RUN_OK':
|
|
622
|
-
return [
|
|
623
|
-
`Loop completed ${summary.totalCycles} cycle(s); ${summary.regressionCount} regression(s).`
|
|
624
|
-
];
|
|
625
|
-
default:
|
|
626
|
-
return ['Inspect the run-driver output above.'];
|
|
627
|
-
}
|
|
8
|
+
registerWorkflowEvalWorkflowCommands(program, io);
|
|
9
|
+
registerLoopEvalCommand(program, io);
|
|
10
|
+
registerLoopCheckMonotonicCommand(program, io);
|
|
11
|
+
registerLoopSpecCommands(program, io);
|
|
12
|
+
registerLoopSpecLintCommand(program, io);
|
|
13
|
+
registerLoopRunCommand(program, io);
|
|
628
14
|
}
|