claude-ide-bridge 2.25.11 → 2.25.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,10 @@ export interface PromptSource {
18
18
  promptName?: string;
19
19
  promptArgs?: Record<string, string>;
20
20
  condition?: string;
21
+ /** Per-hook model override. Falls back to policy.defaultModel (Haiku). */
22
+ model?: string;
23
+ /** Per-hook effort override. Falls back to policy.defaultEffort ("low"). */
24
+ effort?: "low" | "medium" | "high" | "max";
21
25
  }
22
26
  export interface OnDiagnosticsErrorPolicy extends PromptSource {
23
27
  enabled: boolean;
@@ -249,6 +253,19 @@ export interface AutomationPolicy {
249
253
  * Defaults to 20. Set to 0 to disable.
250
254
  */
251
255
  maxTasksPerHour?: number;
256
+ /**
257
+ * Custom system prompt passed via --system-prompt to every automation subprocess.
258
+ * Replaces the default Claude Code system prompt, preventing CLAUDE.md from being
259
+ * loaded as workspace instructions. Keep it short — this is the biggest token lever.
260
+ * Default: a lean "be brief" prompt. Max 4096 chars.
261
+ */
262
+ automationSystemPrompt?: string;
263
+ /**
264
+ * Default effort level for all automation tasks (low/medium/high/max).
265
+ * Defaults to "low" — automation tasks rarely need deep reasoning.
266
+ * Override per-hook via the hook's own effort field.
267
+ */
268
+ defaultEffort?: "low" | "medium" | "high" | "max";
252
269
  onDiagnosticsError?: OnDiagnosticsErrorPolicy;
253
270
  onFileSave?: OnFileSavePolicy;
254
271
  /** Fired by Claude Code 2.1.83+ FileChanged hook — reacts to any file edit, not just explicit saves. */
@@ -341,6 +358,10 @@ export declare class AutomationHooks {
341
358
  private prevDiagnosticErrors;
342
359
  /** Active task ID for the task-success handler (workspace-global). */
343
360
  private activeTaskSuccessTaskId;
361
+ /** Active task ID for the post-compact handler (workspace-global). */
362
+ private activePostCompactTaskId;
363
+ /** Active task ID for the instructions-loaded handler (workspace-global). */
364
+ private activeInstructionsLoadedTaskId;
344
365
  /**
345
366
  * Rolling window of task enqueue timestamps for maxTasksPerHour enforcement.
346
367
  * Entries older than 60 minutes are pruned on each enqueue.
@@ -498,5 +519,7 @@ export declare class AutomationHooks {
498
519
  defaultModel: string;
499
520
  maxTasksPerHour: number;
500
521
  tasksThisHour: number;
522
+ defaultEffort: string;
523
+ automationSystemPrompt: string;
501
524
  };
502
525
  }
@@ -5,6 +5,7 @@ import { minimatch } from "minimatch";
5
5
  import { getPrompt } from "./prompts.js";
6
6
  /** Maximum length (chars) of a single diagnostic message before truncation */
7
7
  const MAX_DIAGNOSTIC_MSG_CHARS = 500;
8
+ const MAX_DIAGNOSTICS_IN_PROMPT = 20;
8
9
  /**
9
10
  * Wrap an untrusted user-controlled value in delimiters that include a
10
11
  * per-trigger nonce so a crafted value cannot forge a closing delimiter.
@@ -50,6 +51,10 @@ function truncatePrompt(prompt) {
50
51
  }
51
52
  /** Prune lastTrigger entries older than this to prevent unbounded Map growth */
52
53
  const LAST_TRIGGER_MAX_AGE_MS = 7 * 24 * 60 * 60 * 1000; // 1 week
54
+ /** Default system prompt for automation subprocesses when none is set in policy. */
55
+ const DEFAULT_AUTOMATION_SYSTEM_PROMPT = "You are a concise automation assistant. " +
56
+ "Respond in \u22645 lines. No preamble. No markdown headers. " +
57
+ "Call the tools listed in the task prompt, then report results only.";
53
58
  /**
54
59
  * Deterministic signature over a diagnostic list — used for content-aware
55
60
  * dedupe in onDiagnosticsError. Sorting by a stable key makes the signature
@@ -104,6 +109,16 @@ function validatePromptSource(hookName, cfg) {
104
109
  throw new Error(`"${hookName}.condition" must be a string ≤ 1024 characters`);
105
110
  }
106
111
  }
112
+ if (cfg.model !== undefined) {
113
+ if (typeof cfg.model !== "string" || cfg.model.trim() === "") {
114
+ throw new Error(`"${hookName}.model" must be a non-empty string`);
115
+ }
116
+ }
117
+ if (cfg.effort !== undefined) {
118
+ if (!["low", "medium", "high", "max"].includes(cfg.effort)) {
119
+ throw new Error(`"${hookName}.effort" must be one of "low", "medium", "high", "max"`);
120
+ }
121
+ }
107
122
  }
108
123
  /** Load and validate a JSON automation policy file. Throws on any failure. */
109
124
  export function loadPolicy(filePath) {
@@ -137,6 +152,19 @@ export function loadPolicy(filePath) {
137
152
  throw new Error(`"maxTasksPerHour" must be a non-negative integer`);
138
153
  }
139
154
  }
155
+ if (policy.automationSystemPrompt !== undefined) {
156
+ if (typeof policy.automationSystemPrompt !== "string") {
157
+ throw new Error(`"automationSystemPrompt" must be a string`);
158
+ }
159
+ if (policy.automationSystemPrompt.length > 4096) {
160
+ throw new Error(`"automationSystemPrompt" must be ≤ 4096 characters`);
161
+ }
162
+ }
163
+ if (policy.defaultEffort !== undefined) {
164
+ if (!["low", "medium", "high", "max"].includes(policy.defaultEffort)) {
165
+ throw new Error(`"defaultEffort" must be one of "low", "medium", "high", "max"`);
166
+ }
167
+ }
140
168
  // Validate onDiagnosticsError
141
169
  if (policy.onDiagnosticsError !== undefined) {
142
170
  const d = policy.onDiagnosticsError;
@@ -550,6 +578,10 @@ export class AutomationHooks {
550
578
  prevDiagnosticErrors = new Map();
551
579
  /** Active task ID for the task-success handler (workspace-global). */
552
580
  activeTaskSuccessTaskId = null;
581
+ /** Active task ID for the post-compact handler (workspace-global). */
582
+ activePostCompactTaskId = null;
583
+ /** Active task ID for the instructions-loaded handler (workspace-global). */
584
+ activeInstructionsLoadedTaskId = null;
553
585
  /**
554
586
  * Rolling window of task enqueue timestamps for maxTasksPerHour enforcement.
555
587
  * Entries older than 60 minutes are pruned on each enqueue.
@@ -584,13 +616,19 @@ export class AutomationHooks {
584
616
  }
585
617
  this.taskTimestamps.push(now);
586
618
  }
587
- const model = this.policy.defaultModel ?? "claude-haiku-4-5-20251001";
619
+ const model = opts.hookCfg?.model ??
620
+ this.policy.defaultModel ??
621
+ "claude-haiku-4-5-20251001";
622
+ const effort = opts.hookCfg?.effort ?? this.policy.defaultEffort ?? "low";
623
+ const systemPrompt = this.policy.automationSystemPrompt ?? DEFAULT_AUTOMATION_SYSTEM_PROMPT;
588
624
  return this.orchestrator.enqueue({
589
625
  prompt: opts.prompt,
590
626
  sessionId: "",
591
627
  isAutomationTask: true,
592
628
  triggerSource: opts.triggerSource,
593
629
  model,
630
+ effort,
631
+ systemPrompt,
594
632
  });
595
633
  }
596
634
  /**
@@ -731,9 +769,12 @@ export class AutomationHooks {
731
769
  prompt = resolved;
732
770
  }
733
771
  else {
734
- const diagnosticsText = matching
772
+ const displayMatching = matching.slice(0, MAX_DIAGNOSTICS_IN_PROMPT);
773
+ const omittedCount = matching.length - displayMatching.length;
774
+ const diagnosticsText = displayMatching
735
775
  .map((d) => `[${d.severity}] ${d.message.slice(0, MAX_DIAGNOSTIC_MSG_CHARS)}`)
736
- .join("\n");
776
+ .join("\n") +
777
+ (omittedCount > 0 ? `\n… and ${omittedCount} more` : "");
737
778
  const nonce = crypto.randomBytes(6).toString("hex");
738
779
  prompt =
739
780
  (cfg.prompt ?? "")
@@ -745,6 +786,7 @@ export class AutomationHooks {
745
786
  const taskId = this._enqueueAutomationTask({
746
787
  prompt,
747
788
  triggerSource: "onDiagnosticsError",
789
+ hookCfg: cfg,
748
790
  });
749
791
  this.lastTrigger.set(key, now);
750
792
  this.activeDiagnosticsTasks.set(normalizedFile, taskId);
@@ -802,6 +844,7 @@ export class AutomationHooks {
802
844
  const taskId = this._enqueueAutomationTask({
803
845
  prompt,
804
846
  triggerSource: "onCwdChanged",
847
+ hookCfg: cfg,
805
848
  });
806
849
  this.lastTrigger.set(key, now);
807
850
  this.log(`[automation] triggered cwd-changed task ${taskId.slice(0, 8)} for ${newCwd}`);
@@ -818,6 +861,15 @@ export class AutomationHooks {
818
861
  const cfg = this.policy.onPostCompact;
819
862
  if (!cfg?.enabled)
820
863
  return;
864
+ if (this.activePostCompactTaskId) {
865
+ const existing = this.orchestrator.getTask(this.activePostCompactTaskId);
866
+ if (existing &&
867
+ (existing.status === "pending" || existing.status === "running")) {
868
+ this.log(`[automation] skipping post-compact trigger — task ${this.activePostCompactTaskId.slice(0, 8)} still active`);
869
+ return;
870
+ }
871
+ this.activePostCompactTaskId = null;
872
+ }
821
873
  const key = "post-compact";
822
874
  const now = Date.now();
823
875
  const last = this.lastTrigger.get(key) ?? 0;
@@ -841,10 +893,12 @@ export class AutomationHooks {
841
893
  const taskId = this._enqueueAutomationTask({
842
894
  prompt: postCompactPrompt,
843
895
  triggerSource: "onPostCompact",
896
+ hookCfg: cfg,
844
897
  });
845
898
  // Set lastTrigger AFTER successful enqueue so a failed enqueue does not
846
899
  // impose a spurious cooldown on the next trigger attempt.
847
900
  this.lastTrigger.set(key, now);
901
+ this.activePostCompactTaskId = taskId;
848
902
  this.log(`[automation] triggered PostCompact task ${taskId.slice(0, 8)}`);
849
903
  }
850
904
  catch (err) {
@@ -859,6 +913,15 @@ export class AutomationHooks {
859
913
  const cfg = this.policy.onInstructionsLoaded;
860
914
  if (!cfg?.enabled)
861
915
  return;
916
+ if (this.activeInstructionsLoadedTaskId) {
917
+ const existing = this.orchestrator.getTask(this.activeInstructionsLoadedTaskId);
918
+ if (existing &&
919
+ (existing.status === "pending" || existing.status === "running")) {
920
+ this.log(`[automation] skipping instructions-loaded trigger — task ${this.activeInstructionsLoadedTaskId.slice(0, 8)} still active`);
921
+ return;
922
+ }
923
+ this.activeInstructionsLoadedTaskId = null;
924
+ }
862
925
  const cooldownMs = cfg.cooldownMs ?? 60_000;
863
926
  const key = "instructions-loaded";
864
927
  const now = Date.now();
@@ -883,8 +946,10 @@ export class AutomationHooks {
883
946
  const taskId = this._enqueueAutomationTask({
884
947
  prompt: instrPrompt,
885
948
  triggerSource: "onInstructionsLoaded",
949
+ hookCfg: cfg,
886
950
  });
887
951
  this.lastTrigger.set(key, now);
952
+ this.activeInstructionsLoadedTaskId = taskId;
888
953
  this.log(`[automation] triggered InstructionsLoaded task ${taskId.slice(0, 8)}`);
889
954
  }
890
955
  catch (err) {
@@ -951,6 +1016,7 @@ export class AutomationHooks {
951
1016
  const taskId = this._enqueueAutomationTask({
952
1017
  prompt,
953
1018
  triggerSource: "onFileSave",
1019
+ hookCfg: cfg,
954
1020
  });
955
1021
  this.lastTrigger.set(key, now);
956
1022
  this.activeSaveTasks.set(normalizedFile, taskId);
@@ -1023,6 +1089,7 @@ export class AutomationHooks {
1023
1089
  const taskId = this._enqueueAutomationTask({
1024
1090
  prompt,
1025
1091
  triggerSource: "onFileChanged",
1092
+ hookCfg: cfg,
1026
1093
  });
1027
1094
  this.lastTrigger.set(key, now);
1028
1095
  this.activeFileChangedTasks.set(normalizedFile, taskId);
@@ -1114,6 +1181,7 @@ export class AutomationHooks {
1114
1181
  const taskId = this._enqueueAutomationTask({
1115
1182
  prompt,
1116
1183
  triggerSource: "onTestRun",
1184
+ hookCfg: cfg,
1117
1185
  });
1118
1186
  this.lastTrigger.set(key, now);
1119
1187
  this.activeTestRunTaskId = taskId;
@@ -1201,7 +1269,7 @@ export class AutomationHooks {
1201
1269
  .replace(/\{\{hash\}\}/g, untrustedBlock("COMMIT HASH", safeHash, nonce))
1202
1270
  .replace(/\{\{branch\}\}/g, untrustedBlock("BRANCH", safeBranchCommit, nonce))
1203
1271
  .replace(/\{\{message\}\}/g, untrustedBlock("COMMIT MESSAGE", safeMessage, nonce))
1204
- .replace(/\{\{count\}\}/g, String(result.count))
1272
+ .replace(/\{\{count\}\}/g, untrustedBlock("COMMIT COUNT", String(result.count), nonce))
1205
1273
  .replace(/\{\{files\}\}/g, untrustedBlock("COMMITTED FILES", filesText, nonce))
1206
1274
  .replace(/\{\{changeImpact\}\}/g, changeImpact
1207
1275
  ? untrustedBlock("CHANGE IMPACT", changeImpact, nonce)
@@ -1212,6 +1280,7 @@ export class AutomationHooks {
1212
1280
  const taskId = this._enqueueAutomationTask({
1213
1281
  prompt,
1214
1282
  triggerSource: "onGitCommit",
1283
+ hookCfg: cfg,
1215
1284
  });
1216
1285
  this.lastTrigger.set(key, now);
1217
1286
  this.activeGitCommitTaskId = taskId;
@@ -1273,6 +1342,7 @@ export class AutomationHooks {
1273
1342
  const taskId = this._enqueueAutomationTask({
1274
1343
  prompt,
1275
1344
  triggerSource: "onGitPush",
1345
+ hookCfg: cfg,
1276
1346
  });
1277
1347
  this.lastTrigger.set(key, now);
1278
1348
  this.activeGitPushTaskId = taskId;
@@ -1331,6 +1401,7 @@ export class AutomationHooks {
1331
1401
  const taskId = this._enqueueAutomationTask({
1332
1402
  prompt,
1333
1403
  triggerSource: "onGitPull",
1404
+ hookCfg: cfg,
1334
1405
  });
1335
1406
  this.lastTrigger.set(key, now);
1336
1407
  this.activeGitPullTaskId = taskId;
@@ -1395,6 +1466,7 @@ export class AutomationHooks {
1395
1466
  const taskId = this._enqueueAutomationTask({
1396
1467
  prompt,
1397
1468
  triggerSource: "onBranchCheckout",
1469
+ hookCfg: cfg,
1398
1470
  });
1399
1471
  this.lastTrigger.set(key, now);
1400
1472
  this.activeBranchCheckoutTaskId = taskId;
@@ -1461,6 +1533,7 @@ export class AutomationHooks {
1461
1533
  const taskId = this._enqueueAutomationTask({
1462
1534
  prompt,
1463
1535
  triggerSource: "onPullRequest",
1536
+ hookCfg: cfg,
1464
1537
  });
1465
1538
  this.lastTrigger.set(key, now);
1466
1539
  this.activePullRequestTaskId = taskId;
@@ -1513,6 +1586,7 @@ export class AutomationHooks {
1513
1586
  const taskId = this._enqueueAutomationTask({
1514
1587
  prompt,
1515
1588
  triggerSource: "onTaskCreated",
1589
+ hookCfg: cfg,
1516
1590
  });
1517
1591
  this.lastTrigger.set(key, now);
1518
1592
  this.activeTaskCreatedTaskId = taskId;
@@ -1566,6 +1640,7 @@ export class AutomationHooks {
1566
1640
  const taskId = this._enqueueAutomationTask({
1567
1641
  prompt,
1568
1642
  triggerSource: "onPermissionDenied",
1643
+ hookCfg: cfg,
1569
1644
  });
1570
1645
  this.lastTrigger.set(key, now);
1571
1646
  this.activePermissionDeniedTaskId = taskId;
@@ -1622,6 +1697,7 @@ export class AutomationHooks {
1622
1697
  const taskId = this._enqueueAutomationTask({
1623
1698
  prompt,
1624
1699
  triggerSource: "onDiagnosticsCleared",
1700
+ hookCfg: cfg,
1625
1701
  });
1626
1702
  this.lastTrigger.set(key, now);
1627
1703
  this.activeDiagnosticsClearedTasks.set(normalizedFile, taskId);
@@ -1679,6 +1755,7 @@ export class AutomationHooks {
1679
1755
  const taskId = this._enqueueAutomationTask({
1680
1756
  prompt,
1681
1757
  triggerSource: "onTaskSuccess",
1758
+ hookCfg: cfg,
1682
1759
  });
1683
1760
  this.lastTrigger.set(key, now);
1684
1761
  this.activeTaskSuccessTaskId = taskId;
@@ -1797,6 +1874,8 @@ export class AutomationHooks {
1797
1874
  defaultModel: p.defaultModel ?? "claude-haiku-4-5-20251001",
1798
1875
  maxTasksPerHour: p.maxTasksPerHour ?? 20,
1799
1876
  tasksThisHour: this.taskTimestamps.filter((t) => t >= Date.now() - 60 * 60 * 1_000).length,
1877
+ defaultEffort: p.defaultEffort ?? "low",
1878
+ automationSystemPrompt: (p.automationSystemPrompt ?? DEFAULT_AUTOMATION_SYSTEM_PROMPT).slice(0, 80),
1800
1879
  };
1801
1880
  }
1802
1881
  }