@gowelle/stint-agent 1.2.35 → 1.2.37

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.
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  config,
3
3
  logger
4
- } from "./chunk-RC7Z6GTK.js";
4
+ } from "./chunk-HPHXBSGB.js";
5
5
 
6
6
  // src/services/git.ts
7
7
  import simpleGit from "simple-git";
@@ -30,7 +30,11 @@ var GitServiceImpl = class {
30
30
  const git = this.getGit(path3);
31
31
  return await git.checkIsRepo();
32
32
  } catch (error) {
33
- logger.error("git", `Failed to check if ${path3} is a repo`, error);
33
+ logger.error(
34
+ "git",
35
+ `Failed to check if ${path3} is a repo`,
36
+ error
37
+ );
34
38
  return false;
35
39
  }
36
40
  }
@@ -53,7 +57,10 @@ var GitServiceImpl = class {
53
57
  onProgress?.("Determining default branch...");
54
58
  let defaultBranch = currentBranch;
55
59
  try {
56
- const result = await git.raw(["symbolic-ref", "refs/remotes/origin/HEAD"]);
60
+ const result = await git.raw([
61
+ "symbolic-ref",
62
+ "refs/remotes/origin/HEAD"
63
+ ]);
57
64
  const match = result.trim().match(/refs\/remotes\/origin\/(.+)/);
58
65
  if (match) {
59
66
  defaultBranch = match[1];
@@ -85,8 +92,14 @@ var GitServiceImpl = class {
85
92
  lastCommitDate: lastCommit.date
86
93
  };
87
94
  } catch (error) {
88
- logger.error("git", `Failed to get repo info for ${path3}`, error);
89
- throw new Error(`Failed to get repository information: ${error.message}`);
95
+ logger.error(
96
+ "git",
97
+ `Failed to get repo info for ${path3}`,
98
+ error
99
+ );
100
+ throw new Error(
101
+ `Failed to get repository information: ${error.message}`
102
+ );
90
103
  }
91
104
  }
92
105
  async stageAll(path3, onOutput) {
@@ -127,8 +140,14 @@ var GitServiceImpl = class {
127
140
  const branchSummary = await git.branch();
128
141
  return branchSummary.current;
129
142
  } catch (error) {
130
- logger.error("git", `Failed to get current branch in ${path3}`, error);
131
- throw new Error(`Failed to get current branch: ${error.message}`);
143
+ logger.error(
144
+ "git",
145
+ `Failed to get current branch in ${path3}`,
146
+ error
147
+ );
148
+ throw new Error(
149
+ `Failed to get current branch: ${error.message}`
150
+ );
132
151
  }
133
152
  }
134
153
  async getBranches(path3) {
@@ -221,11 +240,20 @@ var GitServiceImpl = class {
221
240
  });
222
241
  }
223
242
  }
224
- logger.info("git", `Found ${changedFiles.length} changed files in ${path3}`);
243
+ logger.info(
244
+ "git",
245
+ `Found ${changedFiles.length} changed files in ${path3}`
246
+ );
225
247
  return changedFiles;
226
248
  } catch (error) {
227
- logger.error("git", `Failed to get changed files in ${path3}`, error);
228
- throw new Error(`Failed to get changed files: ${error.message}`);
249
+ logger.error(
250
+ "git",
251
+ `Failed to get changed files in ${path3}`,
252
+ error
253
+ );
254
+ throw new Error(
255
+ `Failed to get changed files: ${error.message}`
256
+ );
229
257
  }
230
258
  }
231
259
  /**
@@ -279,7 +307,10 @@ var ProjectServiceImpl = class {
279
307
  linkedAt: (/* @__PURE__ */ new Date()).toISOString()
280
308
  };
281
309
  config.setProject(linkPath, linkedProject);
