@henryqw/pi-prompt-creator 1.0.6 → 1.1.1
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 +62 -59
- 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",
|
|
@@ -278,8 +278,6 @@ export default function promptCreatorExtension(pi: ExtensionAPI, options: Prompt
|
|
|
278
278
|
let configWarned = false;
|
|
279
279
|
let inputCount = 0;
|
|
280
280
|
let branchGeneration = 0;
|
|
281
|
-
let closed = false;
|
|
282
|
-
let analysisRan = false;
|
|
283
281
|
let candidate: PromptCandidate | undefined;
|
|
284
282
|
let candidateNameHint: string | undefined;
|
|
285
283
|
let reviewBoundary: ReviewBoundary | undefined;
|
|
@@ -295,11 +293,10 @@ export default function promptCreatorExtension(pi: ExtensionAPI, options: Prompt
|
|
|
295
293
|
if (ctx.mode === "tui") ctx.ui.setWidget(WIDGET_KEY, [FAILURE_WIDGET]);
|
|
296
294
|
};
|
|
297
295
|
const isCurrent = (run: ActiveRun) =>
|
|
298
|
-
|
|
296
|
+
activeRun === run && branchGeneration === run.branchGeneration && !run.controller.signal.aborted;
|
|
299
297
|
const resetBranch = (ctx: ExtensionContext) => {
|
|
300
298
|
branchGeneration += 1;
|
|
301
299
|
inputCount = 0;
|
|
302
|
-
analysisRan = false;
|
|
303
300
|
candidate = undefined;
|
|
304
301
|
candidateNameHint = undefined;
|
|
305
302
|
reviewBoundary = undefined;
|
|
@@ -311,10 +308,23 @@ export default function promptCreatorExtension(pi: ExtensionAPI, options: Prompt
|
|
|
311
308
|
maxTurns: 3,
|
|
312
309
|
timeout: { idleMs: 2 * 60_000, maxMs: 5 * 60_000 },
|
|
313
310
|
});
|
|
311
|
+
const showCandidate = (ctx: ExtensionContext) => {
|
|
312
|
+
if (!candidate) return;
|
|
313
|
+
const shown = candidate;
|
|
314
|
+
const boundaryId = ctx.sessionManager.getLeafId();
|
|
315
|
+
candidate = undefined;
|
|
316
|
+
candidateNameHint = shown.name;
|
|
317
|
+
clearWidget(ctx);
|
|
318
|
+
pi.sendMessage({
|
|
319
|
+
customType: CANDIDATE_MESSAGE_TYPE,
|
|
320
|
+
content: candidateMessage(shown),
|
|
321
|
+
display: true,
|
|
322
|
+
}, { triggerTurn: false });
|
|
323
|
+
reviewBoundary = boundaryId ? { entryId: boundaryId } : undefined;
|
|
324
|
+
};
|
|
314
325
|
const startAnalysis = (ctx: ExtensionContext, manual: boolean) => {
|
|
315
326
|
if (ctx.mode !== "tui" || activeRun || candidate) return;
|
|
316
327
|
if (manual) automaticConsumed = true;
|
|
317
|
-
analysisRan = true;
|
|
318
328
|
failure = false;
|
|
319
329
|
candidateNameHint = undefined;
|
|
320
330
|
const payload = analysisPayload(pi, ctx);
|
|
@@ -353,7 +363,8 @@ export default function promptCreatorExtension(pi: ExtensionAPI, options: Prompt
|
|
|
353
363
|
}
|
|
354
364
|
candidate = drafted;
|
|
355
365
|
candidateNameHint = drafted.name;
|
|
356
|
-
|
|
366
|
+
if (manual) showCandidate(ctx);
|
|
367
|
+
else ctx.ui.setWidget(WIDGET_KEY, [READY_WIDGET]);
|
|
357
368
|
}).catch(() => {
|
|
358
369
|
if (isCurrent(run)) showFailure(ctx);
|
|
359
370
|
}).finally(() => {
|
|
@@ -363,13 +374,13 @@ export default function promptCreatorExtension(pi: ExtensionAPI, options: Prompt
|
|
|
363
374
|
|
|
364
375
|
const saveLatestDraft = async (
|
|
365
376
|
draft: string,
|
|
377
|
+
requestedName: string | undefined,
|
|
366
378
|
expectedReview: ReviewBoundary,
|
|
367
379
|
expectedBranch: number,
|
|
368
380
|
ctx: ExtensionCommandContext,
|
|
369
381
|
) => {
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
const name = requested.trim();
|
|
382
|
+
if (expectedBranch !== branchGeneration || reviewBoundary !== expectedReview) return;
|
|
383
|
+
const name = requestedName ?? candidateNameHint ?? "";
|
|
373
384
|
if (!isPromptName(name)) {
|
|
374
385
|
ctx.ui.notify(`Use lowercase kebab-case starting with a letter, up to ${MAX_NAME_CHARS} characters.`, "warning");
|
|
375
386
|
return;
|
|
@@ -398,7 +409,6 @@ export default function promptCreatorExtension(pi: ExtensionAPI, options: Prompt
|
|
|
398
409
|
};
|
|
399
410
|
|
|
400
411
|
pi.on("session_start", (_event, ctx) => {
|
|
401
|
-
closed = false;
|
|
402
412
|
activeRun?.controller.abort(new Error("Prompt Creator session changed."));
|
|
403
413
|
activeRun = undefined;
|
|
404
414
|
resetBranch(ctx);
|
|
@@ -442,44 +452,59 @@ export default function promptCreatorExtension(pi: ExtensionAPI, options: Prompt
|
|
|
442
452
|
|
|
443
453
|
pi.on("session_tree", (_event, ctx) => resetBranch(ctx));
|
|
444
454
|
pi.on("session_shutdown", (_event, ctx) => {
|
|
445
|
-
|
|
446
|
-
branchGeneration += 1;
|
|
447
|
-
candidate = undefined;
|
|
448
|
-
candidateNameHint = undefined;
|
|
449
|
-
reviewBoundary = undefined;
|
|
450
|
-
failure = false;
|
|
451
|
-
inputCount = 0;
|
|
452
|
-
clearWidget(ctx);
|
|
455
|
+
resetBranch(ctx);
|
|
453
456
|
activeRun?.controller.abort(new Error("Prompt Creator shut down."));
|
|
454
457
|
activeRun = undefined;
|
|
455
458
|
});
|
|
456
459
|
|
|
457
460
|
pi.registerCommand("promptor", {
|
|
458
|
-
description: "Analyze
|
|
459
|
-
|
|
461
|
+
description: "Analyze prompts, show a ready candidate, or save with /promptor save [name]",
|
|
462
|
+
getArgumentCompletions: (prefix) => {
|
|
463
|
+
const commands = ["analyze", "dismiss", "save", "automatic on", "automatic off"];
|
|
464
|
+
const matches = commands.filter((command) => command.startsWith(prefix));
|
|
465
|
+
return matches.length ? matches.map((command) => ({ value: command, label: command })) : null;
|
|
466
|
+
},
|
|
467
|
+
handler: async (args, ctx) => {
|
|
460
468
|
if (ctx.mode !== "tui") {
|
|
461
469
|
ctx.ui.notify("/promptor requires the interactive TUI.", "warning");
|
|
462
470
|
return;
|
|
463
471
|
}
|
|
464
|
-
const
|
|
472
|
+
const commandBranch = branchGeneration;
|
|
465
473
|
const review = reviewBoundary;
|
|
466
474
|
const draft = latestAssistantDraft(ctx, review);
|
|
467
|
-
const
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
startAnalysis(ctx, true);
|
|
475
|
+
const [action = "", ...values] = args.trim().split(/\s+/);
|
|
476
|
+
if (!action) {
|
|
477
|
+
if (candidate) showCandidate(ctx);
|
|
478
|
+
else if (activeRun) ctx.ui.notify("Prompt analysis is already running.", "info");
|
|
479
|
+
else if (draft) ctx.ui.notify("Run /promptor save to create the reviewed prompt.", "info");
|
|
480
|
+
else startAnalysis(ctx, true);
|
|
481
|
+
return;
|
|
482
|
+
}
|
|
483
|
+
if (action === "analyze" && values.length === 0) {
|
|
484
|
+
if (candidate) ctx.ui.notify("Show or dismiss the pending candidate first.", "warning");
|
|
485
|
+
else if (activeRun) ctx.ui.notify("Prompt analysis is already running.", "info");
|
|
486
|
+
else startAnalysis(ctx, true);
|
|
479
487
|
return;
|
|
480
488
|
}
|
|
481
|
-
if (
|
|
482
|
-
|
|
489
|
+
if (action === "dismiss" && values.length === 0) {
|
|
490
|
+
if (!candidate) ctx.ui.notify("No prompt candidate is waiting.", "warning");
|
|
491
|
+
else {
|
|
492
|
+
candidate = undefined;
|
|
493
|
+
candidateNameHint = undefined;
|
|
494
|
+
clearWidget(ctx);
|
|
495
|
+
}
|
|
496
|
+
return;
|
|
497
|
+
}
|
|
498
|
+
if (action === "save" && values.length <= 1) {
|
|
499
|
+
if (!draft || !review) {
|
|
500
|
+
ctx.ui.notify("No reviewed Main draft is ready to save.", "warning");
|
|
501
|
+
return;
|
|
502
|
+
}
|
|
503
|
+
await saveLatestDraft(draft, values[0], review, commandBranch, ctx);
|
|
504
|
+
return;
|
|
505
|
+
}
|
|
506
|
+
if (action === "automatic" && values.length === 1 && (values[0] === "on" || values[0] === "off")) {
|
|
507
|
+
const next = values[0] === "on";
|
|
483
508
|
try {
|
|
484
509
|
await configStore.save({ automatic: next, inputThreshold });
|
|
485
510
|
automatic = next;
|
|
@@ -489,29 +514,7 @@ export default function promptCreatorExtension(pi: ExtensionAPI, options: Prompt
|
|
|
489
514
|
}
|
|
490
515
|
return;
|
|
491
516
|
}
|
|
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
|
-
}
|
|
517
|
+
ctx.ui.notify("Use /promptor, /promptor analyze, /promptor dismiss, /promptor save [name], or /promptor automatic <on|off>.", "warning");
|
|
515
518
|
},
|
|
516
519
|
});
|
|
517
520
|
}
|