session-orchestrator 3.24.0 → 4.0.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 +18 -0
- package/.agents/skills/autopilot/SKILL.md +17 -0
- package/.agents/skills/bootstrap/SKILL.md +20 -0
- package/.agents/skills/brainstorm/SKILL.md +22 -0
- package/.agents/skills/claude-md-drift-check/SKILL.md +15 -0
- package/.agents/skills/convergence-monitoring/SKILL.md +22 -0
- package/.agents/skills/debug/SKILL.md +22 -0
- package/.agents/skills/discovery/SKILL.md +20 -0
- package/.agents/skills/dispatcher/SKILL.md +15 -0
- package/.agents/skills/docs-orchestrator/SKILL.md +18 -0
- package/.agents/skills/ecosystem-health/SKILL.md +20 -0
- package/.agents/skills/eli5/SKILL.md +20 -0
- package/.agents/skills/eval/SKILL.md +21 -0
- package/.agents/skills/evolve/SKILL.md +21 -0
- package/.agents/skills/frontmatter-guard/SKILL.md +15 -0
- package/.agents/skills/gitlab-ops/SKILL.md +20 -0
- package/.agents/skills/gitlab-portfolio/SKILL.md +15 -0
- package/.agents/skills/grill/SKILL.md +22 -0
- package/.agents/skills/hook-development/SKILL.md +15 -0
- package/.agents/skills/mcp-builder/SKILL.md +15 -0
- package/.agents/skills/memory-cleanup/SKILL.md +21 -0
- package/.agents/skills/mode-selector/SKILL.md +17 -0
- package/.agents/skills/npm-publish/SKILL.md +16 -0
- package/.agents/skills/peekaboo-driver/SKILL.md +18 -0
- package/.agents/skills/persona-panel/SKILL.md +17 -0
- package/.agents/skills/plan/SKILL.md +20 -0
- package/.agents/skills/playwright-driver/SKILL.md +20 -0
- package/.agents/skills/quality-gates/SKILL.md +20 -0
- package/.agents/skills/reconcile/SKILL.md +21 -0
- package/.agents/skills/remote-offload/SKILL.md +20 -0
- package/.agents/skills/repo-audit/SKILL.md +16 -0
- package/.agents/skills/session-end/SKILL.md +20 -0
- package/.agents/skills/session-plan/SKILL.md +20 -0
- package/.agents/skills/session-start/SKILL.md +20 -0
- package/.agents/skills/spinout/SKILL.md +16 -0
- package/.agents/skills/sunset-review/SKILL.md +16 -0
- package/.agents/skills/test-runner/SKILL.md +20 -0
- package/.agents/skills/tmux-layout/SKILL.md +21 -0
- package/.agents/skills/using-orchestrator/SKILL.md +17 -0
- package/.agents/skills/vault-mirror/SKILL.md +15 -0
- package/.agents/skills/vault-sync/SKILL.md +15 -0
- package/.agents/skills/wave-executor/SKILL.md +20 -0
- package/.agents/skills/write-executable-plan/SKILL.md +22 -0
- package/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/.codex-plugin/plugin.json +1 -1
- package/.cursor/commands/autopilot.md +2 -2
- package/.cursor/commands/bootstrap.md +1 -1
- package/.cursor/commands/brainstorm.md +1 -1
- package/.cursor/commands/debug.md +1 -1
- package/.cursor/commands/discovery.md +1 -1
- package/.cursor/commands/dispatcher.md +2 -2
- package/.cursor/commands/eli5.md +2 -2
- package/.cursor/commands/eval.md +2 -2
- package/.cursor/commands/evolve.md +1 -1
- package/.cursor/commands/go.md +1 -1
- package/.cursor/commands/grill.md +2 -2
- package/.cursor/commands/memory-cleanup.md +2 -2
- package/.cursor/commands/persona-panel.md +1 -1
- package/.cursor/commands/plan.md +1 -1
- package/.cursor/commands/portfolio.md +1 -1
- package/.cursor/commands/reconcile.md +2 -2
- package/.cursor/commands/release.md +2 -2
- package/.cursor/commands/session.md +2 -2
- package/.cursor/commands/spinout.md +2 -2
- package/.cursor/commands/sunset-review.md +2 -2
- package/.cursor/commands/templates-ack.md +2 -2
- package/.cursor/commands/test.md +2 -2
- package/.cursor/skills/brainstorm/SKILL.md +1 -1
- package/.cursor/skills/eval/SKILL.md +1 -1
- package/.cursor/skills/quality-gates/SKILL.md +1 -1
- package/.cursor/skills/remote-offload/SKILL.md +1 -1
- package/.orchestrator/policy/blocked-commands.json +121 -0
- package/.orchestrator/policy/ecosystem.schema.json +66 -0
- package/.orchestrator/policy/quality-gates.example.json +16 -0
- package/.orchestrator/policy/quality-gates.schema.json +38 -0
- package/.orchestrator/policy/templates-policy.json +27 -0
- package/.orchestrator/policy/test-profiles.json +47 -0
- package/AGENTS.md +225 -0
- package/CHANGELOG.md +1125 -2
- package/NOTICE +11 -6
- package/README.md +127 -94
- package/agents/eval-judge.md +1 -1
- package/agents/skill-applied-judge.md +1 -1
- package/assets/wave-lifecycle.svg +98 -0
- package/commands/release.md +6 -3
- package/commands/session.md +18 -3
- package/docs/README.md +4 -0
- package/{agents/AGENTS.md → docs/agent-authoring.md} +19 -26
- package/docs/baseline.md +67 -0
- package/docs/ci-setup.md +108 -62
- package/docs/codex-setup.md +65 -21
- package/docs/components.md +36 -15
- package/docs/cursor-setup.md +6 -2
- package/docs/events-schema.md +9 -6
- package/docs/instruction-delivery.md +62 -0
- package/{agents/memory-proposal-collector.md → docs/memory-proposal-flow.md} +1 -8
- package/docs/migration-v4.md +341 -0
- package/docs/pi-setup.md +6 -1
- package/docs/plugin-architecture-v3.md +1 -1
- package/docs/rule-authoring.md +85 -19
- package/docs/scope-collision-guard.md +5 -5
- package/docs/session-config-reference.md +57 -56
- package/docs/session-config-template.md +6 -29
- package/docs/telemetry.md +157 -3
- package/docs/vault-docs-architecture.md +50 -11
- package/hooks/_lib/hook-import-set.json +1487 -0
- package/hooks/_lib/subagent-transcript.mjs +562 -0
- package/hooks/config-protection.mjs +2 -2
- package/hooks/cwd-change-restore.mjs +2 -2
- package/hooks/enforce-commands.mjs +69 -0
- package/hooks/hooks-codex.json +1 -1
- package/hooks/hooks-cursor.json +10 -0
- package/hooks/hooks-pi.json +5 -0
- package/hooks/hooks.json +6 -1
- package/hooks/loop-guard.mjs +3 -3
- package/hooks/on-session-end.mjs +2 -2
- package/hooks/on-session-start.mjs +103 -2
- package/hooks/on-stop.mjs +36 -11
- package/hooks/operator-steer.mjs +2 -2
- package/hooks/post-bash-write-verify.mjs +85 -0
- package/hooks/post-edit-import-probe.mjs +344 -0
- package/hooks/post-subagent-discovery-validator.mjs +187 -431
- package/hooks/post-tool-batch-wave-signal.mjs +118 -4
- package/hooks/post-tool-failure-corrective-context.mjs +2 -2
- package/hooks/post-tooluse-frontend-slop.mjs +3 -3
- package/hooks/pre-bash-destructive-guard.mjs +39 -13
- package/hooks/skill-invocation-telemetry.mjs +17 -5
- package/hooks/subagent-telemetry.mjs +13 -4
- package/monitors/monitors.json +3 -3
- package/package.json +9 -1
- package/pi/prompts/session.md +2 -2
- package/plugin.json +27 -0
- package/scripts/backfill-abandoned-sessions.mjs +50 -4
- package/scripts/backfill-learnings-from-vault.mjs +9 -3
- package/scripts/dialectic-deriver.mjs +73 -8
- package/scripts/export-hw-learnings.mjs +113 -1
- package/scripts/generate-agents-skills.mjs +378 -0
- package/scripts/generate-cursor-adapter.mjs +45 -8
- package/scripts/generate-hook-import-set.mjs +249 -0
- package/scripts/lib/agent-status.mjs +13 -2
- package/scripts/lib/auto-dream.mjs +38 -36
- package/scripts/lib/autonomy/suitability.mjs +6 -0
- package/scripts/lib/autopilot/loop.mjs +2 -2
- package/scripts/lib/ci-status-banner.mjs +220 -75
- package/scripts/lib/codex/plugin-contract.mjs +82 -6
- package/scripts/lib/config/auto-dream.mjs +2 -1
- package/scripts/lib/config/block-header.mjs +8 -0
- package/scripts/lib/config/block-preprocess.mjs +177 -0
- package/scripts/lib/config/broken-window.mjs +2 -1
- package/scripts/lib/config/cold-start.mjs +2 -1
- package/scripts/lib/config/config-protection.mjs +22 -2
- package/scripts/lib/config/context-coverage.mjs +2 -1
- package/scripts/lib/config/cross-repo.mjs +2 -1
- package/scripts/lib/config/custom-phases.mjs +2 -1
- package/scripts/lib/config/dialectic.mjs +2 -1
- package/scripts/lib/config/discovery-validator.mjs +2 -1
- package/scripts/lib/config/dispatcher-autonomy-capture.mjs +24 -1
- package/scripts/lib/config/dispatcher-autonomy.mjs +2 -1
- package/scripts/lib/config/docs-orchestrator.mjs +2 -1
- package/scripts/lib/config/docs-staleness.mjs +2 -1
- package/scripts/lib/config/drift-check.mjs +2 -1
- package/scripts/lib/config/eval.mjs +2 -1
- package/scripts/lib/config/events-rotation.mjs +2 -1
- package/scripts/lib/config/evolve.mjs +8 -2
- package/scripts/lib/config/frontend-slop-hook.mjs +7 -3
- package/scripts/lib/config/gitlab-portfolio.mjs +2 -1
- package/scripts/lib/config/handover-gate.mjs +2 -1
- package/scripts/lib/config/health-endpoints.mjs +7 -2
- package/scripts/lib/config/issue-budget.mjs +2 -1
- package/scripts/lib/config/loop-guard.mjs +2 -1
- package/scripts/lib/config/memory.mjs +2 -1
- package/scripts/lib/config/moc-staleness.mjs +2 -1
- package/scripts/lib/config/persona-gate-wave.mjs +2 -1
- package/scripts/lib/config/private-config-dir.mjs +67 -0
- package/scripts/lib/config/reconcile.mjs +2 -1
- package/scripts/lib/config/remote-hosts.mjs +2 -1
- package/scripts/lib/config/section-extractor.mjs +7 -1
- package/scripts/lib/config/skill-evolution.mjs +2 -1
- package/scripts/lib/config/slopcheck.mjs +2 -1
- package/scripts/lib/config/state-md-lock.mjs +2 -1
- package/scripts/lib/config/templates-first.mjs +2 -1
- package/scripts/lib/config/test.mjs +2 -1
- package/scripts/lib/config/vault-integration.mjs +7 -1
- package/scripts/lib/config/vault-mirror-quality.mjs +2 -1
- package/scripts/lib/config/vault-staleness.mjs +2 -1
- package/scripts/lib/config/vault-sync.mjs +2 -1
- package/scripts/lib/config/verification-auto-fix.mjs +2 -1
- package/scripts/lib/config/wave-reviewers.mjs +2 -1
- package/scripts/lib/config/worktree-orphans.mjs +2 -1
- package/scripts/lib/convergence-monitor.mjs +82 -16
- package/scripts/lib/dispatcher/rank.mjs +124 -48
- package/scripts/lib/ecosystem-health.mjs +16 -2
- package/scripts/lib/eval/engine.mjs +9 -1
- package/scripts/lib/eval/session-resolve.mjs +23 -4
- package/scripts/lib/events.mjs +22 -6
- package/scripts/lib/frontmatter-guard.mjs +131 -13
- package/scripts/lib/gates/gate-full.mjs +26 -0
- package/scripts/lib/gates/gate-helpers.mjs +76 -0
- package/scripts/lib/hardware-pattern-detector.mjs +18 -1
- package/scripts/lib/harness-audit/categories/category4.mjs +31 -11
- package/scripts/lib/host-identity.mjs +50 -11
- package/scripts/lib/instruction-budget-guard.mjs +171 -5
- package/scripts/lib/learnings/evolve-telemetry.mjs +178 -0
- package/scripts/lib/learnings/io.mjs +60 -6
- package/scripts/lib/memory-proposals/store.mjs +30 -22
- package/scripts/lib/owner-config-banner.mjs +43 -6
- package/scripts/lib/owner-config-loader.mjs +21 -10
- package/scripts/lib/owner-interview.mjs +3 -3
- package/scripts/lib/owner-yaml.mjs +207 -14
- package/scripts/lib/platform.mjs +108 -15
- package/scripts/lib/plugin-update-banner.mjs +406 -0
- package/scripts/lib/project-hygiene.mjs +38 -2
- package/scripts/lib/qg-command-drift-banner.mjs +50 -12
- package/scripts/lib/quality-gate.mjs +133 -44
- package/scripts/lib/reconcile/emitter.mjs +68 -6
- package/scripts/lib/reconcile/engine.mjs +13 -4
- package/scripts/lib/reconcile/idempotency.mjs +37 -4
- package/scripts/lib/reconcile/writer.mjs +40 -18
- package/scripts/lib/session-close-backfill.mjs +67 -9
- package/scripts/lib/session-id.mjs +12 -23
- package/scripts/lib/session-identity/own-session.mjs +125 -10
- package/scripts/lib/session-lock-shape.mjs +43 -0
- package/scripts/lib/session-lock.mjs +5 -10
- package/scripts/lib/session-registry.mjs +25 -9
- package/scripts/lib/session-schema/constants.mjs +36 -2
- package/scripts/lib/session-schema/validator.mjs +38 -4
- package/scripts/lib/session-start-probes.mjs +18 -1
- package/scripts/lib/sessions-staleness-banner.mjs +18 -11
- package/scripts/lib/skill-health/join.mjs +17 -4
- package/scripts/lib/state-md.mjs +78 -0
- package/scripts/lib/sunset/walker.mjs +6 -0
- package/scripts/lib/telemetry/schema.mjs +181 -9
- package/scripts/lib/telemetry/sync.mjs +368 -12
- package/scripts/lib/validate/check-agents-skills.mjs +327 -0
- package/scripts/lib/validate/check-agents.mjs +3 -3
- package/scripts/lib/validate/check-cursor-adapter.mjs +234 -72
- package/scripts/lib/validate/check-hooks-symmetry.mjs +45 -16
- package/scripts/lib/validate/check-owner-leakage.mjs +281 -20
- package/scripts/lib/validate/check-skill-links.mjs +163 -0
- package/scripts/lib/validate/check-skill-script-paths.mjs +47 -28
- package/scripts/lib/validate/check-unwired-features.mjs +0 -2
- package/scripts/lib/validate/check-validator-registration.mjs +10 -4
- package/scripts/lib/validate/enumerate-repo-files.mjs +317 -0
- package/scripts/lib/vault-backfill/template.mjs +63 -6
- package/scripts/lib/vault-mirror/process.mjs +165 -42
- package/scripts/lib/vault-mirror/telemetry.mjs +2 -2
- package/scripts/lib/vault-status/narrative-mirror.mjs +127 -18
- package/scripts/lib/wave-executor/dispatch-common.mjs +164 -0
- package/scripts/lib/wave-executor/foreign-dispatch.mjs +7 -142
- package/scripts/lib/wave-executor/remote-dispatch.mjs +5 -7
- package/scripts/lib/wave-resource-gate.mjs +8 -2
- package/scripts/lib/wave-sizing.mjs +4 -1
- package/scripts/lib/wave-transcript-tail.mjs +118 -4
- package/scripts/materialize-wave-scope.mjs +12 -5
- package/scripts/memory-propose.mjs +19 -5
- package/scripts/migrate-cold-start-seed.mjs +4 -1
- package/scripts/parse-config.mjs +60 -3
- package/scripts/release.mjs +337 -29
- package/scripts/repair-invalid-sessions.mjs +3 -3
- package/scripts/run-quality-gate.mjs +128 -11
- package/scripts/sweep-expired-learnings.mjs +90 -0
- package/scripts/sync-vault-schema.mjs +3 -1
- package/scripts/telemetry.mjs +2 -2
- package/scripts/validate-plugin.mjs +161 -0
- package/scripts/validate-wave-scope.mjs +28 -8
- package/scripts/wave-scope-binding.mjs +215 -0
- package/skills/_shared/instruction-file-resolution.md +10 -0
- package/skills/_shared/parallel-aware-preamble.md +1 -0
- package/skills/_shared/platform-tools.md +1 -1
- package/skills/_shared/state-ownership.md +1 -1
- package/skills/architecture/SKILL.md +7 -5
- package/skills/{domain-model/SKILL.md → architecture/references/domain-model.md} +9 -9
- package/skills/autopilot/SKILL.md +4 -18
- package/skills/claude-md-drift-check/SKILL.md +5 -1
- package/skills/claude-md-drift-check/checker.mjs +62 -2
- package/skills/convergence-monitoring/SIGNALS.md +55 -0
- package/skills/discovery/probes/vault-staleness.mjs +37 -13
- package/skills/discovery/probes-arch.md +20 -18
- package/skills/dispatcher/SKILL.md +3 -2
- package/skills/evolve/SKILL.md +65 -26
- package/skills/frontmatter-guard/SKILL.md +11 -5
- package/skills/npm-publish/SKILL.md +1 -1
- package/skills/reconcile/SKILL.md +33 -0
- package/skills/remote-offload/SKILL.md +1 -1
- package/skills/session-end/SKILL.md +18 -905
- package/skills/session-end/phase-3-6-tail.md +10 -3
- package/skills/session-end/plan-verification.md +221 -155
- package/skills/session-end/references/phase-2-quality-gate.md +93 -0
- package/skills/session-end/references/phase-3-documentation-updates.md +229 -0
- package/skills/session-end/references/phase-4a-worktree-cleanup.md +120 -0
- package/skills/session-end/references/phase-4b-worktree-orphan-sweep.md +58 -0
- package/skills/session-end/references/phase-5-issue-cleanup.md +104 -0
- package/skills/session-end/references/session-summary-template.md +62 -0
- package/skills/session-plan/SKILL.md +49 -0
- package/skills/session-start/SKILL.md +22 -904
- package/skills/session-start/phase-8-5-express-path.md +1 -1
- package/skills/session-start/references/phase-1-1-dispatcher-autonomy-capture.md +55 -0
- package/skills/session-start/references/phase-1-2-session-lock.md +140 -0
- package/skills/session-start/references/phase-1-5-session-continuity.md +254 -0
- package/skills/session-start/references/phase-1-7-vault-status-board.md +53 -0
- package/skills/session-start/references/phase-2-7-portfolio-snapshot.md +75 -0
- package/skills/session-start/references/phase-4-ssot-environment-check.md +155 -0
- package/skills/session-start/references/phase-6-5-forced-reads.md +75 -0
- package/skills/session-start/references/phase-6-6-project-intelligence.md +81 -0
- package/skills/session-start/references/phase-6-7-memory-banner-telemetry-consent.md +103 -0
- package/skills/vault-sync/validator.mjs +21 -27
- package/skills/wave-executor/SKILL.md +15 -1
- package/skills/wave-executor/references/wave-loop-dispatch.md +612 -0
- package/skills/wave-executor/references/wave-loop-review.md +570 -0
- package/skills/wave-executor/references/wave-loop-scope-manifest.md +162 -0
- package/skills/wave-executor/wave-loop.md +14 -1309
- package/templates/_shared/journey-manifest.md +10 -6
- package/.cursor/commands/autopilot-multi.md +0 -14
- package/.cursor/commands/contract-version-bump.md +0 -14
- package/.cursor/commands/journey-audit.md +0 -14
- package/.cursor/skills/contract-version-bump/SKILL.md +0 -12
- package/.cursor/skills/daily/SKILL.md +0 -12
- package/.cursor/skills/domain-model/SKILL.md +0 -13
- package/.cursor/skills/journey-audit/SKILL.md +0 -13
- package/.cursor/skills/skill-creator/SKILL.md +0 -13
- package/.cursor/skills/ubiquitous-language/SKILL.md +0 -13
- package/commands/autopilot-multi.md +0 -74
- package/commands/contract-version-bump.md +0 -28
- package/commands/journey-audit.md +0 -43
- package/pi/prompts/autopilot-multi.md +0 -12
- package/pi/prompts/contract-version-bump.md +0 -12
- package/pi/prompts/journey-audit.md +0 -12
- package/scripts/autopilot-multi.mjs +0 -885
- package/scripts/backfill-learnings-expires.mjs +0 -196
- package/scripts/backfill-learnings.mjs +0 -203
- package/scripts/fleet-instruction-scan.mjs +0 -141
- package/scripts/lib/autopilot/dep-graph.mjs +0 -417
- package/scripts/lib/autopilot/multi-killswitch.mjs +0 -184
- package/scripts/lib/webhook-url.mjs +0 -105
- package/scripts/lifecycle-sim-v6.mjs +0 -347
- package/scripts/migrate-learnings-jsonl.mjs +0 -189
- package/scripts/migrate-subagents-jsonl.mjs +0 -196
- package/scripts/upload-social-preview.mjs +0 -316
- package/skills/_shared/model-selection.md +0 -64
- package/skills/contract-version-bump/SKILL.md +0 -219
- package/skills/daily/SKILL.md +0 -222
- package/skills/daily/generate.sh +0 -92
- package/skills/daily/templates/daily.md.tpl +0 -36
- package/skills/journey-audit/SKILL.md +0 -270
- package/skills/skill-creator/SKILL.md +0 -168
- package/skills/ubiquitous-language/SKILL.md +0 -97
- package/skills/vault-sync/package-lock.json +0 -40
- /package/skills/{domain-model → architecture/references}/ADR-FORMAT.md +0 -0
- /package/skills/{domain-model → architecture/references}/CONTEXT-FORMAT.md +0 -0
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* generate-hook-import-set.mjs — build the committed allowlist of modules that
|
|
4
|
+
* are transitively reachable from a hook entry file (GitLab #1224).
|
|
5
|
+
*
|
|
6
|
+
* WHY: a hook entry file imports a helper module on EVERY tool call. A helper
|
|
7
|
+
* saved in a broken intermediate state (2026-09-04: a call to a function that
|
|
8
|
+
* did not exist yet) turns every Bash/Edit/Write call into "Internal hook error
|
|
9
|
+
* — request blocked", host-wide, for every session sharing the working copy.
|
|
10
|
+
* `hooks/post-edit-import-probe.mjs` checks a file right after it is edited —
|
|
11
|
+
* but only when the file is in THIS set, so the probe stays cheap.
|
|
12
|
+
*
|
|
13
|
+
* The set is a COMMITTED artefact (`hooks/_lib/hook-import-set.json`) rather
|
|
14
|
+
* than a runtime crawl: the probe runs inside a 5 s PostToolUse budget and must
|
|
15
|
+
* not walk 142 modules per edit. `--check` re-generates and diffs, which is the
|
|
16
|
+
* drift guard that keeps the committed copy honest.
|
|
17
|
+
*
|
|
18
|
+
* Usage:
|
|
19
|
+
* node scripts/generate-hook-import-set.mjs [--plugin-root <dir>] [--out <file>] [--check]
|
|
20
|
+
*
|
|
21
|
+
* Exit codes: 0 = written / in sync; 1 = drift (with --check) or usage error.
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
import { readFileSync, writeFileSync, existsSync, statSync, realpathSync } from 'node:fs';
|
|
25
|
+
import path from 'node:path';
|
|
26
|
+
import { fileURLToPath } from 'node:url';
|
|
27
|
+
import { execFileSync } from 'node:child_process';
|
|
28
|
+
|
|
29
|
+
/** The four hook manifests whose entry files seed the crawl. */
|
|
30
|
+
export const HOOK_MANIFESTS = [
|
|
31
|
+
'hooks.json',
|
|
32
|
+
'hooks-codex.json',
|
|
33
|
+
'hooks-cursor.json',
|
|
34
|
+
'hooks-pi.json',
|
|
35
|
+
];
|
|
36
|
+
|
|
37
|
+
/** Module extensions the crawl resolves an extension-less specifier against. */
|
|
38
|
+
const RESOLVE_EXTS = ['.mjs', '.js', '.cjs'];
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Collect every `<something>.mjs` path mentioned in a hook manifest's command
|
|
42
|
+
* strings. The manifests differ per platform ($CLAUDE_PLUGIN_ROOT /
|
|
43
|
+
* $PI_PLUGIN_ROOT / ${PLUGIN_ROOT}), so match on the hooks/-relative tail
|
|
44
|
+
* rather than on any one variable spelling.
|
|
45
|
+
*
|
|
46
|
+
* @param {string} json - raw manifest contents
|
|
47
|
+
* @returns {string[]} plugin-root-relative entry paths (e.g. "hooks/enforce-scope.mjs")
|
|
48
|
+
*/
|
|
49
|
+
export function extractEntryFiles(json) {
|
|
50
|
+
const out = new Set();
|
|
51
|
+
for (const m of json.matchAll(/hooks\/((?:[\w.-]+\/)*[\w.-]+\.mjs)/g)) {
|
|
52
|
+
const rel = `hooks/${m[1]}`;
|
|
53
|
+
// run-node.sh is the launcher, never an entry module; .../hooks/_lib/* is
|
|
54
|
+
// reached through imports, not through a manifest command.
|
|
55
|
+
out.add(rel);
|
|
56
|
+
}
|
|
57
|
+
return [...out].sort();
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Extract relative module specifiers (static import/export-from and dynamic
|
|
62
|
+
* `import()`) from a module's source.
|
|
63
|
+
*
|
|
64
|
+
* @param {string} src
|
|
65
|
+
* @returns {string[]} relative specifiers, e.g. ["./_lib/profile-gate.mjs"]
|
|
66
|
+
*/
|
|
67
|
+
export function extractRelativeSpecifiers(src) {
|
|
68
|
+
const out = new Set();
|
|
69
|
+
const patterns = [
|
|
70
|
+
/(?:^|[\s;}])(?:import|export)\s[^;'"]*?\sfrom\s*['"](\.[^'"]+)['"]/g,
|
|
71
|
+
/(?:^|[\s;}])import\s*['"](\.[^'"]+)['"]/g,
|
|
72
|
+
/\bimport\s*\(\s*['"](\.[^'"]+)['"]\s*\)/g,
|
|
73
|
+
];
|
|
74
|
+
for (const re of patterns) {
|
|
75
|
+
for (const m of src.matchAll(re)) out.add(m[1]);
|
|
76
|
+
}
|
|
77
|
+
return [...out];
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Resolve a relative specifier against the importing file.
|
|
82
|
+
*
|
|
83
|
+
* @param {string} fromFile - absolute path of the importing module
|
|
84
|
+
* @param {string} spec - relative specifier
|
|
85
|
+
* @returns {string|null} absolute path of an existing file, or null
|
|
86
|
+
*/
|
|
87
|
+
function resolveSpecifier(fromFile, spec) {
|
|
88
|
+
const base = path.resolve(path.dirname(fromFile), spec);
|
|
89
|
+
const candidates = [base, ...RESOLVE_EXTS.map((e) => base + e),
|
|
90
|
+
...RESOLVE_EXTS.map((e) => path.join(base, `index${e}`))];
|
|
91
|
+
for (const c of candidates) {
|
|
92
|
+
try {
|
|
93
|
+
if (existsSync(c) && statSync(c).isFile()) return c;
|
|
94
|
+
} catch { /* unreadable → not a resolution */ }
|
|
95
|
+
}
|
|
96
|
+
return null;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Crawl every hook entry file transitively and return the reachable module set.
|
|
101
|
+
*
|
|
102
|
+
* @param {string} pluginRoot
|
|
103
|
+
* @returns {{file: string, reachable_from: string[]}[]} sorted by `file`
|
|
104
|
+
*/
|
|
105
|
+
export function buildImportSet(pluginRoot) {
|
|
106
|
+
/** @type {Map<string, Set<string>>} rel path → set of hook entry basenames */
|
|
107
|
+
const reach = new Map();
|
|
108
|
+
|
|
109
|
+
const entries = new Set();
|
|
110
|
+
for (const manifest of HOOK_MANIFESTS) {
|
|
111
|
+
const abs = path.join(pluginRoot, 'hooks', manifest);
|
|
112
|
+
if (!existsSync(abs)) continue;
|
|
113
|
+
for (const rel of extractEntryFiles(readFileSync(abs, 'utf8'))) {
|
|
114
|
+
if (existsSync(path.join(pluginRoot, rel))) entries.add(rel);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
for (const entryRel of [...entries].sort()) {
|
|
119
|
+
const entryName = path.basename(entryRel);
|
|
120
|
+
const stack = [path.join(pluginRoot, entryRel)];
|
|
121
|
+
const seen = new Set();
|
|
122
|
+
while (stack.length > 0) {
|
|
123
|
+
const abs = stack.pop();
|
|
124
|
+
if (seen.has(abs)) continue;
|
|
125
|
+
seen.add(abs);
|
|
126
|
+
|
|
127
|
+
const rel = path.relative(pluginRoot, abs).split(path.sep).join('/');
|
|
128
|
+
if (!reach.has(rel)) reach.set(rel, new Set());
|
|
129
|
+
reach.get(rel).add(entryName);
|
|
130
|
+
|
|
131
|
+
let src;
|
|
132
|
+
try { src = readFileSync(abs, 'utf8'); } catch { continue; }
|
|
133
|
+
for (const spec of extractRelativeSpecifiers(src)) {
|
|
134
|
+
const next = resolveSpecifier(abs, spec);
|
|
135
|
+
if (next) stack.push(next);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
return [...reach.entries()]
|
|
141
|
+
.sort((a, b) => (a[0] < b[0] ? -1 : a[0] > b[0] ? 1 : 0))
|
|
142
|
+
.map(([file, from]) => ({ file, reachable_from: [...from].sort() }));
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Current HEAD sha of the plugin root, or "unknown" outside a git checkout.
|
|
147
|
+
*
|
|
148
|
+
* @param {string} pluginRoot
|
|
149
|
+
* @returns {string}
|
|
150
|
+
*/
|
|
151
|
+
function headSha(pluginRoot) {
|
|
152
|
+
try {
|
|
153
|
+
return execFileSync('git', ['rev-parse', 'HEAD'], {
|
|
154
|
+
cwd: pluginRoot,
|
|
155
|
+
encoding: 'utf8',
|
|
156
|
+
stdio: ['ignore', 'pipe', 'ignore'],
|
|
157
|
+
}).trim();
|
|
158
|
+
} catch {
|
|
159
|
+
return 'unknown';
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Build the full artefact payload.
|
|
165
|
+
*
|
|
166
|
+
* @param {string} pluginRoot
|
|
167
|
+
* @returns {{generated_at: string, head: string, entries: {file: string, reachable_from: string[]}[]}}
|
|
168
|
+
*/
|
|
169
|
+
export function buildArtifact(pluginRoot) {
|
|
170
|
+
return {
|
|
171
|
+
generated_at: new Date().toISOString(),
|
|
172
|
+
head: headSha(pluginRoot),
|
|
173
|
+
entries: buildImportSet(pluginRoot),
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// ---------------------------------------------------------------------------
|
|
178
|
+
// CLI
|
|
179
|
+
// ---------------------------------------------------------------------------
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* @param {string[]} argv
|
|
183
|
+
* @returns {number} process exit code
|
|
184
|
+
*/
|
|
185
|
+
export function main(argv) {
|
|
186
|
+
const args = argv.slice(2);
|
|
187
|
+
const flag = (name) => {
|
|
188
|
+
const i = args.indexOf(name);
|
|
189
|
+
return i >= 0 && i + 1 < args.length ? args[i + 1] : null;
|
|
190
|
+
};
|
|
191
|
+
const pluginRoot = path.resolve(flag('--plugin-root') ?? path.resolve(import.meta.dirname, '..'));
|
|
192
|
+
const out = path.resolve(flag('--out') ?? path.join(pluginRoot, 'hooks', '_lib', 'hook-import-set.json'));
|
|
193
|
+
const check = args.includes('--check');
|
|
194
|
+
|
|
195
|
+
const fresh = buildArtifact(pluginRoot);
|
|
196
|
+
|
|
197
|
+
if (check) {
|
|
198
|
+
if (!existsSync(out)) {
|
|
199
|
+
process.stderr.write(`✗ hook-import-set: ${path.relative(pluginRoot, out)} is missing — run generate-hook-import-set.mjs\n`);
|
|
200
|
+
return 1;
|
|
201
|
+
}
|
|
202
|
+
let committed;
|
|
203
|
+
try { committed = JSON.parse(readFileSync(out, 'utf8')); } catch {
|
|
204
|
+
process.stderr.write('✗ hook-import-set: committed file is not valid JSON\n');
|
|
205
|
+
return 1;
|
|
206
|
+
}
|
|
207
|
+
// Only `entries` is compared — generated_at/head are provenance, not content.
|
|
208
|
+
const a = JSON.stringify(committed.entries ?? null);
|
|
209
|
+
const b = JSON.stringify(fresh.entries);
|
|
210
|
+
if (a !== b) {
|
|
211
|
+
process.stderr.write('✗ hook-import-set: committed set differs from a fresh crawl — run `node scripts/generate-hook-import-set.mjs`\n');
|
|
212
|
+
return 1;
|
|
213
|
+
}
|
|
214
|
+
process.stdout.write(`✓ hook-import-set: ${fresh.entries.length} modules, in sync\n`);
|
|
215
|
+
return 0;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
writeFileSync(out, JSON.stringify(fresh, null, 2) + '\n', 'utf8');
|
|
219
|
+
process.stdout.write(`✓ hook-import-set: wrote ${fresh.entries.length} modules to ${path.relative(pluginRoot, out)}\n`);
|
|
220
|
+
return 0;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* True when this module was launched as the process entry script.
|
|
225
|
+
*
|
|
226
|
+
* A string compare of `import.meta.url` against `file://${process.argv[1]}` is
|
|
227
|
+
* fragile in exactly the invocations CI and husky use: any symlinked or
|
|
228
|
+
* realpath-differing path (measured: `/tmp` → `/private/tmp` on macOS) makes
|
|
229
|
+
* the compare false, so the whole CLI body becomes a silent no-op that still
|
|
230
|
+
* exits 0 — a fail-OPEN drift gate. A space or `#` in the path breaks the
|
|
231
|
+
* hand-built URL the same way. Same shape as `hooks/post-bash-write-verify.mjs`.
|
|
232
|
+
*
|
|
233
|
+
* @returns {boolean}
|
|
234
|
+
*/
|
|
235
|
+
function invokedAsScript() {
|
|
236
|
+
const entry = process.argv[1];
|
|
237
|
+
if (!entry) return false;
|
|
238
|
+
const self = fileURLToPath(import.meta.url);
|
|
239
|
+
try {
|
|
240
|
+
return realpathSync(entry) === realpathSync(self);
|
|
241
|
+
} catch {
|
|
242
|
+
// argv[1] unresolvable (deleted/renamed mid-run) — best-effort raw compare.
|
|
243
|
+
return entry === self;
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
if (invokedAsScript()) {
|
|
248
|
+
process.exit(main(process.argv));
|
|
249
|
+
}
|
|
@@ -166,7 +166,11 @@ function releaseLock(lockFile, _myBody) {
|
|
|
166
166
|
}
|
|
167
167
|
|
|
168
168
|
/**
|
|
169
|
-
* Read the current LWW map from disk. Returns {} on
|
|
169
|
+
* Read the current LWW map from disk. Returns {} on a missing file (ENOENT)
|
|
170
|
+
* silently; an unreadable/unparseable one (EACCES/EISDIR/malformed JSON/…)
|
|
171
|
+
* also returns {} but with a stderr WARN (#1210 — ENOENT and other failures
|
|
172
|
+
* are different facts, same split as `sessions-canonical.mjs`
|
|
173
|
+
* `readCanonicalSessions`).
|
|
170
174
|
* @param {string} currentFile
|
|
171
175
|
* @returns {Record<string, object>}
|
|
172
176
|
*/
|
|
@@ -176,7 +180,14 @@ function readCurrentMap(currentFile) {
|
|
|
176
180
|
const obj = JSON.parse(raw);
|
|
177
181
|
if (obj && typeof obj === 'object' && !Array.isArray(obj)) return obj;
|
|
178
182
|
return {};
|
|
179
|
-
} catch {
|
|
183
|
+
} catch (err) {
|
|
184
|
+
if (!err || err.code !== 'ENOENT') {
|
|
185
|
+
process.stderr.write(
|
|
186
|
+
`⚠ readCurrentMap: cannot read ${currentFile} ` +
|
|
187
|
+
`(${err?.code ?? '?'}: ${err?.message ?? String(err)}) — ` +
|
|
188
|
+
'treating as EMPTY, counts below are floors\n',
|
|
189
|
+
);
|
|
190
|
+
}
|
|
180
191
|
return {};
|
|
181
192
|
}
|
|
182
193
|
}
|
|
@@ -25,6 +25,7 @@ import { existsSync, statSync } from 'node:fs';
|
|
|
25
25
|
import { randomUUID } from 'node:crypto';
|
|
26
26
|
import path from 'node:path';
|
|
27
27
|
|
|
28
|
+
import { readCanonicalSessions } from './sessions-canonical.mjs';
|
|
28
29
|
import { filterRealSessions } from './session-schema.mjs';
|
|
29
30
|
|
|
30
31
|
// ---------------------------------------------------------------------------
|
|
@@ -61,49 +62,50 @@ export async function readDreamSignals({ repoRoot, memoryDir }) {
|
|
|
61
62
|
}
|
|
62
63
|
|
|
63
64
|
// 2. Read sessions.jsonl entries to compute lastCleanupAt + sessionsSinceCleanup
|
|
65
|
+
//
|
|
66
|
+
// #1209: migrated from a hand-rolled parse loop to `readCanonicalSessions`
|
|
67
|
+
// — the raw file is APPEND-ONLY (a physical session can carry more than one
|
|
68
|
+
// LINE: crash-recovery re-appends, #1068 stub/supersede pairs), and the
|
|
69
|
+
// hand-rolled loop counted every line. `sessionsSinceCleanup` is a CADENCE
|
|
70
|
+
// count ("how many sessions since the last cleanup"), so counting a
|
|
71
|
+
// duplicated line twice inflated it — this migration LOWERS the count,
|
|
72
|
+
// which is the fix, not a side effect. `canonicalizeSessions` also drops
|
|
73
|
+
// lines with no `session_id` (they cannot be deduplicated, so they were
|
|
74
|
+
// never a countable "session" to begin with); the existing fixtures below
|
|
75
|
+
// were given synthetic `session_id` values so their asserted counts still
|
|
76
|
+
// reflect the same intended scenario post-migration.
|
|
64
77
|
let lastCleanupAt = null;
|
|
65
78
|
let sessionsSinceCleanup = 0;
|
|
66
79
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
80
|
+
const entries = readCanonicalSessions({ filePath: sessionsFilePath });
|
|
81
|
+
|
|
82
|
+
// Abandoned-session filter (#834): sessions.jsonl carries phantom
|
|
83
|
+
// `status: 'abandoned'` stubs (session-close-backfill records for
|
|
84
|
+
// sessions that ended without a real close). They are legitimate DATA
|
|
85
|
+
// but not legitimate SIGNAL — a burst of abandoned stubs must not fire
|
|
86
|
+
// /memory-cleanup off zero real work. filterRealSessions() is the
|
|
87
|
+
// shared, tested implementation (scripts/lib/session-schema/filters.mjs).
|
|
88
|
+
const realEntries = filterRealSessions(entries);
|
|
89
|
+
|
|
90
|
+
// Find the most recent memory_cleanup_at timestamp across all REAL entries.
|
|
91
|
+
for (const entry of realEntries) {
|
|
92
|
+
const ts = entry.memory_cleanup_at;
|
|
93
|
+
if (typeof ts === 'string' && ts.length > 0) {
|
|
94
|
+
if (lastCleanupAt === null || ts > lastCleanupAt) {
|
|
95
|
+
lastCleanupAt = ts;
|
|
76
96
|
}
|
|
77
97
|
}
|
|
98
|
+
}
|
|
78
99
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
// shared, tested implementation (scripts/lib/session-schema/filters.mjs).
|
|
85
|
-
const realEntries = filterRealSessions(entries);
|
|
86
|
-
|
|
87
|
-
// Find the most recent memory_cleanup_at timestamp across all REAL entries.
|
|
100
|
+
// Count REAL entries newer than the last cleanup (or total REAL entries
|
|
101
|
+
// when no cleanup ever ran).
|
|
102
|
+
if (lastCleanupAt === null) {
|
|
103
|
+
sessionsSinceCleanup = realEntries.length;
|
|
104
|
+
} else {
|
|
88
105
|
for (const entry of realEntries) {
|
|
89
|
-
const
|
|
90
|
-
if (typeof
|
|
91
|
-
|
|
92
|
-
lastCleanupAt = ts;
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
// Count REAL entries newer than the last cleanup (or total REAL entries
|
|
98
|
-
// when no cleanup ever ran).
|
|
99
|
-
if (lastCleanupAt === null) {
|
|
100
|
-
sessionsSinceCleanup = realEntries.length;
|
|
101
|
-
} else {
|
|
102
|
-
for (const entry of realEntries) {
|
|
103
|
-
const startedAt = entry.started_at;
|
|
104
|
-
if (typeof startedAt === 'string' && startedAt > lastCleanupAt) {
|
|
105
|
-
sessionsSinceCleanup += 1;
|
|
106
|
-
}
|
|
106
|
+
const startedAt = entry.started_at;
|
|
107
|
+
if (typeof startedAt === 'string' && startedAt > lastCleanupAt) {
|
|
108
|
+
sessionsSinceCleanup += 1;
|
|
107
109
|
}
|
|
108
110
|
}
|
|
109
111
|
}
|
|
@@ -158,6 +158,12 @@ export function computeSuitabilityVerdict(deps = {}) {
|
|
|
158
158
|
const ciNotRed = ciStatus !== 'red'; // null status (absent/malformed) passes
|
|
159
159
|
if (ciAbsent) warnings.push('CI signal absent');
|
|
160
160
|
else if (ciMalformed) warnings.push('CI signal malformed — treated as absent');
|
|
161
|
+
// `{status:'unknown'}` is a WELL-FORMED reading that establishes nothing —
|
|
162
|
+
// since #1031 it is what a degraded probe produces, and it hits neither
|
|
163
|
+
// branch above. Without this line it passed G3 with NO warning at all, i.e.
|
|
164
|
+
// more quietly than the plain-absent case it is strictly weaker than. The
|
|
165
|
+
// gate stays non-blocking (only a literal 'red' fails); only the silence goes.
|
|
166
|
+
else if (ciStatus === 'unknown') warnings.push('CI signal unknown — treated as absent');
|
|
161
167
|
const ciSegment = ciAbsent ? 'absent' : ciMalformed ? 'malformed' : `${ciStatus}`;
|
|
162
168
|
|
|
163
169
|
// --- G4 — resource gate (green/warn/degraded/null pass; only 'critical' fails)
|
|
@@ -20,7 +20,7 @@ import {
|
|
|
20
20
|
finalizeState,
|
|
21
21
|
} from './telemetry.mjs';
|
|
22
22
|
import { durableCommit } from './durable-telemetry.mjs';
|
|
23
|
-
import {
|
|
23
|
+
import { getStateDir } from '../platform.mjs';
|
|
24
24
|
|
|
25
25
|
// Internal clamp — mirrors flags.mjs internal; not re-exported there.
|
|
26
26
|
function clampNumber(value, { min, max, fallback }) {
|
|
@@ -311,7 +311,7 @@ export async function runLoop(opts = {}) {
|
|
|
311
311
|
files: [
|
|
312
312
|
jsonlPath.replace(process.cwd() + '/', ''),
|
|
313
313
|
'.orchestrator/metrics/sessions.jsonl',
|
|
314
|
-
`${
|
|
314
|
+
`${getStateDir()}/STATE.md`,
|
|
315
315
|
],
|
|
316
316
|
enabled: false,
|
|
317
317
|
});
|