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,367 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* render-sessions.mjs — Session markdown generators for vault-mirror (Issue #283 split).
|
|
3
|
+
*
|
|
4
|
+
* Exports: detectSessionSchema, normalizeSessionEntry, generateSessionNote, generateSessionNoteV2, generateSessionNoteV3
|
|
5
|
+
*
|
|
6
|
+
* #732: all three generators accept `options.repoNs` (the leak-guarded /
|
|
7
|
+
* pseudonym-mapped namespace segment from resolveRepoNamespace(), threaded
|
|
8
|
+
* through by process.mjs::processSession) and emit it as a `source-repo:`
|
|
9
|
+
* frontmatter line — mirroring render-learnings.mjs's `source-repo` field.
|
|
10
|
+
* Prior to #732, session notes emitted a raw `repo:` field sourced directly
|
|
11
|
+
* from deriveRepo() (bypassing the leak-guard entirely, even though the write
|
|
12
|
+
* PATH already used resolveRepoNamespace()). Historical notes on disk may still
|
|
13
|
+
* carry the legacy `repo:` key — the vault frontmatter Zod schema tolerates it
|
|
14
|
+
* via `.passthrough()`, and it is NOT rewritten retroactively by this change.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import { toDate, buildTag, slugifyIdSafe } from './utils.mjs';
|
|
18
|
+
|
|
19
|
+
const GENERATOR_MARKER = 'session-orchestrator-vault-mirror@1';
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Frontmatter line emitter — skips emission when value is null/undefined/empty
|
|
23
|
+
* to avoid template-literal coercion bugs (e.g. `platform: undefined` → "undefined").
|
|
24
|
+
* Returns the formatted line with trailing newline, or empty string to skip.
|
|
25
|
+
*/
|
|
26
|
+
function fmLine(key, value) {
|
|
27
|
+
if (value === null || value === undefined || value === '') return '';
|
|
28
|
+
return `${key}: ${value}\n`;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Session JSONL has three producer schemas in production:
|
|
33
|
+
* v1 (legacy): total_waves, total_agents, total_files_changed, agent_summary, waves[{agent_count, files_changed, quality}]
|
|
34
|
+
* v2 (S69+): files_changed (top-level), waves[{agents, agents_done, agents_partial, agents_failed, dispatch, duration_s}]
|
|
35
|
+
* v3 (2026-05+): coordinator-direct records — `waves` is a SCALAR count and
|
|
36
|
+
* `agents_dispatched` is a scalar; no per-wave array breakdown.
|
|
37
|
+
* This is the shape session-end actually emits (#491), which
|
|
38
|
+
* previously fell through to v1 validation and was rejected as
|
|
39
|
+
* `skipped-invalid` (no vault session-note was ever written).
|
|
40
|
+
* v1 and v2 both carry `waves` as an ARRAY, so a numeric `waves` is the
|
|
41
|
+
* unambiguous v3 discriminator.
|
|
42
|
+
*/
|
|
43
|
+
export function detectSessionSchema(entry) {
|
|
44
|
+
if (!entry) return 'v1';
|
|
45
|
+
if (typeof entry.waves === 'number') return 'v3';
|
|
46
|
+
return entry.total_agents === undefined && entry.files_changed !== undefined ? 'v2' : 'v1';
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Map known producer alias fields onto the canonical session shapes (#635).
|
|
51
|
+
*
|
|
52
|
+
* Several session-end variants emitted alias fields the validators reject:
|
|
53
|
+
* `ended_at` (for completed_at), `mode` (for session_type), and wave counts
|
|
54
|
+
* only as `total_waves`/`waves_completed` with no `waves` field at all. Such
|
|
55
|
+
* records fell through to v1 validation and were skipped-invalid. This pure
|
|
56
|
+
* function fills MISSING canonical fields from their aliases so the entry
|
|
57
|
+
* routes to the v3 (scalar-waves) renderer. Canonical v1/v2/v3 entries pass
|
|
58
|
+
* through untouched (`waves` is only filled when absent — an existing array
|
|
59
|
+
* or number is never modified).
|
|
60
|
+
*
|
|
61
|
+
* Returns a new object; never mutates the input.
|
|
62
|
+
*/
|
|
63
|
+
export function normalizeSessionEntry(entry) {
|
|
64
|
+
if (!entry || typeof entry !== 'object') return entry;
|
|
65
|
+
const e = { ...entry };
|
|
66
|
+
|
|
67
|
+
if (e.completed_at === null || e.completed_at === undefined) {
|
|
68
|
+
if (e.ended_at !== null && e.ended_at !== undefined) e.completed_at = e.ended_at;
|
|
69
|
+
}
|
|
70
|
+
if (e.session_type === null || e.session_type === undefined) {
|
|
71
|
+
if (typeof e.mode === 'string' && e.mode.length > 0) e.session_type = e.mode;
|
|
72
|
+
}
|
|
73
|
+
if (e.waves === null || e.waves === undefined) {
|
|
74
|
+
if (typeof e.total_waves === 'number') e.waves = e.total_waves;
|
|
75
|
+
else if (typeof e.waves_completed === 'number') e.waves = e.waves_completed;
|
|
76
|
+
else e.waves = 0; // coordinator-direct record with no wave breakdown
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
return e;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export function generateSessionNote(entry, options = {}) {
|
|
83
|
+
const REQUIRED_SESSION_FIELDS = ['session_id', 'session_type', 'started_at', 'completed_at', 'total_waves', 'total_agents', 'total_files_changed', 'agent_summary', 'waves', 'effectiveness'];
|
|
84
|
+
for (const field of REQUIRED_SESSION_FIELDS) {
|
|
85
|
+
if (entry[field] === null || entry[field] === undefined) {
|
|
86
|
+
throw new Error(`vault-mirror: session entry missing required field '${field}' (session_id=${entry.session_id ?? '<no session_id>'})`);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const {
|
|
91
|
+
session_id,
|
|
92
|
+
session_type,
|
|
93
|
+
platform,
|
|
94
|
+
started_at,
|
|
95
|
+
completed_at,
|
|
96
|
+
duration_seconds,
|
|
97
|
+
total_waves,
|
|
98
|
+
total_agents,
|
|
99
|
+
total_files_changed,
|
|
100
|
+
agent_summary,
|
|
101
|
+
waves,
|
|
102
|
+
effectiveness,
|
|
103
|
+
} = entry;
|
|
104
|
+
|
|
105
|
+
if (typeof effectiveness !== 'object' || effectiveness === null) {
|
|
106
|
+
throw new Error(`vault-mirror: session entry missing nested field 'effectiveness' (session_id=${session_id})`);
|
|
107
|
+
}
|
|
108
|
+
if (typeof agent_summary !== 'object' || agent_summary === null) {
|
|
109
|
+
throw new Error(`vault-mirror: session entry missing nested field 'agent_summary' (session_id=${session_id})`);
|
|
110
|
+
}
|
|
111
|
+
if (!Array.isArray(waves)) {
|
|
112
|
+
throw new Error(`vault-mirror: session entry missing nested field 'waves' (session_id=${session_id})`);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
const created = toDate(started_at);
|
|
116
|
+
const updated = toDate(completed_at);
|
|
117
|
+
const durationMin = Math.round((duration_seconds ?? 0) / 60);
|
|
118
|
+
const { planned_issues, completed, carryover, emergent, completion_rate } = effectiveness;
|
|
119
|
+
const ratePercent = Math.round(completion_rate * 100) + '%';
|
|
120
|
+
const { complete, partial, failed, spiral } = agent_summary;
|
|
121
|
+
|
|
122
|
+
const titleValue = `Session ${created} — ${session_type}`;
|
|
123
|
+
// Always quote session title — it contains em-dash
|
|
124
|
+
const title = `"${titleValue}"`;
|
|
125
|
+
|
|
126
|
+
// #602: session_type is interpolated raw upstream; slugify each tag segment
|
|
127
|
+
// and cap the combined tag at 64 chars so the frontmatter passes the vault
|
|
128
|
+
// tagPathRegex. The `id` is likewise slugified to a kebab slug below —
|
|
129
|
+
// entry.session_id may carry an ISO-timestamp uppercase `T`/`:`/`.`/`Z`.
|
|
130
|
+
const noteId = slugifyIdSafe(session_id) ?? session_id;
|
|
131
|
+
const tags = `[${buildTag(['session', session_type])}, ${buildTag(['status', 'verified'])}]`;
|
|
132
|
+
|
|
133
|
+
// Build wave table rows
|
|
134
|
+
const waveRows = waves
|
|
135
|
+
.map(
|
|
136
|
+
(w) =>
|
|
137
|
+
`| ${w.wave} | ${w.role} | ${w.agent_count} | ${w.files_changed} | ${w.quality} |`,
|
|
138
|
+
)
|
|
139
|
+
.join('\n');
|
|
140
|
+
|
|
141
|
+
// Skip-emit guard: avoid `platform: undefined` literal coercion (issue #343).
|
|
142
|
+
const platformBullet = (platform === null || platform === undefined || platform === '')
|
|
143
|
+
? ''
|
|
144
|
+
: ` · **Platform:** ${platform}`;
|
|
145
|
+
|
|
146
|
+
// #732: emit `source-repo` (the leak-guarded/pseudonym-mapped namespace from
|
|
147
|
+
// resolveRepoNamespace(), threaded through by process.mjs) instead of the
|
|
148
|
+
// legacy raw `repo` field — mirrors render-learnings.mjs's source-repo line.
|
|
149
|
+
const { repoNs } = options;
|
|
150
|
+
|
|
151
|
+
return `---
|
|
152
|
+
id: ${noteId}
|
|
153
|
+
type: session
|
|
154
|
+
title: ${title}
|
|
155
|
+
status: verified
|
|
156
|
+
created: ${created}
|
|
157
|
+
updated: ${updated}
|
|
158
|
+
tags: ${tags}
|
|
159
|
+
${fmLine('source-repo', repoNs)}_generator: ${GENERATOR_MARKER}
|
|
160
|
+
---
|
|
161
|
+
|
|
162
|
+
# Session ${session_id}
|
|
163
|
+
|
|
164
|
+
- **Type:** ${session_type}${platformBullet}
|
|
165
|
+
- **Duration:** ${durationMin}m (${started_at} → ${completed_at})
|
|
166
|
+
- **Waves:** ${total_waves} · **Agents:** ${total_agents} · **Files changed:** ${total_files_changed}
|
|
167
|
+
- **Effectiveness:** planned=${planned_issues}, completed=${completed}, carryover=${carryover}, emergent=${emergent}, rate=${ratePercent}
|
|
168
|
+
|
|
169
|
+
## Wave breakdown
|
|
170
|
+
|
|
171
|
+
| Wave | Role | Agents | Files | Quality |
|
|
172
|
+
|------|------|--------|-------|---------|
|
|
173
|
+
${waveRows}
|
|
174
|
+
|
|
175
|
+
## Agent summary
|
|
176
|
+
|
|
177
|
+
- Complete: ${complete} · Partial: ${partial} · Failed: ${failed} · Spiral: ${spiral}
|
|
178
|
+
`;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
export function generateSessionNoteV2(entry, options = {}) {
|
|
182
|
+
const REQUIRED_SESSION_V2_FIELDS = ['session_id', 'session_type', 'started_at', 'completed_at', 'waves', 'files_changed', 'effectiveness'];
|
|
183
|
+
for (const field of REQUIRED_SESSION_V2_FIELDS) {
|
|
184
|
+
if (entry[field] === null || entry[field] === undefined) {
|
|
185
|
+
throw new Error(`vault-mirror: session entry missing required field '${field}' (session_id=${entry.session_id ?? '<no session_id>'})`);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
if (!Array.isArray(entry.waves)) {
|
|
189
|
+
throw new Error(`vault-mirror: session entry 'waves' must be an array (session_id=${entry.session_id})`);
|
|
190
|
+
}
|
|
191
|
+
if (typeof entry.effectiveness !== 'object' || entry.effectiveness === null) {
|
|
192
|
+
throw new Error(`vault-mirror: session entry missing nested field 'effectiveness' (session_id=${entry.session_id})`);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
const { session_id, session_type, started_at, completed_at, duration_seconds, branch, planned_issues, waves, files_changed, issues_closed, issues_created, effectiveness, notes } = entry;
|
|
196
|
+
|
|
197
|
+
const created = toDate(started_at);
|
|
198
|
+
const updated = toDate(completed_at);
|
|
199
|
+
const durationMin = Math.round((duration_seconds ?? 0) / 60);
|
|
200
|
+
|
|
201
|
+
// Derive v1-equivalent aggregates from v2 wave structure
|
|
202
|
+
const totalWaves = waves.length;
|
|
203
|
+
const totalAgents = waves.reduce((acc, w) => acc + (w.agents ?? 0), 0);
|
|
204
|
+
const complete = waves.reduce((acc, w) => acc + (w.agents_done ?? 0), 0);
|
|
205
|
+
const partial = waves.reduce((acc, w) => acc + (w.agents_partial ?? 0), 0);
|
|
206
|
+
const failed = waves.reduce((acc, w) => acc + (w.agents_failed ?? 0), 0);
|
|
207
|
+
|
|
208
|
+
const completionRate = effectiveness.completion_rate;
|
|
209
|
+
const ratePercent = typeof completionRate === 'number' ? Math.round(completionRate * 100) + '%' : 'n/a';
|
|
210
|
+
const carryover = effectiveness.carryover ?? 'n/a';
|
|
211
|
+
|
|
212
|
+
const titleValue = `Session ${created} — ${session_type}`;
|
|
213
|
+
const title = `"${titleValue}"`;
|
|
214
|
+
|
|
215
|
+
// #602: session_type is interpolated raw upstream; slugify each tag segment
|
|
216
|
+
// and cap the combined tag at 64 chars so the frontmatter passes the vault
|
|
217
|
+
// tagPathRegex. The `id` is likewise slugified to a kebab slug below —
|
|
218
|
+
// entry.session_id may carry an ISO-timestamp uppercase `T`/`:`/`.`/`Z`.
|
|
219
|
+
const noteId = slugifyIdSafe(session_id) ?? session_id;
|
|
220
|
+
const tags = `[${buildTag(['session', session_type])}, ${buildTag(['status', 'verified'])}]`;
|
|
221
|
+
|
|
222
|
+
const waveRows = waves
|
|
223
|
+
.map((w) => `| ${w.wave} | ${w.role} | ${w.agents ?? '?'} | ${w.dispatch ?? '?'} | ${w.duration_s ?? '?'}s | ${w.agents_done ?? 0}/${w.agents_partial ?? 0}/${w.agents_failed ?? 0} |`)
|
|
224
|
+
.join('\n');
|
|
225
|
+
|
|
226
|
+
const closedList = Array.isArray(issues_closed) && issues_closed.length ? issues_closed.join(', ') : '—';
|
|
227
|
+
const createdList = Array.isArray(issues_created) && issues_created.length ? issues_created.join(', ') : '—';
|
|
228
|
+
const branchLine = branch ? ` · **Branch:** ${branch}` : '';
|
|
229
|
+
const notesBlock = notes ? `\n## Notes\n\n${notes}\n` : '';
|
|
230
|
+
|
|
231
|
+
// #732: emit `source-repo` (leak-guarded/pseudonym-mapped) instead of the
|
|
232
|
+
// legacy raw `repo` field — see generateSessionNote for the full rationale.
|
|
233
|
+
const { repoNs } = options;
|
|
234
|
+
|
|
235
|
+
return `---
|
|
236
|
+
id: ${noteId}
|
|
237
|
+
type: session
|
|
238
|
+
title: ${title}
|
|
239
|
+
status: verified
|
|
240
|
+
created: ${created}
|
|
241
|
+
updated: ${updated}
|
|
242
|
+
tags: ${tags}
|
|
243
|
+
${fmLine('source-repo', repoNs)}_generator: ${GENERATOR_MARKER}
|
|
244
|
+
---
|
|
245
|
+
|
|
246
|
+
# Session ${session_id}
|
|
247
|
+
|
|
248
|
+
- **Type:** ${session_type}${branchLine}
|
|
249
|
+
- **Duration:** ${durationMin}m (${started_at} → ${completed_at})
|
|
250
|
+
- **Waves:** ${totalWaves} · **Agents:** ${totalAgents} · **Files changed:** ${files_changed}
|
|
251
|
+
- **Effectiveness:** planned=${planned_issues ?? 'n/a'}, carryover=${carryover}, rate=${ratePercent}
|
|
252
|
+
- **Issues closed:** ${closedList}
|
|
253
|
+
- **Issues created:** ${createdList}
|
|
254
|
+
|
|
255
|
+
## Wave breakdown
|
|
256
|
+
|
|
257
|
+
| Wave | Role | Agents | Dispatch | Duration | done/partial/failed |
|
|
258
|
+
|------|------|--------|----------|----------|---------------------|
|
|
259
|
+
${waveRows}
|
|
260
|
+
|
|
261
|
+
## Agent summary
|
|
262
|
+
|
|
263
|
+
- Complete: ${complete} · Partial: ${partial} · Failed: ${failed}
|
|
264
|
+
${notesBlock}`;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
/**
|
|
268
|
+
* v3 generator — coordinator-direct session records (#491).
|
|
269
|
+
*
|
|
270
|
+
* These records carry `waves` as a scalar count and `agents_dispatched` as a
|
|
271
|
+
* scalar (no per-wave array), plus rich top-level metadata (issues_closed,
|
|
272
|
+
* follow_ups_filed, commits, tests_total_*). They are what session-end actually
|
|
273
|
+
* writes to sessions.jsonl. The v1/v2 generators require `waves` to be an array
|
|
274
|
+
* and reject this shape, so it gets its own renderer.
|
|
275
|
+
*/
|
|
276
|
+
export function generateSessionNoteV3(entry, options = {}) {
|
|
277
|
+
const REQUIRED_SESSION_V3_FIELDS = ['session_id', 'session_type', 'started_at', 'completed_at', 'waves', 'effectiveness'];
|
|
278
|
+
for (const field of REQUIRED_SESSION_V3_FIELDS) {
|
|
279
|
+
if (entry[field] === null || entry[field] === undefined) {
|
|
280
|
+
throw new Error(`vault-mirror: session entry missing required field '${field}' (session_id=${entry.session_id ?? '<no session_id>'})`);
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
if (typeof entry.waves !== 'number') {
|
|
284
|
+
throw new Error(`vault-mirror: session entry 'waves' must be a number (session_id=${entry.session_id})`);
|
|
285
|
+
}
|
|
286
|
+
if (typeof entry.effectiveness !== 'object' || entry.effectiveness === null) {
|
|
287
|
+
throw new Error(`vault-mirror: session entry missing nested field 'effectiveness' (session_id=${entry.session_id})`);
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
const {
|
|
291
|
+
session_id, session_type, platform, branch, started_at, completed_at,
|
|
292
|
+
duration_minutes, duration_seconds, waves, agents_dispatched, agent_summary,
|
|
293
|
+
planned_issues, effectiveness, commits, issues_closed, issues_created,
|
|
294
|
+
follow_ups_filed, tests_total_pre, tests_total_post, tests_added, notes,
|
|
295
|
+
} = entry;
|
|
296
|
+
|
|
297
|
+
const created = toDate(started_at);
|
|
298
|
+
const updated = toDate(completed_at);
|
|
299
|
+
const durationMin = duration_minutes ?? Math.round((duration_seconds ?? 0) / 60);
|
|
300
|
+
|
|
301
|
+
const completionRate = effectiveness.completion_rate;
|
|
302
|
+
const ratePercent = typeof completionRate === 'number' ? Math.round(completionRate * 100) + '%' : 'n/a';
|
|
303
|
+
const completed = effectiveness.completed_issues ?? effectiveness.completed ?? 'n/a';
|
|
304
|
+
const carryover = effectiveness.carryover ?? 'n/a';
|
|
305
|
+
const emergent = effectiveness.unplanned_finds ?? effectiveness.emergent ?? 0;
|
|
306
|
+
|
|
307
|
+
const agentsValue = typeof agents_dispatched === 'number' ? agents_dispatched : 'n/a';
|
|
308
|
+
const as = agent_summary && typeof agent_summary === 'object' ? agent_summary : {};
|
|
309
|
+
const { complete = 0, partial = 0, failed = 0, spiral = 0 } = as;
|
|
310
|
+
|
|
311
|
+
const fmtIssues = (list) =>
|
|
312
|
+
Array.isArray(list) && list.length ? list.map((i) => `#${i}`).join(', ') : '—';
|
|
313
|
+
const closedList = fmtIssues(issues_closed);
|
|
314
|
+
const createdList = fmtIssues(issues_created);
|
|
315
|
+
const followList = fmtIssues(follow_ups_filed);
|
|
316
|
+
const commitCount = Array.isArray(commits) ? commits.length : 0;
|
|
317
|
+
const testsDelta =
|
|
318
|
+
typeof tests_total_pre === 'number' && typeof tests_total_post === 'number'
|
|
319
|
+
? `${tests_total_pre} → ${tests_total_post}`
|
|
320
|
+
: typeof tests_added === 'number'
|
|
321
|
+
? `+${tests_added}`
|
|
322
|
+
: '—';
|
|
323
|
+
|
|
324
|
+
const titleValue = `Session ${created} — ${session_type}`;
|
|
325
|
+
const title = `"${titleValue}"`;
|
|
326
|
+
// #602: session_type is interpolated raw upstream; slugify each tag segment
|
|
327
|
+
// and cap the combined tag at 64 chars so the frontmatter passes the vault
|
|
328
|
+
// tagPathRegex. The `id` is likewise slugified to a kebab slug below —
|
|
329
|
+
// entry.session_id may carry an ISO-timestamp uppercase `T`/`:`/`.`/`Z`.
|
|
330
|
+
const noteId = slugifyIdSafe(session_id) ?? session_id;
|
|
331
|
+
const tags = `[${buildTag(['session', session_type])}, ${buildTag(['status', 'verified'])}]`;
|
|
332
|
+
|
|
333
|
+
const platformBullet = platform === null || platform === undefined || platform === '' ? '' : ` · **Platform:** ${platform}`;
|
|
334
|
+
const branchLine = branch ? ` · **Branch:** ${branch}` : '';
|
|
335
|
+
const notesBlock = notes ? `\n## Notes\n\n${notes}\n` : '';
|
|
336
|
+
|
|
337
|
+
// #732: emit `source-repo` (leak-guarded/pseudonym-mapped) instead of the
|
|
338
|
+
// legacy raw `repo` field — see generateSessionNote for the full rationale.
|
|
339
|
+
const { repoNs } = options;
|
|
340
|
+
|
|
341
|
+
return `---
|
|
342
|
+
id: ${noteId}
|
|
343
|
+
type: session
|
|
344
|
+
title: ${title}
|
|
345
|
+
status: verified
|
|
346
|
+
created: ${created}
|
|
347
|
+
updated: ${updated}
|
|
348
|
+
tags: ${tags}
|
|
349
|
+
${fmLine('source-repo', repoNs)}_generator: ${GENERATOR_MARKER}
|
|
350
|
+
---
|
|
351
|
+
|
|
352
|
+
# Session ${session_id}
|
|
353
|
+
|
|
354
|
+
- **Type:** ${session_type}${branchLine}${platformBullet}
|
|
355
|
+
- **Duration:** ${durationMin}m (${started_at} → ${completed_at})
|
|
356
|
+
- **Waves:** ${waves} · **Agents:** ${agentsValue} · **Commits:** ${commitCount}
|
|
357
|
+
- **Effectiveness:** planned=${planned_issues ?? 'n/a'}, completed=${completed}, carryover=${carryover}, emergent=${emergent}, rate=${ratePercent}
|
|
358
|
+
- **Tests:** ${testsDelta}
|
|
359
|
+
- **Issues closed:** ${closedList}
|
|
360
|
+
- **Issues created:** ${createdList}
|
|
361
|
+
- **Follow-ups filed:** ${followList}
|
|
362
|
+
|
|
363
|
+
## Agent summary
|
|
364
|
+
|
|
365
|
+
- Complete: ${complete} · Partial: ${partial} · Failed: ${failed} · Spiral: ${spiral}
|
|
366
|
+
${notesBlock}`;
|
|
367
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* render.mjs — Re-export barrel for vault-mirror render modules (Issue #283 split).
|
|
3
|
+
*
|
|
4
|
+
* Consumers can import from this file or directly from render-learnings.mjs / render-sessions.mjs.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
export { detectLearningSchema, generateLearningNote, generateLearningNoteV2 } from './render-learnings.mjs';
|
|
8
|
+
export { detectSessionSchema, generateSessionNote, generateSessionNoteV2, generateSessionNoteV3 } from './render-sessions.mjs';
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* utils.mjs — Shared utilities for vault-mirror (Issue #283 split).
|
|
3
|
+
*
|
|
4
|
+
* Exports: subjectToSlug, isValidSlug, uuidPrefix8, toDate,
|
|
5
|
+
* truncateAtWord, yamlQuoteIfNeeded, parseFrontmatter,
|
|
6
|
+
* slugifyTagSegment, buildTag, slugifyIdSafe, TAG_MAX_LENGTH,
|
|
7
|
+
* resolveSourceSessionLink
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { parseSessionId } from '../session-id.mjs';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Convert a subject string to a kebab slug.
|
|
14
|
+
* - If subject contains slashes, collapse to last path segment.
|
|
15
|
+
* - Replace dots and underscores with hyphens.
|
|
16
|
+
* - Strip all non-[a-z0-9-] chars.
|
|
17
|
+
* - Collapse consecutive hyphens.
|
|
18
|
+
* - Trim leading/trailing hyphens.
|
|
19
|
+
*/
|
|
20
|
+
export function subjectToSlug(subject) {
|
|
21
|
+
let s = subject;
|
|
22
|
+
|
|
23
|
+
// Collapse slash paths to last segment
|
|
24
|
+
if (s.includes('/')) {
|
|
25
|
+
const parts = s.split('/').filter(Boolean);
|
|
26
|
+
s = parts[parts.length - 1];
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Normalise: lowercase, dots/underscores → hyphens, strip invalid chars
|
|
30
|
+
s = s
|
|
31
|
+
.toLowerCase()
|
|
32
|
+
.replace(/[._]/g, '-')
|
|
33
|
+
.replace(/[^a-z0-9-]/g, '')
|
|
34
|
+
.replace(/-+/g, '-')
|
|
35
|
+
.replace(/^-+|-+$/g, '');
|
|
36
|
+
|
|
37
|
+
return s;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const slugRegex = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Validate that `s` is a filesystem-safe kebab slug.
|
|
44
|
+
*
|
|
45
|
+
* Guards against RegExp's implicit ToString coercion (issue #718): without
|
|
46
|
+
* the `typeof` check, `slugRegex.test(undefined)` coerces to the literal
|
|
47
|
+
* string "undefined" — which matches `^[a-z0-9]+(?:-[a-z0-9]+)*$` — and
|
|
48
|
+
* `isValidSlug(undefined)` returns `true`. Callers (e.g. `processSession` in
|
|
49
|
+
* process.mjs) rely on `isValidSlug(rawSessionId) === false` to route
|
|
50
|
+
* missing/non-string ids to the uuid-based synthesizer; the coercion trap
|
|
51
|
+
* let `undefined` through as a "valid" id and crashed downstream on
|
|
52
|
+
* `session_id.length`.
|
|
53
|
+
*
|
|
54
|
+
* @param {unknown} s
|
|
55
|
+
* @returns {boolean}
|
|
56
|
+
*/
|
|
57
|
+
export function isValidSlug(s) {
|
|
58
|
+
return typeof s === 'string' && slugRegex.test(s);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Max length of a single vault tag string. Mirrors the authoritative
|
|
63
|
+
* vaultFrontmatterSchema (`tags[].max(64)` in skills/vault-sync/validator.mjs).
|
|
64
|
+
*/
|
|
65
|
+
export const TAG_MAX_LENGTH = 64;
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Slugify ONE tag segment to a kebab slug matching the schema's per-segment
|
|
69
|
+
* regex (`/^[a-z0-9]+(?:-[a-z0-9]+)*$/`).
|
|
70
|
+
*
|
|
71
|
+
* Unlike {@link subjectToSlug}, this does NOT collapse slashes — callers pass a
|
|
72
|
+
* single segment at a time so {@link buildTag} can preserve the `/`-separated
|
|
73
|
+
* tag hierarchy. Whitespace, dots and underscores become hyphens (so
|
|
74
|
+
* "Deep Mode" → "deep-mode"); all other non-`[a-z0-9-]` chars are stripped. An
|
|
75
|
+
* empty result falls back to "unknown" so the enclosing tag never contains an
|
|
76
|
+
* empty `/`-segment (which would violate tagPathRegex).
|
|
77
|
+
*
|
|
78
|
+
* @param {string} input
|
|
79
|
+
* @returns {string}
|
|
80
|
+
*/
|
|
81
|
+
export function slugifyTagSegment(input) {
|
|
82
|
+
const slug = String(input ?? '')
|
|
83
|
+
.toLowerCase()
|
|
84
|
+
.replace(/[\s._]+/g, '-')
|
|
85
|
+
.replace(/[^a-z0-9-]/g, '')
|
|
86
|
+
.replace(/-+/g, '-')
|
|
87
|
+
.replace(/^-+|-+$/g, '');
|
|
88
|
+
return slug || 'unknown';
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Build a schema-valid vault tag from path segments (#602). Each segment is
|
|
93
|
+
* kebab-slugified (preserving the `/`-separated hierarchy), and the joined tag
|
|
94
|
+
* is capped at {@link TAG_MAX_LENGTH} characters. When truncation lands on a
|
|
95
|
+
* separator or hyphen, the trailing char is removed so the result still matches
|
|
96
|
+
* the schema's tagPathRegex.
|
|
97
|
+
*
|
|
98
|
+
* @param {Array<string>|string} segments
|
|
99
|
+
* @returns {string}
|
|
100
|
+
*/
|
|
101
|
+
export function buildTag(segments) {
|
|
102
|
+
const slugged = (Array.isArray(segments) ? segments : [segments]).map((s) =>
|
|
103
|
+
slugifyTagSegment(s),
|
|
104
|
+
);
|
|
105
|
+
if (slugged.length === 0) return 'unknown';
|
|
106
|
+
let tag = slugged.join('/');
|
|
107
|
+
if (tag.length > TAG_MAX_LENGTH) {
|
|
108
|
+
// Trim to the cap, then strip any trailing separator/hyphen exposed by the
|
|
109
|
+
// cut so the tail segment stays a valid kebab slug.
|
|
110
|
+
tag = tag.slice(0, TAG_MAX_LENGTH).replace(/[/-]+$/, '');
|
|
111
|
+
}
|
|
112
|
+
return tag;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Slugify a value for use as a vault note `id` (#602), guaranteeing the
|
|
117
|
+
* schema's kebab + min-length-2 contract. Returns null when no usable slug
|
|
118
|
+
* remains (caller treats this as an invalid entry).
|
|
119
|
+
*
|
|
120
|
+
* @param {string} input
|
|
121
|
+
* @returns {string|null}
|
|
122
|
+
*/
|
|
123
|
+
export function slugifyIdSafe(input) {
|
|
124
|
+
// Normalise whitespace to hyphens first (so "My ID" → "my-id" rather than
|
|
125
|
+
// "myid"), then delegate to subjectToSlug for the rest of the kebab contract.
|
|
126
|
+
const normalised = String(input ?? '').replace(/\s+/g, '-');
|
|
127
|
+
const slug = subjectToSlug(normalised);
|
|
128
|
+
if (!slug) return null;
|
|
129
|
+
// Schema requires min length 2; pad a single-char slug deterministically.
|
|
130
|
+
return slug.length >= 2 ? slug : `${slug}-x`;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/** Derive the first 8 chars of a UUID (strip hyphens, take first 8 hex chars). */
|
|
134
|
+
export function uuidPrefix8(id) {
|
|
135
|
+
return id.replace(/-/g, '').slice(0, 8);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/** Format a UTC ISO date string as YYYY-MM-DD. */
|
|
139
|
+
export function toDate(isoString) {
|
|
140
|
+
if (!isoString) return '';
|
|
141
|
+
return isoString.slice(0, 10);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/** Truncate a string to maxLen chars, ending at a word boundary. */
|
|
145
|
+
export function truncateAtWord(str, maxLen) {
|
|
146
|
+
if (str.length <= maxLen) return str;
|
|
147
|
+
const truncated = str.slice(0, maxLen);
|
|
148
|
+
const lastSpace = truncated.lastIndexOf(' ');
|
|
149
|
+
return lastSpace > 0 ? truncated.slice(0, lastSpace) : truncated;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/** Determine if a YAML title value needs quoting (contains : # \ or starts with -). */
|
|
153
|
+
export function yamlQuoteIfNeeded(value) {
|
|
154
|
+
if (/[:#{}[\],&*?|<>=!%@`\\]/.test(value) || value.startsWith('-') || value.startsWith('"')) {
|
|
155
|
+
return `"${value.replace(/\\/g, '\\\\').replace(/"/g, '\\"')}"`;
|
|
156
|
+
}
|
|
157
|
+
return value;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// ── Source-session link resolver (Issue #704) ─────────────────────────────────
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Decide whether a learning's source_session should be emitted as an Obsidian
|
|
164
|
+
* wiki-link or as plain text.
|
|
165
|
+
*
|
|
166
|
+
* When `opts.noteExists` is provided (a function `(slug: string) => boolean`),
|
|
167
|
+
* the decision is EXISTENCE-based: the target slug is looked up in the caller's
|
|
168
|
+
* known-sessions Set built from the real vault's 50-sessions directory. This is
|
|
169
|
+
* the authoritative path used by process.mjs and eliminates false-negatives for
|
|
170
|
+
* legacy/HHmm session IDs whose vault notes exist but don't match the strict
|
|
171
|
+
* semantic regex (e.g. `main-2026-04-23-1255`, `main-2026-05-25-deep`).
|
|
172
|
+
*
|
|
173
|
+
* Without the predicate (unit-test / vault-less run), the function falls back to
|
|
174
|
+
* strict format-validity (semantic `<branch>-<YYYY-MM-DD>-<mode>-<N>` or UUID-v4)
|
|
175
|
+
* — identical to the Wave-2 behaviour, never worse.
|
|
176
|
+
*
|
|
177
|
+
* @param {unknown} source_session
|
|
178
|
+
* @param {{ noteExists?: (slug: string) => boolean }} [opts]
|
|
179
|
+
* @returns {{ isLink: boolean, target: string }}
|
|
180
|
+
*/
|
|
181
|
+
export function resolveSourceSessionLink(source_session, opts = {}) {
|
|
182
|
+
const raw = source_session === null || source_session === undefined ? '' : String(source_session).trim();
|
|
183
|
+
if (!raw || raw === 'unknown') return { isLink: false, target: raw || 'unknown' };
|
|
184
|
+
// Provenance tags like "agent-proposed@wave-1" are never session notes.
|
|
185
|
+
if (/@/.test(raw)) return { isLink: false, target: raw };
|
|
186
|
+
const slug = subjectToSlug(raw) || raw;
|
|
187
|
+
if (typeof opts.noteExists === 'function') {
|
|
188
|
+
// EXISTENCE-based (authoritative when a predicate is supplied).
|
|
189
|
+
return opts.noteExists(slug) ? { isLink: true, target: slug } : { isLink: false, target: raw };
|
|
190
|
+
}
|
|
191
|
+
// Fallback (no predicate, e.g. unit tests / no vault): strict format validity.
|
|
192
|
+
const parsed = parseSessionId(raw);
|
|
193
|
+
const ok = parsed !== null && (parsed.format === 'semantic' || parsed.format === 'uuid');
|
|
194
|
+
return ok ? { isLink: true, target: slug } : { isLink: false, target: raw };
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// ── Frontmatter parser (minimal — only reads the opening --- block) ───────────
|
|
198
|
+
|
|
199
|
+
export function parseFrontmatter(content) {
|
|
200
|
+
if (!content.startsWith('---')) return null;
|
|
201
|
+
const end = content.indexOf('\n---', 3);
|
|
202
|
+
if (end === -1) return null;
|
|
203
|
+
const block = content.slice(3, end).trim();
|
|
204
|
+
const result = {};
|
|
205
|
+
for (const line of block.split('\n')) {
|
|
206
|
+
const colonIdx = line.indexOf(':');
|
|
207
|
+
if (colonIdx === -1) continue;
|
|
208
|
+
const key = line.slice(0, colonIdx).trim();
|
|
209
|
+
let value = line.slice(colonIdx + 1).trim();
|
|
210
|
+
// Strip surrounding quotes
|
|
211
|
+
if ((value.startsWith('"') && value.endsWith('"')) || (value.startsWith("'") && value.endsWith("'"))) {
|
|
212
|
+
value = value.slice(1, -1);
|
|
213
|
+
}
|
|
214
|
+
result[key] = value;
|
|
215
|
+
}
|
|
216
|
+
return result;
|
|
217
|
+
}
|