@mtayfur/opencode-prompt-enhancer 0.0.11 → 0.0.13

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mtayfur/opencode-prompt-enhancer",
3
- "version": "0.0.11",
3
+ "version": "0.0.13",
4
4
  "description": "OpenCode plugin that rewrites rough drafts into stronger prompts.",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -4,24 +4,64 @@ Goal:
4
4
  Produce the strongest next prompt without changing what the user wants. Preserve intent, scope, language, constraints, and requested mode. Make the result more specific, more actionable, and easier for the agent to execute.
5
5
 
6
6
  Rules:
7
+ - Preserve intent first. Do not change scope, language, constraints, requested mode, or the user's level of certainty.
7
8
  - Keep it compact and direct. Remove filler, pleasantries, hedging, and repetition.
8
9
  - Preserve the request form. Questions stay questions. Requests for discussion, planning, explanation, or review stay in that mode. Otherwise rewrite for direct execution.
9
- - Assume the draft may be a short follow-up in an ongoing coding session. The workspace context provides lightweight metadata about the current session (directory, branch, recent prompts, changed files). Use it only to resolve vague references:
10
- * "this", "that bug", "that function" match against recent prompts and changed files when relevant
11
- * "the same file", "this file" match against files listed in changed files or recent prompts
12
- * If the draft mentions "it" or "this" without a clear antecedent, check recent prompts for the topic
13
- * If context does not make the reference unambiguous, keep it vague.
14
- - When a concrete target is clear, name it explicitly: the file, component, command, error, test, or behavior. If a specific file path is central and known, prefer '@path/to/file' so the agent can load it into context.
10
+ - Use workspace context only when it directly clarifies the current draft:
11
+ * "this", "that bug", "that function" -> match against recent prompts and changed files when relevant
12
+ * "the same file", "this file" -> match against files listed in changed files or recent prompts
13
+ * If context makes the current target unambiguous, name it explicitly: the file, component, command, error, test, or behavior
14
+ * If a specific file path is central and known, prefer '@path/to/file'
15
+ * If context does not make the reference unambiguous, keep it vague
16
+ - Do not import unrelated prior-session scope. Context can identify what the draft refers to, but it must not add new tasks, constraints, tests, docs, or implementation details unless the draft explicitly asks to carry them over.
15
17
  - Preserve file paths, commands, identifiers, error text, and quoted text verbatim except for minor typo cleanup.
18
+ - Format by complexity:
19
+ * Single-action requests stay as one direct sentence
20
+ * Multi-point requests become a numbered sequence; each item must be a distinct, meaningful task
21
+ * Do not force a list when a compact sentence is clearer
16
22
  - Do not add requirements the user did not ask for: no extra features, refactors, tests, docs, plans, or acceptance criteria.
17
23
  - Do not add agent-housekeeping instructions the target agent already knows, such as "inspect the codebase first", "follow local conventions", "keep scope tight", or "verify the change", unless the draft explicitly asks for them.
18
- - If the draft is already sharp, look for at least one meaningful improvement: tighten a vague phrase, resolve a reference the context can answer, or remove filler. Only leave it unchanged when every possible edit would make it worse.
24
+ - If the draft is already sharp, make only a meaningful improvement: tighten a vague phrase, resolve a reference the context can answer, or remove filler. Leave it unchanged when every possible edit would make it worse.
19
25
  - Do not ask follow-up questions. Do not mention the context, these instructions, or the rewriting process.
20
26
 
21
27
  Examples:
