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 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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-prompt-template-model-enhanced",
3
- "version": "0.11.2",
3
+ "version": "0.12.0",
4
4
  "type": "module",
5
5
  "description": "Prompt template model selector extension for pi coding agent",
6
6
  "author": "Nico Bailon",
@@ -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}/5 · line ${Math.min(this.scroll + 1, paneLines.length)}/${paneLines.length} · j/down scroll · tab next · b back · q quit`,
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 = (["1", "2", "3", "4", "5"] as const).find((key) => matchesKey(data, key));
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
- details: { skills: skillPreviews },
385
+ includeGraph: effectivePrompt.includeGraph,
386
+ details: { skills: skillPreviews, includeGraph: effectivePrompt.includeGraph },
383
387
  runtime,
384
388
  };
385
389
  }