session-orchestrator 5.2.0 → 5.4.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/.agents/skills/architecture/SKILL.md +3 -1
- package/.agents/skills/autopilot/SKILL.md +5 -1
- package/.agents/skills/autopilot/agents/openai.yaml +5 -0
- package/.agents/skills/bootstrap/SKILL.md +5 -1
- package/.agents/skills/bootstrap/agents/openai.yaml +5 -0
- package/.agents/skills/brainstorm/SKILL.md +5 -1
- package/.agents/skills/brainstorm/agents/openai.yaml +5 -0
- package/.agents/skills/claude-md-drift-check/SKILL.md +4 -2
- package/.agents/skills/close/SKILL.md +5 -1
- package/.agents/skills/close/agents/openai.yaml +5 -0
- package/.agents/skills/convergence-monitoring/SKILL.md +4 -2
- package/.agents/skills/debug/SKILL.md +5 -1
- package/.agents/skills/debug/agents/openai.yaml +5 -0
- package/.agents/skills/discovery/SKILL.md +5 -1
- package/.agents/skills/discovery/agents/openai.yaml +5 -0
- package/.agents/skills/dispatcher/SKILL.md +5 -1
- package/.agents/skills/dispatcher/agents/openai.yaml +5 -0
- package/.agents/skills/docs-orchestrator/SKILL.md +3 -1
- package/.agents/skills/ecosystem-health/SKILL.md +3 -1
- package/.agents/skills/eli5/SKILL.md +5 -1
- package/.agents/skills/eli5/agents/openai.yaml +5 -0
- package/.agents/skills/eval/SKILL.md +6 -2
- package/.agents/skills/eval/agents/openai.yaml +5 -0
- package/.agents/skills/evolve/SKILL.md +6 -2
- package/.agents/skills/evolve/agents/openai.yaml +5 -0
- package/.agents/skills/frontmatter-guard/SKILL.md +3 -1
- package/.agents/skills/gitlab-ops/SKILL.md +3 -1
- package/.agents/skills/gitlab-portfolio/SKILL.md +3 -1
- package/.agents/skills/go/SKILL.md +5 -1
- package/.agents/skills/go/agents/openai.yaml +5 -0
- package/.agents/skills/grill/SKILL.md +5 -1
- package/.agents/skills/grill/agents/openai.yaml +5 -0
- package/.agents/skills/harness-audit/SKILL.md +5 -1
- package/.agents/skills/harness-audit/agents/openai.yaml +5 -0
- package/.agents/skills/hook-development/SKILL.md +3 -1
- package/.agents/skills/mcp-builder/SKILL.md +3 -1
- package/.agents/skills/memory-cleanup/SKILL.md +5 -1
- package/.agents/skills/memory-cleanup/agents/openai.yaml +5 -0
- package/.agents/skills/mode-selector/SKILL.md +3 -1
- package/.agents/skills/navigator/SKILL.md +22 -0
- package/.agents/skills/navigator/agents/openai.yaml +5 -0
- package/.agents/skills/npm-publish/SKILL.md +4 -2
- package/.agents/skills/peekaboo-driver/SKILL.md +3 -1
- package/.agents/skills/persona-panel/SKILL.md +5 -1
- package/.agents/skills/persona-panel/agents/openai.yaml +5 -0
- package/.agents/skills/plan/SKILL.md +5 -1
- package/.agents/skills/plan/agents/openai.yaml +5 -0
- package/.agents/skills/playwright-driver/SKILL.md +3 -1
- package/.agents/skills/portfolio/SKILL.md +5 -1
- package/.agents/skills/portfolio/agents/openai.yaml +5 -0
- package/.agents/skills/quality-gates/SKILL.md +3 -1
- package/.agents/skills/reconcile/SKILL.md +5 -1
- package/.agents/skills/reconcile/agents/openai.yaml +5 -0
- package/.agents/skills/release/SKILL.md +5 -1
- package/.agents/skills/release/agents/openai.yaml +5 -0
- package/.agents/skills/remote-offload/SKILL.md +3 -1
- package/.agents/skills/repo-audit/SKILL.md +5 -1
- package/.agents/skills/repo-audit/agents/openai.yaml +5 -0
- package/.agents/skills/session/SKILL.md +21 -0
- package/.agents/skills/session/agents/openai.yaml +5 -0
- package/.agents/skills/session-end/SKILL.md +3 -1
- package/.agents/skills/session-plan/SKILL.md +3 -1
- package/.agents/skills/session-start/SKILL.md +3 -1
- package/.agents/skills/spinout/SKILL.md +5 -1
- package/.agents/skills/spinout/agents/openai.yaml +5 -0
- package/.agents/skills/sunset-review/SKILL.md +5 -1
- package/.agents/skills/sunset-review/agents/openai.yaml +5 -0
- package/.agents/skills/templates-ack/SKILL.md +21 -0
- package/.agents/skills/templates-ack/agents/openai.yaml +5 -0
- package/.agents/skills/test/SKILL.md +5 -1
- package/.agents/skills/test/agents/openai.yaml +5 -0
- package/.agents/skills/test-audit/SKILL.md +21 -0
- package/.agents/skills/test-audit/agents/openai.yaml +5 -0
- package/.agents/skills/test-runner/SKILL.md +3 -1
- package/.agents/skills/tmux-layout/SKILL.md +3 -1
- package/.agents/skills/using-orchestrator/SKILL.md +3 -1
- package/.agents/skills/ux-grill/SKILL.md +5 -1
- package/.agents/skills/ux-grill/agents/openai.yaml +5 -0
- package/.agents/skills/vault-mirror/SKILL.md +3 -1
- package/.agents/skills/vault-sync/SKILL.md +3 -1
- package/.agents/skills/wave-executor/SKILL.md +3 -1
- package/.agents/skills/write-executable-plan/SKILL.md +3 -1
- package/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/.codex-plugin/plugin.json +4 -4
- package/.codex-plugin/skills/claude-md-drift-check/SKILL.md +1 -1
- package/.codex-plugin/skills/convergence-monitoring/SKILL.md +1 -3
- package/.codex-plugin/skills/eval/SKILL.md +1 -1
- package/.codex-plugin/skills/evolve/SKILL.md +1 -1
- package/.codex-plugin/skills/navigator/SKILL.md +22 -0
- package/.codex-plugin/skills/{convergence-monitoring → navigator}/agents/openai.yaml +2 -2
- package/.codex-plugin/skills/npm-publish/SKILL.md +1 -3
- package/.codex-plugin/skills/session/SKILL.md +1 -1
- package/.codex-plugin/skills/test-audit/SKILL.md +21 -0
- package/.codex-plugin/skills/{npm-publish → test-audit}/agents/openai.yaml +1 -1
- package/.cursor/commands/eval.md +1 -1
- package/.cursor/commands/navigator.md +15 -0
- package/.cursor/commands/session.md +1 -1
- package/.cursor/commands/test-audit.md +14 -0
- package/.cursor/rules/000-session-orchestrator.mdc +0 -2
- package/.cursor/rules/050-plan.mdc +1 -1
- package/.cursor/rules/060-evolve.mdc +27 -12
- package/.cursor/skills/claude-md-drift-check/SKILL.md +1 -1
- package/.cursor/skills/convergence-monitoring/SKILL.md +1 -0
- package/.cursor/skills/eval/SKILL.md +1 -1
- package/.cursor/skills/navigator/SKILL.md +13 -0
- package/.cursor/skills/npm-publish/SKILL.md +1 -0
- package/.cursor/skills/test-audit/SKILL.md +12 -0
- package/.cursor-plugin/plugin.json +1 -1
- package/.orchestrator/policy/blocked-commands.json +12 -3
- package/AGENTS.md +9 -30
- package/CHANGELOG.md +206 -0
- package/README.md +13 -9
- package/SECURITY.md +12 -0
- package/agents/dialectic-deriver.md +13 -10
- package/agents/eval-judge.md +67 -45
- package/agents/skill-applied-judge.md +34 -19
- package/commands/session.md +7 -3
- package/docs/agent-authoring.md +2 -2
- package/docs/baseline.md +22 -7
- package/docs/ci-setup.md +57 -4
- package/docs/codex-setup.md +14 -2
- package/docs/components.md +12 -10
- package/docs/events-schema.md +65 -15
- package/docs/persona-panel.md +5 -4
- package/docs/rule-authoring.md +84 -17
- package/docs/scope-collision-guard.md +26 -4
- package/docs/session-config-reference.md +154 -12
- package/docs/session-config-template.md +41 -3
- package/docs/telemetry.md +2 -0
- package/hooks/_lib/hook-import-set.json +87 -6
- package/hooks/_lib/subagent-transcript.mjs +582 -31
- package/hooks/config-protection.mjs +11 -3
- package/hooks/cwd-change-restore.mjs +11 -3
- package/hooks/enforce-commands.mjs +70 -23
- package/hooks/enforce-scope.mjs +143 -33
- package/hooks/hooks-codex.json +1 -1
- package/hooks/hooks-cursor.json +5 -0
- package/hooks/hooks-pi.json +5 -0
- package/hooks/hooks.json +6 -1
- package/hooks/loop-guard.mjs +11 -3
- package/hooks/on-session-end.mjs +58 -23
- package/hooks/on-session-start.mjs +101 -11
- package/hooks/on-stop.mjs +170 -22
- package/hooks/operator-steer.mjs +11 -3
- package/hooks/post-bash-issue-budget-refund.mjs +18 -8
- package/hooks/post-bash-write-verify.mjs +3 -2
- package/hooks/post-edit-import-probe.mjs +17 -9
- package/hooks/post-edit-validate.mjs +13 -5
- package/hooks/post-subagent-discovery-validator.mjs +98 -13
- package/hooks/post-tool-batch-wave-signal.mjs +202 -38
- package/hooks/post-tool-failure-corrective-context.mjs +11 -5
- package/hooks/post-tooluse-frontend-slop.mjs +10 -4
- package/hooks/pre-auq-clarity.mjs +15 -2
- package/hooks/pre-bash-destructive-guard.mjs +80 -9
- package/hooks/pre-bash-issue-budget.mjs +16 -11
- package/hooks/pre-bash-memory-propose-audit.mjs +137 -56
- package/hooks/pre-bash-sessions-ledger-guard.mjs +419 -22
- package/hooks/pre-bash-staging-fence.mjs +335 -31
- package/hooks/pre-bash-templates-first.mjs +19 -14
- package/hooks/pre-edit-sessions-ledger-guard.mjs +162 -0
- package/hooks/pre-task-scope-disjoint.mjs +792 -74
- package/hooks/subagent-telemetry.mjs +111 -38
- package/hooks/wave-scope-commit-guard.mjs +197 -100
- package/monitors/monitors.json +1 -1
- package/output-styles/wave-summary.md +1 -1
- package/package.json +3 -3
- package/pi/prompts/eval.md +1 -1
- package/pi/prompts/navigator.md +12 -0
- package/pi/prompts/session.md +1 -1
- package/pi/prompts/test-audit.md +12 -0
- package/rules/README.md +7 -2
- package/rules/_index.md +3 -3
- package/rules/always-on/bash-harness-pitfalls.md +7 -1
- package/rules/opt-in-domain/prompt-caching.md +6 -2
- package/rules/opt-in-stack/backend-data.md +10 -2
- package/rules/opt-in-stack/backend.md +10 -4
- package/rules/opt-in-stack/frontend.md +7 -2
- package/rules/opt-in-stack/security-web.md +10 -4
- package/rules/opt-in-stack/swift.md +6 -2
- package/scripts/apply-session-learnings.mjs +492 -0
- package/scripts/archive-closed-prds.mjs +2 -1
- package/scripts/autopilot.mjs +70 -53
- package/scripts/backfill-abandoned-sessions.mjs +118 -16
- package/scripts/baseline-archetypes.mjs +1 -1
- package/scripts/check-package-manager.mjs +62 -5
- package/scripts/check-sessions-integrity.mjs +300 -0
- package/scripts/cursor-install.mjs +1 -4
- package/scripts/dialectic-deriver.mjs +50 -13
- package/scripts/emit-session.mjs +371 -31
- package/scripts/eval-session.mjs +65 -3
- package/scripts/export-hw-learnings.mjs +22 -3
- package/scripts/fleet-checkin.mjs +147 -0
- package/scripts/generate-agents-skills.mjs +102 -29
- package/scripts/generate-cursor-adapter.mjs +61 -16
- package/scripts/lib/agent-frontmatter.mjs +3 -3
- package/scripts/lib/agent-status.mjs +2 -31
- package/scripts/lib/auq/clarity.mjs +10 -2
- package/scripts/lib/auq/parse.mjs +12 -31
- package/scripts/lib/auq/schema.mjs +56 -41
- package/scripts/lib/auto-dialectic.mjs +304 -15
- package/scripts/lib/auto-dream.mjs +7 -6
- package/scripts/lib/autopilot/flags.mjs +18 -2
- package/scripts/lib/autopilot/kill-switches.mjs +6 -3
- package/scripts/lib/autopilot/loop.mjs +19 -2
- package/scripts/lib/autopilot/stall-sampler.mjs +80 -23
- package/scripts/lib/baseline-archetypes.mjs +41 -22
- package/scripts/lib/ci-status-banner.mjs +376 -16
- package/scripts/lib/command-blocker.mjs +275 -28
- package/scripts/lib/config/dialectic.mjs +12 -3
- package/scripts/lib/config/gate.mjs +74 -0
- package/scripts/lib/config/host-paths.mjs +79 -0
- package/scripts/lib/config/persona-gate-wave.mjs +3 -3
- package/scripts/lib/config/reaper.mjs +163 -0
- package/scripts/lib/config-schema.mjs +13 -0
- package/scripts/lib/config.mjs +85 -10
- package/scripts/lib/convergence-monitor.mjs +74 -11
- package/scripts/lib/ecosystem-health.mjs +11 -0
- package/scripts/lib/eval/engine.mjs +421 -53
- package/scripts/lib/eval/judge.mjs +463 -40
- package/scripts/lib/eval/schema.mjs +10 -1
- package/scripts/lib/events-rotation.mjs +221 -25
- package/scripts/lib/events-schema.mjs +114 -0
- package/scripts/lib/events.mjs +545 -7
- package/scripts/lib/fleet-protocol.mjs +270 -0
- package/scripts/lib/frontmatter-guard.mjs +21 -10
- package/scripts/lib/gates/gate-baseline.mjs +40 -5
- package/scripts/lib/gates/gate-full.mjs +35 -5
- package/scripts/lib/gates/gate-helpers.mjs +336 -23
- package/scripts/lib/gates/gate-incremental.mjs +49 -23
- package/scripts/lib/gates/gate-per-file.mjs +42 -8
- package/scripts/lib/gitlab-portfolio/markdown-writer.mjs +6 -1
- package/scripts/lib/instruction-budget-guard.mjs +208 -8
- package/scripts/lib/io.mjs +42 -8
- package/scripts/lib/issue-close-strip-labels.mjs +207 -49
- package/scripts/lib/js-mask.mjs +197 -0
- package/scripts/lib/learnings/evolve-telemetry.mjs +11 -7
- package/scripts/lib/learnings/expiry-sweep.mjs +34 -3
- package/scripts/lib/learnings/io.mjs +154 -26
- package/scripts/lib/learnings/schema.mjs +163 -15
- package/scripts/lib/learnings/sizing-subject.mjs +2 -2
- package/scripts/lib/maintenance-due-banner.mjs +53 -88
- package/scripts/lib/memory-proposals/schema.mjs +11 -5
- package/scripts/lib/named-baseline-resolver.mjs +53 -28
- package/scripts/lib/named-vault-resolver.mjs +104 -5
- package/scripts/lib/orphan-reaper/defaults.mjs +41 -0
- package/scripts/lib/orphan-reaper/ps-snapshot.mjs +181 -0
- package/scripts/lib/orphan-reaper/reaper-audit.mjs +291 -0
- package/scripts/lib/orphan-reaper/reaper-decide.mjs +395 -0
- package/scripts/lib/orphan-reaper/scan-throttle.mjs +92 -0
- package/scripts/lib/orphan-reaper/scan.mjs +506 -0
- package/scripts/lib/orphan-reaper.mjs +316 -0
- package/scripts/lib/owner-yaml.mjs +104 -6
- package/scripts/lib/peer-cards/merger.mjs +48 -10
- package/scripts/lib/peer-cards/reader.mjs +78 -2
- package/scripts/lib/persona-panel/catalog-loader.mjs +1 -1
- package/scripts/lib/process-group.mjs +950 -0
- package/scripts/lib/project-hygiene.mjs +21 -1
- package/scripts/lib/quality-gate.mjs +152 -33
- package/scripts/lib/reconcile/backlog.mjs +368 -0
- package/scripts/lib/reconcile/engine.mjs +55 -188
- package/scripts/lib/reconcile/rule-expiry-sweep.mjs +302 -60
- package/scripts/lib/reconcile/sanitize.mjs +69 -3
- package/scripts/lib/reconcile-nudge-banner.mjs +138 -45
- package/scripts/lib/resource-probe/parsers.mjs +31 -0
- package/scripts/lib/resource-probe.mjs +1 -1
- package/scripts/lib/rule-loader.mjs +141 -74
- package/scripts/lib/rules-sync.mjs +27 -12
- package/scripts/lib/scope-baseline.mjs +3 -4
- package/scripts/lib/scope-echo.mjs +39 -2
- package/scripts/lib/scope-gate.mjs +605 -1
- package/scripts/lib/session-close-backfill.mjs +33 -6
- package/scripts/lib/session-id.mjs +9 -20
- package/scripts/lib/session-invocation.mjs +20 -0
- package/scripts/lib/session-record-repair.mjs +183 -26
- package/scripts/lib/session-registry.mjs +27 -2
- package/scripts/lib/session-schema/constants.mjs +56 -2
- package/scripts/lib/session-schema/normalizer.mjs +56 -4
- package/scripts/lib/session-schema/validator.mjs +28 -1
- package/scripts/lib/session-schema.mjs +8 -3
- package/scripts/lib/session-start-probes.mjs +95 -10
- package/scripts/lib/session-token-rollup.mjs +245 -52
- package/scripts/lib/sessions-canonical.mjs +118 -4
- package/scripts/lib/sessions-integrity-banner.mjs +7 -1
- package/scripts/lib/sessions-staleness-banner.mjs +193 -51
- package/scripts/lib/sh-quote.mjs +29 -0
- package/scripts/lib/skill-evidence-window.mjs +891 -0
- package/scripts/lib/skill-evolution/candidate-intake.mjs +133 -12
- package/scripts/lib/skill-evolution/engine.mjs +18 -9
- package/scripts/lib/skill-judge.mjs +45 -3
- package/scripts/lib/subagents-schema.mjs +30 -3
- package/scripts/lib/tail-window.mjs +56 -0
- package/scripts/lib/telemetry/pricing.mjs +118 -71
- package/scripts/lib/telemetry/schema.mjs +30 -0
- package/scripts/lib/telemetry/sync.mjs +61 -6
- package/scripts/lib/telemetry-flush-health-banner.mjs +4 -22
- package/scripts/lib/test-runner/issue-reconcile.mjs +48 -16
- package/scripts/lib/tests-src-ratio.mjs +140 -23
- package/scripts/lib/tmux-layout/layouts.mjs +1 -11
- package/scripts/lib/tmux-layout/telemetry-stats.mjs +72 -13
- package/scripts/lib/tmux-layout/tmux-shell.mjs +2 -11
- package/scripts/lib/user-invocable-skills.mjs +23 -3
- package/scripts/lib/ux-grill/reconcile.mjs +48 -22
- package/scripts/lib/validate/check-agents-skills.mjs +26 -15
- package/scripts/lib/validate/check-agents.mjs +1 -1
- package/scripts/lib/validate/check-cursor-adapter.mjs +1 -0
- package/scripts/lib/validate/check-entry-guard.mjs +13 -50
- package/scripts/lib/validate/check-hook-entry-guards.mjs +636 -0
- package/scripts/lib/validate/check-hooks-symmetry.mjs +5 -0
- package/scripts/lib/validate/check-learnings-shell-writes.mjs +214 -0
- package/scripts/lib/validate/check-pi-prompts.mjs +1 -0
- package/scripts/lib/validate/check-rules.mjs +7 -5
- package/scripts/lib/validate/check-skill-links.mjs +9 -1
- package/scripts/lib/validate/check-skill-script-paths.mjs +239 -27
- package/scripts/lib/validate/check-test-git-config-target.mjs +24 -34
- package/scripts/lib/validate/check-test-value-bans.mjs +87 -14
- package/scripts/lib/validate/check-unicode-safety.mjs +1 -0
- package/scripts/lib/validate/check-untracked-test-deps.mjs +7 -102
- package/scripts/lib/validate/check-unwired-features.mjs +130 -27
- package/scripts/lib/validate/check-validator-registration.mjs +34 -10
- package/scripts/lib/validate/confidential-names.mjs +10 -0
- package/scripts/lib/validate-vendored-rules.mjs +210 -84
- package/scripts/lib/vault-mirror/namespace.mjs +46 -8
- package/scripts/lib/vault-mirror/process.mjs +10 -3
- package/scripts/lib/vault-mirror/render-sessions.mjs +12 -2
- package/scripts/lib/vault-mirror/telemetry.mjs +8 -1
- package/scripts/lib/vault-status/board-writer.mjs +48 -11
- package/scripts/lib/vault-status/narrative-mirror.mjs +71 -16
- package/scripts/lib/vault-yaml.mjs +118 -0
- package/scripts/lib/wave-executor/dispatch-common.mjs +10 -18
- package/scripts/lib/wave-executor/foreign-dispatch.mjs +1 -1
- package/scripts/lib/wave-executor/remote-dispatch.mjs +1 -1
- package/scripts/lib/wave-resource-gate.mjs +34 -8
- package/scripts/lib/worktree/lifecycle.mjs +153 -1
- package/scripts/memory-propose.mjs +18 -5
- package/scripts/release-session-lock.mjs +306 -0
- package/scripts/release.mjs +40 -8
- package/scripts/repair-invalid-sessions.mjs +37 -4
- package/scripts/resolve-session-invocation.mjs +59 -0
- package/scripts/run-migrate-v2-cross-repo.mjs +158 -30
- package/scripts/run-quality-gate.mjs +357 -197
- package/scripts/site-numbers.mjs +163 -5
- package/scripts/sweep-expired-learnings.mjs +56 -5
- package/scripts/sweep-expired-rules.mjs +16 -4
- package/scripts/validate-plugin.mjs +19 -0
- package/scripts/validate-wave-scope.mjs +32 -105
- package/scripts/vault-integration-watcher.mjs +2 -1
- package/scripts/vault-mirror.mjs +81 -47
- package/scripts/wave-scope-binding.mjs +2 -1
- package/skills/_shared/config-reading.md +3 -3
- package/skills/_shared/fleet-protocol.md +131 -0
- package/skills/_shared/platform-tools.md +23 -11
- package/skills/_shared/state-ownership.md +5 -0
- package/skills/autopilot/SKILL.md +47 -18
- package/skills/bootstrap/SKILL.md +1 -1
- package/skills/bootstrap/_shared-template.md +1 -1
- package/skills/bootstrap/fast-template.md +1 -1
- package/skills/bootstrap/private-contract.md +2 -1
- package/skills/bootstrap/public-fallback.md +6 -3
- package/skills/bootstrap/references/bootstrap-retroactive-flow.md +2 -1
- package/skills/claude-md-drift-check/SKILL.md +10 -10
- package/skills/claude-md-drift-check/checker.mjs +26 -18
- package/skills/convergence-monitoring/README.md +8 -1
- package/skills/convergence-monitoring/SIGNALS.md +50 -6
- package/skills/convergence-monitoring/SKILL.md +15 -6
- package/skills/eval/SKILL.md +39 -24
- package/skills/eval/rubric-v1.md +1 -0
- package/skills/eval/rubric-v2.md +457 -0
- package/skills/evolve/SKILL.md +3 -3
- package/skills/evolve/references/evolve-analyze-mode.md +10 -6
- package/skills/evolve/references/evolve-dialectic-mode.md +42 -25
- package/skills/gitlab-ops/SKILL.md +3 -2
- package/skills/memory-cleanup/SKILL.md +1 -1
- package/skills/navigator/SKILL.md +193 -0
- package/skills/navigator/references/account-check.sh +87 -0
- package/skills/navigator/references/fleet-decisions.mjs +231 -0
- package/skills/navigator/references/slot-check.sh +114 -0
- package/skills/npm-publish/SKILL.md +1 -1
- package/skills/persona-panel/SKILL.md +2 -2
- package/skills/persona-panel/persona-format.md +2 -2
- package/skills/persona-panel/presets/designer-lens.md +1 -1
- package/skills/persona-panel/presets/engineer-lens.md +1 -1
- package/skills/persona-panel/presets/pm-lens.md +1 -1
- package/skills/plan/mode-retro.md +19 -4
- package/skills/quality-gates/SKILL.md +6 -4
- package/skills/reconcile/SKILL.md +11 -0
- package/skills/remote-offload/SKILL.md +4 -2
- package/skills/session-end/SKILL.md +13 -16
- package/skills/session-end/discovery-scan.md +1 -1
- package/skills/session-end/learning-patterns.md +22 -12
- package/skills/session-end/metrics-collection.md +5 -6
- package/skills/session-end/phase-3-6-tail.md +55 -9
- package/skills/session-end/references/phase-3-documentation-updates.md +2 -2
- package/skills/session-end/references/phase-4a-worktree-cleanup.md +2 -0
- package/skills/session-end/references/phase-5-issue-cleanup.md +9 -14
- package/skills/session-end/references/session-summary-template.md +1 -1
- package/skills/session-end/session-metrics-write.md +32 -60
- package/skills/session-plan/SKILL.md +17 -8
- package/skills/session-plan/references/session-plan-task-classification.md +2 -2
- package/skills/session-start/SKILL.md +6 -0
- package/skills/session-start/references/operations-contract.md +4 -0
- package/skills/session-start/references/phase-1-7-vault-status-board.md +1 -1
- package/skills/session-start/references/phase-4-ssot-environment-check.md +4 -3
- package/skills/test-audit/SKILL.md +152 -0
- package/skills/test-audit/references/campaign.md +111 -0
- package/skills/test-audit/references/mutate.sh +50 -0
- package/skills/ux-grill/SKILL.md +1 -1
- package/skills/wave-executor/SKILL.md +9 -5
- package/skills/wave-executor/circuit-breaker.md +2 -0
- package/skills/wave-executor/references/wave-executor-state-init.md +8 -3
- package/skills/wave-executor/references/wave-loop-dispatch.md +5 -2
- package/skills/wave-executor/references/wave-loop-review.md +3 -5
- package/templates/personas/README.md +5 -5
- package/templates/personas/accounting-compliance.v1.md +1 -1
- package/templates/personas/accounting-tax-advisor.v1.md +1 -1
- package/templates/personas/buyer-p1-cto.v1.md +1 -1
- package/templates/personas/buyer-p2-kanzlei.v1.md +1 -1
- package/templates/personas/buyer-p3-build.v1.md +1 -1
- package/templates/personas/buyer-p4-tech-veto.v1.md +1 -1
- package/templates/personas/buyer-p5-solo.v1.md +1 -1
- package/templates/personas/buyer-p6-ld.v1.md +1 -1
- package/templates/personas/klima-ai-expert.v1.md +1 -1
- package/templates/personas/klima-physicist.v1.md +1 -1
- package/.cursor/commands/convergence-monitoring.md +0 -13
- package/.cursor/commands/npm-publish.md +0 -13
- package/pi/prompts/convergence-monitoring.md +0 -11
- package/pi/prompts/npm-publish.md +0 -11
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* fleet-checkin.mjs — write this session's fleet check-in (#1462, PRD !53 § 3.2).
|
|
4
|
+
*
|
|
5
|
+
* Thin CLI over `scripts/lib/fleet-protocol.mjs`, called by session-start
|
|
6
|
+
* Phase 7.6 and the operations-contract peer preflight BEFORE the first writing
|
|
7
|
+
* action of a session.
|
|
8
|
+
*
|
|
9
|
+
* Usage:
|
|
10
|
+
* echo '<check-in JSON>' | node scripts/fleet-checkin.mjs
|
|
11
|
+
*
|
|
12
|
+
* Reads one JSON object from stdin, stamps `zeit` (always overwritten), validates
|
|
13
|
+
* it with `validateCheckin()`, and writes it atomically (tmp file + rename in
|
|
14
|
+
* the target directory, mode 0600) to `<navigatorDir>/checkin/<session>.json`.
|
|
15
|
+
* Then reads the navigator lease and emits `orchestrator.fleet.checkin` with the
|
|
16
|
+
* payload `{ session, repo, modus, kandidaten, navigator_state }` — no paths, no
|
|
17
|
+
* quota, no `auftrag_ref`.
|
|
18
|
+
*
|
|
19
|
+
* stdout: one JSON line `{ ok, path, navigator_state, event }`.
|
|
20
|
+
*
|
|
21
|
+
* Exit codes:
|
|
22
|
+
* 0 — check-in written (an event failure only WARNs; `event: false` on stdout)
|
|
23
|
+
* 1 — write error
|
|
24
|
+
* 2 — invalid input (not JSON, not an object, or validation errors; no file)
|
|
25
|
+
*/
|
|
26
|
+
|
|
27
|
+
import { randomBytes } from 'node:crypto';
|
|
28
|
+
import { promises as fs } from 'node:fs';
|
|
29
|
+
import os from 'node:os';
|
|
30
|
+
import path from 'node:path';
|
|
31
|
+
import { emitEvent } from './lib/events.mjs';
|
|
32
|
+
import {
|
|
33
|
+
checkinPath,
|
|
34
|
+
navigatorDir,
|
|
35
|
+
readNavigatorLease,
|
|
36
|
+
utcSecondsTimestamp,
|
|
37
|
+
validateCheckin,
|
|
38
|
+
} from './lib/fleet-protocol.mjs';
|
|
39
|
+
import { isMainModule } from './lib/is-main-module.mjs';
|
|
40
|
+
|
|
41
|
+
const PREFIX = 'fleet-checkin:';
|
|
42
|
+
|
|
43
|
+
/** @returns {Promise<string>} */
|
|
44
|
+
async function readStdin() {
|
|
45
|
+
let data = '';
|
|
46
|
+
process.stdin.setEncoding('utf8');
|
|
47
|
+
for await (const chunk of process.stdin) data += chunk;
|
|
48
|
+
return data;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Create `<navigatorDir>/checkin/` (0700) and write `record` atomically to
|
|
53
|
+
* `target` (0600). Refuses a `checkin/` that is not a real directory, so a
|
|
54
|
+
* planted symlink cannot redirect the chmod or the write.
|
|
55
|
+
* @param {string} target
|
|
56
|
+
* @param {object} record
|
|
57
|
+
*/
|
|
58
|
+
async function writeCheckinFile(target, record) {
|
|
59
|
+
const checkinDir = path.dirname(target);
|
|
60
|
+
await fs.mkdir(navigatorDir(), { recursive: true, mode: 0o700 });
|
|
61
|
+
await fs.mkdir(checkinDir, { recursive: true, mode: 0o700 });
|
|
62
|
+
const st = await fs.lstat(checkinDir);
|
|
63
|
+
if (!st.isDirectory()) throw new Error(`${checkinDir} is not a directory`);
|
|
64
|
+
// mkdir's mode does not apply to a directory that already existed.
|
|
65
|
+
await fs.chmod(checkinDir, 0o700);
|
|
66
|
+
|
|
67
|
+
const tmp = path.join(checkinDir, `.${path.basename(target)}.${randomBytes(6).toString('hex')}.tmp`);
|
|
68
|
+
try {
|
|
69
|
+
// `wx`: never follow or reuse a pre-existing path at the tmp name.
|
|
70
|
+
await fs.writeFile(tmp, JSON.stringify(record, null, 2) + '\n', { mode: 0o600, flag: 'wx' });
|
|
71
|
+
// The creation mode is filtered through the umask; pin it explicitly.
|
|
72
|
+
await fs.chmod(tmp, 0o600);
|
|
73
|
+
await fs.rename(tmp, target);
|
|
74
|
+
} catch (err) {
|
|
75
|
+
await fs.rm(tmp, { force: true }).catch(() => {});
|
|
76
|
+
throw err;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/** @returns {Promise<number>} exit code */
|
|
81
|
+
async function main() {
|
|
82
|
+
let input;
|
|
83
|
+
try {
|
|
84
|
+
input = JSON.parse(await readStdin());
|
|
85
|
+
} catch {
|
|
86
|
+
process.stderr.write(`${PREFIX} stdin is not valid JSON\n`);
|
|
87
|
+
return 2;
|
|
88
|
+
}
|
|
89
|
+
if (input === null || typeof input !== 'object' || Array.isArray(input)) {
|
|
90
|
+
process.stderr.write(`${PREFIX} stdin must be a JSON object\n`);
|
|
91
|
+
return 2;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const record = { ...input, zeit: utcSecondsTimestamp() };
|
|
95
|
+
const errors = validateCheckin(record);
|
|
96
|
+
if (errors.length > 0) {
|
|
97
|
+
for (const e of errors) process.stderr.write(`${PREFIX} ${e}\n`);
|
|
98
|
+
return 2;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const target = checkinPath(record.session);
|
|
102
|
+
try {
|
|
103
|
+
await writeCheckinFile(target, record);
|
|
104
|
+
} catch (err) {
|
|
105
|
+
process.stderr.write(`${PREFIX} write failed: ${err instanceof Error ? err.message : String(err)}\n`);
|
|
106
|
+
return 1;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const { state: navigatorState } = await readNavigatorLease();
|
|
110
|
+
|
|
111
|
+
let event = true;
|
|
112
|
+
try {
|
|
113
|
+
const repoRoot = (process.env.CLAUDE_PROJECT_DIR || '').trim() || process.cwd();
|
|
114
|
+
await emitEvent(
|
|
115
|
+
'orchestrator.fleet.checkin',
|
|
116
|
+
{
|
|
117
|
+
session: record.session,
|
|
118
|
+
repo: record.repo,
|
|
119
|
+
modus: record.modus,
|
|
120
|
+
kandidaten: record.kandidaten,
|
|
121
|
+
navigator_state: navigatorState,
|
|
122
|
+
},
|
|
123
|
+
{ repoRoot },
|
|
124
|
+
);
|
|
125
|
+
} catch (err) {
|
|
126
|
+
event = false;
|
|
127
|
+
process.stderr.write(
|
|
128
|
+
`${PREFIX} WARN: check-in written but event emission failed: ${err instanceof Error ? err.message : String(err)}\n`,
|
|
129
|
+
);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// Report the path with `~` for the home prefix: stdout lands in transcripts.
|
|
133
|
+
const home = os.homedir();
|
|
134
|
+
const shownPath = target.startsWith(`${home}${path.sep}`) ? `~${target.slice(home.length)}` : target;
|
|
135
|
+
process.stdout.write(JSON.stringify({ ok: true, path: shownPath, navigator_state: navigatorState, event }) + '\n');
|
|
136
|
+
return 0;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
if (isMainModule(import.meta.url)) {
|
|
140
|
+
main().then(
|
|
141
|
+
(code) => { process.exitCode = code; },
|
|
142
|
+
(err) => {
|
|
143
|
+
process.stderr.write(`${PREFIX} ${err instanceof Error ? err.message : String(err)}\n`);
|
|
144
|
+
process.exitCode = 1;
|
|
145
|
+
},
|
|
146
|
+
);
|
|
147
|
+
}
|
|
@@ -12,10 +12,11 @@
|
|
|
12
12
|
* This generator writes two artefacts, both DERIVED and never hand-edited:
|
|
13
13
|
*
|
|
14
14
|
* 1. `AGENTS.md` — a BYTE-IDENTICAL copy of `CLAUDE.md`.
|
|
15
|
-
* 2. `.agents/skills/<name>/SKILL.md` — a portable mirror of each
|
|
16
|
-
*
|
|
15
|
+
* 2. `.agents/skills/<name>/SKILL.md` — a portable mirror of each skill and
|
|
16
|
+
* command (commands own overlapping names), carrying ONLY spec-legal
|
|
17
17
|
* frontmatter plus a pointer body (progressive disclosure — the mirror
|
|
18
|
-
* never duplicates the canonical instructions).
|
|
18
|
+
* never duplicates the canonical instructions). Native Codex invocation
|
|
19
|
+
* policy travels in an adjacent `agents/openai.yaml` when applicable.
|
|
19
20
|
*
|
|
20
21
|
* ## Why a generated copy and NOT a symlink (measured, not preferred)
|
|
21
22
|
*
|
|
@@ -53,6 +54,9 @@ import path from 'node:path';
|
|
|
53
54
|
import { fileURLToPath } from 'node:url';
|
|
54
55
|
import { execFileSync } from 'node:child_process';
|
|
55
56
|
import yaml from 'js-yaml';
|
|
57
|
+
import { isUserInvocableValue } from './lib/user-invocable-skills.mjs';
|
|
58
|
+
|
|
59
|
+
const POLICY_MARKER = '# Generated by scripts/generate-agents-skills.mjs; do not edit.\n';
|
|
56
60
|
|
|
57
61
|
/**
|
|
58
62
|
* The complete set of SKILL.md frontmatter keys agentskills.io permits outside
|
|
@@ -159,29 +163,40 @@ export function toPortableFrontmatter(source, skillName) {
|
|
|
159
163
|
*
|
|
160
164
|
* @param {string} skillName
|
|
161
165
|
* @param {Record<string, unknown>} portableFm
|
|
166
|
+
* @param {string} [canonical] plugin-relative source path; defaults to the skill
|
|
162
167
|
* @returns {string}
|
|
163
168
|
*/
|
|
164
|
-
export function renderMirror(skillName, portableFm) {
|
|
169
|
+
export function renderMirror(skillName, portableFm, canonical = `skills/${skillName}/SKILL.md`) {
|
|
165
170
|
const front = yaml.dump(portableFm, { lineWidth: -1, noRefs: true, quotingType: '"' });
|
|
166
|
-
const
|
|
167
|
-
|
|
171
|
+
const command = canonical.startsWith('commands/');
|
|
172
|
+
const lines = [
|
|
168
173
|
'---',
|
|
169
174
|
front.trimEnd(),
|
|
170
175
|
'---',
|
|
171
176
|
'',
|
|
172
177
|
`# ${skillName}`,
|
|
173
178
|
'',
|
|
174
|
-
`> **Portable mirror — generated, do not edit.** The canonical
|
|
179
|
+
`> **Portable mirror — generated, do not edit.** The canonical workflow lives at`,
|
|
175
180
|
`> [\`${canonical}\`](../../../${canonical}); read that file for the full instructions.`,
|
|
176
181
|
`> This mirror carries only agentskills.io-spec-legal frontmatter so harnesses that`,
|
|
177
182
|
`> discover skills under \`.agents/skills/\` can find and route to the skill.`,
|
|
178
183
|
'>',
|
|
179
184
|
'> Regenerate with `node scripts/generate-agents-skills.mjs`.',
|
|
180
185
|
'',
|
|
181
|
-
|
|
186
|
+
'Read the linked document in full and follow its complete workflow, including prechecks and stop conditions. Resolve its link relative to this SKILL.md, not the project working directory. The plugin root is three directories above this file. Resolve package paths such as `skills/` and `scripts/` from that root and relative links in the canonical document from its own directory. Keep the user’s project as the target of project operations.',
|
|
187
|
+
];
|
|
188
|
+
if (command || isUserInvocableValue(portableFm.metadata?.['user-invocable'])) lines.push(
|
|
189
|
+
'',
|
|
190
|
+
'`$ARGUMENTS` means the trailing user input after the selected command skill, or an empty string when absent. Preserve flags, quoted text, and Unicode as data. Do not perform global substitution in the command document, shell expansion on the argument string, or execution of that string as shell code. Pass command arguments through structured tool parameters or safely quoted individual arguments.',
|
|
191
|
+
);
|
|
192
|
+
if (command) lines.push(
|
|
193
|
+
'',
|
|
194
|
+
'Read the full command before invoking an internal skill. An instruction to invoke a skill (including `session-orchestrator:<name>` or the `Skill` tool) means read and follow the canonical `skills/<name>/SKILL.md` beneath the plugin root. Do not redispatch the public command adapter: a same-named command and internal skill are distinct documents, and redispatch would recurse.',
|
|
195
|
+
);
|
|
196
|
+
return [...lines, ''].join('\n');
|
|
182
197
|
}
|
|
183
198
|
|
|
184
|
-
/** Parse a
|
|
199
|
+
/** Parse a source's YAML frontmatter block; returns null when absent/unparseable. */
|
|
185
200
|
function readFrontmatter(absPath) {
|
|
186
201
|
const content = readFileSync(absPath, 'utf8');
|
|
187
202
|
const m = /^---\r?\n([\s\S]*?)\r?\n---/.exec(content);
|
|
@@ -205,6 +220,44 @@ export function listSourceSkills(pluginRoot) {
|
|
|
205
220
|
.sort();
|
|
206
221
|
}
|
|
207
222
|
|
|
223
|
+
/** Union of canonical skills and commands, preserving command precedence. */
|
|
224
|
+
function listSourceEntries(pluginRoot) {
|
|
225
|
+
const sources = new Map(listSourceSkills(pluginRoot).map((name) => [name, `skills/${name}/SKILL.md`]));
|
|
226
|
+
const commands = path.join(pluginRoot, 'commands');
|
|
227
|
+
if (existsSync(commands) && statSync(commands).isDirectory()) {
|
|
228
|
+
for (const entry of readdirSync(commands, { withFileTypes: true })) {
|
|
229
|
+
if (!entry.isFile() || !entry.name.endsWith('.md') || /^[._]/.test(entry.name)) continue;
|
|
230
|
+
sources.set(entry.name.slice(0, -3), `commands/${entry.name}`);
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
return sources;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/** Match Codex policy precedence: command, native skill override, public skill. */
|
|
237
|
+
function invocationPolicy(pluginRoot, name, canonical, fm) {
|
|
238
|
+
if (!canonical.startsWith('commands/')) {
|
|
239
|
+
const nativePath = path.join(pluginRoot, 'skills', name, 'agents', 'openai.yaml');
|
|
240
|
+
if (existsSync(nativePath)) {
|
|
241
|
+
const native = yaml.load(readFileSync(nativePath, 'utf8'), { schema: yaml.CORE_SCHEMA });
|
|
242
|
+
const isRecord = (value) => value !== null && typeof value === 'object' && !Array.isArray(value);
|
|
243
|
+
if (!isRecord(native) || (Object.hasOwn(native, 'policy') && !isRecord(native.policy))) {
|
|
244
|
+
throw new Error(`${nativePath}: native skill metadata and policy must be mappings`);
|
|
245
|
+
}
|
|
246
|
+
if (native.policy && Object.hasOwn(native.policy, 'allow_implicit_invocation')) {
|
|
247
|
+
if (typeof native.policy.allow_implicit_invocation !== 'boolean') {
|
|
248
|
+
throw new Error(`${nativePath}: policy.allow_implicit_invocation must be a boolean`);
|
|
249
|
+
}
|
|
250
|
+
return native.policy.allow_implicit_invocation;
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
if (!isUserInvocableValue(fm['user-invocable'], canonical)) return undefined;
|
|
254
|
+
}
|
|
255
|
+
if (Object.hasOwn(fm, 'disable-model-invocation') && typeof fm['disable-model-invocation'] !== 'boolean') {
|
|
256
|
+
throw new Error(`${canonical}: disable-model-invocation must be a boolean`);
|
|
257
|
+
}
|
|
258
|
+
return fm['disable-model-invocation'] !== true;
|
|
259
|
+
}
|
|
260
|
+
|
|
208
261
|
/**
|
|
209
262
|
* Is `AGENTS.md` already an alias of `CLAUDE.md` by construction?
|
|
210
263
|
*
|
|
@@ -268,34 +321,53 @@ export function generateAgentsSurface({ pluginRoot, check = false }) {
|
|
|
268
321
|
|
|
269
322
|
// --- Artefact 2: .agents/skills/<name>/SKILL.md ---------------------------
|
|
270
323
|
const mirrorRoot = path.join(pluginRoot, '.agents', 'skills');
|
|
271
|
-
const
|
|
324
|
+
const sources = listSourceEntries(pluginRoot);
|
|
325
|
+
const skills = [...sources.keys()].sort();
|
|
272
326
|
let files = 0;
|
|
273
327
|
let bytes = 0;
|
|
274
328
|
|
|
275
329
|
for (const name of skills) {
|
|
276
|
-
const
|
|
330
|
+
const canonical = sources.get(name);
|
|
331
|
+
const src = path.join(pluginRoot, canonical);
|
|
277
332
|
const fm = readFrontmatter(src);
|
|
278
333
|
if (fm === null) {
|
|
279
|
-
warnings.push(`${
|
|
334
|
+
warnings.push(`${canonical}: has no parseable frontmatter — skipped`);
|
|
280
335
|
continue;
|
|
281
336
|
}
|
|
282
337
|
const { fm: portable, warnings: w } = toPortableFrontmatter(fm, name);
|
|
283
338
|
warnings.push(...w);
|
|
284
|
-
const
|
|
285
|
-
const
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
339
|
+
const artifacts = new Map([['SKILL.md', renderMirror(name, portable, canonical)]]);
|
|
340
|
+
const policy = invocationPolicy(pluginRoot, name, canonical, fm);
|
|
341
|
+
const policyPath = path.join(mirrorRoot, name, 'agents', 'openai.yaml');
|
|
342
|
+
if (policy !== undefined) {
|
|
343
|
+
artifacts.set('agents/openai.yaml', POLICY_MARKER + yaml.dump({
|
|
344
|
+
interface: { display_name: name },
|
|
345
|
+
policy: { allow_implicit_invocation: policy },
|
|
346
|
+
}, { lineWidth: -1, noRefs: true }));
|
|
347
|
+
} else if (existsSync(policyPath) && readFileSync(policyPath, 'utf8').startsWith(POLICY_MARKER)) {
|
|
348
|
+
if (check) drift.push(`.agents/skills/${name}/agents/openai.yaml is obsolete`);
|
|
349
|
+
else {
|
|
350
|
+
rmSync(policyPath);
|
|
351
|
+
written.push(`removed .agents/skills/${name}/agents/openai.yaml`);
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
for (const [file, rendered] of artifacts) {
|
|
355
|
+
const relative = `.agents/skills/${name}/${file}`;
|
|
356
|
+
const dest = path.join(mirrorRoot, name, file);
|
|
357
|
+
files += 1;
|
|
358
|
+
bytes += Buffer.byteLength(rendered, 'utf8');
|
|
359
|
+
const current = existsSync(dest) ? readFileSync(dest, 'utf8') : null;
|
|
360
|
+
if (file === 'agents/openai.yaml' && current !== null && !current.startsWith(POLICY_MARKER)) {
|
|
361
|
+
drift.push(`${relative} conflicts with an unrelated native policy — retained`);
|
|
362
|
+
continue;
|
|
363
|
+
}
|
|
364
|
+
if (current === rendered) continue;
|
|
365
|
+
if (check) drift.push(`${relative} is ${current === null ? 'missing' : 'stale'}`);
|
|
366
|
+
else {
|
|
367
|
+
mkdirSync(path.dirname(dest), { recursive: true });
|
|
368
|
+
writeFileSync(dest, rendered, 'utf8');
|
|
369
|
+
written.push(relative);
|
|
370
|
+
}
|
|
299
371
|
}
|
|
300
372
|
}
|
|
301
373
|
|
|
@@ -306,7 +378,7 @@ export function generateAgentsSurface({ pluginRoot, check = false }) {
|
|
|
306
378
|
for (const entry of readdirSync(mirrorRoot, { withFileTypes: true })) {
|
|
307
379
|
if (!entry.isDirectory() || known.has(entry.name)) continue;
|
|
308
380
|
if (check) {
|
|
309
|
-
drift.push(`.agents/skills/${entry.name}/ is an orphan (no
|
|
381
|
+
drift.push(`.agents/skills/${entry.name}/ is an orphan (no skill or command source)`);
|
|
310
382
|
} else {
|
|
311
383
|
rmSync(path.join(mirrorRoot, entry.name), { recursive: true, force: true });
|
|
312
384
|
written.push(`removed .agents/skills/${entry.name}/`);
|
|
@@ -356,8 +428,9 @@ function main(argv) {
|
|
|
356
428
|
}
|
|
357
429
|
} else {
|
|
358
430
|
for (const w of result.warnings) process.stderr.write(` WARN: agents-surface: ${w}\n`);
|
|
431
|
+
for (const d of result.drift) process.stderr.write(`✗ agents-surface: ${d}\n`);
|
|
359
432
|
process.stdout.write(
|
|
360
|
-
`agents-surface: ${result.written.length} file(s) written, ${result.files}
|
|
433
|
+
`agents-surface: ${result.written.length} file(s) written, ${result.files} portable artefact(s), ${result.bytes} bytes\n`,
|
|
361
434
|
);
|
|
362
435
|
}
|
|
363
436
|
return result.ok ? 0 : 1;
|
|
@@ -39,6 +39,9 @@ const CURSOR_SKILLS_DIR = path.join(ROOT, '.cursor', 'skills');
|
|
|
39
39
|
const CHECK_ONLY = process.argv.includes('--check');
|
|
40
40
|
const DESCRIPTION_MAX = 1024;
|
|
41
41
|
|
|
42
|
+
/** Memo for {@link skillFlags} — skill name → parsed frontmatter + both markers. */
|
|
43
|
+
const SKILL_FLAGS = new Map();
|
|
44
|
+
|
|
42
45
|
function isDir(p) {
|
|
43
46
|
try { return statSync(p).isDirectory(); } catch { return false; }
|
|
44
47
|
}
|
|
@@ -201,11 +204,41 @@ Cursor has no Skill tool. When the command says to invoke a skill, Read \`skills
|
|
|
201
204
|
* @returns {string[]} skill names, sorted
|
|
202
205
|
*/
|
|
203
206
|
function userInvocableSkills() {
|
|
204
|
-
return skillDirs().filter((name) =>
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
207
|
+
return skillDirs().filter((name) => skillFlags(name).userInvocable);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* Both boolean markers of ONE skill, parsed and judged exactly once.
|
|
212
|
+
*
|
|
213
|
+
* Memoised because the predicate is not side-effect free: a truthy-lookalike
|
|
214
|
+
* value (`yes`, `on`, `1`) emits a stderr WARN, so reading the same key from
|
|
215
|
+
* three call sites emitted the same warning three times — measured 2026-09-18
|
|
216
|
+
* on a two-skill fixture: 3 WARN lines for 2 defects. One skill, one reading,
|
|
217
|
+
* one diagnostic.
|
|
218
|
+
*
|
|
219
|
+
* @param {string} skillName
|
|
220
|
+
* @returns {{file: string, fields: Record<string, string>, userInvocable: boolean,
|
|
221
|
+
* sourceDisablesModel: boolean, disablesModel: boolean}}
|
|
222
|
+
*/
|
|
223
|
+
function skillFlags(skillName) {
|
|
224
|
+
const cached = SKILL_FLAGS.get(skillName);
|
|
225
|
+
if (cached) return cached;
|
|
226
|
+
const file = path.join(SKILLS_DIR, skillName, 'SKILL.md');
|
|
227
|
+
const fields = parseFrontmatter(readFileSync(file, 'utf8'));
|
|
228
|
+
const userInvocable = isUserInvocableValue(fields['user-invocable'], file, 'user-invocable');
|
|
229
|
+
const sourceDisablesModel = isUserInvocableValue(
|
|
230
|
+
fields['disable-model-invocation'], file, 'disable-model-invocation',
|
|
231
|
+
);
|
|
232
|
+
const flags = {
|
|
233
|
+
file,
|
|
234
|
+
fields,
|
|
235
|
+
userInvocable,
|
|
236
|
+
sourceDisablesModel,
|
|
237
|
+
// See {@link disablesModelInvocation} for the two OR-ed grounds.
|
|
238
|
+
disablesModel: sourceDisablesModel || !userInvocable,
|
|
239
|
+
};
|
|
240
|
+
SKILL_FLAGS.set(skillName, flags);
|
|
241
|
+
return flags;
|
|
209
242
|
}
|
|
210
243
|
|
|
211
244
|
/**
|
|
@@ -217,8 +250,7 @@ function userInvocableSkills() {
|
|
|
217
250
|
* @returns {string}
|
|
218
251
|
*/
|
|
219
252
|
function renderSkillCommand(skillName) {
|
|
220
|
-
const
|
|
221
|
-
const fields = parseFrontmatter(readFileSync(skillPath, 'utf8'));
|
|
253
|
+
const { fields, sourceDisablesModel } = skillFlags(skillName);
|
|
222
254
|
const description = clampDescription(fields.description || `Session Orchestrator skill: ${skillName}`);
|
|
223
255
|
// Same GH#54 rule as renderCommand: `argument-hint` ALWAYS goes through
|
|
224
256
|
// yamlQuote(), because its canonical authored form (`[mode] [--flag]`) is a
|
|
@@ -233,7 +265,7 @@ function renderSkillCommand(skillName) {
|
|
|
233
265
|
'---',
|
|
234
266
|
frontmatterLine('description', yamlQuote(description)),
|
|
235
267
|
frontmatterLine('argument-hint', yamlQuote(fields['argument-hint'])),
|
|
236
|
-
|
|
268
|
+
sourceDisablesModel ? 'disable-model-invocation: true' : null,
|
|
237
269
|
'---',
|
|
238
270
|
].filter(Boolean).join('\n');
|
|
239
271
|
|
|
@@ -269,6 +301,18 @@ Cursor has no Skill tool. When the skill says to invoke another skill, Read \`sk
|
|
|
269
301
|
* (measured 2026-09-17) on no Cursor-side evidence, which is the one
|
|
270
302
|
* direction a guard fix must never move.
|
|
271
303
|
*
|
|
304
|
+
* NAMED CEILING on ground 2 (BV-004). Whether Cursor reads
|
|
305
|
+
* `disable-model-invocation` at all is UNVERIFIED as of 2026-09-18: the key is
|
|
306
|
+
* documented here for Claude Code and Codex only, and `rg
|
|
307
|
+
* "disable-model-invocation" docs .claude .cursor/rules` returns zero Cursor
|
|
308
|
+
* citations. Ground 2 therefore stamps 24 wrappers on the ASSUMPTION that
|
|
309
|
+
* Cursor honours it — kept because stamping a restriction a reader ignores
|
|
310
|
+
* costs nothing, while dropping it would loosen 24 wrappers if the reader does
|
|
311
|
+
* honour it. REVISIT TRIGGER: Cursor documents the key (or a measured Cursor
|
|
312
|
+
* run shows it ignored) — then decide ground 2 on that evidence instead.
|
|
313
|
+
* Pinned by `tests/scripts/generate-cursor-adapter.test.mjs` ("stamps
|
|
314
|
+
* disable-model-invocation on ground 2 alone").
|
|
315
|
+
*
|
|
272
316
|
* Truth table — both readings normalised by `isUserInvocableValue`, so every
|
|
273
317
|
* form a YAML parser reads as `true` (`True`, `"true"`, `true # note`) counts
|
|
274
318
|
* as `true` on BOTH axes. Ground 2 made that load-bearing rather than cosmetic:
|
|
@@ -283,18 +327,19 @@ Cursor has no Skill tool. When the skill says to invoke another skill, Read \`sk
|
|
|
283
327
|
* | false/absent| true | no |
|
|
284
328
|
* | false/absent| false/absent | yes (2) |
|
|
285
329
|
*
|
|
286
|
-
*
|
|
287
|
-
*
|
|
330
|
+
* Both readings come from {@link skillFlags} (`sourceDisablesModel ||
|
|
331
|
+
* !userInvocable`), which parses each SKILL.md once — so a truthy-lookalike
|
|
332
|
+
* value warns once per key per skill instead of once per call site.
|
|
333
|
+
*
|
|
334
|
+
* @param {string} skillName
|
|
288
335
|
* @returns {boolean}
|
|
289
336
|
*/
|
|
290
|
-
function disablesModelInvocation(
|
|
291
|
-
return
|
|
292
|
-
|| !isUserInvocableValue(fields['user-invocable'], file);
|
|
337
|
+
function disablesModelInvocation(skillName) {
|
|
338
|
+
return skillFlags(skillName).disablesModel;
|
|
293
339
|
}
|
|
294
340
|
|
|
295
341
|
function renderSkill(skillName) {
|
|
296
|
-
const
|
|
297
|
-
const fields = parseFrontmatter(readFileSync(skillPath, 'utf8'));
|
|
342
|
+
const { fields } = skillFlags(skillName);
|
|
298
343
|
const description = clampDescription(fields.description || `Session Orchestrator skill: ${skillName}`);
|
|
299
344
|
// Same rule as renderCommand: no frontmatter value is emitted raw. `name` is
|
|
300
345
|
// a directory basename today, so it is plain-safe in practice — routing it
|
|
@@ -304,7 +349,7 @@ function renderSkill(skillName) {
|
|
|
304
349
|
`name: ${yamlQuote(skillName)}`,
|
|
305
350
|
`description: ${yamlQuote(description)}`,
|
|
306
351
|
];
|
|
307
|
-
if (disablesModelInvocation(
|
|
352
|
+
if (disablesModelInvocation(skillName)) {
|
|
308
353
|
lines.push('disable-model-invocation: true');
|
|
309
354
|
}
|
|
310
355
|
lines.push('---');
|
|
@@ -21,11 +21,11 @@ const FRONTMATTER_RE = /^---\r?\n([\s\S]*?)\r?\n---\r?\n?([\s\S]*)$/;
|
|
|
21
21
|
|
|
22
22
|
// Aliases per https://code.claude.com/docs/en/sub-agents § model resolution.
|
|
23
23
|
// Full model IDs (claude-{opus|sonnet|haiku|fable}-N[-N][-YYYYMMDD], e.g.
|
|
24
|
-
// "claude-opus-
|
|
24
|
+
// "claude-opus-5-5", "claude-sonnet-5", "claude-fable-5-20260101") also
|
|
25
25
|
// accepted via MODEL_ID_RE. `fable` is a valid upstream model family
|
|
26
26
|
// alongside opus/sonnet/haiku (#768) — the trailing numeric group is
|
|
27
27
|
// optional so both the two-part ("claude-sonnet-5") and three-part
|
|
28
|
-
// ("claude-opus-
|
|
28
|
+
// ("claude-opus-5-5") ID shapes validate.
|
|
29
29
|
// Exported so persona-panel modules (catalog-loader.mjs, persona-gate-wave.mjs) can
|
|
30
30
|
// validate against the SSOT instead of re-declaring (W4 architect MED-1, 2026-05-19).
|
|
31
31
|
export const ALLOWED_MODEL_ALIASES = new Set(['inherit', 'sonnet', 'opus', 'haiku', 'fable']);
|
|
@@ -187,7 +187,7 @@ export function validateAgentFrontmatter(frontmatter) {
|
|
|
187
187
|
errors.push({
|
|
188
188
|
path: 'model',
|
|
189
189
|
rule: 'enum',
|
|
190
|
-
message: `model must be one of ${[...ALLOWED_MODEL_ALIASES].map((m) => JSON.stringify(m)).join('|')} or a full model ID like "claude-opus-
|
|
190
|
+
message: `model must be one of ${[...ALLOWED_MODEL_ALIASES].map((m) => JSON.stringify(m)).join('|')} or a full model ID like "claude-opus-5-5" or "claude-sonnet-5" (got ${JSON.stringify(model)})`,
|
|
191
191
|
});
|
|
192
192
|
}
|
|
193
193
|
|
|
@@ -58,6 +58,7 @@ import { isDeepStrictEqual } from 'node:util';
|
|
|
58
58
|
import { appendJsonl } from './common.mjs';
|
|
59
59
|
import { writeJsonAtomicSync } from './io.mjs';
|
|
60
60
|
import { tryAcquireFileLock, releaseFileLock } from './file-lock.mjs';
|
|
61
|
+
import { readTailWindow } from './tail-window.mjs';
|
|
61
62
|
|
|
62
63
|
// ---------------------------------------------------------------------------
|
|
63
64
|
// Constants
|
|
@@ -303,36 +304,6 @@ function newestTsMs(entries) {
|
|
|
303
304
|
return newest;
|
|
304
305
|
}
|
|
305
306
|
|
|
306
|
-
/**
|
|
307
|
-
* Read the last `maxBytes` of a file. Returns the raw text plus whether the
|
|
308
|
-
* window cut into the file (i.e. the first line in `text` may be partial).
|
|
309
|
-
* @param {string} file
|
|
310
|
-
* @param {number} maxBytes
|
|
311
|
-
* @returns {{ text: string, size: number, cut: boolean }}
|
|
312
|
-
*/
|
|
313
|
-
function readTail(file, maxBytes) {
|
|
314
|
-
const fd = fs.openSync(file, 'r');
|
|
315
|
-
try {
|
|
316
|
-
const size = fs.fstatSync(fd).size;
|
|
317
|
-
const want = Math.min(size, maxBytes);
|
|
318
|
-
const start = size - want;
|
|
319
|
-
const buf = Buffer.allocUnsafe(want);
|
|
320
|
-
let read = 0;
|
|
321
|
-
while (read < want) {
|
|
322
|
-
const n = fs.readSync(fd, buf, read, want - read, start + read);
|
|
323
|
-
if (n <= 0) break;
|
|
324
|
-
read += n;
|
|
325
|
-
}
|
|
326
|
-
return { text: buf.subarray(0, read).toString('utf8'), size, cut: start > 0 };
|
|
327
|
-
} finally {
|
|
328
|
-
try {
|
|
329
|
-
fs.closeSync(fd);
|
|
330
|
-
} catch {
|
|
331
|
-
/* best-effort */
|
|
332
|
-
}
|
|
333
|
-
}
|
|
334
|
-
}
|
|
335
|
-
|
|
336
307
|
/**
|
|
337
308
|
* Shared write path for both setters: append the JSONL record, then perform a
|
|
338
309
|
* lock-serialised read-modify-write of the LWW map. No-throw.
|
|
@@ -481,7 +452,7 @@ export function rebuildCurrentFromLedger(opts = {}) {
|
|
|
481
452
|
|
|
482
453
|
let tail;
|
|
483
454
|
try {
|
|
484
|
-
tail =
|
|
455
|
+
tail = readTailWindow(jsonlFile, maxBytes);
|
|
485
456
|
} catch (err) {
|
|
486
457
|
const reason = err?.code === 'ENOENT' ? 'ledger-missing' : 'ledger-unreadable';
|
|
487
458
|
return {
|
|
@@ -801,9 +801,17 @@ function checkK5(q, at) {
|
|
|
801
801
|
severity: 'fail',
|
|
802
802
|
target: 'header',
|
|
803
803
|
...at,
|
|
804
|
+
// Der Text sagte bis hierher "Das Tool zeigt nur 12 — den Rest sieht der
|
|
805
|
+
// Operator nie". Das ist widerlegt: die 12 steht allein in der Prosa des
|
|
806
|
+
// Schemas (kein `.max(12)`, kein kuerzender Render-Pfad in Bundle
|
|
807
|
+
// 2.1.268), und 125 laengere Kopfzeilen wurden angenommen und beantwortet
|
|
808
|
+
// (ask-via-tool.md AUQ-006). H1 blockt deshalb nicht (BLOCKING_HURDLES =
|
|
809
|
+
// ['H2']) — die Meldung ist ein Hinweis, keine Abweisung, und darf keine
|
|
810
|
+
// Kuerzung begruenden, die es nicht braucht.
|
|
804
811
|
message:
|
|
805
|
-
`Die Kopfzeile "${header}" hat ${literal} Zeichen
|
|
806
|
-
`${THRESHOLDS.K5.headerCharsFail}
|
|
812
|
+
`Die Kopfzeile "${header}" hat ${literal} Zeichen; Ziel sind ` +
|
|
813
|
+
`${THRESHOLDS.K5.headerCharsFail} Codepoints. Das Tool schneidet nicht ab — ` +
|
|
814
|
+
`Kuerzen ist optional.`,
|
|
807
815
|
measured: literal,
|
|
808
816
|
threshold: THRESHOLDS.K5.headerCharsFail,
|
|
809
817
|
hurdle: HURDLES.H1.id,
|
|
@@ -83,6 +83,7 @@ import {
|
|
|
83
83
|
isRecommendedOption,
|
|
84
84
|
} from './schema.mjs';
|
|
85
85
|
import { scanFenceBlocks } from '../validate/markdown-fences.mjs';
|
|
86
|
+
import { maskSource } from '../js-mask.mjs';
|
|
86
87
|
|
|
87
88
|
// ---------------------------------------------------------------------------
|
|
88
89
|
// Korpus-Abgrenzung
|
|
@@ -142,42 +143,22 @@ export function corpusKindOf(file) {
|
|
|
142
143
|
* Anführungspaar, das jeder „nimm das letzte Literal der Zeile"-Ansatz als
|
|
143
144
|
* Label extrahiert.
|
|
144
145
|
*
|
|
146
|
+
* Seit #1388 delegiert das an den geteilten Lexer `maskSource`
|
|
147
|
+
* (`../js-mask.mjs`, Modus `keepLiterals`): gleiche Zusicherung (Länge und
|
|
148
|
+
* Zeilenumbrüche erhalten, Literal-Inhalte unangetastet), aber MIT
|
|
149
|
+
* Regex-Literal-Zweig. Die frühere handgeschriebene Fassung hatte keinen —
|
|
150
|
+
* gemessen 2026-09-18 verschluckte `const re = /\/*$/;` als vermeintlicher
|
|
151
|
+
* Block-Kommentar-Anfang alles bis zum nächsten `*` + `/`, sodass ein
|
|
152
|
+
* darunter stehendes `label: "X (Recommended)"` spurlos verschwand: ein
|
|
153
|
+
* STILLER FALSCH-NEGATIVER Befund in der Klarheitsmessung, längentreu und
|
|
154
|
+
* daher an keiner Zeilennummer erkennbar.
|
|
155
|
+
*
|
|
145
156
|
* @param {string} text
|
|
146
157
|
* @returns {string} gleiche Länge, Kommentarinhalt durch Leerzeichen ersetzt
|
|
147
158
|
*/
|
|
148
159
|
export function stripComments(text) {
|
|
149
160
|
if (typeof text !== 'string' || text === '') return '';
|
|
150
|
-
|
|
151
|
-
let i = 0;
|
|
152
|
-
while (i < out.length) {
|
|
153
|
-
const c = text[i];
|
|
154
|
-
if (c === '"' || c === "'" || c === '`') {
|
|
155
|
-
const lit = readStringLiteral(text, i);
|
|
156
|
-
i = lit ? lit.end : i + 1;
|
|
157
|
-
continue;
|
|
158
|
-
}
|
|
159
|
-
if (c === '/' && text[i + 1] === '/') {
|
|
160
|
-
while (i < out.length && text[i] !== '\n') out[i++] = ' ';
|
|
161
|
-
continue;
|
|
162
|
-
}
|
|
163
|
-
if (c === '/' && text[i + 1] === '*') {
|
|
164
|
-
out[i] = ' ';
|
|
165
|
-
out[i + 1] = ' ';
|
|
166
|
-
i += 2;
|
|
167
|
-
while (i < out.length && !(text[i] === '*' && text[i + 1] === '/')) {
|
|
168
|
-
if (text[i] !== '\n') out[i] = ' ';
|
|
169
|
-
i++;
|
|
170
|
-
}
|
|
171
|
-
if (i < out.length) {
|
|
172
|
-
out[i] = ' ';
|
|
173
|
-
out[i + 1] = ' ';
|
|
174
|
-
i += 2;
|
|
175
|
-
}
|
|
176
|
-
continue;
|
|
177
|
-
}
|
|
178
|
-
i++;
|
|
179
|
-
}
|
|
180
|
-
return out.join('');
|
|
161
|
+
return maskSource(text, { keepLiterals: true });
|
|
181
162
|
}
|
|
182
163
|
|
|
183
164
|
/**
|