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,404 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* vault-archive.mjs — reusable helper to archive a repo file into the Meta-Vault.
|
|
3
|
+
*
|
|
4
|
+
* Epic #774 (docs Public-Split): process records (PRDs, retros, session notes)
|
|
5
|
+
* move OUT of the public repo and INTO the Meta-Vault. This helper is the single
|
|
6
|
+
* shared mechanism for that move, consumed by TWO callers:
|
|
7
|
+
*
|
|
8
|
+
* 1. scripts/archive-closed-prds.mjs — the durable Epic-close routine (S8/#782)
|
|
9
|
+
* that runs as a `custom-phases:` entry, archiving PRDs whose Epic is closed.
|
|
10
|
+
* 2. The Wave-3 corpus mover (S3) — bulk-moves ~66 process records into the
|
|
11
|
+
* vault, passing the returned manifest back to the coordinator.
|
|
12
|
+
*
|
|
13
|
+
* The helper NEVER deletes the source (the caller decides: the CLI uses `git rm`
|
|
14
|
+
* under --apply; the W3 mover hands a manifest to the coordinator). It only
|
|
15
|
+
* COPIES the source into the vault with a generated, schema-valid frontmatter.
|
|
16
|
+
*
|
|
17
|
+
* ── Frontmatter schema (mirror, not import) ─────────────────────────────────
|
|
18
|
+
* The canonical vault frontmatter schema is the GENERATED Zod block in
|
|
19
|
+
* skills/vault-sync/validator.mjs (Z.104-134). That schema is NOT exported, and
|
|
20
|
+
* `zod` is not a dependency of scripts/ (it lives only as a skill-local shim in
|
|
21
|
+
* skills/vault-sync/node_modules/). So the field constraints below are a
|
|
22
|
+
* hand-rolled MIRROR of that schema. If the canonical schema changes, update the
|
|
23
|
+
* constants here to match. The mirror is proven equivalent by an integration
|
|
24
|
+
* test that runs the REAL validator subprocess over generated output
|
|
25
|
+
* (tests/lib/vault-archive.test.mjs § "REAL validator").
|
|
26
|
+
* ────────────────────────────────────────────────────────────────────────────
|
|
27
|
+
*/
|
|
28
|
+
|
|
29
|
+
import { readFileSync, writeFileSync, mkdirSync } from 'node:fs';
|
|
30
|
+
import { basename, dirname, isAbsolute, join, relative } from 'node:path';
|
|
31
|
+
import YAML from 'js-yaml';
|
|
32
|
+
|
|
33
|
+
import { expandTilde } from './common.mjs';
|
|
34
|
+
|
|
35
|
+
// ── Schema mirror (SSOT: skills/vault-sync/validator.mjs Z.104-134) ──────────
|
|
36
|
+
export const SLUG_RE = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
|
|
37
|
+
export const ISO_DATE_RE =
|
|
38
|
+
/^\d{4}-\d{2}-\d{2}(?:T\d{2}:\d{2}(?::\d{2}(?:\.\d+)?)?(?:Z|[+-]\d{2}:\d{2})?)?$/;
|
|
39
|
+
export const VAULT_TYPE_ENUM = Object.freeze([
|
|
40
|
+
'note',
|
|
41
|
+
'daily',
|
|
42
|
+
'project',
|
|
43
|
+
'person',
|
|
44
|
+
'reference',
|
|
45
|
+
'idea',
|
|
46
|
+
'learning',
|
|
47
|
+
'session',
|
|
48
|
+
'peer-card',
|
|
49
|
+
'board',
|
|
50
|
+
]);
|
|
51
|
+
export const VAULT_STATUS_ENUM = Object.freeze([
|
|
52
|
+
'draft',
|
|
53
|
+
'active',
|
|
54
|
+
'verified',
|
|
55
|
+
'archived',
|
|
56
|
+
'production',
|
|
57
|
+
'mvp',
|
|
58
|
+
'idea',
|
|
59
|
+
]);
|
|
60
|
+
|
|
61
|
+
const SOURCE_REPO = 'session-orchestrator';
|
|
62
|
+
const FRONTMATTER_RE = /^---\r?\n([\s\S]*?)\r?\n---\r?\n?/;
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Return today's date as an ISO YYYY-MM-DD string.
|
|
66
|
+
* @param {Date} [now] — injectable clock for deterministic tests.
|
|
67
|
+
* @returns {string}
|
|
68
|
+
*/
|
|
69
|
+
export function todayIso(now) {
|
|
70
|
+
const d = now instanceof Date ? now : new Date();
|
|
71
|
+
return d.toISOString().slice(0, 10);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Derive a collision-safe kebab-case vault `id` from a filename.
|
|
76
|
+
*
|
|
77
|
+
* Strips the `.md` extension, lowercases, collapses every non-alphanumeric run
|
|
78
|
+
* to a single hyphen, trims leading/trailing hyphens, and enforces the schema's
|
|
79
|
+
* min-2 / max-128 length. When a `takenIds` Set is provided, the returned slug
|
|
80
|
+
* is guaranteed unique against it (a numeric `-2`, `-3`, … suffix is appended on
|
|
81
|
+
* collision) and the chosen slug is registered into the Set.
|
|
82
|
+
*
|
|
83
|
+
* @param {string} filename
|
|
84
|
+
* @param {Set<string>} [takenIds] — mutated: the returned slug is added.
|
|
85
|
+
* @returns {string} a valid slug matching SLUG_RE (length 2..128).
|
|
86
|
+
*/
|
|
87
|
+
export function slugFromFilename(filename, takenIds) {
|
|
88
|
+
const stem = String(filename).replace(/\.md$/i, '');
|
|
89
|
+
let slug = stem
|
|
90
|
+
.toLowerCase()
|
|
91
|
+
.replace(/[^a-z0-9]+/g, '-')
|
|
92
|
+
.replace(/^-+|-+$/g, '');
|
|
93
|
+
|
|
94
|
+
if (!slug) slug = 'note';
|
|
95
|
+
if (slug.length < 2) slug = `${slug}-note`;
|
|
96
|
+
if (slug.length > 128) slug = slug.slice(0, 128).replace(/-+$/, '');
|
|
97
|
+
|
|
98
|
+
if (takenIds instanceof Set) {
|
|
99
|
+
let candidate = slug;
|
|
100
|
+
let n = 2;
|
|
101
|
+
while (takenIds.has(candidate)) {
|
|
102
|
+
const suffix = `-${n}`;
|
|
103
|
+
candidate = slug.slice(0, 128 - suffix.length).replace(/-+$/, '') + suffix;
|
|
104
|
+
n++;
|
|
105
|
+
}
|
|
106
|
+
slug = candidate;
|
|
107
|
+
takenIds.add(slug);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
return slug;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Validate a frontmatter field object against the mirrored schema.
|
|
115
|
+
* Returns { ok, errors[] } — never throws. Mirrors the REQUIRED fields plus the
|
|
116
|
+
* optional fields this helper emits; unknown keys are ignored (schema is
|
|
117
|
+
* `.passthrough()`).
|
|
118
|
+
*
|
|
119
|
+
* @param {Record<string, unknown>} f
|
|
120
|
+
* @returns {{ ok: boolean, errors: string[] }}
|
|
121
|
+
*/
|
|
122
|
+
export function validateFrontmatterFields(f) {
|
|
123
|
+
const errors = [];
|
|
124
|
+
if (
|
|
125
|
+
typeof f.id !== 'string' ||
|
|
126
|
+
!SLUG_RE.test(f.id) ||
|
|
127
|
+
f.id.length < 2 ||
|
|
128
|
+
f.id.length > 128
|
|
129
|
+
) {
|
|
130
|
+
errors.push(`invalid id: ${JSON.stringify(f.id)}`);
|
|
131
|
+
}
|
|
132
|
+
if (!VAULT_TYPE_ENUM.includes(f.type)) {
|
|
133
|
+
errors.push(`invalid type: ${JSON.stringify(f.type)}`);
|
|
134
|
+
}
|
|
135
|
+
if (typeof f.created !== 'string' || !ISO_DATE_RE.test(f.created)) {
|
|
136
|
+
errors.push(`invalid created: ${JSON.stringify(f.created)}`);
|
|
137
|
+
}
|
|
138
|
+
if (typeof f.updated !== 'string' || !ISO_DATE_RE.test(f.updated)) {
|
|
139
|
+
errors.push(`invalid updated: ${JSON.stringify(f.updated)}`);
|
|
140
|
+
}
|
|
141
|
+
if (
|
|
142
|
+
f.title !== undefined &&
|
|
143
|
+
(typeof f.title !== 'string' || f.title.length < 1 || f.title.length > 200)
|
|
144
|
+
) {
|
|
145
|
+
errors.push(`invalid title: ${JSON.stringify(f.title)}`);
|
|
146
|
+
}
|
|
147
|
+
if (f.status !== undefined && !VAULT_STATUS_ENUM.includes(f.status)) {
|
|
148
|
+
errors.push(`invalid status: ${JSON.stringify(f.status)}`);
|
|
149
|
+
}
|
|
150
|
+
if (f.tags !== undefined && !Array.isArray(f.tags)) {
|
|
151
|
+
errors.push('invalid tags (must be an array)');
|
|
152
|
+
}
|
|
153
|
+
return { ok: errors.length === 0, errors };
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Split a raw markdown document into its (optional) YAML frontmatter object and
|
|
158
|
+
* body. A file without a leading `---` fence returns { frontmatter: null }.
|
|
159
|
+
*
|
|
160
|
+
* @param {string} raw
|
|
161
|
+
* @returns {{ frontmatter: Record<string, unknown>|null, body: string, hadFrontmatter: boolean, parseError: boolean }}
|
|
162
|
+
*/
|
|
163
|
+
export function splitFrontmatter(raw) {
|
|
164
|
+
const m = String(raw).match(FRONTMATTER_RE);
|
|
165
|
+
if (!m) return { frontmatter: null, body: String(raw), hadFrontmatter: false, parseError: false };
|
|
166
|
+
|
|
167
|
+
let fm;
|
|
168
|
+
let parseError = false;
|
|
169
|
+
try {
|
|
170
|
+
fm = YAML.load(m[1]);
|
|
171
|
+
} catch {
|
|
172
|
+
fm = null;
|
|
173
|
+
parseError = true;
|
|
174
|
+
}
|
|
175
|
+
const frontmatter = fm && typeof fm === 'object' && !Array.isArray(fm) ? fm : {};
|
|
176
|
+
return { frontmatter, body: String(raw).slice(m[0].length), hadFrontmatter: true, parseError };
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Extract the first markdown H1 (`# Title`) from a document as a title candidate.
|
|
181
|
+
* @param {string} raw
|
|
182
|
+
* @returns {string|undefined}
|
|
183
|
+
*/
|
|
184
|
+
export function titleFromMarkdown(raw) {
|
|
185
|
+
const m = String(raw).match(/^#\s+(.+?)\s*$/m);
|
|
186
|
+
if (!m) return undefined;
|
|
187
|
+
return m[1].trim().slice(0, 200) || undefined;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* Build the validated archive frontmatter field object for a source file.
|
|
192
|
+
*
|
|
193
|
+
* Merges (when present) the source's existing frontmatter — the mandatory vault
|
|
194
|
+
* fields are filled/normalised WITHOUT producing a second frontmatter block.
|
|
195
|
+
* Field precedence: `overrides` > archival-mandated values (updated/status/
|
|
196
|
+
* source/source-repo) > valid existing values (id/type/created/title) > derived
|
|
197
|
+
* defaults. Throws when the resulting object fails the schema mirror.
|
|
198
|
+
*
|
|
199
|
+
* @param {object} opts
|
|
200
|
+
* @param {string} opts.sourcePath — absolute path of the file being archived.
|
|
201
|
+
* @param {string} [opts.title] — explicit title (else existing.title, else derived).
|
|
202
|
+
* @param {string|number} [opts.issueRef] — Epic/issue iid, recorded as `epic-ref`.
|
|
203
|
+
* @param {string} [opts.type='reference']
|
|
204
|
+
* @param {string} [opts.status='archived']
|
|
205
|
+
* @param {Date} [opts.now] — injectable clock.
|
|
206
|
+
* @param {string} [opts.repoRoot] — for computing the repo-relative `source`.
|
|
207
|
+
* @param {Record<string, unknown>|null} [opts.existing] — source's parsed frontmatter.
|
|
208
|
+
* @param {Set<string>} [opts.takenIds] — collision-safety registry for ids.
|
|
209
|
+
* @param {Record<string, unknown>} [opts.overrides] — final field overrides.
|
|
210
|
+
* @returns {Record<string, unknown>} validated, ordered field object.
|
|
211
|
+
*/
|
|
212
|
+
export function buildArchiveFields({
|
|
213
|
+
sourcePath,
|
|
214
|
+
title,
|
|
215
|
+
issueRef,
|
|
216
|
+
type = 'reference',
|
|
217
|
+
status = 'archived',
|
|
218
|
+
now,
|
|
219
|
+
repoRoot,
|
|
220
|
+
existing = null,
|
|
221
|
+
takenIds,
|
|
222
|
+
overrides = {},
|
|
223
|
+
} = {}) {
|
|
224
|
+
if (!sourcePath) throw new Error('vault-archive: sourcePath is required');
|
|
225
|
+
|
|
226
|
+
const filename = basename(sourcePath);
|
|
227
|
+
const dateStr = todayIso(now);
|
|
228
|
+
const base = existing && typeof existing === 'object' ? { ...existing } : {};
|
|
229
|
+
|
|
230
|
+
// id: keep a valid existing id, else derive a collision-safe slug.
|
|
231
|
+
let id;
|
|
232
|
+
if (typeof base.id === 'string' && SLUG_RE.test(base.id) && base.id.length >= 2 && base.id.length <= 128) {
|
|
233
|
+
id = base.id;
|
|
234
|
+
if (takenIds instanceof Set) takenIds.add(id);
|
|
235
|
+
} else {
|
|
236
|
+
id = slugFromFilename(filename, takenIds);
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
// type: keep a valid existing enum value, else the archival default.
|
|
240
|
+
const finalType = VAULT_TYPE_ENUM.includes(base.type) ? base.type : type;
|
|
241
|
+
|
|
242
|
+
// created: keep a valid existing date (original creation), else today.
|
|
243
|
+
const created =
|
|
244
|
+
typeof base.created === 'string' && ISO_DATE_RE.test(base.created) ? base.created : dateStr;
|
|
245
|
+
|
|
246
|
+
// title: explicit arg wins, else existing, else undefined (caller may derive).
|
|
247
|
+
let finalTitle = title;
|
|
248
|
+
if ((finalTitle === undefined || finalTitle === null || finalTitle === '') && typeof base.title === 'string') {
|
|
249
|
+
finalTitle = base.title;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
const merged = {
|
|
253
|
+
...base,
|
|
254
|
+
id,
|
|
255
|
+
type: finalType,
|
|
256
|
+
created,
|
|
257
|
+
updated: dateStr,
|
|
258
|
+
status,
|
|
259
|
+
source: repoRoot ? relative(repoRoot, sourcePath) : filename,
|
|
260
|
+
'source-repo': SOURCE_REPO,
|
|
261
|
+
};
|
|
262
|
+
if (finalTitle !== undefined && finalTitle !== null && String(finalTitle).length > 0) {
|
|
263
|
+
merged.title = String(finalTitle).slice(0, 200);
|
|
264
|
+
}
|
|
265
|
+
if (issueRef !== undefined && issueRef !== null && String(issueRef).length > 0) {
|
|
266
|
+
merged['epic-ref'] = String(issueRef);
|
|
267
|
+
}
|
|
268
|
+
Object.assign(merged, overrides);
|
|
269
|
+
|
|
270
|
+
const { ok, errors } = validateFrontmatterFields(merged);
|
|
271
|
+
if (!ok) {
|
|
272
|
+
throw new Error(`vault-archive: generated frontmatter invalid: ${errors.join('; ')}`);
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
// Deterministic, readable field order; preserve passthrough keys at the tail.
|
|
276
|
+
const ordered = {};
|
|
277
|
+
ordered.id = merged.id;
|
|
278
|
+
ordered.type = merged.type;
|
|
279
|
+
ordered.created = merged.created;
|
|
280
|
+
ordered.updated = merged.updated;
|
|
281
|
+
if (merged.title !== undefined) ordered.title = merged.title;
|
|
282
|
+
if (merged.status !== undefined) ordered.status = merged.status;
|
|
283
|
+
if (merged.tags !== undefined) ordered.tags = merged.tags;
|
|
284
|
+
ordered.source = merged.source;
|
|
285
|
+
ordered['source-repo'] = merged['source-repo'];
|
|
286
|
+
for (const [k, v] of Object.entries(merged)) {
|
|
287
|
+
if (!(k in ordered)) ordered[k] = v;
|
|
288
|
+
}
|
|
289
|
+
return ordered;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
/**
|
|
293
|
+
* Render a frontmatter field object into a `---\n…\n---\n` YAML block string.
|
|
294
|
+
* @param {Record<string, unknown>} fields
|
|
295
|
+
* @returns {string}
|
|
296
|
+
*/
|
|
297
|
+
export function renderFrontmatter(fields) {
|
|
298
|
+
const yaml = YAML.dump(fields, { lineWidth: -1, sortKeys: false, noRefs: true });
|
|
299
|
+
return `---\n${yaml}---\n`;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
/**
|
|
303
|
+
* Generate the archive frontmatter STRING for a source file. Thin wrapper over
|
|
304
|
+
* buildArchiveFields + renderFrontmatter — validates and throws on invalid.
|
|
305
|
+
*
|
|
306
|
+
* @param {Parameters<typeof buildArchiveFields>[0]} opts — see buildArchiveFields.
|
|
307
|
+
* @returns {string} a `---\n…\n---\n` block.
|
|
308
|
+
*/
|
|
309
|
+
export function generateArchiveFrontmatter(opts = {}) {
|
|
310
|
+
return renderFrontmatter(buildArchiveFields(opts));
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
/**
|
|
314
|
+
* Archive a repo file into the vault by copying it with a generated, schema-valid
|
|
315
|
+
* frontmatter. Does NOT delete the source. Under `dryRun` (default true) NOTHING
|
|
316
|
+
* is written — the returned manifest entry describes the planned move only.
|
|
317
|
+
*
|
|
318
|
+
* If the source already carries YAML frontmatter, its mandatory vault fields are
|
|
319
|
+
* merged in place (no double `---` block).
|
|
320
|
+
*
|
|
321
|
+
* @param {object} opts
|
|
322
|
+
* @param {string} [opts.repoRoot] — repo root for the repo-relative `source`/manifest.
|
|
323
|
+
* @param {string} opts.vaultDir — absolute or `~`-prefixed vault root (REQUIRED);
|
|
324
|
+
* a leading `~` is expanded via {@link expandTilde} before use.
|
|
325
|
+
* @param {string} opts.sourcePath — absolute path of the file to archive (REQUIRED).
|
|
326
|
+
* @param {string} opts.targetSubdir — vault-relative destination directory.
|
|
327
|
+
* @param {Record<string, unknown>} [opts.frontmatterOverrides] — final field overrides.
|
|
328
|
+
* @param {boolean} [opts.dryRun=true]
|
|
329
|
+
* @param {Date} [opts.now]
|
|
330
|
+
* @param {Set<string>} [opts.takenIds]
|
|
331
|
+
* @param {string} [opts.title]
|
|
332
|
+
* @param {string|number} [opts.issueRef]
|
|
333
|
+
* @param {string} [opts.type]
|
|
334
|
+
* @param {string} [opts.status]
|
|
335
|
+
* @returns {{ source: string, target: string, action: 'archived'|'would-archive', id: string }}
|
|
336
|
+
*/
|
|
337
|
+
export function archiveFileToVault({
|
|
338
|
+
repoRoot,
|
|
339
|
+
vaultDir,
|
|
340
|
+
sourcePath,
|
|
341
|
+
targetSubdir = '',
|
|
342
|
+
frontmatterOverrides = {},
|
|
343
|
+
dryRun = true,
|
|
344
|
+
now,
|
|
345
|
+
takenIds,
|
|
346
|
+
title,
|
|
347
|
+
issueRef,
|
|
348
|
+
type,
|
|
349
|
+
status,
|
|
350
|
+
} = {}) {
|
|
351
|
+
if (!vaultDir) throw new Error('vault-archive: vaultDir is required');
|
|
352
|
+
if (!sourcePath) throw new Error('vault-archive: sourcePath is required');
|
|
353
|
+
|
|
354
|
+
const filename = basename(sourcePath);
|
|
355
|
+
const targetRel = join(targetSubdir, filename);
|
|
356
|
+
// Expand a `~`-prefixed vaultDir (e.g. the committed Session Config default
|
|
357
|
+
// `~/Projects/vault`) before joining — without this, a host lacking
|
|
358
|
+
// owner.yaml/SO_VAULT_DIR writes into a literal `./~` directory instead of
|
|
359
|
+
// the real home-relative vault (single seam covers both the
|
|
360
|
+
// archive-closed-prds CLI and the docs-Public-Split corpus mover).
|
|
361
|
+
const vaultRoot = expandTilde(vaultDir);
|
|
362
|
+
const targetAbs = join(vaultRoot, targetSubdir, filename);
|
|
363
|
+
|
|
364
|
+
// #793 GAP-3: containment guard — a caller-supplied targetSubdir (e.g.
|
|
365
|
+
// `../../etc`) must not resolve outside the vault root. relative()-based,
|
|
366
|
+
// not a bare startsWith(vaultRoot) prefix check — a sibling directory that
|
|
367
|
+
// merely shares a string prefix (`/vault-evil` vs `/vault`) would otherwise
|
|
368
|
+
// slip past a naive check. Runs before any write AND before the dry-run
|
|
369
|
+
// report, so a planned escape is refused rather than silently manifested.
|
|
370
|
+
const relToVault = relative(vaultRoot, targetAbs);
|
|
371
|
+
if (relToVault.startsWith('..') || isAbsolute(relToVault)) {
|
|
372
|
+
throw new Error(`vault-archive: targetSubdir '${targetSubdir}' escapes vault root`);
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
const raw = readFileSync(sourcePath, 'utf8');
|
|
376
|
+
const { frontmatter: existing, body } = splitFrontmatter(raw);
|
|
377
|
+
|
|
378
|
+
const fields = buildArchiveFields({
|
|
379
|
+
sourcePath,
|
|
380
|
+
title: title ?? titleFromMarkdown(body),
|
|
381
|
+
issueRef,
|
|
382
|
+
type,
|
|
383
|
+
status,
|
|
384
|
+
now,
|
|
385
|
+
repoRoot,
|
|
386
|
+
existing,
|
|
387
|
+
takenIds,
|
|
388
|
+
overrides: frontmatterOverrides,
|
|
389
|
+
});
|
|
390
|
+
|
|
391
|
+
const content = renderFrontmatter(fields) + body;
|
|
392
|
+
|
|
393
|
+
if (!dryRun) {
|
|
394
|
+
mkdirSync(dirname(targetAbs), { recursive: true });
|
|
395
|
+
writeFileSync(targetAbs, content, 'utf8');
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
return {
|
|
399
|
+
source: repoRoot ? relative(repoRoot, sourcePath) : sourcePath,
|
|
400
|
+
target: targetRel,
|
|
401
|
+
action: dryRun ? 'would-archive' : 'archived',
|
|
402
|
+
id: fields.id,
|
|
403
|
+
};
|
|
404
|
+
}
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* glab.mjs — GitLab CLI helpers for vault-backfill.
|
|
3
|
+
*
|
|
4
|
+
* All functions shell out to `glab`; never throw — return ok:false on failure.
|
|
5
|
+
* Part of scripts/vault-backfill.mjs (Issue #241).
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { spawnSync } from 'node:child_process';
|
|
9
|
+
|
|
10
|
+
let _verbose = false;
|
|
11
|
+
|
|
12
|
+
/** Enable verbose stderr logging. */
|
|
13
|
+
export function setVerbose(v) {
|
|
14
|
+
_verbose = v;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function vlog(msg) {
|
|
18
|
+
if (_verbose) process.stderr.write(`[vault-backfill:verbose] ${msg}\n`);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Check glab is on PATH. Calls dieFn(1, ...) if missing.
|
|
23
|
+
* Uses `glab --version` (no shell:true) to avoid the DEP0190 warning.
|
|
24
|
+
*/
|
|
25
|
+
export function assertGlabExists(dieFn) {
|
|
26
|
+
const result = spawnSync('glab', ['--version'], { encoding: 'utf8' });
|
|
27
|
+
if (result.error || result.status !== 0) {
|
|
28
|
+
dieFn(
|
|
29
|
+
1,
|
|
30
|
+
'glab CLI not found — install per https://gitlab.com/gitlab-org/cli\n' +
|
|
31
|
+
' (macOS: brew install glab)',
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
vlog('glab CLI found on PATH');
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Run a glab command, return { ok, stdout, stderr }.
|
|
39
|
+
*/
|
|
40
|
+
export function glabRun(glabArgs) {
|
|
41
|
+
vlog(`glab ${glabArgs.join(' ')}`);
|
|
42
|
+
const result = spawnSync('glab', glabArgs, {
|
|
43
|
+
encoding: 'utf8',
|
|
44
|
+
maxBuffer: 10 * 1024 * 1024,
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
if (result.error) {
|
|
48
|
+
return { ok: false, stdout: '', stderr: result.error.message };
|
|
49
|
+
}
|
|
50
|
+
return {
|
|
51
|
+
ok: result.status === 0,
|
|
52
|
+
stdout: result.stdout || '',
|
|
53
|
+
stderr: result.stderr || '',
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Parse `glab repo list --output json` output (JSON array or JSONL).
|
|
59
|
+
* Returns array of { id, path, name, visibility, createdAt }.
|
|
60
|
+
*/
|
|
61
|
+
export function parseRepoList(stdout) {
|
|
62
|
+
const trimmed = stdout.trim();
|
|
63
|
+
if (!trimmed) return [];
|
|
64
|
+
|
|
65
|
+
try {
|
|
66
|
+
const data = JSON.parse(trimmed);
|
|
67
|
+
if (!Array.isArray(data)) return [];
|
|
68
|
+
return data.map((r) => ({
|
|
69
|
+
id: r.id ?? 0,
|
|
70
|
+
path: r.path_with_namespace ?? r.path ?? '',
|
|
71
|
+
name: r.name ?? '',
|
|
72
|
+
visibility: r.visibility ?? 'private',
|
|
73
|
+
createdAt: (r.created_at ?? '').slice(0, 10),
|
|
74
|
+
}));
|
|
75
|
+
} catch {
|
|
76
|
+
// Try line-by-line JSONL
|
|
77
|
+
const repos = [];
|
|
78
|
+
for (const line of trimmed.split('\n')) {
|
|
79
|
+
const l = line.trim();
|
|
80
|
+
if (!l) continue;
|
|
81
|
+
try {
|
|
82
|
+
const r = JSON.parse(l);
|
|
83
|
+
repos.push({
|
|
84
|
+
id: r.id ?? 0,
|
|
85
|
+
path: r.path_with_namespace ?? r.path ?? '',
|
|
86
|
+
name: r.name ?? '',
|
|
87
|
+
visibility: r.visibility ?? 'private',
|
|
88
|
+
createdAt: (r.created_at ?? '').slice(0, 10),
|
|
89
|
+
});
|
|
90
|
+
} catch {
|
|
91
|
+
// skip malformed line
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
return repos;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* List all repos in a GitLab group. Returns repo array or null on API error.
|
|
100
|
+
*/
|
|
101
|
+
export function listGroupRepos(group) {
|
|
102
|
+
const { ok, stdout, stderr } = glabRun([
|
|
103
|
+
'repo', 'list', '-g', group, '--output', 'json', '--per-page', '100',
|
|
104
|
+
]);
|
|
105
|
+
|
|
106
|
+
if (!ok) {
|
|
107
|
+
process.stderr.write(
|
|
108
|
+
`[vault-backfill] WARN: glab repo list failed for group '${group}': ${stderr.trim()}\n`,
|
|
109
|
+
);
|
|
110
|
+
return null;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
return parseRepoList(stdout);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Check if a repo already has .vault.yaml via glab API.
|
|
118
|
+
* Returns: 'present' | 'absent' | 'error'
|
|
119
|
+
*/
|
|
120
|
+
export function checkVaultYaml(repoPath) {
|
|
121
|
+
const encodedPath = encodeURIComponent(repoPath);
|
|
122
|
+
const { ok, stderr, stdout } = glabRun([
|
|
123
|
+
'api', `projects/${encodedPath}/repository/files/.vault.yaml/raw`,
|
|
124
|
+
]);
|
|
125
|
+
|
|
126
|
+
if (ok) {
|
|
127
|
+
vlog(`${repoPath}: .vault.yaml present (${stdout.length} bytes)`);
|
|
128
|
+
return 'present';
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
if (
|
|
132
|
+
stderr.includes('404') ||
|
|
133
|
+
stderr.includes('not found') ||
|
|
134
|
+
stderr.includes('File Not Found')
|
|
135
|
+
) {
|
|
136
|
+
vlog(`${repoPath}: .vault.yaml absent (404)`);
|
|
137
|
+
return 'absent';
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
process.stderr.write(
|
|
141
|
+
`[vault-backfill] WARN: could not probe ${repoPath}: ${stderr.trim().slice(0, 120)}\n`,
|
|
142
|
+
);
|
|
143
|
+
return 'error';
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Fetch repo owner via glab API. Returns username or 'unknown' on failure.
|
|
148
|
+
*/
|
|
149
|
+
export function fetchRepoOwner(repoId) {
|
|
150
|
+
const { ok, stdout } = glabRun(['api', `projects/${repoId}`]);
|
|
151
|
+
if (!ok) return 'unknown';
|
|
152
|
+
|
|
153
|
+
try {
|
|
154
|
+
const data = JSON.parse(stdout);
|
|
155
|
+
return (
|
|
156
|
+
data?.namespace?.path ||
|
|
157
|
+
data?.owner?.username ||
|
|
158
|
+
data?.namespace?.name ||
|
|
159
|
+
'unknown'
|
|
160
|
+
);
|
|
161
|
+
} catch {
|
|
162
|
+
return 'unknown';
|
|
163
|
+
}
|
|
164
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* manifest.mjs — JSON manifest validation for vault-backfill headless mode.
|
|
3
|
+
*
|
|
4
|
+
* Validates the --yes <manifest.json> file format.
|
|
5
|
+
* Part of scripts/vault-backfill.mjs (Issue #241).
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const VALID_TIERS = new Set(['top', 'active', 'archived']);
|
|
9
|
+
const VALID_VISIBILITIES = new Set(['public', 'internal', 'private']);
|
|
10
|
+
export const SLUG_RE = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Validate and normalise a manifest object.
|
|
14
|
+
* Calls dieFn(1, ...) on any schema violation.
|
|
15
|
+
*
|
|
16
|
+
* @param {unknown} raw - parsed JSON from manifest file
|
|
17
|
+
* @param {(code: number, msg: string) => never} dieFn
|
|
18
|
+
* @returns {Array<{ id: number, path: string, slug: string, tier: string, visibility: string, skip: boolean }>}
|
|
19
|
+
*/
|
|
20
|
+
export function validateManifest(raw, dieFn) {
|
|
21
|
+
if (typeof raw !== 'object' || raw === null) {
|
|
22
|
+
dieFn(1, 'manifest must be a JSON object');
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
if (raw.version !== 1) {
|
|
26
|
+
dieFn(1, `manifest.version must be 1, got: ${JSON.stringify(raw.version)}`);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
if (!Array.isArray(raw.repos)) {
|
|
30
|
+
dieFn(1, 'manifest.repos must be an array');
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
return raw.repos.map((entry, i) => {
|
|
34
|
+
const prefix = `manifest.repos[${i}]`;
|
|
35
|
+
|
|
36
|
+
if (typeof entry.id !== 'number') dieFn(1, `${prefix}.id must be a number`);
|
|
37
|
+
if (typeof entry.path !== 'string' || !entry.path) {
|
|
38
|
+
dieFn(1, `${prefix}.path must be a non-empty string`);
|
|
39
|
+
}
|
|
40
|
+
if (entry.path.startsWith('/') || entry.path.split('/').includes('..')) {
|
|
41
|
+
dieFn(
|
|
42
|
+
1,
|
|
43
|
+
`${prefix}.path '${entry.path}' is invalid — must be a relative repo path (no leading '/' or '..' segments)`,
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
if (typeof entry.slug !== 'string') dieFn(1, `${prefix}.slug must be a string`);
|
|
47
|
+
if (!SLUG_RE.test(entry.slug)) {
|
|
48
|
+
dieFn(
|
|
49
|
+
1,
|
|
50
|
+
`${prefix}.slug '${entry.slug}' is invalid — must match /^[a-z0-9]+(?:-[a-z0-9]+)*$/`,
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
if (!VALID_TIERS.has(entry.tier)) {
|
|
54
|
+
dieFn(
|
|
55
|
+
1,
|
|
56
|
+
`${prefix}.tier '${entry.tier}' is invalid — must be one of: top|active|archived`,
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
if (!VALID_VISIBILITIES.has(entry.visibility)) {
|
|
60
|
+
dieFn(
|
|
61
|
+
1,
|
|
62
|
+
`${prefix}.visibility '${entry.visibility}' is invalid — must be one of: public|internal|private`,
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return {
|
|
67
|
+
id: entry.id,
|
|
68
|
+
path: entry.path,
|
|
69
|
+
slug: entry.slug,
|
|
70
|
+
tier: entry.tier,
|
|
71
|
+
visibility: entry.visibility,
|
|
72
|
+
skip: entry.skip === true,
|
|
73
|
+
};
|
|
74
|
+
});
|
|
75
|
+
}
|