@scotthamilton77/sidekick 0.1.3 → 0.1.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/daemon.js CHANGED
@@ -16237,7 +16237,7 @@ var require_state = __commonJS({
16237
16237
  "../types/dist/services/state.js"(exports2) {
16238
16238
  "use strict";
16239
16239
  Object.defineProperty(exports2, "__esModule", { value: true });
16240
- exports2.DEFAULT_LATENCY_STATS = exports2.LLMMetricsStateSchema = exports2.LLMSessionTotalsSchema = exports2.LLMProviderMetricsSchema = exports2.LLMModelMetricsSchema = exports2.LLMLatencyStatsSchema = exports2.DEFAULT_PROJECT_METRICS = exports2.DEFAULT_BASE_METRICS = exports2.SessionContextMetricsSchema = exports2.ProjectContextMetricsSchema = exports2.BaseTokenMetricsStateSchema = exports2.VCUnverifiedStateSchema = exports2.PRBaselineStateSchema = exports2.EMPTY_LOG_METRICS = exports2.LogMetricsStateSchema = exports2.TranscriptMetricsStateSchema = exports2.ResumeMessageStateSchema = exports2.SnarkyMessageStateSchema = exports2.SummaryCountdownStateSchema = exports2.LastStagedPersonaSchema = exports2.SessionPersonaStateSchema = exports2.SESSION_SUMMARY_PLACEHOLDERS = exports2.SessionSummaryStateSchema = void 0;
16240
+ exports2.DEFAULT_LATENCY_STATS = exports2.LLMMetricsStateSchema = exports2.LLMSessionTotalsSchema = exports2.LLMProviderMetricsSchema = exports2.LLMModelMetricsSchema = exports2.LLMLatencyStatsSchema = exports2.DEFAULT_PROJECT_METRICS = exports2.DEFAULT_BASE_METRICS = exports2.SessionContextMetricsSchema = exports2.ProjectContextMetricsSchema = exports2.BaseTokenMetricsStateSchema = exports2.UPSThrottleStateSchema = exports2.VerificationToolsStateSchema = exports2.VerificationToolStatusSchema = exports2.VCUnverifiedStateSchema = exports2.PRBaselineStateSchema = exports2.EMPTY_LOG_METRICS = exports2.LogMetricsStateSchema = exports2.TranscriptMetricsStateSchema = exports2.ResumeMessageStateSchema = exports2.SnarkyMessageStateSchema = exports2.SummaryCountdownStateSchema = exports2.LastStagedPersonaSchema = exports2.SessionPersonaStateSchema = exports2.SESSION_SUMMARY_PLACEHOLDERS = exports2.SessionSummaryStateSchema = void 0;
16241
16241
  exports2.createDefaultLLMMetrics = createDefaultLLMMetrics;
16242
16242
  var zod_1 = require_zod();
16243
16243
  exports2.SessionSummaryStateSchema = zod_1.z.object({
@@ -16386,6 +16386,25 @@ var require_state = __commonJS({
16386
16386
  confidence: zod_1.z.number()
16387
16387
  })
16388
16388
  });
16389
+ exports2.VerificationToolStatusSchema = zod_1.z.object({
16390
+ /** Current state: staged (needs run), verified (recently run), cooldown (post-verified, counting edits) */
16391
+ status: zod_1.z.enum(["staged", "verified", "cooldown"]),
16392
+ /** Number of qualifying file edits since last verification */
16393
+ editsSinceVerified: zod_1.z.number(),
16394
+ /** Unix timestamp (ms) when last verified, null if never */
16395
+ lastVerifiedAt: zod_1.z.number().nullable(),
16396
+ /** Unix timestamp (ms) when last staged, null if never */
16397
+ lastStagedAt: zod_1.z.number().nullable(),
16398
+ /** tool_id of the pattern that last matched (metadata for future scope-aware logic) */
16399
+ lastMatchedToolId: zod_1.z.string().nullable().optional(),
16400
+ /** Scope of the last matched pattern */
16401
+ lastMatchedScope: zod_1.z.enum(["project", "package", "file"]).nullable().optional()
16402
+ });
16403
+ exports2.VerificationToolsStateSchema = zod_1.z.record(zod_1.z.string(), exports2.VerificationToolStatusSchema);
16404
+ exports2.UPSThrottleStateSchema = zod_1.z.object({
16405
+ /** Number of conversation messages since the reminder was last staged */
16406
+ messagesSinceLastStaging: zod_1.z.number()
16407
+ });
16389
16408
  exports2.BaseTokenMetricsStateSchema = zod_1.z.object({
16390
16409
  /** System prompt tokens (~3.2k) */
16391
16410
  systemPromptTokens: zod_1.z.number(),
@@ -44274,7 +44293,10 @@ var require_structured_logging = __commonJS({
44274
44293
  mkdir: true
44275
44294
  }).then((stream) => {
44276
44295
  this.realStream = stream;
44277
- this.drainPending((chunk, cb) => stream.write(chunk, cb));
44296
+ this.drainPending((chunk, cb) => {
44297
+ stream.write(chunk);
44298
+ cb();
44299
+ });
44278
44300
  }).catch((err) => {
44279
44301
  process.stderr.write(`[sidekick] pino-roll init failed, using appendFileSync fallback: ${err}
44280
44302
  `);
@@ -44283,7 +44305,8 @@ var require_structured_logging = __commonJS({
44283
44305
  }
44284
44306
  _write(chunk, _encoding, callback) {
44285
44307
  if (this.realStream) {
44286
- this.realStream.write(chunk, callback);
44308
+ this.realStream.write(chunk);
44309
+ callback();
44287
44310
  } else if (this.initPending) {
44288
44311
  this.pending.push({ chunk, callback });
44289
44312
  } else {
@@ -69925,7 +69948,154 @@ var require_types2 = __commonJS({
69925
69948
  "../feature-reminders/dist/types.js"(exports2) {
69926
69949
  "use strict";
69927
69950
  Object.defineProperty(exports2, "__esModule", { value: true });
69928
- exports2.ReminderIds = exports2.DEFAULT_REMINDERS_SETTINGS = exports2.DEFAULT_COMPLETION_DETECTION_SETTINGS = exports2.DEFAULT_SOURCE_CODE_PATTERNS = void 0;
69951
+ exports2.ALL_VC_REMINDER_IDS = exports2.VC_TOOL_REMINDER_IDS = exports2.ReminderIds = exports2.DEFAULT_REMINDERS_SETTINGS = exports2.DEFAULT_COMPLETION_DETECTION_SETTINGS = exports2.DEFAULT_SOURCE_CODE_PATTERNS = exports2.DEFAULT_VERIFICATION_TOOLS = exports2.VerificationToolsMapSchema = exports2.VerificationToolConfigSchema = exports2.ToolPatternSchema = exports2.ToolPatternScopeSchema = void 0;
69952
+ var zod_1 = require_zod2();
69953
+ exports2.ToolPatternScopeSchema = zod_1.z.enum(["project", "package", "file"]);
69954
+ exports2.ToolPatternSchema = zod_1.z.object({
69955
+ tool_id: zod_1.z.string(),
69956
+ tool: zod_1.z.string().nullable(),
69957
+ scope: exports2.ToolPatternScopeSchema.default("project")
69958
+ });
69959
+ exports2.VerificationToolConfigSchema = zod_1.z.object({
69960
+ enabled: zod_1.z.boolean(),
69961
+ patterns: zod_1.z.array(exports2.ToolPatternSchema).min(1),
69962
+ clearing_threshold: zod_1.z.number().int().positive(),
69963
+ clearing_patterns: zod_1.z.array(zod_1.z.string()).min(1)
69964
+ });
69965
+ exports2.VerificationToolsMapSchema = zod_1.z.record(zod_1.z.string(), exports2.VerificationToolConfigSchema);
69966
+ exports2.DEFAULT_VERIFICATION_TOOLS = {
69967
+ build: {
69968
+ enabled: true,
69969
+ patterns: [
69970
+ // TypeScript/JavaScript
69971
+ { tool_id: "tsc", tool: "tsc", scope: "project" },
69972
+ { tool_id: "esbuild", tool: "esbuild", scope: "file" },
69973
+ { tool_id: "pnpm-filter-build", tool: "pnpm --filter * build", scope: "package" },
69974
+ { tool_id: "pnpm-build", tool: "pnpm build", scope: "project" },
69975
+ { tool_id: "npm-build", tool: "npm run build", scope: "project" },
69976
+ { tool_id: "yarn-workspace-build", tool: "yarn workspace * build", scope: "package" },
69977
+ { tool_id: "yarn-build", tool: "yarn build", scope: "project" },
69978
+ // Python
69979
+ { tool_id: "python-setup-build", tool: "python setup.py build", scope: "project" },
69980
+ { tool_id: "pip-install", tool: "pip install", scope: "project" },
69981
+ { tool_id: "poetry-build", tool: "poetry build", scope: "project" },
69982
+ // JVM
69983
+ { tool_id: "mvn-compile", tool: "mvn compile", scope: "project" },
69984
+ { tool_id: "mvn-package", tool: "mvn package", scope: "project" },
69985
+ { tool_id: "gradle-build", tool: "gradle build", scope: "project" },
69986
+ { tool_id: "gradlew-build", tool: "./gradlew build", scope: "project" },
69987
+ // Go
69988
+ { tool_id: "go-build", tool: "go build", scope: "project" },
69989
+ // Rust
69990
+ { tool_id: "cargo-build", tool: "cargo build", scope: "project" },
69991
+ // C/C++
69992
+ { tool_id: "make-build", tool: "make build", scope: "project" },
69993
+ { tool_id: "make-default", tool: "make", scope: "project" },
69994
+ { tool_id: "cmake-build", tool: "cmake --build", scope: "project" },
69995
+ // Containers
69996
+ { tool_id: "docker-build", tool: "docker build", scope: "project" }
69997
+ ],
69998
+ clearing_threshold: 3,
69999
+ clearing_patterns: [
70000
+ "**/*.ts",
70001
+ "**/*.tsx",
70002
+ "**/*.js",
70003
+ "**/*.jsx",
70004
+ "**/*.py",
70005
+ "**/*.java",
70006
+ "**/*.kt",
70007
+ "**/*.go",
70008
+ "**/*.rs",
70009
+ "**/*.c",
70010
+ "**/*.cpp",
70011
+ "**/*.cs"
70012
+ ]
70013
+ },
70014
+ typecheck: {
70015
+ enabled: true,
70016
+ patterns: [
70017
+ { tool_id: "tsc-noEmit", tool: "tsc --noEmit", scope: "project" },
70018
+ { tool_id: "pnpm-filter-typecheck", tool: "pnpm --filter * typecheck", scope: "package" },
70019
+ { tool_id: "pnpm-typecheck", tool: "pnpm typecheck", scope: "project" },
70020
+ { tool_id: "npm-typecheck", tool: "npm run typecheck", scope: "project" },
70021
+ { tool_id: "yarn-workspace-typecheck", tool: "yarn workspace * typecheck", scope: "package" },
70022
+ { tool_id: "yarn-typecheck", tool: "yarn typecheck", scope: "project" },
70023
+ { tool_id: "mypy", tool: "mypy", scope: "project" },
70024
+ { tool_id: "pyright", tool: "pyright", scope: "project" },
70025
+ { tool_id: "pytype", tool: "pytype", scope: "project" },
70026
+ { tool_id: "go-vet", tool: "go vet", scope: "project" }
70027
+ ],
70028
+ clearing_threshold: 3,
70029
+ clearing_patterns: ["**/*.ts", "**/*.tsx", "**/*.py", "**/*.go"]
70030
+ },
70031
+ test: {
70032
+ enabled: true,
70033
+ patterns: [
70034
+ { tool_id: "vitest", tool: "vitest", scope: "project" },
70035
+ { tool_id: "jest", tool: "jest", scope: "project" },
70036
+ { tool_id: "pnpm-filter-test", tool: "pnpm --filter * test", scope: "package" },
70037
+ { tool_id: "pnpm-test", tool: "pnpm test", scope: "project" },
70038
+ { tool_id: "npm-test", tool: "npm test", scope: "project" },
70039
+ { tool_id: "yarn-workspace-test", tool: "yarn workspace * test", scope: "package" },
70040
+ { tool_id: "yarn-test", tool: "yarn test", scope: "project" },
70041
+ { tool_id: "pytest", tool: "pytest", scope: "project" },
70042
+ { tool_id: "python-pytest", tool: "python -m pytest", scope: "project" },
70043
+ { tool_id: "python-unittest", tool: "python -m unittest", scope: "project" },
70044
+ { tool_id: "mvn-test", tool: "mvn test", scope: "project" },
70045
+ { tool_id: "gradle-test", tool: "gradle test", scope: "project" },
70046
+ { tool_id: "gradlew-test", tool: "./gradlew test", scope: "project" },
70047
+ { tool_id: "go-test", tool: "go test", scope: "project" },
70048
+ { tool_id: "cargo-test", tool: "cargo test", scope: "project" },
70049
+ { tool_id: "dotnet-test", tool: "dotnet test", scope: "project" },
70050
+ { tool_id: "make-test", tool: "make test", scope: "project" }
70051
+ ],
70052
+ clearing_threshold: 3,
70053
+ clearing_patterns: [
70054
+ "**/*.ts",
70055
+ "**/*.tsx",
70056
+ "**/*.js",
70057
+ "**/*.jsx",
70058
+ "**/*.py",
70059
+ "**/*.java",
70060
+ "**/*.kt",
70061
+ "**/*.go",
70062
+ "**/*.rs",
70063
+ "**/*.test.*",
70064
+ "**/*.spec.*",
70065
+ "**/test_*"
70066
+ ]
70067
+ },
70068
+ lint: {
70069
+ enabled: true,
70070
+ patterns: [
70071
+ { tool_id: "eslint", tool: "eslint", scope: "project" },
70072
+ { tool_id: "pnpm-filter-lint", tool: "pnpm --filter * lint", scope: "package" },
70073
+ { tool_id: "pnpm-lint", tool: "pnpm lint", scope: "project" },
70074
+ { tool_id: "npm-lint", tool: "npm run lint", scope: "project" },
70075
+ { tool_id: "yarn-workspace-lint", tool: "yarn workspace * lint", scope: "package" },
70076
+ { tool_id: "yarn-lint", tool: "yarn lint", scope: "project" },
70077
+ { tool_id: "ruff", tool: "ruff", scope: "project" },
70078
+ { tool_id: "flake8", tool: "flake8", scope: "project" },
70079
+ { tool_id: "pylint", tool: "pylint", scope: "project" },
70080
+ { tool_id: "golangci-lint", tool: "golangci-lint", scope: "project" },
70081
+ { tool_id: "cargo-clippy", tool: "cargo clippy", scope: "project" },
70082
+ { tool_id: "ktlint", tool: "ktlint", scope: "project" },
70083
+ { tool_id: "dotnet-format", tool: "dotnet format", scope: "project" }
70084
+ ],
70085
+ clearing_threshold: 5,
70086
+ clearing_patterns: [
70087
+ "**/*.ts",
70088
+ "**/*.tsx",
70089
+ "**/*.js",
70090
+ "**/*.jsx",
70091
+ "**/*.py",
70092
+ "**/*.java",
70093
+ "**/*.kt",
70094
+ "**/*.go",
70095
+ "**/*.rs"
70096
+ ]
70097
+ }
70098
+ };
69929
70099
  exports2.DEFAULT_SOURCE_CODE_PATTERNS = [
69930
70100
  // TypeScript/JavaScript
69931
70101
  "**/*.ts",
@@ -69975,17 +70145,65 @@ var require_types2 = __commonJS({
69975
70145
  exports2.DEFAULT_REMINDERS_SETTINGS = {
69976
70146
  pause_and_reflect_threshold: 60,
69977
70147
  source_code_patterns: exports2.DEFAULT_SOURCE_CODE_PATTERNS,
69978
- max_verification_cycles: -1
70148
+ max_verification_cycles: -1,
69979
70149
  // -1 = unlimited, 0 = disabled
70150
+ verification_tools: exports2.DEFAULT_VERIFICATION_TOOLS,
70151
+ user_prompt_submit_threshold: 10
69980
70152
  };
69981
70153
  exports2.ReminderIds = {
69982
70154
  USER_PROMPT_SUBMIT: "user-prompt-submit",
69983
70155
  PAUSE_AND_REFLECT: "pause-and-reflect",
69984
70156
  VERIFY_COMPLETION: "verify-completion",
70157
+ VC_BUILD: "vc-build",
70158
+ VC_TYPECHECK: "vc-typecheck",
70159
+ VC_TEST: "vc-test",
70160
+ VC_LINT: "vc-lint",
69985
70161
  REMEMBER_YOUR_PERSONA: "remember-your-persona",
69986
70162
  PERSONA_CHANGED: "persona-changed",
69987
70163
  USER_PROFILE: "user-profile"
69988
70164
  };
70165
+ exports2.VC_TOOL_REMINDER_IDS = [
70166
+ exports2.ReminderIds.VC_BUILD,
70167
+ exports2.ReminderIds.VC_TYPECHECK,
70168
+ exports2.ReminderIds.VC_TEST,
70169
+ exports2.ReminderIds.VC_LINT
70170
+ ];
70171
+ exports2.ALL_VC_REMINDER_IDS = [exports2.ReminderIds.VERIFY_COMPLETION, ...exports2.VC_TOOL_REMINDER_IDS];
70172
+ }
70173
+ });
70174
+
70175
+ // ../feature-reminders/dist/state.js
70176
+ var require_state3 = __commonJS({
70177
+ "../feature-reminders/dist/state.js"(exports2) {
70178
+ "use strict";
70179
+ Object.defineProperty(exports2, "__esModule", { value: true });
70180
+ exports2.createRemindersState = createRemindersState;
70181
+ var core_1 = require_dist4();
70182
+ var types_1 = require_dist();
70183
+ var PRBaselineDescriptor = (0, core_1.sessionState)("pr-baseline.json", types_1.PRBaselineStateSchema, {
70184
+ defaultValue: null,
70185
+ trackHistory: true
70186
+ });
70187
+ var VCUnverifiedDescriptor = (0, core_1.sessionState)("vc-unverified.json", types_1.VCUnverifiedStateSchema, {
70188
+ defaultValue: null,
70189
+ trackHistory: true
70190
+ });
70191
+ var VerificationToolsDescriptor = (0, core_1.sessionState)("verification-tools.json", types_1.VerificationToolsStateSchema, {
70192
+ defaultValue: {},
70193
+ trackHistory: false
70194
+ });
70195
+ var UPSThrottleDescriptor = (0, core_1.sessionState)("ups-throttle.json", types_1.UPSThrottleStateSchema, {
70196
+ defaultValue: { messagesSinceLastStaging: 0 },
70197
+ trackHistory: false
70198
+ });
70199
+ function createRemindersState(stateService) {
70200
+ return {
70201
+ prBaseline: new core_1.SessionStateAccessor(stateService, PRBaselineDescriptor),
70202
+ vcUnverified: new core_1.SessionStateAccessor(stateService, VCUnverifiedDescriptor),
70203
+ verificationTools: new core_1.SessionStateAccessor(stateService, VerificationToolsDescriptor),
70204
+ upsThrottle: new core_1.SessionStateAccessor(stateService, UPSThrottleDescriptor)
70205
+ };
70206
+ }
69989
70207
  }
69990
70208
  });
69991
70209
 
@@ -69998,6 +70216,8 @@ var require_stage_default_user_prompt = __commonJS({
69998
70216
  var types_1 = require_dist();
69999
70217
  var staging_handler_utils_js_1 = require_staging_handler_utils();
70000
70218
  var types_js_1 = require_types2();
70219
+ var reminder_utils_js_1 = require_reminder_utils();
70220
+ var state_js_1 = require_state3();
70001
70221
  function registerStageDefaultUserPrompt(context) {
70002
70222
  (0, staging_handler_utils_js_1.createStagingHandler)(context, {
70003
70223
  id: "reminders:stage-default-user-prompt",
@@ -70015,6 +70235,23 @@ var require_stage_default_user_prompt = __commonJS({
70015
70235
  };
70016
70236
  }
70017
70237
  });
70238
+ if ((0, types_1.isDaemonContext)(context)) {
70239
+ const startCtx = context;
70240
+ context.handlers.register({
70241
+ id: "reminders:ups-throttle-reset-session-start",
70242
+ priority: 49,
70243
+ filter: { kind: "hook", hooks: ["SessionStart"] },
70244
+ handler: async (event) => {
70245
+ if (!(0, types_1.isHookEvent)(event) || !(0, types_1.isSessionStartEvent)(event))
70246
+ return;
70247
+ const sessionId = event.context.sessionId;
70248
+ if (!sessionId)
70249
+ return;
70250
+ const remindersState = (0, state_js_1.createRemindersState)(startCtx.stateService);
70251
+ await remindersState.upsThrottle.write(sessionId, { messagesSinceLastStaging: 0 });
70252
+ }
70253
+ });
70254
+ }
70018
70255
  (0, staging_handler_utils_js_1.createStagingHandler)(context, {
70019
70256
  id: "reminders:stage-default-user-prompt-after-bulk",
70020
70257
  priority: 50,
@@ -70031,31 +70268,68 @@ var require_stage_default_user_prompt = __commonJS({
70031
70268
  };
70032
70269
  }
70033
70270
  });
70034
- }
70035
- }
70036
- });
70037
-
70038
- // ../feature-reminders/dist/state.js
70039
- var require_state3 = __commonJS({
70040
- "../feature-reminders/dist/state.js"(exports2) {
70041
- "use strict";
70042
- Object.defineProperty(exports2, "__esModule", { value: true });
70043
- exports2.createRemindersState = createRemindersState;
70044
- var core_1 = require_dist4();
70045
- var types_1 = require_dist();
70046
- var PRBaselineDescriptor = (0, core_1.sessionState)("pr-baseline.json", types_1.PRBaselineStateSchema, {
70047
- defaultValue: null,
70048
- trackHistory: true
70049
- });
70050
- var VCUnverifiedDescriptor = (0, core_1.sessionState)("vc-unverified.json", types_1.VCUnverifiedStateSchema, {
70051
- defaultValue: null,
70052
- trackHistory: true
70053
- });
70054
- function createRemindersState(stateService) {
70055
- return {
70056
- prBaseline: new core_1.SessionStateAccessor(stateService, PRBaselineDescriptor),
70057
- vcUnverified: new core_1.SessionStateAccessor(stateService, VCUnverifiedDescriptor)
70058
- };
70271
+ if ((0, types_1.isDaemonContext)(context)) {
70272
+ const bulkCtx = context;
70273
+ context.handlers.register({
70274
+ id: "reminders:ups-throttle-reset-bulk",
70275
+ priority: 49,
70276
+ filter: { kind: "transcript", eventTypes: ["BulkProcessingComplete"] },
70277
+ handler: async (event) => {
70278
+ if (!(0, types_1.isTranscriptEvent)(event))
70279
+ return;
70280
+ if (event.metadata.isBulkProcessing)
70281
+ return;
70282
+ const sessionId = event.context?.sessionId;
70283
+ if (!sessionId)
70284
+ return;
70285
+ const remindersState = (0, state_js_1.createRemindersState)(bulkCtx.stateService);
70286
+ await remindersState.upsThrottle.write(sessionId, { messagesSinceLastStaging: 0 });
70287
+ }
70288
+ });
70289
+ }
70290
+ if (!(0, types_1.isDaemonContext)(context))
70291
+ return;
70292
+ context.handlers.register({
70293
+ id: "reminders:ups-throttle-restage",
70294
+ priority: 50,
70295
+ filter: { kind: "transcript", eventTypes: ["UserPrompt", "AssistantMessage"] },
70296
+ handler: async (event, ctx) => {
70297
+ if (!(0, types_1.isTranscriptEvent)(event))
70298
+ return;
70299
+ if (!(0, types_1.isDaemonContext)(ctx))
70300
+ return;
70301
+ if (event.metadata.isBulkProcessing)
70302
+ return;
70303
+ const sessionId = event.context?.sessionId;
70304
+ if (!sessionId)
70305
+ return;
70306
+ const handlerCtx = ctx;
70307
+ const remindersState = (0, state_js_1.createRemindersState)(handlerCtx.stateService);
70308
+ const result = await remindersState.upsThrottle.read(sessionId);
70309
+ const current = result.data.messagesSinceLastStaging;
70310
+ const featureConfig = handlerCtx.config.getFeature("reminders");
70311
+ const config = { ...types_js_1.DEFAULT_REMINDERS_SETTINGS, ...featureConfig.settings };
70312
+ const threshold = config.user_prompt_submit_threshold ?? 10;
70313
+ const newCount = current + 1;
70314
+ if (newCount >= threshold) {
70315
+ const reminder = (0, reminder_utils_js_1.resolveReminder)(types_js_1.ReminderIds.USER_PROMPT_SUBMIT, {
70316
+ context: { sessionId },
70317
+ assets: handlerCtx.assets
70318
+ });
70319
+ if (reminder) {
70320
+ await (0, reminder_utils_js_1.stageReminder)(handlerCtx, "UserPromptSubmit", reminder);
70321
+ handlerCtx.logger.debug("UPS throttle: re-staged reminder", {
70322
+ sessionId,
70323
+ messageCount: newCount,
70324
+ threshold
70325
+ });
70326
+ }
70327
+ await remindersState.upsThrottle.write(sessionId, { messagesSinceLastStaging: 0 });
70328
+ } else {
70329
+ await remindersState.upsThrottle.write(sessionId, { messagesSinceLastStaging: newCount });
70330
+ }
70331
+ }
70332
+ });
70059
70333
  }
70060
70334
  }
70061
70335
  });
@@ -71635,73 +71909,228 @@ var require_picomatch2 = __commonJS({
71635
71909
  }
71636
71910
  });
71637
71911
 
71638
- // ../feature-reminders/dist/handlers/staging/stage-stop-reminders.js
71639
- var require_stage_stop_reminders = __commonJS({
71640
- "../feature-reminders/dist/handlers/staging/stage-stop-reminders.js"(exports2) {
71912
+ // ../feature-reminders/dist/tool-pattern-matcher.js
71913
+ var require_tool_pattern_matcher = __commonJS({
71914
+ "../feature-reminders/dist/tool-pattern-matcher.js"(exports2) {
71915
+ "use strict";
71916
+ Object.defineProperty(exports2, "__esModule", { value: true });
71917
+ exports2.matchesToolPattern = matchesToolPattern;
71918
+ exports2.findMatchingPattern = findMatchingPattern;
71919
+ var SHELL_OPERATOR_RE = /\s*(?:&&|\|\||[;|])\s*/;
71920
+ function matchesToolPattern(command, pattern) {
71921
+ if (!command || !pattern)
71922
+ return false;
71923
+ const segments = command.split(SHELL_OPERATOR_RE);
71924
+ const patternTokens = pattern.trim().split(/\s+/).filter(Boolean);
71925
+ if (patternTokens.length === 0)
71926
+ return false;
71927
+ return segments.some((segment) => {
71928
+ const cmdTokens = segment.trim().split(/\s+/);
71929
+ if (cmdTokens.length === 0 || cmdTokens[0] === "")
71930
+ return false;
71931
+ if (cmdTokens[0] !== patternTokens[0])
71932
+ return false;
71933
+ let pi = 1;
71934
+ for (let ci = 1; ci < cmdTokens.length && pi < patternTokens.length; ci++) {
71935
+ if (patternTokens[pi] === "*" || patternTokens[pi] === cmdTokens[ci]) {
71936
+ pi++;
71937
+ }
71938
+ }
71939
+ return pi === patternTokens.length;
71940
+ });
71941
+ }
71942
+ function findMatchingPattern(command, patterns) {
71943
+ for (const pattern of patterns) {
71944
+ if (pattern.tool === null)
71945
+ continue;
71946
+ if (matchesToolPattern(command, pattern.tool))
71947
+ return pattern;
71948
+ }
71949
+ return null;
71950
+ }
71951
+ }
71952
+ });
71953
+
71954
+ // ../feature-reminders/dist/handlers/staging/track-verification-tools.js
71955
+ var require_track_verification_tools = __commonJS({
71956
+ "../feature-reminders/dist/handlers/staging/track-verification-tools.js"(exports2) {
71641
71957
  "use strict";
71642
71958
  var __importDefault = exports2 && exports2.__importDefault || function(mod) {
71643
71959
  return mod && mod.__esModule ? mod : { "default": mod };
71644
71960
  };
71645
71961
  Object.defineProperty(exports2, "__esModule", { value: true });
71646
- exports2.registerStageStopReminders = registerStageStopReminders;
71962
+ exports2.registerTrackVerificationTools = registerTrackVerificationTools;
71963
+ exports2.stageToolsForFiles = stageToolsForFiles;
71647
71964
  var types_1 = require_dist();
71648
71965
  var picomatch_1 = __importDefault(require_picomatch2());
71649
- var staging_handler_utils_js_1 = require_staging_handler_utils();
71966
+ var tool_pattern_matcher_js_1 = require_tool_pattern_matcher();
71967
+ var reminder_utils_js_1 = require_reminder_utils();
71650
71968
  var types_js_1 = require_types2();
71969
+ var state_js_1 = require_state3();
71651
71970
  var FILE_EDIT_TOOLS = ["Write", "Edit", "MultiEdit"];
71652
- function registerStageStopReminders(context) {
71653
- (0, staging_handler_utils_js_1.createStagingHandler)(context, {
71654
- id: "reminders:stage-stop-reminders",
71971
+ var TOOL_REMINDER_MAP = {
71972
+ build: types_js_1.ReminderIds.VC_BUILD,
71973
+ typecheck: types_js_1.ReminderIds.VC_TYPECHECK,
71974
+ test: types_js_1.ReminderIds.VC_TEST,
71975
+ lint: types_js_1.ReminderIds.VC_LINT
71976
+ };
71977
+ var VC_TOOL_NAME_SET = new Set(types_js_1.VC_TOOL_REMINDER_IDS);
71978
+ function extractToolInput(event) {
71979
+ const entry = event.payload.entry;
71980
+ return entry?.input;
71981
+ }
71982
+ function registerTrackVerificationTools(context) {
71983
+ if (!(0, types_1.isDaemonContext)(context))
71984
+ return;
71985
+ context.handlers.register({
71986
+ id: "reminders:track-verification-tools",
71655
71987
  priority: 60,
71656
71988
  filter: { kind: "transcript", eventTypes: ["ToolCall"] },
71657
- execute: async (event, ctx) => {
71989
+ handler: async (event, ctx) => {
71658
71990
  if (!(0, types_1.isTranscriptEvent)(event))
71659
- return void 0;
71991
+ return;
71992
+ if (event.metadata.isBulkProcessing)
71993
+ return;
71994
+ if (!(0, types_1.isDaemonContext)(ctx))
71995
+ return;
71996
+ const daemonCtx = ctx;
71997
+ const sessionId = event.context?.sessionId;
71998
+ if (!sessionId)
71999
+ return;
71660
72000
  const toolName = event.payload.toolName;
71661
- if (!toolName || !FILE_EDIT_TOOLS.includes(toolName))
71662
- return void 0;
72001
+ if (!toolName)
72002
+ return;
71663
72003
  const featureConfig = context.config.getFeature("reminders");
71664
72004
  const config = { ...types_js_1.DEFAULT_REMINDERS_SETTINGS, ...featureConfig.settings };
71665
- const entry = event.payload.entry;
71666
- const filePath = entry?.input?.file_path;
71667
- if (!filePath)
71668
- return void 0;
71669
- const isMatch = picomatch_1.default.isMatch(filePath, config.source_code_patterns);
71670
- if (!isMatch) {
71671
- ctx.logger.debug("VC staging: file edit skipped (no pattern match)", {
71672
- toolName,
71673
- filePath
71674
- });
71675
- return void 0;
72005
+ const verificationTools = config.verification_tools ?? {};
72006
+ const remindersState = (0, state_js_1.createRemindersState)(daemonCtx.stateService);
72007
+ const stateResult = await remindersState.verificationTools.read(sessionId);
72008
+ const toolsState = { ...stateResult.data };
72009
+ if (FILE_EDIT_TOOLS.includes(toolName)) {
72010
+ await handleFileEdit(event, daemonCtx, sessionId, verificationTools, toolsState, remindersState);
72011
+ } else if (toolName === "Bash") {
72012
+ await handleBashCommand(event, daemonCtx, sessionId, verificationTools, toolsState, remindersState);
71676
72013
  }
71677
- const metrics = event.metadata.metrics;
71678
- const lastConsumed = await ctx.staging.getLastConsumed("Stop", types_js_1.ReminderIds.VERIFY_COMPLETION);
71679
- if (lastConsumed?.stagedAt) {
71680
- const shouldReactivate = metrics.turnCount > lastConsumed.stagedAt.turnCount;
71681
- if (!shouldReactivate) {
71682
- ctx.logger.debug("VC staging: skipped (already consumed this turn)", {
71683
- currentTurn: metrics.turnCount,
71684
- lastConsumedTurn: lastConsumed.stagedAt.turnCount
71685
- });
71686
- return void 0;
72014
+ }
72015
+ });
72016
+ }
72017
+ async function stageToolsForFiles(filePaths, daemonCtx, sessionId, verificationTools, toolsState, remindersState) {
72018
+ const existingReminders = await daemonCtx.staging.listReminders("Stop");
72019
+ const stagedNames = new Set(existingReminders.map((r) => r.name));
72020
+ let anyStaged = false;
72021
+ for (const filePath of filePaths) {
72022
+ for (const [toolName, toolConfig] of Object.entries(verificationTools)) {
72023
+ if (!toolConfig.enabled)
72024
+ continue;
72025
+ const reminderId = TOOL_REMINDER_MAP[toolName];
72026
+ if (!reminderId)
72027
+ continue;
72028
+ if (!picomatch_1.default.isMatch(filePath, toolConfig.clearing_patterns))
72029
+ continue;
72030
+ const current = toolsState[toolName];
72031
+ if (!current || current.status === "staged") {
72032
+ if (!current) {
72033
+ toolsState[toolName] = {
72034
+ status: "staged",
72035
+ editsSinceVerified: 0,
72036
+ lastVerifiedAt: null,
72037
+ lastStagedAt: Date.now()
72038
+ };
72039
+ }
72040
+ await stageToolReminderIfNeeded(daemonCtx, reminderId, stagedNames);
72041
+ anyStaged = true;
72042
+ } else {
72043
+ const newEdits = current.editsSinceVerified + 1;
72044
+ if (newEdits >= toolConfig.clearing_threshold) {
72045
+ toolsState[toolName] = {
72046
+ ...current,
72047
+ status: "staged",
72048
+ editsSinceVerified: 0,
72049
+ lastStagedAt: Date.now()
72050
+ };
72051
+ await stageToolReminderIfNeeded(daemonCtx, reminderId, stagedNames);
72052
+ anyStaged = true;
72053
+ } else {
72054
+ toolsState[toolName] = {
72055
+ ...current,
72056
+ status: "cooldown",
72057
+ editsSinceVerified: newEdits
72058
+ };
71687
72059
  }
71688
- ctx.logger.info("VC staging: reactivating on new turn", {
71689
- currentTurn: metrics.turnCount,
71690
- lastConsumedTurn: lastConsumed.stagedAt.turnCount
71691
- });
71692
72060
  }
71693
- ctx.logger.info("VC staging: staging verify-completion", {
71694
- toolName,
71695
- filePath,
71696
- turnCount: metrics.turnCount,
71697
- toolCount: metrics.toolCount,
71698
- firstTimeThisSession: !lastConsumed
71699
- });
71700
- return {
71701
- reminderId: types_js_1.ReminderIds.VERIFY_COMPLETION,
71702
- targetHook: "Stop"
71703
- };
71704
72061
  }
72062
+ }
72063
+ if (anyStaged) {
72064
+ await stageToolReminderIfNeeded(daemonCtx, types_js_1.ReminderIds.VERIFY_COMPLETION, stagedNames);
72065
+ }
72066
+ await remindersState.verificationTools.write(sessionId, toolsState);
72067
+ return anyStaged;
72068
+ }
72069
+ async function handleFileEdit(event, daemonCtx, sessionId, verificationTools, toolsState, remindersState) {
72070
+ const filePath = extractToolInput(event)?.file_path;
72071
+ if (!filePath)
72072
+ return;
72073
+ const projectDir2 = daemonCtx.paths?.projectDir;
72074
+ if (projectDir2 && !filePath.startsWith(projectDir2))
72075
+ return;
72076
+ await stageToolsForFiles([filePath], daemonCtx, sessionId, verificationTools, toolsState, remindersState);
72077
+ }
72078
+ async function handleBashCommand(event, daemonCtx, sessionId, verificationTools, toolsState, remindersState) {
72079
+ const command = extractToolInput(event)?.command;
72080
+ if (!command)
72081
+ return;
72082
+ let anyUnstaged = false;
72083
+ for (const [toolName, toolConfig] of Object.entries(verificationTools)) {
72084
+ if (!toolConfig.enabled)
72085
+ continue;
72086
+ const reminderId = TOOL_REMINDER_MAP[toolName];
72087
+ if (!reminderId)
72088
+ continue;
72089
+ const match = (0, tool_pattern_matcher_js_1.findMatchingPattern)(command, toolConfig.patterns);
72090
+ if (!match)
72091
+ continue;
72092
+ toolsState[toolName] = {
72093
+ status: "verified",
72094
+ editsSinceVerified: 0,
72095
+ lastVerifiedAt: Date.now(),
72096
+ lastStagedAt: toolsState[toolName]?.lastStagedAt ?? null,
72097
+ lastMatchedToolId: match.tool_id,
72098
+ lastMatchedScope: match.scope
72099
+ };
72100
+ await daemonCtx.staging.deleteReminder("Stop", reminderId);
72101
+ anyUnstaged = true;
72102
+ daemonCtx.logger.debug("VC tool verified", {
72103
+ toolName,
72104
+ reminderId,
72105
+ matchedToolId: match.tool_id,
72106
+ matchedScope: match.scope,
72107
+ command: command.slice(0, 100)
72108
+ });
72109
+ }
72110
+ if (anyUnstaged) {
72111
+ const remaining = await daemonCtx.staging.listReminders("Stop");
72112
+ const hasPerToolReminders = remaining.some((r) => VC_TOOL_NAME_SET.has(r.name));
72113
+ if (!hasPerToolReminders) {
72114
+ await daemonCtx.staging.deleteReminder("Stop", types_js_1.ReminderIds.VERIFY_COMPLETION);
72115
+ daemonCtx.logger.info("All VC tools verified, unstaged wrapper", { sessionId });
72116
+ }
72117
+ await remindersState.verificationTools.write(sessionId, toolsState);
72118
+ }
72119
+ }
72120
+ async function stageToolReminderIfNeeded(daemonCtx, reminderId, stagedNames) {
72121
+ if (stagedNames.has(reminderId))
72122
+ return;
72123
+ const reminder = (0, reminder_utils_js_1.resolveReminder)(reminderId, {
72124
+ context: {},
72125
+ assets: daemonCtx.assets
72126
+ });
72127
+ if (!reminder) {
72128
+ daemonCtx.logger.warn("Failed to resolve VC tool reminder", { reminderId });
72129
+ return;
72130
+ }
72131
+ await (0, reminder_utils_js_1.stageReminder)(daemonCtx, "Stop", {
72132
+ ...reminder,
72133
+ stagedAt: { timestamp: Date.now(), turnCount: 0, toolsThisTurn: 0, toolCount: 0 }
71705
72134
  });
71706
72135
  }
71707
72136
  }
@@ -71719,8 +72148,9 @@ var require_stage_stop_bash_changes = __commonJS({
71719
72148
  var core_1 = require_dist4();
71720
72149
  var types_1 = require_dist();
71721
72150
  var picomatch_1 = __importDefault(require_picomatch2());
71722
- var staging_handler_utils_js_1 = require_staging_handler_utils();
72151
+ var track_verification_tools_js_1 = require_track_verification_tools();
71723
72152
  var types_js_1 = require_types2();
72153
+ var state_js_1 = require_state3();
71724
72154
  var GIT_STATUS_TIMEOUT_MS = 200;
71725
72155
  var MAX_BASELINES = 50;
71726
72156
  function registerStageBashChanges(context) {
@@ -71757,65 +72187,69 @@ var require_stage_stop_bash_changes = __commonJS({
71757
72187
  });
71758
72188
  }
71759
72189
  });
71760
- (0, staging_handler_utils_js_1.createStagingHandler)(context, {
72190
+ context.handlers.register({
71761
72191
  id: "reminders:stage-stop-bash-changes",
71762
72192
  priority: 55,
71763
72193
  filter: { kind: "transcript", eventTypes: ["ToolResult"] },
71764
- execute: async (event, ctx) => {
72194
+ handler: async (event, ctx) => {
71765
72195
  if (!(0, types_1.isTranscriptEvent)(event))
71766
- return void 0;
72196
+ return;
72197
+ if (event.metadata.isBulkProcessing)
72198
+ return;
72199
+ if (!(0, types_1.isDaemonContext)(ctx))
72200
+ return;
72201
+ const daemonCtx = ctx;
71767
72202
  const toolName = event.payload.toolName;
71768
72203
  if (toolName !== "Bash")
71769
- return void 0;
72204
+ return;
71770
72205
  const sessionId = event.context?.sessionId;
71771
72206
  if (!sessionId)
71772
- return void 0;
72207
+ return;
71773
72208
  const baseline = baselines.get(sessionId);
71774
72209
  if (!baseline) {
71775
- ctx.logger.debug("Bash VC: no baseline for session, skipping", { sessionId });
71776
- return void 0;
72210
+ daemonCtx.logger.debug("Bash VC: no baseline for session, skipping", { sessionId });
72211
+ return;
71777
72212
  }
71778
72213
  const metrics = event.metadata.metrics;
71779
- const lastConsumed = await ctx.staging.getLastConsumed("Stop", types_js_1.ReminderIds.VERIFY_COMPLETION);
72214
+ const lastConsumed = await daemonCtx.staging.getLastConsumed("Stop", types_js_1.ReminderIds.VERIFY_COMPLETION);
71780
72215
  if (lastConsumed?.stagedAt) {
71781
72216
  const shouldReactivate = metrics.turnCount > lastConsumed.stagedAt.turnCount;
71782
72217
  if (!shouldReactivate) {
71783
- ctx.logger.debug("Bash VC: skipped (already consumed this turn)", {
72218
+ daemonCtx.logger.debug("Bash VC: skipped (already consumed this turn)", {
71784
72219
  currentTurn: metrics.turnCount,
71785
72220
  lastConsumedTurn: lastConsumed.stagedAt.turnCount
71786
72221
  });
71787
- return void 0;
72222
+ return;
71788
72223
  }
71789
72224
  }
71790
72225
  const current = await (0, core_1.getGitFileStatus)(cwd, GIT_STATUS_TIMEOUT_MS);
71791
72226
  const baselineSet = new Set(baseline);
71792
72227
  const newFiles = current.filter((f) => !baselineSet.has(f));
71793
- ctx.logger.debug("Bash VC: git status diff", {
72228
+ daemonCtx.logger.debug("Bash VC: git status diff", {
71794
72229
  baselineCount: baseline.length,
71795
72230
  currentCount: current.length,
71796
72231
  newFileCount: newFiles.length
71797
72232
  });
71798
72233
  if (newFiles.length === 0)
71799
- return void 0;
72234
+ return;
71800
72235
  const featureConfig = context.config.getFeature("reminders");
71801
72236
  const config = { ...types_js_1.DEFAULT_REMINDERS_SETTINGS, ...featureConfig.settings };
71802
72237
  const sourceMatches = newFiles.filter((f) => picomatch_1.default.isMatch(f, config.source_code_patterns));
71803
72238
  if (sourceMatches.length === 0) {
71804
- ctx.logger.debug("Bash VC: new files found but no source code matches", {
71805
- newFiles
71806
- });
71807
- return void 0;
72239
+ daemonCtx.logger.debug("Bash VC: new files found but no source code matches", { newFiles });
72240
+ return;
71808
72241
  }
71809
- ctx.logger.info("Bash VC: staging verify-completion", {
72242
+ daemonCtx.logger.info("Bash VC: staging per-tool reminders for source changes", {
71810
72243
  sourceMatches,
71811
72244
  turnCount: metrics.turnCount,
71812
72245
  toolCount: metrics.toolCount
71813
72246
  });
71814
72247
  baselines.set(sessionId, current);
71815
- return {
71816
- reminderId: types_js_1.ReminderIds.VERIFY_COMPLETION,
71817
- targetHook: "Stop"
71818
- };
72248
+ const verificationTools = config.verification_tools ?? {};
72249
+ const remindersState = (0, state_js_1.createRemindersState)(daemonCtx.stateService);
72250
+ const stateResult = await remindersState.verificationTools.read(sessionId);
72251
+ const toolsState = { ...stateResult.data };
72252
+ await (0, track_verification_tools_js_1.stageToolsForFiles)(sourceMatches, daemonCtx, sessionId, verificationTools, toolsState, remindersState);
71819
72253
  }
71820
72254
  });
71821
72255
  }
@@ -71869,28 +72303,52 @@ var require_unstage_verify_completion = __commonJS({
71869
72303
  setAtToolCount: unverifiedState.setAt.toolCount
71870
72304
  });
71871
72305
  if (maxCycles < 0 || unverifiedState.cycleCount < maxCycles) {
71872
- const reminder = (0, reminder_utils_js_1.resolveReminder)(types_js_1.ReminderIds.VERIFY_COMPLETION, {
71873
- context: {},
71874
- assets: daemonCtx.assets
72306
+ const verificationToolsResult = await remindersState.verificationTools.read(sessionId);
72307
+ const toolsState = verificationToolsResult.data;
72308
+ const verificationTools = config.verification_tools ?? {};
72309
+ const hasToolsNeedingVerification = Object.entries(verificationTools).some(([toolName, toolConfig]) => {
72310
+ if (!toolConfig.enabled)
72311
+ return false;
72312
+ const state = toolsState[toolName];
72313
+ if (!state)
72314
+ return true;
72315
+ if (state.status === "staged")
72316
+ return true;
72317
+ if (state.status === "verified" || state.status === "cooldown") {
72318
+ return state.editsSinceVerified >= toolConfig.clearing_threshold;
72319
+ }
72320
+ return false;
71875
72321
  });
71876
- if (reminder) {
71877
- await (0, reminder_utils_js_1.stageReminder)(daemonCtx, "Stop", {
71878
- ...reminder,
71879
- stagedAt: {
71880
- timestamp: Date.now(),
71881
- turnCount: unverifiedState.setAt.turnCount,
71882
- toolsThisTurn: unverifiedState.setAt.toolsThisTurn,
71883
- toolCount: unverifiedState.setAt.toolCount
71884
- }
71885
- });
71886
- daemonCtx.logger.info("VC unstage: re-staged for next Stop", {
72322
+ if (!hasToolsNeedingVerification) {
72323
+ daemonCtx.logger.info("VC unstage: all tools verified, skipping wrapper re-stage", {
71887
72324
  sessionId,
71888
- cycleCount: unverifiedState.cycleCount,
71889
- lastCategory: unverifiedState.lastClassification.category
72325
+ cycleCount: unverifiedState.cycleCount
71890
72326
  });
71891
- return;
72327
+ await remindersState.vcUnverified.delete(sessionId);
71892
72328
  } else {
71893
- daemonCtx.logger.warn("VC unstage: failed to resolve reminder for re-staging");
72329
+ const reminder = (0, reminder_utils_js_1.resolveReminder)(types_js_1.ReminderIds.VERIFY_COMPLETION, {
72330
+ context: {},
72331
+ assets: daemonCtx.assets
72332
+ });
72333
+ if (reminder) {
72334
+ await (0, reminder_utils_js_1.stageReminder)(daemonCtx, "Stop", {
72335
+ ...reminder,
72336
+ stagedAt: {
72337
+ timestamp: Date.now(),
72338
+ turnCount: unverifiedState.setAt.turnCount,
72339
+ toolsThisTurn: unverifiedState.setAt.toolsThisTurn,
72340
+ toolCount: unverifiedState.setAt.toolCount
72341
+ }
72342
+ });
72343
+ daemonCtx.logger.info("VC unstage: re-staged for next Stop", {
72344
+ sessionId,
72345
+ cycleCount: unverifiedState.cycleCount,
72346
+ lastCategory: unverifiedState.lastClassification.category
72347
+ });
72348
+ return;
72349
+ } else {
72350
+ daemonCtx.logger.warn("VC unstage: failed to resolve reminder for re-staging");
72351
+ }
71894
72352
  }
71895
72353
  } else {
71896
72354
  daemonCtx.logger.info("VC unstage: cycle limit reached, clearing", {
@@ -71906,8 +72364,10 @@ var require_unstage_verify_completion = __commonJS({
71906
72364
  hadState: unverifiedState !== null
71907
72365
  });
71908
72366
  }
71909
- await daemonCtx.staging.deleteReminder("Stop", types_js_1.ReminderIds.VERIFY_COMPLETION);
71910
- daemonCtx.logger.debug("VC unstage: deleted staged verify-completion reminder");
72367
+ for (const vcId of types_js_1.ALL_VC_REMINDER_IDS) {
72368
+ await daemonCtx.staging.deleteReminder("Stop", vcId);
72369
+ }
72370
+ daemonCtx.logger.debug("VC unstage: deleted all VC reminders");
71911
72371
  }
71912
72372
  });
71913
72373
  }
@@ -72138,15 +72598,15 @@ var require_staging2 = __commonJS({
72138
72598
  exports2.registerStagingHandlers = registerStagingHandlers;
72139
72599
  var stage_default_user_prompt_1 = require_stage_default_user_prompt();
72140
72600
  var stage_pause_and_reflect_1 = require_stage_pause_and_reflect();
72141
- var stage_stop_reminders_1 = require_stage_stop_reminders();
72142
72601
  var stage_stop_bash_changes_1 = require_stage_stop_bash_changes();
72602
+ var track_verification_tools_1 = require_track_verification_tools();
72143
72603
  var unstage_verify_completion_1 = require_unstage_verify_completion();
72144
72604
  var stage_persona_reminders_1 = require_stage_persona_reminders();
72145
72605
  var stage_user_profile_reminders_1 = require_stage_user_profile_reminders();
72146
72606
  function registerStagingHandlers(context) {
72147
72607
  (0, stage_default_user_prompt_1.registerStageDefaultUserPrompt)(context);
72148
72608
  (0, stage_pause_and_reflect_1.registerStagePauseAndReflect)(context);
72149
- (0, stage_stop_reminders_1.registerStageStopReminders)(context);
72609
+ (0, track_verification_tools_1.registerTrackVerificationTools)(context);
72150
72610
  (0, stage_stop_bash_changes_1.registerStageBashChanges)(context);
72151
72611
  (0, unstage_verify_completion_1.registerUnstageVerifyCompletion)(context);
72152
72612
  (0, stage_persona_reminders_1.registerStagePersonaReminders)(context);
@@ -72858,10 +73318,12 @@ var require_orchestrator = __commonJS({
72858
73318
  if (reminder.name === types_js_1.ReminderIds.PAUSE_AND_REFLECT) {
72859
73319
  try {
72860
73320
  const staging = this.deps.getStagingService(sessionId);
72861
- await staging.deleteReminder("Stop", types_js_1.ReminderIds.VERIFY_COMPLETION);
72862
- this.deps.logger.debug("Unstaged VC after P&R staged", { sessionId });
73321
+ for (const vcId of types_js_1.ALL_VC_REMINDER_IDS) {
73322
+ await staging.deleteReminder("Stop", vcId);
73323
+ }
73324
+ this.deps.logger.debug("Unstaged all VC reminders after P&R staged", { sessionId });
72863
73325
  } catch (err) {
72864
- this.deps.logger.warn("Failed to unstage VC after P&R staged", {
73326
+ this.deps.logger.warn("Failed to unstage VC reminders after P&R staged", {
72865
73327
  sessionId,
72866
73328
  error: err instanceof Error ? err.message : String(err)
72867
73329
  });
@@ -73112,7 +73574,8 @@ var require_types3 = __commonJS({
73112
73574
  injectPersonaIntoClaude: true,
73113
73575
  defaultLlmProfile: "",
73114
73576
  llmProfiles: {},
73115
- weights: {}
73577
+ weights: {},
73578
+ persistThroughClear: true
73116
73579
  }
73117
73580
  };
73118
73581
  exports2.RESUME_MIN_CONFIDENCE = 0.7;
@@ -73187,7 +73650,7 @@ var require_persona_selection = __commonJS({
73187
73650
  }
73188
73651
  return weighted[weighted.length - 1].persona;
73189
73652
  }
73190
- async function selectPersonaForSession(sessionId, config, ctx) {
73653
+ async function selectPersonaForSession(sessionId, config, ctx, options) {
73191
73654
  const personaConfig = {
73192
73655
  ...types_js_1.DEFAULT_SESSION_SUMMARY_CONFIG.personas,
73193
73656
  ...config.personas
@@ -73226,6 +73689,38 @@ var require_persona_selection = __commonJS({
73226
73689
  availablePersonas: Array.from(allPersonas.keys())
73227
73690
  });
73228
73691
  }
73692
+ const persistThroughClear = personaConfig.persistThroughClear ?? true;
73693
+ ctx.logger.debug("Persona clear handoff check", {
73694
+ persistThroughClear,
73695
+ startType: options?.startType,
73696
+ hasClearCache: !!ctx.personaClearCache
73697
+ });
73698
+ if (persistThroughClear && options?.startType === "clear" && ctx.personaClearCache) {
73699
+ const cachedPersonaId = ctx.personaClearCache.consume();
73700
+ if (cachedPersonaId) {
73701
+ const cachedPersona = allPersonas.get(cachedPersonaId);
73702
+ if (cachedPersona) {
73703
+ const personaState2 = {
73704
+ persona_id: cachedPersona.id,
73705
+ selected_from: [cachedPersona.id],
73706
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
73707
+ };
73708
+ const summaryState2 = (0, state_js_1.createSessionSummaryState)(ctx.stateService);
73709
+ await summaryState2.sessionPersona.write(sessionId, personaState2);
73710
+ ctx.logger.info("Preserved persona through clear", {
73711
+ sessionId,
73712
+ personaId: cachedPersona.id,
73713
+ personaName: cachedPersona.display_name
73714
+ });
73715
+ return cachedPersona.id;
73716
+ } else {
73717
+ ctx.logger.warn("Cached persona from clear not found in available personas, falling back to selection", {
73718
+ sessionId,
73719
+ cachedPersonaId
73720
+ });
73721
+ }
73722
+ }
73723
+ }
73229
73724
  const allowList = parsePersonaList(personaConfig.allowList ?? "");
73230
73725
  const blockList = parsePersonaList(personaConfig.blockList ?? "");
73231
73726
  const eligiblePersonas = filterPersonas(allPersonas, allowList, blockList, ctx.logger);
@@ -73312,7 +73807,7 @@ var require_create_first_summary = __commonJS({
73312
73807
  const summaryState = (0, state_js_1.createSessionSummaryState)(ctx.stateService);
73313
73808
  await summaryState.sessionSummary.write(sessionId, placeholder);
73314
73809
  ctx.logger.info("Created placeholder session summary", { sessionId });
73315
- await (0, persona_selection_js_1.selectPersonaForSession)(sessionId, config, ctx);
73810
+ await (0, persona_selection_js_1.selectPersonaForSession)(sessionId, config, ctx, { startType });
73316
73811
  }
73317
73812
  }
73318
73813
  });
@@ -74172,10 +74667,10 @@ var require_handlers = __commonJS({
74172
74667
  id: "session-summary:init",
74173
74668
  priority: 80,
74174
74669
  filter: { kind: "hook", hooks: ["SessionStart"] },
74175
- handler: async (event) => {
74670
+ handler: async (event, context2) => {
74176
74671
  if (!(0, core_1.isHookEvent)(event) || !(0, core_1.isSessionStartEvent)(event))
74177
74672
  return;
74178
- await (0, create_first_summary_js_1.createFirstSessionSummary)(event, ctx);
74673
+ await (0, create_first_summary_js_1.createFirstSessionSummary)(event, context2);
74179
74674
  }
74180
74675
  });
74181
74676
  ctx.handlers.register({
@@ -74951,6 +75446,7 @@ var require_context_metrics_service = __commonJS({
74951
75446
  if (!(0, transcript_parser_js_1.isContextCommandOutput)(wrappedOutput)) {
74952
75447
  const errorMessage = "CLI stdout does not appear to be /context output";
74953
75448
  this.logger.warn(errorMessage, {
75449
+ sessionId,
74954
75450
  stdoutLength: stdout.length,
74955
75451
  stdoutPreview: stdout.slice(0, 500)
74956
75452
  });
@@ -74961,6 +75457,7 @@ var require_context_metrics_service = __commonJS({
74961
75457
  if (!parsed) {
74962
75458
  const errorMessage = "Failed to parse /context table from CLI stdout";
74963
75459
  this.logger.warn(errorMessage, {
75460
+ sessionId,
74964
75461
  stdoutLength: stdout.length,
74965
75462
  stdoutPreview: stdout.slice(0, 500)
74966
75463
  });
@@ -75777,6 +76274,8 @@ var require_daemon = __commonJS({
75777
76274
  orchestrator;
75778
76275
  /** Per-session log counters for statusline {logs} indicator */
75779
76276
  logCounters = /* @__PURE__ */ new Map();
76277
+ /** Transient cache for persona handoff across /clear boundaries */
76278
+ lastClearedPersona = null;
75780
76279
  /** Global log counters for daemon-level errors (not tied to any session) */
75781
76280
  globalLogCounters = { warnings: 0, errors: 0 };
75782
76281
  /** Typed accessor for daemon status state */
@@ -75789,6 +76288,29 @@ var require_daemon = __commonJS({
75789
76288
  heartbeatInterval = null;
75790
76289
  evictionTimer = null;
75791
76290
  startTime = Date.now();
76291
+ /** Cache persona for handoff on clear. */
76292
+ cachePersonaForClear(personaId) {
76293
+ this.lastClearedPersona = { personaId, timestamp: Date.now() };
76294
+ this.logger.debug("Cached persona for clear handoff", { personaId });
76295
+ }
76296
+ /** Consume cached persona if fresh (< 5s). Returns null if stale/absent. */
76297
+ consumeCachedPersona() {
76298
+ if (!this.lastClearedPersona) {
76299
+ this.logger.debug("consumeCachedPersona: no cached persona available");
76300
+ return null;
76301
+ }
76302
+ const HANDOFF_TTL_MS = 5e3;
76303
+ const age = Date.now() - this.lastClearedPersona.timestamp;
76304
+ if (age > HANDOFF_TTL_MS) {
76305
+ this.logger.debug("Stale persona handoff ignored", { age, personaId: this.lastClearedPersona.personaId });
76306
+ this.lastClearedPersona = null;
76307
+ return null;
76308
+ }
76309
+ const personaId = this.lastClearedPersona.personaId;
76310
+ this.lastClearedPersona = null;
76311
+ this.logger.debug("Consumed persona from clear handoff", { personaId, age });
76312
+ return personaId;
76313
+ }
75792
76314
  constructor(projectDir2) {
75793
76315
  this.projectDir = projectDir2;
75794
76316
  this.assetResolver = (0, core_1.createAssetResolver)({
@@ -76172,7 +76694,7 @@ var require_daemon = __commonJS({
76172
76694
  const requestLogger = this.logger.child({
76173
76695
  context: { sessionId, correlationId }
76174
76696
  });
76175
- requestLogger.debug("Handling hook invocation", { hook });
76697
+ requestLogger.debug("Hook invocation received", { hook, sessionId });
76176
76698
  if (sessionId) {
76177
76699
  (0, core_1.logEvent)(requestLogger, core_1.LogEvents.eventReceived({ sessionId, correlationId, hook }, { eventKind: "hook", hook }));
76178
76700
  }
@@ -76249,6 +76771,24 @@ var require_daemon = __commonJS({
76249
76771
  const log = options?.logger ?? this.logger;
76250
76772
  const sessionId = event.context?.sessionId;
76251
76773
  if (sessionId) {
76774
+ const payload = event.payload;
76775
+ log.debug("handleSessionEnd entered", { sessionId, endReason: payload.endReason });
76776
+ if (payload.endReason === "clear") {
76777
+ try {
76778
+ const summaryState = (0, feature_session_summary_1.createSessionSummaryState)(this.stateService);
76779
+ const result = await summaryState.sessionPersona.read(sessionId);
76780
+ if (result.data?.persona_id) {
76781
+ log.debug("Caching persona for clear handoff", { personaId: result.data.persona_id });
76782
+ this.cachePersonaForClear(result.data.persona_id);
76783
+ }
76784
+ } catch (err) {
76785
+ log.warn("Failed to cache persona for clear handoff", {
76786
+ error: err instanceof Error ? err.message : String(err)
76787
+ });
76788
+ }
76789
+ } else {
76790
+ log.debug("SessionEnd reason is not clear, skipping persona cache", { endReason: payload.endReason });
76791
+ }
76252
76792
  const instrumentedProvider = this.instrumentedProviders.get(sessionId);
76253
76793
  if (instrumentedProvider) {
76254
76794
  await instrumentedProvider.shutdown();
@@ -76686,7 +77226,10 @@ var require_daemon = __commonJS({
76686
77226
  staging: stagingService,
76687
77227
  transcript: transcriptService,
76688
77228
  stateService: this.stateService,
76689
- orchestrator: this.orchestrator
77229
+ orchestrator: this.orchestrator,
77230
+ personaClearCache: {
77231
+ consume: () => this.consumeCachedPersona()
77232
+ }
76690
77233
  };
76691
77234
  this.handlerRegistry.updateSession({ sessionId, transcriptPath });
76692
77235
  this.handlerRegistry.setStagingProvider(() => stagingService);