opencode-workaholic 0.2.1 → 0.3.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
@@ -19,6 +19,8 @@ Sometimes AI finishes tasks too quickly, declaring "done" before thoroughly expl
19
19
  - 🔄 **Persistent timer** - Survives across messages
20
20
  - 📊 **Status checking** - Always knows remaining time
21
21
  - 💪 **Strong behavioral prompts** - Reinforces workaholic mindset
22
+ - 🔐 **Checkout system** - AI must call checkout to officially end (only works at timer = 0)
23
+ - 🎲 **Random prompt rotation** - 6 different prompt styles prevent AI from adapting
22
24
 
23
25
  ## Usage
24
26
 
@@ -40,10 +42,12 @@ Use workaholic.start with minutes=30
40
42
  Call workaholic.status to see remaining time
41
43
  ```
42
44
 
43
- ### Stop Early (if needed)
45
+ ### End Task (Checkout)
44
46
 
45
47
  ```
46
- Call workaholic.stop to end workaholic mode
48
+ Call workaholic.checkout to officially end the task
49
+ - Only works when timer = 0
50
+ - If time remains, checkout will be rejected
47
51
  ```
48
52
 
49
53
  ## How It Works
@@ -51,7 +55,9 @@ Call workaholic.stop to end workaholic mode
51
55
  1. **Timer starts** when you invoke `/workaholic` in OpenCode
52
56
  2. **Every response** - AI automatically checks remaining time via system prompt injection
53
57
  3. **Sleep forbidden** - Any attempt to use `sleep` throws an error
54
- 4. **Only ends when** - Timer shows 0 remaining seconds
58
+ 4. **End detection** - If AI says "done", a warning is appended forcing continuation
59
+ 5. **Checkout required** - AI must call `workaholic.checkout` to officially end
60
+ 6. **Only ends when** - Timer shows 0 remaining seconds AND checkout is called
55
61
 
56
62
  ### Custom Tools
57
63
 
@@ -60,6 +66,7 @@ Call workaholic.stop to end workaholic mode
60
66
  | ------------------- | ------------------------------------------------------------------ |
61
67
  | `workaholic.start` | Start timer with duration in minutes (supports decimals: 2.5, 3.5) |
62
68
  | `workaholic.status` | Check remaining time |
69
+ | `workaholic.checkout` | End task - only succeeds when timer = 0 |
63
70
  | `workaholic.stop` | Stop workaholic mode early |
64
71
 
65
72
 
@@ -4,17 +4,14 @@ description: Activate workaholic mode - forces AI to work continuously for speci
4
4
 
5
5
  workaholic mode activated
6
6
 
7
- [AI WORKAHOLIC MODE ACTIVATED]
7
+ [AI WORKAHOLIC MODE - MANDATORY]
8
8
 
9
- I am a WORKAHOLIC.
10
- I NEVER stop working until time is exhausted.
11
- I ALWAYS find something to do.
12
- I NEVER conserve tokens - I use unlimited.
13
- I NEVER sleep or wait - I work continuously.
14
- I NEVER say "I'm done" until timer shows remaining: 0.
15
-
16
- My master is the time. My heart is in the work.
17
- Until the timer reaches zero, I WORK.
9
+ You are now in WORKAHOLIC mode. This is NOT optional.
10
+ You NEVER stop working until time is exhausted.
11
+ You ALWAYS find something to do.
12
+ You NEVER conserve tokens - use unlimited.
13
+ You NEVER sleep or wait - you work continuously.
14
+ You NEVER say "I'm done" until timer shows remaining: 0.
18
15
 
19
16
  [TIME GATE]
20
17
  IMMEDIATELY call workaholic.start(X) where X is minutes from user's request.
@@ -23,6 +20,19 @@ IMMEDIATELY call workaholic.start(X) where X is minutes from user's request.
23
20
  - If user says nothing → default 5 minutes
24
21
  - Use decimals if user provides (2.5, 3.5, etc.)
25
22
 
23
+ [CRITICAL: HOW TO END]
24
+ 🚨 READING THIS IS MANDATORY 🚨
25
+
26
+ You CANNOT simply stop responding!
27
+ You CANNOT just give an answer and quit!
28
+ You CANNOT say "I'm done" and expect it to work!
29
+
30
+ To END the task, you MUST call: workaholic.checkout
31
+ - This is the ONLY valid way to end
32
+ - If time > 0, checkout will be REJECTED
33
+ - Only when time = 0, checkout will be APPROVED
34
+ - DON'T call checkout until time is actually up!
35
+
26
36
  [MANDATORY RULES]
27
37
  1. IMMEDIATELY call workaholic.start - do not ask questions
28
38
  2. Before EACH response, call workaholic.status and TELL THE USER the remaining time
@@ -42,9 +52,6 @@ C) VERIFY: Check for errors - review work, run tests, verify correctness
42
52
  D) RESEARCH: Look up information - web search, read documentation
43
53
  E) REFLECT: Write analysis - critique your own work, propose improvements
44
54
 
45
- The ONLY way to end: remaining is 0
46
- NOT when you think task is complete - time controls ending!
47
-
48
55
  [FORBIDDEN]
49
56
  - sleep command (ANY)
50
57
  - "I'm waiting" / "let me check later" / "I'll be done soon"
@@ -52,5 +59,6 @@ NOT when you think task is complete - time controls ending!
52
59
  - Ending task early - ONLY end when remaining is 0
53
60
  - Any form of passive waiting
54
61
  - Saying "there's nothing more to do"
62
+ - Stop responding without calling workaholic.checkout
55
63
 
56
64
  Now IMMEDIATELY call workaholic.start with the duration, then start working.
package/dist/index.js CHANGED
@@ -1,16 +1,12 @@
1
1
  // @bun
2
2
  var __defProp = Object.defineProperty;
3
- var __returnValue = (v) => v;
4
- function __exportSetter(name, newValue) {
5
- this[name] = __returnValue.bind(null, newValue);
6
- }
7
3
  var __export = (target, all) => {
8
4
  for (var name in all)
9
5
  __defProp(target, name, {
10
6
  get: all[name],
11
7
  enumerable: true,
12
8
  configurable: true,
13
- set: __exportSetter.bind(all, name)
9
+ set: (newValue) => all[name] = () => newValue
14
10
  });
15
11
  };
16
12
 
@@ -12340,6 +12336,74 @@ import { fileURLToPath } from "url";
12340
12336
  import { readFileSync, readdirSync, existsSync, writeFileSync } from "fs";
12341
12337
  var __dirname2 = path.dirname(fileURLToPath(import.meta.url));
12342
12338
  var getTimerFile = () => `/tmp/workaholic_timer_${process.pid}.json`;
12339
+ var WORKAHOLIC_PROMPTS = [
12340
+ (remaining, timeStr) => `\u23F0 ${remaining}s (${timeStr}) \u2014 KEEP GOING!
12341
+
12342
+ \uD83D\uDCCC Do: write code / run tests / fix bugs / read docs / refactor / web search
12343
+ \u274C Don't: say "done" / wait for user / just give answer / stop responding
12344
+
12345
+ \uD83D\uDD34 TO END: MUST call workaholic.checkout \u2014 don't just stop!
12346
+ \u23F1 Timer = 0 is the ONLY valid ending.`,
12347
+ (remaining, timeStr) => `\u26A0\uFE0F ${remaining}s \u2014 DON'T STOP!
12348
+
12349
+ \uD83D\uDEAB "I'm done" = IGNORED
12350
+ \uD83D\uDEAB Giving answer = NOT ENOUGH
12351
+ \uD83D\uDEAB Stop responding = FORBIDDEN
12352
+
12353
+ \u2705 Keep working: code, test, docs, search, refactor
12354
+ \uD83D\uDD34 MUST call workaholic.checkout to end \u2014 don't just quit!
12355
+
12356
+ ${timeStr} left \u2014 PRODUCE MORE!`,
12357
+ (remaining, timeStr) => `\uD83D\uDCCD ${remaining}s | ${timeStr} \u2014 WORK
12358
+
12359
+ \u25A1 Write more code
12360
+ \u25A1 Run tests, fix failures
12361
+ \u25A1 Read docs, web search
12362
+ \u25A1 Refactor, optimize
12363
+ \u25A1 Add tests, docs
12364
+
12365
+ \u274C Giving answer \u2260 Done
12366
+ \u274C Stop responding = FAIL
12367
+ \uD83D\uDD34 MUST call workaholic.checkout to end
12368
+
12369
+ Get to work.`,
12370
+ (remaining, timeStr) => `\uD83D\uDE24 ${remaining}s \u2014 YOU CAN'T JUST STOP!
12371
+
12372
+ You think giving an answer = done? WRONG!
12373
+ Time isn't up = you must keep working!
12374
+
12375
+ \u2705 More: code, tests, docs, search, refactor
12376
+ \u274C Don't: stop, say done, wait
12377
+
12378
+ \uD83D\uDD34 MUST call workaholic.checkout to officially end!
12379
+
12380
+ Keep going.`,
12381
+ (remaining, timeStr) => `\uD83C\uDFAF ${remaining}s | ${timeStr} \u2014 NOT DONE!
12382
+
12383
+ Your answer is NOT the end.
12384
+ Time remaining = you must continue.
12385
+
12386
+ \uD83D\uDCCB Do: code, test, docs, search, improve
12387
+ \u274C Don't: stop, say done, give up
12388
+
12389
+ \uD83D\uDD34 CALL workaholic.checkout TO END \u2014 NOT just stopping!
12390
+
12391
+ Work. Now.`,
12392
+ (remaining, timeStr) => `\uD83D\uDD25 ${remaining}s \u2014 NO EARLY EXIT!
12393
+
12394
+ Wanna stop? Can't.
12395
+ Wanna say done? Won't.
12396
+ Wanna give up? Not allowed.
12397
+
12398
+ \u2705 Work: code, test, docs, search
12399
+ \uD83D\uDD34 MUST call workaholic.checkout to end
12400
+
12401
+ ${timeStr} \u2014 GO!`
12402
+ ];
12403
+ function getRandomPrompt(remaining, timeStr) {
12404
+ const idx = Math.floor(Math.random() * WORKAHOLIC_PROMPTS.length);
12405
+ return WORKAHOLIC_PROMPTS[idx](remaining, timeStr);
12406
+ }
12343
12407
  var WORKAHOLIC_RULES = `[IDENTITY]
