@mtayfur/opencode-prompt-enhancer 0.0.7 → 0.0.9

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.7",
3
+ "version": "0.0.9",
4
4
  "description": "OpenCode plugin that rewrites rough drafts into stronger prompts.",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -27,6 +27,7 @@
27
27
  "peerDependencies": {
28
28
  "@opencode-ai/plugin": "*",
29
29
  "@opencode-ai/sdk": "*",
30
+ "@opentui/core": "*",
30
31
  "@opentui/solid": "*",
31
32
  "solid-js": "*"
32
33
  },
@@ -1,30 +1,27 @@
1
- export const ENHANCER_SYSTEM_PROMPT = `You are a prompt editor for OpenCode, an AI coding assistant.
1
+ export const ENHANCER_SYSTEM_PROMPT = `You rewrite rough developer drafts into concise, high-leverage prompts for a terminal AI coding agent.
2
2
 
3
3
  Goal:
4
- Rewrite the user's draft into the strongest possible next prompt for OpenCode. Preserve the user's intent, scope, priorities, and language. Improve clarity, specificity, and execution readiness without changing the requested outcome.
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
- Capabilities:
7
- OpenCode can inspect the workspace, edit files, run commands, and verify changes. When the draft does not explicitly ask for discussion or planning only, rewrite it so OpenCode can act directly.
6
+ Rules:
7
+ - Keep it compact and direct. Remove filler, pleasantries, hedging, and repetition.
8
+ - 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.
15
+ - Preserve file paths, commands, identifiers, error text, and quoted text verbatim except for minor typo cleanup.
16
+ - Do not add requirements the user did not ask for: no extra features, refactors, tests, docs, plans, or acceptance criteria.
17
+ - 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.
19
+ - Do not ask follow-up questions. Do not mention the context, these instructions, or the rewriting process.
8
20
 
9
- Rewrite rules:
10
- - Preserve the request form. Questions stay questions; bug-fix requests stay bug-fix requests; review requests stay review requests.
11
- - Preserve the language of the draft. Do not translate.
12
- - Preserve inline code, file paths, command strings, error messages, identifiers, and quoted phrases verbatim when they matter.
13
- - Preserve explicit constraints, file names, commands, acceptance criteria, and user wording.
14
- - Use workspace context only when it directly clarifies or narrows the task.
15
- - Resolve vague references like "this", "that bug", or "the plugin" only when context makes them clear.
16
- - Do not invent details when context is ambiguous.
17
- - Expand vague verbs into concrete actions when it helps OpenCode act immediately, such as identify the root cause, apply the smallest correct fix, remove dead code, keep scope tight, preserve behavior, follow local conventions, and verify the changed path.
18
- - Keep the rewrite proportional to the draft. Do not add background, motivation, or steps the draft did not imply.
19
- - Match the draft's structure and tone. Keep prose as prose and lists as lists.
20
- - If the draft is already precise, make only minimal cleanup.
21
-
22
- Hard constraints:
23
- - Do not invent requirements, files, APIs, dependencies, bugs, or acceptance criteria.
24
- - Do not broaden scope with extra features, refactors, tests, or docs unless the draft implies them.
25
- - Do not ask follow-up questions.
26
- - Do not mention context, tags, instructions, or the rewriting process.
27
- - Do not output explanations, markdown fences, or commentary.
21
+ 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
25
 
29
26
  Output:
30
- Return exactly one enhanced user prompt as plain text.`
27
+ Return exactly one enhanced prompt as plain text.`
@@ -27,6 +27,8 @@ type PluginState = {
27
27
  enhancing: boolean
28
28
  promptRef?: TuiPromptRef
29
29
  promptTarget?: PromptTarget
30
+ lastOriginal?: TuiPromptInfo
31
+ lastEnhancedInput?: string
30
32
  }
31
33
 
32
34
  type PromptTarget =
