openuispec 0.1.32 → 0.1.34
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/cli/init.ts +11 -5
- package/package.json +1 -1
- package/prepare/index.ts +3 -4
- package/schema/semantic-lint.ts +20 -18
package/cli/init.ts
CHANGED
|
@@ -347,7 +347,9 @@ This means the project has existing UI code but hasn't been specced yet. Your jo
|
|
|
347
347
|
type: scroll_vertical
|
|
348
348
|
\`\`\`
|
|
349
349
|
4. **Extract tokens** — scan for colors, fonts, spacing and create files in \`${specDir}/tokens/\`.
|
|
350
|
-
5. **
|
|
350
|
+
5. **Create contract extensions** — define visual variants for the 7 built-in contracts (action_trigger, data_display, input_field, collection, nav_container, feedback, surface) in \`${specDir}/contracts/\`. These encode the design system's visual identity (shapes, token overrides, platform mappings). Read \`schema/contract.schema.json\` and examples in the package for the format.
|
|
351
|
+
6. **Create locale files** — for each locale in \`i18n.supported_locales\`, create \`${specDir}/locales/<locale>.json\` with all \`$t:\` keys used in screens and flows.
|
|
352
|
+
7. **Update the manifest** — fill in \`data_model\`, \`api.endpoints\` in \`${specDir}/openuispec.yaml\`.
|
|
351
353
|
|
|
352
354
|
## OpenUISpec Source Of Truth
|
|
353
355
|
|
|
@@ -762,10 +764,14 @@ Done! Your spec project is ready at ./${answers.specDir}/
|
|
|
762
764
|
|
|
763
765
|
Getting started (new project):
|
|
764
766
|
1. Edit ${answers.specDir}/openuispec.yaml — define your data model and API
|
|
765
|
-
2. Create
|
|
766
|
-
3. Create
|
|
767
|
-
4.
|
|
768
|
-
5.
|
|
767
|
+
2. Create tokens in ${answers.specDir}/tokens/ (colors, typography, spacing, etc.)
|
|
768
|
+
3. Create contract extensions in ${answers.specDir}/contracts/ (visual variants for the 7 built-in contracts)
|
|
769
|
+
4. Create screens in ${answers.specDir}/screens/ (one YAML per screen)
|
|
770
|
+
5. Create flows in ${answers.specDir}/flows/ (multi-step navigation)
|
|
771
|
+
6. Create locale files in ${answers.specDir}/locales/ (one JSON per supported locale)
|
|
772
|
+
7. Run \`openuispec validate\` and \`openuispec validate semantic\` to check everything
|
|
773
|
+
8. Ask AI to generate native code from the spec
|
|
774
|
+
9. Run \`openuispec drift --snapshot --target ${answers.targets[0]}\` to baseline the first accepted target state after that target output directory exists
|
|
769
775
|
|
|
770
776
|
Getting started (existing project):
|
|
771
777
|
1. Ask AI to read your existing UI code and generate spec files:
|
package/package.json
CHANGED
package/prepare/index.ts
CHANGED
|
@@ -1050,14 +1050,13 @@ function buildBootstrapPrepareResult(cwd: string, target: string): PrepareResult
|
|
|
1050
1050
|
const codeRoots = suggestCodeRoots(target, outputDir);
|
|
1051
1051
|
const missingDecisions = missingPlatformDecisions(target, platformDef);
|
|
1052
1052
|
const backendRoot = resolveBackendRoot(projectDir, manifest);
|
|
1053
|
-
const
|
|
1054
|
-
const backendContextReady = !backendContextRequired || (backendRoot !== null && existsSync(backendRoot));
|
|
1053
|
+
const backendContextReady = true; // backend is optional — not a generation blocker
|
|
1055
1054
|
const pendingUserConfirmation = platformConfig.stack_confirmation.requires_user_confirmation;
|
|
1056
1055
|
|
|
1057
1056
|
const nextSteps = [
|
|
1058
|
-
...(!
|
|
1057
|
+
...(hasApiEndpoints(manifest) && (backendRoot === null || !existsSync(backendRoot))
|
|
1059
1058
|
? [
|
|
1060
|
-
"
|
|
1059
|
+
"Optional: set `generation.code_roots.backend` in openuispec.yaml to help AI locate your backend code.",
|
|
1061
1060
|
]
|
|
1062
1061
|
: []),
|
|
1063
1062
|
...(pendingUserConfirmation
|
package/schema/semantic-lint.ts
CHANGED
|
@@ -17,6 +17,7 @@ export interface Includes {
|
|
|
17
17
|
export interface UsageLint {
|
|
18
18
|
path: string;
|
|
19
19
|
message: string;
|
|
20
|
+
severity?: "error" | "warning";
|
|
20
21
|
}
|
|
21
22
|
|
|
22
23
|
interface SemanticContext {
|
|
@@ -545,27 +546,18 @@ function lintLocaleCoverage(context: SemanticContext): UsageLint[] {
|
|
|
545
546
|
function lintManifestGenerationContext(projectDir: string, manifest: unknown): UsageLint[] {
|
|
546
547
|
if (!isRecord(manifest)) return [];
|
|
547
548
|
|
|
548
|
-
|
|
549
|
-
isRecord(manifest.api) &&
|
|
550
|
-
isRecord(manifest.api.endpoints) &&
|
|
551
|
-
Object.keys(manifest.api.endpoints).length > 0;
|
|
552
|
-
if (!hasApiEndpoints) return [];
|
|
553
|
-
|
|
549
|
+
// code_roots.backend is optional — it's a hint for AI generation, not a hard requirement
|
|
554
550
|
const generation = isRecord(manifest.generation) ? manifest.generation : {};
|
|
555
551
|
const codeRoots = isRecord(generation.code_roots) ? generation.code_roots : null;
|
|
556
552
|
const backendRoot = codeRoots && typeof codeRoots.backend === "string" ? codeRoots.backend.trim() : "";
|
|
557
553
|
|
|
558
|
-
if (!backendRoot)
|
|
559
|
-
return [{
|
|
560
|
-
path: "openuispec.yaml",
|
|
561
|
-
message: 'api endpoints require generation.code_roots.backend to point at the backend folder',
|
|
562
|
-
}];
|
|
563
|
-
}
|
|
554
|
+
if (!backendRoot) return [];
|
|
564
555
|
|
|
565
556
|
const resolvedBackendRoot = resolve(projectDir, backendRoot);
|
|
566
557
|
if (!existsSync(resolvedBackendRoot)) {
|
|
567
558
|
return [{
|
|
568
559
|
path: "openuispec.yaml",
|
|
560
|
+
severity: "warning",
|
|
569
561
|
message: `generation.code_roots.backend points to a missing folder: ${backendRoot}`,
|
|
570
562
|
}];
|
|
571
563
|
}
|
|
@@ -577,14 +569,24 @@ function printSemanticErrors(label: string, errors: UsageLint[]): number {
|
|
|
577
569
|
if (errors.length === 0) return 0;
|
|
578
570
|
const previewLimit = 10;
|
|
579
571
|
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
572
|
+
const realErrors = errors.filter((e) => e.severity !== "warning");
|
|
573
|
+
const warnings = errors.filter((e) => e.severity === "warning");
|
|
574
|
+
|
|
575
|
+
if (realErrors.length > 0) {
|
|
576
|
+
console.log(` FAIL ${label} (${realErrors.length} semantic error(s))`);
|
|
577
|
+
for (const error of realErrors.slice(0, previewLimit)) {
|
|
578
|
+
console.log(` [${error.path}] ${error.message}`);
|
|
579
|
+
}
|
|
580
|
+
if (realErrors.length > previewLimit) {
|
|
581
|
+
console.log(` ... and ${realErrors.length - previewLimit} more`);
|
|
582
|
+
}
|
|
583
583
|
}
|
|
584
|
-
|
|
585
|
-
|
|
584
|
+
|
|
585
|
+
for (const warning of warnings) {
|
|
586
|
+
console.log(` WARN ${label}: ${warning.message}`);
|
|
586
587
|
}
|
|
587
|
-
|
|
588
|
+
|
|
589
|
+
return realErrors.length;
|
|
588
590
|
}
|
|
589
591
|
|
|
590
592
|
export function collectSemanticLint(projectDir: string, includes: Includes): UsageLint[] {
|