@montytools/cli 0.5.4 → 0.5.5
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 +349 -385
- package/lib/compile.mjs +3 -3
- package/lib/schemaCodegen.mjs +2 -2
- package/lib/schemaPull.mjs +3 -3
- package/lib/views.mjs +19 -0
- package/package.json +2 -2
- package/skills/monty-build/SKILL.md +90 -105
- package/template/AGENTS.md +6 -5
- package/template/package.json +1 -1
package/lib/compile.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// The ONE config-compile pipeline: esbuild-bundle a temp entry that runs the
|
|
2
2
|
// app's OWN compileApp/zod/sdk instances on monty.config.ts, execute it in a
|
|
3
|
-
// subprocess, parse the emitted metadata. Used by `monty dev`/`monty
|
|
3
|
+
// subprocess, parse the emitted metadata. Used by `monty dev`/`monty save`
|
|
4
4
|
// (bin/monty.mjs) and the demo harness (scripts/demo.mjs) — one pipeline, so
|
|
5
5
|
// what a demo installs is byte-for-byte what a deploy would send.
|
|
6
6
|
import { spawnSync } from "node:child_process";
|
|
@@ -19,7 +19,7 @@ export class CompileError extends Error {
|
|
|
19
19
|
// Throws CompileError:
|
|
20
20
|
// - CONFIG_BUNDLE_FAILED — esbuild could not bundle (usually unlinked deps)
|
|
21
21
|
// - CONFIG_COMPILE_FAILED — the config threw while loading
|
|
22
|
-
export async function compileAppConfig(appDir
|
|
22
|
+
export async function compileAppConfig(appDir) {
|
|
23
23
|
appDir = resolve(appDir); // esbuild requires an absolute absWorkingDir
|
|
24
24
|
// esbuild is a dependency of THIS package (@montytools/cli), so resolution
|
|
25
25
|
// from here works for any caller — no per-script resolution dance.
|
|
@@ -31,7 +31,7 @@ export async function compileAppConfig(appDir, { forceManifest = false } = {}) {
|
|
|
31
31
|
writeFileSync(entry, [
|
|
32
32
|
`import { app } from "../monty.config";`,
|
|
33
33
|
`import { compileApp } from "@montytools/sdk/compile";`,
|
|
34
|
-
`process.stdout.write(JSON.stringify(compileApp(app
|
|
34
|
+
`process.stdout.write(JSON.stringify(compileApp(app)));`,
|
|
35
35
|
].join("\n"));
|
|
36
36
|
try {
|
|
37
37
|
await build({
|
package/lib/schemaCodegen.mjs
CHANGED
|
@@ -220,8 +220,8 @@ export function manifestToConfig(manifest, { name, icon } = {}) {
|
|
|
220
220
|
...sdkImports.map((n) => ` ${n},`),
|
|
221
221
|
`} from "@montytools/sdk";`,
|
|
222
222
|
``,
|
|
223
|
-
`// Regenerated by \`monty schema pull\` from the app's stored
|
|
224
|
-
`// Edit freely — \`monty dev\` / \`monty
|
|
223
|
+
`// Regenerated by \`monty schema pull\` from the app's config stored in the workspace.`,
|
|
224
|
+
`// Edit freely — \`monty dev\` / \`monty save\` push changes back; another`,
|
|
225
225
|
`// editor's remote changes surface as MANIFEST_DRIFT (then pull again).`,
|
|
226
226
|
`export const app = defineApp({`,
|
|
227
227
|
` slug: ${JSON.stringify(manifest.slug)},`,
|
package/lib/schemaPull.mjs
CHANGED
|
@@ -41,8 +41,8 @@ export async function schemaPull({ appDir, host, key, slug, force, compileAppCon
|
|
|
41
41
|
}
|
|
42
42
|
if (!body.manifest) {
|
|
43
43
|
fail(
|
|
44
|
-
"
|
|
45
|
-
`"${slug}" has no stored
|
|
44
|
+
"NO_CONFIG",
|
|
45
|
+
`"${slug}" has no config stored in the workspace yet (a V1 app, or one that never pushed). Author monty.config.ts with V2 features and run \`monty dev\` or \`monty save\` first.`,
|
|
46
46
|
);
|
|
47
47
|
}
|
|
48
48
|
|
|
@@ -67,7 +67,7 @@ export async function schemaPull({ appDir, host, key, slug, force, compileAppCon
|
|
|
67
67
|
if (localHash !== null && localHash !== cleanAgainst && localHash !== body.hash) {
|
|
68
68
|
fail(
|
|
69
69
|
"SCHEMA_DIRTY",
|
|
70
|
-
"monty.config.ts has schema changes that never reached the registry. Push them first (`monty dev`
|
|
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
71
|
);
|
|
72
72
|
}
|
|
73
73
|
}
|
package/lib/views.mjs
CHANGED
|
@@ -107,8 +107,25 @@ export function parseHiddenColumns(input) {
|
|
|
107
107
|
return [...new Set(input.split(",").map((field) => field.trim()).filter(Boolean))];
|
|
108
108
|
}
|
|
109
109
|
|
|
110
|
+
/** `--kanban <field>` makes the view a kanban laned by that select field's
|
|
111
|
+
* values; `--kanban none` makes it a table. */
|
|
112
|
+
export function parseKanbanFlag(input) {
|
|
113
|
+
if (input === undefined) return undefined;
|
|
114
|
+
if (input === "none") return null;
|
|
115
|
+
const groupBy = input.trim();
|
|
116
|
+
if (groupBy === "") {
|
|
117
|
+
badViewConfig('--kanban needs a select field name, or "none" to make the view a table.');
|
|
118
|
+
}
|
|
119
|
+
return { type: "kanban", groupBy };
|
|
120
|
+
}
|
|
121
|
+
|
|
110
122
|
export function mergeViewConfig(existing, patch) {
|
|
111
123
|
const base = isObject(existing) ? existing : {};
|
|
124
|
+
const kanban = Object.hasOwn(patch, "kanban")
|
|
125
|
+
? patch.kanban
|
|
126
|
+
: base.type === "kanban" && typeof base.groupBy === "string"
|
|
127
|
+
? { type: "kanban", groupBy: base.groupBy }
|
|
128
|
+
: null;
|
|
112
129
|
return {
|
|
113
130
|
filters: Object.hasOwn(patch, "filters")
|
|
114
131
|
? patch.filters
|
|
@@ -117,6 +134,7 @@ export function mergeViewConfig(existing, patch) {
|
|
|
117
134
|
hidden: Object.hasOwn(patch, "hidden")
|
|
118
135
|
? patch.hidden
|
|
119
136
|
: (Array.isArray(base.hidden) ? base.hidden : []),
|
|
137
|
+
...(kanban ? { type: "kanban", groupBy: kanban.groupBy } : {}),
|
|
120
138
|
};
|
|
121
139
|
}
|
|
122
140
|
|
|
@@ -126,6 +144,7 @@ export function validateViewColumns(config, fieldNames) {
|
|
|
126
144
|
...Object.keys(config.filters),
|
|
127
145
|
...(config.sort ? [config.sort.column] : []),
|
|
128
146
|
...config.hidden,
|
|
147
|
+
...(config.type === "kanban" && config.groupBy ? [config.groupBy] : []),
|
|
129
148
|
];
|
|
130
149
|
const unknown = [...new Set(used.filter((field) => !known.has(field)))];
|
|
131
150
|
if (unknown.length > 0) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@montytools/cli",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.5",
|
|
4
4
|
"repository": {
|
|
5
5
|
"type": "git",
|
|
6
6
|
"url": "git+https://github.com/TomasMonty/monty-v2.git",
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
"scripts": {
|
|
23
23
|
"prepack": "node scripts/bundle-template.mjs",
|
|
24
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",
|
|
25
|
-
"test": "node --test test
|
|
25
|
+
"test": "node --test \"test/*.test.mjs\"",
|
|
26
26
|
"postinstall": "node bin/postinstall.mjs",
|
|
27
27
|
"test:roundtrip": "node scripts/schema-roundtrip.mjs"
|
|
28
28
|
},
|
|
@@ -1,126 +1,111 @@
|
|
|
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.config.ts, the monty CLI (create/dev/logs/save
|
|
3
|
+
description: Build, run, and save Monty workspace apps. Use whenever the task involves a Monty app, monty.config.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
|
|
7
7
|
|
|
8
|
-
Monty is a work OS
|
|
9
|
-
product logic only
|
|
10
|
-
platform's job. The
|
|
11
|
-
(nearest
|
|
12
|
-
the territory.
|
|
8
|
+
Monty is a work OS. Teams get internal apps built by coding agents. You write
|
|
9
|
+
product logic only. Data, auth, tenancy, deployment, and embedding are the
|
|
10
|
+
platform's job. The full contract is in the app's own `AGENTS.md`
|
|
11
|
+
(nearest file wins). Read it before writing code.
|
|
13
12
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
the
|
|
17
|
-
|
|
18
|
-
|
|
13
|
+
Every app has ONE copy: **Live**, the cloud copy the team uses. `monty save`
|
|
14
|
+
updates it. While a dev **session** runs (often already started for you by
|
|
15
|
+
the Monty desktop), workspace admins see the session's version automatically.
|
|
16
|
+
There is no publish step. "dev" and "prod" name platform environments, never
|
|
17
|
+
app states.
|
|
19
18
|
|
|
20
19
|
## Rules
|
|
21
20
|
|
|
22
|
-
1. **
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
`
|
|
26
|
-
|
|
27
|
-
`monty
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
`monty
|
|
37
|
-
|
|
38
|
-
`
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
is running it starts the shell (start it in the background and move on).
|
|
43
|
-
Then iterate: edit code → vite hot-reloads → `monty logs -n 50` shows
|
|
44
|
-
whether it compiled and any browser errors. Re-running `monty dev` is
|
|
45
|
-
always safe — it attaches, prints status, and exits. Never try to run a
|
|
46
|
-
second dev *server* for the same app (attach handles this for you) and
|
|
47
|
-
never kill a dev shell you didn't start; `monty dev --takeover` is the
|
|
48
|
-
only sanctioned restart when a session is wedged. **You are not done
|
|
49
|
-
until you've saved: once the work is verified in the session, run
|
|
50
|
-
`monty save "<what changed>"`** — it builds, typechecks, and pushes the
|
|
51
|
-
working copy to the cloud copy, like `git push main`. Save after every
|
|
52
|
-
meaningful change, not just at the end; unsaved work exists only on this
|
|
21
|
+
1. **Respect the folder.** `monty create` scaffolds new apps;
|
|
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 the
|
|
24
|
+
`id:` line in `monty.config.ts`. `monty current` says which app folder
|
|
25
|
+
you are in. Every file in the folder rides the source snapshot on
|
|
26
|
+
`monty save`, so keep scratch files out. Use the OS temp dir, or pipe
|
|
27
|
+
(`monty schema | <edit> | monty schema set -` needs no file), and delete
|
|
28
|
+
anything temporary before saving.
|
|
29
|
+
2. **The loop.** `monty create <slug> --name "Name" --icon <tabler-icon>`
|
|
30
|
+
(pass `--build <id>` when the prompt includes one), then `monty install`.
|
|
31
|
+
Shape the schema with `monty schema` and `monty schema set`; edit
|
|
32
|
+
`src/routes/`. Run `monty dev` once. If a shell is already running it
|
|
33
|
+
prints status and exits; otherwise start it in the background. Then
|
|
34
|
+
iterate: edit, vite hot-reloads, `monty logs -n 50` shows compile and
|
|
35
|
+
browser errors. Re-running `monty dev` is always safe. Never start a
|
|
36
|
+
second dev server for the same app and never kill a shell you didn't
|
|
37
|
+
start; `monty dev --takeover` is the one sanctioned restart.
|
|
38
|
+
**You are not done until you've saved.** `monty save "<what changed>"`
|
|
39
|
+
builds, typechecks, and pushes to the cloud copy, like `git push main`.
|
|
40
|
+
Save after every meaningful change. Unsaved work exists only on this
|
|
53
41
|
machine.
|
|
54
42
|
3. **Everything through the CLI.** `monty install`, `monty build`,
|
|
55
|
-
`monty typecheck`, `monty dev`, `monty save
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
the
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
a
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
one table with different filters, sorting, or hidden columns, use
|
|
79
|
-
`monty views set <table> <name> ...`. For example, an evaluation queue is
|
|
43
|
+
`monty typecheck`, `monty dev`, `monty save`. Never run vite, tsc, pnpm,
|
|
44
|
+
or npm scripts directly. `monty logs` (add `-f` to follow) is where vite
|
|
45
|
+
errors, browser errors, and save results land.
|
|
46
|
+
4. **Import only the SDK.** `@montytools/sdk` (`defineApp`, zod) and
|
|
47
|
+
`@montytools/sdk/react` (hooks: `useList`, `useInsert`, ...). Never
|
|
48
|
+
import Clerk or Convex; never fetch external APIs from app code, the CSP
|
|
49
|
+
blocks them. Open external pages with `openExternal(url)` from the SDK,
|
|
50
|
+
called synchronously from the click handler. `window.open` and
|
|
51
|
+
`target="_blank"` are blocked in the app iframe.
|
|
52
|
+
5. **The schema lives in the workspace, not in code.** Read it with
|
|
53
|
+
`monty schema` (JSON on stdout), change it with
|
|
54
|
+
`monty schema set '<json>'` — the whole config as one JSON argument, or
|
|
55
|
+
piped: `monty schema | <edit> | monty schema set -` (validated
|
|
56
|
+
server-side, additive by default). On workspace-owned apps,
|
|
57
|
+
editing `monty.config.ts` does not change the schema. Give every field a
|
|
58
|
+
`description` and every enum a `valueDescriptions` map saying when each
|
|
59
|
+
option applies; record-writing agents follow that guidance later.
|
|
60
|
+
Declare a page before shipping its route (`monty page add <name>` does
|
|
61
|
+
both); a save with an undeclared route refuses. Field names `_*`,
|
|
62
|
+
`updatedAt`, `createdBy` are reserved.
|
|
63
|
+
6. **Prefer a saved view for filtered tables.** One table with different
|
|
64
|
+
filters, sorting, or hidden columns is `monty views set <table> <name>`.
|
|
65
|
+
An evaluation queue:
|
|
80
66
|
`monty views set leads "Evaluate" --filter '{"pipelineId":null}'`.
|
|
81
|
-
|
|
82
|
-
`monty views
|
|
83
|
-
|
|
84
|
-
`@montytools/sdk/react`
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
`monty
|
|
90
|
-
|
|
91
|
-
the `monty-design` skill; read it before styling any page.
|
|
67
|
+
A board: `--kanban <select-field>` lanes records by that field
|
|
68
|
+
(`monty views set deals "Pipeline" --kanban stage`). Update later with
|
|
69
|
+
`monty views update`; omitted properties stay unchanged. If the table
|
|
70
|
+
needs actions, compose `RecordPage` from `@montytools/sdk/react` with
|
|
71
|
+
controls from `@montytools/sdk/ui`. Write a whole custom page only when
|
|
72
|
+
the shared components no longer fit.
|
|
73
|
+
7. **UI is stock shadcn** (preset already wired). Install curated components
|
|
74
|
+
with `monty components add <name>`; `monty components [query]` lists the
|
|
75
|
+
catalog, `monty components docs <name>` prints one's source. How pages
|
|
76
|
+
should look is the `monty-design` skill; read it before styling.
|
|
92
77
|
8. **Errors are instructions.** Every failure prints
|
|
93
|
-
`[MontyError CODE] Fix:
|
|
94
|
-
|
|
78
|
+
`[MontyError CODE] Fix: ...`. Do what the Fix says. Typecheck failures
|
|
79
|
+
block the save by design.
|
|
95
80
|
9. **Edits are real.** The dev shell reads and writes the app's LIVE
|
|
96
|
-
records
|
|
97
|
-
app
|
|
98
|
-
create; never seed junk into a team's working tables.
|
|
81
|
+
records, and every write journals into the app's Activity. Exercise the
|
|
82
|
+
app for real, clean up test rows, never seed junk into a team's tables.
|
|
99
83
|
10. **Report platform failures when sharing is enabled.** `monty support
|
|
100
|
-
status` shows whether
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
consent yourself. Do not file reports for ordinary app-code bugs or send
|
|
108
|
-
duplicates for the same incident.
|
|
84
|
+
status` shows whether an admin allowed agent reports. If enabled, submit
|
|
85
|
+
one report when a platform error still blocks you after following its
|
|
86
|
+
Fix, or when the task needs a missing platform feature. State the
|
|
87
|
+
problem, expected behavior, attempted fixes, and exact error. Don't
|
|
88
|
+
paste the conversation. If sharing is off, ask the user before running
|
|
89
|
+
`monty support enable --yes`. No reports for ordinary app-code bugs, no
|
|
90
|
+
duplicates.
|
|
109
91
|
|
|
110
92
|
## CLI reference
|
|
111
93
|
|
|
112
94
|
| command | purpose |
|
|
113
95
|
|---|---|
|
|
114
|
-
| `monty login` | browser sign-in
|
|
115
|
-
| `monty
|
|
116
|
-
| `monty
|
|
117
|
-
| `monty
|
|
118
|
-
| `monty
|
|
119
|
-
| `monty
|
|
120
|
-
| `monty
|
|
121
|
-
| `monty
|
|
122
|
-
| `monty
|
|
123
|
-
| `monty
|
|
124
|
-
| `monty
|
|
125
|
-
| `monty
|
|
126
|
-
| `monty
|
|
96
|
+
| `monty login` | browser sign-in, once per machine |
|
|
97
|
+
| `monty connect <slug> [dir]` | pull an existing app into any folder, ready for `monty dev` |
|
|
98
|
+
| `monty create <slug>` | register a new app and scaffold it (needs login) |
|
|
99
|
+
| `monty current` / `select` / `apps` | where am I / jump to app / list local copies |
|
|
100
|
+
| `monty install` / `build` / `typecheck` | lifecycle, no raw pnpm/vite/tsc |
|
|
101
|
+
| `monty dev` | run the app's session, or attach to a running one |
|
|
102
|
+
| `monty logs [-n N] [-f]` | read/follow the session log |
|
|
103
|
+
| `monty save ["what changed"]` | push the working copy to the cloud copy (build + typecheck gate it) |
|
|
104
|
+
| `monty history [slug]` | saved-version history, one row per save |
|
|
105
|
+
| `monty schema [slug]` | print the app's config (stored in the workspace) as JSON |
|
|
106
|
+
| `monty schema set '<json>'` (or `set -` piped) | write the edited config back (validated, CAS) |
|
|
107
|
+
| `monty views <list\|set\|update\|remove> <table>` | manage shared saved views |
|
|
108
|
+
| `monty page add <name>` | declare + scaffold a custom page |
|
|
109
|
+
| `monty components add <name...>` | install curated shadcn components |
|
|
110
|
+
| `monty support <status\|enable\|disable\|submit>` | consent + platform reports |
|
|
111
|
+
| `monty skills` | (re)install this skill |
|
package/template/AGENTS.md
CHANGED
|
@@ -4,7 +4,7 @@ Monty is a work OS: your app runs inside a team's workspace, on shared reactive
|
|
|
4
4
|
data, with auth and deployment handled by the platform. **You only write product
|
|
5
5
|
logic.** Everything below is the complete contract.
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
`monty current` confirms which app folder you are in. Never create app folders by hand — `monty create` and `monty connect` do it.
|
|
8
8
|
|
|
9
9
|
## The three files that matter
|
|
10
10
|
|
|
@@ -24,10 +24,11 @@ Folders are managed for you: this app lives in `~/Monty/<slug>`. `monty current`
|
|
|
24
24
|
3. **UI is shadcn/ui, preconfigured — never build components from scratch.**
|
|
25
25
|
Before building any UI piece, run `monty components`. If the capability is
|
|
26
26
|
listed (data tables, kanban, calendar, combobox, file upload, rich text,
|
|
27
|
-
charts, …) install the curated implementation with
|
|
28
|
-
core shadcn components install by bare
|
|
29
|
-
|
|
30
|
-
|
|
27
|
+
charts, …) install the curated implementation with
|
|
28
|
+
`monty components add <name>`; core shadcn components install by bare
|
|
29
|
+
name (`monty components add dialog tabs`). Everything lands in
|
|
30
|
+
`src/components/ui/*` already carrying the Monty theme.
|
|
31
|
+
`monty components docs <name>` shows a component's source before installing.
|
|
31
32
|
Icons from `lucide-react`. Don't install other component libraries or
|
|
32
33
|
write raw-color CSS — use semantic tokens (`bg-background`,
|
|
33
34
|
`text-muted-foreground`, …). Don't edit `src/index.css` theme tokens.
|
package/template/package.json
CHANGED