@gowelle/stint-agent 1.0.8 → 1.2.0

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,102 +1,7 @@
1
1
  import {
2
- apiService,
3
2
  config,
4
3
  logger
5
- } from "./chunk-5OKRSNU4.js";
6
-
7
- // src/utils/process.ts
8
- import fs from "fs";
9
- import path from "path";
10
- import os from "os";
11
- import { spawn } from "child_process";
12
- var PID_FILE = path.join(os.homedir(), ".config", "stint", "daemon.pid");
13
- function isProcessRunning(pid) {
14
- try {
15
- process.kill(pid, 0);
16
- return true;
17
- } catch {
18
- return false;
19
- }
20
- }
21
- function killProcess(pid, signal = "SIGTERM") {
22
- try {
23
- process.kill(pid, signal);
24
- logger.info("process", `Sent ${signal} to process ${pid}`);
25
- } catch (error) {
26
- logger.error("process", `Failed to kill process ${pid}`, error);
27
- throw new Error(`Failed to kill process ${pid}: ${error.message}`);
28
- }
29
- }
30
- function spawnDetached(command, args, options = {}) {
31
- const logDir = path.join(os.homedir(), ".config", "stint", "logs");
32
- if (!fs.existsSync(logDir)) {
33
- fs.mkdirSync(logDir, { recursive: true });
34
- }
35
- const stdoutPath = options.stdout || path.join(logDir, "daemon.log");
36
- const stderrPath = options.stderr || path.join(logDir, "daemon-error.log");
37
- const out = fs.openSync(stdoutPath, "a");
38
- const err = fs.openSync(stderrPath, "a");
39
- const child = spawn(command, args, {
40
- detached: true,
41
- stdio: ["ignore", out, err],
42
- cwd: options.cwd || process.cwd(),
43
- env: options.env || process.env
44
- });
45
- child.unref();
46
- logger.info("process", `Spawned detached process ${child.pid}`);
47
- return child.pid;
48
- }
49
- function writePidFile(pid) {
50
- const pidDir = path.dirname(PID_FILE);
51
- if (!fs.existsSync(pidDir)) {
52
- fs.mkdirSync(pidDir, { recursive: true });
53
- }
54
- fs.writeFileSync(PID_FILE, pid.toString(), { mode: 384 });
55
- logger.info("process", `Wrote PID ${pid} to ${PID_FILE}`);
56
- }
57
- function readPidFile() {
58
- try {
59
- if (!fs.existsSync(PID_FILE)) {
60
- return null;
61
- }
62
- const pidStr = fs.readFileSync(PID_FILE, "utf8").trim();
63
- const pid = parseInt(pidStr, 10);
64
- if (isNaN(pid)) {
65
- logger.warn("process", `Invalid PID in file: ${pidStr}`);
66
- return null;
67
- }
68
- return pid;
69
- } catch (error) {
70
- logger.error("process", "Failed to read PID file", error);
71
- return null;
72
- }
73
- }
74
- function removePidFile() {
75
- try {
76
- if (fs.existsSync(PID_FILE)) {
77
- fs.unlinkSync(PID_FILE);
78
- logger.info("process", "Removed PID file");
79
- }
80
- } catch (error) {
81
- logger.error("process", "Failed to remove PID file", error);
82
- }
83
- }
84
- function validatePidFile() {
85
- const pid = readPidFile();
86
- if (pid === null) {
87
- return { valid: false, pid: null };
88
- }
89
- const running = isProcessRunning(pid);
90
- if (!running) {
91
- logger.warn("process", `PID file exists but process ${pid} is not running`);
92
- removePidFile();
93
- return { valid: false, pid: null };
94
- }
95
- return { valid: true, pid };
96
- }
97
- function getPidFilePath() {
98
- return PID_FILE;
99
- }
4
+ } from "./chunk-RHMTZK2J.js";
100
5
 
101
6
  // src/services/git.ts
102
7
  import simpleGit from "simple-git";
