@mcuste/pi-diagram 0.0.0 → 0.2.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/README.md +20 -3
- package/dist/display.d.ts.map +1 -1
- package/dist/display.js +19 -4
- package/dist/display.js.map +1 -1
- package/dist/guidance.d.ts +28 -0
- package/dist/guidance.d.ts.map +1 -0
- package/dist/guidance.js +54 -0
- package/dist/guidance.js.map +1 -0
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/display.ts +25 -5
- package/src/guidance.ts +77 -0
- package/src/index.ts +3 -1
package/README.md
CHANGED
|
@@ -16,8 +16,7 @@ If any of those names are new to you:
|
|
|
16
16
|
|
|
17
17
|
> **Status: images, text, and files work.** In a terminal that supports inline images the diagram
|
|
18
18
|
> itself is shown; everywhere else it is box drawing or plain ASCII. Files land outside the
|
|
19
|
-
> repository unless a destination is named.
|
|
20
|
-
> [docs/terminal_diagram_tool_proposal.md](docs/terminal_diagram_tool_proposal.md).
|
|
19
|
+
> repository unless a destination is named.
|
|
21
20
|
|
|
22
21
|
## Why
|
|
23
22
|
|
|
@@ -124,6 +123,19 @@ language.
|
|
|
124
123
|
| `formats` | Files to produce: `source`, `svg`, `png`, `txt`. Written outside the repository |
|
|
125
124
|
| `save` | Also copy them into the repository. `dir` is required |
|
|
126
125
|
|
|
126
|
+
## When the model draws
|
|
127
|
+
|
|
128
|
+
A described tool gets called when the user asks for a diagram, not when a diagram is the clearer
|
|
129
|
+
answer. So the extension adds a short block to the host system prompt: draw when structure, flow,
|
|
130
|
+
or relationships are easier to see than to read, call the tool before explaining, keep the prose
|
|
131
|
+
to what the picture does not show, and never hand-write ASCII art or a Mermaid block. The block
|
|
132
|
+
also maps a question to a diagram, so components go to `architecture`, message order to a sequence
|
|
133
|
+
diagram, tables to `data`, a hierarchy to `tree`, and so on.
|
|
134
|
+
|
|
135
|
+
The result is fewer walls of text: an answer about how parts connect arrives as a picture with a
|
|
136
|
+
few lines around it. The block is appended to whatever the host built, is added once, and is left
|
|
137
|
+
out when the `diagram` tool is not active.
|
|
138
|
+
|
|
127
139
|
## What a profile changes
|
|
128
140
|
|
|
129
141
|
The model says what a diagram is for, and the tool decides how it looks:
|
|
@@ -161,6 +173,12 @@ to forward the protocol: tmux needs `allow-passthrough`, and herdr needs
|
|
|
161
173
|
The image never enters the model's context. It is written to a private temporary directory and
|
|
162
174
|
read back when the row is displayed.
|
|
163
175
|
|
|
176
|
+
The row is bounded to 80 by 30 character cells, so a dense diagram is drawn small. Where the
|
|
177
|
+
terminal supports OSC 8 hyperlinks, the title above the diagram links to that file: click it and
|
|
178
|
+
the diagram opens in the image viewer of the machine, which zooms and pans. A diagram with no
|
|
179
|
+
title shows the linked file name under the image instead. Ghostty, Kitty, WezTerm, and iTerm2 all
|
|
180
|
+
open such a link, some of them on a modified click.
|
|
181
|
+
|
|
164
182
|
D2 exports PNG by driving a headless browser it downloads on first use, which this tool will not
|
|
165
183
|
do during a call. Instead the SVG it already produces is rasterized locally by
|
|
166
184
|
[resvg](https://github.com/yisibl/resvg-js), which needs no browser and no network. Labels are
|
|
@@ -231,7 +249,6 @@ and a model given those knobs spends tokens on styling and produces a different
|
|
|
231
249
|
## Documentation
|
|
232
250
|
|
|
233
251
|
- [Safety model](docs/safety.md)
|
|
234
|
-
- [Design proposal](docs/terminal_diagram_tool_proposal.md)
|
|
235
252
|
- [Development and release](docs/development.md)
|
|
236
253
|
- [Changelog](CHANGELOG.md)
|
|
237
254
|
|
package/dist/display.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"display.d.ts","sourceRoot":"","sources":["../src/display.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"display.d.ts","sourceRoot":"","sources":["../src/display.ts"],"names":[],"mappings":"AAiBA,MAAM,WAAW,SAAS;IACxB,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;CACjC;AAED,MAAM,WAAW,YAAY;IAC3B,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;CACzC;AAED,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC;IAC7B,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;IAC3B,8EAA8E;IAC9E,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACzC;AAED,UAAU,YAAY;IACpB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;CAC3B;AAqBD;;;GAGG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,CAS9D;AAED,4EAA4E;AAC5E,wBAAgB,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC,CAY5C;AAED,kGAAkG;AAClG,wBAAgB,eAAe,IAAI,OAAO,GAAG,SAAS,CAErD;AAED,4FAA4F;AAC5F,wBAAgB,aAAa,IAAI,OAAO,CAEvC;AAWD,MAAM,WAAW,eAAe;IAC9B,8EAA8E;IAC9E,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,qEAAqE;IACrE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB;AAED,uFAAuF;AACvF,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,eAAe,EAAE,KAAK,EAAE,YAAY,GAAG,SAAS,CASvF;AAED,wBAAgB,mBAAmB,CACjC,IAAI,EAAE,WAAW,EACjB,KAAK,EAAE,YAAY,EACnB,OAAO,EAAE,cAAc,GACtB,SAAS,CA4CX;AAoBD,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,KAAK,EAAE,YAAY,GAAG,SAAS,CAAC;IACzC,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,CAAC;IACnC,8EAA8E;IAC9E,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;IAClC,mFAAmF;IACnF,QAAQ,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;CACrC"}
|
package/dist/display.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { readFileSync } from "node:fs";
|
|
2
2
|
import { createRequire } from "node:module";
|
|
3
|
+
import { basename, isAbsolute } from "node:path";
|
|
3
4
|
import { pathToFileURL } from "node:url";
|
|
4
5
|
/**
|
|
5
6
|
* Turns a diagram result into terminal components. Both the image and the text are drawn here
|
|
@@ -71,10 +72,11 @@ export function renderDiagramResult(view, theme, context) {
|
|
|
71
72
|
const line = (text) => {
|
|
72
73
|
container.addChild(new module.Text(theme.fg("toolOutput", text), 0, 0));
|
|
73
74
|
};
|
|
75
|
+
const image = drawable(module, view.image, context) ? view.image : undefined;
|
|
76
|
+
const url = image === undefined ? undefined : openable(module, image);
|
|
74
77
|
if (view.title !== undefined) {
|
|
75
|
-
line(view.title);
|
|
78
|
+
line(url === undefined ? view.title : module.hyperlink(view.title, url));
|
|
76
79
|
}
|
|
77
|
-
const image = drawable(module, view.image, context) ? view.image : undefined;
|
|
78
80
|
if (image === undefined) {
|
|
79
81
|
line(view.text);
|
|
80
82
|
}
|
|
@@ -85,6 +87,10 @@ export function renderDiagramResult(view, theme, context) {
|
|
|
85
87
|
maxHeightCells: MAX_HEIGHT_CELLS,
|
|
86
88
|
filename: image.path,
|
|
87
89
|
}, { widthPx: image.widthPx, heightPx: image.heightPx }));
|
|
90
|
+
if (view.title === undefined && url !== undefined) {
|
|
91
|
+
const name = module.hyperlink(basename(image.path), url);
|
|
92
|
+
container.addChild(new module.Text(theme.fg("muted", name), 0, 0));
|
|
93
|
+
}
|
|
88
94
|
}
|
|
89
95
|
catch {
|
|
90
96
|
// The picture is gone from the temp store, so show the text instead.
|
|
@@ -97,12 +103,21 @@ export function renderDiagramResult(view, theme, context) {
|
|
|
97
103
|
}
|
|
98
104
|
return container;
|
|
99
105
|
}
|
|
106
|
+
/**
|
|
107
|
+
* The picture as a link, so a click opens it where it can be zoomed. The row is bounded to keep
|
|
108
|
+
* the transcript readable, so the file is the only way to see a dense diagram in full.
|
|
109
|
+
*/
|
|
110
|
+
function openable(module, image) {
|
|
111
|
+
return module.getCapabilities().hyperlinks && isAbsolute(image.path)
|
|
112
|
+
? pathToFileURL(image.path).href
|
|
113
|
+
: undefined;
|
|
114
|
+
}
|
|
100
115
|
function drawable(module, image, context) {
|
|
101
116
|
return image !== undefined && context.showImages && module.getCapabilities().images !== null;
|
|
102
117
|
}
|
|
103
118
|
/** Reads the image once per result row: the host calls the renderer again on every redraw. */
|
|
104
119
|
function read(image, context) {
|
|
105
|
-
const cached = context.state
|
|
120
|
+
const cached = context.state.diagramImage;
|
|
106
121
|
if (typeof cached === "object" && cached !== null) {
|
|
107
122
|
const { path, encoded } = cached;
|
|
108
123
|
if (path === image.path && typeof encoded === "string") {
|
|
@@ -114,7 +129,7 @@ function read(image, context) {
|
|
|
114
129
|
throw new Error(`The image at ${image.path} is ${bytes.length} bytes.`);
|
|
115
130
|
}
|
|
116
131
|
const encoded = bytes.toString("base64");
|
|
117
|
-
context.state
|
|
132
|
+
context.state.diagramImage = { path: image.path, encoded };
|
|
118
133
|
return encoded;
|
|
119
134
|
}
|
|
120
135
|
//# sourceMappingURL=display.js.map
|
package/dist/display.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"display.js","sourceRoot":"","sources":["../src/display.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEzC;;;;GAIG;AAEH,8FAA8F;AAC9F,MAAM,eAAe,GAAG,EAAE,CAAC;AAC3B,kFAAkF;AAClF,MAAM,gBAAgB,GAAG,EAAE,CAAC;AAC5B,MAAM,eAAe,GAAG,CAAC,GAAG,IAAI,GAAG,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"display.js","sourceRoot":"","sources":["../src/display.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The prompt block that makes the model draw by itself. The tool description alone gets the tool
|
|
3
|
+
* called when the user asks for a diagram, not when a diagram is the clearer answer.
|
|
4
|
+
*/
|
|
5
|
+
export declare const DIAGRAM_GUIDANCE: string;
|
|
6
|
+
/** Pi hands over one prompt string; Oh My Pi hands over ordered blocks. */
|
|
7
|
+
type HostSystemPrompt = string | readonly string[];
|
|
8
|
+
interface AgentStartEvent {
|
|
9
|
+
readonly systemPrompt?: HostSystemPrompt;
|
|
10
|
+
/** Only Pi reports what it built the prompt from. */
|
|
11
|
+
readonly systemPromptOptions?: {
|
|
12
|
+
readonly selectedTools?: readonly string[];
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
interface AgentStartResult {
|
|
16
|
+
readonly systemPrompt: HostSystemPrompt;
|
|
17
|
+
}
|
|
18
|
+
export interface GuidanceExtensionApi {
|
|
19
|
+
on?(event: "before_agent_start", handler: (event: AgentStartEvent) => AgentStartResult | undefined): void;
|
|
20
|
+
}
|
|
21
|
+
export declare function registerDiagramGuidance(pi: GuidanceExtensionApi): void;
|
|
22
|
+
/**
|
|
23
|
+
* The hook replaces the prompt, so returning the guidance alone would drop everything the host
|
|
24
|
+
* built. A prompt that cannot be read is left as it is.
|
|
25
|
+
*/
|
|
26
|
+
export declare function withGuidance(event: AgentStartEvent): AgentStartResult | undefined;
|
|
27
|
+
export {};
|
|
28
|
+
//# sourceMappingURL=guidance.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"guidance.d.ts","sourceRoot":"","sources":["../src/guidance.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,eAAO,MAAM,gBAAgB,QAqBjB,CAAC;AAEb,2EAA2E;AAC3E,KAAK,gBAAgB,GAAG,MAAM,GAAG,SAAS,MAAM,EAAE,CAAC;AAEnD,UAAU,eAAe;IACvB,QAAQ,CAAC,YAAY,CAAC,EAAE,gBAAgB,CAAC;IACzC,qDAAqD;IACrD,QAAQ,CAAC,mBAAmB,CAAC,EAAE;QAAE,QAAQ,CAAC,aAAa,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;KAAE,CAAC;CAC/E;AAED,UAAU,gBAAgB;IACxB,QAAQ,CAAC,YAAY,EAAE,gBAAgB,CAAC;CACzC;AAED,MAAM,WAAW,oBAAoB;IACnC,EAAE,CAAC,CACD,KAAK,EAAE,oBAAoB,EAC3B,OAAO,EAAE,CAAC,KAAK,EAAE,eAAe,KAAK,gBAAgB,GAAG,SAAS,GAChE,IAAI,CAAC;CACT;AAED,wBAAgB,uBAAuB,CAAC,EAAE,EAAE,oBAAoB,GAAG,IAAI,CAEtE;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,eAAe,GAAG,gBAAgB,GAAG,SAAS,CAcjF"}
|
package/dist/guidance.js
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The prompt block that makes the model draw by itself. The tool description alone gets the tool
|
|
3
|
+
* called when the user asks for a diagram, not when a diagram is the clearer answer.
|
|
4
|
+
*/
|
|
5
|
+
export const DIAGRAM_GUIDANCE = [
|
|
6
|
+
"Diagrams:",
|
|
7
|
+
"- Call the diagram tool when structure, flow, or relationships are easier to see than to read.",
|
|
8
|
+
"- Draw instead of writing a wall of text: three or more connected parts means a diagram.",
|
|
9
|
+
"- Call the tool before you explain, then write only what the picture does not show.",
|
|
10
|
+
"- Do not repeat the diagram in prose. The user already sees it.",
|
|
11
|
+
"- Never hand-write ASCII art or a Mermaid block. Call the tool.",
|
|
12
|
+
"- Do not draw one fact, a short list, a list of commands, or code that reads better as code.",
|
|
13
|
+
"- Match the diagram to the question:",
|
|
14
|
+
" - components and their connections: profile architecture",
|
|
15
|
+
" - the order of messages in time: a sequence diagram, shape: sequence_diagram",
|
|
16
|
+
" - tables, columns, and keys: profile data, shape: sql_table",
|
|
17
|
+
" - a hierarchy, such as a call tree or a file layout: profile tree",
|
|
18
|
+
" - an import, module, or package graph: profile dependency",
|
|
19
|
+
" - C4 levels, when the reader expects that convention: profile c4",
|
|
20
|
+
" - a diagram to keep in the repository: profile docs, with save",
|
|
21
|
+
" - a data flow, a state change, or a request path in an answer: profile explain, the default",
|
|
22
|
+
"- Keep one idea per diagram, about 5 to 15 nodes. Split a bigger picture into more calls.",
|
|
23
|
+
"- Label every edge with what moves or what happens.",
|
|
24
|
+
"- Fix the source and call again when the tool reports an error in it.",
|
|
25
|
+
"- Pass save only when the user asks to keep the diagram.",
|
|
26
|
+
].join("\n");
|
|
27
|
+
export function registerDiagramGuidance(pi) {
|
|
28
|
+
pi.on?.("before_agent_start", withGuidance);
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* The hook replaces the prompt, so returning the guidance alone would drop everything the host
|
|
32
|
+
* built. A prompt that cannot be read is left as it is.
|
|
33
|
+
*/
|
|
34
|
+
export function withGuidance(event) {
|
|
35
|
+
const prompt = event.systemPrompt;
|
|
36
|
+
if (!toolActive(event)) {
|
|
37
|
+
return undefined;
|
|
38
|
+
}
|
|
39
|
+
if (typeof prompt === "string") {
|
|
40
|
+
return prompt === "" || prompt.includes(DIAGRAM_GUIDANCE)
|
|
41
|
+
? undefined
|
|
42
|
+
: { systemPrompt: `${prompt}\n\n${DIAGRAM_GUIDANCE}` };
|
|
43
|
+
}
|
|
44
|
+
if (!Array.isArray(prompt) || prompt.length === 0 || prompt.includes(DIAGRAM_GUIDANCE)) {
|
|
45
|
+
return undefined;
|
|
46
|
+
}
|
|
47
|
+
return { systemPrompt: [...prompt, DIAGRAM_GUIDANCE] };
|
|
48
|
+
}
|
|
49
|
+
/** Telling the model to draw with a tool the user turned off would waste a turn. */
|
|
50
|
+
function toolActive(event) {
|
|
51
|
+
const tools = event.systemPromptOptions?.selectedTools;
|
|
52
|
+
return tools === undefined || tools.includes("diagram");
|
|
53
|
+
}
|
|
54
|
+
//# sourceMappingURL=guidance.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"guidance.js","sourceRoot":"","sources":["../src/guidance.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,MAAM,CAAC,MAAM,gBAAgB,GAAG;IAC9B,WAAW;IACX,gGAAgG;IAChG,0FAA0F;IAC1F,qFAAqF;IACrF,iEAAiE;IACjE,iEAAiE;IACjE,8FAA8F;IAC9F,sCAAsC;IACtC,4DAA4D;IAC5D,gFAAgF;IAChF,+DAA+D;IAC/D,qEAAqE;IACrE,6DAA6D;IAC7D,oEAAoE;IACpE,kEAAkE;IAClE,+FAA+F;IAC/F,2FAA2F;IAC3F,qDAAqD;IACrD,uEAAuE;IACvE,0DAA0D;CAC3D,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAsBb,MAAM,UAAU,uBAAuB,CAAC,EAAwB;IAC9D,EAAE,CAAC,EAAE,EAAE,CAAC,oBAAoB,EAAE,YAAY,CAAC,CAAC;AAC9C,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,YAAY,CAAC,KAAsB;IACjD,MAAM,MAAM,GAAG,KAAK,CAAC,YAAY,CAAC;IAClC,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC,EAAE,CAAC;QACvB,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,IAAI,OAAO,MAAM,KAAK,QAAQ,EAAE,CAAC;QAC/B,OAAO,MAAM,KAAK,EAAE,IAAI,MAAM,CAAC,QAAQ,CAAC,gBAAgB,CAAC;YACvD,CAAC,CAAC,SAAS;YACX,CAAC,CAAC,EAAE,YAAY,EAAE,GAAG,MAAM,OAAO,gBAAgB,EAAE,EAAE,CAAC;IAC3D,CAAC;IACD,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,IAAI,MAAM,CAAC,QAAQ,CAAC,gBAAgB,CAAC,EAAE,CAAC;QACvF,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,OAAO,EAAE,YAAY,EAAE,CAAC,GAAG,MAAM,EAAE,gBAAgB,CAAC,EAAE,CAAC;AACzD,CAAC;AAED,oFAAoF;AACpF,SAAS,UAAU,CAAC,KAAsB;IACxC,MAAM,KAAK,GAAG,KAAK,CAAC,mBAAmB,EAAE,aAAa,CAAC;IACvD,OAAO,KAAK,KAAK,SAAS,IAAI,KAAK,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC;AAC1D,CAAC"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type GuidanceExtensionApi } from "./guidance.js";
|
|
1
2
|
import { type DiagramExtensionApi } from "./tools.js";
|
|
2
|
-
export default function piDiagram(pi: DiagramExtensionApi): void;
|
|
3
|
+
export default function piDiagram(pi: DiagramExtensionApi & GuidanceExtensionApi): void;
|
|
3
4
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,mBAAmB,EAAwB,MAAM,YAAY,CAAC;AAE5E,MAAM,CAAC,OAAO,UAAU,SAAS,CAAC,EAAE,EAAE,mBAAmB,GAAG,IAAI,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,oBAAoB,EAA2B,MAAM,eAAe,CAAC;AACnF,OAAO,EAAE,KAAK,mBAAmB,EAAwB,MAAM,YAAY,CAAC;AAE5E,MAAM,CAAC,OAAO,UAAU,SAAS,CAAC,EAAE,EAAE,mBAAmB,GAAG,oBAAoB,GAAG,IAAI,CAGtF"}
|
package/dist/index.js
CHANGED
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAA4B,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAE5E,MAAM,CAAC,OAAO,UAAU,SAAS,CAAC,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAA6B,uBAAuB,EAAE,MAAM,eAAe,CAAC;AACnF,OAAO,EAA4B,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAE5E,MAAM,CAAC,OAAO,UAAU,SAAS,CAAC,EAA8C;IAC9E,oBAAoB,CAAC,EAAE,CAAC,CAAC;IACzB,uBAAuB,CAAC,EAAE,CAAC,CAAC;AAC9B,CAAC"}
|
package/package.json
CHANGED
package/src/display.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { readFileSync } from "node:fs";
|
|
2
2
|
import { createRequire } from "node:module";
|
|
3
|
+
import { basename, isAbsolute } from "node:path";
|
|
3
4
|
import { pathToFileURL } from "node:url";
|
|
4
5
|
|
|
5
6
|
/**
|
|
@@ -36,7 +37,11 @@ interface DisplayImage {
|
|
|
36
37
|
}
|
|
37
38
|
|
|
38
39
|
interface TuiModule {
|
|
39
|
-
readonly getCapabilities: () => {
|
|
40
|
+
readonly getCapabilities: () => {
|
|
41
|
+
readonly images: "kitty" | "iterm2" | null;
|
|
42
|
+
readonly hyperlinks: boolean;
|
|
43
|
+
};
|
|
44
|
+
readonly hyperlink: (text: string, url: string) => string;
|
|
40
45
|
readonly Text: new (text?: string, paddingX?: number, paddingY?: number) => Component;
|
|
41
46
|
readonly Container: new () => Component & { addChild(child: Component): void };
|
|
42
47
|
readonly Image: new (
|
|
@@ -128,11 +133,12 @@ export function renderDiagramResult(
|
|
|
128
133
|
const line = (text: string): void => {
|
|
129
134
|
container.addChild(new module.Text(theme.fg("toolOutput", text), 0, 0));
|
|
130
135
|
};
|
|
136
|
+
const image = drawable(module, view.image, context) ? view.image : undefined;
|
|
137
|
+
const url = image === undefined ? undefined : openable(module, image);
|
|
131
138
|
if (view.title !== undefined) {
|
|
132
|
-
line(view.title);
|
|
139
|
+
line(url === undefined ? view.title : module.hyperlink(view.title, url));
|
|
133
140
|
}
|
|
134
141
|
|
|
135
|
-
const image = drawable(module, view.image, context) ? view.image : undefined;
|
|
136
142
|
if (image === undefined) {
|
|
137
143
|
line(view.text);
|
|
138
144
|
} else {
|
|
@@ -150,6 +156,10 @@ export function renderDiagramResult(
|
|
|
150
156
|
{ widthPx: image.widthPx, heightPx: image.heightPx },
|
|
151
157
|
),
|
|
152
158
|
);
|
|
159
|
+
if (view.title === undefined && url !== undefined) {
|
|
160
|
+
const name = module.hyperlink(basename(image.path), url);
|
|
161
|
+
container.addChild(new module.Text(theme.fg("muted", name), 0, 0));
|
|
162
|
+
}
|
|
153
163
|
} catch {
|
|
154
164
|
// The picture is gone from the temp store, so show the text instead.
|
|
155
165
|
line(view.text);
|
|
@@ -163,6 +173,16 @@ export function renderDiagramResult(
|
|
|
163
173
|
return container;
|
|
164
174
|
}
|
|
165
175
|
|
|
176
|
+
/**
|
|
177
|
+
* The picture as a link, so a click opens it where it can be zoomed. The row is bounded to keep
|
|
178
|
+
* the transcript readable, so the file is the only way to see a dense diagram in full.
|
|
179
|
+
*/
|
|
180
|
+
function openable(module: TuiModule, image: DisplayImage): string | undefined {
|
|
181
|
+
return module.getCapabilities().hyperlinks && isAbsolute(image.path)
|
|
182
|
+
? pathToFileURL(image.path).href
|
|
183
|
+
: undefined;
|
|
184
|
+
}
|
|
185
|
+
|
|
166
186
|
function drawable(
|
|
167
187
|
module: TuiModule,
|
|
168
188
|
image: DisplayImage | undefined,
|
|
@@ -183,7 +203,7 @@ export interface DiagramView {
|
|
|
183
203
|
|
|
184
204
|
/** Reads the image once per result row: the host calls the renderer again on every redraw. */
|
|
185
205
|
function read(image: DisplayImage, context: DisplayContext): string {
|
|
186
|
-
const cached = context.state
|
|
206
|
+
const cached = context.state.diagramImage;
|
|
187
207
|
if (typeof cached === "object" && cached !== null) {
|
|
188
208
|
const { path, encoded } = cached as { path?: unknown; encoded?: unknown };
|
|
189
209
|
if (path === image.path && typeof encoded === "string") {
|
|
@@ -196,6 +216,6 @@ function read(image: DisplayImage, context: DisplayContext): string {
|
|
|
196
216
|
throw new Error(`The image at ${image.path} is ${bytes.length} bytes.`);
|
|
197
217
|
}
|
|
198
218
|
const encoded = bytes.toString("base64");
|
|
199
|
-
context.state
|
|
219
|
+
context.state.diagramImage = { path: image.path, encoded };
|
|
200
220
|
return encoded;
|
|
201
221
|
}
|
package/src/guidance.ts
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The prompt block that makes the model draw by itself. The tool description alone gets the tool
|
|
3
|
+
* called when the user asks for a diagram, not when a diagram is the clearer answer.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export const DIAGRAM_GUIDANCE = [
|
|
7
|
+
"Diagrams:",
|
|
8
|
+
"- Call the diagram tool when structure, flow, or relationships are easier to see than to read.",
|
|
9
|
+
"- Draw instead of writing a wall of text: three or more connected parts means a diagram.",
|
|
10
|
+
"- Call the tool before you explain, then write only what the picture does not show.",
|
|
11
|
+
"- Do not repeat the diagram in prose. The user already sees it.",
|
|
12
|
+
"- Never hand-write ASCII art or a Mermaid block. Call the tool.",
|
|
13
|
+
"- Do not draw one fact, a short list, a list of commands, or code that reads better as code.",
|
|
14
|
+
"- Match the diagram to the question:",
|
|
15
|
+
" - components and their connections: profile architecture",
|
|
16
|
+
" - the order of messages in time: a sequence diagram, shape: sequence_diagram",
|
|
17
|
+
" - tables, columns, and keys: profile data, shape: sql_table",
|
|
18
|
+
" - a hierarchy, such as a call tree or a file layout: profile tree",
|
|
19
|
+
" - an import, module, or package graph: profile dependency",
|
|
20
|
+
" - C4 levels, when the reader expects that convention: profile c4",
|
|
21
|
+
" - a diagram to keep in the repository: profile docs, with save",
|
|
22
|
+
" - a data flow, a state change, or a request path in an answer: profile explain, the default",
|
|
23
|
+
"- Keep one idea per diagram, about 5 to 15 nodes. Split a bigger picture into more calls.",
|
|
24
|
+
"- Label every edge with what moves or what happens.",
|
|
25
|
+
"- Fix the source and call again when the tool reports an error in it.",
|
|
26
|
+
"- Pass save only when the user asks to keep the diagram.",
|
|
27
|
+
].join("\n");
|
|
28
|
+
|
|
29
|
+
/** Pi hands over one prompt string; Oh My Pi hands over ordered blocks. */
|
|
30
|
+
type HostSystemPrompt = string | readonly string[];
|
|
31
|
+
|
|
32
|
+
interface AgentStartEvent {
|
|
33
|
+
readonly systemPrompt?: HostSystemPrompt;
|
|
34
|
+
/** Only Pi reports what it built the prompt from. */
|
|
35
|
+
readonly systemPromptOptions?: { readonly selectedTools?: readonly string[] };
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
interface AgentStartResult {
|
|
39
|
+
readonly systemPrompt: HostSystemPrompt;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export interface GuidanceExtensionApi {
|
|
43
|
+
on?(
|
|
44
|
+
event: "before_agent_start",
|
|
45
|
+
handler: (event: AgentStartEvent) => AgentStartResult | undefined,
|
|
46
|
+
): void;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export function registerDiagramGuidance(pi: GuidanceExtensionApi): void {
|
|
50
|
+
pi.on?.("before_agent_start", withGuidance);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* The hook replaces the prompt, so returning the guidance alone would drop everything the host
|
|
55
|
+
* built. A prompt that cannot be read is left as it is.
|
|
56
|
+
*/
|
|
57
|
+
export function withGuidance(event: AgentStartEvent): AgentStartResult | undefined {
|
|
58
|
+
const prompt = event.systemPrompt;
|
|
59
|
+
if (!toolActive(event)) {
|
|
60
|
+
return undefined;
|
|
61
|
+
}
|
|
62
|
+
if (typeof prompt === "string") {
|
|
63
|
+
return prompt === "" || prompt.includes(DIAGRAM_GUIDANCE)
|
|
64
|
+
? undefined
|
|
65
|
+
: { systemPrompt: `${prompt}\n\n${DIAGRAM_GUIDANCE}` };
|
|
66
|
+
}
|
|
67
|
+
if (!Array.isArray(prompt) || prompt.length === 0 || prompt.includes(DIAGRAM_GUIDANCE)) {
|
|
68
|
+
return undefined;
|
|
69
|
+
}
|
|
70
|
+
return { systemPrompt: [...prompt, DIAGRAM_GUIDANCE] };
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/** Telling the model to draw with a tool the user turned off would waste a turn. */
|
|
74
|
+
function toolActive(event: AgentStartEvent): boolean {
|
|
75
|
+
const tools = event.systemPromptOptions?.selectedTools;
|
|
76
|
+
return tools === undefined || tools.includes("diagram");
|
|
77
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
|
+
import { type GuidanceExtensionApi, registerDiagramGuidance } from "./guidance.js";
|
|
1
2
|
import { type DiagramExtensionApi, registerDiagramTools } from "./tools.js";
|
|
2
3
|
|
|
3
|
-
export default function piDiagram(pi: DiagramExtensionApi): void {
|
|
4
|
+
export default function piDiagram(pi: DiagramExtensionApi & GuidanceExtensionApi): void {
|
|
4
5
|
registerDiagramTools(pi);
|
|
6
|
+
registerDiagramGuidance(pi);
|
|
5
7
|
}
|