@montytools/cli 0.1.2 → 0.1.4

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 CHANGED
@@ -63,6 +63,7 @@ async function login() {
63
63
  mkdirSync(MONTY_HOME, { recursive: true });
64
64
  console.log(`logged-in: ${host} (key saved to ~/.monty/config.json)`);
65
65
  console.log(`apps home: ${MONTY_HOME}`);
66
+ installSkills({ silent: false });
66
67
  }
67
68
 
68
69
  // Loopback auth: serve one callback on 127.0.0.1, send the browser to
@@ -129,6 +130,79 @@ function openInBrowser(url) {
129
130
  }
130
131
 
131
132
 
133
+
134
+ // ── agent skills ─────────────────────────────────────────────────────────────
135
+ // The build skill ships inside this package. Primary installer is the
136
+ // cross-agent standard `npx skills add` (same as InsForge) which fans out to
137
+ // every agent's folder — Claude Code, Codex (.agents/skills), Cursor, etc.
138
+ // Manual copy is the offline fallback. A version marker gates the work so
139
+ // ordinary commands stay fast.
140
+ const CLI_ROOT = join(dirname(fileURLToPath(import.meta.url)), "..");
141
+ const CLI_VERSION = JSON.parse(readFileSync(join(CLI_ROOT, "package.json"), "utf8")).version;
142
+ const SKILLS_SRC = join(CLI_ROOT, "skills");
143
+ const GLOBAL_SKILLS_MARKER = join(CONFIG_DIR, "skills-version");
144
+ // InsForge's agent list — enumerate rather than '*' so we only touch real agents.
145
+ const SKILL_AGENTS = ["antigravity", "augment", "claude-code", "cline", "codex", "cursor", "gemini-cli", "github-copilot", "kilo", "qoder", "qwen-code", "roo", "trae", "windsurf"]
146
+ .flatMap((a) => ["-a", a]);
147
+
148
+ function readMarker(path) {
149
+ try {
150
+ return readFileSync(path, "utf8").trim();
151
+ } catch {
152
+ return null;
153
+ }
154
+ }
155
+
156
+ function skillsCliAdd({ global = false, cwd = undefined } = {}) {
157
+ const args = ["-y", "skills", "add", SKILLS_SRC, "-y", "--copy", ...(global ? ["-g"] : []), ...SKILL_AGENTS];
158
+ const res = spawnSync("npx", args, { cwd, stdio: "ignore", timeout: 120_000 });
159
+ return res.status === 0;
160
+ }
161
+
162
+ // Fallbacks for offline/npx-less environments: cover the two standard folders.
163
+ function installSkillsInto(dir) {
164
+ if (!existsSync(SKILLS_SRC)) return;
165
+ for (const name of readdirSync(SKILLS_SRC)) {
166
+ cpSync(join(SKILLS_SRC, name), join(dir, name), { recursive: true });
167
+ }
168
+ }
169
+
170
+ function manualInstall(appDir) {
171
+ if (appDir) {
172
+ installSkillsInto(join(appDir, ".claude", "skills"));
173
+ installSkillsInto(join(appDir, ".agents", "skills"));
174
+ } else {
175
+ installSkillsInto(join(homedir(), ".claude", "skills"));
176
+ installSkillsInto(join(homedir(), ".agents", "skills"));
177
+ }
178
+ }
179
+
180
+ function installSkills({ appDir = null, silent = true, force = false } = {}) {
181
+ if (process.env.MONTY_NO_SKILLS) return;
182
+ try {
183
+ let changed = false;
184
+ if (force || readMarker(GLOBAL_SKILLS_MARKER) !== CLI_VERSION) {
185
+ if (!skillsCliAdd({ global: true })) manualInstall(null);
186
+ mkdirSync(CONFIG_DIR, { recursive: true });
187
+ writeFileSync(GLOBAL_SKILLS_MARKER, CLI_VERSION + "\n");
188
+ changed = true;
189
+ }
190
+ if (appDir) {
191
+ const marker = join(appDir, ".agents", "skills", "monty-build", "VERSION");
192
+ if (force || readMarker(marker) !== CLI_VERSION) {
193
+ if (!skillsCliAdd({ cwd: appDir })) manualInstall(appDir);
194
+ mkdirSync(dirname(marker), { recursive: true });
195
+ writeFileSync(marker, CLI_VERSION + "\n");
196
+ changed = true;
197
+ }
198
+ }
199
+ if (changed && !silent) console.log(`skills: installed for all agents (v${CLI_VERSION})`);
200
+ else if (changed) console.log(`skills: refreshed (v${CLI_VERSION})`);
201
+ } catch {
202
+ /* skills are best-effort — never block a command */
203
+ }
204
+ }
205
+
132
206
  // ── monty current / select / apps ───────────────────────────────────────────
