session-orchestrator 5.0.0 → 5.2.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/autopilot/SKILL.md +1 -0
- package/.agents/skills/bootstrap/SKILL.md +2 -0
- package/.agents/skills/brainstorm/SKILL.md +3 -0
- package/.agents/skills/close/SKILL.md +17 -0
- package/.agents/skills/debug/SKILL.md +2 -0
- package/.agents/skills/discovery/SKILL.md +2 -1
- package/.agents/skills/dispatcher/SKILL.md +2 -0
- package/.agents/skills/eli5/SKILL.md +2 -0
- package/.agents/skills/eval/SKILL.md +1 -0
- package/.agents/skills/evolve/SKILL.md +2 -1
- package/.agents/skills/go/SKILL.md +18 -0
- package/.agents/skills/grill/SKILL.md +2 -0
- package/.agents/skills/harness-audit/SKILL.md +16 -0
- package/.agents/skills/memory-cleanup/SKILL.md +1 -0
- package/.agents/skills/persona-panel/SKILL.md +1 -0
- package/.agents/skills/plan/SKILL.md +3 -1
- package/.agents/skills/portfolio/SKILL.md +17 -0
- package/.agents/skills/reconcile/SKILL.md +1 -0
- package/.agents/skills/release/SKILL.md +18 -0
- package/.agents/skills/repo-audit/SKILL.md +1 -0
- package/.agents/skills/spinout/SKILL.md +1 -0
- package/.agents/skills/sunset-review/SKILL.md +2 -0
- package/.agents/skills/test/SKILL.md +17 -0
- package/.agents/skills/ux-grill/SKILL.md +2 -0
- package/.claude-plugin/marketplace.json +3 -3
- package/.claude-plugin/plugin.json +2 -2
- package/.codex-plugin/plugin.json +2 -2
- package/.codex-plugin/skills/autopilot/SKILL.md +5 -4
- package/.codex-plugin/skills/bootstrap/SKILL.md +8 -4
- package/.codex-plugin/skills/brainstorm/SKILL.md +11 -4
- package/.codex-plugin/skills/close/SKILL.md +3 -3
- package/.codex-plugin/skills/convergence-monitoring/SKILL.md +2 -0
- package/.codex-plugin/skills/convergence-monitoring/agents/openai.yaml +5 -0
- package/.codex-plugin/skills/debug/SKILL.md +11 -4
- package/.codex-plugin/skills/discovery/SKILL.md +8 -4
- package/.codex-plugin/skills/dispatcher/SKILL.md +4 -4
- package/.codex-plugin/skills/eli5/SKILL.md +9 -4
- package/.codex-plugin/skills/eval/SKILL.md +9 -4
- package/.codex-plugin/skills/evolve/SKILL.md +9 -4
- package/.codex-plugin/skills/go/SKILL.md +3 -3
- package/.codex-plugin/skills/grill/SKILL.md +11 -4
- package/.codex-plugin/skills/harness-audit/SKILL.md +4 -3
- package/.codex-plugin/skills/memory-cleanup/SKILL.md +9 -4
- package/.codex-plugin/skills/npm-publish/SKILL.md +2 -0
- package/.codex-plugin/skills/npm-publish/agents/openai.yaml +5 -0
- package/.codex-plugin/skills/persona-panel/SKILL.md +5 -5
- package/.codex-plugin/skills/plan/SKILL.md +8 -4
- package/.codex-plugin/skills/portfolio/SKILL.md +3 -3
- package/.codex-plugin/skills/reconcile/SKILL.md +9 -4
- package/.codex-plugin/skills/release/SKILL.md +3 -3
- package/.codex-plugin/skills/repo-audit/SKILL.md +6 -4
- package/.codex-plugin/skills/spinout/SKILL.md +4 -4
- package/.codex-plugin/skills/sunset-review/SKILL.md +5 -4
- package/.codex-plugin/skills/test/SKILL.md +3 -3
- package/.codex-plugin/skills/ux-grill/SKILL.md +11 -4
- package/.cursor/commands/autopilot.md +4 -4
- package/.cursor/commands/bootstrap.md +5 -4
- package/.cursor/commands/brainstorm.md +5 -4
- package/.cursor/commands/close.md +4 -3
- package/.cursor/commands/convergence-monitoring.md +13 -0
- package/.cursor/commands/debug.md +4 -4
- package/.cursor/commands/discovery.md +4 -4
- package/.cursor/commands/dispatcher.md +4 -4
- package/.cursor/commands/eli5.md +4 -4
- package/.cursor/commands/eval.md +4 -4
- package/.cursor/commands/evolve.md +4 -4
- package/.cursor/commands/go.md +4 -3
- package/.cursor/commands/grill.md +4 -4
- package/.cursor/commands/harness-audit.md +3 -3
- package/.cursor/commands/memory-cleanup.md +4 -4
- package/.cursor/commands/npm-publish.md +13 -0
- package/.cursor/commands/persona-panel.md +4 -4
- package/.cursor/commands/plan.md +5 -4
- package/.cursor/commands/portfolio.md +3 -3
- package/.cursor/commands/reconcile.md +4 -4
- package/.cursor/commands/release.md +4 -3
- package/.cursor/commands/repo-audit.md +4 -4
- package/.cursor/commands/spinout.md +4 -4
- package/.cursor/commands/sunset-review.md +4 -4
- package/.cursor/commands/test.md +3 -3
- package/.cursor/commands/ux-grill.md +4 -4
- package/.cursor/rules/010-session-workflow.mdc +2 -2
- package/.cursor/skills/bootstrap/SKILL.md +1 -0
- package/.cursor/skills/close/SKILL.md +13 -0
- package/.cursor/skills/debug/SKILL.md +0 -1
- package/.cursor/skills/discovery/SKILL.md +0 -1
- package/.cursor/skills/dispatcher/SKILL.md +0 -1
- package/.cursor/skills/eli5/SKILL.md +0 -1
- package/.cursor/skills/evolve/SKILL.md +0 -1
- package/.cursor/skills/go/SKILL.md +13 -0
- package/.cursor/skills/grill/SKILL.md +0 -1
- package/.cursor/skills/harness-audit/SKILL.md +12 -0
- package/.cursor/skills/portfolio/SKILL.md +12 -0
- package/.cursor/skills/release/SKILL.md +13 -0
- package/.cursor/skills/repo-audit/SKILL.md +0 -1
- package/.cursor/skills/sunset-review/SKILL.md +0 -1
- package/.cursor/skills/test/SKILL.md +12 -0
- package/.cursor/skills/ux-grill/SKILL.md +0 -1
- package/.cursor-plugin/plugin.json +2 -2
- package/.orchestrator/policy/blocked-commands.json +10 -0
- package/AGENTS.md +1 -1
- package/CHANGELOG.md +80 -0
- package/README.md +74 -235
- package/commands/session.md +10 -0
- package/docs/USER-GUIDE.md +24 -0
- package/docs/ci-setup.md +53 -0
- package/docs/codex-setup.md +1 -1
- package/docs/components.md +12 -5
- package/docs/events-schema.md +5 -1
- package/docs/install.md +128 -0
- package/docs/persona-panel.md +1 -1
- package/docs/pi-setup.md +1 -1
- package/docs/rule-authoring.md +83 -14
- package/docs/scope-collision-guard.md +2 -0
- package/docs/session-config-reference.md +6 -4
- package/docs/session-config-template.md +38 -0
- package/docs/telemetry.md +15 -0
- package/hooks/_lib/hook-import-set.json +46 -6
- package/hooks/_lib/subagent-paths.mjs +15 -0
- package/hooks/_lib/vcs-create-matcher.mjs +217 -62
- package/hooks/enforce-scope.mjs +42 -1
- package/hooks/hooks-codex.json +1 -1
- package/hooks/hooks.json +1 -1
- package/hooks/on-session-end.mjs +14 -2
- package/hooks/on-stop.mjs +43 -1
- package/hooks/post-bash-write-verify.mjs +3 -0
- package/hooks/pre-auq-clarity.mjs +3 -0
- package/hooks/pre-bash-issue-budget.mjs +103 -17
- package/hooks/pre-task-scope-disjoint.mjs +152 -3
- package/hooks/skill-invocation-telemetry.mjs +2 -1
- package/package.json +3 -2
- package/pi/prompts/autopilot.md +3 -3
- package/pi/prompts/bootstrap.md +3 -3
- package/pi/prompts/brainstorm.md +3 -3
- package/pi/prompts/close.md +2 -2
- package/pi/prompts/convergence-monitoring.md +11 -0
- package/pi/prompts/debug.md +3 -3
- package/pi/prompts/discovery.md +3 -3
- package/pi/prompts/dispatcher.md +3 -3
- package/pi/prompts/eli5.md +3 -3
- package/pi/prompts/eval.md +3 -3
- package/pi/prompts/evolve.md +3 -3
- package/pi/prompts/go.md +2 -2
- package/pi/prompts/grill.md +3 -3
- package/pi/prompts/harness-audit.md +2 -3
- package/pi/prompts/memory-cleanup.md +3 -3
- package/pi/prompts/npm-publish.md +11 -0
- package/pi/prompts/persona-panel.md +3 -3
- package/pi/prompts/plan.md +3 -3
- package/pi/prompts/portfolio.md +2 -2
- package/pi/prompts/reconcile.md +3 -3
- package/pi/prompts/release.md +3 -3
- package/pi/prompts/repo-audit.md +3 -4
- package/pi/prompts/session.md +1 -1
- package/pi/prompts/spinout.md +3 -3
- package/pi/prompts/sunset-review.md +3 -3
- package/pi/prompts/templates-ack.md +1 -1
- package/pi/prompts/test.md +3 -3
- package/pi/prompts/ux-grill.md +3 -3
- package/scripts/archive-closed-prds.mjs +2 -2
- package/scripts/auq-audit.mjs +2 -3
- package/scripts/backfill-abandoned-sessions.mjs +57 -3
- package/scripts/backfill-evidence-digest.mjs +2 -1
- package/scripts/backfill-learnings-from-vault.mjs +2 -2
- package/scripts/check-package-manager.mjs +2 -2
- package/scripts/ci/assert-vitest-green.mjs +2 -1
- package/scripts/emit-session.mjs +2 -3
- package/scripts/export-hw-learnings.mjs +2 -1
- package/scripts/express-path.mjs +1 -1
- package/scripts/gc-stale-worktrees.mjs +2 -1
- package/scripts/generate-codex-skills.mjs +48 -4
- package/scripts/generate-cursor-adapter.mjs +173 -9
- package/scripts/generate-hook-import-set.mjs +12 -27
- package/scripts/generate-pi-prompts.mjs +183 -13
- package/scripts/github-protection-audit.mjs +2 -3
- package/scripts/lib/agent-frontmatter.mjs +23 -1
- package/scripts/lib/claude-md-budget-lint.mjs +2 -5
- package/scripts/lib/command-blocker.mjs +209 -9
- package/scripts/lib/config/drift-check.mjs +19 -0
- package/scripts/lib/convergence-monitor.mjs +2 -2
- package/scripts/lib/cursor-hook-bridge.mjs +2 -2
- package/scripts/lib/description-surface.mjs +2 -5
- package/scripts/lib/dispatcher/cli.mjs +2 -1
- package/scripts/lib/ecosystem-wizard.mjs +2 -1
- package/scripts/lib/fetch-baseline.mjs +3 -8
- package/scripts/lib/gitlab-ops/stale-mr-sweep.mjs +2 -1
- package/scripts/lib/gitlab-portfolio/cli.mjs +2 -1
- package/scripts/lib/instruction-budget-guard.mjs +186 -46
- package/scripts/lib/is-main-module.mjs +82 -0
- package/scripts/lib/locks/index.mjs +32 -25
- package/scripts/lib/maintenance-due-banner.mjs +69 -3
- package/scripts/lib/peer-discovery.mjs +2 -5
- package/scripts/lib/playwright-driver/runner.mjs +63 -2
- package/scripts/lib/reconcile/rule-expiry-sweep.mjs +642 -0
- package/scripts/lib/rules-sync.mjs +2 -5
- package/scripts/lib/scope-echo.mjs +392 -7
- package/scripts/lib/session-close-backfill.mjs +58 -6
- package/scripts/lib/state-md.mjs +84 -3
- package/scripts/lib/sunset/walker.mjs +31 -4
- package/scripts/lib/tests-src-ratio.mjs +2 -6
- package/scripts/lib/tmux-layout/telemetry-stats.mjs +2 -1
- package/scripts/lib/user-invocable-skills.mjs +185 -0
- package/scripts/lib/validate/check-banner-parity.mjs +2 -2
- package/scripts/lib/validate/check-cursor-adapter.mjs +2 -2
- package/scripts/lib/validate/check-dead-bridge.mjs +2 -2
- package/scripts/lib/validate/check-doc-cli-commands.mjs +2 -2
- package/scripts/lib/validate/check-entry-guard.mjs +366 -0
- package/scripts/lib/validate/check-guard-requires-parity.mjs +2 -2
- package/scripts/lib/validate/check-hooks-emit-event-guard.mjs +2 -2
- package/scripts/lib/validate/check-learning-provenance.mjs +2 -2
- package/scripts/lib/validate/check-skill-links.mjs +27 -6
- package/scripts/lib/validate/check-skill-script-paths.mjs +2 -2
- package/scripts/lib/validate/check-test-git-config-target.mjs +2 -2
- package/scripts/lib/validate/check-unicode-safety.mjs +2 -2
- package/scripts/lib/validate/check-untracked-test-deps.mjs +2 -2
- package/scripts/lib/validate/check-unwired-features.mjs +266 -11
- package/scripts/lib/validate/check-validator-registration.mjs +2 -2
- package/scripts/lib/validate/check-vcs-repo-flag.mjs +2 -2
- package/scripts/lib/validate-vendored-rules.mjs +35 -9
- package/scripts/lib/wave-transcript-tail.mjs +2 -2
- package/scripts/lock-reaper.mjs +2 -1
- package/scripts/materialize-wave-scope.mjs +87 -4
- package/scripts/migrate-sessions-jsonl.mjs +2 -1
- package/scripts/migrate-vault-paths.mjs +2 -3
- package/scripts/release.mjs +124 -35
- package/scripts/relocate-vault-corpus.mjs +2 -3
- package/scripts/repair-invalid-sessions.mjs +2 -2
- package/scripts/session-shape.mjs +2 -2
- package/scripts/site-numbers.mjs +35 -11
- package/scripts/sweep-expired-rules.mjs +216 -0
- package/scripts/validate-plugin.mjs +9 -0
- package/scripts/vault-consolidate.mjs +2 -2
- package/scripts/vault-mirror.mjs +2 -3
- package/scripts/wave-scope-binding.mjs +2 -3
- package/skills/_shared/bootstrap-gate.md +1 -1
- package/skills/_shared/monitor-patterns.md +1 -1
- package/skills/_shared/research-evidence.md +53 -0
- package/skills/_shared/state-ownership.md +3 -0
- package/skills/autopilot/SKILL.md +58 -4
- package/skills/bootstrap/SKILL.md +51 -1
- package/skills/brainstorm/SKILL.md +16 -0
- package/skills/claude-md-drift-check/checker.mjs +49 -11
- package/{commands/close.md → skills/close/SKILL.md} +9 -3
- package/skills/debug/SKILL.md +10 -0
- package/skills/discovery/SKILL.md +24 -1
- package/skills/discovery/probes-session.md +2 -2
- package/skills/dispatcher/SKILL.md +38 -7
- package/skills/eli5/SKILL.md +11 -0
- package/skills/eval/SKILL.md +14 -0
- package/skills/evolve/SKILL.md +8 -1
- package/skills/evolve/references/evolve-dialectic-mode.md +6 -2
- package/{commands/go.md → skills/go/SKILL.md} +9 -1
- package/skills/grill/SKILL.md +19 -0
- package/{commands/harness-audit.md → skills/harness-audit/SKILL.md} +7 -2
- package/skills/hook-development/SKILL.md +46 -41
- package/skills/memory-cleanup/SKILL.md +7 -0
- package/skills/npm-publish/SKILL.md +1 -1
- package/skills/persona-panel/SKILL.md +56 -1
- package/skills/persona-panel/persona-format.md +1 -1
- package/skills/plan/SKILL.md +28 -1
- package/skills/playwright-driver/SKILL.md +7 -10
- package/{commands/portfolio.md → skills/portfolio/SKILL.md} +8 -2
- package/skills/reconcile/SKILL.md +10 -0
- package/{commands/release.md → skills/release/SKILL.md} +16 -2
- package/skills/repo-audit/SKILL.md +7 -0
- package/skills/session-end/plan-verification.md +2 -2
- package/skills/session-plan/SKILL.md +1 -1
- package/skills/session-start/SKILL.md +5 -4
- package/skills/session-start/phase-8-5-express-path.md +6 -6
- package/skills/session-start/references/phase-1-5-session-continuity.md +1 -1
- package/skills/session-start/references/phase-2-7-portfolio-snapshot.md +1 -1
- package/skills/session-start/references/phase-4-ssot-environment-check.md +4 -3
- package/skills/spinout/SKILL.md +12 -1
- package/skills/sunset-review/SKILL.md +13 -0
- package/{commands/test.md → skills/test/SKILL.md} +10 -4
- package/skills/ux-grill/SKILL.md +19 -1
- package/skills/wave-executor/SKILL.md +7 -4
- package/skills/wave-executor/references/wave-executor-state-init.md +13 -1
- package/skills/wave-executor/references/wave-loop-dispatch.md +3 -1
- package/skills/wave-executor/references/wave-loop-review.md +17 -1
- package/commands/autopilot.md +0 -80
- package/commands/bootstrap.md +0 -56
- package/commands/brainstorm.md +0 -48
- package/commands/debug.md +0 -36
- package/commands/discovery.md +0 -32
- package/commands/dispatcher.md +0 -59
- package/commands/eli5.md +0 -33
- package/commands/eval.md +0 -28
- package/commands/evolve.md +0 -10
- package/commands/grill.md +0 -45
- package/commands/memory-cleanup.md +0 -26
- package/commands/persona-panel.md +0 -121
- package/commands/plan.md +0 -15
- package/commands/reconcile.md +0 -23
- package/commands/repo-audit.md +0 -24
- package/commands/spinout.md +0 -15
- package/commands/sunset-review.md +0 -27
- package/commands/ux-grill.md +0 -51
|
@@ -39,9 +39,10 @@
|
|
|
39
39
|
|
|
40
40
|
import { existsSync, readdirSync, readFileSync } from 'node:fs';
|
|
41
41
|
import path from 'node:path';
|
|
42
|
-
import { pathToFileURL } from 'node:url';
|
|
43
42
|
import { normalizeSkillInvocation } from '../skill-invocations-schema.mjs';
|
|
44
43
|
import { normalizeSkillJudgment } from '../skill-judgments-schema.mjs';
|
|
44
|
+
import { isMainModule } from '../is-main-module.mjs';
|
|
45
|
+
import { userInvocableSkills } from '../user-invocable-skills.mjs';
|
|
45
46
|
|
|
46
47
|
// ---------------------------------------------------------------------------
|
|
47
48
|
// Named threshold constants
|
|
@@ -602,6 +603,16 @@ export function staticReferenceScan(repoRoot, { kind, name }) {
|
|
|
602
603
|
* Parse commands/*.md for the skill each command invokes. Recognises the
|
|
603
604
|
* conventional `skills/<name>/SKILL.md` reference inside a command file.
|
|
604
605
|
*
|
|
606
|
+
* NOTE since the 2026-09-16 command→skill fold (#1370): this linkage now covers
|
|
607
|
+
* almost nothing. `commands/` holds two files (`session`, `templates-ack`), so
|
|
608
|
+
* `skillToCommands` is near-empty by construction — the operator entry point for
|
|
609
|
+
* every other slash command is an explicit `user-invocable: true` in the skill's
|
|
610
|
+
* own frontmatter, not a command file pointing at it. `runSunsetWalk` therefore
|
|
611
|
+
* threads that flag alongside this linkage, and `classifyItem` treats the two as
|
|
612
|
+
* equivalent evidence of "an operator can invoke this directly". Without that, 26
|
|
613
|
+
* live slash commands would have been demoted the day their command file was
|
|
614
|
+
* deleted, for a reason that is a rename, not a disuse signal.
|
|
615
|
+
*
|
|
605
616
|
* @param {string} repoRoot
|
|
606
617
|
* @returns {{commandToSkill: Map<string,string|null>, skillToCommands: Map<string,string[]>}}
|
|
607
618
|
*/
|
|
@@ -772,6 +783,11 @@ export function classifyItem({
|
|
|
772
783
|
const strictRefs = staticRefs.strictRefs;
|
|
773
784
|
const nonBoilerplateRefs = staticRefs.nonBoilerplateRefs;
|
|
774
785
|
const invokedByCommands = linkage?.invokedByCommands ?? [];
|
|
786
|
+
// #1370 fold: an explicit `user-invocable: true` in SKILL.md frontmatter is the
|
|
787
|
+
// post-fold form of "an operator invokes this directly" — the same evidence a
|
|
788
|
+
// command file used to carry. Default false so older callers/tests that do not
|
|
789
|
+
// thread it behave exactly as before.
|
|
790
|
+
const userInvocable = linkage?.userInvocable === true;
|
|
775
791
|
const invokesSkill = linkage?.invokesSkill ?? null;
|
|
776
792
|
// When a command invokes a skill, the caller passes whether that skill is
|
|
777
793
|
// present on disk. Default-true so callers that don't thread the surface set
|
|
@@ -786,6 +802,7 @@ export function classifyItem({
|
|
|
786
802
|
proseRefs,
|
|
787
803
|
nonBoilerplateRefs,
|
|
788
804
|
invokedByCommands,
|
|
805
|
+
userInvocable,
|
|
789
806
|
invokesSkill,
|
|
790
807
|
invokedSkillExists,
|
|
791
808
|
lowConfidence,
|
|
@@ -842,6 +859,10 @@ export function classifyItem({
|
|
|
842
859
|
active = true;
|
|
843
860
|
reasons.push(`skill invoked by command(s): ${invokedByCommands.join(', ')}`);
|
|
844
861
|
}
|
|
862
|
+
if (kind === 'skill' && userInvocable) {
|
|
863
|
+
active = true;
|
|
864
|
+
reasons.push('skill is an operator-facing slash command (user-invocable: true)');
|
|
865
|
+
}
|
|
845
866
|
if (kind === 'command' && invokesSkill && invokedSkillExists) {
|
|
846
867
|
active = true;
|
|
847
868
|
reasons.push(`command invokes live skill: ${invokesSkill}`);
|
|
@@ -878,7 +899,10 @@ export function classifyItem({
|
|
|
878
899
|
|
|
879
900
|
// --- Demote (near-zero) — skills & commands (agents handled above) ---------
|
|
880
901
|
const lonelySkill =
|
|
881
|
-
kind === 'skill' &&
|
|
902
|
+
kind === 'skill' &&
|
|
903
|
+
nonBoilerplateRefs <= 1 &&
|
|
904
|
+
invokedByCommands.length === 0 &&
|
|
905
|
+
!userInvocable;
|
|
882
906
|
const lonelyCommand =
|
|
883
907
|
kind === 'command' && !invokesSkill && nonBoilerplateRefs <= 1;
|
|
884
908
|
if (lonelySkill || lonelyCommand) {
|
|
@@ -942,6 +966,9 @@ export function runSunsetWalk(repoRoot, opts = {}) {
|
|
|
942
966
|
// classification below behaves byte-for-byte identically to the L1-only path.
|
|
943
967
|
const skillJudgments = readSkillJudgmentCounts(judgmentsPath, { windowDays, now: nowMs });
|
|
944
968
|
const linkage = commandSkillLinkage(repoRoot);
|
|
969
|
+
// Post-#1370 replacement for the command→skill linkage as an operator-entry
|
|
970
|
+
// signal (see commandSkillLinkage's note). Read once per walk, not per skill.
|
|
971
|
+
const userInvocableSet = new Set(userInvocableSkills(repoRoot));
|
|
945
972
|
const coverageDays = dispatch.coverageDays;
|
|
946
973
|
const lowConfidence = coverageDays < windowDays;
|
|
947
974
|
|
|
@@ -967,7 +994,7 @@ export function runSunsetWalk(repoRoot, opts = {}) {
|
|
|
967
994
|
name,
|
|
968
995
|
dispatch: skillDispatch,
|
|
969
996
|
static: staticRefs,
|
|
970
|
-
linkage: { invokedByCommands },
|
|
997
|
+
linkage: { invokedByCommands, userInvocable: userInvocableSet.has(name) },
|
|
971
998
|
windowDays,
|
|
972
999
|
coverageDays,
|
|
973
1000
|
judge,
|
|
@@ -1192,7 +1219,7 @@ function main(argv) {
|
|
|
1192
1219
|
}
|
|
1193
1220
|
|
|
1194
1221
|
// Import-safe main-guard.
|
|
1195
|
-
if (import.meta.url
|
|
1222
|
+
if (isMainModule(import.meta.url)) {
|
|
1196
1223
|
const code = main(process.argv.slice(2));
|
|
1197
1224
|
process.exit(code);
|
|
1198
1225
|
}
|
|
@@ -76,9 +76,9 @@
|
|
|
76
76
|
import { readFileSync, existsSync, statSync } from 'node:fs';
|
|
77
77
|
import { join, isAbsolute, relative, resolve } from 'node:path';
|
|
78
78
|
import { execFileSync } from 'node:child_process';
|
|
79
|
-
import { fileURLToPath } from 'node:url';
|
|
80
79
|
|
|
81
80
|
import { writeStdoutLineSync } from './io.mjs';
|
|
81
|
+
import { isMainModule } from './is-main-module.mjs';
|
|
82
82
|
|
|
83
83
|
// ---------------------------------------------------------------------------
|
|
84
84
|
// The definition — the part that must never be re-derived by hand
|
|
@@ -475,10 +475,6 @@ function main() {
|
|
|
475
475
|
process.exit(args.check && !result.withinCorridor ? 1 : 0);
|
|
476
476
|
}
|
|
477
477
|
|
|
478
|
-
const isMain =
|
|
479
|
-
typeof process !== 'undefined' &&
|
|
480
|
-
process.argv[1] !== null &&
|
|
481
|
-
process.argv[1] !== undefined &&
|
|
482
|
-
resolve(process.argv[1]) === resolve(fileURLToPath(import.meta.url));
|
|
478
|
+
const isMain =isMainModule(import.meta.url);
|
|
483
479
|
|
|
484
480
|
if (isMain) main();
|
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
*/
|
|
15
15
|
|
|
16
16
|
import { readFileSync, existsSync } from 'node:fs';
|
|
17
|
+
import { isMainModule } from '../is-main-module.mjs';
|
|
17
18
|
|
|
18
19
|
const EVENTS_PATH = '.orchestrator/metrics/events.jsonl';
|
|
19
20
|
|
|
@@ -93,7 +94,7 @@ export function computeStats(events) {
|
|
|
93
94
|
}
|
|
94
95
|
|
|
95
96
|
// CLI entry-point — emit stats as JSON to stdout when run directly
|
|
96
|
-
if (import.meta.url
|
|
97
|
+
if (isMainModule(import.meta.url)) {
|
|
97
98
|
const events = readTmuxEvents();
|
|
98
99
|
const stats = computeStats(events);
|
|
99
100
|
console.log(JSON.stringify(stats, null, 2));
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* scripts/lib/user-invocable-skills.mjs
|
|
3
|
+
*
|
|
4
|
+
* ONE definition of "this skill is an operator-facing slash command".
|
|
5
|
+
*
|
|
6
|
+
* Since the #1370 command→skill fold (2026-09-16) `commands/` holds only the two
|
|
7
|
+
* names that cannot be a skill (`session`, `templates-ack` — see
|
|
8
|
+
* `tests/commands/headless-bare-command-availability.test.mjs` for the measured
|
|
9
|
+
* mechanism). Every other slash command IS a skill, marked by an EXPLICIT
|
|
10
|
+
* `user-invocable: true` in its SKILL.md frontmatter. Consumers that used to
|
|
11
|
+
* count or link `commands/*.md` therefore need the union:
|
|
12
|
+
*
|
|
13
|
+
* commands/*.md ∪ skills with explicit `user-invocable: true`
|
|
14
|
+
*
|
|
15
|
+
* This module exists so that union has one implementation rather than one per
|
|
16
|
+
* consumer (site-numbers tile, sunset walker linkage, the guard tests). It is
|
|
17
|
+
* deliberately tiny and dependency-free: `scripts/lib/sunset/walker.mjs` imports it,
|
|
18
|
+
* and the walker sits in the hook import graph, which must load WITHOUT
|
|
19
|
+
* node_modules (GH#62/#63) — so no `js-yaml` here; the frontmatter parser is the
|
|
20
|
+
* repo's own `parseAgentFrontmatter`, already in that graph.
|
|
21
|
+
*
|
|
22
|
+
* PARSED, never line-matched: `.claude/rules/test-hygiene.md` records that a
|
|
23
|
+
* line-regex frontmatter reader is blind to unparseable YAML and mis-measures
|
|
24
|
+
* block scalars. A SKILL.md whose frontmatter does not parse is treated as NOT
|
|
25
|
+
* user-invocable — fail-closed, and loud where it matters because such a file
|
|
26
|
+
* already fails `check-skills`/`validate-plugin`.
|
|
27
|
+
*/
|
|
28
|
+
|
|
29
|
+
import { existsSync, readdirSync, readFileSync, statSync } from 'node:fs';
|
|
30
|
+
import path from 'node:path';
|
|
31
|
+
import { parseAgentFrontmatter } from './agent-frontmatter.mjs';
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* YAML 1.1 boolean lookalikes that YAML 1.2 / js-yaml's CORE_SCHEMA (what
|
|
35
|
+
* `generate-codex-skills` parses with) reads as PLAIN STRINGS, not as `true`.
|
|
36
|
+
* They are therefore NOT user-invocable here either — but silently demoting a
|
|
37
|
+
* skill on one of them is the #1370 defect class, so each one gets a WARN.
|
|
38
|
+
*/
|
|
39
|
+
const TRUTHY_LOOKALIKE = /^(?:yes|y|on|t|1)$/i;
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Strip a trailing `#` comment, honouring quotes: `true # note` is the same
|
|
43
|
+
* declaration as `true`, but `"a # b"` carries the hash as content.
|
|
44
|
+
*
|
|
45
|
+
* @param {string} text
|
|
46
|
+
* @returns {string}
|
|
47
|
+
*/
|
|
48
|
+
function stripTrailingComment(text) {
|
|
49
|
+
let quote = null;
|
|
50
|
+
for (let i = 0; i < text.length; i += 1) {
|
|
51
|
+
const ch = text[i];
|
|
52
|
+
if (quote) {
|
|
53
|
+
if (ch === quote) quote = null;
|
|
54
|
+
continue;
|
|
55
|
+
}
|
|
56
|
+
if (ch === '"' || ch === "'") {
|
|
57
|
+
quote = ch;
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
60
|
+
// A `#` opens a YAML comment only at the start or after whitespace.
|
|
61
|
+
if (ch === '#' && (i === 0 || /\s/.test(text[i - 1]))) return text.slice(0, i);
|
|
62
|
+
}
|
|
63
|
+
return text;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Normalise a raw frontmatter scalar to the token a YAML parser would see:
|
|
68
|
+
* trim, drop a trailing comment, drop one layer of matching surrounding quotes.
|
|
69
|
+
*
|
|
70
|
+
* @param {string} text
|
|
71
|
+
* @returns {string}
|
|
72
|
+
*/
|
|
73
|
+
function normaliseScalar(text) {
|
|
74
|
+
let out = stripTrailingComment(text).trim();
|
|
75
|
+
if (out.length >= 2 && (out[0] === '"' || out[0] === "'") && out[out.length - 1] === out[0]) {
|
|
76
|
+
out = out.slice(1, -1).trim();
|
|
77
|
+
}
|
|
78
|
+
return out;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* THE one normaliser for `user-invocable` — the marker "this skill is an
|
|
83
|
+
* operator-facing slash command". Every generator (Cursor, Pi, Codex) and every
|
|
84
|
+
* counter (site-numbers tile, drift-check `command-count`, the guard tests) must
|
|
85
|
+
* route through this function; four private re-implementations disagreeing on a
|
|
86
|
+
* quoted value is the defect this replaces.
|
|
87
|
+
*
|
|
88
|
+
* Explicit-true only. `undefined` (flag absent) is NOT user-invocable: the whole
|
|
89
|
+
* point of the marker is that it is written down, so a skill that forgot it is a
|
|
90
|
+
* defect to surface, not a default to guess.
|
|
91
|
+
*
|
|
92
|
+
* Accepted: the boolean `true`, and a string that normalises to `true`
|
|
93
|
+
* case-insensitively — so `"true"`, `'true'`, `True`, `true # note` and
|
|
94
|
+
* `true ` (trailing space, invisible in an editor) all read as the same
|
|
95
|
+
* declaration. Trailing-whitespace tolerance is load-bearing rather than
|
|
96
|
+
* cosmetic: without it a stray space silently demotes a skill out of the
|
|
97
|
+
* generated adapters with no diagnostic anywhere.
|
|
98
|
+
*
|
|
99
|
+
* REJECTED: `yes` / `y` / `on` / `t` / `1`. Those are YAML 1.1 booleans; under
|
|
100
|
+
* YAML 1.2 — which is what js-yaml's CORE_SCHEMA in `generate-codex-skills`
|
|
101
|
+
* applies — they are plain strings, and this predicate follows the parser rather
|
|
102
|
+
* than inventing a fifth dialect. Because the demotion is the surprising half,
|
|
103
|
+
* each one emits exactly one stderr WARN naming the file.
|
|
104
|
+
*
|
|
105
|
+
* @param {unknown} value the raw frontmatter value
|
|
106
|
+
* @param {string} [file] path named in the WARN when a truthy-looking value is demoted
|
|
107
|
+
* @returns {boolean}
|
|
108
|
+
*/
|
|
109
|
+
export function isUserInvocableValue(value, file) {
|
|
110
|
+
if (value === true) return true;
|
|
111
|
+
if (typeof value !== 'string') return false;
|
|
112
|
+
const token = normaliseScalar(value);
|
|
113
|
+
if (token.toLowerCase() === 'true') return true;
|
|
114
|
+
if (TRUTHY_LOOKALIKE.test(token)) {
|
|
115
|
+
const where = file ? `${file}: ` : '';
|
|
116
|
+
process.stderr.write(
|
|
117
|
+
`WARN ${where}user-invocable: ${JSON.stringify(value)} is NOT a slash-command marker — `
|
|
118
|
+
+ 'only `true` is (YAML 1.2 reads yes/on/1 as strings). Write `user-invocable: true`.\n',
|
|
119
|
+
);
|
|
120
|
+
}
|
|
121
|
+
return false;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Parse a SKILL.md's YAML frontmatter. Returns `null` when there is no
|
|
126
|
+
* frontmatter block or it does not parse.
|
|
127
|
+
*
|
|
128
|
+
* @param {string} content
|
|
129
|
+
* @returns {Record<string, unknown>|null}
|
|
130
|
+
*/
|
|
131
|
+
export function parseSkillFrontmatter(content) {
|
|
132
|
+
if (typeof content !== 'string') return null;
|
|
133
|
+
const parsed = parseAgentFrontmatter(content);
|
|
134
|
+
return parsed.ok ? parsed.frontmatter : null;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Names of skills carrying an explicit `user-invocable: true`, sorted.
|
|
139
|
+
*
|
|
140
|
+
* @param {string} repoRoot plugin root (the directory holding `skills/`)
|
|
141
|
+
* @returns {string[]}
|
|
142
|
+
*/
|
|
143
|
+
export function userInvocableSkills(repoRoot) {
|
|
144
|
+
const dir = path.join(repoRoot, 'skills');
|
|
145
|
+
if (!existsSync(dir) || !statSync(dir).isDirectory()) return [];
|
|
146
|
+
const names = [];
|
|
147
|
+
for (const entry of readdirSync(dir, { withFileTypes: true })) {
|
|
148
|
+
if (!entry.isDirectory() || entry.name.startsWith('.')) continue;
|
|
149
|
+
const file = path.join(dir, entry.name, 'SKILL.md');
|
|
150
|
+
if (!existsSync(file) || !statSync(file).isFile()) continue;
|
|
151
|
+
let fm;
|
|
152
|
+
try {
|
|
153
|
+
fm = parseSkillFrontmatter(readFileSync(file, 'utf8'));
|
|
154
|
+
} catch {
|
|
155
|
+
continue;
|
|
156
|
+
}
|
|
157
|
+
if (fm && isUserInvocableValue(fm['user-invocable'], file)) names.push(entry.name);
|
|
158
|
+
}
|
|
159
|
+
return names.sort();
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/** Names of `commands/*.md` files (without the extension), sorted. */
|
|
163
|
+
export function commandFileNames(repoRoot) {
|
|
164
|
+
const dir = path.join(repoRoot, 'commands');
|
|
165
|
+
if (!existsSync(dir) || !statSync(dir).isDirectory()) return [];
|
|
166
|
+
return readdirSync(dir)
|
|
167
|
+
.filter((f) => f.endsWith('.md') && !f.startsWith('.'))
|
|
168
|
+
.map((f) => f.replace(/\.md$/, ''))
|
|
169
|
+
.sort();
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* The product's "slash commands" set: `commands/*.md` ∪ explicitly
|
|
174
|
+
* user-invocable skills, deduplicated by NAME.
|
|
175
|
+
*
|
|
176
|
+
* Dedup is load-bearing rather than defensive: a name present as BOTH is the
|
|
177
|
+
* picker-duplicate bug (the body runs, and the entry lists twice), so it must
|
|
178
|
+
* count once here and go red in the guard test — never inflate the number.
|
|
179
|
+
*
|
|
180
|
+
* @param {string} repoRoot
|
|
181
|
+
* @returns {string[]} sorted unique names
|
|
182
|
+
*/
|
|
183
|
+
export function slashCommandNames(repoRoot) {
|
|
184
|
+
return [...new Set([...commandFileNames(repoRoot), ...userInvocableSkills(repoRoot)])].sort();
|
|
185
|
+
}
|
|
@@ -46,8 +46,8 @@
|
|
|
46
46
|
import { execFileSync } from 'node:child_process';
|
|
47
47
|
import { existsSync, lstatSync, readFileSync, readdirSync, realpathSync } from 'node:fs';
|
|
48
48
|
import path from 'node:path';
|
|
49
|
-
import { pathToFileURL } from 'node:url';
|
|
50
49
|
import { HISTORICAL_GUARD_BANNER } from '../historical-guard.mjs';
|
|
50
|
+
import { isMainModule } from '../is-main-module.mjs';
|
|
51
51
|
|
|
52
52
|
/** Directories whose markdown quotes the banner as coordinator-facing prose. */
|
|
53
53
|
const SCAN_DIRS = Object.freeze(['skills', 'commands']);
|
|
@@ -365,7 +365,7 @@ export function runCheckBannerParity(pluginRoot) {
|
|
|
365
365
|
return inspection.toolError ? 2 : 1;
|
|
366
366
|
}
|
|
367
367
|
|
|
368
|
-
const isMain =
|
|
368
|
+
const isMain =isMainModule(import.meta.url);
|
|
369
369
|
if (isMain) {
|
|
370
370
|
const pluginRoot = process.argv[2];
|
|
371
371
|
if (!pluginRoot) {
|
|
@@ -6,11 +6,11 @@
|
|
|
6
6
|
import { spawnSync } from 'node:child_process';
|
|
7
7
|
import { existsSync, readdirSync, readFileSync, statSync } from 'node:fs';
|
|
8
8
|
import { join, relative } from 'node:path';
|
|
9
|
-
import { pathToFileURL } from 'node:url';
|
|
10
9
|
|
|
11
10
|
import yaml from 'js-yaml';
|
|
12
11
|
|
|
13
12
|
import { CURSOR_TO_CANONICAL_EVENT } from '../cursor-hook-bridge.mjs';
|
|
13
|
+
import { isMainModule } from '../is-main-module.mjs';
|
|
14
14
|
|
|
15
15
|
/** agentskills.io caps a skill/command description at 1024 characters. */
|
|
16
16
|
const DESCRIPTION_MAX = 1024;
|
|
@@ -259,6 +259,6 @@ function runCli(pluginRoot) {
|
|
|
259
259
|
}
|
|
260
260
|
|
|
261
261
|
// Direct invocation only — importing this module must not run the CLI.
|
|
262
|
-
if (
|
|
262
|
+
if (isMainModule(import.meta.url)) {
|
|
263
263
|
runCli(process.argv[2]);
|
|
264
264
|
}
|
|
@@ -22,12 +22,12 @@
|
|
|
22
22
|
// or process.exit — the isMain guard at the bottom is the only side-effecting
|
|
23
23
|
// path. The Quality wave imports runCheckDeadBridge directly for unit tests.
|
|
24
24
|
|
|
25
|
-
import { pathToFileURL } from 'node:url';
|
|
26
25
|
import { existsSync, readFileSync, readdirSync, statSync } from 'node:fs';
|
|
27
26
|
import path from 'node:path';
|
|
28
27
|
import { DETECTORS } from './dead-bridge-detectors.mjs';
|
|
29
28
|
import * as corpus from './dead-bridge-corpus.mjs';
|
|
30
29
|
import { listRepoFiles } from './repo-files.mjs';
|
|
30
|
+
import { isMainModule } from '../is-main-module.mjs';
|
|
31
31
|
|
|
32
32
|
const DEFAULT_EXTS = ['.mjs', '.md'];
|
|
33
33
|
|
|
@@ -191,7 +191,7 @@ export function runCheckDeadBridge(pluginRoot) {
|
|
|
191
191
|
|
|
192
192
|
// CLI entry — only when executed directly, never on import (keeps the exports
|
|
193
193
|
// safe to import from tests without triggering process.exit).
|
|
194
|
-
const isMain =
|
|
194
|
+
const isMain =isMainModule(import.meta.url);
|
|
195
195
|
if (isMain) {
|
|
196
196
|
const pluginRoot = process.argv[2];
|
|
197
197
|
if (!pluginRoot) {
|
|
@@ -65,9 +65,9 @@
|
|
|
65
65
|
import { readFileSync } from 'node:fs';
|
|
66
66
|
import path from 'node:path';
|
|
67
67
|
import { spawnSync } from 'node:child_process';
|
|
68
|
-
import { pathToFileURL } from 'node:url';
|
|
69
68
|
import { listRepoFiles } from './repo-files.mjs';
|
|
70
69
|
import { SHELL_LANGS, forEachLine } from './markdown-fences.mjs';
|
|
70
|
+
import { isMainModule } from '../is-main-module.mjs';
|
|
71
71
|
|
|
72
72
|
/** Directories walked for documentation. Mirrors check-vcs-repo-flag.mjs. */
|
|
73
73
|
const SCAN_DIRS = Object.freeze([
|
|
@@ -461,7 +461,7 @@ export function runCheckDocCliCommands(pluginRoot) {
|
|
|
461
461
|
return 0;
|
|
462
462
|
}
|
|
463
463
|
|
|
464
|
-
const isMain =
|
|
464
|
+
const isMain =isMainModule(import.meta.url);
|
|
465
465
|
if (isMain) {
|
|
466
466
|
const args = process.argv.slice(2).filter((arg) => arg !== '--json');
|
|
467
467
|
const root = path.resolve(args[0] || process.cwd());
|