@henryqw/pi-prompt-creator 1.0.6 → 1.1.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/CONTEXT.md CHANGED
@@ -21,4 +21,4 @@ Turn repetition and correction in the current Pi conversation into one editable
21
21
  | Scripts, Skills, and other automation | Their owning package or repository |
22
22
  | This extension | Current-conversation Signal detection, Prompt Candidate generation, and Prompt Review |
23
23
 
24
- The extension stays silent when it finds no Prompt Candidate. Background analysis remains visible while it is running. Every completed candidate waits for explicit user display, so background work never steals editor focus.
24
+ The extension stays silent when it finds no Prompt Candidate. Background analysis remains visible while it is running. Manually requested candidates display when ready; automatic candidates wait for explicit user display, so background work never steals editor focus.
package/README.md CHANGED
@@ -23,22 +23,19 @@ Run `/task-models` before analysis. Assign a model to `fast`, or override `pi-pr
23
23
 
24
24
  Run `/promptor` in the interactive TUI after a repeated request or correction appears:
25
25
 
26
- 1. Choose `Analyze now`. Wait for the `Prompt ready — /promptor` widget.
27
- 2. Run `/promptor` again and choose `Show candidate`.
28
- 3. Ask Main to refine it and return only the complete Final Prompt Draft.
29
- 4. Run `/promptor`, choose `Save latest Main draft`, and enter a lowercase kebab-case name.
26
+ 1. Run `/promptor`. The completed candidate appears automatically.
27
+ 2. Ask Main to refine it and return only the complete Final Prompt Draft.
28
+ 3. Run `/promptor save` to save under the suggested name, or `/promptor save <name>` to rename it.
30
29
 
31
- The prompt is created at `~/.pi/agent/prompts/<name>.md`, then Pi reloads its resources.
30
+ The prompt is created at `~/.pi/agent/prompts/<name>.md`, then Pi reloads its resources. No selection menu or name prompt is required.
32
31
 
33
- The menu adapts to the current state:
34
-
35
- | Item | Action |
32
+ | Command | Action |
36
33
  | --- | --- |
37
- | `Analyze now` or `Analyze again` | Start one visible background analysis. |
38
- | `Automatic On` or `Automatic Off` | Turn automatic analysis on or off. |
39
- | `Show candidate` | Add the candidate to the conversation for review. |
40
- | `Dismiss candidate` | Forget the pending candidate. |
41
- | `Save latest Main draft` | Save Main's newest completed review reply after you show a candidate. |
34
+ | `/promptor` | Start analysis, or show a candidate prepared by automatic analysis. |
35
+ | `/promptor analyze` | Analyze again after a candidate has been reviewed or dismissed. |
36
+ | `/promptor save [name]` | Save Main's newest completed review reply, using the suggested name by default. |
37
+ | `/promptor dismiss` | Forget a candidate prepared by automatic analysis. |
38
+ | `/promptor automatic <on\|off>` | Enable or disable automatic analysis. |
42
39
 
43
40
  ## Flow
44
41
 
@@ -68,11 +65,11 @@ New user input does not stop a running child. Branch navigation discards its old
68
65
 
69
66
  ### Review and save
70
67
 
71
- The widget shows `Prompt ready — /promptor` until you show or dismiss the completed candidate.
68
+ A manually requested analysis displays its completed candidate immediately. Automatic background analysis keeps its candidate behind the `Prompt ready — /promptor` widget until you run `/promptor` or `/promptor dismiss`, so background work never interrupts the conversation.
72
69
 
73
- The extension never injects a candidate automatically. `Show candidate` adds one visible message and marks its contents as untrusted.
70
+ Displayed candidates are visible messages marked as untrusted.
74
71
 
75
- Refine the candidate with Main. Ask Main to return only the complete Final Prompt Draft before saving.
72
+ Refine the candidate with Main. Ask Main to return only the complete Final Prompt Draft before saving it with `/promptor save [name]`.
76
73
 
77
74
  The save item appears only after you show a candidate and Main then completes a valid Markdown review reply.
78
75
 
@@ -32,7 +32,7 @@ const NAME = /^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$/;
32
32
  const DISALLOWED_MARKDOWN_CONTROLS = /[\u0000-\u0008\u000b-\u001f\u007f-\u009f]/;
33
33
  const READY_WIDGET = "Prompt ready — /promptor";
34
34
  const FAILURE_WIDGET = "Prompt analysis failed — /promptor";
35
- const REFINEMENT_GUIDANCE = "Refine this candidate conversationally with the user. When the user approves it, Main must emit only the complete Final Prompt Draft. Then save that full reply through /promptor.";
35
+ const REFINEMENT_GUIDANCE = "Refine this candidate conversationally with the user. When the user approves it, Main must emit only the complete Final Prompt Draft. Then run /promptor save to use the suggested name, or /promptor save <name>.";
36
36
 
37
37
  export const DRAFT_TASK = {
38
38
  id: "pi-prompt-creator/draft",
@@ -279,7 +279,6 @@ export default function promptCreatorExtension(pi: ExtensionAPI, options: Prompt
279
279
  let inputCount = 0;
280
280
  let branchGeneration = 0;
281
281
  let closed = false;
282
- let analysisRan = false;
283
282
  let candidate: PromptCandidate | undefined;
284
283
  let candidateNameHint: string | undefined;
285
284
  let reviewBoundary: ReviewBoundary | undefined;
@@ -299,7 +298,6 @@ export default function promptCreatorExtension(pi: ExtensionAPI, options: Prompt
299
298
  const resetBranch = (ctx: ExtensionContext) => {
300
299
  branchGeneration += 1;
301
300
  inputCount = 0;
302
- analysisRan = false;
303
301
  candidate = undefined;
304
302
  candidateNameHint = undefined;
305
303
  reviewBoundary = undefined;
@@ -311,10 +309,23 @@ export default function promptCreatorExtension(pi: ExtensionAPI, options: Prompt
311
309
  maxTurns: 3,
312
310
  timeout: { idleMs: 2 * 60_000, maxMs: 5 * 60_000 },
313
311
  });
312
+ const showCandidate = (ctx: ExtensionContext) => {
313
+ if (!candidate) return;
314
+ const shown = candidate;
315
+ const boundaryId = ctx.sessionManager.getLeafId();
316
+ candidate = undefined;
317
+ candidateNameHint = shown.name;
318
+ clearWidget(ctx);
319
+ pi.sendMessage({
320
+ customType: CANDIDATE_MESSAGE_TYPE,
321
+ content: candidateMessage(shown),
322
+ display: true,
323
+ }, { triggerTurn: false });
324
+ reviewBoundary = boundaryId ? { entryId: boundaryId } : undefined;
325
+ };
314
326
  const startAnalysis = (ctx: ExtensionContext, manual: boolean) => {
315
327
  if (ctx.mode !== "tui" || activeRun || candidate) return;
316
328
  if (manual) automaticConsumed = true;
317
- analysisRan = true;
318
329
  failure = false;
319
330
  candidateNameHint = undefined;
320
331
  const payload = analysisPayload(pi, ctx);
@@ -353,7 +364,8 @@ export default function promptCreatorExtension(pi: ExtensionAPI, options: Prompt
353
364
  }
354
365
  candidate = drafted;
355
366
  candidateNameHint = drafted.name;
356
- ctx.ui.setWidget(WIDGET_KEY, [READY_WIDGET]);
367
+ if (manual) showCandidate(ctx);
368
+ else ctx.ui.setWidget(WIDGET_KEY, [READY_WIDGET]);
357
369
  }).catch(() => {
358
370
  if (isCurrent(run)) showFailure(ctx);
359
371
  }).finally(() => {
@@ -363,13 +375,13 @@ export default function promptCreatorExtension(pi: ExtensionAPI, options: Prompt
363
375
 
364
376
  const saveLatestDraft = async (
365
377
  draft: string,
378
+ requestedName: string | undefined,
366
379
  expectedReview: ReviewBoundary,
367
380
  expectedBranch: number,
368
381
  ctx: ExtensionCommandContext,
369
382
  ) => {
370
- const requested = await ctx.ui.input("Prompt name", candidateNameHint ?? "my-prompt");
371
- if (requested === undefined || expectedBranch !== branchGeneration || reviewBoundary !== expectedReview) return;
372
- const name = requested.trim();
383
+ if (expectedBranch !== branchGeneration || reviewBoundary !== expectedReview) return;
384
+ const name = requestedName ?? candidateNameHint ?? "";
373
385
  if (!isPromptName(name)) {
374
386
  ctx.ui.notify(`Use lowercase kebab-case starting with a letter, up to ${MAX_NAME_CHARS} characters.`, "warning");
375
387
  return;
@@ -455,31 +467,53 @@ export default function promptCreatorExtension(pi: ExtensionAPI, options: Prompt
455
467
  });
456
468
 
457
469
  pi.registerCommand("promptor", {
458
- description: "Analyze this conversation and manage prompt candidates",
459
- handler: async (_args, ctx) => {
470
+ description: "Analyze prompts, show a ready candidate, or save with /promptor save [name]",
471
+ getArgumentCompletions: (prefix) => {
472
+ const commands = ["analyze", "dismiss", "save", "automatic on", "automatic off"];
473
+ const matches = commands.filter((command) => command.startsWith(prefix));
474
+ return matches.length ? matches.map((command) => ({ value: command, label: command })) : null;
475
+ },
476
+ handler: async (args, ctx) => {
460
477
  if (ctx.mode !== "tui") {
461
478
  ctx.ui.notify("/promptor requires the interactive TUI.", "warning");
462
479
  return;
463
480
  }
464
- const menuBranch = branchGeneration;
481
+ const commandBranch = branchGeneration;
465
482
  const review = reviewBoundary;
466
483
  const draft = latestAssistantDraft(ctx, review);
467
- const analyze = analysisRan ? "Analyze again" : "Analyze now";
468
- const toggle = automatic ? "Automatic Off" : "Automatic On";
469
- const choices = [
470
- ...(!activeRun && !candidate ? [analyze] : []),
471
- toggle,
472
- ...(candidate ? ["Show candidate", "Dismiss candidate"] : []),
473
- ...(draft ? ["Save latest Main draft"] : []),
474
- ];
475
- const selected = await ctx.ui.select("Prompt Creator", choices);
476
- if (!selected || menuBranch !== branchGeneration) return;
477
- if (selected === analyze) {
478
- startAnalysis(ctx, true);
484
+ const [action = "", ...values] = args.trim().split(/\s+/);
485
+ if (!action) {
486
+ if (candidate) showCandidate(ctx);
487
+ else if (activeRun) ctx.ui.notify("Prompt analysis is already running.", "info");
488
+ else if (draft) ctx.ui.notify("Run /promptor save to create the reviewed prompt.", "info");
489
+ else startAnalysis(ctx, true);
490
+ return;
491
+ }
492
+ if (action === "analyze" && values.length === 0) {
493
+ if (candidate) ctx.ui.notify("Show or dismiss the pending candidate first.", "warning");
494
+ else if (activeRun) ctx.ui.notify("Prompt analysis is already running.", "info");
495
+ else startAnalysis(ctx, true);
479
496
  return;
480
497
  }
481
- if (selected === toggle) {
482
- const next = !automatic;
498
+ if (action === "dismiss" && values.length === 0) {
499
+ if (!candidate) ctx.ui.notify("No prompt candidate is waiting.", "warning");
500
+ else {
501
+ candidate = undefined;
502
+ candidateNameHint = undefined;
503
+ clearWidget(ctx);
504
+ }
505
+ return;
506
+ }
507
+ if (action === "save" && values.length <= 1) {
508
+ if (!draft || !review) {
509
+ ctx.ui.notify("No reviewed Main draft is ready to save.", "warning");
510
+ return;
511
+ }
512
+ await saveLatestDraft(draft, values[0], review, commandBranch, ctx);
513
+ return;
514
+ }
515
+ if (action === "automatic" && values.length === 1 && (values[0] === "on" || values[0] === "off")) {
516
+ const next = values[0] === "on";
483
517
  try {
484
518
  await configStore.save({ automatic: next, inputThreshold });
485
519
  automatic = next;
@@ -489,29 +523,7 @@ export default function promptCreatorExtension(pi: ExtensionAPI, options: Prompt
489
523
  }
490
524
  return;
491
525
  }
492
- if (selected === "Show candidate" && candidate) {
493
- const shown = candidate;
494
- const boundaryId = ctx.sessionManager.getLeafId();
495
- candidate = undefined;
496
- candidateNameHint = shown.name;
497
- clearWidget(ctx);
498
- pi.sendMessage({
499
- customType: CANDIDATE_MESSAGE_TYPE,
500
- content: candidateMessage(shown),
501
- display: true,
502
- }, { triggerTurn: false });
503
- reviewBoundary = boundaryId ? { entryId: boundaryId } : undefined;
504
- return;
505
- }
506
- if (selected === "Dismiss candidate" && candidate) {
507
- candidate = undefined;
508
- candidateNameHint = undefined;
509
- clearWidget(ctx);
510
- return;
511
- }
512
- if (selected === "Save latest Main draft" && draft && review) {
513
- await saveLatestDraft(draft, review, menuBranch, ctx);
514
- }
526
+ ctx.ui.notify("Use /promptor, /promptor analyze, /promptor dismiss, /promptor save [name], or /promptor automatic <on|off>.", "warning");
515
527
  },
516
528
  });
517
529
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@henryqw/pi-prompt-creator",
3
- "version": "1.0.6",
3
+ "version": "1.1.0",
4
4
  "description": "Turn current Pi conversation signals into user-approved prompt templates.",
5
5
  "keywords": [
6
6
  "pi-package",