@montytools/cli 0.5.3 → 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/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 deploy`
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, { forceManifest = false } = {}) {
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, { forceManifest: ${forceManifest} })));`,
34
+ `process.stdout.write(JSON.stringify(compileApp(app)));`,
35
35
  ].join("\n"));
36
36
  try {
37
37
  await build({
@@ -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 manifest.`,
224
- `// Edit freely — \`monty dev\` / \`monty deploy\` push changes back; another`,
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)},`,
@@ -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
- "NO_MANIFEST",
45
- `"${slug}" has no stored App Manifest (it is a V1 app or has never pushed one). Author monty.config.ts with V2 features and run \`monty dev\` or \`monty deploy\` first.`,
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` save or `monty deploy`), or discard them with --force (a .bak is kept).",
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",
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/views.test.mjs",
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,123 +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/add), the @montytools/sdk, or a prompt mentioning usemonty.dev. Covers folder discipline, the build loop, data/auth rules, and error handling.
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: 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 complete contract lives in the app's own `AGENTS.md`
11
- (nearest-file-wins read it before writing code). This skill is the map, not
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
- There is ONE copy of every app: **Live**, the cloud copy the team uses
15
- (`monty save` updates it). While a dev **session** runs (a dev shell on
16
- the app's LIVE records, often already started for you by the Monty desktop),
17
- workspace admins see the session's version automatically no publish, no
18
- channel switch. "dev"/"prod" mean platform environments, never app states.
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. **Folders are managed.** Apps live in `~/.monty/apps/<id>` (a server-issued
23
- id, minted when `monty create` registers the app so create needs
24
- `monty login` first). `monty current` tells you where you are;
25
- `cd "$(monty select <slug>)"` jumps to an app; `monty apps` lists local
26
- ones. Never mkdir app folders by hand, and never edit the `id:` line in
27
- `monty.config.ts`. The folder IS the app's source: every file in it
28
- rides the source snapshot on `monty save`, so never leave scratch files
29
- here (manifest edits, notes, one-off scripts). Work in the OS temp dir
30
- instead, or pipe `monty schema | <edit> | monty schema set -` needs
31
- no file at all and delete anything temporary before saving.
32
- 2. **The loop:** `monty create <slug> --name "Name" --icon <tabler-icon>` →
33
- (if the prompt includes a `build id`, pass it: `--build <id>` — the
34
- workspace's New app screen tracks your progress live)
35
- `monty install` shape the schema through `monty schema` /
36
- `monty schema set` (a brand-new app's very first session lands its
37
- monty.config.ts once; after that the workspace owns the schema) + edit
38
- `src/routes/`
39
- verify in the session: run `monty dev` once — if the dev shell is already
40
- running (the Monty desktop usually runs it for you) it prints the status,
41
- app URL, and recent log lines, then **exits immediately**; if nothing
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` never run vite, tsc,
56
- pnpm, or npm scripts directly. `monty dev` auto-picks a free port and
57
- prints it; `monty typecheck` builds first when needed. `monty logs`
58
- (add `-f` to follow) is how you read the dev shell's output vite build
59
- errors, browser errors, and save results all land there.
60
- 4. **One import surface:** `@montytools/sdk` (`defineApp`, zod) and
61
- `@montytools/sdk/react` (hooks: `useList`, `useInsert`, …). Never import
62
- Clerk or Convex directly; never fetch external APIs from app code — the
63
- platform CSP blocks them.
64
- 5. **The data schema lives in the WORKSPACE, not in code.** Read it with
65
- `monty schema` (JSON on stdout); change it by editing that JSON and
66
- running `monty schema set <file>`validated server-side, additive by
67
- default. On workspace-owned apps, `monty.config.ts` edits do NOT change
68
- the schema. Give every field a `description` and every enum/multiSelect
69
- a `valueDescriptions` map saying WHEN each option applies that's the
70
- guidance later record-writing agents follow. Declare a page in the
71
- manifest BEFORE shipping its route a save with an undeclared route
72
- refuses with the fix. Field names `_*`, `updatedAt`, `createdBy` are
73
- reserved.
74
- 6. **Prefer a saved view for filtered tables.** If the request is
75
- one table with different filters, sorting, or hidden columns, use
76
- `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:
77
66
  `monty views set leads "Evaluate" --filter '{"pipelineId":null}'`.
78
- Change selected properties or rename it later with
79
- `monty views update <table> <name> ...`; omitted properties stay unchanged.
80
- If that table needs actions, compose `RecordPage` from
81
- `@montytools/sdk/react` and add typed row/header/selection/record actions
82
- with the controls from `@montytools/sdk/ui`. Write the whole page yourself
83
- only when the shared components no longer fit. Custom pages remain the
84
- escape hatch.
85
- 7. **UI is stock shadcn** (preset already wired). Add curated components with
86
- `monty add <name>`; browse with `monty components` / `monty docs <name>`.
87
- How pages should LOOK Lyra surfaces, dark-only, the chart language is
88
- 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.
89
77
  8. **Errors are instructions.** Every failure prints
90
- `[MontyError CODE] Fix: …` do exactly what the Fix says; don't guess.
91
- Typecheck failures block the save by design.
78
+ `[MontyError CODE] Fix: ...`. Do what the Fix says. Typecheck failures
79
+ block the save by design.
92
80
  9. **Edits are real.** The dev shell reads and writes the app's LIVE
93
- records — there is one set of data, and every write journals into the
94
- app's Activity. Exercise the app for real; clean up test rows you
95
- 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.
96
83
  10. **Report platform failures when sharing is enabled.** `monty support
97
- status` shows whether a workspace admin has allowed agent support
98
- reports. If enabled, submit one report when a Monty platform error still
99
- blocks the task after you followed its `Fix` and retried, or when the
100
- requested work needs a missing Monty platform feature. Summarize the
101
- thread. State the observed problem, expected behavior, attempted fixes,
102
- and exact error. Do not paste the full conversation. If sharing is off,
103
- ask the user before running `monty support enable --yes`; never grant
104
- consent yourself. Do not file reports for ordinary app-code bugs or send
105
- 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.
106
91
 
107
92
  ## CLI reference
108
93
 
109
94
  | command | purpose |
110
95
  |---|---|
111
- | `monty login` | browser sign-in (loopback authorize), once per machine |
112
- | `monty create <slug>` | register the app in the workspace + stamp it into `~/.monty/apps/<id>` (needs login) |
113
- | `monty current` / `select` / `apps` | where am I / jump to app / list local |
114
- | `monty install` / `build` / `typecheck` | full lifecycle via the CLI no raw pnpm/vite/tsc |
115
- | `monty dev` | run the app's session, or attach to an already-running one (auto-port, live data, auto-auth) |
116
- | `monty logs [-n N] [-f]` | read/follow the dev shell log the debugging window after every edit |
117
- | `monty add <name…>` | install curated shadcn components |
118
- | `monty schema [slug]` | print the app's stored manifest (tables, pages, metrics) as JSON |
119
- | `monty schema set <file\|->` | write an edited manifest back (validated, CAS, additive by default) |
120
- | `monty views <list\|set\|update\|remove> <table>` | manage the shared saved views on a system record page |
121
- | `monty save ["what changed"]` | push the working copy to the cloud copy, like `git push main` (build + typecheck gate it) |
122
- | `monty support <status\|enable\|disable\|submit>` | manage consent and send a bounded agent-authored platform report |
123
- | `monty skills` | (re)install this skill for your agent |
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 |
@@ -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
- Folders are managed for you: this app lives in `~/Monty/<slug>`. `monty current` confirms where you are; `cd "$(monty select <slug>)"` jumps to any app; never create app folders by hand.
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 `monty add <name>`;
28
- core shadcn components install by bare name (`monty add dialog tabs`).
29
- Everything lands in `src/components/ui/*` already carrying the Monty
30
- theme. `monty docs <name>` shows a component's source before installing.
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.
@@ -106,6 +107,19 @@ await update(row._id, { receipt }); // store descriptor, not
106
107
  const { url } = useFileUrl(app, row.receipt); // authenticated blob: URL for previews/downloads
107
108
  ```
108
109
 
110
+ Open an external HTTPS page with the SDK. Do not use `target="_blank"` or
111
+ `window.open`; the app iframe blocks popups by design. Call `openExternal`
112
+ synchronously from the click or key handler so the host can verify current
113
+ user activation:
114
+
115
+ ```tsx
116
+ import { openExternal } from "@montytools/sdk";
117
+
118
+ <Button onClick={() => openExternal(profile.linkedinUrl)}>
119
+ Open LinkedIn
120
+ </Button>
121
+ ```
122
+
109
123
  ## Derived fields, metrics, pages (V2 — the platform renders these)
110
124
 
111
125
  Your config can carry a whole database app the platform shell renders for
@@ -10,7 +10,7 @@
10
10
  },
11
11
  "dependencies": {
12
12
  "@fontsource-variable/roboto": "^5.2.10",
13
- "@montytools/sdk": "^0.2.3",
13
+ "@montytools/sdk": "^0.2.5",
14
14
  "@tanstack/react-router": "1.170.17",
15
15
  "class-variance-authority": "^0.7.1",
16
16
  "clsx": "^2.1.1",