@kubuild/ai 0.3.0 → 0.4.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/dist/{chunk-27AY2KBI.js → chunk-3DRAVYU3.js} +173 -46
- package/dist/chunk-3DRAVYU3.js.map +1 -0
- package/dist/chunk-ILLNRABB.js +24 -0
- package/dist/chunk-ILLNRABB.js.map +1 -0
- package/dist/{chunk-JY4NHD3Q.js → chunk-T72YWBJS.js} +62 -1
- package/dist/chunk-T72YWBJS.js.map +1 -0
- package/dist/{chunk-UH24LHRY.js → chunk-YETTTWHI.js} +226 -1
- package/dist/chunk-YETTTWHI.js.map +1 -0
- package/dist/client/ai-client.d.ts +20 -1
- package/dist/client/ai-client.d.ts.map +1 -1
- package/dist/client/index.cjs +61 -0
- package/dist/client/index.cjs.map +1 -1
- package/dist/client/index.js +1 -1
- package/dist/core/document-outline.d.ts +60 -0
- package/dist/core/document-outline.d.ts.map +1 -0
- package/dist/core/index.d.ts +2 -0
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/messages.d.ts +18 -0
- package/dist/core/messages.d.ts.map +1 -0
- package/dist/core/prompt-compiler.d.ts +29 -0
- package/dist/core/prompt-compiler.d.ts.map +1 -1
- package/dist/index.cjs +261 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +31 -3
- package/dist/react/index.cjs +173 -0
- package/dist/react/index.cjs.map +1 -1
- package/dist/react/index.d.ts +1 -0
- package/dist/react/index.d.ts.map +1 -1
- package/dist/react/index.js +112 -1
- package/dist/react/index.js.map +1 -1
- package/dist/react/use-ai-agent.d.ts +47 -0
- package/dist/react/use-ai-agent.d.ts.map +1 -0
- package/dist/server/adapters/anthropic.d.ts.map +1 -1
- package/dist/server/adapters/custom.d.ts.map +1 -1
- package/dist/server/adapters/gemini.d.ts.map +1 -1
- package/dist/server/adapters/index.cjs +180 -45
- package/dist/server/adapters/index.cjs.map +1 -1
- package/dist/server/adapters/index.js +2 -1
- package/dist/server/adapters/openai.d.ts +25 -0
- package/dist/server/adapters/openai.d.ts.map +1 -1
- package/dist/server/agent.d.ts +59 -0
- package/dist/server/agent.d.ts.map +1 -0
- package/dist/server/engine.d.ts.map +1 -1
- package/dist/server/handler.d.ts +10 -1
- package/dist/server/handler.d.ts.map +1 -1
- package/dist/server/index.cjs +1734 -260
- package/dist/server/index.cjs.map +1 -1
- package/dist/server/index.d.ts +2 -0
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +1229 -94
- package/dist/server/index.js.map +1 -1
- package/dist/server/tools/helpers.d.ts +66 -0
- package/dist/server/tools/helpers.d.ts.map +1 -0
- package/dist/server/tools/index.d.ts +25 -0
- package/dist/server/tools/index.d.ts.map +1 -0
- package/dist/server/tools/read-tools.d.ts +3 -0
- package/dist/server/tools/read-tools.d.ts.map +1 -0
- package/dist/server/tools/types.d.ts +51 -0
- package/dist/server/tools/types.d.ts.map +1 -0
- package/dist/server/tools/write-tools.d.ts +3 -0
- package/dist/server/tools/write-tools.d.ts.map +1 -0
- package/dist/types.d.ts +193 -2
- package/dist/types.d.ts.map +1 -1
- package/package.json +5 -5
- package/dist/chunk-27AY2KBI.js.map +0 -1
- package/dist/chunk-JY4NHD3Q.js.map +0 -1
- package/dist/chunk-UH24LHRY.js.map +0 -1
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import type { Node, PageDocument } from '@kubuild/schema';
|
|
2
|
+
/**
|
|
3
|
+
* Best-effort human label for a node, derived from its text-carrying props. Returns an
|
|
4
|
+
* empty string when the node has none (layout containers, mostly).
|
|
5
|
+
*/
|
|
6
|
+
export declare function getNodeLabel(node: Node, max?: number): string;
|
|
7
|
+
export interface DocumentOutlineOptions {
|
|
8
|
+
/** How deep to walk before collapsing into a `(+N nested)` marker. Default: 3. */
|
|
9
|
+
maxDepth?: number;
|
|
10
|
+
/**
|
|
11
|
+
* When set, the branch containing this node is always expanded in full regardless of
|
|
12
|
+
* `maxDepth`, and the node itself is marked with `← SELECTED`. This is what makes
|
|
13
|
+
* "user selected a component on the canvas" legible to the model.
|
|
14
|
+
*/
|
|
15
|
+
focusNodeId?: string;
|
|
16
|
+
/** Hard ceiling on emitted rows, so a huge page can't blow up the prompt. Default: 200. */
|
|
17
|
+
maxNodes?: number;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Renders a compact, indented outline of a node tree (STORA-530).
|
|
21
|
+
*
|
|
22
|
+
* This is the agent's *default* view of the document: one line per node with id, type,
|
|
23
|
+
* label and child count — never full props/styles. Detail is fetched on demand via the
|
|
24
|
+
* `read_node` tool, which keeps per-step prompt cost roughly constant no matter how big
|
|
25
|
+
* the page is. `engine.buildChatPrompt`'s depth-1 section summary can't do this: nested
|
|
26
|
+
* nodes were invisible to the model, so it could never target them.
|
|
27
|
+
*/
|
|
28
|
+
export declare function summarizeNodeTree(root: Node, options?: DocumentOutlineOptions): string;
|
|
29
|
+
/** Total number of descendants below a node (not counting the node itself). */
|
|
30
|
+
export declare function countDescendants(node: Node): number;
|
|
31
|
+
/** Outline of a whole `PageDocument`, prefixed with its metadata. */
|
|
32
|
+
export declare function summarizeDocument(document: PageDocument, options?: DocumentOutlineOptions): string;
|
|
33
|
+
export interface SelectionContext {
|
|
34
|
+
node: Node;
|
|
35
|
+
/** Root-first chain of ancestors, excluding the node itself. */
|
|
36
|
+
ancestors: Node[];
|
|
37
|
+
/** The node's siblings, in document order, including the node itself. */
|
|
38
|
+
siblings: Node[];
|
|
39
|
+
index: number;
|
|
40
|
+
parentId: string | null;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Resolves everything the agent needs to know about the canvas selection (STORA-530).
|
|
44
|
+
*
|
|
45
|
+
* Sending only `selectedNodeId` — what `engine.buildChatPrompt` does today — tells the
|
|
46
|
+
* model an id but not what the node *is*, what it lives inside, or what sits next to it,
|
|
47
|
+
* so it can't judge whether an instruction like "make this wider" belongs on the node or
|
|
48
|
+
* on its parent container. Returns `null` when the id isn't in the tree.
|
|
49
|
+
*/
|
|
50
|
+
export declare function buildSelectionContext(root: Node, nodeId: string): SelectionContext | null;
|
|
51
|
+
/**
|
|
52
|
+
* Depth-limits a node before it is serialized into a prompt. A selected section can carry
|
|
53
|
+
* hundreds of descendants; dumping all of them would defeat the point of the outline.
|
|
54
|
+
* Children below `maxDepth` are replaced by a marker node telling the model to call
|
|
55
|
+
* `read_node` if it needs them.
|
|
56
|
+
*/
|
|
57
|
+
export declare function pruneNodeForPrompt(node: Node, maxDepth?: number, depth?: number): Node;
|
|
58
|
+
/** Renders a `SelectionContext` as the prompt block describing the current selection. */
|
|
59
|
+
export declare function formatSelectionContext(context: SelectionContext): string;
|
|
60
|
+
//# sourceMappingURL=document-outline.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"document-outline.d.ts","sourceRoot":"","sources":["../../src/core/document-outline.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAmB1D;;;GAGG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,IAAI,EAAE,GAAG,SAAoB,GAAG,MAAM,CASxE;AAED,MAAM,WAAW,sBAAsB;IACrC,kFAAkF;IAClF,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;;OAIG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,2FAA2F;IAC3F,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAOD;;;;;;;;GAQG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,IAAI,EAAE,OAAO,GAAE,sBAA2B,GAAG,MAAM,CA4C1F;AAED,+EAA+E;AAC/E,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,IAAI,GAAG,MAAM,CAEnD;AAED,qEAAqE;AACrE,wBAAgB,iBAAiB,CAC/B,QAAQ,EAAE,YAAY,EACtB,OAAO,GAAE,sBAA2B,GACnC,MAAM,CAMR;AAED,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,IAAI,CAAC;IACX,gEAAgE;IAChE,SAAS,EAAE,IAAI,EAAE,CAAC;IAClB,yEAAyE;IACzE,QAAQ,EAAE,IAAI,EAAE,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;CACzB;AAED;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,GAAG,gBAAgB,GAAG,IAAI,CAwBzF;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,IAAI,EAAE,QAAQ,SAAI,EAAE,KAAK,SAAI,GAAG,IAAI,CAuB5E;AAED,yFAAyF;AACzF,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,gBAAgB,GAAG,MAAM,CAyBxE"}
|
package/dist/core/index.d.ts
CHANGED
package/dist/core/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/core/index.ts"],"names":[],"mappings":"AAAA,cAAc,mBAAmB,CAAC;AAClC,cAAc,cAAc,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/core/index.ts"],"names":[],"mappings":"AAAA,cAAc,mBAAmB,CAAC;AAClC,cAAc,cAAc,CAAC;AAC7B,cAAc,YAAY,CAAC;AAC3B,cAAc,oBAAoB,CAAC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { AiChatMessage, AiToolUseBlock, AiToolResultBlock } from '../types';
|
|
2
|
+
/**
|
|
3
|
+
* Flattens a message's content to plain text (STORA-530).
|
|
4
|
+
*
|
|
5
|
+
* `AiChatMessage.content` became `string | AiContentBlock[]` when tool calling landed;
|
|
6
|
+
* every place that previously assumed a bare string (adapter message mapping, the chat
|
|
7
|
+
* prompt's "last user message" lookup, chat UIs) goes through this helper instead of
|
|
8
|
+
* re-implementing the narrowing. Tool-use/tool-result blocks contribute nothing to the
|
|
9
|
+
* text rendering — they're transported separately by the adapters.
|
|
10
|
+
*/
|
|
11
|
+
export declare function getMessageText(message: Pick<AiChatMessage, 'content'>): string;
|
|
12
|
+
/** All `tool_use` blocks carried by a message, in order. Empty for plain-text messages. */
|
|
13
|
+
export declare function getToolUseBlocks(message: Pick<AiChatMessage, 'content'>): AiToolUseBlock[];
|
|
14
|
+
/** All `tool_result` blocks carried by a message, in order. */
|
|
15
|
+
export declare function getToolResultBlocks(message: Pick<AiChatMessage, 'content'>): AiToolResultBlock[];
|
|
16
|
+
/** True when the message carries any non-text block — i.e. adapters must map it structurally. */
|
|
17
|
+
export declare function hasStructuredContent(message: Pick<AiChatMessage, 'content'>): boolean;
|
|
18
|
+
//# sourceMappingURL=messages.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"messages.d.ts","sourceRoot":"","sources":["../../src/core/messages.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAkB,cAAc,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAC;AAEjG;;;;;;;;GAQG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,IAAI,CAAC,aAAa,EAAE,SAAS,CAAC,GAAG,MAAM,CAM9E;AAED,2FAA2F;AAC3F,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,IAAI,CAAC,aAAa,EAAE,SAAS,CAAC,GAAG,cAAc,EAAE,CAG1F;AAED,+DAA+D;AAC/D,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,IAAI,CAAC,aAAa,EAAE,SAAS,CAAC,GAAG,iBAAiB,EAAE,CAGhG;AAED,iGAAiG;AACjG,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,IAAI,CAAC,aAAa,EAAE,SAAS,CAAC,GAAG,OAAO,CAErF"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { PageDocument } from '@kubuild/schema';
|
|
1
2
|
import type { AiCompiledComponentSpec, ComponentRegistryLike, AiGenerationMode } from '../types';
|
|
2
3
|
export declare function compileComponentCatalog(registry?: ComponentRegistryLike): AiCompiledComponentSpec[];
|
|
3
4
|
export declare function buildSystemPrompt(options: {
|
|
@@ -7,4 +8,32 @@ export declare function buildSystemPrompt(options: {
|
|
|
7
8
|
stylePreference?: string;
|
|
8
9
|
}): string;
|
|
9
10
|
export declare function buildJsonSchemaForMode(mode: AiGenerationMode): Record<string, unknown>;
|
|
11
|
+
/**
|
|
12
|
+
* Renders the component catalog as the compact reference block shared by every prompt.
|
|
13
|
+
* Extracted from `buildSystemPrompt` so the agent prompt (STORA-530) reuses the exact
|
|
14
|
+
* same catalog rendering instead of drifting into a second format.
|
|
15
|
+
*/
|
|
16
|
+
export declare function formatComponentCatalog(catalog: AiCompiledComponentSpec[]): string;
|
|
17
|
+
export interface BuildAgentSystemPromptOptions {
|
|
18
|
+
catalog: AiCompiledComponentSpec[];
|
|
19
|
+
document: PageDocument;
|
|
20
|
+
selectedNodeId?: string;
|
|
21
|
+
prefix?: string;
|
|
22
|
+
stylePreference?: string;
|
|
23
|
+
/** Extra host/product context appended verbatim. */
|
|
24
|
+
additionalContext?: string;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* System prompt for agent mode (STORA-530).
|
|
28
|
+
*
|
|
29
|
+
* Deliberately different from `buildSystemPrompt`: the agent does NOT emit document JSON.
|
|
30
|
+
* It reads the page through tools and edits through tools, so the prompt's job is to
|
|
31
|
+
* establish (a) the outline as the shared map, (b) the selection as the default target,
|
|
32
|
+
* and (c) the surgical-edit discipline — the whole point of the feature is that asking to
|
|
33
|
+
* recolor one button must not regenerate its section.
|
|
34
|
+
*
|
|
35
|
+
* The catalog goes before the volatile per-turn context so the stable prefix stays
|
|
36
|
+
* prompt-cacheable across the steps of a run.
|
|
37
|
+
*/
|
|
38
|
+
export declare function buildAgentSystemPrompt(options: BuildAgentSystemPromptOptions): string;
|
|
10
39
|
//# sourceMappingURL=prompt-compiler.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"prompt-compiler.d.ts","sourceRoot":"","sources":["../../src/core/prompt-compiler.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,uBAAuB,EAEvB,qBAAqB,EAErB,gBAAgB,EACjB,MAAM,UAAU,CAAC;
|
|
1
|
+
{"version":3,"file":"prompt-compiler.d.ts","sourceRoot":"","sources":["../../src/core/prompt-compiler.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AACpD,OAAO,KAAK,EACV,uBAAuB,EAEvB,qBAAqB,EAErB,gBAAgB,EACjB,MAAM,UAAU,CAAC;AAOlB,wBAAgB,uBAAuB,CACrC,QAAQ,CAAC,EAAE,qBAAqB,GAC/B,uBAAuB,EAAE,CA+B3B;AAED,wBAAgB,iBAAiB,CAAC,OAAO,EAAE;IACzC,OAAO,EAAE,uBAAuB,EAAE,CAAC;IACnC,IAAI,EAAE,gBAAgB,CAAC;IACvB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B,GAAG,MAAM,CAkHT;AAED,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,gBAAgB,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CA8CtF;AAGD;;;;GAIG;AACH,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,uBAAuB,EAAE,GAAG,MAAM,CAsBjF;AAED,MAAM,WAAW,6BAA6B;IAC5C,OAAO,EAAE,uBAAuB,EAAE,CAAC;IACnC,QAAQ,EAAE,YAAY,CAAC;IACvB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,oDAAoD;IACpD,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B;AA4BD;;;;;;;;;;;GAWG;AACH,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,6BAA6B,GAAG,MAAM,CA6CrF"}
|
package/dist/index.cjs
CHANGED
|
@@ -20,18 +20,167 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
20
20
|
// src/index.ts
|
|
21
21
|
var src_exports = {};
|
|
22
22
|
__export(src_exports, {
|
|
23
|
+
buildAgentSystemPrompt: () => buildAgentSystemPrompt,
|
|
23
24
|
buildJsonSchemaForMode: () => buildJsonSchemaForMode,
|
|
25
|
+
buildSelectionContext: () => buildSelectionContext,
|
|
24
26
|
buildSystemPrompt: () => buildSystemPrompt,
|
|
25
27
|
compileComponentCatalog: () => compileComponentCatalog,
|
|
28
|
+
countDescendants: () => countDescendants,
|
|
26
29
|
extractJsonFromResponse: () => extractJsonFromResponse,
|
|
30
|
+
formatComponentCatalog: () => formatComponentCatalog,
|
|
31
|
+
formatSelectionContext: () => formatSelectionContext,
|
|
32
|
+
getMessageText: () => getMessageText,
|
|
33
|
+
getNodeLabel: () => getNodeLabel,
|
|
34
|
+
getToolResultBlocks: () => getToolResultBlocks,
|
|
35
|
+
getToolUseBlocks: () => getToolUseBlocks,
|
|
36
|
+
hasStructuredContent: () => hasStructuredContent,
|
|
27
37
|
normalizeAndValidatePageDocument: () => normalizeAndValidatePageDocument,
|
|
28
38
|
normalizeAndValidateRefactoredNode: () => normalizeAndValidateRefactoredNode,
|
|
29
39
|
normalizeAndValidateSectionNode: () => normalizeAndValidateSectionNode,
|
|
30
40
|
normalizeNodeTree: () => normalizeNodeTree,
|
|
31
|
-
normalizeStyles: () => normalizeStyles
|
|
41
|
+
normalizeStyles: () => normalizeStyles,
|
|
42
|
+
pruneNodeForPrompt: () => pruneNodeForPrompt,
|
|
43
|
+
summarizeDocument: () => summarizeDocument,
|
|
44
|
+
summarizeNodeTree: () => summarizeNodeTree
|
|
32
45
|
});
|
|
33
46
|
module.exports = __toCommonJS(src_exports);
|
|
34
47
|
|
|
48
|
+
// src/core/document-outline.ts
|
|
49
|
+
var LABEL_PROPS = ["text", "label", "title", "heading", "content", "placeholder", "alt", "src"];
|
|
50
|
+
var DEFAULT_LABEL_MAX = 60;
|
|
51
|
+
function truncate(value, max = DEFAULT_LABEL_MAX) {
|
|
52
|
+
const oneLine = value.replace(/\s+/g, " ").trim();
|
|
53
|
+
return oneLine.length > max ? `${oneLine.slice(0, max - 1)}\u2026` : oneLine;
|
|
54
|
+
}
|
|
55
|
+
function getNodeLabel(node, max = DEFAULT_LABEL_MAX) {
|
|
56
|
+
const props = node.props ?? {};
|
|
57
|
+
for (const key of LABEL_PROPS) {
|
|
58
|
+
const value = props[key];
|
|
59
|
+
if (typeof value === "string" && value.trim()) {
|
|
60
|
+
return truncate(value, max);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
return "";
|
|
64
|
+
}
|
|
65
|
+
function isOnPathTo(node, targetId) {
|
|
66
|
+
if (node.id === targetId) return true;
|
|
67
|
+
return (node.children ?? []).some((child) => isOnPathTo(child, targetId));
|
|
68
|
+
}
|
|
69
|
+
function summarizeNodeTree(root, options = {}) {
|
|
70
|
+
const { maxDepth = 3, focusNodeId, maxNodes = 200 } = options;
|
|
71
|
+
const lines = [];
|
|
72
|
+
let emitted = 0;
|
|
73
|
+
let truncated = false;
|
|
74
|
+
const walk = (node, depth) => {
|
|
75
|
+
if (emitted >= maxNodes) {
|
|
76
|
+
truncated = true;
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
const indent = " ".repeat(depth);
|
|
80
|
+
const label = getNodeLabel(node);
|
|
81
|
+
const children = node.children ?? [];
|
|
82
|
+
const parts = [`${indent}- #${node.id} (${node.type})`];
|
|
83
|
+
if (label) parts.push(`"${label}"`);
|
|
84
|
+
if (children.length > 0) parts.push(`[${children.length} children]`);
|
|
85
|
+
if (focusNodeId && node.id === focusNodeId) parts.push("\u2190 SELECTED");
|
|
86
|
+
lines.push(parts.join(" "));
|
|
87
|
+
emitted++;
|
|
88
|
+
if (children.length === 0) return;
|
|
89
|
+
const onFocusPath = Boolean(focusNodeId) && isOnPathTo(node, focusNodeId);
|
|
90
|
+
if (depth + 1 > maxDepth && !onFocusPath) {
|
|
91
|
+
lines.push(`${indent} (+${countDescendants(node)} nested nodes \u2014 use read_node to expand)`);
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
for (const child of children) {
|
|
95
|
+
walk(child, depth + 1);
|
|
96
|
+
}
|
|
97
|
+
};
|
|
98
|
+
walk(root, 0);
|
|
99
|
+
if (truncated) {
|
|
100
|
+
lines.push(` (outline truncated at ${maxNodes} nodes \u2014 use find_nodes to locate specifics)`);
|
|
101
|
+
}
|
|
102
|
+
return lines.join("\n");
|
|
103
|
+
}
|
|
104
|
+
function countDescendants(node) {
|
|
105
|
+
return (node.children ?? []).reduce((total, child) => total + 1 + countDescendants(child), 0);
|
|
106
|
+
}
|
|
107
|
+
function summarizeDocument(document, options = {}) {
|
|
108
|
+
const header = [
|
|
109
|
+
`Title: ${document.metadata?.title || "Untitled Page"}`,
|
|
110
|
+
`Description: ${document.metadata?.description || "N/A"}`
|
|
111
|
+
].join("\n");
|
|
112
|
+
return `${header}
|
|
113
|
+
|
|
114
|
+
Page outline:
|
|
115
|
+
${summarizeNodeTree(document.document, options)}`;
|
|
116
|
+
}
|
|
117
|
+
function buildSelectionContext(root, nodeId) {
|
|
118
|
+
const ancestors = [];
|
|
119
|
+
const walk = (node, parent, index) => {
|
|
120
|
+
if (node.id === nodeId) {
|
|
121
|
+
return {
|
|
122
|
+
node,
|
|
123
|
+
ancestors: [...ancestors],
|
|
124
|
+
siblings: parent ? parent.children ?? [] : [node],
|
|
125
|
+
index,
|
|
126
|
+
parentId: parent?.id ?? null
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
ancestors.push(node);
|
|
130
|
+
const children = node.children ?? [];
|
|
131
|
+
for (let i = 0; i < children.length; i++) {
|
|
132
|
+
const found = walk(children[i], node, i);
|
|
133
|
+
if (found) return found;
|
|
134
|
+
}
|
|
135
|
+
ancestors.pop();
|
|
136
|
+
return null;
|
|
137
|
+
};
|
|
138
|
+
return walk(root, null, 0);
|
|
139
|
+
}
|
|
140
|
+
function pruneNodeForPrompt(node, maxDepth = 2, depth = 0) {
|
|
141
|
+
const children = node.children ?? [];
|
|
142
|
+
if (children.length === 0) return node;
|
|
143
|
+
if (depth >= maxDepth) {
|
|
144
|
+
return {
|
|
145
|
+
...node,
|
|
146
|
+
children: [
|
|
147
|
+
{
|
|
148
|
+
id: `${node.id}__collapsed`,
|
|
149
|
+
type: "__collapsed__",
|
|
150
|
+
props: {
|
|
151
|
+
note: `${countDescendants(node)} nested nodes omitted \u2014 call read_node on #${node.id} with includeChildren to expand`
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
]
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
return {
|
|
158
|
+
...node,
|
|
159
|
+
children: children.map((child) => pruneNodeForPrompt(child, maxDepth, depth + 1))
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
function formatSelectionContext(context) {
|
|
163
|
+
const path = context.ancestors.map((a) => `#${a.id}(${a.type})`).join(" > ");
|
|
164
|
+
const siblings = context.siblings.map((sibling, i) => {
|
|
165
|
+
const marker = sibling.id === context.node.id ? " \u2190 selected" : "";
|
|
166
|
+
const label = getNodeLabel(sibling);
|
|
167
|
+
return ` [${i}] #${sibling.id} (${sibling.type})${label ? ` "${label}"` : ""}${marker}`;
|
|
168
|
+
}).join("\n");
|
|
169
|
+
return [
|
|
170
|
+
"### Currently Selected Component",
|
|
171
|
+
`Node: #${context.node.id} (${context.node.type})`,
|
|
172
|
+
`Parent: ${context.parentId ? `#${context.parentId}` : "(root)"} \u2014 position ${context.index}`,
|
|
173
|
+
path ? `Path: ${path}` : "",
|
|
174
|
+
"Full node JSON:",
|
|
175
|
+
"```json",
|
|
176
|
+
JSON.stringify(pruneNodeForPrompt(context.node), null, 2),
|
|
177
|
+
"```",
|
|
178
|
+
"Siblings:",
|
|
179
|
+
siblings,
|
|
180
|
+
'When the user says "this", "ini", "komponen ini" or similar, they mean the selected node above.'
|
|
181
|
+
].filter(Boolean).join("\n");
|
|
182
|
+
}
|
|
183
|
+
|
|
35
184
|
// src/core/prompt-compiler.ts
|
|
36
185
|
function compileComponentCatalog(registry) {
|
|
37
186
|
if (!registry) return [];
|
|
@@ -213,6 +362,86 @@ function buildJsonSchemaForMode(mode) {
|
|
|
213
362
|
}
|
|
214
363
|
return nodeSchema;
|
|
215
364
|
}
|
|
365
|
+
function formatComponentCatalog(catalog) {
|
|
366
|
+
return catalog.map((c) => {
|
|
367
|
+
let desc = `- **${c.type}** (${c.category}): ${c.label}`;
|
|
368
|
+
if (c.description) desc += ` - ${c.description}`;
|
|
369
|
+
desc += ` | acceptsChildren: ${c.acceptsChildren}`;
|
|
370
|
+
if (c.allowedChildren && c.allowedChildren.length > 0) {
|
|
371
|
+
desc += ` | allowedChildren: [${c.allowedChildren.join(", ")}]`;
|
|
372
|
+
}
|
|
373
|
+
if (c.props && c.props.length > 0) {
|
|
374
|
+
const propList = c.props.map((p) => {
|
|
375
|
+
let pStr = `${p.name} (${p.type})`;
|
|
376
|
+
if (p.options) pStr += `[${p.options.map(String).join("|")}]`;
|
|
377
|
+
return pStr;
|
|
378
|
+
}).join(", ");
|
|
379
|
+
desc += ` | props: { ${propList} }`;
|
|
380
|
+
}
|
|
381
|
+
return desc;
|
|
382
|
+
}).join("\n");
|
|
383
|
+
}
|
|
384
|
+
var AGENT_INSTRUCTIONS = `You are the KUBUILD page-editing agent. You edit an existing page by calling tools \u2014 you never output page JSON directly.
|
|
385
|
+
|
|
386
|
+
### HOW YOU WORK
|
|
387
|
+
1. The page outline below is your map. It shows every node's id, type and label, but not its props or styles.
|
|
388
|
+
2. To see a node's actual props/styles, call read_node. To locate nodes by type or text, call find_nodes. Never guess a node id \u2014 every id you pass must come from the outline or from a tool result.
|
|
389
|
+
3. Make the change with the smallest tool that expresses it, then stop and summarize what you changed, in the user's language.
|
|
390
|
+
|
|
391
|
+
### SURGICAL EDIT RULE (the most important rule)
|
|
392
|
+
Change only what the user asked for.
|
|
393
|
+
- Changing text/label/href/src -> update_node_props on that one node.
|
|
394
|
+
- Changing color/spacing/size/alignment -> update_node_styles on that one node.
|
|
395
|
+
- Adding something new -> insert_component (a single element) or insert_section (a whole new section).
|
|
396
|
+
- replace_node is a last resort, only when a node's children genuinely must be restructured. Never use it to change a prop or a style.
|
|
397
|
+
- Never rebuild a section, and never touch nodes the user did not ask about. If an instruction is ambiguous about scope, edit the narrowest node that satisfies it and say so in your summary.
|
|
398
|
+
|
|
399
|
+
### DESTRUCTIVE ACTIONS
|
|
400
|
+
delete_node and replace_node destroy user content. Only call them when the user asked unambiguously (e.g. "hapus section harga"). If the request is broad or vague ("bersihkan halaman ini", "hapus yang tidak perlu"), do not call them \u2014 ask the user which nodes to remove instead.
|
|
401
|
+
|
|
402
|
+
### STYLING RULES
|
|
403
|
+
- styles values are CSS primitives (strings/numbers). Use breakpoint ('base' | 'desktop' | 'tablet' | 'mobile') for normal styling and state (':hover', ':focus', ':active') for interactive states. Never nest a pseudo-class inside a breakpoint layer.
|
|
404
|
+
- Only use component types that exist in the catalog below, and respect acceptsChildren/allowedChildren.
|
|
405
|
+
- Never emit scripts, javascript: URIs, or event-handler attributes.
|
|
406
|
+
|
|
407
|
+
### WHEN A TOOL FAILS
|
|
408
|
+
A tool result with "ok": false means your input was wrong (unknown node id, invalid type, disallowed nesting). Read the error, fix your arguments, and retry \u2014 do not repeat the identical call, and do not give up silently.`;
|
|
409
|
+
function buildAgentSystemPrompt(options) {
|
|
410
|
+
const { catalog, document, selectedNodeId, prefix, stylePreference, additionalContext } = options;
|
|
411
|
+
const selection = selectedNodeId ? buildSelectionContext(document.document, selectedNodeId) : null;
|
|
412
|
+
const sections = [];
|
|
413
|
+
if (prefix) sections.push(prefix);
|
|
414
|
+
sections.push(AGENT_INSTRUCTIONS);
|
|
415
|
+
sections.push(
|
|
416
|
+
`### REGISTERED COMPONENT CATALOG
|
|
417
|
+
${formatComponentCatalog(catalog) || "(Standard core components: page, section, container, columns, heading, paragraph, text, button, input, textarea, select, image, video, icon, badge, code-block)"}`
|
|
418
|
+
);
|
|
419
|
+
sections.push(`### CURRENT PAGE
|
|
420
|
+
${summarizeDocument(document, { focusNodeId: selectedNodeId })}`);
|
|
421
|
+
if (selection) {
|
|
422
|
+
sections.push(formatSelectionContext(selection));
|
|
423
|
+
} else if (selectedNodeId) {
|
|
424
|
+
sections.push(
|
|
425
|
+
`### Selection
|
|
426
|
+
The editor reported node "${selectedNodeId}" as selected, but it is not present in the current page. Ignore it and ask the user what to target.`
|
|
427
|
+
);
|
|
428
|
+
} else {
|
|
429
|
+
sections.push(
|
|
430
|
+
'### Selection\nNo component is selected on the canvas. If the user says "this", ask which component they mean instead of guessing.'
|
|
431
|
+
);
|
|
432
|
+
}
|
|
433
|
+
if (stylePreference) {
|
|
434
|
+
sections.push(
|
|
435
|
+
`### Style Preference
|
|
436
|
+
"${stylePreference}" \u2014 follow this aesthetic for anything you create or restyle.`
|
|
437
|
+
);
|
|
438
|
+
}
|
|
439
|
+
if (additionalContext) {
|
|
440
|
+
sections.push(`### Additional Context
|
|
441
|
+
${additionalContext}`);
|
|
442
|
+
}
|
|
443
|
+
return sections.join("\n\n");
|
|
444
|
+
}
|
|
216
445
|
|
|
217
446
|
// src/core/normalizer.ts
|
|
218
447
|
var import_schema = require("@kubuild/schema");
|
|
@@ -453,16 +682,46 @@ function normalizeAndValidateRefactoredNode(rawJson, originalNode, securityLimit
|
|
|
453
682
|
}
|
|
454
683
|
return parsed.data;
|
|
455
684
|
}
|
|
685
|
+
|
|
686
|
+
// src/core/messages.ts
|
|
687
|
+
function getMessageText(message) {
|
|
688
|
+
if (typeof message.content === "string") return message.content;
|
|
689
|
+
return message.content.filter((block) => block.type === "text").map((block) => block.text).join("\n");
|
|
690
|
+
}
|
|
691
|
+
function getToolUseBlocks(message) {
|
|
692
|
+
if (typeof message.content === "string") return [];
|
|
693
|
+
return message.content.filter((b) => b.type === "tool_use");
|
|
694
|
+
}
|
|
695
|
+
function getToolResultBlocks(message) {
|
|
696
|
+
if (typeof message.content === "string") return [];
|
|
697
|
+
return message.content.filter((b) => b.type === "tool_result");
|
|
698
|
+
}
|
|
699
|
+
function hasStructuredContent(message) {
|
|
700
|
+
return typeof message.content !== "string" && message.content.some((b) => b.type !== "text");
|
|
701
|
+
}
|
|
456
702
|
// Annotate the CommonJS export names for ESM import in node:
|
|
457
703
|
0 && (module.exports = {
|
|
704
|
+
buildAgentSystemPrompt,
|
|
458
705
|
buildJsonSchemaForMode,
|
|
706
|
+
buildSelectionContext,
|
|
459
707
|
buildSystemPrompt,
|
|
460
708
|
compileComponentCatalog,
|
|
709
|
+
countDescendants,
|
|
461
710
|
extractJsonFromResponse,
|
|
711
|
+
formatComponentCatalog,
|
|
712
|
+
formatSelectionContext,
|
|
713
|
+
getMessageText,
|
|
714
|
+
getNodeLabel,
|
|
715
|
+
getToolResultBlocks,
|
|
716
|
+
getToolUseBlocks,
|
|
717
|
+
hasStructuredContent,
|
|
462
718
|
normalizeAndValidatePageDocument,
|
|
463
719
|
normalizeAndValidateRefactoredNode,
|
|
464
720
|
normalizeAndValidateSectionNode,
|
|
465
721
|
normalizeNodeTree,
|
|
466
|
-
normalizeStyles
|
|
722
|
+
normalizeStyles,
|
|
723
|
+
pruneNodeForPrompt,
|
|
724
|
+
summarizeDocument,
|
|
725
|
+
summarizeNodeTree
|
|
467
726
|
});
|
|
468
727
|
//# sourceMappingURL=index.cjs.map
|