@@ -104,6 +9,11 @@ var GitServiceImpl = class {
104
9
  getGit(path3) {
105
10
  return simpleGit(path3);
106
11
  }
12
+ /**
13
+ * Check if a directory is a git repository
14
+ * @param path - Directory path to check
15
+ * @returns True if directory is a git repository
16
+ */
107
17
  async isRepo(path3) {
108
18
  try {
109
19
  const git = this.getGit(path3);
@@ -113,14 +23,23 @@ var GitServiceImpl = class {
113
23
  return false;
114
24
  }
115
25
  }
116
- async getRepoInfo(path3) {
26
+ /**
27
+ * Get comprehensive repository information
28
+ * @param path - Repository path
29
+ * @returns Repository info including branches, status, and last commit
30
+ * @throws Error if not a valid repository or no commits found
31
+ */
32
+ async getRepoInfo(path3, onProgress) {
117
33
  try {
118
34
  const git = this.getGit(path3);
35
+ onProgress?.("Getting branch information...");
119
36
  const branchSummary = await git.branch();
120
37
  const currentBranch = branchSummary.current;
121
38
  const branches = branchSummary.all;
39
+ onProgress?.("Checking remote configuration...");
122
40
  const remotes = await git.getRemotes(true);
123
41
  const remoteUrl = remotes.length > 0 ? remotes[0].refs.fetch : null;
42
+ onProgress?.("Determining default branch...");
124
43
  let defaultBranch = currentBranch;
125
44
  try {
126
45
  const result = await git.raw(["symbolic-ref", "refs/remotes/origin/HEAD"]);
@@ -135,7 +54,9 @@ var GitServiceImpl = class {
135
54
  defaultBranch = "master";
136
55
  }
137
56
  }
57
+ onProgress?.("Checking repository status...");
138
58
  const status = await this.getStatus(path3);
59
+ onProgress?.("Getting commit history...");
139
60
  const log = await git.log({ maxCount: 1 });
140
61
  const lastCommit = log.latest;
141
62
  if (!lastCommit) {
@@ -242,11 +163,11 @@ var GitServiceImpl = class {
242
163
  var gitService = new GitServiceImpl();
243
164
 
244
165
  // src/services/project.ts
245
- import path2 from "path";
166
+ import path from "path";
246
167
  var ProjectServiceImpl = class {
247
168
  async linkProject(projectPath, projectId) {
248
169
  try {
249
- const absolutePath = path2.resolve(projectPath);
170
+ const absolutePath = path.resolve(projectPath);
250
171
  const repoRoot = await gitService.getRepoRoot(absolutePath);
251
172
  const linkPath = repoRoot || absolutePath;
252
173
  if (!repoRoot) {
@@ -268,7 +189,7 @@ var ProjectServiceImpl = class {
268
189
  }
269
190
  async unlinkProject(projectPath) {
270
191
  try {
271
- const absolutePath = path2.resolve(projectPath);
192
+ const absolutePath = path.resolve(projectPath);
272
193
  const repoRoot = await gitService.getRepoRoot(absolutePath);
273
194
  const lookupPath = repoRoot || absolutePath;
274
195
  const linkedProject = config.getProject(lookupPath);
@@ -283,7 +204,7 @@ var ProjectServiceImpl = class {
283
204
  }
284
205
  }
285
206
  async getLinkedProject(projectPath) {
286
- const absolutePath = path2.resolve(projectPath);
207
+ const absolutePath = path.resolve(projectPath);
287
208
  let project = config.getProject(absolutePath);
288
209
  if (project) return project;
289
210
  const repoRoot = await gitService.getRepoRoot(absolutePath);
@@ -311,120 +232,99 @@ var ProjectServiceImpl = class {
311
232
  };
312
233
  var projectService = new ProjectServiceImpl();
313
234
 
314
- // src/daemon/queue.ts
315
- var CommitQueueProcessor = class {
316
- queue = [];
317
- isProcessing = false;
318
- /**
319
- * Add commit to processing queue
320
- */
321
- addToQueue(commit, project) {
322
- this.queue.push({ commit, project });
323
- logger.info("queue", `Added commit ${commit.id} to queue (position: ${this.queue.length})`);
324
- if (!this.isProcessing) {
325
- this.processQueue();
326
- }
235
+ // src/utils/process.ts
236
+ import fs from "fs";
237
+ import path2 from "path";
238
+ import os from "os";
239
+ import { spawn } from "child_process";
240
+ var PID_FILE = path2.join(os.homedir(), ".config", "stint", "daemon.pid");
241
+ function isProcessRunning(pid) {
242
+ try {
243
+ process.kill(pid, 0);
244
+ return true;
245
+ } catch {
246
+ return false;
327
247
  }
328
- /**
329
- * Process commits sequentially
330
- */
331
- async processQueue() {
332
- if (this.isProcessing) {
333
- return;
334
- }
335
- this.isProcessing = true;
336
- while (this.queue.length > 0) {
337
- const item = this.queue.shift();
338
- if (!item) break;
339
- try {
340
- await this.executeCommit(item.commit, item.project);
341
- } catch (error) {
342
- logger.error("queue", `Failed to execute commit ${item.commit.id}`, error);
343
- }
344
- }
345
- this.isProcessing = false;
248
+ }
249
+ function killProcess(pid, signal = "SIGTERM") {
250
+ try {
251
+ process.kill(pid, signal);
252
+ logger.info("process", `Sent ${signal} to process ${pid}`);
253
+ } catch (error) {
254
+ logger.error("process", `Failed to kill process ${pid}`, error);
255
+ throw new Error(`Failed to kill process ${pid}: ${error.message}`);
346
256
  }
347
- /**
348
- * Execute a single commit
349
- */
350
- async executeCommit(commit, project) {
351
- logger.info("queue", `Processing commit: ${commit.id} - ${commit.message}`);
352
- try {
353
- const projectPath = this.findProjectPath(project.id);
354
- if (!projectPath) {
355
- throw new Error(`Project ${project.id} is not linked to any local directory`);
356
- }
357
- logger.info("queue", `Executing in directory: ${projectPath}`);
358
- const isRepo = await gitService.isRepo(projectPath);
359
- if (!isRepo) {
360
- throw new Error(`Directory ${projectPath} is not a git repository`);
361
- }
362
- const status = await gitService.getStatus(projectPath);
363
- const hasStagedChanges = status.staged.length > 0;
364
- if (!hasStagedChanges) {
365
- throw new Error('No staged changes to commit. Please stage files using "git add" before committing.');
366
- }
367
- logger.info("queue", `Committing ${status.staged.length} staged files.`);
368
- logger.info("queue", `Creating commit with message: "${commit.message}"`);
369
- const sha = await gitService.commit(projectPath, commit.message);
370
- logger.success("queue", `Commit created successfully: ${sha}`);
371
- await this.reportSuccess(commit.id, sha);
372
- return sha;
373
- } catch (error) {
374
- const errorMessage = error.message;
375
- logger.error("queue", `Commit execution failed: ${errorMessage}`);
376
- await this.reportFailure(commit.id, errorMessage);
377
- throw error;
378
- }
257
+ }
258
+ function spawnDetached(command, args, options = {}) {
259
+ const logDir = path2.join(os.homedir(), ".config", "stint", "logs");
260
+ if (!fs.existsSync(logDir)) {
261
+ fs.mkdirSync(logDir, { recursive: true });
379
262
  }
380
- /**
381
- * Report successful execution to API
382
- */
383
- async reportSuccess(commitId, sha) {
384
- try {
385
- await apiService.markCommitExecuted(commitId, sha);
386
- logger.success("queue", `Reported commit execution to API: ${commitId} -> ${sha}`);
387
- } catch (error) {
388
- logger.error("queue", "Failed to report commit success to API", error);
389
- }
263
+ const stdoutPath = options.stdout || path2.join(logDir, "daemon.log");
264
+ const stderrPath = options.stderr || path2.join(logDir, "daemon-error.log");
265
+ const out = fs.openSync(stdoutPath, "a");
266
+ const err = fs.openSync(stderrPath, "a");
267
+ const child = spawn(command, args, {
268
+ detached: true,
269
+ stdio: ["ignore", out, err],
270
+ cwd: options.cwd || process.cwd(),
271
+ env: options.env || process.env
272
+ });
273
+ child.unref();
274
+ logger.info("process", `Spawned detached process ${child.pid}`);
275
+ return child.pid;
276
+ }
277
+ function writePidFile(pid) {
278
+ const pidDir = path2.dirname(PID_FILE);
279
+ if (!fs.existsSync(pidDir)) {
280
+ fs.mkdirSync(pidDir, { recursive: true });
390
281
  }
391
- /**
392
- * Report failed execution to API
393
- */
394
- async reportFailure(commitId, error) {
395
- try {
396
- await apiService.markCommitFailed(commitId, error);
397
- logger.info("queue", `Reported commit failure to API: ${commitId}`);
398
- } catch (apiError) {
399
- logger.error("queue", "Failed to report commit failure to API", apiError);
282
+ fs.writeFileSync(PID_FILE, pid.toString(), { mode: 384 });
283
+ logger.info("process", `Wrote PID ${pid} to ${PID_FILE}`);
284
+ }
285
+ function readPidFile() {
286
+ try {
287
+ if (!fs.existsSync(PID_FILE)) {
288
+ return null;
400
289
  }
401
- }
402
- /**
403
- * Find local path for a project ID
404
- */
405
- findProjectPath(projectId) {
406
- const allProjects = projectService.getAllLinkedProjects();
407
- for (const [path3, linkedProject] of Object.entries(allProjects)) {
408
- if (linkedProject.projectId === projectId) {
409
- return path3;
410
- }
290
+ const pidStr = fs.readFileSync(PID_FILE, "utf8").trim();
291
+ const pid = parseInt(pidStr, 10);
292
+ if (isNaN(pid)) {
293
+ logger.warn("process", `Invalid PID in file: ${pidStr}`);
294
+ return null;
411
295
  }
296
+ return pid;
297
+ } catch (error) {
298
+ logger.error("process", "Failed to read PID file", error);
412
299
  return null;
413
300
  }
414
- /**
415
- * Check if queue is currently processing
416
- */
417
- isCurrentlyProcessing() {
418
- return this.isProcessing;
301
+ }
302
+ function removePidFile() {
303
+ try {
304
+ if (fs.existsSync(PID_FILE)) {
305
+ fs.unlinkSync(PID_FILE);
306
+ logger.info("process", "Removed PID file");
307
+ }
308
+ } catch (error) {
309
+ logger.error("process", "Failed to remove PID file", error);
419
310
  }
420
- /**
421
- * Get queue length
422
- */
423
- getQueueLength() {
424
- return this.queue.length;
311
+ }
312
+ function validatePidFile() {
313
+ const pid = readPidFile();
314
+ if (pid === null) {
315
+ return { valid: false, pid: null };
425
316
  }
426
- };
427
- var commitQueue = new CommitQueueProcessor();
317
+ const running = isProcessRunning(pid);
318
+ if (!running) {
319
+ logger.warn("process", `PID file exists but process ${pid} is not running`);
320
+ removePidFile();
321
+ return { valid: false, pid: null };
322
+ }
323
+ return { valid: true, pid };
324
+ }
325
+ function getPidFilePath() {
326
+ return PID_FILE;
327
+ }
428
328
 
429
329
  export {
430
330
  gitService,
@@ -435,6 +335,5 @@ export {
435
335
  writePidFile,
436
336
  removePidFile,
437
337
  validatePidFile,
438
- getPidFilePath,
439
- commitQueue
338
+ getPidFilePath
440
339
  };