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
@@ -18155,6 +18155,34 @@ var HARNESS_IDS = [
18155
18155
  "antigravity-cli"
18156
18156
  ];
18157
18157
 
18158
+ // src/core/contracts/skill-name.ts
18159
+ var MAX_SKILL_NAME_LENGTH = 64;
18160
+ var NAME_SEGMENT = "[a-z0-9]+(?:-[a-z0-9]+)*";
18161
+ var NAMESPACE_SEPARATOR = ":";
18162
+ var INVOCATION_CHARACTERS = ["/", "$", "@"];
18163
+ var MAX_QUALIFIED_LENGTH = MAX_SKILL_NAME_LENGTH * 2 + NAMESPACE_SEPARATOR.length;
18164
+ function segmentFits(end) {
18165
+ return `(?=[^${NAMESPACE_SEPARATOR}]{1,${MAX_SKILL_NAME_LENGTH}}${end})`;
18166
+ }
18167
+ var PREFIX = `(?:${segmentFits(NAMESPACE_SEPARATOR)}${NAME_SEGMENT}${NAMESPACE_SEPARATOR})?`;
18168
+ var SKILL_NAME_PATTERN = new RegExp(`^${PREFIX}${segmentFits("$")}${NAME_SEGMENT}$`);
18169
+ 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`;
18170
+ function isSkillName(value) {
18171
+ return SKILL_NAME_PATTERN.test(value);
18172
+ }
18173
+ function skillNameProblem(value) {
18174
+ if (value === "") return "must not be empty";
18175
+ if (isSkillName(value)) return null;
18176
+ const bare = INVOCATION_CHARACTERS.includes(value.charAt(0)) ? value.slice(1) : null;
18177
+ if (bare !== null && isSkillName(bare)) return `"${value}" is not a skill name; use the bare name ${bare}`;
18178
+ return `"${value}" is not a skill name; use ${SHAPE_RULE}`;
18179
+ }
18180
+ var skillNameSchema = external_exports.string().check((ctx) => {
18181
+ const message = skillNameProblem(ctx.value);
18182
+ if (message !== null) ctx.issues.push({ code: "custom", input: ctx.value, message });
18183
+ });
18184
+ external_exports.globalRegistry.add(skillNameSchema, { pattern: SKILL_NAME_PATTERN.source, maxLength: MAX_QUALIFIED_LENGTH });
18185
+
18158
18186
  // src/core/contracts/zones.ts
18159
18187
  var ZONES = ["GREEN", "YELLOW", "RED", "CRITICAL"];
18160
18188
  var USAGE_SOURCES = ["measured", "estimated"];
@@ -18178,15 +18206,8 @@ function uniqueCheck(message) {
18178
18206
  if (!isUnique(ctx.value)) addIssue(ctx, { code: "custom", path: [], input: ctx.value, message });
18179
18207
  };
18180
18208
  }
18181
- var MAX_AGENT_COMMAND_LENGTH = 200;
18182
- var TRIMMED_RULE = "must not have leading or trailing whitespace";
18183
18209
  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) => {
18210
+ 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
18211
  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
18212
  });
18192
18213
  var DEFAULT_SNAPSHOT = { triggerZone: "RED" };
@@ -18337,6 +18358,17 @@ import { createHash } from "node:crypto";
18337
18358
  import { mkdir, writeFile } from "node:fs/promises";
18338
18359
  import { join as join2, resolve as resolve2 } from "node:path";
18339
18360
 
18361
+ // src/core/contracts/context-reading.ts
18362
+ var RUNTIME_IGNORE_CONTENT = "*\n";
18363
+ var PERCENTAGE_MAXIMUM = 100;
18364
+ var contextReadingSchema = external_exports.strictObject({
18365
+ v: external_exports.literal(1),
18366
+ at: external_exports.string().check(external_exports.minLength(1)),
18367
+ windowTokens: external_exports.nullable(external_exports.int().check(external_exports.positive())),
18368
+ inputTokens: external_exports.nullable(external_exports.int().check(external_exports.nonnegative())),
18369
+ usedPercentage: external_exports.nullable(external_exports.number().check(external_exports.gte(0), external_exports.lte(PERCENTAGE_MAXIMUM)))
18370
+ });
18371
+
18340
18372
  // src/infrastructure/storage/runtime-state-files.ts
18341
18373
  import { readdir } from "node:fs/promises";
18342
18374
  import { join, relative, resolve } from "node:path";
@@ -18356,7 +18388,7 @@ async function listRuntimeStateFiles(root, readdirFn = readdir) {
18356
18388
  }
18357
18389
 
18358
18390
  // src/infrastructure/runtime/runtime-paths.ts
18359
- var RUNTIME_GITIGNORE_CONTENT = "*\n";
18391
+ var RUNTIME_GITIGNORE_CONTENT = RUNTIME_IGNORE_CONTENT;
18360
18392
  var SESSIONS_DIR_NAME = "sessions";
18361
18393
  var LEDGER_FILE_EXTENSION = ".jsonl";
18362
18394
  var SESSIONS_RELATIVE_PREFIX = `${RUNTIME_STATE_RELATIVE_DIR}/${SESSIONS_DIR_NAME}/`;
@@ -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 dollarInvocation(skill) {
9
+ return `$${skill}`;
10
+ }
11
+ function sentenceInvocation(skill) {
12
+ return `use the ${skill} skill`;
13
+ }
14
+
15
+ // src/infrastructure/harnesses/codex-cli/skill-invocation.ts
16
+ var CODEX_SKILL_INVOCATION = dollarInvocation;
17
+
7
18
  // node_modules/zod/v4/mini/external.js
8
19
  var external_exports = {};
9
20
  __export(external_exports, {
@@ -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) {
@@ -18440,7 +18476,7 @@ function isDebugModeInEffect(config2) {
18440
18476
 
18441
18477
  // src/core/services/injection-policy.ts
18442
18478
  function decideInjection(input2) {
18443
- if (input2.debug || input2.telemetry.injectionMode === "always") return true;
18479
+ if (input2.debug || input2.resetDue === true || input2.telemetry.injectionMode === "always") return true;
18444
18480
  return input2.zone !== "GREEN" || input2.usagePercentage >= input2.telemetry.activationThresholdPercentage;
18445
18481
  }
18446
18482
 
@@ -18591,7 +18627,7 @@ async function handleSessionReset(options, event, hooks = {}) {
18591
18627
  if (!options.descriptor.capabilities.some((entry) => entry.id === "session_boot" && entry.state === "supported")) return NEUTRAL;
18592
18628
  if (event.reason === "compact" && !COMPACTION_BOOT_HARNESSES.includes(options.descriptor.harness)) return NEUTRAL;
18593
18629
  hooks.onPhase?.("guidance");
18594
- const text = resumeText(options.config.snapshot) ?? await claimHandoff(options, event.reason, hooks.deadline);
18630
+ const text = resumeText(options.config.snapshot, options.descriptor.skillInvocation) ?? await claimHandoff(options, event.reason, hooks.deadline);
18595
18631
  return text === null ? NEUTRAL : { kind: "context", block: text };
18596
18632
  }
18597
18633
  async function claimHandoff(options, reason, deadline) {
@@ -18633,8 +18669,9 @@ async function handlePreInvocation(options, event, input2) {
18633
18669
  return telemetryDecision(options, summary.turns, readZone(options, { summary, turns: summary.turns, observedCharacters: 0, measured: input2.measured }));
18634
18670
  }
18635
18671
  async function telemetryDecision(options, turn, view) {
18636
- if (!decideInjection({ telemetry: options.config.telemetry, zone: view.zone, usagePercentage: view.percentage, debug: isDebugModeInEffect(options.config) })) return NEUTRAL2;
18637
- const action = zoneAction(view.zone, options.config.snapshot, restartMode(options.config));
18672
+ const resetDue = isResetDue({ zone: view.zone, usagePercentage: view.percentage }, options.config.snapshot);
18673
+ if (!decideInjection({ telemetry: options.config.telemetry, zone: view.zone, usagePercentage: view.percentage, debug: isDebugModeInEffect(options.config), resetDue })) return NEUTRAL2;
18674
+ const action = zoneAction(view.zone, options.config.snapshot, { mode: restartMode(options.config), invoke: options.descriptor.skillInvocation, usagePercentage: view.percentage });
18638
18675
  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) }) };
18639
18676
  }
18640
18677
  async function handleResponseEnd(options, event) {
@@ -18668,6 +18705,17 @@ import { createHash } from "node:crypto";
18668
18705
  import { mkdir, writeFile } from "node:fs/promises";
18669
18706
  import { join as join2, resolve as resolve3 } from "node:path";
18670
18707
 
18708
+ // src/core/contracts/context-reading.ts
18709
+ var RUNTIME_IGNORE_CONTENT = "*\n";
18710
+ var PERCENTAGE_MAXIMUM = 100;
18711
+ var contextReadingSchema = external_exports.strictObject({
18712
+ v: external_exports.literal(1),
18713
+ at: external_exports.string().check(external_exports.minLength(1)),
18714
+ windowTokens: external_exports.nullable(external_exports.int().check(external_exports.positive())),
18715
+ inputTokens: external_exports.nullable(external_exports.int().check(external_exports.nonnegative())),
18716
+ usedPercentage: external_exports.nullable(external_exports.number().check(external_exports.gte(0), external_exports.lte(PERCENTAGE_MAXIMUM)))
18717
+ });
18718
+
18671
18719
  // src/infrastructure/storage/runtime-state-files.ts
18672
18720
  import { readdir } from "node:fs/promises";
18673
18721
  import { join, relative, resolve as resolve2 } from "node:path";
@@ -18687,7 +18735,7 @@ async function listRuntimeStateFiles(root, readdirFn = readdir) {
18687
18735
  }
18688
18736
 
18689
18737
  // src/infrastructure/runtime/runtime-paths.ts
18690
- var RUNTIME_GITIGNORE_CONTENT = "*\n";
18738
+ var RUNTIME_GITIGNORE_CONTENT = RUNTIME_IGNORE_CONTENT;
18691
18739
  var SESSIONS_DIR_NAME = "sessions";
18692
18740
  var LEDGER_FILE_EXTENSION = ".jsonl";
18693
18741
  var SESSIONS_RELATIVE_PREFIX = `${RUNTIME_STATE_RELATIVE_DIR}/${SESSIONS_DIR_NAME}/`;
@@ -19222,7 +19270,7 @@ var codexPayloadSchema = external_exports.looseObject({
19222
19270
  var HARNESS = "codex-cli";
19223
19271
  var ESTIMATION = { baselineTokens: 15e3, tokensPerTurn: 150 };
19224
19272
  var PATCH_PATH_PREFIXES = ["*** Add File: ", "*** Update File: ", "*** Delete File: ", "*** Move to: "];
19225
- var codexDescriptor = { harness: HARNESS, capabilities: CODEX_CAPABILITIES, estimation: ESTIMATION, newSessionCommand: "/new" };
19273
+ var codexDescriptor = { harness: HARNESS, capabilities: CODEX_CAPABILITIES, estimation: ESTIMATION, newSessionCommand: "/new", skillInvocation: CODEX_SKILL_INVOCATION };
19226
19274
  function sessionOf(payload) {
19227
19275
  return { harness: HARNESS, sessionId: requireIdentifier(payload.session_id), agentId: payload.agent_id ?? null };
19228
19276
  }
@@ -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/cursor/skill-invocation.ts
16
+ var CURSOR_SKILL_INVOCATION = slashInvocation;
17
+
7
18
  // src/infrastructure/harnesses/common/runtime-support.ts
8
19
  import { realpath } from "node:fs/promises";
9
20
  import { dirname, resolve } from "node:path";
@@ -18160,6 +18171,34 @@ var HARNESS_IDS = [
18160
18171
  "antigravity-cli"
18161
18172
  ];
18162
18173
 
18174
+ // src/core/contracts/skill-name.ts
18175
+ var MAX_SKILL_NAME_LENGTH = 64;
18176
+ var NAME_SEGMENT = "[a-z0-9]+(?:-[a-z0-9]+)*";
18177
+ var NAMESPACE_SEPARATOR = ":";
18178
+ var INVOCATION_CHARACTERS = ["/", "$", "@"];
18179
+ var MAX_QUALIFIED_LENGTH = MAX_SKILL_NAME_LENGTH * 2 + NAMESPACE_SEPARATOR.length;
18180
+ function segmentFits(end) {
18181
+ return `(?=[^${NAMESPACE_SEPARATOR}]{1,${MAX_SKILL_NAME_LENGTH}}${end})`;
18182
+ }
18183
+ var PREFIX = `(?:${segmentFits(NAMESPACE_SEPARATOR)}${NAME_SEGMENT}${NAMESPACE_SEPARATOR})?`;
18184
+ var SKILL_NAME_PATTERN = new RegExp(`^${PREFIX}${segmentFits("$")}${NAME_SEGMENT}$`);
18185
+ 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`;
18186
+ function isSkillName(value) {
18187
+ return SKILL_NAME_PATTERN.test(value);
18188
+ }
18189
+ function skillNameProblem(value) {
18190
+ if (value === "") return "must not be empty";
18191
+ if (isSkillName(value)) return null;
18192
+ const bare = INVOCATION_CHARACTERS.includes(value.charAt(0)) ? value.slice(1) : null;
18193
+ if (bare !== null && isSkillName(bare)) return `"${value}" is not a skill name; use the bare name ${bare}`;
18194
+ return `"${value}" is not a skill name; use ${SHAPE_RULE}`;
18195
+ }
18196
+ var skillNameSchema = external_exports.string().check((ctx) => {
18197
+ const message = skillNameProblem(ctx.value);
18198
+ if (message !== null) ctx.issues.push({ code: "custom", input: ctx.value, message });
18199
+ });
18200
+ external_exports.globalRegistry.add(skillNameSchema, { pattern: SKILL_NAME_PATTERN.source, maxLength: MAX_QUALIFIED_LENGTH });
18201
+
18163
18202
  // src/core/contracts/zones.ts
