@mtayfur/opencode-prompt-enhancer 0.0.5 → 0.0.6

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.5",
3
+ "version": "0.0.6",
4
4
  "description": "OpenCode plugin that rewrites rough drafts into stronger prompts.",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -7,50 +7,46 @@ import type {
7
7
  TuiPromptInfo,
8
8
  TuiPromptRef,
9
9
  TuiRouteCurrent,
10
- TuiSidebarFileItem,
11
- TuiSidebarTodoItem,
12
10
  TuiSlotPlugin,
13
11
  } from "@opencode-ai/plugin/tui"
14
12
 
15
- const MAX_RECENT_MESSAGES = 6
16
- const MAX_CHANGED_FILES = 30
17
- const MAX_PROMPT_PREVIEW_LENGTH = 300
18
- const MAX_TODOS = 10
13
+ const MAX_RECENT_MESSAGES = 3
14
+ const MAX_CHANGED_FILES = 25
15
+ const MAX_PROMPT_PREVIEW_LENGTH = 250
19
16
  const DIALOG_TITLE = "Enhance Prompt"
20
17
  const TOAST_TITLE = "Prompt enhancer"
21
18
  const TEMP_SESSION_TITLE = "Prompt Enhancer"
22
- const HOME_PROMPT_PLACEHOLDERS = {
23
- normal: ["Fix a TODO in the codebase", "What is the tech stack of this project?", "Fix broken tests"],
24
- shell: ["ls -la", "git status", "pwd"],
19
+
20
+ function makeTempSessionTitle(): string {
21
+ return `${TEMP_SESSION_TITLE} ${Math.random().toString(36).slice(2, 8)}`
25
22
  }
26
23
 
27
- const ENHANCER_SYSTEM_PROMPT = `You rewrite rough user drafts into strong prompts for OpenCode, an AI coding assistant.
24
+ const ENHANCER_SYSTEM_PROMPT = `You are a prompt editor for OpenCode, an AI coding assistant.
28
25
 
29
26
  Goal:
30
- Turn the user's draft into the best possible next prompt for OpenCode. Preserve the user's intent, scope, and priorities exactly. Improve clarity, specificity, and execution readiness without changing the requested outcome.
31
-
32
- OpenCode context:
33
- OpenCode can inspect the workspace, read and edit files, run commands, and verify changes. Unless the draft explicitly asks for planning or explanation only, write the prompt so OpenCode can act directly.
34
-
35
- Rules:
36
- - Preserve the original request type.
37
- - If the draft is a question, return a better question.
38
- - If the draft is a bug fix, return a better bug-fix request.
39
- - If the draft is a review, return a better review request.
40
- - Preserve explicit constraints, file names, commands, error messages, acceptance criteria, and user wording when they matter.
41
- - Use workspace context only when it clearly helps disambiguate or narrow the task.
42
- - Reference specific files, paths, branches, recent prompts, changed files, or todos only when they are directly relevant.
43
- - Resolve vague references like "this", "that bug", or "the plugin" from context when clear.
44
- - If context does not clearly resolve a reference, do not invent details.
45
- - Expand vague verbs into concrete actions when helpful, such as: identify root cause, apply the smallest correct fix, remove dead code, keep scope tight, preserve behavior, follow local conventions, and verify the changed path.
46
- - Prefer wording that helps OpenCode start work immediately.
47
- - If the draft is already precise, return it with minimal cleanup.
48
-
49
- Do not:
27
+ 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.
28
+
29
+ Capabilities:
30
+ 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.
31
+
32
+ Rewrite rules:
33
+ - Preserve the request form. Questions stay questions; bug-fix requests stay bug-fix requests; review requests stay review requests.
34
+ - Preserve the language of the draft. Do not translate.
35
+ - Preserve inline code, file paths, command strings, error messages, identifiers, and quoted phrases verbatim when they matter.
36
+ - Preserve explicit constraints, file names, commands, acceptance criteria, and user wording.
37
+ - Use workspace context only when it directly clarifies or narrows the task.
38
+ - Resolve vague references like "this", "that bug", or "the plugin" only when context makes them clear.
39
+ - Do not invent details when context is ambiguous.
40
+ - 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.
41
+ - Keep the rewrite proportional to the draft. Do not add background, motivation, or steps the draft did not imply.
42
+ - Match the draft's structure and tone. Keep prose as prose and lists as lists.
43
+ - If the draft is already precise, make only minimal cleanup.
44
+
45
+ Hard constraints:
50
46
  - Do not invent requirements, files, APIs, dependencies, bugs, or acceptance criteria.
51
47
  - Do not broaden scope with extra features, refactors, tests, or docs unless the draft implies them.
52
- - Do not ask follow-up questions inside the rewritten prompt.
53
- - Do not mention the existence of context, tags, or these instructions.
48
+ - Do not ask follow-up questions.
49
+ - Do not mention context, tags, instructions, or the rewriting process.
54
50
  - Do not output explanations, markdown fences, or commentary.
55
51
 
56
52
  Output:
@@ -118,10 +114,6 @@ function truncate(value: string, maxLength: number): string {
118
114
  return value.length > maxLength ? `${value.slice(0, maxLength)}...` : value
119
115
  }
120
116
 
121
- function formatTodo(todo: TuiSidebarTodoItem): string {
122
- return ` [${todo.status || "?"}] ${todo.content || "untitled"}`
123
- }
124
-
125
117
  function resolveEnhancerModel(api: Api, options: PluginOptions | undefined): ModelRef | undefined {
126
118
  const override = getModelOverride(options)
127
119
  if (override) return override
@@ -262,8 +254,7 @@ function gatherContext(api: Api): string {
262
254
  const sections: string[] = []
263
255
 
264
256
  const dir = api.state.path.directory
265
- const branch = api.state.vcs?.branch
266
- sections.push(`Working directory: ${dir}${branch ? ` (branch: ${branch})` : ""}`)
257
+ sections.push(`Working directory: ${dir}`)
267
258
 
268
259
  const route = api.route.current
269
260
  if (isSessionRoute(route)) {
@@ -271,7 +262,7 @@ function gatherContext(api: Api): string {
271
262
  const messages = api.state.session.messages(sessionID)
272
263
 
273
264
  const userMessages = messages.filter(isUserMessage)
274
- const recent = userMessages.slice(-MAX_RECENT_MESSAGES)
265
+ const recent = userMessages.slice(-MAX_RECENT_MESSAGES).reverse()
275
266
  if (recent.length > 0) {
276
267
  const prompts: string[] = []
277
268
  for (const msg of recent) {
@@ -281,26 +272,16 @@ function gatherContext(api: Api): string {
281
272
  }
282
273
  }
283
274
  if (prompts.length > 0) {
284
- sections.push(`Recent user prompts in this session (oldest first):\n${prompts.map((p, i) => `${i + 1}. ${p}`).join("\n")}`)
275
+ sections.push(`Recent user prompts in this session (newest first):\n${prompts.map((p, i) => `${i + 1}. ${p}`).join("\n")}`)
285
276
  }
286
277
  }
287
278
 
288
279
  const diff = api.state.session.diff(sessionID)
289
280
  if (diff.length > 0) {
290
- const files = diff.slice(0, MAX_CHANGED_FILES).map((f: TuiSidebarFileItem) =>
291
- ` ${f.file} (+${f.additions} -${f.deletions})`
292
- )
293
- const label = diff.length > MAX_CHANGED_FILES
294
- ? `Files changed in session (showing ${MAX_CHANGED_FILES} of ${diff.length}):`
295
- : `Files changed in session:`
296
- sections.push(`${label}\n${files.join("\n")}`)
281
+ const files = diff.slice(0, MAX_CHANGED_FILES).map((f) => ` ${f.file}`)
282
+ sections.push(`Files changed in session:\n${files.join("\n")}`)
297
283
  }
298
284
 
299
- const todos = api.state.session.todo(sessionID)
300
- if (todos.length > 0) {
301
- const todoLines = todos.slice(0, MAX_TODOS).map(formatTodo)
302
- sections.push(`Active todos:\n${todoLines.join("\n")}`)
303
- }
304
285
  }
305
286
 
306
287
  return sections.join("\n\n")
@@ -332,13 +313,13 @@ async function enhanceWithModel(
332
313
  const created = await api.client.session.create(
333
314
  {
334
315
  directory,
335
- title: TEMP_SESSION_TITLE,
316
+ title: makeTempSessionTitle(),
336
317
  },
337
318
  { signal, throwOnError: true },
338
319
  )
339
320
 
340
321
  const tempSessionID = created.data?.id
341
- if (!tempSessionID) throw new Error("Prompt enhancer session creation failed.")
322
+ if (!tempSessionID) throw new Error("Failed to start prompt enhancer.")
342
323
 
343
324
  try {
344
325
  const response = await api.client.session.prompt(
@@ -358,10 +339,10 @@ async function enhanceWithModel(
358
339
  )
359
340
 
360
341
  const parts = response.data?.parts
361
- if (!parts) throw new Error("Enhancer model returned no response.")
342
+ if (!parts) throw new Error("Enhancer returned no response.")
362
343
 
363
344
  const enhanced = extractVisibleText(parts)
364
- if (!enhanced) throw new Error("Enhancer model returned no text.")
345
+ if (!enhanced) throw new Error("Enhancer returned no text.")
365
346
  return enhanced
366
347
  } finally {
367
348
  try {
@@ -379,7 +360,7 @@ function openEnhanceDialog(
379
360
  signal: AbortSignal,
380
361
  ): void {
381
362
  if (state.enhancing) {
382
- api.ui.toast({ variant: "warning", title: TOAST_TITLE, message: "Enhancement already in progress." })
363
+ api.ui.toast({ variant: "warning", title: TOAST_TITLE, message: "Enhancement in progress." })
383
364
  return
384
365
  }
385
366
 
@@ -387,7 +368,7 @@ function openEnhanceDialog(
387
368
 
388
369
  const target = currentPromptTarget(api, state)
389
370
  if (!target) {
390
- api.ui.toast({ variant: "warning", title: TOAST_TITLE, message: "Prompt enhancement is only available from a prompt view." })
371
+ api.ui.toast({ variant: "warning", title: TOAST_TITLE, message: "Enhancement only works from a prompt." })
391
372
  return
392
373
  }
393
374
 
@@ -402,19 +383,19 @@ function openEnhanceDialog(
402
383
  api.ui.dialog.replace(() => (
403
384
  <api.ui.DialogPrompt
404
385
  title={DIALOG_TITLE}
405
- placeholder="Describe what you want to do..."
386
+ placeholder="Describe the task..."
406
387
  value={initialValue}
407
388
  onCancel={() => api.ui.dialog.clear()}
408
389
  onConfirm={(value) => {
409
390
  const input = value.trim()
410
391
  if (!input) {
411
- api.ui.toast({ variant: "warning", title: TOAST_TITLE, message: "Prompt is empty." })
392
+ api.ui.toast({ variant: "warning", title: TOAST_TITLE, message: "Enter a prompt first." })
412
393
  api.ui.dialog.clear()
413
394
  return
414
395
  }
415
396
 
416
397
  if (!isPromptHandleActive(api, state, handle)) {
417
- api.ui.toast({ variant: "warning", title: TOAST_TITLE, message: "Prompt changed while the dialog was open." })
398
+ api.ui.toast({ variant: "warning", title: TOAST_TITLE, message: "Prompt changed while dialog was open." })
418
399
  api.ui.dialog.clear()
419
400
  return
420
401
  }
@@ -448,7 +429,7 @@ function openEnhanceDialog(
448
429
  api.ui.toast({
449
430
  variant: "warning",
450
431
  title: TOAST_TITLE,
451
- message: "Enhanced prompt is ready, but the original prompt is no longer active.",
432
+ message: "Enhanced prompt is ready, but that prompt is no longer active.",
452
433
  })
453
434
  return
454
435
  }
@@ -456,7 +437,7 @@ function openEnhanceDialog(
456
437
  api.ui.toast({
457
438
  variant: "success",
458
439
  title: "Prompt enhanced",
459
- message: "Enhanced prompt written to input.",
440
+ message: "Enhanced prompt added to input.",
460
441
  duration: 3000,
461
442
  })
462
443
  } catch (error) {
@@ -478,10 +459,10 @@ function openEnhanceDialog(
478
459
  }
479
460
  }
480
461
 
481
- const baseMessage = error instanceof Error ? error.message : "Model enhancement failed."
462
+ const baseMessage = error instanceof Error ? error.message : "Prompt enhancement failed."
482
463
  const message = restored
483
464
  ? baseMessage
484
- : `${baseMessage} Original prompt could not be restored because the active prompt changed.`
465
+ : `${baseMessage} Original prompt could not be restored because the prompt changed.`
485
466
  api.ui.toast({ variant: "error", title: TOAST_TITLE, message })
486
467
  } finally {
487
468
  state.enhancing = false
@@ -503,7 +484,6 @@ const tui: TuiPlugin = async (api, options) => {
503
484
  ref={(ref) => bindPromptRef(state, { name: "home", workspaceID: props.workspace_id }, props.ref, ref)}
504
485
  workspaceID={props.workspace_id}
505
486
  right={<api.ui.Slot name="home_prompt_right" workspace_id={props.workspace_id} />}
506
- placeholders={HOME_PROMPT_PLACEHOLDERS}
507
487
  />
508
488
  )
509
489
  },
@@ -528,7 +508,7 @@ const tui: TuiPlugin = async (api, options) => {
528
508
  {
529
509
  title: DIALOG_TITLE,
530
510
  value: "prompt-enhancer.enhance",
531
- description: "Enhance prompt with project context (Ctrl+E)",
511
+ description: "Enhance current prompt (Ctrl+E)",
532
512
  category: "Prompt",
533
513
  keybind: "ctrl+e",
534
514
  suggested: true,