Skip to content

Commit 7e5fa43

Browse files
committed
chore(fleet): refresh mirrors from bundle 1.0.13
1 parent 066aa6c commit 7e5fa43

152 files changed

Lines changed: 8820 additions & 5321 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.claude/hooks/fleet/_dispatch/dispatch.mts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,8 @@ export interface DispatchHookEntry {
138138
/**
139139
* Legacy pure entry: returns reminder text to surface on stderr, or
140140
* `undefined`. Mutually exclusive with `check` — exactly one is set per entry.
141-
* (bundle-stale-reminder style.)
141+
* No current hook uses it — every bundled hook is a `defineHook` `check`;
142+
* the seam stays for a future legacy port.
142143
*/
143144
readonly run?: (payload: DispatchPayload) => string | undefined
144145
/**
@@ -177,7 +178,7 @@ export const SINGLE_HOOK_NOT_FOUND_EXIT = 3
177178
* source).
178179
*/
179180
export function hookInTable(event: string, hookName: string): boolean {
180-
const entries = DISPATCH_TABLE[event] ?? []
181+
const entries: readonly DispatchHookEntry[] = DISPATCH_TABLE[event] ?? []
181182
return entries.some(entry => entry.name === hookName)
182183
}
183184

@@ -234,7 +235,7 @@ export async function dispatch(
234235
payload: DispatchPayload,
235236
onlyHook?: string | undefined,
236237
): Promise<DispatchResult> {
237-
const all = DISPATCH_TABLE[event] ?? []
238+
const all: readonly DispatchHookEntry[] = DISPATCH_TABLE[event] ?? []
238239
const entries =
239240
onlyHook === undefined ? all : all.filter(entry => entry.name === onlyHook)
240241
const reminders: string[] = []

.claude/hooks/fleet/_shared/ast/core.mts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,16 @@ export interface ParseOptions {
5252
* model.
5353
*/
5454
comments?: boolean | undefined
55+
/**
56+
* Attach a 1-based `loc` object (start/end line + column) to every node.
57+
* Honored by the underlying parser at runtime; default `false`.
58+
*/
59+
locations?: boolean | undefined
60+
/**
61+
* Attach a `range` `[start, end]` tuple to every node. Honored by the
62+
* underlying parser at runtime; default `false`.
63+
*/
64+
ranges?: boolean | undefined
5565
}
5666

5767
export interface CallSite {
@@ -73,6 +83,8 @@ export const DEFAULT_PARSE_OPTIONS: Required<ParseOptions> = {
7383
comments: false,
7484
ecmaVersion: 2026,
7585
jsx: false,
86+
locations: false,
87+
ranges: false,
7688
sourceType: 'module',
7789
typescript: true,
7890
}

.claude/hooks/fleet/_shared/es-polyfills.mts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -426,7 +426,7 @@ function validateTypedArrayReceiver(receiver: unknown): TypedArrayInstance {
426426
if (!ArrayBuffer.isView(receiver) || receiver instanceof DataView) {
427427
throw new TypeError('Receiver must be a TypedArray')
428428
}
429-
return receiver as TypedArrayInstance
429+
return receiver as unknown as TypedArrayInstance
430430
}
431431

432432
// 23.2.3.32 %TypedArray%.prototype.toReversed.

.claude/hooks/fleet/alpha-sort-nudge/index.mts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ function isAscadSorted(keys: readonly string[]): boolean {
4949
function indentOf(line: string): number {
5050
const m = line.match(/^(?<indent>\s*)/)
5151
/* c8 ignore next - regex always matches, fallback is unreachable */
52-
return m ? m.groups!.indent!.length : 0
52+
return m ? m.groups!['indent']!.length : 0
5353
}
5454

5555
// Walk lines, grouping maximal runs of lines that (a) match `keyFor` to a
@@ -94,7 +94,7 @@ function scanRuns(
9494
// JSON / JSONC object keys: `"name": ...` (allow trailing comma).
9595
function jsonKey(line: string): string | undefined {
9696
const m = line.match(/^\s*"(?<key>[^"]+)"\s*:/)
97-
return m ? m.groups?.key : undefined
97+
return m ? m.groups?.['key'] : undefined
9898
}
9999

100100
// YAML mapping keys: `name:` at line start (not a `- ` sequence item, not a
@@ -104,7 +104,7 @@ function yamlKey(line: string): string | undefined {
104104
return undefined
105105
}
106106
const m = line.match(/^\s*(?<key>[A-Za-z0-9_.-]+)\s*:(?:\s|$)/)
107-
return m ? m.groups?.key : undefined
107+
return m ? m.groups?.['key'] : undefined
108108
}
109109

110110
// Markdown bullets: `- text` / `* text`. Returns the text after the marker.
@@ -114,13 +114,13 @@ function mdBullet(line: string): string | undefined {
114114
return undefined
115115
}
116116
// Skip task-list checkboxes and nested numbered intent.
117-
return m.groups!.text!.toLowerCase()
117+
return m.groups!['text']!.toLowerCase()
118118
}
119119

120120
// Bash all-caps assignments: `NAME=...` (cache-key var style).
121121
function bashAssign(line: string): string | undefined {
122122
const m = line.match(/^\s*(?<name>[A-Z][A-Z0-9_]+)=/)
123-
return m ? m.groups?.name : undefined
123+
return m ? m.groups?.['name'] : undefined
124124
}
125125

126126
/**

.claude/hooks/fleet/avoid-cd-nudge/index.mts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ function detectsBareCd(command: string): boolean {
4242
const cdRe = /(?:^|[\s;&|])cd\s+(?<target>\S+)/g
4343
let m: RegExpExecArray | null
4444
while ((m = cdRe.exec(flat)) !== null) {
45-
const target = m.groups!.target!
45+
const target = m.groups!['target']!
4646

4747
// Skip `cd -` (intentional return).
4848
if (target === '-') {

.claude/hooks/fleet/broken-hook-detector/index.mts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -317,11 +317,11 @@ export function parseMissingPackages(stderr: string): readonly string[] {
317317
const pkgs = new Set<string>()
318318
// ESM form: Cannot find package '<name>' …
319319
for (const m of stderr.matchAll(/Cannot find package '(?<pkg>[^']+)'/g)) {
320-
pkgs.add(m.groups!.pkg!)
320+
pkgs.add(m.groups!['pkg']!)
321321
}
322322
// CJS form: Cannot find module '<name>'
323323
for (const m of stderr.matchAll(/Cannot find module '(?<pkg>[^']+)'/g)) {
324-
const name = m.groups!.pkg!
324+
const name = m.groups!['pkg']!
325325
// Skip relative + absolute paths (those are import-path bugs, not
326326
// missing-dep bugs, and the user can't `pnpm i` a relative path).
327327
if (!name.startsWith('.') && !name.startsWith('/')) {

.claude/hooks/fleet/bundle-stale-reminder/README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ After any `Edit` or `Write`:
1212

1313
1. Decide whether the edited path is a hook-bundle source.
1414
2. Compare the source's mtime against `_dist/bundle.cjs` — a missing bundle counts as stale.
15-
3. If stale, print a rebuild reminder to stderr. It never blocks (PostToolUse can't reject the prior call); it always exits 0.
15+
3. If stale, return a `notify` verdict — the reminder lands on stderr. It never blocks (PostToolUse can't reject the prior call); it always exits 0.
1616

1717
Rebuild with:
1818

@@ -22,7 +22,7 @@ node scripts/fleet/build-hook-bundle.mts
2222

2323
## Bypass
2424

25-
Type `Allow hook-bundle-current bypass` to silence the reminder when the rebuild is genuinely deferred.
25+
Type `Allow hook-bundle-current bypass` to silence the reminder when the rebuild is genuinely deferred. The slug is declared as `bypass` metadata on the hook's `defineHook` spec, so the framework wires phrase detection and appends the uniform footer naming the exact phrase — the message never hand-writes it.
2626

2727
## Test
2828

.claude/hooks/fleet/bundle-stale-reminder/index.mts

Lines changed: 45 additions & 98 deletions
Original file line numberDiff line numberDiff line change
@@ -3,43 +3,33 @@
33
//
44
// renamed-from: bundle-stale-guard
55
//
6-
// Mirrors extension-build-current-reminder. Fires after an Edit/Write whose
7-
// path is a hook-bundle SOURCE: the `_dispatch/` dispatcher, the generated
8-
// `dispatch-table.mts`, any bundled hook's `index.mts`, or anything under
9-
// `_shared/`. When the edited source is NEWER than the built
10-
// `_dist/bundle.cjs`, the bundle is stale and the operator is reminded to
11-
// rebuild it with `node scripts/fleet/build-hook-bundle.mts`.
6+
// Fires after an Edit/Write whose path is a hook-bundle SOURCE: the
7+
// `_dispatch/` dispatcher, the generated `dispatch-table.mts`, any bundled
8+
// hook's `index.mts`, or anything under `_shared/`. When the edited source is
9+
// NEWER than the built `_dist/bundle.cjs`, the bundle is stale and the
10+
// operator is reminded to rebuild it with
11+
// `node scripts/fleet/build-hook-bundle.mts`.
1212
//
13-
// The hook is a REMINDER, never a block: it only writes to stderr and always
14-
// exits 0. PostToolUse can't reject the prior tool call anyway.
13+
// The hook is a REMINDER, never a block: it returns a notify verdict, so the
14+
// tool call always proceeds. PostToolUse can't reject the prior tool call
15+
// anyway.
1516
//
16-
// Bypass: `Allow hook-bundle-current bypass` (silences the reminder when the
17-
// rebuild is genuinely deferred). See docs/agents.md/fleet/hook-bundle.md.
17+
// Bypass: the `hook-bundle-current` slug is declared as `bypass` metadata on
18+
// defineHook — the framework wires phrase detection and the uniform footer
19+
// from that one array, so typing the phrase silences the reminder when the
20+
// rebuild is genuinely deferred. See docs/agents.md/fleet/hook-bundle.md.
1821

1922
import { existsSync, statSync } from 'node:fs'
2023
import path from 'node:path'
21-
import process from 'node:process'
2224

2325
import { normalizePath } from '@socketsecurity/lib-stable/paths/normalize'
2426

25-
import { isHookEntrypoint } from '../_shared/entrypoint.mts'
26-
import { bypassPhrasePresent } from '../_shared/transcript.mts'
27+
import { defineHook, notify, runHook } from '../_shared/guard.mts'
28+
import type { GuardResult } from '../_shared/guard.mts'
29+
import { readFilePath } from '../_shared/payload.mts'
30+
import type { ToolCallPayload } from '../_shared/payload.mts'
2731
import { resolveProjectDir } from '../_shared/project-dir.mts'
2832

29-
export interface BundleStalePayload {
30-
readonly cwd?: string | undefined
31-
readonly hook_event_name?: string | undefined
32-
readonly tool_input?: { readonly file_path?: unknown | undefined } | undefined
33-
readonly tool_name?: string | undefined
34-
readonly transcript_path?: string | undefined
35-
}
36-
37-
// Read by scripts/fleet/gen/hook-dispatch.mts to place this hook in the
38-
// static dispatch table (the bundled fast-path). PostToolUse, Edit|Write.
39-
export const DISPATCH_EVENT = 'PostToolUse'
40-
export const DISPATCH_TOOLS: readonly string[] = ['Edit', 'Write']
41-
42-
const BYPASS_PHRASE = 'Allow hook-bundle-current bypass'
4333
const BUNDLE_REL = '.claude/hooks/fleet/_dist/bundle.cjs'
4434
// The wheelhouse holds TWO bundles: the live one (above) and the cascaded
4535
// canonical under template/base/. A hook-source edit leaves both stale until
@@ -144,47 +134,40 @@ export function bundleIsStale(
144134
}
145135

146136
/**
147-
* Builds the multi-line stderr reminder.
137+
* Builds the multi-line reminder. The bypass instruction is NOT part of the
138+
* message — defineHook appends the uniform footer from the `bypass` metadata.
148139
*/
149140
export function formatReminder(sourceRel: string): string {
150-
return (
151-
[
152-
`[bundle-stale-reminder] Edited a hook-bundle source without rebuilding the bundle.`,
153-
``,
154-
` Source: ${sourceRel}`,
155-
` Bundle: ${BUNDLE_REL}`,
156-
` (+ ${TEMPLATE_BUNDLE_REL} in the wheelhouse) — missing or older than the source`,
157-
``,
158-
` Rebuild so warm hook dispatch loads current code:`,
159-
` node scripts/fleet/build-hook-bundle.mts`,
160-
``,
161-
` Deferring the rebuild on purpose? Type "${BYPASS_PHRASE}".`,
162-
].join('\n') + '\n'
163-
)
141+
return [
142+
`[bundle-stale-reminder] Edited a hook-bundle source without rebuilding the bundle.`,
143+
``,
144+
` Source: ${sourceRel}`,
145+
` Bundle: ${BUNDLE_REL}`,
146+
` (+ ${TEMPLATE_BUNDLE_REL} in the wheelhouse) — missing or older than the source`,
147+
``,
148+
` Rebuild so warm hook dispatch loads current code:`,
149+
` node scripts/fleet/build-hook-bundle.mts`,
150+
].join('\n')
164151
}
165152

166153
/**
167154
* Core hook logic, decoupled from process I/O so the dispatcher bundle can
168-
* call it directly. Returns the reminder text when the bundle is stale, or
169-
* undefined when there is nothing to say.
155+
* call it via the `check` seam. Returns a notify verdict when the bundle is
156+
* stale, or undefined when there is nothing to say.
170157
*/
171-
export function run(payload: BundleStalePayload): string | undefined {
172-
if (payload.hook_event_name && payload.hook_event_name !== 'PostToolUse') {
158+
export const check = (payload: ToolCallPayload): GuardResult => {
159+
const eventName = (payload as { hook_event_name?: unknown | undefined })
160+
.hook_event_name
161+
if (eventName && eventName !== 'PostToolUse') {
173162
return undefined
174163
}
175164
if (payload.tool_name !== 'Edit' && payload.tool_name !== 'Write') {
176165
return undefined
177166
}
178-
const filePath =
179-
typeof payload.tool_input?.file_path === 'string'
180-
? payload.tool_input.file_path
181-
: ''
167+
const filePath = readFilePath(payload)
182168
if (!filePath || !isBundledSource(filePath)) {
183169
return undefined
184170
}
185-
if (bypassPhrasePresent(payload.transcript_path, BYPASS_PHRASE)) {
186-
return undefined
187-
}
188171
const cwd = resolveProjectDir(
189172
typeof payload.cwd === 'string' ? payload.cwd : undefined,
190173
)
@@ -199,50 +182,14 @@ export function run(payload: BundleStalePayload): string | undefined {
199182
return undefined
200183
}
201184
const sourceRel = path.relative(repoRoot, sourceAbs) || filePath
202-
return formatReminder(sourceRel)
203-
}
204-
205-
/* c8 ignore start - process-entrypoint I/O; only reachable when invoked as main script */
206-
async function readStdin(): Promise<string> {
207-
let raw = ''
208-
for await (const chunk of process.stdin) {
209-
raw += chunk
210-
}
211-
return raw
212-
}
213-
214-
async function main(): Promise<void> {
215-
let raw: string
216-
try {
217-
raw = await readStdin()
218-
} catch {
219-
process.exit(0)
220-
}
221-
if (!raw.trim()) {
222-
process.exit(0)
223-
}
224-
let payload: BundleStalePayload
225-
try {
226-
payload = JSON.parse(raw) as BundleStalePayload
227-
} catch {
228-
process.exit(0)
229-
}
230-
const reminder = run(payload)
231-
if (reminder) {
232-
process.stderr.write(reminder)
233-
}
234-
// Reminder-only: never blocks.
235-
process.exit(0)
185+
return notify(formatReminder(sourceRel))
236186
}
237187

238-
// Entrypoint-guarded: run main() only when invoked directly, NOT when the test
239-
// or the dispatch bundle imports this module for its pure `run` helper.
240-
// `isHookEntrypoint` also short-circuits inside a snapshot build pass, where the
241-
// absolute-`--build-snapshot`-path coincidence would otherwise fire this guard
242-
// during the build and abort serialization (see _shared/entrypoint.mts).
243-
if (isHookEntrypoint(import.meta.url)) {
244-
main().catch(() => {
245-
process.exit(0)
246-
})
247-
}
248-
/* c8 ignore stop */
188+
export const hook = defineHook({
189+
bypass: ['hook-bundle-current'],
190+
check,
191+
event: 'PostToolUse',
192+
matcher: ['Edit', 'Write'],
193+
type: 'nudge',
194+
})
195+
void runHook(hook, import.meta.url)

.claude/hooks/fleet/c8-ignore-reason-guard/index.mts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -57,11 +57,11 @@ export function findUnexplainedIgnores(source: string): Finding[] {
5757
IGNORE_DIRECTIVE_RE.lastIndex = 0
5858
let match: RegExpExecArray | null
5959
while ((match = IGNORE_DIRECTIVE_RE.exec(line)) !== null) {
60-
const kind = match.groups!.kind!
60+
const kind = match.groups!['kind']!
6161
if (kind === 'stop') {
6262
continue
6363
}
64-
const rawTail = match.groups!.tail!
64+
const rawTail = match.groups!['tail']!
6565
// A multi-line `next N` (N >= 2) is broken even WITH a reason: c8/v8
6666
// count physical lines, not statements, so `next 3` silently drops
6767
// covered lines. Require a start/stop bracket instead. Bare `next`
@@ -70,7 +70,7 @@ export function findUnexplainedIgnores(source: string): Finding[] {
7070
if (
7171
kind === 'next' &&
7272
countMatch &&
73-
Number(countMatch.groups!.count) >= 2
73+
Number(countMatch.groups!['count']) >= 2
7474
) {
7575
findings.push({ line: i + 1, text: line.trim(), kind: 'next-n' })
7676
continue

.claude/hooks/fleet/catch-message-guard/index.mts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@ export function findCatchMessageViolations(after: string): Finding[] {
125125
CATCH_OPEN_RE.lastIndex = 0
126126
const pending: string[] = []
127127
while ((m = CATCH_OPEN_RE.exec(code)) !== null) {
128-
pending.push(m.groups!.binding!)
128+
pending.push(m.groups!['binding']!)
129129
}
130130
// Look for ${<binding>.message} for any currently-open binding
131131
// BEFORE updating depth, so the line that closes the catch
@@ -261,6 +261,7 @@ function escapeRegex(s: string): string {
261261
}
262262

263263
export const check = editGuard((filePath, content, payload) => {
264+
void content
264265
if (!isJsOrTs(filePath) || isTestTree(filePath)) {
265266
return undefined
266267
}

0 commit comments

Comments
 (0)