openuispec 0.1.38 → 0.1.40
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +7 -0
- package/cli/init.ts +34 -2
- package/mcp-server/index.ts +5 -2
- package/package.json +1 -1
- package/prepare/index.ts +20 -0
package/README.md
CHANGED
|
@@ -265,6 +265,13 @@ OpenUISpec includes an MCP (Model Context Protocol) server that exposes CLI comm
|
|
|
265
265
|
}
|
|
266
266
|
```
|
|
267
267
|
|
|
268
|
+
**Codex** (`.codex/config.toml`):
|
|
269
|
+
```toml
|
|
270
|
+
[mcp_servers.openuispec]
|
|
271
|
+
command = "openuispec"
|
|
272
|
+
args = ["mcp"]
|
|
273
|
+
```
|
|
274
|
+
|
|
268
275
|
Or run directly: `openuispec mcp`
|
|
269
276
|
|
|
270
277
|
Set `OPENUISPEC_PROJECT_DIR` to override the working directory.
|
package/cli/init.ts
CHANGED
|
@@ -489,13 +489,42 @@ const EXPECTED_MCP_CONFIG = {
|
|
|
489
489
|
*
|
|
490
490
|
* All use the same { mcpServers: { openuispec: { command, args } } } shape.
|
|
491
491
|
*/
|
|
492
|
-
const
|
|
492
|
+
const JSON_MCP_PATHS = [
|
|
493
493
|
".mcp.json",
|
|
494
494
|
join(".vscode", "mcp.json"),
|
|
495
495
|
];
|
|
496
496
|
|
|
497
|
+
/** Codex uses TOML: .codex/config.toml */
|
|
498
|
+
const CODEX_CONFIG_PATH = join(".codex", "config.toml");
|
|
499
|
+
const CODEX_MCP_BLOCK = `\n[mcp_servers.openuispec]\ncommand = "openuispec"\nargs = ["mcp"]\n`;
|
|
500
|
+
|
|
501
|
+
function configureCodexMcp(cwd: string, quiet: boolean): void {
|
|
502
|
+
const codexDir = join(cwd, ".codex");
|
|
503
|
+
if (!existsSync(codexDir)) return;
|
|
504
|
+
|
|
505
|
+
const configPath = join(codexDir, "config.toml");
|
|
506
|
+
try {
|
|
507
|
+
let content = "";
|
|
508
|
+
try {
|
|
509
|
+
content = readFileSync(configPath, "utf-8");
|
|
510
|
+
} catch {
|
|
511
|
+
// file doesn't exist — will create
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
if (content.includes("[mcp_servers.openuispec]")) {
|
|
515
|
+
if (!quiet) console.log(` skip ${CODEX_CONFIG_PATH} (openuispec MCP already configured)`);
|
|
516
|
+
return;
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
writeFileSync(configPath, content + CODEX_MCP_BLOCK);
|
|
520
|
+
if (!quiet) console.log(` ${content ? "update" : "create"} ${CODEX_CONFIG_PATH} (MCP server configured)`);
|
|
521
|
+
} catch {
|
|
522
|
+
if (!quiet) console.log(` skip ${CODEX_CONFIG_PATH} (could not configure MCP server)`);
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
|
|
497
526
|
function configureMcp(cwd: string, showRestart: boolean, quiet: boolean = false): void {
|
|
498
|
-
for (const relPath of
|
|
527
|
+
for (const relPath of JSON_MCP_PATHS) {
|
|
499
528
|
const configPath = join(cwd, relPath);
|
|
500
529
|
|
|
501
530
|
// .vscode/mcp.json: only write if .vscode/ already exists
|
|
@@ -529,6 +558,9 @@ function configureMcp(cwd: string, showRestart: boolean, quiet: boolean = false)
|
|
|
529
558
|
}
|
|
530
559
|
}
|
|
531
560
|
|
|
561
|
+
// Codex: .codex/config.toml (TOML format)
|
|
562
|
+
configureCodexMcp(cwd, quiet);
|
|
563
|
+
|
|
532
564
|
if (showRestart) console.log(`\n Restart your AI coding agent to activate the MCP server.`);
|
|
533
565
|
|
|
534
566
|
// Clean up stale .claude.json MCP config from older versions
|
package/mcp-server/index.ts
CHANGED
|
@@ -68,7 +68,10 @@ Spec files (YAML) are the single source of truth for all UI across platforms.
|
|
|
68
68
|
|
|
69
69
|
MANDATORY WORKFLOW for any UI-related request (screens, navigation, layout, tokens, flows, localization):
|
|
70
70
|
1. BEFORE writing or modifying any UI code, call openuispec_prepare with the target platform.
|
|
71
|
-
This returns the spec context, platform config,
|
|
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.
|
|
72
75
|
2. If the request requires spec changes, update the spec files FIRST, then call openuispec_check to validate.
|
|
73
76
|
3. Only then generate or update the platform UI code based on the prepare output.
|
|
74
77
|
|
|
@@ -82,7 +85,7 @@ or explicitly platform-specific polish that doesn't affect shared UI semantics.`
|
|
|
82
85
|
server.registerTool(
|
|
83
86
|
"openuispec_prepare",
|
|
84
87
|
{
|
|
85
|
-
description: "Build AI-ready work bundle for a target platform. REQUIRED before any UI code generation. Returns spec context, platform config, semantic changes,
|
|
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.",
|
|
86
89
|
inputSchema: { target: targetSchema },
|
|
87
90
|
},
|
|
88
91
|
async ({ target }) => {
|
package/package.json
CHANGED
package/prepare/index.ts
CHANGED
|
@@ -116,6 +116,12 @@ interface PrepareBootstrapBundle {
|
|
|
116
116
|
reference_examples: string[];
|
|
117
117
|
}
|
|
118
118
|
|
|
119
|
+
interface SpecFileContent {
|
|
120
|
+
path: string;
|
|
121
|
+
category: string;
|
|
122
|
+
content: string;
|
|
123
|
+
}
|
|
124
|
+
|
|
119
125
|
export interface PrepareResult {
|
|
120
126
|
mode: "bootstrap" | "update";
|
|
121
127
|
project: string;
|
|
@@ -138,6 +144,7 @@ export interface PrepareResult {
|
|
|
138
144
|
explanation_note?: string;
|
|
139
145
|
items: PrepareItem[];
|
|
140
146
|
bootstrap?: PrepareBootstrapBundle;
|
|
147
|
+
spec_contents: SpecFileContent[];
|
|
141
148
|
next_steps: string[];
|
|
142
149
|
}
|
|
143
150
|
|
|
@@ -842,6 +849,17 @@ function generationWarnings(target: string, platformConfig: PreparePlatformConfi
|
|
|
842
849
|
return warnings;
|
|
843
850
|
}
|
|
844
851
|
|
|
852
|
+
function readAllSpecContents(projectDir: string): SpecFileContent[] {
|
|
853
|
+
return discoverSpecFiles(projectDir).map((filePath) => {
|
|
854
|
+
const relPath = relative(projectDir, filePath);
|
|
855
|
+
return {
|
|
856
|
+
path: relPath,
|
|
857
|
+
category: categorizeSpecFile(relPath),
|
|
858
|
+
content: readFileSync(filePath, "utf-8"),
|
|
859
|
+
};
|
|
860
|
+
});
|
|
861
|
+
}
|
|
862
|
+
|
|
845
863
|
function bootstrapSpecFiles(projectDir: string, target: string): BootstrapSpecFile[] {
|
|
846
864
|
return discoverSpecFiles(projectDir)
|
|
847
865
|
.map((filePath) => {
|
|
@@ -1106,6 +1124,7 @@ function buildBootstrapPrepareResult(cwd: string, target: string): PrepareResult
|
|
|
1106
1124
|
changes_available: false,
|
|
1107
1125
|
explanation_note: "No snapshot exists yet. This is a first-time generation bundle.",
|
|
1108
1126
|
items: [],
|
|
1127
|
+
spec_contents: readAllSpecContents(projectDir),
|
|
1109
1128
|
bootstrap: {
|
|
1110
1129
|
output_exists: existsSync(outputDir),
|
|
1111
1130
|
generation_ready: missingDecisions.length === 0 && backendContextReady && !pendingUserConfirmation,
|
|
@@ -1176,6 +1195,7 @@ function buildUpdatePrepareResult(cwd: string, target: string): PrepareResult {
|
|
|
1176
1195
|
changes_available: result.explanation?.available ?? false,
|
|
1177
1196
|
explanation_note: result.explanation?.note,
|
|
1178
1197
|
items,
|
|
1198
|
+
spec_contents: readAllSpecContents(projectDir),
|
|
1179
1199
|
next_steps: nextSteps,
|
|
1180
1200
|
};
|
|
1181
1201
|
}
|