ply-ui-cli 1.1.140 → 1.1.141
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 +3 -3
- package/dist/commands/init.js +37 -25
- package/dist/index.js +9 -4
- package/dist/vendor.js +50 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -26,7 +26,7 @@ The old npm name `base-ui-cli` is **deprecated** (name collision with [MUI Base
|
|
|
26
26
|
- **progress-button** (free) — button with a stable-width loading spinner: the label fades out, the spinner takes its place, and the button keeps its exact size and color while the async action runs.
|
|
27
27
|
- **shell** (Pro) — unified page/dashboard application shell with optional footer, push drawers, and mini rail.
|
|
28
28
|
- **AI agents** — copy-in install is cheaper in tokens than generating a widget. Walkthrough (simulation, not a lab benchmark): [rich text editor](https://ply-ui.com/learn/ai-agent-tokens-rich-text-editor/).
|
|
29
|
-
- **UI design skill** — `npx ply-ui-cli init` writes `.cursor/skills/ply-ui-design/` when
|
|
29
|
+
- **UI design skill** — `npx ply-ui-cli init` writes `.cursor/skills/ply-ui-design/` and `.agents/skills/ply-ui-design/` when those folders are missing. `npx ply-ui-cli init --refresh-editor-kit` replaces a kit that is already there. Walkthrough: [Why Ply installs a UI design skill when you run init](https://ply-ui.com/learn/ply-ui-design-skill/).
|
|
30
30
|
|
|
31
31
|
Full history: [ply-ui.com/changelog](https://ply-ui.com/changelog)
|
|
32
32
|
|
|
@@ -272,10 +272,10 @@ Run npx ply-ui-cli doctor. It checks ply-ui.json, the Angular workspace, @angula
|
|
|
272
272
|
Open Theme Studio at https://ply-ui.com/theme/ or the docs customizer, pick a preset or hue, then copy the Tailwind v4 @theme block into src/tailwind.css. ply-ui.css points --ply-primary and Tailwind blue-* utilities at that palette (--ply-brand and --ply-scale-*). The live preview shows buttons, tabs, forms, and overlays together.
|
|
273
273
|
|
|
274
274
|
**Can AI agents install Ply?**
|
|
275
|
-
Yes. npx -y ply-ui-mcp runs over stdio in Cursor, VS Code, Claude Code, Claude Desktop, Codex, Antigravity, Grok Build, Kimi Code, Qwen Code, Windsurf, and other local hosts. ChatGPT and Gemini chat apps use the Streamable HTTP connector at https://mcp.ply-ui.com/mcp (catalog tools) or a local npx -y ply-ui-mcp --http without --read-only so add/init/update can see the project.
|
|
275
|
+
Yes. npx -y ply-ui-mcp runs over stdio in Cursor, VS Code, Claude Code, Claude Desktop, Codex, Antigravity, Grok Build, Kimi Code, Qwen Code, Windsurf, and other local hosts. ChatGPT and Gemini chat apps use the Streamable HTTP connector at https://mcp.ply-ui.com/mcp (catalog tools) or a local npx -y ply-ui-mcp --http without --read-only so add/init/update can see the project. doctor, add_components, and update_components return structuredContent: checks, missing packages with the install command, and one result per file. Hosts that can ask confirm an overwrite before replace. init_project accepts refreshEditorKit. When PLY_LICENSE_KEY is already set, leave it in the environment and out of the tool call. Setup for each host: https://ply-ui.com/getting-started/#ai-agents-mcp. Editors that skip MCP can copy the Cursor rule and snippet pack from https://ply-ui.com/getting-started/#editor-rules.
|
|
276
276
|
|
|
277
277
|
**What does init install for AI agents?**
|
|
278
|
-
npx ply-ui-cli init writes ply-ui.json, ply-ui.css, icon sprites, .cursor/rules/ply-ui.mdc, .vscode/ply-ui.code-snippets, and the UI design skill at .cursor/skills/ply-ui-design/ (SKILL.md, reference.md, examples.md) when those files are missing. The skill tells the agent to reuse ply-button, ply-card, and ply-input-group, keep one accent, and include loading, error, empty, and data states. It does not install components. Published copy: https://ply-ui.com/docs/ai/skills/ply-ui-design/SKILL.md. Walkthrough: https://ply-ui.com/learn/ply-ui-design-skill/.
|
|
278
|
+
npx ply-ui-cli init writes ply-ui.json, ply-ui.css, icon sprites, .cursor/rules/ply-ui.mdc, .vscode/ply-ui.code-snippets, and the UI design skill at .cursor/skills/ply-ui-design/ and .agents/skills/ply-ui-design/ (SKILL.md, reference.md, examples.md) when those files are missing. npx ply-ui-cli init --refresh-editor-kit replaces a kit that is already there. The skill tells the agent to reuse ply-button, ply-card, and ply-input-group, keep one accent, and include loading, error, empty, and data states. It does not install components. Published copy: https://ply-ui.com/docs/ai/skills/ply-ui-design/SKILL.md. Walkthrough: https://ply-ui.com/learn/ply-ui-design-skill/.
|
|
279
279
|
|
|
280
280
|
**Do AI agents use fewer tokens with Ply?**
|
|
281
281
|
Usually, yes. With Ply the agent copies a finished component (CLI or MCP) and wires it, instead of generating a toolbar, selection model, sanitizer, and SSR guards from a blank file. Token use still depends on the prompt and the model. A reconstructed Claude/Grok session for the rich text editor is at https://ply-ui.com/learn/ai-agent-tokens-rich-text-editor/ — that is a teaching simulation, not a lab benchmark.
|
package/dist/commands/init.js
CHANGED
|
@@ -32,7 +32,8 @@ var init_exports = {};
|
|
|
32
32
|
__export(init_exports, {
|
|
33
33
|
discoverProjectLayout: () => discoverProjectLayout,
|
|
34
34
|
init: () => init,
|
|
35
|
-
insertComponentSource: () => insertComponentSource
|
|
35
|
+
insertComponentSource: () => insertComponentSource,
|
|
36
|
+
writeEditorKit: () => writeEditorKit
|
|
36
37
|
});
|
|
37
38
|
module.exports = __toCommonJS(init_exports);
|
|
38
39
|
var import_fs3 = __toESM(require("fs"));
|
|
@@ -55,6 +56,7 @@ var LEGACY_LOCKFILE_NAME = "base-ui-lock.json";
|
|
|
55
56
|
var CURSOR_RULE_REL = ".cursor/rules/ply-ui.mdc";
|
|
56
57
|
var SNIPPETS_REL = ".vscode/ply-ui.code-snippets";
|
|
57
58
|
var DESIGN_SKILL_DIR = ".cursor/skills/ply-ui-design";
|
|
59
|
+
var AGENTS_SKILL_DIR = ".agents/skills/ply-ui-design";
|
|
58
60
|
var DOCS_CURSOR_RULE = "docs/ai/ply-ui.mdc";
|
|
59
61
|
var DOCS_SNIPPETS = "docs/ai/ply-ui.code-snippets";
|
|
60
62
|
var DOCS_DESIGN_SKILL = "docs/ai/skills/ply-ui-design";
|
|
@@ -281,8 +283,8 @@ async function downloadSprite(name, targetDir) {
|
|
|
281
283
|
return false;
|
|
282
284
|
}
|
|
283
285
|
}
|
|
284
|
-
function writeEditorFile(dest, content, label, docsPath = DOCS_CURSOR_RULE) {
|
|
285
|
-
if (import_fs3.default.existsSync(dest)) return;
|
|
286
|
+
function writeEditorFile(dest, content, label, docsPath = DOCS_CURSOR_RULE, refresh = false) {
|
|
287
|
+
if (import_fs3.default.existsSync(dest) && !refresh) return;
|
|
286
288
|
try {
|
|
287
289
|
import_fs3.default.mkdirSync(import_path2.default.dirname(dest), { recursive: true });
|
|
288
290
|
import_fs3.default.writeFileSync(dest, content, "utf8");
|
|
@@ -293,6 +295,34 @@ function writeEditorFile(dest, content, label, docsPath = DOCS_CURSOR_RULE) {
|
|
|
293
295
|
);
|
|
294
296
|
}
|
|
295
297
|
}
|
|
298
|
+
function writeEditorKit(cwd, options = {}) {
|
|
299
|
+
const refresh = options.refresh === true;
|
|
300
|
+
writeEditorFile(
|
|
301
|
+
import_path2.default.resolve(cwd, CURSOR_RULE_REL),
|
|
302
|
+
CURSOR_RULE,
|
|
303
|
+
CURSOR_RULE_REL,
|
|
304
|
+
DOCS_CURSOR_RULE,
|
|
305
|
+
refresh
|
|
306
|
+
);
|
|
307
|
+
writeEditorFile(
|
|
308
|
+
import_path2.default.resolve(cwd, SNIPPETS_REL),
|
|
309
|
+
CODE_SNIPPETS,
|
|
310
|
+
SNIPPETS_REL,
|
|
311
|
+
DOCS_SNIPPETS,
|
|
312
|
+
refresh
|
|
313
|
+
);
|
|
314
|
+
for (const dir of [DESIGN_SKILL_DIR, AGENTS_SKILL_DIR]) {
|
|
315
|
+
for (const [name, content] of Object.entries(DESIGN_SKILL_FILES)) {
|
|
316
|
+
writeEditorFile(
|
|
317
|
+
import_path2.default.resolve(cwd, dir, name),
|
|
318
|
+
content,
|
|
319
|
+
`${dir}/${name}`,
|
|
320
|
+
`${DOCS_DESIGN_SKILL}/${name}`,
|
|
321
|
+
refresh
|
|
322
|
+
);
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
}
|
|
296
326
|
var INIT_DEFAULTS = {
|
|
297
327
|
componentsAlias: "src/app/components",
|
|
298
328
|
styles: "src/styles.scss",
|
|
@@ -519,32 +549,14 @@ ${stylesContent}`, "utf8");
|
|
|
519
549
|
for (const sprite of ICON_SPRITES) {
|
|
520
550
|
await downloadSprite(sprite, assetsDir);
|
|
521
551
|
}
|
|
522
|
-
spinner.text = "Writing editor rules, snippets, and the UI design skill...";
|
|
523
|
-
|
|
524
|
-
import_path2.default.resolve(process.cwd(), CURSOR_RULE_REL),
|
|
525
|
-
CURSOR_RULE,
|
|
526
|
-
CURSOR_RULE_REL,
|
|
527
|
-
DOCS_CURSOR_RULE
|
|
528
|
-
);
|
|
529
|
-
writeEditorFile(
|
|
530
|
-
import_path2.default.resolve(process.cwd(), SNIPPETS_REL),
|
|
531
|
-
CODE_SNIPPETS,
|
|
532
|
-
SNIPPETS_REL,
|
|
533
|
-
DOCS_SNIPPETS
|
|
534
|
-
);
|
|
535
|
-
for (const [name, content] of Object.entries(DESIGN_SKILL_FILES)) {
|
|
536
|
-
writeEditorFile(
|
|
537
|
-
import_path2.default.resolve(process.cwd(), DESIGN_SKILL_DIR, name),
|
|
538
|
-
content,
|
|
539
|
-
`${DESIGN_SKILL_DIR}/${name}`,
|
|
540
|
-
`${DOCS_DESIGN_SKILL}/${name}`
|
|
541
|
-
);
|
|
542
|
-
}
|
|
552
|
+
spinner.text = options.refreshEditorKit ? "Refreshing editor rules, snippets, and the UI design skill..." : "Writing editor rules, snippets, and the UI design skill...";
|
|
553
|
+
writeEditorKit(process.cwd(), { refresh: options.refreshEditorKit });
|
|
543
554
|
spinner.succeed(`Project initialized! Add your first component with "${CLI_NPX} add button".`);
|
|
544
555
|
}
|
|
545
556
|
// Annotate the CommonJS export names for ESM import in node:
|
|
546
557
|
0 && (module.exports = {
|
|
547
558
|
discoverProjectLayout,
|
|
548
559
|
init,
|
|
549
|
-
insertComponentSource
|
|
560
|
+
insertComponentSource,
|
|
561
|
+
writeEditorKit
|
|
550
562
|
});
|
package/dist/index.js
CHANGED
|
@@ -34,7 +34,7 @@ function printHelp() {
|
|
|
34
34
|
console.log(`Usage: ${CLI_BIN} <command> [options]
|
|
35
35
|
|
|
36
36
|
Commands:
|
|
37
|
-
init Initialize your project (ply-ui.json, styles, icons,
|
|
37
|
+
init Initialize your project (ply-ui.json, styles, icons, editor kit)
|
|
38
38
|
add [components...] Add components; omit names to pick from the catalog
|
|
39
39
|
list List all available components (free and pro)
|
|
40
40
|
diff [components...] Show upstream changes for installed components since you added them
|
|
@@ -48,6 +48,7 @@ Options:
|
|
|
48
48
|
-f, --force On conflict, take the upstream version (update)
|
|
49
49
|
--overwrite-local On update, also revert files you edited that upstream did not change
|
|
50
50
|
--merge On conflict, 3-way merge using the installed snapshot as ancestor
|
|
51
|
+
--refresh-editor-kit Replace the Cursor rule, snippets, and design skill (.cursor/skills and .agents/skills)
|
|
51
52
|
-V, --version Print version
|
|
52
53
|
-h, --help Show help
|
|
53
54
|
|
|
@@ -62,6 +63,7 @@ function parseArgs(argv) {
|
|
|
62
63
|
force: false,
|
|
63
64
|
overwriteLocal: false,
|
|
64
65
|
merge: false,
|
|
66
|
+
refreshEditorKit: false,
|
|
65
67
|
help: false,
|
|
66
68
|
version: false
|
|
67
69
|
};
|
|
@@ -86,6 +88,9 @@ function parseArgs(argv) {
|
|
|
86
88
|
case "--merge":
|
|
87
89
|
flags.merge = true;
|
|
88
90
|
break;
|
|
91
|
+
case "--refresh-editor-kit":
|
|
92
|
+
flags.refreshEditorKit = true;
|
|
93
|
+
break;
|
|
89
94
|
case "-h":
|
|
90
95
|
case "--help":
|
|
91
96
|
flags.help = true;
|
|
@@ -108,7 +113,7 @@ function parseArgs(argv) {
|
|
|
108
113
|
async function main() {
|
|
109
114
|
const { command, positionals, flags } = parseArgs(process.argv.slice(2));
|
|
110
115
|
if (flags.version && !command) {
|
|
111
|
-
console.log("1.1.
|
|
116
|
+
console.log("1.1.141");
|
|
112
117
|
return;
|
|
113
118
|
}
|
|
114
119
|
if (flags.help || !command) {
|
|
@@ -118,7 +123,7 @@ async function main() {
|
|
|
118
123
|
switch (command) {
|
|
119
124
|
case "init": {
|
|
120
125
|
const { init } = await import("./commands/init.js");
|
|
121
|
-
await init({ yes: flags.yes });
|
|
126
|
+
await init({ yes: flags.yes, refreshEditorKit: flags.refreshEditorKit });
|
|
122
127
|
return;
|
|
123
128
|
}
|
|
124
129
|
case "add": {
|
|
@@ -158,7 +163,7 @@ async function main() {
|
|
|
158
163
|
}
|
|
159
164
|
case "--version":
|
|
160
165
|
case "-V":
|
|
161
|
-
console.log("1.1.
|
|
166
|
+
console.log("1.1.141");
|
|
162
167
|
return;
|
|
163
168
|
default:
|
|
164
169
|
console.error(`Unknown command: ${command}`);
|
package/dist/vendor.js
CHANGED
|
@@ -13148,6 +13148,7 @@ __export(util_exports, {
|
|
|
13148
13148
|
required: () => required,
|
|
13149
13149
|
safeExtend: () => safeExtend,
|
|
13150
13150
|
shallowClone: () => shallowClone,
|
|
13151
|
+
sizeCaps: () => sizeCaps,
|
|
13151
13152
|
slugify: () => slugify,
|
|
13152
13153
|
stringifyPrimitive: () => stringifyPrimitive,
|
|
13153
13154
|
toZod: () => toZod,
|
|
@@ -13667,6 +13668,18 @@ function prefixIssues(path, issues) {
|
|
|
13667
13668
|
function unwrapMessage(message) {
|
|
13668
13669
|
return typeof message === "string" ? message : message?.message;
|
|
13669
13670
|
}
|
|
13671
|
+
function sizeCaps(defChecks) {
|
|
13672
|
+
const caps = [];
|
|
13673
|
+
for (const ch of defChecks ?? []) {
|
|
13674
|
+
const d = ch._zod.def;
|
|
13675
|
+
if (!/_(length|size)$|_equals$/.test(d.check))
|
|
13676
|
+
break;
|
|
13677
|
+
const max = d.maximum ?? d.length ?? d.size;
|
|
13678
|
+
if (max >= 0)
|
|
13679
|
+
caps.push([max, ch]);
|
|
13680
|
+
}
|
|
13681
|
+
return caps;
|
|
13682
|
+
}
|
|
13670
13683
|
function attachSchema(issues, start, inst) {
|
|
13671
13684
|
var _a3;
|
|
13672
13685
|
for (let i = start; i < issues.length; i++) {
|
|
@@ -15087,7 +15100,7 @@ ${content.join("\n")}
|
|
|
15087
15100
|
var version = {
|
|
15088
15101
|
major: 4,
|
|
15089
15102
|
minor: 6,
|
|
15090
|
-
patch:
|
|
15103
|
+
patch: 7
|
|
15091
15104
|
};
|
|
15092
15105
|
|
|
15093
15106
|
// node_modules/zod/v4/core/schemas.js
|
|
@@ -15843,6 +15856,31 @@ var $ZodDate = /* @__PURE__ */ $constructor("$ZodDate", (inst, def) => {
|
|
|
15843
15856
|
return payload;
|
|
15844
15857
|
};
|
|
15845
15858
|
});
|
|
15859
|
+
function capSize(inst, size) {
|
|
15860
|
+
const caps = sizeCaps(inst._zod.def.checks);
|
|
15861
|
+
if (!caps.length)
|
|
15862
|
+
return;
|
|
15863
|
+
const cap = Math.min(...caps.map((c) => c[0]));
|
|
15864
|
+
const parse3 = inst._zod.parse;
|
|
15865
|
+
inst._zod.parse = (payload, ctx) => {
|
|
15866
|
+
const n = size(payload.value);
|
|
15867
|
+
if (n > cap && !ctx?.skipChecks && ctx?.direction !== "backward") {
|
|
15868
|
+
for (const [max, ch] of sizeCaps(inst._zod.def.checks)) {
|
|
15869
|
+
if (n > max && ch._zod.def.when(payload)) {
|
|
15870
|
+
const start = payload.issues.length;
|
|
15871
|
+
ch._zod.check(payload);
|
|
15872
|
+
payload.issues[start].continue = false;
|
|
15873
|
+
attachSchema(payload.issues, start, inst);
|
|
15874
|
+
return payload;
|
|
15875
|
+
}
|
|
15876
|
+
}
|
|
15877
|
+
}
|
|
15878
|
+
return parse3(payload, ctx);
|
|
15879
|
+
};
|
|
15880
|
+
}
|
|
15881
|
+
var arraySize = (input2) => Array.isArray(input2) ? input2.length : -1;
|
|
15882
|
+
var mapSize = (input2) => input2 instanceof Map ? input2.size : -1;
|
|
15883
|
+
var setSize = (input2) => input2 instanceof Set ? input2.size : -1;
|
|
15846
15884
|
function handleArrayResult(result, final, index) {
|
|
15847
15885
|
if (result.issues.length) {
|
|
15848
15886
|
final.issues.push(...prefixIssues(index, result.issues));
|
|
@@ -15886,6 +15924,7 @@ var $ZodArray = /* @__PURE__ */ $constructor("$ZodArray", (inst, def) => {
|
|
|
15886
15924
|
}
|
|
15887
15925
|
return payload;
|
|
15888
15926
|
};
|
|
15927
|
+
capSize(inst, arraySize);
|
|
15889
15928
|
});
|
|
15890
15929
|
function handlePropertyResult(result, final, key, input2, optin, optout) {
|
|
15891
15930
|
const isPresent = key in input2;
|
|
@@ -16842,6 +16881,7 @@ var $ZodMap = /* @__PURE__ */ $constructor("$ZodMap", (inst, def) => {
|
|
|
16842
16881
|
return Promise.all(proms).then(() => payload);
|
|
16843
16882
|
return payload;
|
|
16844
16883
|
};
|
|
16884
|
+
capSize(inst, mapSize);
|
|
16845
16885
|
});
|
|
16846
16886
|
function handleMapResult(keyResult, valueResult, final, key, input2, inst, ctx) {
|
|
16847
16887
|
if (keyResult.issues.length) {
|
|
@@ -16908,6 +16948,7 @@ var $ZodSet = /* @__PURE__ */ $constructor("$ZodSet", (inst, def) => {
|
|
|
16908
16948
|
return Promise.all(proms).then(() => payload);
|
|
16909
16949
|
return payload;
|
|
16910
16950
|
};
|
|
16951
|
+
capSize(inst, setSize);
|
|
16911
16952
|
});
|
|
16912
16953
|
function handleSetResult(result, final) {
|
|
16913
16954
|
if (result.issues.length) {
|
|
@@ -26438,9 +26479,15 @@ function generateNullableCheck(doc, ctx, schema, accessor, buildsValue = true) {
|
|
|
26438
26479
|
doc.write(`}`);
|
|
26439
26480
|
return outputVar;
|
|
26440
26481
|
}
|
|
26482
|
+
function generateSizeGate(doc, schema, size) {
|
|
26483
|
+
for (const [max, check2] of sizeCaps(schema._zod.def.checks)) {
|
|
26484
|
+
doc.write(`if (${size} > ${numericOperand(max, check2._zod.def.check)}) return INVALID;`);
|
|
26485
|
+
}
|
|
26486
|
+
}
|
|
26441
26487
|
function generateArrayCheck(doc, ctx, schema, accessor, buildsValue = true) {
|
|
26442
26488
|
const def = schema._zod.def;
|
|
26443
26489
|
doc.write(`if (!Array.isArray(${accessor})) return INVALID;`);
|
|
26490
|
+
generateSizeGate(doc, schema, `${accessor}.length`);
|
|
26444
26491
|
const outputVar = buildsValue ? newVar(ctx) : null;
|
|
26445
26492
|
const iVar = newVar(ctx);
|
|
26446
26493
|
const elemVar = newVar(ctx);
|
|
@@ -26840,6 +26887,7 @@ function literalPropertyKey(ctx, key) {
|
|
|
26840
26887
|
function generateMapCheck(doc, ctx, schema, accessor) {
|
|
26841
26888
|
const def = schema._zod.def;
|
|
26842
26889
|
doc.write(`if (!(${accessor} instanceof Map)) return INVALID;`);
|
|
26890
|
+
generateSizeGate(doc, schema, `${accessor}.size`);
|
|
26843
26891
|
const outputVar = newVar(ctx);
|
|
26844
26892
|
const kVar = newVar(ctx);
|
|
26845
26893
|
const valVar = newVar(ctx);
|
|
@@ -26856,6 +26904,7 @@ function generateMapCheck(doc, ctx, schema, accessor) {
|
|
|
26856
26904
|
function generateSetCheck(doc, ctx, schema, accessor) {
|
|
26857
26905
|
const def = schema._zod.def;
|
|
26858
26906
|
doc.write(`if (!(${accessor} instanceof Set)) return INVALID;`);
|
|
26907
|
+
generateSizeGate(doc, schema, `${accessor}.size`);
|
|
26859
26908
|
const outputVar = newVar(ctx);
|
|
26860
26909
|
const valVar = newVar(ctx);
|
|
26861
26910
|
doc.write(`const ${outputVar} = new Set();`);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ply-ui-cli",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.141",
|
|
4
4
|
"description": "Ply CLI — add Angular + Tailwind CSS components to your project (shadcn-style, code is copied in and owned by you). Not MUI Base UI.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"publishConfig": {
|