context-brake 1.0.0 → 1.1.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.
Files changed (195) hide show
  1. package/README.md +31 -41
  2. package/dist/assets/runtime/antigravity-cli-hook.mjs +68 -23
  3. package/dist/assets/runtime/claude-code-hook.mjs +133 -35
  4. package/dist/assets/runtime/claude-code-mod.mjs +165 -59
  5. package/dist/assets/runtime/claude-code-statusline.mjs +41 -9
  6. package/dist/assets/runtime/codex-cli-hook.mjs +71 -23
  7. package/dist/assets/runtime/cursor-hook.mjs +71 -23
  8. package/dist/assets/runtime/github-copilot-cli-hook.mjs +71 -23
  9. package/dist/assets/runtime/omp-extension.js +70 -23
  10. package/dist/assets/runtime/omp-restart.js +41 -9
  11. package/dist/assets/runtime/opencode-plugin.js +70 -23
  12. package/dist/assets/runtime/pi-extension.js +73 -23
  13. package/dist/assets/runtime/pi-restart.js +41 -9
  14. package/dist/src/cli/assistant/assistant-context.js +7 -0
  15. package/dist/src/cli/assistant/assistant-questions.js +5 -4
  16. package/dist/src/cli/assistant/assistant-session.js +3 -1
  17. package/dist/src/cli/assistant/invocation-facts.d.ts +5 -0
  18. package/dist/src/cli/assistant/invocation-facts.js +6 -0
  19. package/dist/src/cli/assistant/questions-misc.d.ts +1 -0
  20. package/dist/src/cli/assistant/questions-misc.js +3 -2
  21. package/dist/src/cli/assistant/questions-skill.d.ts +21 -0
  22. package/dist/src/cli/assistant/questions-skill.js +45 -0
  23. package/dist/src/cli/assistant/questions-snapshot.d.ts +3 -1
  24. package/dist/src/cli/assistant/questions-snapshot.js +21 -26
  25. package/dist/src/cli/assistant/questions-trigger.d.ts +6 -0
  26. package/dist/src/cli/assistant/questions-trigger.js +56 -0
  27. package/dist/src/cli/assistant/skill-options.d.ts +20 -0
  28. package/dist/src/cli/assistant/skill-options.js +40 -0
  29. package/dist/src/cli/assistant/snapshot-specs.d.ts +7 -1
  30. package/dist/src/cli/assistant/snapshot-specs.js +17 -7
  31. package/dist/src/cli/assistant/summary-rows.d.ts +5 -0
  32. package/dist/src/cli/assistant/summary-rows.js +47 -0
  33. package/dist/src/cli/assistant/summary.d.ts +2 -1
  34. package/dist/src/cli/assistant/summary.js +21 -30
  35. package/dist/src/cli/assistant/types.d.ts +15 -0
  36. package/dist/src/cli/commands/doctor.js +2 -1
  37. package/dist/src/cli/commands/remove.js +1 -1
  38. package/dist/src/cli/init-arguments.js +5 -4
  39. package/dist/src/cli/init-flow.js +1 -1
  40. package/dist/src/cli/init-option-rules.d.ts +5 -0
  41. package/dist/src/cli/init-option-rules.js +16 -0
  42. package/dist/src/cli/output/card.d.ts +8 -0
  43. package/dist/src/cli/output/card.js +30 -0
  44. package/dist/src/cli/output/doctor-mode-text.js +8 -2
  45. package/dist/src/cli/output/install-rows.d.ts +6 -0
  46. package/dist/src/cli/output/install-rows.js +31 -0
  47. package/dist/src/cli/output/install-text.d.ts +4 -0
  48. package/dist/src/cli/output/install-text.js +26 -0
  49. package/dist/src/cli/output/style.d.ts +22 -0
  50. package/dist/src/cli/output/style.js +27 -0
  51. package/dist/src/cli/output/text.d.ts +1 -2
  52. package/dist/src/cli/output/text.js +1 -28
  53. package/dist/src/core/contracts/adapter.d.ts +2 -1
  54. package/dist/src/core/contracts/configuration.d.ts +2 -1
  55. package/dist/src/core/contracts/configuration.js +2 -10
  56. package/dist/src/core/contracts/context-reading.d.ts +16 -0
  57. package/dist/src/core/contracts/context-reading.js +20 -0
  58. package/dist/src/core/contracts/context-window-report.d.ts +8 -1
  59. package/dist/src/core/contracts/context-window-report.js +3 -1
  60. package/dist/src/core/contracts/diagnostics.d.ts +21 -0
  61. package/dist/src/core/contracts/diagnostics.js +2 -1
  62. package/dist/src/core/contracts/manifest.d.ts +16 -0
  63. package/dist/src/core/contracts/manifest.js +6 -0
  64. package/dist/src/core/contracts/runtime.d.ts +2 -0
  65. package/dist/src/core/contracts/skill-catalog.d.ts +11 -0
  66. package/dist/src/core/contracts/skill-catalog.js +1 -0
  67. package/dist/src/core/contracts/skill-invocation.d.ts +3 -0
  68. package/dist/src/core/contracts/skill-invocation.js +1 -0
  69. package/dist/src/core/contracts/skill-name.d.ts +5 -0
  70. package/dist/src/core/contracts/skill-name.js +31 -0
  71. package/dist/src/core/services/brake-engine.js +4 -3
  72. package/dist/src/core/services/doctor-report-extras.d.ts +2 -0
  73. package/dist/src/core/services/doctor-report-extras.js +22 -5
  74. package/dist/src/core/services/doctor-service.d.ts +2 -0
  75. package/dist/src/core/services/doctor-service.js +1 -1
  76. package/dist/src/core/services/failure-policy.js +1 -1
  77. package/dist/src/core/services/injection-policy.d.ts +1 -0
  78. package/dist/src/core/services/injection-policy.js +1 -1
  79. package/dist/src/core/services/installation-adapters.d.ts +2 -1
  80. package/dist/src/core/services/installation-adapters.js +3 -1
  81. package/dist/src/core/services/installation-builder.js +3 -2
  82. package/dist/src/core/services/installation-service.js +1 -1
  83. package/dist/src/core/services/manifest-change.d.ts +2 -1
  84. package/dist/src/core/services/manifest-change.js +2 -1
  85. package/dist/src/core/services/session-reset-handler.js +1 -1
  86. package/dist/src/core/services/skill-candidates.d.ts +16 -0
  87. package/dist/src/core/services/skill-candidates.js +14 -0
  88. package/dist/src/core/services/skill-invocation.d.ts +4 -0
  89. package/dist/src/core/services/skill-invocation.js +12 -0
  90. package/dist/src/core/services/skill-ranking.d.ts +7 -0
  91. package/dist/src/core/services/skill-ranking.js +25 -0
  92. package/dist/src/core/services/snapshot-merge.d.ts +1 -0
  93. package/dist/src/core/services/snapshot-merge.js +12 -5
  94. package/dist/src/core/services/trigger-text.d.ts +5 -0
  95. package/dist/src/core/services/trigger-text.js +4 -0
  96. package/dist/src/core/services/zone-guidance.d.ts +13 -2
  97. package/dist/src/core/services/zone-guidance.js +14 -8
  98. package/dist/src/infrastructure/harnesses/antigravity-cli/runtime.js +2 -1
  99. package/dist/src/infrastructure/harnesses/antigravity-cli/skill-folders.d.ts +1 -0
  100. package/dist/src/infrastructure/harnesses/antigravity-cli/skill-folders.js +1 -0
  101. package/dist/src/infrastructure/harnesses/antigravity-cli/skill-invocation.d.ts +2 -0
  102. package/dist/src/infrastructure/harnesses/antigravity-cli/skill-invocation.js +2 -0
  103. package/dist/src/infrastructure/harnesses/claude-code/adapter.js +6 -4
  104. package/dist/src/infrastructure/harnesses/claude-code/auto-restart-diagnostics.d.ts +1 -0
  105. package/dist/src/infrastructure/harnesses/claude-code/auto-restart-diagnostics.js +2 -2
  106. package/dist/src/infrastructure/harnesses/claude-code/auto-restart-planner.d.ts +5 -1
  107. package/dist/src/infrastructure/harnesses/claude-code/auto-restart-planner.js +6 -6
  108. package/dist/src/infrastructure/harnesses/claude-code/capabilities.js +1 -1
  109. package/dist/src/infrastructure/harnesses/claude-code/context-readings-scan.d.ts +2 -0
  110. package/dist/src/infrastructure/harnesses/claude-code/context-readings-scan.js +21 -0
  111. package/dist/src/infrastructure/harnesses/claude-code/context-source.d.ts +10 -0
  112. package/dist/src/infrastructure/harnesses/claude-code/context-source.js +16 -0
  113. package/dist/src/infrastructure/harnesses/claude-code/mod/context-reading.d.ts +2 -0
  114. package/dist/src/infrastructure/harnesses/claude-code/mod/context-reading.js +67 -0
  115. package/dist/src/infrastructure/harnesses/claude-code/mod/host.d.ts +9 -0
  116. package/dist/src/infrastructure/harnesses/claude-code/mod/mod-info.d.ts +1 -1
  117. package/dist/src/infrastructure/harnesses/claude-code/mod/mod-info.js +1 -1
  118. package/dist/src/infrastructure/harnesses/claude-code/mod/mod-log.d.ts +1 -0
  119. package/dist/src/infrastructure/harnesses/claude-code/mod/mod-log.js +3 -0
  120. package/dist/src/infrastructure/harnesses/claude-code/mod/register.js +6 -1
  121. package/dist/src/infrastructure/harnesses/claude-code/mod-reading.d.ts +6 -0
  122. package/dist/src/infrastructure/harnesses/claude-code/mod-reading.js +37 -0
  123. package/dist/src/infrastructure/harnesses/claude-code/planner.d.ts +2 -1
  124. package/dist/src/infrastructure/harnesses/claude-code/planner.js +4 -4
  125. package/dist/src/infrastructure/harnesses/claude-code/project-root.d.ts +2 -0
  126. package/dist/src/infrastructure/harnesses/claude-code/project-root.js +5 -0
  127. package/dist/src/infrastructure/harnesses/claude-code/runtime.js +12 -7
  128. package/dist/src/infrastructure/harnesses/claude-code/skill-folders.d.ts +1 -0
  129. package/dist/src/infrastructure/harnesses/claude-code/skill-folders.js +1 -0
  130. package/dist/src/infrastructure/harnesses/claude-code/skill-invocation.d.ts +2 -0
  131. package/dist/src/infrastructure/harnesses/claude-code/skill-invocation.js +2 -0
  132. package/dist/src/infrastructure/harnesses/claude-code/statusline-context-window.js +15 -2
  133. package/dist/src/infrastructure/harnesses/claude-code/statusline-diagnostics.js +3 -3
  134. package/dist/src/infrastructure/harnesses/claude-code/statusline-migration.d.ts +3 -0
  135. package/dist/src/infrastructure/harnesses/claude-code/statusline-migration.js +23 -0
  136. package/dist/src/infrastructure/harnesses/claude-code/statusline-planner.d.ts +2 -1
  137. package/dist/src/infrastructure/harnesses/claude-code/statusline-planner.js +7 -4
  138. package/dist/src/infrastructure/harnesses/claude-code/user-settings.d.ts +2 -0
  139. package/dist/src/infrastructure/harnesses/claude-code/user-settings.js +11 -0
  140. package/dist/src/infrastructure/harnesses/codex-cli/adapter.js +5 -4
  141. package/dist/src/infrastructure/harnesses/codex-cli/runtime.js +2 -1
  142. package/dist/src/infrastructure/harnesses/codex-cli/skill-folders.d.ts +1 -0
  143. package/dist/src/infrastructure/harnesses/codex-cli/skill-folders.js +1 -0
  144. package/dist/src/infrastructure/harnesses/codex-cli/skill-invocation.d.ts +2 -0
  145. package/dist/src/infrastructure/harnesses/codex-cli/skill-invocation.js +2 -0
  146. package/dist/src/infrastructure/harnesses/common/config-origin.d.ts +10 -0
  147. package/dist/src/infrastructure/harnesses/common/config-origin.js +52 -0
  148. package/dist/src/infrastructure/harnesses/cursor/adapter.js +5 -4
  149. package/dist/src/infrastructure/harnesses/cursor/runtime.js +2 -1
  150. package/dist/src/infrastructure/harnesses/cursor/skill-folders.d.ts +1 -0
  151. package/dist/src/infrastructure/harnesses/cursor/skill-folders.js +1 -0
  152. package/dist/src/infrastructure/harnesses/cursor/skill-invocation.d.ts +2 -0
  153. package/dist/src/infrastructure/harnesses/cursor/skill-invocation.js +2 -0
  154. package/dist/src/infrastructure/harnesses/github-copilot-cli/runtime.js +2 -1
  155. package/dist/src/infrastructure/harnesses/github-copilot-cli/skill-folders.d.ts +1 -0
  156. package/dist/src/infrastructure/harnesses/github-copilot-cli/skill-folders.js +1 -0
  157. package/dist/src/infrastructure/harnesses/github-copilot-cli/skill-invocation.d.ts +2 -0
  158. package/dist/src/infrastructure/harnesses/github-copilot-cli/skill-invocation.js +2 -0
  159. package/dist/src/infrastructure/harnesses/oh-my-pi/runtime.js +2 -1
  160. package/dist/src/infrastructure/harnesses/oh-my-pi/skill-folders.d.ts +1 -0
  161. package/dist/src/infrastructure/harnesses/oh-my-pi/skill-folders.js +1 -0
  162. package/dist/src/infrastructure/harnesses/oh-my-pi/skill-invocation.d.ts +2 -0
  163. package/dist/src/infrastructure/harnesses/oh-my-pi/skill-invocation.js +2 -0
  164. package/dist/src/infrastructure/harnesses/opencode/runtime.js +2 -1
  165. package/dist/src/infrastructure/harnesses/opencode/skill-folders.d.ts +1 -0
  166. package/dist/src/infrastructure/harnesses/opencode/skill-folders.js +1 -0
  167. package/dist/src/infrastructure/harnesses/opencode/skill-invocation.d.ts +2 -0
  168. package/dist/src/infrastructure/harnesses/opencode/skill-invocation.js +2 -0
  169. package/dist/src/infrastructure/harnesses/pi/runtime.js +2 -1
  170. package/dist/src/infrastructure/harnesses/pi/skill-folders.d.ts +1 -0
  171. package/dist/src/infrastructure/harnesses/pi/skill-folders.js +1 -0
  172. package/dist/src/infrastructure/harnesses/pi/skill-invocation.d.ts +2 -0
  173. package/dist/src/infrastructure/harnesses/pi/skill-invocation.js +2 -0
  174. package/dist/src/infrastructure/harnesses/skill-folders.d.ts +3 -0
  175. package/dist/src/infrastructure/harnesses/skill-folders.js +21 -0
  176. package/dist/src/infrastructure/harnesses/skill-invocations.d.ts +4 -0
  177. package/dist/src/infrastructure/harnesses/skill-invocations.js +21 -0
  178. package/dist/src/infrastructure/runtime/runtime-paths.js +2 -1
  179. package/dist/src/infrastructure/skills/frontmatter.d.ts +5 -0
  180. package/dist/src/infrastructure/skills/frontmatter.js +25 -0
  181. package/dist/src/infrastructure/skills/skill-catalog-reader.d.ts +4 -0
  182. package/dist/src/infrastructure/skills/skill-catalog-reader.js +78 -0
  183. package/dist/src/infrastructure/skills/skill-catalog.d.ts +2 -0
  184. package/dist/src/infrastructure/skills/skill-catalog.js +5 -0
  185. package/dist/src/infrastructure/storage/change-applier.d.ts +1 -0
  186. package/dist/src/infrastructure/storage/change-applier.js +2 -1
  187. package/dist/src/infrastructure/storage/json-document-editor.js +2 -1
  188. package/dist/src/infrastructure/storage/json-node-removal.d.ts +2 -0
  189. package/dist/src/infrastructure/storage/json-node-removal.js +70 -0
  190. package/dist/src/infrastructure/storage/json-span-utils.d.ts +1 -1
  191. package/dist/src/infrastructure/storage/json-span-utils.js +1 -48
  192. package/docs/telemetry-block.md +3 -1
  193. package/package.json +1 -1
  194. package/schemas/context-brake.config.schema.json +9 -4
  195. package/schemas/doctor-report.schema.json +51 -0
