session-orchestrator 3.16.0
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 +29 -0
- package/.claude-plugin/plugin.json +18 -0
- package/.codex-plugin/agents/explorer.toml +14 -0
- package/.codex-plugin/agents/session-reviewer.toml +23 -0
- package/.codex-plugin/agents/wave-worker.toml +15 -0
- package/.codex-plugin/config.toml +20 -0
- package/.codex-plugin/plugin.json +37 -0
- package/.cursor/rules/000-session-orchestrator.mdc +73 -0
- package/.cursor/rules/010-session-workflow.mdc +170 -0
- package/.cursor/rules/020-quality-gates.mdc +128 -0
- package/.cursor/rules/030-wave-execution.mdc +216 -0
- package/.cursor/rules/040-discovery.mdc +242 -0
- package/.cursor/rules/050-plan.mdc +235 -0
- package/.cursor/rules/060-evolve.mdc +232 -0
- package/.cursor/rules/070-gitlab-ops.mdc +246 -0
- package/.cursor/rules/080-ecosystem-health.mdc +145 -0
- package/.mcp.json +8 -0
- package/CHANGELOG.md +1544 -0
- package/LICENSE +21 -0
- package/NOTICE +64 -0
- package/README.md +242 -0
- package/SECURITY.md +90 -0
- package/agents/AGENTS.md +136 -0
- package/agents/analyst.md +99 -0
- package/agents/architect-reviewer.md +93 -0
- package/agents/code-implementer.md +106 -0
- package/agents/db-specialist.md +104 -0
- package/agents/dialectic-deriver.md +139 -0
- package/agents/docs-writer.md +113 -0
- package/agents/eval-judge.md +146 -0
- package/agents/memory-proposal-collector.md +297 -0
- package/agents/qa-strategist.md +102 -0
- package/agents/schemas/analyst.schema.json +46 -0
- package/agents/schemas/architect-reviewer.schema.json +50 -0
- package/agents/schemas/code-implementer.schema.json +61 -0
- package/agents/schemas/db-specialist.schema.json +80 -0
- package/agents/schemas/docs-writer.schema.json +56 -0
- package/agents/schemas/persona-panel-sidecar.schema.json +245 -0
- package/agents/schemas/qa-strategist.schema.json +46 -0
- package/agents/schemas/security-reviewer.schema.json +86 -0
- package/agents/schemas/session-reviewer.schema.json +69 -0
- package/agents/schemas/test-writer.schema.json +69 -0
- package/agents/schemas/ui-developer.schema.json +90 -0
- package/agents/schemas/ux-evaluator.schema.json +51 -0
- package/agents/security-reviewer.md +236 -0
- package/agents/session-reviewer.md +201 -0
- package/agents/skill-applied-judge.md +122 -0
- package/agents/test-writer.md +123 -0
- package/agents/ui-developer.md +109 -0
- package/agents/ux-evaluator.md +161 -0
- package/assets/icon.svg +11 -0
- package/assets/og-card.png +0 -0
- package/assets/og-card.svg +47 -0
- package/commands/autopilot-multi.md +74 -0
- package/commands/autopilot.md +80 -0
- package/commands/bootstrap.md +56 -0
- package/commands/brainstorm.md +48 -0
- package/commands/close.md +24 -0
- package/commands/debug.md +36 -0
- package/commands/discovery.md +32 -0
- package/commands/dispatcher.md +59 -0
- package/commands/eval.md +28 -0
- package/commands/evolve.md +10 -0
- package/commands/go.md +41 -0
- package/commands/grill.md +45 -0
- package/commands/harness-audit.md +26 -0
- package/commands/memory-cleanup.md +25 -0
- package/commands/persona-panel.md +121 -0
- package/commands/plan.md +15 -0
- package/commands/portfolio.md +97 -0
- package/commands/reconcile.md +23 -0
- package/commands/repo-audit.md +24 -0
- package/commands/session.md +30 -0
- package/commands/spinout.md +15 -0
- package/commands/sunset-review.md +27 -0
- package/commands/templates-ack.md +96 -0
- package/commands/test.md +97 -0
- package/docs/README.md +105 -0
- package/docs/USER-GUIDE.md +1403 -0
- package/docs/ci-setup.md +81 -0
- package/docs/codex-setup.md +142 -0
- package/docs/components.md +74 -0
- package/docs/cursor-setup.md +104 -0
- package/docs/events-schema.md +81 -0
- package/docs/migration-v3.md +148 -0
- package/docs/owner-config-schema.md +154 -0
- package/docs/persona-panel.md +433 -0
- package/docs/pi-setup.md +115 -0
- package/docs/plugin-architecture-v3.md +296 -0
- package/docs/pm-skills-marketplace.md +114 -0
- package/docs/policy-cache-validation-2026-04-28.md +118 -0
- package/docs/rule-authoring.md +316 -0
- package/docs/session-config-reference.md +1439 -0
- package/docs/session-config-template.md +961 -0
- package/docs/vault-docs-architecture.md +297 -0
- package/hooks/_lib/lock-bootstrap.mjs +272 -0
- package/hooks/_lib/lock-reconcile.mjs +93 -0
- package/hooks/_lib/profile-gate.mjs +95 -0
- package/hooks/_lib/transcript-history.mjs +211 -0
- package/hooks/agent-teams-h3-test.sh +362 -0
- package/hooks/config-protection.mjs +0 -0
- package/hooks/cwd-change-restore.mjs +131 -0
- package/hooks/enforce-commands.mjs +179 -0
- package/hooks/enforce-scope.mjs +273 -0
- package/hooks/hooks-codex.json +60 -0
- package/hooks/hooks-cursor.json +15 -0
- package/hooks/hooks-pi.json +115 -0
- package/hooks/hooks.json +215 -0
- package/hooks/loop-guard.mjs +260 -0
- package/hooks/on-session-end.mjs +217 -0
- package/hooks/on-session-start.mjs +660 -0
- package/hooks/on-stop.mjs +294 -0
- package/hooks/operator-steer.mjs +64 -0
- package/hooks/post-edit-validate.mjs +225 -0
- package/hooks/post-subagent-discovery-validator.mjs +398 -0
- package/hooks/post-tool-batch-wave-signal.mjs +328 -0
- package/hooks/post-tool-failure-corrective-context.mjs +248 -0
- package/hooks/post-tooluse-frontend-slop.mjs +184 -0
- package/hooks/pre-bash-destructive-guard.mjs +515 -0
- package/hooks/pre-bash-memory-propose-audit.mjs +206 -0
- package/hooks/pre-bash-staging-fence.mjs +223 -0
- package/hooks/pre-bash-templates-first.mjs +404 -0
- package/hooks/run-node.sh +72 -0
- package/hooks/skill-invocation-telemetry.mjs +99 -0
- package/hooks/subagent-telemetry.mjs +249 -0
- package/hooks/wave-scope-commit-guard.mjs +191 -0
- package/monitors/monitors.json +14 -0
- package/output-styles/finding-report.md +48 -0
- package/output-styles/session-report.md +53 -0
- package/output-styles/wave-summary.md +38 -0
- package/package.json +94 -0
- package/pi/extensions/session-orchestrator.ts +25 -0
- package/pi/prompts/autopilot-multi.md +12 -0
- package/pi/prompts/autopilot.md +12 -0
- package/pi/prompts/bootstrap.md +12 -0
- package/pi/prompts/brainstorm.md +12 -0
- package/pi/prompts/close.md +11 -0
- package/pi/prompts/debug.md +12 -0
- package/pi/prompts/discovery.md +12 -0
- package/pi/prompts/dispatcher.md +12 -0
- package/pi/prompts/eval.md +12 -0
- package/pi/prompts/evolve.md +12 -0
- package/pi/prompts/go.md +12 -0
- package/pi/prompts/grill.md +12 -0
- package/pi/prompts/harness-audit.md +12 -0
- package/pi/prompts/memory-cleanup.md +12 -0
- package/pi/prompts/persona-panel.md +12 -0
- package/pi/prompts/plan.md +12 -0
- package/pi/prompts/portfolio.md +12 -0
- package/pi/prompts/reconcile.md +12 -0
- package/pi/prompts/repo-audit.md +12 -0
- package/pi/prompts/session.md +12 -0
- package/pi/prompts/spinout.md +12 -0
- package/pi/prompts/sunset-review.md +12 -0
- package/pi/prompts/templates-ack.md +12 -0
- package/pi/prompts/test.md +12 -0
- package/rules/_index.md +51 -0
- package/rules/always-on/commit-discipline.md +26 -0
- package/rules/always-on/npm-quality-gates.md +26 -0
- package/rules/always-on/parallel-sessions.md +43 -0
- package/rules/opt-in-domain/prompt-caching.md +270 -0
- package/rules/opt-in-stack/backend-data.md +188 -0
- package/rules/opt-in-stack/backend.md +390 -0
- package/rules/opt-in-stack/frontend.md +98 -0
- package/rules/opt-in-stack/security-web.md +194 -0
- package/rules/opt-in-stack/swift.md +65 -0
- package/scripts/archive-closed-prds.mjs +416 -0
- package/scripts/autopilot-multi.mjs +802 -0
- package/scripts/autopilot.mjs +383 -0
- package/scripts/backfill-abandoned-sessions.mjs +265 -0
- package/scripts/backfill-learnings-expires.mjs +196 -0
- package/scripts/backfill-learnings.mjs +203 -0
- package/scripts/backfill-sessions.mjs +282 -0
- package/scripts/check-doc-consistency.sh +279 -0
- package/scripts/check-package-manager.mjs +445 -0
- package/scripts/ci/assert-vitest-green.mjs +267 -0
- package/scripts/codex-install.mjs +435 -0
- package/scripts/compute-grounding-injection.sh +186 -0
- package/scripts/cursor-install.mjs +113 -0
- package/scripts/dialectic-deriver.mjs +573 -0
- package/scripts/emit-event.mjs +160 -0
- package/scripts/emit-session.mjs +212 -0
- package/scripts/eval-session.mjs +262 -0
- package/scripts/export-hw-learnings.mjs +437 -0
- package/scripts/gc-stale-worktrees.mjs +666 -0
- package/scripts/generate-pi-prompts.mjs +127 -0
- package/scripts/harness-audit.mjs +287 -0
- package/scripts/lib/agent-frontmatter.mjs +266 -0
- package/scripts/lib/agent-output-schema.mjs +166 -0
- package/scripts/lib/agent-status.mjs +303 -0
- package/scripts/lib/ajv-loader.mjs +34 -0
- package/scripts/lib/auto-dialectic.mjs +382 -0
- package/scripts/lib/auto-dream.mjs +471 -0
- package/scripts/lib/autonomy/suitability.mjs +212 -0
- package/scripts/lib/autopilot/dep-graph.mjs +417 -0
- package/scripts/lib/autopilot/durable-telemetry.mjs +121 -0
- package/scripts/lib/autopilot/flags.mjs +104 -0
- package/scripts/lib/autopilot/kill-switches.mjs +174 -0
- package/scripts/lib/autopilot/loop.mjs +320 -0
- package/scripts/lib/autopilot/mr-draft.mjs +520 -0
- package/scripts/lib/autopilot/multi-killswitch.mjs +184 -0
- package/scripts/lib/autopilot/recent-runs.mjs +106 -0
- package/scripts/lib/autopilot/stall-sampler.mjs +97 -0
- package/scripts/lib/autopilot/telemetry.mjs +224 -0
- package/scripts/lib/autopilot/worktree-pipeline.mjs +605 -0
- package/scripts/lib/autopilot-telemetry.mjs +11 -0
- package/scripts/lib/autopilot.mjs +39 -0
- package/scripts/lib/backlog-scan.mjs +179 -0
- package/scripts/lib/bootstrap-lock-freshness.mjs +260 -0
- package/scripts/lib/bootstrap-lock-refresh.mjs +186 -0
- package/scripts/lib/build-live-signals.mjs +150 -0
- package/scripts/lib/ci-status-banner.mjs +425 -0
- package/scripts/lib/claude-md-budget-lint.mjs +246 -0
- package/scripts/lib/cli-flags.mjs +158 -0
- package/scripts/lib/codex/plugin-contract.mjs +610 -0
- package/scripts/lib/cold-start-detector.mjs +240 -0
- package/scripts/lib/command-blocker.mjs +458 -0
- package/scripts/lib/common.mjs +333 -0
- package/scripts/lib/config/auto-dream.mjs +77 -0
- package/scripts/lib/config/block-header.mjs +94 -0
- package/scripts/lib/config/broken-window.mjs +114 -0
- package/scripts/lib/config/coercers.mjs +248 -0
- package/scripts/lib/config/cold-start.mjs +92 -0
- package/scripts/lib/config/config-protection.mjs +120 -0
- package/scripts/lib/config/cross-repo.mjs +104 -0
- package/scripts/lib/config/custom-phases.mjs +213 -0
- package/scripts/lib/config/dialectic.mjs +92 -0
- package/scripts/lib/config/discovery-validator.mjs +75 -0
- package/scripts/lib/config/dispatcher-autonomy-capture.mjs +240 -0
- package/scripts/lib/config/dispatcher-autonomy.mjs +152 -0
- package/scripts/lib/config/docs-orchestrator.mjs +90 -0
- package/scripts/lib/config/docs-staleness.mjs +96 -0
- package/scripts/lib/config/drift-check.mjs +155 -0
- package/scripts/lib/config/eval.mjs +130 -0
- package/scripts/lib/config/events-rotation.mjs +74 -0
- package/scripts/lib/config/evolve.mjs +308 -0
- package/scripts/lib/config/frontend-slop-hook.mjs +104 -0
- package/scripts/lib/config/gitlab-portfolio.mjs +150 -0
- package/scripts/lib/config/handover-gate.mjs +106 -0
- package/scripts/lib/config/host-paths.mjs +76 -0
- package/scripts/lib/config/io.mjs +54 -0
- package/scripts/lib/config/loop-guard.mjs +117 -0
- package/scripts/lib/config/memory.mjs +150 -0
- package/scripts/lib/config/persona-gate-wave.mjs +258 -0
- package/scripts/lib/config/reconcile.mjs +205 -0
- package/scripts/lib/config/section-extractor.mjs +100 -0
- package/scripts/lib/config/skill-evolution.mjs +112 -0
- package/scripts/lib/config/slopcheck.mjs +99 -0
- package/scripts/lib/config/state-md-lock.mjs +83 -0
- package/scripts/lib/config/templates-first.mjs +94 -0
- package/scripts/lib/config/test.mjs +113 -0
- package/scripts/lib/config/vault-integration.mjs +201 -0
- package/scripts/lib/config/vault-mirror-quality.mjs +99 -0
- package/scripts/lib/config/vault-staleness.mjs +84 -0
- package/scripts/lib/config/vault-sync.mjs +96 -0
- package/scripts/lib/config/verification-auto-fix.mjs +84 -0
- package/scripts/lib/config/wave-reviewers.mjs +133 -0
- package/scripts/lib/config-schema.mjs +345 -0
- package/scripts/lib/config.mjs +474 -0
- package/scripts/lib/convergence-monitor.mjs +389 -0
- package/scripts/lib/coordinator-snapshot.mjs +371 -0
- package/scripts/lib/crypto-digest-utils.mjs +91 -0
- package/scripts/lib/discovery/helpers.mjs +127 -0
- package/scripts/lib/discovery/triage-state.mjs +279 -0
- package/scripts/lib/dispatcher/cli.mjs +257 -0
- package/scripts/lib/dispatcher/enumerate.mjs +243 -0
- package/scripts/lib/dispatcher/rank.mjs +363 -0
- package/scripts/lib/ecosystem-health.mjs +224 -0
- package/scripts/lib/ecosystem-wizard/ci-detector.mjs +18 -0
- package/scripts/lib/ecosystem-wizard/config-parser.mjs +54 -0
- package/scripts/lib/ecosystem-wizard/config-writer.mjs +287 -0
- package/scripts/lib/ecosystem-wizard/package-manager-detector.mjs +42 -0
- package/scripts/lib/ecosystem-wizard/wizard-prompt.mjs +246 -0
- package/scripts/lib/ecosystem-wizard.mjs +48 -0
- package/scripts/lib/env-check.mjs +89 -0
- package/scripts/lib/eval/engine.mjs +605 -0
- package/scripts/lib/eval/judge.mjs +433 -0
- package/scripts/lib/eval/report.mjs +367 -0
- package/scripts/lib/eval/schema.mjs +618 -0
- package/scripts/lib/eval/session-resolve.mjs +137 -0
- package/scripts/lib/eval/sink.mjs +77 -0
- package/scripts/lib/events-rotation.mjs +86 -0
- package/scripts/lib/events-schema.mjs +81 -0
- package/scripts/lib/events.mjs +80 -0
- package/scripts/lib/evolve/autonomy-verdict.mjs +461 -0
- package/scripts/lib/evolve/autopilot-effectiveness.mjs +293 -0
- package/scripts/lib/exclusivity-matrix.mjs +68 -0
- package/scripts/lib/fetch-baseline.mjs +311 -0
- package/scripts/lib/file-lock.mjs +512 -0
- package/scripts/lib/frontend-detect/detect.mjs +138 -0
- package/scripts/lib/frontend-detect/rules.mjs +295 -0
- package/scripts/lib/frontmatter-guard.mjs +241 -0
- package/scripts/lib/gates/echo-stub-detect.mjs +39 -0
- package/scripts/lib/gates/gate-baseline.mjs +42 -0
- package/scripts/lib/gates/gate-full.mjs +85 -0
- package/scripts/lib/gates/gate-helpers.mjs +231 -0
- package/scripts/lib/gates/gate-incremental.mjs +76 -0
- package/scripts/lib/gates/gate-per-file.mjs +55 -0
- package/scripts/lib/gitlab-ops/stale-mr-sweep.mjs +447 -0
- package/scripts/lib/gitlab-portfolio/aggregator.mjs +383 -0
- package/scripts/lib/gitlab-portfolio/cli.mjs +428 -0
- package/scripts/lib/gitlab-portfolio/markdown-writer.mjs +289 -0
- package/scripts/lib/gitlab-portfolio/vcs-detect.mjs +182 -0
- package/scripts/lib/handover-gate.mjs +222 -0
- package/scripts/lib/hardening.mjs +43 -0
- package/scripts/lib/hardware-pattern-detector.mjs +238 -0
- package/scripts/lib/harness-audit/categories/category1.mjs +123 -0
- package/scripts/lib/harness-audit/categories/category2.mjs +145 -0
- package/scripts/lib/harness-audit/categories/category3.mjs +143 -0
- package/scripts/lib/harness-audit/categories/category4.mjs +202 -0
- package/scripts/lib/harness-audit/categories/category5.mjs +152 -0
- package/scripts/lib/harness-audit/categories/category6.mjs +211 -0
- package/scripts/lib/harness-audit/categories/category7.mjs +125 -0
- package/scripts/lib/harness-audit/categories/category8.mjs +328 -0
- package/scripts/lib/harness-audit/categories/category9.mjs +294 -0
- package/scripts/lib/harness-audit/categories/helpers.mjs +165 -0
- package/scripts/lib/harness-audit/categories.mjs +19 -0
- package/scripts/lib/historical-guard.mjs +15 -0
- package/scripts/lib/host-identity.mjs +262 -0
- package/scripts/lib/instruction-budget-guard.mjs +332 -0
- package/scripts/lib/io.mjs +304 -0
- package/scripts/lib/issue-close-strip-labels.mjs +161 -0
- package/scripts/lib/language-mappers/README.md +57 -0
- package/scripts/lib/language-mappers/index.mjs +165 -0
- package/scripts/lib/language-mappers/markdown.mjs +149 -0
- package/scripts/lib/language-mappers/python.mjs +249 -0
- package/scripts/lib/language-mappers/swift.mjs +201 -0
- package/scripts/lib/language-mappers/typescript.mjs +433 -0
- package/scripts/lib/learnings/expiry-sweep.mjs +164 -0
- package/scripts/lib/learnings/filters.mjs +43 -0
- package/scripts/lib/learnings/io.mjs +255 -0
- package/scripts/lib/learnings/schema.mjs +518 -0
- package/scripts/lib/learnings/surface.mjs +207 -0
- package/scripts/lib/learnings.mjs +42 -0
- package/scripts/lib/lock-reaper.mjs +648 -0
- package/scripts/lib/locks/index.mjs +31 -0
- package/scripts/lib/locks/lock-body.mjs +62 -0
- package/scripts/lib/locks/staging-fence-lock.mjs +267 -0
- package/scripts/lib/locks/state-md-lock.mjs +351 -0
- package/scripts/lib/loop-readiness-banner.mjs +144 -0
- package/scripts/lib/memory-banner.mjs +478 -0
- package/scripts/lib/memory-cleanup/worktree-sweep.mjs +108 -0
- package/scripts/lib/memory-cleanup-stamp.mjs +56 -0
- package/scripts/lib/memory-paths.mjs +31 -0
- package/scripts/lib/memory-proposals/collector.mjs +334 -0
- package/scripts/lib/memory-proposals/schema.mjs +289 -0
- package/scripts/lib/memory-proposals/sink.mjs +507 -0
- package/scripts/lib/memory-proposals/store.mjs +441 -0
- package/scripts/lib/mission-status-schema.mjs +114 -0
- package/scripts/lib/mode-selector/alternatives.mjs +64 -0
- package/scripts/lib/mode-selector/constants.mjs +29 -0
- package/scripts/lib/mode-selector/context-pressure.mjs +157 -0
- package/scripts/lib/mode-selector/rationale.mjs +55 -0
- package/scripts/lib/mode-selector/scoring.mjs +221 -0
- package/scripts/lib/mode-selector-accuracy.mjs +121 -0
- package/scripts/lib/mode-selector.mjs +160 -0
- package/scripts/lib/multi-provider-build/providers.mjs +64 -0
- package/scripts/lib/multi-provider-build/templating.mjs +130 -0
- package/scripts/lib/named-baseline-resolver.mjs +233 -0
- package/scripts/lib/named-vault-resolver.mjs +433 -0
- package/scripts/lib/owner-config/coerce.mjs +29 -0
- package/scripts/lib/owner-config/constants.mjs +21 -0
- package/scripts/lib/owner-config/defaults.mjs +50 -0
- package/scripts/lib/owner-config/error.mjs +19 -0
- package/scripts/lib/owner-config/index.mjs +13 -0
- package/scripts/lib/owner-config/merge.mjs +52 -0
- package/scripts/lib/owner-config/validate.mjs +259 -0
- package/scripts/lib/owner-config-banner.mjs +126 -0
- package/scripts/lib/owner-config-loader.mjs +159 -0
- package/scripts/lib/owner-config.example.yaml +72 -0
- package/scripts/lib/owner-config.mjs +28 -0
- package/scripts/lib/owner-interview.mjs +243 -0
- package/scripts/lib/owner-yaml.mjs +571 -0
- package/scripts/lib/package-manager.mjs +160 -0
- package/scripts/lib/path-utils.mjs +217 -0
- package/scripts/lib/peer-cards/merger.mjs +310 -0
- package/scripts/lib/peer-cards/reader.mjs +125 -0
- package/scripts/lib/peer-cards/schema.mjs +230 -0
- package/scripts/lib/peer-cards/staleness-banner.mjs +86 -0
- package/scripts/lib/peer-cards/writer.mjs +138 -0
- package/scripts/lib/peer-discovery.mjs +200 -0
- package/scripts/lib/persona-panel/catalog-loader.mjs +577 -0
- package/scripts/lib/persona-panel/consolidator.mjs +370 -0
- package/scripts/lib/persona-panel/persona-runner.mjs +375 -0
- package/scripts/lib/persona-panel/threshold.mjs +130 -0
- package/scripts/lib/pi-hook-bridge.mjs +328 -0
- package/scripts/lib/platform.mjs +266 -0
- package/scripts/lib/playwright-driver/runner.mjs +297 -0
- package/scripts/lib/plugin-root.mjs +210 -0
- package/scripts/lib/pre-dispatch-check.mjs +126 -0
- package/scripts/lib/product-repo-detect.mjs +121 -0
- package/scripts/lib/profiles/registry.mjs +176 -0
- package/scripts/lib/profiles/schema.mjs +209 -0
- package/scripts/lib/qg-command-drift-banner.mjs +88 -0
- package/scripts/lib/quality-gate/diagnostics.mjs +92 -0
- package/scripts/lib/quality-gate.mjs +536 -0
- package/scripts/lib/quality-gates-cache.mjs +228 -0
- package/scripts/lib/quality-gates-policy.mjs +95 -0
- package/scripts/lib/recommendations-v0.mjs +156 -0
- package/scripts/lib/reconcile/eligibility.mjs +203 -0
- package/scripts/lib/reconcile/emitter.mjs +244 -0
- package/scripts/lib/reconcile/engine.mjs +412 -0
- package/scripts/lib/reconcile/idempotency.mjs +239 -0
- package/scripts/lib/reconcile/renderer.mjs +211 -0
- package/scripts/lib/reconcile/writer.mjs +293 -0
- package/scripts/lib/reconcile-nudge-banner.mjs +284 -0
- package/scripts/lib/resource-probe/evaluate.mjs +190 -0
- package/scripts/lib/resource-probe/parsers.mjs +181 -0
- package/scripts/lib/resource-probe/probe-platform.mjs +300 -0
- package/scripts/lib/resource-probe.mjs +95 -0
- package/scripts/lib/rule-loader.mjs +552 -0
- package/scripts/lib/rules-sync.mjs +439 -0
- package/scripts/lib/scope-gate.mjs +496 -0
- package/scripts/lib/session-close-backfill.mjs +539 -0
- package/scripts/lib/session-discovery.mjs +256 -0
- package/scripts/lib/session-end/phase-skip.mjs +357 -0
- package/scripts/lib/session-end/worktree-cleanup.mjs +112 -0
- package/scripts/lib/session-id.mjs +362 -0
- package/scripts/lib/session-lock.mjs +703 -0
- package/scripts/lib/session-registry.mjs +355 -0
- package/scripts/lib/session-schema/aliases.mjs +71 -0
- package/scripts/lib/session-schema/constants.mjs +115 -0
- package/scripts/lib/session-schema/normalizer.mjs +66 -0
- package/scripts/lib/session-schema/timestamps.mjs +64 -0
- package/scripts/lib/session-schema/validator.mjs +453 -0
- package/scripts/lib/session-schema.mjs +71 -0
- package/scripts/lib/session-token-rollup.mjs +137 -0
- package/scripts/lib/sessions-staleness-banner.mjs +247 -0
- package/scripts/lib/skill-evolution/blast-radius-classifier.mjs +114 -0
- package/scripts/lib/skill-evolution/candidate-intake.mjs +270 -0
- package/scripts/lib/skill-evolution/config-validation-gate.mjs +279 -0
- package/scripts/lib/skill-evolution/engine.mjs +719 -0
- package/scripts/lib/skill-evolution/idempotency.mjs +279 -0
- package/scripts/lib/skill-evolution/mr-opener.mjs +507 -0
- package/scripts/lib/skill-health/join.mjs +181 -0
- package/scripts/lib/skill-health/score.mjs +123 -0
- package/scripts/lib/skill-invocations-schema.mjs +214 -0
- package/scripts/lib/skill-judge.mjs +348 -0
- package/scripts/lib/skill-judgments-schema.mjs +264 -0
- package/scripts/lib/slopcheck.mjs +501 -0
- package/scripts/lib/soul-resolve.mjs +118 -0
- package/scripts/lib/spiral-carryover.mjs +495 -0
- package/scripts/lib/state-md/body-sections.mjs +851 -0
- package/scripts/lib/state-md/frontmatter-mutators.mjs +453 -0
- package/scripts/lib/state-md/mission-status.mjs +247 -0
- package/scripts/lib/state-md/recommendations.mjs +57 -0
- package/scripts/lib/state-md/yaml-parser.mjs +234 -0
- package/scripts/lib/state-md-peer-guard.mjs +232 -0
- package/scripts/lib/state-md.mjs +53 -0
- package/scripts/lib/subagents-schema.mjs +309 -0
- package/scripts/lib/sunset/walker.mjs +1192 -0
- package/scripts/lib/test-runner/artifact-paths.mjs +94 -0
- package/scripts/lib/test-runner/fingerprint.mjs +33 -0
- package/scripts/lib/test-runner/issue-reconcile.mjs +770 -0
- package/scripts/lib/tmux-layout/layouts.mjs +224 -0
- package/scripts/lib/tmux-layout/telemetry-stats.mjs +100 -0
- package/scripts/lib/tmux-layout/telemetry.mjs +88 -0
- package/scripts/lib/tmux-layout/tmux-shell.mjs +82 -0
- package/scripts/lib/tmux-layout/vcs-detector.mjs +88 -0
- package/scripts/lib/validate/check-agents.mjs +457 -0
- package/scripts/lib/validate/check-codex-plugin.mjs +37 -0
- package/scripts/lib/validate/check-commands.mjs +148 -0
- package/scripts/lib/validate/check-component-paths.mjs +112 -0
- package/scripts/lib/validate/check-dead-bridge.mjs +180 -0
- package/scripts/lib/validate/check-hooks-symmetry.mjs +258 -0
- package/scripts/lib/validate/check-json-files.mjs +116 -0
- package/scripts/lib/validate/check-owner-leakage.mjs +1011 -0
- package/scripts/lib/validate/check-path-utils-canary.mjs +175 -0
- package/scripts/lib/validate/check-peekaboo-driver-canary.mjs +201 -0
- package/scripts/lib/validate/check-pi-package.mjs +110 -0
- package/scripts/lib/validate/check-pi-prompts.mjs +43 -0
- package/scripts/lib/validate/check-playwright-mcp-canary.mjs +154 -0
- package/scripts/lib/validate/check-plugin-json.mjs +96 -0
- package/scripts/lib/validate/check-plugin-monitors.mjs +206 -0
- package/scripts/lib/validate/check-plugin-schema.mjs +137 -0
- package/scripts/lib/validate/check-rules.mjs +143 -0
- package/scripts/lib/validate/check-session-plan-routing.mjs +154 -0
- package/scripts/lib/validate/check-test-fixture-shapes.mjs +280 -0
- package/scripts/lib/validate/check-unicode-safety.mjs +533 -0
- package/scripts/lib/validate/confidential-names.mjs +169 -0
- package/scripts/lib/validate/dead-bridge-corpus.mjs +141 -0
- package/scripts/lib/validate/dead-bridge-detectors.mjs +568 -0
- package/scripts/lib/validate/tier-inference.mjs +100 -0
- package/scripts/lib/validate-vendored-rules.mjs +519 -0
- package/scripts/lib/vault-archive.mjs +404 -0
- package/scripts/lib/vault-backfill/glab.mjs +164 -0
- package/scripts/lib/vault-backfill/manifest.mjs +75 -0
- package/scripts/lib/vault-backfill/template.mjs +130 -0
- package/scripts/lib/vault-consolidate-fs.mjs +331 -0
- package/scripts/lib/vault-migration-rules.mjs +155 -0
- package/scripts/lib/vault-mirror/auto-commit.mjs +203 -0
- package/scripts/lib/vault-mirror/namespace.mjs +152 -0
- package/scripts/lib/vault-mirror/process.mjs +567 -0
- package/scripts/lib/vault-mirror/pseudonym-map.mjs +164 -0
- package/scripts/lib/vault-mirror/render-learnings.mjs +201 -0
- package/scripts/lib/vault-mirror/render-sessions.mjs +367 -0
- package/scripts/lib/vault-mirror/render.mjs +8 -0
- package/scripts/lib/vault-mirror/utils.mjs +217 -0
- package/scripts/lib/vault-relocation-rules.mjs +555 -0
- package/scripts/lib/vault-repo-backfill.mjs +235 -0
- package/scripts/lib/vault-staleness-banner.mjs +142 -0
- package/scripts/lib/vault-status/board-writer.mjs +769 -0
- package/scripts/lib/vault-status/narrative-mirror.mjs +544 -0
- package/scripts/lib/vault-sync-baseline.mjs +152 -0
- package/scripts/lib/wave-context.mjs +29 -0
- package/scripts/lib/wave-executor/pool.mjs +248 -0
- package/scripts/lib/wave-resource-gate.mjs +204 -0
- package/scripts/lib/wave-sizing.mjs +75 -0
- package/scripts/lib/webhook-url.mjs +105 -0
- package/scripts/lib/workspace.mjs +198 -0
- package/scripts/lib/worktree/constants.mjs +35 -0
- package/scripts/lib/worktree/index.mjs +17 -0
- package/scripts/lib/worktree/lifecycle.mjs +287 -0
- package/scripts/lib/worktree/listing.mjs +118 -0
- package/scripts/lib/worktree/meta.mjs +64 -0
- package/scripts/lib/worktree-freshness.mjs +313 -0
- package/scripts/lib/worktree.mjs +15 -0
- package/scripts/lifecycle-sim-v6.mjs +347 -0
- package/scripts/lock-reaper.mjs +185 -0
- package/scripts/mcp-server.sh +241 -0
- package/scripts/measure-policy-cache-effectiveness.mjs +427 -0
- package/scripts/memory-propose.mjs +464 -0
- package/scripts/migrate-cold-start-seed.mjs +404 -0
- package/scripts/migrate-learnings-jsonl.mjs +189 -0
- package/scripts/migrate-legacy-learnings.sh +61 -0
- package/scripts/migrate-sessions-jsonl.mjs +448 -0
- package/scripts/migrate-subagents-jsonl.mjs +196 -0
- package/scripts/migrate-vault-paths.mjs +796 -0
- package/scripts/parse-config.mjs +149 -0
- package/scripts/pi-install.mjs +117 -0
- package/scripts/print-applicable-rules.mjs +247 -0
- package/scripts/promote-vault-strict.mjs +496 -0
- package/scripts/relocate-vault-corpus.mjs +1178 -0
- package/scripts/run-migrate-v2-cross-repo.mjs +385 -0
- package/scripts/run-quality-gate.mjs +216 -0
- package/scripts/spikes/h3-agent-teams/preflight.sh +53 -0
- package/scripts/spikes/h3-agent-teams/run-h3.sh +112 -0
- package/scripts/spikes/h3-agent-teams/setup.sh +137 -0
- package/scripts/spikes/h3-agent-teams/toggle.sh +38 -0
- package/scripts/sweep-expired-learnings.mjs +135 -0
- package/scripts/sync-vault-schema.mjs +376 -0
- package/scripts/tests/fixtures/fetch-baseline/sample-rule.md +8 -0
- package/scripts/tmux-layout.mjs +245 -0
- package/scripts/token-audit.sh +191 -0
- package/scripts/typecheck.mjs +42 -0
- package/scripts/upload-social-preview.mjs +316 -0
- package/scripts/validate-config.mjs +46 -0
- package/scripts/validate-plugin-manifests.mjs +163 -0
- package/scripts/validate-plugin.mjs +264 -0
- package/scripts/validate-wave-scope.mjs +289 -0
- package/scripts/vault-backfill.mjs +404 -0
- package/scripts/vault-consolidate.mjs +596 -0
- package/scripts/vault-integration-watcher.mjs +394 -0
- package/scripts/vault-mirror.mjs +430 -0
- package/skills/_shared/bootstrap-gate.md +111 -0
- package/skills/_shared/config-reading.md +226 -0
- package/skills/_shared/instruction-file-resolution.md +79 -0
- package/skills/_shared/model-selection.md +64 -0
- package/skills/_shared/monitor-patterns.md +300 -0
- package/skills/_shared/parallel-aware-auq.md +121 -0
- package/skills/_shared/parallel-aware-preamble.md +185 -0
- package/skills/_shared/platform-tools.md +96 -0
- package/skills/_shared/state-ownership.md +221 -0
- package/skills/architecture/DEEPENING.md +37 -0
- package/skills/architecture/INTERFACE-DESIGN.md +44 -0
- package/skills/architecture/LANGUAGE.md +53 -0
- package/skills/architecture/SKILL.md +92 -0
- package/skills/autopilot/SKILL.md +419 -0
- package/skills/bootstrap/SKILL.md +592 -0
- package/skills/bootstrap/STATE.md.template +24 -0
- package/skills/bootstrap/_shared-template.md +243 -0
- package/skills/bootstrap/deep-template.md +659 -0
- package/skills/bootstrap/fast-template.md +251 -0
- package/skills/bootstrap/intensity-heuristic.md +80 -0
- package/skills/bootstrap/public-fallback.md +342 -0
- package/skills/bootstrap/standard-template.md +736 -0
- package/skills/bootstrap/templates/agents/project-code-review.md +18 -0
- package/skills/bootstrap/templates/agents/project-discovery.md +18 -0
- package/skills/bootstrap/templates/agents/project-quality-gate.md +18 -0
- package/skills/brainstorm/SKILL.md +268 -0
- package/skills/brainstorm/soul.md +49 -0
- package/skills/claude-md-drift-check/SKILL.md +186 -0
- package/skills/claude-md-drift-check/checker.mjs +1380 -0
- package/skills/claude-md-drift-check/checker.sh +37 -0
- package/skills/claude-md-drift-check/package.json +16 -0
- package/skills/convergence-monitoring/README.md +39 -0
- package/skills/convergence-monitoring/SIGNALS.md +246 -0
- package/skills/convergence-monitoring/SKILL.md +285 -0
- package/skills/daily/SKILL.md +222 -0
- package/skills/daily/generate.sh +92 -0
- package/skills/daily/templates/daily.md.tpl +36 -0
- package/skills/debug/SKILL.md +188 -0
- package/skills/debug/soul.md +35 -0
- package/skills/discovery/SKILL.md +567 -0
- package/skills/discovery/issue-templates.md +237 -0
- package/skills/discovery/probes/docs-staleness.mjs +195 -0
- package/skills/discovery/probes/frontend-slop.mjs +186 -0
- package/skills/discovery/probes/ssot-code-diff.mjs +310 -0
- package/skills/discovery/probes/supply-chain-slopcheck.mjs +440 -0
- package/skills/discovery/probes/vault-narrative-staleness.mjs +355 -0
- package/skills/discovery/probes/vault-staleness.mjs +272 -0
- package/skills/discovery/probes-arch.md +252 -0
- package/skills/discovery/probes-audit.md +95 -0
- package/skills/discovery/probes-code.md +329 -0
- package/skills/discovery/probes-docs.md +76 -0
- package/skills/discovery/probes-feature.md +150 -0
- package/skills/discovery/probes-infra.md +138 -0
- package/skills/discovery/probes-intro.md +25 -0
- package/skills/discovery/probes-session.md +495 -0
- package/skills/discovery/probes-supply-chain.md +94 -0
- package/skills/discovery/probes-ui.md +147 -0
- package/skills/discovery/probes-vault.md +64 -0
- package/skills/discovery/slop-patterns.md +115 -0
- package/skills/dispatcher/SKILL.md +173 -0
- package/skills/docs-orchestrator/SKILL.md +362 -0
- package/skills/docs-orchestrator/audience-mapping.md +140 -0
- package/skills/domain-model/ADR-FORMAT.md +47 -0
- package/skills/domain-model/CONTEXT-FORMAT.md +77 -0
- package/skills/domain-model/SKILL.md +85 -0
- package/skills/ecosystem-health/SKILL.md +119 -0
- package/skills/ecosystem-health/wizard.md +193 -0
- package/skills/eval/SKILL.md +293 -0
- package/skills/eval/rubric-v1.md +218 -0
- package/skills/evolve/SKILL.md +546 -0
- package/skills/frontmatter-guard/SKILL.md +126 -0
- package/skills/gitlab-ops/SKILL.md +368 -0
- package/skills/gitlab-portfolio/SKILL.md +196 -0
- package/skills/grill/SKILL.md +185 -0
- package/skills/grill/soul.md +55 -0
- package/skills/hook-development/SKILL.md +413 -0
- package/skills/mcp-builder/SKILL.md +260 -0
- package/skills/memory-cleanup/SKILL.md +310 -0
- package/skills/mode-selector/SKILL.md +226 -0
- package/skills/peekaboo-driver/SKILL.md +237 -0
- package/skills/peekaboo-driver/soul.md +32 -0
- package/skills/persona-panel/SKILL.md +365 -0
- package/skills/persona-panel/persona-format.md +205 -0
- package/skills/persona-panel/presets/designer-lens.md +87 -0
- package/skills/persona-panel/presets/engineer-lens.md +88 -0
- package/skills/persona-panel/presets/pm-lens.md +86 -0
- package/skills/plan/SKILL.md +496 -0
- package/skills/plan/mode-feature.md +141 -0
- package/skills/plan/mode-new.md +297 -0
- package/skills/plan/mode-retro.md +271 -0
- package/skills/plan/prd-feature-template.md +132 -0
- package/skills/plan/prd-full-template.md +151 -0
- package/skills/plan/prd-reviewer-prompt.md +103 -0
- package/skills/plan/retro-template.md +75 -0
- package/skills/plan/soul.md +62 -0
- package/skills/playwright-driver/SKILL.md +226 -0
- package/skills/playwright-driver/soul.md +30 -0
- package/skills/quality-gates/SKILL.md +212 -0
- package/skills/reconcile/SKILL.md +324 -0
- package/skills/repo-audit/SKILL.md +272 -0
- package/skills/session-end/SKILL.md +1044 -0
- package/skills/session-end/discovery-scan.md +37 -0
- package/skills/session-end/drift-operations.md +97 -0
- package/skills/session-end/learning-patterns.md +78 -0
- package/skills/session-end/metrics-collection.md +175 -0
- package/skills/session-end/phase-3-2-docs-verification.md +148 -0
- package/skills/session-end/phase-3-6-tail.md +344 -0
- package/skills/session-end/phase-3-7a-recommendations.md +86 -0
- package/skills/session-end/plan-verification.md +288 -0
- package/skills/session-end/session-metrics-write.md +223 -0
- package/skills/session-end/vault-operations.md +50 -0
- package/skills/session-end/verification-checklist.md +20 -0
- package/skills/session-plan/SKILL.md +554 -0
- package/skills/session-plan/wave-template.md +37 -0
- package/skills/session-start/SKILL.md +1043 -0
- package/skills/session-start/phase-2-5-docs-planning.md +119 -0
- package/skills/session-start/phase-4-5-resource-health.md +49 -0
- package/skills/session-start/phase-7-1-premise-check.md +47 -0
- package/skills/session-start/phase-7-5-mode-selector.md +237 -0
- package/skills/session-start/phase-8-5-express-path.md +61 -0
- package/skills/session-start/presentation-format.md +81 -0
- package/skills/session-start/soul.md +57 -0
- package/skills/skill-creator/SKILL.md +168 -0
- package/skills/spinout/SKILL.md +76 -0
- package/skills/sunset-review/SKILL.md +96 -0
- package/skills/test-runner/SKILL.md +362 -0
- package/skills/test-runner/rubric-v1.md +388 -0
- package/skills/test-runner/soul.md +46 -0
- package/skills/tmux-layout/SKILL.md +104 -0
- package/skills/ubiquitous-language/SKILL.md +97 -0
- package/skills/using-orchestrator/SKILL.md +144 -0
- package/skills/vault-mirror/SKILL.md +234 -0
- package/skills/vault-sync/SKILL.md +319 -0
- package/skills/vault-sync/package-lock.json +40 -0
- package/skills/vault-sync/package.json +11 -0
- package/skills/vault-sync/tests/fixtures/archive-test-vault/90-archive/bad-archived.md +8 -0
- package/skills/vault-sync/tests/fixtures/archive-test-vault/_meta/.gitkeep +0 -0
- package/skills/vault-sync/tests/fixtures/archive-test-vault/live-note.md +8 -0
- package/skills/vault-sync/tests/fixtures/broken-frontmatter-vault/_meta/.gitkeep +0 -0
- package/skills/vault-sync/tests/fixtures/broken-frontmatter-vault/bad-type.md +8 -0
- package/skills/vault-sync/tests/fixtures/broken-frontmatter-vault/good-note.md +8 -0
- package/skills/vault-sync/tests/fixtures/clean-vault/.obsidian/config.md +8 -0
- package/skills/vault-sync/tests/fixtures/clean-vault/01-projects/foo/projects-baseline.md +10 -0
- package/skills/vault-sync/tests/fixtures/clean-vault/03-daily/daily-2026-04-13.md +8 -0
- package/skills/vault-sync/tests/fixtures/clean-vault/README.md +3 -0
- package/skills/vault-sync/tests/fixtures/clean-vault/hello-world.md +11 -0
- package/skills/vault-sync/tests/fixtures/dangling-link-vault/_meta/.gitkeep +0 -0
- package/skills/vault-sync/tests/fixtures/dangling-link-vault/has-dangling.md +9 -0
- package/skills/vault-sync/tests/fixtures/dangling-link-vault/real-target.md +8 -0
- package/skills/vault-sync/tests/fixtures/empty-vault/_meta/.gitkeep +0 -0
- package/skills/vault-sync/tests/fixtures/missing-field-vault/_meta/.gitkeep +0 -0
- package/skills/vault-sync/tests/fixtures/missing-field-vault/missing-id.md +7 -0
- package/skills/vault-sync/tests/fixtures/nested-tag-vault/03-daily/daily-2026-04-13.md +9 -0
- package/skills/vault-sync/tests/fixtures/nested-tag-vault/_meta/.gitkeep +0 -0
- package/skills/vault-sync/tests/fixtures/nested-tag-vault/nested-tags-note.md +11 -0
- package/skills/vault-sync/tests/fixtures/no-frontmatter-vault/README.md +3 -0
- package/skills/vault-sync/tests/fixtures/no-frontmatter-vault/_MOC.md +3 -0
- package/skills/vault-sync/tests/fixtures/no-frontmatter-vault/_meta/.gitkeep +0 -0
- package/skills/vault-sync/tests/fixtures/with-moc-vault/_MOC.md +11 -0
- package/skills/vault-sync/tests/fixtures/with-moc-vault/_meta/.gitkeep +0 -0
- package/skills/vault-sync/tests/fixtures/with-moc-vault/hello-world.md +11 -0
- package/skills/vault-sync/tests/schema-drift.test.mjs +133 -0
- package/skills/vault-sync/validator.mjs +658 -0
- package/skills/vault-sync/validator.sh +55 -0
- package/skills/wave-executor/SKILL.md +496 -0
- package/skills/wave-executor/circuit-breaker.md +169 -0
- package/skills/wave-executor/wave-loop.md +1043 -0
- package/skills/write-executable-plan/SKILL.md +237 -0
- package/skills/write-executable-plan/plan-template.md +154 -0
- package/templates/_minimal/CLAUDE.md.tmpl +41 -0
- package/templates/_minimal/README.md.tmpl +15 -0
- package/templates/_minimal/gitignore.tmpl +47 -0
- package/templates/_shared/harte-regeln.md +16 -0
- package/templates/_shared/loop.md +90 -0
- package/templates/_shared/rules/parallel-sessions.md +77 -0
- package/templates/nextjs-minimal/README.md +30 -0
- package/templates/nextjs-minimal/app/layout.tsx +18 -0
- package/templates/nextjs-minimal/app/page.tsx +7 -0
- package/templates/nextjs-minimal/eslint.config.mjs +16 -0
- package/templates/nextjs-minimal/next.config.mjs +4 -0
- package/templates/nextjs-minimal/package.json +27 -0
- package/templates/nextjs-minimal/tsconfig.json +23 -0
- package/templates/node-minimal/README.md +33 -0
- package/templates/node-minimal/eslint.config.mjs +10 -0
- package/templates/node-minimal/package.json +21 -0
- package/templates/node-minimal/src/index.ts +1 -0
- package/templates/node-minimal/tests/sanity.test.ts +5 -0
- package/templates/node-minimal/tsconfig.json +17 -0
- package/templates/personas/README.md +150 -0
- package/templates/personas/accounting-compliance.v1.md +120 -0
- package/templates/personas/accounting-tax-advisor.v1.md +116 -0
- package/templates/personas/buyer-p1-cto.v1.md +125 -0
- package/templates/personas/buyer-p2-kanzlei.v1.md +134 -0
- package/templates/personas/buyer-p3-build.v1.md +130 -0
- package/templates/personas/buyer-p4-tech-veto.v1.md +130 -0
- package/templates/personas/buyer-p5-solo.v1.md +132 -0
- package/templates/personas/buyer-p6-ld.v1.md +130 -0
- package/templates/personas/klima-ai-expert.v1.md +114 -0
- package/templates/personas/klima-physicist.v1.md +117 -0
- package/templates/python-uv/README.md +28 -0
- package/templates/python-uv/pyproject.toml +32 -0
- package/templates/python-uv/src/__PROJECT_NAME__/__init__.py +0 -0
- package/templates/python-uv/src/__PROJECT_NAME__/main.py +6 -0
- package/templates/python-uv/tests/test_sanity.py +2 -0
- package/templates/static-html/README.md +19 -0
- package/templates/static-html/index.html +15 -0
- package/templates/static-html/script.js +1 -0
- package/templates/static-html/styles.css +26 -0
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Mission-status helpers for STATE.md.
|
|
3
|
+
*
|
|
4
|
+
* Handles both the frontmatter `mission-status:` block-seq field and the
|
|
5
|
+
* `## Mission Status` body section (body-level per-task status lines).
|
|
6
|
+
*
|
|
7
|
+
* Pure functions — no file I/O.
|
|
8
|
+
*
|
|
9
|
+
* Plus on-disk wrappers (`writeMissionStatusOnDisk`, `setMissionStatusOnDisk`)
|
|
10
|
+
* added for PRD 2026-05-22 § 4 Pattern 1 (issue #518). The on-disk wrappers
|
|
11
|
+
* delegate to the pure helpers above and route the read+write cycle through
|
|
12
|
+
* `writeStateMd` from frontmatter-mutators.mjs, which acquires
|
|
13
|
+
* `.orchestrator/state.lock` for mechanical serialization (PSA-004).
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import { parseStateMd, serializeStateMd } from './yaml-parser.mjs';
|
|
17
|
+
import { updateFrontmatterFields, writeStateMd } from './frontmatter-mutators.mjs';
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Parses the optional `mission-status:` block from a STATE.md frontmatter object
|
|
21
|
+
* (as returned by `parseStateMd(...).frontmatter`).
|
|
22
|
+
*
|
|
23
|
+
* Returns `null` when the `mission-status` key is absent (backward-compat: pre-#340
|
|
24
|
+
* STATE.md files). Returns `[]` when the key is present but the value is an empty
|
|
25
|
+
* array. Returns the array of entries when present and non-empty.
|
|
26
|
+
*
|
|
27
|
+
* Does NOT validate individual entry shapes — callers that need schema validation
|
|
28
|
+
* should use `validateMissionStatusEntry` from mission-status-schema.mjs.
|
|
29
|
+
*
|
|
30
|
+
* @param {object} frontmatter
|
|
31
|
+
* @returns {object[]|null}
|
|
32
|
+
*/
|
|
33
|
+
export function parseMissionStatus(frontmatter) {
|
|
34
|
+
if (frontmatter === null || typeof frontmatter !== 'object' || Array.isArray(frontmatter)) {
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
if (!Object.prototype.hasOwnProperty.call(frontmatter, 'mission-status')) {
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
const raw = frontmatter['mission-status'];
|
|
41
|
+
if (!Array.isArray(raw)) {
|
|
42
|
+
// Present but not an array (e.g. null scalar from empty key) — treat as absent
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
45
|
+
// Return a shallow copy to prevent mutation of the parsed frontmatter
|
|
46
|
+
return raw.slice();
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Writes (or replaces) the `mission-status:` field in STATE.md frontmatter.
|
|
51
|
+
*
|
|
52
|
+
* - When `missionStatusArray` is an empty array, writes `mission-status:` with an
|
|
53
|
+
* empty block sequence — preserving the key so consumers know the feature is active.
|
|
54
|
+
* - When `missionStatusArray` is null or undefined, DELETES the key (opt-out / reset).
|
|
55
|
+
* - Works on string input (pure — no file I/O). Returns the updated STATE.md contents.
|
|
56
|
+
* - No-ops if `contents` has no parseable frontmatter (returns input unchanged).
|
|
57
|
+
*
|
|
58
|
+
* Individual entry objects must conform to the shape validated by
|
|
59
|
+
* `validateMissionStatusEntry` in mission-status-schema.mjs, but this function does
|
|
60
|
+
* NOT enforce that constraint — callers are responsible for pre-validation.
|
|
61
|
+
*
|
|
62
|
+
* @param {string} contents
|
|
63
|
+
* @param {object[]|null|undefined} missionStatusArray
|
|
64
|
+
* @returns {string}
|
|
65
|
+
*/
|
|
66
|
+
export function writeMissionStatus(contents, missionStatusArray) {
|
|
67
|
+
if (missionStatusArray === null || missionStatusArray === undefined) {
|
|
68
|
+
return updateFrontmatterFields(contents, { 'mission-status': null });
|
|
69
|
+
}
|
|
70
|
+
if (!Array.isArray(missionStatusArray)) {
|
|
71
|
+
return contents;
|
|
72
|
+
}
|
|
73
|
+
// isBlockSeqOfMappings requires at least 1 entry to classify as block-seq; for an empty
|
|
74
|
+
// array we store it as an empty block seq by treating it as a plain empty array which
|
|
75
|
+
// serializeScalar renders as `[]`. For non-empty arrays of objects, use the block format.
|
|
76
|
+
return updateFrontmatterFields(contents, { 'mission-status': missionStatusArray.slice() });
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Sets (or updates) the mission status for a single task in the `## Mission Status` body
|
|
81
|
+
* section of STATE.md. Creates the section if it does not exist.
|
|
82
|
+
*
|
|
83
|
+
* Format of each entry in the section:
|
|
84
|
+
* - <taskId>: <status> (updated <ISO timestamp>)
|
|
85
|
+
*
|
|
86
|
+
* Pure function — no I/O. Returns original `contents` unchanged on bad input.
|
|
87
|
+
*
|
|
88
|
+
* @param {string} contents - Current STATE.md file contents (string)
|
|
89
|
+
* @param {string} taskId - Task identifier (e.g. 'm-1', 'docs-2')
|
|
90
|
+
* @param {string} status - One of: brainstormed | validated | in-dev | testing | completed
|
|
91
|
+
* @returns {string}
|
|
92
|
+
*/
|
|
93
|
+
export function setMissionStatus(contents, taskId, status) {
|
|
94
|
+
if (typeof contents !== 'string') return contents;
|
|
95
|
+
if (!taskId || typeof taskId !== 'string') return contents;
|
|
96
|
+
if (!status || typeof status !== 'string') return contents;
|
|
97
|
+
const parsed = parseStateMd(contents);
|
|
98
|
+
if (parsed === null) return contents;
|
|
99
|
+
|
|
100
|
+
const timestamp = new Date().toISOString();
|
|
101
|
+
const bullet = `- ${taskId}: ${status} (updated ${timestamp})`;
|
|
102
|
+
const lines = parsed.body.split('\n');
|
|
103
|
+
|
|
104
|
+
// Find existing ## Mission Status section
|
|
105
|
+
let headingIdx = -1;
|
|
106
|
+
for (let i = 0; i < lines.length; i++) {
|
|
107
|
+
if (/^##\s+Mission Status\b/.test(lines[i])) {
|
|
108
|
+
headingIdx = i;
|
|
109
|
+
break;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
if (headingIdx === -1) {
|
|
114
|
+
// Section does not exist — append it at the end
|
|
115
|
+
let bodyOut = parsed.body;
|
|
116
|
+
if (!bodyOut.endsWith('\n')) bodyOut += '\n';
|
|
117
|
+
bodyOut += `\n## Mission Status\n\n${bullet}\n`;
|
|
118
|
+
return serializeStateMd({ frontmatter: parsed.frontmatter, body: bodyOut });
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// Find end of section: next ## heading or end of lines
|
|
122
|
+
let sectionEnd = lines.length;
|
|
123
|
+
for (let i = headingIdx + 1; i < lines.length; i++) {
|
|
124
|
+
if (/^##\s+/.test(lines[i])) {
|
|
125
|
+
sectionEnd = i;
|
|
126
|
+
break;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// Look for an existing entry with this taskId within the section
|
|
131
|
+
const entryRe = new RegExp(`^-\\s+${taskId.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}:`);
|
|
132
|
+
let existingIdx = -1;
|
|
133
|
+
for (let i = headingIdx + 1; i < sectionEnd; i++) {
|
|
134
|
+
if (entryRe.test(lines[i])) {
|
|
135
|
+
existingIdx = i;
|
|
136
|
+
break;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
if (existingIdx !== -1) {
|
|
141
|
+
// Replace existing entry in-place
|
|
142
|
+
lines[existingIdx] = bullet;
|
|
143
|
+
} else {
|
|
144
|
+
// Append after the last bullet in this section (or right after heading if empty)
|
|
145
|
+
let lastBulletIdx = -1;
|
|
146
|
+
for (let i = headingIdx + 1; i < sectionEnd; i++) {
|
|
147
|
+
if (/^-\s+/.test(lines[i])) lastBulletIdx = i;
|
|
148
|
+
}
|
|
149
|
+
if (lastBulletIdx !== -1) {
|
|
150
|
+
lines.splice(lastBulletIdx + 1, 0, bullet);
|
|
151
|
+
} else {
|
|
152
|
+
// No bullets yet — insert after heading with blank line
|
|
153
|
+
let insertAt = headingIdx + 1;
|
|
154
|
+
while (insertAt < sectionEnd && lines[insertAt].trim() === '') insertAt++;
|
|
155
|
+
const before = lines.slice(0, headingIdx + 1);
|
|
156
|
+
const after = lines.slice(insertAt);
|
|
157
|
+
const rebuilt = [...before, '', bullet, ...after];
|
|
158
|
+
return serializeStateMd({ frontmatter: parsed.frontmatter, body: rebuilt.join('\n') });
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
return serializeStateMd({ frontmatter: parsed.frontmatter, body: lines.join('\n') });
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Reads the current mission status for a single task from the `## Mission Status` body
|
|
167
|
+
* section of STATE.md.
|
|
168
|
+
*
|
|
169
|
+
* Returns the status string (e.g. `'in-dev'`) or `null` if the task is not found or the
|
|
170
|
+
* section does not exist. Never throws — returns `null` on any bad input.
|
|
171
|
+
*
|
|
172
|
+
* @param {string} contents - Current STATE.md file contents (string)
|
|
173
|
+
* @param {string} taskId - Task identifier to look up (e.g. 'm-1')
|
|
174
|
+
* @returns {string|null}
|
|
175
|
+
*/
|
|
176
|
+
export function readMissionStatus(contents, taskId) {
|
|
177
|
+
if (typeof contents !== 'string') return null;
|
|
178
|
+
if (!taskId || typeof taskId !== 'string') return null;
|
|
179
|
+
const parsed = parseStateMd(contents);
|
|
180
|
+
if (parsed === null) return null;
|
|
181
|
+
|
|
182
|
+
const lines = parsed.body.split('\n');
|
|
183
|
+
let headingIdx = -1;
|
|
184
|
+
for (let i = 0; i < lines.length; i++) {
|
|
185
|
+
if (/^##\s+Mission Status\b/.test(lines[i])) {
|
|
186
|
+
headingIdx = i;
|
|
187
|
+
break;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
if (headingIdx === -1) return null;
|
|
191
|
+
|
|
192
|
+
let sectionEnd = lines.length;
|
|
193
|
+
for (let i = headingIdx + 1; i < lines.length; i++) {
|
|
194
|
+
if (/^##\s+/.test(lines[i])) {
|
|
195
|
+
sectionEnd = i;
|
|
196
|
+
break;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// Match: - <taskId>: <status> (updated ...)
|
|
201
|
+
const escapedId = taskId.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
202
|
+
const entryRe = new RegExp(`^-\\s+${escapedId}:\\s+(\\S+)`);
|
|
203
|
+
for (let i = headingIdx + 1; i < sectionEnd; i++) {
|
|
204
|
+
const m = entryRe.exec(lines[i]);
|
|
205
|
+
if (m) return m[1];
|
|
206
|
+
}
|
|
207
|
+
return null;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// ---------------------------------------------------------------------------
|
|
211
|
+
// On-disk wrappers (PRD 2026-05-22 § 4 — Pattern 1, issue #518)
|
|
212
|
+
// ---------------------------------------------------------------------------
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* Lock-guarded `writeMissionStatus` — replaces the frontmatter `mission-status`
|
|
216
|
+
* block-seq under the state-lock.
|
|
217
|
+
*
|
|
218
|
+
* @param {string|undefined} repoRoot
|
|
219
|
+
* @param {object[]|null|undefined} missionStatusArray
|
|
220
|
+
* @param {object} [opts]
|
|
221
|
+
* @returns {Promise<{ written: boolean, path: string, contents: string|null }>}
|
|
222
|
+
*/
|
|
223
|
+
export async function writeMissionStatusOnDisk(repoRoot, missionStatusArray, opts = {}) {
|
|
224
|
+
return writeStateMd(
|
|
225
|
+
repoRoot,
|
|
226
|
+
(contents) => writeMissionStatus(contents, missionStatusArray),
|
|
227
|
+
opts
|
|
228
|
+
);
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* Lock-guarded `setMissionStatus` — sets or replaces a single task entry in
|
|
233
|
+
* the `## Mission Status` body section under the state-lock.
|
|
234
|
+
*
|
|
235
|
+
* @param {string|undefined} repoRoot
|
|
236
|
+
* @param {string} taskId
|
|
237
|
+
* @param {string} status brainstormed | validated | in-dev | testing | completed
|
|
238
|
+
* @param {object} [opts]
|
|
239
|
+
* @returns {Promise<{ written: boolean, path: string, contents: string|null }>}
|
|
240
|
+
*/
|
|
241
|
+
export async function setMissionStatusOnDisk(repoRoot, taskId, status, opts = {}) {
|
|
242
|
+
return writeStateMd(
|
|
243
|
+
repoRoot,
|
|
244
|
+
(contents) => setMissionStatus(contents, taskId, status),
|
|
245
|
+
opts
|
|
246
|
+
);
|
|
247
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Recommendation-fields contract for STATE.md v1.1 (extracted from state-md.mjs, issue #358).
|
|
3
|
+
*
|
|
4
|
+
* Self-contained leaf module — no imports from state-md.mjs (avoids circular deps).
|
|
5
|
+
* Re-exported from scripts/lib/state-md.mjs for callsite compatibility.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Parses the 5 v1.1 Recommendation fields from a STATE.md frontmatter object
|
|
10
|
+
* (as returned by `parseStateMd(...).frontmatter`).
|
|
11
|
+
*
|
|
12
|
+
* Returns `null` when NONE of the 5 fields are present (backward-compat:
|
|
13
|
+
* pre-v1.1 STATE.md files).
|
|
14
|
+
*
|
|
15
|
+
* When a subset is present, populates the object with the parsed values and
|
|
16
|
+
* sets missing fields to null. Type-mismatched fields are also coerced to
|
|
17
|
+
* null (defensive — do not propagate garbage into downstream Mode-Selector).
|
|
18
|
+
* Caller is responsible for emitting partial/type-mismatch warn events.
|
|
19
|
+
*
|
|
20
|
+
* @param {object} frontmatter
|
|
21
|
+
* @returns {{mode: string|null, priorities: number[]|null, carryoverRatio: number|null, completionRate: number|null, rationale: string|null}|null}
|
|
22
|
+
*/
|
|
23
|
+
export function parseRecommendations(frontmatter) {
|
|
24
|
+
if (frontmatter === null || typeof frontmatter !== 'object' || Array.isArray(frontmatter)) {
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
const keys = [
|
|
28
|
+
'recommended-mode',
|
|
29
|
+
'top-priorities',
|
|
30
|
+
'carryover-ratio',
|
|
31
|
+
'completion-rate',
|
|
32
|
+
'rationale',
|
|
33
|
+
];
|
|
34
|
+
const anyPresent = keys.some((k) => Object.prototype.hasOwnProperty.call(frontmatter, k));
|
|
35
|
+
if (!anyPresent) return null;
|
|
36
|
+
|
|
37
|
+
const mode = typeof frontmatter['recommended-mode'] === 'string'
|
|
38
|
+
? frontmatter['recommended-mode']
|
|
39
|
+
: null;
|
|
40
|
+
const priorities = Array.isArray(frontmatter['top-priorities'])
|
|
41
|
+
&& frontmatter['top-priorities'].every((x) => Number.isInteger(x))
|
|
42
|
+
? frontmatter['top-priorities'].slice()
|
|
43
|
+
: null;
|
|
44
|
+
const carryoverRatio = typeof frontmatter['carryover-ratio'] === 'number'
|
|
45
|
+
&& !Number.isNaN(frontmatter['carryover-ratio'])
|
|
46
|
+
? frontmatter['carryover-ratio']
|
|
47
|
+
: null;
|
|
48
|
+
const completionRate = typeof frontmatter['completion-rate'] === 'number'
|
|
49
|
+
&& !Number.isNaN(frontmatter['completion-rate'])
|
|
50
|
+
? frontmatter['completion-rate']
|
|
51
|
+
: null;
|
|
52
|
+
const rationale = typeof frontmatter.rationale === 'string'
|
|
53
|
+
? frontmatter.rationale
|
|
54
|
+
: null;
|
|
55
|
+
|
|
56
|
+
return { mode, priorities, carryoverRatio, completionRate, rationale };
|
|
57
|
+
}
|
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Minimal hand-rolled YAML-subset parser + serializer for the fields used by
|
|
3
|
+
* the session-orchestrator STATE.md contract. Not a general-purpose YAML
|
|
4
|
+
* implementation — handles:
|
|
5
|
+
* - Scalar strings, booleans, integers, nulls
|
|
6
|
+
* - Flow-style integer arrays (`[1, 2, 3]`)
|
|
7
|
+
* - Block-style sequences of mappings (issue #244), e.g. `docs-tasks:` with
|
|
8
|
+
* indented `- key: value` entries. Only one nesting level supported.
|
|
9
|
+
*
|
|
10
|
+
* That is the full grammar permitted by skills/_shared/state-ownership.md.
|
|
11
|
+
*
|
|
12
|
+
* Never throws. Returns null for unparseable input rather than raising.
|
|
13
|
+
*
|
|
14
|
+
* Inverse property (#747 — root-fix for the 6.3-MB balloon incident #739):
|
|
15
|
+
* `parseScalar` and `serializeScalar` are mutually inverse for scalars the
|
|
16
|
+
* SERIALIZER produced. Concretely:
|
|
17
|
+
* - serialize∘parse is a BYTE-fixpoint for any file this serializer emitted:
|
|
18
|
+
* double-quoted scalars are JSON-escaped on emit and JSON.parse-unescaped
|
|
19
|
+
* on read, so a literal `"` / `\` / newline no longer accretes an extra
|
|
20
|
+
* backslash layer per round-trip (the compounding-growth mechanism).
|
|
21
|
+
* - parse∘serialize preserves the VALUE and its runtime type exactly:
|
|
22
|
+
* bool/null/number-SHAPED strings ('true', 'null', '42', '1.0') are
|
|
23
|
+
* force-quoted on emit so they survive as strings instead of coercing to a
|
|
24
|
+
* boolean/null/number on the next parse.
|
|
25
|
+
* - KNOWN non-byte-fixpoint: a SINGLE-quoted source line normalises to
|
|
26
|
+
* double-quoted on first serialize (content identical, bytes differ), then
|
|
27
|
+
* converges to a byte-fixpoint after one cycle. The serializer never emits
|
|
28
|
+
* single-quoted, so this only affects hand-authored input.
|
|
29
|
+
*
|
|
30
|
+
* Leaf module — no imports from peer state-md submodules (avoids circular deps).
|
|
31
|
+
*/
|
|
32
|
+
|
|
33
|
+
const FRONTMATTER_RE = /^---\r?\n([\s\S]*?)\r?\n---\r?\n?([\s\S]*)$/;
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Parses a STATE.md file into frontmatter + body.
|
|
37
|
+
*
|
|
38
|
+
* @param {string} contents
|
|
39
|
+
* @returns {{frontmatter: object, body: string}|null}
|
|
40
|
+
*/
|
|
41
|
+
export function parseStateMd(contents) {
|
|
42
|
+
if (typeof contents !== 'string') return null;
|
|
43
|
+
const match = FRONTMATTER_RE.exec(contents);
|
|
44
|
+
if (!match) return null;
|
|
45
|
+
const [, fmText, body] = match;
|
|
46
|
+
const frontmatter = parseFrontmatter(fmText);
|
|
47
|
+
if (frontmatter === null) return null;
|
|
48
|
+
return { frontmatter, body: body.startsWith('\n') ? body.slice(1) : body };
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Serializes a frontmatter object + body back into STATE.md format.
|
|
53
|
+
*
|
|
54
|
+
* @param {{frontmatter: object, body: string}} input
|
|
55
|
+
* @returns {string}
|
|
56
|
+
*/
|
|
57
|
+
export function serializeStateMd({ frontmatter, body }) {
|
|
58
|
+
const fmLines = [];
|
|
59
|
+
for (const [k, v] of Object.entries(frontmatter)) {
|
|
60
|
+
if (isBlockSeqOfMappings(v)) {
|
|
61
|
+
fmLines.push(`${k}:`);
|
|
62
|
+
for (const entry of v) {
|
|
63
|
+
serializeBlockSeqEntry(entry, fmLines);
|
|
64
|
+
}
|
|
65
|
+
} else {
|
|
66
|
+
fmLines.push(`${k}: ${serializeScalar(v)}`);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
const bodyOut = body.startsWith('\n') ? body : `\n${body}`;
|
|
70
|
+
return `---\n${fmLines.join('\n')}\n---\n${bodyOut}`;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function parseFrontmatter(text) {
|
|
74
|
+
const out = {};
|
|
75
|
+
const lines = text.split(/\r?\n/);
|
|
76
|
+
let i = 0;
|
|
77
|
+
while (i < lines.length) {
|
|
78
|
+
const rstripped = lines[i].replace(/\s+$/, '');
|
|
79
|
+
if (rstripped === '' || /^\s*#/.test(rstripped)) {
|
|
80
|
+
i++;
|
|
81
|
+
continue;
|
|
82
|
+
}
|
|
83
|
+
if (/^\s/.test(rstripped)) return null;
|
|
84
|
+
const idx = rstripped.indexOf(':');
|
|
85
|
+
if (idx === -1) return null;
|
|
86
|
+
const key = rstripped.slice(0, idx).trim();
|
|
87
|
+
if (key === '') return null;
|
|
88
|
+
const valuePart = rstripped.slice(idx + 1).trim();
|
|
89
|
+
if (valuePart === '') {
|
|
90
|
+
const result = parseBlockValue(lines, i + 1);
|
|
91
|
+
if (result === null) return null;
|
|
92
|
+
out[key] = result.value;
|
|
93
|
+
i = result.nextIndex;
|
|
94
|
+
} else {
|
|
95
|
+
out[key] = parseScalar(valuePart);
|
|
96
|
+
i++;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
return out;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Parses an optional block-sequence-of-mappings value following an empty
|
|
104
|
+
* `key:` line. Returns `{ value, nextIndex }` where:
|
|
105
|
+
* - `value === null` means no block sequence was present (the `key:` has
|
|
106
|
+
* no body) and `nextIndex === start` so the caller resumes at `start`.
|
|
107
|
+
* - `value === [...]` means a block sequence was consumed.
|
|
108
|
+
* Returns `null` on malformed block syntax.
|
|
109
|
+
*/
|
|
110
|
+
function parseBlockValue(lines, start) {
|
|
111
|
+
let i = start;
|
|
112
|
+
while (i < lines.length) {
|
|
113
|
+
const rstripped = lines[i].replace(/\s+$/, '');
|
|
114
|
+
if (rstripped === '' || /^\s*#/.test(rstripped)) {
|
|
115
|
+
i++;
|
|
116
|
+
continue;
|
|
117
|
+
}
|
|
118
|
+
break;
|
|
119
|
+
}
|
|
120
|
+
if (i >= lines.length) return { value: null, nextIndex: start };
|
|
121
|
+
const peek = lines[i].replace(/\s+$/, '');
|
|
122
|
+
const bulletMatch = peek.match(/^(\s+)- /);
|
|
123
|
+
if (!bulletMatch) return { value: null, nextIndex: start };
|
|
124
|
+
const indent = bulletMatch[1];
|
|
125
|
+
const contIndent = indent + ' ';
|
|
126
|
+
const entries = [];
|
|
127
|
+
while (i < lines.length) {
|
|
128
|
+
const rstripped = lines[i].replace(/\s+$/, '');
|
|
129
|
+
if (rstripped === '' || /^\s*#/.test(rstripped)) {
|
|
130
|
+
i++;
|
|
131
|
+
continue;
|
|
132
|
+
}
|
|
133
|
+
if (!rstripped.startsWith(indent + '- ')) break;
|
|
134
|
+
const firstBody = rstripped.slice(indent.length + 2);
|
|
135
|
+
const firstColon = firstBody.indexOf(':');
|
|
136
|
+
if (firstColon === -1) return null;
|
|
137
|
+
const firstKey = firstBody.slice(0, firstColon).trim();
|
|
138
|
+
if (firstKey === '') return null;
|
|
139
|
+
const entry = {};
|
|
140
|
+
entry[firstKey] = parseScalar(firstBody.slice(firstColon + 1).trim());
|
|
141
|
+
i++;
|
|
142
|
+
while (i < lines.length) {
|
|
143
|
+
const inner = lines[i].replace(/\s+$/, '');
|
|
144
|
+
if (inner === '' || /^\s*#/.test(inner)) {
|
|
145
|
+
i++;
|
|
146
|
+
continue;
|
|
147
|
+
}
|
|
148
|
+
if (!inner.startsWith(contIndent) || inner.startsWith(indent + '- ')) break;
|
|
149
|
+
const body = inner.slice(contIndent.length);
|
|
150
|
+
if (/^\s/.test(body)) return null;
|
|
151
|
+
const colon = body.indexOf(':');
|
|
152
|
+
if (colon === -1) return null;
|
|
153
|
+
const key = body.slice(0, colon).trim();
|
|
154
|
+
if (key === '') return null;
|
|
155
|
+
entry[key] = parseScalar(body.slice(colon + 1).trim());
|
|
156
|
+
i++;
|
|
157
|
+
}
|
|
158
|
+
entries.push(entry);
|
|
159
|
+
}
|
|
160
|
+
return { value: entries, nextIndex: i };
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
function parseScalar(raw) {
|
|
164
|
+
if (raw === '' || raw === 'null' || raw === '~') return null;
|
|
165
|
+
if (raw === 'true') return true;
|
|
166
|
+
if (raw === 'false') return false;
|
|
167
|
+
if (/^-?\d+$/.test(raw)) return parseInt(raw, 10);
|
|
168
|
+
if (/^-?\d+\.\d+$/.test(raw)) return parseFloat(raw);
|
|
169
|
+
if (raw.startsWith('[') && raw.endsWith(']')) {
|
|
170
|
+
const inner = raw.slice(1, -1).trim();
|
|
171
|
+
if (inner === '') return [];
|
|
172
|
+
return inner.split(',').map((s) => parseScalar(s.trim()));
|
|
173
|
+
}
|
|
174
|
+
if (raw.startsWith('"') && raw.endsWith('"')) {
|
|
175
|
+
// Double-quoted: the serializer emits these via JSON.stringify, so JSON.parse
|
|
176
|
+
// is the exact inverse (unescapes `\"`, `\\`, `\n`, `\t`, `\uXXXX`, …). Fall
|
|
177
|
+
// back to a naive quote-strip if the interior is not valid JSON (hand-authored
|
|
178
|
+
// or already-corrupt content) — this module's never-throw contract holds.
|
|
179
|
+
try {
|
|
180
|
+
return JSON.parse(raw);
|
|
181
|
+
} catch {
|
|
182
|
+
return raw.slice(1, -1);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
if (raw.startsWith("'") && raw.endsWith("'")) {
|
|
186
|
+
// Single-quoted: the serializer never emits this form (it always uses
|
|
187
|
+
// double-quotes via JSON.stringify), so a plain quote-strip is correct and
|
|
188
|
+
// no unescaping is defined. Sources like this normalise to double-quoted on
|
|
189
|
+
// the next serialize — a KNOWN non-byte-fixpoint (value preserved).
|
|
190
|
+
return raw.slice(1, -1);
|
|
191
|
+
}
|
|
192
|
+
return raw;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
function isBlockSeqOfMappings(v) {
|
|
196
|
+
return (
|
|
197
|
+
Array.isArray(v) &&
|
|
198
|
+
v.length > 0 &&
|
|
199
|
+
v.every((x) => x !== null && typeof x === 'object' && !Array.isArray(x))
|
|
200
|
+
);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
function serializeBlockSeqEntry(entry, fmLines) {
|
|
204
|
+
const entries = Object.entries(entry);
|
|
205
|
+
if (entries.length === 0) {
|
|
206
|
+
fmLines.push(' - {}');
|
|
207
|
+
return;
|
|
208
|
+
}
|
|
209
|
+
const [firstKey, firstValue] = entries[0];
|
|
210
|
+
fmLines.push(` - ${firstKey}: ${serializeScalar(firstValue)}`);
|
|
211
|
+
for (let idx = 1; idx < entries.length; idx++) {
|
|
212
|
+
const [key, value] = entries[idx];
|
|
213
|
+
fmLines.push(` ${key}: ${serializeScalar(value)}`);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/** Characters permitted in an unquoted (bare) scalar emission. */
|
|
218
|
+
const BARE_SCALAR_RE = /^[\w\-./:+@]+$/;
|
|
219
|
+
|
|
220
|
+
function serializeScalar(v) {
|
|
221
|
+
if (v === null || v === undefined) return 'null';
|
|
222
|
+
if (typeof v === 'boolean') return v ? 'true' : 'false';
|
|
223
|
+
if (typeof v === 'number') return String(v);
|
|
224
|
+
if (Array.isArray(v)) return `[${v.map(serializeScalar).join(', ')}]`;
|
|
225
|
+
const s = String(v);
|
|
226
|
+
// Emit bare ONLY when the value would round-trip back to the identical STRING.
|
|
227
|
+
// `parseScalar(s) === s` force-quotes bool/null/number-shaped strings
|
|
228
|
+
// ('true'/'false'/'null'/'42'/'1.0'), which would otherwise re-parse to a
|
|
229
|
+
// boolean/null/number — the silent type-coercion asymmetry (#747). Otherwise
|
|
230
|
+
// JSON.stringify, whose exact inverse is the JSON.parse branch in parseScalar.
|
|
231
|
+
// (parseScalar is a hoisted function declaration — safe to reference here.)
|
|
232
|
+
if (BARE_SCALAR_RE.test(s) && parseScalar(s) === s) return s;
|
|
233
|
+
return JSON.stringify(s);
|
|
234
|
+
}
|