coffee-time 1.0.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/LICENSE +21 -0
- package/README.md +42 -0
- package/bin/coffee-time.js +135 -0
- package/package.json +16 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Troy
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# coffee-time
|
|
2
|
+
|
|
3
|
+
A lightweight CLI that schedules recurring coffee breaks while you code.
|
|
4
|
+
|
|
5
|
+
## Why
|
|
6
|
+
Long uninterrupted work sessions hurt focus. `coffee-time` keeps a reliable, low-noise loop that nudges you to step away at predictable intervals.
|
|
7
|
+
|
|
8
|
+
## Install
|
|
9
|
+
```
|
|
10
|
+
npm install -g .
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Usage
|
|
14
|
+
Start a continuous break loop with a required interval (minutes):
|
|
15
|
+
```
|
|
16
|
+
coffee-time start --interval 45
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
### What happens
|
|
20
|
+
- Prints a single startup line: `Coffee breaks scheduled every 45 minutes. Press Ctrl+C to stop.`
|
|
21
|
+
- Waits 45 minutes, then triggers a break notification.
|
|
22
|
+
- Immediately schedules the next interval and repeats until you stop it (Ctrl+C).
|
|
23
|
+
|
|
24
|
+
### Interval rules
|
|
25
|
+
- Integer minutes only, must be >= 1.
|
|
26
|
+
- Invalid values print a clear error to stderr and exit with code `2`.
|
|
27
|
+
|
|
28
|
+
### Notifications
|
|
29
|
+
- Always prints a console message on each break: `☕ Time for a coffee break! (45 min interval)`.
|
|
30
|
+
- Attempts a desktop notification where available:
|
|
31
|
+
- macOS: `osascript -e 'display notification ...'`
|
|
32
|
+
- Linux: `notify-send` (if installed)
|
|
33
|
+
- Windows: toast via PowerShell (best-effort)
|
|
34
|
+
- Missing notification tools never crash the app; console output is the fallback.
|
|
35
|
+
|
|
36
|
+
### Stopping
|
|
37
|
+
Press `Ctrl+C` to stop cleanly. Exits with code `0` and prints `Stopped. Stay fresh ☕`.
|
|
38
|
+
|
|
39
|
+
### Exit codes
|
|
40
|
+
- `0` — normal exit (including Ctrl+C)
|
|
41
|
+
- `2` — usage/argument error
|
|
42
|
+
- `>0` — unexpected runtime error
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const { spawn } = require('child_process');
|
|
4
|
+
|
|
5
|
+
const USAGE_ERROR = 2;
|
|
6
|
+
|
|
7
|
+
function printUsage() {
|
|
8
|
+
console.error('Usage: coffee-time start --interval <minutes>');
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function parseArgs(argv) {
|
|
12
|
+
const args = argv.slice(2);
|
|
13
|
+
|
|
14
|
+
if (args.length === 0) {
|
|
15
|
+
printUsage();
|
|
16
|
+
process.exit(USAGE_ERROR);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const [command, ...rest] = args;
|
|
20
|
+
if (command !== 'start') {
|
|
21
|
+
console.error(`Unknown command: ${command}`);
|
|
22
|
+
printUsage();
|
|
23
|
+
process.exit(USAGE_ERROR);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
let intervalMinutes;
|
|
27
|
+
for (let i = 0; i < rest.length; i += 1) {
|
|
28
|
+
const token = rest[i];
|
|
29
|
+
if (token === '--interval') {
|
|
30
|
+
intervalMinutes = rest[i + 1];
|
|
31
|
+
i += 1;
|
|
32
|
+
} else {
|
|
33
|
+
console.error(`Unknown option: ${token}`);
|
|
34
|
+
printUsage();
|
|
35
|
+
process.exit(USAGE_ERROR);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
if (intervalMinutes === undefined) {
|
|
40
|
+
console.error('Missing required option: --interval <minutes>');
|
|
41
|
+
printUsage();
|
|
42
|
+
process.exit(USAGE_ERROR);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const parsedInterval = Number(intervalMinutes);
|
|
46
|
+
if (!Number.isInteger(parsedInterval) || parsedInterval < 1) {
|
|
47
|
+
console.error('Invalid interval. Provide an integer value >= 1 minute.');
|
|
48
|
+
process.exit(USAGE_ERROR);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return parsedInterval;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function sendDesktopNotification(message) {
|
|
55
|
+
const platform = process.platform;
|
|
56
|
+
|
|
57
|
+
if (platform === 'darwin') {
|
|
58
|
+
return attemptNotification('osascript', [
|
|
59
|
+
'-e',
|
|
60
|
+
`display notification "${message}" with title "coffee-time"`,
|
|
61
|
+
]);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (platform === 'linux') {
|
|
65
|
+
return attemptNotification('notify-send', ['coffee-time', message]);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if (platform === 'win32') {
|
|
69
|
+
const script = `Add-Type -AssemblyName System.Windows.Forms;` +
|
|
70
|
+
`$notify = New-Object System.Windows.Forms.NotifyIcon;` +
|
|
71
|
+
`$notify.Icon = [System.Drawing.SystemIcons]::Information;` +
|
|
72
|
+
`$notify.Visible = $true;` +
|
|
73
|
+
`$notify.ShowBalloonTip(10000, 'coffee-time', '${message}', [System.Windows.Forms.ToolTipIcon]::Info);`;
|
|
74
|
+
return attemptNotification('powershell.exe', ['-NoProfile', '-Command', script]);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return Promise.resolve();
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function attemptNotification(command, args) {
|
|
81
|
+
return new Promise((resolve) => {
|
|
82
|
+
const child = spawn(command, args, { stdio: 'ignore' });
|
|
83
|
+
|
|
84
|
+
child.on('error', () => resolve());
|
|
85
|
+
child.on('exit', () => resolve());
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function notify(intervalMinutes) {
|
|
90
|
+
const message = `Time for a coffee break! (${intervalMinutes} min interval)`;
|
|
91
|
+
console.log(`☕ ${message}`);
|
|
92
|
+
sendDesktopNotification(message).catch(() => {});
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function startLoop(intervalMinutes) {
|
|
96
|
+
console.log(`Coffee breaks scheduled every ${intervalMinutes} minutes. Press Ctrl+C to stop.`);
|
|
97
|
+
|
|
98
|
+
const intervalMs = intervalMinutes * 60 * 1000;
|
|
99
|
+
let nextTime = Date.now() + intervalMs;
|
|
100
|
+
let timeoutId = null;
|
|
101
|
+
|
|
102
|
+
function scheduleNext() {
|
|
103
|
+
const delay = Math.max(0, nextTime - Date.now());
|
|
104
|
+
timeoutId = setTimeout(() => {
|
|
105
|
+
notify(intervalMinutes);
|
|
106
|
+
nextTime += intervalMs;
|
|
107
|
+
scheduleNext();
|
|
108
|
+
}, delay);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
scheduleNext();
|
|
112
|
+
|
|
113
|
+
const handleExit = () => {
|
|
114
|
+
if (timeoutId) {
|
|
115
|
+
clearTimeout(timeoutId);
|
|
116
|
+
}
|
|
117
|
+
console.log('\nStopped. Stay fresh ☕');
|
|
118
|
+
process.exit(0);
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
process.on('SIGINT', handleExit);
|
|
122
|
+
process.on('SIGTERM', handleExit);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function main() {
|
|
126
|
+
try {
|
|
127
|
+
const intervalMinutes = parseArgs(process.argv);
|
|
128
|
+
startLoop(intervalMinutes);
|
|
129
|
+
} catch (error) {
|
|
130
|
+
console.error('Unexpected error:', error.message);
|
|
131
|
+
process.exit(1);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
main();
|
package/package.json
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "coffee-time",
|
|
3
|
+
"publishConfig": {
|
|
4
|
+
"access": "public"
|
|
5
|
+
},
|
|
6
|
+
"version": "1.0.0",
|
|
7
|
+
"description": "Lightweight CLI to schedule coffee breaks at a fixed interval.",
|
|
8
|
+
"bin": {
|
|
9
|
+
"coffee-time": "./bin/coffee-time.js"
|
|
10
|
+
},
|
|
11
|
+
"license": "MIT",
|
|
12
|
+
"type": "commonjs",
|
|
13
|
+
"scripts": {
|
|
14
|
+
"start": "node ./bin/coffee-time.js"
|
|
15
|
+
}
|
|
16
|
+
}
|