@montytools/cli 0.1.3 → 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
@@ -132,37 +132,72 @@ function openInBrowser(url) {
132
132
 
133
133
 
134
134
  // ── agent skills ─────────────────────────────────────────────────────────────
135
- // The build skill ships inside this package and is stamped wherever agents
136
- // look for it: ~/.claude/skills (user) and <app>/.claude + <app>/.agents
137
- // (projectself-propagating contract, refreshed whenever the CLI runs).
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 installSkillsInto(dir, { silent = true } = {}) {
143
- if (!existsSync(SKILLS_SRC)) return false;
144
- let changed = false;
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
- const dest = join(dir, name);
147
- const versionFile = join(dest, "VERSION");
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 installSkills({ appDir = null, silent = true } = {}) {
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 = installSkillsInto(join(homedir(), ".claude", "skills"), { silent });
162
- for (const base of appDir ? [join(appDir, ".claude", "skills"), join(appDir, ".agents", "skills")] : []) {
163
- changed = installSkillsInto(base, { silent }) || changed;
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
+ }
164
198
  }
165
- if (changed && silent) console.log(`skills: refreshed (v${CLI_VERSION})`);
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
  }
@@ -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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@montytools/cli",
3
- "version": "0.1.3",
3
+ "version": "0.1.4",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "monty": "./bin/monty.mjs"