session-orchestrator 3.17.0 → 3.19.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 +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/.codex-plugin/plugin.json +1 -1
- package/.cursor/rules/030-wave-execution.mdc +17 -1
- package/CHANGELOG.md +105 -412
- package/README.md +12 -9
- package/SECURITY.md +190 -27
- package/agents/AGENTS.md +20 -3
- package/agents/code-implementer.md +6 -6
- package/agents/db-specialist.md +1 -1
- package/agents/qa-strategist.md +31 -6
- package/agents/schemas/qa-strategist.schema.json +27 -0
- package/agents/schemas/test-writer.schema.json +60 -2
- package/agents/security-reviewer.md +1 -1
- package/agents/session-reviewer.md +1 -1
- package/agents/test-writer.md +29 -10
- package/agents/ui-developer.md +1 -1
- package/commands/contract-version-bump.md +28 -0
- package/commands/portfolio.md +1 -1
- package/docs/USER-GUIDE.md +8 -3
- package/docs/ci-setup.md +121 -7
- package/docs/codex-setup.md +1 -1
- package/docs/components.md +6 -6
- package/docs/cursor-setup.md +22 -9
- package/docs/events-schema.md +5 -1
- package/docs/instruction-delivery.md +444 -0
- package/docs/rule-authoring.md +58 -9
- package/docs/session-config-reference.md +244 -9
- package/docs/session-config-template.md +39 -3
- package/hooks/_lib/guard-source-loader.mjs +467 -0
- package/hooks/_lib/lock-bootstrap.mjs +21 -0
- package/hooks/_lib/vcs-create-matcher.mjs +119 -0
- package/hooks/config-protection.mjs +0 -0
- package/hooks/enforce-commands.mjs +10 -2
- package/hooks/hooks-codex.json +1 -1
- package/hooks/hooks-cursor.json +11 -2
- package/hooks/hooks-pi.json +10 -0
- package/hooks/hooks.json +21 -1
- package/hooks/on-session-end.mjs +178 -18
- package/hooks/on-session-start.mjs +23 -0
- package/hooks/post-bash-write-verify.mjs +977 -0
- package/hooks/post-subagent-discovery-validator.mjs +256 -41
- package/hooks/pre-bash-destructive-guard.mjs +525 -160
- package/hooks/pre-bash-issue-budget.mjs +167 -0
- package/hooks/pre-bash-sessions-ledger-guard.mjs +627 -0
- package/hooks/pre-bash-templates-first.mjs +96 -63
- package/hooks/subagent-telemetry.mjs +527 -37
- package/package.json +5 -2
- package/pi/prompts/contract-version-bump.md +12 -0
- package/rules/README.md +32 -0
- package/scripts/archive-closed-prds.mjs +12 -22
- package/scripts/autopilot-multi.mjs +103 -20
- package/scripts/backfill-abandoned-sessions.mjs +160 -4
- package/scripts/check-doc-consistency.sh +17 -1
- package/scripts/eval-session.mjs +50 -9
- package/scripts/fleet-instruction-scan.mjs +141 -0
- package/scripts/lib/autopilot/mr-draft.mjs +31 -1
- package/scripts/lib/autopilot/worktree-pipeline.mjs +113 -5
- package/scripts/lib/backlog-scan.mjs +39 -6
- package/scripts/lib/blocked-commands-policy.mjs +340 -0
- package/scripts/lib/ci-status-banner.mjs +75 -12
- package/scripts/lib/claude-md-budget-lint.mjs +283 -34
- package/scripts/lib/command-blocker.mjs +1013 -58
- package/scripts/lib/config/config-protection.mjs +2 -1
- package/scripts/lib/config/drift-check.mjs +9 -1
- package/scripts/lib/config/gitlab-portfolio.mjs +1 -1
- package/scripts/lib/config/issue-budget.mjs +123 -0
- package/scripts/lib/config/reconcile.mjs +21 -0
- package/scripts/lib/config/section-extractor.mjs +121 -1
- package/scripts/lib/config-schema.mjs +23 -3
- package/scripts/lib/config.mjs +17 -0
- package/scripts/lib/convergence-monitor.mjs +49 -3
- package/scripts/lib/description-surface.mjs +535 -0
- package/scripts/lib/dispatcher/enumerate.mjs +26 -40
- package/scripts/lib/ecosystem-wizard/config-writer.mjs +26 -24
- package/scripts/lib/ecosystem-wizard/wizard-prompt.mjs +1 -1
- package/scripts/lib/eval/engine.mjs +47 -5
- package/scripts/lib/events.mjs +59 -7
- package/scripts/lib/gates/gate-full.mjs +15 -3
- package/scripts/lib/gates/gate-helpers.mjs +132 -6
- package/scripts/lib/gitlab-ops/stale-mr-sweep.mjs +28 -8
- package/scripts/lib/gitlab-portfolio/aggregator.mjs +8 -2
- package/scripts/lib/gitlab-portfolio/cli.mjs +1 -1
- package/scripts/lib/handover-gate.mjs +7 -3
- package/scripts/lib/harness-audit/categories/category4.mjs +9 -3
- package/scripts/lib/instruction-budget-guard.mjs +402 -51
- package/scripts/lib/io.mjs +345 -10
- package/scripts/lib/issue-budget.mjs +269 -0
- package/scripts/lib/issue-close-strip-labels.mjs +39 -9
- package/scripts/lib/label-scope.mjs +47 -0
- package/scripts/lib/learnings/schema.mjs +43 -3
- package/scripts/lib/lock-reaper.mjs +1 -2
- package/scripts/lib/memory-proposals/schema.mjs +36 -1
- package/scripts/lib/peer-discovery.mjs +645 -0
- package/scripts/lib/pi-hook-bridge.mjs +146 -17
- package/scripts/lib/product-repo-detect.mjs +9 -8
- package/scripts/lib/project-hygiene.mjs +432 -0
- package/scripts/lib/quality-gate.mjs +167 -0
- package/scripts/lib/recommendations-v0.mjs +1 -1
- package/scripts/lib/reconcile/eligibility.mjs +1 -1
- package/scripts/lib/reconcile/emitter.mjs +23 -4
- package/scripts/lib/reconcile/engine.mjs +147 -39
- package/scripts/lib/reconcile/idempotency.mjs +114 -14
- package/scripts/lib/reconcile-nudge-banner.mjs +65 -9
- package/scripts/lib/resource-probe/evaluate.mjs +70 -4
- package/scripts/lib/resource-probe.mjs +19 -0
- package/scripts/lib/rule-loader.mjs +6 -0
- package/scripts/lib/scope-baseline.mjs +564 -0
- package/scripts/lib/scope-gate.mjs +399 -98
- package/scripts/lib/session-close-backfill.mjs +61 -6
- package/scripts/lib/session-end/phase-skip.mjs +1 -0
- package/scripts/lib/session-id.mjs +221 -41
- package/scripts/lib/session-lock.mjs +304 -6
- package/scripts/lib/session-schema/constants.mjs +22 -3
- package/scripts/lib/session-schema/validator.mjs +16 -0
- package/scripts/lib/sessions-integrity-banner.mjs +294 -0
- package/scripts/lib/sessions-staleness-banner.mjs +121 -12
- package/scripts/lib/skill-evolution/idempotency.mjs +135 -16
- package/scripts/lib/skill-evolution/mr-opener.mjs +9 -1
- package/scripts/lib/spiral-carryover.mjs +142 -30
- package/scripts/lib/state-md/mission-status.mjs +53 -3
- package/scripts/lib/subagents-schema.mjs +43 -9
- package/scripts/lib/test-runner/issue-reconcile.mjs +53 -13
- package/scripts/lib/tests-src-ratio.mjs +484 -0
- package/scripts/lib/validate/check-agents.mjs +56 -0
- package/scripts/lib/validate/check-hooks-symmetry.mjs +244 -10
- package/scripts/lib/validate/check-rules.mjs +217 -35
- package/scripts/lib/validate/check-test-value-bans.mjs +782 -0
- package/scripts/lib/validate/check-unicode-safety.mjs +1 -0
- package/scripts/lib/validate-vendored-rules.mjs +10 -2
- package/scripts/lib/vault-archive.mjs +17 -2
- package/scripts/lib/vault-backfill/glab.mjs +8 -0
- package/scripts/lib/vault-mirror/process.mjs +30 -0
- package/scripts/lib/vault-mirror/render-sessions.mjs +293 -36
- package/scripts/lib/vcs-repo-spec.mjs +362 -0
- package/scripts/lib/wave-resource-gate.mjs +115 -11
- package/scripts/lib/worktree/listing.mjs +44 -7
- package/scripts/mcp-server.sh +17 -3
- package/scripts/measure-context-overhead.sh +151 -0
- package/scripts/memory-propose.mjs +72 -9
- package/scripts/print-applicable-rules.mjs +51 -12
- package/scripts/release.mjs +534 -0
- package/scripts/run-quality-gate.mjs +123 -5
- package/scripts/validate-wave-scope.mjs +182 -17
- package/scripts/vault-integration-watcher.mjs +32 -10
- package/skills/_shared/config-reading.md +2 -2
- package/skills/bootstrap/fast-template.md +1 -1
- package/skills/claude-md-drift-check/checker.mjs +145 -28
- package/skills/contract-version-bump/SKILL.md +219 -0
- package/skills/discovery/SKILL.md +4 -4
- package/skills/discovery/issue-templates.md +11 -11
- package/skills/discovery/probes-audit.md +1 -1
- package/skills/discovery/probes-feature.md +1 -1
- package/skills/discovery/probes-session.md +26 -5
- package/skills/ecosystem-health/SKILL.md +1 -1
- package/skills/ecosystem-health/wizard.md +4 -4
- package/skills/evolve/SKILL.md +1 -0
- package/skills/gitlab-ops/SKILL.md +20 -12
- package/skills/gitlab-portfolio/SKILL.md +2 -2
- package/skills/hook-development/SKILL.md +1 -1
- package/skills/mode-selector/SKILL.md +1 -1
- package/skills/npm-publish/SKILL.md +17 -1
- package/skills/plan/SKILL.md +5 -5
- package/skills/plan/mode-feature.md +4 -4
- package/skills/plan/mode-new.md +10 -10
- package/skills/plan/mode-retro.md +1 -1
- package/skills/quality-gates/SKILL.md +1 -1
- package/skills/reconcile/SKILL.md +21 -4
- package/skills/session-end/SKILL.md +34 -13
- package/skills/session-end/discovery-scan.md +4 -2
- package/skills/session-end/drift-operations.md +4 -4
- package/skills/session-end/metrics-collection.md +13 -0
- package/skills/session-end/phase-3-2-docs-verification.md +1 -1
- package/skills/session-end/phase-3-6-tail.md +2 -1
- package/skills/session-end/plan-verification.md +5 -2
- package/skills/session-end/vault-operations.md +1 -1
- package/skills/session-end/verification-checklist.md +1 -1
- package/skills/session-plan/SKILL.md +6 -2
- package/skills/session-plan/wave-template.md +2 -0
- package/skills/session-start/SKILL.md +73 -7
- package/skills/session-start/phase-4-5-resource-health.md +15 -2
- package/skills/test-runner/SKILL.md +2 -2
- package/skills/vault-sync/validator.mjs +108 -7
- package/skills/wave-executor/SKILL.md +5 -2
- package/skills/wave-executor/circuit-breaker.md +2 -0
- package/skills/wave-executor/wave-loop.md +163 -10
- package/templates/_shared/loop.md +4 -4
|
@@ -22,8 +22,8 @@
|
|
|
22
22
|
* G7 transcript inspection via hooks/_lib/transcript-history.mjs.
|
|
23
23
|
* If any prior Read tool call matches one of the host-specific template
|
|
24
24
|
* paths from the policy, exit 0.
|
|
25
|
-
* G8 fall-through: emit
|
|
26
|
-
* template
|
|
25
|
+
* G8 fall-through: emit the PreToolUse deny envelope on stdout via emitDeny
|
|
26
|
+
* (exit 0) with the template-path list + ack hint inside the reason.
|
|
27
27
|
*
|
|
28
28
|
* Fail-safe posture: any internal exception is swallowed in main().catch and
|
|
29
29
|
* the hook exits 0 (allow). Rationale matches pre-bash-destructive-guard.mjs:
|
|
@@ -31,14 +31,17 @@
|
|
|
31
31
|
* worst case is a missed enforcement, not a wedged session.
|
|
32
32
|
*
|
|
33
33
|
* Exit codes:
|
|
34
|
-
* 0 —
|
|
35
|
-
*
|
|
34
|
+
* 0 — every path. Pass-through (G1-G3 short-circuits, bypass match,
|
|
35
|
+
* acknowledgement, Read found, error) emits nothing; G8 emits the deny
|
|
36
|
+
* envelope on stdout. Exit 2 is NEVER used: Claude Code discards stdout
|
|
37
|
+
* on exit 2, which would throw away the deny envelope entirely (#906).
|
|
36
38
|
*/
|
|
37
39
|
|
|
38
|
-
import { readStdin, emitAllow } from '../scripts/lib/io.mjs';
|
|
40
|
+
import { readStdin, emitAllow, emitDeny } from '../scripts/lib/io.mjs';
|
|
39
41
|
import { resolveProjectDir, resolvePluginRoot } from '../scripts/lib/platform.mjs';
|
|
40
42
|
import { readJson } from '../scripts/lib/common.mjs';
|
|
41
43
|
import { hasReadInSession } from './_lib/transcript-history.mjs';
|
|
44
|
+
import { resolveHost, matchesBypass } from './_lib/vcs-create-matcher.mjs';
|
|
42
45
|
|
|
43
46
|
import { shouldRunHook } from './_lib/profile-gate.mjs';
|
|
44
47
|
import { existsSync, readdirSync, lstatSync } from 'node:fs';
|
|
@@ -54,13 +57,11 @@ if (!shouldRunHook('pre-bash-templates-first')) process.exit(0);
|
|
|
54
57
|
// ---------------------------------------------------------------------------
|
|
55
58
|
|
|
56
59
|
/**
|
|
57
|
-
*
|
|
58
|
-
*
|
|
59
|
-
*
|
|
60
|
-
*
|
|
61
|
-
* are deliberately out of scope per PRD § 2 Out-of-Scope.
|
|
60
|
+
* The `gh`/`glab` create matcher (`CREATE_REGEX`, `resolveHost`,
|
|
61
|
+
* `matchesBypass`) now lives in `hooks/_lib/vcs-create-matcher.mjs` so the
|
|
62
|
+
* sibling issue-budget hook parses create commands with byte-identical
|
|
63
|
+
* semantics instead of a diverging copy. Behaviour here is unchanged.
|
|
62
64
|
*/
|
|
63
|
-
const CREATE_REGEX = /^\s*(gh|glab)\s+(pr|mr|issue)\s+(create|new)\b/;
|
|
64
65
|
|
|
65
66
|
/**
|
|
66
67
|
* Default acknowledgement path, relative to project root. Used when the
|
|
@@ -68,14 +69,91 @@ const CREATE_REGEX = /^\s*(gh|glab)\s+(pr|mr|issue)\s+(create|new)\b/;
|
|
|
68
69
|
*/
|
|
69
70
|
const DEFAULT_ACK_PATH = '.orchestrator/runtime/templates-acknowledged.json';
|
|
70
71
|
|
|
72
|
+
/**
|
|
73
|
+
* Ceiling (in characters) for the raw bash command echoed into the deny reason.
|
|
74
|
+
*
|
|
75
|
+
* ## Why this file needs its own bound at all (#919, follow-up to #906)
|
|
76
|
+
*
|
|
77
|
+
* `command` is the ONE attacker/agent-controlled term in the reason — every
|
|
78
|
+
* other line is fixed text or a repo-derived path list. Echoing it unbounded
|
|
79
|
+
* made this hook the shortest path to the failure mode #906 was repaired for:
|
|
80
|
+
* a reason large enough to push the stdout envelope past the 65 536-byte kernel
|
|
81
|
+
* pipe buffer, where a truncated envelope reads as "no decision" and the tool
|
|
82
|
+
* call is ALLOWED. `emitDeny`'s {@link DENY_REASON_MAX} clamp (16 000) now
|
|
83
|
+
* stands in that path, but a consumer that respects the bound itself is the
|
|
84
|
+
* more robust shape — defence in depth, not reliance on the single downstream
|
|
85
|
+
* clamp.
|
|
86
|
+
*
|
|
87
|
+
* The clamp alone is also NOT sufficient here, which is the concrete bug this
|
|
88
|
+
* constant fixes rather than merely hardens against. `Command:` is line 2 of 6;
|
|
89
|
+
* the template-path list and the `/templates-ack` hint are lines 3-6. A
|
|
90
|
+
* 200 000-char command therefore consumed the entire 16 000-char budget and cut
|
|
91
|
+
* the remedy off the end — the deny still bit, but PRD § 3 Gherkin Pattern 3's
|
|
92
|
+
* required content never reached the reader.
|
|
93
|
+
*
|
|
94
|
+
* ## Why 512 and not the 80 used at the two stderr sites below
|
|
95
|
+
*
|
|
96
|
+
* This file already truncates `command` twice (bypass-matched and
|
|
97
|
+
* no-templates-found), both at `slice(0, 80)`. Those are one-line **stderr log**
|
|
98
|
+
* lines, where 80 is the terminal-width convention — a different consumer class.
|
|
99
|
+
* The repo's precedent for bounding a bash command inside a **structured record
|
|
100
|
+
* field** is `hooks/pre-bash-staging-fence.mjs` (`staged_paths[].command`,
|
|
101
|
+
* `slice(0, 512)`), and `permissionDecisionReason` is exactly that: a structured
|
|
102
|
+
* field, not a log line. Reusing 512 follows the matching house convention
|
|
103
|
+
* instead of inventing a third number. Reusing 80 would clip a realistic
|
|
104
|
+
* `gh pr create --title … --body …` mid-flag, defeating the recognisability the
|
|
105
|
+
* line exists to provide.
|
|
106
|
+
*
|
|
107
|
+
* Bound check: the fixed part of this reason measures 348 chars (see the
|
|
108
|
+
* call-site table in `scripts/lib/io.mjs`), so the worst case is 348 + 512 +
|
|
109
|
+
* the path list — ~19× below `DENY_REASON_MAX` and ~76× below the pipe buffer.
|
|
110
|
+
* This hook no longer contributes an unbounded term to the envelope at all.
|
|
111
|
+
*/
|
|
112
|
+
const COMMAND_ECHO_MAX = 512;
|
|
113
|
+
|
|
71
114
|
// ---------------------------------------------------------------------------
|
|
72
115
|
// Helpers
|
|
73
116
|
// ---------------------------------------------------------------------------
|
|
74
117
|
|
|
75
118
|
/**
|
|
76
|
-
*
|
|
77
|
-
*
|
|
78
|
-
*
|
|
119
|
+
* Clip the echoed command to {@link COMMAND_ECHO_MAX}, marking the cut so the
|
|
120
|
+
* reader can tell the command was truncated rather than ending there.
|
|
121
|
+
*
|
|
122
|
+
* The marker is budgeted INSIDE the ceiling — mirroring `_clampReason` in
|
|
123
|
+
* `scripts/lib/io.mjs` — so the returned string never exceeds it and the
|
|
124
|
+
* worst-case reason length stays a fixed, auditable number.
|
|
125
|
+
*
|
|
126
|
+
* @param {string} command
|
|
127
|
+
* @returns {string}
|
|
128
|
+
*/
|
|
129
|
+
function clipCommand(command) {
|
|
130
|
+
if (command.length <= COMMAND_ECHO_MAX) return command;
|
|
131
|
+
const marker = `… [truncated: showing ${COMMAND_ECHO_MAX} of ${command.length} characters]`;
|
|
132
|
+
return command.slice(0, COMMAND_ECHO_MAX - marker.length) + marker;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Block the create command: emit the PreToolUse deny envelope via emitDeny
|
|
137
|
+
* (exit 0). Mirrors pre-bash-destructive-guard.mjs blockCommand() so downstream
|
|
138
|
+
* Claude Code rendering is consistent.
|
|
139
|
+
*
|
|
140
|
+
* PRD § 3 Gherkin Pattern 3 requires the template-path list plus the
|
|
141
|
+
* `/templates-ack` hint to reach the reader. That requirement is UNCHANGED —
|
|
142
|
+
* only the channel moved (#906). Until #906 this text was written to stderr AND
|
|
143
|
+
* duplicated into an `exit 2` stdout envelope, the mixed form the hook docs
|
|
144
|
+
* forbid ("choose one approach per hook, not both"). Under exit 0, stderr is
|
|
145
|
+
* only surfaced in the debug log — invisible to both operator and model — so a
|
|
146
|
+
* stderr write would look alive while being dead. The full multi-line text
|
|
147
|
+
* therefore travels as the emitDeny `reason`, landing in
|
|
148
|
+
* `permissionDecisionReason`, which is fed to **Claude** — the actor that has
|
|
149
|
+
* to read the template or run `/templates-ack`. The operator sees the first
|
|
150
|
+
* line via the derived `systemMessage` headline.
|
|
151
|
+
*
|
|
152
|
+
* The echoed `command` is bounded by {@link COMMAND_ECHO_MAX} before it enters
|
|
153
|
+
* the reason: it is the only agent-controlled term here, and left unbounded it
|
|
154
|
+
* both risked the pipe-buffer fail-open #906 repaired AND pushed the template
|
|
155
|
+
* list plus the ack hint past `emitDeny`'s clamp — i.e. truncated away exactly
|
|
156
|
+
* the content the PRD requires. See that constant for the full rationale.
|
|
79
157
|
*
|
|
80
158
|
* @param {{ host: string, command: string, templatePaths: string[],
|
|
81
159
|
* ackFile: string }} ctx
|
|
@@ -88,21 +166,16 @@ function blockCreate(ctx) {
|
|
|
88
166
|
: ' (none configured)';
|
|
89
167
|
const reason = [
|
|
90
168
|
`pre-bash-templates-first: ${host} create call detected without prior template Read.`,
|
|
91
|
-
`Command: ${command}`,
|
|
169
|
+
`Command: ${clipCommand(command)}`,
|
|
92
170
|
`Found templates:`,
|
|
93
171
|
pathList,
|
|
94
172
|
`Read one of these first, OR run \`/templates-ack\` (writes ${ackFile}) to bypass for this session.`,
|
|
95
173
|
`See: issue #519, "gsd Pattern Adoption Quick-Wins" (archived in the private Meta-Vault) (Pattern 3)`,
|
|
96
174
|
].join('\n');
|
|
97
175
|
|
|
98
|
-
//
|
|
99
|
-
//
|
|
100
|
-
|
|
101
|
-
process.stderr.write(reason + '\n');
|
|
102
|
-
process.stdout.write(
|
|
103
|
-
JSON.stringify({ permissionDecision: 'deny', reason }) + '\n',
|
|
104
|
-
);
|
|
105
|
-
process.exit(2);
|
|
176
|
+
// Single channel: the full multi-line reason (template paths + ack hint)
|
|
177
|
+
// rides in permissionDecisionReason. Never returns.
|
|
178
|
+
emitDeny(reason);
|
|
106
179
|
}
|
|
107
180
|
|
|
108
181
|
/**
|
|
@@ -142,46 +215,6 @@ function resolvePolicyPath(projectDir) {
|
|
|
142
215
|
return null;
|
|
143
216
|
}
|
|
144
217
|
|
|
145
|
-
/**
|
|
146
|
-
* Determine which host the command targets by inspecting the CREATE_REGEX
|
|
147
|
-
* capture. `gh` → "github", `glab` → "gitlab".
|
|
148
|
-
*
|
|
149
|
-
* @param {string} command
|
|
150
|
-
* @returns {"github"|"gitlab"|null}
|
|
151
|
-
*/
|
|
152
|
-
function resolveHost(command) {
|
|
153
|
-
const m = command.match(CREATE_REGEX);
|
|
154
|
-
if (!m) return null;
|
|
155
|
-
return m[1] === 'gh' ? 'github' : 'gitlab';
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
/**
|
|
159
|
-
* True when the command starts with any of the bypass patterns. Bypass match
|
|
160
|
-
* is a prefix check with a word/EOL boundary on the trailing edge — this
|
|
161
|
-
* prevents trivial bypass via prefix-inclusion (e.g. policy entry
|
|
162
|
-
* "gh issue create --label bot" must not match "gh issue create --label botanical").
|
|
163
|
-
*
|
|
164
|
-
* @param {string} command
|
|
165
|
-
* @param {string[]} bypassPatterns
|
|
166
|
-
* @returns {boolean}
|
|
167
|
-
*/
|
|
168
|
-
function matchesBypass(command, bypassPatterns) {
|
|
169
|
-
if (!Array.isArray(bypassPatterns) || bypassPatterns.length === 0) {
|
|
170
|
-
return false;
|
|
171
|
-
}
|
|
172
|
-
const stripped = command.replace(/^\s+/, '');
|
|
173
|
-
for (const pat of bypassPatterns) {
|
|
174
|
-
if (typeof pat !== 'string' || pat.length === 0) continue;
|
|
175
|
-
const patStripped = pat.replace(/^\s+/, '');
|
|
176
|
-
if (!stripped.startsWith(patStripped)) continue;
|
|
177
|
-
// Boundary check: next character must be whitespace, EOL, or absent.
|
|
178
|
-
// This prevents "gh foo --label bot" from matching policy "gh foo --label botanical".
|
|
179
|
-
const nextChar = stripped.charAt(patStripped.length);
|
|
180
|
-
if (nextChar === '' || /\s/.test(nextChar)) return true;
|
|
181
|
-
}
|
|
182
|
-
return false;
|
|
183
|
-
}
|
|
184
|
-
|
|
185
218
|
/**
|
|
186
219
|
* Check the acknowledgement file for the current session_id. Best-effort:
|
|
187
220
|
* any read or parse error means "no acknowledgement" (returns false) so the
|