halfcycle 0.5.1 → 0.5.3

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/dist/index.js CHANGED
@@ -125,6 +125,9 @@ var USER_PROMPT_REMINDER_REL = ".claude/hooks/user-prompt-reminder.sh";
125
125
  var USER_PROMPT_SCOPE_REL = ".claude/hooks/user-prompt-scope.sh";
126
126
  var USER_PROMPT_FINDINGS_REL = ".claude/hooks/user-prompt-findings.sh";
127
127
  var SESSION_END_MARKER_REL = ".claude/hooks/session-end-marker.sh";
128
+ var CONTEXT_BUDGET_CHECK_REL = ".claude/hooks/context-budget-check.sh";
129
+ var CONTEXT_DRIFT_CHECK_REL = ".claude/hooks/context-drift-check.sh";
130
+ var CONTEXT_DRIFT_MATCHER = "Edit|Write";
128
131
  var WRITE_CAPABLE_TOOLS = ["Edit", "MultiEdit", "NotebookEdit", "Write"];
129
132
  var PRE_TOOL_USE_MATCHER = "^(Bash|Read|Grep|Write|Edit|MultiEdit|NotebookEdit|Agent|PowerShell)$";
130
133
  function hookCommand(script, verb) {
@@ -201,6 +204,20 @@ var HALFCYCLE_HOOK_WIRING = [
201
204
  timeout: 5,
202
205
  command: hookCommand(SESSION_END_MARKER_REL),
203
206
  script: SESSION_END_MARKER_REL
207
+ },
208
+ {
209
+ event: "SessionStart",
210
+ matcher: null,
211
+ timeout: 10,
212
+ command: hookCommand(CONTEXT_BUDGET_CHECK_REL),
213
+ script: CONTEXT_BUDGET_CHECK_REL
214
+ },
215
+ {
216
+ event: "PostToolUse",
217
+ matcher: CONTEXT_DRIFT_MATCHER,
218
+ timeout: 5,
219
+ command: hookCommand(CONTEXT_DRIFT_CHECK_REL),
220
+ script: CONTEXT_DRIFT_CHECK_REL
204
221
  }
205
222
  ];
