@henryqw/pi-prompt-creator 1.0.5 → 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
@@ -11,32 +11,31 @@ pi install npm:@henryqw/pi-task-models
11
11
  pi install npm:@henryqw/pi-prompt-creator
12
12
  ```
13
13
 
14
- Run `/task-models` before analysis. Assign a model to `fast`, or override `pi-prompt-creator/draft`.
14
+ Run `/task-models` before analysis. Assign a model to `fast`, or override `pi-prompt-creator/draft`. Open `/task-models` again and verify the saved route or task assignment.
15
15
 
16
16
  ## Works with
17
17
 
18
- **Required.** [`@henryqw/pi-task-models`](https://pi.henry.wang/extensions/pi-task-models) provides the isolated draft model route.
18
+ | Package | Relationship | Purpose |
19
+ | --- | --- | --- |
20
+ | [`@henryqw/pi-task-models`](https://pi.henry.wang/extensions/pi-task-models) | Required | Provides the isolated draft model route. |
19
21
 
20
22
  ## Use
21
23
 
22
24
  Run `/promptor` in the interactive TUI after a repeated request or correction appears:
23
25
 
24
- 1. Choose `Analyze now`. Wait for the `Prompt ready — /promptor` widget.
25
- 2. Run `/promptor` again and choose `Show candidate`.
26
- 3. Ask Main to refine it and return only the complete Final Prompt Draft.
27
- 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.
28
29
 
29
- 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.
30
31
 
31
- The menu adapts to the current state:
32
-
33
- | Item | Action |
32
+ | Command | Action |
34
33
  | --- | --- |
35
- | `Analyze now` or `Analyze again` | Start one visible background analysis. |
36
- | `Automatic On` or `Automatic Off` | Save the automatic setting. |
37
- | `Show candidate` | Add the candidate to the conversation for review. |
38
- | `Dismiss candidate` | Forget the pending candidate. |
39
- | `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. |
40
39
 
41
40
  ## Flow
42
41
 
@@ -54,17 +53,23 @@ Branch changes reset the input counter. Automatic analysis and candidate widgets
54
53
 
55
54
  Only one analysis can run at a time. A pending candidate blocks another analysis.
56
55
 
56
+ Automatic analysis sends the current conversation to the child model configured through `/task-models`.
57
+
58
+ Its payload includes the active compaction or branch summary, user text, successfully completed assistant text, and effective prompt names and descriptions.
59
+
60
+ Project context files and prompt templates are disabled for the child. Tool traffic, thinking, images, custom messages, and inactive branches are excluded.
61
+
57
62
  Analysis asks a tool-free child for one JSON response. It has no user extensions, Skills, or saved session. The extension does not retry failed analysis.
58
63
 
59
64
  New user input does not stop a running child. Branch navigation discards its old result without stopping the child.
60
65
 
61
66
  ### Review and save
62
67
 
63
- 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.
64
69
 
65
- 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.
66
71
 
67
- 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]`.
68
73
 
69
74
  The save item appears only after you show a candidate and Main then completes a valid Markdown review reply.
70
75
 
@@ -88,12 +93,12 @@ Use `/task-models` to select the model and thinking level. The extension owns no
88
93
 
89
94
  ## Config
90
95
 
91
- `~/.pi/agent/config/pi-prompt-creator/config.json`
96
+ Package-owned: `~/.pi/agent/config/pi-prompt-creator/config.json`
92
97
 
93
- | Field | Required | Possible values | Default |
98
+ | Name | Description | Values | Default |
94
99
  | --- | --- | --- | --- |
95
- | `automatic` | No | `true` or `false` | `true` |
96
- | `inputThreshold` | No | Positive integer | `3` |
100
+ | `automatic` | Runs automatic analysis after enough user inputs. | Boolean. | `true` |
101
+ | `inputThreshold` | Sets the number of non-empty user inputs before automatic analysis. | Positive integer. | `3` |
97
102
 
98
103
  Edit either field, then run `/reload` to apply the change. Omitted fields use their defaults.
99
104
 
@@ -109,59 +114,7 @@ A pending candidate stays in memory only. Automatic mode persists, but counters
109
114
 
110
115
  Saved prompts live at `~/.pi/agent/prompts/<name>.md`. Saving never replaces an existing file.
111
116
 
112
- ## Data, cost, and privacy
113
-
114
- Automatic analysis is on by default. It sends the current conversation to the child model configured through `/task-models`.
115
-
116
- Disable automatic analysis through `/promptor`.
117
-
118
- The payload includes the active compaction or branch summary, user text, and successfully completed assistant text. It also includes effective prompt names and descriptions.
119
-
120
- Project context files and prompt templates are disabled for the child.
121
-
122
- Tool traffic, thinking, images, custom messages, and inactive branches are excluded.
123
-
124
- ## Automatic mode
125
-
126
- Automatic mode waits for the configured number of non-empty user inputs. The default is three.
127
-
128
- It starts at the next idle `agent_settled` event. It starts at most once per extension runtime.
129
-
130
- A manual analysis consumes that opportunity. Automatic mode persists, but counters and candidates do not.
131
-
132
- Branch changes reset the input counter. Automatic analysis and candidate widgets require the interactive TUI.
133
-
134
- ## Review and save
135
-
136
- A completed candidate stays in memory. The widget shows `Prompt ready — /promptor` until you show or dismiss it.
137
-
138
- The extension never injects a candidate automatically. `Show candidate` adds one visible message and marks its contents as untrusted.
139
-
140
- Refine the candidate with Main. Ask Main to return only the complete Final Prompt Draft before saving.
141
-
142
- The save item appears only after you show a candidate and Main then completes a valid Markdown review reply.
143
-
144
- Saving uses that latest retained Main reply as the entire file. Replies before the active compaction or branch summary cannot be saved.
145
-
146
- An interrupted, failed, empty, or tool-use reply cannot be saved. The extension never falls back to an older reply.
147
-
148
- A successful save ends that review. Show another candidate and complete another Main review reply before saving again.
149
-
150
- The menu asks for a lowercase kebab-case name. A candidate name appears only as a hint.
151
-
152
- Names start with a letter and contain at most 64 ASCII characters. Existing command names and prompt files are rejected.
153
-
154
- Prompts are created at `~/.pi/agent/prompts/<name>.md`. Creation never replaces a file.
155
-
156
- A successful save reloads Pi resources. If reload fails, the prompt remains saved and `/reload` can load it.
157
-
158
- ## Model routing
159
-
160
- The extension registers `pi-prompt-creator/draft`. Its default task profile is `fast`.
161
-
162
- Use `/task-models` to select the model and thinking level. The extension owns no model configuration.
163
-
164
- ## Limits and failures
117
+ ## Limits and recovery
165
118
 
166
119
  The child payload stays within 30,000 characters, including its JSON envelope and prompt metadata.
167
120
 
@@ -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.5",
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",