@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 +1 -1
- package/plugins/enhancer-system-prompt.ts +50 -10
- package/plugins/prompt-enhancer.tsx +63 -11
package/package.json
CHANGED
|
@@ -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
|
-
-
|
|
10
|
-
* "this", "that bug", "that function"
|
|
11
|
-
* "the same file", "this file"
|
|
12
|
-
* If the
|
|
13
|
-
* If
|
|
14
|
-
|
|
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,
|
|
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
|
-
|
|
23
|
-
|
|
24
|
-
|
|
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
|
|
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:
|
|
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 = {
|
|
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:
|
|
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
|
-
|
|
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:
|
|
655
|
+
duration: TOAST_DURATION_MS,
|
|
604
656
|
})
|
|
605
657
|
} catch (error) {
|
|
606
658
|
if (signal.aborted) return
|