@lumerahq/cli 0.19.7 → 0.19.8-dev.1

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.
@@ -4,7 +4,7 @@ import {
4
4
  import {
5
5
  createApiClient,
6
6
  isApiErrorStatus
7
- } from "./chunk-WY6UMJNI.js";
7
+ } from "./chunk-SMZESQV2.js";
8
8
  import {
9
9
  findProjectRoot,
10
10
  getAppName
@@ -241,10 +241,25 @@ var ApiClient = class {
241
241
  method: "DELETE"
242
242
  });
243
243
  }
244
- // Agent Skills (for slug-to-ID resolution)
245
- async listAgentSkills() {
246
- const result = await this.request("/api/lm_agent_skills?limit=100");
247
- return result.skills || [];
244
+ // Resolve a list of refs (slugs or IDs) to skill records. Chunks
245
+ // requests so callers don't have to think about the server-side
246
+ // per-request cap.
247
+ async lookupAgentSkills(refs) {
248
+ if (!refs || refs.length === 0) return [];
249
+ const CHUNK = 100;
250
+ const chunks = [];
251
+ for (let i = 0; i < refs.length; i += CHUNK) {
252
+ chunks.push(refs.slice(i, i + CHUNK));
253
+ }
254
+ const responses = await Promise.all(
255
+ chunks.map(
256
+ (chunk) => this.request("/api/lm_agent_skills/lookup", {
257
+ method: "POST",
258
+ body: JSON.stringify({ refs: chunk })
259
+ })
260
+ )
261
+ );
262
+ return responses.flatMap((r) => r.skills || []);
248
263
  }
249
264
  // Agent Invoke
250
265
  async invokeAgent(agentId, message, sessionId) {
@@ -2,9 +2,9 @@ import {
2
2
  deps,
3
3
  projectResourceDepsEnabled,
4
4
  syncDeps
5
- } from "./chunk-RUJRTLHA.js";
5
+ } from "./chunk-2WDZ3QKS.js";
6
6
  import "./chunk-2CR762KB.js";
7
- import "./chunk-WY6UMJNI.js";
7
+ import "./chunk-SMZESQV2.js";
8
8
  import "./chunk-ZH3NVYEQ.js";
9
9
  import "./chunk-FJFIWC7G.js";
10
10
  import "./chunk-PNKVD2UK.js";
@@ -4,13 +4,13 @@ import {
4
4
  import {
5
5
  projectResourceDepsEnabled,
6
6
  syncDeps
7
- } from "./chunk-RUJRTLHA.js";
7
+ } from "./chunk-2WDZ3QKS.js";
8
8
  import {
9
9
  loadEnv
10
10
  } from "./chunk-2CR762KB.js";
11
11
  import {
12
12
  createApiClient
13
- } from "./chunk-WY6UMJNI.js";
13
+ } from "./chunk-SMZESQV2.js";
14
14
  import {
15
15
  findProjectRoot,
16
16
  getApiUrl,
package/dist/index.js CHANGED
@@ -219,39 +219,39 @@ async function main() {
219
219
  switch (command) {
220
220
  // Resource commands
221
221
  case "plan":
222
- await import("./resources-P7WAHOHR.js").then((m) => m.plan(args.slice(1)));
222
+ await import("./resources-EPCEYCFY.js").then((m) => m.plan(args.slice(1)));
223
223
  break;
224
224
  case "apply":
225
- await import("./resources-P7WAHOHR.js").then((m) => m.apply(args.slice(1)));
225
+ await import("./resources-EPCEYCFY.js").then((m) => m.apply(args.slice(1)));
226
226
  break;
227
227
  case "pull":
228
- await import("./resources-P7WAHOHR.js").then((m) => m.pull(args.slice(1)));
228
+ await import("./resources-EPCEYCFY.js").then((m) => m.pull(args.slice(1)));
229
229
  break;
230
230
  case "destroy":
231
- await import("./resources-P7WAHOHR.js").then((m) => m.destroy(args.slice(1)));
231
+ await import("./resources-EPCEYCFY.js").then((m) => m.destroy(args.slice(1)));
232
232
  break;
233
233
  case "list":
234
- await import("./resources-P7WAHOHR.js").then((m) => m.list(args.slice(1)));
234
+ await import("./resources-EPCEYCFY.js").then((m) => m.list(args.slice(1)));
235
235
  break;
236
236
  case "show":
237
- await import("./resources-P7WAHOHR.js").then((m) => m.show(args.slice(1)));
237
+ await import("./resources-EPCEYCFY.js").then((m) => m.show(args.slice(1)));
238
238
  break;
239
239
  case "diff":
240
- await import("./resources-P7WAHOHR.js").then((m) => m.diff(args.slice(1)));
240
+ await import("./resources-EPCEYCFY.js").then((m) => m.diff(args.slice(1)));
241
241
  break;
242
242
  // Development
243
243
  case "dev":
244
- await import("./dev-YYMFCXZ5.js").then((m) => m.dev(args.slice(1)));
244
+ await import("./dev-RENAXSGD.js").then((m) => m.dev(args.slice(1)));
245
245
  break;
246
246
  case "run":
247
- await import("./run-C23KZI4Z.js").then((m) => m.run(args.slice(1)));
247
+ await import("./run-XWXUBWWH.js").then((m) => m.run(args.slice(1)));
248
248
  break;
249
249
  // Project
250
250
  case "init":
251
- await import("./init-7YWMMTBF.js").then((m) => m.init(args.slice(1)));
251
+ await import("./init-J5BNFCSP.js").then((m) => m.init(args.slice(1)));
252
252
  break;
253
253
  case "register":
254
- await import("./register-KJMSMMD6.js").then((m) => m.register(args.slice(1)));
254
+ await import("./register-ZYUFXURK.js").then((m) => m.register(args.slice(1)));
255
255
  break;
256
256
  case "templates":
257
257
  await import("./templates-LNUOTNLN.js").then((m) => m.templates(subcommand, args.slice(2)));
@@ -268,7 +268,7 @@ async function main() {
268
268
  break;
269
269
  // Dependencies
270
270
  case "deps":
271
- await import("./deps-73XZXRYP.js").then((m) => m.deps(args.slice(1)));
271
+ await import("./deps-53AOYHH2.js").then((m) => m.deps(args.slice(1)));
272
272
  break;
273
273
  // Auth
274
274
  case "login":
@@ -7,7 +7,7 @@ import {
7
7
  } from "./chunk-BHYDYR75.js";
8
8
  import {
9
9
  createApiClient
10
- } from "./chunk-WY6UMJNI.js";
10
+ } from "./chunk-SMZESQV2.js";
11
11
  import {
12
12
  getToken,
13
13
  init_auth,
@@ -6,7 +6,7 @@ import {
6
6
  } from "./chunk-BHYDYR75.js";
7
7
  import {
8
8
  createApiClient
9
- } from "./chunk-WY6UMJNI.js";
9
+ } from "./chunk-SMZESQV2.js";
10
10
  import {
11
11
  findProjectRoot,
12
12
  getAppName,
@@ -4,13 +4,13 @@ import {
4
4
  import {
5
5
  projectResourceDepsEnabled,
6
6
  syncDeps
7
- } from "./chunk-RUJRTLHA.js";
7
+ } from "./chunk-2WDZ3QKS.js";
8
8
  import {
9
9
  loadEnv
10
10
  } from "./chunk-2CR762KB.js";
11
11
  import {
12
12
  createApiClient
13
- } from "./chunk-WY6UMJNI.js";
13
+ } from "./chunk-SMZESQV2.js";
14
14
  import {
15
15
  findProjectRoot,
16
16
  getApiUrl,
@@ -1847,19 +1847,41 @@ async function planAgents(api, localAgents, projectId) {
1847
1847
  const remoteByExternalId = new Map(
1848
1848
  remoteAgents.filter((a) => a.external_id && !a.managed).map((a) => [a.external_id, a])
1849
1849
  );
1850
- let skillSlugToId = /* @__PURE__ */ new Map();
1851
- let skillIdToSlug = /* @__PURE__ */ new Map();
1852
- const hasSkillRefs = localAgents.some((a) => a.agent.skills && a.agent.skills.length > 0) || remoteAgents.some((a) => a.skill_ids && a.skill_ids.length > 0);
1853
- if (hasSkillRefs) {
1850
+ const refToId = /* @__PURE__ */ new Map();
1851
+ const skillIdToSlug = /* @__PURE__ */ new Map();
1852
+ const blockedRefs = /* @__PURE__ */ new Set();
1853
+ const planRefs = [
1854
+ .../* @__PURE__ */ new Set([
1855
+ ...localAgents.flatMap((a) => a.agent.skills ?? []),
1856
+ ...remoteAgents.flatMap((a) => a.skill_ids ?? [])
1857
+ ])
1858
+ ];
1859
+ if (planRefs.length > 0) {
1854
1860
  try {
1855
- const skills = await api.listAgentSkills();
1856
- skillSlugToId = new Map(skills.map((s) => [s.slug, s.id]));
1857
- skillIdToSlug = new Map(skills.map((s) => [s.id, s.slug]));
1858
- } catch {
1861
+ const skills = await api.lookupAgentSkills(planRefs);
1862
+ for (const s of skills) {
1863
+ skillIdToSlug.set(s.id, s.slug);
1864
+ if (isAvailableToCustomAgents(s)) {
1865
+ refToId.set(s.slug, s.id);
1866
+ refToId.set(s.id, s.id);
1867
+ } else {
1868
+ blockedRefs.add(s.slug);
1869
+ blockedRefs.add(s.id);
1870
+ }
1871
+ }
1872
+ } catch (e) {
1873
+ console.log(pc2.yellow(` \u26A0 Could not resolve skills for plan: ${e}`));
1859
1874
  }
1860
1875
  }
1861
1876
  for (const { agent, systemPrompt, policyScript } of localAgents) {
1862
1877
  const remote = remoteByExternalId.get(agent.external_id);
1878
+ for (const ref of agent.skills ?? []) {
1879
+ if (blockedRefs.has(ref)) {
1880
+ console.log(pc2.red(` \u2717 Skill "${ref}" is a Lumera platform skill, only available to Lumera-built agents \u2014 apply will fail for this agent`));
1881
+ } else if (!refToId.has(ref)) {
1882
+ console.log(pc2.red(` \u2717 Skill "${ref}" not found \u2014 apply will fail for this agent`));
1883
+ }
1884
+ }
1863
1885
  if (!remote) {
1864
1886
  changes.push({ type: "create", resource: "agent", id: agent.external_id, name: agent.name });
1865
1887
  } else {
@@ -1871,7 +1893,7 @@ async function planAgents(api, localAgents, projectId) {
1871
1893
  if ((remote.policy_script || "").trim() !== (policyScript || "").trim()) diffs.push("policy_script");
1872
1894
  if ((remote.policy_enabled || false) !== (agent.policy_enabled || false)) diffs.push("policy_enabled");
1873
1895
  if ((remote.policy_description || "") !== (agent.policy_description || "")) diffs.push("policy_description");
1874
- const localSkillIds = (agent.skills || []).map((s) => skillSlugToId.get(s) || s).sort();
1896
+ const localSkillIds = (agent.skills || []).map((s) => refToId.get(s)).filter((id) => !!id).sort();
1875
1897
  const remoteSkillIds = [...remote.skill_ids || []].sort();
1876
1898
  if (localSkillIds.join(",") !== remoteSkillIds.join(",")) {
1877
1899
  const addedSlugs = localSkillIds.filter((id) => !remoteSkillIds.includes(id)).map((id) => skillIdToSlug.get(id) || id);
@@ -1895,35 +1917,65 @@ async function planAgents(api, localAgents, projectId) {
1895
1917
  }
1896
1918
  return changes;
1897
1919
  }
1920
+ var TAG_AVAILABLE_TO_CUSTOM_AGENTS = "available_to_custom_agents";
1921
+ function isAvailableToCustomAgents(skill) {
1922
+ if (!skill.managed) return true;
1923
+ return (skill.tags ?? []).includes(TAG_AVAILABLE_TO_CUSTOM_AGENTS);
1924
+ }
1898
1925
  async function applyAgents(api, localAgents, projectId) {
1899
1926
  let errors = 0;
1900
1927
  const remoteAgents = await api.listAgents(projectId ? { project_id: projectId } : void 0);
1901
1928
  const remoteByExternalId = new Map(
1902
1929
  remoteAgents.filter((a) => a.external_id && !a.managed).map((a) => [a.external_id, a])
1903
1930
  );
1904
- let skillMap = /* @__PURE__ */ new Map();
1905
- const hasSkillRefs = localAgents.some((a) => a.agent.skills && a.agent.skills.length > 0);
1906
- if (hasSkillRefs) {
1931
+ const refToId = /* @__PURE__ */ new Map();
1932
+ const blockedRefs = /* @__PURE__ */ new Set();
1933
+ const refs = [...new Set(localAgents.flatMap((a) => a.agent.skills ?? []))];
1934
+ if (refs.length > 0) {
1907
1935
  try {
1908
- const skills = await api.listAgentSkills();
1909
- skillMap = new Map(skills.map((s) => [s.slug, s.id]));
1936
+ const skills = await api.lookupAgentSkills(refs);
1937
+ for (const s of skills) {
1938
+ if (isAvailableToCustomAgents(s)) {
1939
+ refToId.set(s.slug, s.id);
1940
+ refToId.set(s.id, s.id);
1941
+ } else {
1942
+ blockedRefs.add(s.slug);
1943
+ blockedRefs.add(s.id);
1944
+ }
1945
+ }
1910
1946
  } catch (e) {
1911
- console.log(pc2.yellow(` \u26A0 Could not fetch skills for resolution: ${e}`));
1947
+ console.log(pc2.yellow(` \u26A0 Could not resolve skills: ${e}`));
1912
1948
  }
1913
1949
  }
1914
1950
  for (const { agent, systemPrompt, policyScript } of localAgents) {
1915
1951
  const remote = remoteByExternalId.get(agent.external_id);
1916
1952
  const skillIds = [];
1953
+ const blockedForAgent = [];
1954
+ const notFoundForAgent = [];
1917
1955
  if (agent.skills) {
1918
- for (const slug of agent.skills) {
1919
- const id = skillMap.get(slug);
1956
+ for (const ref of agent.skills) {
1957
+ const id = refToId.get(ref);
1920
1958
  if (id) {
1921
1959
  skillIds.push(id);
1960
+ } else if (blockedRefs.has(ref)) {
1961
+ blockedForAgent.push(ref);
1922
1962
  } else {
1923
- console.log(pc2.yellow(` \u26A0 Skill "${slug}" not found, skipping`));
1963
+ notFoundForAgent.push(ref);
1924
1964
  }
1925
1965
  }
1926
1966
  }
1967
+ if (blockedForAgent.length > 0 || notFoundForAgent.length > 0) {
1968
+ console.log(pc2.red(" \u2717"), `${agent.name}:`);
1969
+ if (blockedForAgent.length > 0) {
1970
+ console.log(pc2.red(` skills not available for custom agents: ${blockedForAgent.join(", ")}`));
1971
+ }
1972
+ if (notFoundForAgent.length > 0) {
1973
+ console.log(pc2.red(` skills not found: ${notFoundForAgent.join(", ")}`));
1974
+ }
1975
+ console.log(pc2.dim(` pick skills from GET /api/skills (tags includes "available_to_custom_agents")`));
1976
+ errors++;
1977
+ continue;
1978
+ }
1927
1979
  const payload = {
1928
1980
  external_id: agent.external_id,
1929
1981
  name: agent.name,
@@ -1955,11 +2007,16 @@ async function pullAgents(api, platformDir, filterName, projectId) {
1955
2007
  const agentsDir = join2(platformDir, "agents");
1956
2008
  mkdirSync(agentsDir, { recursive: true });
1957
2009
  const agents = await api.listAgents(projectId ? { project_id: projectId } : void 0);
1958
- let skillIdToSlug = /* @__PURE__ */ new Map();
1959
- try {
1960
- const skills = await api.listAgentSkills();
1961
- skillIdToSlug = new Map(skills.map((s) => [s.id, s.slug]));
1962
- } catch {
2010
+ const skillIdToSlug = /* @__PURE__ */ new Map();
2011
+ const pullRefs = [...new Set(agents.flatMap((a) => a.skill_ids ?? []))];
2012
+ if (pullRefs.length > 0) {
2013
+ try {
2014
+ const skills = await api.lookupAgentSkills(pullRefs);
2015
+ for (const s of skills) skillIdToSlug.set(s.id, s.slug);
2016
+ } catch (e) {
2017
+ console.log(pc2.yellow(` \u26A0 Could not resolve skills for pull: ${e}`));
2018
+ console.log(pc2.yellow(` Pulled configs may omit skills \u2014 review before re-applying.`));
2019
+ }
1963
2020
  }
1964
2021
  for (const agent of agents) {
1965
2022
  if (!agent.external_id || agent.managed) continue;
@@ -3,7 +3,7 @@ import {
3
3
  } from "./chunk-2CR762KB.js";
4
4
  import {
5
5
  createApiClient
6
- } from "./chunk-WY6UMJNI.js";
6
+ } from "./chunk-SMZESQV2.js";
7
7
  import {
8
8
  findProjectRoot,
9
9
  getApiUrl,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lumerahq/cli",
3
- "version": "0.19.7",
3
+ "version": "0.19.8-dev.1",
4
4
  "description": "CLI for building and deploying Lumera apps",
5
5
  "type": "module",
6
6
  "engines": {
@@ -128,7 +128,27 @@ Follow the user's lead. If they tell you exactly what to build, build it. The wo
128
128
  ### Rules
129
129
  7. **Code is source of truth** — Edit files in `platform/`, then deploy with `lumera apply`. Don't edit in the Lumera UI.
130
130
  8. **Keep docs current** — After each slice, update `architecture.md` with what was built (data models, relationships, hook logic, design decisions). Also update the project description at the top of this file (`AGENTS.md`) so it reflects what the project actually does now — not the original template description.
131
- 9. **Commit and push** — After each slice or significant change: `git add -A && git commit -m "descriptive message" && git push`. The sandbox is ephemeral — uncommitted work is lost if recycled.
131
+ 9. **Commit and push** — After each slice or significant change, stage your changes, create a meaningful commit with both a title and description, then push. The sandbox is ephemeral — uncommitted work is lost if recycled.
132
+ - Use a conventional subject (`feat:`, `fix:`, `chore:`, `docs:`, `test:`) that explains the user-visible change, not just the filenames.
133
+ - Include a body that describes why the change was needed, what you changed, and the main areas covered.
134
+ - Avoid generic messages like `update files`, `changes`, `WIP`, or filename-only summaries.
135
+ - Prefer a HEREDOC so multi-line messages are reliable:
136
+ ```bash
137
+ git add -A
138
+ git commit -m "$(cat <<'EOF'
139
+ feat: add invoice approval dashboard
140
+
141
+ Adds the first invoice review slice with a collection schema, seeded
142
+ example data, and a dashboard for approving or rejecting invoices.
143
+
144
+ Covers:
145
+ - invoice collection fields and statuses
146
+ - approval actions in the UI
147
+ - project documentation updates
148
+ EOF
149
+ )"
150
+ git push
151
+ ```
132
152
  10. **Deploy marker** — When your changes need `lumera apply`, include at the end of your response: `<!-- DEPLOY: short commit message -->`. Skip for frontend-only changes.
133
153
 
134
154
  ## File Artifacts