@jigs.design/prompt 0.1.2 → 0.3.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 +2 -2
- package/dist/document.d.ts +12 -0
- package/dist/document.js +35 -0
- package/dist/index.d.ts +2 -1
- package/dist/index.js +2 -1
- package/dist/repair.d.ts +23 -8
- package/dist/repair.js +14 -6
- package/dist/system-prompt.d.ts +1 -1
- package/dist/system-prompt.js +16 -12
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Renders the text the model reads: the system prompt from a host, and the repair text from diagnostics. `@jigs.design/server` uses it; you import it to read or test the prompt a host produces.
|
|
4
4
|
|
|
5
|
-
The system prompt
|
|
5
|
+
The system prompt opens with the output rules and the language, then the host's types, components and tags (`## App tags`, tag jigs only), its design guidance (`## Design`: shared, then the mode's) and situations (`## Situations`: how to choose, then each with its When line, guidance and example); after that come the app's `instructions`, the host's data types, procedures and image hosts, the part of the prompt that may change between turns. It writes each procedure's input and output out as types, with each field's `.describe()` text and constraints as a comment, and shared enums declared once. It has two modes: `document`, where the whole reply is one UI, and `chat`, where the reply is Markdown with fenced `jigs` blocks.
|
|
6
6
|
|
|
7
7
|
## Exports
|
|
8
8
|
|
|
@@ -10,7 +10,7 @@ The system prompt carries the app's `instructions`, then lists the host's proced
|
|
|
10
10
|
| --------------------------- | ----------------------------------------------------------------------------------------------------------------------------- |
|
|
11
11
|
| `systemPrompt` | The system prompt for a host: `{ mode, instructions }` |
|
|
12
12
|
| `needsRepair`, `repairText` | Whether diagnostics call for a repair round, and the message that asks for it, with any design advice the app chose to repair |
|
|
13
|
-
| `editRepairText` | Asks for corrected edit blocks when an edit did not apply, naming why: missing, ambiguous, malformed or broken
|
|
13
|
+
| `editRepairText` | Asks for corrected edit blocks when an edit did not apply, naming why: missing, ambiguous, overlapping, malformed or broken |
|
|
14
14
|
| `fullDocumentText` | Asks for the whole document when a second edit did not apply either |
|
|
15
15
|
| `typeText` | A Zod schema as the TypeScript-like type the prompt shows |
|
|
16
16
|
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
//#region src/document.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* The last user message of a turn about a document: the current UI, then
|
|
4
|
+
* the ask. The server sends it in place of the ask, on every model call, so
|
|
5
|
+
* the UI is the last thing before the request and never in `system`.
|
|
6
|
+
*/
|
|
7
|
+
export declare function withDocument(ask: string, source: string): string;
|
|
8
|
+
/** The ask of a message `withDocument` built, or the message itself. */
|
|
9
|
+
export declare function askOf(content: string): string;
|
|
10
|
+
/** The document of a message `withDocument` built, or null for any other message. */
|
|
11
|
+
export declare function documentOf(content: string): string | null;
|
|
12
|
+
//#endregion
|
package/dist/document.js
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
//#region src/document.ts
|
|
2
|
+
const DOCUMENT_MARK = "The current UI, which your reply changes or replaces:";
|
|
3
|
+
const ASK_MARK = "The request:";
|
|
4
|
+
const DOCUMENT_START = `${DOCUMENT_MARK}\n\n`;
|
|
5
|
+
const ASK_START = `\n\n${ASK_MARK}\n\n`;
|
|
6
|
+
/**
|
|
7
|
+
* The last user message of a turn about a document: the current UI, then
|
|
8
|
+
* the ask. The server sends it in place of the ask, on every model call, so
|
|
9
|
+
* the UI is the last thing before the request and never in `system`.
|
|
10
|
+
*/
|
|
11
|
+
function withDocument(ask, source) {
|
|
12
|
+
return `${DOCUMENT_START}${source}${ASK_START}${ask}`;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Where the ask starts in a message `withDocument` built, or null for any
|
|
16
|
+
* other message. The first ask mark after the document ends it, so an ask
|
|
17
|
+
* may contain the marks; a document that contains one is read short.
|
|
18
|
+
*/
|
|
19
|
+
function askStart(content) {
|
|
20
|
+
if (!content.startsWith(DOCUMENT_START)) return null;
|
|
21
|
+
const index = content.indexOf(ASK_START, DOCUMENT_START.length - 2);
|
|
22
|
+
return index === -1 ? null : index;
|
|
23
|
+
}
|
|
24
|
+
/** The ask of a message `withDocument` built, or the message itself. */
|
|
25
|
+
function askOf(content) {
|
|
26
|
+
const index = askStart(content);
|
|
27
|
+
return index === null ? content : content.slice(index + ASK_START.length);
|
|
28
|
+
}
|
|
29
|
+
/** The document of a message `withDocument` built, or null for any other message. */
|
|
30
|
+
function documentOf(content) {
|
|
31
|
+
const index = askStart(content);
|
|
32
|
+
return index === null ? null : content.slice(DOCUMENT_START.length, Math.max(index, DOCUMENT_START.length));
|
|
33
|
+
}
|
|
34
|
+
//#endregion
|
|
35
|
+
export { askOf, documentOf, withDocument };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
+
import { askOf, documentOf, withDocument } from "./document.js";
|
|
1
2
|
import { editRepairText, fullDocumentText, needsRepair, repairText } from "./repair.js";
|
|
2
3
|
import { PromptMode, PromptOptions, SystemPromptParts, systemPrompt, systemPromptParts } from "./system-prompt.js";
|
|
3
4
|
import { typeText } from "./types.js";
|
|
4
|
-
export { type PromptMode, type PromptOptions, type SystemPromptParts, editRepairText, fullDocumentText, needsRepair, repairText, systemPrompt, systemPromptParts, typeText };
|
|
5
|
+
export { type PromptMode, type PromptOptions, type SystemPromptParts, askOf, documentOf, editRepairText, fullDocumentText, needsRepair, repairText, systemPrompt, systemPromptParts, typeText, withDocument };
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
+
import { askOf, documentOf, withDocument } from "./document.js";
|
|
1
2
|
import { editRepairText, fullDocumentText, needsRepair, repairText } from "./repair.js";
|
|
2
3
|
import { typeText } from "./types.js";
|
|
3
4
|
import { systemPrompt, systemPromptParts } from "./system-prompt.js";
|
|
4
|
-
export { editRepairText, fullDocumentText, needsRepair, repairText, systemPrompt, systemPromptParts, typeText };
|
|
5
|
+
export { askOf, documentOf, editRepairText, fullDocumentText, needsRepair, repairText, systemPrompt, systemPromptParts, typeText, withDocument };
|
package/dist/repair.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Diagnostic } from "@jigs.design/
|
|
1
|
+
import { Diagnostic } from "@jigs.design/protocol";
|
|
2
2
|
//#region src/repair.d.ts
|
|
3
3
|
/** Whether a diagnostic is worth a repair round: something the compiler had to change. */
|
|
4
4
|
export declare function needsRepair(diagnostics: ReadonlyArray<Diagnostic>): boolean;
|
|
@@ -8,11 +8,17 @@ interface RepairAdvice {
|
|
|
8
8
|
span: Diagnostic["span"];
|
|
9
9
|
}
|
|
10
10
|
/**
|
|
11
|
-
* The follow-up turn asking the model to fix the errors in its
|
|
12
|
-
*
|
|
11
|
+
* The follow-up turn asking the model to fix the errors in its UI, and any
|
|
12
|
+
* design advice the app chose to repair. With advice alone, it says
|
|
13
13
|
* the Jigs works and asks for the design change.
|
|
14
14
|
*/
|
|
15
|
-
export declare function repairText(diagnostics: ReadonlyArray<Diagnostic>, advice?: ReadonlyArray<RepairAdvice
|
|
15
|
+
export declare function repairText(diagnostics: ReadonlyArray<Diagnostic>, advice?: ReadonlyArray<RepairAdvice>,
|
|
16
|
+
/**
|
|
17
|
+
* What the line numbers count: the current UI the server shows with this
|
|
18
|
+
* message (a session), or the assistant's answer just before it (a chat
|
|
19
|
+
* block's repair, which shows no document).
|
|
20
|
+
*/
|
|
21
|
+
numbered?: "answer" | "shown"): string;
|
|
16
22
|
/** An edit block that did not apply, as `applyEdits` and the server report it. */
|
|
17
23
|
interface FailedEdit {
|
|
18
24
|
/** For `broken`: the parser repairs the edited UI needs and the current one does not. */
|
|
@@ -24,14 +30,23 @@ interface FailedEdit {
|
|
|
24
30
|
replace: string;
|
|
25
31
|
search: string;
|
|
26
32
|
};
|
|
27
|
-
/**
|
|
33
|
+
/**
|
|
34
|
+
* For `ambiguous`: the line each match of the SEARCH starts on. For
|
|
35
|
+
* `overlapping`: the line the earlier edit's SEARCH starts on, then this one's.
|
|
36
|
+
*/
|
|
28
37
|
lines?: ReadonlyArray<number>;
|
|
38
|
+
/** For `overlapping`: the earlier edit whose lines this one also changes. */
|
|
39
|
+
overlaps?: {
|
|
40
|
+
replace: string;
|
|
41
|
+
search: string;
|
|
42
|
+
};
|
|
29
43
|
/**
|
|
30
44
|
* `missing`: SEARCH is not in the source; `ambiguous`: it is there more
|
|
31
|
-
* than once; `
|
|
32
|
-
*
|
|
45
|
+
* than once; `overlapping`: an earlier edit changes some of its lines;
|
|
46
|
+
* `malformed`: the block has stray markers; `broken`: the edit left a
|
|
47
|
+
* tag unclosed or closed one twice.
|
|
33
48
|
*/
|
|
34
|
-
reason: "ambiguous" | "broken" | "malformed" | "missing";
|
|
49
|
+
reason: "ambiguous" | "broken" | "malformed" | "missing" | "overlapping";
|
|
35
50
|
}
|
|
36
51
|
/**
|
|
37
52
|
* Asks for corrected edit blocks after an edit did not apply. It names what
|
package/dist/repair.js
CHANGED
|
@@ -5,11 +5,11 @@ function needsRepair(diagnostics) {
|
|
|
5
5
|
return diagnostics.some((diagnostic) => diagnostic.severity === "error");
|
|
6
6
|
}
|
|
7
7
|
/**
|
|
8
|
-
* The follow-up turn asking the model to fix the errors in its
|
|
9
|
-
*
|
|
8
|
+
* The follow-up turn asking the model to fix the errors in its UI, and any
|
|
9
|
+
* design advice the app chose to repair. With advice alone, it says
|
|
10
10
|
* the Jigs works and asks for the design change.
|
|
11
11
|
*/
|
|
12
|
-
function repairText(diagnostics, advice = []) {
|
|
12
|
+
function repairText(diagnostics, advice = [], numbered = "shown") {
|
|
13
13
|
const seen = /* @__PURE__ */ new Set();
|
|
14
14
|
const errors = diagnostics.filter((diagnostic) => diagnostic.severity === "error");
|
|
15
15
|
const issues = [...errors, ...advice].filter((issue) => {
|
|
@@ -18,13 +18,14 @@ function repairText(diagnostics, advice = []) {
|
|
|
18
18
|
seen.add(key);
|
|
19
19
|
return true;
|
|
20
20
|
});
|
|
21
|
+
const where = numbered === "shown" ? "Line numbers refer to the current UI shown above." : "Line numbers refer to your last answer.";
|
|
21
22
|
const lines = issues.slice(0, MAX_ISSUES).map((issue) => `- Line ${issue.span.line}: ${issue.message}`);
|
|
22
23
|
if (issues.length > MAX_ISSUES) lines.push(`- ...and ${issues.length - MAX_ISSUES} more`);
|
|
23
|
-
if (errors.length === 0) return `Your jig works, but its design can be better.
|
|
24
|
+
if (errors.length === 0) return `Your jig works, but its design can be better. ${where}
|
|
24
25
|
${lines.join("\n")}
|
|
25
26
|
|
|
26
27
|
Change that and keep everything else as it is. Reply with edit blocks, or with the complete corrected Jigs if the change is large.`;
|
|
27
|
-
return `Your jig has problems.
|
|
28
|
+
return `Your jig has problems. ${where}
|
|
28
29
|
${lines.join("\n")}
|
|
29
30
|
|
|
30
31
|
Fix them and keep everything that already works. Reply with edit blocks, or with the complete corrected Jigs if the fix is large.`;
|
|
@@ -106,6 +107,12 @@ function failureParts(edit, source) {
|
|
|
106
107
|
case "malformed":
|
|
107
108
|
parts.push(`Your edit could not be applied: this edit block has an extra marker line inside it:\n${failed.search}`, `It was meant to become:\n${failed.replace}`);
|
|
108
109
|
return parts;
|
|
110
|
+
case "overlapping": {
|
|
111
|
+
const [first, second] = edit.lines ?? [];
|
|
112
|
+
const where = first === void 0 || second === void 0 ? "" : first === second ? ` (both start on line ${first})` : ` (lines ${first} and ${second})`;
|
|
113
|
+
parts.push(`Your edits could not be applied: these edits change the same lines of the current UI${where}. Each edit is matched against the current UI as shown, so one cannot change lines another one also changes.`, `The first edit replaced:\n${edit.overlaps?.search ?? ""}`, `The second edit replaced:\n${failed.search}`, `It was meant to become:\n${failed.replace}`);
|
|
114
|
+
return parts;
|
|
115
|
+
}
|
|
109
116
|
case "missing": {
|
|
110
117
|
parts.push(`Your edit could not be applied: this SEARCH text is not in the current UI:\n${failed.search}`, `It was meant to become:\n${failed.replace}`);
|
|
111
118
|
const closest = closestLines(source, failed.search);
|
|
@@ -118,7 +125,8 @@ const EDIT_ADVICE = {
|
|
|
118
125
|
ambiguous: "Reply with corrected edit blocks whose SEARCH includes enough of the surrounding lines to match only the place you mean",
|
|
119
126
|
broken: "Reply with corrected edit blocks that open and close every tag within the same block",
|
|
120
127
|
malformed: "Reply with corrected edit blocks, with one SEARCH, one divider and one REPLACE marker each",
|
|
121
|
-
missing: "Reply with corrected edit blocks whose SEARCH copies the current UI exactly, indentation included"
|
|
128
|
+
missing: "Reply with corrected edit blocks whose SEARCH copies the current UI exactly, indentation included",
|
|
129
|
+
overlapping: "Reply with corrected edit blocks that send one edit for that region, written against the current UI"
|
|
122
130
|
};
|
|
123
131
|
/**
|
|
124
132
|
* Asks for corrected edit blocks after an edit did not apply. It names what
|
package/dist/system-prompt.d.ts
CHANGED
|
@@ -20,7 +20,7 @@ export interface PromptOptions {
|
|
|
20
20
|
*
|
|
21
21
|
* What stays the same from turn to turn comes first, so a provider can
|
|
22
22
|
* cache it as a prefix: the output rules, the language, the catalog's
|
|
23
|
-
* types and components, the
|
|
23
|
+
* types and components, the app's tags, the design guidance and situations.
|
|
24
24
|
* What a turn may change comes last: the app's `instructions`, the
|
|
25
25
|
* procedures' own types, the queries, mutations and actions, and images. A
|
|
26
26
|
* procedure enum the catalog already names keeps that name; the others are
|
package/dist/system-prompt.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { enumKey, enumUses, objectUses, typeText } from "./types.js";
|
|
2
2
|
import { sharedObjects, sharedTypes } from "./shared-types.js";
|
|
3
|
-
import {
|
|
3
|
+
import { jigSignature } from "@jigs.design/compiler";
|
|
4
4
|
import { dataSchema } from "@jigs.design/host";
|
|
5
5
|
//#region src/system-prompt.ts
|
|
6
6
|
const JIGS = `Jigs is Markdown for prose, JSX-like tags for components from a fixed catalog, and \`{@body ...}\` lines of JavaScript for state and logic.`;
|
|
@@ -17,10 +17,12 @@ const DOCUMENT_OUTPUT = `You write interactive user interfaces in Jigs. ${JIGS}
|
|
|
17
17
|
- Reply with Jigs only. No code fences, no explanation before or after it.
|
|
18
18
|
|
|
19
19
|
## Changing an existing UI
|
|
20
|
-
When
|
|
20
|
+
When the request shows the current UI:
|
|
21
21
|
- For a focused change, reply with edit blocks only, no other text:
|
|
22
22
|
${EDIT_BLOCK}
|
|
23
|
-
- If the change rewrites most of the UI, reply with the complete updated Jigs instead
|
|
23
|
+
- If the change rewrites most of the UI, reply with the complete updated Jigs instead.
|
|
24
|
+
- A complete reply includes every \`{@body}\` line it needs; the lines shown with the request are not kept unless you repeat them.
|
|
25
|
+
- SEARCH lines come from the current UI shown with the request, not from an earlier reply.`;
|
|
24
26
|
const OUTPUT = {
|
|
25
27
|
chat: `You reply in Markdown, and you can show interactive user interfaces in your replies by writing Jigs. ${JIGS}
|
|
26
28
|
|
|
@@ -33,7 +35,8 @@ const OUTPUT = {
|
|
|
33
35
|
\`\`\`
|
|
34
36
|
- Keep explanation outside the block, in Markdown. Do not repeat in prose what the UI shows.
|
|
35
37
|
- You never see what a query returns: the UI reads it when it runs, after your reply. So your prose must not state figures, totals or conclusions about the user's data; say what the UI shows instead.
|
|
36
|
-
-
|
|
38
|
+
- When the user's data would answer the ask, the block that reads it goes in this reply. Never reply with a promise to check, compare or look something up: there is no later turn to do it in.
|
|
39
|
+
- Tags and \`{@body}\` lines work only inside a block. Outside one, write plain Markdown: no tags, no HTML and no tool-call markup, which would show as text.
|
|
37
40
|
- A reply may hold several blocks, each with its own id. Each block is a separate UI: it cannot see another block's state.
|
|
38
41
|
- Never put Jigs in any other fence, and never put a fence inside a block.
|
|
39
42
|
|
|
@@ -83,7 +86,7 @@ const HOOKS = `## Data and actions
|
|
|
83
86
|
- After a mutation succeeds, the queries listed under "Refreshes" reload by themselves. Write nothing to make that happen.
|
|
84
87
|
- An action listed below is called the same way, with \`useMutation("name")\` and \`mutate(input)\` in a handler. The page handles it instead of the server, for example by sending a message; its handle has the same \`status\`, \`data\` and \`error\`.
|
|
85
88
|
- The first argument is always the name as a string literal. Never write \`useQuery({ queryKey, queryFn })\`.
|
|
86
|
-
- Only use the queries and mutations listed below.
|
|
89
|
+
- Only use the queries and mutations listed below. When one returns data the ask needs, read it with \`useQuery\` and show what it returns. Never write those values into the UI yourself, not even as an example.
|
|
87
90
|
- Inputs and data are TypeScript-like types. A \`//\` comment gives a field's meaning and limits; follow them when you build an input.`;
|
|
88
91
|
const COMPONENTS_INTRO = `## Components
|
|
89
92
|
Each prop is \`name: type = default\`, then what it does. Leave a prop out to get its default.
|
|
@@ -200,13 +203,13 @@ function actionSection(name, procedure, names) {
|
|
|
200
203
|
"Handled by the page, not the server. Refreshes nothing."
|
|
201
204
|
].join("\n");
|
|
202
205
|
}
|
|
203
|
-
const
|
|
204
|
-
|
|
205
|
-
function
|
|
206
|
-
const lines = [`### ${
|
|
206
|
+
const TAGS_INTRO = `## App tags
|
|
207
|
+
Tags this app wrote in Jigs. Use them like any tag, with the props shown; a bare prop is required. They are already defined: never write their <define>. Prefer one over building the same thing from components. When one of them alone answers the ask, the UI is that one tag and nothing else.`;
|
|
208
|
+
function tagSection(entry) {
|
|
209
|
+
const lines = [`### ${jigSignature({
|
|
207
210
|
name: entry.name,
|
|
208
211
|
source: entry.source
|
|
209
|
-
})}`, entry.description];
|
|
212
|
+
})}`, entry.description ?? ""];
|
|
210
213
|
if (entry.example !== void 0) lines.push(`Example: \`${entry.example}\``);
|
|
211
214
|
return lines.join("\n");
|
|
212
215
|
}
|
|
@@ -286,7 +289,7 @@ function typesSection(heading, intro, declarations) {
|
|
|
286
289
|
*
|
|
287
290
|
* What stays the same from turn to turn comes first, so a provider can
|
|
288
291
|
* cache it as a prefix: the output rules, the language, the catalog's
|
|
289
|
-
* types and components, the
|
|
292
|
+
* types and components, the app's tags, the design guidance and situations.
|
|
290
293
|
* What a turn may change comes last: the app's `instructions`, the
|
|
291
294
|
* procedures' own types, the queries, mutations and actions, and images. A
|
|
292
295
|
* procedure enum the catalog already names keeps that name; the others are
|
|
@@ -319,7 +322,8 @@ function systemPromptParts(host, options) {
|
|
|
319
322
|
...typesSection("## Types", "Named lists of values, used by the props below and by the data the queries return.", catalogTypes.declarations),
|
|
320
323
|
[COMPONENTS_INTRO, ...componentSections(host.components.components, catalogTypes.names)].join("\n\n")
|
|
321
324
|
];
|
|
322
|
-
|
|
325
|
+
const tags = host.jigs.filter((entry) => entry.tag);
|
|
326
|
+
if (tags.length > 0) sections.push([TAGS_INTRO, ...tags.map((entry) => tagSection(entry))].join("\n\n"));
|
|
323
327
|
const design = designText(host, mode);
|
|
324
328
|
if (design !== "") sections.push(`## Design\n${design}`);
|
|
325
329
|
if (host.situations.length > 0) sections.push([SITUATIONS_INTRO, ...host.situations.map((entry) => situationSection(entry))].join("\n\n"));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jigs.design/prompt",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "The system prompt and the repair text, rendered from a host and from diagnostics.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -25,9 +25,9 @@
|
|
|
25
25
|
"access": "public"
|
|
26
26
|
},
|
|
27
27
|
"dependencies": {
|
|
28
|
-
"@jigs.design/compiler": "0.
|
|
29
|
-
"@jigs.design/host": "0.
|
|
30
|
-
"@jigs.design/
|
|
28
|
+
"@jigs.design/compiler": "0.3.0",
|
|
29
|
+
"@jigs.design/host": "0.3.0",
|
|
30
|
+
"@jigs.design/protocol": "0.3.0"
|
|
31
31
|
},
|
|
32
32
|
"peerDependencies": {
|
|
33
33
|
"zod": "^4.0.0"
|