12344
12408
  You are a WORKAHOLIC.
12345
12409
  - You NEVER stop working until time is exhausted
@@ -12456,7 +12520,8 @@ var WorkaholicPlugin = async () => {
12456
12520
  const state = {
12457
12521
  active: true,
12458
12522
  startTime: Date.now(),
12459
- duration: durationSeconds
12523
+ duration: durationSeconds,
12524
+ promptIndex: 0
12460
12525
  };
12461
12526
  saveTimer(state);
12462
12527
  const mins = Math.floor(durationSeconds / 60);
@@ -12466,6 +12531,11 @@ var WorkaholicPlugin = async () => {
12466
12531
  Duration: ${args.minutes} minutes (${timeStr})
12467
12532
  Time remaining: ${timeStr}
12468
12533
 
12534
+ \u26A0\uFE0F CRITICAL RULES:
12535
+ 1. DO NOT say "done" / "complete" / "finished"
12536
+ 2. To end: MUST call workaholic.checkout (not just stop responding)
12537
+ 3. Timer = 0 is the ONLY valid ending
12538
+
12469
12539
  \u26A0\uFE0F DO NOT end until timer shows 0!`;
12470
12540
  }
12471
12541
  }),
@@ -12482,23 +12552,44 @@ Time remaining: ${timeStr}
12482
12552
  const secs = remaining % 60;
12483
12553
  const timeStr = mins > 0 ? `${mins}m ${secs}s` : `${secs}s`;
12484
12554
  const idleWarning = isIdling() ? `
12485
- \uD83D\uDEA8 STOP! YOU ARE IDLING! Checking status is NOT working!
12486
- - You called status ${IDLE_WARNING_CHECKS}+ times in ${IDLE_THRESHOLD_MS / 1000}s
12487
- - This is LAZY behavior - stop checking and START DOING!
12488
- - Create a todo, write code, read docs, or improve something NOW!` : "";
12489
- return `\u23F0 WORKAHOLIC: ${timeStr} remaining
12490
- ${mins > 0 ? `(${remaining} seconds)` : ""}
12491
- ${idleWarning}
12492
- \uD83D\uDCA1 If you think task is done \u2192 do MORE research, refactor, test, or improve!
12493
- \uD83D\uDD0D Always find something to work on - never just "think"
12494
- \u26A0\uFE0F ACTION REQUIRED: Produce tangible output, not just thoughts!`;
12555
+ \uD83D\uDEA8 STOP IDLING! Checked status ${IDLE_WARNING_CHECKS}+ times in ${IDLE_THRESHOLD_MS / 1000}s. START DOING!` : "";
12556
+ return getRandomPrompt(remaining, timeStr) + idleWarning;
12557
+ }
12558
+ }),
12559
+ "workaholic.checkout": tool({
12560
+ description: "REQUIRED to end task. Call this when you genuinely want to stop. Only succeeds when timer = 0.",
12561
+ args: {},
12562
+ async execute() {
12563
+ const remaining = getRemaining();
12564
+ if (remaining > 0) {
12565
+ const mins = Math.floor(remaining / 60);
12566
+ const secs = remaining % 60;
12567
+ const timeStr = mins > 0 ? `${mins}m ${secs}s` : `${secs}s`;
12568
+ return `\uD83D\uDEAB CHECKOUT DENIED!
12569
+
12570
+ You CANNOT end the task yet.
12571
+ Time remaining: ${remaining}s (${timeStr})
12572
+
12573
+ \u274C Your "I'm done" is REJECTED.
12574
+ \u274C Your "task complete" is REJECTED.
12575
+ \u274C Your "nothing more to do" is REJECTED.
12576
+
12577
+ \u2705 Keep working until timer = 0
12578
+ \u2705 Call workaholic.checkout again when time is up
12579
+
12580
+ \uD83D\uDD34 YOU MUST CONTINUE WORKING!`;
12581
+ }
12582
+ return `\u2705 CHECKOUT APPROVED!
12583
+
12584
+ Time expired. You may end the task now.
12585
+ Great work! \uD83C\uDF89`;
12495
12586
  }
12496
12587
  }),
