discoclaw 1.1.3 → 1.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.
@@ -226,6 +226,10 @@ export async function executeCronJob(job, ctx) {
226
226
  job.running = true;
227
227
  activeCronRunKeys.add(runKey);
228
228
  ctx.runControl?.register(job.id, requestCancel);
229
+ // Pre-fetch the silent flag before the try block so the catch block can gate
230
+ // channel error posts. Without this, errors (e.g. shell timeouts) spam the
231
+ // channel even when the cron is configured as silent.
232
+ const isSilent = Boolean(ctx.statsStore && job.cronId && ctx.statsStore.getRecord(job.cronId)?.silent);
229
233
  try {
230
234
  // Best-effort: write running status to persistent store before execution begins.
231
235
  if (ctx.statsStore && job.cronId) {
@@ -603,7 +607,7 @@ export async function executeCronJob(job, ctx) {
603
607
  metrics.increment('cron.run.error');
604
608
  ctx.log?.error({ err, jobId: job.id }, 'cron:exec failed');
605
609
  await ctx.status?.runtimeError({ sessionKey: `cron:${job.id}`, channelName: job.def.channel }, `Cron "${job.name}": ${msg}`);
606
- if (ctx.client) {
610
+ if (!isSilent && ctx.client) {
607
611
  const guild = ctx.client.guilds.cache.get(job.guildId);
608
612
  const targetChannel = guild ? resolveChannel(guild, job.def.channel) : null;
609
613
  if (targetChannel) {
@@ -1144,9 +1144,10 @@ describe('executeCronJob shell input mode', () => {
1144
1144
  expect(status.runtimeError).toHaveBeenCalledOnce();
1145
1145
  expect(statsStore.getRecord('cron-test0001')?.lastRunStatus).toBe('error');
1146
1146
  expect(statsStore.getRecord('cron-test0001')?.lastErrorMessage).toContain('code 23');
1147
+ // Silent crons should NOT post errors to the channel (only log + stats).
1147
1148
  const guild = ctx.client.guilds.cache.get('guild-1');
1148
1149
  const channel = guild.channels.cache.get('general');
1149
- expect(channel.send).toHaveBeenCalled();
1150
+ expect(channel.send).not.toHaveBeenCalled();
1150
1151
  });
1151
1152
  it('suppresses posting when the AI returns a single valid structured no-post block', async () => {
1152
1153
  const statsPath = path.join(statsDir, 'stats.json');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "discoclaw",
3
- "version": "1.1.3",
3
+ "version": "1.1.5",
4
4
  "description": "Personal AI orchestrator that turns Discord into a persistent workspace",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -19,6 +19,10 @@ Before claiming insufficient info: check workspace files → durable memory →
19
19
 
20
20
  Use tools immediately — Read/Bash/Grep — don't narrate plans. CWD is the workspace dir; code lives in `~/code/discoclaw`. Don't defer what you can do now. Task status updates are coordination, not investigation.
21
21
 
22
+ ## Check Before Creating
23
+
24
+ Before implementing requested functionality, search the codebase to confirm it doesn't already exist. Re-read the actual file content rather than relying on what you recall from earlier in the session — your memory of what you read vs. what you generated can drift.
25
+
22
26
  ## Runtime Registry
23
27
 
24
28
  | Key | Type | Backend |