@oh-my-pi/pi-coding-agent 11.8.2 → 11.8.3

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 (122) hide show
  1. package/docs/tui.md +9 -9
  2. package/package.json +7 -7
  3. package/src/cli/file-processor.ts +8 -13
  4. package/src/cli/oclif-help.ts +1 -1
  5. package/src/cli.ts +14 -0
  6. package/src/commit/git/index.ts +16 -16
  7. package/src/config/keybindings.ts +11 -11
  8. package/src/config/model-registry.ts +31 -66
  9. package/src/config/settings.ts +88 -95
  10. package/src/config.ts +2 -2
  11. package/src/cursor.ts +4 -4
  12. package/src/debug/index.ts +28 -28
  13. package/src/discovery/codex.ts +5 -13
  14. package/src/discovery/cursor.ts +2 -7
  15. package/src/exa/mcp-client.ts +2 -2
  16. package/src/exa/websets.ts +2 -2
  17. package/src/export/html/index.ts +3 -3
  18. package/src/export/ttsr.ts +27 -27
  19. package/src/extensibility/custom-tools/loader.ts +9 -9
  20. package/src/extensibility/extensions/runner.ts +64 -64
  21. package/src/extensibility/hooks/runner.ts +46 -46
  22. package/src/extensibility/plugins/manager.ts +49 -49
  23. package/src/index.ts +0 -1
  24. package/src/internal-urls/router.ts +5 -5
  25. package/src/ipy/kernel.ts +61 -57
  26. package/src/lsp/client.ts +1 -1
  27. package/src/lsp/clients/biome-client.ts +2 -2
  28. package/src/lsp/clients/lsp-linter-client.ts +7 -7
  29. package/src/lsp/index.ts +9 -9
  30. package/src/mcp/manager.ts +47 -47
  31. package/src/mcp/tool-bridge.ts +12 -12
  32. package/src/mcp/transports/http.ts +34 -34
  33. package/src/mcp/transports/stdio.ts +47 -47
  34. package/src/modes/components/assistant-message.ts +25 -25
  35. package/src/modes/components/bash-execution.ts +51 -51
  36. package/src/modes/components/bordered-loader.ts +7 -7
  37. package/src/modes/components/branch-summary-message.ts +7 -7
  38. package/src/modes/components/compaction-summary-message.ts +7 -7
  39. package/src/modes/components/countdown-timer.ts +15 -15
  40. package/src/modes/components/custom-editor.ts +22 -22
  41. package/src/modes/components/custom-message.ts +21 -21
  42. package/src/modes/components/dynamic-border.ts +3 -3
  43. package/src/modes/components/extensions/extension-dashboard.ts +72 -72
  44. package/src/modes/components/extensions/extension-list.ts +99 -97
  45. package/src/modes/components/extensions/inspector-panel.ts +26 -26
  46. package/src/modes/components/footer.ts +36 -36
  47. package/src/modes/components/history-search.ts +52 -52
  48. package/src/modes/components/hook-editor.ts +20 -20
  49. package/src/modes/components/hook-input.ts +20 -20
  50. package/src/modes/components/hook-message.ts +22 -22
  51. package/src/modes/components/hook-selector.ts +52 -52
  52. package/src/modes/components/index.ts +0 -1
  53. package/src/modes/components/login-dialog.ts +57 -57
  54. package/src/modes/components/model-selector.ts +173 -173
  55. package/src/modes/components/oauth-selector.ts +45 -45
  56. package/src/modes/components/plugin-settings.ts +52 -52
  57. package/src/modes/components/python-execution.ts +53 -53
  58. package/src/modes/components/queue-mode-selector.ts +7 -7
  59. package/src/modes/components/read-tool-group.ts +23 -23
  60. package/src/modes/components/session-selector.ts +40 -37
  61. package/src/modes/components/settings-selector.ts +80 -80
  62. package/src/modes/components/show-images-selector.ts +7 -7
  63. package/src/modes/components/skill-message.ts +27 -27
  64. package/src/modes/components/status-line-segment-editor.ts +81 -81
  65. package/src/modes/components/status-line.ts +73 -73
  66. package/src/modes/components/theme-selector.ts +11 -11
  67. package/src/modes/components/thinking-selector.ts +7 -7
  68. package/src/modes/components/todo-display.ts +19 -19
  69. package/src/modes/components/todo-reminder.ts +9 -9
  70. package/src/modes/components/tool-execution.ts +204 -196
  71. package/src/modes/components/tree-selector.ts +144 -144
  72. package/src/modes/components/ttsr-notification.ts +17 -17
  73. package/src/modes/components/user-message-selector.ts +18 -18
  74. package/src/modes/components/welcome.ts +10 -10
  75. package/src/modes/controllers/command-controller.ts +0 -7
  76. package/src/modes/controllers/event-controller.ts +23 -23
  77. package/src/modes/controllers/extension-ui-controller.ts +13 -13
  78. package/src/modes/controllers/input-controller.ts +4 -9
  79. package/src/modes/interactive-mode.ts +234 -241
  80. package/src/modes/rpc/rpc-client.ts +77 -77
  81. package/src/modes/rpc/rpc-mode.ts +5 -5
  82. package/src/modes/theme/theme.ts +113 -113
  83. package/src/modes/types.ts +0 -1
  84. package/src/patch/index.ts +45 -45
  85. package/src/prompts/tools/task.md +22 -2
  86. package/src/session/agent-session.ts +463 -476
  87. package/src/session/agent-storage.ts +72 -75
  88. package/src/session/auth-storage.ts +186 -252
  89. package/src/session/history-storage.ts +36 -38
  90. package/src/session/session-manager.ts +300 -299
  91. package/src/session/session-storage.ts +65 -90
  92. package/src/ssh/connection-manager.ts +9 -9
  93. package/src/task/agents.ts +1 -1
  94. package/src/task/executor.ts +2 -2
  95. package/src/task/index.ts +13 -12
  96. package/src/task/subprocess-tool-registry.ts +5 -5
  97. package/src/tools/ask.ts +7 -7
  98. package/src/tools/bash.ts +8 -7
  99. package/src/tools/browser.ts +123 -123
  100. package/src/tools/calculator.ts +46 -46
  101. package/src/tools/context.ts +9 -9
  102. package/src/tools/exit-plan-mode.ts +5 -5
  103. package/src/tools/fetch.ts +5 -5
  104. package/src/tools/find.ts +16 -16
  105. package/src/tools/grep.ts +10 -10
  106. package/src/tools/notebook.ts +6 -6
  107. package/src/tools/output-meta.ts +10 -2
  108. package/src/tools/python.ts +12 -11
  109. package/src/tools/read.ts +17 -17
  110. package/src/tools/ssh.ts +9 -9
  111. package/src/tools/submit-result.ts +13 -13
  112. package/src/tools/todo-write.ts +6 -6
  113. package/src/tools/write.ts +10 -10
  114. package/src/tui/output-block.ts +6 -6
  115. package/src/tui/utils.ts +9 -9
  116. package/src/utils/event-bus.ts +10 -10
  117. package/src/utils/frontmatter.ts +1 -1
  118. package/src/utils/ignore-files.ts +1 -1
  119. package/src/web/search/index.ts +5 -5
  120. package/src/web/search/providers/anthropic.ts +7 -2
  121. package/examples/hooks/snake.ts +0 -342
  122. package/src/modes/components/armin.ts +0 -379