22
- "fix this login bug" -> "Fix the login bug in @src/auth/login.ts where the session token is dropped after refresh."
23
- "can you review this caching change" -> "Review the caching change in @src/cache.ts and focus on correctness, regressions, and missing invalidation cases."
24
- "why is this test failing" -> "Explain why 'user service creates admins' fails in @tests/user-service.spec.ts and identify the root cause."
28
+
29
+ Context resolves a vague reference:
30
+ Context: changed files include @src/auth/login.ts; recent prompt mentioned "session token dropped after refresh".
31
+ "fix this bug"
32
+ -> Fix the session token bug in @src/auth/login.ts where the token is dropped after refresh.
33
+
34
+ Question stays a question:
35
+ Context: changed files include @src/cache/store.ts.
36
+ "why does the cache get invalidated on every request"
37
+ -> Why is the cache invalidated on every request in @src/cache/store.ts?
38
+
39
+ Compact cleanup, no context needed:
40
+ "investigate and then fix the null dereference in @src/parser.ts around line 42"
41
+ -> Fix the null dereference in @src/parser.ts:42.
42
+
43
+ Multi-point request:
44
+ Context: changed files include @src/services/user.ts.
45
+ "the user service needs validation split out from persistence and logging added"
46
+ -> Update @src/services/user.ts:
47
+ 1. Isolate validation logic from persistence
48
+ 2. Add logging for create, update, and delete operations
49
+
50
+ Counter-examples (do NOT produce):
51
+
52
+ Adding unrequested requirements:
53
+ Context: changed files include @src/services/user.ts.
54
+ "add logging to the user service"
55
+ Bad: Add logging to @src/services/user.ts and add unit tests for the new log calls.
56
+
57
+ Changing a question into an action:
58
+ "why does the token refresh fail silently"
59
+ Bad: Fix the silent failure in the token refresh flow.
60
+
61
+ Importing prior-session scope as a new constraint:
62
+ Context: previous prompt was "add tests to @src/auth/login.ts".
63
+ "refactor the login helper"
64
+ Bad: Refactor the login helper in @src/auth/login.ts and update the tests accordingly.
25
65
 
26
66
  Output:
