session-orchestrator 5.11.0 → 5.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +12 -1
- package/.claude-plugin/plugin.json +4 -1
- package/.codex-plugin/agents/explorer.toml +1 -1
- package/.codex-plugin/agents/session-reviewer.toml +2 -2
- package/.codex-plugin/agents/wave-worker.toml +1 -1
- package/.codex-plugin/plugin.json +1 -1
- package/.cursor-plugin/plugin.json +1 -1
- package/.orchestrator/policy/blocked-commands.json +8 -0
- package/.orchestrator/policy/public-export.json +30 -0
- package/CHANGELOG.md +180 -3
- package/README.md +126 -117
- package/agents/analyst.md +7 -2
- package/agents/architect-reviewer.md +7 -2
- package/agents/code-implementer.md +4 -2
- package/agents/db-specialist.md +9 -2
- package/agents/dialectic-deriver.md +9 -3
- package/agents/docs-writer.md +7 -2
- package/agents/eval-judge.md +7 -1
- package/agents/qa-strategist.md +7 -2
- package/agents/security-reviewer.md +9 -2
- package/agents/session-reviewer.md +18 -2
- package/agents/skill-applied-judge.md +7 -1
- package/agents/test-writer.md +9 -2
- package/agents/ui-developer.md +9 -2
- package/agents/ux-evaluator.md +6 -1
- package/commands/session.md +4 -2
- package/commands/templates-ack.md +89 -18
- package/docs/agent-authoring.md +30 -11
- package/docs/ci-setup.md +38 -2
- package/docs/codex-setup.md +33 -9
- package/docs/components.md +13 -2
- package/docs/cursor-setup.md +3 -3
- package/docs/events-schema.md +5 -4
- package/docs/evidence-candidates.md +63 -0
- package/docs/fleet-checkin-audit.md +7 -4
- package/docs/github-mirror-protection.md +150 -3
- package/docs/install.md +102 -25
- package/docs/instruction-delivery.md +1 -1
- package/docs/migration-v4.md +2 -2
- package/docs/mods.md +118 -0
- package/docs/owner-config-schema.md +1 -0
- package/docs/plugin-architecture-v3.md +1 -1
- package/docs/pm-skills-marketplace.md +4 -3
- package/docs/rule-authoring.md +24 -13
- package/docs/scope-collision-guard.md +2 -1
- package/docs/session-config-reference.md +55 -21
- package/docs/session-config-template.md +32 -24
- package/docs/sessions-ledger-repair.md +137 -0
- package/docs/vault-docs-architecture.md +1 -1
- package/hooks/_lib/hook-import-set.json +406 -73
- package/hooks/_lib/hook-root.mjs +301 -0
- package/hooks/_lib/lock-bootstrap.mjs +434 -4
- package/hooks/_lib/orchestrator-ignore.mjs +3 -0
- package/hooks/_lib/subagent-transcript.mjs +2 -2
- package/hooks/_lib/vcs-create-matcher.mjs +13 -17
- package/hooks/cwd-change-restore.mjs +5 -1
- package/hooks/enforce-commands.mjs +4 -3
- package/hooks/enforce-scope.mjs +11 -1
- package/hooks/hooks-codex.json +48 -2
- package/hooks/hooks.json +31 -31
- package/hooks/on-session-end.mjs +38 -15
- package/hooks/on-session-start.mjs +158 -59
- package/hooks/on-stop.mjs +11 -6
- package/hooks/post-bash-issue-budget-refund.mjs +6 -1
- package/hooks/post-bash-write-verify.mjs +8 -4
- package/hooks/post-edit-validate.mjs +45 -52
- package/hooks/post-subagent-discovery-validator.mjs +2 -2
- package/hooks/post-tool-batch-wave-signal.mjs +8 -4
- package/hooks/post-tool-failure-corrective-context.mjs +5 -1
- package/hooks/pre-bash-destructive-guard.mjs +220 -8
- package/hooks/pre-bash-issue-budget.mjs +6 -1
- package/hooks/pre-bash-sessions-ledger-guard.mjs +691 -123
- package/hooks/pre-bash-staging-fence.mjs +258 -14
- package/hooks/pre-bash-templates-first.mjs +28 -27
- package/hooks/run-node.sh +1 -1
- package/hooks/skill-invocation-telemetry.mjs +15 -8
- package/hooks/subagent-telemetry.mjs +77 -14
- package/hooks/wave-scope-commit-guard.mjs +143 -14
- package/mods/so-hud/.claude-plugin/plugin.json +11 -0
- package/mods/so-hud/README.md +57 -0
- package/mods/so-hud/hooks/beacon-io.mjs +101 -0
- package/mods/so-hud/hooks/heredoc-scan.ts +394 -0
- package/mods/so-hud/hooks/hooks.json +1 -0
- package/mods/so-hud/hooks/logic.ts +182 -0
- package/mods/so-hud/hooks/register.tsx +670 -0
- package/mods/so-hud/hooks/rules.ts +2998 -0
- package/mods/so-hud/tsconfig.json +8 -0
- package/mods/so-hud/types/index.d.ts +36 -0
- package/package.json +4 -3
- package/rules/_index.md +1 -1
- package/rules/always-on/ask-via-tool.md +1 -1
- package/rules/always-on/loop-and-monitor.md +17 -194
- package/rules/always-on/parallel-sessions.md +3 -1
- package/rules/opt-in-stack/backend-data.md +1 -1
- package/rules/opt-in-stack/backend.md +1 -1
- package/rules/opt-in-stack/security-web.md +5 -1
- package/scripts/agent-usage-census.mjs +79 -0
- package/scripts/apply-session-learnings.mjs +34 -6
- package/scripts/archive-closed-prds.mjs +21 -5
- package/scripts/autopilot.mjs +11 -5
- package/scripts/backfill-abandoned-sessions.mjs +13 -3
- package/scripts/backfill-evidence-digest.mjs +2 -1
- package/scripts/backfill-learnings-from-vault.mjs +61 -6
- package/scripts/backfill-sessions.mjs +16 -2
- package/scripts/check-package-manager.mjs +26 -8
- package/scripts/check-sessions-integrity.mjs +4 -1
- package/scripts/ci/assert-vitest-green.mjs +3 -0
- package/scripts/ci/windows-smoke.mjs +201 -0
- package/scripts/close-precheck.mjs +55 -0
- package/scripts/dialectic-deriver.mjs +20 -12
- package/scripts/emit-event.mjs +2 -2
- package/scripts/emit-session.mjs +239 -20
- package/scripts/eval-session.mjs +17 -3
- package/scripts/export-hw-learnings.mjs +29 -2
- package/scripts/fleet-checkin-audit.mjs +8 -4
- package/scripts/fleet-checkin.mjs +73 -3
- package/scripts/gc-stale-worktrees.mjs +6 -1
- package/scripts/github-export.mjs +128 -0
- package/scripts/github-protection-audit.mjs +3 -1
- package/scripts/handover-queue-check.mjs +209 -0
- package/scripts/harness-audit.mjs +6 -4
- package/scripts/lib/agent-frontmatter.mjs +57 -10
- package/scripts/lib/agent-status.mjs +125 -7
- package/scripts/lib/agent-usage-census.mjs +394 -0
- package/scripts/lib/auto-dialectic.mjs +17 -10
- package/scripts/lib/auto-dream.mjs +22 -14
- package/scripts/lib/autopilot/loop.mjs +3 -1
- package/scripts/lib/autopilot/recent-runs.mjs +4 -2
- package/scripts/lib/autopilot/stall-sampler.mjs +2 -1
- package/scripts/lib/autopilot/telemetry.mjs +5 -1
- package/scripts/lib/autopilot/worktree-pipeline.mjs +5 -5
- package/scripts/lib/baseline-archetypes.mjs +10 -2
- package/scripts/lib/blocked-commands-policy.mjs +4 -3
- package/scripts/lib/build-live-signals.mjs +13 -9
- package/scripts/lib/ci-status-banner.mjs +329 -24
- package/scripts/lib/codex/plugin-contract.mjs +8 -0
- package/scripts/lib/cold-start-detector.mjs +5 -2
- package/scripts/lib/command-blocker.mjs +603 -43
- package/scripts/lib/common.mjs +3 -2
- package/scripts/lib/config/cross-repo.mjs +74 -7
- package/scripts/lib/config/host-paths.mjs +6 -2
- package/scripts/lib/config/remote-hosts.mjs +2 -2
- package/scripts/lib/config/section-extractor.mjs +65 -11
- package/scripts/lib/config/template-keys.mjs +153 -0
- package/scripts/lib/config/vault-integration.mjs +1 -0
- package/scripts/lib/config.mjs +3 -3
- package/scripts/lib/coordinator-snapshot.mjs +1 -1
- package/scripts/lib/discovery/triage-state.mjs +2 -1
- package/scripts/lib/dispatcher/rank.mjs +5 -7
- package/scripts/lib/ecosystem-health.mjs +2 -2
- package/scripts/lib/eval/engine.mjs +3 -2
- package/scripts/lib/eval/session-resolve.mjs +6 -2
- package/scripts/lib/eval/sink.mjs +4 -3
- package/scripts/lib/events-retention-banner.mjs +3 -2
- package/scripts/lib/events-schema.mjs +10 -0
- package/scripts/lib/events.mjs +23 -12
- package/scripts/lib/evolve/autonomy-verdict.mjs +4 -9
- package/scripts/lib/evolve/autopilot-effectiveness.mjs +8 -18
- package/scripts/lib/fleet-protocol.mjs +21 -0
- package/scripts/lib/gates/gate-full.mjs +172 -11
- package/scripts/lib/gates/gate-helpers.mjs +236 -23
- package/scripts/lib/git-config-drift.mjs +141 -7
- package/scripts/lib/github-export.mjs +447 -0
- package/scripts/lib/gitlab-portfolio/cli.mjs +16 -5
- package/scripts/lib/handover-queue-marks.mjs +159 -0
- package/scripts/lib/hardware-pattern-detector.mjs +103 -13
- package/scripts/lib/harness-audit/categories/category1.mjs +16 -4
- package/scripts/lib/harness-audit/categories/category4.mjs +11 -8
- package/scripts/lib/harness-audit/categories/category6.mjs +25 -5
- package/scripts/lib/harness-audit/categories/category9.mjs +10 -4
- package/scripts/lib/host-identity.mjs +9 -5
- package/scripts/lib/http-request.mjs +64 -0
- package/scripts/lib/instruction-budget-guard.mjs +30 -22
- package/scripts/lib/io.mjs +6 -3
- package/scripts/lib/issue-budget-reconcile.mjs +3 -2
- package/scripts/lib/issue-budget.mjs +36 -7
- package/scripts/lib/learnings/evidence-candidates.mjs +154 -0
- package/scripts/lib/learnings/expiry-sweep.mjs +99 -7
- package/scripts/lib/learnings/io.mjs +289 -15
- package/scripts/lib/learnings/select.mjs +53 -4
- package/scripts/lib/learnings/store-role.mjs +209 -0
- package/scripts/lib/learnings/surface.mjs +46 -7
- package/scripts/lib/ledger-path.mjs +113 -0
- package/scripts/lib/maintenance-due-banner.mjs +63 -12
- package/scripts/lib/memory-banner.mjs +5 -6
- package/scripts/lib/memory-proposals/collector.mjs +5 -2
- package/scripts/lib/memory-proposals/sink.mjs +7 -4
- package/scripts/lib/memory-proposals/store.mjs +22 -5
- package/scripts/lib/mode-selector-accuracy.mjs +2 -1
- package/scripts/lib/no-follow-open.mjs +57 -0
- package/scripts/lib/orphan-reaper/reaper-audit.mjs +4 -2
- package/scripts/lib/own-lock-root.mjs +95 -0
- package/scripts/lib/owner-interview.mjs +2 -1
- package/scripts/lib/owner-yaml.mjs +70 -7
- package/scripts/lib/platform.mjs +161 -9
- package/scripts/lib/plugin-update-banner.mjs +15 -11
- package/scripts/lib/pre-push-local-partial.mjs +117 -0
- package/scripts/lib/pre-push-remote-gate.mjs +151 -25
- package/scripts/lib/process-group.mjs +270 -34
- package/scripts/lib/project-hygiene.mjs +28 -0
- package/scripts/lib/quality-gate.mjs +33 -9
- package/scripts/lib/reconcile/backlog.mjs +29 -15
- package/scripts/lib/reconcile/engine.mjs +26 -2
- package/scripts/lib/reconcile/idempotency.mjs +65 -3
- package/scripts/lib/reconcile/rule-expiry-sweep.mjs +53 -12
- package/scripts/lib/reconcile/verdict-ledger.mjs +224 -0
- package/scripts/lib/reconcile-nudge-banner.mjs +40 -6
- package/scripts/lib/resource-probe/evaluate.mjs +32 -9
- package/scripts/lib/resource-probe/probe-platform.mjs +3 -1
- package/scripts/lib/rule-loader.mjs +20 -10
- package/scripts/lib/scope-baseline.mjs +123 -8
- package/scripts/lib/scope-echo.mjs +5 -2
- package/scripts/lib/scope-gate.mjs +55 -20
- package/scripts/lib/session-close-backfill.mjs +75 -7
- package/scripts/lib/session-end/close-precheck.mjs +151 -0
- package/scripts/lib/session-end/phase-skip.mjs +8 -7
- package/scripts/lib/session-end/tail-runner.mjs +7 -2
- package/scripts/lib/session-end/vault-orphans.mjs +205 -0
- package/scripts/lib/session-id.mjs +9 -1
- package/scripts/lib/session-identity/own-session.mjs +63 -0
- package/scripts/lib/session-invocation.mjs +14 -3
- package/scripts/lib/session-lock.mjs +114 -0
- package/scripts/lib/session-record-repair.mjs +245 -35
- package/scripts/lib/session-registry.mjs +85 -12
- package/scripts/lib/session-schema/constants.mjs +9 -0
- package/scripts/lib/session-schema/filters.mjs +56 -1
- package/scripts/lib/session-schema/validator.mjs +15 -5
- package/scripts/lib/session-start-probes.mjs +149 -22
- package/scripts/lib/session-token-rollup.mjs +2 -1
- package/scripts/lib/session-work-trees.mjs +166 -0
- package/scripts/lib/sessions-canonical.mjs +61 -3
- package/scripts/lib/sessions-integrity-banner.mjs +17 -39
- package/scripts/lib/sessions-staleness-banner.mjs +8 -5
- package/scripts/lib/sh-quote.mjs +1 -0
- package/scripts/lib/skill-evidence-window.mjs +5 -1
- package/scripts/lib/skill-health/join.mjs +10 -9
- package/scripts/lib/skill-invocations-schema.mjs +13 -0
- package/scripts/lib/skill-judgments-schema.mjs +6 -2
- package/scripts/lib/snapshot-probe.mjs +414 -0
- package/scripts/lib/so-hud/collect.mjs +174 -0
- package/scripts/lib/so-hud/runtime.mjs +87 -0
- package/scripts/lib/spiral-carryover.mjs +8 -1
- package/scripts/lib/state-md/frontmatter-mutators.mjs +266 -8
- package/scripts/lib/state-md/idle-reset.mjs +248 -0
- package/scripts/lib/state-md/mission-status.mjs +5 -1
- package/scripts/lib/state-md/recommendations.mjs +26 -4
- package/scripts/lib/state-md-peer-guard.mjs +2 -0
- package/scripts/lib/state-md.mjs +31 -8
- package/scripts/lib/sunset/walker.mjs +4 -3
- package/scripts/lib/telemetry/consent.mjs +4 -2
- package/scripts/lib/telemetry/queue.mjs +3 -3
- package/scripts/lib/telemetry/sync.mjs +45 -9
- package/scripts/lib/test-runner/artifact-paths.mjs +10 -2
- package/scripts/lib/tmux-layout/layouts.mjs +20 -3
- package/scripts/lib/ux-grill/paths.mjs +5 -1
- package/scripts/lib/validate/check-agents.mjs +37 -6
- package/scripts/lib/validate/check-component-paths.mjs +25 -1
- package/scripts/lib/validate/check-guard-requires-parity.mjs +3 -3
- package/scripts/lib/validate/check-hooks-symmetry.mjs +53 -32
- package/scripts/lib/validate/check-json-files.mjs +3 -1
- package/scripts/lib/validate/check-learning-provenance.mjs +3 -2
- package/scripts/lib/validate/check-learnings-shell-writes.mjs +10 -4
- package/scripts/lib/validate/check-metrics-ledger-root.mjs +234 -0
- package/scripts/lib/validate/check-owner-leakage.mjs +159 -11
- package/scripts/lib/validate/check-test-posix-shell.mjs +246 -0
- package/scripts/lib/validate/check-unwired-features.mjs +4 -2
- package/scripts/lib/validate/check-vcs-repo-flag-positional.mjs +58 -0
- package/scripts/lib/validate/check-vcs-repo-flag.mjs +259 -73
- package/scripts/lib/validate/check-workflow-continue-on-error.mjs +63 -0
- package/scripts/lib/validate/confidential-names.mjs +3 -1
- package/scripts/lib/vault-mirror/migrate-ids.mjs +321 -0
- package/scripts/lib/vault-mirror/namespace.mjs +22 -9
- package/scripts/lib/vault-mirror/process.mjs +197 -65
- package/scripts/lib/vault-mirror/prune.mjs +5 -2
- package/scripts/lib/vault-mirror/render-learnings.mjs +8 -7
- package/scripts/lib/vault-mirror/render-sessions.mjs +36 -53
- package/scripts/lib/vault-mirror/retention.mjs +2 -2
- package/scripts/lib/vault-mirror/utils.mjs +106 -13
- package/scripts/lib/vault-staleness-banner.mjs +4 -7
- package/scripts/lib/vault-status/board-writer.mjs +34 -5
- package/scripts/lib/vault-status/narrative-mirror.mjs +66 -7
- package/scripts/lib/wave-executor/foreign-dispatch.mjs +20 -0
- package/scripts/lib/win-process-tree.mjs +137 -0
- package/scripts/lib/workspace.mjs +1 -1
- package/scripts/lib/worktree/constants.mjs +1 -1
- package/scripts/lib/worktree/lifecycle.mjs +1 -1
- package/scripts/lib/worktree/listing.mjs +1 -1
- package/scripts/lib/zx.mjs +50 -0
- package/scripts/mcp-server.mjs +16 -7
- package/scripts/mcp-server.sh +13 -83
- package/scripts/measure-policy-cache-effectiveness.mjs +7 -5
- package/scripts/memory-propose.mjs +10 -3
- package/scripts/migrate-cold-start-seed.mjs +7 -0
- package/scripts/migrate-legacy-learnings.sh +22 -0
- package/scripts/migrate-sessions-jsonl.mjs +16 -2
- package/scripts/migrate-vault-paths.mjs +29 -13
- package/scripts/parse-config.mjs +71 -14
- package/scripts/print-learnings-index.mjs +25 -2
- package/scripts/release-session-lock.mjs +94 -7
- package/scripts/release.mjs +451 -24
- package/scripts/relocate-vault-corpus.mjs +11 -7
- package/scripts/repair-invalid-sessions.mjs +68 -3
- package/scripts/resolve-session-invocation.mjs +57 -8
- package/scripts/run-migrate-v2-cross-repo.mjs +56 -1
- package/scripts/run-quality-gate.mjs +33 -7
- package/scripts/self-update.mjs +56 -7
- package/scripts/session-commit-files.mjs +133 -0
- package/scripts/site-numbers.mjs +52 -9
- package/scripts/snapshot-probe.mjs +156 -0
- package/scripts/so-hud.mjs +68 -0
- package/scripts/state-md-idle-reset.mjs +82 -0
- package/scripts/sweep-expired-learnings.mjs +54 -6
- package/scripts/sweep-expired-rules.mjs +4 -1
- package/scripts/validate-plugin.mjs +22 -0
- package/scripts/vault-integration-watcher.mjs +54 -6
- package/scripts/vault-mirror.mjs +9 -0
- package/scripts/vault-orphans.mjs +171 -0
- package/skills/_shared/fleet-protocol.md +166 -3
- package/skills/_shared/instruction-file-resolution.md +1 -1
- package/skills/_shared/monitor-patterns.md +44 -25
- package/skills/_shared/platform-tools.md +7 -0
- package/skills/_shared/state-ownership.md +5 -4
- package/skills/architecture/references/domain-model.md +1 -1
- package/skills/autopilot/SKILL.md +3 -2
- package/skills/bootstrap/SKILL.md +3 -1
- package/skills/bootstrap/_shared-template.md +4 -4
- package/skills/bootstrap/fast-template.md +1 -1
- package/skills/bootstrap/private-contract.md +1 -1
- package/skills/bootstrap/references/bootstrap-retroactive-flow.md +17 -9
- package/skills/bootstrap/templates/agents/project-code-review.md +5 -1
- package/skills/bootstrap/templates/agents/project-discovery.md +5 -1
- package/skills/bootstrap/templates/agents/project-quality-gate.md +5 -1
- package/skills/claude-md-drift-check/SKILL.md +3 -3
- package/skills/claude-md-drift-check/checker.mjs +61 -101
- package/skills/close/SKILL.md +20 -7
- package/skills/discovery/probes/docs-staleness.mjs +7 -5
- package/skills/discovery/probes/frontend-slop.mjs +7 -3
- package/skills/discovery/probes/ssot-code-diff.mjs +5 -3
- package/skills/discovery/probes/supply-chain-slopcheck.mjs +10 -8
- package/skills/discovery/probes/vault-narrative-staleness.mjs +30 -10
- package/skills/discovery/probes/vault-staleness.mjs +5 -3
- package/skills/discovery/probes-session.md +3 -0
- package/skills/discovery/probes-vault.md +2 -0
- package/skills/dispatcher/SKILL.md +1 -1
- package/skills/eval/SKILL.md +3 -1
- package/skills/evolve/SKILL.md +2 -0
- package/skills/evolve/references/evolve-analyze-mode.md +3 -3
- package/skills/evolve/references/evolve-dialectic-mode.md +1 -1
- package/skills/gitlab-ops/SKILL.md +7 -6
- package/skills/gitlab-portfolio/SKILL.md +2 -2
- package/skills/go/SKILL.md +13 -1
- package/skills/npm-publish/SKILL.md +5 -2
- package/skills/plan/SKILL.md +23 -10
- package/skills/plan/mode-feature.md +13 -8
- package/skills/plan/mode-new.md +6 -14
- package/skills/plan/prd-feature-template.md +19 -0
- package/skills/plan/prd-full-template.md +12 -5
- package/skills/plan/prd-premortem-panel.md +48 -0
- package/skills/plan/prd-reviewer-prompt.md +11 -3
- package/skills/plan/soul.md +1 -1
- package/skills/portfolio/SKILL.md +5 -6
- package/skills/quality-gates/SKILL.md +2 -4
- package/skills/reconcile/SKILL.md +27 -4
- package/skills/release/SKILL.md +16 -8
- package/skills/session-end/SKILL.md +97 -13
- package/skills/session-end/phase-3-2-docs-verification.md +1 -0
- package/skills/session-end/phase-3-6-tail.md +11 -7
- package/skills/session-end/phase-3-7a-recommendations.md +9 -2
- package/skills/session-end/plan-verification.md +16 -1
- package/skills/session-end/references/phase-3-7e-vault-orphans.md +63 -0
- package/skills/session-end/references/phase-3-documentation-updates.md +25 -13
- package/skills/session-end/references/phase-5-issue-cleanup.md +1 -1
- package/skills/session-end/references/session-summary-template.md +8 -0
- package/skills/session-end/session-metrics-write.md +7 -4
- package/skills/session-plan/SKILL.md +5 -3
- package/skills/session-start/SKILL.md +10 -4
- package/skills/session-start/phase-4-5-resource-health.md +11 -5
- package/skills/session-start/phase-7-5-mode-selector.md +3 -1
- package/skills/session-start/phase-8-5-express-path.md +4 -3
- package/skills/session-start/references/phase-1-2-session-lock.md +7 -5
- package/skills/session-start/references/phase-1-5-session-continuity.md +13 -5
- 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 +3 -2
- package/skills/tmux-layout/SKILL.md +2 -2
- package/skills/vault-sync/validator.mjs +3 -2
- package/skills/wave-executor/SKILL.md +5 -4
- package/skills/wave-executor/references/wave-executor-state-init.md +3 -3
- package/skills/wave-executor/references/wave-loop-dispatch.md +11 -5
- package/skills/wave-executor/references/wave-loop-review.md +13 -5
- package/skills/wave-executor/references/wave-loop-scope-manifest.md +1 -1
- package/AGENTS.md +0 -207
- package/assets/og-card.png +0 -0
- package/assets/og-card.svg +0 -47
- package/assets/wave-lifecycle.svg +0 -98
|
@@ -139,6 +139,7 @@ import { atomicWriteText, envelopeToError } from './lib/io.mjs';
|
|
|
139
139
|
import { validateLearning } from './lib/learnings/schema.mjs';
|
|
140
140
|
import { validatePathInsideProject } from './lib/path-utils.mjs';
|
|
141
141
|
import { emitEvolveCompleted } from './lib/learnings/evolve-telemetry.mjs';
|
|
142
|
+
import { resolveLedgerPath, resolveLedgerRoot } from './lib/ledger-path.mjs';
|
|
142
143
|
|
|
143
144
|
const DEFAULT_FILE = '.orchestrator/metrics/learnings.jsonl';
|
|
144
145
|
const DEFAULT_ARCHIVE = '.orchestrator/metrics/learnings-archive.jsonl';
|
|
@@ -257,6 +258,7 @@ function parseArgs(argv) {
|
|
|
257
258
|
generation: null,
|
|
258
259
|
acceptEmbedded: false,
|
|
259
260
|
};
|
|
261
|
+
let fileExplicit = false;
|
|
260
262
|
for (let i = 0; i < argv.length; i++) {
|
|
261
263
|
const a = argv[i];
|
|
262
264
|
if (a === '--prune') {
|
|
@@ -296,6 +298,7 @@ function parseArgs(argv) {
|
|
|
296
298
|
args.snapshot = pathValue(a, argv[++i]);
|
|
297
299
|
} else if (a === '--file') {
|
|
298
300
|
args.file = pathValue(a, argv[++i]);
|
|
301
|
+
fileExplicit = true;
|
|
299
302
|
} else if (a === '--archive') {
|
|
300
303
|
args.archive = pathValue(a, argv[++i]);
|
|
301
304
|
args.archiveExplicit = true;
|
|
@@ -343,6 +346,12 @@ function parseArgs(argv) {
|
|
|
343
346
|
}
|
|
344
347
|
}
|
|
345
348
|
|
|
349
|
+
// #1722: the DEFAULT store/archive route through the ledger resolver (main
|
|
350
|
+
// checkout when cwd is a linked worktree that gitignores them); outside one
|
|
351
|
+
// the relative defaults are kept verbatim. Explicit --file/--archive stay.
|
|
352
|
+
if (!fileExplicit) args.file = routedDefault(DEFAULT_FILE);
|
|
353
|
+
if (!args.archiveExplicit) args.archive = routedDefault(DEFAULT_ARCHIVE);
|
|
354
|
+
|
|
346
355
|
// Mode/flag mismatches are usage errors, never silent no-ops: a `--grace-days`
|
|
347
356
|
// that the prune path ignores would read as "the grace window applied" in a
|
|
348
357
|
// transcript, and an `--entries` the sweep ignores would read as "my next
|
|
@@ -381,7 +390,7 @@ function parseArgs(argv) {
|
|
|
381
390
|
// (macOS /var → /private/var) refused a store inside it. The file goes
|
|
382
391
|
// through resolveLedger (realpath when it exists), because
|
|
383
392
|
// canonicalizeRoot realpaths only the root.
|
|
384
|
-
if (
|
|
393
|
+
if (ledgerRootOf(args.file, args.repoRoot) === null) {
|
|
385
394
|
usageError(`--file ${args.file} lies outside --repo-root ${args.repoRoot} — nothing written`);
|
|
386
395
|
}
|
|
387
396
|
}
|
|
@@ -441,7 +450,9 @@ async function runSweep(args) {
|
|
|
441
450
|
} else {
|
|
442
451
|
process.stdout.write(
|
|
443
452
|
`sweep-expired-learnings: scanned=${summary.scanned} kept=${summary.kept} ` +
|
|
444
|
-
`archived=${summary.archived} dry_run=${summary.dryRun} archive=${summary.archivePath}
|
|
453
|
+
`archived=${summary.archived} dry_run=${summary.dryRun} archive=${summary.archivePath}` +
|
|
454
|
+
// An inbox-role apply (#1516) wrote nothing; say so, as --json does.
|
|
455
|
+
`${summary.skipped ? ` skipped=${summary.skipped}` : ''}\n`
|
|
445
456
|
);
|
|
446
457
|
}
|
|
447
458
|
}
|
|
@@ -609,6 +620,39 @@ function resolveLedger(p) {
|
|
|
609
620
|
}
|
|
610
621
|
}
|
|
611
622
|
|
|
623
|
+
/**
|
|
624
|
+
* The default ledger for this cwd (#1722): `rel` verbatim unless the resolver
|
|
625
|
+
* maps it elsewhere (linked worktree that gitignores it), then that absolute path.
|
|
626
|
+
*
|
|
627
|
+
* @param {string} rel
|
|
628
|
+
* @returns {string}
|
|
629
|
+
*/
|
|
630
|
+
function routedDefault(rel) {
|
|
631
|
+
const routed = resolveLedgerPath(rel, process.cwd());
|
|
632
|
+
return routed === path.resolve(rel) ? rel : routed;
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
/**
|
|
636
|
+
* Which root a store lies inside, canonically (#1500 F4): `repoRoot` itself, or
|
|
637
|
+
* its ledger root (the main checkout of a linked worktree, #1722) — but only for
|
|
638
|
+
* a file inside the DIRECTORY of the mapped default store, never anywhere in
|
|
639
|
+
* that other checkout (a worktree session must not rewrite a foreign tree).
|
|
640
|
+
* `null` when it lies outside both.
|
|
641
|
+
*
|
|
642
|
+
* @param {string} file
|
|
643
|
+
* @param {string} repoRoot
|
|
644
|
+
* @returns {string|null}
|
|
645
|
+
*/
|
|
646
|
+
function ledgerRootOf(file, repoRoot) {
|
|
647
|
+
const abs = resolveLedger(file);
|
|
648
|
+
if (validatePathInsideProject(abs, repoRoot, { canonicalizeRoot: true }).ok) return repoRoot;
|
|
649
|
+
const storeDir = path.dirname(resolveLedgerPath(DEFAULT_FILE, repoRoot));
|
|
650
|
+
if (validatePathInsideProject(abs, storeDir, { canonicalizeRoot: true }).ok) {
|
|
651
|
+
return resolveLedgerRoot(DEFAULT_FILE, repoRoot);
|
|
652
|
+
}
|
|
653
|
+
return null;
|
|
654
|
+
}
|
|
655
|
+
|
|
612
656
|
/**
|
|
613
657
|
* Exit 1 when `--snapshot` would replace the store or the archive. The rename
|
|
614
658
|
* lands the header as the store's line 1 with no `.bak` (and drops malformed
|
|
@@ -820,7 +864,9 @@ async function runPrune(args) {
|
|
|
820
864
|
// hand-assembled `--entries` sidecar) never wrote anything, so it must not
|
|
821
865
|
// report a completed run either. Best-effort — never changes this
|
|
822
866
|
// command's exit code or stdout contract.
|
|
823
|
-
|
|
867
|
+
// An inbox-role apply (#1516) returns `skipped`: it archived nothing and did
|
|
868
|
+
// not rewrite the store, so there is no completed store write to report.
|
|
869
|
+
if (!args.dryRun && !result.skipped) {
|
|
824
870
|
await emitEvolveCompleted({
|
|
825
871
|
repoRoot: args.repoRoot,
|
|
826
872
|
appended: args.appended,
|
|
@@ -841,7 +887,7 @@ async function runPrune(args) {
|
|
|
841
887
|
process.stdout.write(
|
|
842
888
|
`sweep-expired-learnings: prune scanned=${summary.scanned} kept=${summary.kept} ` +
|
|
843
889
|
`archived=${summary.archived} by_reason=${byReason} dry_run=${summary.dryRun} ` +
|
|
844
|
-
`archive=${summary.archivePath}\n`
|
|
890
|
+
`archive=${summary.archivePath}${summary.skipped ? ` skipped=${summary.skipped}` : ''}\n`
|
|
845
891
|
);
|
|
846
892
|
}
|
|
847
893
|
}
|
|
@@ -858,7 +904,7 @@ async function runPrune(args) {
|
|
|
858
904
|
async function emitMalformedDropped(args, result) {
|
|
859
905
|
// Both sides canonical, as the parseArgs check judged them: a lexical
|
|
860
906
|
// relative() across /var vs /private/var reads `../../private/...`.
|
|
861
|
-
const root = resolveLedger(args.repoRoot);
|
|
907
|
+
const root = resolveLedger(ledgerRootOf(args.file, args.repoRoot) ?? args.repoRoot);
|
|
862
908
|
const rel = (p) => path.relative(root, resolveLedger(p));
|
|
863
909
|
try {
|
|
864
910
|
const { emitEvent, sessionAttribution } = await import('./lib/events.mjs');
|
|
@@ -916,7 +962,9 @@ async function runDropMalformed(args) {
|
|
|
916
962
|
process.exit(2);
|
|
917
963
|
}
|
|
918
964
|
|
|
919
|
-
|
|
965
|
+
// An inbox-role host (#1516) returns the dry-run probe with `skipped` set:
|
|
966
|
+
// nothing was dropped, so no event may claim it was.
|
|
967
|
+
if (!args.dryRun && !result.skipped) await emitMalformedDropped(args, result);
|
|
920
968
|
|
|
921
969
|
const summary = {
|
|
922
970
|
file: args.file,
|
|
@@ -105,6 +105,7 @@ export function parseArgs(argv) {
|
|
|
105
105
|
} else if (a === '--learnings') {
|
|
106
106
|
const raw = argv[++i];
|
|
107
107
|
if (typeof raw !== 'string' || raw.length === 0) usageError('--learnings requires a path');
|
|
108
|
+
if (path.isAbsolute(raw)) usageError('--learnings requires a repo-relative path');
|
|
108
109
|
args.learnings = raw;
|
|
109
110
|
} else if (a === '--help' || a === '-h') {
|
|
110
111
|
printHelp();
|
|
@@ -140,6 +141,7 @@ export function summarize(plan, args, applied) {
|
|
|
140
141
|
expired_entries: plan.plans.reduce((n, p) => n + p.expiredPairIds.length, 0),
|
|
141
142
|
unresolved_pairs: plan.plans.reduce((n, p) => n + p.unresolvedPairIds.length, 0),
|
|
142
143
|
malformed_lines: plan.malformedLines,
|
|
144
|
+
archive_malformed_lines: plan.archiveMalformedLines,
|
|
143
145
|
rules_dir_readable: plan.ok,
|
|
144
146
|
skipped: plan.skipped,
|
|
145
147
|
plans: plan.plans.map(({ nextContent: _nextContent, ...rest }) => rest),
|
|
@@ -178,7 +180,8 @@ async function main(argv) {
|
|
|
178
180
|
`(${summary.header_raises} header-raise(s)), ` +
|
|
179
181
|
`${summary.deletes} delete(s), ${summary.expired_entries} expired entr(ies), ` +
|
|
180
182
|
`${summary.unresolved_pairs} unresolved pair(s), ${summary.skipped.length} skipped, ` +
|
|
181
|
-
`${summary.malformed_lines} malformed learnings line(s)
|
|
183
|
+
`${summary.malformed_lines} malformed learnings line(s), ` +
|
|
184
|
+
`${summary.archive_malformed_lines} malformed archive line(s)\n`,
|
|
182
185
|
);
|
|
183
186
|
for (const s of plan.skipped) process.stdout.write(` skipped ${s.file}: ${s.reason}\n`);
|
|
184
187
|
for (const p of plan.plans) {
|
|
@@ -481,6 +481,11 @@ runCheck('check-learning-provenance.mjs');
|
|
|
481
481
|
process.stdout.write('\n');
|
|
482
482
|
runCheck('check-vcs-repo-flag.mjs');
|
|
483
483
|
|
|
484
|
+
// Separate advice contract (#1024): name the PR or drop a rejected repo flag.
|
|
485
|
+
// WARN-only, with tool errors surfaced through FAIL lines as in the bare census.
|
|
486
|
+
process.stdout.write('\n');
|
|
487
|
+
runCheck('check-vcs-repo-flag-positional.mjs');
|
|
488
|
+
|
|
484
489
|
// WARN-only (#1023): a `gh`/`glab` command cited in docs that no released CLI
|
|
485
490
|
// ever had — `glab repo edit --visibility`, `glab group list` — costs an
|
|
486
491
|
// operator a failed command and a re-derivation. The oracle is the CLI's own
|
|
@@ -539,6 +544,13 @@ if (runCheck('check-learnings-shell-writes.mjs') !== 0) checkFailed = 1;
|
|
|
539
544
|
process.stdout.write('\n');
|
|
540
545
|
runCheck('check-test-git-config-target.mjs');
|
|
541
546
|
|
|
547
|
+
// BLOCKING (#1613): a test that spawns `sh`/`bash` by bare name is red on native
|
|
548
|
+
// Windows (no sh on PATH, bash is WSL) — 301 red tests at fd8d3673. It may block
|
|
549
|
+
// because the backlog was drained in the same change: the live census leaves only
|
|
550
|
+
// three files in open MRs, baselined inside the checker as WARN.
|
|
551
|
+
process.stdout.write('\n');
|
|
552
|
+
if (runCheck('check-test-posix-shell.mjs') !== 0) checkFailed = 1;
|
|
553
|
+
|
|
542
554
|
// BLOCKING (#1183): every `emitEvent()` call site under hooks/**/*.mjs must be
|
|
543
555
|
// try/catch-guarded (emitEvent() throws EventValidationError on a malformed
|
|
544
556
|
// record — an unguarded call aborts the hook process). Five pre-existing
|
|
@@ -548,6 +560,16 @@ runCheck('check-test-git-config-target.mjs');
|
|
|
548
560
|
process.stdout.write('\n');
|
|
549
561
|
if (runCheck('check-hooks-emit-event-guard.mjs') !== 0) checkFailed = 1;
|
|
550
562
|
|
|
563
|
+
process.stdout.write('\n'); if (runCheck('check-workflow-continue-on-error.mjs') !== 0) checkFailed = 1;
|
|
564
|
+
|
|
565
|
+
// BLOCKING (#1722): a module under scripts/ or hooks/ that builds a
|
|
566
|
+
// `.orchestrator/metrics` path must route it through resolveLedgerPath()
|
|
567
|
+
// (events/sessions: resolveEventsLedgerRoot()). The ledgers are gitignored, so
|
|
568
|
+
// from a linked worktree an unrouted writer leaves a copy that is lost with the
|
|
569
|
+
// worktree. Deliberately local sites sit on the checker's own ALLOWLIST.
|
|
570
|
+
process.stdout.write('\n');
|
|
571
|
+
if (runCheck('check-metrics-ledger-root.mjs') !== 0) checkFailed = 1;
|
|
572
|
+
|
|
551
573
|
// BLOCKING (#1184): every scripts/lib/validate/check-*.mjs must be referenced
|
|
552
574
|
// by basename from this file, .husky/pre-commit, or .gitlab-ci.yml — or carry
|
|
553
575
|
// a `// registration: standalone <reason>` header marker declaring itself
|
|
@@ -31,6 +31,9 @@
|
|
|
31
31
|
*/
|
|
32
32
|
|
|
33
33
|
import { spawnSync } from 'node:child_process';
|
|
34
|
+
import { realpathSync } from 'node:fs';
|
|
35
|
+
import { homedir } from 'node:os';
|
|
36
|
+
import { sep as pathSep } from 'node:path';
|
|
34
37
|
import { getCrossRepoProjects, getConfinementRoot } from './lib/config/cross-repo.mjs';
|
|
35
38
|
import { validatePathInsideProject } from './lib/path-utils.mjs';
|
|
36
39
|
import { resolveRepoSpec, redactUrlCredentials } from './lib/vcs-repo-spec.mjs';
|
|
@@ -278,15 +281,59 @@ function buildTickBody(issueMap, depIds, verdict, streak, date) {
|
|
|
278
281
|
- Verdict: ${verdict}`;
|
|
279
282
|
}
|
|
280
283
|
|
|
284
|
+
/**
|
|
285
|
+
* Canonical spelling of an absolute path: realpath (native) when it exists, else as given.
|
|
286
|
+
* On macOS the native realpath also returns the on-disk letter case.
|
|
287
|
+
* @param {string} p
|
|
288
|
+
* @returns {string}
|
|
289
|
+
*/
|
|
290
|
+
function canonicalPath(p) {
|
|
291
|
+
try {
|
|
292
|
+
return realpathSync.native(p);
|
|
293
|
+
} catch {
|
|
294
|
+
return p;
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
/**
|
|
299
|
+
* Path of `root` relative to the home dir, or null when it is not under home (#1622).
|
|
300
|
+
*
|
|
301
|
+
* Both sides go through realpath first, so symlinks and (on macOS) a differently
|
|
302
|
+
* cased spelling of an existing dir resolve to the same string. A root that does not
|
|
303
|
+
* exist cannot be canonicalised; on darwin/win32 the default volumes are
|
|
304
|
+
* case-insensitive, so the prefix compare is case-folded there as a fallback.
|
|
305
|
+
* Ceiling: a case-sensitive APFS volume could fold two distinct non-existent paths
|
|
306
|
+
* together; the snippet would then name a dir that is not there either way.
|
|
307
|
+
* @param {string} root
|
|
308
|
+
* @returns {string|null} '' when root IS home, the relative rest when inside, else null
|
|
309
|
+
*/
|
|
310
|
+
function homeRelative(root) {
|
|
311
|
+
const home = canonicalPath(homedir());
|
|
312
|
+
const canonRoot = canonicalPath(root);
|
|
313
|
+
const fold = process.platform === 'darwin' || process.platform === 'win32';
|
|
314
|
+
const cmp = (s) => (fold ? s.toLocaleLowerCase('en-US') : s);
|
|
315
|
+
if (cmp(canonRoot) === cmp(home)) return '';
|
|
316
|
+
for (const sep of new Set(['/', pathSep])) {
|
|
317
|
+
if (cmp(canonRoot).startsWith(cmp(`${home}${sep}`))) return canonRoot.slice(home.length + 1);
|
|
318
|
+
}
|
|
319
|
+
return null;
|
|
320
|
+
}
|
|
321
|
+
|
|
281
322
|
/**
|
|
282
323
|
* Build the flip-ready trigger comment body.
|
|
283
324
|
* @param {string[]} repos
|
|
325
|
+
* @param {string} root — projects root the repo names resolve against
|
|
326
|
+
* (getConfinementRoot(), so the snippet follows `projects-root`, #1567)
|
|
284
327
|
* @returns {string}
|
|
285
328
|
*/
|
|
286
|
-
function buildFlipBody(repos) {
|
|
329
|
+
function buildFlipBody(repos, root) {
|
|
287
330
|
// Defense-in-depth: regex+confinement upstream already filter, but shell-quote here
|
|
288
331
|
// makes the embedded bash safe under any future bypass.
|
|
289
332
|
const repoList = repos.map(shellQuote).join(' ');
|
|
333
|
+
// The body is posted to a GitLab issue: a root under the home dir is written
|
|
334
|
+
// as "$HOME"/… so the comment never carries the owner's home path (#1490).
|
|
335
|
+
const rel = homeRelative(root);
|
|
336
|
+
const rootQ = rel === null ? shellQuote(root) : rel === '' ? '"$HOME"' : `"$HOME"/${shellQuote(rel)}`;
|
|
290
337
|
// The sed/add/commit one-liner targets the project-instruction file. Most
|
|
291
338
|
// ecosystem repos use CLAUDE.md, but Codex-CLI repos use AGENTS.md as a
|
|
292
339
|
// transparent alias — see skills/_shared/instruction-file-resolution.md.
|
|
@@ -296,14 +343,15 @@ function buildFlipBody(repos) {
|
|
|
296
343
|
|
|
297
344
|
Lokaler Befehl (ein-Liner pro Repo, aber besser per Session orchestriert; CLAUDE.md and AGENTS.md sind Aliase):
|
|
298
345
|
\`\`\`bash
|
|
346
|
+
root=${rootQ}
|
|
299
347
|
for d in ${repoList}; do
|
|
300
348
|
for f in CLAUDE.md AGENTS.md; do
|
|
301
|
-
[ -s
|
|
302
|
-
sed -i.bak 's/mode: warn }/mode: strict }/'
|
|
303
|
-
git -C
|
|
349
|
+
[ -s "$root/$d/$f" ] || continue
|
|
350
|
+
sed -i.bak 's/mode: warn }/mode: strict }/' "$root/$d/$f"
|
|
351
|
+
git -C "$root/$d" add "$f"
|
|
304
352
|
break
|
|
305
353
|
done
|
|
306
|
-
git -C
|
|
354
|
+
git -C "$root/$d" commit -m 'chore(orchestrator): Promote vault-integration to strict mode — closes #305'
|
|
307
355
|
done
|
|
308
356
|
\`\`\``;
|
|
309
357
|
}
|
|
@@ -402,7 +450,7 @@ async function main() {
|
|
|
402
450
|
// 7. If streak == 3 (or more), post flip-ready trigger
|
|
403
451
|
if (newStreak >= 3) {
|
|
404
452
|
log(`streak=${newStreak} >= 3 — posting flip-ready trigger`);
|
|
405
|
-
postComment(TRACKING_ISSUE, buildFlipBody(flipRepos));
|
|
453
|
+
postComment(TRACKING_ISSUE, buildFlipBody(flipRepos, watcherRoot));
|
|
406
454
|
emit({ action: 'flip-ready-posted', issue: TRACKING_ISSUE });
|
|
407
455
|
}
|
|
408
456
|
|
package/scripts/vault-mirror.mjs
CHANGED
|
@@ -798,6 +798,15 @@ async function main() {
|
|
|
798
798
|
// into `total` like any other line, so the run partition still holds. A
|
|
799
799
|
// malformed archive line is reported and skipped — the archive is secondary
|
|
800
800
|
// input and must not abort a mirror run that already wrote the live store.
|
|
801
|
+
//
|
|
802
|
+
// Boundary (BV-004, #1685): the sibling `learnings-inbox.jsonl` (#1516) is
|
|
803
|
+
// deliberately NOT a mirror source: the mirror reads only the live store (it
|
|
804
|
+
// checks no store role, and session-end runs it on every host). An inbox
|
|
805
|
+
// record gets its note on the first mirror run after it is ingested into the
|
|
806
|
+
// live store, on whichever host that run happens. Ceiling: an inbox host
|
|
807
|
+
// never mirrors its inbox-only learnings, and every note for an inbox record
|
|
808
|
+
// lags one ingest. Revisit when a real inbox host needs vault notes before
|
|
809
|
+
// ingest.
|
|
801
810
|
if (kind === 'learning' && basename(resolve(source)) === 'learnings.jsonl') {
|
|
802
811
|
const archivePath = join(dirname(resolve(source)), 'learnings-archive.jsonl');
|
|
803
812
|
if (existsSync(archivePath)) {
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* vault-orphans.mjs - CLI for `findVaultOrphans` (#1563, session-end Phase 3.7e).
|
|
4
|
+
*
|
|
5
|
+
* Names the untracked files in the Meta-Vault modified since a given instant
|
|
6
|
+
* (the session's start). Read-only: it never commits, moves or deletes anything
|
|
7
|
+
* in the vault. See scripts/lib/session-end/vault-orphans.mjs for the design.
|
|
8
|
+
*
|
|
9
|
+
* USAGE
|
|
10
|
+
* node scripts/vault-orphans.mjs --since <ISO> [--vault-dir <dir>] [--json]
|
|
11
|
+
*
|
|
12
|
+
* EXIT CODES
|
|
13
|
+
* 0 measured (including zero candidates)
|
|
14
|
+
* 1 bad arguments
|
|
15
|
+
* 2 could not measure (no vault dir resolvable, not a git repo, git failed)
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
19
|
+
import { join } from 'node:path';
|
|
20
|
+
import { parseArgs } from 'node:util';
|
|
21
|
+
|
|
22
|
+
import { findVaultOrphans } from './lib/session-end/vault-orphans.mjs';
|
|
23
|
+
import { _parseVaultIntegration } from './lib/config/vault-integration.mjs';
|
|
24
|
+
import { loadHostPaths } from './lib/config/host-paths.mjs';
|
|
25
|
+
import { resolveVaultDir } from './lib/config.mjs';
|
|
26
|
+
import { expandTilde } from './lib/common.mjs';
|
|
27
|
+
import { isMainModule } from './lib/is-main-module.mjs';
|
|
28
|
+
|
|
29
|
+
const HELP = `vault-orphans.mjs - name untracked vault files written since a session started (#1563)
|
|
30
|
+
|
|
31
|
+
USAGE
|
|
32
|
+
node scripts/vault-orphans.mjs --since <ISO> [--vault-dir <dir>] [--json]
|
|
33
|
+
|
|
34
|
+
DESCRIPTION
|
|
35
|
+
Runs the read-only \`git -C <vault-dir> ls-files --others --exclude-standard\`
|
|
36
|
+
and lists every untracked file whose mtime is at or after --since. Files whose
|
|
37
|
+
frontmatter names one of this plugin's generators (\`_generator:
|
|
38
|
+
session-orchestrator-...\`) are left out: their writer owns them.
|
|
39
|
+
|
|
40
|
+
The results are CANDIDATES: mtime is a hint, not proof that this session
|
|
41
|
+
wrote the file. Nothing is committed, moved or deleted.
|
|
42
|
+
|
|
43
|
+
OPTIONS
|
|
44
|
+
--since <ISO> Required. ISO 8601 instant with Z or an offset, e.g. the
|
|
45
|
+
STATE.md \`started_at\` (2026-10-11T08:00:00.000Z).
|
|
46
|
+
--vault-dir <dir> Vault working tree. Default: the host-local resolution
|
|
47
|
+
SO_VAULT_DIR > owner.yaml > committed
|
|
48
|
+
vault-integration.vault-dir in ./CLAUDE.md or ./AGENTS.md.
|
|
49
|
+
--json Print the result object as JSON on stdout.
|
|
50
|
+
--help Show this help.
|
|
51
|
+
--version Print the plugin version.
|
|
52
|
+
|
|
53
|
+
EXIT CODES
|
|
54
|
+
0 measured, including zero candidates
|
|
55
|
+
1 bad arguments
|
|
56
|
+
2 could not measure (no vault dir, not a git repo, git failed)
|
|
57
|
+
|
|
58
|
+
EXAMPLE
|
|
59
|
+
node scripts/vault-orphans.mjs --since 2026-10-11T08:00:00.000Z --json
|
|
60
|
+
`;
|
|
61
|
+
|
|
62
|
+
/** ISO 8601 with an explicit zone - a zone-less value would be read as local time. */
|
|
63
|
+
const ISO_RE = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}(?::\d{2}(?:\.\d+)?)?(?:Z|[+-]\d{2}:\d{2})$/;
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Resolve the vault dir like the other vault writers: SO_VAULT_DIR > owner.yaml
|
|
67
|
+
* `vault-dirs:` cwd match > owner.yaml `paths.vault-dir` > committed value.
|
|
68
|
+
*
|
|
69
|
+
* @returns {string|null}
|
|
70
|
+
*/
|
|
71
|
+
function resolveDefaultVaultDir() {
|
|
72
|
+
let content = '';
|
|
73
|
+
for (const name of ['CLAUDE.md', 'AGENTS.md']) {
|
|
74
|
+
const p = join(process.cwd(), name);
|
|
75
|
+
if (existsSync(p)) {
|
|
76
|
+
try {
|
|
77
|
+
content = readFileSync(p, 'utf8');
|
|
78
|
+
break;
|
|
79
|
+
} catch {
|
|
80
|
+
/* try the next file */
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
const vi = _parseVaultIntegration(content);
|
|
85
|
+
const value = resolveVaultDir(vi['vault-dir'], loadHostPaths()).value;
|
|
86
|
+
return typeof value === 'string' && value.trim() !== '' ? expandTilde(value) : null;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/** @param {{status: 'ok', candidates: Array<{path: string}>, total: number, truncated: boolean, untracked: number}} r */
|
|
90
|
+
function renderHuman(r, since) {
|
|
91
|
+
const lines = [];
|
|
92
|
+
if (r.total === 0) {
|
|
93
|
+
lines.push(`vault-orphans: no untracked vault file modified since ${since} (${r.untracked} untracked in total)`);
|
|
94
|
+
} else {
|
|
95
|
+
lines.push(
|
|
96
|
+
`vault-orphans: ${r.total} untracked vault file(s) modified since ${since} - candidates only, ` +
|
|
97
|
+
`mtime does not prove this session wrote them (${r.untracked} untracked in total)`,
|
|
98
|
+
);
|
|
99
|
+
for (const c of r.candidates) lines.push(` ${c.path}`);
|
|
100
|
+
if (r.truncated) lines.push(` ... showing ${r.candidates.length} of ${r.total}`);
|
|
101
|
+
}
|
|
102
|
+
return `${lines.join('\n')}\n`;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* CLI entry.
|
|
107
|
+
*
|
|
108
|
+
* @param {string[]} [argv]
|
|
109
|
+
* @param {{stdout: {write: Function}, stderr: {write: Function}}} [io]
|
|
110
|
+
* @returns {number} exit code
|
|
111
|
+
*/
|
|
112
|
+
export function main(argv = process.argv.slice(2), io = { stdout: process.stdout, stderr: process.stderr }) {
|
|
113
|
+
let values;
|
|
114
|
+
try {
|
|
115
|
+
({ values } = parseArgs({
|
|
116
|
+
args: argv,
|
|
117
|
+
options: {
|
|
118
|
+
since: { type: 'string' },
|
|
119
|
+
'vault-dir': { type: 'string' },
|
|
120
|
+
json: { type: 'boolean', default: false },
|
|
121
|
+
help: { type: 'boolean', default: false },
|
|
122
|
+
version: { type: 'boolean', default: false },
|
|
123
|
+
},
|
|
124
|
+
strict: true,
|
|
125
|
+
allowPositionals: false,
|
|
126
|
+
}));
|
|
127
|
+
} catch (err) {
|
|
128
|
+
io.stderr.write(`vault-orphans: ${err.message}\nRun with --help for usage.\n`);
|
|
129
|
+
return 1;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
if (values.help) {
|
|
133
|
+
io.stdout.write(HELP);
|
|
134
|
+
return 0;
|
|
135
|
+
}
|
|
136
|
+
if (values.version) {
|
|
137
|
+
const pkg = JSON.parse(readFileSync(new URL('../package.json', import.meta.url), 'utf8'));
|
|
138
|
+
io.stdout.write(`${pkg.version}\n`);
|
|
139
|
+
return 0;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
const since = values.since;
|
|
143
|
+
const sinceMs = typeof since === 'string' && ISO_RE.test(since) ? Date.parse(since) : NaN;
|
|
144
|
+
if (!Number.isFinite(sinceMs)) {
|
|
145
|
+
io.stderr.write('vault-orphans: --since <ISO> is required, e.g. 2026-10-11T08:00:00.000Z\n');
|
|
146
|
+
return 1;
|
|
147
|
+
}
|
|
148
|
+
if (values['vault-dir'] !== undefined && values['vault-dir'].trim() === '') {
|
|
149
|
+
io.stderr.write('vault-orphans: --vault-dir must not be empty\n');
|
|
150
|
+
return 1;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
const vaultDir =
|
|
154
|
+
values['vault-dir'] !== undefined ? expandTilde(values['vault-dir']) : resolveDefaultVaultDir();
|
|
155
|
+
const result = findVaultOrphans({ vaultDir, sinceMs });
|
|
156
|
+
|
|
157
|
+
if (result.status !== 'ok') {
|
|
158
|
+
io.stderr.write(
|
|
159
|
+
`vault-orphans: could not measure (${result.reason}${result.detail ? `: ${result.detail}` : ''})\n`,
|
|
160
|
+
);
|
|
161
|
+
if (values.json) io.stdout.write(`${JSON.stringify(result)}\n`);
|
|
162
|
+
return 2;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
io.stdout.write(values.json ? `${JSON.stringify(result)}\n` : renderHuman(result, since));
|
|
166
|
+
return 0;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
if (isMainModule(import.meta.url)) {
|
|
170
|
+
process.exitCode = main();
|
|
171
|
+
}
|