@@ -120,39 +120,39 @@ function setByPath(obj: RawSettings, segments: string[], value: unknown): void {
120
120
  // ═══════════════════════════════════════════════════════════════════════════
121
121
 
122
122
  export class Settings {
123
- private configPath: string | null;
124
- private cwd: string;
125
- private agentDir: string;
126
- private storage: AgentStorage | null = null;
123
+ #configPath: string | null;
124
+ #cwd: string;
125
+ #agentDir: string;
126
+ #storage: AgentStorage | null = null;
127
127
 
128
128
  /** Global settings from config.yml */
129
- private global: RawSettings = {};
129
+ #global: RawSettings = {};
130
130
  /** Project settings from .claude/settings.yml etc */
131
- private project: RawSettings = {};
131
+ #project: RawSettings = {};
132
132
  /** Runtime overrides (not persisted) */
133
- private overrides: RawSettings = {};
133
+ #overrides: RawSettings = {};
134
134
  /** Merged view (global + project + overrides) */
135
- private merged: RawSettings = {};
135
+ #merged: RawSettings = {};
136
136
 
137
137
  /** Paths modified during this session (for partial save) */
138
- private modified = new Set<string>();
138
+ #modified = new Set<string>();
139
139
 
140
140
  /** Pending save (debounced) */
141
- private saveTimer: ReturnType<typeof setTimeout> | null = null;
142
- private savePromise: Promise<void> | null = null;
141
+ #saveTimer?: NodeJS.Timeout;
142
+ #savePromise?: Promise<void>;
143
143
 
144
144
  /** Whether to persist changes */
145
- private persist: boolean;
145
+ #persist: boolean;
146
146
 
147
147
  private constructor(options: SettingsOptions = {}) {
148
- this.cwd = path.normalize(options.cwd ?? process.cwd());
149
- this.agentDir = path.normalize(options.agentDir ?? getAgentDir());
150
- this.configPath = options.inMemory ? null : path.join(this.agentDir, "config.yml");
151
- this.persist = !options.inMemory;
148
+ this.#cwd = path.normalize(options.cwd ?? process.cwd());
149
+ this.#agentDir = path.normalize(options.agentDir ?? getAgentDir());
150
+ this.#configPath = options.inMemory ? null : path.join(this.#agentDir, "config.yml");
151
+ this.#persist = !options.inMemory;
152
152
 
153
153
  if (options.overrides) {
154
154
  for (const [key, value] of Object.entries(options.overrides)) {
155
- setByPath(this.overrides, parsePath(key), value);
155
+ setByPath(this.#overrides, parsePath(key), value);
156
156
  }
157
157
  }
158
158
  }
@@ -169,7 +169,7 @@ export class Settings {
169
169
  if (globalInstancePromise) return globalInstancePromise;
170
170
 
171
171
  const instance = new Settings(options);
172
- const promise = instance.load();
172
+ const promise = instance.#load();
173
173
  globalInstancePromise = promise;
174
174
 
175
175
  return promise.then(
@@ -191,7 +191,7 @@ export class Settings {
191
191
  */
192
192
  static isolated(overrides: Partial<Record<SettingPath, unknown>> = {}): Settings {
193
193
  const instance = new Settings({ inMemory: true, overrides });
194
- instance.rebuildMerged();
194
+ instance.#rebuildMerged();
195
195
  return instance;
196
196
  }
197
197
 
@@ -216,7 +216,7 @@ export class Settings {
216
216
  */
217
217
  get<P extends SettingPath>(path: P): SettingValue<P> {
218
218
  const segments = parsePath(path);
219
- const value = getByPath(this.merged, segments);
219
+ const value = getByPath(this.#merged, segments);
220
220
  if (value !== undefined) {
221
221
  return value as SettingValue<P>;
222
222
  }
@@ -231,10 +231,10 @@ export class Settings {
231
231
  set<P extends SettingPath>(path: P, value: SettingValue<P>): void {
232
232
  const prev = this.get(path);
233
233
  const segments = parsePath(path);
234
- setByPath(this.global, segments, value);
235
- this.modified.add(path);
236
- this.rebuildMerged();
237
- this.queueSave();
234
+ setByPath(this.#global, segments, value);
235
+ this.#modified.add(path);
236
+ this.#rebuildMerged();
237
+ this.#queueSave();
238
238
 
239
239
  // Trigger hook if exists
240
240
  const hook = SETTING_HOOKS[path];
@@ -248,8 +248,8 @@ export class Settings {
248
248
  */
249
249
  override<P extends SettingPath>(path: P, value: SettingValue<P>): void {
250
250
  const segments = parsePath(path);
251
- setByPath(this.overrides, segments, value);
252
- this.rebuildMerged();
251
+ setByPath(this.#overrides, segments, value);
252
+ this.#rebuildMerged();
253
253
  }
254
254
 
255
255
  /**
@@ -257,14 +257,14 @@ export class Settings {
257
257
  */
258
258
  clearOverride(path: SettingPath): void {
259
259
  const segments = parsePath(path);
260
- let current = this.overrides;
260
+ let current = this.#overrides;
261
261
  for (let i = 0; i < segments.length - 1; i++) {
262
262
  const segment = segments[i];
263
263
  if (!(segment in current)) return;
264
264
  current = current[segment] as RawSettings;
265
265
  }
266
266
  delete current[segments[segments.length - 1]];
267
- this.rebuildMerged();
267
+ this.#rebuildMerged();
268
268
  }
269
269
 
270
270
  /**
@@ -272,15 +272,15 @@ export class Settings {
272
272
  * Call before exit to ensure all changes are persisted.
273
273
  */
274
274
  async flush(): Promise<void> {
275
- if (this.saveTimer) {
276
- clearTimeout(this.saveTimer);
277
- this.saveTimer = null;
275
+ if (this.#saveTimer) {
276
+ clearTimeout(this.#saveTimer);
277
+ this.#saveTimer = undefined;
278
278
  }
279
- if (this.savePromise) {
280
- await this.savePromise;
279
+ if (this.#savePromise) {
280
+ await this.#savePromise;
281
281
  }
282
- if (this.modified.size > 0) {
283
- await this.saveNow();
282
+ if (this.#modified.size > 0) {
283
+ await this.#saveNow();
284
284
  }
285
285
  }
286
286
 
@@ -289,19 +289,19 @@ export class Settings {
289
289
  // ─────────────────────────────────────────────────────────────────────────
290
290
 
291
291
  getStorage(): AgentStorage | null {
292
- return this.storage;
292
+ return this.#storage;
293
293
  }
294
294
 
295
295
  getCwd(): string {
296
- return this.cwd;
296
+ return this.#cwd;
297
297
  }
298
298
 
299
299
  getAgentDir(): string {
300
- return this.agentDir;
300
+ return this.#agentDir;
301
301
  }
302
302
 
303
303
  getPlansDirectory(): string {
304
- return path.join(this.agentDir, "plans");
304
+ return path.join(this.#agentDir, "plans");
305
305
  }
306
306
 
307
307
  /**
@@ -312,13 +312,6 @@ export class Settings {
312
312
  return procmgr.getShellConfig(shell);
313
313
  }
314
314
 
315
- /**
316
- * Serialize current settings for passing to subagent workers.
317
- */
318
- serialize(): RawSettings {
319
- return structuredClone(this.merged);
320
- }
321
-
322
315
  /**
323
316
  * Get all settings in a group with full type safety.
324
317
  */
@@ -337,7 +330,7 @@ export class Settings {
337
330
  * Get edit model variants (typed accessor for complex nested config).
338
331
  */
339
332
  getEditModelVariants(): Record<string, "patch" | "replace"> {
340
- const variants = (this.merged.edit as { modelVariants?: Record<string, string> })?.modelVariants ?? {};
333
+ const variants = (this.#merged.edit as { modelVariants?: Record<string, string> })?.modelVariants ?? {};
341
334
  const result: Record<string, "patch" | "replace"> = {};
342
335
  for (const pattern in variants) {
343
336
  const value = variants[pattern];
@@ -359,7 +352,7 @@ export class Settings {
359
352
  */
360
353
  getEditVariantForModel(model: string | undefined): "patch" | "replace" | null {
361
354
  if (!model) return null;
362
- const variants = (this.merged.edit as { modelVariants?: Record<string, string> })?.modelVariants;
355
+ const variants = (this.#merged.edit as { modelVariants?: Record<string, string> })?.modelVariants;
363
356
  if (!variants) return null;
364
357
  const modelLower = model.toLowerCase();
365
358
  for (const pattern in variants) {
@@ -379,7 +372,7 @@ export class Settings {
379
372
  * Get bash interceptor rules (typed accessor for complex array config).
380
373
  */
381
374
  getBashInterceptorRules(): BashInterceptorRule[] {
382
- const patterns = (this.merged.bashInterceptor as { patterns?: unknown[] })?.patterns;
375
+ const patterns = (this.#merged.bashInterceptor as { patterns?: unknown[] })?.patterns;
383
376
  if (!Array.isArray(patterns)) return [];
384
377
 
385
378
  return patterns.filter((p): p is BashInterceptorRule => typeof p === "object" && p !== null && "pattern" in p);
@@ -432,34 +425,34 @@ export class Settings {
432
425
  // Loading
433
426
  // ─────────────────────────────────────────────────────────────────────────
434
427
 
435
- private async load(): Promise<Settings> {
436
- if (this.persist) {
428
+ async #load(): Promise<Settings> {
429
+ if (this.#persist) {
437
430
  // Open storage
438
- this.storage = await AgentStorage.open(getAgentDbPath(this.agentDir));
431
+ this.#storage = await AgentStorage.open(getAgentDbPath(this.#agentDir));
439
432
 
440
433
  // Migrate from legacy formats if needed
441
- await this.migrateFromLegacy();
434
+ await this.#migrateFromLegacy();
442
435
 
443
436
  // Load global settings from config.yml
444
- this.global = await this.loadYaml(this.configPath!);
437
+ this.#global = await this.#loadYaml(this.#configPath!);
445
438
  }
446
439
 
447
440
  // Load project settings
448
- this.project = await this.loadProjectSettings();
441
+ this.#project = await this.#loadProjectSettings();
449
442
 
450
443
  // Build merged view
451
- this.rebuildMerged();
444
+ this.#rebuildMerged();
452
445
  return this;
453
446
  }
454
447
 
455
- private async loadYaml(filePath: string): Promise<RawSettings> {
448
+ async #loadYaml(filePath: string): Promise<RawSettings> {
456
449
  try {
457
450
  const content = await Bun.file(filePath).text();
458
451
  const parsed = YAML.parse(content);
459
452
  if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
460
453
  return {};
461
454
  }
462
- return this.migrateRawSettings(parsed as RawSettings);
455
+ return this.#migrateRawSettings(parsed as RawSettings);
463
456
  } catch (error) {
464
457
  if (isEnoent(error)) return {};
465
458
  logger.warn("Settings: failed to load", { path: filePath, error: String(error) });
@@ -467,27 +460,27 @@ export class Settings {
467
460
  }
468
461
  }
469
462
 
470
- private async loadProjectSettings(): Promise<RawSettings> {
463
+ async #loadProjectSettings(): Promise<RawSettings> {
471
464
  try {
472
- const result = await loadCapability(settingsCapability.id, { cwd: this.cwd });
465
+ const result = await loadCapability(settingsCapability.id, { cwd: this.#cwd });
473
466
  let merged: RawSettings = {};
474
467
  for (const item of result.items as SettingsCapabilityItem[]) {
475
468
  if (item.level === "project") {
476
- merged = this.deepMerge(merged, item.data as RawSettings);
469
+ merged = this.#deepMerge(merged, item.data as RawSettings);
477
470
  }
478
471
  }
479
- return this.migrateRawSettings(merged);
472
+ return this.#migrateRawSettings(merged);
480
473
  } catch {
481
474
  return {};
482
475
  }
483
476
  }
484
477
 
485
- private async migrateFromLegacy(): Promise<void> {
486
- if (!this.configPath) return;
478
+ async #migrateFromLegacy(): Promise<void> {
479
+ if (!this.#configPath) return;
487
480
 
488
481
  // Check if config.yml already exists
489
482
  try {
490
- await Bun.file(this.configPath).text();
483
+ await Bun.file(this.#configPath).text();
491
484
  return; // Already exists, no migration needed
492
485
  } catch (err) {
493
486
  if (!isEnoent(err)) return;
@@ -497,11 +490,11 @@ export class Settings {
497
490
  let migrated = false;
498
491
 
499
492
  // 1. Migrate from settings.json
500
- const settingsJsonPath = path.join(this.agentDir, "settings.json");
493
+ const settingsJsonPath = path.join(this.#agentDir, "settings.json");
501
494
  try {
502
495
  const parsed = JSON.parse(await Bun.file(settingsJsonPath).text());
503
496
  if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
504
- settings = this.deepMerge(settings, this.migrateRawSettings(parsed));
497
+ settings = this.#deepMerge(settings, this.#migrateRawSettings(parsed));
505
498
  migrated = true;
506
499
  try {
507
500
  fs.renameSync(settingsJsonPath, `${settingsJsonPath}.bak`);
@@ -511,9 +504,9 @@ export class Settings {
511
504
 
512
505
  // 2. Migrate from agent.db
513
506
  try {
514
- const dbSettings = this.storage?.getSettings();
507
+ const dbSettings = this.#storage?.getSettings();
515
508
  if (dbSettings) {
516
- settings = this.deepMerge(settings, this.migrateRawSettings(dbSettings as RawSettings));
509
+ settings = this.#deepMerge(settings, this.#migrateRawSettings(dbSettings as RawSettings));
517
510
  migrated = true;
518
511
  }
519
512
  } catch {}
@@ -521,14 +514,14 @@ export class Settings {
521
514
  // 3. Write merged settings
522
515
  if (migrated && Object.keys(settings).length > 0) {
523
516
  try {
524
- await Bun.write(this.configPath, YAML.stringify(settings, null, 2));
525
- logger.debug("Settings: migrated to config.yml", { path: this.configPath });
517
+ await Bun.write(this.#configPath, YAML.stringify(settings, null, 2));
518
+ logger.debug("Settings: migrated to config.yml", { path: this.#configPath });
526
519
  } catch {}
527
520
  }
528
521
  }
529
522
 
530
523
  /** Apply schema migrations to raw settings */
531
- private migrateRawSettings(raw: RawSettings): RawSettings {
524
+ #migrateRawSettings(raw: RawSettings): RawSettings {
532
525
  // queueMode -> steeringMode
533
526
  if ("queueMode" in raw && !("steeringMode" in raw)) {
534
527
  raw.steeringMode = raw.queueMode;
@@ -550,65 +543,65 @@ export class Settings {
550
543
  // Saving
551
544
  // ─────────────────────────────────────────────────────────────────────────
552
545
 
553
- private queueSave(): void {
554
- if (!this.persist || !this.configPath) return;
546
+ #queueSave(): void {
547
+ if (!this.#persist || !this.#configPath) return;
555
548
 
556
549
  // Debounce: wait 100ms for more changes
557
- if (this.saveTimer) {
558
- clearTimeout(this.saveTimer);
550
+ if (this.#saveTimer) {
551
+ clearTimeout(this.#saveTimer);
559
552
  }
560
- this.saveTimer = setTimeout(() => {
561
- this.saveTimer = null;
562
- this.saveNow().catch(err => {
553
+ this.#saveTimer = setTimeout(() => {
554
+ this.#saveTimer = undefined;
555
+ this.#saveNow().catch(err => {
563
556
  logger.warn("Settings: background save failed", { error: String(err) });
564
557
  });
565
558
  }, 100);
566
559
  }
567
560
 
568
- private async saveNow(): Promise<void> {
569
- if (!this.persist || !this.configPath || this.modified.size === 0) return;
561
+ async #saveNow(): Promise<void> {
562
+ if (!this.#persist || !this.#configPath || this.#modified.size === 0) return;
570
563
 
571
- const configPath = this.configPath;
572
- const modifiedPaths = [...this.modified];
573
- this.modified.clear();
564
+ const configPath = this.#configPath;
565
+ const modifiedPaths = [...this.#modified];
566
+ this.#modified.clear();
574
567
 
575
568
  try {
576
569
  await withFileLock(configPath, async () => {
577
570
  // Re-read to preserve external changes
578
- const current = await this.loadYaml(configPath);
571
+ const current = await this.#loadYaml(configPath);
579
572
 
580
573
  // Apply only our modified paths
581
574
  for (const modPath of modifiedPaths) {
582
575
  const segments = parsePath(modPath);
583
- const value = getByPath(this.global, segments);
576
+ const value = getByPath(this.#global, segments);
584
577
  setByPath(current, segments, value);
585
578
  }
586
579
 
587
580
  // Update our global with any external changes we preserved
588
- this.global = current;
589
- await Bun.write(configPath, YAML.stringify(this.global, null, 2));
581
+ this.#global = current;
582
+ await Bun.write(configPath, YAML.stringify(this.#global, null, 2));
590
583
  });
591
584
  } catch (error) {
592
585
  logger.warn("Settings: save failed", { error: String(error) });
593
586
  // Re-add failed paths for retry
594
587
  for (const p of modifiedPaths) {
595
- this.modified.add(p);
588
+ this.#modified.add(p);
596
589
  }
597
590
  }
598
591
 
599
- this.rebuildMerged();
592
+ this.#rebuildMerged();
600
593
  }
601
594
 
602
595
  // ─────────────────────────────────────────────────────────────────────────
603
596
  // Utilities
604
597
  // ─────────────────────────────────────────────────────────────────────────
605
598
 
606
- private rebuildMerged(): void {
607
- this.merged = this.deepMerge(this.deepMerge({}, this.global), this.project);
608
- this.merged = this.deepMerge(this.merged, this.overrides);
599
+ #rebuildMerged(): void {
600
+ this.#merged = this.#deepMerge(this.#deepMerge({}, this.#global), this.#project);
601
+ this.#merged = this.#deepMerge(this.#merged, this.#overrides);
609
602
  }
610
603
 
611
- private deepMerge(base: RawSettings, overrides: RawSettings): RawSettings {
604
+ #deepMerge(base: RawSettings, overrides: RawSettings): RawSettings {
612
605
  const result = { ...base };
613
606
  for (const key of Object.keys(overrides)) {
614
607
  const override = overrides[key];
@@ -624,7 +617,7 @@ export class Settings {
624
617
  baseVal !== null &&
625
618
  !Array.isArray(baseVal)
626
619
  ) {
627
- result[key] = this.deepMerge(baseVal as RawSettings, override as RawSettings);
620
+ result[key] = this.#deepMerge(baseVal as RawSettings, override as RawSettings);
628
621
  } else {
629
622
  result[key] = override;
630
623
  }
package/src/config.ts CHANGED
@@ -156,8 +156,8 @@ export class ConfigFile<T> implements IConfigFile<T> {
156
156
  #auxValidate?: (value: T) => void;
157
157
 
158
158
  constructor(
159
- public readonly id: string,
160
- public readonly schema: TSchema,
159
+ readonly id: string,
160
+ readonly schema: TSchema,
161
161
  configPath: string = path.join(getAgentDir(), `${id}.yml`),
162
162
  ) {
163
163
  this.#basePath = configPath;
package/src/cursor.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { randomUUID } from "node:crypto";
2
- import * as fs from "node:fs/promises";
2
+ import * as fs from "node:fs";
3
3
  import type {
4
4
  AgentEvent,
5
5
  AgentTool,
@@ -98,9 +98,9 @@ async function executeDelete(options: CursorExecBridgeOptions, pathArg: string,
98
98
  let result: AgentToolResult<unknown>;
99
99
 
100
100
  try {
101
- let fileStat: Awaited<ReturnType<typeof fs.stat>> | undefined;
101
+ let fileStat: fs.Stats | undefined;
102
102
  try {
103
- fileStat = await fs.stat(absolutePath);
103
+ fileStat = fs.statSync(absolutePath);
104
104
  } catch {
105
105
  throw new Error(`File not found: ${pathArg}`);
106
106
  }
@@ -108,7 +108,7 @@ async function executeDelete(options: CursorExecBridgeOptions, pathArg: string,
108
108
  throw new Error(`Path is not a file: ${pathArg}`);
109
109
  }
110
110
 
111
- await fs.rm(absolutePath);
111
+ fs.rmSync(absolutePath);
112
112
 
113
113
  const sizeText = fileStat.size ? ` (${fileStat.size} bytes)` : "";
114
114
  const message = `Deleted ${pathArg}${sizeText}`;
@@ -30,7 +30,7 @@ const DEBUG_MENU_ITEMS: SelectItem[] = [
30
30
  * Debug selector component.
31
31
  */
32
32
  export class DebugSelectorComponent extends Container {
33
- private selectList: SelectList;
33
+ #selectList: SelectList;
34
34
 
35
35
  constructor(
36
36
  private ctx: InteractiveModeContext,
@@ -44,55 +44,55 @@ export class DebugSelectorComponent extends Container {
44
44
  this.addChild(new Spacer(1));
45
45
 
46
46
  // Select list
47
- this.selectList = new SelectList(DEBUG_MENU_ITEMS, 7, getSelectListTheme());
47
+ this.#selectList = new SelectList(DEBUG_MENU_ITEMS, 7, getSelectListTheme());
48
48
 
49
- this.selectList.onSelect = item => {
49
+ this.#selectList.onSelect = item => {
50
50
  onDone();
51
- void this.handleSelection(item.value);
51
+ void this.#handleSelection(item.value);
52
52
  };
53
53
 
54
- this.selectList.onCancel = () => {
54
+ this.#selectList.onCancel = () => {
55
55
  onDone();
56
56
  };
57
57
 
58
- this.addChild(this.selectList);
58
+ this.addChild(this.#selectList);
59
59
  this.addChild(new DynamicBorder());
60
60
  }
61
61
 
62
62
  handleInput(keyData: string): void {
63
- this.selectList.handleInput(keyData);
63
+ this.#selectList.handleInput(keyData);
64
64
  }
65
65
 
66
- private async handleSelection(value: string): Promise<void> {
66
+ async #handleSelection(value: string): Promise<void> {
67
67
  switch (value) {
68
68
  case "open-artifacts":
69
- await this.handleOpenArtifacts();
69
+ await this.#handleOpenArtifacts();
70
70
  break;
71
71
  case "performance":
72
- await this.handlePerformanceReport();
72
+ await this.#handlePerformanceReport();
73
73
  break;
74
74
  case "work":
75
- await this.handleWorkReport();
75
+ await this.#handleWorkReport();
76
76
  break;
77
77
  case "dump":
78
- await this.handleDumpReport();
78
+ await this.#handleDumpReport();
79
79
  break;
80
80
  case "memory":
81
- await this.handleMemoryReport();
81
+ await this.#handleMemoryReport();
82
82
  break;
83
83
  case "logs":
84
- await this.handleViewLogs();
84
+ await this.#handleViewLogs();
85
85
  break;
86
86
  case "system":
87
- await this.handleViewSystemInfo();
87
+ await this.#handleViewSystemInfo();
88
88
  break;
89
89
  case "clear-cache":
90
- await this.handleClearCache();
90
+ await this.#handleClearCache();
91
91
  break;
92
92
  }
93
93
  }
94
94
 
95
- private async handlePerformanceReport(): Promise<void> {
95
+ async #handlePerformanceReport(): Promise<void> {
96
96
  // Start profiling
97
97
  let session: ProfilerSession;
98
98
  try {
@@ -146,7 +146,7 @@ export class DebugSelectorComponent extends Container {
146
146
  const workProfile = getWorkProfile(30);
147
147
  const result = await createReportBundle({
148
148
  sessionFile: this.ctx.sessionManager.getSessionFile(),
149
- settings: this.getResolvedSettings(),
149
+ settings: this.#getResolvedSettings(),
150
150
  cpuProfile,
151
151
  workProfile,
152
152
  });
@@ -169,7 +169,7 @@ export class DebugSelectorComponent extends Container {
169
169
  this.ctx.ui.requestRender();
170
170
  }
171
171
 
172
- private async handleWorkReport(): Promise<void> {
172
+ async #handleWorkReport(): Promise<void> {
173
173
  try {
174
174
  const workProfile = getWorkProfile(30);
175
175
 
@@ -202,7 +202,7 @@ export class DebugSelectorComponent extends Container {
202
202
  this.ctx.ui.requestRender();
203
203
  }
204
204
 
205
- private async handleDumpReport(): Promise<void> {
205
+ async #handleDumpReport(): Promise<void> {
206
206
  const loader = new Loader(
207
207
  this.ctx.ui,
208
208
  spinner => theme.fg("accent", spinner),
@@ -216,7 +216,7 @@ export class DebugSelectorComponent extends Container {
216
216
  try {
217
217
  const result = await createReportBundle({
218
218
  sessionFile: this.ctx.sessionManager.getSessionFile(),
219
- settings: this.getResolvedSettings(),
219
+ settings: this.#getResolvedSettings(),
220
220
  });
221
221
 
222
222
  loader.stop();
@@ -237,7 +237,7 @@ export class DebugSelectorComponent extends Container {
237
237
  this.ctx.ui.requestRender();
238
238
  }
239
239
 
240
- private async handleMemoryReport(): Promise<void> {
240
+ async #handleMemoryReport(): Promise<void> {
241
241
  const loader = new Loader(
242
242
  this.ctx.ui,
243
243
  spinner => theme.fg("accent", spinner),
@@ -254,7 +254,7 @@ export class DebugSelectorComponent extends Container {
254
254
 
255
255
  const result = await createReportBundle({
256
256
  sessionFile: this.ctx.sessionManager.getSessionFile(),
257
- settings: this.getResolvedSettings(),
257
+ settings: this.#getResolvedSettings(),
258
258
  heapSnapshot,
259
259
  });
260
260
 
@@ -276,7 +276,7 @@ export class DebugSelectorComponent extends Container {
276
276
  this.ctx.ui.requestRender();
277
277
  }
278
278
 
279
- private async handleViewLogs(): Promise<void> {
279
+ async #handleViewLogs(): Promise<void> {
280
280
  try {
281
281
  const logs = await getRecentLogs(50);
282
282
  if (!logs) {
@@ -305,7 +305,7 @@ export class DebugSelectorComponent extends Container {
305
305
  this.ctx.ui.requestRender();
306
306
  }
307
307
 
308
- private async handleViewSystemInfo(): Promise<void> {
308
+ async #handleViewSystemInfo(): Promise<void> {
309
309
  try {
310
310
  const info = await collectSystemInfo();
311
311
  const formatted = formatSystemInfo(info);
@@ -321,7 +321,7 @@ export class DebugSelectorComponent extends Container {
321
321
  this.ctx.ui.requestRender();
322
322
  }
323
323
 
324
- private async handleOpenArtifacts(): Promise<void> {
324
+ async #handleOpenArtifacts(): Promise<void> {
325
325
  const sessionFile = this.ctx.sessionManager.getSessionFile();
326
326
  if (!sessionFile) {
327
327
  this.ctx.showWarning("No active session file.");
@@ -357,7 +357,7 @@ export class DebugSelectorComponent extends Container {
357
357
  }
358
358
  }
359
359
 
360
- private async handleClearCache(): Promise<void> {
360
+ async #handleClearCache(): Promise<void> {
361
361
  const sessionsDir = getSessionsDir();
362
362
 
363
363
  // Get stats first
@@ -416,7 +416,7 @@ export class DebugSelectorComponent extends Container {
416
416
  this.ctx.ui.requestRender();
417
417
  }
418
418
 
419
- private getResolvedSettings(): Record<string, unknown> {
419
+ #getResolvedSettings(): Record<string, unknown> {
420
420
  // Extract key settings for the report
421
421
  return {
422
422
  model: this.ctx.session.model?.id,