18164
18203
  var ZONES = ["GREEN", "YELLOW", "RED", "CRITICAL"];
18165
18204
  var USAGE_SOURCES = ["measured", "estimated"];
@@ -18183,15 +18222,8 @@ function uniqueCheck(message) {
18183
18222
  if (!isUnique(ctx.value)) addIssue(ctx, { code: "custom", path: [], input: ctx.value, message });
18184
18223
  };
18185
18224
  }
18186
- var MAX_AGENT_COMMAND_LENGTH = 200;
18187
- var TRIMMED_RULE = "must not have leading or trailing whitespace";
18188
18225
  var RESUME_REQUIRES_COMMAND_RULE = "requires snapshot.command";
18189
- var SINGLE_LINE_RULE = "must be a single line";
18190
- var agentCommand = external_exports.string().check(external_exports.minLength(1), external_exports.maxLength(MAX_AGENT_COMMAND_LENGTH), (ctx) => {
18191
- if (ctx.value.trim() !== ctx.value) addIssue(ctx, { code: "custom", path: [], input: ctx.value, message: TRIMMED_RULE });
18192
- if (/[\r\n]/.test(ctx.value)) addIssue(ctx, { code: "custom", path: [], input: ctx.value, message: SINGLE_LINE_RULE });
18193
- });
18194
- 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) => {
18226
+ 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) => {
18195
18227
  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 });