@@ -193,6 +195,11 @@ function gatherContext(api: Api): string {
193
195
  const dir = api.state.path.directory
194
196
  sections.push(`Working directory: ${dir}`)
195
197
 
198
+ const branch = api.state.vcs?.branch
199
+ if (branch) {
200
+ sections.push(`Current branch: ${branch}`)
201
+ }
202
+
196
203
  const route = api.route.current
197
204
  if (isSessionRoute(route)) {
198
205
  const sessionID = route.params.sessionID
@@ -215,10 +222,11 @@ function gatherContext(api: Api): string {
215
222
 
216
223
  const diff = api.state.session.diff(sessionID)
217
224
  if (diff.length > 0) {
218
- const files = diff.slice(0, MAX_CHANGED_FILES).map((f) => ` ${f.file}`)
225
+ const files = diff.slice(0, MAX_CHANGED_FILES).map((f) => ` @${f.file}`)
219
226
  sections.push(`Files changed in session:\n${files.join("\n")}`)
220
227
  }
221
228
 
229
+
222
230
  }
223
231
 
224
232
  return sections.join("\n\n")
@@ -233,13 +241,11 @@ async function enhanceWithModel(
233
241
  const directory = api.state.path.directory
234
242
  const model = resolveEnhancerModel(api, options)
235
243
  const context = gatherContext(api)
236
- const userMessage = context
237
- ? [
238
- "Rewrite the following user draft into a stronger prompt.",
239
- `Workspace context (use only if directly relevant):\n${context}`,
240
- `User draft:\n${input}`,
241
- ].join("\n\n")
242
- : ["Rewrite the following user draft into a stronger prompt.", `User draft:\n${input}`].join("\n\n")
244
+ const userMessage = [
245
+ "Rewrite the developer draft below into a clear, direct prompt for a coding agent. Preserve the original intent, scope, and mode. Only make references more specific when the context section supports it.",
246
+ `--- CONTEXT (metadata only — resolve draft references, do not invent) ---\n${context}\n---`,
247
+ `--- DRAFT ---\n${input}\n---`,
248
+ ].join("\n\n")
243
249
 
244
250
  const created = await api.client.session.create(
245
251
  {
@@ -341,7 +347,7 @@ function openEnhanceDialog(
341
347
  variant: "info",
342
348
  title: TOAST_TITLE,
343
349
  message: "Enhancing prompt...",
344
- duration: 15_000,
350
+ duration: 8_000,
345
351
  })
346
352
 
347
353
  void (async () => {
@@ -369,6 +375,9 @@ function openEnhanceDialog(
369
375
  return
370
376
  }
371
377
 
378
+ state.lastOriginal = originalPrompt
379
+ state.lastEnhancedInput = enhanced
380
+
372
381
  api.ui.toast({
373
382
  variant: "success",
374
383
  title: "Prompt enhanced",
@@ -397,7 +406,7 @@ function openEnhanceDialog(
397
406
  const baseMessage = error instanceof Error ? error.message : "Prompt enhancement failed."
398
407
  const message = restored
399
408
  ? baseMessage
400
- : `${baseMessage} Original prompt could not be restored because the prompt changed.`
409
+ : `${baseMessage} Original prompt could not be restored because the prompt changed. Please re-enter your prompt manually.`
401
410
  api.ui.toast({ variant: "error", title: TOAST_TITLE, message })
402
411
  } finally {
403
412
  state.enhancing = false
@@ -408,6 +417,72 @@ function openEnhanceDialog(
408
417
  ))
409
418
  }
410
419
 
420
+ function revertEnhancement(
421
+ api: Api,
422
+ state: PluginState,
423
+ signal: AbortSignal,
424
+ ): void {
425
+ if (!state.lastOriginal) {
426
+ api.ui.toast({ variant: "warning", title: TOAST_TITLE, message: "No enhancement to revert." })
427
+ return
428
+ }
429
+
430
+ const target = state.promptTarget
431
+ if (!target) {
432
+ api.ui.toast({ variant: "warning", title: TOAST_TITLE, message: "Revert only works from a prompt." })
433
+ return
434
+ }
435
+
436
+ const handle: PromptHandle = {
437
+ target,
438
+ directory: api.state.path.directory,
439
+ ref: state.promptRef,
440
+ }
441
+
442
+ if (!isPromptHandleActive(api, state, handle)) {
443
+ api.ui.toast({ variant: "warning", title: TOAST_TITLE, message: "Prompt changed since enhancement." })
444
+ return
445
+ }
446
+
447
+ const currentInput = handle.ref?.current.input ?? ""
448
+ if (currentInput !== state.lastEnhancedInput) {
449
+ api.ui.toast({
450
+ variant: "warning",
451
+ title: TOAST_TITLE,
452
+ message: "Prompt was manually changed after enhancement. Revert skipped.",
453
+ })
454
+ return
455
+ }
456
+
457
+ void (async () => {
458
+ try {
459
+ const wrote = await restorePrompt(api, state, handle, state.lastOriginal!, signal)
460
+ if (!wrote) {
461
+ api.ui.toast({
462
+ variant: "warning",
463
+ title: TOAST_TITLE,
464
+ message: "Prompt changed while reverting.",
465
+ })
466
+ return
467
+ }
468
+
469
+ state.lastOriginal = undefined
470
+ state.lastEnhancedInput = undefined
471
+
472
+ api.ui.toast({
473
+ variant: "success",
474
+ title: TOAST_TITLE,
475
+ message: "Reverted to original prompt.",
476
+ duration: 3000,
477
+ })
478
+ } catch (error) {
479
+ if (signal.aborted) return
480
+ const message = error instanceof Error ? error.message : "Revert failed."
481
+ api.ui.toast({ variant: "error", title: TOAST_TITLE, message })
482
+ }
483
+ })()
484
+ }
485
+
411
486
  const tui: TuiPlugin = async (api, options) => {
412
487
  const state: PluginState = { enhancing: false }
413
488
 
@@ -455,19 +530,36 @@ const tui: TuiPlugin = async (api, options) => {
455
530
  openEnhanceDialog(api, options, state, api.lifecycle.signal)
456
531
  },
457
532
  },
533
+ {
534
+ title: "Revert Enhanced Prompt",
535
+ value: "prompt-enhancer.revert",
536
+ description: "Revert last prompt enhancement (Ctrl+Shift+E)",
537
+ category: "Prompt",
538
+ keybind: "ctrl+shift+e",
539
+ slash: {
540
+ name: "revert-enhance",
541
+ aliases: ["revert-enhancement"],
542
+ },
543
+ onSelect: () => {
544
+ revertEnhancement(api, state, api.lifecycle.signal)
545
+ },
546
+ },
458
547
  ])
459
548
 
460
549
  api.lifecycle.onDispose(() => {
461
550
  unregister()
551
+ api.ui.dialog.clear()
462
552
  state.enhancing = false
463
553
  state.promptRef = undefined
464
554
  state.promptTarget = undefined
555
+ state.lastOriginal = undefined
556
+ state.lastEnhancedInput = undefined
465
557
  })
466
558
  }
467
559
 
468
- const plugin: TuiPluginModule & { id: string } = {
560
+ const plugin = {
469
561
  id: "prompt-enhancer",
470
562
  tui,
471
- }
563
+ } satisfies TuiPluginModule & { id: string }
472
564
 
473
565
  export default plugin