@montytools/cli 0.1.3 → 0.1.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 +82 -22
- package/bin/postinstall.mjs +18 -0
- package/package.json +1 -1
- package/skills/monty-build/SKILL.md +2 -0
package/bin/monty.mjs
CHANGED
|
@@ -132,37 +132,72 @@ function openInBrowser(url) {
|
|
|
132
132
|
|
|
133
133
|
|
|
134
134
|
// ── agent skills ─────────────────────────────────────────────────────────────
|
|
135
|
-
// The build skill ships inside this package
|
|
136
|
-
//
|
|
137
|
-
//
|
|
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.
|
|
138
140
|
const CLI_ROOT = join(dirname(fileURLToPath(import.meta.url)), "..");
|
|
139
141
|
const CLI_VERSION = JSON.parse(readFileSync(join(CLI_ROOT, "package.json"), "utf8")).version;
|
|
140
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]);
|
|
141
147
|
|
|
142
|
-
function
|
|
143
|
-
|
|
144
|
-
|
|
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;
|
|
145
165
|
for (const name of readdirSync(SKILLS_SRC)) {
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
const installed = existsSync(versionFile) ? readFileSync(versionFile, "utf8").trim() : null;
|
|
149
|
-
if (installed === CLI_VERSION) continue;
|
|
150
|
-
cpSync(join(SKILLS_SRC, name), dest, { recursive: true });
|
|
151
|
-
writeFileSync(versionFile, CLI_VERSION + "\n");
|
|
152
|
-
changed = true;
|
|
153
|
-
}
|
|
154
|
-
if (changed && !silent) console.log(`skills: installed -> ${dir} (v${CLI_VERSION})`);
|
|
155
|
-
return changed;
|
|
166
|
+
cpSync(join(SKILLS_SRC, name), join(dir, name), { recursive: true });
|
|
167
|
+
}
|
|
156
168
|
}
|
|
157
169
|
|
|
158
|
-
function
|
|
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 } = {}) {
|
|
159
181
|
if (process.env.MONTY_NO_SKILLS) return;
|
|
160
182
|
try {
|
|
161
|
-
let changed =
|
|
162
|
-
|
|
163
|
-
|
|
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;
|
|
164
189
|
}
|
|
165
|
-
if (
|
|
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})`);
|
|
166
201
|
} catch {
|
|
167
202
|
/* skills are best-effort — never block a command */
|
|
168
203
|
}
|
|
@@ -304,6 +339,27 @@ async function create() {
|
|
|
304
339
|
console.log(`config: WARNING — could not reach ${host}/api/config; copy .env.example to .env.local manually`);
|
|
305
340
|
}
|
|
306
341
|
|
|
342
|
+
// Build tracking: the /new screen minted an id; recording it here lets
|
|
343
|
+
// `monty deploy` resolve it and flips the UI into "agent is working" mode.
|
|
344
|
+
const buildId = flag("build");
|
|
345
|
+
if (buildId && /^[a-z0-9]{10,64}$/i.test(buildId)) {
|
|
346
|
+
mkdirSync(join(target, ".monty"), { recursive: true });
|
|
347
|
+
writeFileSync(join(target, ".monty", "build"), buildId + "\n");
|
|
348
|
+
const cfg = loadConfig();
|
|
349
|
+
if (cfg?.key) {
|
|
350
|
+
try {
|
|
351
|
+
await fetch(`${cfg.host ?? DEFAULT_HOST}/api/build`, {
|
|
352
|
+
method: "POST",
|
|
353
|
+
headers: { authorization: `Bearer ${cfg.key}`, "content-type": "application/json" },
|
|
354
|
+
body: JSON.stringify({ buildId, slug }),
|
|
355
|
+
});
|
|
356
|
+
console.log("build: workspace notified — the New app screen is following along");
|
|
357
|
+
} catch {
|
|
358
|
+
/* progress signal only — never block create */
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
|
|
307
363
|
installSkills({ appDir: target });
|
|
308
364
|
console.log(`created: ${target}`);
|
|
309
365
|
console.log(`next: cd ${target} && pnpm install && monty dev`);
|
|
@@ -463,6 +519,10 @@ async function deploy() {
|
|
|
463
519
|
// 3) Multipart POST to the host.
|
|
464
520
|
const dist = join(appDir, "dist");
|
|
465
521
|
const files = walk(dist);
|
|
522
|
+
const buildFile = join(appDir, ".monty", "build");
|
|
523
|
+
if (existsSync(buildFile)) {
|
|
524
|
+
meta.buildId = readFileSync(buildFile, "utf8").trim();
|
|
525
|
+
}
|
|
466
526
|
const form = new FormData();
|
|
467
527
|
form.set("monty", JSON.stringify(meta));
|
|
468
528
|
let total = 0;
|
|
@@ -588,7 +648,7 @@ switch (command) {
|
|
|
588
648
|
default:
|
|
589
649
|
console.log("usage: monty <login|create|current|select|apps|dev|add|components|docs|deploy|skills>");
|
|
590
650
|
console.log(" login [--host <url>] [--key <mk_...>] sign in (opens your browser to authorize)");
|
|
591
|
-
console.log(" create <slug> [--name N] [--icon I]
|
|
651
|
+
console.log(" create <slug> [--name N] [--icon I] [--build ID] stamp a new app into ~/Monty/<slug>");
|
|
592
652
|
console.log(" dev [--port 5173] run the app locally (sandboxed data)");
|
|
593
653
|
console.log(" add <name...> install curated UI components (see `monty components`)");
|
|
594
654
|
console.log(" components [query] list the curated component catalog");
|
package/bin/postinstall.mjs
CHANGED
|
@@ -19,6 +19,24 @@ try {
|
|
|
19
19
|
writeFileSync(join(target, name, "VERSION"), version + "\n");
|
|
20
20
|
}
|
|
21
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
|
+
|
|
22
40
|
} catch {
|
|
23
41
|
/* silent — skills are a convenience, not a dependency */
|
|
24
42
|
}
|
package/package.json
CHANGED
|
@@ -17,6 +17,8 @@ the territory.
|
|
|
17
17
|
tells you where you are; `cd "$(monty select <slug>)"` jumps to an app;
|
|
18
18
|
`monty apps` lists local ones. Never mkdir app folders by hand.
|
|
19
19
|
2. **The loop:** `monty create <slug> --name "Name" --icon <lucide-icon>` →
|
|
20
|
+
(if the prompt includes a `build id`, pass it: `--build <id>` — the
|
|
21
|
+
workspace's New app screen tracks your progress live) →
|
|
20
22
|
edit `monty.config.ts` (zod tables) + `src/routes/` → verify with
|
|
21
23
|
`monty dev` (localhost:5173, already authenticated, sandboxed data) →
|
|
22
24
|
`monty deploy`. You are done when deploy prints the URL.
|