openuispec 0.1.40 → 0.1.41

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.
@@ -16,12 +16,14 @@ import { z } from "zod";
16
16
  import { readFileSync } from "node:fs";
17
17
  import { join, dirname } from "node:path";
18
18
  import { fileURLToPath } from "node:url";
19
- import { SUPPORTED_TARGETS } from "../drift/index.js";
19
+ import { SUPPORTED_TARGETS, findProjectDir, discoverSpecFiles } from "../drift/index.js";
20
20
  import { buildPrepareResult } from "../prepare/index.js";
21
21
  import { buildCheckResult } from "../check/index.js";
22
22
  import { buildStatusResult } from "../status/index.js";
23
23
  import { buildValidateResult } from "../schema/validate.js";
24
24
  import { loadTargetDrift } from "../drift/index.js";
25
+ import { readFileSync as fsReadFileSync } from "node:fs";
26
+ import { relative } from "node:path";
25
27
 
26
28
  // ── resolve project cwd ──────────────────────────────────────────────
27
29
 
@@ -68,12 +70,13 @@ Spec files (YAML) are the single source of truth for all UI across platforms.
68
70
 
69
71
  MANDATORY WORKFLOW for any UI-related request (screens, navigation, layout, tokens, flows, localization):
70
72
  1. BEFORE writing or modifying any UI code, call openuispec_prepare with the target platform.
71
- This returns the spec context, platform config, generation constraints, AND the full contents
72
- of every spec file (tokens, screens, flows, contracts, locales, platform overrides).
73
- Use the spec_contents field as the authoritative source do NOT paraphrase from memory.
74
- Cross-reference exact token values, contract must_handle lists, and locale keys from the inline content.
75
- 2. If the request requires spec changes, update the spec files FIRST, then call openuispec_check to validate.
76
- 3. Only then generate or update the platform UI code based on the prepare output.
73
+ This returns the spec context, platform config, generation constraints, and the list of spec files.
74
+ 2. Then call openuispec_read_specs to load the spec file contents you need for the task.
75
+ Pass specific paths for targeted work, or omit paths to load everything.
76
+ Use the returned contents as the AUTHORITATIVE source do NOT paraphrase from memory.
77
+ Cross-reference exact token values, contract must_handle lists, and locale keys from the content.
78
+ 3. If the request requires spec changes, update the spec files FIRST, then call openuispec_check to validate.
79
+ 4. Only then generate or update the platform UI code based on the spec contents.
77
80
 
78
81
  Skip these tools ONLY when the request is purely non-UI (API logic, database, infrastructure, etc.)
79
82
  or explicitly platform-specific polish that doesn't affect shared UI semantics.`,
