@montytools/cli 0.5.5 → 0.5.6
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/bin/monty.mjs +289 -324
- package/lib/schemaCodegen.mjs +7 -3
- package/lib/schemaPull.mjs +36 -50
- package/lib/styleLint.mjs +175 -0
- package/lib/tokens-manifest.mjs +90 -0
- package/package.json +2 -2
- package/skills/monty-build/SKILL.md +7 -5
- package/skills/monty-design/SKILL.md +104 -55
- package/template/AGENTS.md +47 -28
- package/template/index.html +5 -5
- package/template/package.json +2 -2
- package/template/src/components/ui/badge.tsx +1 -1
- package/template/src/components/ui/button.tsx +7 -7
- package/template/src/components/ui/card.tsx +3 -3
- package/template/src/components/ui/empty.tsx +3 -3
- package/template/src/components/ui/field.tsx +5 -5
- package/template/src/components/ui/input.tsx +1 -1
- package/template/src/components/ui/label.tsx +1 -1
- package/template/src/components/ui/select.tsx +4 -4
- package/template/src/components/ui/table.tsx +3 -3
- package/template/src/index.css +7 -118
- package/template/src/main.tsx +1 -1
- package/template/src/monty.gen.ts +16 -0
- package/template/src/routes/index.tsx +1 -1
- package/template/tsconfig.json +1 -1
- package/template/monty.config.ts +0 -21
package/lib/schemaCodegen.mjs
CHANGED
|
@@ -220,9 +220,13 @@ export function manifestToConfig(manifest, { name, icon } = {}) {
|
|
|
220
220
|
...sdkImports.map((n) => ` ${n},`),
|
|
221
221
|
`} from "@montytools/sdk";`,
|
|
222
222
|
``,
|
|
223
|
-
`//
|
|
224
|
-
`//
|
|
225
|
-
`//
|
|
223
|
+
`// GENERATED by Monty from the app's config stored in the workspace.`,
|
|
224
|
+
`// DO NOT EDIT: edits here never land anywhere, and this file is`,
|
|
225
|
+
`// overwritten on every sync (a running \`monty dev\` regenerates it within`,
|
|
226
|
+
`// a heartbeat of a remote change; \`monty save\` and \`monty schema pull\``,
|
|
227
|
+
`// refresh it too). Change the app through the doors instead:`,
|
|
228
|
+
`// \`monty schema set\`, the MCP schema_update tool, or Configuration.`,
|
|
229
|
+
`// Import { app } from it for typed SDK hooks — that part is yours.`,
|
|
226
230
|
`export const app = defineApp({`,
|
|
227
231
|
` slug: ${JSON.stringify(manifest.slug)},`,
|
|
228
232
|
...(name ? [` name: ${JSON.stringify(name)},`] : []),
|
package/lib/schemaPull.mjs
CHANGED
|
@@ -1,16 +1,17 @@
|
|
|
1
|
-
// `monty schema pull` — regenerate monty.
|
|
2
|
-
//
|
|
3
|
-
//
|
|
4
|
-
// Distinct from `monty pull`,
|
|
1
|
+
// `monty schema pull` — regenerate src/monty.gen.ts from the app's stored
|
|
2
|
+
// manifest (the schema-as-data flow: the workspace owns the config; this
|
|
3
|
+
// file is its generated, never-hand-edited mirror, giving app code typed
|
|
4
|
+
// SDK hooks and the runtime zod schemas). Distinct from `monty pull`,
|
|
5
|
+
// which restores the whole SOURCE SNAPSHOT.
|
|
5
6
|
//
|
|
6
|
-
//
|
|
7
|
-
//
|
|
8
|
-
//
|
|
9
|
-
//
|
|
7
|
+
// No dirty check, no backup: the gen module is not an editor — nothing in
|
|
8
|
+
// it can be "local changes" — so regeneration is a plain overwrite. The
|
|
9
|
+
// same call runs from the dev-session heartbeat (a remote schema edit
|
|
10
|
+
// lands here within ~30s) and from `monty save`/`monty connect`.
|
|
10
11
|
|
|
11
|
-
import { existsSync, mkdirSync, readFileSync,
|
|
12
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
12
13
|
import { join } from "node:path";
|
|
13
|
-
import {
|
|
14
|
+
import { manifestToConfig } from "./schemaCodegen.mjs";
|
|
14
15
|
|
|
15
16
|
const STATE_FILE = ["schema.json"]; // .monty/schema.json
|
|
16
17
|
|
|
@@ -31,7 +32,20 @@ export function writeSchemaState(appDir, hash) {
|
|
|
31
32
|
writeFileSync(join(dir, ...STATE_FILE), JSON.stringify({ hash, syncedAt: Date.now() }) + "\n");
|
|
32
33
|
}
|
|
33
34
|
|
|
34
|
-
|
|
35
|
+
/** Write src/monty.gen.ts from a manifest. Only apps with code get the
|
|
36
|
+
* module (a config-only folder has nothing that could import it). Returns
|
|
37
|
+
* whether a file was written. */
|
|
38
|
+
export function writeGenModule(appDir, manifest, { name, icon } = {}) {
|
|
39
|
+
const srcDir = join(appDir, "src");
|
|
40
|
+
if (!existsSync(srcDir)) return false;
|
|
41
|
+
writeFileSync(
|
|
42
|
+
join(srcDir, "monty.gen.ts"),
|
|
43
|
+
manifestToConfig(manifest, { name, icon: icon ?? undefined }),
|
|
44
|
+
);
|
|
45
|
+
return true;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export async function schemaPull({ appDir, host, key, slug, quiet, fail }) {
|
|
35
49
|
const res = await fetch(`${host}/api/schema?slug=${encodeURIComponent(slug)}`, {
|
|
36
50
|
headers: { authorization: `Bearer ${key}` },
|
|
37
51
|
});
|
|
@@ -39,45 +53,17 @@ export async function schemaPull({ appDir, host, key, slug, force, compileAppCon
|
|
|
39
53
|
if (!res.ok || !body?.ok) {
|
|
40
54
|
fail(body?.code ?? `HTTP_${res.status}`, body?.fix ?? "Could not fetch the app's schema — check the connection and `monty login`.");
|
|
41
55
|
}
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
56
|
+
// An empty registry config is still a config — generate from it.
|
|
57
|
+
const manifest = body.manifest ?? { slug, tables: {} };
|
|
58
|
+
const wrote = writeGenModule(appDir, manifest, { name: body.name, icon: body.icon });
|
|
59
|
+
if (body.hash) writeSchemaState(appDir, body.hash);
|
|
60
|
+
if (!quiet) {
|
|
61
|
+
console.log(
|
|
62
|
+
wrote
|
|
63
|
+
? `schema: pulled "${slug}" (${Object.keys(manifest.tables ?? {}).length} tables) -> src/monty.gen.ts`
|
|
64
|
+
: `schema: "${slug}" synced (config-only app — no code, no gen module; read it with \`monty schema\`)`,
|
|
46
65
|
);
|
|
66
|
+
if (body.hash) console.log(`base: ${body.hash.slice(0, 12)} (.monty/schema.json — the CAS base for the next push)`);
|
|
47
67
|
}
|
|
48
|
-
|
|
49
|
-
const configPath = join(appDir, "monty.config.ts");
|
|
50
|
-
if (existsSync(configPath) && !force) {
|
|
51
|
-
// Dirty check: does the local config compile to the manifest this
|
|
52
|
-
// checkout last synced? If not, pulling would clobber local edits.
|
|
53
|
-
const state = readSchemaState(appDir);
|
|
54
|
-
let localHash = null;
|
|
55
|
-
try {
|
|
56
|
-
const compiled = await compileAppConfig(appDir);
|
|
57
|
-
localHash = compiled.manifest ? manifestHash(compiled.manifest) : null;
|
|
58
|
-
} catch {
|
|
59
|
-
// A config that doesn't compile can't be proven clean — refuse without
|
|
60
|
-
// --force rather than silently discarding whatever it holds.
|
|
61
|
-
fail(
|
|
62
|
-
"SCHEMA_DIRTY",
|
|
63
|
-
"monty.config.ts does not compile, so local schema edits cannot be verified against the registry. Fix it and deploy, or re-run with --force to REPLACE it (a .bak is kept).",
|
|
64
|
-
);
|
|
65
|
-
}
|
|
66
|
-
const cleanAgainst = state?.hash ?? body.hash;
|
|
67
|
-
if (localHash !== null && localHash !== cleanAgainst && localHash !== body.hash) {
|
|
68
|
-
fail(
|
|
69
|
-
"SCHEMA_DIRTY",
|
|
70
|
-
"monty.config.ts has schema changes that never reached the registry. Push them first (`monty dev` or `monty save`), or discard them with --force (a .bak is kept).",
|
|
71
|
-
);
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
if (existsSync(configPath)) {
|
|
76
|
-
renameSync(configPath, `${configPath}.bak`);
|
|
77
|
-
console.log(`backup: monty.config.ts.bak`);
|
|
78
|
-
}
|
|
79
|
-
writeFileSync(configPath, manifestToConfig(body.manifest, { name: body.name, icon: body.icon ?? undefined }));
|
|
80
|
-
writeSchemaState(appDir, body.hash);
|
|
81
|
-
console.log(`schema: pulled "${slug}" (${Object.keys(body.manifest.tables).length} tables) -> monty.config.ts`);
|
|
82
|
-
console.log(`base: ${body.hash.slice(0, 12)} (.monty/schema.json — the CAS base for the next push)`);
|
|
68
|
+
return { wrote, hash: body.hash ?? null, manifest };
|
|
83
69
|
}
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
// The Monty style lint — the platform's stylelint-polaris equivalent for
|
|
2
|
+
// Tailwind class strings. Rules are disallow-lists grouped by category with
|
|
3
|
+
// an instruction message per finding (Polaris's coverage-plugin shape); the
|
|
4
|
+
// allowed vocabularies are IMPORTED from @monty/tokens/manifest.mjs so the
|
|
5
|
+
// lint can never drift from the tokens. It runs blocking inside `monty save`
|
|
6
|
+
// (beside the typecheck), advisory at `monty dev`, and standalone as
|
|
7
|
+
// `monty style`.
|
|
8
|
+
//
|
|
9
|
+
// Scope: agent-authored app code only — src/**/*.{ts,tsx,jsx} minus the
|
|
10
|
+
// vendored kit (src/components/ui/**), generated files, and wiring. A line
|
|
11
|
+
// carrying `monty-style-ignore` is skipped (the stylelint-disable of this
|
|
12
|
+
// system; use it for the rare sanctioned exception, never to silence a page).
|
|
13
|
+
import { existsSync, readFileSync, readdirSync } from "node:fs";
|
|
14
|
+
import { dirname, join, relative } from "node:path";
|
|
15
|
+
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
16
|
+
|
|
17
|
+
const HERE = dirname(fileURLToPath(import.meta.url));
|
|
18
|
+
|
|
19
|
+
let manifestPromise;
|
|
20
|
+
function loadManifest() {
|
|
21
|
+
// Published tarball: prepack copies the manifest beside this file.
|
|
22
|
+
// Repo checkout: import straight from packages/tokens — the dependency
|
|
23
|
+
// direction that keeps rules derived from tokens.
|
|
24
|
+
manifestPromise ??= (async () => {
|
|
25
|
+
for (const p of [
|
|
26
|
+
join(HERE, "tokens-manifest.mjs"),
|
|
27
|
+
join(HERE, "../../tokens/manifest.mjs"),
|
|
28
|
+
]) {
|
|
29
|
+
if (existsSync(p)) return import(pathToFileURL(p).href);
|
|
30
|
+
}
|
|
31
|
+
return null;
|
|
32
|
+
})();
|
|
33
|
+
return manifestPromise;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const SIDE = "(?:-(?:t|b|l|r|tl|tr|bl|br|s|e|ss|se|es|ee))?";
|
|
37
|
+
const COLOR_PROPS = "(?:bg|text|border|ring|outline|fill|stroke|from|via|to|divide|decoration|caret|accent)";
|
|
38
|
+
|
|
39
|
+
function buildRules(m) {
|
|
40
|
+
const hues = m.PALETTE_HUES.join("|");
|
|
41
|
+
return [
|
|
42
|
+
{
|
|
43
|
+
category: "type",
|
|
44
|
+
// Arbitrary sizes only — arbitrary COLOR values on text- belong to color.
|
|
45
|
+
re: /\btext-\[(?!#|rgb|hsl|oklch|var\(|color-mix)[^\]]+\]/g,
|
|
46
|
+
fix: () => "use the ladder (text-body is the default)",
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
category: "type",
|
|
50
|
+
re: /\btext-(?:xs|sm|base|lg|xl|[2-9]xl)\b/g,
|
|
51
|
+
fix: (got) => `use ${m.TEXT_SUGGESTIONS[got] ?? "the ladder"}`,
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
category: "weight",
|
|
55
|
+
re: /\bfont-(?:thin|extralight|light|bold|extrabold|black)\b/g,
|
|
56
|
+
fix: () => "use font-medium (or the weight the ladder style carries)",
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
category: "color",
|
|
60
|
+
re: new RegExp(`\\b${COLOR_PROPS}-\\[(?:#|rgb|hsl|oklch|var\\(|color-mix)[^\\]]*\\]`, "g"),
|
|
61
|
+
fix: () => "use a semantic token class",
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
category: "color",
|
|
65
|
+
re: new RegExp(`\\b${COLOR_PROPS}-(?:${hues})-\\d{2,3}(?:/\\d{1,3})?\\b`, "g"),
|
|
66
|
+
fix: (got) => {
|
|
67
|
+
const hue = got.match(new RegExp(`-(${hues})-`))?.[1];
|
|
68
|
+
return `use ${m.HUE_SUGGESTIONS[hue] ?? "a semantic token"}`;
|
|
69
|
+
},
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
category: "color",
|
|
73
|
+
re: /["'`]#[0-9a-fA-F]{6}(?:[0-9a-fA-F]{2})?["'`]/g,
|
|
74
|
+
fix: () => "use var(--chart-1…5) or a semantic class",
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
category: "radius",
|
|
78
|
+
re: new RegExp(`\\brounded${SIDE}-\\[[^\\]]+\\]`, "g"),
|
|
79
|
+
fix: () => "corners are sharp — delete it",
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
category: "radius",
|
|
83
|
+
re: new RegExp(`\\brounded${SIDE}-(?:xs|sm|md|lg|xl|[234]xl)\\b`, "g"),
|
|
84
|
+
fix: () => "corners are sharp — delete it (rounded-full only for dots/avatars)",
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
category: "elevation",
|
|
88
|
+
re: /\bshadow-\[[^\]]+\]/g,
|
|
89
|
+
fix: () => `use ${m.SHADOWS_ALLOWED.slice(0, 3).join("/")}`,
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
category: "elevation",
|
|
93
|
+
re: /\bshadow-(?:2xs|xs|sm|md|lg|xl|2xl|inner)\b/g,
|
|
94
|
+
fix: () => `use ${m.SHADOWS_ALLOWED.slice(0, 3).join("/")}`,
|
|
95
|
+
},
|
|
96
|
+
];
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const SKIP_FILES = new Set(["main.tsx", "vite-env.d.ts", "monty.gen.ts", "routeTree.gen.ts"]);
|
|
100
|
+
|
|
101
|
+
function* sourceFiles(dir, base = dir) {
|
|
102
|
+
let entries;
|
|
103
|
+
try {
|
|
104
|
+
entries = readdirSync(dir, { withFileTypes: true });
|
|
105
|
+
} catch {
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
for (const entry of entries) {
|
|
109
|
+
const p = join(dir, entry.name);
|
|
110
|
+
const rel = relative(base, p);
|
|
111
|
+
if (entry.isDirectory()) {
|
|
112
|
+
// The vendored kit is template/registry-owned — its internals may
|
|
113
|
+
// legitimately carry classes agents must not write.
|
|
114
|
+
if (rel === join("components", "ui") || entry.name === "node_modules") continue;
|
|
115
|
+
yield* sourceFiles(p, base);
|
|
116
|
+
} else if (/\.(?:tsx|jsx|ts)$/.test(entry.name)) {
|
|
117
|
+
if (SKIP_FILES.has(entry.name) || entry.name.endsWith(".gen.ts")) continue;
|
|
118
|
+
yield p;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/** Scan an app folder. Returns [{ file, line, category, got, fix }] sorted
|
|
124
|
+
* by file then line; [] when clean (or when src/ doesn't exist —
|
|
125
|
+
* config-only apps have nothing to lint). */
|
|
126
|
+
export async function lintStyles(appDir) {
|
|
127
|
+
const manifest = await loadManifest();
|
|
128
|
+
if (!manifest) return []; // never block a save on a broken lint install
|
|
129
|
+
const rules = buildRules(manifest);
|
|
130
|
+
const src = join(appDir, "src");
|
|
131
|
+
const violations = [];
|
|
132
|
+
for (const file of sourceFiles(src)) {
|
|
133
|
+
const rel = join("src", relative(src, file));
|
|
134
|
+
const lines = readFileSync(file, "utf8").split("\n");
|
|
135
|
+
lines.forEach((text, i) => {
|
|
136
|
+
if (text.includes("monty-style-ignore")) return;
|
|
137
|
+
for (const rule of rules) {
|
|
138
|
+
for (const match of text.matchAll(rule.re)) {
|
|
139
|
+
violations.push({
|
|
140
|
+
file: rel,
|
|
141
|
+
line: i + 1,
|
|
142
|
+
category: rule.category,
|
|
143
|
+
got: match[0],
|
|
144
|
+
fix: rule.fix(match[0]),
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
return violations.sort((a, b) => a.file.localeCompare(b.file) || a.line - b.line);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
const MAX_LINES = 40;
|
|
154
|
+
|
|
155
|
+
/** Findings grouped by category, each under its token-vocabulary lede. */
|
|
156
|
+
export async function formatViolations(violations) {
|
|
157
|
+
const manifest = await loadManifest();
|
|
158
|
+
const byCategory = new Map();
|
|
159
|
+
for (const v of violations) {
|
|
160
|
+
if (!byCategory.has(v.category)) byCategory.set(v.category, []);
|
|
161
|
+
byCategory.get(v.category).push(v);
|
|
162
|
+
}
|
|
163
|
+
const out = [];
|
|
164
|
+
let shown = 0;
|
|
165
|
+
for (const [category, list] of byCategory) {
|
|
166
|
+
out.push(`${category} — ${manifest?.CATEGORY_MESSAGES?.[category] ?? ""}`);
|
|
167
|
+
for (const v of list) {
|
|
168
|
+
if (shown >= MAX_LINES) break;
|
|
169
|
+
out.push(` ${v.file}:${v.line} ${v.got} → ${v.fix}`);
|
|
170
|
+
shown++;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
if (violations.length > shown) out.push(` … and ${violations.length - shown} more`);
|
|
174
|
+
return out.join("\n");
|
|
175
|
+
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
// The machine-readable half of the Monty design tokens (tokens.css is the
|
|
2
|
+
// stylesheet). The style lint in the CLI derives its allowed lists and
|
|
3
|
+
// suggestions from HERE — Polaris's pattern (stylelint-polaris imports
|
|
4
|
+
// polaris-tokens) — so the rules can never drift from the tokens.
|
|
5
|
+
// Keep this file in lockstep with tokens.css.
|
|
6
|
+
|
|
7
|
+
/** The six text styles a Monty page uses. */
|
|
8
|
+
export const TEXT_LADDER = [
|
|
9
|
+
"text-tick",
|
|
10
|
+
"text-meta",
|
|
11
|
+
"text-body",
|
|
12
|
+
"text-title",
|
|
13
|
+
"text-heading",
|
|
14
|
+
"text-stat",
|
|
15
|
+
];
|
|
16
|
+
|
|
17
|
+
/** What each off-ladder Tailwind size maps to. */
|
|
18
|
+
export const TEXT_SUGGESTIONS = {
|
|
19
|
+
"text-xs": "text-meta",
|
|
20
|
+
"text-sm": "text-body (content) or text-title (titles)",
|
|
21
|
+
"text-base": "text-body",
|
|
22
|
+
"text-lg": "text-heading",
|
|
23
|
+
"text-xl": "text-heading",
|
|
24
|
+
"text-2xl": "text-stat",
|
|
25
|
+
"text-3xl": "text-stat",
|
|
26
|
+
"text-4xl": "text-stat",
|
|
27
|
+
"text-5xl": "text-stat",
|
|
28
|
+
"text-6xl": "text-stat",
|
|
29
|
+
"text-7xl": "text-stat",
|
|
30
|
+
"text-8xl": "text-stat",
|
|
31
|
+
"text-9xl": "text-stat",
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
/** Allowed font weights (400 default / 500 titled / 600 via heading+stat). */
|
|
35
|
+
export const WEIGHTS_ALLOWED = ["font-normal", "font-medium", "font-semibold"];
|
|
36
|
+
|
|
37
|
+
/** The radius policy: sharp everywhere; these are the only radius classes. */
|
|
38
|
+
export const RADIUS_ALLOWED = [
|
|
39
|
+
"rounded-none",
|
|
40
|
+
"rounded-full",
|
|
41
|
+
"rounded-control",
|
|
42
|
+
"rounded-control-sm",
|
|
43
|
+
"rounded-overlay",
|
|
44
|
+
];
|
|
45
|
+
|
|
46
|
+
/** The only three shadows (plus shadow-none). */
|
|
47
|
+
export const SHADOWS_ALLOWED = ["shadow-control", "shadow-btn-primary", "shadow-menu", "shadow-none"];
|
|
48
|
+
|
|
49
|
+
/** Tailwind default palette hues — banned; the palette is the platform's. */
|
|
50
|
+
export const PALETTE_HUES = [
|
|
51
|
+
"slate", "gray", "zinc", "neutral", "stone",
|
|
52
|
+
"red", "orange", "amber", "yellow", "lime",
|
|
53
|
+
"green", "emerald", "teal", "cyan", "sky",
|
|
54
|
+
"blue", "indigo", "violet", "purple", "fuchsia", "pink", "rose",
|
|
55
|
+
];
|
|
56
|
+
|
|
57
|
+
/** Where a banned hue should point instead. */
|
|
58
|
+
export const HUE_SUGGESTIONS = {
|
|
59
|
+
red: "text-destructive / bg-destructive",
|
|
60
|
+
orange: "text-warning",
|
|
61
|
+
amber: "text-warning",
|
|
62
|
+
yellow: "text-warning",
|
|
63
|
+
lime: "text-success",
|
|
64
|
+
green: "text-success",
|
|
65
|
+
emerald: "text-success",
|
|
66
|
+
teal: "text-success",
|
|
67
|
+
blue: "bg-primary / text-link",
|
|
68
|
+
sky: "text-link",
|
|
69
|
+
indigo: "bg-primary",
|
|
70
|
+
cyan: "text-link",
|
|
71
|
+
violet: "var(--chart-3)",
|
|
72
|
+
purple: "var(--chart-3)",
|
|
73
|
+
fuchsia: "var(--chart-3)",
|
|
74
|
+
pink: "var(--chart-3)",
|
|
75
|
+
rose: "text-destructive",
|
|
76
|
+
slate: "text-muted-foreground / bg-accent",
|
|
77
|
+
gray: "text-muted-foreground / bg-accent",
|
|
78
|
+
zinc: "text-muted-foreground / bg-accent",
|
|
79
|
+
neutral: "text-muted-foreground / bg-accent",
|
|
80
|
+
stone: "text-muted-foreground / bg-accent",
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
/** Category ledes — the default instruction on each violation class. */
|
|
84
|
+
export const CATEGORY_MESSAGES = {
|
|
85
|
+
type: "The type ladder is text-tick/meta/body/title/heading/stat; most text needs no size class at all (text-body is on <body>).",
|
|
86
|
+
weight: "Weights are 400/500/600 — font-medium for titled text; 600 arrives via text-heading/text-stat.",
|
|
87
|
+
color: "Colors come from the semantic tokens (bg-background, text-muted-foreground, var(--chart-2), …) — the palette is the platform's.",
|
|
88
|
+
radius: "Corners are sharp by policy; rounded-full (dots, avatars) is the one exception.",
|
|
89
|
+
elevation: "Elevation is shadow-control (rest controls), shadow-btn-primary (the one primary action), or shadow-menu (overlays).",
|
|
90
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@montytools/cli",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.6",
|
|
4
4
|
"repository": {
|
|
5
5
|
"type": "git",
|
|
6
6
|
"url": "git+https://github.com/TomasMonty/monty-v2.git",
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
},
|
|
22
22
|
"scripts": {
|
|
23
23
|
"prepack": "node scripts/bundle-template.mjs",
|
|
24
|
-
"typecheck": "node --check bin/monty.mjs && node --check lib/compile.mjs && node --check lib/schemaCodegen.mjs && node --check lib/schemaPull.mjs && node --check lib/views.mjs && node --check scripts/schema-roundtrip.mjs",
|
|
24
|
+
"typecheck": "node --check bin/monty.mjs && node --check lib/compile.mjs && node --check lib/schemaCodegen.mjs && node --check lib/schemaPull.mjs && node --check lib/views.mjs && node --check scripts/schema-roundtrip.mjs && node --check lib/styleLint.mjs",
|
|
25
25
|
"test": "node --test \"test/*.test.mjs\"",
|
|
26
26
|
"postinstall": "node bin/postinstall.mjs",
|
|
27
27
|
"test:roundtrip": "node scripts/schema-roundtrip.mjs"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: monty-build
|
|
3
|
-
description: Build, run, and save Monty workspace apps. Use whenever the task involves a Monty app, monty.
|
|
3
|
+
description: Build, run, and save Monty workspace apps. Use whenever the task involves a Monty app, its schema (monty schema / src/monty.gen.ts), the monty CLI (create/connect/dev/logs/save), the @montytools/sdk, or a prompt mentioning usemonty.dev. Covers folder discipline, the build loop, data/auth rules, and error handling.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Building Monty apps
|
|
@@ -20,8 +20,8 @@ app states.
|
|
|
20
20
|
|
|
21
21
|
1. **Respect the folder.** `monty create` scaffolds new apps;
|
|
22
22
|
`monty connect <slug> [dir]` puts a copy of an existing app in any
|
|
23
|
-
folder you choose. Never mkdir an app folder by hand and never edit
|
|
24
|
-
`
|
|
23
|
+
folder you choose. Never mkdir an app folder by hand and never edit
|
|
24
|
+
`.monty/app.json` (the identity stamp). `monty current` says which app folder
|
|
25
25
|
you are in. Every file in the folder rides the source snapshot on
|
|
26
26
|
`monty save`, so keep scratch files out. Use the OS temp dir, or pipe
|
|
27
27
|
(`monty schema | <edit> | monty schema set -` needs no file), and delete
|
|
@@ -53,8 +53,10 @@ app states.
|
|
|
53
53
|
`monty schema` (JSON on stdout), change it with
|
|
54
54
|
`monty schema set '<json>'` — the whole config as one JSON argument, or
|
|
55
55
|
piped: `monty schema | <edit> | monty schema set -` (validated
|
|
56
|
-
server-side, additive by default).
|
|
57
|
-
|
|
56
|
+
server-side, additive by default). `src/monty.gen.ts` is the GENERATED
|
|
57
|
+
mirror of that config — import `{ app }` from it for typed hooks, never
|
|
58
|
+
edit it (it regenerates within a heartbeat of any change while
|
|
59
|
+
`monty dev` runs). Give every field a
|
|
58
60
|
`description` and every enum a `valueDescriptions` map saying when each
|
|
59
61
|
option applies; record-writing agents follow that guidance later.
|
|
60
62
|
Declare a page before shipping its route (`monty page add <name>` does
|