@gowelle/stint-agent 1.1.0 → 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
 
@@ -118,11 +124,13 @@ For comprehensive troubleshooting help, see the **[Troubleshooting Guide](docs/T
118
124
  ### Quick Tips
119
125
 
120
126
  **"Not authenticated" error**
127
+
121
128
  ```bash
122
129
  stint login
123
130
  ```
124
131
 
125
132
  **Daemon won't start**
133
+
126
134
  ```bash
127
135
  stint daemon status # Check if already running
128
136
  stint daemon logs # Check logs for errors
@@ -130,6 +138,7 @@ stint daemon restart # Restart daemon
130
138
  ```
131
139
 
132
140
  **For detailed solutions**, including:
141
+
133
142
  - Connection issues (WebSocket, API, Circuit Breaker)
134
143
  - Daemon problems (crashes, autostart)
135
144
  - Authentication errors
@@ -142,13 +151,14 @@ See the **[Troubleshooting Guide](docs/TROUBLESHOOTING.md)**.
142
151
 
143
152
  Logs are stored in your system's config directory:
144
153
 
145
- | Platform | Log Location |
146
- |----------|--------------|
147
- | **macOS** | `~/.config/stint/logs/` |
148
- | **Linux** | `~/.config/stint/logs/` |
154
+ | Platform | Log Location |
155
+ | ----------- | ----------------------------------- |
156
+ | **macOS** | `~/.config/stint/logs/` |
157
+ | **Linux** | `~/.config/stint/logs/` |
149
158
  | **Windows** | `%USERPROFILE%\.config\stint\logs\` |
150
159
 
151
160
  Log files:
161
+
152
162
  - `agent.log` - General CLI operations
153
163
  - `daemon.log` - Daemon process logs
154
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
+ };