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,224 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* ecosystem-health.mjs — Plugin-monitor watcher for ecosystem-health skill.
|
|
4
|
+
*
|
|
5
|
+
* Runs as a persistent background process for the lifetime of the session
|
|
6
|
+
* (registered via .claude-plugin/plugin.json -> experimental.monitors path).
|
|
7
|
+
* Each NDJSON line written to stdout becomes a `<task_notification>` event
|
|
8
|
+
* Claude sees mid-session.
|
|
9
|
+
*
|
|
10
|
+
* Behaviour:
|
|
11
|
+
* - Watches .orchestrator/metrics/ecosystem-health.jsonl for state changes
|
|
12
|
+
* (line-count growth + last-line change detection).
|
|
13
|
+
* - If the state file does not exist yet, emits one `no-state-yet` event
|
|
14
|
+
* at startup and idles until it appears.
|
|
15
|
+
* - Coverage rule LM-002: stdout emits cover both progress AND failure
|
|
16
|
+
* cases. Errors during the watch loop are surfaced as `error` events,
|
|
17
|
+
* never silently swallowed (silence != success).
|
|
18
|
+
*
|
|
19
|
+
* Line-buffering contract:
|
|
20
|
+
* Every emit uses `process.stdout.write(line + '\n')` — one line per
|
|
21
|
+
* notification. Never console.log multi-line strings.
|
|
22
|
+
*
|
|
23
|
+
* Flags:
|
|
24
|
+
* --watch Run the watcher loop (required; no other mode supported).
|
|
25
|
+
* --interval=N Probe cadence in seconds (default 900 = 15min). Cron-style
|
|
26
|
+
* pre-warming pattern: keep cadence below any cache-related TTLs.
|
|
27
|
+
* --help Print usage to stderr and exit 0.
|
|
28
|
+
*
|
|
29
|
+
* Exit codes:
|
|
30
|
+
* 0 — clean shutdown (SIGTERM/SIGINT received).
|
|
31
|
+
* 1 — user/input error (unknown flag, --watch missing).
|
|
32
|
+
* 2 — system error (unexpected failure outside the watch loop).
|
|
33
|
+
*
|
|
34
|
+
* @typedef {Object} EmitEvent
|
|
35
|
+
* @property {string} event Event name (lowercase.dot.case).
|
|
36
|
+
* @property {string} ts ISO-8601 timestamp.
|
|
37
|
+
* @property {Record<string, unknown>} [details] Optional structured payload.
|
|
38
|
+
*/
|
|
39
|
+
|
|
40
|
+
import { existsSync, statSync, readFileSync } from 'node:fs';
|
|
41
|
+
import { join } from 'node:path';
|
|
42
|
+
|
|
43
|
+
const DEFAULT_INTERVAL_S = 15 * 60; // 15min
|
|
44
|
+
const STATE_FILE_REL = '.orchestrator/metrics/ecosystem-health.jsonl';
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Parse CLI args into a typed config object. No deps — node:util's parseArgs
|
|
48
|
+
* is overkill for this surface and adds 2 lines of overhead per call.
|
|
49
|
+
* @param {string[]} argv
|
|
50
|
+
* @returns {{ watch: boolean, intervalS: number, help: boolean }}
|
|
51
|
+
*/
|
|
52
|
+
function parseArgs(argv) {
|
|
53
|
+
let watch = false;
|
|
54
|
+
let intervalS = DEFAULT_INTERVAL_S;
|
|
55
|
+
let help = false;
|
|
56
|
+
for (const arg of argv) {
|
|
57
|
+
if (arg === '--watch') {
|
|
58
|
+
watch = true;
|
|
59
|
+
} else if (arg === '--help' || arg === '-h') {
|
|
60
|
+
help = true;
|
|
61
|
+
} else if (arg.startsWith('--interval=')) {
|
|
62
|
+
const v = Number(arg.slice('--interval='.length));
|
|
63
|
+
if (!Number.isFinite(v) || v <= 0) {
|
|
64
|
+
process.stderr.write(`ecosystem-health: invalid --interval value: ${arg}\n`);
|
|
65
|
+
process.exit(1);
|
|
66
|
+
}
|
|
67
|
+
intervalS = v;
|
|
68
|
+
} else {
|
|
69
|
+
process.stderr.write(`ecosystem-health: unknown flag: ${arg}\n`);
|
|
70
|
+
process.exit(1);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
return { watch, intervalS, help };
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Emit one NDJSON line to stdout (one notification = one line).
|
|
78
|
+
* @param {EmitEvent} payload
|
|
79
|
+
*/
|
|
80
|
+
function emit(payload) {
|
|
81
|
+
process.stdout.write(`${JSON.stringify(payload)}\n`);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Read state-file fingerprint (size + last-line) for change detection.
|
|
86
|
+
* @param {string} absPath
|
|
87
|
+
* @returns {{ exists: boolean, size: number, mtimeMs: number, lastLine: string | null }}
|
|
88
|
+
*/
|
|
89
|
+
function fingerprint(absPath) {
|
|
90
|
+
if (!existsSync(absPath)) {
|
|
91
|
+
return { exists: false, size: 0, mtimeMs: 0, lastLine: null };
|
|
92
|
+
}
|
|
93
|
+
try {
|
|
94
|
+
const st = statSync(absPath);
|
|
95
|
+
// Tail read for last line — small file in practice (append-only JSONL).
|
|
96
|
+
// Reading full file is acceptable here at 15-min cadence.
|
|
97
|
+
let lastLine = null;
|
|
98
|
+
if (st.size > 0) {
|
|
99
|
+
const text = readFileSync(absPath, 'utf8');
|
|
100
|
+
const trimmed = text.replace(/\n$/, '');
|
|
101
|
+
const idx = trimmed.lastIndexOf('\n');
|
|
102
|
+
lastLine = idx === -1 ? trimmed : trimmed.slice(idx + 1);
|
|
103
|
+
}
|
|
104
|
+
return { exists: true, size: st.size, mtimeMs: st.mtimeMs, lastLine };
|
|
105
|
+
} catch (err) {
|
|
106
|
+
// Surface as an error event rather than silently returning a fake fingerprint.
|
|
107
|
+
emit({
|
|
108
|
+
event: 'error',
|
|
109
|
+
ts: new Date().toISOString(),
|
|
110
|
+
details: { phase: 'fingerprint', file: absPath, message: String(err?.message ?? err) },
|
|
111
|
+
});
|
|
112
|
+
return { exists: false, size: 0, mtimeMs: 0, lastLine: null };
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Main watch loop. Runs until SIGTERM/SIGINT.
|
|
118
|
+
* @param {number} intervalS
|
|
119
|
+
*/
|
|
120
|
+
async function watchLoop(intervalS) {
|
|
121
|
+
const pluginRoot = process.env.CLAUDE_PLUGIN_ROOT || process.cwd();
|
|
122
|
+
const absPath = join(pluginRoot, STATE_FILE_REL);
|
|
123
|
+
|
|
124
|
+
// Startup event — confirms the watcher is alive even when nothing has
|
|
125
|
+
// happened yet. Without this, a "no events" stream is indistinguishable
|
|
126
|
+
// from a crashed monitor.
|
|
127
|
+
let prev = fingerprint(absPath);
|
|
128
|
+
emit({
|
|
129
|
+
event: prev.exists ? 'watcher.started' : 'no-state-yet',
|
|
130
|
+
ts: new Date().toISOString(),
|
|
131
|
+
details: { file: STATE_FILE_REL, intervalSeconds: intervalS, exists: prev.exists },
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
// Loop forever — the Claude harness sends SIGTERM on session-end and the
|
|
135
|
+
// process exits 0 via the signal handlers below.
|
|
136
|
+
while (true) {
|
|
137
|
+
await sleep(intervalS * 1000);
|
|
138
|
+
const next = fingerprint(absPath);
|
|
139
|
+
if (!prev.exists && next.exists) {
|
|
140
|
+
emit({
|
|
141
|
+
event: 'state-file.appeared',
|
|
142
|
+
ts: new Date().toISOString(),
|
|
143
|
+
details: { file: STATE_FILE_REL, size: next.size },
|
|
144
|
+
});
|
|
145
|
+
} else if (prev.exists && !next.exists) {
|
|
146
|
+
emit({
|
|
147
|
+
event: 'state-file.disappeared',
|
|
148
|
+
ts: new Date().toISOString(),
|
|
149
|
+
details: { file: STATE_FILE_REL },
|
|
150
|
+
});
|
|
151
|
+
} else if (next.exists && (next.size !== prev.size || next.lastLine !== prev.lastLine)) {
|
|
152
|
+
emit({
|
|
153
|
+
event: 'state.changed',
|
|
154
|
+
ts: new Date().toISOString(),
|
|
155
|
+
details: {
|
|
156
|
+
file: STATE_FILE_REL,
|
|
157
|
+
sizeDelta: next.size - prev.size,
|
|
158
|
+
lastLine: next.lastLine,
|
|
159
|
+
},
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
prev = next;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* @param {number} ms
|
|
168
|
+
* @returns {Promise<void>}
|
|
169
|
+
*/
|
|
170
|
+
function sleep(ms) {
|
|
171
|
+
return new Promise((resolve) => {
|
|
172
|
+
const t = setTimeout(resolve, ms);
|
|
173
|
+
t.unref?.();
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
function main() {
|
|
178
|
+
const args = parseArgs(process.argv.slice(2));
|
|
179
|
+
if (args.help) {
|
|
180
|
+
process.stderr.write(
|
|
181
|
+
[
|
|
182
|
+
'Usage: ecosystem-health.mjs --watch [--interval=<seconds>]',
|
|
183
|
+
'',
|
|
184
|
+
'Plugin-monitor watcher for the ecosystem-health skill. Persistent',
|
|
185
|
+
'background process; one NDJSON line per state change → stdout.',
|
|
186
|
+
'',
|
|
187
|
+
'Flags:',
|
|
188
|
+
' --watch Required. Run the watch loop.',
|
|
189
|
+
' --interval=<s> Probe cadence in seconds (default 900).',
|
|
190
|
+
' --help, -h Print this message.',
|
|
191
|
+
'',
|
|
192
|
+
'Exit codes: 0 clean / 1 user-error / 2 system-error.',
|
|
193
|
+
].join('\n') + '\n',
|
|
194
|
+
);
|
|
195
|
+
process.exit(0);
|
|
196
|
+
}
|
|
197
|
+
if (!args.watch) {
|
|
198
|
+
process.stderr.write('ecosystem-health: --watch is required\n');
|
|
199
|
+
process.exit(1);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// Graceful shutdown — Claude harness sends SIGTERM at session-end.
|
|
203
|
+
const shutdown = (sig) => {
|
|
204
|
+
emit({
|
|
205
|
+
event: 'watcher.shutdown',
|
|
206
|
+
ts: new Date().toISOString(),
|
|
207
|
+
details: { signal: sig },
|
|
208
|
+
});
|
|
209
|
+
process.exit(0);
|
|
210
|
+
};
|
|
211
|
+
process.on('SIGTERM', () => shutdown('SIGTERM'));
|
|
212
|
+
process.on('SIGINT', () => shutdown('SIGINT'));
|
|
213
|
+
|
|
214
|
+
watchLoop(args.intervalS).catch((err) => {
|
|
215
|
+
emit({
|
|
216
|
+
event: 'error',
|
|
217
|
+
ts: new Date().toISOString(),
|
|
218
|
+
details: { phase: 'watchLoop', message: String(err?.message ?? err) },
|
|
219
|
+
});
|
|
220
|
+
process.exit(2);
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
main();
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ci-detector.mjs — detects CI provider from repo structure.
|
|
3
|
+
* Checks for .gitlab-ci.yml (GitLab) or .github/workflows/ (GitHub).
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { existsSync } from 'node:fs';
|
|
7
|
+
import { join } from 'node:path';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Detects CI provider from repo structure.
|
|
11
|
+
* @param {string} repoRoot
|
|
12
|
+
* @returns {'gitlab' | 'github' | 'none'}
|
|
13
|
+
*/
|
|
14
|
+
export function detectCiProvider(repoRoot) {
|
|
15
|
+
if (existsSync(join(repoRoot, '.gitlab-ci.yml'))) return 'gitlab';
|
|
16
|
+
if (existsSync(join(repoRoot, '.github', 'workflows'))) return 'github';
|
|
17
|
+
return 'none';
|
|
18
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* config-parser.mjs — pure parsing utilities for comma-separated user input.
|
|
3
|
+
* Converts raw wizard answer strings into typed endpoint/pipeline/label arrays.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Parses a comma-separated string into a trimmed array, filtering empty items.
|
|
8
|
+
* @param {string} input
|
|
9
|
+
* @returns {string[]}
|
|
10
|
+
*/
|
|
11
|
+
export function parseCommaSeparated(input) {
|
|
12
|
+
return input
|
|
13
|
+
.split(',')
|
|
14
|
+
.map((s) => s.trim())
|
|
15
|
+
.filter((s) => s.length > 0);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Parses endpoint input lines. Format: "Name|URL" per entry, separated by commas.
|
|
20
|
+
* @param {string} input
|
|
21
|
+
* @returns {Array<{name: string, url: string}>}
|
|
22
|
+
*/
|
|
23
|
+
export function parseEndpoints(input) {
|
|
24
|
+
return parseCommaSeparated(input)
|
|
25
|
+
.map((entry) => {
|
|
26
|
+
const pipe = entry.indexOf('|');
|
|
27
|
+
if (pipe === -1) return null;
|
|
28
|
+
const name = entry.slice(0, pipe).trim();
|
|
29
|
+
const url = entry.slice(pipe + 1).trim();
|
|
30
|
+
if (!name || !url) return null;
|
|
31
|
+
return { name, url };
|
|
32
|
+
})
|
|
33
|
+
.filter(Boolean);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Parses pipeline input lines. Format: "id" or "id:label" per entry.
|
|
38
|
+
* @param {string} input
|
|
39
|
+
* @returns {Array<{id: string, label?: string}>}
|
|
40
|
+
*/
|
|
41
|
+
export function parsePipelines(input) {
|
|
42
|
+
return parseCommaSeparated(input)
|
|
43
|
+
.map((entry) => {
|
|
44
|
+
const colon = entry.indexOf(':');
|
|
45
|
+
if (colon === -1) {
|
|
46
|
+
return entry.trim() ? { id: entry.trim() } : null;
|
|
47
|
+
}
|
|
48
|
+
const id = entry.slice(0, colon).trim();
|
|
49
|
+
const label = entry.slice(colon + 1).trim();
|
|
50
|
+
if (!id) return null;
|
|
51
|
+
return label ? { id, label } : { id };
|
|
52
|
+
})
|
|
53
|
+
.filter(Boolean);
|
|
54
|
+
}
|
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* config-writer.mjs — validates ecosystem policy shape and writes artifacts:
|
|
3
|
+
* .orchestrator/policy/ecosystem.json and the ecosystem-health block in Session Config.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { existsSync, readFileSync, writeFileSync, mkdirSync } from 'node:fs';
|
|
7
|
+
import { join } from 'node:path';
|
|
8
|
+
|
|
9
|
+
// ---------------------------------------------------------------------------
|
|
10
|
+
// Validation
|
|
11
|
+
// ---------------------------------------------------------------------------
|
|
12
|
+
|
|
13
|
+
/** @param {unknown} arr @param {string} field @param {string[]} errors */
|
|
14
|
+
function _validateEndpoints(arr, field, errors) {
|
|
15
|
+
if (!Array.isArray(arr)) { errors.push(`${field} must be an array`); return; }
|
|
16
|
+
for (let i = 0; i < arr.length; i++) {
|
|
17
|
+
const ep = arr[i];
|
|
18
|
+
if (ep === null || typeof ep !== 'object' || Array.isArray(ep)) {
|
|
19
|
+
errors.push(`${field}[${i}] must be a plain object`); continue;
|
|
20
|
+
}
|
|
21
|
+
if (typeof ep['name'] !== 'string' || ep['name'].trim() === '')
|
|
22
|
+
errors.push(`${field}[${i}].name must be a non-empty string`);
|
|
23
|
+
if (typeof ep['url'] !== 'string' || ep['url'].trim() === '')
|
|
24
|
+
errors.push(`${field}[${i}].url must be a non-empty string`);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/** @param {unknown} arr @param {string} field @param {string[]} errors */
|
|
29
|
+
function _validatePipelines(arr, field, errors) {
|
|
30
|
+
if (!Array.isArray(arr)) { errors.push(`${field} must be an array`); return; }
|
|
31
|
+
for (let i = 0; i < arr.length; i++) {
|
|
32
|
+
const pl = arr[i];
|
|
33
|
+
if (pl === null || typeof pl !== 'object' || Array.isArray(pl)) {
|
|
34
|
+
errors.push(`${field}[${i}] must be a plain object`); continue;
|
|
35
|
+
}
|
|
36
|
+
if (typeof pl['id'] !== 'string' || pl['id'].trim() === '')
|
|
37
|
+
errors.push(`${field}[${i}].id must be a non-empty string`);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/** @param {unknown} arr @param {string} field @param {string[]} errors */
|
|
42
|
+
function _validateLabels(arr, field, errors) {
|
|
43
|
+
if (!Array.isArray(arr)) { errors.push(`${field} must be an array`); return; }
|
|
44
|
+
for (let i = 0; i < arr.length; i++) {
|
|
45
|
+
if (typeof arr[i] !== 'string' || arr[i].trim() === '')
|
|
46
|
+
errors.push(`${field}[${i}] must be a non-empty string`);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Validates the ecosystem policy shape.
|
|
52
|
+
* Returns [] when valid; an array of error strings when invalid.
|
|
53
|
+
*
|
|
54
|
+
* @param {unknown} policy
|
|
55
|
+
* @returns {string[]}
|
|
56
|
+
*/
|
|
57
|
+
export function validateEcosystemPolicy(policy) {
|
|
58
|
+
const errors = [];
|
|
59
|
+
if (policy === null || typeof policy !== 'object' || Array.isArray(policy)) {
|
|
60
|
+
errors.push('policy must be a plain object');
|
|
61
|
+
return errors;
|
|
62
|
+
}
|
|
63
|
+
if (policy['version'] !== 1) {
|
|
64
|
+
errors.push(`version must be 1, got ${JSON.stringify(policy['version'])}`);
|
|
65
|
+
}
|
|
66
|
+
if (policy['endpoints'] !== undefined) _validateEndpoints(policy['endpoints'], 'endpoints', errors);
|
|
67
|
+
if (policy['pipelines'] !== undefined) _validatePipelines(policy['pipelines'], 'pipelines', errors);
|
|
68
|
+
if (policy['criticalIssueLabels'] !== undefined)
|
|
69
|
+
_validateLabels(policy['criticalIssueLabels'], 'criticalIssueLabels', errors);
|
|
70
|
+
return errors;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// ---------------------------------------------------------------------------
|
|
74
|
+
// Session Config reader/writer
|
|
75
|
+
// ---------------------------------------------------------------------------
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Returns whether the Session Config section in `text` already contains an
|
|
79
|
+
* ecosystem-health key.
|
|
80
|
+
* @param {string} text
|
|
81
|
+
* @param {RegExpMatchArray|null} startMatch
|
|
82
|
+
* @returns {boolean}
|
|
83
|
+
*/
|
|
84
|
+
function _ecosystemBlockPresent(text, startMatch) {
|
|
85
|
+
if (!startMatch || startMatch.index === undefined) return false;
|
|
86
|
+
const afterBlock = text.slice(startMatch.index + startMatch[0].length);
|
|
87
|
+
const nextHeadingMatch = afterBlock.match(/^## /m);
|
|
88
|
+
const blockContent = nextHeadingMatch ? afterBlock.slice(0, nextHeadingMatch.index) : afterBlock;
|
|
89
|
+
return /^\s*ecosystem-health\s*:/m.test(blockContent);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Replaces an existing ecosystem-health block in `text` with `snippetLines`.
|
|
94
|
+
* Returns the new text (which may equal `text` if the regex matched nothing different).
|
|
95
|
+
* @param {string} text
|
|
96
|
+
* @param {string[]} snippetLines
|
|
97
|
+
* @returns {string}
|
|
98
|
+
*/
|
|
99
|
+
function _replaceEcosystemBlock(text, snippetLines) {
|
|
100
|
+
return text.replace(/^ecosystem-health:(?:\n(?!##|\S).*)*\n?/m, snippetLines.join('\n') + '\n');
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Inserts a new ecosystem-health snippet into `text` within the Session Config
|
|
105
|
+
* section, or appends a new section when none exists.
|
|
106
|
+
* @param {string} text
|
|
107
|
+
* @param {RegExpMatchArray|null} startMatch
|
|
108
|
+
* @param {string[]} snippetLines
|
|
109
|
+
* @returns {string}
|
|
110
|
+
*/
|
|
111
|
+
function _insertEcosystemBlock(text, startMatch, snippetLines) {
|
|
112
|
+
const snippet = '\n' + snippetLines.join('\n');
|
|
113
|
+
if (!startMatch || startMatch.index === undefined) {
|
|
114
|
+
return text + '\n## Session Config\n' + snippet + '\n';
|
|
115
|
+
}
|
|
116
|
+
const afterStartIdx = startMatch.index + startMatch[0].length;
|
|
117
|
+
const afterBlock = text.slice(afterStartIdx);
|
|
118
|
+
const nextHeadingMatch = afterBlock.match(/^## /m);
|
|
119
|
+
if (nextHeadingMatch && nextHeadingMatch.index !== undefined) {
|
|
120
|
+
const insertAt = afterStartIdx + nextHeadingMatch.index;
|
|
121
|
+
return text.slice(0, insertAt) + snippet + '\n' + text.slice(insertAt);
|
|
122
|
+
}
|
|
123
|
+
return text + snippet + '\n';
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Builds the YAML snippet lines for an ecosystem-health block.
|
|
128
|
+
* @param {{endpoints: object[], pipelines: object[], criticalIssueLabels: string[]}} config
|
|
129
|
+
* @returns {string[]}
|
|
130
|
+
*/
|
|
131
|
+
function _buildEcosystemSnippetLines(config) {
|
|
132
|
+
const lines = ['ecosystem-health:'];
|
|
133
|
+
if (config.endpoints.length > 0) {
|
|
134
|
+
lines.push(' health-endpoints:');
|
|
135
|
+
for (const ep of config.endpoints) {
|
|
136
|
+
lines.push(` - name: ${ep.name}`);
|
|
137
|
+
lines.push(` url: ${ep.url}`);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
if (config.pipelines.length > 0) {
|
|
141
|
+
lines.push(' pipelines:');
|
|
142
|
+
for (const pl of config.pipelines) {
|
|
143
|
+
const label = pl.label ? ` # ${pl.label}` : '';
|
|
144
|
+
lines.push(` - id: ${pl.id}${label}`);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
if (config.criticalIssueLabels.length > 0) {
|
|
148
|
+
lines.push(
|
|
149
|
+
` critical-issue-labels: [${config.criticalIssueLabels.map((l) => JSON.stringify(l)).join(', ')}]`
|
|
150
|
+
);
|
|
151
|
+
}
|
|
152
|
+
return lines;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Resolves the config file path (CLAUDE.md or AGENTS.md).
|
|
157
|
+
* @param {string} repoRoot
|
|
158
|
+
* @returns {string|null}
|
|
159
|
+
*/
|
|
160
|
+
export function resolveConfigFile(repoRoot) {
|
|
161
|
+
const claude = join(repoRoot, 'CLAUDE.md');
|
|
162
|
+
const agents = join(repoRoot, 'AGENTS.md');
|
|
163
|
+
if (existsSync(claude)) return claude;
|
|
164
|
+
if (existsSync(agents)) return agents;
|
|
165
|
+
return null;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Reads existing ecosystem-health config from the policy JSON file.
|
|
170
|
+
* Uses .orchestrator/policy/ecosystem.json as the reliable source of truth
|
|
171
|
+
* (valid JSON, precisely parseable). Returns null when missing or malformed.
|
|
172
|
+
*
|
|
173
|
+
* @param {string} repoRoot
|
|
174
|
+
* @returns {{endpoints: object[], pipelines: object[], criticalIssueLabels: string[]} | null}
|
|
175
|
+
*/
|
|
176
|
+
export function readExistingEcosystemConfig(repoRoot) {
|
|
177
|
+
const policyPath = join(repoRoot, '.orchestrator', 'policy', 'ecosystem.json');
|
|
178
|
+
if (!existsSync(policyPath)) return null;
|
|
179
|
+
let parsed;
|
|
180
|
+
try {
|
|
181
|
+
parsed = JSON.parse(readFileSync(policyPath, 'utf8'));
|
|
182
|
+
} catch {
|
|
183
|
+
return null;
|
|
184
|
+
}
|
|
185
|
+
const errors = validateEcosystemPolicy(parsed);
|
|
186
|
+
if (errors.length > 0) return null;
|
|
187
|
+
return {
|
|
188
|
+
endpoints: Array.isArray(parsed.endpoints) ? parsed.endpoints : [],
|
|
189
|
+
pipelines: Array.isArray(parsed.pipelines) ? parsed.pipelines : [],
|
|
190
|
+
criticalIssueLabels: Array.isArray(parsed.criticalIssueLabels) ? parsed.criticalIssueLabels : [],
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* Writes (or updates) an `ecosystem-health:` block inside Session Config.
|
|
196
|
+
* When `overwrite` is true and an ecosystem-health block already exists, it is
|
|
197
|
+
* replaced with the new config (diff-aware merge). When `overwrite` is false
|
|
198
|
+
* and ecosystem-health already exists, the function skips (legacy behaviour).
|
|
199
|
+
*
|
|
200
|
+
* @param {string} configFilePath
|
|
201
|
+
* @param {{endpoints: object[], pipelines: object[], criticalIssueLabels: string[]}} config
|
|
202
|
+
* @param {boolean} dryRun
|
|
203
|
+
* @param {boolean} [overwrite=false]
|
|
204
|
+
* @returns {'written' | 'skipped' | 'error'}
|
|
205
|
+
*/
|
|
206
|
+
export function writeSessionConfigBlock(configFilePath, config, dryRun, overwrite = false) {
|
|
207
|
+
let text;
|
|
208
|
+
try {
|
|
209
|
+
text = readFileSync(configFilePath, 'utf8');
|
|
210
|
+
} catch {
|
|
211
|
+
return 'error';
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
const startMatch = text.match(/^## Session Config[ \t]*(?:\r?\n|$)/m);
|
|
215
|
+
const snippetLines = _buildEcosystemSnippetLines(config);
|
|
216
|
+
|
|
217
|
+
if (_ecosystemBlockPresent(text, startMatch)) {
|
|
218
|
+
if (!overwrite) return 'skipped';
|
|
219
|
+
// Replace the existing ecosystem-health block with the new snippet.
|
|
220
|
+
// The block spans from "ecosystem-health:" to the next top-level key or heading.
|
|
221
|
+
const newText = _replaceEcosystemBlock(text, snippetLines);
|
|
222
|
+
// If text did not change after replacement (regex produced the same string), skip.
|
|
223
|
+
// Also skip if the only difference is trailing whitespace on the replaced block — this
|
|
224
|
+
// happens when the existing file has an equivalent but differently formatted placeholder.
|
|
225
|
+
if (newText === text) return 'skipped';
|
|
226
|
+
if (!dryRun) {
|
|
227
|
+
try { writeFileSync(configFilePath, newText, 'utf8'); } catch { return 'error'; }
|
|
228
|
+
}
|
|
229
|
+
return 'written';
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
// Insert path: append ecosystem-health block to Session Config section
|
|
233
|
+
const newText = _insertEcosystemBlock(text, startMatch, snippetLines);
|
|
234
|
+
if (!dryRun) {
|
|
235
|
+
try { writeFileSync(configFilePath, newText, 'utf8'); } catch { return 'error'; }
|
|
236
|
+
}
|
|
237
|
+
return 'written';
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* Writes .orchestrator/policy/ecosystem.json.
|
|
242
|
+
*
|
|
243
|
+
* @param {string} repoRoot
|
|
244
|
+
* @param {{endpoints: object[], pipelines: object[], criticalIssueLabels: string[]}} config
|
|
245
|
+
* @param {boolean} dryRun
|
|
246
|
+
* @returns {'written' | 'skipped' | 'error'}
|
|
247
|
+
*/
|
|
248
|
+
export function writePolicyFile(repoRoot, config, dryRun) {
|
|
249
|
+
const policyDir = join(repoRoot, '.orchestrator', 'policy');
|
|
250
|
+
const policyPath = join(policyDir, 'ecosystem.json');
|
|
251
|
+
|
|
252
|
+
// Idempotency: read existing — if contents are identical, skip
|
|
253
|
+
if (existsSync(policyPath)) {
|
|
254
|
+
try {
|
|
255
|
+
const existing = JSON.parse(readFileSync(policyPath, 'utf8'));
|
|
256
|
+
// Re-check if it already has the same data (simple deep-equal via JSON round-trip)
|
|
257
|
+
const proposed = buildPolicyObject(config);
|
|
258
|
+
if (JSON.stringify(existing) === JSON.stringify(proposed)) {
|
|
259
|
+
return 'skipped';
|
|
260
|
+
}
|
|
261
|
+
} catch {
|
|
262
|
+
// malformed existing file — overwrite
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
const policy = buildPolicyObject(config);
|
|
267
|
+
if (!dryRun) {
|
|
268
|
+
try {
|
|
269
|
+
mkdirSync(policyDir, { recursive: true });
|
|
270
|
+
writeFileSync(policyPath, JSON.stringify(policy, null, 2) + '\n', 'utf8');
|
|
271
|
+
} catch {
|
|
272
|
+
return 'error';
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
return 'written';
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
export function buildPolicyObject(config) {
|
|
279
|
+
return {
|
|
280
|
+
version: 1,
|
|
281
|
+
rationale:
|
|
282
|
+
'Ecosystem health configuration. Generated by /bootstrap --ecosystem-health. Hand-edit to add or remove endpoints.',
|
|
283
|
+
endpoints: config.endpoints,
|
|
284
|
+
pipelines: config.pipelines,
|
|
285
|
+
criticalIssueLabels: config.criticalIssueLabels,
|
|
286
|
+
};
|
|
287
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* package-manager-detector.mjs — detects package manager from lockfile presence
|
|
3
|
+
* and reads package.json script names for informational surfacing.
|
|
4
|
+
*
|
|
5
|
+
* A root lockfile that is gitignored is treated as absent (issue #715 bug
|
|
6
|
+
* class) — reuses the shared isIgnoredRootFile() helper from
|
|
7
|
+
* package-manager.mjs rather than re-implementing .gitignore parsing.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
11
|
+
import { join } from 'node:path';
|
|
12
|
+
import { isIgnoredRootFile } from '../package-manager.mjs';
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Detects package manager from lockfile presence. A root lockfile that is
|
|
16
|
+
* gitignored is treated as absent.
|
|
17
|
+
* @param {string} repoRoot
|
|
18
|
+
* @returns {'pnpm' | 'yarn' | 'bun' | 'npm' | null}
|
|
19
|
+
*/
|
|
20
|
+
export function detectPackageManagerFromRoot(repoRoot) {
|
|
21
|
+
if (existsSync(join(repoRoot, 'pnpm-lock.yaml')) && !isIgnoredRootFile(repoRoot, 'pnpm-lock.yaml')) return 'pnpm';
|
|
22
|
+
if (existsSync(join(repoRoot, 'yarn.lock')) && !isIgnoredRootFile(repoRoot, 'yarn.lock')) return 'yarn';
|
|
23
|
+
if (existsSync(join(repoRoot, 'bun.lockb')) && !isIgnoredRootFile(repoRoot, 'bun.lockb')) return 'bun';
|
|
24
|
+
if (existsSync(join(repoRoot, 'package-lock.json')) && !isIgnoredRootFile(repoRoot, 'package-lock.json')) return 'npm';
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Reads package.json scripts to surface script names (informational).
|
|
30
|
+
* @param {string} repoRoot
|
|
31
|
+
* @returns {string[]}
|
|
32
|
+
*/
|
|
33
|
+
export function readPackageScripts(repoRoot) {
|
|
34
|
+
const pkgPath = join(repoRoot, 'package.json');
|
|
35
|
+
if (!existsSync(pkgPath)) return [];
|
|
36
|
+
try {
|
|
37
|
+
const pkg = JSON.parse(readFileSync(pkgPath, 'utf8'));
|
|
38
|
+
return Object.keys(pkg.scripts || {});
|
|
39
|
+
} catch {
|
|
40
|
+
return [];
|
|
41
|
+
}
|
|
42
|
+
}
|