@gowelle/stint-agent 1.2.41 → 1.2.43

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.
@@ -2,10 +2,10 @@ import {
2
2
  gitService,
3
3
  projectService,
4
4
  validatePidFile
5
- } from "./chunk-YLZKGUKM.js";
5
+ } from "./chunk-IYPRVBYG.js";
6
6
  import {
7
7
  authService
8
- } from "./chunk-IJAVMAOZ.js";
8
+ } from "./chunk-H7MANPCF.js";
9
9
 
10
10
  // src/components/StatusDashboard.tsx
11
11
  import { useState, useEffect } from "react";
@@ -0,0 +1,7 @@
1
+ import {
2
+ apiService
3
+ } from "./chunk-XWCVS4R6.js";
4
+ import "./chunk-H7MANPCF.js";
5
+ export {
6
+ apiService
7
+ };
@@ -346,7 +346,7 @@ var AuthServiceImpl = class {
346
346
  return null;
347
347
  }
348
348
  try {
349
- const { apiService } = await import("./api-QDJ4MDEI.js");
349
+ const { apiService } = await import("./api-7EO4NO7A.js");
350
350
  const user = await apiService.getCurrentUser();
351
351
  logger.info("auth", `Token validated for user: ${user.email}`);
352
352
  return user;
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  config,
3
3
  logger
4
- } from "./chunk-IJAVMAOZ.js";
4
+ } from "./chunk-H7MANPCF.js";
5
5
 
6
6
  // src/services/git.ts
7
7
  import simpleGit from "simple-git";
@@ -1,15 +1,15 @@
1
1
  import {
2
2
  apiService
3
- } from "./chunk-H4PLSM7I.js";
3
+ } from "./chunk-XWCVS4R6.js";
4
4
  import {
5
5
  gitService,
6
6
  projectService
7
- } from "./chunk-YLZKGUKM.js";
7
+ } from "./chunk-IYPRVBYG.js";
8
8
  import {
9
9
  authService,
10
10
  config,
11
11
  logger
12
- } from "./chunk-IJAVMAOZ.js";
12
+ } from "./chunk-H7MANPCF.js";
13
13
 
14
14
  // src/utils/notify.ts
15
15
  import notifier from "node-notifier";
