@scotthamilton77/sidekick 0.1.9 → 0.1.11

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 (3) hide show
  1. package/dist/bin.js +327 -132
  2. package/dist/daemon.js +336 -131
  3. package/package.json +1 -1
package/dist/daemon.js CHANGED
@@ -122,6 +122,7 @@ var require_events = __commonJS({
122
122
  "reminder:unstaged",
123
123
  "reminder:consumed",
124
124
  "reminder:cleared",
125
+ "reminder:not-staged",
125
126
  // Decision events
126
127
  "decision:recorded",
127
128
  // Session summary events
@@ -170,6 +171,7 @@ var require_events = __commonJS({
170
171
  "reminder:unstaged": "timeline",
171
172
  "reminder:consumed": "timeline",
172
173
  "reminder:cleared": "timeline",
174
+ "reminder:not-staged": "log",
173
175
  "decision:recorded": "timeline",
174
176
  "session-summary:start": "timeline",
175
177
  "session-summary:finish": "timeline",
@@ -44889,7 +44891,10 @@ var require_structured_logging = __commonJS({
44889
44891
  hookName: state.hookName,
44890
44892
  blocking: state.blocking,
44891
44893
  priority: state.priority,
44892
- persistent: state.persistent
44894
+ persistent: state.persistent,
44895
+ ...state.reason !== void 0 && { reason: state.reason },
44896
+ ...state.triggeredBy !== void 0 && { triggeredBy: state.triggeredBy },
44897
+ ...state.thresholdState !== void 0 && { thresholdState: state.thresholdState }
44893
44898
  }
44894
44899
  };
44895
44900
  },
@@ -45186,8 +45191,8 @@ var require_structured_logging = __commonJS({
45186
45191
  },
45187
45192
  // --- Error Events ---
45188
45193
  /**
45189
- * Create an ErrorOccurred event (logged when error/fatal level log is emitted).
45190
- * Emitted automatically by HookableLogger hook no manual call-site changes needed.
45194
+ * Create a daemon ErrorOccurred event.
45195
+ * @see packages/sidekick-daemon/src/daemon.ts HookableLogger error hook calls this factory.
45191
45196
  */
45192
45197
  daemonErrorOccurred(context, state) {
45193
45198
  return {
@@ -45206,6 +45211,28 @@ var require_structured_logging = __commonJS({
45206
45211
  errorStack: state.errorStack
45207
45212
  }
45208
45213
  };
45214
+ },
45215
+ /**
45216
+ * Create a CLI ErrorOccurred event.
45217
+ * Available for CLI error hook implementations.
45218
+ */
45219
+ cliErrorOccurred(context, state) {
45220
+ return {
45221
+ type: "error:occurred",
45222
+ time: Date.now(),
45223
+ source: "cli",
45224
+ context: {
45225
+ sessionId: context.sessionId,
45226
+ correlationId: context.correlationId,
45227
+ traceId: context.traceId,
45228
+ hook: context.hook,
45229
+ taskId: context.taskId
45230
+ },
45231
+ payload: {
45232
+ errorMessage: state.errorMessage,
45233
+ errorStack: state.errorStack
45234
+ }
45235
+ };
45209
45236
  }
45210
45237
  };
45211
45238
  function logEvent(logger, event) {
@@ -57161,24 +57188,20 @@ var require_staging_paths = __commonJS({
57161
57188
  function getReminderPath(stateDir, sessionId, hookName, reminderName) {
57162
57189
  return (0, node_path_1.join)(getHookDir(stateDir, sessionId, hookName), `${reminderName}.json`);
57163
57190
  }
57164
- function isValidPathSegment(segment) {
57165
- if (!segment)
57191
+ function isValidPathSegment(s) {
57192
+ if (s === "")
57166
57193
  return false;
57167
- if (segment.includes("..") || segment.includes("/") || segment.includes("\\"))
57194
+ if (s === "." || s === "..")
57168
57195
  return false;
57169
- if (segment.startsWith("."))
57196
+ if (s.includes("/") || s.includes("\\"))
57170
57197
  return false;
57171
- return true;
57198
+ if ((0, node_path_1.basename)(s) !== s)
57199
+ return false;
57200
+ return /^[a-zA-Z0-9._-]+$/.test(s);
57172
57201
  }
57173
57202
  function validatePathSegment(segment, name) {
57174
- if (!segment) {
57175
- throw new Error(`${name} cannot be empty`);
57176
- }
57177
- if (segment.includes("..") || segment.includes("/") || segment.includes("\\")) {
57178
- throw new Error(`Invalid ${name}: path traversal characters not allowed`);
57179
- }
57180
- if (segment.startsWith(".")) {
57181
- throw new Error(`Invalid ${name}: cannot start with '.'`);
57203
+ if (!isValidPathSegment(segment)) {
57204
+ throw new Error(`Invalid ${name}: must be a non-empty alphanumeric string without path separators`);
57182
57205
  }
57183
57206
  }
57184
57207
  exports2.CONSUMED_FILE_PATTERN = /\.\d+\.json$/;
@@ -57252,7 +57275,7 @@ var require_staging_service = __commonJS({
57252
57275
  *
57253
57276
  * @throws Error if hookName or reminderName contain path traversal characters
57254
57277
  */
57255
- async stageReminder(sessionId, hookName, reminderName, data) {
57278
+ async stageReminder(sessionId, hookName, reminderName, data, enrichment) {
57256
57279
  (0, staging_paths_js_1.validatePathSegment)(hookName, "hookName");
57257
57280
  (0, staging_paths_js_1.validatePathSegment)(reminderName, "reminderName");
57258
57281
  const hookDir = this.getHookDirPath(sessionId, hookName);
@@ -57281,9 +57304,10 @@ var require_staging_service = __commonJS({
57281
57304
  hookName,
57282
57305
  blocking: data.blocking,
57283
57306
  priority: data.priority,
57284
- persistent: data.persistent
57307
+ persistent: data.persistent,
57308
+ ...enrichment
57285
57309
  }, { stagingPath: reminderPath });
57286
- (0, structured_logging_1.logEvent)(this.options.logger, event);
57310
+ (0, structured_logging_1.logEvent)(this.options.logger.child({ context: { sessionId } }), event);
57287
57311
  }
57288
57312
  /**
57289
57313
  * Read a staged reminder.
@@ -57425,8 +57449,8 @@ var require_staging_service = __commonJS({
57425
57449
  // ============================================================================
57426
57450
  // StagingService Interface Implementation (delegates to core)
57427
57451
  // ============================================================================
57428
- async stageReminder(hookName, reminderName, data) {
57429
- return this.core.stageReminder(this.sessionId, hookName, reminderName, data);
57452
+ async stageReminder(hookName, reminderName, data, enrichment) {
57453
+ return this.core.stageReminder(this.sessionId, hookName, reminderName, data, enrichment);
57430
57454
  }
57431
57455
  async readReminder(hookName, reminderName) {
57432
57456
  return this.core.readReminder(this.sessionId, hookName, reminderName);
@@ -70197,8 +70221,8 @@ var require_reminder_utils = __commonJS({
70197
70221
  return null;
70198
70222
  }
70199
70223
  }
70200
- async function stageReminder(ctx, hookName, reminder) {
70201
- await ctx.staging.stageReminder(hookName, reminder.name, reminder);
70224
+ async function stageReminder(ctx, hookName, reminder, enrichment) {
70225
+ await ctx.staging.stageReminder(hookName, reminder.name, reminder, enrichment);
70202
70226
  ctx.logger.debug("Staged reminder", { hookName, reminderName: reminder.name, priority: reminder.priority });
70203
70227
  }
70204
70228
  async function consumeReminder(ctx, hookName) {
@@ -70744,13 +70768,125 @@ var require_stage_default_user_prompt = __commonJS({
70744
70768
  }
70745
70769
  });
70746
70770
 
70771
+ // ../feature-reminders/dist/events.js
70772
+ var require_events2 = __commonJS({
70773
+ "../feature-reminders/dist/events.js"(exports2) {
70774
+ "use strict";
70775
+ Object.defineProperty(exports2, "__esModule", { value: true });
70776
+ exports2.ReminderEvents = void 0;
70777
+ exports2.ReminderEvents = {
70778
+ /**
70779
+ * Create a ReminderConsumed event (logged when CLI returns a staged reminder).
70780
+ */
70781
+ reminderConsumed(context, state, _metadata) {
70782
+ return {
70783
+ type: "reminder:consumed",
70784
+ time: Date.now(),
70785
+ source: "cli",
70786
+ context: {
70787
+ sessionId: context.sessionId,
70788
+ correlationId: context.correlationId,
70789
+ traceId: context.traceId,
70790
+ hook: context.hook,
70791
+ taskId: context.taskId
70792
+ },
70793
+ payload: {
70794
+ reminderName: state.reminderName,
70795
+ reminderReturned: state.reminderReturned,
70796
+ blocking: state.blocking,
70797
+ priority: state.priority,
70798
+ persistent: state.persistent,
70799
+ ...state.classificationResult !== void 0 && {
70800
+ classificationResult: state.classificationResult
70801
+ }
70802
+ }
70803
+ };
70804
+ },
70805
+ // Note: reminderStaged stays in @sidekick/core (used by staging-service.ts, circular dep)
70806
+ /**
70807
+ * Create a ReminderUnstaged event (logged when a reminder is removed from staging).
70808
+ */
70809
+ reminderUnstaged(context, state) {
70810
+ return {
70811
+ type: "reminder:unstaged",
70812
+ time: Date.now(),
70813
+ source: "daemon",
70814
+ context: {
70815
+ sessionId: context.sessionId,
70816
+ correlationId: context.correlationId,
70817
+ traceId: context.traceId,
70818
+ hook: context.hook,
70819
+ taskId: context.taskId
70820
+ },
70821
+ payload: {
70822
+ reminderName: state.reminderName,
70823
+ hookName: state.hookName,
70824
+ reason: state.reason,
70825
+ ...state.triggeredBy !== void 0 && { triggeredBy: state.triggeredBy },
70826
+ ...state.toolState !== void 0 && { toolState: state.toolState }
70827
+ }
70828
+ };
70829
+ },
70830
+ /**
70831
+ * Create a RemindersCleared event (logged when staging directory is cleaned).
70832
+ */
70833
+ remindersCleared(context, state, reason) {
70834
+ return {
70835
+ type: "reminder:cleared",
70836
+ time: Date.now(),
70837
+ source: "daemon",
70838
+ context: {
70839
+ sessionId: context.sessionId,
70840
+ correlationId: context.correlationId,
70841
+ traceId: context.traceId,
70842
+ hook: context.hook,
70843
+ taskId: context.taskId
70844
+ },
70845
+ payload: {
70846
+ clearedCount: state.clearedCount,
70847
+ hookNames: state.hookNames,
70848
+ reason
70849
+ }
70850
+ };
70851
+ },
70852
+ /**
70853
+ * Create a ReminderNotStaged event (logged when daemon evaluates but decides not to stage).
70854
+ */
70855
+ reminderNotStaged(context, state) {
70856
+ return {
70857
+ type: "reminder:not-staged",
70858
+ time: Date.now(),
70859
+ source: "daemon",
70860
+ context: {
70861
+ sessionId: context.sessionId,
70862
+ correlationId: context.correlationId,
70863
+ traceId: context.traceId,
70864
+ hook: context.hook,
70865
+ taskId: context.taskId
70866
+ },
70867
+ payload: {
70868
+ reminderName: state.reminderName,
70869
+ hookName: state.hookName,
70870
+ reason: state.reason,
70871
+ ...state.threshold !== void 0 && { threshold: state.threshold },
70872
+ ...state.currentValue !== void 0 && { currentValue: state.currentValue },
70873
+ ...state.triggeredBy !== void 0 && { triggeredBy: state.triggeredBy }
70874
+ }
70875
+ };
70876
+ }
70877
+ };
70878
+ }
70879
+ });
70880
+
70747
70881
  // ../feature-reminders/dist/handlers/staging/stage-pause-and-reflect.js
70748
70882
  var require_stage_pause_and_reflect = __commonJS({
70749
70883
  "../feature-reminders/dist/handlers/staging/stage-pause-and-reflect.js"(exports2) {
70750
70884
  "use strict";
70751
70885
  Object.defineProperty(exports2, "__esModule", { value: true });
70752
70886
  exports2.registerStagePauseAndReflect = registerStagePauseAndReflect;
70887
+ var core_1 = require_dist4();
70753
70888
  var types_1 = require_dist();
70889
+ var events_js_1 = require_events2();
70754
70890
  var staging_handler_utils_js_1 = require_staging_handler_utils();
70755
70891
  var types_js_1 = require_types2();
70756
70892
  var state_js_1 = require_state4();
@@ -70764,15 +70900,17 @@ var require_stage_pause_and_reflect = __commonJS({
70764
70900
  return void 0;
70765
70901
  const metrics = event.metadata.metrics;
70766
70902
  const sessionId = event.context?.sessionId;
70903
+ if (!sessionId) {
70904
+ ctx.logger.warn("[stage-pause-and-reflect] No sessionId available, skipping");
70905
+ return void 0;
70906
+ }
70767
70907
  const featureConfig = context.config.getFeature("reminders");
70768
70908
  const config = { ...types_js_1.DEFAULT_REMINDERS_SETTINGS, ...featureConfig.settings };
70769
70909
  let prBaseline = null;
70770
- if (sessionId) {
70771
- const remindersState = (0, state_js_1.createRemindersState)(ctx.stateService);
70772
- const result = await remindersState.prBaseline.read(sessionId);
70773
- if (result.source !== "default") {
70774
- prBaseline = result.data;
70775
- }
70910
+ const remindersState = (0, state_js_1.createRemindersState)(ctx.stateService);
70911
+ const result = await remindersState.prBaseline.read(sessionId);
70912
+ if (result.source !== "default") {
70913
+ prBaseline = result.data;
70776
70914
  }
70777
70915
  let effectiveBaseline = 0;
70778
70916
  if (prBaseline && prBaseline.turnCount === metrics.turnCount) {
@@ -70784,12 +70922,28 @@ var require_stage_pause_and_reflect = __commonJS({
70784
70922
  effectiveBaseline = Math.max(effectiveBaseline, lastConsumed.stagedAt.toolsThisTurn);
70785
70923
  }
70786
70924
  const shouldReactivate = metrics.turnCount > lastConsumed.stagedAt.turnCount || metrics.toolsThisTurn >= effectiveBaseline + config.pause_and_reflect_threshold;
70787
- if (!shouldReactivate)
70925
+ if (!shouldReactivate) {
70926
+ (0, core_1.logEvent)(ctx.logger, events_js_1.ReminderEvents.reminderNotStaged({ sessionId }, {
70927
+ reminderName: "pause-and-reflect",
70928
+ hookName: "PreToolUse",
70929
+ reason: "same_turn",
70930
+ triggeredBy: "tool_result"
70931
+ }));
70788
70932
  return void 0;
70933
+ }
70789
70934
  }
70790
70935
  const toolsSinceBaseline = metrics.toolsThisTurn - effectiveBaseline;
70791
- if (toolsSinceBaseline < config.pause_and_reflect_threshold)
70936
+ if (toolsSinceBaseline < config.pause_and_reflect_threshold) {
70937
+ (0, core_1.logEvent)(ctx.logger, events_js_1.ReminderEvents.reminderNotStaged({ sessionId }, {
70938
+ reminderName: "pause-and-reflect",
70939
+ hookName: "PreToolUse",
70940
+ reason: "below_threshold",
70941
+ threshold: config.pause_and_reflect_threshold,
70942
+ currentValue: toolsSinceBaseline,
70943
+ triggeredBy: "tool_result"
70944
+ }));
70792
70945
  return void 0;
70946
+ }
70793
70947
  return {
70794
70948
  reminderId: types_js_1.ReminderIds.PAUSE_AND_REFLECT,
70795
70949
  targetHook: "PreToolUse",
@@ -72319,86 +72473,6 @@ var require_picomatch2 = __commonJS({
72319
72473
  }
72320
72474
  });
72321
72475
 
72322
- // ../feature-reminders/dist/events.js
72323
- var require_events2 = __commonJS({
72324
- "../feature-reminders/dist/events.js"(exports2) {
72325
- "use strict";
72326
- Object.defineProperty(exports2, "__esModule", { value: true });
72327
- exports2.ReminderEvents = void 0;
72328
- exports2.ReminderEvents = {
72329
- /**
72330
- * Create a ReminderConsumed event (logged when CLI returns a staged reminder).
72331
- */
72332
- reminderConsumed(context, state, _metadata) {
72333
- return {
72334
- type: "reminder:consumed",
72335
- time: Date.now(),
72336
- source: "cli",
72337
- context: {
72338
- sessionId: context.sessionId,
72339
- correlationId: context.correlationId,
72340
- traceId: context.traceId,
72341
- hook: context.hook,
72342
- taskId: context.taskId
72343
- },
72344
- payload: {
72345
- reminderName: state.reminderName,
72346
- reminderReturned: state.reminderReturned,
72347
- blocking: state.blocking,
72348
- priority: state.priority,
72349
- persistent: state.persistent
72350
- }
72351
- };
72352
- },
72353
- // Note: reminderStaged stays in @sidekick/core (used by staging-service.ts, circular dep)
72354
- /**
72355
- * Create a ReminderUnstaged event (logged when a reminder is removed from staging).
72356
- */
72357
- reminderUnstaged(context, state) {
72358
- return {
72359
- type: "reminder:unstaged",
72360
- time: Date.now(),
72361
- source: "daemon",
72362
- context: {
72363
- sessionId: context.sessionId,
72364
- correlationId: context.correlationId,
72365
- traceId: context.traceId,
72366
- hook: context.hook,
72367
- taskId: context.taskId
72368
- },
72369
- payload: {
72370
- reminderName: state.reminderName,
72371
- hookName: state.hookName,
72372
- reason: state.reason
72373
- }
72374
- };
72375
- },
72376
- /**
72377
- * Create a RemindersCleared event (logged when staging directory is cleaned).
72378
- */
72379
- remindersCleared(context, state, reason) {
72380
- return {
72381
- type: "reminder:cleared",
72382
- time: Date.now(),
72383
- source: "daemon",
72384
- context: {
72385
- sessionId: context.sessionId,
72386
- correlationId: context.correlationId,
72387
- traceId: context.traceId,
72388
- hook: context.hook,
72389
- taskId: context.taskId
72390
- },
72391
- payload: {
72392
- clearedCount: state.clearedCount,
72393
- hookNames: state.hookNames,
72394
- reason
72395
- }
72396
- };
72397
- }
72398
- };
72399
- }
72400
- });
72401
-
72402
72476
  // ../feature-reminders/dist/tool-pattern-matcher.js
72403
72477
  var require_tool_pattern_matcher = __commonJS({
72404
72478
  "../feature-reminders/dist/tool-pattern-matcher.js"(exports2) {
@@ -72506,22 +72580,48 @@ var require_track_verification_tools = __commonJS({
72506
72580
  }
72507
72581
  });
72508
72582
  }
72509
- async function stageToolsForFiles(filePaths, daemonCtx, sessionId, verificationTools, toolsState, remindersState) {
72583
+ async function stageToolsForFiles(filePaths, daemonCtx, sessionId, verificationTools, toolsState, remindersState, triggeredBy = "file_edit") {
72510
72584
  const existingReminders = await daemonCtx.staging.listReminders("Stop");
72511
72585
  const stagedNames = new Set(existingReminders.map((r) => r.name));
72512
72586
  let anyStaged = false;
72587
+ const emittedNotStaged = /* @__PURE__ */ new Set();
72513
72588
  for (const filePath of filePaths) {
72514
72589
  for (const [toolName, toolConfig] of Object.entries(verificationTools)) {
72515
- if (!toolConfig.enabled)
72516
- continue;
72517
72590
  const reminderId = TOOL_REMINDER_MAP[toolName];
72518
72591
  if (!reminderId)
72519
72592
  continue;
72520
- if (!picomatch_1.default.isMatch(filePath, toolConfig.clearing_patterns))
72593
+ if (!toolConfig.enabled) {
72594
+ const emitKey = `${reminderId}:feature_disabled`;
72595
+ if (!emittedNotStaged.has(emitKey)) {
72596
+ emittedNotStaged.add(emitKey);
72597
+ (0, core_1.logEvent)(daemonCtx.logger, events_js_1.ReminderEvents.reminderNotStaged({ sessionId }, {
72598
+ reminderName: reminderId,
72599
+ hookName: "Stop",
72600
+ reason: "feature_disabled",
72601
+ triggeredBy
72602
+ }));
72603
+ }
72604
+ continue;
72605
+ }
72606
+ if (!picomatch_1.default.isMatch(filePath, toolConfig.clearing_patterns)) {
72607
+ const emitKey = `${reminderId}:pattern_mismatch`;
72608
+ if (!emittedNotStaged.has(emitKey)) {
72609
+ emittedNotStaged.add(emitKey);
72610
+ (0, core_1.logEvent)(daemonCtx.logger, events_js_1.ReminderEvents.reminderNotStaged({ sessionId }, {
72611
+ reminderName: reminderId,
72612
+ hookName: "Stop",
72613
+ reason: "pattern_mismatch",
72614
+ triggeredBy
72615
+ }));
72616
+ }
72521
72617
  continue;
72618
+ }
72522
72619
  const current = toolsState[toolName];
72523
72620
  if (!current || current.status === "staged") {
72524
- const staged = await ensureToolReminderStaged(daemonCtx, reminderId, stagedNames);
72621
+ const staged = await ensureToolReminderStaged(daemonCtx, reminderId, stagedNames, {
72622
+ reason: current ? "re-staged" : "initial",
72623
+ triggeredBy: "file_edit"
72624
+ });
72525
72625
  if (staged) {
72526
72626
  if (!current) {
72527
72627
  toolsState[toolName] = {
@@ -72537,7 +72637,11 @@ var require_track_verification_tools = __commonJS({
72537
72637
  } else {
72538
72638
  const newEdits = current.editsSinceVerified + 1;
72539
72639
  if (newEdits >= toolConfig.clearing_threshold) {
72540
- const staged = await ensureToolReminderStaged(daemonCtx, reminderId, stagedNames);
72640
+ const staged = await ensureToolReminderStaged(daemonCtx, reminderId, stagedNames, {
72641
+ reason: "threshold_reached",
72642
+ triggeredBy: "file_edit",
72643
+ thresholdState: { current: newEdits, threshold: toolConfig.clearing_threshold }
72644
+ });
72541
72645
  if (staged) {
72542
72646
  toolsState[toolName] = {
72543
72647
  ...current,
@@ -72554,6 +72658,14 @@ var require_track_verification_tools = __commonJS({
72554
72658
  status: "cooldown",
72555
72659
  editsSinceVerified: newEdits
72556
72660
  };
72661
+ (0, core_1.logEvent)(daemonCtx.logger, events_js_1.ReminderEvents.reminderNotStaged({ sessionId }, {
72662
+ reminderName: reminderId,
72663
+ hookName: "Stop",
72664
+ reason: "below_threshold",
72665
+ threshold: toolConfig.clearing_threshold,
72666
+ currentValue: newEdits,
72667
+ triggeredBy
72668
+ }));
72557
72669
  }
72558
72670
  }
72559
72671
  }
@@ -72599,7 +72711,16 @@ var require_track_verification_tools = __commonJS({
72599
72711
  lastMatchedScope: match.scope
72600
72712
  };
72601
72713
  await daemonCtx.staging.deleteReminder("Stop", reminderId);
72602
- (0, core_1.logEvent)(daemonCtx.logger, events_js_1.ReminderEvents.reminderUnstaged({ sessionId }, { reminderName: reminderId, hookName: "Stop", reason: "tool_verified" }));
72714
+ (0, core_1.logEvent)(daemonCtx.logger, events_js_1.ReminderEvents.reminderUnstaged({ sessionId }, {
72715
+ reminderName: reminderId,
72716
+ hookName: "Stop",
72717
+ reason: "tool_verified",
72718
+ triggeredBy: "verification_passed",
72719
+ toolState: {
72720
+ status: toolsState[toolName].status,
72721
+ editsSinceVerified: toolsState[toolName].editsSinceVerified
72722
+ }
72723
+ }));
72603
72724
  anyUnstaged = true;
72604
72725
  daemonCtx.logger.debug("VC tool verified", {
72605
72726
  toolName,
@@ -72614,13 +72735,18 @@ var require_track_verification_tools = __commonJS({
72614
72735
  const hasPerToolReminders = remaining.some((r) => VC_TOOL_NAME_SET.has(r.name));
72615
72736
  if (!hasPerToolReminders) {
72616
72737
  await daemonCtx.staging.deleteReminder("Stop", types_js_1.ReminderIds.VERIFY_COMPLETION);
72617
- (0, core_1.logEvent)(daemonCtx.logger, events_js_1.ReminderEvents.reminderUnstaged({ sessionId }, { reminderName: types_js_1.ReminderIds.VERIFY_COMPLETION, hookName: "Stop", reason: "all_tools_verified" }));
72738
+ (0, core_1.logEvent)(daemonCtx.logger, events_js_1.ReminderEvents.reminderUnstaged({ sessionId }, {
72739
+ reminderName: types_js_1.ReminderIds.VERIFY_COMPLETION,
72740
+ hookName: "Stop",
72741
+ reason: "all_tools_verified",
72742
+ triggeredBy: "verification_passed"
72743
+ }));
72618
72744
  daemonCtx.logger.info("All VC tools verified, unstaged wrapper", { sessionId });
72619
72745
  }
72620
72746
  await remindersState.verificationTools.write(sessionId, toolsState);
72621
72747
  }
72622
72748
  }
72623
- async function ensureToolReminderStaged(daemonCtx, reminderId, stagedNames) {
72749
+ async function ensureToolReminderStaged(daemonCtx, reminderId, stagedNames, enrichment) {
72624
72750
  if (stagedNames.has(reminderId))
72625
72751
  return true;
72626
72752
  const reminder = (0, reminder_utils_js_1.resolveReminder)(reminderId, {
@@ -72634,7 +72760,7 @@ var require_track_verification_tools = __commonJS({
72634
72760
  await (0, reminder_utils_js_1.stageReminder)(daemonCtx, "Stop", {
72635
72761
  ...reminder,
72636
72762
  stagedAt: { timestamp: Date.now(), turnCount: 0, toolsThisTurn: 0, toolCount: 0 }
72637
- });
72763
+ }, enrichment);
72638
72764
  return true;
72639
72765
  }
72640
72766
  }
@@ -72650,6 +72776,7 @@ var require_stage_stop_bash_changes = __commonJS({
72650
72776
  Object.defineProperty(exports2, "__esModule", { value: true });
72651
72777
  exports2.registerStageBashChanges = registerStageBashChanges;
72652
72778
  var core_1 = require_dist4();
72779
+ var events_js_1 = require_events2();
72653
72780
  var types_1 = require_dist();
72654
72781
  var picomatch_1 = __importDefault(require_picomatch2());
72655
72782
  var track_verification_tools_js_1 = require_track_verification_tools();
@@ -72719,6 +72846,12 @@ var require_stage_stop_bash_changes = __commonJS({
72719
72846
  if (lastConsumed?.stagedAt) {
72720
72847
  const shouldReactivate = metrics.turnCount > lastConsumed.stagedAt.turnCount;
72721
72848
  if (!shouldReactivate) {
72849
+ (0, core_1.logEvent)(daemonCtx.logger, events_js_1.ReminderEvents.reminderNotStaged({ sessionId }, {
72850
+ reminderName: "verify-completion",
72851
+ hookName: "Stop",
72852
+ reason: "same_turn",
72853
+ triggeredBy: "bash_command"
72854
+ }));
72722
72855
  daemonCtx.logger.debug("Bash VC: skipped (already consumed this turn)", {
72723
72856
  currentTurn: metrics.turnCount,
72724
72857
  lastConsumedTurn: lastConsumed.stagedAt.turnCount
@@ -72734,12 +72867,25 @@ var require_stage_stop_bash_changes = __commonJS({
72734
72867
  currentCount: current.length,
72735
72868
  newFileCount: newFiles.length
72736
72869
  });
72737
- if (newFiles.length === 0)
72870
+ if (newFiles.length === 0) {
72871
+ (0, core_1.logEvent)(daemonCtx.logger, events_js_1.ReminderEvents.reminderNotStaged({ sessionId }, {
72872
+ reminderName: "verify-completion",
72873
+ hookName: "Stop",
72874
+ reason: "no_changes_detected",
72875
+ triggeredBy: "bash_command"
72876
+ }));
72738
72877
  return;
72878
+ }
72739
72879
  const featureConfig = context.config.getFeature("reminders");
72740
72880
  const config = { ...types_js_1.DEFAULT_REMINDERS_SETTINGS, ...featureConfig.settings };
72741
72881
  const sourceMatches = newFiles.filter((f) => picomatch_1.default.isMatch(f, config.source_code_patterns));
72742
72882
  if (sourceMatches.length === 0) {
72883
+ (0, core_1.logEvent)(daemonCtx.logger, events_js_1.ReminderEvents.reminderNotStaged({ sessionId }, {
72884
+ reminderName: "verify-completion",
72885
+ hookName: "Stop",
72886
+ reason: "pattern_mismatch",
72887
+ triggeredBy: "bash_command"
72888
+ }));
72743
72889
  daemonCtx.logger.debug("Bash VC: new files found but no source code matches", { newFiles });
72744
72890
  return;
72745
72891
  }
@@ -72753,7 +72899,7 @@ var require_stage_stop_bash_changes = __commonJS({
72753
72899
  const remindersState = (0, state_js_1.createRemindersState)(daemonCtx.stateService);
72754
72900
  const stateResult = await remindersState.verificationTools.read(sessionId);
72755
72901
  const toolsState = { ...stateResult.data };
72756
- await (0, track_verification_tools_js_1.stageToolsForFiles)(sourceMatches, daemonCtx, sessionId, verificationTools, toolsState, remindersState);
72902
+ await (0, track_verification_tools_js_1.stageToolsForFiles)(sourceMatches, daemonCtx, sessionId, verificationTools, toolsState, remindersState, "bash_command");
72757
72903
  }
72758
72904
  });
72759
72905
  }
@@ -72866,6 +73012,11 @@ var require_unstage_verify_completion = __commonJS({
72866
73012
  await remindersState.vcUnverified.delete(sessionId);
72867
73013
  }
72868
73014
  } else {
73015
+ (0, core_1.logEvent)(daemonCtx.logger, events_js_1.ReminderEvents.reminderNotStaged({ sessionId }, {
73016
+ reminderName: "verify-completion",
73017
+ hookName: "Stop",
73018
+ reason: "no_unverified_changes"
73019
+ }));
72869
73020
  daemonCtx.logger.info("VC unstage: no unverified changes, clearing reminder", {
72870
73021
  sessionId,
72871
73022
  hadState: unverifiedState !== null
@@ -72875,7 +73026,12 @@ var require_unstage_verify_completion = __commonJS({
72875
73026
  const reason = unverifiedState?.hasUnverifiedChanges ? "cycle_limit_reached" : "no_unverified_changes";
72876
73027
  for (const vcId of types_js_1.ALL_VC_REMINDER_IDS) {
72877
73028
  await daemonCtx.staging.deleteReminder("Stop", vcId);
72878
- (0, core_1.logEvent)(daemonCtx.logger, events_js_1.ReminderEvents.reminderUnstaged(eventContext, { reminderName: vcId, hookName: "Stop", reason }));
73029
+ (0, core_1.logEvent)(daemonCtx.logger, events_js_1.ReminderEvents.reminderUnstaged(eventContext, {
73030
+ reminderName: vcId,
73031
+ hookName: "Stop",
73032
+ reason,
73033
+ triggeredBy: unverifiedState?.hasUnverifiedChanges ? "cycle_limit" : "no_unverified_changes"
73034
+ }));
72879
73035
  }
72880
73036
  daemonCtx.logger.debug("VC unstage: deleted all VC reminders");
72881
73037
  }
@@ -72973,12 +73129,22 @@ var require_stage_persona_reminders = __commonJS({
72973
73129
  }
72974
73130
  async function stagePersonaRemindersForSession(ctx, sessionId, options) {
72975
73131
  if (!isPersonaInjectionEnabled(ctx)) {
73132
+ (0, core_1.logEvent)(ctx.logger, events_js_1.ReminderEvents.reminderNotStaged({ sessionId }, {
73133
+ reminderName: "remember-your-persona",
73134
+ hookName: "PreToolUse",
73135
+ reason: "feature_disabled"
73136
+ }));
72976
73137
  await clearPersonaReminders(ctx, sessionId);
72977
73138
  ctx.logger.debug("Persona injection disabled by config, cleaned up reminders", { sessionId });
72978
73139
  return;
72979
73140
  }
72980
73141
  const persona = await loadPersonaForSession(ctx, sessionId);
72981
73142
  if (!persona) {
73143
+ (0, core_1.logEvent)(ctx.logger, events_js_1.ReminderEvents.reminderNotStaged({ sessionId }, {
73144
+ reminderName: "remember-your-persona",
73145
+ hookName: "PreToolUse",
73146
+ reason: "no_persona"
73147
+ }));
72982
73148
  await clearPersonaReminders(ctx, sessionId);
72983
73149
  ctx.logger.debug("Persona cleared or disabled, unstaged persona reminders", { sessionId });
72984
73150
  return;
@@ -73278,7 +73444,19 @@ var require_consumption_handler_factory = __commonJS({
73278
73444
  reader.renameReminder(hook, reminder.name);
73279
73445
  }
73280
73446
  }
73281
- let response = buildResponse ? await buildResponse({ reminder: primary, reader, cliCtx, sessionId, event, supportsBlocking }) : buildDefaultResponse(primary, supportsBlocking);
73447
+ let enrichment;
73448
+ let response;
73449
+ if (buildResponse) {
73450
+ const result = await buildResponse({ reminder: primary, reader, cliCtx, sessionId, event, supportsBlocking });
73451
+ if ("response" in result && typeof result.response === "object") {
73452
+ response = result.response;
73453
+ enrichment = result.enrichment;
73454
+ } else {
73455
+ response = result;
73456
+ }
73457
+ } else {
73458
+ response = buildDefaultResponse(primary, supportsBlocking);
73459
+ }
73282
73460
  const secondaryContexts = reminders.slice(1).map((r) => r.additionalContext).filter((ctx2) => !!ctx2);
73283
73461
  if (secondaryContexts.length > 0) {
73284
73462
  const existing = response.additionalContext;
@@ -73296,7 +73474,8 @@ var require_consumption_handler_factory = __commonJS({
73296
73474
  reminderReturned: true,
73297
73475
  blocking: response.blocking ?? false,
73298
73476
  priority: primary.priority,
73299
- persistent: primary.persistent
73477
+ persistent: primary.persistent,
73478
+ ...enrichment
73300
73479
  }));
73301
73480
  return { response };
73302
73481
  }
@@ -73398,6 +73577,13 @@ var require_inject_stop = __commonJS({
73398
73577
  shouldBlock: classification.shouldBlock,
73399
73578
  reasoning: classification.reasoning?.slice(0, 200)
73400
73579
  });
73580
+ const classificationEnrichment = {
73581
+ classificationResult: {
73582
+ category: classification.category,
73583
+ confidence: classification.confidence,
73584
+ shouldBlock: classification.shouldBlock
73585
+ }
73586
+ };
73401
73587
  if (classification.shouldBlock) {
73402
73588
  cliCtx.logger.info("VC inject-stop: BLOCKING (claiming completion)", { sessionId });
73403
73589
  try {
@@ -73407,7 +73593,10 @@ var require_inject_stop = __commonJS({
73407
73593
  error: String(clearErr)
73408
73594
  });
73409
73595
  }
73410
- return (0, consumption_handler_factory_js_1.buildDefaultResponse)(reminder, supportsBlocking);
73596
+ return {
73597
+ response: (0, consumption_handler_factory_js_1.buildDefaultResponse)(reminder, supportsBlocking),
73598
+ enrichment: classificationEnrichment
73599
+ };
73411
73600
  } else {
73412
73601
  cliCtx.logger.info("VC inject-stop: NOT BLOCKING", {
73413
73602
  sessionId,
@@ -73433,13 +73622,13 @@ var require_inject_stop = __commonJS({
73433
73622
  });
73434
73623
  }
73435
73624
  if (classification.category === "ASKING_QUESTION" || classification.category === "ANSWERING_QUESTION") {
73436
- return {};
73625
+ return { response: {}, enrichment: classificationEnrichment };
73437
73626
  } else {
73438
73627
  const response = {};
73439
73628
  if (classification.userMessage) {
73440
73629
  response.userMessage = classification.userMessage;
73441
73630
  }
73442
- return response;
73631
+ return { response, enrichment: classificationEnrichment };
73443
73632
  }
73444
73633
  }
73445
73634
  } catch (err) {
@@ -73810,7 +73999,8 @@ var require_orchestrator = __commonJS({
73810
73999
  (0, core_1.logEvent)(this.deps.logger, events_js_1.ReminderEvents.reminderUnstaged(eventContext, {
73811
74000
  reminderName: vcId,
73812
74001
  hookName: "Stop",
73813
- reason: "pause_and_reflect_cascade"
74002
+ reason: "pause_and_reflect_cascade",
74003
+ triggeredBy: "cascade_from_pause_and_reflect"
73814
74004
  }));
73815
74005
  }
73816
74006
  this.deps.logger.debug("Unstaged all VC reminders after P&R staged", { sessionId });
@@ -73847,7 +74037,12 @@ var require_orchestrator = __commonJS({
73847
74037
  try {
73848
74038
  const staging = this.deps.getStagingService(sessionId);
73849
74039
  await staging.deleteReminder("PreToolUse", types_js_1.ReminderIds.PAUSE_AND_REFLECT);
73850
- (0, core_1.logEvent)(this.deps.logger, events_js_1.ReminderEvents.reminderUnstaged({ sessionId }, { reminderName: types_js_1.ReminderIds.PAUSE_AND_REFLECT, hookName: "PreToolUse", reason: "vc_consumed_cascade" }));
74040
+ (0, core_1.logEvent)(this.deps.logger, events_js_1.ReminderEvents.reminderUnstaged({ sessionId }, {
74041
+ reminderName: types_js_1.ReminderIds.PAUSE_AND_REFLECT,
74042
+ hookName: "PreToolUse",
74043
+ reason: "vc_consumed_cascade",
74044
+ triggeredBy: "cascade_from_verify_completion"
74045
+ }));
73851
74046
  this.deps.logger.debug("Unstaged P&R after VC consumed", { sessionId });
73852
74047
  } catch (err) {
73853
74048
  this.deps.logger.warn("Failed to unstage P&R after VC consumed", {
@@ -77051,6 +77246,7 @@ var require_daemon = __commonJS({
77051
77246
  async start() {
77052
77247
  try {
77053
77248
  this.logger.info("Daemon starting", { projectDir: this.projectDir, pid: process.pid });
77249
+ (0, core_1.logEvent)(this.logger, core_1.LogEvents.daemonStarting({ projectDir: this.projectDir, pid: process.pid }));
77054
77250
  this.setupErrorHandlers();
77055
77251
  await this.writePid();
77056
77252
  await this.writeToken();
@@ -77062,7 +77258,14 @@ var require_daemon = __commonJS({
77062
77258
  }
77063
77259
  (0, task_handlers_js_1.registerStandardTaskHandlers)(this.taskEngine, this.stateService, this.projectDir, this.logger, this.configService.getAll(), this.assetResolver);
77064
77260
  await this.ipcServer.start();
77261
+ (0, core_1.logEvent)(this.logger, core_1.LogEvents.ipcServerStarted({ socketPath: (0, core_1.getSocketPath)(this.projectDir) }));
77065
77262
  this.configWatcher.start();
77263
+ const watchedFiles = [
77264
+ this.stateService.rootDir(),
77265
+ path_1.default.join((0, os_1.homedir)(), ".sidekick"),
77266
+ ...this.configService.core.development.enabled ? [(0, core_1.getDefaultAssetsDir)()] : []
77267
+ ];
77268
+ (0, core_1.logEvent)(this.logger, core_1.LogEvents.configWatcherStarted({ projectDir: this.projectDir, watchedFiles }));
77066
77269
  this.personaWatcher.start();
77067
77270
  this.startIdleCheck();
77068
77271
  this.startHeartbeat();
@@ -77070,6 +77273,7 @@ var require_daemon = __commonJS({
77070
77273
  await this.registerProject();
77071
77274
  this.startRegistryHeartbeat();
77072
77275
  this.logger.info("Daemon started successfully");
77276
+ (0, core_1.logEvent)(this.logger, core_1.LogEvents.daemonStarted({ startupDurationMs: Date.now() - this.startTime }));
77073
77277
  await (0, core_1.updateDaemonHealth)(this.projectDir, "healthy", this.logger);
77074
77278
  } catch (err) {
77075
77279
  this.logger.fatal("Failed to start daemon", { error: err });
@@ -78035,6 +78239,7 @@ var require_daemon = __commonJS({
78035
78239
  }, EVICTION_INTERVAL_MS);
78036
78240
  this.evictionTimer.unref();
78037
78241
  this.logger.info("Session eviction timer started", { intervalMs: EVICTION_INTERVAL_MS });
78242
+ (0, core_1.logEvent)(this.logger, core_1.LogEvents.sessionEvictionStarted({ intervalMs: EVICTION_INTERVAL_MS }));
78038
78243
  }
78039
78244
  stopEvictionTimer() {
78040
78245
  if (this.evictionTimer) {