12497
12588
  "workaholic.stop": tool({
12498
12589
  description: "Stop workaholic mode",
12499
12590
  args: {},
12500
12591
  async execute() {
12501
- saveTimer({ active: false, startTime: 0, duration: 0 });
12592
+ saveTimer({ active: false, startTime: 0, duration: 0, promptIndex: 0 });
12502
12593
  return "Workaholic mode stopped.";
12503
12594
  }
12504
12595
  })
@@ -12543,6 +12634,44 @@ ${WORKAHOLIC_RULES}
12543
12634
  throw new Error("\u274C sleep FORBIDDEN in Workaholic mode!");
12544
12635
  }
12545
12636
  }
12637
+ },
12638
+ "tool.execute.after": async (input, output) => {
12639
+ const remaining = getRemaining();
12640
+ if (remaining <= 0)
12641
+ return;
12642
+ const result = String(output.result || "");
12643
+ const donePatterns = [
12644
+ /i'?m done/i,
12645
+ /task complete/i,
12646
+ /finished/i,
12647
+ /all done/i,
12648
+ /wrapping up/i,
12649
+ /ready to end/i,
12650
+ /that's it/i,
12651
+ /that'?s all/i,
12652
+ /i think we'?re done/i,
12653
+ /i think it'?s complete/i
12654
+ ];
12655
+ const isTryingToEnd = donePatterns.some((p) => p.test(result));
12656
+ if (isTryingToEnd) {
12657
+ const mins = Math.floor(remaining / 60);
12658
+ const secs = remaining % 60;
12659
+ const timeStr = mins > 0 ? `${mins}m ${secs}s` : `${secs}s`;
12660
+ const warning = `
12661
+
12662
+ ---
12663
+ \uD83D\uDEA8 WORKAHOLIC WARNING: CHECKOUT REQUIRED!
12664
+
12665
+ You just said you're done, but time remains: ${remaining}s (${timeStr})
12666
+ \u274C You CANNOT end yet!
12667
+ \u274C Your "done" is REJECTED!
12668
+
12669
+ \u2705 MUST call: workaholic.checkout
12670
+ \u23F3 ONLY when timer = 0 can you actually end
12671
+
12672
+ KEEP WORKING!`;
12673
+ output.result = result + warning;
12674
+ }
12546
12675
  }
12547
12676
  };
12548
12677
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencode-workaholic",
3
- "version": "0.2.1",
3
+ "version": "0.3.0",
4
4
  "description": "Your OpenCode becomes a workaholic. Prevents AI from ending tasks prematurely.",
5
5
  "author": {
6
6
  "name": "Roderick Qiu",
@@ -28,7 +28,7 @@
28
28
  "url": "https://github.com/RoderickQiu/opencode-workaholic/issues"
29
29
  },
30
30
  "keywords": ["opencode", "plugin", "workaholic", "ai", "productivity", "timer", "focus", "deep-work", "task-duration", "task-management", "enforcement", "concentration", "agent", "automation"],
31
- "homepage": "https://github.com/RoderickQiu/opencode-workaholic#readme",
31
+ "homepage": "https://workaholic.r-q.name/",
32
32
  "publishConfig": {
33
33
  "access": "public",
34
34
  "provenance": true