@@ -344,7 +344,14 @@ var CommitQueueProcessor = class {
344
344
  if (!isRepo) {
345
345
  throw new Error(`Directory ${projectPath} is not a git repository`);
346
346
  }
347
+ let hookModifiedFiles = [];
347
348
  if (project.commitSettings?.hooks?.length) {
349
+ const statusBeforeHooks = await gitService.getStatus(projectPath);
350
+ const filesBeforeHooks = /* @__PURE__ */ new Set([
351
+ ...statusBeforeHooks.staged,
352
+ ...statusBeforeHooks.unstaged,
353
+ ...statusBeforeHooks.untracked
354
+ ]);
348
355
  onProgress?.("Running pre-commit hooks...");
349
356
  logger.info("queue", "Executing pre-commit hooks...");
350
357
  streamer.append("\n> Executing pre-commit hooks...\n");
@@ -384,6 +391,22 @@ ${failedBlockingHook.output}`);
384
391
  streamer.append(
385
392
  `
386
393
  Warning: ${failedNonBlockingHooks.length} non-blocking hooks failed.
394
+ `
395
+ );
396
+ }
397
+ const statusAfterHooks = await gitService.getStatus(projectPath);
398
+ hookModifiedFiles = [
399
+ ...statusAfterHooks.unstaged,
400
+ ...statusAfterHooks.untracked
401
+ ].filter((f) => !filesBeforeHooks.has(f));
402
+ if (hookModifiedFiles.length > 0) {
403
+ logger.info(
404
+ "queue",
405
+ `Hooks modified ${hookModifiedFiles.length} additional files: ${hookModifiedFiles.join(", ")}`
406
+ );
407
+ streamer.append(
408
+ `
409
+ > Hooks modified ${hookModifiedFiles.length} additional files: ${hookModifiedFiles.join(", ")}
387
410
  `
388
411
  );
389
412
  }
@@ -391,13 +414,20 @@ Warning: ${failedNonBlockingHooks.length} non-blocking hooks failed.
391
414
  onProgress?.("Checking repository status...");
392
415
  let status = await gitService.getStatus(projectPath);
393
416
  if (commit.files && commit.files.length > 0) {
394
- onProgress?.(`Staging ${commit.files.length} specified files...`);
395
- streamer.append(`
396
- > Staging ${commit.files.length} files...
397
- `);
398
- await gitService.stageFiles(projectPath, commit.files, streamOutput);
417
+ const filesToStage = [
418
+ .../* @__PURE__ */ new Set([...commit.files, ...hookModifiedFiles])
419
+ ];
420
+ const extraFiles = filesToStage.length - commit.files.length;
421
+ const extraMsg = extraFiles > 0 ? ` (+${extraFiles} from hooks)` : "";
422
+ onProgress?.(`Staging ${filesToStage.length} files${extraMsg}...`);
423
+ streamer.append(
424
+ `
425
+ > Staging ${filesToStage.length} files${extraMsg}...
426
+ `
427
+ );
428
+ await gitService.stageFiles(projectPath, filesToStage, streamOutput);
399
429
  status = await gitService.getStatus(projectPath);
400
- logger.info("queue", `Auto-staged files: ${commit.files.join(", ")}`);
430
+ logger.info("queue", `Auto-staged files: ${filesToStage.join(", ")}`);
401
431
  } else if (status.staged.length === 0) {
402
432
  const hasChanges = status.unstaged.length > 0 || status.untracked.length > 0;
403
433
  if (hasChanges) {
@@ -911,7 +941,7 @@ var WebSocketServiceImpl = class {
911
941
  "websocket",
912
942
  `Commit ${commit.id} marked as large, fetching full details...`
913
943
  );
914
- const { apiService: apiService2 } = await import("./api-QDJ4MDEI.js");
944
+ const { apiService: apiService2 } = await import("./api-7EO4NO7A.js");
915
945
  const fullCommit = await apiService2.getCommit(commit.id);
916
946
  commit = {
917
947
  ...commit,
@@ -2,7 +2,7 @@ import {
2
2
  authService,
3
3
  config,
4
4
  logger
5
- } from "./chunk-IJAVMAOZ.js";
5
+ } from "./chunk-H7MANPCF.js";
6
6
 
7
7
  // src/utils/circuit-breaker.ts
8
8
  var CircuitBreaker = class {
@@ -100,7 +100,7 @@ var CircuitBreaker = class {
100
100
  };
101
101
 
102
102
  // src/services/api.ts
103
- var AGENT_VERSION = "1.2.41";
103
+ var AGENT_VERSION = "1.2.43";
104
104
  var ApiServiceImpl = class {
105
105
  sessionId = null;
106
106
  circuitBreaker = new CircuitBreaker({
@@ -3,20 +3,20 @@ import {
3
3
  commitQueue,
4
4
  notify,
5
5
  websocketService
6
- } from "../chunk-NTG3X2HK.js";
6
+ } from "../chunk-KGDXQOLB.js";
7
7
  import {
8
8
  apiService
9
- } from "../chunk-H4PLSM7I.js";
9
+ } from "../chunk-XWCVS4R6.js";
10
10
  import {
11
11
  gitService,
12
12
  projectService,
13
13
  removePidFile,
14
14
  writePidFile
15
- } from "../chunk-YLZKGUKM.js";
15
+ } from "../chunk-IYPRVBYG.js";
16
16
  import {
17
17
  authService,
18
18
  logger
19
- } from "../chunk-IJAVMAOZ.js";
19
+ } from "../chunk-H7MANPCF.js";
20
20
 
21
21
  // src/daemon/runner.ts
22
22
  import "dotenv/config";
@@ -484,6 +484,135 @@ var HealthMonitor = class {
484
484
  };
485
485
  var healthMonitor = new HealthMonitor();
486
486
 
487
+ // src/daemon/memory-trend.ts
488
+ var MAX_SAMPLES = 120;
489
+ var MIN_SAMPLES_FOR_ANALYSIS = 10;
490
+ var RSS_SLOPE_CAUTION_THRESHOLD = 20;
491
+ var RSS_SLOPE_WARNING_THRESHOLD = 50;
492
+ var R2_CONFIDENCE_THRESHOLD = 0.7;
493
+ var MemoryTrendTracker = class {
494
+ samples = [];
495
+ baselineRss = 0;
496
+ /**
497
+ * Add a new memory sample
498
+ */
499
+ addSample(memoryUsage, uptime) {
500
+ const sample = {
501
+ timestamp: Date.now(),
502
+ rss: Math.round(memoryUsage.rss / 1024 / 1024),
503
+ heapUsed: Math.round(memoryUsage.heapUsed / 1024 / 1024),
504
+ uptime
505
+ };
506
+ if (this.samples.length === 0) {
507
+ this.baselineRss = sample.rss;
508
+ }
509
+ this.samples.push(sample);
510
+ if (this.samples.length > MAX_SAMPLES) {
511
+ this.samples.shift();
512
+ }
513
+ }
514
+ /**
515
+ * Calculate linear regression for a dataset
516
+ * Returns { slope, intercept, r2 }
517
+ */
518
+ linearRegression(points) {
519
+ const n = points.length;
520
+ if (n < 2) return { slope: 0, intercept: 0, r2: 0 };
521
+ let sumX = 0;
522
+ let sumY = 0;
523
+ let sumXY = 0;
524
+ let sumXX = 0;
525
+ for (const { x, y } of points) {
526
+ sumX += x;
527
+ sumY += y;
528
+ sumXY += x * y;
529
+ sumXX += x * x;
530
+ }
531
+ const denominator = n * sumXX - sumX * sumX;
532
+ if (denominator === 0) return { slope: 0, intercept: 0, r2: 0 };
533
+ const slope = (n * sumXY - sumX * sumY) / denominator;
534
+ const intercept = (sumY - slope * sumX) / n;
535
+ const yMean = sumY / n;
536
+ let ssTotal = 0;
537
+ let ssResidual = 0;
538
+ for (const { x, y } of points) {
539
+ const yPredicted = slope * x + intercept;
540
+ ssTotal += (y - yMean) ** 2;
541
+ ssResidual += (y - yPredicted) ** 2;
542
+ }
543
+ const r2 = ssTotal === 0 ? 0 : 1 - ssResidual / ssTotal;
544
+ return { slope, intercept, r2 };
545
+ }
546
+ /**
547
+ * Get current memory trend analysis
548
+ */
549
+ getTrend() {
550
+ if (this.samples.length < MIN_SAMPLES_FOR_ANALYSIS) {
551
+ return {
552
+ rssSlope: 0,
553
+ heapSlope: 0,
554
+ rssR2: 0,
555
+ heapR2: 0,
556
+ sampleCount: this.samples.length,
557
+ timeSpanMinutes: 0,
558
+ status: "unknown",
559
+ message: `Collecting data... (${this.samples.length}/${MIN_SAMPLES_FOR_ANALYSIS} samples)`,
560
+ baselineRss: this.baselineRss,
561
+ currentRss: this.samples.length > 0 ? this.samples[this.samples.length - 1].rss : 0
562
+ };
563
+ }
564
+ const firstTimestamp = this.samples[0].timestamp;
565
+ const rssPoints = this.samples.map((s) => ({
566
+ x: (s.timestamp - firstTimestamp) / 1e3 / 3600,
567
+ // hours
568
+ y: s.rss
569
+ }));
570
+ const heapPoints = this.samples.map((s) => ({
571
+ x: (s.timestamp - firstTimestamp) / 1e3 / 3600,
572
+ y: s.heapUsed
573
+ }));
574
+ const rssRegression = this.linearRegression(rssPoints);
575
+ const heapRegression = this.linearRegression(heapPoints);
576
+ const lastTimestamp = this.samples[this.samples.length - 1].timestamp;
577
+ const timeSpanMinutes = (lastTimestamp - firstTimestamp) / 1e3 / 60;
578
+ const currentRss = this.samples[this.samples.length - 1].rss;
579
+ let status = "healthy";
580
+ let message = "Memory usage is stable";
581
+ const isConfidentTrend = rssRegression.r2 >= R2_CONFIDENCE_THRESHOLD;
582
+ if (isConfidentTrend && rssRegression.slope >= RSS_SLOPE_WARNING_THRESHOLD) {
583
+ status = "warning";
584
+ message = `Memory leak detected: +${rssRegression.slope.toFixed(1)} MB/hr (R\xB2=${rssRegression.r2.toFixed(2)})`;
585
+ } else if (isConfidentTrend && rssRegression.slope >= RSS_SLOPE_CAUTION_THRESHOLD) {
586
+ status = "caution";
587
+ message = `Memory trending up: +${rssRegression.slope.toFixed(1)} MB/hr`;
588
+ } else if (rssRegression.slope < 0 && rssRegression.r2 >= 0.5) {
589
+ message = `Memory stable/decreasing: ${rssRegression.slope.toFixed(1)} MB/hr`;
590
+ } else if (!isConfidentTrend && Math.abs(rssRegression.slope) > 10) {
591
+ message = `Memory fluctuating (low confidence: R\xB2=${rssRegression.r2.toFixed(2)})`;
592
+ }
593
+ return {
594
+ rssSlope: rssRegression.slope,
595
+ heapSlope: heapRegression.slope,
596
+ rssR2: rssRegression.r2,
597
+ heapR2: heapRegression.r2,
598
+ sampleCount: this.samples.length,
599
+ timeSpanMinutes,
600
+ status,
601
+ message,
602
+ baselineRss: this.baselineRss,
603
+ currentRss
604
+ };
605
+ }
606
+ /**
607
+ * Reset all samples (e.g., after restart)
608
+ */
609
+ reset() {
610
+ this.samples = [];
611
+ this.baselineRss = 0;
612
+ }
613
+ };
614
+ var memoryTrendTracker = new MemoryTrendTracker();
615
+
487
616
  // src/daemon/index.ts
488
617
  var heartbeatInterval = null;
489
618
  var isShuttingDown = false;
@@ -645,6 +774,9 @@ function startHeartbeat() {
645
774
  if (isShuttingDown) return;
646
775
  try {
647
776
  const memoryUsage = process.memoryUsage();
777
+ const uptime = Math.floor(process.uptime());
778
+ memoryTrendTracker.addSample(memoryUsage, uptime);
779
+ const memoryTrend = memoryTrendTracker.getTrend();
648
780
  await apiService.heartbeat({
649
781
  websocket_connected: websocketService.isConnected(),
650
782
  queue_size: commitQueue.getQueueLength(),
@@ -656,7 +788,19 @@ function startHeartbeat() {
656
788
  heapUsed: Math.round(memoryUsage.heapUsed / 1024 / 1024),
657
789
  external: Math.round(memoryUsage.external / 1024 / 1024)
658
790
  },
659
- uptime: Math.floor(process.uptime())
791
+ memory_trend: {
792
+ rss_slope: memoryTrend.rssSlope,
793
+ heap_slope: memoryTrend.heapSlope,
794
+ rss_r2: memoryTrend.rssR2,
795
+ heap_r2: memoryTrend.heapR2,
796
+ sample_count: memoryTrend.sampleCount,
797
+ time_span_minutes: memoryTrend.timeSpanMinutes,
798
+ status: memoryTrend.status,
799
+ message: memoryTrend.message,
800
+ baseline_rss: memoryTrend.baselineRss,
801
+ current_rss: memoryTrend.currentRss
802
+ },
803
+ uptime
660
804
  });
661
805
  healthMonitor.recordHeartbeat();
662
806
  logger.debug("daemon", "Heartbeat sent successfully");
package/dist/index.js CHANGED
@@ -2,10 +2,10 @@
2
2
  import {
3
3
  commitQueue,
4
4
  websocketService
5
- } from "./chunk-NTG3X2HK.js";
5
+ } from "./chunk-KGDXQOLB.js";
6
6
  import {
7
7
  apiService
8
- } from "./chunk-H4PLSM7I.js";
8
+ } from "./chunk-XWCVS4R6.js";
9
9
  import {
10
10
  getPidFilePath,
11
11
  gitService,
@@ -14,14 +14,14 @@ import {
14
14
  projectService,
15
15
  spawnDetached,
16
16
  validatePidFile
17
- } from "./chunk-YLZKGUKM.js";
17
+ } from "./chunk-IYPRVBYG.js";
18
18
  import {
19
19
  __commonJS,
20
20
  __toESM,
21
21
  authService,
22
22
  config,
23
23
  logger
24
- } from "./chunk-IJAVMAOZ.js";
24
+ } from "./chunk-H7MANPCF.js";
25
25
 
26
26
  // node_modules/semver/internal/constants.js
27
27
  var require_constants = __commonJS({
@@ -2657,7 +2657,7 @@ function registerStatusCommand(program2) {
2657
2657
  try {
2658
2658
  const { render } = await import("ink");
2659
2659
  const { createElement } = await import("react");
2660
- const { StatusDashboard } = await import("./StatusDashboard-VKR5SXS4.js");
2660
+ const { StatusDashboard } = await import("./StatusDashboard-QOFG2BEM.js");
2661
2661
  render(createElement(StatusDashboard, { cwd }));
2662
2662
  return;
2663
2663
  } catch (error) {
@@ -4928,7 +4928,7 @@ Tasks for project ${chalk15.cyan(linkedProject.projectId)}`
4928
4928
  });
4929
4929
 
4930
4930
  // src/index.ts
4931
- var AGENT_VERSION = "1.2.41";
4931
+ var AGENT_VERSION = "1.2.43";
4932
4932
  var program = new Command2();
4933
4933
  program.name("stint").description("Stint Agent - Local daemon for Stint Project Assistant").version(AGENT_VERSION, "-v, --version", "output the current version").addHelpText(
4934
4934
  "after",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gowelle/stint-agent",
3
- "version": "1.2.41",
3
+ "version": "1.2.43",
4
4
  "description": "Local agent for Stint - Project Assistant",
5
5
  "author": "Gowelle John <gowelle.john@icloud.com>",
6
6
  "license": "MIT",
@@ -1,7 +0,0 @@
1
- import {
2
- apiService
3
- } from "./chunk-H4PLSM7I.js";
4
- import "./chunk-IJAVMAOZ.js";
5
- export {
6
- apiService
7
- };