133
207
  // Folder management users never think about: every app lives in ~/Monty,
134
208
  // `current` says where you are, `select` prints the folder for cd $(...).
@@ -265,6 +339,7 @@ async function create() {
265
339
  console.log(`config: WARNING — could not reach ${host}/api/config; copy .env.example to .env.local manually`);
266
340
  }
267
341
 
342
+ installSkills({ appDir: target });
268
343
  console.log(`created: ${target}`);
269
344
  console.log(`next: cd ${target} && pnpm install && monty dev`);
270
345
  }
@@ -506,6 +581,9 @@ function walk(dir) {
506
581
  }
507
582
 
508
583
  // ── dispatch ───────────────────────────────────────────────────────────────
584
+ // Keep agent skills fresh on every invocation (user level + current app).
585
+ installSkills({ appDir: findAppRoot(process.cwd()) });
586
+
509
587
  switch (command) {
510
588
  case "login":
511
589
  await login();
@@ -535,11 +613,15 @@ switch (command) {
535
613
  case "apps":
536
614
  apps();
537
615
  break;
616
+ case "skills":
617
+ installSkills({ appDir: findAppRoot(process.cwd()), silent: false });
618
+ console.log("skills: up to date");
619
+ break;
538
620
  case "deploy":
539
621
  await deploy();
540
622
  break;
541
623
  default:
542
- console.log("usage: monty <login|create|current|select|apps|dev|add|components|docs|deploy>");
624
+ console.log("usage: monty <login|create|current|select|apps|dev|add|components|docs|deploy|skills>");
543
625
  console.log(" login [--host <url>] [--key <mk_...>] sign in (opens your browser to authorize)");
544
626
  console.log(" create <slug> [--name N] [--icon I] stamp a new app into ~/Monty/<slug>");
545
627
  console.log(" dev [--port 5173] run the app locally (sandboxed data)");
@@ -550,5 +632,6 @@ switch (command) {
550
632
  console.log(" select <slug> print an app's folder — cd \"$(monty select x)\"");
551
633
  console.log(" apps list local apps in ~/Monty");
552
634
  console.log(" deploy build + upload this app");
635
+ console.log(" skills install/refresh the agent build skill");
553
636
  process.exit(command ? 1 : 0);
554
637
  }
@@ -0,0 +1,42 @@
1
+ // Best-effort: put the agent build skill where Claude Code looks for it
2
+ // (~/.claude/skills) as soon as the package lands. Never fails the install;
3
+ // set MONTY_NO_SKILLS=1 to opt out. `monty skills` re-runs this any time.
4
+ import { cpSync, existsSync, mkdirSync, readFileSync, readdirSync, writeFileSync } from "node:fs";
5
+ import { homedir } from "node:os";
6
+ import { dirname, join } from "node:path";
7
+ import { fileURLToPath } from "node:url";
8
+
9
+ try {
10
+ if (process.env.MONTY_NO_SKILLS || process.env.CI) process.exit(0);
11
+ const root = join(dirname(fileURLToPath(import.meta.url)), "..");
12
+ const src = join(root, "skills");
13
+ if (!existsSync(src)) process.exit(0);
14
+ const version = JSON.parse(readFileSync(join(root, "package.json"), "utf8")).version;
15
+ const target = join(homedir(), ".claude", "skills");
16
+ mkdirSync(target, { recursive: true });
17
+ for (const name of readdirSync(src)) {
18
+ cpSync(join(src, name), join(target, name), { recursive: true });
19
+ writeFileSync(join(target, name, "VERSION"), version + "\n");
20
+ }
21
+ console.log(`monty: agent skill installed (~/.claude/skills, v${version})`);
22
+ // Codex: managed block in ~/.codex/AGENTS.md when Codex is present.
23
+ const codexDir = join(homedir(), ".codex");
24
+ const skillFile = join(src, "monty-build", "SKILL.md");
25
+ if (existsSync(codexDir) && existsSync(skillFile)) {
26
+ const body = readFileSync(skillFile, "utf8").replace(/^---[\s\S]*?---\n/, "").trim();
27
+ const START = "<!-- monty-build:start";
28
+ const END = "<!-- monty-build:end -->";
29
+ const block = `${START} v${version} -->\n${body}\n${END}`;
30
+ const file = join(codexDir, "AGENTS.md");
31
+ let cur = existsSync(file) ? readFileSync(file, "utf8") : "";
32
+ if (!cur.includes(`${START} v${version} -->`)) {
33
+ const re = new RegExp(`${START}[\\s\\S]*?${END}`);
34
+ cur = re.test(cur) ? cur.replace(re, block) : (cur ? cur.trimEnd() + "\n\n" : "") + block + "\n";
35
+ writeFileSync(file, cur);
36
+ console.log(`monty: agent contract added to ~/.codex/AGENTS.md (v${version})`);
37
+ }
38
+ }
39
+
40
+ } catch {
41
+ /* silent — skills are a convenience, not a dependency */
42
+ }
package/package.json CHANGED
@@ -1,20 +1,22 @@
1
1
  {
2
2
  "name": "@montytools/cli",
3
- "version": "0.1.2",
3
+ "version": "0.1.4",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "monty": "./bin/monty.mjs"
7
7
  },
8
8
  "files": [
9
9
  "bin",
10
- "template"
10
+ "template",
11
+ "skills"
11
12
  ],
12
13
  "engines": {
13
14
  "node": ">=22"
14
15
  },
15
16
  "scripts": {
16
17
  "prepack": "node scripts/bundle-template.mjs",
17
- "typecheck": "node --check bin/monty.mjs"
18
+ "typecheck": "node --check bin/monty.mjs",
19
+ "postinstall": "node bin/postinstall.mjs"
18
20
  },
19
21
  "dependencies": {
20
22
  "esbuild": "^0.28.1"
@@ -0,0 +1,47 @@
1
+ ---
2
+ name: monty-build
3
+ description: Build, run, and deploy Monty workspace apps. Use whenever the task involves a Monty app, monty.config.ts, the monty CLI (create/dev/deploy/add), the @montytools/sdk, or a prompt mentioning usemonty.dev. Covers folder discipline, the build loop, data/auth rules, and error handling.
4
+ ---
5
+
6
+ # Building Monty apps
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.
13
+
14
+ ## Rules
15
+
16
+ 1. **Folders are managed.** Apps live in `~/Monty/<slug>`. `monty current`
17
+ tells you where you are; `cd "$(monty select <slug>)"` jumps to an app;
18
+ `monty apps` lists local ones. Never mkdir app folders by hand.
19
+ 2. **The loop:** `monty create <slug> --name "Name" --icon <lucide-icon>` →
20
+ edit `monty.config.ts` (zod tables) + `src/routes/` → verify with
21
+ `monty dev` (localhost:5173, already authenticated, sandboxed data) →
22
+ `monty deploy`. You are done when deploy prints the URL.
23
+ 3. **One import surface:** `@montytools/sdk` (`defineApp`, zod) and
24
+ `@montytools/sdk/react` (hooks: `useList`, `useInsert`, …). Never import
25
+ Clerk or Convex directly; never fetch external APIs from app code — the
26
+ platform CSP blocks them.
27
+ 4. **Schema is zod in `monty.config.ts`.** Field names `_*`, `updatedAt`,
28
+ `createdBy` are reserved. Push happens automatically on dev/deploy.
29
+ 5. **UI is stock shadcn** (preset already wired). Add curated components with
30
+ `monty add <name>`; browse with `monty components` / `monty docs <name>`.
31
+ 6. **Errors are instructions.** Every failure prints
32
+ `[MontyError CODE] Fix: …` — do exactly what the Fix says; don't guess.
33
+ Typecheck failures block deploy by design.
34
+ 7. **Verify before deploy.** `monty dev` writes to a `#dev` sandbox — live
35
+ team records are never touched, so exercise the app for real.
36
+
37
+ ## CLI reference
38
+
39
+ | command | purpose |
40
+ |---|---|
41
+ | `monty login` | browser sign-in (loopback authorize), once per machine |
42
+ | `monty create <slug>` | stamp a new app into `~/Monty/<slug>` |
43
+ | `monty current` / `select` / `apps` | where am I / jump to app / list local |
44
+ | `monty dev` | run locally on :5173, sandboxed data, auto-auth |
45
+ | `monty add <name…>` | install curated shadcn components |
46
+ | `monty deploy` | build + typecheck + upload; app appears in the workspace |
47
+ | `monty skills` | (re)install this skill for your agent |