pi-prompt-template-model-enhanced 0.11.2 → 0.12.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/CHANGELOG.md +22 -2
- package/README.md +13 -1
- package/package.json +1 -1
- package/prompt-dry-run-tui.ts +70 -3
- package/prompt-dry-run.ts +5 -1
- package/prompt-includes.ts +510 -39
- package/prompt-loader.ts +296 -1
- package/prompt-validation.ts +132 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,24 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.12.0](https://github.com/Nabsku/pi-prompt-template-model-enhanced/compare/v0.11.2...v0.12.0) (2026-06-20)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Features
|
|
7
|
+
|
|
8
|
+
* add includes pane to dry-run TUI ([50624a4](https://github.com/Nabsku/pi-prompt-template-model-enhanced/commit/50624a46a5722d2ca74ebcb0423e5e99f75dbad1))
|
|
9
|
+
* attach include graphs to dry runs ([a1d7eba](https://github.com/Nabsku/pi-prompt-template-model-enhanced/commit/a1d7eba676158faed0c4e9cf889e5525cf5e5923))
|
|
10
|
+
* attach include graphs to validation ([d694724](https://github.com/Nabsku/pi-prompt-template-model-enhanced/commit/d69472452a6af1b7f94f75df3002b65d5bdc62c8))
|
|
11
|
+
* collect prompt include graphs ([be7c726](https://github.com/Nabsku/pi-prompt-template-model-enhanced/commit/be7c726fb17a43524068ef8da819306304e79091))
|
|
12
|
+
* collect prompt source records ([0d0df7a](https://github.com/Nabsku/pi-prompt-template-model-enhanced/commit/0d0df7a38a460fb1d890596e8f334a9be5772892))
|
|
13
|
+
* expose prompt include parsing helpers ([3e784a6](https://github.com/Nabsku/pi-prompt-template-model-enhanced/commit/3e784a672304100e70ae3a6603a05be6c2744e5e))
|
|
14
|
+
* report prompt include graphs ([5332c8e](https://github.com/Nabsku/pi-prompt-template-model-enhanced/commit/5332c8e8c49925aec20ebe2ba913e6b6a13f814f))
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
### Bug Fixes
|
|
18
|
+
|
|
19
|
+
* avoid duplicate include graph diagnostics ([adb6c97](https://github.com/Nabsku/pi-prompt-template-model-enhanced/commit/adb6c971fa3e1faea902aead4493fd47cf4121a6))
|
|
20
|
+
* preserve skipped include graphs for overrides ([2b0e69f](https://github.com/Nabsku/pi-prompt-template-model-enhanced/commit/2b0e69f3f158c8e5e8ef3bae45053cdd267a19e7))
|
|
21
|
+
|
|
3
22
|
## [0.11.2](https://github.com/Nabsku/pi-prompt-template-model-enhanced/compare/v0.11.1...v0.11.2) (2026-06-19)
|
|
4
23
|
|
|
5
24
|
|
|
@@ -39,10 +58,11 @@
|
|
|
39
58
|
|
|
40
59
|
### Added
|
|
41
60
|
- Added `/print-prompt` and `/dry-run-prompt` to preview rendered prompt templates without execution.
|
|
42
|
-
- Added a Pi-native dry-run TUI: no-name dry-run commands open a searchable template picker in Pi TUI mode, while named templates open a read-only inspector for prompt body, metadata, skills, warnings, and the raw report.
|
|
61
|
+
- Added a Pi-native dry-run TUI: no-name dry-run commands open a searchable template picker in Pi TUI mode, while named templates open a read-only inspector for prompt body, metadata, skills, includes, warnings, and the raw report.
|
|
62
|
+
- Added a static, permanent `Includes` pane to the dry-run TUI inspector. It shows the include graph captured during dry-run rendering, or `No includes.` for prompts without include metadata or inline include directives.
|
|
43
63
|
|
|
44
64
|
### Changed
|
|
45
|
-
- Added `/validate-prompts` to validate prompt templates, includes, chain declarations, and skill references before runtime.
|
|
65
|
+
- Added `/validate-prompts` to validate prompt templates, includes, chain declarations, and skill references before runtime, including include graph reporting for prompt dependencies and include failures.
|
|
46
66
|
- Updated development-only Pi packages from the deprecated `@mariozechner/*` namespace to `@earendil-works/*` `0.79.7`, and bumped `tsx` to `^4.22.4`.
|
|
47
67
|
- Added Dependabot version updates for npm development dependencies and GitHub Actions.
|
|
48
68
|
- Added Release Please release automation and npm trusted publishing workflow.
|
package/README.md
CHANGED
|
@@ -83,10 +83,15 @@ Start a Python REPL session and help me debug: $@
|
|
|
83
83
|
|
|
84
84
|
Run `/validate-prompts` to check prompt templates before using them. It reloads the project and user prompt directories, validates frontmatter, include paths, include cycles, chain declarations, reserved command names, and skill references that can be resolved from registered or filesystem skills.
|
|
85
85
|
|
|
86
|
+
Validation also reports an include graph for prompts that declare frontmatter includes (`include` / `includes`), use inline include directives, or fail include processing. Each relevant prompt is listed with its include dependencies, including nested partial-to-partial includes.
|
|
87
|
+
|
|
86
88
|
A clean library reports success:
|
|
87
89
|
|
|
88
90
|
```text
|
|
89
91
|
[pi-prompt-template-model-enhanced] Prompt validation passed: 4 prompt template(s) loaded.
|
|
92
|
+
Include graph:
|
|
93
|
+
- review [ok] /repo/.pi/prompts/review.md
|
|
94
|
+
- review -> /repo/.pi/prompts/shared/rules.md (frontmatter shared/rules.md) [ok]
|
|
90
95
|
```
|
|
91
96
|
|
|
92
97
|
Invalid libraries fail with explicit diagnostics:
|
|
@@ -95,8 +100,14 @@ Invalid libraries fail with explicit diagnostics:
|
|
|
95
100
|
[pi-prompt-template-model-enhanced] Prompt validation failed: 2 issue(s) found across 3 loaded prompt template(s).
|
|
96
101
|
- include-not-found (project) /repo/.pi/prompts/review.md: Prompt include "shared/rules.md" was not found ...
|
|
97
102
|
- skill-not-found (project) /repo/.pi/prompts/debug.md: Prompt template ... references skill "tmux", but it was not found ...
|
|
103
|
+
Include graph:
|
|
104
|
+
- review [skipped] /repo/.pi/prompts/review.md
|
|
105
|
+
- review -> unresolved:shared/rules.md (frontmatter shared/rules.md) [failed]
|
|
106
|
+
! include-not-found: Prompt include "shared/rules.md" was not found ...
|
|
98
107
|
```
|
|
99
108
|
|
|
109
|
+
Include graph statuses are concise: `[ok]` means the prompt or edge resolved successfully, `[skipped]` means the loader skipped that prompt because include processing failed or was invalid, and `[failed]` marks a failed edge or root diagnostic. Prompts skipped for missing, cyclic, or invalid includes still appear in the include graph with `[skipped]`, failed include edges, and diagnostic codes such as `include-not-found` or `include-cycle`.
|
|
110
|
+
|
|
100
111
|
## Dry-run and TUI preview
|
|
101
112
|
|
|
102
113
|
Use `/print-prompt` or `/dry-run-prompt` to preview what a prompt template would send before it executes. The preview uses the same include rendering, model conditionals, argument substitution, skill resolution, loop metadata, delegation metadata, and runtime flags as normal execution, but it does not switch models, send user messages, run deterministic commands, or start subagents.
|
|
@@ -126,7 +137,8 @@ TUI behavior:
|
|
|
126
137
|
- a template name opens the inspector directly;
|
|
127
138
|
- `--plain` forces the stdout/plain report even in TUI mode;
|
|
128
139
|
- unsupported templates, such as deterministic or chain templates, show the same diagnostic as the plain path;
|
|
129
|
-
- full skill content remains hidden unless the dry-run result was created with `--show-skills
|
|
140
|
+
- full skill content remains hidden unless the dry-run result was created with `--show-skills`;
|
|
141
|
+
- the inspector always includes a static `Includes` pane. Prompts with include metadata or inline include directives show the include graph captured during dry-run rendering; prompts without includes show `No includes.`.
|
|
130
142
|
|
|
131
143
|
The inspector is read-only. It has no execute button and does not mutate the session.
|
|
132
144
|
|
package/package.json
CHANGED
package/prompt-dry-run-tui.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { decodeKittyPrintable, Key, matchesKey, truncateToWidth, type Component } from "@earendil-works/pi-tui";
|
|
2
2
|
import type { PromptDryRunResult } from "./prompt-dry-run.js";
|
|
3
|
+
import type { PromptIncludeGraph, PromptIncludeGraphEdge, PromptIncludeGraphNode } from "./prompt-includes.js";
|
|
4
|
+
import type { PromptLoaderDiagnostic } from "./prompt-loader.js";
|
|
3
5
|
|
|
4
6
|
export interface PromptTemplateCatalogItem {
|
|
5
7
|
name: string;
|
|
@@ -20,6 +22,7 @@ export interface PromptDryRunTuiViewModel {
|
|
|
20
22
|
prompt: string;
|
|
21
23
|
metadata: string;
|
|
22
24
|
skills: string;
|
|
25
|
+
includes: string;
|
|
23
26
|
warnings: string;
|
|
24
27
|
raw: string;
|
|
25
28
|
};
|
|
@@ -83,6 +86,69 @@ function formatSkills(result: PromptDryRunResult): string {
|
|
|
83
86
|
return lines.join("\n");
|
|
84
87
|
}
|
|
85
88
|
|
|
89
|
+
function lexicalCompare(a: string, b: string): number {
|
|
90
|
+
if (a < b) return -1;
|
|
91
|
+
if (a > b) return 1;
|
|
92
|
+
return 0;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function diagnosticKey(diagnostic: PromptLoaderDiagnostic): string {
|
|
96
|
+
return diagnostic.key || `${diagnostic.code}:${diagnostic.source}:${diagnostic.filePath}:${diagnostic.message}`;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function sortDiagnostics(diagnostics: PromptLoaderDiagnostic[]): PromptLoaderDiagnostic[] {
|
|
100
|
+
return [...diagnostics].sort((a, b) => lexicalCompare(a.filePath, b.filePath) || lexicalCompare(a.code, b.code) || lexicalCompare(a.message, b.message));
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function sortIncludeGraphEdges(edges: PromptIncludeGraphEdge[]): PromptIncludeGraphEdge[] {
|
|
104
|
+
return [...edges].sort((a, b) => a.order - b.order || lexicalCompare(a.fromNodeId, b.fromNodeId) || lexicalCompare(a.toNodeId, b.toNodeId) || lexicalCompare(a.includePath, b.includePath));
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function includeGraphNodeLabel(nodes: Map<string, PromptIncludeGraphNode>, nodeId: string): string {
|
|
108
|
+
const node = nodes.get(nodeId);
|
|
109
|
+
if (!node) return nodeId;
|
|
110
|
+
if (node.filePath) return node.filePath;
|
|
111
|
+
if (node.includePath) return `unresolved:${node.includePath}`;
|
|
112
|
+
return node.id;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function formatIncludeDiagnostic(prefix: string, diagnostic: PromptLoaderDiagnostic): string {
|
|
116
|
+
return `${prefix}${diagnostic.code}: ${diagnostic.message}`;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function rootOnlyIncludeDiagnostics(graph: PromptIncludeGraph): PromptLoaderDiagnostic[] {
|
|
120
|
+
const edgeDiagnosticKeys = new Set(graph.edges.flatMap((edge) => edge.diagnostics.map(diagnosticKey)));
|
|
121
|
+
return graph.diagnostics.filter((diagnostic) => !edgeDiagnosticKeys.has(diagnosticKey(diagnostic)));
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function formatIncludes(result: PromptDryRunResult): string {
|
|
125
|
+
const graph = result.status === "ok" ? result.includeGraph : undefined;
|
|
126
|
+
if (!graph) return "No includes.";
|
|
127
|
+
|
|
128
|
+
const rootDiagnostics = sortDiagnostics(rootOnlyIncludeDiagnostics(graph));
|
|
129
|
+
if (graph.edges.length === 0 && rootDiagnostics.length === 0) return "No includes.";
|
|
130
|
+
|
|
131
|
+
const nodes = new Map(graph.nodes.map((node) => [node.id, node]));
|
|
132
|
+
const rootNode = graph.nodes.find((node) => node.kind === "prompt" && node.filePath === graph.root.filePath);
|
|
133
|
+
const rootStatus = rootNode?.status ?? (rootDiagnostics.length ? "failed" : "ok");
|
|
134
|
+
const lines = [`- ${graph.root.promptName} [${rootStatus}] ${graph.root.filePath}`];
|
|
135
|
+
|
|
136
|
+
for (const diagnostic of rootDiagnostics) {
|
|
137
|
+
lines.push(formatIncludeDiagnostic(" ! ", diagnostic));
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
for (const edge of sortIncludeGraphEdges(graph.edges)) {
|
|
141
|
+
const from = includeGraphNodeLabel(nodes, edge.fromNodeId);
|
|
142
|
+
const to = includeGraphNodeLabel(nodes, edge.toNodeId);
|
|
143
|
+
lines.push(` - ${from} -> ${to} (${edge.kind} ${edge.includePath}) [${edge.status}]`);
|
|
144
|
+
for (const diagnostic of sortDiagnostics(edge.diagnostics)) {
|
|
145
|
+
lines.push(formatIncludeDiagnostic(" ! ", diagnostic));
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
return lines.join("\n");
|
|
150
|
+
}
|
|
151
|
+
|
|
86
152
|
export function createPromptDryRunTuiViewModel(result: PromptDryRunResult, plainReport: string): PromptDryRunTuiViewModel {
|
|
87
153
|
const warnings = result.warnings.length ? result.warnings.map((warning) => `- ${warning}`).join("\n") : "No warnings.";
|
|
88
154
|
const prompt = result.status === "ok" ? `# Prompt body\n${result.content}` : `# Error\n${result.error}`;
|
|
@@ -100,6 +166,7 @@ export function createPromptDryRunTuiViewModel(result: PromptDryRunResult, plain
|
|
|
100
166
|
prompt,
|
|
101
167
|
metadata,
|
|
102
168
|
skills: formatSkills(result),
|
|
169
|
+
includes: formatIncludes(result),
|
|
103
170
|
warnings,
|
|
104
171
|
raw: plainReport,
|
|
105
172
|
},
|
|
@@ -191,7 +258,7 @@ export class PromptDryRunPicker implements Component {
|
|
|
191
258
|
invalidate(): void {}
|
|
192
259
|
}
|
|
193
260
|
|
|
194
|
-
const PANE_NAMES = ["Prompt", "Metadata", "Skills", "Warnings", "Raw"] as const;
|
|
261
|
+
const PANE_NAMES = ["Prompt", "Metadata", "Skills", "Includes", "Warnings", "Raw"] as const;
|
|
195
262
|
type PaneName = typeof PANE_NAMES[number];
|
|
196
263
|
|
|
197
264
|
export class PromptDryRunInspector implements Component {
|
|
@@ -229,7 +296,7 @@ export class PromptDryRunInspector implements Component {
|
|
|
229
296
|
"",
|
|
230
297
|
...body,
|
|
231
298
|
"",
|
|
232
|
-
`pane ${this.paneIndex + 1}
|
|
299
|
+
`pane ${this.paneIndex + 1}/${PANE_NAMES.length} · line ${Math.min(this.scroll + 1, paneLines.length)}/${paneLines.length} · j/down scroll · tab next · b back · q quit`,
|
|
233
300
|
];
|
|
234
301
|
return linesSafe(lines, width);
|
|
235
302
|
}
|
|
@@ -248,7 +315,7 @@ export class PromptDryRunInspector implements Component {
|
|
|
248
315
|
this.scroll = 0;
|
|
249
316
|
return;
|
|
250
317
|
}
|
|
251
|
-
const paneKey = (
|
|
318
|
+
const paneKey = Array.from({ length: PANE_NAMES.length }, (_, index) => String(index + 1)).find((key) => matchesKey(data, key));
|
|
252
319
|
if (paneKey) {
|
|
253
320
|
this.paneIndex = Number(paneKey) - 1;
|
|
254
321
|
this.scroll = 0;
|
package/prompt-dry-run.ts
CHANGED
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
import type { RegistryLike } from "./model-selection.js";
|
|
12
12
|
import { preparePromptExecution } from "./prompt-execution.js";
|
|
13
13
|
import { expandCwdPath, type PromptWithModel } from "./prompt-loader.js";
|
|
14
|
+
import type { PromptIncludeGraph } from "./prompt-includes.js";
|
|
14
15
|
import { getRequestedSkills, resolvePromptSkills, type RuntimeSkillCommand } from "./prompt-skills.js";
|
|
15
16
|
import { DEFAULT_SUBAGENT_NAME } from "./subagent-runtime.js";
|
|
16
17
|
|
|
@@ -53,6 +54,7 @@ export interface PromptDryRunRuntimeMetadata {
|
|
|
53
54
|
|
|
54
55
|
export interface PromptDryRunDetails {
|
|
55
56
|
skills: PromptDryRunSkillPreview[];
|
|
57
|
+
includeGraph?: PromptIncludeGraph;
|
|
56
58
|
}
|
|
57
59
|
|
|
58
60
|
export interface PromptDryRunSuccess {
|
|
@@ -65,6 +67,7 @@ export interface PromptDryRunSuccess {
|
|
|
65
67
|
warnings: string[];
|
|
66
68
|
skills: PromptDryRunSkillPreview[];
|
|
67
69
|
details: PromptDryRunDetails;
|
|
70
|
+
includeGraph?: PromptIncludeGraph;
|
|
68
71
|
runtime: PromptDryRunRuntimeMetadata;
|
|
69
72
|
}
|
|
70
73
|
|
|
@@ -379,7 +382,8 @@ export async function createPromptDryRun(
|
|
|
379
382
|
modelAlreadyActive: prepared.selectedModel.alreadyActive,
|
|
380
383
|
warnings,
|
|
381
384
|
skills: skillPreviews,
|
|
382
|
-
|
|
385
|
+
includeGraph: effectivePrompt.includeGraph,
|
|
386
|
+
details: { skills: skillPreviews, includeGraph: effectivePrompt.includeGraph },
|
|
383
387
|
runtime,
|
|
384
388
|
};
|
|
385
389
|
}
|