@lumerahq/cli 0.19.1 → 0.19.3-dev.0

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.
@@ -3,7 +3,7 @@ import {
3
3
  } from "./chunk-XDTWVFPE.js";
4
4
  import {
5
5
  deploy
6
- } from "./chunk-SU26C4GL.js";
6
+ } from "./chunk-53NOF33P.js";
7
7
  import {
8
8
  loadEnv
9
9
  } from "./chunk-2CR762KB.js";
@@ -25,19 +25,205 @@ import {
25
25
  import "./chunk-PNKVD2UK.js";
26
26
 
27
27
  // src/commands/resources.ts
28
- import pc from "picocolors";
28
+ import pc2 from "picocolors";
29
29
  import prompts from "prompts";
30
30
  import { execFileSync, execSync } from "child_process";
31
31
  import { existsSync, readdirSync, readFileSync, writeFileSync, mkdirSync } from "fs";
32
32
  import { join, resolve } from "path";
33
+
34
+ // src/lib/lint/rules/llm-import.ts
35
+ var FROM_LUMERA_IMPORT_LLM = /^\s*from\s+lumera\s+import\s+\(?\s*(?:[\w\s,]*?\b)?llm\b/;
36
+ var LUMERA_LLM_SUBMODULE = /^\s*(?:from\s+lumera\.llm\b|import\s+lumera\.llm\b)/;
37
+ var llmImportRule = {
38
+ id: "llm-import",
39
+ description: "Flags imports of 'lumera.llm' \u2014 prefer custom agents for LLM/agentic capabilities.",
40
+ appliesTo: ["automation"],
41
+ check(target) {
42
+ const warnings = [];
43
+ const lines = target.source.split(/\r?\n/);
44
+ let inTriple = false;
45
+ for (let i = 0; i < lines.length; i++) {
46
+ const raw = lines[i];
47
+ if (inTriple) {
48
+ if (raw.includes(inTriple)) inTriple = false;
49
+ continue;
50
+ }
51
+ const tripleMatch = raw.match(/("""|''')/);
52
+ if (tripleMatch) {
53
+ const q = tripleMatch[1];
54
+ const rest = raw.slice(raw.indexOf(q) + 3);
55
+ if (!rest.includes(q)) {
56
+ inTriple = q;
57
+ continue;
58
+ }
59
+ }
60
+ const trimmed = raw.trimStart();
61
+ if (trimmed.startsWith("#")) continue;
62
+ if (FROM_LUMERA_IMPORT_LLM.test(raw) || LUMERA_LLM_SUBMODULE.test(raw)) {
63
+ warnings.push({
64
+ ruleId: "llm-import",
65
+ target,
66
+ line: i + 1,
67
+ snippet: raw.trim(),
68
+ message: "Imports 'lumera.llm'. Prefer a custom agent for LLM/agentic capabilities in new automations."
69
+ });
70
+ }
71
+ }
72
+ return warnings;
73
+ }
74
+ };
75
+
76
+ // src/lib/lint/registry.ts
77
+ var ALL_RULES = [llmImportRule];
78
+
79
+ // src/lib/lint/format.ts
80
+ import { relative } from "path";
81
+ import pc from "picocolors";
82
+ function printLintWarnings(warnings, projectRoot) {
83
+ if (warnings.length === 0) return;
84
+ console.log();
85
+ console.log(pc.bold(" Warnings:"));
86
+ for (const w of warnings) {
87
+ const rel = relative(projectRoot, w.target.filePath);
88
+ const loc = w.line ? `${rel}:${w.line}` : rel;
89
+ console.log(` ${pc.yellow("\u26A0")} ${loc} ${pc.dim(`[${w.ruleId}]`)}`);
90
+ console.log(` ${w.message}`);
91
+ if (w.snippet) console.log(pc.dim(` > ${w.snippet}`));
92
+ }
93
+ console.log();
94
+ const n = warnings.length;
95
+ console.log(pc.dim(` ${n} warning${n === 1 ? "" : "s"} \u2014 advisory, will not block apply.`));
96
+ console.log();
97
+ }
98
+ function serializeLintWarnings(warnings, projectRoot) {
99
+ return warnings.map((w) => ({
100
+ ruleId: w.ruleId,
101
+ target: { kind: w.target.kind, name: w.target.name, filePath: relative(projectRoot, w.target.filePath) },
102
+ message: w.message,
103
+ line: w.line,
104
+ snippet: w.snippet
105
+ }));
106
+ }
107
+
108
+ // src/lib/lint/index.ts
109
+ function runLint(ctx) {
110
+ const out = [];
111
+ for (const rule of ALL_RULES) {
112
+ for (const t of ctx.targets) {
113
+ if (!rule.appliesTo.includes(t.kind)) continue;
114
+ try {
115
+ out.push(...rule.check(t, ctx));
116
+ } catch (err) {
117
+ if (process.env.LUMERA_DEBUG) {
118
+ console.error(`[lint] rule "${rule.id}" threw on ${t.filePath}:`, err);
119
+ }
120
+ }
121
+ }
122
+ }
123
+ return out;
124
+ }
125
+ function buildAutomationTargets(localAutomations) {
126
+ return localAutomations.map((a) => ({
127
+ kind: "automation",
128
+ name: a.automation.external_id,
129
+ filePath: a.filePath,
130
+ source: a.code
131
+ }));
132
+ }
133
+
134
+ // src/commands/resources.ts
33
135
  init_auth();
34
- function detectPackageManager() {
35
- for (const pm of ["bun", "pnpm", "yarn", "npm"]) {
36
- try {
37
- execFileSync(pm, ["--version"], { stdio: "ignore" });
38
- return pm;
39
- } catch {
136
+ function safeLint(projectRoot, localAutomations) {
137
+ try {
138
+ return runLint({ projectRoot, targets: buildAutomationTargets(localAutomations) });
139
+ } catch (err) {
140
+ if (process.env.LUMERA_DEBUG) console.error("[lint] pass failed:", err);
141
+ return [];
142
+ }
143
+ }
144
+ function safePrintLint(warnings, projectRoot) {
145
+ try {
146
+ printLintWarnings(warnings, projectRoot);
147
+ } catch (err) {
148
+ if (process.env.LUMERA_DEBUG) console.error("[lint] print failed:", err);
149
+ }
150
+ }
151
+ var PACKAGE_MANAGERS = ["bun", "pnpm", "yarn", "npm"];
152
+ var PACKAGE_MANAGER_VALUE_FLAGS = /* @__PURE__ */ new Set(["--package-manager"]);
153
+ function isPackageManager(value) {
154
+ return PACKAGE_MANAGERS.includes(value);
155
+ }
156
+ function commandAvailable(command) {
157
+ try {
158
+ execFileSync(command, ["--version"], { stdio: "ignore" });
159
+ return true;
160
+ } catch {
161
+ return false;
162
+ }
163
+ }
164
+ function parsePackageManagerSpecifier(value) {
165
+ const raw = (value || "").trim();
166
+ if (!raw) return void 0;
167
+ const name = raw.split("@")[0];
168
+ return isPackageManager(name) ? name : void 0;
169
+ }
170
+ function packageManagerFromPackageJson(projectRoot) {
171
+ try {
172
+ const pkg = JSON.parse(readFileSync(join(projectRoot, "package.json"), "utf-8"));
173
+ return parsePackageManagerSpecifier(pkg.packageManager);
174
+ } catch {
175
+ return void 0;
176
+ }
177
+ }
178
+ function getFlagValue(args, name) {
179
+ const long = `--${name}`;
180
+ for (let i = 0; i < args.length; i++) {
181
+ const arg = args[i];
182
+ if (arg === long) {
183
+ const next = args[i + 1];
184
+ return next && !next.startsWith("-") ? next : void 0;
185
+ }
186
+ if (arg.startsWith(`${long}=`)) {
187
+ return arg.slice(long.length + 1);
188
+ }
189
+ }
190
+ return void 0;
191
+ }
192
+ function getPositionalArgs(args) {
193
+ const out = [];
194
+ for (let i = 0; i < args.length; i++) {
195
+ const arg = args[i];
196
+ if (PACKAGE_MANAGER_VALUE_FLAGS.has(arg)) {
197
+ i++;
198
+ continue;
40
199
  }
200
+ if (arg.startsWith("-")) continue;
201
+ out.push(arg);
202
+ }
203
+ return out;
204
+ }
205
+ function detectPackageManager(projectRoot, override) {
206
+ const requested = parsePackageManagerSpecifier(override || process.env.LUMERA_PACKAGE_MANAGER);
207
+ if (requested) {
208
+ if (!commandAvailable(requested)) {
209
+ throw new Error(`Package manager '${requested}' was requested but is not available on PATH`);
210
+ }
211
+ return requested;
212
+ }
213
+ const declared = packageManagerFromPackageJson(projectRoot);
214
+ if (declared && commandAvailable(declared)) return declared;
215
+ const lockfileManagers = [
216
+ { pm: "pnpm", file: "pnpm-lock.yaml" },
217
+ { pm: "bun", file: "bun.lockb" },
218
+ { pm: "bun", file: "bun.lock" },
219
+ { pm: "yarn", file: "yarn.lock" },
220
+ { pm: "npm", file: "package-lock.json" }
221
+ ];
222
+ for (const { pm, file } of lockfileManagers) {
223
+ if (existsSync(join(projectRoot, file)) && commandAvailable(pm)) return pm;
224
+ }
225
+ for (const pm of ["pnpm", "bun", "yarn", "npm"]) {
226
+ if (commandAvailable(pm)) return pm;
41
227
  }
42
228
  return "npm";
43
229
  }
@@ -267,13 +453,13 @@ function normalizeLocalAutomation(config) {
267
453
  }
268
454
  function showPlanHelp() {
269
455
  console.log(`
270
- ${pc.dim("Usage:")}
456
+ ${pc2.dim("Usage:")}
271
457
  lumera plan [resource]
272
458
 
273
- ${pc.dim("Description:")}
459
+ ${pc2.dim("Description:")}
274
460
  Preview changes between local files and remote state.
275
461
 
276
- ${pc.dim("Resources:")}
462
+ ${pc2.dim("Resources:")}
277
463
  (none) Plan all resources
278
464
  collections Plan only collections
279
465
  collections/<name> Plan single collection
@@ -286,7 +472,7 @@ ${pc.dim("Resources:")}
286
472
  mailboxes/<slug> Plan single mailbox
287
473
  app Plan app deployment
288
474
 
289
- ${pc.dim("Examples:")}
475
+ ${pc2.dim("Examples:")}
290
476
  lumera plan # Plan all resources
291
477
  lumera plan collections # Plan only collections
292
478
  lumera plan automations/sync # Plan single automation
@@ -295,13 +481,13 @@ ${pc.dim("Examples:")}
295
481
  }
296
482
  function showApplyHelp() {
297
483
  console.log(`
298
- ${pc.dim("Usage:")}
484
+ ${pc2.dim("Usage:")}
299
485
  lumera apply [resource]
300
486
 
301
- ${pc.dim("Description:")}
487
+ ${pc2.dim("Description:")}
302
488
  Create or update resources from local files.
303
489
 
304
- ${pc.dim("Resources:")}
490
+ ${pc2.dim("Resources:")}
305
491
  (none) Apply all resources
306
492
  collections Apply only collections
307
493
  collections/<name> Apply single collection
@@ -314,33 +500,38 @@ ${pc.dim("Resources:")}
314
500
  mailboxes/<slug> Apply single mailbox
315
501
  app Deploy the frontend app
316
502
 
317
- ${pc.dim("Options:")}
503
+ ${pc2.dim("Options:")}
318
504
  --yes, -y Skip confirmation prompt (for CI/CD)
319
505
  --skip-build Skip build step when applying app
506
+ --no-app Apply resources only; do not build/deploy the app
507
+ --resources-only Alias for --no-app
508
+ --package-manager Package manager for app builds (pnpm, bun, yarn, npm)
320
509
 
321
- ${pc.dim("Examples:")}
510
+ ${pc2.dim("Examples:")}
322
511
  lumera apply # Apply everything (shows plan, asks to confirm)
323
512
  lumera apply collections # Apply all collections
324
513
  lumera apply collections/users # Apply single collection
325
514
  lumera apply agents -y # Apply agents without confirmation
326
515
  lumera apply app # Deploy frontend
327
516
  lumera apply app --skip-build # Deploy without rebuilding
517
+ lumera apply --no-app -y # Apply resources without app build/deploy
518
+ lumera apply app --package-manager pnpm
328
519
  `);
329
520
  }
330
521
  function showPullHelp() {
331
522
  console.log(`
332
- ${pc.dim("Usage:")}
523
+ ${pc2.dim("Usage:")}
333
524
  lumera pull [resource] [--force]
334
525
 
335
- ${pc.dim("Description:")}
526
+ ${pc2.dim("Description:")}
336
527
  Download remote state to local files.
337
528
  Refuses to overwrite local files that have uncommitted changes
338
529
  (use --force to override, or 'lumera diff' to inspect first).
339
530
 
340
- ${pc.dim("Options:")}
531
+ ${pc2.dim("Options:")}
341
532
  --force, -f Overwrite local files even if they have changes
342
533
 
343
- ${pc.dim("Resources:")}
534
+ ${pc2.dim("Resources:")}
344
535
  (none) Pull all resources
345
536
  collections Pull only collections
346
537
  collections/<name> Pull single collection
@@ -352,7 +543,7 @@ ${pc.dim("Resources:")}
352
543
  mailboxes Pull only mailboxes
353
544
  mailboxes/<slug> Pull single mailbox
354
545
 
355
- ${pc.dim("Examples:")}
546
+ ${pc2.dim("Examples:")}
356
547
  lumera pull # Pull all (safe \u2014 warns on conflicts)
357
548
  lumera pull agents # Pull only agents
358
549
  lumera pull --force # Pull all, overwrite local changes
@@ -361,13 +552,13 @@ ${pc.dim("Examples:")}
361
552
  }
362
553
  function showDestroyHelp() {
363
554
  console.log(`
364
- ${pc.dim("Usage:")}
555
+ ${pc2.dim("Usage:")}
365
556
  lumera destroy [resource]
366
557
 
367
- ${pc.dim("Description:")}
558
+ ${pc2.dim("Description:")}
368
559
  Delete resources from remote.
369
560
 
370
- ${pc.dim("Resources:")}
561
+ ${pc2.dim("Resources:")}
371
562
  (none) Destroy all resources
372
563
  collections Destroy only collections
373
564
  collections/<name> Destroy single collection
@@ -378,11 +569,11 @@ ${pc.dim("Resources:")}
378
569
  agents/<name> Destroy single agent
379
570
  app Delete app registration
380
571
 
381
- ${pc.dim("Options:")}
572
+ ${pc2.dim("Options:")}
382
573
  --confirm Skip confirmation prompt
383
574
  --force-cycles Remove relation fields to break circular references before deleting
384
575
 
385
- ${pc.dim("Examples:")}
576
+ ${pc2.dim("Examples:")}
386
577
  lumera destroy # Destroy everything
387
578
  lumera destroy collections/users # Destroy single collection
388
579
  lumera destroy app # Delete app registration
@@ -390,13 +581,13 @@ ${pc.dim("Examples:")}
390
581
  }
391
582
  function showListHelp() {
392
583
  console.log(`
393
- ${pc.dim("Usage:")}
584
+ ${pc2.dim("Usage:")}
394
585
  lumera list [type] [--all]
395
586
 
396
- ${pc.dim("Description:")}
587
+ ${pc2.dim("Description:")}
397
588
  List resources with status. By default, remote-only resources are hidden.
398
589
 
399
- ${pc.dim("Types:")}
590
+ ${pc2.dim("Types:")}
400
591
  (none) List all resources
401
592
  collections List only collections
402
593
  automations List only automations
@@ -404,10 +595,10 @@ ${pc.dim("Types:")}
404
595
  agents List only agents
405
596
  mailboxes List only mailboxes
406
597
 
407
- ${pc.dim("Options:")}
598
+ ${pc2.dim("Options:")}
408
599
  --all Include remote-only resources
409
600
 
410
- ${pc.dim("Examples:")}
601
+ ${pc2.dim("Examples:")}
411
602
  lumera list # List local resources
412
603
  lumera list --all # Include remote-only resources
413
604
  lumera list collections # List only collections
@@ -415,13 +606,13 @@ ${pc.dim("Examples:")}
415
606
  }
416
607
  function showShowHelp() {
417
608
  console.log(`
418
- ${pc.dim("Usage:")}
609
+ ${pc2.dim("Usage:")}
419
610
  lumera show <resource>
420
611
 
421
- ${pc.dim("Description:")}
612
+ ${pc2.dim("Description:")}
422
613
  Show details of a single resource.
423
614
 
424
- ${pc.dim("Resources:")}
615
+ ${pc2.dim("Resources:")}
425
616
  collections/<name> Show collection details
426
617
  automations/<name> Show automation details
427
618
  hooks/<name> Show hook details
@@ -429,7 +620,7 @@ ${pc.dim("Resources:")}
429
620
  mailboxes/<slug> Show mailbox details
430
621
  app Show app details
431
622
 
432
- ${pc.dim("Examples:")}
623
+ ${pc2.dim("Examples:")}
433
624
  lumera show collections/users # Show collection details
434
625
  lumera show automations/sync # Show automation details
435
626
  lumera show app # Show app details
@@ -444,7 +635,7 @@ function parseResource(resourcePath) {
444
635
  const name = parts.slice(1).join("/") || null;
445
636
  const validTypes = ["collections", "automations", "hooks", "agents", "mailboxes", "app"];
446
637
  if (!validTypes.includes(type)) {
447
- console.log(pc.red(` Unknown resource type "${type}". Valid types: ${validTypes.join(", ")}`));
638
+ console.log(pc2.red(` Unknown resource type "${type}". Valid types: ${validTypes.join(", ")}`));
448
639
  process.exit(1);
449
640
  }
450
641
  return { type, name };
@@ -509,9 +700,9 @@ function loadLocalCollections(platformDir, filterName) {
509
700
  }
510
701
  }
511
702
  if (errors.length > 0) {
512
- console.log(pc.red(" Collection errors:"));
703
+ console.log(pc2.red(" Collection errors:"));
513
704
  for (const err of errors) {
514
- console.log(pc.red(` \u2717 ${err}`));
705
+ console.log(pc2.red(` \u2717 ${err}`));
515
706
  }
516
707
  throw new Error(`Found ${errors.length} collection error(s)`);
517
708
  }
@@ -569,15 +760,15 @@ function loadLocalAutomations(platformDir, filterName, appName) {
569
760
  if (appName) {
570
761
  code = code.replaceAll("{{app}}", appName);
571
762
  }
572
- automations.push({ automation: config, code });
763
+ automations.push({ automation: config, code, filePath: mainPath });
573
764
  } catch (e) {
574
765
  errors.push(`${entry.name}: failed to parse config.json - ${e}`);
575
766
  }
576
767
  }
577
768
  if (errors.length > 0) {
578
- console.log(pc.red(" Automation errors:"));
769
+ console.log(pc2.red(" Automation errors:"));
579
770
  for (const err of errors) {
580
- console.log(pc.red(` \u2717 ${err}`));
771
+ console.log(pc2.red(` \u2717 ${err}`));
581
772
  }
582
773
  throw new Error(`Found ${errors.length} automation error(s)`);
583
774
  }
@@ -595,14 +786,14 @@ function loadLocalHooks(platformDir, filterName, appName) {
595
786
  const content = readFileSync(filePath, "utf-8");
596
787
  const config = parseHookConfig(content);
597
788
  if (!config) {
598
- console.log(pc.yellow(` \u26A0 Skipping ${file}: could not parse config export`));
789
+ console.log(pc2.yellow(` \u26A0 Skipping ${file}: could not parse config export`));
599
790
  continue;
600
791
  }
601
792
  if (!config.external_id) {
602
793
  if (appName) {
603
794
  config.external_id = `${appName}:${file.replace(/\.(js|ts)$/, "")}`;
604
795
  } else {
605
- console.log(pc.yellow(` \u26A0 Skipping ${file}: missing external_id in config`));
796
+ console.log(pc2.yellow(` \u26A0 Skipping ${file}: missing external_id in config`));
606
797
  continue;
607
798
  }
608
799
  }
@@ -917,7 +1108,7 @@ function loadLocalMailboxes(platformDir, filterName) {
917
1108
  if (!file.endsWith(".json")) continue;
918
1109
  const slug = file.replace(/\.json$/, "").trim();
919
1110
  if (!slug) {
920
- console.log(pc.yellow(` \u26A0 Skipping ${file}: empty filename`));
1111
+ console.log(pc2.yellow(` \u26A0 Skipping ${file}: empty filename`));
921
1112
  continue;
922
1113
  }
923
1114
  if (filterName && slug !== filterName) {
@@ -928,11 +1119,11 @@ function loadLocalMailboxes(platformDir, filterName) {
928
1119
  try {
929
1120
  raw = JSON.parse(readFileSync(filePath, "utf-8"));
930
1121
  } catch (e) {
931
- console.log(pc.yellow(` \u26A0 Skipping ${file}: invalid JSON (${e.message})`));
1122
+ console.log(pc2.yellow(` \u26A0 Skipping ${file}: invalid JSON (${e.message})`));
932
1123
  continue;
933
1124
  }
934
1125
  if (!isPlainObject(raw)) {
935
- console.log(pc.yellow(` \u26A0 Skipping ${file}: top-level value must be an object`));
1126
+ console.log(pc2.yellow(` \u26A0 Skipping ${file}: top-level value must be an object`));
936
1127
  continue;
937
1128
  }
938
1129
  const descriptionRaw = raw["description"];
@@ -983,17 +1174,17 @@ async function applyMailboxes(api, localMailboxes) {
983
1174
  const localDesc = (mb.description ?? "").trim();
984
1175
  const remoteDesc = (existing.description ?? "").trim();
985
1176
  if (localDesc !== remoteDesc) {
986
- console.log(pc.yellow(" \u26A0"), `mailbox ${mb.slug}: description drift not reconcilable by CLI (no PATCH endpoint yet) \u2014 update in the UI or DB`);
1177
+ console.log(pc2.yellow(" \u26A0"), `mailbox ${mb.slug}: description drift not reconcilable by CLI (no PATCH endpoint yet) \u2014 update in the UI or DB`);
987
1178
  } else {
988
- console.log(pc.green(" \u2713"), pc.dim(`mailbox ${mb.slug} already exists (${existing.email})`));
1179
+ console.log(pc2.green(" \u2713"), pc2.dim(`mailbox ${mb.slug} already exists (${existing.email})`));
989
1180
  }
990
1181
  continue;
991
1182
  }
992
1183
  try {
993
1184
  const created = await api.createMailbox({ slug: mb.slug, description: mb.description });
994
- console.log(pc.green(" \u2713"), `created mailbox ${mb.slug} ${pc.dim(`\u2192 ${created.email}`)}`);
1185
+ console.log(pc2.green(" \u2713"), `created mailbox ${mb.slug} ${pc2.dim(`\u2192 ${created.email}`)}`);
995
1186
  } catch (e) {
996
- console.log(pc.red(" \u2717"), `mailbox ${mb.slug}: ${e.message}`);
1187
+ console.log(pc2.red(" \u2717"), `mailbox ${mb.slug}: ${e.message}`);
997
1188
  errors++;
998
1189
  }
999
1190
  }
@@ -1002,7 +1193,7 @@ async function applyMailboxes(api, localMailboxes) {
1002
1193
  async function pullMailboxes(api, platformDir, filterName) {
1003
1194
  const mailboxes = await api.listMailboxes();
1004
1195
  if (mailboxes.length === 0) {
1005
- console.log(pc.dim(" (no mailboxes)"));
1196
+ console.log(pc2.dim(" (no mailboxes)"));
1006
1197
  return;
1007
1198
  }
1008
1199
  const outDir = join(platformDir, "mailboxes");
@@ -1016,11 +1207,11 @@ async function pullMailboxes(api, platformDir, filterName) {
1016
1207
  if (mb.description) body.description = mb.description;
1017
1208
  const file = join(outDir, `${toSafeFilename(mb.slug)}.json`);
1018
1209
  writeFileSync(file, JSON.stringify(body, null, 2) + "\n");
1019
- console.log(pc.green(" \u2713"), `pulled mailbox ${mb.slug} ${pc.dim(`\u2192 ${file}`)}`);
1210
+ console.log(pc2.green(" \u2713"), `pulled mailbox ${mb.slug} ${pc2.dim(`\u2192 ${file}`)}`);
1020
1211
  count++;
1021
1212
  }
1022
1213
  if (count === 0 && filterName) {
1023
- console.log(pc.yellow(` \u26A0 mailbox '${filterName}' not found on remote`));
1214
+ console.log(pc2.yellow(` \u26A0 mailbox '${filterName}' not found on remote`));
1024
1215
  }
1025
1216
  }
1026
1217
  async function planHooks(api, localHooks, collections) {
@@ -1031,7 +1222,7 @@ async function planHooks(api, localHooks, collections) {
1031
1222
  const remote = remoteByExternalId.get(hook.external_id);
1032
1223
  const collectionId = collections.get(hook.collection);
1033
1224
  if (!collectionId) {
1034
- console.log(pc.yellow(` \u26A0 Skipping ${fileName}: collection '${hook.collection}' not found`));
1225
+ console.log(pc2.yellow(` \u26A0 Skipping ${fileName}: collection '${hook.collection}' not found`));
1035
1226
  continue;
1036
1227
  }
1037
1228
  if (!remote) {
@@ -1079,9 +1270,9 @@ async function applyCollections(api, localCollections) {
1079
1270
  for (const local of localCollections) {
1080
1271
  try {
1081
1272
  await api.ensureCollection(local.name, { id: local.id });
1082
- console.log(pc.green(" \u2713"), `${local.name}`);
1273
+ console.log(pc2.green(" \u2713"), `${local.name}`);
1083
1274
  } catch (e) {
1084
- console.log(pc.red(" \u2717"), `${local.name}: ${e}`);
1275
+ console.log(pc2.red(" \u2717"), `${local.name}: ${e}`);
1085
1276
  pass1Failed.add(local.name);
1086
1277
  errors++;
1087
1278
  }
@@ -1091,9 +1282,9 @@ async function applyCollections(api, localCollections) {
1091
1282
  const apiFormat = convertCollectionToApiFormat(local);
1092
1283
  try {
1093
1284
  await api.ensureCollection(local.name, apiFormat);
1094
- console.log(pc.green(" \u2713"), `${local.name} (schema)`);
1285
+ console.log(pc2.green(" \u2713"), `${local.name} (schema)`);
1095
1286
  } catch (e) {
1096
- console.log(pc.red(" \u2717"), `${local.name} (schema): ${e}`);
1287
+ console.log(pc2.red(" \u2717"), `${local.name} (schema): ${e}`);
1097
1288
  errors++;
1098
1289
  }
1099
1290
  }
@@ -1102,9 +1293,9 @@ async function applyCollections(api, localCollections) {
1102
1293
  const apiFormat = convertCollectionToApiFormat(local);
1103
1294
  try {
1104
1295
  await api.ensureCollection(local.name, apiFormat);
1105
- console.log(pc.green(" \u2713"), `${local.name}`);
1296
+ console.log(pc2.green(" \u2713"), `${local.name}`);
1106
1297
  } catch (e) {
1107
- console.log(pc.red(" \u2717"), `${local.name}: ${e}`);
1298
+ console.log(pc2.red(" \u2717"), `${local.name}: ${e}`);
1108
1299
  errors++;
1109
1300
  }
1110
1301
  }
@@ -1134,11 +1325,11 @@ async function applyAutomations(api, localAutomations, projectId) {
1134
1325
  if (remote) {
1135
1326
  await api.updateAutomation(remote.id, payload);
1136
1327
  automationId = remote.id;
1137
- console.log(pc.green(" \u2713"), `${automation.name} (updated)`);
1328
+ console.log(pc2.green(" \u2713"), `${automation.name} (updated)`);
1138
1329
  } else {
1139
1330
  const created = await api.createAutomation(payload);
1140
1331
  automationId = created.id;
1141
- console.log(pc.green(" \u2713"), `${automation.name} (created)`);
1332
+ console.log(pc2.green(" \u2713"), `${automation.name} (created)`);
1142
1333
  }
1143
1334
  const localPresets = automation.inputs?.presets;
1144
1335
  if (localPresets) {
@@ -1148,7 +1339,7 @@ async function applyAutomations(api, localAutomations, projectId) {
1148
1339
  await setSchedule(api, automationId, automation.schedule, localPresets || {});
1149
1340
  } else if (remote?.schedule) {
1150
1341
  await api.updateAutomation(automationId, { schedule: "", schedule_tz: "" });
1151
- console.log(pc.dim(` Cleared schedule`));
1342
+ console.log(pc2.dim(` Cleared schedule`));
1152
1343
  }
1153
1344
  if (localPresets) {
1154
1345
  const current = await api.getAutomation(automationId).catch(() => null);
@@ -1162,7 +1353,7 @@ async function applyAutomations(api, localAutomations, projectId) {
1162
1353
  }
1163
1354
  }
1164
1355
  } catch (e) {
1165
- console.log(pc.red(" \u2717"), `${automation.name}: ${e}`);
1356
+ console.log(pc2.red(" \u2717"), `${automation.name}: ${e}`);
1166
1357
  errors++;
1167
1358
  }
1168
1359
  }
@@ -1177,13 +1368,13 @@ async function syncPresets(api, automationId, localPresets) {
1177
1368
  try {
1178
1369
  if (existing) {
1179
1370
  await api.updatePreset(existing.id, { name: presetName, inputs: preset.inputs });
1180
- console.log(pc.dim(` Updated preset: ${presetName}`));
1371
+ console.log(pc2.dim(` Updated preset: ${presetName}`));
1181
1372
  } else {
1182
1373
  await api.createPreset(automationId, { name: presetName, inputs: preset.inputs });
1183
- console.log(pc.dim(` Created preset: ${presetName}`));
1374
+ console.log(pc2.dim(` Created preset: ${presetName}`));
1184
1375
  }
1185
1376
  } catch (e) {
1186
- console.log(pc.yellow(` \u26A0 Failed to sync preset ${presetName}: ${e}`));
1377
+ console.log(pc2.yellow(` \u26A0 Failed to sync preset ${presetName}: ${e}`));
1187
1378
  }
1188
1379
  }
1189
1380
  }
@@ -1198,9 +1389,9 @@ async function deleteStalePresets(api, automationId, localPresets, preservePrese
1198
1389
  }
1199
1390
  try {
1200
1391
  await api.deletePreset(remote.id);
1201
- console.log(pc.dim(` Deleted preset: ${remote.name}`));
1392
+ console.log(pc2.dim(` Deleted preset: ${remote.name}`));
1202
1393
  } catch (e) {
1203
- console.log(pc.yellow(` \u26A0 Failed to delete preset ${remote.name}: ${e}`));
1394
+ console.log(pc2.yellow(` \u26A0 Failed to delete preset ${remote.name}: ${e}`));
1204
1395
  }
1205
1396
  }
1206
1397
  }
@@ -1209,7 +1400,7 @@ async function setSchedule(api, automationId, schedule, localPresets) {
1209
1400
  const remotePresets = await api.listPresets(automationId);
1210
1401
  const preset = remotePresets.find((p) => p.name === presetName);
1211
1402
  if (!preset) {
1212
- console.log(pc.yellow(` \u26A0 Schedule preset '${schedule.preset}' not found, skipping schedule`));
1403
+ console.log(pc2.yellow(` \u26A0 Schedule preset '${schedule.preset}' not found, skipping schedule`));
1213
1404
  return;
1214
1405
  }
1215
1406
  try {
@@ -1218,9 +1409,9 @@ async function setSchedule(api, automationId, schedule, localPresets) {
1218
1409
  schedule_tz: schedule.timezone || "UTC",
1219
1410
  schedule_preset_id: preset.id
1220
1411
  });
1221
- console.log(pc.dim(` Set schedule: ${schedule.cron}`));
1412
+ console.log(pc2.dim(` Set schedule: ${schedule.cron}`));
1222
1413
  } catch (e) {
1223
- console.log(pc.yellow(` \u26A0 Failed to set schedule: ${e}`));
1414
+ console.log(pc2.yellow(` \u26A0 Failed to set schedule: ${e}`));
1224
1415
  }
1225
1416
  }
1226
1417
  async function applyHooks(api, localHooks, collections, projectId) {
@@ -1231,7 +1422,7 @@ async function applyHooks(api, localHooks, collections, projectId) {
1231
1422
  const remote = remoteByExternalId.get(hook.external_id);
1232
1423
  const collectionId = collections.get(hook.collection);
1233
1424
  if (!collectionId) {
1234
- console.log(pc.red(` \u2717 ${fileName}: collection '${hook.collection}' not found. Apply the collection first or use 'lumera apply' to apply all resources.`));
1425
+ console.log(pc2.red(` \u2717 ${fileName}: collection '${hook.collection}' not found. Apply the collection first or use 'lumera apply' to apply all resources.`));
1235
1426
  errors++;
1236
1427
  continue;
1237
1428
  }
@@ -1250,13 +1441,13 @@ async function applyHooks(api, localHooks, collections, projectId) {
1250
1441
  try {
1251
1442
  if (remote) {
1252
1443
  await api.updateHook(remote.id, payload);
1253
- console.log(pc.green(" \u2713"), `${payload.name} (updated)`);
1444
+ console.log(pc2.green(" \u2713"), `${payload.name} (updated)`);
1254
1445
  } else {
1255
1446
  await api.createHook(payload);
1256
- console.log(pc.green(" \u2713"), `${payload.name} (created)`);
1447
+ console.log(pc2.green(" \u2713"), `${payload.name} (created)`);
1257
1448
  }
1258
1449
  } catch (e) {
1259
- console.log(pc.red(" \u2717"), `${payload.name}: ${e}`);
1450
+ console.log(pc2.red(" \u2717"), `${payload.name}: ${e}`);
1260
1451
  errors++;
1261
1452
  }
1262
1453
  }
@@ -1271,9 +1462,9 @@ async function applyApp(args) {
1271
1462
  const appTitle = getAppTitle(projectRoot);
1272
1463
  const apiUrl = getApiUrl();
1273
1464
  if (!skipBuild) {
1274
- console.log(pc.dim(" Building..."));
1465
+ console.log(pc2.dim(" Building..."));
1275
1466
  try {
1276
- const pm = detectPackageManager();
1467
+ const pm = detectPackageManager(projectRoot, getFlagValue(args, "package-manager"));
1277
1468
  execSync(`${pm} run build`, { cwd: projectRoot, stdio: "inherit" });
1278
1469
  } catch {
1279
1470
  throw new Error("Build failed");
@@ -1329,7 +1520,7 @@ async function pullCollections(api, platformDir, filterName, appName) {
1329
1520
  const fileName = toSafeFilename(localName);
1330
1521
  const filePath = join(collectionsDir, `${fileName}.json`);
1331
1522
  writeFileSync(filePath, JSON.stringify(localFormat, null, 2) + "\n");
1332
- console.log(pc.green(" \u2713"), `${localName} \u2192 collections/${fileName}.json`);
1523
+ console.log(pc2.green(" \u2713"), `${localName} \u2192 collections/${fileName}.json`);
1333
1524
  }
1334
1525
  }
1335
1526
  async function pullAutomations(api, platformDir, filterName, projectId) {
@@ -1379,7 +1570,7 @@ async function pullAutomations(api, platformDir, filterName, projectId) {
1379
1570
  }
1380
1571
  writeFileSync(join(automationDir, "config.json"), JSON.stringify(config, null, 2) + "\n");
1381
1572
  writeFileSync(join(automationDir, "main.py"), automation.code || "");
1382
- console.log(pc.green(" \u2713"), `${automation.name} \u2192 automations/${dirName}/`);
1573
+ console.log(pc2.green(" \u2713"), `${automation.name} \u2192 automations/${dirName}/`);
1383
1574
  }
1384
1575
  }
1385
1576
  async function pullHooks(api, platformDir, filterName, appName, projectId) {
@@ -1406,7 +1597,7 @@ ${hook.script.split("\n").map((line) => " " + line).join("\n")}
1406
1597
  }
1407
1598
  `;
1408
1599
  writeFileSync(join(hooksDir, fileName), content);
1409
- console.log(pc.green(" \u2713"), `${hook.name} \u2192 hooks/${fileName}`);
1600
+ console.log(pc2.green(" \u2713"), `${hook.name} \u2192 hooks/${fileName}`);
1410
1601
  }
1411
1602
  }
1412
1603
  function loadLocalAgents(platformDir, filterName, appName) {
@@ -1435,7 +1626,7 @@ function loadLocalAgents(platformDir, filterName, appName) {
1435
1626
  }
1436
1627
  if (config.mcp_servers !== void 0) {
1437
1628
  console.log(
1438
- pc.yellow(
1629
+ pc2.yellow(
1439
1630
  ` \u26A0 ${entry.name}: "mcp_servers" is deprecated and ignored \u2014 MCP servers are now scoped per project in Platform \u2192 Integrations.`
1440
1631
  )
1441
1632
  );
@@ -1464,9 +1655,9 @@ function loadLocalAgents(platformDir, filterName, appName) {
1464
1655
  }
1465
1656
  }
1466
1657
  if (errors.length > 0) {
1467
- console.log(pc.red(" Agent errors:"));
1658
+ console.log(pc2.red(" Agent errors:"));
1468
1659
  for (const err of errors) {
1469
- console.log(pc.red(` \u2717 ${err}`));
1660
+ console.log(pc2.red(` \u2717 ${err}`));
1470
1661
  }
1471
1662
  throw new Error(`Found ${errors.length} agent error(s)`);
1472
1663
  }
@@ -1539,7 +1730,7 @@ async function applyAgents(api, localAgents, projectId) {
1539
1730
  const skills = await api.listAgentSkills();
1540
1731
  skillMap = new Map(skills.map((s) => [s.slug, s.id]));
1541
1732
  } catch (e) {
1542
- console.log(pc.yellow(` \u26A0 Could not fetch skills for resolution: ${e}`));
1733
+ console.log(pc2.yellow(` \u26A0 Could not fetch skills for resolution: ${e}`));
1543
1734
  }
1544
1735
  }
1545
1736
  for (const { agent, systemPrompt, policyScript } of localAgents) {
@@ -1551,7 +1742,7 @@ async function applyAgents(api, localAgents, projectId) {
1551
1742
  if (id) {
1552
1743
  skillIds.push(id);
1553
1744
  } else {
1554
- console.log(pc.yellow(` \u26A0 Skill "${slug}" not found, skipping`));
1745
+ console.log(pc2.yellow(` \u26A0 Skill "${slug}" not found, skipping`));
1555
1746
  }
1556
1747
  }
1557
1748
  }
@@ -1569,14 +1760,14 @@ async function applyAgents(api, localAgents, projectId) {
1569
1760
  try {
1570
1761
  if (remote) {
1571
1762
  await api.updateAgent(remote.id, payload);
1572
- console.log(pc.green(" \u2713"), `${agent.name} (updated)`);
1763
+ console.log(pc2.green(" \u2713"), `${agent.name} (updated)`);
1573
1764
  } else {
1574
1765
  if (projectId) payload.project_id = projectId;
1575
1766
  await api.createAgent(payload);
1576
- console.log(pc.green(" \u2713"), `${agent.name} (created)`);
1767
+ console.log(pc2.green(" \u2713"), `${agent.name} (created)`);
1577
1768
  }
1578
1769
  } catch (e) {
1579
- console.log(pc.red(" \u2717"), `${agent.name}: ${e}`);
1770
+ console.log(pc2.red(" \u2717"), `${agent.name}: ${e}`);
1580
1771
  errors++;
1581
1772
  }
1582
1773
  }
@@ -1620,7 +1811,7 @@ async function pullAgents(api, platformDir, filterName, projectId) {
1620
1811
  if (agent.policy_script) {
1621
1812
  writeFileSync(join(agentDir, "policy.js"), agent.policy_script);
1622
1813
  }
1623
- console.log(pc.green(" \u2713"), `${agent.name} \u2192 agents/${dirName}/`);
1814
+ console.log(pc2.green(" \u2713"), `${agent.name} \u2192 agents/${dirName}/`);
1624
1815
  }
1625
1816
  }
1626
1817
  async function listResources(api, platformDir, filterType, appName, projectId) {
@@ -1886,13 +2077,13 @@ async function destroyResources(api, platformDir, resourceType, resourceName, sk
1886
2077
  }
1887
2078
  }
1888
2079
  if (toDelete.length === 0) {
1889
- console.log(pc.green(" \u2713 No resources found to delete"));
2080
+ console.log(pc2.green(" \u2713 No resources found to delete"));
1890
2081
  return;
1891
2082
  }
1892
- console.log(pc.bold(" Resources to delete:"));
2083
+ console.log(pc2.bold(" Resources to delete:"));
1893
2084
  console.log();
1894
2085
  for (const item of toDelete) {
1895
- console.log(pc.red(` - ${item.type}: ${item.name}`));
2086
+ console.log(pc2.red(` - ${item.type}: ${item.name}`));
1896
2087
  }
1897
2088
  console.log();
1898
2089
  if (!skipConfirm) {
@@ -1903,7 +2094,7 @@ async function destroyResources(api, platformDir, resourceType, resourceName, sk
1903
2094
  initial: false
1904
2095
  });
1905
2096
  if (!confirmed) {
1906
- console.log(pc.dim(" Cancelled"));
2097
+ console.log(pc2.dim(" Cancelled"));
1907
2098
  return;
1908
2099
  }
1909
2100
  }
@@ -1915,45 +2106,45 @@ async function destroyResources(api, platformDir, resourceType, resourceName, sk
1915
2106
  for (const resource of agents) {
1916
2107
  try {
1917
2108
  await api.deleteAgent(resource.remoteId);
1918
- console.log(pc.green(" \u2713"), `Deleted agent: ${resource.name}`);
2109
+ console.log(pc2.green(" \u2713"), `Deleted agent: ${resource.name}`);
1919
2110
  } catch (e) {
1920
- console.log(pc.red(" \u2717"), `Failed to delete agent ${resource.name}: ${e}`);
2111
+ console.log(pc2.red(" \u2717"), `Failed to delete agent ${resource.name}: ${e}`);
1921
2112
  errors++;
1922
2113
  }
1923
2114
  }
1924
2115
  for (const resource of hooks) {
1925
2116
  try {
1926
2117
  await api.deleteHook(resource.remoteId);
1927
- console.log(pc.green(" \u2713"), `Deleted hook: ${resource.name}`);
2118
+ console.log(pc2.green(" \u2713"), `Deleted hook: ${resource.name}`);
1928
2119
  } catch (e) {
1929
- console.log(pc.red(" \u2717"), `Failed to delete hook ${resource.name}: ${e}`);
2120
+ console.log(pc2.red(" \u2717"), `Failed to delete hook ${resource.name}: ${e}`);
1930
2121
  errors++;
1931
2122
  }
1932
2123
  }
1933
2124
  for (const resource of automations) {
1934
2125
  try {
1935
2126
  await api.deleteAutomation(resource.remoteId);
1936
- console.log(pc.green(" \u2713"), `Deleted automation: ${resource.name}`);
2127
+ console.log(pc2.green(" \u2713"), `Deleted automation: ${resource.name}`);
1937
2128
  } catch (e) {
1938
- console.log(pc.red(" \u2717"), `Failed to delete automation ${resource.name}: ${e}`);
2129
+ console.log(pc2.red(" \u2717"), `Failed to delete automation ${resource.name}: ${e}`);
1939
2130
  errors++;
1940
2131
  }
1941
2132
  }
1942
2133
  const deletePlan = planCollectionDelete(collections, platformDir);
1943
2134
  if (deletePlan.cycleNames.length > 0 && !forceCycles) {
1944
2135
  console.log();
1945
- console.log(pc.yellow(" Circular references detected:"));
2136
+ console.log(pc2.yellow(" Circular references detected:"));
1946
2137
  for (const edge of deletePlan.cycleEdges) {
1947
- console.log(pc.yellow(` ${edge.from}.${edge.field} \u2192 ${edge.to}`));
2138
+ console.log(pc2.yellow(` ${edge.from}.${edge.field} \u2192 ${edge.to}`));
1948
2139
  }
1949
2140
  console.log();
1950
- console.log(pc.dim(" To destroy these, relation fields forming the cycle must be removed first."));
1951
- console.log(pc.dim(" Use --force-cycles to proceed."));
2141
+ console.log(pc2.dim(" To destroy these, relation fields forming the cycle must be removed first."));
2142
+ console.log(pc2.dim(" Use --force-cycles to proceed."));
1952
2143
  console.log();
1953
2144
  process.exit(1);
1954
2145
  }
1955
2146
  if (deletePlan.cycleNames.length > 0 && forceCycles) {
1956
- console.log(pc.dim(" Breaking circular references..."));
2147
+ console.log(pc2.dim(" Breaking circular references..."));
1957
2148
  for (const edge of deletePlan.cycleEdges) {
1958
2149
  const resource = collections.find((c) => c.name === edge.from);
1959
2150
  if (!resource?.remoteId) continue;
@@ -1963,10 +2154,10 @@ async function destroyResources(api, platformDir, resourceType, resourceName, sk
1963
2154
  if (remote) {
1964
2155
  const updatedSchema = remote.schema.filter((f) => f.name !== edge.field);
1965
2156
  await api.ensureCollection(remote.name, { name: remote.name, schema: updatedSchema });
1966
- console.log(pc.green(" \u2713"), `Removed ${edge.from}.${edge.field}`);
2157
+ console.log(pc2.green(" \u2713"), `Removed ${edge.from}.${edge.field}`);
1967
2158
  }
1968
2159
  } catch (e) {
1969
- console.log(pc.red(" \u2717"), `Failed to remove ${edge.from}.${edge.field}: ${e}`);
2160
+ console.log(pc2.red(" \u2717"), `Failed to remove ${edge.from}.${edge.field}: ${e}`);
1970
2161
  errors++;
1971
2162
  }
1972
2163
  }
@@ -1974,9 +2165,9 @@ async function destroyResources(api, platformDir, resourceType, resourceName, sk
1974
2165
  for (const resource of deletePlan.sorted) {
1975
2166
  try {
1976
2167
  await api.deleteCollection(resource.remoteId);
1977
- console.log(pc.green(" \u2713"), `Deleted collection: ${resource.name}`);
2168
+ console.log(pc2.green(" \u2713"), `Deleted collection: ${resource.name}`);
1978
2169
  } catch (e) {
1979
- console.log(pc.red(" \u2717"), `Failed to delete collection ${resource.name}: ${e}`);
2170
+ console.log(pc2.red(" \u2717"), `Failed to delete collection ${resource.name}: ${e}`);
1980
2171
  errors++;
1981
2172
  }
1982
2173
  }
@@ -2006,10 +2197,10 @@ async function destroyApp(skipConfirm) {
2006
2197
  const data = await searchRes.json();
2007
2198
  const appRecord = data.items?.[0];
2008
2199
  if (!appRecord) {
2009
- console.log(pc.yellow(` App "${appName}" not found in Lumera.`));
2200
+ console.log(pc2.yellow(` App "${appName}" not found in Lumera.`));
2010
2201
  return;
2011
2202
  }
2012
- console.log(pc.dim(` App to delete: ${appRecord.name} (${appRecord.external_id})`));
2203
+ console.log(pc2.dim(` App to delete: ${appRecord.name} (${appRecord.external_id})`));
2013
2204
  console.log();
2014
2205
  if (!skipConfirm) {
2015
2206
  const { confirmed } = await prompts({
@@ -2019,7 +2210,7 @@ async function destroyApp(skipConfirm) {
2019
2210
  initial: false
2020
2211
  });
2021
2212
  if (!confirmed) {
2022
- console.log(pc.dim(" Cancelled"));
2213
+ console.log(pc2.dim(" Cancelled"));
2023
2214
  return;
2024
2215
  }
2025
2216
  }
@@ -2036,7 +2227,7 @@ async function destroyApp(skipConfirm) {
2036
2227
  if (!deleteRes.ok) {
2037
2228
  throw new Error(`Failed to delete app: ${await deleteRes.text()}`);
2038
2229
  }
2039
- console.log(pc.green(" \u2713"), `App "${appRecord.name}" deleted from Lumera.`);
2230
+ console.log(pc2.green(" \u2713"), `App "${appRecord.name}" deleted from Lumera.`);
2040
2231
  }
2041
2232
  async function showResource(api, platformDir, resourceType, resourceName, appName, projectId) {
2042
2233
  if (resourceType === "collections") {
@@ -2045,11 +2236,11 @@ async function showResource(api, platformDir, resourceType, resourceName, appNam
2045
2236
  const local = localCollections[0];
2046
2237
  const remote = remoteCollections.find((c) => c.name === resourceName || c.id === resourceName);
2047
2238
  if (!local && !remote) {
2048
- console.log(pc.red(` Collection "${resourceName}" not found`));
2239
+ console.log(pc2.red(` Collection "${resourceName}" not found`));
2049
2240
  process.exit(1);
2050
2241
  }
2051
2242
  console.log();
2052
- console.log(pc.bold(` Collection: ${resourceName}`));
2243
+ console.log(pc2.bold(` Collection: ${resourceName}`));
2053
2244
  console.log();
2054
2245
  let collectionStatus;
2055
2246
  let addedFields = [];
@@ -2066,38 +2257,38 @@ async function showResource(api, platformDir, resourceType, resourceName, appNam
2066
2257
  collectionStatus = "remote-only";
2067
2258
  }
2068
2259
  const statusDisplay = {
2069
- "synced": pc.green("synced"),
2070
- "changed": pc.yellow("changed"),
2071
- "local-only": pc.yellow("local only"),
2072
- "remote-only": pc.cyan("remote only")
2260
+ "synced": pc2.green("synced"),
2261
+ "changed": pc2.yellow("changed"),
2262
+ "local-only": pc2.yellow("local only"),
2263
+ "remote-only": pc2.cyan("remote only")
2073
2264
  };
2074
2265
  console.log(` Status: ${statusDisplay[collectionStatus]}`);
2075
2266
  console.log();
2076
2267
  const addedSet = new Set(addedFields);
2077
2268
  const removedSet = new Set(removedFields);
2078
- console.log(pc.bold(" Fields:"));
2269
+ console.log(pc2.bold(" Fields:"));
2079
2270
  if (local) {
2080
2271
  for (const field of local.fields) {
2081
- const req = field.required ? pc.red("*") : "";
2272
+ const req = field.required ? pc2.red("*") : "";
2082
2273
  if (addedSet.has(field.name)) {
2083
- console.log(` ${pc.green("+")} ${field.name}${req} ${pc.dim(`(${field.type})`)}`);
2274
+ console.log(` ${pc2.green("+")} ${field.name}${req} ${pc2.dim(`(${field.type})`)}`);
2084
2275
  } else {
2085
- console.log(` ${field.name}${req} ${pc.dim(`(${field.type})`)}`);
2276
+ console.log(` ${field.name}${req} ${pc2.dim(`(${field.type})`)}`);
2086
2277
  }
2087
2278
  }
2088
2279
  }
2089
2280
  if (remote) {
2090
2281
  for (const field of remote.schema) {
2091
2282
  if (removedSet.has(field.name)) {
2092
- const req = field.required ? pc.red("*") : "";
2093
- console.log(` ${pc.red("-")} ${field.name}${req} ${pc.dim(`(${field.type})`)}`);
2283
+ const req = field.required ? pc2.red("*") : "";
2284
+ console.log(` ${pc2.red("-")} ${field.name}${req} ${pc2.dim(`(${field.type})`)}`);
2094
2285
  }
2095
2286
  }
2096
2287
  }
2097
2288
  if (!local && remote) {
2098
2289
  for (const field of remote.schema) {
2099
- const req = field.required ? pc.red("*") : "";
2100
- console.log(` ${field.name}${req} ${pc.dim(`(${field.type})`)}`);
2290
+ const req = field.required ? pc2.red("*") : "";
2291
+ console.log(` ${field.name}${req} ${pc2.dim(`(${field.type})`)}`);
2101
2292
  }
2102
2293
  }
2103
2294
  console.log();
@@ -2107,18 +2298,18 @@ async function showResource(api, platformDir, resourceType, resourceName, appNam
2107
2298
  const local = localAutomations[0];
2108
2299
  const remote = remoteAutomations.find((a) => a.external_id === resourceName || a.name === resourceName);
2109
2300
  if (!local && !remote) {
2110
- console.log(pc.red(` Automation "${resourceName}" not found`));
2301
+ console.log(pc2.red(` Automation "${resourceName}" not found`));
2111
2302
  process.exit(1);
2112
2303
  }
2113
2304
  console.log();
2114
- console.log(pc.bold(` Automation: ${local?.automation.name || remote?.name}`));
2305
+ console.log(pc2.bold(` Automation: ${local?.automation.name || remote?.name}`));
2115
2306
  console.log();
2116
2307
  if (local && remote) {
2117
- console.log(` Status: ${pc.green("synced")}`);
2308
+ console.log(` Status: ${pc2.green("synced")}`);
2118
2309
  } else if (local) {
2119
- console.log(` Status: ${pc.yellow("local only")}`);
2310
+ console.log(` Status: ${pc2.yellow("local only")}`);
2120
2311
  } else {
2121
- console.log(` Status: ${pc.cyan("remote only")}`);
2312
+ console.log(` Status: ${pc2.cyan("remote only")}`);
2122
2313
  }
2123
2314
  if (local?.automation.description || remote?.description) {
2124
2315
  console.log(` Description: ${local?.automation.description || remote?.description}`);
@@ -2130,11 +2321,11 @@ async function showResource(api, platformDir, resourceType, resourceName, appNam
2130
2321
  const local = localHooks[0];
2131
2322
  const remote = remoteHooks.find((h) => h.external_id === resourceName);
2132
2323
  if (!local && !remote) {
2133
- console.log(pc.red(` Hook "${resourceName}" not found`));
2324
+ console.log(pc2.red(` Hook "${resourceName}" not found`));
2134
2325
  process.exit(1);
2135
2326
  }
2136
2327
  console.log();
2137
- console.log(pc.bold(` Hook: ${local?.hook.external_id || remote?.external_id}`));
2328
+ console.log(pc2.bold(` Hook: ${local?.hook.external_id || remote?.external_id}`));
2138
2329
  console.log();
2139
2330
  console.log(` Collection: ${local?.hook.collection || remote?.collection_name}`);
2140
2331
  console.log(` Trigger: ${local?.hook.trigger || remote?.event}`);
@@ -2146,25 +2337,25 @@ async function showResource(api, platformDir, resourceType, resourceName, appNam
2146
2337
  const local = localAgents[0];
2147
2338
  const remote = remoteAgents.find((a) => a.external_id === resourceName || a.name === resourceName);
2148
2339
  if (!local && !remote) {
2149
- console.log(pc.red(` Agent "${resourceName}" not found`));
2340
+ console.log(pc2.red(` Agent "${resourceName}" not found`));
2150
2341
  process.exit(1);
2151
2342
  }
2152
2343
  console.log();
2153
- console.log(pc.bold(` Agent: ${local?.agent.name || remote?.name}`));
2344
+ console.log(pc2.bold(` Agent: ${local?.agent.name || remote?.name}`));
2154
2345
  console.log();
2155
2346
  if (local && remote) {
2156
2347
  const promptChanged = (remote.system_prompt || "").trim() !== local.systemPrompt.trim();
2157
2348
  const nameChanged = remote.name !== local.agent.name;
2158
2349
  const descChanged = (remote.description || "") !== (local.agent.description || "");
2159
2350
  if (promptChanged || nameChanged || descChanged) {
2160
- console.log(` Status: ${pc.yellow("changed")}`);
2351
+ console.log(` Status: ${pc2.yellow("changed")}`);
2161
2352
  } else {
2162
- console.log(` Status: ${pc.green("synced")}`);
2353
+ console.log(` Status: ${pc2.green("synced")}`);
2163
2354
  }
2164
2355
  } else if (local) {
2165
- console.log(` Status: ${pc.yellow("local only")}`);
2356
+ console.log(` Status: ${pc2.yellow("local only")}`);
2166
2357
  } else {
2167
- console.log(` Status: ${pc.cyan("remote only")}`);
2358
+ console.log(` Status: ${pc2.cyan("remote only")}`);
2168
2359
  }
2169
2360
  if (local?.agent.external_id || remote?.external_id) {
2170
2361
  console.log(` External ID: ${local?.agent.external_id || remote?.external_id}`);
@@ -2183,19 +2374,19 @@ async function showResource(api, platformDir, resourceType, resourceName, appNam
2183
2374
  const local = localMailboxes[0];
2184
2375
  const remote = remoteMailboxes.find((m) => m.slug === resourceName);
2185
2376
  if (!local && !remote) {
2186
- console.log(pc.red(` Mailbox "${resourceName}" not found`));
2377
+ console.log(pc2.red(` Mailbox "${resourceName}" not found`));
2187
2378
  process.exit(1);
2188
2379
  }
2189
2380
  console.log();
2190
- console.log(pc.bold(` Mailbox: ${local?.slug || remote?.slug}`));
2381
+ console.log(pc2.bold(` Mailbox: ${local?.slug || remote?.slug}`));
2191
2382
  console.log();
2192
2383
  if (local && remote) {
2193
2384
  const descChanged = (local.description ?? "").trim() !== (remote.description ?? "").trim();
2194
- console.log(` Status: ${descChanged ? pc.yellow("changed") : pc.green("synced")}`);
2385
+ console.log(` Status: ${descChanged ? pc2.yellow("changed") : pc2.green("synced")}`);
2195
2386
  } else if (local) {
2196
- console.log(` Status: ${pc.yellow("local only")}`);
2387
+ console.log(` Status: ${pc2.yellow("local only")}`);
2197
2388
  } else {
2198
- console.log(` Status: ${pc.cyan("remote only")}`);
2389
+ console.log(` Status: ${pc2.cyan("remote only")}`);
2199
2390
  }
2200
2391
  if (remote?.email) console.log(` Email: ${remote.email}`);
2201
2392
  const desc = local?.description || remote?.description;
@@ -2210,7 +2401,7 @@ async function showResource(api, platformDir, resourceType, resourceName, appNam
2210
2401
  const appTitle = getAppTitle(projectRoot);
2211
2402
  let apiUrl = getApiUrl().replace(/\/+$/, "").replace(/\/api$/, "");
2212
2403
  console.log();
2213
- console.log(pc.bold(` App: ${appTitle || appName2}`));
2404
+ console.log(pc2.bold(` App: ${appTitle || appName2}`));
2214
2405
  console.log();
2215
2406
  console.log(` External ID: ${appName2}`);
2216
2407
  const filterParam = encodeURIComponent(JSON.stringify({ external_id: appName2 }));
@@ -2227,12 +2418,12 @@ async function showResource(api, platformDir, resourceType, resourceName, appNam
2227
2418
  const data = await searchRes.json();
2228
2419
  const appRecord = data.items?.[0];
2229
2420
  if (appRecord) {
2230
- console.log(` Status: ${pc.green("deployed")}`);
2421
+ console.log(` Status: ${pc2.green("deployed")}`);
2231
2422
  if (appRecord.hosting_type) console.log(` Hosting: ${appRecord.hosting_type}`);
2232
2423
  if (appRecord.current_version) console.log(` Version: ${appRecord.current_version}`);
2233
2424
  if (appRecord.deployed_at) console.log(` Deployed: ${appRecord.deployed_at}`);
2234
2425
  } else {
2235
- console.log(` Status: ${pc.yellow("not deployed")}`);
2426
+ console.log(` Status: ${pc2.yellow("not deployed")}`);
2236
2427
  }
2237
2428
  }
2238
2429
  console.log();
@@ -2253,8 +2444,8 @@ async function plan(args) {
2253
2444
  const positionalArgs = args.filter((a) => !a.startsWith("-"));
2254
2445
  const { type, name } = parseResource(positionalArgs.filter((a) => a !== "--json")[0]);
2255
2446
  console.log();
2256
- console.log(pc.cyan(pc.bold(" Plan")));
2257
- console.log(pc.dim(" Comparing local files to remote state..."));
2447
+ console.log(pc2.cyan(pc2.bold(" Plan")));
2448
+ console.log(pc2.dim(" Comparing local files to remote state..."));
2258
2449
  console.log();
2259
2450
  await syncDeps(projectRoot);
2260
2451
  const allChanges = [];
@@ -2272,8 +2463,9 @@ async function plan(args) {
2272
2463
  allChanges.push(...changes);
2273
2464
  }
2274
2465
  }
2466
+ let localAutomations = [];
2275
2467
  if (!type || type === "automations") {
2276
- const localAutomations = loadLocalAutomations(platformDir, name || void 0, appName);
2468
+ localAutomations = loadLocalAutomations(platformDir, name || void 0, appName);
2277
2469
  if (localAutomations.length > 0) {
2278
2470
  const changes = await planAutomations(api, localAutomations);
2279
2471
  allChanges.push(...changes);
@@ -2300,13 +2492,15 @@ async function plan(args) {
2300
2492
  allChanges.push(...changes);
2301
2493
  }
2302
2494
  }
2495
+ const lintWarnings = safeLint(projectRoot, localAutomations);
2303
2496
  if (allChanges.length === 0) {
2304
2497
  if (jsonOutput) {
2305
- console.log(JSON.stringify({ changes: [], warnings: [] }));
2498
+ console.log(JSON.stringify({ changes: [], warnings: [], lintWarnings: serializeLintWarnings(lintWarnings, projectRoot) }));
2306
2499
  return;
2307
2500
  }
2308
- console.log(pc.green(" \u2713 No changes detected"));
2501
+ console.log(pc2.green(" \u2713 No changes detected"));
2309
2502
  console.log();
2503
+ safePrintLint(lintWarnings, projectRoot);
2310
2504
  return;
2311
2505
  }
2312
2506
  if (jsonOutput) {
@@ -2319,21 +2513,21 @@ async function plan(args) {
2319
2513
  }
2320
2514
  }
2321
2515
  }
2322
- console.log(JSON.stringify({ changes: allChanges, warnings }));
2516
+ console.log(JSON.stringify({ changes: allChanges, warnings, lintWarnings: serializeLintWarnings(lintWarnings, projectRoot) }));
2323
2517
  return;
2324
2518
  }
2325
- console.log(pc.bold(" Changes:"));
2519
+ console.log(pc2.bold(" Changes:"));
2326
2520
  console.log();
2327
2521
  for (const change of allChanges) {
2328
2522
  const icon = change.type === "create" ? "+" : change.type === "update" ? "~" : "-";
2329
- const color = change.type === "create" ? pc.green : change.type === "update" ? pc.yellow : pc.red;
2523
+ const color = change.type === "create" ? pc2.green : change.type === "update" ? pc2.yellow : pc2.red;
2330
2524
  const details = change.details ? ` (${change.details})` : "";
2331
- console.log(` ${color(icon)} ${change.resource}: ${change.name}${pc.dim(details)}`);
2525
+ console.log(` ${color(icon)} ${change.resource}: ${change.name}${pc2.dim(details)}`);
2332
2526
  if (change.fieldDetails && change.fieldDetails.length > 0) {
2333
2527
  for (const field of change.fieldDetails) {
2334
- const fColor = field.action === "+" ? pc.green : pc.red;
2528
+ const fColor = field.action === "+" ? pc2.green : pc2.red;
2335
2529
  const req = field.required ? "*" : "";
2336
- console.log(` ${fColor(field.action)} ${field.name}${req} ${pc.dim(`(${field.type})`)}`);
2530
+ console.log(` ${fColor(field.action)} ${field.name}${req} ${pc2.dim(`(${field.type})`)}`);
2337
2531
  }
2338
2532
  console.log();
2339
2533
  }
@@ -2342,16 +2536,16 @@ async function plan(args) {
2342
2536
  for (const td of change.textDiffs) {
2343
2537
  const diffLines = computeLineDiff(td.oldText, td.newText);
2344
2538
  if (diffLines.length > 0) {
2345
- console.log(pc.dim(` --- ${td.field}`));
2539
+ console.log(pc2.dim(` --- ${td.field}`));
2346
2540
  const shown = diffLines.slice(0, maxDiffLines);
2347
2541
  for (const dl of shown) {
2348
- if (dl.type === "+") console.log(pc.green(` + ${dl.line}`));
2349
- else if (dl.type === "-") console.log(pc.red(` - ${dl.line}`));
2350
- else console.log(pc.dim(` ${dl.line}`));
2542
+ if (dl.type === "+") console.log(pc2.green(` + ${dl.line}`));
2543
+ else if (dl.type === "-") console.log(pc2.red(` - ${dl.line}`));
2544
+ else console.log(pc2.dim(` ${dl.line}`));
2351
2545
  }
2352
2546
  if (diffLines.length > maxDiffLines) {
2353
2547
  const remaining = diffLines.length - maxDiffLines;
2354
- console.log(pc.dim(` ... ${remaining} more lines \u2014 use ${pc.cyan(`lumera diff ${change.resource}s/${change.name}`)} for full diff`));
2548
+ console.log(pc2.dim(` ... ${remaining} more lines \u2014 use ${pc2.cyan(`lumera diff ${change.resource}s/${change.name}`)} for full diff`));
2355
2549
  }
2356
2550
  console.log();
2357
2551
  }
@@ -2359,7 +2553,8 @@ async function plan(args) {
2359
2553
  }
2360
2554
  }
2361
2555
  console.log();
2362
- console.log(pc.dim(` Run 'lumera apply' to apply these changes.`));
2556
+ safePrintLint(lintWarnings, projectRoot);
2557
+ console.log(pc2.dim(` Run 'lumera apply' to apply these changes.`));
2363
2558
  console.log();
2364
2559
  }
2365
2560
  async function apply(args) {
@@ -2373,17 +2568,21 @@ async function apply(args) {
2373
2568
  const appName = getAppName(projectRoot);
2374
2569
  const api = createApiClient(void 0, void 0, appName);
2375
2570
  const projectId = getProjectId(projectRoot);
2376
- const positionalArgs = args.filter((a) => !a.startsWith("-"));
2571
+ const positionalArgs = getPositionalArgs(args);
2377
2572
  const { type, name } = parseResource(positionalArgs[0]);
2378
2573
  const autoConfirm = args.includes("--yes") || args.includes("-y") || !!process.env.CI;
2574
+ const skipApp = args.includes("--no-app") || args.includes("--resources-only");
2575
+ if (type === "app" && skipApp) {
2576
+ throw new Error("Cannot combine app resource with --no-app / --resources-only");
2577
+ }
2379
2578
  if (type === "app") {
2380
2579
  console.log();
2381
- console.log(pc.cyan(pc.bold(" Apply")));
2580
+ console.log(pc2.cyan(pc2.bold(" Apply")));
2382
2581
  console.log();
2383
- console.log(pc.bold(" App:"));
2582
+ console.log(pc2.bold(" App:"));
2384
2583
  await applyApp(args);
2385
2584
  console.log();
2386
- console.log(pc.green(" Done!"));
2585
+ console.log(pc2.green(" Done!"));
2387
2586
  console.log();
2388
2587
  return;
2389
2588
  }
@@ -2411,12 +2610,12 @@ async function apply(args) {
2411
2610
  const hasLocal = localCollections.length > 0 || localAutomations.length > 0 || localHooks.length > 0 || localAgents.length > 0 || localMailboxes.length > 0;
2412
2611
  if (!hasLocal) {
2413
2612
  console.log();
2414
- console.log(pc.red(` Resource "${name}" not found locally`));
2613
+ console.log(pc2.red(` Resource "${name}" not found locally`));
2415
2614
  process.exit(1);
2416
2615
  }
2417
2616
  }
2418
2617
  let willDeployApp = false;
2419
- if (!type) {
2618
+ if (!type && !skipApp) {
2420
2619
  try {
2421
2620
  if (existsSync(join(projectRoot, "dist")) || existsSync(join(projectRoot, "src"))) {
2422
2621
  willDeployApp = true;
@@ -2426,35 +2625,37 @@ async function apply(args) {
2426
2625
  }
2427
2626
  if (allChanges.length === 0 && !willDeployApp) {
2428
2627
  console.log();
2429
- console.log(pc.green(" \u2713 Nothing to apply \u2014 local and remote are in sync."));
2628
+ console.log(pc2.green(" \u2713 Nothing to apply \u2014 local and remote are in sync."));
2430
2629
  console.log();
2630
+ safePrintLint(safeLint(projectRoot, localAutomations), projectRoot);
2431
2631
  return;
2432
2632
  }
2433
2633
  console.log();
2434
- console.log(pc.cyan(pc.bold(" Apply")));
2634
+ console.log(pc2.cyan(pc2.bold(" Apply")));
2435
2635
  console.log();
2436
2636
  const creates = allChanges.filter((c) => c.type === "create");
2437
2637
  const updates = allChanges.filter((c) => c.type === "update");
2438
2638
  if (allChanges.length > 0) {
2439
- console.log(pc.bold(" Plan:"));
2639
+ console.log(pc2.bold(" Plan:"));
2440
2640
  for (const change of allChanges) {
2441
2641
  const icon = change.type === "create" ? "+" : "~";
2442
- const color = change.type === "create" ? pc.green : pc.yellow;
2642
+ const color = change.type === "create" ? pc2.green : pc2.yellow;
2443
2643
  const details = change.details ? ` (${change.details})` : "";
2444
- console.log(` ${color(icon)} ${change.resource}: ${change.name}${pc.dim(details)}`);
2644
+ console.log(` ${color(icon)} ${change.resource}: ${change.name}${pc2.dim(details)}`);
2445
2645
  }
2446
- if (willDeployApp) console.log(` ${pc.blue("\u25CF")} app: frontend build + deploy`);
2646
+ if (willDeployApp) console.log(` ${pc2.blue("\u25CF")} app: frontend build + deploy`);
2447
2647
  console.log();
2448
2648
  const parts = [];
2449
- if (creates.length > 0) parts.push(pc.green(`${creates.length} create`));
2450
- if (updates.length > 0) parts.push(pc.yellow(`${updates.length} update`));
2451
- if (willDeployApp) parts.push(pc.blue("1 app deploy"));
2649
+ if (creates.length > 0) parts.push(pc2.green(`${creates.length} create`));
2650
+ if (updates.length > 0) parts.push(pc2.yellow(`${updates.length} update`));
2651
+ if (willDeployApp) parts.push(pc2.blue("1 app deploy"));
2452
2652
  console.log(` ${parts.join(", ")}`);
2453
2653
  console.log();
2454
2654
  } else if (willDeployApp) {
2455
- console.log(pc.dim(" No infrastructure changes \u2014 deploying app only."));
2655
+ console.log(pc2.dim(" No infrastructure changes \u2014 deploying app only."));
2456
2656
  console.log();
2457
2657
  }
2658
+ safePrintLint(safeLint(projectRoot, localAutomations), projectRoot);
2458
2659
  if (!autoConfirm && allChanges.length > 0) {
2459
2660
  const { confirm } = await prompts({
2460
2661
  type: "confirm",
@@ -2463,7 +2664,7 @@ async function apply(args) {
2463
2664
  initial: true
2464
2665
  });
2465
2666
  if (!confirm) {
2466
- console.log(pc.dim(" Cancelled."));
2667
+ console.log(pc2.dim(" Cancelled."));
2467
2668
  console.log();
2468
2669
  return;
2469
2670
  }
@@ -2474,7 +2675,7 @@ async function apply(args) {
2474
2675
  let totalUpdated = 0;
2475
2676
  let totalSkipped = 0;
2476
2677
  if (localCollections.length > 0) {
2477
- console.log(pc.bold(" Collections:"));
2678
+ console.log(pc2.bold(" Collections:"));
2478
2679
  totalErrors += await applyCollections(api, localCollections);
2479
2680
  console.log();
2480
2681
  }
@@ -2485,42 +2686,42 @@ async function apply(args) {
2485
2686
  } catch {
2486
2687
  }
2487
2688
  if (localAutomations.length > 0) {
2488
- console.log(pc.bold(" Automations:"));
2689
+ console.log(pc2.bold(" Automations:"));
2489
2690
  totalErrors += await applyAutomations(api, localAutomations, projectId);
2490
2691
  console.log();
2491
2692
  }
2492
2693
  if (localHooks.length > 0) {
2493
- console.log(pc.bold(" Hooks:"));
2694
+ console.log(pc2.bold(" Hooks:"));
2494
2695
  totalErrors += await applyHooks(api, localHooks, collections, projectId);
2495
2696
  console.log();
2496
2697
  }
2497
2698
  if (localAgents.length > 0) {
2498
- console.log(pc.bold(" Agents:"));
2699
+ console.log(pc2.bold(" Agents:"));
2499
2700
  totalErrors += await applyAgents(api, localAgents, projectId);
2500
2701
  console.log();
2501
2702
  }
2502
2703
  if (localMailboxes.length > 0) {
2503
- console.log(pc.bold(" Mailboxes:"));
2704
+ console.log(pc2.bold(" Mailboxes:"));
2504
2705
  totalErrors += await applyMailboxes(api, localMailboxes);
2505
2706
  console.log();
2506
2707
  }
2507
2708
  if (willDeployApp) {
2508
- console.log(pc.bold(" App:"));
2709
+ console.log(pc2.bold(" App:"));
2509
2710
  await applyApp(args);
2510
2711
  console.log();
2511
2712
  }
2512
2713
  totalCreated = creates.length;
2513
2714
  totalUpdated = updates.length;
2514
2715
  if (totalErrors > 0) {
2515
- console.log(pc.red(` \u2717 ${totalErrors} error${totalErrors > 1 ? "s" : ""} during apply.`));
2716
+ console.log(pc2.red(` \u2717 ${totalErrors} error${totalErrors > 1 ? "s" : ""} during apply.`));
2516
2717
  console.log();
2517
2718
  process.exit(1);
2518
2719
  }
2519
2720
  const summary = [];
2520
- if (totalCreated > 0) summary.push(pc.green(`${totalCreated} created`));
2521
- if (totalUpdated > 0) summary.push(pc.yellow(`${totalUpdated} updated`));
2522
- if (willDeployApp) summary.push(pc.blue("app deployed"));
2523
- console.log(pc.green(" \u2713 Done!") + (summary.length > 0 ? ` ${pc.dim("\u2014")} ${summary.join(", ")}` : ""));
2721
+ if (totalCreated > 0) summary.push(pc2.green(`${totalCreated} created`));
2722
+ if (totalUpdated > 0) summary.push(pc2.yellow(`${totalUpdated} updated`));
2723
+ if (willDeployApp) summary.push(pc2.blue("app deployed"));
2724
+ console.log(pc2.green(" \u2713 Done!") + (summary.length > 0 ? ` ${pc2.dim("\u2014")} ${summary.join(", ")}` : ""));
2524
2725
  console.log();
2525
2726
  }
2526
2727
  async function pull(args) {
@@ -2575,47 +2776,47 @@ async function pull(args) {
2575
2776
  }
2576
2777
  if (conflicts.length > 0) {
2577
2778
  console.log();
2578
- console.log(pc.yellow(` \u26A0 ${conflicts.length} local file${conflicts.length > 1 ? "s have" : " has"} changes that would be lost:`));
2779
+ console.log(pc2.yellow(` \u26A0 ${conflicts.length} local file${conflicts.length > 1 ? "s have" : " has"} changes that would be lost:`));
2579
2780
  for (const f of conflicts) {
2580
- console.log(pc.dim(` ${f}`));
2781
+ console.log(pc2.dim(` ${f}`));
2581
2782
  }
2582
2783
  console.log();
2583
- console.log(pc.dim(` Use ${pc.cyan("lumera diff <resource>")} to inspect changes.`));
2584
- console.log(pc.dim(` Use ${pc.cyan("lumera pull --force")} to overwrite local files.`));
2784
+ console.log(pc2.dim(` Use ${pc2.cyan("lumera diff <resource>")} to inspect changes.`));
2785
+ console.log(pc2.dim(` Use ${pc2.cyan("lumera pull --force")} to overwrite local files.`));
2585
2786
  console.log();
2586
2787
  process.exit(1);
2587
2788
  }
2588
2789
  }
2589
2790
  console.log();
2590
- console.log(pc.cyan(pc.bold(" Pull")));
2591
- console.log(pc.dim(` Downloading remote state to ${platformDir}/...`));
2791
+ console.log(pc2.cyan(pc2.bold(" Pull")));
2792
+ console.log(pc2.dim(` Downloading remote state to ${platformDir}/...`));
2592
2793
  console.log();
2593
2794
  if (!type || type === "collections") {
2594
- console.log(pc.bold(" Collections:"));
2795
+ console.log(pc2.bold(" Collections:"));
2595
2796
  await pullCollections(api, platformDir, name || void 0, appName);
2596
2797
  console.log();
2597
2798
  }
2598
2799
  if (!type || type === "automations") {
2599
- console.log(pc.bold(" Automations:"));
2800
+ console.log(pc2.bold(" Automations:"));
2600
2801
  await pullAutomations(api, platformDir, name || void 0, projectId);
2601
2802
  console.log();
2602
2803
  }
2603
2804
  if (!type || type === "hooks") {
2604
- console.log(pc.bold(" Hooks:"));
2805
+ console.log(pc2.bold(" Hooks:"));
2605
2806
  await pullHooks(api, platformDir, name || void 0, appName, projectId);
2606
2807
  console.log();
2607
2808
  }
2608
2809
  if (!type || type === "agents") {
2609
- console.log(pc.bold(" Agents:"));
2810
+ console.log(pc2.bold(" Agents:"));
2610
2811
  await pullAgents(api, platformDir, name || void 0, projectId);
2611
2812
  console.log();
2612
2813
  }
2613
2814
  if (!type || type === "mailboxes") {
2614
- console.log(pc.bold(" Mailboxes:"));
2815
+ console.log(pc2.bold(" Mailboxes:"));
2615
2816
  await pullMailboxes(api, platformDir, name || void 0);
2616
2817
  console.log();
2617
2818
  }
2618
- console.log(pc.green(" Done!"));
2819
+ console.log(pc2.green(" Done!"));
2619
2820
  console.log();
2620
2821
  }
2621
2822
  async function destroy(args) {
@@ -2633,7 +2834,7 @@ async function destroy(args) {
2633
2834
  const skipConfirm = args.includes("--confirm");
2634
2835
  const forceCycles = args.includes("--force-cycles");
2635
2836
  console.log();
2636
- console.log(pc.red(pc.bold(" Destroy")));
2837
+ console.log(pc2.red(pc2.bold(" Destroy")));
2637
2838
  console.log();
2638
2839
  if (type === "app") {
2639
2840
  await destroyApp(skipConfirm);
@@ -2657,19 +2858,19 @@ async function list(args) {
2657
2858
  const positionalArgs = args.filter((a) => !a.startsWith("--"));
2658
2859
  const filterType = positionalArgs[0];
2659
2860
  console.log();
2660
- console.log(pc.cyan(pc.bold(" Resources")));
2861
+ console.log(pc2.cyan(pc2.bold(" Resources")));
2661
2862
  console.log();
2662
2863
  const allResources = await listResources(api, platformDir, filterType, appName, projectId);
2663
2864
  const remoteOnlyCount = allResources.filter((r) => r.status === "remote-only").length;
2664
2865
  const resources = showAll ? allResources : allResources.filter((r) => r.status !== "remote-only");
2665
2866
  if (resources.length === 0 && remoteOnlyCount === 0) {
2666
- console.log(pc.dim(" No resources found"));
2867
+ console.log(pc2.dim(" No resources found"));
2667
2868
  console.log();
2668
2869
  return;
2669
2870
  }
2670
2871
  if (resources.length === 0 && remoteOnlyCount > 0) {
2671
- console.log(pc.dim(" No local resources found"));
2672
- console.log(pc.dim(` ${remoteOnlyCount} remote-only resource(s) hidden. Use --all to show.`));
2872
+ console.log(pc2.dim(" No local resources found"));
2873
+ console.log(pc2.dim(` ${remoteOnlyCount} remote-only resource(s) hidden. Use --all to show.`));
2673
2874
  console.log();
2674
2875
  return;
2675
2876
  }
@@ -2679,29 +2880,29 @@ async function list(args) {
2679
2880
  byType.get(r.type).push(r);
2680
2881
  }
2681
2882
  for (const [type, items] of byType) {
2682
- console.log(pc.bold(` ${type.charAt(0).toUpperCase() + type.slice(1)}:`));
2883
+ console.log(pc2.bold(` ${type.charAt(0).toUpperCase() + type.slice(1)}:`));
2683
2884
  for (const item of items) {
2684
2885
  let icon;
2685
2886
  let color;
2686
2887
  switch (item.status) {
2687
2888
  case "synced":
2688
2889
  icon = "\u2713";
2689
- color = pc.green;
2890
+ color = pc2.green;
2690
2891
  break;
2691
2892
  case "changed":
2692
2893
  icon = "~";
2693
- color = pc.yellow;
2894
+ color = pc2.yellow;
2694
2895
  break;
2695
2896
  case "local-only":
2696
2897
  icon = "+";
2697
- color = pc.cyan;
2898
+ color = pc2.cyan;
2698
2899
  break;
2699
2900
  case "remote-only":
2700
2901
  icon = "?";
2701
- color = pc.dim;
2902
+ color = pc2.dim;
2702
2903
  break;
2703
2904
  }
2704
- const details = item.details ? pc.dim(` (${item.details})`) : "";
2905
+ const details = item.details ? pc2.dim(` (${item.details})`) : "";
2705
2906
  console.log(` ${color(icon)} ${item.name}${details}`);
2706
2907
  }
2707
2908
  console.log();
@@ -2711,14 +2912,14 @@ async function list(args) {
2711
2912
  const localOnly = resources.filter((r) => r.status === "local-only").length;
2712
2913
  const displayedRemoteOnly = resources.filter((r) => r.status === "remote-only").length;
2713
2914
  const summary = [];
2714
- if (synced > 0) summary.push(pc.green(`${synced} synced`));
2715
- if (changed > 0) summary.push(pc.yellow(`${changed} changed`));
2716
- if (localOnly > 0) summary.push(pc.cyan(`${localOnly} local-only`));
2717
- if (displayedRemoteOnly > 0) summary.push(pc.dim(`${displayedRemoteOnly} remote-only`));
2915
+ if (synced > 0) summary.push(pc2.green(`${synced} synced`));
2916
+ if (changed > 0) summary.push(pc2.yellow(`${changed} changed`));
2917
+ if (localOnly > 0) summary.push(pc2.cyan(`${localOnly} local-only`));
2918
+ if (displayedRemoteOnly > 0) summary.push(pc2.dim(`${displayedRemoteOnly} remote-only`));
2718
2919
  console.log(` ${summary.join(" | ")}`);
2719
- console.log(pc.dim(` ${pc.green("\u2713")} synced ${pc.yellow("~")} changed ${pc.cyan("+")} local-only ? remote-only`));
2920
+ console.log(pc2.dim(` ${pc2.green("\u2713")} synced ${pc2.yellow("~")} changed ${pc2.cyan("+")} local-only ? remote-only`));
2720
2921
  if (!showAll && remoteOnlyCount > 0) {
2721
- console.log(pc.dim(` ${remoteOnlyCount} remote-only resource(s) hidden. Use --all to show.`));
2922
+ console.log(pc2.dim(` ${remoteOnlyCount} remote-only resource(s) hidden. Use --all to show.`));
2722
2923
  }
2723
2924
  console.log();
2724
2925
  }
@@ -2736,15 +2937,15 @@ async function show(args) {
2736
2937
  const projectId = getProjectId(projectRoot);
2737
2938
  const { type, name } = parseResource(args[0]);
2738
2939
  if (!type) {
2739
- console.log(pc.red(` Invalid resource path: ${args[0]}`));
2740
- console.log(pc.dim(" Use format: <type>/<name> (e.g., collections/users)"));
2940
+ console.log(pc2.red(` Invalid resource path: ${args[0]}`));
2941
+ console.log(pc2.dim(" Use format: <type>/<name> (e.g., collections/users)"));
2741
2942
  process.exit(1);
2742
2943
  }
2743
2944
  if (type === "app") {
2744
2945
  await showResource(api, platformDir, "app", "", appName, projectId);
2745
2946
  } else if (!name) {
2746
- console.log(pc.red(` Resource name required`));
2747
- console.log(pc.dim(" Use format: <type>/<name> (e.g., collections/users)"));
2947
+ console.log(pc2.red(` Resource name required`));
2948
+ console.log(pc2.dim(" Use format: <type>/<name> (e.g., collections/users)"));
2748
2949
  process.exit(1);
2749
2950
  } else {
2750
2951
  await showResource(api, platformDir, type, name, appName, projectId);
@@ -2752,17 +2953,17 @@ async function show(args) {
2752
2953
  }
2753
2954
  function showDiffHelp() {
2754
2955
  console.log(`
2755
- ${pc.bold("lumera diff")} - Show full diff between local and remote state
2956
+ ${pc2.bold("lumera diff")} - Show full diff between local and remote state
2756
2957
 
2757
- ${pc.dim("Usage:")}
2958
+ ${pc2.dim("Usage:")}
2758
2959
  lumera diff <resource>
2759
2960
 
2760
- ${pc.dim("Resources:")}
2961
+ ${pc2.dim("Resources:")}
2761
2962
  agents/<name> Diff agent (system_prompt, policy_script)
2762
2963
  automations/<name> Diff automation code
2763
2964
  hooks/<name> Diff hook script
2764
2965
 
2765
- ${pc.dim("Examples:")}
2966
+ ${pc2.dim("Examples:")}
2766
2967
  lumera diff agents/bank_activity_matcher
2767
2968
  lumera diff automations/sync
2768
2969
  lumera diff hooks/encoding_protect_create
@@ -2771,14 +2972,14 @@ ${pc.dim("Examples:")}
2771
2972
  function renderFullDiff(field, oldText, newText) {
2772
2973
  const diffLines = computeLineDiff(oldText, newText);
2773
2974
  if (diffLines.length === 0) {
2774
- console.log(pc.dim(` ${field}: no changes`));
2975
+ console.log(pc2.dim(` ${field}: no changes`));
2775
2976
  return;
2776
2977
  }
2777
- console.log(pc.bold(` --- ${field}`));
2978
+ console.log(pc2.bold(` --- ${field}`));
2778
2979
  for (const dl of diffLines) {
2779
- if (dl.type === "+") console.log(pc.green(` + ${dl.line}`));
2780
- else if (dl.type === "-") console.log(pc.red(` - ${dl.line}`));
2781
- else console.log(pc.dim(` ${dl.line}`));
2980
+ if (dl.type === "+") console.log(pc2.green(` + ${dl.line}`));
2981
+ else if (dl.type === "-") console.log(pc2.red(` - ${dl.line}`));
2982
+ else console.log(pc2.dim(` ${dl.line}`));
2782
2983
  }
2783
2984
  console.log();
2784
2985
  }
@@ -2796,8 +2997,8 @@ async function diff(args) {
2796
2997
  const projectId = getProjectId(projectRoot);
2797
2998
  const { type, name } = parseResource(args[0]);
2798
2999
  if (!type || !name) {
2799
- console.log(pc.red(` Invalid resource path: ${args[0]}`));
2800
- console.log(pc.dim(" Use format: <type>/<name> (e.g., agents/bank_activity_matcher)"));
3000
+ console.log(pc2.red(` Invalid resource path: ${args[0]}`));
3001
+ console.log(pc2.dim(" Use format: <type>/<name> (e.g., agents/bank_activity_matcher)"));
2801
3002
  process.exit(1);
2802
3003
  }
2803
3004
  console.log();
@@ -2810,31 +3011,31 @@ async function diff(args) {
2810
3011
  (a) => a.external_id === name || a.name === name || localExtId && a.external_id === localExtId
2811
3012
  );
2812
3013
  if (!local && !remote) {
2813
- console.log(pc.red(` Agent "${name}" not found locally or remotely`));
3014
+ console.log(pc2.red(` Agent "${name}" not found locally or remotely`));
2814
3015
  process.exit(1);
2815
3016
  }
2816
3017
  if (!local) {
2817
- console.log(pc.cyan(` Agent "${name}" exists only remotely (not in local files)`));
3018
+ console.log(pc2.cyan(` Agent "${name}" exists only remotely (not in local files)`));
2818
3019
  process.exit(0);
2819
3020
  }
2820
3021
  if (!remote) {
2821
- console.log(pc.yellow(` Agent "${name}" exists only locally (not yet deployed)`));
3022
+ console.log(pc2.yellow(` Agent "${name}" exists only locally (not yet deployed)`));
2822
3023
  process.exit(0);
2823
3024
  }
2824
- console.log(pc.bold(` Agent: ${local.agent.name}`));
3025
+ console.log(pc2.bold(` Agent: ${local.agent.name}`));
2825
3026
  console.log();
2826
3027
  if (remote.name !== local.agent.name)
2827
- console.log(` name: ${pc.red(remote.name)} \u2192 ${pc.green(local.agent.name)}`);
3028
+ console.log(` name: ${pc2.red(remote.name)} \u2192 ${pc2.green(local.agent.name)}`);
2828
3029
  if ((remote.description || "") !== (local.agent.description || ""))
2829
- console.log(` description: ${pc.red(remote.description || "(empty)")} \u2192 ${pc.green(local.agent.description || "(empty)")}`);
3030
+ console.log(` description: ${pc2.red(remote.description || "(empty)")} \u2192 ${pc2.green(local.agent.description || "(empty)")}`);
2830
3031
  if ((remote.model || "") !== (local.agent.model || ""))
2831
- console.log(` model: ${pc.red(remote.model || "(default)")} \u2192 ${pc.green(local.agent.model || "(default)")}`);
3032
+ console.log(` model: ${pc2.red(remote.model || "(default)")} \u2192 ${pc2.green(local.agent.model || "(default)")}`);
2832
3033
  if ((remote.policy_enabled || false) !== (local.agent.policy_enabled || false))
2833
- console.log(` policy_enabled: ${pc.red(String(remote.policy_enabled || false))} \u2192 ${pc.green(String(local.agent.policy_enabled || false))}`);
3034
+ console.log(` policy_enabled: ${pc2.red(String(remote.policy_enabled || false))} \u2192 ${pc2.green(String(local.agent.policy_enabled || false))}`);
2834
3035
  const promptChanged = (remote.system_prompt || "").trim() !== local.systemPrompt.trim();
2835
3036
  const policyChanged = (remote.policy_script || "").trim() !== (local.policyScript || "").trim();
2836
3037
  if (!promptChanged && !policyChanged && remote.name === local.agent.name && (remote.description || "") === (local.agent.description || "") && (remote.model || "") === (local.agent.model || "") && (remote.policy_enabled || false) === (local.agent.policy_enabled || false)) {
2837
- console.log(pc.green(` \u2713 No changes`));
3038
+ console.log(pc2.green(` \u2713 No changes`));
2838
3039
  } else {
2839
3040
  if (promptChanged) renderFullDiff("system_prompt.md", remote.system_prompt || "", local.systemPrompt);
2840
3041
  if (policyChanged) renderFullDiff("policy.js", remote.policy_script || "", local.policyScript);
@@ -2848,24 +3049,24 @@ async function diff(args) {
2848
3049
  (a) => a.external_id === name || a.name === name || localExtId && a.external_id === localExtId
2849
3050
  );
2850
3051
  if (!local && !remote) {
2851
- console.log(pc.red(` Automation "${name}" not found locally or remotely`));
3052
+ console.log(pc2.red(` Automation "${name}" not found locally or remotely`));
2852
3053
  process.exit(1);
2853
3054
  }
2854
3055
  if (!local) {
2855
- console.log(pc.cyan(` Automation "${name}" exists only remotely`));
3056
+ console.log(pc2.cyan(` Automation "${name}" exists only remotely`));
2856
3057
  process.exit(0);
2857
3058
  }
2858
3059
  if (!remote) {
2859
- console.log(pc.yellow(` Automation "${name}" exists only locally (not yet deployed)`));
3060
+ console.log(pc2.yellow(` Automation "${name}" exists only locally (not yet deployed)`));
2860
3061
  process.exit(0);
2861
3062
  }
2862
- console.log(pc.bold(` Automation: ${local.automation.name}`));
3063
+ console.log(pc2.bold(` Automation: ${local.automation.name}`));
2863
3064
  console.log();
2864
3065
  if (remote.name !== local.automation.name)
2865
- console.log(` name: ${pc.red(remote.name)} \u2192 ${pc.green(local.automation.name)}`);
3066
+ console.log(` name: ${pc2.red(remote.name)} \u2192 ${pc2.green(local.automation.name)}`);
2866
3067
  const codeChanged = (remote.code || "") !== local.code;
2867
3068
  if (!codeChanged && remote.name === local.automation.name) {
2868
- console.log(pc.green(` \u2713 No changes`));
3069
+ console.log(pc2.green(` \u2713 No changes`));
2869
3070
  } else if (codeChanged) {
2870
3071
  renderFullDiff("main.py", remote.code || "", local.code);
2871
3072
  }
@@ -2878,29 +3079,29 @@ async function diff(args) {
2878
3079
  (h) => h.external_id === name || h.name === name || localExtId && h.external_id === localExtId
2879
3080
  );
2880
3081
  if (!local && !remote) {
2881
- console.log(pc.red(` Hook "${name}" not found locally or remotely`));
3082
+ console.log(pc2.red(` Hook "${name}" not found locally or remotely`));
2882
3083
  process.exit(1);
2883
3084
  }
2884
3085
  if (!local) {
2885
- console.log(pc.cyan(` Hook "${name}" exists only remotely`));
3086
+ console.log(pc2.cyan(` Hook "${name}" exists only remotely`));
2886
3087
  process.exit(0);
2887
3088
  }
2888
3089
  if (!remote) {
2889
- console.log(pc.yellow(` Hook "${name}" exists only locally (not yet deployed)`));
3090
+ console.log(pc2.yellow(` Hook "${name}" exists only locally (not yet deployed)`));
2890
3091
  process.exit(0);
2891
3092
  }
2892
- console.log(pc.bold(` Hook: ${local.hook.external_id}`));
3093
+ console.log(pc2.bold(` Hook: ${local.hook.external_id}`));
2893
3094
  console.log();
2894
3095
  if (remote.event !== local.hook.trigger)
2895
- console.log(` trigger: ${pc.red(remote.event)} \u2192 ${pc.green(local.hook.trigger)}`);
3096
+ console.log(` trigger: ${pc2.red(remote.event)} \u2192 ${pc2.green(local.hook.trigger)}`);
2896
3097
  const scriptChanged = (remote.script || "").trim() !== local.script.trim();
2897
3098
  if (!scriptChanged && remote.event === local.hook.trigger) {
2898
- console.log(pc.green(` \u2713 No changes`));
3099
+ console.log(pc2.green(` \u2713 No changes`));
2899
3100
  } else if (scriptChanged) {
2900
3101
  renderFullDiff(local.fileName, remote.script || "", local.script);
2901
3102
  }
2902
3103
  } else {
2903
- console.log(pc.red(` Diff not supported for "${type}" \u2014 use agents, automations, or hooks`));
3104
+ console.log(pc2.red(` Diff not supported for "${type}" \u2014 use agents, automations, or hooks`));
2904
3105
  process.exit(1);
2905
3106
  }
2906
3107
  console.log();