206
223
  var RETIRED_HOOK_COMMANDS = [
@@ -210,7 +227,9 @@ var RETIRED_HOOK_COMMANDS = [
210
227
  "bash ./.claude/hooks/guard-runner.sh hook post-tool-use",
211
228
  "bash ./.claude/hooks/guard-runner.sh hook stop",
212
229
  "bash ./.claude/hooks/guard-runner.sh hook subagent-stop",
213
- "bash ./.claude/hooks/session-end-marker.sh"
230
+ "bash ./.claude/hooks/session-end-marker.sh",
231
+ "bash ./.claude/hooks/context-budget-check.sh",
232
+ "bash ./.claude/hooks/context-drift-check.sh"
214
233
  ];
215
234
  var HALFCYCLE_HOOK_COMMANDS = /* @__PURE__ */ new Set([
216
235
  ...HALFCYCLE_HOOK_WIRING.map((entry) => entry.command),
@@ -402,7 +421,9 @@ var CODEX_HOOK_WIRING = [
402
421
  codexHookRow("PostToolUse", CODEX_WRITE_CAPABLE_TOOLS.join("|"), 60, GUARD_RUNNER_REL, "hook post-tool-use --agent codex"),
403
422
  codexHookRow("Stop", null, 60, GUARD_RUNNER_REL, "hook stop --agent codex"),
404
423
  codexHookRow("SubagentStop", null, 60, GUARD_RUNNER_REL, "hook subagent-stop --agent codex"),
405
- codexHookRow("SessionEnd", null, 3, SESSION_END_MARKER_REL)
424
+ codexHookRow("SessionEnd", null, 3, SESSION_END_MARKER_REL),
425
+ codexHookRow("SessionStart", null, 10, CONTEXT_BUDGET_CHECK_REL),
426
+ codexHookRow("PostToolUse", CODEX_WRITE_CAPABLE_TOOLS.join("|"), 5, CONTEXT_DRIFT_CHECK_REL)
406
427
  ];
407
428
  var RETIRED_CODEX_HOOK_COMMANDS = [];
408
429
  function codexHookCommandSet(retired) {
@@ -3139,16 +3160,43 @@ var ownerWaitDeclarationSchema = z23.object({
3139
3160
  var ownerWaitDeclarationsSchema = z23.object({ declarations: z23.array(ownerWaitDeclarationSchema) }).strict();
3140
3161
  var OWNER_WAIT_TOO_MANY = `This project already has ${String(OWNER_WAIT_ADDITIONS_MAX)} things added to what waits for the owner. Ask the owner to take one off on their settings page in the console first; it cannot be done by typing in chat.`;
3141
3162
 
3142
- // ../events/dist/autonomy.js
3163
+ // ../events/dist/owner-wait-answer.js
3143
3164
  import { z as z24 } from "zod";
3165
+ var ownerWaitShortIdSchema = z24.string().regex(/^[0-9a-f]{8}$/);
3166
+ var OWNER_WAIT_ANSWER_ACTS = ["approve", "decline"];
3167
+ var ownerWaitAnswerActSchema = z24.enum(OWNER_WAIT_ANSWER_ACTS);
3168
+ var ownerWaitChatRequestSchema = z24.discriminatedUnion("act", [
3169
+ z24.object({ act: z24.literal("approve"), wait: ownerWaitShortIdSchema }).strict(),
3170
+ z24.object({ act: z24.literal("decline"), wait: ownerWaitShortIdSchema, note: scopeNoteWireSchema }).strict()
3171
+ ]);
3172
+ function bareOutcome(outcome) {
3173
+ return z24.object({ outcome: z24.literal(outcome) }).strict();
3174
+ }
3175
+ var ownerWaitChatResultSchema = z24.discriminatedUnion("outcome", [
3176
+ z24.object({
3177
+ outcome: z24.literal("recorded"),
3178
+ act: ownerWaitAnswerActSchema,
3179
+ question: z24.string().min(1)
3180
+ }).strict(),
3181
+ z24.object({ outcome: z24.literal("refused"), problems: z24.array(systemDescriptionProblemSchema).min(1) }).strict(),
3182
+ bareOutcome("not-owner"),
3183
+ bareOutcome("no-owner"),
3184
+ bareOutcome("not-found"),
3185
+ bareOutcome("ambiguous"),
3186
+ bareOutcome("not-offered"),
3187
+ bareOutcome("unavailable")
3188
+ ]);
3189
+
3190
+ // ../events/dist/autonomy.js
3191
+ import { z as z25 } from "zod";
3144
3192
  var AUTONOMY_CHANGED_IN = ["console", "chat"];
3145
- var autonomyChangedInSchema = z24.enum(AUTONOMY_CHANGED_IN);
3146
- var autonomySettingsSchema = z24.object({
3147
- on: z24.boolean(),
3193
+ var autonomyChangedInSchema = z25.enum(AUTONOMY_CHANGED_IN);
3194
+ var autonomySettingsSchema = z25.object({
3195
+ on: z25.boolean(),
3148
3196
  // whether builds run on their own
3149
- mayPublish: z24.boolean(),
3197
+ mayPublish: z25.boolean(),
3150
3198
  // whether builds may publish or release without waiting
3151
- maySpend: z24.boolean(),
3199
+ maySpend: z25.boolean(),
3152
3200
  // whether builds may spend money or sign up without waiting
3153
3201
  changedAt: utcIso8601.nullable(),
3154
3202
  // when the setting last changed; null if never
@@ -3158,37 +3206,37 @@ var autonomySettingsSchema = z24.object({
3158
3206
  message: "says when and where it changed, or neither",
3159
3207
  path: ["changedIn"]
3160
3208
  });
3161
- var autonomyChangeRequestSchema = z24.object({
3162
- on: z24.boolean(),
3209
+ var autonomyChangeRequestSchema = z25.object({
3210
+ on: z25.boolean(),
3163
3211
  // the switch
3164
- mayPublish: z24.boolean(),
3212
+ mayPublish: z25.boolean(),
3165
3213
  // the publish box
3166
- maySpend: z24.boolean()
3214
+ maySpend: z25.boolean()
3167
3215
  // the spend box
3168
3216
  }).strict();
3169
3217
  var AUTONOMY_CHAT_ACTS = ["on", "off"];
3170
- var autonomyChatActSchema = z24.enum(AUTONOMY_CHAT_ACTS);
3171
- var autonomyChatRequestSchema = z24.object({
3218
+ var autonomyChatActSchema = z25.enum(AUTONOMY_CHAT_ACTS);
3219
+ var autonomyChatRequestSchema = z25.object({
3172
3220
  act: autonomyChatActSchema
3173
3221
  // what the owner typed
3174
3222
  }).strict();
3175
- var autonomyChatResultSchema = z24.discriminatedUnion("outcome", [
3176
- z24.object({
3177
- outcome: z24.literal("recorded"),
3178
- on: z24.boolean()
3223
+ var autonomyChatResultSchema = z25.discriminatedUnion("outcome", [
3224
+ z25.object({
3225
+ outcome: z25.literal("recorded"),
3226
+ on: z25.boolean()
3179
3227
  // whether builds now run on their own
3180
3228
  }).strict(),
3181
- z24.object({ outcome: z24.literal("not-owner") }).strict(),
3182
- z24.object({ outcome: z24.literal("no-owner") }).strict(),
3183
- z24.object({ outcome: z24.literal("not-offered") }).strict(),
3184
- z24.object({ outcome: z24.literal("unavailable") }).strict()
3229
+ z25.object({ outcome: z25.literal("not-owner") }).strict(),
3230
+ z25.object({ outcome: z25.literal("no-owner") }).strict(),
3231
+ z25.object({ outcome: z25.literal("not-offered") }).strict(),
3232
+ z25.object({ outcome: z25.literal("unavailable") }).strict()
3185
3233
  ]);
3186
- var autonomyStateSchema = z24.object({
3187
- on: z24.boolean(),
3234
+ var autonomyStateSchema = z25.object({
3235
+ on: z25.boolean(),
3188
3236
  // whether builds run on their own
3189
- mayPublish: z24.boolean(),
3237
+ mayPublish: z25.boolean(),
3190
3238
  // whether builds may publish or release without waiting
3191
- maySpend: z24.boolean()
3239
+ maySpend: z25.boolean()
3192
3240
  // whether builds may spend money or sign up without waiting
3193
3241
  }).strict();
3194
3242
  var autonomyStateFileSchema = autonomyStateSchema.extend({
@@ -3197,8 +3245,8 @@ var autonomyStateFileSchema = autonomyStateSchema.extend({
3197
3245
  }).strict();
3198
3246
  var AUTONOMY_STATE_FILE_MAX_AGE_MS = 24 * 60 * 60 * 1e3;
3199
3247
  var AUTONOMY_ACTIVITY_KINDS = ["went-ahead", "waiting", "approved", "closed", "waiting-on-owner"];
3200
- var autonomyActivityKindSchema = z24.enum(AUTONOMY_ACTIVITY_KINDS);
3201
- var autonomyActivityRowSchema = z24.object({
3248
+ var autonomyActivityKindSchema = z25.enum(AUTONOMY_ACTIVITY_KINDS);
3249
+ var autonomyActivityRowSchema = z25.object({
3202
3250
  kind: autonomyActivityKindSchema,
3203
3251
  // what happened
3204
3252
  phase: scopePhaseSchema,
@@ -3211,6 +3259,8 @@ var autonomyActivityRowSchema = z24.object({
3211
3259
  // whether the agent says it can be undone
3212
3260
  how: statementText(STATEMENT_MAX).optional(),
3213
3261
  // how to undo it, when it can be
3262
+ wait: ownerWaitShortIdSchema.optional(),
3263
+ // the short id the owner types to answer a waiting decision
3214
3264
  at: utcIso8601
3215
3265
  // when it happened
3216
3266
  }).strict().superRefine((row, ctx) => {
@@ -3237,6 +3287,9 @@ var autonomyActivityRowSchema = z24.object({
3237
3287
  });
3238
3288
  }
3239
3289
  }
3290
+ if (row.kind !== "waiting" && row.wait !== void 0) {
3291
+ ctx.addIssue({ code: "custom", message: "only a waiting decision is answered by typing", path: ["wait"] });
3292
+ }
3240
3293
  if (row.kind === "waiting" && (row.undoable !== void 0 || row.how !== void 0)) {
3241
3294
  ctx.addIssue({ code: "custom", message: "a waiting decision has not been made yet", path: ["undoable"] });
3242
3295
  }
@@ -3244,11 +3297,11 @@ var autonomyActivityRowSchema = z24.object({
3244
3297
  ctx.addIssue({ code: "custom", message: "says how to undo it exactly when it can be undone", path: ["how"] });
3245
3298
  }
3246
3299
  });
3247
- var autonomyActivitySchema = z24.object({
3248
- rows: z24.array(autonomyActivityRowSchema)
3300
+ var autonomyActivitySchema = z25.object({
3301
+ rows: z25.array(autonomyActivityRowSchema)
3249
3302
  // newest first
3250
3303
  }).strict();
3251
- var savedIdSchema = z24.string().min(1).max(64).regex(/^[a-z0-9]+(?:-[a-z0-9]+)*$/, "must be lower-case letters, digits and single hyphens");
3304
+ var savedIdSchema = z25.string().min(1).max(64).regex(/^[a-z0-9]+(?:-[a-z0-9]+)*$/, "must be lower-case letters, digits and single hyphens");
3252
3305
  var SAVED_REF_PREFIX = "refs/halfcycle/saved/";
3253
3306
  function savedRefOf(id) {
3254
3307
  return `${SAVED_REF_PREFIX}${id}`;
@@ -3269,33 +3322,33 @@ function isGitFolderSegment(segment) {
3269
3322
  return t === ".git" || /^git~\d+$/.test(t);
3270
3323
  }
3271
3324
  var SAVED_PATH_KEPT_IN = ["commit", "copy"];
3272
- var savedPathSchema = z24.object({
3273
- path: z24.string().max(4096).refine(isProjectRelativePath, { message: "must be a path inside the project" }),
3325
+ var savedPathSchema = z25.object({
3326
+ path: z25.string().max(4096).refine(isProjectRelativePath, { message: "must be a path inside the project" }),
3274
3327
  // the file, relative to the project
3275
- keptIn: z24.enum(SAVED_PATH_KEPT_IN)
3328
+ keptIn: z25.enum(SAVED_PATH_KEPT_IN)
3276
3329
  // in the save's commit, or copied beside this file
3277
3330
  }).strict();
3278
- var savedCommitSchema = z24.string().regex(/^(?:[0-9a-f]{40}|[0-9a-f]{64})$/, "must be a full commit hash");
3279
- var savedBranchSchema = z24.object({
3280
- name: z24.string().min(1).max(255).refine((n) => !hasControlCharacter(n) && !n.startsWith("-") && !n.includes("..") && n !== "HEAD", {
3331
+ var savedCommitSchema = z25.string().regex(/^(?:[0-9a-f]{40}|[0-9a-f]{64})$/, "must be a full commit hash");
3332
+ var savedBranchSchema = z25.object({
3333
+ name: z25.string().min(1).max(255).refine((n) => !hasControlCharacter(n) && !n.startsWith("-") && !n.includes("..") && n !== "HEAD", {
3281
3334
  message: "must be a branch name"
3282
3335
  }),
3283
3336
  // the branch's name
3284
3337
  commit: savedCommitSchema
3285
3338
  // the commit it pointed at
3286
3339
  }).strict();
3287
- var savedSchema = z24.object({
3340
+ var savedSchema = z25.object({
3288
3341
  id: savedIdSchema,
3289
3342
  // what restore is typed with
3290
3343
  savedAt: utcIso8601,
3291
3344
  // when the copy was made
3292
- ref: z24.string().min(1),
3345
+ ref: z25.string().min(1),
3293
3346
  // where the saved commit is kept
3294
- what: z24.string().min(1).max(2e3),
3347
+ what: z25.string().min(1).max(2e3),
3295
3348
  // what the action would have changed, in words
3296
- paths: z24.array(savedPathSchema),
3349
+ paths: z25.array(savedPathSchema),
3297
3350
  // every saved file
3298
- branches: z24.array(savedBranchSchema)
3351
+ branches: z25.array(savedBranchSchema)
3299
3352
  // every saved line of work
3300
3353
  }).strict().refine((s) => s.ref === savedRefOf(s.id), { message: "must be the ref of this save", path: ["ref"] });
3301
3354
  function autonomyC8(agent, question, chose) {
@@ -4211,6 +4264,8 @@ function kinds() {
4211
4264
  [".claude/hooks/user-prompt-findings.sh", "check-script"],
4212
4265
  [".claude/hooks/session-end-marker.sh", "check-script"],
4213
4266
  [".claude/hooks/session-start-marker.sh", "check-script"],
4267
+ [".claude/hooks/context-budget-check.sh", "check-script"],
4268
+ [".claude/hooks/context-drift-check.sh", "check-script"],
4214
4269
  [SETTINGS_REL, "settings"],
4215
4270
  [MCP_REGISTRATION_REL, "server-entry"],
4216
4271
  [GITIGNORE_REL, "ignore-list"],
@@ -6166,13 +6221,133 @@ var MCP_HEADERS_KNOWN_HEADER_HASHES = [
6166
6221
  function planMcpHeadersHelperRemoval(targetRepo) {
6167
6222
  return decideRetiredRemoval(targetRepo, RETIRED_MCP_HEADERS_HELPER_REL, 1, MCP_HEADERS_KNOWN_HEADER_HASHES);
6168
6223
  }
6224
+ var GENERATED_CONTEXT_BUDGET_HEADER = "# Context budget check \u2014 generated by";
6225
+ function generateContextBudgetCheck() {
6226
+ return `#!/usr/bin/env bash
6227
+ ${GENERATED_CONTEXT_BUDGET_HEADER} the Halfcycle installer.
6228
+ # Runs when a session starts. It warns, and never blocks, when the instructions
6229
+ # every session loads have grown past their budget, so they can be trimmed
6230
+ # before every later session pays for them:
6231
+ # - AGENTS.md at the project root, over 16384 bytes;
6232
+ # - this project's memory index in Claude Code, over 20480 bytes or 150 lines
6233
+ # (Claude Code cuts the index off at about 25 KB or 200 lines when it loads it).
6234
+
6235
+ cat >/dev/null 2>&1 || true
6236
+
6237
+ SCRIPT_DIR="$( cd "$( dirname "\${BASH_SOURCE[0]}" )" && pwd -P )" || exit 0
6238
+ PROJECT_ROOT="$( cd "$SCRIPT_DIR/../.." && pwd -P )" || exit 0
6239
+
6240
+ NOTES=""
6241
+
6242
+ AGENTS_FILE="$PROJECT_ROOT/AGENTS.md"
6243
+ if [ -f "$AGENTS_FILE" ]; then
6244
+ BYTES=$(wc -c < "$AGENTS_FILE" | tr -d ' ')
6245
+ if [ "\${BYTES:-0}" -gt 16384 ]; then
6246
+ NOTES="\${NOTES}AGENTS.md is \${BYTES} bytes, over its budget of 16384. "
6247
+ fi
6248
+ fi
6249
+
6250
+ CONFIG_DIR="\${CLAUDE_CONFIG_DIR:-$HOME/.claude}"
6251
+ SLUG=$(printf '%s' "\${CLAUDE_PROJECT_DIR:-$PROJECT_ROOT}" | sed 's/[^A-Za-z0-9]/-/g')
6252
+ INDEX="$CONFIG_DIR/projects/$SLUG/memory/MEMORY.md"
6253
+ if [ -f "$INDEX" ]; then
6254
+ BYTES=$(wc -c < "$INDEX" | tr -d ' ')
6255
+ LINES=$(wc -l < "$INDEX" | tr -d ' ')
6256
+ if [ "\${BYTES:-0}" -gt 20480 ] || [ "\${LINES:-0}" -gt 150 ]; then
6257
+ NOTES="\${NOTES}The memory index is \${BYTES} bytes and \${LINES} lines, over its budget of 20480 bytes and 150 lines. "
6258
+ fi
6259
+ fi
6260
+
6261
+ if [ -z "$NOTES" ]; then exit 0; fi
6262
+
6263
+ MESSAGE="Context budget: \${NOTES}Run the context-update command to fold and trim it."
6264
+ printf '{"systemMessage":"%s","hookSpecificOutput":{"hookEventName":"SessionStart","additionalContext":"%s"}}\\n' "$MESSAGE" "$MESSAGE"
6265
+ exit 0
6266
+ `;
6267
+ }
6268
+ var GENERATED_CONTEXT_DRIFT_HEADER = "# Context drift check \u2014 generated by";
6269
+ function generateContextDriftCheck() {
6270
+ return `#!/usr/bin/env bash
6271
+ ${GENERATED_CONTEXT_DRIFT_HEADER} the Halfcycle installer.
6272
+ # Context drift detector \u2014 runs after each file edit. If a trigger file was modified,
6273
+ # it asks the session to run the context-update command before the session ends.
6274
+
6275
+ set -euo pipefail
6276
+
6277
+ INPUT=$(cat)
6278
+
6279
+ # First line: the event's name, for the reply. Then one line per edited file, made
6280
+ # absolute against the event's cwd: the edit's file_path, or every path a patch names.
6281
+ # With no python3 on the machine it says nothing, rather than fail after every edit.
6282
+ PARSED=$(HOOK_INPUT="$INPUT" python3 - <<'PYEOF'
6283
+ import json, os, re
6284
+ try:
6285
+ data = json.loads(os.environ["HOOK_INPUT"])
6286
+ except Exception:
6287
+ raise SystemExit(0)
6288
+ tool_input = data.get("tool_input") or {}
6289
+ cwd = data.get("cwd") or os.getcwd()
6290
+ if tool_input.get("file_path"):
6291
+ paths = [tool_input["file_path"]]
6292
+ else:
6293
+ headers = r"^\\*\\*\\* (?:Add File|Update File|Delete File|Move to): (.+)$"
6294
+ paths = re.findall(headers, str(tool_input.get("command", "")), re.M)
6295
+ print(data.get("hook_event_name", ""))
6296
+ for p in paths:
6297
+ print(os.path.realpath(os.path.join(cwd, p.strip())))
6298
+ PYEOF
6299
+ ) || exit 0
6300
+
6301
+ if [ -z "$PARSED" ]; then exit 0; fi
6302
+ HOOK_EVENT=$(printf '%s\\n' "$PARSED" | head -n 1)
6303
+
6304
+ # Portable PROJECT_ROOT derivation: the script lives two folders below the root
6305
+ SCRIPT_DIR="$( cd "$( dirname "\${BASH_SOURCE[0]}" )" && pwd -P )"
6306
+ PROJECT_ROOT="$( cd "$SCRIPT_DIR/../.." && pwd -P )"
6307
+
6308
+ REASONS=""
6309
+ while IFS= read -r FILE_PATH; do
6310
+ [ -n "$FILE_PATH" ] || continue
6311
+ case "$FILE_PATH" in
6312
+ "$PROJECT_ROOT"/*) ;;
6313
+ *) continue ;;
6314
+ esac
6315
+
6316
+ REL_PATH="\${FILE_PATH#$PROJECT_ROOT/}"
6317
+
6318
+ # Skip the instructions files and each agent's own folders (editing them needs no reminder)
6319
+ case "$REL_PATH" in
6320
+ CLAUDE.md|AGENTS.md|README.md|.claude/*|.codex/*|.agents/*|dist/*|node_modules/*|.git/*)
6321
+ continue ;;
6322
+ esac
6323
+
6324
+ REASON=""
6325
+ case "$REL_PATH" in
6326
+ package.json|package-lock.json) REASON="package.json changed \u2014 check Tech Stack / Commands sections" ;;
6327
+ wrangler.jsonc|wrangler.toml) REASON="wrangler config changed \u2014 check Deployment section" ;;
6328
+ astro.config.*|next.config.*|vite.config.*) REASON="build config changed \u2014 check Tech Stack / Architecture" ;;
6329
+ src/pages/api/*|src/routes/api/*|pages/api/*) REASON="API route changed \u2014 check API Endpoints" ;;
6330
+ scripts/*) REASON="scripts/ changed \u2014 check Commands section" ;;
6331
+ tsconfig.json|.eslintrc*|.prettierrc*) REASON="tool config changed \u2014 check Code Conventions" ;;
6332
+ esac
6333
+ if [ -n "$REASON" ]; then REASONS="\${REASONS}\${REL_PATH} was modified: \${REASON}. "; fi
6334
+ done < <(printf '%s\\n' "$PARSED" | tail -n +2)
6335
+
6336
+ if [ -z "$REASONS" ]; then exit 0; fi
6337
+
6338
+ REMINDER="Context drift detected. \${REASONS}Before ending this session, run the context-update command to audit and update AGENTS.md and the memory folder."
6339
+ python3 -c 'import json, sys; print(json.dumps({"hookSpecificOutput": {"hookEventName": sys.argv[1], "additionalContext": sys.argv[2]}}))' "$HOOK_EVENT" "$REMINDER"
6340
+ `;
6341
+ }
6169
6342
  var OWNED_GENERATED_HEADERS = {
6170
6343
  ".claude/hooks/guard-runner.sh": GENERATED_GUARD_RUNNER_HEADER,
6171
6344
  ".claude/hooks/user-prompt-reminder.sh": GENERATED_USER_PROMPT_REMINDER_HEADER,
6172
6345
  ".claude/hooks/user-prompt-scope.sh": GENERATED_USER_PROMPT_SCOPE_HEADER,
6173
6346
  ".claude/hooks/user-prompt-findings.sh": GENERATED_USER_PROMPT_FINDINGS_HEADER,
6174
6347
  ".claude/hooks/session-end-marker.sh": GENERATED_SESSION_END_HEADER,
6175
- ".claude/hooks/session-start-marker.sh": GENERATED_SESSION_START_HEADER
6348
+ ".claude/hooks/session-start-marker.sh": GENERATED_SESSION_START_HEADER,
6349
+ ".claude/hooks/context-budget-check.sh": GENERATED_CONTEXT_BUDGET_HEADER,
6350
+ ".claude/hooks/context-drift-check.sh": GENERATED_CONTEXT_DRIFT_HEADER
6176
6351
  };
6177
6352
  function hookScriptsThisVersionWrites() {
6178
6353
  return [
@@ -6181,7 +6356,9 @@ function hookScriptsThisVersionWrites() {
6181
6356
  [".claude/hooks/user-prompt-reminder.sh", generateUserPromptReminderHook()],
6182
6357
  [".claude/hooks/user-prompt-scope.sh", generateUserPromptScopeHook()],
6183
6358
  [".claude/hooks/user-prompt-findings.sh", generateUserPromptFindingsHook()],
6184
- [".claude/hooks/session-start-marker.sh", generateSessionStartMarker()]
6359
+ [".claude/hooks/session-start-marker.sh", generateSessionStartMarker()],
6360
+ [".claude/hooks/context-budget-check.sh", generateContextBudgetCheck()],
6361
+ [".claude/hooks/context-drift-check.sh", generateContextDriftCheck()]
6185
6362
  ];
6186
6363
  }
6187
6364
  function generateMcpRegistration(existing, mcpOrigin) {