@henryqw/pi-prompt-creator 3.1.4 → 3.1.5
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 -0
- package/README.md +5 -3
- package/extensions/prompt-creator.ts +27 -36
- package/package.json +3 -3
package/CONTEXT.md
CHANGED
|
@@ -19,6 +19,7 @@ Turn repetition and correction in the current Pi conversation into one editable
|
|
|
19
19
|
| --- | --- |
|
|
20
20
|
| Cross-session evidence and historical search | `@henryqw/pi-session-recall` |
|
|
21
21
|
| Scripts, Skills, and other automation | Their owning package or repository |
|
|
22
|
+
| User config load rules and launch reporting | `@henryqw/pi-config-store` ([ADR 004](../../docs/adr/004-extension-config-safety.md)) |
|
|
22
23
|
| This extension | Current-conversation Signal detection, Prompt Candidate generation, and Prompt Review |
|
|
23
24
|
|
|
24
25
|
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
|
@@ -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.5",
|
|
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"
|