@gowelle/stint-agent 1.2.44 → 1.2.46

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-4IAVYCEN.js";
5
+ } from "./chunk-6G57QEQ3.js";
6
6
  import {
7
7
  authService
8
- } from "./chunk-X2Z4ILIY.js";
8
+ } from "./chunk-W6DXU2TZ.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-7WHS3Q3J.js";
4
+ import "./chunk-W6DXU2TZ.js";
5
+ export {
6
+ apiService
7
+ };
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  config,
3
3
  logger
4
- } from "./chunk-X2Z4ILIY.js";
4
+ } from "./chunk-W6DXU2TZ.js";
5
5
 
6
6
  // src/services/git.ts
7
7
  import simpleGit from "simple-git";
@@ -285,6 +285,30 @@ var GitServiceImpl = class {
285
285
  return null;
286
286
  }
287
287
  }
288
+ /**
289
+ * Get list of all tracked files in the repository
290
+ * @param path - Repository path
291
+ * @returns Array of file paths relative to repo root
292
+ */
293
+ async getFileTree(path3) {
294
+ try {
295
+ const git = this.getGit(path3);
296
+ const tracked = await git.raw(["ls-files"]);
297
+ const untracked = await git.raw([
298
+ "ls-files",
299
+ "--others",
300
+ "--exclude-standard"
301
+ ]);
302
+ const files = [
303
+ ...tracked.split("\n").filter(Boolean),
304
+ ...untracked.split("\n").filter(Boolean)
305
+ ];
306
+ return [...new Set(files)].sort();
307
+ } catch (error) {
308
+ logger.error("git", `Failed to get file tree in ${path3}`, error);
309
+ return [];
310
+ }
311
+ }
288
312
  };
289
313
  var gitService = new GitServiceImpl();
290
314
 
