@scotthamilton77/sidekick 0.1.15 → 0.1.16

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.
@@ -18,6 +18,34 @@ settings:
18
18
  user-prompt-submit: 10
19
19
  remember-your-persona: 5
20
20
 
21
+ # Command runner prefixes that trigger unanchored pattern matching.
22
+ # When a bash command starts with one of these prefixes, the tool pattern
23
+ # matcher drops its first-token anchor and scans for the tool name at any
24
+ # position. This allows commands like 'uv run mypy' to match pattern 'mypy'.
25
+ # Array merge strategy: replace (project/user config replaces defaults entirely).
26
+ command_runners:
27
+ # Python
28
+ - prefix: "uv run"
29
+ - prefix: "poetry run"
30
+ - prefix: "pipx run"
31
+ - prefix: "pdm run"
32
+ - prefix: "hatch run"
33
+ - prefix: "conda run"
34
+ # Node.js
35
+ - prefix: "npx"
36
+ - prefix: "pnpx"
37
+ - prefix: "bunx"
38
+ - prefix: "pnpm dlx"
39
+ - prefix: "pnpm exec"
40
+ - prefix: "bun run"
41
+ - prefix: "yarn dlx"
42
+ - prefix: "yarn exec"
43
+ - prefix: "npm exec"
44
+ # Ruby
45
+ - prefix: "bundle exec"
46
+ # .NET
47
+ - prefix: "dotnet tool run"
48
+
21
49
  # Glob patterns that trigger verify-completion reminder on file edit.
22
50
  # Uses picomatch syntax: https://github.com/micromatch/picomatch
23
51
  # Only edits to files matching these patterns will stage the reminder.
