@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.
package/README.md CHANGED
@@ -15,7 +15,13 @@ The official CLI agent for [Stint](https://stint.codes) — a lightweight daemon
15
15
  - 📦 Automatic commit execution
16
16
  - 🔍 Repository status syncing
17
17
  - 🖥️ Background daemon process
18
- - 📝 Comprehensive logging
18
+ - 📝 Comprehensive logging and filtering
19
+ - 📊 Interactive status dashboard
20
+ - 🚀 Multiple release channels (stable/beta/nightly)
21
+ - 🔍 Built-in environment diagnostics
22
+ - 📈 Resource usage monitoring
23
+
24
+ For detailed feature documentation, see the **[Features Guide](docs/features.md)**.
19
25
 
20
26
  ## Installation
21
27
 
@@ -49,47 +55,47 @@ stint daemon status
49
55
 
50
56
  ### General
51
57
 
52
- | Command | Description |
53
- |---------|-------------|
54
- | `stint --version`, `stint -V` | Show current agent version |
55
- | `stint --help`, `stint -h` | Show help information |
56
- | `stint update` | Update agent to the latest version |
58
+ | Command | Description |
59
+ | ----------------------------- | ---------------------------------- |
60
+ | `stint --version`, `stint -V` | Show current agent version |
61
+ | `stint --help`, `stint -h` | Show help information |
62
+ | `stint update` | Update agent to the latest version |
57
63
 
58
64
  ### Authentication
59
65
 
60
- | Command | Description |
61
- |---------|-------------|
62
- | `stint login` | Authenticate with Stint (opens browser for OAuth) |
63
- | `stint logout` | Remove stored credentials |
64
- | `stint whoami` | Show current user and machine information |
66
+ | Command | Description |
67
+ | -------------- | ------------------------------------------------- |
68
+ | `stint login` | Authenticate with Stint (opens browser for OAuth) |
69
+ | `stint logout` | Remove stored credentials |
70
+ | `stint whoami` | Show current user and machine information |
65
71
 
66
72
  ### Daemon Lifecycle
67
73
 
68
- | Command | Description |
69
- |---------|-------------|
70
- | `stint install` | Register daemon to run on system startup (Login required) |
71
- | `stint uninstall` | Remove daemon from system startup |
72
- | `stint daemon start` | Start background daemon manually |
73
- | `stint daemon stop` | Stop daemon gracefully |
74
- | `stint daemon status` | Check if daemon is running |
75
- | `stint daemon logs [--lines N]` | View daemon logs (default: 50 lines) |
76
- | `stint daemon restart` | Restart the daemon |
74
+ | Command | Description |
75
+ | ------------------------------- | --------------------------------------------------------- |
76
+ | `stint install` | Register daemon to run on system startup (Login required) |
77
+ | `stint uninstall` | Remove daemon from system startup |
78
+ | `stint daemon start` | Start background daemon manually |
79
+ | `stint daemon stop` | Stop daemon gracefully |
80
+ | `stint daemon status` | Check if daemon is running |
81
+ | `stint daemon logs [--lines N]` | View daemon logs (default: 50 lines) |
82
+ | `stint daemon restart` | Restart the daemon |
77
83
 
78
84
  ### Project Management
79
85
 
80
- | Command | Description |
81
- |---------|-------------|
82
- | `stint link` | Link current directory to a Stint project (or create a new one) |
83
- | `stint unlink [--force]` | Remove project link |
84
- | `stint status` | Show project, git, auth, and daemon status |
85
- | `stint sync` | Manually sync repository information to server |
86
+ | Command | Description |
87
+ | ---------------------------- | --------------------------------------------------------------- |
88
+ | `stint link` | Link current directory to a Stint project (or create a new one) |
89
+ | `stint unlink [--force]` | Remove project link |
90
+ | `stint status [--dashboard]` | Show status (use `-d` for interactive dashboard) |
91
+ | `stint sync` | Manually sync repository information to server |
86
92
 
87
93
  ### Commit Operations
88
94
 
89
- | Command | Description |
90
- |---------|-------------|
91
- | `stint commits` | List pending commits for this repository |
92
- | `stint commit <id>` | Execute a specific pending commit |
95
+ | Command | Description |
96
+ | ------------------- | ---------------------------------------- |
97
+ | `stint commits` | List pending commits for this repository |
98
+ | `stint commit <id>` | Execute a specific pending commit |
93
99
 
94
100
  ## Complete Workflow
95
101
 
@@ -113,44 +119,46 @@ stint status
113
119
 
114
120
  ## Troubleshooting
115
121
 
116
- ### "Not authenticated" error
117
-
118
- Run `stint login` to authenticate with your Stint account.
122
+ For comprehensive troubleshooting help, see the **[Troubleshooting Guide](docs/TROUBLESHOOTING.md)**.
119
123
 
120
- ### "Repository has uncommitted changes"
124
+ ### Quick Tips
121
125
 
122
- The agent requires a clean repository to execute commits:
126
+ **"Not authenticated" error**
123
127
 
124
128
  ```bash
125
- git stash # Temporarily stash changes
126
- # or
127
- git add . && git commit -m "message"
129
+ stint login
128
130
  ```
129
131
 
130
- ### Daemon won't start
132
+ **Daemon won't start**
131
133
 
132
134
  ```bash
133
135
  stint daemon status # Check if already running
134
136
  stint daemon logs # Check logs for errors
135
- stint daemon stop # Stop first
136
- stint daemon start # Then start again
137
+ stint daemon restart # Restart daemon
137
138
  ```
138
139
 
139
- ### WebSocket connection issues
140
+ **For detailed solutions**, including:
141
+
142
+ - Connection issues (WebSocket, API, Circuit Breaker)
143
+ - Daemon problems (crashes, autostart)
144
+ - Authentication errors
145
+ - Git operation failures
146
+ - Platform-specific issues (Windows, macOS, Linux)
140
147
 
141
- Check your network connection and firewall settings.
148
+ See the **[Troubleshooting Guide](docs/TROUBLESHOOTING.md)**.
142
149
 
143
150
  ## Logging
144
151
 
145
152
  Logs are stored in your system's config directory:
146
153
 
147
- | Platform | Log Location |
148
- |----------|--------------|
149
- | **macOS** | `~/.config/stint/logs/` |
150
- | **Linux** | `~/.config/stint/logs/` |
154
+ | Platform | Log Location |
155
+ | ----------- | ----------------------------------- |
156
+ | **macOS** | `~/.config/stint/logs/` |
157
+ | **Linux** | `~/.config/stint/logs/` |
151
158
  | **Windows** | `%USERPROFILE%\.config\stint\logs\` |
152
159
 
153
160
  Log files:
161
+
154
162
  - `agent.log` - General CLI operations
155
163
  - `daemon.log` - Daemon process logs
156
164
  - `error.log` - Error details
@@ -0,0 +1,232 @@
1
+ import {
2
+ gitService,
3
+ projectService,
4
+ validatePidFile
5
+ } from "./chunk-FBQA4K5J.js";
6
+ import {
7
+ authService
8
+ } from "./chunk-RHMTZK2J.js";
9
+
10
+ // src/components/StatusDashboard.tsx
11
+ import { useState, useEffect } from "react";
12
+ import { Box as Box3, Text as Text3, useApp, useInput } from "ink";
13
+
14
+ // src/components/Panel.tsx
15
+ import { Box, Text } from "ink";
16
+ import { jsx, jsxs } from "react/jsx-runtime";
17
+ function Panel({ title, icon, children }) {
18
+ return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginBottom: 1, children: [
19
+ /* @__PURE__ */ jsxs(Text, { color: "blue", children: [
20
+ icon ? `${icon} ` : "",
21
+ title,
22
+ ":"
23
+ ] }),
24
+ /* @__PURE__ */ jsx(Text, { color: "gray", children: "\u2500".repeat(50) }),
25
+ /* @__PURE__ */ jsx(Box, { flexDirection: "column", paddingLeft: 0, children })
26
+ ] });
27
+ }
28
+
29
+ // src/components/StatusRow.tsx
30
+ import { Box as Box2, Text as Text2 } from "ink";
31
+ import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
32
+ function StatusRow({ label, value, labelWidth = 12 }) {
33
+ return /* @__PURE__ */ jsxs2(Box2, { children: [
34
+ /* @__PURE__ */ jsx2(Text2, { bold: true, children: label.padEnd(labelWidth) }),
35
+ typeof value === "string" ? /* @__PURE__ */ jsx2(Text2, { children: value }) : value
36
+ ] });
37
+ }
38
+
39
+ // src/components/StatusDashboard.tsx
40
+ import { Fragment, jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
41
+ function StatusDashboard({ cwd }) {
42
+ const { exit } = useApp();
43
+ const [state, setState] = useState({
44
+ linkedProject: null,
45
+ repoInfo: null,
46
+ user: null,
47
+ daemonRunning: false,
48
+ daemonPid: null,
49
+ isRepo: false,
50
+ loading: true,
51
+ lastRefresh: /* @__PURE__ */ new Date(),
52
+ error: null
53
+ });
54
+ const [showHelp, setShowHelp] = useState(false);
55
+ const refreshStatus = async () => {
56
+ setState((prev) => ({ ...prev, loading: true, error: null }));
57
+ try {
58
+ const [linkedProject2, user2, isRepo2] = await Promise.all([
59
+ projectService.getLinkedProject(cwd),
60
+ authService.validateToken(),
61
+ gitService.isRepo(cwd)
62
+ ]);
63
+ let repoInfo2 = null;
64
+ if (isRepo2) {
65
+ try {
66
+ repoInfo2 = await gitService.getRepoInfo(cwd);
67
+ } catch {
68
+ }
69
+ }
70
+ const { valid, pid } = validatePidFile();
71
+ setState({
72
+ linkedProject: linkedProject2,
73
+ repoInfo: repoInfo2,
74
+ user: user2,
75
+ daemonRunning: valid,
76
+ daemonPid: pid,
77
+ isRepo: isRepo2,
78
+ loading: false,
79
+ lastRefresh: /* @__PURE__ */ new Date(),
80
+ error: null
81
+ });
82
+ } catch (error2) {
83
+ setState((prev) => ({
84
+ ...prev,
85
+ loading: false,
86
+ error: error2.message
87
+ }));
88
+ }
89
+ };
90
+ useEffect(() => {
91
+ refreshStatus();
92
+ const interval = setInterval(refreshStatus, 5e3);
93
+ return () => clearInterval(interval);
94
+ }, [cwd]);
95
+ useInput((input, key) => {
96
+ if (input === "q" || key.escape) {
97
+ exit();
98
+ } else if (input === "r") {
99
+ refreshStatus();
100
+ } else if (input === "?") {
101
+ setShowHelp((prev) => !prev);
102
+ }
103
+ });
104
+ const { linkedProject, repoInfo, user, daemonRunning, daemonPid, isRepo, loading, lastRefresh, error } = state;
105
+ return /* @__PURE__ */ jsxs3(Box3, { flexDirection: "column", padding: 1, children: [
106
+ /* @__PURE__ */ jsx3(Box3, { marginBottom: 1, children: /* @__PURE__ */ jsx3(Text3, { bold: true, color: "cyan", children: "\u256D\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256E" }) }),
107
+ /* @__PURE__ */ jsxs3(Box3, { marginBottom: 1, children: [
108
+ /* @__PURE__ */ jsx3(Text3, { bold: true, color: "cyan", children: "\u2502" }),
109
+ /* @__PURE__ */ jsx3(Text3, { bold: true, children: " \u{1F4CA} Stint Status Dashboard " }),
110
+ /* @__PURE__ */ jsx3(Text3, { bold: true, color: "cyan", children: "\u2502" })
111
+ ] }),
112
+ /* @__PURE__ */ jsx3(Box3, { marginBottom: 1, children: /* @__PURE__ */ jsx3(Text3, { bold: true, color: "cyan", children: "\u2570\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256F" }) }),
113
+ error && /* @__PURE__ */ jsx3(Box3, { marginBottom: 1, children: /* @__PURE__ */ jsxs3(Text3, { color: "red", children: [
114
+ "\u26A0 Error: ",
115
+ error
116
+ ] }) }),
117
+ loading && /* @__PURE__ */ jsx3(Box3, { marginBottom: 1, children: /* @__PURE__ */ jsx3(Text3, { color: "yellow", children: "\u27F3 Refreshing..." }) }),
118
+ /* @__PURE__ */ jsx3(Panel, { title: "Project Status", icon: "\u{1F4E6}", children: linkedProject ? /* @__PURE__ */ jsxs3(Fragment, { children: [
119
+ /* @__PURE__ */ jsx3(StatusRow, { label: "Status:", value: /* @__PURE__ */ jsx3(Text3, { color: "green", children: "\u2713 Linked" }) }),
120
+ /* @__PURE__ */ jsx3(StatusRow, { label: "Project ID:", value: linkedProject.projectId }),
121
+ /* @__PURE__ */ jsx3(StatusRow, { label: "Linked At:", value: new Date(linkedProject.linkedAt).toLocaleString() })
122
+ ] }) : /* @__PURE__ */ jsxs3(Fragment, { children: [
123
+ /* @__PURE__ */ jsx3(StatusRow, { label: "Status:", value: /* @__PURE__ */ jsx3(Text3, { color: "yellow", children: "Not linked" }) }),
124
+ /* @__PURE__ */ jsx3(Text3, { color: "gray", children: 'Run "stint link" to link this directory to a project.' })
125
+ ] }) }),
126
+ /* @__PURE__ */ jsx3(Panel, { title: "Git Repository", icon: "\u{1F4C2}", children: isRepo && repoInfo ? /* @__PURE__ */ jsxs3(Fragment, { children: [
127
+ /* @__PURE__ */ jsx3(StatusRow, { label: "Branch:", value: /* @__PURE__ */ jsx3(Text3, { color: "cyan", children: repoInfo.currentBranch }) }),
128
+ /* @__PURE__ */ jsx3(StatusRow, { label: "Remote:", value: repoInfo.remoteUrl || /* @__PURE__ */ jsx3(Text3, { color: "gray", children: "None" }) }),
129
+ /* @__PURE__ */ jsx3(
130
+ StatusRow,
131
+ {
132
+ label: "Last Commit:",
133
+ value: `${repoInfo.lastCommitSha.substring(0, 7)} - ${repoInfo.lastCommitMessage.substring(0, 40)}${repoInfo.lastCommitMessage.length > 40 ? "..." : ""}`
134
+ }
135
+ ),
136
+ renderGitChanges(repoInfo)
137
+ ] }) : /* @__PURE__ */ jsx3(Text3, { color: "yellow", children: "Not a git repository" }) }),
138
+ /* @__PURE__ */ jsx3(Panel, { title: "Authentication", icon: "\u{1F510}", children: user ? /* @__PURE__ */ jsxs3(Fragment, { children: [
139
+ /* @__PURE__ */ jsx3(StatusRow, { label: "Status:", value: /* @__PURE__ */ jsx3(Text3, { color: "green", children: "\u2713 Authenticated" }) }),
140
+ /* @__PURE__ */ jsx3(StatusRow, { label: "User:", value: `${user.name} (${user.email})` }),
141
+ /* @__PURE__ */ jsx3(StatusRow, { label: "Machine:", value: authService.getMachineName() })
142
+ ] }) : /* @__PURE__ */ jsxs3(Fragment, { children: [
143
+ /* @__PURE__ */ jsx3(StatusRow, { label: "Status:", value: /* @__PURE__ */ jsx3(Text3, { color: "yellow", children: "Not logged in" }) }),
144
+ /* @__PURE__ */ jsx3(Text3, { color: "gray", children: 'Run "stint login" to authenticate.' })
145
+ ] }) }),
146
+ /* @__PURE__ */ jsx3(Panel, { title: "Daemon", icon: "\u2699\uFE0F", children: daemonRunning ? /* @__PURE__ */ jsxs3(Fragment, { children: [
147
+ /* @__PURE__ */ jsx3(StatusRow, { label: "Status:", value: /* @__PURE__ */ jsx3(Text3, { color: "green", children: "\u2713 Running" }) }),
148
+ /* @__PURE__ */ jsx3(StatusRow, { label: "PID:", value: String(daemonPid) })
149
+ ] }) : /* @__PURE__ */ jsxs3(Fragment, { children: [
150
+ /* @__PURE__ */ jsx3(StatusRow, { label: "Status:", value: /* @__PURE__ */ jsx3(Text3, { color: "yellow", children: "Not running" }) }),
151
+ /* @__PURE__ */ jsx3(Text3, { color: "gray", children: 'Run "stint daemon start" to start the background agent.' })
152
+ ] }) }),
153
+ /* @__PURE__ */ jsxs3(Box3, { marginTop: 1, flexDirection: "column", children: [
154
+ /* @__PURE__ */ jsx3(Text3, { color: "gray", children: "\u2500".repeat(50) }),
155
+ /* @__PURE__ */ jsxs3(Box3, { children: [
156
+ /* @__PURE__ */ jsxs3(Text3, { color: "gray", children: [
157
+ "Last refresh: ",
158
+ lastRefresh.toLocaleTimeString()
159
+ ] }),
160
+ /* @__PURE__ */ jsx3(Text3, { color: "gray", children: " \u2502 Auto-refresh: 5s" })
161
+ ] }),
162
+ /* @__PURE__ */ jsxs3(Box3, { marginTop: 1, children: [
163
+ /* @__PURE__ */ jsx3(Text3, { color: "cyan", children: "[q]" }),
164
+ /* @__PURE__ */ jsx3(Text3, { children: " Quit " }),
165
+ /* @__PURE__ */ jsx3(Text3, { color: "cyan", children: "[r]" }),
166
+ /* @__PURE__ */ jsx3(Text3, { children: " Refresh " }),
167
+ /* @__PURE__ */ jsx3(Text3, { color: "cyan", children: "[?]" }),
168
+ /* @__PURE__ */ jsx3(Text3, { children: " Help" })
169
+ ] })
170
+ ] }),
171
+ showHelp && /* @__PURE__ */ jsxs3(
172
+ Box3,
173
+ {
174
+ flexDirection: "column",
175
+ marginTop: 1,
176
+ borderStyle: "round",
177
+ borderColor: "cyan",
178
+ padding: 1,
179
+ children: [
180
+ /* @__PURE__ */ jsx3(Text3, { bold: true, color: "cyan", children: "Keyboard Shortcuts" }),
181
+ /* @__PURE__ */ jsx3(Text3, { children: " q, Esc - Exit dashboard" }),
182
+ /* @__PURE__ */ jsx3(Text3, { children: " r - Refresh status immediately" }),
183
+ /* @__PURE__ */ jsx3(Text3, { children: " ? - Toggle this help" })
184
+ ]
185
+ }
186
+ )
187
+ ] });
188
+ }
189
+ function renderGitChanges(repoInfo) {
190
+ const { staged, unstaged, untracked, ahead, behind } = repoInfo.status;
191
+ const totalChanges = staged.length + unstaged.length + untracked.length;
192
+ return /* @__PURE__ */ jsxs3(Box3, { flexDirection: "column", children: [
193
+ totalChanges > 0 ? /* @__PURE__ */ jsxs3(Fragment, { children: [
194
+ /* @__PURE__ */ jsx3(
195
+ StatusRow,
196
+ {
197
+ label: "Changes:",
198
+ value: /* @__PURE__ */ jsxs3(Text3, { color: "yellow", children: [
199
+ totalChanges,
200
+ " file(s)"
201
+ ] })
202
+ }
203
+ ),
204
+ staged.length > 0 && /* @__PURE__ */ jsx3(Box3, { paddingLeft: 2, children: /* @__PURE__ */ jsxs3(Text3, { color: "green", children: [
205
+ "Staged: ",
206
+ staged.length
207
+ ] }) }),
208
+ unstaged.length > 0 && /* @__PURE__ */ jsx3(Box3, { paddingLeft: 2, children: /* @__PURE__ */ jsxs3(Text3, { color: "yellow", children: [
209
+ "Unstaged: ",
210
+ unstaged.length
211
+ ] }) }),
212
+ untracked.length > 0 && /* @__PURE__ */ jsx3(Box3, { paddingLeft: 2, children: /* @__PURE__ */ jsxs3(Text3, { color: "gray", children: [
213
+ "Untracked: ",
214
+ untracked.length
215
+ ] }) })
216
+ ] }) : /* @__PURE__ */ jsx3(StatusRow, { label: "Changes:", value: /* @__PURE__ */ jsx3(Text3, { color: "green", children: "Clean working tree" }) }),
217
+ (ahead > 0 || behind > 0) && /* @__PURE__ */ jsx3(
218
+ StatusRow,
219
+ {
220
+ label: "Sync:",
221
+ value: /* @__PURE__ */ jsxs3(Text3, { color: "yellow", children: [
222
+ ahead > 0 ? `\u2191${ahead}` : "",
223
+ " ",
224
+ behind > 0 ? `\u2193${behind}` : ""
225
+ ] })
226
+ }
227
+ )
228
+ ] });
229
+ }
230
+ export {
231
+ StatusDashboard
232
+ };
@@ -0,0 +1,7 @@
1
+ import {
2
+ apiService
3
+ } from "./chunk-W4JGOGR7.js";
4
+ import "./chunk-RHMTZK2J.js";
5
+ export {
6
+ apiService
7
+ };