session-orchestrator 3.24.0 → 4.0.1
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/.agents/skills/architecture/SKILL.md +18 -0
- package/.agents/skills/autopilot/SKILL.md +17 -0
- package/.agents/skills/bootstrap/SKILL.md +20 -0
- package/.agents/skills/brainstorm/SKILL.md +22 -0
- package/.agents/skills/claude-md-drift-check/SKILL.md +15 -0
- package/.agents/skills/convergence-monitoring/SKILL.md +22 -0
- package/.agents/skills/debug/SKILL.md +22 -0
- package/.agents/skills/discovery/SKILL.md +20 -0
- package/.agents/skills/dispatcher/SKILL.md +15 -0
- package/.agents/skills/docs-orchestrator/SKILL.md +18 -0
- package/.agents/skills/ecosystem-health/SKILL.md +20 -0
- package/.agents/skills/eli5/SKILL.md +20 -0
- package/.agents/skills/eval/SKILL.md +21 -0
- package/.agents/skills/evolve/SKILL.md +21 -0
- package/.agents/skills/frontmatter-guard/SKILL.md +15 -0
- package/.agents/skills/gitlab-ops/SKILL.md +20 -0
- package/.agents/skills/gitlab-portfolio/SKILL.md +15 -0
- package/.agents/skills/grill/SKILL.md +22 -0
- package/.agents/skills/hook-development/SKILL.md +15 -0
- package/.agents/skills/mcp-builder/SKILL.md +15 -0
- package/.agents/skills/memory-cleanup/SKILL.md +21 -0
- package/.agents/skills/mode-selector/SKILL.md +17 -0
- package/.agents/skills/npm-publish/SKILL.md +16 -0
- package/.agents/skills/peekaboo-driver/SKILL.md +18 -0
- package/.agents/skills/persona-panel/SKILL.md +17 -0
- package/.agents/skills/plan/SKILL.md +20 -0
- package/.agents/skills/playwright-driver/SKILL.md +20 -0
- package/.agents/skills/quality-gates/SKILL.md +20 -0
- package/.agents/skills/reconcile/SKILL.md +21 -0
- package/.agents/skills/remote-offload/SKILL.md +20 -0
- package/.agents/skills/repo-audit/SKILL.md +16 -0
- package/.agents/skills/session-end/SKILL.md +20 -0
- package/.agents/skills/session-plan/SKILL.md +20 -0
- package/.agents/skills/session-start/SKILL.md +20 -0
- package/.agents/skills/spinout/SKILL.md +16 -0
- package/.agents/skills/sunset-review/SKILL.md +16 -0
- package/.agents/skills/test-runner/SKILL.md +20 -0
- package/.agents/skills/tmux-layout/SKILL.md +21 -0
- package/.agents/skills/using-orchestrator/SKILL.md +17 -0
- package/.agents/skills/vault-mirror/SKILL.md +15 -0
- package/.agents/skills/vault-sync/SKILL.md +15 -0
- package/.agents/skills/wave-executor/SKILL.md +20 -0
- package/.agents/skills/write-executable-plan/SKILL.md +22 -0
- package/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/.codex-plugin/plugin.json +3 -2
- package/.codex-plugin/skills/architecture/SKILL.md +20 -0
- package/.codex-plugin/skills/autopilot/SKILL.md +21 -0
- package/.codex-plugin/skills/autopilot/agents/openai.yaml +5 -0
- package/.codex-plugin/skills/bootstrap/SKILL.md +22 -0
- package/.codex-plugin/skills/bootstrap/agents/openai.yaml +5 -0
- package/.codex-plugin/skills/brainstorm/SKILL.md +22 -0
- package/.codex-plugin/skills/brainstorm/agents/openai.yaml +5 -0
- package/.codex-plugin/skills/claude-md-drift-check/SKILL.md +17 -0
- package/.codex-plugin/skills/close/SKILL.md +21 -0
- package/.codex-plugin/skills/close/agents/openai.yaml +5 -0
- package/.codex-plugin/skills/convergence-monitoring/SKILL.md +24 -0
- package/.codex-plugin/skills/debug/SKILL.md +21 -0
- package/.codex-plugin/skills/debug/agents/openai.yaml +5 -0
- package/.codex-plugin/skills/discovery/SKILL.md +21 -0
- package/.codex-plugin/skills/discovery/agents/openai.yaml +5 -0
- package/.codex-plugin/skills/dispatcher/SKILL.md +21 -0
- package/.codex-plugin/skills/dispatcher/agents/openai.yaml +5 -0
- package/.codex-plugin/skills/docs-orchestrator/SKILL.md +20 -0
- package/.codex-plugin/skills/ecosystem-health/SKILL.md +22 -0
- package/.codex-plugin/skills/eli5/SKILL.md +21 -0
- package/.codex-plugin/skills/eli5/agents/openai.yaml +5 -0
- package/.codex-plugin/skills/eval/SKILL.md +21 -0
- package/.codex-plugin/skills/eval/agents/openai.yaml +5 -0
- package/.codex-plugin/skills/evolve/SKILL.md +21 -0
- package/.codex-plugin/skills/evolve/agents/openai.yaml +5 -0
- package/.codex-plugin/skills/frontmatter-guard/SKILL.md +17 -0
- package/.codex-plugin/skills/gitlab-ops/SKILL.md +22 -0
- package/.codex-plugin/skills/gitlab-portfolio/SKILL.md +17 -0
- package/.codex-plugin/skills/go/SKILL.md +22 -0
- package/.codex-plugin/skills/go/agents/openai.yaml +5 -0
- package/.codex-plugin/skills/grill/SKILL.md +21 -0
- package/.codex-plugin/skills/grill/agents/openai.yaml +5 -0
- package/.codex-plugin/skills/harness-audit/SKILL.md +19 -0
- package/.codex-plugin/skills/harness-audit/agents/openai.yaml +5 -0
- package/.codex-plugin/skills/hook-development/SKILL.md +17 -0
- package/.codex-plugin/skills/mcp-builder/SKILL.md +17 -0
- package/.codex-plugin/skills/memory-cleanup/SKILL.md +21 -0
- package/.codex-plugin/skills/memory-cleanup/agents/openai.yaml +5 -0
- package/.codex-plugin/skills/mode-selector/SKILL.md +19 -0
- package/.codex-plugin/skills/npm-publish/SKILL.md +18 -0
- package/.codex-plugin/skills/peekaboo-driver/SKILL.md +20 -0
- package/.codex-plugin/skills/persona-panel/SKILL.md +22 -0
- package/.codex-plugin/skills/persona-panel/agents/openai.yaml +5 -0
- package/.codex-plugin/skills/plan/SKILL.md +22 -0
- package/.codex-plugin/skills/plan/agents/openai.yaml +5 -0
- package/.codex-plugin/skills/playwright-driver/SKILL.md +22 -0
- package/.codex-plugin/skills/portfolio/SKILL.md +21 -0
- package/.codex-plugin/skills/portfolio/agents/openai.yaml +5 -0
- package/.codex-plugin/skills/quality-gates/SKILL.md +22 -0
- package/.codex-plugin/skills/reconcile/SKILL.md +21 -0
- package/.codex-plugin/skills/reconcile/agents/openai.yaml +5 -0
- package/.codex-plugin/skills/release/SKILL.md +22 -0
- package/.codex-plugin/skills/release/agents/openai.yaml +5 -0
- package/.codex-plugin/skills/remote-offload/SKILL.md +22 -0
- package/.codex-plugin/skills/repo-audit/SKILL.md +19 -0
- package/.codex-plugin/skills/repo-audit/agents/openai.yaml +5 -0
- package/.codex-plugin/skills/session/SKILL.md +21 -0
- package/.codex-plugin/skills/session/agents/openai.yaml +5 -0
- package/.codex-plugin/skills/session-end/SKILL.md +22 -0
- package/.codex-plugin/skills/session-plan/SKILL.md +22 -0
- package/.codex-plugin/skills/session-start/SKILL.md +22 -0
- package/.codex-plugin/skills/spinout/SKILL.md +21 -0
- package/.codex-plugin/skills/spinout/agents/openai.yaml +5 -0
- package/.codex-plugin/skills/sunset-review/SKILL.md +21 -0
- package/.codex-plugin/skills/sunset-review/agents/openai.yaml +5 -0
- package/.codex-plugin/skills/templates-ack/SKILL.md +21 -0
- package/.codex-plugin/skills/templates-ack/agents/openai.yaml +5 -0
- package/.codex-plugin/skills/test/SKILL.md +21 -0
- package/.codex-plugin/skills/test/agents/openai.yaml +5 -0
- package/.codex-plugin/skills/test-runner/SKILL.md +22 -0
- package/.codex-plugin/skills/tmux-layout/SKILL.md +23 -0
- package/.codex-plugin/skills/using-orchestrator/SKILL.md +19 -0
- package/.codex-plugin/skills/vault-mirror/SKILL.md +17 -0
- package/.codex-plugin/skills/vault-sync/SKILL.md +17 -0
- package/.codex-plugin/skills/wave-executor/SKILL.md +22 -0
- package/.codex-plugin/skills/write-executable-plan/SKILL.md +24 -0
- package/.cursor/commands/autopilot.md +2 -2
- package/.cursor/commands/bootstrap.md +1 -1
- package/.cursor/commands/brainstorm.md +1 -1
- package/.cursor/commands/debug.md +1 -1
- package/.cursor/commands/discovery.md +1 -1
- package/.cursor/commands/dispatcher.md +2 -2
- package/.cursor/commands/eli5.md +2 -2
- package/.cursor/commands/eval.md +2 -2
- package/.cursor/commands/evolve.md +1 -1
- package/.cursor/commands/go.md +1 -1
- package/.cursor/commands/grill.md +2 -2
- package/.cursor/commands/memory-cleanup.md +2 -2
- package/.cursor/commands/persona-panel.md +1 -1
- package/.cursor/commands/plan.md +1 -1
- package/.cursor/commands/portfolio.md +1 -1
- package/.cursor/commands/reconcile.md +2 -2
- package/.cursor/commands/release.md +2 -2
- package/.cursor/commands/session.md +2 -2
- package/.cursor/commands/spinout.md +2 -2
- package/.cursor/commands/sunset-review.md +2 -2
- package/.cursor/commands/templates-ack.md +2 -2
- package/.cursor/commands/test.md +2 -2
- package/.cursor/skills/brainstorm/SKILL.md +1 -1
- package/.cursor/skills/eval/SKILL.md +1 -1
- package/.cursor/skills/quality-gates/SKILL.md +1 -1
- package/.cursor/skills/remote-offload/SKILL.md +1 -1
- package/.cursor-plugin/plugin.json +30 -0
- package/.orchestrator/policy/blocked-commands.json +121 -0
- package/.orchestrator/policy/ecosystem.schema.json +66 -0
- package/.orchestrator/policy/quality-gates.example.json +16 -0
- package/.orchestrator/policy/quality-gates.schema.json +38 -0
- package/.orchestrator/policy/templates-policy.json +27 -0
- package/.orchestrator/policy/test-profiles.json +47 -0
- package/AGENTS.md +225 -0
- package/CHANGELOG.md +1314 -2
- package/NOTICE +11 -6
- package/README.md +135 -94
- package/agents/eval-judge.md +1 -1
- package/agents/skill-applied-judge.md +1 -1
- package/assets/wave-lifecycle.svg +98 -0
- package/commands/release.md +6 -3
- package/commands/session.md +18 -3
- package/docs/README.md +4 -0
- package/{agents/AGENTS.md → docs/agent-authoring.md} +19 -26
- package/docs/baseline.md +67 -0
- package/docs/ci-setup.md +108 -62
- package/docs/codex-setup.md +107 -29
- package/docs/components.md +38 -16
- package/docs/cursor-setup.md +6 -2
- package/docs/events-schema.md +9 -6
- package/docs/instruction-delivery.md +69 -0
- package/{agents/memory-proposal-collector.md → docs/memory-proposal-flow.md} +1 -8
- package/docs/migration-v4.md +365 -0
- package/docs/pi-setup.md +6 -1
- package/docs/plugin-architecture-v3.md +1 -1
- package/docs/rule-authoring.md +85 -19
- package/docs/scope-collision-guard.md +5 -5
- package/docs/session-config-reference.md +57 -56
- package/docs/session-config-template.md +6 -29
- package/docs/telemetry.md +157 -3
- package/docs/vault-docs-architecture.md +50 -11
- package/hooks/_lib/hook-import-set.json +1488 -0
- package/hooks/_lib/subagent-transcript.mjs +562 -0
- package/hooks/config-protection.mjs +2 -2
- package/hooks/cwd-change-restore.mjs +2 -2
- package/hooks/enforce-commands.mjs +69 -0
- package/hooks/hooks-codex.json +1 -1
- package/hooks/hooks-cursor.json +10 -0
- package/hooks/hooks-pi.json +5 -0
- package/hooks/hooks.json +6 -1
- package/hooks/loop-guard.mjs +3 -3
- package/hooks/on-session-end.mjs +2 -2
- package/hooks/on-session-start.mjs +103 -2
- package/hooks/on-stop.mjs +60 -14
- package/hooks/operator-steer.mjs +2 -2
- package/hooks/post-bash-write-verify.mjs +85 -0
- package/hooks/post-edit-import-probe.mjs +344 -0
- package/hooks/post-subagent-discovery-validator.mjs +187 -431
- package/hooks/post-tool-batch-wave-signal.mjs +118 -4
- package/hooks/post-tool-failure-corrective-context.mjs +2 -2
- package/hooks/post-tooluse-frontend-slop.mjs +3 -3
- package/hooks/pre-bash-destructive-guard.mjs +39 -13
- package/hooks/skill-invocation-telemetry.mjs +17 -5
- package/hooks/subagent-telemetry.mjs +13 -4
- package/monitors/monitors.json +3 -3
- package/package.json +9 -1
- package/pi/prompts/session.md +2 -2
- package/scripts/backfill-abandoned-sessions.mjs +50 -4
- package/scripts/backfill-learnings-from-vault.mjs +9 -3
- package/scripts/dialectic-deriver.mjs +73 -8
- package/scripts/export-hw-learnings.mjs +113 -1
- package/scripts/generate-agents-skills.mjs +378 -0
- package/scripts/generate-codex-skills.mjs +246 -0
- package/scripts/generate-cursor-adapter.mjs +45 -8
- package/scripts/generate-hook-import-set.mjs +292 -0
- package/scripts/lib/agent-status.mjs +13 -2
- package/scripts/lib/auto-dream.mjs +38 -36
- package/scripts/lib/autonomy/suitability.mjs +6 -0
- package/scripts/lib/autopilot/loop.mjs +2 -2
- package/scripts/lib/ci-status-banner.mjs +220 -75
- package/scripts/lib/codex/plugin-contract.mjs +88 -6
- package/scripts/lib/config/auto-dream.mjs +2 -1
- package/scripts/lib/config/block-header.mjs +8 -0
- package/scripts/lib/config/block-preprocess.mjs +177 -0
- package/scripts/lib/config/broken-window.mjs +2 -1
- package/scripts/lib/config/cold-start.mjs +2 -1
- package/scripts/lib/config/config-protection.mjs +22 -2
- package/scripts/lib/config/context-coverage.mjs +2 -1
- package/scripts/lib/config/cross-repo.mjs +2 -1
- package/scripts/lib/config/custom-phases.mjs +2 -1
- package/scripts/lib/config/dialectic.mjs +2 -1
- package/scripts/lib/config/discovery-validator.mjs +2 -1
- package/scripts/lib/config/dispatcher-autonomy-capture.mjs +24 -1
- package/scripts/lib/config/dispatcher-autonomy.mjs +2 -1
- package/scripts/lib/config/docs-orchestrator.mjs +2 -1
- package/scripts/lib/config/docs-staleness.mjs +2 -1
- package/scripts/lib/config/drift-check.mjs +2 -1
- package/scripts/lib/config/eval.mjs +2 -1
- package/scripts/lib/config/events-rotation.mjs +2 -1
- package/scripts/lib/config/evolve.mjs +8 -2
- package/scripts/lib/config/frontend-slop-hook.mjs +7 -3
- package/scripts/lib/config/gitlab-portfolio.mjs +2 -1
- package/scripts/lib/config/handover-gate.mjs +2 -1
- package/scripts/lib/config/health-endpoints.mjs +7 -2
- package/scripts/lib/config/host-paths.mjs +20 -4
- package/scripts/lib/config/issue-budget.mjs +2 -1
- package/scripts/lib/config/loop-guard.mjs +2 -1
- package/scripts/lib/config/memory.mjs +2 -1
- package/scripts/lib/config/moc-staleness.mjs +2 -1
- package/scripts/lib/config/persona-gate-wave.mjs +2 -1
- package/scripts/lib/config/private-config-dir.mjs +67 -0
- package/scripts/lib/config/reconcile.mjs +2 -1
- package/scripts/lib/config/remote-hosts.mjs +2 -1
- package/scripts/lib/config/section-extractor.mjs +7 -1
- package/scripts/lib/config/skill-evolution.mjs +2 -1
- package/scripts/lib/config/slopcheck.mjs +2 -1
- package/scripts/lib/config/state-md-lock.mjs +2 -1
- package/scripts/lib/config/templates-first.mjs +2 -1
- package/scripts/lib/config/test.mjs +2 -1
- package/scripts/lib/config/vault-integration.mjs +7 -1
- package/scripts/lib/config/vault-mirror-quality.mjs +2 -1
- package/scripts/lib/config/vault-staleness.mjs +2 -1
- package/scripts/lib/config/vault-sync.mjs +2 -1
- package/scripts/lib/config/verification-auto-fix.mjs +2 -1
- package/scripts/lib/config/wave-reviewers.mjs +2 -1
- package/scripts/lib/config/worktree-orphans.mjs +2 -1
- package/scripts/lib/convergence-monitor.mjs +82 -16
- package/scripts/lib/dispatcher/rank.mjs +124 -48
- package/scripts/lib/ecosystem-health.mjs +16 -2
- package/scripts/lib/eval/engine.mjs +9 -1
- package/scripts/lib/eval/session-resolve.mjs +23 -4
- package/scripts/lib/events.mjs +22 -6
- package/scripts/lib/frontmatter-guard.mjs +131 -13
- package/scripts/lib/gates/gate-full.mjs +30 -0
- package/scripts/lib/gates/gate-helpers.mjs +76 -0
- package/scripts/lib/hardware-pattern-detector.mjs +18 -1
- package/scripts/lib/harness-audit/categories/category4.mjs +31 -11
- package/scripts/lib/host-identity.mjs +50 -11
- package/scripts/lib/instruction-budget-guard.mjs +171 -5
- package/scripts/lib/learnings/evolve-telemetry.mjs +178 -0
- package/scripts/lib/learnings/io.mjs +60 -6
- package/scripts/lib/memory-proposals/store.mjs +30 -22
- package/scripts/lib/owner-config-banner.mjs +41 -6
- package/scripts/lib/owner-config-loader.mjs +21 -10
- package/scripts/lib/owner-interview.mjs +3 -3
- package/scripts/lib/owner-yaml.mjs +215 -15
- package/scripts/lib/platform.mjs +108 -15
- package/scripts/lib/plugin-update-banner.mjs +414 -0
- package/scripts/lib/project-hygiene.mjs +38 -2
- package/scripts/lib/qg-command-drift-banner.mjs +50 -12
- package/scripts/lib/quality-gate.mjs +133 -44
- package/scripts/lib/reconcile/emitter.mjs +68 -6
- package/scripts/lib/reconcile/engine.mjs +51 -11
- package/scripts/lib/reconcile/idempotency.mjs +37 -4
- package/scripts/lib/reconcile/writer.mjs +40 -18
- package/scripts/lib/session-close-backfill.mjs +67 -9
- package/scripts/lib/session-id.mjs +12 -23
- package/scripts/lib/session-identity/own-session.mjs +125 -10
- package/scripts/lib/session-lock-shape.mjs +43 -0
- package/scripts/lib/session-lock.mjs +5 -10
- package/scripts/lib/session-registry.mjs +25 -9
- package/scripts/lib/session-schema/constants.mjs +64 -3
- package/scripts/lib/session-schema/validator.mjs +38 -4
- package/scripts/lib/session-start-probes.mjs +30 -1
- package/scripts/lib/sessions-staleness-banner.mjs +18 -11
- package/scripts/lib/skill-health/join.mjs +17 -4
- package/scripts/lib/state-md.mjs +78 -0
- package/scripts/lib/sunset/walker.mjs +6 -0
- package/scripts/lib/telemetry/schema.mjs +202 -9
- package/scripts/lib/telemetry/sync.mjs +368 -12
- package/scripts/lib/telemetry-flush-health-banner.mjs +211 -0
- package/scripts/lib/validate/check-agents-skills.mjs +327 -0
- package/scripts/lib/validate/check-agents.mjs +3 -3
- package/scripts/lib/validate/check-codex-skills.mjs +191 -0
- package/scripts/lib/validate/check-cursor-adapter.mjs +234 -72
- package/scripts/lib/validate/check-hooks-symmetry.mjs +45 -16
- package/scripts/lib/validate/check-owner-leakage.mjs +319 -22
- package/scripts/lib/validate/check-skill-links.mjs +193 -0
- package/scripts/lib/validate/check-skill-script-paths.mjs +47 -28
- package/scripts/lib/validate/check-test-git-config-target.mjs +192 -12
- package/scripts/lib/validate/check-unwired-features.mjs +163 -15
- package/scripts/lib/validate/check-validator-registration.mjs +10 -4
- package/scripts/lib/validate/confidential-names.mjs +95 -30
- package/scripts/lib/validate/enumerate-repo-files.mjs +317 -0
- package/scripts/lib/validate/repo-files.mjs +48 -14
- package/scripts/lib/vault-backfill/template.mjs +63 -6
- package/scripts/lib/vault-mirror/process.mjs +165 -42
- package/scripts/lib/vault-mirror/telemetry.mjs +2 -2
- package/scripts/lib/vault-status/narrative-mirror.mjs +127 -18
- package/scripts/lib/wave-executor/dispatch-common.mjs +164 -0
- package/scripts/lib/wave-executor/foreign-dispatch.mjs +7 -142
- package/scripts/lib/wave-executor/remote-dispatch.mjs +5 -7
- package/scripts/lib/wave-resource-gate.mjs +8 -2
- package/scripts/lib/wave-sizing.mjs +4 -1
- package/scripts/lib/wave-transcript-tail.mjs +118 -4
- package/scripts/materialize-wave-scope.mjs +12 -5
- package/scripts/memory-propose.mjs +19 -5
- package/scripts/migrate-cold-start-seed.mjs +4 -1
- package/scripts/parse-config.mjs +60 -3
- package/scripts/release.mjs +430 -31
- package/scripts/repair-invalid-sessions.mjs +3 -3
- package/scripts/run-quality-gate.mjs +128 -11
- package/scripts/site-numbers.mjs +344 -8
- package/scripts/sweep-expired-learnings.mjs +90 -0
- package/scripts/sync-vault-schema.mjs +3 -1
- package/scripts/telemetry.mjs +2 -2
- package/scripts/validate-plugin.mjs +164 -0
- package/scripts/validate-wave-scope.mjs +28 -8
- package/scripts/wave-scope-binding.mjs +215 -0
- package/skills/_shared/instruction-file-resolution.md +10 -0
- package/skills/_shared/parallel-aware-preamble.md +1 -0
- package/skills/_shared/platform-tools.md +1 -1
- package/skills/_shared/state-ownership.md +1 -1
- package/skills/architecture/SKILL.md +7 -5
- package/skills/{domain-model/SKILL.md → architecture/references/domain-model.md} +9 -9
- package/skills/autopilot/SKILL.md +4 -18
- package/skills/claude-md-drift-check/SKILL.md +5 -1
- package/skills/claude-md-drift-check/checker.mjs +62 -2
- package/skills/convergence-monitoring/SIGNALS.md +55 -0
- package/skills/discovery/probes/vault-staleness.mjs +37 -13
- package/skills/discovery/probes-arch.md +20 -18
- package/skills/dispatcher/SKILL.md +3 -2
- package/skills/evolve/SKILL.md +65 -26
- package/skills/frontmatter-guard/SKILL.md +11 -5
- package/skills/npm-publish/SKILL.md +1 -1
- package/skills/reconcile/SKILL.md +33 -0
- package/skills/remote-offload/SKILL.md +1 -1
- package/skills/session-end/SKILL.md +18 -905
- package/skills/session-end/phase-3-6-tail.md +10 -3
- package/skills/session-end/plan-verification.md +221 -155
- package/skills/session-end/references/phase-2-quality-gate.md +93 -0
- package/skills/session-end/references/phase-3-documentation-updates.md +229 -0
- package/skills/session-end/references/phase-4a-worktree-cleanup.md +120 -0
- package/skills/session-end/references/phase-4b-worktree-orphan-sweep.md +58 -0
- package/skills/session-end/references/phase-5-issue-cleanup.md +104 -0
- package/skills/session-end/references/session-summary-template.md +62 -0
- package/skills/session-plan/SKILL.md +49 -0
- package/skills/session-start/SKILL.md +22 -904
- package/skills/session-start/phase-8-5-express-path.md +1 -1
- package/skills/session-start/references/phase-1-1-dispatcher-autonomy-capture.md +55 -0
- package/skills/session-start/references/phase-1-2-session-lock.md +140 -0
- package/skills/session-start/references/phase-1-5-session-continuity.md +254 -0
- package/skills/session-start/references/phase-1-7-vault-status-board.md +53 -0
- package/skills/session-start/references/phase-2-7-portfolio-snapshot.md +75 -0
- package/skills/session-start/references/phase-4-ssot-environment-check.md +160 -0
- package/skills/session-start/references/phase-6-5-forced-reads.md +75 -0
- package/skills/session-start/references/phase-6-6-project-intelligence.md +81 -0
- package/skills/session-start/references/phase-6-7-memory-banner-telemetry-consent.md +103 -0
- package/skills/vault-sync/SKILL.md +10 -0
- package/skills/vault-sync/validator.mjs +21 -27
- package/skills/wave-executor/SKILL.md +15 -1
- package/skills/wave-executor/references/wave-loop-dispatch.md +612 -0
- package/skills/wave-executor/references/wave-loop-review.md +570 -0
- package/skills/wave-executor/references/wave-loop-scope-manifest.md +162 -0
- package/skills/wave-executor/wave-loop.md +14 -1309
- package/templates/_shared/journey-manifest.md +10 -6
- package/.cursor/commands/autopilot-multi.md +0 -14
- package/.cursor/commands/contract-version-bump.md +0 -14
- package/.cursor/commands/journey-audit.md +0 -14
- package/.cursor/skills/contract-version-bump/SKILL.md +0 -12
- package/.cursor/skills/daily/SKILL.md +0 -12
- package/.cursor/skills/domain-model/SKILL.md +0 -13
- package/.cursor/skills/journey-audit/SKILL.md +0 -13
- package/.cursor/skills/skill-creator/SKILL.md +0 -13
- package/.cursor/skills/ubiquitous-language/SKILL.md +0 -13
- package/commands/autopilot-multi.md +0 -74
- package/commands/contract-version-bump.md +0 -28
- package/commands/journey-audit.md +0 -43
- package/pi/prompts/autopilot-multi.md +0 -12
- package/pi/prompts/contract-version-bump.md +0 -12
- package/pi/prompts/journey-audit.md +0 -12
- package/scripts/autopilot-multi.mjs +0 -885
- package/scripts/backfill-learnings-expires.mjs +0 -196
- package/scripts/backfill-learnings.mjs +0 -203
- package/scripts/fleet-instruction-scan.mjs +0 -141
- package/scripts/lib/autopilot/dep-graph.mjs +0 -417
- package/scripts/lib/autopilot/multi-killswitch.mjs +0 -184
- package/scripts/lib/webhook-url.mjs +0 -105
- package/scripts/lifecycle-sim-v6.mjs +0 -347
- package/scripts/migrate-learnings-jsonl.mjs +0 -189
- package/scripts/migrate-subagents-jsonl.mjs +0 -196
- package/scripts/upload-social-preview.mjs +0 -316
- package/skills/_shared/model-selection.md +0 -64
- package/skills/contract-version-bump/SKILL.md +0 -219
- package/skills/daily/SKILL.md +0 -222
- package/skills/daily/generate.sh +0 -92
- package/skills/daily/templates/daily.md.tpl +0 -36
- package/skills/journey-audit/SKILL.md +0 -270
- package/skills/skill-creator/SKILL.md +0 -168
- package/skills/ubiquitous-language/SKILL.md +0 -97
- package/skills/vault-sync/package-lock.json +0 -40
- /package/skills/{domain-model → architecture/references}/ADR-FORMAT.md +0 -0
- /package/skills/{domain-model → architecture/references}/CONTEXT-FORMAT.md +0 -0
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Independently inspect Codex discovery artifacts; generator freshness alone
|
|
3
|
+
// cannot detect a generator that consistently emits an invalid skill contract.
|
|
4
|
+
import { existsSync, readFileSync, readdirSync, realpathSync } from 'node:fs';
|
|
5
|
+
import { dirname, join, relative, resolve, sep } from 'node:path';
|
|
6
|
+
import { pathToFileURL } from 'node:url';
|
|
7
|
+
import yaml from 'js-yaml';
|
|
8
|
+
|
|
9
|
+
const SKILL_ROOT = '.codex-plugin/skills';
|
|
10
|
+
const FRONTMATTER_KEYS = new Set([
|
|
11
|
+
'name', 'description', 'license', 'compatibility', 'metadata', 'allowed-tools',
|
|
12
|
+
]);
|
|
13
|
+
|
|
14
|
+
/** @param {unknown} value */
|
|
15
|
+
function isRecord(value) {
|
|
16
|
+
return value !== null && typeof value === 'object' && !Array.isArray(value);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Read emitted artifacts against their source contract without importing the
|
|
21
|
+
* generator or comparing its prose. Paths are resolved as installed files.
|
|
22
|
+
* @param {string} pluginRoot
|
|
23
|
+
* @returns {{skills: string[], violations: string[]}}
|
|
24
|
+
*/
|
|
25
|
+
export function validateCodexSkills(pluginRoot) {
|
|
26
|
+
const root = realpathSync(pluginRoot);
|
|
27
|
+
const violations = [];
|
|
28
|
+
const sources = new Map();
|
|
29
|
+
const skills = [];
|
|
30
|
+
const label = (file) => relative(root, file).split(sep).join('/');
|
|
31
|
+
|
|
32
|
+
function text(file) {
|
|
33
|
+
try {
|
|
34
|
+
const actual = realpathSync(file);
|
|
35
|
+
if (!actual.startsWith(`${root}${sep}`)) {
|
|
36
|
+
violations.push(`${label(file)}: outside the plugin package`);
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
return readFileSync(file, 'utf8');
|
|
40
|
+
} catch (error) {
|
|
41
|
+
violations.push(`${label(file)}: missing or unreadable (${error.code ?? error.message})`);
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function mapping(raw, file) {
|
|
47
|
+
if (raw === null) return null;
|
|
48
|
+
try {
|
|
49
|
+
const parsed = yaml.load(raw, { schema: yaml.CORE_SCHEMA });
|
|
50
|
+
if (!isRecord(parsed)) throw new Error('expected a YAML mapping');
|
|
51
|
+
return parsed;
|
|
52
|
+
} catch (error) {
|
|
53
|
+
violations.push(`${label(file)}: invalid YAML (${error.message.split('\n')[0]})`);
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function frontmatter(file) {
|
|
59
|
+
const raw = text(file);
|
|
60
|
+
if (raw === null) return null;
|
|
61
|
+
const match = /^---\r?\n([\s\S]*?)\r?\n---(?:\r?\n|$)/.exec(raw);
|
|
62
|
+
if (!match) {
|
|
63
|
+
violations.push(`${label(file)}: missing YAML frontmatter`);
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
66
|
+
const fm = mapping(match[1], file);
|
|
67
|
+
return fm && { fm, body: raw.slice(match[0].length) };
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Codex's Agent Plugins loader fixes skills to ./skills; its native overlay
|
|
71
|
+
// cannot override skills or version. A valid native manifest alone is not
|
|
72
|
+
// enough when a standard root manifest intercepts discovery (CLI 0.153.3/4).
|
|
73
|
+
const rootManifestFile = join(root, 'plugin.json');
|
|
74
|
+
if (existsSync(rootManifestFile)) {
|
|
75
|
+
try {
|
|
76
|
+
const manifest = JSON.parse(text(rootManifestFile));
|
|
77
|
+
if (typeof manifest?.$schema === 'string' && manifest.$schema.startsWith('https://agent-plugins.org/schemas/')) {
|
|
78
|
+
violations.push('Root Agent Plugins manifest overrides native Codex skills and cache version; use the native harness manifests');
|
|
79
|
+
}
|
|
80
|
+
} catch (error) {
|
|
81
|
+
violations.push(`Root plugin manifest: invalid JSON (${error.message})`);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const manifestFile = join(root, '.codex-plugin/plugin.json');
|
|
86
|
+
try {
|
|
87
|
+
const raw = text(manifestFile);
|
|
88
|
+
const manifest = raw === null ? null : JSON.parse(raw);
|
|
89
|
+
if (manifest?.skills !== `./${SKILL_ROOT}/`) {
|
|
90
|
+
violations.push(`Codex manifest skills must register only ./${SKILL_ROOT}/`);
|
|
91
|
+
}
|
|
92
|
+
if (!Array.isArray(manifest?.commands) || manifest.commands.length !== 0) {
|
|
93
|
+
violations.push('Codex manifest commands must be an explicit empty array to disable automatic command migration');
|
|
94
|
+
}
|
|
95
|
+
} catch (error) {
|
|
96
|
+
violations.push(`Codex manifest: invalid JSON (${error.message})`);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Skill first, command second: a command owns the public entry on overlap.
|
|
100
|
+
for (const kind of ['skill', 'command']) {
|
|
101
|
+
const directory = join(root, kind === 'skill' ? 'skills' : 'commands');
|
|
102
|
+
if (!existsSync(directory)) {
|
|
103
|
+
violations.push(`${label(directory)}: canonical source directory is missing`);
|
|
104
|
+
continue;
|
|
105
|
+
}
|
|
106
|
+
for (const entry of readdirSync(directory, { withFileTypes: true })) {
|
|
107
|
+
const name = kind === 'command' ? entry.name.replace(/\.md$/, '') : entry.name;
|
|
108
|
+
if (name.startsWith('.') || name.startsWith('_')) continue;
|
|
109
|
+
if (kind === 'command' && !entry.name.endsWith('.md')) continue;
|
|
110
|
+
const file = kind === 'command' ? join(directory, entry.name) : join(directory, name, 'SKILL.md');
|
|
111
|
+
if (kind === 'skill' && !existsSync(file)) continue;
|
|
112
|
+
const source = frontmatter(file);
|
|
113
|
+
sources.set(name, { kind, file, fm: source?.fm });
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
if (sources.size === 0) violations.push('No canonical commands or skills found');
|
|
117
|
+
|
|
118
|
+
const generated = join(root, SKILL_ROOT);
|
|
119
|
+
const actualNames = existsSync(generated)
|
|
120
|
+
? readdirSync(generated, { withFileTypes: true })
|
|
121
|
+
.filter((entry) => (entry.isDirectory() || entry.isSymbolicLink()) && existsSync(join(generated, entry.name, 'SKILL.md')))
|
|
122
|
+
.map((entry) => entry.name)
|
|
123
|
+
: [];
|
|
124
|
+
for (const name of [...new Set([...sources.keys(), ...actualNames])].sort()) {
|
|
125
|
+
const source = sources.get(name);
|
|
126
|
+
if (!source) {
|
|
127
|
+
violations.push(`${SKILL_ROOT}/${name}: no canonical source`);
|
|
128
|
+
continue;
|
|
129
|
+
}
|
|
130
|
+
const file = join(generated, name, 'SKILL.md');
|
|
131
|
+
const artifact = frontmatter(file);
|
|
132
|
+
if (!artifact) continue;
|
|
133
|
+
skills.push(name);
|
|
134
|
+
const { fm, body } = artifact;
|
|
135
|
+
if (fm.name !== name) violations.push(`${label(file)}: name must equal ${name}`);
|
|
136
|
+
if (typeof fm.description !== 'string' || !fm.description.trim() || fm.description.length > 1024) {
|
|
137
|
+
violations.push(`${label(file)}: description must be a non-empty string of at most 1024 characters`);
|
|
138
|
+
}
|
|
139
|
+
for (const key of Object.keys(fm)) {
|
|
140
|
+
if (!FRONTMATTER_KEYS.has(key)) violations.push(`${label(file)}: unsupported frontmatter key ${key}`);
|
|
141
|
+
}
|
|
142
|
+
if (fm.metadata !== undefined && (!isRecord(fm.metadata) || Object.values(fm.metadata).some((value) => typeof value !== 'string'))) {
|
|
143
|
+
violations.push(`${label(file)}: metadata must contain string values`);
|
|
144
|
+
}
|
|
145
|
+
const links = [...body.matchAll(/\]\(([^\s)]+)\)/g)].map((match) => match[1]);
|
|
146
|
+
if (!links.some((target) => !target.includes(':') && !target.startsWith('/') && resolve(dirname(file), target) === source.file)) {
|
|
147
|
+
violations.push(`${label(file)}: missing canonical link to ${label(source.file)}`);
|
|
148
|
+
}
|
|
149
|
+
if (source.kind === 'command' && source.fm) {
|
|
150
|
+
const disabled = source.fm['disable-model-invocation'];
|
|
151
|
+
if (disabled !== undefined && typeof disabled !== 'boolean') {
|
|
152
|
+
violations.push(`${label(source.file)}: disable-model-invocation must be boolean`);
|
|
153
|
+
}
|
|
154
|
+
const policyFile = join(generated, name, 'agents/openai.yaml');
|
|
155
|
+
const sidecar = mapping(text(policyFile), policyFile);
|
|
156
|
+
if (sidecar && (typeof sidecar.policy?.allow_implicit_invocation !== 'boolean'
|
|
157
|
+
|| sidecar.policy.allow_implicit_invocation !== (disabled !== true))) {
|
|
158
|
+
violations.push(`${label(policyFile)}: allow_implicit_invocation must preserve the command's boolean policy`);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
return { skills, violations };
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
async function main(pluginRoot) {
|
|
166
|
+
if (!pluginRoot) throw new Error('Usage: check-codex-skills.mjs <plugin-root>');
|
|
167
|
+
console.log('--- Check: Codex command skills and generated freshness ---');
|
|
168
|
+
const { skills, violations } = validateCodexSkills(pluginRoot);
|
|
169
|
+
if (violations.length === 0) {
|
|
170
|
+
// Invoke the function directly: a child CLI that silently skips its entry
|
|
171
|
+
// point must never turn a missing freshness comparison into success.
|
|
172
|
+
const { generateCodexSurface } = await import('../../generate-codex-skills.mjs');
|
|
173
|
+
const fresh = generateCodexSurface({ pluginRoot, check: true });
|
|
174
|
+
if (!fresh.ok || !Array.isArray(fresh.files) || fresh.files.length === 0) {
|
|
175
|
+
violations.push(...(fresh.drift ?? []), ...(fresh.errors ?? []));
|
|
176
|
+
if (violations.length === 0) violations.push('Codex generator did not report a successful artifact comparison');
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
for (const violation of violations) console.log(` FAIL: ${violation}`);
|
|
180
|
+
if (violations.length === 0) console.log(` PASS: ${skills.length} unique Codex skills, command policies and generated files in sync`);
|
|
181
|
+
console.log(`Results: ${violations.length === 0 ? 1 : 0} passed, ${violations.length} failed`);
|
|
182
|
+
if (violations.length) console.log('Remedy: node scripts/generate-codex-skills.mjs');
|
|
183
|
+
process.exitCode = violations.length ? 1 : 0;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
if (process.argv[1] && import.meta.url === pathToFileURL(realpathSync(process.argv[1])).href) {
|
|
187
|
+
main(process.argv[2]).catch((error) => {
|
|
188
|
+
console.error(`FAIL: Codex skills check: ${error.message}`);
|
|
189
|
+
process.exitCode = 1;
|
|
190
|
+
});
|
|
191
|
+
}
|
|
@@ -4,99 +4,261 @@
|
|
|
4
4
|
// Exit 0 = all checks passed, 1 = at least one failure.
|
|
5
5
|
|
|
6
6
|
import { spawnSync } from 'node:child_process';
|
|
7
|
-
import { existsSync, readFileSync } from 'node:fs';
|
|
8
|
-
import { join } from 'node:path';
|
|
7
|
+
import { existsSync, readdirSync, readFileSync, statSync } from 'node:fs';
|
|
8
|
+
import { join, relative } from 'node:path';
|
|
9
|
+
import { pathToFileURL } from 'node:url';
|
|
10
|
+
|
|
11
|
+
import yaml from 'js-yaml';
|
|
9
12
|
|
|
10
13
|
import { CURSOR_TO_CANONICAL_EVENT } from '../cursor-hook-bridge.mjs';
|
|
11
14
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
console.error('Usage: check-cursor-adapter.mjs <plugin-root>');
|
|
15
|
-
process.exit(1);
|
|
16
|
-
}
|
|
15
|
+
/** agentskills.io caps a skill/command description at 1024 characters. */
|
|
16
|
+
const DESCRIPTION_MAX = 1024;
|
|
17
17
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
encoding: 'utf8',
|
|
33
|
-
stdio: ['ignore', 'pipe', 'pipe'],
|
|
34
|
-
});
|
|
35
|
-
if (result.status === 0) {
|
|
36
|
-
pass('.cursor/commands and .cursor/skills are up to date with commands/ and skills/');
|
|
37
|
-
} else {
|
|
38
|
-
const detail = ((result.stdout ?? '') + (result.stderr ?? '')).trim();
|
|
39
|
-
fail(`Cursor adapter wrappers are stale${detail ? `: ${detail}` : ''}`);
|
|
18
|
+
/**
|
|
19
|
+
* Locate a top-level frontmatter key's line number (1-based) so a violation can
|
|
20
|
+
* be reported as `file:line` rather than `file`.
|
|
21
|
+
*
|
|
22
|
+
* @param {string} content raw file content
|
|
23
|
+
* @param {string} key frontmatter key
|
|
24
|
+
* @returns {number} 1-based line number, or 1 when the key is absent
|
|
25
|
+
*/
|
|
26
|
+
function frontmatterKeyLine(content, key) {
|
|
27
|
+
const lines = content.split('\n');
|
|
28
|
+
const needle = new RegExp(`^${key.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}\\s*:`);
|
|
29
|
+
for (let i = 0; i < lines.length; i += 1) {
|
|
30
|
+
if (i > 0 && lines[i] === '---') break; // end of frontmatter
|
|
31
|
+
if (needle.test(lines[i])) return i + 1;
|
|
40
32
|
}
|
|
33
|
+
return 1;
|
|
41
34
|
}
|
|
42
35
|
|
|
43
|
-
|
|
44
|
-
|
|
36
|
+
/**
|
|
37
|
+
* Validate ONE generated artefact's frontmatter against the Cursor/agentskills
|
|
38
|
+
* spec, independent of the generator that produced it.
|
|
39
|
+
*
|
|
40
|
+
* @param {string} filePath absolute path to the artefact
|
|
41
|
+
* @param {string} rel path to report violations against
|
|
42
|
+
* @param {{requireName: boolean}} opts
|
|
43
|
+
* @returns {string[]} violation strings, empty when the artefact is conformant
|
|
44
|
+
*/
|
|
45
|
+
function validateArtefactFrontmatter(filePath, rel, { requireName }) {
|
|
46
|
+
const violations = [];
|
|
47
|
+
const content = readFileSync(filePath, 'utf8');
|
|
48
|
+
|
|
49
|
+
if (!content.startsWith('---\n')) {
|
|
50
|
+
return [`${rel}:1 — no YAML frontmatter block (file must start with '---')`];
|
|
51
|
+
}
|
|
52
|
+
const end = content.indexOf('\n---\n', 3);
|
|
53
|
+
if (end === -1) {
|
|
54
|
+
return [`${rel}:1 — frontmatter block is never closed`];
|
|
55
|
+
}
|
|
45
56
|
|
|
46
|
-
|
|
47
|
-
if (!existsSync(hooksPath)) {
|
|
48
|
-
fail('.cursor/hooks.json exists');
|
|
49
|
-
} else {
|
|
50
|
-
let hooksJson;
|
|
57
|
+
let fm;
|
|
51
58
|
try {
|
|
52
|
-
|
|
53
|
-
pass('.cursor/hooks.json is valid JSON');
|
|
59
|
+
fm = yaml.load(content.slice(4, end + 1), { schema: yaml.CORE_SCHEMA });
|
|
54
60
|
} catch (err) {
|
|
55
|
-
|
|
56
|
-
|
|
61
|
+
return [`${rel}:1 — frontmatter is not parseable YAML: ${err.message.split('\n')[0]}`];
|
|
62
|
+
}
|
|
63
|
+
if (fm === null || typeof fm !== 'object' || Array.isArray(fm)) {
|
|
64
|
+
return [`${rel}:1 — frontmatter does not parse to a mapping`];
|
|
57
65
|
}
|
|
58
66
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
67
|
+
// `argument-hint`, when present, MUST be a string. Its canonical authored
|
|
68
|
+
// form is `[mode] [--flag]`, which YAML reads as a flow SEQUENCE unless the
|
|
69
|
+
// generator quotes it — GH#54, which made Copilot CLI >= 1.0.65 silently drop
|
|
70
|
+
// the file. Type, not presence, is the assertion: an array here is the bug.
|
|
71
|
+
if (Object.hasOwn(fm, 'argument-hint')) {
|
|
72
|
+
const hint = fm['argument-hint'];
|
|
73
|
+
if (typeof hint !== 'string') {
|
|
74
|
+
violations.push(
|
|
75
|
+
`${rel}:${frontmatterKeyLine(content, 'argument-hint')} — argument-hint must be a string, got ${Array.isArray(hint) ? 'array' : typeof hint} (GH#54: an unquoted [a] [b] is a YAML flow sequence)`,
|
|
76
|
+
);
|
|
77
|
+
} else if (hint.trim() === '') {
|
|
78
|
+
violations.push(`${rel}:${frontmatterKeyLine(content, 'argument-hint')} — argument-hint is present but empty`);
|
|
64
79
|
}
|
|
80
|
+
}
|
|
65
81
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
const
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
82
|
+
const required = requireName ? ['name', 'description'] : ['description'];
|
|
83
|
+
for (const key of required) {
|
|
84
|
+
const value = fm[key];
|
|
85
|
+
if (typeof value !== 'string' || value.trim() === '') {
|
|
86
|
+
violations.push(
|
|
87
|
+
`${rel}:${frontmatterKeyLine(content, key)} — ${key} must be a non-empty string, got ${value === undefined ? 'nothing' : (Array.isArray(value) ? 'array' : typeof value)}`,
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
if (typeof fm.description === 'string' && fm.description.length > DESCRIPTION_MAX) {
|
|
93
|
+
violations.push(
|
|
94
|
+
`${rel}:${frontmatterKeyLine(content, 'description')} — description is ${fm.description.length} chars, over the ${DESCRIPTION_MAX} limit (agentskills.io)`,
|
|
95
|
+
);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
return violations;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Validate every generated Cursor artefact under `<pluginRoot>/.cursor`.
|
|
103
|
+
*
|
|
104
|
+
* This deliberately does NOT run the generator. Comparing generator output to
|
|
105
|
+
* generator output — which is all `--check` does — cannot see a defect the
|
|
106
|
+
* generator itself emits: it would report "up to date" while every file is
|
|
107
|
+
* broken, and a snapshot-style guard would freeze the broken files as the
|
|
108
|
+
* expectation. This reads the artefacts and judges them against the spec.
|
|
109
|
+
*
|
|
110
|
+
* @param {string} pluginRoot
|
|
111
|
+
* @returns {{files: number, violations: string[]}}
|
|
112
|
+
*/
|
|
113
|
+
export function validateCursorArtefacts(pluginRoot) {
|
|
114
|
+
const violations = [];
|
|
115
|
+
let files = 0;
|
|
116
|
+
|
|
117
|
+
const commandsDir = join(pluginRoot, '.cursor', 'commands');
|
|
118
|
+
if (existsSync(commandsDir)) {
|
|
119
|
+
for (const name of readdirSync(commandsDir).filter((n) => n.endsWith('.md')).sort()) {
|
|
120
|
+
const filePath = join(commandsDir, name);
|
|
121
|
+
files += 1;
|
|
122
|
+
violations.push(
|
|
123
|
+
...validateArtefactFrontmatter(filePath, relative(pluginRoot, filePath), { requireName: false }),
|
|
124
|
+
);
|
|
75
125
|
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
const skillsDir = join(pluginRoot, '.cursor', 'skills');
|
|
129
|
+
if (existsSync(skillsDir)) {
|
|
130
|
+
for (const name of readdirSync(skillsDir).sort()) {
|
|
131
|
+
const filePath = join(skillsDir, name, 'SKILL.md');
|
|
132
|
+
let isDir;
|
|
133
|
+
try { isDir = statSync(join(skillsDir, name)).isDirectory(); } catch { isDir = false; }
|
|
134
|
+
if (!isDir || !existsSync(filePath)) continue;
|
|
135
|
+
files += 1;
|
|
136
|
+
violations.push(
|
|
137
|
+
...validateArtefactFrontmatter(filePath, relative(pluginRoot, filePath), { requireName: true }),
|
|
138
|
+
);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
return { files, violations };
|
|
143
|
+
}
|
|
76
144
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
145
|
+
/**
|
|
146
|
+
* CLI entry point. Kept behind a direct-invocation guard so the module can be
|
|
147
|
+
* imported for {@link validateCursorArtefacts} without running (and exiting).
|
|
148
|
+
*
|
|
149
|
+
* @param {string|undefined} pluginRoot
|
|
150
|
+
* @returns {never}
|
|
151
|
+
*/
|
|
152
|
+
function runCli(pluginRoot) {
|
|
153
|
+
if (!pluginRoot) {
|
|
154
|
+
console.error('Usage: check-cursor-adapter.mjs <plugin-root>');
|
|
155
|
+
process.exit(1);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
let passed = 0;
|
|
159
|
+
let failed = 0;
|
|
160
|
+
|
|
161
|
+
function pass(msg) { console.log(` PASS: ${msg}`); passed += 1; }
|
|
162
|
+
function fail(msg) { console.log(` FAIL: ${msg}`); failed += 1; }
|
|
163
|
+
|
|
164
|
+
console.log('--- Check 1: generated Cursor command and skill wrappers ---');
|
|
165
|
+
|
|
166
|
+
const generator = join(pluginRoot, 'scripts', 'generate-cursor-adapter.mjs');
|
|
167
|
+
if (!existsSync(generator)) {
|
|
168
|
+
fail('scripts/generate-cursor-adapter.mjs exists');
|
|
169
|
+
} else {
|
|
170
|
+
const result = spawnSync(process.execPath, [generator, '--check'], {
|
|
171
|
+
cwd: pluginRoot,
|
|
172
|
+
encoding: 'utf8',
|
|
173
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
81
174
|
});
|
|
82
|
-
if (
|
|
83
|
-
pass('.cursor/
|
|
175
|
+
if (result.status === 0) {
|
|
176
|
+
pass('.cursor/commands and .cursor/skills are up to date with commands/ and skills/');
|
|
84
177
|
} else {
|
|
85
|
-
|
|
178
|
+
const detail = ((result.stdout ?? '') + (result.stderr ?? '')).trim();
|
|
179
|
+
fail(`Cursor adapter wrappers are stale${detail ? `: ${detail}` : ''}`);
|
|
86
180
|
}
|
|
87
181
|
}
|
|
88
|
-
}
|
|
89
182
|
|
|
90
|
-
console.log('');
|
|
91
|
-
console.log('--- Check
|
|
183
|
+
console.log('');
|
|
184
|
+
console.log('--- Check 1b: generated artefacts conform to the frontmatter spec ---');
|
|
185
|
+
|
|
186
|
+
// Check 1 above runs the generator against its own output. That comparison is
|
|
187
|
+
// structurally blind to a defect the generator EMITS — it reported "up to
|
|
188
|
+
// date" while 24 of 28 command wrappers carried an array-shaped
|
|
189
|
+
// `argument-hint` (GH#54). Check 1b judges the artefacts against the spec
|
|
190
|
+
// instead, so a generator bug surfaces as a failure rather than as a snapshot.
|
|
191
|
+
const { files: artefactCount, violations } = validateCursorArtefacts(pluginRoot);
|
|
192
|
+
if (violations.length === 0) {
|
|
193
|
+
pass(`${artefactCount} generated Cursor artefact(s) have spec-conformant frontmatter`);
|
|
194
|
+
} else {
|
|
195
|
+
for (const violation of violations) process.stderr.write(`FAIL: ${violation}\n`);
|
|
196
|
+
fail(`${violations.length} generated Cursor artefact(s) violate the frontmatter spec (see stderr for file:line)`);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
console.log('');
|
|
200
|
+
console.log('--- Check 2: .cursor/hooks.json native manifest ---');
|
|
201
|
+
|
|
202
|
+
const hooksPath = join(pluginRoot, '.cursor', 'hooks.json');
|
|
203
|
+
if (!existsSync(hooksPath)) {
|
|
204
|
+
fail('.cursor/hooks.json exists');
|
|
205
|
+
} else {
|
|
206
|
+
let hooksJson;
|
|
207
|
+
try {
|
|
208
|
+
hooksJson = JSON.parse(readFileSync(hooksPath, 'utf8'));
|
|
209
|
+
pass('.cursor/hooks.json is valid JSON');
|
|
210
|
+
} catch (err) {
|
|
211
|
+
fail(`.cursor/hooks.json is not valid JSON: ${err.message}`);
|
|
212
|
+
hooksJson = null;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
if (hooksJson) {
|
|
216
|
+
if (hooksJson.version === 1) {
|
|
217
|
+
pass('.cursor/hooks.json version is 1');
|
|
218
|
+
} else {
|
|
219
|
+
fail('.cursor/hooks.json version must be 1');
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
const declared = Object.keys(hooksJson.hooks || {});
|
|
223
|
+
const expected = Object.keys(CURSOR_TO_CANONICAL_EVENT);
|
|
224
|
+
const missing = expected.filter((event) => !declared.includes(event));
|
|
225
|
+
const extra = declared.filter((event) => !expected.includes(event));
|
|
226
|
+
if (missing.length === 0 && extra.length === 0) {
|
|
227
|
+
pass(`.cursor/hooks.json events match CURSOR_TO_CANONICAL_EVENT (${expected.length})`);
|
|
228
|
+
} else {
|
|
229
|
+
if (missing.length > 0) fail(`.cursor/hooks.json missing events: ${missing.join(', ')}`);
|
|
230
|
+
if (extra.length > 0) fail(`.cursor/hooks.json extra events: ${extra.join(', ')}`);
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
const bridgeRef = 'scripts/lib/cursor-hook-bridge.mjs';
|
|
234
|
+
const allPointAtBridge = declared.every((event) => {
|
|
235
|
+
const entries = Array.isArray(hooksJson.hooks[event]) ? hooksJson.hooks[event] : [];
|
|
236
|
+
return entries.some((entry) => typeof entry.command === 'string' && entry.command.includes(bridgeRef) && entry.command.includes(`--event ${event}`));
|
|
237
|
+
});
|
|
238
|
+
if (allPointAtBridge) {
|
|
239
|
+
pass('.cursor/hooks.json commands invoke cursor-hook-bridge.mjs with --event');
|
|
240
|
+
} else {
|
|
241
|
+
fail('.cursor/hooks.json commands must invoke scripts/lib/cursor-hook-bridge.mjs --event <name>');
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
console.log('');
|
|
247
|
+
console.log('--- Check 3: Cursor hook bridge module ---');
|
|
248
|
+
|
|
249
|
+
const bridgePath = join(pluginRoot, 'scripts', 'lib', 'cursor-hook-bridge.mjs');
|
|
250
|
+
if (existsSync(bridgePath)) {
|
|
251
|
+
pass('scripts/lib/cursor-hook-bridge.mjs exists');
|
|
252
|
+
} else {
|
|
253
|
+
fail('scripts/lib/cursor-hook-bridge.mjs exists');
|
|
254
|
+
}
|
|
92
255
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
} else {
|
|
97
|
-
fail('scripts/lib/cursor-hook-bridge.mjs exists');
|
|
256
|
+
console.log('');
|
|
257
|
+
console.log(`Results: ${passed} passed, ${failed} failed`);
|
|
258
|
+
process.exit(failed > 0 ? 1 : 0);
|
|
98
259
|
}
|
|
99
260
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
process.
|
|
261
|
+
// Direct invocation only — importing this module must not run the CLI.
|
|
262
|
+
if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {
|
|
263
|
+
runCli(process.argv[2]);
|
|
264
|
+
}
|
|
@@ -66,11 +66,33 @@ const DOCUMENTED_ASYMMETRIES = {
|
|
|
66
66
|
// A handler missing on a shared event and NOT listed here → FAIL.
|
|
67
67
|
handlerAsymmetries: {
|
|
68
68
|
codex: {
|
|
69
|
-
//
|
|
70
|
-
//
|
|
71
|
-
//
|
|
72
|
-
//
|
|
73
|
-
//
|
|
69
|
+
// WHY these stay unwired — corrected 2026-09-06 against the shipped
|
|
70
|
+
// codex-cli 0.144.4 binary, because the previous rationale here was
|
|
71
|
+
// measurably wrong and pointed at the wrong repair.
|
|
72
|
+
//
|
|
73
|
+
// REFUTED: "no Codex bridge delivers tool_name (grep scripts/lib/codex/
|
|
74
|
+
// *.mjs -> 0 matches)". That grep measured OUR adapter code, not the
|
|
75
|
+
// Codex payload contract. There is no bridge because none is needed:
|
|
76
|
+
// `pre-tool-use.command.input`, embedded verbatim in the binary, lists
|
|
77
|
+
// `tool_name` and `tool_input` among its REQUIRED fields (with `cwd`,
|
|
78
|
+
// `hook_event_name`, `model`, `permission_mode`, `session_id`,
|
|
79
|
+
// `tool_use_id`, `transcript_path`, `turn_id`). The output envelope is
|
|
80
|
+
// `hookSpecificOutput.{hookEventName,permissionDecision,
|
|
81
|
+
// permissionDecisionReason}` — byte-identical to what `emitDeny()`
|
|
82
|
+
// writes, and Codex's own error strings show it enforces exactly that
|
|
83
|
+
// shape ("PreToolUse hook returned permissionDecision:deny without a
|
|
84
|
+
// non-empty permissionDecisionReason").
|
|
85
|
+
//
|
|
86
|
+
// THE ACTUAL BLOCKER is the tool-name VOCABULARY, not the payload shape.
|
|
87
|
+
// Codex ships no `Bash`, `Edit`, `Write` or `MultiEdit` tool — it has
|
|
88
|
+
// `shell` / `exec_command` / `unified_exec` / `apply_patch`
|
|
89
|
+
// (`strings -a <codex> | grep -c '"Bash"'` -> 0). Every handler below
|
|
90
|
+
// opens with an equality gate on a Claude tool name and returns
|
|
91
|
+
// `emitAllow()` otherwise, so wiring one produces a hook that runs,
|
|
92
|
+
// matches nothing and allows everything: FALSE enforcement, which is
|
|
93
|
+
// strictly worse than the registered gap (#919-P2 class). The repair is
|
|
94
|
+
// a tool-name map (see CODEX_TOOL_NAMES in scripts/lib/codex/
|
|
95
|
+
// plugin-contract.mjs), not a manifest entry.
|
|
74
96
|
PreToolUse: [
|
|
75
97
|
// pre-task-scope-disjoint (#1020): NOT ported by construction. The hook
|
|
76
98
|
// matches the `Agent` dispatch tool, which this platform does not have —
|
|
@@ -94,25 +116,32 @@ const DOCUMENTED_ASYMMETRIES = {
|
|
|
94
116
|
'pre-bash-destructive-guard.mjs',
|
|
95
117
|
'pre-bash-staging-fence.mjs',
|
|
96
118
|
'pre-bash-memory-propose-audit.mjs',
|
|
97
|
-
// pre-bash-sessions-ledger-guard (#958): same
|
|
98
|
-
// as the guards above — it
|
|
99
|
-
//
|
|
100
|
-
//
|
|
101
|
-
//
|
|
102
|
-
// enforcement — #919-P2 class. Gap registered, not faked.
|
|
119
|
+
// pre-bash-sessions-ledger-guard (#958): same tool-name-vocabulary
|
|
120
|
+
// dependency as the guards above — it gates on tool_name === 'Bash',
|
|
121
|
+
// and Codex names its shell tools `shell` / `exec_command` /
|
|
122
|
+
// `unified_exec`. The fields it reads ARE delivered; the value it
|
|
123
|
+
// compares against never occurs. A manifest entry would be a silent
|
|
124
|
+
// no-op, not enforcement — #919-P2 class. Gap registered, not faked.
|
|
103
125
|
'pre-bash-sessions-ledger-guard.mjs',
|
|
104
126
|
'pre-bash-templates-first.mjs',
|
|
105
127
|
'pre-bash-issue-budget.mjs',
|
|
106
128
|
'enforce-commands.mjs',
|
|
107
129
|
],
|
|
108
|
-
// post-bash-write-verify (#942):
|
|
109
|
-
// Codex
|
|
110
|
-
//
|
|
111
|
-
//
|
|
112
|
-
//
|
|
130
|
+
// post-bash-write-verify (#942): gates on tool_name === 'Bash', a value
|
|
131
|
+
// Codex never emits (its shell tools are `shell` / `exec_command` /
|
|
132
|
+
// `unified_exec`). Documented exception until a tool-name map exists —
|
|
133
|
+
// a manifest entry today would be a silent no-op, not enforcement. Same
|
|
134
|
+
// #919-P2 class as the PreToolUse block. `post-tool-use.command.input`
|
|
135
|
+
// at 0.144.4 does require `tool_name`, `tool_input` AND `tool_response`,
|
|
136
|
+
// so the port is a rename map, not a payload bridge.
|
|
113
137
|
PostToolUse: [
|
|
114
138
|
'post-edit-validate.mjs',
|
|
115
139
|
'post-tooluse-frontend-slop.mjs',
|
|
140
|
+
// post-edit-import-probe (#1224): same missing Edit/Write payload
|
|
141
|
+
// adapter as post-edit-validate — the hook reads tool_name and
|
|
142
|
+
// tool_input.file_path, neither of which any Codex bridge delivers.
|
|
143
|
+
// A manifest entry would be a silent no-op, not enforcement.
|
|
144
|
+
'post-edit-import-probe.mjs',
|
|
116
145
|
'post-bash-write-verify.mjs',
|
|
117
146
|
],
|
|
118
147
|
SubagentStart: ['subagent-telemetry.mjs'],
|