@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 +1 -1
- package/README.md +13 -16
- package/extensions/prompt-creator.ts +60 -48
- package/package.json +1 -1
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.
|
|
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.
|
|
27
|
-
2.
|
|
28
|
-
3.
|
|
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
|
-
|
|
34
|
-
|
|
35
|
-
| Item | Action |
|
|
32
|
+
| Command | Action |
|
|
36
33
|
| --- | --- |
|
|
37
|
-
| `
|
|
38
|
-
|
|
|
39
|
-
|
|
|
40
|
-
|
|
|
41
|
-
|
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
371
|
-
|
|
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
|
|
459
|
-
|
|
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
|
|
481
|
+
const commandBranch = branchGeneration;
|
|
465
482
|
const review = reviewBoundary;
|
|
466
483
|
const draft = latestAssistantDraft(ctx, review);
|
|
467
|
-
const
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
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 (
|
|
482
|
-
|
|
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
|
-
|
|
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
|
}
|