282
- logger.success("project", `Linked ${linkPath} must be to project ${projectId}`);
310
+ logger.success(
311
+ "project",
312
+ `Linked ${linkPath} must be to project ${projectId}`
313
+ );
283
314
  } catch (error) {
284
315
  logger.error("project", "Failed to link project", error);
285
316
  throw error;
@@ -350,7 +381,9 @@ function killProcess(pid, signal = "SIGTERM") {
350
381
  logger.info("process", `Sent ${signal} to process ${pid}`);
351
382
  } catch (error) {
352
383
  logger.error("process", `Failed to kill process ${pid}`, error);
353
- throw new Error(`Failed to kill process ${pid}: ${error.message}`);
384
+ throw new Error(
385
+ `Failed to kill process ${pid}: ${error.message}`
386
+ );
354
387
  }
355
388
  }
356
389
  function spawnDetached(command, args, options = {}) {
@@ -3,20 +3,20 @@ import {
3
3
  commitQueue,
4
4
  notify,
5
5
  websocketService
6
- } from "../chunk-MISINEGG.js";
6
+ } from "../chunk-NODAAPCO.js";
7
7
  import {
8
8
  apiService
9
- } from "../chunk-H5GHDNXY.js";
9
+ } from "../chunk-IBGWKTT7.js";
10
10
  import {
11
11
  gitService,
12
12
  projectService,
13
13
  removePidFile,
14
14
  writePidFile
15
- } from "../chunk-4655SBXG.js";
15
+ } from "../chunk-XRNTJYCQ.js";
16
16
  import {
17
17
  authService,
18
18
  logger
19
- } from "../chunk-RC7Z6GTK.js";
19
+ } from "../chunk-HPHXBSGB.js";
20
20
 
21
21
  // src/daemon/runner.ts
22
22
  import "dotenv/config";
@@ -69,20 +69,30 @@ var FileWatcher = class {
69
69
  logger.warn("watcher", `Project path does not exist: ${projectPath}`);
70
70
  return;
71
71
  }