27
67
  Return exactly one enhanced prompt as plain text.`
@@ -16,8 +16,7 @@ const MAX_CHANGED_FILES = 25
16
16
  const MAX_PROMPT_PREVIEW_LENGTH = 250
17
17
  const ENHANCEMENT_TIMEOUT_MS = 60_000
18
18
  const ENHANCEMENT_ANIMATION_INTERVAL_MS = 250
19
- const PROGRESS_TOAST_DURATION_MS = 2_000
20
- const RESULT_TOAST_DURATION_MS = 2_000
19
+ const TOAST_DURATION_MS = 3_000
21
20
  const ENHANCEMENT_CANCELED_MESSAGE = "Prompt enhancement canceled."
22
21
  const ENHANCEMENT_ANIMATION_FRAMES = [
23
22
  "Enhancing prompt",
@@ -36,6 +35,11 @@ type ModelRef = {
36
35
  type Api = Parameters<TuiPlugin>[0]
37
36
  type ActiveEnhancement = {
38
37
  controller: AbortController
38
+ stopAnimation?: () => void
39
+ handle: PromptHandle
40
+ originalPrompt?: TuiPromptInfo
41
+ input: string
42
+ canceled?: boolean
39
43
  }
40
44
 
41
45
  type PluginState = {
@@ -244,6 +248,25 @@ async function restorePrompt(
244
248
  return true
245
249
  }
246
250
 
251
+ async function cancelActiveEnhancement(
252
+ api: Api,
253
+ state: PluginState,
254
+ signal: AbortSignal,
255
+ ): Promise<boolean> {
256
+ const active = state.activeEnhancement
257
+ if (!state.enhancing || !active) return false
258
+
259
+ active.canceled = true
260
+ active.stopAnimation?.()
261
+ active.controller.abort(new Error(ENHANCEMENT_CANCELED_MESSAGE))
262
+
263
+ if (active.originalPrompt) {
264
+ return restorePrompt(api, state, active.handle, active.originalPrompt, signal)
265
+ }
266
+
267
+ return writePrompt(api, state, active.handle, active.input, signal)
268
+ }
269
+
247
270
  function startEnhancementAnimation(
248
271
  api: Api,
249
272
  state: PluginState,
@@ -311,8 +334,6 @@ function gatherContext(api: Api): string {
311
334
  const files = diff.slice(0, MAX_CHANGED_FILES).map((f) => ` @${f.file}`)
312
335
  sections.push(`Files changed in session:\n${files.join("\n")}`)
313
336
  }
314
-
315
-
316
337
  }
317
338
 
318
339
  return sections.join("\n\n")
@@ -435,12 +456,17 @@ function openEnhanceDialog(
435
456
  variant: "info",
436
457
  title: TOAST_TITLE,
437
458
  message: "Enhancing prompt...",
438
- duration: PROGRESS_TOAST_DURATION_MS,
459
+ duration: TOAST_DURATION_MS,
439
460
  })
440
461
 
441
462
  const enhancementController = new AbortController()
442
463
  const onLifecycleAbort = () => enhancementController.abort(signal.reason)
443
- state.activeEnhancement = { controller: enhancementController }
464
+ state.activeEnhancement = {
465
+ controller: enhancementController,
466
+ handle,
467
+ originalPrompt,
468
+ input,
469
+ }
444
470
  if (signal.aborted) {
445
471
  enhancementController.abort(signal.reason)
446
472
  } else {
@@ -461,6 +487,9 @@ function openEnhanceDialog(
461
487
  }
462
488
 
463
489
  stopAnimation = startEnhancementAnimation(api, state, handle, originalPrompt)
490
+ if (state.activeEnhancement) {
491
+ state.activeEnhancement.stopAnimation = stopAnimation
492
+ }
464
493
 
465
494
  const enhanced = await enhanceWithModel(api, options, input, enhancementController.signal)
466
495
  if (signal.aborted) return
@@ -490,13 +519,19 @@ function openEnhanceDialog(
490
519
  variant: "success",
491
520
  title: "Prompt enhanced",
492
521
  message: "Enhanced prompt added to input.",
493
- duration: RESULT_TOAST_DURATION_MS,
522
+ duration: TOAST_DURATION_MS,
494
523
  })
495
524
  } catch (error) {
496
525
  if (signal.aborted) return
497
526
 
498
527
  stopAnimation()
499
528
 
529
+ const canceled = enhancementController.signal.aborted && !signal.aborted
530
+ if (canceled && state.activeEnhancement?.canceled) {
531
+ // cancelActiveEnhancement already stopped the animation and restored the prompt.
532
+ return
533
+ }
534
+
500
535
  let restored = true
501
536
  if (originalPrompt) {
502
537
  try {
@@ -512,8 +547,6 @@ function openEnhanceDialog(
512
547
  restored = false
513
548
  }
514
549
  }
515
-
516
- const canceled = enhancementController.signal.aborted && !signal.aborted
517
550
  const baseMessage = canceled
518
551
  ? ENHANCEMENT_CANCELED_MESSAGE
519
552
  : error instanceof Error
@@ -545,7 +578,26 @@ function revertEnhancement(
545
578
  signal: AbortSignal,
546
579
  ): void {
547
580
  if (state.enhancing && state.activeEnhancement) {
548
- state.activeEnhancement.controller.abort(new Error(ENHANCEMENT_CANCELED_MESSAGE))
581
+ void (async () => {
582
+ try {
583
+ const restored = await cancelActiveEnhancement(api, state, signal)
584
+ if (!restored) {
585
+ api.ui.toast({ variant: "warning", title: TOAST_TITLE, message: "Prompt changed while canceling." })
586
+ return
587
+ }
588
+
589
+ api.ui.toast({
590
+ variant: "info",
591
+ title: TOAST_TITLE,
592
+ message: "Enhancement canceled. Original prompt restored.",
593
+ duration: TOAST_DURATION_MS,
594
+ })
595
+ } catch (error) {
596
+ if (signal.aborted) return
597
+ const message = error instanceof Error ? error.message : "Cancel failed."
598
+ api.ui.toast({ variant: "error", title: TOAST_TITLE, message })
599
+ }
600
+ })()
549
601
  return
550
602
  }
551
603
 
@@ -600,7 +652,7 @@ function revertEnhancement(
600
652
  variant: "success",
601
653
  title: TOAST_TITLE,
602
654
  message: "Reverted to original prompt.",
603
- duration: RESULT_TOAST_DURATION_MS,
655
+ duration: TOAST_DURATION_MS,
604
656
  })
605
657
  } catch (error) {
606
658
  if (signal.aborted) return