package/dist/bin.js CHANGED
@@ -71535,7 +71535,7 @@ var require_types2 = __commonJS({
71535
71535
  "../feature-reminders/dist/types.js"(exports2) {
71536
71536
  "use strict";
71537
71537
  Object.defineProperty(exports2, "__esModule", { value: true });
71538
- exports2.ALL_VC_REMINDER_IDS = exports2.VC_TOOL_REMINDER_IDS = exports2.TOOL_REMINDER_MAP = 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;
71538
+ exports2.ALL_VC_REMINDER_IDS = exports2.VC_TOOL_REMINDER_IDS = exports2.TOOL_REMINDER_MAP = exports2.ReminderIds = exports2.DEFAULT_REMINDERS_SETTINGS = exports2.DEFAULT_COMPLETION_DETECTION_SETTINGS = exports2.DEFAULT_SOURCE_CODE_PATTERNS = exports2.DEFAULT_VERIFICATION_TOOLS = exports2.CommandRunnerSchema = exports2.VerificationToolsMapSchema = exports2.VerificationToolConfigSchema = exports2.ToolPatternSchema = exports2.ToolPatternScopeSchema = void 0;
71539
71539
  var zod_1 = require_zod2();
71540
71540
  exports2.ToolPatternScopeSchema = zod_1.z.enum(["project", "package", "file"]);
71541
71541
  exports2.ToolPatternSchema = zod_1.z.object({
@@ -71550,6 +71550,9 @@ var require_types2 = __commonJS({
71550
71550
  clearing_patterns: zod_1.z.array(zod_1.z.string()).min(1)
71551
71551
  });
71552
71552
  exports2.VerificationToolsMapSchema = zod_1.z.record(zod_1.z.string(), exports2.VerificationToolConfigSchema);
71553
+ exports2.CommandRunnerSchema = zod_1.z.object({
71554
+ prefix: zod_1.z.string().trim().min(1)
71555
+ });
71553
71556
  exports2.DEFAULT_VERIFICATION_TOOLS = {
71554
71557
  build: {
71555
71558
  enabled: true,
@@ -71738,7 +71741,30 @@ var require_types2 = __commonJS({
71738
71741
  reminder_thresholds: {
71739
71742
  "user-prompt-submit": 10,
71740
71743
  "remember-your-persona": 5
71741
- }
71744
+ },
71745
+ command_runners: [
71746
+ // Python
71747
+ { prefix: "uv run" },
71748
+ { prefix: "poetry run" },
71749
+ { prefix: "pipx run" },
71750
+ { prefix: "pdm run" },
71751
+ { prefix: "hatch run" },
71752
+ { prefix: "conda run" },
71753
+ // Node.js
71754
+ { prefix: "npx" },
71755
+ { prefix: "pnpx" },
71756
+ { prefix: "bunx" },
71757
+ { prefix: "pnpm dlx" },
71758
+ { prefix: "pnpm exec" },
71759
+ { prefix: "bun run" },
71760
+ { prefix: "yarn dlx" },
71761
+ { prefix: "yarn exec" },
71762
+ { prefix: "npm exec" },
71763
+ // Ruby
71764
+ { prefix: "bundle exec" },
71765
+ // .NET
71766
+ { prefix: "dotnet tool run" }
71767
+ ]
71742
71768
  };
71743
71769
  exports2.ReminderIds = {
71744
71770
  USER_PROMPT_SUBMIT: "user-prompt-submit",
@@ -71970,6 +71996,16 @@ var require_stage_default_user_prompt = __commonJS({
71970
71996
  continue;
71971
71997
  const newCount = typedEntry.messagesSinceLastStaging + 1;
71972
71998
  if (newCount >= threshold) {
71999
+ (0, core_1.logEvent)(handlerCtx.logger, types_1.DecisionEvents.decisionRecorded({ sessionId }, {
72000
+ decision: "staged",
72001
+ reason: [
72002
+ `message count reached threshold (${newCount}/${threshold})`,
72003
+ `target hook: ${typedEntry.targetHook}`,
72004
+ `messages since last staging: ${typedEntry.messagesSinceLastStaging}`
72005
+ ].join("; "),
72006
+ subsystem: "reminder-throttle",
72007
+ title: `Re-stage ${reminderId} reminder`
72008
+ }));
71973
72009
  const metrics = event.metadata.metrics;
71974
72010
  const stagedAt = {
71975
72011
  timestamp: Date.now(),
@@ -71981,12 +72017,6 @@ var require_stage_default_user_prompt = __commonJS({
71981
72017
  ...typedEntry.cachedReminder,
71982
72018
  stagedAt
71983
72019
  });
71984
- (0, core_1.logEvent)(handlerCtx.logger, types_1.DecisionEvents.decisionRecorded({ sessionId }, {
71985
- decision: "staged",
71986
- reason: `message count reached threshold (${newCount}/${threshold})`,
71987
- subsystem: "user-prompt-reminders",
71988
- title: "Stage user-prompt reminder"
71989
- }));
71990
72020
  state[reminderId] = { ...typedEntry, messagesSinceLastStaging: 0 };
71991
72021
  handlerCtx.logger.debug("Throttle: re-staged reminder", {
71992
72022
  sessionId,
@@ -72036,6 +72066,9 @@ var require_events2 = __commonJS({
72036
72066
  blocking: state.blocking,
72037
72067
  priority: state.priority,
72038
72068
  persistent: state.persistent,
72069
+ ...state.renderedText !== void 0 && {
72070
+ renderedText: state.renderedText
72071
+ },
72039
72072
  ...state.classificationResult !== void 0 && {
72040
72073
  classificationResult: state.classificationResult
72041
72074
  }
@@ -73727,17 +73760,51 @@ var require_tool_pattern_matcher = __commonJS({
73727
73760
  exports2.matchesToolPattern = matchesToolPattern;
73728
73761
  exports2.findMatchingPattern = findMatchingPattern;
73729
73762
  var SHELL_OPERATOR_RE = /\s*(?:&&|\|\||[;|])\s*/;
73730
- function matchesToolPattern(command, pattern) {
73763
+ function detectRunnerPrefix(segmentTokens, runners) {
73764
+ let longestMatch = 0;
73765
+ for (const runner of runners) {
73766
+ if (typeof runner.prefix !== "string")
73767
+ continue;
73768
+ const prefixTokens = runner.prefix.trim().split(/\s+/);
73769
+ if (prefixTokens.length === 0)
73770
+ continue;
73771
+ if (prefixTokens.length > segmentTokens.length)
73772
+ continue;
73773
+ let matches = true;
73774
+ for (let i = 0; i < prefixTokens.length; i++) {
73775
+ if (segmentTokens[i] !== prefixTokens[i]) {
73776
+ matches = false;
73777
+ break;
73778
+ }
73779
+ }
73780
+ if (matches && prefixTokens.length > longestMatch) {
73781
+ longestMatch = prefixTokens.length;
73782
+ }
73783
+ }
73784
+ return longestMatch;
73785
+ }
73786
+ function matchesToolPattern(command, pattern, runners) {
73731
73787
  if (!command || !pattern)
73732
73788
  return false;
73733
73789
  const segments = command.split(SHELL_OPERATOR_RE);
73734
73790
  const patternTokens = pattern.trim().split(/\s+/).filter(Boolean);
73735
73791
  if (patternTokens.length === 0)
73736
73792
  return false;
73793
+ const activeRunners = runners?.length ? runners : void 0;
73737
73794
  return segments.some((segment) => {
73738
73795
  const cmdTokens = segment.trim().split(/\s+/);
73739
73796
  if (cmdTokens.length === 0 || cmdTokens[0] === "")
73740
73797
  return false;
73798
+ const runnerTokenCount = activeRunners ? detectRunnerPrefix(cmdTokens, activeRunners) : 0;
73799
+ if (runnerTokenCount > 0) {
73800
+ let pi2 = 0;
73801
+ for (let ci = runnerTokenCount; ci < cmdTokens.length && pi2 < patternTokens.length; ci++) {
73802
+ if (patternTokens[pi2] === "*" || patternTokens[pi2] === cmdTokens[ci]) {
73803
+ pi2++;
73804
+ }
73805
+ }
73806
+ return pi2 === patternTokens.length;
73807
+ }
73741
73808
  if (cmdTokens[0] !== patternTokens[0])
73742
73809
  return false;
73743
73810
  let pi = 1;
@@ -73749,11 +73816,11 @@ var require_tool_pattern_matcher = __commonJS({
73749
73816
  return pi === patternTokens.length;
73750
73817
  });
73751
73818
  }
73752
- function findMatchingPattern(command, patterns) {
73819
+ function findMatchingPattern(command, patterns, runners) {
73753
73820
  for (const pattern of patterns) {
73754
73821
  if (pattern.tool === null)
73755
73822
  continue;
73756
- if (matchesToolPattern(command, pattern.tool))
73823
+ if (matchesToolPattern(command, pattern.tool, runners))
73757
73824
  return pattern;
73758
73825
  }
73759
73826
  return null;
@@ -73809,13 +73876,14 @@ var require_track_verification_tools = __commonJS({
73809
73876
  const featureConfig = context.config.getFeature("reminders");
73810
73877
  const config = { ...types_js_1.DEFAULT_REMINDERS_SETTINGS, ...featureConfig.settings };
73811
73878
  const verificationTools = config.verification_tools ?? {};
73879
+ const runners = config.command_runners ?? [];
73812
73880
  const remindersState = (0, state_js_1.createRemindersState)(daemonCtx.stateService);
73813
73881
  const stateResult = await remindersState.verificationTools.read(sessionId);
73814
73882
  const toolsState = { ...stateResult.data };
73815
73883
  if (FILE_EDIT_TOOLS.includes(toolName)) {
73816
73884
  await handleFileEdit(event, daemonCtx, sessionId, verificationTools, toolsState, remindersState);
73817
73885
  } else if (toolName === "Bash") {
73818
- await handleBashCommand(event, daemonCtx, sessionId, verificationTools, toolsState, remindersState);
73886
+ await handleBashCommand(event, daemonCtx, sessionId, verificationTools, toolsState, remindersState, runners);
73819
73887
  }
73820
73888
  }
73821
73889
  });
@@ -73937,7 +74005,7 @@ var require_track_verification_tools = __commonJS({
73937
74005
  return;
73938
74006
  await stageToolsForFiles([filePath], daemonCtx, sessionId, verificationTools, toolsState, remindersState);
73939
74007
  }
73940
- async function handleBashCommand(event, daemonCtx, sessionId, verificationTools, toolsState, remindersState) {
74008
+ async function handleBashCommand(event, daemonCtx, sessionId, verificationTools, toolsState, remindersState, runners = []) {
73941
74009
  const command = extractToolInput(event)?.command;
73942
74010
  if (!command)
73943
74011
  return;
@@ -73948,7 +74016,7 @@ var require_track_verification_tools = __commonJS({
73948
74016
  const reminderId = types_js_1.TOOL_REMINDER_MAP[toolName];
73949
74017
  if (!reminderId)
73950
74018
  continue;
73951
- const match = (0, tool_pattern_matcher_js_1.findMatchingPattern)(command, toolConfig.patterns);
74019
+ const match = (0, tool_pattern_matcher_js_1.findMatchingPattern)(command, toolConfig.patterns, runners);
73952
74020
  if (!match)
73953
74021
  continue;
73954
74022
  toolsState[toolName] = {
@@ -74764,6 +74832,12 @@ var require_consumption_handler_factory = __commonJS({
74764
74832
  if (onConsume) {
74765
74833
  await onConsume({ reminder: primary, reader, cliCtx, sessionId });
74766
74834
  }
74835
+ const renderedParts = [];
74836
+ if (response.userMessage)
74837
+ renderedParts.push(response.userMessage);
74838
+ if (response.additionalContext)
74839
+ renderedParts.push(response.additionalContext);
74840
+ const renderedText = renderedParts.length > 0 ? renderedParts.join("\n\n") : void 0;
74767
74841
  (0, core_1.logEvent)(cliCtx.logger, events_js_1.ReminderEvents.reminderConsumed({
74768
74842
  sessionId,
74769
74843
  hook
@@ -74773,6 +74847,7 @@ var require_consumption_handler_factory = __commonJS({
74773
74847
  blocking: response.blocking ?? false,
74774
74848
  priority: primary.priority,
74775
74849
  persistent: primary.persistent,
74850
+ renderedText,
74776
74851
  ...enrichment
74777
74852
  }));
74778
74853
  return { response };
@@ -77149,6 +77224,7 @@ var require_update_summary = __commonJS({
77149
77224
  "use strict";
77150
77225
  Object.defineProperty(exports2, "__esModule", { value: true });
77151
77226
  exports2.buildPersonaContext = void 0;
77227
+ exports2.resetAnalysisGuard = resetAnalysisGuard;
77152
77228
  exports2.interpolateTemplate = interpolateTemplate;
77153
77229
  exports2.updateSessionSummary = updateSessionSummary;
77154
77230
  var core_1 = require_dist4();
@@ -77161,6 +77237,7 @@ var require_update_summary = __commonJS({
77161
77237
  var persona_selection_js_1 = require_persona_selection();
77162
77238
  var DECISION_TITLE_SKIP = "Skip session analysis";
77163
77239
  var DECISION_TITLE_RUN = "Run session analysis";
77240
+ var analysisInFlight = /* @__PURE__ */ new Map();
77164
77241
  var PROMPT_FILE = "prompts/session-summary.prompt.txt";
77165
77242
  var SNARKY_PROMPT_FILE = "prompts/snarky-message.prompt.txt";
77166
77243
  var RESUME_PROMPT_FILE = "prompts/resume-message.prompt.txt";
@@ -77178,6 +77255,9 @@ var require_update_summary = __commonJS({
77178
77255
  Object.defineProperty(exports2, "buildPersonaContext", { enumerable: true, get: function() {
77179
77256
  return persona_utils_js_2.buildPersonaContext;
77180
77257
  } });
77258
+ function resetAnalysisGuard() {
77259
+ analysisInFlight.clear();
77260
+ }
77181
77261
  function interpolateTemplate(template, context) {
77182
77262
  let result = template;
77183
77263
  const conditionalRegex = /\{\{#if\s+(\w+)\}\}([\s\S]*?)\{\{\/if\}\}/g;
@@ -77221,12 +77301,12 @@ var require_update_summary = __commonJS({
77221
77301
  }
77222
77302
  (0, core_1.logEvent)(ctx.logger, types_1.DecisionEvents.decisionRecorded(event.context, {
77223
77303
  decision: "calling",
77224
- reason: "BulkProcessingComplete - analyzing full transcript",
77304
+ reason: "Bulk transcript replay complete \u2014 running catch-up analysis",
77225
77305
  subsystem: "session-summary",
77226
77306
  title: DECISION_TITLE_RUN
77227
77307
  }));
77228
77308
  const countdown2 = await loadCountdownState(summaryState, sessionId);
77229
- void performAnalysis(event, ctx, summaryState, countdown2, "user_prompt_forced");
77309
+ void performAnalysis(event, ctx, summaryState, countdown2, "bulk_replay_complete");
77230
77310
  return;
77231
77311
  }
77232
77312
  const countdown = await loadCountdownState(summaryState, sessionId);
@@ -77241,7 +77321,7 @@ var require_update_summary = __commonJS({
77241
77321
  }
77242
77322
  (0, core_1.logEvent)(ctx.logger, types_1.DecisionEvents.decisionRecorded(event.context, {
77243
77323
  decision: "calling",
77244
- reason: "countdown reached zero after ToolResult",
77324
+ reason: "Prompt countdown reached zero \u2014 running scheduled analysis",
77245
77325
  subsystem: "session-summary",
77246
77326
  title: DECISION_TITLE_RUN
77247
77327
  }));
@@ -77276,6 +77356,17 @@ var require_update_summary = __commonJS({
77276
77356
  }
77277
77357
  async function performAnalysis(event, ctx, summaryState, countdown, reason) {
77278
77358
  const { sessionId } = event.context;
77359
+ if (analysisInFlight.has(sessionId)) {
77360
+ analysisInFlight.set(sessionId, true);
77361
+ (0, core_1.logEvent)(ctx.logger, types_1.DecisionEvents.decisionRecorded(event.context, {
77362
+ decision: "deferred",
77363
+ reason: "Analysis deferred \u2014 will rerun after current analysis completes",
77364
+ subsystem: "session-summary",
77365
+ title: DECISION_TITLE_SKIP
77366
+ }));
77367
+ return;
77368
+ }
77369
+ analysisInFlight.set(sessionId, false);
77279
77370
  try {
77280
77371
  const startTime = Date.now();
77281
77372
  (0, core_1.logEvent)(ctx.logger, events_js_1.SessionSummaryEvents.summaryStart(event.context, {
@@ -77420,6 +77511,13 @@ var require_update_summary = __commonJS({
77420
77511
  reason,
77421
77512
  error: err instanceof Error ? err.message : String(err)
77422
77513
  });
77514
+ } finally {
77515
+ const rerunPending = analysisInFlight.get(sessionId) ?? false;
77516
+ analysisInFlight.delete(sessionId);
77517
+ if (rerunPending) {
77518
+ const freshCountdown = await loadCountdownState(summaryState, sessionId);
77519
+ void performAnalysis(event, ctx, summaryState, freshCountdown, reason);
77520
+ }
77423
77521
  }
77424
77522
  }
77425
77523
  async function loadCurrentSummary(summaryState, sessionId) {
@@ -83915,7 +84013,7 @@ var require_cli = __commonJS({
83915
84013
  var promises_12 = require("node:fs/promises");
83916
84014
  var node_stream_1 = require("node:stream");
83917
84015
  var yargs_parser_1 = __importDefault2(require_build());
83918
- var VERSION = true ? "0.1.15" : "dev";
84016
+ var VERSION = true ? "0.1.16" : "dev";
83919
84017
  var SANDBOX_ERROR_MESSAGE = `Error: Daemon commands cannot run in sandbox mode.
83920
84018
 
83921
84019
  Claude Code's sandbox blocks Unix socket operations required for daemon IPC.
package/dist/daemon.js CHANGED
@@ -70382,7 +70382,7 @@ var require_types2 = __commonJS({
70382
70382
  "../feature-reminders/dist/types.js"(exports2) {
70383
70383
  "use strict";
70384
70384
  Object.defineProperty(exports2, "__esModule", { value: true });
70385
- exports2.ALL_VC_REMINDER_IDS = exports2.VC_TOOL_REMINDER_IDS = exports2.TOOL_REMINDER_MAP = 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;
70385
+ exports2.ALL_VC_REMINDER_IDS = exports2.VC_TOOL_REMINDER_IDS = exports2.TOOL_REMINDER_MAP = exports2.ReminderIds = exports2.DEFAULT_REMINDERS_SETTINGS = exports2.DEFAULT_COMPLETION_DETECTION_SETTINGS = exports2.DEFAULT_SOURCE_CODE_PATTERNS = exports2.DEFAULT_VERIFICATION_TOOLS = exports2.CommandRunnerSchema = exports2.VerificationToolsMapSchema = exports2.VerificationToolConfigSchema = exports2.ToolPatternSchema = exports2.ToolPatternScopeSchema = void 0;
70386
70386
  var zod_1 = require_zod2();
70387
70387
  exports2.ToolPatternScopeSchema = zod_1.z.enum(["project", "package", "file"]);
70388
70388
  exports2.ToolPatternSchema = zod_1.z.object({
@@ -70397,6 +70397,9 @@ var require_types2 = __commonJS({
70397
70397
  clearing_patterns: zod_1.z.array(zod_1.z.string()).min(1)
70398
70398
  });
70399
70399
  exports2.VerificationToolsMapSchema = zod_1.z.record(zod_1.z.string(), exports2.VerificationToolConfigSchema);
70400
+ exports2.CommandRunnerSchema = zod_1.z.object({
70401
+ prefix: zod_1.z.string().trim().min(1)
70402
+ });
70400
70403
  exports2.DEFAULT_VERIFICATION_TOOLS = {
70401
70404
  build: {
70402
70405
  enabled: true,
@@ -70585,7 +70588,30 @@ var require_types2 = __commonJS({
70585
70588
  reminder_thresholds: {
70586
70589
  "user-prompt-submit": 10,
70587
70590
  "remember-your-persona": 5
70588
- }
70591
+ },
70592
+ command_runners: [
70593
+ // Python
70594
+ { prefix: "uv run" },
70595
+ { prefix: "poetry run" },
70596
+ { prefix: "pipx run" },
70597
+ { prefix: "pdm run" },
70598
+ { prefix: "hatch run" },
70599
+ { prefix: "conda run" },
70600
+ // Node.js
70601
+ { prefix: "npx" },
70602
+ { prefix: "pnpx" },
70603
+ { prefix: "bunx" },
70604
+ { prefix: "pnpm dlx" },
70605
+ { prefix: "pnpm exec" },
70606
+ { prefix: "bun run" },
70607
+ { prefix: "yarn dlx" },
70608
+ { prefix: "yarn exec" },
70609
+ { prefix: "npm exec" },
70610
+ // Ruby
70611
+ { prefix: "bundle exec" },
70612
+ // .NET
70613
+ { prefix: "dotnet tool run" }
70614
+ ]
70589
70615
  };
70590
70616
  exports2.ReminderIds = {
70591
70617
  USER_PROMPT_SUBMIT: "user-prompt-submit",
@@ -70817,6 +70843,16 @@ var require_stage_default_user_prompt = __commonJS({
70817
70843
  continue;
70818
70844
  const newCount = typedEntry.messagesSinceLastStaging + 1;
70819
70845
  if (newCount >= threshold) {
70846
+ (0, core_1.logEvent)(handlerCtx.logger, types_1.DecisionEvents.decisionRecorded({ sessionId }, {
70847
+ decision: "staged",
70848
+ reason: [
70849
+ `message count reached threshold (${newCount}/${threshold})`,
70850
+ `target hook: ${typedEntry.targetHook}`,
70851
+ `messages since last staging: ${typedEntry.messagesSinceLastStaging}`
70852
+ ].join("; "),
70853
+ subsystem: "reminder-throttle",
70854
+ title: `Re-stage ${reminderId} reminder`
70855
+ }));
70820
70856
  const metrics = event.metadata.metrics;
70821
70857
  const stagedAt = {
70822
70858
  timestamp: Date.now(),
@@ -70828,12 +70864,6 @@ var require_stage_default_user_prompt = __commonJS({
70828
70864
  ...typedEntry.cachedReminder,
70829
70865
  stagedAt
70830
70866
  });
70831
- (0, core_1.logEvent)(handlerCtx.logger, types_1.DecisionEvents.decisionRecorded({ sessionId }, {
70832
- decision: "staged",
70833
- reason: `message count reached threshold (${newCount}/${threshold})`,
70834
- subsystem: "user-prompt-reminders",
70835
- title: "Stage user-prompt reminder"
70836
- }));
70837
70867
  state[reminderId] = { ...typedEntry, messagesSinceLastStaging: 0 };
70838
70868
  handlerCtx.logger.debug("Throttle: re-staged reminder", {
70839
70869
  sessionId,
@@ -70883,6 +70913,9 @@ var require_events2 = __commonJS({
70883
70913
  blocking: state.blocking,
70884
70914
  priority: state.priority,
70885
70915
  persistent: state.persistent,
70916
+ ...state.renderedText !== void 0 && {
70917
+ renderedText: state.renderedText
70918
+ },
70886
70919
  ...state.classificationResult !== void 0 && {
70887
70920
  classificationResult: state.classificationResult
70888
70921
  }
@@ -72574,17 +72607,51 @@ var require_tool_pattern_matcher = __commonJS({
72574
72607
  exports2.matchesToolPattern = matchesToolPattern;
72575
72608
  exports2.findMatchingPattern = findMatchingPattern;
72576
72609
  var SHELL_OPERATOR_RE = /\s*(?:&&|\|\||[;|])\s*/;
72577
- function matchesToolPattern(command, pattern) {
72610
+ function detectRunnerPrefix(segmentTokens, runners) {
72611
+ let longestMatch = 0;
72612
+ for (const runner of runners) {
72613
+ if (typeof runner.prefix !== "string")
72614
+ continue;
72615
+ const prefixTokens = runner.prefix.trim().split(/\s+/);
72616
+ if (prefixTokens.length === 0)
72617
+ continue;
72618
+ if (prefixTokens.length > segmentTokens.length)
72619
+ continue;
72620
+ let matches = true;
72621
+ for (let i = 0; i < prefixTokens.length; i++) {
72622
+ if (segmentTokens[i] !== prefixTokens[i]) {
72623
+ matches = false;
72624
+ break;
72625
+ }
72626
+ }
72627
+ if (matches && prefixTokens.length > longestMatch) {
72628
+ longestMatch = prefixTokens.length;
72629
+ }
72630
+ }
72631
+ return longestMatch;
72632
+ }
72633
+ function matchesToolPattern(command, pattern, runners) {
72578
72634
  if (!command || !pattern)
72579
72635
  return false;
72580
72636
  const segments = command.split(SHELL_OPERATOR_RE);
72581
72637
  const patternTokens = pattern.trim().split(/\s+/).filter(Boolean);
72582
72638
  if (patternTokens.length === 0)
72583
72639
  return false;
72640
+ const activeRunners = runners?.length ? runners : void 0;
72584
72641
  return segments.some((segment) => {
72585
72642
  const cmdTokens = segment.trim().split(/\s+/);
72586
72643
  if (cmdTokens.length === 0 || cmdTokens[0] === "")
72587
72644
  return false;
72645
+ const runnerTokenCount = activeRunners ? detectRunnerPrefix(cmdTokens, activeRunners) : 0;
72646
+ if (runnerTokenCount > 0) {
72647
+ let pi2 = 0;
72648
+ for (let ci = runnerTokenCount; ci < cmdTokens.length && pi2 < patternTokens.length; ci++) {
72649
+ if (patternTokens[pi2] === "*" || patternTokens[pi2] === cmdTokens[ci]) {
72650
+ pi2++;
72651
+ }
72652
+ }
72653
+ return pi2 === patternTokens.length;
72654
+ }
72588
72655
  if (cmdTokens[0] !== patternTokens[0])
72589
72656
  return false;
72590
72657
  let pi = 1;
@@ -72596,11 +72663,11 @@ var require_tool_pattern_matcher = __commonJS({
72596
72663
  return pi === patternTokens.length;
72597
72664
  });
72598
72665
  }
72599
- function findMatchingPattern(command, patterns) {
72666
+ function findMatchingPattern(command, patterns, runners) {
72600
72667
  for (const pattern of patterns) {
72601
72668
  if (pattern.tool === null)
72602
72669
  continue;
72603
- if (matchesToolPattern(command, pattern.tool))
72670
+ if (matchesToolPattern(command, pattern.tool, runners))
72604
72671
  return pattern;
72605
72672
  }
72606
72673
  return null;
@@ -72656,13 +72723,14 @@ var require_track_verification_tools = __commonJS({
72656
72723
  const featureConfig = context.config.getFeature("reminders");
72657
72724
  const config = { ...types_js_1.DEFAULT_REMINDERS_SETTINGS, ...featureConfig.settings };
72658
72725
  const verificationTools = config.verification_tools ?? {};
72726
+ const runners = config.command_runners ?? [];
72659
72727
  const remindersState = (0, state_js_1.createRemindersState)(daemonCtx.stateService);
72660
72728
  const stateResult = await remindersState.verificationTools.read(sessionId);
72661
72729
  const toolsState = { ...stateResult.data };
72662
72730
  if (FILE_EDIT_TOOLS.includes(toolName)) {
72663
72731
  await handleFileEdit(event, daemonCtx, sessionId, verificationTools, toolsState, remindersState);
72664
72732
  } else if (toolName === "Bash") {
72665
- await handleBashCommand(event, daemonCtx, sessionId, verificationTools, toolsState, remindersState);
72733
+ await handleBashCommand(event, daemonCtx, sessionId, verificationTools, toolsState, remindersState, runners);
72666
72734
  }
72667
72735
  }
72668
72736
  });
@@ -72784,7 +72852,7 @@ var require_track_verification_tools = __commonJS({
72784
72852
  return;
72785
72853
  await stageToolsForFiles([filePath], daemonCtx, sessionId, verificationTools, toolsState, remindersState);
72786
72854
  }
72787
- async function handleBashCommand(event, daemonCtx, sessionId, verificationTools, toolsState, remindersState) {
72855
+ async function handleBashCommand(event, daemonCtx, sessionId, verificationTools, toolsState, remindersState, runners = []) {
72788
72856
  const command = extractToolInput(event)?.command;
72789
72857
  if (!command)
72790
72858
  return;
@@ -72795,7 +72863,7 @@ var require_track_verification_tools = __commonJS({
72795
72863
  const reminderId = types_js_1.TOOL_REMINDER_MAP[toolName];
72796
72864
  if (!reminderId)
72797
72865
  continue;
72798
- const match = (0, tool_pattern_matcher_js_1.findMatchingPattern)(command, toolConfig.patterns);
72866
+ const match = (0, tool_pattern_matcher_js_1.findMatchingPattern)(command, toolConfig.patterns, runners);
72799
72867
  if (!match)
72800
72868
  continue;
72801
72869
  toolsState[toolName] = {
@@ -73611,6 +73679,12 @@ var require_consumption_handler_factory = __commonJS({
73611
73679
  if (onConsume) {
73612
73680
  await onConsume({ reminder: primary, reader, cliCtx, sessionId });
73613
73681
  }
73682
+ const renderedParts = [];
73683
+ if (response.userMessage)
73684
+ renderedParts.push(response.userMessage);
73685
+ if (response.additionalContext)
73686
+ renderedParts.push(response.additionalContext);
73687
+ const renderedText = renderedParts.length > 0 ? renderedParts.join("\n\n") : void 0;
73614
73688
  (0, core_1.logEvent)(cliCtx.logger, events_js_1.ReminderEvents.reminderConsumed({
73615
73689
  sessionId,
73616
73690
  hook
@@ -73620,6 +73694,7 @@ var require_consumption_handler_factory = __commonJS({
73620
73694
  blocking: response.blocking ?? false,
73621
73695
  priority: primary.priority,
73622
73696
  persistent: primary.persistent,
73697
+ renderedText,
73623
73698
  ...enrichment
73624
73699
  }));
73625
73700
  return { response };
@@ -74942,6 +75017,7 @@ var require_update_summary = __commonJS({
74942
75017
  "use strict";
74943
75018
  Object.defineProperty(exports2, "__esModule", { value: true });
74944
75019
  exports2.buildPersonaContext = void 0;
75020
+ exports2.resetAnalysisGuard = resetAnalysisGuard;
74945
75021
  exports2.interpolateTemplate = interpolateTemplate;
74946
75022
  exports2.updateSessionSummary = updateSessionSummary;
74947
75023
  var core_1 = require_dist4();
@@ -74954,6 +75030,7 @@ var require_update_summary = __commonJS({
74954
75030
  var persona_selection_js_1 = require_persona_selection();
74955
75031
  var DECISION_TITLE_SKIP = "Skip session analysis";
74956
75032
  var DECISION_TITLE_RUN = "Run session analysis";
75033
+ var analysisInFlight = /* @__PURE__ */ new Map();
74957
75034
  var PROMPT_FILE = "prompts/session-summary.prompt.txt";
74958
75035
  var SNARKY_PROMPT_FILE = "prompts/snarky-message.prompt.txt";
74959
75036
  var RESUME_PROMPT_FILE = "prompts/resume-message.prompt.txt";
@@ -74971,6 +75048,9 @@ var require_update_summary = __commonJS({
74971
75048
  Object.defineProperty(exports2, "buildPersonaContext", { enumerable: true, get: function() {
74972
75049
  return persona_utils_js_2.buildPersonaContext;
74973
75050
  } });
75051
+ function resetAnalysisGuard() {
75052
+ analysisInFlight.clear();
75053
+ }
74974
75054
  function interpolateTemplate(template, context) {
74975
75055
  let result = template;
74976
75056
  const conditionalRegex = /\{\{#if\s+(\w+)\}\}([\s\S]*?)\{\{\/if\}\}/g;
@@ -75014,12 +75094,12 @@ var require_update_summary = __commonJS({
75014
75094
  }
75015
75095
  (0, core_1.logEvent)(ctx.logger, types_1.DecisionEvents.decisionRecorded(event.context, {
75016
75096
  decision: "calling",
75017
- reason: "BulkProcessingComplete - analyzing full transcript",
75097
+ reason: "Bulk transcript replay complete \u2014 running catch-up analysis",
75018
75098
  subsystem: "session-summary",
75019
75099
  title: DECISION_TITLE_RUN
75020
75100
  }));
75021
75101
  const countdown2 = await loadCountdownState(summaryState, sessionId);
75022
- void performAnalysis(event, ctx, summaryState, countdown2, "user_prompt_forced");
75102
+ void performAnalysis(event, ctx, summaryState, countdown2, "bulk_replay_complete");
75023
75103
  return;
75024
75104
  }
75025
75105
  const countdown = await loadCountdownState(summaryState, sessionId);
@@ -75034,7 +75114,7 @@ var require_update_summary = __commonJS({
75034
75114
  }
75035
75115
  (0, core_1.logEvent)(ctx.logger, types_1.DecisionEvents.decisionRecorded(event.context, {
75036
75116
  decision: "calling",
75037
- reason: "countdown reached zero after ToolResult",
75117
+ reason: "Prompt countdown reached zero \u2014 running scheduled analysis",
75038
75118
  subsystem: "session-summary",
75039
75119
  title: DECISION_TITLE_RUN
75040
75120
  }));
@@ -75069,6 +75149,17 @@ var require_update_summary = __commonJS({
75069
75149
  }
75070
75150
  async function performAnalysis(event, ctx, summaryState, countdown, reason) {
75071
75151
  const { sessionId } = event.context;
75152
+ if (analysisInFlight.has(sessionId)) {
75153
+ analysisInFlight.set(sessionId, true);
75154
+ (0, core_1.logEvent)(ctx.logger, types_1.DecisionEvents.decisionRecorded(event.context, {
75155
+ decision: "deferred",
75156
+ reason: "Analysis deferred \u2014 will rerun after current analysis completes",
75157
+ subsystem: "session-summary",
75158
+ title: DECISION_TITLE_SKIP
75159
+ }));
75160
+ return;
75161
+ }
75162
+ analysisInFlight.set(sessionId, false);
75072
75163
  try {
75073
75164
  const startTime = Date.now();
75074
75165
  (0, core_1.logEvent)(ctx.logger, events_js_1.SessionSummaryEvents.summaryStart(event.context, {
@@ -75213,6 +75304,13 @@ var require_update_summary = __commonJS({
75213
75304
  reason,
75214
75305
  error: err instanceof Error ? err.message : String(err)
75215
75306
  });
75307
+ } finally {
75308
+ const rerunPending = analysisInFlight.get(sessionId) ?? false;
75309
+ analysisInFlight.delete(sessionId);
75310
+ if (rerunPending) {
75311
+ const freshCountdown = await loadCountdownState(summaryState, sessionId);
75312
+ void performAnalysis(event, ctx, summaryState, freshCountdown, reason);
75313
+ }
75216
75314
  }
75217
75315
  }
75218
75316
  async function loadCurrentSummary(summaryState, sessionId) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@scotthamilton77/sidekick",
3
- "version": "0.1.15",
3
+ "version": "0.1.16",
4
4
  "description": "AI pair programming assistant with personas, session tracking, and contextual nudges",
5
5
  "bin": {
6
6
  "sidekick": "dist/bin.js"