opencode-workaholic 0.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.
@@ -0,0 +1,108 @@
1
+ #!/usr/bin/env python3
2
+ import json
3
+ import os
4
+ import sys
5
+ import time
6
+
7
+ TIMER_DIR = "/tmp/workaholic_timers"
8
+
9
+ def get_timer_file(session_id):
10
+ os.makedirs(TIMER_DIR, exist_ok=True)
11
+ return os.path.join(TIMER_DIR, f"{session_id}.json")
12
+
13
+ def load_timer(session_id):
14
+ timer_file = get_timer_file(session_id)
15
+ if os.path.exists(timer_file):
16
+ with open(timer_file, "r") as f:
17
+ return json.load(f)
18
+ return None
19
+
20
+ def save_timer(data, session_id):
21
+ timer_file = get_timer_file(session_id)
22
+ with open(timer_file, "w") as f:
23
+ json.dump(data, f)
24
+
25
+ def start(seconds, session_id):
26
+ data = {
27
+ "start_time": time.time(),
28
+ "min_duration": seconds,
29
+ "started": True,
30
+ "session_id": session_id
31
+ }
32
+ save_timer(data, session_id)
33
+ print(f"Timer started: {seconds}s, session: {session_id}")
34
+
35
+ def status(session_id):
36
+ data = load_timer(session_id)
37
+ if not data or not data.get("started"):
38
+ print(f"Timer not started (session: {session_id})")
39
+ return
40
+
41
+ elapsed = time.time() - data["start_time"]
42
+ remaining = max(0, data["min_duration"] - elapsed)
43
+ can_end = elapsed >= data["min_duration"]
44
+
45
+ print(f"Session: {session_id}")
46
+ print(f" Min: {data['min_duration']}s, Elapsed: {elapsed:.1f}s, Remaining: {remaining:.1f}s")
47
+ print(f" Can end: {'YES' if can_end else 'NO'}")
48
+
49
+ def can_end(session_id):
50
+ data = load_timer(session_id)
51
+ if not data or not data.get("started"):
52
+ print("1")
53
+ return
54
+
55
+ elapsed = time.time() - data["start_time"]
56
+ print("1" if elapsed >= data["min_duration"] else "0")
57
+
58
+ def remaining(session_id):
59
+ data = load_timer(session_id)
60
+ if not data or not data.get("started"):
61
+ print("0")
62
+ return
63
+
64
+ elapsed = time.time() - data["start_time"]
65
+ print(max(0, int(data["min_duration"] - elapsed)))
66
+
67
+ def stop(session_id):
68
+ timer_file = get_timer_file(session_id)
69
+ if os.path.exists(timer_file):
70
+ os.remove(timer_file)
71
+ print(f"Timer stopped (session: {session_id})")
72
+
73
+ def main():
74
+ if len(sys.argv) < 2:
75
+ print(__doc__)
76
+ sys.exit(1)
77
+
78
+ command = sys.argv[1]
79
+
80
+ if command == "start":
81
+ if len(sys.argv) < 3:
82
+ print("Usage: timer.py start <seconds> [session_id]")
83
+ sys.exit(1)
84
+ try:
85
+ seconds = int(sys.argv[2])
86
+ session_id = sys.argv[3] if len(sys.argv) > 3 else "default"
87
+ start(seconds, session_id)
88
+ except ValueError:
89
+ print("Duration must be an integer")
90
+ sys.exit(1)
91
+ else:
92
+ session_id = sys.argv[2] if len(sys.argv) > 2 else "default"
93
+
94
+ if command == "status":
95
+ status(session_id)
96
+ elif command == "can-end":
97
+ can_end(session_id)
98
+ elif command == "remaining":
99
+ remaining(session_id)
100
+ elif command == "stop":
101
+ stop(session_id)
102
+ else:
103
+ print(f"Unknown command: {command}")
104
+ print(__doc__)
105
+ sys.exit(1)
106
+
107
+ if __name__ == "__main__":
108
+ main()
package/package.json ADDED
@@ -0,0 +1,57 @@
1
+ {
2
+ "name": "opencode-workaholic",
3
+ "version": "0.2.0",
4
+ "description": "Your OpenCode becomes a workaholic. Prevents AI from ending tasks prematurely.",
5
+ "author": {
6
+ "name": "Roderick Qiu",
7
+ "email": "scrisqiu@hotmail.com",
8
+ "url": "https://r-q.name"
9
+ },
10
+ "license": "MIT",
11
+ "type": "module",
12
+ "main": "./dist/index.js",
13
+ "types": "./dist/index.d.ts",
14
+ "exports": {
15
+ ".": {
16
+ "types": "./dist/index.d.ts",
17
+ "default": "./dist/index.js"
18
+ }
19
+ },
20
+ "repository": {
21
+ "type": "git",
22
+ "url": "git+https://github.com/RoderickQiu/opencode-workaholic.git"
23
+ },
24
+ "bugs": {
25
+ "url": "https://github.com/RoderickQiu/opencode-workaholic/issues"
26
+ },
27
+ "keywords": ["opencode", "plugin", "workaholic", "ai", "productivity", "timer", "focus", "deep-work", "task-duration", "task-management", "enforcement", "concentration", "agent", "automation"],
28
+ "homepage": "https://github.com/RoderickQiu/opencode-workaholic#readme",
29
+ "publishConfig": {
30
+ "access": "public",
31
+ "provenance": true
32
+ },
33
+ "files": [
34
+ "dist",
35
+ "scripts"
36
+ ],
37
+ "scripts": {
38
+ "build": "bun build ./src/index.ts --outdir dist --target bun && cp -r src/command dist/ && cp -r scripts dist/"
39
+ },
40
+ "dependencies": {
41
+ "@opencode-ai/plugin": "1.0.85",
42
+ "zod": "^3.24.0"
43
+ },
44
+ "devDependencies": {
45
+ "@eslint/js": "^9.39.1",
46
+ "@types/node": "^20.11.5",
47
+ "@typescript-eslint/eslint-plugin": "8.47.0",
48
+ "@typescript-eslint/parser": "8.47.0",
49
+ "bun-types": "latest",
50
+ "eslint": "^9.39.1",
51
+ "eslint-config-prettier": "10.1.8",
52
+ "eslint-plugin-prettier": "^5.1.3",
53
+ "prettier": "^3.2.4",
54
+ "typescript-eslint": "^8.47.0",
55
+ "vitest": "^3.2.4"
56
+ }
57
+ }
@@ -0,0 +1,108 @@
1
+ #!/usr/bin/env python3
2
+ import json
3
+ import os
4
+ import sys
5
+ import time
6
+
7
+ TIMER_DIR = "/tmp/workaholic_timers"
8
+
9
+ def get_timer_file(session_id):
10
+ os.makedirs(TIMER_DIR, exist_ok=True)
11
+ return os.path.join(TIMER_DIR, f"{session_id}.json")
12
+
13
+ def load_timer(session_id):
14
+ timer_file = get_timer_file(session_id)
15
+ if os.path.exists(timer_file):
16
+ with open(timer_file, "r") as f:
17
+ return json.load(f)
18
+ return None
19
+
20
+ def save_timer(data, session_id):
21
+ timer_file = get_timer_file(session_id)
22
+ with open(timer_file, "w") as f:
23
+ json.dump(data, f)
24
+
25
+ def start(seconds, session_id):
26
+ data = {
27
+ "start_time": time.time(),
28
+ "min_duration": seconds,
29
+ "started": True,
30
+ "session_id": session_id
31
+ }
32
+ save_timer(data, session_id)
33
+ print(f"Timer started: {seconds}s, session: {session_id}")
34
+
35
+ def status(session_id):
36
+ data = load_timer(session_id)
37
+ if not data or not data.get("started"):
38
+ print(f"Timer not started (session: {session_id})")
39
+ return
40
+
41
+ elapsed = time.time() - data["start_time"]
42
+ remaining = max(0, data["min_duration"] - elapsed)
43
+ can_end = elapsed >= data["min_duration"]
44
+
45
+ print(f"Session: {session_id}")
46
+ print(f" Min: {data['min_duration']}s, Elapsed: {elapsed:.1f}s, Remaining: {remaining:.1f}s")
47
+ print(f" Can end: {'YES' if can_end else 'NO'}")
48
+
49
+ def can_end(session_id):
50
+ data = load_timer(session_id)
51
+ if not data or not data.get("started"):
52
+ print("1")
53
+ return
54
+
55
+ elapsed = time.time() - data["start_time"]
56
+ print("1" if elapsed >= data["min_duration"] else "0")
57
+
58
+ def remaining(session_id):
59
+ data = load_timer(session_id)
60
+ if not data or not data.get("started"):
61
+ print("0")
62
+ return
63
+
64
+ elapsed = time.time() - data["start_time"]
65
+ print(max(0, int(data["min_duration"] - elapsed)))
66
+
67
+ def stop(session_id):
68
+ timer_file = get_timer_file(session_id)
69
+ if os.path.exists(timer_file):
70
+ os.remove(timer_file)
71
+ print(f"Timer stopped (session: {session_id})")
72
+
73
+ def main():
74
+ if len(sys.argv) < 2:
75
+ print(__doc__)
76
+ sys.exit(1)
77
+
78
+ command = sys.argv[1]
79
+
80
+ if command == "start":
81
+ if len(sys.argv) < 3:
82
+ print("Usage: timer.py start <seconds> [session_id]")
83
+ sys.exit(1)
84
+ try:
85
+ seconds = int(sys.argv[2])
86
+ session_id = sys.argv[3] if len(sys.argv) > 3 else "default"
87
+ start(seconds, session_id)
88
+ except ValueError:
89
+ print("Duration must be an integer")
90
+ sys.exit(1)
91
+ else:
92
+ session_id = sys.argv[2] if len(sys.argv) > 2 else "default"
93
+
94
+ if command == "status":
95
+ status(session_id)
96
+ elif command == "can-end":
97
+ can_end(session_id)
98
+ elif command == "remaining":
99
+ remaining(session_id)
100
+ elif command == "stop":
101
+ stop(session_id)
102
+ else:
103
+ print(f"Unknown command: {command}")
104
+ print(__doc__)
105
+ sys.exit(1)
106
+
107
+ if __name__ == "__main__":
108
+ main()