@henryqw/pi-prompt-creator 3.1.4 → 3.1.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/CONTEXT.md +16 -17
- package/README.md +5 -3
- package/extensions/prompt-creator.ts +27 -36
- package/package.json +3 -3
package/CONTEXT.md
CHANGED
|
@@ -1,24 +1,23 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Pi Prompt Creator
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Pi Prompt Creator finds reusable prompt ideas in the current conversation. Main and the user review each idea before the user approves a new global prompt.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
## Language
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
**Current Conversation**:
|
|
8
|
+
The model-visible conversation on the active branch, including its retained summary and effective user and completed assistant text. Inactive branches, removed context, tool traffic, and thinking are not prompt-analysis evidence.
|
|
8
9
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
- **Prompt Candidate**: One unsaved, editable prompt template proposed from the strongest Signal. A session has at most one pending Prompt Candidate; the user may explicitly request another after the pending candidate is shown or dismissed.
|
|
12
|
-
- **Prompt Review**: Conversational refinement of a Prompt Candidate by the user and Main.
|
|
13
|
-
- **Final Prompt Draft**: Complete prompt-template Markdown authored by Main after Prompt Review. A Prompt Candidate is not a Final Prompt Draft.
|
|
14
|
-
- **Approved Prompt**: A Final Prompt Draft the user explicitly accepts for creation as a global Pi prompt. Approval never authorizes replacing an existing prompt.
|
|
10
|
+
**Signal**:
|
|
11
|
+
Evidence that a reusable prompt could help: explicit recurrence language, requests with the same intent, or repeated corrections of Main's behavior.
|
|
15
12
|
|
|
16
|
-
|
|
13
|
+
**Prompt Candidate**:
|
|
14
|
+
One unsaved prompt template proposed from a Signal. A pending candidate is untrusted review material, not an approved prompt.
|
|
17
15
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
| Cross-session evidence and historical search | `@henryqw/pi-session-recall` |
|
|
21
|
-
| Scripts, Skills, and other automation | Their owning package or repository |
|
|
22
|
-
| This extension | Current-conversation Signal detection, Prompt Candidate generation, and Prompt Review |
|
|
16
|
+
**Prompt Review**:
|
|
17
|
+
Conversational refinement of a displayed Prompt Candidate by the user and Main.
|
|
23
18
|
|
|
24
|
-
|
|
19
|
+
**Final Prompt Draft**:
|
|
20
|
+
The complete prompt-template Markdown in Main's latest valid retained review reply after the candidate was displayed. A Prompt Candidate is not a Final Prompt Draft.
|
|
21
|
+
|
|
22
|
+
**Approved Prompt**:
|
|
23
|
+
A Final Prompt Draft that the user explicitly accepts for creation as a new global Pi prompt. Approval does not permit replacement of an existing prompt.
|
package/README.md
CHANGED
|
@@ -89,7 +89,7 @@ A successful save reloads Pi resources. If reload fails, the prompt remains save
|
|
|
89
89
|
|
|
90
90
|
The extension registers `pi-prompt-creator/draft`. Its default task profile is `fast`.
|
|
91
91
|
|
|
92
|
-
Use `/task-models` to select the model and thinking level. The extension owns no model configuration.
|
|
92
|
+
Use `/task-models` to select the model and thinking level. The extension owns no model configuration. At session start and before each analysis, it shows each problem in the shared task-model config one time in each session.
|
|
93
93
|
|
|
94
94
|
## Config
|
|
95
95
|
|
|
@@ -104,9 +104,11 @@ Edit either field. Then run `/reload` to apply the change. Omitted fields use th
|
|
|
104
104
|
|
|
105
105
|
A missing file quietly uses both defaults. Startup never creates or rewrites the file.
|
|
106
106
|
|
|
107
|
-
|
|
107
|
+
At session start, Pi shows a warning with the file path and each problem for an unknown key or an invalid value. Pi ignores the unknown key. A misspelled key near a missing field, such as `automatc`, counts as an invalid value of that field. An invalid `inputThreshold` uses the default `3`.
|
|
108
108
|
|
|
109
|
-
|
|
109
|
+
Automatic analysis stays off for the session when `automatic` is invalid or misspelled, or when the file has malformed JSON, invalid UTF-8, more than 64 KiB, a read error, or a non-object root. Startup leaves the file unchanged.
|
|
110
|
+
|
|
111
|
+
Only `/promptor automatic on` or `/promptor automatic off` writes the config. A toggle reads the file again and keeps its current `inputThreshold`, also when another session changed it. A toggle also replaces a file with problems: it first shows the problems, then keeps the usable values and removes unknown keys. If another session changes that file before the write, the toggle stops and writes nothing.
|
|
110
112
|
|
|
111
113
|
## State and storage
|
|
112
114
|
|
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
type ExtensionContext,
|
|
11
11
|
type SessionEntry,
|
|
12
12
|
} from "@earendil-works/pi-coding-agent";
|
|
13
|
-
import { createConfigStore } from "@henryqw/pi-config-store";
|
|
13
|
+
import { createConfigStore, field, parsers, registerConfigReporting } from "@henryqw/pi-config-store";
|
|
14
14
|
import {
|
|
15
15
|
createEphemeralSubagentExecutor,
|
|
16
16
|
resolveRoleLaunch,
|
|
@@ -19,6 +19,7 @@ import {
|
|
|
19
19
|
} from "@henryqw/pi-subagent";
|
|
20
20
|
import {
|
|
21
21
|
registerModelTask,
|
|
22
|
+
registerTaskModelsConfigReporting,
|
|
22
23
|
type ModelTask,
|
|
23
24
|
} from "@henryqw/pi-task-models";
|
|
24
25
|
|
|
@@ -60,7 +61,6 @@ or
|
|
|
60
61
|
The object must have exactly these keys. name must start with a lowercase ASCII letter, use only lowercase ASCII letters, digits, and single hyphens, and be at most ${MAX_NAME_CHARS} characters. markdown must be nonempty, at most ${MAX_MARKDOWN_BYTES} UTF-8 bytes, and contain no C0 or C1 controls except tab and LF.`,
|
|
61
62
|
} satisfies Role;
|
|
62
63
|
|
|
63
|
-
type Config = { automatic: boolean; inputThreshold: number };
|
|
64
64
|
type PromptCandidate = { name: string; markdown: string };
|
|
65
65
|
type ConversationItem = { role: "summary" | "user" | "assistant"; text: string };
|
|
66
66
|
type ContextMessage = ReturnType<typeof buildSessionContext>["messages"][number];
|
|
@@ -80,21 +80,6 @@ function exactKeys(value: Record<string, unknown>, keys: readonly string[]): boo
|
|
|
80
80
|
return actual.length === keys.length && keys.every((key) => Object.hasOwn(value, key));
|
|
81
81
|
}
|
|
82
82
|
|
|
83
|
-
function parseConfig(value: unknown): Config {
|
|
84
|
-
if (!value || typeof value !== "object" || Array.isArray(value)) throw new Error("Config must be an object.");
|
|
85
|
-
const config = value as Record<string, unknown>;
|
|
86
|
-
const validKeys = Object.keys(config).every((key) => key === "automatic" || key === "inputThreshold");
|
|
87
|
-
const { automatic = true, inputThreshold = DEFAULT_INPUT_THRESHOLD } = config;
|
|
88
|
-
if (
|
|
89
|
-
!validKeys
|
|
90
|
-
|| typeof automatic !== "boolean"
|
|
91
|
-
|| typeof inputThreshold !== "number"
|
|
92
|
-
|| !Number.isSafeInteger(inputThreshold)
|
|
93
|
-
|| inputThreshold < 1
|
|
94
|
-
) throw new Error("Config may contain automatic:boolean and inputThreshold:positive integer.");
|
|
95
|
-
return { automatic, inputThreshold };
|
|
96
|
-
}
|
|
97
|
-
|
|
98
83
|
function isPromptName(value: unknown): value is string {
|
|
99
84
|
return typeof value === "string" && value.length <= MAX_NAME_CHARS && NAME.test(value);
|
|
100
85
|
}
|
|
@@ -253,17 +238,20 @@ async function createPromptFile(path: string, markdown: string): Promise<void> {
|
|
|
253
238
|
export default function promptCreatorExtension(pi: ExtensionAPI, options: PromptCreatorOptions = {}): void {
|
|
254
239
|
registerModelTask(pi, DRAFT_TASK);
|
|
255
240
|
const agentDir = options.agentDir ?? getAgentDir();
|
|
256
|
-
const configStore = createConfigStore
|
|
241
|
+
const configStore = createConfigStore({
|
|
257
242
|
extensionId: EXTENSION_ID,
|
|
258
243
|
agentDir,
|
|
259
|
-
|
|
260
|
-
|
|
244
|
+
fields: {
|
|
245
|
+
automatic: field(parsers.boolean(), true),
|
|
246
|
+
inputThreshold: field(parsers.integer({ min: 1 }), DEFAULT_INPUT_THRESHOLD),
|
|
247
|
+
},
|
|
261
248
|
});
|
|
249
|
+
const configReporter = registerConfigReporting(pi, [configStore]);
|
|
250
|
+
const reportTaskModels = registerTaskModelsConfigReporting(pi, agentDir);
|
|
262
251
|
let executor = options.executor;
|
|
263
252
|
let automatic = false;
|
|
264
253
|
let inputThreshold = DEFAULT_INPUT_THRESHOLD;
|
|
265
254
|
let automaticConsumed = false;
|
|
266
|
-
let configWarned = false;
|
|
267
255
|
let inputCount = 0;
|
|
268
256
|
let branchGeneration = 0;
|
|
269
257
|
let candidate: PromptCandidate | undefined;
|
|
@@ -328,6 +316,7 @@ export default function promptCreatorExtension(pi: ExtensionAPI, options: Prompt
|
|
|
328
316
|
void Promise.resolve().then(() => getExecutor().run({
|
|
329
317
|
signal: run.controller.signal,
|
|
330
318
|
prepare: async () => {
|
|
319
|
+
reportTaskModels(ctx);
|
|
331
320
|
const launch = resolveRoleLaunch(pi, ctx, {
|
|
332
321
|
role: DRAFT_ROLE,
|
|
333
322
|
task: DRAFT_TASK,
|
|
@@ -397,18 +386,11 @@ export default function promptCreatorExtension(pi: ExtensionAPI, options: Prompt
|
|
|
397
386
|
activeRun?.controller.abort(new Error("Prompt Creator session changed."));
|
|
398
387
|
activeRun = undefined;
|
|
399
388
|
resetBranch(ctx);
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
automatic = false;
|
|
406
|
-
inputThreshold = DEFAULT_INPUT_THRESHOLD;
|
|
407
|
-
if (!configWarned) {
|
|
408
|
-
configWarned = true;
|
|
409
|
-
ctx.ui.notify("Prompt Creator config is invalid. Automatic analysis is disabled; the file was left unchanged.", "warning");
|
|
410
|
-
}
|
|
411
|
-
}
|
|
389
|
+
const loaded = configStore.loadSync();
|
|
390
|
+
configReporter.report(ctx, configStore, loaded);
|
|
391
|
+
// A file-level problem or an invalid or misspelled `automatic` keeps automatic analysis off; a missing file uses the default.
|
|
392
|
+
automatic = loaded.status === "ok" && loaded.value.automatic && !loaded.issues.some(({ key }) => key === "" || key === "automatic");
|
|
393
|
+
inputThreshold = loaded.status === "ok" ? loaded.value.inputThreshold : DEFAULT_INPUT_THRESHOLD;
|
|
412
394
|
});
|
|
413
395
|
|
|
414
396
|
pi.on("input", (event, ctx) => {
|
|
@@ -491,11 +473,20 @@ export default function promptCreatorExtension(pi: ExtensionAPI, options: Prompt
|
|
|
491
473
|
if (action === "automatic" && values.length === 1 && (values[0] === "on" || values[0] === "off")) {
|
|
492
474
|
const next = values[0] === "on";
|
|
493
475
|
try {
|
|
494
|
-
|
|
476
|
+
// Write the latest file, not the session's cached threshold, and report its problems first.
|
|
477
|
+
const loaded = configStore.loadSync();
|
|
478
|
+
configReporter.report(ctx, configStore, loaded);
|
|
479
|
+
// No field disables this config, so a load always has a value.
|
|
480
|
+
if (loaded.status !== "ok") return;
|
|
481
|
+
let written = { ...loaded.value, automatic: next };
|
|
482
|
+
if (!loaded.issues.length) written = await configStore.update((latest) => ({ ...latest, automatic: next }));
|
|
483
|
+
// A file with problems is replaced with its usable values, and only if no other session changed it meanwhile.
|
|
484
|
+
else await configStore.replaceIfUnchanged(loaded, written);
|
|
495
485
|
automatic = next;
|
|
486
|
+
inputThreshold = written.inputThreshold;
|
|
496
487
|
ctx.ui.notify(`Automatic analysis ${next ? "enabled" : "disabled"}.`, "info");
|
|
497
|
-
} catch {
|
|
498
|
-
ctx.ui.notify(
|
|
488
|
+
} catch (error) {
|
|
489
|
+
ctx.ui.notify(`Could not save Prompt Creator config: ${error instanceof Error ? error.message : String(error)}`, "error");
|
|
499
490
|
}
|
|
500
491
|
return;
|
|
501
492
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@henryqw/pi-prompt-creator",
|
|
3
|
-
"version": "3.1.
|
|
3
|
+
"version": "3.1.6",
|
|
4
4
|
"description": "Turn current Pi conversation signals into user-approved prompt templates.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"pi-package",
|
|
@@ -45,9 +45,9 @@
|
|
|
45
45
|
]
|
|
46
46
|
},
|
|
47
47
|
"dependencies": {
|
|
48
|
-
"@henryqw/pi-config-store": "^
|
|
48
|
+
"@henryqw/pi-config-store": "^4.0.0",
|
|
49
49
|
"@henryqw/pi-subagent": "^22.0.0 || ^23.0.0 || ^24.0.0 || ^25.0.0 || ^26.0.0 || ^27.0.0",
|
|
50
|
-
"@henryqw/pi-task-models": "^
|
|
50
|
+
"@henryqw/pi-task-models": "^9.0.0"
|
|
51
51
|
},
|
|
52
52
|
"devDependencies": {
|
|
53
53
|
"@earendil-works/pi-coding-agent": "1.1.0"
|