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,295 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* scripts/lib/frontend-detect/rules.mjs
|
|
3
|
+
*
|
|
4
|
+
* Deterministic frontend-slop rule registry (regex tier — no LLM, no API key,
|
|
5
|
+
* no browser). Each rule is a pure function over file text that returns zero or
|
|
6
|
+
* more matches with the offending line + snippet.
|
|
7
|
+
*
|
|
8
|
+
* Design provenance: the rule *catalogue* (which AI-generated design tells to
|
|
9
|
+
* detect) is inspired by pbakaus/impeccable (Apache-2.0) and our own
|
|
10
|
+
* `.claude/rules/frontend.md`. The detection *code* here is a clean-room
|
|
11
|
+
* regex-tier reimplementation — we deliberately skip impeccable's heavier
|
|
12
|
+
* static-HTML CSS-cascade + Puppeteer tiers (cost ≫ value for a pre-commit /
|
|
13
|
+
* discovery probe; see the analysis in the linked backlog issue).
|
|
14
|
+
*
|
|
15
|
+
* The load-bearing idea we adopt: every prose design rule should have a
|
|
16
|
+
* MECHANICAL counterpart. `ruleRef` ties each detector rule back to the prose
|
|
17
|
+
* guidance it enforces — the "Disziplin statt Mechanik" pattern applied to a
|
|
18
|
+
* domain (frontend quality) we previously enforced with prose alone.
|
|
19
|
+
*
|
|
20
|
+
* Rule contract:
|
|
21
|
+
* {
|
|
22
|
+
* id: string, // stable kebab-case id
|
|
23
|
+
* ruleRef: string, // prose-guidance anchor it enforces
|
|
24
|
+
* severity: 'high'|'medium'|'low',
|
|
25
|
+
* category: 'ai-slop'|'quality',
|
|
26
|
+
* title: string,
|
|
27
|
+
* recommendation: string,
|
|
28
|
+
* fpRisk: 'low'|'medium'|'high', // false-positive risk (honesty signal)
|
|
29
|
+
* scan(content: string): Array<{ line: number, snippet: string, value?: string }>
|
|
30
|
+
* }
|
|
31
|
+
*
|
|
32
|
+
* A "match" is reported with a 1-based line number and a trimmed snippet of the
|
|
33
|
+
* offending source line.
|
|
34
|
+
*/
|
|
35
|
+
|
|
36
|
+
// ---------------------------------------------------------------------------
|
|
37
|
+
// Shared helpers
|
|
38
|
+
// ---------------------------------------------------------------------------
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Convert a 0-based string index to a 1-based line number.
|
|
42
|
+
* @param {string} content
|
|
43
|
+
* @param {number} index
|
|
44
|
+
* @returns {number}
|
|
45
|
+
*/
|
|
46
|
+
function lineOf(content, index) {
|
|
47
|
+
let line = 1;
|
|
48
|
+
for (let i = 0; i < index && i < content.length; i++) {
|
|
49
|
+
if (content[i] === '\n') line++;
|
|
50
|
+
}
|
|
51
|
+
return line;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Return the trimmed full source line containing `index`, capped to 160 chars.
|
|
56
|
+
* @param {string} content
|
|
57
|
+
* @param {number} index
|
|
58
|
+
* @returns {string}
|
|
59
|
+
*/
|
|
60
|
+
function snippetAt(content, index) {
|
|
61
|
+
const start = content.lastIndexOf('\n', index - 1) + 1;
|
|
62
|
+
let end = content.indexOf('\n', index);
|
|
63
|
+
if (end === -1) end = content.length;
|
|
64
|
+
const raw = content.slice(start, end).trim();
|
|
65
|
+
return raw.length > 160 ? `${raw.slice(0, 157)}…` : raw;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Run a global regex over content and emit one match per hit.
|
|
70
|
+
* Deduplicates by line so a single offending line reports once.
|
|
71
|
+
* @param {string} content
|
|
72
|
+
* @param {RegExp} re — MUST have the global flag
|
|
73
|
+
* @returns {Array<{ line: number, snippet: string, value: string }>}
|
|
74
|
+
*/
|
|
75
|
+
function scanRegex(content, re) {
|
|
76
|
+
const out = [];
|
|
77
|
+
const seenLines = new Set();
|
|
78
|
+
let m;
|
|
79
|
+
// Defensive: reset lastIndex in case a non-fresh regex is passed.
|
|
80
|
+
re.lastIndex = 0;
|
|
81
|
+
while ((m = re.exec(content)) !== null) {
|
|
82
|
+
const line = lineOf(content, m.index);
|
|
83
|
+
if (seenLines.has(line)) continue;
|
|
84
|
+
seenLines.add(line);
|
|
85
|
+
out.push({ line, snippet: snippetAt(content, m.index), value: m[0].trim() });
|
|
86
|
+
// Guard against zero-width matches looping forever.
|
|
87
|
+
if (m.index === re.lastIndex) re.lastIndex++;
|
|
88
|
+
}
|
|
89
|
+
return out;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// Purple / indigo / violet color tokens — the canonical AI-gradient palette.
|
|
93
|
+
const PURPLE_TOKENS = [
|
|
94
|
+
'purple',
|
|
95
|
+
'violet',
|
|
96
|
+
'indigo',
|
|
97
|
+
'fuchsia',
|
|
98
|
+
'rebeccapurple',
|
|
99
|
+
'#7c3aed',
|
|
100
|
+
'#8b5cf6',
|
|
101
|
+
'#a855f7',
|
|
102
|
+
'#6366f1',
|
|
103
|
+
'#818cf8',
|
|
104
|
+
'#d946ef',
|
|
105
|
+
'#c084fc',
|
|
106
|
+
'#9333ea',
|
|
107
|
+
];
|
|
108
|
+
const BLUE_TOKENS = ['blue', '#3b82f6', '#2563eb', '#60a5fa', '#0ea5e9', '#06b6d4', 'cyan'];
|
|
109
|
+
|
|
110
|
+
// ---------------------------------------------------------------------------
|
|
111
|
+
// Rules
|
|
112
|
+
// ---------------------------------------------------------------------------
|
|
113
|
+
|
|
114
|
+
/** @type {Array<import('./detect.mjs').Rule>} */
|
|
115
|
+
export const RULES = [
|
|
116
|
+
{
|
|
117
|
+
id: 'gradient-text',
|
|
118
|
+
ruleRef: 'frontend.md#absolute-bans (gradient text)',
|
|
119
|
+
severity: 'high',
|
|
120
|
+
category: 'ai-slop',
|
|
121
|
+
title: 'Gradient text (background-clip:text + gradient)',
|
|
122
|
+
recommendation:
|
|
123
|
+
'Decorative, never meaningful. Use a single solid color; carry emphasis with weight or size.',
|
|
124
|
+
fpRisk: 'low',
|
|
125
|
+
scan(content) {
|
|
126
|
+
const matches = [];
|
|
127
|
+
// CSS form: background-clip:text (or -webkit-) anywhere AND a gradient() anywhere.
|
|
128
|
+
const hasClipText = /(?:-webkit-)?background-clip\s*:\s*text\b/i.test(content);
|
|
129
|
+
const hasGradient = /\b(?:linear|radial|conic)-gradient\s*\(/i.test(content);
|
|
130
|
+
if (hasClipText && hasGradient) {
|
|
131
|
+
matches.push(...scanRegex(content, /(?:-webkit-)?background-clip\s*:\s*text\b/gi));
|
|
132
|
+
}
|
|
133
|
+
// Tailwind form: the `bg-clip-text` utility is the unambiguous tell.
|
|
134
|
+
matches.push(...scanRegex(content, /\bbg-clip-text\b/g));
|
|
135
|
+
return dedupeByLine(matches);
|
|
136
|
+
},
|
|
137
|
+
},
|
|
138
|
+
|
|
139
|
+
{
|
|
140
|
+
id: 'side-stripe-border',
|
|
141
|
+
ruleRef: 'frontend.md#absolute-bans (side-stripe borders)',
|
|
142
|
+
severity: 'high',
|
|
143
|
+
category: 'ai-slop',
|
|
144
|
+
title: 'Side-stripe accent border (border-left/right ≥ 2px)',
|
|
145
|
+
recommendation:
|
|
146
|
+
'Never intentional as a colored accent. Use full borders, a background tint, a leading icon/number, or nothing.',
|
|
147
|
+
fpRisk: 'low',
|
|
148
|
+
scan(content) {
|
|
149
|
+
const matches = [];
|
|
150
|
+
// CSS: border-left: 4px solid <color> (width >= 2, not transparent/none)
|
|
151
|
+
const css = /border-(?:left|right)\s*:\s*(\d+)px\s+(?:solid|dashed|dotted|double)\b(?![^;]*\b(?:transparent|none)\b)/gi;
|
|
152
|
+
let m;
|
|
153
|
+
css.lastIndex = 0;
|
|
154
|
+
while ((m = css.exec(content)) !== null) {
|
|
155
|
+
if (Number(m[1]) >= 2) {
|
|
156
|
+
matches.push({ line: lineOf(content, m.index), snippet: snippetAt(content, m.index), value: m[0].trim() });
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
// Tailwind: border-l-2 / border-l-4 / border-l-8 / border-r-4 ...
|
|
160
|
+
matches.push(...scanRegex(content, /\bborder-(?:l|r)-(?:2|4|8)\b/g));
|
|
161
|
+
return dedupeByLine(matches);
|
|
162
|
+
},
|
|
163
|
+
},
|
|
164
|
+
|
|
165
|
+
{
|
|
166
|
+
id: 'overused-font',
|
|
167
|
+
ruleRef: 'frontend.md / development.md (avoid overused fonts)',
|
|
168
|
+
severity: 'medium',
|
|
169
|
+
category: 'ai-slop',
|
|
170
|
+
title: 'Overused primary font (Inter / Roboto / Arial / Helvetica)',
|
|
171
|
+
recommendation:
|
|
172
|
+
'Every model reaches for these. Pick a font with a point of view; keep these only as fallbacks deeper in the stack.',
|
|
173
|
+
fpRisk: 'medium',
|
|
174
|
+
scan(content) {
|
|
175
|
+
// First family in a font-family stack is one of the overused four.
|
|
176
|
+
return scanRegex(content, /font-family\s*:\s*["']?(?:Inter|Roboto|Arial|Helvetica)\b/gi);
|
|
177
|
+
},
|
|
178
|
+
},
|
|
179
|
+
|
|
180
|
+
{
|
|
181
|
+
id: 'bounce-easing',
|
|
182
|
+
ruleRef: 'frontend.md#motion (no bounce/elastic easing)',
|
|
183
|
+
severity: 'medium',
|
|
184
|
+
category: 'ai-slop',
|
|
185
|
+
title: 'Bounce / elastic / overshoot easing',
|
|
186
|
+
recommendation: 'Feels dated. Ease out with exponential curves (ease-out-quart / quint / expo). No overshoot.',
|
|
187
|
+
fpRisk: 'low',
|
|
188
|
+
scan(content) {
|
|
189
|
+
const matches = [];
|
|
190
|
+
// Keyword easings.
|
|
191
|
+
matches.push(...scanRegex(content, /\b(?:bounce|elastic|wobble|spring)\b/gi));
|
|
192
|
+
// cubic-bezier with a y-control point > 1 or < 0 (overshoot).
|
|
193
|
+
const cb = /cubic-bezier\(\s*([\d.]+)\s*,\s*(-?[\d.]+)\s*,\s*([\d.]+)\s*,\s*(-?[\d.]+)\s*\)/gi;
|
|
194
|
+
let m;
|
|
195
|
+
cb.lastIndex = 0;
|
|
196
|
+
while ((m = cb.exec(content)) !== null) {
|
|
197
|
+
const y1 = Number(m[2]);
|
|
198
|
+
const y2 = Number(m[4]);
|
|
199
|
+
if (y1 > 1 || y2 > 1 || y1 < 0 || y2 < 0) {
|
|
200
|
+
matches.push({ line: lineOf(content, m.index), snippet: snippetAt(content, m.index), value: m[0].trim() });
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
return dedupeByLine(matches);
|
|
204
|
+
},
|
|
205
|
+
},
|
|
206
|
+
|
|
207
|
+
{
|
|
208
|
+
id: 'ai-purple-gradient',
|
|
209
|
+
ruleRef: 'frontend.md / PRODUCT anti-references (purple-to-blue gradients)',
|
|
210
|
+
severity: 'medium',
|
|
211
|
+
category: 'ai-slop',
|
|
212
|
+
title: 'Purple / indigo AI gradient',
|
|
213
|
+
recommendation:
|
|
214
|
+
'The single most recognizable AI tell. If purple is genuinely the brand, keep it flat; otherwise pick a committed strategy.',
|
|
215
|
+
fpRisk: 'high',
|
|
216
|
+
scan(content) {
|
|
217
|
+
const matches = [];
|
|
218
|
+
const grad = /\b(?:linear|radial|conic)-gradient\s*\(([^)]*)\)/gi;
|
|
219
|
+
let m;
|
|
220
|
+
grad.lastIndex = 0;
|
|
221
|
+
while ((m = grad.exec(content)) !== null) {
|
|
222
|
+
const args = m[1].toLowerCase();
|
|
223
|
+
const purpleHits = PURPLE_TOKENS.filter((t) => args.includes(t)).length;
|
|
224
|
+
const blueHits = BLUE_TOKENS.filter((t) => args.includes(t)).length;
|
|
225
|
+
// Tell = two purples, OR a purple→blue ramp.
|
|
226
|
+
if (purpleHits >= 2 || (purpleHits >= 1 && blueHits >= 1)) {
|
|
227
|
+
matches.push({ line: lineOf(content, m.index), snippet: snippetAt(content, m.index), value: m[0].trim().slice(0, 80) });
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
return dedupeByLine(matches);
|
|
231
|
+
},
|
|
232
|
+
},
|
|
233
|
+
|
|
234
|
+
{
|
|
235
|
+
id: 'pure-black-ink',
|
|
236
|
+
ruleRef: 'frontend.md (never pure black/gray — always tint)',
|
|
237
|
+
severity: 'low',
|
|
238
|
+
category: 'quality',
|
|
239
|
+
title: 'Pure black text color (#000 / black / rgb(0,0,0))',
|
|
240
|
+
recommendation: 'Pure black reads harsh on screen. Tint toward the brand hue (e.g. very dark, slightly hued ink).',
|
|
241
|
+
fpRisk: 'medium',
|
|
242
|
+
scan(content) {
|
|
243
|
+
return scanRegex(
|
|
244
|
+
content,
|
|
245
|
+
/color\s*:\s*(?:#000(?:000)?\b|black\b|rgb\(\s*0\s*,\s*0\s*,\s*0\s*\))/gi,
|
|
246
|
+
);
|
|
247
|
+
},
|
|
248
|
+
},
|
|
249
|
+
|
|
250
|
+
{
|
|
251
|
+
id: 'arbitrary-z-index',
|
|
252
|
+
ruleRef: 'frontend.md#layout (semantic z-index scale, never 999/9999)',
|
|
253
|
+
severity: 'low',
|
|
254
|
+
category: 'quality',
|
|
255
|
+
title: 'Arbitrary z-index (999 / 9999 …)',
|
|
256
|
+
recommendation: 'Build a semantic z-index scale (dropdown → sticky → modal → toast → tooltip). Never magic numbers.',
|
|
257
|
+
fpRisk: 'low',
|
|
258
|
+
scan(content) {
|
|
259
|
+
return scanRegex(content, /z-index\s*:\s*(?:9{3,})\b/gi);
|
|
260
|
+
},
|
|
261
|
+
},
|
|
262
|
+
|
|
263
|
+
{
|
|
264
|
+
id: 'layout-property-transition',
|
|
265
|
+
ruleRef: 'frontend.md#motion (don\'t animate layout properties)',
|
|
266
|
+
severity: 'low',
|
|
267
|
+
category: 'quality',
|
|
268
|
+
title: 'Animating layout properties (width/height/margin/…)',
|
|
269
|
+
recommendation:
|
|
270
|
+
'Animating layout props thrashes the main thread. Animate transform/opacity; reserve layout animation for genuine need.',
|
|
271
|
+
fpRisk: 'medium',
|
|
272
|
+
scan(content) {
|
|
273
|
+
// `transition:` shorthand whose property list includes a layout prop.
|
|
274
|
+
return scanRegex(
|
|
275
|
+
content,
|
|
276
|
+
/transition\s*:\s*[^;{}]*\b(?:width|height|top|left|right|bottom|margin|padding)\b/gi,
|
|
277
|
+
);
|
|
278
|
+
},
|
|
279
|
+
},
|
|
280
|
+
];
|
|
281
|
+
|
|
282
|
+
/**
|
|
283
|
+
* Collapse multiple matches on the same line into one (stable order).
|
|
284
|
+
* @param {Array<{line:number, snippet:string, value?:string}>} matches
|
|
285
|
+
*/
|
|
286
|
+
function dedupeByLine(matches) {
|
|
287
|
+
const byLine = new Map();
|
|
288
|
+
for (const mt of matches) {
|
|
289
|
+
if (!byLine.has(mt.line)) byLine.set(mt.line, mt);
|
|
290
|
+
}
|
|
291
|
+
return [...byLine.values()].sort((a, b) => a.line - b.line);
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
/** Stable list of rule ids (for config validation + tests). */
|
|
295
|
+
export const RULE_IDS = RULES.map((r) => r.id);
|
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Frontmatter-Guard library (issue #328).
|
|
3
|
+
*
|
|
4
|
+
* Reads the canonical vault-frontmatter Zod schema source and exposes helpers
|
|
5
|
+
* for generating a contextual schema snippet that can be injected into agent
|
|
6
|
+
* prompts before vault-write tasks.
|
|
7
|
+
*
|
|
8
|
+
* Pure ESM, no top-level side effects. All I/O is wrapped in functions.
|
|
9
|
+
* Uses `node:fs` and `node:crypto` only — no external dependencies.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { digestSha256Short } from './crypto-digest-utils.mjs';
|
|
13
|
+
import { readFileSync, statSync } from 'node:fs';
|
|
14
|
+
import { homedir } from 'node:os';
|
|
15
|
+
import { join } from 'node:path';
|
|
16
|
+
|
|
17
|
+
/** Absolute path to the canonical vault-frontmatter schema source. */
|
|
18
|
+
const SCHEMA_SOURCE_PATH = join(
|
|
19
|
+
homedir(),
|
|
20
|
+
'Projects/projects-baseline/packages/zod-schemas/src/vault-frontmatter.ts',
|
|
21
|
+
);
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* In-memory mtime cache so repeated calls within a single process invocation
|
|
25
|
+
* skip the FS read when the file has not changed. Re-reads on mtime change.
|
|
26
|
+
*
|
|
27
|
+
* @type {{ mtime: number | null; result: ReturnType<typeof _parseSchema> | null }}
|
|
28
|
+
*/
|
|
29
|
+
const _cache = { mtime: null, result: null };
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Parse the type-enum values out of a Zod `z.enum([...])` call in source text.
|
|
33
|
+
*
|
|
34
|
+
* @param {string} text
|
|
35
|
+
* @param {string} exportName - e.g. 'vaultNoteTypeSchema'
|
|
36
|
+
* @returns {string[]}
|
|
37
|
+
*/
|
|
38
|
+
function _extractEnum(text, exportName) {
|
|
39
|
+
// Match: export const <name> = z.enum([ ...values... ]);
|
|
40
|
+
const re = new RegExp(
|
|
41
|
+
`export\\s+const\\s+${exportName}\\s*=\\s*z\\.enum\\(\\s*\\[([^\\]]+)\\]`,
|
|
42
|
+
's',
|
|
43
|
+
);
|
|
44
|
+
const match = text.match(re);
|
|
45
|
+
if (!match) return [];
|
|
46
|
+
return match[1]
|
|
47
|
+
.split(',')
|
|
48
|
+
.map((s) => s.trim().replace(/^['"]|['"]$/g, ''))
|
|
49
|
+
.filter(Boolean);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Internal parser — extracts structured info from the TS source.
|
|
54
|
+
*
|
|
55
|
+
* @param {string} text
|
|
56
|
+
* @returns {{ typeEnum: string[], statusEnum: string[], requiredFields: string[], idRegex: string, tagsRegex: string, schemaText: string }}
|
|
57
|
+
*/
|
|
58
|
+
function _parseSchema(text) {
|
|
59
|
+
const typeEnum = _extractEnum(text, 'vaultNoteTypeSchema');
|
|
60
|
+
const statusEnum = _extractEnum(text, 'vaultNoteStatusSchema');
|
|
61
|
+
|
|
62
|
+
// Required fields are id, type, created, updated — stable; extracted
|
|
63
|
+
// from the schema object declaration (non-optional fields).
|
|
64
|
+
const requiredFields = ['id', 'type', 'created', 'updated'];
|
|
65
|
+
|
|
66
|
+
// Regex values — parse from source or use hard-coded fallback that matches
|
|
67
|
+
// the known schema. Prefer source-parse for drift-safety.
|
|
68
|
+
const slugMatch = text.match(/const slugRegex\s*=\s*([^;]+);/);
|
|
69
|
+
const tagMatch = text.match(/const tagPathRegex\s*=\s*([^;]+);/);
|
|
70
|
+
|
|
71
|
+
// Convert JS regex literal /pattern/ to pattern string
|
|
72
|
+
const toPattern = (raw) => {
|
|
73
|
+
if (!raw) return null;
|
|
74
|
+
const m = raw.trim().match(/^\/(.+)\/[gimsuy]*$/);
|
|
75
|
+
return m ? m[1] : null;
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
const idRegex =
|
|
79
|
+
toPattern(slugMatch?.[1]) ?? '^[a-z0-9]+(?:-[a-z0-9]+)*$';
|
|
80
|
+
const tagsRegex =
|
|
81
|
+
toPattern(tagMatch?.[1]) ??
|
|
82
|
+
'^[a-z0-9]+(?:-[a-z0-9]+)*(?:/[a-z0-9]+(?:-[a-z0-9]+)*)*$';
|
|
83
|
+
|
|
84
|
+
return { typeEnum, statusEnum, requiredFields, idRegex, tagsRegex, schemaText: text };
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Read and parse the canonical vault-frontmatter schema source.
|
|
89
|
+
*
|
|
90
|
+
* Caches the result in-memory; re-reads only when the file mtime changes.
|
|
91
|
+
* Returns `null` (no throw) if the file is missing or unreadable.
|
|
92
|
+
*
|
|
93
|
+
* @returns {{ typeEnum: string[], statusEnum: string[], requiredFields: string[], idRegex: string, tagsRegex: string, schemaText: string } | null}
|
|
94
|
+
*/
|
|
95
|
+
export function readVaultSchema() {
|
|
96
|
+
let mtime;
|
|
97
|
+
try {
|
|
98
|
+
mtime = statSync(SCHEMA_SOURCE_PATH).mtimeMs;
|
|
99
|
+
} catch {
|
|
100
|
+
// File missing or inaccessible
|
|
101
|
+
return null;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
if (_cache.mtime === mtime && _cache.result !== null) {
|
|
105
|
+
return _cache.result;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
let text;
|
|
109
|
+
try {
|
|
110
|
+
text = readFileSync(SCHEMA_SOURCE_PATH, 'utf8');
|
|
111
|
+
} catch {
|
|
112
|
+
return null;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
const result = _parseSchema(text);
|
|
116
|
+
_cache.mtime = mtime;
|
|
117
|
+
_cache.result = result;
|
|
118
|
+
return result;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Compute an 8-character SHA-256 hex prefix of the given schema source text.
|
|
123
|
+
* Stable across calls for the same input — useful as a cache-busting token.
|
|
124
|
+
*
|
|
125
|
+
* @param {string} schemaText
|
|
126
|
+
* @returns {string}
|
|
127
|
+
*/
|
|
128
|
+
export function computeSchemaHash(schemaText) {
|
|
129
|
+
return digestSha256Short(schemaText);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Generate a deterministic Markdown snippet documenting the vault frontmatter
|
|
134
|
+
* schema, suitable for injection into agent prompts before vault-write tasks.
|
|
135
|
+
*
|
|
136
|
+
* @param {{ typeEnum: string[], statusEnum: string[], requiredFields: string[], idRegex: string, tagsRegex: string }} schema
|
|
137
|
+
* @returns {string}
|
|
138
|
+
*/
|
|
139
|
+
export function generateFrontmatterSnippet(schema) {
|
|
140
|
+
const { typeEnum, statusEnum, requiredFields, idRegex, tagsRegex: _tagsRegex } = schema;
|
|
141
|
+
|
|
142
|
+
const typeList = typeEnum.map((v) => `\`${v}\``).join(' | ');
|
|
143
|
+
const statusList = statusEnum.map((v) => `\`${v}\``).join(' | ');
|
|
144
|
+
const requiredList = requiredFields.map((f) => `\`${f}\``).join(', ');
|
|
145
|
+
|
|
146
|
+
return `## Vault Frontmatter Schema (REQUIRED for files under ~/Projects/vault/)
|
|
147
|
+
|
|
148
|
+
**Required fields (every note):** ${requiredList}
|
|
149
|
+
|
|
150
|
+
### Enums
|
|
151
|
+
- **\`type\`:** ${typeList}
|
|
152
|
+
- **\`status\`** (optional): ${statusList}
|
|
153
|
+
|
|
154
|
+
### Field formats
|
|
155
|
+
- \`id\`: kebab-case, 2-128 chars, regex \`${idRegex}\`
|
|
156
|
+
- \`tags\`: array of kebab-case strings, \`/\` separator allowed (e.g., \`learning/cli-design\`)
|
|
157
|
+
- \`created\` / \`updated\` / \`expires\`: ISO 8601 (\`YYYY-MM-DD\` or \`YYYY-MM-DDTHH:MM:SSZ\`)
|
|
158
|
+
|
|
159
|
+
### Examples
|
|
160
|
+
|
|
161
|
+
#### type: reference
|
|
162
|
+
\`\`\`yaml
|
|
163
|
+
id: parallel-session-rules
|
|
164
|
+
type: reference
|
|
165
|
+
created: 2026-05-08
|
|
166
|
+
updated: 2026-05-08
|
|
167
|
+
status: active
|
|
168
|
+
tags: [reference/rules]
|
|
169
|
+
\`\`\`
|
|
170
|
+
#### type: session
|
|
171
|
+
\`\`\`yaml
|
|
172
|
+
id: session-2026-05-08-deep
|
|
173
|
+
type: session
|
|
174
|
+
created: 2026-05-08
|
|
175
|
+
updated: 2026-05-08
|
|
176
|
+
status: archived
|
|
177
|
+
tags: [session/deep]
|
|
178
|
+
\`\`\`
|
|
179
|
+
#### type: learning
|
|
180
|
+
\`\`\`yaml
|
|
181
|
+
id: w1-discovery-shrinks-scope
|
|
182
|
+
type: learning
|
|
183
|
+
created: 2026-05-08
|
|
184
|
+
updated: 2026-05-08
|
|
185
|
+
status: active
|
|
186
|
+
\`\`\`
|
|
187
|
+
#### type: daily
|
|
188
|
+
\`\`\`yaml
|
|
189
|
+
id: 2026-05-08
|
|
190
|
+
type: daily
|
|
191
|
+
created: 2026-05-08
|
|
192
|
+
updated: 2026-05-08
|
|
193
|
+
\`\`\`
|
|
194
|
+
#### type: project
|
|
195
|
+
\`\`\`yaml
|
|
196
|
+
id: session-orchestrator
|
|
197
|
+
type: project
|
|
198
|
+
created: 2026-04-01
|
|
199
|
+
updated: 2026-05-08
|
|
200
|
+
status: active
|
|
201
|
+
\`\`\`
|
|
202
|
+
`;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Heuristic detector: returns true when the current task is likely to write
|
|
207
|
+
* vault files, in which case the frontmatter-guard snippet should be injected.
|
|
208
|
+
*
|
|
209
|
+
* Detection rules (OR logic — any match returns true):
|
|
210
|
+
* 1. Any file in `fileScope` whose path contains `/Projects/vault/`.
|
|
211
|
+
* 2. Any file in `fileScope` under known vault subdirectories:
|
|
212
|
+
* `40-learnings/`, `50-sessions/`, `03-daily/`, `01-projects/`.
|
|
213
|
+
* 3. `taskDescription` mentions "vault" or "vault-mirror" AND contains a
|
|
214
|
+
* write-intent keyword ("write", "creat", "generat", "emit", "mirror",
|
|
215
|
+
* "update", "add", "insert").
|
|
216
|
+
*
|
|
217
|
+
* Pure function — no I/O.
|
|
218
|
+
*
|
|
219
|
+
* @param {string} taskDescription
|
|
220
|
+
* @param {string[]} fileScope
|
|
221
|
+
* @returns {boolean}
|
|
222
|
+
*/
|
|
223
|
+
export function detectVaultTaskScope(taskDescription, fileScope) {
|
|
224
|
+
const VAULT_PATH_RE = /\/Projects\/vault\//;
|
|
225
|
+
const VAULT_SUBDIR_RE = /(?:40-learnings|50-sessions|03-daily|01-projects)\//;
|
|
226
|
+
|
|
227
|
+
for (const f of fileScope) {
|
|
228
|
+
if (VAULT_PATH_RE.test(f) || VAULT_SUBDIR_RE.test(f)) {
|
|
229
|
+
return true;
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
const desc = taskDescription.toLowerCase();
|
|
234
|
+
const mentionsVault = desc.includes('vault') || desc.includes('vault-mirror');
|
|
235
|
+
const WRITE_INTENT_RE = /\b(?:write[a-z]*|creat[a-z]*|generat[a-z]*|emit[a-z]*|mirror[a-z]*|updat[a-z]*|add[a-z]*|insert[a-z]*)\b/;
|
|
236
|
+
if (mentionsVault && WRITE_INTENT_RE.test(desc)) {
|
|
237
|
+
return true;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
return false;
|
|
241
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* echo-stub-detect.mjs — Detect quality-gate commands that are no-op stubs.
|
|
3
|
+
* Prevents `echo "no automated tests yet"` from producing a false-positive pass.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Detect whether a quality-gate command is a no-op echo-stub or a `:` no-op.
|
|
8
|
+
*
|
|
9
|
+
* Detection rules (applied in order):
|
|
10
|
+
* 1. Empty / undefined / `'skip'` → not a stub (skip branch handles these).
|
|
11
|
+
* 2. Whole command equals `:` → stub of kind `'noop'`.
|
|
12
|
+
* 3. Whole command is `echo` followed by a single quoted or simple argument
|
|
13
|
+
* with no shell-meta tail (pipe, redirect, command-substitution, etc.)
|
|
14
|
+
* → stub of kind `'echo'`.
|
|
15
|
+
* 4. Otherwise → not a stub.
|
|
16
|
+
*
|
|
17
|
+
* @param {string|undefined} cmd
|
|
18
|
+
* @returns {{ isStub: boolean, kind?: 'echo'|'noop' }}
|
|
19
|
+
*/
|
|
20
|
+
export function detectStubCommand(cmd) {
|
|
21
|
+
if (!cmd || cmd === 'skip') {
|
|
22
|
+
return { isStub: false };
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const trimmed = cmd.trim();
|
|
26
|
+
|
|
27
|
+
if (trimmed === ':') {
|
|
28
|
+
return { isStub: true, kind: 'noop' };
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Matches: echo "quoted" | echo 'quoted' | echo simpleArg
|
|
32
|
+
// Rejects any shell-meta characters after the argument (|, ;, &, <, >, $, `, (, ))
|
|
33
|
+
const ECHO_STUB_RE = /^echo\s+(?:"[^"]*"|'[^']*'|[^\s|;&<>$`()]+)\s*$/;
|
|
34
|
+
if (ECHO_STUB_RE.test(trimmed)) {
|
|
35
|
+
return { isStub: true, kind: 'echo' };
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return { isStub: false };
|
|
39
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// gate-baseline.mjs — baseline quality gate handler
|
|
3
|
+
// Runs typecheck (tail-5) + test (tail-5); informational only, always exits 0.
|
|
4
|
+
//
|
|
5
|
+
// Required env: TYPECHECK_CMD, TEST_CMD
|
|
6
|
+
// Each may equal "skip" or empty string → treated as skip.
|
|
7
|
+
|
|
8
|
+
import { runCheck } from './gate-helpers.mjs';
|
|
9
|
+
|
|
10
|
+
const typecheckCmd = process.env.TYPECHECK_CMD;
|
|
11
|
+
const testCmd = process.env.TEST_CMD;
|
|
12
|
+
|
|
13
|
+
if (!typecheckCmd) {
|
|
14
|
+
process.stderr.write('TYPECHECK_CMD must be set\n');
|
|
15
|
+
process.exit(1);
|
|
16
|
+
}
|
|
17
|
+
if (!testCmd) {
|
|
18
|
+
process.stderr.write('TEST_CMD must be set\n');
|
|
19
|
+
process.exit(1);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
let tcResult = { status: 'skip', output: '' };
|
|
23
|
+
let testResult = { status: 'skip', output: '' };
|
|
24
|
+
|
|
25
|
+
if (typecheckCmd !== 'skip') {
|
|
26
|
+
tcResult = await runCheck(typecheckCmd);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
if (testCmd !== 'skip') {
|
|
30
|
+
testResult = await runCheck(testCmd);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const result = {
|
|
34
|
+
variant: 'baseline',
|
|
35
|
+
typecheck: tcResult.status,
|
|
36
|
+
test: testResult.status,
|
|
37
|
+
typecheck_output: tcResult.output,
|
|
38
|
+
test_output: testResult.output,
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
process.stdout.write(JSON.stringify(result) + '\n');
|
|
42
|
+
process.exit(0);
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// gate-full.mjs — full-gate quality gate handler
|
|
3
|
+
// Runs typecheck + tests + lint sequentially; exits 2 if any check fails, 0 otherwise.
|
|
4
|
+
//
|
|
5
|
+
// Required env: TYPECHECK_CMD, TEST_CMD, LINT_CMD (each may equal "skip")
|
|
6
|
+
// Optional env: SESSION_START_REF
|
|
7
|
+
|
|
8
|
+
import {
|
|
9
|
+
runCheck,
|
|
10
|
+
extractCount,
|
|
11
|
+
extractTestCounts,
|
|
12
|
+
collectDebugArtifacts,
|
|
13
|
+
} from './gate-helpers.mjs';
|
|
14
|
+
|
|
15
|
+
const typecheckCmd = process.env.TYPECHECK_CMD;
|
|
16
|
+
const testCmd = process.env.TEST_CMD;
|
|
17
|
+
const lintCmd = process.env.LINT_CMD;
|
|
18
|
+
const sessionStartRef = process.env.SESSION_START_REF ?? '';
|
|
19
|
+
|
|
20
|
+
if (!typecheckCmd) {
|
|
21
|
+
process.stderr.write('TYPECHECK_CMD must be set\n');
|
|
22
|
+
process.exit(1);
|
|
23
|
+
}
|
|
24
|
+
if (!testCmd) {
|
|
25
|
+
process.stderr.write('TEST_CMD must be set\n');
|
|
26
|
+
process.exit(1);
|
|
27
|
+
}
|
|
28
|
+
if (!lintCmd) {
|
|
29
|
+
process.stderr.write('LINT_CMD must be set\n');
|
|
30
|
+
process.exit(1);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const startTime = Date.now();
|
|
34
|
+
|
|
35
|
+
// --- Typecheck ---
|
|
36
|
+
const tcResult = runCheck(typecheckCmd);
|
|
37
|
+
const tcErrorCount =
|
|
38
|
+
tcResult.status === 'fail'
|
|
39
|
+
? extractCount(tcResult.output, /error TS\d+/)
|
|
40
|
+
: 0;
|
|
41
|
+
|
|
42
|
+
// --- Test ---
|
|
43
|
+
const testResult = runCheck(testCmd);
|
|
44
|
+
const { passed: testPassed, total: testTotal } =
|
|
45
|
+
testResult.status !== 'skip'
|
|
46
|
+
? extractTestCounts(testResult.output)
|
|
47
|
+
: { passed: 0, total: 0 };
|
|
48
|
+
|
|
49
|
+
// --- Lint ---
|
|
50
|
+
const lintResult = runCheck(lintCmd);
|
|
51
|
+
const lintWarnings =
|
|
52
|
+
lintResult.status !== 'skip'
|
|
53
|
+
? extractCount(lintResult.output, /warning/i)
|
|
54
|
+
: 0;
|
|
55
|
+
|
|
56
|
+
// --- Debug artifacts ---
|
|
57
|
+
const debugArtifacts = collectDebugArtifacts(sessionStartRef);
|
|
58
|
+
|
|
59
|
+
// --- Duration ---
|
|
60
|
+
const durationSeconds = Math.round((Date.now() - startTime) / 1000);
|
|
61
|
+
|
|
62
|
+
// --- Stub map ---
|
|
63
|
+
const stubbed = {};
|
|
64
|
+
if (tcResult.stubbed) stubbed.typecheck = tcResult.stubbed;
|
|
65
|
+
if (testResult.stubbed) stubbed.test = testResult.stubbed;
|
|
66
|
+
if (lintResult.stubbed) stubbed.lint = lintResult.stubbed;
|
|
67
|
+
|
|
68
|
+
// --- Output ---
|
|
69
|
+
const output = {
|
|
70
|
+
variant: 'full-gate',
|
|
71
|
+
duration_seconds: durationSeconds,
|
|
72
|
+
typecheck: { status: tcResult.status, error_count: tcErrorCount },
|
|
73
|
+
test: { status: testResult.status, total: testTotal, passed: testPassed },
|
|
74
|
+
lint: { status: lintResult.status, warnings: lintWarnings },
|
|
75
|
+
debug_artifacts: debugArtifacts,
|
|
76
|
+
stubbed,
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
process.stdout.write(JSON.stringify(output) + '\n');
|
|
80
|
+
|
|
81
|
+
// Exit 2 if any check failed; skipped counts as pass.
|
|
82
|
+
const failed = [tcResult, testResult, lintResult].some(
|
|
83
|
+
(r) => r.status === 'fail',
|
|
84
|
+
);
|
|
85
|
+
process.exit(failed ? 2 : 0);
|