@@ -2,7 +2,7 @@ import {
2
2
  authService,
3
3
  config,
4
4
  logger
5
- } from "./chunk-X2Z4ILIY.js";
5
+ } from "./chunk-W6DXU2TZ.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.44";
103
+ var AGENT_VERSION = "1.2.46";
104
104
  var ApiServiceImpl = class {
105
105
  sessionId = null;
106
106
  circuitBreaker = new CircuitBreaker({
@@ -412,8 +412,9 @@ var ApiServiceImpl = class {
412
412
  * @param data - Repository information (path, remote URL, branches)
413
413
  * @param changedFiles - Optional array of changed files for commit file selection
414
414
  * @param packageInfo - Optional array of detected package types
415
+ * @param fileTree - Optional array of all files in the project
415
416
  */
416
- async syncProject(projectId, data, changedFiles, packageInfo) {
417
+ async syncProject(projectId, data, changedFiles, packageInfo, fileTree) {
417
418
  logger.info("api", `Syncing project ${projectId}`);
418
419
  return this.withRetry(async () => {
419
420
  const payload = {
@@ -429,6 +430,9 @@ var ApiServiceImpl = class {
429
430
  if (packageInfo && packageInfo.length > 0) {
430
431
  payload.package_info = packageInfo;
431
432
  }
433
+ if (fileTree && fileTree.length > 0) {
434
+ payload.file_tree = fileTree;
435
+ }
432
436
  const response = await this.request(`/api/agent/projects/${projectId}/sync`, {
433
437
  method: "POST",
434
438
  body: JSON.stringify(payload)
@@ -436,10 +440,11 @@ var ApiServiceImpl = class {
436
440
  const packageTypes = packageInfo?.map((p) => p.type).join(", ") || "none";
437
441
  logger.success(
438
442
  "api",
439
- `Project ${projectId} synced (${changedFiles?.length ?? 0} files, packages: ${packageTypes})`
443
+ `Project ${projectId} synced (changed: ${changedFiles?.length ?? 0}, tree: ${fileTree?.length ?? 0}, pkgs: ${packageTypes})`
440
444
  );
441
445
  return {
442
- auto_sync: response.auto_sync
446
+ auto_sync: response.auto_sync,
447
+ package_detection: response.package_detection
443
448
  };
444
449
  }, "Sync project");
445
450
  }
@@ -1,19 +1,20 @@
1
1
  import {
2
2
  apiService
3
- } from "./chunk-UBU6PUVO.js";
3
+ } from "./chunk-7WHS3Q3J.js";
4
4
  import {
5
5
  gitService,
6
6
  projectService
7
- } from "./chunk-4IAVYCEN.js";
7
+ } from "./chunk-6G57QEQ3.js";
8
8
  import {
9
9
  authService,
10
10
  config,
11
11
  logger
12
- } from "./chunk-X2Z4ILIY.js";
12
+ } from "./chunk-W6DXU2TZ.js";
13
13
 
14
14
  // src/services/package-detector.ts
15
15
  import fs from "fs";
16
16
  import path from "path";
17
+ import fetch2 from "node-fetch";
17
18
  var PackageDetectorService = class {
18
19
  /**
19
20
  * Detect all package types in a project directory
@@ -50,8 +51,235 @@ var PackageDetectorService = class {
50
51
  }
51
52
  }
52
53
  /**
53
- * Detect npm package (package.json)
54
+ * Check if patches are published on public registries
54
55
  */
56
+ async checkPublicationStatus(packageInfo) {
57
+ if (!config.isPackageDetectionEnabled()) {
58
+ return packageInfo;
59
+ }
60
+ const timeout = 3e3;
61
+ const controller = new AbortController();
62
+ const timeoutId = setTimeout(() => controller.abort(), timeout);
63
+ try {
64
+ let result = {};
65
+ switch (packageInfo.type) {
66
+ case "npm":
67
+ result = await this.checkNpmPublication(
68
+ packageInfo.name,
69
+ controller.signal
70
+ );
71
+ break;
72
+ case "composer":
73
+ result = await this.checkComposerPublication(
74
+ packageInfo.name,
75
+ controller.signal
76
+ );
77
+ break;
78
+ case "cargo":
79
+ result = await this.checkCargoPublication(
80
+ packageInfo.name,
81
+ controller.signal
82
+ );
83
+ break;
84
+ case "pip":
85
+ result = await this.checkPipPublication(
86
+ packageInfo.name,
87
+ controller.signal
88
+ );
89
+ break;
90
+ case "gem":
91
+ result = await this.checkGemPublication(
92
+ packageInfo.name,
93
+ controller.signal
94
+ );
95
+ break;
96
+ case "go":
97
+ result = await this.checkGoPublication(
98
+ packageInfo.name,
99
+ controller.signal
100
+ );
101
+ break;
102
+ }
103
+ if (result.isPublished) {
104
+ return {
105
+ ...packageInfo,
106
+ ...result,
107
+ lastCheckedAt: (/* @__PURE__ */ new Date()).toISOString()
108
+ };
109
+ }
110
+ } catch (error) {
111
+ if (error instanceof Error && error.name !== "AbortError") {
112
+ logger.debug("package-detector", `Check failed: ${error}`);
113
+ }
114
+ } finally {
115
+ clearTimeout(timeoutId);
116
+ }
117
+ return packageInfo;
118
+ }
119
+ async checkNpmPublication(name, signal) {
120
+ try {
121
+ const response = await fetch2(`https://registry.npmjs.org/${name}`, {
122
+ signal,
123
+ headers: { Accept: "application/json" }
124
+ });
125
+ if (response.ok) {
126
+ const data = await response.json();
127
+ const stats = await this.fetchNpmStats(name, signal);
128
+ return {
129
+ isPublished: true,
130
+ publishedVersion: data["dist-tags"]?.latest,
131
+ registryUrl: `https://www.npmjs.com/package/${name}`,
132
+ downloads: stats
133
+ };
134
+ }
135
+ } catch (_error) {
136
+ }
137
+ return {};
138
+ }
139
+ async fetchNpmStats(name, signal) {
140
+ try {
141
+ const response = await fetch2(
142
+ `https://api.npmjs.org/downloads/point/last-month/${name}`,
143
+ {
144
+ signal,
145
+ headers: { Accept: "application/json" }
146
+ }
147
+ );
148
+ if (response.ok) {
149
+ const data = await response.json();
150
+ return { monthly: data.downloads };
151
+ }
152
+ } catch {
153
+ }
154
+ return {};
155
+ }
156
+ async checkComposerPublication(name, signal) {
157
+ try {
158
+ const response = await fetch2(
159
+ `https://packagist.org/packages/${name}.json`,
160
+ {
161
+ signal,
162
+ headers: { Accept: "application/json" }
163
+ }
164
+ );
165
+ if (response.ok) {
166
+ const data = await response.json();
167
+ const downloads = data.package?.downloads || {};
168
+ const latestVersion = Object.keys(data.package?.versions || {}).pop() || "";
169
+ return {
170
+ isPublished: true,
171
+ publishedVersion: latestVersion,
172
+ registryUrl: `https://packagist.org/packages/${name}`,
173
+ downloads: {
174
+ total: downloads.total,
175
+ monthly: downloads.monthly
176
+ }
177
+ };
178
+ }
179
+ } catch (_error) {
180
+ }
181
+ return {};
182
+ }
183
+ async checkCargoPublication(name, signal) {
184
+ try {
185
+ const response = await fetch2(`https://crates.io/api/v1/crates/${name}`, {
186
+ signal,
187
+ headers: {
188
+ Accept: "application/json",
189
+ "User-Agent": "stint-agent (stint.codes)"
190
+ }
191
+ });
192
+ if (response.ok) {
193
+ const data = await response.json();
194
+ return {
195
+ isPublished: true,
196
+ publishedVersion: data.crate?.max_version,
197
+ registryUrl: `https://crates.io/crates/${name}`,
198
+ downloads: {
199
+ total: data.crate?.downloads
200
+ }
201
+ };
202
+ }
203
+ } catch (_error) {
204
+ }
205
+ return {};
206
+ }
207
+ async checkPipPublication(name, signal) {
208
+ try {
209
+ const response = await fetch2(`https://pypi.org/pypi/${name}/json`, {
210
+ signal,
211
+ headers: { Accept: "application/json" }
212
+ });
213
+ if (response.ok) {
214
+ const data = await response.json();
215
+ const stats = await this.fetchPipStats(name, signal);
216
+ return {
217
+ isPublished: true,
218
+ publishedVersion: data.info?.version,
219
+ registryUrl: `https://pypi.org/project/${name}/`,
220
+ downloads: stats
221
+ };
222
+ }
223
+ } catch (_error) {
224
+ }
225
+ return {};
226
+ }
227
+ async fetchPipStats(name, signal) {
228
+ try {
229
+ const response = await fetch2(
230
+ `https://pypistats.org/api/packages/${name}/recent`,
231
+ {
232
+ signal,
233
+ headers: { Accept: "application/json" }
234
+ }
235
+ );
236
+ if (response.ok) {
237
+ const data = await response.json();
238
+ return { monthly: data.data?.last_month };
239
+ }
240
+ } catch {
241
+ }
242
+ return {};
243
+ }
244
+ async checkGoPublication(name, signal) {
245
+ try {
246
+ const response = await fetch2(`https://proxy.golang.org/${name}/@v/list`, {
247
+ signal
248
+ });
249
+ if (response.ok) {
250
+ return {
251
+ isPublished: true,
252
+ registryUrl: `https://pkg.go.dev/${name}`
253
+ };
254
+ }
255
+ } catch {
256
+ }
257
+ return {};
258
+ }
259
+ async checkGemPublication(name, signal) {
260
+ try {
261
+ const response = await fetch2(
262
+ `https://rubygems.org/api/v1/gems/${name}.json`,
263
+ {
264
+ signal,
265
+ headers: { Accept: "application/json" }
266
+ }
267
+ );
268
+ if (response.ok) {
269
+ const data = await response.json();
270
+ return {
271
+ isPublished: true,
272
+ publishedVersion: data.version,
273
+ registryUrl: `https://rubygems.org/gems/${name}`,
274
+ downloads: {
275
+ total: data.downloads
276
+ }
277
+ };
278
+ }
279
+ } catch {
280
+ }
281
+ return {};
282
+ }
55
283
  async detectNpm(projectPath) {
56
284
  const manifestPath = path.join(projectPath, "package.json");
57
285
  if (!fs.existsSync(manifestPath)) {
@@ -1159,7 +1387,7 @@ var WebSocketServiceImpl = class {
1159
1387
  "websocket",
1160
1388
  `Commit ${commit.id} marked as large, fetching full details...`
1161
1389
  );
1162
- const { apiService: apiService2 } = await import("./api-A5W4VYEH.js");
1390
+ const { apiService: apiService2 } = await import("./api-BKJF4YTU.js");
1163
1391
  const fullCommit = await apiService2.getCommit(commit.id);
1164
1392
  commit = {
1165
1393
  ...commit,
@@ -51,6 +51,9 @@ var DEFAULT_CONFIG = {
51
51
  // Noisy - off by default
52
52
  suggestions: true
53
53
  // Actionable - on by default
54
+ },
55
+ packageDetection: {
56
+ enabled: true
54
57
  }
55
58
  };
56
59
  var ConfigManager = class {
@@ -198,6 +201,15 @@ var ConfigManager = class {
198
201
  const current = this.conf.get("notifications") || { enabled: true };
199
202
  this.conf.set("notifications", { ...current, [category]: enabled });
200
203
  }
204
+ // Package Detection
205
+ isPackageDetectionEnabled() {
206
+ const pkgConfig = this.conf.get("packageDetection");
207
+ return pkgConfig?.enabled ?? true;
208
+ }
209
+ setPackageDetectionEnabled(enabled) {
210
+ const current = this.conf.get("packageDetection") || {};
211
+ this.conf.set("packageDetection", { ...current, enabled });
212
+ }
201
213
  };
202
214
  var config = new ConfigManager();
203
215
 
@@ -346,7 +358,7 @@ var AuthServiceImpl = class {
346
358
  return null;
347
359
  }
348
360
  try {
349
- const { apiService } = await import("./api-A5W4VYEH.js");
361
+ const { apiService } = await import("./api-BKJF4YTU.js");
350
362
  const user = await apiService.getCurrentUser();
351
363
  logger.info("auth", `Token validated for user: ${user.email}`);
352
364
  return user;
@@ -4,20 +4,20 @@ import {
4
4
  notify,
5
5
  packageDetector,
6
6
  websocketService
7
- } from "../chunk-7B4YYEBR.js";
7
+ } from "../chunk-VMK4LAM2.js";
8
8
  import {
9
9
  apiService
10
- } from "../chunk-UBU6PUVO.js";
10
+ } from "../chunk-7WHS3Q3J.js";
11
11
  import {
12
12
  gitService,
13
13
  projectService,
14
14
  removePidFile,
15
15
  writePidFile
16
- } from "../chunk-4IAVYCEN.js";
16
+ } from "../chunk-6G57QEQ3.js";
17
17
  import {
18
18
  authService,
19
19
  logger
20
- } from "../chunk-X2Z4ILIY.js";
20
+ } from "../chunk-W6DXU2TZ.js";
21
21
 
22
22
  // src/daemon/runner.ts
23
23
  import "dotenv/config";
@@ -191,7 +191,10 @@ var FileWatcher = class {
191
191
  }
192
192
  const repoInfo = await gitService.getRepoInfo(projectPath);
193
193
  const changedFiles = await gitService.getChangedFiles(projectPath);
194
- const packageInfo = await packageDetector.detectAll(projectPath);
194
+ const rawPackageInfo = await packageDetector.detectAll(projectPath);
195
+ const packageInfo = await Promise.all(
196
+ rawPackageInfo.map((p) => packageDetector.checkPublicationStatus(p))
197
+ );
195
198
  const response = await apiService.syncProject(
196
199
  projectId,
197
200
  repoInfo,
@@ -201,6 +204,9 @@ var FileWatcher = class {
201
204
  if (response.auto_sync) {
202
205
  this.updateProjectSettings(projectId, response.auto_sync);
203
206
  }
207
+ if (response.package_detection) {
208
+ config.setPackageDetectionEnabled(response.package_detection.enabled);
209
+ }
204
210
  logger.success("watcher", `Synced project ${projectId}`);
205
211
  } catch (error) {
206
212
  logger.error(
package/dist/index.js CHANGED
@@ -3,10 +3,10 @@ import {
3
3
  commitQueue,
4
4
  packageDetector,
5
5
  websocketService
6
- } from "./chunk-7B4YYEBR.js";
6
+ } from "./chunk-VMK4LAM2.js";
7
7
  import {
8
8
  apiService
9
- } from "./chunk-UBU6PUVO.js";
9
+ } from "./chunk-7WHS3Q3J.js";
10
10
  import {
11
11
  getPidFilePath,
12
12
  gitService,
@@ -15,14 +15,14 @@ import {
15
15
  projectService,
16
16
  spawnDetached,
17
17
  validatePidFile
18
- } from "./chunk-4IAVYCEN.js";
18
+ } from "./chunk-6G57QEQ3.js";
19
19
  import {
20
20
  __commonJS,
21
21
  __toESM,
22
22
  authService,
23
23
  config,
24
24
  logger
25
- } from "./chunk-X2Z4ILIY.js";
25
+ } from "./chunk-W6DXU2TZ.js";
26
26
 
27
27
  // node_modules/semver/internal/constants.js
28
28
  var require_constants = __commonJS({
@@ -2658,7 +2658,7 @@ function registerStatusCommand(program2) {
2658
2658
  try {
2659
2659
  const { render } = await import("ink");
2660
2660
  const { createElement } = await import("react");
2661
- const { StatusDashboard } = await import("./StatusDashboard-LG4VC32Y.js");
2661
+ const { StatusDashboard } = await import("./StatusDashboard-BHUFNUJC.js");
2662
2662
  render(createElement(StatusDashboard, { cwd }));
2663
2663
  return;
2664
2664
  } catch (error) {
@@ -2885,7 +2885,13 @@ function registerSyncCommand(program2) {
2885
2885
  spinner.text = "Getting changed files...";
2886
2886
  const changedFiles = await gitService.getChangedFiles(cwd);
2887
2887
  spinner.text = "Detecting package types...";
2888
- const packageInfo = await packageDetector.detectAll(cwd);
2888
+ const rawPackageInfo = await packageDetector.detectAll(cwd);
2889
+ spinner.text = "Checking publication status...";
2890
+ const packageInfo = await Promise.all(
2891
+ rawPackageInfo.map((p) => packageDetector.checkPublicationStatus(p))
2892
+ );
2893
+ spinner.text = "Scanning file tree...";
2894
+ const fileTree = await gitService.getFileTree(cwd);
2889
2895
  spinner.text = "Preparing sync payload...";
2890
2896
  const syncSpinner = ora7("Connecting to server...").start();
2891
2897
  try {
@@ -2893,7 +2899,8 @@ function registerSyncCommand(program2) {
2893
2899
  linkedProject.projectId,
2894
2900
  repoInfo,
2895
2901
  changedFiles,
2896
- packageInfo
2902
+ packageInfo,
2903
+ fileTree
2897
2904
  );
2898
2905
  syncSpinner.succeed("Server sync completed");
2899
2906
  } catch (error) {
@@ -2912,6 +2919,9 @@ function registerSyncCommand(program2) {
2912
2919
  const pkgTypes = packageInfo.map((p) => `${p.type} (${p.name}@${p.version})`).join(", ");
2913
2920
  console.log(`${chalk7.bold("Packages:")} ${pkgTypes}`);
2914
2921
  }
2922
+ console.log(
2923
+ `${chalk7.bold("File Tree:")} ${fileTree.length} files indexed`
2924
+ );
2915
2925
  console.log(`${chalk7.bold("Project ID:")} ${linkedProject.projectId}`);
2916
2926
  console.log(`${chalk7.bold("Branch:")} ${repoInfo.currentBranch}`);
2917
2927
  console.log(
@@ -4936,7 +4946,7 @@ Tasks for project ${chalk15.cyan(linkedProject.projectId)}`
4936
4946
  });
4937
4947
 
4938
4948
  // src/index.ts
4939
- var AGENT_VERSION = "1.2.44";
4949
+ var AGENT_VERSION = "1.2.46";
4940
4950
  var program = new Command2();
4941
4951
  program.name("stint").description("Stint Agent - Local daemon for Stint Project Assistant").version(AGENT_VERSION, "-v, --version", "output the current version").addHelpText(
4942
4952
  "after",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gowelle/stint-agent",
3
- "version": "1.2.44",
3
+ "version": "1.2.46",
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-UBU6PUVO.js";
4
- import "./chunk-X2Z4ILIY.js";
5
- export {
6
- apiService
7
- };