@@ -85,7 +88,7 @@ or explicitly platform-specific polish that doesn't affect shared UI semantics.`
85
88
  server.registerTool(
86
89
  "openuispec_prepare",
87
90
  {
88
- description: "Build AI-ready work bundle for a target platform. REQUIRED before any UI code generation. Returns spec context, platform config, semantic changes, generation constraints, AND the full contents of every spec file inline. Use spec_contents as the authoritative source for tokens, screens, contracts, and locales — do not paraphrase from memory.",
91
+ description: "Build AI-ready work bundle for a target platform. REQUIRED before any UI code generation. Returns spec context, platform config, semantic changes, and generation constraints. Call openuispec_read_specs afterward to load the actual spec file contents you need for generation.",
89
92
  inputSchema: { target: targetSchema },
90
93
  },
91
94
  async ({ target }) => {
@@ -152,6 +155,42 @@ server.registerTool(
152
155
  }
153
156
  );
154
157
 
158
+ // ── tool: openuispec_read_specs ───────────────────────────────────────
159
+
160
+ server.registerTool(
161
+ "openuispec_read_specs",
162
+ {
163
+ description: "Read the full contents of spec files. Call after openuispec_prepare to load the actual YAML/JSON content. Pass specific file paths from the prepare output, or omit to read all spec files. Use these contents as the authoritative source — do NOT paraphrase from memory.",
164
+ inputSchema: {
165
+ paths: z
166
+ .array(z.string())
167
+ .optional()
168
+ .describe("Specific spec file paths to read (relative to spec root, e.g. 'screens/home.yaml'). If omitted, reads all spec files."),
169
+ },
170
+ },
171
+ async ({ paths }) => {
172
+ try {
173
+ const projectDir = findProjectDir(projectCwd);
174
+ const allFiles = discoverSpecFiles(projectDir);
175
+ const filesToRead = paths && paths.length > 0
176
+ ? allFiles.filter((f) => {
177
+ const rel = relative(projectDir, f);
178
+ return paths.some((p) => rel === p || rel.endsWith(p));
179
+ })
180
+ : allFiles;
181
+
182
+ const contents = filesToRead.map((f) => ({
183
+ path: relative(projectDir, f),
184
+ content: fsReadFileSync(f, "utf-8"),
185
+ }));
186
+
187
+ return toolResult(contents);
188
+ } catch (err) {
189
+ return toolError(err);
190
+ }
191
+ }
192
+ );
193
+
155
194
  // ── tool: openuispec_drift ───────────────────────────────────────────
156
195
 
157
196
  server.registerTool(
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openuispec",
3
- "version": "0.1.40",
3
+ "version": "0.1.41",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "description": "A semantic UI specification format for AI-native, platform-native app development",
package/prepare/index.ts CHANGED
@@ -144,7 +144,7 @@ export interface PrepareResult {
144
144
  explanation_note?: string;
145
145
  items: PrepareItem[];
146
146
  bootstrap?: PrepareBootstrapBundle;
147
- spec_contents: SpecFileContent[];
147
+ spec_contents?: SpecFileContent[];
148
148
  next_steps: string[];
149
149
  }
150
150
 
@@ -1057,7 +1057,7 @@ function printReport(result: PrepareResult): void {
1057
1057
  }
1058
1058
  }
1059
1059
 
1060
- function buildBootstrapPrepareResult(cwd: string, target: string): PrepareResult {
1060
+ function buildBootstrapPrepareResult(cwd: string, target: string, includeContents: boolean = false): PrepareResult {
1061
1061
  const projectDir = findProjectDir(cwd);
1062
1062
  const projectName = readProjectName(projectDir);
1063
1063
  const outputDir = resolveOutputDir(projectDir, projectName, target);
@@ -1124,7 +1124,7 @@ function buildBootstrapPrepareResult(cwd: string, target: string): PrepareResult
1124
1124
  changes_available: false,
1125
1125
  explanation_note: "No snapshot exists yet. This is a first-time generation bundle.",
1126
1126
  items: [],
1127
- spec_contents: readAllSpecContents(projectDir),
1127
+ ...(includeContents ? { spec_contents: readAllSpecContents(projectDir) } : {}),
1128
1128
  bootstrap: {
1129
1129
  output_exists: existsSync(outputDir),
1130
1130
  generation_ready: missingDecisions.length === 0 && backendContextReady && !pendingUserConfirmation,
@@ -1150,7 +1150,7 @@ function buildBootstrapPrepareResult(cwd: string, target: string): PrepareResult
1150
1150
  };
1151
1151
  }
1152
1152
 
1153
- function buildUpdatePrepareResult(cwd: string, target: string): PrepareResult {
1153
+ function buildUpdatePrepareResult(cwd: string, target: string, includeContents: boolean = false): PrepareResult {
1154
1154
  const { projectDir, projectName, result } = loadTargetDrift(cwd, target, false, true);
1155
1155
  const outputDir = resolveOutputDir(projectDir, projectName, target);
1156
1156
  const codeRoots = suggestCodeRoots(target, outputDir);
@@ -1195,20 +1195,20 @@ function buildUpdatePrepareResult(cwd: string, target: string): PrepareResult {
1195
1195
  changes_available: result.explanation?.available ?? false,
1196
1196
  explanation_note: result.explanation?.note,
1197
1197
  items,
1198
- spec_contents: readAllSpecContents(projectDir),
1198
+ ...(includeContents ? { spec_contents: readAllSpecContents(projectDir) } : {}),
1199
1199
  next_steps: nextSteps,
1200
1200
  };
1201
1201
  }
1202
1202
 
1203
- export function buildPrepareResult(target: string, cwd: string = process.cwd()): PrepareResult {
1203
+ export function buildPrepareResult(target: string, cwd: string = process.cwd(), includeContents: boolean = false): PrepareResult {
1204
1204
  const projectDir = findProjectDir(cwd);
1205
1205
  const projectName = readProjectName(projectDir);
1206
1206
  const outputDir = resolveOutputDir(projectDir, projectName, target);
1207
1207
  const statePath = join(outputDir, ".openuispec-state.json");
1208
1208
  if (!existsSync(statePath)) {
1209
- return buildBootstrapPrepareResult(cwd, target);
1209
+ return buildBootstrapPrepareResult(cwd, target, includeContents);
1210
1210
  }
1211
- return buildUpdatePrepareResult(cwd, target);
1211
+ return buildUpdatePrepareResult(cwd, target, includeContents);
1212
1212
  }
1213
1213
 
1214
1214
  export function runPrepare(argv: string[]): void {