@decantr/core 1.0.0-beta.9 → 1.0.1
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/LICENSE +21 -0
- package/README.md +66 -0
- package/dist/index.d.ts +296 -7
- package/dist/index.js +923 -17
- package/dist/index.js.map +1 -1
- package/package.json +28 -12
- package/schema/execution-pack-bundle.v1.json +58 -0
- package/schema/execution-pack.common.v1.json +192 -0
- package/schema/mutation-pack.v1.json +94 -0
- package/schema/pack-manifest.v1.json +110 -0
- package/schema/page-pack.v1.json +101 -0
- package/schema/review-pack.v1.json +97 -0
- package/schema/scaffold-pack.v1.json +87 -0
- package/schema/section-pack.v1.json +92 -0
- package/schema/selected-execution-pack.v1.json +75 -0
package/dist/index.js
CHANGED
|
@@ -89,9 +89,12 @@ function routePath(pageId, index) {
|
|
|
89
89
|
function capitalize(str) {
|
|
90
90
|
return str.charAt(0).toUpperCase() + str.slice(1);
|
|
91
91
|
}
|
|
92
|
-
function buildNavItems(pages) {
|
|
92
|
+
function buildNavItems(pages, routes) {
|
|
93
|
+
const routeLookup = new Map(
|
|
94
|
+
(routes ?? []).map((route) => [route.pageId, route.path])
|
|
95
|
+
);
|
|
93
96
|
return pages.map((page, i) => ({
|
|
94
|
-
href: routePath(page.id, i),
|
|
97
|
+
href: routeLookup.get(page.id) ?? routePath(page.id, i),
|
|
95
98
|
icon: NAV_ICONS[page.id] || "circle",
|
|
96
99
|
label: capitalize(page.id.replace(/-/g, " "))
|
|
97
100
|
}));
|
|
@@ -106,7 +109,7 @@ function buildThemeDecoration(theme) {
|
|
|
106
109
|
header: shell.header || "",
|
|
107
110
|
brand: shellAny["brand"] || "",
|
|
108
111
|
navLabel: shellAny["navLabel"] || "",
|
|
109
|
-
// AUTO: default nav style is 'pill'
|
|
112
|
+
// AUTO: default nav style is 'pill' when the theme does not declare one
|
|
110
113
|
navStyle: shell.nav_style || "pill",
|
|
111
114
|
defaultNavState: shellAny["default_nav_state"] || "expanded",
|
|
112
115
|
dimensions: shell.dimensions || null
|
|
@@ -137,6 +140,30 @@ function blueprintPageToStructurePage(page, defaultShell) {
|
|
|
137
140
|
...page.surface ? { surface: page.surface } : {}
|
|
138
141
|
};
|
|
139
142
|
}
|
|
143
|
+
function buildV3Routes(essence, blueprintPages, structurePages) {
|
|
144
|
+
const explicitRoutes = /* @__PURE__ */ new Map();
|
|
145
|
+
for (const [path, entry] of Object.entries(essence.blueprint.routes ?? {})) {
|
|
146
|
+
if (entry?.page && !explicitRoutes.has(entry.page)) {
|
|
147
|
+
explicitRoutes.set(entry.page, path);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
for (const page of blueprintPages) {
|
|
151
|
+
if (page.route && !explicitRoutes.has(page.id)) {
|
|
152
|
+
explicitRoutes.set(page.id, page.route);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
if (explicitRoutes.size > 0) {
|
|
156
|
+
return structurePages.flatMap((page) => {
|
|
157
|
+
const path = explicitRoutes.get(page.id);
|
|
158
|
+
return path ? [{ path, pageId: page.id, shell: page.shell }] : [];
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
return structurePages.map((page, i) => ({
|
|
162
|
+
path: routePath(page.id, i),
|
|
163
|
+
pageId: page.id,
|
|
164
|
+
shell: page.shell
|
|
165
|
+
}));
|
|
166
|
+
}
|
|
140
167
|
function convertWiring(wiringResults) {
|
|
141
168
|
if (wiringResults.length === 0) return null;
|
|
142
169
|
const signals = [];
|
|
@@ -243,7 +270,8 @@ async function resolveEssence(essence, resolver) {
|
|
|
243
270
|
};
|
|
244
271
|
const routes = simpleEssence.structure.map((page, i) => ({
|
|
245
272
|
path: routePath(page.id, i),
|
|
246
|
-
pageId: page.id
|
|
273
|
+
pageId: page.id,
|
|
274
|
+
shell: page.shell
|
|
247
275
|
}));
|
|
248
276
|
return {
|
|
249
277
|
essence,
|
|
@@ -278,13 +306,14 @@ async function resolveV3Essence(essence, resolver) {
|
|
|
278
306
|
const theme = buildThemeFromV3(essence, isAddon);
|
|
279
307
|
const blueprintPages = blueprint.pages ?? (blueprint.sections ? blueprint.sections.flatMap((s) => s.pages) : [{ id: "home", layout: ["hero"] }]);
|
|
280
308
|
const defaultShell = blueprint.shell ?? (blueprint.sections?.[0]?.shell ?? "sidebar-main");
|
|
281
|
-
const structurePages =
|
|
282
|
-
(
|
|
283
|
-
);
|
|
309
|
+
const structurePages = blueprint.pages ? blueprint.pages.map((page) => blueprintPageToStructurePage(page, defaultShell)) : blueprint.sections ? blueprint.sections.flatMap(
|
|
310
|
+
(section) => section.pages.map((page) => blueprintPageToStructurePage(page, section.shell ?? defaultShell))
|
|
311
|
+
) : [blueprintPageToStructurePage({ id: "home", layout: ["hero"] }, defaultShell)];
|
|
284
312
|
const resolvedPages = await resolvePages(structurePages, resolver, registryTheme);
|
|
285
313
|
const shellType = defaultShell;
|
|
286
314
|
const brand = pascalCase(meta.archetype);
|
|
287
|
-
const
|
|
315
|
+
const routes = buildV3Routes(essence, blueprintPages, structurePages);
|
|
316
|
+
const nav = buildNavItems(structurePages, routes);
|
|
288
317
|
const decoration = registryTheme ? buildThemeDecoration(registryTheme) : null;
|
|
289
318
|
const shell = {
|
|
290
319
|
type: shellType,
|
|
@@ -293,10 +322,6 @@ async function resolveV3Essence(essence, resolver) {
|
|
|
293
322
|
inset: false,
|
|
294
323
|
decoration
|
|
295
324
|
};
|
|
296
|
-
const routes = structurePages.map((page, i) => ({
|
|
297
|
-
path: routePath(page.id, i),
|
|
298
|
-
pageId: page.id
|
|
299
|
-
}));
|
|
300
325
|
return {
|
|
301
326
|
essence,
|
|
302
327
|
pages: resolvedPages,
|
|
@@ -434,7 +459,8 @@ function buildPageIR(page, resolvedPatterns, wiring, theme, density, layer) {
|
|
|
434
459
|
children.push(gridNode);
|
|
435
460
|
}
|
|
436
461
|
}
|
|
437
|
-
const
|
|
462
|
+
const gapAtom = density.gap.startsWith("_") ? density.gap : density.gap.startsWith("gap") ? `_${density.gap}` : `_gap${density.gap}`;
|
|
463
|
+
const surface = page.surface || `_flex _col ${gapAtom} _p4 _overauto _flex1`;
|
|
438
464
|
return {
|
|
439
465
|
type: "page",
|
|
440
466
|
id: page.id,
|
|
@@ -459,10 +485,15 @@ async function runPipeline(essence, options) {
|
|
|
459
485
|
throw new Error(`Invalid essence: ${validation.errors.join(", ")}`);
|
|
460
486
|
}
|
|
461
487
|
const effectiveEssence = isV32(essence) ? essence : migrateV2ToV3(essence);
|
|
462
|
-
const resolver =
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
488
|
+
const resolver = options.resolver ?? (() => {
|
|
489
|
+
if (!options.contentRoot) {
|
|
490
|
+
throw new Error("Pipeline options must include either a contentRoot or a resolver.");
|
|
491
|
+
}
|
|
492
|
+
return createResolver({
|
|
493
|
+
contentRoot: options.contentRoot,
|
|
494
|
+
overridePaths: options.overridePaths
|
|
495
|
+
});
|
|
496
|
+
})();
|
|
466
497
|
const resolved = await resolveEssence(effectiveEssence, resolver);
|
|
467
498
|
const layer = resolved.isV3Source ? "blueprint" : void 0;
|
|
468
499
|
const pageNodes = [];
|
|
@@ -555,14 +586,889 @@ function validateIR(root) {
|
|
|
555
586
|
});
|
|
556
587
|
return errors;
|
|
557
588
|
}
|
|
589
|
+
|
|
590
|
+
// src/packs.ts
|
|
591
|
+
import { isV3 as isV33, migrateV2ToV3 as migrateV2ToV32 } from "@decantr/essence-spec";
|
|
592
|
+
var EXECUTION_PACK_SCHEMA_URLS = {
|
|
593
|
+
scaffold: "https://decantr.ai/schemas/scaffold-pack.v1.json",
|
|
594
|
+
section: "https://decantr.ai/schemas/section-pack.v1.json",
|
|
595
|
+
page: "https://decantr.ai/schemas/page-pack.v1.json",
|
|
596
|
+
mutation: "https://decantr.ai/schemas/mutation-pack.v1.json",
|
|
597
|
+
review: "https://decantr.ai/schemas/review-pack.v1.json"
|
|
598
|
+
};
|
|
599
|
+
var PACK_MANIFEST_SCHEMA_URL = "https://decantr.ai/schemas/pack-manifest.v1.json";
|
|
600
|
+
var EXECUTION_PACK_BUNDLE_SCHEMA_URL = "https://decantr.ai/schemas/execution-pack-bundle.v1.json";
|
|
601
|
+
var SELECTED_EXECUTION_PACK_SCHEMA_URL = "https://decantr.ai/schemas/selected-execution-pack.v1.json";
|
|
602
|
+
var DEFAULT_TARGET = {
|
|
603
|
+
platform: "web",
|
|
604
|
+
framework: null,
|
|
605
|
+
runtime: null,
|
|
606
|
+
adapter: "generic-web"
|
|
607
|
+
};
|
|
608
|
+
var DEFAULT_TOKEN_BUDGET = {
|
|
609
|
+
target: 1400,
|
|
610
|
+
max: 2200,
|
|
611
|
+
strategy: [
|
|
612
|
+
"Prefer route summaries over repeated prose.",
|
|
613
|
+
"Use compact vocabulary lists instead of large reference tables.",
|
|
614
|
+
"Include only task-relevant examples and checks."
|
|
615
|
+
]
|
|
616
|
+
};
|
|
617
|
+
var DEFAULT_SUCCESS_CHECKS = [
|
|
618
|
+
{
|
|
619
|
+
id: "route-topology",
|
|
620
|
+
label: "Routes and page IDs match the compiled topology.",
|
|
621
|
+
severity: "error"
|
|
622
|
+
},
|
|
623
|
+
{
|
|
624
|
+
id: "shell-consistency",
|
|
625
|
+
label: "The declared shell contract is preserved unless the task explicitly mutates it.",
|
|
626
|
+
severity: "error"
|
|
627
|
+
},
|
|
628
|
+
{
|
|
629
|
+
id: "theme-consistency",
|
|
630
|
+
label: "Theme identity and mode remain consistent across scaffolded routes.",
|
|
631
|
+
severity: "warn"
|
|
632
|
+
}
|
|
633
|
+
];
|
|
634
|
+
var DEFAULT_SECTION_SUCCESS_CHECKS = [
|
|
635
|
+
{
|
|
636
|
+
id: "section-route-coherence",
|
|
637
|
+
label: "Section pages and routes remain coherent with the compiled topology.",
|
|
638
|
+
severity: "error"
|
|
639
|
+
},
|
|
640
|
+
{
|
|
641
|
+
id: "section-shell-consistency",
|
|
642
|
+
label: "The section shell contract stays consistent across its routes.",
|
|
643
|
+
severity: "error"
|
|
644
|
+
},
|
|
645
|
+
{
|
|
646
|
+
id: "section-pattern-coverage",
|
|
647
|
+
label: "Primary section patterns are represented without adding off-contract filler sections.",
|
|
648
|
+
severity: "warn"
|
|
649
|
+
}
|
|
650
|
+
];
|
|
651
|
+
var DEFAULT_PAGE_SUCCESS_CHECKS = [
|
|
652
|
+
{
|
|
653
|
+
id: "page-route-contract",
|
|
654
|
+
label: "The page keeps the compiled route, shell, and section contract intact.",
|
|
655
|
+
severity: "error"
|
|
656
|
+
},
|
|
657
|
+
{
|
|
658
|
+
id: "page-pattern-contract",
|
|
659
|
+
label: "The page preserves its primary compiled patterns instead of drifting into unrelated layouts.",
|
|
660
|
+
severity: "error"
|
|
661
|
+
},
|
|
662
|
+
{
|
|
663
|
+
id: "page-state-contract",
|
|
664
|
+
label: "Any declared wiring signals remain coherent with the rendered page structure.",
|
|
665
|
+
severity: "warn"
|
|
666
|
+
}
|
|
667
|
+
];
|
|
668
|
+
var DEFAULT_MUTATION_SUCCESS_CHECKS = {
|
|
669
|
+
"add-page": [
|
|
670
|
+
{
|
|
671
|
+
id: "mutation-essence-first",
|
|
672
|
+
label: "New pages are declared in the essence before any code generation begins.",
|
|
673
|
+
severity: "error"
|
|
674
|
+
},
|
|
675
|
+
{
|
|
676
|
+
id: "mutation-shell-contract",
|
|
677
|
+
label: "New routes inherit an existing shell and section contract unless the essence changes first.",
|
|
678
|
+
severity: "error"
|
|
679
|
+
},
|
|
680
|
+
{
|
|
681
|
+
id: "mutation-refresh",
|
|
682
|
+
label: "Refresh compiled packs after the mutation so downstream tasks read current topology.",
|
|
683
|
+
severity: "warn"
|
|
684
|
+
}
|
|
685
|
+
],
|
|
686
|
+
modify: [
|
|
687
|
+
{
|
|
688
|
+
id: "mutation-existing-topology",
|
|
689
|
+
label: "Modified routes remain coherent with the compiled topology unless the essence changes first.",
|
|
690
|
+
severity: "error"
|
|
691
|
+
},
|
|
692
|
+
{
|
|
693
|
+
id: "mutation-theme-contract",
|
|
694
|
+
label: "Theme, shell, and page identity stay aligned with the current contract during edits.",
|
|
695
|
+
severity: "error"
|
|
696
|
+
},
|
|
697
|
+
{
|
|
698
|
+
id: "mutation-page-pack-first",
|
|
699
|
+
label: "Route-local edits should start from the compiled page pack rather than improvised structure.",
|
|
700
|
+
severity: "warn"
|
|
701
|
+
}
|
|
702
|
+
]
|
|
703
|
+
};
|
|
704
|
+
var DEFAULT_REVIEW_SUCCESS_CHECKS = [
|
|
705
|
+
{
|
|
706
|
+
id: "review-contract-baseline",
|
|
707
|
+
label: "Review findings should use the compiled route, shell, and theme contract as the baseline.",
|
|
708
|
+
severity: "error"
|
|
709
|
+
},
|
|
710
|
+
{
|
|
711
|
+
id: "review-evidence",
|
|
712
|
+
label: "Each critique finding should cite concrete evidence from the generated workspace.",
|
|
713
|
+
severity: "error"
|
|
714
|
+
},
|
|
715
|
+
{
|
|
716
|
+
id: "review-remediation",
|
|
717
|
+
label: "Suggested fixes should point back to code changes or essence updates when contract drift exists.",
|
|
718
|
+
severity: "warn"
|
|
719
|
+
}
|
|
720
|
+
];
|
|
721
|
+
var DEFAULT_REVIEW_ANTI_PATTERNS = [
|
|
722
|
+
{
|
|
723
|
+
id: "inline-styles",
|
|
724
|
+
summary: "Avoid inline style literals as the primary styling path.",
|
|
725
|
+
guidance: "Move visual styling into tokens.css and treatments.css instead of component-local style objects."
|
|
726
|
+
},
|
|
727
|
+
{
|
|
728
|
+
id: "hardcoded-colors",
|
|
729
|
+
summary: "Avoid hardcoded color literals.",
|
|
730
|
+
guidance: "Use CSS variables and theme decorators instead of hex, rgb, or hsl values."
|
|
731
|
+
},
|
|
732
|
+
{
|
|
733
|
+
id: "utility-framework-leakage",
|
|
734
|
+
summary: "Avoid utility-framework leakage as the primary design language.",
|
|
735
|
+
guidance: "Prefer compiled Decantr treatments and contract vocabulary over ad hoc utility class stacks."
|
|
736
|
+
}
|
|
737
|
+
];
|
|
738
|
+
function collectPatternIds(page) {
|
|
739
|
+
const patternIds = [];
|
|
740
|
+
walkIR(page, (node) => {
|
|
741
|
+
if (node.type !== "pattern") return;
|
|
742
|
+
const patternNode = node;
|
|
743
|
+
patternIds.push(patternNode.pattern.patternId);
|
|
744
|
+
});
|
|
745
|
+
return [...new Set(patternIds)];
|
|
746
|
+
}
|
|
747
|
+
function summarizeRoutes(appNode) {
|
|
748
|
+
return appNode.routes.flatMap((route) => {
|
|
749
|
+
const pageNode = findPageNode(appNode, route.pageId);
|
|
750
|
+
if (!pageNode) return [];
|
|
751
|
+
return [{
|
|
752
|
+
pageId: pageNode.pageId,
|
|
753
|
+
path: route.path,
|
|
754
|
+
patternIds: collectPatternIds(pageNode),
|
|
755
|
+
shell: route.shell
|
|
756
|
+
}];
|
|
757
|
+
});
|
|
758
|
+
}
|
|
759
|
+
function summarizeSectionRoutes(appNode, input) {
|
|
760
|
+
return summarizeRoutes(appNode).filter((route) => input.pageIds.includes(route.pageId));
|
|
761
|
+
}
|
|
762
|
+
function summarizePageRoute(appNode, pageId) {
|
|
763
|
+
return summarizeRoutes(appNode).find((route) => route.pageId === pageId) ?? null;
|
|
764
|
+
}
|
|
765
|
+
function findPageNode(appNode, pageId) {
|
|
766
|
+
const page = appNode.children.find((node) => node.pageId === pageId);
|
|
767
|
+
return page ? page : null;
|
|
768
|
+
}
|
|
769
|
+
function collectPagePatterns(page) {
|
|
770
|
+
const patterns = [];
|
|
771
|
+
walkIR(page, (node) => {
|
|
772
|
+
if (node.type !== "pattern") return;
|
|
773
|
+
const patternNode = node;
|
|
774
|
+
patterns.push({
|
|
775
|
+
id: patternNode.pattern.patternId,
|
|
776
|
+
alias: patternNode.pattern.alias,
|
|
777
|
+
preset: patternNode.pattern.preset,
|
|
778
|
+
layout: patternNode.pattern.layout
|
|
779
|
+
});
|
|
780
|
+
});
|
|
781
|
+
return patterns;
|
|
782
|
+
}
|
|
783
|
+
function mergeTokenBudget(overrides) {
|
|
784
|
+
return {
|
|
785
|
+
target: overrides?.target ?? DEFAULT_TOKEN_BUDGET.target,
|
|
786
|
+
max: overrides?.max ?? DEFAULT_TOKEN_BUDGET.max,
|
|
787
|
+
strategy: overrides?.strategy ?? DEFAULT_TOKEN_BUDGET.strategy
|
|
788
|
+
};
|
|
789
|
+
}
|
|
790
|
+
function renderList(title, entries) {
|
|
791
|
+
if (entries.length === 0) return [];
|
|
792
|
+
return [title, ...entries.map((entry) => `- ${entry}`), ""];
|
|
793
|
+
}
|
|
794
|
+
function renderExecutionPackMarkdown(pack) {
|
|
795
|
+
const lines = [];
|
|
796
|
+
lines.push(`# ${pack.packType.charAt(0).toUpperCase()}${pack.packType.slice(1)} Pack`);
|
|
797
|
+
lines.push("");
|
|
798
|
+
lines.push(`**Objective:** ${pack.objective}`);
|
|
799
|
+
lines.push(`**Target:** ${pack.target.adapter}${pack.target.framework ? ` (${pack.target.framework})` : ""}`);
|
|
800
|
+
lines.push(`**Scope:** pages=${pack.scope.pageIds.join(", ") || "none"} | patterns=${pack.scope.patternIds.join(", ") || "none"}`);
|
|
801
|
+
lines.push("");
|
|
802
|
+
if (pack.packType === "scaffold") {
|
|
803
|
+
const scaffoldPack = pack;
|
|
804
|
+
const scaffoldShells = [...new Set(scaffoldPack.data.routes.map((route) => route.shell).filter(Boolean))];
|
|
805
|
+
lines.push("## Scaffold Contract");
|
|
806
|
+
lines.push(`- Shell: ${scaffoldPack.data.shell}`);
|
|
807
|
+
if (scaffoldShells.length > 1) {
|
|
808
|
+
const secondaryShells = scaffoldShells.filter((shell) => shell !== scaffoldPack.data.shell);
|
|
809
|
+
lines.push(`- Shells: ${[`${scaffoldPack.data.shell} (primary)`, ...secondaryShells].join(", ")}`);
|
|
810
|
+
}
|
|
811
|
+
lines.push(`- Theme: ${scaffoldPack.data.theme.id} (${scaffoldPack.data.theme.mode})`);
|
|
812
|
+
lines.push(`- Routing: ${scaffoldPack.data.routing}`);
|
|
813
|
+
if (scaffoldPack.data.features.length > 0) {
|
|
814
|
+
lines.push(`- Features: ${scaffoldPack.data.features.join(", ")}`);
|
|
815
|
+
}
|
|
816
|
+
if (scaffoldPack.data.navigation?.commandPalette || scaffoldPack.data.navigation?.hotkeys.length) {
|
|
817
|
+
lines.push("- Navigation:");
|
|
818
|
+
if (scaffoldPack.data.navigation.commandPalette) {
|
|
819
|
+
lines.push(" - command palette required");
|
|
820
|
+
}
|
|
821
|
+
for (const hotkey of scaffoldPack.data.navigation.hotkeys) {
|
|
822
|
+
const target = [hotkey.label, hotkey.route].filter(Boolean).join(" \u2014 ");
|
|
823
|
+
lines.push(` - ${hotkey.key}${target ? `: ${target}` : ""}`);
|
|
824
|
+
}
|
|
825
|
+
}
|
|
826
|
+
lines.push("");
|
|
827
|
+
lines.push("## Route Plan");
|
|
828
|
+
for (const route of scaffoldPack.data.routes) {
|
|
829
|
+
const patterns = route.patternIds.length > 0 ? route.patternIds.join(", ") : "none";
|
|
830
|
+
lines.push(`- ${route.path} -> ${route.pageId}${route.shell ? ` @ ${route.shell}` : ""} [${patterns}]`);
|
|
831
|
+
}
|
|
832
|
+
lines.push("");
|
|
833
|
+
}
|
|
834
|
+
if (pack.packType === "section") {
|
|
835
|
+
const sectionPack = pack;
|
|
836
|
+
lines.push("## Section Contract");
|
|
837
|
+
lines.push(`- Section: ${sectionPack.data.sectionId}`);
|
|
838
|
+
lines.push(`- Role: ${sectionPack.data.role}`);
|
|
839
|
+
lines.push(`- Shell: ${sectionPack.data.shell}`);
|
|
840
|
+
lines.push(`- Theme: ${sectionPack.data.theme.id} (${sectionPack.data.theme.mode})`);
|
|
841
|
+
if (sectionPack.data.features.length > 0) {
|
|
842
|
+
lines.push(`- Features: ${sectionPack.data.features.join(", ")}`);
|
|
843
|
+
}
|
|
844
|
+
if (sectionPack.data.description) {
|
|
845
|
+
lines.push(`- Description: ${sectionPack.data.description}`);
|
|
846
|
+
}
|
|
847
|
+
lines.push("");
|
|
848
|
+
lines.push("## Section Routes");
|
|
849
|
+
for (const route of sectionPack.data.routes) {
|
|
850
|
+
const patterns = route.patternIds.length > 0 ? route.patternIds.join(", ") : "none";
|
|
851
|
+
lines.push(`- ${route.path} -> ${route.pageId}${route.shell ? ` @ ${route.shell}` : ""} [${patterns}]`);
|
|
852
|
+
}
|
|
853
|
+
lines.push("");
|
|
854
|
+
}
|
|
855
|
+
if (pack.packType === "page") {
|
|
856
|
+
const pagePack = pack;
|
|
857
|
+
lines.push("## Page Contract");
|
|
858
|
+
lines.push(`- Page: ${pagePack.data.pageId}`);
|
|
859
|
+
lines.push(`- Path: ${pagePack.data.path}`);
|
|
860
|
+
lines.push(`- Shell: ${pagePack.data.shell}`);
|
|
861
|
+
if (pagePack.data.sectionId) {
|
|
862
|
+
const role = pagePack.data.sectionRole ? ` (${pagePack.data.sectionRole})` : "";
|
|
863
|
+
lines.push(`- Section: ${pagePack.data.sectionId}${role}`);
|
|
864
|
+
}
|
|
865
|
+
lines.push(`- Theme: ${pagePack.data.theme.id} (${pagePack.data.theme.mode})`);
|
|
866
|
+
if (pagePack.data.features.length > 0) {
|
|
867
|
+
lines.push(`- Features: ${pagePack.data.features.join(", ")}`);
|
|
868
|
+
}
|
|
869
|
+
if (pagePack.data.surface) {
|
|
870
|
+
lines.push(`- Surface: ${pagePack.data.surface}`);
|
|
871
|
+
}
|
|
872
|
+
lines.push("");
|
|
873
|
+
lines.push("## Page Patterns");
|
|
874
|
+
for (const pattern of pagePack.data.patterns) {
|
|
875
|
+
lines.push(`- ${pattern.alias} -> ${pattern.id} [${pattern.layout}${pattern.preset ? ` | ${pattern.preset}` : ""}]`);
|
|
876
|
+
}
|
|
877
|
+
lines.push("");
|
|
878
|
+
if (pagePack.data.wiringSignals.length > 0) {
|
|
879
|
+
lines.push("## Wiring Signals");
|
|
880
|
+
for (const signal of pagePack.data.wiringSignals) {
|
|
881
|
+
lines.push(`- ${signal}`);
|
|
882
|
+
}
|
|
883
|
+
lines.push("");
|
|
884
|
+
}
|
|
885
|
+
}
|
|
886
|
+
if (pack.packType === "mutation") {
|
|
887
|
+
const mutationPack = pack;
|
|
888
|
+
lines.push("## Mutation Contract");
|
|
889
|
+
lines.push(`- Operation: ${mutationPack.data.mutationType}`);
|
|
890
|
+
lines.push(`- Shell: ${mutationPack.data.shell}`);
|
|
891
|
+
lines.push(`- Theme: ${mutationPack.data.theme.id} (${mutationPack.data.theme.mode})`);
|
|
892
|
+
lines.push(`- Routing: ${mutationPack.data.routing}`);
|
|
893
|
+
if (mutationPack.data.features.length > 0) {
|
|
894
|
+
lines.push(`- Features: ${mutationPack.data.features.join(", ")}`);
|
|
895
|
+
}
|
|
896
|
+
lines.push("");
|
|
897
|
+
lines.push("## Route Topology");
|
|
898
|
+
for (const route of mutationPack.data.routes) {
|
|
899
|
+
const patterns = route.patternIds.length > 0 ? route.patternIds.join(", ") : "none";
|
|
900
|
+
lines.push(`- ${route.path} -> ${route.pageId}${route.shell ? ` @ ${route.shell}` : ""} [${patterns}]`);
|
|
901
|
+
}
|
|
902
|
+
lines.push("");
|
|
903
|
+
if (mutationPack.data.workflow.length > 0) {
|
|
904
|
+
lines.push("## Workflow");
|
|
905
|
+
for (const step of mutationPack.data.workflow) {
|
|
906
|
+
lines.push(`- ${step}`);
|
|
907
|
+
}
|
|
908
|
+
lines.push("");
|
|
909
|
+
}
|
|
910
|
+
}
|
|
911
|
+
if (pack.packType === "review") {
|
|
912
|
+
const reviewPack = pack;
|
|
913
|
+
lines.push("## Review Contract");
|
|
914
|
+
lines.push(`- Review Type: ${reviewPack.data.reviewType}`);
|
|
915
|
+
lines.push(`- Shell: ${reviewPack.data.shell}`);
|
|
916
|
+
lines.push(`- Theme: ${reviewPack.data.theme.id} (${reviewPack.data.theme.mode})`);
|
|
917
|
+
lines.push(`- Routing: ${reviewPack.data.routing}`);
|
|
918
|
+
if (reviewPack.data.features.length > 0) {
|
|
919
|
+
lines.push(`- Features: ${reviewPack.data.features.join(", ")}`);
|
|
920
|
+
}
|
|
921
|
+
lines.push("");
|
|
922
|
+
lines.push("## Review Topology");
|
|
923
|
+
for (const route of reviewPack.data.routes) {
|
|
924
|
+
const patterns = route.patternIds.length > 0 ? route.patternIds.join(", ") : "none";
|
|
925
|
+
lines.push(`- ${route.path} -> ${route.pageId}${route.shell ? ` @ ${route.shell}` : ""} [${patterns}]`);
|
|
926
|
+
}
|
|
927
|
+
lines.push("");
|
|
928
|
+
if (reviewPack.data.focusAreas.length > 0) {
|
|
929
|
+
lines.push("## Focus Areas");
|
|
930
|
+
for (const focusArea of reviewPack.data.focusAreas) {
|
|
931
|
+
lines.push(`- ${focusArea}`);
|
|
932
|
+
}
|
|
933
|
+
lines.push("");
|
|
934
|
+
}
|
|
935
|
+
if (reviewPack.data.workflow.length > 0) {
|
|
936
|
+
lines.push("## Review Workflow");
|
|
937
|
+
for (const step of reviewPack.data.workflow) {
|
|
938
|
+
lines.push(`- ${step}`);
|
|
939
|
+
}
|
|
940
|
+
lines.push("");
|
|
941
|
+
}
|
|
942
|
+
}
|
|
943
|
+
lines.push("## Required Setup");
|
|
944
|
+
if (pack.requiredSetup.length === 0) {
|
|
945
|
+
lines.push("- None declared.");
|
|
946
|
+
} else {
|
|
947
|
+
lines.push(...pack.requiredSetup.map((entry) => `- ${entry}`));
|
|
948
|
+
}
|
|
949
|
+
lines.push("");
|
|
950
|
+
lines.push("## Allowed Vocabulary");
|
|
951
|
+
if (pack.allowedVocabulary.length === 0) {
|
|
952
|
+
lines.push("- None declared.");
|
|
953
|
+
} else {
|
|
954
|
+
lines.push(...pack.allowedVocabulary.map((entry) => `- ${entry}`));
|
|
955
|
+
}
|
|
956
|
+
lines.push("");
|
|
957
|
+
lines.push(...renderList("## Success Checks", pack.successChecks.map((entry) => `${entry.label} [${entry.severity}]`)));
|
|
958
|
+
lines.push(...renderList("## Anti-Patterns", pack.antiPatterns.map((entry) => `${entry.summary}: ${entry.guidance}`)));
|
|
959
|
+
lines.push(...renderList("## Examples", pack.examples.map((entry) => `${entry.label} (${entry.language})`)));
|
|
960
|
+
lines.push("## Token Budget");
|
|
961
|
+
lines.push(`- Target: ${pack.tokenBudget.target}`);
|
|
962
|
+
lines.push(`- Max: ${pack.tokenBudget.max}`);
|
|
963
|
+
lines.push(...pack.tokenBudget.strategy.map((entry) => `- ${entry}`));
|
|
964
|
+
lines.push("");
|
|
965
|
+
return lines.join("\n").trimEnd() + "\n";
|
|
966
|
+
}
|
|
967
|
+
function resolvePackAdapter(target, platformType) {
|
|
968
|
+
if (target === "react" && platformType === "spa") return "react-vite";
|
|
969
|
+
if (target === "react") return "react-web";
|
|
970
|
+
if (target === "vue" && platformType === "spa") return "vue-vite";
|
|
971
|
+
if (target === "svelte" && platformType === "spa") return "sveltekit";
|
|
972
|
+
if (target) return target;
|
|
973
|
+
return "generic-web";
|
|
974
|
+
}
|
|
975
|
+
function listPackSections(essence) {
|
|
976
|
+
const declaredSections = essence.blueprint.sections;
|
|
977
|
+
if (declaredSections && declaredSections.length > 0) {
|
|
978
|
+
const routedSectionPages = new Set(
|
|
979
|
+
Object.values(essence.blueprint.routes ?? {}).map((entry) => `${entry.section}:${entry.page}`)
|
|
980
|
+
);
|
|
981
|
+
return declaredSections.map((section) => {
|
|
982
|
+
const pageIds = section.pages.filter((page) => routedSectionPages.size === 0 || routedSectionPages.has(`${section.id}:${page.id}`)).map((page) => page.id);
|
|
983
|
+
return {
|
|
984
|
+
id: section.id,
|
|
985
|
+
role: section.role,
|
|
986
|
+
shell: section.shell,
|
|
987
|
+
description: section.description,
|
|
988
|
+
features: section.features,
|
|
989
|
+
pageIds
|
|
990
|
+
};
|
|
991
|
+
}).filter((section) => section.pageIds.length > 0);
|
|
992
|
+
}
|
|
993
|
+
const pages = essence.blueprint.pages ?? [{ id: "home", layout: ["hero"] }];
|
|
994
|
+
return [{
|
|
995
|
+
id: essence.meta.archetype || "default",
|
|
996
|
+
role: "primary",
|
|
997
|
+
shell: essence.blueprint.shell ?? "sidebar-main",
|
|
998
|
+
description: `${essence.meta.archetype || "Application"} section`,
|
|
999
|
+
features: essence.blueprint.features || [],
|
|
1000
|
+
pageIds: pages.map((page) => page.id)
|
|
1001
|
+
}];
|
|
1002
|
+
}
|
|
1003
|
+
function listPackPages(essence) {
|
|
1004
|
+
const declaredSections = essence.blueprint.sections;
|
|
1005
|
+
if (declaredSections && declaredSections.length > 0) {
|
|
1006
|
+
const routedSectionPages = new Set(
|
|
1007
|
+
Object.values(essence.blueprint.routes ?? {}).map((entry) => `${entry.section}:${entry.page}`)
|
|
1008
|
+
);
|
|
1009
|
+
return declaredSections.flatMap(
|
|
1010
|
+
(section) => section.pages.map((page) => ({
|
|
1011
|
+
pageId: page.id,
|
|
1012
|
+
shell: page.shell_override ?? section.shell,
|
|
1013
|
+
sectionId: section.id,
|
|
1014
|
+
sectionRole: section.role,
|
|
1015
|
+
features: section.features
|
|
1016
|
+
})).filter((page) => routedSectionPages.size === 0 || routedSectionPages.has(`${page.sectionId}:${page.pageId}`))
|
|
1017
|
+
);
|
|
1018
|
+
}
|
|
1019
|
+
const pages = essence.blueprint.pages ?? [{ id: "home", layout: ["hero"] }];
|
|
1020
|
+
const defaultShell = essence.blueprint.shell ?? "sidebar-main";
|
|
1021
|
+
return pages.map((page) => ({
|
|
1022
|
+
pageId: page.id,
|
|
1023
|
+
shell: page.shell_override ?? defaultShell,
|
|
1024
|
+
sectionId: essence.meta.archetype || "default",
|
|
1025
|
+
sectionRole: "primary",
|
|
1026
|
+
features: essence.blueprint.features || []
|
|
1027
|
+
}));
|
|
1028
|
+
}
|
|
1029
|
+
function buildScaffoldPack(appNode, options = {}) {
|
|
1030
|
+
const routes = summarizeRoutes(appNode);
|
|
1031
|
+
const scopePatternIds = [...new Set(routes.flatMap((route) => route.patternIds))];
|
|
1032
|
+
const pack = {
|
|
1033
|
+
$schema: EXECUTION_PACK_SCHEMA_URLS.scaffold,
|
|
1034
|
+
packVersion: "1.0.0",
|
|
1035
|
+
packType: "scaffold",
|
|
1036
|
+
objective: options.objective ?? `Scaffold the ${appNode.theme.id} app shell and declared routes.`,
|
|
1037
|
+
target: {
|
|
1038
|
+
...DEFAULT_TARGET,
|
|
1039
|
+
...options.target
|
|
1040
|
+
},
|
|
1041
|
+
preset: options.preset ?? null,
|
|
1042
|
+
scope: {
|
|
1043
|
+
appId: appNode.id,
|
|
1044
|
+
pageIds: routes.map((route) => route.pageId),
|
|
1045
|
+
patternIds: scopePatternIds
|
|
1046
|
+
},
|
|
1047
|
+
requiredSetup: options.requiredSetup ?? [
|
|
1048
|
+
"Treat the declared routes as the topology source of truth.",
|
|
1049
|
+
"Preserve the resolved theme and shell contract unless the task explicitly mutates them."
|
|
1050
|
+
],
|
|
1051
|
+
allowedVocabulary: [.../* @__PURE__ */ new Set([
|
|
1052
|
+
appNode.shell.config.type,
|
|
1053
|
+
appNode.theme.id,
|
|
1054
|
+
appNode.theme.mode,
|
|
1055
|
+
...appNode.features,
|
|
1056
|
+
...scopePatternIds
|
|
1057
|
+
])],
|
|
1058
|
+
examples: options.examples ?? [],
|
|
1059
|
+
antiPatterns: options.antiPatterns ?? [],
|
|
1060
|
+
successChecks: options.successChecks ?? DEFAULT_SUCCESS_CHECKS,
|
|
1061
|
+
tokenBudget: mergeTokenBudget(options.tokenBudget),
|
|
1062
|
+
data: {
|
|
1063
|
+
shell: appNode.shell.config.type,
|
|
1064
|
+
theme: {
|
|
1065
|
+
id: appNode.theme.id,
|
|
1066
|
+
mode: appNode.theme.mode,
|
|
1067
|
+
shape: appNode.theme.shape
|
|
1068
|
+
},
|
|
1069
|
+
routing: appNode.routing,
|
|
1070
|
+
features: appNode.features,
|
|
1071
|
+
navigation: options.navigation,
|
|
1072
|
+
routes
|
|
1073
|
+
},
|
|
1074
|
+
renderedMarkdown: ""
|
|
1075
|
+
};
|
|
1076
|
+
pack.renderedMarkdown = renderExecutionPackMarkdown(pack);
|
|
1077
|
+
return pack;
|
|
1078
|
+
}
|
|
1079
|
+
function buildSectionPack(appNode, input, options = {}) {
|
|
1080
|
+
const routes = summarizeSectionRoutes(appNode, input);
|
|
1081
|
+
const scopePatternIds = [...new Set(routes.flatMap((route) => route.patternIds))];
|
|
1082
|
+
const pack = {
|
|
1083
|
+
$schema: EXECUTION_PACK_SCHEMA_URLS.section,
|
|
1084
|
+
packVersion: "1.0.0",
|
|
1085
|
+
packType: "section",
|
|
1086
|
+
objective: options.objective ?? `Implement the ${input.id} section using the compiled ${input.shell} shell contract.`,
|
|
1087
|
+
target: {
|
|
1088
|
+
...DEFAULT_TARGET,
|
|
1089
|
+
...options.target
|
|
1090
|
+
},
|
|
1091
|
+
preset: options.preset ?? null,
|
|
1092
|
+
scope: {
|
|
1093
|
+
appId: appNode.id,
|
|
1094
|
+
pageIds: routes.map((route) => route.pageId),
|
|
1095
|
+
patternIds: scopePatternIds
|
|
1096
|
+
},
|
|
1097
|
+
requiredSetup: options.requiredSetup ?? [
|
|
1098
|
+
"Use the declared section routes as the source of truth for this slice of the app.",
|
|
1099
|
+
"Keep the section shell consistent unless the task explicitly changes the shell contract."
|
|
1100
|
+
],
|
|
1101
|
+
allowedVocabulary: [.../* @__PURE__ */ new Set([
|
|
1102
|
+
input.id,
|
|
1103
|
+
input.role,
|
|
1104
|
+
input.shell,
|
|
1105
|
+
appNode.theme.id,
|
|
1106
|
+
appNode.theme.mode,
|
|
1107
|
+
...input.features,
|
|
1108
|
+
...scopePatternIds
|
|
1109
|
+
])],
|
|
1110
|
+
examples: options.examples ?? [],
|
|
1111
|
+
antiPatterns: options.antiPatterns ?? [],
|
|
1112
|
+
successChecks: options.successChecks ?? DEFAULT_SECTION_SUCCESS_CHECKS,
|
|
1113
|
+
tokenBudget: mergeTokenBudget(options.tokenBudget),
|
|
1114
|
+
data: {
|
|
1115
|
+
sectionId: input.id,
|
|
1116
|
+
role: input.role,
|
|
1117
|
+
shell: input.shell,
|
|
1118
|
+
description: input.description,
|
|
1119
|
+
features: input.features,
|
|
1120
|
+
theme: {
|
|
1121
|
+
id: appNode.theme.id,
|
|
1122
|
+
mode: appNode.theme.mode,
|
|
1123
|
+
shape: appNode.theme.shape
|
|
1124
|
+
},
|
|
1125
|
+
routes
|
|
1126
|
+
},
|
|
1127
|
+
renderedMarkdown: ""
|
|
1128
|
+
};
|
|
1129
|
+
pack.renderedMarkdown = renderExecutionPackMarkdown(pack);
|
|
1130
|
+
return pack;
|
|
1131
|
+
}
|
|
1132
|
+
function buildPagePack(appNode, input, options = {}) {
|
|
1133
|
+
const pageNode = findPageNode(appNode, input.pageId);
|
|
1134
|
+
const route = summarizePageRoute(appNode, input.pageId);
|
|
1135
|
+
if (!pageNode || !route) {
|
|
1136
|
+
throw new Error(`Unknown page for page pack: ${input.pageId}`);
|
|
1137
|
+
}
|
|
1138
|
+
const patterns = collectPagePatterns(pageNode);
|
|
1139
|
+
const pack = {
|
|
1140
|
+
$schema: EXECUTION_PACK_SCHEMA_URLS.page,
|
|
1141
|
+
packVersion: "1.0.0",
|
|
1142
|
+
packType: "page",
|
|
1143
|
+
objective: options.objective ?? `Implement the ${input.pageId} route using the compiled page contract.`,
|
|
1144
|
+
target: {
|
|
1145
|
+
...DEFAULT_TARGET,
|
|
1146
|
+
...options.target
|
|
1147
|
+
},
|
|
1148
|
+
preset: options.preset ?? null,
|
|
1149
|
+
scope: {
|
|
1150
|
+
appId: appNode.id,
|
|
1151
|
+
pageIds: [route.pageId],
|
|
1152
|
+
patternIds: [...new Set(patterns.map((pattern) => pattern.id))]
|
|
1153
|
+
},
|
|
1154
|
+
requiredSetup: options.requiredSetup ?? [
|
|
1155
|
+
"Keep the compiled route and shell contract stable for this page.",
|
|
1156
|
+
"Treat the listed page patterns as the primary structure for this route."
|
|
1157
|
+
],
|
|
1158
|
+
allowedVocabulary: [...new Set([
|
|
1159
|
+
input.pageId,
|
|
1160
|
+
input.shell,
|
|
1161
|
+
input.sectionId ?? "",
|
|
1162
|
+
input.sectionRole ?? "",
|
|
1163
|
+
appNode.theme.id,
|
|
1164
|
+
appNode.theme.mode,
|
|
1165
|
+
...input.features,
|
|
1166
|
+
...patterns.flatMap((pattern) => [pattern.id, pattern.alias, pattern.layout])
|
|
1167
|
+
].filter(Boolean))],
|
|
1168
|
+
examples: options.examples ?? [],
|
|
1169
|
+
antiPatterns: options.antiPatterns ?? [],
|
|
1170
|
+
successChecks: options.successChecks ?? DEFAULT_PAGE_SUCCESS_CHECKS,
|
|
1171
|
+
tokenBudget: mergeTokenBudget(options.tokenBudget),
|
|
1172
|
+
data: {
|
|
1173
|
+
pageId: route.pageId,
|
|
1174
|
+
path: route.path,
|
|
1175
|
+
shell: input.shell,
|
|
1176
|
+
sectionId: input.sectionId,
|
|
1177
|
+
sectionRole: input.sectionRole,
|
|
1178
|
+
features: input.features,
|
|
1179
|
+
surface: pageNode.surface,
|
|
1180
|
+
theme: {
|
|
1181
|
+
id: appNode.theme.id,
|
|
1182
|
+
mode: appNode.theme.mode,
|
|
1183
|
+
shape: appNode.theme.shape
|
|
1184
|
+
},
|
|
1185
|
+
wiringSignals: pageNode.wiring?.signals.map((signal) => signal.name) ?? [],
|
|
1186
|
+
patterns
|
|
1187
|
+
},
|
|
1188
|
+
renderedMarkdown: ""
|
|
1189
|
+
};
|
|
1190
|
+
pack.renderedMarkdown = renderExecutionPackMarkdown(pack);
|
|
1191
|
+
return pack;
|
|
1192
|
+
}
|
|
1193
|
+
function buildMutationPack(appNode, options) {
|
|
1194
|
+
const routes = summarizeRoutes(appNode);
|
|
1195
|
+
const scopePatternIds = [...new Set(routes.flatMap((route) => route.patternIds))];
|
|
1196
|
+
const defaultWorkflow = options.mutationType === "add-page" ? [
|
|
1197
|
+
"Declare the new page in the essence before generating code.",
|
|
1198
|
+
"Refresh Decantr context so section and page packs include the new route.",
|
|
1199
|
+
"Read the relevant section pack and new page pack before implementation."
|
|
1200
|
+
] : [
|
|
1201
|
+
"Read the page pack for the route you are modifying first.",
|
|
1202
|
+
"Stop and update the essence before changing route, shell, or pattern contracts.",
|
|
1203
|
+
"Validate and check drift after code changes complete."
|
|
1204
|
+
];
|
|
1205
|
+
const pack = {
|
|
1206
|
+
$schema: EXECUTION_PACK_SCHEMA_URLS.mutation,
|
|
1207
|
+
packVersion: "1.0.0",
|
|
1208
|
+
packType: "mutation",
|
|
1209
|
+
objective: options.objective ?? `Execute the ${options.mutationType} workflow against the compiled app contract.`,
|
|
1210
|
+
target: {
|
|
1211
|
+
...DEFAULT_TARGET,
|
|
1212
|
+
...options.target
|
|
1213
|
+
},
|
|
1214
|
+
preset: options.preset ?? null,
|
|
1215
|
+
scope: {
|
|
1216
|
+
appId: appNode.id,
|
|
1217
|
+
pageIds: routes.map((route) => route.pageId),
|
|
1218
|
+
patternIds: scopePatternIds
|
|
1219
|
+
},
|
|
1220
|
+
requiredSetup: options.requiredSetup ?? [
|
|
1221
|
+
"Treat the compiled topology as the source of truth until the essence changes.",
|
|
1222
|
+
"Refresh Decantr context after structural mutations so downstream tasks read current packs."
|
|
1223
|
+
],
|
|
1224
|
+
allowedVocabulary: [.../* @__PURE__ */ new Set([
|
|
1225
|
+
options.mutationType,
|
|
1226
|
+
appNode.shell.config.type,
|
|
1227
|
+
appNode.theme.id,
|
|
1228
|
+
appNode.theme.mode,
|
|
1229
|
+
...appNode.features,
|
|
1230
|
+
...scopePatternIds
|
|
1231
|
+
])],
|
|
1232
|
+
examples: options.examples ?? [],
|
|
1233
|
+
antiPatterns: options.antiPatterns ?? [],
|
|
1234
|
+
successChecks: options.successChecks ?? DEFAULT_MUTATION_SUCCESS_CHECKS[options.mutationType],
|
|
1235
|
+
tokenBudget: mergeTokenBudget(options.tokenBudget),
|
|
1236
|
+
data: {
|
|
1237
|
+
mutationType: options.mutationType,
|
|
1238
|
+
shell: appNode.shell.config.type,
|
|
1239
|
+
theme: {
|
|
1240
|
+
id: appNode.theme.id,
|
|
1241
|
+
mode: appNode.theme.mode,
|
|
1242
|
+
shape: appNode.theme.shape
|
|
1243
|
+
},
|
|
1244
|
+
routing: appNode.routing,
|
|
1245
|
+
features: appNode.features,
|
|
1246
|
+
routes,
|
|
1247
|
+
workflow: options.workflow ?? defaultWorkflow
|
|
1248
|
+
},
|
|
1249
|
+
renderedMarkdown: ""
|
|
1250
|
+
};
|
|
1251
|
+
pack.renderedMarkdown = renderExecutionPackMarkdown(pack);
|
|
1252
|
+
return pack;
|
|
1253
|
+
}
|
|
1254
|
+
function buildReviewPack(appNode, options = {}) {
|
|
1255
|
+
const routes = summarizeRoutes(appNode);
|
|
1256
|
+
const scopePatternIds = [...new Set(routes.flatMap((route) => route.patternIds))];
|
|
1257
|
+
const reviewType = options.reviewType ?? "app";
|
|
1258
|
+
const focusAreas = options.focusAreas ?? [
|
|
1259
|
+
"route-topology",
|
|
1260
|
+
"theme-consistency",
|
|
1261
|
+
"treatment-usage",
|
|
1262
|
+
"accessibility",
|
|
1263
|
+
"responsive-design"
|
|
1264
|
+
];
|
|
1265
|
+
const workflow = options.workflow ?? [
|
|
1266
|
+
"Read the scaffold pack and page packs before evaluating generated code.",
|
|
1267
|
+
"Compare findings against the compiled route, shell, and theme contract first.",
|
|
1268
|
+
"Escalate contract drift into essence updates when the requested output intentionally changes topology or theme identity."
|
|
1269
|
+
];
|
|
1270
|
+
const pack = {
|
|
1271
|
+
$schema: EXECUTION_PACK_SCHEMA_URLS.review,
|
|
1272
|
+
packVersion: "1.0.0",
|
|
1273
|
+
packType: "review",
|
|
1274
|
+
objective: options.objective ?? "Review generated output against the compiled Decantr contract.",
|
|
1275
|
+
target: {
|
|
1276
|
+
...DEFAULT_TARGET,
|
|
1277
|
+
...options.target
|
|
1278
|
+
},
|
|
1279
|
+
preset: options.preset ?? null,
|
|
1280
|
+
scope: {
|
|
1281
|
+
appId: appNode.id,
|
|
1282
|
+
pageIds: routes.map((route) => route.pageId),
|
|
1283
|
+
patternIds: scopePatternIds
|
|
1284
|
+
},
|
|
1285
|
+
requiredSetup: options.requiredSetup ?? [
|
|
1286
|
+
"Read the compiled scaffold and route packs before reviewing code.",
|
|
1287
|
+
"Use concrete evidence from the workspace instead of purely stylistic intuition."
|
|
1288
|
+
],
|
|
1289
|
+
allowedVocabulary: [.../* @__PURE__ */ new Set([
|
|
1290
|
+
reviewType,
|
|
1291
|
+
appNode.shell.config.type,
|
|
1292
|
+
appNode.theme.id,
|
|
1293
|
+
appNode.theme.mode,
|
|
1294
|
+
...appNode.features,
|
|
1295
|
+
...scopePatternIds,
|
|
1296
|
+
...focusAreas
|
|
1297
|
+
])],
|
|
1298
|
+
examples: options.examples ?? [],
|
|
1299
|
+
antiPatterns: options.antiPatterns ?? DEFAULT_REVIEW_ANTI_PATTERNS,
|
|
1300
|
+
successChecks: options.successChecks ?? DEFAULT_REVIEW_SUCCESS_CHECKS,
|
|
1301
|
+
tokenBudget: mergeTokenBudget(options.tokenBudget),
|
|
1302
|
+
data: {
|
|
1303
|
+
reviewType,
|
|
1304
|
+
shell: appNode.shell.config.type,
|
|
1305
|
+
theme: {
|
|
1306
|
+
id: appNode.theme.id,
|
|
1307
|
+
mode: appNode.theme.mode,
|
|
1308
|
+
shape: appNode.theme.shape
|
|
1309
|
+
},
|
|
1310
|
+
routing: appNode.routing,
|
|
1311
|
+
features: appNode.features,
|
|
1312
|
+
routes,
|
|
1313
|
+
focusAreas,
|
|
1314
|
+
workflow
|
|
1315
|
+
},
|
|
1316
|
+
renderedMarkdown: ""
|
|
1317
|
+
};
|
|
1318
|
+
pack.renderedMarkdown = renderExecutionPackMarkdown(pack);
|
|
1319
|
+
return pack;
|
|
1320
|
+
}
|
|
1321
|
+
async function compileExecutionPackBundle(essence, options = {}) {
|
|
1322
|
+
const effectiveEssence = isV33(essence) ? essence : migrateV2ToV32(essence);
|
|
1323
|
+
const generatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
1324
|
+
const sharedTarget = {
|
|
1325
|
+
framework: effectiveEssence.meta.target || null,
|
|
1326
|
+
runtime: effectiveEssence.meta.platform.type || null,
|
|
1327
|
+
adapter: resolvePackAdapter(effectiveEssence.meta.target, effectiveEssence.meta.platform.type)
|
|
1328
|
+
};
|
|
1329
|
+
const pipeline = await runPipeline(essence, {
|
|
1330
|
+
contentRoot: options.contentRoot,
|
|
1331
|
+
overridePaths: options.overridePaths,
|
|
1332
|
+
resolver: options.resolver
|
|
1333
|
+
});
|
|
1334
|
+
const scaffold = buildScaffoldPack(pipeline.ir, {
|
|
1335
|
+
target: sharedTarget,
|
|
1336
|
+
navigation: {
|
|
1337
|
+
commandPalette: Boolean(effectiveEssence.meta.navigation?.command_palette),
|
|
1338
|
+
hotkeys: Array.isArray(effectiveEssence.meta.navigation?.hotkeys) ? effectiveEssence.meta.navigation.hotkeys.filter((hotkey) => Boolean(
|
|
1339
|
+
hotkey && typeof hotkey.key === "string" && typeof hotkey.label === "string"
|
|
1340
|
+
)).map((hotkey) => ({
|
|
1341
|
+
key: hotkey.key,
|
|
1342
|
+
route: hotkey.route,
|
|
1343
|
+
label: hotkey.label
|
|
1344
|
+
})) : []
|
|
1345
|
+
}
|
|
1346
|
+
});
|
|
1347
|
+
const review = buildReviewPack(pipeline.ir, {
|
|
1348
|
+
target: sharedTarget
|
|
1349
|
+
});
|
|
1350
|
+
const sections = listPackSections(effectiveEssence).map((section) => buildSectionPack(pipeline.ir, section, {
|
|
1351
|
+
target: sharedTarget
|
|
1352
|
+
}));
|
|
1353
|
+
const pages = listPackPages(effectiveEssence).map((page) => buildPagePack(pipeline.ir, page, {
|
|
1354
|
+
target: sharedTarget
|
|
1355
|
+
}));
|
|
1356
|
+
const mutations = ["add-page", "modify"].map((mutationType) => buildMutationPack(pipeline.ir, {
|
|
1357
|
+
mutationType,
|
|
1358
|
+
target: sharedTarget
|
|
1359
|
+
}));
|
|
1360
|
+
const manifest = {
|
|
1361
|
+
$schema: PACK_MANIFEST_SCHEMA_URL,
|
|
1362
|
+
version: "1.0.0",
|
|
1363
|
+
generatedAt,
|
|
1364
|
+
scaffold: {
|
|
1365
|
+
id: "scaffold",
|
|
1366
|
+
markdown: "scaffold-pack.md",
|
|
1367
|
+
json: "scaffold-pack.json"
|
|
1368
|
+
},
|
|
1369
|
+
review: {
|
|
1370
|
+
id: "review",
|
|
1371
|
+
markdown: "review-pack.md",
|
|
1372
|
+
json: "review-pack.json"
|
|
1373
|
+
},
|
|
1374
|
+
sections: listPackSections(effectiveEssence).map((section) => ({
|
|
1375
|
+
id: section.id,
|
|
1376
|
+
markdown: `section-${section.id}-pack.md`,
|
|
1377
|
+
json: `section-${section.id}-pack.json`,
|
|
1378
|
+
pageIds: section.pageIds
|
|
1379
|
+
})),
|
|
1380
|
+
pages: listPackPages(effectiveEssence).map((page) => ({
|
|
1381
|
+
id: page.pageId,
|
|
1382
|
+
markdown: `page-${page.pageId}-pack.md`,
|
|
1383
|
+
json: `page-${page.pageId}-pack.json`,
|
|
1384
|
+
sectionId: page.sectionId,
|
|
1385
|
+
sectionRole: page.sectionRole
|
|
1386
|
+
})),
|
|
1387
|
+
mutations: ["add-page", "modify"].map((mutationType) => ({
|
|
1388
|
+
id: mutationType,
|
|
1389
|
+
markdown: `mutation-${mutationType}-pack.md`,
|
|
1390
|
+
json: `mutation-${mutationType}-pack.json`,
|
|
1391
|
+
mutationType
|
|
1392
|
+
}))
|
|
1393
|
+
};
|
|
1394
|
+
return {
|
|
1395
|
+
$schema: EXECUTION_PACK_BUNDLE_SCHEMA_URL,
|
|
1396
|
+
generatedAt,
|
|
1397
|
+
sourceEssenceVersion: essence.version,
|
|
1398
|
+
manifest,
|
|
1399
|
+
scaffold,
|
|
1400
|
+
review,
|
|
1401
|
+
sections,
|
|
1402
|
+
pages,
|
|
1403
|
+
mutations
|
|
1404
|
+
};
|
|
1405
|
+
}
|
|
1406
|
+
function selectExecutionPackFromBundle(bundle, selector) {
|
|
1407
|
+
const id = selector.id ?? null;
|
|
1408
|
+
let pack = null;
|
|
1409
|
+
switch (selector.packType) {
|
|
1410
|
+
case "scaffold":
|
|
1411
|
+
pack = bundle.scaffold;
|
|
1412
|
+
break;
|
|
1413
|
+
case "review":
|
|
1414
|
+
pack = bundle.review;
|
|
1415
|
+
break;
|
|
1416
|
+
case "section":
|
|
1417
|
+
pack = id ? bundle.sections.find((entry) => entry.data.sectionId === id) ?? null : null;
|
|
1418
|
+
break;
|
|
1419
|
+
case "page":
|
|
1420
|
+
pack = id ? bundle.pages.find((entry) => entry.data.pageId === id) ?? null : null;
|
|
1421
|
+
break;
|
|
1422
|
+
case "mutation":
|
|
1423
|
+
pack = id ? bundle.mutations.find((entry) => entry.data.mutationType === id) ?? null : null;
|
|
1424
|
+
break;
|
|
1425
|
+
default:
|
|
1426
|
+
pack = null;
|
|
1427
|
+
break;
|
|
1428
|
+
}
|
|
1429
|
+
if (!pack) {
|
|
1430
|
+
return null;
|
|
1431
|
+
}
|
|
1432
|
+
return {
|
|
1433
|
+
$schema: SELECTED_EXECUTION_PACK_SCHEMA_URL,
|
|
1434
|
+
generatedAt: bundle.generatedAt,
|
|
1435
|
+
sourceEssenceVersion: bundle.sourceEssenceVersion,
|
|
1436
|
+
manifest: bundle.manifest,
|
|
1437
|
+
selector: {
|
|
1438
|
+
packType: selector.packType,
|
|
1439
|
+
id
|
|
1440
|
+
},
|
|
1441
|
+
pack
|
|
1442
|
+
};
|
|
1443
|
+
}
|
|
1444
|
+
async function compileSelectedExecutionPack(essence, selector, options = {}) {
|
|
1445
|
+
const bundle = await compileExecutionPackBundle(essence, options);
|
|
1446
|
+
return selectExecutionPackFromBundle(bundle, selector);
|
|
1447
|
+
}
|
|
558
1448
|
export {
|
|
1449
|
+
EXECUTION_PACK_BUNDLE_SCHEMA_URL,
|
|
1450
|
+
EXECUTION_PACK_SCHEMA_URLS,
|
|
1451
|
+
PACK_MANIFEST_SCHEMA_URL,
|
|
1452
|
+
SELECTED_EXECUTION_PACK_SCHEMA_URL,
|
|
1453
|
+
buildMutationPack,
|
|
559
1454
|
buildPageIR,
|
|
1455
|
+
buildPagePack,
|
|
1456
|
+
buildReviewPack,
|
|
1457
|
+
buildScaffoldPack,
|
|
1458
|
+
buildSectionPack,
|
|
1459
|
+
compileExecutionPackBundle,
|
|
1460
|
+
compileSelectedExecutionPack,
|
|
560
1461
|
countPatterns,
|
|
561
1462
|
findNodes,
|
|
1463
|
+
listPackPages,
|
|
1464
|
+
listPackSections,
|
|
562
1465
|
pascalCase,
|
|
1466
|
+
renderExecutionPackMarkdown,
|
|
563
1467
|
resolveEssence,
|
|
1468
|
+
resolvePackAdapter,
|
|
564
1469
|
resolveVisualEffects,
|
|
565
1470
|
runPipeline,
|
|
1471
|
+
selectExecutionPackFromBundle,
|
|
566
1472
|
validateIR,
|
|
567
1473
|
walkIR
|
|
568
1474
|
};
|