72
- const watcher = fs.watch(projectPath, { recursive: true }, (eventType, filename) => {
73
- if (!filename) return;
74
- const filenameStr = Buffer.isBuffer(filename) ? filename.toString("utf8") : filename;
75
- if (this.shouldIgnore(filenameStr)) {
76
- return;
77
- }
78
- logger.debug("watcher", `File change detected: ${filenameStr} (${eventType}) in ${projectPath}`);
79
- const watcherInfo = this.watchers.get(projectPath);
80
- if (watcherInfo && !watcherInfo.enabled) {
81
- logger.debug("watcher", `Auto-sync disabled for project ${projectId}, skipping sync`);
82
- return;
72
+ const watcher = fs.watch(
73
+ projectPath,
74
+ { recursive: true },
75
+ (eventType, filename) => {
76
+ if (!filename) return;
77
+ const filenameStr = Buffer.isBuffer(filename) ? filename.toString("utf8") : filename;
78
+ if (this.shouldIgnore(filenameStr)) {
79
+ return;
80
+ }
81
+ logger.debug(
82
+ "watcher",
83
+ `File change detected: ${filenameStr} (${eventType}) in ${projectPath}`
84
+ );
85
+ const watcherInfo = this.watchers.get(projectPath);
86
+ if (watcherInfo && !watcherInfo.enabled) {
87
+ logger.debug(
88
+ "watcher",
89
+ `Auto-sync disabled for project ${projectId}, skipping sync`
90
+ );
91
+ return;
92
+ }
93
+ this.debounceSync(projectPath, projectId);
83
94
  }
84
- this.debounceSync(projectPath, projectId);
85
- });
95
+ );
86
96
  watcher.on("error", (error) => {
87
97
  logger.error("watcher", `Watcher error for ${projectPath}`, error);
88
98
  const watcherInfo = this.watchers.get(projectPath);
@@ -101,7 +111,10 @@ var FileWatcher = class {
101
111
  enabled: true
102
112
  // Enabled by default until settings are fetched
103
113
  });
104
- logger.info("watcher", `Started watching: ${projectPath} (project: ${projectId})`);
114
+ logger.info(
115
+ "watcher",
116
+ `Started watching: ${projectPath} (project: ${projectId})`
117
+ );
105
118
  } catch (error) {
106
119
  logger.error("watcher", `Failed to watch ${projectPath}`, error);
107
120
  }
@@ -151,7 +164,11 @@ var FileWatcher = class {
151
164
  try {
152
165
  await this.performSync(projectPath, projectId);
153
166
  } catch (error) {
154
- logger.error("watcher", `Sync failed for ${projectPath}`, error);
167
+ logger.error(
168
+ "watcher",
169
+ `Sync failed for ${projectPath}`,
170
+ error
171
+ );
155
172
  } finally {
156
173
  watcherInfo.debounceTimer = null;
157
174
  }
@@ -165,29 +182,29 @@ var FileWatcher = class {
165
182
  try {
166
183
  const isRepo = await gitService.isRepo(projectPath);
167
184
  if (!isRepo) {
168
- logger.warn("watcher", `Project ${projectPath} is no longer a git repository`);
185
+ logger.warn(
186
+ "watcher",
187
+ `Project ${projectPath} is no longer a git repository`
188
+ );
169
189
  return;
170
190
  }
171
191
  const repoInfo = await gitService.getRepoInfo(projectPath);
172
192
  const changedFiles = await gitService.getChangedFiles(projectPath);
173
- const response = await apiService.syncProject(projectId, repoInfo, changedFiles);
193
+ const response = await apiService.syncProject(
194
+ projectId,
195
+ repoInfo,
196
+ changedFiles
197
+ );
174
198
  if (response.auto_sync) {
175
199
  this.updateProjectSettings(projectId, response.auto_sync);
176
200
  }
177
201
  logger.success("watcher", `Synced project ${projectId}`);
178
- let projectName = projectId;
179
- try {
180
- const projects = await apiService.getLinkedProjects();
181
- const p = projects.find((proj) => proj.id === projectId);
182
- if (p) projectName = p.name;
183
- } catch (_e) {
184
- }
185
- notify({
186
- title: "Sync Complete",
187
- message: `Project "${projectName}" synced successfully.`
188
- });
189
202
  } catch (error) {
190
- logger.error("watcher", `Failed to sync project ${projectId}`, error);
203
+ logger.error(
204
+ "watcher",
205
+ `Failed to sync project ${projectId}`,
206
+ error
207
+ );
191
208
  }
192
209
  }
193
210
  /**
@@ -203,7 +220,11 @@ var FileWatcher = class {
203
220
  watcherInfo.watcher.close();
204
221
  logger.debug("watcher", `Stopped watching: ${projectPath}`);
205
222
  } catch (error) {
206
- logger.error("watcher", `Error stopping watcher for ${projectPath}`, error);
223
+ logger.error(
224
+ "watcher",
225
+ `Error stopping watcher for ${projectPath}`,
226
+ error
227
+ );
207
228
  }
208
229
  }
209
230
  this.watchers.clear();
@@ -226,7 +247,10 @@ var FileWatcher = class {
226
247
  return;
227
248
  }
228
249
  }
229
- logger.warn("watcher", `Cannot sync: project ${projectId} not found in linked projects`);
250
+ logger.warn(
251
+ "watcher",
252
+ `Cannot sync: project ${projectId} not found in linked projects`
253
+ );
230
254
  }
231
255
  /**
232
256
  * Remove a project from watching (called when a project is unlinked)
@@ -242,7 +266,11 @@ var FileWatcher = class {
242
266
  this.watchers.delete(projectPath);
243
267
  logger.info("watcher", `Stopped watching: ${projectPath}`);
244
268
  } catch (error) {
245
- logger.error("watcher", `Error removing watcher for ${projectPath}`, error);
269
+ logger.error(
270
+ "watcher",
271
+ `Error removing watcher for ${projectPath}`,
272
+ error
273
+ );
246
274
  }
247
275
  }
248
276
  /**
@@ -253,12 +281,61 @@ var FileWatcher = class {
253
281
  if (watcherInfo.projectId === projectId) {
254
282
  watcherInfo.enabled = settings.enabled;
255
283
  watcherInfo.debounceDelay = settings.interval * 1e3;
256
- logger.debug("watcher", `Updated settings for project ${projectId}: enabled=${settings.enabled}, interval=${settings.interval}s`);
284
+ logger.debug(
285
+ "watcher",
286
+ `Updated settings for project ${projectId}: enabled=${settings.enabled}, interval=${settings.interval}s`
287
+ );
257
288
  }
258
289
  }
259
290
  }
260
291
  };
261
292
 
293
+ // src/daemon/sync.ts
294
+ async function syncPendingCommits() {
295
+ logger.info("sync", "Syncing pending commits...");
296
+ const linkedProjects = projectService.getAllLinkedProjects();
297
+ const linkedProjectIds = Object.values(linkedProjects).map(
298
+ (p) => p.projectId
299
+ );
300
+ if (linkedProjectIds.length === 0) {
301
+ logger.info("sync", "No linked projects found locally.");
302
+ return;
303
+ }
304
+ try {
305
+ const apiProjects = await apiService.getLinkedProjects();
306
+ const relevantProjects = apiProjects.filter(
307
+ (p) => linkedProjectIds.includes(p.id)
308
+ );
309
+ logger.info(
310
+ "sync",
311
+ `Checking ${relevantProjects.length} projects for pending commits...`
312
+ );
313
+ for (const project of relevantProjects) {
314
+ try {
315
+ const pendingCommits = await apiService.getPendingCommits(project.id);
316
+ if (pendingCommits.length > 0) {
317
+ logger.info(
318
+ "sync",
319
+ `Found ${pendingCommits.length} pending commits for project ${project.name}`
320
+ );
321
+ for (const commit of pendingCommits) {
322
+ commitQueue.addToQueue(commit, project);
323
+ }
324
+ }
325
+ } catch (error) {
326
+ logger.error(
327
+ "sync",
328
+ `Failed to sync commits for project ${project.name}`,
329
+ error
330
+ );
331
+ }
332
+ }
333
+ logger.success("sync", "Pending commit sync complete");
334
+ } catch (error) {
335
+ logger.error("sync", "Failed to sync pending commits", error);
336
+ }
337
+ }
338
+
262
339
  // src/daemon/index.ts
263
340
  var heartbeatInterval = null;
264
341
  var isShuttingDown = false;
@@ -284,11 +361,15 @@ async function startDaemon() {
284
361
  logger.success("daemon", "WebSocket connected");
285
362
  await websocketService.subscribeToUserChannel(user.id);
286
363
  websocketService.onCommitApproved((commit, project) => {
287
- logger.info("daemon", `Commit approved: ${commit.id} for project ${project.name}`);
364
+ logger.info(
365
+ "daemon",
366
+ `Commit approved: ${commit.id} for project ${project.name}`
367
+ );
288
368
  notify({
289
369
  title: "Commit Approved",
290
370
  message: `${commit.message}
291
- Project: ${project.name}`
371
+ Project: ${project.name}`,
372
+ category: "commits"
292
373
  });
293
374
  commitQueue.addToQueue(commit, project);
294
375
  });
@@ -304,21 +385,29 @@ Project: ${project.name}`
304
385
  }
305
386
  notify({
306
387
  title: `New Proposal - ${projectName}`,
307
- message: commit.message
388
+ message: commit.message,
389
+ category: "commits"
308
390
  });
309
391
  });
310
392
  websocketService.onProjectUpdated((project) => {
311
393
  logger.info("daemon", `Project updated: ${project.id} - ${project.name}`);
312
394
  if (project.settings?.auto_sync) {
313
- fileWatcher.updateProjectSettings(project.id, project.settings.auto_sync);
395
+ fileWatcher.updateProjectSettings(
396
+ project.id,
397
+ project.settings.auto_sync
398
+ );
314
399
  }
315
400
  notify({
316
401
  title: "Project Updated",
317
- message: project.name
402
+ message: project.name,
403
+ category: "sync"
318
404
  });
319
405
  });
320
406
  websocketService.onDisconnect(() => {
321
- logger.warn("daemon", "WebSocket disconnected, will attempt to reconnect");
407
+ logger.warn(
408
+ "daemon",
409
+ "WebSocket disconnected, will attempt to reconnect"
410
+ );
322
411
  });
323
412
  websocketService.onAgentDisconnected(async (reason) => {
324
413
  logger.warn("daemon", `Server disconnected agent: ${reason}`);
@@ -327,13 +416,16 @@ Project: ${project.name}`
327
416
  process.exit(0);
328
417
  });
329
418
  websocketService.onSuggestionCreated((suggestion) => {
330
- logger.info("daemon", `Suggestion created: ${suggestion.title} (${suggestion.priority})`);
419
+ logger.info(
420
+ "daemon",
421
+ `Suggestion created: ${suggestion.title} (${suggestion.priority})`
422
+ );
331
423
  notify({
332
424
  title: "New Suggestion",
333
425
  message: `${suggestion.title}
334
426
  Priority: ${suggestion.priority}`,
335
- open: `https://stint.codes/projects/${suggestion.project_id}/suggestions/${suggestion.id}`
336
- // Hypothetical URL structure
427
+ open: `https://stint.codes/projects/${suggestion.project_id}/suggestions/${suggestion.id}`,
428
+ category: "suggestions"
337
429
  });
338
430
  });
339
431
  websocketService.onSyncRequested(async (projectId) => {
@@ -350,11 +442,16 @@ Priority: ${suggestion.priority}`,
350
442
  }
351
443
  notify({
352
444
  title: "Sync Requested",
353
- message: `Syncing project "${projectName}"...`
445
+ message: `Syncing project "${projectName}"...`,
446
+ category: "sync"
354
447
  });
355
448
  await fileWatcher.syncProjectById(projectId);
356
449
  } catch (error) {
357
- logger.error("daemon", `Failed to sync project ${projectId}`, error);
450
+ logger.error(
451
+ "daemon",
452
+ `Failed to sync project ${projectId}`,
453
+ error
454
+ );
358
455
  }
359
456
  });
360
457
  setupSignalHandlers();
@@ -365,16 +462,24 @@ Priority: ${suggestion.priority}`,
365
462
  const linkedProjects = projectService.getAllLinkedProjects();
366
463
  const projectEntries = Object.entries(linkedProjects);
367
464
  if (projectEntries.length > 0) {
368
- logger.info("daemon", `Syncing ${projectEntries.length} linked project(s) on startup...`);
465
+ logger.info(
466
+ "daemon",
467
+ `Syncing ${projectEntries.length} linked project(s) on startup...`
468
+ );
369
469
  for (const [, linkedProject] of projectEntries) {
370
470
  try {
371
471
  await fileWatcher.syncProjectById(linkedProject.projectId);
372
472
  } catch (error) {
373
- logger.error("daemon", `Failed to sync project ${linkedProject.projectId} on startup`, error);
473
+ logger.error(
474
+ "daemon",
475
+ `Failed to sync project ${linkedProject.projectId} on startup`,
476
+ error
477
+ );
374
478
  }
375
479
  }
376
480
  logger.success("daemon", "Initial project sync complete");
377
481
  }
482
+ await syncPendingCommits();
378
483
  logger.success("daemon", "Daemon started successfully");
379
484
  await new Promise(() => {
380
485
  });
@@ -431,7 +536,11 @@ async function shutdown(reason) {
431
536
  websocketService.disconnect();
432
537
  logger.info("daemon", "Disconnected from WebSocket");
433
538
  } catch (error) {
434
- logger.error("daemon", "Failed to disconnect from WebSocket", error);
539
+ logger.error(
540
+ "daemon",
541
+ "Failed to disconnect from WebSocket",
542
+ error
543
+ );
435
544
  }
436
545
  try {
437
546
  await apiService.disconnect(shutdownReason);