18196
18228
  });
18197
18229
  var DEFAULT_SNAPSHOT = { triggerZone: "RED" };
@@ -18272,21 +18304,25 @@ var GENERIC_ACTIONS = {
18272
18304
  RED: "finish or pause the current unit and tell the user what remains",
18273
18305
  CRITICAL: "stop starting new work; tell the user what remains"
18274
18306
  };
18275
- function zoneAction(zone, snapshot, mode = "off") {
18276
- if (zone === "GREEN" || !isAtOrAbove(zone, snapshot.triggerZone)) return GENERIC_ACTIONS[zone];
18277
- if (snapshot.command !== void 0) return snapshotAction(zone, snapshot.command);
18278
- if (mode !== "handoff") return GENERIC_ACTIONS[zone];
18307
+ function isResetDue(reading, snapshot) {
18308
+ if (snapshot.triggerPercentage !== void 0) return reading.usagePercentage >= snapshot.triggerPercentage;
18309
+ return reading.zone !== "GREEN" && isAtOrAbove(reading.zone, snapshot.triggerZone);
18310
+ }
18311
+ function zoneAction(zone, snapshot, options = {}) {
18312
+ if (!isResetDue({ zone, usagePercentage: options.usagePercentage ?? 0 }, snapshot)) return GENERIC_ACTIONS[zone];
18313
+ if (snapshot.command !== void 0) return snapshotAction(zone, (options.invoke ?? sentenceInvocation)(snapshot.command));
18314
+ if ((options.mode ?? "off") !== "handoff") return GENERIC_ACTIONS[zone];
18279
18315
  return `save handoff to ${HANDOFF_PATH}, end reply with ${SESSION_RESET_SIGNAL}`;
18280
18316
  }
18281
- function snapshotAction(zone, command) {
18317
+ function snapshotAction(zone, invocation) {
18282
18318
  const now = zone === "CRITICAL" ? " now" : "";
18283
- return `run "${command}"${now}, then end reply with ${SESSION_RESET_SIGNAL}`;
18319
+ return `run "${invocation}"${now}, then end reply with ${SESSION_RESET_SIGNAL}`;
18284
18320
  }
18285
18321
  function handoffResumeText(archivedPath) {
18286
18322
  return `${RESUME_PREFIX} Read "${archivedPath}" and continue the previous work from it.`;
18287
18323
  }
18288
- function resumeText(snapshot) {
18289
- return snapshot.resumeCommand === void 0 ? null : `${RESUME_PREFIX} Run "${snapshot.resumeCommand}" before continuing.`;
18324
+ function resumeText(snapshot, invoke = sentenceInvocation) {
18325
+ return snapshot.resumeCommand === void 0 ? null : `${RESUME_PREFIX} Run "${invoke(snapshot.resumeCommand)}" before continuing.`;
18290
18326
  }
18291
18327
  function isAtOrAbove(zone, trigger) {
18292
18328
  return ZONES.indexOf(zone) >= ZONES.indexOf(trigger);
@@ -18331,7 +18367,7 @@ async function resolveFailure(input2) {
18331
18367
  return { kind: "neutral" };
18332
18368
  }
18333
18369
  function deadlineBootDecision(input2) {
18334
- const text = resumeText((input2.config ?? DEFAULT_CONFIG).snapshot);
18370
+ const text = resumeText((input2.config ?? DEFAULT_CONFIG).snapshot, input2.descriptor?.skillInvocation);
18335
18371
  return text === null ? { kind: "neutral" } : { kind: "context", block: text };
18336
18372
  }
18337
18373
  async function recordRuntimeFailure(errors, record2) {
@@ -18449,7 +18485,7 @@ function isDebugModeInEffect(config2) {
18449
18485
 
18450
18486
  // src/core/services/injection-policy.ts
18451
18487
  function decideInjection(input2) {
18452
- if (input2.debug || input2.telemetry.injectionMode === "always") return true;
18488
+ if (input2.debug || input2.resetDue === true || input2.telemetry.injectionMode === "always") return true;
18453
18489
  return input2.zone !== "GREEN" || input2.usagePercentage >= input2.telemetry.activationThresholdPercentage;
18454
18490
  }
18455
18491
 
@@ -18600,7 +18636,7 @@ async function handleSessionReset(options, event, hooks = {}) {
18600
18636
  if (!options.descriptor.capabilities.some((entry) => entry.id === "session_boot" && entry.state === "supported")) return NEUTRAL;
18601
18637
  if (event.reason === "compact" && !COMPACTION_BOOT_HARNESSES.includes(options.descriptor.harness)) return NEUTRAL;
18602
18638
  hooks.onPhase?.("guidance");
18603
- const text = resumeText(options.config.snapshot) ?? await claimHandoff(options, event.reason, hooks.deadline);
18639
+ const text = resumeText(options.config.snapshot, options.descriptor.skillInvocation) ?? await claimHandoff(options, event.reason, hooks.deadline);
18604
18640
  return text === null ? NEUTRAL : { kind: "context", block: text };
18605
18641
  }
18606
18642
  async function claimHandoff(options, reason, deadline) {
@@ -18642,8 +18678,9 @@ async function handlePreInvocation(options, event, input2) {
18642
18678
  return telemetryDecision(options, summary.turns, readZone(options, { summary, turns: summary.turns, observedCharacters: 0, measured: input2.measured }));
18643
18679
  }
18644
18680
  async function telemetryDecision(options, turn, view) {
18645
- if (!decideInjection({ telemetry: options.config.telemetry, zone: view.zone, usagePercentage: view.percentage, debug: isDebugModeInEffect(options.config) })) return NEUTRAL2;
18646
- const action = zoneAction(view.zone, options.config.snapshot, restartMode(options.config));
18681
+ const resetDue = isResetDue({ zone: view.zone, usagePercentage: view.percentage }, options.config.snapshot);
18682
+ if (!decideInjection({ telemetry: options.config.telemetry, zone: view.zone, usagePercentage: view.percentage, debug: isDebugModeInEffect(options.config), resetDue })) return NEUTRAL2;
18683
+ const action = zoneAction(view.zone, options.config.snapshot, { mode: restartMode(options.config), invoke: options.descriptor.skillInvocation, usagePercentage: view.percentage });
18647
18684
  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) }) };
18648
18685
  }
18649
18686
  async function handleResponseEnd(options, event) {
@@ -18677,6 +18714,17 @@ import { createHash } from "node:crypto";
18677
18714
  import { mkdir, writeFile } from "node:fs/promises";
18678
18715
  import { join as join2, resolve as resolve3 } from "node:path";
18679
18716
 
18717
+ // src/core/contracts/context-reading.ts
18718
+ var RUNTIME_IGNORE_CONTENT = "*\n";
18719
+ var PERCENTAGE_MAXIMUM = 100;
18720
+ var contextReadingSchema = external_exports.strictObject({
18721
+ v: external_exports.literal(1),
18722
+ at: external_exports.string().check(external_exports.minLength(1)),
18723
+ windowTokens: external_exports.nullable(external_exports.int().check(external_exports.positive())),
18724
+ inputTokens: external_exports.nullable(external_exports.int().check(external_exports.nonnegative())),
18725
+ usedPercentage: external_exports.nullable(external_exports.number().check(external_exports.gte(0), external_exports.lte(PERCENTAGE_MAXIMUM)))
18726
+ });
18727
+
18680
18728
  // src/infrastructure/storage/runtime-state-files.ts
18681
18729
  import { readdir } from "node:fs/promises";
18682
18730
  import { join, relative, resolve as resolve2 } from "node:path";
@@ -18696,7 +18744,7 @@ async function listRuntimeStateFiles(root, readdirFn = readdir) {
18696
18744
  }
18697
18745
 
18698
18746
  // src/infrastructure/runtime/runtime-paths.ts
18699
- var RUNTIME_GITIGNORE_CONTENT = "*\n";
18747
+ var RUNTIME_GITIGNORE_CONTENT = RUNTIME_IGNORE_CONTENT;
18700
18748
  var SESSIONS_DIR_NAME = "sessions";
18701
18749
  var LEDGER_FILE_EXTENSION = ".jsonl";
18702
18750
  var SESSIONS_RELATIVE_PREFIX = `${RUNTIME_STATE_RELATIVE_DIR}/${SESSIONS_DIR_NAME}/`;
@@ -19132,7 +19180,7 @@ var cursorPayloadSchema = external_exports.looseObject({
19132
19180
  // src/infrastructure/harnesses/cursor/runtime.ts
19133
19181
  var HARNESS = "cursor";
19134
19182
  var ESTIMATION = { baselineTokens: 15e3, tokensPerTurn: 150 };
19135
- var cursorDescriptor = { harness: HARNESS, capabilities: CURSOR_CAPABILITIES, estimation: ESTIMATION, newSessionCommand: null };
19183
+ var cursorDescriptor = { harness: HARNESS, capabilities: CURSOR_CAPABILITIES, estimation: ESTIMATION, newSessionCommand: null, skillInvocation: CURSOR_SKILL_INVOCATION };
19136
19184
  function sessionOf(payload) {
19137
19185
  const sessionId = payload.session_id ?? payload.conversation_id;
19138
19186
  return { harness: HARNESS, sessionId: requireIdentifier(sessionId), agentId: null };
@@ -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/github-copilot-cli/skill-invocation.ts
16
+ var COPILOT_SKILL_INVOCATION = slashInvocation;
17
+
7
18
  // src/infrastructure/harnesses/common/runtime-support.ts
8
19
  import { realpath } from "node:fs/promises";
9
20
  import { dirname, resolve } from "node:path";
@@ -18160,6 +18171,34 @@ var HARNESS_IDS = [
18160
18171
  "antigravity-cli"
18161
18172
  ];
18162
18173
 
18174
+ // src/core/contracts/skill-name.ts
18175
+ var MAX_SKILL_NAME_LENGTH = 64;
18176
+ var NAME_SEGMENT = "[a-z0-9]+(?:-[a-z0-9]+)*";
18177
+ var NAMESPACE_SEPARATOR = ":";
18178
+ var INVOCATION_CHARACTERS = ["/", "$", "@"];
18179
+ var MAX_QUALIFIED_LENGTH = MAX_SKILL_NAME_LENGTH * 2 + NAMESPACE_SEPARATOR.length;
18180
+ function segmentFits(end) {
18181
+ return `(?=[^${NAMESPACE_SEPARATOR}]{1,${MAX_SKILL_NAME_LENGTH}}${end})`;
18182
+ }
18183
+ var PREFIX = `(?:${segmentFits(NAMESPACE_SEPARATOR)}${NAME_SEGMENT}${NAMESPACE_SEPARATOR})?`;
18184
+ var SKILL_NAME_PATTERN = new RegExp(`^${PREFIX}${segmentFits("$")}${NAME_SEGMENT}$`);
18185
+ 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`;
18186
+ function isSkillName(value) {
18187
+ return SKILL_NAME_PATTERN.test(value);
18188
+ }
18189
+ function skillNameProblem(value) {
18190
+ if (value === "") return "must not be empty";
18191
+ if (isSkillName(value)) return null;
18192
+ const bare = INVOCATION_CHARACTERS.includes(value.charAt(0)) ? value.slice(1) : null;
18193
+ if (bare !== null && isSkillName(bare)) return `"${value}" is not a skill name; use the bare name ${bare}`;
18194
+ return `"${value}" is not a skill name; use ${SHAPE_RULE}`;
18195
+ }
18196
+ var skillNameSchema = external_exports.string().check((ctx) => {
18197
+ const message = skillNameProblem(ctx.value);
18198
+ if (message !== null) ctx.issues.push({ code: "custom", input: ctx.value, message });
18199
+ });
18200
+ external_exports.globalRegistry.add(skillNameSchema, { pattern: SKILL_NAME_PATTERN.source, maxLength: MAX_QUALIFIED_LENGTH });
18201
+
18163
18202
  // src/core/contracts/zones.ts
18164
18203
  var ZONES = ["GREEN", "YELLOW", "RED", "CRITICAL"];
18165
18204
  var USAGE_SOURCES = ["measured", "estimated"];
@@ -18183,15 +18222,8 @@ function uniqueCheck(message) {
18183
18222
  if (!isUnique(ctx.value)) addIssue(ctx, { code: "custom", path: [], input: ctx.value, message });
18184
18223
  };
18185
18224
  }
18186
- var MAX_AGENT_COMMAND_LENGTH = 200;
18187
- var TRIMMED_RULE = "must not have leading or trailing whitespace";
18188
18225
  var RESUME_REQUIRES_COMMAND_RULE = "requires snapshot.command";
18189
- var SINGLE_LINE_RULE = "must be a single line";
18190
- var agentCommand = external_exports.string().check(external_exports.minLength(1), external_exports.maxLength(MAX_AGENT_COMMAND_LENGTH), (ctx) => {
18191
- if (ctx.value.trim() !== ctx.value) addIssue(ctx, { code: "custom", path: [], input: ctx.value, message: TRIMMED_RULE });
18192
- if (/[\r\n]/.test(ctx.value)) addIssue(ctx, { code: "custom", path: [], input: ctx.value, message: SINGLE_LINE_RULE });
18193
- });
18194
- 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) => {
18226
+ 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) => {
18195
18227
  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 });
18196
18228
  });
18197
18229
  var DEFAULT_SNAPSHOT = { triggerZone: "RED" };
@@ -18272,21 +18304,25 @@ var GENERIC_ACTIONS = {
18272
18304
  RED: "finish or pause the current unit and tell the user what remains",
18273
18305
  CRITICAL: "stop starting new work; tell the user what remains"
18274
18306
  };
18275
- function zoneAction(zone, snapshot, mode = "off") {
18276
- if (zone === "GREEN" || !isAtOrAbove(zone, snapshot.triggerZone)) return GENERIC_ACTIONS[zone];
18277
- if (snapshot.command !== void 0) return snapshotAction(zone, snapshot.command);
18278
- if (mode !== "handoff") return GENERIC_ACTIONS[zone];
18307
+ function isResetDue(reading, snapshot) {
18308
+ if (snapshot.triggerPercentage !== void 0) return reading.usagePercentage >= snapshot.triggerPercentage;
18309
+ return reading.zone !== "GREEN" && isAtOrAbove(reading.zone, snapshot.triggerZone);
18310
+ }
18311
+ function zoneAction(zone, snapshot, options = {}) {
18312
+ if (!isResetDue({ zone, usagePercentage: options.usagePercentage ?? 0 }, snapshot)) return GENERIC_ACTIONS[zone];
18313
+ if (snapshot.command !== void 0) return snapshotAction(zone, (options.invoke ?? sentenceInvocation)(snapshot.command));
18314
+ if ((options.mode ?? "off") !== "handoff") return GENERIC_ACTIONS[zone];
18279
18315
  return `save handoff to ${HANDOFF_PATH}, end reply with ${SESSION_RESET_SIGNAL}`;
18280
18316
  }
18281
- function snapshotAction(zone, command) {
18317
+ function snapshotAction(zone, invocation) {
18282
18318
  const now = zone === "CRITICAL" ? " now" : "";
18283
- return `run "${command}"${now}, then end reply with ${SESSION_RESET_SIGNAL}`;
18319
+ return `run "${invocation}"${now}, then end reply with ${SESSION_RESET_SIGNAL}`;
18284
18320
  }
18285
18321
  function handoffResumeText(archivedPath) {
18286
18322
  return `${RESUME_PREFIX} Read "${archivedPath}" and continue the previous work from it.`;
18287
18323
  }
18288
- function resumeText(snapshot) {
18289
- return snapshot.resumeCommand === void 0 ? null : `${RESUME_PREFIX} Run "${snapshot.resumeCommand}" before continuing.`;
18324
+ function resumeText(snapshot, invoke = sentenceInvocation) {
18325
+ return snapshot.resumeCommand === void 0 ? null : `${RESUME_PREFIX} Run "${invoke(snapshot.resumeCommand)}" before continuing.`;
18290
18326
  }
18291
18327
  function isAtOrAbove(zone, trigger) {
18292
18328
  return ZONES.indexOf(zone) >= ZONES.indexOf(trigger);
@@ -18331,7 +18367,7 @@ async function resolveFailure(input2) {
18331
18367
  return { kind: "neutral" };
18332
18368
  }
18333
18369
  function deadlineBootDecision(input2) {
18334
- const text = resumeText((input2.config ?? DEFAULT_CONFIG).snapshot);
18370
+ const text = resumeText((input2.config ?? DEFAULT_CONFIG).snapshot, input2.descriptor?.skillInvocation);
18335
18371
  return text === null ? { kind: "neutral" } : { kind: "context", block: text };
18336
18372
  }
18337
18373
  async function recordRuntimeFailure(errors, record2) {
@@ -18442,7 +18478,7 @@ function isDebugModeInEffect(config2) {
18442
18478
 
18443
18479
  // src/core/services/injection-policy.ts
18444
18480
  function decideInjection(input2) {
18445
- if (input2.debug || input2.telemetry.injectionMode === "always") return true;
18481
+ if (input2.debug || input2.resetDue === true || input2.telemetry.injectionMode === "always") return true;
18446
18482
  return input2.zone !== "GREEN" || input2.usagePercentage >= input2.telemetry.activationThresholdPercentage;
18447
18483
  }
18448
18484
 
@@ -18593,7 +18629,7 @@ async function handleSessionReset(options, event, hooks = {}) {
18593
18629
  if (!options.descriptor.capabilities.some((entry) => entry.id === "session_boot" && entry.state === "supported")) return NEUTRAL;
18594
18630
  if (event.reason === "compact" && !COMPACTION_BOOT_HARNESSES.includes(options.descriptor.harness)) return NEUTRAL;
18595
18631
  hooks.onPhase?.("guidance");
18596
- const text = resumeText(options.config.snapshot) ?? await claimHandoff(options, event.reason, hooks.deadline);
18632
+ const text = resumeText(options.config.snapshot, options.descriptor.skillInvocation) ?? await claimHandoff(options, event.reason, hooks.deadline);
18597
18633
  return text === null ? NEUTRAL : { kind: "context", block: text };
18598
18634
  }
18599
18635
  async function claimHandoff(options, reason, deadline) {
@@ -18635,8 +18671,9 @@ async function handlePreInvocation(options, event, input2) {
18635
18671
  return telemetryDecision(options, summary.turns, readZone(options, { summary, turns: summary.turns, observedCharacters: 0, measured: input2.measured }));
18636
18672
  }
18637
18673
  async function telemetryDecision(options, turn, view) {
18638
- if (!decideInjection({ telemetry: options.config.telemetry, zone: view.zone, usagePercentage: view.percentage, debug: isDebugModeInEffect(options.config) })) return NEUTRAL2;
18639
- const action = zoneAction(view.zone, options.config.snapshot, restartMode(options.config));
18674
+ const resetDue = isResetDue({ zone: view.zone, usagePercentage: view.percentage }, options.config.snapshot);
18675
+ if (!decideInjection({ telemetry: options.config.telemetry, zone: view.zone, usagePercentage: view.percentage, debug: isDebugModeInEffect(options.config), resetDue })) return NEUTRAL2;
18676
+ const action = zoneAction(view.zone, options.config.snapshot, { mode: restartMode(options.config), invoke: options.descriptor.skillInvocation, usagePercentage: view.percentage });
18640
18677
  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) }) };
18641
18678
  }
18642
18679
  async function handleResponseEnd(options, event) {
@@ -18670,6 +18707,17 @@ import { createHash } from "node:crypto";
18670
18707
  import { mkdir, writeFile } from "node:fs/promises";
18671
18708
  import { join as join2, resolve as resolve3 } from "node:path";
18672
18709
 
18710
+ // src/core/contracts/context-reading.ts
18711
+ var RUNTIME_IGNORE_CONTENT = "*\n";
18712
+ var PERCENTAGE_MAXIMUM = 100;
18713
+ var contextReadingSchema = external_exports.strictObject({
18714
+ v: external_exports.literal(1),
18715
+ at: external_exports.string().check(external_exports.minLength(1)),
18716
+ windowTokens: external_exports.nullable(external_exports.int().check(external_exports.positive())),
18717
+ inputTokens: external_exports.nullable(external_exports.int().check(external_exports.nonnegative())),
18718
+ usedPercentage: external_exports.nullable(external_exports.number().check(external_exports.gte(0), external_exports.lte(PERCENTAGE_MAXIMUM)))
18719
+ });
18720
+
18673
18721
  // src/infrastructure/storage/runtime-state-files.ts
18674
18722
  import { readdir } from "node:fs/promises";
18675
18723
  import { join, relative, resolve as resolve2 } from "node:path";
@@ -18689,7 +18737,7 @@ async function listRuntimeStateFiles(root, readdirFn = readdir) {
18689
18737
  }
18690
18738
 
18691
18739
  // src/infrastructure/runtime/runtime-paths.ts
18692
- var RUNTIME_GITIGNORE_CONTENT = "*\n";
18740
+ var RUNTIME_GITIGNORE_CONTENT = RUNTIME_IGNORE_CONTENT;
18693
18741
  var SESSIONS_DIR_NAME = "sessions";
18694
18742
  var LEDGER_FILE_EXTENSION = ".jsonl";
18695
18743
  var SESSIONS_RELATIVE_PREFIX = `${RUNTIME_STATE_RELATIVE_DIR}/${SESSIONS_DIR_NAME}/`;
@@ -19126,7 +19174,7 @@ var ESTIMATION = { baselineTokens: 15e3, tokensPerTurn: 150 };
19126
19174
  var READ_TOOLS = ["view"];
19127
19175
  var WRITE_TOOLS = ["edit", "create"];
19128
19176
  var SHELL_TOOLS = ["bash", "powershell"];
19129
- var copilotDescriptor = { harness: HARNESS, capabilities: COPILOT_CAPABILITIES, estimation: ESTIMATION, newSessionCommand: null };
19177
+ var copilotDescriptor = { harness: HARNESS, capabilities: COPILOT_CAPABILITIES, estimation: ESTIMATION, newSessionCommand: null, skillInvocation: COPILOT_SKILL_INVOCATION };
19130
19178
  function sessionOf(payload) {
19131
19179
  return { harness: HARNESS, sessionId: requireIdentifier(payload.sessionId), agentId: null };
19132
19180
  }