@@ -4,6 +4,17 @@ var __export = (target, all) => {
4
4
  __defProp(target, name, { get: all[name], enumerable: true });
5
5
  };
6
6
 
7
+ // src/core/services/skill-invocation.ts
8
+ function slashInvocation(skill) {
9
+ return `/${skill}`;
10
+ }
11
+ function sentenceInvocation(skill) {
12
+ return `use the ${skill} skill`;
13
+ }
14
+
15
+ // src/infrastructure/harnesses/claude-code/skill-invocation.ts
16
+ var CLAUDE_SKILL_INVOCATION = slashInvocation;
17
+
7
18
  // node_modules/zod/v4/mini/external.js
8
19
  var external_exports = {};
9
20
  __export(external_exports, {
@@ -5029,7 +5040,7 @@ var recursive = /* @__PURE__ */ new WeakMap();
5029
5040
  var NONE = 0;
5030
5041
  var ASSUMED = 1;
5031
5042
  var PROVEN = 2;
5032
- function isRecursive(inst, stack, resolve6) {
5043
+ function isRecursive(inst, stack, resolve7) {
5033
5044
  const cached2 = recursive.get(inst);
5034
5045
  if (cached2 !== void 0)
5035
5046
  return cached2 ? PROVEN : NONE;
@@ -5039,7 +5050,7 @@ function isRecursive(inst, stack, resolve6) {
5039
5050
  let result = NONE;
5040
5051
  const check2 = (child) => {
5041
5052
  if (result !== PROVEN && child?._zod) {
5042
- const answer = isRecursive(child, stack, resolve6);
5053
+ const answer = isRecursive(child, stack, resolve7);
5043
5054
  if (answer > result)
5044
5055
  result = answer;
5045
5056
  }
@@ -5050,7 +5061,7 @@ function isRecursive(inst, stack, resolve6) {
5050
5061
  const desc = Object.getOwnPropertyDescriptor(sh, key);
5051
5062
  if (spread && !desc.enumerable)
5052
5063
  continue;
5053
- const child = desc.get ? ASSUMED : desc.value?._zod ? isRecursive(desc.value, stack, resolve6) : NONE;
5064
+ const child = desc.get ? ASSUMED : desc.value?._zod ? isRecursive(desc.value, stack, resolve7) : NONE;
5054
5065
  if (child > answer)
5055
5066
  answer = child;
5056
5067
  }
@@ -5114,7 +5125,7 @@ function isRecursive(inst, stack, resolve6) {
5114
5125
  break;
5115
5126
  // `$ZodLazy` caches its inner on the def, so a resolved edge is followed exactly
5116
5127
  case "lazy": {
5117
- const inner = def._cachedInner ?? (resolve6 ? inst._zod.innerType : void 0);
5128
+ const inner = def._cachedInner ?? (resolve7 ? inst._zod.innerType : void 0);
5118
5129
  merge3(inner ? isRecursive(inner, stack, false) : ASSUMED);
5119
5130
  break;
5120
5131
  }
@@ -18155,6 +18166,34 @@ var HARNESS_IDS = [
18155
18166
  "antigravity-cli"
18156
18167
  ];
18157
18168
 
18169
+ // src/core/contracts/skill-name.ts
18170
+ var MAX_SKILL_NAME_LENGTH = 64;
18171
+ var NAME_SEGMENT = "[a-z0-9]+(?:-[a-z0-9]+)*";
18172
+ var NAMESPACE_SEPARATOR = ":";
18173
+ var INVOCATION_CHARACTERS = ["/", "$", "@"];
18174
+ var MAX_QUALIFIED_LENGTH = MAX_SKILL_NAME_LENGTH * 2 + NAMESPACE_SEPARATOR.length;
18175
+ function segmentFits(end) {
18176
+ return `(?=[^${NAMESPACE_SEPARATOR}]{1,${MAX_SKILL_NAME_LENGTH}}${end})`;
18177
+ }
18178
+ var PREFIX = `(?:${segmentFits(NAMESPACE_SEPARATOR)}${NAME_SEGMENT}${NAMESPACE_SEPARATOR})?`;
18179
+ var SKILL_NAME_PATTERN = new RegExp(`^${PREFIX}${segmentFits("$")}${NAME_SEGMENT}$`);
18180
+ var SHAPE_RULE = `lowercase letters, digits, and single hyphens, up to ${MAX_SKILL_NAME_LENGTH} characters, optionally prefixed by a plugin name and a colon`;
18181
+ function isSkillName(value) {
18182
+ return SKILL_NAME_PATTERN.test(value);
18183
+ }
18184
+ function skillNameProblem(value) {
18185
+ if (value === "") return "must not be empty";
18186
+ if (isSkillName(value)) return null;
18187
+ const bare = INVOCATION_CHARACTERS.includes(value.charAt(0)) ? value.slice(1) : null;
18188
+ if (bare !== null && isSkillName(bare)) return `"${value}" is not a skill name; use the bare name ${bare}`;
18189
+ return `"${value}" is not a skill name; use ${SHAPE_RULE}`;
18190
+ }
18191
+ var skillNameSchema = external_exports.string().check((ctx) => {
18192
+ const message = skillNameProblem(ctx.value);
18193
+ if (message !== null) ctx.issues.push({ code: "custom", input: ctx.value, message });
18194
+ });
18195
+ external_exports.globalRegistry.add(skillNameSchema, { pattern: SKILL_NAME_PATTERN.source, maxLength: MAX_QUALIFIED_LENGTH });
18196
+
18158
18197
  // src/core/contracts/zones.ts
18159
18198
  var ZONES = ["GREEN", "YELLOW", "RED", "CRITICAL"];
18160
18199
  var USAGE_SOURCES = ["measured", "estimated"];
@@ -18178,15 +18217,8 @@ function uniqueCheck(message) {
18178
18217
  if (!isUnique(ctx.value)) addIssue(ctx, { code: "custom", path: [], input: ctx.value, message });
18179
18218
  };
18180
18219
  }
18181
- var MAX_AGENT_COMMAND_LENGTH = 200;
18182
- var TRIMMED_RULE = "must not have leading or trailing whitespace";
18183
18220
  var RESUME_REQUIRES_COMMAND_RULE = "requires snapshot.command";
18184
- var SINGLE_LINE_RULE = "must be a single line";
18185
- var agentCommand = external_exports.string().check(external_exports.minLength(1), external_exports.maxLength(MAX_AGENT_COMMAND_LENGTH), (ctx) => {
18186
- if (ctx.value.trim() !== ctx.value) addIssue(ctx, { code: "custom", path: [], input: ctx.value, message: TRIMMED_RULE });
18187
- if (/[\r\n]/.test(ctx.value)) addIssue(ctx, { code: "custom", path: [], input: ctx.value, message: SINGLE_LINE_RULE });
18188
- });
18189
- var snapshotSchema = external_exports.strictObject({ triggerZone: external_exports._default(external_exports.enum(SNAPSHOT_TRIGGER_ZONES), "RED"), command: external_exports.optional(agentCommand), resumeCommand: external_exports.optional(agentCommand) }).check((ctx) => {
18221
+ var snapshotSchema = external_exports.strictObject({ triggerZone: external_exports._default(external_exports.enum(SNAPSHOT_TRIGGER_ZONES), "RED"), triggerPercentage: external_exports.optional(external_exports.int().check(external_exports.minimum(1), external_exports.maximum(100))), command: external_exports.optional(skillNameSchema), resumeCommand: external_exports.optional(skillNameSchema) }).check((ctx) => {
18190
18222
  if (ctx.value.resumeCommand !== void 0 && ctx.value.command === void 0) addIssue(ctx, { code: "custom", path: ["resumeCommand"], input: ctx.value.resumeCommand, message: RESUME_REQUIRES_COMMAND_RULE });
18191
18223
  });
18192
18224
  var DEFAULT_SNAPSHOT = { triggerZone: "RED" };
@@ -18267,21 +18299,25 @@ var GENERIC_ACTIONS = {
18267
18299
  RED: "finish or pause the current unit and tell the user what remains",
18268
18300
  CRITICAL: "stop starting new work; tell the user what remains"
18269
18301
  };
18270
- function zoneAction(zone, snapshot, mode = "off") {
18271
- if (zone === "GREEN" || !isAtOrAbove(zone, snapshot.triggerZone)) return GENERIC_ACTIONS[zone];
18272
- if (snapshot.command !== void 0) return snapshotAction(zone, snapshot.command);
18273
- if (mode !== "handoff") return GENERIC_ACTIONS[zone];
18302
+ function isResetDue(reading, snapshot) {
18303
+ if (snapshot.triggerPercentage !== void 0) return reading.usagePercentage >= snapshot.triggerPercentage;
18304
+ return reading.zone !== "GREEN" && isAtOrAbove(reading.zone, snapshot.triggerZone);
18305
+ }
18306
+ function zoneAction(zone, snapshot, options = {}) {
18307
+ if (!isResetDue({ zone, usagePercentage: options.usagePercentage ?? 0 }, snapshot)) return GENERIC_ACTIONS[zone];
18308
+ if (snapshot.command !== void 0) return snapshotAction(zone, (options.invoke ?? sentenceInvocation)(snapshot.command));
18309
+ if ((options.mode ?? "off") !== "handoff") return GENERIC_ACTIONS[zone];
18274
18310
  return `save handoff to ${HANDOFF_PATH}, end reply with ${SESSION_RESET_SIGNAL}`;
18275
18311
  }
18276
- function snapshotAction(zone, command) {
18312
+ function snapshotAction(zone, invocation) {
18277
18313
  const now = zone === "CRITICAL" ? " now" : "";
18278
- return `run "${command}"${now}, then end reply with ${SESSION_RESET_SIGNAL}`;
18314
+ return `run "${invocation}"${now}, then end reply with ${SESSION_RESET_SIGNAL}`;
18279
18315
  }
18280
18316
  function handoffResumeText(archivedPath) {
18281
18317
  return `${RESUME_PREFIX} Read "${archivedPath}" and continue the previous work from it.`;
18282
18318
  }
18283
- function resumeText(snapshot) {
18284
- return snapshot.resumeCommand === void 0 ? null : `${RESUME_PREFIX} Run "${snapshot.resumeCommand}" before continuing.`;
18319
+ function resumeText(snapshot, invoke = sentenceInvocation) {
18320
+ return snapshot.resumeCommand === void 0 ? null : `${RESUME_PREFIX} Run "${invoke(snapshot.resumeCommand)}" before continuing.`;
18285
18321
  }
18286
18322
  function isAtOrAbove(zone, trigger) {
18287
18323
  return ZONES.indexOf(zone) >= ZONES.indexOf(trigger);
@@ -18326,7 +18362,7 @@ async function resolveFailure(input2) {
18326
18362
  return { kind: "neutral" };
18327
18363
  }
18328
18364
  function deadlineBootDecision(input2) {
18329
- const text = resumeText((input2.config ?? DEFAULT_CONFIG).snapshot);
18365
+ const text = resumeText((input2.config ?? DEFAULT_CONFIG).snapshot, input2.descriptor?.skillInvocation);
18330
18366
  return text === null ? { kind: "neutral" } : { kind: "context", block: text };
18331
18367
  }
18332
18368
  async function recordRuntimeFailure(errors, record2) {
@@ -18447,7 +18483,7 @@ function isDebugModeInEffect(config2) {
18447
18483
 
18448
18484
  // src/core/services/injection-policy.ts
18449
18485
  function decideInjection(input2) {
18450
- if (input2.debug || input2.telemetry.injectionMode === "always") return true;
18486
+ if (input2.debug || input2.resetDue === true || input2.telemetry.injectionMode === "always") return true;
18451
18487
  return input2.zone !== "GREEN" || input2.usagePercentage >= input2.telemetry.activationThresholdPercentage;
18452
18488
  }
18453
18489
 
@@ -18598,7 +18634,7 @@ async function handleSessionReset(options, event, hooks = {}) {
18598
18634
  if (!options.descriptor.capabilities.some((entry) => entry.id === "session_boot" && entry.state === "supported")) return NEUTRAL;
18599
18635
  if (event.reason === "compact" && !COMPACTION_BOOT_HARNESSES.includes(options.descriptor.harness)) return NEUTRAL;
18600
18636
  hooks.onPhase?.("guidance");
18601
- const text = resumeText(options.config.snapshot) ?? await claimHandoff(options, event.reason, hooks.deadline);
18637
+ const text = resumeText(options.config.snapshot, options.descriptor.skillInvocation) ?? await claimHandoff(options, event.reason, hooks.deadline);
18602
18638
  return text === null ? NEUTRAL : { kind: "context", block: text };
18603
18639
  }
18604
18640
  async function claimHandoff(options, reason, deadline) {
@@ -18640,8 +18676,9 @@ async function handlePreInvocation(options, event, input2) {
18640
18676
  return telemetryDecision(options, summary.turns, readZone(options, { summary, turns: summary.turns, observedCharacters: 0, measured: input2.measured }));
18641
18677
  }
18642
18678
  async function telemetryDecision(options, turn, view) {
18643
- if (!decideInjection({ telemetry: options.config.telemetry, zone: view.zone, usagePercentage: view.percentage, debug: isDebugModeInEffect(options.config) })) return NEUTRAL2;
18644
- const action = zoneAction(view.zone, options.config.snapshot, restartMode(options.config));
18679
+ const resetDue = isResetDue({ zone: view.zone, usagePercentage: view.percentage }, options.config.snapshot);
18680
+ if (!decideInjection({ telemetry: options.config.telemetry, zone: view.zone, usagePercentage: view.percentage, debug: isDebugModeInEffect(options.config), resetDue })) return NEUTRAL2;
18681
+ const action = zoneAction(view.zone, options.config.snapshot, { mode: restartMode(options.config), invoke: options.descriptor.skillInvocation, usagePercentage: view.percentage });
18645
18682
  return { kind: "context", block: renderTelemetryBlock({ turn, turnCeiling: redStartTurn(options.config.telemetry.zones), usagePercentage: view.percentage, usage: view.reading, zone: view.zone, action, debug: isDebugModeInEffect(options.config) }) };
18646
18683
  }
18647
18684
  async function handleResponseEnd(options, event) {
@@ -18675,6 +18712,26 @@ import { createHash } from "node:crypto";
18675
18712
  import { mkdir, writeFile } from "node:fs/promises";
18676
18713
  import { join as join2, resolve as resolve3 } from "node:path";
18677
18714
 
18715
+ // src/core/contracts/context-reading.ts
18716
+ var CONTEXT_READINGS_RELATIVE_DIR = ".context-brake/runtime/context-readings";
18717
+ var CONTEXT_READING_MAXIMUM_CHARACTERS = 1024;
18718
+ var RUNTIME_IGNORE_CONTENT = "*\n";
18719
+ var SESSION_ID_PATTERN = /^[A-Za-z0-9_-]{1,128}$/;
18720
+ var PERCENTAGE_MAXIMUM = 100;
18721
+ var contextReadingSchema = external_exports.strictObject({
18722
+ v: external_exports.literal(1),
18723
+ at: external_exports.string().check(external_exports.minLength(1)),
18724
+ windowTokens: external_exports.nullable(external_exports.int().check(external_exports.positive())),
18725
+ inputTokens: external_exports.nullable(external_exports.int().check(external_exports.nonnegative())),
18726
+ usedPercentage: external_exports.nullable(external_exports.number().check(external_exports.gte(0), external_exports.lte(PERCENTAGE_MAXIMUM)))
18727
+ });
18728
+ function isReadingSessionId(value) {
18729
+ return value !== void 0 && SESSION_ID_PATTERN.test(value);
18730
+ }
18731
+ function contextReadingRelativePath(harness, sessionId) {
18732
+ return `${CONTEXT_READINGS_RELATIVE_DIR}/${harness}/${sessionId}.json`;
18733
+ }
18734
+
18678
18735
  // src/infrastructure/storage/runtime-state-files.ts
18679
18736
  import { readdir } from "node:fs/promises";
18680
18737
  import { join, relative, resolve as resolve2 } from "node:path";
@@ -18694,7 +18751,7 @@ async function listRuntimeStateFiles(root, readdirFn = readdir) {
18694
18751
  }
18695
18752
 
18696
18753
  // src/infrastructure/runtime/runtime-paths.ts
18697
- var RUNTIME_GITIGNORE_CONTENT = "*\n";
18754
+ var RUNTIME_GITIGNORE_CONTENT = RUNTIME_IGNORE_CONTENT;
18698
18755
  var SESSIONS_DIR_NAME = "sessions";
18699
18756
  var LEDGER_FILE_EXTENSION = ".jsonl";
18700
18757
  var SESSIONS_RELATIVE_PREFIX = `${RUNTIME_STATE_RELATIVE_DIR}/${SESSIONS_DIR_NAME}/`;
@@ -19035,7 +19092,7 @@ var defaultProcessHookContext = {
19035
19092
  sessionStartDeadlineMilliseconds: SESSION_START_DEADLINE_MILLISECONDS
19036
19093
  };
19037
19094
  function readStdinUpTo(stream, maximumBytes) {
19038
- return new Promise((resolve6, reject) => {
19095
+ return new Promise((resolve7, reject) => {
19039
19096
  const chunks = [];
19040
19097
  let size = 0;
19041
19098
  stream.on("data", (chunk) => {
@@ -19044,7 +19101,7 @@ function readStdinUpTo(stream, maximumBytes) {
19044
19101
  chunks.push(remaining >= chunk.length ? chunk : chunk.subarray(0, remaining));
19045
19102
  size += chunk.length;
19046
19103
  });
19047
- stream.on("end", () => resolve6(Buffer.concat(chunks).toString("utf8")));
19104
+ stream.on("end", () => resolve7(Buffer.concat(chunks).toString("utf8")));
19048
19105
  stream.on("error", (error62) => reject(error62));
19049
19106
  });
19050
19107
  }
@@ -19100,10 +19157,49 @@ function parsePayload2(raw) {
19100
19157
  var CLAUDE_CAPABILITIES = [
19101
19158
  { id: "post_tool_telemetry", state: "supported" },
19102
19159
  { id: "session_boot", state: "supported" },
19103
- { id: "context_usage", state: "unknown", impact: "Read from the session transcript, whose format is undocumented; falls back to an estimate. The context window comes from the status line bridge, which init installs by default; without it, the window falls back to contextWindowCeiling." },
19160
+ { id: "context_usage", state: "unknown", impact: "Read from the session transcript, whose format is undocumented; falls back to an estimate. The context window comes from the mod that init installs on Claude Code 2.1.287 or later, or from the status line bridge on older versions; without either, it falls back to contextWindowCeiling." },
19104
19161
  { id: "auto_restart", state: "unknown", impact: "Opt-in through init --auto-restart; needs Claude Code 2.1.287 or later with mods enabled. Run doctor to check that the mod loads." }
19105
19162
  ];
19106
19163
 
19164
+ // src/infrastructure/harnesses/claude-code/mod-reading.ts
19165
+ import { readFile as readFile3 } from "node:fs/promises";
19166
+ import { resolve as resolve6 } from "node:path";
19167
+
19168
+ // src/infrastructure/harnesses/claude-code/project-root.ts
19169
+ var PROJECT_DIRECTORY_VARIABLE = "CLAUDE_PROJECT_DIR";
19170
+ async function resolveClaudeProjectRoot() {
19171
+ return projectRootFromEnvironment([PROJECT_DIRECTORY_VARIABLE]) ?? await assetProjectRoot();
19172
+ }
19173
+
19174
+ // src/infrastructure/harnesses/claude-code/mod-reading.ts
19175
+ function parseContextReading(text) {
19176
+ if (text.length > CONTEXT_READING_MAXIMUM_CHARACTERS) return null;
19177
+ try {
19178
+ const parsed = contextReadingSchema.safeParse(JSON.parse(text));
19179
+ return parsed.success ? parsed.data : null;
19180
+ } catch {
19181
+ return null;
19182
+ }
19183
+ }
19184
+ async function readContextReading(sessionId) {
19185
+ if (!isReadingSessionId(sessionId)) return null;
19186
+ const path = resolve6(await resolveClaudeProjectRoot(), contextReadingRelativePath("claude-code", sessionId));
19187
+ return parseContextReading(await readFile3(path, "utf8").catch(() => ""));
19188
+ }
19189
+ function isNotOlder(reading, transcript) {
19190
+ return transcript === null || Date.parse(reading.at) >= Date.parse(transcript.at);
19191
+ }
19192
+ function chooseTokens(transcript, reading) {
19193
+ if (reading !== null && reading.inputTokens !== null && isNotOlder(reading, transcript)) return { tokens: reading.inputTokens, at: reading.at };
19194
+ if (transcript === null) return { tokens: null, at: reading?.at };
19195
+ return { tokens: transcript.tokens, at: transcript.at };
19196
+ }
19197
+ function freshestMeasured(transcript, reading) {
19198
+ if (transcript === null && reading === null) return void 0;
19199
+ const choice = chooseTokens(transcript, reading);
19200
+ return { tokens: choice.tokens, contextWindow: reading?.windowTokens ?? null, at: choice.at };
19201
+ }
19202
+
19107
19203
  // src/infrastructure/harnesses/claude-code/schemas.ts
19108
19204
  var claudeHookItemSchema = external_exports.looseObject({
19109
19205
  type: external_exports.optional(external_exports.string()),
@@ -19232,7 +19328,7 @@ var HARNESS = "claude-code";
19232
19328
  var ESTIMATION = { baselineTokens: 15e3, tokensPerTurn: 150 };
19233
19329
  var WRITE_TOOLS = ["Write", "Edit", "MultiEdit", "NotebookEdit"];
19234
19330
  var SKILL_TOOL = "Skill";
19235
- var claudeDescriptor = { harness: HARNESS, capabilities: CLAUDE_CAPABILITIES, estimation: ESTIMATION, newSessionCommand: "/clear" };
19331
+ var claudeDescriptor = { harness: HARNESS, capabilities: CLAUDE_CAPABILITIES, estimation: ESTIMATION, newSessionCommand: "/clear", skillInvocation: CLAUDE_SKILL_INVOCATION };
19236
19332
  function sessionOf(payload) {
19237
19333
  return { harness: HARNESS, sessionId: requireIdentifier(payload.session_id), agentId: payload.agent_id ?? null };
19238
19334
  }
@@ -19269,17 +19365,19 @@ function mapClaudeEvent(eventName, payload) {
19269
19365
  async function mapClaudeInput(eventName, payload, errors) {
19270
19366
  if (eventName !== "PostToolUse") return {};
19271
19367
  const data = parsePayload(claudePayloadSchema, payload);
19272
- const measured = data.agent_id === void 0 ? await measuredUsage({ path: data.transcript_path, eventName, errors }) : void 0;
19368
+ const measured = data.agent_id === void 0 ? await measuredUsage({ path: data.transcript_path, sessionId: data.session_id, eventName, errors }) : void 0;
19273
19369
  const usage = measured === void 0 ? {} : { measured };
19274
19370
  return { ...usage, observedCharacters: characterLength(data.tool_input) + characterLength(data.tool_response) };
19275
19371
  }
19276
19372
  async function measuredUsage(input2) {
19373
+ return freshestMeasured(await transcriptUsage(input2), await readContextReading(input2.sessionId));
19374
+ }
19375
+ async function transcriptUsage(input2) {
19277
19376
  try {
19278
- const usage = await readTranscriptUsage(input2.path);
19279
- return usage === null ? void 0 : { tokens: usage.tokens, contextWindow: null, at: usage.at };
19377
+ return await readTranscriptUsage(input2.path);
19280
19378
  } catch (error62) {
19281
19379
  await recordRuntimeFailure(input2.errors, { harness: HARNESS, event: input2.eventName, code: "UNEXPECTED", detail: failureDetail(error62) });
19282
- return void 0;
19380
+ return null;
19283
19381
  }
19284
19382
  }
19285
19383
  function renderClaudeDecision(decision, eventName) {
@@ -19292,7 +19390,7 @@ var claudeAdapter = {
19292
19390
  mapEvent: mapClaudeEvent,
19293
19391
  mapInput: mapClaudeInput,
19294
19392
  renderDecision: renderClaudeDecision,
19295
- resolveProjectRoot: async () => projectRootFromEnvironment(["CLAUDE_PROJECT_DIR"]) ?? await assetProjectRoot()
19393
+ resolveProjectRoot: resolveClaudeProjectRoot
19296
19394
  };
19297
19395
  function runClaudeCodeHook() {
19298
19396
  return runProcessHook(claudeAdapter);
@@ -18136,6 +18136,26 @@ function date4(params) {
18136
18136
  return _coercedDate(ZodMiniDate, params);
18137
18137
  }
18138
18138
 
18139
+ // src/core/contracts/context-reading.ts
18140
+ var CONTEXT_READINGS_RELATIVE_DIR = ".context-brake/runtime/context-readings";
18141
+ var RUNTIME_IGNORE_RELATIVE_PATH = ".context-brake/runtime/.gitignore";
18142
+ var RUNTIME_IGNORE_CONTENT = "*\n";
18143
+ var SESSION_ID_PATTERN = /^[A-Za-z0-9_-]{1,128}$/;
18144
+ var PERCENTAGE_MAXIMUM = 100;
18145
+ var contextReadingSchema = external_exports.strictObject({
18146
+ v: external_exports.literal(1),
18147
+ at: external_exports.string().check(external_exports.minLength(1)),
18148
+ windowTokens: external_exports.nullable(external_exports.int().check(external_exports.positive())),
18149
+ inputTokens: external_exports.nullable(external_exports.int().check(external_exports.nonnegative())),
18150
+ usedPercentage: external_exports.nullable(external_exports.number().check(external_exports.gte(0), external_exports.lte(PERCENTAGE_MAXIMUM)))
18151
+ });
18152
+ function isReadingSessionId(value) {
18153
+ return value !== void 0 && SESSION_ID_PATTERN.test(value);
18154
+ }
18155
+ function contextReadingRelativePath(harness, sessionId) {
18156
+ return `${CONTEXT_READINGS_RELATIVE_DIR}/${harness}/${sessionId}.json`;
18157
+ }
18158
+
18139
18159
  // src/core/contracts/auto-restart.ts
18140
18160
  var DEFAULT_MAX_CONSECUTIVE_RESTARTS = 2;
18141
18161
  var MIN_CONSECUTIVE_RESTARTS = 1;
@@ -18180,56 +18200,6 @@ var restartLogSchema = external_exports.strictObject({
18180
18200
  records: external_exports.array(restartLogRecordSchema).check(external_exports.maxLength(RESTART_LOG_MAX_RECORDS))
18181
18201
  });
18182
18202
 
18183
- // src/infrastructure/harnesses/claude-code/mod/mod-info.ts
18184
- var MOD_VERSION = "1.0.0";
18185
- var MOD_LOG_DIR = `${RESTART_LOG_RELATIVE_DIR}/claude-code`;
18186
- var CONFIG_FILE = "context-brake.config.json";
18187
- var PERSON_PROMPT_ORIGINS = ["composer", "bridge"];
18188
-
18189
- // src/core/contracts/restart-host.ts
18190
- var guardStateSchema = external_exports.strictObject({ consecutive: external_exports.int().check(external_exports.minimum(0)), toolCallsSinceSeed: external_exports.nullable(external_exports.int().check(external_exports.minimum(0))) });
18191
- var IDLE_GUARD_STATE = { consecutive: 0, toolCallsSinceSeed: null };
18192
-
18193
- // src/core/services/restart-guards.ts
18194
- async function bumpConsecutive(store) {
18195
- const state = await store.read();
18196
- await store.write({ ...state, consecutive: state.consecutive + 1 });
18197
- }
18198
- async function rollbackConsecutive(store) {
18199
- const state = await store.read();
18200
- await store.write({ ...state, consecutive: Math.max(0, state.consecutive - 1) });
18201
- }
18202
- async function markSeeded(store) {
18203
- const state = await store.read();
18204
- await store.write({ ...state, toolCallsSinceSeed: 0 });
18205
- }
18206
- async function foldToolCalls(store, calls) {
18207
- const state = await store.read();
18208
- if (state.toolCallsSinceSeed === null || calls === 0) return;
18209
- await store.write({ ...state, toolCallsSinceSeed: state.toolCallsSinceSeed + calls });
18210
- }
18211
- async function resetConsecutive(store) {
18212
- const state = await store.read();
18213
- if (state.consecutive === 0 && state.toolCallsSinceSeed === null) return;
18214
- await store.write(IDLE_GUARD_STATE);
18215
- }
18216
-
18217
- // src/infrastructure/harnesses/claude-code/mod/mod-guards.ts
18218
- function guardKey(root) {
18219
- return `contextbrake:autorestart:${root}`;
18220
- }
18221
- function modGuardStore($, root) {
18222
- return {
18223
- read: async () => {
18224
- const parsed = guardStateSchema.safeParse(await $.store.get(guardKey(root)));
18225
- return parsed.success ? parsed.data : IDLE_GUARD_STATE;
18226
- },
18227
- write: async (state) => {
18228
- await $.store.set(guardKey(root), state);
18229
- }
18230
- };
18231
- }
18232
-
18233
18203
  // src/core/services/auto-restart-notices.ts
18234
18204
  var SEED_BASE = "ContextBrake: this session was restarted automatically. Continue the previous work from the state it recorded.";
18235
18205
  var SKIPPED = "ContextBrake: automatic restart skipped: ";
@@ -18260,6 +18230,12 @@ function appendLogRecord(records, record2) {
18260
18230
  return [...records, record2].slice(-RESTART_LOG_MAX_RECORDS);
18261
18231
  }
18262
18232
 
18233
+ // src/infrastructure/harnesses/claude-code/mod/mod-info.ts
18234
+ var MOD_VERSION = "1.1.0";
18235
+ var MOD_LOG_DIR = `${RESTART_LOG_RELATIVE_DIR}/claude-code`;
18236
+ var CONFIG_FILE = "context-brake.config.json";
18237
+ var PERSON_PROMPT_ORIGINS = ["composer", "bridge"];
18238
+
18263
18239
  // src/infrastructure/harnesses/claude-code/mod/mod-log.ts
18264
18240
  async function logPath($) {
18265
18241
  return `${await $.session.root()}/${MOD_LOG_DIR}/${await $.session.id()}.json`;
@@ -18280,6 +18256,9 @@ async function writeLog($, path, log) {
18280
18256
  await $.fs.write(path, `${JSON.stringify(log, null, 2)}
18281
18257
  `);
18282
18258
  }
18259
+ async function lastRecordedCode($) {
18260
+ return (await readLog($, await logPath($))).records.at(-1)?.code;
18261
+ }
18283
18262
  async function recordLoaded($) {
18284
18263
  const path = await logPath($);
18285
18264
  const log = await readLog($, path);
@@ -18298,6 +18277,34 @@ function endsWithResetSignal(text) {
18298
18277
  return text.trimEnd().split(/\r?\n/).at(-1)?.endsWith(SESSION_RESET_SIGNAL) ?? false;
18299
18278
  }
18300
18279
 
18280
+ // src/core/contracts/restart-host.ts
18281
+ var guardStateSchema = external_exports.strictObject({ consecutive: external_exports.int().check(external_exports.minimum(0)), toolCallsSinceSeed: external_exports.nullable(external_exports.int().check(external_exports.minimum(0))) });
18282
+ var IDLE_GUARD_STATE = { consecutive: 0, toolCallsSinceSeed: null };
18283
+
18284
+ // src/core/services/restart-guards.ts
18285
+ async function bumpConsecutive(store) {
18286
+ const state = await store.read();
18287
+ await store.write({ ...state, consecutive: state.consecutive + 1 });
18288
+ }
18289
+ async function rollbackConsecutive(store) {
18290
+ const state = await store.read();
18291
+ await store.write({ ...state, consecutive: Math.max(0, state.consecutive - 1) });
18292
+ }
18293
+ async function markSeeded(store) {
18294
+ const state = await store.read();
18295
+ await store.write({ ...state, toolCallsSinceSeed: 0 });
18296
+ }
18297
+ async function foldToolCalls(store, calls) {
18298
+ const state = await store.read();
18299
+ if (state.toolCallsSinceSeed === null || calls === 0) return;
18300
+ await store.write({ ...state, toolCallsSinceSeed: state.toolCallsSinceSeed + calls });
18301
+ }
18302
+ async function resetConsecutive(store) {
18303
+ const state = await store.read();
18304
+ if (state.consecutive === 0 && state.toolCallsSinceSeed === null) return;
18305
+ await store.write(IDLE_GUARD_STATE);
18306
+ }
18307
+
18301
18308
  // src/core/services/auto-restart-policy.ts
18302
18309
  function standDownCode(facts) {
18303
18310
  if (facts.disabledByEnv) return "SKIP_DISABLED_ENV";
@@ -18361,6 +18368,34 @@ async function handleTurnEnd(host, turn) {
18361
18368
  });
18362
18369
  }
18363
18370
 
18371
+ // src/core/contracts/skill-name.ts
18372
+ var MAX_SKILL_NAME_LENGTH = 64;
18373
+ var NAME_SEGMENT = "[a-z0-9]+(?:-[a-z0-9]+)*";
18374
+ var NAMESPACE_SEPARATOR = ":";
18375
+ var INVOCATION_CHARACTERS = ["/", "$", "@"];
18376
+ var MAX_QUALIFIED_LENGTH = MAX_SKILL_NAME_LENGTH * 2 + NAMESPACE_SEPARATOR.length;
18377
+ function segmentFits(end) {
18378
+ return `(?=[^${NAMESPACE_SEPARATOR}]{1,${MAX_SKILL_NAME_LENGTH}}${end})`;
18379
+ }
18380
+ var PREFIX = `(?:${segmentFits(NAMESPACE_SEPARATOR)}${NAME_SEGMENT}${NAMESPACE_SEPARATOR})?`;
18381
+ var SKILL_NAME_PATTERN = new RegExp(`^${PREFIX}${segmentFits("$")}${NAME_SEGMENT}$`);
18382
+ var SHAPE_RULE = `lowercase letters, digits, and single hyphens, up to ${MAX_SKILL_NAME_LENGTH} characters, optionally prefixed by a plugin name and a colon`;
18383
+ function isSkillName(value) {
18384
+ return SKILL_NAME_PATTERN.test(value);
18385
+ }
18386
+ function skillNameProblem(value) {
18387
+ if (value === "") return "must not be empty";
18388
+ if (isSkillName(value)) return null;
18389
+ const bare = INVOCATION_CHARACTERS.includes(value.charAt(0)) ? value.slice(1) : null;
18390
+ if (bare !== null && isSkillName(bare)) return `"${value}" is not a skill name; use the bare name ${bare}`;
18391
+ return `"${value}" is not a skill name; use ${SHAPE_RULE}`;
18392
+ }
18393
+ var skillNameSchema = external_exports.string().check((ctx) => {
18394
+ const message = skillNameProblem(ctx.value);
18395
+ if (message !== null) ctx.issues.push({ code: "custom", input: ctx.value, message });
18396
+ });
18397
+ external_exports.globalRegistry.add(skillNameSchema, { pattern: SKILL_NAME_PATTERN.source, maxLength: MAX_QUALIFIED_LENGTH });
18398
+
18364
18399
  // src/core/contracts/zones.ts
18365
18400
  var SNAPSHOT_TRIGGER_ZONES = ["YELLOW", "RED"];
18366
18401
 
@@ -18381,15 +18416,8 @@ function uniqueCheck(message) {
18381
18416
  if (!isUnique(ctx.value)) addIssue(ctx, { code: "custom", path: [], input: ctx.value, message });
18382
18417
  };
18383
18418
  }
18384
- var MAX_AGENT_COMMAND_LENGTH = 200;
18385
- var TRIMMED_RULE = "must not have leading or trailing whitespace";
18386
18419
  var RESUME_REQUIRES_COMMAND_RULE = "requires snapshot.command";
18387
- var SINGLE_LINE_RULE = "must be a single line";
18388
- var agentCommand = external_exports.string().check(external_exports.minLength(1), external_exports.maxLength(MAX_AGENT_COMMAND_LENGTH), (ctx) => {
18389
- if (ctx.value.trim() !== ctx.value) addIssue(ctx, { code: "custom", path: [], input: ctx.value, message: TRIMMED_RULE });
18390
- if (/[\r\n]/.test(ctx.value)) addIssue(ctx, { code: "custom", path: [], input: ctx.value, message: SINGLE_LINE_RULE });
18391
- });
18392
- var snapshotSchema = external_exports.strictObject({ triggerZone: external_exports._default(external_exports.enum(SNAPSHOT_TRIGGER_ZONES), "RED"), command: external_exports.optional(agentCommand), resumeCommand: external_exports.optional(agentCommand) }).check((ctx) => {
18420
+ var snapshotSchema = external_exports.strictObject({ triggerZone: external_exports._default(external_exports.enum(SNAPSHOT_TRIGGER_ZONES), "RED"), triggerPercentage: external_exports.optional(external_exports.int().check(external_exports.minimum(1), external_exports.maximum(100))), command: external_exports.optional(skillNameSchema), resumeCommand: external_exports.optional(skillNameSchema) }).check((ctx) => {
18393
18421
  if (ctx.value.resumeCommand !== void 0 && ctx.value.command === void 0) addIssue(ctx, { code: "custom", path: ["resumeCommand"], input: ctx.value.resumeCommand, message: RESUME_REQUIRES_COMMAND_RULE });
18394
18422
  });
18395
18423
  var DEFAULT_SNAPSHOT = { triggerZone: "RED" };
@@ -18442,6 +18470,22 @@ async function readModConfig($) {
18442
18470
  };
18443
18471
  }
18444
18472
 
18473
+ // src/infrastructure/harnesses/claude-code/mod/mod-guards.ts
18474
+ function guardKey(root) {
18475
+ return `contextbrake:autorestart:${root}`;
18476
+ }
18477
+ function modGuardStore($, root) {
18478
+ return {
18479
+ read: async () => {
18480
+ const parsed = guardStateSchema.safeParse(await $.store.get(guardKey(root)));
18481
+ return parsed.success ? parsed.data : IDLE_GUARD_STATE;
18482
+ },
18483
+ write: async (state) => {
18484
+ await $.store.set(guardKey(root), state);
18485
+ }
18486
+ };
18487
+ }
18488
+
18445
18489
  // src/core/contracts/handoff.ts
18446
18490
  var HANDOFF_RELATIVE_PATH = ".context-brake/handoff.md";
18447
18491
 
@@ -18517,6 +18561,64 @@ async function handleTurnComplete($, event) {
18517
18561
  await handleTurnEnd(createModRestartHost($, config2), { text, settings: { maxConsecutive: config2.maxConsecutive, mode: config2.mode } });
18518
18562
  }
18519
18563
 
18564
+ // src/infrastructure/harnesses/claude-code/mod/context-reading.ts
18565
+ var READING_HARNESS = "claude-code";
18566
+ var READING_VERSION = 1;
18567
+ var PERCENTAGE_MAXIMUM2 = 100;
18568
+ var UNREADABLE_CODE = "ERROR_INTERNAL";
18569
+ function positiveIntegerOrNull(value) {
18570
+ return typeof value === "number" && Number.isInteger(value) && value > 0 ? value : null;
18571
+ }
18572
+ function countOrNull(value) {
18573
+ return typeof value === "number" && Number.isInteger(value) && value >= 0 ? value : null;
18574
+ }
18575
+ function percentageOrNull(value) {
18576
+ return typeof value === "number" && value >= 0 && value <= PERCENTAGE_MAXIMUM2 ? value : null;
18577
+ }
18578
+ function readingOf(context, at) {
18579
+ return { v: READING_VERSION, at, windowTokens: positiveIntegerOrNull(context.window), inputTokens: countOrNull(context.tokens), usedPercentage: percentageOrNull(context.percent) };
18580
+ }
18581
+ async function isAlreadyPublished($, path, reading) {
18582
+ if (!await $.fs.exists(path)) return false;
18583
+ try {
18584
+ const previous = contextReadingSchema.safeParse(JSON.parse(String(await $.fs.read(path))));
18585
+ return previous.success && previous.data.windowTokens === reading.windowTokens && previous.data.inputTokens === reading.inputTokens;
18586
+ } catch {
18587
+ return false;
18588
+ }
18589
+ }
18590
+ async function ensureRuntimeIgnore($, root) {
18591
+ const path = `${root}/${RUNTIME_IGNORE_RELATIVE_PATH}`;
18592
+ if (!await $.fs.exists(path)) await $.fs.write(path, RUNTIME_IGNORE_CONTENT);
18593
+ }
18594
+ async function reportOnce($) {
18595
+ try {
18596
+ if (await lastRecordedCode($) === UNREADABLE_CODE) return;
18597
+ } catch {
18598
+ return;
18599
+ }
18600
+ await reportSafely($, UNREADABLE_CODE);
18601
+ }
18602
+ async function publishChanged($) {
18603
+ const sessionId = await $.session.id();
18604
+ if (!isReadingSessionId(sessionId)) return;
18605
+ const reading = readingOf((await $.session.usage()).context, new Date(await $.clock.now()).toISOString());
18606
+ if (reading.windowTokens === null) return reportOnce($);
18607
+ const root = await $.session.root();
18608
+ const path = `${root}/${contextReadingRelativePath(READING_HARNESS, sessionId)}`;
18609
+ if (await isAlreadyPublished($, path, reading)) return;
18610
+ await ensureRuntimeIgnore($, root);
18611
+ await $.fs.write(path, `${JSON.stringify(reading)}
18612
+ `);
18613
+ }
18614
+ async function publishContextReading($) {
18615
+ try {
18616
+ await publishChanged($);
18617
+ } catch {
18618
+ await reportOnce($);
18619
+ }
18620
+ }
18621
+
18520
18622
  // src/infrastructure/harnesses/claude-code/mod/hooks.ts
18521
18623
  async function onSessionStart($) {
18522
18624
  try {
@@ -18555,17 +18657,21 @@ async function onPromptSubmit($, event) {
18555
18657
  function register(on) {
18556
18658
  on("session.start", async ($, e, next) => {
18557
18659
  await onSessionStart($);
18660
+ await publishContextReading($);
18558
18661
  return next(e);
18559
18662
  });
18560
18663
  on("turn.start", async ($, e, next) => {
18561
18664
  await onTurnStart($);
18665
+ await publishContextReading($);
18562
18666
  return next(e);
18563
18667
  });
18564
- on("tool.call", async (_$, e, next) => {
18668
+ on("tool.call", async ($, e, next) => {
18565
18669
  onToolCall();
18670
+ await publishContextReading($);
18566
18671
  return next(e);
18567
18672
  });
18568
18673
  on("turn.complete", async ($, e, next) => {
18674
+ await publishContextReading($);
18569
18675
  await onTurnComplete($, e);
18570
18676
  return next(e);
18571
18677
  });